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 = {
94 "Incompatible attribute.",
95 "Unexpected identifier.",
96 "Unknown assembly language type.",
97 "Invalid expression of OSECPU Binary.",
98 "Binary translation error.",
101 Flag_Sign_Signed : 0x00000001,
102 Flag_Sign_Unsigned : 0x00000002,
105 this.separated = null;
106 this.bin = new Array();
108 this.structure = new Array();
109 this.currentStructure = this.structure;
110 this.structureStack = new Array();
112 //0はエントリポイント(main)用に予約
113 this.nextLabelID = 1;
114 this.integerRegisterAllocationTable = new Array();
115 this.pointerRegisterAllocationTable = new Array();
117 compile: function(str){
118 //戻り値はコンパイルされたバイナリのHex文字列。nullの場合コンパイル失敗。
121 this.line = str.split("\n");
123 this.separated = str.splitByArraySeparatorSeparatedLong(this.keyWordList);
125 this.compile_removeComment();
128 this.separated.removeAllObject("\t");
129 this.separated.removeAllObject(" ");
132 var currentExpression = null;
133 var numValSignFlag = 0;
135 var pointerCount = 0;
143 //10: 変数・定数の前置属性・型・または識別子
144 //11: 変数・定数の識別子またはポインタ属性
145 //12: 変数・定数の後置属性・初期化式または終端記号、もしくは連続した変数宣言の区切り
146 //13: 変数・定数の初期化式または終端記号、もしくは連続した変数宣言の区切り
150 //17: 初期化式の値部分または終了括弧
151 //18: 初期化式の区切りまたは終了括弧
162 //70: OSECPUアセンブリ直接記述モード
163 //71: OSECPUアセンブリ評価式の内容または終端記号
165 for(var i = 0, iLen = this.separated.length; i < iLen; i++){
166 var s = this.separated[i].toLowerCase();
173 } else if(s == "signed" && (mode == 0 || mode == 10)){
175 if(numValSignFlag != 0){
176 throw new ELCHNOSCompiler_CompileException(1, [s], lineCount);
178 numValSignFlag |= this.Flag_Sign_Signed;
180 } else if(s == "unsigned" && (mode == 0 || mode == 10)){
182 if(numValSignFlag != 0){
183 throw new ELCHNOSCompiler_CompileException(1, [s], lineCount);
185 numValSignFlag |= this.Flag_Sign_Unsigned;
187 } else if(s == "char" && (mode == 0 || mode == 10 || mode == 11 || mode == 52)){
190 if(mode == 0 || mode == 10 || mode == 11){
193 } else if(s == "int" && (mode == 0 || mode == 10 || mode == 11 || mode == 52)){
196 if(mode == 0 || mode == 10 || mode == 11){
199 } else if(s == ";" && (mode == 0 || mode == 12 || mode == 13 || mode == 19 || mode == 60 || mode == 71)){
200 if(mode == 12 || mode == 13 || mode == 19){
204 currentExpression = null;
206 } else if(mode == 60 || mode == 71){
208 currentExpression = null;
211 } else if(mode == 71){
215 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
217 } else if(s == "=" && (mode == 13)){
222 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
224 } else if(s == "*" && (mode == 11)){
227 } else if(s == "," && (mode == 18 || mode == 12 || mode == 13 || mode == 53)){
231 } else if(mode == 12 || mode == 13 || mode == 53){
233 if(mode == 12 || mode == 13){
235 } else if(mode == 53){
238 currentExpression = null;
240 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
243 } else if(s == "[" && (mode == 12)){
246 } else if(s == "]" && (mode == 15)){
249 } else if(s == "{" && (mode == 16 || mode == 54)){
253 } else if(mode == 54){
255 currentExpression = null;
258 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
260 } else if(s == "}" && (mode == 17 || mode == 18 || mode == 0)){
261 if(mode == 17 || mode == 18){
264 } else if(mode == 0){
265 if(this.structureStack.length > 0){
266 this.restoreCurrentStructure();
268 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
271 } else if(s == "(" && (mode == 51)){
274 } else if(s == ")" && (mode == 52 || mode == 53)){
277 currentExpression = null;
278 for(var j = 0, jLen = this.currentStructure.length; j < jLen; j++){
280 this.currentStructure[j].argumentIndex = j;
283 } else if(s == "@asm" && (mode == 0)){
285 s = this.separated[i].toLowerCase();
289 throw new ELCHNOSCompiler_CompileException(3, [s], lineCount);
291 } else if(s == "@end" && (mode == 70)){
294 } else if(s == "procedure" && (mode == 0)){
296 var f = new ELCHNOSCompiler_ExpressionStructure_Function(this, lineCount);
297 this.currentStructure.push(f);
298 this.changeCurrentStructure(f.structure);
299 currentExpression = f;
302 } else if(s == "inline" && (mode == 50)){
303 currentExpression.isInline = true;
304 } else if(s == "for" && (mode == 0)){
307 s = this.separated[i];
309 var f = new ELCHNOSCompiler_ExpressionStructure_Loop_for(this, lineCount);
310 this.currentStructure.push(f);
311 this.changeCurrentStructure(f.structure);
312 currentExpression = f;
314 f.initializer = new ELCHNOSCompiler_ExpressionStructure_Expression(this, lineCount);
315 i = f.initializer.readExpressionToTerminator(i, ";");
317 f.conditonalExpression = new ELCHNOSCompiler_ExpressionStructure_Expression(this, lineCount);
318 i = f.conditonalExpression.readExpressionToTerminator(i, ";");
320 f.incrementalExpression = new ELCHNOSCompiler_ExpressionStructure_Expression(this, lineCount);
321 i = f.incrementalExpression.readExpressionToTerminator(i, ")");
324 if(this.separated[i] != "{"){
325 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
327 currentExpression = null;
329 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
331 } else if(s == "if" && (mode == 0)){
338 s = this.separated[i];
340 var f = new ELCHNOSCompiler_ExpressionStructure_if(this, lineCount);
341 this.currentStructure.push(f);
342 this.changeCurrentStructure(f.structure);
343 currentExpression = f;
345 f.conditonalExpression = new ELCHNOSCompiler_ExpressionStructure_Expression(this, lineCount);
346 i = f.conditonalExpression.readExpressionToTerminator(i, ")");
349 if(this.separated[i] != "{"){
350 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
352 currentExpression = null;
354 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
357 } else if(s == "remark" && (mode == 70)){
359 var b = new ELCHNOSCompiler_ExpressionStructure_OSECPUBinary(this, lineCount);
360 this.currentStructure.push(b);
365 s = this.separated[i];
367 var len = parseInt(s, 16);
370 throw new ELCHNOSCompiler_CompileException(4, [s], lineCount);
374 s = this.separated[i];
375 if(s.length == len * 2){
376 for(var j = 0; j < len; j++){
377 b.bin.push(parseInt(s.substr(j * 2, 2), 16));
380 throw new ELCHNOSCompiler_CompileException(4, [s], lineCount);
384 if(this.separated[i] != ";"){
385 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
387 //この命令は定数のみで構成されているのでコンパイル済みとマークする
391 } else if(s == "call" && (mode == 70)){
393 var b = new ELCHNOSCompiler_ExpressionStructure_OSECPUBinary(this, lineCount);
394 this.currentStructure.push(b);
398 s = this.separated[i].toLowerCase();
399 if((s.indexOf("p") == 0) && s.length == 3){
402 var labelID = this.allocateLabelID();
403 //PLIMM(P30, labelID)
407 b.appendInstruction_UINT32BE(labelID);
412 b.bin.push(parseInt(s.substr(1),16));
414 b.appendInstruction_LB(0x01, labelID);
421 if(this.separated[i] != ";"){
422 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
424 //この命令は定数のみで構成されているのでコンパイル済みとマークする
429 throw new ELCHNOSCompiler_CompileException(0, [s], lineCount);
432 } else if(mode == 11 || mode == 52){
435 var v = new ELCHNOSCompiler_ExpressionStructure_Variable(this, lineCount);
437 v.isSigned = (numValSignFlag == 0) ? false : (0 != (numValSignFlag & this.Flag_Sign_Signed));
438 s = this.separated[i];
439 v.isPointer = (pointerCount != 0) ? true : false;
442 throw new ELCHNOSCompiler_CompileException(2, [s], lineCount);
445 this.currentStructure.push(v);
446 currentExpression = v;
450 } else if(mode == 14){
451 currentExpression.length = parseInt(s);
453 } else if(mode == 17){
455 currentExpression.initValue.push(parseInt(s));
457 } else if(mode == 50){
460 currentExpression.identifier = this.separated[i];
463 if(mode == 0 || mode == 70 || mode == 60 || mode == 71){
465 s = this.separated[i];
466 if(mode == 0 || mode == 70){
468 var f = new ELCHNOSCompiler_ExpressionStructure_Expression(this, lineCount);
469 this.currentStructure.push(f);
470 currentExpression = f;
472 currentExpression.pushIdentifier(s);
475 } else if(mode == 70){
483 //0x00-0xff: そのまま使えるバイナリ
484 //0x10000000-0x1fffffff: 未決定の仮想整数レジスタ番号
485 //0x20000000-0x2fffffff: 未決定の仮想ポインタレジスタ番号
488 //OSECPUフロントエンドコードヘッダ
489 this.bin.push(0x05, 0xe1, 0x00);
491 for(var i = 0, iLen = this.structure.length; i < iLen; i++){
492 var b = this.structure[i].createBinary();
497 this.expandBinaryString();
498 this.assignRegister();
499 this.bin.logAsHexByte(this.debug);
500 this.debug("(" + this.bin.length + "Bytes)\n");
504 var cpu = new WebCPU();
505 cpu.loadBinaryText(this.bin.stringAsHexByte());
506 cpu.staticOptimize();
507 var binStr = cpu.createBackendBinaryString();
509 this.debug("(" + (binStr.length >> 1) + "Bytes)\n");
513 if(e instanceof ELCHNOSCompiler_CompileException){
514 this.debug(e.getMessageString());
522 compile_removeComment: function(){
524 var commentLineStartIndex = -1;
525 var commentBlockStartIndex = -1;
526 var commentBlockCount = 0;
527 var linesInCommentBlock = 0;
529 for(var i = 0, iLen = this.separated.length; i < iLen; i++){
530 var s = this.separated[i];
531 if(commentLineStartIndex == -1){
532 if(s == "//" || s == "#"){
534 commentLineStartIndex = i;
539 var len = i - commentLineStartIndex;
540 this.separated.splice(commentLineStartIndex, len);
543 commentLineStartIndex = -1;
548 if(commentBlockCount == 0){
549 commentBlockStartIndex = i;
552 } else if(s == "*/"){
555 if(commentBlockCount == 0){
556 var len = i - commentBlockStartIndex + 1;
557 var padding = new Array();
558 padding.push(commentBlockStartIndex);
560 for(var j = 0, jLen = linesInCommentBlock; j < jLen; j++){
563 this.separated.splice.apply(this.separated, padding);
564 i -= len - linesInCommentBlock;
565 iLen -= len - linesInCommentBlock;
566 linesInCommentBlock = 0;
567 } else if(commentBlockCount < 0){
568 this.debug("Too many block comment closure [].\n");
571 } else if(commentBlockCount > 0 && s == "\n"){
572 linesInCommentBlock++;
576 raiseError: function(errno, lineCount, infoArray){
577 if(errno < 0 || this.errorMessageList.length <= errno){
578 this.debug(lineCount + ":Error" + errno.toString().toUpperCase() + ":Unknown\n");
580 this.debug(lineCount + ":Error" + errno.toString().toUpperCase() + ":" + this.errorMessageList[errno] + "\n");
583 this.debug(" >" + infoArray.toString() + "\n");
586 changeCurrentStructure: function(newstructure){
587 this.structureStack.push(this.currentStructure);
588 this.currentStructure = newstructure;
590 restoreCurrentStructure: function(){
591 this.currentStructure = this.structureStack.pop();
593 searchIdentifier: function(identifier){
595 var cf = function(aryobj, obj){ return (aryobj.identifier == obj) };
597 o = this.currentStructure.isIncluded(identifier, cf);
599 for(var i = this.structureStack.length - 1; i >= 0; i--){
600 o = this.structureStack[i].isIncluded(identifier, cf);
608 isOperator: function(s){
624 allocateLabelID: function(){
626 return this.nextLabelID - 1;
628 virtualIntegerRegisterOffset: 0x10000000,
629 allocateIntegerRegister: function(owner){
630 //レジスタテーブルは先頭から番号の小さい順につめて格納する。
632 //0x10000000-0x1fffffff: 未決定の仮想整数レジスタ番号
634 for(var i = 0, iLen = this.integerRegisterAllocationTable.length; i < iLen; i++){
635 if(this.integerRegisterAllocationTable[i][0] != i + this.virtualIntegerRegisterOffset){
638 this.integerRegisterAllocationTable.splice(i, 0, [i + this.virtualIntegerRegisterOffset, owner]);
643 //途中に空いているレジスタはなかったので追加
644 this.integerRegisterAllocationTable.push([i + this.virtualIntegerRegisterOffset, owner]);
646 return i + this.virtualIntegerRegisterOffset;
648 freeIntegerRegister: function(regID, owner){
649 for(var i = 0, iLen = this.integerRegisterAllocationTable.length; i < iLen; i++){
650 if(this.integerRegisterAllocationTable[i][0] == regID){
653 if(this.integerRegisterAllocationTable[i][1] == owner){
655 this.integerRegisterAllocationTable.splice(i, 1);
661 throw new ELCHNOSCompiler_CompileException(0, ["freeIntegerRegister:regID not found."], -1);
664 virtualPointerRegisterOffset: 0x20000000,
665 allocatePointerRegister: function(owner){
666 //レジスタテーブルは先頭から番号の小さい順につめて格納する。
668 //0x20000000-0x2fffffff: 未決定の仮想ポインタレジスタ番号
670 for(var i = 0, iLen = this.pointerRegisterAllocationTable.length; i < iLen; i++){
671 if(this.pointerRegisterAllocationTable[i][0] != i + this.virtualPointerRegisterOffset){
673 this.pointerRegisterAllocationTable.splice(i, 0, [i + this.virtualPointerRegisterOffset, owner]);
677 //途中に空いているレジスタはなかったので追加
678 this.pointerRegisterAllocationTable.push([i + this.virtualPointerRegisterOffset, owner]);
680 return i + this.virtualPointerRegisterOffset;
682 freePointerRegister: function(regID, owner){
683 for(var i = 0, iLen = this.pointerRegisterAllocationTable.length; i < iLen; i++){
684 if(this.pointerRegisterAllocationTable[i][0] == regID){
687 if(this.pointerRegisterAllocationTable[i][1] == owner){
689 this.pointerRegisterAllocationTable.splice(i, 1);
695 throw new ELCHNOSCompiler_CompileException(0, ["freePointerRegister:regID not found."], -1);
698 freeAllRegisterOfOwner: function(owner){
699 //ownerが確保しているレジスタをすべて解放する
701 for(var i = 0, iLen = this.integerRegisterAllocationTable.length; i < iLen; i++){
702 if(this.integerRegisterAllocationTable[i][1] == owner){
704 this.integerRegisterAllocationTable.splice(i, 1);
709 for(var i = 0, iLen = this.pointerRegisterAllocationTable.length; i < iLen; i++){
710 if(this.pointerRegisterAllocationTable[i][1] == owner){
712 this.pointerRegisterAllocationTable.splice(i, 1);
717 expandBinaryString: function(){
718 //入れ子構造になっている配列を一次元配列に展開して返す。
721 var aryStack = new Array();
722 var indexStack = new Array();
723 for(var i = 0; ; i++){
724 if(ary[i] === undefined){
725 ary = aryStack.pop();
726 if(ary === undefined){
729 i = indexStack.pop();
731 if(ary[i] instanceof Array){
733 indexStack.push(i + 1);
736 } else if(ary[i] !== undefined){
742 assignRegister: function(){
743 for(var i = 0, iLen = this.bin.length; i < iLen; i++){
744 if(this.bin[i] > 0xff){
745 if(this.virtualIntegerRegisterOffset <= this.bin[i] && this.bin[i] <= this.virtualIntegerRegisterOffset + 0x1f){
746 //R00-R1fは無条件で割り当ててOK
747 this.bin[i] -= this.virtualIntegerRegisterOffset;
748 } else if(this.virtualPointerRegisterOffset <= this.bin[i] && this.bin[i] <= this.virtualPointerRegisterOffset + 0x1f - 1){
749 //P01-P1fは無条件で割り当ててOK
750 this.bin[i] -= this.virtualPointerRegisterOffset;
753 throw new ELCHNOSCompiler_CompileException(0, ["Sorry, but no more register."]);
758 saveBinary: function(){
760 var v = new Uint8Array(this.bin);
761 var d = new Blob([v]);
765 url = window.URL.createObjectURL(d);
766 } else if(window.webkitURL){
767 url = window.webkitURL.createObjectURL(d);
769 window.alert("Can't create URL");
772 if(this.downloadBoxDOMObject){
773 this.downloadBoxDOMObject.innerHTML = "<a href='" + url + "' target='_blank'>右クリックしてダウンロード</a>";
775 this.debug("BinarySaved: " + url + "\n");