ESH

Executive Shell
git clone http://git.omkov.net/ESH
Log | Tree | Refs | README | Download

ESH/src/parse.c (104 lines, 2.6 KiB) -rw-r--r-- blame download

0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
// Copyright (C) 2023, Jakob Wakeling
// All rights reserved.

#include "parse.h"
#include "util/log.h"
#include "util/stack.h"

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

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); }
}