123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728 |
- package nncl
- import "fmt"
- import "os"
- import "io"
- import "strings"
- const MAX_VAR_LENGTH=256
- type Ncl struct {
- }
- func (ncl *Ncl) eval(s string) {
- }
- /* Token type and control flow constants */
- const (
- TCMD = iota
- TWORD
- TPART
- TERROR
- )
- const (
- FERROR = iota
- FNORMAL
- FRETURN
- FBREAK
- FAGAIN
- )
- func isSpecial(c rune, quote bool) bool {
- return (c == '$' || (!quote && (c == '{' || c == '}' || c == ';' || c == '\r' ||
- c == '\n')) ||
- c == '[' || c == ']' || c == '"')
- }
- func isSpace(c rune) bool {
- return (c == ' ' || c == '\t')
- }
- func isEnd(c rune) bool {
- return (c == '\n' || c == '\r' || c == ';');
- }
- func scan(in RuneScanner, line, col *int) (rune, error) {
- r, _, err := in.ReadRune()
- if r == '\n' {
- *col = 0
- *rows = *rows + 1
- }
- *col = *col + 1
- return r, err
- }
- func next(in RuneScanner, token * string.Builder, quote *bool, line, col *int) (int, error) {
- var err error
- var c rune
- depth := 0
- var open rune
- var end rune
- for c, err = scan(in, line, col); err != nil ; c, err = scan(in, line, col) {
- if !*quote {
- /* Skip leading spaces if not quoted */
- for ; isSpace(c); c,err = scan(in, line, col) {
- if err != nil {
- return TERROR, err
- }
- }
- /* Terminate command if not quoted */
- if isEnd(c) {
- token.WriteRune(c)
- return TCMD, nil
- }
- }
- if (c == '$') { /* Variable token, must not start with a space or quote */
- c2, err := scan(in, line, col)
- if err != nil {
- return TERROR, err
- }
- if is_space(c2) {
- return TERROR, fmt.Errorf("Space not allowed after $.")
- }
- token.WriteRune(c)
- mode := *quote
- *quote = false
- r, err := next(in, token, quote);
- *quote = mode
- if r == TWORD && quote && err == nil {
- return TPART, nil
- }
- return r, err
- }
- if (c == '[' || (!*quote && c == '{')) {
- /* Interleaving pairs are not welcome, but it simplifies the code */
- open = c
- end = '}'
- if open == '[' {
- end = ']'
- }
- for c,_,err, depth = in.ReadRune(), 1 ; depth != 0; c,_,err = in.ReadRune() {
- if err != nil {
- return TERROR, error
- }
- if c == open {
- depth++
- } else if c == end {
- depth--
- }
- token.WriteRune(c)
- }
- } else if c == '"' {
- *quote = !*quote;
- if (*quote) {
- return TPART;
- }
- if (n < 2 || (!ncl_is_space(s[1]) && !ncl_is_end(s[1]))) {
- return TERROR;
- }
- *from = *to = s + 1;
- return TWORD;
- } else if (*s == ']' || *s == '}') {
- /* Unbalanced bracket or brace */
- return TERROR;
- } else {
- while (i < n && (*q || !ncl_is_space(s[i])) && !ncl_is_special(s[i], *q)) {
- i++;
- }
- }
- *to = s + i;
- if (i == n) {
- return TERROR;
- }
- if (*q) {
- return TPART;
- }
- return (ncl_is_space(s[i]) || ncl_is_end(s[i])) ? TWORD : TPART;
- }
- /* A helper parser struct and macro (requires C99) */
- struct ncl_parser {
- const char *from;
- const char *to;
- const char *start;
- const char *end;
- int q;
- int token;
- };
- #define ncl_each(s, len, skiperr) \
- for (struct ncl_parser p = {NULL, NULL, (s), (s) + (len), 0, TERROR}; \
- p.start < p.end && \
- (((p.token = ncl_next(p.start, p.end - p.start, &p.from, &p.to, \
- &p.q)) != TERROR) || \
- (skiperr)); \
- p.start = p.to)
- /* ------------------------------------------------------- */
- /* ------------------------------------------------------- */
- /* ------------------------------------------------------- */
- /* ------------------------------------------------------- */
- /* ------------------------------------------------------- */
- typedef char ncl_value_t;
- const char *ncl_string(ncl_value_t *v) { return v; }
- int ncl_int(ncl_value_t *v) { return atoi(v); }
- int ncl_length(ncl_value_t *v) { return v == NULL ? 0 : strlen(v); }
- void ncl_free(ncl_value_t *v) { free(v); }
- ncl_value_t *ncl_append_string(ncl_value_t *v, s string) {
- size_t n = ncl_length(v);
- v = realloc(v, n + len + 1);
- memset((char *)ncl_string(v) + n, 0, len + 1);
- strncpy((char *)ncl_string(v) + n, s, len);
- return v;
- }
- ncl_value_t *ncl_append(ncl_value_t *v, ncl_value_t *tail) {
- v = ncl_append_string(v, ncl_string(tail), ncl_length(tail));
- ncl_free(tail);
- return v;
- }
- ncl_value_t *ncl_alloc(s string) {
- return ncl_append_string(NULL, s, len);
- }
- ncl_value_t *ncl_dup(ncl_value_t *v) {
- return ncl_alloc(ncl_string(v), ncl_length(v));
- }
- ncl_value_t *ncl_list_alloc() { return ncl_alloc("", 0); }
- int ncl_list_length(ncl_value_t *v) {
- int count = 0;
- ncl_each(ncl_string(v), ncl_length(v) + 1, 0) {
- if (p.token == TWORD) {
- count++;
- }
- }
- return count;
- }
- void ncl_list_free(ncl_value_t *v) { free(v); }
- ncl_value_t *ncl_list_at(ncl_value_t *v, int index) {
- int i = 0;
- ncl_each(ncl_string(v), ncl_length(v) + 1, 0) {
- if (p.token == TWORD) {
- if (i == index) {
- if (p.from[0] == '{') {
- return ncl_alloc(p.from + 1, p.to - p.from - 2);
- }
- return ncl_alloc(p.from, p.to - p.from);
- }
- i++;
- }
- }
- return NULL;
- }
- ncl_value_t *ncl_list_append(ncl_value_t *v, ncl_value_t *tail) {
- if (ncl_length(v) > 0) {
- v = ncl_append(v, ncl_alloc(" ", 2));
- }
- if (ncl_length(tail) > 0) {
- int q = 0;
- const char *p;
- for (p = ncl_string(tail); *p; p++) {
- if (ncl_is_space(*p) || ncl_is_special(*p, 0)) {
- q = 1;
- break;
- }
- }
- if (q) {
- v = ncl_append(v, ncl_alloc("{", 1));
- }
- v = ncl_append(v, ncl_dup(tail));
- if (q) {
- v = ncl_append(v, ncl_alloc("}", 1));
- }
- } else {
- v = ncl_append(v, ncl_alloc("{}", 2));
- }
- return v;
- }
- /* ----------------------------- */
- /* ----------------------------- */
- /* ----------------------------- */
- /* ----------------------------- */
- typedef int (*ncl_cmd_fn_t)(struct ncl *, ncl_value_t *, void *);
- struct ncl_cmd {
- ncl_value_t *name;
- int arity;
- ncl_cmd_fn_t fn;
- void *arg;
- struct ncl_cmd *next;
- };
- struct ncl_var {
- ncl_value_t *name;
- ncl_value_t *value;
- struct ncl_var *next;
- };
- struct ncl_env {
- struct ncl_var *vars;
- struct ncl_env *parent;
- };
- static struct ncl_env *ncl_env_alloc(struct ncl_env *parent) {
- struct ncl_env *env = malloc(sizeof(*env));
- env->vars = NULL;
- env->parent = parent;
- return env;
- }
- static struct ncl_var *ncl_env_var(struct ncl_env *env, ncl_value_t *name) {
- struct ncl_var *var = malloc(sizeof(struct ncl_var));
- var->name = ncl_dup(name);
- var->next = env->vars;
- var->value = ncl_alloc("", 0);
- env->vars = var;
- return var;
- }
- static struct ncl_env *ncl_env_free(struct ncl_env *env) {
- struct ncl_env *parent = env->parent;
- while (env->vars) {
- struct ncl_var *var = env->vars;
- env->vars = env->vars->next;
- ncl_free(var->name);
- ncl_free(var->value);
- free(var);
- }
- free(env);
- return parent;
- }
- struct ncl {
- struct ncl_env *env;
- struct ncl_cmd *cmds;
- ncl_value_t *result;
- };
- ncl_value_t *ncl_var(struct ncl *ncl, ncl_value_t *name, ncl_value_t *v) {
- DBG("var(%s := %.*s)\n", ncl_string(name), ncl_length(v), ncl_string(v));
- struct ncl_var *var;
- for (var = ncl->env->vars; var != NULL; var = var->next) {
- if (strcmp(var->name, ncl_string(name)) == 0) {
- break;
- }
- }
- if (var == NULL) {
- var = ncl_env_var(ncl->env, name);
- }
- if (v != NULL) {
- ncl_free(var->value);
- var->value = ncl_dup(v);
- ncl_free(v);
- }
- return var->value;
- }
- int ncl_result(struct ncl *ncl, int flow, ncl_value_t *result) {
- DBG("ncl_result %.*s, flow=%d\n", ncl_length(result), ncl_string(result),
- flow);
- ncl_free(ncl->result);
- ncl->result = result;
- return flow;
- }
- int ncl_subst(struct ncl *ncl, s string) {
- DBG("subst(%.*s)\n", (int)len, s);
- if (len == 0) {
- return ncl_result(ncl, FNORMAL, ncl_alloc("", 0));
- }
- switch (s[0]) {
- case '{':
- if (len <= 1) {
- return ncl_result(ncl, FERROR, ncl_alloc("", 0));
- }
- return ncl_result(ncl, FNORMAL, ncl_alloc(s + 1, len - 2));
- case '$': {
- if (len >= MAX_VAR_LENGTH) {
- return ncl_result(ncl, FERROR, ncl_alloc("", 0));
- }
- char buf[5 + MAX_VAR_LENGTH] = "set ";
- strncat(buf, s + 1, len - 1);
- return ncl_eval(ncl, buf, strlen(buf) + 1);
- }
- case '[': {
- ncl_value_t *expr = ncl_alloc(s + 1, len - 2);
- int r = ncl_eval(ncl, ncl_string(expr), ncl_length(expr) + 1);
- ncl_free(expr);
- return r;
- }
- default:
- return ncl_result(ncl, FNORMAL, ncl_alloc(s, len));
- }
- }
- int ncl_eval(struct ncl *ncl, s string) {
- DBG("eval(%.*s)->\n", (int)len, s);
- ncl_value_t *list = ncl_list_alloc();
- ncl_value_t *cur = NULL;
- ncl_each(s, len, 1) {
- DBG("ncl_next %d %.*s\n", p.token, (int)(p.to - p.from), p.from);
- switch (p.token) {
- case TERROR:
- DBG("eval: FERROR, lexer error\n");
- return ncl_result(ncl, FERROR, ncl_alloc("", 0));
- case TWORD:
- DBG("token %.*s, length=%d, cur=%p (3.1.1)\n", (int)(p.to - p.from),
- p.from, (int)(p.to - p.from), cur);
- if (cur != NULL) {
- ncl_subst(ncl, p.from, p.to - p.from);
- ncl_value_t *part = ncl_dup(ncl->result);
- cur = ncl_append(cur, part);
- } else {
- ncl_subst(ncl, p.from, p.to - p.from);
- cur = ncl_dup(ncl->result);
- }
- list = ncl_list_append(list, cur);
- ncl_free(cur);
- cur = NULL;
- break;
- case TPART:
- ncl_subst(ncl, p.from, p.to - p.from);
- ncl_value_t *part = ncl_dup(ncl->result);
- cur = ncl_append(cur, part);
- break;
- case TCMD:
- if (ncl_list_length(list) == 0) {
- ncl_result(ncl, FNORMAL, ncl_alloc("", 0));
- } else {
- ncl_value_t *cmdname = ncl_list_at(list, 0);
- struct ncl_cmd *cmd = NULL;
- int r = FERROR;
- for (cmd = ncl->cmds; cmd != NULL; cmd = cmd->next) {
- if (strcmp(ncl_string(cmdname), ncl_string(cmd->name)) == 0) {
- if (cmd->arity == 0 || cmd->arity == ncl_list_length(list)) {
- r = cmd->fn(ncl, list, cmd->arg);
- break;
- }
- }
- }
- ncl_free(cmdname);
- if (cmd == NULL || r != FNORMAL) {
- ncl_list_free(list);
- return r;
- }
- }
- ncl_list_free(list);
- list = ncl_list_alloc();
- break;
- }
- }
- ncl_list_free(list);
- return FNORMAL;
- }
- /* --------------------------------- */
- /* --------------------------------- */
- /* --------------------------------- */
- /* --------------------------------- */
- /* --------------------------------- */
- void ncl_register(struct ncl *ncl, const char *name, ncl_cmd_fn_t fn, int arity,
- void *arg) {
- struct ncl_cmd *cmd = malloc(sizeof(struct ncl_cmd));
- cmd->name = ncl_alloc(name, strlen(name));
- cmd->fn = fn;
- cmd->arg = arg;
- cmd->arity = arity;
- cmd->next = ncl->cmds;
- ncl->cmds = cmd;
- }
- static int ncl_cmd_set(struct ncl *ncl, ncl_value_t *args, void *arg) {
- (void)arg;
- ncl_value_t *var = ncl_list_at(args, 1);
- ncl_value_t *val = ncl_list_at(args, 2);
- int r = ncl_result(ncl, FNORMAL, ncl_dup(ncl_var(ncl, var, val)));
- ncl_free(var);
- return r;
- }
- static int ncl_cmd_subst(struct ncl *ncl, ncl_value_t *args, void *arg) {
- (void)arg;
- ncl_value_t *s = ncl_list_at(args, 1);
- int r = ncl_subst(ncl, ncl_string(s), ncl_length(s));
- ncl_free(s);
- return r;
- }
- #ifndef TCL_DISABLE_PUTS
- static int ncl_cmd_puts(struct ncl *ncl, ncl_value_t *args, void *arg) {
- (void)arg;
- ncl_value_t *text = ncl_list_at(args, 1);
- puts(ncl_string(text));
- putchar('\n');
- return ncl_result(ncl, FNORMAL, text);
- }
- #endif
- static int ncl_user_proc(struct ncl *ncl, ncl_value_t *args, void *arg) {
- ncl_value_t *code = (ncl_value_t *)arg;
- ncl_value_t *params = ncl_list_at(code, 2);
- ncl_value_t *body = ncl_list_at(code, 3);
- ncl->env = ncl_env_alloc(ncl->env);
- for (int i = 0; i < ncl_list_length(params); i++) {
- ncl_value_t *param = ncl_list_at(params, i);
- ncl_value_t *v = ncl_list_at(args, i + 1);
- ncl_var(ncl, param, v);
- ncl_free(param);
- }
- ncl_eval(ncl, ncl_string(body), ncl_length(body) + 1);
- ncl->env = ncl_env_free(ncl->env);
- ncl_free(params);
- ncl_free(body);
- return FNORMAL;
- }
- static int ncl_cmd_proc(struct ncl *ncl, ncl_value_t *args, void *arg) {
- (void)arg;
- ncl_value_t *name = ncl_list_at(args, 1);
- ncl_register(ncl, ncl_string(name), ncl_user_proc, 0, ncl_dup(args));
- ncl_free(name);
- return ncl_result(ncl, FNORMAL, ncl_alloc("", 0));
- }
- static int ncl_cmd_if(struct ncl *ncl, ncl_value_t *args, void *arg) {
- (void)arg;
- int i = 1;
- int n = ncl_list_length(args);
- int r = FNORMAL;
- while (i < n) {
- ncl_value_t *cond = ncl_list_at(args, i);
- ncl_value_t *branch = NULL;
- if (i + 1 < n) {
- branch = ncl_list_at(args, i + 1);
- }
- r = ncl_eval(ncl, ncl_string(cond), ncl_length(cond) + 1);
- ncl_free(cond);
- if (r != FNORMAL) {
- ncl_free(branch);
- break;
- }
- if (ncl_int(ncl->result)) {
- r = ncl_eval(ncl, ncl_string(branch), ncl_length(branch) + 1);
- ncl_free(branch);
- break;
- }
- i = i + 2;
- ncl_free(branch);
- }
- return r;
- }
- static int ncl_cmd_flow(struct ncl *ncl, ncl_value_t *args, void *arg) {
- (void)arg;
- int r = FERROR;
- ncl_value_t *flowval = ncl_list_at(args, 0);
- const char *flow = ncl_string(flowval);
- if (strcmp(flow, "break") == 0) {
- r = FBREAK;
- } else if (strcmp(flow, "continue") == 0) {
- r = FAGAIN;
- } else if (strcmp(flow, "return") == 0) {
- r = ncl_result(ncl, FRETURN, ncl_list_at(args, 1));
- }
- ncl_free(flowval);
- return r;
- }
- static int ncl_cmd_while(struct ncl *ncl, ncl_value_t *args, void *arg) {
- (void)arg;
- ncl_value_t *cond = ncl_list_at(args, 1);
- ncl_value_t *loop = ncl_list_at(args, 2);
- int r;
- for (;;) {
- r = ncl_eval(ncl, ncl_string(cond), ncl_length(cond) + 1);
- if (r != FNORMAL) {
- ncl_free(cond);
- ncl_free(loop);
- return r;
- }
- if (!ncl_int(ncl->result)) {
- ncl_free(cond);
- ncl_free(loop);
- return FNORMAL;
- }
- int r = ncl_eval(ncl, ncl_string(loop), ncl_length(loop) + 1);
- switch (r) {
- case FBREAK:
- ncl_free(cond);
- ncl_free(loop);
- return FNORMAL;
- case FRETURN:
- ncl_free(cond);
- ncl_free(loop);
- return FRETURN;
- case FAGAIN:
- continue;
- case FERROR:
- ncl_free(cond);
- ncl_free(loop);
- return FERROR;
- }
- }
- }
- #ifndef TCL_DISABLE_MATH
- static int ncl_cmd_math(struct ncl *ncl, ncl_value_t *args, void *arg) {
- (void)arg;
- char buf[64];
- ncl_value_t *opval = ncl_list_at(args, 0);
- ncl_value_t *aval = ncl_list_at(args, 1);
- ncl_value_t *bval = ncl_list_at(args, 2);
- const char *op = ncl_string(opval);
- int a = ncl_int(aval);
- int b = ncl_int(bval);
- int c = 0;
- if (op[0] == '+') {
- c = a + b;
- } else if (op[0] == '-') {
- c = a - b;
- } else if (op[0] == '*') {
- c = a * b;
- } else if (op[0] == '/') {
- c = a / b;
- } else if (op[0] == '>' && op[1] == '\0') {
- c = a > b;
- } else if (op[0] == '>' && op[1] == '=') {
- c = a >= b;
- } else if (op[0] == '<' && op[1] == '\0') {
- c = a < b;
- } else if (op[0] == '<' && op[1] == '=') {
- c = a <= b;
- } else if (op[0] == '=' && op[1] == '=') {
- c = a == b;
- } else if (op[0] == '!' && op[1] == '=') {
- c = a != b;
- }
- char *p = buf + sizeof(buf) - 1;
- char neg = (c < 0);
- *p-- = 0;
- if (neg) {
- c = -c;
- }
- do {
- *p-- = '0' + (c % 10);
- c = c / 10;
- } while (c > 0);
- if (neg) {
- *p-- = '-';
- }
- p++;
- ncl_free(opval);
- ncl_free(aval);
- ncl_free(bval);
- return ncl_result(ncl, FNORMAL, ncl_alloc(p, strlen(p)));
- }
- #endif
- void ncl_init(struct ncl *ncl) {
- ncl->env = ncl_env_alloc(NULL);
- ncl->result = ncl_alloc("", 0);
- ncl->cmds = NULL;
- ncl_register(ncl, "set", ncl_cmd_set, 0, NULL);
- ncl_register(ncl, "subst", ncl_cmd_subst, 2, NULL);
- #ifndef TCL_DISABLE_PUTS
- ncl_register(ncl, "puts", ncl_cmd_puts, 2, NULL);
- #endif
- ncl_register(ncl, "proc", ncl_cmd_proc, 4, NULL);
- ncl_register(ncl, "if", ncl_cmd_if, 0, NULL);
- ncl_register(ncl, "while", ncl_cmd_while, 3, NULL);
- ncl_register(ncl, "return", ncl_cmd_flow, 0, NULL);
- ncl_register(ncl, "break", ncl_cmd_flow, 1, NULL);
- ncl_register(ncl, "continue", ncl_cmd_flow, 1, NULL);
- #ifndef TCL_DISABLE_MATH
- char *math[] = {"+", "-", "*", "/", ">", ">=", "<", "<=", "==", "!="};
- for (unsigned int i = 0; i < (sizeof(math) / sizeof(math[0])); i++) {
- ncl_register(ncl, math[i], ncl_cmd_math, 3, NULL);
- }
- #endif
- }
- void ncl_destroy(struct ncl *ncl) {
- while (ncl->env) {
- ncl->env = ncl_env_free(ncl->env);
- }
- while (ncl->cmds) {
- struct ncl_cmd *cmd = ncl->cmds;
- ncl->cmds = ncl->cmds->next;
- ncl_free(cmd->name);
- free(cmd->arg);
- free(cmd);
- }
- ncl_free(ncl->result);
- }
- #ifndef TEST
- #define CHUNK 1024
- int main() {
- struct ncl ncl;
- int buflen = CHUNK;
- char *buf = malloc(buflen);
- int i = 0;
- ncl_init(&ncl);
- while (1) {
- int inp = fgetc(stdin);
- if (i > buflen - 1) {
- buf = realloc(buf, buflen += CHUNK);
- }
- if (inp == 0 || inp == EOF) {
- break;
- }
- buf[i++] = inp;
- ncl_each(buf, i, 1) {
- if (p.token == TERROR && (p.to - buf) != i) {
- memset(buf, 0, buflen);
- i = 0;
- break;
- } else if (p.token == TCMD && *(p.from) != '\0') {
- int r = ncl_eval(&ncl, buf, strlen(buf));
- if (r != FERROR) {
- printf("result> %.*s\n", ncl_length(ncl.result),
- ncl_string(ncl.result));
- } else {
- printf("?!\n");
- }
- memset(buf, 0, buflen);
- i = 0;
- break;
- }
- }
- }
- free(buf);
- if (i) {
- printf("incomplete input\n");
- return -1;
- }
- return 0;
- }
- #endif
|