From 715d3d48f962d17575ff9de0034f2ac89b59f975 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Wed, 26 Sep 2018 15:29:17 +0300 Subject: Goodbye C compiler, hello colm compiler --- src/compiler/compiler.lm | 298 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100644 src/compiler/compiler.lm (limited to 'src/compiler/compiler.lm') diff --git a/src/compiler/compiler.lm b/src/compiler/compiler.lm new file mode 100644 index 0000000..59b0ee3 --- /dev/null +++ b/src/compiler/compiler.lm @@ -0,0 +1,298 @@ +include 'expr.lm' + +context fspec + token WS / space / + + 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 / 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 item + value:int + [name:name::type `= expr::enum::type `, item] { lhs.value = const_int_expr(r3.collapsed) } + | [name:name::type `= expr::enum::type] { lhs.value = const_int_expr(r3.collapsed) } + | [name:name::type `, item] { lhs.value = 0 } # TODO: count + | [name:name::type] { lhs.value = 0 } # TODO: count + + def type + name:str + items:item+ + [type:`enum WS+ name::type? `{ item+ `}] { if (name::type in r3) lhs.name = $(name::type in r3) lhs.items = r5 } + end + + context strukt # <- struct is taken :( + lex + ignore / space+ / + literal `{ `} + end + + literal `struct + + def item + [data:declaration::type] + + def type + name:str + items:item+ + [type:`struct WS+ name::type? `{ item+ `}] { if (name::type in r3) lhs.name = $(name::type in r3) lhs.items = r5 } + end + + context select + lex + ignore / space+ / + literal `( `) `{ `} `* + end + + literal `select + + def item + [expr:expr::paren::type `) data:declaration::type] + | [expr:`* `) data:declaration::type] + + def type + name:str + items:item+ # BUG: marking item+ with items: in the match below causes weird behaviour + [type:`select `( expr::paren::type `) `{ item+ `}] { lhs.items = r6 } + end + + def type + [data:enum::type] | [data:strukt::type] | [data:select::type] + end + + context declaration + lex + ignore / space+ / + literal `; `| `[ `] + end + + literal `enum `struct + token VISUAL / 'nul' | 'dec' | 'hex' | 'str' / + + def visual + [WS+ name:VISUAL] + + def filter + [`| function:reference::function::type] + + def length + [`[ expr:expr::bracket::type `]] + + def extra + length:collapser::collapsed + [length* filter:filter* visual:visual?] { + f:str = '' + for l:length in repeat(r1) { + if (f != '') + f = f + '*' + + if (l.expr.collapsed.result.value) { + f = f + '(' + $l.expr.collapsed.result.value + ')' + } else { + f = f + '(' + $l.expr.collapsed + ')' + } + } + lhs.length = collapser::collapsestr(f) + } + + def type + # enum name name ; + [cref:`enum WS+ parent:name::type WS+ primitive:primitive::type WS+ name:name::type extra:extra `;] commit + # struct name name ; + | [cref:`struct WS+ parent:name::type WS+ name:name::type extra:extra `;] commit + # name ; + | [primitive:primitive::type WS+ name:name::type extra:extra `;] commit + # select ((thing)) { ... } ; INVALID + | [container::select::type extra `;] commit { reject } + # select ((thing)) { ... } name ; INVALID + | [container::select::type primitive::type WS+ name::type extra `;] commit { reject } + # struct (optional) { ... } name ; INVALID + | [container::strukt::type primitive::type WS+ name::type extra `;] commit { reject } + # enum (optional) { ... } name ; + | [container:container::type primitive:primitive::type WS+ name:name::type extra:extra `;] commit + # select ((expr)) { ... } name ; + # struct (optional) { ... } name ; + | [container:container::type name:name::type extra:extra `;] + # (enum|struct) name { ... }; + | [container:container::type `;] + end + + def source + [items:declaration::type*] commit +end + +parse source:fspec::source[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:ref) { if (!s) return '' return s } + +str +signed_str(s:ref) { if (s) return 'signed' return 'unsigned' } + +void +print_declaration(d:fspec::declaration::type) +{ + insert('variable', $d.name, d) + print('variable `', $d.name, '` is ') + + 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 (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(' 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(' it has a constant length of `', $d.extra.length.result.value, '`\n') + } else if (d.extra.length.result.value.string) { + print(' its length will increase until pattern `', $d.extra.length.result.value.string.raw, '` has been read from stream\n') + } + } + } + + for f:fspec::declaration::filter in repeat(d.extra.filter) + print(' it needs to be filtered with `', $f.function, '`\n') + + for v:fspec::declaration::visual in child(d.extra.visual) + print(' and it should be visualized as `', $v.name, '`\n') + } +} + +void +walk(s:fspec::container::type) +{ + insert($s.data.type, s.data.name, s) + if ($s.data.type == 'enum') { + for i:fspec::container::enum::item in repeat(s.data.items) + insert('variable', $i.name, i) + } else if ($s.data.type == 'struct') { + push_scope() + for d:fspec::container::strukt::item in repeat(s.data.items) { + if (d.data.container) + walk(d.data.container) + if (d.data.name) + print_declaration(d.data) + } + pop_scope() + } else if ($s.data.type == 'select') { + push_scope() + for d:fspec::container::select::item in repeat(s.data.items) { + if (d.data.container) + walk(d.data.container) + if (d.data.name) + print_declaration(d.data) + } + pop_scope() + } +} + +push_scope() +for s:fspec::declaration::type in repeat(source.items) + walk(s.container) +pop_scope() -- cgit v1.2.3