123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865 |
- \chapter{Modelverse Kernel}
- We will now consider the Modelverse Kernel (MvK), which is responsible for the execution of action code.
- Execution of action code causes changes to the PTM, which need to be handled by the MvS.
- As such, the Modelverse Kernel is responsible for the mapping between the user-level and the PTM.
- Users can create action code constructs directly, thus forming a direct interface to the MvS for the user.
- Alternatively, users can create models using a formalism which has action code constructs defined (\textit{e.g.}, to define the model semantics).
- As everything is modelled explicitly (\axiomModelEverything), both the execution context and instructions to execute are part of the MvS, and can thus be accessed by the MvK and ultimately the user.
- When executing an action language model, the execution context is modified in the MvS.
- Therefore, the MvK itself does not have any local state.
- By making all states and intermediate steps explicit, we obtain enhanced debugability and introspection.
- This furthermore contributes to \axiomForeverRunning, as it allows action code to modify other action code (\textit{i.e.}, self-modifiability).
- We first introduce the notion of transformations for our graph, subsequently called graph transformations
- \footnote{They are called graph transformations, though are different from the usual meaning of graph transformations in the literature. While the idea is similar, we provide a different mapping as we do not work on Typed Attributed Graphs (TAGs).}.
- Such transformations consist of a matching part, which we will use to determine if the execution context is well-defined, and a rewriting part, which we can use to define the action language semantics by defining transformations of the execution context.
- \section{Graph transformations}
- Before we can use graph transformations in our well-formedness check and semantical definition, we need to define them first.
- We need to bridge the gap between graph transformations and the CRUD operations defined by our MvS interface.
- For each transformation rule, it is possible to decompose it in four distinct (sequentially ordered) components.
- The first two are read operations, which are used for the matching, and the last two are create and delete operations, which are used for the rewriting.
- \begin{enumerate}
- \item \textbf{Positive read} operations are used for elements which are present before and after execution of the transformation rule.
- They are used for finding a possible match during the matching phase.
- Note that all elements need to be matched, even those that are about to be removed.
- All elements that are now matched, can be used during the rewriting phase.
- Elements that are simply required for the match, but without any changes to them, are visualized by black, solid lines.
- \item \textbf{Negative read} operations are used for the negative application conditions.
- Such elements should not be present before application of the transformation rule.
- If they are present in a match, the match is considered incorrect and another match is searched for.
- Elements which are searched for here, can of course not be used during the rewriting phase, as we explicitly required that they are absent.
- They are visualized by a red, dotted line.
- \item \textbf{Delete} operations are used for elements that need to be removed during the rewriting phase.
- Elements which should be removed, should also be matched in the positive read operation.
- These elements are visualized by a blue, dashed line.
- \item \textbf{Create} operations are used for elements that need to be created during the rewriting phase.
- Because the element is newly created, it does not need to be matched by a positive read operation.
- However, we do not require them to be absent either.
- They are visualized by a green, wide solid line.
- \end{enumerate}
- Each rule can be written in the following form, assuming success status, thus mapping to our previously defined formalization of the MvS.
- If an error is encountered, it is propagated to the user.
- \begin{center}
- \begin{tabular}{c}
- $PositiveRead_A(G)$ \\
- $NegativeRead_A(G)$ \\
- $G' = Create_A(G)$ \\
- $G'' = Delete_A(G')$ \\
- \hline
- $G \xrightarrow{step_A} G''$
- \end{tabular}
- \end{center}
- Each rule uses the matched elements, which get bound during application.
- As such, the $PositiveRead$ operation binds the variables, which are then used in the $NegativeRead$ to detect invalid matches, in the $Delete$ to delete elements, and in the $Create$ to create new elements.
- For conciseness, we define the shorthand notation for graph elements shown in Fig.~\ref{fig:shorthand_notation_a}, equivalent to Fig.~\ref{fig:shorthand_notation_b}, meaning:
- \begin{figure}
- \center
- \begin{subfigure}[c]{0.2\columnwidth}
- \includegraphics[width=\textwidth]{images/shorthand_notation.eps}
- \caption{Shorthand notation}
- \label{fig:shorthand_notation_a}
- \end{subfigure}
- \begin{subfigure}[c]{0.2\columnwidth}
- \includegraphics[width=\textwidth]{images/shorthand_notation_expanded.eps}
- \caption{Expanded notation}
- \label{fig:shorthand_notation_b}
- \end{subfigure}
- \caption{Shorthand notation and equivalent expanded notation.}
- \label{fig:shorthand_notation}
- \end{figure}
- \begin{gather*}
- (X_{node}, W_{link}, Y_{node}) \in E \\
- (W_{link}, e, W_{node}) \in E \\
- N_V(X_{node}) = X \\
- N_V(Y_{node}) = Y \\
- N_V(W_{node}) = W \\
- \end{gather*}
- If $X$, $Y$, or $W$ is not shown in the shorthand notation, then the $N_V$ mapping is unconstrained, and might not even exist.
- An example of the mapping between the shorthand notation and the previously defined semantics is given next.
- We explain the transformation shown in Figure~\ref{fig:example_transformation}.
- First, the \textit{success} status code is stored in $s$ (equation~\ref{eqn:status}), to shorten subsequent rules.
- All parts of the rule are assumed to result in a success status code.
- The positive read operations start at equation~\ref{eqn:positive_read}, followed by the negative read operation at equation~\ref{eqn:negative_read}.
- Now that all nodes and edges are bound, the create operation creates the necessary links starting from equation~\ref{eqn:create}.
- From equation~\ref{eqn:delete} to the end, operations try to match the edge to delete at a finer granularity and delete it in equation~\ref{eqn:delete_true}.
- \begin{figure}
- \center
- \includegraphics[width=0.2\textwidth]{images/example_transformation.eps}
- \caption{Example graph transformation which is expanded}
- \label{fig:example_transformation}
- \end{figure}
- \begin{center}
- \begin{tabular}{p{7cm}}
- {
- \begin{gather}
- s = (100, \_) \label{eqn:status}\\
- R_{dict}(G, a, "A") = (b, s) \label{eqn:positive_read}\\
- R_{dict}(G, a, "B") = (c, s) \\
- R_{dict}(G, a, "F") = (e, s) \\
- R_V(G, e) = ("E", s) \\
- \not \exists d : R_{dict}(G, d, "C") = (c, s) \label{eqn:negative_read}\\
- C_{NV}(G, "D") = (G', f, s) \label{eqn:create}\\
- C_E(G', b, c) = (G'', g, s) \\
- C_E(G'', g, f) = (G''', h, s) \\
- \langle V_1, s \rangle = R_O(G''', a, s) \label{eqn:delete}\\
- \langle V_2, s \rangle = R_I(G''', c, s) \\
- i \in V_1 \\
- i \in V_2 \\
- \langle V_3, s \rangle = R_O(G''', i, s) \\
- j \in V_3 \\
- R_E(G''', j) = ((i, k), s) \\
- R_V(G''', k) = ("B", s) \\
- D_E(G''', i) = (G'''', s) \label{eqn:delete_true}
- \end{gather}
- } \\
- \hline
- \centering $G \xrightarrow{step_{A}} G''''$
- \end{tabular}
- \end{center}
- Note that this does not explicitly remove all parts of the edge.
- Specifically, the node containing the data value still remains.
- This is because it might still be referenced from somewhere else, and deleting that might have serious repercussions.
- As a safety measure, only the link itself is removed.
- All elements that are no longer reachable from the root will later be removed in the garbage collection phase.
- The current notion of graph transformations should not be confused with the notion of model transformations, which the user can use.
- The graph transformations we have defined here, are merely a conceptual construct, used to formalize the semantics of action language constructs.
- It is therefore not mandatory for an MvK implementation to implement the semantics as if it were a graph transformation.
- On the other hand, model transformations, which are implemented on top of action language constructs, will be usable by the user, and as such are really implemented as transformations.
- Furthermore, model transformations will be at a level closer to the user (\textit{i.e.}, not on raw graphs), and will therefore be typed.
- We will not discuss model transformations any further in this technical report, as this is part of future work.
- \subsection{Performance}
- It is important to mention that our graph transformations do not use the notion of types.
- As we are working on simple graphs, which do not have a real notion of type, and it can therefore not be used.
- This implies that \emph{nodes} in the transformation rules can as well be edges, since the semantics of a point in the tranformation rules is simply an identifier from $IDS$.
- Conversely, this might imply a performance impact, as the only basis to determine a match is the use of primitive values, and edges between specific nodes.
- While this is a concern relating to \axiomScalability, it is not a fundamental problem for the following reasons:
- \begin{enumerate}
- \item We start from a pivot, which is the Modelverse Root previously defined.
- All MvK instances will know which node to use for this, and therefore there is already a place where the matching can start.
- \item At most one match exists.
- This means that we can already stop searching as soon as a single match is found.
- \item All edges have a constant on them, which needs to be unique.
- Therefore, no trackbacking is required as soon as the correct edge is found: each edge will be identifiable.
- \item A primitive operation exists to read out the aforementioned edges: the $R_{dict}$ operations.
- If this operation is implemented in $\mathcal{O}(1)$ (\textit{e.g.}, using hashmaps), this means that the complexity of finding a match is unrelated to the size of the host graph.
- \end{enumerate}
- Combining all of this information, we can write a simple algorithm for each rule, which starts from the Modelverse Root, and just performs a serie of $R_{dict}$ operations.
- As there is only one possible result for that operation, we do not need to rely on backtracking.
- Some exceptions exist to these findings though, such as the accessing of variables in the symbol table.
- These do not use values on the edge that are in $\mathbb{U}$, and therefore require more advanced algorithms.
- \section{Execution context}
- We specify the structure of the execution context by defining a graph that has to be matched.
- If the graph is matched, the execution context is valid and execution is possible if the current instruction is valid.
- If no match can be found for the specified user, the user's execution context is invalid and execution is impossible.
- If multiple matches are found, the execution context is also invalid, and results will be undefined.
- We make no distinction between \textit{no execution context} (\textit{i.e.}, nothing at all) and \textit{corrupt execution context} (\textit{e.g.}, a single missing link).
- In either case, no execution is possible.
- During normal operation, the user is unable to corrupt or remove its own execution context, as all action language primitives are guaranteed to keep the execution context in a valid state.
- But in case introspection or self-modifiability is used (\textit{i.e.}, if an intentional change is made by the user), it is possible to alter, and possibly corrupt, the execution context.
- This is possible because the execution context is itself again another model in the MvS, and it can be manipulated like any other.
- We do this to enable self-modifiability, introspection, reflection, and debugability, which can now be performed directly on the graph.
- For debugging it is even possible for another (privileged) user to debug the state of another user, or process.
- \begin{figure}
- \center
- \includegraphics[width=0.25\columnwidth]{images/execution_context.eps}
- \caption{Graph to match as execution context. Some nodes might be identical.}
- \label{fig:execution_context}
- \end{figure}
- A valid execution context is one that is matched by the structure in Fig.~\ref{fig:execution_context}.
- At the top of the structure sits the Modelverse root node, which is a node that is known to the MvK.
- From this root node, there is a link to all user root nodes, containing the name of the user.
- In our figure, \textit{username} has to be interpreted as a variable for the transformation.
- From the user root, there are links for \textit{input} and \textit{output} lists, and a \textit{``frame''} link.
- These input and output links come with both an initial link, and the \textit{last\_} variant, which points to the last element of the list.
- The last element will always be empty (have no value), but needs to be there to guard for the case of an empty list.
- Each element in such a list will have a \textit{``value''} link, which points to the actual value, and a \textit{``next''} link, which points to the next element in the list.
- The exception to this is the element pointed to by the \textit{``last\_''} element, which is the empty placeholder.
- The destination of the \textit{``frame''} link is the currently active execution frame for that user.
- Each execution frame has several outgoing links.
- First is the \textit{``symbols''} link, which points to the symbol table.
- A symbol table is a node which is interpreted as a dictionary, where all outgoing edges have a unique key.
- The symbol table can then be accessed using the $R_{dict}$ CRUD operation of the MvS.
- In this case, the key is the variable definition in the code being executed.
- The destination of the edge is the current value of the specified variable.
- It is not possible to save this variable in the executing code itself, as the code can possibly be executed by different users simultaneously (\axiomMultiUser).
- Second is the \textit{``IP''} link, which points to the current action code primitive being executed.
- It is similar to an Instruction Pointer, with the exception that it does not advance linearly, nor is there a default direction.
- Every instruction primitive is responsible to update the instruction pointer.
- Third is the \textit{``evalstack''} link, pointing to the evaluation stack.
- In this stack, instructions are stored, which need to be made in the same scope.
- Such a structure is necessary because we do not use compiled bytecodes which modify a stack.
- For example, for the execution of an \textit{If} construct, we first need to evaluate the condition.
- In such ``stack-based'' languages, the result of the condition is first put on the stack by the appropriate bytecodes.
- Only then a bytecode concerning the \textit{If} construct is encountered, which consumes the evaluated value on the stack.
- In our language, the \textit{If} is encountered first, which then explicitly states to evaluate the condition first (by moving the ``IP'' link), and come back as soon as it is evaluated (by putting it on the evaluation stack).
- There is also the \textit{``phase''} link, allowing for a distinction between the different sub-states in the evaluation of a primitive.
- For example in the \textit{If} construct, a distinction between the \textit{``evaluate condition''} and \textit{``branch on value''} phases is necessary.
- To make this possible, the phase keeps the current state of the evaluation of that specific execution primitive.
- Finally, there is the \textit{``returnvalue''} link, which links to a node which contains the value from the previous execution.
- Each instruction primitive can read and update this link.
- It is used for the exchange of temporary values between different instructions.
- In contrast to languages which use a stack, there is only one temporary variable in our language.
- This offers us a slightly more efficient implementation of most constructs, due to avoiding the use of a list.
- Some constructs get more complex (though not necessarily slower, performance-wise), such as those where it is natural to evaluate multiple values sequentially.
- Some additional links might be present on the frame, and their use is mandatory, though they are not required for a well-defined execution context.
- These links are the \textit{``prev''} and \textit{``variable''} links.
- The ``prev'' links to the previous execution frame, that is, the invoker of the function for which the frame is created.
- The ``variable'' link is used during assignment, as we need two evaluated elements at the same time: the variable to write to, and the value to assign.
- If these links are not present at the time where they are necessary, the execution context is considered to be corrupt.
- These optional links could be made mandatory, by setting making them point to an empty node if they are not necessary.
- The execution context is well-defined if exactly one such match is found for a given user.
- No matches means that there is no execution context with all required elements (\textit{i.e.}, either corrupt or completely missing).
- Multiple matches indicate non-determinism and are therefore not allowed.
- Additional elements, though not indicated here, are allowed, as they do not interfere with the match.
- These elements should be considered implementation-dependent and should not be used for the implementation of functional requirements.
- Apart from the user-specific execution context, there is also a global symbol table, stored as if it were the \textit{``\_\_global''} user.
- This symbol table is shared by all users, and is accessed if a variable could not be found in the local symbol table of the current execution frame.
- \section{Execution primitives}
- What remains is the semantics of each of the action language constructs.
- For each construct, defined in $\mathbb{A}$, the required modifications on the execution context needs to be defined.
- As proposed in previous sections, graph transformations are used to define the semantics.
- These graph transformation rules are defined such that there should always be exactly one possible match.
- If no matches can be found, this indicates that the execution context, the current action language primitive, or both, are invalid.
- If multiple matches are found, non-determinism is possible, which is disallowed.
- In the presence of multiple users (\axiomMultiUser), interleaving is necessary between them to guarantee fairness.
- This also prevents uninterruptible loops (\axiomForeverRunning), as another (privileged) user can then always halt the execution of another user.
- For performance reasons (\axiomScalability), an MvK can ignore updates to the execution context (\textit{e.g.}, by not propagating them to the MvS, or by implementing compiled operations).
- But this comes at the cost of debugability, introspection, and self-modifiability.
- Hybrid approaches are supported, meaning that some functions will be called without modifications to the execution context (\textit{e.g.}, primitive operations such as integer addition), whereas others modify to the execution context.
- A step function is defined for each user, which applies the only applicable rule.
- \begin{center}
- \begin{tabular}{c}
- $G \xrightarrow{step_A} G' \; \lor$ \\
- $G \xrightarrow{step_B} G' \; \lor$ \\
- $...$ \\
- \hline
- $G \xrightarrow{step} G'$ \\
- \end{tabular}
- \end{center}
- The interleaving of different users, and thus of different steps, is not specified, as long as there is some fairness between all users.
- This allows for the definition of primitive operations in the Modelverse Kernel, which consist of several (atomically executed) instructions.
- Such primitives can then be used for performance reasons (\axiomScalability), but also as a core function (\axiomModelEverything).
- In Table~\ref{table:well-formed}, we present an overview of all specified outgoing links for each primitive element.
- A construct is valid if all mandatory elements are present.
- Links which guide the instruction pointer, require the target of the link to be executable (\textit{i.e.}, be another primitive construct, $\in \mathbb{A}$).
- If that is not the case, execution will terminate.
- Normally, the action language constructs are created by a different tool, such as a HUTN MvI, which will guarantee that the constructs are well formed.
- But it is possible for users to access all parts of the MvS, thanks to \axiomModelEverything, and therefore to manually create (or alter) action language constructs.
- Such actions cannot automatically be checked for correctness, due to our lack of typing: there is no metamodel for the primitive action language constructs.
- And since there is no metamodel, there is no constraint on the graph.
- Although counter-intuitive, this is actually what we want: unconstrained modifications on the raw model representation, thus allowing model management operations.
- In the next chapter, we will add a layer on top of all this, which is typed and more constrained.
- Notwithstanding, it is possible to create a function which manually checks whether or not a construct is well-defined, using the information from Table~\ref{table:well-formed}.
- \begin{table}
- \center
- \begin{tabular}{lcccl}
- Construct & Name & Mandatory & Executable & Meaning (informal) \\
- \hline
- \hline
- \multirow{4}{*}{If} & cond & Yes & Yes & Condition \\
- & true & Yes & Yes & Block to execute if condition is True \\
- & false & No & Yes & Block to execute if condition is False \\
- & next & No & Yes & Next instruction after True/False block \\
- \hline
- \multirow{3}{*}{While} & cond & Yes & Yes & Condition \\
- & body & Yes & Yes & Body to execute while condition is True \\
- & next & No & Yes & Next instruction after condition is False \\
- \hline
- \multirow{1}{*}{Break} & while & Yes & Yes & While construct that should be broken \\
- \hline
- \multirow{1}{*}{Continue}& while & Yes & Yes & While construct that should be continued \\
- \hline
- \multirow{1}{*}{Access} & var & Yes & Yes & Variable to access \\
- \hline
- \multirow{1}{*}{Resolve}& var & Yes & No & Variable definition to access \\
- \hline
- \multirow{3}{*}{Assign} & var & Yes & Yes & Variable to assign to \\
- & value & Yes & Yes & Value to assign to variable \\
- & next & No & Yes & Next instruction after assignment \\
- \hline
- \multirow{4}{*}{Call} & func & Yes & Yes & Function signature to call \\
- & next & No & Yes & Next instruction after function call returned \\
- & params & Yes & No & First parameter, linking to a \textit{Parameter} \\
- & last\_param & Yes & No & Last parameter, linking to a \textit{Parameter} \\
- \hline
- \multirow{3}{*}{Parameter}& name & Yes & No & Name of the parameter, used to link with the formal parameters \\
- & value & Yes & Yes & Instructions to evaluate as parameter \\
- & next\_param & No & No & Next parameter to be evaluated (optional if this is the last parameter) \\
- \hline
- \multirow{1}{*}{Return} & value & No & Yes & Value to return \\
- \hline
- \multirow{1}{*}{Const} & node & Yes & No & Node containing the constant to access \\
- \hline
- \multirow{1}{*}{Input} & & N/A & N/A & N/A \\
- \hline
- \multirow{1}{*}{Output} & value & Yes & Yes & The node to output \\
- \end{tabular}
- \caption{Outgoing link specification.}
- \label{table:well-formed}
- \end{table}
- \clearpage
- \subsection{If}
- \begin{figure}[h!]
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_if__cond.eps}
- \caption{Evaluate condition}
- \label{fig:rule_if_a}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_if__true.eps}
- \caption{Returned True}
- \label{fig:rule_if_b}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_if__false-else.eps}
- \caption{Returned False and there is an 'else' block}
- \label{fig:rule_if_c}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_if__false-nothing.eps}
- \caption{Returned False but there is no 'else' block}
- \label{fig:rule_if_d}
- \end{subfigure}
- \caption{If branch rules}
- \end{figure}
- The \textit{If} construct will first evaluate the condition (\textit{cond} link) by moving the instruction pointer there.
- It signals that it should be executed again afterwards, but now in phase \textit{cond}, by putting this on the evaluation stack (Figure~\ref{fig:rule_if_a}).
- As soon as the condition is evaluated, and the \textit{If} popped back from the stack, the return value (of the condition) can either be True or False.
- If it is True (Figure~\ref{fig:rule_if_b}), the \textit{then} link is executed, and the \textit{if} is pushed on the stack again, but now in the final phase \textit{finish}.
- This is the phase which signals to another rule that this operation has finished, and the next instruction can be loaded.
- If it is False, and there is an \textit{else} link (Figure~\ref{fig:rule_if_c}), it is executed, similar to the previous case.
- If it is False, but there is no \textit{else} link (Figure~\ref{fig:rule_if_d}), the \textit{If} is marked as completed immediately, without any subsequent actions.
- \clearpage
- \subsection{While}
- \begin{figure}[h!]
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_while__cond.eps}
- \caption{Evaluate the condition of the While}
- \label{fig:rule_while_a}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_while__true.eps}
- \caption{Condition was true}
- \label{fig:rule_while_b}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_while__false.eps}
- \caption{Condition was false}
- \label{fig:rule_while_c}
- \end{subfigure}
- \caption{While loop rules}
- \end{figure}
- The \textit{While} construct will first evaluate the condition (\textit{cond} link) by moving the instruction pointer there.
- It signals that it should be executed again afterwards, but now in phase \textit{cond}, by putting this on the stack (Figure~\ref{fig:rule_while_a}).
- As soon as the condition is evaluated, and the \textit{While} popped from the stack, the return value (of the condition) can either be True or False.
- If it is True (Figure~\ref{fig:rule_while_b}), the \textit{body} link is executed, and the \textit{While} is pushed on the stack again, but with its phase set to \textit{init}.
- This way, the while construct will again be executed after the body has terminated.
- By setting the phase to \textit{init}, we effectively cause looping, as the condition will again be evaluated, and, depending on the result, the body gets executed once more.
- If it is False (Figure~\ref{fig:rule_while_c}), the \textit{While} is immediately marked as finished and the body is not executed.
- \clearpage
- \subsection{Break}
- \begin{figure}[h!]
- \center
- \includegraphics[width=0.49\textwidth]{images/rule_break.eps}
- \caption{Break rule}
- \label{fig:rule_break}
- \end{figure}
- The \textit{Break} construct will move the instruction pointer back to the \textit{While} construct it belongs to (Figure~\ref{fig:rule_break}).
- The phase is set to \textit{finish} to indicate that the loop has finished.
- This prevents the condition evaluation and marks the end of the while loop.
- \subsection{Continue}
- \begin{figure}[h!]
- \center
- \includegraphics[width=0.49\textwidth]{images/rule_continue.eps}
- \caption{Continue rule}
- \label{fig:rule_continue}
- \end{figure}
- The \textit{Continue} construct will move the instruction pointer back to the \textit{While} construct to which it belongs (Figure~\ref{fig:rule_continue}).
- The phase is set to \textit{init} to indicate that the loop needs to continue.
- This causes the condition to be evaluated again, indicating the next iteration of the loop.
- \clearpage
- \subsection{Access}
- \begin{figure}[h!]
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_access__init.eps}
- \caption{Evaluate variable to access}
- \label{fig:rule_access_a}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_access__eval.eps}
- \caption{Access evaluated variable}
- \label{fig:rule_access_b}
- \end{subfigure}
- \caption{Variable dereference rules}
- \end{figure}
- The \textit{Access} construct will move the instruction pointer to the variable which has to be resolved first (Figure~\ref{fig:rule_access_a}).
- It signals that it needs to be executed again after the variable was resolved, by putting itself on the evaluation stack.
- After resolution of the variable, the value of the variable is accessed and set as the new return value (Figure~\ref{fig:rule_access_b}).
- \clearpage
- \subsection{Resolve}
- \begin{figure}[h!]
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_resolve__no-attr.eps}
- \caption{Access the variable from the local symbol table}
- \label{fig:rule_resolve_c}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_resolve__no-attr-global.eps}
- \caption{Access the variable from the global symbol table}
- \label{fig:rule_resolve_d}
- \end{subfigure}
- \caption{Resolution rules}
- \end{figure}
- With the \textit{resolve} rule, a variable is looked up in either the local (Figure~\ref{fig:rule_resolve_c}) or global (Figure~\ref{fig:rule_resolve_d}) symbol table.
- The variable in the symbol table will be set as the returnvalue.
- The local symbol table has priority over the global symbol table.
- Note that the returned value is only a reference, similar to the lvalue in parsers.
- A further \textit{Access} is required to read out the actual value.
- \clearpage
- \subsection{Assign}
- \begin{figure}[h!]
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_assign__init.eps}
- \caption{Resolve the variable to assign to}
- \label{fig:rule_assign_a}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_assign__value.eps}
- \caption{Evaluate the value to assign}
- \label{fig:rule_assign_b}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_assign__assign.eps}
- \caption{Assign the value to the variable}
- \label{fig:rule_assign_c}
- \end{subfigure}
- \caption{Assignment rules}
- \end{figure}
- The \textit{Assign} rule will first evaluate the variable (Figure~\ref{fig:rule_assign_a}), as it will first need to be resolved.
- After resolution (Figure~\ref{fig:rule_assign_b}), the found value is stored in a temporary link from the frame (\textit{variable} link).
- The instruction pointer is moved to the value that will be assigned, as it will also need to be evaluated.
- After the value is evaluated (Figure~\ref{fig:rule_assign_c}), the value link in the stored variable is changed to the evaluated value.
- \clearpage
- \subsection{Function call}
- \begin{figure}[h!]
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_call__resolve-no-params.eps}
- \caption{Resolve function without parameters}
- \label{fig:rule_call_a}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_call__resolve-params.eps}
- \caption{Resolve function with parameters}
- \label{fig:rule_call_b}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_call__call-no-params.eps}
- \caption{Execute call with no parameters}
- \label{fig:rule_call_c}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_call__call-params.eps}
- \caption{Execute call with parameters}
- \label{fig:rule_call_d}
- \end{subfigure}
- \caption{Function call rules for resolution and execution}
- \end{figure}
- A \textit{Call} construct has different paths, depending on how many parameters there are.
- The distinct situations are:
- \begin{enumerate}
- \item \textbf{No parameters}: in this simple case, the method is first resolved by moving the instruction pointer there, and the call is already put on the stack (Figure~\ref{fig:rule_call_a}).
- After the function is resolved (Figure~\ref{fig:rule_call_c}), the call is made by creating a new execution frame and making it the active frame.
- \item \textbf{One parameter}: similar to the previous situation, the function is first resolved (Figure~\ref{fig:rule_call_b}), but instead of putting the \textit{call} on the stack, the first parameter is used.
- Afterwards (Figure~\ref{fig:rule_call_f}), the stack is created for the resolved function, the instruction pointer is set to evaluate the argument, and the \textit{call} is put on the stack.
- When the parameter is evaluated (Figure~\ref{fig:rule_call_d}), the result is put in the symbol table of the new execution frame and the new frame is made active.
- \item \textbf{Two parameters}: similar to a single parameter, the first parameter is again put on the stack for after the function resolution (Figure~\ref{fig:rule_call_c}).
- When evaluating the first parameter (Figure~\ref{fig:rule_call_e}), the \textit{next\_param} parameter is put on the stack, instead of the \textit{call} phase.
- The second parameter is already the last parameter, so we then put the \textit{call} on the stack (Figure~\ref{fig:rule_call_g}).
- Finally, the function is called as with only a single parameter (Figure~\ref{fig:rule_call_d}).
- \item \textbf{More than two parameters}: similar to two parameters, but with an iteration rule (Figure~\ref{fig:rule_call_h}) for all parameters except the first and last.
- This iteration rule simply evaluates the parameters in order of their \textit{next\_param} links.
- \end{enumerate}
- In all cases, the \textit{finish} is put on the stack during the call to the function.
- As soon as the called function has finished, it will invoke a return and thus pop the active execution frame.
- This will make the current frame active again, which will then progress towards the next instruction.
- Parameter passing happens through the use of both named variables and positional parameters.
- However, the positional parameters are only used to determine the evaluation order, and not for binding of actual to formal parameter.
- It is possible for a front-end to offer positional parameters, by automatically mapping them onto their formal parameters.
- \begin{figure}[t!]
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_call__params-first-multi.eps}
- \caption{Set first parameter of multiple}
- \label{fig:rule_call_e}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_call__params-first-single.eps}
- \caption{Set first and only parameter}
- \label{fig:rule_call_f}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_call__params-last.eps}
- \caption{Set last parameter of multiple}
- \label{fig:rule_call_g}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_call__params-next.eps}
- \caption{Set next parameter}
- \label{fig:rule_call_h}
- \end{subfigure}
- \caption{Function call rules for parameter evaluation}
- \end{figure}
- \clearpage
- \subsection{Return}
- \begin{figure}[h!]
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_return__no-value.eps}
- \caption{Return without value}
- \label{fig:rule_return_a}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_return__value.eps}
- \caption{Evaluate the value of the return}
- \label{fig:rule_return_b}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_return__eval.eps}
- \caption{Return with the evaluated value}
- \label{fig:rule_return_c}
- \end{subfigure}
- \caption{Return rules}
- \end{figure}
- For the \textit{Return} construct, there are again two options: either there is a value to return, or there is none.
- If there is no return value (Figure~\ref{fig:rule_return_a}), the current execution frame is removed and the previous one is made active again, without touching the return value of the underlying frame.
- If there is a return value (Figure~\ref{fig:rule_return_b}), it is first evaluated by moving the instruction pointer there.
- After evaluation (Figure~\ref{fig:rule_return_c}), the evaluated value is stored in the returnvalue of the previous frame, and the current frame is deleted.
- \clearpage
- \subsection{Input and Output}
- \begin{figure}[h!]
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_output__init.eps}
- \caption{Output rule evaluates value}
- \label{fig:output_init}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_output__output.eps}
- \caption{Output rule outputs value}
- \label{fig:output_output}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_input.eps}
- \caption{Input rule consumes input}
- \label{fig:input}
- \end{subfigure}
- \end{figure}
- The \textit{Output} construct will first evaluate the element the 'value' link points to (Figure~\ref{fig:output_init}), and afterwards it puts the returnvalue in the output queue (Figure~\ref{fig:output_output}).
- The \textit{Input} construct will read the value that is in the input queue and put it in place of the returnvalue.
- No evaluation whatsoever is done on the values.
- \clearpage
- \subsection{Constant access}
- \begin{figure}[h!]
- \includegraphics[width=0.49\textwidth]{images/rule_const.eps}
- \caption{Constant access rule}
- \label{fig:rule_const}
- \end{figure}
- The \textit{Const} construct is used for constants, which are closely linked to the primitive data types presented in the Modelverse State.
- It is only used as an 'executable wrapper' for a literal: evaluation of this construct will yield the contained node (Figure~\ref{fig:rule_const}).
- The phase is also set to \textit{finish}, to indicate termination of the construct.
- \subsection{Helper rules}
- \begin{figure}[h!]
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_next__next.eps}
- \caption{Progress to the next instruction}
- \label{fig:rule_helper_a}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/rule_next__no-next.eps}
- \caption{Pop the next instruction from the stack}
- \label{fig:rule_helper_b}
- \end{subfigure}
- \caption{Next rules}
- \end{figure}
- When the instruction pointer points to an instruction which is marked as \textit{finish}ed, one of these helper rules becomes active.
- These are responsible for progressing towards the next instruction.
- Either there is a \textit{next} link (Figure~\ref{fig:rule_helper_a}), which links towards the next instruction to execute.
- If it is present, the instruction pointer is moved to this instruction, and the phase is reset to \textit{init} as it is the first time this construct is executed.
- In case no \textit{next} link exists (Figure~\ref{fig:rule_helper_b}), the next instruction is popped from the stack, together with its phase.
- This popping not only sets the instruction pointer, but also copies the saved phase, making it possible to progress where we left off.
- \clearpage
- \subsection{Declare}
- \begin{figure}[h!]
- \includegraphics[width=0.49\textwidth]{images/rule_declare__init.eps}
- \caption{Declare instruction}
- \label{fig:rule_declare__init}
- \end{figure}
- The Declare instruction will add the specified node to the symbol table, so that it can be assigned a value, or read out.
- As the declare does not take a value, the default value of the node is just an empty node.
- Future instructions can use the node connected to the Declare instruction to reference to the variable.
- \subsection{Global}
- \begin{figure}[h!]
- \includegraphics[width=0.49\textwidth]{images/rule_global__init.eps}
- \caption{Global declare instruction}
- \label{fig:rule_global__init}
- \end{figure}
- Apart from a declaration in the symbol table of the current user, it is also possible to declare it in the global namespace.
- This makes sure that other users can also find it and access the values.
- Its primary use will be function resolution though, as functions should be declared in a higher scope than the current scope.
- Nonetheless, it is possible to define everything else as a global too, making it accessible.
- \section{Primitive operations}
- As there are no special, built-in constructs for basic operations, such as mathematical operations, all of them have to map to a normal, user-level function.
- But these functions cannot implement the specified behaviour either, as the provided data values are MvS primitives.
- Such functions are primitive functions, which form the core of the MvK, and are hardcoded in the MvK implementation.
- Primitive functions are hardcoded functions in the MvK, which get loaded like normal operations (\textit{i.e.}, their parameters are evaluated and loaded on the stack).
- The execution of their body differs though, as it is executed without intermediate steps.
- As they cannot be written in Action Language, they do not have an implementation in the Action Language either.
- It is the MvK which recognizes that there is a primitive function available for the called function.
- If so, it calls the primitive instead of the (empty) body.
- To comply with our axioms (\axiomModelEverything), we need to model these functions explicitly.
- This can be done by taking the same approach as Squeak~\cite{Squeak}, where an interpreter is written in the interpreted language.
- Doing this, we can map the interpreted function (in the code being executed) to the primitive function of our used interpreter (in the implementation of the interpreter).
- Optionally, the interpreter could also be compiled, where these functions are then changed to primitive operations in the target language.
- The operations in Table~\ref{table:primitives_1} and~\ref{table:primitives_2} need to be defined as a primitive by all Modelverse Kernel implementations, with the specified semantics.
- None of them are allowed to modify any of the incoming parameters.
- Semantics are given in simple Python code.
- \begin{table}
- \center
- \begin{tabular}{llll}
- Name & Parameters & Returns & Semantics \\
- \hline
- \hline
- integer\_addition & $a$ : Integer; $b$ : Integer & $c$ : Integer & $c = a + b$ \\
- integer\_subtraction & $a$ : Integer; $b$ : Integer & $c$ : Integer & $c = a - b$ \\
- integer\_multiplication & $a$ : Integer; $b$ : Integer & $c$ : Integer & $c = a \times b$ \\
- integer\_division & $a$ : Integer; $b$ : Integer & $c$ : Integer & $c = a / b$ \\
- integer\_eq & $a$ : Integer; $b$ : Integer & $c$ : Bool & $c = a == b$ \\
- integer\_neq & $a$ : Integer; $b$ : Integer & $c$ : Bool & $c = a \neq b$ \\
- integer\_lt & $a$ : Integer; $b$ : Integer & $c$ : Bool & $c = a < b$ \\
- integer\_lte & $a$ : Integer; $b$ : Integer & $c$ : Bool & $c = a \leq b$ \\
- integer\_gt & $a$ : Integer; $b$ : Integer & $c$ : Bool & $c = a > b$ \\
- integer\_gte & $a$ : Integer; $b$ : Integer & $c$ : Bool & $c = a \geq b$ \\
- integer\_neg & $a$ : Integer & $c$ : Bool & $c = -a$ \\
- \hline
- float\_addition & $a$ : Float; $b$ : Float & $c$ : Float & $c = a + b$ \\
- float\_subtraction & $a$ : Float; $b$ : Float & $c$ : Float & $c = a - b$ \\
- float\_multiplication & $a$ : Float; $b$ : Float & $c$ : Float & $c = a \times b$ \\
- float\_division & $a$ : Float; $b$ : Float & $c$ : Float & $c = a / b$ \\
- float\_eq & $a$ : Float; $b$ : Float & $c$ : Bool & $c = a == b$ \\
- float\_neq & $a$ : Float; $b$ : Float & $c$ : Bool & $c = a \neq b$ \\
- float\_lt & $a$ : Float; $b$ : Float & $c$ : Bool & $c = a < b$ \\
- float\_lte & $a$ : Float; $b$ : Float & $c$ : Bool & $c = a \leq b$ \\
- float\_gt & $a$ : Float; $b$ : Float & $c$ : Bool & $c = a > b$ \\
- float\_gte & $a$ : Float; $b$ : Float & $c$ : Bool & $c = a \geq b$ \\
- float\_neg & $a$ : Float & $c$ : Bool & $c = -a$ \\
- \hline
- bool\_and & $a$ : Bool; $b$ : Bool & $c$ : Bool & $c = a \land b$ \\
- bool\_or & $a$ : Bool; $b$ : Bool & $c$ : Bool & $c = a \lor b$ \\
- bool\_not & $a$ : Bool & $c$ : Bool & $c = \neg a$ \\
- \hline
- list\_append & $a$ : Element; $b$ : Element & $a$ : Element & $a += b$ \\
- list\_insert & $a$ : Element; $b$ : Element; $c$ : Integer & $a$ : Element & $a.insert(b,c)$ \\
- list\_delete & $a$ : Element; $b$ : Integer & $a$ : Element & $a = a.pop(b)$ \\
- list\_len & $a$ : Element & $b$ : Integer & $b = len(a)$ \\
- \hline
- dict\_add & $a$ : Element; $b$ : Element, $c$ : Element & $a$ : Element & $a[b] = c$ \\
- dict\_delete & $a$ : Element; $b$ : Element & $a$ : Element & $delete~a[b]$ \\
- dict\_read & $a$ : Element; $b$ : Value & $c$ : Element & $c = a[b]$ \\
- dict\_read\_node& $a$ : Element; $b$ : Element & $c$ : Element & $c = a[b]$ \\
- dict\_len & $a$ : Element & $b$ : Integer & $b = len(a)$ \\
- dict\_in & $a$ : Element; $b$ : Value & $c$ : Boolean & $c = b in a$ \\
- dict\_in\_node & $a$ : Element; $b$ : Element & $c$ : Boolean & $c = b in a$ \\
- \hline
- string\_join & $a$ : String; $b$ : String & $c$ : String & $c = a . b$ \\
- string\_get & $a$ : String; $b$ : Integer & $c$ : String & $c = a[b]$ \\
- string\_substr & $a$ : String; $b$ : Integer; $c$ : Integer & $d$ : String & $d = a[b:c]$ \\
- string\_len & $a$ : String & $b$ : Integer & $b = len(a)$ \\
- \hline
- set\_add & $a$ : Element; $b$ : Element & $a$ : Element & $a.add(b)$ \\
- set\_pop & $a$ : Element & $b$ : Element & $b = a.pop()$ \\
- set\_remove & $a$ : Element; $b$ : Element & $a$ : Element & $a.remove(b)$ \\
- set\_in & $a$ : Element; $b$ : Element & $c$ : Boolean & $c = b in a$ \\
- \hline
- action\_eq & $a$ : Action; $b$ : Action & $c$ : Bool & $c = a == b$ \\
- action\_neq & $a$ : Action; $b$ : Action & $c$ : Bool & $c = a \neq b$ \\
- \hline
- type\_eq & $a$ : TypeType; $b$ : TypeType& $c$ : Bool & $c = a == b$ \\
- type\_neq & $a$ : TypeType; $b$ : TypeType& $c$ : Bool & $c = a \neq b$ \\
- \hline
- typeof & $a$ : Element & $b$ : TypeType & $b = type(a)$ \\
- \end{tabular}
- \caption{Primitive functions modifying primitive datavalues. If a Value is taken or returned, this refers to the value of the returned node.}
- \label{table:primitives_1}
- \end{table}
- \begin{table}
- \center
- \begin{tabular}{llll}
- Name & Parameters & Returns & Semantics \\
- \hline
- \hline
- cast\_i2f & $a$ : Integer & $b$ : Float & $b = float(a)$ \\
- cast\_i2s & $a$ : Integer & $b$ : String & $b = str(a)$ \\
- cast\_i2b & $a$ : Integer & $b$ : Bool & $b = bool(a)$ \\
- cast\_f2i & $a$ : Float & $b$ : Integer & $b = int(a)$ \\
- cast\_f2s & $a$ : Float & $b$ : String & $b = str(a)$ \\
- cast\_f2b & $a$ : Float & $b$ : Bool & $b = bool(a)$ \\
- cast\_s2i & $a$ : String & $b$ : Integer & $b = int(a)$ \\
- cast\_s2f & $a$ : String & $b$ : Float & $b = float(a)$ \\
- cast\_s2b & $a$ : String & $b$ : Bool & $b = bool(a)$ \\
- cast\_b2i & $a$ : Bool & $b$ : Integer & $b = int(a)$ \\
- cast\_b2f & $a$ : Bool & $b$ : Float & $b = float(a)$ \\
- cast\_b2s & $a$ : Bool & $b$ : String & $b = str(a)$ \\
- cast\_e2s & $a$ : Element & $b$ : String & $b = str(a)$ \\
- \hline
- create\_node & --- & $a$ : Element & create node and return ID \\
- create\_edge & $a$ : Element; $b$ : Element & $c$ : Edge & create edge from $a$ to $b$ and return ID \\
- create\_value & $a$ : Value & $b$ : Element & create node with value $a$ and return ID \\
- \hline
- is\_edge & $a$ : Element & $b$ : Boolean & return whether $a$ is an edge or not \\
- read\_nr\_out & $a$ : Element & $b$ : Integer & return number of outgoing links from $a$ \\
- read\_out & $a$ : Element; $b$ : Integer & $c$ : Element & return the $b$th element which has an outgoing link from $a$ \\
- read\_nr\_in & $a$ : Element & $b$ : Integer & return number of incoming links from $a$ \\
- read\_in & $a$ : Element; $b$ : Integer & $c$ : Element & return the $b$th element which has an incoming link from $a$ \\
- read\_edge\_src & $a$ : Edge & $b$ : Element & return the source of edge $a$ \\
- read\_edge\_dst & $a$ : Edge & $b$ : Element & return the destination of edge $a$ \\
- \hline
- delete\_element & $a$ : Element & $a$ : Boolean & delete element $a$ \\
- element\_eq & $a$ : Element; $b$ : Element & $c$ : Boolean & return whether or not $a$ and $b$ are exactly equal \\
- \hline
- deserialize & $a$ : String & $b$ : Element & merge serialized graph with current and return initial node \\
- \hline
- import\_node & $a$ : String & $b$ : Element & import a previously exported node \\
- export\_node & $a$ : String; $b$ : Element & $b$ : Element & export a node so it can be imported \\
- \hline
- \end{tabular}
- \caption{Lower-level primitive functions to implement. If a Value is taken or returned, this refers to the value of the returned node.}
- \label{table:primitives_2}
- \end{table}
- An MvK is free to implement additional functions as primitives, as long as each primitive instruction is guaranteed to terminate and does not violate the fairness between different users (\axiomMultiUser).
- Additionally, all additional functions need to have an equivalent implementation in Action Language for interoperability between different MvKs (\axiomInteroperability).
- To enforce this fairness, and guarantee that all users have a fairly low response time, an upper bound is placed on the time allocated for such a primitive.
- If the operation times out, the operations done by the primitive are ignored and the function is interpreted as usual.
- The mandatory primitive operations should never time out due to their simplicity.
- Modelled functions can therefore be compiled to new primitives for performance reasons (\axiomScalability): they get mapped to native code, and they no longer need to update the execution context after every instruction.
- As the execution context is not updated, primitive operations cannot be debugged easily.
- For debugging, the user needs to be able to toggle an \textit{interpreter-only} flag, which forces the Modelverse Kernel to execute in interpreter mode, bypassing all possible optimizations.
- This flag also requires the Modelverse Kernel to continuously update the execution context, as described in the previous sections.
- Execution of the primitives defined in Table~\ref{table:primitives_1} and~\ref{table:primitives_2} will still be through their hardcoded implementation though.
- As almost everything is a function call, including mathematical operations, no order of operations is imposed, apart from the one in the function calls.
- Instead, the user is required to expand this to the correct function call.
- Most users, however, will use an MvI with a parsed concrete syntax, which can generate an automatically modified abstract syntax graph from this.
- Therefore, the user might still be able to write $d = a + b * c$, as long as the MvI expands this to $d = integer\_add(a, integer\_mul(b, c))$, taking into account the typing and order of evaluation during parsing.
- This offloads the work required for the implementation of a MvK.
- Several primitive operations require some additional explanation:
- \begin{itemize}
- \item \textbf{float} operations only work on floats and not on integers, due to possible loss of accuracy.
- To get the desired results, explicit type conversions are required using the \textit{cast} operations.
- \item \textbf{string} operations work on both strings and characters, as a character is a string of length 1.
- \item \textbf{type} will return the type of the provided element.
- This is possible as types of primitives are primitive data values too.
- \item \textbf{cast} operations are used to switch between types.
- Casts from a string will try to parse the result, whereas casting to a string will pretty-print the value.
- Boolean True is equal to integers or floats different from 0 or 0.0, respectively.
- Conversion from float to integer is rounded \textit{down} if necessary.
- \item \textbf{create} operations are a one-to-one mapping with the MvS CRUD interface.
- \item \textbf{read\_nr\_(out/in)} returns the number of outgoing and incoming edges, respectively.
- \item \textbf{read\_(out/in)} returns the specified outgoing or incoming edge, respectively.
- \item \textbf{read\_dict} is a one-to-one mapping with the $R_{dict}$ MvS CRUD operation, thus reading out from the dictionary based on value in the node.
- \item \textbf{read\_dict} is a one-to-one mapping with the $R_{dict\_node}$ MvS CRUD operation, thus reading out from the dictionary based on the actual node.
- \item The \textbf{delete} operation will automatically determine the correct MvS delete operation to call.
- \end{itemize}
- \section{Interface}
- Since the MvK is not an autonomous process, it requires input from the user, and needs to forward output to the user when required.
- Therefore, an interface towards the MvI is required, which offers only two functions: add something to the input queue, and pop something from the output queue.
- This interface is sufficient to execute all operations, as the input value can be any element in the modelverse, for example a function signature or name to resolve and subsequently execute.
- While this makes the interface very minimal, it pushes all API definitions to the MvK itself, thus explicitly modelling parts that were normally hardcoded.
- Another advantage of this very versatile API, is that the MvK can be customized per-user.
- The running process of the user will just have to function as the API itself, and process all incoming messages.
- It also makes sure that all desired functionality is present, as users can manually implement it if necessary.
- We now formalize the behaviour of these two functions (\texttt{set\_input} and \texttt{get\_output}) just like the execution rules.
- It should be noted however, that these rules are not executed when they are applicable, but only when they are invoked through the API.
- The rules also reference elements that are passed to the invoking API call, and returns the marked node.
- \begin{figure}[h!]
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/api_input.eps}
- \caption{API rule for input processing}
- \label{fig:api_input}
- \end{subfigure}
- \begin{subfigure}[b]{0.49\textwidth}
- \includegraphics[width=\textwidth]{images/api_output.eps}
- \caption{API rule for output processing}
- \label{fig:api_output}
- \end{subfigure}
- \caption{API rules}
- \end{figure}
|