Converted fractional, fixed integral

This commit is contained in:
Johan B.W. de Vries 2023-11-16 16:08:26 +01:00
parent 4001b086db
commit be28450658
7 changed files with 122 additions and 49 deletions

View File

@ -39,6 +39,16 @@ INSTANCES = {
'a=f32': stdlib_types.f32_eq_equals, 'a=f32': stdlib_types.f32_eq_equals,
'a=f64': stdlib_types.f64_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['+']: { type3classes.Num.operators['+']: {
'a=u32': stdlib_types.u32_num_add, 'a=u32': stdlib_types.u32_num_add,
'a=u64': stdlib_types.u64_num_add, 'a=u64': stdlib_types.u64_num_add,
@ -157,7 +167,6 @@ U32_OPERATOR_MAP = {
'^': 'xor', '^': 'xor',
'|': 'or', '|': 'or',
'&': 'and', '&': 'and',
'/': 'div_u' # Division by zero is a trap and the program will panic
} }
U64_OPERATOR_MAP = { U64_OPERATOR_MAP = {
@ -170,7 +179,6 @@ U64_OPERATOR_MAP = {
'^': 'xor', '^': 'xor',
'|': 'or', '|': 'or',
'&': 'and', '&': 'and',
'/': 'div_u' # Division by zero is a trap and the program will panic
} }
I32_OPERATOR_MAP = { I32_OPERATOR_MAP = {
@ -178,7 +186,6 @@ I32_OPERATOR_MAP = {
'>': 'gt_s', '>': 'gt_s',
'<=': 'le_s', '<=': 'le_s',
'>=': 'ge_s', '>=': 'ge_s',
'/': 'div_s' # Division by zero is a trap and the program will panic
} }
I64_OPERATOR_MAP = { I64_OPERATOR_MAP = {
@ -186,15 +193,6 @@ I64_OPERATOR_MAP = {
'>': 'gt_s', '>': 'gt_s',
'<=': 'le_s', '<=': 'le_s',
'>=': 'ge_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: def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) -> None:
@ -397,14 +395,6 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
if operator := I64_OPERATOR_MAP.get(inp.operator, None): if operator := I64_OPERATOR_MAP.get(inp.operator, None):
wgn.add_statement(f'i64.{operator}') wgn.add_statement(f'i64.{operator}')
return return
if inp.type3 == type3types.f32:
if operator := F32_OPERATOR_MAP.get(inp.operator, None):
wgn.add_statement(f'f32.{operator}')
return
if inp.type3 == type3types.f64:
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) raise NotImplementedError(expression, inp.operator, inp.left.type3, inp.right.type3, inp.type3)
@ -439,6 +429,30 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
for arg in inp.arguments: for arg in inp.arguments:
expression(wgn, arg) 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)) wgn.add_statement('call', '${}'.format(inp.function.name))
return return

View File

@ -6,7 +6,7 @@ from typing import Dict, Iterable, List, Optional, Union
from typing_extensions import Final from typing_extensions import Final
from .type3 import typeclasses as type3classes from .type3 import typeclasses as type3typeclasses
from .type3 import types as type3types from .type3 import types as type3types
from .type3.types import PlaceholderForType, StructType3, Type3, Type3OrPlaceholder from .type3.types import PlaceholderForType, StructType3, Type3, Type3OrPlaceholder
@ -150,11 +150,11 @@ class BinaryOp(Expression):
""" """
__slots__ = ('operator', 'left', 'right', ) __slots__ = ('operator', 'left', 'right', )
operator: Union[str, type3classes.Type3ClassMethod] operator: Union[str, type3typeclasses.Type3ClassMethod]
left: Expression left: Expression
right: Expression right: Expression
def __init__(self, operator: Union[str, type3classes.Type3ClassMethod], left: Expression, right: Expression) -> None: def __init__(self, operator: Union[str, type3typeclasses.Type3ClassMethod], left: Expression, right: Expression) -> None:
super().__init__() super().__init__()
self.operator = operator self.operator = operator
@ -170,10 +170,10 @@ class FunctionCall(Expression):
""" """
__slots__ = ('function', 'arguments', ) __slots__ = ('function', 'arguments', )
function: 'Function' function: Union['Function', type3typeclasses.Type3ClassMethod]
arguments: List[Expression] arguments: List[Expression]
def __init__(self, function: 'Function') -> None: def __init__(self, function: Union['Function', type3typeclasses.Type3ClassMethod]) -> None:
super().__init__() super().__init__()
self.function = function self.function = function

View File

@ -37,9 +37,18 @@ from .type3 import types as type3types
PRELUDE_OPERATORS = { PRELUDE_OPERATORS = {
**type3typeclasses.Eq.operators, **type3typeclasses.Eq.operators,
**type3typeclasses.Fractional.operators,
**type3typeclasses.Integral.operators,
**type3typeclasses.Num.operators, **type3typeclasses.Num.operators,
} }
PRELUDE_METHODS = {
**type3typeclasses.Eq.methods,
**type3typeclasses.Fractional.methods,
**type3typeclasses.Integral.methods,
**type3typeclasses.Num.methods,
}
def phasm_parse(source: str) -> Module: def phasm_parse(source: str) -> Module:
""" """
Public method for parsing Phasm code into a Phasm Module Public method for parsing Phasm code into a Phasm Module
@ -465,7 +474,11 @@ class OurVisitor:
if not isinstance(node.func.ctx, ast.Load): if not isinstance(node.func.ctx, ast.Load):
_raise_static_error(node, 'Must be load context') _raise_static_error(node, 'Must be load context')
if node.func.id in module.struct_definitions: 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:
struct_definition = module.struct_definitions[node.func.id] struct_definition = module.struct_definitions[node.func.id]
struct_constructor = StructConstructor(struct_definition.struct_type3) struct_constructor = StructConstructor(struct_definition.struct_type3)
@ -526,13 +539,15 @@ class OurVisitor:
func = module.functions[node.func.id] func = module.functions[node.func.id]
if len(func.posonlyargs) != len(node.args): exp_arg_count = len(func.posonlyargs) if isinstance(func, Function) else len(func.signature) - 1
_raise_static_error(node, f'Function {node.func.id} requires {len(func.posonlyargs)} arguments but {len(node.args)} are given')
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')
result = FunctionCall(func) result = FunctionCall(func)
result.arguments.extend( result.arguments.extend(
self.visit_Module_FunctionDef_expr(module, function, our_locals, arg_expr) self.visit_Module_FunctionDef_expr(module, function, our_locals, arg_expr)
for arg_expr, param in zip(node.args, func.posonlyargs) for arg_expr in node.args
) )
return result return result

View File

@ -90,6 +90,24 @@ def f32_eq_equals(g: Generator) -> None:
def f64_eq_equals(g: Generator) -> None: def f64_eq_equals(g: Generator) -> None:
g.add_statement('f64.eq') 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: def u32_num_add(g: Generator) -> None:
g.add_statement('i32.add') g.add_statement('i32.add')

View File

@ -95,10 +95,8 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
exp_type = type3types.LOOKUP_TABLE[sig_part.name] exp_type = type3types.LOOKUP_TABLE[sig_part.name]
yield SameTypeConstraint(exp_type, arg_expr.type3) yield SameTypeConstraint(exp_type, arg_expr.type3)
continue continue
return return
if inp.operator in ('|', '&', '^', ): if inp.operator in ('|', '&', '^', ):
yield from expression(ctx, inp.left) yield from expression(ctx, inp.left)
yield from expression(ctx, inp.right) yield from expression(ctx, inp.right)
@ -117,15 +115,6 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
comment=f'({inp.operator}) :: a -> a -> a') comment=f'({inp.operator}) :: a -> a -> a')
return return
if inp.operator in ('/', ):
yield from expression(ctx, inp.left)
yield from expression(ctx, inp.right)
yield MustImplementTypeClassConstraint('Fractional', inp.left.type3)
yield SameTypeConstraint(inp.left.type3, inp.right.type3, inp.type3,
comment=f'({inp.operator}) :: a -> a -> a')
return
if inp.operator == '==': if inp.operator == '==':
yield from expression(ctx, inp.left) yield from expression(ctx, inp.left)
yield from expression(ctx, inp.right) yield from expression(ctx, inp.right)
@ -151,6 +140,39 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
raise NotImplementedError(expression, inp) raise NotImplementedError(expression, inp)
if isinstance(inp, ourlang.FunctionCall): 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, 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') comment=f'The type of a function call to {inp.function.name} is the same as the type that the function returns')

View File

@ -88,6 +88,10 @@ Eq = Type3Class('Eq', ['a'], methods={}, operators={
'==': 'a -> a -> bool', '==': 'a -> a -> bool',
}) })
Fractional = Type3Class('Fractional', ['a'], methods={}, operators={
'/': 'a -> a -> a',
})
Integral = Type3Class('Eq', ['a'], methods={ Integral = Type3Class('Eq', ['a'], methods={
'div': 'a -> a -> a', 'div': 'a -> a -> a',
}, operators={}) }, operators={})

View File

@ -6,7 +6,7 @@ constraint generator works with.
""" """
from typing import Any, Dict, Iterable, List, Optional, Protocol, Union from typing import Any, Dict, Iterable, List, Optional, Protocol, Union
from .typeclasses import Eq, Num, Type3Class 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' TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before you can call any other method'
@ -242,28 +242,28 @@ The bool type, either True or False
Suffixes with an underscores, as it's a Python builtin Suffixes with an underscores, as it's a Python builtin
""" """
u8 = PrimitiveType3('u8', [Eq]) u8 = PrimitiveType3('u8', [Eq, Integral])
""" """
The unsigned 8-bit integer type. The unsigned 8-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^8. Operations on variables employ modular arithmetic, with modulus 2^8.
""" """
u32 = PrimitiveType3('u32', [Eq, Num]) u32 = PrimitiveType3('u32', [Eq, Integral, Num])
""" """
The unsigned 32-bit integer type. The unsigned 32-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^32. Operations on variables employ modular arithmetic, with modulus 2^32.
""" """
u64 = PrimitiveType3('u64', [Eq, Num]) u64 = PrimitiveType3('u64', [Eq, Integral, Num])
""" """
The unsigned 64-bit integer type. The unsigned 64-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^64. Operations on variables employ modular arithmetic, with modulus 2^64.
""" """
i8 = PrimitiveType3('i8', [Eq]) i8 = PrimitiveType3('i8', [Eq, Integral])
""" """
The signed 8-bit integer type. The signed 8-bit integer type.
@ -271,7 +271,7 @@ Operations on variables employ modular arithmetic, with modulus 2^8, but
with the middel point being 0. with the middel point being 0.
""" """
i32 = PrimitiveType3('i32', [Eq, Num]) i32 = PrimitiveType3('i32', [Eq, Integral, Num])
""" """
The unsigned 32-bit integer type. The unsigned 32-bit integer type.
@ -279,7 +279,7 @@ Operations on variables employ modular arithmetic, with modulus 2^32, but
with the middel point being 0. with the middel point being 0.
""" """
i64 = PrimitiveType3('i64', [Eq, Num]) i64 = PrimitiveType3('i64', [Eq, Integral, Num])
""" """
The unsigned 64-bit integer type. The unsigned 64-bit integer type.
@ -287,12 +287,12 @@ Operations on variables employ modular arithmetic, with modulus 2^64, but
with the middel point being 0. with the middel point being 0.
""" """
f32 = PrimitiveType3('f32', [Eq, Num]) f32 = PrimitiveType3('f32', [Eq, Fractional, Num])
""" """
A 32-bits IEEE 754 float, of 32 bits width. A 32-bits IEEE 754 float, of 32 bits width.
""" """
f64 = PrimitiveType3('f64', [Eq, Num]) f64 = PrimitiveType3('f64', [Eq, Fractional, Num])
""" """
A 32-bits IEEE 754 float, of 64 bits width. A 32-bits IEEE 754 float, of 64 bits width.
""" """