From a044e3ef0ce25d7cfe61204f81562f45ef951b4b Mon Sep 17 00:00:00 2001 From: "Johan B.W. de Vries" Date: Mon, 10 Apr 2023 15:24:56 +0200 Subject: [PATCH] Ideas for the IO Monad --- phasm/compiler.py | 14 ++++++++++++++ phasm/ourlang.py | 13 +++++++++++++ phasm/parser.py | 20 ++++++++++++++++++++ phasm/type3/constraintsgenerator.py | 10 ++++++++++ phasm/type3/types.py | 12 ++++++++++++ 5 files changed, 69 insertions(+) diff --git a/phasm/compiler.py b/phasm/compiler.py index c2bbad1..0fb65ea 100644 --- a/phasm/compiler.py +++ b/phasm/compiler.py @@ -84,6 +84,10 @@ def type3(inp: type3types.Type3OrPlaceholder) -> wasm.WasmType: # Tuples are passed as pointer, which are i32 return wasm.WasmTypeInt32() + if inp.base == type3types.IO: + assert 1 == len(inp.args) + return type3(inp.args[0]) + raise NotImplementedError(type3, inp) # Operators that work for i32, i64, f32, f64 @@ -557,6 +561,12 @@ def statement_if(wgn: WasmGenerator, inp: ourlang.StatementIf) -> None: # for stat in inp.else_statements: # statement(wgn, stat) +def statement_expression(wgn: WasmGenerator, inp: ourlang.StatementExpression) -> None: + """ + Compile: Expression whose result is ignored + """ + expression(wgn, inp.expr) + def statement(wgn: WasmGenerator, inp: ourlang.Statement) -> None: """ Compile: any statement @@ -572,6 +582,10 @@ def statement(wgn: WasmGenerator, inp: ourlang.Statement) -> None: if isinstance(inp, ourlang.StatementPass): return + if isinstance(inp, ourlang.StatementExpression): + statement_expression(wgn, inp) + return + raise NotImplementedError(statement, inp) def function_argument(inp: ourlang.FunctionParam) -> wasm.Param: diff --git a/phasm/ourlang.py b/phasm/ourlang.py index 01430b9..677ca64 100644 --- a/phasm/ourlang.py +++ b/phasm/ourlang.py @@ -253,6 +253,19 @@ class StatementIf(Statement): self.statements = [] self.else_statements = [] +class StatementExpression(Statement): + """ + An expression within a function + + The result of the expression is by the code. + """ + __slots__ = ('expr', ) + + expr: Expression + + def __init__(self, expr: Expression) -> None: + self.expr = expr + class FunctionParam: """ A parameter for a Function diff --git a/phasm/parser.py b/phasm/parser.py index 5ee9d56..4f054ef 100644 --- a/phasm/parser.py +++ b/phasm/parser.py @@ -27,6 +27,7 @@ from .ourlang import ( Statement, StatementIf, StatementPass, StatementReturn, + StatementExpression, FunctionParam, ModuleConstantDef, @@ -317,6 +318,9 @@ class OurVisitor: if isinstance(node, ast.Pass): return StatementPass() + if isinstance(node, ast.Expr): + return StatementExpression(self.visit_Module_FunctionDef_expr(module, function, our_locals, node.value)) + raise NotImplementedError(f'{node} as stmt in FunctionDef') def visit_Module_FunctionDef_expr(self, module: Module, function: Function, our_locals: OurLocals, node: ast.expr) -> Expression: @@ -561,6 +565,22 @@ class OurVisitor: raise NotImplementedError(f'{node.value} as constant') def visit_type(self, module: Module, node: ast.expr) -> type3types.Type3: + if isinstance(node, ast.Call): + if not isinstance(node.func, ast.Name): + _raise_static_error(node, 'Can only call Monads by name') + + if node.keywords: + _raise_static_error(node, 'Monads cannot have keyword arguments') + + monad_type = self.visit_type(module, node.func) + if not isinstance(monad_type, type3types.MonadType3): + _raise_static_error(node, 'Must be a Monad') + + return type3types.AppliedType3( + monad_type, + (self.visit_type(module, x) for x in node.args) + ) + if isinstance(node, ast.Constant): if node.value is None: return type3types.none diff --git a/phasm/type3/constraintsgenerator.py b/phasm/type3/constraintsgenerator.py index 61e7ec9..85a8cb9 100644 --- a/phasm/type3/constraintsgenerator.py +++ b/phasm/type3/constraintsgenerator.py @@ -183,6 +183,12 @@ def statement_if(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementIf) for stmt in inp.else_statements: yield from statement(ctx, fun, stmt) +def statement_expression(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementExpression) -> ConstraintGenerator: + yield from expression(ctx, inp.expr) + + yield SameTypeConstraint(fun.returns_type3, inp.expr.type3, + comment=f'TODO') + def statement(ctx: Context, fun: ourlang.Function, inp: ourlang.Statement) -> ConstraintGenerator: if isinstance(inp, ourlang.StatementReturn): yield from statement_return(ctx, fun, inp) @@ -192,6 +198,10 @@ def statement(ctx: Context, fun: ourlang.Function, inp: ourlang.Statement) -> Co yield from statement_if(ctx, fun, inp) return + if isinstance(inp, ourlang.StatementExpression): + yield from statement_expression(ctx, fun, inp) + return + raise NotImplementedError(statement, fun, inp) def function(ctx: Context, inp: ourlang.Function) -> ConstraintGenerator: diff --git a/phasm/type3/types.py b/phasm/type3/types.py index bd10abf..2b03173 100644 --- a/phasm/type3/types.py +++ b/phasm/type3/types.py @@ -69,6 +69,14 @@ class PrimitiveType3(Type3): __slots__ = () +class MonadType3(Type3): + """ + A type method to indicate that actions have to be made + one after the other, usually because they affect outside state. + """ + + __slots__ = () + class IntType3(Type3): """ Sometimes you can have an int as type, e.g. when using static arrays @@ -307,6 +315,8 @@ It should be applied with zero or more arguments. It has a compile time determined length, and each argument can be different. """ +IO = MonadType3('IO') + LOOKUP_TABLE: Dict[str, Type3] = { 'none': none, 'bool': bool_, @@ -319,4 +329,6 @@ LOOKUP_TABLE: Dict[str, Type3] = { 'f32': f32, 'f64': f64, 'bytes': bytes, + + 'IO': IO, }