asm/asm.c
static struct Token *parse(struct Context *ctx, struct Token *first) {
    while (first && (first->flags & TF_IGNORE)) {
        asmdbg(ctx, first, "Ignoring token");
        first = first->next;
    }

    if (!first) {
        return NULL;
    }

    if (first->kind == TK_EOL) {
        return first->next;
    }

    struct Token *last, *eol = first;
    while (eol->kind != TK_EOL) {
        eol = eol->next;
    }
    last = eol->prev;

    if (first->kind == TK_AT) {
        asmchk(
            first->next->kind == TK_SYMBOL,
            ctx, first, "Expected directive"
        );
        directive(ctx, first, false);
        return eol->next;
    }

    // label
    if (last->kind == TK_COLON) {
        if (ctx->macro) {
            // macro ends on first empty line (double EOL)
            struct Token *macro_end = last->next;

            while (macro_end->next &&
                    !(macro_end->kind == TK_EOL &&
                        macro_end->next->kind == TK_EOL)) {
                macro_end = macro_end->next;
            }

            parse_macro(ctx, first, macro_end);
            ctx->macro = false;
            return macro_end->next;
        } else if (!ctx->microcode) {
            struct Label *label = asmalloc(sizeof(struct Label));

            bool sub = false;
            struct Token *name = first;

            if (name->kind == TK_DOT) {
                sub = true;
                name = name->next;
            }

            asmchk(name->kind == TK_SYMBOL, ctx, name, "Malformed label");

            *label = (struct Label) {
                .name = token_data(name, NULL, 0),
                .sub = sub,
                .value = location(ctx),
                .prev = NULL,
                .next = ctx->labels
            };

            if (ctx->labels) {
                ctx->labels->prev = label;
            }

            ctx->labels = label;
            return eol->next;
        }
    }

    asmchk(!ctx->macro, ctx, first, "Expected macro");

    if (ctx->microcode) {
        return microcode_parse(ctx, first);
    }

    // evaluate labels
    eval_labels(ctx, first->next, eol->prev);

    // evaluate arguments into final values
    struct Token *tk;
    asmchk(
        !(tk = eval_between(ctx, first->next, eol->prev)),
        ctx, tk,
        "Error parsing expression"
    );

    asmchk(first->kind == TK_SYMBOL, ctx, first, "Malformed syntax");

    // must be an operation
    char op_name[256];
    assert(token_data(first, op_name, sizeof(op_name)));

    // search for the right operation which also type checks
    usize num_matching = 0;
    struct Op *op = &ctx->ops.buffer[0], *matching_ops[16];

    enum Arg args[16], *error_arg;
    struct Token *arg_tokens[16];
    usize num_args =
        type_args(ctx, first->next, args, arg_tokens, ARRLEN(args));

    // find a matching operation
    while (op != &ctx->ops.buffer[ctx->ops.size]) {
        if (strcasecmp(op_name, op->name)) {
            op++;
            continue;
        }

        matching_ops[num_matching++] = op;

        // check that args can match
        if (!(error_arg = check_args(ctx, op, args, num_args))) {
            break;
        }

        op++;
    }

    if (op == &ctx->ops.buffer[ctx->ops.size]) {
        asmchk(num_matching > 0, ctx, first, "Unknown operation");
        asmchk(
            num_matching != 1, ctx,
            error_arg ? arg_tokens[(usize) (error_arg - args)] : first,
            "Argument type mismatch"
        );
        asmchk(
            false, ctx, first,
            "TODO: good error message for 'could not find op'"
        );
    }

    if (op->macro) {
        return expand_macro(
            ctx, op, first, eol->prev,
            args, arg_tokens, num_args
        );
    }

    emit(ctx, op, first, args, arg_tokens, num_args);

    return eol->next;
}
// parses and emits the specified token stream
parses and emits the specified token stream
parses and emits the specified token stream
static struct Token *parse(struct Context *ctx, struct Token *first) {
    while (first && (first->flags & TF_IGNORE)) {
        asmdbg(ctx, first, "Ignoring token");
        first = first->next;
    }
    if (!first) {
        return NULL;
    }
    if (first->kind == TK_EOL) {
        return first->next;
    }
    struct Token *last, *eol = first;
    while (eol->kind != TK_EOL) {
        eol = eol->next;
    }
    last = eol->prev;
    if (first->kind == TK_AT) {
        asmchk(
            first->next->kind == TK_SYMBOL,
            ctx, first, "Expected directive"
        );
        directive(ctx, first, false);
        return eol->next;
    }
    if (last->kind == TK_COLON) {
        if (ctx->macro) {
            struct Token *macro_end = last->next;
            while (macro_end->next &&
                    !(macro_end->kind == TK_EOL &&
                        macro_end->next->kind == TK_EOL)) {
                macro_end = macro_end->next;
            }
            parse_macro(ctx, first, macro_end);
            ctx->macro = false;
            return macro_end->next;
        } else if (!ctx->microcode) {
            struct Label *label = asmalloc(sizeof(struct Label));
            bool sub = false;
            struct Token *name = first;
            if (name->kind == TK_DOT) {
                sub = true;
                name = name->next;
            }
            asmchk(name->kind == TK_SYMBOL, ctx, name, "Malformed label");
            *label = (struct Label) {
                .name = token_data(name, NULL, 0),
                .sub = sub,
                .value = location(ctx),
                .prev = NULL,
                .next = ctx->labels
            };
            if (ctx->labels) {
                ctx->labels->prev = label;
            }
            ctx->labels = label;
            return eol->next;
        }
    }
    asmchk(!ctx->macro, ctx, first, "Expected macro");
    if (ctx->microcode) {
        return microcode_parse(ctx, first);
    }
    eval_labels(ctx, first->next, eol->prev);
    struct Token *tk;
    asmchk(
        !(tk = eval_between(ctx, first->next, eol->prev)),
        ctx, tk,
        "Error parsing expression"
    );
    asmchk(first->kind == TK_SYMBOL, ctx, first, "Malformed syntax");
    char op_name[256];
    assert(token_data(first, op_name, sizeof(op_name)));
    usize num_matching = 0;
    struct Op *op = &ctx->ops.buffer[0], *matching_ops[16];
    enum Arg args[16], *error_arg;
    struct Token *arg_tokens[16];
    usize num_args =
        type_args(ctx, first->next, args, arg_tokens, ARRLEN(args));
    while (op != &ctx->ops.buffer[ctx->ops.size]) {
        if (strcasecmp(op_name, op->name)) {
            op++;
            continue;
        }
        matching_ops[num_matching++] = op;
        if (!(error_arg = check_args(ctx, op, args, num_args))) {
            break;
        }
        op++;
    }
    if (op == &ctx->ops.buffer[ctx->ops.size]) {
        asmchk(num_matching > 0, ctx, first, "Unknown operation");
        asmchk(
            num_matching != 1, ctx,
            error_arg ? arg_tokens[(usize) (error_arg - args)] : first,
            "Argument type mismatch"
        );
        asmchk(
            false, ctx, first,
            "TODO: good error message for 'could not find op'"
        );
    }
    if (op->macro) {
        return expand_macro(
            ctx, op, first, eol->prev,
            args, arg_tokens, num_args
        );
    }
    emit(ctx, op, first, args, arg_tokens, num_args);
    return eol->next;
}
static struct Token * parse ( struct Context * ctx , struct Token * first ) { while ( first && ( first -> flags & TF_IGNORE ) ) { asmdbg ( ctx , first , " " ) ; first = first -> next ; } if ( ! first ) { return NULL ; } if ( first -> kind == TK_EOL ) { return first -> next ; } struct Token * last , * eol = first ; while ( eol -> kind != TK_EOL ) { eol = eol -> next ; } last = eol -> prev ; if ( first -> kind == TK_AT ) { asmchk ( first -> next -> kind == TK_SYMBOL , ctx , first , " " ) ; directive ( ctx , first , false ) ; return eol -> next ; } if ( last -> kind == TK_COLON ) { if ( ctx -> macro ) { struct Token * macro_end = last -> next ; while ( macro_end -> next && ! ( macro_end -> kind == TK_EOL && macro_end -> next -> kind == TK_EOL ) ) { macro_end = macro_end -> next ; } parse_macro ( ctx , first , macro_end ) ; ctx -> macro = false ; return macro_end -> next ; } else if ( ! ctx -> microcode ) { struct Label * label = asmalloc ( sizeof ( struct Label ) ) ; bool sub = false ; struct Token * name = first ; if ( name -> kind == TK_DOT ) { sub = true ; name = name -> next ; } asmchk ( name -> kind == TK_SYMBOL , ctx , name , " " ) ; * label = ( struct Label ) { . name = token_data ( name , NULL , 0 ) , . sub = sub , . value = location ( ctx ) , . prev = NULL , . next = ctx -> labels } ; if ( ctx -> labels ) { ctx -> labels -> prev = label ; } ctx -> labels = label ; return eol -> next ; } } asmchk ( ! ctx -> macro , ctx , first , " " ) ; if ( ctx -> microcode ) { return microcode_parse ( ctx , first ) ; } eval_labels ( ctx , first -> next , eol -> prev ) ; struct Token * tk ; asmchk ( ! ( tk = eval_between ( ctx , first -> next , eol -> prev ) ) , ctx , tk , " " ) ; asmchk ( first -> kind == TK_SYMBOL , ctx , first , " " ) ; char op_name [ 256 ] ; assert ( token_data ( first , op_name , sizeof ( op_name ) ) ) ; usize num_matching = 0 ; struct Op * op = & ctx -> ops . buffer [ 0 ] , * matching_ops [ 16 ] ; enum Arg args [ 16 ] , * error_arg ; struct Token * arg_tokens [ 16 ] ; usize num_args = type_args ( ctx , first -> next , args , arg_tokens , ARRLEN ( args ) ) ; while ( op != & ctx -> ops . buffer [ ctx -> ops . size ] ) { if ( strcasecmp ( op_name , op -> name ) ) { op ++ ; continue ; } matching_ops [ num_matching ++ ] = op ; if ( ! ( error_arg = check_args ( ctx , op , args , num_args ) ) ) { break ; } op ++ ; } if ( op == & ctx -> ops . buffer [ ctx -> ops . size ] ) { asmchk ( num_matching > 0 , ctx , first , " " ) ; asmchk ( num_matching != 1 , ctx , error_arg ? arg_tokens [ ( usize ) ( error_arg - args ) ] : first , " " ) ; asmchk ( false , ctx , first , " " ) ; } if ( op -> macro ) { return expand_macro ( ctx , op , first , eol -> prev , args , arg_tokens , num_args ) ; } emit ( ctx , op , first , args , arg_tokens , num_args ) ; return eol -> next ; }
parses and emits the specified token stream
parses and emits the specified token stream
// label // macro ends on first empty line (double EOL) // evaluate labels // evaluate arguments into final values // must be an operation // search for the right operation which also type checks // find a matching operation // check that args can match