|
@@ -2,13 +2,42 @@ from abc import *
|
|
from typing import *
|
|
from typing import *
|
|
from dataclasses import *
|
|
from dataclasses import *
|
|
from sccd.util.duration import *
|
|
from sccd.util.duration import *
|
|
-from sccd.syntax.scope import *
|
|
|
|
|
|
+from sccd.action_lang.static.scope import *
|
|
|
|
+
|
|
|
|
+class MemoryInterface(ABC):
|
|
|
|
+
|
|
|
|
+ @abstractmethod
|
|
|
|
+ def current_frame(self) -> 'StackFrame':
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+ @abstractmethod
|
|
|
|
+ def push_frame(self, scope: Scope):
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+ @abstractmethod
|
|
|
|
+ def push_frame_w_context(self, scope: Scope, context: 'StackFrame'):
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+ @abstractmethod
|
|
|
|
+ def pop_frame(self):
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+ @abstractmethod
|
|
|
|
+ def load(self, offset: int) -> Any:
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+ @abstractmethod
|
|
|
|
+ def store(self, offset: int, value: Any):
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+ @abstractmethod
|
|
|
|
+ def flush_transition(self, read_only: bool = False):
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+ @abstractmethod
|
|
|
|
+ def flush_round(self):
|
|
|
|
+ pass
|
|
|
|
|
|
-@dataclass
|
|
|
|
-class EvalContext:
|
|
|
|
- current_state: 'StatechartState'
|
|
|
|
- events: List['Event']
|
|
|
|
- memory: 'MemoryInterface'
|
|
|
|
|
|
|
|
# Thrown if the type checker encountered something illegal.
|
|
# Thrown if the type checker encountered something illegal.
|
|
# Not to be confused with Python's TypeError exception.
|
|
# Not to be confused with Python's TypeError exception.
|
|
@@ -20,17 +49,17 @@ class Expression(ABC):
|
|
# Determines the static type of the expression. May throw if there is a type error.
|
|
# Determines the static type of the expression. May throw if there is a type error.
|
|
# Returns static type of expression.
|
|
# Returns static type of expression.
|
|
@abstractmethod
|
|
@abstractmethod
|
|
- def init_rvalue(self, scope: Scope) -> SCCDType:
|
|
|
|
|
|
+ def init_expr(self, scope: Scope) -> SCCDType:
|
|
pass
|
|
pass
|
|
|
|
|
|
# Evaluation should NOT have side effects.
|
|
# Evaluation should NOT have side effects.
|
|
# Motivation is that the evaluation of a guard condition cannot have side effects.
|
|
# Motivation is that the evaluation of a guard condition cannot have side effects.
|
|
@abstractmethod
|
|
@abstractmethod
|
|
- def eval(self, ctx: EvalContext):
|
|
|
|
|
|
+ def eval(self, memory: MemoryInterface):
|
|
pass
|
|
pass
|
|
|
|
|
|
# The LValue type is any type that can serve as an expression OR an LValue (left hand of assignment)
|
|
# The LValue type is any type that can serve as an expression OR an LValue (left hand of assignment)
|
|
-# Either 'init_rvalue' or 'init_lvalue' is called to initialize the LValue.
|
|
|
|
|
|
+# Either 'init_expr' or 'init_lvalue' is called to initialize the LValue.
|
|
# Then either 'eval' or 'eval_lvalue' can be called any number of times.
|
|
# Then either 'eval' or 'eval_lvalue' can be called any number of times.
|
|
class LValue(Expression):
|
|
class LValue(Expression):
|
|
# Initialize the LValue as an LValue.
|
|
# Initialize the LValue as an LValue.
|
|
@@ -42,27 +71,27 @@ class LValue(Expression):
|
|
# offset ∈ [0, +∞[ : variable's memory address is within current scope
|
|
# offset ∈ [0, +∞[ : variable's memory address is within current scope
|
|
# offset ∈ ]-∞, 0[ : variable's memory address is in a parent scope (or better: 'context scope')
|
|
# offset ∈ ]-∞, 0[ : variable's memory address is in a parent scope (or better: 'context scope')
|
|
@abstractmethod
|
|
@abstractmethod
|
|
- def eval_lvalue(self, ctx: EvalContext) -> int:
|
|
|
|
|
|
+ def eval_lvalue(self) -> int:
|
|
pass
|
|
pass
|
|
|
|
|
|
- # LValues can also serve as expressions!
|
|
|
|
- def eval(self, ctx: EvalContext):
|
|
|
|
- offset = self.eval_lvalue(ctx)
|
|
|
|
- return ctx.memory.load(offset)
|
|
|
|
|
|
+ # Any type that is an LValue can also serve as an expression!
|
|
|
|
+ def eval(self, memory: MemoryInterface):
|
|
|
|
+ offset = self.eval_lvalue()
|
|
|
|
+ return memory.load(offset)
|
|
|
|
|
|
@dataclass
|
|
@dataclass
|
|
class Identifier(LValue):
|
|
class Identifier(LValue):
|
|
name: str
|
|
name: str
|
|
offset: Optional[int] = None
|
|
offset: Optional[int] = None
|
|
|
|
|
|
- def init_rvalue(self, scope: Scope) -> SCCDType:
|
|
|
|
|
|
+ def init_expr(self, scope: Scope) -> SCCDType:
|
|
self.offset, type = scope.get_rvalue(self.name)
|
|
self.offset, type = scope.get_rvalue(self.name)
|
|
return type
|
|
return type
|
|
|
|
|
|
def init_lvalue(self, scope: Scope, type):
|
|
def init_lvalue(self, scope: Scope, type):
|
|
self.offset = scope.put_lvalue(self.name, type)
|
|
self.offset = scope.put_lvalue(self.name, type)
|
|
|
|
|
|
- def eval_lvalue(self, ctx: EvalContext) -> int:
|
|
|
|
|
|
+ def eval_lvalue(self) -> int:
|
|
return self.offset
|
|
return self.offset
|
|
|
|
|
|
def render(self):
|
|
def render(self):
|
|
@@ -75,24 +104,24 @@ class FunctionCall(Expression):
|
|
|
|
|
|
type: Optional[type] = None
|
|
type: Optional[type] = None
|
|
|
|
|
|
- def init_rvalue(self, scope: Scope) -> SCCDType:
|
|
|
|
- function_type = self.function.init_rvalue(scope)
|
|
|
|
|
|
+ def init_expr(self, scope: Scope) -> SCCDType:
|
|
|
|
+ function_type = self.function.init_expr(scope)
|
|
if not isinstance(function_type, SCCDFunction):
|
|
if not isinstance(function_type, SCCDFunction):
|
|
raise StaticTypeError("Function call: Expression '%s' is not a function" % self.function.render())
|
|
raise StaticTypeError("Function call: Expression '%s' is not a function" % self.function.render())
|
|
|
|
|
|
formal_types = function_type.param_types
|
|
formal_types = function_type.param_types
|
|
return_type = function_type.return_type
|
|
return_type = function_type.return_type
|
|
|
|
|
|
- actual_types = [p.init_rvalue(scope) for p in self.params]
|
|
|
|
|
|
+ actual_types = [p.init_expr(scope) for p in self.params]
|
|
for i, (formal, actual) in enumerate(zip(formal_types, actual_types)):
|
|
for i, (formal, actual) in enumerate(zip(formal_types, actual_types)):
|
|
if formal != actual:
|
|
if formal != actual:
|
|
raise StaticTypeError("Function call, argument %d: %s is not expected type %s, instead is %s" % (i, self.params[i].render(), str(formal), str(actual)))
|
|
raise StaticTypeError("Function call, argument %d: %s is not expected type %s, instead is %s" % (i, self.params[i].render(), str(formal), str(actual)))
|
|
return return_type
|
|
return return_type
|
|
|
|
|
|
- def eval(self, ctx: EvalContext):
|
|
|
|
- f = self.function.eval(ctx)
|
|
|
|
- p = [p.eval(ctx) for p in self.params]
|
|
|
|
- return f(ctx, *p)
|
|
|
|
|
|
+ def eval(self, memory: MemoryInterface):
|
|
|
|
+ f = self.function.eval(memory)
|
|
|
|
+ p = [p.eval(memory) for p in self.params]
|
|
|
|
+ return f(memory, *p)
|
|
|
|
|
|
def render(self):
|
|
def render(self):
|
|
return self.function.render()+'('+','.join([p.render() for p in self.params])+')'
|
|
return self.function.render()+'('+','.join([p.render() for p in self.params])+')'
|
|
@@ -116,7 +145,7 @@ class FunctionDeclaration(Expression):
|
|
body: 'Statement'
|
|
body: 'Statement'
|
|
scope: Optional[Scope] = None
|
|
scope: Optional[Scope] = None
|
|
|
|
|
|
- def init_rvalue(self, scope: Scope) -> SCCDType:
|
|
|
|
|
|
+ def init_expr(self, scope: Scope) -> SCCDType:
|
|
self.scope = Scope("function", scope)
|
|
self.scope = Scope("function", scope)
|
|
# Reserve space for arguments on stack
|
|
# Reserve space for arguments on stack
|
|
for p in self.params_decl:
|
|
for p in self.params_decl:
|
|
@@ -125,30 +154,30 @@ class FunctionDeclaration(Expression):
|
|
return_type = ret.get_return_type()
|
|
return_type = ret.get_return_type()
|
|
return SCCDFunction([p.formal_type for p in self.params_decl], return_type)
|
|
return SCCDFunction([p.formal_type for p in self.params_decl], return_type)
|
|
|
|
|
|
- def eval(self, ctx: EvalContext):
|
|
|
|
- context: StackFrame = ctx.memory.current_frame()
|
|
|
|
- def FUNCTION(ctx: EvalContext, *params):
|
|
|
|
- ctx.memory.push_frame_w_context(self.scope, context)
|
|
|
|
|
|
+ def eval(self, memory: MemoryInterface):
|
|
|
|
+ context: 'StackFrame' = memory.current_frame()
|
|
|
|
+ def FUNCTION(memory: MemoryInterface, *params):
|
|
|
|
+ memory.push_frame_w_context(self.scope, context)
|
|
# Copy arguments to stack
|
|
# Copy arguments to stack
|
|
for val, p in zip(params, self.params_decl):
|
|
for val, p in zip(params, self.params_decl):
|
|
- ctx.memory.store(p.offset, val)
|
|
|
|
- ret = self.body.exec(ctx)
|
|
|
|
- ctx.memory.pop_frame()
|
|
|
|
|
|
+ memory.store(p.offset, val)
|
|
|
|
+ ret = self.body.exec(memory)
|
|
|
|
+ memory.pop_frame()
|
|
return ret.val
|
|
return ret.val
|
|
return FUNCTION
|
|
return FUNCTION
|
|
|
|
|
|
def render(self) -> str:
|
|
def render(self) -> str:
|
|
- return "<func_decl>" # todo
|
|
|
|
|
|
+ return "func(%s) [...]" % ", ".join(p.render() for p in self.params_decl) # todo
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
@dataclass
|
|
class StringLiteral(Expression):
|
|
class StringLiteral(Expression):
|
|
string: str
|
|
string: str
|
|
|
|
|
|
- def init_rvalue(self, scope: Scope) -> SCCDType:
|
|
|
|
|
|
+ def init_expr(self, scope: Scope) -> SCCDType:
|
|
return SCCDString
|
|
return SCCDString
|
|
|
|
|
|
- def eval(self, ctx: EvalContext):
|
|
|
|
|
|
+ def eval(self, memory: MemoryInterface):
|
|
return self.string
|
|
return self.string
|
|
|
|
|
|
def render(self):
|
|
def render(self):
|
|
@@ -159,10 +188,10 @@ class StringLiteral(Expression):
|
|
class IntLiteral(Expression):
|
|
class IntLiteral(Expression):
|
|
i: int
|
|
i: int
|
|
|
|
|
|
- def init_rvalue(self, scope: Scope) -> SCCDType:
|
|
|
|
|
|
+ def init_expr(self, scope: Scope) -> SCCDType:
|
|
return SCCDInt
|
|
return SCCDInt
|
|
|
|
|
|
- def eval(self, ctx: EvalContext):
|
|
|
|
|
|
+ def eval(self, memory: MemoryInterface):
|
|
return self.i
|
|
return self.i
|
|
|
|
|
|
def render(self):
|
|
def render(self):
|
|
@@ -172,10 +201,10 @@ class IntLiteral(Expression):
|
|
class BoolLiteral(Expression):
|
|
class BoolLiteral(Expression):
|
|
b: bool
|
|
b: bool
|
|
|
|
|
|
- def init_rvalue(self, scope: Scope) -> SCCDType:
|
|
|
|
|
|
+ def init_expr(self, scope: Scope) -> SCCDType:
|
|
return SCCDBool
|
|
return SCCDBool
|
|
|
|
|
|
- def eval(self, ctx: EvalContext):
|
|
|
|
|
|
+ def eval(self, memory: MemoryInterface):
|
|
return self.b
|
|
return self.b
|
|
|
|
|
|
def render(self):
|
|
def render(self):
|
|
@@ -185,10 +214,10 @@ class BoolLiteral(Expression):
|
|
class DurationLiteral(Expression):
|
|
class DurationLiteral(Expression):
|
|
d: Duration
|
|
d: Duration
|
|
|
|
|
|
- def init_rvalue(self, scope: Scope) -> SCCDType:
|
|
|
|
|
|
+ def init_expr(self, scope: Scope) -> SCCDType:
|
|
return SCCDDuration
|
|
return SCCDDuration
|
|
|
|
|
|
- def eval(self, ctx: EvalContext):
|
|
|
|
|
|
+ def eval(self, memory: MemoryInterface):
|
|
return self.d
|
|
return self.d
|
|
|
|
|
|
def render(self):
|
|
def render(self):
|
|
@@ -200,17 +229,17 @@ class Array(Expression):
|
|
|
|
|
|
element_type: Optional[SCCDType] = None
|
|
element_type: Optional[SCCDType] = None
|
|
|
|
|
|
- def init_rvalue(self, scope: Scope) -> SCCDType:
|
|
|
|
|
|
+ def init_expr(self, scope: Scope) -> SCCDType:
|
|
for e in self.elements:
|
|
for e in self.elements:
|
|
- t = e.init_rvalue(scope)
|
|
|
|
|
|
+ t = e.init_expr(scope)
|
|
if self.element_type and self.element_type != t:
|
|
if self.element_type and self.element_type != t:
|
|
raise StaticTypeError("Mixed element types in Array expression: %s and %s" % (str(self.element_type), str(t)))
|
|
raise StaticTypeError("Mixed element types in Array expression: %s and %s" % (str(self.element_type), str(t)))
|
|
self.element_type = t
|
|
self.element_type = t
|
|
|
|
|
|
return SCCDArray(self.element_type)
|
|
return SCCDArray(self.element_type)
|
|
|
|
|
|
- def eval(self, ctx: EvalContext):
|
|
|
|
- return [e.eval(ctx) for e in self.elements]
|
|
|
|
|
|
+ def eval(self, memory: MemoryInterface):
|
|
|
|
+ return [e.eval(memory) for e in self.elements]
|
|
|
|
|
|
def render(self):
|
|
def render(self):
|
|
return '['+','.join([e.render() for e in self.elements])+']'
|
|
return '['+','.join([e.render() for e in self.elements])+']'
|
|
@@ -221,11 +250,11 @@ class Array(Expression):
|
|
class Group(Expression):
|
|
class Group(Expression):
|
|
subexpr: Expression
|
|
subexpr: Expression
|
|
|
|
|
|
- def init_rvalue(self, scope: Scope) -> SCCDType:
|
|
|
|
- return self.subexpr.init_rvalue(scope)
|
|
|
|
|
|
+ def init_expr(self, scope: Scope) -> SCCDType:
|
|
|
|
+ return self.subexpr.init_expr(scope)
|
|
|
|
|
|
- def eval(self, ctx: EvalContext):
|
|
|
|
- return self.subexpr.eval(ctx)
|
|
|
|
|
|
+ def eval(self, memory: MemoryInterface):
|
|
|
|
+ return self.subexpr.eval(memory)
|
|
|
|
|
|
def render(self):
|
|
def render(self):
|
|
return '('+self.subexpr.render()+')'
|
|
return '('+self.subexpr.render()+')'
|
|
@@ -236,9 +265,9 @@ class BinaryExpression(Expression):
|
|
operator: str # token name from the grammar.
|
|
operator: str # token name from the grammar.
|
|
rhs: Expression
|
|
rhs: Expression
|
|
|
|
|
|
- def init_rvalue(self, scope: Scope) -> SCCDType:
|
|
|
|
- lhs_t = self.lhs.init_rvalue(scope)
|
|
|
|
- rhs_t = self.rhs.init_rvalue(scope)
|
|
|
|
|
|
+ def init_expr(self, scope: Scope) -> SCCDType:
|
|
|
|
+ lhs_t = self.lhs.init_expr(scope)
|
|
|
|
+ rhs_t = self.rhs.init_expr(scope)
|
|
|
|
|
|
def comparison_type():
|
|
def comparison_type():
|
|
same_type()
|
|
same_type()
|
|
@@ -278,24 +307,24 @@ class BinaryExpression(Expression):
|
|
"**": same_type(),
|
|
"**": same_type(),
|
|
}[self.operator]
|
|
}[self.operator]
|
|
|
|
|
|
- def eval(self, ctx: EvalContext):
|
|
|
|
|
|
+ def eval(self, memory: MemoryInterface):
|
|
|
|
|
|
return {
|
|
return {
|
|
- "and": lambda x,y: x.eval(ctx) and y.eval(ctx),
|
|
|
|
- "or": lambda x,y: x.eval(ctx) or y.eval(ctx),
|
|
|
|
- "==": lambda x,y: x.eval(ctx) == y.eval(ctx),
|
|
|
|
- "!=": lambda x,y: x.eval(ctx) != y.eval(ctx),
|
|
|
|
- ">": lambda x,y: x.eval(ctx) > y.eval(ctx),
|
|
|
|
- ">=": lambda x,y: x.eval(ctx) >= y.eval(ctx),
|
|
|
|
- "<": lambda x,y: x.eval(ctx) < y.eval(ctx),
|
|
|
|
- "<=": lambda x,y: x.eval(ctx) <= y.eval(ctx),
|
|
|
|
- "+": lambda x,y: x.eval(ctx) + y.eval(ctx),
|
|
|
|
- "-": lambda x,y: x.eval(ctx) - y.eval(ctx),
|
|
|
|
- "*": lambda x,y: x.eval(ctx) * y.eval(ctx),
|
|
|
|
- "/": lambda x,y: x.eval(ctx) / y.eval(ctx),
|
|
|
|
- "//": lambda x,y: x.eval(ctx) // y.eval(ctx),
|
|
|
|
- "%": lambda x,y: x.eval(ctx) % y.eval(ctx),
|
|
|
|
- "**": lambda x,y: x.eval(ctx) ** y.eval(ctx),
|
|
|
|
|
|
+ "and": lambda x,y: x.eval(memory) and y.eval(memory),
|
|
|
|
+ "or": lambda x,y: x.eval(memory) or y.eval(memory),
|
|
|
|
+ "==": lambda x,y: x.eval(memory) == y.eval(memory),
|
|
|
|
+ "!=": lambda x,y: x.eval(memory) != y.eval(memory),
|
|
|
|
+ ">": lambda x,y: x.eval(memory) > y.eval(memory),
|
|
|
|
+ ">=": lambda x,y: x.eval(memory) >= y.eval(memory),
|
|
|
|
+ "<": lambda x,y: x.eval(memory) < y.eval(memory),
|
|
|
|
+ "<=": lambda x,y: x.eval(memory) <= y.eval(memory),
|
|
|
|
+ "+": lambda x,y: x.eval(memory) + y.eval(memory),
|
|
|
|
+ "-": lambda x,y: x.eval(memory) - y.eval(memory),
|
|
|
|
+ "*": lambda x,y: x.eval(memory) * y.eval(memory),
|
|
|
|
+ "/": lambda x,y: x.eval(memory) / y.eval(memory),
|
|
|
|
+ "//": lambda x,y: x.eval(memory) // y.eval(memory),
|
|
|
|
+ "%": lambda x,y: x.eval(memory) % y.eval(memory),
|
|
|
|
+ "**": lambda x,y: x.eval(memory) ** y.eval(memory),
|
|
}[self.operator](self.lhs, self.rhs) # Borrow Python's lazy evaluation
|
|
}[self.operator](self.lhs, self.rhs) # Borrow Python's lazy evaluation
|
|
|
|
|
|
def render(self):
|
|
def render(self):
|
|
@@ -306,17 +335,17 @@ class UnaryExpression(Expression):
|
|
operator: str # token value from the grammar.
|
|
operator: str # token value from the grammar.
|
|
expr: Expression
|
|
expr: Expression
|
|
|
|
|
|
- def init_rvalue(self, scope: Scope) -> SCCDType:
|
|
|
|
- expr_type = self.expr.init_rvalue(scope)
|
|
|
|
|
|
+ def init_expr(self, scope: Scope) -> SCCDType:
|
|
|
|
+ expr_type = self.expr.init_expr(scope)
|
|
return {
|
|
return {
|
|
"not": SCCDBool,
|
|
"not": SCCDBool,
|
|
"-": expr_type,
|
|
"-": expr_type,
|
|
}[self.operator]
|
|
}[self.operator]
|
|
|
|
|
|
- def eval(self, ctx: EvalContext):
|
|
|
|
|
|
+ def eval(self, memory: MemoryInterface):
|
|
return {
|
|
return {
|
|
- "not": lambda x: not x.eval(ctx),
|
|
|
|
- "-": lambda x: - x.eval(ctx),
|
|
|
|
|
|
+ "not": lambda x: not x.eval(memory),
|
|
|
|
+ "-": lambda x: - x.eval(memory),
|
|
}[self.operator](self.expr)
|
|
}[self.operator](self.expr)
|
|
|
|
|
|
def render(self):
|
|
def render(self):
|