199 lines
5.5 KiB
Python
199 lines
5.5 KiB
Python
"""
|
|
This module generates source code based on the parsed AST
|
|
|
|
It's intented to be a "any color, as long as it's black" kind of renderer
|
|
"""
|
|
from typing import Generator, Optional
|
|
|
|
from . import ourlang
|
|
from . import typing
|
|
|
|
def phasm_render(inp: ourlang.Module) -> str:
|
|
"""
|
|
Public method for rendering a Phasm module into Phasm code
|
|
"""
|
|
return module(inp)
|
|
|
|
Statements = Generator[str, None, None]
|
|
|
|
def type_var(inp: Optional[typing.TypeVar]) -> str:
|
|
"""
|
|
Render: type's name
|
|
"""
|
|
assert inp is not None, typing.ASSERTION_ERROR
|
|
|
|
mtyp = typing.simplify(inp)
|
|
if mtyp is None:
|
|
raise NotImplementedError(f'Rendering type {inp}')
|
|
|
|
return mtyp
|
|
|
|
def struct_definition(inp: typing.TypeStruct) -> str:
|
|
"""
|
|
Render: TypeStruct's definition
|
|
"""
|
|
result = f'class {inp.name}:\n'
|
|
for mem in inp.members:
|
|
raise NotImplementedError('Structs broken after new type system')
|
|
# result += f' {mem.name}: {type_(mem.type)}\n'
|
|
|
|
return result
|
|
|
|
def constant_definition(inp: ourlang.ModuleConstantDef) -> str:
|
|
"""
|
|
Render: Module Constant's definition
|
|
"""
|
|
return f'{inp.name}: {type_var(inp.type_var)} = {expression(inp.constant)}\n'
|
|
|
|
def expression(inp: ourlang.Expression) -> str:
|
|
"""
|
|
Render: A Phasm expression
|
|
"""
|
|
if isinstance(inp, ourlang.ConstantPrimitive):
|
|
# Floats might not round trip if the original constant
|
|
# could not fit in the given float type
|
|
return str(inp.value)
|
|
|
|
if isinstance(inp, (ourlang.ConstantTuple, ourlang.ConstantStaticArray, )):
|
|
return '(' + ', '.join(
|
|
expression(x)
|
|
for x in inp.value
|
|
) + ', )'
|
|
|
|
if isinstance(inp, ourlang.VariableReference):
|
|
return str(inp.variable.name)
|
|
|
|
if isinstance(inp, ourlang.UnaryOp):
|
|
if (
|
|
inp.operator in ourlang.WEBASSEMBLY_BUILDIN_FLOAT_OPS
|
|
or inp.operator in ourlang.WEBASSEMBLY_BUILDIN_BYTES_OPS):
|
|
return f'{inp.operator}({expression(inp.right)})'
|
|
|
|
if inp.operator == 'cast':
|
|
mtyp = type_var(inp.type_var)
|
|
if mtyp is None:
|
|
raise NotImplementedError(f'Casting to type {inp.type_var}')
|
|
|
|
return f'{mtyp}({expression(inp.right)})'
|
|
|
|
return f'{inp.operator}{expression(inp.right)}'
|
|
|
|
if isinstance(inp, ourlang.BinaryOp):
|
|
return f'{expression(inp.left)} {inp.operator} {expression(inp.right)}'
|
|
|
|
if isinstance(inp, ourlang.FunctionCall):
|
|
args = ', '.join(
|
|
expression(arg)
|
|
for arg in inp.arguments
|
|
)
|
|
|
|
if isinstance(inp.function, ourlang.StructConstructor):
|
|
return f'{inp.function.struct.name}({args})'
|
|
|
|
if isinstance(inp.function, ourlang.TupleConstructor):
|
|
return f'({args}, )'
|
|
|
|
return f'{inp.function.name}({args})'
|
|
|
|
if isinstance(inp, ourlang.AccessBytesIndex):
|
|
return f'{expression(inp.varref)}[{expression(inp.index)}]'
|
|
|
|
if isinstance(inp, ourlang.AccessStructMember):
|
|
return f'{expression(inp.varref)}.{inp.member.name}'
|
|
|
|
if isinstance(inp, (ourlang.AccessTupleMember, ourlang.AccessStaticArrayMember, )):
|
|
if isinstance(inp.member, ourlang.Expression):
|
|
return f'{expression(inp.varref)}[{expression(inp.member)}]'
|
|
|
|
return f'{expression(inp.varref)}[{inp.member.idx}]'
|
|
|
|
if isinstance(inp, ourlang.Fold):
|
|
fold_name = 'foldl' if ourlang.Fold.Direction.LEFT == inp.dir else 'foldr'
|
|
return f'{fold_name}({inp.func.name}, {expression(inp.base)}, {expression(inp.iter)})'
|
|
|
|
if isinstance(inp, ourlang.ModuleConstantReference):
|
|
return inp.definition.name
|
|
|
|
raise NotImplementedError(expression, inp)
|
|
|
|
def statement(inp: ourlang.Statement) -> Statements:
|
|
"""
|
|
Render: A list of Phasm statements
|
|
"""
|
|
if isinstance(inp, ourlang.StatementPass):
|
|
yield 'pass'
|
|
return
|
|
|
|
if isinstance(inp, ourlang.StatementReturn):
|
|
yield f'return {expression(inp.value)}'
|
|
return
|
|
|
|
if isinstance(inp, ourlang.StatementIf):
|
|
yield f'if {expression(inp.test)}:'
|
|
|
|
for stmt in inp.statements:
|
|
for line in statement(stmt):
|
|
yield f' {line}' if line else ''
|
|
|
|
yield ''
|
|
return
|
|
|
|
raise NotImplementedError(statement, inp)
|
|
|
|
def function(inp: ourlang.Function) -> str:
|
|
"""
|
|
Render: Function body
|
|
|
|
Imported functions only have "pass" as a body. Later on we might replace
|
|
this by the function documentation, if any.
|
|
"""
|
|
result = ''
|
|
if inp.exported:
|
|
result += '@exported\n'
|
|
if inp.imported:
|
|
result += '@imported\n'
|
|
|
|
args = ', '.join(
|
|
f'{p.name}: {type_var(p.type_var)}'
|
|
for p in inp.posonlyargs
|
|
)
|
|
|
|
result += f'def {inp.name}({args}) -> {type_var(inp.returns_type_var)}:\n'
|
|
|
|
if inp.imported:
|
|
result += ' pass\n'
|
|
else:
|
|
for stmt in inp.statements:
|
|
for line in statement(stmt):
|
|
result += f' {line}\n' if line else '\n'
|
|
|
|
return result
|
|
|
|
|
|
def module(inp: ourlang.Module) -> str:
|
|
"""
|
|
Render: Module
|
|
"""
|
|
result = ''
|
|
|
|
for struct in inp.structs.values():
|
|
if result:
|
|
result += '\n'
|
|
result += struct_definition(struct)
|
|
|
|
for cdef in inp.constant_defs.values():
|
|
if result:
|
|
result += '\n'
|
|
result += constant_definition(cdef)
|
|
|
|
for func in inp.functions.values():
|
|
if func.lineno < 0:
|
|
# Buildin (-2) or auto generated (-1)
|
|
continue
|
|
|
|
if result:
|
|
result += '\n'
|
|
result += function(func)
|
|
|
|
return result
|