MVP #1
@ -29,6 +29,12 @@ def type_(inp: typing.TypeBase) -> str:
|
|||||||
if isinstance(inp, typing.TypeUInt8):
|
if isinstance(inp, typing.TypeUInt8):
|
||||||
return 'u8'
|
return 'u8'
|
||||||
|
|
||||||
|
if isinstance(inp, typing.TypeUInt32):
|
||||||
|
return 'u32'
|
||||||
|
|
||||||
|
if isinstance(inp, typing.TypeUInt64):
|
||||||
|
return 'u64'
|
||||||
|
|
||||||
if isinstance(inp, typing.TypeInt32):
|
if isinstance(inp, typing.TypeInt32):
|
||||||
return 'i32'
|
return 'i32'
|
||||||
|
|
||||||
@ -71,7 +77,10 @@ def expression(inp: ourlang.Expression) -> str:
|
|||||||
"""
|
"""
|
||||||
Render: A Phasm expression
|
Render: A Phasm expression
|
||||||
"""
|
"""
|
||||||
if isinstance(inp, (ourlang.ConstantUInt8, ourlang.ConstantInt32, ourlang.ConstantInt64, )):
|
if isinstance(inp, (
|
||||||
|
ourlang.ConstantUInt8, ourlang.ConstantUInt32, ourlang.ConstantUInt64,
|
||||||
|
ourlang.ConstantInt32, ourlang.ConstantInt64,
|
||||||
|
)):
|
||||||
return str(inp.value)
|
return str(inp.value)
|
||||||
|
|
||||||
if isinstance(inp, (ourlang.ConstantFloat32, ourlang.ConstantFloat64, )):
|
if isinstance(inp, (ourlang.ConstantFloat32, ourlang.ConstantFloat64, )):
|
||||||
|
|||||||
@ -11,6 +11,8 @@ Statements = Generator[wasm.Statement, None, None]
|
|||||||
|
|
||||||
LOAD_STORE_TYPE_MAP = {
|
LOAD_STORE_TYPE_MAP = {
|
||||||
typing.TypeUInt8: 'i32',
|
typing.TypeUInt8: 'i32',
|
||||||
|
typing.TypeUInt32: 'i32',
|
||||||
|
typing.TypeUInt64: 'i64',
|
||||||
typing.TypeInt32: 'i32',
|
typing.TypeInt32: 'i32',
|
||||||
typing.TypeInt64: 'i64',
|
typing.TypeInt64: 'i64',
|
||||||
typing.TypeFloat32: 'f32',
|
typing.TypeFloat32: 'f32',
|
||||||
@ -39,6 +41,12 @@ def type_(inp: typing.TypeBase) -> wasm.WasmType:
|
|||||||
# So we need to store more memory per byte
|
# So we need to store more memory per byte
|
||||||
return wasm.WasmTypeInt32()
|
return wasm.WasmTypeInt32()
|
||||||
|
|
||||||
|
if isinstance(inp, typing.TypeUInt32):
|
||||||
|
return wasm.WasmTypeInt32()
|
||||||
|
|
||||||
|
if isinstance(inp, typing.TypeUInt64):
|
||||||
|
return wasm.WasmTypeInt64()
|
||||||
|
|
||||||
if isinstance(inp, typing.TypeInt32):
|
if isinstance(inp, typing.TypeInt32):
|
||||||
return wasm.WasmTypeInt32()
|
return wasm.WasmTypeInt32()
|
||||||
|
|
||||||
@ -66,14 +74,28 @@ OPERATOR_MAP = {
|
|||||||
'==': 'eq',
|
'==': 'eq',
|
||||||
}
|
}
|
||||||
|
|
||||||
I32_OPERATOR_MAP = { # TODO: Introduce UInt32 type
|
U32_OPERATOR_MAP = {
|
||||||
|
'<': 'lt_u',
|
||||||
|
'>': 'gt_u',
|
||||||
|
'<=': 'le_u',
|
||||||
|
'>=': 'ge_u',
|
||||||
|
}
|
||||||
|
|
||||||
|
U64_OPERATOR_MAP = {
|
||||||
|
'<': 'lt_u',
|
||||||
|
'>': 'gt_u',
|
||||||
|
'<=': 'le_u',
|
||||||
|
'>=': 'ge_u',
|
||||||
|
}
|
||||||
|
|
||||||
|
I32_OPERATOR_MAP = {
|
||||||
'<': 'lt_s',
|
'<': 'lt_s',
|
||||||
'>': 'gt_s',
|
'>': 'gt_s',
|
||||||
'<=': 'le_s',
|
'<=': 'le_s',
|
||||||
'>=': 'ge_s',
|
'>=': 'ge_s',
|
||||||
}
|
}
|
||||||
|
|
||||||
I64_OPERATOR_MAP = { # TODO: Introduce UInt32 type
|
I64_OPERATOR_MAP = {
|
||||||
'<': 'lt_s',
|
'<': 'lt_s',
|
||||||
'>': 'gt_s',
|
'>': 'gt_s',
|
||||||
'<=': 'le_s',
|
'<=': 'le_s',
|
||||||
@ -88,6 +110,14 @@ def expression(inp: ourlang.Expression) -> Statements:
|
|||||||
yield wasm.Statement('i32.const', str(inp.value))
|
yield wasm.Statement('i32.const', str(inp.value))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if isinstance(inp, ourlang.ConstantUInt32):
|
||||||
|
yield wasm.Statement('i32.const', str(inp.value))
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(inp, ourlang.ConstantUInt64):
|
||||||
|
yield wasm.Statement('i64.const', str(inp.value))
|
||||||
|
return
|
||||||
|
|
||||||
if isinstance(inp, ourlang.ConstantInt32):
|
if isinstance(inp, ourlang.ConstantInt32):
|
||||||
yield wasm.Statement('i32.const', str(inp.value))
|
yield wasm.Statement('i32.const', str(inp.value))
|
||||||
return
|
return
|
||||||
@ -112,6 +142,20 @@ def expression(inp: ourlang.Expression) -> Statements:
|
|||||||
yield from expression(inp.left)
|
yield from expression(inp.left)
|
||||||
yield from expression(inp.right)
|
yield from expression(inp.right)
|
||||||
|
|
||||||
|
if isinstance(inp.type, typing.TypeUInt32):
|
||||||
|
if operator := OPERATOR_MAP.get(inp.operator, None):
|
||||||
|
yield wasm.Statement(f'i32.{operator}')
|
||||||
|
return
|
||||||
|
if operator := U32_OPERATOR_MAP.get(inp.operator, None):
|
||||||
|
yield wasm.Statement(f'i32.{operator}')
|
||||||
|
return
|
||||||
|
if isinstance(inp.type, typing.TypeUInt64):
|
||||||
|
if operator := OPERATOR_MAP.get(inp.operator, None):
|
||||||
|
yield wasm.Statement(f'i64.{operator}')
|
||||||
|
return
|
||||||
|
if operator := U64_OPERATOR_MAP.get(inp.operator, None):
|
||||||
|
yield wasm.Statement(f'i64.{operator}')
|
||||||
|
return
|
||||||
if isinstance(inp.type, typing.TypeInt32):
|
if isinstance(inp.type, typing.TypeInt32):
|
||||||
if operator := OPERATOR_MAP.get(inp.operator, None):
|
if operator := OPERATOR_MAP.get(inp.operator, None):
|
||||||
yield wasm.Statement(f'i32.{operator}')
|
yield wasm.Statement(f'i32.{operator}')
|
||||||
@ -288,7 +332,7 @@ def function(inp: ourlang.Function) -> wasm.Function:
|
|||||||
for y in inp.statements
|
for y in inp.statements
|
||||||
for x in statement(y)
|
for x in statement(y)
|
||||||
]
|
]
|
||||||
locals_ = [] # TODO
|
locals_ = [] # FIXME: Implement function locals, if required
|
||||||
|
|
||||||
return wasm.Function(
|
return wasm.Function(
|
||||||
inp.name,
|
inp.name,
|
||||||
|
|||||||
@ -12,7 +12,7 @@ from .typing import (
|
|||||||
TypeBase,
|
TypeBase,
|
||||||
TypeNone,
|
TypeNone,
|
||||||
TypeBool,
|
TypeBool,
|
||||||
TypeUInt8,
|
TypeUInt8, TypeUInt32, TypeUInt64,
|
||||||
TypeInt32, TypeInt64,
|
TypeInt32, TypeInt64,
|
||||||
TypeFloat32, TypeFloat64,
|
TypeFloat32, TypeFloat64,
|
||||||
TypeBytes,
|
TypeBytes,
|
||||||
@ -49,6 +49,30 @@ class ConstantUInt8(Constant):
|
|||||||
super().__init__(type_)
|
super().__init__(type_)
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
|
class ConstantUInt32(Constant):
|
||||||
|
"""
|
||||||
|
An UInt32 constant value expression within a statement
|
||||||
|
"""
|
||||||
|
__slots__ = ('value', )
|
||||||
|
|
||||||
|
value: int
|
||||||
|
|
||||||
|
def __init__(self, type_: TypeUInt32, value: int) -> None:
|
||||||
|
super().__init__(type_)
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
class ConstantUInt64(Constant):
|
||||||
|
"""
|
||||||
|
An UInt64 constant value expression within a statement
|
||||||
|
"""
|
||||||
|
__slots__ = ('value', )
|
||||||
|
|
||||||
|
value: int
|
||||||
|
|
||||||
|
def __init__(self, type_: TypeUInt64, value: int) -> None:
|
||||||
|
super().__init__(type_)
|
||||||
|
self.value = value
|
||||||
|
|
||||||
class ConstantInt32(Constant):
|
class ConstantInt32(Constant):
|
||||||
"""
|
"""
|
||||||
An Int32 constant value expression within a statement
|
An Int32 constant value expression within a statement
|
||||||
@ -317,6 +341,8 @@ class Module:
|
|||||||
self.types = {
|
self.types = {
|
||||||
'None': TypeNone(),
|
'None': TypeNone(),
|
||||||
'u8': TypeUInt8(),
|
'u8': TypeUInt8(),
|
||||||
|
'u32': TypeUInt32(),
|
||||||
|
'u64': TypeUInt64(),
|
||||||
'i32': TypeInt32(),
|
'i32': TypeInt32(),
|
||||||
'i64': TypeInt64(),
|
'i64': TypeInt64(),
|
||||||
'f32': TypeFloat32(),
|
'f32': TypeFloat32(),
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import ast
|
|||||||
from .typing import (
|
from .typing import (
|
||||||
TypeBase,
|
TypeBase,
|
||||||
TypeUInt8,
|
TypeUInt8,
|
||||||
|
TypeUInt32,
|
||||||
|
TypeUInt64,
|
||||||
TypeInt32,
|
TypeInt32,
|
||||||
TypeInt64,
|
TypeInt64,
|
||||||
TypeFloat32,
|
TypeFloat32,
|
||||||
@ -30,7 +32,8 @@ from .ourlang import (
|
|||||||
Expression,
|
Expression,
|
||||||
AccessBytesIndex, AccessStructMember, AccessTupleMember,
|
AccessBytesIndex, AccessStructMember, AccessTupleMember,
|
||||||
BinaryOp,
|
BinaryOp,
|
||||||
ConstantFloat32, ConstantFloat64, ConstantInt32, ConstantInt64, ConstantUInt8,
|
ConstantFloat32, ConstantFloat64, ConstantInt32, ConstantInt64,
|
||||||
|
ConstantUInt8, ConstantUInt32, ConstantUInt64,
|
||||||
FunctionCall,
|
FunctionCall,
|
||||||
StructConstructor, TupleConstructor,
|
StructConstructor, TupleConstructor,
|
||||||
UnaryOp, VariableReference,
|
UnaryOp, VariableReference,
|
||||||
@ -485,15 +488,35 @@ class OurVisitor:
|
|||||||
if not isinstance(node.value, int):
|
if not isinstance(node.value, int):
|
||||||
_raise_static_error(node, 'Expected integer value')
|
_raise_static_error(node, 'Expected integer value')
|
||||||
|
|
||||||
# FIXME: Range check
|
if node.value < 0 or node.value > 255:
|
||||||
|
_raise_static_error(node, 'Integer value out of range')
|
||||||
|
|
||||||
return ConstantUInt8(exp_type, node.value)
|
return ConstantUInt8(exp_type, node.value)
|
||||||
|
|
||||||
|
if isinstance(exp_type, TypeUInt32):
|
||||||
|
if not isinstance(node.value, int):
|
||||||
|
_raise_static_error(node, 'Expected integer value')
|
||||||
|
|
||||||
|
if node.value < 0 or node.value > 4294967295:
|
||||||
|
_raise_static_error(node, 'Integer value out of range')
|
||||||
|
|
||||||
|
return ConstantUInt32(exp_type, node.value)
|
||||||
|
|
||||||
|
if isinstance(exp_type, TypeUInt64):
|
||||||
|
if not isinstance(node.value, int):
|
||||||
|
_raise_static_error(node, 'Expected integer value')
|
||||||
|
|
||||||
|
if node.value < 0 or node.value > 18446744073709551615:
|
||||||
|
_raise_static_error(node, 'Integer value out of range')
|
||||||
|
|
||||||
|
return ConstantUInt64(exp_type, node.value)
|
||||||
|
|
||||||
if isinstance(exp_type, TypeInt32):
|
if isinstance(exp_type, TypeInt32):
|
||||||
if not isinstance(node.value, int):
|
if not isinstance(node.value, int):
|
||||||
_raise_static_error(node, 'Expected integer value')
|
_raise_static_error(node, 'Expected integer value')
|
||||||
|
|
||||||
# FIXME: Range check
|
if node.value < -2147483648 or node.value > 2147483647:
|
||||||
|
_raise_static_error(node, 'Integer value out of range')
|
||||||
|
|
||||||
return ConstantInt32(exp_type, node.value)
|
return ConstantInt32(exp_type, node.value)
|
||||||
|
|
||||||
@ -501,7 +524,8 @@ class OurVisitor:
|
|||||||
if not isinstance(node.value, int):
|
if not isinstance(node.value, int):
|
||||||
_raise_static_error(node, 'Expected integer value')
|
_raise_static_error(node, 'Expected integer value')
|
||||||
|
|
||||||
# FIXME: Range check
|
if node.value < -9223372036854775808 or node.value > 9223372036854775807:
|
||||||
|
_raise_static_error(node, 'Integer value out of range')
|
||||||
|
|
||||||
return ConstantInt64(exp_type, node.value)
|
return ConstantInt64(exp_type, node.value)
|
||||||
|
|
||||||
|
|||||||
@ -30,12 +30,37 @@ class TypeBool(TypeBase):
|
|||||||
class TypeUInt8(TypeBase):
|
class TypeUInt8(TypeBase):
|
||||||
"""
|
"""
|
||||||
The Integer type, unsigned and 8 bits wide
|
The Integer type, unsigned and 8 bits wide
|
||||||
|
|
||||||
|
Note that under the hood we need to use i32 to represent
|
||||||
|
these values in expressions. So we need to add some operations
|
||||||
|
to make sure the math checks out.
|
||||||
|
|
||||||
|
So while this does save bytes in memory, it may not actually
|
||||||
|
speed up or improve your code.
|
||||||
"""
|
"""
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
def alloc_size(self) -> int:
|
def alloc_size(self) -> int:
|
||||||
return 4 # Int32 under the hood
|
return 4 # Int32 under the hood
|
||||||
|
|
||||||
|
class TypeUInt32(TypeBase):
|
||||||
|
"""
|
||||||
|
The Integer type, unsigned and 32 bits wide
|
||||||
|
"""
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
def alloc_size(self) -> int:
|
||||||
|
return 4
|
||||||
|
|
||||||
|
class TypeUInt64(TypeBase):
|
||||||
|
"""
|
||||||
|
The Integer type, unsigned and 64 bits wide
|
||||||
|
"""
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
def alloc_size(self) -> int:
|
||||||
|
return 8
|
||||||
|
|
||||||
class TypeInt32(TypeBase):
|
class TypeInt32(TypeBase):
|
||||||
"""
|
"""
|
||||||
The Integer type, signed and 32 bits wide
|
The Integer type, signed and 32 bits wide
|
||||||
|
|||||||
@ -4,14 +4,22 @@ from .helpers import Suite
|
|||||||
|
|
||||||
TYPE_MAP = {
|
TYPE_MAP = {
|
||||||
'u8': int,
|
'u8': int,
|
||||||
|
'u32': int,
|
||||||
|
'u64': int,
|
||||||
'i32': int,
|
'i32': int,
|
||||||
'i64': int,
|
'i64': int,
|
||||||
'f32': float,
|
'f32': float,
|
||||||
'f64': float,
|
'f64': float,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
COMPLETE_SIMPLE_TYPES = [
|
||||||
|
'u32', 'u64',
|
||||||
|
'i32', 'i64',
|
||||||
|
'f32', 'f64',
|
||||||
|
]
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@pytest.mark.parametrize('type_', ['i32', 'i64', 'f32', 'f64', 'u8'])
|
@pytest.mark.parametrize('type_', TYPE_MAP.keys())
|
||||||
def test_return(type_):
|
def test_return(type_):
|
||||||
code_py = f"""
|
code_py = f"""
|
||||||
@exported
|
@exported
|
||||||
@ -25,7 +33,7 @@ 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_', ['i32', 'i64', 'f32', 'f64'])
|
@pytest.mark.parametrize('type_', COMPLETE_SIMPLE_TYPES)
|
||||||
def test_addition(type_):
|
def test_addition(type_):
|
||||||
code_py = f"""
|
code_py = f"""
|
||||||
@exported
|
@exported
|
||||||
@ -39,7 +47,7 @@ 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_', ['i32', 'i64', 'f32', 'f64'])
|
@pytest.mark.parametrize('type_', COMPLETE_SIMPLE_TYPES)
|
||||||
def test_subtraction(type_):
|
def test_subtraction(type_):
|
||||||
code_py = f"""
|
code_py = f"""
|
||||||
@exported
|
@exported
|
||||||
@ -67,7 +75,7 @@ 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_', ['i32', 'i64', 'f32', 'f64', 'u8'])
|
@pytest.mark.parametrize('type_', TYPE_MAP.keys())
|
||||||
def test_arg(type_):
|
def test_arg(type_):
|
||||||
code_py = f"""
|
code_py = f"""
|
||||||
@exported
|
@exported
|
||||||
@ -265,7 +273,7 @@ def helper(left: i32, right: i32) -> i32:
|
|||||||
assert [] == result.log_int32_list
|
assert [] == result.log_int32_list
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@pytest.mark.parametrize('type_', ['i32', 'i64', 'f32', 'f64'])
|
@pytest.mark.parametrize('type_', COMPLETE_SIMPLE_TYPES)
|
||||||
def test_call_with_expression(type_):
|
def test_call_with_expression(type_):
|
||||||
code_py = f"""
|
code_py = f"""
|
||||||
@exported
|
@exported
|
||||||
@ -298,7 +306,7 @@ def testEntry() -> i32:
|
|||||||
assert [] == result.log_int32_list
|
assert [] == result.log_int32_list
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@pytest.mark.parametrize('type_', ['i32', 'i64', 'f32', 'f64', 'u8'])
|
@pytest.mark.parametrize('type_', TYPE_MAP.keys())
|
||||||
def test_struct_0(type_):
|
def test_struct_0(type_):
|
||||||
code_py = f"""
|
code_py = f"""
|
||||||
class CheckedValue:
|
class CheckedValue:
|
||||||
@ -360,7 +368,7 @@ def helper(shape1: Rectangle, shape2: Rectangle) -> i32:
|
|||||||
assert [] == result.log_int32_list
|
assert [] == result.log_int32_list
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@pytest.mark.parametrize('type_', ['i32', 'i64', 'f32', 'f64'])
|
@pytest.mark.parametrize('type_', COMPLETE_SIMPLE_TYPES)
|
||||||
def test_tuple_simple(type_):
|
def test_tuple_simple(type_):
|
||||||
code_py = f"""
|
code_py = f"""
|
||||||
@exported
|
@exported
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user