From 2198478cd22cec4ea2b645d4e9f31ee9c2508ff5 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Tue, 9 Oct 2018 08:49:46 +0300 Subject: push for remote work --- Makefile | 5 +- misc/filespec.vim | 59 ++++++ misc/radare2/Makefile | 30 +++ misc/radare2/asm_fspec.c | 142 +++++++++++++ spec/ability.fspec | 103 ++++++++- spec/bsw.fspec | 2 +- spec/eaf.fspec | 15 +- spec/elf.fspec | 20 +- spec/spell.fspec | 38 ++-- src/bin/fspec-dump.c | 540 +++++++++++++++++++++++++++++++++++++++++++++++ src/bin/utils.h | 81 +++++++ src/compiler/compiler.lm | 424 ++++++++++++++++++++++++++----------- src/compiler/expr.lm | 4 +- src/compiler/native.c | 80 ++++++- vim/filespec.vim | 59 ------ 15 files changed, 1373 insertions(+), 229 deletions(-) create mode 100644 misc/filespec.vim create mode 100644 misc/radare2/Makefile create mode 100644 misc/radare2/asm_fspec.c create mode 100644 src/bin/fspec-dump.c create mode 100644 src/bin/utils.h delete mode 100644 vim/filespec.vim diff --git a/Makefile b/Makefile index b82ea95..dff16b4 100644 --- a/Makefile +++ b/Makefile @@ -8,11 +8,11 @@ WARNINGS := -Wall -Wextra -Wpedantic -Wformat=2 -Wstrict-aliasing=3 -Wstrict-ove -Wfloat-equal -Wcast-align -Wpointer-arith -Wchar-subscripts -Warray-bounds=2 override CFLAGS ?= -g -override CFLAGS += -std=c11 $(WARNINGS) +override CFLAGS += -std=c11 -D_DEFAULT_SOURCE $(WARNINGS) override CPPFLAGS += -Isrc override COLMFLAGS += -Isrc/compiler -bins = fspec-info dec2bin xidec xi2path xils xifile uneaf +bins = fspec-info fspec-dump dec2bin xidec xi2path xils xifile uneaf all: $(bins) %.c: %.lm @@ -35,6 +35,7 @@ fspec-compiler.a: src/compiler/compiler.c fspec-compiler-native.a fspec-info: private LDLIBS += -lcolm fspec-info: src/bin/fspec-info.c fspec-compiler.a fspec-compiler-native.a +fspec-dump: src/bin/fspec-dump.c dec2bin: src/bin/misc/dec2bin.c diff --git a/misc/filespec.vim b/misc/filespec.vim new file mode 100644 index 0000000..15664d7 --- /dev/null +++ b/misc/filespec.vim @@ -0,0 +1,59 @@ +" Vim syntax file +" Language: Filespec +" Author: Jari Vetoniemi + +syntax clear + +syn match fsBadContinuation contained "\\\s\+$" +syn keyword fsTodo contained TODO FIXME XXX +syn cluster fsCommentGroup contains=fsTodo,fsBadContinuation +syn region fsComment start="//" skip="\\$" end="$" keepend contains=@fsCommentGroup,@Spell + +syn keyword fsKeyword select until +syn keyword fsStructure enum struct +syn keyword fsConstant nul dec hex str true false +syn match fsPrimitive "[su][1-9][0-9]*" + +syn case ignore +syn match fsNumbers display transparent "\<\d\|\.\d" contains=fsNumber,fsFloat,fsOctalError,fsOctal +syn match fsNumbersCom display contained transparent "\<\d\|\.\d" contains=fsNumber,fsFloat,fsOctal +syn match fsNumber display contained "\d\+\(u\=l\{0,2}\|ll\=u\)\>" +syn match fsNumber display contained "0x\x\+\(u\=l\{0,2}\|ll\=u\)\>" +syn match fsOctal display contained "0\o\+\(u\=l\{0,2}\|ll\=u\)\>" contains=fsOctalZero +syn match fsOctalZero display contained "\<0" +syn match fsFloat display contained "\d\+f" +syn match fsFloat display contained "\d\+\.\d*\(e[-+]\=\d\+\)\=[fl]\=" +syn match fsFloat display contained "\.\d\+\(e[-+]\=\d\+\)\=[fl]\=\>" +syn match fsFloat display contained "\d\+e[-+]\=\d\+[fl]\=\>" +syn match fsOctalError display contained "0\o*[89]\d*" +syn case match + +syn match fsSpecial display contained "\\\(x\x\+\|\o\{1,3}\|.\|$\)" +syn match fsString1 "'[^']*'" contains=fsSpecial +syn match fsString2 '"[^"]*"' contains=fsSpecial +syn match fsBinary "0b[0-1x]\+" + +syn match fsBlock "[{}]" +syn match fsBracket "[\[\]]" +syn match fsOperator display "[-+&|<>=!*\/~.,;:%&^?()]" contains=fsComment + +" Define the default highlighting. +" Only used when an item doesn't have highlighting yet +hi def link fsTodo Todo +hi def link fsComment Comment +hi def link fsKeyword Keyword +hi def link fsStructure Structure +hi def link fsPrimitive Type +hi def link fsConstant Constant +hi def link fsBinary Number +hi def link fsNumber Number +hi def link fsOctal Number +hi def link fsOctalZero PreProc +hi def link fsFloat Float +hi def link fsOctalError Error +hi def link fsString1 Character +hi def link fsString2 Character +hi def link fsSpecial SpecialChar +hi def link fsBlock Constant +hi def link fsBracket Constant +hi def link fsOperator Operator diff --git a/misc/radare2/Makefile b/misc/radare2/Makefile new file mode 100644 index 0000000..aff2fdb --- /dev/null +++ b/misc/radare2/Makefile @@ -0,0 +1,30 @@ +MAKEFLAGS += --no-builtin-rules +R2_PLUGIN_PATH ?= $(shell r2 -H R2_USER_PLUGINS) + +# GCC 7: -Wstringop-overflow=, -Walloc-size-larger-than=, -Wduplicated-{branches,cond} +WARNINGS := -Wall -Wextra -Wformat=2 -Wstrict-aliasing=3 -Wstrict-overflow=5 -Wstack-usage=12500 \ + -Wfloat-equal -Wcast-align -Wpointer-arith -Wchar-subscripts -Warray-bounds=2 + +override CFLAGS ?= -g +override CFLAGS += -std=c11 $(WARNINGS) + +libs = asm_fspec.so +all: $(libs) + +%.so: + $(LINK.c) -fPIC -shared $(filter %.c,$^) $(LDLIBS) -o $@ + +asm_fspec.so: private CFLAGS += $(shell pkg-config --cflags r_anal) +asm_fspec.so: private LDLIBS += $(shell pkg-config --libs-only-l r_anal) +asm_fspec.so: asm_fspec.c + +install: $(libs) + install -Dm755 $^ -t "$(R2_PLUGIN_PATH)" + +uninstall: + $(RM) "$(R2_PLUGIN_PATH)"/asm_fspec.so + +clean: + $(RM) $(libs) + +.PHONY: all clean install uninstall diff --git a/misc/radare2/asm_fspec.c b/misc/radare2/asm_fspec.c new file mode 100644 index 0000000..fa7c1ad --- /dev/null +++ b/misc/radare2/asm_fspec.c @@ -0,0 +1,142 @@ +/* radare - LGPL - Copyright 2018 - Jari Vetoniemi */ + +#include +#include +#include +#include +#include +#include + +enum fspec_instruction { + INS_VERSION, + INS_REG, + INS_PUSH, + INS_PUSHR, + INS_STORE, + INS_OP, + INS_QUEUE, + INS_IO, + INS_EXEC, + INS_CALL, + INS_JMP, + INS_JMPIF +}; + +enum fspec_operation { + OP_UNM, + OP_LNOT, + OP_BNOT, + OP_MUL, + OP_DIV, + OP_MOD, + OP_ADD, + OP_SUB, + OP_SHIFTL, + OP_SHIFTR, + OP_LESS, + OP_LESSEQ, + OP_EQ, + OP_NOTEQ, + OP_BAND, + OP_BOR, + OP_BXOR, + OP_LAND, + OP_LOR, + OP_CTERNARY, + OP_SUBSCRIPT +}; + +static const char* +ins_name_str(const enum fspec_instruction name) +{ + switch (name) { + case INS_VERSION: return "version"; + case INS_REG: return "reg"; + case INS_PUSH: return "push"; + case INS_PUSHR: return "pushr"; + case INS_STORE: return "store"; + case INS_OP: return "op"; + case INS_QUEUE: return "queue"; + case INS_IO: return "io"; + case INS_EXEC: return "exec"; + case INS_CALL: return "call"; + case INS_JMP: return "jmp"; + case INS_JMPIF: return "jmpif"; + } + return "invalid"; +} + +static const char* +op_name_str(const enum fspec_operation op) +{ + switch (op) { + case OP_UNM: return "unm"; + case OP_LNOT: return "lnot"; + case OP_BNOT: return "bnot"; + case OP_MUL: return "mul"; + case OP_DIV: return "div"; + case OP_MOD: return "mod"; + case OP_ADD: return "add"; + case OP_SUB: return "sub"; + case OP_SHIFTL: return "shiftl"; + case OP_SHIFTR: return "shiftr"; + case OP_LESS: return "less"; + case OP_LESSEQ: return "lesseq"; + case OP_EQ: return "eq"; + case OP_NOTEQ: return "noteq"; + case OP_BAND: return "band"; + case OP_BOR: return "bor"; + case OP_BXOR: return "bxor"; + case OP_LAND: return "land"; + case OP_LOR: return "lor"; + case OP_CTERNARY: return "cternary"; + case OP_SUBSCRIPT: return "subscript"; + } + return "invalid"; +} + +static int +disassemble(RAsm *a, RAsmOp *op, const ut8 *buf, int len) +{ + union { + struct { unsigned name:5; unsigned n:2; uint64_t v:57; } ins; + uint8_t v[16]; + } u = {0}; + + memcpy(u.v, buf, R_MIN(sizeof(u.v[0]), len)); + const uint8_t insw = sizeof(uint16_t) * (1 << u.ins.n); + memcpy(u.v, buf, R_MIN(insw, len)); + const char *buf_asm = "invalid"; + + if (u.ins.name == INS_OP) + buf_asm = sdb_fmt("%s %s", ins_name_str(u.ins.name), op_name_str(u.ins.v)); + else if (u.ins.n == 0) + buf_asm = sdb_fmt("%s 0x%02x", ins_name_str(u.ins.name), (uint16_t)u.ins.v); + else if (u.ins.n == 1) + buf_asm = sdb_fmt("%s 0x%04x", ins_name_str(u.ins.name), (uint32_t)u.ins.v); + else if (u.ins.n == 2) + buf_asm = sdb_fmt("%s 0x%08x", ins_name_str(u.ins.name), (uint64_t)u.ins.v); + else + return 0; + + r_strbuf_set(&op->buf_asm, buf_asm); + return (op->size = insw + (u.ins.name == INS_REG ? u.ins.v : 0)); +} + +RAsmPlugin r_asm_plugin_fspec = { + .name = "fspec", + .license = "LGPL3", + .desc = "fspec disassembly plugin", + .arch = "fspec", + .bits = 16 | 32 | 64, + .endian = R_SYS_ENDIAN_LITTLE, + .disassemble = disassemble +}; + +#ifndef CORELIB +R_API RLibStruct radare_plugin = { + .type = R_LIB_TYPE_ASM, + .data = &r_asm_plugin_fspec, + .version = R2_VERSION +}; +#endif diff --git a/spec/ability.fspec b/spec/ability.fspec index 80a07fd..aab2bcf 100644 --- a/spec/ability.fspec +++ b/spec/ability.fspec @@ -1,11 +1,94 @@ -struct ability { - u16 index; - u16 icon_id; - u16 mp_cost; - u16 unknown; - u16 targets; - u8 name[32] | encoding('sjis') str; // The encoding actually depends on ROM region - u8 description[256] | encoding('sjis') str; // ^ Ditto, we can't express this (we need parser options) - u8 padding[726] nul; - struct ability ability[until (false)]; +struct dat { + struct asd { + u16 index; + u16 icon_id; + u16 mp_cost; + u16 unknown; + u16 targets; + u8 name[32] | encoding('sjis') str; // The encoding actually depends on ROM region + u8 description[256] | encoding('sjis') str; // ^ Ditto, we can't express this (we need parser options) + u8 padding[726] nul; + } ability[until (false)]; }; + +// # Instructions +// +// Instructions are variable-length with minimum size of 16 bits and maximum size of 128 bits[1]. +// They are encoded with the following schema `5:name 2:n (16 * 2^n - 7):modifiers`, where `name` specifies the +// instruction name and `n` specifies the total width of the instruction in bits with the formula `16 * 2^n`. +// +// Here's a table that shows how `n` maps to the instruction size: +// n | instruction size in bits +// 0 | 16 +// 1 | 32 +// 2 | 64 +// 3 | 128 +// +// In the list below the notation for instruction and its modifiers is `NAME<1:arg1, 1:arg2> [STACK1] (STACK2)` +// where `NAME` is the name of instruction, and inside the `<>` brackets are the modifiers for the instruction. +// The optional number before the modifier tells the modifier size in bits, if omitted rest of the bits are used. +// Inside `[]` brackets are the arguments that will be popped from the stack. `()` parenthesis are used instead, +// if the stack argument is optional. +// +// List of instructions: +// Name | Hex | Description +// VERSION | 0x00 | Indicates the version of this bytecode. +// REG | 0x01 | Allocates a new register. +// * * If `len` is not zero, the next `len` bytes will be stored in this register. +// PUSH | 0x02 | Pushes `v` to the stack. +// PUSHR | 0x03 | Pushes the contents of register at index `R` to the stack. +// STORE [v] | 0x04 | Stores `v` into register at index `R`. +// OP [...] | 0x05 | Performs operation specified by the `op`, and pushes the result to the stack. +// QUEUE (...) | 0x06 | Queues next `len` bytes for execution for the next `IO` instruction. +// * * The code is executed before or after `IO` instruction, depending whether VM is packing or unpacking. +// IO [R] (...) | 0x07 | Unpacking: Reads data from external VM input (usually a file) to register at index `R`. +// * * Packing: Writes data to external VM output (usually a file) from register at index `R`. +// * * `sz` is the size of the element in bits. +// * * Rest of the stack is the number of elements, if empty, the elements to read/write is 1. +// EXEC (...) | 0x08 | Executes instructions stored in register at index `R`. +// * * Rest of the stack is the number of times to execute, if empty, execution happens only once. +// CALL (...) | 0x09 | Calls a function. The name of the function is stored in register at index `R`. +// JMP | 0x0A | Jumps to the `off` (in bytes). +// JMPIF [v] | 0x0B | Performs `JMP`, if `v` is true. +// STRUCT [R2] | 0x0C | Describes register at index `R` as struct structure. Register at index `R2` contains the name of the struct. +// SELECT | 0x0D | Describes register at index `R` as select structure. +// FIELD (...) | 0x0E | Links field to last structure. Register at index `R` contains the name of the field. +// * * Rest of the stack contains register indices for registers that are instances of this field. +// BSZ [sz] | 0x0F | Describes last field as a primitive field. `s` describes whether the field is signed. `v` describes how the +// * * field should be represented. `sz` contains the size of field in bits. +// REF | 0x10 | Describes field as a substructure. Register at index `R` contains the structure definition. +// FDIMENSION | 0x11 | Adds fixed dimension to a field. `sz` indicates the size of the dimension. +// VDIMENSION | 0x12 | Adds variable length dimension to a field. +// ENUM | 0x13 | Links field to a enum +// +// List of operations for the `OP` instruction: +// Name | Hex | Argc | Description +// UNM | 0x00 | 1 | Unary minus operation `-r1`. +// LNOT | 0x01 | 1 | Logical not operation `!r1`. +// BNOT | 0x02 | 1 | Bitwise not operation `~r1`. +// MUL | 0x03 | 2 | Multiplication operation `r1 * r2`. +// DIV | 0x04 | 2 | Division operation `r1 * r2`. +// MOD | 0x05 | 2 | Modulo operation `r1 % r2`. +// ADD | 0x06 | 2 | Addition operation `r1 + r2`. +// SUB | 0x07 | 2 | Substraction operation `r1 - r2`. +// SHIFTL | 0x08 | 2 | Left shift operation `r1 << r2`. +// SHIFTR | 0x09 | 2 | Right shift operation `r1 >> r2`. +// LESS | 0x0A | 2 | Less than operation `r1 < r2`. +// LESSEQ | 0x0B | 2 | Less or equal operation `r1 <= r2`. +// EQ | 0x0C | 2 | Equal operation `r1 == r2`. +// NOTEQ | 0x0D | 2 | Not equal operation `r1 != r2`. +// BAND | 0x0E | 2 | Bitwise and operation `r1 & r2`. +// BOR | 0x0F | 2 | Bitwise or operation `r1 | r2`. +// BXOR | 0x10 | 2 | Bitwise xor operation `r1 ^ r2`. +// LAND | 0x11 | 2 | Logical and operation `r1 && r2`. +// LOR | 0x12 | 2 | Logical or operation `r1 || r2`. +// CTERNARY | 0x13 | 3 | Conditional ternary operation `r1 ? r2 : r3` +// SUBSCRIPT | 0x14 | 2 | Subscript operation `r1[r2]` +// +// List of visuals for the `PIO` instruction: +// Name | Hex | Representation +// NUL | 0x00 | None +// DEC | 0x01 | Decimal +// HEX | 0x02 | Hexdecimal +// STR | 0x03 | String +// FLT | 0x04 | Float diff --git a/spec/bsw.fspec b/spec/bsw.fspec index 5e868eb..3714337 100644 --- a/spec/bsw.fspec +++ b/spec/bsw.fspec @@ -1,4 +1,4 @@ -struct v { +struct bsw { u8 header[4] | matches('CWS\0') str; u32 size dec; u8 data[until(false)] | compression('zlib', size) hex; diff --git a/spec/eaf.fspec b/spec/eaf.fspec index 139539d..3373de9 100644 --- a/spec/eaf.fspec +++ b/spec/eaf.fspec @@ -1,10 +1,3 @@ -struct file { - u8 path[256] | encoding('ascii') str; - u64 offset; - u64 size; - u8 padding[16] nul; -}; - struct eaf { u8 header[4] | matches('#EAF') str; u16 major; @@ -13,5 +6,11 @@ struct eaf { u32 count; u64 unknown; u8 padding[100] nul; - struct file files[count]; + + struct { + u8 path[256] | encoding('ascii') str; + u64 offset hex; + u64 size; + u8 padding[16] nul; + } metadata[count]; }; diff --git a/spec/elf.fspec b/spec/elf.fspec index 6f70459..a9eb1a0 100644 --- a/spec/elf.fspec +++ b/spec/elf.fspec @@ -198,9 +198,23 @@ struct elf { u16 e_shnum; u16 e_shstrndx; - // TODO: need to handle offsets - struct program_header e_ph[e_phnum:e_phentsize]; - struct section_header e_sh[e_shnum:e_shentsize]; + // TODO: would be nicer if we didn't need this select + select (e_ident.ei_class) { + e_ident.CLASS_32) + struct { + seek(arch.elf32.e_phoff); + struct program_header e_ph[e_phnum:e_phentsize]; + seek(arch.elf32.e_shoff); + struct section_header e_sh[e_shnum:e_shentsize]; + } elf32; + e_ident.CLASS_64) + struct { + seek(arch.elf64.e_phoff); + struct program_header e_ph[e_phnum:e_phentsize]; + seek(arch.elf64.e_shoff); + struct section_header e_sh[e_shnum:e_shentsize]; + } elf64; + } headers; }; select (e_ident.ei_data) { diff --git a/spec/spell.fspec b/spec/spell.fspec index e1c012a..73b9cc4 100644 --- a/spec/spell.fspec +++ b/spec/spell.fspec @@ -1,22 +1,20 @@ -struct spell { - u16 index; - u16 type; // 1-6 for White/Black/Summon/Ninja/Bard/Blue - u16 element; - u16 targets; - u16 skill; - u16 mp_cost; - u8 casting_time; // in quarter of seconds - u8 recast_delay; // in quarter of seconds - u8 level[24] hex; // 1 byte per job, 0xxFF if not learnable, first slot is NONE job so always 0xFF - u16 id; // 0 for "unused" spells; often, but not always, equal to index - u8 unknown; - u8 jp_name[20] | encoding('sjis') str; - u8 en_name[20] | encoding('ascii') str; - u8 jp_description[128] | encoding('sjis') str; - u8 en_description[128] | encoding('ascii') str; - u8 padding[687] nul; -}; - struct dat { - struct spell spell[until (false)]; + struct { + u16 index; + u16 type; // 1-6 for White/Black/Summon/Ninja/Bard/Blue + u16 element; + u16 targets; + u16 skill; + u16 mp_cost; + u8 casting_time; // in quarter of seconds + u8 recast_delay; // in quarter of seconds + u8 level[24] hex; // 1 byte per job, 0xxFF if not learnable, first slot is NONE job so always 0xFF + u16 id; // 0 for "unused" spells; often, but not always, equal to index + u8 unknown; + u8 jp_name[20] | encoding('sjis') str; + u8 en_name[20] | encoding('ascii') str; + u8 jp_description[128] | encoding('sjis') str; + u8 en_description[128] | encoding('ascii') str; + u8 padding[687] nul; + } spell[until (false)]; }; diff --git a/src/bin/fspec-dump.c b/src/bin/fspec-dump.c new file mode 100644 index 0000000..195674f --- /dev/null +++ b/src/bin/fspec-dump.c @@ -0,0 +1,540 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#define DIV_ROUND_UP(a, b) (1 + ((a - 1) / b)) + +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + +static const int INDSTP = 3; + +static size_t +to_hex(const uint8_t *buf, const size_t buf_sz, char *out, const size_t out_sz, const bool reverse) +{ + assert(out); + const char nibble[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + const uint8_t nbs = sizeof(nibble) - 1; + + size_t w = 0, last_non_zero = w; + for (size_t i = 0; i < buf_sz && out_sz > 2 && w < out_sz - 2; ++i) { + for (uint8_t c = 0; c < CHAR_BIT / 8 && w < out_sz; ++c) { + const size_t idx = (reverse ? (buf_sz - 1) - i : i); + const uint8_t hi = (buf[idx] >> (4 * (c + 1))) & nbs; + const uint8_t lo = (buf[idx] >> (8 * c)) & nbs; + + if (w || hi || lo) { + out[w++] = nibble[hi]; + out[w++] = nibble[lo]; + last_non_zero = (hi || lo ? w : last_non_zero); + } + } + } + + if (!w) { + out[w++] = nibble[0]; + out[w++] = nibble[0]; + } else { + w = last_non_zero; + } + + assert(w < out_sz); + out[w] = 0; + return w; +} + +static void +print_dec(const uint8_t *buf, const size_t size, const bool is_signed) +{ + char hex[2 * sizeof(uint64_t) + 1]; + to_hex(buf, size, hex, sizeof(hex), true); + + if (is_signed) { + printf("%ld", (int64_t)strtoll(hex, NULL, 16)); + } else { + printf("%lu", (uint64_t)strtoull(hex, NULL, 16)); + } +} + +static void +print_udec(const uint8_t *buf, const size_t size) +{ + print_dec(buf, size, false); +} + +static void +print_sdec(const uint8_t *buf, const size_t size) +{ + print_dec(buf, size, true); +} + +static void +print_hex(const uint8_t *buf, const size_t size) +{ + char hex[2 * sizeof(uint64_t) + 1]; + to_hex(buf, size, hex, sizeof(hex), true); + printf("0x%s", hex); +} + +static void +print_array(const uint8_t *buf, const size_t size, const size_t nmemb, void (*fun)(const uint8_t *buf, const size_t size), const int sindent) +{ + const int indent = sindent + INDSTP; + if (nmemb > 8) { + printf("{\n%*s", indent, ""); + } else if (nmemb > 1) { + printf("{ "); + } + + for (size_t n = 0; n < nmemb; ++n) { + fun(buf + n * size, size); + printf("%s", (nmemb > 1 && n + 1 < nmemb ? ", " : "")); + + if (n + 1 < nmemb && !((n + 1) % 8)) + printf("\n%*s", indent, ""); + } + + if (nmemb > 8) { + printf("\n%*s}\n", sindent, ""); + } else { + printf("%s\n", (nmemb > 1 ? " }" : "")); + } +} + +static void +print_str(const char *buf, const size_t size, const size_t nmemb) +{ + const bool has_nl = memchr(buf, '\n', size * nmemb); + if (has_nl) + puts("```"); + + for (size_t n = 0; n < size * nmemb && buf[n] != 0; ++n) + printf("%c", buf[n]); + + puts((has_nl ? "```" : "")); +} + +enum fspec_visual { + VISUAL_NUL, + VISUAL_DEC, + VISUAL_HEX, + VISUAL_STR, + VISUAL_FLT +}; + +static void +fspec_print(const uint8_t *buf, const size_t size, const size_t nmemb, const bool sign, const enum fspec_visual visual, const int indent) +{ + const uint64_t szb = DIV_ROUND_UP(size, CHAR_BIT); + switch (visual) { + case VISUAL_STR: + print_str((char*)buf, szb, nmemb); + break; + + case VISUAL_HEX: + print_array(buf, szb, nmemb, print_hex, indent); + break; + + case VISUAL_DEC: + print_array(buf, szb, nmemb, (sign ? print_sdec : print_udec), indent); + break; + + case VISUAL_FLT: + // TODO + break; + + case VISUAL_NUL: + break; + } +} + +enum fspec_instruction { + INS_VERSION, + INS_REG, + INS_PUSH, + INS_PUSHR, + INS_STORE, + INS_OP, + INS_QUEUE, + INS_IO, + INS_EXEC, + INS_CALL, + INS_JMP, + INS_JMPIF, +}; + +enum fspec_operation { + OP_UNM, + OP_LNOT, + OP_BNOT, + OP_MUL, + OP_DIV, + OP_MOD, + OP_ADD, + OP_SUB, + OP_SHIFTL, + OP_SHIFTR, + OP_LESS, + OP_LESSEQ, + OP_EQ, + OP_NOTEQ, + OP_BAND, + OP_BOR, + OP_BXOR, + OP_LAND, + OP_LOR, + OP_CTERNARY, + OP_SUBSCRIPT +}; + +struct fspec_register { + uint64_t off, len; + uint8_t shift[2]; +}; + +struct fspec_stack { + struct fspec_register value[64]; + uint8_t numv; +}; + +struct fspec_istream { + size_t (*read)(void *ctx, void *buf, const size_t size); +}; + +struct fspec_buffer { + uint8_t *data; + uint64_t ptr, size; +}; + +static void +fspec_buffer_write(struct fspec_buffer *buf, const void *data, const size_t size) +{ + assert(buf->ptr + size <= buf->size); + memcpy(buf->data + buf->ptr, data, size); + buf->ptr += size; +} + +struct fspec_ctx { + struct fspec_buffer mem; + struct fspec_stack S, R; + struct fspec_istream ir, binary; +}; + +static void +stack_push_num(struct fspec_stack *stack, struct fspec_buffer *buf, const uint64_t v) +{ + assert(stack->numv < ARRAY_SIZE(stack->value)); + const uint8_t bsz = DIV_ROUND_UP(__builtin_ctzl((v ? v : 1)), CHAR_BIT); + stack->value[stack->numv++] = (struct fspec_register){ .off = buf->ptr, .len = bsz }; + const union { uint8_t u8[sizeof(v)]; uint64_t v; } u = { .v = v }; + fspec_buffer_write(buf, u.u8, bsz); +} + +static void +stack_push(struct fspec_stack *stack, struct fspec_register *value) +{ + assert(stack->numv < ARRAY_SIZE(stack->value)); + stack->value[stack->numv++] = *value; +} + +static void +stack_pop(struct fspec_stack *stack, struct fspec_register *out_value) +{ + assert(stack->numv > 0); + *out_value = stack->value[--stack->numv]; +} + +static uint64_t +stack_pop_num(struct fspec_stack *stack, const struct fspec_buffer *buf) +{ + assert(stack->numv > 0); + const struct fspec_register v = stack->value[--stack->numv]; + union { uint8_t u8[sizeof(uint64_t)]; uint64_t v; } u = {0}; + memcpy(u.u8, buf->data + v.off, MIN(v.len, sizeof(u.u8))); + return (u.v << v.shift[0]) >> v.shift[1]; +} + +static bool +is_binary(const uint8_t *data, const uint64_t len) +{ + for (uint64_t i = 0; i < len; ++i) + if (!isprint(data[i])) + return true; + return false; +} + +static void +fspec_seek(struct fspec_ctx *ctx) +{ + const uint64_t off = stack_pop_num(&ctx->S, &ctx->mem); + // fseek(ctx->input, off, SEEK_SET); +} + +static uint64_t +math(const enum fspec_operation op, const uint64_t r[3]) +{ + switch (op) { + case OP_UNM: return -r[0]; + case OP_LNOT: return !r[0]; + case OP_BNOT: return ~r[0]; + case OP_MUL: return r[0] * r[1]; + case OP_DIV: return r[0] / r[1]; + case OP_MOD: return r[0] % r[1]; + case OP_ADD: return r[0] + r[1]; + case OP_SUB: return r[0] - r[1]; + case OP_SHIFTL: return r[0] << r[1]; + case OP_SHIFTR: return r[0] >> r[1]; + case OP_LESS: return r[0] < r[1]; + case OP_LESSEQ: return r[0] <= r[1]; + case OP_EQ: return r[0] == r[1]; + case OP_NOTEQ: return r[0] != r[1]; + case OP_BAND: return r[0] & r[1]; + case OP_BOR: return r[0] | r[1]; + case OP_BXOR: return r[0] ^ r[1]; + case OP_LAND: return r[0] && r[1]; + case OP_LOR: return r[0] || r[1]; + case OP_CTERNARY: return r[0] ? r[1] : r[2]; + case OP_SUBSCRIPT: assert(0 && "should not happen"); + } + return 0; +} + +static void +do_op(struct fspec_ctx *ctx, const enum fspec_operation op) +{ + const struct { + char *name; + uint8_t args; + } map[] = { + { "UNM", 1 }, { "LNOT", 1 }, { "BNOT", 1 }, // unary + { "MUL", 2 }, { "DIV", 2 }, { "MOD", 2 }, { "ADD", 2 }, { "SUB", 2 }, // binary math + { "SHIFTL", 2 }, { "SHIFTR", 2 }, // bitshifts + { "LESS", 2 }, { "LESSEQ", 2 }, { "EQ", 2 }, { "NOTEQ", 2 }, // logical comparison + { "BAND", 2 }, { "BOR", 2 }, { "BXOR", 2 }, // bitwise operations + { "LAND", 2 }, { "LOR", 2 }, // logical and && or + { "CTERNARY", 3 }, // ternary + { "SUBSCRIPT", 2 }, // subscript + }; + + assert(op < ARRAY_SIZE(map)); + + uint64_t r[3]; + fprintf(stderr, "%s: ", map[op].name); + for (uint8_t i = 0; i < map[op].args; ++i) { + r[i] = stack_pop_num(&ctx->S, &ctx->mem); + fprintf(stderr, "%lu%s", r[i], (i + 1 < map[op].args ? ", " : "\n")); + } + + stack_push_num(&ctx->S, &ctx->mem, math(op, r)); +} + +static bool +fspec_execute(struct fspec_ctx *ctx, const uint8_t *ir, const uint64_t irlen, const int ind) +{ + const struct filter { + const char *name, **unpacking, **packing; + } filters[] = { + { "encoding", (const char*[]){ "iconv", "-f", NULL }, (const char*[]){ "iconv", "-t", NULL } } + }; + + const struct function { + const char *name; + void (*fun)(struct fspec_ctx *ctx); + } functions[] = { + { "seek", fspec_seek } + }; + + for (const uint8_t *pc = ir; pc < ir + irlen;) { + union { + struct { unsigned name:5; unsigned n:2; uint64_t v:57; } ins; + uint8_t v[16]; + } u = {0}; + + memcpy(u.v, pc, sizeof(u.v[0])); + const uint8_t insw = sizeof(uint16_t) * (1 << u.ins.n); + memcpy(u.v, pc, insw); + pc += insw; + + const uint64_t insv = u.ins.v; + switch (u.ins.name) { + case INS_VERSION: + fprintf(stderr, "VERSION: %lu\n", insv); + break; + case INS_REG: + stack_push(&ctx->R, (struct fspec_register[]){{ .off = pc - ctx->mem.data, .len = insv }}); + if (is_binary(pc, insv)) { + fprintf(stderr, "REG len: %lu, [binary data]\n", insv); + } else { + fprintf(stderr, "REG len: %lu, %.*s\n", insv, (int)insv, (char*)pc); + } + pc += insv; + break; + case INS_PUSH: + fprintf(stderr, "PUSH v: %lu\n", insv); + stack_push(&ctx->S, (struct fspec_register[]){{ .off = pc - ctx->mem.data - insw, .len = insw, .shift = {0,7} }}); + break; + case INS_PUSHR: + fprintf(stderr, "PUSHR R: %lu\n", insv); + stack_push(&ctx->S, &ctx->R.value[insv]); + break; + case INS_STORE: + fprintf(stderr, "STORE R: %lu\n", insv); + stack_pop(&ctx->S, &ctx->R.value[insv]); + break; + case INS_OP: + fprintf(stderr, "OP op: %lu\n", insv); + do_op(ctx, insv); + break; + case INS_QUEUE: + fprintf(stderr, "QUEUE len: %lu\n", insv); + break; + case INS_IO: { + const uint64_t R = stack_pop_num(&ctx->S, &ctx->mem); + fprintf(stderr, "IO: sz: %lu, R: %lu\n", insv, R); + ctx->R.value[R].off = ctx->mem.ptr; + const uint64_t szb = DIV_ROUND_UP(insv, CHAR_BIT), bpe = (szb * CHAR_BIT) / insv; + uint64_t nmemb = 1; + do { + nmemb *= (ctx->S.numv ? stack_pop_num(&ctx->S, &ctx->mem) : 1) / bpe; + assert(ctx->mem.ptr + szb * nmemb <= ctx->mem.size); + ctx->mem.ptr += ctx->binary.read(ctx, ctx->mem.data + ctx->mem.ptr, szb * nmemb); + } while (ctx->S.numv); + if (ctx->mem.ptr == ctx->R.value[R].off) + return true; + ctx->R.value[R].len = ctx->mem.ptr - ctx->R.value[R].off; + } + break; + case INS_EXEC: { + fprintf(stderr, "EXEC R: %lu\n", insv); + uint64_t nmemb = 1; + do { + nmemb *= (ctx->S.numv ? stack_pop_num(&ctx->S, &ctx->mem) : 1); + for (uint64_t i = 0; i < nmemb; ++i) + if (fspec_execute(ctx, ctx->mem.data + ctx->R.value[insv].off, ctx->R.value[insv].len, ind + INDSTP)) + return true; + } while (ctx->S.numv); + } + break; + case INS_CALL: { + fprintf(stderr, "CALL R: %lu\n", insv); + ctx->S.numv = 0; +#if 0 + const struct filter *filter = NULL; + const struct fspec_data *name = &ctx->D[num - 1]; + for (size_t i = 0; i < ARRAY_SIZE(filters); ++i) { + if (strlen(filters[i].name) != name->len || memcmp(filters[i].name, (char*)ctx->code + name->off, name->len)) + continue; + + filter = &filters[i]; + break; + } + + if (filter) { + size_t i; + const char *args[32]; + for (i = 0; filters->unpacking[i]; ++i) { + args[i] = filters->unpacking[i]; + fprintf(stderr, "%zu: %s\n", i, args[i]); + } + + size_t aw = 0; + char additional[1024]; + memset(additional, 0, sizeof(additional)); + for (; ctx->S.written; ++i) { + const struct fspec_value v = stack_pop(&ctx->S); + if (v.type == FSPEC_VALUE_NUMBER) { + aw += snprintf(additional, sizeof(additional) - aw, "%lu", v.u.num) + 1; + } else if (v.type == FSPEC_VALUE_DATA) { + args[i] = additional + aw; + memcpy(additional + aw, (char*)ctx->code + v.u.data.off, v.u.data.len); + aw += v.u.data.len + 1; + } else if (v.type == FSPEC_VALUE_FIELD) { + args[i] = additional + aw; + memcpy(additional + aw, (char*)ctx->binary + v.u.data.off, v.u.data.len); + aw += v.u.data.len + 1; + } + fprintf(stderr, "%zu: %s\n", i, args[i]); + } + args[i] = NULL; + + struct proc p; + if (proc_open(args[0], (char*const*)args, &p)) { + ctx->bsz -= write(p.fds[0], ctx->binary + last->u.primitive.data.off, ctx->bsz - last->u.primitive.data.off); + close_fd(&p.fds[0]); + assert(ctx->bsz == last->u.primitive.data.off); + ssize_t rd; + for (; (rd = read(p.fds[1], ctx->binary + last->u.primitive.data.off, 1024)) == 1024; ctx->bsz += rd); + ctx->bsz += rd; + proc_close(&p); + } else { + warn("failed to spawn: %s", args[0]); + } + } else { + ctx->S.numv = 0; + } +#endif + } + break; + case INS_JMP: + fprintf(stderr, "JMP off: %lu\n", insv); + pc = ir + insv; + break; + case INS_JMPIF: + fprintf(stderr, "JMPIF off: %lu\n", insv); + const uint64_t r1 = stack_pop_num(&ctx->S, &ctx->mem); + pc = (r1 ? ir + insv : pc); + break; + default: + errx(EXIT_FAILURE, "unknown instruction: %u\n", u.ins.name); + } + } + return false; +} + +static FILE *input; + +static size_t +read_binary(void *ctx, void *ptr, const size_t size) +{ + return fread(ptr, 1, size, input); +} + +static size_t +read_ir(void *ctx, void *ptr, const size_t size) +{ + return fread(ptr, 1, size, stdin); +} + +int +main(int argc, char *argv[]) +{ + input = fopen(argv[1], "rb"); + + + struct fspec_ctx ctx = { + .mem = { .data = calloc(4096, 4096), .size = 4096 * 4096 }, + .ir.read = read_ir, + .binary.read = read_binary, + .R.numv = 1 + }; + + ctx.mem.ptr += ctx.ir.read(&ctx, ctx.mem.data, ctx.mem.size); + fspec_execute(&ctx, ctx.mem.data, ctx.mem.ptr, 0); + fspec_execute(&ctx, ctx.mem.data + ctx.R.value[ctx.R.numv - 1].off, ctx.R.value[ctx.R.numv - 1].len, 0); + + for (uint64_t i = 0; i < ctx.R.numv; ++i) { + printf("REG%lu: ", i); + fspec_print(ctx.mem.data + ctx.R.value[i].off, 1, ctx.R.value[i].len, false, VISUAL_HEX, 0); + } + + return EXIT_SUCCESS; +} diff --git a/src/bin/utils.h b/src/bin/utils.h new file mode 100644 index 0000000..3baa1b1 --- /dev/null +++ b/src/bin/utils.h @@ -0,0 +1,81 @@ +#pragma once + +// #include +#include +#include + +struct proc { + pid_t pid; + int fds[2]; +}; + +static inline void +close_fd(int *fd) +{ + assert(fd); + if (*fd >= 0) + close(*fd); +} + +static inline bool +proc_open(const char *file, char *const argv[], struct proc *out_proc) +{ + assert(file && argv && out_proc); + *out_proc = (struct proc){0}; + + int pipes[4]; + pipe(&pipes[0]); /* parent */ + pipe(&pipes[2]); /* child */ + +#if 0 + // Doesn't work, no idea why + posix_spawn_file_actions_t fa; + if (posix_spawn_file_actions_init(&fa) != 0 || + posix_spawn_file_actions_addclose(&fa, pipes[0]) != 0 || + posix_spawn_file_actions_addclose(&fa, pipes[3]) != 0 || + posix_spawn_file_actions_adddup2(&fa, pipes[2], 0) != 0 || + posix_spawn_file_actions_adddup2(&fa, pipes[1], 1) != 0 || + posix_spawn_file_actions_addclose(&fa, pipes[2]) != 0 || + posix_spawn_file_actions_addclose(&fa, pipes[1]) != 0 || + posix_spawnp(&out_proc->pid, file, &fa, NULL, argv, NULL) != 0) { + posix_spawn_file_actions_destroy(&fa); + for (uint8_t i = 0; i < ARRAY_SIZE(pipes); ++i) + close(pipes[i]); + return false; + } + posix_spawn_file_actions_destroy(&fa); +#else + if ((out_proc->pid = fork()) > 0) { + out_proc->fds[0] = pipes[3]; + out_proc->fds[1] = pipes[0]; + close(pipes[1]); + close(pipes[2]); + return true; + } else { + close(pipes[0]); + close(pipes[3]); + dup2(pipes[2], 0); + dup2(pipes[1], 1); + close(pipes[2]); + close(pipes[1]); + execvp(file, argv); + _exit(0); + } +#endif + + out_proc->fds[0] = pipes[3]; + out_proc->fds[1] = pipes[0]; + close(pipes[1]); + close(pipes[2]); + return true; +} + +static inline void +proc_close(struct proc *proc) +{ + assert(proc); + waitpid(proc->pid, NULL, 0); + close_fd(&proc->fds[0]); + close_fd(&proc->fds[1]); + *proc = (struct proc){0}; +} diff --git a/src/compiler/compiler.lm b/src/compiler/compiler.lm index 3571527..d0864b4 100644 --- a/src/compiler/compiler.lm +++ b/src/compiler/compiler.lm @@ -40,7 +40,7 @@ context fspec 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 `= 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 @@ -81,14 +81,13 @@ context fspec context select lex ignore / '//' [^\n]* '\n' | space+ / - literal `( `) `{ `} `* + literal `( `) `{ `} `* `: end literal `select def item - [`* `) data:declaration::type] - | [expr:expr::paren::type `) data:declaration::type] + [expr:expr::select::type `) data:declaration::type] def type name:str @@ -105,7 +104,7 @@ context fspec lex ignore / '//' [^\n]* '\n' | space+ / literal `: `[ `] `| `; - token VISUAL / 'nul' | 'dec' | 'hex' | 'str' / + token VISUAL / 'nul' | 'dec' | 'hex' | 'str' | 'flt' / end literal `enum `struct @@ -116,39 +115,12 @@ context fspec def filter [`| function:reference::function::type] - def subscript + def dimension [`[ 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) - } + [dimension:dimension* filter:filter* visual:visual?] def type # enum name name ; @@ -170,6 +142,8 @@ context fspec | [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 @@ -210,15 +184,19 @@ 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 { - cmap:map = s->names->find(type) - if (cmap) { - var:any = cmap->find(name) - if (var) - return var - } + v:any = lookup_in_scope_no_error(type, name, s) + if (v) return v } return nil } @@ -249,9 +227,10 @@ insert(type:str, name:str, var:any) } any -lookup(type:str, name:str) +lookup(type:str, name:str, s:scope) { - r:any = lookup_no_error(type, name) + 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) @@ -259,119 +238,324 @@ lookup(type:str, name:str) return r } -str -container_name_str(s:str) { if (!s) return '' return s } +global g_cscope:map = new map() +global g_regs:map = new map() +global g_offs: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_STORE:int = 4 +global INS_OP:int = 5 +global INS_QUEUE:int = 6 +global INS_IO:int = 7 +global INS_EXEC:int = 8 +global INS_CALL:int = 9 +global INS_JMP:int = 10 +global INS_JMPIF:int = 11 + +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) + } -str -signed_str(s:bool) { if (s) return 'signed' return 'unsigned' } + if (!data) data = '' + write_ins_with_data(INS_REG, data) + print(flush_insbuf()) + g_regs->insert(v, g_regc) + g_regc = g_regc + 1 +} -global INDSTP:str = '░ ' +void +write_data_if_not_there(v:str) +{ + if (!g_regs->find(v)) + new_reg(v, v) +} void -print_declaration(d:fspec::declaration::type, ind:str) +find_data_in(s:any) { - insert('variable', $d.name, d) - print(ind, 'variable `', $d.name, "` that's ") + 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) +} - c:fspec::container::type - if (d.cref) c = lookup($d.cref, $d.parent) else c = d.container +write_ins(INS_VERSION, 1) +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.name) + write_data_if_not_there($d.name) + if (d.container && d.container.data.name) + write_data_if_not_there($d.container.data.name) +} - if (c) { - print('`', c.data.type, ' ', container_name_str(c.data.name), '` ') +global g_fcr:int = g_regc +write_ins_with_data(INS_REG, '') +g_regc = g_regc + 1 - if (c.data.select) - print('with expression `', $c.data.select, '` ') +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) + off = g_offs->find(%d.container) + } + } + write_ins(INS_PUSHR, off + g_regs->find(%d)) + } else { + write_ins(INS_OP, g_ops->find($vop.op)) + } + } } +} - if (d.primitive) - print(d.primitive.bits, ' bits and ', signed_str(d.primitive.signed)) +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) + } + } + } + } +} - print('\n') +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) { - 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 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) - print(ind, INDSTP, 'it needs to be filtered with `', $f.function, '`\n') + #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_PUSHR, g_fcr) + write_ins(INS_PUSH, index) + write_ins(INS_OP, g_ops->find('#+')) + write_ins(INS_IO, d.primitive.bits) + } else { + write_ins(INS_PUSH, g_offs->find(%c)) + write_ins(INS_STORE, g_fcr) + write_ins(INS_EXEC, g_regs->find(%c)) + } - for v:fspec::declaration::visual in child(d.extra.visual) - print(ind, INDSTP, 'it should be visualized as `', $v.name, '`\n') + if (d.extra) { + for l:fspec::declaration::dimension in repeat(d.extra.dimension) + write_postexpr(l.expr.collapsed.result, locs->find(%l)) } } void -walk(d:fspec::declaration::type, ind:str) +walk1(d:fspec::declaration::type) { if (!d.container) { print('something went wrong!\n') exit(1) } - s:fspec::container::type = d.container - insert($s.data.type, s.data.name, s) + for i:fspec::container::strukt::item in repeat(d.container.data.items) + if (i.data.container) + walk1(i.data) - 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') - } + for i:fspec::container::select::item in repeat(d.container.data.items) + if (i.data.container) + walk1(i.data) + + g_offs->insert(%d.container, g_regc) + + for i:fspec::container::enum::item in repeat(d.container.data.items) { + # somehow need to get this constant time (not reg) } - 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') { + for i:fspec::container::strukt::item in repeat(d.container.data.items) { + if (i.data.primitive) + new_reg(%i.data, nil) + } + + for i:fspec::container::select::item in repeat(d.container.data.items) { + if (i.data.primitive) + new_reg(%i.data, nil) + } +} + +void +walk2(d:fspec::declaration::type) +{ + 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::strukt::item in repeat(s.data.items) { - if (i.data.container) - walk(i.data, ind + INDSTP) - else - print_declaration(i.data, ind + INDSTP) + + 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.container) + walk2(i.data) + if (i.data.name) + insert('variable', $i.data.name, %i.data) + } + + for i:fspec::container::select::item in repeat(d.container.data.items) { + if (i.data.container) + walk2(i.data) + if (i.data.name) + insert('variable', $i.data.name, %i.data) + } + + if (!d.name) { + write_ins(INS_PUSH, g_offs->find(%d.container)) + write_ins(INS_STORE, g_fcr) + } + + index:int = 0 + 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)) } - 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) + + for i:fspec::container::select::item in repeat(d.container.data.items) { + if (i.data.name) { + 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 } - pop_scope() } + + if (insbuf_written()) { + new_reg(%d.container, flush_insbuf()) + print(flush_insbuf()) + } + + g_cscope->insert(%d, g_scopes->top) + + if ($d.container.data.type != 'enum') + pop_scope() } +for d:fspec::declaration::type in repeat(source.items) + walk1(d) + push_scope() for d:fspec::declaration::type in repeat(source.items) - walk(d, '') + walk2(d) pop_scope() + +print(flush_insbuf()) diff --git a/src/compiler/expr.lm b/src/compiler/expr.lm index feff7af..7c650b4 100644 --- a/src/compiler/expr.lm +++ b/src/compiler/expr.lm @@ -37,9 +37,9 @@ context expr def type collapsed:collapser::collapsed - [syntax+] { + [syntax+] commit { lhs.collapsed = collapser::stream($r1) - if (!lhs.collapsed) reject + if (!lhs.collapsed && $r1 != '*') reject } end diff --git a/src/compiler/native.c b/src/compiler/native.c index d0ca252..bbb0060 100644 --- a/src/compiler/native.c +++ b/src/compiler/native.c @@ -81,8 +81,7 @@ c_op_stack_top(program_t *prg, tree_t **sp, value_t a) return NULL; const union opstr op = { .u8 = stack->data[stack->index - 1] }; - tree_t *s = construct_string(prg, colm_string_alloc_pointer(prg, opstr_lookup + op.offset, 1 + op.size)); - return (str_t*)upref(prg, s); + return (str_t*)upref(prg, construct_string(prg, colm_string_alloc_pointer(prg, opstr_lookup + op.offset, 1 + op.size))); } value_t @@ -111,6 +110,77 @@ c_op_stack_pop(program_t *prg, tree_t **sp, value_t a) return r; } +static uint8_t +bits_for_n(const uint8_t n, const uint8_t used) +{ + return 16 * (1 << n) - used; +} + +static uint8_t +n_for_v(const uint64_t v, const uint8_t used) +{ + const uint8_t bits = __builtin_ctzl((v ? v : 1)); + if (used <= 16 && bits < bits_for_n(0, used)) + return 0; + else if (used <= 32 && bits < bits_for_n(1, used)) + return 1; + else if (used <= 64 && bits < bits_for_n(2, used)) + return 2; + + errx(EXIT_FAILURE, "numbers over 57 bits not supported right now.. sorry :D"); + return 3; +} + +static void +vle_instruction(const uint8_t name, const uint64_t v, uint8_t out[16], uint8_t *out_written) +{ + assert(out && out_written); + const union { + struct { unsigned name:5; unsigned n:2; uint64_t v:57; } ins; + uint8_t v[16]; + } u = { .ins = { .name = name, .n = n_for_v(v, 7), .v = v } }; + *out_written = sizeof(uint16_t) * (1 << u.ins.n); + memcpy(out, u.v, *out_written); +} + +struct insbuf { + uint8_t data[sizeof(uint16_t) * 1024]; + size_t written; +} insbuf = {0}; + +str_t* +c_flush_insbuf(program_t *prg, tree_t **sp) +{ + tree_t *s = upref(prg, construct_string(prg, string_alloc_full(prg, insbuf.data, insbuf.written))); + insbuf.written = 0; + return (str_t*)s; +} + +int +c_insbuf_written(program_t *prg, tree_t **sp) +{ + return insbuf.written; +} + +void +c_write_ins(program_t *prg, tree_t **sp, value_t a, value_t b) +{ + uint8_t out[16], written; + vle_instruction(a, b, out, &written); + memcpy(&insbuf.data[insbuf.written], out, written); + insbuf.written += written; +} + +void +c_write_ins_with_data(program_t *prg, tree_t **sp, value_t a, str_t *b) +{ + assert(b); + c_write_ins(prg, sp, a, b->value->length); + memcpy(&insbuf.data[insbuf.written], b->value->data, b->value->length); + insbuf.written += b->value->length; + colm_tree_downref(prg, sp, (tree_t*)b); +} + value_t c_strtoull(program_t *prg, tree_t **sp, str_t *a, value_t b) { @@ -131,6 +201,8 @@ str_t* c_esc2chr(program_t *prg, tree_t **sp, str_t *a) { assert(a); + const char hay = *a->value->data; + colm_tree_downref(prg, sp, (tree_t*)a); static const struct { const char e, v; } map[] = { { .e = 'a', .v = '\a' }, @@ -147,14 +219,14 @@ c_esc2chr(program_t *prg, tree_t **sp, str_t *a) }; for (size_t i = 0; i < ARRAY_SIZE(map); ++i) { - if (*a->value->data != map[i].e) + if (hay != map[i].e) continue; tree_t *s = construct_string(prg, colm_string_alloc_pointer(prg, &map[i].v, 1)); return (str_t*)upref(prg, s); } - errx(EXIT_FAILURE, "%s: unknown escape character `%c`", __func__, *a->value->data); + errx(EXIT_FAILURE, "%s: unknown escape character `%c`", __func__, hay); return NULL; } diff --git a/vim/filespec.vim b/vim/filespec.vim deleted file mode 100644 index 15664d7..0000000 --- a/vim/filespec.vim +++ /dev/null @@ -1,59 +0,0 @@ -" Vim syntax file -" Language: Filespec -" Author: Jari Vetoniemi - -syntax clear - -syn match fsBadContinuation contained "\\\s\+$" -syn keyword fsTodo contained TODO FIXME XXX -syn cluster fsCommentGroup contains=fsTodo,fsBadContinuation -syn region fsComment start="//" skip="\\$" end="$" keepend contains=@fsCommentGroup,@Spell - -syn keyword fsKeyword select until -syn keyword fsStructure enum struct -syn keyword fsConstant nul dec hex str true false -syn match fsPrimitive "[su][1-9][0-9]*" - -syn case ignore -syn match fsNumbers display transparent "\<\d\|\.\d" contains=fsNumber,fsFloat,fsOctalError,fsOctal -syn match fsNumbersCom display contained transparent "\<\d\|\.\d" contains=fsNumber,fsFloat,fsOctal -syn match fsNumber display contained "\d\+\(u\=l\{0,2}\|ll\=u\)\>" -syn match fsNumber display contained "0x\x\+\(u\=l\{0,2}\|ll\=u\)\>" -syn match fsOctal display contained "0\o\+\(u\=l\{0,2}\|ll\=u\)\>" contains=fsOctalZero -syn match fsOctalZero display contained "\<0" -syn match fsFloat display contained "\d\+f" -syn match fsFloat display contained "\d\+\.\d*\(e[-+]\=\d\+\)\=[fl]\=" -syn match fsFloat display contained "\.\d\+\(e[-+]\=\d\+\)\=[fl]\=\>" -syn match fsFloat display contained "\d\+e[-+]\=\d\+[fl]\=\>" -syn match fsOctalError display contained "0\o*[89]\d*" -syn case match - -syn match fsSpecial display contained "\\\(x\x\+\|\o\{1,3}\|.\|$\)" -syn match fsString1 "'[^']*'" contains=fsSpecial -syn match fsString2 '"[^"]*"' contains=fsSpecial -syn match fsBinary "0b[0-1x]\+" - -syn match fsBlock "[{}]" -syn match fsBracket "[\[\]]" -syn match fsOperator display "[-+&|<>=!*\/~.,;:%&^?()]" contains=fsComment - -" Define the default highlighting. -" Only used when an item doesn't have highlighting yet -hi def link fsTodo Todo -hi def link fsComment Comment -hi def link fsKeyword Keyword -hi def link fsStructure Structure -hi def link fsPrimitive Type -hi def link fsConstant Constant -hi def link fsBinary Number -hi def link fsNumber Number -hi def link fsOctal Number -hi def link fsOctalZero PreProc -hi def link fsFloat Float -hi def link fsOctalError Error -hi def link fsString1 Character -hi def link fsString2 Character -hi def link fsSpecial SpecialChar -hi def link fsBlock Constant -hi def link fsBracket Constant -hi def link fsOperator Operator -- cgit v1.2.3