123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- Action Language
- ===============
- Apart from simple attributes, such as the number of tokens on a PetriNet Place, code blocks are another frequent requirement.
- Current tools often do away with this by typing it as a string, and then passing it on internally to, for example, a Python interpreter.
- In this case, the code is nothing else than a string and there is no control whatsoever over the structure and semantics.
- Also, requiring an existing interpreter for that language, restricts the set of platforms for which a Modelverse can be implemented.
- In the Modelverse, we alleviate this problem by providing a built-in and explicitly modelled action language.
- This action language is primitive in the Modelverse, meaning that its abstract syntax tree can be represented, instead of just its textual representation.
- The abstract syntax tree is executed on-the-fly in the Modelverse, preventing the mapping to any other internal representation.
- The semantics of this language is also explicitly modelled, making it possible to automatically generate interpreters for any desired platform.
- In this section, we now focus on how to use this action language.
- As PetriNets are a bit too simple to require actions, we will use Finite State Automata as an example.
- .. warning::
- The current action language compiler is rather basic and contains quite some problems.
- Example: Finite State Automata
- ------------------------------
- In the context of Finite State Automata, it is often necessary to define actions on specific transitions.
- Upon execution of the transition, the associated action is executed.
- When defining a model, we can also upload code snippets as attribute values::
- >>> instantiate_attr_code("models/my_fsa", "transition_1", "action", open("actions/transition_1.alc", "r").read())
- This will assign the action stored in the file *actions/transition_1.alc* to the *action* attribute of the transition.
- Of course, it is not necessary to open a file for this, as the function itself could just as well be inlined.
- The only requirement on the value, is that it is a string that can be parsed as Action Language::
- >>> instantiate_attr_code("models/my_fsa", "transition_1", "action", \
- ... """
- ... include "primitives.alh"
- ... Void function action(model : Element):
- ... log("Executed action!")
- ... return!
- ... """
- Action language attributes are considered identical to any other attribute.
- The exception is of course that they are assigned using the code specific function, as otherwise we would have uploaded a string.
- Note the difference between the assignment of a string, and the assignment of action code::
- >>> instantiate_attr("models/my_fsa", "transition_1", "action", open("actions/transition_1.alc", "r").read())
- >>> instantiate_attr_code("models/my_fsa", "transition_1", "action", open("actions/transition_1.alc", "r").read())
- The first line assigns a string to the attribute, and is therefore not executable, nor is it parsed or considered in any way.
- The second line assigns code to the attribute, and is therefore executable, and parsed upon assignment.
- The signature of each function, in the case of our example this is "Void function action(model : Element)", depends on how the code will be used later on.
- Multi-function code
- -------------------
- Some code is ideally expressed using multiple functions.
- As the Modelverse does not support subfunctions, it supports the second best alternative: multiple functions can be defined.
- When multiple functions are defined, the *main* function will be executed, which can refer to any of the other functions defined in the same block.
- If no *main* function is found, the topmost function is executed.
- Language description
- --------------------
- Next we describe the basic constructs of the language, as offered by our compiler.
- The language is inspired by Python syntax, and highly resembles it.
- Nonetheless, it is much more minimal.
- In the remainder of this subsection, we go over the different language constructs.
- If
- ^^
- The IF construct is similar to that found in other languages.
- Its structure is as follows::
- if condition1:
- do_when_condition1
- elif condition2:
- do_when_condition2
- else:
- do_otherwise
- Each condition can be an expression.
- The code blocks can be any other kind of code.
- Just like Python, indentation is the only way to structure code.
- Unlike Python, only tabs are allowed (thus no space), and the number of tabs must be exactly one higher for a deeper block.
- The presence of an "elif" and "else" is optional.
- While
- ^^^^^
- The WHILE construct is similar to that found in other languages.
- Contrary to Python, there is no support for the "else" construct.
- Its structure is as follows::
- while condition:
- action
- Conditions and actions are similar to the If construct.
- Break
- ^^^^^
- The BREAK construct is similar to that found in other languages.
- Contrary to Python, it is followed by an exclamation mark to differentiate it from the variable *break*.
- Its structure is as follows::
- while condition:
- break!
- While the Modelverse supports breaking out of multiple loops simultaneously, this is not currently supported by the HUTN parser.
- Continue
- ^^^^^^^^
- The CONTINUE construct is similar to that found in other languages.
- Contrary to Python, it is followed by an exclamation mark to differentiate it from the variable *continue*.
- Its structure is as follows::
- while condition:
- continue!
- While the Modelverse supports continuing a higher loop directly, this is not currently supported by the HUTN parser.
- Return
- ^^^^^^
- The RETURN construct is again similar to how it is expected.
- To prevent ambiguity in the grammar, an exclamation mark should follow after the expression to return.
- Its structure is as follows::
- return expression!
- The expression can be any expression, similar to the condition in an If and While.
- When the function has returntype void, the expression must be empty::
- return!
- Function call
- ^^^^^^^^^^^^^
- Function calls happen like usual, by appending an opening and closing parenthesis at the end.
- Its structure is as follows::
- my_function(argument_a, argument_b)
- Arguments can again be any kind of expression.
- Named parameters are not supported in the grammar, though the Modelverse can internally handle them.
- Function definition
- ^^^^^^^^^^^^^^^^^^^
- Defining a function makes the function available as a variable in the remainder of the context.
- Forward declaration is unnecessary: all function names are retrieved before going over the body of the functions.
- This makes mutual recursion easy.
- A function needs to define its return type, as well as the type of all its parameters.
- In case the type is unknown, or can be anything, *Element* can be used as a kind of "void pointer".
- Its structure is as follows::
- Element function function_name(parameter_a : Integer):
- body_of_the_function
- First comes the returntype, followed by the keyword "function".
- Again, indentation needs to happen using tabs.
- Assignment
- ^^^^^^^^^^
- Assignment is like usual.
- Its structure is::
- a = expression
- This assumes that a is already defined.
- Declaration
- ^^^^^^^^^^^
- All variables used in the Modelverse need to be defined statically.
- Defining a variable reserves a part of the Modelverse State to hold the value for this value.
- As such, variables cannot be used if they are not defined.
- Additionally, a declared variable will have a type defined with it.
- Again, if the type can vary or is unknown, this can be *Element*.
- .. warning::
- Contrary to other languages, declaring a variable does not equal defining the variable.
- Therefore, after a variable is declared, it can be used, but its contents will be non-existing.
- It is wise to always assign a value to a declared value right after declaration!
- Its structure is as follows::
- String abc
- There are two kinds of declaration: local declaration, and global declaration.
- Local declarations happen in the current block of the code, and their references get removed when exiting the block.
- Global declarations have identically the same syntax, but are specified at the top level (*i.e.*, they have no tabs in front of them).
- Global declarations do not assign a value and are thus external references, to be defined in the future or by some other module.
- Sharing between modules is possible this way, as all global names are linked together when linking the application together.
- A global declaration that is never defined is flagged as invalid by the compiler and will prevent compilation.
- Definition
- ^^^^^^^^^^
- Defining a variable is similar to a global declaration, but now there is an immediate assignment.
- Immediate assignment is only possible at the top level.
- Note that the assignment must be of a constant which requires no execution of functions or such.
- Also, it is impossible to assign the value of another variable.
- Its structure is as follows::
- String abc = "abc"
- It is possible that a definition does not yet know the value to assign, or is an empty element.
- To notify the other files that this is the defining element, use the import syntax to assign *?* to it::
- Element abc = ?
- This results in the Modelverse creating an empty node as placeholder for a value.
- The value will now be defined and accessible.
- Imports
- ^^^^^^^
- Direct imports in the Modelverse are possible, but are not recommended for ordinary users.
- This syntax allows a variable to be bound to an absolute location in the Modelverse, which is resolved at compile time.
- The primary use case for this is the creation of bootstrap files, but it is also possible for other purposes.
- Use with care!
- Its structure is as follows::
- String abc = ?path/in/modelverse
- A special case is when the path is empty, which indicates that a new (anonymous) node should be created for it::
- Element abc = ?
- Include
- ^^^^^^^
- Other files can easily be included using the include syntax.
- This is a raw include: the contents of the file are just copied inside the file at that exact spot.
- Generally, you should only include *.alh* files, unless you know what you are doing.
- There is a set of standard header files included in the Modelverse, which are probably the only ones you will need.
- Constants
- ^^^^^^^^^
- The Modelverse supports a set of constants that can be used.
- All constants can be assigned in a definition of globals.
- ============= ========================================================================== =======
- Constant type Values Example
- ============= ========================================================================== =======
- Integer Any possible integer, either positive (no prefix) or negative (- prefix) 1
- Float Any possible floating point value; scientific notation is not supported 1.0
- Boolean Either True or False True
- String Any possible string, enclosed with either double or single quotes "abc"
- Action An action construct from the Modelverse, prefixed with an exclamation mark !If
- ============= ========================================================================== =======
- Operators
- ^^^^^^^^^
- While operators are seemingly supported by the compiler, these are actually expanded to function calls to relevant functions.
- For example, *1 + 2*, is expanded to *integer_addition(1, 2)*.
- To do this conversion, it is mandatory that the type of both arguments can be determined statically.
- User I/O
- ^^^^^^^^
- User input and output is done through the keyworded operations *input()* and *output(msg)*.
- *input()* returns the first message in the input queue of the current user, potentially blocking until there is input.
- *output(msg)* places the value of the expression in the output queue of the current user.
- All I/O is done using queues: the value is only read and written to a specific place in the Modelverse.
- To actually read or write it at the client side, these special places should be accessed through dedicated Modelverse operations.
- Primitive operations
- ^^^^^^^^^^^^^^^^^^^^
- Apart from the minimal syntax, the Modelverse supports a wide library of functions.
- These functions are all built on top of the previously defined constructs.
- A list of these functions is shown below.
- * *Boolean function value_eq(a: Element, b: Element)*
- * *Boolean function value_neq(a: Element, b: Element)*
- * *Boolean function bool_and(a: Boolean, b: Boolean)*
- * *Boolean function bool_or(a: Boolean, b: Boolean)*
- * *Boolean function bool_not(a: Boolean)*
- * *Element function create_node()*
- * *Element function create_edge(a: Element, b: Element)*
- * *Element function create_value(a: Element)*
- * *Boolean function is_edge(a: Element)*
- * *Integer function read_nr_out(a: Element)*
- * *Element function read_out(a: Element, b: Integer)*
- * *Integer function read_nr_in(a: Element)*
- * *Element function read_in(a: Element, b: Integer)*
- * *Element function read_edge_src(a: Element)*
- * *Element function read_edge_dst(a: Element)*
- * *Boolean function delete_element(a: Element)*
- * *Boolean function element_eq(a: Element, b: Element)*
- * *Boolean function element_neq(a: Element, b: Element)*
- * *Float function cast_float(a: Element)*
- * *String function cast_string(a: Element)*
- * *Boolean function cast_boolean(a: Element)*
- * *Integer function cast_integer(a: Element)*
- * *String function cast_id(a: Element)*
- * *String function cast_value(a: Element)*
- * *Element function dict_add(a: Element, b: Element, c: Element)*
- * *Element function dict_add_fast(a: Element, b: Element, c: Element)*
- * *Element function dict_delete(a: Element, b: Element)*
- * *Element function dict_delete_node(a: Element, b: Element)*
- * *Element function dict_read(a: Element, b: Element)*
- * *Element function dict_read_edge(a: Element, b: Element)*
- * *Element function dict_read_node(a: Element, b: Element)*
- * *Integer function dict_len(a: Element)*
- * *Boolean function dict_in(a: Element, b: Element)*
- * *Boolean function dict_in_node(a: Element, b: Element)*
- * *Element function dict_keys(a: Element)*
- * *Float function float_addition(a: Float, b: Float)*
- * *Float function float_subtraction(a: Float, b: Float)*
- * *Float function float_multiplication(a: Float, b: Float)*
- * *Float function float_division(a: Float, b: Float)*
- * *Float function float_neg(a: Float)*
- * *Boolean function float_gt(a: Float, b: Float)*
- * *Boolean function float_gte(a: Float, b: Float)*
- * *Boolean function float_lt(a: Float, b: Float)*
- * *Boolean function float_lte(a: Float, b: Float)*
- * *Integer function integer_addition(a: Integer, b: Integer)*
- * *Integer function integer_subtraction(a: Integer, b: Integer)*
- * *Integer function integer_multiplication(a: Integer, b: Integer)*
- * *Integer function integer_division(a: Integer, b: Integer)*
- * *Integer function integer_modulo(a: Integer, b: Integer)*
- * *Boolean function integer_gt(a: Integer, b: Integer)*
- * *Boolean function integer_gte(a: Integer, b: Integer)*
- * *Boolean function integer_lt(a: Integer, b: Integer)*
- * *Boolean function integer_lte(a: Integer, b: Integer)*
- * *Boolean function integer_neg(a: Integer)*
- * *Element function list_read(a: Element, b: Integer)*
- * *Element function list_append(a: Element, b: Element)*
- * *Element function list_insert(a: Element, b: Element, c: Integer)*
- * *Element function list_delete(a: Element, b: Integer)*
- * *Integer function list_len(a: Element)*
- * *Boolean function list_in(a : Element, b : Element)*
- * *Element function set_add(a: Element, b: Element)*
- * *Element function set_add_node(a: Element, b: Element)*
- * *Element function set_pop(a: Element)*
- * *Element function set_read(a : Element)*
- * *Element function set_remove(a: Element, b: Element)*
- * *Boolean function set_in(a: Element, b: Element)*
- * *Element function set_remove_node(a: Element, b: Element)*
- * *Element function set_in_node(a: Element, b: Element)*
- * *Integer function set_len(a: Element)*
- * *String function string_join(a: String, b: String)*
- * *String function string_get(a: String, b: Integer)*
- * *String function string_substr(a: String, b: Integer, c: Integer)*
- * *Integer function string_len(a: String)*
- * *Element function string_split(a: String, b: String)*
- * *Boolean function string_startswith(a: String, b: String)*
- * *String function log(a: String)*
- * *Element function read_root()*
- * *Element function read_taskroot()*
- * *Element function input()*
- * *Element function output(a : Element)*
- * *Boolean function is_physical_int(a : Element)*
- * *Boolean function is_physical_float(a : Element)*
- * *Boolean function is_physical_string(a : Element)*
- * *Boolean function is_physical_action(a : Element)*
- * *Boolean function is_physical_boolean(a : Element)*
- * *Boolean function is_physical_none(a : Element)*
- * *Boolean function is_error(a : Element)*
- * *Boolean function has_value(a : Element)*
- * *Float function time()*
- * *String function hash(a : String)*
- * *Void function sleep(a : Float)*
- * *Void function interruptable_sleep(a : Float)*
- * *Element function exec(a : Element)*
- * *Element function resolve(var_name : String)*
- * *Boolean function has_input()*
- * *Element function list_pop(lst : Element, index : Integer)*
- * *Element function list_pop_final(lst : Element)*
- * *Element function set_copy(set : Element)*
- * *String function set_to_string(set : Element)*
- * *String function list_to_string(set : Element)*
- * *String function dict_to_string(dict : Element)*
- * *Element function set_overlap(sa : Element, sb : Element)*
- * *Element function set_equality(sa : Element, sb : Element)*
- * *Element function dict_eq(da : Element, db : Element)*
- * *Element function dict_copy(dict : Element)*
- * *Element function set_to_list(s : Element)*
- * *Element function create_tuple(a : Element, b : Element)*
- * *Void function dict_overwrite(a : Element, b : Element, c : Element)*
- * *Void function set_merge(sa : Element, sb : Element)*
- * *Element function make_reverse_dictionary(dict : Element)*
- * *Element function set_create()*
- * *Element function list_create()*
- * *Element function dict_create()*
- * *String function reverseKeyLookup(a: Element, b: Element)*
- * *Element function reverseKeyLookupMulti(a: Element, b: Element)*
- * *Element function dict_values(dict : Element)*
|