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::productNumericalFunctions::sumSequenceFunctions::notEmptySequenceFunctions::isEmptySequenceFunctions::sizeSequenceFunctions::includesSequenceFunctions::excludesStringFunctions::LengthStringFunctions::Substring
Support for more standard library functions will be added in future updates.