# Filespec compiler # Takes in fspec source code and outputs bytecode for further processing include 'expr.lm' context fspec enum_counter:int void enum_inc() { enum_counter = enum_counter + 1 } void enum_set(v:int) { enum_counter = v } int enum_get() { return enum_counter } context primitive token TYPE_SIGN / [us] / token TYPE_BITS / [1-9][0-9]* / int strtoull(a:str, b:int) = c_strtoull def type signed:bool bits:int [TYPE_SIGN TYPE_BITS] { lhs.signed = ($r1 == 's') lhs.bits = strtoull($r2, 10) } end context container context enum lex ignore / '//' [^\n]* '\n' | space+ / literal `= `, `{ `} end literal `enum int const_int_expr(expr:collapser::collapsed) { if (!expr || !expr.result.value || !expr.result.value.number) reject return expr.result.value.number.value } def declaration value:int [name:reference::variable::type `= expr::enum::type] commit { lhs.value = const_int_expr(r3.collapsed) enum_set(lhs.value) enum_inc() } | [name:reference::variable::type] { lhs.value = enum_get() enum_inc() } def item [decl:declaration `, item] | [decl:declaration] def type name:str items:item+ select:expr::paren::type [type:`enum WS+ reference::variable::type? `{ item+ `}] { for n:reference::variable::type in child(r3) lhs.name = $n lhs.items = r5 enum_set(0) } end context strukt # <- struct is taken :( lex ignore / '//' [^\n]* '\n' | space+ / literal `{ `} end literal `struct def item [data:declaration::type] def type name:str items:item+ select:expr::paren::type [type:`struct WS+ reference::variable::type? `{ item+ `}] { for n:reference::variable::type in child(r3) lhs.name = $n lhs.items = r5 } end context select lex ignore / '//' [^\n]* '\n' | space+ / literal `( `) `{ `} `* `: end literal `select def item [expr:expr::select::type `) data:declaration::type] def type name:str items:item+ # BUG: marking item+ with items: in the match below causes weird behaviour select:expr::paren::type [type:`select `( expr::paren::type `) `{ item+ `}] { lhs.select = r3 lhs.items = r6 } end def type [data:enum::type] | [data:strukt::type] | [data:select::type] end context declaration lex ignore / '//' [^\n]* '\n' | space+ / literal `: `[ `] `| `; token VISUAL / 'nul' | 'dec' | 'hex' | 'str' | 'flt' / end literal `enum `struct def visual [name:VISUAL] def filter [`| function:reference::function::type] def dimension [`[ expr:expr::bracket::type `: slice:expr::bracket::type `]] | [`[ expr:expr::bracket::type `]] def extra [dimension:dimension* filter:filter* visual:visual?] def type # enum name name ; [cref:`enum WS+ parent:reference::variable::type WS+ primitive:primitive::type WS+ name:reference::variable::type extra:extra `;] # struct name name ; | [cref:`struct WS+ parent:reference::variable::type WS+ name:reference::variable::type extra:extra `;] # name ; | [primitive:primitive::type WS+ name:reference::variable::type extra:extra `;] # select ((thing)) { ... } ; INVALID | [container::select::type extra `;] commit { reject } # select ((thing)) { ... } name ; INVALID | [container::select::type primitive::type WS+ reference::variable::type extra `;] commit { reject } # struct (optional) { ... } name ; INVALID | [container::strukt::type primitive::type WS+ reference::variable::type extra `;] commit { reject } # enum (optional) { ... } name ; | [container:container::type primitive:primitive::type WS+ name:reference::variable::type extra:extra `;] # select ((expr)) { ... } name ; # struct (optional) { ... } name ; | [container:container::type name:reference::variable::type extra:extra `;] # (enum|struct) name { ... } ; | [container:container::type filter:filter* `;] # name(...); | [function:reference::function::type `;] end def source [items:declaration::type*] commit source stream(s:stream) { c:fspec = new fspec() c->enum_counter = 0 return parse source(c)[s] } end source:fspec::source = fspec::stream(stdin) if (!source) { print(error) exit(1) } struct scope names:map> roff:int end global g_scopes:list = new list() void push_scope() { s:scope = new scope() s->names = new map>() g_scopes->push_head(s) } void pop_scope() { g_scopes->pop_head() } any lookup_in_scope_no_error(type:str, name:str, s:scope) { cmap:map = s->names->find(type) if (cmap) return cmap->find(name) return nil } any lookup_no_error(type:str, name:str) { for s:scope in g_scopes { v:any = lookup_in_scope_no_error(type, name, s) if (v) return v } return nil } any insert(type:str, name:str, var:any) { if (!name) return var # if (type != 'variable' && lookup_no_error(type, name)) { print('`', type, ' ', name, '` is already declared as a `', type, '` in current scope!\n') exit(1) } cmap:map = g_scopes->top->names->find(type) if (!cmap) { cmap = new map() } else if (cmap->find(name)) { print('`', type, ' ', name, '` is already declared as a `', type, '` in current scope!\n') exit(1) } cmap->insert(name, var) g_scopes->top->names->insert(type, cmap) return var } any lookup(type:str, name:str, s:scope) { r:any = nil if (s) r = lookup_in_scope_no_error(type, name, s) else r = lookup_no_error(type, name) if (!r) { print('`', type, ' ', name, '` is not declared in this or outer scope!\n') exit(1) } return r } global g_cscope:map = new map() global g_regs:map = new map() global g_ops:map = new map() global g_visuals:map = new map() global g_types:map = new map() g_ops->insert('-#', 0) g_ops->insert('!', 1) g_ops->insert('~', 2) g_ops->insert('*', 3) g_ops->insert('/', 4) g_ops->insert('%', 5) g_ops->insert('#+', 6) g_ops->insert('#-', 7) g_ops->insert('<<', 8) g_ops->insert('>>', 9) g_ops->insert('<', 10) g_ops->insert('<=', 11) g_ops->insert('==', 12) g_ops->insert('!=', 13) g_ops->insert('&', 14) g_ops->insert('|', 15) g_ops->insert('^', 16) g_ops->insert('&&', 17) g_ops->insert('||', 18) g_ops->insert(':', 19) g_ops->insert(']', 20) g_visuals->insert('nul', 0) g_visuals->insert('dec', 1) g_visuals->insert('hex', 2) g_visuals->insert('str', 3) g_visuals->insert('flt', 4) g_types->insert('enum', 0) g_types->insert('struct', 1) g_types->insert('select', 2) # UNION global INS_VERSION:int = 0 global INS_REG:int = 1 global INS_PUSH:int = 2 global INS_PUSHR:int = 3 global INS_POP:int = 4 global INS_OP:int = 5 global INS_QUEUE:int = 6 global INS_IO:int = 7 global INS_CALL:int = 8 global INS_JMP:int = 9 global INS_JMPIF:int = 10 int insbuf_written() = c_insbuf_written str flush_insbuf() = c_flush_insbuf void write_ins(ins:int, num:int) = c_write_ins void write_ins_with_data(ins:int, data:str) = c_write_ins_with_data global g_regc:int = 1 void new_reg(v:any, data:str) { if (g_regs->find(v)) { print('Register for `', $v, '` already exists!\n') exit(1) } if (!data) data = '' write_ins_with_data(INS_REG, data) print(flush_insbuf()) g_regs->insert(v, g_regc) g_regc = g_regc + 1 } void write_data_if_not_there(v:str) { if (!g_regs->find(v)) new_reg(v, v) } void find_data_in(s:any) { for v:string::type in s write_data_if_not_there(v.raw) for v:reference::function::type in s write_data_if_not_there($v.name) } write_ins(INS_VERSION, 1) write_ins_with_data(INS_REG, '') for d:fspec::declaration::type in source { if (d.primitive) new_reg(%d, nil) } for e:expr::paren::type in source find_data_in(e.collapsed) for e:expr::bracket::type in source find_data_in(e.collapsed) for e:expr::arg::type in source find_data_in(e.collapsed) for f:reference::function::type in source write_data_if_not_there($f.name) for d:fspec::declaration::type in source { if (d.container && d.container.data.name) write_data_if_not_there($d.container.data.name) if (d.name) write_data_if_not_there($d.name) } print("ASD\n") void write_expr(expr:collapser::reducer::collapsed) { for o:collapser::reducer::operation in repeat(expr) { for v:collapser::reducer::value in child(o) { if (v.number) { write_ins(INS_PUSH, v.number.value) } else if (v.string) { write_ins(INS_PUSHR, g_regs->find(v.string.raw)) } else if (v.reference) { if (v.reference.variable) { write_ins(INS_PUSHR, g_regs->find(lookup('variable', $v.reference.variable.name, nil))) } else if (v.reference.function) { if ($v.reference.function.name != 'until') { for a:expr::arg::type in v.reference.function write_expr(a.collapsed.result) write_ins(INS_CALL, g_regs->find($v.reference.function.name)) } } } } for vop:collapser::reducer::valueop in child(o) { if ($vop.op == '.') { s:scope = nil d:fspec::declaration::type = nil off:int = 0 for r:reference::variable::type in vop { d = lookup('variable', $r.name, s) if (d.container) s = g_cscope->find(%d.container) } write_ins(INS_PUSHR, g_regs->find(%d)) } else { write_ins(INS_OP, g_ops->find($vop.op)) } } } } void write_postexpr(expr:collapser::reducer::collapsed, start:int) { for o:collapser::reducer::operation in repeat(expr) { for v:collapser::reducer::value in child(o) { if (v.reference && v.reference.function) { if ($v.reference.function.name == 'until') { for a:expr::arg::type in v.reference.function write_expr(a.collapsed.result) write_ins(INS_OP, g_ops->find('!')) write_ins(INS_JMPIF, start) } } } } } void write_declaration(d:fspec::declaration::type, index:int) { if (!d.name) { print('something went wrong!\n') exit(1) } c:fspec::container::type = d.container if (d.cref) c = lookup($d.cref, $d.parent, nil) locs:map = new map() if (d.extra) { for l:fspec::declaration::dimension in repeat(d.extra.dimension) { locs->insert(%l, insbuf_written()) write_expr(l.expr.collapsed.result) } #for f:fspec::declaration::filter in repeat(d.extra.filter) { # for a:expr::arg::type in f # write_expr(a.collapsed.result) # write_ins(INS_CALL, g_regs->find($f.function.name)) #} } if (!c) { write_ins(INS_PUSH, index) write_ins(INS_IO, d.primitive.bits) } else { for i:fspec::container::strukt::item in repeat(d.container.data.items) { if (i.data.name) { write_declaration(i.data, index) index = index + 1 } else if (i.data.function) { for a:expr::arg::type in i.data.function write_expr(a.collapsed.result) write_ins(INS_CALL, g_regs->find($i.data.function.name)) } } } if (d.extra) { for l:fspec::declaration::dimension in repeat(d.extra.dimension) write_postexpr(l.expr.collapsed.result, locs->find(%l)) } } int walk2(d:fspec::declaration::type, index:int) { if (!d.container) { print('something went wrong!\n') exit(1) } insert($d.container.data.type, d.container.data.name, %d.container) if ($d.container.data.type != 'enum') push_scope() for i:fspec::container::enum::item in repeat(d.container.data.items) insert('variable', $i.decl.name, %i.decl) for i:fspec::container::strukt::item in repeat(d.container.data.items) { if (i.data.name) { insert('variable', $i.data.name, %i.data) write_declaration(i.data, index) index = index + 1 } else if (i.data.function) { for a:expr::arg::type in i.data.function write_expr(a.collapsed.result) write_ins(INS_CALL, g_regs->find($i.data.function.name)) } else if (i.data.container) { index = walk2(i.data, index) } } for i:fspec::container::select::item in repeat(d.container.data.items) { if (i.data.name) { insert('variable', $i.data.name, %i.data) if (i.expr) { write_expr(d.container.data.select.collapsed.result) write_expr(i.expr.collapsed.result) write_ins(INS_OP, g_ops->find('==')) } write_declaration(i.data, index) index = index + 1 } else if (i.data.container) { index = walk2(i.data, index) } } g_cscope->insert(%d, g_scopes->top) if ($d.container.data.type != 'enum') pop_scope() return index } push_scope() index:int = 1 for d:fspec::declaration::type in repeat(source.items) { walk2(d, index) if (insbuf_written()) { write_ins_with_data(INS_REG, flush_insbuf()) print(flush_insbuf()) } } pop_scope()