phasm/phasm/codestyle.py
Johan B.W. de Vries e589223dbb Static Arrays. CRC32 compiles \o/
Doesn't give right answer yet and out of bound check fails.
No constructor yet for static arrays, but module constants
work. Which don't work yet for tuples and structs.

Also, u32 for indexing please.

Also, more module constant types.
2022-08-18 20:53:21 +02:00

238 lines
6.3 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
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_(inp: typing.TypeBase) -> str:
"""
Render: Type (name)
"""
if isinstance(inp, typing.TypeNone):
return 'None'
if isinstance(inp, typing.TypeBool):
return 'bool'
if isinstance(inp, typing.TypeUInt8):
return 'u8'
if isinstance(inp, typing.TypeUInt32):
return 'u32'
if isinstance(inp, typing.TypeUInt64):
return 'u64'
if isinstance(inp, typing.TypeInt32):
return 'i32'
if isinstance(inp, typing.TypeInt64):
return 'i64'
if isinstance(inp, typing.TypeFloat32):
return 'f32'
if isinstance(inp, typing.TypeFloat64):
return 'f64'
if isinstance(inp, typing.TypeBytes):
return 'bytes'
if isinstance(inp, typing.TypeTuple):
mems = ', '.join(
type_(x.type)
for x in inp.members
)
return f'({mems}, )'
if isinstance(inp, typing.TypeStaticArray):
return f'{type_(inp.member_type)}[{len(inp.members)}]'
if isinstance(inp, typing.TypeStruct):
return inp.name
raise NotImplementedError(type_, inp)
def struct_definition(inp: typing.TypeStruct) -> str:
"""
Render: TypeStruct's definition
"""
result = f'class {inp.name}:\n'
for mem in inp.members:
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_(inp.type)} = {expression(inp.constant)}\n'
def expression(inp: ourlang.Expression) -> str:
"""
Render: A Phasm expression
"""
if isinstance(inp, (
ourlang.ConstantUInt8, ourlang.ConstantUInt32, ourlang.ConstantUInt64,
ourlang.ConstantInt32, ourlang.ConstantInt64,
)):
return str(inp.value)
if isinstance(inp, (ourlang.ConstantFloat32, ourlang.ConstantFloat64, )):
# These 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.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':
return f'{type_(inp.type)}({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'{x}: {type_(y)}'
for x, y in inp.posonlyargs
)
result += f'def {inp.name}({args}) -> {type_(inp.returns)}:\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