Compare commits
No commits in common. "111cb0f702f6c1d14aee411c2d062ae1c1d656c6" and "5d9ef0e276e092d2a8aeaed2bff0f1520022add0" have entirely different histories.
111cb0f702
...
5d9ef0e276
7
TODO.md
7
TODO.md
@ -11,8 +11,8 @@
|
||||
- Implement a FizzBuzz example
|
||||
- Also, check the codes for FIXME and TODO
|
||||
- Allocation is done using pointers for members, is this desired?
|
||||
- Functions don't seem to be a thing on typing level yet?
|
||||
- static_array and tuple should probably not be PrimitiveType3, but instead subclass AppliedType3?
|
||||
|
||||
- Merge in type classes
|
||||
|
||||
- test_bitwise_or_inv_type
|
||||
- test_bytes_index_out_of_bounds vs static trap(?)
|
||||
@ -27,6 +27,3 @@
|
||||
- Casting is not implemented except u32 which is special cased
|
||||
- Parser is putting stuff in ModuleDataBlock
|
||||
- Compiler should probably do that
|
||||
- ourlang.BinaryOp should probably always be a Type3ClassMethod
|
||||
- Remove U32_OPERATOR_MAP / U64_OPERATOR_MAP
|
||||
- Make prelude more an actual thing
|
||||
|
||||
@ -6,7 +6,6 @@ It's intented to be a "any color, as long as it's black" kind of renderer
|
||||
from typing import Generator
|
||||
|
||||
from . import ourlang
|
||||
from .type3 import typeclasses as type3classes
|
||||
from .type3 import types as type3types
|
||||
from .type3.types import TYPE3_ASSERTION_ERROR, Type3, Type3OrPlaceholder
|
||||
|
||||
@ -104,12 +103,7 @@ def expression(inp: ourlang.Expression) -> str:
|
||||
return f'{inp.operator}{expression(inp.right)}'
|
||||
|
||||
if isinstance(inp, ourlang.BinaryOp):
|
||||
if isinstance(inp.operator, type3classes.Type3ClassMethod):
|
||||
operator = inp.operator.name
|
||||
else:
|
||||
operator = inp.operator
|
||||
|
||||
return f'{expression(inp.left)} {operator} {expression(inp.right)}'
|
||||
return f'{expression(inp.left)} {inp.operator} {expression(inp.right)}'
|
||||
|
||||
if isinstance(inp, ourlang.FunctionCall):
|
||||
args = ', '.join(
|
||||
|
||||
@ -2,13 +2,12 @@
|
||||
This module contains the code to convert parsed Ourlang into WebAssembly code
|
||||
"""
|
||||
import struct
|
||||
from typing import Dict, List, Optional
|
||||
from typing import List, Optional
|
||||
|
||||
from . import codestyle, ourlang, wasm
|
||||
from .runtime import calculate_alloc_size, calculate_member_offset
|
||||
from .stdlib import alloc as stdlib_alloc
|
||||
from .stdlib import types as stdlib_types
|
||||
from .type3 import typeclasses as type3classes
|
||||
from .type3 import types as type3types
|
||||
from .wasmgenerator import Generator as WasmGenerator
|
||||
|
||||
@ -26,55 +25,6 @@ LOAD_STORE_TYPE_MAP = {
|
||||
'bytes': 'i32', # Bytes are passed around as pointers
|
||||
}
|
||||
|
||||
# For now this is nice & clean, but this will get messy quick
|
||||
# Especially once we get functions with polymorphying applied types
|
||||
INSTANCES = {
|
||||
type3classes.Eq.operators['==']: {
|
||||
'a=u8': stdlib_types.u8_eq_equals,
|
||||
'a=u32': stdlib_types.u32_eq_equals,
|
||||
'a=u64': stdlib_types.u64_eq_equals,
|
||||
'a=i8': stdlib_types.i8_eq_equals,
|
||||
'a=i32': stdlib_types.i32_eq_equals,
|
||||
'a=i64': stdlib_types.i64_eq_equals,
|
||||
'a=f32': stdlib_types.f32_eq_equals,
|
||||
'a=f64': stdlib_types.f64_eq_equals,
|
||||
},
|
||||
type3classes.Fractional.operators['/']: {
|
||||
'a=f32': stdlib_types.f32_fractional_div,
|
||||
'a=f64': stdlib_types.f64_fractional_div,
|
||||
},
|
||||
type3classes.Integral.methods['div']: {
|
||||
'a=u32': stdlib_types.u32_integral_div,
|
||||
'a=u64': stdlib_types.u64_integral_div,
|
||||
'a=i32': stdlib_types.i32_integral_div,
|
||||
'a=i64': stdlib_types.i64_integral_div,
|
||||
},
|
||||
type3classes.Num.operators['+']: {
|
||||
'a=u32': stdlib_types.u32_num_add,
|
||||
'a=u64': stdlib_types.u64_num_add,
|
||||
'a=i32': stdlib_types.i32_num_add,
|
||||
'a=i64': stdlib_types.i64_num_add,
|
||||
'a=f32': stdlib_types.f32_num_add,
|
||||
'a=f64': stdlib_types.f64_num_add,
|
||||
},
|
||||
type3classes.Num.operators['-']: {
|
||||
'a=u32': stdlib_types.u32_num_sub,
|
||||
'a=u64': stdlib_types.u64_num_sub,
|
||||
'a=i32': stdlib_types.i32_num_sub,
|
||||
'a=i64': stdlib_types.i64_num_sub,
|
||||
'a=f32': stdlib_types.f32_num_sub,
|
||||
'a=f64': stdlib_types.f64_num_sub,
|
||||
},
|
||||
type3classes.Num.operators['*']: {
|
||||
'a=u32': stdlib_types.u32_num_mul,
|
||||
'a=u64': stdlib_types.u64_num_mul,
|
||||
'a=i32': stdlib_types.i32_num_mul,
|
||||
'a=i64': stdlib_types.i64_num_mul,
|
||||
'a=f32': stdlib_types.f32_num_mul,
|
||||
'a=f64': stdlib_types.f64_num_mul,
|
||||
},
|
||||
}
|
||||
|
||||
def phasm_compile(inp: ourlang.Module) -> wasm.Module:
|
||||
"""
|
||||
Public method for compiling a parsed Phasm module into
|
||||
@ -94,11 +44,6 @@ def type3(inp: type3types.Type3OrPlaceholder) -> wasm.WasmType:
|
||||
if inp == type3types.none:
|
||||
return wasm.WasmTypeNone()
|
||||
|
||||
if inp == type3types.bool_:
|
||||
# WebAssembly stores booleans as i32
|
||||
# See e.g. f32.eq, which is [f32 f32] -> [i32]
|
||||
return wasm.WasmTypeInt32()
|
||||
|
||||
if inp == type3types.u8:
|
||||
# WebAssembly has only support for 32 and 64 bits
|
||||
# So we need to store more memory per byte
|
||||
@ -147,6 +92,14 @@ def type3(inp: type3types.Type3OrPlaceholder) -> wasm.WasmType:
|
||||
|
||||
raise NotImplementedError(type3, inp)
|
||||
|
||||
# Operators that work for i32, i64, f32, f64
|
||||
OPERATOR_MAP = {
|
||||
'+': 'add',
|
||||
'-': 'sub',
|
||||
'*': 'mul',
|
||||
'==': 'eq',
|
||||
}
|
||||
|
||||
U8_OPERATOR_MAP = {
|
||||
# Under the hood, this is an i32
|
||||
# Implementing Right Shift XOR, OR, AND is fine since the 3 remaining
|
||||
@ -167,6 +120,7 @@ U32_OPERATOR_MAP = {
|
||||
'^': 'xor',
|
||||
'|': 'or',
|
||||
'&': 'and',
|
||||
'/': 'div_u' # Division by zero is a trap and the program will panic
|
||||
}
|
||||
|
||||
U64_OPERATOR_MAP = {
|
||||
@ -179,6 +133,7 @@ U64_OPERATOR_MAP = {
|
||||
'^': 'xor',
|
||||
'|': 'or',
|
||||
'&': 'and',
|
||||
'/': 'div_u' # Division by zero is a trap and the program will panic
|
||||
}
|
||||
|
||||
I32_OPERATOR_MAP = {
|
||||
@ -186,6 +141,7 @@ I32_OPERATOR_MAP = {
|
||||
'>': 'gt_s',
|
||||
'<=': 'le_s',
|
||||
'>=': 'ge_s',
|
||||
'/': 'div_s' # Division by zero is a trap and the program will panic
|
||||
}
|
||||
|
||||
I64_OPERATOR_MAP = {
|
||||
@ -193,6 +149,15 @@ I64_OPERATOR_MAP = {
|
||||
'>': 'gt_s',
|
||||
'<=': 'le_s',
|
||||
'>=': 'ge_s',
|
||||
'/': 'div_s' # Division by zero is a trap and the program will panic
|
||||
}
|
||||
|
||||
F32_OPERATOR_MAP = {
|
||||
'/': 'div' # Division by zero is a trap and the program will panic
|
||||
}
|
||||
|
||||
F64_OPERATOR_MAP = {
|
||||
'/': 'div' # Division by zero is a trap and the program will panic
|
||||
}
|
||||
|
||||
def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) -> None:
|
||||
@ -338,30 +303,6 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
expression(wgn, inp.right)
|
||||
|
||||
assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
|
||||
|
||||
if isinstance(inp.operator, type3classes.Type3ClassMethod):
|
||||
type_var_map: Dict[type3classes.TypeVariable, type3types.Type3] = {}
|
||||
|
||||
for type_var, arg_expr in zip(inp.operator.signature, [inp.left, inp.right, inp]):
|
||||
if not isinstance(type_var, type3classes.TypeVariable):
|
||||
# Fixed type, not part of the lookup requirements
|
||||
continue
|
||||
|
||||
assert isinstance(arg_expr.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
|
||||
type_var_map[type_var] = arg_expr.type3
|
||||
|
||||
instance_key = ','.join(
|
||||
f'{k.letter}={v.name}'
|
||||
for k, v in type_var_map.items()
|
||||
)
|
||||
|
||||
instance = INSTANCES.get(inp.operator, {}).get(instance_key, None)
|
||||
if instance is not None:
|
||||
instance(wgn)
|
||||
return
|
||||
|
||||
raise NotImplementedError(inp.operator, instance_key)
|
||||
|
||||
# FIXME: Re-implement build-in operators
|
||||
# Maybe operator_annotation is the way to go
|
||||
# Maybe the older stuff below that is the way to go
|
||||
@ -374,27 +315,56 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
if operator_annotation == '(<) :: u64 -> u64 -> bool':
|
||||
wgn.add_statement('i64.lt_u')
|
||||
return
|
||||
if operator_annotation == '(==) :: u64 -> u64 -> bool':
|
||||
wgn.add_statement('i64.eq')
|
||||
return
|
||||
|
||||
if inp.type3 == type3types.u8:
|
||||
if operator := U8_OPERATOR_MAP.get(inp.operator, None):
|
||||
wgn.add_statement(f'i32.{operator}')
|
||||
return
|
||||
if inp.type3 == type3types.u32:
|
||||
if operator := OPERATOR_MAP.get(inp.operator, None):
|
||||
wgn.add_statement(f'i32.{operator}')
|
||||
return
|
||||
if operator := U32_OPERATOR_MAP.get(inp.operator, None):
|
||||
wgn.add_statement(f'i32.{operator}')
|
||||
return
|
||||
if inp.type3 == type3types.u64:
|
||||
if operator := OPERATOR_MAP.get(inp.operator, None):
|
||||
wgn.add_statement(f'i64.{operator}')
|
||||
return
|
||||
if operator := U64_OPERATOR_MAP.get(inp.operator, None):
|
||||
wgn.add_statement(f'i64.{operator}')
|
||||
return
|
||||
if inp.type3 == type3types.i32:
|
||||
if operator := OPERATOR_MAP.get(inp.operator, None):
|
||||
wgn.add_statement(f'i32.{operator}')
|
||||
return
|
||||
if operator := I32_OPERATOR_MAP.get(inp.operator, None):
|
||||
wgn.add_statement(f'i32.{operator}')
|
||||
return
|
||||
if inp.type3 == type3types.i64:
|
||||
if operator := OPERATOR_MAP.get(inp.operator, None):
|
||||
wgn.add_statement(f'i64.{operator}')
|
||||
return
|
||||
if operator := I64_OPERATOR_MAP.get(inp.operator, None):
|
||||
wgn.add_statement(f'i64.{operator}')
|
||||
return
|
||||
if inp.type3 == type3types.f32:
|
||||
if operator := OPERATOR_MAP.get(inp.operator, None):
|
||||
wgn.add_statement(f'f32.{operator}')
|
||||
return
|
||||
if operator := F32_OPERATOR_MAP.get(inp.operator, None):
|
||||
wgn.add_statement(f'f32.{operator}')
|
||||
return
|
||||
if inp.type3 == type3types.f64:
|
||||
if operator := OPERATOR_MAP.get(inp.operator, None):
|
||||
wgn.add_statement(f'f64.{operator}')
|
||||
return
|
||||
if operator := F64_OPERATOR_MAP.get(inp.operator, None):
|
||||
wgn.add_statement(f'f64.{operator}')
|
||||
return
|
||||
|
||||
raise NotImplementedError(expression, inp.operator, inp.left.type3, inp.right.type3, inp.type3)
|
||||
|
||||
@ -429,30 +399,6 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
for arg in inp.arguments:
|
||||
expression(wgn, arg)
|
||||
|
||||
if isinstance(inp.function, type3classes.Type3ClassMethod):
|
||||
# FIXME: Duplicate code with BinaryOp
|
||||
type_var_map = {}
|
||||
|
||||
for type_var, arg_expr in zip(inp.function.signature, inp.arguments + [inp]):
|
||||
if not isinstance(type_var, type3classes.TypeVariable):
|
||||
# Fixed type, not part of the lookup requirements
|
||||
continue
|
||||
|
||||
assert isinstance(arg_expr.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
|
||||
type_var_map[type_var] = arg_expr.type3
|
||||
|
||||
instance_key = ','.join(
|
||||
f'{k.letter}={v.name}'
|
||||
for k, v in type_var_map.items()
|
||||
)
|
||||
|
||||
instance = INSTANCES.get(inp.function, {}).get(instance_key, None)
|
||||
if instance is not None:
|
||||
instance(wgn)
|
||||
return
|
||||
|
||||
raise NotImplementedError(inp.function, instance_key)
|
||||
|
||||
wgn.add_statement('call', '${}'.format(inp.function.name))
|
||||
return
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@ from typing import Dict, Iterable, List, Optional, Union
|
||||
|
||||
from typing_extensions import Final
|
||||
|
||||
from .type3 import typeclasses as type3typeclasses
|
||||
from .type3 import types as type3types
|
||||
from .type3.types import PlaceholderForType, StructType3, Type3, Type3OrPlaceholder
|
||||
|
||||
@ -150,11 +149,11 @@ class BinaryOp(Expression):
|
||||
"""
|
||||
__slots__ = ('operator', 'left', 'right', )
|
||||
|
||||
operator: Union[str, type3typeclasses.Type3ClassMethod]
|
||||
operator: str
|
||||
left: Expression
|
||||
right: Expression
|
||||
|
||||
def __init__(self, operator: Union[str, type3typeclasses.Type3ClassMethod], left: Expression, right: Expression) -> None:
|
||||
def __init__(self, operator: str, left: Expression, right: Expression) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.operator = operator
|
||||
@ -170,10 +169,10 @@ class FunctionCall(Expression):
|
||||
"""
|
||||
__slots__ = ('function', 'arguments', )
|
||||
|
||||
function: Union['Function', type3typeclasses.Type3ClassMethod]
|
||||
function: 'Function'
|
||||
arguments: List[Expression]
|
||||
|
||||
def __init__(self, function: Union['Function', type3typeclasses.Type3ClassMethod]) -> None:
|
||||
def __init__(self, function: 'Function') -> None:
|
||||
super().__init__()
|
||||
|
||||
self.function = function
|
||||
|
||||
@ -32,22 +32,8 @@ from .ourlang import (
|
||||
UnaryOp,
|
||||
VariableReference,
|
||||
)
|
||||
from .type3 import typeclasses as type3typeclasses
|
||||
from .type3 import types as type3types
|
||||
|
||||
PRELUDE_OPERATORS = {
|
||||
**type3typeclasses.Eq.operators,
|
||||
**type3typeclasses.Fractional.operators,
|
||||
**type3typeclasses.Integral.operators,
|
||||
**type3typeclasses.Num.operators,
|
||||
}
|
||||
|
||||
PRELUDE_METHODS = {
|
||||
**type3typeclasses.Eq.methods,
|
||||
**type3typeclasses.Fractional.methods,
|
||||
**type3typeclasses.Integral.methods,
|
||||
**type3typeclasses.Num.methods,
|
||||
}
|
||||
|
||||
def phasm_parse(source: str) -> Module:
|
||||
"""
|
||||
@ -352,8 +338,6 @@ class OurVisitor:
|
||||
|
||||
def visit_Module_FunctionDef_expr(self, module: Module, function: Function, our_locals: OurLocals, node: ast.expr) -> Expression:
|
||||
if isinstance(node, ast.BinOp):
|
||||
operator: Union[str, type3typeclasses.Type3ClassMethod]
|
||||
|
||||
if isinstance(node.op, ast.Add):
|
||||
operator = '+'
|
||||
elif isinstance(node.op, ast.Sub):
|
||||
@ -375,9 +359,6 @@ class OurVisitor:
|
||||
else:
|
||||
raise NotImplementedError(f'Operator {node.op}')
|
||||
|
||||
if operator in PRELUDE_OPERATORS:
|
||||
operator = PRELUDE_OPERATORS[operator]
|
||||
|
||||
return BinaryOp(
|
||||
operator,
|
||||
self.visit_Module_FunctionDef_expr(module, function, our_locals, node.left),
|
||||
@ -410,9 +391,6 @@ class OurVisitor:
|
||||
else:
|
||||
raise NotImplementedError(f'Operator {node.ops}')
|
||||
|
||||
if operator in PRELUDE_OPERATORS:
|
||||
operator = PRELUDE_OPERATORS[operator]
|
||||
|
||||
return BinaryOp(
|
||||
operator,
|
||||
self.visit_Module_FunctionDef_expr(module, function, our_locals, node.left),
|
||||
@ -474,11 +452,7 @@ class OurVisitor:
|
||||
if not isinstance(node.func.ctx, ast.Load):
|
||||
_raise_static_error(node, 'Must be load context')
|
||||
|
||||
func: Union[Function, type3typeclasses.Type3ClassMethod]
|
||||
|
||||
if node.func.id in PRELUDE_METHODS:
|
||||
func = PRELUDE_METHODS[node.func.id]
|
||||
elif node.func.id in module.struct_definitions:
|
||||
if node.func.id in module.struct_definitions:
|
||||
struct_definition = module.struct_definitions[node.func.id]
|
||||
struct_constructor = StructConstructor(struct_definition.struct_type3)
|
||||
|
||||
@ -539,15 +513,13 @@ class OurVisitor:
|
||||
|
||||
func = module.functions[node.func.id]
|
||||
|
||||
exp_arg_count = len(func.posonlyargs) if isinstance(func, Function) else len(func.signature) - 1
|
||||
|
||||
if exp_arg_count != len(node.args):
|
||||
_raise_static_error(node, f'Function {node.func.id} requires {exp_arg_count} arguments but {len(node.args)} are given')
|
||||
if len(func.posonlyargs) != len(node.args):
|
||||
_raise_static_error(node, f'Function {node.func.id} requires {len(func.posonlyargs)} arguments but {len(node.args)} are given')
|
||||
|
||||
result = FunctionCall(func)
|
||||
result.arguments.extend(
|
||||
self.visit_Module_FunctionDef_expr(module, function, our_locals, arg_expr)
|
||||
for arg_expr in node.args
|
||||
for arg_expr, param in zip(node.args, func.posonlyargs)
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
@ -65,99 +65,3 @@ def __subscript_bytes__(g: Generator, adr: i32, ofs: i32) -> i32:
|
||||
g.return_()
|
||||
|
||||
return i32('return') # To satisfy mypy
|
||||
|
||||
def u8_eq_equals(g: Generator) -> None:
|
||||
g.add_statement('i32.eq')
|
||||
|
||||
def u32_eq_equals(g: Generator) -> None:
|
||||
g.add_statement('i32.eq')
|
||||
|
||||
def u64_eq_equals(g: Generator) -> None:
|
||||
g.add_statement('i64.eq')
|
||||
|
||||
def i8_eq_equals(g: Generator) -> None:
|
||||
g.add_statement('i32.eq')
|
||||
|
||||
def i32_eq_equals(g: Generator) -> None:
|
||||
g.add_statement('i32.eq')
|
||||
|
||||
def i64_eq_equals(g: Generator) -> None:
|
||||
g.add_statement('i64.eq')
|
||||
|
||||
def f32_eq_equals(g: Generator) -> None:
|
||||
g.add_statement('f32.eq')
|
||||
|
||||
def f64_eq_equals(g: Generator) -> None:
|
||||
g.add_statement('f64.eq')
|
||||
|
||||
def f32_fractional_div(g: Generator) -> None:
|
||||
g.add_statement('f32.div')
|
||||
|
||||
def f64_fractional_div(g: Generator) -> None:
|
||||
g.add_statement('f64.div')
|
||||
|
||||
def u32_integral_div(g: Generator) -> None:
|
||||
g.add_statement('i32.div_u')
|
||||
|
||||
def u64_integral_div(g: Generator) -> None:
|
||||
g.add_statement('i64.div_u')
|
||||
|
||||
def i32_integral_div(g: Generator) -> None:
|
||||
g.add_statement('i32.div_s')
|
||||
|
||||
def i64_integral_div(g: Generator) -> None:
|
||||
g.add_statement('i64.div_s')
|
||||
|
||||
def u32_num_add(g: Generator) -> None:
|
||||
g.add_statement('i32.add')
|
||||
|
||||
def u64_num_add(g: Generator) -> None:
|
||||
g.add_statement('i64.add')
|
||||
|
||||
def i32_num_add(g: Generator) -> None:
|
||||
g.add_statement('i32.add')
|
||||
|
||||
def i64_num_add(g: Generator) -> None:
|
||||
g.add_statement('i64.add')
|
||||
|
||||
def f32_num_add(g: Generator) -> None:
|
||||
g.add_statement('f32.add')
|
||||
|
||||
def f64_num_add(g: Generator) -> None:
|
||||
g.add_statement('f64.add')
|
||||
|
||||
def u32_num_sub(g: Generator) -> None:
|
||||
g.add_statement('i32.sub')
|
||||
|
||||
def u64_num_sub(g: Generator) -> None:
|
||||
g.add_statement('i64.sub')
|
||||
|
||||
def i32_num_sub(g: Generator) -> None:
|
||||
g.add_statement('i32.sub')
|
||||
|
||||
def i64_num_sub(g: Generator) -> None:
|
||||
g.add_statement('i64.sub')
|
||||
|
||||
def f32_num_sub(g: Generator) -> None:
|
||||
g.add_statement('f32.sub')
|
||||
|
||||
def f64_num_sub(g: Generator) -> None:
|
||||
g.add_statement('f64.sub')
|
||||
|
||||
def u32_num_mul(g: Generator) -> None:
|
||||
g.add_statement('i32.mul')
|
||||
|
||||
def u64_num_mul(g: Generator) -> None:
|
||||
g.add_statement('i64.mul')
|
||||
|
||||
def i32_num_mul(g: Generator) -> None:
|
||||
g.add_statement('i32.mul')
|
||||
|
||||
def i64_num_mul(g: Generator) -> None:
|
||||
g.add_statement('i64.mul')
|
||||
|
||||
def f32_num_mul(g: Generator) -> None:
|
||||
g.add_statement('f32.mul')
|
||||
|
||||
def f64_num_mul(g: Generator) -> None:
|
||||
g.add_statement('f64.mul')
|
||||
|
||||
@ -6,7 +6,7 @@ These need to be resolved before the program can be compiled.
|
||||
from typing import Dict, List, Optional, Tuple, Union
|
||||
|
||||
from .. import ourlang
|
||||
from . import typeclasses, types
|
||||
from . import types
|
||||
|
||||
|
||||
class Error:
|
||||
@ -288,21 +288,21 @@ class MustImplementTypeClassConstraint(ConstraintBase):
|
||||
"""
|
||||
__slots__ = ('type_class3', 'type3', )
|
||||
|
||||
type_class3: Union[str, typeclasses.Type3Class]
|
||||
type_class3: str
|
||||
type3: types.Type3OrPlaceholder
|
||||
|
||||
DATA = {
|
||||
'u8': {'BitWiseOperation', 'EqualComparison', 'StrictPartialOrder'},
|
||||
'u32': {'BitWiseOperation', 'EqualComparison', 'StrictPartialOrder'},
|
||||
'u64': {'BitWiseOperation', 'EqualComparison', 'StrictPartialOrder'},
|
||||
'i32': {'EqualComparison', 'StrictPartialOrder'},
|
||||
'i64': {'EqualComparison', 'StrictPartialOrder'},
|
||||
'u8': {'BitWiseOperation', 'BasicMathOperation', 'EqualComparison', 'StrictPartialOrder'},
|
||||
'u32': {'BitWiseOperation', 'BasicMathOperation', 'EqualComparison', 'StrictPartialOrder'},
|
||||
'u64': {'BitWiseOperation', 'BasicMathOperation', 'EqualComparison', 'StrictPartialOrder'},
|
||||
'i32': {'BasicMathOperation', 'EqualComparison', 'StrictPartialOrder'},
|
||||
'i64': {'BasicMathOperation', 'EqualComparison', 'StrictPartialOrder'},
|
||||
'bytes': {'Foldable', 'Sized'},
|
||||
'f32': {'Fractional', 'FloatingPoint'},
|
||||
'f64': {'Fractional', 'FloatingPoint'},
|
||||
'f32': {'BasicMathOperation', 'FloatingPoint'},
|
||||
'f64': {'BasicMathOperation', 'FloatingPoint'},
|
||||
}
|
||||
|
||||
def __init__(self, type_class3: Union[str, typeclasses.Type3Class], type3: types.Type3OrPlaceholder, comment: Optional[str] = None) -> None:
|
||||
def __init__(self, type_class3: str, type3: types.Type3OrPlaceholder, comment: Optional[str] = None) -> None:
|
||||
super().__init__(comment=comment)
|
||||
|
||||
self.type_class3 = type_class3
|
||||
@ -316,12 +316,8 @@ class MustImplementTypeClassConstraint(ConstraintBase):
|
||||
if isinstance(typ, types.PlaceholderForType):
|
||||
return RequireTypeSubstitutes()
|
||||
|
||||
if isinstance(self.type_class3, typeclasses.Type3Class):
|
||||
if self.type_class3 in typ.classes:
|
||||
return None
|
||||
else:
|
||||
if self.type_class3 in self.__class__.DATA.get(typ.name, set()):
|
||||
return None
|
||||
if self.type_class3 in self.__class__.DATA.get(typ.name, set()):
|
||||
return None
|
||||
|
||||
return Error(f'{typ.name} does not implement the {self.type_class3} type class')
|
||||
|
||||
@ -329,7 +325,7 @@ class MustImplementTypeClassConstraint(ConstraintBase):
|
||||
return (
|
||||
'{type3} derives {type_class3}',
|
||||
{
|
||||
'type_class3': str(self.type_class3),
|
||||
'type_class3': self.type_class3,
|
||||
'type3': self.type3,
|
||||
},
|
||||
)
|
||||
|
||||
@ -6,7 +6,6 @@ The constraints solver can then try to resolve all constraints.
|
||||
from typing import Generator, List
|
||||
|
||||
from .. import ourlang
|
||||
from . import typeclasses as type3typeclasses
|
||||
from . import types as type3types
|
||||
from .constraints import (
|
||||
CanBeSubscriptedConstraint,
|
||||
@ -66,37 +65,6 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
|
||||
raise NotImplementedError(expression, inp, inp.operator)
|
||||
|
||||
if isinstance(inp, ourlang.BinaryOp):
|
||||
if isinstance(inp.operator, type3typeclasses.Type3ClassMethod):
|
||||
type_var_map = {
|
||||
x: type3types.PlaceholderForType([])
|
||||
for x in inp.operator.signature
|
||||
if isinstance(x, type3typeclasses.TypeVariable)
|
||||
}
|
||||
|
||||
yield from expression(ctx, inp.left)
|
||||
yield from expression(ctx, inp.right)
|
||||
|
||||
for type_var in inp.operator.type3_class.args:
|
||||
assert type_var in type_var_map # When can this happen?
|
||||
|
||||
yield MustImplementTypeClassConstraint(
|
||||
inp.operator.type3_class,
|
||||
type_var_map[type_var],
|
||||
)
|
||||
|
||||
for sig_part, arg_expr in zip(inp.operator.signature, [inp.left, inp.right, inp]):
|
||||
if isinstance(sig_part, type3typeclasses.TypeVariable):
|
||||
yield SameTypeConstraint(type_var_map[sig_part], arg_expr.type3)
|
||||
continue
|
||||
|
||||
if isinstance(sig_part, type3typeclasses.TypeReference):
|
||||
# On key error: We probably have to a lot of work to do refactoring
|
||||
# the type lookups
|
||||
exp_type = type3types.LOOKUP_TABLE[sig_part.name]
|
||||
yield SameTypeConstraint(exp_type, arg_expr.type3)
|
||||
continue
|
||||
return
|
||||
|
||||
if inp.operator in ('|', '&', '^', ):
|
||||
yield from expression(ctx, inp.left)
|
||||
yield from expression(ctx, inp.right)
|
||||
@ -115,6 +83,15 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
|
||||
comment=f'({inp.operator}) :: a -> a -> a')
|
||||
return
|
||||
|
||||
if inp.operator in ('+', '-', '*', '/', ):
|
||||
yield from expression(ctx, inp.left)
|
||||
yield from expression(ctx, inp.right)
|
||||
|
||||
yield MustImplementTypeClassConstraint('BasicMathOperation', inp.left.type3)
|
||||
yield SameTypeConstraint(inp.left.type3, inp.right.type3, inp.type3,
|
||||
comment=f'({inp.operator}) :: a -> a -> a')
|
||||
return
|
||||
|
||||
if inp.operator == '==':
|
||||
yield from expression(ctx, inp.left)
|
||||
yield from expression(ctx, inp.right)
|
||||
@ -140,39 +117,6 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
|
||||
raise NotImplementedError(expression, inp)
|
||||
|
||||
if isinstance(inp, ourlang.FunctionCall):
|
||||
if isinstance(inp.function, type3typeclasses.Type3ClassMethod):
|
||||
# FIXME: Duplicate code with BinaryOp
|
||||
|
||||
type_var_map = {
|
||||
x: type3types.PlaceholderForType([])
|
||||
for x in inp.function.signature
|
||||
if isinstance(x, type3typeclasses.TypeVariable)
|
||||
}
|
||||
|
||||
for call_arg in inp.arguments:
|
||||
yield from expression(ctx, call_arg)
|
||||
|
||||
for type_var in inp.function.type3_class.args:
|
||||
assert type_var in type_var_map # When can this happen?
|
||||
|
||||
yield MustImplementTypeClassConstraint(
|
||||
inp.function.type3_class,
|
||||
type_var_map[type_var],
|
||||
)
|
||||
|
||||
for sig_part, arg_expr in zip(inp.function.signature, inp.arguments + [inp]):
|
||||
if isinstance(sig_part, type3typeclasses.TypeVariable):
|
||||
yield SameTypeConstraint(type_var_map[sig_part], arg_expr.type3)
|
||||
continue
|
||||
|
||||
if isinstance(sig_part, type3typeclasses.TypeReference):
|
||||
# On key error: We probably have to a lot of work to do refactoring
|
||||
# the type lookups
|
||||
exp_type = type3types.LOOKUP_TABLE[sig_part.name]
|
||||
yield SameTypeConstraint(exp_type, arg_expr.type3)
|
||||
continue
|
||||
return
|
||||
|
||||
yield SameTypeConstraint(inp.function.returns_type3, inp.type3,
|
||||
comment=f'The type of a function call to {inp.function.name} is the same as the type that the function returns')
|
||||
|
||||
|
||||
@ -1,103 +0,0 @@
|
||||
from typing import Any, Dict, Iterable, List, Mapping, Union
|
||||
|
||||
|
||||
class TypeVariable:
|
||||
__slots__ = ('letter', )
|
||||
|
||||
letter: str
|
||||
|
||||
def __init__(self, letter: str) -> None:
|
||||
assert len(letter) == 1, f'{letter} is not a valid type variable'
|
||||
self.letter = letter
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.letter)
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
if not isinstance(other, TypeVariable):
|
||||
raise NotImplementedError
|
||||
|
||||
return self.letter == other.letter
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'TypeVariable({repr(self.letter)})'
|
||||
|
||||
class TypeReference:
|
||||
__slots__ = ('name', )
|
||||
|
||||
name: str
|
||||
|
||||
def __init__(self, name: str) -> None:
|
||||
assert len(name) > 1, f'{name} is not a valid type reference'
|
||||
self.name = name
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.name)
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
if not isinstance(other, TypeReference):
|
||||
raise NotImplementedError
|
||||
|
||||
return self.name == other.name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'TypeReference({repr(self.name)})'
|
||||
|
||||
class Type3ClassMethod:
|
||||
__slots__ = ('type3_class', 'name', 'signature', )
|
||||
|
||||
type3_class: 'Type3Class'
|
||||
name: str
|
||||
signature: List[Union[TypeReference, TypeVariable]]
|
||||
|
||||
def __init__(self, type3_class: 'Type3Class', name: str, signature: str) -> None:
|
||||
self.type3_class = type3_class
|
||||
self.name = name
|
||||
self.signature = [
|
||||
TypeVariable(x) if len(x) == 1 else TypeReference(x)
|
||||
for x in signature.split(' -> ')
|
||||
]
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'Type3ClassMethod({repr(self.type3_class)}, {repr(self.name)}, {repr(self.signature)})'
|
||||
|
||||
class Type3Class:
|
||||
__slots__ = ('name', 'args', 'methods', 'operators', )
|
||||
|
||||
name: str
|
||||
args: List[TypeVariable]
|
||||
methods: Dict[str, Type3ClassMethod]
|
||||
operators: Dict[str, Type3ClassMethod]
|
||||
|
||||
def __init__(self, name: str, args: Iterable[str], methods: Mapping[str, str], operators: Mapping[str, str]) -> None:
|
||||
self.name = name
|
||||
self.args = [TypeVariable(x) for x in args]
|
||||
self.methods = {
|
||||
k: Type3ClassMethod(self, k, v)
|
||||
for k, v in methods.items()
|
||||
}
|
||||
self.operators = {
|
||||
k: Type3ClassMethod(self, k, v)
|
||||
for k, v in operators.items()
|
||||
}
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return self.name
|
||||
|
||||
Eq = Type3Class('Eq', ['a'], methods={}, operators={
|
||||
'==': 'a -> a -> bool',
|
||||
})
|
||||
|
||||
Fractional = Type3Class('Fractional', ['a'], methods={}, operators={
|
||||
'/': 'a -> a -> a',
|
||||
})
|
||||
|
||||
Integral = Type3Class('Eq', ['a'], methods={
|
||||
'div': 'a -> a -> a',
|
||||
}, operators={})
|
||||
|
||||
Num = Type3Class('Num', ['a'], methods={}, operators={
|
||||
'+': 'a -> a -> a',
|
||||
'-': 'a -> a -> a',
|
||||
'*': 'a -> a -> a',
|
||||
})
|
||||
@ -6,8 +6,6 @@ constraint generator works with.
|
||||
"""
|
||||
from typing import Any, Dict, Iterable, List, Optional, Protocol, Union
|
||||
|
||||
from .typeclasses import Eq, Fractional, Integral, Num, Type3Class
|
||||
|
||||
TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before you can call any other method'
|
||||
|
||||
class ExpressionProtocol(Protocol):
|
||||
@ -24,24 +22,18 @@ class Type3:
|
||||
"""
|
||||
Base class for the type3 types
|
||||
"""
|
||||
__slots__ = ('name', 'classes', )
|
||||
__slots__ = ('name', )
|
||||
|
||||
name: str
|
||||
"""
|
||||
The name of the string, as parsed and outputted by codestyle.
|
||||
"""
|
||||
|
||||
classes: List[Type3Class]
|
||||
"""
|
||||
The type classes that this type implements
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, classes: Iterable[Type3Class]) -> None:
|
||||
def __init__(self, name: str) -> None:
|
||||
self.name = name
|
||||
self.classes = [*classes]
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'Type3({repr(self.name)}, {repr(self.classes)})'
|
||||
return f'Type3("{self.name}")'
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
@ -87,7 +79,7 @@ class IntType3(Type3):
|
||||
value: int
|
||||
|
||||
def __init__(self, value: int) -> None:
|
||||
super().__init__(str(value), [])
|
||||
super().__init__(str(value))
|
||||
|
||||
assert 0 <= value
|
||||
self.value = value
|
||||
@ -172,8 +164,7 @@ class AppliedType3(Type3):
|
||||
base.name
|
||||
+ ' ('
|
||||
+ ') ('.join(str(x) for x in args) # FIXME: Do we need to redo the name on substitution?
|
||||
+ ')',
|
||||
[]
|
||||
+ ')'
|
||||
)
|
||||
|
||||
self.base = base
|
||||
@ -222,7 +213,7 @@ class StructType3(Type3):
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, members: Dict[str, Type3]) -> None:
|
||||
super().__init__(name, [])
|
||||
super().__init__(name)
|
||||
|
||||
self.name = name
|
||||
self.members = dict(members)
|
||||
@ -230,40 +221,38 @@ class StructType3(Type3):
|
||||
def __repr__(self) -> str:
|
||||
return f'StructType3(repr({self.name}), repr({self.members}))'
|
||||
|
||||
none = PrimitiveType3('none', [])
|
||||
none = PrimitiveType3('none')
|
||||
"""
|
||||
The none type, for when functions simply don't return anything. e.g., IO().
|
||||
"""
|
||||
|
||||
bool_ = PrimitiveType3('bool', [])
|
||||
bool_ = PrimitiveType3('bool')
|
||||
"""
|
||||
The bool type, either True or False
|
||||
|
||||
Suffixes with an underscores, as it's a Python builtin
|
||||
"""
|
||||
|
||||
u8 = PrimitiveType3('u8', [Eq, Integral])
|
||||
u8 = PrimitiveType3('u8')
|
||||
"""
|
||||
The unsigned 8-bit integer type.
|
||||
|
||||
Operations on variables employ modular arithmetic, with modulus 2^8.
|
||||
"""
|
||||
|
||||
u32 = PrimitiveType3('u32', [Eq, Integral, Num])
|
||||
u32 = PrimitiveType3('u32')
|
||||
"""
|
||||
The unsigned 32-bit integer type.
|
||||
|
||||
Operations on variables employ modular arithmetic, with modulus 2^32.
|
||||
"""
|
||||
|
||||
u64 = PrimitiveType3('u64', [Eq, Integral, Num])
|
||||
u64 = PrimitiveType3('u64')
|
||||
"""
|
||||
The unsigned 64-bit integer type.
|
||||
|
||||
Operations on variables employ modular arithmetic, with modulus 2^64.
|
||||
"""
|
||||
|
||||
i8 = PrimitiveType3('i8', [Eq, Integral])
|
||||
i8 = PrimitiveType3('i8')
|
||||
"""
|
||||
The signed 8-bit integer type.
|
||||
|
||||
@ -271,7 +260,7 @@ Operations on variables employ modular arithmetic, with modulus 2^8, but
|
||||
with the middel point being 0.
|
||||
"""
|
||||
|
||||
i32 = PrimitiveType3('i32', [Eq, Integral, Num])
|
||||
i32 = PrimitiveType3('i32')
|
||||
"""
|
||||
The unsigned 32-bit integer type.
|
||||
|
||||
@ -279,7 +268,7 @@ Operations on variables employ modular arithmetic, with modulus 2^32, but
|
||||
with the middel point being 0.
|
||||
"""
|
||||
|
||||
i64 = PrimitiveType3('i64', [Eq, Integral, Num])
|
||||
i64 = PrimitiveType3('i64')
|
||||
"""
|
||||
The unsigned 64-bit integer type.
|
||||
|
||||
@ -287,22 +276,22 @@ Operations on variables employ modular arithmetic, with modulus 2^64, but
|
||||
with the middel point being 0.
|
||||
"""
|
||||
|
||||
f32 = PrimitiveType3('f32', [Eq, Fractional, Num])
|
||||
f32 = PrimitiveType3('f32')
|
||||
"""
|
||||
A 32-bits IEEE 754 float, of 32 bits width.
|
||||
"""
|
||||
|
||||
f64 = PrimitiveType3('f64', [Eq, Fractional, Num])
|
||||
f64 = PrimitiveType3('f64')
|
||||
"""
|
||||
A 32-bits IEEE 754 float, of 64 bits width.
|
||||
"""
|
||||
|
||||
bytes = PrimitiveType3('bytes', [])
|
||||
bytes = PrimitiveType3('bytes')
|
||||
"""
|
||||
This is a runtime-determined length piece of memory that can be indexed at runtime.
|
||||
"""
|
||||
|
||||
static_array = PrimitiveType3('static_array', [])
|
||||
static_array = PrimitiveType3('static_array')
|
||||
"""
|
||||
This is a fixed length piece of memory that can be indexed at runtime.
|
||||
|
||||
@ -310,7 +299,7 @@ It should be applied with one argument. It has a runtime-dynamic length
|
||||
of the same type repeated.
|
||||
"""
|
||||
|
||||
tuple = PrimitiveType3('tuple', []) # pylint: disable=W0622
|
||||
tuple = PrimitiveType3('tuple') # pylint: disable=W0622
|
||||
"""
|
||||
This is a fixed length piece of memory.
|
||||
|
||||
|
||||
@ -249,10 +249,6 @@ def _load_memory_stored_returned_value(
|
||||
if ret_type3 is type3types.none:
|
||||
return None
|
||||
|
||||
if ret_type3 is type3types.bool_:
|
||||
assert isinstance(wasm_value, int), wasm_value
|
||||
return 0 != wasm_value
|
||||
|
||||
if ret_type3 in (type3types.i32, type3types.i64):
|
||||
assert isinstance(wasm_value, int), wasm_value
|
||||
return wasm_value
|
||||
|
||||
@ -1,101 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from phasm.type3.entry import Type3Exception
|
||||
|
||||
from ..helpers import Suite
|
||||
|
||||
INT_TYPES = ['u8', 'u32', 'u64', 'i8', 'i32', 'i64']
|
||||
FLOAT_TYPES = ['f32', 'f64']
|
||||
|
||||
TYPE_MAP = {
|
||||
'u8': int,
|
||||
'u32': int,
|
||||
'u64': int,
|
||||
'i8': int,
|
||||
'i32': int,
|
||||
'i64': int,
|
||||
'f32': float,
|
||||
'f64': float,
|
||||
}
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_equals_not_implemented():
|
||||
code_py = """
|
||||
class Foo:
|
||||
val: i32
|
||||
|
||||
@exported
|
||||
def testEntry(x: Foo, y: Foo) -> Foo:
|
||||
return x + y
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='Foo does not implement the Num type class'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', INT_TYPES)
|
||||
def test_equals_int_same(type_):
|
||||
code_py = f"""
|
||||
CONSTANT0: {type_} = 10
|
||||
|
||||
CONSTANT1: {type_} = 10
|
||||
|
||||
@exported
|
||||
def testEntry() -> bool:
|
||||
return CONSTANT0 == CONSTANT1
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert True is result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', INT_TYPES)
|
||||
def test_equals_int_different(type_):
|
||||
code_py = f"""
|
||||
CONSTANT0: {type_} = 10
|
||||
|
||||
CONSTANT1: {type_} = 3
|
||||
|
||||
@exported
|
||||
def testEntry() -> bool:
|
||||
return CONSTANT0 == CONSTANT1
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert False is result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', FLOAT_TYPES)
|
||||
def test_equals_float_same(type_):
|
||||
code_py = f"""
|
||||
CONSTANT0: {type_} = 10.125
|
||||
|
||||
CONSTANT1: {type_} = 10.125
|
||||
|
||||
@exported
|
||||
def testEntry() -> bool:
|
||||
return CONSTANT0 == CONSTANT1
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert True is result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', FLOAT_TYPES)
|
||||
def test_equals_float_different(type_):
|
||||
code_py = f"""
|
||||
CONSTANT0: {type_} = 10.32
|
||||
|
||||
CONSTANT1: {type_} = 10.33
|
||||
|
||||
@exported
|
||||
def testEntry() -> bool:
|
||||
return CONSTANT0 == CONSTANT1
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert False is result.returned_value
|
||||
@ -10,7 +10,7 @@ def test_division_int(type_):
|
||||
code_py = f"""
|
||||
@exported
|
||||
def testEntry() -> {type_}:
|
||||
return div(10, 3)
|
||||
return 10 / 3
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
@ -24,7 +24,7 @@ def test_division_zero_let_it_crash_int(type_):
|
||||
code_py = f"""
|
||||
@exported
|
||||
def testEntry() -> {type_}:
|
||||
return div(10, 0)
|
||||
return 10 / 0
|
||||
"""
|
||||
|
||||
# WebAssembly dictates that integer division is a partial operator (e.g. unreachable for 0)
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import pytest
|
||||
|
||||
from phasm.type3.entry import Type3Exception
|
||||
|
||||
from ..helpers import Suite
|
||||
|
||||
INT_TYPES = ['u32', 'u64', 'i32', 'i64']
|
||||
@ -16,20 +14,6 @@ TYPE_MAP = {
|
||||
'f64': float,
|
||||
}
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_addition_not_implemented():
|
||||
code_py = """
|
||||
class Foo:
|
||||
val: i32
|
||||
|
||||
@exported
|
||||
def testEntry(x: Foo, y: Foo) -> Foo:
|
||||
return x + y
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='Foo does not implement the Num type class'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', INT_TYPES)
|
||||
def test_addition_int(type_):
|
||||
@ -87,56 +71,20 @@ def testEntry() -> {type_}:
|
||||
assert TYPE_MAP[type_] is type(result.returned_value)
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_subtraction_negative_result():
|
||||
code_py = """
|
||||
@pytest.mark.skip('TODO: Runtimes return a signed value, which is difficult to test')
|
||||
@pytest.mark.parametrize('type_', ('u32', 'u64')) # FIXME: u8
|
||||
def test_subtraction_underflow(type_):
|
||||
code_py = f"""
|
||||
@exported
|
||||
def testEntry() -> i32:
|
||||
def testEntry() -> {type_}:
|
||||
return 10 - 11
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert -1 == result.returned_value
|
||||
assert 0 < result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_subtraction_underflow():
|
||||
code_py = """
|
||||
@exported
|
||||
def testEntry() -> u32:
|
||||
return 10 - 11
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert 4294967295 == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', INT_TYPES)
|
||||
def test_multiplication_int(type_):
|
||||
code_py = f"""
|
||||
@exported
|
||||
def testEntry() -> {type_}:
|
||||
return 10 * 3
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert 30 == result.returned_value
|
||||
assert TYPE_MAP[type_] == type(result.returned_value)
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', FLOAT_TYPES)
|
||||
def test_multiplication_float(type_):
|
||||
code_py = f"""
|
||||
@exported
|
||||
def testEntry() -> {type_}:
|
||||
return 32.0 * 0.125
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert 4.0 == result.returned_value
|
||||
assert TYPE_MAP[type_] == type(result.returned_value)
|
||||
# TODO: Multiplication
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', INT_TYPES)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user