Worked on floats
This commit is contained in:
parent
b2816164f9
commit
4b46483895
@ -154,6 +154,16 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
wgn.i64.const(inp.value)
|
wgn.i64.const(inp.value)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if stp == 'f32':
|
||||||
|
assert isinstance(inp.value, float)
|
||||||
|
wgn.f32.const(inp.value)
|
||||||
|
return
|
||||||
|
|
||||||
|
if stp == 'f64':
|
||||||
|
assert isinstance(inp.value, float)
|
||||||
|
wgn.f64.const(inp.value)
|
||||||
|
return
|
||||||
|
|
||||||
raise NotImplementedError(f'Constants with type {stp}')
|
raise NotImplementedError(f'Constants with type {stp}')
|
||||||
|
|
||||||
if isinstance(inp, ourlang.VariableReference):
|
if isinstance(inp, ourlang.VariableReference):
|
||||||
|
|||||||
@ -12,9 +12,7 @@ def constant(ctx: 'Context', inp: ourlang.Constant) -> 'TypeVar':
|
|||||||
if isinstance(inp, ourlang.ConstantPrimitive):
|
if isinstance(inp, ourlang.ConstantPrimitive):
|
||||||
result = ctx.new_var()
|
result = ctx.new_var()
|
||||||
|
|
||||||
if not isinstance(inp.value, int):
|
if isinstance(inp.value, int):
|
||||||
raise NotImplementedError('Float constants in new type system')
|
|
||||||
|
|
||||||
result.add_constraint(TypeConstraintPrimitive(TypeConstraintPrimitive.Primitive.INT))
|
result.add_constraint(TypeConstraintPrimitive(TypeConstraintPrimitive.Primitive.INT))
|
||||||
|
|
||||||
# Need at least this many bits to store this constant value
|
# Need at least this many bits to store this constant value
|
||||||
@ -29,6 +27,34 @@ def constant(ctx: 'Context', inp: ourlang.Constant) -> 'TypeVar':
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
if isinstance(inp.value, float):
|
||||||
|
result.add_constraint(TypeConstraintPrimitive(TypeConstraintPrimitive.Primitive.FLOAT))
|
||||||
|
|
||||||
|
# We don't have fancy logic here to detect if the float constant
|
||||||
|
# fits in the given type. There a number of edge cases to consider,
|
||||||
|
# before implementing this.
|
||||||
|
|
||||||
|
# 1) It may fit anyhow
|
||||||
|
# e.g., if the user has 3.14 as a float constant, neither a
|
||||||
|
# f32 nor a f64 can really fit this value. But does that mean
|
||||||
|
# we should throw an error?
|
||||||
|
|
||||||
|
# If we'd implement it, we'd want to convert it to hex using
|
||||||
|
# inp.value.hex(), which would give us the mantissa and exponent.
|
||||||
|
# We can use those to determine what bit size the value should be in.
|
||||||
|
|
||||||
|
# If that doesn't work out, we'd need another way to calculate the
|
||||||
|
# difference between what was written and what actually gets stored
|
||||||
|
# in memory, and warn if the difference is beyond a treshold.
|
||||||
|
|
||||||
|
result.add_location(str(inp.value))
|
||||||
|
|
||||||
|
inp.type_var = result
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
raise NotImplementedError(constant, inp, inp.value)
|
||||||
|
|
||||||
raise NotImplementedError(constant, inp)
|
raise NotImplementedError(constant, inp)
|
||||||
|
|
||||||
def expression(ctx: 'Context', inp: ourlang.Expression) -> 'TypeVar':
|
def expression(ctx: 'Context', inp: ourlang.Expression) -> 'TypeVar':
|
||||||
@ -39,6 +65,13 @@ def expression(ctx: 'Context', inp: ourlang.Expression) -> 'TypeVar':
|
|||||||
assert inp.variable.type_var is not None, inp
|
assert inp.variable.type_var is not None, inp
|
||||||
return inp.variable.type_var
|
return inp.variable.type_var
|
||||||
|
|
||||||
|
if isinstance(inp, ourlang.UnaryOp):
|
||||||
|
if inp.operator not in ('sqrt', ):
|
||||||
|
raise NotImplementedError(expression, inp, inp.operator)
|
||||||
|
|
||||||
|
right = expression(ctx, inp.right)
|
||||||
|
return right
|
||||||
|
|
||||||
if isinstance(inp, ourlang.BinaryOp):
|
if isinstance(inp, ourlang.BinaryOp):
|
||||||
if inp.operator not in ('+', '-', '*', '|', '&', '^'):
|
if inp.operator not in ('+', '-', '*', '|', '&', '^'):
|
||||||
raise NotImplementedError(expression, inp, inp.operator)
|
raise NotImplementedError(expression, inp, inp.operator)
|
||||||
@ -51,7 +84,7 @@ def expression(ctx: 'Context', inp: ourlang.Expression) -> 'TypeVar':
|
|||||||
if isinstance(inp, ourlang.FunctionCall):
|
if isinstance(inp, ourlang.FunctionCall):
|
||||||
assert inp.function.returns_type_var is not None
|
assert inp.function.returns_type_var is not None
|
||||||
if inp.function.posonlyargs:
|
if inp.function.posonlyargs:
|
||||||
raise NotImplementedError
|
raise NotImplementedError('TODO: Functions with arguments')
|
||||||
|
|
||||||
return inp.function.returns_type_var
|
return inp.function.returns_type_var
|
||||||
|
|
||||||
@ -123,4 +156,16 @@ def _convert_old_type(ctx: Context, inp: typing.TypeBase, location: str) -> Type
|
|||||||
result.add_location(location)
|
result.add_location(location)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
if isinstance(inp, typing.TypeFloat32):
|
||||||
|
result.add_constraint(TypeConstraintPrimitive(TypeConstraintPrimitive.Primitive.FLOAT))
|
||||||
|
result.add_constraint(TypeConstraintBitWidth(minb=32, maxb=32))
|
||||||
|
result.add_location(location)
|
||||||
|
return result
|
||||||
|
|
||||||
|
if isinstance(inp, typing.TypeFloat64):
|
||||||
|
result.add_constraint(TypeConstraintPrimitive(TypeConstraintPrimitive.Primitive.FLOAT))
|
||||||
|
result.add_constraint(TypeConstraintBitWidth(minb=64, maxb=64))
|
||||||
|
result.add_location(location)
|
||||||
|
return result
|
||||||
|
|
||||||
raise NotImplementedError(_convert_old_type, inp)
|
raise NotImplementedError(_convert_old_type, inp)
|
||||||
|
|||||||
@ -421,4 +421,15 @@ def simplify(inp: TypeVar) -> Optional[str]:
|
|||||||
base = 'i' if tc_sign.signed else 'u'
|
base = 'i' if tc_sign.signed else 'u'
|
||||||
return f'{base}{tc_bits.minb}'
|
return f'{base}{tc_bits.minb}'
|
||||||
|
|
||||||
|
if primitive is TypeConstraintPrimitive.Primitive.FLOAT:
|
||||||
|
if tc_bits is None or tc_sign is not None: # Floats should not hava sign contraint
|
||||||
|
return None
|
||||||
|
|
||||||
|
assert isinstance(tc_bits, TypeConstraintBitWidth) # type hint
|
||||||
|
|
||||||
|
if tc_bits.minb != tc_bits.maxb or tc_bits.minb not in (32, 64):
|
||||||
|
return None
|
||||||
|
|
||||||
|
return f'f{tc_bits.minb}'
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|||||||
@ -2,14 +2,12 @@ import pytest
|
|||||||
|
|
||||||
from .helpers import Suite
|
from .helpers import Suite
|
||||||
|
|
||||||
|
ALL_INT_TYPES = ['u8', 'u32', 'u64', 'i32', 'i64']
|
||||||
|
ALL_FLOAT_TYPES = ['f32', 'f64']
|
||||||
|
|
||||||
TYPE_MAP = {
|
TYPE_MAP = {
|
||||||
'u8': int,
|
**{x: int for x in ALL_INT_TYPES},
|
||||||
'u32': int,
|
**{x: float for x in ALL_FLOAT_TYPES},
|
||||||
'u64': int,
|
|
||||||
'i32': int,
|
|
||||||
'i64': int,
|
|
||||||
'f32': float,
|
|
||||||
'f64': float,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
COMPLETE_SIMPLE_TYPES = [
|
COMPLETE_SIMPLE_TYPES = [
|
||||||
@ -19,8 +17,8 @@ COMPLETE_SIMPLE_TYPES = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@pytest.mark.parametrize('type_', TYPE_MAP.keys())
|
@pytest.mark.parametrize('type_', ALL_INT_TYPES)
|
||||||
def test_return(type_):
|
def test_return_int(type_):
|
||||||
code_py = f"""
|
code_py = f"""
|
||||||
@exported
|
@exported
|
||||||
def testEntry() -> {type_}:
|
def testEntry() -> {type_}:
|
||||||
@ -33,8 +31,22 @@ def testEntry() -> {type_}:
|
|||||||
assert TYPE_MAP[type_] == type(result.returned_value)
|
assert TYPE_MAP[type_] == type(result.returned_value)
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@pytest.mark.parametrize('type_', COMPLETE_SIMPLE_TYPES)
|
@pytest.mark.parametrize('type_', ALL_FLOAT_TYPES)
|
||||||
def test_addition(type_):
|
def test_return_float(type_):
|
||||||
|
code_py = f"""
|
||||||
|
@exported
|
||||||
|
def testEntry() -> {type_}:
|
||||||
|
return 32.125
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = Suite(code_py).run_code()
|
||||||
|
|
||||||
|
assert 32.125 == result.returned_value
|
||||||
|
assert TYPE_MAP[type_] == type(result.returned_value)
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
@pytest.mark.parametrize('type_', ALL_INT_TYPES)
|
||||||
|
def test_addition_int(type_):
|
||||||
code_py = f"""
|
code_py = f"""
|
||||||
@exported
|
@exported
|
||||||
def testEntry() -> {type_}:
|
def testEntry() -> {type_}:
|
||||||
@ -47,8 +59,22 @@ def testEntry() -> {type_}:
|
|||||||
assert TYPE_MAP[type_] == type(result.returned_value)
|
assert TYPE_MAP[type_] == type(result.returned_value)
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@pytest.mark.parametrize('type_', COMPLETE_SIMPLE_TYPES)
|
@pytest.mark.parametrize('type_', ALL_FLOAT_TYPES)
|
||||||
def test_subtraction(type_):
|
def test_addition_float(type_):
|
||||||
|
code_py = f"""
|
||||||
|
@exported
|
||||||
|
def testEntry() -> {type_}:
|
||||||
|
return 32.0 + 0.125
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = Suite(code_py).run_code()
|
||||||
|
|
||||||
|
assert 32.125 == result.returned_value
|
||||||
|
assert TYPE_MAP[type_] == type(result.returned_value)
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
@pytest.mark.parametrize('type_', ALL_INT_TYPES)
|
||||||
|
def test_subtraction_int(type_):
|
||||||
code_py = f"""
|
code_py = f"""
|
||||||
@exported
|
@exported
|
||||||
def testEntry() -> {type_}:
|
def testEntry() -> {type_}:
|
||||||
@ -60,6 +86,20 @@ def testEntry() -> {type_}:
|
|||||||
assert 7 == result.returned_value
|
assert 7 == result.returned_value
|
||||||
assert TYPE_MAP[type_] == type(result.returned_value)
|
assert TYPE_MAP[type_] == type(result.returned_value)
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
@pytest.mark.parametrize('type_', ALL_FLOAT_TYPES)
|
||||||
|
def test_subtraction(type_):
|
||||||
|
code_py = f"""
|
||||||
|
@exported
|
||||||
|
def testEntry() -> {type_}:
|
||||||
|
return 100.0 - 67.875
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = Suite(code_py).run_code()
|
||||||
|
|
||||||
|
assert 32.125 == result.returned_value
|
||||||
|
assert TYPE_MAP[type_] == type(result.returned_value)
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@pytest.mark.parametrize('type_', ['u32', 'u64']) # FIXME: Support u8, requires an extra AND operation
|
@pytest.mark.parametrize('type_', ['u32', 'u64']) # FIXME: Support u8, requires an extra AND operation
|
||||||
def test_logical_left_shift(type_):
|
def test_logical_left_shift(type_):
|
||||||
@ -136,7 +176,7 @@ def test_buildins_sqrt(type_):
|
|||||||
code_py = f"""
|
code_py = f"""
|
||||||
@exported
|
@exported
|
||||||
def testEntry() -> {type_}:
|
def testEntry() -> {type_}:
|
||||||
return sqrt(25)
|
return sqrt(25.0)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
result = Suite(code_py).run_code()
|
result = Suite(code_py).run_code()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user