Expression Evaluation

As part of semantic resolution, minimal expression evaluation is implemented. It covers most arithmetic expressions and some standard library functions, enough to parse the standard library and accompanying examples. Currently, the following are not yet supported:

  • arbitrary user defined functions and expressions

Evaluation is implemented to work on a read-only model so it is not allowed to construct new elements. As a side effect, this reduces the memory usage as models parsed from syntactically valid source files cannot have orphan elements. Rather than constructing orphan literal expression elements, evaluation returns the values directly, see Compiler.evaluate and Compiler.evaluate_feature. In both cases, stdlib parameter is used to accelerate internal conformance checks, and scope – to change the scope that expressions are evaluated in, equivalent to Python self. The Compiler is non-recursive and supports terminating evaluation after a number of max_steps, e.g. if the expression is an infinite loop. For the common use case of evaluating member access, use Compiler.evaluate_feature:

result, report = compiler.evaluate_feature(
    feature=feature, scope=owning_type
)

Which is equivalent to evaluating owning_type.feature in SysML. Note that this will take redefinitions of feature in owning_type into account.

Most SysML operators are supported, however a few are not:

  • all, type extent

  • ~, undefined by KerML specification

Labs

Quantity expressions, e.g. 10 [kg], received initial evaluation support in v0.8.2 which needs to be enabled manually by passing experimental_quantities=True to Compiler.evaluate and Compiler.evaluate_feature.

ConstructorExpressions return their owned return parameters (InvocationExpressions – themselves prior to v0.8) because they implicitly conform to the constructed object.

This means that their arguments are not evaluated eagerly. Given an owning_type with a feature that evaluated to a constructor expression value, their arguments can be evaluated as:

compiler = syside.Compiler()
for parameter in value.owned_inputs.collect():
    expr = parameter.feature_value_expression
    if not expr:
        # should not be executed in a valid model
        print(f"{parameter} has no feature value")
    else:
        result, report = compiler.evaluate(expr, scope=owning_type)
        if not report:
            print(f"{parameter} failed to evaluate: {report.diagnostics}")
        elif isinstance(result, str):
            print(f'{parameter} = "{result}"')
        else:
            print(f"{parameter} = {result}")

Note

Before v0.8, constructor expressions could only be inferred as InvocationExpressions that invoked a Type that was neither a subtype of Behavior nor Step, e.g. not isinstance(expr.types.at(0), (*syside.Behavior.STD, *syside.Step.STD)).

In addition, currently supported standard library functions include:

  • NumericalFunctions::product

  • NumericalFunctions::sum

  • SequenceFunctions::notEmpty

  • SequenceFunctions::isEmpty

  • SequenceFunctions::size

  • SequenceFunctions::includes

  • SequenceFunctions::excludes

  • StringFunctions::Length

  • StringFunctions::Substring

Support for more standard library functions will be added in future updates.