More work on type3
This commit is contained in:
parent
b5a28daebf
commit
9f21d0fd1d
7
TODO.md
7
TODO.md
@ -1,7 +1,12 @@
|
|||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
- Implement a trace() builtin for debugging
|
|
||||||
- Implement a proper type matching / checking system
|
- Implement a proper type matching / checking system
|
||||||
|
- Implement subscript as an operator
|
||||||
|
- Implement another operator
|
||||||
|
- Figure out how to do type classes
|
||||||
|
- Implement structs again, with the `.foo` notation working
|
||||||
|
|
||||||
|
- Implement a trace() builtin for debugging
|
||||||
- Check if we can use DataView in the Javascript examples, e.g. with setUint32
|
- Check if we can use DataView in the Javascript examples, e.g. with setUint32
|
||||||
- Storing u8 in memory still claims 32 bits (since that's what you need in local variables). However, using load8_u / loadu_s we can optimize this.
|
- Storing u8 in memory still claims 32 bits (since that's what you need in local variables). However, using load8_u / loadu_s we can optimize this.
|
||||||
- Implement a FizzBuzz example
|
- Implement a FizzBuzz example
|
||||||
|
|||||||
@ -36,16 +36,15 @@ def type3(inp: Type3OrPlaceholder) -> str:
|
|||||||
|
|
||||||
return inp.name
|
return inp.name
|
||||||
|
|
||||||
# def struct_definition(inp: typing.TypeStruct) -> str:
|
def struct_definition(inp: ourlang.StructDefinition) -> str:
|
||||||
# """
|
"""
|
||||||
# Render: TypeStruct's definition
|
Render: TypeStruct's definition
|
||||||
# """
|
"""
|
||||||
# result = f'class {inp.name}:\n'
|
result = f'class {inp.struct_type3.name}:\n'
|
||||||
# for mem in inp.members: # TODO: Broken after new type system
|
for mem, typ in inp.struct_type3.members.items():
|
||||||
# raise NotImplementedError('Structs broken after new type system')
|
result += f' {mem}: {type3(typ)}\n'
|
||||||
# # result += f' {mem.name}: {type_(mem.type)}\n'
|
|
||||||
#
|
return result
|
||||||
# return result
|
|
||||||
|
|
||||||
def constant_definition(inp: ourlang.ModuleConstantDef) -> str:
|
def constant_definition(inp: ourlang.ModuleConstantDef) -> str:
|
||||||
"""
|
"""
|
||||||
@ -95,10 +94,10 @@ def expression(inp: ourlang.Expression) -> str:
|
|||||||
for arg in inp.arguments
|
for arg in inp.arguments
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if isinstance(inp.function, ourlang.StructConstructor):
|
||||||
|
return f'{inp.function.struct_type3.name}({args})'
|
||||||
|
|
||||||
# TODO: Broken after new type system
|
# TODO: Broken after new type system
|
||||||
# if isinstance(inp.function, ourlang.StructConstructor):
|
|
||||||
# return f'{inp.function.struct.name}({args})'
|
|
||||||
#
|
|
||||||
# if isinstance(inp.function, ourlang.TupleConstructor):
|
# if isinstance(inp.function, ourlang.TupleConstructor):
|
||||||
# return f'({args}, )'
|
# return f'({args}, )'
|
||||||
|
|
||||||
@ -110,9 +109,8 @@ def expression(inp: ourlang.Expression) -> str:
|
|||||||
|
|
||||||
return f'{varref}[{index}]'
|
return f'{varref}[{index}]'
|
||||||
|
|
||||||
# TODO: Broken after new type system
|
if isinstance(inp, ourlang.AccessStructMember):
|
||||||
# if isinstance(inp, ourlang.AccessStructMember):
|
return f'{expression(inp.varref)}.{inp.member}'
|
||||||
# return f'{expression(inp.varref)}.{inp.member.name}'
|
|
||||||
|
|
||||||
if isinstance(inp, ourlang.Fold):
|
if isinstance(inp, ourlang.Fold):
|
||||||
fold_name = 'foldl' if ourlang.Fold.Direction.LEFT == inp.dir else 'foldr'
|
fold_name = 'foldl' if ourlang.Fold.Direction.LEFT == inp.dir else 'foldr'
|
||||||
@ -180,10 +178,10 @@ def module(inp: ourlang.Module) -> str:
|
|||||||
"""
|
"""
|
||||||
result = ''
|
result = ''
|
||||||
|
|
||||||
# for struct in inp.structs.values():
|
for struct in inp.struct_definitions.values():
|
||||||
# if result:
|
if result:
|
||||||
# result += '\n'
|
result += '\n'
|
||||||
# result += struct_definition(struct)
|
result += struct_definition(struct)
|
||||||
|
|
||||||
for cdef in inp.constant_defs.values():
|
for cdef in inp.constant_defs.values():
|
||||||
if result:
|
if result:
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
This module contains the code to convert parsed Ourlang into WebAssembly code
|
This module contains the code to convert parsed Ourlang into WebAssembly code
|
||||||
"""
|
"""
|
||||||
from typing import List, Optional
|
from typing import List, Union
|
||||||
|
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
@ -14,6 +14,16 @@ from .stdlib import alloc as stdlib_alloc
|
|||||||
from .stdlib import types as stdlib_types
|
from .stdlib import types as stdlib_types
|
||||||
from .wasmgenerator import Generator as WasmGenerator
|
from .wasmgenerator import Generator as WasmGenerator
|
||||||
|
|
||||||
|
LOAD_STORE_TYPE_MAP = {
|
||||||
|
'u8': 'i32', # Have to use an u32, since there is no native u8 type
|
||||||
|
'i32': 'i32',
|
||||||
|
'i64': 'i64',
|
||||||
|
'u32': 'i32',
|
||||||
|
'u64': 'i64',
|
||||||
|
'f32': 'f32',
|
||||||
|
'f64': 'f64',
|
||||||
|
}
|
||||||
|
|
||||||
def phasm_compile(inp: ourlang.Module) -> wasm.Module:
|
def phasm_compile(inp: ourlang.Module) -> wasm.Module:
|
||||||
"""
|
"""
|
||||||
Public method for compiling a parsed Phasm module into
|
Public method for compiling a parsed Phasm module into
|
||||||
@ -53,16 +63,10 @@ def type3(inp: type3types.Type3OrPlaceholder) -> wasm.WasmType:
|
|||||||
if inp is type3types.f64:
|
if inp is type3types.f64:
|
||||||
return wasm.WasmTypeFloat64()
|
return wasm.WasmTypeFloat64()
|
||||||
|
|
||||||
# if tc_prim.primitive is typing.TypeConstraintPrimitive.Primitive.STATIC_ARRAY:
|
if isinstance(inp, type3types.StructType3):
|
||||||
# # StaticArray, Tuples and Structs are passed as pointer
|
# Structs and tuples are passed as pointer
|
||||||
# # And pointers are i32
|
# And pointers are i32
|
||||||
# return wasm.WasmTypeInt32()
|
return wasm.WasmTypeInt32()
|
||||||
|
|
||||||
# TODO: Broken after new type system
|
|
||||||
# if isinstance(inp, (typing.TypeStruct, typing.TypeTuple, typing.TypeStaticArray, typing.TypeBytes)):
|
|
||||||
# # Structs and tuples are passed as pointer
|
|
||||||
# # And pointers are i32
|
|
||||||
# return wasm.WasmTypeInt32()
|
|
||||||
|
|
||||||
raise NotImplementedError(type3, inp)
|
raise NotImplementedError(type3, inp)
|
||||||
|
|
||||||
@ -349,17 +353,19 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
# wgn.call(stdlib_types.__subscript_bytes__)
|
# wgn.call(stdlib_types.__subscript_bytes__)
|
||||||
# return
|
# return
|
||||||
|
|
||||||
# if isinstance(inp, ourlang.AccessStructMember):
|
if isinstance(inp, ourlang.AccessStructMember):
|
||||||
# mtyp = LOAD_STORE_TYPE_MAP.get(inp.member.type.__class__)
|
mtyp = LOAD_STORE_TYPE_MAP.get(inp.struct_type3.members[inp.member].name)
|
||||||
# if mtyp is None:
|
if mtyp is None:
|
||||||
# # In the future might extend this by having structs or tuples
|
# In the future might extend this by having structs or tuples
|
||||||
# # as members of struct or tuples
|
# as members of struct or tuples
|
||||||
# raise NotImplementedError(expression, inp, inp.member)
|
raise NotImplementedError(expression, inp, inp.struct_type3)
|
||||||
#
|
|
||||||
# expression(wgn, inp.varref)
|
expression(wgn, inp.varref)
|
||||||
# wgn.add_statement(f'{mtyp}.load', 'offset=' + str(inp.member.offset))
|
wgn.add_statement(f'{mtyp}.load', 'offset=' + str(_calculate_member_offset(
|
||||||
# return
|
inp.struct_type3, inp.member
|
||||||
#
|
)))
|
||||||
|
return
|
||||||
|
|
||||||
# if isinstance(inp, ourlang.AccessTupleMember):
|
# if isinstance(inp, ourlang.AccessTupleMember):
|
||||||
# mtyp = LOAD_STORE_TYPE_MAP.get(inp.member.type.__class__)
|
# mtyp = LOAD_STORE_TYPE_MAP.get(inp.member.type.__class__)
|
||||||
# if mtyp is None:
|
# if mtyp is None:
|
||||||
@ -544,8 +550,8 @@ def function(inp: ourlang.Function) -> wasm.Function:
|
|||||||
|
|
||||||
if False: # TODO: isinstance(inp, ourlang.TupleConstructor):
|
if False: # TODO: isinstance(inp, ourlang.TupleConstructor):
|
||||||
pass # _generate_tuple_constructor(wgn, inp)
|
pass # _generate_tuple_constructor(wgn, inp)
|
||||||
elif False: # TODO: isinstance(inp, ourlang.StructConstructor):
|
elif isinstance(inp, ourlang.StructConstructor):
|
||||||
pass # _generate_struct_constructor(wgn, inp)
|
_generate_struct_constructor(wgn, inp)
|
||||||
else:
|
else:
|
||||||
for stat in inp.statements:
|
for stat in inp.statements:
|
||||||
statement(wgn, stat)
|
statement(wgn, stat)
|
||||||
@ -733,26 +739,34 @@ def module(inp: ourlang.Module) -> wasm.Module:
|
|||||||
#
|
#
|
||||||
# # Return the allocated address
|
# # Return the allocated address
|
||||||
# wgn.local.get(tmp_var)
|
# wgn.local.get(tmp_var)
|
||||||
#
|
|
||||||
# def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstructor) -> None:
|
def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstructor) -> None:
|
||||||
# tmp_var = wgn.temp_var_i32('struct_adr')
|
tmp_var = wgn.temp_var_i32('struct_adr')
|
||||||
#
|
|
||||||
# # Allocated the required amounts of bytes in memory
|
# Allocated the required amounts of bytes in memory
|
||||||
# wgn.i32.const(inp.struct.alloc_size())
|
wgn.i32.const(_calculate_alloc_size(inp.struct_type3))
|
||||||
# wgn.call(stdlib_alloc.__alloc__)
|
wgn.call(stdlib_alloc.__alloc__)
|
||||||
# wgn.local.set(tmp_var)
|
wgn.local.set(tmp_var)
|
||||||
#
|
|
||||||
# # Store each member individually
|
# Store each member individually
|
||||||
# for member in inp.struct.members:
|
for memname, mtyp3 in inp.struct_type3.members.items():
|
||||||
# mtyp = LOAD_STORE_TYPE_MAP.get(member.type.__class__)
|
mtyp = LOAD_STORE_TYPE_MAP.get(mtyp3.name)
|
||||||
# if mtyp is None:
|
if mtyp is None:
|
||||||
# # In the future might extend this by having structs or tuples
|
# In the future might extend this by having structs or tuples
|
||||||
# # as members of struct or tuples
|
# as members of struct or tuples
|
||||||
# raise NotImplementedError(expression, inp, member)
|
raise NotImplementedError(expression, inp, mtyp3)
|
||||||
#
|
|
||||||
# wgn.local.get(tmp_var)
|
wgn.local.get(tmp_var)
|
||||||
# wgn.add_statement('local.get', f'${member.name}')
|
wgn.add_statement('local.get', f'${memname}')
|
||||||
# wgn.add_statement(f'{mtyp}.store', 'offset=' + str(member.offset))
|
wgn.add_statement(f'{mtyp}.store', 'offset=' + str(_calculate_member_offset(
|
||||||
#
|
inp.struct_type3, memname
|
||||||
# # Return the allocated address
|
)))
|
||||||
# wgn.local.get(tmp_var)
|
|
||||||
|
# Return the allocated address
|
||||||
|
wgn.local.get(tmp_var)
|
||||||
|
|
||||||
|
def _calculate_alloc_size(type3: Union[type3types.StructType3, type3types.Type3]) -> int:
|
||||||
|
return 0 # FIXME: Stub
|
||||||
|
|
||||||
|
def _calculate_member_offset(struct_type3: type3types.StructType3, member: str) -> int:
|
||||||
|
return 0 # FIXME: Stub
|
||||||
|
|||||||
@ -11,7 +11,7 @@ WEBASSEMBLY_BUILTIN_FLOAT_OPS: Final = ('abs', 'sqrt', 'ceil', 'floor', 'trunc',
|
|||||||
WEBASSEMBLY_BUILTIN_BYTES_OPS: Final = ('len', )
|
WEBASSEMBLY_BUILTIN_BYTES_OPS: Final = ('len', )
|
||||||
|
|
||||||
from .type3 import types as type3types
|
from .type3 import types as type3types
|
||||||
from .type3.types import Type3, Type3OrPlaceholder, PlaceholderForType
|
from .type3.types import Type3, Type3OrPlaceholder, PlaceholderForType, StructType3
|
||||||
|
|
||||||
class Expression:
|
class Expression:
|
||||||
"""
|
"""
|
||||||
@ -134,6 +134,23 @@ class Subscript(Expression):
|
|||||||
self.varref = varref
|
self.varref = varref
|
||||||
self.index = index
|
self.index = index
|
||||||
|
|
||||||
|
class AccessStructMember(Expression):
|
||||||
|
"""
|
||||||
|
Access a struct member for reading of writing
|
||||||
|
"""
|
||||||
|
__slots__ = ('varref', 'struct_type3', 'member', )
|
||||||
|
|
||||||
|
varref: VariableReference
|
||||||
|
struct_type3: StructType3
|
||||||
|
member: str
|
||||||
|
|
||||||
|
def __init__(self, varref: VariableReference, struct_type3: StructType3, member: str) -> None:
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.varref = varref
|
||||||
|
self.struct_type3 = struct_type3
|
||||||
|
self.member = member
|
||||||
|
|
||||||
class Fold(Expression):
|
class Fold(Expression):
|
||||||
"""
|
"""
|
||||||
A (left or right) fold
|
A (left or right) fold
|
||||||
@ -240,28 +257,41 @@ class Function:
|
|||||||
self.returns_str = 'None'
|
self.returns_str = 'None'
|
||||||
self.posonlyargs = []
|
self.posonlyargs = []
|
||||||
|
|
||||||
|
class StructDefinition:
|
||||||
|
"""
|
||||||
|
The definition for a struct
|
||||||
|
"""
|
||||||
|
__slots__ = ('struct_type3', 'lineno', )
|
||||||
|
|
||||||
|
struct_type3: StructType3
|
||||||
|
lineno: int
|
||||||
|
|
||||||
|
def __init__(self, struct_type3: StructType3, lineno: int) -> None:
|
||||||
|
self.struct_type3 = struct_type3
|
||||||
|
self.lineno = lineno
|
||||||
|
|
||||||
|
class StructConstructor(Function):
|
||||||
|
"""
|
||||||
|
The constructor method for a struct
|
||||||
|
|
||||||
|
A function will generated to instantiate a struct. The arguments
|
||||||
|
will be the defaults
|
||||||
|
"""
|
||||||
|
__slots__ = ('struct_type3', )
|
||||||
|
|
||||||
|
struct_type3: StructType3
|
||||||
|
|
||||||
|
def __init__(self, struct_type3: StructType3) -> None:
|
||||||
|
super().__init__(f'@{struct_type3.name}@__init___@', -1)
|
||||||
|
|
||||||
|
self.returns_type3 = struct_type3
|
||||||
|
|
||||||
|
for mem, typ in struct_type3.members.items():
|
||||||
|
self.posonlyargs.append(FunctionParam(mem, typ, ))
|
||||||
|
|
||||||
|
self.struct_type3 = struct_type3
|
||||||
|
|
||||||
# TODO: Broken after new type system
|
# TODO: Broken after new type system
|
||||||
# class StructConstructor(Function):
|
|
||||||
# """
|
|
||||||
# The constructor method for a struct
|
|
||||||
#
|
|
||||||
# A function will generated to instantiate a struct. The arguments
|
|
||||||
# will be the defaults
|
|
||||||
# """
|
|
||||||
# __slots__ = ('struct', )
|
|
||||||
#
|
|
||||||
# struct: TypeStruct
|
|
||||||
#
|
|
||||||
# def __init__(self, struct: TypeStruct) -> None:
|
|
||||||
# super().__init__(f'@{struct.name}@__init___@', -1)
|
|
||||||
#
|
|
||||||
# self.returns = struct
|
|
||||||
#
|
|
||||||
# for mem in struct.members:
|
|
||||||
# self.posonlyargs.append(FunctionParam(mem.name, mem.type, ))
|
|
||||||
#
|
|
||||||
# self.struct = struct
|
|
||||||
#
|
|
||||||
# class TupleConstructor(Function):
|
# class TupleConstructor(Function):
|
||||||
# """
|
# """
|
||||||
# The constructor method for a tuple
|
# The constructor method for a tuple
|
||||||
@ -331,15 +361,15 @@ class Module:
|
|||||||
"""
|
"""
|
||||||
A module is a file and consists of functions
|
A module is a file and consists of functions
|
||||||
"""
|
"""
|
||||||
__slots__ = ('data', 'types', 'structs', 'constant_defs', 'functions',)
|
__slots__ = ('data', 'types', 'struct_definitions', 'constant_defs', 'functions',)
|
||||||
|
|
||||||
data: ModuleData
|
data: ModuleData
|
||||||
# structs: Dict[str, TypeStruct]
|
struct_definitions: Dict[str, StructDefinition]
|
||||||
constant_defs: Dict[str, ModuleConstantDef]
|
constant_defs: Dict[str, ModuleConstantDef]
|
||||||
functions: Dict[str, Function]
|
functions: Dict[str, Function]
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.data = ModuleData()
|
self.data = ModuleData()
|
||||||
# self.structs = {}
|
self.struct_definitions = {}
|
||||||
self.constant_defs = {}
|
self.constant_defs = {}
|
||||||
self.functions = {}
|
self.functions = {}
|
||||||
|
|||||||
166
phasm/parser.py
166
phasm/parser.py
@ -18,8 +18,9 @@ from .ourlang import (
|
|||||||
BinaryOp,
|
BinaryOp,
|
||||||
ConstantPrimitive, ConstantTuple,
|
ConstantPrimitive, ConstantTuple,
|
||||||
|
|
||||||
FunctionCall, Subscript,
|
FunctionCall, AccessStructMember, Subscript,
|
||||||
# StructConstructor, TupleConstructor,
|
StructDefinition, StructConstructor,
|
||||||
|
# TupleConstructor,
|
||||||
UnaryOp, VariableReference,
|
UnaryOp, VariableReference,
|
||||||
|
|
||||||
Fold,
|
Fold,
|
||||||
@ -75,16 +76,15 @@ class OurVisitor:
|
|||||||
|
|
||||||
module.constant_defs[res.name] = res
|
module.constant_defs[res.name] = res
|
||||||
|
|
||||||
# TODO: Broken after type system
|
if isinstance(res, StructDefinition):
|
||||||
# if isinstance(res, TypeStruct):
|
if res.struct_type3.name in module.struct_definitions:
|
||||||
# if res.name in module.structs:
|
raise StaticError(
|
||||||
# raise StaticError(
|
f'{res.struct_type3.name} already defined on line {module.struct_definitions[res.struct_type3.name].lineno}'
|
||||||
# f'{res.name} already defined on line {module.structs[res.name].lineno}'
|
)
|
||||||
# )
|
|
||||||
#
|
module.struct_definitions[res.struct_type3.name] = res
|
||||||
# module.structs[res.name] = res
|
constructor = StructConstructor(res.struct_type3)
|
||||||
# constructor = StructConstructor(res)
|
module.functions[constructor.name] = constructor
|
||||||
# module.functions[constructor.name] = constructor
|
|
||||||
|
|
||||||
if isinstance(res, Function):
|
if isinstance(res, Function):
|
||||||
if res.name in module.functions:
|
if res.name in module.functions:
|
||||||
@ -101,12 +101,12 @@ class OurVisitor:
|
|||||||
|
|
||||||
return module
|
return module
|
||||||
|
|
||||||
def pre_visit_Module_stmt(self, module: Module, node: ast.stmt) -> Union[Function, ModuleConstantDef]: # TypeStruct
|
def pre_visit_Module_stmt(self, module: Module, node: ast.stmt) -> Union[Function, StructDefinition, ModuleConstantDef]:
|
||||||
if isinstance(node, ast.FunctionDef):
|
if isinstance(node, ast.FunctionDef):
|
||||||
return self.pre_visit_Module_FunctionDef(module, node)
|
return self.pre_visit_Module_FunctionDef(module, node)
|
||||||
|
|
||||||
# if isinstance(node, ast.ClassDef):
|
if isinstance(node, ast.ClassDef):
|
||||||
# return self.pre_visit_Module_ClassDef(module, node)
|
return self.pre_visit_Module_ClassDef(module, node)
|
||||||
|
|
||||||
if isinstance(node, ast.AnnAssign):
|
if isinstance(node, ast.AnnAssign):
|
||||||
return self.pre_visit_Module_AnnAssign(module, node)
|
return self.pre_visit_Module_AnnAssign(module, node)
|
||||||
@ -155,36 +155,33 @@ class OurVisitor:
|
|||||||
|
|
||||||
return function
|
return function
|
||||||
|
|
||||||
def pre_visit_Module_ClassDef(self, module: Module, node: ast.ClassDef) -> None: # TypeStruct:
|
def pre_visit_Module_ClassDef(self, module: Module, node: ast.ClassDef) -> StructDefinition:
|
||||||
raise NotImplementedError
|
|
||||||
# struct = TypeStruct(node.name, node.lineno)
|
_not_implemented(not node.bases, 'ClassDef.bases')
|
||||||
#
|
_not_implemented(not node.keywords, 'ClassDef.keywords')
|
||||||
# _not_implemented(not node.bases, 'ClassDef.bases')
|
_not_implemented(not node.decorator_list, 'ClassDef.decorator_list')
|
||||||
# _not_implemented(not node.keywords, 'ClassDef.keywords')
|
|
||||||
# _not_implemented(not node.decorator_list, 'ClassDef.decorator_list')
|
members: Dict[str, type3types.Type3] = {}
|
||||||
#
|
|
||||||
# offset = 0
|
for stmt in node.body:
|
||||||
#
|
if not isinstance(stmt, ast.AnnAssign):
|
||||||
# for stmt in node.body:
|
raise NotImplementedError(f'Class with {stmt} nodes')
|
||||||
# if not isinstance(stmt, ast.AnnAssign):
|
|
||||||
# raise NotImplementedError(f'Class with {stmt} nodes')
|
if not isinstance(stmt.target, ast.Name):
|
||||||
#
|
raise NotImplementedError('Class with default values')
|
||||||
# if not isinstance(stmt.target, ast.Name):
|
|
||||||
# raise NotImplementedError('Class with default values')
|
if not stmt.value is None:
|
||||||
#
|
raise NotImplementedError('Class with default values')
|
||||||
# if not stmt.value is None:
|
|
||||||
# raise NotImplementedError('Class with default values')
|
if stmt.simple != 1:
|
||||||
#
|
raise NotImplementedError('Class with non-simple arguments')
|
||||||
# if stmt.simple != 1:
|
|
||||||
# raise NotImplementedError('Class with non-simple arguments')
|
if stmt.target.id in members:
|
||||||
#
|
_raise_static_error(stmt, 'Struct members must have unique names')
|
||||||
# raise NotImplementedError('TODO: Broken after new type system')
|
|
||||||
# member = TypeStructMember(stmt.target.id, self.visit_type(module, stmt.annotation), offset)
|
members[stmt.target.id] = self.visit_type(module, stmt.annotation)
|
||||||
#
|
|
||||||
# struct.members.append(member)
|
return StructDefinition(type3types.StructType3(node.name, members), node.lineno)
|
||||||
# offset += member.type.alloc_size()
|
|
||||||
#
|
|
||||||
# return struct
|
|
||||||
|
|
||||||
def pre_visit_Module_AnnAssign(self, module: Module, node: ast.AnnAssign) -> ModuleConstantDef:
|
def pre_visit_Module_AnnAssign(self, module: Module, node: ast.AnnAssign) -> ModuleConstantDef:
|
||||||
if not isinstance(node.target, ast.Name):
|
if not isinstance(node.target, ast.Name):
|
||||||
@ -474,13 +471,14 @@ 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.structs:
|
if node.func.id in module.struct_definitions:
|
||||||
# raise NotImplementedError('TODO: Broken after new type system')
|
struct_definition = module.struct_definitions[node.func.id]
|
||||||
# struct = module.structs[node.func.id]
|
struct_constructor = StructConstructor(struct_definition.struct_type3)
|
||||||
# struct_constructor = StructConstructor(struct)
|
|
||||||
#
|
# FIXME: Defer struct de-allocation
|
||||||
# func = module.functions[struct_constructor.name]
|
|
||||||
if node.func.id in WEBASSEMBLY_BUILTIN_FLOAT_OPS:
|
func = module.functions[struct_constructor.name]
|
||||||
|
elif node.func.id in WEBASSEMBLY_BUILTIN_FLOAT_OPS:
|
||||||
if 1 != len(node.args):
|
if 1 != len(node.args):
|
||||||
_raise_static_error(node, f'Function {node.func.id} requires 1 arguments but {len(node.args)} are given')
|
_raise_static_error(node, f'Function {node.func.id} requires 1 arguments but {len(node.args)} are given')
|
||||||
|
|
||||||
@ -552,33 +550,33 @@ class OurVisitor:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def visit_Module_FunctionDef_Attribute(self, module: Module, function: Function, our_locals: OurLocals, node: ast.Attribute) -> Expression:
|
def visit_Module_FunctionDef_Attribute(self, module: Module, function: Function, our_locals: OurLocals, node: ast.Attribute) -> Expression:
|
||||||
raise NotImplementedError('Broken after new type system')
|
del module
|
||||||
# del module
|
del function
|
||||||
# del function
|
|
||||||
#
|
if not isinstance(node.value, ast.Name):
|
||||||
# if not isinstance(node.value, ast.Name):
|
_raise_static_error(node, 'Must reference a name')
|
||||||
# _raise_static_error(node, 'Must reference a name')
|
|
||||||
#
|
if not isinstance(node.ctx, ast.Load):
|
||||||
# if not isinstance(node.ctx, ast.Load):
|
_raise_static_error(node, 'Must be load context')
|
||||||
# _raise_static_error(node, 'Must be load context')
|
|
||||||
#
|
if not node.value.id in our_locals:
|
||||||
# if not node.value.id in our_locals:
|
_raise_static_error(node, f'Undefined variable {node.value.id}')
|
||||||
# _raise_static_error(node, f'Undefined variable {node.value.id}')
|
|
||||||
#
|
param = our_locals[node.value.id]
|
||||||
# param = our_locals[node.value.id]
|
|
||||||
#
|
node_typ = param.type3
|
||||||
# node_typ = param.type
|
if not isinstance(node_typ, type3types.StructType3):
|
||||||
# if not isinstance(node_typ, TypeStruct):
|
_raise_static_error(node, f'Cannot take attribute of non-struct {node.value.id}')
|
||||||
# _raise_static_error(node, f'Cannot take attribute of non-struct {node.value.id}')
|
|
||||||
#
|
member = node_typ.members.get(node.attr)
|
||||||
# member = node_typ.get_member(node.attr)
|
if member is None:
|
||||||
# if member is None:
|
_raise_static_error(node, f'{node_typ.name} has no attribute {node.attr}')
|
||||||
# _raise_static_error(node, f'{node_typ.name} has no attribute {node.attr}')
|
|
||||||
#
|
return AccessStructMember(
|
||||||
# return AccessStructMember(
|
VariableReference(param),
|
||||||
# VariableReference(param),
|
node_typ,
|
||||||
# member,
|
node.attr,
|
||||||
# )
|
)
|
||||||
|
|
||||||
def visit_Module_FunctionDef_Subscript(self, module: Module, function: Function, our_locals: OurLocals, node: ast.Subscript) -> Expression:
|
def visit_Module_FunctionDef_Subscript(self, module: Module, function: Function, our_locals: OurLocals, node: ast.Subscript) -> Expression:
|
||||||
if not isinstance(node.value, ast.Name):
|
if not isinstance(node.value, ast.Name):
|
||||||
@ -687,12 +685,10 @@ class OurVisitor:
|
|||||||
if node.id in type3types.LOOKUP_TABLE:
|
if node.id in type3types.LOOKUP_TABLE:
|
||||||
return type3types.LOOKUP_TABLE[node.id]
|
return type3types.LOOKUP_TABLE[node.id]
|
||||||
|
|
||||||
raise NotImplementedError('TODO: Broken after type system')
|
if node.id in module.struct_definitions:
|
||||||
#
|
return module.struct_definitions[node.id].struct_type3
|
||||||
# if node.id in module.structs:
|
|
||||||
# return module.structs[node.id]
|
_raise_static_error(node, f'Unrecognized type {node.id}')
|
||||||
#
|
|
||||||
# _raise_static_error(node, f'Unrecognized type {node.id}')
|
|
||||||
|
|
||||||
if isinstance(node, ast.Subscript):
|
if isinstance(node, ast.Subscript):
|
||||||
if not isinstance(node.value, ast.Name):
|
if not isinstance(node.value, ast.Name):
|
||||||
|
|||||||
@ -13,7 +13,6 @@ from .constraints import (
|
|||||||
ConstraintBase,
|
ConstraintBase,
|
||||||
LiteralFitsConstraint, SameTypeConstraint,
|
LiteralFitsConstraint, SameTypeConstraint,
|
||||||
)
|
)
|
||||||
from . import types
|
|
||||||
|
|
||||||
def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase]:
|
def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase]:
|
||||||
ctx = Context()
|
ctx = Context()
|
||||||
@ -48,9 +47,17 @@ def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBas
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if isinstance(inp, ourlang.AccessStructMember):
|
||||||
|
yield SameTypeConstraint(inp.struct_type3.members[inp.member], inp.type3,
|
||||||
|
f'The type of a struct member reference is the same as the type of struct member {inp.struct_type3.name}.{inp.member}')
|
||||||
|
return
|
||||||
|
|
||||||
raise NotImplementedError(expression, inp)
|
raise NotImplementedError(expression, inp)
|
||||||
|
|
||||||
def function(ctx: Context, inp: ourlang.Function) -> Generator[ConstraintBase, None, None]:
|
def function(ctx: Context, inp: ourlang.Function) -> Generator[ConstraintBase, None, None]:
|
||||||
|
if isinstance(inp, ourlang.StructConstructor):
|
||||||
|
return
|
||||||
|
|
||||||
if len(inp.statements) != 1 or not isinstance(inp.statements[0], ourlang.StatementReturn):
|
if len(inp.statements) != 1 or not isinstance(inp.statements[0], ourlang.StatementReturn):
|
||||||
raise NotImplementedError('Functions with not just a return statement')
|
raise NotImplementedError('Functions with not just a return statement')
|
||||||
|
|
||||||
|
|||||||
@ -139,6 +139,31 @@ class AppliedType3(Type3):
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'AppliedType3({repr(self.base)}, {repr(self.args)})'
|
return f'AppliedType3({repr(self.base)}, {repr(self.args)})'
|
||||||
|
|
||||||
|
class StructType3(Type3):
|
||||||
|
"""
|
||||||
|
A Type3 struct with named members
|
||||||
|
"""
|
||||||
|
__slots__ = ('name', 'members', )
|
||||||
|
|
||||||
|
name: str
|
||||||
|
"""
|
||||||
|
The structs fully qualified name
|
||||||
|
"""
|
||||||
|
|
||||||
|
members: Dict[str, Type3]
|
||||||
|
"""
|
||||||
|
The struct's field definitions
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name: str, members: Dict[str, Type3]) -> None:
|
||||||
|
super().__init__(name)
|
||||||
|
|
||||||
|
self.name = name
|
||||||
|
self.members = dict(members)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f'StructType3(repr({self.name}), repr({self.members}))'
|
||||||
|
|
||||||
none = Type3('none')
|
none = Type3('none')
|
||||||
"""
|
"""
|
||||||
The none type, for when functions simply don't return anything. e.g., IO().
|
The none type, for when functions simply don't return anything. e.g., IO().
|
||||||
|
|||||||
@ -1,12 +1,36 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from phasm.exceptions import StaticError
|
from phasm.exceptions import StaticError
|
||||||
|
from phasm.type3.entry import Type3Exception
|
||||||
|
|
||||||
from phasm.parser import phasm_parse
|
from phasm.parser import phasm_parse
|
||||||
|
|
||||||
|
from ..constants import (
|
||||||
|
ALL_INT_TYPES
|
||||||
|
)
|
||||||
from ..helpers import Suite
|
from ..helpers import Suite
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@pytest.mark.parametrize('type_', ('i32', 'f64', ))
|
@pytest.mark.parametrize('type_', ALL_INT_TYPES)
|
||||||
|
def test_module_constant(type_):
|
||||||
|
code_py = f"""
|
||||||
|
class CheckedValue:
|
||||||
|
value: {type_}
|
||||||
|
|
||||||
|
CONSTANT: CheckedValue = CheckedValue(24)
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry() -> {type_}:
|
||||||
|
return CONSTANT.value
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = Suite(code_py).run_code()
|
||||||
|
|
||||||
|
assert 24 == result.returned_value
|
||||||
|
assert TYPE_MAP[type_] == type(result.returned_value)
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
@pytest.mark.parametrize('type_', ALL_INT_TYPES)
|
||||||
def test_struct_0(type_):
|
def test_struct_0(type_):
|
||||||
code_py = f"""
|
code_py = f"""
|
||||||
class CheckedValue:
|
class CheckedValue:
|
||||||
@ -75,43 +99,5 @@ def testEntry(arg: Struct) -> (i32, i32, ):
|
|||||||
return arg.param
|
return arg.param
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with pytest.raises(StaticError, match=f'Static error on line 6: Expected \\(i32, i32, \\), arg.param is actually {type_}'):
|
with pytest.raises(Type3Exception, match=r'\(i32, i32, \) must be ' + type_ + ' instead'):
|
||||||
phasm_parse(code_py)
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
@pytest.mark.parametrize('type_', ['i32', 'i64', 'f32', 'f64'])
|
|
||||||
def test_type_mismatch_tuple_member(type_):
|
|
||||||
code_py = f"""
|
|
||||||
def testEntry(arg: ({type_}, )) -> (i32, i32, ):
|
|
||||||
return arg[0]
|
|
||||||
"""
|
|
||||||
|
|
||||||
with pytest.raises(StaticError, match=f'Static error on line 3: Expected \\(i32, i32, \\), arg\\[0\\] is actually {type_}'):
|
|
||||||
phasm_parse(code_py)
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_tuple_constant_too_few_values():
|
|
||||||
code_py = """
|
|
||||||
CONSTANT: (u32, u8, u8, ) = (24, 57, )
|
|
||||||
"""
|
|
||||||
|
|
||||||
with pytest.raises(StaticError, match='Static error on line 2: Invalid number of tuple values'):
|
|
||||||
phasm_parse(code_py)
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_tuple_constant_too_many_values():
|
|
||||||
code_py = """
|
|
||||||
CONSTANT: (u32, u8, u8, ) = (24, 57, 1, 1, )
|
|
||||||
"""
|
|
||||||
|
|
||||||
with pytest.raises(StaticError, match='Static error on line 2: Invalid number of tuple values'):
|
|
||||||
phasm_parse(code_py)
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_tuple_constant_type_mismatch():
|
|
||||||
code_py = """
|
|
||||||
CONSTANT: (u32, u8, u8, ) = (24, 4000, 1, )
|
|
||||||
"""
|
|
||||||
|
|
||||||
with pytest.raises(StaticError, match='Static error on line 2: Integer value out of range; expected 0..255, actual 4000'):
|
|
||||||
phasm_parse(code_py)
|
|
||||||
|
|||||||
@ -82,3 +82,30 @@ def testEntry() -> i32x4:
|
|||||||
result = Suite(code_py).run_code()
|
result = Suite(code_py).run_code()
|
||||||
|
|
||||||
assert (1, 2, 3, 0) == result.returned_value
|
assert (1, 2, 3, 0) == result.returned_value
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
def test_tuple_constant_too_few_values():
|
||||||
|
code_py = """
|
||||||
|
CONSTANT: (u32, u8, u8, ) = (24, 57, )
|
||||||
|
"""
|
||||||
|
|
||||||
|
with pytest.raises(StaticError, match='Static error on line 2: Invalid number of tuple values'):
|
||||||
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
def test_tuple_constant_too_many_values():
|
||||||
|
code_py = """
|
||||||
|
CONSTANT: (u32, u8, u8, ) = (24, 57, 1, 1, )
|
||||||
|
"""
|
||||||
|
|
||||||
|
with pytest.raises(StaticError, match='Static error on line 2: Invalid number of tuple values'):
|
||||||
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
def test_tuple_constant_type_mismatch():
|
||||||
|
code_py = """
|
||||||
|
CONSTANT: (u32, u8, u8, ) = (24, 4000, 1, )
|
||||||
|
"""
|
||||||
|
|
||||||
|
with pytest.raises(StaticError, match='Static error on line 2: Integer value out of range; expected 0..255, actual 4000'):
|
||||||
|
Suite(code_py).run_code()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user