Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
ego_script [2026/07/22 03:05] hermann |
ego_script [2026/07/22 20:26] (current) hermann |
||
|---|---|---|---|
| Line 3: | Line 3: | ||
| ====== Introduction ====== | ====== Introduction ====== | ||
| - | EGO Script is a simple, text-based language for creating and editing Dinamica EGO models. A model written in this format is stored in a file with the ''.ego'' extension. An ''.ego'' file is just another way of describing a model — it is equivalent to the XML form Dinamica EGO uses internally. Because the two are interchangeable, you can write a model as an EGO script, open it in the graphical interface, edit it there, and save it again, moving freely between the textual and visual representations. See [[basic_data_flow|Basic Data Flow]] for the execution model shared by both representations; this tutorial covers the textual notation specifically. | + | EGO Script is a simple, text-based language for creating and editing Dinamica EGO models. A model written in this format is stored in a file with the ''.ego'' extension. An ''.ego'' file is just another way of describing a model — it is equivalent to the XML form Dinamica EGO uses internally. Because the two are interchangeable, you can write a model as an EGO script, open it in the graphical interface, edit it there, and save it again, moving freely between the textual and visual representations. See [[basic_data_flow|Basic Data Flow]] for the execution model shared by both representations; this tutorial covers the textual notation specifically. |
| > **Caution:** When you round-trip a model through the graphical interface, the interface can rewrite a model's layout and the format of its inputs and outputs according to the options currently in effect, so a script reopened and saved from the GUI may not come back byte-for-byte identical to what you wrote. This is normal, and the resulting model is equivalent. | > **Caution:** When you round-trip a model through the graphical interface, the interface can rewrite a model's layout and the format of its inputs and outputs according to the options currently in effect, so a script reopened and saved from the GUI may not come back byte-for-byte identical to what you wrote. This is normal, and the resulting model is equivalent. | ||
| - | > **Tip:** There are two practical ways to move between the visual and textual representations. First, a whole model can be saved as a ''.ego'' file from the GUI via //File// → //Save Model//, and reopened via //File// → //Open Model//. Second, any selection of functors in the GUI sketch — or the entire model — can be copied to the clipboard and pasted directly into an external text editor as an EGO Script fragment; conversely, any EGO Script fragment copied from a text editor can be pasted straight into the GUI sketch. | + | > **Tip:** There are two practical ways to move between the visual and textual representations. First, a whole model can be saved as a ''.ego'' file from the GUI via //File// → //Save Model//, and reopened via //File// → //Open Model//. Second, any selection of functors in the GUI sketch — or the entire model — can be copied to the clipboard and pasted directly into an external text editor as an EGO Script fragment; conversely, any EGO Script fragment copied from a text editor can be pasted straight into the GUI sketch. |
| This tutorial introduces the language from the ground up: the structure of a model, how functors are called and wired together, how containers and properties work, and the shorthand used to write expressions. | This tutorial introduces the language from the ground up: the structure of a model, how functors are called and wired together, how containers and properties work, and the shorthand used to write expressions. | ||
| Line 30: | Line 30: | ||
| The ''Script <nowiki>{{ }}</nowiki>'' block delimits the script text. A single file can carry several script elements, and the ''Script'' block is what groups them together. For backward compatibility with earlier versions, this declaration may be omitted. | The ''Script <nowiki>{{ }}</nowiki>'' block delimits the script text. A single file can carry several script elements, and the ''Script'' block is what groups them together. For backward compatibility with earlier versions, this declaration may be omitted. | ||
| - | > **Tip:** Omitting the ''Script'' block is discouraged — always wrap a model in an explicit ''Script'' block. | + | > **Tip:** Omitting the ''Script'' block is discouraged — always wrap a model in an explicit ''Script'' block. |
| ===== Functors, variables, and binding ===== | ===== Functors, variables, and binding ===== | ||
| - | Each statement inside the block calls a **functor** — a processing unit that consumes inputs and produces outputs. The second line of the example calls a [[Load Map]] to load the GeoTiff file ''"map.tif"'', and binds the resulting map to the variable ''x''. Two conventions are worth noting right away. First, a functor's name is written **without spaces** (''LoadMap'', not ''Load Map''). Second, a variable is always defined and bound with the '':='' operator. The third line calls [[Save Map]] to write the map held by ''x'' to an ERMapper file named ''"c:/map-copy.ers"''. The position of a value relative to the functor name determines whether it is an input or an output: | + | Each statement inside the block calls a **functor** — a processing unit that consumes inputs and produces outputs. The second line of the example calls a [[Load Map]] to load the GeoTiff file ''"map.tif"'', and binds the resulting map to the variable ''x''. Two conventions are worth noting right away. First, a functor's name is written **without spaces** (''LoadMap'', not ''Load Map''). Second, a variable is always defined and bound with the '':='' operator. The third line calls [[Save Map]] to write the map held by ''x'' to an ERMapper file named ''"c:/map-copy.ers"''. The position of a value relative to the functor name determines whether it is an input or an output: |
| * Values and variables written **after** the functor name are bound to its **input** ports. | * Values and variables written **after** the functor name are bound to its **input** ports. | ||
| Line 43: | Line 43: | ||
| A variable name must begin with an underscore or a letter, and may be followed by any combination of underscores, letters, and digits (''[_a-zA-Z][_a-zA-Z0-9]*''). Variable names are case-sensitive. | A variable name must begin with an underscore or a letter, and may be followed by any combination of underscores, letters, and digits (''[_a-zA-Z][_a-zA-Z0-9]*''). Variable names are case-sensitive. | ||
| - | Variables are the primary mechanism by which functors pass data to one another, and data dependencies are the main source of execution ordering. A variable must be bound by the functor that produces it before any functor that reads it can run — this is what creates a **dependency** between the two. Functors that do not share a variable, directly or through a chain of intermediate variables, have no dependency on each other and may execute in any order or at the same time. [[#container_functors|Container functors]], described in the Container functors section of the reference, impose a second ordering constraint on top of this. This is a property of the underlying functor graph, not of EGO Script specifically — see [[basic_data_flow|Basic Data Flow]]. | + | Variables are the primary mechanism by which functors pass data to one another, and data dependencies are the main source of execution ordering. A variable must be bound by the functor that produces it before any functor that reads it can run — this is what creates a **dependency** between the two. Functors that do not share a variable, directly or through a chain of intermediate variables, have no dependency on each other and may execute in any order or at the same time. [[#container_functors|Container functors]], described in the Container functors section of the reference, impose a second ordering constraint on top of this. This is a property of the underlying functor graph, not of EGO Script specifically — see [[basic_data_flow|Basic Data Flow]]. |
| - | > **Note:** The position of a functor definition in the script file does not determine when it runs. Unless two functors are connected by a data dependency — directly or through a chain of variables — never assume anything about their relative execution order. The only exception is [[#container_functors|containers]], covered later in this section. | + | > **Note:** The position of a functor definition in the script file does not determine when it runs. Unless two functors are connected by a data dependency — directly or through a chain of variables — never assume anything about their relative execution order. The only exception is [[#container_functors|containers]], covered later in this section. |
| ===== A first look at comments ===== | ===== A first look at comments ===== | ||
| - | The sequence ''<nowiki>//</nowiki>'' begins a comment, which continues to the end of the line. The two comment lines in the example document what each step does. Comments are more than annotations — they attach to the functor that follows them and are preserved with the model; the [[#comments|Comments]] section of the reference describes this in detail. | + | The sequence ''<nowiki>//</nowiki>'' begins a comment, which continues to the end of the line. The two comment lines in the example document what each step does. Comments are more than annotations — they attach to the functor that follows them and are preserved with the model; the [[#comments|Comments]] section of the reference describes this in detail. |
| ====== Reference ====== | ====== Reference ====== | ||
| Line 59: | Line 59: | ||
| ===== Functor calls ===== | ===== Functor calls ===== | ||
| - | A functor call binds **outputs** — the variables written before '':='' — and consumes **inputs** — the values, variables, and constants written after the functor name. Every functor defines a fixed set of typed **[[ports]]** for its inputs and outputs: | + | A functor call binds **outputs** — the variables written before '':='' — and consumes **inputs** — the values, variables, and constants written after the functor name. Every functor defines a fixed set of typed **[[ports]]** for its inputs and outputs: |
| <code> | <code> | ||
| Line 65: | Line 65: | ||
| </code> | </code> | ||
| - | Here [[Calc Areas]] reads two inputs (''landscape'' and the constant ''.no'') and produces one output, bound to the variable ''areaTable''. Constants are written with a leading dot (''.no'', ''.default'', ''.int32'', ''.none'') — see [[#constants|Constants]] below. | + | Here [[Calc Areas]] reads two inputs (''landscape'' and the constant ''.no'') and produces one output, bound to the variable ''areaTable''. Constants are written with a leading dot (''.no'', ''.default'', ''.int32'', ''.none'') — see [[#constants|Constants]] below. |
| - | Inputs can be supplied in three interchangeable styles — [[#positional_syntax|positional]], [[#nominal_syntax|nominal]], and [[#inline_syntax|inline]]. The styles may be mixed freely between calls, and even within a single call. | + | Inputs can be supplied in three interchangeable styles — [[#positional_syntax|positional]], [[#nominal_syntax|nominal]], and [[#inline_syntax|inline]]. The styles may be mixed freely between calls, and even within a single call. |
| ==== Constants ==== | ==== Constants ==== | ||
| - | Constants supply a fixed value directly to an input port, without connecting a functor. Despite often looking similar in syntax, every constant is interpreted and validated according to the type of the port it is supplied to — a value that is valid for one port type may be rejected by another. They come in four forms. | + | Constants supply a fixed value directly to an input port, without connecting a functor. Despite often looking similar in syntax, every constant is interpreted and validated according to the type of the port it is supplied to — a value that is valid for one port type may be rejected by another. They come in four forms. |
| - | **Numeric literals** — integer or real values written directly: ''0'', ''250'', ''3.14''. | + | **Numeric literals** — integer or real values written directly: ''0'', ''250'', ''3.14''. |
| - | **Text constants** — text enclosed in double quotes. Despite looking identical in syntax, text constants are interpreted and validated according to the port's type. The types that accept the basic ''%%"%%text%%"%%'' form include ''String'', ''MapFilename'', ''TableFilename'', ''LookupTableFilename'', ''WeightsFilename'', ''GenericFilename'', ''Folder'', and ''Name'' — but each imposes its own validation: | + | **Text constants** — text enclosed in double quotes. Despite looking identical in syntax, text constants are interpreted and validated according to the port's type. The types that accept the basic ''%%"%%text%%"%%'' form include ''String'', ''MapFilename'', ''TableFilename'', ''LookupTableFilename'', ''WeightsFilename'', ''GenericFilename'', ''Folder'', and ''Name'' — but each imposes its own validation: |
| * **Filename ports** (''MapFilename'', ''TableFilename'', etc.) typically verify that the file extension is appropriate for that type. For example, ''"test.tif"'' is accepted by a ''MapFilename'' port but rejected by a ''TableFilename'' port, since ''.tif'' is not a valid table file extension. | * **Filename ports** (''MapFilename'', ''TableFilename'', etc.) typically verify that the file extension is appropriate for that type. For example, ''"test.tif"'' is accepted by a ''MapFilename'' port but rejected by a ''TableFilename'' port, since ''.tif'' is not a valid table file extension. | ||
| - | * **''Name'' ports** only accept identifiers that follow the same naming rules as variables — starting with a letter or underscore, followed only by letters, digits, and underscores. ''"distance_to_roads"'' is valid; ''"Distance To Roads"'' is not. | + | * **''Name'' ports** only accept identifiers that follow the same naming rules as variables — starting with a letter or underscore, followed only by letters, digits, and underscores. ''"distance_to_roads"'' is valid; ''"Distance To Roads"'' is not. |
| <code> | <code> | ||
| Line 87: | Line 87: | ||
| </code> | </code> | ||
| - | For ''String'' ports that contain line breaks or double-quote characters, use the extended form ''$"DELIMITER(content)DELIMITER"''. The content is everything between the opening ''('' and the closing '')'', and the DELIMITER — any sequence of characters placed between the ''%%"%%'' and the ''('', including empty — makes the closing '')"'' unambiguous. The delimiter must be identical on both sides. This extended form is **only accepted by ''String'' ports**: | + | For ''String'' ports that contain line breaks or double-quote characters, use the extended form ''$"DELIMITER(content)DELIMITER"''. The content is everything between the opening ''('' and the closing '')'', and the DELIMITER — any sequence of characters placed between the ''%%"%%'' and the ''('', including empty — makes the closing '')"'' unambiguous. The delimiter must be identical on both sides. This extended form is **only accepted by ''String'' ports**: |
| <code> | <code> | ||
| - | // basic form — line break inside the content | + | // basic form — line break inside the content |
| $"(Hello | $"(Hello | ||
| world)" | world)" | ||
| - | // X as delimiter — content contains )" which would otherwise terminate the basic form early | + | // X as delimiter — content contains )" which would otherwise terminate the basic form early |
| $"X(some )" text)X" | $"X(some )" text)X" | ||
| </code> | </code> | ||
| - | **Dot constants** — a leading dot followed by a keyword: ''.yes'', ''.no'', ''.int32'', ''.default''. Each data type defines its own set of dot constants and the constants accepted by a port depend entirely on its type. Two dot constants are predefined and available for **any** input port regardless of type: | + | **Dot constants** — a leading dot followed by a keyword: ''.yes'', ''.no'', ''.int32'', ''.default''. Each data type defines its own set of dot constants and the constants accepted by a port depend entirely on its type. Two dot constants are predefined and available for **any** input port regardless of type: |
| - | * ''.UNBOUND'' — the port is not connected, or its connection is deliberately being ignored. This appears in particular when a script fragment is copied from the GUI to a text editor and some of the original connections are not part of the copied selection. | + | * ''.UNBOUND'' — the port is not connected, or its connection is deliberately being ignored. This appears in particular when a script fragment is copied from the GUI to a text editor and some of the original connections are not part of the copied selection. |
| - | * ''.none'' — the port is intentionally left without a value. Valid **only for optional nullable input ports**. | + | * ''.none'' — the port is intentionally left without a value. Valid **only for optional nullable input ports**. |
| - | **Structured constants** — complex values enclosed in square brackets ''[ … ]'', specific to individual functors and can be multi-line: ''[ 2->1 0.05 ]'', ''[ "Key" "Value" ]''. | + | **Structured constants** — complex values enclosed in square brackets ''[ … ]'', specific to individual functors and can be multi-line: ''[ 2->1 0.05 ]'', ''[ "Key" "Value" ]''. |
| + | |||
| + | Lookup table literals support an additional shorthand: ''startKey startValue .. endKey endValue'' auto-generates every key from ''startKey'' to ''endKey'' (incrementing by one), with values linearly interpolated between ''startValue'' and ''endValue''. Range entries and plain ''key value'' entries can be mixed freely, and commas between entries are optional: | ||
| + | |||
| + | <code> | ||
| + | [ "Key" "Value", 1 1 .. 4 1, 5 -8, 6 1 .. 9 1 ] | ||
| + | </code> | ||
| + | |||
| + | Since the start and end values in each range above are equal, interpolating between them keeps the value constant — keys 1–4 and 6–9 all map to ''1'', and key 5 maps to ''-8''. When a script is saved from the GUI, ranges like this are automatically collapsed back into ''..'' notation where possible. See [[lookup_table_type|Lookup Table Type]] for the full syntax, including optional key/value column names, and [[table_type|Table Type]] for the syntax rules that apply to tables in general. | ||
| + | |||
| + | A general table follows the same bracket syntax but allows any number of columns, mixing Real and String data, with types inferred from each column's own values unless overridden with a ''#type'' suffix: | ||
| + | |||
| + | <code> | ||
| + | [ | ||
| + | "PatchId*#string", "Area", "Category", | ||
| + | 007, 12.4, "forest", | ||
| + | 013, 8.7, "pasture" | ||
| + | ] | ||
| + | </code> | ||
| + | |||
| + | Without the ''#string'' override, ''PatchId'''s unquoted values would be inferred as Real numbers — ''007'' would collapse to the number ''7'', losing its leading zero. The explicit type forces String interpretation instead, preserving each value exactly as written. ''Area'' and ''Category'' are left to plain inference: Real from ''12.4''/''8.7'', String from the quoted ''"forest"''/''"pasture"''. | ||
| A call can mix all forms with variable references in a single argument list: | A call can mix all forms with variable references in a single argument list: | ||
| Line 119: | Line 139: | ||
| Here ''[ 2->1 0.05 ]'' is a structured constant representing a transition matrix with a 5% rate from class 2 to class 1. | Here ''[ 2->1 0.05 ]'' is a structured constant representing a transition matrix with a 5% rate from class 2 to class 1. | ||
| - | > **Note:** ''Code'' — the type used for expression inputs such as the Python code in [[Calculate Python Expression]] — is not a text constant. Code values are stored as base64 in the underlying script, making them impractical to write directly. When a port expects a ''Code'' value, supply a ''String'' carrier functor containing the expression text; the automatic ''String → Code'' conversion handles the rest. | + | > **Note:** ''Code'' — the type used for expression inputs such as the Python code in [[Calculate Python Expression]] — is not a text constant. Code values are stored as base64 in the underlying script, making them impractical to write directly. When a port expects a ''Code'' value, supply a ''String'' carrier functor containing the expression text; the automatic ''String → Code'' conversion handles the rest. |
| ==== Positional syntax ==== | ==== Positional syntax ==== | ||
| Line 130: | Line 150: | ||
| </code> | </code> | ||
| - | **Omitting optional inputs.** A functor's optional input ports always come after its required ones. In positional syntax you may stop supplying arguments early, but you cannot skip an optional input and then supply a later one — once you omit a port, every port after it is omitted too. To leave a gap, use [[#nominal_syntax|nominal syntax]] (described below) for that call. | + | **Omitting optional inputs.** A functor's optional input ports always come after its required ones. In positional syntax you may stop supplying arguments early, but you cannot skip an optional input and then supply a later one — once you omit a port, every port after it is omitted too. To leave a gap, use [[#nominal_syntax|nominal syntax]] (described below) for that call. |
| [[Load Categorical Map]], for example, takes a required filename followed by several optional inputs; this call supplies the first few of them positionally and omits the rest: | [[Load Categorical Map]], for example, takes a required filename followed by several optional inputs; this call supplies the first few of them positionally and omits the rest: | ||
| Line 166: | Line 186: | ||
| </code> | </code> | ||
| - | > **Note:** Port names in the script use camelCase with a lowercase first letter (e.g. ''cellType'', ''resultIsSparse''). In the GUI the same ports are displayed with spaces between words and all words capitalised (e.g. "Cell Type", "Result Is Sparse"). The same conversion rules that govern functor aliases and variable names apply here — see [[#alias_and_variable_name_conversion|Alias and variable name conversion]]. | + | > **Note:** Port names in the script use camelCase with a lowercase first letter (e.g. ''cellType'', ''resultIsSparse''). In the GUI the same ports are displayed with spaces between words and all words capitalised (e.g. "Cell Type", "Result Is Sparse"). The same conversion rules that govern functor aliases and variable names apply here — see [[#alias_and_variable_name_conversion|Alias and variable name conversion]]. |
| > **Note:** The single braces ''{ }'' used here for nominal syntax are distinct from the double braces ''<nowiki>{{ }}</nowiki>'' used to delimit a [[#container_functors|container functor]]'s block of contained functors. | > **Note:** The single braces ''{ }'' used here for nominal syntax are distinct from the double braces ''<nowiki>{{ }}</nowiki>'' used to delimit a [[#container_functors|container functor]]'s block of contained functors. | ||
| - | Outputs follow the **opposite** rule. An output is bound by name as ''variable = portName'' — the variable you choose on the left, the functor's output port name on the right — also inside single braces ''{ }'', placed before '':='': | + | Outputs follow the **opposite** rule. An output is bound by name as ''variable = portName'' — the variable you choose on the left, the functor's output port name on the right — also inside single braces ''{ }'', placed before '':='': |
| <code> | <code> | ||
| Line 202: | Line 222: | ||
| ==== Inline syntax ==== | ==== Inline syntax ==== | ||
| - | Inline syntax nests one call directly inside another instead of binding an intermediate variable. The nested call is wrapped in parentheses, and its single output is passed straight into the enclosing input port — the same idea as nesting function calls in conventional programming languages. | + | Inline syntax nests one call directly inside another instead of binding an intermediate variable. The nested call is wrapped in parentheses, and its single output is passed straight into the enclosing input port — the same idea as nesting function calls in conventional programming languages. |
| These two fragments are equivalent. The first names an intermediate variable: | These two fragments are equivalent. The first names an intermediate variable: | ||
| Line 233: | Line 253: | ||
| Inlining is convenient only when you need a single output from the nested call. If you need more than one of its outputs, bind it to variables instead. | Inlining is convenient only when you need a single output from the nested call. If you need more than one of its outputs, bind it to variables instead. | ||
| - | Setting the ''@inline'' property on a functor prevents it from being inlined when the script is written. The same effect also applies automatically whenever any other property is defined for the functor — such as a comment, a custom alias, or any other annotation — because an inlined functor carries no separate node to attach that information to, so it would be lost. See [[#properties|Properties]] for the full list of functor properties. | + | Setting the ''@inline'' property on a functor prevents it from being inlined when the script is written. The same effect also applies automatically whenever any other property is defined for the functor — such as a comment, a custom alias, or any other annotation — because an inlined functor carries no separate node to attach that information to, so it would be lost. See [[#properties|Properties]] for the full list of functor properties. |
| ---- | ---- | ||
| Line 241: | Line 261: | ||
| A **container functor** is a functor that holds other functors inside it. The contained functors execute within the container's scope. | A **container functor** is a functor that holds other functors inside it. The contained functors execute within the container's scope. | ||
| - | Containers are the second source of execution ordering in Dinamica EGO, alongside data dependencies. All functors inside a container execute as part of that container's execution. This means that if container B depends on container A — for example, B consumes a value produced by something inside A — then B will only start after A has fully completed. As a consequence, every functor inside B is guaranteed to run after every functor inside A, and the contents of the two containers will never execute simultaneously. See [[basic_data_flow|Basic Data Flow]] for how this fits into the broader, notation-independent execution model — including how containers also control whether and how many times their contents run. | + | Containers are the second source of execution ordering in Dinamica EGO, alongside data dependencies. All functors inside a container execute as part of that container's execution. This means that if container B depends on container A — for example, B consumes a value produced by something inside A — then B will only start after A has fully completed. As a consequence, every functor inside B is guaranteed to run after every functor inside A, and the contents of the two containers will never execute simultaneously. See [[basic_data_flow|Basic Data Flow]] for how this fits into the broader, notation-independent execution model — including how containers also control whether and how many times their contents run. |
| Containers fall into a few families: | Containers fall into a few families: | ||
| Line 249: | Line 269: | ||
| * **Loops.** Several containers repeat their contents. [[Repeat]] runs them a fixed number of times; [[For]] iterates over a numeric range; [[For Each]] iterates over the rows of a table; [[For Each Category]] iterates over the categories of a map; [[For Each Region]] iterates over the regions of a map; and [[While]] and [[Do While]] repeat for as long as a condition holds. | * **Loops.** Several containers repeat their contents. [[Repeat]] runs them a fixed number of times; [[For]] iterates over a numeric range; [[For Each]] iterates over the rows of a table; [[For Each Category]] iterates over the categories of a map; [[For Each Region]] iterates over the regions of a map; and [[While]] and [[Do While]] repeat for as long as a condition holds. | ||
| * **Region management.** [[Region Manager]] establishes a region context and runs its contents once for that context, exposing the region to the functors inside it. | * **Region management.** [[Region Manager]] establishes a region context and runs its contents once for that context, exposing the region to the functors inside it. | ||
| - | * **Error handling.** [[Skip On Error]] and [[Skip All On Error]] execute their contained functors while capturing and ignoring any error that occurs. Both accept a ''trapAndIgnoreErrors'' boolean input — when set to false, they behave as a plain ''Group'' and errors are not suppressed. Both also produce an ''executionCompletedSuccessfully'' output that can be tested by subsequent functors. The two differ in what happens to results already produced when an error is raised: [[Skip On Error]] preserves the outputs of any functors that completed successfully before the error; [[Skip All On Error]] discards all of them. They are typically paired with a junction outside the container to react to the outcome — see [[#error-handling_pattern|Error-handling pattern]] below. | + | * **Error handling.** [[Skip On Error]] and [[Skip All On Error]] execute their contained functors while capturing and ignoring any error that occurs. Both accept a ''trapAndIgnoreErrors'' boolean input — when set to false, they behave as a plain ''Group'' and errors are not suppressed. Both also produce an ''executionCompletedSuccessfully'' output that can be tested by subsequent functors. The two differ in what happens to results already produced when an error is raised: [[Skip On Error]] preserves the outputs of any functors that completed successfully before the error; [[Skip All On Error]] discards all of them. They are typically paired with a junction outside the container to react to the outcome — see [[#error-handling_pattern|Error-handling pattern]] below. |
| The [[#calculator_functor_shorthand|calculator functors]] described later in this section are also containers: each holds the expression it evaluates, and in its verbose form holds a block of operand definitions. | The [[#calculator_functor_shorthand|calculator functors]] described later in this section are also containers: each holds the expression it evaluates, and in its verbose form holds a block of operand definitions. | ||
| - | The contained functors are written inside a double-brace block ''<nowiki>{{ … }}</nowiki>'' that follows the container call: | + | The contained functors are written inside a double-brace block ''<nowiki>{{ … }}</nowiki>'' that follows the container call: |
| <code> | <code> | ||
| Line 267: | Line 287: | ||
| A container exchanges data with the functors inside it through **internal ports**: internal output ports carry values from the container into the block, and internal input ports carry values from the block back to the container. | A container exchanges data with the functors inside it through **internal ports**: internal output ports carry values from the container into the block, and internal input ports carry values from the block back to the container. | ||
| - | Not all containers are about control flow. Some functors are implemented as containers specifically to exploit the execution ordering guarantee that containers provide. In a dataflow language, two functors with no data dependency between them may execute in any order. Making a functor a container means its contents execute as part of that functor's own execution — the container starts, its contents run, then it finishes. This makes it possible to bracket a computation with side effects that are guaranteed to happen before and after it. | + | Not all containers are about control flow. Some functors are implemented as containers specifically to exploit the execution ordering guarantee that containers provide. In a dataflow language, two functors with no data dependency between them may execute in any order. Making a functor a container means its contents execute as part of that functor's own execution — the container starts, its contents run, then it finishes. This makes it possible to bracket a computation with side effects that are guaranteed to happen before and after it. |
| [[Print]] is the clearest example. It prints an ''initialMessage'' before its contents run and a ''finalMessage'' after they complete. Because the calculation lives inside the container, the ordering is guaranteed: | [[Print]] is the clearest example. It prints an ''initialMessage'' before its contents run and a ''finalMessage'' after they complete. Because the calculation lives inside the container, the ordering is guaranteed: | ||
| Line 278: | Line 298: | ||
| </code> | </code> | ||
| - | Either message can be omitted with ''.none''. If the final message needs to include a value produced by the computation, a nested [[Print]] can be placed at the end of the body. The inner ''Print'' only runs after ''result'' is computed, since it depends on it — and [[Create String]] with [[Number Value]] is used to embed the computed value in the message string: | + | Either message can be omitted with ''.none''. If the final message needs to include a value produced by the computation, a nested [[Print]] can be placed at the end of the body. The inner ''Print'' only runs after ''result'' is computed, since it depends on it — and [[Create String]] with [[Number Value]] is used to embed the computed value in the message string: |
| <code> | <code> | ||
| Line 292: | Line 312: | ||
| </code> | </code> | ||
| - | This would be impossible to guarantee with plain functors in a dataflow language — without the container, nothing would prevent the calculation from running before or after either print, since there is no data dependency between them. | + | This would be impossible to guarantee with plain functors in a dataflow language — without the container, nothing would prevent the calculation from running before or after either print, since there is no data dependency between them. |
| ==== Sequence ports ==== | ==== Sequence ports ==== | ||
| Line 298: | Line 318: | ||
| Most container functors expose two optional sequencing ports alongside their regular inputs and outputs: | Most container functors expose two optional sequencing ports alongside their regular inputs and outputs: | ||
| - | * ''sequenceInput'' — a value connected here has no effect on the computation, but forces the functor that produced that value to complete before this container starts. | + | * ''sequenceInput'' — a value connected here has no effect on the computation, but forces the functor that produced that value to complete before this container starts. |
| - | * ''sequenceOutput'' — connecting this output to another container's ''sequenceInput'' forces this container to complete before the other one starts. | + | * ''sequenceOutput'' — connecting this output to another container's ''sequenceInput'' forces this container to complete before the other one starts. |
| These ports carry no data of their own; their only purpose is to impose execution ordering. Because any data type can be connected to a ''sequenceInput'' port, the output of one container can feed directly into the next container's sequencing slot: | These ports carry no data of their own; their only purpose is to impose execution ordering. Because any data type can be connected to a ''sequenceInput'' port, the output of one container can feed directly into the next container's sequencing slot: | ||
| Line 313: | Line 333: | ||
| </code> | </code> | ||
| - | > **Note:** Sequence ports are only needed when two containers have no natural data dependency between them — if one container already produces a value that the other consumes, the ordering is already established and no sequencing port is required. Before sequence ports were available, models used a **forced-dependency pattern** — creating a dummy data value inside one container and consuming it inside another to manufacture an artificial data dependency. This idiom still appears in older scripts but is now a last resort, used only for functors that do not expose ''sequenceInput'' and ''sequenceOutput'' ports (such as [[Skip On Error]] and [[Skip All On Error]], which instead expose an ''executionCompletedSuccessfully'' boolean output that can serve the same purpose). | + | > **Note:** Sequence ports are only needed when two containers have no natural data dependency between them — if one container already produces a value that the other consumes, the ordering is already established and no sequencing port is required. Before sequence ports were available, models used a **forced-dependency pattern** — creating a dummy data value inside one container and consuming it inside another to manufacture an artificial data dependency. This idiom still appears in older scripts but is now a last resort, used only for functors that do not expose ''sequenceInput'' and ''sequenceOutput'' ports (such as [[Skip On Error]] and [[Skip All On Error]], which instead expose an ''executionCompletedSuccessfully'' boolean output that can serve the same purpose). |
| ==== Internal output ports ==== | ==== Internal output ports ==== | ||
| Line 319: | Line 339: | ||
| An **internal output port** carries a value from the container into the functors nested inside it. The container produces the value; the contained functors consume it. A loop's current iteration value and a manager object made available to a block are typical internal outputs. | An **internal output port** carries a value from the container into the functors nested inside it. The container produces the value; the contained functors consume it. A loop's current iteration value and a manager object made available to a block are typical internal outputs. | ||
| - | An internal output port is read at the top of the block with the same ''variable = portName'' form used for ordinary outputs — your variable on the left, the port name on the right. If present, these bindings **must be the first declarations inside the container block**, before any functor calls. A loop, for example, exposes its current element through an internal output port named ''step'': | + | An internal output port is read at the top of the block with the same ''variable = portName'' form used for ordinary outputs — your variable on the left, the port name on the right. If present, these bindings **must be the first declarations inside the container block**, before any functor calls. A loop, for example, exposes its current element through an internal output port named ''step'': |
| <code> | <code> | ||
| Line 346: | Line 366: | ||
| An **internal input port** carries a value the other way: from the functors inside the block back to the container. The contained functors produce the value; the container consumes it. The condition that decides whether a loop repeats is the clearest example. | An **internal input port** carries a value the other way: from the functors inside the block back to the container. The contained functors produce the value; the container consumes it. The condition that decides whether a loop repeats is the clearest example. | ||
| - | A ''While'' or ''DoWhile'' loop has an internal input port for its repeat condition. It is fed by placing a [[Set While Condition]] functor inside the loop body — the ''condition'' output port of ''SetWhileCondition'' is **auto-bound** to the loop's ''condition'' internal input, so no explicit connection is needed: | + | A ''While'' or ''DoWhile'' loop has an internal input port for its repeat condition. It is fed by placing a [[Set While Condition]] functor inside the loop body — the ''condition'' output port of ''SetWhileCondition'' is **auto-bound** to the loop's ''condition'' internal input, so no explicit connection is needed: |
| <code> | <code> | ||
| Line 357: | Line 377: | ||
| The ''SetWhileCondition'' auto-binding works in both directions: the output is automatically wired to whichever enclosing ''While'' or ''DoWhile'' contains it, and the loop evaluates it before each iteration to decide whether to run the body again. | The ''SetWhileCondition'' auto-binding works in both directions: the output is automatically wired to whichever enclosing ''While'' or ''DoWhile'' contains it, and the loop evaluates it before each iteration to decide whether to run the body again. | ||
| - | A container may also expose a sequencing-only internal port (carrying no data) used to force one container to finish before another begins — see [[#sequence_ports|Sequence ports]]. | + | A container may also expose a sequencing-only internal port (carrying no data) used to force one container to finish before another begins — see [[#sequence_ports|Sequence ports]]. |
| ==== Carrying and selecting values across iterations ==== | ==== Carrying and selecting values across iterations ==== | ||
| Line 363: | Line 383: | ||
| Two families of functors are commonly used together with loops and manage how a value flows from one place, or one iteration, to the next. | Two families of functors are commonly used together with loops and manage how a value flows from one place, or one iteration, to the next. | ||
| - | A **mux** carries a value across the iterations of a loop. On the first iteration it emits its //initial// input. On every later iteration it emits its //feedback// input — the value produced by the previous pass — so the loop can build on its own prior result. Muxes are typed: [[Mux Map]], [[Mux Categorical Map]], [[Mux Table]], [[Mux Lookup Table]], [[Mux Value]], [[Mux Tuple]], and so on. A mux at the top of a loop body, paired with a variable assigned at the bottom, forms the loop's running state. This loop counts from 0 and stops once the counter reaches 10: | + | A **mux** carries a value across the iterations of a loop. On the first iteration it emits its //initial// input. On every later iteration it emits its //feedback// input — the value produced by the previous pass — so the loop can build on its own prior result. Muxes are typed: [[Mux Map]], [[Mux Categorical Map]], [[Mux Table]], [[Mux Lookup Table]], [[Mux Value]], [[Mux Tuple]], and so on. A mux at the top of a loop body, paired with a variable assigned at the bottom, forms the loop's running state. This loop counts from 0 and stops once the counter reaches 10: |
| <code> | <code> | ||
| Line 380: | Line 400: | ||
| </code> | </code> | ||
| - | > **Note:** A mux's feedback input is the one exception to the dependency rule in [[#functors_variables_and_binding|Functors, variables, and binding]] — the mux does not wait for the functor that produces its feedback value to run before it runs; it emits the value that functor produced on the previous iteration instead. This is also why, in the example above, ''nextCount'' is passed into ''MuxValue'' on a line above the line that assigns ''nextCount'': the mux and the calculator each depend on the other's output, so no matter which one is written first in the script, the other's variable is necessarily used before it is assigned. | + | > **Note:** A mux's feedback input is the one exception to the dependency rule in [[#functors_variables_and_binding|Functors, variables, and binding]] — the mux does not wait for the functor that produces its feedback value to run before it runs; it emits the value that functor produced on the previous iteration instead. This is also why, in the example above, ''nextCount'' is passed into ''MuxValue'' on a line above the line that assigns ''nextCount'': the mux and the calculator each depend on the other's output, so no matter which one is written first in the script, the other's variable is necessarily used before it is assigned. |
| - | A **junction** selects between two dataflows. It reads its first input; if that input carries data, the junction passes it through. If the first input is empty, it passes the second input through instead. If both are empty, the junction reports an error. Junctions are typed, with one per data kind — [[Map Junction]], [[Categorical Map Junction]], [[Table Junction]], [[Lookup Table Junction]], [[Value Junction]], [[String Junction]], and so on: | + | A **junction** selects between two dataflows. It reads its first input; if that input carries data, the junction passes it through. If the first input is empty, it passes the second input through instead. If both are empty, the junction reports an error. Junctions are typed, with one per data kind — [[Map Junction]], [[Categorical Map Junction]], [[Table Junction]], [[Lookup Table Junction]], [[Value Junction]], [[String Junction]], and so on: |
| <code> | <code> | ||
| Line 393: | Line 413: | ||
| The error-handling containers are most useful when combined with a [[#carrying_and_selecting_values_across_iterations|junction]] outside the block. The sentinel and the risky functor inside the container have no data dependency on each other, so they execute independently. The pattern works as follows: | The error-handling containers are most useful when combined with a [[#carrying_and_selecting_values_across_iterations|junction]] outside the block. The sentinel and the risky functor inside the container have no data dependency on each other, so they execute independently. The pattern works as follows: | ||
| - | - If the risky functor raises an error, [[Skip All On Error]] captures it and discards **all** results produced by functors inside the container — including the sentinel, even though it completed successfully on its own. | + | - If the risky functor raises an error, [[Skip All On Error]] captures it and discards **all** results produced by functors inside the container — including the sentinel, even though it completed successfully on its own. |
| - Outside the container, a **junction** tests whether anything propagated out. If the sentinel was discarded (error case), the container produced nothing and the junction falls back to its default value. If no error occurred, the sentinel propagates normally and the junction forwards it. | - Outside the container, a **junction** tests whether anything propagated out. If the sentinel was discarded (error case), the container produced nothing and the junction falls back to its default value. If no error occurred, the sentinel propagates normally and the junction forwards it. | ||
| Line 400: | Line 420: | ||
| <code> | <code> | ||
| _ := SkipAllOnError .yes {{ | _ := SkipAllOnError .yes {{ | ||
| - | // The sentinel has no dependency on LoadMap — both execute independently. | + | // The sentinel has no dependency on LoadMap — both execute independently. |
| // If LoadMap raises an error, SkipAllOnError discards all results inside, | // If LoadMap raises an error, SkipAllOnError discards all results inside, | ||
| // including this sentinel. | // including this sentinel. | ||
| Line 411: | Line 431: | ||
| </code> | </code> | ||
| - | [[Skip On Error]] behaves differently: instead of discarding all results, it preserves the outputs of any functors that had already completed when the error was raised. In the pattern above this means the sentinel would always propagate — making [[Skip On Error]] suitable for cases where partial results from a failed block are still useful, not for a simple success/failure test. | + | [[Skip On Error]] behaves differently: instead of discarding all results, it preserves the outputs of any functors that had already completed when the error was raised. In the pattern above this means the sentinel would always propagate — making [[Skip On Error]] suitable for cases where partial results from a failed block are still useful, not for a simple success/failure test. |
| ---- | ---- | ||
| Line 426: | Line 446: | ||
| | [[calculate_functors|Calculate Value]] | a single value | | | [[calculate_functors|Calculate Value]] | a single value | | ||
| - | > **See also:** This section covers only the EGO Script syntax used to call these functors. For the full semantics of each one — inputs, parameters, the expression language, and null handling — see [[calculate_functors|Calculate Functors — Complete Operator Documentation]]. | + | > **See also:** This section covers only the EGO Script syntax used to call these functors. For the full semantics of each one — inputs, parameters, the expression language, and null handling — see [[calculate_functors|Calculate Functors — Complete Operator Documentation]]. |
| Each of these functors can be written in two syntactic forms: the **verbose form**, which uses the functor's full name and explicit **hook** functors to connect data, or the **abbreviated syntax** (also called **shorthand**), which replaces the functor name with a compact symbol and references operands directly by variable name. The abbreviated symbol for each functor is: | Each of these functors can be written in two syntactic forms: the **verbose form**, which uses the functor's full name and explicit **hook** functors to connect data, or the **abbreviated syntax** (also called **shorthand**), which replaces the functor name with a compact symbol and references operands directly by variable name. The abbreviated symbol for each functor is: | ||
| Line 437: | Line 457: | ||
| | ''$'' | ''CalculateValue'' | | | ''$'' | ''CalculateValue'' | | ||
| - | > **Note:** The shorthand is the symbol alone — the ''[ … ]'' that usually appears next is simply the functor's first argument (the expression to evaluate), not part of the symbol itself. Whatever follows the bracket (''.int8 .default .no .none'', or a base table and column names) are the functor's remaining inputs, supplied in positional order. | + | > **Note:** The shorthand is the symbol alone — the ''[ … ]'' that usually appears next is simply the functor's first argument (the expression to evaluate), not part of the symbol itself. Whatever follows the bracket (''.int8 .default .no .none'', or a base table and column names) are the functor's remaining inputs, supplied in positional order. |
| ==== Verbose form ==== | ==== Verbose form ==== | ||
| - | In the verbose form, data is connected through **hook** functors — [[Number Map]], [[Number Table]], and [[Number Value]] — which supply maps, tables, and scalar values to the expression respectively. Each hook is assigned a number, and the expression references it by a type-specific numbered prefix: ''i1'', ''i2''… for maps; ''t1'', ''t2''… for tables and lookup tables; ''v1'', ''v2''… for values. Each type is numbered independently: | + | In the verbose form, data is connected through **hook** functors — [[Number Map]], [[Number Table]], and [[Number Value]] — which supply maps, tables, and scalar values to the expression respectively. Each hook is assigned a number, and the expression references it by a type-specific numbered prefix: ''i1'', ''i2''… for maps; ''t1'', ''t2''… for tables and lookup tables; ''v1'', ''v2''… for values. Each type is numbered independently: |
| <code> | <code> | ||
| Line 482: | Line 502: | ||
| ^ Type ^ Sigil ^ Named form ^ Verbose equivalent ^ | ^ Type ^ Sigil ^ Named form ^ Verbose equivalent ^ | ||
| - | | map | ''#'' | ''#regions'' | ''i1'', ''i2'', … | | + | | map | ''#'' | ''#regions'' | ''i1'', ''i2'', … | |
| - | | table or lookup table | ''%'' | ''%regionCounts'' | ''t1'', ''t2'', … | | + | | table or lookup table | ''%'' | ''%regionCounts'' | ''t1'', ''t2'', … | |
| - | | value | ''$'' | ''$radius'' | ''v1'', ''v2'', … | | + | | value | ''$'' | ''$radius'' | ''v1'', ''v2'', … | |
| The same two calculations from the verbose section above, written in abbreviated syntax: | The same two calculations from the verbose section above, written in abbreviated syntax: | ||
| Line 506: | Line 526: | ||
| Converting abbreviated syntax to verbose is always lossless. Converting verbose to abbreviated is only possible when all hooks are free of comments, properties, and identifiers that do not appear in the expression. | Converting abbreviated syntax to verbose is always lossless. Converting verbose to abbreviated is only possible when all hooks are free of comments, properties, and identifiers that do not appear in the expression. | ||
| - | A common situation where this matters: if you connect a map or lookup table purely to define the output format — not because it is referenced in the expression — connecting it via a ''NumberMap'' or ''NumberTable'' hook will block abbreviated syntax, since the hook defines an identifier absent from the expression. Use the dedicated port instead: the ''Result Format'' input port for map calculators, or the ''Base Lookup Table'' input port for lookup table calculators. These connections do not create hooks and do not affect the availability of abbreviated syntax. | + | > **Note:** This distinction applies only to saved or generated script text. The graphical interface always displays and edits a calculator functor's expression in the verbose ''i1''/''t1''/''v1'' form, with each hook represented as its own functor node, regardless of whether the //Use abbreviated syntax// option is enabled or which form the underlying file uses. The ''#name''/''%name''/''$name'' abbreviated form exists only in script text; the graphical interface has no corresponding abbreviated representation. |
| + | |||
| + | A common situation where this matters: if you connect a map or lookup table purely to define the output format — not because it is referenced in the expression — connecting it via a ''NumberMap'' or ''NumberTable'' hook will block abbreviated syntax, since the hook defines an identifier absent from the expression. Use the dedicated port instead: the ''Result Format'' input port for map calculators, or the ''Base Lookup Table'' input port for lookup table calculators. These connections do not create hooks and do not affect the availability of abbreviated syntax. | ||
| - | > **Note:** This constraint applies specifically when the GUI saves a script or generates a fragment in abbreviated syntax. When writing a script by hand in a text editor, the situation does not arise — you simply write the appropriate form directly. | + | > **Note:** This constraint applies specifically when the GUI saves a script or generates a fragment in abbreviated syntax. When writing a script by hand in a text editor, the situation does not arise — you simply write the appropriate form directly. |
| - | > **Tip:** You can control which form Dinamica EGO uses when saving a script via the //Use abbreviated syntax for Calculate family functors// option, found under //Tools// → //Options// | //EGO Script// — see [[#script_generation_options|Script generation options]] for the full list of generation options. | + | > **Tip:** You can control which form Dinamica EGO uses when saving a script via the //Use abbreviated syntax for Calculate family functors// option, found under //Tools// → //Options// | //EGO Script// — see [[#script_generation_options|Script generation options]] for the full list of generation options. |
| ==== The two lookup-table calculators ==== | ==== The two lookup-table calculators ==== | ||
| Line 589: | Line 611: | ||
| </code> | </code> | ||
| - | In the verbose form the three maps arrive as ''i1'', ''i2'', ''i3'' and the value as ''v1'', each supplied by a hook in the trailing block. In the abbreviated form the same operands are referenced directly by their variable names — ''#arePixelsOriginalOrNoisyWithCategories'', ''#originalPixelsOnlyInRestoredImage'', ''#currentRestoredImage'', and ''$lK'' — with the trailing block omitted entirely. | + | In the verbose form the three maps arrive as ''i1'', ''i2'', ''i3'' and the value as ''v1'', each supplied by a hook in the trailing block. In the abbreviated form the same operands are referenced directly by their variable names — ''#arePixelsOriginalOrNoisyWithCategories'', ''#originalPixelsOnlyInRestoredImage'', ''#currentRestoredImage'', and ''$lK'' — with the trailing block omitted entirely. |
| ---- | ---- | ||
| Line 638: | Line 660: | ||
| A **property** annotates a functor (or the model as a whole) with metadata. Properties do not change what a model computes; they affect how it is labeled, displayed, and organized in the visual editor, and they carry documentation. | A **property** annotates a functor (or the model as a whole) with metadata. Properties do not change what a model computes; they affect how it is labeled, displayed, and organized in the visual editor, and they carry documentation. | ||
| - | A property is written immediately before the functor it applies to. There are two equivalent syntaxes — the ''@'' form and the comment form: | + | A property is written immediately before the functor it applies to. There are two equivalent syntaxes — the ''@'' form and the comment form: |
| <code> | <code> | ||
| Line 670: | Line 692: | ||
| | ''color'' | ''dff.functor.color'' | A background color for the functor's node, as a hex code such as ''#ccffcc''. Useful to visually differentiate or group related functors and groups. | | | ''color'' | ''dff.functor.color'' | A background color for the functor's node, as a hex code such as ''#ccffcc''. Useful to visually differentiate or group related functors and groups. | | ||
| | ''inline'' | ''dff.functor.inline'' | When set, prevents the functor from being inlined when the script is written. Any other property defined on the functor (a comment, alias, etc.) has the same effect automatically, since an inlined functor has no separate node to preserve that information. Values: ''yes''/''no'' or ''true''/''false''. | | | ''inline'' | ''dff.functor.inline'' | When set, prevents the functor from being inlined when the script is written. Any other property defined on the functor (a comment, alias, etc.) has the same effect automatically, since an inlined functor has no separate node to preserve that information. Values: ''yes''/''no'' or ''true''/''false''. | | ||
| - | | ''comment'' | ''dff.functor.comment'' | The functor's brief comment — the text before ''==='' in a ''<nowiki>//</nowiki>'' comment (see [[#comments|Comments]]). | | + | | ''comment'' | ''dff.functor.comment'' | The functor's brief comment — the text before ''==='' in a ''<nowiki>//</nowiki>'' comment (see [[#comments|Comments]]). | |
| - | | ''extendedcomment'' | ''dff.functor.extendedcomment'' | The functor's extended comment — the text after ''==='' in a ''<nowiki>//</nowiki>'' comment (see [[#comments|Comments]]). | | + | | ''extendedcomment'' | ''dff.functor.extendedcomment'' | The functor's extended comment — the text after ''==='' in a ''<nowiki>//</nowiki>'' comment (see [[#comments|Comments]]). | |
| | ''collapsed'' | ''dff.container.collapsed'' | Indicates that a container is drawn closed (its contents hidden) in the graphical interface. Applies to containers only. Values: ''yes''/''no'' or ''true''/''false''. | | | ''collapsed'' | ''dff.container.collapsed'' | Indicates that a container is drawn closed (its contents hidden) in the graphical interface. Applies to containers only. Values: ''yes''/''no'' or ''true''/''false''. | | ||
| - | | ''ignoredIssues'' | ''dff.functor.ignoredissues'' | Suppresses a specific category of GUI warning for the functor — observed values include ''Experimental'' (the functor is experimental) and ''ResultIgnored'' (an output is intentionally left unused). | | + | | ''ignoredIssues'' | ''dff.functor.ignoredissues'' | Suppresses a specific category of GUI warning for the functor — observed values include ''Experimental'' (the functor is experimental) and ''ResultIgnored'' (an output is intentionally left unused). | |
| > **Note:** A few per-functor properties exist only while a model is open in the editor and are never written to the saved model: ''dff.functor.text'', ''dff.functor.file'', ''dff.functor.line'', and ''dff.functor.column''. | > **Note:** A few per-functor properties exist only while a model is open in the editor and are never written to the saved model: ''dff.functor.text'', ''dff.functor.file'', ''dff.functor.line'', and ''dff.functor.column''. | ||
| Line 681: | Line 703: | ||
| A **light enum** turns an integer-valued port into a named dropdown in the GUI, replacing the free-form numeric field with a set of labelled choices. The underlying value stored and passed is still an integer; the dropdown is a presentation layer only. | A **light enum** turns an integer-valued port into a named dropdown in the GUI, replacing the free-form numeric field with a set of labelled choices. The underlying value stored and passed is still an integer; the dropdown is a presentation layer only. | ||
| - | Any port belonging to the integer family of types (''IntegerValue'', ''NonNegativeIntegerValue'', ''PositiveIntegerValue'') can be annotated as a light enum, in any model — not just submodels. The annotation applies to carrier functors of those types that expose an editor. | + | Any port belonging to the integer family of types (''IntegerValue'', ''NonNegativeIntegerValue'', ''PositiveIntegerValue'') can be annotated as a light enum, in any model — not just submodels. The annotation applies to carrier functors of those types that expose an editor. |
| The annotation is written immediately before the carrier functor using the ''@lightenum.<portName>'' property, where ''<portName>'' is the actual name of the port being annotated. The value is a JSON object with two fields: | The annotation is written immediately before the carrier functor using the ''@lightenum.<portName>'' property, where ''<portName>'' is the actual name of the port being annotated. The value is a JSON object with two fields: | ||
| - | * ''enabled'' — ''true'' to activate the dropdown, ''false'' to disable it. | + | * ''enabled'' — ''true'' to activate the dropdown, ''false'' to disable it. |
| - | * ''values'' — a mapping from integer values (as quoted strings) to human-readable labels. | + | * ''values'' — a mapping from integer values (as quoted strings) to human-readable labels. |
| <code> | <code> | ||
| Line 701: | Line 723: | ||
| The GUI and the script use two directions of conversion between an alias and a variable name. | The GUI and the script use two directions of conversion between an alias and a variable name. | ||
| - | **Saving (alias → variable name):** When the GUI writes a script, it derives the variable name from the alias by splitting the alias into words — using spaces, underscores, and lowercase-to-uppercase transitions (CamelCase) as word boundaries — then recombining them as a valid identifier. Leading and trailing spaces or underscores are ignored. Non-alphanumeric characters other than underscores are ignored entirely. The result starts with a lowercase letter. | + | **Saving (alias → variable name):** When the GUI writes a script, it derives the variable name from the alias by splitting the alias into words — using spaces, underscores, and lowercase-to-uppercase transitions (CamelCase) as word boundaries — then recombining them as a valid identifier. Leading and trailing spaces or underscores are ignored. Non-alphanumeric characters other than underscores are ignored entirely. The result starts with a lowercase letter. |
| ^ Alias ^ Variable name ^ | ^ Alias ^ Variable name ^ | ||
| Line 712: | Line 734: | ||
| If two or more functors produce the same variable name from their aliases, a number is appended to disambiguate: the first functor keeps the base name, subsequent ones become ''name2'', ''name3'', and so on. | If two or more functors produce the same variable name from their aliases, a number is appended to disambiguate: the first functor keeps the base name, subsequent ones become ''name2'', ''name3'', and so on. | ||
| - | If the conversion alias → variable → alias does not round-trip back to the same original alias — for example because non-alphanumeric characters were discarded — the GUI explicitly writes the ''@alias'' property in the saved script to preserve the original alias without loss of information. In such cases you will see an explicit ''@alias = ...'' annotation even though the functor's variable name is present. | + | If the conversion alias → variable → alias does not round-trip back to the same original alias — for example because non-alphanumeric characters were discarded — the GUI explicitly writes the ''@alias'' property in the saved script to preserve the original alias without loss of information. In such cases you will see an explicit ''@alias = ...'' annotation even though the functor's variable name is present. |
| - | **Loading (variable name → alias):** When a script is read back into the GUI and a functor has no explicit ''@alias'' property, the alias is reconstructed from the variable name using the following rules: | + | **Loading (variable name → alias):** When a script is read back into the GUI and a functor has no explicit ''@alias'' property, the alias is reconstructed from the variable name using the following rules: |
| * The first letter is converted to uppercase. | * The first letter is converted to uppercase. | ||
| * A space is inserted at every lowercase-to-uppercase transition. | * A space is inserted at every lowercase-to-uppercase transition. | ||
| * A space is inserted at every letter-to-digit and digit-to-letter transition. | * A space is inserted at every letter-to-digit and digit-to-letter transition. | ||
| - | * Sequences of consecutive uppercase letters are treated as a single word — no space is inserted within them. | + | * Sequences of consecutive uppercase letters are treated as a single word — no space is inserted within them. |
| * Leading and trailing underscores are discarded. | * Leading and trailing underscores are discarded. | ||
| * Internal underscores (and sequences of multiple underscores) are converted to a single space. | * Internal underscores (and sequences of multiple underscores) are converted to a single space. | ||
| Line 730: | Line 752: | ||
| | ''saddleHeight2'' | ''Saddle Height 2'' | | | ''saddleHeight2'' | ''Saddle Height 2'' | | ||
| - | One case is excluded from this reconstruction: a variable name that is just the functor's own name, lowercased at the front, with a trailing number — for example ''lookupTable0'' for a ''LookupTable'' functor. This pattern is recognized as an **auto-generated name**: one the GUI assigned on its own because no alias was ever set, rather than a name a person chose. Such names are not reconstructed into an alias like ''Lookup Table 0''; the functor is simply shown with no alias. | + | One case is excluded from this reconstruction: a variable name that is just the functor's own name, lowercased at the front, with a trailing number — for example ''lookupTable0'' for a ''LookupTable'' functor. This pattern is recognized as an **auto-generated name**: one the GUI assigned on its own because no alias was ever set, rather than a name a person chose. Such names are not reconstructed into an alias like ''Lookup Table 0''; the functor is simply shown with no alias. |
| ==== Model-level metadata ==== | ==== Model-level metadata ==== | ||
| Line 762: | Line 784: | ||
| ^ Property ^ Description ^ Example ^ | ^ Property ^ Description ^ Example ^ | ||
| - | | ''@submodel.name'' | The functor name of the submodel — this is effectively the name of the new functor that other models and submodels can use. It also appears as the display name in the functor library. | ''AbortConditionally'' | | + | | ''@submodel.name'' | The functor name of the submodel — this is effectively the name of the new functor that other models and submodels can use. It also appears as the display name in the functor library. | ''AbortConditionally'' | |
| | ''@submodel.description'' | Documentation for the submodel. | ''Aborts the execution conditionally and prints a message before terminating the model.'' | | | ''@submodel.description'' | Documentation for the submodel. | ''Aborts the execution conditionally and prints a message before terminating the model.'' | | ||
| | ''@submodel.group'' | Functor library group path, using '':'' as a hierarchy separator. | ''Elevation Graph:Utilities'' | | | ''@submodel.group'' | Functor library group path, using '':'' as a hierarchy separator. | ''Elevation Graph:Utilities'' | | ||
| | ''@submodel.documentation'' | URL where the submodel's documentation can be found. | ''https://example.com/docs/my-submodel'' | | | ''@submodel.documentation'' | URL where the submodel's documentation can be found. | ''https://example.com/docs/my-submodel'' | | ||
| - | | ''@submodel.largeicon'' | Base64-encoded large icon (32×32 px) displayed in the functor library. | //base64 string// | | + | | ''@submodel.largeicon'' | Base64-encoded large icon (32×32 px) displayed in the functor library. | //base64 string// | |
| - | | ''@submodel.smallicon'' | Base64-encoded small icon (16×16 px) displayed in the functor library. | //base64 string// | | + | | ''@submodel.smallicon'' | Base64-encoded small icon (16×16 px) displayed in the functor library. | //base64 string// | |
| ==== Declaring input ports ==== | ==== Declaring input ports ==== | ||
| Line 779: | Line 801: | ||
| | ''@submodel.in.<portName>.order'' | Position of the port in the resulting submodel's interface. | ''1'' | | | ''@submodel.in.<portName>.order'' | Position of the port in the resulting submodel's interface. | ''1'' | | ||
| | ''@submodel.in.<portName>.advanced'' | Whether the port is hidden under an "Advanced" section in the GUI. | ''yes'' or ''no'' | | | ''@submodel.in.<portName>.advanced'' | Whether the port is hidden under an "Advanced" section in the GUI. | ''yes'' or ''no'' | | ||
| - | | ''@submodel.in.<portName>.group'' | Places the port in a named tab in the GUI functor editor. Ports sharing the same group name appear together in the same tab. A numeric prefix of the form ''N -'' (e.g. ''1 - Patches'', ''4 - Hilltop Criteria'') controls the tab's position — the prefix is stripped from the displayed tab name, so ''1 - Patches'' creates a tab named //Patches// that appears first. If no group is specified, the port appears in the default //General// tab; ports marked ''advanced = yes'' appear in the //Advanced// tab. | ''2 - Hilltop Clustering'' | | + | | ''@submodel.in.<portName>.group'' | Places the port in a named tab in the GUI functor editor. Ports sharing the same group name appear together in the same tab. A numeric prefix of the form ''N -'' (e.g. ''1 - Patches'', ''4 - Hilltop Criteria'') controls the tab's position — the prefix is stripped from the displayed tab name, so ''1 - Patches'' creates a tab named //Patches// that appears first. If no group is specified, the port appears in the default //General// tab; ports marked ''advanced = yes'' appear in the //Advanced// tab. | ''2 - Hilltop Clustering'' | |
| - | Integer-valued ports can also be annotated with ''@lightenum'' to present them as a named dropdown in the GUI — see [[#light_enum|Light enum]] for details. The annotation is placed before the ''@submodel.in.*'' block: | + | Integer-valued ports can also be annotated with ''@lightenum'' to present them as a named dropdown in the GUI — see [[#light_enum|Light enum]] for details. The annotation is placed before the ''@submodel.in.*'' block: |
| <code> | <code> | ||
| Line 875: | Line 897: | ||
| ^ Order ^ Name ^ Type ^ Required ^ Default ^ Advanced ^ Description ^ | ^ Order ^ Name ^ Type ^ Required ^ Default ^ Advanced ^ Description ^ | ||
| - | | 1 | ''shouldAbort'' | BooleanValue | Yes | — | No | If true, aborts the model execution. | | + | | 1 | ''shouldAbort'' | BooleanValue | Yes | — | No | If true, aborts the model execution. | |
| - | | 2 | ''errorMessage'' | String | Yes | — | No | Message displayed before aborting the model. | | + | | 2 | ''errorMessage'' | String | Yes | — | No | Message displayed before aborting the model. | |
| **ValidateInputValue** (calls ''AbortConditionally'' internally): | **ValidateInputValue** (calls ''AbortConditionally'' internally): | ||
| Line 924: | Line 946: | ||
| ^ Order ^ Name ^ Type ^ Required ^ Default ^ Advanced ^ Description ^ | ^ Order ^ Name ^ Type ^ Required ^ Default ^ Advanced ^ Description ^ | ||
| - | | 1 | ''inputValue'' | RealValue | Yes | — | No | Value that will be validated. | | + | | 1 | ''inputValue'' | RealValue | Yes | — | No | Value that will be validated. | |
| - | | 2 | ''validationExpression'' | ImageExpression | Yes | — | No | Expression used to validate the input value. The input value should be mentioned as "v1". Must return zero if valid, non-zero if invalid. | | + | | 2 | ''validationExpression'' | ImageExpression | Yes | — | No | Expression used to validate the input value. The input value should be mentioned as "v1". Must return zero if valid, non-zero if invalid. | |
| | 3 | ''errorMessageFormat'' | String | No | ''$"(The value <v1> is not valid.)"'' | Yes | Message used to report an invalid value. The value can be mentioned using the tag <v1>. | | | 3 | ''errorMessageFormat'' | String | No | ''$"(The value <v1> is not valid.)"'' | Yes | Message used to report an invalid value. The value can be mentioned using the tag <v1>. | | ||
| Line 947: | Line 969: | ||
| It is conventional to name the submodel file after its ''@submodel.name'' value with a ''.ego'' extension, but what matters is the value of that property, not the filename. Where the file is placed determines whether it becomes a local or user submodel: | It is conventional to name the submodel file after its ''@submodel.name'' value with a ''.ego'' extension, but what matters is the value of that property, not the filename. Where the file is placed determines whether it becomes a local or user submodel: | ||
| - | * **Local submodel** — place the file in the ''<modelfile>_ego_Submodels'' folder next to the main model file. | + | * **Local submodel** — place the file in the ''<modelfile>_ego_Submodels'' folder next to the main model file. |
| - | * **User submodel** — place the file in the user submodel folder: | + | * **User submodel** — place the file in the user submodel folder: |
| * **Windows:** ''C:\Users\<User>\Documents\Dinamica EGO <version>\Submodels\'' | * **Windows:** ''C:\Users\<User>\Documents\Dinamica EGO <version>\Submodels\'' | ||
| * **Linux:** ''/home/<User>/Dinamica EGO <version>/Submodels/'' | * **Linux:** ''/home/<User>/Dinamica EGO <version>/Submodels/'' | ||
| Line 956: | Line 978: | ||
| ===== Script generation options ===== | ===== Script generation options ===== | ||
| - | When the GUI saves a script or generates an EGO Script fragment, a set of options controls how the output is formatted. These are found under //Tools// → //Options// | //EGO Script//: | + | When the GUI saves a script or generates an EGO Script fragment, a set of options controls how the output is formatted. These are found under //Tools// → //Options// | //EGO Script//: |
| ^ Option ^ Description ^ | ^ Option ^ Description ^ | ||
| | **Minimum number of functor inputs to use named parameters** | Below this threshold, inputs are written in positional syntax; at or above it, nominal (named parameter) syntax is used instead. | | | **Minimum number of functor inputs to use named parameters** | Below this threshold, inputs are written in positional syntax; at or above it, nominal (named parameter) syntax is used instead. | | ||
| | **Inline any suitable functor** | When enabled, eligible functors are written inline rather than as separate named definitions. | | | **Inline any suitable functor** | When enabled, eligible functors are written inline rather than as separate named definitions. | | ||
| - | | ↳ **Inline suitable functors using named parameters** | When enabled, functors are eligible for inlining even if their inputs would be written using nominal syntax. | | + | | ↳ **Inline suitable functors using named parameters** | When enabled, functors are eligible for inlining even if their inputs would be written using nominal syntax. | |
| - | | ↳ **Also inline: Suitable containers** | Extends inlining to container functors that qualify. | | + | | ↳ **Also inline: Suitable containers** | Extends inlining to container functors that qualify. | |
| - | | ↳ **Also inline: Suitable muxes** | Extends inlining to mux functors that qualify. | | + | | ↳ **Also inline: Suitable muxes** | Extends inlining to mux functors that qualify. | |
| - | | ↳ **Also inline: Suitable functors bound to feedback inputs** | Extends inlining to functors whose output feeds back into a loop's mux. | | + | | ↳ **Also inline: Suitable functors bound to feedback inputs** | Extends inlining to functors whose output feeds back into a loop's mux. | |
| | **Use abbreviated syntax for Calculate family functors** | Whether the ''Calculate'' family is written using the shorthand symbol (''#'', ''##'', ''%'', etc.) or the full functor name. See the [[#calculator_functor_shorthand|Calculator functor shorthand]] section for details. | | | **Use abbreviated syntax for Calculate family functors** | Whether the ''Calculate'' family is written using the shorthand symbol (''#'', ''##'', ''%'', etc.) or the full functor name. See the [[#calculator_functor_shorthand|Calculator functor shorthand]] section for details. | | ||
| | **Preferred number of columns before wrapping comments** | The line width at which the generator wraps long comment text. | | | **Preferred number of columns before wrapping comments** | The line width at which the generator wraps long comment text. | | ||