| 36 | | evaluate: function evaluate() { |
| 37 | | try { |
| 38 | | while(true) { |
| 39 | | var insn = this.sequence[this.pc]; |
| 40 | | this.dispatch(insn); |
| 41 | | } |
| 42 | | } catch(e) { |
| 43 | | if(!(e instanceof HSPException)) { |
| 44 | | throw e; |
| 45 | | } |
| 46 | | this.disposeException(e); |
| 47 | | } |
| 48 | | }, |
| 49 | | resume: function resume(callback) { |
| 50 | | try { |
| 51 | | if(callback) callback(); |
| 52 | | this.pc ++; |
| 53 | | while(true) { |
| 54 | | var insn = this.sequence[this.pc]; |
| 55 | | this.dispatch(insn); |
| 56 | | } |
| 57 | | } catch(e) { |
| 58 | | if(!(e instanceof HSPException)) { |
| 59 | | throw e; |
| 60 | | } |
| 61 | | this.disposeException(e); |
| 62 | | } |
| 63 | | }, |
| 64 | | dispatch: function dispatch(insn) { |
| 65 | | var lhs, rhs; |
| 66 | | switch(insn.code) { |
| 67 | | case Instruction.Code.NOP: |
| 68 | | break; |
| 69 | | case Instruction.Code.PUSH: |
| 70 | | this.stack.push(insn.opts[0]); |
| 71 | | break; |
| 72 | | case Instruction.Code.PUSH_VAR: |
| 73 | | var varId = insn.opts[0]; |
| 74 | | var argc = insn.opts[1]; |
| 75 | | var indices = this.popIndices(argc); |
| 76 | | this.stack.push(new VariableAgent(this.variables[varId], indices)); |
| 77 | | break; |
| 78 | | case Instruction.Code.POP: |
| 79 | | this.stack.pop(); |
| 80 | | break; |
| 81 | | case Instruction.Code.DUP: |
| 82 | | this.stack.push(this.stack[this.stack.length-1]); |
| 83 | | break; |
| 84 | | case Instruction.Code.ADD: |
| 85 | | rhs = this.stack.pop(), lhs = this.stack.pop(); |
| 86 | | this.stack.push(lhs.add(rhs)); |
| 87 | | break; |
| 88 | | case Instruction.Code.SUB: |
| 89 | | rhs = this.stack.pop(), lhs = this.stack.pop(); |
| 90 | | this.stack.push(lhs.sub(rhs)); |
| 91 | | break; |
| 92 | | case Instruction.Code.MUL: |
| 93 | | rhs = this.stack.pop(), lhs = this.stack.pop(); |
| 94 | | this.stack.push(lhs.mul(rhs)); |
| 95 | | break; |
| 96 | | case Instruction.Code.DIV: |
| 97 | | rhs = this.stack.pop(), lhs = this.stack.pop(); |
| 98 | | this.stack.push(lhs.div(rhs)); |
| 99 | | break; |
| 100 | | case Instruction.Code.MOD: |
| 101 | | rhs = this.stack.pop(), lhs = this.stack.pop(); |
| 102 | | this.stack.push(lhs.mod(rhs)); |
| 103 | | break; |
| 104 | | case Instruction.Code.AND: |
| 105 | | rhs = this.stack.pop(), lhs = this.stack.pop(); |
| 106 | | this.stack.push(lhs.and(rhs)); |
| 107 | | break; |
| 108 | | case Instruction.Code.OR: |
| 109 | | rhs = this.stack.pop(), lhs = this.stack.pop(); |
| 110 | | this.stack.push(lhs.or(rhs)); |
| 111 | | break; |
| 112 | | case Instruction.Code.XOR: |
| 113 | | rhs = this.stack.pop(), lhs = this.stack.pop(); |
| 114 | | this.stack.push(lhs.xor(rhs)); |
| 115 | | break; |
| 116 | | case Instruction.Code.EQ: |
| 117 | | rhs = this.stack.pop(), lhs = this.stack.pop(); |
| 118 | | this.stack.push(lhs.eq(rhs)); |
| 119 | | break; |
| 120 | | case Instruction.Code.NE: |
| 121 | | rhs = this.stack.pop(), lhs = this.stack.pop(); |
| 122 | | this.stack.push(lhs.ne(rhs)); |
| 123 | | break; |
| 124 | | case Instruction.Code.GT: |
| 125 | | rhs = this.stack.pop(), lhs = this.stack.pop(); |
| 126 | | this.stack.push(lhs.gt(rhs)); |
| 127 | | break; |
| 128 | | case Instruction.Code.LT: |
| 129 | | rhs = this.stack.pop(), lhs = this.stack.pop(); |
| 130 | | this.stack.push(lhs.lt(rhs)); |
| 131 | | break; |
| 132 | | case Instruction.Code.GTEQ: |
| 133 | | rhs = this.stack.pop(), lhs = this.stack.pop(); |
| 134 | | this.stack.push(lhs.gteq(rhs)); |
| 135 | | break; |
| 136 | | case Instruction.Code.LTEQ: |
| 137 | | rhs = this.stack.pop(), lhs = this.stack.pop(); |
| 138 | | this.stack.push(lhs.lteq(rhs)); |
| 139 | | break; |
| 140 | | case Instruction.Code.RSH: |
| 141 | | rhs = this.stack.pop(), lhs = this.stack.pop(); |
| 142 | | this.stack.push(lhs.rsh(rhs)); |
| 143 | | break; |
| 144 | | case Instruction.Code.LSH: |
| 145 | | rhs = this.stack.pop(), lhs = this.stack.pop(); |
| 146 | | this.stack.push(lhs.lsh(rhs)); |
| 147 | | break; |
| 148 | | case Instruction.Code.GOTO: |
| 149 | | this.pc = insn.opts[0].pos; |
| 150 | | return; |
| 151 | | case Instruction.Code.IFNE: |
| 152 | | if(this.stack.pop().toIntValue()._value) { |
| 153 | | this.pc = insn.opts[0].pos; |
| 154 | | return; |
| 155 | | } |
| 156 | | break; |
| 157 | | case Instruction.Code.IFEQ: |
| 158 | | if(!this.stack.pop().toIntValue()._value) { |
| 159 | | this.pc = insn.opts[0].pos; |
| 160 | | return; |
| 161 | | } |
| 162 | | break; |
| 163 | | case Instruction.Code.SETVAR: |
| 164 | | var argc = insn.opts[0]; |
| 165 | | var args = Utils.aryPopN(this.stack, argc); |
| 166 | | var agent = this.stack.pop(); |
| 167 | | var variable = agent.variable; |
| 168 | | var indices = agent.indices.slice(); |
| 169 | | variable.assign(indices, args[0]); |
| 170 | | if(indices.length == 0) indices[0] = 0; |
| 171 | | indices[0] ++; |
| 172 | | for(var i = 1; i < argc; i ++) { |
| 173 | | variable.assign(indices, args[i]); |
| 174 | | indices[0] ++; |
| 175 | | } |
| 176 | | break; |
| 177 | | case Instruction.Code.EXPANDARRAY: |
| 178 | | var agent = this.stack[this.stack.length - 1]; |
| 179 | | agent.variable.expand(agent.indices); |
| 180 | | break; |
| 181 | | case Instruction.Code.INC: |
| 182 | | var agent = this.stack.pop(); |
| 183 | | agent.variable.expand(agent.indices); |
| 184 | | agent.assign(agent.add(new IntValue(1))); |
| 185 | | break; |
| 186 | | case Instruction.Code.DEC: |
| 187 | | var agent = this.stack.pop(); |
| 188 | | agent.variable.expand(agent.indices); |
| 189 | | agent.assign(agent.sub(new IntValue(1))); |
| 190 | | break; |
| 191 | | case Instruction.Code.CALL_BUILTIN_CMD: |
| 192 | | this.callBuiltinFunc(insn); |
| 193 | | break; |
| 194 | | case Instruction.Code.CALL_BUILTIN_FUNC: |
| 195 | | this.stack.push(this.callBuiltinFunc(insn)); |
| 196 | | break; |
| 197 | | case Instruction.Code.CALL_USERDEF_CMD: |
| 198 | | case Instruction.Code.CALL_USERDEF_FUNC: |
| 199 | | var userDefFunc = insn.opts[0]; |
| 200 | | var argc = insn.opts[1]; |
| 201 | | var args = Utils.aryPopN(this.stack, argc); |
| 202 | | this.callUserDefFunc(userDefFunc, args); |
| 203 | | break; |
| 204 | | case Instruction.Code.GETARG: |
| 205 | | var argNum = insn.opts[0]; |
| 206 | | this.stack.push(this.getArg(argNum)); |
| 207 | | break; |
| 208 | | case Instruction.Code.PUSH_ARG_VAR: |
| 209 | | var argNum = insn.opts[0]; |
| 210 | | var argc = insn.opts[1]; |
| 211 | | var variable = this.getArg(argNum); |
| 212 | | var indices = this.popIndices(argc); |
| 213 | | this.stack.push(new VariableAgent(variable, indices)); |
| 214 | | break; |
| 215 | | case Instruction.Code.PUSH_MEMBER: |
| 216 | | var memberNum = insn.opts[0]; |
| 217 | | var argc = insn.opts[1]; |
| 218 | | var struct = this.getThismod().toValue(); |
| 219 | | var indices = this.popIndices(argc); |
| 220 | | this.stack.push(new VariableAgent(struct.members[memberNum], indices)); |
| 221 | | break; |
| 222 | | case Instruction.Code.THISMOD: |
| 223 | | this.stack.push(this.getThismod()); |
| 224 | | break; |
| 225 | | case Instruction.Code.NEWMOD: |
| 226 | | var module = insn.opts[0]; |
| 227 | | var argc = insn.opts[1]; |
| 228 | | var args = Utils.aryPopN(this.stack, argc); |
| 229 | | var agent = args[0]; |
| 230 | | this.scanArg(agent, 'a', false); |
| 231 | | if(agent.getType() != VarType.STRUCT) { |
| 232 | | agent.variable.dim(VarType.STRUCT, 1, 0, 0, 0); |
| 233 | | } |
| 234 | | var array = agent.variable.value; |
| 235 | | var offset = array.newmod(module); |
| 236 | | if(module.constructor) { |
| 237 | | args[0] = new VariableAgent(agent.variable, [offset]); |
| 238 | | this.callUserDefFunc(module.constructor, args); |
| 239 | | } else if(args.length > 1) { |
| 240 | | throw new HSPError(ErrorCode.TOO_MANY_PARAMETERS); |
| 241 | | } |
| 242 | | break; |
| 243 | | case Instruction.Code.RETURN: |
| 244 | | var val = insn.opts[0] ? this.stack.pop() : undefined; |
| 245 | | if(this.frameStack.length == 0) { |
| 246 | | throw new HSPError(ErrorCode.RETURN_WITHOUT_GOSUB); |
| 247 | | } |
| 248 | | var frame = this.frameStack.pop(); |
| 249 | | if(frame.userDefFunc && frame.userDefFunc.isCType) { |
| 250 | | if(!val) throw new HSPError(ErrorCode.NORETVAL); |
| 251 | | this.stack.push(val.toValue()); |
| 252 | | } else if(val) { |
| 253 | | switch(val.getType()) { |
| 254 | | case VarType.STR: |
| 255 | | this.refstr.assign(0, val.toStrValue()); |
| 256 | | break; |
| 257 | | case VarType.DOUBLE: |
| 258 | | this.refdval.assign(0, val.toDoubleValue()); |
| 259 | | break; |
| 260 | | case VarType.INT: |
| 261 | | this.stat.assign(0, val.toIntValue()); |
| 262 | | break; |
| 263 | | default: |
| 264 | | throw new HSPError(ErrorCode.TYPE_MISMATCH); |
| 265 | | } |
| 266 | | } |
| 267 | | this.pc = frame.pc - 1; |
| 268 | | var runCallback = function() { |
| 269 | | if(frame.callback) { |
| 270 | | var fn = frame.callback(); |
| 271 | | while(fn) { |
| 272 | | fn = fn(); |
| | 65 | createMainLoop: function createMainLoop(literals, userDefFuncs) { |
| | 66 | function push(line) { |
| | 67 | lines.push(Utils.strTimes('\t', indent) + line); |
| | 68 | } |
| | 69 | function pushJumpSubroutineCode(posExpr) { |
| | 70 | push('if(self.frameStack.length >= 256) {'); |
| | 71 | push(' throw new HSPError(ErrorCode.STACK_OVERFLOW);'); |
| | 72 | push('}'); |
| | 73 | push('self.frameStack.push(new Frame(self.pc + 1, null, null));'); |
| | 74 | push('self.pc = '+posExpr+';'); |
| | 75 | } |
| | 76 | var lines = []; |
| | 77 | var indent = 0; |
| | 78 | var sequence = this.sequence; |
| | 79 | var operateMethodNames = 'add,sub,mul,div,mod,and,or,xor,eq,ne,gt,lt,gteq,lteq,rsh,lsh'.split(','); |
| | 80 | |
| | 81 | push('for(;;) {'); indent ++; |
| | 82 | push('switch(self.pc) {'); |
| | 83 | for(var pc = 0; pc < sequence.length; pc ++) { |
| | 84 | var insn = sequence[pc]; |
| | 85 | push('case '+pc+':'); indent ++; |
| | 86 | switch(insn.code) { |
| | 87 | case Instruction.Code.NOP: |
| | 88 | break; |
| | 89 | case Instruction.Code.PUSH: |
| | 90 | literals.push(insn.opts[0]); |
| | 91 | push('stack.push(literals['+(literals.length - 1)+']);'); |
| | 92 | break; |
| | 93 | case Instruction.Code.PUSH_VAR: |
| | 94 | var varId = insn.opts[0]; |
| | 95 | var argc = insn.opts[1]; |
| | 96 | push('stack.push(new VariableAgent(variables['+varId+'], self.popIndices('+argc+')));'); |
| | 97 | break; |
| | 98 | case Instruction.Code.POP: |
| | 99 | push('stack.pop();'); |
| | 100 | break; |
| | 101 | case Instruction.Code.DUP: |
| | 102 | push('stack.push(stack[stack.length-1]);'); |
| | 103 | break; |
| | 104 | case Instruction.Code.ADD: |
| | 105 | case Instruction.Code.SUB: |
| | 106 | case Instruction.Code.MUL: |
| | 107 | case Instruction.Code.DIV: |
| | 108 | case Instruction.Code.MOD: |
| | 109 | case Instruction.Code.AND: |
| | 110 | case Instruction.Code.OR: |
| | 111 | case Instruction.Code.XOR: |
| | 112 | case Instruction.Code.EQ: |
| | 113 | case Instruction.Code.NE: |
| | 114 | case Instruction.Code.GT: |
| | 115 | case Instruction.Code.LT: |
| | 116 | case Instruction.Code.GTEQ: |
| | 117 | case Instruction.Code.LTEQ: |
| | 118 | case Instruction.Code.RSH: |
| | 119 | case Instruction.Code.LSH: |
| | 120 | push('var rhs = stack.pop();'); |
| | 121 | push('stack[stack.length - 1] = stack[stack.length - 1].'+ |
| | 122 | operateMethodNames[insn.code - Instruction.Code.ADD]+'(rhs);'); |
| | 123 | break; |
| | 124 | case Instruction.Code.GOTO: |
| | 125 | push('self.pc = '+insn.opts[0].pos+';'); |
| | 126 | push('continue;'); |
| | 127 | break; |
| | 128 | case Instruction.Code.IFNE: |
| | 129 | push('if(stack.pop().toIntValue()._value) {'); |
| | 130 | push(' self.pc = '+insn.opts[0].pos+';'); |
| | 131 | push(' continue;'); |
| | 132 | push('}'); |
| | 133 | break; |
| | 134 | case Instruction.Code.IFEQ: |
| | 135 | push('if(!stack.pop().toIntValue()._value) {'); |
| | 136 | push(' self.pc = '+insn.opts[0].pos+';'); |
| | 137 | push(' continue;'); |
| | 138 | push('}'); |
| | 139 | break; |
| | 140 | case Instruction.Code.SETVAR: |
| | 141 | var argc = insn.opts[0]; |
| | 142 | if(argc > 1) { |
| | 143 | push('var args = Utils.aryPopN(stack, '+argc+');'); |
| | 144 | push('var agent = stack.pop();'); |
| | 145 | push('var variable = agent.variable;'); |
| | 146 | push('var indices = agent.indices.slice();'); |
| | 147 | push('if(indices.length == 0) indices[0] = 0;'); |
| | 148 | for(var i = 0; i < argc; i ++) { |
| | 149 | push('variable.assign(indices, args['+i+']);'); |
| | 150 | push('indices[0] ++;'); |
| 274 | | } |
| 275 | | }; |
| 276 | | if(frame.userDefFunc) { |
| 277 | | this.deleteLocalVars(frame.userDefFunc.paramTypes, frame.args, runCallback); |
| 278 | | } else { |
| 279 | | runCallback(); |
| 280 | | } |
| 281 | | break; |
| 282 | | case Instruction.Code.DELMOD: |
| 283 | | var v = this.stack.pop(); |
| 284 | | this.scanArg(v, 'v', false); |
| 285 | | if(v.getType() != VarType.STRUCT) { |
| 286 | | throw new HSPError(ErrorCode.TYPE_MISMATCH); |
| 287 | | } |
| 288 | | this.deleteStruct(v); |
| 289 | | break; |
| 290 | | case Instruction.Code.REPEAT: |
| 291 | | if(this.loopStack.length >= 31) { |
| 292 | | throw new HSPError(ErrorCode.TOO_MANY_NEST); |
| 293 | | } |
| 294 | | var label = insn.opts[0]; |
| 295 | | var argc = insn.opts[1]; |
| 296 | | var begin = 0, end = Infinity; |
| 297 | | if(argc == 2) { |
| 298 | | var begin = this.stack.pop(); |
| 299 | | this.scanArg(begin, 'n', false); |
| 300 | | begin = begin.toIntValue()._value; |
| 301 | | } |
| 302 | | if(argc >= 1) { |
| 303 | | var end = this.stack.pop(); |
| 304 | | this.scanArg(end, 'n', false); |
| 305 | | end = end.toIntValue()._value; |
| 306 | | if(end < 0) end = Infinity; |
| 307 | | } |
| 308 | | if(end == 0) { |
| 309 | | this.pc = label.pos - 1; |
| 310 | | break; |
| 311 | | } |
| 312 | | end += begin; |
| 313 | | this.loopStack.push(new LoopData(begin, end, this.pc + 1)); |
| 314 | | break; |
| 315 | | case Instruction.Code.LOOP: |
| 316 | | if(this.loopStack.length == 0) { |
| 317 | | throw new HSPError(ErrorCode.LOOP_WITHOUT_REPEAT); |
| 318 | | } |
| 319 | | var data = this.loopStack[this.loopStack.length - 1]; |
| 320 | | data.cnt ++; |
| 321 | | if(data.cnt >= data.end) { |
| 322 | | this.loopStack.pop(); |
| 323 | | break; |
| 324 | | } |
| 325 | | this.pc = data.pc - 1; |
| 326 | | break; |
| 327 | | case Instruction.Code.CNT: |
| 328 | | if(this.loopStack.length == 0) { |
| 329 | | this.stack.push(new IntValue(0)); |
| 330 | | break; |
| 331 | | } |
| 332 | | this.stack.push(new IntValue(this.loopStack[this.loopStack.length - 1].cnt)); |
| 333 | | break; |
| 334 | | case Instruction.Code.CONTINUE: |
| 335 | | if(this.loopStack.length == 0) { |
| 336 | | throw new HSPError(ErrorCode.LOOP_WITHOUT_REPEAT); |
| 337 | | } |
| 338 | | var data = this.loopStack[this.loopStack.length - 1]; |
| 339 | | var label = insn.opts[0]; |
| 340 | | var argc = insn.opts[1]; |
| 341 | | var newCnt = data.cnt + 1; |
| 342 | | if(argc) { |
| 343 | | newCnt = this.stack.pop(); |
| 344 | | this.scanArg(newCnt, 'n', false); |
| 345 | | newCnt = newCnt.toIntValue()._value; |
| 346 | | } |
| 347 | | data.cnt = newCnt; |
| 348 | | if(data.cnt >= data.end) { |
| 349 | | this.loopStack.pop(); |
| 350 | | this.pc = label.pos - 1; |
| 351 | | break; |
| 352 | | } |
| 353 | | this.pc = data.pc - 1; |
| 354 | | break; |
| 355 | | case Instruction.Code.BREAK: |
| 356 | | if(this.loopStack.length == 0) { |
| 357 | | throw new HSPError(ErrorCode.LOOP_WITHOUT_REPEAT); |
| 358 | | } |
| 359 | | var label = insn.opts[0]; |
| 360 | | this.loopStack.pop(); |
| 361 | | this.pc = label.pos - 1; |
| 362 | | break; |
| 363 | | case Instruction.Code.FOREACH: |
| 364 | | if(this.loopStack.length >= 31) { |
| 365 | | throw new HSPError(ErrorCode.TOO_MANY_NEST); |
| 366 | | } |
| 367 | | this.loopStack.push(new LoopData(0, Infinity, this.pc + 1)); |
| 368 | | break; |
| 369 | | case Instruction.Code.EACHCHK: |
| 370 | | if(this.loopStack.length == 0) { |
| 371 | | throw new HSPError(ErrorCode.LOOP_WITHOUT_REPEAT); |
| 372 | | } |
| 373 | | var label = insn.opts[0]; |
| 374 | | var v = this.stack.pop(); |
| 375 | | this.scanArg(v, 'v', false); |
| 376 | | var data = this.loopStack[this.loopStack.length - 1]; |
| 377 | | if(data.cnt >= v.variable.getL0()) { |
| 378 | | this.loopStack.pop(); |
| 379 | | this.pc = label.pos - 1; |
| 380 | | break; |
| 381 | | } |
| 382 | | if(v.variable.at([data.cnt]).isUsing() == false) { // label 型 や struct 型の empty を飛ばす |
| 383 | | data.cnt ++; |
| 384 | | if(data.cnt >= data.end) { |
| 385 | | this.loopStack.pop(); |
| 386 | | this.pc = label.pos - 1; |
| 387 | | break; |
| 388 | | } |
| 389 | | this.pc = data.pc - 1; |
| 390 | | } |
| 391 | | break; |
| 392 | | case Instruction.Code.GOSUB: |
| 393 | | this.subroutineJump(insn.opts[0].pos); |
| 394 | | break; |
| 395 | | case Instruction.Code.GOTO_EXPR: |
| 396 | | this.pc = this.scanArg(this.stack.pop(), 'l').toValue().pos - 1; |
| 397 | | break; |
| 398 | | case Instruction.Code.GOSUB_EXPR: |
| 399 | | this.subroutineJump(this.scanArg(this.stack.pop(), 'l').toValue().pos); |
| 400 | | break; |
| 401 | | case Instruction.Code.EXGOTO: |
| 402 | | var pos = this.scanArg(this.stack.pop(), 'l').toValue().pos; |
| 403 | | var b = this.scanArg(this.stack.pop(), 'n').toIntValue()._value; |
| 404 | | var mode = this.scanArg(this.stack.pop(), 'n').toIntValue()._value; |
| 405 | | var a = this.scanArg(this.scanArg(this.stack.pop(), 'v'), 'i').toIntValue()._value; |
| 406 | | if(mode >= 0) { |
| 407 | | if(a >= b) this.pc = pos - 1; |
| 408 | | } else { |
| 409 | | if(a <= b) this.pc = pos - 1; |
| 410 | | } |
| 411 | | break; |
| 412 | | case Instruction.Code.EXGOTO_OPT1: |
| 413 | | var a = this.scanArg(this.variables[insn.opts[0]].at(0), 'i').toIntValue()._value; |
| 414 | | var pos = insn.opts[1].pos; |
| 415 | | var b = this.scanArg(this.stack.pop(), 'n').toIntValue()._value; |
| 416 | | var mode = this.scanArg(this.stack.pop(), 'n').toIntValue()._value; |
| 417 | | if(mode >= 0) { |
| 418 | | if(a >= b) this.pc = pos - 1; |
| 419 | | } else { |
| 420 | | if(a <= b) this.pc = pos - 1; |
| 421 | | } |
| 422 | | break; |
| 423 | | case Instruction.Code.EXGOTO_OPT2: |
| 424 | | var a = this.scanArg(this.variables[insn.opts[0]].at(0), 'i').toIntValue()._value; |
| 425 | | var pos = insn.opts[1].pos; |
| 426 | | var b = this.scanArg(this.stack.pop(), 'n').toIntValue()._value; |
| 427 | | if(a >= b) this.pc = pos - 1; |
| 428 | | break; |
| 429 | | case Instruction.Code.EXGOTO_OPT3: |
| 430 | | var a = this.scanArg(this.variables[insn.opts[0]].at(0), 'i').toIntValue()._value; |
| 431 | | var pos = insn.opts[1].pos; |
| 432 | | var b = this.scanArg(this.stack.pop(), 'n').toIntValue()._value; |
| 433 | | if(a <= b) this.pc = pos - 1; |
| 434 | | break; |
| 435 | | case Instruction.Code.ON: |
| 436 | | var argc = insn.opts[0]; |
| 437 | | var isGosub = insn.opts[1]; |
| 438 | | for(var i = this.stack.length - argc, l = this.stack.length; i < l; i ++) { |
| 439 | | this.scanArg(this.stack[i], 'l'); |
| 440 | | } |
| 441 | | var n = this.scanArg(this.stack[this.stack.length - argc - 1], 'n').toIntValue()._value; |
| 442 | | if(!(0 <= n && n < argc)) break; |
| 443 | | var pos = this.stack[this.stack.length - argc + n].toValue().pos; |
| 444 | | this.stack.length -= argc + 1; |
| 445 | | if(isGosub) { |
| 446 | | this.subroutineJump(pos); |
| 447 | | } else { |
| 448 | | this.pc = pos - 1; |
| 449 | | } |
| 450 | | break; |
| 451 | | default: |
| 452 | | throw new Error("未対応の命令コード: "+insn.code); |
| 453 | | } |
| 454 | | this.pc ++; |
| | 152 | } else { |
| | 153 | push('var arg = stack.pop();'); |
| | 154 | push('var agent = stack.pop();'); |
| | 155 | push('agent.variable.assign(agent.indices, arg);'); |
| | 156 | } |
| | 157 | break; |
| | 158 | case Instruction.Code.EXPANDARRAY: |
| | 159 | push('var agent = stack[stack.length - 1];'); |
| | 160 | push('agent.variable.expand(agent.indices);'); |
| | 161 | break; |
| | 162 | case Instruction.Code.INC: |
| | 163 | push('var agent = stack.pop();'); |
| | 164 | push('agent.variable.expand(agent.indices);'); |
| | 165 | push('agent.assign(agent.add(new IntValue(1)));'); |
| | 166 | break; |
| | 167 | case Instruction.Code.DEC: |
| | 168 | push('var agent = stack.pop();'); |
| | 169 | push('agent.variable.expand(agent.indices);'); |
| | 170 | push('agent.assign(agent.sub(new IntValue(1)));'); |
| | 171 | break; |
| | 172 | case Instruction.Code.CALL_BUILTIN_CMD: |
| | 173 | case Instruction.Code.CALL_BUILTIN_FUNC: |
| | 174 | var type = insn.opts[0]; |
| | 175 | var subid = insn.opts[1]; |
| | 176 | var argc = insn.opts[2]; |
| | 177 | push('var func = BuiltinFuncs['+type+']['+subid+'];'); |
| | 178 | push('if(!func) throw new HSPError(ErrorCode.UNSUPPORTED_FUNCTION);'); |
| | 179 | push('var args = Utils.aryPopN(stack, '+argc+');'); |
| | 180 | if(insn.code == Instruction.Code.CALL_BUILTIN_FUNC) { |
| | 181 | push('stack.push(func.apply(self, args));'); |
| | 182 | } else { |
| | 183 | push('func.apply(self, args);'); |
| | 184 | } |
| | 185 | break; |
| | 186 | case Instruction.Code.CALL_USERDEF_CMD: |
| | 187 | case Instruction.Code.CALL_USERDEF_FUNC: |
| | 188 | var userDefFunc = insn.opts[0]; |
| | 189 | var argc = insn.opts[1]; |
| | 190 | if(!userDefFuncs[userDefFunc.id]) { |
| | 191 | userDefFuncs[userDefFunc.id] = userDefFunc; |
| | 192 | } |
| | 193 | push('self.callUserDefFunc(userDefFuncs['+userDefFunc.id+'], '+ |
| | 194 | 'Utils.aryPopN(stack, '+argc+'));'); |
| | 195 | push('self.pc ++;'); |
| | 196 | push('continue;'); |
| | 197 | break; |
| | 198 | case Instruction.Code.GETARG: |
| | 199 | var argNum = insn.opts[0]; |
| | 200 | push('stack.push(self.getArg('+argNum+'));'); |
| | 201 | break; |
| | 202 | case Instruction.Code.PUSH_ARG_VAR: |
| | 203 | var argNum = insn.opts[0]; |
| | 204 | var argc = insn.opts[1]; |
| | 205 | push('var variable = self.getArg('+argNum+');'); |
| | 206 | push('var indices = self.popIndices('+argc+');'); |
| | 207 | push('stack.push(new VariableAgent(variable, indices));'); |
| | 208 | break; |
| | 209 | case Instruction.Code.PUSH_MEMBER: |
| | 210 | var memberNum = insn.opts[0]; |
| | 211 | var argc = insn.opts[1]; |
| | 212 | push('var struct = self.getThismod().toValue();'); |
| | 213 | push('var indices = self.popIndices('+argc+');'); |
| | 214 | push('stack.push(new VariableAgent(struct.members['+memberNum+'], indices));'); |
| | 215 | break; |
| | 216 | case Instruction.Code.THISMOD: |
| | 217 | push('stack.push(self.getThismod());'); |
| | 218 | break; |
| | 219 | case Instruction.Code.NEWMOD: |
| | 220 | var module = insn.opts[0]; |
| | 221 | var argc = insn.opts[1]; |
| | 222 | if(!userDefFuncs[module.id]) { |
| | 223 | userDefFuncs[module.id] = module; |
| | 224 | } |
| | 225 | var moduleExpr = 'userDefFuncs['+module.id+']'; |
| | 226 | push('var args = Utils.aryPopN(stack, '+argc+');'); |
| | 227 | push('var agent = self.scanArg(args[0], "a");'); |
| | 228 | push('if(agent.getType() != '+VarType.STRUCT+') {'); |
| | 229 | push(' agent.variable.dim('+VarType.STRUCT+', 1, 0, 0, 0);'); |
| | 230 | push('}'); |
| | 231 | push('var array = agent.variable.value;'); |
| | 232 | push('var offset = array.newmod('+moduleExpr+');'); |
| | 233 | if(module.constructor) { |
| | 234 | push('args[0] = new VariableAgent(agent.variable, [offset]);'); |
| | 235 | if(!userDefFuncs[module.constructor.id]) { |
| | 236 | userDefFuncs[module.constructor.id] = module.constructor; |
| | 237 | } |
| | 238 | push('self.callUserDefFunc(userDefFuncs['+module.constructor.id+'], args);'); |
| | 239 | push('self.pc ++;'); |
| | 240 | push('continue;'); |
| | 241 | } else if(argc > 1) { |
| | 242 | push('throw new HSPError(ErrorCode.TOO_MANY_PARAMETERS);') |
| | 243 | } |
| | 244 | break; |
| | 245 | case Instruction.Code.RETURN: |
| | 246 | if(insn.opts[0]) { |
| | 247 | push('self.return_(stack.pop());'); |
| | 248 | } else { |
| | 249 | push('self.return_();'); |
| | 250 | } |
| | 251 | push('self.pc ++;'); |
| | 252 | push('continue;'); |
| | 253 | break; |
| | 254 | case Instruction.Code.DELMOD: |
| | 255 | push('var v = self.scanArg(stack.pop(), "v");'); |
| | 256 | push('if(v.getType() != VarType.STRUCT) {'); |
| | 257 | push(' throw new HSPError(ErrorCode.TYPE_MISMATCH);'); |
| | 258 | push('}'); |
| | 259 | push('self.deleteStruct(v);'); |
| | 260 | push('self.pc ++;'); |
| | 261 | push('continue;'); |
| | 262 | break; |
| | 263 | case Instruction.Code.REPEAT: |
| | 264 | var pos = insn.opts[0].pos; |
| | 265 | var argc = insn.opts[1]; |
| | 266 | push('if(self.loopStack.length >= 31) {'); |
| | 267 | push(' throw new HSPError(ErrorCode.TOO_MANY_NEST);'); |
| | 268 | push('}'); |
| | 269 | if(argc == 2) { |
| | 270 | push('var begin = self.scanArg(stack.pop(), "n").toIntValue()._value;'); |
| | 271 | } else { |
| | 272 | push('var begin = 0;'); |
| | 273 | } |
| | 274 | if(argc >= 1) { |
| | 275 | push('var end = self.scanArg(stack.pop(), "n").toIntValue()._value;'); |
| | 276 | push('if(end < 0) end = Infinity;'); |
| | 277 | } else { |
| | 278 | push('var end = Infinity;'); |
| | 279 | } |
| | 280 | push('if(end == 0) {'); |
| | 281 | push(' self.pc = '+pos+';'); |
| | 282 | push(' continue;'); |
| | 283 | push('}'); |
| | 284 | push('end += begin;'); |
| | 285 | push('self.loopStack.push(new LoopData(begin, end, '+(pc + 1)+'));'); |
| | 286 | break; |
| | 287 | case Instruction.Code.LOOP: |
| | 288 | push('if(self.loopStack.length == 0) {'); |
| | 289 | push(' throw new HSPError(ErrorCode.LOOP_WITHOUT_REPEAT);'); |
| | 290 | push('}'); |
| | 291 | push('var data = self.loopStack[self.loopStack.length - 1];'); |
| | 292 | push('data.cnt ++;'); |
| | 293 | push('if(data.cnt < data.end) {'); |
| | 294 | push(' self.pc = data.pc;'); |
| | 295 | push(' continue;'); |
| | 296 | push('}'); |
| | 297 | push('self.loopStack.pop();'); |
| | 298 | break; |
| | 299 | case Instruction.Code.CNT: |
| | 300 | push('if(self.loopStack.length == 0) {'); |
| | 301 | push(' stack.push(new IntValue(0));'); |
| | 302 | push('} else {'); |
| | 303 | push(' stack.push(new IntValue(self.loopStack[self.loopStack.length - 1].cnt));'); |
| | 304 | push('}'); |
| | 305 | break; |
| | 306 | case Instruction.Code.CONTINUE: |
| | 307 | var pos = insn.opts[0].pos; |
| | 308 | var argc = insn.opts[1]; |
| | 309 | push('if(self.loopStack.length == 0) {'); |
| | 310 | push(' throw new HSPError(ErrorCode.LOOP_WITHOUT_REPEAT);'); |
| | 311 | push('}'); |
| | 312 | push('var data = self.loopStack[self.loopStack.length - 1];'); |
| | 313 | var newCntExpr; |
| | 314 | if(argc) { |
| | 315 | newCntExpr = '(data.cnt = self.scanArg(self.stack.pop(), "n").toIntValue()._value)'; |
| | 316 | } else { |
| | 317 | newCntExpr = '++data.cnt'; |
| | 318 | } |
| | 319 | push('if('+newCntExpr+' >= data.end) {'); |
| | 320 | push(' self.loopStack.pop();'); |
| | 321 | push(' self.pc = '+pos+';'); |
| | 322 | push('} else {'); |
| | 323 | push(' self.pc = data.pc;'); |
| | 324 | push('}'); |
| | 325 | push('continue;'); |
| | 326 | break; |
| | 327 | case Instruction.Code.BREAK: |
| | 328 | var label = insn.opts[0]; |
| | 329 | push('if(self.loopStack.length == 0) {'); |
| | 330 | push(' throw new HSPError(ErrorCode.LOOP_WITHOUT_REPEAT);'); |
| | 331 | push('}'); |
| | 332 | push('self.loopStack.pop();'); |
| | 333 | push('self.pc = '+label.pos+';'); |
| | 334 | push('continue;'); |
| | 335 | break; |
| | 336 | case Instruction.Code.FOREACH: |
| | 337 | push('if(self.loopStack.length >= 31) {'); |
| | 338 | push(' throw new HSPError(ErrorCode.TOO_MANY_NEST);'); |
| | 339 | push('}'); |
| | 340 | push('self.loopStack.push(new LoopData(0, Infinity, '+(pc + 1)+'));'); |
| | 341 | break; |
| | 342 | case Instruction.Code.EACHCHK: |
| | 343 | push('if(self.loopStack.length == 0) {'); |
| | 344 | push(' throw new HSPError(ErrorCode.LOOP_WITHOUT_REPEAT);') |
| | 345 | push('}') |
| | 346 | var pos = insn.opts[0].pos; |
| | 347 | push('var v = self.scanArg(stack.pop(), "v");'); |
| | 348 | push('var data = self.loopStack[self.loopStack.length - 1];'); |
| | 349 | push('if(data.cnt >= v.variable.getL0()) {') |
| | 350 | push(' self.loopStack.pop();'); |
| | 351 | push(' self.pc = '+pos+';'); |
| | 352 | push(' continue;'); |
| | 353 | push('}'); |
| | 354 | push('if(v.variable.at([data.cnt]).isUsing() == false) {'); // label 型 や struct 型の empty を飛ばす |
| | 355 | push(' data.cnt ++;'); |
| | 356 | push(' if(data.cnt >= data.end) {'); |
| | 357 | push(' self.loopStack.pop();'); |
| | 358 | push(' self.pc = '+pos+';'); |
| | 359 | push(' } else {'); |
| | 360 | push(' self.pc = data.pc - 1;'); |
| | 361 | push(' }'); |
| | 362 | push(' continue;'); |
| | 363 | push('}'); |
| | 364 | break; |
| | 365 | case Instruction.Code.GOSUB: |
| | 366 | pushJumpSubroutineCode(insn.opts[0].pos); |
| | 367 | push('continue;'); |
| | 368 | break; |
| | 369 | case Instruction.Code.GOTO_EXPR: |
| | 370 | push('self.pc = self.scanArg(stack.pop(), "l").toValue().pos;'); |
| | 371 | push('continue;'); |
| | 372 | break; |
| | 373 | case Instruction.Code.GOSUB_EXPR: |
| | 374 | pushJumpSubroutineCode('self.scanArg(stack.pop(), "l").toValue().pos'); |
| | 375 | push('continue;'); |
| | 376 | break; |
| | 377 | case Instruction.Code.EXGOTO: |
| | 378 | push('var pos = self.scanArg(stack.pop(), "l").toValue().pos;'); |
| | 379 | push('var b = self.scanArg(stack.pop(), "n").toIntValue()._value;'); |
| | 380 | push('var mode = self.scanArg(stack.pop(), "n").toIntValue()._value;'); |
| | 381 | push('var a = self.scanArg(self.scanArg(stack.pop(), "v"), "i").toIntValue()._value;'); |
| | 382 | push('if(mode >= 0) {'); |
| | 383 | push(' if(a >= b) { self.pc = pos; continue; }'); |
| | 384 | push('} else {'); |
| | 385 | push(' if(a <= b) { self.pc = pos; continue; }'); |
| | 386 | push('}'); |
| | 387 | break; |
| | 388 | case Instruction.Code.EXGOTO_OPT1: |
| | 389 | var pos = insn.opts[1].pos; |
| | 390 | push('var a = self.scanArg(variables['+insn.opts[0]+'].at(0), "i").toIntValue()._value;'); |
| | 391 | push('var b = self.scanArg(stack.pop(), "n").toIntValue()._value;'); |
| | 392 | push('var mode = self.scanArg(stack.pop(), "n").toIntValue()._value;'); |
| | 393 | push('if(mode >= 0) {'); |
| | 394 | push(' if(a >= b) { self.pc = '+pos+'; continue; }'); |
| | 395 | push('} else {'); |
| | 396 | push(' if(a <= b) { self.pc = '+pos+'; continue; }'); |
| | 397 | push('}'); |
| | 398 | break; |
| | 399 | case Instruction.Code.EXGOTO_OPT2: |
| | 400 | var pos = insn.opts[1].pos; |
| | 401 | push('var a = self.scanArg(variables['+insn.opts[0]+'].at(0), "i").toIntValue()._value;'); |
| | 402 | push('var b = self.scanArg(stack.pop(), "n").toIntValue()._value;'); |
| | 403 | push('if(a >= b) { self.pc = '+pos+'; continue; }'); |
| | 404 | break; |
| | 405 | case Instruction.Code.EXGOTO_OPT3: |
| | 406 | var pos = insn.opts[1].pos; |
| | 407 | push('var a = self.scanArg(variables['+insn.opts[0]+'].at(0), "i").toIntValue()._value;'); |
| | 408 | push('var b = self.scanArg(stack.pop(), "n").toIntValue()._value;'); |
| | 409 | push('if(a <= b) { self.pc = '+pos+'; continue; }'); |
| | 410 | break; |
| | 411 | case Instruction.Code.ON: |
| | 412 | var argc = insn.opts[0]; |
| | 413 | var isGosub = insn.opts[1]; |
| | 414 | push('var len = stack.length;'); |
| | 415 | for(var i = 0; i < argc; i ++) { |
| | 416 | push('self.scanArg(stack[len - '+(argc - i)+'], "l");'); |
| | 417 | } |
| | 418 | push('var n = self.scanArg(stack[len - '+(argc + 1)+'], "n").toIntValue()._value;'); |
| | 419 | push('if(!(0 <= n && n < argc)) break;'); |
| | 420 | push('var pos = stack[len - '+argc+' + n].toValue().pos;'); |
| | 421 | push('stack.length -= '+(argc + 1)+';'); |
| | 422 | if(isGosub) { |
| | 423 | pushJumpSubroutineCode('pos'); |
| | 424 | push('continue;'); |
| | 425 | } else { |
| | 426 | push('self.pc = pos;'); |
| | 427 | push('continue;'); |
| | 428 | } |
| | 429 | break; |
| | 430 | default: |
| | 431 | throw new Error("未対応の命令コード: "+insn.code); |
| | 432 | } |
| | 433 | push('self.pc ++;'); |
| | 434 | indent --; |
| | 435 | } |
| | 436 | push('}'); |
| | 437 | indent --; push('}'); |
| | 438 | //print(lines.join("\n")); |
| | 439 | return lines.join("\n"); |