Commit f31b3365 authored by Chris Jewell's avatar Chris Jewell
Browse files

Rewrite of the FunctionSymbol system, differentiating formal arguments from...

Rewrite of the FunctionSymbol system, differentiating formal arguments from config arguments in STMSymbol.
parent 03f79c43
Pipeline #252 passed with stage
in 4 minutes and 20 seconds
......@@ -18,7 +18,6 @@
#
from gem.gemlang.ast.utils import serialize
from gem.gemlang.ast.parsetree2ast import ASTValidator
from gem.gemlang.ast_walker import ASTWalker
from gem.gemlang.parse_gemlang import GEMParser
from gem.gemlang.parse_gemlang import gemparse
......@@ -20,7 +20,7 @@
"""Base classes for GEM Abstract Syntax Tree"""
__all__ = ['GEMProgram', 'Statement', 'ASTNode', 'NullNode',
'Block', 'KwRef'] # Augmented automatically with operator classes
'Block', 'FArg', 'KwRef'] # Augmented automatically with operator classes
# Generic expression framework
......@@ -113,6 +113,29 @@ class Statement(ASTNode):
super().__init__(**kwargs)
class FArg(ASTNode):
"""Represents a function argument definition.
:param name: the argument name
"""
def __init__(self, name, **kwargs):
super().__init__(**kwargs)
self.value = name
def __str__(self):
return f"{self.__class__.__name__} {self.value}"
class FArgList(ASTNode):
"""Represents a list of ArgDefs
:param args: a list of ArgDefs
"""
def __init__(self, argdefs, **kwargs):
super().__init__(**kwargs)
for arg in argdefs:
assert isinstance(arg, FArg)
self.add_child(arg)
class KwRef(ASTNode):
"""Represents a reference to a keyword in function arguments.
......
......@@ -19,7 +19,7 @@
"""Statement-related ASTNode derived classes defined here."""
from gem.gemlang.ast.ast_base import Statement, Block
from gem.gemlang.ast.ast_base import Statement, Block, FArgList
from gem.gemlang.ast.ast_expression import IdRef, Expr, TransitionDef, ArgList
__all__ = ['Decl', 'EpiDecl', 'AssignExpr', 'AssignTransition',
......@@ -47,7 +47,7 @@ class EpiDecl(Decl):
super().__init__(**kwargs)
assert isinstance(name, str)
assert isinstance(config, ArgList)
assert isinstance(param, ArgList)
assert isinstance(param, FArgList), f"param of type {type(param)} is not ArgDefList"
assert isinstance(block, Block)
self.name = name
self.add_child(config)
......
# Copyright 2019 Chris Jewell <c.jewell@lancaster.ac.uk>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software
# and associated documentation files (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge, publish, distribute,
# sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all copies or
# substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
# IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
"""Classes and functions for translating parser tree to AST"""
# pylint: disable=wildcard-import,unused-wildcard-import
from lark import Transformer, v_args
from lark import Tree as lark_tree
from gem.gemlang.ast import ast_parser
from gem.gemlang.ast.utils import serialize
from gem.gemlang.ast.ast_base import GEMProgram, Block, KwRef, ASTNode
from gem.gemlang.ast.ast_expression import *
from gem.gemlang.ast.ast_statement import EpiDecl, AssignExpr, AssignTransition, StochasticAssignExpr
class Meta:
"""This replaces the Lark Meta class. We *could* use the Lark Meta class, but this isn't part of the
documented interface and may be subject to change."""
# pylint: disable=too-few-public-methods
def __init__(self, line, column):
"""Holds meta-information about where in the GEM source an operation lies."""
self.line = line
self.column = column
@v_args(inline=True)
class ASTValidator(Transformer):
"""Validates an AST using a grammar. May be used as a base class for optimisers"""
# pylint: disable=no-self-use,missing-docstring,undefined-variable,invalid-name,too-many-public-methods
def transform(self, tree):
"""Runs a tree transformer.
:param tree an instance of a string (serialized AST), ASTNode, or a Lark Tree object.
:return an ASTNode object."""
if isinstance(tree, str):
return super().transform(ast_parser.parse(tree))
elif isinstance(tree, ASTNode):
return super().transform(ast_parser.parse(serialize(tree)))
elif isinstance(tree, lark_tree):
return super().transform(tree)
else:
raise RuntimeError(
"AST is not an instance of 'str', 'ASTNode', or 'lark.Tree'")
def start(self, *stmts):
return GEMProgram(stmts)
def string(self, s):
return String(s[1:-1])
def idref(self, s):
return IdRef(str(s))
def number(self, n):
return Number(float(n))
def epidecl(self, symbol, config, arglist, block):
return EpiDecl(symbol.value, config, arglist, block)
def transitiondef(self, src, dest):
return TransitionDef(src, dest)
def block(self, *stmts):
return Block(stmts)
def assign(self, lhs, rhs):
return AssignExpr(lhs, rhs)
def assign_stochastic(self, lhs, rhs):
return StochasticAssignExpr(lhs, rhs)
def assign_transition(self, lhs, rhs):
return AssignTransition(lhs, rhs)
def expr(self, expr):
return expr
def call(self, symbol, arglist):
return Call(symbol, arglist)
def add(self, lhs, rhs):
return AddExpr(lhs, rhs)
def sub(self, lhs, rhs):
return SubExpr(lhs, rhs)
def mul(self, lhs, rhs):
return MulExpr(lhs, rhs)
def truediv(self, lhs, rhs):
return TruedivExpr(lhs, rhs)
def le(self, lhs, rhs):
return LeExpr(lhs, rhs)
def lt(self, lhs, rhs):
return LtExpr(lhs, rhs)
def ge(self, lhs, rhs):
return GeExpr(lhs, rhs)
def gt(self, lhs, rhs):
return GtExpr(lhs, rhs)
def eq(self, lhs, rhs):
return EqExpr(lhs, rhs)
def ne(self, lhs, rhs):
return NeExpr(lhs, rhs)
def neg(self, x):
return NegExpr(x)
def not_(self, x):
return Not_Expr(x)
def arglist(self, *args):
return ArgList(args)
def kwarg(self, lhs, rhs):
return KwArg(lhs, rhs)
def kwref(self, symbol):
return KwRef(symbol)
......@@ -9,7 +9,9 @@ simple_statement: assign_expr
| assign_sojourn_density
| expr
epidecl: "Epidemic" ["<" arglist ">"] CNAME "(" arglist ")" block
epidecl: "Epidemic" ["<" arglist ">"] CNAME "(" argdefs ")" block
argdefs: [argdef ("," argdef)*]
transitiondef: "[" idref "->" idref "]"
assign_expr: idref "=" expr
......@@ -67,6 +69,7 @@ kwarg: CNAME "=" expr
// Defs
idref: CNAME ("." CNAME)*
argdef: CNAME
string: ESCAPED_STRING
number: FLOAT | INT
......
......@@ -33,7 +33,6 @@ class DataAttacher(ASTWalker):
children.insert(0, AssignExpr(IdRef(k),
IdRef(f"gem_external['{k}']")))
ast_node.children = children
return ast_node
def onExit_AssignExpr(self, assign):
lhs = assign.children[0]
......
......@@ -24,7 +24,7 @@ import inspect
from lark import Lark, Transformer, v_args
from gem.gemlang.ast.ast_base import GEMProgram, Block, KwRef, Statement
from gem.gemlang.ast.ast_base import GEMProgram, Block, FArgList, FArg, KwRef, Statement
from gem.gemlang.ast.ast_statement import EpiDecl, AssignExpr, AssignTransition, StochasticAssignExpr
from gem.gemlang.ast.ast_expression import *
......@@ -49,7 +49,7 @@ class GEMParser(Transformer):
return arg[0]
def epidecl(self, args, meta):
if isinstance(args[0], ArgList):
if isinstance(args[0], ArgList): # Epidemic declaration has options
return EpiDecl(str(args[1]), args[0], args[2], args[3], meta=meta)
else:
return EpiDecl(str(args[0]), ArgList([]), args[1], args[2], meta=meta)
......@@ -74,10 +74,16 @@ class GEMParser(Transformer):
if len(ex) > 1:
raise ValueError("Number of arguments > 1 in expression")
return ex[0]
def argdef(self, args, meta):
return FArg(args[0], meta=meta)
def arglist(self, args, meta):
return ArgList(args, meta=meta)
def argdefs(self, args, meta):
return FArgList(args, meta=meta)
def kwarg(self, args, meta):
kwref = KwRef(args[0])
expr = args[1]
......
......@@ -9,7 +9,7 @@ __all__ = ['Symbol', 'GlobalScope', 'LocalScope', 'ExternalDataSymbol',
'RandomVariableSymbol',
'PlaceholderSymbol', 'BuiltInFunctionSymbol',
'ProbabilityDistributionSymbol',
'BuiltinProbabilityDistributionSymbol', 'STMSymbol', 'Scope',
'BuiltinProbabilityDistributionSymbol', 'FArgSymbol', 'STMSymbol', 'Scope',
'ScopedSymbol']
_anon_scope_count = 0
......@@ -28,18 +28,11 @@ class Scope:
"""
def __init__(self, name=None, enclosing_scope=None):
self._symbols = OrderedDict()
if name is None:
global _anon_scope_count
self.name = "LOCAL_{num}".format(num=_anon_scope_count)
_anon_scope_count += 1
else:
self.name = name
assert enclosing_scope is None or isinstance(enclosing_scope, Scope)
self.name = str(name)
self.enclosing_scope = enclosing_scope
self.indent = 0
if enclosing_scope is not None:
self.indent = enclosing_scope.indent + 4
self._symbols = OrderedDict()
self.level = enclosing_scope.level + 1 if enclosing_scope is not None else 0
self._init_builtins()
def _init_builtins(self):
......@@ -49,7 +42,7 @@ class Scope:
symtab_header = "{} scope symbol table".format(self.__class__)
lines = [symtab_header]
lines.extend(
('%s%s: %r' % (" " * self.indent, key, value))
('%s%s: %r' % (" " * self.level, key, value))
for key, value in self._symbols.items()
)
s = '\n'.join(lines)
......@@ -57,6 +50,9 @@ class Scope:
__repr__ = __str__
def __len__(self):
return len(self._symbols)
def is_global(self):
"""Returns true if this is a global scope (i.e. no enclosing scope)."""
return self.enclosing_scope is None
......@@ -114,30 +110,30 @@ class GlobalScope(Scope):
super().__init__(name='global')
def _init_builtins(self):
self.declare(
BuiltinProbabilityDistributionSymbol('Gamma', ['shape', 'scale'],
enclosing_scope=self))
self.declare(
BuiltinProbabilityDistributionSymbol('Normal', ['mean', 'stddev'],
enclosing_scope=self))
self.declare(make_builtin_symbol(BuiltinProbabilityDistributionSymbol, 'Gamma', ['shape', 'scale'],
enclosing_scope=self))
self.declare(make_builtin_symbol(BuiltinProbabilityDistributionSymbol, 'Normal', ['mean', 'stddev'],
enclosing_scope=self))
self.declare(PlaceholderSymbol('Scalar', [], enclosing_scope=self))
self.declare(PlaceholderSymbol('Vector', [], enclosing_scope=self))
self.declare(PlaceholderSymbol('Matrix', [], enclosing_scope=self))
self.declare(PlaceholderSymbol('NDArray', [], enclosing_scope=self))
self.declare(
BuiltInFunctionSymbol('exp', ['val'], enclosing_scope=self))
self.declare(make_builtin_symbol(BuiltInFunctionSymbol, 'exp', ['val'], enclosing_scope=self))
class LocalScope(Scope):
"""A local scope
:param name: the scope name
:param ast_node: the AST node representing a scoped object
:param enclosing_scope: the parent scope
"""
def __init__(self, name=None, ast_node=None, enclosing_scope=None):
__local_scope_count = 0
def __init__(self, ast_node=None, enclosing_scope=None):
assert enclosing_scope is not None
name = f"_{self.__local_scope_count}"
self.__local_scope_count += 1
super().__init__(name, enclosing_scope)
self.definition = ast_node
......@@ -150,7 +146,7 @@ class Symbol:
:returns: an object of type :class:`Symbol`
"""
def __init__(self, name, definition):
self.name = name
self.name = str(name)
self.definition = definition
def __str__(self):
......@@ -226,6 +222,11 @@ class ScopedSymbol(Scope, Symbol):
super().__init__(name, enclosing_scope=enclosing_scope)
class FArgSymbol(Symbol):
def __init__(self, name, definition):
super().__init__(name, definition)
class FunctionSymbol(ScopedSymbol):
"""Represents a function scope.
......@@ -233,23 +234,18 @@ class FunctionSymbol(ScopedSymbol):
:param definition: ASTNode where the function is defined.
:param enclosing_scope: the enclosing scope
"""
def __init__(self, name, definition, argnames, enclosing_scope=None):
def __init__(self, name, definition, enclosing_scope=None):
assert enclosing_scope is not None
assert isinstance(definition, (ASTNode,
str)), f"definition={definition} is of type {type(definition)}"
assert hasattr(argnames,
'__iter__'), f"argnames {type(argnames)} is not iterable"
super().__init__(name, definition, enclosing_scope=enclosing_scope)
self.__argcount = len(argnames)
for argname in argnames:
arg_symb = VariableSymbol(argname, 'builtin')
self.declare(arg_symb)
self.__params = Scope()
@property
def argcount(self):
"""Returns the number of required arguments"""
return self.__argcount
symbols = [k for k in self._symbols.keys() if k[0] != '_']
return len(symbols)
class PlaceholderSymbol(FunctionSymbol):
......@@ -261,7 +257,7 @@ class PlaceholderSymbol(FunctionSymbol):
"""
def __init__(self, name, argnames, enclosing_scope=None):
super().__init__(name, 'placeholder', argnames, enclosing_scope)
super().__init__(name, 'placeholder', enclosing_scope)
class BuiltInFunctionSymbol(FunctionSymbol):
......@@ -271,8 +267,8 @@ class BuiltInFunctionSymbol(FunctionSymbol):
:param enclosing scope: the enclosing scope.
"""
def __init__(self, name, argnames, enclosing_scope=None):
super().__init__(name, 'builtin_function', argnames, enclosing_scope)
def __init__(self, name, enclosing_scope=None):
super().__init__(name, 'builtin', enclosing_scope)
class ProbabilityDistributionSymbol(FunctionSymbol):
......@@ -280,23 +276,18 @@ class ProbabilityDistributionSymbol(FunctionSymbol):
:param name
:param enclosing scope: the enclosing scope
"""
def __init__(self, name, ast_ref, argnames, enclosing_scope=None):
super().__init__(name, ast_ref, argnames, enclosing_scope)
def __init__(self, name, ast_ref, enclosing_scope=None):
super().__init__(name, ast_ref, enclosing_scope)
class BuiltinProbabilityDistributionSymbol(ProbabilityDistributionSymbol):
"""Represents a Random Variable scope.
:param name
:param enclosing scope: the enclosing scope
"""
def __init__(self, name, argnames, enclosing_scope=None):
super().__init__(name, 'random_variable', argnames, enclosing_scope)
def __init__(self, name, enclosing_scope=None):
super().__init__(name, 'builtin', enclosing_scope)
class STMSymbol(ProbabilityDistributionSymbol):
......@@ -307,14 +298,26 @@ class STMSymbol(ProbabilityDistributionSymbol):
:param enclosing_scope: the enclosing scope.
"""
def __init__(self, name, ast_ref, argnames, enclosing_scope=None):
def __init__(self, name, ast_ref, enclosing_scope=None):
assert enclosing_scope is not None
super().__init__(str(name), ast_ref, argnames, enclosing_scope)
super().__init__(str(name), ast_ref, enclosing_scope)
def _init_builtins(self):
state = BuiltInFunctionSymbol('State', ['init'],
enclosing_scope=self) # State only allowable in an STM
self.declare(state)
self.declare(make_builtin_symbol(BuiltInFunctionSymbol, 'State', ['init'],
enclosing_scope=self)) # State only allowable in an STM
self.declare(VariableSymbol('class', self.definition))
self.declare(VariableSymbol('time_origin', self.definition))
self.declare(VariableSymbol('time_step', self.definition))
@property
def argcount(self):
return len(self.get_by_type(FArgSymbol))
def make_builtin_symbol(symbol_type, name, args, enclosing_scope):
symbol = symbol_type(name=name, enclosing_scope=enclosing_scope)
for arg in args:
symbol.declare(VariableSymbol(arg, 'builtin'))
#symbol.declare(LocalScope(enclosing_scope=symbol))
return symbol
......@@ -71,7 +71,6 @@ class SymbolDeclarer(SemanticAnalysis):
def onEnter_EpiDecl(self, epidecl):
sym = STMSymbol(name=epidecl.name,
ast_ref=epidecl,
argnames=[arg.children[0].value for arg in epidecl.children[1].children],
enclosing_scope=self.current_scope)
self.current_scope.declare(sym)
self._push_scope(sym)
......@@ -80,6 +79,10 @@ class SymbolDeclarer(SemanticAnalysis):
def onExit_EpiDecl(self, epidecl):
self._pop_scope()
def onExit_FArg(self, argdef):
sym = FArgSymbol(name=argdef.value, definition=argdef)
self.current_scope.declare(sym)
def onEnter_Block(self, block):
scope = LocalScope(ast_node=block, enclosing_scope=self.current_scope)
block.symbol = scope
......@@ -170,6 +173,7 @@ class SymbolResolver(SemanticAnalysis):
try:
sym = self.current_scope.resolve(ast_node.value)
except NameError as e:
print(self.current_scope)
raise NameError(
"{} at line {} column {}".format(e, ast_node.meta.line,
ast_node.meta.column))
......@@ -201,4 +205,3 @@ def get_random_vars(symtab):
else:
free_rvs.append(symbol.name)
return tuple(free_rvs)
......@@ -21,11 +21,11 @@
from lark import v_args
from gem.gemlang import ASTValidator
from lark import Transformer
@v_args(inline=True)
class Expr2Infix(ASTValidator):
class Expr2Infix(Transformer):
def idref(self, s):
return str(s)
......
......@@ -25,26 +25,11 @@ from lark import Lark
from gem import gemlang
from gem.gemlang.ast.utils import serialize
from gem.gemlang.parse_gemlang import GEMParser
from gem.gemlang.ast.parsetree2ast import ASTValidator
from gem.gemlang.util import expr2infix
from gem.gemlang.ast_walker import ASTWalker
class TestASTTransformer(unittest.TestCase):
def setUp(self):
glpath = os.path.dirname(inspect.getfile(gemlang))
with open(os.path.join(glpath, 'ast/ast_grammar.cfgr'), 'r') as f:
grammar = f.read()
self.parser = Lark(grammar, propagate_positions=True)
def test_parse_serialize(self):
prog = """(GEMProgram (AssignExpr (IdRef popsize) (Number 100.0)) (StochasticAssignExpr (IdRef epsilon) (Call (IdRef Gamma) (ArgList (Number 1.0) (Number 1.0)))) (StochasticAssignExpr (IdRef beta) (Call (IdRef Gamma) (ArgList (Number 1.0) (Number 1.0)))) (StochasticAssignExpr (IdRef gamma) (Call (IdRef Gamma) (ArgList (Number 1.0) (Number 0.1)))) (StochasticAssignExpr (IdRef I0) (Call (IdRef Multinomial) (ArgList (KwArg (KwRef n) (Number 1.0)) (KwArg (KwRef size) (IdRef popsize))))) (EpiDecl MyModel (ArgList (KwArg (KwRef type) (String "ILM")) (KwArg (KwRef time_origin) (Number 0.0)) (KwArg (KwRef time_step) (Number 1.0))) (ArgList (KwArg (KwRef lemon) (IdRef yellow))) (Block (AssignExpr (IdRef S) (Call (IdRef State) (ArgList (KwArg (KwRef init) (SubExpr (Number 1.0) (IdRef I0)))))) (AssignExpr (IdRef I) (Call (IdRef State) (ArgList (KwArg (KwRef init) (IdRef I0))))) (AssignExpr (IdRef R) (Call (IdRef State) (ArgList (KwArg (KwRef init) (Number 0.0))))) (AssignExpr (IdRef siRate) (AddExpr (IdRef epsilon) (MulExpr (IdRef beta) (Call (IdRef sizeof) (ArgList (IdRef I)))))) (AssignTransition (TransitionDef (IdRef S) (IdRef I)) (Call (IdRef Markov) (ArgList (IdRef siRate)))) (AssignTransition (TransitionDef (IdRef I) (IdRef R)) (Call (IdRef Gamma) (ArgList (Number 2.0) (IdRef gamma)))))) (AssignExpr (IdRef x) (Call (IdRef MyModel) (ArgList))))"""
ast_tx = ASTValidator()
ast = ast_tx.transform(prog)
self.assertEqual(prog, serialize(ast))
@unittest.skip("Known bug in expr2infix")
class TestExpr2Infix(unittest.TestCase):
def setUp(self):
......@@ -62,12 +47,10 @@ class TestExpr2Infix(unittest.TestCase):
gem_tree = self.gem_lp.parse(prog)
ast = GEMParser().transform(gem_tree)
ast_tree = self.ast_lp.parse(serialize(ast))
ast2 = ASTValidator().transform(ast_tree)
infix = expr2infix(ast_tree)
self.assertEqual("(((beta * sizeof(S)) * sizeof(I)) + (3.0 / (3.0 - 1.0)))",
infix)
class TestASTVisitor(unittest.TestCase):
def setUp(self):
......
......@@ -134,7 +134,7 @@ class TestDeclParse(unittest.TestCase):
def test_epidecl(self):
stmt = """
Epidemic<type="ILM", time_origin=0., time_step=1.>
myModel (initial_population_size=popsize) {
myModel (initial_population_size) {
S = State(init=!I0)
I = State(init=I0)
......@@ -145,6 +145,7 @@ class TestDeclParse(unittest.TestCase):
[S -> I] = siRate
[I -> R] = gamma
}
epi ~ myModel(1000)
"""
tree = self.parser.parse(stmt)
......@@ -152,7 +153,7 @@ class TestDeclParse(unittest.TestCase):
ast = sp.transform(tree)
self.maxDiff = None
self.assertEqual(
"""(GEMProgram (EpiDecl myModel (ArgList (KwArg (KwRef type) (String "ILM")) (KwArg (KwRef time_origin) (Number 0.0)) (KwArg (KwRef time_step) (Number 1.0))) (ArgList (KwArg (KwRef initial_population_size) (IdRef popsize))) (Block (AssignExpr (IdRef S) (Call (IdRef State) (ArgList (KwArg (KwRef init) (Not_Expr (IdRef I0)))))) (AssignExpr (IdRef I) (Call (IdRef State) (ArgList (KwArg (KwRef init) (IdRef I0))))) (AssignExpr (IdRef R) (Call (IdRef State) (ArgList (KwArg (KwRef init) (Number 0.0))))) (AssignExpr (IdRef siRate) (AddExpr (IdRef epsilon) (MulExpr (IdRef beta) (Call (IdRef sizeof) (ArgList (IdRef I)))))) (AssignTransition (TransitionDef (IdRef S) (IdRef I)) (IdRef siRate)) (AssignTransition (TransitionDef (IdRef I) (IdRef R)) (IdRef gamma)))))""",
"""(GEMProgram (EpiDecl myModel (ArgList (KwArg (KwRef type) (String "ILM")) (KwArg (KwRef time_origin) (Number 0.0)) (KwArg (KwRef time_step) (Number 1.0))) (FArgList (FArg initial_population_size)) (Block (AssignExpr (IdRef S) (Call (IdRef State) (ArgList (KwArg (KwRef init) (Not_Expr (IdRef I0)))))) (AssignExpr (IdRef I) (Call (IdRef State) (ArgList (KwArg (KwRef init) (IdRef I0))))) (AssignExpr (IdRef R) (Call (IdRef State) (ArgList (KwArg (KwRef init) (Number 0.0))))) (AssignExpr (IdRef siRate) (AddExpr (IdRef epsilon) (MulExpr (IdRef beta) (Call (IdRef sizeof) (ArgList (IdRef I)))))) (AssignTransition (TransitionDef (IdRef S) (IdRef I)) (IdRef siRate)) (AssignTransition (TransitionDef (IdRef I) (IdRef R)) (IdRef gamma)))) (StochasticAssignExpr (IdRef epi) (Call (IdRef myModel) (ArgList (Number 1000.0)))))""",
serialize(ast))
......@@ -172,7 +173,7 @@ class TestGEMLang(unittest.TestCase):
I0 ~ Multinomial(n=1, size=popsize)
Epidemic <type="ILM", time_origin=0., time_step=1.>
MyModel(lemon=yellow) {
MyModel(lemon) {
S = State(init=1-I0)
I = State(init=I0)
......@@ -190,7 +191,7 @@ class TestGEMLang(unittest.TestCase):