Implements Extendable and Promotable
This commit is contained in:
parent
a2e1dfd799
commit
1da1adac9f
@ -237,6 +237,28 @@ INSTANCES = {
|
|||||||
prelude.Sized_.methods['len']: {
|
prelude.Sized_.methods['len']: {
|
||||||
'a=bytes': stdlib_types.bytes_sized_len,
|
'a=bytes': stdlib_types.bytes_sized_len,
|
||||||
},
|
},
|
||||||
|
prelude.Extendable.methods['extend']: {
|
||||||
|
'a=u8,b=u32': stdlib_types.u8_u32_extend,
|
||||||
|
'a=u8,b=u64': stdlib_types.u8_u64_extend,
|
||||||
|
'a=u32,b=u64': stdlib_types.u32_u64_extend,
|
||||||
|
'a=i8,b=i32': stdlib_types.i8_i32_extend,
|
||||||
|
'a=i8,b=i64': stdlib_types.i8_i64_extend,
|
||||||
|
'a=i32,b=i64': stdlib_types.i32_i64_extend,
|
||||||
|
},
|
||||||
|
prelude.Extendable.methods['wrap']: {
|
||||||
|
'a=u8,b=u32': stdlib_types.u8_u32_wrap,
|
||||||
|
'a=u8,b=u64': stdlib_types.u8_u64_wrap,
|
||||||
|
'a=u32,b=u64': stdlib_types.u32_u64_wrap,
|
||||||
|
'a=i8,b=i32': stdlib_types.i8_i32_wrap,
|
||||||
|
'a=i8,b=i64': stdlib_types.i8_i64_wrap,
|
||||||
|
'a=i32,b=i64': stdlib_types.i32_i64_wrap,
|
||||||
|
},
|
||||||
|
prelude.Promotable.methods['promote']: {
|
||||||
|
'a=f32,b=f64': stdlib_types.f32_f64_promote,
|
||||||
|
},
|
||||||
|
prelude.Promotable.methods['demote']: {
|
||||||
|
'a=f32,b=f64': stdlib_types.f32_f64_demote,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def phasm_compile(inp: ourlang.Module) -> wasm.Module:
|
def phasm_compile(inp: ourlang.Module) -> wasm.Module:
|
||||||
@ -487,7 +509,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
|
|
||||||
instance_key = ','.join(
|
instance_key = ','.join(
|
||||||
f'{k.letter}={v.name}'
|
f'{k.letter}={v.name}'
|
||||||
for k, v in type_var_map.items()
|
for k, v in sorted(type_var_map.items(), key=lambda x: x[0].letter)
|
||||||
)
|
)
|
||||||
|
|
||||||
instance = INSTANCES.get(inp.function, {}).get(instance_key, None)
|
instance = INSTANCES.get(inp.function, {}).get(instance_key, None)
|
||||||
|
|||||||
@ -14,12 +14,12 @@ from ..type3.types import (
|
|||||||
PRELUDE_TYPE_CLASS_INSTANCES_EXISTING: set[tuple[Type3Class, tuple[Type3, ...]]] = set()
|
PRELUDE_TYPE_CLASS_INSTANCES_EXISTING: set[tuple[Type3Class, tuple[Type3, ...]]] = set()
|
||||||
|
|
||||||
|
|
||||||
def instance_type_class(cls: Type3Class, typ: Type3) -> None:
|
def instance_type_class(cls: Type3Class, *typ: Type3) -> None:
|
||||||
global PRELUDE_TYPE_CLASS_INSTANCES_EXISTING
|
global PRELUDE_TYPE_CLASS_INSTANCES_EXISTING
|
||||||
|
|
||||||
# TODO: Check for required existing instantiations
|
# TODO: Check for required existing instantiations
|
||||||
|
|
||||||
PRELUDE_TYPE_CLASS_INSTANCES_EXISTING.add((cls, (typ, ), ))
|
PRELUDE_TYPE_CLASS_INSTANCES_EXISTING.add((cls, tuple(typ), ))
|
||||||
|
|
||||||
none = Type3('none')
|
none = Type3('none')
|
||||||
"""
|
"""
|
||||||
@ -141,6 +141,7 @@ PRELUDE_TYPES: dict[str, Type3] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a = TypeVariable('a')
|
a = TypeVariable('a')
|
||||||
|
b = TypeVariable('b')
|
||||||
|
|
||||||
|
|
||||||
InternalPassAsPointer = Type3Class('InternalPassAsPointer', [a], methods={}, operators={})
|
InternalPassAsPointer = Type3Class('InternalPassAsPointer', [a], methods={}, operators={})
|
||||||
@ -266,6 +267,25 @@ Sized_ = Type3Class('Sized', [a], methods={
|
|||||||
|
|
||||||
instance_type_class(Sized_, bytes_)
|
instance_type_class(Sized_, bytes_)
|
||||||
|
|
||||||
|
Extendable = Type3Class('Extendable', [a, b], methods={
|
||||||
|
'extend': [a, b],
|
||||||
|
'wrap': [b, a],
|
||||||
|
}, operators={})
|
||||||
|
|
||||||
|
instance_type_class(Extendable, u8, u32)
|
||||||
|
instance_type_class(Extendable, u8, u64)
|
||||||
|
instance_type_class(Extendable, u32, u64)
|
||||||
|
instance_type_class(Extendable, i8, i32)
|
||||||
|
instance_type_class(Extendable, i8, i64)
|
||||||
|
instance_type_class(Extendable, i32, i64)
|
||||||
|
|
||||||
|
Promotable = Type3Class('Promotable', [a, b], methods={
|
||||||
|
'promote': [a, b],
|
||||||
|
'demote': [b, a],
|
||||||
|
}, operators={})
|
||||||
|
|
||||||
|
instance_type_class(Promotable, f32, f64)
|
||||||
|
|
||||||
PRELUDE_TYPE_CLASSES = {
|
PRELUDE_TYPE_CLASSES = {
|
||||||
'Eq': Eq,
|
'Eq': Eq,
|
||||||
'Ord': Ord,
|
'Ord': Ord,
|
||||||
@ -275,6 +295,8 @@ PRELUDE_TYPE_CLASSES = {
|
|||||||
'Integral': Integral,
|
'Integral': Integral,
|
||||||
'Fractional': Fractional,
|
'Fractional': Fractional,
|
||||||
'Floating': Floating,
|
'Floating': Floating,
|
||||||
|
'Extendable': Extendable,
|
||||||
|
'Promotable': Promotable,
|
||||||
}
|
}
|
||||||
|
|
||||||
PRELUDE_OPERATORS = {
|
PRELUDE_OPERATORS = {
|
||||||
@ -297,4 +319,6 @@ PRELUDE_METHODS = {
|
|||||||
**IntNum.methods,
|
**IntNum.methods,
|
||||||
**NatNum.methods,
|
**NatNum.methods,
|
||||||
**Sized_.methods,
|
**Sized_.methods,
|
||||||
|
**Extendable.methods,
|
||||||
|
**Promotable.methods,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -865,3 +865,59 @@ def f64_intnum_neg(g: Generator) -> None:
|
|||||||
def bytes_sized_len(g: Generator) -> None:
|
def bytes_sized_len(g: Generator) -> None:
|
||||||
# The length is stored in the first 4 bytes
|
# The length is stored in the first 4 bytes
|
||||||
g.i32.load()
|
g.i32.load()
|
||||||
|
|
||||||
|
## ###
|
||||||
|
## Extendable
|
||||||
|
|
||||||
|
def u8_u32_extend(g: Generator) -> None:
|
||||||
|
# No-op
|
||||||
|
# u8 is already stored as u32
|
||||||
|
pass
|
||||||
|
|
||||||
|
def u8_u64_extend(g: Generator) -> None:
|
||||||
|
g.i64.extend_i32_u()
|
||||||
|
|
||||||
|
def u32_u64_extend(g: Generator) -> None:
|
||||||
|
g.i64.extend_i32_u()
|
||||||
|
|
||||||
|
def i8_i32_extend(g: Generator) -> None:
|
||||||
|
# No-op
|
||||||
|
# i8 is already stored as i32
|
||||||
|
pass
|
||||||
|
|
||||||
|
def i8_i64_extend(g: Generator) -> None:
|
||||||
|
g.i64.extend_i32_s()
|
||||||
|
|
||||||
|
def i32_i64_extend(g: Generator) -> None:
|
||||||
|
g.i64.extend_i32_s()
|
||||||
|
|
||||||
|
def u8_u32_wrap(g: Generator) -> None:
|
||||||
|
g.i32.const(0xFF)
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def u8_u64_wrap(g: Generator) -> None:
|
||||||
|
g.i32.wrap_i64()
|
||||||
|
g.i32.const(0xFF)
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def u32_u64_wrap(g: Generator) -> None:
|
||||||
|
g.i32.wrap_i64()
|
||||||
|
|
||||||
|
def i8_i32_wrap(g: Generator) -> None:
|
||||||
|
g.i32.const(0xFF)
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def i8_i64_wrap(g: Generator) -> None:
|
||||||
|
g.i32.wrap_i64()
|
||||||
|
|
||||||
|
def i32_i64_wrap(g: Generator) -> None:
|
||||||
|
g.i32.wrap_i64()
|
||||||
|
|
||||||
|
## ###
|
||||||
|
## Promotable
|
||||||
|
|
||||||
|
def f32_f64_promote(g: Generator) -> None:
|
||||||
|
g.f64.promote_f32()
|
||||||
|
|
||||||
|
def f32_f64_demote(g: Generator) -> None:
|
||||||
|
g.f32.demote_f64()
|
||||||
|
|||||||
@ -74,6 +74,9 @@ class Generator_i32(Generator_i32i64):
|
|||||||
def __init__(self, generator: 'Generator') -> None:
|
def __init__(self, generator: 'Generator') -> None:
|
||||||
super().__init__('i32', generator)
|
super().__init__('i32', generator)
|
||||||
|
|
||||||
|
# 2.4.1. Numeric Instructions
|
||||||
|
self.wrap_i64 = functools.partial(self.generator.add_statement, 'i32.wrap_i64')
|
||||||
|
|
||||||
class Generator_i64(Generator_i32i64):
|
class Generator_i64(Generator_i32i64):
|
||||||
def __init__(self, generator: 'Generator') -> None:
|
def __init__(self, generator: 'Generator') -> None:
|
||||||
super().__init__('i64', generator)
|
super().__init__('i64', generator)
|
||||||
@ -132,10 +135,16 @@ class Generator_f32(Generator_f32f64):
|
|||||||
def __init__(self, generator: 'Generator') -> None:
|
def __init__(self, generator: 'Generator') -> None:
|
||||||
super().__init__('f32', generator)
|
super().__init__('f32', generator)
|
||||||
|
|
||||||
|
# 2.4.1 Numeric Instructions
|
||||||
|
self.demote_f64 = functools.partial(self.generator.add_statement, 'f32.demote_f64')
|
||||||
|
|
||||||
class Generator_f64(Generator_f32f64):
|
class Generator_f64(Generator_f32f64):
|
||||||
def __init__(self, generator: 'Generator') -> None:
|
def __init__(self, generator: 'Generator') -> None:
|
||||||
super().__init__('f64', generator)
|
super().__init__('f64', generator)
|
||||||
|
|
||||||
|
# 2.4.1 Numeric Instructions
|
||||||
|
self.promote_f32 = functools.partial(self.generator.add_statement, 'f64.promote_f32')
|
||||||
|
|
||||||
class Generator_Local:
|
class Generator_Local:
|
||||||
def __init__(self, generator: 'Generator') -> None:
|
def __init__(self, generator: 'Generator') -> None:
|
||||||
self.generator = generator
|
self.generator = generator
|
||||||
|
|||||||
@ -264,6 +264,13 @@ def _load_memory_stored_returned_value(
|
|||||||
|
|
||||||
if ret_type3 in (prelude.i8, prelude.i32, prelude.i64):
|
if ret_type3 in (prelude.i8, prelude.i32, prelude.i64):
|
||||||
assert isinstance(wasm_value, int), wasm_value
|
assert isinstance(wasm_value, int), wasm_value
|
||||||
|
|
||||||
|
if ret_type3 is prelude.i8:
|
||||||
|
# Values are actually i32
|
||||||
|
# Have to reinterpret to load proper value
|
||||||
|
data = struct.pack('<i', wasm_value)
|
||||||
|
wasm_value, = struct.unpack('<bxxx', data)
|
||||||
|
|
||||||
return wasm_value
|
return wasm_value
|
||||||
|
|
||||||
if ret_type3 in (prelude.u8, prelude.u32, prelude.u64):
|
if ret_type3 in (prelude.u8, prelude.u32, prelude.u64):
|
||||||
|
|||||||
112
tests/integration/test_lang/test_extendable.py
Normal file
112
tests/integration/test_lang/test_extendable.py
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from phasm.type3.entry import Type3Exception
|
||||||
|
|
||||||
|
from ..helpers import Suite
|
||||||
|
|
||||||
|
EXTENTABLE = [
|
||||||
|
('u8', 'u32', ),
|
||||||
|
('u8', 'u64', ),
|
||||||
|
('u32', 'u64', ),
|
||||||
|
|
||||||
|
('i8', 'i32', ),
|
||||||
|
('i8', 'i64', ),
|
||||||
|
('i32', 'i64', ),
|
||||||
|
]
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
def test_extend_not_implemented():
|
||||||
|
code_py = """
|
||||||
|
class Foo:
|
||||||
|
val: i32
|
||||||
|
|
||||||
|
class Baz:
|
||||||
|
val: i32
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry(x: Foo) -> Baz:
|
||||||
|
return extend(x)
|
||||||
|
"""
|
||||||
|
|
||||||
|
with pytest.raises(Type3Exception, match='Missing type class instantation: Extendable Foo Baz'):
|
||||||
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
@pytest.mark.parametrize('ext_from,ext_to', EXTENTABLE)
|
||||||
|
def test_extend_ok(ext_from,ext_to):
|
||||||
|
code_py = f"""
|
||||||
|
CONSTANT: {ext_from} = 10
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry() -> {ext_to}:
|
||||||
|
return extend(CONSTANT)
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = Suite(code_py).run_code()
|
||||||
|
|
||||||
|
assert 10 == result.returned_value
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
@pytest.mark.parametrize('ext_from,in_put,ext_to,exp_out', [
|
||||||
|
('u8', 241, 'u32', 241),
|
||||||
|
('u32', 4059165169, 'u64', 4059165169),
|
||||||
|
('u8', 241, 'u64', 241),
|
||||||
|
|
||||||
|
('i8', 113, 'i32', 113),
|
||||||
|
('i32', 1911681521, 'i64', 1911681521),
|
||||||
|
('i8', 113, 'i64', 113),
|
||||||
|
|
||||||
|
('i8', -15, 'i32', -15),
|
||||||
|
('i32', -15, 'i64', -15),
|
||||||
|
('i8', -15, 'i64', -15),
|
||||||
|
|
||||||
|
])
|
||||||
|
def test_extend_results(ext_from, ext_to, in_put, exp_out):
|
||||||
|
code_py = f"""
|
||||||
|
@exported
|
||||||
|
def testEntry(x: {ext_from}) -> {ext_to}:
|
||||||
|
return extend(x)
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = Suite(code_py).run_code(in_put)
|
||||||
|
|
||||||
|
assert exp_out == result.returned_value
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
@pytest.mark.parametrize('ext_from,ext_to', EXTENTABLE)
|
||||||
|
def test_wrap_ok(ext_from,ext_to):
|
||||||
|
code_py = f"""
|
||||||
|
CONSTANT: {ext_to} = 10
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry() -> {ext_from}:
|
||||||
|
return wrap(CONSTANT)
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = Suite(code_py).run_code()
|
||||||
|
|
||||||
|
assert 10 == result.returned_value
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
@pytest.mark.parametrize('ext_to,in_put,ext_from,exp_out', [
|
||||||
|
('u32', 0xF1F1F1F1, 'u8', 0xF1),
|
||||||
|
('u64', 0xF1F1F1F1F1F1F1F1, 'u32', 0xF1F1F1F1),
|
||||||
|
('u64', 0xF1F1F1F1F1F1F1F1, 'u8', 0xF1),
|
||||||
|
|
||||||
|
('i32', 0xF1F1F171, 'i8', 113),
|
||||||
|
('i32', 0xF1F1F1F1, 'i8', -15),
|
||||||
|
('i64', 0x71F1F1F171F1F1F1, 'i32', 1911681521),
|
||||||
|
('i64', 0x71F1F1F1F1F1F1F1, 'i32', -235802127),
|
||||||
|
('i64', 0xF1F1F1F1F1F1F171, 'i8', 113),
|
||||||
|
('i64', 0xF1F1F1F1F1F1F1F1, 'i8', -15),
|
||||||
|
])
|
||||||
|
def test_wrap_results(ext_from, ext_to, in_put, exp_out):
|
||||||
|
code_py = f"""
|
||||||
|
@exported
|
||||||
|
def testEntry(x: {ext_to}) -> {ext_from}:
|
||||||
|
return wrap(x)
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = Suite(code_py).run_code(in_put)
|
||||||
|
|
||||||
|
assert exp_out == result.returned_value
|
||||||
51
tests/integration/test_lang/test_promotable.py
Normal file
51
tests/integration/test_lang/test_promotable.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from phasm.type3.entry import Type3Exception
|
||||||
|
|
||||||
|
from ..helpers import Suite
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
def test_promote_not_implemented():
|
||||||
|
code_py = """
|
||||||
|
class Foo:
|
||||||
|
val: i32
|
||||||
|
|
||||||
|
class Baz:
|
||||||
|
val: i32
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry(x: Foo) -> Baz:
|
||||||
|
return promote(x)
|
||||||
|
"""
|
||||||
|
|
||||||
|
with pytest.raises(Type3Exception, match='Missing type class instantation: Promotable Foo Baz'):
|
||||||
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
def test_promote_ok():
|
||||||
|
code_py = """
|
||||||
|
CONSTANT: f32 = 10.5
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry() -> f64:
|
||||||
|
return promote(CONSTANT)
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = Suite(code_py).run_code()
|
||||||
|
|
||||||
|
assert 10.5 == result.returned_value
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
def test_demote_ok():
|
||||||
|
code_py = """
|
||||||
|
CONSTANT: f64 = 10.5
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry() -> f32:
|
||||||
|
return demote(CONSTANT)
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = Suite(code_py).run_code()
|
||||||
|
|
||||||
|
assert 10.5 == result.returned_value
|
||||||
Loading…
x
Reference in New Issue
Block a user