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