5c05ce9b99
Today's milestone: dist/platform/elc compiles itself byte-for-byte to
itself (stage1 == stage2 == stage3 verified). The compiler is now a
real binary in the world.
What landed
- Spec rewrite (language.md) to truth — every feature marked
implemented / planned / not-in-this-language with no fiction.
- C runtime extension: 51 new builtins. JSON parser + accessors,
time, UUID, env, in-process state K/V, float formatting + math,
string ops (index_of, split, char_at, char_code, pad_left/right,
format), list ops (push, push_front, join, range), bool_to_str.
Runtime grew 631 → 1611 lines, header 171 → 247.
- Codegen fix: transform_implicit_return lifts a function's bare
trailing expression into an explicit return. Without it, lex(),
parse(), and every other implicit-return function returned 0/nil
and the whole pipeline produced empty C output.
- Codegen fix: index expressions dispatch on AST kind. obj["literal"]
→ el_get_field (map), arr[i] → el_list_get (list). Same Index node
in the parser, two different runtime calls.
- Codegen fix: skip emitting fn main() (collides with C main()) and
honor parsed return-type annotations so Void functions don't get
return-wrapped (return println(x) is a C type error).
- Parser: capture return-type identifier from -> Ret annotations.
- Lexer: + vessel keyword, + % operator, + \r escape.
- Runtime fix: el_list_append now allocates a fresh list rather than
realloc'ing the input. Realloc moved blocks made caller pointers
dangle, which was inserting garbage values into declared lists and
causing strcmp segfaults. Persistent allocation eliminates the
whole class of use-after-free at modest memory cost.
Bootstrap path
- One-shot Python helper translated elc-combined.el to C and
produced stage1. Helper is disposable; not committed.
- stage1 compiles elc-combined.el → stage2.c which cc compiles to
stage2; stage2 compiles elc-combined.el → stage3.c. stage2.c and
stage3.c are byte-identical. Closure proven.
- New elc installed at dist/platform/elc; old broken binary
preserved as dist/platform/elc.legacy.
- dist/platform/elc.c is the canonical generated source.
- elvm and the bytecode pipeline are no longer on the critical path.
Known gap
- The `+` operator's heuristic dispatch still picks string concat
when both operands are Idents with no literal anchor. Self-hosting
works because the compiler source is careful, but `fn add(a:Int,
b:Int) { a + b }` will not do arithmetic until codegen reads the
parsed type annotations to dispatch. Fix is wiring; not done here.
Tested
- tiny / lextest / whiletest / map+field / array build all run.
- cgi-studio (1037 lines real El) compiles to C cleanly. Link fails
only because runtime is missing fs_list, json_encode, llm_*; those
are scheduled batches.
- Three-stage closure (stage1 vs stage2 vs stage3) byte-identical.
2242 lines
74 KiB
C
2242 lines
74 KiB
C
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include "el_runtime.h"
|
|
|
|
el_val_t is_digit(el_val_t ch);
|
|
el_val_t is_alpha(el_val_t ch);
|
|
el_val_t is_alnum_or_underscore(el_val_t ch);
|
|
el_val_t is_whitespace(el_val_t ch);
|
|
el_val_t make_tok(el_val_t kind, el_val_t value);
|
|
el_val_t keyword_kind(el_val_t word);
|
|
el_val_t scan_digits(el_val_t chars, el_val_t start, el_val_t total);
|
|
el_val_t scan_ident(el_val_t chars, el_val_t start, el_val_t total);
|
|
el_val_t scan_string(el_val_t chars, el_val_t start, el_val_t total);
|
|
el_val_t lex(el_val_t source);
|
|
el_val_t tok_at(el_val_t tokens, el_val_t pos);
|
|
el_val_t tok_kind(el_val_t tokens, el_val_t pos);
|
|
el_val_t tok_value(el_val_t tokens, el_val_t pos);
|
|
el_val_t expect(el_val_t tokens, el_val_t pos, el_val_t kind);
|
|
el_val_t make_result(el_val_t node, el_val_t pos);
|
|
el_val_t skip_type(el_val_t tokens, el_val_t pos);
|
|
el_val_t parse_params(el_val_t tokens, el_val_t pos);
|
|
el_val_t parse_primary(el_val_t tokens, el_val_t pos);
|
|
el_val_t parse_if(el_val_t tokens, el_val_t pos);
|
|
el_val_t parse_match(el_val_t tokens, el_val_t pos);
|
|
el_val_t parse_pattern(el_val_t tokens, el_val_t pos);
|
|
el_val_t parse_for_expr(el_val_t tokens, el_val_t pos);
|
|
el_val_t parse_block(el_val_t tokens, el_val_t pos);
|
|
el_val_t parse_postfix(el_val_t tokens, el_val_t pos);
|
|
el_val_t op_precedence(el_val_t kind);
|
|
el_val_t is_binop(el_val_t kind);
|
|
el_val_t parse_binop(el_val_t tokens, el_val_t pos, el_val_t min_prec);
|
|
el_val_t parse_expr(el_val_t tokens, el_val_t pos);
|
|
el_val_t parse_stmt(el_val_t tokens, el_val_t pos);
|
|
el_val_t parse(el_val_t tokens);
|
|
el_val_t c_escape(el_val_t s);
|
|
el_val_t c_str_lit(el_val_t s);
|
|
el_val_t el_type_to_c(el_val_t type_str);
|
|
el_val_t emit_line(el_val_t line);
|
|
el_val_t emit_blank(void);
|
|
el_val_t binop_to_c(el_val_t op);
|
|
el_val_t cg_expr(el_val_t expr);
|
|
el_val_t list_contains(el_val_t lst, el_val_t s);
|
|
el_val_t cg_stmt(el_val_t stmt, el_val_t indent, el_val_t declared);
|
|
el_val_t strip_outer_parens(el_val_t s);
|
|
el_val_t cg_if_stmt(el_val_t expr, el_val_t indent, el_val_t declared);
|
|
el_val_t cg_for_body(el_val_t item, el_val_t list_expr, el_val_t body, el_val_t indent, el_val_t declared);
|
|
el_val_t cg_for_stmt(el_val_t expr, el_val_t indent, el_val_t declared);
|
|
el_val_t cg_stmts(el_val_t stmts, el_val_t indent, el_val_t declared);
|
|
el_val_t param_decl(el_val_t param, el_val_t idx);
|
|
el_val_t params_to_c(el_val_t params);
|
|
el_val_t transform_implicit_return(el_val_t body);
|
|
el_val_t cg_fn(el_val_t stmt);
|
|
el_val_t is_fndef(el_val_t stmt);
|
|
el_val_t is_top_level_decl(el_val_t stmt);
|
|
el_val_t codegen(el_val_t stmts, el_val_t source);
|
|
el_val_t compile(el_val_t source);
|
|
|
|
el_val_t is_digit(el_val_t ch) {
|
|
if (str_eq(ch, EL_STR("0"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("1"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("2"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("3"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("4"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("5"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("6"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("7"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("8"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("9"))) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
return 0;
|
|
}
|
|
|
|
el_val_t is_alpha(el_val_t ch) {
|
|
if (str_eq(ch, EL_STR("a"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("b"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("c"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("d"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("e"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("f"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("g"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("h"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("i"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("j"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("k"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("l"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("m"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("n"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("o"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("p"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("q"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("r"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("s"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("t"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("u"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("v"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("w"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("x"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("y"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("z"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("A"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("B"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("C"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("D"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("E"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("F"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("G"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("H"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("I"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("J"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("K"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("L"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("M"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("N"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("O"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("P"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("Q"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("R"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("S"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("T"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("U"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("V"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("W"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("X"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("Y"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("Z"))) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
return 0;
|
|
}
|
|
|
|
el_val_t is_alnum_or_underscore(el_val_t ch) {
|
|
if (is_digit(ch)) {
|
|
return 1;
|
|
}
|
|
if (is_alpha(ch)) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("_"))) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
return 0;
|
|
}
|
|
|
|
el_val_t is_whitespace(el_val_t ch) {
|
|
if (str_eq(ch, EL_STR(" "))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("\t"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("\n"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(ch, EL_STR("\r"))) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
return 0;
|
|
}
|
|
|
|
el_val_t make_tok(el_val_t kind, el_val_t value) {
|
|
return el_map_new(2, "kind", kind, "value", value);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t keyword_kind(el_val_t word) {
|
|
if (str_eq(word, EL_STR("let"))) {
|
|
return EL_STR("Let");
|
|
}
|
|
if (str_eq(word, EL_STR("fn"))) {
|
|
return EL_STR("Fn");
|
|
}
|
|
if (str_eq(word, EL_STR("type"))) {
|
|
return EL_STR("Type");
|
|
}
|
|
if (str_eq(word, EL_STR("enum"))) {
|
|
return EL_STR("Enum");
|
|
}
|
|
if (str_eq(word, EL_STR("match"))) {
|
|
return EL_STR("Match");
|
|
}
|
|
if (str_eq(word, EL_STR("return"))) {
|
|
return EL_STR("Return");
|
|
}
|
|
if (str_eq(word, EL_STR("if"))) {
|
|
return EL_STR("If");
|
|
}
|
|
if (str_eq(word, EL_STR("else"))) {
|
|
return EL_STR("Else");
|
|
}
|
|
if (str_eq(word, EL_STR("for"))) {
|
|
return EL_STR("For");
|
|
}
|
|
if (str_eq(word, EL_STR("in"))) {
|
|
return EL_STR("In");
|
|
}
|
|
if (str_eq(word, EL_STR("while"))) {
|
|
return EL_STR("While");
|
|
}
|
|
if (str_eq(word, EL_STR("import"))) {
|
|
return EL_STR("Import");
|
|
}
|
|
if (str_eq(word, EL_STR("from"))) {
|
|
return EL_STR("From");
|
|
}
|
|
if (str_eq(word, EL_STR("as"))) {
|
|
return EL_STR("As");
|
|
}
|
|
if (str_eq(word, EL_STR("with"))) {
|
|
return EL_STR("With");
|
|
}
|
|
if (str_eq(word, EL_STR("sealed"))) {
|
|
return EL_STR("Sealed");
|
|
}
|
|
if (str_eq(word, EL_STR("activate"))) {
|
|
return EL_STR("Activate");
|
|
}
|
|
if (str_eq(word, EL_STR("where"))) {
|
|
return EL_STR("Where");
|
|
}
|
|
if (str_eq(word, EL_STR("test"))) {
|
|
return EL_STR("Test");
|
|
}
|
|
if (str_eq(word, EL_STR("seed"))) {
|
|
return EL_STR("Seed");
|
|
}
|
|
if (str_eq(word, EL_STR("assert"))) {
|
|
return EL_STR("Assert");
|
|
}
|
|
if (str_eq(word, EL_STR("protocol"))) {
|
|
return EL_STR("Protocol");
|
|
}
|
|
if (str_eq(word, EL_STR("impl"))) {
|
|
return EL_STR("Impl");
|
|
}
|
|
if (str_eq(word, EL_STR("retry"))) {
|
|
return EL_STR("Retry");
|
|
}
|
|
if (str_eq(word, EL_STR("times"))) {
|
|
return EL_STR("Times");
|
|
}
|
|
if (str_eq(word, EL_STR("fallback"))) {
|
|
return EL_STR("Fallback");
|
|
}
|
|
if (str_eq(word, EL_STR("reason"))) {
|
|
return EL_STR("Reason");
|
|
}
|
|
if (str_eq(word, EL_STR("parallel"))) {
|
|
return EL_STR("Parallel");
|
|
}
|
|
if (str_eq(word, EL_STR("trace"))) {
|
|
return EL_STR("Trace");
|
|
}
|
|
if (str_eq(word, EL_STR("requires"))) {
|
|
return EL_STR("Requires");
|
|
}
|
|
if (str_eq(word, EL_STR("deploy"))) {
|
|
return EL_STR("Deploy");
|
|
}
|
|
if (str_eq(word, EL_STR("to"))) {
|
|
return EL_STR("To");
|
|
}
|
|
if (str_eq(word, EL_STR("via"))) {
|
|
return EL_STR("Via");
|
|
}
|
|
if (str_eq(word, EL_STR("target"))) {
|
|
return EL_STR("Target");
|
|
}
|
|
if (str_eq(word, EL_STR("true"))) {
|
|
return EL_STR("Bool");
|
|
}
|
|
if (str_eq(word, EL_STR("false"))) {
|
|
return EL_STR("Bool");
|
|
}
|
|
if (str_eq(word, EL_STR("cgi"))) {
|
|
return EL_STR("Cgi");
|
|
}
|
|
if (str_eq(word, EL_STR("manager"))) {
|
|
return EL_STR("Manager");
|
|
}
|
|
if (str_eq(word, EL_STR("engine"))) {
|
|
return EL_STR("Engine");
|
|
}
|
|
if (str_eq(word, EL_STR("accessor"))) {
|
|
return EL_STR("Accessor");
|
|
}
|
|
if (str_eq(word, EL_STR("vessel"))) {
|
|
return EL_STR("Vessel");
|
|
}
|
|
return EL_STR("");
|
|
return 0;
|
|
}
|
|
|
|
el_val_t scan_digits(el_val_t chars, el_val_t start, el_val_t total) {
|
|
el_val_t i = start;
|
|
el_val_t text = EL_STR("");
|
|
el_val_t running = 1;
|
|
while (running) {
|
|
if (i >= total) {
|
|
running = 0;
|
|
} else {
|
|
el_val_t ch = native_list_get(chars, i);
|
|
if (is_digit(ch)) {
|
|
text = el_str_concat(text, ch);
|
|
i = (i + 1);
|
|
} else {
|
|
running = 0;
|
|
}
|
|
}
|
|
}
|
|
return el_map_new(2, "text", text, "pos", i);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t scan_ident(el_val_t chars, el_val_t start, el_val_t total) {
|
|
el_val_t i = start;
|
|
el_val_t text = EL_STR("");
|
|
el_val_t running = 1;
|
|
while (running) {
|
|
if (i >= total) {
|
|
running = 0;
|
|
} else {
|
|
el_val_t ch = native_list_get(chars, i);
|
|
if (is_alnum_or_underscore(ch)) {
|
|
text = el_str_concat(text, ch);
|
|
i = (i + 1);
|
|
} else {
|
|
running = 0;
|
|
}
|
|
}
|
|
}
|
|
return el_map_new(2, "text", text, "pos", i);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t scan_string(el_val_t chars, el_val_t start, el_val_t total) {
|
|
el_val_t i = start;
|
|
el_val_t text = EL_STR("");
|
|
el_val_t running = 1;
|
|
while (running) {
|
|
if (i >= total) {
|
|
running = 0;
|
|
} else {
|
|
el_val_t ch = native_list_get(chars, i);
|
|
if (str_eq(ch, EL_STR("\\"))) {
|
|
el_val_t next_i = (i + 1);
|
|
if (next_i < total) {
|
|
el_val_t next_ch = native_list_get(chars, next_i);
|
|
if (str_eq(next_ch, EL_STR("\""))) {
|
|
text = el_str_concat(text, EL_STR("\""));
|
|
i = (next_i + 1);
|
|
} else {
|
|
if (str_eq(next_ch, EL_STR("n"))) {
|
|
text = el_str_concat(text, EL_STR("\n"));
|
|
i = (next_i + 1);
|
|
} else {
|
|
if (str_eq(next_ch, EL_STR("t"))) {
|
|
text = el_str_concat(text, EL_STR("\t"));
|
|
i = (next_i + 1);
|
|
} else {
|
|
if (str_eq(next_ch, EL_STR("r"))) {
|
|
text = el_str_concat(text, EL_STR("\r"));
|
|
i = (next_i + 1);
|
|
} else {
|
|
if (str_eq(next_ch, EL_STR("\\"))) {
|
|
text = el_str_concat(text, EL_STR("\\"));
|
|
i = (next_i + 1);
|
|
} else {
|
|
text = el_str_concat(text, next_ch);
|
|
i = (next_i + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
i = (i + 1);
|
|
}
|
|
} else {
|
|
if (str_eq(ch, EL_STR("\""))) {
|
|
i = (i + 1);
|
|
running = 0;
|
|
} else {
|
|
text = el_str_concat(text, ch);
|
|
i = (i + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return el_map_new(2, "text", text, "pos", i);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t lex(el_val_t source) {
|
|
el_val_t chars = native_string_chars(source);
|
|
el_val_t total = native_list_len(chars);
|
|
el_val_t tokens = native_list_empty();
|
|
el_val_t i = 0;
|
|
while (i < total) {
|
|
el_val_t ch = native_list_get(chars, i);
|
|
if (is_whitespace(ch)) {
|
|
i = (i + 1);
|
|
} else {
|
|
if (str_eq(ch, EL_STR("/"))) {
|
|
el_val_t next_i = (i + 1);
|
|
if (next_i < total) {
|
|
el_val_t next_ch = native_list_get(chars, next_i);
|
|
if (str_eq(next_ch, EL_STR("/"))) {
|
|
i = (i + 2);
|
|
el_val_t running2 = 1;
|
|
while (running2) {
|
|
if (i >= total) {
|
|
running2 = 0;
|
|
} else {
|
|
el_val_t lch = native_list_get(chars, i);
|
|
if (str_eq(lch, EL_STR("\n"))) {
|
|
running2 = 0;
|
|
} else {
|
|
i = (i + 1);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Slash"), EL_STR("/")));
|
|
i = (i + 1);
|
|
}
|
|
} else {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Slash"), EL_STR("/")));
|
|
i = (i + 1);
|
|
}
|
|
} else {
|
|
if (str_eq(ch, EL_STR("\""))) {
|
|
el_val_t result = scan_string(chars, (i + 1), total);
|
|
el_val_t str_text = el_get_field(result, EL_STR("text"));
|
|
el_val_t new_pos = el_get_field(result, EL_STR("pos"));
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Str"), str_text));
|
|
i = new_pos;
|
|
} else {
|
|
if (is_digit(ch)) {
|
|
el_val_t result = scan_digits(chars, i, total);
|
|
el_val_t num_text = el_get_field(result, EL_STR("text"));
|
|
el_val_t new_pos = el_get_field(result, EL_STR("pos"));
|
|
if (new_pos < total) {
|
|
el_val_t dot_ch = native_list_get(chars, new_pos);
|
|
if (str_eq(dot_ch, EL_STR("."))) {
|
|
el_val_t after_dot = (new_pos + 1);
|
|
if (after_dot < total) {
|
|
el_val_t after_dot_ch = native_list_get(chars, after_dot);
|
|
if (is_digit(after_dot_ch)) {
|
|
el_val_t frac_result = scan_digits(chars, after_dot, total);
|
|
el_val_t frac_text = el_get_field(frac_result, EL_STR("text"));
|
|
el_val_t frac_pos = el_get_field(frac_result, EL_STR("pos"));
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Float"), el_str_concat(el_str_concat(num_text, EL_STR(".")), frac_text)));
|
|
i = frac_pos;
|
|
} else {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Int"), num_text));
|
|
i = new_pos;
|
|
}
|
|
} else {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Int"), num_text));
|
|
i = new_pos;
|
|
}
|
|
} else {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Int"), num_text));
|
|
i = new_pos;
|
|
}
|
|
} else {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Int"), num_text));
|
|
i = new_pos;
|
|
}
|
|
} else {
|
|
if (is_alpha(ch) || str_eq(ch, EL_STR("_"))) {
|
|
el_val_t result = scan_ident(chars, i, total);
|
|
el_val_t word = el_get_field(result, EL_STR("text"));
|
|
el_val_t new_pos = el_get_field(result, EL_STR("pos"));
|
|
el_val_t kw = keyword_kind(word);
|
|
if (str_eq(kw, EL_STR(""))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Ident"), word));
|
|
} else {
|
|
tokens = native_list_append(tokens, make_tok(kw, word));
|
|
}
|
|
i = new_pos;
|
|
} else {
|
|
el_val_t peek_i = (i + 1);
|
|
el_val_t peek_ch = EL_STR("");
|
|
if (peek_i < total) {
|
|
peek_ch = native_list_get(chars, peek_i);
|
|
}
|
|
if (str_eq(ch, EL_STR("="))) {
|
|
if (str_eq(peek_ch, EL_STR("="))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("EqEq"), EL_STR("==")));
|
|
i = (i + 2);
|
|
} else {
|
|
if (str_eq(peek_ch, EL_STR(">"))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("FatArrow"), EL_STR("=>")));
|
|
i = (i + 2);
|
|
} else {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Eq"), EL_STR("=")));
|
|
i = (i + 1);
|
|
}
|
|
}
|
|
} else {
|
|
if (str_eq(ch, EL_STR("!"))) {
|
|
if (str_eq(peek_ch, EL_STR("="))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("NotEq"), EL_STR("!=")));
|
|
i = (i + 2);
|
|
} else {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Not"), EL_STR("!")));
|
|
i = (i + 1);
|
|
}
|
|
} else {
|
|
if (str_eq(ch, EL_STR("<"))) {
|
|
if (str_eq(peek_ch, EL_STR("="))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("LtEq"), EL_STR("<=")));
|
|
i = (i + 2);
|
|
} else {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Lt"), EL_STR("<")));
|
|
i = (i + 1);
|
|
}
|
|
} else {
|
|
if (str_eq(ch, EL_STR(">"))) {
|
|
if (str_eq(peek_ch, EL_STR("="))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("GtEq"), EL_STR(">=")));
|
|
i = (i + 2);
|
|
} else {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Gt"), EL_STR(">")));
|
|
i = (i + 1);
|
|
}
|
|
} else {
|
|
if (str_eq(ch, EL_STR("&"))) {
|
|
if (str_eq(peek_ch, EL_STR("&"))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("And"), EL_STR("&&")));
|
|
i = (i + 2);
|
|
} else {
|
|
i = (i + 1);
|
|
}
|
|
} else {
|
|
if (str_eq(ch, EL_STR("|"))) {
|
|
if (str_eq(peek_ch, EL_STR("|"))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Or"), EL_STR("||")));
|
|
i = (i + 2);
|
|
} else {
|
|
if (str_eq(peek_ch, EL_STR(">"))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("PipeOp"), EL_STR("|>")));
|
|
i = (i + 2);
|
|
} else {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Pipe"), EL_STR("|")));
|
|
i = (i + 1);
|
|
}
|
|
}
|
|
} else {
|
|
if (str_eq(ch, EL_STR("-"))) {
|
|
if (str_eq(peek_ch, EL_STR(">"))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Arrow"), EL_STR("->")));
|
|
i = (i + 2);
|
|
} else {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Minus"), EL_STR("-")));
|
|
i = (i + 1);
|
|
}
|
|
} else {
|
|
if (str_eq(ch, EL_STR(":"))) {
|
|
if (str_eq(peek_ch, EL_STR(":"))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("ColonColon"), EL_STR("::")));
|
|
i = (i + 2);
|
|
} else {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Colon"), EL_STR(":")));
|
|
i = (i + 1);
|
|
}
|
|
} else {
|
|
if (str_eq(ch, EL_STR("+"))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Plus"), EL_STR("+")));
|
|
i = (i + 1);
|
|
} else {
|
|
if (str_eq(ch, EL_STR("*"))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Star"), EL_STR("*")));
|
|
i = (i + 1);
|
|
} else {
|
|
if (str_eq(ch, EL_STR("%"))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Percent"), EL_STR("%")));
|
|
i = (i + 1);
|
|
} else {
|
|
if (str_eq(ch, EL_STR("("))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("LParen"), EL_STR("(")));
|
|
i = (i + 1);
|
|
} else {
|
|
if (str_eq(ch, EL_STR(")"))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("RParen"), EL_STR(")")));
|
|
i = (i + 1);
|
|
} else {
|
|
if (str_eq(ch, EL_STR("{"))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("LBrace"), EL_STR("{")));
|
|
i = (i + 1);
|
|
} else {
|
|
if (str_eq(ch, EL_STR("}"))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("RBrace"), EL_STR("}")));
|
|
i = (i + 1);
|
|
} else {
|
|
if (str_eq(ch, EL_STR("["))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("LBracket"), EL_STR("[")));
|
|
i = (i + 1);
|
|
} else {
|
|
if (str_eq(ch, EL_STR("]"))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("RBracket"), EL_STR("]")));
|
|
i = (i + 1);
|
|
} else {
|
|
if (str_eq(ch, EL_STR(","))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Comma"), EL_STR(",")));
|
|
i = (i + 1);
|
|
} else {
|
|
if (str_eq(ch, EL_STR("."))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Dot"), EL_STR(".")));
|
|
i = (i + 1);
|
|
} else {
|
|
if (str_eq(ch, EL_STR(";"))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Semicolon"), EL_STR(";")));
|
|
i = (i + 1);
|
|
} else {
|
|
if (str_eq(ch, EL_STR("@"))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("At"), EL_STR("@")));
|
|
i = (i + 1);
|
|
} else {
|
|
if (str_eq(ch, EL_STR("?"))) {
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("QuestionMark"), EL_STR("?")));
|
|
i = (i + 1);
|
|
} else {
|
|
i = (i + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
tokens = native_list_append(tokens, make_tok(EL_STR("Eof"), EL_STR("")));
|
|
return tokens;
|
|
return 0;
|
|
}
|
|
|
|
el_val_t tok_at(el_val_t tokens, el_val_t pos) {
|
|
return native_list_get(tokens, pos);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t tok_kind(el_val_t tokens, el_val_t pos) {
|
|
el_val_t t = native_list_get(tokens, pos);
|
|
return el_get_field(t, EL_STR("kind"));
|
|
return 0;
|
|
}
|
|
|
|
el_val_t tok_value(el_val_t tokens, el_val_t pos) {
|
|
el_val_t t = native_list_get(tokens, pos);
|
|
return el_get_field(t, EL_STR("value"));
|
|
return 0;
|
|
}
|
|
|
|
el_val_t expect(el_val_t tokens, el_val_t pos, el_val_t kind) {
|
|
el_val_t k = tok_kind(tokens, pos);
|
|
if (str_eq(k, kind)) {
|
|
return (pos + 1);
|
|
}
|
|
return (pos + 1);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t make_result(el_val_t node, el_val_t pos) {
|
|
return el_map_new(2, "node", node, "pos", pos);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t skip_type(el_val_t tokens, el_val_t pos) {
|
|
el_val_t k = tok_kind(tokens, pos);
|
|
if (str_eq(k, EL_STR("LBracket"))) {
|
|
el_val_t p = (pos + 1);
|
|
p = skip_type(tokens, p);
|
|
p = expect(tokens, p, EL_STR("RBracket"));
|
|
return p;
|
|
}
|
|
if (str_eq(k, EL_STR("Ident"))) {
|
|
el_val_t p = (pos + 1);
|
|
el_val_t k2 = tok_kind(tokens, p);
|
|
if (str_eq(k2, EL_STR("Lt"))) {
|
|
p = (p + 1);
|
|
el_val_t depth = 1;
|
|
el_val_t running = 1;
|
|
while (running) {
|
|
el_val_t kk = tok_kind(tokens, p);
|
|
if (str_eq(kk, EL_STR("Eof"))) {
|
|
running = 0;
|
|
} else {
|
|
if (str_eq(kk, EL_STR("Lt"))) {
|
|
depth = (depth + 1);
|
|
p = (p + 1);
|
|
} else {
|
|
if (str_eq(kk, EL_STR("Gt"))) {
|
|
depth = (depth - 1);
|
|
p = (p + 1);
|
|
if (depth <= 0) {
|
|
running = 0;
|
|
}
|
|
} else {
|
|
p = (p + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
el_val_t k3 = tok_kind(tokens, p);
|
|
if (str_eq(k3, EL_STR("QuestionMark"))) {
|
|
p = (p + 1);
|
|
}
|
|
return p;
|
|
}
|
|
if (str_eq(k2, EL_STR("QuestionMark"))) {
|
|
return (p + 1);
|
|
}
|
|
return p;
|
|
}
|
|
return (pos + 1);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t parse_params(el_val_t tokens, el_val_t pos) {
|
|
el_val_t p = expect(tokens, pos, EL_STR("LParen"));
|
|
el_val_t params = native_list_empty();
|
|
el_val_t running = 1;
|
|
while (running) {
|
|
el_val_t k = tok_kind(tokens, p);
|
|
if (str_eq(k, EL_STR("RParen"))) {
|
|
running = 0;
|
|
} else {
|
|
if (str_eq(k, EL_STR("Eof"))) {
|
|
running = 0;
|
|
} else {
|
|
el_val_t pname = tok_value(tokens, p);
|
|
p = (p + 1);
|
|
p = expect(tokens, p, EL_STR("Colon"));
|
|
p = skip_type(tokens, p);
|
|
el_val_t param = el_map_new(1, "name", pname);
|
|
params = native_list_append(params, param);
|
|
el_val_t k2 = tok_kind(tokens, p);
|
|
if (str_eq(k2, EL_STR("Comma"))) {
|
|
p = (p + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
p = expect(tokens, p, EL_STR("RParen"));
|
|
return el_map_new(2, "params", params, "pos", p);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t parse_primary(el_val_t tokens, el_val_t pos) {
|
|
el_val_t k = tok_kind(tokens, pos);
|
|
el_val_t v = tok_value(tokens, pos);
|
|
if (str_eq(k, EL_STR("Int"))) {
|
|
return make_result(el_map_new(2, "expr", EL_STR("Int"), "value", v), (pos + 1));
|
|
}
|
|
if (str_eq(k, EL_STR("Float"))) {
|
|
return make_result(el_map_new(2, "expr", EL_STR("Float"), "value", v), (pos + 1));
|
|
}
|
|
if (str_eq(k, EL_STR("Str"))) {
|
|
return make_result(el_map_new(2, "expr", EL_STR("Str"), "value", v), (pos + 1));
|
|
}
|
|
if (str_eq(k, EL_STR("Bool"))) {
|
|
return make_result(el_map_new(2, "expr", EL_STR("Bool"), "value", v), (pos + 1));
|
|
}
|
|
if (str_eq(k, EL_STR("Ident"))) {
|
|
return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1));
|
|
}
|
|
if (str_eq(k, EL_STR("LParen"))) {
|
|
el_val_t r = parse_expr(tokens, (pos + 1));
|
|
el_val_t node = el_get_field(r, EL_STR("node"));
|
|
el_val_t p = el_get_field(r, EL_STR("pos"));
|
|
p = expect(tokens, p, EL_STR("RParen"));
|
|
return make_result(node, p);
|
|
}
|
|
if (str_eq(k, EL_STR("LBracket"))) {
|
|
el_val_t p = (pos + 1);
|
|
el_val_t elems = native_list_empty();
|
|
el_val_t running = 1;
|
|
while (running) {
|
|
el_val_t k2 = tok_kind(tokens, p);
|
|
if (str_eq(k2, EL_STR("RBracket"))) {
|
|
running = 0;
|
|
} else {
|
|
if (str_eq(k2, EL_STR("Eof"))) {
|
|
running = 0;
|
|
} else {
|
|
el_val_t r = parse_expr(tokens, p);
|
|
el_val_t elem = el_get_field(r, EL_STR("node"));
|
|
p = el_get_field(r, EL_STR("pos"));
|
|
elems = native_list_append(elems, elem);
|
|
el_val_t k3 = tok_kind(tokens, p);
|
|
if (str_eq(k3, EL_STR("Comma"))) {
|
|
p = (p + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
p = expect(tokens, p, EL_STR("RBracket"));
|
|
return make_result(el_map_new(2, "expr", EL_STR("Array"), "elems", elems), p);
|
|
}
|
|
if (str_eq(k, EL_STR("LBrace"))) {
|
|
el_val_t p = (pos + 1);
|
|
el_val_t pairs = native_list_empty();
|
|
el_val_t running = 1;
|
|
while (running) {
|
|
el_val_t k2 = tok_kind(tokens, p);
|
|
if (str_eq(k2, EL_STR("RBrace"))) {
|
|
running = 0;
|
|
} else {
|
|
if (str_eq(k2, EL_STR("Eof"))) {
|
|
running = 0;
|
|
} else {
|
|
el_val_t key = tok_value(tokens, p);
|
|
p = (p + 1);
|
|
p = expect(tokens, p, EL_STR("Colon"));
|
|
el_val_t r = parse_expr(tokens, p);
|
|
el_val_t val_node = el_get_field(r, EL_STR("node"));
|
|
p = el_get_field(r, EL_STR("pos"));
|
|
el_val_t pair = el_map_new(2, "key", key, "value", val_node);
|
|
pairs = native_list_append(pairs, pair);
|
|
el_val_t k3 = tok_kind(tokens, p);
|
|
if (str_eq(k3, EL_STR("Comma"))) {
|
|
p = (p + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
p = expect(tokens, p, EL_STR("RBrace"));
|
|
return make_result(el_map_new(2, "expr", EL_STR("Map"), "pairs", pairs), p);
|
|
}
|
|
if (str_eq(k, EL_STR("If"))) {
|
|
el_val_t r = parse_if(tokens, pos);
|
|
return r;
|
|
}
|
|
if (str_eq(k, EL_STR("Match"))) {
|
|
el_val_t r = parse_match(tokens, pos);
|
|
return r;
|
|
}
|
|
if (str_eq(k, EL_STR("For"))) {
|
|
el_val_t r = parse_for_expr(tokens, pos);
|
|
return r;
|
|
}
|
|
if (str_eq(k, EL_STR("Not"))) {
|
|
el_val_t r = parse_primary(tokens, (pos + 1));
|
|
el_val_t inner = el_get_field(r, EL_STR("node"));
|
|
el_val_t p = el_get_field(r, EL_STR("pos"));
|
|
return make_result(el_map_new(2, "expr", EL_STR("Not"), "inner", inner), p);
|
|
}
|
|
if (str_eq(k, EL_STR("Minus"))) {
|
|
el_val_t r = parse_primary(tokens, (pos + 1));
|
|
el_val_t inner = el_get_field(r, EL_STR("node"));
|
|
el_val_t p = el_get_field(r, EL_STR("pos"));
|
|
return make_result(el_map_new(2, "expr", EL_STR("Neg"), "inner", inner), p);
|
|
}
|
|
return make_result(el_map_new(1, "expr", EL_STR("Nil")), (pos + 1));
|
|
return 0;
|
|
}
|
|
|
|
el_val_t parse_if(el_val_t tokens, el_val_t pos) {
|
|
el_val_t p = expect(tokens, pos, EL_STR("If"));
|
|
el_val_t r = parse_expr(tokens, p);
|
|
el_val_t cond = el_get_field(r, EL_STR("node"));
|
|
p = el_get_field(r, EL_STR("pos"));
|
|
el_val_t r2 = parse_block(tokens, p);
|
|
el_val_t then_stmts = el_get_field(r2, EL_STR("stmts"));
|
|
p = el_get_field(r2, EL_STR("pos"));
|
|
el_val_t has_else = 0;
|
|
el_val_t else_stmts = native_list_empty();
|
|
el_val_t k2 = tok_kind(tokens, p);
|
|
if (str_eq(k2, EL_STR("Else"))) {
|
|
p = (p + 1);
|
|
el_val_t k3 = tok_kind(tokens, p);
|
|
if (str_eq(k3, EL_STR("If"))) {
|
|
el_val_t r3 = parse_if(tokens, p);
|
|
el_val_t nested = el_get_field(r3, EL_STR("node"));
|
|
p = el_get_field(r3, EL_STR("pos"));
|
|
else_stmts = native_list_append(else_stmts, el_map_new(2, "stmt", EL_STR("Expr"), "value", nested));
|
|
has_else = 1;
|
|
} else {
|
|
el_val_t r3 = parse_block(tokens, p);
|
|
else_stmts = el_get_field(r3, EL_STR("stmts"));
|
|
p = el_get_field(r3, EL_STR("pos"));
|
|
has_else = 1;
|
|
}
|
|
}
|
|
return make_result(el_map_new(5, "expr", EL_STR("If"), "cond", cond, "then", then_stmts, "else", else_stmts, "has_else", has_else), p);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t parse_match(el_val_t tokens, el_val_t pos) {
|
|
el_val_t p = expect(tokens, pos, EL_STR("Match"));
|
|
el_val_t r = parse_expr(tokens, p);
|
|
el_val_t subject = el_get_field(r, EL_STR("node"));
|
|
p = el_get_field(r, EL_STR("pos"));
|
|
p = expect(tokens, p, EL_STR("LBrace"));
|
|
el_val_t arms = native_list_empty();
|
|
el_val_t running = 1;
|
|
while (running) {
|
|
el_val_t k = tok_kind(tokens, p);
|
|
if (str_eq(k, EL_STR("RBrace"))) {
|
|
running = 0;
|
|
} else {
|
|
if (str_eq(k, EL_STR("Eof"))) {
|
|
running = 0;
|
|
} else {
|
|
el_val_t r2 = parse_pattern(tokens, p);
|
|
el_val_t pattern = el_get_field(r2, EL_STR("node"));
|
|
p = el_get_field(r2, EL_STR("pos"));
|
|
p = expect(tokens, p, EL_STR("FatArrow"));
|
|
el_val_t r3 = parse_expr(tokens, p);
|
|
el_val_t body = el_get_field(r3, EL_STR("node"));
|
|
p = el_get_field(r3, EL_STR("pos"));
|
|
el_val_t arm = el_map_new(2, "pattern", pattern, "body", body);
|
|
arms = native_list_append(arms, arm);
|
|
el_val_t k2 = tok_kind(tokens, p);
|
|
if (str_eq(k2, EL_STR("Comma"))) {
|
|
p = (p + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
p = expect(tokens, p, EL_STR("RBrace"));
|
|
return make_result(el_map_new(3, "expr", EL_STR("Match"), "subject", subject, "arms", arms), p);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t parse_pattern(el_val_t tokens, el_val_t pos) {
|
|
el_val_t k = tok_kind(tokens, pos);
|
|
if (str_eq(k, EL_STR("Ident"))) {
|
|
el_val_t v = tok_value(tokens, pos);
|
|
if (str_eq(v, EL_STR("_"))) {
|
|
return make_result(el_map_new(1, "pattern", EL_STR("Wildcard")), (pos + 1));
|
|
}
|
|
return make_result(el_map_new(2, "pattern", EL_STR("Binding"), "name", v), (pos + 1));
|
|
}
|
|
if (str_eq(k, EL_STR("Int"))) {
|
|
return make_result(el_map_new(2, "pattern", EL_STR("LitInt"), "value", tok_value(tokens, pos)), (pos + 1));
|
|
}
|
|
if (str_eq(k, EL_STR("Str"))) {
|
|
return make_result(el_map_new(2, "pattern", EL_STR("LitStr"), "value", tok_value(tokens, pos)), (pos + 1));
|
|
}
|
|
if (str_eq(k, EL_STR("Bool"))) {
|
|
return make_result(el_map_new(2, "pattern", EL_STR("LitBool"), "value", tok_value(tokens, pos)), (pos + 1));
|
|
}
|
|
return make_result(el_map_new(1, "pattern", EL_STR("Wildcard")), (pos + 1));
|
|
return 0;
|
|
}
|
|
|
|
el_val_t parse_for_expr(el_val_t tokens, el_val_t pos) {
|
|
el_val_t p = expect(tokens, pos, EL_STR("For"));
|
|
el_val_t item_name = tok_value(tokens, p);
|
|
p = (p + 1);
|
|
p = expect(tokens, p, EL_STR("In"));
|
|
el_val_t r = parse_expr(tokens, p);
|
|
el_val_t list_expr = el_get_field(r, EL_STR("node"));
|
|
p = el_get_field(r, EL_STR("pos"));
|
|
el_val_t r2 = parse_block(tokens, p);
|
|
el_val_t body = el_get_field(r2, EL_STR("stmts"));
|
|
p = el_get_field(r2, EL_STR("pos"));
|
|
return make_result(el_map_new(4, "expr", EL_STR("For"), "item", item_name, "list", list_expr, "body", body), p);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t parse_block(el_val_t tokens, el_val_t pos) {
|
|
el_val_t p = expect(tokens, pos, EL_STR("LBrace"));
|
|
el_val_t stmts = native_list_empty();
|
|
el_val_t running = 1;
|
|
while (running) {
|
|
el_val_t k = tok_kind(tokens, p);
|
|
if (str_eq(k, EL_STR("RBrace"))) {
|
|
running = 0;
|
|
} else {
|
|
if (str_eq(k, EL_STR("Eof"))) {
|
|
running = 0;
|
|
} else {
|
|
el_val_t r = parse_stmt(tokens, p);
|
|
el_val_t stmt = el_get_field(r, EL_STR("node"));
|
|
p = el_get_field(r, EL_STR("pos"));
|
|
stmts = native_list_append(stmts, stmt);
|
|
}
|
|
}
|
|
}
|
|
p = expect(tokens, p, EL_STR("RBrace"));
|
|
return el_map_new(2, "stmts", stmts, "pos", p);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t parse_postfix(el_val_t tokens, el_val_t pos) {
|
|
el_val_t r = parse_primary(tokens, pos);
|
|
el_val_t node = el_get_field(r, EL_STR("node"));
|
|
el_val_t p = el_get_field(r, EL_STR("pos"));
|
|
el_val_t running = 1;
|
|
while (running) {
|
|
el_val_t k = tok_kind(tokens, p);
|
|
if (str_eq(k, EL_STR("LParen"))) {
|
|
p = (p + 1);
|
|
el_val_t args = native_list_empty();
|
|
el_val_t run2 = 1;
|
|
while (run2) {
|
|
el_val_t k2 = tok_kind(tokens, p);
|
|
if (str_eq(k2, EL_STR("RParen"))) {
|
|
run2 = 0;
|
|
} else {
|
|
if (str_eq(k2, EL_STR("Eof"))) {
|
|
run2 = 0;
|
|
} else {
|
|
el_val_t r2 = parse_expr(tokens, p);
|
|
el_val_t arg = el_get_field(r2, EL_STR("node"));
|
|
p = el_get_field(r2, EL_STR("pos"));
|
|
args = native_list_append(args, arg);
|
|
el_val_t k3 = tok_kind(tokens, p);
|
|
if (str_eq(k3, EL_STR("Comma"))) {
|
|
p = (p + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
p = expect(tokens, p, EL_STR("RParen"));
|
|
node = el_map_new(3, "expr", EL_STR("Call"), "func", node, "args", args);
|
|
} else {
|
|
if (str_eq(k, EL_STR("Dot"))) {
|
|
el_val_t field = tok_value(tokens, (p + 1));
|
|
p = (p + 2);
|
|
node = el_map_new(3, "expr", EL_STR("Field"), "object", node, "field", field);
|
|
} else {
|
|
if (str_eq(k, EL_STR("LBracket"))) {
|
|
el_val_t r2 = parse_expr(tokens, (p + 1));
|
|
el_val_t idx = el_get_field(r2, EL_STR("node"));
|
|
p = el_get_field(r2, EL_STR("pos"));
|
|
p = expect(tokens, p, EL_STR("RBracket"));
|
|
node = el_map_new(3, "expr", EL_STR("Index"), "object", node, "index", idx);
|
|
} else {
|
|
if (str_eq(k, EL_STR("QuestionMark"))) {
|
|
p = (p + 1);
|
|
node = el_map_new(2, "expr", EL_STR("Try"), "inner", node);
|
|
} else {
|
|
running = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return make_result(node, p);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t op_precedence(el_val_t kind) {
|
|
if (str_eq(kind, EL_STR("Or"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(kind, EL_STR("And"))) {
|
|
return 2;
|
|
}
|
|
if (str_eq(kind, EL_STR("EqEq"))) {
|
|
return 3;
|
|
}
|
|
if (str_eq(kind, EL_STR("NotEq"))) {
|
|
return 3;
|
|
}
|
|
if (str_eq(kind, EL_STR("Lt"))) {
|
|
return 4;
|
|
}
|
|
if (str_eq(kind, EL_STR("Gt"))) {
|
|
return 4;
|
|
}
|
|
if (str_eq(kind, EL_STR("LtEq"))) {
|
|
return 4;
|
|
}
|
|
if (str_eq(kind, EL_STR("GtEq"))) {
|
|
return 4;
|
|
}
|
|
if (str_eq(kind, EL_STR("Plus"))) {
|
|
return 5;
|
|
}
|
|
if (str_eq(kind, EL_STR("Minus"))) {
|
|
return 5;
|
|
}
|
|
if (str_eq(kind, EL_STR("Star"))) {
|
|
return 6;
|
|
}
|
|
if (str_eq(kind, EL_STR("Slash"))) {
|
|
return 6;
|
|
}
|
|
return 0;
|
|
return 0;
|
|
}
|
|
|
|
el_val_t is_binop(el_val_t kind) {
|
|
if (str_eq(kind, EL_STR("Or"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(kind, EL_STR("And"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(kind, EL_STR("EqEq"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(kind, EL_STR("NotEq"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(kind, EL_STR("Lt"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(kind, EL_STR("Gt"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(kind, EL_STR("LtEq"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(kind, EL_STR("GtEq"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(kind, EL_STR("Plus"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(kind, EL_STR("Minus"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(kind, EL_STR("Star"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(kind, EL_STR("Slash"))) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
return 0;
|
|
}
|
|
|
|
el_val_t parse_binop(el_val_t tokens, el_val_t pos, el_val_t min_prec) {
|
|
el_val_t r = parse_postfix(tokens, pos);
|
|
el_val_t left = el_get_field(r, EL_STR("node"));
|
|
el_val_t p = el_get_field(r, EL_STR("pos"));
|
|
el_val_t running = 1;
|
|
while (running) {
|
|
el_val_t k = tok_kind(tokens, p);
|
|
el_val_t prec = op_precedence(k);
|
|
if (is_binop(k)) {
|
|
if (prec >= min_prec) {
|
|
el_val_t op = k;
|
|
el_val_t r2 = parse_binop(tokens, (p + 1), (prec + 1));
|
|
el_val_t right = el_get_field(r2, EL_STR("node"));
|
|
p = el_get_field(r2, EL_STR("pos"));
|
|
left = el_map_new(4, "expr", EL_STR("BinOp"), "op", op, "left", left, "right", right);
|
|
} else {
|
|
running = 0;
|
|
}
|
|
} else {
|
|
running = 0;
|
|
}
|
|
}
|
|
return make_result(left, p);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t parse_expr(el_val_t tokens, el_val_t pos) {
|
|
return parse_binop(tokens, pos, 1);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t parse_stmt(el_val_t tokens, el_val_t pos) {
|
|
el_val_t k = tok_kind(tokens, pos);
|
|
if (str_eq(k, EL_STR("Let"))) {
|
|
el_val_t p = (pos + 1);
|
|
el_val_t name = tok_value(tokens, p);
|
|
p = (p + 1);
|
|
el_val_t k2 = tok_kind(tokens, p);
|
|
if (str_eq(k2, EL_STR("Colon"))) {
|
|
p = (p + 1);
|
|
p = skip_type(tokens, p);
|
|
}
|
|
p = expect(tokens, p, EL_STR("Eq"));
|
|
el_val_t r = parse_expr(tokens, p);
|
|
el_val_t val = el_get_field(r, EL_STR("node"));
|
|
p = el_get_field(r, EL_STR("pos"));
|
|
return make_result(el_map_new(3, "stmt", EL_STR("Let"), "name", name, "value", val), p);
|
|
}
|
|
if (str_eq(k, EL_STR("Return"))) {
|
|
el_val_t p = (pos + 1);
|
|
el_val_t k2 = tok_kind(tokens, p);
|
|
if (str_eq(k2, EL_STR("RBrace"))) {
|
|
return make_result(el_map_new(2, "stmt", EL_STR("Return"), "value", el_map_new(1, "expr", EL_STR("Nil"))), p);
|
|
}
|
|
if (str_eq(k2, EL_STR("Eof"))) {
|
|
return make_result(el_map_new(2, "stmt", EL_STR("Return"), "value", el_map_new(1, "expr", EL_STR("Nil"))), p);
|
|
}
|
|
el_val_t r = parse_expr(tokens, p);
|
|
el_val_t val = el_get_field(r, EL_STR("node"));
|
|
p = el_get_field(r, EL_STR("pos"));
|
|
return make_result(el_map_new(2, "stmt", EL_STR("Return"), "value", val), p);
|
|
}
|
|
if (str_eq(k, EL_STR("Fn"))) {
|
|
el_val_t p = (pos + 1);
|
|
el_val_t name = tok_value(tokens, p);
|
|
p = (p + 1);
|
|
el_val_t r = parse_params(tokens, p);
|
|
el_val_t params = el_get_field(r, EL_STR("params"));
|
|
p = el_get_field(r, EL_STR("pos"));
|
|
el_val_t ret_type = EL_STR("");
|
|
el_val_t k2 = tok_kind(tokens, p);
|
|
if (str_eq(k2, EL_STR("Arrow"))) {
|
|
p = (p + 1);
|
|
el_val_t kt = tok_kind(tokens, p);
|
|
if (str_eq(kt, EL_STR("Ident"))) {
|
|
ret_type = tok_value(tokens, p);
|
|
}
|
|
p = skip_type(tokens, p);
|
|
}
|
|
el_val_t r2 = parse_block(tokens, p);
|
|
el_val_t body = el_get_field(r2, EL_STR("stmts"));
|
|
p = el_get_field(r2, EL_STR("pos"));
|
|
return make_result(el_map_new(5, "stmt", EL_STR("FnDef"), "name", name, "params", params, "body", body, "ret_type", ret_type), p);
|
|
}
|
|
if (str_eq(k, EL_STR("Type"))) {
|
|
el_val_t p = (pos + 1);
|
|
el_val_t name = tok_value(tokens, p);
|
|
p = (p + 1);
|
|
p = expect(tokens, p, EL_STR("LBrace"));
|
|
el_val_t fields = native_list_empty();
|
|
el_val_t running = 1;
|
|
while (running) {
|
|
el_val_t k2 = tok_kind(tokens, p);
|
|
if (str_eq(k2, EL_STR("RBrace"))) {
|
|
running = 0;
|
|
} else {
|
|
if (str_eq(k2, EL_STR("Eof"))) {
|
|
running = 0;
|
|
} else {
|
|
el_val_t fname = tok_value(tokens, p);
|
|
p = (p + 1);
|
|
p = expect(tokens, p, EL_STR("Colon"));
|
|
p = skip_type(tokens, p);
|
|
fields = native_list_append(fields, el_map_new(1, "name", fname));
|
|
el_val_t k3 = tok_kind(tokens, p);
|
|
if (str_eq(k3, EL_STR("Comma"))) {
|
|
p = (p + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
p = expect(tokens, p, EL_STR("RBrace"));
|
|
return make_result(el_map_new(3, "stmt", EL_STR("TypeDef"), "name", name, "fields", fields), p);
|
|
}
|
|
if (str_eq(k, EL_STR("Enum"))) {
|
|
el_val_t p = (pos + 1);
|
|
el_val_t name = tok_value(tokens, p);
|
|
p = (p + 1);
|
|
p = expect(tokens, p, EL_STR("LBrace"));
|
|
el_val_t variants = native_list_empty();
|
|
el_val_t running = 1;
|
|
while (running) {
|
|
el_val_t k2 = tok_kind(tokens, p);
|
|
if (str_eq(k2, EL_STR("RBrace"))) {
|
|
running = 0;
|
|
} else {
|
|
if (str_eq(k2, EL_STR("Eof"))) {
|
|
running = 0;
|
|
} else {
|
|
el_val_t vname = tok_value(tokens, p);
|
|
p = (p + 1);
|
|
variants = native_list_append(variants, el_map_new(1, "name", vname));
|
|
el_val_t k3 = tok_kind(tokens, p);
|
|
if (str_eq(k3, EL_STR("Comma"))) {
|
|
p = (p + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
p = expect(tokens, p, EL_STR("RBrace"));
|
|
return make_result(el_map_new(3, "stmt", EL_STR("EnumDef"), "name", name, "variants", variants), p);
|
|
}
|
|
if (str_eq(k, EL_STR("Import"))) {
|
|
el_val_t p = (pos + 1);
|
|
el_val_t path = tok_value(tokens, p);
|
|
p = (p + 1);
|
|
return make_result(el_map_new(2, "stmt", EL_STR("Import"), "path", path), p);
|
|
}
|
|
if (str_eq(k, EL_STR("From"))) {
|
|
el_val_t p = (pos + 1);
|
|
el_val_t module_name = tok_value(tokens, p);
|
|
p = (p + 1);
|
|
el_val_t k2 = tok_kind(tokens, p);
|
|
if (str_eq(k2, EL_STR("Import"))) {
|
|
p = (p + 1);
|
|
}
|
|
el_val_t k3 = tok_kind(tokens, p);
|
|
if (str_eq(k3, EL_STR("LBrace"))) {
|
|
p = (p + 1);
|
|
el_val_t running = 1;
|
|
while (running) {
|
|
el_val_t k4 = tok_kind(tokens, p);
|
|
if (str_eq(k4, EL_STR("RBrace"))) {
|
|
running = 0;
|
|
} else {
|
|
if (str_eq(k4, EL_STR("Eof"))) {
|
|
running = 0;
|
|
} else {
|
|
p = (p + 1);
|
|
el_val_t k5 = tok_kind(tokens, p);
|
|
if (str_eq(k5, EL_STR("Comma"))) {
|
|
p = (p + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
p = expect(tokens, p, EL_STR("RBrace"));
|
|
}
|
|
return make_result(el_map_new(2, "stmt", EL_STR("Import"), "path", module_name), p);
|
|
}
|
|
if (str_eq(k, EL_STR("While"))) {
|
|
el_val_t p = (pos + 1);
|
|
el_val_t r = parse_expr(tokens, p);
|
|
el_val_t cond = el_get_field(r, EL_STR("node"));
|
|
p = el_get_field(r, EL_STR("pos"));
|
|
el_val_t r2 = parse_block(tokens, p);
|
|
el_val_t body = el_get_field(r2, EL_STR("stmts"));
|
|
p = el_get_field(r2, EL_STR("pos"));
|
|
return make_result(el_map_new(3, "stmt", EL_STR("While"), "cond", cond, "body", body), p);
|
|
}
|
|
if (str_eq(k, EL_STR("For"))) {
|
|
el_val_t p = (pos + 1);
|
|
el_val_t item_name = tok_value(tokens, p);
|
|
p = (p + 1);
|
|
p = expect(tokens, p, EL_STR("In"));
|
|
el_val_t r = parse_expr(tokens, p);
|
|
el_val_t list_expr = el_get_field(r, EL_STR("node"));
|
|
p = el_get_field(r, EL_STR("pos"));
|
|
el_val_t r2 = parse_block(tokens, p);
|
|
el_val_t body = el_get_field(r2, EL_STR("stmts"));
|
|
p = el_get_field(r2, EL_STR("pos"));
|
|
return make_result(el_map_new(4, "stmt", EL_STR("For"), "item", item_name, "list", list_expr, "body", body), p);
|
|
}
|
|
if (str_eq(k, EL_STR("At"))) {
|
|
el_val_t p = (pos + 1);
|
|
p = (p + 1);
|
|
return parse_stmt(tokens, p);
|
|
}
|
|
el_val_t r = parse_expr(tokens, pos);
|
|
el_val_t val = el_get_field(r, EL_STR("node"));
|
|
el_val_t p = el_get_field(r, EL_STR("pos"));
|
|
return make_result(el_map_new(2, "stmt", EL_STR("Expr"), "value", val), p);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t parse(el_val_t tokens) {
|
|
el_val_t total = native_list_len(tokens);
|
|
el_val_t stmts = native_list_empty();
|
|
el_val_t pos = 0;
|
|
el_val_t running = 1;
|
|
while (running) {
|
|
if (pos >= total) {
|
|
running = 0;
|
|
} else {
|
|
el_val_t k = tok_kind(tokens, pos);
|
|
if (str_eq(k, EL_STR("Eof"))) {
|
|
running = 0;
|
|
} else {
|
|
el_val_t r = parse_stmt(tokens, pos);
|
|
el_val_t stmt = el_get_field(r, EL_STR("node"));
|
|
el_val_t new_pos = el_get_field(r, EL_STR("pos"));
|
|
stmts = native_list_append(stmts, stmt);
|
|
if (new_pos <= pos) {
|
|
pos = (pos + 1);
|
|
} else {
|
|
pos = new_pos;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return stmts;
|
|
return 0;
|
|
}
|
|
|
|
el_val_t c_escape(el_val_t s) {
|
|
el_val_t chars = native_string_chars(s);
|
|
el_val_t total = native_list_len(chars);
|
|
el_val_t out = EL_STR("");
|
|
el_val_t i = 0;
|
|
while (i < total) {
|
|
el_val_t ch = native_list_get(chars, i);
|
|
if (str_eq(ch, EL_STR("\""))) {
|
|
out = el_str_concat(out, EL_STR("\\\""));
|
|
} else {
|
|
if (str_eq(ch, EL_STR("\\"))) {
|
|
out = el_str_concat(out, EL_STR("\\\\"));
|
|
} else {
|
|
if (str_eq(ch, EL_STR("\n"))) {
|
|
out = el_str_concat(out, EL_STR("\\n"));
|
|
} else {
|
|
if (str_eq(ch, EL_STR("\r"))) {
|
|
out = el_str_concat(out, EL_STR("\\r"));
|
|
} else {
|
|
if (str_eq(ch, EL_STR("\t"))) {
|
|
out = el_str_concat(out, EL_STR("\\t"));
|
|
} else {
|
|
out = el_str_concat(out, ch);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
i = (i + 1);
|
|
}
|
|
return out;
|
|
return 0;
|
|
}
|
|
|
|
el_val_t c_str_lit(el_val_t s) {
|
|
return el_str_concat(el_str_concat(EL_STR("\""), c_escape(s)), EL_STR("\""));
|
|
return 0;
|
|
}
|
|
|
|
el_val_t el_type_to_c(el_val_t type_str) {
|
|
if (str_eq(type_str, EL_STR("String"))) {
|
|
return EL_STR("const char*");
|
|
}
|
|
if (str_eq(type_str, EL_STR("Int"))) {
|
|
return EL_STR("int64_t");
|
|
}
|
|
if (str_eq(type_str, EL_STR("Bool"))) {
|
|
return EL_STR("int");
|
|
}
|
|
if (str_eq(type_str, EL_STR("Float"))) {
|
|
return EL_STR("double");
|
|
}
|
|
if (str_eq(type_str, EL_STR("Void"))) {
|
|
return EL_STR("void");
|
|
}
|
|
if (str_eq(type_str, EL_STR("void"))) {
|
|
return EL_STR("void");
|
|
}
|
|
return EL_STR("void*");
|
|
return 0;
|
|
}
|
|
|
|
el_val_t emit_line(el_val_t line) {
|
|
println(line);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t emit_blank(void) {
|
|
println(EL_STR(""));
|
|
return 0;
|
|
}
|
|
|
|
el_val_t binop_to_c(el_val_t op) {
|
|
if (str_eq(op, EL_STR("Plus"))) {
|
|
return EL_STR("+");
|
|
}
|
|
if (str_eq(op, EL_STR("Minus"))) {
|
|
return EL_STR("-");
|
|
}
|
|
if (str_eq(op, EL_STR("Star"))) {
|
|
return EL_STR("*");
|
|
}
|
|
if (str_eq(op, EL_STR("Slash"))) {
|
|
return EL_STR("/");
|
|
}
|
|
if (str_eq(op, EL_STR("EqEq"))) {
|
|
return EL_STR("==");
|
|
}
|
|
if (str_eq(op, EL_STR("NotEq"))) {
|
|
return EL_STR("!=");
|
|
}
|
|
if (str_eq(op, EL_STR("Lt"))) {
|
|
return EL_STR("<");
|
|
}
|
|
if (str_eq(op, EL_STR("Gt"))) {
|
|
return EL_STR(">");
|
|
}
|
|
if (str_eq(op, EL_STR("LtEq"))) {
|
|
return EL_STR("<=");
|
|
}
|
|
if (str_eq(op, EL_STR("GtEq"))) {
|
|
return EL_STR(">=");
|
|
}
|
|
if (str_eq(op, EL_STR("And"))) {
|
|
return EL_STR("&&");
|
|
}
|
|
if (str_eq(op, EL_STR("Or"))) {
|
|
return EL_STR("||");
|
|
}
|
|
return op;
|
|
return 0;
|
|
}
|
|
|
|
el_val_t cg_expr(el_val_t expr) {
|
|
el_val_t kind = el_get_field(expr, EL_STR("expr"));
|
|
if (str_eq(kind, EL_STR("Int"))) {
|
|
el_val_t v = el_get_field(expr, EL_STR("value"));
|
|
return v;
|
|
}
|
|
if (str_eq(kind, EL_STR("Float"))) {
|
|
el_val_t v = el_get_field(expr, EL_STR("value"));
|
|
return v;
|
|
}
|
|
if (str_eq(kind, EL_STR("Str"))) {
|
|
el_val_t v = el_get_field(expr, EL_STR("value"));
|
|
return el_str_concat(el_str_concat(EL_STR("EL_STR("), c_str_lit(v)), EL_STR(")"));
|
|
}
|
|
if (str_eq(kind, EL_STR("Bool"))) {
|
|
el_val_t v = el_get_field(expr, EL_STR("value"));
|
|
if (str_eq(v, EL_STR("true"))) {
|
|
return EL_STR("1");
|
|
}
|
|
return EL_STR("0");
|
|
}
|
|
if (str_eq(kind, EL_STR("Nil"))) {
|
|
return EL_STR("EL_NULL");
|
|
}
|
|
if (str_eq(kind, EL_STR("Ident"))) {
|
|
el_val_t name = el_get_field(expr, EL_STR("name"));
|
|
return name;
|
|
}
|
|
if (str_eq(kind, EL_STR("Not"))) {
|
|
el_val_t inner = el_get_field(expr, EL_STR("inner"));
|
|
el_val_t inner_c = cg_expr(inner);
|
|
return el_str_concat(EL_STR("!"), inner_c);
|
|
}
|
|
if (str_eq(kind, EL_STR("Neg"))) {
|
|
el_val_t inner = el_get_field(expr, EL_STR("inner"));
|
|
el_val_t inner_c = cg_expr(inner);
|
|
return el_str_concat(el_str_concat(EL_STR("(-"), inner_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(kind, EL_STR("BinOp"))) {
|
|
el_val_t op = el_get_field(expr, EL_STR("op"));
|
|
el_val_t left = el_get_field(expr, EL_STR("left"));
|
|
el_val_t right = el_get_field(expr, EL_STR("right"));
|
|
el_val_t left_c = cg_expr(left);
|
|
el_val_t right_c = cg_expr(right);
|
|
el_val_t left_kind = el_get_field(left, EL_STR("expr"));
|
|
el_val_t right_kind = el_get_field(right, EL_STR("expr"));
|
|
if (str_eq(op, EL_STR("Plus"))) {
|
|
if (str_eq(left_kind, EL_STR("Str"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_str_concat("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(right_kind, EL_STR("Str"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_str_concat("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(left_kind, EL_STR("Int"))) {
|
|
el_val_t op_c = binop_to_c(op);
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" ")), op_c), EL_STR(" ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(right_kind, EL_STR("Int"))) {
|
|
el_val_t op_c = binop_to_c(op);
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" ")), op_c), EL_STR(" ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(left_kind, EL_STR("Call"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_str_concat("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(right_kind, EL_STR("Call"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_str_concat("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(left_kind, EL_STR("BinOp"))) {
|
|
el_val_t left_op = el_get_field(left, EL_STR("op"));
|
|
if (str_eq(left_op, EL_STR("Plus"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_str_concat("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
}
|
|
if (str_eq(right_kind, EL_STR("BinOp"))) {
|
|
el_val_t right_op = el_get_field(right, EL_STR("op"));
|
|
if (str_eq(right_op, EL_STR("Plus"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_str_concat("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
}
|
|
if (str_eq(left_kind, EL_STR("Ident"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_str_concat("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(right_kind, EL_STR("Ident"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_str_concat("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
}
|
|
if (str_eq(op, EL_STR("EqEq"))) {
|
|
if (str_eq(left_kind, EL_STR("Int"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" == ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(right_kind, EL_STR("Int"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" == ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(left_kind, EL_STR("Bool"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" == ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(right_kind, EL_STR("Bool"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" == ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(left_kind, EL_STR("Str"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("str_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(right_kind, EL_STR("Str"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("str_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(left_kind, EL_STR("Ident"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("str_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(right_kind, EL_STR("Ident"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("str_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(left_kind, EL_STR("Call"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("str_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(right_kind, EL_STR("Call"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("str_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
}
|
|
if (str_eq(op, EL_STR("NotEq"))) {
|
|
if (str_eq(left_kind, EL_STR("Int"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" != ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(right_kind, EL_STR("Int"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" != ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(left_kind, EL_STR("Bool"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" != ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(right_kind, EL_STR("Bool"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" != ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(left_kind, EL_STR("Str"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("!str_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(right_kind, EL_STR("Str"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("!str_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(left_kind, EL_STR("Ident"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("!str_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(right_kind, EL_STR("Ident"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("!str_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(left_kind, EL_STR("Call"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("!str_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(right_kind, EL_STR("Call"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("!str_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
|
}
|
|
}
|
|
el_val_t op_c = binop_to_c(op);
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" ")), op_c), EL_STR(" ")), right_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(kind, EL_STR("Call"))) {
|
|
el_val_t func = el_get_field(expr, EL_STR("func"));
|
|
el_val_t args = el_get_field(expr, EL_STR("args"));
|
|
el_val_t arity = native_list_len(args);
|
|
el_val_t func_kind = el_get_field(func, EL_STR("expr"));
|
|
el_val_t args_c = EL_STR("");
|
|
el_val_t i = 0;
|
|
while (i < arity) {
|
|
el_val_t arg = native_list_get(args, i);
|
|
el_val_t arg_c = cg_expr(arg);
|
|
if (i > 0) {
|
|
args_c = el_str_concat(args_c, EL_STR(", "));
|
|
}
|
|
args_c = el_str_concat(args_c, arg_c);
|
|
i = (i + 1);
|
|
}
|
|
if (str_eq(func_kind, EL_STR("Ident"))) {
|
|
el_val_t fn_name = el_get_field(func, EL_STR("name"));
|
|
return el_str_concat(el_str_concat(el_str_concat(fn_name, EL_STR("(")), args_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(func_kind, EL_STR("Field"))) {
|
|
el_val_t obj = el_get_field(func, EL_STR("object"));
|
|
el_val_t field = el_get_field(func, EL_STR("field"));
|
|
el_val_t obj_c = cg_expr(obj);
|
|
if (arity > 0) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(field, EL_STR("(")), obj_c), EL_STR(", ")), args_c), EL_STR(")"));
|
|
}
|
|
return el_str_concat(el_str_concat(el_str_concat(field, EL_STR("(")), obj_c), EL_STR(")"));
|
|
}
|
|
el_val_t fn_c = cg_expr(func);
|
|
return el_str_concat(el_str_concat(el_str_concat(fn_c, EL_STR("(")), args_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(kind, EL_STR("Field"))) {
|
|
el_val_t obj = el_get_field(expr, EL_STR("object"));
|
|
el_val_t field = el_get_field(expr, EL_STR("field"));
|
|
el_val_t obj_c = cg_expr(obj);
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_get_field("), obj_c), EL_STR(", ")), c_str_lit(field)), EL_STR(")"));
|
|
}
|
|
if (str_eq(kind, EL_STR("Index"))) {
|
|
el_val_t obj = el_get_field(expr, EL_STR("object"));
|
|
el_val_t idx = el_get_field(expr, EL_STR("index"));
|
|
el_val_t obj_c = cg_expr(obj);
|
|
el_val_t idx_c = cg_expr(idx);
|
|
el_val_t idx_kind = el_get_field(idx, EL_STR("expr"));
|
|
if (str_eq(idx_kind, EL_STR("Str"))) {
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_get_field("), obj_c), EL_STR(", ")), idx_c), EL_STR(")"));
|
|
}
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_list_get("), obj_c), EL_STR(", ")), idx_c), EL_STR(")"));
|
|
}
|
|
if (str_eq(kind, EL_STR("Array"))) {
|
|
el_val_t elems = el_get_field(expr, EL_STR("elems"));
|
|
el_val_t n = native_list_len(elems);
|
|
el_val_t items = EL_STR("");
|
|
el_val_t i = 0;
|
|
while (i < n) {
|
|
el_val_t elem = native_list_get(elems, i);
|
|
el_val_t elem_c = cg_expr(elem);
|
|
if (i > 0) {
|
|
items = el_str_concat(items, EL_STR(", "));
|
|
}
|
|
items = el_str_concat(items, elem_c);
|
|
i = (i + 1);
|
|
}
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_list_new("), native_int_to_str(n)), EL_STR(", ")), items), EL_STR(")"));
|
|
}
|
|
if (str_eq(kind, EL_STR("Map"))) {
|
|
el_val_t pairs = el_get_field(expr, EL_STR("pairs"));
|
|
el_val_t n = native_list_len(pairs);
|
|
el_val_t items = EL_STR("");
|
|
el_val_t i = 0;
|
|
while (i < n) {
|
|
el_val_t pair = native_list_get(pairs, i);
|
|
el_val_t key = el_get_field(pair, EL_STR("key"));
|
|
el_val_t val = el_get_field(pair, EL_STR("value"));
|
|
el_val_t val_c = cg_expr(val);
|
|
if (i > 0) {
|
|
items = el_str_concat(items, EL_STR(", "));
|
|
}
|
|
items = el_str_concat(el_str_concat(el_str_concat(items, c_str_lit(key)), EL_STR(", ")), val_c);
|
|
i = (i + 1);
|
|
}
|
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_map_new("), native_int_to_str(n)), EL_STR(", ")), items), EL_STR(")"));
|
|
}
|
|
if (str_eq(kind, EL_STR("Try"))) {
|
|
el_val_t inner = el_get_field(expr, EL_STR("inner"));
|
|
return cg_expr(inner);
|
|
}
|
|
if (str_eq(kind, EL_STR("If"))) {
|
|
el_val_t cond = el_get_field(expr, EL_STR("cond"));
|
|
el_val_t cond_c = cg_expr(cond);
|
|
return el_str_concat(el_str_concat(EL_STR("/* if-expr */ (("), cond_c), EL_STR(") ? (el_val_t)1 : (el_val_t)0)"));
|
|
}
|
|
return EL_STR("EL_NULL");
|
|
return 0;
|
|
}
|
|
|
|
el_val_t list_contains(el_val_t lst, el_val_t s) {
|
|
el_val_t n = native_list_len(lst);
|
|
el_val_t i = 0;
|
|
while (i < n) {
|
|
el_val_t item = native_list_get(lst, i);
|
|
if (str_eq(item, s)) {
|
|
return 1;
|
|
}
|
|
i = (i + 1);
|
|
}
|
|
return 0;
|
|
return 0;
|
|
}
|
|
|
|
el_val_t cg_stmt(el_val_t stmt, el_val_t indent, el_val_t declared) {
|
|
el_val_t kind = el_get_field(stmt, EL_STR("stmt"));
|
|
if (str_eq(kind, EL_STR("Let"))) {
|
|
el_val_t name = el_get_field(stmt, EL_STR("name"));
|
|
el_val_t val = el_get_field(stmt, EL_STR("value"));
|
|
el_val_t val_c = cg_expr(val);
|
|
if (list_contains(declared, name)) {
|
|
emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(indent, name), EL_STR(" = ")), val_c), EL_STR(";")));
|
|
return declared;
|
|
} else {
|
|
emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("el_val_t ")), name), EL_STR(" = ")), val_c), EL_STR(";")));
|
|
return native_list_append(declared, name);
|
|
}
|
|
}
|
|
if (str_eq(kind, EL_STR("Return"))) {
|
|
el_val_t val = el_get_field(stmt, EL_STR("value"));
|
|
el_val_t val_kind = el_get_field(val, EL_STR("expr"));
|
|
if (str_eq(val_kind, EL_STR("Nil"))) {
|
|
emit_line(el_str_concat(indent, EL_STR("return 0;")));
|
|
} else {
|
|
el_val_t val_c = cg_expr(val);
|
|
emit_line(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("return ")), val_c), EL_STR(";")));
|
|
}
|
|
return declared;
|
|
}
|
|
if (str_eq(kind, EL_STR("Expr"))) {
|
|
el_val_t val = el_get_field(stmt, EL_STR("value"));
|
|
el_val_t val_kind = el_get_field(val, EL_STR("expr"));
|
|
if (str_eq(val_kind, EL_STR("If"))) {
|
|
cg_if_stmt(val, indent, declared);
|
|
return declared;
|
|
}
|
|
if (str_eq(val_kind, EL_STR("For"))) {
|
|
cg_for_stmt(val, indent, declared);
|
|
return declared;
|
|
}
|
|
el_val_t val_c = cg_expr(val);
|
|
emit_line(el_str_concat(el_str_concat(indent, val_c), EL_STR(";")));
|
|
return declared;
|
|
}
|
|
if (str_eq(kind, EL_STR("While"))) {
|
|
el_val_t cond = el_get_field(stmt, EL_STR("cond"));
|
|
el_val_t body = el_get_field(stmt, EL_STR("body"));
|
|
el_val_t cond_c = cg_expr(cond);
|
|
cond_c = strip_outer_parens(cond_c);
|
|
emit_line(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("while (")), cond_c), EL_STR(") {")));
|
|
cg_stmts(body, el_str_concat(indent, EL_STR(" ")), declared);
|
|
emit_line(el_str_concat(indent, EL_STR("}")));
|
|
return declared;
|
|
}
|
|
if (str_eq(kind, EL_STR("For"))) {
|
|
el_val_t item = el_get_field(stmt, EL_STR("item"));
|
|
el_val_t list_expr = el_get_field(stmt, EL_STR("list"));
|
|
el_val_t body = el_get_field(stmt, EL_STR("body"));
|
|
cg_for_body(item, list_expr, body, indent, declared);
|
|
return declared;
|
|
}
|
|
if (str_eq(kind, EL_STR("FnDef"))) {
|
|
return declared;
|
|
}
|
|
if (str_eq(kind, EL_STR("TypeDef"))) {
|
|
return declared;
|
|
}
|
|
if (str_eq(kind, EL_STR("EnumDef"))) {
|
|
return declared;
|
|
}
|
|
if (str_eq(kind, EL_STR("Import"))) {
|
|
return declared;
|
|
}
|
|
return declared;
|
|
return 0;
|
|
}
|
|
|
|
el_val_t strip_outer_parens(el_val_t s) {
|
|
el_val_t chars = native_string_chars(s);
|
|
el_val_t n = native_list_len(chars);
|
|
if (n < 2) {
|
|
return s;
|
|
}
|
|
el_val_t first = native_list_get(chars, 0);
|
|
el_val_t last = native_list_get(chars, (n - 1));
|
|
if (str_eq(first, EL_STR("("))) {
|
|
if (str_eq(last, EL_STR(")"))) {
|
|
el_val_t depth = 1;
|
|
el_val_t i = 1;
|
|
el_val_t balanced = 1;
|
|
while (i < (n - 1)) {
|
|
el_val_t ch = native_list_get(chars, i);
|
|
if (str_eq(ch, EL_STR("("))) {
|
|
depth = (depth + 1);
|
|
}
|
|
if (str_eq(ch, EL_STR(")"))) {
|
|
depth = (depth - 1);
|
|
if (depth == 0) {
|
|
balanced = 0;
|
|
i = n;
|
|
}
|
|
}
|
|
i = (i + 1);
|
|
}
|
|
if (balanced) {
|
|
el_val_t inner = EL_STR("");
|
|
el_val_t j = 1;
|
|
while (j < (n - 1)) {
|
|
el_val_t ch = native_list_get(chars, j);
|
|
inner = el_str_concat(inner, ch);
|
|
j = (j + 1);
|
|
}
|
|
return inner;
|
|
}
|
|
}
|
|
}
|
|
return s;
|
|
return 0;
|
|
}
|
|
|
|
el_val_t cg_if_stmt(el_val_t expr, el_val_t indent, el_val_t declared) {
|
|
el_val_t cond = el_get_field(expr, EL_STR("cond"));
|
|
el_val_t then_stmts = el_get_field(expr, EL_STR("then"));
|
|
el_val_t else_stmts = el_get_field(expr, EL_STR("else"));
|
|
el_val_t has_else = el_get_field(expr, EL_STR("has_else"));
|
|
el_val_t cond_c = cg_expr(cond);
|
|
cond_c = strip_outer_parens(cond_c);
|
|
emit_line(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("if (")), cond_c), EL_STR(") {")));
|
|
cg_stmts(then_stmts, el_str_concat(indent, EL_STR(" ")), declared);
|
|
if (has_else) {
|
|
emit_line(el_str_concat(indent, EL_STR("} else {")));
|
|
cg_stmts(else_stmts, el_str_concat(indent, EL_STR(" ")), declared);
|
|
}
|
|
emit_line(el_str_concat(indent, EL_STR("}")));
|
|
return 0;
|
|
}
|
|
|
|
el_val_t cg_for_body(el_val_t item, el_val_t list_expr, el_val_t body, el_val_t indent, el_val_t declared) {
|
|
el_val_t list_c = cg_expr(list_expr);
|
|
el_val_t idx = EL_STR("_el_i");
|
|
el_val_t list_tmp = EL_STR("_el_lst");
|
|
el_val_t len_tmp = EL_STR("_el_len");
|
|
emit_line(el_str_concat(indent, EL_STR("{")));
|
|
emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR(" el_val_t ")), list_tmp), EL_STR(" = ")), list_c), EL_STR(";")));
|
|
emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR(" el_val_t ")), len_tmp), EL_STR(" = el_list_len(")), list_tmp), EL_STR(");")));
|
|
emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR(" for (el_val_t ")), idx), EL_STR(" = 0; ")), idx), EL_STR(" < ")), len_tmp), EL_STR("; ")), idx), EL_STR("++) {")));
|
|
emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR(" el_val_t ")), item), EL_STR(" = el_list_get(")), list_tmp), EL_STR(", ")), idx), EL_STR(");")));
|
|
cg_stmts(body, el_str_concat(indent, EL_STR(" ")), declared);
|
|
emit_line(el_str_concat(indent, EL_STR(" }")));
|
|
emit_line(el_str_concat(indent, EL_STR("}")));
|
|
return 0;
|
|
}
|
|
|
|
el_val_t cg_for_stmt(el_val_t expr, el_val_t indent, el_val_t declared) {
|
|
el_val_t item = el_get_field(expr, EL_STR("item"));
|
|
el_val_t list_expr = el_get_field(expr, EL_STR("list"));
|
|
el_val_t body = el_get_field(expr, EL_STR("body"));
|
|
cg_for_body(item, list_expr, body, indent, declared);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t cg_stmts(el_val_t stmts, el_val_t indent, el_val_t declared) {
|
|
el_val_t n = native_list_len(stmts);
|
|
el_val_t i = 0;
|
|
el_val_t decl = declared;
|
|
while (i < n) {
|
|
el_val_t stmt = native_list_get(stmts, i);
|
|
decl = cg_stmt(stmt, indent, decl);
|
|
i = (i + 1);
|
|
}
|
|
return decl;
|
|
return 0;
|
|
}
|
|
|
|
el_val_t param_decl(el_val_t param, el_val_t idx) {
|
|
el_val_t name = el_get_field(param, EL_STR("name"));
|
|
return el_str_concat(EL_STR("el_val_t "), name);
|
|
return 0;
|
|
}
|
|
|
|
el_val_t params_to_c(el_val_t params) {
|
|
el_val_t n = native_list_len(params);
|
|
if (n == 0) {
|
|
return EL_STR("void");
|
|
}
|
|
el_val_t out = EL_STR("");
|
|
el_val_t i = 0;
|
|
while (i < n) {
|
|
el_val_t param = native_list_get(params, i);
|
|
el_val_t decl = param_decl(param, i);
|
|
if (i > 0) {
|
|
out = el_str_concat(out, EL_STR(", "));
|
|
}
|
|
out = el_str_concat(out, decl);
|
|
i = (i + 1);
|
|
}
|
|
return out;
|
|
return 0;
|
|
}
|
|
|
|
el_val_t transform_implicit_return(el_val_t body) {
|
|
el_val_t n = native_list_len(body);
|
|
if (n == 0) {
|
|
return body;
|
|
}
|
|
el_val_t last = native_list_get(body, (n - 1));
|
|
el_val_t last_kind = el_get_field(last, EL_STR("stmt"));
|
|
if (str_eq(last_kind, EL_STR("Expr"))) {
|
|
el_val_t val = el_get_field(last, EL_STR("value"));
|
|
el_val_t val_kind = el_get_field(val, EL_STR("expr"));
|
|
if (str_eq(val_kind, EL_STR("If"))) {
|
|
return body;
|
|
}
|
|
if (str_eq(val_kind, EL_STR("For"))) {
|
|
return body;
|
|
}
|
|
el_val_t new_body = native_list_empty();
|
|
el_val_t i = 0;
|
|
while (i < (n - 1)) {
|
|
new_body = native_list_append(new_body, native_list_get(body, i));
|
|
i = (i + 1);
|
|
}
|
|
el_val_t return_stmt = el_map_new(2, "stmt", EL_STR("Return"), "value", val);
|
|
new_body = native_list_append(new_body, return_stmt);
|
|
return new_body;
|
|
}
|
|
return body;
|
|
return 0;
|
|
}
|
|
|
|
el_val_t cg_fn(el_val_t stmt) {
|
|
el_val_t fn_name = el_get_field(stmt, EL_STR("name"));
|
|
if (str_eq(fn_name, EL_STR("main"))) {
|
|
return 0;
|
|
}
|
|
el_val_t params = el_get_field(stmt, EL_STR("params"));
|
|
el_val_t body = el_get_field(stmt, EL_STR("body"));
|
|
el_val_t ret_type = el_get_field(stmt, EL_STR("ret_type"));
|
|
el_val_t params_c = params_to_c(params);
|
|
emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_val_t "), fn_name), EL_STR("(")), params_c), EL_STR(") {")));
|
|
el_val_t decl = native_list_empty();
|
|
el_val_t np = native_list_len(params);
|
|
el_val_t pi = 0;
|
|
while (pi < np) {
|
|
el_val_t param = native_list_get(params, pi);
|
|
el_val_t pname = el_get_field(param, EL_STR("name"));
|
|
decl = native_list_append(decl, pname);
|
|
pi = (pi + 1);
|
|
}
|
|
el_val_t body_xformed = body;
|
|
if (!str_eq(ret_type, EL_STR("Void"))) {
|
|
body_xformed = transform_implicit_return(body);
|
|
}
|
|
cg_stmts(body_xformed, EL_STR(" "), decl);
|
|
emit_line(EL_STR(" return 0;"));
|
|
emit_line(EL_STR("}"));
|
|
emit_blank();
|
|
return 0;
|
|
}
|
|
|
|
el_val_t is_fndef(el_val_t stmt) {
|
|
el_val_t kind = el_get_field(stmt, EL_STR("stmt"));
|
|
if (str_eq(kind, EL_STR("FnDef"))) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
return 0;
|
|
}
|
|
|
|
el_val_t is_top_level_decl(el_val_t stmt) {
|
|
el_val_t kind = el_get_field(stmt, EL_STR("stmt"));
|
|
if (str_eq(kind, EL_STR("TypeDef"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(kind, EL_STR("EnumDef"))) {
|
|
return 1;
|
|
}
|
|
if (str_eq(kind, EL_STR("Import"))) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
return 0;
|
|
}
|
|
|
|
el_val_t codegen(el_val_t stmts, el_val_t source) {
|
|
emit_line(EL_STR("#include <stdint.h>"));
|
|
emit_line(EL_STR("#include <stdlib.h>"));
|
|
emit_line(EL_STR("#include \"el_runtime.h\""));
|
|
emit_blank();
|
|
el_val_t n = native_list_len(stmts);
|
|
el_val_t i = 0;
|
|
while (i < n) {
|
|
el_val_t stmt = native_list_get(stmts, i);
|
|
el_val_t kind = el_get_field(stmt, EL_STR("stmt"));
|
|
if (str_eq(kind, EL_STR("FnDef"))) {
|
|
el_val_t fn_name = el_get_field(stmt, EL_STR("name"));
|
|
if (!str_eq(fn_name, EL_STR("main"))) {
|
|
el_val_t params = el_get_field(stmt, EL_STR("params"));
|
|
el_val_t params_c = params_to_c(params);
|
|
emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_val_t "), fn_name), EL_STR("(")), params_c), EL_STR(");")));
|
|
}
|
|
}
|
|
i = (i + 1);
|
|
}
|
|
emit_blank();
|
|
i = 0;
|
|
while (i < n) {
|
|
el_val_t stmt = native_list_get(stmts, i);
|
|
if (is_fndef(stmt)) {
|
|
cg_fn(stmt);
|
|
}
|
|
i = (i + 1);
|
|
}
|
|
emit_line(EL_STR("int main(int argc, char** argv) {"));
|
|
emit_line(EL_STR(" el_runtime_init_args(argc, argv);"));
|
|
el_val_t main_decl = native_list_empty();
|
|
i = 0;
|
|
while (i < n) {
|
|
el_val_t stmt = native_list_get(stmts, i);
|
|
if (is_fndef(stmt)) {
|
|
} else {
|
|
if (is_top_level_decl(stmt)) {
|
|
} else {
|
|
main_decl = cg_stmt(stmt, EL_STR(" "), main_decl);
|
|
}
|
|
}
|
|
i = (i + 1);
|
|
}
|
|
emit_line(EL_STR(" return 0;"));
|
|
emit_line(EL_STR("}"));
|
|
emit_blank();
|
|
return EL_STR("");
|
|
return 0;
|
|
}
|
|
|
|
el_val_t compile(el_val_t source) {
|
|
el_val_t tokens = lex(source);
|
|
el_val_t stmts = parse(tokens);
|
|
return codegen(stmts, source);
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
el_runtime_init_args(argc, argv);
|
|
el_val_t _argv = args();
|
|
el_val_t _src_path = native_list_get(_argv, 0);
|
|
el_val_t _source = fs_read(_src_path);
|
|
compile(_source);
|
|
return 0;
|
|
}
|
|
|