# 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] { 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 [`* `) data:declaration::type] | [expr:expr::paren::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' / end literal `enum `struct def visual [name:VISUAL] def filter [`| function:reference::function::type] def subscript [`[ expr:expr::bracket::type `: slice:expr::bracket::type `]] | [`[ expr:expr::bracket::type `]] def extra # if set, this field has trivial length, otherwise need to read subscripts length:collapser::collapsed [subscript:subscript* filter:filter* visual:visual?] { f:str = '' has_slice:bool for l:subscript in repeat(r1) { if (l.slice) { f = '' has_slice = true break } if (f != '') f = f + '*' if (l.expr.collapsed.result.value) { f = f + '(' + $l.expr.collapsed.result.value + ')' } else { f = f + '(' + $l.expr.collapsed + ')' } } if (f == '' && !has_slice) f = '1' if (f != '') lhs.length = collapser::stream(f) } 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* `;] 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> 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_no_error(type:str, name:str) { for s:scope in g_scopes { cmap:map = s->names->find(type) if (cmap) { var:any = cmap->find(name) if (var) return var } } 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) { r:any = lookup_no_error(type, name) if (!r) { print('`', type, ' ', name, '` is not declared in this or outer scope!\n') exit(1) } return r } str container_name_str(s:str) { if (!s) return '' return s } str signed_str(s:bool) { if (s) return 'signed' return 'unsigned' } global INDSTP:str = '░ ' void print_declaration(d:fspec::declaration::type, ind:str) { insert('variable', $d.name, d) print(ind, 'variable `', $d.name, "` that's ") c:fspec::container::type if (d.cref) c = lookup($d.cref, $d.parent) else c = d.container if (c) { print('`', c.data.type, ' ', container_name_str(c.data.name), '` ') if (c.data.select) print('with expression `', $c.data.select, '` ') } if (d.primitive) print(d.primitive.bits, ' bits and ', signed_str(d.primitive.signed)) print('\n') if (d.extra) { if (d.extra.length) { if (!d.extra.length.result.value || d.extra.length.result.value.reference) { print(ind, INDSTP, 'it has a variable length that needs to be computed with formula `', $d.extra.length, '`\n') } else { if (d.extra.length.result.value.number) { print(ind, INDSTP, 'it has a constant length of ', $d.extra.length.result.value, '\n') } else if (d.extra.length.result.value.string) { print(ind, INDSTP, ' its length will increase until pattern `', d.extra.length.result.value.string.escaped, '` has been read from stream\n') } } } else { print(ind, INDSTP, 'the subscripts contain slices, and thus needs some runtime loops to be computed\n') } for f:fspec::declaration::filter in repeat(d.extra.filter) print(ind, INDSTP, 'it needs to be filtered with `', $f.function, '`\n') for v:fspec::declaration::visual in child(d.extra.visual) print(ind, INDSTP, 'it should be visualized as `', $v.name, '`\n') } } void walk(d:fspec::declaration::type, ind:str) { if (!d.container) { print('something went wrong!\n') exit(1) } s:fspec::container::type = d.container insert($s.data.type, s.data.name, s) if (d.name) { print_declaration(d, ind) ind = ind + INDSTP print(ind, 'and it contains\n') } else { print(ind, 'container `', s.data.type, ' ', container_name_str(s.data.name), '`') if ($d.filter != '') { print('\n') ind = ind + INDSTP for f:fspec::declaration::filter in repeat(d.filter) print(ind, 'it needs to be filtered with `', $f.function, '`\n') print(ind, 'and it contains\n') } else { print(' that contains\n') } } if ($s.data.type == 'enum') { for i:fspec::container::enum::item in repeat(s.data.items) { print(ind, INDSTP, 'constant `', $i.decl.name, '` which value is ', $i.decl.value, '\n') insert('variable', $i.decl.name, i) } } else if ($s.data.type == 'struct') { push_scope() for i:fspec::container::strukt::item in repeat(s.data.items) { if (i.data.container) walk(i.data, ind + INDSTP) else print_declaration(i.data, ind + INDSTP) } pop_scope() } else if ($s.data.type == 'select') { ind = ind + INDSTP push_scope() for i:fspec::container::select::item in repeat(s.data.items) { if (i.expr) print(ind, 'in case of (', $i.expr, ')\n') else print(ind, 'or otherwise\n') if (i.data.container) walk(i.data, ind + INDSTP) else print_declaration(i.data, ind + INDSTP) } pop_scope() } } push_scope() for d:fspec::declaration::type in repeat(source.items) walk(d, '') pop_scope()