MVP #1

Merged
jbwdevries merged 73 commits from idea_crc32 into master 2022-08-21 12:59:21 +00:00
6 changed files with 152 additions and 16 deletions
Showing only changes of commit a83858aca7 - Show all commits

View File

@ -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, )):

View File

@ -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,

View File

@ -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(),

View File

@ -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)

View File

@ -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

View File

@ -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