mirror of
https://github.com/egormanga/Slang.git
synced 2025-08-01 01:16:59 +03:00
Huge work; i'm too tired to write commit message for all of it. Pleez let me sleep acouple of hours. Made most tests work. Started SBC.
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
#!/usr/bin/python3
|
||||
# Slang compilers
|
||||
|
||||
import abc
|
||||
from ..ast import SlSyntaxError, SlValidationError
|
||||
import abc, traceback
|
||||
|
||||
class Compiler(abc.ABC):
|
||||
@abc.abstractclassmethod
|
||||
@@ -23,9 +24,10 @@ class SlCompilationError(Exception):
|
||||
def __str__(self):
|
||||
l, line = lstripcount(self.line.partition('\n')[0].replace('\t', ' '), ' \t')
|
||||
offset = (self.node.offset-l) if (self.node.offset != -1) else len(line)
|
||||
return (f'\033[2m(in {self.scope})\033[0m ' if (self.scope is not None) else '')+f"Compilation error: {self.desc}{self.at}"+(':\n'+\
|
||||
' \033[1m'+line[:offset]+'\033[91m'+line[offset:]+'\033[0m\n'+\
|
||||
' '+' '*offset+'\033[95m^'+'~'*(self.node.length-1) if (line) else '')
|
||||
return (f'\033[2m(in {self.scope})\033[0m ' if (self.scope is not None) else '') + f"Compilation error: {self.desc}{self.at}"+(':\n' + \
|
||||
' \033[1m'+line[:offset]+'\033[91m'+line[offset:]+'\033[0m\n' + \
|
||||
' '+' '*offset+'\033[95m^'+'~'*(self.node.length-1)+'\033[0m' if (line) else '') + \
|
||||
(f"\n\n\033[1;95mCaused by:\033[0m\n{self.__cause__ if (isinstance(self.__cause__, (SlSyntaxError, SlValidationError, SlCompilationError))) else ' '+str().join(traceback.format_exception(type(self.__cause__), self.__cause__, self.__cause__.__traceback__))}" if (self.__cause__ is not None) else '')
|
||||
|
||||
@property
|
||||
def at(self):
|
||||
|
135
compilers/esolang/gibberish.py
Normal file
135
compilers/esolang/gibberish.py
Normal file
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/python3
|
||||
# Slang Esolang Gibberish compilation target (PoC)
|
||||
# https://esolangs.org/wiki/Gibberish_(programming_language)
|
||||
|
||||
from .. import *
|
||||
from Slang.ast import *
|
||||
from utils import *
|
||||
|
||||
class Instrs:
|
||||
binopmap = {
|
||||
'+': b'ea',
|
||||
'-': b'es',
|
||||
'*': b'em',
|
||||
'/': b'ed',
|
||||
'<<': b'fl',
|
||||
'>>': b'fr',
|
||||
'&': b'ga',
|
||||
}
|
||||
|
||||
@init_defaults
|
||||
@autocast
|
||||
def __init__(self, ns, stack: Slist, functions: dict):
|
||||
self.ns, self.stack, self.functions = ns, stack, functions
|
||||
self.code = bytearray()
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTRootNode):
|
||||
self.add(x.code)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTCodeNode):
|
||||
for i in x.nodes:
|
||||
self.add(i)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTVardefNode):
|
||||
if (x.value is not None):
|
||||
self.load(x.value)
|
||||
self.stack[-1] = Signature.build(x, self.ns)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTAssignmentNode):
|
||||
sig = Signature.build(x.name, self.ns)
|
||||
for ii, i in enumerate(self.stack):
|
||||
if (i == sig): self.stack[ii] = None
|
||||
self.load(x.value)
|
||||
self.stack[-1] = sig
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTFunccallNode):
|
||||
self.load(x)
|
||||
self.code += b'ev'
|
||||
self.stack.pop()
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTFuncdefNode):
|
||||
code_ns = self.ns.derive(x.name.identifier)
|
||||
if (x.name.identifier == 'main'):
|
||||
assert not x.argdefs
|
||||
name = x.name.identifier
|
||||
else: name = f"{x.name.identifier}__{self.ns.signatures[x.name.identifier].call.index(CallArguments(args=tuple(Signature.build(i, code_ns) for i in x.argdefs)))}"
|
||||
f_instrs = Instrs(ns=code_ns, stack=(Signature.build(i, code_ns) for i in x.argdefs), functions=self.functions.copy())
|
||||
f_instrs.add(x.code)
|
||||
#dlog(f"{x.__fsig__()} instrs:\n"+'\n'.join(f_instrs.instrs)+'\n')
|
||||
self.functions[name] = f_instrs
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTKeywordExprNode):
|
||||
if (x.keyword.keyword == 'return'):
|
||||
self.load(x.value)
|
||||
else: raise NotImplementedError(x.keyword)
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTLiteralNode):
|
||||
s = str(eval(str(x.literal))).encode().split(b']')
|
||||
if (s):
|
||||
s[0] = b'['+s[0]
|
||||
s[-1] += b']'
|
||||
self.code += b'][93]eigtec['.join(s) + b'c'*(len(s)-1)
|
||||
if (issubclass(literal_type(x.literal), (int, float))): self.code += b'ei'
|
||||
self.stack.append(None)
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTIdentifierNode):
|
||||
dlog(self.stack)
|
||||
sig = Signature.build(x, self.ns)
|
||||
i = self.stack.rindex(sig)
|
||||
self.code += (b'[%d]eip' if (i >= 10) else b'%dep') % i
|
||||
self.stack.append(sig)
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTValueNode):
|
||||
self.load(x.value)
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTFunccallNode):
|
||||
assert not (x.callargs.starargs or x.callkwargs.callkwargs or x.callkwargs.starkwargs)
|
||||
if (x.callable.value.identifier == 'print'):
|
||||
for i in x.callargs.callargs[:-1]:
|
||||
self.load(i)
|
||||
self.code += b'eq[32]eigteq'
|
||||
self.stack.pop()
|
||||
if (x.callargs.callargs):
|
||||
self.load(x.callargs.callargs[-1])
|
||||
self.stack.pop()
|
||||
else: self.code += b'[]'
|
||||
self.code += b'eo'
|
||||
self.stack.append(None)
|
||||
return
|
||||
for i in x.callargs.callargs[::-1]:
|
||||
self.load(i)
|
||||
self.stack.pop()
|
||||
self.code += self.functions[f"{x.callable.value.identifier}__{self.ns.signatures[x.callable.value.identifier].call.index(CallArguments.build(x, self.ns))}" if (isinstance(x.callable, ASTValueNode) and isinstance(x.callable.value, ASTIdentifierNode) and x.callable.value.identifier in self.ns.signatures) else x.callable.value.identifier].code#.join((b'{', b'}'))
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTBinaryExprNode):
|
||||
self.load(x.lvalue)
|
||||
self.load(x.rvalue)
|
||||
self.code += self.binopmap[x.operator.operator]
|
||||
self.stack.pop()
|
||||
self.stack.pop()
|
||||
self.stack.append(None)
|
||||
|
||||
class GibberishCompiler(Compiler):
|
||||
ext = '.gib'
|
||||
|
||||
@classmethod
|
||||
def compile_ast(cls, ast, ns, *, filename):
|
||||
instrs = Instrs(ns=ns)
|
||||
instrs.add(ast)
|
||||
code = bytes(instrs.code)
|
||||
dlog("Code:\n"+code.decode())
|
||||
return code
|
||||
|
||||
# by Sdore, 2019
|
@@ -1,278 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
# Slang Pyssembly compiler target
|
||||
|
||||
import pyssembly
|
||||
from . import *
|
||||
from Slang.ast import *
|
||||
from utils import *
|
||||
|
||||
class Instrs:
|
||||
unopmap = {
|
||||
'+': 'POS',
|
||||
'-': 'NEG',
|
||||
'!': 'NOT',
|
||||
'~': 'INV',
|
||||
'not': 'NOT',
|
||||
}
|
||||
|
||||
binopmap = {
|
||||
'+': 'ADD',
|
||||
'-': 'SUB',
|
||||
'*': 'MUL',
|
||||
'/': 'DIV',
|
||||
'//': 'FLOORDIV',
|
||||
'%': 'MOD',
|
||||
'**': 'POW',
|
||||
'<<': 'LSHIFT',
|
||||
'>>': 'RSHIFT',
|
||||
'&': 'AND',
|
||||
'|': 'OR',
|
||||
'^': 'XOR',
|
||||
'and': 'AND',
|
||||
'or': 'OR',
|
||||
}
|
||||
|
||||
@init_defaults
|
||||
def __init__(self, *, name, ns, filename, argdefs=()):
|
||||
self.name, self.ns, self.filename = name, ns, filename
|
||||
self.instrs = list()
|
||||
self.consts = list()
|
||||
self.argnames = list()
|
||||
for i in argdefs:
|
||||
if (i.modifier is not None): raise SlCompilationError("argument modifiers are not supported yet", i.modifier)
|
||||
self.argnames.append(i.name.identifier)
|
||||
self.cellvars = self.argnames.copy()
|
||||
self.srclnotab = list()
|
||||
|
||||
def compile(self):
|
||||
return pyssembly.Code('\n'.join(self.instrs), name=self.name, filename=self.filename.strip('"'), srclnotab=self.srclnotab, consts=self.consts, argnames=self.argnames)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTRootNode):
|
||||
self.add(x.code)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTCodeNode):
|
||||
lastln = int()
|
||||
for i in x.nodes:
|
||||
self.srclnotab.append(i.lineno-lastln)
|
||||
lastln = i.lineno
|
||||
l = len(self.instrs)
|
||||
self.add(i)
|
||||
self.srclnotab += [0]*(len(self.instrs)-l-1)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTValueNode):
|
||||
self.add(x.value)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTVardefNode):
|
||||
if (x.value is not None):
|
||||
self.load(x.value)
|
||||
self.store(x.name)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTAssignmentNode):
|
||||
if (x.inplace_operator is not None): self.instrs.append(f"LOAD ({x.name})")
|
||||
self.load(x.value)
|
||||
if (x.inplace_operator is not None): self.instrs.append(f"INP_{self.binopmap[x.inplace_operator.operator]}")
|
||||
self.store(x.name)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTFunccallNode):
|
||||
self.load(x)
|
||||
self.instrs.append("POP")
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTBlockNode):
|
||||
self.add(x.code)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTFuncdefNode):
|
||||
code_ns = self.ns.derive(x.name.identifier)
|
||||
name = f"{x.name.identifier}__{self.ns.signatures[x.name.identifier].call.index(CallArguments(args=tuple(Signature.build(i, code_ns) for i in x.argdefs)))}"
|
||||
f_instrs = Instrs(name=f"{self.name}.{name}", ns=code_ns, filename=self.filename, argdefs=x.argdefs)
|
||||
f_instrs.add(x.code)
|
||||
#dlog(f"{x.__fsig__()} instrs:\n"+'\n'.join(f_instrs.instrs)+'\n')
|
||||
self.consts.append(f_instrs.compile().to_code())
|
||||
self.instrs += [
|
||||
f"LOAD {len(self.consts)-1}",
|
||||
f"LOAD ('{self.name}.{name}')",
|
||||
f"MAKE_FUNCTION 0", # TODO: flags
|
||||
]
|
||||
self.store(name)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTKeywordExprNode):
|
||||
if (x.keyword.keyword == 'import'):
|
||||
ns, _, name = x.value.identifier.partition('::')
|
||||
assert ns == 'py'
|
||||
self.instrs += [
|
||||
"LOAD (0)", # TODO
|
||||
"LOAD (None)", # TODO
|
||||
]
|
||||
self.instrs.append(f"IMPORT_NAME ({name})")
|
||||
self.store(name)
|
||||
elif (x.keyword.keyword == 'return'):
|
||||
self.load(x.value)
|
||||
self.instrs.append("RET")
|
||||
else: raise NotImplementedError(x.keyword)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTConditionalNode):
|
||||
self.load(x.condition)
|
||||
self.instrs.append("JPOPF :else")
|
||||
self.add(x.code)
|
||||
self.instrs += [
|
||||
"JUMPF :end",
|
||||
":else",
|
||||
":end",
|
||||
]
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTForLoopNode):
|
||||
self.load(x.iterable)
|
||||
#self.cellvars.append(x.name.identifier) # TODO FIXME
|
||||
self.instrs += [
|
||||
"SETUP_LOOP :end",
|
||||
"ITER",
|
||||
":for",
|
||||
"FOR :else",
|
||||
]
|
||||
self.store(x.name)
|
||||
self.add(x.code)
|
||||
self.instrs += [
|
||||
"JUMPA :for",
|
||||
":else",
|
||||
"POP_BLOCK",
|
||||
":end",
|
||||
]
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTWhileLoopNode):
|
||||
self.instrs += [
|
||||
"SETUP_LOOP :end",
|
||||
":while",
|
||||
]
|
||||
self.load(x.condition)
|
||||
self.instrs.append("JPOPF :else")
|
||||
self.add(x.code)
|
||||
self.instrs += [
|
||||
"JUMPA :while",
|
||||
":else",
|
||||
"POP_BLOCK",
|
||||
":end",
|
||||
]
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTElseClauseNode):
|
||||
assert (self.instrs[-1] == ":end")
|
||||
popped = [self.instrs.pop()]
|
||||
if (self.instrs[-1] == "POP_BLOCK"): popped.append(self.instrs.pop())
|
||||
self.add(x.code)
|
||||
self.instrs += reversed(popped)
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTLiteralNode):
|
||||
self.instrs.append(f"LOAD ({x.literal})")
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTIdentifierNode):
|
||||
self.load(x.identifier)
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTValueNode):
|
||||
self.load(x.value)
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTFunccallNode):
|
||||
if (isinstance(x.callable, ASTValueNode) and isinstance(x.callable.value, ASTIdentifierNode) and x.callable.value.identifier in self.ns.signatures):
|
||||
self.load(f"{x.callable.value.identifier}__{self.ns.signatures[x.callable.value.identifier].call.index(CallArguments.build(x, self.ns))}")
|
||||
else: self.load(x.callable)
|
||||
n = int()
|
||||
|
||||
for i in x.callargs.callargs:
|
||||
self.load(i)
|
||||
n += 1
|
||||
if (x.callargs.starargs):
|
||||
if (n):
|
||||
self.instrs.append(f"BUILD_TUPLE {n}")
|
||||
n = 1
|
||||
for i in x.callargs.starargs:
|
||||
self.load(i)
|
||||
n += 1
|
||||
self.instrs.append(f"BUILD_TUPLE_UNPACK_WITH_CALL {n}")
|
||||
n = 0
|
||||
|
||||
for i in x.callkwargs.callkwargs:
|
||||
self.load(f"'{i[0]}'")
|
||||
self.load(i[1])
|
||||
n += 1
|
||||
if (n and (x.callargs.starargs or x.callkwargs.starkwargs)):
|
||||
self.instrs.append(f"BUILD_MAP {n}")
|
||||
n = 1
|
||||
if (x.callkwargs.starkwargs):
|
||||
for i in x.callkwargs.starkwargs:
|
||||
self.load(i)
|
||||
n += 1
|
||||
self.instrs.append(f"BUILD_MAP_UNPACK_WITH_CALL {n}")
|
||||
n = 1
|
||||
|
||||
self.instrs.append(f"CALL{'EX' if (x.callargs.starargs or x.callkwargs.starkwargs) else 'KW' if (x.callkwargs.callkwargs) else ''} {n}")
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTAttrgetNode):
|
||||
self.load(x.value)
|
||||
assert x.optype.special == '.' # TODO
|
||||
self.instrs.append(f"GETATTR ({x.attr})")
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTUnaryExprNode):
|
||||
self.load(x.value)
|
||||
self.instrs.append(self.unopmap[x.operator.operator])
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTBinaryExprNode):
|
||||
self.load(x.lvalue)
|
||||
char = isinstance(Signature.build(x.lvalue, self.ns), stdlib.char)
|
||||
if (char): self.instrs.append("CALL (ord) 1")
|
||||
if (x.operator.operator == 'xor'): self.instrs.append("BOOL")
|
||||
self.load(x.rvalue)
|
||||
if (char and isinstance(Signature.build(x.rvalue, self.ns), stdlib.char)): self.instrs.append("CALL (ord) 1")
|
||||
if (x.operator.operator == 'xor'): self.instrs.append("BOOL")
|
||||
if (x.operator.operator == 'to'): self.instrs.append("CALL (range) 2")
|
||||
else: self.instrs.append(self.binopmap[x.operator.operator])
|
||||
if (x.operator.operator == 'xor'): self.instrs.append("BOOL")
|
||||
if (char and x.operator.operator not in keyword_operators): self.instrs.append("CALL (chr) 1")
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTItemgetNode):
|
||||
self.load(x.value)
|
||||
self.load(x.key)
|
||||
self.instrs.append("SUBSCR")
|
||||
|
||||
@dispatch
|
||||
def load(self, x: str):
|
||||
self.instrs.append(f"LOAD {f'${x}' if (x in self.cellvars) else f'({x})'}")
|
||||
|
||||
@dispatch
|
||||
def store(self, x: ASTIdentifierNode):
|
||||
self.store(x.identifier)
|
||||
|
||||
@dispatch
|
||||
def store(self, x: str):
|
||||
self.instrs.append(f"STORE {f'${x}' if (x in self.cellvars) else f'({x})'}")
|
||||
|
||||
class PyssemblyCompiler(Compiler):
|
||||
@classmethod
|
||||
def compile_ast(cls, ast, ns, *, filename):
|
||||
instrs = Instrs(name='<module>', ns=ns, filename=filename)
|
||||
instrs.add(ast)
|
||||
#dlog("Instrs:\n"+'\n'.join(instrs.instrs)+'\n')
|
||||
code = instrs.compile().to_code()
|
||||
#dis.show_code(code)
|
||||
#dis.dis(code)
|
||||
#print()
|
||||
return code
|
||||
|
||||
# by Sdore, 2019
|
486
compilers/pyssembly/__init__.py
Normal file
486
compilers/pyssembly/__init__.py
Normal file
@@ -0,0 +1,486 @@
|
||||
#!/usr/bin/python3
|
||||
# Slang Pyssembly compiler target
|
||||
|
||||
import pyssembly
|
||||
from . import std
|
||||
from .. import *
|
||||
from ...ast import *
|
||||
from utils import *
|
||||
|
||||
class Instrs:
|
||||
unopmap = {
|
||||
'+': 'POS',
|
||||
'-': 'NEG',
|
||||
'!': 'NOT',
|
||||
'~': 'INV',
|
||||
'not': 'NOT',
|
||||
}
|
||||
|
||||
binopmap = {
|
||||
'+': 'ADD',
|
||||
'-': 'SUB',
|
||||
'*': 'MUL',
|
||||
'/': 'DIV',
|
||||
'//': 'IDIV',
|
||||
'%': 'MOD',
|
||||
'**': 'POW',
|
||||
'<<': 'LSHIFT',
|
||||
'>>': 'RSHIFT',
|
||||
'&': 'AND',
|
||||
'|': 'OR',
|
||||
'^': 'XOR',
|
||||
'and': 'AND',
|
||||
'or': 'OR',
|
||||
}
|
||||
|
||||
_self_argdef = ASTArgdefNode(Class, ASTIdentifierNode('<self>', lineno=None, offset=None), None, None, lineno=None, offset=None)
|
||||
|
||||
def _class_init(self, *args, **kwargs):
|
||||
getattr(self, '<init>')()
|
||||
getattr(self, f"<constructor ({', '.join(type(i).__name__ for i in (*args, *kwargs.values()))})>")(*args, **kwargs)
|
||||
_class_init = _class_init.__code__.replace(co_name='<new>')
|
||||
|
||||
@init_defaults
|
||||
def __init__(self, *, name, ns, filename, argdefs=(), lastnodens: lambda: [None, None], firstlineno=0):
|
||||
self.name, self.ns, self.filename, self.lastnodens, self.firstlineno = name, ns, filename, lastnodens, firstlineno
|
||||
self.instrs = list()
|
||||
self.consts = list()
|
||||
self.argnames = list()
|
||||
for i in argdefs:
|
||||
if (i.modifier is not None): raise NotImplementedError("argument modifiers are not supported yet")
|
||||
self.argnames.append(i.name.identifier)
|
||||
self.cellvars = self.argnames.copy()
|
||||
self.srclnotab = list()
|
||||
self.lastln = self.firstlineno
|
||||
self._class_init = self._class_init.replace(co_filename=self.filename, co_consts=tuple(i.replace(co_filename=self.filename) if (isinstance(i, CodeType)) else i for i in self._class_init.co_consts))
|
||||
|
||||
def compile(self):
|
||||
return pyssembly.Code('\n'.join(self.instrs), name=self.name, filename=self.filename.strip('"'), srclnotab=self.srclnotab, firstlineno=self.firstlineno, consts=self.consts, argnames=self.argnames)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTRootNode):
|
||||
self.add(x.code)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTCodeNode):
|
||||
for ii, i in enumerate(x.nodes):
|
||||
assert (i.lineno >= self.lastln)
|
||||
if (i.lineno != self.lastln): self.instrs.append(f"#line {i.lineno}")
|
||||
self.lastln = i.lineno
|
||||
self.lastnodens[0] = i
|
||||
self.add(i)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTValueNode):
|
||||
self.add(x.value)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTVardefNode):
|
||||
typesig = Signature.build(x.type, self.ns)
|
||||
if (x.value is not None):
|
||||
self.load(x.value)
|
||||
self.store(x.name)
|
||||
elif (isinstance(typesig, Class)):
|
||||
self.instrs += [
|
||||
"LOAD (object)",
|
||||
"GETATTR (__new__)",
|
||||
]
|
||||
self.load(x.type.type)
|
||||
self.instrs += [
|
||||
"CALL 1",
|
||||
"DUP",
|
||||
"GETATTR <<init>>",
|
||||
"CALL",
|
||||
"POP",
|
||||
]
|
||||
self.store(x.name)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTAssignmentNode):
|
||||
if (x.inplace_operator is not None): self.instrs.append(f"LOAD ({x.name})")
|
||||
self.load(x.value)
|
||||
if (x.inplace_operator is not None): self.instrs.append(f"IP{self.binopmap[x.inplace_operator.operator]}")
|
||||
if (x.isattr):
|
||||
self.instrs += [
|
||||
"LOAD $<self>",
|
||||
f"SETATTR ({x.name})",
|
||||
]
|
||||
else: self.store(x.name)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTUnpackAssignmentNode):
|
||||
assert (x.inplace_operator is None) # TODO
|
||||
self.load(x.value)
|
||||
self.instrs.append(f"UNPACK {len(x.names)}")
|
||||
for name in x.names:
|
||||
self.store(name)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTAttrsetNode):
|
||||
assert (x.assignment.isattr)
|
||||
if (x.assignment.inplace_operator is not None):
|
||||
self.load(x.value)
|
||||
self.instrs += [
|
||||
"DUP",
|
||||
"GETATTR ({x.assignment.name})",
|
||||
]
|
||||
self.load(x.assignment.value)
|
||||
self.instrs += [
|
||||
f"IP{self.binopmap[x.assignment.inplace_operator.operator]}",
|
||||
"ROT",
|
||||
]
|
||||
else:
|
||||
self.load(x.assignment.value)
|
||||
self.load(x.value)
|
||||
self.instrs.append(f"SETATTR ({x.assignment.name})")
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTFunccallNode):
|
||||
self.load(x)
|
||||
self.instrs.append("POP")
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTBlockNode):
|
||||
self.add(x.code)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTFuncdefNode):
|
||||
code_ns = self.ns.derive(x.name.identifier)
|
||||
name = f"{x.name.identifier}({CallArguments(args=tuple(Signature.build(i, code_ns) for i in x.argdefs))})"
|
||||
self.lastnodens[1] = code_ns
|
||||
fname = f"{self.name}.<{x.__fsig__()}>"
|
||||
f_instrs = Instrs(name=fname, ns=code_ns, filename=self.filename, argdefs=x.argdefs, lastnodens=self.lastnodens, firstlineno=x.lineno)
|
||||
f_instrs.add(x.code)
|
||||
#dlog(f"{fname} instrs:\n"+'\n'.join(f_instrs.instrs)+'\n')
|
||||
self.consts.append(f_instrs.compile().to_code())
|
||||
self.lastnodens[1] = self.ns
|
||||
self.instrs += [
|
||||
f"LOAD {len(self.consts)-1}",
|
||||
f"LOAD ('{fname}')",
|
||||
"MKFUNC 0", # TODO: flags
|
||||
]
|
||||
self.store(name)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTClassdefNode):
|
||||
code_ns = self.ns.derive(x.name.identifier)
|
||||
name = x.name.identifier
|
||||
self.lastnodens[1] = code_ns
|
||||
cname = str(name)
|
||||
c_instrs = Instrs(name=cname, ns=code_ns, filename=self.filename, lastnodens=self.lastnodens, firstlineno=x.lineno)
|
||||
c_instrs.consts.append(self._class_init)
|
||||
c_instrs.instrs += [
|
||||
f"LOAD {len(self.consts)-1}",
|
||||
f"LOAD ('<new>')",
|
||||
"MKFUNC 0", # TODO: flags
|
||||
]
|
||||
c_instrs.store('__init__')
|
||||
c_instrs.add(x.code)
|
||||
self.consts.append(c_instrs.compile().to_code())
|
||||
self.lastnodens[1] = self.ns
|
||||
self.instrs += [
|
||||
"LOAD_BUILD_CLASS",
|
||||
f"LOAD {len(self.consts)-1}",
|
||||
f"LOAD ('{cname}')",
|
||||
"MKFUNC 0", # TODO: flags?
|
||||
f"LOAD ('{cname}')",
|
||||
"CALL 2",
|
||||
]
|
||||
self.store(name)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTKeywordExprNode):
|
||||
if (x.keyword.keyword == 'import'):
|
||||
m = re.fullmatch(r'(?:(?:(\w+):)?(?:([\w./]+)/)?([\w.]+):)?([\w*]+)', x.value.identifier)
|
||||
assert (m is not None)
|
||||
namespace, path, pkg, name = m.groups()
|
||||
if (namespace is None): namespace = 'sl'
|
||||
if (path is None): path = '.'
|
||||
if (pkg is None): pkg = name
|
||||
if (namespace == 'py'):
|
||||
assert (path == '.')
|
||||
self.instrs += [
|
||||
"LOAD (0)", # TODO
|
||||
"LOAD (())", # TODO
|
||||
f"IMPORT ({pkg})",
|
||||
]
|
||||
if (name == '*'): self.instrs.append("IMPALL")
|
||||
else:
|
||||
if (name != pkg): self.instrs.append("IMPFROM")
|
||||
self.store(name)
|
||||
elif (namespace == 'sl'):
|
||||
pkg = pkg.replace('.', '/')
|
||||
filename = f"{os.path.join(path, pkg)}.sl"
|
||||
src = open(filename, 'r').read()
|
||||
tl = parse_string(src)
|
||||
ast = build_ast(tl, filename)
|
||||
optimize_ast(ast, validate_ast(ast))
|
||||
instrs = Instrs(name='<module>', ns=validate_ast(ast), filename=filename)
|
||||
instrs.add(ast)
|
||||
code = instrs.compile().to_code()
|
||||
# TODO
|
||||
else: raise WTFException(namespace)
|
||||
elif (x.keyword.keyword == 'return'):
|
||||
self.load(x.value)
|
||||
self.instrs.append("RET")
|
||||
elif (x.keyword.keyword == 'delete'):
|
||||
self.delete(x.value)
|
||||
elif (x.keyword.keyword == 'break'):
|
||||
self.instrs.append("JF :end")
|
||||
else: raise NotImplementedError(x.keyword)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTKeywordDefNode):
|
||||
name = x.name.identifier
|
||||
if (x.keyword.keyword == 'main'):
|
||||
code_ns = self.ns.derive(name)
|
||||
self.lastnodens[1] = code_ns
|
||||
f_instrs = Instrs(name=name, ns=code_ns, filename=self.filename, lastnodens=self.lastnodens, firstlineno=x.lineno)
|
||||
f_instrs.add(x.code)
|
||||
#dlog(f"{name} instrs:\n"+'\n'.join(f_instrs.instrs)+'\n')
|
||||
self.consts.append(f_instrs.compile().to_code())
|
||||
self.lastnodens[1] = self.ns
|
||||
self.instrs += [
|
||||
"LOAD (__name__)",
|
||||
"LOAD ('__main__')",
|
||||
"CMP (==)",
|
||||
"JFP :nomain",
|
||||
f"LOAD {len(self.consts)-1}",
|
||||
f"LOAD ('{name}')",
|
||||
"MKFUNC 0",
|
||||
"CALL 0",
|
||||
"POP",
|
||||
":nomain",
|
||||
]
|
||||
elif (x.keyword.keyword == 'init'):
|
||||
code_ns = self.ns.derive(name)
|
||||
self.lastnodens[1] = code_ns
|
||||
f_instrs = Instrs(name=name, argdefs=(self._self_argdef,), ns=code_ns, filename=self.filename, lastnodens=self.lastnodens, firstlineno=x.lineno)
|
||||
f_instrs.add(x.code)
|
||||
#dlog(f"{name} instrs:\n"+'\n'.join(f_instrs.instrs)+'\n')
|
||||
self.consts.append(f_instrs.compile().to_code())
|
||||
self.lastnodens[1] = self.ns
|
||||
self.instrs += [
|
||||
f"LOAD {len(self.consts)-1}",
|
||||
f"LOAD ('{name}')",
|
||||
"MKFUNC 0",
|
||||
]
|
||||
self.store(name)
|
||||
elif (x.keyword.keyword == 'constr'):
|
||||
code_ns = self.ns.derive(name)
|
||||
self.ns.define(x, redefine=True)
|
||||
self.lastnodens[1] = code_ns
|
||||
name = f"<constructor ({S(', ').join(i.type for i in x.argdefs)})>"
|
||||
f_instrs = Instrs(name=name, ns=code_ns, filename=self.filename, argdefs=(self._self_argdef, *x.argdefs), lastnodens=self.lastnodens, firstlineno=x.lineno)
|
||||
f_instrs.add(x.code)
|
||||
#dlog(f"{name} instrs:\n"+'\n'.join(f_instrs.instrs)+'\n')
|
||||
self.consts.append(f_instrs.compile().to_code())
|
||||
self.lastnodens[1] = self.ns
|
||||
self.instrs += [
|
||||
f"LOAD {len(self.consts)-1}",
|
||||
f"LOAD ('{name}')",
|
||||
"MKFUNC 0", # TODO: flags
|
||||
]
|
||||
self.store(name)
|
||||
else: raise NotImplementedError(x.keyword)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTConditionalNode):
|
||||
self.load(x.condition)
|
||||
self.instrs.append("JFP :else")
|
||||
self.add(x.code)
|
||||
self.instrs += [
|
||||
"JF :end",
|
||||
":else",
|
||||
":end",
|
||||
]
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTForLoopNode):
|
||||
self.load(x.iterable)
|
||||
#self.cellvars.append(x.name.identifier) # TODO FIXME
|
||||
self.instrs += [
|
||||
"ITER",
|
||||
":for",
|
||||
"FOR :else",
|
||||
]
|
||||
self.store(x.name)
|
||||
self.add(x.code) # TODO: stack effect (py38)
|
||||
self.instrs += [
|
||||
"JA :for",
|
||||
":else",
|
||||
":end",
|
||||
]
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTWhileLoopNode):
|
||||
self.instrs += [
|
||||
":while",
|
||||
]
|
||||
self.load(x.condition)
|
||||
self.instrs.append("JFP :else")
|
||||
self.add(x.code) # TODO: stack effect (py38)
|
||||
self.instrs += [
|
||||
"JA :while",
|
||||
":else",
|
||||
":end",
|
||||
]
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTElseClauseNode):
|
||||
ii = -1 - self.instrs[-1].startswith('#line')
|
||||
assert (self.instrs.pop(ii) == ":end")
|
||||
self.add(x.code) # TODO: stack effect (py38)
|
||||
self.instrs.append(":end")
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTLiteralNode):
|
||||
self.instrs.append(f"LOAD ({x.literal})")
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTIdentifierNode):
|
||||
self.load(x.identifier)
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTListNode):
|
||||
for i in x.values:
|
||||
self.load(i)
|
||||
self.instrs.append(f"BUILD_LIST {len(x.values)}")
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTTupleNode):
|
||||
for i in x.values:
|
||||
self.load(i)
|
||||
self.instrs.append(f"BUILD_TUPLE {len(x.values)}")
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTValueNode):
|
||||
self.load(x.value)
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTFunccallNode):
|
||||
if (isinstance(x.callable, ASTValueNode) and isinstance(x.callable.value, ASTIdentifierNode) and x.callable.value.identifier in self.ns.signatures and not isinstance(self.ns.signatures[x.callable.value.identifier], Class)):
|
||||
self.load(f"{x.callable.value.identifier}({CallArguments.build(x, self.ns)})")
|
||||
else: self.load(x.callable)
|
||||
n = int()
|
||||
|
||||
for i in x.callargs.callargs:
|
||||
self.load(i)
|
||||
n += 1
|
||||
if (x.callargs.starargs):
|
||||
if (n):
|
||||
self.instrs.append(f"BUILD_TUPLE {n}")
|
||||
n = 1
|
||||
for i in x.callargs.starargs:
|
||||
self.load(i)
|
||||
n += 1
|
||||
self.instrs.append(f"BUILD_TUPLE_UNPACK_WITH_CALL {n}")
|
||||
n = 0
|
||||
|
||||
for i in x.callkwargs.callkwargs:
|
||||
self.load(f"'{i[0]}'")
|
||||
self.load(i[1])
|
||||
n += 1
|
||||
if (n and (x.callargs.starargs or x.callkwargs.starkwargs)):
|
||||
self.instrs.append(f"BUILD_MAP {n}")
|
||||
n = 1
|
||||
if (x.callkwargs.starkwargs):
|
||||
for i in x.callkwargs.starkwargs:
|
||||
self.load(i)
|
||||
n += 1
|
||||
self.instrs.append(f"BUILD_MAP_UNPACK_WITH_CALL {n}")
|
||||
n = 1
|
||||
|
||||
self.instrs.append(f"CALL{'EX' if (x.callargs.starargs or x.callkwargs.starkwargs) else 'KW' if (x.callkwargs.callkwargs) else ''} {n}")
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTAttrgetNode):
|
||||
self.load(x.value)
|
||||
assert (x.optype.special == '.') # TODO
|
||||
self.instrs.append(f"GETATTR ({x.attr})")
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTUnaryExprNode):
|
||||
self.load(x.value)
|
||||
self.instrs.append(self.unopmap[x.operator.operator])
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTBinaryExprNode):
|
||||
self.load(x.lvalue)
|
||||
char = isinstance(Signature.build(x.lvalue, self.ns), stdlib.char)
|
||||
if (char): self.instrs.append("CALL (ord) 1")
|
||||
if (x.operator.operator == 'xor'): self.instrs.append("BOOL")
|
||||
self.load(x.rvalue)
|
||||
if (char and isinstance(Signature.build(x.rvalue, self.ns), stdlib.char)): self.instrs.append("CALL (ord) 1")
|
||||
if (x.operator.operator == 'xor'): self.instrs.append("BOOL")
|
||||
if (x.operator.operator == 'to'): self.instrs.append("CALL (range) 2")
|
||||
else: self.instrs.append(f"CMP ({x.operator.operator})" if (x.operator.operator in dis.cmp_op) else self.binopmap[x.operator.operator])
|
||||
if (x.operator.operator == 'xor'): self.instrs.append("BOOL")
|
||||
if (char and x.operator.operator not in logical_operators): self.instrs.append("CALL (chr) 1")
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTItemgetNode):
|
||||
self.load(x.value)
|
||||
self.load(x.key)
|
||||
self.instrs.append("SUBSCR")
|
||||
|
||||
@dispatch
|
||||
def load(self, x: str):
|
||||
self.instrs.append(f"LOAD {f'${x}' if (x in self.cellvars) else f'<{x}>'}")
|
||||
|
||||
@dispatch
|
||||
def load(self, x):
|
||||
self.instrs.append(f"LOAD_CONST ({repr(x)})")
|
||||
|
||||
@dispatch
|
||||
def store(self, x: ASTIdentifierNode):
|
||||
self.store(x.identifier)
|
||||
|
||||
@dispatch
|
||||
def store(self, x: str):
|
||||
self.instrs.append(f"STORE {f'${x}' if (x in self.cellvars) else f'<{x}>'}")
|
||||
|
||||
@dispatch
|
||||
def delete(self, x: ASTIdentifierNode):
|
||||
self.delete(x.identifier)
|
||||
|
||||
@dispatch
|
||||
def delete(self, x: str):
|
||||
self.instrs.append(f"DELETE {f'${x}' if (x in self.cellvars) else f'<{x}>'}")
|
||||
|
||||
class PyssemblyCompiler(Compiler):
|
||||
ext = '.pyc'
|
||||
|
||||
@classmethod
|
||||
def compile_ast(cls, ast, ns, *, filename):
|
||||
instrs = Instrs(name='<module>', ns=ns, filename=filename)
|
||||
|
||||
instrs.consts.append(compile(open(std.__file__).read(), '<std>', 'exec'))
|
||||
instrs.instrs += [
|
||||
f"LOAD {len(instrs.consts)-1}",
|
||||
"LOAD ('<std>')",
|
||||
"MKFUNC 0", # TODO?
|
||||
"CALL",
|
||||
]
|
||||
|
||||
try: instrs.add(ast)
|
||||
except Exception as ex: raise SlCompilationError('Compilation error', instrs.lastnodens[0], scope=instrs.lastnodens[1].scope) from ex
|
||||
|
||||
#dlog("Instrs:\n"+'\n'.join(instrs.instrs)+'\n')
|
||||
|
||||
try:
|
||||
code = instrs.compile().to_code()
|
||||
#dis.show_code(code); dis.dis(code); print()
|
||||
code = pyssembly.asm(code)
|
||||
except pyssembly.PyssemblyError as ex:
|
||||
print('Error:', ex)
|
||||
try: code = ex.code.to_code()
|
||||
except pyssembly.PyssemblyError: pass
|
||||
else:
|
||||
print("\nHere is full pyssembly code 'til the errorneous line:\n")
|
||||
dis.dis(code)
|
||||
raise SlCompilationError('Pyssembly error', ast, scope=instrs.ns) from ex
|
||||
|
||||
return code
|
||||
|
||||
# by Sdore, 2020
|
2
compilers/pyssembly/std.py
Normal file
2
compilers/pyssembly/std.py
Normal file
@@ -0,0 +1,2 @@
|
||||
class stdio:
|
||||
println = print
|
1
compilers/pyssembly/typeshed
Submodule
1
compilers/pyssembly/typeshed
Submodule
Submodule compilers/pyssembly/typeshed added at b44cd294c4
247
compilers/sbc.py
247
compilers/sbc.py
@@ -6,7 +6,11 @@ from Slang.ast import *
|
||||
from utils import *
|
||||
|
||||
NOP = 0x00
|
||||
RET = 0x01
|
||||
END = 0x01
|
||||
POP = 0x02
|
||||
RET = 0x03
|
||||
BLTIN = 0x04
|
||||
CODE = 0x05
|
||||
|
||||
POS = 0x10
|
||||
NEG = 0x11
|
||||
@@ -24,15 +28,28 @@ ADD = 0x20
|
||||
SUB = 0x21
|
||||
MUL = 0x22
|
||||
DIV = 0x23
|
||||
FLRDIV = 0x24
|
||||
IDIV = 0x24
|
||||
MOD = 0x25
|
||||
POW = 0x26
|
||||
SHL = 0x27
|
||||
SHR = 0x28
|
||||
LSH = 0x27
|
||||
RSH = 0x28
|
||||
AND = 0x29
|
||||
OR = 0x2a
|
||||
XOR = 0x2b
|
||||
|
||||
EQ = 0x30
|
||||
NE = 0x31
|
||||
LT = 0x32
|
||||
GT = 0x33
|
||||
LE = 0x34
|
||||
GE = 0x35
|
||||
IS = 0x36
|
||||
ISNOT = 0x37
|
||||
|
||||
IF = 0x40
|
||||
ELSE = 0x41
|
||||
EXEC = 0x42
|
||||
|
||||
ALLOC = 0xa0
|
||||
EXTEND = 0xa1
|
||||
CONST = 0xa2
|
||||
@@ -40,26 +57,81 @@ JUMPF = 0xa3
|
||||
JUMPB = 0xa4
|
||||
SCPGET = 0xa5
|
||||
SCPSET = 0xa6
|
||||
CALL = 0xa7
|
||||
|
||||
HASARG = 0xa0
|
||||
|
||||
def readVarInt(s):
|
||||
r = int()
|
||||
i = int()
|
||||
while (True):
|
||||
b = s.recv(1)[0]
|
||||
r |= (b & (1 << 7)-1) << (7*i)
|
||||
if (not b & (1 << 7)): break
|
||||
i += 1
|
||||
return r
|
||||
|
||||
def writeVarInt(v):
|
||||
assert v >= 0
|
||||
r = bytearray()
|
||||
while (True):
|
||||
c = v & (1 << 7)-1
|
||||
v >>= 7
|
||||
if (v): c |= (1 << 7)
|
||||
r.append(c)
|
||||
if (not v): break
|
||||
return bytes(r)
|
||||
|
||||
class Instrs:
|
||||
unops = '+-!~'
|
||||
binops = (*'+-*/%', '**', '<<', '>>', '&', '|', '^')
|
||||
unopmap = {
|
||||
'+': POS,
|
||||
'-': NEG,
|
||||
'!': NOT,
|
||||
'~': INV,
|
||||
'not': NOT,
|
||||
}
|
||||
binopmap = {
|
||||
'+': ADD,
|
||||
'-': SUB,
|
||||
'*': MUL,
|
||||
'/': DIV,
|
||||
'//': IDIV,
|
||||
'%': MOD,
|
||||
'**': POW,
|
||||
'<<': LSH,
|
||||
'>>': RSH,
|
||||
'&': AND,
|
||||
'|': OR,
|
||||
'^': XOR,
|
||||
|
||||
'==': EQ,
|
||||
'!=': NE,
|
||||
'<': LT,
|
||||
'>': GT,
|
||||
'<=': LE,
|
||||
'>=': GE,
|
||||
'is': IS,
|
||||
'is not': ISNOT,
|
||||
}
|
||||
|
||||
@init_defaults
|
||||
def __init__(self, *, name, ns, filename, argdefs=()):
|
||||
self.ns = ns
|
||||
def __init__(self, *, name, ns, filename, scpcells: indexset):
|
||||
self.name, self.ns, self.filename, self.scpcells = name, ns, filename, scpcells
|
||||
self.instrs = bytearray()
|
||||
|
||||
def compile(self):
|
||||
raise TODO
|
||||
return bytes(self.instrs)
|
||||
|
||||
@dispatch
|
||||
def add(self, opcode: int, oparg: int = None):
|
||||
def add(self, opcode: lambda x: isinstance(x, int) and x < HASARG):
|
||||
self.instrs.append(opcode)
|
||||
if (opcode >= HASARG): self.instrs.append(oparg)
|
||||
else: assert oparg is None
|
||||
|
||||
@dispatch
|
||||
def add(self, opcode: lambda x: isinstance(x, int) and x >= HASARG, oparg: int):
|
||||
self.instrs.append(opcode)
|
||||
self.instrs.append(oparg)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTRootNode):
|
||||
@@ -67,7 +139,6 @@ class Instrs:
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTCodeNode):
|
||||
lastln = int()
|
||||
for i in x.nodes:
|
||||
self.add(i)
|
||||
|
||||
@@ -83,15 +154,14 @@ class Instrs:
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTAssignmentNode):
|
||||
if (x.inplace_operator is not None): self.instrs.append(f"LOAD ({x.name})")
|
||||
if (x.inplace_operator is not None): raise NotImplementedError()
|
||||
self.load(x.value)
|
||||
if (x.inplace_operator is not None): self.instrs.append(f"INP_{self.binopmap[x.inplace_operator.operator]}")
|
||||
self.store(x.name)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTFunccallNode):
|
||||
self.load(x)
|
||||
self.instrs.append("POP")
|
||||
self.add(POP)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTBlockNode):
|
||||
@@ -101,46 +171,52 @@ class Instrs:
|
||||
def add(self, x: ASTFuncdefNode):
|
||||
code_ns = self.ns.derive(x.name.identifier)
|
||||
name = f"{x.name.identifier}__{self.ns.signatures[x.name.identifier].call.index(CallArguments(args=tuple(Signature.build(i, code_ns) for i in x.argdefs)))}"
|
||||
f_instrs = Instrs(name=f"{self.name}.{name}", ns=code_ns, filename=self.filename, argdefs=x.argdefs)
|
||||
fname = f"{self.name}.<{x.__fsig__()}>"
|
||||
self.scpcells[name]
|
||||
f_instrs = Instrs(name=fname, ns=code_ns, filename=self.filename, scpcells=self.scpcells.copy())
|
||||
for i in x.argdefs:
|
||||
f_instrs.scpcells[i.name.identifier]
|
||||
f_instrs.add(x.code)
|
||||
#dlog(f"{x.__fsig__()} instrs:\n"+'\n'.join(f_instrs.instrs)+'\n')
|
||||
self.consts.append(f_instrs.compile().to_code())
|
||||
self.instrs += [
|
||||
f"LOAD {len(self.consts)-1}",
|
||||
f"LOAD ('{self.name}.{name}')",
|
||||
f"MAKE_FUNCTION 0", # TODO: flags
|
||||
]
|
||||
self.add(CODE)
|
||||
self.instrs += f_instrs.instrs
|
||||
self.add(END)
|
||||
self.store(name+'.len')
|
||||
self.store(name)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTKeywordExprNode):
|
||||
if (x.keyword.keyword == 'import'):
|
||||
ns, _, name = x.value.identifier.partition('::')
|
||||
assert ns == 'py'
|
||||
self.instrs += [
|
||||
"LOAD (0)", # TODO
|
||||
"LOAD (None)", # TODO
|
||||
]
|
||||
self.instrs.append(f"IMPORT_NAME ({name})")
|
||||
raise TODO
|
||||
self.store(name)
|
||||
elif (x.keyword.keyword == 'return'):
|
||||
self.load(x.value)
|
||||
self.instrs.append("RET")
|
||||
self.add(RET)
|
||||
else: raise NotImplementedError(x.keyword)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTKeywordDefNode):
|
||||
if (x.keyword.keyword == 'main'):
|
||||
name = '<main>'
|
||||
code_ns = self.ns.derive(name)
|
||||
f_instrs = Instrs(name=name, ns=code_ns, filename=self.filename)
|
||||
f_instrs.add(x.code)
|
||||
self.add(CODE)
|
||||
self.instrs += f_instrs.instrs
|
||||
self.add(END)
|
||||
self.add(EXEC)
|
||||
self.add(POP)
|
||||
else: raise NotImplementedError(x.keyword)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTConditionalNode):
|
||||
self.load(x.condition)
|
||||
self.instrs.append("JPOPF :else")
|
||||
self.add(IF)
|
||||
self.add(x.code)
|
||||
self.instrs += [
|
||||
"JUMPF :end",
|
||||
":else",
|
||||
":end",
|
||||
]
|
||||
self.add(END)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTForLoopNode):
|
||||
#@dispatch
|
||||
def add_(self, x: ASTForLoopNode):
|
||||
self.load(x.iterable)
|
||||
#self.cellvars.append(x.name.identifier) # TODO FIXME
|
||||
self.instrs += [
|
||||
@@ -158,8 +234,8 @@ class Instrs:
|
||||
":end",
|
||||
]
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTWhileLoopNode):
|
||||
#@dispatch
|
||||
def add_(self, x: ASTWhileLoopNode):
|
||||
self.instrs += [
|
||||
"SETUP_LOOP :end",
|
||||
":while",
|
||||
@@ -174,8 +250,8 @@ class Instrs:
|
||||
":end",
|
||||
]
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTElseClauseNode):
|
||||
#@dispatch
|
||||
def add_(self, x: ASTElseClauseNode):
|
||||
assert (self.instrs[-1] == ":end")
|
||||
popped = [self.instrs.pop()]
|
||||
if (self.instrs[-1] == "POP_BLOCK"): popped.append(self.instrs.pop())
|
||||
@@ -184,7 +260,16 @@ class Instrs:
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTLiteralNode):
|
||||
self.instrs.append(f"LOAD ({x.literal})")
|
||||
sig = Signature.build(x, ns=self.ns)
|
||||
if (hasattr(sig, 'fmt')):
|
||||
v = struct.pack(sig.fmt, int(x.literal))
|
||||
elif (isinstance(sig, stdlib.int)):
|
||||
v = writeVarInt(int(x.literal))
|
||||
elif (isinstance(sig, stdlib.str)):
|
||||
v = x.literal.encode('utf8')
|
||||
else: raise NotImplementedError(sig)
|
||||
self.add(CONST, len(v))
|
||||
self.instrs += v
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTIdentifierNode):
|
||||
@@ -196,42 +281,29 @@ class Instrs:
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTFunccallNode):
|
||||
if (isinstance(x.callable, ASTValueNode) and isinstance(x.callable.value, ASTIdentifierNode) and x.callable.value.identifier in self.ns.signatures):
|
||||
self.load(f"{x.callable.value.identifier}__{self.ns.signatures[x.callable.value.identifier].call.index(CallArguments.build(x, self.ns))}")
|
||||
else: self.load(x.callable)
|
||||
n = int()
|
||||
nargs = int()
|
||||
|
||||
for i in x.callargs.callargs:
|
||||
self.load(i)
|
||||
n += 1
|
||||
if (x.callargs.starargs):
|
||||
if (n):
|
||||
self.instrs.append(f"BUILD_TUPLE {n}")
|
||||
n = 1
|
||||
for i in x.callargs.starargs:
|
||||
self.load(i)
|
||||
n += 1
|
||||
self.instrs.append(f"BUILD_TUPLE_UNPACK_WITH_CALL {n}")
|
||||
n = 0
|
||||
nargs += 1
|
||||
if (x.callargs.starargs): raise NotImplementedError()
|
||||
|
||||
for i in x.callkwargs.callkwargs:
|
||||
self.load(f"'{i[0]}'")
|
||||
self.load(i[1])
|
||||
n += 1
|
||||
if (n and (x.callargs.starargs or x.callkwargs.starkwargs)):
|
||||
self.instrs.append(f"BUILD_MAP {n}")
|
||||
n = 1
|
||||
if (x.callkwargs.starkwargs):
|
||||
for i in x.callkwargs.starkwargs:
|
||||
self.load(i)
|
||||
n += 1
|
||||
self.instrs.append(f"BUILD_MAP_UNPACK_WITH_CALL {n}")
|
||||
n = 1
|
||||
if (x.callkwargs.callkwargs): raise NotImplementedError()
|
||||
if (x.callkwargs.starkwargs): raise NotImplementedError()
|
||||
|
||||
self.instrs.append(f"CALL{'EX' if (x.callargs.starargs or x.callkwargs.starkwargs) else 'KW' if (x.callkwargs.callkwargs) else ''} {n}")
|
||||
if (isinstance(x.callable, ASTValueNode) and isinstance(x.callable.value, ASTIdentifierNode) and x.callable.value.identifier in self.ns.signatures):
|
||||
name = f"{x.callable.value.identifier}__{self.ns.signatures[x.callable.value.identifier].call.index(CallArguments.build(x, self.ns))}"
|
||||
self.load(name)
|
||||
self.load(name+'.len')
|
||||
self.add(EXEC)
|
||||
else: # builtin
|
||||
#self.add(ITOA) # TODO FIXME cast
|
||||
self.add(BLTIN)
|
||||
self.instrs += x.callable.value.identifier.encode('ascii')+b'\0'
|
||||
self.add(CALL, nargs)
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTAttrgetNode):
|
||||
#@dispatch
|
||||
def load_(self, x: ASTAttrgetNode):
|
||||
self.load(x.value)
|
||||
assert x.optype.special == '.' # TODO
|
||||
self.instrs.append(f"GETATTR ({x.attr})")
|
||||
@@ -239,31 +311,24 @@ class Instrs:
|
||||
@dispatch
|
||||
def load(self, x: ASTUnaryExprNode):
|
||||
self.load(x.value)
|
||||
self.instrs.append(self.unopmap[x.operator.operator])
|
||||
self.add(self.unopmap[x.operator.operator])
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTBinaryExprNode):
|
||||
self.load(x.lvalue)
|
||||
char = isinstance(Signature.build(x.lvalue, self.ns), stdlib.char)
|
||||
if (char): self.instrs.append("CALL (ord) 1")
|
||||
if (x.operator.operator == 'xor'): self.instrs.append("BOOL")
|
||||
self.load(x.rvalue)
|
||||
if (char and isinstance(Signature.build(x.rvalue, self.ns), stdlib.char)): self.instrs.append("CALL (ord) 1")
|
||||
if (x.operator.operator == 'xor'): self.instrs.append("BOOL")
|
||||
if (x.operator.operator == 'to'): self.instrs.append("CALL (range) 2")
|
||||
else: self.instrs.append(self.binopmap[x.operator.operator])
|
||||
if (x.operator.operator == 'xor'): self.instrs.append("BOOL")
|
||||
if (char and x.operator.operator not in keyword_operators): self.instrs.append("CALL (chr) 1")
|
||||
if (x.operator.operator == 'to'): raise NotImplementedError()
|
||||
else: self.add(self.binopmap[x.operator.operator])
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTItemgetNode):
|
||||
#@dispatch
|
||||
def load_(self, x: ASTItemgetNode):
|
||||
self.load(x.value)
|
||||
self.load(x.key)
|
||||
self.instrs.append("SUBSCR")
|
||||
|
||||
@dispatch
|
||||
def load(self, x: str):
|
||||
self.instrs.append(f"LOAD {f'${x}' if (x in self.cellvars) else f'({x})'}")
|
||||
self.add(SCPGET, self.scpcells[x])
|
||||
|
||||
@dispatch
|
||||
def store(self, x: ASTIdentifierNode):
|
||||
@@ -271,18 +336,16 @@ class Instrs:
|
||||
|
||||
@dispatch
|
||||
def store(self, x: str):
|
||||
self.instrs.append(f"STORE {f'${x}' if (x in self.cellvars) else f'({x})'}")
|
||||
self.add(SCPSET, self.scpcells[x])
|
||||
|
||||
class SBCCompiler(Compiler):
|
||||
ext = '.sbc'
|
||||
|
||||
class PyssemblyCompiler(Compiler):
|
||||
@classmethod
|
||||
def compile_ast(cls, ast, ns, *, filename):
|
||||
instrs = Instrs(name='<module>', ns=ns, filename=filename)
|
||||
instrs.add(ast)
|
||||
#dlog("Instrs:\n"+'\n'.join(instrs.instrs)+'\n')
|
||||
code = instrs.compile().to_code()
|
||||
#dis.show_code(code)
|
||||
#dis.dis(code)
|
||||
#print()
|
||||
code = instrs.compile()
|
||||
return code
|
||||
|
||||
# by Sdore, 2019
|
||||
|
Reference in New Issue
Block a user