boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs

commit c40935b691eabb80b07e5bb9d65d5d86c402dbe1
parent a0b1dde7214f0ea5574565c1c0fbcdd16fedef90
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 23 Apr 2026 08:58:37 -0700

Regularize M1M macro oracle

Diffstat:
Mdocs/M1M-IMPL.md | 61++++++++++++++++++++++++++++++-------------------------------
Mdocs/M1M-P1-PORT.md | 14++++++--------
Mpost.md | 5+++--
Msrc/m1m.M1 | 52+++++++++++++++++++++-------------------------------
Msrc/m1macro.c | 1149++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
5 files changed, 704 insertions(+), 577 deletions(-)

diff --git a/docs/M1M-IMPL.md b/docs/M1M-IMPL.md @@ -65,48 +65,44 @@ The program should be structured as a small compiler pipeline: Use fixed BSS arenas and simple power-of-two-ish records. -Token record, 32 bytes: +Text spans and token records are kept separate: ```text +TextSpan: ++0 start u64 ++8 len u64 + +Token: +0 kind u64 -+8 text_ptr u64 -+16 len u64 -+24 line u64 ++8 text TextSpan ``` -Macro record, 320 bytes: +Macro record: ```text -+0 name_ptr u64 -+8 name_len u64 -+16 def_line u64 -+24 param_count u64 -+32 body_ptr u64 pointer into macro_body_tokens -+40 body_count u64 -+48 param_ptr[16] 16 * u64 -+176 param_len[16] 16 * u64 -+304 reserved / padding +name TextSpan +param_count u64 +params TextSpan[16] +body_start Token* +body_end Token* ``` -Stream record, 40 bytes: +Stream record: ```text -+0 toks_start u64 -+8 toks_end u64 exclusive pointer, not count -+16 pos u64 current token pointer -+24 line_start u64 -+32 pool_mark u64 -1 for source-owned streams +toks_start Token* +toks_end Token*, exclusive +pos Token* +line_start bool +pool_mark stack mark, -1 for source-owned streams ``` -Expression frame, around 48 bytes: +Expression frame: ```text -+0 op_code u64 -+8 op_token u64 -+16 argc u64 -+24 accum u64 -+32 line u64 -+40 reserved +op_code enum +argc u64 +args i64[16] ``` Global arenas: @@ -126,8 +122,11 @@ expr_frames ``` Token range boundaries should be stored as token pointers rather than -indices. That keeps stream and argument walking simple in P1: increment -by 32 bytes, compare pointers, no repeated `base + index << 5`. +indices. That keeps stream and argument walking simple in P1: advance by +one token record, compare pointers, no repeated `base + index << 5`. + +Source token spans point into `input_buf`. `text_buf` is reserved for +synthesized token text such as `##` pastes and `%le32`/`%le64` output. ### Bottom-Up Helper Layers @@ -142,12 +141,12 @@ copy_bytes(dst, src, len) #### Layer 1: token helpers ```text -push_source_token(kind, text_ptr, len, line) +push_source_token(kind, text) push_macro_body_token(token_ptr) push_pool_token(token_ptr) copy_token(dst_ptr, src_ptr) tok_eq_const(tok, const_ptr, len) -> bool -span_eq_token(ptr, len, tok) -> bool +span_eq_token(span, tok) -> bool ``` #### Layer 2: stream helpers diff --git a/docs/M1M-P1-PORT.md b/docs/M1M-P1-PORT.md @@ -60,15 +60,13 @@ Use fixed BSS arenas, mirroring the C implementation: Token record layout should be compact and uniform: ``` -kind 8 bytes -text_ptr 8 bytes -len 8 bytes -line 8 bytes +kind token kind +text source span in `input_buf` or synthesized span in `text_buf` ``` -Macro records should store offsets/pointers into the text arena and token -arena, not inline strings. Prefer power-of-two record sizes so address math -stays simple in P1. +Macro records should store name/parameter text spans plus a body token +range, not inline strings. Prefer record shapes that stay uniform across the +codebase so the address math remains easy to audit in P1. ## Implementation Milestones @@ -86,7 +84,7 @@ applicable. source token records, compare token text with constants, emit tokens, and emit newlines. - [x] Port the C tokenizer model for source input: whitespace skipping, - string tokens, `##`, comments, delimiters, word tokens, line tracking, and + string tokens, `##`, comments, delimiters, word tokens, newline tokens, and text-buffer copies. - [x] Add a first processor skeleton that normalizes pass-through output and structurally skips line-start `%macro` ... `%endm` definitions. diff --git a/post.md b/post.md @@ -127,7 +127,8 @@ M0 to TCC (but no promises). Mostly it's just a cute base to work on. (define m1m (link (M0 (catm P1.M1 m1m.M1)))) ;; Normal P1 build: expand macros, then assemble/link. -(defn p1compile (src) (M0 (m1m (catm P1M.M1 src)))) +;; P1A.M1=arch-specific backend, P1M.M1=P1 macros +(defn p1compile (src) (M0 (m1m (catm P1A.M1 P1M.M1 src)))) (defn p1exe (src) (link (p1compile src))) ``` @@ -136,7 +137,7 @@ We use that to build a Scheme with file IO and process support. ``` ;; Build the language that will host the rest of the climb. -(define scheme (p1exe scheme.M1)) +(define scheme (p1exe scheme.P1)) ``` And then Scheme is our shell and our C compiler. diff --git a/src/m1m.M1 b/src/m1m.M1 @@ -832,8 +832,8 @@ DEFINE TOK_PASTE 06000000 :ELF_end ## BSS. The ELF loader maps this region zero-filled after truncation at ELF_end. -:m1m_input_buf +:m1m_input_buf ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 @@ -962,6 +962,7 @@ ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 + :m1m_output_buf ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 @@ -1219,6 +1220,7 @@ ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 + :m1m_text_buf ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 @@ -1476,6 +1478,7 @@ ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 + :source_tokens ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 @@ -2501,34 +2504,21 @@ ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 -:m1m_input_len -ZERO32 -:text_used -ZERO32 -:source_count -ZERO32 -:output_used -ZERO32 -:output_need_space -ZERO32 -:lex_ptr -ZERO32 -:lex_line -ZERO32 -:lex_start -ZERO32 -:lex_quote -ZERO32 -:proc_pos -ZERO32 -:proc_line_start -ZERO32 -:proc_tok_addr -ZERO32 -:skip_line_start -ZERO32 -:emit_tok_tmp -ZERO32 -:output_path -ZERO32 + +:m1m_input_len ZERO32 +:text_used ZERO32 +:source_count ZERO32 +:output_used ZERO32 +:output_need_space ZERO32 +:lex_ptr ZERO32 +:lex_line ZERO32 +:lex_start ZERO32 +:lex_quote ZERO32 +:proc_pos ZERO32 +:proc_line_start ZERO32 +:proc_tok_addr ZERO32 +:skip_line_start ZERO32 +:emit_tok_tmp ZERO32 +:output_path ZERO32 + :ELF_bss_end diff --git a/src/m1macro.c b/src/m1macro.c @@ -1,8 +1,3 @@ -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <errno.h> - /* * Tiny single-pass M1 macro expander. * @@ -23,24 +18,59 @@ * (& a b), (| a b), (^ a b), (~ a), (= a b), (!= a b), * (< a b), (<= a b), (> a b), (>= a b) * + * Flow: + * 1. lex_source(): scan input_buf into source_tokens[]. Tokens are words, + * strings, newlines, parens, commas, and ## paste markers. Whitespace + * (excluding newlines) is dropped; # and ; comments are dropped. + * + * 2. process_tokens(): main loop driven by a stream stack (streams[]). + * The source token array is pushed as the initial stream. Each iteration + * pops a token from the top stream: + * + * %macro NAME(p,...) / %endm at line-start + * -> define_macro(): consume header + body tokens into macros[] and + * macro_body_tokens[]; register name and param list. + * + * %le32(e) / %le64(e) / %select(c,t,e) + * -> expand_builtin_call(): parse arg spans, eval S-expression(s) via + * eval_expr_range(), emit LE hex or push the chosen token span. + * + * %NAME(...) matching a defined macro + * -> expand_call() -> expand_macro_tokens(): substitute arguments, + * apply ## paste via paste_pool_range(), write result into + * expand_pool[], then push that slice as a new stream (rescan). + * + * Anything else + * -> emit_token() / emit_newline() directly into output_buf. + * + * When a stream is exhausted it is popped; pool_used is rewound to the + * stream's pool_mark, reclaiming the expand_pool space it used. + * + * 3. Write output_buf to the output file. + * * Notes: * - Macros are define-before-use. There is no prescan. * - Expansion rescans by pushing expanded tokens back through the same loop. * - There is no cycle detection. Recursive macros will loop until a limit. * - Only recognized %NAME(...) calls expand. Other text passes through. * - Output formatting is normalized to tokens plus '\n', not preserved. - * - Locals (@name) and prefixed params (:param/&param) are not supported. */ -#define MAX_INPUT 262144 -#define MAX_OUTPUT 524288 -#define MAX_TEXT 524288 -#define MAX_TOKENS 65536 -#define MAX_MACROS 256 -#define MAX_PARAMS 16 -#define MAX_BODY_TOKENS 4096 -#define MAX_EXPAND 65536 -#define MAX_STACK 64 +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define MAX_INPUT 262144 +#define MAX_OUTPUT 524288 +#define MAX_TEXT 524288 +#define MAX_TOKENS 65536 +#define MAX_MACROS 256 +#define MAX_PARAMS 16 +#define MAX_MACRO_BODY_TOKENS MAX_TOKENS +#define MAX_EXPAND 65536 +#define MAX_STACK 64 +#define MAX_EXPR_FRAMES 256 enum { TOK_WORD, @@ -52,111 +82,157 @@ enum { TOK_PASTE }; +enum ExprOp { + EXPR_ADD, + EXPR_SUB, + EXPR_MUL, + EXPR_DIV, + EXPR_MOD, + EXPR_SHL, + EXPR_SHR, + EXPR_AND, + EXPR_OR, + EXPR_XOR, + EXPR_NOT, + EXPR_EQ, + EXPR_NE, + EXPR_LT, + EXPR_LE, + EXPR_GT, + EXPR_GE, + EXPR_INVALID +}; + +struct TextSpan { + const char *ptr; + int len; +}; + struct Token { int kind; - int start; - int len; - int line; + struct TextSpan text; +}; + +struct TokenSpan { + struct Token *start; + struct Token *end; }; struct Macro { - int name_start; - int name_len; - int line; + struct TextSpan name; int param_count; - int param_start[MAX_PARAMS]; - int param_len[MAX_PARAMS]; - struct Token body[MAX_BODY_TOKENS]; - int body_count; + struct TextSpan params[MAX_PARAMS]; + struct Token *body_start; + struct Token *body_end; }; struct Stream { - struct Token *toks; - int count; - int pos; + struct Token *start; + struct Token *end; + struct Token *pos; int line_start; int pool_mark; }; +struct ExprFrame { + enum ExprOp op; + long long args[MAX_PARAMS]; + int argc; +}; + static char input_buf[MAX_INPUT + 1]; static char output_buf[MAX_OUTPUT + 1]; static char text_buf[MAX_TEXT]; static struct Token source_tokens[MAX_TOKENS]; +static struct Token macro_body_tokens[MAX_MACRO_BODY_TOKENS]; static struct Token expand_pool[MAX_EXPAND]; static struct Macro macros[MAX_MACROS]; static struct Stream streams[MAX_STACK]; static int text_used; static int source_count; -static int pool_used; static int macro_count; +static int macro_body_used; +static int pool_used; static int output_used; static int output_need_space; static int stream_top; -static char error_buf[256]; +static struct Token *arg_starts[MAX_PARAMS]; +static struct Token *arg_ends[MAX_PARAMS]; +static int arg_count; +static struct Token *call_end_pos; + +static const char *error_msg; + +static int fail(const char *msg) +{ + error_msg = msg; + return 0; +} static int is_space_no_nl(int c) { return c == ' ' || c == '\t' || c == '\r' || c == '\f' || c == '\v'; } -static int append_text_len(const char *s, int len) +static char *append_text_len(const char *s, int len) { int start; if (text_used + len + 1 > MAX_TEXT) { - snprintf(error_buf, sizeof(error_buf), "text buffer overflow"); - return -1; + fail("text overflow"); + return NULL; } start = text_used; memcpy(text_buf + text_used, s, (size_t)len); text_used += len; text_buf[text_used++] = '\0'; - return start; -} - -static int append_text_str(const char *s) -{ - return append_text_len(s, (int)strlen(s)); + return text_buf + start; } static int push_token(struct Token *buf, int *count, int max_count, - int kind, int start, int len, int line) + int kind, struct TextSpan text) { if (*count >= max_count) { - snprintf(error_buf, sizeof(error_buf), "token buffer overflow"); - return 0; + return fail("token overflow"); } buf[*count].kind = kind; - buf[*count].start = start; - buf[*count].len = len; - buf[*count].line = line; + buf[*count].text = text; *count += 1; return 1; } +static int push_pool_token(struct Token tok) +{ + if (pool_used >= MAX_EXPAND) { + return fail("expansion overflow"); + } + expand_pool[pool_used++] = tok; + return 1; +} + static int token_text_eq(const struct Token *tok, const char *s) { int len = (int)strlen(s); - return tok->len == len && memcmp(text_buf + tok->start, s, (size_t)len) == 0; + return tok->text.len == len && + memcmp(tok->text.ptr, s, (size_t)len) == 0; } -static int span_eq(int start, int len, const struct Token *tok) +static int span_eq_token(struct TextSpan span, const struct Token *tok) { - return len == tok->len && memcmp(text_buf + start, text_buf + tok->start, (size_t)len) == 0; + return span.len == tok->text.len && + memcmp(span.ptr, tok->text.ptr, (size_t)span.len) == 0; } static int lex_source(const char *src) { int i = 0; - int line = 1; while (src[i] != '\0') { int start; - int text_start; int len; if (is_space_no_nl((unsigned char)src[i])) { @@ -165,11 +241,10 @@ static int lex_source(const char *src) } if (src[i] == '\n') { if (!push_token(source_tokens, &source_count, MAX_TOKENS, - TOK_NEWLINE, -1, 0, line)) { + TOK_NEWLINE, (struct TextSpan){src + i, 1})) { return 0; } i++; - line++; continue; } if (src[i] == '"' || src[i] == '\'') { @@ -184,23 +259,15 @@ static int lex_source(const char *src) i++; } len = i - start; - text_start = append_text_len(src + start, len); - if (text_start < 0) { - return 0; - } if (!push_token(source_tokens, &source_count, MAX_TOKENS, - TOK_STRING, text_start, len, line)) { + TOK_STRING, (struct TextSpan){src + start, len})) { return 0; } continue; } if (src[i] == '#' && src[i + 1] == '#') { - text_start = append_text_str("##"); - if (text_start < 0) { - return 0; - } if (!push_token(source_tokens, &source_count, MAX_TOKENS, - TOK_PASTE, text_start, 2, line)) { + TOK_PASTE, (struct TextSpan){src + i, 2})) { return 0; } i += 2; @@ -213,36 +280,24 @@ static int lex_source(const char *src) continue; } if (src[i] == '(') { - text_start = append_text_str("("); - if (text_start < 0) { - return 0; - } if (!push_token(source_tokens, &source_count, MAX_TOKENS, - TOK_LPAREN, text_start, 1, line)) { + TOK_LPAREN, (struct TextSpan){src + i, 1})) { return 0; } i++; continue; } if (src[i] == ')') { - text_start = append_text_str(")"); - if (text_start < 0) { - return 0; - } if (!push_token(source_tokens, &source_count, MAX_TOKENS, - TOK_RPAREN, text_start, 1, line)) { + TOK_RPAREN, (struct TextSpan){src + i, 1})) { return 0; } i++; continue; } if (src[i] == ',') { - text_start = append_text_str(","); - if (text_start < 0) { - return 0; - } if (!push_token(source_tokens, &source_count, MAX_TOKENS, - TOK_COMMA, text_start, 1, line)) { + TOK_COMMA, (struct TextSpan){src + i, 1})) { return 0; } i++; @@ -262,12 +317,8 @@ static int lex_source(const char *src) i++; } len = i - start; - text_start = append_text_len(src + start, len); - if (text_start < 0) { - return 0; - } if (!push_token(source_tokens, &source_count, MAX_TOKENS, - TOK_WORD, text_start, len, line)) { + TOK_WORD, (struct TextSpan){src + start, len})) { return 0; } } @@ -275,29 +326,25 @@ static int lex_source(const char *src) return 1; } -static int find_macro(const struct Token *tok) +static const struct Macro *find_macro(const struct Token *tok) { int i; - const char *s; - if (tok->kind != TOK_WORD) { - return -1; + if (tok->kind != TOK_WORD || tok->text.len < 2) { + return NULL; } - if (tok->len < 2) { - return -1; - } - s = text_buf + tok->start; - if (s[0] != '%') { - return -1; + if (tok->text.ptr[0] != '%') { + return NULL; } for (i = 0; i < macro_count; i++) { - if (tok->len - 1 == macros[i].name_len && - memcmp(s + 1, text_buf + macros[i].name_start, - (size_t)macros[i].name_len) == 0) { - return i; + if (macros[i].name.len == tok->text.len - 1 && + memcmp(tok->text.ptr + 1, + macros[i].name.ptr, + (size_t)macros[i].name.len) == 0) { + return &macros[i]; } } - return -1; + return NULL; } static int find_param(const struct Macro *m, const struct Token *tok) @@ -305,31 +352,20 @@ static int find_param(const struct Macro *m, const struct Token *tok) int i; if (tok->kind != TOK_WORD) { - return -1; + return 0; } for (i = 0; i < m->param_count; i++) { - if (span_eq(m->param_start[i], m->param_len[i], tok)) { - return i; + if (span_eq_token(m->params[i], tok)) { + return i + 1; } } - return -1; -} - -static int push_pool_token(struct Token tok) -{ - if (pool_used >= MAX_EXPAND) { - snprintf(error_buf, sizeof(error_buf), "expansion pool overflow"); - return 0; - } - expand_pool[pool_used++] = tok; - return 1; + return 0; } static int emit_newline(void) { if (output_used + 1 >= MAX_OUTPUT) { - snprintf(error_buf, sizeof(error_buf), "output buffer overflow"); - return 0; + return fail("output overflow"); } output_buf[output_used++] = '\n'; output_need_space = 0; @@ -340,38 +376,44 @@ static int emit_token(const struct Token *tok) { if (output_need_space) { if (output_used + 1 >= MAX_OUTPUT) { - snprintf(error_buf, sizeof(error_buf), "output buffer overflow"); - return 0; + return fail("output overflow"); } output_buf[output_used++] = ' '; } - if (output_used + tok->len >= MAX_OUTPUT) { - snprintf(error_buf, sizeof(error_buf), "output buffer overflow"); - return 0; + if (output_used + tok->text.len >= MAX_OUTPUT) { + return fail("output overflow"); } - memcpy(output_buf + output_used, text_buf + tok->start, (size_t)tok->len); - output_used += tok->len; + memcpy(output_buf + output_used, tok->text.ptr, + (size_t)tok->text.len); + output_used += tok->text.len; output_need_space = 1; return 1; } -static int push_stream(struct Token *toks, int count, int pool_mark) +static int push_stream_span(struct TokenSpan span, int pool_mark) { struct Stream *s; if (stream_top >= MAX_STACK) { - snprintf(error_buf, sizeof(error_buf), "stream stack overflow"); - return 0; + return fail("stream overflow"); } s = &streams[stream_top++]; - s->toks = toks; - s->count = count; - s->pos = 0; + s->start = span.start; + s->end = span.end; + s->pos = span.start; s->line_start = 1; s->pool_mark = pool_mark; return 1; } +static struct Stream *current_stream(void) +{ + if (stream_top <= 0) { + return NULL; + } + return &streams[stream_top - 1]; +} + static void pop_stream(void) { if (stream_top <= 0) { @@ -383,49 +425,74 @@ static void pop_stream(void) } } +static int copy_span_to_pool(struct TokenSpan span) +{ + struct Token *tok; + + for (tok = span.start; tok < span.end; tok++) { + if (!push_pool_token(*tok)) { + return 0; + } + } + return 1; +} + +static int push_pool_stream_from_mark(int mark) +{ + if (pool_used == mark) { + pool_used = mark; + return 1; + } + return push_stream_span((struct TokenSpan){expand_pool + mark, expand_pool + pool_used}, + mark); +} + +static void skip_expr_newlines(struct Token **pos, struct Token *end) +{ + while (*pos < end && (*pos)->kind == TOK_NEWLINE) { + *pos += 1; + } +} + static int define_macro(struct Stream *s) { struct Macro *m; int line_start; if (macro_count >= MAX_MACROS) { - snprintf(error_buf, sizeof(error_buf), "too many macros"); - return 0; + return fail("too many macros"); + } + if (macro_body_used >= MAX_MACRO_BODY_TOKENS) { + return fail("macro body overflow"); } + m = &macros[macro_count]; memset(m, 0, sizeof(*m)); - m->line = s->toks[s->pos].line; s->pos++; - if (s->pos >= s->count || s->toks[s->pos].kind != TOK_WORD) { - snprintf(error_buf, sizeof(error_buf), "expected macro name at line %d", m->line); - return 0; + if (s->pos >= s->end || s->pos->kind != TOK_WORD) { + return fail("bad macro header"); } - m->name_start = s->toks[s->pos].start; - m->name_len = s->toks[s->pos].len; + m->name = s->pos->text; s->pos++; - if (s->pos >= s->count || s->toks[s->pos].kind != TOK_LPAREN) { - snprintf(error_buf, sizeof(error_buf), "expected '(' after macro name at line %d", m->line); - return 0; + if (s->pos >= s->end || s->pos->kind != TOK_LPAREN) { + return fail("bad macro header"); } s->pos++; - if (s->pos < s->count && s->toks[s->pos].kind != TOK_RPAREN) { + if (s->pos < s->end && s->pos->kind != TOK_RPAREN) { while (1) { if (m->param_count >= MAX_PARAMS) { - snprintf(error_buf, sizeof(error_buf), "too many params on macro at line %d", m->line); - return 0; + return fail("bad macro header"); } - if (s->pos >= s->count || s->toks[s->pos].kind != TOK_WORD) { - snprintf(error_buf, sizeof(error_buf), "expected parameter name at line %d", m->line); - return 0; + if (s->pos >= s->end || s->pos->kind != TOK_WORD) { + return fail("bad macro header"); } - m->param_start[m->param_count] = s->toks[s->pos].start; - m->param_len[m->param_count] = s->toks[s->pos].len; + m->params[m->param_count] = s->pos->text; m->param_count++; s->pos++; - if (s->pos < s->count && s->toks[s->pos].kind == TOK_COMMA) { + if (s->pos < s->end && s->pos->kind == TOK_COMMA) { s->pos++; continue; } @@ -433,236 +500,221 @@ static int define_macro(struct Stream *s) } } - if (s->pos >= s->count || s->toks[s->pos].kind != TOK_RPAREN) { - snprintf(error_buf, sizeof(error_buf), "expected ')' in macro header at line %d", m->line); - return 0; + if (s->pos >= s->end || s->pos->kind != TOK_RPAREN) { + return fail("bad macro header"); } s->pos++; - if (s->pos >= s->count || s->toks[s->pos].kind != TOK_NEWLINE) { - snprintf(error_buf, sizeof(error_buf), "expected newline after macro header at line %d", m->line); - return 0; + if (s->pos >= s->end || s->pos->kind != TOK_NEWLINE) { + return fail("bad macro header"); } s->pos++; + m->body_start = macro_body_tokens + macro_body_used; line_start = 1; - while (s->pos < s->count) { + while (s->pos < s->end) { if (line_start && - s->toks[s->pos].kind == TOK_WORD && - token_text_eq(&s->toks[s->pos], "%endm")) { - while (s->pos < s->count && s->toks[s->pos].kind != TOK_NEWLINE) { + s->pos->kind == TOK_WORD && + token_text_eq(s->pos, "%endm")) { + while (s->pos < s->end && s->pos->kind != TOK_NEWLINE) { s->pos++; } - if (s->pos < s->count && s->toks[s->pos].kind == TOK_NEWLINE) { + if (s->pos < s->end && s->pos->kind == TOK_NEWLINE) { s->pos++; } + m->body_end = macro_body_tokens + macro_body_used; s->line_start = 1; macro_count++; return 1; } - if (m->body_count >= MAX_BODY_TOKENS) { - snprintf(error_buf, sizeof(error_buf), "macro body too large at line %d", m->line); - return 0; + if (macro_body_used >= MAX_MACRO_BODY_TOKENS) { + return fail("macro body overflow"); } - m->body[m->body_count++] = s->toks[s->pos]; - line_start = (s->toks[s->pos].kind == TOK_NEWLINE); + macro_body_tokens[macro_body_used++] = *s->pos; + line_start = (s->pos->kind == TOK_NEWLINE); s->pos++; } - snprintf(error_buf, sizeof(error_buf), "unterminated %%macro starting at line %d", m->line); - return 0; + return fail("unterminated macro"); } -static int parse_args(struct Stream *s, int lparen_pos, - int arg_starts[MAX_PARAMS], int arg_ends[MAX_PARAMS], - int *arg_count, int *end_pos) +static int parse_args(struct Token *lparen, struct Token *limit) { - int i = lparen_pos + 1; + struct Token *tok = lparen + 1; + struct Token *arg_start = tok; int depth = 1; - int cur = 0; + int arg_index = 0; - memset(arg_starts, 0, sizeof(int) * MAX_PARAMS); - memset(arg_ends, 0, sizeof(int) * MAX_PARAMS); - arg_starts[0] = i; - - while (i < s->count) { - if (s->toks[i].kind == TOK_LPAREN) { + while (tok < limit) { + if (tok->kind == TOK_LPAREN) { depth++; - i++; + tok++; continue; } - if (s->toks[i].kind == TOK_RPAREN) { + if (tok->kind == TOK_RPAREN) { depth--; if (depth == 0) { - arg_ends[cur] = i; - *arg_count = cur + 1; - *end_pos = i + 1; - if (arg_starts[0] == arg_ends[0] && cur == 0) { - *arg_count = 0; + if (arg_start == tok && arg_index == 0) { + arg_count = 0; + } else { + if (arg_index >= MAX_PARAMS) { + return fail("too many args"); + } + arg_starts[arg_index] = arg_start; + arg_ends[arg_index] = tok; + arg_count = arg_index + 1; } + call_end_pos = tok + 1; return 1; } - i++; + tok++; continue; } - if (s->toks[i].kind == TOK_COMMA && depth == 1) { - arg_ends[cur] = i; - cur++; - if (cur >= MAX_PARAMS) { - snprintf(error_buf, sizeof(error_buf), "too many macro args at line %d", s->toks[i].line); - return 0; + if (tok->kind == TOK_COMMA && depth == 1) { + if (arg_index >= MAX_PARAMS) { + return fail("too many args"); } - arg_starts[cur] = i + 1; - i++; + arg_starts[arg_index] = arg_start; + arg_ends[arg_index] = tok; + arg_index++; + arg_start = tok + 1; + tok++; continue; } - i++; + tok++; } - snprintf(error_buf, sizeof(error_buf), "unterminated macro call at line %d", s->toks[lparen_pos].line); - return 0; + return fail("unterminated macro call"); } -static int append_arg(struct Stream *s, int start, int end, int single) +static int copy_arg_tokens_to_pool(struct TokenSpan span) { - int i; + if (span.start == span.end) { + return fail("bad macro argument"); + } + return copy_span_to_pool(span); +} - if (start == end) { - snprintf(error_buf, sizeof(error_buf), "empty macro argument"); - return 0; +static int copy_paste_arg_to_pool(struct TokenSpan span) +{ + if (span.end - span.start != 1) { + return fail("bad macro argument"); } - if (single && end - start != 1) { - snprintf(error_buf, sizeof(error_buf), "pasted arg must be one token"); + return copy_span_to_pool(span); +} + +static int append_pasted_token(struct Token *dst, + const struct Token *left, + const struct Token *right) +{ + char tmp[512]; + char *text_ptr; + int n; + + n = snprintf(tmp, sizeof(tmp), "%.*s%.*s", + left->text.len, left->text.ptr, + right->text.len, right->text.ptr); + if (n < 0 || n >= (int)sizeof(tmp)) { + return fail("bad paste"); + } + text_ptr = append_text_len(tmp, n); + if (text_ptr == NULL) { return 0; } - for (i = start; i < end; i++) { - if (!push_pool_token(s->toks[i])) { - return 0; - } - } + dst->kind = TOK_WORD; + dst->text.ptr = text_ptr; + dst->text.len = n; return 1; } -static int paste_range(int start, int *count) +static int paste_pool_range(int mark) { - int in = start; - int out = start; - int end = start + *count; + struct Token *start = expand_pool + mark; + struct Token *in = start; + struct Token *out = start; + struct Token *end = expand_pool + pool_used; while (in < end) { - if (expand_pool[in].kind == TOK_PASTE) { - char tmp[512]; - int n; - int text_start; - + if (in->kind == TOK_PASTE) { if (out == start || in + 1 >= end) { - snprintf(error_buf, sizeof(error_buf), "misplaced ## at line %d", expand_pool[in].line); - return 0; - } - if (expand_pool[out - 1].kind == TOK_NEWLINE || - expand_pool[in + 1].kind == TOK_NEWLINE || - expand_pool[out - 1].kind == TOK_PASTE || - expand_pool[in + 1].kind == TOK_PASTE) { - snprintf(error_buf, sizeof(error_buf), "bad ## operand at line %d", expand_pool[in].line); - return 0; + pool_used = mark; + return fail("bad paste"); } - - n = snprintf(tmp, sizeof(tmp), "%.*s%.*s", - expand_pool[out - 1].len, text_buf + expand_pool[out - 1].start, - expand_pool[in + 1].len, text_buf + expand_pool[in + 1].start); - if (n < 0 || n >= (int)sizeof(tmp)) { - snprintf(error_buf, sizeof(error_buf), "pasted token too long at line %d", expand_pool[in].line); - return 0; + if ((out - 1)->kind == TOK_NEWLINE || + (out - 1)->kind == TOK_PASTE || + (in + 1)->kind == TOK_NEWLINE || + (in + 1)->kind == TOK_PASTE) { + pool_used = mark; + return fail("bad paste"); } - text_start = append_text_len(tmp, n); - if (text_start < 0) { + if (!append_pasted_token(out - 1, out - 1, in + 1)) { + pool_used = mark; return 0; } - expand_pool[out - 1].kind = TOK_WORD; - expand_pool[out - 1].start = text_start; - expand_pool[out - 1].len = n; in += 2; continue; } if (out != in) { - expand_pool[out] = expand_pool[in]; + *out = *in; } out++; in++; } - *count = out - start; + pool_used = (int)(out - expand_pool); return 1; } -static int expand_macro_at(struct Stream *s, int call_pos, int macro_idx, - int *end_pos_out, int *mark_out, int *count_out) +static int expand_macro_tokens(struct Token *call_tok, struct Token *limit, + const struct Macro *m, struct Token **after_out, + int *mark_out) { - struct Macro *m = &macros[macro_idx]; - int arg_starts[MAX_PARAMS]; - int arg_ends[MAX_PARAMS]; - int arg_count; - int end_pos; + struct Token *body_tok; + struct Token *end_pos; int mark; - int count; - int i; - if (call_pos + 1 >= s->count || s->toks[call_pos + 1].kind != TOK_LPAREN) { - snprintf(error_buf, sizeof(error_buf), "internal macro call error"); - return 0; + if (call_tok + 1 >= limit || (call_tok + 1)->kind != TOK_LPAREN) { + return fail("bad macro call"); } - if (!parse_args(s, call_pos + 1, arg_starts, arg_ends, &arg_count, &end_pos)) { + if (!parse_args(call_tok + 1, limit)) { return 0; } if (arg_count != m->param_count) { - snprintf(error_buf, sizeof(error_buf), - "macro '%.*s' expects %d args, got %d at line %d", - m->name_len, text_buf + m->name_start, - m->param_count, arg_count, s->toks[call_pos].line); - return 0; + return fail("wrong arg count"); } + end_pos = call_end_pos; mark = pool_used; - - for (i = 0; i < m->body_count; i++) { - int param_idx = find_param(m, &m->body[i]); + for (body_tok = m->body_start; body_tok < m->body_end; body_tok++) { + int param_idx = find_param(m, body_tok); int pasted = 0; - - if (param_idx >= 0) { - pasted = (i > 0 && m->body[i - 1].kind == TOK_PASTE) || - (i + 1 < m->body_count && m->body[i + 1].kind == TOK_PASTE); - if (!append_arg(s, arg_starts[param_idx], arg_ends[param_idx], pasted)) { + int ok; + + if (param_idx != 0) { + struct TokenSpan arg = {arg_starts[param_idx - 1], arg_ends[param_idx - 1]}; + pasted = (body_tok > m->body_start && (body_tok - 1)->kind == TOK_PASTE) || + (body_tok + 1 < m->body_end && (body_tok + 1)->kind == TOK_PASTE); + ok = pasted ? copy_paste_arg_to_pool(arg) : copy_arg_tokens_to_pool(arg); + if (!ok) { pool_used = mark; return 0; } continue; } - - if (!push_pool_token(m->body[i])) { + if (!push_pool_token(*body_tok)) { pool_used = mark; return 0; } } - count = pool_used - mark; - if (!paste_range(mark, &count)) { - pool_used = mark; + if (!paste_pool_range(mark)) { return 0; } - pool_used = mark + count; - *end_pos_out = end_pos; + *after_out = end_pos; *mark_out = mark; - *count_out = count; return 1; } -static void skip_expr_newlines(struct Token *toks, int count, int *pos) -{ - while (*pos < count && toks[*pos].kind == TOK_NEWLINE) { - *pos += 1; - } -} - static int parse_int_token(const struct Token *tok, long long *out) { char tmp[128]; @@ -670,19 +722,17 @@ static int parse_int_token(const struct Token *tok, long long *out) unsigned long long uv; long long sv; - if (tok->kind != TOK_WORD || tok->len <= 0 || tok->len >= (int)sizeof(tmp)) { - snprintf(error_buf, sizeof(error_buf), "expected integer atom at line %d", tok->line); - return 0; + if (tok->kind != TOK_WORD || tok->text.len <= 0 || tok->text.len >= (int)sizeof(tmp)) { + return fail("bad integer"); } - memcpy(tmp, text_buf + tok->start, (size_t)tok->len); - tmp[tok->len] = '\0'; + memcpy(tmp, tok->text.ptr, (size_t)tok->text.len); + tmp[tok->text.len] = '\0'; errno = 0; if (tmp[0] == '-') { sv = strtoll(tmp, &end, 0); if (errno != 0 || *end != '\0') { - snprintf(error_buf, sizeof(error_buf), "bad integer literal '%s' at line %d", tmp, tok->line); - return 0; + return fail("bad integer"); } *out = sv; return 1; @@ -690,228 +740,325 @@ static int parse_int_token(const struct Token *tok, long long *out) uv = strtoull(tmp, &end, 0); if (errno != 0 || *end != '\0') { - snprintf(error_buf, sizeof(error_buf), "bad integer literal '%s' at line %d", tmp, tok->line); - return 0; + return fail("bad integer"); } *out = (long long)uv; return 1; } -static int eval_expr_tokens(struct Token *toks, int count, int *pos, long long *out); - -static int eval_macro_expr(struct Token *toks, int count, int *pos, long long *out) +static enum ExprOp expr_op_code(const struct Token *tok) { - struct Stream tmp; - int macro_idx; - int end_pos; - int mark; - int expanded_count; - int expanded_pos = 0; - int ok; - - macro_idx = find_macro(&toks[*pos]); - if (macro_idx < 0 || *pos + 1 >= count || toks[*pos + 1].kind != TOK_LPAREN) { - return parse_int_token(&toks[(*pos)++], out); + if (tok->kind != TOK_WORD) { + return EXPR_INVALID; } - - tmp.toks = toks; - tmp.count = count; - tmp.pos = *pos; - tmp.line_start = 0; - tmp.pool_mark = -1; - - if (!expand_macro_at(&tmp, *pos, macro_idx, &end_pos, &mark, &expanded_count)) { - return 0; + if (token_text_eq(tok, "+")) { + return EXPR_ADD; } - if (expanded_count == 0) { - pool_used = mark; - snprintf(error_buf, sizeof(error_buf), "macro in expression expanded to nothing"); - return 0; + if (token_text_eq(tok, "-")) { + return EXPR_SUB; } - - ok = eval_expr_tokens(expand_pool + mark, expanded_count, &expanded_pos, out); - if (ok) { - skip_expr_newlines(expand_pool + mark, expanded_count, &expanded_pos); - if (expanded_pos != expanded_count) { - snprintf(error_buf, sizeof(error_buf), "macro in expression expanded to extra tokens"); - ok = 0; - } + if (token_text_eq(tok, "*")) { + return EXPR_MUL; } - pool_used = mark; - if (!ok) { - return 0; + if (token_text_eq(tok, "/")) { + return EXPR_DIV; } - *pos = end_pos; - return 1; + if (token_text_eq(tok, "%")) { + return EXPR_MOD; + } + if (token_text_eq(tok, "<<")) { + return EXPR_SHL; + } + if (token_text_eq(tok, ">>")) { + return EXPR_SHR; + } + if (token_text_eq(tok, "&")) { + return EXPR_AND; + } + if (token_text_eq(tok, "|")) { + return EXPR_OR; + } + if (token_text_eq(tok, "^")) { + return EXPR_XOR; + } + if (token_text_eq(tok, "~")) { + return EXPR_NOT; + } + if (token_text_eq(tok, "=") || token_text_eq(tok, "==")) { + return EXPR_EQ; + } + if (token_text_eq(tok, "!=")) { + return EXPR_NE; + } + if (token_text_eq(tok, "<")) { + return EXPR_LT; + } + if (token_text_eq(tok, "<=")) { + return EXPR_LE; + } + if (token_text_eq(tok, ">")) { + return EXPR_GT; + } + if (token_text_eq(tok, ">=")) { + return EXPR_GE; + } + return EXPR_INVALID; } -static int apply_expr_op(const struct Token *op, long long *args, int argc, long long *out) +static int apply_expr_op(enum ExprOp op, const long long *args, int argc, long long *out) { int i; - if (token_text_eq(op, "+")) { + switch (op) { + case EXPR_ADD: if (argc < 1) { - snprintf(error_buf, sizeof(error_buf), "'+' needs at least one argument"); - return 0; + return fail("bad expression"); } *out = args[0]; for (i = 1; i < argc; i++) { *out += args[i]; } return 1; - } - if (token_text_eq(op, "-")) { + case EXPR_SUB: if (argc < 1) { - snprintf(error_buf, sizeof(error_buf), "'-' needs at least one argument"); - return 0; + return fail("bad expression"); } *out = (argc == 1) ? -args[0] : args[0]; for (i = 1; i < argc; i++) { *out -= args[i]; } return 1; - } - if (token_text_eq(op, "*")) { + case EXPR_MUL: if (argc < 1) { - snprintf(error_buf, sizeof(error_buf), "'*' needs at least one argument"); - return 0; + return fail("bad expression"); } *out = args[0]; for (i = 1; i < argc; i++) { *out *= args[i]; } return 1; - } - if (token_text_eq(op, "/") || token_text_eq(op, "%") || - token_text_eq(op, "<<") || token_text_eq(op, ">>") || - token_text_eq(op, "=") || token_text_eq(op, "==") || - token_text_eq(op, "!=") || token_text_eq(op, "<") || - token_text_eq(op, "<=") || token_text_eq(op, ">") || - token_text_eq(op, ">=")) { + case EXPR_DIV: + if (argc != 2 || args[1] == 0) { + return fail("bad expression"); + } + *out = args[0] / args[1]; + return 1; + case EXPR_MOD: + if (argc != 2 || args[1] == 0) { + return fail("bad expression"); + } + *out = args[0] % args[1]; + return 1; + case EXPR_SHL: if (argc != 2) { - snprintf(error_buf, sizeof(error_buf), "binary expression operator needs two arguments"); - return 0; + return fail("bad expression"); } - if (token_text_eq(op, "/")) { - if (args[1] == 0) { - snprintf(error_buf, sizeof(error_buf), "division by zero"); - return 0; - } - *out = args[0] / args[1]; - } else if (token_text_eq(op, "%")) { - if (args[1] == 0) { - snprintf(error_buf, sizeof(error_buf), "modulo by zero"); - return 0; - } - *out = args[0] % args[1]; - } else if (token_text_eq(op, "<<")) { - *out = (long long)((unsigned long long)args[0] << args[1]); - } else if (token_text_eq(op, ">>")) { - *out = args[0] >> args[1]; - } else if (token_text_eq(op, "=") || token_text_eq(op, "==")) { - *out = args[0] == args[1]; - } else if (token_text_eq(op, "!=")) { - *out = args[0] != args[1]; - } else if (token_text_eq(op, "<")) { - *out = args[0] < args[1]; - } else if (token_text_eq(op, "<=")) { - *out = args[0] <= args[1]; - } else if (token_text_eq(op, ">")) { - *out = args[0] > args[1]; - } else { - *out = args[0] >= args[1]; + *out = (long long)((unsigned long long)args[0] << args[1]); + return 1; + case EXPR_SHR: + if (argc != 2) { + return fail("bad expression"); } + *out = args[0] >> args[1]; return 1; - } - if (token_text_eq(op, "&") || token_text_eq(op, "|") || token_text_eq(op, "^")) { + case EXPR_AND: if (argc < 1) { - snprintf(error_buf, sizeof(error_buf), "bitwise expression operator needs at least one argument"); - return 0; + return fail("bad expression"); } *out = args[0]; for (i = 1; i < argc; i++) { - if (token_text_eq(op, "&")) { - *out &= args[i]; - } else if (token_text_eq(op, "|")) { - *out |= args[i]; - } else { - *out ^= args[i]; - } + *out &= args[i]; } return 1; - } - if (token_text_eq(op, "~")) { + case EXPR_OR: + if (argc < 1) { + return fail("bad expression"); + } + *out = args[0]; + for (i = 1; i < argc; i++) { + *out |= args[i]; + } + return 1; + case EXPR_XOR: + if (argc < 1) { + return fail("bad expression"); + } + *out = args[0]; + for (i = 1; i < argc; i++) { + *out ^= args[i]; + } + return 1; + case EXPR_NOT: if (argc != 1) { - snprintf(error_buf, sizeof(error_buf), "'~' needs one argument"); - return 0; + return fail("bad expression"); } *out = ~args[0]; return 1; + case EXPR_EQ: + if (argc != 2) { + return fail("bad expression"); + } + *out = (args[0] == args[1]); + return 1; + case EXPR_NE: + if (argc != 2) { + return fail("bad expression"); + } + *out = (args[0] != args[1]); + return 1; + case EXPR_LT: + if (argc != 2) { + return fail("bad expression"); + } + *out = (args[0] < args[1]); + return 1; + case EXPR_LE: + if (argc != 2) { + return fail("bad expression"); + } + *out = (args[0] <= args[1]); + return 1; + case EXPR_GT: + if (argc != 2) { + return fail("bad expression"); + } + *out = (args[0] > args[1]); + return 1; + case EXPR_GE: + if (argc != 2) { + return fail("bad expression"); + } + *out = (args[0] >= args[1]); + return 1; + case EXPR_INVALID: + break; } - snprintf(error_buf, sizeof(error_buf), "unknown expression operator '%.*s'", - op->len, text_buf + op->start); - return 0; + return fail("bad expression"); } -static int eval_expr_tokens(struct Token *toks, int count, int *pos, long long *out) -{ - struct Token op; - long long args[MAX_PARAMS]; - int argc = 0; +static int eval_expr_range(struct TokenSpan span, long long *out); - skip_expr_newlines(toks, count, pos); - if (*pos >= count) { - snprintf(error_buf, sizeof(error_buf), "expected expression"); - return 0; - } +static int eval_expr_atom(struct Token *tok, struct Token *limit, + struct Token **after_out, long long *out) +{ + const struct Macro *macro; + struct Token *after; + int mark; - if (toks[*pos].kind != TOK_LPAREN) { - return eval_macro_expr(toks, count, pos, out); + macro = find_macro(tok); + if (macro != NULL && tok + 1 < limit && (tok + 1)->kind == TOK_LPAREN) { + if (!expand_macro_tokens(tok, limit, macro, &after, &mark)) { + return 0; + } + if (pool_used == mark) { + pool_used = mark; + return fail("bad expression"); + } + if (!eval_expr_range((struct TokenSpan){expand_pool + mark, expand_pool + pool_used}, out)) { + pool_used = mark; + return 0; + } + pool_used = mark; + *after_out = after; + return 1; } - *pos += 1; - skip_expr_newlines(toks, count, pos); - if (*pos >= count || toks[*pos].kind != TOK_WORD) { - snprintf(error_buf, sizeof(error_buf), "expected expression operator"); + if (!parse_int_token(tok, out)) { return 0; } - op = toks[*pos]; - *pos += 1; + *after_out = tok + 1; + return 1; +} - while (1) { - skip_expr_newlines(toks, count, pos); - if (*pos >= count) { - snprintf(error_buf, sizeof(error_buf), "unterminated expression"); - return 0; +static int eval_expr_range(struct TokenSpan span, long long *out) +{ + struct ExprFrame frames[MAX_EXPR_FRAMES]; + int frame_top = 0; + struct Token *pos = span.start; + long long value = 0; + long long result = 0; + int have_value = 0; + int have_result = 0; + + for (;;) { + if (have_value) { + if (frame_top > 0) { + struct ExprFrame *frame = &frames[frame_top - 1]; + + if (frame->argc >= MAX_PARAMS) { + return fail("bad expression"); + } + frame->args[frame->argc++] = value; + have_value = 0; + continue; + } + if (have_result) { + return fail("bad expression"); + } + result = value; + have_result = 1; + have_value = 0; + continue; } - if (toks[*pos].kind == TOK_RPAREN) { - *pos += 1; - return apply_expr_op(&op, args, argc, out); + + skip_expr_newlines(&pos, span.end); + if (pos >= span.end) { + break; } - if (argc >= MAX_PARAMS) { - snprintf(error_buf, sizeof(error_buf), "too many expression arguments"); - return 0; + + if (pos->kind == TOK_LPAREN) { + enum ExprOp op; + + pos++; + skip_expr_newlines(&pos, span.end); + if (pos >= span.end) { + return fail("bad expression"); + } + op = expr_op_code(pos); + if (op == EXPR_INVALID) { + return fail("bad expression"); + } + if (frame_top >= MAX_EXPR_FRAMES) { + return fail("expression overflow"); + } + frames[frame_top].op = op; + frames[frame_top].argc = 0; + frame_top++; + pos++; + continue; } - if (!eval_expr_tokens(toks, count, pos, &args[argc])) { + + if (pos->kind == TOK_RPAREN) { + if (frame_top <= 0) { + return fail("bad expression"); + } + if (!apply_expr_op(frames[frame_top - 1].op, + frames[frame_top - 1].args, + frames[frame_top - 1].argc, + &value)) { + return 0; + } + frame_top--; + pos++; + have_value = 1; + continue; + } + + if (!eval_expr_atom(pos, span.end, &pos, &value)) { return 0; } - argc++; + have_value = 1; } -} -static int eval_expr_range(struct Token *toks, int start, int end, long long *out) -{ - int pos = start; - - if (!eval_expr_tokens(toks, end, &pos, out)) { - return 0; + if (frame_top != 0 || !have_result) { + return fail("bad expression"); } - skip_expr_newlines(toks, end, &pos); - if (pos != end) { - snprintf(error_buf, sizeof(error_buf), "extra tokens after expression"); - return 0; + if (pos != span.end) { + return fail("bad expression"); } + + *out = result; return 1; } @@ -919,132 +1066,123 @@ static int emit_hex_value(unsigned long long value, int bytes) { char tmp[17]; static const char hex[] = "0123456789ABCDEF"; - int i; - int text_start; struct Token tok; + int i; + char *text_ptr; for (i = 0; i < bytes; i++) { unsigned int b = (unsigned int)((value >> (8 * i)) & 0xFF); tmp[2 * i] = hex[b >> 4]; - tmp[2 * i + 1] = hex[b & 0xF]; + tmp[2 * i + 1] = hex[b & 0x0F]; } tmp[2 * bytes] = '\0'; - text_start = append_text_len(tmp, 2 * bytes); - if (text_start < 0) { + text_ptr = append_text_len(tmp, 2 * bytes); + if (text_ptr == NULL) { return 0; } tok.kind = TOK_WORD; - tok.start = text_start; - tok.len = 2 * bytes; - tok.line = 0; + tok.text.ptr = text_ptr; + tok.text.len = 2 * bytes; return emit_token(&tok); } -static int expand_builtin_call(struct Stream *s, const char *name) +static int expand_builtin_call(struct Stream *s, const struct Token *tok) { - int arg_starts[MAX_PARAMS]; - int arg_ends[MAX_PARAMS]; - int arg_count; - int end_pos; long long value; - if (s->pos + 1 >= s->count || s->toks[s->pos + 1].kind != TOK_LPAREN) { - snprintf(error_buf, sizeof(error_buf), "expected '(' after %s", name); - return 0; + if (tok + 1 >= s->end || (tok + 1)->kind != TOK_LPAREN) { + return fail("bad builtin"); } - if (!parse_args(s, s->pos + 1, arg_starts, arg_ends, &arg_count, &end_pos)) { + if (!parse_args((struct Token *)tok + 1, s->end)) { return 0; } - if (strcmp(name, "%le32") == 0 || strcmp(name, "%le64") == 0) { + if (token_text_eq(tok, "%le32") || token_text_eq(tok, "%le64")) { + struct TokenSpan arg; + struct Token *end_pos; + if (arg_count != 1) { - snprintf(error_buf, sizeof(error_buf), "%s expects one argument", name); - return 0; + return fail("bad builtin"); } - if (!eval_expr_range(s->toks, arg_starts[0], arg_ends[0], &value)) { + arg.start = arg_starts[0]; + arg.end = arg_ends[0]; + end_pos = call_end_pos; + if (!eval_expr_range(arg, &value)) { return 0; } s->pos = end_pos; s->line_start = 0; - return emit_hex_value((unsigned long long)value, strcmp(name, "%le32") == 0 ? 4 : 8); + return emit_hex_value((unsigned long long)value, + token_text_eq(tok, "%le32") ? 4 : 8); } - if (strcmp(name, "%select") == 0) { - int selected_start; - int selected_end; + if (token_text_eq(tok, "%select")) { + struct TokenSpan cond_arg, then_arg, else_arg, chosen; + struct Token *end_pos; int mark; - int i; if (arg_count != 3) { - snprintf(error_buf, sizeof(error_buf), "%%select expects three arguments"); - return 0; + return fail("bad builtin"); } - if (!eval_expr_range(s->toks, arg_starts[0], arg_ends[0], &value)) { + cond_arg.start = arg_starts[0]; cond_arg.end = arg_ends[0]; + then_arg.start = arg_starts[1]; then_arg.end = arg_ends[1]; + else_arg.start = arg_starts[2]; else_arg.end = arg_ends[2]; + end_pos = call_end_pos; + if (!eval_expr_range(cond_arg, &value)) { return 0; } - selected_start = value != 0 ? arg_starts[1] : arg_starts[2]; - selected_end = value != 0 ? arg_ends[1] : arg_ends[2]; - + chosen = (value != 0) ? then_arg : else_arg; s->pos = end_pos; s->line_start = 0; - if (selected_start == selected_end) { + if (chosen.start == chosen.end) { return 1; } - mark = pool_used; - for (i = selected_start; i < selected_end; i++) { - if (!push_pool_token(s->toks[i])) { - pool_used = mark; - return 0; - } + if (!copy_span_to_pool(chosen)) { + pool_used = mark; + return 0; } - return push_stream(expand_pool + mark, selected_end - selected_start, mark); + return push_pool_stream_from_mark(mark); } - snprintf(error_buf, sizeof(error_buf), "unknown builtin '%s'", name); - return 0; + return fail("bad builtin"); } -static int expand_call(struct Stream *s, int macro_idx) +static int expand_call(struct Stream *s, const struct Macro *macro) { - int end_pos; + struct Token *after; int mark; - int count; - if (!expand_macro_at(s, s->pos, macro_idx, &end_pos, &mark, &count)) { + if (!expand_macro_tokens(s->pos, s->end, macro, &after, &mark)) { return 0; } - s->pos = end_pos; + s->pos = after; s->line_start = 0; - if (count == 0) { - pool_used = mark; - return 1; - } - return push_stream(expand_pool + mark, count, mark); + return push_pool_stream_from_mark(mark); } static int process_tokens(void) { - if (!push_stream(source_tokens, source_count, -1)) { + if (!push_stream_span((struct TokenSpan){source_tokens, source_tokens + source_count}, -1)) { return 0; } for (;;) { struct Stream *s; struct Token *tok; - int macro_idx; + const struct Macro *macro; - if (stream_top <= 0) { + s = current_stream(); + if (s == NULL) { break; } - s = &streams[stream_top - 1]; - if (s->pos >= s->count) { + if (s->pos >= s->end) { pop_stream(); continue; } - tok = &s->toks[s->pos]; + tok = s->pos; if (s->line_start && tok->kind == TOK_WORD && @@ -1065,20 +1203,22 @@ static int process_tokens(void) } if (tok->kind == TOK_WORD && + tok + 1 < s->end && + (tok + 1)->kind == TOK_LPAREN && (token_text_eq(tok, "%le32") || token_text_eq(tok, "%le64") || - token_text_eq(tok, "%select")) && - s->pos + 1 < s->count && - s->toks[s->pos + 1].kind == TOK_LPAREN) { - if (!expand_builtin_call(s, text_buf + tok->start)) { + token_text_eq(tok, "%select"))) { + if (!expand_builtin_call(s, tok)) { return 0; } continue; } - macro_idx = find_macro(tok); - if (macro_idx >= 0 && s->pos + 1 < s->count && s->toks[s->pos + 1].kind == TOK_LPAREN) { - if (!expand_call(s, macro_idx)) { + macro = find_macro(tok); + if (macro != NULL && + tok + 1 < s->end && + (tok + 1)->kind == TOK_LPAREN) { + if (!expand_call(s, macro)) { return 0; } continue; @@ -1092,8 +1232,7 @@ static int process_tokens(void) } if (output_used >= MAX_OUTPUT) { - snprintf(error_buf, sizeof(error_buf), "output buffer overflow"); - return 0; + return fail("output overflow"); } output_buf[output_used] = '\0'; return 1; @@ -1129,7 +1268,7 @@ int main(int argc, char **argv) input_buf[nread] = '\0'; if (!lex_source(input_buf) || !process_tokens()) { - fprintf(stderr, "m1macro: %s\n", error_buf); + fprintf(stderr, "m1macro: %s\n", error_msg != NULL ? error_msg : "failed"); return 1; }