sonnum/pysonnum/compiler.py

161 lines
No EOL
3.4 KiB
Python

from sonnum import Sonnum
from activity import Activity
from instruction import *
class SonnumCompiler:
def __init__(self):
self.sonnum = Sonnum() # {order: name}
self.activities = []
def list_activities(self):
print('ACTIVITY LISTING')
for activity in self.activities:
print(activity)
def list_bytecode(self):
print('BYTECODE LISTING')
for activity in self.activities:
print(list(activity.to_bytes()))
def add_activity(self, name, tick_start, tick_end, src_node, trg_node, operands):
a = Activity(self.sonnum, name, tick_start, tick_end, src_node, trg_node, operands)
self.activities.append(a)
def sanitize_operand(self, operand):
try:
return str(float(operand))
except:
return str(operand)
def transpile_snm_to_py(self, snm_src):
py_src = []
for ln in snm_src.split('\n'):
tablevel = 0
while ln.startswith('\t'):
tablevel += 1
ln = ln[1:]
if ln.startswith(';'):
if ln.endswith('*'):
name = ln[1:-1]
ln = f"i_create_simple(self, self.sonnum, '{name}')"
elif ln.endswith('!'):
ticklen = int(ln[1:-1])
ln = f"i_end_tick(self, self.sonnum, {ticklen})"
elif ln.endswith('@'):
name = ln[1:-1]
ln = f"i_create_relay(self, self.sonnum, '{name}')"
elif '=>' in ln:
name_src, name_trg = ln[1:].split('=>')
ln = f"i_wire(self, self.sonnum, '{name_src}', '{name_trg}')"
elif '->' in ln:
name_src, name_trg = ln[1:].split('->')
ln = f"i_air(self, self.sonnum, '{name_src}', '{name_trg}')"
else:
print(ln)
lst = ln[1:].split(' ')
src_node_name = lst.pop(0)
instr = lst.pop(0)
operands = [self.sanitize_operand(op) for op in lst]
ln = f"i_{instr}(self, self.sonnum, '{src_node_name}', "+', '.join(operands)+")"
ln = tablevel*'\t' + ln
py_src.append(ln)
else:
ln = tablevel*'\t' + ln
py_src.append(ln)
return '\n'.join(py_src)
def run_transpiled_code(self, py_src):
exec(py_src)
def sort_activities(self):
new = []
to_delete = []
for activity in self.activities:
if activity.name == 'endtick':
new.append(activity)
to_delete.append(activity)
for activity in to_delete:
self.activities.remove(activity)
to_delete = []
for activity in self.activities:
if activity.name == 'create':
new.append(activity)
to_delete.append(activity)
for activity in to_delete:
self.activities.remove(activity)
to_delete = []
for activity in self.activities:
if activity.name in ('wire','air'):
new.append(activity)
to_delete.append(activity)
for activity in to_delete:
self.activities.remove(activity)
self.activities.sort(key=lambda x: x.tick_start, reverse = False)
new.extend(self.activities)
self.activities = new
def compile_to_smnb(self, snm_src, fn):
py_src = self.transpile_snm_to_py(snm_src)
self.run_transpiled_code(py_src)
self.sort_activities()
self.list_activities()
bytecode = []
for activity in self.activities:
bytecode.append(activity.to_bytes())
with open(fn, 'wb') as fl:
fl.write(b''.join(bytecode))
TEST = """;44100!
;left_mic@
;right_mic@
;synth*
;synth setfreq 0 440 0.9
;synth=>left_mic
;synth=>right_mic"""
C = SonnumCompiler()
snm = C.compile_to_smnb(TEST, '../zigsonnum/test.snmb')