diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b1549f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.gradle/** +.idea/** +build/** +src/generated/** +out/** +*.iml \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..850056c --- /dev/null +++ b/README.md @@ -0,0 +1,756 @@ +# Chronos + +*Programming language for discrete event simulation* + + +## Introduction + +Discrete event simulation (DES) is a branch of Operations Research applied to a wide range of domains, which is why several valuable programming languages have been developed over the years to create executable models according to this set of techniques. + +Chronos is a didactic, interpreted, context-free language for DES, inspired by [SIMSCRIPT](http://www.simscript.com/), an important language described by Professor Silvano Martello in [his lectures](http://www.or.deis.unibo.it/staff_pages/martello/Slides_LM_new.html) and [his book](http://www.editrice-esculapio.com/martello-ricerca-operativa-per-la-laurea-magistrale/); a few ideas and elements were borrowed from Python, JavaScript and Scala as well. + +Chronos is designed to help students foster their acquaintance with DES and its concepts, which can be easily applied to other languages; it can also be a valid tool for research workers. + +Last but not least, Chronos is my very first context-free language - I hope you'll enjoy it! ^\_\_^ + + +## Features + +* **Intepreted language**: variables require no type declaration, and values of different types can be assigned to the same variable at different times. + +* **Rich type system**: variables can be of type *Double*, *Int*, *Bool*, *String*, *Infinity*, *Entity* or *Map*. + +* **Infinity algebra**: **inf** and **-inf** are constants that can be assigned to variables and employed in numerical operations and comparisons. + +* **Entities**: entities are objects whose attributes can be set at runtime - for example, you can create a *Book* entity and assign it *title*, *author* and further attributes. + +* **Procedures**: parameterized lines of code can be grouped into a procedure, just like traditional scripting languages. The user *cannot* define functions in the typical sense, but a procedure can actually return one or more values by modifying attributes of passed entities. + +* **Global scope**: identifiers not found in the current scope will be looked for in the global scope. Setting an identifier in the global scope requires the **global** keyword, to prevent namespace pollution. + +* **Queues**: entities can be stored into global containers called *queues*: queues support just a few basic operations: + + * Insert an element + * Get and remove the first element + * Remove an element (whatever its position) + * Is the queue empty? + + Furthermore, each queue is ruled by an *insertion strategy*, which defines how elements should be ordered: + + * **FIFO**: First-In, First-Out + * **LIFO**: Last-In, First-Out + * **SORTED**: according to an attribute of the entities (for example: *title*). Sort order can be either *ascending* or *descending*. + + +* **Events**: the most important aspect of Chronos. An event is a named block of code - similar to a procedure - but it takes no parameters. On the other hand, events can be *reified* into a special kind of entity, called *event notice*, which can be *scheduled* - that is, the related event code will be executed when the internal clock reaches its appointed instant. +Multiple events can be scheduled for the same moment, in FIFO order. Actually, the engine does not really employ a clock, but an optimized timeline. + +* **Flow control**: **if/else** and **while** statements are provided. There is no **for** loop, because Chronos tries to adhere as much as possible to block diagrams. + +* **Advanced random functions**: language constructs can generate values employing uniform double and integer distributions, as well as exponential distributions. It is also possible to set the random seed. + +* **Fine-grained user input**: the user can input doubles, integers, booleans and strings - as required by the program. + +* **Conversion functions**: all types can be cast to each other whenever possible. The *floor()* and *ceil()* functions can be applied to *Double* values to obtain the corresponding *Int* value. + +* **Assertions**: the **assert** statement introduces design-by-contract. + +* **Deallocation check**: to support students learning languages that do not provide garbage collection, the Chronos Virtual Machine reports all the entities and event notices that are still in memory when the program ends; this features can also be disabled via a dedicated global directive. + +* **Chronos IDE**: programming in Chronos can be fun! ^\_\_^! [Chronos IDE](https://github.com/giancosta86/Chronos-IDE) is a JavaFX application for writing and testing programs in an Integrated Development Environment (IDE). + + +## Requirements + +Chronos requires Java 8u51 or later. + +The **JAVA_HOME** environment variable must also be set. To know more about this, please refer to [this guide](http://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/index.html). + + +## Running Chronos + + +The recommended way of running Chronos is [Chronos IDE](https://github.com/giancosta86/Chronos-IDE), its JavaFX development environment. + +However, Chronos can also be used in command-line scripts (its command-line interpreter supports I/O redirection) - just employ the scripts in the *bin* directory of the [binary distribution](https://github.com/giancosta86/Chronos/releases/latest). + +The exit code of the interpreter is 0 if the program successfully ends, 1 otherwise. + + +## Language reference + +To fully understand the rationale behind Chronos, it is strongly recommended to study Professor Martello's papers on the subject. + +Chronos is based on an [ANTLR 4 grammar](https://github.com/giancosta86/Chronos/blob/master/src/main/antlr/Chronos.g4) - reading the grammar is fairly easy and usually provides more detailed information. + +What follows is a brief introduction to the syntax and semantics of the language. + + +### Comments + +Comments are like in Java or Scala: + +* Single-line comments + + ``` + // Single-line comment + ``` + +* Multi-line comments + + ``` + /* + Multi-line + comment + */ + ``` + +### Program structure + +The program structure can be outlined as follows: + +``` + + + + +event start { + +} +``` + +where *global statements* can be, in any order: + +* assignments - in this case, the **global** keyword is forbidden - this is the only case where a basic assignment creates a global variable + +* queue creation statements (see below) + +* map creation statements (see below) + +* the **$disableHeapCheck** directive, to turn off deallocation checks at the end of a program (see below) + + +The *start* event is automatically called when the program starts, like the *main* function in Java or Scala: it must schedule other events, which, in turn, can schedule other events. + +The program terminates when: + +* there are no more events scheduled +* the user does not provide required input +* a wrong operation is performed (for example, an invalid type conversion) + + +### Hello, world! + +A minimal example is as simple as: + +``` +event start { + println "Hello, world! ^__^"; +} +``` + +The **println** statement does not require parentheses: this is a common trait in Chronos, but you can always add parentheses, if you wish. +Statements must *always* end with a semicolon, ";", unless they end with a "}" (for example, the **if** and **while** blocks). + +The *start* event is always executed at time 0: the current time is represented by a double, read-only value which can be accessed via the keyword expression **time.v**. For example: + +``` +println time.v + 5; +``` + + +### Variables, basic data type and scopes + +To declare and initialize a variable in Chronos, just assign it a value. For example: + +``` +myVariable := 5; +``` + +When assigning a variable, it is visible, starting from that line, within the current event/procedure (the *local scope*). + +Identifiers must begin with a letter (uppercase or lowercase) or an underscore ("\_"), and can also include digits starting from the second character. + +The *primitive data types* are immutable: + + * *Double*: for example, 9.4. Maps to *double* on the JVM + * *Int*: for example, 7. Maps to *int* on the JVM + * *Bool*: **true** and **false**. Maps to *boolean* on the JVM. + * *String*: for example, "Hello world" - with double quotation marks. String currently do not support escape sequences. Maps to *String* on the JVM + * *[+/-]inf*: infinity values. They can be mixed with numbers in expressions. + + +Variables can be reassigned, even to different value types: + + ``` + X := "Hello"; //Now X refers to a String + X := 80; //Now an Int + X := -inf; //Now an infinity + ``` + +When a binary operation relates an *Int* and a *Double*, the result is *Double*. + +If both operands are *Int*, the result is *Int* - especially in *a / b*. For example: + + ``` + println 2 / 3; //Prints 0 + println 2.0 / 3; //Prints 0.6666666666666666 + ``` + +In *a + b*, if either operand is a *String*, the result will always be a *String*. You can alter this behaviour by using type conversions. For example: + + ``` + println 7 + "10"; //Prints 710 + println 7 + toInt("10"); //Prints 17 + ``` + +A global assignment can be achieved in 2 ways: + + * Via a normal assignment *before* any procedure/event declaration (that is, in the *global statements* section of the program) + + * By employing the **global** keyword before the assignment within the event/procedure block: + + ``` + global alpha := 9; //Assigns global var Y, creating it if needed + ``` + +When a variable is employed in an expression, it will be looked for in 2 different scopes: + + 1. the current local scope - which also includes: + * in the case of a procedure, the parameter names + * in the case of an event, the event name itself (see below) + + 2. the global scope + +Therefore, a local variable can *shadow* a global variable - by design, the only way to avoid the issue is to +choose a different name for the local variable. + + +### Type conversions + +A few predefined functions provide checked, meaningful type conversions: + +* **toDouble**(*expression*): converts *expression* to *Double*, if possible +* **toInt**(*expression*): converts *expression* to *Int*, if possible +* **toBool**(*expression*): converts *expression* to *Bool*, if possible +* **toString**(*expression*): converts *expression* to *String* (always possible) + +A failed conversion will crash the program - therefore, it is important to ensure that input data is correctly typed - which is why the language provides dedicated input statements. + + +### Input statements + +Input is always typed: + +* **readDouble** *prompt* *reference* +* **readInt** *prompt* *reference* +* **readBool** *prompt* *reference* +* **readString** *prompt* *reference* + +where: + +* *prompt* is an expression that will be converted to *String* and shown to the user. It is usually a string literal + +* *reference* can be a variable or an attribute - such reference will be assigned - *or created*, if it doesn't already exist + + +The actual implementation of these statements depends on the underlying environment, but it's reasonable to assume, for example, that **readInt** will not return until the user has typed an integer value, and that it will terminate the program in case of interrupted input. + + + +### Logic expressions + +Boolean expressions can be mixed via the usual *logic operators*, which in Chronos are +called **and**, **or** and **not**: + + ``` + alpha := true; + beta := false; + gamma := alpha and (beta or not (7 < 5)); + ``` + +Logic operators have the same priority as in Java - but it is always best to employ parentheses for clarity. + + +### Comparison operators + +*Comparison operators* are **=** (equality, whereas assignment is **:=**), **!=**, **>**, **>=**, **<**, **<=**: + + * *Double*, *Int* and infinities are comparable + + * *Strings* are comparable + + * **true** > **false** + + * *Entities* and *event notices* (which are a kind of entity) are comparable, but the comparison is performed on their memory address: consequently, only **=** and **!=** are meaningful, to understand if two entity variables refer to the very same object + +Comparison operators have more priority than logical operators, but, as usual, it is best to use parentheses. + +### Output + +Chronos provides 2 simple statements: + +* **print** *expression*: prints out the given expression + +* **println** *expression*: prints out the given expression, followed by a newline character + + +### If statement + +The general form of the **if** statement is defined as: + +``` +if conditionA { + //Statements to be executed if conditionA is true +} else if conditionB { + //Statements to be executed if conditionB is true +} else { + //Statements to be executed if the above conditions are all false +} +``` +Where: + +* There can be as many **else if** branches as needed - even zero + +* The **else** part is optional as well + +Please, note that *condition* does not require parentheses - but it *must* be followed by a block between braces, to improve readability. + +Conditional blocks do *not* define a scope: variables defined in conditional blocks get defined in the scope of the current event/procedure. + + + +### While statement + +The **while** statement is defined as: + +``` +while condition { + //Statements to be executed +} +``` + +Please, note that *condition* does not require parentheses - but it *must* be followed by a block between braces, to improve readability. + +The loop body does *not* define a scope: variables defined within such block get defined in the scope of the current event/procedure. + + +### Assertions + +The **assert** statement enables a basic but effective form of [design by contract](https://en.wikipedia.org/wiki/Design_by_contract): + +``` +assert condition; +``` + +Whenever such a statement is encountered, *condition* is evaluated and, if is **false**, the program crashes, reporting such line. + +Assertions are very useful to state program constraints, therefore ensuring consistency. + + +### Creating entities + +To create an entity and assign it to a local variable, you need to *allocate* it with the **create** statement, which comes in 2 forms: + + * the basic form is, for example: + ``` + create book; + ``` + + This creates an entity of type *book* and also assigns it to the local variable named *book*. + + + * the extended form supports the declaration of both an entity type and a local variable, and it's preferable in order to declare 2 entities of the same type in the same scope: + + ``` + create book called exampleBook; + create book called anotherBook; + ``` + + Now the local context contains 2 variables - *exampleBook* and *anotherBook*, both of type *book*. + +Strictly speaking, the entity type does not affect the internal structure of entities. It has just 2 main purposes: + + * in a domain-driven approach, it participates in the description of the entity - for example, printing an entity shows its type and its attributes + + * if the entity type coincides with an event name, an entity notice is created instead; we'll return on this concept later. + +After creating an entity, you can assign it to other local/global variables. Please, keep in mind that only the *reference* to the entity will be copied, therefore all the variables will point to the very same entity in memory. + + +### Entity attributes + +Entities behave like key/value pairs and are initially empty. + +Further key/value pairs are called *attributes* (or *properties*) and can be added or set using this syntax: + + ``` + author(book) := "Alan Turing"; + ``` + +Any object can be assigned to an attribute - including other entities, recursively! + + ``` + create person called greatScientist; + name(greatScientist) := "Alan Turing"; + println greatScientist; + + create book; + author(book) := greatScientist; + ``` + + +### Deallocating entities + +Entities *should* be deallocated, via the following statement: + +``` +destroy localOrGlobalVariable; //Must reference an entity +``` + +Deallocating the same entity twice results in a failure. + +If the program successfully terminates, Chronos will report all the entities that were not deallocated: this check has actually no effect, but it is due to the didactic nature of Chronos. + +To optimize performances and memory requirements, this check can be turned off via the **$disableHeapCheck** global statement at the beginning of the program: + +``` +$disableHeapCheck; +``` + + +### Queues + +Entity queues are paramount in simulations, and Chronos supports them extensively. + +Queues belong to the global scope, but in their own namespace, accessible via the queue manipulation instructions. + +To create a queue, use one of the following global statements before any event or procedure: + + * **queue** queueName **fifo**; *//creates a FIFO queue* + * **queue** queueName **lifo**; *//creates a LIFO queue* + * **queue** queueName **sorted by** propertyName **asc**; *//sorted in ascending order. The default if the sort order is omitted* + * **queue** queueName **sorted by** propertyName **desc**; *//sorted in descending order* + +The last 2 statements both define a **sorted** queue - that is, a queue whose elements are sorted according to a given attribute (for example, the property *title* in a queue of *book* entities) + + +Given any type of queue, the related operations are fairly straightforward: + +``` +//The type of the queue (in this case, "fifo") does not alter the syntax of queue operations +queue myQueue fifo; + +event start { + create book; + title(book) := "Chronos - Reference guide"; + + create anotherBook; + title(anotherBook) := "Chronos IDE - User guide"; + + insert book into myQueue; + insert anotherBook into myQueue; + + assert not isEmpty(myQueue); + + firstBookFound := get first from myQueue; + println firstBookFound; + + secondBookFound := get first from myQueue; + println secondBookFound; + + assert isEmpty(myQueue); + + insert book into myQueue; + + //Removes the entity from the book, whatever its position + remove book from myQueue; + + if isEmpty(myQueue) { + println "The queue is now empty!"; + } + + destroy book; + destroy anotherBook; +} +``` + +There are 2 important aspects to take into account when dealing with sorted queues: + + * queues are *dynamically typed*: when you add an entity to a sorted queue, it is only required that such entity contains the *property* on which the queue is based - for example, if a queue is sorted by *title*, you could add both *book* and *film* objects, provided they both have a *title* property. + + * For performance reasons, items are sorted *when added to the queue*: consequently, you should *not* alter the property on which the queue is based, at least as long as the entity is in the queue - otherwise, the correct order cannot be ensured. + + +### Procedures + +Procedures are top-level, named blocks of statements. + +A procedure can define zero or more parameters and can contain any number of statements. + +For example: +``` +procedure procedureWithNoParams { + //Statements here +} + +procedure updateStats(book, quantity) { + //Statements here +} +``` + +Procedures cannot be nested. + +To call a procedure, employ the **call** statement: + +``` +call procedureWithNoParams; + +call updateStats(myReferenceGuide, 5); +``` + +User-defined functions are, by design, *not* supported, but entities provide a way to simulate them: + +``` +event start { + create result; + call sum(6, 8, result); + println "The sum is: " + value(result); + + destroy result; +} + +procedure sum(x, y, result) { + value(result) := x + y; +} +``` + +Incidentally, the example above shows that events and procedures can be declared in any order. + +To return from a procedure, just employ the **return** statement. + +``` +return; +``` + +### Events + +Events are the cornerstone of a simulation system, which is why Chronos is very flexible in this regard. + +An event is a named, global, non-nestable block defining a local scope, exactly like procedure. But, unlike procedures, events cannot be called via the **call** statement and, consequently, they do not support parameters. + +First of all, every program requires a *start* event, which is its entry point - a sort of *main* function, found in many traditional programming languages. The duty of the *start* event is to *schedule event notices*. + +An *event notice* is simply *an entity whose type matches the name of an event*; being an entity, an event notice can be assigned properties. + +For example: + +``` +event start { + /* + Creates an event notice bound to additionalEvent and assigns it + to a local variable named "additionalEvent" + */ + create additionalEvent; + + /* + Creates an event notice bound to additionalEvent, assigning it + to the local variable "myEventNotice" + */ + create additionalEvent called myEventNotice; +} + + +event additionalEvent { + +} +``` + +The first form is lighter, the second form is preferred to create multiple event notices referencing the same event within the same local context. + +An event notice can also be *scheduled* - that is, the code of its related event will be executed at a given instant. + +To schedule an event notice there are 4 variants of the **schedule** statement: + +* **schedule** *eventName* **at** *instant* +* **schedule** *eventName* **called** *eventNotice* **at** *instant* + +* **schedule** *eventName* **after** *interval* +* **schedule** *eventName* **called** *eventNotice* **after** *interval* + +The first 2 forms schedule event notices by instant, the other 2 forms schedule by interval from the current instant. For example: + +``` +schedule additionalEvent at 90; +schedule additionalEvent called myEventNotice after 10; +``` + + +To pass data to events, apart from (fairly inelegant) global variables, one can use *attributes of the triggering event notice*: in other words, every event has a local variable named like the event itself and referencing *the event notice created to schedule the event* - this means that it's possible to set attributes of the event notice while scheduling it, and retrieve such attributes within the event body. For example: + +``` +event start { + //This creates an event notice referring customEvent + //and assigned to the customEvent local variable + create customEvent; + magicNumber(customEvent) := 42; + + //This creates an event notice referring customEvent + //and assigned to the myNotice local variable + create customEvent called myNotice; + magicNumber(myNotice) := magicNumber(customEvent) + 1; + + schedule customEvent at 10; + schedule customEvent called myNotice at 15.5; +} + + +event customEvent { + println magicNumber(customEvent); + + //Always destroy the event notice if you do not reschedule it + destroy customEvent; +} +``` + +In the event block, the event notice can be rescheduled, or it can be destroyed, just like other entities; anyway, event notices *must* be destroyed at some point - unless the deallocation check is disabled via global directive, of course. + +In both events and procedures, the special constant *time.v* returns the double value of the current simulation time - which is 0 in the *start* event and is automatically increased by the engine as the events flow. + +The **return** keyword stops the current event, making the execution proceed to the next event in the schedule, if available. + + +### Maps + +Setting properties of entities is effective, but it presents 2 major drawbacks: + +* Property names must be valid identifiers - in particular, they cannot be numbers +* Property names cannot be *dynamic* - to access the *title* property of a *book* entity, we need to write *title(book)*, which is hardwired in the code. + +To overcome this limitation, at the beginning of the program it is possible to define special global variables called *maps*: + +``` +map mapName; +``` + +A map is a dictionary of properties, like an entity, but: + +* Its keys can be *any primitive*, immutable value type (especially numbers) +* The key can be any expression, unknown until the very moment when it's accessed + +For example: + +``` +map myMap; + +event start { + myMap("title") := "Chronos reference guide"; + + titleProperty := "title"; + + //We are accessing the map via an expression! + println myMap(titleProperty); +} +``` + +Arrays are a very useful application of maps: + +``` +map bookArray; +bookCount := 10; + +event start { + i := 1; + while (i <= bookCount) { + create book; + title(book) := "Reference Guide - Part " + i; + bookArray(i) := book; + i := i + 1; + } + + i := 1; + while (i <= bookCount) { + println title(bookArray(i)); + i := i + 1; + } + + i := 1; + while (i <= bookCount) { + destroy bookArray(i); + i := i + 1; + } +} +``` + +In this example, we didn't need to declare the "array size" - because it is actually a map: such feature is especially useful to define *sparse arrays*. + +**Note**: unlike queues, maps belong to the *global namespace*, so they might be shadowed by local variables. Also, it is perfectly licit (but rather obscure and not recommendable) to overwrite them via *global assignments*. + + +### Random numbers + +Chronos supports random numbers via 3 predefined functions: + +* *uniformRandom(min, max)*: returns a random *Double* value with uniform distribution in the range [min; max). Please note that *max* is *excluded* + +* *uniformIntRandom(min, max)*: returns a random *Int* value with uniform distribution in the range [min; max]. In this case, *max* is *included* + +* *expRandom(averageValue)*: return a random *Double* value with exponential distribution having the given average value + +Whenever a program starts, a new random seed is chosen; you can set the random seed at any time by using the **setRandomSeed** statement: + +``` +setRandomSeed numericExpression; +``` + + +### Terminating a program + +Programs naturally end their execution when there are no more events in the schedule. + +To force immediate (but still successful) program termination, you can use the **exit** statement: + +``` +exit; +``` + +To force a failure, you could use, for example: + +``` +assert false; +``` + +## Experimenting + +Chronos is an agile, interpreted language - feel free to try it yourself, especially in its [user-friendly IDE](https://github.com/giancosta86/Chronos-IDE)! ^\_\_^ + +As a plus, the test packages contain a suite of test programs, crafted to verify the language features: you might want to check them out as well. + + +## The Chronos Virtual Machine + +Chronos is a programming language based on an multi-layered architecture: + +* The *parser* is in Java, generated via ANTLR. + +* The concrete tree visitor, creating the AST, is written in Scala. In particular, the methods of the *BasicAstBuilder* object generate the AST from the program source code. + +* The virtual machine is written in Scala as well, and it's pluggable in other applications: by implementing the traits *Input* and *Output*, and passing them to an instance of the *Interpreter* class, other software can integrate the Chronos engine. + +* [Chronos-IDE](https://github.com/giancosta86/Chronos-IDE) is an application mixing the Chronos Virtual Machine into [OmniEditor](https://github.com/giancosta86/OmniEditor), an easy-to-use JavaFX library for creating custom IDEs. + + +## Special thanks + + +* [Professor Silvano Martello](http://www.or.deis.unibo.it/staff_pages/martello/cvitae.html), for his valuable advice and teaching in the field of Operations Research + +* [Professor Enrico Denti](http://enricodenti.disi.unibo.it/index.shtml), for his valuable advice and teaching in the field of Languages and Computational Models + +* [Scala](http://www.scala-lang.org/) - an extremely elegant language that has quickly become one of my favourite languages + +* [SIMSCRIPT](http://www.simscript.com/) - the language that mainly inspired Chronos + +* [Python](https://www.python.org/) - another language that contributed to inspiring Chronos + +* [ANTLR](http://www.antlr.org/) - a great parser generator, and much more diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..554c8ed --- /dev/null +++ b/build.gradle @@ -0,0 +1,166 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +import org.apache.tools.ant.filters.ReplaceTokens + +buildscript { + repositories { + maven { + url 'https://dl.bintray.com/giancosta86/Hephaestus' + } + + jcenter() + } + + dependencies { + classpath 'info.gianlucacosta.moonlicense:moonlicense-gradle:3.1' + classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.1' + classpath 'info.gianlucacosta.aurora:aurora:2.1' + classpath 'com.github.maiflai:gradle-scalatest:0.8' + } +} + +apply plugin: 'scala' +apply plugin: 'antlr' +apply plugin: 'application' +apply plugin: 'com.github.maiflai.scalatest' + +apply plugin: 'info.gianlucacosta.aurora' + + +group = 'info.gianlucacosta.chronos' +archivesBaseName = "chronos" +version = '1.0' + +description = 'Programming language for discrete event simulation' + + +ext.facebookPage = "https://www.facebook.com/chronos.language" + + +mainClassName = 'info.gianlucacosta.chronos.App' + +sourceSets { + generated { + java { + srcDirs = ['src/generated/java'] + } + } + + main { + scala { + srcDirs += ['src/generated/java'] + } + } +} + + +dependencies { + compile 'org.scala-lang:scala-library:2.11.7' + compile 'org.antlr:antlr4-runtime:4.5.1' + compile 'org.scala-lang:scala-xml:2.11.0-M4' + + testCompile 'org.scalatest:scalatest_2.11:2.2.5' + testRuntime 'org.pegdown:pegdown:1.1.0' + + antlr 'org.antlr:antlr4:4.5.1' +} + + + +aurora { + docTask = 'scaladoc' + gitHubUser = 'giancosta86' + + author { + name = 'Gianluca Costa' + email = 'gianluca@gianlucacosta.info' + url = 'http://gianlucacosta.info/' + } + + + bintray { + user = project.bintrayUser + key = project.bintrayKey + repo = 'Hephaestus' + licenses = ['Apache-2.0'] + labels = [ + 'programming language', + 'discrete event simulation', + 'DES', + 'interactive', + 'interpreter', + 'command-line', + 'Scala' + ] + } +} + + +moonLicense { + license = apache2 + + productInfo { + productName = 'Chronos' + inceptionYear = 2015 + copyrightHolder = 'Gianluca Costa' + } + + includes = includes + [ + /.*\.g4$/: javaFormat + ] +} + + + + +task deleteGeneratedFiles(type: Delete) { + delete 'src/generated' +} + +generateGrammarSource { + outputDirectory = project.file("src/generated/java/info/gianlucacosta/chronos/parser") + arguments += [ + '-visitor', + '-long-messages', + '-no-listener', + '-Werror' + ] +} +generateGrammarSource.dependsOn deleteGeneratedFiles + + +clean.dependsOn deleteGeneratedFiles + + + +processResources { + filesMatching('**/Language.properties.xml') { + filter ReplaceTokens, tokens: [ + 'name' : project.moonLicense.productInfo.productName, + 'version' : project.version, + 'copyrightYears': project.moonLicense.getCopyrightYears(), + 'license' : project.moonLicense.license.name, + 'url' : project.url.toString(), + 'facebookPage' : project.facebookPage, + 'release' : project.auroraSettings.release.toString() + ] + } +} diff --git a/mainIcon.svg b/mainIcon.svg new file mode 100644 index 0000000..38f0343 --- /dev/null +++ b/mainIcon.svg @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..762722b --- /dev/null +++ b/settings.gradle @@ -0,0 +1,22 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +rootProject.name = 'Chronos' + diff --git a/src/main/antlr/Chronos.g4 b/src/main/antlr/Chronos.g4 new file mode 100644 index 0000000..08c28b8 --- /dev/null +++ b/src/main/antlr/Chronos.g4 @@ -0,0 +1,324 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +grammar Chronos; + +@header { +package info.gianlucacosta.chronos.parser; +} + + +// ------------------ +// PROGRAM DEFINITION +// ------------------ + +program: globalStatement* (event | procedure)+ EOF; + + + +// ----------------- +// GLOBAL STATEMENTS +// ----------------- + +globalStatement: (createQueue | createMap | implicitGlobalAssignment | disableHeapCheck) ';'; + +createQueue: 'queue' IDENTIFIER (FIFO | LIFO | 'sorted' 'by' IDENTIFIER (ASC|DESC)?); + +createMap: 'map' IDENTIFIER; + +implicitGlobalAssignment: assignment; + + + + +// ---------------- +// TOP-LEVEL BLOCKS +// ---------------- + + +event: 'event' IDENTIFIER block; + +procedure: 'procedure' IDENTIFIER ('(' param (',' param)* ')')? block; +param: IDENTIFIER; + + +block: '{' localStatement* '}'; + +disableHeapCheck: '$disableHeapCheck'; + + + +// ---------- +// STATEMENTS +// ---------- + +localStatement: (basicLocalStatement ';') | ifStatement | whileStatement; + + +basicLocalStatement: createEntity + | destroyEntity + + | schedule + | cancel + + | localAssignment + | globalAssignment + + | enqueue + | remove + + | call + | returnStatement + + | exit + + | read + + | print + | println + + | assertion + + | setRandomSeed; + + + +// -------------------- +// CREATION/DESTRUCTION +// -------------------- + +createEntity: 'create' IDENTIFIER ('called' reference)?; +destroyEntity: 'destroy' reference; + + + +// ---------------- +// EVENT MANAGEMENT +// ---------------- + +schedule: 'schedule' IDENTIFIER ('called' reference)? (AT | AFTER) expression; +cancel: 'cancel' IDENTIFIER ('called' reference)?; + + + +// ---------- +// ASSIGNMENT +// ---------- + +localAssignment: assignment; +globalAssignment: 'global' assignment; + + +assignment: reference ':=' expression; +reference: IDENTIFIER ('(' expression ')')?; + + + +// ---------------- +// QUEUE MANAGEMENT +// ---------------- + +enqueue: 'insert' reference 'into' IDENTIFIER; +remove: 'remove' reference 'from' IDENTIFIER; + + + +// ------------ +// FLOW CONTROL +// ------------ + +call: 'call' IDENTIFIER ('(' argument ( ',' argument)* ')')?; +argument: expression; + +returnStatement: 'return'; + +exit: 'exit'; + +ifStatement: 'if' condition thenPart (elsePart)?; +condition: expression; +thenPart: block; +elsePart: 'else' (block | ifStatement); + +whileStatement: 'while' condition block; + +assertion: 'assert' expression; + + + +// ----------- +// EXPRESSIONS +// ----------- + + +expression: orOperation; + +orOperation: andOperation (OP_OR andOperation)*; +andOperation: logicLiteral (OP_AND logicLiteral)*; +logicLiteral: OP_NOT? comparison; + + +comparison: algebraicSum (comparisonOperator algebraicSum)?; +comparisonOperator: '>' | '>=' | '<' | '<=' | '=' | '!='; + + +algebraicSum: firstAddend additionalAddend*; +firstAddend: (OP_PLUS|OP_MINUS)? algebraicProduct; +additionalAddend: (OP_PLUS|OP_MINUS) algebraicProduct; + + +algebraicProduct: firstFactor additionalFactor*; +firstFactor: term; +additionalFactor: additionalFactorOperator term; +additionalFactorOperator: OP_TIMES|OP_OVER; + + + +term: ground + | priorityExpression + | referenceValue + | dequeue + | functionCall; + + +priorityExpression: '(' expression ')'; + +referenceValue: reference; + +dequeue: 'get' 'first' 'from' IDENTIFIER; + +ground: number + | plusInf + | minusInf + | trueValue + | falseValue + | stringValue + | now; + + +number: NUMBER; +plusInf: PLUS_INF; +minusInf: MINUS_INF; +trueValue: TRUE; +falseValue: FALSE; +stringValue: STRING; +now: NOW; + + + +// -------------------- +// PREDEFINED FUNCTIONS +// -------------------- + +functionCall: emptyCheck + | uniformRandom + | uniformIntRandom + | exponentialRandom + | cast + | floor + | ceil; + + +emptyCheck: 'isEmpty' '(' IDENTIFIER ')'; + +uniformRandom: 'uniformRandom' '(' expression ',' expression ')'; +uniformIntRandom: 'uniformIntRandom' '(' expression ',' expression ')'; +exponentialRandom: 'expRandom' '(' expression ')'; + + +cast: castToDouble | castToInt | castToBoolean | castToString; + +castToDouble: 'toDouble' '(' expression ')'; +castToInt: 'toInt' '(' expression ')'; + +castToBoolean: 'toBool' '(' expression ')'; +castToString: 'toString' '(' expression ')'; + + +floor: 'floor' '(' expression ')'; +ceil: 'ceil' '(' expression ')'; + + + +// ------------ +// INPUT/OUTPUT +// ------------ + +print: 'print' expression; +println: 'println' expression; + +read: readDouble | readInt | readBoolean | readString; + +readDouble: 'readDouble' prompt reference; +readInt: 'readInt' prompt reference; +readBoolean: 'readBool' prompt reference; +readString: 'readString' prompt reference; +prompt: expression; + + +// ------------- +// MISCELLANEOUS +// ------------- + +setRandomSeed: 'setRandomSeed' expression; + + + +// ------ +// TOKENS +// ------ + + +FIFO: 'fifo'; +LIFO: 'lifo'; +ASC: 'asc'; +DESC: 'desc'; + +FIRST: 'first'; + + +AT: 'at'; +AFTER: 'after'; + + +OP_OR: 'or'; +OP_AND: 'and'; +OP_NOT: 'not'; +OP_PLUS: '+'; +OP_MINUS: '-'; +OP_TIMES: '*'; +OP_OVER: '/'; + + +NUMBER: ('0'|[1-9][0-9]*)('.'[0-9]+)?; +PLUS_INF: '+inf'|'inf'; +MINUS_INF: '-inf'; +TRUE: 'true'; +FALSE: 'false'; +STRING: '"' ~["]* '"'; +NOW: 'time.v'; + + +IDENTIFIER: [A-Za-z_][A-Za-z0-9_]*; + + +WHITESPACE: [ \t\r\n]+ -> skip ; + +SINGLE_LINE_COMMENT: '//' ~( '\r' | '\n' )* -> skip; +MULTI_LINE_COMMENT: '/*' .*? '*/' -> skip; diff --git a/src/main/resources/info/gianlucacosta/chronos/Language.properties.xml b/src/main/resources/info/gianlucacosta/chronos/Language.properties.xml new file mode 100644 index 0000000..fb7b7e7 --- /dev/null +++ b/src/main/resources/info/gianlucacosta/chronos/Language.properties.xml @@ -0,0 +1,31 @@ + + + + + + @name@ + @version@ + @copyrightYears@ + @license@ + @url@ + @facebookPage@ + @release@ + diff --git a/src/main/scala/info/gianlucacosta/chronos/App.scala b/src/main/scala/info/gianlucacosta/chronos/App.scala new file mode 100644 index 0000000..4ca7809 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/App.scala @@ -0,0 +1,66 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos + +import java.io.{File, FileReader} + +import info.gianlucacosta.chronos.interpreter.Interpreter +import info.gianlucacosta.chronos.interpreter.io.{ConsoleInput, ConsoleOutput} +import info.gianlucacosta.chronos.parser.BasicAstBuilder +import info.gianlucacosta.chronos.parser.exceptions.ParsingException + + +object App { + def main(args: Array[String]): Unit = { + if (args.length != 1) { + exitWithUsage() + } + + val sourceFile = new File(args(0)) + if (!sourceFile.exists()) { + exitWithUsage() + } + + + try { + val program = BasicAstBuilder.buildAST(new FileReader(sourceFile)) + + val input = new ConsoleInput + val output = new ConsoleOutput + val interpreter = new Interpreter(input, output) + + interpreter.run(program) + System.exit(0) + } catch { + case ex@(_: ParsingException) => System.err.println(ex.getMessage) + System.exit(1) + } + } + + + private def exitWithUsage(): Unit = { + println(s"${Language.name} - Version ${Language.version}") + println(s"Copyright © ${Language.copyrightYears} Gianluca Costa") + println() + println(s"Usage: ") + System.exit(1) + } +} diff --git a/src/main/scala/info/gianlucacosta/chronos/Language.scala b/src/main/scala/info/gianlucacosta/chronos/Language.scala new file mode 100644 index 0000000..b31f45c --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/Language.scala @@ -0,0 +1,27 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos + +import info.gianlucacosta.chronos.util.CommonXmlProperties + +object Language extends CommonXmlProperties( + App.getClass.getResourceAsStream("Language.properties.xml") +) diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/AstVisitor.scala b/src/main/scala/info/gianlucacosta/chronos/ast/AstVisitor.scala new file mode 100644 index 0000000..68ea9ca --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/AstVisitor.scala @@ -0,0 +1,158 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast + +import info.gianlucacosta.chronos.ast.expressions._ +import info.gianlucacosta.chronos.ast.expressions.ground._ +import info.gianlucacosta.chronos.ast.statements._ + +trait AstVisitor[T] { + def visit(node: Node): T + + def visit(node: Program): T + + def visit(node: DisableHeapCheck): T + + def visit(node: Event): T + + def visit(node: Procedure): T + + def visit(node: Block): T + + def visit(statement: Statement): T + + def visit(node: Return): T + + def visit(node: Exit): T + + def visit(node: Print): T + + def visit(node: Println): T + + def visit(node: AssignLocalReference): T + + def visit(node: AssignGlobalReference): T + + def visit(node: Call): T + + def visit(node: Assert): T + + def visit(node: CreateMap): T + + def visit(node: CreateEntity): T + + def visit(node: DestroyEntity): T + + def visit(node: ReadDouble): T + + def visit(node: ReadInt): T + + def visit(node: ReadBoolean): T + + def visit(node: ReadString): T + + def visit(node: CreateFifoQueue): T + + def visit(node: CreateLifoQueue): T + + def visit(node: CreateSortedQueue): T + + def visit(node: Enqueue): T + + def visit(node: Remove): T + + def visit(node: ScheduleAt): T + + def visit(node: ScheduleAfter): T + + def visit(node: Cancel): T + + def visit(node: If): T + + def visit(node: While): T + + def visit(node: SetRandomSeed): T + + + /* + * EXPRESSIONS + */ + + def visit(expression: Expression): T + + def visit(expression: Or): T + + def visit(expression: And): T + + def visit(expression: Not): T + + def visit(expression: Comparison): T + + def visit(expression: AlgebraicSum): T + + def visit(expression: UnaryMinus): T + + def visit(expression: Multiplication): T + + def visit(expression: Division): T + + def visit(expression: ReferenceValue): T + + def visit(expression: Dequeue): T + + def visit(expression: CastToDouble): T + + def visit(expression: CastToInt): T + + def visit(expression: CastToBoolean): T + + def visit(expression: CastToString): T + + def visit(expression: Floor): T + + def visit(expression: Ceil): T + + def visit(expression: IsEmpty): T + + def visit(expression: UniformRandom): T + + def visit(expression: UniformIntRandom): T + + def visit(expression: ExponentialRandom): T + + def visit(expression: DoubleTerm): T + + def visit(expression: IntTerm): T + + def visit(expression: BooleanTerm): T + + def visit(expression: StringTerm): T + + def visit(expression: PlusInfTerm): T + + def visit(expression: MinusInfTerm): T + + def visit(expression: Now): T + + def visit(expression: Condition): T + + def visit(reference: Reference): T +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/Block.scala b/src/main/scala/info/gianlucacosta/chronos/ast/Block.scala new file mode 100644 index 0000000..1ff830a --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/Block.scala @@ -0,0 +1,27 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast + + +case class Block(statements: Seq[Statement]) extends Node { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/Event.scala b/src/main/scala/info/gianlucacosta/chronos/ast/Event.scala new file mode 100644 index 0000000..60e9a33 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/Event.scala @@ -0,0 +1,26 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast + +case class Event(name: String, block: Block) extends Node { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/Expression.scala b/src/main/scala/info/gianlucacosta/chronos/ast/Expression.scala new file mode 100644 index 0000000..9c71dad --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/Expression.scala @@ -0,0 +1,23 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast + +trait Expression extends Node diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/Node.scala b/src/main/scala/info/gianlucacosta/chronos/ast/Node.scala new file mode 100644 index 0000000..618f663 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/Node.scala @@ -0,0 +1,25 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast + +abstract class Node { + def accept[T](visitor: AstVisitor[T]): T +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/Procedure.scala b/src/main/scala/info/gianlucacosta/chronos/ast/Procedure.scala new file mode 100644 index 0000000..b0b7994 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/Procedure.scala @@ -0,0 +1,41 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast + +import info.gianlucacosta.chronos.ast.exceptions.DuplicateParameterException + + +case class Procedure(name: String, params: Seq[String], block: Block) extends Node { + { + var uniqueParams = Set.empty[String] + + for (param <- params) { + if (uniqueParams.contains(param)) { + throw new DuplicateParameterException(name, param) + } + + uniqueParams += param + } + } + + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/Program.scala b/src/main/scala/info/gianlucacosta/chronos/ast/Program.scala new file mode 100644 index 0000000..77e1384 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/Program.scala @@ -0,0 +1,66 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast + +import info.gianlucacosta.chronos.ast.exceptions.{DuplicateEventException, DuplicateProcedureException} + + +case class Program(globalStatements: Block, events: Seq[Event], procedures: Seq[Procedure]) extends Node { + private val eventsMap = { + var result = Map.empty[String, Event] + + for (event <- events) { + if (result.contains(event.name)) { + throw new DuplicateEventException(event.name) + } + + result += (event.name -> event) + } + + result + } + + + private val proceduresMap = { + var result = Map.empty[String, Procedure] + + for (procedure <- procedures) { + if (result.contains(procedure.name)) { + throw new DuplicateProcedureException(procedure.name) + } + + result += (procedure.name -> procedure) + } + + result + } + + + def startEvent: Option[Event] = getEvent("start") + + def getEvent(eventName: String): Option[Event] = eventsMap.get(eventName) + + def getProcedure(procedureName: String): Option[Procedure] = proceduresMap.get(procedureName) + + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) + +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/Reference.scala b/src/main/scala/info/gianlucacosta/chronos/ast/Reference.scala new file mode 100644 index 0000000..757f4c6 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/Reference.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast + +case class Reference(identifier: String, parameter: Option[Expression]) extends Node { + def this(identifier: String) = this(identifier, None) + + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/SortOrder.scala b/src/main/scala/info/gianlucacosta/chronos/ast/SortOrder.scala new file mode 100644 index 0000000..88b69de --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/SortOrder.scala @@ -0,0 +1,27 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast + +case object SortOrder extends Enumeration { + type Type = Value + + val Asc, Desc = Value +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/Statement.scala b/src/main/scala/info/gianlucacosta/chronos/ast/Statement.scala new file mode 100644 index 0000000..bab72e4 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/Statement.scala @@ -0,0 +1,26 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast + + +trait Statement extends Node { + val lineNumber: Int +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/exceptions/DuplicateEventException.scala b/src/main/scala/info/gianlucacosta/chronos/ast/exceptions/DuplicateEventException.scala new file mode 100644 index 0000000..2916a5f --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/exceptions/DuplicateEventException.scala @@ -0,0 +1,25 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.exceptions + +class DuplicateEventException(val eventName: String) extends DuplicateIdentifierException( + s"Duplicate event: '${eventName}'" +) \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/exceptions/DuplicateIdentifierException.scala b/src/main/scala/info/gianlucacosta/chronos/ast/exceptions/DuplicateIdentifierException.scala new file mode 100644 index 0000000..c624511 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/exceptions/DuplicateIdentifierException.scala @@ -0,0 +1,23 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.exceptions + +class DuplicateIdentifierException(message: String) extends RuntimeException(message) diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/exceptions/DuplicateParameterException.scala b/src/main/scala/info/gianlucacosta/chronos/ast/exceptions/DuplicateParameterException.scala new file mode 100644 index 0000000..e99e311 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/exceptions/DuplicateParameterException.scala @@ -0,0 +1,25 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.exceptions + +class DuplicateParameterException(val procedureName: String, val param: String) extends DuplicateIdentifierException( + s"Duplicate parameter '${param}' for procedure '${procedureName}'" +) diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/exceptions/DuplicateProcedureException.scala b/src/main/scala/info/gianlucacosta/chronos/ast/exceptions/DuplicateProcedureException.scala new file mode 100644 index 0000000..1d5f782 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/exceptions/DuplicateProcedureException.scala @@ -0,0 +1,25 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.exceptions + +class DuplicateProcedureException(val procedureName: String) extends DuplicateIdentifierException( + s"Duplicate procedure: '${procedureName}'" +) \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/AlgebraicSum.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/AlgebraicSum.scala new file mode 100644 index 0000000..8c403dc --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/AlgebraicSum.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +case class AlgebraicSum(addends: Seq[Expression]) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/And.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/And.scala new file mode 100644 index 0000000..aea5085 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/And.scala @@ -0,0 +1,29 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + + +case class And(operands: Seq[Expression]) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/CastToBoolean.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/CastToBoolean.scala new file mode 100644 index 0000000..420b26c --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/CastToBoolean.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +case class CastToBoolean(expression: Expression) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/CastToDouble.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/CastToDouble.scala new file mode 100644 index 0000000..b0838a1 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/CastToDouble.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +case class CastToDouble(expression: Expression) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/CastToInt.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/CastToInt.scala new file mode 100644 index 0000000..cfec727 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/CastToInt.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +case class CastToInt(expression: Expression) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/CastToString.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/CastToString.scala new file mode 100644 index 0000000..cfda83d --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/CastToString.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +case class CastToString(expression: Expression) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Ceil.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Ceil.scala new file mode 100644 index 0000000..38e844d --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Ceil.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +case class Ceil(expression: Expression) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Comparison.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Comparison.scala new file mode 100644 index 0000000..1de7872 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Comparison.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +case class Comparison(left: Expression, operator: ComparisonOperator, right: Expression) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ComparisonOperator.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ComparisonOperator.scala new file mode 100644 index 0000000..3b1cd5f --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ComparisonOperator.scala @@ -0,0 +1,42 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +object ComparisonOperator { + + case object Less extends ComparisonOperator("<") + + case object LessEqual extends ComparisonOperator("<=") + + case object Greater extends ComparisonOperator(">") + + case object GreaterEqual extends ComparisonOperator(">=") + + case object Equal extends ComparisonOperator("=") + + case object NotEqual extends ComparisonOperator("!=") + +} + + +sealed abstract class ComparisonOperator(operator: String) { + override def toString = operator +} \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Condition.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Condition.scala new file mode 100644 index 0000000..2064f81 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Condition.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +case class Condition(expression: Expression) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Dequeue.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Dequeue.scala new file mode 100644 index 0000000..dca4cf9 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Dequeue.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +case class Dequeue(queueName: String) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Division.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Division.scala new file mode 100644 index 0000000..2a2bead --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Division.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +case class Division(left: Expression, right: Expression) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ExponentialRandom.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ExponentialRandom.scala new file mode 100644 index 0000000..e12a45c --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ExponentialRandom.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +case class ExponentialRandom(averageValue: Expression) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Floor.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Floor.scala new file mode 100644 index 0000000..62219d0 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Floor.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +case class Floor(expression: Expression) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/IsEmpty.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/IsEmpty.scala new file mode 100644 index 0000000..baec3eb --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/IsEmpty.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +case class IsEmpty(queueName: String) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Multiplication.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Multiplication.scala new file mode 100644 index 0000000..d3ee77b --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Multiplication.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +case class Multiplication(left: Expression, right: Expression) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Not.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Not.scala new file mode 100644 index 0000000..5ad01ac --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Not.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +case class Not(operand: Expression) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Or.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Or.scala new file mode 100644 index 0000000..4f10656 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/Or.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +case class Or(operands: Seq[Expression]) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ReferenceValue.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ReferenceValue.scala new file mode 100644 index 0000000..37209e4 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ReferenceValue.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression, Reference} + +case class ReferenceValue(reference: Reference) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/UnaryMinus.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/UnaryMinus.scala new file mode 100644 index 0000000..28bf3cf --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/UnaryMinus.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +case class UnaryMinus(operand: Expression) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/UniformIntRandom.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/UniformIntRandom.scala new file mode 100644 index 0000000..ea5a140 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/UniformIntRandom.scala @@ -0,0 +1,29 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +case class UniformIntRandom(minValue: Expression, maxValue: Expression) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = { + visitor.visit(this) + } +} \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/UniformRandom.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/UniformRandom.scala new file mode 100644 index 0000000..9b7718b --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/UniformRandom.scala @@ -0,0 +1,29 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +case class UniformRandom(minValue: Expression, maxValue: Expression) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = { + visitor.visit(this) + } +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/BooleanTerm.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/BooleanTerm.scala new file mode 100644 index 0000000..32cce9c --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/BooleanTerm.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions.ground + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +final case class BooleanTerm(value: Boolean) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/DoubleTerm.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/DoubleTerm.scala new file mode 100644 index 0000000..bf66c67 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/DoubleTerm.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions.ground + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +final case class DoubleTerm(value: Double) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/IntTerm.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/IntTerm.scala new file mode 100644 index 0000000..13abf2b --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/IntTerm.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions.ground + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +final case class IntTerm(value: Int) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/MinusInfTerm.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/MinusInfTerm.scala new file mode 100644 index 0000000..8c54c02 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/MinusInfTerm.scala @@ -0,0 +1,31 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions.ground + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +sealed abstract class MinusInfTerm extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} + + +case object MinusInfTerm extends MinusInfTerm diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/Now.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/Now.scala new file mode 100644 index 0000000..b2e4660 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/Now.scala @@ -0,0 +1,30 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions.ground + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +sealed abstract class Now extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} + +case object Now extends Now diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/PlusInfTerm.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/PlusInfTerm.scala new file mode 100644 index 0000000..80e035e --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/PlusInfTerm.scala @@ -0,0 +1,31 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions.ground + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +sealed abstract class PlusInfTerm extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} + + +case object PlusInfTerm extends PlusInfTerm \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/StringTerm.scala b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/StringTerm.scala new file mode 100644 index 0000000..27d1b68 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/expressions/ground/StringTerm.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.expressions.ground + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression} + +final case class StringTerm(value: String) extends Expression { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/Assert.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/Assert.scala new file mode 100644 index 0000000..26e2602 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/Assert.scala @@ -0,0 +1,29 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression, Statement} + + +case class Assert(expression: Expression, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/AssignGlobalReference.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/AssignGlobalReference.scala new file mode 100644 index 0000000..4945bfe --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/AssignGlobalReference.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression, Reference, Statement} + +case class AssignGlobalReference(reference: Reference, value: Expression, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/AssignLocalReference.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/AssignLocalReference.scala new file mode 100644 index 0000000..2a1f7d7 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/AssignLocalReference.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression, Reference, Statement} + +case class AssignLocalReference(reference: Reference, value: Expression, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/Call.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/Call.scala new file mode 100644 index 0000000..349366c --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/Call.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression, Statement} + +case class Call(procedureName: String, arguments: Seq[Expression], lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/Cancel.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/Cancel.scala new file mode 100644 index 0000000..f4a9077 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/Cancel.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Reference, Statement} + +case class Cancel(eventName: String, eventNoticeReference: Option[Reference], lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/CreateEntity.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/CreateEntity.scala new file mode 100644 index 0000000..7ddf9c1 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/CreateEntity.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Reference, Statement} + +case class CreateEntity(entityType: String, reference: Option[Reference], lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/CreateFifoQueue.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/CreateFifoQueue.scala new file mode 100644 index 0000000..f568757 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/CreateFifoQueue.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Statement} + +case class CreateFifoQueue(queueName: String, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/CreateLifoQueue.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/CreateLifoQueue.scala new file mode 100644 index 0000000..94cd62e --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/CreateLifoQueue.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Statement} + +case class CreateLifoQueue(queueName: String, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/CreateMap.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/CreateMap.scala new file mode 100644 index 0000000..ca47d72 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/CreateMap.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Statement} + +case class CreateMap(identifier: String, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/CreateSortedQueue.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/CreateSortedQueue.scala new file mode 100644 index 0000000..3168a90 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/CreateSortedQueue.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, SortOrder, Statement} + +case class CreateSortedQueue(queueName: String, propertyName: String, sortOrder: SortOrder.Value, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/DestroyEntity.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/DestroyEntity.scala new file mode 100644 index 0000000..6112b7f --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/DestroyEntity.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Reference, Statement} + +case class DestroyEntity(reference: Reference, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/DisableHeapCheck.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/DisableHeapCheck.scala new file mode 100644 index 0000000..c9c3443 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/DisableHeapCheck.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Statement} + +case class DisableHeapCheck(lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/Enqueue.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/Enqueue.scala new file mode 100644 index 0000000..f69df75 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/Enqueue.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Reference, Statement} + +case class Enqueue(queueName: String, reference: Reference, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/Exit.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/Exit.scala new file mode 100644 index 0000000..e6200b6 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/Exit.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Statement} + +case class Exit(lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/If.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/If.scala new file mode 100644 index 0000000..2a08141 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/If.scala @@ -0,0 +1,29 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast._ +import info.gianlucacosta.chronos.ast.expressions.Condition + +case class If(condition: Condition, thenPart: Block, elsePart: Option[Node], lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/Print.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/Print.scala new file mode 100644 index 0000000..d773988 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/Print.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression, Statement} + +case class Print(expression: Expression, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/Println.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/Println.scala new file mode 100644 index 0000000..0676cf5 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/Println.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression, Statement} + +case class Println(expression: Expression, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/ReadBoolean.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/ReadBoolean.scala new file mode 100644 index 0000000..6b7d7a3 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/ReadBoolean.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{Expression, AstVisitor, Reference, Statement} + +case class ReadBoolean(prompt: Expression, reference: Reference, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/ReadDouble.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/ReadDouble.scala new file mode 100644 index 0000000..ae1da4f --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/ReadDouble.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{Expression, AstVisitor, Reference, Statement} + +case class ReadDouble(prompt: Expression, reference: Reference, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/ReadInt.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/ReadInt.scala new file mode 100644 index 0000000..9ef5ada --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/ReadInt.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{Expression, AstVisitor, Reference, Statement} + +case class ReadInt(prompt: Expression, reference: Reference, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/ReadString.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/ReadString.scala new file mode 100644 index 0000000..7d9d021 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/ReadString.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{Expression, AstVisitor, Reference, Statement} + +case class ReadString(prompt: Expression, reference: Reference, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/Remove.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/Remove.scala new file mode 100644 index 0000000..85866b1 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/Remove.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Reference, Statement} + +case class Remove(queueName: String, reference: Reference, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/Return.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/Return.scala new file mode 100644 index 0000000..ae0fd89 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/Return.scala @@ -0,0 +1,29 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Statement} + + +case class Return(lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/ScheduleAfter.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/ScheduleAfter.scala new file mode 100644 index 0000000..f7625a3 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/ScheduleAfter.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression, Reference, Statement} + +case class ScheduleAfter(eventName: String, eventNoticeReference: Option[Reference], delay: Expression, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/ScheduleAt.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/ScheduleAt.scala new file mode 100644 index 0000000..82df57b --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/ScheduleAt.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression, Reference, Statement} + +case class ScheduleAt(eventName: String, eventNoticeReference: Option[Reference], instant: Expression, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/SetRandomSeed.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/SetRandomSeed.scala new file mode 100644 index 0000000..c6f17bb --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/SetRandomSeed.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.{AstVisitor, Expression, Statement} + +case class SetRandomSeed(randomSeed: Expression, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/ast/statements/While.scala b/src/main/scala/info/gianlucacosta/chronos/ast/statements/While.scala new file mode 100644 index 0000000..3b1a2c3 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/ast/statements/While.scala @@ -0,0 +1,29 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.ast.statements + +import info.gianlucacosta.chronos.ast.expressions.Condition +import info.gianlucacosta.chronos.ast.{AstVisitor, Block, Statement} + +case class While(condition: Condition, block: Block, lineNumber: Int) extends Statement { + override def accept[T](visitor: AstVisitor[T]): T = + visitor.visit(this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/ContextFrame.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/ContextFrame.scala new file mode 100644 index 0000000..de22b3b --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/ContextFrame.scala @@ -0,0 +1,144 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter + +import info.gianlucacosta.chronos.interpreter.atoms.{Atom, Entity, HeapAtom, StringAtom} +import info.gianlucacosta.chronos.interpreter.exceptions.{ExecutionException, IdentifierNotFoundException} +import info.gianlucacosta.chronos.interpreter.heap.HeapView + + +private class ContextFrame(heapView: HeapView, parentFrame: Option[ContextFrame]) { + private var variables = Map.empty[String, Atom] + private var _canRun = true + + + def this(heapView: HeapView) = this(heapView, None) + + def canRun = _canRun + + def terminate(): Unit = { + _canRun = false + } + + + def resolveReference(reference: RuntimeReference): Atom = { + val identifier = reference.identifier + + reference.parameter match { + case None => + resolveIdentifier(identifier) + + + case Some(entity: Entity) => + val propertyValue = entity(StringAtom(identifier)) + checkAtomValidity(propertyValue) + propertyValue + + + case Some(key: Atom) => + val resolvedMap = resolveIdentifier(identifier) + val retrievedValue = resolvedMap(key) + checkAtomValidity(retrievedValue) + retrievedValue + + case _ => throw new ExecutionException(s"Error while resolving the reference: '${reference}'") + } + } + + + def setReference(reference: RuntimeReference, value: Atom): Unit = { + val identifier = reference.identifier + + reference.parameter match { + case None => + setIdentifier(identifier, value) + + case Some(entity: Entity) => + entity(StringAtom(identifier)) = value + + + case Some(key: Atom) => + val resolvedMap = resolveIdentifier(identifier) + resolvedMap(key) = value + + case _ => throw new ExecutionException(s"Error while resolving the reference: '${reference}'") + } + } + + + def removeReference(reference: RuntimeReference): Unit = { + val identifier = reference.identifier + + reference.parameter match { + case None => + removeIdentifier(identifier) + + case Some(entity: Entity) => + entity -= StringAtom(identifier) + + case Some(key: Atom) => + val resolvedMap = resolveIdentifier(identifier) + resolvedMap -= key + } + } + + + private def resolveIdentifier(identifier: String): Atom = { + val resolvedAtom = variables.get(identifier) + + if (resolvedAtom.isDefined) { + checkAtomValidity(resolvedAtom.get) + resolvedAtom.get + } else if (parentFrame.isDefined) { + parentFrame.get.resolveIdentifier(identifier) + } else { + throw new IdentifierNotFoundException(identifier) + } + } + + + private def setIdentifier(identifier: String, atom: Atom): Unit = { + variables += (identifier -> atom) + } + + + private def removeIdentifier(identifier: String): Unit = { + if (!variables.contains(identifier)) { + if (parentFrame.isDefined) { + parentFrame.get.removeIdentifier(identifier) + } else { + throw new IdentifierNotFoundException(identifier) + } + } else { + variables -= identifier + } + } + + + private def checkAtomValidity(atom: Atom): Unit = { + atom match { + case heapAtom: HeapAtom => + heapView.assertAtomValidity(heapAtom) + + case _ => + } + } +} \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/ExecutingVisitor.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/ExecutingVisitor.scala new file mode 100644 index 0000000..f159ea2 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/ExecutingVisitor.scala @@ -0,0 +1,631 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter + +import info.gianlucacosta.chronos.ast._ +import info.gianlucacosta.chronos.ast.expressions._ +import info.gianlucacosta.chronos.ast.expressions.ground._ +import info.gianlucacosta.chronos.ast.statements._ +import info.gianlucacosta.chronos.interpreter.atoms._ +import info.gianlucacosta.chronos.interpreter.exceptions._ +import info.gianlucacosta.chronos.interpreter.queues.{FifoQueue, LifoQueue, SortedQueue} + + +private class ExecutingVisitor( + val simulationContext: SimulationContext, + val input: Input, + val output: Output + ) extends AstVisitor[Any] { + + override def visit(node: Node) = + node.accept(this) + + + @throws[InterruptedException] + override def visit(node: Program): Unit = { + setupSimulation(node) + + mainLoop() + + simulationContext.onSimulationEnded() + } + + + private def setupSimulation(programNode: Program): Unit = { + visit(programNode.globalStatements) + + programNode.events.foreach(visit) + programNode.procedures.foreach(visit) + + + val startEvent = programNode.startEvent + + val startEventNotice = + startEvent.map(event => new EventNotice(event.name, event)) + .getOrElse(throw new ExecutionException("Cannot find the program's start event")) + + executeEventNotice(startEventNotice) + } + + + private def mainLoop(): Unit = { + while (simulationContext.hasEventNotices) { + val eventNotices = simulationContext.fetchNextEventNotices() + + eventNotices.foreach(executeEventNotice) + } + } + + + override def visit(node: DisableHeapCheck): Unit = { + simulationContext.disableHeapCheck() + } + + + override def visit(node: Event): Unit = { + simulationContext.registerEvent(node) + } + + + override def visit(node: Procedure): Unit = { + simulationContext.registerProcedure(node) + } + + + private def executeEventNotice(eventNotice: EventNotice): Unit = { + val event = eventNotice.event + + simulationContext.pushNewFrame() + simulationContext.setReference(new RuntimeReference(event.name), eventNotice) + + visit(event.block) + + simulationContext.popCurrentFrame() + } + + + override def visit(node: Block): Unit = { + node.statements.takeWhile(statement => { + visit(statement) + + simulationContext.canRunProgram && simulationContext.canRunCurrentFrame + }) + } + + + override def visit(statement: Statement): Unit = { + if (Thread.interrupted()) { + throw new InterruptedException + } + + try { + statement.accept(this) + } catch { + case e: InterruptedException => throw e + case e: Exception => throw new StatementFailedException(statement, e) + } + } + + + override def visit(node: Return): Unit = { + simulationContext.terminateCurrentFrame() + } + + + override def visit(node: Exit): Unit = { + simulationContext.terminateProgram() + } + + + override def visit(node: Print): Unit = { + val outputAtom = visit(node.expression) + + output.print(outputAtom) + } + + + override def visit(node: Println): Unit = { + val outputAtom = visit(node.expression) + + output.println(outputAtom) + } + + + override def visit(node: AssignLocalReference): Unit = { + val identifier = node.reference.identifier + val parameterAtom = node.reference.parameter.map(visit) + val valueAtom = visit(node.value) + + val reference = new RuntimeReference(identifier, parameterAtom) + + simulationContext.setReference(reference, valueAtom) + } + + + override def visit(node: AssignGlobalReference): Unit = { + val identifier = node.reference.identifier + val parameterAtom = node.reference.parameter.map(visit) + val valueAtom = visit(node.value) + + val reference = new RuntimeReference(identifier, parameterAtom) + + + simulationContext.setGlobalReference(reference, valueAtom) + } + + + override def visit(node: Call): Unit = { + val procedure = simulationContext.getProcedure(node.procedureName) + val arguments = node.arguments + + if (arguments.size != procedure.params.size) { + throw new IllegalCallException(procedure, arguments) + } + + val boundParams = procedure.params zip arguments.map(visit) + + + simulationContext.pushNewFrame() + + boundParams.foreach(boundParam => { + val paramName = boundParam._1 + val argumentValue = boundParam._2 + simulationContext.setReference(new RuntimeReference(paramName), argumentValue) + }) + + visit(procedure.block) + + simulationContext.popCurrentFrame() + } + + + override def visit(node: Assert): Unit = { + val assertedAtom = visit(node.expression) + + assertedAtom.assert() + } + + + override def visit(node: CreateMap): Unit = { + val identifier = node.identifier + + val atomMap = new AtomMap + + simulationContext.setGlobalReference( + new RuntimeReference(identifier), + atomMap + ) + } + + + override def visit(node: CreateEntity): Unit = { + simulationContext.createEntity( + node.entityType, + node.reference.map(visit) + ) + } + + + override def visit(node: DestroyEntity): Unit = { + simulationContext.destroyEntity( + visit(node.reference) + ) + } + + + override def visit(node: ReadDouble): Unit = { + val prompt = visit(node.prompt).toStringAtom.value + val inputValue = input.readDouble(prompt) + + val reference = visit(node.reference) + + simulationContext.setReference(reference, inputValue) + } + + + override def visit(node: ReadInt): Unit = { + val prompt = visit(node.prompt).toStringAtom.value + val inputValue = input.readInt(prompt) + + val reference = visit(node.reference) + + simulationContext.setReference(reference, inputValue) + } + + + override def visit(node: ReadBoolean): Unit = { + val prompt = visit(node.prompt).toStringAtom.value + val inputValue = input.readBoolean(prompt) + + val reference = visit(node.reference) + + simulationContext.setReference(reference, inputValue) + } + + + override def visit(node: ReadString): Unit = { + val prompt = visit(node.prompt).toStringAtom.value + val inputValue = input.readString(prompt) + + val reference = visit(node.reference) + + simulationContext.setReference(reference, inputValue) + } + + + override def visit(node: CreateFifoQueue): Unit = { + simulationContext.addQueue( + node.queueName, + new FifoQueue + ) + } + + + override def visit(node: CreateLifoQueue): Unit = { + simulationContext.addQueue( + node.queueName, + new LifoQueue + ) + } + + + override def visit(node: CreateSortedQueue): Unit = { + simulationContext.addQueue( + node.queueName, + new SortedQueue(node.propertyName, node.sortOrder) + ) + } + + + override def visit(node: Enqueue): Unit = { + val queue = simulationContext.getQueue(node.queueName) + val reference = visit(node.reference) + val resolvedAtom = simulationContext.resolveReference(reference) + + + resolvedAtom match { + case entity: Entity => queue.enqueue(entity) + + case _ => throw new QueueException(s"Only entities can be inserted into queues, not '${resolvedAtom}'") + } + } + + + override def visit(node: Remove): Unit = { + val queue = simulationContext.getQueue(node.queueName) + val reference = visit(node.reference) + val resolvedAtom = simulationContext.resolveReference(reference) + + resolvedAtom match { + case entity: Entity => queue.remove(entity) + + case _ => throw new QueueException(s"Only entities can be removed from queues, not '${resolvedAtom}'") + } + } + + + override def visit(node: ScheduleAt): Unit = { + val eventName = node.eventName + val instantAtom = visit(node.instant) + + + val eventNoticeReference = node.eventNoticeReference + .map(visit) + .getOrElse(new RuntimeReference(eventName)) + + simulationContext.scheduleAt(eventName, eventNoticeReference, instantAtom) + } + + + override def visit(node: ScheduleAfter): Unit = { + val eventName = node.eventName + val delayAtom = visit(node.delay) + val instantAtom = delayAtom + DoubleAtom(simulationContext.currentTime) + + + + val eventNoticeReference = node.eventNoticeReference + .map(visit) + .getOrElse(new RuntimeReference(eventName)) + + simulationContext.scheduleAt(eventName, eventNoticeReference, instantAtom) + } + + + override def visit(node: Cancel): Unit = { + val eventName = node.eventName + + val eventNoticeReference = node.eventNoticeReference + .map(visit) + .getOrElse(new RuntimeReference(eventName)) + + simulationContext.cancel(eventName, eventNoticeReference) + } + + + override def visit(node: If): Unit = { + val conditionAtom = visit(node.condition).toBooleanAtom + + if (conditionAtom.value) { + visit(node.thenPart) + } else { + node.elsePart.foreach(visit) + } + } + + + override def visit(node: While): Unit = { + var conditionAtom = visit(node.condition).toBooleanAtom + + while (conditionAtom.value) { + if (conditionAtom.value) { + visit(node.block) + conditionAtom = visit(node.condition).toBooleanAtom + } + } + } + + + override def visit(node: SetRandomSeed): Any = { + val randomSeed = visit(node.randomSeed) + + randomSeed match { + case DoubleAtom(doubleSeed) => simulationContext.setRandomSeed(doubleSeed) + + case IntAtom(intSeed) => simulationContext.setRandomSeed(intSeed) + + case _ => throw new ExecutionException(s"Invalid random seed: '${randomSeed}'") + } + } + + + /* + * EXPRESSIONS + */ + + override def visit(expression: Expression): Atom = { + expression.accept(this).asInstanceOf[Atom] + } + + override def visit(expression: Or): Atom = { + val operands = expression.operands.map(visit) + + (operands.head /: operands.tail)(_ || _) + } + + + override def visit(expression: And): Atom = { + val operands = expression.operands.map(visit) + + (operands.head /: operands.tail)(_ && _) + } + + + override def visit(expression: Not): Atom = { + val operand = visit(expression.operand) + + !operand + } + + + override def visit(expression: Comparison): Atom = { + val leftAtom = visit(expression.left) + val rightAtom = visit(expression.right) + + val result = expression.operator match { + case ComparisonOperator.Equal => + (leftAtom compare rightAtom) == 0 + + case ComparisonOperator.NotEqual => + (leftAtom compare rightAtom) != 0 + + case ComparisonOperator.Greater => + leftAtom > rightAtom + + case ComparisonOperator.GreaterEqual => + leftAtom >= rightAtom + + + case ComparisonOperator.Less => + leftAtom < rightAtom + + + case ComparisonOperator.LessEqual => + leftAtom <= rightAtom + } + + BooleanAtom(result) + } + + + override def visit(expression: AlgebraicSum): Atom = { + val addendAtoms = expression.addends.map(visit) + + (addendAtoms.head /: addendAtoms.tail)(_ + _) + } + + + override def visit(expression: UnaryMinus): Atom = { + val originalExpressionAtom = visit(expression.operand) + + -originalExpressionAtom + } + + + override def visit(expression: Multiplication): Atom = { + val leftAtom = visit(expression.left) + val rightAtom = visit(expression.right) + + leftAtom * rightAtom + } + + + override def visit(expression: Division): Atom = { + val leftAtom = visit(expression.left) + val rightAtom = visit(expression.right) + + leftAtom / rightAtom + } + + + override def visit(expression: ReferenceValue): Atom = { + val reference = visit(expression.reference) + + simulationContext.resolveReference(reference) + } + + + override def visit(expression: Dequeue): Entity = { + val queueName = expression.queueName + val queue = simulationContext.getQueue(queueName) + + if (queue.isEmpty) { + throw new QueueException(s"Queue '${queueName}' is empty") + } + + queue.dequeue() + } + + + override def visit(expression: CastToDouble): DoubleAtom = { + val sourceAtom = visit(expression.expression) + + sourceAtom.toDoubleAtom + } + + + override def visit(expression: CastToInt): IntAtom = { + val sourceAtom = visit(expression.expression) + + sourceAtom.toIntAtom + } + + + override def visit(expression: CastToBoolean): BooleanAtom = { + val sourceAtom = visit(expression.expression) + + sourceAtom.toBooleanAtom + } + + + override def visit(expression: CastToString): StringAtom = { + val sourceAtom = visit(expression.expression) + + sourceAtom.toStringAtom + } + + + override def visit(expression: Floor): IntAtom = { + val sourceAtom = visit(expression.expression) + + sourceAtom.getFloor + } + + + override def visit(expression: Ceil): IntAtom = { + val sourceAtom = visit(expression.expression) + + sourceAtom.getCeil + } + + + override def visit(expression: IsEmpty): BooleanAtom = { + val queue = simulationContext.getQueue(expression.queueName) + + BooleanAtom(queue.isEmpty) + } + + + override def visit(expression: UniformRandom): DoubleAtom = { + val minValue = visit(expression.minValue).toDoubleAtom.value + val maxValue = visit(expression.maxValue).toDoubleAtom.value + + val randomBase = simulationContext.nextRandomDouble() + + DoubleAtom(minValue + randomBase * (maxValue - minValue)) + } + + + override def visit(expression: UniformIntRandom): IntAtom = { + val minValue = visit(expression.minValue).toIntAtom.value + val maxValue = visit(expression.maxValue).toIntAtom.value + + val randomBase = simulationContext.nextRandomDouble() + + IntAtom(math.floor(minValue + randomBase * (maxValue - minValue + 1)).toInt) + } + + override def visit(expression: ExponentialRandom): DoubleAtom = { + val averageValue = visit(expression.averageValue).toDoubleAtom.value + + val randomSalt = simulationContext.nextRandomDouble() + DoubleAtom(-averageValue * math.log(1 - randomSalt)) + } + + + override def visit(expression: DoubleTerm): DoubleAtom = { + DoubleAtom(expression.value) + } + + + override def visit(expression: IntTerm): IntAtom = { + IntAtom(expression.value) + } + + + override def visit(expression: BooleanTerm): BooleanAtom = { + BooleanAtom(expression.value) + } + + override def visit(expression: StringTerm): StringAtom = { + StringAtom(expression.value) + } + + override def visit(expression: PlusInfTerm): InfinityAtom = { + PlusInfAtom + } + + + override def visit(expression: MinusInfTerm): InfinityAtom = { + MinusInfAtom + } + + override def visit(expression: Now): DoubleAtom = { + DoubleAtom(simulationContext.currentTime) + } + + + override def visit(expression: Condition): Atom = { + if (Thread.interrupted()) { + throw new InterruptedException + } + + visit(expression.expression) + } + + + override def visit(reference: Reference): RuntimeReference = { + val identifier = reference.identifier + val parameter = reference.parameter.map(visit) + + new RuntimeReference(identifier, parameter) + } +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/Input.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/Input.scala new file mode 100644 index 0000000..84569f8 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/Input.scala @@ -0,0 +1,33 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter + +import info.gianlucacosta.chronos.interpreter.atoms._ + +trait Input { + def readInt(prompt: String): IntAtom + + def readDouble(prompt: String): DoubleAtom + + def readBoolean(prompt: String): BooleanAtom + + def readString(prompt: String): StringAtom +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/Interpreter.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/Interpreter.scala new file mode 100644 index 0000000..34eec91 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/Interpreter.scala @@ -0,0 +1,61 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter + +import info.gianlucacosta.chronos.ast.Program + +/** + * Virtual machine facade. + * + * @param input The input source + * @param output The output target + */ +class Interpreter(input: Input, output: Output) { + /** + * Runs the given program. + * + * In case of any exception *except* InterruptedException, + * the interpretation will stop and the exception will be passed + * to the output target. + * + * InterruptedException, on the other hand, will propagate as a Java-checked + * exception. + * + * @param program The root node of a parsed program - which might have been generated by BasicAstBuilder. + */ + @throws[InterruptedException] + def run(program: Program): Unit = { + val simulationContext = new SimulationContext + + val executingVisitor = new ExecutingVisitor( + simulationContext, + input, + output + ) + + try { + executingVisitor.visit(program) + } catch { + case e: InterruptedException => throw e; + case e: Exception => output.printException(e) + } + } +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/Output.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/Output.scala new file mode 100644 index 0000000..3625dd2 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/Output.scala @@ -0,0 +1,31 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter + +import info.gianlucacosta.chronos.interpreter.atoms.Atom + +trait Output { + def print(atom: Atom) + + def println(atom: Atom) + + def printException(exception: Exception) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/RuntimeReference.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/RuntimeReference.scala new file mode 100644 index 0000000..f8e6762 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/RuntimeReference.scala @@ -0,0 +1,31 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter + +import info.gianlucacosta.chronos.interpreter.atoms.Atom + +private class RuntimeReference(val identifier: String, val parameter: Option[Atom]) { + def this(identifier: String) = this(identifier, None) + + override def toString: String = s"${identifier}${ + parameter.map(parameter => s"(${parameter})").getOrElse("") + }" +} \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/SimulationContext.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/SimulationContext.scala new file mode 100644 index 0000000..0b7bb9a --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/SimulationContext.scala @@ -0,0 +1,253 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter + +import info.gianlucacosta.chronos.ast.{Event, Procedure} +import info.gianlucacosta.chronos.interpreter.atoms._ +import info.gianlucacosta.chronos.interpreter.exceptions._ +import info.gianlucacosta.chronos.interpreter.heap.{ActiveHeap, DisabledHeap, Heap} +import info.gianlucacosta.chronos.interpreter.queues.EntityQueue + +import scala.util.Random + + +private class SimulationContext { + private var queues = Map.empty[String, EntityQueue] + + private var events = Map.empty[String, Event] + private var procedures = Map.empty[String, Procedure] + + private var heap: Heap = new ActiveHeap + + private val globalFrame = new ContextFrame(heap) + private var frameStack = List(globalFrame) + + private val random = new Random() + + private val timeline = new Timeline + + + /* + * QUEUES + */ + + def addQueue(queueName: String, queue: EntityQueue): Unit = { + if (queues.contains(queueName)) { + throw new QueueException(s"Queue '${queueName}' already defined") + } + + queues += (queueName -> queue) + } + + + def getQueue(queueName: String): EntityQueue = { + if (!queues.contains(queueName)) { + throw new QueueException(s"Queue '${queueName}' not found") + } + + queues(queueName) + } + + + /* + * GLOBAL ELEMENTS + */ + + + def registerEvent(event: Event): Unit = { + events += (event.name -> event) + } + + + def registerProcedure(procedure: Procedure): Unit = { + procedures += (procedure.name -> procedure) + } + + + def getProcedure(procedureName: String): Procedure = { + procedures.getOrElse( + procedureName, + throw new ProcedureNotFoundException(procedureName) + ) + } + + + def disableHeapCheck(): Unit = { + require(heap.isEmpty) + heap = new DisabledHeap + } + + + /* + * FRAMES + */ + + private def currentFrame = frameStack.head + + def pushNewFrame(): Unit = { + val localFrame = new ContextFrame(heap, Some(globalFrame)) + frameStack = localFrame :: frameStack + } + + + def terminateCurrentFrame(): Unit = { + currentFrame.terminate() + } + + + def popCurrentFrame(): Unit = { + frameStack = frameStack.tail + + require(frameStack.nonEmpty) + } + + + def terminateProgram(): Unit = { + globalFrame.terminate() + } + + + def canRunCurrentFrame = currentFrame.canRun + + def canRunProgram = globalFrame.canRun + + + /* + * REFERENCES + */ + + def resolveReference(reference: RuntimeReference): Atom = { + currentFrame.resolveReference(reference) + } + + + def setReference(reference: RuntimeReference, value: Atom): Unit = { + currentFrame.setReference(reference, value) + } + + + def setGlobalReference(reference: RuntimeReference, value: Atom): Unit = { + globalFrame.setReference(reference, value) + } + + + /* + * HEAP OPERATIONS + */ + + def createEntity(entityType: String, reference: Option[RuntimeReference]): Unit = { + val entityName = reference.map(_.identifier).getOrElse(entityType) + + val relatedEvent = events.get(entityType) + val entity = + relatedEvent.map(new EventNotice(entityName, _)) + .getOrElse(new Entity(entityName, entityType)) + + setReference( + reference.getOrElse(new RuntimeReference(entityName)), + entity) + + heap.allocate(entity) + } + + + def destroyEntity(reference: RuntimeReference): Unit = { + val resolvedObject = resolveReference(reference) + + resolvedObject match { + case resolvedEntity: Entity => + heap.deallocate(resolvedEntity) + currentFrame.removeReference(reference) + + + case _ => throw new HeapException(s"Cannot delete object '${resolvedObject}'") + } + } + + + /* + * TIMELINE + */ + + + def scheduleAt(eventName: String, eventNoticeReference: RuntimeReference, instantAtom: Atom): Unit = { + val instant = instantAtom match { + case DoubleAtom(doubleInstant) => doubleInstant + case IntAtom(intInstant) => intInstant.toDouble + case _ => throw new SchedulingException(s"Invalid scheduling instant: '${instantAtom}'") + } + + + val resolvedEventNotice = resolveReference(eventNoticeReference) + + resolvedEventNotice match { + case eventNotice: EventNotice => + if (eventNotice.event.name != eventName) { + throw new SchedulingException(s"The event notice is related to the event '${eventNotice.event.name}', not '${eventName}'") + } + + timeline.scheduleAt(instant, eventNotice) + + case _ => + throw new SchedulingException(s"Only event notices can be schedules, not '${resolvedEventNotice}'") + } + } + + + def cancel(eventName: String, eventNoticeReference: RuntimeReference): Unit = { + val resolvedEventNotice = resolveReference(eventNoticeReference) + + resolvedEventNotice match { + case eventNotice: EventNotice => + if (eventNotice.event.name != eventName) { + throw new SchedulingException(s"The event notice is related to the event '${eventNotice.event.name}', not '${eventName}'") + } + + timeline.cancel(eventName, eventNotice) + case _ => + throw new SchedulingException(s"Only event notices can be schedules, not '${resolvedEventNotice}'") + } + } + + def currentTime = timeline.currentTime + + def hasEventNotices = timeline.hasEventNotices + + def fetchNextEventNotices(): List[EventNotice] = timeline.fetchNextEventNotices() + + + def onSimulationEnded(): Unit = { + heap.assertEmpty() + } + + /* + * MISCELLANEOUS + */ + + def setRandomSeed(randomSeed: Double): Unit = { + random.setSeed(randomSeed.toLong) + } + + + def nextRandomDouble(): Double = { + random.nextDouble() + } +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/Timeline.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/Timeline.scala new file mode 100644 index 0000000..f69d977 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/Timeline.scala @@ -0,0 +1,86 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter + +import info.gianlucacosta.chronos.interpreter.atoms.EventNotice +import info.gianlucacosta.chronos.interpreter.exceptions.SchedulingException + +import scala.collection.immutable.TreeMap + +private class Timeline { + private var _currentTime: Double = 0 + + def currentTime = _currentTime + + private var instantsToNoticesMap = TreeMap.empty[Double, List[EventNotice]] + private var noticesToInstantsMap = Map.empty[EventNotice, Double] + + def hasEventNotices = + instantsToNoticesMap.nonEmpty + + + def fetchNextEventNotices(): List[EventNotice] = { + require(hasEventNotices) + + val currentCoordinates = instantsToNoticesMap.head + + _currentTime = currentCoordinates._1 + val eventNoticesAtInstant = currentCoordinates._2.reverse + + instantsToNoticesMap -= _currentTime + eventNoticesAtInstant.foreach(noticesToInstantsMap -= _) + + eventNoticesAtInstant + } + + + def scheduleAt(instant: Double, eventNotice: EventNotice): Unit = { + if (instant < _currentTime) { + throw new SchedulingException( + s"At time ${_currentTime}, it is impossible to schedule event for time ${instant}" + ) + } + + if (noticesToInstantsMap contains eventNotice) { + throw new SchedulingException(s"Event notice already scheduled: ${eventNotice}") + } + + val eventNoticesAtInstant = + eventNotice :: instantsToNoticesMap.getOrElse(instant, Nil) + + instantsToNoticesMap += (instant -> eventNoticesAtInstant) + noticesToInstantsMap += (eventNotice -> instant) + } + + + def cancel(eventName: String, eventNotice: EventNotice): Unit = { + val instant = noticesToInstantsMap.getOrElse(eventNotice, + throw new SchedulingException( + s"Cannot cancel this unscheduled event notice - ${eventNotice}" + ) + ) + + val filteredNoticesAtInstant = instantsToNoticesMap(instant).filter(!eventNotice.eq(_)) + instantsToNoticesMap += (instant -> filteredNoticesAtInstant) + + noticesToInstantsMap -= eventNotice + } +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/Atom.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/Atom.scala new file mode 100644 index 0000000..1bdcf90 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/Atom.scala @@ -0,0 +1,68 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.atoms + +import info.gianlucacosta.chronos.interpreter.exceptions.{IllegalBinaryOperandsException, IllegalUnaryOperandException} + +/** + * Atoms are the fundamental runtime values manipulated by the virtual machine. + */ +trait Atom extends Ordered[Atom] { + def ||(right: Atom): Atom = throw new IllegalBinaryOperandsException("or", this, right) + + def &&(right: Atom): Atom = throw new IllegalBinaryOperandsException("and", this, right) + + def unary_!(): Atom = throw new IllegalUnaryOperandException("not", this) + + + def unary_-(): Atom = throw new IllegalUnaryOperandException("unary -", this) + + def +(right: Atom): Atom = throw new IllegalBinaryOperandsException("+", this, right) + + def -(right: Atom): Atom = throw new IllegalBinaryOperandsException("-", this, right) + + def *(right: Atom): Atom = throw new IllegalBinaryOperandsException("*", this, right) + + def /(right: Atom): Atom = throw new IllegalBinaryOperandsException("/", this, right) + + def compare(right: Atom): Int = throw new IllegalBinaryOperandsException("comparison", this, right) + + def apply(index: Atom): Atom = throw new IllegalUnaryOperandException("indexing", this) + + def update(index: Atom, value: Atom): Unit = throw new IllegalUnaryOperandException("indexing", this) + + def -=(index: Atom): Unit = throw new IllegalUnaryOperandException("indexing", this) + + + def toDoubleAtom: DoubleAtom = throw new IllegalUnaryOperandException("Cast to double", this) + + def toIntAtom: IntAtom = throw new IllegalUnaryOperandException("Cast to int", this) + + def toBooleanAtom: BooleanAtom = throw new IllegalUnaryOperandException("Cast to boolean", this) + + def toStringAtom: StringAtom = StringAtom(this.toString) + + def getFloor: IntAtom = throw new IllegalUnaryOperandException("Floor", this) + + def getCeil: IntAtom = throw new IllegalUnaryOperandException("Ceil", this) + + def assert(): Unit = throw new IllegalUnaryOperandException("Assert", this) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/AtomMap.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/AtomMap.scala new file mode 100644 index 0000000..a7b519e --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/AtomMap.scala @@ -0,0 +1,65 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.atoms + +import info.gianlucacosta.chronos.interpreter.exceptions.{IllegalIndexTypeException, MapKeyNotFoundException} + +class AtomMap extends Atom { + var internalMap = Map.empty[Atom, Atom] + + override def apply(key: Atom): Atom = { + key match { + case _: ImmutableAtom => + internalMap.getOrElse( + key, + + throw new MapKeyNotFoundException(key) + ) + + case _ => throw new IllegalIndexTypeException(s"Invalid key: '${key}'") + } + } + + + override def update(key: Atom, value: Atom): Unit = { + key match { + case _: ImmutableAtom => + internalMap += (key -> value) + + case _ => throw new IllegalIndexTypeException(s"Invalid key: '${key}'") + } + } + + + override def -=(key: Atom): Unit = { + key match { + case _: ImmutableAtom => + if (!internalMap.contains(key)) { + throw new MapKeyNotFoundException(key) + } + internalMap -= key + + case _ => throw new IllegalIndexTypeException(s"Invalid key: '${key}'") + } + } + + override def toString = s"Map(${internalMap.mkString(",")})" +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/BooleanAtom.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/BooleanAtom.scala new file mode 100644 index 0000000..bc86a09 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/BooleanAtom.scala @@ -0,0 +1,73 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.atoms + +import info.gianlucacosta.chronos.interpreter.exceptions.AssertionFailedException + +case object BooleanAtom { + val TrueString = "true" + val FalseString = "false" +} + +case class BooleanAtom(value: Boolean) extends ImmutableAtom { + + override def ||(right: Atom): Atom = + right match { + case BooleanAtom(rightBoolean) => BooleanAtom(value || rightBoolean) + + case _ => super.||(right) + } + + + override def &&(right: Atom): Atom = + right match { + case BooleanAtom(rightBoolean) => BooleanAtom(value && rightBoolean) + + case _ => super.&&(right) + } + + + override def unary_!(): Atom = + BooleanAtom(!value) + + + override def compare(right: Atom): Int = + right match { + case BooleanAtom(rightBoolean) => value compareTo rightBoolean + + case _ => super.compare(right) + } + + + override def toDoubleAtom: DoubleAtom = DoubleAtom(if (value) 1 else 0) + + + override def toIntAtom: IntAtom = IntAtom(if (value) 1 else 0) + + override def toBooleanAtom: BooleanAtom = this + + + override def assert(): Unit = if (!value) { + throw new AssertionFailedException + } + + override def toString: String = if (value) BooleanAtom.TrueString else BooleanAtom.FalseString +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/DoubleAtom.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/DoubleAtom.scala new file mode 100644 index 0000000..67f142d --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/DoubleAtom.scala @@ -0,0 +1,103 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.atoms + + +case class DoubleAtom(value: Double) extends ImmutableAtom { + override def unary_-(): Atom = DoubleAtom(-value) + + override def +(right: Atom): Atom = + right match { + case DoubleAtom(rightDouble) => DoubleAtom(value + rightDouble) + case IntAtom(rightInt) => DoubleAtom(value + rightInt) + case PlusInfAtom => PlusInfAtom + case MinusInfAtom => MinusInfAtom + case StringAtom(rightString) => StringAtom(value.toString + rightString) + case _ => super.+(right) + } + + override def -(right: Atom): Atom = + right match { + case DoubleAtom(rightDouble) => DoubleAtom(value - rightDouble) + case IntAtom(rightInt) => DoubleAtom(value - rightInt) + case PlusInfAtom => MinusInfAtom + case MinusInfAtom => PlusInfAtom + case _ => super.-(right) + } + + override def *(right: Atom): Atom = + right match { + case DoubleAtom(rightDouble) => + DoubleAtom(value * rightDouble) + + case IntAtom(rightInt) => + DoubleAtom(value * rightInt) + + case inf: InfinityAtom => + InfinityAtom.fromSign(Math.signum(value) * inf.sign) + + case _ => super.*(right) + } + + override def /(right: Atom): Atom = + right match { + case DoubleAtom(0) => + InfinityAtom.fromSign(value) + + case DoubleAtom(rightDouble) => + DoubleAtom(value / rightDouble) + + case IntAtom(0) => + InfinityAtom.fromSign(value) + + case IntAtom(rightInt) => + DoubleAtom(value / rightInt) + + case inf: InfinityAtom => DoubleAtom(0) + + case _ => super./(right) + } + + + override def compare(right: Atom): Int = + right match { + case DoubleAtom(rightDouble: Double) => value compareTo rightDouble + case IntAtom(rightInt: Int) => value compareTo rightInt + case PlusInfAtom => -1 + case MinusInfAtom => +1 + case _ => super.compare(right) + } + + + override def toDoubleAtom: DoubleAtom = this + + override def toIntAtom: IntAtom = IntAtom(value.toInt) + + override def toBooleanAtom: BooleanAtom = BooleanAtom(value != 0) + + + override def getFloor: IntAtom = IntAtom(math.floor(value).toInt) + + override def getCeil: IntAtom = IntAtom(math.ceil(value).toInt) + + + override def toString: String = value.toString +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/Entity.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/Entity.scala new file mode 100644 index 0000000..5733b90 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/Entity.scala @@ -0,0 +1,76 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.atoms + +import info.gianlucacosta.chronos.interpreter.exceptions.{IllegalIndexTypeException, PropertyNotFoundException} + +class Entity(val name: String, val entityType: String) extends Atom with HeapAtom { + protected var properties = Map.empty[String, Atom] + + + override def compare(right: Atom): Int = { + right match { + case rightEntity: Entity => + System.identityHashCode(this) compare System.identityHashCode(rightEntity) + + case _ => super.compare(right) + } + } + + override def apply(property: Atom): Atom = { + property match { + case StringAtom(propertyName) => + properties.getOrElse( + propertyName, + + throw new PropertyNotFoundException(property) + ) + + case _ => throw new IllegalIndexTypeException(s"Invalid value for property name: '${property}'") + } + } + + override def update(property: Atom, value: Atom): Unit = { + property match { + case StringAtom(propertyName) => + properties += (propertyName -> value) + + case _ => throw new IllegalIndexTypeException(s"Invalid value for property name: '${property}'") + } + } + + + override def -=(property: Atom): Unit = { + property match { + case StringAtom(propertyName) => + if (!properties.contains(propertyName)) { + throw new PropertyNotFoundException(property) + } + properties -= propertyName + + case _ => throw new IllegalIndexTypeException(s"Invalid property name: '${property}'") + } + } + + + override def toString = + s"Entity(name: ${name}, type: ${entityType}) {${properties.mkString(",")}}" +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/EventNotice.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/EventNotice.scala new file mode 100644 index 0000000..34b61ff --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/EventNotice.scala @@ -0,0 +1,28 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.atoms + +import info.gianlucacosta.chronos.ast.Event + +class EventNotice(name: String, val event: Event) extends Entity(name, event.name) { + override def toString = + s"Event notice '${name}' for event '${event.name}' {${properties.mkString(",")}}" +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/HeapAtom.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/HeapAtom.scala new file mode 100644 index 0000000..1ebdc15 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/HeapAtom.scala @@ -0,0 +1,23 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.atoms + +trait HeapAtom extends Atom diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/ImmutableAtom.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/ImmutableAtom.scala new file mode 100644 index 0000000..93736ed --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/ImmutableAtom.scala @@ -0,0 +1,23 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.atoms + +trait ImmutableAtom extends Atom diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/InfinityAtom.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/InfinityAtom.scala new file mode 100644 index 0000000..7d8fc9f --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/InfinityAtom.scala @@ -0,0 +1,69 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.atoms + +object InfinityAtom { + def fromSign(sign: Double): InfinityAtom = + if (sign > 0) { + PlusInfAtom + } else if (sign < 0) { + MinusInfAtom + } else { + throw new IllegalArgumentException("Inf without sign demanded (for example, in ∞ * 0)") + } +} + + +trait InfinityAtom extends ImmutableAtom { + val sign: Int + + override def unary_-(): Atom = InfinityAtom.fromSign(-sign) + + override def *(right: Atom): Atom = + right match { + case DoubleAtom(rightDouble) => + InfinityAtom.fromSign(this.sign * Math.signum(rightDouble)) + + case IntAtom(rightInt) => + InfinityAtom.fromSign(this.sign * Math.signum(rightInt)) + + case rightInf: InfinityAtom => + InfinityAtom.fromSign(this.sign * rightInf.sign) + + case _ => super.*(right) + + } + + + override def /(right: Atom): Atom = + right match { + case DoubleAtom(rightDouble) => + InfinityAtom.fromSign(this.sign * Math.signum(rightDouble)) + + case IntAtom(rightInt) => + InfinityAtom.fromSign(this.sign * Math.signum(rightInt)) + + + case _ => super./(right) + } + + override def toBooleanAtom: BooleanAtom = BooleanAtom(true) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/IntAtom.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/IntAtom.scala new file mode 100644 index 0000000..f28e048 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/IntAtom.scala @@ -0,0 +1,99 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.atoms + +case class IntAtom(value: Int) extends ImmutableAtom { + override def unary_-(): Atom = IntAtom(-value) + + override def +(right: Atom): Atom = + right match { + case DoubleAtom(rightDouble) => DoubleAtom(value + rightDouble) + case IntAtom(rightInt) => IntAtom(value + rightInt) + case PlusInfAtom => PlusInfAtom + case MinusInfAtom => MinusInfAtom + case StringAtom(rightString) => StringAtom(value.toString + rightString) + case _ => super.+(right) + } + + + override def -(right: Atom): Atom = + right match { + case DoubleAtom(rightDouble) => DoubleAtom(value - rightDouble) + case IntAtom(rightInt) => IntAtom(value - rightInt) + case PlusInfAtom => MinusInfAtom + case MinusInfAtom => PlusInfAtom + case _ => super.-(right) + } + + + override def *(right: Atom): Atom = + right match { + case DoubleAtom(rightDouble) => + DoubleAtom(value * rightDouble) + + case IntAtom(rightInt) => + IntAtom(value * rightInt) + + case inf: InfinityAtom => + InfinityAtom.fromSign(Math.signum(value) * inf.sign) + + case _ => super.*(right) + } + + + override def /(right: Atom): Atom = + right match { + case DoubleAtom(0) => InfinityAtom.fromSign(value) + case DoubleAtom(rightDouble) => + DoubleAtom(value / rightDouble) + + case IntAtom(0) => InfinityAtom.fromSign(value) + case IntAtom(rightInt) => + IntAtom(value / rightInt) + + case inf: InfinityAtom => IntAtom(0) + + case _ => super./(right) + } + + + override def compare(right: Atom): Int = + right match { + case DoubleAtom(rightDouble: Double) => value.toDouble compareTo rightDouble + case IntAtom(rightInt: Int) => value compareTo rightInt + case PlusInfAtom => -1 + case MinusInfAtom => +1 + case _ => super.compare(right) + } + + + override def toDoubleAtom: DoubleAtom = DoubleAtom(value) + + override def toIntAtom: IntAtom = this + + override def toBooleanAtom: BooleanAtom = BooleanAtom(value != 0) + + override def getFloor: IntAtom = this + + override def getCeil: IntAtom = this + + override def toString: String = value.toString +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/MinusInfAtom.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/MinusInfAtom.scala new file mode 100644 index 0000000..d14e6e8 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/MinusInfAtom.scala @@ -0,0 +1,58 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.atoms + + +case object MinusInfAtom extends InfinityAtom { + override val sign: Int = -1 + + + override def unary_-(): Atom = PlusInfAtom + + override def +(right: Atom): Atom = + right match { + case DoubleAtom(_) | IntAtom(_) => MinusInfAtom + case MinusInfAtom => MinusInfAtom + case StringAtom(rightString) => StringAtom(this.toString + rightString) + case _ => super.+(right) + } + + + override def -(right: Atom): Atom = + right match { + case DoubleAtom(_) | IntAtom(_) => MinusInfAtom + case PlusInfAtom => MinusInfAtom + case _ => super.-(right) + } + + + override def compare(right: Atom): Int = + right match { + case DoubleAtom(rightDouble: Double) => -1 + case IntAtom(rightInt: Int) => -1 + case PlusInfAtom => -1 + case MinusInfAtom => 0 + case _ => super.compare(right) + } + + + override def toString = "-∞" +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/PlusInfAtom.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/PlusInfAtom.scala new file mode 100644 index 0000000..0f06cb6 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/PlusInfAtom.scala @@ -0,0 +1,59 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.atoms + + +case object PlusInfAtom extends InfinityAtom { + override val sign: Int = +1 + + + override def unary_-(): Atom = MinusInfAtom + + override def +(right: Atom): Atom = + right match { + case DoubleAtom(_) | IntAtom(_) => PlusInfAtom + case PlusInfAtom => PlusInfAtom + case StringAtom(rightString) => StringAtom(this.toString + rightString) + case _ => super.+(right) + } + + + override def -(right: Atom): Atom = + right match { + case DoubleAtom(_) | IntAtom(_) => PlusInfAtom + case MinusInfAtom => PlusInfAtom + case _ => super.-(right) + + } + + + override def compare(right: Atom): Int = + right match { + case DoubleAtom(rightDouble: Double) => +1 + case IntAtom(rightInt: Int) => +1 + case PlusInfAtom => 0 + case MinusInfAtom => +1 + case _ => super.compare(right) + } + + + override def toString = "∞" +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/StringAtom.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/StringAtom.scala new file mode 100644 index 0000000..32ebe18 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/atoms/StringAtom.scala @@ -0,0 +1,44 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.atoms + + +case class StringAtom(value: String) extends ImmutableAtom { + override def +(right: Atom): Atom = StringAtom(value + right.toString) + + override def compare(right: Atom): Int = + right match { + case StringAtom(rightString) => value compareTo rightString + case _ => super.compare(right) + } + + + override def toDoubleAtom: DoubleAtom = DoubleAtom(value.toDouble) + + override def toIntAtom: IntAtom = IntAtom(value.toInt) + + override def toBooleanAtom: BooleanAtom = BooleanAtom(if (value == BooleanAtom.TrueString) true else false) + + override def toStringAtom: StringAtom = this + + + override def toString: String = value.toString +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/AssertionFailedException.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/AssertionFailedException.scala new file mode 100644 index 0000000..dbfba51 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/AssertionFailedException.scala @@ -0,0 +1,25 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.exceptions + +class AssertionFailedException extends ExecutionException( + "Assertion failed" +) \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/ExecutionException.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/ExecutionException.scala new file mode 100644 index 0000000..9efc11c --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/ExecutionException.scala @@ -0,0 +1,25 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.exceptions + +class ExecutionException(message: String, cause: Throwable) extends RuntimeException(message, cause) { + def this(message: String) = this(message, null) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/FailedReadException.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/FailedReadException.scala new file mode 100644 index 0000000..d663a5e --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/FailedReadException.scala @@ -0,0 +1,25 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.exceptions + +class FailedReadException(message: String) extends ExecutionException(message) { + +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/HeapException.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/HeapException.scala new file mode 100644 index 0000000..b9f8c6c --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/HeapException.scala @@ -0,0 +1,25 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.exceptions + +class HeapException(message: String) extends ExecutionException(message) { + +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/IdentifierNotFoundException.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/IdentifierNotFoundException.scala new file mode 100644 index 0000000..20fb116 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/IdentifierNotFoundException.scala @@ -0,0 +1,25 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.exceptions + +class IdentifierNotFoundException(val identifier: String) extends ExecutionException( + s"Identifier '${identifier}' not found" +) \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/IllegalBinaryOperandsException.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/IllegalBinaryOperandsException.scala new file mode 100644 index 0000000..9cf239e --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/IllegalBinaryOperandsException.scala @@ -0,0 +1,27 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.exceptions + +import info.gianlucacosta.chronos.interpreter.atoms.Atom + +class IllegalBinaryOperandsException(operation: String, left: Atom, right: Atom) extends ExecutionException( + s"Invalid operands for '${operation}': '${left}' and '${right}'" +) \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/IllegalCallException.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/IllegalCallException.scala new file mode 100644 index 0000000..5d72503 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/IllegalCallException.scala @@ -0,0 +1,27 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.exceptions + +import info.gianlucacosta.chronos.ast.{Expression, Procedure} + +class IllegalCallException(val procedure: Procedure, val arguments: Seq[Expression]) extends ExecutionException( + s"Procedure '${procedure.name}' expects ${procedure.params.size} parameters, not ${arguments.size}" +) \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/IllegalIndexTypeException.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/IllegalIndexTypeException.scala new file mode 100644 index 0000000..b9a034a --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/IllegalIndexTypeException.scala @@ -0,0 +1,23 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.exceptions + +class IllegalIndexTypeException(message: String) extends ExecutionException(message) diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/IllegalUnaryOperandException.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/IllegalUnaryOperandException.scala new file mode 100644 index 0000000..749ed80 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/IllegalUnaryOperandException.scala @@ -0,0 +1,27 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.exceptions + +import info.gianlucacosta.chronos.interpreter.atoms.Atom + +class IllegalUnaryOperandException(operation: String, target: Atom) extends ExecutionException( + s"Invalid operand for '${operation}': '${target}'" +) diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/MapKeyNotFoundException.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/MapKeyNotFoundException.scala new file mode 100644 index 0000000..f66bc97 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/MapKeyNotFoundException.scala @@ -0,0 +1,27 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.exceptions + +import info.gianlucacosta.chronos.interpreter.atoms.Atom + +class MapKeyNotFoundException(index: Atom) extends ExecutionException( + s"Map key '${index}' not found" +) diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/MissedDeallocationException.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/MissedDeallocationException.scala new file mode 100644 index 0000000..88bb6cc --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/MissedDeallocationException.scala @@ -0,0 +1,35 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.exceptions + +import info.gianlucacosta.chronos.interpreter.atoms.HeapAtom + +class MissedDeallocationException(val heapAtoms: Iterable[HeapAtom]) extends HeapException( +{ + val messageBuilder = new StringBuilder + messageBuilder.append("The following objects were not dellocated (use destroy to deallocate them):\n") + + + heapAtoms.foreach(item => messageBuilder.append(s"${item}\n")) + + messageBuilder.toString() +} +) \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/ProcedureNotFoundException.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/ProcedureNotFoundException.scala new file mode 100644 index 0000000..3c79140 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/ProcedureNotFoundException.scala @@ -0,0 +1,25 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.exceptions + +class ProcedureNotFoundException(procedureName: String) extends ExecutionException( + s"Procedure '${procedureName}' not found" +) diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/PropertyNotFoundException.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/PropertyNotFoundException.scala new file mode 100644 index 0000000..2b57b39 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/PropertyNotFoundException.scala @@ -0,0 +1,27 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.exceptions + +import info.gianlucacosta.chronos.interpreter.atoms.Atom + +class PropertyNotFoundException(val property: Atom) extends ExecutionException( + s"Property '${property}' not found" +) \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/QueueException.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/QueueException.scala new file mode 100644 index 0000000..6f86f2b --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/QueueException.scala @@ -0,0 +1,23 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.exceptions + +class QueueException(message: String) extends ExecutionException(message) \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/SchedulingException.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/SchedulingException.scala new file mode 100644 index 0000000..4bd3c75 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/SchedulingException.scala @@ -0,0 +1,23 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.exceptions + +class SchedulingException(message: String) extends ExecutionException(message) diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/StatementFailedException.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/StatementFailedException.scala new file mode 100644 index 0000000..2577672 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/exceptions/StatementFailedException.scala @@ -0,0 +1,29 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.exceptions + +import info.gianlucacosta.chronos.ast.Statement + +class StatementFailedException(val statement: Statement, cause: Throwable) extends ExecutionException( + s"Line: ${statement.lineNumber} - ${cause.getMessage}", + + cause +) diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/heap/ActiveHeap.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/heap/ActiveHeap.scala new file mode 100644 index 0000000..34ffb09 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/heap/ActiveHeap.scala @@ -0,0 +1,55 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.heap + +import info.gianlucacosta.chronos.interpreter.atoms.HeapAtom +import info.gianlucacosta.chronos.interpreter.exceptions.{HeapException, MissedDeallocationException} + +import scala.collection.mutable + +private[interpreter] class ActiveHeap extends Heap { + private val allocatedAtoms = new mutable.HashSet[HeapAtom]() + + + def assertAtomValidity(atom: HeapAtom) { + if (!allocatedAtoms.contains(atom)) { + throw new HeapException(s"${atom} has already been deallocated") + } + } + + + override def assertEmpty(): Unit = { + if (allocatedAtoms.nonEmpty) { + throw new MissedDeallocationException(allocatedAtoms) + } + } + + def allocate(atom: HeapAtom): Unit = { + allocatedAtoms += atom + } + + + def deallocate(atom: HeapAtom): Unit = { + allocatedAtoms -= atom + } + + override def isEmpty: Boolean = allocatedAtoms.isEmpty +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/heap/DisabledHeap.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/heap/DisabledHeap.scala new file mode 100644 index 0000000..bb27cc3 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/heap/DisabledHeap.scala @@ -0,0 +1,35 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.heap + +import info.gianlucacosta.chronos.interpreter.atoms.HeapAtom + +private[interpreter] class DisabledHeap extends Heap { + override def allocate(atom: HeapAtom): Unit = {} + + override def deallocate(atom: HeapAtom): Unit = {} + + override def assertAtomValidity(atom: HeapAtom): Unit = {} + + override def assertEmpty(): Unit = {} + + override def isEmpty: Boolean = true +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/heap/Heap.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/heap/Heap.scala new file mode 100644 index 0000000..28bfea6 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/heap/Heap.scala @@ -0,0 +1,29 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.heap + +import info.gianlucacosta.chronos.interpreter.atoms.HeapAtom + +private[interpreter] trait Heap extends HeapView { + def allocate(atom: HeapAtom): Unit + + def deallocate(atom: HeapAtom): Unit +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/heap/HeapView.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/heap/HeapView.scala new file mode 100644 index 0000000..75ac212 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/heap/HeapView.scala @@ -0,0 +1,31 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.heap + +import info.gianlucacosta.chronos.interpreter.atoms.HeapAtom + +private[interpreter] trait HeapView { + def assertAtomValidity(atom: HeapAtom) + + def assertEmpty(): Unit + + def isEmpty: Boolean +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/io/BasicInput.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/io/BasicInput.scala new file mode 100644 index 0000000..eeb9726 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/io/BasicInput.scala @@ -0,0 +1,78 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.io + +import info.gianlucacosta.chronos.interpreter.Input +import info.gianlucacosta.chronos.interpreter.atoms.{BooleanAtom, DoubleAtom, IntAtom} + +/** + * Simplifies input by implementing most functions in terms of readString(), + * which remains abtract. + */ +abstract class BasicInput extends Input { + override def readDouble(prompt: String): DoubleAtom = { + var result: Option[DoubleAtom] = None + + while (result.isEmpty) { + val inputString = readString(prompt) + + try { + result = Some(inputString.toDoubleAtom) + } catch { + case _: Exception => + } + } + + result.get + } + + override def readInt(prompt: String): IntAtom = { + var result: Option[IntAtom] = None + + while (result.isEmpty) { + val inputString = readString(prompt) + + try { + result = Some(inputString.toIntAtom) + } catch { + case _: Exception => + } + } + + result.get + } + + override def readBoolean(prompt: String): BooleanAtom = { + var result: Option[BooleanAtom] = None + + while (result.isEmpty) { + val inputString = readString(prompt) + + try { + result = Some(inputString.toBooleanAtom) + } catch { + case _: Exception => + } + } + + result.get + } +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/io/ConsoleInput.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/io/ConsoleInput.scala new file mode 100644 index 0000000..afefd8e --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/io/ConsoleInput.scala @@ -0,0 +1,42 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.io + +import java.io.{BufferedReader, InputStreamReader} + +import info.gianlucacosta.chronos.interpreter.atoms.StringAtom +import info.gianlucacosta.chronos.interpreter.exceptions.FailedReadException + +class ConsoleInput extends BasicInput { + private val lineReader = new BufferedReader(new InputStreamReader(System.in)) + + override def readString(prompt: String): StringAtom = { + print(prompt) + + val line = lineReader.readLine() + + if (line == null) { + throw new FailedReadException("Interrupted input") + } + + StringAtom(line) + } +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/io/ConsoleOutput.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/io/ConsoleOutput.scala new file mode 100644 index 0000000..fb83c15 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/io/ConsoleOutput.scala @@ -0,0 +1,44 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.io + +import info.gianlucacosta.chronos.interpreter.Output +import info.gianlucacosta.chronos.interpreter.atoms.Atom +import info.gianlucacosta.chronos.interpreter.exceptions.StatementFailedException + +class ConsoleOutput extends Output { + override def print(atom: Atom): Unit = { + System.out.print(atom) + } + + override def println(atom: Atom): Unit = { + System.out.println(atom) + } + + override def printException(exception: Exception): Unit = { + exception match { + case _: StatementFailedException => System.err.println(exception.getMessage) + case _ => exception.printStackTrace(System.err) + } + + System.exit(1) + } +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/queues/EntityQueue.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/queues/EntityQueue.scala new file mode 100644 index 0000000..31914ac --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/queues/EntityQueue.scala @@ -0,0 +1,33 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.queues + +import info.gianlucacosta.chronos.interpreter.atoms.Entity + +private[interpreter] trait EntityQueue { + def enqueue(entity: Entity) + + def dequeue(): Entity + + def remove(entity: Entity) + + def isEmpty: Boolean +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/queues/FifoQueue.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/queues/FifoQueue.scala new file mode 100644 index 0000000..b8ee197 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/queues/FifoQueue.scala @@ -0,0 +1,45 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.queues + +import info.gianlucacosta.chronos.interpreter.atoms.Entity + +import scala.collection.mutable.ListBuffer + + +private[interpreter] class FifoQueue extends EntityQueue { + private val internalQueue = ListBuffer.empty[Entity] + + override def enqueue(entity: Entity): Unit = { + internalQueue += entity + } + + override def dequeue(): Entity = { + internalQueue.remove(0) + } + + override def remove(entity: Entity): Unit = { + internalQueue -= entity + } + + override def isEmpty: Boolean = + internalQueue.isEmpty +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/queues/LifoQueue.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/queues/LifoQueue.scala new file mode 100644 index 0000000..976b5c1 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/queues/LifoQueue.scala @@ -0,0 +1,44 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.queues + +import info.gianlucacosta.chronos.interpreter.atoms.Entity + +import scala.collection.mutable.ListBuffer + +private[interpreter] class LifoQueue extends EntityQueue { + private val internalQueue = ListBuffer.empty[Entity] + + override def enqueue(entity: Entity): Unit = { + internalQueue.insert(0, entity) + } + + override def dequeue(): Entity = { + internalQueue.remove(0) + } + + override def remove(entity: Entity): Unit = { + internalQueue -= entity + } + + override def isEmpty: Boolean = + internalQueue.isEmpty +} diff --git a/src/main/scala/info/gianlucacosta/chronos/interpreter/queues/SortedQueue.scala b/src/main/scala/info/gianlucacosta/chronos/interpreter/queues/SortedQueue.scala new file mode 100644 index 0000000..4be96da --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/interpreter/queues/SortedQueue.scala @@ -0,0 +1,62 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter.queues + +import info.gianlucacosta.chronos.ast.SortOrder +import info.gianlucacosta.chronos.interpreter.atoms.{Entity, StringAtom} + +import scala.collection.mutable + +private[interpreter] class SortedQueue(propertyName: String, sortOrder: SortOrder.Type) extends EntityQueue { + + private case class QueueItem(entity: Entity) extends Ordered[QueueItem] { + override def compare(that: QueueItem): Int = { + val thisPropertyValue = entity(StringAtom(propertyName)) + val thatPropertyValue = that.entity(StringAtom(propertyName)) + + val ascendingComparisonResult = thisPropertyValue.compare(thatPropertyValue) + + if (sortOrder == SortOrder.Asc) + ascendingComparisonResult + else + -ascendingComparisonResult + } + } + + private val internalSet = mutable.TreeSet.empty[QueueItem] + + override def enqueue(entity: Entity): Unit = { + internalSet += new QueueItem(entity) + } + + override def dequeue(): Entity = { + val resultItem = internalSet.head + internalSet -= resultItem + + resultItem.entity + } + + override def remove(entity: Entity): Unit = { + internalSet.remove(QueueItem(entity)) + } + + override def isEmpty: Boolean = internalSet.isEmpty +} diff --git a/src/main/scala/info/gianlucacosta/chronos/parser/AstBuildingVisitor.scala b/src/main/scala/info/gianlucacosta/chronos/parser/AstBuildingVisitor.scala new file mode 100644 index 0000000..690d687 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/parser/AstBuildingVisitor.scala @@ -0,0 +1,598 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.parser + +import info.gianlucacosta.chronos.ast._ +import info.gianlucacosta.chronos.ast.expressions._ +import info.gianlucacosta.chronos.ast.expressions.ground._ +import info.gianlucacosta.chronos.ast.statements._ +import info.gianlucacosta.chronos.parser.ChronosParser._ +import org.antlr.v4.runtime.tree.{ParseTree, TerminalNode} + +import scala.collection.JavaConversions._ + +/** + * Low-level visitor, generating an AST from the concrete parsing tree. + * + * You can employ it at the end of a custom pipeline of customized ANTLR classes; + * for more basic usage, please consider BasicAstBuilder. + */ +class AstBuildingVisitor extends ChronosBaseVisitor[Node] { + + override def visitProgram(ctx: ProgramContext): Program = { + val globalStatements = Block( + ctx.globalStatement.map(visitGlobalStatement) + ) + + val events = ctx.event().map(visitEvent) + + val procedures = ctx.procedure().map(visitProcedure) + + Program(globalStatements, events, procedures) + } + + + override def visitGlobalStatement(ctx: GlobalStatementContext): Statement = { + visitContextAs[Statement](ctx.getChild(0), classOf[Statement]) + } + + + private def visitContextAs[T](parseTree: ParseTree, resultClass: Class[T]): T = { + val visitResult = parseTree.accept(this) + + if (!resultClass.isInstance(visitResult)) { + throw new IllegalArgumentException(s"Parsing interval: '${parseTree.getText}' The visit returned: '${visitResult}', not '${resultClass.getSimpleName}'") + } + + visitResult.asInstanceOf[T] + } + + + override def visitEvent(ctx: EventContext): Event = { + val eventName = ctx.IDENTIFIER().getText + val block = visitBlock(ctx.block()) + + Event(eventName, block) + } + + + override def visitProcedure(ctx: ProcedureContext): Procedure = { + val procedureName = ctx.IDENTIFIER().getText + val parameters = ctx.param().map(_.IDENTIFIER().getText) + val block = visitBlock(ctx.block()) + + Procedure(procedureName, parameters, block) + } + + + /* + * GLOBAL DIRECTIVES + */ + + override def visitDisableHeapCheck(ctx: DisableHeapCheckContext): DisableHeapCheck = { + DisableHeapCheck(ctx.getStart.getLine) + } + + + /* + * STATEMENT PARSING + */ + + + override def visitLocalStatement(ctx: LocalStatementContext): Statement = { + visitContextAs[Statement](ctx.getChild(0), classOf[Statement]) + } + + + override def visitPrint(ctx: PrintContext): Print = { + val expression = visitExpression(ctx.expression()) + + Print(expression, ctx.getStart.getLine) + } + + + override def visitPrintln(ctx: PrintlnContext): Println = { + val expression = visitExpression(ctx.expression()) + + Println(expression, ctx.getStart.getLine) + } + + + override def visitLocalAssignment(ctx: LocalAssignmentContext): AssignLocalReference = { + visitAssignment(ctx.assignment()) + } + + + override def visitImplicitGlobalAssignment(ctx: ImplicitGlobalAssignmentContext): AssignLocalReference = { + visitAssignment(ctx.assignment()) + } + + + override def visitAssignment(assignmentContext: AssignmentContext): AssignLocalReference = { + val reference = visitReference(assignmentContext.reference()) + val valueExpression = visitExpression(assignmentContext.expression()) + + AssignLocalReference( + reference, + valueExpression, + assignmentContext.getStart.getLine) + } + + + override def visitGlobalAssignment(ctx: GlobalAssignmentContext): AssignGlobalReference = { + val assignmentContext = ctx.assignment() + + val reference = visitReference(ctx.assignment().reference()) + val valueExpression = visitExpression(assignmentContext.expression()) + + AssignGlobalReference( + reference, + valueExpression, + ctx.getStart.getLine) + } + + + override def visitAssertion(ctx: AssertionContext): Assert = { + val expression = visitExpression(ctx.expression()) + + Assert(expression, ctx.getStart.getLine) + } + + + override def visitCreateMap(ctx: CreateMapContext): CreateMap = { + val mapName = ctx.IDENTIFIER().getText + + CreateMap(mapName, ctx.getStart.getLine) + } + + + override def visitCreateEntity(ctx: CreateEntityContext): CreateEntity = { + val entityType = ctx.IDENTIFIER().getText + + val reference = if (ctx.reference() != null) { + Some(visitReference(ctx.reference())) + } else { + None + } + + CreateEntity(entityType, reference, ctx.getStart.getLine) + } + + + override def visitDestroyEntity(ctx: DestroyEntityContext): DestroyEntity = { + val reference = visitReference(ctx.reference()) + + DestroyEntity(reference, ctx.getStart.getLine) + } + + + override def visitReturnStatement(ctx: ReturnStatementContext): Return = { + Return(ctx.getStart.getLine) + } + + + override def visitExit(ctx: ExitContext): Exit = { + Exit(ctx.getStart.getLine) + } + + + override def visitReadDouble(ctx: ReadDoubleContext): ReadDouble = { + val prompt = visitExpression(ctx.prompt().expression()); + val reference = visitReference(ctx.reference()) + ReadDouble(prompt, reference, ctx.getStart.getLine) + } + + override def visitReadInt(ctx: ReadIntContext): ReadInt = { + val prompt = visitExpression(ctx.prompt().expression()); + val reference = visitReference(ctx.reference()) + ReadInt(prompt, reference, ctx.getStart.getLine) + } + + override def visitReadBoolean(ctx: ReadBooleanContext): ReadBoolean = { + val prompt = visitExpression(ctx.prompt().expression()); + val reference = visitReference(ctx.reference()) + ReadBoolean(prompt, reference, ctx.getStart.getLine) + } + + override def visitReadString(ctx: ReadStringContext): ReadString = { + val prompt = visitExpression(ctx.prompt().expression()); + val reference = visitReference(ctx.reference()) + ReadString(prompt, reference, ctx.getStart.getLine) + } + + + override def visitCreateQueue(ctx: CreateQueueContext): Statement = { + val queueName = ctx.IDENTIFIER(0).getText + + if (ctx.FIFO() != null) { + CreateFifoQueue(queueName, ctx.getStart.getLine) + } else if (ctx.LIFO() != null) { + CreateLifoQueue(queueName, ctx.getStart.getLine) + } else { + val propertyName = ctx.IDENTIFIER(1).getText + + val sortOrder = + if (ctx.DESC() != null) + SortOrder.Desc + else + SortOrder.Asc + + CreateSortedQueue(queueName, propertyName, sortOrder, ctx.getStart.getLine) + } + } + + + override def visitBlock(ctx: BlockContext): Block = { + val statements = ctx.localStatement().map(visitLocalStatement) + Block(statements) + } + + + override def visitIfStatement(ctx: IfStatementContext): If = { + val condition = visitCondition(ctx.condition()) + + val thenPart = visitBlock(ctx.thenPart().block()) + val elsePart = + if (ctx.elsePart() != null) + Some(visitElsePart(ctx.elsePart())) + else + None + + + If(condition, thenPart, elsePart, ctx.getStart.getLine) + } + + + override def visitCondition(ctx: ConditionContext): Condition = { + val expression = visitExpression(ctx.expression()) + + Condition(expression) + } + + override def visitElsePart(ctx: ElsePartContext): Node = { + if (ctx.block() != null) { + visitBlock(ctx.block()) + } else if (ctx.ifStatement() != null) { + visitIfStatement(ctx.ifStatement()) + } else { + throw new IllegalStateException("Unknown else part") + } + } + + + override def visitWhileStatement(ctx: WhileStatementContext): While = { + val condition = visitCondition(ctx.condition()) + + val block = visitBlock(ctx.block()) + + While(condition, block, ctx.getStart.getLine) + } + + + override def visitCall(ctx: CallContext): Call = { + val procedureName = ctx.IDENTIFIER().getText + + val arguments = ctx.argument().map(argument => visitExpression(argument.expression())) + + Call(procedureName, arguments, ctx.getStart.getLine) + } + + + override def visitEnqueue(ctx: EnqueueContext): Enqueue = { + val queueName = ctx.IDENTIFIER().getText + + val reference = visitReference(ctx.reference()) + + Enqueue(queueName, reference, ctx.getStart.getLine) + } + + + override def visitRemove(ctx: RemoveContext): Remove = { + val queueName = ctx.IDENTIFIER().getText + val reference = visitReference(ctx.reference()) + + Remove(queueName, reference, ctx.getStart.getLine) + } + + + override def visitSchedule(ctx: ScheduleContext): Statement = { + val eventName = ctx.IDENTIFIER().getText + + val eventNoticeReference = if (ctx.reference() != null) { + Some(visitReference(ctx.reference())) + } else { + None + } + + val timeExpression = visitExpression(ctx.expression()) + + if (ctx.AT() != null) { + ScheduleAt(eventName, eventNoticeReference, timeExpression, ctx.getStart.getLine) + } else if (ctx.AFTER() != null) { + ScheduleAfter(eventName, eventNoticeReference, timeExpression, ctx.getStart.getLine) + } else { + throw new IllegalStateException + } + } + + override def visitCancel(ctx: CancelContext): Cancel = { + val eventName = ctx.IDENTIFIER().getText + + val eventNoticeReference = if (ctx.reference() != null) { + Some(visitReference(ctx.reference())) + } else { + None + } + + Cancel(eventName, eventNoticeReference, ctx.getStart.getLine) + } + + + override def visitSetRandomSeed(ctx: SetRandomSeedContext): SetRandomSeed = { + val expression = visitExpression(ctx.expression()) + + SetRandomSeed(expression, ctx.getStart.getLine) + } + + + /* + * EXPRESSION PARSING + */ + + + override def visitExpression(ctx: ExpressionContext): Expression = + visitContextAs(ctx.getChild(0), classOf[Expression]) + + + override def visitOrOperation(ctx: OrOperationContext): Expression = { + val operands = ctx.andOperation().map(visitAndOperation) + + if (operands.size >= 2) { + Or(operands) + } else { + operands.head + } + } + + + override def visitAndOperation(ctx: AndOperationContext): Expression = { + val operands = ctx.logicLiteral().map(visitLogicLiteral) + + if (operands.size >= 2) { + And(operands) + } else { + operands.head + } + } + + + override def visitLogicLiteral(ctx: LogicLiteralContext): Expression = { + val target = visitComparison(ctx.comparison()) + + if (ctx.OP_NOT() != null) { + Not(target) + } else { + target + } + } + + + override def visitComparison(ctx: ComparisonContext): Expression = { + val leftExpression = visitAlgebraicSum(ctx.algebraicSum(0)) + + if (ctx.comparisonOperator() != null) { + val rightExpression = visitAlgebraicSum(ctx.algebraicSum(1)) + + val comparisonOperator = ctx.comparisonOperator().getText match { + case "=" => ComparisonOperator.Equal + case "!=" => ComparisonOperator.NotEqual + case ">" => ComparisonOperator.Greater + case ">=" => ComparisonOperator.GreaterEqual + case "<" => ComparisonOperator.Less + case "<=" => ComparisonOperator.LessEqual + } + + Comparison(leftExpression, comparisonOperator, rightExpression) + } else { + leftExpression + } + } + + + override def visitAlgebraicSum(ctx: AlgebraicSumContext): Expression = { + val firstAddendModulus = visitAlgebraicProduct(ctx.firstAddend().algebraicProduct()) + val firstAddend = if (ctx.firstAddend().OP_MINUS() != null) UnaryMinus(firstAddendModulus) else firstAddendModulus + + val addends = ctx.additionalAddend().map { addendContext => + val rightModulus = visitAlgebraicProduct(addendContext.algebraicProduct()) + + if (addendContext.OP_MINUS() != null) { + UnaryMinus(rightModulus) + } else { + rightModulus + } + } + + if (addends.nonEmpty) { + addends.insert(0, firstAddend) + AlgebraicSum(addends) + } else { + firstAddend + } + } + + + override def visitAlgebraicProduct(ctx: AlgebraicProductContext): Expression = { + val firstTerm: Expression = visitTerm(ctx.firstFactor().term()) + + (firstTerm /: ctx.additionalFactor()) { (leftTerm, rightContext) => + val rightTerm = visitTerm(rightContext.term()) + val operator = rightContext.additionalFactorOperator().getText + + operator match { + case "*" => Multiplication(leftTerm, rightTerm) + case "/" => Division(leftTerm, rightTerm) + } + } + } + + + override def visitTerm(ctx: TermContext): Expression = + visitContextAs[Expression](ctx.getChild(0), classOf[Expression]) + + + override def visitPriorityExpression(ctx: PriorityExpressionContext): Expression = { + visitExpression(ctx.expression()) + } + + + override def visitReference(ctx: ReferenceContext): Reference = { + val identifier = ctx.IDENTIFIER().getText + + val parameterExpression = if (ctx.expression() != null) { + Some(visitExpression(ctx.expression())) + } else { + None + } + + Reference(identifier, parameterExpression) + } + + + override def visitReferenceValue(ctx: ReferenceValueContext): ReferenceValue = { + ReferenceValue(visitReference(ctx.reference())) + } + + + override def visitDequeue(ctx: DequeueContext): Dequeue = { + Dequeue(ctx.IDENTIFIER().getText) + } + + + override def visitFloor(ctx: FloorContext): Floor = { + Floor(visitExpression(ctx.expression())) + } + + + override def visitCeil(ctx: CeilContext): Ceil = { + Ceil(visitExpression(ctx.expression())) + } + + override def visitEmptyCheck(ctx: EmptyCheckContext): IsEmpty = { + + IsEmpty(ctx.IDENTIFIER().getText) + } + + override def visitUniformRandom(ctx: UniformRandomContext): UniformRandom = { + val minValue = visitExpression(ctx.expression(0)) + val maxValue = visitExpression(ctx.expression(1)) + + + UniformRandom(minValue, maxValue) + } + + + override def visitUniformIntRandom(ctx: UniformIntRandomContext): Node = { + val minValue = visitExpression(ctx.expression(0)) + val maxValue = visitExpression(ctx.expression(1)) + + UniformIntRandom(minValue, maxValue) + } + + override def visitExponentialRandom(ctx: ExponentialRandomContext): ExponentialRandom = { + val averageValue = visitExpression(ctx.expression()) + + ExponentialRandom(averageValue) + } + + + override def visitCastToDouble(ctx: CastToDoubleContext): CastToDouble = { + CastToDouble(visitExpression(ctx.expression())) + } + + + override def visitCastToInt(ctx: CastToIntContext): CastToInt = { + CastToInt(visitExpression(ctx.expression())) + } + + + override def visitCastToBoolean(ctx: CastToBooleanContext): CastToBoolean = { + CastToBoolean(visitExpression(ctx.expression())) + } + + + override def visitCastToString(ctx: CastToStringContext): CastToString = { + CastToString(visitExpression(ctx.expression())) + } + + + /* + * GROUND TERMS + */ + + override def visitNumber(ctx: NumberContext): Expression = { + val numberText = ctx.NUMBER().getText + + if (numberText.contains(".")) { + DoubleTerm(numberText.toDouble) + } else { + IntTerm(numberText.toInt) + } + } + + + override def visitPlusInf(ctx: PlusInfContext): PlusInfTerm = { + PlusInfTerm + } + + + override def visitMinusInf(ctx: MinusInfContext): MinusInfTerm = { + MinusInfTerm + } + + + override def visitTrueValue(ctx: TrueValueContext): BooleanTerm = { + BooleanTerm(true) + } + + + override def visitFalseValue(ctx: FalseValueContext): BooleanTerm = { + BooleanTerm(false) + } + + + override def visitStringValue(ctx: StringValueContext): StringTerm = { + val stringValue = parseStringValue(ctx.STRING()) + + new StringTerm(stringValue) + } + + + private def parseStringValue(stringNode: TerminalNode): String = { + val stringWithDelimiters = stringNode.getText + + stringWithDelimiters.substring(1, stringWithDelimiters.length - 1) + } + + override def visitNow(ctx: NowContext): Now = { + Now + } +} diff --git a/src/main/scala/info/gianlucacosta/chronos/parser/BasicAstBuilder.scala b/src/main/scala/info/gianlucacosta/chronos/parser/BasicAstBuilder.scala new file mode 100644 index 0000000..4eb9ec0 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/parser/BasicAstBuilder.scala @@ -0,0 +1,56 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.parser + +import java.io.{Reader, StringReader} + +import info.gianlucacosta.chronos.ast.Program +import org.antlr.v4.runtime._ + +/** + * Provides basic functions to create an AST from source code. + * + * For more granular control, please consider AstBuildingVisitor. + * + */ +object BasicAstBuilder { + def buildAST(sourceReader: Reader): Program = { + val charStream = new ANTLRInputStream(sourceReader) + val lexer = new ChronosLexer(charStream) + val tokenStream = new CommonTokenStream(lexer) + + val parser = new ChronosParser(tokenStream) + + lexer.removeErrorListeners() + parser.removeErrorListeners() + parser.addErrorListener(new SyntaxErrorListener) + + val programContext = parser.program() + + val astBuildingVisitor = new AstBuildingVisitor + astBuildingVisitor.visitProgram(programContext) + } + + + def buildAST(sourceString: String): Program = { + buildAST(new StringReader(sourceString)) + } +} diff --git a/src/main/scala/info/gianlucacosta/chronos/parser/SyntaxErrorListener.scala b/src/main/scala/info/gianlucacosta/chronos/parser/SyntaxErrorListener.scala new file mode 100644 index 0000000..d1b829c --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/parser/SyntaxErrorListener.scala @@ -0,0 +1,29 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.parser + +import info.gianlucacosta.chronos.parser.exceptions.SyntaxException +import org.antlr.v4.runtime._ + +class SyntaxErrorListener extends BaseErrorListener { + override def syntaxError(recognizer: Recognizer[_, _], offendingSymbol: scala.Any, line: Int, charPositionInLine: Int, msg: String, e: RecognitionException): Unit = + throw new SyntaxException(line, charPositionInLine, msg, offendingSymbol, e) +} diff --git a/src/main/scala/info/gianlucacosta/chronos/parser/exceptions/ParsingException.scala b/src/main/scala/info/gianlucacosta/chronos/parser/exceptions/ParsingException.scala new file mode 100644 index 0000000..58d9c0d --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/parser/exceptions/ParsingException.scala @@ -0,0 +1,25 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.parser.exceptions + +class ParsingException(message: String, cause: Throwable) extends RuntimeException(message, cause) { + def this(message: String) = this(message, null) +} \ No newline at end of file diff --git a/src/main/scala/info/gianlucacosta/chronos/parser/exceptions/SyntaxException.scala b/src/main/scala/info/gianlucacosta/chronos/parser/exceptions/SyntaxException.scala new file mode 100644 index 0000000..423efd2 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/parser/exceptions/SyntaxException.scala @@ -0,0 +1,32 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.parser.exceptions + +import org.antlr.v4.runtime.RecognitionException + +class SyntaxException( + val line: Int, + val charPositionInLine: Int, + val errorMessage: String, + val offendingSymbol: Any, + val recognitionException: RecognitionException) extends ParsingException( + s"Syntax error at line ${line}, position: ${charPositionInLine}: ${errorMessage} " +) diff --git a/src/main/scala/info/gianlucacosta/chronos/util/CommonXmlProperties.scala b/src/main/scala/info/gianlucacosta/chronos/util/CommonXmlProperties.scala new file mode 100644 index 0000000..156de96 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/util/CommonXmlProperties.scala @@ -0,0 +1,38 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.util + +import java.io.InputStream + +/** + * Extends XmlProperties by providing common, typed fields + * + * @param sourceStream the source stream + */ +class CommonXmlProperties(sourceStream: InputStream) extends XmlProperties(sourceStream) { + val name = this("name") + val version = this("version") + val copyrightYears = this("copyrightYears") + val license = this("license") + val website = this("website") + val facebookPage = this("facebookPage") + val release = this("release").toBoolean +} diff --git a/src/main/scala/info/gianlucacosta/chronos/util/XmlProperties.scala b/src/main/scala/info/gianlucacosta/chronos/util/XmlProperties.scala new file mode 100644 index 0000000..856f457 --- /dev/null +++ b/src/main/scala/info/gianlucacosta/chronos/util/XmlProperties.scala @@ -0,0 +1,45 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.util + +import java.io.InputStream + +import scala.xml.XML + + +/** + * Map-like structure initialized from a given standard XML properties file. + * + * @param sourceStream the source stream + */ +class XmlProperties(sourceStream: InputStream) { + private val propertyMap = { + val infoXml = XML.load(sourceStream) + + (infoXml \ "entry").map( + propertyNode => (propertyNode \ "@key").text -> propertyNode.text + ).toMap + } + + def apply(key: String): String = propertyMap(key) + + override def toString: String = propertyMap.mkString("{", ",", "}") +} diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/arithmeticExpressions.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/arithmeticExpressions.chronos new file mode 100644 index 0000000..8880341 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/arithmeticExpressions.chronos @@ -0,0 +1,26 @@ +event start { + println 9; + println 1 + 5; + println 6 * 2; + println 3 + (6 - 5); + println 46 / 2 + (7 - (12 - 10)) * 4; + + println 9 / 2; + println 9.0 / 2; + + println 23.4 - 0.5; + + println -5 + 7 - 0.3; + println -0 + 0 + 0.2; + + assert -2 = 7 - 9; + + assert -(29 + 4) < 0; + + + assert 9 - 1 - 10 = - 2; + + assert 100 / 25 / 4 = 1; + + assert (1 + 3) * 5 = 20; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/assertion.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/assertion.chronos new file mode 100644 index 0000000..2ef5b32 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/assertion.chronos @@ -0,0 +1,11 @@ +//This program should fail + +event start { + assert true; + + println "Test"; + + assert false; //FAILURE! + + println "Never reached"; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/booleanExpressions.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/booleanExpressions.chronos new file mode 100644 index 0000000..96e5099 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/booleanExpressions.chronos @@ -0,0 +1,64 @@ +event start { + println 3 < 5; + println 3.4 < 0.4; + + println 4 <= 11; + println 5 <= 5; + println 7 <= 3; + + println 8 = 8.0; + println 3 = 9; + + + println 4 != 2; + println 2.0 != 2; + println 9 != 9; + + println 7 > 2; + println 5 > 5; + + + println 10 >= 10; + println 11.7 >= 2; + println 3.4 >= 22; + + println (8 < 9) = true; + println (false = ((7 - 4) < -1)); + + + assert (not true) = false; + assert (not true) = false; + assert (not false) = true; + assert not false; + + assert (true or true) = true; + assert (true or false) = true; + assert (false or true) = true; + assert false or true; + assert (false or false) = false; + assert not (false or false); + + assert (true and true) = true; + assert true and true; + assert (true and false) = false; + assert (false and true) = false; + assert (false and false) = false; + assert not (false or false); + + + assert true or false or true; + assert not false or false or false; + + assert (not true and false and true) = false; + assert not(true and false and true) = true; + + assert false or false or true; + assert not false or false or true; + + assert not 4 > 53; + + assert 3 < 5 and 9 > 8 or -2 < 0; + + + +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/cancelingTwice.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/cancelingTwice.chronos new file mode 100644 index 0000000..3ecec74 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/cancelingTwice.chronos @@ -0,0 +1,15 @@ +//This program should fail + +event start { + println "Hello"; + + create anotherEvent; + + schedule anotherEvent at 30; + cancel anotherEvent; + cancel anotherEvent; //FAILURE! +} + +event anotherEvent { + assert false; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/cancelingUnscheduled.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/cancelingUnscheduled.chronos new file mode 100644 index 0000000..edfab2e --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/cancelingUnscheduled.chronos @@ -0,0 +1,13 @@ +//This program should fail + +event start { + println "Hello"; + + create anotherEvent; + + cancel anotherEvent; //FAILURE! +} + +event anotherEvent { + assert false; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/casts.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/casts.chronos new file mode 100644 index 0000000..5bddff1 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/casts.chronos @@ -0,0 +1,45 @@ +event start { + assert toDouble(20.9) = 20.9; + assert toDouble(20) = 20.0; + assert toDouble(true) = 1.0; + assert toDouble(false) = 0.0; + assert toDouble("20.9") = 20.9; + + assert toInt(20.9) = 20; + assert toInt(20) = 20; + assert toInt(true) = 1; + assert toInt(false) = 0; + assert toInt("20") = 20; + + + assert toBool(20.9) = true; + assert toBool(0.0) = false; + assert toBool(20) = true; + assert toBool(0) = false; + assert toBool(true) = true; + assert toBool(false) = false; + assert toBool("true") = true; + assert toBool("false") = false; + assert toBool("any") = false; + + + assert toString(20.9) = "20.9"; + assert toString(-20.9) = "-20.9"; + assert toString(-45) = "-45"; + assert toString(+inf) = "∞"; + assert toString(-inf) = "-∞"; + assert toString(true) = "true"; + assert toString(false) = "false"; + assert toString("Hello") = "Hello"; + + + assert floor(20.2) = 20; + assert floor(-20.2) = -21; + assert floor(20) = 20; + assert floor(-20) = -20; + + assert ceil(20.2) = 21; + assert ceil(-20.2) = -20; + assert ceil(20) = 20; + assert ceil(-20) = -20; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/destroyingTwice.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/destroyingTwice.chronos new file mode 100644 index 0000000..90614ed --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/destroyingTwice.chronos @@ -0,0 +1,5 @@ +event start { + create book; + destroy book; + destroy book; //FAILURE! +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/disableHeapCheck.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/disableHeapCheck.chronos new file mode 100644 index 0000000..4cb2a08 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/disableHeapCheck.chronos @@ -0,0 +1,13 @@ +$disableHeapCheck; + +event start { + create car; + create book called x; + + + create anotherCar; + destroy anotherCar; + + create book called y; + destroy y; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/duplicateEvent.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/duplicateEvent.chronos new file mode 100644 index 0000000..499a083 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/duplicateEvent.chronos @@ -0,0 +1,15 @@ +//This program should fail + +event start { + println "Hello, world! ^__^"; +} + + +event alpha { + println "Something"; +} + + +event alpha { + println "Something else"; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/duplicateProcedure.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/duplicateProcedure.chronos new file mode 100644 index 0000000..60f435c --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/duplicateProcedure.chronos @@ -0,0 +1,15 @@ +//This program should fail + +event start { + println "Hello, world! ^__^"; +} + + +procedure beta { + println "Something"; +} + + +procedure beta { + println "Something else"; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/duplicateProcedureParam.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/duplicateProcedureParam.chronos new file mode 100644 index 0000000..239ff2b --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/duplicateProcedureParam.chronos @@ -0,0 +1,10 @@ +//This program should fail + +procedure gamma(x, x) { + println "Hello!"; +} + + +event start { + call printSum(14, 3); +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/entities.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/entities.chronos new file mode 100644 index 0000000..f9c3e50 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/entities.chronos @@ -0,0 +1,71 @@ +event start { + bookTitle := "Harry Potter and the Deathly Hallows"; + + create book; + author(book) := "J. K. Rowling"; + title(book) := bookTitle; + + assert author(book) = "J. K. Rowling"; + assert title(book) = bookTitle; + + output := "Harry Potter's saga was written by: " + author(book) + "! ^__^"; + + assert output = "Harry Potter's saga was written by: J. K. Rowling! ^__^"; + + + create alpha; + create beta; + create gamma; + + stringVar := "Nested entities! ^__^"; + x(alpha) := beta; + y(beta) := gamma; + z(gamma) := stringVar; + + assert z(y(x(alpha))) = stringVar; + + create car called example; + name(example) := "Example car"; + + assert name(example) = "Example car"; + + create example1; + create example2; + assert example1 = example1; + assert example2 = example2; + assert example1 != example2; + destroy example1; + destroy example2; + + + destroy book; + destroy alpha; + destroy beta; + destroy gamma; + destroy example; + + create garbage1; + call cleaner(garbage1); + + create someOtherEntity called garbage2; + call cleaner(garbage2); + + + + create book called refTest; + title(refTest) := "Reference test"; + refTest2 := refTest; + title(refTest2) := "New title"; + assert title(refTest) = "New title"; + + create book called refTest3; + entityAttrib(refTest3) := refTest2; + destroy entityAttrib(refTest3); + + destroy refTest3; +} + + +procedure cleaner(myEntity) { + destroy myEntity; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/eventsAdvanced.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/eventsAdvanced.chronos new file mode 100644 index 0000000..5da3a53 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/eventsAdvanced.chronos @@ -0,0 +1,70 @@ +alpha1Time := 10; +alpha2Time := 20; +alpha3Time := 30; +betaDelay := 5.8; + +alpha1Value := 50; +alpha2Value := 60; +alpha2bisValue := alpha2Value * 10; + +event start { + create alpha called alpha1; + x(alpha1) := alpha1Value; + + create alpha called alpha2; + x(alpha2) := alpha2Value; + + + create alpha called alpha2bis; + x(alpha2bis) := alpha2bisValue; + + create alpha called alpha3; + global a := alpha3; + + println "In main"; + + + schedule alpha called alpha2 at alpha2Time; + + schedule alpha called alpha3 at alpha3Time; + + schedule alpha called alpha1 at alpha1Time; + + schedule alpha called alpha2bis at alpha2Time; +} + + +event alpha { + println "X = " + x(alpha); + + if x(alpha) = alpha1Value { + assert time.v = alpha1Time; + create beta; + schedule beta after betaDelay; + } else if x(alpha) = alpha2Value { + assert time.v = alpha2Time; + } else if x(alpha) = alpha2bisValue { + assert time.v = alpha2Time; + } else { + assert false; + } + + destroy alpha; +} + + +event beta { + println ("In beta!"); + + assert time.v = alpha1Time + betaDelay; + + cancel alpha called a; + destroy a; + + create alpha; + schedule alpha at 60; + cancel alpha; + destroy alpha; + + destroy beta; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/eventsBasic.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/eventsBasic.chronos new file mode 100644 index 0000000..b152a63 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/eventsBasic.chronos @@ -0,0 +1,17 @@ +event start { + create anotherEvent; + name(anotherEvent) := "world"; + + schedule anotherEvent at 10; + + create anotherEvent called myEventNotice; + name(myEventNotice) := "Scala"; + + schedule anotherEvent called myEventNotice at 20; +} + + +event anotherEvent { + println "Hello, " + name(anotherEvent) + "! ^__^"; + destroy anotherEvent; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/globals.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/globals.chronos new file mode 100644 index 0000000..a502187 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/globals.chronos @@ -0,0 +1,36 @@ +a := 4; + +event start { + assert a = 4; + + call f; + + assert a = 70; + + a := 45; + + call omega; +} + + +procedure f { + assert a = 4; + a := 8; + assert a = 8; + + call g; + + assert a = 8; +} + + +procedure g { + assert a = 4; + global a := 70; +} + + + +procedure omega { + assert a = 70; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/hello.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/hello.chronos new file mode 100644 index 0000000..8626da7 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/hello.chronos @@ -0,0 +1,7 @@ +event start { + /* + This is a comment! ^__^ + */ + + println "Hello, world! ^__^"; //This is a comment, too +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/if.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/if.chronos new file mode 100644 index 0000000..f416911 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/if.chronos @@ -0,0 +1,47 @@ +event start { + a := 9; + + if a > 5 { + println "Omicron"; + println "Ro"; + } + + b := "Hello, " + "world"; + + if b = "Hello, world" { + println "Sigma"; + } else { + assert false; + } + + c := +inf; + if (c < 0) { + assert false; + } else { + println "Tau"; + } + + + if 9 > 8 { + if 4 != 3 { + println("Omega"); + } else { + assert false; + } + } + + + z := 7; + + if (z < 0) { + assert false; + } else if (z < 5) { + assert false; + } else if (z > 10) { + assert false; + } else if (z = 7) { + assert true; + } else { + assert false; + } +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/infOperations.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/infOperations.chronos new file mode 100644 index 0000000..a26b485 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/infOperations.chronos @@ -0,0 +1,54 @@ +event start { + assert inf = +inf; + assert inf = -(-inf); + + assert inf > 0; + assert -inf < 0; + assert inf > -inf; + + + assert 3 + inf = inf; + assert 3.9 + -inf = -inf; + + assert -4 + inf = inf; + assert -4.9 + -inf = -inf; + + + assert 3 - inf = -inf; + assert 3.3 - -inf = +inf; + + assert -4 - inf = -inf; + assert -9.2 - inf = -inf; + assert -15 - -inf = +inf; + + + + assert "Your value is: " + inf = "Your value is: ∞"; + assert "Your value is: " + -inf = "Your value is: -∞"; + + assert inf * +inf = inf; + assert inf * -inf = -inf; + assert -inf * -inf = +inf; + + assert 0 / inf = 0; + assert 0 / -inf = 0; + + assert 7 / inf = 0; + assert 7.6 / -inf = 0; + assert -4 / inf = 0; + assert -2 / -inf = 0; + + assert toBool(inf); + assert toBool(-inf); + + + assert 9 / 0.0 = +inf; + assert 9.0 / 0.0 = +inf; + assert -9 / 0.0 = -inf; + assert -9.0 / 0.0 = -inf; + + assert 9 / 0 = +inf; + assert 9.0 / 0 = +inf; + assert -9 / 0 = -inf; + assert -9.0 / 0 = -inf; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/maps.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/maps.chronos new file mode 100644 index 0000000..8f06540 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/maps.chronos @@ -0,0 +1,35 @@ +map myMap; + +event start { + x := 93; + myMap(7) := x; + + assert myMap(7) = 93; + + + myMap(90 + 8.7) := "Hello"; + y := 98.7; + assert myMap(y) = "Hello"; + + + myMap("gamma") := 90; + myMap("beta") :="gamma"; + myMap("alpha") := "beta"; + + assert myMap(myMap(myMap("alpha"))) = 90; + + myMap(inf) := 34; + assert myMap(+inf) = 34; + + myMap(-inf) := false; + assert myMap(-inf) = false; + + //Maps are global identifiers + global myMap := 9; + call checkMyMap; +} + + +procedure checkMyMap { + assert myMap = 9; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/missedDeallocation.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/missedDeallocation.chronos new file mode 100644 index 0000000..a99a19b --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/missedDeallocation.chronos @@ -0,0 +1,6 @@ +//This program should fail + +event start { + create car; + create book called x; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/procedures.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/procedures.chronos new file mode 100644 index 0000000..cb31928 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/procedures.chronos @@ -0,0 +1,26 @@ +procedure printSum(x, y) { + localVar := 9; + + create operands; + x(operands) := x; + y(operands) := y; + call sum(operands); + + println "Your result is: " + result(operands); + destroy operands; + + assert localVar = 9; +} + + +event start { + call printSum(14, 3); +} + +procedure sum(sumOperands) { + localVar := 52; + + result(sumOperands) := x(sumOperands) + y(sumOperands); + + assert localVar = 52; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/queues.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/queues.chronos new file mode 100644 index 0000000..b388d78 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/queues.chronos @@ -0,0 +1,131 @@ +queue myFifoQueue fifo; +queue myLifoQueue lifo; +queue mySortedAscQueue sorted by title; +queue mySortedDescQueue sorted by title desc; + + +event start { + create alphaBook; + create alpha2Book; + create betaBook; + create gammaBook; + + title(alphaBook) := "Alpha"; + title(alpha2Book) := "Alpha 2"; + title(betaBook) := "Beta"; + title(gammaBook) := "Gamma"; + + + /* + * FIFO QUEUE + */ + assert isEmpty(myFifoQueue); + + insert betaBook into myFifoQueue; + insert alphaBook into myFifoQueue; + insert alpha2Book into myFifoQueue; + insert gammaBook into myFifoQueue; + + + b := alpha2Book; + remove b from myFifoQueue; + + item1 := get first from myFifoQueue; + assert title(item1) = title(betaBook); + + item2 := get first from myFifoQueue; + assert title(item2) = title(alphaBook); + + item3 := get first from myFifoQueue; + assert title(item3) = title(gammaBook); + + assert isEmpty(myFifoQueue); + + + /* + * LIFO QUEUE + */ + assert isEmpty(myLifoQueue); + + insert betaBook into myLifoQueue; + insert alphaBook into myLifoQueue; + insert alpha2Book into myLifoQueue; + insert gammaBook into myLifoQueue; + + + b := alpha2Book; + remove b from myLifoQueue; + + + item1 := get first from myLifoQueue; + assert title(item1) = title(gammaBook); + + item2 := get first from myLifoQueue; + assert title(item2) = title(alphaBook); + + item3 := get first from myLifoQueue; + assert title(item3) = title(betaBook); + + assert isEmpty(myLifoQueue); + + + + /* + * SORTED QUEUE (ASC) + */ + assert isEmpty(mySortedAscQueue); + + insert betaBook into mySortedAscQueue; + insert alphaBook into mySortedAscQueue; + insert alpha2Book into mySortedAscQueue; + insert gammaBook into mySortedAscQueue; + + b := alpha2Book; + remove b from mySortedAscQueue; + + + item1 := get first from mySortedAscQueue; + assert title(item1) = title(alphaBook); + + item2 := get first from mySortedAscQueue; + assert title(item2) = title(betaBook); + + item3 := get first from mySortedAscQueue; + assert title(item3) = title(gammaBook); + + assert isEmpty(mySortedAscQueue); + + + + /* + * SORTED QUEUE (DESC) + */ + assert isEmpty(mySortedDescQueue); + + insert betaBook into mySortedDescQueue; + insert alphaBook into mySortedDescQueue; + insert alpha2Book into mySortedDescQueue; + insert gammaBook into mySortedDescQueue; + + + b := alpha2Book; + remove b from mySortedDescQueue; + + + item1 := get first from mySortedDescQueue; + assert title(item1) = title(gammaBook); + + item2 := get first from mySortedDescQueue; + assert title(item2) = title(betaBook); + + item3 := get first from mySortedDescQueue; + assert title(item3) = title(alphaBook); + + assert isEmpty(mySortedDescQueue); + + + destroy alphaBook; + destroy alpha2Book; + destroy betaBook; + destroy gammaBook; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/random.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/random.chronos new file mode 100644 index 0000000..cb098ba --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/random.chronos @@ -0,0 +1,28 @@ +event start { + NUM_CYCLES := 300; + + x := 1; + while x <= NUM_CYCLES { + min := 739; + max := 825.0; + + currentRandom := uniformRandom(min, max); + + assert(currentRandom >= min); + assert(currentRandom <= max); + + x := x + 1; + } + + + x := 1; + while x <= NUM_CYCLES { + averageValue := 8; + + currentRandom := expRandom(averageValue); + + assert(currentRandom >= 0); + + x := x + 1; + } +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/randomSeed.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/randomSeed.chronos new file mode 100644 index 0000000..447fe7e --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/randomSeed.chronos @@ -0,0 +1,38 @@ +event start { + NUM_CYCLES := 300; + + uniformRandomSeed := uniformRandom(1, 10000); + + min := 739; + max := 825.0; + setRandomSeed uniformRandomSeed; + firstUnifOutput := uniformRandom(min, max); + + x := 1; + while x <= NUM_CYCLES { + setRandomSeed uniformRandomSeed; + currentUniformRandom := uniformRandom(min, max); + + assert currentUniformRandom = firstUnifOutput; + x := x + 1; + } + + + + expRandomSeed := uniformRandom(1, 10000); + averageValue := 8; + setRandomSeed expRandomSeed; + firstExpValue := expRandom(averageValue); + + + x := 1; + while x <= NUM_CYCLES { + setRandomSeed expRandomSeed; + + currentExpRandom := expRandom(averageValue); + + assert currentExpRandom = firstExpValue; + + x := x + 1; + } +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/rescheduling.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/rescheduling.chronos new file mode 100644 index 0000000..e65fd61 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/rescheduling.chronos @@ -0,0 +1,23 @@ +deltaTime := 10; +MAX_TRIGGERS := 3; + +event start { + create anotherEvent; + + triggers(anotherEvent) := 0; + + schedule anotherEvent after 0; +} + + +event anotherEvent { + println toInt(time.v); + + triggers(anotherEvent) := triggers(anotherEvent) + 1; + + if triggers(anotherEvent) = MAX_TRIGGERS { + destroy anotherEvent; + } else { + schedule anotherEvent at time.v + deltaTime; + } +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/returnExit.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/returnExit.chronos new file mode 100644 index 0000000..b9386b6 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/returnExit.chronos @@ -0,0 +1,30 @@ +event start { + call f; + create closingEvent; + schedule closingEvent after 0; + + return; + + assert false; +} + + + +procedure f { + println "Alpha"; + call g; + println "Gamma"; + exit; + assert false; +} + +procedure g { + println "Beta"; + return; + assert false; +} + +event closingEvent { + exit; + assert false; +} diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/schedulingAnEntity.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/schedulingAnEntity.chronos new file mode 100644 index 0000000..d349596 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/schedulingAnEntity.chronos @@ -0,0 +1,5 @@ +event start { + create car; + + schedule car at 10; //FAILURE! +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/schedulingForThePast.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/schedulingForThePast.chronos new file mode 100644 index 0000000..753232a --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/schedulingForThePast.chronos @@ -0,0 +1,16 @@ +//This program should fail + +event start { + println "Hello"; + + create anotherEvent; + + schedule anotherEvent at 10; +} + + +event anotherEvent { + println "Hi!"; + + schedule anotherEvent at 0; //FAILURE! +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/schedulingTwice.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/schedulingTwice.chronos new file mode 100644 index 0000000..271848f --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/schedulingTwice.chronos @@ -0,0 +1,14 @@ +//This program should fail + +event start { + println "Hello"; + + create anotherEvent; + + schedule anotherEvent at 10; + schedule anotherEvent at 15; //FAILURE! +} + +event anotherEvent { + println "Hi!"; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/schedulingWithWrongType.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/schedulingWithWrongType.chronos new file mode 100644 index 0000000..560be58 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/schedulingWithWrongType.chronos @@ -0,0 +1,20 @@ +event start { + print "Hello"; + + create anotherEvent called myNotice; + + name(myNotice) := "world"; + + schedule yetAnotherEvent called myNotice at 10; //FAILURE! +} + + +event anotherEvent { + println ", " + name(anotherEvent) + "! ^__^"; + destroy anotherEvent; +} + + +event yetAnotherEvent { + println "Hi!"; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/sortedQueueAdvanced.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/sortedQueueAdvanced.chronos new file mode 100644 index 0000000..e0061d6 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/sortedQueueAdvanced.chronos @@ -0,0 +1,35 @@ +queue sortedQueue sorted by title; + +event start { + create album; + title(album) := "Rock Symphonies"; + year(album) := 2010; + + create book; + title(book) := "Alice in Wonderland"; + + + insert album into sortedQueue; + insert book into sortedQueue; + + firstItem := get first from sortedQueue; + assert firstItem = book; + + secondItem := get first from sortedQueue; + assert secondItem = album; + assert year(secondItem) = 2010; + + assert isEmpty(sortedQueue); + + insert album into sortedQueue; + assert not isEmpty(sortedQueue); + + create rabbit; + name(rabbit) := "white rabbit"; + + insert rabbit into sortedQueue; //FAILURE! + + destroy album; + destroy book; + destroy rabbit; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/uniformIntRandom.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/uniformIntRandom.chronos new file mode 100644 index 0000000..fb286de --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/uniformIntRandom.chronos @@ -0,0 +1,36 @@ +map numOfGeneratedValues; + +event start { + numCycles := 10000; + min := 1; + max := 6; + + i := min; + while i <= max { + numOfGeneratedValues(i) := 0; + i := i +1; + } + + i := 1; + while i <= numCycles { + discreteValue := uniformIntRandom(min, max); + + numOfGeneratedValues(discreteValue) := numOfGeneratedValues(discreteValue) + 1; + + i := i + 1; + } + + + totalCount := 0; + + i := min; + while i <= max { + numValues := numOfGeneratedValues(i); + assert numValues > 0; + + totalCount := totalCount + numValues; + i := i + 1; + } + + assert totalCount = numCycles; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/userInput.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/userInput.chronos new file mode 100644 index 0000000..7538dc7 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/userInput.chronos @@ -0,0 +1,25 @@ +event start { + create sumOperands; + + readDouble "Please, enter a double X: " x(sumOperands); + readInt "Please, enter an int Y: " y(sumOperands); + + z := x(sumOperands) + y(sumOperands); + println "The result is: " + z; + + destroy sumOperands; + + + variablePromptString := "Please, enter a boolean: "; + + readBool variablePromptString myBool; + println myBool; + + create book; + + variableIntegerInput := 56; + readString variableIntegerInput title(book); + println title(book); + + destroy book; +} diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/variables.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/variables.chronos new file mode 100644 index 0000000..bc56cee --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/variables.chronos @@ -0,0 +1,26 @@ +event start { + intValue := 90; + println intValue; + assert intValue = 90; + + alpha := 5.3; + beta := 7; + + gamma := alpha + ((beta - 4) * 2); + + println gamma; + assert gamma = 11.3; + + + firstString := "Hello"; + secondString := ", world! ^__^"; + + println (firstString + secondString) = "Hello, world! ^__^"; + check := (firstString + secondString) = "Hello, world! ^__^"; + assert check; + + global myGlobal := 286; + assert myGlobal = 286; + + println "The result is: " + myGlobal; +} \ No newline at end of file diff --git a/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/while.chronos b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/while.chronos new file mode 100644 index 0000000..33fa3b7 --- /dev/null +++ b/src/test/resources/info/gianlucacosta/chronos/interpreter/programs/while.chronos @@ -0,0 +1,18 @@ +event start { + x := 1; + + while x <= 4 { + println "X is now: " + x; + x := x + 1; + } + + y := 13; + while (y > 10) { + println y + " is the value of Y"; + y := y - 1; + } + + while (100 < 2) { + assert false; + } +} \ No newline at end of file diff --git a/src/test/scala/info/gianlucacosta/chronos/interpreter/InterpreterSpec.scala b/src/test/scala/info/gianlucacosta/chronos/interpreter/InterpreterSpec.scala new file mode 100644 index 0000000..14ef2e1 --- /dev/null +++ b/src/test/scala/info/gianlucacosta/chronos/interpreter/InterpreterSpec.scala @@ -0,0 +1,401 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter + +import info.gianlucacosta.chronos.ast.exceptions.{DuplicateEventException, DuplicateParameterException, DuplicateProcedureException} +import info.gianlucacosta.chronos.ast.{Block, Event} +import info.gianlucacosta.chronos.interpreter.atoms.{Entity, EventNotice} +import info.gianlucacosta.chronos.interpreter.exceptions._ +import org.scalatest.{FlatSpec, Matchers} + +import scala.io.Source + +class InterpreterSpec extends FlatSpec with Matchers { + + + "HelloWorld" should "run" in { + runProgram("hello.chronos", Seq( + "Hello, world! ^__^" + )) + } + + + "Arithmetic expressions" should "be correctly evaluated" in { + runProgram("arithmeticExpressions.chronos", Seq( + "9", + "6", + "12", + "4", + "43", + + "4", + "4.5", + + "22.9", + + "1.7", + "0.2" + )) + } + + + "Infinity values" should "work as expected" in { + runProgram("infOperations.chronos") + } + + + "Boolean expressions" should "be correctly evaluated" in { + runProgram("booleanExpressions.chronos", Seq( + "true", + "false", + + "true", + "true", + "false", + + "true", + "false", + + + "true", + "false", + "false", + + "true", + "false", + + "true", + "true", + "false", + + "true", + "true" + )) + } + + + "Assertion" should "crash the program on failure" in { + runProgramWithFailingStatement[AssertionFailedException]("assertion.chronos") + } + + + "Variables" should "be correctly set and read" in { + runProgram("variables.chronos", Seq( + "90", + + "11.3", + + "true", + + "The result is: 286" + )) + } + + + "Maps" should "work" in { + runProgram("maps.chronos") + } + + + "Entities" should "work" in { + runProgram("entities.chronos") + } + + "Destroying an entity twice" should "fail" in { + runProgramWithFailingStatement[IdentifierNotFoundException]("destroyingTwice.chronos") + } + + + "Lingering entities" should "cause an exception" in { + val exception = intercept[MissedDeallocationException] { + runProgram("missedDeallocation.chronos") + } + + val entityInfoSet = exception.heapAtoms.map(heapAtom => { + val heapEntity = heapAtom.asInstanceOf[Entity] + (heapEntity.name, heapEntity.entityType) + }).toSet + + entityInfoSet should be(Set( + ("car", "car"), + ("x", "book") + )) + } + + + "Lingering entities" should "NOT raise an exception when the heap check is disabled" in { + runProgram("disableHeapCheck.chronos") + } + + + "Procedures" should "be correctly called" in { + runProgram("procedures.chronos", Seq( + "Your result is: 17" + )) + } + + + "Return and exit" should "work" in { + runProgram("returnExit.chronos", Seq( + "Alpha", + "Beta", + "Gamma" + )) + } + + + "Read" should "work" in { + runProgram( + "userInput.chronos", + + new TestInput( + Seq(9.8), + Seq(11), + Seq(true), + Seq("Just a test") + ), + + Seq( + "The result is: 20.8", + "true", + "Just a test" + )) + } + + + + "Casts" should "work" in { + runProgram("casts.chronos") + } + + + "If-then-else" should "work" in { + runProgram("if.chronos", Seq( + "Omicron", + "Ro", + "Sigma", + "Tau", + "Omega" + )) + } + + "While" should "work" in { + runProgram("while.chronos", Seq( + "X is now: 1", + "X is now: 2", + "X is now: 3", + "X is now: 4", + "13 is the value of Y", + "12 is the value of Y", + "11 is the value of Y" + )) + } + + + + "Queues" should "work" in { + runProgram("queues.chronos") + } + + + "Sorted queues" should "work as expected" in { + runProgramWithFailingStatement[PropertyNotFoundException]("sortedQueueAdvanced.chronos") + } + + "Random functions" should "return values within bounds" in { + runProgram("random.chronos") + } + + + "UniformIntRandom" should "return all discrete values in the range" in { + runProgram("uniformIntRandom.chronos") + } + + + "The random seed" should "affect randomizing functions" in { + runProgram("randomSeed.chronos") + } + + + "Global variables" should "work" in { + runProgram("globals.chronos") + } + + "Basic events example" should "work" in { + runProgram("eventsBasic.chronos", Seq( + "Hello, world! ^__^", + "Hello, Scala! ^__^" + )) + } + + + "Advanced events example" should "work" in { + runProgram("eventsAdvanced.chronos", Seq( + "In main", + "X = 50", + "In beta!", + "X = 60", + "X = 600" + )) + } + + "Rescheduling an event notice" should "work" in { + runProgram("rescheduling.chronos", Seq( + "0", + "10", + "20" + )) + } + + "Scheduling an event notice for the past" should "fail" in { + runProgramWithFailingStatement[SchedulingException]("schedulingForThePast.chronos") + } + + + "Scheduling an event notice twice" should "fail" in { + runProgramWithFailingStatement[SchedulingException]("schedulingTwice.chronos") + } + + + "Canceling an unscheduled event notice" should "fail" in { + runProgramWithFailingStatement[SchedulingException]("cancelingUnscheduled.chronos") + } + + + "Canceling an event notice twice" should "fail" in { + runProgramWithFailingStatement[SchedulingException]("cancelingTwice.chronos") + } + + + "Scheduling an object which is not an event notice" should "fail" in { + runProgramWithFailingStatement[SchedulingException]("schedulingAnEntity.chronos") + } + + + "Scheduling an event notice not having the declared event type" should "fail" in { + runProgramWithFailingStatement[SchedulingException]("schedulingWithWrongType.chronos") + } + + + + "A duplicate event" should "raise an exception" in { + val duplicateException = intercept[DuplicateEventException] { + runProgram("duplicateEvent.chronos") + } + + duplicateException.eventName should be("alpha") + } + + + "A duplicateProcedure" should "raise an exception" in { + val duplicateException = intercept[DuplicateProcedureException] { + runProgram("duplicateProcedure.chronos") + } + + duplicateException.procedureName should be("beta") + } + + "A procedure with a duplicate parameter" should "raise an exception" in { + val duplicateException = intercept[DuplicateParameterException] { + runProgram("duplicateProcedureParam.chronos") + } + + duplicateException.procedureName should be("gamma") + duplicateException.param should be("x") + } + + + "Entities not having the same memory address" should "NOT be equal" in { + val entityA = new Entity("test", "testType") + val entityB = new Entity("test", "testType") + + entityA should be(entityA) + entityA should not be (entityB) + } + + + "Event notices not having the same memory address" should "NOT be equal" in { + val event = Event("Test", Block(Nil)) + + val noticeA = new EventNotice("test", event) + val noticeB = new EventNotice("test", event) + + noticeA should be(noticeA) + noticeA should not be (noticeB) + } + + + private def runProgram( + programName: String): Unit = { + val program = TestSourcePool.getProgram(programName) + + val testInput = new TestInput + val testOutput = new TestOutput + val interpreter = new Interpreter(testInput, testOutput) + + interpreter.run(program) + } + + + private def runProgram( + programName: String, + expectedOutputLines: Seq[String]): Unit = runProgram(programName, new TestInput, expectedOutputLines) + + + private def runProgram( + programName: String, + testInput: TestInput, + expectedOutputLines: Seq[String]): Unit = { + val program = TestSourcePool.getProgram(programName) + + + val testOutput = new TestOutput + val interpreter = new Interpreter(testInput, testOutput) + + interpreter.run(program) + testOutput.outputLines should be(expectedOutputLines) + } + + + private def runProgramWithFailingStatement[ExpectedUnderlyingException: Manifest](programName: String): Unit = { + val statementException = intercept[StatementFailedException] { + runProgram(programName) + } + + val programSource = Source.fromInputStream(TestSourcePool.getProgramStream(programName)) + + val failureLineNumbers = programSource.getLines().zipWithIndex.filter { + case (line, lineIndex) => + line.contains("//FAILURE") + }.map { + case (line, lineIndex) => lineIndex + 1 + }.toList + + if (failureLineNumbers.size != 1) { + fail(s"There must be exactly 1 failure line, but ${failureLineNumbers.size} were found") + } + + + statementException.statement.lineNumber should be(failureLineNumbers.head) + statementException.getCause should be(an[ExpectedUnderlyingException]) + + } +} diff --git a/src/test/scala/info/gianlucacosta/chronos/interpreter/TestInput.scala b/src/test/scala/info/gianlucacosta/chronos/interpreter/TestInput.scala new file mode 100644 index 0000000..a6ad616 --- /dev/null +++ b/src/test/scala/info/gianlucacosta/chronos/interpreter/TestInput.scala @@ -0,0 +1,57 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter + +import info.gianlucacosta.chronos.interpreter.atoms.{BooleanAtom, DoubleAtom, IntAtom, StringAtom} + +class TestInput( + private var doubles: Seq[Double], + private var ints: Seq[Int], + private var booleans: Seq[Boolean], + private var strings: Seq[String]) extends Input { + def this() = this(Nil, Nil, Nil, Nil) + + override def readDouble(prompt: String): DoubleAtom = { + val result = DoubleAtom(doubles.head) + doubles = doubles.tail + result + } + + override def readInt(prompt: String): IntAtom = { + val result = IntAtom(ints.head) + ints = ints.tail + result + } + + + override def readBoolean(prompt: String): BooleanAtom = { + val result = BooleanAtom(booleans.head) + booleans = booleans.tail + result + } + + + override def readString(prompt: String): StringAtom = { + val result = StringAtom(strings.head) + strings = strings.tail + result + } +} diff --git a/src/test/scala/info/gianlucacosta/chronos/interpreter/TestOutput.scala b/src/test/scala/info/gianlucacosta/chronos/interpreter/TestOutput.scala new file mode 100644 index 0000000..6b74576 --- /dev/null +++ b/src/test/scala/info/gianlucacosta/chronos/interpreter/TestOutput.scala @@ -0,0 +1,50 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter + +import info.gianlucacosta.chronos.interpreter.atoms.Atom + +class TestOutput extends Output { + var currentLine = "" + + var _outputLines = Seq.empty[String] + + + override def print(atom: Atom): Unit = { + val stringValue = atom.toString + currentLine += stringValue + } + + + override def println(atom: Atom): Unit = { + val stringValue = atom.toString + + _outputLines :+= (currentLine + stringValue) + currentLine = "" + } + + + override def printException(exception: Exception): Unit = { + throw exception + } + + def outputLines = _outputLines +} diff --git a/src/test/scala/info/gianlucacosta/chronos/interpreter/TestSourcePool.scala b/src/test/scala/info/gianlucacosta/chronos/interpreter/TestSourcePool.scala new file mode 100644 index 0000000..701d014 --- /dev/null +++ b/src/test/scala/info/gianlucacosta/chronos/interpreter/TestSourcePool.scala @@ -0,0 +1,38 @@ +/*§ + =========================================================================== + Chronos + =========================================================================== + Copyright (C) 2015 Gianluca Costa + =========================================================================== + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + =========================================================================== +*/ + +package info.gianlucacosta.chronos.interpreter + +import java.io.{InputStream, InputStreamReader} + +import info.gianlucacosta.chronos.ast.Program +import info.gianlucacosta.chronos.parser.BasicAstBuilder + +object TestSourcePool { + def getProgram(programFileName: String): Program = { + val sourceStream = new InputStreamReader(getProgramStream(programFileName)) + + BasicAstBuilder.buildAST(sourceStream) + } + + + def getProgramStream(programFileName: String): InputStream = + getClass.getResourceAsStream("programs/" + programFileName) +}