|
@@ -0,0 +1,728 @@
|
|
|
+package nncl
|
|
|
+
|
|
|
+import "fmt"
|
|
|
+import "os"
|
|
|
+import "io"
|
|
|
+import "strings"
|
|
|
+
|
|
|
+const MAX_VAR_LENGTH=256
|
|
|
+
|
|
|
+type Ncl struct {
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+func (ncl *Ncl) eval(s string) {
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+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 {
|
|
|
+
|
|
|
+ for ; isSpace(c); c,err = scan(in, line, col) {
|
|
|
+ if err != nil {
|
|
|
+ return TERROR, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if isEnd(c) {
|
|
|
+ token.WriteRune(c)
|
|
|
+ return TCMD, nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (c == '$') {
|
|
|
+ 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 == '{')) {
|
|
|
+
|
|
|
+ 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 == '}') {
|
|
|
+
|
|
|
+ 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;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+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
|