import * as fs from "fs"; var path = process.argv[2]; var pathout = process.argv[3]; if(!path||!pathout){ console.log("PLease this Schema: node ToAs.js [path] [pathout]"); process.exit(); } var file = fs.readFileSync(path).toString(); function error(msg,l=null,c=null){ var out = "Error" if(l!=null&&c!=null)out+=` in line ${l} and character ${c}`; out+=": "+msg; console.log("\x1b[31m",out,"\x1b[0m"); process.exit(1); } var lispSeperator = [" ",":","\n"]; function cutCmd(code,line,chars){ if(!code.startsWith("("))return ["",error("Compiler Error",line,chars)]; if(!code.endsWith(")"))return ["",error("Compiler Error",line,chars)]; code = code.substring(1,code.length-1); var countLinesBegin = line; var countCharsBegin = chars; var countLines = line; var countChars = chars; var inC = 0; var wasinC = false; var inStr = 0; var buffer = ""; var i = 0; var out = []; function finishBuff(){ out.push( wasinC? new LISPcmd(buffer.trim(),countLinesBegin,countCharsBegin) : new LISPstring(buffer.trim(),countLinesBegin,countCharsBegin) ); countLinesBegin = countLines; countCharsBegin = countChars; buffer = ""; wasinC = false; } for (var i = 0; i < code.length; i++) { countChars++; //console.log(code,countLines,countChars); let c = code[i]; if(!inC&&!inStr){ countLinesBegin = countLines; countCharsBegin = countChars; } if(c=="\\"){ buffer+=code[++i]; continue; } if(inStr){ if(c=="\n"){ countLines++; countChars = 0; }else if(c=='"'){ if(!inC)finishBuff(); inStr = false; }else{ buffer+=c; } continue; } if(c=='"'){ inStr = true; continue; } if(c==";"&&!inC){ while(code[++i]!="\n"){} countLines++; countChars = 0; continue; } if(c=="\n"){ countLines++; countChars = 0; } if(/*(c == " "||c=="\n")*/lispSeperator.includes(c)&&!inC){ if(buffer.trim()!=""){ finishBuff(); } continue; } if(c == "("){ if(!inC&&buffer.trim()!=""){ finishBuff(); } inC++; wasinC = true; } if(c == ")"){ inC--; if(inC<0)error("Closing braket to much!",countLines,countChars); if(!inC){ buffer+=c; finishBuff(); continue; } } buffer+=c; } if(inStr)error("Missing closing quotation mark!",countLines,countChars); if(inC)error("Missing closing braket!",countLines,countChars); if(buffer.trim()!="")finishBuff(); return out; } class LISPstring extends String { #l; #c; constructor(string,line,char) { super(string); this.#l = line; this.#c = char; } get lineCount(){ return this.#l; } get charCount(){ return this.#l; } get pos(){ return [this.#l,this.#c]; } } class LISPcmd extends Array { #l; #c; constructor(code,line,char) { var childs = cutCmd(code,line,char); super(...childs); this.#l = line; this.#c = char; } get lineCount(){ return this.#l; } get charCount(){ return this.#l; } get pos(){ return [this.#l,this.#c]; } } var data = new LISPcmd("(\n"+file+"\n)",0,0); //console.log(JSON.stringify(data)); function getType(data){ /*if(data=="empty"){ return ["emp", ""]; }*/ if(data=="true"){ return ["bool", 1]; } if(data=="false"){ return ["bool", 0]; } if(data=="NaN"){ return ["num", NaN]; } /*if(typeof data == "string"&&data.startsWith('"') && data.endsWith('"')){ return ["str", data.slice(1,-1)]; }*/ if((data instanceof LISPstring)&&!isNaN(data)/*!data.match(new RegExp("[^0-9.-e+]","g"))*/){ return ["num", Number(data)]; } if(data instanceof LISPcmd/*Array.isArray(data)*/){ return ["code", data]; } return ["var",data]; } var nid = 0; var dataTypes = { uint32: {ptype:0, size:4, mask:null }, uint16: {ptype:0, size:2, mask:0xffff }, uint8: {ptype:0, size:1, mask:0xff }, int32: {ptype:1, size:4, mask:null }, int16: {ptype:1, size:2, mask:0xffff }, int8: {ptype:1, size:1, mask:0xff }, float: {ptype:2, size:4, mask:null }, bool: {ptype:0, size:1, mask:1 }, }; dataTypes["*uint32"] = {ptype:0, size:2, mask:0xffff, pointing: dataTypes.uint32 }; dataTypes["*uint16"] = {ptype:0, size:2, mask:0xffff, pointing: dataTypes.uint16 }; dataTypes["*uint8"] = {ptype:0, size:2, mask:0xffff, pointing: dataTypes.uint8 }; dataTypes["*int32"] = {ptype:0, size:2, mask:0xffff, pointing: dataTypes.int32 }; dataTypes["*int16"] = {ptype:0, size:2, mask:0xffff, pointing: dataTypes.int16 }; dataTypes["*int8"] = {ptype:0, size:2, mask:0xffff, pointing: dataTypes.int8 }; dataTypes["*float"] = {ptype:0, size:2, mask:0xffff, pointing: dataTypes.float }; dataTypes["*bool"] = {ptype:0, size:2, mask:0xffff, pointing: dataTypes.bool }; var dataTypesReversed = { 0:"uint/bool/pointer", 1:"int", 2:"float" }; /*var numTypes = ["uint32", "uint16","uint8","int32","int16","int8","float","bool"];*/ function last(d){ return d[d.length-1]; } function find(d,n){ for (var i = d.length-1; i >= 0 ; i--) { if(typeof d[i][n] == "object")return d[i][n]; } return null; } function createVars(c){ var l = 0; return [Object.entries(c).map(d=>{ if(d[1].type=="custom"){ var out = `${d[1].id} = ${l};${d[1].length}`; l+=d[1].length; return out; }else{ var out = `${d[1].id} = ${l};${dataTypes[d[1].type].size}`; if(d[1].used==0) return ";"+out; l+=dataTypes[d[1].type].size; return out; } }).join("\n"),l]; } var extrafuns = { }; function execute(data,expect,context,local){ var code = ""; var ptype = 0; var doconv = true; let [type,d] = getType(data); if(type == "code"){ if(data[0]=="defvar"){ var ctx = last(context); var vname = data[1]; var vtype = data[2]; //var [vname,vtype] = data[1].split(":"); if(ctx[vname])error(`Can not redefine: ${cname}! It is already defined!`,...data.pos); if(!dataTypes[vtype])error(`Unknown Datatype: ${vtype}`,...data.pos); let mvar = {type:vtype,used:0,id:"v"+nid++,local:local}; ctx["v"+vname] = mvar; let [c,etype] = execute(data[3],mvar.type,context,local); code+= ` ;defvar: executing value: ${c} ;defvar: Store Value STA ${dataTypes[mvar.type].size|(mvar.local?0x10:0)} ${mvar.id} `; ptype = dataTypes[mvar.type].ptype; }else if(data[0]=="defun"){ var fname = data[1]; var ftype = data[2]; //var [fname,ftype] = (data[1]??"").split(":"); if(local)error(`Nested functions are currently not supported: ${fname}`,...data.pos); if(extrafuns[fname])error(`You can not declare functions double: ${fname}`,...fname.pos); if(!dataTypes[ftype])error(`Unknown Datatype: ${ftype}`,...ftype.pos); var ctx = last(context); var funsCtx = {}; var args = []; var lcode = ""; for (var i = 0; i < data[3].length; i++) { let vname = data[3][i]; let vtype = data[3][i+1]; if(funsCtx[vname])error(`You declared the Argument ${vname} in function",fname,"twice!`,...vname.pos); if(!dataTypes[vtype])error(`Unknown Datatype: ${vtype}`,...vtype.pos); var mvarg = {type:vtype,used:1,id:"arg"+nid++,local:false}; var mvarl = {type:vtype,used:1,id:"arg"+nid++,local:true}; funsCtx["v"+vname] = mvarl; args.push(mvarg); ctx["arg_"+vname] = mvarg; lcode+=` ;function copy args LDA ${dataTypes[mvarg.type].size} ${mvarg.id} STA ${dataTypes[mvarl.type].size|0x10} ${mvarl.id} ` } /*data[3].forEach(v=>{ var [vname,vtype] = v.split(":"); if(funsCtx[vname])error(`You declared the Argument ${vname} in function",fname,"twice!`,...vname.pos); if(!dataTypes[vtype])error(`Unknown Datatype: ${vtype}`,...vtype.pos); var mvarg = {type:vtype,used:1,id:"arg"+nid++,local:false}; var mvarl = {type:vtype,used:1,id:"arg"+nid++,local:true}; funsCtx["v"+vname] = mvarl; args.push(mvarg); ctx["arg_"+vname] = mvarg; lcode+=` ;function copy args LDA ${dataTypes[mvarg.type].size} ${mvarg.id} STA ${dataTypes[mvarl.type].size|0x10} ${mvarl.id} ` });*/ var mvar = {type:ftype,used:1,id:"return"+nid++,local:false}; var fun = { type:ftype, code:"", args, return:mvar }; extrafuns[fname] = fun; //execute lcode lcode+="\n;function code:\n"; for (var i = 4; i < data.length; i++) { let [c,t] = execute(data[i],data.length-1==i?ftype:"any",[...context,funsCtx],true); lcode+=c; } ctx["return_"+fname] = mvar; lcode+= ` ;return from subrutine STA ${dataTypes[mvar.type].size} ${mvar.id} RSR `; let [localvars,localL] = createVars(funsCtx); lcode = ` ;function reserve Stackspace ${(new Array(Math.floor(localL/255))).fill("PSH 255").join("\n ")} ${(localL%255>0?`PSH ${localL%255}`:'')} ;function place variable pointers ${localvars} ${lcode} `; fun.code = lcode; code=` LIA .${fname} `; ptype=0; }else if(data[0]=="let"){ var mvar = find(context,"v"+data[1]); if(mvar == null)error(`Unknown Variable: ${data[1]}`,...data.pos); mvar.used++; let [c,etype] = execute(data[2],mvar.type,context,local); code+= ` ;let: executing value: ${c} ;let: Store Value STA ${dataTypes[mvar.type].size|(mvar.local?0x10:0)} ${mvar.id} `; ptype = dataTypes[mvar.type].ptype; }else if(data[0]=="+"){ var etypes = []; var etypeMax = 0; for (var i = 1; i < data.length; i++) { let [c,etype] = execute(data[i],"num",context,local); code+=` ;+: next value ${c} `; if(ietypeMax)etypeMax=etype; } if(etypes[0]!=etypeMax){ code+=` ;+: converting not matching types CVA ${etypes[0]} ${etypeMax} `; } for (var i = 1; i < etypes.length; i++) { code+=` ;+: Pull/Add next CAB PUL 4 ADD ${etypeMax} ${etypes[i]} ${etypeMax} `; } ptype = etypeMax; }else if(data[0]=="-"){ var etypes = []; var etypeMax = 0; for (var i = data.length-1; i >=1 ; i--) { let [c,etype] = execute(data[i],"num",context,local); code+=` ;-: next value ${c} `; if(i>1){ code+=` PSH 4 `; } etypes.push(etype); if(etype>etypeMax)etypeMax=etype; } if(etypes[0]!=etypeMax){ code+=` ;-: converting not matching types CVA ${etypes[0]} ${etypeMax} `; } for (var i = 1; i < etypes.length; i++) { code+=` ;-: Pull/Add next CAC PUL 4 CAB CCA SUB ${etypeMax} ${etypes[i]} ${etypeMax} `; } ptype = etypeMax; }else if(data[0]=="*"){ var etypes = []; var etypeMax = 0; for (var i = 1; i < data.length; i++) { let [c,etype] = execute(data[i],"num",context,local); code+=` ;*: next value ${c} `; if(ietypeMax)etypeMax=etype; } if(etypes[0]!=etypeMax){ code+=` ;*: converting not matching types CVA ${etypes[0]} ${etypeMax} `; } for (var i = 1; i < etypes.length; i++) { code+=` ;*: Pull/Add next CAB PUL 4 MUL ${etypeMax} ${etypes[i]} ${etypeMax} `; } ptype = etypeMax; }else if(data[0]=="/"){ var etypes = []; var etypeMax = 0; for (var i = data.length-1; i >=1 ; i--) { let [c,etype] = execute(data[i],"num",context,local); code+=` ;/: next value ${c} `; if(i>1){ code+=` PSH 4 `; } etypes.push(etype); if(etype>etypeMax)etypeMax=etype; } if(etypes[0]!=etypeMax){ code+=` ;/: converting not matching types CVA ${etypes[0]} ${etypeMax} `; } for (var i = 1; i < etypes.length; i++) { code+=` ;/: Pull/Add next CAC PUL 4 CAB CCA DIV ${etypeMax} ${etypes[i]} ${etypeMax} `; } ptype = etypeMax; }else if(data[0]=="print"){ let [c,etype] = execute(data[1],"any",context,local); code+=` ;print: executing value: ${c} ;print Value OUT ${etype} `; ptype = etype; }else if(data[0]=="if"){ let [c,etype] = execute(data[1],"bool",context,local); let [c1,etype1] = execute(data[2],expect,context,local); let c2,etype2; if(typeof data[3]!="undefined"){ [c2,etype2] = execute(data[3],expect,context,local); }else{ [c2,etype2] = ["LIA 0",0]; } var id = nid++; code+=` ;if: executing value: ${c} ;if: LIB 1 CMP 0 0 JNE .else${id} ;then code ${c1} JMP .afterif${id} else${id}: ;else code ${c2} afterif${id}: `; ptype = etype1; }else if(data[0]==">"){ let [c1,etype1] = execute(data[1],"num",context,local); let [c2,etype2] = execute(data[2],"num",context,local); doconv = false; var id = nid++; code+=` ;>: execute first ${c1} PSH 4 ;>: execute secound ${c2} CAB PUL 4 CMP ${etype1} ${etype2} LIA 1 JB .endcompare${id} LIA 0 endcompare${id}: `; }else if(data[0]=="<"){ let [c1,etype1] = execute(data[1],"num",context,local); let [c2,etype2] = execute(data[2],"num",context,local); doconv = false; var id = nid++; code+=` ;<: execute first ${c1} PSH 4 ;<: execute secound ${c2} CAB PUL 4 CMP ${etype1} ${etype2} LIA 1 JS .endcompare${id} LIA 0 endcompare${id}: `; }else if(data[0]=="="){ let [c1,etype1] = execute(data[1],"num",context,local); let [c2,etype2] = execute(data[2],"num",context,local); doconv = false; var id = nid++; code+=` ;>: execute first ${c1} PSH 4 ;>: execute secound ${c2} CAB PUL 4 CMP ${etype1} ${etype2} LIA 1 JE .endcompare${id} LIA 0 endcompare${id}: `; }else if(data[0]=="loop"){ if(data[1] == "while"){ let [c,etype] = execute(data[2],"bool",context,local); var idbefor = nid++; var idafter = nid++; var codeinsert = ""; for (var i = 3; i < data.length; i++) { let [ci,ti] = execute(data[i],i==data.length-1?expect:"any",context,local); ptype = ti; codeinsert+=ci; } code+=` beforloop${idbefor}: ${c} LIB 1 CMP 0 0 JNE .afterloop${idafter} ;loop code ${codeinsert} JMP .beforloop${idbefor} afterloop${idafter}: `; }else if(data[1] == "for"){ error("Methode 'for' will be implemented later.",...data[1].pos); }else{ error(`Unknown loop Methode: ${data[1]}`,...data[1].pos); } }else if(data[0]=="list"){ var ltype = expect;//data[1]; if (!ltype.startsWith("*"))error("list requires appropriated context to determine the Datatype requires appropriated context to determine the datatype",...data[0].pos); if(!dataTypes[ltype])error(`Unknown Datatype: ${ltype}`,...ltype.pos); let mvar = {type:"custom",length:dataTypes[ltype].pointing.size*(data.length-1),id:"l"+nid++,local:local}; var ctx = last(context); ctx["l"+nid++] = mvar; for (var i = 0; i < data.length-1; i++) { let [c,t] = execute(data[i+1],ltype.substr(1),context,local); code+=c; code+=` ; load ${i} List element LIX ${i*dataTypes[ltype].size} SOA ${dataTypes[ltype].size|(mvar.local?0x10:0)} ${mvar.id} `; } code+=` ; return List Pointer LIA ${mvar.id} `; ptype=0; }else if(data[0]=="list-nth"){ var mvar = find(context,"v"+data[1]); mvar.used++; if (!mvar.type.startsWith("*"))error("list-nth requires Listpointer as Input.",...data[1].pos); let [c,etype] = execute(data[2],"uint16",context,local); code+=` ; Load list pointers ${c} ; calculate Byte offset LIB ${dataTypes[mvar.type].pointing.size} MUL 0 0 0 ; Load pointer Address LIX ${mvar.id} LOB ${2|(mvar.local?0x10:0)} ${mvar.id} ADD 0 0 0 CAX ;fetch list-nth LOA ${dataTypes[mvar.type].pointing.size|(mvar.local?0x10:0)} 0 `; ptype = dataTypes[mvar.type].pointing.ptype; }else if(data[0]=="list-set"){ var mvar = find(context,"v"+data[1]); mvar.used++; if (!mvar.type.startsWith("*"))error("list-set requires Listpointer as Input.",...data[1].pos); let [c1,etype1] = execute(data[2],"uint16",context,local); let [c2,etype2] = execute(data[3],mvar.type.substr(1),context,local); code+=` ;Load value ${c2} PSH ${dataTypes[mvar.type].pointing.size} ; Load list pointers ${c1} ; calculate Byte offset LIB ${dataTypes[mvar.type].pointing.size} MUL 0 0 0 ; Load pointer Address LIX ${mvar.id} LOB ${2|(mvar.local?0x10:0)} ${mvar.id} ADD 0 0 0 CAX PUL ${dataTypes[mvar.type].pointing.size} ;fetch list-nth SOA ${dataTypes[mvar.type].pointing.size|(mvar.local?0x10:0)} 0 `; ptype = dataTypes[mvar.type].pointing.ptype; }else{ if(extrafuns[data[0]]){ var fun = extrafuns[data[0]]; for (var i = 0; i < fun.args.length; i++) { if(typeof data[i+1] == "undefined")error(`Argument missing for function ${data[0]}`,...data.pos); var [ecode,etype] = execute(data[i+1],fun.args[i].type,context,local); code+=` ;${i+1} Argument ${ecode} STA ${dataTypes[fun.args[i].type].size} ${fun.args[i].id} `; } code+=` ;excute function JSR .${data[0]} ;loading return value LDA ${dataTypes[fun.return.type].size} ${fun.return.id} `; ptype = dataTypes[fun.return.type].ptype; }else{ error(`Unknown command: ${data[0]}`,...data.pos); } } }else if(type == "var"){ let mvar = find(context,"v"+d); if(mvar == null)error(`Unknown Variable/Expression: ${d}`,...data.pos); mvar.used++; code += ` ;var: load Variable LDA ${dataTypes[mvar.type].size|(mvar.local?0x10:0)} ${mvar.id} `; ptype = dataTypes[mvar.type].ptype; }else if(type == "num"||type=="bool"){ ptype = Number.isInteger(d)?(d>=0?0:1):2; if(dataTypes[expect]?.ptype == ptype){ code += ` ;num: Loading num LIA ${dataTypes[expect].mask==null?d:dataTypes[expect].mask&d} `; doconv = false; }else{ code += ` ;num: Loading num LIA ${d} `; } }else{ error(`Not Supported execution type: ${type} of ${d}`,...data.pos); } if(expect=="any")return [code,ptype]; if(expect=="num"){ if(ptype==0||ptype==1||ptype==2){ return [code,ptype]; }else{ error(`Can not convert ${dataTypesReversed[ptype]} to Number`,...data.pos); } } if(ptype!=dataTypes[expect].ptype){ code+=` CVA ${ptype} ${dataTypes[expect].ptype} `; } if(doconv&&dataTypes[expect].mask != null){ code+=` LIB ${dataTypes[expect].mask} BWA `; } return [code,dataTypes[expect].ptype]; } var code = ""; var context = [{}]; for (var i = 0; i < data.length; i++) { let [c,t] = execute(data[i],"any",context); code += c; //console.log(c); } var [globvars,globL] = createVars(context[0]); var finish = ` ${globvars} SHS ${globL} ;code ${code} HLT ;functions ${Object.entries(extrafuns).map(d=>d[0]+":\n"+d[1].code).join("\n\n")} `; fs.writeFileSync(pathout,finish); console.log(`Finished compiling in ${Math.round(performance.now())/1000}sec. Assembly saved to: ${pathout}`);