// Copyright (C) 2023, Jakob Wakeling // All rights reserved. #include "parse.h" #include "util/log.h" #include "util/stack.h" #include #include #include char *ast_ks[] = { "AK_VOID", "AK_COMM", "AK_PIPE", }; /* Initialise an AST node. */ extern ast *ast_init(ast_k kind) { ast *a = xcalloc(1, sizeof(*a)); a->k = kind; return a; } /* Uninitialise an AST node and its children. */ extern void ast_free(ast *a) { if (a == NULL) { return; } ast_free(a->lc); ast_free(a->rc); stack_free(&a->c); free(a->rin.s); free(a->rout.s); free(a->rerr.s); free(a); } #define T (l->t) /* Current Token */ static ast *parse_comm(lex *l); static ast *parse_pipe(lex *l, ast *lhs); /* Parse a program. */ extern ast *parse(lex *l) { for (; T.k == TK_END; lex_next(l)); if (T.k == TK_EOF) { return NULL; } if (T.k != TK_WORD) { log_warn("Unexpected \"%s\", was expecting a word", tok_ks[T.k]); return NULL; } ast *lhs = parse_comm(l); for (;;) switch (lex_next(l).k) { case TK_PIPE: { lhs = parse_pipe(l, lhs); } continue; default: {} return lhs; } } /* Parse a command statement. */ static ast *parse_comm(lex *l) { assert(T.k == TK_WORD); ast *a = ast_init(AK_COMM); a->c = stack_init(sizeof (char *), &free); /* Push each command argument onto the child stack */ stack_push(&a->c, (a->s = lex_next(l).s)); for (;;) switch (lex_peek(l).k) { case TK_WORD: { stack_push(&a->c, lex_next(l).s); } continue; case TK_RIN: { if (lex_next(l), T.k == TK_WORD) { a->rin.s = lex_next(l).s; } else { log_warn("Unexpected token \"%s\"", tok_ks[T.k]); } } continue; case TK_ROUT: { if (lex_next(l), T.k == TK_WORD) { a->rout.s = lex_next(l).s; a->rout.app = false; } else { log_warn("Unexpected token \"%s\"", tok_ks[T.k]); } } continue; case TK_RAPP: { if (lex_next(l), T.k == TK_WORD) { a->rout.s = lex_next(l).s; a->rout.app = true; } else { log_warn("Unexpected token \"%s\"", tok_ks[T.k]); } } continue; default: { stack_push(&a->c, NULL); } return a; } } /* Parse a pipe statement. */ static ast *parse_pipe(lex *l, ast *lhs) { ast *a = ast_init(AK_PIPE); a->lc = lhs; a->rc = parse_comm(l); return a; } /* Print parser debug output. */ extern void ast_debug(ast *a, u64 indent) { for (u64 i = 0; i != indent; i += 1) { printf("\t"); } printf("%s: %s", ast_ks[a->k], a->s); if (a->k == AK_COMM) { for (u64 i = 1; i != a->c.al - 1; i += 1) { printf(" %s", ((char **)a->c.a)[i]); } if (a->rin.s) { printf(" < %s", a->rin.s); } if (a->rout.s) { printf(a->rout.app ? " >> %s" : " > %s", a->rout.s); } } printf("\n"); if (a->lc) { ast_debug(a->lc, indent + 1); } if (a->rc) { ast_debug(a->rc, indent + 1); } }