mirror of
https://github.com/egormanga/Slang.git
synced 2025-08-01 01:16:59 +03:00
Latest changes back from 2021, before starting from scratch.
This commit is contained in:
@@ -1,40 +1,24 @@
|
||||
#!/usr/bin/python3
|
||||
# Slang compilers
|
||||
|
||||
from ..ast import SlSyntaxError, SlValidationError
|
||||
from ..ast import Slots, lstripcount, SlSyntaxError, SlNodeException, SlValidationError
|
||||
import abc, traceback
|
||||
|
||||
class Compiler(abc.ABC):
|
||||
ext = ''
|
||||
|
||||
@abc.abstractclassmethod
|
||||
def compile_ast(cls, ast):
|
||||
pass
|
||||
|
||||
def lstripcount(s, chars): # TODO: commonize
|
||||
for ii, i in enumerate(s):
|
||||
if (i not in chars): break
|
||||
else: ii = 0
|
||||
return (ii, s[ii:])
|
||||
class SlCompilationError(SlNodeException):
|
||||
desc: ...
|
||||
|
||||
class SlCompilationError(Exception):
|
||||
__slots__ = ('desc', 'node', 'line', 'scope')
|
||||
def __init__(self, desc, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.desc = desc
|
||||
|
||||
def __init__(self, desc, node, line='', *, scope=None):
|
||||
self.desc, self.node, self.line, self.scope = desc, node, line, scope
|
||||
def __exline__(self):
|
||||
return f"Compilation error: {self.desc}"
|
||||
|
||||
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)+'\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):
|
||||
return f" at line {self.node.lineno}, offset {self.node.offset}"
|
||||
|
||||
@property
|
||||
def lineno(self):
|
||||
return self.node.lineno
|
||||
|
||||
# by Sdore, 2019
|
||||
# by Sdore, 2021
|
||||
|
@@ -3,7 +3,7 @@
|
||||
# https://esolangs.org/wiki/Gibberish_(programming_language)
|
||||
|
||||
from .. import *
|
||||
from Slang.ast import *
|
||||
from ...ast import *
|
||||
from utils import *
|
||||
|
||||
class Instrs:
|
||||
@@ -132,4 +132,6 @@ class GibberishCompiler(Compiler):
|
||||
dlog("Code:\n"+code.decode())
|
||||
return code
|
||||
|
||||
# by Sdore, 2019
|
||||
compiler = GibberishCompiler
|
||||
|
||||
# by Sdore, 2020
|
||||
|
13
compilers/native/__init__.py
Normal file
13
compilers/native/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/python3
|
||||
# Slang Native (ASM) compiler target
|
||||
|
||||
from .. import *
|
||||
from ...ast import *
|
||||
from utils import *
|
||||
|
||||
class NativeCompiler(Compiler): pass
|
||||
|
||||
try: compiler = importlib.import_module(f".{platform.machine()}", __package__).compiler
|
||||
except ModuleNotFoundError: compiler = NativeCompiler
|
||||
|
||||
# by Sdore, 2021
|
235
compilers/native/x86_64.py
Normal file
235
compilers/native/x86_64.py
Normal file
@@ -0,0 +1,235 @@
|
||||
#!/usr/bin/python3
|
||||
# Slang Native (ASM) x86_64 compiler target
|
||||
|
||||
from . import *
|
||||
|
||||
word_size = 8
|
||||
registers = ('rax', 'rcx', 'rdx', 'rbx', 'rsi', 'rdi', 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15')
|
||||
regs64 = {i: () for i in registers}
|
||||
regs32 = {'e'+i: ('r'+i, *regs64['r'+i]) for i in ('ax', 'cx', 'dx', 'bx', 'si', 'di')}
|
||||
regs16 = {i: ('e'+i, *regs32['e'+i]) for i in ('ax', 'cx', 'dx', 'bx', 'si', 'di')}
|
||||
regs8 = {i+j: (i+'x', *regs16[i+'x']) for i in 'acdb' for j in 'lh'}
|
||||
|
||||
class Instrs:
|
||||
binopmap = {
|
||||
'+': 'add',
|
||||
}
|
||||
stacksize = 0x10 # TODO
|
||||
|
||||
@init_defaults
|
||||
def __init__(self, *, name, ns, filename, nargs: int, freeregs: set):
|
||||
self.name, self.ns, self.filename, self.nargs, self.freeregs = name, ns, filename, nargs, freeregs
|
||||
self.instrs = list()
|
||||
self.varoff = bidict.OrderedBidict()
|
||||
self.varsize = dict()
|
||||
self.regpool = list(registers)
|
||||
self.clobbered = set()
|
||||
|
||||
def compile(self):
|
||||
saveregs = tuple(sorted(self.clobbered - self.freeregs))
|
||||
instrs = [
|
||||
"push rbp",
|
||||
"mov rbp, rsp",
|
||||
*(f"push {i}" for i in saveregs),
|
||||
f"sub rsp, {self.stacksize}",
|
||||
'\n',
|
||||
*self.instrs,
|
||||
'\n',
|
||||
*(f"pop {i}" for i in saveregs[::-1]),
|
||||
"leave",
|
||||
f"ret {self.nargs}" if (self.nargs > 0) else "ret",
|
||||
]
|
||||
return f"global {self.name}\n{self.name}:\n\t"+'\n\t'.join('\t'.join(i.split(maxsplit=1)) for i in instrs)+'\n'
|
||||
|
||||
def var(self, name):
|
||||
if (self.varoff[name] == 0): return '[rbp]'
|
||||
return "[rbp%+d]" % -self.varoff[name]
|
||||
|
||||
@contextlib.contextmanager
|
||||
def vars(self, *names):
|
||||
yield tuple(map(self.var, names))
|
||||
|
||||
@contextlib.contextmanager
|
||||
def regs(self, n):
|
||||
regs = {self.regpool.pop(0) for _ in range(n)}
|
||||
self.clobbered |= regs
|
||||
try: yield regs
|
||||
finally: self.regpool = [i for i in registers if i in {*self.regpool, *regs}]
|
||||
|
||||
#def syscall(self, rax, rdi=None, rsi=None, rdx=None, r10=None, r8=None, r9=None):
|
||||
# self.instrs.append("
|
||||
|
||||
@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):
|
||||
name = x.name.identifier
|
||||
try:
|
||||
ln, lo = last(self.varoff.items())
|
||||
self.varoff[name] = lo+self.varsize[ln]
|
||||
except StopIteration: self.varoff[name] = word_size
|
||||
self.varsize[name] = self.sizeof(x.type)
|
||||
if (x.value is not None): self.set(x.name, x.value)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTFunccallNode):
|
||||
callarguments = CallArguments.build(x, self.ns)
|
||||
fsig = Signature.build(x.callable, self.ns)
|
||||
fcall = fsig.compatible_call(callarguments, self.ns)
|
||||
if (fcall is None): raise TODO(fcall)
|
||||
|
||||
n = int()
|
||||
if (fcall[0] is not None): fname = f"{fsig.name}({CallArguments(args=fcall[0], ns=self.ns)})"
|
||||
else:
|
||||
if (isinstance(x.callable.value, ASTAttrgetNode)):
|
||||
ofsig = Signature.build(x.callable.value.value, self.ns)
|
||||
if (isinstance(fsig, stdlib.Builtin)):
|
||||
fname = x.callable
|
||||
else: raise NotImplementedError(ofsig)
|
||||
else: raise NotImplementedError(x.callable.value)
|
||||
|
||||
for i in x.callargs.callargs:
|
||||
self.push(i)
|
||||
n += 1
|
||||
|
||||
if (x.callargs.starargs or x.callkwargs.callkwargs or x.callkwargs.starkwargs): raise NotImplementedError()
|
||||
|
||||
self.instrs.append(f"call {fname}")
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTValueNode, *reg):
|
||||
return self.load(x.value, *reg)
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTLiteralNode):
|
||||
return x.literal
|
||||
|
||||
@dispatch
|
||||
def load(self, x):
|
||||
with self.regs(1) as (reg,):
|
||||
self.load(x, reg)
|
||||
return reg
|
||||
#reg = self.regpool.pop(0)
|
||||
#self.clobbered.add(reg)
|
||||
#self.load(x, reg)
|
||||
#return reg
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTIdentifierNode, reg):
|
||||
self.instrs.append(f"mov {reg}, {self.var(x.identifier)}")
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTBinaryExprNode, reg):
|
||||
self.load(x.lvalue, reg)
|
||||
self.instrs.append(f"{self.binopmap[x.operator.operator]} {reg}, {self.load(x.rvalue)}")
|
||||
|
||||
@dispatch
|
||||
def load(self, x: str, reg):
|
||||
self.instrs.append(f"mov {reg}, {x}")
|
||||
|
||||
@dispatch
|
||||
def set(self, name: ASTIdentifierNode, value):
|
||||
self.set(name.identifier, value)
|
||||
|
||||
@dispatch
|
||||
def set(self, name: str, value: ASTValueNode):
|
||||
self.set(name, value.value)
|
||||
|
||||
@dispatch
|
||||
def set(self, name: str, value: ASTLiteralNode):
|
||||
self.set(name, self.load(value))
|
||||
|
||||
@dispatch
|
||||
def set(self, name: str, value):
|
||||
with self.vars(name) as (var,):
|
||||
self.instrs.append(f"mov {var}, {self.load(value)}")
|
||||
|
||||
def push(self, x):
|
||||
self.instrs.append(f"push {self.load(x)}")
|
||||
|
||||
@dispatch
|
||||
def sizeof(self, x: ASTTypedefNode):
|
||||
return self.sizeof(Signature.build(x, self.ns))
|
||||
|
||||
@dispatch
|
||||
def sizeof(self, x: lambda x: hasattr(x, 'fmt')):
|
||||
return struct.calcsize(x.fmt)
|
||||
|
||||
class BuiltinInstrs(Instrs):
|
||||
@init_defaults
|
||||
def __init__(self, *, freeregs: set):
|
||||
self.freeregs = freeregs
|
||||
|
||||
@singleton
|
||||
class builtin_stdio_println(BuiltinInstrs):
|
||||
name = 'stdio.println'
|
||||
instrs = [
|
||||
"mov al, [rbp+0x10]",
|
||||
"add al, '0'",
|
||||
"mov [rbp-2], al",
|
||||
"mov al, 10",
|
||||
"mov [rbp-1], al",
|
||||
|
||||
"mov rax, 1 ; sys_write",
|
||||
"mov rdi, 1 ; stdout",
|
||||
"lea rsi, [rbp-2] ; buf",
|
||||
"mov rdx, 2 ; count",
|
||||
"syscall",
|
||||
]
|
||||
nargs = 1
|
||||
stacksize = 2
|
||||
clobbered = {'rax', 'rcx', 'rdx', 'rdi', 'rsi', 'r11'}
|
||||
|
||||
class x86_64Compiler(NativeCompiler):
|
||||
ext = '.o'
|
||||
|
||||
header = """
|
||||
section .text
|
||||
|
||||
global _start
|
||||
_start:
|
||||
call main
|
||||
mov rax, 60 ; sys_exit
|
||||
mov rdi, 0 ; error_code
|
||||
syscall
|
||||
hlt
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def compile_ast(cls, ast, ns, *, filename):
|
||||
instrs = Instrs(name='main', ns=ns, filename=filename)
|
||||
instrs.add(ast)
|
||||
src = f"# {filename}\n{S(cls.header).unindent().strip()}\n"
|
||||
src += '\n'+builtin_stdio_println.compile()
|
||||
src += '\n'+instrs.compile()
|
||||
|
||||
sname = os.path.splitext(filename)[0]+'.s'
|
||||
sfd = open(sname, 'w')
|
||||
try:
|
||||
sfd.write(src)
|
||||
sfd.close()
|
||||
log("Src:\n"+src)
|
||||
|
||||
ofd, oname = tempfile.mkstemp(prefix=f"slc-{os.path.splitext(filename)[0]}-", suffix='.o')
|
||||
try:
|
||||
subprocess.run(('yasm', '-felf64', '-o', oname, sname), stderr=subprocess.PIPE, text=True, check=True)
|
||||
return os.fdopen(ofd, 'rb').read()
|
||||
except subprocess.CalledProcessError as ex: raise SlCompilationError('\n'+S(ex.stderr).indent(), ast, scope=instrs.ns) from ex
|
||||
finally:
|
||||
try: os.remove(oname)
|
||||
except OSError: pass
|
||||
finally:
|
||||
sfd.close()
|
||||
try: os.remove(sname)
|
||||
except OSError: pass
|
||||
|
||||
compiler = x86_64Compiler
|
||||
|
||||
# by Sdore, 2021
|
1
compilers/pyssembly/.gitignore
vendored
Normal file
1
compilers/pyssembly/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
stdlib/
|
@@ -38,7 +38,7 @@ class Instrs:
|
||||
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>')
|
||||
_class_init = code_with(_class_init.__code__, co_name='<new>')
|
||||
|
||||
@init_defaults
|
||||
def __init__(self, *, name, ns, filename, argdefs=(), lastnodens: lambda: [None, None], firstlineno=0):
|
||||
@@ -52,7 +52,7 @@ class Instrs:
|
||||
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))
|
||||
self._class_init = code_with(self._class_init, co_filename=self.filename, co_consts=tuple(code_with(i, 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)
|
||||
@@ -146,7 +146,7 @@ class Instrs:
|
||||
@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))})"
|
||||
name = f"{x.name.identifier}({CallArguments(args=x.argdefs, ns=code_ns)})"
|
||||
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)
|
||||
@@ -214,8 +214,9 @@ class Instrs:
|
||||
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)
|
||||
if (x.flags.optimized): optimize_ast(ast, validate_ast(ast))
|
||||
ns = validate_ast(ast)
|
||||
instrs = Instrs(name=filename, ns=ns, filename=filename)
|
||||
instrs.add(ast)
|
||||
code = instrs.compile().to_code()
|
||||
# TODO
|
||||
@@ -359,36 +360,54 @@ 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 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)
|
||||
#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)):
|
||||
callarguments = CallArguments.build(x, self.ns)
|
||||
fsig = Signature.build(x.callable, self.ns)
|
||||
fcall = fsig.compatible_call(callarguments, self.ns)
|
||||
if (fcall is None): raise TODO(fcall)
|
||||
n = int()
|
||||
if (fcall[0] is not None): self.load(f"{fsig.name}({CallArguments(args=fcall[0], ns=self.ns)})")
|
||||
else:
|
||||
if (isinstance(x.callable.value, ASTAttrgetNode)):
|
||||
ofsig = Signature.build(x.callable.value.value, self.ns)
|
||||
if (type(ofsig) is Function):
|
||||
ofcall = ofsig.compatible_call(callarguments, self.ns)
|
||||
assert (ofcall is not None)
|
||||
self.load(fsig.name)
|
||||
self.load(f"{ofsig.name}({CallArguments(args=ofcall[0], ns=self.ns)})")
|
||||
n += 1
|
||||
elif (type(ofsig) is stdlib.list):
|
||||
f = ofsig.attrops[x.callable.value.optype.special, x.callable.value.attr.identifier]
|
||||
self.load(f.name)
|
||||
self.load(x.callable.value.value)
|
||||
n += 1
|
||||
elif (isinstance(fsig, stdlib.Builtin)):
|
||||
self.load(x.callable)
|
||||
else: raise NotImplementedError(ofsig)
|
||||
else: raise NotImplementedError(x.callable.value)
|
||||
|
||||
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
|
||||
if (n or len(x.callargs.starargs) > 1): self.instrs.append(f"BUILD_LIST {n}")
|
||||
for i in x.callargs.starargs:
|
||||
self.load(i)
|
||||
n += 1
|
||||
self.instrs.append(f"BUILD_TUPLE_UNPACK_WITH_CALL {n}")
|
||||
if (n or len(x.callargs.starargs) > 1): self.instrs.append("LIST_EXTEND 1")
|
||||
if (n or len(x.callargs.starargs) > 1): self.instrs.append("LIST_TO_TUPLE")
|
||||
n = 0
|
||||
elif (x.callkwargs.starkwargs):
|
||||
self.instrs.append("BUILD_TUPLE {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):
|
||||
self.instrs.append(f"BUILD_MAP {n}")
|
||||
for i in x.callkwargs.starkwargs:
|
||||
self.load(i)
|
||||
n += 1
|
||||
self.instrs.append(f"BUILD_MAP_UNPACK_WITH_CALL {n}")
|
||||
self.instrs.append("DICT_MERGE 1")
|
||||
n = 1
|
||||
|
||||
self.instrs.append(f"CALL{'EX' if (x.callargs.starargs or x.callkwargs.starkwargs) else 'KW' if (x.callkwargs.callkwargs) else ''} {n}")
|
||||
@@ -453,18 +472,18 @@ class PyssemblyCompiler(Compiler):
|
||||
|
||||
@classmethod
|
||||
def compile_ast(cls, ast, ns, *, filename):
|
||||
instrs = Instrs(name='<module>', ns=ns, filename=filename)
|
||||
instrs = Instrs(name=filename, ns=ns, filename=filename)
|
||||
|
||||
instrs.consts.append(compile(open(std.__file__).read(), '<std>', 'exec'))
|
||||
instrs.consts.append(code_with(compile(open(std.__file__).read(), '<stdlib>', 'exec'), name='stdlib'))
|
||||
instrs.instrs += [
|
||||
f"LOAD {len(instrs.consts)-1}",
|
||||
"LOAD ('<std>')",
|
||||
"LOAD ('<stdlib>')",
|
||||
"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
|
||||
except Exception as ex: raise SlCompilationError('Compilation error', instrs.lastnodens[0], scope=instrs.lastnodens[1].scope if (instrs.lastnodens[1] is not None) else '<UNKNOWN>') from ex
|
||||
|
||||
#dlog("Instrs:\n"+'\n'.join(instrs.instrs)+'\n')
|
||||
|
||||
@@ -483,4 +502,6 @@ class PyssemblyCompiler(Compiler):
|
||||
|
||||
return code
|
||||
|
||||
compiler = PyssemblyCompiler
|
||||
|
||||
# by Sdore, 2020
|
||||
|
181
compilers/pyssembly/genstdlib.py
Executable file
181
compilers/pyssembly/genstdlib.py
Executable file
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
from utils.nolog import *
|
||||
|
||||
builtin_types = {i for i in itertools.chain(builtins.__dict__.values(), types.__dict__.values()) if isinstance(i, type)}
|
||||
|
||||
@dispatch
|
||||
def gen(x, name: NoneType, *, scope: dict):
|
||||
return gen(x, '', scope=scope).rstrip(' ')
|
||||
|
||||
@dispatch
|
||||
def gen(x: lambda x: x is NoneType, name: str, *, scope: dict):
|
||||
return f"void {name}"
|
||||
|
||||
@dispatch
|
||||
def gen(x: lambda x: isinstance(x, type) and (getattr(x, '__module__', None) == '__main__' or x in builtin_types), name: str, *, scope: dict):
|
||||
return f"{x.__name__} {name}"
|
||||
|
||||
@dispatch
|
||||
def gen(x: lambda x: typing_inspect.get_origin(x) is not None, name: str, *, scope: dict):
|
||||
o = typing_inspect.get_origin(x)
|
||||
if (o is typing.Literal):
|
||||
return gen(type(x.__args__[0]), name, scope=scope)
|
||||
elif (o is typing.Union):
|
||||
if (x.__args__[1] is NoneType): return f"{gen(x.__args__[0], name, scope=scope).rstrip()}?"
|
||||
else: return f"{'|'.join(gen(i, None, scope=scope) for i in x.__args__)} {name}"
|
||||
elif (o in (tuple, list, dict, set, frozenset)):
|
||||
return f"{o.__name__}{', '.join(gen(i, None, scope=scope) for i in x.__args__ if i is not ...).join('[]') if (x.__args__ and x.__args__ != (typing.Any,) and x.__args__ != (typing.Any, ...)) else ''} {name}" # TODO args
|
||||
|
||||
# TODO FIXME:
|
||||
elif (o is collections.abc.Iterable):
|
||||
return f"iterable {name}"
|
||||
elif (o is collections.abc.Iterator):
|
||||
return f"iterator {name}"
|
||||
elif (o is collections.abc.Sequence):
|
||||
return f"sequence {name}"
|
||||
elif (o is collections.abc.Mapping):
|
||||
return f"mapping[{gen(x.__args__[0], None, scope=scope)}, {gen(x.__args__[1], None, scope=scope)}] {name}"
|
||||
elif (o is collections.abc.ItemsView):
|
||||
return f"items {name}"
|
||||
elif (o is collections.abc.KeysView):
|
||||
return f"keys {name}"
|
||||
elif (o is collections.abc.ValuesView):
|
||||
return f"values {name}"
|
||||
elif (o is collections.abc.Set):
|
||||
return f"set {name}"
|
||||
elif (o is collections.abc.Callable):
|
||||
return f"callable {name}"
|
||||
|
||||
elif (o is type):
|
||||
return f"{x.__args__[0].__name__} {name}"
|
||||
else: raise NotImplementedError(x, o)
|
||||
|
||||
@dispatch
|
||||
def gen(x: lambda x: x is typing.Any, name: str, *, scope: dict): # TODO FIXME
|
||||
return f"object {name}"
|
||||
|
||||
@dispatch
|
||||
def gen(x: typing.TypeVar, name: str, *, scope: dict):
|
||||
return f"{x.__name__} {name}"
|
||||
|
||||
@dispatch
|
||||
def gen(x: lambda x: isinstance(x, type) and issubclass(x, typing.Protocol), name: str, *, scope: dict):
|
||||
if (x is typing.SupportsInt):
|
||||
t = int
|
||||
elif (x is typing.SupportsFloat):
|
||||
t = float
|
||||
else: raise NotImplementedError(x)
|
||||
return gen(t, name, scope=scope)
|
||||
|
||||
@dispatch
|
||||
def gen(x: function, name: str, *, scope: dict):
|
||||
fsig = inspect.signature(x)
|
||||
return f"{gen(typing.get_type_hints(x, scope)['return'], None, scope=scope)} {name}({', '.join(gen(v, x, k, scope=scope) for ii, (k, v) in enumerate(fsig.parameters.items()) if not (ii == 0 and k == 'self'))})"
|
||||
|
||||
@dispatch
|
||||
def gen(x: inspect.Parameter, f, name: str, *, scope: dict):
|
||||
t = gen(typing.get_type_hints(f, scope)[x.name], name, scope=scope)
|
||||
if (x.default is not inspect._empty):
|
||||
if (x.default is ...):
|
||||
if (t[-1:] != '?'): t += '?'
|
||||
else: raise NotImplementedError(x.default)
|
||||
return t
|
||||
|
||||
@dispatch
|
||||
def gen(x: property, name: str, *, scope: dict):
|
||||
return gen(typing.get_type_hints(x.fget, scope)['return'], name, scope=scope)
|
||||
|
||||
@dispatch
|
||||
def gen(x: lambda x: isinstance(x, type) and x.__module__ == '__main__', name: str, *, scope: dict):
|
||||
return f"{x.__name__} {name}"
|
||||
|
||||
#@dispatch
|
||||
#def gen(x: type, name: str, *, scope: dict):
|
||||
# return f"{type.__name__} {name}"
|
||||
|
||||
@dispatch
|
||||
def gen(x: lambda x: isinstance(x, type) and x.__module__ == '__main__', *, scope: dict):
|
||||
r = []
|
||||
|
||||
for k, v in typing.get_type_hints(x, scope).items():
|
||||
r.append(f"{gen(v, k, scope=scope)};".lstrip(';'))
|
||||
|
||||
for k, v in x.__dict__.items():
|
||||
if (k in ('__module__', '__doc__')): continue
|
||||
if (not isinstance(v, property) and (not isinstance(v, function) or getattr(v, '__module__', None) != '__main__')): continue
|
||||
r.append(f"{gen(v, k, scope=scope)};".lstrip(';'))
|
||||
|
||||
if (not r): return ''
|
||||
return f"class {x.__name__} {{\n\t"+'\n\t'.join(r)+'\n}'
|
||||
|
||||
@dispatch
|
||||
def gen(x: CodeType, *, package):
|
||||
r = {'__name__': '__main__', '__package__': package}
|
||||
exec(x, r)
|
||||
return '\n\n'.join(Slist(gen(i, scope=r) for i in r.values() if isinstance(i, type) and i.__module__ == '__main__').strip(''))
|
||||
|
||||
class AnnotationsFileLoader(importlib.machinery.SourceFileLoader):
|
||||
header = "from __future__ import annotations"
|
||||
|
||||
def get_data(self, path):
|
||||
dlog(path)
|
||||
data = super().get_data(path)
|
||||
if (not path.endswith('.pyi')): return data
|
||||
return (self.header+'\n\n').encode() + data
|
||||
|
||||
def path_hook_factory(f):
|
||||
def path_hook(path):
|
||||
finder = f(path)
|
||||
finder._loaders.insert(0, ('.pyi', AnnotationsFileLoader))
|
||||
return path_hook
|
||||
|
||||
@apmain
|
||||
@aparg('typeshed', metavar='<typeshed repo path>')
|
||||
@aparg('output', metavar='<stdlib .sld output dir>')
|
||||
def main(cargs):
|
||||
if (sys.version_info < (3,8)): raise NotImplementedError("Currently only Python 3.8+ is supported.")
|
||||
|
||||
dirs = ('stdlib', 'stubs')
|
||||
|
||||
import __future__
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
sys.path = [__future__.__file__] + [os.path.join(cargs.typeshed, i) for i in dirs] + [os.path.join(cargs.typeshed, 'stubs', i, j) for i in os.listdir(os.path.join(cargs.typeshed, 'stubs')) for j in os.listdir(os.path.join(cargs.typeshed, 'stubs', i))]
|
||||
#sys.path_hooks[-1] = path_hook_factory(sys.path_hooks[-1])
|
||||
#sys.path_hooks[-1] = importlib.machinery.FileFinder.path_hook((AnnotationsFileLoader, ['.pyi']+[i for i in importlib.machinery.all_suffixes() if i != '.pyc']))
|
||||
sys.path_hooks = [importlib.machinery.FileFinder.path_hook((AnnotationsFileLoader, ['.pyi']))]
|
||||
sys.meta_path = [sys.meta_path[2]]
|
||||
|
||||
skipped = int()
|
||||
for d in dirs:
|
||||
for v in sorted(os.listdir(os.path.join(cargs.typeshed, d))):
|
||||
if (v in ('@python2', '_typeshed')): continue
|
||||
for p, _, n in os.walk(os.path.join(cargs.typeshed, d, v)):
|
||||
if ('@python2' in p): continue
|
||||
o = os.path.join(cargs.output, p.partition(os.path.join(cargs.typeshed, d, v, ''))[2])
|
||||
os.makedirs(o, exist_ok=True)
|
||||
for i in sorted(n, key=lambda x: 'builtins.pyi' not in x):
|
||||
if (not i.endswith('.pyi')): continue
|
||||
filename = os.path.join(p, i)
|
||||
log(filename)
|
||||
code = compile(b"from __future__ import annotations\n\n"+open(filename, 'rb').read(), filename, 'exec')
|
||||
try: r = gen(code, package=os.path.splitext(filename)[0].replace('/', '.'))
|
||||
except Exception as ex:
|
||||
if ('builtins.pyi' in i): raise
|
||||
logexception(ex)
|
||||
skipped += 1
|
||||
raise # XXX
|
||||
else:
|
||||
if (not r): continue
|
||||
if ('builtins.pyi' in i): r = 'import sld:std:*;\n\n'+r
|
||||
else: r = 'import py:builtins:*;\n\n'+r
|
||||
open(os.path.join(o, os.path.splitext(i)[0]+'.sld'), 'w').write(r+'\n')
|
||||
|
||||
if (skipped): print(f"\033[93m{skipped} errors caught ({skipped} files skipped).\033[0m")
|
||||
print("\033[1mSuccess!\033[0m")
|
||||
|
||||
if (__name__ == '__main__'): exit(main(nolog=True))
|
||||
else: logimported()
|
||||
|
||||
# by Sdore, 2021
|
1
compilers/pyssembly/requirements.txt
Normal file
1
compilers/pyssembly/requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
typing_inspect
|
@@ -1,2 +1,5 @@
|
||||
class stdio:
|
||||
println = print
|
||||
|
||||
def _map(f, l): return list(map(f, l))
|
||||
def _each(l, f): return list(map(f, l))
|
||||
|
Submodule compilers/pyssembly/typeshed updated: b44cd294c4...e2fd852952
351
compilers/sbc.py
351
compilers/sbc.py
@@ -1,351 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
# Slang Bytecode (SBC) compiler target
|
||||
|
||||
from . import *
|
||||
from Slang.ast import *
|
||||
from utils import *
|
||||
|
||||
NOP = 0x00
|
||||
END = 0x01
|
||||
POP = 0x02
|
||||
RET = 0x03
|
||||
BLTIN = 0x04
|
||||
CODE = 0x05
|
||||
|
||||
POS = 0x10
|
||||
NEG = 0x11
|
||||
NOT = 0x12
|
||||
INV = 0x13
|
||||
ATOI = 0x14
|
||||
ITOA = 0x15
|
||||
ITOF = 0x16
|
||||
CEIL = 0x17
|
||||
FLR = 0x18
|
||||
RND = 0x19
|
||||
CTOS = 0x1a
|
||||
|
||||
ADD = 0x20
|
||||
SUB = 0x21
|
||||
MUL = 0x22
|
||||
DIV = 0x23
|
||||
IDIV = 0x24
|
||||
MOD = 0x25
|
||||
POW = 0x26
|
||||
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
|
||||
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, scpcells: indexset):
|
||||
self.name, self.ns, self.filename, self.scpcells = name, ns, filename, scpcells
|
||||
self.instrs = bytearray()
|
||||
|
||||
def compile(self):
|
||||
return bytes(self.instrs)
|
||||
|
||||
@dispatch
|
||||
def add(self, opcode: lambda x: isinstance(x, int) and x < HASARG):
|
||||
self.instrs.append(opcode)
|
||||
|
||||
@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):
|
||||
self.add(x.code)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTCodeNode):
|
||||
for i in x.nodes:
|
||||
self.add(i)
|
||||
|
||||
@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): raise NotImplementedError()
|
||||
self.load(x.value)
|
||||
self.store(x.name)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTFunccallNode):
|
||||
self.load(x)
|
||||
self.add(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)))}"
|
||||
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)
|
||||
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('::')
|
||||
raise TODO
|
||||
self.store(name)
|
||||
elif (x.keyword.keyword == 'return'):
|
||||
self.load(x.value)
|
||||
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.add(IF)
|
||||
self.add(x.code)
|
||||
self.add(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):
|
||||
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):
|
||||
self.load(x.identifier)
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTValueNode):
|
||||
self.load(x.value)
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTFunccallNode):
|
||||
nargs = int()
|
||||
|
||||
for i in x.callargs.callargs:
|
||||
self.load(i)
|
||||
nargs += 1
|
||||
if (x.callargs.starargs): raise NotImplementedError()
|
||||
|
||||
if (x.callkwargs.callkwargs): raise NotImplementedError()
|
||||
if (x.callkwargs.starkwargs): raise NotImplementedError()
|
||||
|
||||
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):
|
||||
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.add(self.unopmap[x.operator.operator])
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTBinaryExprNode):
|
||||
self.load(x.lvalue)
|
||||
self.load(x.rvalue)
|
||||
if (x.operator.operator == 'to'): raise NotImplementedError()
|
||||
else: self.add(self.binopmap[x.operator.operator])
|
||||
|
||||
#@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.add(SCPGET, self.scpcells[x])
|
||||
|
||||
@dispatch
|
||||
def store(self, x: ASTIdentifierNode):
|
||||
self.store(x.identifier)
|
||||
|
||||
@dispatch
|
||||
def store(self, x: str):
|
||||
self.add(SCPSET, self.scpcells[x])
|
||||
|
||||
class SBCCompiler(Compiler):
|
||||
ext = '.sbc'
|
||||
|
||||
@classmethod
|
||||
def compile_ast(cls, ast, ns, *, filename):
|
||||
instrs = Instrs(name='<module>', ns=ns, filename=filename)
|
||||
instrs.add(ast)
|
||||
code = instrs.compile()
|
||||
return code
|
||||
|
||||
# by Sdore, 2019
|
289
compilers/sbc/__init__.py
Normal file
289
compilers/sbc/__init__.py
Normal file
@@ -0,0 +1,289 @@
|
||||
#!/usr/bin/python3
|
||||
# Slang Bytecode (SBC) compiler target
|
||||
|
||||
from . import *
|
||||
from .. import *
|
||||
from ...ast import *
|
||||
from utils import *
|
||||
|
||||
NOP = 0x00
|
||||
POP = 0x01
|
||||
DUP = 0x02
|
||||
RET = 0x03
|
||||
CODE = 0x04
|
||||
IF = 0x05
|
||||
LOOP = 0x06
|
||||
ELSE = 0x07
|
||||
END = 0x08
|
||||
CALL = 0x09
|
||||
ASGN = 0x0A
|
||||
BLTIN = 0x0B
|
||||
CONST = 0x0C
|
||||
SGET = 0x0D
|
||||
SSET = 0x0E
|
||||
|
||||
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:
|
||||
unopmap = {
|
||||
'!': 'not',
|
||||
'+': 'abs',
|
||||
'-': 'neg',
|
||||
'~': 'inv',
|
||||
'++': 'inc',
|
||||
'--': 'dec',
|
||||
'**': 'sqr',
|
||||
'not': 'not',
|
||||
}
|
||||
binopmap = {
|
||||
'+': 'add',
|
||||
'-': 'sub',
|
||||
'*': 'mul',
|
||||
'/': 'div',
|
||||
'//': 'idiv',
|
||||
'%': 'mod',
|
||||
'**': 'pow',
|
||||
'<<': 'ls',
|
||||
'>>': 'rs',
|
||||
'&': 'and',
|
||||
'^': 'xor',
|
||||
'|': 'or',
|
||||
|
||||
'&&': 'and',
|
||||
'^^': 'xor',
|
||||
'||': 'or',
|
||||
'==': 'eq',
|
||||
'!=': 'ne',
|
||||
'<': 'lt',
|
||||
'>': 'gt',
|
||||
'<=': 'le',
|
||||
'>=': 'ge',
|
||||
|
||||
'is': 'is',
|
||||
'is not': 'isnot',
|
||||
'in': 'in',
|
||||
'not in': 'notin',
|
||||
'isof': 'isof',
|
||||
'and': 'and',
|
||||
'but': 'and',
|
||||
'xor': 'xor',
|
||||
'or': 'or',
|
||||
'to': 'range',
|
||||
}
|
||||
|
||||
@init_defaults
|
||||
def __init__(self, *, name, ns, filename, scpcells: indexset):
|
||||
self.name, self.ns, self.filename, self.scpcells = name, ns, filename, scpcells
|
||||
self.instrs = bytearray()
|
||||
self.opmap = dict()
|
||||
|
||||
def compile(self):
|
||||
return bytes(self.instrs)
|
||||
|
||||
@dispatch
|
||||
def add(self, opcode: int, *args: int):
|
||||
self.instrs.append(opcode)
|
||||
if (args): self.instrs += bytes(args)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTRootNode):
|
||||
self.add(x.code)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTBlockNode):
|
||||
self.add(x.code)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTCodeNode):
|
||||
for i in x.nodes:
|
||||
self.add(i)
|
||||
|
||||
@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, sig=Signature.build(x.type, ns=self.ns))
|
||||
self.store(x.name)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTFunccallNode):
|
||||
self.load(x)
|
||||
self.add(POP)
|
||||
|
||||
@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(CALL, 0)
|
||||
self.add(POP)
|
||||
else: raise NotImplementedError(x.keyword)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTConditionalNode):
|
||||
self.load(x.condition)
|
||||
self.add(IF)
|
||||
self.add(x.code)
|
||||
self.add(END)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTForLoopNode):
|
||||
self.load(x.iterable)
|
||||
self.builtin('iter', 1)
|
||||
self.store(x.name)
|
||||
self.add(DUP, 0)
|
||||
self.add(LOOP)
|
||||
self.add(x.code)
|
||||
self.builtin('iter', 1)
|
||||
self.store(x.name)
|
||||
self.add(DUP, 0)
|
||||
self.add(END)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTWhileLoopNode):
|
||||
self.load(x.condition)
|
||||
self.add(LOOP)
|
||||
self.add(x.code)
|
||||
self.load(x.condition)
|
||||
self.add(END)
|
||||
|
||||
@dispatch
|
||||
def add(self, x: ASTElseClauseNode):
|
||||
assert (self.instrs[-1] == END)
|
||||
end = self.instrs.pop()
|
||||
self.add(ELSE)
|
||||
self.add(x.code)
|
||||
self.add(end)
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTLiteralNode, *, sig=None):
|
||||
if (sig is None): sig = Signature.build(x, ns=self.ns)
|
||||
|
||||
if (hasattr(sig, 'fmt')):
|
||||
t, v = sig.__class__.__name__, struct.pack(sig.fmt, int(x.literal))
|
||||
elif (isinstance(sig, stdlib.int)):
|
||||
t, v = 'i', writeVarInt(eval_literal(x))
|
||||
elif (isinstance(sig, stdlib.str)):
|
||||
t, v = 's', eval_literal(x).encode('utf-8')
|
||||
elif (isinstance(sig, stdlib.char)):
|
||||
t, v = 'c', eval_literal(x).encode('utf-8') # TODO
|
||||
else: raise NotImplementedError(sig)
|
||||
|
||||
self.add(CONST)
|
||||
self.instrs += t.encode('utf-8')+b'\0' + writeVarInt(len(v)) + v
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTIdentifierNode, **kwargs):
|
||||
self.load(x.identifier, **kwargs)
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTValueNode, **kwargs):
|
||||
self.load(x.value, **kwargs)
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTFunccallNode):
|
||||
nargs = int()
|
||||
|
||||
for i in x.callargs.callargs:
|
||||
self.load(i)
|
||||
nargs += 1
|
||||
if (x.callargs.starargs): raise NotImplementedError()
|
||||
|
||||
if (x.callkwargs.callkwargs): raise NotImplementedError()
|
||||
if (x.callkwargs.starkwargs): raise NotImplementedError()
|
||||
|
||||
if (isinstance(x.callable, ASTValueNode) and isinstance(x.callable.value, ASTIdentifierNode) and x.callable.value.identifier in self.ns.signatures):
|
||||
raise NotImplementedError()
|
||||
#name = f"{x.callable.value.identifier}({CallArguments.build(x, self.ns)})"
|
||||
#self.load(name)
|
||||
#self.load(name+'.len')
|
||||
#self.add(EXEC)
|
||||
else:
|
||||
self.load(x.callable)
|
||||
self.add(CALL, nargs)
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTAttrgetNode):
|
||||
if (str(x.value) != 'stdio'): raise NotImplementedError(x)
|
||||
if (x.optype.special != '.'): raise NotImplementedError(x)
|
||||
self.builtin(str(x.attr))
|
||||
|
||||
@dispatch
|
||||
def load(self, x: ASTBinaryExprNode, **kwargs):
|
||||
self.load(x.lvalue, **kwargs)
|
||||
self.load(x.rvalue, **kwargs)
|
||||
self.builtin(self.binopmap[x.operator.operator], 2)
|
||||
|
||||
@dispatch
|
||||
def load(self, x: str, **kwargs):
|
||||
self.add(SGET, self.scpcells[x])
|
||||
|
||||
@dispatch
|
||||
def store(self, x: ASTIdentifierNode):
|
||||
self.store(x.identifier)
|
||||
|
||||
@dispatch
|
||||
def store(self, x: str):
|
||||
self.add(SSET, self.scpcells[x])
|
||||
|
||||
@dispatch
|
||||
def assign(self, builtin: str,
|
||||
nargs_code: lambda nargs_code: isinstance(nargs_code, int) and 0 <= nargs_code < 4,
|
||||
nargs_stack: lambda nargs_stack: isinstance(nargs_stack, int) and 0 <= nargs_stack < 64,
|
||||
opcode: int = None):
|
||||
self.builtin(builtin)
|
||||
if (opcode is None): opcode = first(sorted(set(range(0x10, 0x100)) - set(self.opmap.values())))
|
||||
self.add(ASGN, opcode, (nargs_code << 6) | nargs_stack)
|
||||
self.opmap[builtin, nargs_code, nargs_stack] = opcode
|
||||
|
||||
@dispatch
|
||||
def builtin(self, builtin: str):
|
||||
self.add(BLTIN)
|
||||
self.instrs += builtin.encode('ascii')+b'\0'
|
||||
|
||||
@dispatch
|
||||
def builtin(self, builtin: str, nargs: int):
|
||||
if (nargs is not None and len(self.opmap) < 0xf0): self.assign(builtin, 0, nargs)
|
||||
if ((builtin, 0, nargs) in self.opmap): self.add(self.opmap[builtin, 0, nargs])
|
||||
else: self.builtin(builtin); self.add(CALL, nargs)
|
||||
|
||||
class SBCCompiler(Compiler):
|
||||
ext = '.sbc'
|
||||
|
||||
@classmethod
|
||||
def compile_ast(cls, ast, ns, *, filename):
|
||||
instrs = Instrs(name='<module>', ns=ns, filename=filename)
|
||||
instrs.add(ast)
|
||||
code = instrs.compile()
|
||||
return code
|
||||
|
||||
compiler = SBCCompiler
|
||||
|
||||
# by Sdore, 2020
|
Reference in New Issue
Block a user