├── Makefile ├── code.c ├── code.h ├── error.h ├── eval.c ├── eval.h ├── filesys.ex.c ├── filesys.ex.h ├── func.c ├── func.h ├── lexer.c ├── lexer.h ├── main.c ├── mempool.c ├── mempool.h ├── number.c ├── number.h ├── parser.c ├── parser.h ├── parser.y ├── proto.array.c ├── proto.array.h ├── proto.c ├── proto.h ├── proto.number.c ├── proto.number.h ├── proto.string.c ├── proto.string.h ├── pstate.c ├── pstate.h ├── rbtree.c ├── rbtree.h ├── regexp.c ├── regexp.h ├── samples ├── 99bottles.ss ├── brainfuck.ss └── contchkcmp.ss ├── scope.c ├── scope.h ├── testsuit ├── 99.ss ├── arg.ss ├── arg2.ss ├── arguments.ss ├── argumentshared.ss ├── bin │ └── runtest.sh ├── block.ss ├── block2.ss ├── callee2.ss ├── delete.ss ├── do.ss ├── eval.ss ├── evalthrow.ss ├── expr.ss ├── ffi.ss ├── fib.ss ├── file.ss ├── forin.ss ├── forinstack.ss ├── func.ss ├── grep.ss ├── invoketime.ss ├── io.ss ├── jiechen.ss ├── labeled.ss ├── local.ss ├── obj.ss ├── person1.ss ├── person2.ss ├── prob │ ├── callee.ss │ └── withscope.ss ├── prototypes.ss ├── ref.ss ├── regex.ss ├── scope.ss ├── switch.ss ├── switch2.ss ├── testapply.ss ├── testarray.ss ├── testcall.ss ├── testeval.ss ├── testeval2.ss ├── testexcpt.ss ├── testexcpt2.ss ├── testexcpt3.ss ├── testexcpt4.ss ├── testexcpt5.ss ├── teststring.ss ├── testthis.ss ├── testthis2.ss ├── testthis3.ss ├── while.ss ├── with.ss └── yhsj.ss ├── unichar.c ├── unichar.h ├── utils.c ├── utils.h ├── value.c └── value.h /Makefile: -------------------------------------------------------------------------------- 1 | ACFILES = parser.c 2 | CFILES = $(ACFILES) lexer.c code.c eval.c func.c value.c regexp.c main.c pstate.c \ 3 | rbtree.c scope.c utils.c proto.c filesys.ex.c unichar.c proto.string.c \ 4 | number.c proto.number.c proto.array.c mempool.c 5 | OBJS = $(CFILES:.c=.o) 6 | DEFIN = -DUSE_FILESYS_EX 7 | CFLAGS = -O2 -Wall -Werror -ansi $(DEFIN) 8 | YACC = bison -v 9 | TARGET = quadwheel 10 | 11 | .PHONY: all clean cleanall 12 | 13 | all: $(OBJS) 14 | $(CC) $(CFLAGS) $(OBJS) -o $(TARGET) -lm 15 | 16 | debug: $(OBJS) 17 | $(CC) -g -Wall -DDONT_USE_POOL $(DEFIN) $(CFILES) -o $(TARGET) -lm 18 | 19 | stepdebug: $(OBJS) 20 | $(CC) -g -Wall -DDEBUG $(DEFIN) $(CFILES) -o $(TARGET) -lm 21 | 22 | parser.c: parser.y 23 | $(YACC) -oparser.c -d parser.y 24 | 25 | 26 | clean: 27 | rm -f *.o *.output *.stackdump $(TARGET) 28 | 29 | cleanall: 30 | rm -f $(ACFILES) *.o parser.h *.output *.stackdump $(TARGET) 31 | -------------------------------------------------------------------------------- /code.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "code.h" 6 | 7 | const char *op_names[OP_LASTOP] = { 8 | "NOP", 9 | "PUSHNUM", 10 | "PUSHSTR", 11 | "PUSHVAR", 12 | "PUSHUND", 13 | "PUSHBOO", 14 | "PUSHFUN", 15 | "PUSHREG", 16 | "PUSHARG", 17 | "PUSHTHS", 18 | "PUSHTOP", 19 | "PUSHTOP2", 20 | "UNREF", 21 | "POP", 22 | "LOCAL", 23 | "NEG", 24 | "POS", 25 | "NOT", 26 | "BNOT", 27 | "ADD", 28 | "SUB", 29 | "MUL", 30 | "DIV", 31 | "MOD", 32 | "LESS", 33 | "GREATER", 34 | "LESSEQU", 35 | "GREATEREQU", 36 | "EQUAL", 37 | "NOTEQUAL", 38 | "STRICTEQU", 39 | "STRICTNEQ", 40 | "BAND", 41 | "BOR", 42 | "BXOR", 43 | "SHF", 44 | "ASSIGN", 45 | "SUBSCRIPT", 46 | "INC", 47 | "DEC", 48 | "KEY", 49 | "NEXT", 50 | "JTRUE", 51 | "JFALSE", 52 | "JTRUE_NP", 53 | "JFALSE_NP", 54 | "JMP", 55 | "JMPPOP", 56 | "FCALL", 57 | "NEWFCALL", 58 | "RET", 59 | "DELETE", 60 | "CHTHIS", 61 | "OBJECT", 62 | "ARRAY", 63 | "EVAL", 64 | "STRY", 65 | "ETRY", 66 | "SCATCH", 67 | "ECATCH", 68 | "SFINAL", 69 | "EFINAL", 70 | "THROW", 71 | "WITH", 72 | "EWITH", 73 | "RESERVED", 74 | "DEBUG", 75 | }; 76 | 77 | OpCodes *codes_new(int size) 78 | { 79 | OpCodes *ret = malloc(sizeof(OpCodes)); 80 | memset(ret, 0, sizeof(OpCodes)); 81 | ret->codes = malloc(sizeof(OpCode) * size); 82 | ret->code_size = size; 83 | return ret; 84 | } 85 | 86 | int codes_insert(OpCodes *c, Eopcode code, void *extra) 87 | { 88 | if (c->code_size - c->code_len <= 0) { 89 | c->code_size += 100; 90 | c->codes = realloc(c->codes, c->code_size * sizeof(OpCode)); 91 | } 92 | c->codes[c->code_len].op = code; 93 | c->codes[c->code_len].data = extra; 94 | c->code_len ++; 95 | return 0; 96 | } 97 | 98 | OpCodes *codes_join(OpCodes *a, OpCodes *b) 99 | { 100 | OpCodes *ret = codes_new(a->code_len + b->code_len); 101 | memcpy(ret->codes, a->codes, a->code_len * sizeof(OpCode)); 102 | memcpy(&ret->codes[a->code_len], b->codes, b->code_len * sizeof(OpCode)); 103 | ret->code_size = a->code_len + b->code_len; 104 | ret->code_len = ret->code_size; 105 | ret->expr_counter = a->expr_counter + b->expr_counter; 106 | free(a->codes); 107 | free(b->codes); 108 | free(a); 109 | free(b); 110 | return ret; 111 | } 112 | 113 | OpCodes *codes_join3(OpCodes *a, OpCodes *b, OpCodes *c) 114 | { 115 | return codes_join(codes_join(a, b), c); 116 | } 117 | 118 | OpCodes *codes_join4(OpCodes *a, OpCodes *b, OpCodes *c, OpCodes *d) 119 | { 120 | return codes_join(codes_join(a, b), codes_join(c, d)); 121 | } 122 | 123 | #define NEW_CODES(code, extra) do { \ 124 | OpCodes *r = codes_new(3); \ 125 | codes_insert(r, (code), (void *)(extra)); \ 126 | return r; \ 127 | } while(0) 128 | 129 | OpCodes *code_push_undef() { NEW_CODES(OP_PUSHUND, 0); } 130 | OpCodes *code_push_bool(int v) { NEW_CODES(OP_PUSHBOO, v); } 131 | OpCodes *code_push_num(double *v) { NEW_CODES(OP_PUSHNUM, v); } 132 | OpCodes *code_push_string(const unichar *str) { NEW_CODES(OP_PUSHSTR, str); } 133 | OpCodes *code_push_index(unichar *varname) 134 | { 135 | FastVar *n = malloc(sizeof(FastVar)); 136 | memset(n, 0, sizeof(FastVar)); 137 | n->context_id = -1; 138 | n->var.varname = varname; 139 | NEW_CODES(OP_PUSHVAR, n); 140 | } 141 | OpCodes *code_push_this() { NEW_CODES(OP_PUSHTHS, 0); } 142 | OpCodes *code_push_top() { NEW_CODES(OP_PUSHTOP, 0); } 143 | OpCodes *code_push_top2() { NEW_CODES(OP_PUSHTOP2, 0); } 144 | OpCodes *code_unref() { NEW_CODES(OP_UNREF, 0); } 145 | OpCodes *code_push_args() { NEW_CODES(OP_PUSHARG, 0); } 146 | OpCodes *code_push_func(struct Func *fun) { NEW_CODES(OP_PUSHFUN, fun); } 147 | OpCodes *code_push_regex(regex_t *reg) { NEW_CODES(OP_PUSHREG, reg); } 148 | 149 | OpCodes *code_local(const unichar *varname) { NEW_CODES(OP_LOCAL, varname); } 150 | 151 | OpCodes *code_nop() { NEW_CODES(OP_NOP, 0); } 152 | OpCodes *code_neg() { NEW_CODES(OP_NEG, 0); } 153 | OpCodes *code_pos() { NEW_CODES(OP_POS, 0); } 154 | OpCodes *code_bnot() { NEW_CODES(OP_BNOT, 0); } 155 | OpCodes *code_not() { NEW_CODES(OP_NOT, 0); } 156 | OpCodes *code_mul() { NEW_CODES(OP_MUL, 0); } 157 | OpCodes *code_div() { NEW_CODES(OP_DIV, 0); } 158 | OpCodes *code_mod() { NEW_CODES(OP_MOD, 0); } 159 | OpCodes *code_add() { NEW_CODES(OP_ADD, 0); } 160 | OpCodes *code_sub() { NEW_CODES(OP_SUB, 0); } 161 | OpCodes *code_less() { NEW_CODES(OP_LESS, 0); } 162 | OpCodes *code_greater() { NEW_CODES(OP_GREATER, 0); } 163 | OpCodes *code_lessequ() { NEW_CODES(OP_LESSEQU, 0); } 164 | OpCodes *code_greaterequ() { NEW_CODES(OP_GREATEREQU, 0); } 165 | OpCodes *code_equal() { NEW_CODES(OP_EQUAL, 0); } 166 | OpCodes *code_notequal() { NEW_CODES(OP_NOTEQUAL, 0); } 167 | OpCodes *code_eequ() { NEW_CODES(OP_STRICTEQU, 0); } 168 | OpCodes *code_nneq() { NEW_CODES(OP_STRICTNEQ, 0); } 169 | OpCodes *code_band() { NEW_CODES(OP_BAND, 0); } 170 | OpCodes *code_bor() { NEW_CODES(OP_BOR, 0); } 171 | OpCodes *code_bxor() { NEW_CODES(OP_BXOR, 0); } 172 | OpCodes *code_shf(int right) { NEW_CODES(OP_SHF, right); } 173 | OpCodes *code_assign(int h) { NEW_CODES(OP_ASSIGN, h); } 174 | OpCodes *code_subscript(int right_val) { NEW_CODES(OP_SUBSCRIPT, right_val); } 175 | OpCodes *code_inc(int e) { NEW_CODES(OP_INC, e); } 176 | OpCodes *code_dec(int e) { NEW_CODES(OP_DEC, e); } 177 | 178 | OpCodes *code_fcall(int argc) { NEW_CODES(OP_FCALL, argc); } 179 | OpCodes *code_newfcall(int argc) { NEW_CODES(OP_NEWFCALL, argc); } 180 | OpCodes *code_ret(int n) { NEW_CODES(OP_RET, n); } 181 | OpCodes *code_delete(int n) { NEW_CODES(OP_DELETE, n); } 182 | OpCodes *code_chthis(int n) { NEW_CODES(OP_CHTHIS, n); } 183 | OpCodes *code_pop(int n) { NEW_CODES(OP_POP, n); } 184 | OpCodes *code_jfalse(int off) { NEW_CODES(OP_JFALSE, off); } 185 | OpCodes *code_jtrue(int off) { NEW_CODES(OP_JTRUE, off); } 186 | OpCodes *code_jfalse_np(int off) { NEW_CODES(OP_JFALSE_NP, off); } 187 | OpCodes *code_jtrue_np(int off) { NEW_CODES(OP_JTRUE_NP, off); } 188 | OpCodes *code_jmp(int off) { NEW_CODES(OP_JMP, off); } 189 | OpCodes *code_object(int c) { NEW_CODES(OP_OBJECT, c); } 190 | OpCodes *code_array(int c) { NEW_CODES(OP_ARRAY, c); } 191 | OpCodes *code_key() { NEW_CODES(OP_KEY, 0); } 192 | OpCodes *code_next() { NEW_CODES(OP_NEXT, 0); } 193 | 194 | OpCodes *code_eval(int argc) { NEW_CODES(OP_EVAL, argc); } 195 | 196 | OpCodes *code_stry(int trylen, int catchlen, int finlen) 197 | { 198 | TryInfo *ti = malloc(sizeof(TryInfo)); 199 | ti->trylen = trylen; 200 | ti->catchlen = catchlen; 201 | ti->finallen = finlen; 202 | NEW_CODES(OP_STRY, ti); 203 | } 204 | OpCodes *code_etry() { NEW_CODES(OP_ETRY, 0); } 205 | OpCodes *code_scatch(const unichar *var) { NEW_CODES(OP_SCATCH, var); } 206 | OpCodes *code_ecatch() { NEW_CODES(OP_ECATCH, 0); } 207 | OpCodes *code_sfinal() { NEW_CODES(OP_SFINAL, 0); } 208 | OpCodes *code_efinal() { NEW_CODES(OP_EFINAL, 0); } 209 | OpCodes *code_throw() { NEW_CODES(OP_THROW, 0); } 210 | OpCodes *code_with(int withlen) { NEW_CODES(OP_WITH, withlen); } 211 | OpCodes *code_ewith() { NEW_CODES(OP_EWITH, 0); } 212 | 213 | OpCodes *code_debug() { NEW_CODES(OP_DEBUG, 0); } 214 | OpCodes *code_reserved(int type, unichar *id) 215 | { 216 | ReservedInfo *ri = malloc(sizeof(ReservedInfo)); 217 | ri->type = type; 218 | ri->label = id; 219 | ri->topop = 0; 220 | NEW_CODES(OP_RESERVED, ri); 221 | } 222 | 223 | JmpPopInfo *jpinfo_new(int off, int topop) 224 | { 225 | JmpPopInfo *r = malloc(sizeof(JmpPopInfo)); 226 | r->off = off; 227 | r->topop = topop; 228 | return r; 229 | } 230 | 231 | void code_reserved_replace(OpCodes *ops, int step_len, int break_only, 232 | const unichar *desire_label, int topop) 233 | { 234 | int i; 235 | for (i = 0; i < ops->code_len; ++i) { 236 | if (ops->codes[i].op != OP_RESERVED) continue; 237 | ReservedInfo *ri = ops->codes[i].data; 238 | 239 | if (ri->label) { 240 | if (!desire_label || unistrcmp(ri->label, desire_label) != 0) { 241 | ri->topop += topop; 242 | continue; 243 | } 244 | } 245 | 246 | if (ri->type == RES_CONTINUE) { 247 | if (break_only) { 248 | ri->topop += topop; 249 | continue; 250 | } else { 251 | int topop = ri->topop; 252 | free(ri); /* kill reserved info, replace with other opcode */ 253 | if (topop) { 254 | ops->codes[i].data = jpinfo_new(ops->code_len - i, topop); 255 | ops->codes[i].op = OP_JMPPOP; 256 | } else { 257 | ops->codes[i].data = (void *)(ops->code_len - i); 258 | ops->codes[i].op = OP_JMP; 259 | } 260 | } 261 | } else if (ri->type == RES_BREAK) { 262 | int topop = ri->topop; 263 | free(ri); 264 | if (topop) { 265 | ops->codes[i].data = jpinfo_new(step_len + ops->code_len - i, topop); 266 | ops->codes[i].op = OP_JMPPOP; 267 | } else { 268 | ops->codes[i].data = (void *)(step_len + ops->code_len - i); 269 | ops->codes[i].op = OP_JMP; 270 | } 271 | } 272 | } 273 | } 274 | 275 | void code_decode(OpCode *op, int currentip) 276 | { 277 | if (op->op < 0 || op->op >= OP_LASTOP) { 278 | printf("Bad opcode[%d] at %d\n", op->op, currentip); 279 | } 280 | printf("%d:\t%s", currentip, op_names[op->op]); 281 | if (op->op == OP_PUSHBOO || op->op == OP_FCALL || op->op == OP_EVAL || 282 | op->op == OP_POP || op->op == OP_ASSIGN || 283 | op->op == OP_RET || op->op == OP_NEWFCALL || 284 | op->op == OP_DELETE || op->op == OP_CHTHIS || 285 | op->op == OP_OBJECT || op->op == OP_ARRAY || 286 | op->op == OP_SHF || 287 | op->op == OP_INC || op->op == OP_DEC) printf("\t%d\n", (int)op->data); 288 | else if (op->op == OP_PUSHNUM) printf("\t%g\n", *((double *)op->data)); 289 | else if (op->op == OP_PUSHSTR || op->op == OP_LOCAL || 290 | op->op == OP_SCATCH) printf("\t\"%s\"\n", tochars(op->data ? op->data:"(NoCatch)")); 291 | else if (op->op == OP_PUSHVAR) printf("\tvar: \"%s\"\n", tochars(((FastVar *)op->data)->var.varname)); 292 | else if (op->op == OP_PUSHFUN) printf("\tfunc: 0x%x\n", (int)op->data); 293 | else if (op->op == OP_JTRUE || op->op == OP_JFALSE || 294 | op->op == OP_JTRUE_NP || op->op == OP_JFALSE_NP || 295 | op->op == OP_JMP) printf("\t{%d}\t#%d\n", (int)op->data, currentip + (int)op->data); 296 | else if (op->op == OP_JMPPOP) { 297 | JmpPopInfo *jp = op->data; 298 | printf("\t{%d},%d\t#%d\n", jp->off, jp->topop, currentip + jp->off); 299 | } 300 | else if (op->op == OP_STRY) { 301 | TryInfo *t = (TryInfo *)op->data; 302 | printf("\t{try:%d, catch:%d, final:%d}\n", t->trylen, t->catchlen, t->finallen); 303 | } else printf("\n"); 304 | } 305 | 306 | void codes_free(OpCodes *ops) 307 | { 308 | /* TODO*/ 309 | free(ops->codes); 310 | free(ops); 311 | } 312 | 313 | void codes_print(OpCodes *ops) 314 | { 315 | int i = 0; 316 | OpCode *opcodes = ops->codes; 317 | int opcodesi = ops->code_len; 318 | 319 | printf("opcodes count = %d\n", opcodesi); 320 | 321 | while(i < opcodesi) { 322 | code_decode(&opcodes[i], i); 323 | i++; 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /code.h: -------------------------------------------------------------------------------- 1 | #ifndef __CODE_H__ 2 | #define __CODE_H__ 3 | 4 | #include 5 | 6 | #include "unichar.h" 7 | 8 | /* stack change */ 9 | /* 0 nothing change */ 10 | /* +1 push */ 11 | /* -1 pop */ 12 | typedef enum { /* SC type of data comment */ 13 | OP_NOP, /* 0 */ 14 | OP_PUSHNUM, /* +1 *double number */ 15 | OP_PUSHSTR, /* +1 *unichar string */ 16 | OP_PUSHVAR, /* +1 *FastVar variable name */ 17 | OP_PUSHUND, /* +1 - undefined */ 18 | OP_PUSHBOO, /* +1 int bool */ 19 | OP_PUSHFUN, /* +1 *Func function */ 20 | OP_PUSHREG, /* +1 *regex_t regex */ 21 | OP_PUSHARG, /* +1 - push arguments(cur scope) */ 22 | OP_PUSHTHS, /* +1 - push this */ 23 | OP_PUSHTOP, /* +1 - duplicate top */ 24 | OP_PUSHTOP2, /* +2 - duplicate toq and top */ 25 | OP_UNREF, /* 0 - make top be right value */ 26 | OP_POP, /* -n int pop n elements */ 27 | OP_LOCAL, /* 0 *unichar add a var to current scope */ 28 | OP_NEG, /* 0 - make top = - top */ 29 | OP_POS, /* 0 - make top = + top, (conv to number) */ 30 | OP_NOT, /* 0 - reserve top */ 31 | OP_BNOT, /* 0 - bitwise not */ 32 | OP_ADD, /* -1 - all math opr pop 2 elem from stack, */ 33 | OP_SUB, /* -1 - calc and push back in to the stack */ 34 | OP_MUL, /* -1 - */ 35 | OP_DIV, /* -1 - */ 36 | OP_MOD, /* -1 - */ 37 | OP_LESS, /* -1 - logical opr, same as math opr */ 38 | OP_GREATER, /* -1 - */ 39 | OP_LESSEQU, /* -1 - */ 40 | OP_GREATEREQU, /* -1 - */ 41 | OP_EQUAL, /* -1 - */ 42 | OP_NOTEQUAL, /* -1 - */ 43 | OP_STRICTEQU, /* -1 - */ 44 | OP_STRICTNEQ, /* -1 - */ 45 | OP_BAND, /* -1 - bitwise and */ 46 | OP_BOR, /* -1 - bitwise or */ 47 | OP_BXOR, /* -1 - bitwise xor */ 48 | OP_SHF, /* -1 int(right) signed shift left or shift right */ 49 | 50 | OP_ASSIGN, /* -n int if n = 1, assign to lval, */ 51 | /* n = 2, assign to object member */ 52 | OP_SUBSCRIPT, /* -1 - do subscript TOQ[TOP] */ 53 | OP_INC, /* 0 int data indicate prefix/postfix inc/dec */ 54 | OP_DEC, /* 0 int */ 55 | OP_KEY, /* +1 - push an iter object that contain all key in top */ 56 | OP_NEXT, /* -1 - assign next key to top, make top be res of this opr */ 57 | OP_JTRUE, /* -1 int jmp to offset if top is true, */ 58 | OP_JFALSE, /* -1 int jmp to offset if top is false, */ 59 | OP_JTRUE_NP, /* 0 int jtrue no pop version */ 60 | OP_JFALSE_NP, /* 0 int jfalse no pop version */ 61 | OP_JMP, /* 0 int jmp to offset */ 62 | OP_JMPPOP, /* -n *JmpPopInfo jmp to offset with pop n */ 63 | OP_FCALL, /* -n+1 int call func with n args, pop then, make ret to be top */ 64 | OP_NEWFCALL, /* -n+1 int same as fcall, call as a constructor */ 65 | OP_RET, /* -n int n = 0|1, return with arg */ 66 | OP_DELETE, /* -n int n = 1, delete var, n = 2, delete object member */ 67 | OP_CHTHIS, /* 0, - make toq as new 'this' */ 68 | OP_OBJECT, /* -n*2+1 int create object from stack, and push back in */ 69 | OP_ARRAY, /* -n+1 int create array object from stack, and push back in */ 70 | OP_EVAL, /* -n+1 int eval can not be assign to other var */ 71 | OP_STRY, /* 0 *TryInfo push try statment poses info to trylist */ 72 | OP_ETRY, /* 0 - end of try block, jmp to finally */ 73 | OP_SCATCH, /* 0 *unichar create new scope, assign to current excption */ 74 | OP_ECATCH, /* 0 - jmp to finally */ 75 | OP_SFINAL, /* 0 - restore scope chain create by Scatch */ 76 | OP_EFINAL, /* 0 - end of finally, any unfinish code in catch, do it */ 77 | OP_THROW, /* 0 - make top be last exception, pop trylist till catched*/ 78 | OP_WITH, /* -1 - make top be top of scopechain, add to trylist */ 79 | OP_EWITH, /* 0 - pop trylist */ 80 | OP_RESERVED, /* 0 ReservedInfo* reserved, be replaced by iterstat by jmp/jmppop */ 81 | OP_DEBUG, /* 0 - DEBUG OPCODE, output top */ 82 | OP_LASTOP /* 0 - END OF OPCODE */ 83 | } Eopcode; 84 | 85 | #define RES_CONTINUE 1 86 | #define RES_BREAK 2 87 | 88 | extern const char *op_names[OP_LASTOP]; 89 | 90 | typedef struct { 91 | Eopcode op; 92 | void *data; 93 | } OpCode; 94 | 95 | typedef struct OpCodes { 96 | OpCode *codes; 97 | int code_len; 98 | int code_size; 99 | 100 | int expr_counter; /* context related expr count */ 101 | int lvalue_flag; /* left value count/flag */ 102 | const unichar *lvalue_name; /* left value name */ 103 | } OpCodes; 104 | 105 | struct Func; 106 | struct Value; 107 | 108 | typedef struct FastVar { 109 | int context_id; 110 | struct { 111 | unichar *varname; 112 | struct Value *lval; 113 | } var; 114 | } FastVar; 115 | 116 | typedef struct TryInfo { 117 | int trylen; 118 | int catchlen; 119 | int finallen; 120 | } TryInfo; 121 | 122 | typedef struct ReservedInfo { 123 | int type; 124 | const unichar *label; 125 | int topop; 126 | } ReservedInfo; 127 | 128 | typedef struct JmpPopInfo { 129 | int off; 130 | int topop; 131 | } JmpPopInfo; 132 | 133 | OpCodes *code_push_undef(); 134 | OpCodes *code_push_bool(int v); 135 | OpCodes *code_push_num(double *v); 136 | OpCodes *code_push_string(const unichar *str); 137 | OpCodes *code_push_index(unichar *varname); 138 | OpCodes *code_push_args(); 139 | OpCodes *code_push_this(); 140 | OpCodes *code_push_func(struct Func *fun); 141 | OpCodes *code_push_regex(regex_t *reg); 142 | OpCodes *code_push_top(); 143 | OpCodes *code_push_top2(); 144 | OpCodes *code_unref(); 145 | OpCodes *code_local(const unichar *varname); 146 | 147 | OpCodes *code_nop(); 148 | OpCodes *code_neg(); 149 | OpCodes *code_pos(); 150 | OpCodes *code_bnot(); 151 | OpCodes *code_not(); 152 | OpCodes *code_mul(); 153 | OpCodes *code_div(); 154 | OpCodes *code_mod(); 155 | OpCodes *code_add(); 156 | OpCodes *code_sub(); 157 | OpCodes *code_less(); 158 | OpCodes *code_greater(); 159 | OpCodes *code_lessequ(); 160 | OpCodes *code_greaterequ(); 161 | OpCodes *code_equal(); 162 | OpCodes *code_notequal(); 163 | OpCodes *code_eequ(); 164 | OpCodes *code_nneq(); 165 | OpCodes *code_band(); 166 | OpCodes *code_bor(); 167 | OpCodes *code_bxor(); 168 | OpCodes *code_shf(int right); 169 | 170 | OpCodes *code_assign(int h); 171 | OpCodes *code_subscript(int right_val); 172 | OpCodes *code_inc(int e); 173 | OpCodes *code_dec(int e); 174 | 175 | OpCodes *code_fcall(int argc); 176 | OpCodes *code_newfcall(int argc); 177 | OpCodes *code_pop(int n); 178 | OpCodes *code_ret(int n); 179 | OpCodes *code_object(int c); 180 | OpCodes *code_array(int c); 181 | OpCodes *code_key(); 182 | OpCodes *code_next(); 183 | OpCodes *code_delete(int n); 184 | OpCodes *code_chthis(int n); 185 | 186 | OpCodes *code_jfalse(int off); 187 | OpCodes *code_jtrue(int off); 188 | OpCodes *code_jfalse_np(int off); 189 | OpCodes *code_jtrue_np(int off); 190 | OpCodes *code_jmp(int off); 191 | OpCodes *code_eval(int argc); 192 | 193 | OpCodes *code_throw(); 194 | OpCodes *code_stry(int trylen, int catchlen, int finlen); 195 | OpCodes *code_etry(); 196 | OpCodes *code_scatch(const unichar *var); 197 | OpCodes *code_ecatch(); 198 | OpCodes *code_sfinal(); 199 | OpCodes *code_efinal(); 200 | OpCodes *code_throw(); 201 | OpCodes *code_with(int withlen); 202 | OpCodes *code_ewith(); 203 | 204 | OpCodes *code_debug(); 205 | OpCodes *code_reserved(int type, unichar *id); 206 | 207 | OpCodes *codes_join(OpCodes *a, OpCodes *b); 208 | OpCodes *codes_join3(OpCodes *a, OpCodes *b, OpCodes *c); 209 | OpCodes *codes_join4(OpCodes *a, OpCodes *b, OpCodes *c, OpCodes *d); 210 | 211 | /* replace continue/break(coded as OP_RESERVED) jmp 212 | * |------------------| \ 213 | * | | \\ where 'continue' jmp (jmp to step code) 214 | * | ops | / 215 | * | | / \ 216 | * |------------------| \ where 'break' jmp (jmp after step code) 217 | * | | / 218 | * | step | / 219 | * | | / 220 | * |------------------| / 221 | * 1. break_only used only in swith 222 | * 2. desire_label, only replace if current iter statement has the same label with opcode 223 | * 3. topop, if not replace in current iter statment, make sure when jmp out of this loop/switch 224 | * corrent stack elems poped(for in always has 2 elem, while switch has 1) 225 | */ 226 | void code_reserved_replace(OpCodes *ops, int step_len, int break_only, 227 | const unichar *desire_label, int topop); 228 | 229 | void code_decode(OpCode *op, int currentip); 230 | void codes_print(OpCodes *ops); 231 | OpCodes *codes_new(int size); 232 | void codes_free(OpCodes *ops); 233 | #endif 234 | -------------------------------------------------------------------------------- /error.h: -------------------------------------------------------------------------------- 1 | #ifndef __ERROR_H__ 2 | #define __ERROR_H__ 3 | 4 | #define die(format,args...) \ 5 | do { fprintf(stderr, "[Fatal] "format, ##args); exit(1); }while(0) 6 | 7 | #define warn(format,args...) \ 8 | do { fprintf(stderr, "[Warning:%s:%d] "format, __FILE__, __LINE__, ##args); }while(0) 9 | 10 | #define info(format,args...) \ 11 | do { fprintf(stderr, "[Info:%s:%d] "format, __FILE__, __LINE__, ##args); }while(0) 12 | 13 | #define bug(format,args...) \ 14 | do { fprintf(stderr, "[Bug:%s:%d] "format"\nplease contact wenxichang@163.com\n", \ 15 | __FILE__, __LINE__, ##args); exit(1); }while(0) 16 | 17 | #define todo(format,args...) \ 18 | do { fprintf(stderr, "[TODO:%s:%d] "format"\nunfinish version, sorry\n", \ 19 | __FILE__, __LINE__, ##args); exit(1); }while(0) 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /eval.h: -------------------------------------------------------------------------------- 1 | #ifndef __EVAL_H__ 2 | #define __EVAL_H__ 3 | 4 | #include "code.h" 5 | 6 | struct ScopeChain; 7 | struct Value; 8 | struct PSTATE; 9 | 10 | /* excute opcodes 11 | * 1. ps, program execution context 12 | * 2. opcodes, codes to be executed 13 | * 3. scope, current scopechain, not include current scope 14 | * 4. currentScope, current scope 15 | * 5. _this, where 'this' indicated 16 | * 6. vret, return value 17 | */ 18 | int eval(struct PSTATE *ps, OpCodes *opcodes, 19 | struct ScopeChain *scope, struct Value *currentScope, 20 | struct Value *_this, 21 | struct Value *vret); 22 | 23 | void eval_print(struct PSTATE *ps); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /filesys.ex.c: -------------------------------------------------------------------------------- 1 | /* 2 | * file system extern. 3 | * File - Constructor of File Object 4 | * .prototype - prototype of File 5 | * .prototype.open - open file 6 | * .prototype.close - close file 7 | * .prototype.eof - end of file 8 | * .prototype.gets - read line 9 | */ 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "pstate.h" 16 | #include "error.h" 17 | #include "value.h" 18 | #include "func.h" 19 | #include "proto.h" 20 | #include "unichar.h" 21 | 22 | #ifdef USE_FILESYS_EX 23 | 24 | /* global fileoject id, used to access userdata struct from Object */ 25 | static udid global_fileobject_udid; 26 | static Value *File_prototype; 27 | 28 | /* here demo how to store user-defined data into Object */ 29 | /* first, defined what will be store */ 30 | typedef struct UdFileobj { 31 | FILE *fp; 32 | unichar *filename; 33 | unichar *mode; 34 | } UdFileobj; 35 | 36 | static void fileobject_erase(UdFileobj *fo) 37 | { 38 | if (fo->filename) { 39 | fclose(fo->fp); 40 | unifree(fo->filename); 41 | unifree(fo->mode); 42 | } 43 | fo->filename = NULL; 44 | } 45 | 46 | static void fileobject_free(void *data) 47 | { 48 | UdFileobj *fo = data; 49 | fileobject_erase(fo); 50 | free(fo); 51 | } 52 | 53 | static int fileobject_istrue(void *data) 54 | { 55 | UdFileobj *fo = data; 56 | if (!fo->filename) return 0; 57 | else return 1; 58 | } 59 | 60 | static int fileobject_equ(void *data1, void *data2) 61 | { 62 | return (data1 == data2); 63 | } 64 | 65 | /* second, defined a UserDataReg with how to proccess userdefined data */ 66 | static UserDataReg fileobject = { 67 | "fileobject", 68 | fileobject_free, 69 | fileobject_istrue, 70 | fileobject_equ 71 | }; 72 | 73 | static int try_open_file(UdFileobj *udf, Value *args) 74 | { 75 | int ret = 0; 76 | fileobject_erase(udf); 77 | 78 | Value *fname = value_object_lookup_array(args, 0, NULL); 79 | if (fname && fname->vt == VT_STRING) { 80 | Value *vmode = value_object_lookup_array(args, 1, NULL); 81 | const char *mode = NULL; 82 | if (vmode && vmode->vt == VT_STRING) { 83 | mode = tochars(vmode->d.str); 84 | } 85 | char *rmode = c_strdup(mode ? mode : "r"); 86 | FILE *fp = fopen(tochars(fname->d.str), rmode); 87 | if (fp) { 88 | udf->fp = fp; 89 | udf->filename = unistrdup(fname->d.str); 90 | udf->mode = unistrdup_str(rmode); 91 | } else ret = -1; 92 | c_strfree(rmode); 93 | } 94 | return ret; 95 | } 96 | 97 | static int File_constructor(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 98 | { 99 | Value *toacc = NULL; 100 | if (asc) { 101 | toacc = _this; 102 | } else { 103 | Object *o = object_new(); 104 | o->__proto__ = File_prototype; 105 | value_make_object(*ret, o); 106 | toacc = ret; 107 | } 108 | 109 | UdFileobj *udf = malloc(sizeof(UdFileobj)); 110 | memset(udf, 0, sizeof(UdFileobj)); 111 | 112 | try_open_file(udf, args); 113 | 114 | userdata_set(toacc->d.obj, global_fileobject_udid, udf); 115 | return 0; 116 | } 117 | 118 | static int File_prototype_open(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 119 | { 120 | if (asc) die("Can not call File.open as constructor\n"); 121 | if (_this->vt != VT_OBJECT) bug("this is not object\n"); 122 | 123 | UdFileobj *udf = userdata_get(_this->d.obj, global_fileobject_udid); 124 | if (!udf) die("Apply File.open in a non-file object\n"); 125 | 126 | if (try_open_file(udf, args)) { 127 | value_make_bool(*ret, 0); 128 | } 129 | value_make_bool(*ret, 1); 130 | return 0; 131 | } 132 | 133 | static int File_prototype_close(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 134 | { 135 | if (asc) die("Can not call File.close as constructor\n"); 136 | if (_this->vt != VT_OBJECT) bug("this is not object\n"); 137 | 138 | UdFileobj *udf = userdata_get(_this->d.obj, global_fileobject_udid); 139 | if (!udf) die("Apply File.close in a non-file object\n"); 140 | 141 | fileobject_erase(udf); 142 | value_make_bool(*ret, 1); 143 | return 0; 144 | } 145 | 146 | static int File_prototype_gets(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 147 | { 148 | if (asc) die("Can not call File.gets as constructor\n"); 149 | if (_this->vt != VT_OBJECT) bug("this is not object\n"); 150 | 151 | UdFileobj *udf = userdata_get(_this->d.obj, global_fileobject_udid); 152 | if (!udf) die("Apply File.gets in a non-file object\n"); 153 | 154 | if (!udf->filename) { 155 | value_make_undef(*ret); 156 | return 0; 157 | } 158 | char buf[2048]; 159 | if (!fgets(buf, 2046, udf->fp)) { 160 | value_make_undef(*ret); 161 | return 0; 162 | } 163 | buf[2046] = 0; 164 | char *r = strchr(buf, '\r'); 165 | if (r) *r = 0; 166 | else { 167 | r = strchr(buf, '\n'); 168 | if (r) *r = 0; 169 | } 170 | value_make_string(*ret, unistrdup_str(buf)); 171 | return 0; 172 | } 173 | 174 | static int File_prototype_puts(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 175 | { 176 | if (asc) die("Can not call File.puts as constructor\n"); 177 | if (_this->vt != VT_OBJECT) bug("this is not object\n"); 178 | 179 | UdFileobj *udf = userdata_get(_this->d.obj, global_fileobject_udid); 180 | if (!udf) die("Apply File.puts in a non-file object\n"); 181 | 182 | if (!udf->filename) { 183 | value_make_bool(*ret, 0); 184 | return 0; 185 | } 186 | Value *toput = value_object_lookup_array(args, 0, NULL); 187 | if (!toput) { 188 | value_make_bool(*ret, 0); 189 | return 0; 190 | } 191 | value_tostring(toput); 192 | 193 | if (fprintf(udf->fp, "%s\n", tochars(toput->d.str)) < 0) { 194 | value_make_bool(*ret, 0); 195 | return 0; 196 | } 197 | value_make_bool(*ret, 1); 198 | return 0; 199 | } 200 | 201 | static int File_prototype_eof(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 202 | { 203 | if (asc) die("Can not call File.eof as constructor\n"); 204 | if (_this->vt != VT_OBJECT) bug("this is not object\n"); 205 | 206 | UdFileobj *udf = userdata_get(_this->d.obj, global_fileobject_udid); 207 | if (!udf) die("Apply File.eof in a non-file object\n"); 208 | 209 | value_make_bool(*ret, feof(udf->fp)); 210 | return 0; 211 | } 212 | 213 | void filesys_init(Value *global) 214 | { 215 | /* third, register userdata */ 216 | global_fileobject_udid = userdata_register(&fileobject); 217 | if (global_fileobject_udid < 0) die("Can not init file system\n"); 218 | 219 | /* File.prototype */ 220 | File_prototype = value_object_utils_new_object(); 221 | File_prototype->d.obj->__proto__ = Object_prototype; 222 | value_object_utils_insert(File_prototype, tounichars("open"), 223 | func_utils_make_func_value(File_prototype_open), 0, 0, 0); 224 | value_object_utils_insert(File_prototype, tounichars("close"), 225 | func_utils_make_func_value(File_prototype_close), 0, 0, 0); 226 | value_object_utils_insert(File_prototype, tounichars("gets"), 227 | func_utils_make_func_value(File_prototype_gets), 0, 0, 0); 228 | value_object_utils_insert(File_prototype, tounichars("puts"), 229 | func_utils_make_func_value(File_prototype_puts), 0, 0, 0); 230 | value_object_utils_insert(File_prototype, tounichars("eof"), 231 | func_utils_make_func_value(File_prototype_eof), 0, 0, 0); 232 | 233 | Value *_File = func_utils_make_func_value(File_constructor); 234 | value_object_utils_insert(_File, tounichars("prototype"), 235 | File_prototype, 0, 0, 0); 236 | _File->d.obj->__proto__ = Function_prototype; 237 | 238 | value_object_utils_insert(global, tounichars("File"), _File, 1, 1, 0); 239 | } 240 | 241 | #else 242 | 243 | /* no filesystem extern, simply empty init */ 244 | void filesys_init(Value *global) {} 245 | 246 | #endif 247 | 248 | -------------------------------------------------------------------------------- /filesys.ex.h: -------------------------------------------------------------------------------- 1 | #ifndef __FILESYS_EX_H__ 2 | #define __FILESYS_EX_H__ 3 | 4 | struct Value; 5 | void filesys_init(struct Value *global); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /func.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "code.h" 6 | #include "func.h" 7 | #include "error.h" 8 | #include "value.h" 9 | #include "scope.h" 10 | #include "unichar.h" 11 | 12 | Func *func_make_static(strs *args, strs *localvar, struct OpCodes *ops) 13 | { 14 | Func *f = malloc(sizeof(Func)); 15 | memset(f, 0, sizeof(Func)); 16 | f->type = FC_NORMAL; 17 | f->exec.opcodes = ops; 18 | f->argnames = args; 19 | f->localnames = localvar; 20 | return f; 21 | } 22 | 23 | void func_init_localvar(Value *arguments, Func *who) 24 | { 25 | if (who->localnames) { 26 | int i; 27 | for (i = 0; i < who->localnames->count; ++i) { 28 | const unichar *argkey = strs_get(who->localnames, i); 29 | if (argkey) { 30 | ObjKey *strkey = objkey_new(argkey, OM_DONTEMU); 31 | value_object_insert(arguments, strkey, value_new()); 32 | } 33 | } 34 | } 35 | } 36 | 37 | static FuncObj *func_make_internal(SSFunc callback) 38 | { 39 | Func *f = malloc(sizeof(Func)); 40 | memset(f, 0, sizeof(Func)); 41 | f->type = FC_BUILDIN; 42 | f->exec.callback = callback; 43 | 44 | return funcobj_new(f); 45 | } 46 | 47 | Value *func_utils_make_func_value(SSFunc callback) 48 | { 49 | Object *o = object_new(); 50 | o->ot = OT_FUNCTION; 51 | o->d.fobj = func_make_internal(callback); 52 | 53 | Value *v = value_new(); 54 | value_make_object(*v, o); 55 | return v; 56 | } 57 | 58 | -------------------------------------------------------------------------------- /func.h: -------------------------------------------------------------------------------- 1 | #ifndef __FUNC_H__ 2 | #define __FUNC_H__ 3 | 4 | #include "scope.h" 5 | 6 | struct Value; 7 | struct OpCodes; 8 | struct PSTATE; 9 | 10 | /* System func callback type */ 11 | typedef int (*SSFunc)(struct PSTATE *ps, struct Value *args, 12 | struct Value *_this, struct Value *ret, int asconstructor); 13 | 14 | /* raw function data, with script function or system SSFunc */ 15 | typedef struct Func { 16 | enum { 17 | FC_NORMAL, 18 | FC_BUILDIN 19 | } type; /* type */ 20 | union { 21 | struct OpCodes *opcodes; /* FC_NORMAL, codes of this function */ 22 | SSFunc callback; /* FC_BUILDIN, callback */ 23 | } exec; 24 | strs *argnames; /* FC_NORMAL, argument names */ 25 | strs *localnames; /* FC_NORMAL, local var names */ 26 | } Func; 27 | 28 | Func *func_make_static(strs *args, strs *localvar, struct OpCodes *ops); 29 | 30 | /* Make a function value from SSFunc */ 31 | struct Value *func_utils_make_func_value(SSFunc callback); 32 | void func_init_localvar(struct Value *arguments, Func *who); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /lexer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "lexer.h" 8 | #include "parser.h" 9 | #include "error.h" 10 | #include "regexp.h" 11 | 12 | #define COMMENT (-128) 13 | 14 | static int lexer_getchar(Lexer *lex) 15 | { 16 | int c = 0; 17 | if (!lex) bug("No lexer init"); 18 | if (lex->ltype == LT_FILE) { 19 | c = fgetc(lex->d.fp); 20 | if (c == EOF) c = 0; 21 | } else { 22 | c = lex->d.str[lex->cur]; 23 | if (c != 0) lex->cur++; 24 | } 25 | if (c == '\n') { 26 | lex->cur_line++; 27 | lex->cur_char = 0; 28 | } 29 | lex->cur_char++; 30 | return c; 31 | } 32 | 33 | static void lexer_ungetc(int c, Lexer *lex) 34 | { 35 | if (!lex) bug("No lexer init"); 36 | if (!c) return; 37 | 38 | if (lex->ltype == LT_FILE) { 39 | ungetc(c, lex->d.fp); 40 | } else { 41 | lex->cur--; 42 | } 43 | } 44 | 45 | static int iskey(const char *word) 46 | { 47 | static struct st_kw { 48 | const char *name; 49 | int value; 50 | } keywords[] = { 51 | { "if", IF }, 52 | { "else", ELSE }, 53 | { "for", FOR }, 54 | { "in", IN }, 55 | { "while", WHILE }, 56 | { "do", DO }, 57 | { "continue", CONTINUE }, 58 | { "switch", SWITCH }, 59 | { "case", CASE }, 60 | { "default", DEFAULT }, 61 | { "break", BREAK }, 62 | { "function", FUNC }, 63 | { "return", RETURN }, 64 | { "var", LOCAL }, 65 | { "new", NEW }, 66 | { "delete", DELETE }, 67 | { "try", TRY }, 68 | { "catch", CATCH }, 69 | { "throw", THROW }, 70 | { "finally", FINALLY }, 71 | { "with", WITH }, 72 | { "undefined", UNDEF }, 73 | { "true", _TRUE }, 74 | { "false", _FALSE }, 75 | { "this", _THIS }, 76 | { "arguments", ARGUMENTS }, 77 | { "void", VOID }, 78 | { "__debug", __DEBUG } 79 | }; 80 | int i; 81 | for (i = 0; i < sizeof(keywords) / sizeof(struct st_kw); ++i) { 82 | if (strcmp(word, keywords[i].name) == 0) 83 | return keywords[i].value; 84 | } 85 | return 0; 86 | } 87 | 88 | unichar *do_string(Lexer *lex) 89 | { 90 | int c = lexer_getchar(lex); 91 | int endchar = c; 92 | 93 | UNISTR(65536) unibuf; 94 | 95 | unichar *buf = unibuf.unistr; 96 | int bufi = 0; 97 | 98 | while (bufi < 65530) { 99 | c = lexer_getchar(lex); 100 | if (c == EOF || c == 0) { 101 | die("Unexpected EOF parsing string.\n"); 102 | } 103 | if (c == '\\') { 104 | int n = lexer_getchar(lex); 105 | switch(n) { 106 | case 'b': buf[bufi++] = '\b'; break; 107 | case 'f': buf[bufi++] = '\f'; break; 108 | case 'n': buf[bufi++] = '\n'; break; 109 | case 'r': buf[bufi++] = '\r'; break; 110 | case 't': buf[bufi++] = '\t'; break; 111 | case EOF: 112 | case 0: 113 | die("Unexpected EOF parsing string.\n"); 114 | default: buf[bufi++] = n; 115 | } 116 | } else { 117 | buf[bufi++] = c; 118 | } 119 | if (c == endchar) { 120 | bufi --; 121 | break; 122 | } 123 | } 124 | buf[bufi] = 0; 125 | unibuf.len = bufi; 126 | return unistrdup(buf); 127 | } 128 | 129 | char *do_regex(Lexer *lex, int *flag) 130 | { 131 | char buf[65536]; 132 | int bufi = 0; 133 | char *ret; 134 | 135 | lexer_getchar(lex); /* first '/'*/ 136 | while (bufi < 65530) { 137 | int c = lexer_getchar(lex); 138 | if (c == EOF || c == 0) { 139 | die("Unexpected EOF parsing regular expression.\n"); 140 | } 141 | if (c == '\\') { 142 | int n = lexer_getchar(lex); 143 | if (n == EOF || c == 0) die("Unexpected EOF parsing regular expression.\n"); 144 | 145 | buf[bufi++] = c; 146 | buf[bufi++] = n; 147 | } else if (c == '/') { 148 | buf[bufi] = 0; 149 | while (1) { 150 | c = lexer_getchar(lex); 151 | if (!isalnum(c)) break; 152 | if (c == 'i') *flag |= REG_ICASE; 153 | } 154 | lexer_ungetc(c, lex); 155 | break; 156 | } else { 157 | buf[bufi++] = c; 158 | } 159 | } 160 | ret = c_strdup(buf); 161 | return ret; 162 | } 163 | 164 | int do_sign(Lexer *lex) 165 | { 166 | static struct st_sn { 167 | const char *name; 168 | int len; 169 | int value; 170 | } signs[] = { 171 | { ">>>=", 4, URSHFAS }, 172 | { "<<=", 3, LSHFAS }, 173 | { ">>=", 3, RSHFAS }, 174 | { "===", 3, EEQU }, 175 | { "!==", 3, NNEQ }, 176 | { ">>>", 3, URSHF }, 177 | { "==", 2, EQU }, 178 | { "!=", 2, NEQ }, 179 | { "<=", 2, LEQ }, 180 | { ">=", 2, GEQ }, 181 | { "++", 2, INC }, 182 | { "--", 2, DEC }, 183 | { "&&", 2, AND }, 184 | { "||", 2, OR }, 185 | { "+=", 2, ADDAS }, 186 | { "-=", 2, MNSAS }, 187 | { "*=", 2, MULAS }, 188 | { "/=", 2, DIVAS }, 189 | { "%=", 2, MODAS }, 190 | { "&=", 2, BANDAS }, 191 | { "|=", 2, BORAS }, 192 | { "^=", 2, BXORAS }, 193 | { "<<", 2, LSHF }, 194 | { ">>", 2, RSHF } 195 | }; 196 | 197 | int bufi; 198 | char buf[4]; 199 | int i; 200 | for (bufi = 0; bufi < 4; ++bufi) { 201 | int c = lexer_getchar(lex); 202 | if (c == 0 || c == '\n') break; 203 | buf[bufi] = c; 204 | } 205 | if (!bufi) return 0; 206 | 207 | for (i = 0; i < sizeof(signs)/sizeof(struct st_sn); ++i) { 208 | if (bufi < signs[i].len) continue; 209 | if (strncmp(buf, signs[i].name, signs[i].len) == 0) { 210 | int j; 211 | for (j = bufi - 1; j >= signs[i].len; --j) 212 | lexer_ungetc(buf[j], lex); 213 | 214 | return signs[i].value; 215 | } 216 | } 217 | 218 | for (i = bufi - 1; i >= 1; --i) 219 | lexer_ungetc(buf[i], lex); 220 | 221 | return buf[0]; 222 | } 223 | 224 | #define LOCATION_START(loc, lex) do { \ 225 | (loc)->first_line = (lex)->cur_line; \ 226 | (loc)->first_column = (lex)->cur_char; \ 227 | } while(0) 228 | #define LOCATION_END(loc, lex) do { \ 229 | (loc)->last_line = (lex)->cur_line; \ 230 | (loc)->last_column = (lex)->cur_char; \ 231 | } while(0) 232 | 233 | static void eat_comment(Lexer *lex) 234 | { 235 | int c; 236 | while((c = lexer_getchar(lex))) { 237 | if (c == '*') { 238 | c = lexer_getchar(lex); 239 | if (c == '/') return; 240 | lexer_ungetc(c, lex); 241 | } 242 | } 243 | die("Comment reach end of file\n"); 244 | } 245 | 246 | static int _yylex (YYSTYPE *yylvalp, YYLTYPE *yyllocp, Lexer *lex) 247 | { 248 | int c; 249 | 250 | UNISTR(1024) unibuf; 251 | 252 | unichar *word = unibuf.unistr; 253 | int wi = 0; 254 | 255 | LOCATION_START(yyllocp, lex); 256 | while ((c = lexer_getchar(lex)) == ' ' || c == '\t' || c == '\n' || c == '\r'); 257 | 258 | if (isdigit(c)) { 259 | int fnum = 0; 260 | word[wi++] = c; 261 | while (wi < 1020) { 262 | c = lexer_getchar(lex); 263 | if (isdigit(c)) word[wi++] = c; 264 | else if (c == '.') { 265 | if (fnum) die("Number format error"); 266 | fnum = 1; 267 | word[wi++] = c; 268 | } else { 269 | lexer_ungetc(c, lex); 270 | break; 271 | } 272 | } 273 | LOCATION_END(yyllocp, lex); 274 | word[wi] = 0; 275 | unibuf.len = wi; 276 | double *db = malloc(sizeof(double)); 277 | sscanf(tochars(word), "%lf", db); 278 | *yylvalp = db; 279 | return FNUMBER; 280 | } else if (c == '"' || c == '\'') { 281 | lexer_ungetc(c, lex); 282 | *yylvalp = do_string(lex); 283 | LOCATION_END(yyllocp, lex); 284 | return STRING; 285 | } else if (isalpha(c) || c == '_' || c == '$') { 286 | lexer_ungetc(c, lex); 287 | while (wi < 1020) { 288 | c = lexer_getchar(lex); 289 | if (!isalnum(c) && c != '_' && c != '$') break; 290 | word[wi++] = c; 291 | } 292 | lexer_ungetc(c, lex); 293 | 294 | word[wi] = 0; 295 | unibuf.len = wi; 296 | int r = iskey(tochars(word)); 297 | if (r) return r; 298 | *yylvalp = unistrdup(word); 299 | LOCATION_END(yyllocp, lex); 300 | return IDENTIFIER; 301 | } else if (c == '/') { 302 | int d = lexer_getchar(lex); 303 | if (d == '/') { 304 | while ((d = lexer_getchar(lex)) != '\r' && d != '\n' && d != 0); 305 | return COMMENT; 306 | } else if (d == '*') { 307 | eat_comment(lex); 308 | return COMMENT; 309 | } else lexer_ungetc(d, lex); 310 | 311 | if (lex->last_token != FNUMBER && lex->last_token != STRING && 312 | lex->last_token != REGEXP && lex->last_token != UNDEF && 313 | lex->last_token != _TRUE && lex->last_token != _FALSE && 314 | lex->last_token != ARGUMENTS && lex->last_token != _THIS && 315 | lex->last_token != IDENTIFIER) { 316 | lexer_ungetc(c, lex); 317 | int flag = REG_EXTENDED; 318 | char *regtxt = do_regex(lex, &flag); 319 | *yylvalp = regex_new(regtxt, flag); 320 | c_strfree(regtxt); 321 | return REGEXP; 322 | } 323 | } 324 | 325 | lexer_ungetc(c, lex); 326 | 327 | int r = do_sign(lex); 328 | LOCATION_END(yyllocp, lex); 329 | return r; 330 | } 331 | 332 | int yylex (YYSTYPE *yylvalp, YYLTYPE *yyllocp, PSTATE *pstate) 333 | { 334 | int ret; 335 | do { 336 | ret = _yylex(yylvalp, yyllocp, pstate->lexer); 337 | } while (ret == COMMENT); 338 | /* 339 | if (ret < 128 && ret > 0) printf("%c\n", ret); 340 | else printf("%d\n", ret); 341 | */ 342 | pstate->lexer->last_token = ret; 343 | return ret; 344 | } 345 | 346 | void yyerror(YYLTYPE *yylloc, PSTATE *ps, char *msg) 347 | { 348 | fprintf(stderr, "%d[%d-%d]:%s\n", yylloc->first_line, 349 | yylloc->first_column, yylloc->last_column, msg); 350 | ps->err_count++; 351 | } 352 | -------------------------------------------------------------------------------- /lexer.h: -------------------------------------------------------------------------------- 1 | #ifndef __LEXER_H__ 2 | #define __LEXER_H__ 3 | 4 | #define YYSTYPE void * 5 | 6 | #include "parser.h" 7 | #include "pstate.h" 8 | 9 | /* Lexer, where input seq provided */ 10 | typedef struct Lexer { 11 | enum { 12 | LT_FILE, /* read from file */ 13 | LT_STRING /* read from a string */ 14 | } ltype; 15 | union { 16 | FILE *fp; /* LT_FILE, where to read */ 17 | const char *str; /* LT_STRING */ 18 | } d; 19 | int last_token; /* last token returned */ 20 | int cur; /* LT_STRING, current char */ 21 | int cur_line; /* current line no. */ 22 | int cur_char; /* current column no. */ 23 | } Lexer; 24 | 25 | int yylex (YYSTYPE *yylvalp, YYLTYPE *yyllocp, PSTATE *pstate); 26 | void yyerror(YYLTYPE *yylloc, PSTATE *ps, char *msg); 27 | 28 | #endif 29 | 30 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "lexer.h" 6 | #include "pstate.h" 7 | #include "parser.h" 8 | #include "regexp.h" 9 | #include "code.h" 10 | #include "value.h" 11 | #include "eval.h" 12 | #include "func.h" 13 | #include "utils.h" 14 | #include "proto.h" 15 | #include "filesys.ex.h" 16 | #include "error.h" 17 | #include "mempool.h" 18 | 19 | extern int yyparse(PSTATE *ps); 20 | 21 | int Usage() 22 | { 23 | fprintf(stderr, "Usage: smallscript [input file] [arguments]\n"); 24 | return -1; 25 | } 26 | 27 | int main(int argc, char **argv) 28 | { 29 | FILE *input = stdin; 30 | 31 | argv++; 32 | argc--; 33 | 34 | if (argc > 0) { 35 | input = fopen(argv[0], "r"); 36 | if (!input) { 37 | fprintf(stderr, "Can not open '%s'\n", argv[0]); 38 | return Usage(); 39 | } 40 | argv++; 41 | argc--; 42 | } 43 | 44 | /* subsystem init */ 45 | mpool_init(); /* general mempool */ 46 | objects_init(); 47 | 48 | PSTATE *ps = pstate_new_from_file(input); 49 | yyparse(ps); 50 | 51 | if (!ps->err_count) { 52 | Value ret; 53 | 54 | /* current scope, also global */ 55 | Value *csc = value_new(); 56 | value_make_object(*csc, object_new()); 57 | 58 | /* top this and prototype chain */ 59 | proto_init(csc); 60 | 61 | /* global funtion, debugger, etc */ 62 | utils_init(csc, argc, argv); 63 | 64 | /* file system extern init */ 65 | filesys_init(csc); 66 | 67 | /* initial scope chain, nothing */ 68 | ScopeChain *gsc = scope_chain_new(0); 69 | 70 | #ifdef DEBUG 71 | codes_print(ps->opcodes); 72 | printf("------------------------\n"); 73 | #endif 74 | if (eval(ps, ps->opcodes, gsc, csc, csc, &ret)) { 75 | die("Uncatched error"); 76 | } else { 77 | extern int sp; 78 | if (sp != 0) { 79 | bug("Stack not ballence after execute script\n"); 80 | } 81 | } 82 | scope_chain_free(gsc); 83 | value_free(csc); 84 | } 85 | fclose(input); 86 | pstate_free(ps); 87 | return 0; 88 | } 89 | 90 | -------------------------------------------------------------------------------- /mempool.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "error.h" 6 | #include "mempool.h" 7 | 8 | #ifndef DONT_USE_POOL 9 | static void pool_extern(mpool_t mp) 10 | { 11 | int size = sizeof(Memblock) + ((mp->elemsize + sizeof(Memnode)) * MP_BLOCK_SIZE); 12 | struct Memblock *mb = malloc(size); 13 | if (!mb) die("Out of memory\n"); 14 | 15 | /* push to block head */ 16 | mb->next = mp->blockhead; 17 | mp->blockhead = mb; 18 | 19 | char *p = (char *)mb; /* raw byte proccess */ 20 | p += (int)sizeof(Memblock); /* p pointed to (Memnode+elesize) * BLOCKSIZE */ 21 | 22 | int i; 23 | for (i = 0; i < MP_BLOCK_SIZE; ++i) { 24 | Memnode *n = (Memnode *)p; 25 | n->esize = mp->elemsize; 26 | 27 | n->next = mp->nodehead; 28 | mp->nodehead = n; 29 | 30 | p += (mp->elemsize + sizeof(Memnode)); 31 | } 32 | } 33 | #endif 34 | 35 | mpool_t mpool_create(unsigned int elemsize) 36 | { 37 | mpool_t mp = malloc(sizeof(struct Mempool)); 38 | if (!mp) die("Out of memory\n"); 39 | 40 | memset(mp, 0, sizeof(struct Mempool)); 41 | 42 | if (elemsize % 4 != 0) 43 | bug("Problem, create a non-align mempool, failed"); 44 | 45 | mp->elemsize = elemsize; 46 | 47 | #ifndef DONT_USE_POOL 48 | pool_extern(mp); 49 | #endif 50 | 51 | return mp; 52 | } 53 | 54 | void *mpool_alloc(mpool_t mp) 55 | { 56 | #ifndef DONT_USE_POOL 57 | if (!mp->nodehead) /* running out of cache */ 58 | pool_extern(mp); 59 | 60 | char *ret = (char *)mp->nodehead; 61 | mp->nodehead = mp->nodehead->next; 62 | 63 | return (void *)(ret + (int)sizeof(Memnode)); 64 | #else 65 | return malloc(mp->elemsize); 66 | #endif 67 | } 68 | 69 | void mpool_free(void *p, mpool_t mp) 70 | { 71 | #ifndef DONT_USE_POOL 72 | Memnode *n = (Memnode *)(((char *)p) - sizeof(Memnode)); 73 | if (n->esize != mp->elemsize) { 74 | bug("Release an (%d)sized memory into (%d)sized pool", 75 | n->esize, mp->elemsize); 76 | } 77 | n->next = mp->nodehead; 78 | mp->nodehead = n; 79 | #else 80 | free(p); 81 | #endif 82 | } 83 | 84 | #ifndef DONT_USE_POOL 85 | /* Create pools to replace malloc */ 86 | #define POOL_COUNT 8 87 | 88 | static mpool_t general_pools[POOL_COUNT]; 89 | static unsigned int gpools_sizes[POOL_COUNT] = { 90 | 8, 12, 16, 24, 32, 64, 128, 256 91 | }; 92 | 93 | static int sizeindexes[129] = { 94 | 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 8 */ 95 | 1, 1, 1, 1, /* 9 - 12 */ 96 | 2, 2, 2, 2, /* 13 - 16 */ 97 | 3, 3, 3, 3, 3, 3, 3, 3, /* 17 - 24 */ 98 | 4, 4, 4, 4, 4, 4, 4, 4, /* 25 - 32 */ 99 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 100 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, /* 33 - 64 */ 101 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 102 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 103 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 104 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 /* 65 - 128 */ 105 | }; 106 | 107 | #define roundsize(n) (((n)<=128)?sizeindexes[n]:(((n)>256)?-1:7)) 108 | #endif 109 | 110 | void mpool_init() 111 | { 112 | #ifndef DONT_USE_POOL 113 | int i; 114 | for (i = 0; i < POOL_COUNT; ++i) { 115 | if (gpools_sizes[i]) { 116 | general_pools[i] = mpool_create(gpools_sizes[i]); 117 | } 118 | } 119 | #endif 120 | } 121 | 122 | void *mm_alloc(unsigned int size) /* size_t ... */ 123 | { 124 | #ifndef DONT_USE_POOL 125 | int poolindex = roundsize(size); 126 | if (poolindex >= 0) { 127 | return mpool_alloc(general_pools[poolindex]); 128 | } 129 | 130 | /* block that bigger then 256 bytes, use malloc */ 131 | /* still add a Memnode struct before the allocated memory */ 132 | Memnode *n = malloc(size + sizeof(Memnode)); 133 | n->esize = size; 134 | 135 | n++; 136 | return n; 137 | #else 138 | return malloc(size); 139 | #endif 140 | } 141 | 142 | void mm_free(void *p) 143 | { 144 | #ifndef DONT_USE_POOL 145 | Memnode *n = (Memnode *)(((char *)p) - sizeof(Memnode)); 146 | int poolindex = roundsize(n->esize); 147 | if (poolindex >= 0) { 148 | mpool_free(p, general_pools[poolindex]); 149 | return; 150 | } 151 | 152 | /* more then 256 bytes, use free */ 153 | free(n); 154 | #else 155 | free(p); 156 | #endif 157 | } 158 | 159 | -------------------------------------------------------------------------------- /mempool.h: -------------------------------------------------------------------------------- 1 | #ifndef __MEM_POOL_H__ 2 | #define __MEM_POOL_H__ 3 | 4 | /* 5 | * struct relationship: 6 | * |------------------------MemBlock------------------------| 7 | * | ________ __________ | 8 | * | next* / \ / \ | 9 | * |struct Memblock|Memnode|UserData|Memnode|UserData|... | 10 | * next* | /\ 11 | * | Return to user 12 | * |struct Memblock| 13 | * ... 14 | * 15 | * define DONT_USE_POOL to disable mempool and use native c mem funcs 16 | * Note: 17 | * all allocated mem from pool is not zero filled 18 | */ 19 | 20 | /* how many node in one block */ 21 | #define MP_BLOCK_SIZE 1024 22 | 23 | /* per node size is (sizeof(Memnode) + mp->elemsize); */ 24 | typedef struct Memnode { 25 | struct Memnode *next; 26 | unsigned int esize; 27 | } Memnode; 28 | 29 | typedef struct Memblock { 30 | struct Memblock *next; 31 | } Memblock; 32 | 33 | typedef struct Mempool { 34 | unsigned int elemsize; 35 | 36 | Memblock *blockhead; 37 | Memnode *nodehead; 38 | } *mpool_t; 39 | 40 | void mpool_init(); 41 | 42 | /* create a pool, with size and initial element count */ 43 | /* return pool handle */ 44 | mpool_t mpool_create(unsigned int elemsize); 45 | 46 | /* allocate an element from pool */ 47 | /* same as malloc */ 48 | void *mpool_alloc(mpool_t mpool); 49 | 50 | /* free an element to pool */ 51 | /* same as free */ 52 | void mpool_free(void *p, mpool_t mpool); 53 | 54 | /* general malloc/free replacement 55 | * cutting down calling times of malloc and free, 56 | * but some times slower then native malloc/free, 57 | * don't know why, if so 58 | * simply USE DONT_USE_POOL to disable mempool 59 | */ 60 | void *mm_alloc(unsigned int size); 61 | void mm_free(void *p); 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /number.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "number.h" 3 | 4 | #ifndef _NO_LL_SUPPORT 5 | static const long long _numnan = 0x7ff8000000000000LL; 6 | static const long long *NAN = &_numnan; 7 | static const long long _numinf = 0x7ff0000000000000LL; 8 | static const long long *INF = &_numinf; 9 | 10 | #else 11 | #ifndef _BIGENDIAN 12 | static const unsigned char _numnan[] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF8, 0x7F }; 13 | static const unsigned char _numinf[] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF0, 0x7F }; 14 | #else 15 | static const unsigned char _numnan[] = { 0x7F, 0xF8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; 16 | static const unsigned char _numinf[] = { 0x7F, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; 17 | #endif 18 | static const unsigned char *NAN = _numnan; 19 | static const unsigned char *INF = _numinf; 20 | #endif 21 | 22 | double ieee_makeinf(int i) 23 | { 24 | double r = *(double *)INF; 25 | if (i < 0) r = -r; 26 | return r; 27 | } 28 | 29 | double ieee_makenan() 30 | { 31 | return *(double *)NAN; 32 | } 33 | 34 | /* 35 | * below numtoa function modify from : 36 | * http://code.google.com/p/stringencoders/ 37 | * Copyright © 2007, Nick Galbreath -- nickg [at] modp [dot] com 38 | * All rights reserved. 39 | * Released under the MIT license. 40 | */ 41 | 42 | static const double pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 43 | 10000000, 100000000, 1000000000}; 44 | 45 | static void strreverse(unichar* begin, unichar* end) 46 | { 47 | unichar aux; 48 | while (end > begin) 49 | aux = *end, *end-- = *begin, *begin++ = aux; 50 | } 51 | 52 | void num_itoa10(int value, unichar* str) 53 | { 54 | unichar* wstr = str; 55 | /* Take care of sign */ 56 | unsigned int uvalue = (value < 0) ? -value : value; 57 | /* Conversion. Number is reversed. */ 58 | do *wstr++ = (unichar)(48 + (uvalue % 10)); while(uvalue /= 10); 59 | if (value < 0) *wstr++ = '-'; 60 | 61 | unistrlen(str) = wstr - str; 62 | /* Reverse string */ 63 | strreverse(str,wstr-1); 64 | } 65 | 66 | void num_uitoa10(unsigned int value, unichar* str) 67 | { 68 | unichar* wstr=str; 69 | /* Conversion. Number is reversed. */ 70 | do *wstr++ = (unichar)(48 + (value % 10)); while (value /= 10); 71 | 72 | unistrlen(str) = wstr - str; 73 | /* Reverse string */ 74 | strreverse(str, wstr-1); 75 | } 76 | 77 | /* This is near identical to modp_dtoa above */ 78 | /* The differnce is noted below */ 79 | void num_dtoa2(double value, unichar* str, int prec) 80 | { 81 | /* Hacky test for NaN 82 | * under -fast-math this won't work, but then you also won't 83 | * have correct nan values anyways. The alternative is 84 | * to link with libmath (bad) or hack IEEE double bits (bad) 85 | */ 86 | if (! (value == value)) { 87 | str[0] = 'N'; str[1] = 'a'; str[2] = 'N'; 88 | unistrlen(str) = 3; 89 | return; 90 | } 91 | 92 | /* if input is larger than thres_max, revert to exponential */ 93 | const double thres_max = (double)(0x7FFFFFFF); 94 | 95 | int count; 96 | double diff = 0.0; 97 | unichar* wstr = str; 98 | 99 | if (prec < 0) { 100 | prec = 0; 101 | } else if (prec > 9) { 102 | /* precision of >= 10 can lead to overflow errors */ 103 | prec = 9; 104 | } 105 | 106 | /* we'll work in positive values and deal with the 107 | negative sign issue later */ 108 | int neg = 0; 109 | if (value < 0) { 110 | neg = 1; 111 | value = -value; 112 | } 113 | 114 | int whole = (int) value; 115 | double tmp = (value - whole) * pow10[prec]; 116 | unsigned int frac = (unsigned int)(tmp); 117 | diff = tmp - frac; 118 | 119 | if (diff > 0.5) { 120 | ++frac; 121 | /* handle rollover, e.g. case 0.99 with prec 1 is 1.0 */ 122 | if (frac >= pow10[prec]) { 123 | frac = 0; 124 | ++whole; 125 | } 126 | } else if (diff == 0.5 && ((frac == 0) || (frac & 1))) { 127 | /* if halfway, round up if odd, OR 128 | if last digit is 0. That last part is strange */ 129 | ++frac; 130 | } 131 | 132 | /* for very large numbers switch back to native sprintf for exponentials. 133 | anyone want to write code to replace this? */ 134 | /* 135 | normal printf behavior is to print EVERY whole number digit 136 | which can be 100s of characters overflowing your buffers == bad 137 | */ 138 | if (value > thres_max) { 139 | char bstr[64]; 140 | int i; 141 | sprintf(bstr, "%e", neg ? -value : value); 142 | for (i = 0; bstr[i]; ++i) 143 | str[i] = bstr[i]; 144 | 145 | unistrlen(str) = i; 146 | return; 147 | } 148 | 149 | if (prec == 0) { 150 | diff = value - whole; 151 | if (diff > 0.5) { 152 | /* greater than 0.5, round up, e.g. 1.6 -> 2 */ 153 | ++whole; 154 | } else if (diff == 0.5 && (whole & 1)) { 155 | /* exactly 0.5 and ODD, then round up */ 156 | /* 1.5 -> 2, but 2.5 -> 2 */ 157 | ++whole; 158 | } 159 | 160 | /*vvvvvvvvvvvvvvvvvvv Diff from modp_dto2 */ 161 | } else if (frac) { 162 | count = prec; 163 | /* now do fractional part, as an unsigned number */ 164 | /* we know it is not 0 but we can have leading zeros, these */ 165 | /* should be removed */ 166 | while (!(frac % 10)) { 167 | --count; 168 | frac /= 10; 169 | } 170 | /*^^^^^^^^^^^^^^^^^^^ Diff from modp_dto2 */ 171 | 172 | /* now do fractional part, as an unsigned number */ 173 | do { 174 | --count; 175 | *wstr++ = (unichar)(48 + (frac % 10)); 176 | } while (frac /= 10); 177 | /* add extra 0s */ 178 | while (count-- > 0) *wstr++ = '0'; 179 | /* add decimal */ 180 | *wstr++ = '.'; 181 | } 182 | 183 | /* do whole part */ 184 | /* Take care of sign */ 185 | /* Conversion. Number is reversed. */ 186 | do *wstr++ = (unichar)(48 + (whole % 10)); while (whole /= 10); 187 | if (neg) { 188 | *wstr++ = '-'; 189 | } 190 | unistrlen(str) = wstr - str; 191 | strreverse(str, wstr-1); 192 | } 193 | 194 | -------------------------------------------------------------------------------- /number.h: -------------------------------------------------------------------------------- 1 | #ifndef __NUMBER_H__ 2 | #define __NUMBER_H__ 3 | 4 | #include "unichar.h" 5 | 6 | /* 7 | * since nan/infinity math function are not supported in var c compiler, 8 | * we make our own here, in well supported compiler, you may replace those 9 | * mass to build-in function, such as isnan, isfinite. 10 | * 11 | * IEEE/ISO C double type(64 bits): 12 | * bit Function 13 | * 63 Sign 14 | * 52 - 62 Exponent 15 | * 0 - 51 Fraction 16 | * 17 | * Normal number (0 <= e < 2047) 18 | * Infinity e = 2047(max); f = 0 (all zeroes) 19 | * NaN e = 2047, but not Infinity 20 | * 21 | * in little endian maching, a double should like this 22 | * ffffffff*6 EEEEffff SEEEEEEE 23 | */ 24 | 25 | /* cpu calc faster 64bit long long type, comment this */ 26 | #define _NO_LL_SUPPORT 27 | 28 | 29 | #define MAXEXP 2047 30 | 31 | #ifndef _NO_LL_SUPPORT 32 | #define EXP(a) ((0x7ff0000000000000LL & (*(long long*)&a)) >> 52) 33 | #define FRAZERO(a) ((0xfffffffffffffLL & (*(long long*)&a)) == 0LL) 34 | #else 35 | #ifndef _BIGENDIAN 36 | /* little endian (intel) */ 37 | typedef unsigned short u16t; 38 | typedef unsigned int u32t; 39 | 40 | #define EXP(a) ((((u16t *)(&a))[3] & 0x7ff0) >> 4) 41 | #define FRAZERO(a) (((u32t *)(&a))[0] == 0 && \ 42 | (((u32t *)(&a))[1] & 0x0fffff) == 0) 43 | #else 44 | /* big endian */ 45 | /* not support yet */ 46 | #endif 47 | #endif 48 | 49 | #define ieee_isnormal(a) (EXP(a) != MAXEXP) 50 | #define ieee_isnan(a) (EXP(a) == MAXEXP && !FRAZERO(a)) 51 | #define ieee_infinity(a) ((EXP(a) == MAXEXP && FRAZERO(a)) ? (a > 0 ? 1 : -1) : 0) 52 | 53 | double ieee_makeinf(int i); 54 | double ieee_makenan(); 55 | 56 | void num_itoa10(int value, unichar* str); 57 | void num_uitoa10(unsigned int value, unichar* str); 58 | void num_dtoa2(double value, unichar* str, int prec); 59 | 60 | #endif 61 | 62 | -------------------------------------------------------------------------------- /parser.h: -------------------------------------------------------------------------------- 1 | /* A Bison parser, made by GNU Bison 2.3. */ 2 | 3 | /* Skeleton interface for Bison's Yacc-like parsers in C 4 | 5 | Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 6 | Free Software Foundation, Inc. 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2, or (at your option) 11 | any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 | Boston, MA 02110-1301, USA. */ 22 | 23 | /* As a special exception, you may create a larger work that contains 24 | part or all of the Bison parser skeleton and distribute that work 25 | under terms of your choice, so long as that work isn't itself a 26 | parser generator using the skeleton or a modified version thereof 27 | as a parser skeleton. Alternatively, if you modify or redistribute 28 | the parser skeleton itself, you may (at your option) remove this 29 | special exception, which will cause the skeleton and the resulting 30 | Bison output files to be licensed under the GNU General Public 31 | License without this special exception. 32 | 33 | This special exception was added by the Free Software Foundation in 34 | version 2.2 of Bison. */ 35 | 36 | /* Tokens. */ 37 | #ifndef YYTOKENTYPE 38 | # define YYTOKENTYPE 39 | /* Put the tokens into the symbol table, so that GDB and other debuggers 40 | know about them. */ 41 | enum yytokentype { 42 | STRING = 258, 43 | IDENTIFIER = 259, 44 | IF = 260, 45 | ELSE = 261, 46 | FOR = 262, 47 | IN = 263, 48 | WHILE = 264, 49 | DO = 265, 50 | CONTINUE = 266, 51 | SWITCH = 267, 52 | CASE = 268, 53 | DEFAULT = 269, 54 | BREAK = 270, 55 | FUNC = 271, 56 | RETURN = 272, 57 | LOCAL = 273, 58 | NEW = 274, 59 | DELETE = 275, 60 | TRY = 276, 61 | CATCH = 277, 62 | FINALLY = 278, 63 | THROW = 279, 64 | WITH = 280, 65 | UNDEF = 281, 66 | _TRUE = 282, 67 | _FALSE = 283, 68 | _THIS = 284, 69 | ARGUMENTS = 285, 70 | FNUMBER = 286, 71 | REGEXP = 287, 72 | __DEBUG = 288, 73 | MIN_PRI = 289, 74 | ARGCOMMA = 290, 75 | DIVAS = 291, 76 | BXORAS = 292, 77 | BORAS = 293, 78 | BANDAS = 294, 79 | URSHFAS = 295, 80 | RSHFAS = 296, 81 | LSHFAS = 297, 82 | MODAS = 298, 83 | MULAS = 299, 84 | MNSAS = 300, 85 | ADDAS = 301, 86 | OR = 302, 87 | AND = 303, 88 | NNEQ = 304, 89 | EEQU = 305, 90 | NEQ = 306, 91 | EQU = 307, 92 | INSTANCEOF = 308, 93 | GEQ = 309, 94 | LEQ = 310, 95 | URSHF = 311, 96 | RSHF = 312, 97 | LSHF = 313, 98 | VOID = 314, 99 | TYPEOF = 315, 100 | DEC = 316, 101 | INC = 317, 102 | NEG = 318, 103 | MAX_PRI = 319 104 | }; 105 | #endif 106 | /* Tokens. */ 107 | #define STRING 258 108 | #define IDENTIFIER 259 109 | #define IF 260 110 | #define ELSE 261 111 | #define FOR 262 112 | #define IN 263 113 | #define WHILE 264 114 | #define DO 265 115 | #define CONTINUE 266 116 | #define SWITCH 267 117 | #define CASE 268 118 | #define DEFAULT 269 119 | #define BREAK 270 120 | #define FUNC 271 121 | #define RETURN 272 122 | #define LOCAL 273 123 | #define NEW 274 124 | #define DELETE 275 125 | #define TRY 276 126 | #define CATCH 277 127 | #define FINALLY 278 128 | #define THROW 279 129 | #define WITH 280 130 | #define UNDEF 281 131 | #define _TRUE 282 132 | #define _FALSE 283 133 | #define _THIS 284 134 | #define ARGUMENTS 285 135 | #define FNUMBER 286 136 | #define REGEXP 287 137 | #define __DEBUG 288 138 | #define MIN_PRI 289 139 | #define ARGCOMMA 290 140 | #define DIVAS 291 141 | #define BXORAS 292 142 | #define BORAS 293 143 | #define BANDAS 294 144 | #define URSHFAS 295 145 | #define RSHFAS 296 146 | #define LSHFAS 297 147 | #define MODAS 298 148 | #define MULAS 299 149 | #define MNSAS 300 150 | #define ADDAS 301 151 | #define OR 302 152 | #define AND 303 153 | #define NNEQ 304 154 | #define EEQU 305 155 | #define NEQ 306 156 | #define EQU 307 157 | #define INSTANCEOF 308 158 | #define GEQ 309 159 | #define LEQ 310 160 | #define URSHF 311 161 | #define RSHF 312 162 | #define LSHF 313 163 | #define VOID 314 164 | #define TYPEOF 315 165 | #define DEC 316 166 | #define INC 317 167 | #define NEG 318 168 | #define MAX_PRI 319 169 | 170 | 171 | 172 | 173 | #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED 174 | typedef int YYSTYPE; 175 | # define yystype YYSTYPE /* obsolescent; will be withdrawn */ 176 | # define YYSTYPE_IS_DECLARED 1 177 | # define YYSTYPE_IS_TRIVIAL 1 178 | #endif 179 | 180 | 181 | 182 | #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED 183 | typedef struct YYLTYPE 184 | { 185 | int first_line; 186 | int first_column; 187 | int last_line; 188 | int last_column; 189 | } YYLTYPE; 190 | # define yyltype YYLTYPE /* obsolescent; will be withdrawn */ 191 | # define YYLTYPE_IS_DECLARED 1 192 | # define YYLTYPE_IS_TRIVIAL 1 193 | #endif 194 | 195 | 196 | -------------------------------------------------------------------------------- /proto.array.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "pstate.h" 6 | #include "error.h" 7 | #include "value.h" 8 | #include "proto.h" 9 | 10 | /* push */ 11 | static int arrpto_push(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 12 | { 13 | if (asc) die("Execute Array.prototype.push as constructor\n"); 14 | 15 | if (_this->vt != VT_OBJECT || !obj_isarray(_this->d.obj)) { 16 | value_make_number(*ret, 0); 17 | return 0; 18 | } 19 | 20 | int argc = value_get_length(args); 21 | int curlen = object_get_length(_this->d.obj); 22 | if (curlen < 0) { 23 | object_set_length(_this->d.obj, 0); 24 | } 25 | 26 | int i; 27 | for (i = 0; i < argc; ++i) { 28 | Value *v = value_new(); 29 | Value *ov = value_object_lookup_array(args, i, NULL); 30 | if (!ov) bug("Arguments error\n"); 31 | 32 | value_copy(*v, *ov); 33 | 34 | value_object_utils_insert_array(_this, curlen + i, v, 1, 1, 1); 35 | } 36 | 37 | value_make_number(*ret, object_get_length(_this->d.obj)); 38 | return 0; 39 | } 40 | 41 | /* pop */ 42 | static int arrpto_pop(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 43 | { 44 | if (asc) die("Execute Array.prototype.pop as constructor\n"); 45 | 46 | if (_this->vt != VT_OBJECT || !obj_isarray(_this->d.obj)) { 47 | value_make_number(*ret, 0); 48 | return 0; 49 | } 50 | 51 | int i = object_get_length(_this->d.obj) - 1; 52 | 53 | if (i >= 0) { 54 | Value *v = value_object_lookup_array(_this, i, NULL); 55 | if (v) { 56 | value_copy(*ret, *v); 57 | value_erase(*v); /* diff from ecma, not actually delete the key */ 58 | } 59 | object_set_length(_this->d.obj, i); 60 | return 0; 61 | } 62 | 63 | value_make_undef(*ret); 64 | return 0; 65 | } 66 | 67 | static struct st_arrpro_tab { 68 | const char *name; 69 | SSFunc func; 70 | } arrpro_funcs[] = { 71 | { "push", arrpto_push }, 72 | { "pop", arrpto_pop } 73 | }; 74 | 75 | void proto_array_init(Value *global) 76 | { 77 | if (!Array_prototype) bug("proto init failed?"); 78 | int i; 79 | for (i = 0; i < sizeof(arrpro_funcs) / sizeof(struct st_arrpro_tab); ++i) { 80 | Value *n = func_utils_make_func_value(arrpro_funcs[i].func); 81 | n->d.obj->__proto__ = Function_prototype; 82 | value_object_utils_insert(Array_prototype, tounichars(arrpro_funcs[i].name), n, 0, 0, 0); 83 | } 84 | } 85 | 86 | -------------------------------------------------------------------------------- /proto.array.h: -------------------------------------------------------------------------------- 1 | #ifndef __PROTO_ARRAY_H__ 2 | #define __PROTO_ARRAY_H__ 3 | 4 | struct Value; 5 | 6 | /* Array.prototype */ 7 | void proto_array_init(Value *global); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /proto.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "pstate.h" 6 | #include "error.h" 7 | #include "value.h" 8 | #include "proto.h" 9 | #include "regexp.h" 10 | #include "eval.h" 11 | 12 | #include "proto.string.h" 13 | #include "proto.number.h" 14 | #include "proto.array.h" 15 | 16 | Value *Object_prototype; 17 | static Value *Function_prototype_prototype; 18 | Value *Function_prototype; 19 | Value *String_prototype; 20 | Value *Number_prototype; 21 | Value *Boolean_prototype; 22 | Value *Array_prototype; 23 | Value *RegExp_prototype; 24 | Value *Top_object; 25 | 26 | /* Object constructor */ 27 | static int Object_constructor(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 28 | { 29 | if (asc) { 30 | /* new oprator will do the rest */ 31 | return 0; 32 | } 33 | 34 | if (value_get_length(args) <= 0) { 35 | Object *o = object_new(); 36 | o->__proto__ = Object_prototype; 37 | value_make_object(*ret, o); 38 | return 0; 39 | } 40 | Value *v = value_object_lookup_array(args, 0, NULL); 41 | if (!v || v->vt == VT_UNDEF || v->vt == VT_NULL) { 42 | Object *o = object_new(); 43 | o->__proto__ = Object_prototype; 44 | value_make_object(*ret, o); 45 | return 0; 46 | } 47 | value_copy(*ret, *v); 48 | value_toobject(ret); 49 | return 0; 50 | } 51 | 52 | /* Function.prototype pointed to a empty function */ 53 | static int Function_prototype_constructor(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 54 | { 55 | return 0; 56 | } 57 | 58 | static int Function_constructor(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 59 | { 60 | if (asc) { /* todo, parse the argument, return the new function obj */ 61 | _this->d.obj->ot = OT_FUNCTION; 62 | return 0; 63 | } 64 | Object *o = object_new(); 65 | o->ot = OT_FUNCTION; 66 | o->__proto__ = Function_prototype; 67 | value_make_object(*ret, o); 68 | return 0; 69 | } 70 | 71 | /* delete array[0], array[1]->array[0] */ 72 | static void value_array_shift(Value *v) 73 | { 74 | if (v->vt != VT_OBJECT) bug("value_array_shift, target is not object\n"); 75 | 76 | int len = object_get_length(v->d.obj); 77 | if (len <= 0) return; 78 | 79 | Value *v0 = value_object_lookup_array(v, 0, NULL); 80 | if (!v0) return; 81 | 82 | value_erase(*v0); 83 | 84 | int i; 85 | Value *last = v0; 86 | for (i = 1; i < len; ++i) { 87 | Value *t = value_object_lookup_array(v, i, NULL); 88 | if (!t) return; 89 | value_copy(*last, *t); 90 | value_erase(*t); 91 | last = t; 92 | } 93 | 94 | object_set_length(v->d.obj, len - 1); 95 | } 96 | 97 | void fcall_shared_arguments(Value *args, strs *argnames) 98 | { 99 | int i; 100 | if (!argnames) return; 101 | 102 | for (i = 0; i < argnames->count; ++i) { 103 | const unichar *argkey = strs_get(argnames, i); 104 | if (!argkey) break; 105 | 106 | Value *v = value_object_lookup_array(args, i, NULL); 107 | if (v) { 108 | ObjKey *strkey = objkey_new(argkey, OM_DONTEMU | OM_INNERSHARED); 109 | value_object_insert(args, strkey, v); 110 | } else { 111 | ObjKey *strkey = objkey_new(argkey, OM_DONTEMU); 112 | value_object_insert(args, strkey, value_new()); 113 | } 114 | } 115 | } 116 | 117 | static UNISTR(8) _CALLEE_ = { 8, { 1, 'c', 'a', 'l', 'l', 'e', 'e', 1 } }; 118 | static UNISTR(0) EMPTY = { 0, { 0 } }; 119 | 120 | void fcall_set_callee(Value *args, Value *tocall) 121 | { 122 | Value *callee = value_new(); 123 | value_copy(*callee, *tocall); 124 | value_object_utils_insert(args, _CALLEE_.unistr, callee, 0, 0, 0); 125 | } 126 | 127 | 128 | /* Function.prototype.call */ 129 | static int Function_prototype_call(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 130 | { 131 | if (asc) die("Execute call as constructor\n"); 132 | 133 | Value *tocall = _this; 134 | if (tocall->vt != VT_OBJECT || tocall->d.obj->ot != OT_FUNCTION) { 135 | die("can not execute expression, expression is not a function\n"); 136 | } 137 | 138 | if (!tocall->d.obj->d.fobj) { /* empty function */ 139 | return 0; 140 | } 141 | 142 | /* func to call */ 143 | Func *fstatic = tocall->d.obj->d.fobj->func; 144 | 145 | /* new this */ 146 | Value newthis = {0}; 147 | Value *arg1 = NULL; 148 | if ((arg1 = value_object_lookup_array(args, 0, NULL))) { 149 | value_copy(newthis, *arg1); 150 | value_toobject(&newthis); 151 | } else { 152 | value_copy(newthis, *Top_object); 153 | } 154 | 155 | /* prepare args */ 156 | value_array_shift(args); 157 | fcall_shared_arguments(args, fstatic->argnames); 158 | 159 | func_init_localvar(args, fstatic); 160 | fcall_set_callee(args, tocall); 161 | 162 | int res = 0; 163 | if (fstatic->type == FC_NORMAL) { 164 | res = eval(ps, fstatic->exec.opcodes, tocall->d.obj->d.fobj->scope, 165 | args, &newthis, ret); 166 | } else { 167 | res = fstatic->exec.callback(ps, args, &newthis, ret, 0); 168 | } 169 | 170 | value_erase(newthis); 171 | return res; 172 | } 173 | 174 | /* Function.prototype.apply */ 175 | static int Function_prototype_apply(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 176 | { 177 | if (asc) die("Execute apply as constructor\n"); 178 | 179 | Value *tocall = _this; 180 | if (tocall->vt != VT_OBJECT || tocall->d.obj->ot != OT_FUNCTION) { 181 | die("can not execute expression, expression is not a function\n"); 182 | } 183 | 184 | if (!tocall->d.obj->d.fobj) { /* empty function */ 185 | return 0; 186 | } 187 | 188 | /* func to call */ 189 | Func *fstatic = tocall->d.obj->d.fobj->func; 190 | 191 | /* new this */ 192 | Value newthis = {0}; 193 | Value *arg1 = NULL; 194 | if ((arg1 = value_object_lookup_array(args, 0, NULL))) { 195 | value_copy(newthis, *arg1); 196 | value_toobject(&newthis); 197 | } else { 198 | value_copy(newthis, *Top_object); 199 | } 200 | 201 | /* prepare args */ 202 | Value *newscope = value_object_lookup_array(args, 1, NULL); 203 | if (newscope) { 204 | if (newscope->vt != VT_OBJECT || !obj_isarray(newscope->d.obj)) { 205 | die("second argument to Function.prototype.apply must be an array\n"); 206 | } 207 | } else { 208 | newscope = value_object_utils_new_object(); 209 | object_set_length(newscope->d.obj, 0); 210 | } 211 | 212 | fcall_shared_arguments(newscope, fstatic->argnames); 213 | func_init_localvar(newscope, fstatic); 214 | fcall_set_callee(newscope, tocall); 215 | 216 | int res = 0; 217 | if (fstatic->type == FC_NORMAL) { 218 | res = eval(ps, fstatic->exec.opcodes, tocall->d.obj->d.fobj->scope, 219 | newscope, &newthis, ret); 220 | } else { 221 | res = fstatic->exec.callback(ps, newscope, &newthis, ret, 0); 222 | } 223 | value_erase(newthis); 224 | return res; 225 | } 226 | 227 | static int String_constructor(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 228 | { 229 | if (asc) { 230 | const unichar *nv = EMPTY.unistr; 231 | if (value_get_length(args) > 0) { 232 | Value *v = value_object_lookup_array(args, 0, NULL); 233 | if (v) { 234 | value_tostring(v); 235 | nv = v->d.str; 236 | } 237 | } 238 | _this->d.obj->ot = OT_STRING; 239 | _this->d.obj->d.str = unistrdup(nv); 240 | object_set_length(_this->d.obj, 0); 241 | 242 | int i; 243 | int len = unistrlen(nv); 244 | for (i = 0; i < len; ++i) { 245 | Value *v = value_new(); 246 | value_make_string(*v, unisubstrdup(nv, i, 1)); 247 | 248 | object_utils_insert_array(_this->d.obj, i, v, 0, 0, 1); 249 | } 250 | 251 | return 0; 252 | } 253 | if (value_get_length(args) > 0) { 254 | Value *v = value_object_lookup_array(args, 0, NULL); 255 | if (v) { 256 | value_copy(*ret, *v); 257 | value_tostring(ret); 258 | return 0; 259 | } 260 | } 261 | value_make_string(*ret, unistrdup(EMPTY.unistr)); 262 | return 0; 263 | } 264 | 265 | static int String_fromCharCode(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 266 | { 267 | UNISTR(4096) unibuf; 268 | 269 | if (asc) die("Can not execute String.fromCharCode as a constructor\n"); 270 | 271 | int len = value_get_length(args); 272 | int i; 273 | 274 | if (len > 4096) len = 4096; 275 | 276 | unibuf.len = len; 277 | unichar *u = unibuf.unistr; 278 | 279 | for (i = 0; i < len; ++i) { 280 | Value *v = value_object_lookup_array(args, i, NULL); 281 | if (!v) bug("Arguments error\n"); 282 | 283 | value_tonumber(v); 284 | u[i] = (unichar) v->d.num; 285 | } 286 | value_make_string(*ret, unistrdup(unibuf.unistr)); 287 | return 0; 288 | } 289 | 290 | static int Number_constructor(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 291 | { 292 | if (asc) { 293 | double nv = 0.0; 294 | if (value_get_length(args) > 0) { 295 | Value *v = value_object_lookup_array(args, 0, NULL); 296 | if (v) { 297 | value_tonumber(v); 298 | nv = v->d.num; 299 | } 300 | } 301 | _this->d.obj->ot = OT_NUMBER; 302 | _this->d.obj->d.num = nv; 303 | return 0; 304 | } 305 | if (value_get_length(args) > 0) { 306 | Value *v = value_object_lookup_array(args, 0, NULL); 307 | if (v) { 308 | value_copy(*ret, *v); 309 | value_tonumber(ret); 310 | return 0; 311 | } 312 | } 313 | value_make_number(*ret, 0.0); 314 | return 0; 315 | } 316 | 317 | static int Array_constructor(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 318 | { 319 | int argc = value_get_length(args); 320 | Value *target; 321 | 322 | if (asc) target = _this; 323 | else { 324 | Object *o = object_new(); 325 | o->__proto__ = Array_prototype; 326 | value_make_object(*ret, o); 327 | target = ret; 328 | } 329 | 330 | if (argc == 1) { 331 | Value *v = value_object_lookup_array(args, 0, NULL); 332 | if (v && is_number(v)) { 333 | if (!is_integer(v->d.num) || v->d.num < 0) die("Invalid array length\n"); 334 | object_set_length(target->d.obj, (int)v->d.num); 335 | return 0; 336 | } 337 | } 338 | 339 | int i; 340 | object_set_length(target->d.obj, 0); 341 | 342 | for (i = 0; i < argc; ++i) { 343 | Value *v = value_new(); 344 | Value *argv = value_object_lookup_array(args, i, NULL); 345 | 346 | value_copy(*v, *argv); 347 | 348 | value_object_utils_insert_array(_this, i, v, 1, 1, 1); 349 | } 350 | return 0; 351 | } 352 | 353 | static int Boolean_constructor(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 354 | { 355 | if (asc) { 356 | int nv = 0; 357 | if (value_get_length(args) > 0) { 358 | Value *v = value_object_lookup_array(args, 0, NULL); 359 | if (v) { 360 | nv = value_istrue(v); 361 | } 362 | } 363 | _this->d.obj->ot = OT_BOOL; 364 | _this->d.obj->d.val = nv; 365 | return 0; 366 | } 367 | if (value_get_length(args) > 0) { 368 | Value *v = value_object_lookup_array(args, 0, NULL); 369 | if (v) { 370 | value_make_bool(*ret, value_istrue(v)); 371 | return 0; 372 | } 373 | } 374 | value_make_bool(*ret, 0); 375 | return 0; 376 | } 377 | 378 | static int RegExp_constructor(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 379 | { 380 | Value *target; 381 | 382 | if (asc) target = _this; 383 | else { 384 | Object *o = object_new(); 385 | o->__proto__ = RegExp_prototype; 386 | value_make_object(*ret, o); 387 | target = ret; 388 | } 389 | 390 | Value *v = value_object_lookup_array(args, 0, NULL); 391 | const unichar *regtxt = EMPTY.unistr; 392 | if (v) { 393 | if (v->vt == VT_OBJECT && v->d.obj->ot == OT_REGEXP) { 394 | value_copy(*target, *v); 395 | return 0; 396 | } else if (v->vt == VT_STRING) { 397 | regtxt = v->d.str; 398 | } /* todo tostring */ 399 | } 400 | 401 | int flag = REG_EXTENDED; 402 | Value *f = value_object_lookup_array(args, 1, NULL); 403 | if (f && f->vt == VT_STRING) { 404 | if (unistrchr(f->d.str, 'i')) flag |= REG_ICASE; 405 | } 406 | 407 | target->d.obj->d.robj = regex_new(tochars(regtxt), flag);; 408 | target->d.obj->ot = OT_REGEXP; 409 | return 0; 410 | } 411 | 412 | void proto_init(Value *global) 413 | { 414 | /* object_prototype the start of protochain */ 415 | Object_prototype = value_object_utils_new_object(); 416 | 417 | /* Top, the default "this" value, pointed to global, is an object */ 418 | Top_object = global; 419 | Top_object->d.obj->__proto__ = Object_prototype; 420 | 421 | /* Function.prototype.prototype is a common object */ 422 | Function_prototype_prototype = value_object_utils_new_object(); 423 | Function_prototype_prototype->d.obj->__proto__ = Object_prototype; 424 | 425 | /* Function.prototype.__proto__ pointed to Object.prototype */ 426 | Function_prototype = func_utils_make_func_value(Function_prototype_constructor); 427 | value_object_utils_insert(Function_prototype, tounichars("prototype"), 428 | Function_prototype_prototype, 0, 0, 0); 429 | Function_prototype->d.obj->__proto__ = Object_prototype; 430 | 431 | /* Function prototype.call */ 432 | Value *_Function_p_call = func_utils_make_func_value(Function_prototype_call); 433 | value_object_utils_insert(Function_prototype, tounichars("call"), 434 | _Function_p_call, 0, 0, 0); 435 | _Function_p_call->d.obj->__proto__ = Function_prototype; 436 | 437 | /* Function prototype.apply */ 438 | Value *_Function_p_apply = func_utils_make_func_value(Function_prototype_apply); 439 | value_object_utils_insert(Function_prototype, tounichars("apply"), 440 | _Function_p_apply, 0, 0, 0); 441 | _Function_p_apply->d.obj->__proto__ = Function_prototype; 442 | 443 | /* Object.__proto__ pointed to Function.prototype */ 444 | Value *_Object = func_utils_make_func_value(Object_constructor); 445 | value_object_utils_insert(_Object, tounichars("prototype"), Object_prototype, 0, 0, 0); 446 | _Object->d.obj->__proto__ = Function_prototype; 447 | 448 | /* both Function.prototype,__proto__ pointed to Function.prototype */ 449 | Value *_Function = func_utils_make_func_value(Function_constructor); 450 | value_object_utils_insert(_Function, tounichars("prototype"), Function_prototype, 0, 0, 0); 451 | _Function->d.obj->__proto__ = Function_prototype; 452 | 453 | /* String.prototype is a common object */ 454 | String_prototype = value_object_utils_new_object(); 455 | String_prototype->d.obj->__proto__ = Object_prototype; 456 | 457 | Value *_String = func_utils_make_func_value(String_constructor); 458 | value_object_utils_insert(_String, tounichars("prototype"), String_prototype, 0, 0, 0); 459 | _String->d.obj->__proto__ = Function_prototype; 460 | Value *_String_fcc = func_utils_make_func_value(String_fromCharCode); 461 | _String_fcc->d.obj->__proto__ = Function_prototype; 462 | value_object_utils_insert(_String, tounichars("fromCharCode"), _String_fcc, 0, 0, 0); 463 | 464 | Number_prototype= value_object_utils_new_object(); 465 | Number_prototype->d.obj->__proto__ = Object_prototype; 466 | 467 | Value *_Number = func_utils_make_func_value(Number_constructor); 468 | value_object_utils_insert(_Number, tounichars("prototype"), Number_prototype, 0, 0, 0); 469 | _Number->d.obj->__proto__ = Function_prototype; 470 | 471 | Boolean_prototype= value_object_utils_new_object(); 472 | Boolean_prototype->d.obj->__proto__ = Object_prototype; 473 | 474 | Value *_Boolean = func_utils_make_func_value(Boolean_constructor); 475 | value_object_utils_insert(_Boolean, tounichars("prototype"), Boolean_prototype, 0, 0, 0); 476 | _Boolean->d.obj->__proto__ = Function_prototype; 477 | 478 | Array_prototype= value_object_utils_new_object(); 479 | Array_prototype->d.obj->__proto__ = Object_prototype; 480 | object_set_length(Array_prototype->d.obj, 0); 481 | 482 | Value *_Array = func_utils_make_func_value(Array_constructor); 483 | value_object_utils_insert(_Array, tounichars("prototype"), Array_prototype, 0, 0, 0); 484 | _Array->d.obj->__proto__ = Function_prototype; 485 | 486 | RegExp_prototype = value_object_utils_new_object(); 487 | RegExp_prototype->d.obj->__proto__ = Object_prototype; 488 | 489 | Value *_RegExp = func_utils_make_func_value(RegExp_constructor); 490 | value_object_utils_insert(_RegExp, tounichars("prototype"), RegExp_prototype, 0, 0, 0); 491 | _RegExp->d.obj->__proto__ = Function_prototype; 492 | 493 | value_object_utils_insert(global, tounichars("Object"), _Object, 1, 1, 0); 494 | value_object_utils_insert(global, tounichars("Function"), _Function, 1, 1, 0); 495 | value_object_utils_insert(global, tounichars("String"), _String, 1, 1, 0); 496 | value_object_utils_insert(global, tounichars("Number"), _Number, 1, 1, 0); 497 | value_object_utils_insert(global, tounichars("Boolean"), _Boolean, 1, 1, 0); 498 | value_object_utils_insert(global, tounichars("Array"), _Array, 1, 1, 0); 499 | value_object_utils_insert(global, tounichars("RegExp"), _RegExp, 1, 1, 0); 500 | 501 | proto_string_init(global); 502 | proto_number_init(global); 503 | proto_array_init(global); 504 | } 505 | 506 | -------------------------------------------------------------------------------- /proto.h: -------------------------------------------------------------------------------- 1 | #ifndef __PROTO_H__ 2 | #define __PROTO_H__ 3 | 4 | struct Value; 5 | struct strs; 6 | 7 | /* prototype init to global naming space, return Top_object - the default 'this' value */ 8 | void proto_init(struct Value *global); 9 | 10 | extern struct Value *Object_prototype; 11 | extern struct Value *Function_prototype; 12 | extern struct Value *String_prototype; 13 | extern struct Value *Number_prototype; 14 | extern struct Value *Boolean_prototype; 15 | extern struct Value *Array_prototype; 16 | extern struct Value *RegExp_prototype; 17 | extern struct Value *Top_object; 18 | 19 | void fcall_shared_arguments(struct Value *args, struct strs *argnames); 20 | void fcall_set_callee(Value *args, Value *tocall); 21 | 22 | #endif 23 | 24 | -------------------------------------------------------------------------------- /proto.number.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "pstate.h" 6 | #include "error.h" 7 | #include "value.h" 8 | #include "proto.h" 9 | 10 | static struct st_numpro_tab { 11 | const char *name; 12 | SSFunc func; 13 | } numpro_funcs[] = { 14 | }; 15 | 16 | void proto_number_init(Value *global) 17 | { 18 | if (!Number_prototype) bug("proto init failed?"); 19 | int i; 20 | for (i = 0; i < sizeof(numpro_funcs) / sizeof(struct st_numpro_tab); ++i) { 21 | Value *n = func_utils_make_func_value(numpro_funcs[i].func); 22 | n->d.obj->__proto__ = Function_prototype; 23 | value_object_utils_insert(Number_prototype, tounichars(numpro_funcs[i].name), n, 0, 0, 0); 24 | } 25 | 26 | 27 | Value *NaN = value_new(); 28 | value_make_number(*NaN, ieee_makenan()); 29 | 30 | Value *Inf = value_new(); 31 | value_make_number(*Inf, ieee_makeinf(1)); 32 | 33 | value_object_utils_insert(global, tounichars("NaN"), NaN, 0, 0, 0); 34 | value_object_utils_insert(global, tounichars("Infinity"), Inf, 0, 0, 0); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /proto.number.h: -------------------------------------------------------------------------------- 1 | #ifndef __PROTO_NUMBER_H__ 2 | #define __PROTO_NUMBER_H__ 3 | 4 | struct Value; 5 | 6 | /* Number.prototype */ 7 | void proto_number_init(struct Value *global); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /proto.string.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "pstate.h" 6 | #include "error.h" 7 | #include "value.h" 8 | #include "proto.h" 9 | #include "regexp.h" 10 | 11 | #define MAX_SUBREGEX 256 12 | 13 | /* substr */ 14 | static int strpto_substr(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 15 | { 16 | if (asc) die("Execute String.prototype.substr as constructor\n"); 17 | if (_this->vt != VT_OBJECT || _this->d.obj->ot != OT_STRING) { 18 | die("apply String.prototype.substr to a non-string object\n"); 19 | } 20 | 21 | unichar *v = _this->d.obj->d.str; 22 | Value *start = value_object_lookup_array(args, 0, NULL); 23 | Value *len = value_object_lookup_array(args, 1, NULL); 24 | 25 | if (!start || !is_number(start)) { 26 | value_make_string(*ret, unistrdup(v)); 27 | return 0; 28 | } 29 | int istart = (int) start->d.num; 30 | 31 | if (!len || !is_number(len)) { 32 | value_make_string(*ret, unisubstrdup(v, istart, -1)); 33 | return 0; 34 | } 35 | int ilen = (int) len->d.num; 36 | if (ilen <= 0) { 37 | value_make_string(*ret, unistrdup_str("")); 38 | } else { 39 | value_make_string(*ret, unisubstrdup(v, istart, ilen)); 40 | } 41 | return 0; 42 | } 43 | 44 | /* indexOf */ 45 | /* todo regex */ 46 | static int strpto_indexOf(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 47 | { 48 | if (asc) die("Execute String.prototype.indexOf as constructor\n"); 49 | if (_this->vt != VT_OBJECT || _this->d.obj->ot != OT_STRING) { 50 | die("apply String.prototype.indexOf to a non-string object\n"); 51 | } 52 | 53 | unichar *v = _this->d.obj->d.str; 54 | Value *seq = value_object_lookup_array(args, 0, NULL); 55 | Value *start = value_object_lookup_array(args, 1, NULL); 56 | 57 | if (!seq) { 58 | value_make_number(*ret, -1); 59 | return 0; 60 | } 61 | 62 | value_tostring(seq); 63 | unichar *vseq = seq->d.str; 64 | 65 | int istart = 0; 66 | if (start && is_number(start)) { 67 | istart = (int) start->d.num; 68 | if (istart < 0) istart = 0; 69 | } 70 | 71 | int r = unistrpos(v, istart, vseq); 72 | value_make_number(*ret, r); 73 | 74 | return 0; 75 | } 76 | 77 | static UNISTR(5) INDEX = { 5, { 'i', 'n', 'd', 'e', 'x' } }; 78 | static UNISTR(5) INPUT = { 5, { 'i', 'n', 'p', 'u', 't' } }; 79 | 80 | 81 | /* match */ 82 | static int strpto_match(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 83 | { 84 | if (asc) die("Execute String.prototype.match as constructor\n"); 85 | if (_this->vt != VT_OBJECT || _this->d.obj->ot != OT_STRING) { 86 | die("apply String.prototype.match to a non-string object\n"); 87 | } 88 | 89 | unichar *v = _this->d.obj->d.str; 90 | Value *seq = value_object_lookup_array(args, 0, NULL); 91 | 92 | if (!seq || seq->vt != VT_OBJECT || seq->d.obj->ot != OT_REGEXP) { 93 | value_make_null(*ret); 94 | return 0; 95 | } 96 | 97 | regex_t *reg = seq->d.obj->d.robj; 98 | 99 | regmatch_t pos[MAX_SUBREGEX]; 100 | memset(&pos, 0, MAX_SUBREGEX * sizeof(regmatch_t)); 101 | int r; 102 | if ((r = regexec(reg, tochars(v), MAX_SUBREGEX, pos, 0)) != 0) { 103 | if (r == REG_NOMATCH) { 104 | value_make_null(*ret); 105 | return 0; 106 | } else die("Out of memory\n"); 107 | } 108 | 109 | Object *obj = object_new(); 110 | obj->__proto__ = Array_prototype; 111 | value_make_object(*ret, obj); 112 | object_set_length(ret->d.obj, 0); 113 | 114 | int i; 115 | for (i = 0; i < MAX_SUBREGEX; ++i) { 116 | if (pos[i].rm_so <= 0 && pos[i].rm_eo <= 0) break; 117 | 118 | Value *val = value_new(); 119 | value_make_string(*val, 120 | unisubstrdup(v, pos[i].rm_so, pos[i].rm_eo - pos[i].rm_so)); 121 | value_object_utils_insert_array(ret, i, val, 1, 1, 1); 122 | } 123 | 124 | Value *vind = value_new(); 125 | value_make_number(*vind, pos[0].rm_so); 126 | value_object_utils_insert(ret, INDEX.unistr, vind, 1, 1, 1); 127 | Value *vinput = value_new(); 128 | value_make_string(*vinput, unistrdup(v)); 129 | value_object_utils_insert(ret, INPUT.unistr, vinput, 1, 1, 1); 130 | 131 | return 0; 132 | } 133 | 134 | /* charCodeAt */ 135 | static int strpto_charCodeAt(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 136 | { 137 | if (asc) die("Execute String.prototype.charCodeAt as constructor\n"); 138 | 139 | Value target = { 0 }; 140 | value_copy(target, *_this); 141 | value_tostring(&target); 142 | 143 | int slen = unistrlen(target.d.str); 144 | int pos = 0; 145 | Value *vpos; 146 | if ((vpos = value_object_lookup_array(args, 0, NULL))) { 147 | value_toint32(vpos); 148 | pos = (int)vpos->d.num; 149 | } 150 | 151 | if (pos < 0 || pos >= slen) { 152 | value_make_number(*ret, ieee_makenan()); 153 | } else { 154 | value_make_number(*ret, target.d.str[pos]); 155 | } 156 | value_erase(target); 157 | return 0; 158 | } 159 | 160 | static struct st_strpro_tab { 161 | const char *name; 162 | SSFunc func; 163 | } strpro_funcs[] = { 164 | { "substr", strpto_substr }, 165 | { "indexOf", strpto_indexOf }, 166 | { "match", strpto_match }, 167 | { "charCodeAt", strpto_charCodeAt } 168 | }; 169 | 170 | void proto_string_init(Value *global) 171 | { 172 | if (!String_prototype) bug("proto init failed?"); 173 | int i; 174 | for (i = 0; i < sizeof(strpro_funcs) / sizeof(struct st_strpro_tab); ++i) { 175 | Value *n = func_utils_make_func_value(strpro_funcs[i].func); 176 | n->d.obj->__proto__ = Function_prototype; 177 | value_object_utils_insert(String_prototype, tounichars(strpro_funcs[i].name), n, 0, 0, 0); 178 | } 179 | } 180 | 181 | -------------------------------------------------------------------------------- /proto.string.h: -------------------------------------------------------------------------------- 1 | #ifndef __PROTO_STRING_H__ 2 | #define __PROTO_STRING_H__ 3 | 4 | struct Value; 5 | 6 | /* String.prototype */ 7 | void proto_string_init(Value *global); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /pstate.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "lexer.h" 5 | #include "pstate.h" 6 | 7 | PSTATE *pstate_new_from_file(FILE *fp) 8 | { 9 | PSTATE *ps = malloc(sizeof(PSTATE)); 10 | memset(ps, 0, sizeof(PSTATE)); 11 | Lexer *l = malloc(sizeof(Lexer)); 12 | memset(l, 0, sizeof(Lexer)); 13 | 14 | ps->lexer = l; 15 | l->ltype = LT_FILE; 16 | l->d.fp = fp; 17 | rewind(fp); 18 | l->cur_line = 1; 19 | return ps; 20 | } 21 | 22 | PSTATE *pstate_new_from_string(const char *str) 23 | { 24 | PSTATE *ps = malloc(sizeof(PSTATE)); 25 | memset(ps, 0, sizeof(PSTATE)); 26 | Lexer *l = malloc(sizeof(Lexer)); 27 | memset(l, 0, sizeof(Lexer)); 28 | 29 | ps->lexer = l; 30 | l->ltype = LT_STRING; 31 | l->d.str = str; 32 | l->cur_line = 1; 33 | return ps; 34 | } 35 | 36 | void pstate_free(PSTATE *ps) 37 | { 38 | /* todo: free opcodes */ 39 | free(ps->lexer); 40 | free(ps); 41 | } 42 | 43 | -------------------------------------------------------------------------------- /pstate.h: -------------------------------------------------------------------------------- 1 | #ifndef __PSTAT_H__ 2 | #define __PSTAT_H__ 3 | 4 | #include 5 | #include "code.h" 6 | #include "value.h" 7 | 8 | /* Program state(context) */ 9 | typedef struct PSTATE { 10 | int err_count; /* error count after parse */ 11 | int eval_flag; /* currently execute in eval function */ 12 | struct OpCodes *opcodes; /* opcodes result(parsing result) */ 13 | struct Lexer *lexer; /* seq provider */ 14 | 15 | int _context_id; /* used in FastVar-locating */ 16 | Value last_exception; /* exception */ 17 | } PSTATE; 18 | 19 | PSTATE *pstate_new_from_file(FILE *fp); 20 | PSTATE *pstate_new_from_string(const char *str); 21 | void pstate_free(PSTATE *ps); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /rbtree.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010 the authors listed at the following URL, and/or 2 | the authors of referenced articles or incorporated external code: 3 | http://en.literateprograms.org/Red-black_tree_(C)?action=history&offset=20090121005050 4 | took too many lines, deleted 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #include "rbtree.h" 11 | #include "mempool.h" 12 | 13 | typedef rbtree_node node; 14 | typedef enum rbtree_node_color color; 15 | 16 | static rb_compare_func global_compare_func; 17 | static rb_vfree_func global_vfree_func; 18 | static rb_kfree_func global_kfree_func; 19 | static rb_vreplace_func global_vreplace_func; 20 | static rb_lookup_helper global_lookup_helper; 21 | static rb_insert_helper global_insert_helper; 22 | 23 | static mpool_t rbtree_pool; 24 | static mpool_t node_pool; 25 | 26 | static node grandparent(node n) { 27 | assert (n != NULL); 28 | assert (n->parent != NULL); /* Not the root node */ 29 | assert (n->parent->parent != NULL); /* Not child of root */ 30 | return n->parent->parent; 31 | } 32 | 33 | static node sibling(node n) { 34 | assert (n != NULL); 35 | assert (n->parent != NULL); /* Root node has no sibling */ 36 | if (n == n->parent->left) 37 | return n->parent->right; 38 | else 39 | return n->parent->left; 40 | } 41 | 42 | static node uncle(node n) { 43 | assert (n != NULL); 44 | assert (n->parent != NULL); /* Root node has no uncle */ 45 | assert (n->parent->parent != NULL); /* Children of root have no uncle */ 46 | return sibling(n->parent); 47 | } 48 | 49 | static color node_color(node n) { 50 | return n == NULL ? BLACK : n->color; 51 | } 52 | 53 | static node new_node(void* key, void* value, color node_color, node left, node right) { 54 | node result = mpool_alloc(node_pool); 55 | result->key = key; 56 | result->value = value; 57 | result->color = node_color; 58 | result->left = left; 59 | result->right = right; 60 | if (left != NULL) left->parent = result; 61 | if (right != NULL) right->parent = result; 62 | result->parent = NULL; 63 | return result; 64 | } 65 | 66 | static node lookup_node(rbtree t, void* key, rb_compare_func compare, void *userdata) { 67 | node n = t->root; 68 | while (n != NULL) { 69 | int comp_result = compare(key, n->key); 70 | if (comp_result == 0) { 71 | if (global_lookup_helper) global_lookup_helper(n->key, userdata); 72 | return n; 73 | } else if (comp_result < 0) { 74 | n = n->left; 75 | } else { 76 | assert(comp_result > 0); 77 | n = n->right; 78 | } 79 | } 80 | return n; 81 | } 82 | 83 | static void replace_node(rbtree t, node oldn, node newn) { 84 | if (oldn->parent == NULL) { 85 | t->root = newn; 86 | } else { 87 | if (oldn == oldn->parent->left) 88 | oldn->parent->left = newn; 89 | else 90 | oldn->parent->right = newn; 91 | } 92 | if (newn != NULL) { 93 | newn->parent = oldn->parent; 94 | } 95 | } 96 | 97 | static void rotate_left(rbtree t, node n) { 98 | node r = n->right; 99 | replace_node(t, n, r); 100 | n->right = r->left; 101 | if (r->left != NULL) { 102 | r->left->parent = n; 103 | } 104 | r->left = n; 105 | n->parent = r; 106 | } 107 | 108 | static void rotate_right(rbtree t, node n) { 109 | node L = n->left; 110 | replace_node(t, n, L); 111 | n->left = L->right; 112 | if (L->right != NULL) { 113 | L->right->parent = n; 114 | } 115 | L->right = n; 116 | n->parent = L; 117 | } 118 | 119 | static void insert_case5(rbtree t, node n) { 120 | n->parent->color = BLACK; 121 | grandparent(n)->color = RED; 122 | if (n == n->parent->left && n->parent == grandparent(n)->left) { 123 | rotate_right(t, grandparent(n)); 124 | } else { 125 | assert (n == n->parent->right && n->parent == grandparent(n)->right); 126 | rotate_left(t, grandparent(n)); 127 | } 128 | } 129 | 130 | static void insert_case4(rbtree t, node n) { 131 | if (n == n->parent->right && n->parent == grandparent(n)->left) { 132 | rotate_left(t, n->parent); 133 | n = n->left; 134 | } else if (n == n->parent->left && n->parent == grandparent(n)->right) { 135 | rotate_right(t, n->parent); 136 | n = n->right; 137 | } 138 | insert_case5(t, n); 139 | } 140 | 141 | static void insert_case1(rbtree t, node n); 142 | 143 | static void insert_case3(rbtree t, node n) { 144 | if (node_color(uncle(n)) == RED) { 145 | n->parent->color = BLACK; 146 | uncle(n)->color = BLACK; 147 | grandparent(n)->color = RED; 148 | insert_case1(t, grandparent(n)); 149 | } else { 150 | insert_case4(t, n); 151 | } 152 | } 153 | 154 | static void insert_case2(rbtree t, node n) { 155 | if (node_color(n->parent) == BLACK) 156 | return; /* Tree is still valid */ 157 | else 158 | insert_case3(t, n); 159 | } 160 | 161 | static void insert_case1(rbtree t, node n) { 162 | if (n->parent == NULL) 163 | n->color = BLACK; 164 | else 165 | insert_case2(t, n); 166 | } 167 | 168 | static node maximum_node(node n) { 169 | assert (n != NULL); 170 | while (n->right != NULL) { 171 | n = n->right; 172 | } 173 | return n; 174 | } 175 | 176 | static void delete_case6(rbtree t, node n) { 177 | sibling(n)->color = node_color(n->parent); 178 | n->parent->color = BLACK; 179 | if (n == n->parent->left) { 180 | assert (node_color(sibling(n)->right) == RED); 181 | sibling(n)->right->color = BLACK; 182 | rotate_left(t, n->parent); 183 | } 184 | else 185 | { 186 | assert (node_color(sibling(n)->left) == RED); 187 | sibling(n)->left->color = BLACK; 188 | rotate_right(t, n->parent); 189 | } 190 | } 191 | 192 | static void delete_case5(rbtree t, node n) { 193 | if (n == n->parent->left && 194 | node_color(sibling(n)) == BLACK && 195 | node_color(sibling(n)->left) == RED && 196 | node_color(sibling(n)->right) == BLACK) 197 | { 198 | sibling(n)->color = RED; 199 | sibling(n)->left->color = BLACK; 200 | rotate_right(t, sibling(n)); 201 | } 202 | else if (n == n->parent->right && 203 | node_color(sibling(n)) == BLACK && 204 | node_color(sibling(n)->right) == RED && 205 | node_color(sibling(n)->left) == BLACK) 206 | { 207 | sibling(n)->color = RED; 208 | sibling(n)->right->color = BLACK; 209 | rotate_left(t, sibling(n)); 210 | } 211 | delete_case6(t, n); 212 | } 213 | 214 | static void delete_case4(rbtree t, node n) { 215 | if (node_color(n->parent) == RED && 216 | node_color(sibling(n)) == BLACK && 217 | node_color(sibling(n)->left) == BLACK && 218 | node_color(sibling(n)->right) == BLACK) 219 | { 220 | sibling(n)->color = RED; 221 | n->parent->color = BLACK; 222 | } 223 | else 224 | delete_case5(t, n); 225 | } 226 | 227 | static void delete_case1(rbtree t, node n); 228 | static void delete_case3(rbtree t, node n) { 229 | if (node_color(n->parent) == BLACK && 230 | node_color(sibling(n)) == BLACK && 231 | node_color(sibling(n)->left) == BLACK && 232 | node_color(sibling(n)->right) == BLACK) 233 | { 234 | sibling(n)->color = RED; 235 | delete_case1(t, n->parent); 236 | } 237 | else 238 | delete_case4(t, n); 239 | } 240 | 241 | static void delete_case2(rbtree t, node n) { 242 | if (node_color(sibling(n)) == RED) { 243 | n->parent->color = RED; 244 | sibling(n)->color = BLACK; 245 | if (n == n->parent->left) 246 | rotate_left(t, n->parent); 247 | else 248 | rotate_right(t, n->parent); 249 | } 250 | delete_case3(t, n); 251 | } 252 | 253 | static void delete_case1(rbtree t, node n) { 254 | if (n->parent == NULL) 255 | return; 256 | else 257 | delete_case2(t, n); 258 | } 259 | 260 | rbtree rbtree_create() { 261 | rbtree t = mpool_alloc(rbtree_pool); 262 | t->root = NULL; 263 | return t; 264 | } 265 | 266 | void* rbtree_lookup(rbtree t, void* key, void *userdata) { 267 | node n = lookup_node(t, key, global_compare_func, userdata); 268 | return n == NULL ? NULL : n->value; 269 | } 270 | 271 | /* @return 0 - only replace existing key 272 | * 1 - successfully insert new node 273 | * -1 - error occour 274 | */ 275 | int rbtree_insert(rbtree t, void* key, void* value) { 276 | node inserted_node = new_node(key, value, RED, NULL, NULL); 277 | if (t->root == NULL) { 278 | t->root = inserted_node; 279 | } else { 280 | node n = t->root; 281 | while (1) { 282 | int comp_result = global_compare_func(key, n->key); 283 | if (comp_result == 0) { 284 | int ret = -1; 285 | if (!global_insert_helper(n->key)) { 286 | global_vreplace_func(n->value, value); 287 | ret = 0; 288 | } 289 | 290 | global_vfree_func(key, value); 291 | global_kfree_func(key); 292 | mpool_free (inserted_node, node_pool); 293 | return ret; 294 | } else if (comp_result < 0) { 295 | if (n->left == NULL) { 296 | n->left = inserted_node; 297 | break; 298 | } else { 299 | n = n->left; 300 | } 301 | } else { 302 | assert (comp_result > 0); 303 | if (n->right == NULL) { 304 | n->right = inserted_node; 305 | break; 306 | } else { 307 | n = n->right; 308 | } 309 | } 310 | } 311 | inserted_node->parent = n; 312 | } 313 | insert_case1(t, inserted_node); 314 | return 1; 315 | } 316 | 317 | void rbtree_delete(rbtree t, void* key) { 318 | node child; 319 | node n = lookup_node(t, key, global_compare_func, NULL); 320 | if (n == NULL) return; /* Key not found, do nothing */ 321 | void *keytodelete = n->key; 322 | void *valuetodelete = n->value; 323 | 324 | if (n->left != NULL && n->right != NULL) { 325 | /* Copy key/value from predecessor and then delete it instead */ 326 | node pred = maximum_node(n->left); 327 | 328 | n->key = pred->key; 329 | n->value = pred->value; 330 | n = pred; 331 | } 332 | 333 | assert(n->left == NULL || n->right == NULL); 334 | child = n->right == NULL ? n->left : n->right; 335 | if (node_color(n) == BLACK) { 336 | n->color = node_color(child); 337 | delete_case1(t, n); 338 | } 339 | replace_node(t, n, child); 340 | if (n->parent == NULL && child != NULL) 341 | child->color = BLACK; 342 | 343 | global_vfree_func(keytodelete, valuetodelete); 344 | global_kfree_func(keytodelete); 345 | mpool_free(n, node_pool); 346 | } 347 | 348 | int walk_tree_helper(rbtree_node n, void *userdata, rb_walk_callback callback) { 349 | if (n == NULL) return 0; 350 | 351 | if (n->right != NULL) walk_tree_helper(n->right, userdata, callback); 352 | if (callback(n->key, n->value, userdata)) return -1; 353 | if (n->left != NULL) walk_tree_helper(n->left, userdata, callback); 354 | 355 | return 0; 356 | } 357 | 358 | int rbtree_walk(rbtree t, void *userdata, rb_walk_callback callback) { 359 | return walk_tree_helper(t->root, userdata, callback); 360 | } 361 | 362 | void destroy_node(rbtree_node n) 363 | { 364 | if (n == NULL) return; 365 | if (n->right != NULL) destroy_node(n->right); 366 | if (n->left != NULL) destroy_node(n->left); 367 | global_vfree_func(n->key, n->value); 368 | global_kfree_func(n->key); 369 | mpool_free(n, node_pool); 370 | } 371 | 372 | void rbtree_destroy(rbtree t) 373 | { 374 | destroy_node(t->root); 375 | mpool_free(t, rbtree_pool); 376 | } 377 | 378 | void rbtree_module_init(rb_compare_func compare, 379 | rb_kfree_func kfreefunc, 380 | rb_vfree_func vfreefunc, 381 | rb_vreplace_func vreplacefunc, 382 | rb_lookup_helper lookuphelper, 383 | rb_insert_helper inserthelper) 384 | { 385 | rbtree_pool = mpool_create(sizeof(struct rbtree_t)); 386 | node_pool = mpool_create(sizeof(struct rbtree_node_t)); 387 | 388 | global_compare_func = compare; 389 | global_kfree_func = kfreefunc; 390 | global_vfree_func = vfreefunc; 391 | global_vreplace_func = vreplacefunc; 392 | global_lookup_helper = lookuphelper; 393 | global_insert_helper = inserthelper; 394 | } 395 | -------------------------------------------------------------------------------- /rbtree.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010 the authors listed at the following URL, and/or 2 | the authors of referenced articles or incorporated external code: 3 | 4 | Retrieved from: http://en.literateprograms.org/Red-black_tree_(C)?oldid=16016 5 | */ 6 | 7 | #ifndef _RBTREE_H_ 8 | 9 | enum rbtree_node_color { RED, BLACK }; 10 | 11 | typedef struct rbtree_node_t { 12 | void* key; 13 | void* value; 14 | struct rbtree_node_t* left; 15 | struct rbtree_node_t* right; 16 | struct rbtree_node_t* parent; 17 | enum rbtree_node_color color; 18 | } *rbtree_node; 19 | 20 | typedef struct rbtree_t { 21 | rbtree_node root; 22 | } *rbtree; 23 | 24 | typedef int (*rb_compare_func)(void* left, void* right); 25 | typedef void (*rb_vfree_func)(void *key, void* value); 26 | typedef void (*rb_kfree_func)(void* data); 27 | 28 | typedef void (*rb_lookup_helper)(void *key, void* userdata); 29 | typedef int (*rb_insert_helper)(void *key); 30 | 31 | typedef void (*rb_vreplace_func)(void *to, void* from); 32 | typedef int (*rb_walk_callback)(void *key, void *value, void *userdata); 33 | 34 | void rbtree_module_init(rb_compare_func compare, 35 | rb_kfree_func kfreefunc, 36 | rb_vfree_func vfreefunc, 37 | rb_vreplace_func vreplacefunc, 38 | rb_lookup_helper lookuphelper, 39 | rb_insert_helper inserthelper); 40 | rbtree rbtree_create(); 41 | void* rbtree_lookup(rbtree t, void* key, void *userdata); 42 | int rbtree_insert(rbtree t, void* key, void* value); 43 | void rbtree_delete(rbtree t, void* key); 44 | int rbtree_walk(rbtree t, void *userdata, rb_walk_callback callback); 45 | void rbtree_destroy(rbtree t); 46 | 47 | #endif 48 | 49 | -------------------------------------------------------------------------------- /regexp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "value.h" 7 | #include "regexp.h" 8 | #include "func.h" 9 | #include "error.h" 10 | 11 | regex_t *regex_new(const char *str, int compflag) 12 | { 13 | regex_t *reg = malloc(sizeof(regex_t)); 14 | if (!reg) die("Out of memory\n"); 15 | 16 | if (regcomp(reg, str, compflag)) 17 | die("Invalid regex string '%s'\n", str); 18 | 19 | return reg; 20 | } 21 | 22 | 23 | -------------------------------------------------------------------------------- /regexp.h: -------------------------------------------------------------------------------- 1 | #ifndef __REGEXP_H__ 2 | #define __REGEXP_H__ 3 | 4 | #include 5 | #include "unichar.h" 6 | 7 | regex_t *regex_new(const char *str, int compflag); 8 | 9 | #endif 10 | 11 | -------------------------------------------------------------------------------- /samples/99bottles.ss: -------------------------------------------------------------------------------- 1 | function song() { 2 | bottlesOfBeer = function(i) { return i+' bottles of beer'; }; 3 | bottlesOfBeerOnTheWall = function(i) { return this.bottlesOfBeer(i)+' on the wall'; }; 4 | takeOneDown = function() { return 'Take one down and pass it around, '; }; 5 | 6 | createVerse= function(first,second) { 7 | console.log(first); 8 | console.log(second); 9 | }; 10 | getNormalVerseFunction = function(i) { 11 | return function() { 12 | createVerse( 13 | bottlesOfBeerOnTheWall(i)+', '+bottlesOfBeer(i), 14 | takeOneDown()+bottlesOfBeerOnTheWall(i-1)+'.' 15 | ); 16 | }; 17 | }; 18 | 19 | verse = new Array(); 20 | 21 | for( var i = 3; i < 100; i++ ) 22 | verse[i] = getNormalVerseFunction(i); 23 | verse[2] = function() { 24 | createVerse( 25 | bottlesOfBeerOnTheWall(2)+', '+bottlesOfBeer(2), 26 | takeOneDown()+'1 bottle of beer.' 27 | ); 28 | }; 29 | verse[1] = function() { 30 | createVerse( 31 | '1 bottle of beer on the wall, 1 bottle of beer.', 32 | takeOneDown()+bottlesOfBeerOnTheWall('no more')+'.' 33 | ); 34 | }; 35 | verse[0] = function() { 36 | createVerse( 37 | bottlesOfBeerOnTheWall('No more')+', '+bottlesOfBeer('no more'), 38 | 'Go to the store and buy some more, '+bottlesOfBeerOnTheWall(99)+'.' 39 | ); 40 | }; 41 | 42 | this.getDom = function() { 43 | for( var i = 99; i >= 0 ; i-- ) { 44 | verse[i](); 45 | } 46 | }; 47 | }; 48 | 49 | new song().getDom(); -------------------------------------------------------------------------------- /samples/brainfuck.ss: -------------------------------------------------------------------------------- 1 | /* 2 | * javascript brainf*ck interpreter 3 | * by wenxichang@163.com 4 | */ 5 | 6 | function execute(code) 7 | { 8 | var mem = new Array(30000); 9 | var sp = 10000; 10 | var opcode = new String(code); 11 | var oplen = opcode.length; 12 | var ip = 0; 13 | var loopstack = new Array(); 14 | var output = ""; 15 | 16 | for (var i = 0; i < 30000; ++i) mem[i] = 0; 17 | //console.log(oplen); 18 | 19 | while (ip < oplen) { 20 | //console.log("ip = " + ip + ", sp = " + sp); 21 | //console.log(loopstack); 22 | //console.log(mem); 23 | //console.log(" " + opcode[ip]); 24 | switch(opcode[ip]) { 25 | case '+': 26 | mem[sp]++; 27 | break; 28 | case '-': 29 | mem[sp]--; 30 | break; 31 | case '>': 32 | sp++; 33 | break; 34 | case '<': 35 | sp--; 36 | break; 37 | case '.': 38 | if (mem[sp] != 10 && mem[sp] != 13) { 39 | output = output + String.fromCharCode(mem[sp]); 40 | } else { 41 | console.log(output); 42 | output = ""; 43 | } 44 | break; 45 | case ',': 46 | var s = console.input(); 47 | if (!s) exit(0); 48 | 49 | mem[sp] = s.charCodeAt(0); 50 | break; 51 | case '[': 52 | if (mem[sp]) { 53 | loopstack.push(ip); 54 | } else { 55 | for (var k = ip, j = 0; k < oplen; k++) { 56 | opcode[k] == '[' && j++; 57 | opcode[k] == ']' && j--; 58 | if (j == 0) break; 59 | } 60 | if (j == 0) ip = k; 61 | else { 62 | console.log("Unmatched loop"); 63 | return false; 64 | } 65 | } 66 | break; 67 | case ']': 68 | ip = loopstack.pop() - 1; 69 | break; 70 | default: 71 | break; 72 | } 73 | ip++; 74 | } 75 | return true; 76 | }; 77 | 78 | execute('\ 79 | ##########################\ 80 | ###\ 81 | ### Severely updated version!\ 82 | ### (now says "1 bottle" and\ 83 | ### contains no extra "0" verse)\ 84 | ###\ 85 | ##########################\ 86 | ### 99 Bottles of Beer ###\ 87 | ### coded in Brainfuck ###\ 88 | ### with explanations ###\ 89 | ##########################\ 90 | #\ 91 | # This Bottles of Beer program\ 92 | # was written by Andrew Paczkowski\ 93 | # Coder Alias: thepacz\ 94 | # three_halves_plus_one@yahoo.com\ 95 | #####\ 96 | \ 97 | > 0 in the zeroth cell\ 98 | +++++++>++++++++++[<+++++>-] 57 in the first cell or "9"\ 99 | +++++++>++++++++++[<+++++>-] 57 in second cell or "9"\ 100 | ++++++++++ 10 in third cell\ 101 | >+++++++++ 9 in fourth cell\ 102 | \ 103 | ##########################################\ 104 | ### create ASCII chars in higher cells ###\ 105 | ##########################################\ 106 | \ 107 | >>++++++++[<++++>-] " "\ 108 | >++++++++++++++[<+++++++>-] b\ 109 | +>+++++++++++[<++++++++++>-] o\ 110 | ++>+++++++++++++++++++[<++++++>-] t\ 111 | ++>+++++++++++++++++++[<++++++>-] t\ 112 | >++++++++++++[<+++++++++>-] l\ 113 | +>++++++++++[<++++++++++>-] e\ 114 | +>+++++++++++++++++++[<++++++>-] s\ 115 | >++++++++[<++++>-] " "\ 116 | +>+++++++++++[<++++++++++>-] o\ 117 | ++>++++++++++[<++++++++++>-] f\ 118 | >++++++++[<++++>-] " "\ 119 | >++++++++++++++[<+++++++>-] b\ 120 | +>++++++++++[<++++++++++>-] e\ 121 | +>++++++++++[<++++++++++>-] e\ 122 | >+++++++++++++++++++[<++++++>-] r\ 123 | >++++++++[<++++>-] " "\ 124 | +>+++++++++++[<++++++++++>-] o\ 125 | >+++++++++++[<++++++++++>-] n\ 126 | >++++++++[<++++>-] " "\ 127 | ++>+++++++++++++++++++[<++++++>-] t\ 128 | ++++>++++++++++[<++++++++++>-] h\ 129 | +>++++++++++[<++++++++++>-] e\ 130 | >++++++++[<++++>-] " "\ 131 | ++>+++++++++++++[<+++++++++>-] w\ 132 | +>++++++++++++[<++++++++>-] a\ 133 | >++++++++++++[<+++++++++>-] l\ 134 | >++++++++++++[<+++++++++>-] l\ 135 | >+++++[<++>-] LF\ 136 | ++>+++++++++++++++++++[<++++++>-] t\ 137 | +>++++++++++++[<++++++++>-] a\ 138 | +++>+++++++++++++[<++++++++>-] k\ 139 | +>++++++++++[<++++++++++>-] e\ 140 | >++++++++[<++++>-] " "\ 141 | +>+++++++++++[<++++++++++>-] o\ 142 | >+++++++++++[<++++++++++>-] n\ 143 | +>++++++++++[<++++++++++>-] e\ 144 | >++++++++[<++++>-] " "\ 145 | >++++++++++[<++++++++++>-] d\ 146 | +>+++++++++++[<++++++++++>-] o\ 147 | ++>+++++++++++++[<+++++++++>-] w\ 148 | >+++++++++++[<++++++++++>-] n\ 149 | >++++++++[<++++>-] " "\ 150 | +>++++++++++++[<++++++++>-] a\ 151 | >+++++++++++[<++++++++++>-] n\ 152 | >++++++++++[<++++++++++>-] d\ 153 | >++++++++[<++++>-] " "\ 154 | ++>+++++++++++[<++++++++++>-] p\ 155 | +>++++++++++++[<++++++++>-] a\ 156 | +>+++++++++++++++++++[<++++++>-] s\ 157 | +>+++++++++++++++++++[<++++++>-] s\ 158 | >++++++++[<++++>-] " "\ 159 | +>+++++++++++++[<++++++++>-] i\ 160 | ++>+++++++++++++++++++[<++++++>-] t\ 161 | >++++++++[<++++>-] " "\ 162 | +>++++++++++++[<++++++++>-] a\ 163 | >+++++++++++++++++++[<++++++>-] r\ 164 | +>+++++++++++[<++++++++++>-] o\ 165 | >+++++++++++++[<+++++++++>-] u\ 166 | >+++++++++++[<++++++++++>-] n\ 167 | >++++++++++[<++++++++++>-] d\ 168 | >+++++[<++>-] LF\ 169 | +++++++++++++ CR\ 170 | \ 171 | [<]>>>> go back to fourth cell\ 172 | \ 173 | #################################\ 174 | ### initiate the display loop ###\ 175 | #################################\ 176 | \ 177 | [ loop\ 178 | < back to cell 3\ 179 | [ loop\ 180 | [>]<< go to last cell and back to LF\ 181 | .. output 2 newlines\ 182 | [<]> go to first cell\ 183 | \ 184 | ###################################\ 185 | #### begin display of characters###\ 186 | ###################################\ 187 | #\ 188 | #.>.>>>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>\ 189 | #X X b o t t l e s o f b e e r \ 190 | #.>.>.>.>.>.>.>.>.>.>.>.\ 191 | #o n t h e w a l l N\ 192 | #[<]> go to first cell\ 193 | #.>.>>>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>>>>>>>>>>>>>.>\ 194 | #X X b o t t l e s o f b e e r N\ 195 | #.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>\ 196 | #t a k e o n e d o w n a n d p a s s \ 197 | #.>.>.>.>.>.>.>.>.>.\ 198 | #i t a r o u n d N\ 199 | #####\ 200 | \ 201 | [<]>> go to cell 2\ 202 | - subtract 1 from cell 2\ 203 | < go to cell 1\ 204 | \ 205 | ########################\ 206 | ### display last line ##\ 207 | ########################\ 208 | #\ 209 | #.>.>>>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>\ 210 | #X X b o t t l e s o f b e e r \ 211 | #.>.>.>.>.>.>.>.>.>.>.\ 212 | #o n t h e w a l l\ 213 | #####\ 214 | \ 215 | [<]>>>- go to cell 3/subtract 1\ 216 | ] end loop when cell 3 is 0\ 217 | ++++++++++ add 10 to cell 3\ 218 | <++++++++++ back to cell 2/add 10\ 219 | <- back to cell 1/subtract 1\ 220 | [>]<. go to last line/carriage return\ 221 | [<]> go to first line\ 222 | \ 223 | ########################\ 224 | ### correct last line ##\ 225 | ########################\ 226 | #\ 227 | #.>.>>>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>\ 228 | #X X b o t t l e s o f b e e r \ 229 | #.>.>.>.>.>.>.>.>.>.>.\ 230 | #o n t h e w a l l\ 231 | #####\ 232 | \ 233 | [<]>>>>- go to cell 4/subtract 1\ 234 | ] end loop when cell 4 is 0\ 235 | \ 236 | ##############################################################\ 237 | ### By this point verses 99\10 are displayed but to work ###\ 238 | ### with the lower numbered verses in a more readable way ###\ 239 | ### we initiate a new loop for verses 9{CODE} that will not ###\ 240 | ### use the fourth cell at all ###\ 241 | ##############################################################\ 242 | \ 243 | + add 1 to cell four (to keep it non\zero)\ 244 | <-- back to cell 3/subtract 2\ 245 | \ 246 | [ loop\ 247 | [>]<< go to last cell and back to LF\ 248 | .. output 2 newlines\ 249 | [<]> go to first cell\ 250 | \ 251 | ###################################\ 252 | #### begin display of characters###\ 253 | ###################################\ 254 | #\ 255 | #>.>>>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>\ 256 | # X b o t t l e s o f b e e r \ 257 | #.>.>.>.>.>.>.>.>.>.>.>.\ 258 | #o n t h e w a l l N\ 259 | #[<]> go to first cell\ 260 | #>.>>>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>>>>>>>>>>>>>.>\ 261 | # X b o t t l e s o f b e e r N\ 262 | #.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>\ 263 | #t a k e o n e d o w n a n d p a s s \ 264 | #.>.>.>.>.>.>.>.>.>.\ 265 | #i t a r o u n d N\ 266 | #####\ 267 | \ 268 | [<]>> go to cell 2\ 269 | - subtract 1 from cell 2\ 270 | \ 271 | ########################\ 272 | ### display last line ##\ 273 | ########################\ 274 | #\ 275 | #.>>>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>\ 276 | #X b o t t l e s o f b e e r \ 277 | #.>.>.>.>.>.>.>.>.>.>.\ 278 | #o n t h e w a l l\ 279 | #####\ 280 | \ 281 | [<]>>>- go to cell 3/subtract 1\ 282 | ] end loop when cell 3 is 0\ 283 | + add 1 to cell 3 to keep it non\zero\ 284 | \ 285 | [>]<. go to last line/carriage return\ 286 | [<]> go to first line\ 287 | \ 288 | ########################\ 289 | ### correct last line ##\ 290 | ########################\ 291 | #\ 292 | #>.>>>.>.>.>.>.>.>.>>.>.>.>.>.>.>.>.>.>\ 293 | # X b o t t l e o f b e e r \ 294 | #.>.>.>.>.>.>.>.>.>.>.<<<<.\ 295 | #o n t h e w a l l\ 296 | #####\ 297 | \ 298 | [>]<< go to last cell and back to LF\ 299 | .. output 2 newlines\ 300 | [<]> go to first line\ 301 | \ 302 | #########################\ 303 | ### the final verse ##\ 304 | #########################\ 305 | #\ 306 | #>.>>>.>.>.>.>.>.>.>>.>.>.>.>.>.>.>.>.>\ 307 | # X b o t t l e o f b e e r \ 308 | #.>.>.>.>.>.>.>.>.>.>.>.\ 309 | #o n t h e w a l l N\ 310 | #[<]> go to first cell\ 311 | #>.>>>.>.>.>.>.>.>.>>.>.>.>.>.>.>.>.>>>>>>>>>>>>>.>\ 312 | # X b o t t l e o f b e e r N\ 313 | #.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>\ 314 | #t a k e o n e d o w n a n d p a s s \ 315 | #.>.>.>.>.>.>.>.>.>.\ 316 | #i t a r o u n d N\ 317 | #[>]< go to last line\ 318 | #<<<.<<.<<<.\ 319 | # n o \ 320 | #[<]>>>> go to fourth cell\ 321 | #>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>\ 322 | # b o t t l e s o f b e e r \ 323 | #.>.>.>.>.>.>.>.>.>.>.>.\ 324 | #o n t h e w a l l N\ 325 | #####fin##\ 326 | '); 327 | 328 | execute("\ 329 | +++++ +++++ initialize counter (cell #0) to 10\ 330 | [ use loop to set the next four cells to 70/100/30/10\ 331 | > +++++ ++ add 7 to cell #1\ 332 | > +++++ +++++ add 10 to cell #2 \ 333 | > +++ add 3 to cell #3\ 334 | > + add 1 to cell #4\ 335 | <<<< - decrement counter (cell #0)\ 336 | ]\ 337 | > ++ . print 'H'\ 338 | > + . print 'e'\ 339 | +++++ ++ . print 'l'\ 340 | . print 'l'\ 341 | +++ . print 'o'\ 342 | > ++ . print ' '\ 343 | << +++++ +++++ +++++ . print 'W'\ 344 | > . print 'o'\ 345 | +++ . print 'r'\ 346 | ----- - . print 'l'\ 347 | ----- --- . print 'd'\ 348 | > + . print '!'\ 349 | > . print '\n'\ 350 | "); 351 | -------------------------------------------------------------------------------- /samples/contchkcmp.ss: -------------------------------------------------------------------------------- 1 | if (console.args.length < 2) { 2 | console.log("Usage: smallscript contchkcmp.ss "); 3 | exit(-1); 4 | } 5 | 6 | function parsefile(filename) 7 | { 8 | var file = new File(filename, "r"); 9 | var obj = {}; 10 | 11 | if (!file) { 12 | console.log("Can not open file: " + filename); 13 | exit(-1); 14 | } 15 | 16 | var curname = ""; 17 | var cursetion = {}; 18 | while ((line = file.gets()) != undefined) { 19 | var arr; 20 | if (line.match(/\[.*\]/)) { 21 | if (curname) { 22 | obj[curname] = cursetion; 23 | curname = ""; 24 | cursetion = {}; 25 | } 26 | } else if ((arr = line.match(/([a-zA-Z0-9]+)\ *=\ *(.*)/))) { 27 | cursetion[arr[1]] = arr[2]; 28 | if (arr[1] == 'Name') { 29 | curname = arr[2]; 30 | } 31 | } 32 | } 33 | if (curname) obj[curname] = cursetion; 34 | file.close(); 35 | return obj; 36 | }; 37 | 38 | var file1 = parsefile(console.args[0]); 39 | var file2 = parsefile(console.args[1]); 40 | var outfile = new File("report.txt", "w"); 41 | 42 | for (var sess1 in file1) { 43 | var se = file1[sess1]; 44 | if (!file2[sess1]) { 45 | outfile.puts("Rule missing: " + sess1 + " Not found at: " + console.args[1]); 46 | continue; 47 | } 48 | var te = file2[sess1]; 49 | 50 | for (var li in se) { 51 | if (se[li] != te[li]) { 52 | outfile.puts("Key diff: " + li + " at Rule: " + sess1); 53 | outfile.puts(" " + console.args[0] + ": " + se[li]); 54 | outfile.puts(" " + console.args[1] + ": " + te[li]); 55 | } 56 | delete te[li]; 57 | } 58 | for (var li in te) { 59 | if (se[li] != te[li]) { 60 | outfile.puts("Key diff: " + li + " at Rule: " + sess1); 61 | outfile.puts(" " + console.args[0] + ": " + se[li]); 62 | outfile.puts(" " + console.args[1] + ": " + te[li]); 63 | } 64 | } 65 | delete file2[sess1]; 66 | } 67 | 68 | for (var sess1 in file2) { 69 | if (!file1[sess1]) { 70 | outfile.puts("Rule missing: " + sess1 + " Not found at: " + console.args[0]); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /scope.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "error.h" 6 | #include "scope.h" 7 | 8 | #define MAX_SCOPE 4096 9 | 10 | strs *strs_new() 11 | { 12 | strs *ret = malloc(sizeof(strs)); 13 | memset(ret, 0, sizeof(strs)); 14 | return ret; 15 | } 16 | 17 | void strs_push(strs *ss, const unichar *string) 18 | { 19 | if (ss->count >= ss->_size) { 20 | ss->_size += 5; 21 | ss->strings = realloc(ss->strings, (ss->_size) * sizeof(unichar *)); 22 | } 23 | ss->strings[ss->count] = unistrdup(string); 24 | ss->count++; 25 | } 26 | 27 | strs *strs_dup(strs *ss) 28 | { 29 | strs *n = strs_new(); 30 | int i; 31 | if (!ss) return n; 32 | for (i = 0; i < ss->count; ++i) { 33 | strs_push(n, ss->strings[i]); 34 | } 35 | return n; 36 | } 37 | 38 | const unichar *strs_get(strs *ss, int i) 39 | { 40 | if (i < 0 || i >= ss->count) return NULL; 41 | return ss->strings[i]; 42 | } 43 | 44 | void strs_free(strs *ss) 45 | { 46 | if (!ss) return; 47 | 48 | int i; 49 | for (i = 0; i < ss->count; ++i) { 50 | unifree(ss->strings[i]); 51 | } 52 | free(ss->strings); 53 | free(ss); 54 | } 55 | 56 | /* lexical scope */ 57 | static strs *scopes[MAX_SCOPE]; 58 | static int cur_scope; 59 | 60 | void scope_push() 61 | { 62 | if (cur_scope >= MAX_SCOPE - 1) bug("Scope chain to short\n"); 63 | cur_scope++; 64 | } 65 | 66 | void scope_pop() 67 | { 68 | if (cur_scope <= 0) bug("No more scope to pop\n"); 69 | strs_free(scopes[cur_scope]); 70 | scopes[cur_scope] = NULL; 71 | cur_scope--; 72 | } 73 | 74 | void scope_add_var(const unichar *str) 75 | { 76 | int i; 77 | if (scopes[cur_scope] == NULL) scopes[cur_scope] = strs_new(); 78 | 79 | for (i = 0; i < scopes[cur_scope]->count; ++i) { 80 | if (unistrcmp(str, scopes[cur_scope]->strings[i]) == 0) return; 81 | } 82 | strs_push(scopes[cur_scope], str); 83 | } 84 | 85 | strs *scope_get_varlist() 86 | { 87 | return strs_dup(scopes[cur_scope]); 88 | } 89 | 90 | -------------------------------------------------------------------------------- /scope.h: -------------------------------------------------------------------------------- 1 | #ifndef __SCOPE_H__ 2 | #define __SCOPE_H__ 3 | 4 | #include "unichar.h" 5 | 6 | typedef struct strs { 7 | unichar **strings; 8 | int count; 9 | int _size; 10 | } strs; 11 | 12 | strs *strs_new(); 13 | void strs_push(strs *ss, const unichar *string); 14 | void strs_free(strs *ss); 15 | const unichar *strs_get(strs *ss, int i); 16 | 17 | /* what lexical scope means: 18 | * -------------- 19 | * var a; // this is first level of scope 20 | * function foo() { // parsing function make lexical scope to push 21 | * var b; // this is the second level 22 | * var c = function() { // push again 23 | * var d; // third level 24 | * } // end of an function, pop scope 25 | * } // return to first scope 26 | * -------------- 27 | */ 28 | void scope_push(); 29 | void scope_pop(); 30 | void scope_add_var(const unichar *str); 31 | strs *scope_get_varlist(); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /testsuit/99.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 1 * 1 = 1 4 | 1 * 2 = 2 5 | 1 * 3 = 3 6 | 1 * 4 = 4 7 | 1 * 5 = 5 8 | 1 * 6 = 6 9 | 1 * 7 = 7 10 | 3 * 3 = 9 11 | 3 * 4 = 12 12 | 3 * 5 = 15 13 | 3 * 6 = 18 14 | 3 * 7 = 21 15 | 5 * 5 = 25 16 | 5 * 6 = 30 17 | 5 * 7 = 35 18 | 7 * 7 = 49 19 | =!EXPECTEND!= 20 | */ 21 | 22 | for (i = 1; i < 10; i = i + 1) { 23 | if (i % 2 == 0) continue; 24 | for (j = i; j < 10; j = j + 1) { 25 | if (j > 7) break; 26 | console.log(i + " * " + j + " = " + (i * j)); 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /testsuit/arg.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 3 4 | [ "1", "2", "3" ] 5 | argv[0] = 1 6 | argv[1] = 2 7 | argv[2] = 3 8 | =!EXPECTEND!= 9 | */ 10 | console.log(console.args.length); 11 | console.log(console.args); 12 | 13 | for (i = 0; i < console.args.length; ++i) { 14 | console.log ("argv[" + i + "] = " + console.args[i]); 15 | } 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /testsuit/arg2.ss: -------------------------------------------------------------------------------- 1 | function a() { 2 | console.log('in a'); 3 | }; 4 | 5 | function b() { 6 | console.log('in b'); 7 | }; 8 | 9 | var c = a; 10 | 11 | c((c = b), 2); 12 | -------------------------------------------------------------------------------- /testsuit/arguments.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 1 4 | 2 5 | 3 6 | 4 7 | xfsldafkjasldf 8 | asdfsadfsadf 9 | 6 10 | =!EXPECTEND!= 11 | */ 12 | 13 | function a() { 14 | for (i = 0; i < arguments.length; ++i) { 15 | console.log(arguments[i]); 16 | } 17 | }; 18 | 19 | a(1,2,3,4,"xfsldafkjasldf","asdfsadfsadf"); 20 | console.log(i); -------------------------------------------------------------------------------- /testsuit/argumentshared.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 100 4 | =!EXPECTEND!= 5 | */ 6 | 7 | function a(x) { 8 | arguments[0] = 100; 9 | console.log(x); 10 | }; 11 | 12 | a(1); -------------------------------------------------------------------------------- /testsuit/bin/runtest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | RUNVALG=0 4 | 5 | if [ $# -lt 2 ]; then 6 | echo "Usage: runtest.sh [vg|-v]" 7 | exit 1 8 | fi 9 | 10 | if [ ! -x "$1" ]; then 11 | echo "$1 is not executable" 12 | exit 1 13 | fi 14 | 15 | if [ ! -d "$2" ]; then 16 | echo "$2 is not a directory" 17 | exit 1 18 | fi 19 | 20 | function functionaltest() 21 | { 22 | allfiles=`find "$2" -name "*.ss"` 23 | for file in $allfiles; do 24 | echo -n "Executing $file" 25 | awk '{ 26 | if ($0 ~ /^=\!EXPECTSTART\!=$/) start = 1; 27 | else if ($0 ~ /=\!EXPECTEND\!=/) start = 0; 28 | else if (start) print 29 | }' $file >/tmp/ss_expected 30 | 31 | echo -e "input\ntest\nwenxichang@163.com\n" | "$1" $file 1 2 3 >/tmp/ss_result 2>/dev/null 32 | 33 | if ! diff /tmp/ss_expected /tmp/ss_result >/dev/null; then 34 | DATA=`cat /tmp/ss_expected` 35 | if [ "$DATA" == "" ]; then CM='*'; else CM=' '; fi 36 | echo -e "\r[ FAILED ] $CM $file" 37 | if [ "$3" == "-v" ]; then 38 | echo "============compare=============" 39 | cat /tmp/ss_expected 40 | echo "--------------------------------" 41 | cat /tmp/ss_result 42 | echo "==============end===============" 43 | fi 44 | else 45 | echo -e "\r[ OK ] $file" 46 | fi 47 | done 48 | } 49 | 50 | function runvalgind() 51 | { 52 | allfiles=`find "$2" -name "*.ss"` 53 | rm -f valgrind.report.txt 54 | 55 | for file in $allfiles; do 56 | echo -n "Executing $file" 57 | echo "**************** $file ******************" >>valgrind.report.txt 58 | f=$(basename $file) 59 | if [ "$f" != "block.ss" -a "$f" != "block2.ss" ]; then 60 | echo -e "input\ntest\nwenxichang@163.com\n" | valgrind "$1" $file 1 2 3 >>valgrind.report.txt 2>&1 61 | fi 62 | echo -e "\r[ DONE ] $file" 63 | done 64 | echo "All done, report: valgrind.report.txt" 65 | } 66 | 67 | if [ "$3" == "vg" ]; then 68 | runvalgind "$1" "$2" 69 | else 70 | functionaltest "$1" "$2" "$3" 71 | fi 72 | -------------------------------------------------------------------------------- /testsuit/block.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 2 7 6 4 | 9 5 1 5 | 4 3 8 6 | -------- 7 | 2 9 4 8 | 7 5 3 9 | 6 1 8 10 | -------- 11 | 4 3 8 12 | 9 5 1 13 | 2 7 6 14 | -------- 15 | 4 9 2 16 | 3 5 7 17 | 8 1 6 18 | -------- 19 | 6 1 8 20 | 7 5 3 21 | 2 9 4 22 | -------- 23 | 6 7 2 24 | 1 5 9 25 | 8 3 4 26 | -------- 27 | 8 1 6 28 | 3 5 7 29 | 4 9 2 30 | -------- 31 | 8 3 4 32 | 1 5 9 33 | 6 7 2 34 | -------- 35 | =!EXPECTEND!= 36 | */ 37 | 38 | for (a = 1; a < 10; ++a) { 39 | for (b = 1; b < 10; ++b) { 40 | if (b == a) continue; 41 | for (c = 1; c < 10; ++c) { 42 | if (c == a || c == b) continue; 43 | for (d = 1; d < 10; ++d) { 44 | if (d == a || d == b || d == c) continue; 45 | for (e = 1; e < 10; ++e) { 46 | if (e == a || e == b || e == c || e == d) continue; 47 | for (f = 1; f < 10; ++f) { 48 | if (f == a || f == b || f == c || f == d || f == e) continue; 49 | for (g = 1; g < 10; ++g) { 50 | if (g == a || g == b || g == c || g == d || g == e || g == f) continue; 51 | for (h = 1; h < 10; ++h) { 52 | if (h == a || h == b || h == c || h == d || h == e || h == f || h == g) continue; 53 | for (i = 1; i < 10; ++i) { 54 | if (i == a || i == b || i == c || i == d || i == e || i == f || i == g || i == h) continue; 55 | 56 | if (a + b + c == 15 && 57 | d + e + f == 15 && 58 | g + h + i == 15 && 59 | a + d + g == 15 && 60 | b + e + h == 15 && 61 | c + f + i == 15 && 62 | a + e + i == 15 && 63 | c + e + g == 15) { 64 | console.log(a + " " + b + " " + c); 65 | console.log(d + " " + e + " " + f); 66 | console.log(g + " " + h + " " + i); 67 | console.log("--------"); 68 | } 69 | } 70 | } 71 | } 72 | } 73 | } 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /testsuit/block2.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 2 7 6 4 | 9 5 1 5 | 4 3 8 6 | -------- 7 | 2 9 4 8 | 7 5 3 9 | 6 1 8 10 | -------- 11 | 4 3 8 12 | 9 5 1 13 | 2 7 6 14 | -------- 15 | 4 9 2 16 | 3 5 7 17 | 8 1 6 18 | -------- 19 | 6 1 8 20 | 7 5 3 21 | 2 9 4 22 | -------- 23 | 6 7 2 24 | 1 5 9 25 | 8 3 4 26 | -------- 27 | 8 1 6 28 | 3 5 7 29 | 4 9 2 30 | -------- 31 | 8 3 4 32 | 1 5 9 33 | 6 7 2 34 | -------- 35 | =!EXPECTEND!= 36 | */ 37 | 38 | for (a = 1; a < 10; ++a) { 39 | for (b = 1; b < 10; ++b) { 40 | if (b == a) continue; 41 | for (c = 1; c < 10; ++c) { 42 | if (c == a) continue; 43 | if (c == b) continue; 44 | for (d = 1; d < 10; ++d) { 45 | if (d == a) continue; 46 | if (d == b) continue; 47 | if (d == c) continue; 48 | for (e = 1; e < 10; ++e) { 49 | if (e == a) continue; 50 | if (e == b) continue; 51 | if (e == c) continue; 52 | if (e == d) continue; 53 | for (f = 1; f < 10; ++f) { 54 | if (f == a) continue; 55 | if (f == b) continue; 56 | if (f == c) continue; 57 | if (f == d) continue; 58 | if (f == e) continue; 59 | for (g = 1; g < 10; ++g) { 60 | if (g == a) continue; 61 | if (g == b) continue; 62 | if (g == c) continue; 63 | if (g == d) continue; 64 | if (g == e) continue; 65 | if (g == f) continue; 66 | for (h = 1; h < 10; ++h) { 67 | if (h == a) continue; 68 | if (h == b) continue; 69 | if (h == c) continue; 70 | if (h == d) continue; 71 | if (h == e) continue; 72 | if (h == f) continue; 73 | if (h == g) continue; 74 | for (i = 1; i < 10; ++i) { 75 | if (i == a) continue; 76 | if (i == b) continue; 77 | if (i == c) continue; 78 | if (i == d) continue; 79 | if (i == e) continue; 80 | if (i == f) continue; 81 | if (i == g) continue; 82 | if (i == h) continue; 83 | 84 | if (a + b + c != 15) continue; 85 | if (d + e + f != 15) continue; 86 | if (g + h + i != 15) continue; 87 | if (a + d + g != 15) continue; 88 | if (b + e + h != 15) continue; 89 | if (c + f + i != 15) continue; 90 | if (a + e + i != 15) continue; 91 | if (c + e + g != 15) continue; 92 | 93 | console.log(a + " " + b + " " + c); 94 | console.log(d + " " + e + " " + f); 95 | console.log(g + " " + h + " " + i); 96 | console.log("--------"); 97 | } 98 | } 99 | } 100 | } 101 | } 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /testsuit/callee2.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 100 4 | 99 5 | 98 6 | 97 7 | 96 8 | 95 9 | 94 10 | 93 11 | 92 12 | 91 13 | 90 14 | 89 15 | 88 16 | 87 17 | 86 18 | 85 19 | 84 20 | 83 21 | 82 22 | 81 23 | 80 24 | 79 25 | 78 26 | 77 27 | 76 28 | 75 29 | 74 30 | 73 31 | 72 32 | 71 33 | 70 34 | 69 35 | 68 36 | 67 37 | 66 38 | 65 39 | 64 40 | 63 41 | 62 42 | 61 43 | 60 44 | 59 45 | 58 46 | 57 47 | 56 48 | 55 49 | 54 50 | 53 51 | 52 52 | 51 53 | 50 54 | 49 55 | 48 56 | 47 57 | 46 58 | 45 59 | 44 60 | 43 61 | 42 62 | 41 63 | 40 64 | 39 65 | 38 66 | 37 67 | 36 68 | 35 69 | 34 70 | 33 71 | 32 72 | 31 73 | 30 74 | 29 75 | 28 76 | 27 77 | 26 78 | 25 79 | 24 80 | 23 81 | 22 82 | 21 83 | 20 84 | 19 85 | 18 86 | 17 87 | 16 88 | 15 89 | 14 90 | 13 91 | 12 92 | 11 93 | 10 94 | 9 95 | 8 96 | 7 97 | 6 98 | 5 99 | 4 100 | 3 101 | 2 102 | 1 103 | =!EXPECTEND!= 104 | */ 105 | 106 | (function (a) { 107 | if (a <= 0) { 108 | return; 109 | } 110 | console.log(a); 111 | arguments.callee(a - 1); 112 | })(100); 113 | -------------------------------------------------------------------------------- /testsuit/delete.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | shit 4 | { x:1, y:2, z:"fuck" } 5 | { x:1, y:2, z:"fuck" } 6 | 1 7 | 2 8 | =!EXPECTEND!= 9 | */ 10 | 11 | a = { a:"shit", b:{x:1,y:2,z:"fuck"}}; 12 | for (x in a) console.log(a[x]); 13 | delete a.a; 14 | for (x in a) console.log(a[x]); 15 | delete a.b.z; 16 | 17 | for (x in a.b) console.log(a.b[x]); 18 | -------------------------------------------------------------------------------- /testsuit/do.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 20 4 | 22 5 | 24 6 | 26 7 | =!EXPECTEND!= 8 | */ 9 | 10 | i = 0; 11 | do { 12 | if (i < 20) continue; 13 | console.log(i); 14 | if (i > 25) break; 15 | i++; 16 | } while (++i < 30); 17 | -------------------------------------------------------------------------------- /testsuit/eval.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | { test:1 } 4 | { a:1, b:2, c:3, d:4 } 5 | undefined 6 | { a:1, b:2, c:3, d:4 } 7 | undefined 8 | =!EXPECTEND!= 9 | */ 10 | 11 | a = eval("{ test:1}"); 12 | console.log(a); 13 | 14 | b = eval("console.log({a:1,b:2,c:3,d:4});"); 15 | console.log(b); 16 | b = eval("console.log({a:1,b:2,c:3,d:4})", x, n, g, k); 17 | console.log(b); 18 | -------------------------------------------------------------------------------- /testsuit/evalthrow.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | abc 4 | finally 5 | =!EXPECTEND!= 6 | */ 7 | 8 | try { 9 | eval("throw('abc');"); 10 | } catch(e) { 11 | console.log(e); 12 | } finally { 13 | console.log("finally"); 14 | } 15 | 16 | -------------------------------------------------------------------------------- /testsuit/expr.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | undefined 4 | =!EXPECTEND!= 5 | */ 6 | 7 | //test void expr 8 | console.log(void 1); 9 | 10 | //test - expr 11 | console.log(-1); 12 | console.log(-0); 13 | console.log(-NaN); 14 | console.log(-Infinity); 15 | console.log(- true); 16 | console.log(- "123" ); 17 | 18 | //wrong: NaN, not 0 19 | console.log(- {a:1}); 20 | 21 | //test + expr 22 | console.log(1 + 2); 23 | console.log(1.3 + 2.3); 24 | console.log(1.2 + "12.3"); 25 | console.log(4 + true); 26 | console.log("2342" + true); 27 | console.log({} + 12); 28 | console.log(NaN + NaN); 29 | console.log(NaN + "NaN"); 30 | console.log(Infinity - Infinity); 31 | console.log(NaN + 3); 32 | 33 | console.log("================"); 34 | console.log(1<<4); 35 | console.log(1<<344.3); 36 | console.log(2>>4); 37 | console.log(-200000 >> -4); 38 | 39 | console.log("==============="); 40 | console.log(1.0 < 2.3); 41 | console.log(NaN < NaN); 42 | console.log(Infinity < -Infinity); 43 | console.log(Infinity > -Infinity); 44 | console.log(10000.456 < 10000.456); 45 | console.log(10000.456 > 10000.456); 46 | console.log(10000.456 <= 10000.456); 47 | console.log(10000.456 >= 10000.456); 48 | console.log("10000.456" < "10000.456"); 49 | console.log("10000.456" > "10000.456"); 50 | console.log("10000.456" <= "10000.456"); 51 | console.log("10000.456" >= "10000.456"); 52 | console.log("a" > "b"); 53 | console.log("a" >= "a"); 54 | console.log("a" < "aa"); 55 | console.log("a" < "b"); 56 | console.log("==============="); 57 | console.log(1 == 1); 58 | console.log(2 == 1); 59 | console.log(NaN == NaN); 60 | console.log("2" == 2); 61 | console.log(true == null); 62 | console.log("234234" == "234234"); 63 | console.log(true == 1); 64 | 65 | console.log("==================="); 66 | console.log(1 === true); 67 | console.log(1 === "1"); 68 | console.log(NaN === NaN); 69 | console.log("abc" === "abc"); 70 | console.log(3.1415926 === 3.1415926); 71 | 72 | console.log("============"); 73 | console.log(1 | 2 | 4 | 8 | 16); 74 | console.log(123 & 234); 75 | console.log(3456 ^ 2342); 76 | 77 | console.log("==============="); 78 | var a = 12; 79 | a += 4; 80 | console.log(a); 81 | a -= 4; 82 | console.log(a); 83 | a /= 4; 84 | console.log(a); 85 | a *= 4; 86 | console.log(a); 87 | a %= 4; 88 | console.log(a); 89 | a <<= 4; 90 | console.log(a); 91 | a >>= 3; 92 | console.log(a); 93 | a += true; 94 | console.log(a); 95 | a += "fuck"; 96 | console.log(a); 97 | 98 | a = { a: 120 }; 99 | a.a += 4; 100 | console.log(a); 101 | a.a -= 4; 102 | console.log(a); 103 | a.a /= 4; 104 | console.log(a); 105 | a.a *= 4; 106 | console.log(a); 107 | a.a %= 4; 108 | console.log(a); 109 | a.a <<= 4; 110 | console.log(a); 111 | a.a >>= 3; 112 | console.log(a); 113 | a.a += true; 114 | console.log(a); 115 | a.a += "fuck"; 116 | console.log(a); 117 | -------------------------------------------------------------------------------- /testsuit/ffi.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | undefined 4 | ================ 5 | a:1 6 | b:2 7 | c:3 8 | d:4 9 | ================ 10 | a:1 11 | b:2 12 | c:3 13 | d:4 14 | shit:fuck 15 | ================ 16 | a:1 17 | b:2 18 | c:3 19 | d:4 20 | fuck:shit 21 | =!EXPECTEND!= 22 | */ 23 | 24 | a = {a: 1,b:2,c:3,d:4}; 25 | console.log(a.e); 26 | 27 | console.log("================"); 28 | for(var s in a) { console.log(s + ":" + a[s]); } 29 | 30 | a.shit = "fuck"; 31 | 32 | console.log("================"); 33 | for(var s in a) { a.fuck = "shit"; console.log(s + ":" + a[s]); } 34 | 35 | console.log("================"); 36 | for(var s in a) { 37 | console.log(s + ":" + a[s]); 38 | delete a.shit; 39 | } -------------------------------------------------------------------------------- /testsuit/fib.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 1 4 | 1 5 | 2 6 | 3 7 | 5 8 | 8 9 | 13 10 | 21 11 | 34 12 | 55 13 | 89 14 | 144 15 | 233 16 | 377 17 | 610 18 | 987 19 | 1597 20 | 2584 21 | 4181 22 | 6765 23 | 10946 24 | 17711 25 | 28657 26 | 46368 27 | 75025 28 | 121393 29 | 196418 30 | 317811 31 | 514229 32 | 832040 33 | 1346269 34 | 2178309 35 | 3524578 36 | 5702887 37 | 9227465 38 | 14930352 39 | 24157817 40 | 39088169 41 | 63245986 42 | 102334155 43 | 165580141 44 | 267914296 45 | =!EXPECTEND!= 46 | */ 47 | 48 | 49 | 50 | a = 1; 51 | b = 1; 52 | console.log(a); 53 | console.log(b); 54 | for (i = 0; i < 20; i = i + 1) { 55 | a = a+b; 56 | console.log(a); 57 | b = a+b; 58 | console.log(b); 59 | } 60 | 61 | -------------------------------------------------------------------------------- /testsuit/file.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | =!EXPECTEND!= 4 | */ 5 | 6 | var f = new File('proto.h'); 7 | if (f) { 8 | while((n = f.gets())!=undefined) { 9 | console.log(n); 10 | } 11 | } else console.log('Can not open proto.h'); 12 | -------------------------------------------------------------------------------- /testsuit/forin.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 0:1 4 | 1:2 5 | 2:3 6 | 4:5 7 | a: 8 | 1 9 | b: 10 | 2 11 | c: 12 | 3 13 | d: 14 | [ 4, 3, 2, 1 ] 15 | e: 16 | { x:"x", y:"y", z:"z" } 17 | =!EXPECTEND!= 18 | */ 19 | 20 | var x = [1,2,3,4,5, 6,7,8]; 21 | for (var i in x) { 22 | console.log(i + ":" +x[i]); 23 | if (i == 3) continue; 24 | if (i > 4) break; 25 | } 26 | 27 | var obj = { a: 1, b:2, c:3, d:[4,3,2,1], e:{x:"x", y:"y", z:"z"}}; 28 | for (i in obj) { 29 | console.log(i + ":"); 30 | console.log( obj[i]); 31 | } 32 | -------------------------------------------------------------------------------- /testsuit/forinstack.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | A 4 | fin 5 | fuck 6 | =!EXPECTEND!= 7 | */ 8 | 9 | var a = {a:1, b:2}; 10 | 11 | try { 12 | for (var n in a) { 13 | try { 14 | switch(n) { 15 | case "a": 16 | console.log("A"); 17 | continue; 18 | case "b": 19 | console.log("B"); 20 | throw("ex"); 21 | } 22 | } catch(e) { 23 | console.log(e); 24 | throw(e); 25 | } finally { 26 | console.log("fin"); 27 | throw("fuck"); 28 | } 29 | } 30 | 31 | } catch (e) { 32 | console.log(e); 33 | } -------------------------------------------------------------------------------- /testsuit/func.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 4950 4 | =!EXPECTEND!= 5 | */ 6 | 7 | function a(n) { 8 | sum = 0; 9 | for ( i = 0; i < n; i = i + 1) sum = sum + i; 10 | console.log(sum); 11 | }; 12 | 13 | a(100); 14 | 15 | -------------------------------------------------------------------------------- /testsuit/grep.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | wenxichang@163.com 4 | =!EXPECTEND!= 5 | */ 6 | 7 | 8 | if (console.args.length < 1) { 9 | console.log("Usage: smallscript grep.ss "); 10 | exit(-1); 11 | } 12 | 13 | var reg = new RegExp(console.args[0]); 14 | line = ""; 15 | while (1) { 16 | line = console.input(); 17 | if (line == undefined) break; 18 | if (line.match(reg)) { 19 | console.log(line); 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /testsuit/invoketime.ss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radare/quad-wheel/ef1747ceeb28fff309de830c3ef863d6cf0b41a1/testsuit/invoketime.ss -------------------------------------------------------------------------------- /testsuit/io.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | Can not open file: 1 4 | =!EXPECTEND!= 5 | */ 6 | 7 | if (console.args.length < 1) { 8 | console.log("Usage: smallscript io.ss "); 9 | exit(-1); 10 | } 11 | 12 | var file = new File(console.args[0], "r"); 13 | if (!file) { 14 | console.log("Can not open file: " + console.args[0]); 15 | exit(-1); 16 | } 17 | 18 | while ((line = file.gets()) != undefined) { 19 | console.log(line); 20 | } 21 | 22 | file.close(); 23 | -------------------------------------------------------------------------------- /testsuit/jiechen.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 3628800 4 | =!EXPECTEND!= 5 | */ 6 | 7 | function jc(n) 8 | { 9 | if (n <= 1) return 1; 10 | return jc(n - 1) * n; 11 | }; 12 | 13 | console.log(jc(10)); 14 | -------------------------------------------------------------------------------- /testsuit/labeled.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 0, 1 4 | shit 5 | 1, 1 6 | shit 7 | 2, 1 8 | shit 9 | 3, 1 10 | shit 11 | 4, 1 12 | shit 13 | { a:{ b:{ c:[ 4, 3 ] } } } 14 | =!EXPECTEND!= 15 | */ 16 | 17 | var a = {a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, j:10}; 18 | var b = {a:{b:{c:1}}}; 19 | fuck: for (var i = 0; i < 5; ++i) { 20 | for (var j in a) { 21 | switch(a[j]) { 22 | case 2: 23 | continue; 24 | case 3: 25 | try { 26 | with (b.a.b) { 27 | c = [i, a[j]]; 28 | continue fuck; 29 | } 30 | } finally { 31 | console.log("shit"); 32 | } 33 | case 4: 34 | break; 35 | case 5: 36 | break fuck; 37 | } 38 | console.log(i + ", " + a[j]); 39 | } 40 | } 41 | 42 | console.log(b); -------------------------------------------------------------------------------- /testsuit/local.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 11 4 | 30 5 | 130 6 | 1 7 | 2 8 | 7 9 | 12 10 | 112 11 | 130 12 | =!EXPECTEND!= 13 | */ 14 | 15 | var a = 1; 16 | var b = 2; 17 | 18 | function abc(a,b) { 19 | var c = a+b; 20 | var d = a * b; 21 | console.log(c); 22 | console.log(d); 23 | return d; 24 | }; 25 | 26 | console.log(x = abc(5,6) + 100); 27 | 28 | console.log(a); 29 | console.log(b); 30 | console.log(abc(3,4)+100); 31 | 32 | console.log(x); 33 | -------------------------------------------------------------------------------- /testsuit/obj.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | { a:[ { b:1, c:[ 2, 3, 5, 6 ], d:{ x:1, y:2 } }, 2, 3, 4 ], x:"yz" } 4 | { x:{ a:1, b:2 }, y:{ a:3, b:4 }, abc:1, cde:"123123" } 5 | =!EXPECTEND!= 6 | */ 7 | 8 | var x = {a:[{b:1,c:[2,3,5,6],d:{x:1,y:2}},2,3,4],x:"yz"}; 9 | console.log (x); 10 | 11 | var a = { abc:1,cde:"123123",x:{a:1,b:2},y:{a:3,b:4}}; 12 | console.log(a); 13 | 14 | -------------------------------------------------------------------------------- /testsuit/person1.ss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radare/quad-wheel/ef1747ceeb28fff309de830c3ef863d6cf0b41a1/testsuit/person1.ss -------------------------------------------------------------------------------- /testsuit/person2.ss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radare/quad-wheel/ef1747ceeb28fff309de830c3ef863d6cf0b41a1/testsuit/person2.ss -------------------------------------------------------------------------------- /testsuit/prob/callee.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 0:4 4 | 1:5 5 | 2:6 6 | 3:8 7 | 0:4 8 | 1:5 9 | 2:6 10 | 3:8 11 | 1 12 | =!EXPECTEND!= 13 | 14 | diff from firefox, arguments.callee can not be changed 15 | */ 16 | 17 | function a(a,b,c) { 18 | for (var x = 0; x < arguments.length; ++x) { 19 | console.log(x + ":" + arguments[x]); 20 | } 21 | arguments.callee = 1; 22 | for (var x = 0; x < arguments.length; ++x) { 23 | console.log(x + ":" + arguments[x]); 24 | } 25 | console.log(arguments.callee); 26 | }; 27 | 28 | a(4,5,6,8); -------------------------------------------------------------------------------- /testsuit/prob/withscope.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | { b:{ c:2 } } 4 | undefined 5 | =!EXPECTEND!= 6 | 7 | diff from ecma, var should make var in with 8 | */ 9 | 10 | var a = { 11 | b: { 12 | c: 1 13 | } 14 | }; 15 | 16 | with (a.b) { 17 | c = 2; 18 | eval("var d = 4;"); 19 | } 20 | console.log(a); 21 | console.log(a.b.d); 22 | 23 | -------------------------------------------------------------------------------- /testsuit/prototypes.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | { } 4 | should be {} 5 | { a:123 } 6 | should be { a:123 } 7 | 123 8 | should be 123 9 | shit 10 | should be shit 11 | { a:123 } 12 | should be { a:123 } 13 | 6 14 | should be 6 15 | ZhangSan 16 | 18 17 | defaultName 18 | =!EXPECTEND!= 19 | */ 20 | 21 | console.log(Object.prototype); 22 | console.log(" should be {}"); 23 | 24 | Object.prototype.a = 123; 25 | Object.prototype = 123; 26 | 27 | console.log(Object.prototype); 28 | console.log(" should be { a:123 }"); 29 | 30 | var a = { b:1, c:2 }; 31 | console.log(a.a); 32 | console.log(" should be 123"); 33 | a.a = 'shit'; 34 | console.log(a.a); 35 | console.log(" should be shit"); 36 | 37 | console.log(Object.prototype); 38 | console.log(" should be { a:123 }"); 39 | 40 | Number.prototype.fuck = function() { 41 | console.log(this / 2); 42 | }; 43 | 44 | var x = 12; 45 | 46 | x.fuck(); 47 | console.log(" should be 6"); 48 | 49 | function Person(name, sex) { 50 | this.name = name; 51 | this.sex = sex; 52 | }; 53 | 54 | Person.prototype = { 55 | getName: function() { 56 | return this.name; 57 | }, 58 | getSex: function() { 59 | return this.sex; 60 | }, 61 | age: 18 62 | }; 63 | 64 | function Employee(name, sex, employeeID) { 65 | this.name = name; 66 | this.sex = sex; 67 | this.employeeID = employeeID; 68 | }; 69 | 70 | Employee.prototype = new Person("defaultName", "defaultSex"); 71 | Employee.prototype.getEmployeeID = function() { 72 | return this.employeeID; 73 | }; 74 | 75 | var zhang = new Employee("ZhangSan", "man", "1234"); 76 | console.log(zhang.getName()); // "ZhangSan 77 | console.log(zhang.age); //18 78 | delete zhang.name; 79 | console.log(zhang.name); //defaultName -------------------------------------------------------------------------------- /testsuit/ref.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | { x:[ 4, 5, 6 ], y:{ a:1, b:2 } } 4 | { a:1, b:2 } 5 | 1 6 | =!EXPECTEND!= 7 | */ 8 | 9 | var a = { "x":[4,5,6], "y":{a:1, b:2}}; 10 | var b = { n:a, m:a.y }; 11 | console.log (b.n); 12 | console.log (b.m); 13 | 14 | function a() { return {x:1, y:{a:1,b:[]}}; }; 15 | console.log(a().y.a); 16 | 17 | -------------------------------------------------------------------------------- /testsuit/regex.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | input the email 4 | invalid email format 5 | input the email 6 | invalid email format 7 | input the email 8 | match at: wenxichang@163.com 9 | name: wenxichang 10 | domain: 163 11 | input the email 12 | invalid email format 13 | input the email 14 | =!EXPECTEND!= 15 | */ 16 | 17 | 18 | while (1) { 19 | console.log("input the email"); 20 | email = console.input(); 21 | if (email == undefined) break; 22 | if ((res = email.match(/([a-zA-Z0-9]+)@([a-zA-Z0-9]+)\.com/))) { 23 | console.log("match at: " + res[0]); 24 | console.log("name: " + res[1]); 25 | console.log("domain: " + res[2]); 26 | } else { 27 | console.log("invalid email format"); 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /testsuit/scope.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 140 4 | 623 5 | =!EXPECTEND!= 6 | */ 7 | 8 | function abc(a, b, c) { 9 | var x = 123; 10 | function fx(a, b) { 11 | return (function (f) { 12 | return x + a + b + c + f; 13 | }); 14 | }; 15 | return fx(a, b); 16 | }; 17 | 18 | var fn = abc(3, 5, 6); 19 | 20 | var fsh = abc(100, 100, 100); 21 | 22 | var x = fn(3); 23 | 24 | var y = fsh(200); 25 | 26 | console.log(x); 27 | 28 | console.log(y); -------------------------------------------------------------------------------- /testsuit/switch.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | default 4 | 1 5 | =!EXPECTEND!= 6 | */ 7 | 8 | switch(3) { 9 | default: console.log("default"); 10 | case 1: 11 | console.log("1"); 12 | break; 13 | case 2: 14 | console.log("2"); 15 | continue; 16 | } 17 | -------------------------------------------------------------------------------- /testsuit/switch2.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 10 4 | 20 5 | 30 6 | 20 7 | 30 8 | 30 9 | 7 10 | =!EXPECTEND!= 11 | */ 12 | 13 | for(var i = 0; i < 100; ++i) { 14 | switch(i) { 15 | case 10: 16 | console.log("10"); 17 | case 20: 18 | console.log("20"); 19 | case 30: 20 | console.log("30"); 21 | break; 22 | case 40: { 23 | continue; 24 | console.log("40"); 25 | } 26 | break; 27 | case 41: break; 28 | case 50: 29 | for (var j = 0; j < 10; ++j) { 30 | if (j < 5) continue; 31 | if (j > 6) break; 32 | } 33 | console.log(j); 34 | break; 35 | default: 36 | break; 37 | } 38 | if (i == 40) console.log("hehe"); 39 | } -------------------------------------------------------------------------------- /testsuit/testapply.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | { a:"a" } 4 | undefined 5 | undefined 6 | undefined 7 | { b:"b" } 8 | 1 9 | 2 10 | undefined 11 | { c:"c" } 12 | 1 13 | 2 14 | 3 15 | =!EXPECTEND!= 16 | */ 17 | 18 | this.top = 'top'; 19 | 20 | function a(a,b,c) { 21 | console.log(this); 22 | console.log(a); 23 | console.log(b); 24 | console.log(c); 25 | }; 26 | 27 | a.apply({a:'a'}); 28 | a.apply({b:'b'}, [1,2]); 29 | a.apply({c:'c'}, [1,2,3,4]); -------------------------------------------------------------------------------- /testsuit/testarray.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | [ undefined ] 4 | [ undefined, 2 ] 5 | 5 6 | 3 7 | 2 8 | 1 9 | 2 10 | 2 11 | undefined 12 | undefined 13 | undefined 14 | 0 15 | =!EXPECTEND!= 16 | */ 17 | 18 | var a = new Array(1); 19 | 20 | console.log(a); 21 | a[1] = 2; 22 | console.log(a); 23 | 24 | console.log(a.push(1,2,3)); 25 | console.log(a.pop()); 26 | console.log(a.pop()); 27 | console.log(a.pop()); 28 | console.log(a.length); 29 | console.log(a.pop()); 30 | console.log(a.pop()); 31 | console.log(a.pop()); 32 | console.log(a.pop()); 33 | console.log(a.length); 34 | 35 | -------------------------------------------------------------------------------- /testsuit/testcall.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | { a:1 } 4 | { a:1 } 5 | 1 6 | 2 7 | 3 8 | 3 9 | { fuck:32 } 10 | 4 11 | 5 12 | 6 13 | 9 14 | 1 15 | 3 16 | { a:1 } 17 | 4 18 | 6 19 | { a:1 } 20 | 1 21 | 3 22 | { fn:4 } 23 | 4 24 | 6 25 | { fm:8 } 26 | =!EXPECTEND!= 27 | */ 28 | 29 | this.a = 1; 30 | console.log(this); 31 | 32 | function f(x, y, z) { 33 | var a = arguments[0] + arguments[1]; 34 | console.log(this); 35 | console.log(x); 36 | console.log(y); 37 | console.log(z); 38 | console.log(a); 39 | var ff = function (a) { 40 | console.log(x); 41 | console.log(z); 42 | console.log(this); 43 | }; 44 | return ff; 45 | }; 46 | 47 | var fn = f(1, 2,3); 48 | 49 | fm = f.call({fuck:32}, 4,5,6); 50 | 51 | fn(456); 52 | fm(789); 53 | 54 | fn.call({fn:4}, 456); 55 | fm.call({fm:8}, 55667); 56 | 57 | -------------------------------------------------------------------------------- /testsuit/testeval.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | { a:1 } 4 | { a:1, b:2, c:"abc", d:[ 1, 2, 3 ], e:{ } } 5 | abc 6 | =!EXPECTEND!= 7 | */ 8 | 9 | var a = {}; 10 | eval("a.a = 1;"); 11 | console.log(a); 12 | 13 | 14 | var y = eval("{ a:1, b:2, c:'abc', d:[1,2,3], e: {}}"); 15 | console.log(y); 16 | 17 | 18 | var n = 'abc'; 19 | eval("(y = n)"); 20 | 21 | console.log(y); 22 | 23 | -------------------------------------------------------------------------------- /testsuit/testeval2.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | at global 4 | predefine must be in global 5 | predefine in global after eval 6 | predefine in global after eval 7 | global predefine: predefine in global after eval 8 | predefine in global after eval 9 | 100 10 | predefine in global after eval 11 | =!EXPECTEND!= 12 | */ 13 | 14 | var PREDEFINE = 'at global'; 15 | 16 | function haha() { 17 | return { 18 | getpredefine: function() { console.log(PREDEFINE); }, 19 | setpredefine: function(a) { PREDEFINE = a; }, 20 | eval: function(n) { eval(n); } 21 | }; 22 | }; 23 | 24 | var a = haha(); 25 | 26 | a.getpredefine(); 27 | a.setpredefine('predefine must be in global'); 28 | a.getpredefine(); 29 | a.eval("PREDEFINE = 'predefine in global after eval';"); 30 | a.getpredefine(); 31 | a.eval("var PREDEFINE = 'predefine now in local';"); 32 | a.getpredefine(); 33 | console.log("global predefine: " + PREDEFINE); 34 | 35 | function hehe(n) 36 | { 37 | if (n) eval(n); 38 | console.log(PREDEFINE); 39 | }; 40 | 41 | hehe(); 42 | hehe("var PREDEFINE = 100;"); 43 | hehe(); -------------------------------------------------------------------------------- /testsuit/testexcpt.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | catch: b:[object Object] 4 | finally: b:1 5 | { b:2 } 6 | 1 7 | =!EXPECTEND!= 8 | */ 9 | 10 | var i = 0, b = 0; 11 | i++; 12 | try { 13 | try { 14 | b++; 15 | throw({a:1}); 16 | } catch (b) { 17 | console.log("catch: b:" + b); 18 | } finally { 19 | console.log("finally: b:" + b); 20 | throw({b:2}); 21 | } 22 | } catch (b) { 23 | console.log(b); 24 | } finally { 25 | console.log(b); 26 | } 27 | -------------------------------------------------------------------------------- /testsuit/testexcpt2.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | =!EXPECTEND!= 4 | */ 5 | 6 | try { 7 | try { 8 | throw({a:1}); 9 | } catch (b) { 10 | throw({b:2}); 11 | } finally { 12 | } 13 | } catch (b) { 14 | } finally { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /testsuit/testexcpt3.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 51 4 | =!EXPECTEND!= 5 | */ 6 | 7 | for (var i = 0; i < 100; ++i) { 8 | try { 9 | if (i == 50) throw(1); 10 | } catch(e) { 11 | break; 12 | } finally { 13 | i++; 14 | } 15 | } 16 | console.log(i); 17 | 18 | -------------------------------------------------------------------------------- /testsuit/testexcpt4.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | catch 4 | final 5 | shit 6 | 2 7 | 2th final 8 | ff 9 | fuck 10 | =!EXPECTEND!= 11 | */ 12 | 13 | var a; 14 | try { 15 | for (a = 0; a < 100; ++a) { 16 | try { 17 | try { 18 | throw(1); 19 | } catch(e) { 20 | console.log("catch"); 21 | continue; 22 | } finally { 23 | console.log("final"); 24 | throw(2); 25 | } 26 | } catch(f) { 27 | console.log("shit"); 28 | console.log(f); 29 | throw('ff'); 30 | } finally { 31 | console.log("2th final"); 32 | } 33 | } 34 | } catch(x) { 35 | console.log(x); 36 | } 37 | console.log("fuck"); 38 | 39 | -------------------------------------------------------------------------------- /testsuit/testexcpt5.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | catch: 4 | abc 5 | final 6 | shit 7 | 2 8 | 2th final 9 | ff 10 | fuck 11 | undefined 12 | =!EXPECTEND!= 13 | */ 14 | 15 | function test() { 16 | try { 17 | throw('abc'); 18 | } finally { 19 | 20 | } 21 | return 0; 22 | }; 23 | 24 | var a, n; 25 | try { 26 | for (a = 0; a < 100; ++a) { 27 | try { 28 | try { 29 | n = test(); 30 | } catch(e) { 31 | console.log("catch:"); 32 | console.log(e); 33 | continue; 34 | } finally { 35 | console.log("final"); 36 | throw(2); 37 | } 38 | } catch(f) { 39 | console.log("shit"); 40 | console.log(f); 41 | throw('ff'); 42 | } finally { 43 | console.log("2th final"); 44 | } 45 | } 46 | } catch(x) { 47 | console.log(x); 48 | } 49 | console.log("fuck"); 50 | console.log(n); 51 | -------------------------------------------------------------------------------- /testsuit/teststring.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | the original string 4 | the original string 5 | he original string 6 | g 7 | 8 | tri 9 | 10 | a 11 | 4 12 | -1 13 | 6 14 | 16 15 | =!EXPECTEND!= 16 | */ 17 | 18 | var a = "the original string"; 19 | 20 | console.log(a.substr()); 21 | console.log(a.substr(false)); 22 | console.log(a.substr(1)); 23 | console.log(a.substr(-1, 1)); 24 | 25 | console.log(a.substr(2, -1)); 26 | console.log(a.substr(-5, 3)); 27 | console.log(a.substr(100, 0)); 28 | console.log(a.substr(10, 1)); 29 | 30 | console.log(a.indexOf("ori")); 31 | console.log(a.indexOf("swer")); 32 | console.log(a.indexOf("i")); 33 | console.log(a.indexOf("i", 9)); 34 | -------------------------------------------------------------------------------- /testsuit/testthis.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | a: 1 4 | this.a: a in x 5 | =!EXPECTEND!= 6 | */ 7 | 8 | function fuck(a,b){ 9 | console.log("a: " + a); 10 | console.log("this.a: " + this.a); 11 | }; 12 | 13 | x = { test: fuck, a: 'a in x', b: 'b in x' }; 14 | 15 | { test: fuck, a: 'a in x', b: 'b in x' }.test(1, 2); 16 | -------------------------------------------------------------------------------- /testsuit/testthis2.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | TOP 4 | n 5 | =!EXPECTEND!= 6 | */ 7 | 8 | this.name = 'TOP'; 9 | 10 | function a() { 11 | console.log(this.name); 12 | }; 13 | 14 | function b(x, y) { 15 | a(); 16 | console.log(this.name); 17 | }; 18 | 19 | var n = { name: 'n', test: b }; 20 | 21 | n.test(1, 2); 22 | -------------------------------------------------------------------------------- /testsuit/testthis3.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | b 4 | c 5 | d 6 | a 7 | =!EXPECTEND!= 8 | */ 9 | 10 | 11 | var showthis = function() { 12 | console.log(this.name); 13 | }; 14 | 15 | var a = { name: 'a', f: showthis }; 16 | var b = { name: 'b', f: showthis }; 17 | var c = { name: 'c', f: showthis }; 18 | var d = { name: 'd', f: showthis }; 19 | 20 | a.f(b.f(), c.f(), d.f()); 21 | -------------------------------------------------------------------------------- /testsuit/while.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 1 4 | 2 5 | 3 6 | 3000000 7 | 1000 8 | 1000 9 | =!EXPECTEND!= 10 | */ 11 | 12 | i = 0; 13 | result = 0; 14 | while (i<3) { 15 | j = 0; 16 | while (j<1000) { 17 | k = 0; 18 | while (k<1000) { 19 | ++k; 20 | ++result; 21 | } 22 | ++j; 23 | } 24 | ++i; 25 | console.log(i); 26 | } 27 | console.log(result); 28 | console.log(j); 29 | console.log(k); -------------------------------------------------------------------------------- /testsuit/with.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | try1 4 | finally2 5 | fuck 6 | finally1 7 | { b:{ c:0 }, x:0 } 8 | =!EXPECTEND!= 9 | */ 10 | 11 | var a = { 12 | b: { 13 | c: 0 14 | }, 15 | x: 0 16 | }; 17 | 18 | for (var i = 0; i < 10; ++i) { 19 | try { 20 | console.log("try1"); 21 | 22 | with(a.b) { 23 | c = i; 24 | try { 25 | if (i == 5) throw("shit"); 26 | } catch (e) { 27 | console.log(e); 28 | with (a) { 29 | x = 'shit'; 30 | throw("sadf"); 31 | } 32 | } finally { 33 | console.log("finally2"); 34 | throw("fuck"); 35 | } 36 | } 37 | } catch(e) { 38 | console.log(e); 39 | break; 40 | } finally { 41 | console.log("finally1"); 42 | } 43 | } 44 | 45 | console.log(a); 46 | -------------------------------------------------------------------------------- /testsuit/yhsj.ss: -------------------------------------------------------------------------------- 1 | /* 2 | =!EXPECTSTART!= 3 | 100 4 | 99 5 | 98 6 | 97 7 | 96 8 | 95 9 | 94 10 | 93 11 | 92 12 | 91 13 | 90 14 | 89 15 | 88 16 | 87 17 | 86 18 | 85 19 | 84 20 | 83 21 | 82 22 | 81 23 | 80 24 | 79 25 | 78 26 | 77 27 | 76 28 | 75 29 | 74 30 | 73 31 | 72 32 | 71 33 | 70 34 | 69 35 | 68 36 | 67 37 | 66 38 | 65 39 | 64 40 | 63 41 | 62 42 | 61 43 | 60 44 | 59 45 | 58 46 | 57 47 | 56 48 | 55 49 | 54 50 | 53 51 | 52 52 | 51 53 | 50 54 | 49 55 | 48 56 | 47 57 | 46 58 | 45 59 | 44 60 | 43 61 | 42 62 | 41 63 | 40 64 | 39 65 | 38 66 | 37 67 | 36 68 | 35 69 | 34 70 | 33 71 | 32 72 | 31 73 | 30 74 | 29 75 | 28 76 | 27 77 | 26 78 | 25 79 | 24 80 | 23 81 | 22 82 | 21 83 | 20 84 | 19 85 | 18 86 | 17 87 | 16 88 | 15 89 | 14 90 | 13 91 | 12 92 | 11 93 | 10 94 | 9 95 | 8 96 | 7 97 | 6 98 | 5 99 | 4 100 | 3 101 | 2 102 | 1 103 | =!EXPECTEND!= 104 | */ 105 | 106 | function jc(n) { 107 | if (n > 0) { 108 | console.log(n); 109 | jc(n - 1); 110 | } 111 | }; 112 | 113 | jc(100); 114 | -------------------------------------------------------------------------------- /unichar.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "unichar.h" 6 | #include "mempool.h" 7 | 8 | unichar *unistrdup(const unichar *str) 9 | { 10 | int len = unistrlen(str); 11 | 12 | unichar *r = mm_alloc((len + 1) * sizeof(unichar) + sizeof(int)); 13 | unichar *rr = (unichar *)((int)r + sizeof(int)); 14 | 15 | *((int *)r) = len; 16 | 17 | memcpy(rr, str, (len) * sizeof(unichar)); 18 | return rr; 19 | } 20 | 21 | unichar *unistrdup_str(const char *str) 22 | { 23 | int len = strlen(str); 24 | 25 | unichar *r = mm_alloc((len + 1) * sizeof(unichar) + sizeof(int)); 26 | unichar *rr = (unichar *)((int)r + sizeof(int)); 27 | int i; 28 | 29 | *((int *)r) = len; 30 | 31 | for (i = 0; i < len; ++i) rr[i] = str[i]; 32 | 33 | return rr; 34 | } 35 | 36 | unichar *unisubstrdup(const unichar *a, int start, int len) 37 | { 38 | if (len == 0) return unistrdup_str(""); 39 | 40 | int lenofa = unistrlen(a); 41 | while (start < 0) start += lenofa; 42 | if (start >= lenofa) return unistrdup_str(""); 43 | 44 | int maxcpy = lenofa - start; 45 | 46 | if (len > 0) { 47 | maxcpy = maxcpy < len ? maxcpy : len; 48 | } 49 | 50 | unichar *r = mm_alloc((maxcpy + 1) * sizeof(unichar) + sizeof(int)); 51 | unichar *rr = (unichar *)((int)r + sizeof(int)); 52 | 53 | *((int *)r) = maxcpy; 54 | 55 | memcpy(rr, a + start, maxcpy * sizeof(unichar)); 56 | return rr; 57 | } 58 | 59 | void unistrcpy(unichar *to, const unichar *from) 60 | { 61 | int len = unistrlen(from); 62 | int i; 63 | for (i = 0; i < len; ++i) to[i] = from[i]; 64 | 65 | unistrlen(to) = len; 66 | } 67 | 68 | void _uniprint(unichar *s) 69 | { 70 | int len = unistrlen(s); 71 | int i; 72 | for (i = 0; i < len; ++i) printf("%c", s[i]); 73 | printf("\n"); 74 | } 75 | 76 | void unifree(unichar *d) 77 | { 78 | mm_free((void *)(((int)d) - sizeof(int))); 79 | } 80 | 81 | int unistrchr(const unichar *str, int c) 82 | { 83 | int len = unistrlen(str); 84 | int i; 85 | for (i = 0; i < len; ++i) { 86 | if (str[i] == c) return 1; 87 | } 88 | return 0; 89 | } 90 | 91 | const char *tochars(const unichar *str) 92 | { 93 | static char buf[65536]; 94 | int i; 95 | int len = unistrlen(str); 96 | 97 | for (i = 0; i < len && i < 65530; ++i) buf[i] = (char)str[i]; 98 | buf[i] = 0; 99 | return buf; 100 | } 101 | 102 | const unichar *tounichars(const char *str) 103 | { 104 | static unichar buf[65536]; 105 | int *len = (int *)buf; 106 | unichar *b = (unichar *)((int)buf + sizeof(int)); 107 | int i; 108 | 109 | for (i = 0; str[i] && i < 65530; ++i) { 110 | b[i] = str[i]; 111 | } 112 | *len = i; 113 | 114 | return b; 115 | } 116 | 117 | int unistrcmp(const unichar *str1, const unichar *str2) 118 | { 119 | int len1 = unistrlen(str1); 120 | int len2 = unistrlen(str2); 121 | 122 | int i, r; 123 | if (len1 != len2) return len1 - len2; 124 | 125 | for (i = 0; i < len1; ++i) { 126 | if ((r = str1[i] - str2[i])) return r; 127 | } 128 | return 0; 129 | } 130 | 131 | unichar *unistrcat(const unichar *str1, const unichar *str2) 132 | { 133 | int len = unistrlen(str1) + unistrlen(str2); 134 | 135 | unichar *r = mm_alloc((len + 1) * sizeof(unichar) + sizeof(int)); 136 | unichar *rr = (unichar *)((int)r + sizeof(int)); 137 | 138 | *((int *)r) = len; 139 | 140 | memcpy(rr, str1, unistrlen(str1) * sizeof(unichar)); 141 | memcpy(rr + unistrlen(str1), str2, unistrlen(str2) * sizeof(unichar)); 142 | return rr; 143 | } 144 | 145 | int unistrpos(unichar *str, int start, unichar *s2) 146 | { 147 | unichar *s1 = str; 148 | int l1 = unistrlen(s1); 149 | int l2 = unistrlen(s2); 150 | 151 | s1 += start; 152 | l1 -= start; 153 | 154 | while (l1 >= l2) { 155 | if (memcmp(s1, s2, l2 * sizeof(unichar)) == 0) { 156 | return s1 - str; 157 | } 158 | s1++; 159 | l1--; 160 | } 161 | return -1; 162 | } 163 | 164 | /* strdup impletement */ 165 | char *c_strdup(const char *buf) 166 | { 167 | int len = strlen(buf); 168 | char *ret = mm_alloc(len + 1); 169 | memcpy(ret, buf, len + 1); 170 | return ret; 171 | } 172 | 173 | void c_strfree(char *buf) 174 | { 175 | mm_free(buf); 176 | } 177 | 178 | -------------------------------------------------------------------------------- /unichar.h: -------------------------------------------------------------------------------- 1 | #ifndef __UNICHAR_H__ 2 | #define __UNICHAR_H__ 3 | 4 | typedef unsigned short unichar; 5 | 6 | /* for declare unicode static string 7 | * in unicode supported compiler, use wchar_t and L"string" may save a lot of works 8 | * any way, you should declare a static unicode string like this: 9 | * 10 | * static UNISTR(5) hello = { 5, { 'h', 'e', 'l', 'l', 'o' } }; 11 | * 12 | * comment: alway declare one more byte for objkey 13 | */ 14 | #define UNISTR(_len) struct{int len;unichar unistr[(_len)+1];} 15 | 16 | #define unistrlen(str) (*((int *)(((int)(str)) - sizeof(int)))) 17 | 18 | unichar *unistrdup(const unichar *str); 19 | unichar *unistrdup_str(const char *str); 20 | int unistrcmp(const unichar *str1, const unichar *str2); 21 | unichar *unistrcat(const unichar *str1, const unichar *str2); 22 | void unistrcpy(unichar *to, const unichar *from); 23 | void unifree(unichar *d); 24 | int unistrchr(const unichar *str, int c); 25 | char *c_strdup(const char *buf); 26 | void c_strfree(char *buf); 27 | int unistrpos(unichar *str, int start, unichar *nid); 28 | unichar *unisubstrdup(const unichar *a, int start, int len); 29 | 30 | /* those two are very dangerous, keep your eyes on */ 31 | const unichar *tounichars(const char *str); 32 | const char *tochars(const unichar *str); 33 | void _uniprint(unichar *s); 34 | 35 | #endif 36 | 37 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "pstate.h" 7 | #include "error.h" 8 | #include "value.h" 9 | #include "func.h" 10 | #include "proto.h" 11 | #include "eval.h" 12 | 13 | static void print_value(Value *v, int quote); 14 | static int _object_print_callback(void *key, void *value, void *userdata) 15 | { 16 | ObjKey *ok = key; 17 | Value *v = value; 18 | int *first = userdata; 19 | int flag = KEYFLAG(ok); 20 | if (bits_get(flag, OM_DONTEMU)) return 0; 21 | 22 | if (*first) { 23 | *first = 0; 24 | } else printf(", "); 25 | 26 | printf("%s:", tochars((unichar *)ok)); 27 | 28 | print_value(v, 1); 29 | 30 | return 0; 31 | } 32 | 33 | static void print_value(Value *v, int quote) 34 | { 35 | switch(v->vt) { 36 | case VT_UNDEF: 37 | printf("undefined"); 38 | break; 39 | case VT_NULL: 40 | printf("null"); 41 | break; 42 | case VT_BOOL: 43 | printf("%s", v->d.val ? "true":"false"); 44 | break; 45 | case VT_NUMBER: 46 | if (is_integer(v->d.num)) { 47 | printf("%d", (int)v->d.num); 48 | } else if (ieee_isnormal(v->d.num)) { 49 | printf("%g", v->d.num); 50 | } else if (ieee_isnan(v->d.num)) { 51 | printf("NaN"); 52 | } else { 53 | int s = ieee_infinity(v->d.num); 54 | if (s > 0) printf("+Infinity"); 55 | else if (s < 0) printf("-Infinity"); 56 | else bug("Ieee function got problem"); 57 | } 58 | break; 59 | case VT_STRING: 60 | if (quote) printf("\"%s\"", tochars(v->d.str)); 61 | else printf("%s", tochars(v->d.str)); 62 | break; 63 | case VT_OBJECT: { 64 | Object *o = v->d.obj; 65 | switch(o->ot) { 66 | case OT_BOOL: 67 | printf("%s ", o->d.val ? "true":"false"); 68 | break; 69 | case OT_NUMBER: 70 | if (is_integer(o->d.num)) { 71 | printf("%d ", (int)o->d.num); 72 | } else { 73 | printf("%g ", o->d.num); 74 | } 75 | break; 76 | case OT_STRING: 77 | printf("%s", tochars(o->d.str)); 78 | break; 79 | case OT_FUNCTION: { 80 | Func *f = o->d.fobj->func; 81 | if (f->type == FC_NORMAL) { 82 | printf("function ("); 83 | int i; 84 | for (i = 0; i < f->argnames->count; ++i) { 85 | printf("%s ", tochars(strs_get(f->argnames, i))); 86 | } 87 | printf(") {\n"); 88 | codes_print(f->exec.opcodes); 89 | printf("}"); 90 | } else { 91 | printf("function () { [Native code] }"); 92 | } 93 | break; 94 | } 95 | case OT_REGEXP: 96 | printf("/regex/ "); 97 | break; 98 | case OT_USERDEF: 99 | printf("#UserData%d ", o->d.uobj->id); 100 | break; 101 | default: 102 | break; 103 | } 104 | 105 | int len = object_get_length(o); 106 | 107 | if (len > 0) { 108 | int i; 109 | Value *nv; 110 | printf("[ "); 111 | 112 | if (len > 0) { 113 | nv = value_object_lookup_array(v, 0, NULL); 114 | if (nv) print_value(nv, 1); 115 | else printf("undefined"); 116 | } 117 | for (i = 1; i < len; ++i) { 118 | nv = value_object_lookup_array(v, i, NULL); 119 | printf(", "); 120 | if (nv) print_value(nv, 1); 121 | else printf("undefined"); 122 | } 123 | printf(" ]"); 124 | } else { 125 | int first = 1; 126 | printf("{ "); 127 | rbtree_walk(o->tree, &first, _object_print_callback); 128 | printf(" }"); 129 | } 130 | break; 131 | } 132 | default: 133 | bug("Unexpected value type: %d\n", v->vt); 134 | } 135 | } 136 | 137 | static int console_input(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 138 | { 139 | char buf[1024]; 140 | char *p = buf; 141 | if (asc) die("Can not call console.input as a constructor\n"); 142 | 143 | if (fgets(buf, 1024, stdin) == NULL) { 144 | value_make_undef(*ret); 145 | return 0; 146 | } 147 | if ((p = strchr(buf, '\r'))) *p = 0; 148 | if ((p = strchr(buf, '\n'))) *p = 0; 149 | 150 | value_make_string(*ret, unistrdup(tounichars(buf))); 151 | return 0; 152 | } 153 | 154 | static int global_exit(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 155 | { 156 | int err = 0; 157 | if (asc) die("Can not call exit as a constructor\n"); 158 | if (value_get_length(args) > 0) { 159 | Value *v = value_object_lookup_array(args, 0, NULL); 160 | if (v && is_number(v)) err = (int)v->d.num; 161 | } 162 | exit(err); 163 | return 0; 164 | } 165 | 166 | extern int yyparse(PSTATE *ps); 167 | 168 | /* eval here is diff from SSFunc, current scope info should be past to eval */ 169 | /* make evaling script execute in the same context */ 170 | int utils_global_eval(PSTATE *ps, const char *program, 171 | ScopeChain *scope, Value *currentScope, Value *_this, Value *ret) 172 | { 173 | PSTATE *newps = pstate_new_from_string(program); 174 | newps->eval_flag = 1; 175 | 176 | yyparse(newps); 177 | 178 | if (!newps->err_count) { 179 | int r = eval(newps, newps->opcodes, scope, currentScope, _this, ret); 180 | if (r) { 181 | value_copy(ps->last_exception, newps->last_exception); 182 | } 183 | pstate_free(newps); 184 | return r; 185 | } else { 186 | /* todo, parse error */ 187 | value_make_string(ps->last_exception, unistrdup_str("Syntax error")); 188 | return -1; 189 | } 190 | } 191 | 192 | /* here demo how to build console.log */ 193 | /* first: define console.log function */ 194 | static int console_log(PSTATE *ps, Value *args, Value *_this, Value *ret, int asc) 195 | { 196 | if (asc) die("Can not call console.log as a constructor\n"); 197 | int argc = value_get_length(args); 198 | int i; 199 | for (i = 0; i < argc; ++i) { 200 | Value *v = value_object_lookup_array(args, i, NULL); 201 | if (v) print_value(v, 0); 202 | } 203 | printf("\n"); 204 | return 0; 205 | } 206 | 207 | Value *init_program_args(int argc, char **argv) 208 | { 209 | Value *ret = value_new(); 210 | Object *obj = object_new(); 211 | obj->__proto__ = Array_prototype; 212 | value_make_object(*ret, obj); 213 | object_set_length(obj, 0); 214 | 215 | int i; 216 | for (i = 0; i < argc; ++i) { 217 | Value *val = value_new(); 218 | value_make_string(*val, unistrdup_str(argv[i])); 219 | value_object_utils_insert_array(ret, i, val, 1, 1, 1); 220 | } 221 | return ret; 222 | } 223 | 224 | void utils_init(Value *global, int argc, char **argv) 225 | { 226 | /* second, build console object */ 227 | Value *console = value_object_utils_new_object(); 228 | /* no __proto__, console is not an Object */ 229 | 230 | /* third, make console.log object */ 231 | Value *conlog = func_utils_make_func_value(console_log); 232 | conlog->d.obj->__proto__ = Function_prototype; 233 | 234 | /* forth, insert console.log value into console object */ 235 | value_object_utils_insert(console, tounichars("log"), conlog, 1, 1, 0); 236 | value_object_utils_insert(console, tounichars("print"), value_dup(conlog), 1, 1, 0); 237 | value_object_utils_insert(console, tounichars("output"), value_dup(conlog), 1, 1, 0); 238 | value_object_utils_insert(console, tounichars("input"), 239 | func_utils_make_func_value(console_input), 240 | 1, 1, 0); 241 | value_object_utils_insert(console, tounichars("args"), 242 | init_program_args(argc, argv), 1, 1, 0); 243 | 244 | /* last, insert console to global naming space */ 245 | value_object_utils_insert(global, tounichars("console"), console, 0, 0, 0); 246 | value_object_utils_insert(global, tounichars("exit"), 247 | func_utils_make_func_value(global_exit), 248 | 0, 0, 0); 249 | } 250 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | #ifndef __UTILS_H__ 2 | #define __UTILS_H__ 3 | 4 | #include "unichar.h" 5 | 6 | struct Value; 7 | struct PSTATE; 8 | struct ScopeChain; 9 | 10 | void utils_init(struct Value *global, int argc, char **argv); 11 | int utils_global_eval(struct PSTATE *ps, const char *program, 12 | struct ScopeChain *scope, struct Value *currentScope, 13 | struct Value *_this, struct Value *ret); 14 | #endif 15 | -------------------------------------------------------------------------------- /value.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "value.h" 6 | #include "error.h" 7 | #include "proto.h" 8 | #include "mempool.h" 9 | 10 | static mpool_t object_pool; 11 | static mpool_t value_pool; 12 | 13 | /* length unichar constant */ 14 | static UNISTR(6) LENGTH = { 6, { 'l', 'e', 'n', 'g', 't', 'h' } }; 15 | 16 | 17 | ObjKey *objkey_new(const unichar *strkey, int flag) 18 | { 19 | void *p = mm_alloc(unistrlen(strkey) * sizeof(unichar) + sizeof(int) * 2); 20 | ObjKey *ok = (ObjKey *)(((int)p) + sizeof(int) * 2); 21 | 22 | unistrcpy((unichar *)ok, strkey); 23 | KEYFLAG(ok) = flag; 24 | 25 | return ok; 26 | } 27 | 28 | ObjKey *objkey_dup(const ObjKey *ori) 29 | { 30 | int size = unistrlen(ori) * sizeof(unichar) + sizeof(int) * 2; 31 | int *p = mm_alloc(size); 32 | memcpy(p, (void *)(((int)ori) - sizeof(int) * 2), size); 33 | 34 | return (ObjKey *) (&p[2]); 35 | } 36 | 37 | static int objkey_compare(void* left, void* right) 38 | { 39 | ObjKey *a = left; 40 | ObjKey *b = right; 41 | 42 | return unistrcmp(b, a); 43 | } 44 | 45 | static void objkey_free(void* data) 46 | { 47 | /* printf("Free key: "); 48 | _uniprint(data); */ 49 | mm_free((void *)(((int)data) - sizeof(int) * 2)); 50 | } 51 | 52 | IterObj *iterobj_new() 53 | { 54 | IterObj *io = malloc(sizeof(IterObj)); 55 | memset(io, 0, sizeof(IterObj)); 56 | return io; 57 | } 58 | 59 | void iterobj_free(IterObj *iobj) 60 | { 61 | int i; 62 | for (i = 0; i < iobj->count; i++) { 63 | objkey_free(iobj->keys[i]); 64 | } 65 | free(iobj->keys); 66 | free(iobj); 67 | } 68 | 69 | static void iterobj_insert(IterObj *io, ObjKey *key) 70 | { 71 | if (io->count >= io->size) { 72 | io->size += 20; 73 | io->keys = realloc(io->keys, io->size * sizeof(ObjKey *)); 74 | } 75 | io->keys[io->count] = objkey_dup(key); 76 | io->count++; 77 | } 78 | 79 | FuncObj *funcobj_new(Func *func) 80 | { 81 | FuncObj *f = mm_alloc(sizeof(FuncObj)); 82 | memset(f, 0, sizeof(FuncObj)); 83 | f->func = func; 84 | return f; 85 | } 86 | 87 | void funcobj_free(FuncObj *fobj) 88 | { 89 | if (fobj->scope) scope_chain_free(fobj->scope); 90 | 91 | /* Do not free fobj->func, always constant */ 92 | mm_free(fobj); 93 | } 94 | 95 | /* raw object */ 96 | Object *object_new() 97 | { 98 | Object *obj = mpool_alloc(object_pool); 99 | memset(obj, 0, sizeof(Object)); 100 | obj->tree = rbtree_create(); 101 | return obj; 102 | } 103 | 104 | void object_free(Object *obj) 105 | { 106 | /* printf("Free obj: %x\n", (int)obj); */ 107 | switch (obj->ot) { 108 | case OT_STRING: 109 | unifree(obj->d.str); 110 | break; 111 | case OT_FUNCTION: 112 | funcobj_free(obj->d.fobj); 113 | break; 114 | case OT_ITER: 115 | iterobj_free(obj->d.iobj); 116 | break; 117 | case OT_USERDEF: 118 | userdata_free(obj->d.uobj); 119 | break; 120 | /* todo regex destroy */ 121 | default: 122 | break; 123 | } 124 | rbtree_destroy(obj->tree); 125 | mpool_free(obj, object_pool); 126 | } 127 | 128 | extern Value *Object_prototype; 129 | Object *object_make(Value *items, int count) 130 | { 131 | Object *obj = object_new(); 132 | int i; 133 | for (i = 0; i < count; i += 2) { 134 | if (items[i].vt != VT_STRING) bug("Making object\n"); 135 | ObjKey *ok = objkey_new(items[i].d.str, 0); 136 | Value *v = value_new(); 137 | value_copy(*v, items[i+1]); 138 | rbtree_insert(obj->tree, ok, v); 139 | } 140 | obj->__proto__ = Object_prototype; 141 | return obj; 142 | } 143 | 144 | extern Value *Array_prototype; 145 | Object *object_make_array(Value *items, int count) 146 | { 147 | Object *obj = object_new(); 148 | UNISTR(12) unibuf; 149 | int i; 150 | for (i = 0; i < count; ++i) { 151 | num_itoa10(i, unibuf.unistr); 152 | ObjKey *ok = objkey_new(unibuf.unistr, 0); 153 | Value *v = value_new(); 154 | value_copy(*v, items[i]); 155 | rbtree_insert(obj->tree, ok, v); 156 | } 157 | object_set_length(obj, count); 158 | obj->__proto__ = Array_prototype; 159 | return obj; 160 | } 161 | 162 | Value *value_new() 163 | { 164 | Value *v = mpool_alloc(value_pool); 165 | memset(v, 0, sizeof(Value)); 166 | return v; 167 | } 168 | 169 | Value *value_dup(Value *v) 170 | { 171 | Value *r = value_new(); 172 | value_copy(*r, *v); 173 | return r; 174 | } 175 | 176 | void value_free(void* data) 177 | { 178 | Value *v = data; 179 | value_erase(*v); 180 | mpool_free(v, value_pool); 181 | } 182 | 183 | /* far diff away from ecma, but behavior is the same */ 184 | void value_toprimitive(Value *v) 185 | { 186 | if (v->vt == VT_OBJECT) { 187 | Value res; 188 | Object *obj = v->d.obj; 189 | switch(obj->ot) { 190 | case OT_BOOL: 191 | value_make_bool(res, obj->d.val); 192 | break; 193 | case OT_NUMBER: 194 | value_make_number(res, obj->d.num); 195 | break; 196 | case OT_STRING: 197 | value_make_string(res, unistrdup(obj->d.str)); 198 | break; 199 | default: 200 | value_make_string(res, unistrdup_str("[object Object]")); 201 | break; 202 | } 203 | value_erase(*v); 204 | *v = res; 205 | } 206 | } 207 | 208 | static UNISTR(4) USTRUE = { 4, {'t','r','u','e'}}; 209 | static UNISTR(5) USFALSE = { 5, {'f','a','l','s','e'}}; 210 | static UNISTR(4) USNULL = { 4, {'n','u','l','l'}}; 211 | static UNISTR(3) USNAN = { 3, {'N','a','N'}}; 212 | static UNISTR(8) USINF = { 8, {'I','n','f','i','n','i','t','y'}}; 213 | static UNISTR(8) USNINF = { 9, {'-','I','n','f','i','n','i','t','y'}}; 214 | static UNISTR(15) USOBJ = { 15, {'[','o','b','j','e','c','t',' ','O','b','j','e','c','t',']'}}; 215 | static UNISTR(9) USUNDEF = { 9, {'u','n','d','e','f','i','n','e','d'}}; 216 | 217 | void value_tostring(Value *v) 218 | { 219 | const unichar *ntxt = NULL; 220 | UNISTR(100) unibuf; 221 | switch(v->vt) { 222 | case VT_BOOL: 223 | ntxt = v->d.val ? USTRUE.unistr:USFALSE.unistr; 224 | break; 225 | case VT_NULL: 226 | ntxt = USNULL.unistr; 227 | break; 228 | case VT_NUMBER: { 229 | if (is_integer(v->d.num)) { 230 | num_itoa10((int)v->d.num, unibuf.unistr); 231 | ntxt = unibuf.unistr; 232 | } else if (ieee_isnormal(v->d.num)) { 233 | num_dtoa2(v->d.num, unibuf.unistr, 10); 234 | ntxt = unibuf.unistr; 235 | } else if (ieee_isnan(v->d.num)) { 236 | ntxt = USNAN.unistr; 237 | } else { 238 | int s = ieee_infinity(v->d.num); 239 | if (s > 0) ntxt = USINF.unistr; 240 | else if (s < 0) ntxt = USNINF.unistr; 241 | else bug("Ieee function got problem"); 242 | } 243 | break; 244 | } 245 | case VT_OBJECT: { 246 | Object *obj = v->d.obj; 247 | switch(obj->ot) { 248 | case OT_BOOL: 249 | ntxt = v->d.val ? USTRUE.unistr:USFALSE.unistr; 250 | break; 251 | case OT_NUMBER: 252 | if (is_integer(obj->d.num)) { 253 | num_itoa10((int)obj->d.num, unibuf.unistr); 254 | ntxt = unibuf.unistr; 255 | } else if (ieee_isnormal(obj->d.num)) { 256 | num_dtoa2(obj->d.num, unibuf.unistr, 10); 257 | ntxt = unibuf.unistr; 258 | } else if (ieee_isnan(obj->d.num)) { 259 | ntxt = USNAN.unistr; 260 | } else { 261 | int s = ieee_infinity(obj->d.num); 262 | if (s > 0) ntxt = USINF.unistr; 263 | else if (s < 0) ntxt = USNINF.unistr; 264 | else bug("Ieee function got problem"); 265 | } 266 | break; 267 | case OT_STRING: 268 | ntxt = obj->d.str; 269 | break; 270 | default: 271 | ntxt = USOBJ.unistr; 272 | break; 273 | } 274 | break; 275 | } 276 | case VT_STRING: 277 | return; 278 | case VT_UNDEF: 279 | ntxt = USUNDEF.unistr; 280 | break; 281 | default: 282 | bug("Convert a unknown type: 0x%x to string\n", v->vt); 283 | break; 284 | } 285 | value_erase(*v); /* may cause problem, gc multithread: erased, but still dup ntxt */ 286 | value_make_string(*v, unistrdup(ntxt)); 287 | } 288 | 289 | void value_tonumber(Value *v) 290 | { 291 | double a = 0; 292 | switch(v->vt) { 293 | case VT_BOOL: 294 | a = (double)(v->d.val ? 1.0: 0); 295 | break; 296 | case VT_NULL: 297 | a = 0; 298 | break; 299 | case VT_OBJECT: { 300 | Object *obj = v->d.obj; 301 | switch(obj->ot) { 302 | case OT_BOOL: 303 | a = (double)(obj->d.val ? 1.0: 0); 304 | break; 305 | case OT_NUMBER: 306 | a = obj->d.num; 307 | break; 308 | case OT_STRING: 309 | a = atof(tochars(obj->d.str)); 310 | break; 311 | default: 312 | a = 0; 313 | break; 314 | } 315 | break; 316 | } 317 | case VT_UNDEF: 318 | a = ieee_makenan(); 319 | break; 320 | case VT_NUMBER: 321 | return; 322 | case VT_STRING: /* todo, NaN */ 323 | a = atof(tochars(v->d.str)); 324 | break; 325 | default: 326 | bug("Convert a unknown type: 0x%x to number\n", v->vt); 327 | break; 328 | } 329 | value_erase(*v); 330 | value_make_number(*v, a); 331 | } 332 | 333 | void value_toint32(Value *v) 334 | { 335 | double d = 0.0; 336 | value_tonumber(v); 337 | if (ieee_isnormal(v->d.num)) d = v->d.num; 338 | 339 | /* todo, not standard procedure */ 340 | v->d.num = (double)((int)d); 341 | } 342 | 343 | void value_toobject(Value *v) 344 | { 345 | if (v->vt == VT_OBJECT) return; 346 | Object *o = object_new(); 347 | switch(v->vt) { 348 | case VT_UNDEF: 349 | case VT_NULL: 350 | die("Can not convert a undefined/null value to object\n"); 351 | case VT_BOOL: { 352 | o->d.val = v->d.val; 353 | o->ot = OT_BOOL; 354 | o->__proto__ = Boolean_prototype; 355 | break; 356 | } 357 | case VT_NUMBER: { 358 | o->d.num = v->d.num; 359 | o->ot = OT_NUMBER; 360 | o->__proto__ = Number_prototype; 361 | break; 362 | } 363 | case VT_STRING: { 364 | o->d.str = unistrdup(v->d.str); 365 | o->ot = OT_STRING; 366 | o->__proto__ = String_prototype; 367 | int i; 368 | int len = unistrlen(o->d.str); 369 | for (i = 0; i < len; ++i) { 370 | Value *v = value_new(); 371 | value_make_string(*v, unisubstrdup(o->d.str, i, 1)); 372 | object_utils_insert_array(o, i, v, 0, 0, 1); 373 | } 374 | break; 375 | } 376 | default: 377 | bug("toobject, not suppose to reach here\n"); 378 | } 379 | value_erase(*v); 380 | value_make_object(*v, o); 381 | } 382 | 383 | /* also toBoolean here, in ecma */ 384 | int value_istrue(Value *v) 385 | { 386 | switch(v->vt) { 387 | case VT_UNDEF: 388 | case VT_NULL: return 0; 389 | case VT_BOOL: return v->d.val ? 1:0; 390 | case VT_NUMBER: 391 | if (v->d.num == 0.0 || ieee_isnan(v->d.num)) return 0; 392 | return 1; 393 | case VT_STRING: return unistrlen(v->d.str) ? 1:0; 394 | case VT_OBJECT: { 395 | Object *o = v->d.obj; 396 | if (o->ot == OT_USERDEF) { 397 | return userdata_istrue(o->d.uobj); 398 | } 399 | return 1; 400 | } 401 | default: bug("TOP is type incorrect: %d\n", v->vt); 402 | } 403 | return 0; 404 | } 405 | 406 | void object_insert(Object *obj, ObjKey *key, Value *value) 407 | { 408 | int ret = rbtree_insert(obj->tree, key, value); 409 | if (ret < 0) warn("Can not assign to a read-only key\n"); 410 | } 411 | 412 | void value_object_insert(Value *target, ObjKey *key, Value *value) 413 | { 414 | if (target->vt != VT_OBJECT) { 415 | warn("Target is not object: %d\n", target->vt); 416 | return; 417 | } 418 | 419 | object_insert(target->d.obj, key, value); 420 | } 421 | 422 | void object_try_extern(Object *obj, int inserted_index) 423 | { 424 | int len = object_get_length(obj); 425 | if (len < 0) return; 426 | 427 | if (len < inserted_index + 1) { 428 | object_set_length(obj, inserted_index + 1); 429 | } 430 | } 431 | 432 | Value *object_lookup(Object *obj, ObjKey *key, int *flag) 433 | { 434 | /* object faster accesser */ 435 | /* == these codes can be removed == */ 436 | int len = unistrlen(key); 437 | if (len >= 0 && len < 8) { 438 | ObjKey *tocmp = obj->_acc_keys[len]; 439 | if (tocmp && unistrcmp(key, tocmp) == 0) { 440 | if (flag) *flag = KEYFLAG(tocmp); 441 | return obj->_acc_values[len]; 442 | } 443 | } 444 | /* == end of removable == */ 445 | 446 | ObjKey *realok = NULL; 447 | 448 | Value *v = rbtree_lookup(obj->tree, key, &realok); 449 | 450 | if (v) { 451 | if (!realok) bug("Object has value, but no key?"); 452 | if (flag) *flag = KEYFLAG(realok); 453 | 454 | 455 | /* == == */ 456 | if (len >= 0 && len < 8) { 457 | obj->_acc_values[len] = v; 458 | obj->_acc_keys[len] = realok; 459 | } 460 | /* == == */ 461 | } 462 | 463 | return v; 464 | } 465 | 466 | Value *value_object_lookup(Value *target, ObjKey *key, int *flag) 467 | { 468 | if (target->vt != VT_OBJECT) { 469 | warn("Target is not object: %d\n", target->vt); 470 | return NULL; 471 | } 472 | 473 | return object_lookup(target->d.obj, key, flag); 474 | } 475 | 476 | Value *value_object_key_assign(Value *target, Value *key, Value *value, int flag) 477 | { 478 | ObjKey *ok = NULL; 479 | int arrayindex = -1; 480 | if (is_number(key) && is_integer(key->d.num) && key->d.num >= 0) { 481 | arrayindex = (int)key->d.num; 482 | } 483 | /* todo: array["1"] also extern the length of array */ 484 | 485 | value_tostring(key); 486 | ok = objkey_new(key->d.str, flag); 487 | 488 | Value *v = value_new(); 489 | value_copy(*v, *value); 490 | value_object_insert(target, ok, v); 491 | if (arrayindex >= 0) object_try_extern(target->d.obj, arrayindex); 492 | return v; 493 | } 494 | 495 | void value_object_delete(Value *target, Value *key) 496 | { 497 | if (target->vt != VT_OBJECT) return; 498 | 499 | value_tostring(key); 500 | 501 | int flag = 0; 502 | object_lookup(target->d.obj, key->d.str, &flag); 503 | if (bits_get(flag, OM_DONTDEL)) return; 504 | 505 | /* all reset to NULL */ 506 | int i; 507 | for (i = 0; i < 8; ++i) { 508 | target->d.obj->_acc_values[i] = NULL; 509 | target->d.obj->_acc_keys[i] = NULL; 510 | } 511 | rbtree_delete(target->d.obj->tree, key->d.str); 512 | } 513 | 514 | void value_subscript(Value *target, Value *key, Value *ret, int right_val) 515 | { 516 | if (!target) { 517 | value_make_undef(*ret); 518 | return; 519 | } 520 | 521 | if (target->vt != VT_OBJECT) { 522 | bug("subscript operand is not object\n"); 523 | } 524 | 525 | /* faster string[i] access */ 526 | /* == these codes can be removed == */ 527 | if (right_val && target->d.obj->ot == OT_STRING && 528 | is_number(key) && is_integer(key->d.num)) { 529 | int ti = (int)key->d.num; 530 | unichar *s = target->d.obj->d.str; 531 | int len = unistrlen(s); 532 | if (ti >= 0 && ti < len) { 533 | value_make_string(*ret, unisubstrdup(s, ti, 1)); 534 | return; 535 | } 536 | } 537 | /* == end of removable codes == */ 538 | 539 | value_tostring(key); 540 | 541 | int flag = 0; 542 | Value *r = value_object_lookup(target, (ObjKey *)key->d.str, &flag); 543 | if (!r) { 544 | /* query from prototype, always no right_val */ 545 | if (target->d.obj->__proto__) { 546 | value_subscript(target->d.obj->__proto__, key, ret, 1); 547 | } 548 | if (right_val == 0) { /* need a lvalue */ 549 | Value *n = value_new(); 550 | value_copy(*n, *ret); /* copy from prototype */ 551 | 552 | ObjKey *nk = objkey_new(key->d.str, 0); 553 | value_object_insert(target, nk, n); 554 | 555 | value_erase(*ret); 556 | ret->vt = VT_VARIABLE; 557 | ret->d.lval = n; 558 | } 559 | } else { 560 | if (right_val || bits_get(flag, OM_READONLY)) { 561 | value_copy(*ret, *r); 562 | } else { 563 | ret->vt = VT_VARIABLE; 564 | ret->d.lval = r; 565 | } 566 | } 567 | } 568 | 569 | int value_key_present(Value *target, ObjKey *k) 570 | { 571 | if (target->vt != VT_OBJECT) return 0; 572 | 573 | if (value_object_lookup(target, k, NULL)) return 1; 574 | if (!target->d.obj->__proto__) return 0; 575 | return value_key_present(target->d.obj->__proto__, k); 576 | } 577 | 578 | static int _object_getkeys_callback(void *key, void *value, void *userdata) 579 | { 580 | ObjKey *ok = key; 581 | IterObj *io = userdata; 582 | int flag = KEYFLAG(ok); 583 | 584 | if (!bits_get(flag, OM_DONTEMU)) { 585 | iterobj_insert(io, ok); 586 | } 587 | return 0; 588 | } 589 | 590 | static void _object_getkeys(Value *target, IterObj *iterobj) 591 | { 592 | if (!target) return; 593 | if (target->vt != VT_OBJECT) { 594 | warn("operand is not a object\n"); 595 | return; 596 | } 597 | rbtree_walk(target->d.obj->tree, iterobj, _object_getkeys_callback); 598 | _object_getkeys(target->d.obj->__proto__, iterobj); 599 | } 600 | 601 | void value_object_getkeys(Value *target, Value *ret) 602 | { 603 | IterObj *io = iterobj_new(); 604 | 605 | _object_getkeys(target, io); 606 | Object *r = object_new(); 607 | r->ot = OT_ITER; 608 | r->d.iobj = io; 609 | 610 | value_make_object(*ret, r); 611 | } 612 | 613 | ScopeChain *scope_chain_new(int cnt) 614 | { 615 | ScopeChain *r = mm_alloc(sizeof(ScopeChain)); 616 | memset(r, 0, sizeof(ScopeChain)); 617 | r->chains = mm_alloc(cnt * sizeof(Value *)); 618 | memset(r->chains, 0, cnt * sizeof(Value *)); 619 | r->chains_cnt = cnt; 620 | return r; 621 | } 622 | 623 | Value *scope_chain_object_lookup(ScopeChain *sc, ObjKey *key) 624 | { 625 | int i; 626 | Value *ret; 627 | for (i = sc->chains_cnt - 1; i >= 0; --i) { 628 | if ((ret = value_object_lookup(sc->chains[i], key, NULL))) { 629 | return ret; 630 | } 631 | } 632 | return NULL; 633 | } 634 | 635 | ScopeChain *scope_chain_dup_next(ScopeChain *sc, Value *next) 636 | { 637 | if (!sc) { 638 | ScopeChain *nr = scope_chain_new(1); 639 | nr->chains[0] = value_new(); 640 | value_copy(*(nr->chains[0]), *next); 641 | nr->chains_cnt = 1; 642 | return nr; 643 | } 644 | ScopeChain *r = scope_chain_new(sc->chains_cnt + 1); 645 | int i; 646 | for (i = 0; i < sc->chains_cnt; ++i) { 647 | r->chains[i] = value_new(); 648 | value_copy(*(r->chains[i]), *(sc->chains[i])); 649 | } 650 | r->chains[i] = value_new(); 651 | value_copy(*(r->chains[i]), *next); 652 | r->chains_cnt = i + 1; 653 | return r; 654 | } 655 | 656 | void scope_chain_free(ScopeChain *sc) 657 | { 658 | int i; 659 | for (i = 0; i < sc->chains_cnt; ++i) { 660 | value_free(sc->chains[i]); 661 | } 662 | mm_free(sc->chains); 663 | mm_free(sc); 664 | } 665 | 666 | /* quick set length of an object */ 667 | void object_set_length(Object *obj, int len) 668 | { 669 | int flag = 0; 670 | Value *r = object_lookup(obj, (ObjKey *)LENGTH.unistr, &flag); 671 | if (!r) { 672 | Value *n = value_new(); 673 | value_make_number(*n, len); 674 | 675 | ObjKey *nk = objkey_new(LENGTH.unistr, OM_DONTDEL | OM_DONTEMU | OM_READONLY); 676 | object_insert(obj, nk, n); 677 | } else { 678 | value_make_number(*r, len); 679 | } 680 | } 681 | 682 | int object_get_length(Object *obj) 683 | { 684 | int flag; 685 | Value *r = object_lookup(obj, (ObjKey *)LENGTH.unistr, &flag); 686 | if (r && is_number(r)) { 687 | if (is_integer(r->d.num)) return (int)r->d.num; 688 | } 689 | return -1; 690 | } 691 | 692 | int value_get_length(Value *v) 693 | { 694 | if (v->vt != VT_OBJECT) return -1; 695 | 696 | return object_get_length(v->d.obj); 697 | } 698 | 699 | 700 | /* get argv[i] */ 701 | Value *value_object_lookup_array(Value *args, int index, int *flag) 702 | { 703 | UNISTR(12) unibuf; 704 | num_itoa10(index, unibuf.unistr); 705 | 706 | return object_lookup(args->d.obj, unibuf.unistr, flag); 707 | } 708 | 709 | 710 | Value *value_object_utils_new_object() 711 | { 712 | Value *n = value_new(); 713 | value_make_object(*n, object_new()); 714 | return n; 715 | } 716 | 717 | void object_utils_insert(Object *obj, const unichar *key, Value *val, 718 | int deletable, int writable, int emuable) 719 | { 720 | int flag = 0; 721 | if (!deletable) flag |= OM_DONTDEL; 722 | if (!writable) flag |= OM_READONLY; 723 | if (!emuable) flag |= OM_DONTEMU; 724 | 725 | ObjKey *ok = objkey_new(key, flag); 726 | object_insert(obj, ok, val); 727 | } 728 | 729 | void value_object_utils_insert(Value *target, const unichar *key, Value *val, 730 | int deletable, int writable, int emuable) 731 | { 732 | if (target->vt != VT_OBJECT) { 733 | warn("Target is not object\n"); 734 | return; 735 | } 736 | 737 | object_utils_insert(target->d.obj, key, val, deletable, writable, emuable); 738 | } 739 | 740 | void object_utils_insert_array(Object *obj, int key, Value *val, 741 | int deletable, int writable, int emuable) 742 | { 743 | int flag = 0; 744 | if (!deletable) flag |= OM_DONTDEL; 745 | if (!writable) flag |= OM_READONLY; 746 | if (!emuable) flag |= OM_DONTEMU; 747 | 748 | UNISTR(12) unibuf; 749 | num_itoa10(key, unibuf.unistr); 750 | 751 | ObjKey *ok = objkey_new(unibuf.unistr, flag); 752 | object_insert(obj, ok, val); 753 | object_try_extern(obj, key); 754 | } 755 | 756 | void value_object_utils_insert_array(Value *target, int key, Value *val, 757 | int deletable, int writable, int emuable) 758 | { 759 | if (target->vt != VT_OBJECT) { 760 | warn("Target is not object\n"); 761 | return; 762 | } 763 | object_utils_insert_array(target->d.obj, key, val, deletable, writable, emuable); 764 | } 765 | 766 | static UserDataReg *global_userdataregs[MAX_UDTYPE]; 767 | static int global_userdataregs_cnt; 768 | udid userdata_register(UserDataReg *udreg) 769 | { 770 | int i = global_userdataregs_cnt; 771 | if (i >= MAX_UDTYPE) return -1; 772 | 773 | global_userdataregs[i] = udreg; 774 | global_userdataregs_cnt++; 775 | return i; 776 | } 777 | 778 | UserData *userdata_new(udid id, void *data) 779 | { 780 | UserData *ud = mm_alloc(sizeof(UserData)); 781 | ud->id = id; 782 | ud->data = data; 783 | return ud; 784 | } 785 | 786 | void userdata_free(UserData *ud) 787 | { 788 | udid id = ud->id; 789 | 790 | if (id < 0 || id >= global_userdataregs_cnt) { 791 | die("UDID error\n"); 792 | } 793 | if (global_userdataregs[id]->freefun) { 794 | global_userdataregs[id]->freefun(ud->data); 795 | } 796 | mm_free(ud); 797 | } 798 | 799 | void userdata_set(Object *obj, udid id, void *data) 800 | { 801 | if (obj->ot != OT_OBJECT) bug("userdata_assign to a non raw object\n"); 802 | obj->d.uobj = userdata_new(id, data); 803 | obj->ot = OT_USERDEF; 804 | } 805 | 806 | void *userdata_get(Object *obj, udid id) 807 | { 808 | if (obj->ot != OT_USERDEF) { 809 | warn("Object not userdefined type\n"); 810 | return NULL; 811 | } 812 | UserData *ud = obj->d.uobj; 813 | if (ud->id != id) { 814 | warn("Get_userdata, id not match\n"); 815 | return NULL; 816 | } 817 | return ud->data; 818 | } 819 | 820 | int userdata_istrue(UserData *ud) 821 | { 822 | int id = ud->id; 823 | if (id < 0 || id >= global_userdataregs_cnt) die("UDID error\n"); 824 | if (global_userdataregs[id]->istrue) { 825 | return global_userdataregs[id]->istrue(ud->data); 826 | } 827 | return 1; 828 | } 829 | 830 | static void objvalue_free(void *key, void *value) 831 | { 832 | int flag = KEYFLAG(key); 833 | if (bits_get(flag, OM_INNERSHARED)) return; 834 | /* printf("Free value of key: "); 835 | _uniprint(key); */ 836 | value_free(value); 837 | } 838 | 839 | static void objvalue_vreplace(void *target, void* from) 840 | { 841 | Value *tv = target; 842 | Value *fv = from; 843 | value_erase(*tv); 844 | value_copy(*tv, *fv); 845 | } 846 | 847 | static void objvalue_lookup_helper(void *key, void* userdata) 848 | { 849 | if (!userdata) return; 850 | ObjKey **outkey = userdata; 851 | *outkey = key; 852 | } 853 | 854 | static int objvalue_insert_helper(void *key) 855 | { 856 | int flag = KEYFLAG(key); 857 | if (bits_get(flag, OM_READONLY)) { 858 | return -1; 859 | } 860 | return 0; 861 | } 862 | 863 | void objects_init() 864 | { 865 | object_pool = mpool_create(sizeof(Object)); 866 | value_pool = mpool_create(sizeof(Value)); 867 | 868 | rbtree_module_init(objkey_compare, objkey_free, 869 | objvalue_free, objvalue_vreplace, 870 | objvalue_lookup_helper, objvalue_insert_helper); 871 | } 872 | 873 | -------------------------------------------------------------------------------- /value.h: -------------------------------------------------------------------------------- 1 | #ifndef __VALUE_H__ 2 | #define __VALUE_H__ 3 | 4 | #include 5 | 6 | #include "unichar.h" 7 | #include "rbtree.h" 8 | #include "scope.h" 9 | #include "func.h" 10 | #include "number.h" 11 | 12 | typedef enum { /* type constructor Data in Value Implicit prototype */ 13 | VT_UNDEF, /* undefined none none none */ 14 | VT_NULL, /* null none none none */ 15 | VT_BOOL, /* boolean Boolean d.val none */ 16 | VT_NUMBER, /* number Number d.num Number.prototype */ 17 | VT_STRING, /* string String d.str String.prototype */ 18 | VT_OBJECT, /* object Object d.obj Object.prototype */ 19 | VT_VARIABLE /* lvalue none d.lval none */ 20 | } vtype; 21 | 22 | typedef unsigned int udid; 23 | 24 | /* note: UserData example, see filesys.ex.c */ 25 | typedef struct UserData { 26 | udid id; 27 | void *data; 28 | } UserData; 29 | 30 | typedef void (*SSUDFree)(void *data); 31 | typedef int (*SSUDIsTrue)(void *data); 32 | typedef int (*SSUDIsEqu)(void *data1, void *data2); 33 | 34 | #define MAX_UDTYPE 1024 35 | typedef struct UserDataReg { 36 | const char *name; 37 | SSUDFree freefun; 38 | SSUDIsTrue istrue; 39 | SSUDIsEqu isequ; 40 | } UserDataReg; 41 | 42 | #define OM_READONLY 0x1 /* ecma read-only */ 43 | #define OM_DONTEMU 0x2 /* ecma emumerable */ 44 | #define OM_DONTDEL 0x4 /* ecma configurable */ 45 | 46 | #define OM_INNERSHARED 0x10 /* shared the same value insider */ 47 | 48 | #define bits_set(who, mask) ((who) |= (mask)) 49 | #define bits_unset(who, mask) ((who) &= (~(mask))) 50 | #define bits_get(who, mask) ((who) & (mask)) 51 | 52 | /* with an ObjKey, here is how it store 53 | * 54 | * - an unichar * pointed here 55 | * | ObjKey also pointed here 56 | * | 57 | * | 4 | 4 | unichars | 58 | * | | 59 | * | - len of unichars 60 | * - flag of objkey 61 | */ 62 | typedef unichar ObjKey; 63 | 64 | #define KEYFLAG(ok) (*((int *)(((int)(ok)) - 2 * sizeof(int)))) 65 | #define OBJKEY(_len) struct{int flag;UNISTR(_len) str;} 66 | 67 | /* Scope chain */ 68 | typedef struct ScopeChain { 69 | struct Value **chains; /* values(objects) */ 70 | int chains_cnt; /* count */ 71 | } ScopeChain; 72 | 73 | /* Function obj */ 74 | /* a FuncObj is a raw function with own scope chain */ 75 | typedef struct FuncObj { 76 | struct Func *func; 77 | ScopeChain *scope; 78 | } FuncObj; 79 | 80 | /* IterObj, use only in for-in statement */ 81 | typedef struct IterObj { 82 | ObjKey **keys; 83 | int size; 84 | int count; 85 | int iter; 86 | } IterObj; 87 | 88 | typedef enum { 89 | OT_OBJECT, /* common object, not use d */ 90 | OT_BOOL, /* Boolean object, use d.val */ 91 | OT_NUMBER, /* Number object, use d.num */ 92 | OT_STRING, /* String object, use d.str */ 93 | OT_FUNCTION, /* Function object, use d.fobj */ 94 | OT_REGEXP, /* RegExp object, use d.robj */ 95 | OT_ITER, /* Iter object, use d.iobj */ 96 | OT_USERDEF /* UserDefined object, use d.uobj */ 97 | } otype; 98 | 99 | typedef struct Object { 100 | otype ot; /* object type */ 101 | union { /* see above */ 102 | int val; 103 | double num; 104 | unichar *str; 105 | FuncObj *fobj; 106 | regex_t *robj; 107 | IterObj *iobj; 108 | UserData *uobj; 109 | } d; 110 | /* 111 | struct Value *acc_length; for array object, faster access 112 | int acc_length_flag; keyflag of length value 113 | */ 114 | 115 | /* faster access keys of object */ 116 | struct Value *_acc_values[8]; 117 | ObjKey *_acc_keys[8]; 118 | 119 | rbtree tree; /* store key-value */ 120 | int __refcnt; /* reference count */ 121 | struct Value *__proto__; /* implicit prototype */ 122 | } Object; 123 | 124 | typedef struct Value { 125 | vtype vt; /* value type */ 126 | union { /* see above */ 127 | int val; 128 | double num; 129 | unichar *str; 130 | Object *obj; 131 | struct Value *lval; 132 | } d; 133 | } Value; 134 | 135 | #define objref_inc(obj) do { \ 136 | (obj)->__refcnt++; \ 137 | } while(0) 138 | 139 | #define objref_dec(obj) do { \ 140 | if (--((obj)->__refcnt) <= 0) object_free((obj)); \ 141 | } while(0) 142 | 143 | #define value_erase(v) do { \ 144 | if ((v).vt == VT_STRING) unifree((v).d.str); \ 145 | else if ((v).vt == VT_OBJECT) objref_dec((v).d.obj); \ 146 | (v).vt = VT_UNDEF; \ 147 | } while(0) 148 | 149 | #define value_copy(to, from) do { \ 150 | (to) = (from); \ 151 | if ((to).vt == VT_STRING) (to).d.str = unistrdup((to).d.str); \ 152 | else if ((to).vt == VT_OBJECT) objref_inc((to).d.obj); \ 153 | } while(0) 154 | 155 | #define value_make_object(v, o) do { \ 156 | (v).vt = VT_OBJECT; \ 157 | (v).d.obj = (o); \ 158 | objref_inc((v).d.obj); \ 159 | } while(0) 160 | 161 | #define value_make_number(v, n) do { \ 162 | (v).vt = VT_NUMBER; \ 163 | (v).d.num = (n); \ 164 | } while(0) 165 | 166 | #define value_make_bool(v, b) do { \ 167 | (v).vt = VT_BOOL; \ 168 | (v).d.val = (b); \ 169 | } while(0) 170 | 171 | #define value_make_string(v, s) do { \ 172 | (v).vt = VT_STRING; \ 173 | (v).d.str = (s); \ 174 | } while(0) 175 | 176 | #define value_make_null(v) do { \ 177 | (v).vt = VT_NULL; \ 178 | } while(0) 179 | 180 | #define value_make_undef(v) do { \ 181 | (v).vt = VT_UNDEF; \ 182 | } while(0) 183 | 184 | #define is_integer(n) (ieee_isnormal(n) && (double)((int)(n)) == (n)) 185 | #define is_number(pv) ((pv)->vt == VT_NUMBER) 186 | #define is_string(pv) ((pv)->vt == VT_STRING) 187 | #define is_bool(pv) ((pv)->vt == VT_BOOL) 188 | #define is_undef(pv) ((pv)->vt == VT_UNDEF) 189 | #define is_null(pv) ((pv)->vt == VT_NULL) 190 | #define obj_isarray(o) ((o)->ot == OT_OBJECT && object_get_length(o) >= 0) 191 | 192 | ObjKey *objkey_new(const unichar *strkey, int flag); 193 | ObjKey *objkey_dup(const ObjKey *ori); 194 | 195 | IterObj *iterobj_new(); 196 | FuncObj *funcobj_new(Func *func); 197 | 198 | Object *object_new(); 199 | void object_free(Object *obj); 200 | Object *object_make(Value *items, int count); 201 | Object *object_make_array(Value *items, int count); 202 | 203 | Value *value_new(); 204 | Value *value_dup(Value *v); 205 | void value_free(void* data); 206 | void value_toprimitive(Value *v); 207 | void value_tostring(Value *v); 208 | void value_tonumber(Value *v); 209 | void value_toint32(Value *v); 210 | void value_toobject(Value *v); 211 | int value_istrue(Value *v); 212 | 213 | void object_insert(Object *obj, ObjKey *key, Value *value); 214 | void value_object_insert(Value *target, ObjKey *key, Value *value); 215 | void object_try_extern(Object *obj, int inserted_index); 216 | Value *object_lookup(Object *obj, ObjKey *key, int *flag); 217 | Value *value_object_lookup(Value *target, ObjKey *key, int *flag); 218 | Value *value_object_key_assign(Value *target, Value *key, Value *value, int flag); 219 | void value_object_delete(Value *target, Value *key); 220 | void value_subscript(Value *target, Value *key, Value *ret, int right_val); 221 | int value_key_present(Value *target, ObjKey *k); 222 | void value_object_getkeys(Value *target, Value *ret); 223 | 224 | ScopeChain *scope_chain_new(int cnt); 225 | Value *scope_chain_object_lookup(ScopeChain *sc, ObjKey *key); 226 | ScopeChain *scope_chain_dup_next(ScopeChain *sc, Value *next); 227 | void scope_chain_free(ScopeChain *sc); 228 | 229 | void object_set_length(Object *obj, int len); 230 | int object_get_length(Object *obj); 231 | int value_get_length(Value *v); 232 | Value *value_object_lookup_array(Value *args, int index, int *flag); 233 | 234 | Value *value_object_utils_new_object(); 235 | void object_utils_insert(Object *obj, const unichar *key, Value *val, 236 | int deletable, int writable, int emuable); 237 | void value_object_utils_insert(Value *target, const unichar *key, Value *val, 238 | int deletable, int writable, int emuable); 239 | void object_utils_insert_array(Object *obj, int key, Value *val, 240 | int deletable, int writable, int emuable); 241 | void value_object_utils_insert_array(Value *target, int key, Value *val, 242 | int deletable, int writable, int emuable); 243 | 244 | udid userdata_register(UserDataReg *udreg); 245 | UserData *userdata_new(udid id, void *data); 246 | void userdata_free(UserData *ud); 247 | void userdata_set(Object *obj, udid id, void *data); 248 | void *userdata_get(Object *obj, udid id); 249 | int userdata_istrue(UserData *ud); 250 | 251 | void objects_init(); 252 | 253 | 254 | #endif 255 | 256 | --------------------------------------------------------------------------------