phasm/phasm/codestyle.py
Johan B.W. de Vries a83858aca7 Adds u32 and u64
Also, adds some range checks to constants.
2022-07-09 14:22:38 +02:00

205 lines
5.2 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.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 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.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)})'
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):
return f'{expression(inp.varref)}[{inp.member.idx}]'
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 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