tcl解释器——C语言

注:区区做了一点点扩展,让这个小TCL能进行字符串的最最基本操作:连接和单字符提取。。。

/**//* Tcl in ~ 500 lines of code by Salvatore antirez Sanfilippo. BSD licensed */
/**//* Hacked by Joyer : add '.' and '|' command to support basic string op*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum ...{PICOL_OK, PICOL_ERR, PICOL_RETURN, PICOL_BREAK, PICOL_CONTINUE};
enum ...{PT_ESC,PT_STR,PT_CMD,PT_VAR,PT_SEP,PT_EOL,PT_EOF};

struct picolParser ...{
    char *text;
    char *p; /**//* current text position */
    int len; /**//* remaining length */
    char *start; /**//* token start */
    char *end; /**//* token end */
    int type; /**//* token type, PT_... */
    int insidequote; /**//* True if inside " " */
};

struct picolVar ...{
    char *name, *val;
    struct picolVar *next;
};

struct picolInterp; /**//* forward declaration */
typedef int (*picolCmdFunc)(struct picolInterp *i, int argc, char **argv, void *privdata);

struct picolCmd ...{
    char *name;
    picolCmdFunc func;
    void *privdata;
    struct picolCmd *next;
};

struct picolCallFrame ...{
    struct picolVar *vars;
    struct picolCallFrame *parent; /**//* parent is NULL at top level */
};

struct picolInterp ...{
    int level; /**//* Level of nesting */
    struct picolCallFrame *callframe;
    struct picolCmd *commands;
    char *result;
};

void picolInitParser(struct picolParser *p, char *text) ...{
    p->text = p->p = text;
    p->len = strlen(text);
    p->start = 0; p->end = 0; p->insidequote = 0;
    p->type = PT_EOL;
}

int picolParseSep(struct picolParser *p) ...{
    p->start = p->p;
    while(*p->p == ' ' || *p->p == ' ' || *p->p == ' ' || *p->p == ' ') ...{
        p->p++; p->len--;
    }
    p->end = p->p-1;
    p->type = PT_SEP;
    return PICOL_OK;
}

int picolParseEol(struct picolParser *p) ...{
    p->start = p->p;
    while(*p->p == ' ' || *p->p == ' ' || *p->p == ' ' || *p->p == ' ' ||
          *p->p == ';')
    ...{
        p->p++; p->len--;
    }
    p->end = p->p-1;
    p->type = PT_EOL;
    return PICOL_OK;
}

int picolParseCommand(struct picolParser *p) ...{
    int level = 1;
    int blevel = 0;
    p->start = ++p->p; p->len--;
    while (1) ...{
        if (p->len == 0) ...{
            break;
        } else if (*p->p == '[' && blevel == 0) ...{
            level++;
        } else if (*p->p == ']' && blevel == 0) ...{
            if (!--level) break;
        } else if (*p->p == '') ...{
            p->p++; p->len--;
        } else if (*p->p == '{') ...{
            blevel++;
        } else if (*p->p == '}') ...{
            if (blevel != 0) blevel--;
        }
        p->p++; p->len--;
    }
    p->end = p->p-1;
    p->type = PT_CMD;
    if (*p->p == ']') ...{
        p->p++; p->len--;
    }
    return PICOL_OK;
}

int picolParseVar(struct picolParser *p) ...{
    p->start = ++p->p; p->len--; /**//* skip the $ */
    while(1) ...{
        if ((*p->p >= 'a' && *p->p <= 'z') || (*p->p >= 'A' && *p->p <= 'Z') ||
            (*p->p >= '0' && *p->p <= '9') || *p->p == '_')
        ...{
            p->p++; p->len--; continue;
        }
        break;
    }
    if (p->start == p->p) ...{ /**//* It's just a single char string "$" */
        p->start = p->end = p->p-1;
        p->type = PT_STR;
    } else ...{
        p->end = p->p-1;
        p->type = PT_VAR;
    }
    return PICOL_OK;
}

int picolParseBrace(struct picolParser *p) ...{
    int level = 1;
    p->start = ++p->p; p->len--;
    while(1) ...{
        if (p->len >= 2 && *p->p == '') ...{
            p->p++; p->len--;
        } else if (p->len == 0 || *p->p == '}') ...{
            level--;
            if (level == 0 || p->len == 0) ...{
                p->end = p->p-1;
                if (p->len) ...{
                    p->p++; p->len--; /**//* Skip final closed brace */
                }
                p->type = PT_STR;
                return PICOL_OK;
            }
        } else if (*p->p == '{')
            level++;
        p->p++; p->len--;
    }
    return PICOL_OK; /**//* unreached */
}

int picolParseString(struct picolParser *p) ...{
    int newword = (p->type == PT_SEP || p->type == PT_EOL || p->type == PT_STR);
    if (newword && *p->p == '{') return picolParseBrace(p);
    else if (newword && *p->p == '"') ...{
        p->insidequote = 1;
        p->p++; p->len--;
    }
    p->start = p->p;
    while(1) ...{
        if (p->len == 0) ...{
            p->end = p->p-1;
            p->type = PT_ESC;
            return PICOL_OK;
        }
        switch(*p->p) ...{
        case '':
            if (p->len >= 2) ...{
                p->p++; p->len--;
            }
            break;
        case '$': case '[':
            p->end = p->p-1;
            p->type = PT_ESC;
            return PICOL_OK;
        case ' ': case ' ': case ' ': case ' ': case ';':
            if (!p->insidequote) ...{
                p->end = p->p-1;
                p->type = PT_ESC;
                return PICOL_OK;
            }
            break;
        case '"':
            if (p->insidequote) ...{
                p->end = p->p-1;
                p->type = PT_ESC;
                p->p++; p->len--;
                p->insidequote = 0;
                return PICOL_OK;
            }
            break;
        }
        p->p++; p->len--;
    }
    return PICOL_OK; /**//* unreached */
}

int picolParseComment(struct picolParser *p) ...{
    while(p->len && *p->p != ' ') ...{
        p->p++; p->len--;
    }
    return PICOL_OK;
}

int picolGetToken(struct picolParser *p) ...{
    while(1) ...{
        if (!p->len) ...{
            if (p->type != PT_EOL && p->type != PT_EOF)
                p->type = PT_EOL;
            else
                p->type = PT_EOF;
            return PICOL_OK;
        }
        switch(*p->p) ...{
        case ' ': case ' ': case ' ':
            if (p->insidequote) return picolParseString(p);
            return picolParseSep(p);
        case ' ': case ';':
            if (p->insidequote) return picolParseString(p);
            return picolParseEol(p);
        case '[':
            return picolParseCommand(p);
        case '$':
            return picolParseVar(p);
        case '#':
            if (p->type == PT_EOL) ...{
                picolParseComment(p);
                continue;
            }
            return picolParseString(p);
        default:
            return picolParseString(p);
        }
    }
    return PICOL_OK; /**//* unreached */
}

void picolInitInterp(struct picolInterp *i) ...{
    i->level = 0;
    i->callframe = malloc(sizeof(struct picolCallFrame));
    i->callframe->vars = NULL;
    i->callframe->parent = NULL;
    i->commands = NULL;
    i->result = strdup("");
}

void picolSetResult(struct picolInterp *i, char *s) ...{
    free(i->result);
    i->result = strdup(s);
}

struct picolVar *picolGetVar(struct picolInterp *i, char *name) ...{
    struct picolVar *v = i->callframe->vars;
    while(v) ...{
        if (strcmp(v->name,name) == 0) return v;
        v = v->next;
    }
    return NULL;
}

int picolSetVar(struct picolInterp *i, char *name, char *val) ...{
    struct picolVar *v = picolGetVar(i,name);
    if (v) ...{
        free(v->val);
        v->val = strdup(val);
    } else ...{
        v = malloc(sizeof(*v));
        v->name = strdup(name);
        v->val = strdup(val);
        v->next = i->callframe->vars;
        i->callframe->vars = v;
    }
    return PICOL_OK;
}

struct picolCmd *picolGetCommand(struct picolInterp *i, char *name) ...{
    struct picolCmd *c = i->commands;
    while(c) ...{
        if (strcmp(c->name,name) == 0) return c;
        c = c->next;
    }
    return NULL;
}

int picolRegisterCommand(struct picolInterp *i, char *name, picolCmdFunc f, void *privdata) ...{
    struct picolCmd *c = picolGetCommand(i,name);
    char errbuf[1024];
    if (c) ...{
        snprintf(errbuf,1024,"Command '%s' already defined",name);
        picolSetResult(i,errbuf);
        return PICOL_ERR;
    }
    c = malloc(sizeof(*c));
    c->name = strdup(name);
    c->func = f;
    c->privdata = privdata;
    c->next = i->commands;
    i->commands = c;
    return PICOL_OK;
}

/**//* EVAL! */
int picolEval(struct picolInterp *i, char *t) ...{
    struct picolParser p;
    int argc = 0, j;
    char **argv = NULL;
    char errbuf[1024];
    int retcode = PICOL_OK;
    picolSetResult(i,"");
    picolInitParser(&p,t);
    while(1) ...{
        char *t;
        int tlen;
        int prevtype = p.type;
        picolGetToken(&p);
        if (p.type == PT_EOF) break;
        tlen = p.end-p.start+1;
        if (tlen < 0) tlen = 0;
        t = malloc(tlen+1);
        memcpy(t, p.start, tlen);
        t[tlen] = '';
        if (p.type == PT_VAR) ...{
            struct picolVar *v = picolGetVar(i,t);
            if (!v) ...{
                snprintf(errbuf,1024,"No such variable '%s'",t);
                free(t);
                picolSetResult(i,errbuf);
                retcode = PICOL_ERR;
                goto err;
            }
            free(t);
            t = strdup(v->val);
        } else if (p.type == PT_CMD) ...{
            retcode = picolEval(i,t);
            free(t);
            if (retcode != PICOL_OK) goto err;
            t = strdup(i->result);
        } else if (p.type == PT_ESC) ...{
            /**//* XXX: escape handling missing! */
        } else if (p.type == PT_SEP) ...{
            prevtype = p.type;
            free(t);
            continue;
        }
        /**//* We have a complete command + args. Call it! */
        if (p.type == PT_EOL) ...{
            struct picolCmd *c;
            free(t);
            prevtype = p.type;
            if (argc) ...{
                if ((c = picolGetCommand(i,argv[0])) == NULL) ...{
                    snprintf(errbuf,1024,"No such command '%s'",argv[0]);
                    picolSetResult(i,errbuf);
                    retcode = PICOL_ERR;
                    goto err;
                }
                retcode = c->func(i,argc,argv,c->privdata);
                if (retcode != PICOL_OK) goto err;
            }
            /**//* Prepare for the next command */
            for (j = 0; j < argc; j++) free(argv[j]);
            free(argv);
            argv = NULL;
            argc = 0;
            continue;
        }
        /**//* We have a new token, append to the previous or as new arg? */
        if (prevtype == PT_SEP || prevtype == PT_EOL) ...{
            argv = realloc(argv, sizeof(char*)*(argc+1));
            argv[argc] = t;
            argc++;
        } else ...{ /**//* Interpolation */
            int oldlen = strlen(argv[argc-1]), tlen = strlen(t);
            argv[argc-1] = realloc(argv[argc-1], oldlen+tlen+1);
            memcpy(argv[argc-1]+oldlen, t, tlen);
            argv[argc-1][oldlen+tlen]='';
            free(t);
        }
        prevtype = p.type;
    }
err:
    for (j = 0; j < argc; j++) free(argv[j]);
    free(argv);
    return retcode;
}

/**//* ACTUAL COMMANDS! */
int picolArityErr(struct picolInterp *i, char *name) ...{
    char buf[1024];
    snprintf(buf,1024,"Wrong number of args for %s",name);
    picolSetResult(i,buf);
    return PICOL_ERR;
}

int picolCommandMath(struct picolInterp *i, int argc, char **argv, void *pd) ...{
  char rawbuf[1024]; int a, b, c, len; char *buf = rawbuf;
    if (argc != 3) return picolArityErr(i,argv[0]);
    a = atoi(argv[1]); b = atoi(argv[2]);
    if (argv[0][0] == '+') c = a+b;
    else if (argv[0][0] == '-') c = a-b;
    else if (argv[0][0] == '*') c = a*b;
    else if (argv[0][0] == '/') c = a/b;
    else if (argv[0][0] == '>' && argv[0][1] == '') c = a > b;
    else if (argv[0][0] == '>' && argv[0][1] == '=') c = a >= b;
    else if (argv[0][0] == '<' && argv[0][1] == '') c = a < b;
    else if (argv[0][0] == '<' && argv[0][1] == '=') c = a <= b;
    else if (argv[0][0] == '=' && argv[0][1] == '=') c = a == b;
    else if (argv[0][0] == '!' && argv[0][1] == '=') c = a != b;
    else if (argv[0][0] == '.') ...{ /**//*字符串连接*/
      len = strlen(argv[1]) + strlen(argv[2]);
      if (len>1024)...{
    buf = malloc(len+1);
    len = len+1;
      }
      else...{
    len=1024;
      }
      snprintf(buf, len, "%s%s", argv[1], argv[2]);
      picolSetResult(i,buf);
      if (buf!=rawbuf)
    free(buf);
      return PICOL_OK;
    }
    else if (argv[0][0] == '|') ...{ /**//*字符串提取*/
      snprintf(buf, 1024, "%c", argv[1][b]);
      picolSetResult(i,buf);
      return PICOL_OK;
    }
    else c = 0; /**//* I hate warnings */
    snprintf(buf,64,"%d",c);
    picolSetResult(i,buf);
    return PICOL_OK;
}

int picolCommandSet(struct picolInterp *i, int argc, char **argv, void *pd) ...{
    if (argc != 3) return picolArityErr(i,argv[0]);
    picolSetVar(i,argv[1],argv[2]);
    picolSetResult(i,argv[2]);
    return PICOL_OK;
}

int picolCommandPuts(struct picolInterp *i, int argc, char **argv, void *pd) ...{
    if (argc != 2) return picolArityErr(i,argv[0]);
    printf("%s ", argv[1]);
    return PICOL_OK;
}

int picolCommandIf(struct picolInterp *i, int argc, char **argv, void *pd) ...{
    int retcode;
    if (argc != 3 && argc != 5) return picolArityErr(i,argv[0]);
    if ((retcode = picolEval(i,argv[1])) != PICOL_OK) return retcode;
    if (atoi(i->result)) return picolEval(i,argv[2]);
    else if (argc == 5) return picolEval(i,argv[4]);
    return PICOL_OK;
}

int picolCommandWhile(struct picolInterp *i, int argc, char **argv, void *pd) ...{
    if (argc != 3) return picolArityErr(i,argv[0]);
    while(1) ...{
        int retcode = picolEval(i,argv[1]);
        if (retcode != PICOL_OK) return retcode;
        if (atoi(i->result)) ...{
            if ((retcode = picolEval(i,argv[2])) == PICOL_CONTINUE) continue;
            else if (retcode == PICOL_OK) continue;
            else if (retcode == PICOL_BREAK) return PICOL_OK;
            else return retcode;
        } else ...{
            return PICOL_OK;
        }
    }
}

int picolCommandRetCodes(struct picolInterp *i, int argc, char **argv, void *pd) ...{
    if (argc != 1) return picolArityErr(i,argv[0]);
    if (strcmp(argv[0],"break") == 0) return PICOL_BREAK;
    else if (strcmp(argv[0],"continue") == 0) return PICOL_CONTINUE;
    return PICOL_OK;
}

void picolDropCallFrame(struct picolInterp *i) ...{
    struct picolCallFrame *cf = i->callframe;
    struct picolVar *v = cf->vars, *t;
    while(v) ...{
        t = v->next;
        free(v->name);
        free(v->val);
        free(v);
        v = t;
    }
    i->callframe = cf->parent;
    free(cf);
}

int picolCommandCallProc(struct picolInterp *i, int argc, char **argv, void *pd) ...{
    char **x=pd, *alist=x[0], *body=x[1], *p=strdup(alist), *tofree;
    struct picolCallFrame *cf = malloc(sizeof(*cf));
    int arity = 0, done = 0, errcode = PICOL_OK;
    char errbuf[1024];
    cf->vars = NULL;
    cf->parent = i->callframe;
    i->callframe = cf;
    tofree = p;
    while(1) ...{
        char *start = p;
        while(*p != ' ' && *p != '') p++;
        if (*p != '' && p == start) ...{
            p++; continue;
        }
        if (p == start) break;
        if (*p == '') done=1; else *p = '';
        if (++arity > argc-1) goto arityerr;
        picolSetVar(i,start,argv[arity]);
        p++;
        if (done) break;
    }
    free(tofree);
    if (arity != argc-1) goto arityerr;
    errcode = picolEval(i,body);
    if (errcode == PICOL_RETURN) errcode = PICOL_OK;
    picolDropCallFrame(i); /**//* remove the called proc callframe */
    return errcode;
arityerr:
    snprintf(errbuf,1024,"Proc '%s' called with wrong arg num",argv[0]);
    picolSetResult(i,errbuf);
    picolDropCallFrame(i); /**//* remove the called proc callframe */
    return PICOL_ERR;
}

int picolCommandProc(struct picolInterp *i, int argc, char **argv, void *pd) ...{
    char **procdata = malloc(sizeof(char*)*2);
    if (argc != 4) return picolArityErr(i,argv[0]);
    procdata[0] = strdup(argv[2]); /**//* arguments list */
    procdata[1] = strdup(argv[3]); /**//* procedure body */
    return picolRegisterCommand(i,argv[1],picolCommandCallProc,procdata);
}

int picolCommandReturn(struct picolInterp *i, int argc, char **argv, void *pd) ...{
    if (argc != 1 && argc != 2) return picolArityErr(i,argv[0]);
    picolSetResult(i, (argc == 2) ? argv[1] : "");
    return PICOL_RETURN;
}

void picolRegisterCoreCommands(struct picolInterp *i) ...{
  int j; char *name[] = ...{"+","-","*","/",">",">=","<","<=","==","!=",".","|"};
    for (j = 0; j < (int)(sizeof(name)/sizeof(char*)); j++)
        picolRegisterCommand(i,name[j],picolCommandMath,NULL);
    picolRegisterCommand(i,"set",picolCommandSet,NULL);
    picolRegisterCommand(i,"puts",picolCommandPuts,NULL);
    picolRegisterCommand(i,"if",picolCommandIf,NULL);
    picolRegisterCommand(i,"while",picolCommandWhile,NULL);
    picolRegisterCommand(i,"break",picolCommandRetCodes,NULL);
    picolRegisterCommand(i,"continue",picolCommandRetCodes,NULL);
    picolRegisterCommand(i,"proc",picolCommandProc,NULL);
    picolRegisterCommand(i,"return",picolCommandReturn,NULL);
}

int main(int argc, char **argv) ...{
    struct picolInterp interp;
    picolInitInterp(&interp);
    picolRegisterCoreCommands(&interp);
    if (argc == 1) ...{
        while(1) ...{
            char clibuf[1024];
            int retcode;
            printf("picol> "); fflush(stdout);
            if (fgets(clibuf,1024,stdin) == NULL) return 0;
            retcode = picolEval(&interp,clibuf);
            if (interp.result[0] != '')
                printf("[%d] %s ", retcode, interp.result);
        }
    } else if (argc == 2) ...{
        char buf[1024*16];
        FILE *fp = fopen(argv[1],"r");
        if (!fp) ...{
            perror("open"); exit(1);
        }
        buf[fread(buf,1,1024*16,fp)] = '';
        fclose(fp);
        if (picolEval(&interp,buf) != PICOL_OK) printf("%s ", interp.result);
    }
    return 0;
}
posted on 2012-07-27 16:55  百万军中  阅读(1144)  评论(0编辑  收藏  举报