1 // ELCHNOSCompiler for AI004
2 // AI004(WebCPU)がなければ動作しない。
9 function ELCHNOSCompiler_CompileException(errno, infoArray, lineCount){
11 this.infoArray = infoArray;
12 this.lineCount = lineCount;
14 ELCHNOSCompiler_CompileException.prototype = {
17 "Incompatible value attribute.",
18 "Unexpected identifier.",
19 "Unknown assembly language type.",
20 "Invalid expression of OSECPU Binary.",
21 "Binary translation error.",
23 getMessageString: function(){
25 if(!isNaN(this.lineCount)){
26 s += this.lineCount + ":";
28 s += "Error" + this.errno.toString().toUpperCase();
29 if(this.errno < 0 || this.errorMessageList.length <= this.errno){
32 s += ":" + this.errorMessageList[this.errno] + "\n";
35 s += " >" + this.infoArray.join("\n > ") + "\n";
40 // throw new ELCHNOSCompiler_CompileException(5, [""], this.lineCount);
43 function ELCHNOSCompiler(printFunc, downloadBoxDOMObject){
44 //printFunc省略時はconsole.logに出力。
46 this.debug = printFunc;
48 this.debug = function(s){ console.log(s); };
50 this.downloadBoxDOMObject = downloadBoxDOMObject;
53 //0はエントリポイント(main)用に予約
55 this.integerRegisterAllocationTable = new Array();
56 this.pointerRegisterAllocationTable = new Array();
58 ELCHNOSCompiler.prototype = {
93 "Incompatible value attribute.",
94 "Unexpected identifier.",
95 "Unknown assembly language type.",
96 "Invalid expression of OSECPU Binary.",
97 "Binary translation error.",
100 Flag_Sign_Signed : 0x00000001,
101 Flag_Sign_Unsigned : 0x00000002,
104 this.separated = null;
105 this.bin = new Array();
107 this.structure = new Array();
108 this.currentStructure = this.structure;
109 this.structureStack = new Array();
111 //0はエントリポイント(main)用に予約
112 this.nextLabelID = 1;
113 this.integerRegisterAllocationTable = new Array();
114 this.pointerRegisterAllocationTable = new Array();
116 compile: function(str){
120 this.line = str.split("\n");
122 this.separated = str.splitByArraySeparatorSeparatedLong(this.keyWordList);
124 this.compile_removeComment();
127 this.separated.removeAllObject("\t");
128 this.separated.removeAllObject(" ");
131 var currentExpression = null;
132 var numValSignFlag = 0;
134 var pointerCount = 0;
142 //10: 変数・定数の前置属性・型・または識別子
143 //11: 変数・定数の識別子またはポインタ属性
144 //12: 変数・定数の後置属性・初期化式または終端記号、もしくは連続した変数宣言の区切り
145 //13: 変数・定数の初期化式または終端記号、もしくは連続した変数宣言の区切り
149 //17: 初期化式の値部分または終了括弧
150 //18: 初期化式の区切りまたは終了括弧
161 //70: OSECPUアセンブリ直接記述モード
162 //71: OSECPUアセンブリ評価式の内容または終端記号
164 for(var i = 0, iLen = this.separated.length; i < iLen; i++){
165 var s = this.separated[i].toLowerCase();
172 } else if(s == "signed" && (mode == 0 || mode == 10)){
174 if(numValSignFlag != 0){
175 throw new ELCHNOSCompiler_CompileException(1, [s], lineCount);
177 numValSignFlag |= this.Flag_Sign_Signed;
179 } else if(s == "unsigned" && (mode == 0 || mode == 10)){
181 if(numValSignFlag != 0){
182 throw new ELCHNOSCompiler_CompileException(1, [s], lineCount);
184 numValSignFlag |= this.Flag_Sign_Unsigned;
186 } else if(s == "char" && (mode == 0 || mode == 10 || mode == 11 || mode == 52)){
189 if(mode == 0 || mode == 10 || mode == 11){
192 } else if(s == "int" && (mode == 0 || mode == 10 || mode == 11 || mode == 52)){
195 if(mode == 0 || mode == 10 || mode == 11){
198 } else if(s == ";" && (mode == 0 || mode == 12 || mode == 13 || mode == 19 || mode == 60 || mode == 71)){
199 if(mode == 12 || mode == 13 || mode == 19){
203 currentExpression = null;
205 } else if(mode == 60 || mode == 71){
207 currentExpression = null;
210 } else if(mode == 71){
214 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
216 } else if(s == "=" && (mode == 13)){
221 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
223 } else if(s == "*" && (mode == 11)){
226 } else if(s == "," && (mode == 18 || mode == 12 || mode == 13 || mode == 53)){
230 } else if(mode == 12 || mode == 13 || mode == 53){
232 if(mode == 12 || mode == 13){
234 } else if(mode == 53){
237 currentExpression = null;
239 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
242 } else if(s == "[" && (mode == 12)){
245 } else if(s == "]" && (mode == 15)){
248 } else if(s == "{" && (mode == 16 || mode == 54)){
252 } else if(mode == 54){
254 currentExpression = null;
257 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
259 } else if(s == "}" && (mode == 17 || mode == 18 || mode == 0)){
260 if(mode == 17 || mode == 18){
263 } else if(mode == 0){
264 if(this.structureStack.length > 0){
265 this.restoreCurrentStructure();
267 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
270 } else if(s == "(" && (mode == 51)){
273 } else if(s == ")" && (mode == 52 || mode == 53)){
276 currentExpression = null;
277 for(var j = 0, jLen = this.currentStructure.length; j < jLen; j++){
279 this.currentStructure[j].argumentIndex = j;
282 } else if(s == "@asm" && (mode == 0)){
284 s = this.separated[i].toLowerCase();
288 throw new ELCHNOSCompiler_CompileException(3, [s], lineCount);
290 } else if(s == "@end" && (mode == 70)){
293 } else if(s == "procedure" && (mode == 0)){
295 var f = new ELCHNOSCompiler_ExpressionStructure_Function(this, lineCount);
296 this.currentStructure.push(f);
297 this.changeCurrentStructure(f.structure);
298 currentExpression = f;
301 } else if(s == "inline" && (mode == 50)){
302 currentExpression.isInline = true;
303 } else if(s == "for" && (mode == 0)){
306 s = this.separated[i];
308 var f = new ELCHNOSCompiler_ExpressionStructure_Loop_for(this, lineCount);
309 this.currentStructure.push(f);
310 this.changeCurrentStructure(f.structure);
311 currentExpression = f;
313 f.initializer = new ELCHNOSCompiler_ExpressionStructure_Expression(this, lineCount);
314 i = f.initializer.readExpressionToTerminator(i, ";");
316 f.conditonalExpression = new ELCHNOSCompiler_ExpressionStructure_Expression(this, lineCount);
317 i = f.conditonalExpression.readExpressionToTerminator(i, ";");
319 f.incrementalExpression = new ELCHNOSCompiler_ExpressionStructure_Expression(this, lineCount);
320 i = f.incrementalExpression.readExpressionToTerminator(i, ")");
323 if(this.separated[i] != "{"){
324 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
326 currentExpression = null;
328 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
330 } else if(s == "if" && (mode == 0)){
337 s = this.separated[i];
339 var f = new ELCHNOSCompiler_ExpressionStructure_if(this, lineCount);
340 this.currentStructure.push(f);
341 this.changeCurrentStructure(f.structure);
342 currentExpression = f;
344 f.conditonalExpression = new ELCHNOSCompiler_ExpressionStructure_Expression(this, lineCount);
345 i = f.conditonalExpression.readExpressionToTerminator(i, ")");
348 if(this.separated[i] != "{"){
349 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
351 currentExpression = null;
353 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
356 } else if(s == "remark" && (mode == 70)){
358 var b = new ELCHNOSCompiler_ExpressionStructure_OSECPUBinary(this, lineCount);
359 this.currentStructure.push(b);
364 s = this.separated[i];
366 var len = parseInt(s, 16);
369 throw new ELCHNOSCompiler_CompileException(4, [s], lineCount);
373 s = this.separated[i];
374 if(s.length == len * 2){
375 for(var j = 0; j < len; j++){
376 b.bin.push(parseInt(s.substr(j * 2, 2), 16));
379 throw new ELCHNOSCompiler_CompileException(4, [s], lineCount);
383 if(this.separated[i] != ";"){
384 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
386 //この命令は定数のみで構成されているのでコンパイル済みとマークする
390 } else if(s == "call" && (mode == 70)){
392 var b = new ELCHNOSCompiler_ExpressionStructure_OSECPUBinary(this, lineCount);
393 this.currentStructure.push(b);
397 s = this.separated[i].toLowerCase();
398 if((s.indexOf("p") == 0) && s.length == 3){
401 var labelID = this.allocateLabelID();
402 //PLIMM(P30, labelID)
406 b.appendInstruction_UINT32BE(labelID);
411 b.bin.push(parseInt(s.substr(1),16));
413 b.appendInstruction_LB(0x01, labelID);
420 if(this.separated[i] != ";"){
421 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
423 //この命令は定数のみで構成されているのでコンパイル済みとマークする
428 throw new ELCHNOSCompiler_CompileException(0, [s], lineCount);
431 } else if(mode == 11 || mode == 52){
434 var v = new ELCHNOSCompiler_ExpressionStructure_Variable(this, lineCount);
436 v.isSigned = (numValSignFlag == 0) ? false : (0 != (numValSignFlag & this.Flag_Sign_Signed));
437 s = this.separated[i];
438 v.isPointer = (pointerCount != 0) ? true : false;
441 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
444 this.currentStructure.push(v);
445 currentExpression = v;
449 } else if(mode == 14){
450 currentExpression.length = parseInt(s);
452 } else if(mode == 17){
454 currentExpression.initValue.push(parseInt(s));
456 } else if(mode == 50){
459 currentExpression.identifier = this.separated[i];
462 if(mode == 0 || mode == 70 || mode == 60 || mode == 71){
464 s = this.separated[i];
465 if(mode == 0 || mode == 70){
467 var f = new ELCHNOSCompiler_ExpressionStructure_Expression(this, lineCount);
468 this.currentStructure.push(f);
469 currentExpression = f;
471 currentExpression.pushIdentifier(s);
474 } else if(mode == 70){
482 //0x00-0xff: そのまま使えるバイナリ
483 //0x10000000-0x1fffffff: 未決定の仮想整数レジスタ番号
484 //0x20000000-0x2fffffff: 未決定の仮想ポインタレジスタ番号
487 //OSECPUフロントエンドコードヘッダ
488 this.bin.push(0x05, 0xe1, 0x00);
490 for(var i = 0, iLen = this.structure.length; i < iLen; i++){
491 var b = this.structure[i].createBinary();
496 this.expandBinaryString();
497 this.assignRegister();
498 this.bin.logAsHexByte(this.debug);
503 var cpu = new WebCPU();
504 cpu.loadBinaryText(this.bin.stringAsHexByte());
505 cpu.staticOptimize();
506 this.debug(cpu.createBackendBinaryString());
511 if(e instanceof ELCHNOSCompiler_CompileException){
512 this.debug(e.getMessageString());
520 compile_removeComment: function(){
522 var commentLineStartIndex = -1;
523 var commentBlockStartIndex = -1;
524 var commentBlockCount = 0;
525 var linesInCommentBlock = 0;
527 for(var i = 0, iLen = this.separated.length; i < iLen; i++){
528 var s = this.separated[i];
529 if(commentLineStartIndex == -1){
532 commentLineStartIndex = i;
537 var len = i - commentLineStartIndex;
538 this.separated.splice(commentLineStartIndex, len);
541 commentLineStartIndex = -1;
546 if(commentBlockCount == 0){
547 commentBlockStartIndex = i;
550 } else if(s == "*/"){
553 if(commentBlockCount == 0){
554 var len = i - commentBlockStartIndex + 1;
555 var padding = new Array();
556 padding.push(commentBlockStartIndex);
558 for(var j = 0, jLen = linesInCommentBlock; j < jLen; j++){
561 this.separated.splice.apply(this.separated, padding);
562 i -= len - linesInCommentBlock;
563 iLen -= len - linesInCommentBlock;
564 linesInCommentBlock = 0;
565 } else if(commentBlockCount < 0){
566 this.debug("Too many block comment closure [].\n");
569 } else if(commentBlockCount > 0 && s == "\n"){
570 linesInCommentBlock++;
574 raiseError: function(errno, lineCount, infoArray){
575 if(errno < 0 || this.errorMessageList.length <= errno){
576 this.debug(lineCount + ":Error" + errno.toString().toUpperCase() + ":Unknown\n");
578 this.debug(lineCount + ":Error" + errno.toString().toUpperCase() + ":" + this.errorMessageList[errno] + "\n");
581 this.debug(" >" + infoArray.toString() + "\n");
584 changeCurrentStructure: function(newstructure){
585 this.structureStack.push(this.currentStructure);
586 this.currentStructure = newstructure;
588 restoreCurrentStructure: function(){
589 this.currentStructure = this.structureStack.pop();
591 searchIdentifier: function(identifier){
593 var cf = function(aryobj, obj){ return (aryobj.identifier == obj) };
595 o = this.currentStructure.isIncluded(identifier, cf);
597 for(var i = this.structureStack.length - 1; i >= 0; i--){
598 o = this.structureStack[i].isIncluded(identifier, cf);
606 isOperator: function(s){
622 allocateLabelID: function(){
624 return this.nextLabelID - 1;
626 virtualIntegerRegisterOffset: 0x10000000,
627 allocateIntegerRegister: function(owner){
628 //レジスタテーブルは先頭から番号の小さい順につめて格納する。
630 //0x10000000-0x1fffffff: 未決定の仮想整数レジスタ番号
632 for(var i = 0, iLen = this.integerRegisterAllocationTable.length; i < iLen; i++){
633 if(this.integerRegisterAllocationTable[i][0] != i + this.virtualIntegerRegisterOffset){
636 this.integerRegisterAllocationTable.splice(i, 0, [i + this.virtualIntegerRegisterOffset, owner]);
641 //途中に空いているレジスタはなかったので追加
642 this.integerRegisterAllocationTable.push([i + this.virtualIntegerRegisterOffset, owner]);
644 return i + this.virtualIntegerRegisterOffset;
646 freeIntegerRegister: function(regID, owner){
647 for(var i = 0, iLen = this.integerRegisterAllocationTable.length; i < iLen; i++){
648 if(this.integerRegisterAllocationTable[i][0] == regID){
651 if(this.integerRegisterAllocationTable[i][1] == owner){
653 this.integerRegisterAllocationTable.splice(i, 1);
659 throw new ELCHNOSCompiler_CompileException(0, ["freeIntegerRegister:regID not found."], -1);
662 virtualPointerRegisterOffset: 0x20000000,
663 allocatePointerRegister: function(owner){
664 //レジスタテーブルは先頭から番号の小さい順につめて格納する。
666 //0x20000000-0x2fffffff: 未決定の仮想ポインタレジスタ番号
668 for(var i = 0, iLen = this.pointerRegisterAllocationTable.length; i < iLen; i++){
669 if(this.pointerRegisterAllocationTable[i][0] != i + this.virtualPointerRegisterOffset){
671 this.pointerRegisterAllocationTable.splice(i, 0, [i + this.virtualPointerRegisterOffset, owner]);
675 //途中に空いているレジスタはなかったので追加
676 this.pointerRegisterAllocationTable.push([i + this.virtualPointerRegisterOffset, owner]);
678 return i + this.virtualPointerRegisterOffset;
680 freePointerRegister: function(regID, owner){
681 for(var i = 0, iLen = this.pointerRegisterAllocationTable.length; i < iLen; i++){
682 if(this.pointerRegisterAllocationTable[i][0] == regID){
685 if(this.pointerRegisterAllocationTable[i][1] == owner){
687 this.pointerRegisterAllocationTable.splice(i, 1);
693 throw new ELCHNOSCompiler_CompileException(0, ["freePointerRegister:regID not found."], -1);
696 freeAllRegisterOfOwner: function(owner){
697 //ownerが確保しているレジスタをすべて解放する
699 for(var i = 0, iLen = this.integerRegisterAllocationTable.length; i < iLen; i++){
700 if(this.integerRegisterAllocationTable[i][1] == owner){
702 this.integerRegisterAllocationTable.splice(i, 1);
707 for(var i = 0, iLen = this.pointerRegisterAllocationTable.length; i < iLen; i++){
708 if(this.pointerRegisterAllocationTable[i][1] == owner){
710 this.pointerRegisterAllocationTable.splice(i, 1);
715 expandBinaryString: function(){
718 var aryStack = new Array();
719 var indexStack = new Array();
720 for(var i = 0; ; i++){
721 if(ary[i] === undefined){
722 ary = aryStack.pop();
723 if(ary === undefined){
726 i = indexStack.pop();
728 if(ary[i] instanceof Array){
730 indexStack.push(i + 1);
733 } else if(ary[i] !== undefined){
739 assignRegister: function(){
740 for(var i = 0, iLen = this.bin.length; i < iLen; i++){
741 if(this.bin[i] > 0xff){
742 if(this.virtualIntegerRegisterOffset <= this.bin[i] && this.bin[i] <= this.virtualIntegerRegisterOffset + 0x1f){
743 //R00-R1fは無条件で割り当ててOK
744 this.bin[i] -= this.virtualIntegerRegisterOffset;
745 } else if(this.virtualPointerRegisterOffset <= this.bin[i] && this.bin[i] <= this.virtualPointerRegisterOffset + 0x1f - 1){
746 //P01-P1fは無条件で割り当ててOK
747 this.bin[i] -= this.virtualPointerRegisterOffset;
750 throw new ELCHNOSCompiler_CompileException(0, ["Sorry, but no more register."]);
755 saveBinary: function(){
757 var v = new Uint8Array(this.bin);
758 var d = new Blob([v]);
762 url = window.URL.createObjectURL(d);
763 } else if(window.webkitURL){
764 url = window.webkitURL.createObjectURL(d);
766 window.alert("Can't create URL");
769 if(this.downloadBoxDOMObject){
770 this.downloadBoxDOMObject.innerHTML = "<a href='" + url + "' target='_blank'>ダウンロード</a>";
772 this.debug("BinarySaved: " + url + "\n");