├── .gitmodules ├── LICENSE ├── README.md ├── fibonacci.HC ├── holy.js ├── holyc-interpreter.js ├── index.html ├── makefile ├── nix └── shell.nix ├── package.json └── templeos_font.ttf /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "html-holyc-highlighter"] 2 | path = html-holyc-highlighter 3 | url = https://github.com/leozamboni/html-holyc-highlighter 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Leonardo Z. Nunes 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HolyC Interpreter 2 | 3 | A small sandboxed handwritten HolyC interpreter in JavaScript. 4 | 5 | [Demo](https://leozamboni.github.io/HolyC-interpreter/) 6 | 7 | If you want a CLI use [HolyC repl](https://github.com/leozamboni/HolyC-repl). 8 | 9 | ## Run 10 | 11 | ``` 12 | bun dev 13 | ``` 14 | 15 | ### Dependencies 16 | 17 | - bun 18 | 19 | ## Self host 20 | 21 | For local run you will need a HTTP server: 22 | 23 | ``` 24 | bun dev 25 | ``` 26 | 27 | Create a input tag with "stdin" tag and another with "stdout/stderr": 28 | 29 | ```html 30 | 31 | ``` 32 | 33 | The stdin will be for the code and the stdout/stderr for the interpreter output. 34 | 35 | Import this procedure in your BOM inside the head tag: 36 | 37 | ```html 38 | 42 | ``` 43 | 44 | And call this procedure with a button, for example: 45 | 46 | ```html 47 | 48 | ``` 49 | 50 |
51 | 52 | If you want to support this project you can star the repository. 53 | -------------------------------------------------------------------------------- /fibonacci.HC: -------------------------------------------------------------------------------- 1 | // HolyC Fibonacci 2 | U0 3 | Fibonacci() 4 | { 5 | U32 a = 0, b = 1, c, i; 6 | 7 | for (i = 0; i < 20; i++) 8 | { 9 | c = a + b; 10 | "%d\n", c; 11 | a = b; 12 | b = c; 13 | } 14 | } 15 | 16 | Fibonacci; -------------------------------------------------------------------------------- /holy.js: -------------------------------------------------------------------------------- 1 | let hc={modes:{HolyNode:!1},files:{stdin:'',stdout:'',stderr:''},lexer:{char:'',line:1,index:0},parser:{index:0},symtab:{idle:!1,global:[],scoped:[],prototypes:[],class:[],types:[]}};const stderr=eval("(value) =>"+(hc.modes.HolyNode?void 0:'(document.getElementById("stdout/stderr").value = value) && (document.getElementById("stdout/stderr").style.color = "red")')),stdin=()=>eval(hc.modes.HolyNode?void 0:'document.getElementById("stdin")'),stdout=e=>(document.getElementById("stdout/stderr").value=e)&&(document.getElementById("stdout/stderr").style.color="black");export const holyc_web_run=async()=>stdout(await output(parser(await lexer(init_hc()))));export const holy_node_idle=async e=>(hc.symtab.idle=hc.modes.HolyNode=!0)&&await output(parser(await lexer(init_hc(e))));export const holy_node_script=async e=>(hc.modes.HolyNode=!0)&&await output(parser(await lexer(init_hc(e))));export const holy_script=async e=>document.getElementById("stdout/stderr").innerText=await output(parser(await lexer(init_hc(e))));class AstNode{token;next;left;right;constructor(e){this.type=e}}class Ast{next;ast}class Token{constructor(e,t,n){this.id=e,this.type=t,this.line=n}}const token_type={number:1,str:2,id:3,add:4,sub:5,div:6,mul:7,semi:8,lbrace:9,rbrace:10,i0:11,u0:12,i8:13,u8:14,i16:15,u16:16,i32:17,u32:18,i64:19,u64:20,f64:21,rparen:22,lparen:23,call:24,comma:25,assig:26,less:27,big:28,for:29,increment:30,decrement:31,assingsum:32,assingsub:33,assingdiv:34,assingmul:35,if:36,or:37,and:38,not:39,else:40,true:41,false:42,equal:43,return:44,bigequal:45,lessequal:46,bool:47,class:48,dot:49,classExp:50,define:51,include:52,js:53,jscode:54},token_cases=[{char:"/",render:e=>{if(e.files.stdin[e.lexer.index]==="/"){for(;e.files.stdin[e.lexer.index++]&&e.files.stdin[e.lexer.index]!=="\n";);return"{comment}"}return e.files.stdin[e.lexer.index]==="="?(e.lexer.index++,new Token("/=",token_type.assingdiv,e.lexer.line)):new Token("/",token_type.div,e.lexer.line)}},{char:"'",render:e=>lex_simple_quote_string(e)},{char:'"',render:e=>lex_string(e)},{char:"+",render:e=>e.files.stdin[e.lexer.index]==="+"&&e.lexer.index++&&new Token("++",token_type.increment,e.lexer.line)||e.files.stdin[e.lexer.index]==="="&&e.lexer.index++&&new Token("+=",token_type.assingsum,e.lexer.line)||new Token("+",token_type.add,e.lexer.line)},{char:"-",render:e=>e.files.stdin[e.lexer.index]==="-"&&e.lexer.index++&&new Token("--",token_type.increment,e.lexer.decrement)||e.files.stdin[e.lexer.index]==="="&&e.lexer.index++&&new Token("-=",token_type.assingsub,e.lexer.line)||new Token("-",token_type.sub,e.lexer.line)},{char:"*",render:e=>e.files.stdin[e.lexer.index]==="="&&e.lexer.index++&&new Token("*=",token_type.assingmul,e.lexer.line)||new Token("*",token_type.mul,e.lexer.line)},{char:";",render:e=>new Token(";",token_type.semi,e.lexer.line)},{char:".",render:e=>new Token(".",token_type.dot,e.lexer.line)},{char:"{",render:e=>new Token("{",token_type.rbrace,e.lexer.line)},{char:"}",render:e=>new Token("}",token_type.lbrace,e.lexer.line)},{char:"(",render:e=>new Token("(",token_type.rparen,e.lexer.line)},{char:")",render:e=>new Token(")",token_type.lparen,e.lexer.line)},{char:",",render:e=>new Token(",",token_type.comma,e.lexer.line)},{char:"!",render:e=>new Token("!",token_type.not,e.lexer.line)},{char:"=",render:e=>e.files.stdin[e.lexer.index]==="="&&e.lexer.index++&&new Token("==",token_type.equal,e.lexer.line)||new Token("=",token_type.assig,e.lexer.line)},{char:"<",render:e=>e.files.stdin[e.lexer.index]==="="&&e.lexer.index++&&new Token("<=",token_type.lessequal,e.lexer.line)||new Token("<",token_type.less,e.lexer.line)},{char:">",render:e=>e.files.stdin[e.lexer.index]==="="&&e.lexer.index++&&new Token(">=",token_type.bigequal,e.lexer.line)||new Token(">",token_type.big,e.lexer.line)},{char:"&",render:e=>e.files.stdin[e.lexer.index]==="&"&&e.lexer.index++&&new Token("&&",token_type.and,e.lexer.line)},{char:"|",render:e=>e.files.stdin[e.lexer.index]==="|"&&e.lexer.index++&&new Token("||",token_type.or,e.lexer.line)}],token_keywords=[{id:"for",type:token_type.for},{id:"class",type:token_type.class},{id:"if",type:token_type.if},{id:"else",type:token_type.else},{id:"return",type:token_type.return},{id:"TRUE",type:token_type.true},{id:"FALSE",type:token_type.false},{id:"Bool",type:token_type.bool},{id:"I0",type:token_type.i0},{id:"U0",type:token_type.u0},{id:"I8",type:token_type.i8},{id:"U8",type:token_type.u8},{id:"I16",type:token_type.i16},{id:"U16",type:token_type.u16},{id:"I32",type:token_type.i32},{id:"U32",type:token_type.u32},{id:"I64",type:token_type.i64},{id:"U64",type:token_type.u64},{id:"F64",type:token_type.f64},{id:"#define",type:token_type.define},{id:"#include",type:token_type.include},{id:"js",type:token_type.js}],is_alpha=e=>/^[A-Z0-9_#]$/i.test(e),is_number=e=>/^[0-9]$/.test(e),is_ignored=e=>(e==="\n"&&hc.lexer.line++,/[ \n\t]/.test(e)),lex_include=async e=>{const t=lex_alpha(e);return t.id!=="#include"?t:(e.lexer.index+=2,e.files.stdin=await fetch(lex_string(e).id).then(e=>e.text()).then(e=>e)+e.files.stdin.slice(e.lexer.index,e.files.stdin.length),e.lexer.index=0,"{include}")},lex_alpha=e=>{let t='';do t+=e.lexer.char;while(is_alpha(e.lexer.char=e.files.stdin[e.lexer.index++]))return e.lexer.index--,new Token(t,token_keywords.find(e=>e.id===t)?.type||token_type.id,e.lexer.line)},lex_number=e=>{let t='';do t+=e.lexer.char;while(is_number(e.lexer.char=e.files.stdin[e.lexer.index++]))return e.lexer.index--,new Token(t,token_type.number,e.lexer.line)},lex_simple_quote_string=e=>{e.lexer.char=e.files.stdin[e.lexer.index++];let t='';do!e.lexer.char&&lexer_error({id:"EOF",line:e.lexer.line}),t+=e.lexer.char;while((e.lexer.char=e.files.stdin[e.lexer.index++])!=="'")return new Token(t,token_type.str,e.lexer.line)},lex_string=e=>{e.lexer.char=e.files.stdin[e.lexer.index++];let t='';do!e.lexer.char&&lexer_error({id:"EOF",line:e.lexer.line}),t+=e.lexer.char;while((e.lexer.char=e.files.stdin[e.lexer.index++])!=='"')return new Token(t,token_type.str,e.lexer.line)},lexer_lex=async e=>{for(;(e.lexer.char=e.files.stdin[e.lexer.index++])&&e.lexer.char;){let t='';if(is_ignored(e.lexer.char))continue;if(e.lexer.char==="#"){if(t=await lex_include(e),t==="{include}")continue;return t}if(is_number(e.lexer.char))return lex_number(e);if(is_alpha(e.lexer.char))return lex_alpha(e);if(t=token_cases.find(t=>t.char===e.lexer.char)?.render(e),t==="{comment}")continue;if(t)return t;lexer_error({id:e.lexer.char,line:e.lexer.line})}},init_hc=e=>{if(stderr&&stderr(''),stdin()?.value&&(hc.files.stdin=stdin().value)||(hc.files.stdin=e),hc.files.stdout='',hc.files.stderr='',hc.lexer.char='',hc.lexer.index=0,hc.lexer.line=1,hc.parser.index=0,!hc.symtab.idle&&(hc.symtab.types=[]),!hc.symtab.idle&&(hc.symtab.global=[]),!hc.symtab.idle&&(hc.symtab.prototypes=[]),!hc.symtab.idle&&(hc.symtab.class=[]),!hc.symtab.idle&&(hc.symtab.scoped=[]),!hc.files.stdin){hc.files.stderr+="Compile failure\nLexer: nothing to compile\n";try{throw stderr(hc.files.stderr)}catch{throw new Error(hc.files.stderr)}}return hc},lexer=async e=>{let t=[];for(;1;){const n=await lexer_lex(e);if(!n)break;if(t.push(n),n.type===token_type.js&&e.files.stdin[e.lexer.index+1]==="{"){e.lexer.index++,t.push(new Token("{",token_type.rbrace,e.lexer.line)),e.lexer.index++,e.lexer.char=e.files.stdin[e.lexer.index];let n='';do e.lexer.char=e.files.stdin[e.lexer.index++],!e.lexer.char&&lexer_error({id:"EOF",line:e.lexer.line}),n+=e.lexer.char;while(e?.files?.stdin.substring(e.lexer.index,e.lexer.index+2)!=="};")t.push(new Token(n,token_type.jscode,e.lexer.line)),t.push(new Token("}",token_type.lbrace,e.lexer.line)),t.push(new Token(";",token_type.semi,e.lexer.line)),e.lexer.index+=2}}return t},get_symtab=e=>{for(let t=0;t{for(let t=0;t{for(let t=0;t{hc.files.stderr=`Compile failure\nLexer: '${e.id}' unexpected token in line ${e.line}\n`;try{throw stderr(hc.files.stderr)}catch{throw new Error(hc.files.stderr)}},internal_error=e=>{hc.files.stderr=`Compile failure\nInternal error: '${e}'`;try{throw stderr(hc.files.stderr)}catch{throw new Error(hc.files.stderr)}},parser_error=e=>{hc.files.stderr=`Compile failure\nParser: '${e.id}' unexpected token in line ${e.line}\n`;try{throw stderr(hc.files.stderr)}catch{throw new Error(hc.files.stderr)}},check_symtab=(e,t)=>{symtab_contain(e[hc.parser.index])!==t&&parser_error(e[hc.parser.index])},list_eat=(e,t)=>{try{if(e[hc.parser.index].type!==t)throw new Error;hc.parser.index++}catch{parser_error(e[hc.parser.index]?e[hc.parser.index]:e[hc.parser.index-1])}},symtab_contain=e=>!!hc.symtab.global.filter(t=>t.id===e.id).length,check_token=(e,t,n)=>{try{return e[t].type===n}catch{parser_error(e[t-1])}},check_ast_type=(e,t)=>{switch(t){case"id":return e===token_type.id;case"data_type":return e===token_type.i0||e===token_type.u0||e===token_type.i8||e===token_type.u8||e===token_type.i16||e===token_type.u16||e===token_type.i32||e===token_type.u32||e===token_type.i64||e===token_type.u64||e===token_type.f64||e===token_type.bool;case"assignment_operator":return e===token_type.assingdiv||e===token_type.assingmul||e===token_type.assingsub||e===token_type.assingsum}},is_dtype=(e,t)=>{try{let n=e[t].type;return n===token_type.i0||n===token_type.u0||n===token_type.i8||n===token_type.u8||n===token_type.i16||n===token_type.u16||n===token_type.i32||n===token_type.u32||n===token_type.i64||n===token_type.u64||n===token_type.f64||n===token_type.bool||hc.symtab.types.findIndex(n=>n.id===e[t].id)>-1}catch{parser_error(e[t-1])}},is_logicalop=(e,t)=>{try{let n=e[t].type;return n===token_type.big||n===token_type.less||n===token_type.or||n===token_type.and||n===token_type.not||n===token_type.equal||n===token_type.bigequal||n===token_type.lessequal}catch{parser_error(e[t-1])}},is_mathop=(e,t)=>{try{let n=e[t].type;return n===token_type.add||n===token_type.sub||n===token_type.div||n===token_type.mul||n===token_type.increment||n===token_type.decrement}catch{parser_error(e[t-1])}},is_assingop=(e,t)=>{try{let n=e[t].type;return n===token_type.assingdiv||n===token_type.assingmul||n===token_type.assingsub||n===token_type.assingsum||n===token_type.assig}catch{parser_error(e[t-1])}},list_eat_type=e=>{is_dtype(e,hc.parser.index)?hc.parser.index++:parser_error(e[hc.parser.index])},list_eat_logical=e=>{is_logicalop(e,hc.parser.index)?hc.parser.index++:parser_error(e[hc.parser.index])},list_eat_math=e=>{is_mathop(e,hc.parser.index)?hc.parser.index++:parser_error(e[hc.parser.index])},list_eat_compassing=e=>{is_assingop(e,hc.parser.index)?hc.parser.index++:parser_error(e[hc.parser.index])},parser_parse_class=e=>{let t=new AstNode(token_type.class);t.token=e[hc.parser.index],list_eat(e,token_type.class),check_symtab(e,!1),get_class(e[hc.parser.index])>-1?lexer_error(e[hc.parser.index]):hc.symtab.class.push(e[hc.parser.index]);const s=get_class(e[hc.parser.index]);hc.symtab.class[s].content=[],t.left=new AstNode(token_type.id),t.left.token=e[hc.parser.index],list_eat(e,token_type.id),list_eat(e,token_type.rbrace);let n=hc.parser.index;if(t.right=parser_parse_class_vars(e,s+1),list_eat(e,token_type.lbrace),check_token(e,hc.parser.index,token_type.semi))list_eat(e,token_type.semi);else{let s=[];for(;e[n++]&&e[n].type!==token_type.lbrace;)check_token(e,n,token_type.id)&&s.push({...e[n],value:0});hc.symtab.types.findIndex(t=>t.id===e[hc.parser.index].id)>-1&&lexer_error(e[hc.parser.index]),hc.symtab.types.push({...e[hc.parser.index],value:s,classType:!0}),t.left.left=new AstNode(token_type.id),t.left.left.token=e[hc.parser.index],list_eat(e,token_type.id),list_eat(e,token_type.semi)}return t},parser_parse_logical_exp=e=>{if(check_token(e,hc.parser.index,token_type.semi)||check_token(e,hc.parser.index,token_type.comma)||check_token(e,hc.parser.index,token_type.lparen))return null;let t;return check_token(e,hc.parser.index-1,token_type.id)||check_token(e,hc.parser.index-1,token_type.number)||check_token(e,hc.parser.index-1,token_type.true)||check_token(e,hc.parser.index-1,token_type.false)?is_logicalop(e,hc.parser.index)?(t=new AstNode(e[hc.parser.index]?.type),t.token=e[hc.parser.index],list_eat_logical(e)):(check_token(e,hc.parser.index-1,token_type.id)||check_token(e,hc.parser.index-1,token_type.number)||check_token(e,hc.parser.index-1,token_type.true)||check_token(e,hc.parser.index-1,token_type.false))&&(t=new AstNode(e[hc.parser.index]?.type),t.token=e[hc.parser.index],list_eat_math(e)):is_logicalop(e,hc.parser.index-1)?check_token(e,hc.parser.index,token_type.not)?(t=new AstNode(token_type.not),t.token=e[hc.parser.index],list_eat(e,token_type.not)):check_token(e,hc.parser.index,token_type.id)?(check_symtab(e,!0),t=new AstNode(token_type.id),t.token=e[hc.parser.index],list_eat(e,token_type.id)):check_token(e,hc.parser.index,token_type.true)?(t=new AstNode(token_type.true),t.token={id:1,line:e[hc.parser.index].line,type:token_type.number},list_eat(e,token_type.true)):check_token(e,hc.parser.index,token_type.false)?(t=new AstNode(token_type.number),t.token={id:0,line:e[hc.parser.index].line,type:token_type.number},list_eat(e,token_type.false)):(t=new AstNode(token_type.number),t.token=e[hc.parser.index],list_eat(e,token_type.number)):check_token(e,hc.parser.index,token_type.id)?(check_symtab(e,!0),t=new AstNode(token_type.id),t.token=e[hc.parser.index],list_eat(e,token_type.id)):check_token(e,hc.parser.index,token_type.true)?(t=new AstNode(token_type.true),t.token={id:1,line:e[hc.parser.index].line,type:token_type.number},list_eat(e,token_type.true)):check_token(e,hc.parser.index,token_type.false)?(t=new AstNode(token_type.number),t.token={id:0,line:e[hc.parser.index].line,type:token_type.number},list_eat(e,token_type.false)):(t=new AstNode(token_type.number),t.token=e[hc.parser.index],list_eat(e,token_type.number)),t.right=parser_parse_logical_exp(e),t},parser_parse_exp=(e,s,n)=>{if(check_token(e,hc.parser.index,token_type.semi)||check_token(e,hc.parser.index,token_type.comma)||s&&check_token(e,hc.parser.index,token_type.lparen))return null;let t;if(hc.symtab.prototypes.find(t=>t.id===e[hc.parser.index].id)&&!check_token(e,hc.parser.index-1,token_type.number)&&!check_token(e,hc.parser.index-1,token_type.id))return parser_parse_call(e);if(check_token(e,hc.parser.index-1,token_type.id)||check_token(e,hc.parser.index-1,token_type.number)||check_token(e,hc.parser.index-1,token_type.classExp))is_mathop(e,hc.parser.index)?(check_token(e,hc.parser.index+1,token_type.id)||check_token(e,hc.parser.index+1,token_type.number))&&(t=new AstNode(e[hc.parser.index]?.type),t.token=e[hc.parser.index],list_eat_math(e)):is_assingop(e,hc.parser.index)?(t=new AstNode(e[hc.parser.index]?.type),t.token=e[hc.parser.index],list_eat_compassing(e)):(t=new AstNode(token_type.assig),t.token=e[hc.parser.index],list_eat(e,token_type.assig));else if(check_token(e,hc.parser.index-1,token_type.assig)||is_assingop(e,hc.parser.index-1))if(check_token(e,hc.parser.index,token_type.id)){let s;if(n?(s=hc.symtab.prototypes[n-1].args.find(t=>t.id===e[hc.parser.index].id),s||(s=hc.symtab.scoped[n-1]?.find(t=>t.id===e[hc.parser.index].id))):s=hc.symtab.prototypes.find(t=>t.id===e[hc.parser.index].id),!s&&check_symtab(e,!0),check_token(e,hc.parser.index+1,token_type.dot)){let n=[];for(;e[hc.parser.index].type===token_type.id;)e[hc.parser.index].type===token_type.id&&n.push(e[hc.parser.index].id),hc.parser.index+=2;hc.parser.index-=2,e[hc.parser.index].id=n.join("."),e[hc.parser.index].type=token_type.classExp,t=new AstNode(token_type.classExp),t.token=e[hc.parser.index],list_eat(e,token_type.classExp)}else t=new AstNode(token_type.id),t.token=e[hc.parser.index],list_eat(e,token_type.id)}else{if(!n){const t=get_symtab(e[hc.parser.index-2]);t!=void 0&&(hc.symtab.global[t]={id:e[hc.parser.index-2].id,line:e[hc.parser.index-2].line,value:e[hc.parser.index].id})}check_token(e,hc.parser.index,token_type.true)?(hc.symtab.global[get_symtab(e[hc.parser.index-2])].value=1,e[hc.parser.index].type=token_type.number,e[hc.parser.index].id=1,t=new AstNode(token_type.number),t.token=e[hc.parser.index],list_eat(e,token_type.number)):check_token(e,hc.parser.index,token_type.false)?(hc.symtab.global[get_symtab(e[hc.parser.index-2])].value=0,e[hc.parser.index].type=token_type.number,e[hc.parser.index].id=0,t=new AstNode(token_type.number),t.token=e[hc.parser.index],list_eat(e,token_type.number)):(t=new AstNode(token_type.number),t.token=e[hc.parser.index],list_eat(e,token_type.number))}else if(is_mathop(e,hc.parser.index-1)||check_token(e,hc.parser.index-1,token_type.return))if(check_token(e,hc.parser.index,token_type.id)){let s;n?(s=hc.symtab.prototypes[n-1].args.find(t=>t.id===e[hc.parser.index].id),s||(s=hc.symtab.scoped[n-1]?.find(t=>t.id===e[hc.parser.index].id))):s=hc.symtab.prototypes.find(t=>t.id===e[hc.parser.index].id),!s&&check_symtab(e,!0),t=new AstNode(token_type.id),t.token=e[hc.parser.index],list_eat(e,token_type.id)}else t=new AstNode(token_type.number),t.token=e[hc.parser.index],list_eat(e,token_type.number);else if(check_token(e,hc.parser.index,token_type.id)){let s;n?(s=hc.symtab.prototypes[n-1].args.find(t=>t.id===e[hc.parser.index].id),s||(s=hc.symtab.scoped[n-1]?.find(t=>t.id===e[hc.parser.index].id))):s=hc.symtab.prototypes.find(t=>t.id===e[hc.parser.index].id),!s&&check_symtab(e,!0),t=new AstNode(token_type.id),t.token=e[hc.parser.index],list_eat(e,token_type.id)}else check_token(e,hc.parser.index,token_type.semi)?parser_error(e[hc.parser.index]):(t=new AstNode(token_type.comma),t.token=e[hc.parser.index],list_eat(e,token_type.comma));return t.right=parser_parse_exp(e,s,n),t},parser_parse_str_args=e=>{if(check_token(e,hc.parser.index,token_type.semi))return null;let t;if(check_token(e,hc.parser.index,token_type.id)||check_token(e,hc.parser.index,token_type.number))if(check_token(e,hc.parser.index+1,token_type.dot)){let n=[];for(;e[hc.parser.index].type!==token_type.semi;)e[hc.parser.index].type===token_type.id&&n.push(e[hc.parser.index].id),hc.parser.index++;hc.parser.index--,e[hc.parser.index].id=n.join("."),e[hc.parser.index].type=token_type.classExp,t=new AstNode(token_type.classExp),t.token=e[hc.parser.index],list_eat(e,token_type.classExp)}else t=new AstNode(e[hc.parser.index]?.type),t.token=e[hc.parser.index],list_eat(e,e[hc.parser.index].type);else check_token(e,hc.parser.index,token_type.str)?(t=new AstNode(token_type.str),t.token=e[hc.parser.index],list_eat(e,token_type.str)):t=parser_parse_exp(e,!1);return check_token(e,hc.parser.index,token_type.assig)?(t.left=new AstNode(token_type.assig),t.left.token=e[hc.parser.index],list_eat(e,token_type.assig),t.left.right=parser_parse_exp(e,!1)):check_token(e,hc.parser.index,token_type.semi)||(t.left=new AstNode(token_type.comma),t.left.token=e[hc.parser.index],list_eat(e,token_type.comma)),t.right=parser_parse_str_args(e),t},parser_parse_inline_str=e=>{if(check_token(e,hc.parser.index,token_type.semi))return null;let t=new AstNode(token_type.str);return t.token=e[hc.parser.index],list_eat(e,token_type.str),check_token(e,hc.parser.index,token_type.semi)||(t.next=new AstNode(token_type.comma),t.next.token=e[hc.parser.index],list_eat(e,token_type.comma),check_token(e,hc.parser.index,token_type.str)||parser_error(e[hc.parser.index])),t.right=parser_parse_inline_str(e),t},parser_parse_return=(e,n)=>{let t=new AstNode(token_type.return);return t.token=e[hc.parser.index],list_eat(e,token_type.return),t.right=parser_parse_exp(e,!1,n),t.left=new AstNode(token_type.semi),t.left.token=e[hc.parser.index],list_eat(e,token_type.semi),t},parser_parse_str=e=>{let t=new AstNode(token_type.str);return t.token=e[hc.parser.index],list_eat(e,token_type.str),check_token(e,hc.parser.index,token_type.semi)?(t.left=new AstNode(token_type.semi),t.left.token=e[hc.parser.index],list_eat(e,token_type.semi)):(t.left=new AstNode(token_type.comma),t.left.token=e[hc.parser.index],list_eat(e,token_type.comma),check_token(e,hc.parser.index,token_type.str)?(t.right=parser_parse_inline_str(e),t.left.left=new AstNode(token_type.semi),t.left.left.token=e[hc.parser.index],list_eat(e,token_type.semi)):(t.left.right=parser_parse_str_args(e),t.left.left=new AstNode(token_type.semi),t.left.left.token=e[hc.parser.index],list_eat(e,token_type.semi))),t},parser_parse_args=(e=[])=>{if(check_token(e,hc.parser.index,token_type.lparen))return null;let t=new AstNode(e[hc.parser.index]?.type);return t.token=e[hc.parser.index],list_eat_type(e),t.next=new AstNode(token_type.id),t.next.token=e[hc.parser.index],list_eat(e,token_type.id),check_token(e,hc.parser.index,token_type.assig)&&(t.next.next=new AstNode(token_type.assig),t.next.next.token=e[hc.parser.index],list_eat(e,token_type.assig),check_token(e,hc.parser.index,token_type.number)?(t.next.next.next=new AstNode(token_type.number),t.next.next.next.token=e[hc.parser.index],list_eat(e,token_type.number)):(t.next.next.next=new AstNode(token_type.str),t.next.next.next.token=e[hc.parser.index],list_eat(e,token_type.str))),check_token(e,hc.parser.index,token_type.lparen)||(t.next.next=new AstNode(token_type.comma),t.next.next.token=e[hc.parser.index],list_eat(e,token_type.comma)),t.right=parser_parse_args(e),t},parser_parse_call_args=(e,n,o,s)=>{if(hc.symtab.prototypes[n].args.length===s)return null;let t;if(hc.symtab.prototypes[n].args[s]?.id?check_token(e,hc.parser.index,token_type.id)?(check_symtab(e,!0),t=new AstNode(token_type.id),t.token=e[hc.parser.index],list_eat(e,token_type.id),is_mathop(e,hc.parser.index)&&t):check_token(e,hc.parser.index,token_type.number)&&(t=new AstNode(token_type.number),t.token=e[hc.parser.index],list_eat(e,token_type.number)):check_token(e,hc.parser.index,token_type.id)?(check_symtab(e,!0),t=new AstNode(token_type.id),t.token=e[hc.parser.index],list_eat(e,token_type.id)):(t=new AstNode(token_type.number),t.token=e[hc.parser.index],list_eat(e,token_type.number)),s{hc.symtab.prototypes.findIndex(t=>t.id===e[hc.parser.index].id)<0&&check_symtab(e,!0);const n=get_prototype(e[hc.parser.index]);let t=new AstNode(token_type.call);if(t.token=e[hc.parser.index],list_eat(e,token_type.id),hc.symtab.prototypes[n].args.length){const s=hc.symtab.prototypes[n].args.filter(e=>e.id===void 0).length;if(check_token(e,hc.parser.index,token_type.semi)&&!s)return t.left=new AstNode(token_type.semi),t.left.token=e[hc.parser.index],list_eat(e,token_type.semi),t;t.left=new AstNode(token_type.rparen),t.left.token=e[hc.parser.index],list_eat(e,token_type.rparen),check_token(e,hc.parser.index,token_type.str)&&!s&&e[hc.parser.index].id==="*"?(t.left.left=new AstNode(token_type.str),t.left.left.token=e[hc.parser.index],list_eat(e,token_type.str)):hc.symtab.prototypes[n].args.length&&(t.right=parser_parse_call_args(e,n,s,0)),t.left.left=new AstNode(token_type.lparen),t.left.left.token=e[hc.parser.index],list_eat(e,token_type.lparen)}else check_token(e,hc.parser.index,token_type.rparen)&&(t.left=new AstNode(token_type.rparen),t.left.token=e[hc.parser.index],list_eat(e,token_type.rparen),check_token(e,hc.parser.index,token_type.str)&&e[hc.parser.index].id==="*"&&(t.left.next=new AstNode(token_type.str),t.left.next.token=e[hc.parser.index],list_eat(e,token_type.str)),t.left.left=new AstNode(token_type.lparen),t.left.left.token=e[hc.parser.index],list_eat(e,token_type.lparen));return t},parser_parse_inline_vars=(e,o,n)=>{if(check_token(e,hc.parser.index,token_type.semi)&&!check_token(e,hc.parser.index-1,token_type.comma))return null;check_symtab(e,!1);let t=e[hc.parser.index],s=new AstNode(token_type.id);return s.token=e[hc.parser.index],list_eat(e,token_type.id),check_token(e,hc.parser.index,token_type.assig)?(o?hc.symtab.class[o-1].content.push({...t,value:0}):n?hc.symtab.scoped[n-1].push({...t,value:0}):hc.symtab.global.push({...t,value:0}),s.left=parser_parse_exp(e,!1,n),check_token(e,hc.parser.index,token_type.semi)||(s.left.left=new AstNode(token_type.comma),s.left.left.token=e[hc.parser.index],list_eat(e,token_type.comma))):check_token(e,hc.parser.index,token_type.semi)?o?hc.symtab.class[o-1].content.push({...t,value:0}):n?hc.symtab.scoped[n-1].push({...t,value:0}):hc.symtab.global.push({...t,value:0}):(o?hc.symtab.class[o-1].content.push({...t,value:0}):n?hc.symtab.scoped[n-1].push({...t,value:0}):hc.symtab.global.push({...t,value:0}),s.left=new AstNode(token_type.comma),s.left.token=e[hc.parser.index],list_eat(e,token_type.comma)),s.right=parser_parse_inline_vars(e,o,n),s},parser_parse_class_vars=(e,n)=>{if(check_token(e,hc.parser.index,token_type.lbrace))return null;let t=new AstNode(e[hc.parser.index]?.type);return t.token=e[hc.parser.index],list_eat_type(e),t.left=parser_parse_inline_vars(e,n),t.left.left=new AstNode(token_type.semi),t.left.left.token=e[hc.parser.index],list_eat(e,token_type.semi),t.right=parser_parse_class_vars(e,n),t},parser_parse_class_var_exp=e=>{let t=new AstNode(token_type.id);return t.token=e[hc.parser.index],list_eat(e,token_type.id),check_token(e,hc.parser.index,token_type.assig)?t:(t.right=new AstNode(token_type.dot),t.right.token=e[hc.parser.index],list_eat(e,token_type.dot),t.right.right=parser_parse_class_var_exp(e),t)},parser_parse_class_assig=(e,s)=>{let n=hc.parser.index,i=[];for(;e[n].type!==token_type.assig&&e[++n];)e[n].type===token_type.id&&i.push(e[n].id);let o=!1;s&&hc.symtab.prototypes[prototypeIndex]?.args?.findIndex(t=>t.id===e[hc.parser.index].id)<0?parser_error(e[hc.parser.index]):(check_symtab(e,!0),s=hc.symtab.global.findIndex(t=>t.id===e[hc.parser.index].id),o=!0);let t=parser_parse_class_var_exp(e);return t.left=new AstNode(token_type.assig),t.left.token=e[hc.parser.index],list_eat(e,token_type.assig),o?t.left.left=parser_parse_exp(e,!1):t.left.left=parser_parse_exp(e,!1,s),t.left.left.left=new AstNode(token_type.semi),t.left.left.left.token=e[hc.parser.index],list_eat(e,token_type.semi),t},parser_parse_id=(e,n)=>{if(!hc.symtab.types.find(t=>t.id===e[hc.parser.index].id)&&check_token(e,hc.parser.index,token_type.id)){if(is_assingop(e,hc.parser.index+1)){let t=parser_parse_exp(e,!1,n);return t.left=new AstNode(token_type.semi),t.left.token=e[hc.parser.index],list_eat(e,token_type.semi),t}if(check_token(e,hc.parser.index+1,token_type.increment)||check_token(e,hc.parser.index+1,token_type.decrement))return parser_parse_prepostfix(e,!1);if(check_token(e,hc.parser.index+1,token_type.dot))return parser_parse_class_assig(e,n);let t=parser_parse_call(e);return list_eat(e,token_type.semi),t}let t=new AstNode(e[hc.parser.index]?.type);t.token=e[hc.parser.index],list_eat_type(e),n&&hc.symtab.prototypes[n-1]?.args?.find(t=>t.id===e[hc.parser.index].id)||n&&hc.symtab.scoped[n-1]?.find(t=>t.id===e[hc.parser.index].id)?parser_error(e[hc.parser.index]):check_symtab(e,!1);const s=e[hc.parser.index];if(t.left=new AstNode(token_type.id),t.left.token=e[hc.parser.index],list_eat(e,token_type.id),check_token(e,hc.parser.index,token_type.semi))n?(!hc.symtab.scoped[n-1]&&(hc.symtab.scoped[n-1]=[]),hc.symtab.scoped[n-1].push({...s,value:0})):hc.symtab.global.push({...s,value:0}),t.left.left=new AstNode(token_type.semi),t.left.left.token=e[hc.parser.index],list_eat(e,token_type.semi);else if(check_token(e,hc.parser.index,token_type.comma))n?(!hc.symtab.scoped[n-1]&&(hc.symtab.scoped[n-1]=[]),hc.symtab.scoped[n-1].push({...s,value:0})):hc.symtab.global.push({...s,value:0}),t.left.left=new AstNode(token_type.comma),t.left.left.token=e[hc.parser.index],list_eat(e,token_type.comma),t.right=parser_parse_inline_vars(e),t.left.left.left=new AstNode(token_type.semi),t.left.left.left.token=e[hc.parser.index],list_eat(e,token_type.semi);else if(check_token(e,hc.parser.index,token_type.assig))n?(!hc.symtab.scoped[n-1]&&(hc.symtab.scoped[n-1]=[]),hc.symtab.scoped[n-1].push({...s,value:0})):hc.symtab.global.push({...s,value:0}),t.right=parser_parse_exp(e,!1,n),check_token(e,hc.parser.index,token_type.comma)?(t.left.left=new AstNode(token_type.comma),t.left.left.token=e[hc.parser.index],list_eat(e,token_type.comma),t.left.right=parser_parse_inline_vars(e,null,n),t.left.left.left=new AstNode(token_type.semi),t.left.left.left.token=e[hc.parser.index],list_eat(e,token_type.semi)):(t.left.left=new AstNode(token_type.semi),t.left.left.token=e[hc.parser.index],list_eat(e,token_type.semi));else if(!n){const s=e[hc.parser.index-1].id;let o={id:s,type:e[hc.parser.index-2].type,args:void 0,return:void 0};t.left.left=new AstNode(token_type.rparen),t.left.left.token=e[hc.parser.index],list_eat(e,token_type.rparen);let n=hc.parser.index;t.left.left.right=parser_parse_args(e);let i=[];for(;ne.id===s)+1;t.left.left.left=new AstNode(token_type.lparen),t.left.left.left.token=e[hc.parser.index],list_eat(e,token_type.lparen),t.left.left.left.left=new AstNode(token_type.rbrace),t.left.left.left.left.token=e[hc.parser.index],list_eat(e,token_type.rbrace),t.right=parser_parse_block(e,a),t.left.left.left.left.left=new AstNode(token_type.lbrace),t.left.left.left.left.left.token=e[hc.parser.index],list_eat(e,token_type.lbrace)}return t},parser_parse_prepostfix=(e,s,n)=>{let t;if(is_mathop(e,hc.parser.index)){if(check_token(e,hc.parser.index,token_type.increment)?(t=new AstNode(token_type.increment),t.token=e[hc.parser.index],list_eat_math(e)):(t=new AstNode(token_type.decrement),t.token=e[hc.parser.index],list_eat_math(e)),n){let t=hc.symtab.prototypes[n]?.args?.findIndex(t=>t.id===e[hc.parser.index]);t<0&&(t=hc.symtab.scoped[n].findIndex(t=>t.id===e[hc.parser.index]),t<0&&parser_error(e[hc.parser.index]))}else check_symtab(e,!0);t.right=new AstNode(token_type.id),t.right.token=e[hc.parser.index],list_eat(e,token_type.id)}else{if(n){let t=hc.symtab.prototypes[n]?.args?.findIndex(t=>t.id===e[hc.parser.index]);t<0&&(t=hc.symtab.scoped[n].findIndex(t=>t.id===e[hc.parser.index]),t<0&&parser_error(e[hc.parser.index]))}else check_symtab(e,!0);t=new AstNode(token_type.id),t.token=e[hc.parser.index],list_eat(e,token_type.id),check_token(e,hc.parser.index,token_type.increment)?(t.right=new AstNode(token_type.increment),t.right.token=e[hc.parser.index],list_eat_math(e)):(t.right=new AstNode(token_type.decrement),t.right.token=e[hc.parser.index],list_eat_math(e))}return s||(t.left=new AstNode(token_type.semi),t.left.token=e[hc.parser.index],list_eat(e,token_type.semi)),t},parser_parse_ifelse=(e,n)=>{let t=new AstNode(token_type.if);return t.token=e[hc.parser.index],list_eat(e,token_type.if),t.left=new AstNode(token_type.rparen),t.left.token=e[hc.parser.index],list_eat(e,token_type.rparen),check_token(e,hc.parser.index,token_type.number)?(t.left.left=new AstNode(token_type.number),t.left.left.token=e[hc.parser.index],list_eat(e,token_type.number)):check_token(e,hc.parser.index,token_type.true)?(t.left.left=new AstNode(token_type.true),t.left.left.token={id:1,line:e[hc.parser.index].line,type:token_type.number},list_eat(e,token_type.true)):check_token(e,hc.parser.index,token_type.false)?(t.left.left=new AstNode(token_type.number),t.left.left.token={id:0,line:e[hc.parser.index].line,type:token_type.number},list_eat(e,token_type.false)):(!hc.symtab.prototypes[n-1]?.args?.find(t=>t.id===e[hc.parser.index].id)&&!hc.symtab.scoped[n-1]?.find(t=>t.id===e[hc.parser.index].id)&&check_symtab(e,!0),t.left.left=new AstNode(token_type.id),t.left.left.token=e[hc.parser.index],list_eat(e,token_type.id)),t.right=parser_parse_logical_exp(e,n),t.left.left.left=new AstNode(token_type.lparen),t.left.left.left.token=e[hc.parser.index],list_eat(e,token_type.lparen),t.left.left.next=new AstNode(token_type.rbrace),t.left.left.next.token=e[hc.parser.index],list_eat(e,token_type.rbrace),t.left.left.left.right=parser_parse_block(e,n),t.left.left.next.next=new AstNode(token_type.lbrace),t.left.left.next.next.token=e[hc.parser.index],list_eat(e,token_type.lbrace),hc.parser.index{let t=new AstNode(token_type.for);if(t.token=e[hc.parser.index],list_eat(e,token_type.for),t.next=new AstNode(token_type.rparen),t.next.token=e[hc.parser.index],list_eat(e,token_type.rparen),n){let t=hc.symtab.prototypes[n]?.args?.findIndex(t=>t.id===e[hc.parser.index]);t<0&&(t=hc.symtab.scoped[n].findIndex(t=>t.id===e[hc.parser.index]),t<0&&parser_error(e[hc.parser.index]))}else check_symtab(e,!0);if(t.left=new AstNode(token_type.id),t.left.token=e[hc.parser.index],list_eat(e,token_type.id),t.right=parser_parse_exp(e,!1,n),t.left.left=new AstNode(token_type.semi),t.left.left.token=e[hc.parser.index],list_eat(e,token_type.semi),n){let t=hc.symtab.prototypes[n]?.args?.findIndex(t=>t.id===e[hc.parser.index]);t<0&&(t=hc.symtab.scoped[n].findIndex(t=>t.id===e[hc.parser.index]),t<0&&parser_error(e[hc.parser.index]))}else check_symtab(e,!0);return t.left.left.left=new AstNode(token_type.id),t.left.left.left.token=e[hc.parser.index],list_eat(e,token_type.id),t.left.left.left.left=new AstNode(e[hc.parser.index]?.type),t.left.left.left.left.token=e[hc.parser.index],list_eat_logical(e),t.left.left.left.left.left=new AstNode(token_type.number),t.left.left.left.left.left.token=e[hc.parser.index],list_eat(e,token_type.number),t.left.left.left.left.left.left=new AstNode(token_type.semi),t.left.left.left.left.left.left.token=e[hc.parser.index],list_eat(e,token_type.semi),check_token(e,hc.parser.index,token_type.increment)||check_token(e,hc.parser.index,token_type.decrement)||check_token(e,hc.parser.index+1,token_type.decrement)||check_token(e,hc.parser.index+1,token_type.increment)?t.left.left.left.left.left.left.left=parser_parse_prepostfix(e,!0,n):(check_symtab(e,!0),t.left.left.left.left.left.left.next=new AstNode(token_type.id),t.left.left.left.left.left.left.next.token=e[hc.parser.index],list_eat(e,token_type.id),t.left.left.left.left.left.left.left=parser_parse_exp(e,!0,n)),t.left.left.left.left.left.left.left.next=new AstNode(token_type.lparen),t.left.left.left.left.left.left.left.next.token=e[hc.parser.index],list_eat(e,token_type.lparen),t.left.left.left.left.left.left.next=new AstNode(token_type.rbrace),t.left.left.left.left.left.left.next.token=e[hc.parser.index],list_eat(e,token_type.rbrace),t.left.left.left.left.left.left.right=parser_parse_block(e,n),t.left.left.left.left.left.left.next.next=new AstNode(token_type.lbrace),t.left.left.left.left.left.left.next.next.token=e[hc.parser.index],list_eat(e,token_type.lbrace),t},parser_parse_include=e=>{let t=new AstNode(token_type.include);return t.token=e[hc.parser.index],list_eat(e,token_type.include),t.left=new AstNode(token_type.str),t.left.token=e[hc.parser.index],list_eat(e,token_type.str),t},parser_parse_js=e=>{let t=new AstNode(token_type.js);return t.token=e[hc.parser.index],list_eat(e,token_type.js),t.left=new AstNode(token_type.rbrace),t.left.token=e[hc.parser.index],list_eat(e,token_type.rbrace),t.left.left=new AstNode(token_type.jscode),t.left.left.token=e[hc.parser.index],list_eat(e,token_type.jscode),t.left.left.left=new AstNode(token_type.lbrace),t.left.left.left.token=e[hc.parser.index],list_eat(e,token_type.lbrace),t.left.left.left.left=new AstNode(token_type.semi),t.left.left.left.left.token=e[hc.parser.index],list_eat(e,token_type.semi),t},parser_parse_define=e=>{let t=new AstNode(token_type.define);t.token=e[hc.parser.index],list_eat(e,token_type.define);let s=e[hc.parser.index];check_symtab(e,!1),t.left=new AstNode(token_type.id),t.left.token=e[hc.parser.index],list_eat(e,token_type.id);let n;return check_token(e,hc.parser.index,token_type.number)?(n=e[hc.parser.index].id,t.left.left=new AstNode(token_type.number),t.left.left.token=e[hc.parser.index],list_eat(e,token_type.number)):check_token(e,hc.parser.index,token_type.true)?(n=e[hc.parser.index].id,t.left.left=new AstNode(token_type.true),t.left.left.token={id:1,line:e[hc.parser.index].line,type:token_type.number},list_eat(e,token_type.true)):check_token(e,hc.parser.index,token_type.false)?(n=e[hc.parser.index].id,t.left.left=new AstNode(token_type.number),t.left.left.token={id:0,line:e[hc.parser.index].line,type:token_type.number},list_eat(e,token_type.false)):(n=e[hc.parser.index].id,t.left.left=new AstNode(token_type.str),t.left.left.token=e[hc.parser.index],list_eat(e,token_type.str)),hc.symtab.global.push({...s,value:n}),t},parser_parse_block=(e,n)=>{if(check_token(e,hc.parser.index,token_type.lbrace))return null;let t;switch(e[hc.parser.index].type){case token_type.bool:case token_type.i0:case token_type.u0:case token_type.i8:case token_type.u8:case token_type.i16:case token_type.u16:case token_type.i32:case token_type.u32:case token_type.i64:case token_type.u64:case token_type.f64:case token_type.id:t=parser_parse_id(e,n);break;case token_type.increment:case token_type.decrement:t=parser_parse_prepostfix(e,!1);break;case token_type.str:t=parser_parse_str(e);break;case token_type.for:t=parser_parse_for(e,n);break;case token_type.if:t=parser_parse_ifelse(e,n);break;case token_type.return:t=parser_parse_return(e,n);break;case token_type.class:t=parser_parse_class(e);break;default:parser_error(e[hc.parser.index])}return t.next=parser_parse_block(e,n),t},parser_parse=e=>{if(!e[hc.parser.index])return null;let t=new Ast;const n=e[hc.parser.index].type;switch(n){case token_type.bool:case token_type.i0:case token_type.u0:case token_type.i8:case token_type.u8:case token_type.i16:case token_type.u16:case token_type.i32:case token_type.u32:case token_type.i64:case token_type.u64:case token_type.f64:case token_type.id:t.ast=parser_parse_id(e);break;case token_type.define:t.ast=parser_parse_define(e);break;case token_type.js:t.ast=parser_parse_js(e);break;case token_type.include:t.ast=parser_parse_include(e);break;case token_type.increment:case token_type.decrement:t.ast=parser_parse_prepostfix(e,!1);break;case token_type.str:t.ast=parser_parse_str(e);break;case token_type.for:t.ast=parser_parse_for(e);break;case token_type.if:t.ast=parser_parse_ifelse(e);break;case token_type.class:t.ast=parser_parse_class(e);break;default:parser_error(e[hc.parser.index])}return t.next=parser_parse(e),t},parser=e=>e&&parser_parse(e),output_out_math_exp=(n,e)=>{let t=n;for(;e;){switch(e.type){case token_type.div:e.right.token.type===token_type.number?t/=parseInt(e.right.token.id):t/=parseInt(hc.symtab.global[get_symtab(e.right.token)].value);break;case token_type.mul:e.right.token.type===token_type.number?t*=parseInt(e.right.token.id):t*=parseInt(hc.symtab.global[get_symtab(e.right.token)].value);break;case token_type.add:e.right.token.type===token_type.number?t+=parseInt(e.right.token.id):t+=parseInt(hc.symtab.global[get_symtab(e.right.token)].value);break;case token_type.sub:e.right.token.type===token_type.number?t-=parseInt(e.right.token.id):t-=parseInt(hc.symtab.global[get_symtab(e.right.token)].value);break}if(e?.right?.right)e=e.right.right;else break;if(e.type!==token_type.add&&e.type!==token_type.sub&&e.type!==token_type.div&&e.type!==token_type.mul)break}return t},output_out_logical_exp=(s,i,a)=>{let o,e,t={number:0,boolean:!1};if(!i&&s.left.left.token.type===token_type.not?(o=s.right.token,e=s.right.right):i?i&&s.right.token.type===token_type.not?(o=s.right.right.token,e=s.right.right.right):i&&(o=s.right.token,e=s.right.right):(o=s.left.left.token,e=s.right),o.type===token_type.id){let e='';(e=hc.symtab.prototypes[a]?.args?.find(e=>e.id===o.id))?t.number=parseInt(e.value):(e=hc.symtab.scoped[a]?.find(e=>e.id===o.id))?t.number=parseInt(e.value):t.number=parseInt(hc.symtab.global[get_symtab(o)].value)}else t.number=parseInt(o.id);if(e?.token.type===token_type.add||e?.token.type===token_type.sub||e?.token.type===token_type.div||e?.token.type===token_type.mul)for(t.number=output_out_math_exp(t.number,e);e;)if(e=e.right,e&&(e.token.type===token_type.or||e.token.type===token_type.and||e.token.type===token_type.big||e.token.type===token_type.less||e.token.type===token_type.bigequal||e.token.type===token_type.lessequal||e.token.type===token_type.equal))break;t.number&&(t.boolean=!0);let n;for(;e;){if(e.right?.right?.token.type===token_type.add||e.right?.right?.token.type===token_type.sub||e.right?.right?.token.type===token_type.div||e.right?.right?.token.type===token_type.mul||e.right?.right?.token.type===token_type.not){let t;e.right.token.type===token_type.id?t=parseInt(hc.symtab.global[get_symtab(e.right.token)].value):t=parseInt(e.right.token.id),n=output_out_math_exp(t,e.right.right)}else e.right.token.type===token_type.id?n=parseInt(hc.symtab.global[get_symtab(e.right.token)].value):e.right.token.type===token_type.number?n=parseInt(e.right.token.id):e.right.token.type===token_type.not&&(e.right.right.token.type===token_type.id?n=parseInt(hc.symtab.global[get_symtab(e.right.right.token)].value):e.right.right.token.type===token_type.number&&(n=parseInt(e.right.right.token.id)));switch(e.type){case token_type.less:t.boolean=t.numbern,t.number=n;break;case token_type.bigequal:t.boolean=t.number>=n,t.number=n;break;case token_type.or:const s=output_out_logical_exp(e,!0,a);for(t.boolean=!!(t.boolean||s);e;)if(e=e.right,e&&e.token.type===token_type.or)break;break;case token_type.and:t.boolean=t.boolean&&!!n;break;case token_type.not:t.boolean=!t.boolean;break;case token_type.equal:t.boolean=t.number===n;break}if(!e)break;e=e.right.right,e&&e?.token?.type===token_type.not&&(e=e.right)}return t.boolean},output_out_exp=(a,l,m,t,r)=>{if(a?.left?.left?.token.id==="(")return;let o=-1,s=-1,i;if(check_ast_type(a?.token.type,"data_type"))i=a.left.token,t+1&&hc.symtab.prototypes[t]?.args?.find(e=>e.id===i.id)||hc.symtab.scoped[t]?.find(e=>e.id===i.id)?(s=hc.symtab.prototypes[t]?.args.findIndex(e=>e.id===i.id),s<0&&(s=hc.symtab.scoped[t].findIndex(e=>e.id===i.id))):o=get_symtab(a.left.token);else if(a?.token)i=a.token,t+1&&hc.symtab.prototypes[t]?.args.find(e=>e.id===i.id)||hc.symtab.scoped[t]?.find(e=>e.id===i.id)?(s=hc.symtab.prototypes[t]?.args.findIndex(e=>e.id===i.id),s<0&&(s=hc.symtab.scoped[t].findIndex(e=>e.id===i.id))):a.token.type===token_type.id&&(o=get_symtab(a.token));else return;let n,h;t+1&&(h=hc.symtab.prototypes[t]?.args.find(e=>e.id===i.id))?n=parseInt(h.value):o>-1?n=parseInt(hc.symtab.global[o].value):n=parseInt(i.id);let e;m?e=a.left:r?e=a.right:e=a;let d=[],u=null,c=e;for(;e;){switch(e.type){case token_type.dot:if(!u.left&&d.push(u.token),!e.right?.right){for(d.push(e.right.token),n=0;c;){switch(c.type){case token_type.assig:c.left.token.type===token_type.number&&(n=parseInt(c.left.token.id));break;case token_type.div:c.left.token.type===token_type.number&&(n/=parseInt(c.left.token.id));break}c=c.left}r||(o>=0?(Array.isArray(hc.symtab.global[o].value)||(hc.symtab.global[o].value=[]),hc.symtab.global[o].value.push({id:d.map(e=>e.id).join("."),value:n})):hc.symtab.prototypes[t].args[s]?.id===i.id?(Array.isArray(hc.symtab.prototypes[t].args[s].value)||(hc.symtab.prototypes[t].args[s].value=[]),hc.symtab.prototypes[t].args[s].value.push({id:d.map(e=>e.id).join("."),value:n})):(Array.isArray(hc.symtab.scoped[t][s].value)||(hc.symtab.scoped[t][s].value=[]),hc.symtab.scoped[t][s].value.push({id:d.map(e=>e.id).join("."),value:n})))}break;case token_type.div:case token_type.assingdiv:if(e.right.token.type===token_type.number)n/=parseInt(e.right.token.id);else{let s;t+1&&(s=hc.symtab.prototypes[t]?.args.find(t=>t.id===e.right.token.id))||(s=hc.symtab.scoped[t]?.find(t=>t.id===e.right.token.id))?e.right.token.type===token_type.classExp?n/=s.value.find(e=>e.id===a.join("."))?.value:n/=parseInt(s.value):(s=hc.symtab.prototypes.find(t=>t.id===e.right.token.id))?(output_out_procedures(e.right,l),n/=s.return):n/=parseInt(hc.symtab.global[get_symtab(e.right.token)].value)}r||(o>=0?hc.symtab.global[o].value=n:hc.symtab.prototypes[t].args[s]?.id===i.id?hc.symtab.prototypes[t].args[s].value=n:hc.symtab.scoped[t][s].value=n);break;case token_type.mul:case token_type.assingmul:if(e.right.token.type===token_type.number)n*=parseInt(e.right.token.id);else{let s;t+1&&(s=hc.symtab.prototypes[t]?.args.find(t=>t.id===e.right.token.id))||(s=hc.symtab.scoped[t]?.find(t=>t.id===e.right.token.id))?e.right.token.type===token_type.classExp?n*=s.value.find(e=>e.id===a.join("."))?.value:n*=parseInt(s.value):(s=hc.symtab.prototypes.find(t=>t.id===e.right.token.id))?(output_out_procedures(e.right,l),n*=s.return):n*=parseInt(hc.symtab.global[get_symtab(e.right.token)].value)}r||(o>=0?hc.symtab.global[o].value=n:hc.symtab.prototypes[t].args[s]?.id===i.id?hc.symtab.prototypes[t].args[s].value=n:hc.symtab.scoped[t][s].value=n);break;case token_type.assig:let a='';if(e.right.token.type===token_type.classExp&&(a=e.right.token.id.split("."),e.right.token.id=a[0],a.shift()),e.right.token.type===token_type.number)n=parseInt(e.right.token.id);else{let s;t+1&&(s=hc.symtab.prototypes[t]?.args.find(t=>t.id===e.right.token.id))||(s=hc.symtab.scoped[t]?.find(t=>t.id===e.right.token.id))?e.right.token.type===token_type.classExp?n=s.value.find(e=>e.id===a.join("."))?.value:n=parseInt(s.value):(s=hc.symtab.prototypes.find(t=>t.id===e.right.token.id))?(output_out_procedures(e.right,l),n=s.return):e.right.token.type===token_type.classExp?n=parseInt(hc.symtab.global[get_symtab(e.right.token)].value.find(e=>e.id===a.join("."))?.value):n=parseInt(hc.symtab.global[get_symtab(e.right.token)].value)}r||(o>=0?hc.symtab.global[o].value=n:hc.symtab.prototypes[t].args[s]?.id===i.id?hc.symtab.prototypes[t].args[s].value=n:hc.symtab.scoped[t][s].value=n);break;case token_type.assingsum:case token_type.add:if(e.right.token.type===token_type.number)n+=parseInt(e.right.token.id);else{let s;t+1&&(s=hc.symtab.prototypes[t]?.args.find(t=>t.id===e.right.token.id))||(s=hc.symtab.scoped[t]?.find(t=>t.id===e.right.token.id))?e.right.token.type===token_type.classExp?n+=s.value.find(e=>e.id===a.join("."))?.value:n+=parseInt(s.value):(s=hc.symtab.prototypes.find(t=>t.id===e.right.token.id))?(output_out_procedures(e.right,l),n+=s.return):n+=parseInt(hc.symtab.global[get_symtab(e.right.token)].value)}r||(o>=0?hc.symtab.global[o].value=n:hc.symtab.prototypes[t].args[s]?.id===i.id?hc.symtab.prototypes[t].args[s].value=n:hc.symtab.scoped[t][s].value=n);break;case token_type.assingsub:case token_type.sub:if(e.right.token.type===token_type.number)n-=parseInt(e.right.token.id);else{let s;t+1&&(s=hc.symtab.prototypes[t]?.args.find(t=>t.id===e.right.token.id))||(s=hc.symtab.scoped[t]?.find(t=>t.id===e.right.token.id))?e.right.token.type===token_type.classExp?n-=s.value.find(e=>e.id===a.join("."))?.value:n-=parseInt(s.value):(s=hc.symtab.prototypes.find(t=>t.id===e.right.token.id))?(output_out_procedures(e.right,l),n-=s.return):n-=parseInt(hc.symtab.global[get_symtab(e.right.token)].value)}r||(o>=0?hc.symtab.global[o].value=n:hc.symtab.prototypes[t].args[s]?.id===i.id?hc.symtab.prototypes[t].args[s].value=n:hc.symtab.scoped[t][s].value=n);break;case token_type.semi:return e}u=e,e=e.right}return check_ast_type(a?.left?.right?.token.type,"id")?output_out_exp(a.left.right,l,!0,t,r):check_ast_type(a?.right?.token.type,"id")&&output_out_exp(a.right,l,!0,t,r),r&&(hc.symtab.prototypes[t].return=n),e},output_out_get_ast_check=(e,t)=>e.left.token.id===t.id,output_out_get_ast=(e,t)=>{if(!e)return null;if(check_ast_type(e.ast.type,"data_type")){let n=output_out_get_ast_check(e.ast,t);if(n)return e.ast}return output_out_get_ast(e.next,t)},output_out_block=(e,t,n)=>{if(!e)return;switch(e.type){case token_type.bool:case token_type.i0:case token_type.u0:case token_type.i8:case token_type.u8:case token_type.i16:case token_type.u16:case token_type.i32:case token_type.u32:case token_type.i64:case token_type.u64:case token_type.f64:case token_type.id:e.left&&output_out_exp(e,t,!1,n);break;case token_type.if:const s=output_out_ifelse(e,t,n);if(s==="{RETURN}")return s;break;case token_type.for:const o=output_out_for(e,t,n);if(o==="{RETURN}")return o;break;case token_type.str:printf(e,n);break;case token_type.call:output_out_procedures(e,t);break;case token_type.return:return output_out_return(e,t,n),"{RETURN}";default:break}return output_out_block(e.right,t,n),output_out_block(e.next,t,n)},output_out_ifelse=(e,t,n)=>{const i=output_out_logical_exp(e,!1,n);let s;e.left?.left?.left?.left&&(s=e.left.left.left.left.right);let o='';return i?o=output_out_block(e.left.left.left.right,t,n):s&&(o=output_out_block(s,t,n)),!i&&e?.left?.left?.left?.next?.token.type===token_type.if&&output_out_ifelse(e.left.left.left.next,t),o},output_out_js=e=>{eval(e.left.left.token.id)},output_out_for=(t,a,n)=>{let e=get_symtab(t.left.token);e||(e=hc.symtab.prototypes[n]?.args?.findIndex(e=>e.id===t.left.token.id),e<0&&(e=hc.symtab.scoped[n]?.findIndex(e=>e.id===t.left.token.id)));const r=parseInt(t.right.right.token.id),l=t.left.left.left.left.token,c=parseInt(t.left.left.left.left.left.token.id);let i=t.left.left.left.left.left.left.left;i.type===token_type.id&&(i=t.left.left.left.left.left.left.left.right);let s;check_ast_type(i.type,"assignment_operator")?s=parseInt(i.right.token.id):s=1;let o='';switch(l.type){case token_type.less:for(let i=r;ic;i+=s){if(o=output_out_block(t.left.left.left.left.left.left.right,a,n),o==="{RETURN}")return o;hc.symtab.global[e]?.id===t.left.token.id?hc.symtab.global[e].value=parseInt(hc.symtab.global[e].value)+s:hc.symtab.prototypes[n]?.args[e]?.id===t.left.token.id?hc.symtab.prototypes[n].args[e].value=parseInt(hc.symtab.prototypes[n].args[e].value)+s:hc.symtab.scoped[n][e].value=parseInt(hc.symtab.scoped[n][e].value)+s}break}return o},output_out_procedures=(s,o)=>{const i=output_out_get_ast(o,s.token),t=get_prototype(i?.left?.token);let e=s.right,n=0;for(;hc.symtab.prototypes[t].args[n]&&e;)e.token.type===token_type.id?hc.symtab.prototypes[t].args[n].value=hc.symtab.global[get_symtab(e.token)].value:e.token.type===token_type.number&&(hc.symtab.prototypes[t].args[n].value=e.token.id),n++,e=e.right;output_out_block(i.right,o,t)},output_out_return=(e,t,n)=>{output_out_exp(e.right,t,!1,n,!0)},output=async t=>{if(!t)return hc.files.stdout;let e=t;do{switch(e.ast.type){case token_type.bool:case token_type.i0:case token_type.u0:case token_type.i8:case token_type.u8:case token_type.i16:case token_type.u16:case token_type.i32:case token_type.u32:case token_type.i64:case token_type.u64:case token_type.f64:case token_type.id:output_out_exp(e.ast,t);break;case token_type.if:output_out_ifelse(e.ast,t);break;case token_type.for:output_out_for(e.ast,t);break;case token_type.js:output_out_js(e.ast);break;case token_type.str:let n=e.ast;do printf(n),n=n.right;while(n)break;case token_type.call:output_out_procedures(e.ast,t);break;default:break}e=e.next}while(e)return hc.files.stdout},printf=(t,n)=>{let e=t.token.id;if(t.token.id.includes("%")){let s;if(n+1&&(s=hc.symtab.prototypes[n]?.args.find(e=>e.id===t.left.right.token.id))||(s=hc.symtab.scoped[n]?.find(e=>e.id===t.left.right.token.id)))e=e.replace(/%d/g,s.value);else if(t.left.right.token.type===token_type.number)e=e.replace(/%d/g,t.left.right.token.id);else if(t.left.right.token.type===token_type.classExp){let n=t.left.right.token.id.split("."),s=t.left.right.token;s.id=n.shift(),e=e.replace(/%d/g,hc.symtab.global[get_symtab(s)].value.find(e=>e.id===n.join("."))?.value)}else e=e.replace(/%d/g,hc.symtab.global[get_symtab(t.left.right.token)].value)}e=e.replace(/undefined/g,"NULL"),e=e.replace(/null/g,"NULL"),hc.files.stdout+=e.replace(/\\n|\\t/g,e=>{switch(e){case"\\r":case"\\n":return"\n";case"\\t":return" ";default:return e}})} -------------------------------------------------------------------------------- /holyc-interpreter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Copyright Leonardo Z. Nunes 2022 3 | * @license MIT 4 | * @fileoverview JS HolyC Interpreter 5 | * @version 0.0.0 6 | */ 7 | /** 8 | * Run interpreter in web front-end 9 | * @requires 10 | * @description 11 | * Make sure you have a input tag with "stdin" id in your HTML DOM; 12 | * You need to import this procedure in your BOM: 13 | * 14 | * 18 | * 19 | * You can call this procedure for run the stdin with a button, for example: 20 | * 21 | * 22 | * 23 | * After run the stdout will appear in the alert box in your site, 24 | * so make sure you have it enabled in your browser; 25 | * For local run you need a HTTP server: 26 | * 27 | * bun dev 28 | * 29 | */ 30 | let hc = { 31 | modes: { 32 | HolyNode: false 33 | }, 34 | files: { 35 | stdin: '', 36 | stdout: '', 37 | stderr: '', 38 | }, 39 | lexer: { 40 | char: '', 41 | line: 1, 42 | index: 0, 43 | }, 44 | parser: { 45 | index: 0, 46 | }, 47 | symtab: { 48 | idle: false, 49 | global: [], 50 | scoped: [], 51 | prototypes: [], 52 | class: [], 53 | types: [], 54 | }, 55 | } 56 | 57 | const stderr = eval('(value) =>' + (hc.modes.HolyNode ? undefined : '(document.getElementById("stdout/stderr").value = value) && (document.getElementById("stdout/stderr").style.color = "red")')) 58 | const stdin = () => eval(hc.modes.HolyNode ? undefined : 'document.getElementById("stdin")'); 59 | const stdout = (value) => (document.getElementById("stdout/stderr").value = value) 60 | && (document.getElementById("stdout/stderr").style.color = "black"); 61 | 62 | export const holyc_web_run = async () => stdout(await output(parser(await lexer(init_hc())))); 63 | 64 | export const holy_node_idle = async (stdin) => 65 | (hc.symtab.idle = hc.modes.HolyNode = true) && await output(parser(await lexer(init_hc(stdin)))); 66 | 67 | export const holy_node_script = async (stdin) => 68 | (hc.modes.HolyNode = true) && await output(parser(await lexer(init_hc(stdin)))); 69 | 70 | export const holy_script = async (stdin) => document.getElementById("stdout/stderr").innerText = await output(parser(await lexer(init_hc(stdin)))); 71 | 72 | /** 73 | * AST Node 74 | * @constructor 75 | * @param {{ 76 | * token: AstNode, 77 | * next: AstNode, 78 | * left: AstNode, 79 | * right: AstNode, 80 | * type: token_type, 81 | * }} 82 | */ 83 | class AstNode { 84 | token; 85 | next; 86 | left; 87 | right; 88 | constructor(type) { 89 | this.type = type; 90 | } 91 | } 92 | 93 | /** 94 | * AST 95 | * @constructor 96 | * @param {{ 97 | * next: AstNode, 98 | * ast: AstNode, 99 | * }} 100 | */ 101 | class Ast { 102 | next; 103 | ast; 104 | } 105 | 106 | class Token { 107 | constructor(id, type, line) { 108 | this.id = id; 109 | this.type = type; 110 | this.line = line; 111 | } 112 | } 113 | 114 | const token_type = { 115 | number: 1, 116 | str: 2, 117 | id: 3, 118 | add: 4, 119 | sub: 5, 120 | div: 6, 121 | mul: 7, 122 | semi: 8, 123 | lbrace: 9, 124 | rbrace: 10, 125 | i0: 11, 126 | u0: 12, 127 | i8: 13, 128 | u8: 14, 129 | i16: 15, 130 | u16: 16, 131 | i32: 17, 132 | u32: 18, 133 | i64: 19, 134 | u64: 20, 135 | f64: 21, 136 | rparen: 22, 137 | lparen: 23, 138 | call: 24, 139 | comma: 25, 140 | assig: 26, 141 | less: 27, 142 | big: 28, 143 | for: 29, 144 | increment: 30, 145 | decrement: 31, 146 | assingsum: 32, 147 | assingsub: 33, 148 | assingdiv: 34, 149 | assingmul: 35, 150 | if: 36, 151 | or: 37, 152 | and: 38, 153 | not: 39, 154 | else: 40, 155 | true: 41, 156 | false: 42, 157 | equal: 43, 158 | return: 44, 159 | bigequal: 45, 160 | lessequal: 46, 161 | bool: 47, 162 | class: 48, 163 | dot: 49, 164 | classExp: 50, 165 | define: 51, 166 | include: 52, 167 | js: 53, 168 | jscode: 54, 169 | }; 170 | 171 | const token_cases = [ 172 | { 173 | char: '/', render: (hc) => { 174 | if (hc.files.stdin[hc.lexer.index] === '/') { 175 | while (hc.files.stdin[hc.lexer.index++] 176 | && hc.files.stdin[hc.lexer.index] !== '\n') { }; 177 | return "{comment}"; 178 | } else if (hc.files.stdin[hc.lexer.index] === '=') { 179 | hc.lexer.index++ 180 | return new Token('/=', token_type.assingdiv, hc.lexer.line) 181 | } else { 182 | return new Token('/', token_type.div, hc.lexer.line) 183 | } 184 | } 185 | }, 186 | { 187 | char: "'", render: (hc) => lex_simple_quote_string(hc) 188 | }, 189 | { 190 | char: '"', render: (hc) => lex_string(hc) 191 | }, 192 | { 193 | char: "+", render: (hc) => hc.files.stdin[hc.lexer.index] === "+" 194 | && hc.lexer.index++ 195 | && (new Token('++', token_type.increment, hc.lexer.line)) 196 | || hc.files.stdin[hc.lexer.index] === "=" 197 | && hc.lexer.index++ 198 | && (new Token('+=', token_type.assingsum, hc.lexer.line)) 199 | || (new Token('+', token_type.add, hc.lexer.line)) 200 | }, 201 | { 202 | char: "-", render: (hc) => hc.files.stdin[hc.lexer.index] === "-" 203 | && hc.lexer.index++ 204 | && (new Token('--', token_type.increment, hc.lexer.decrement)) 205 | || hc.files.stdin[hc.lexer.index] === "=" 206 | && hc.lexer.index++ 207 | && (new Token('-=', token_type.assingsub, hc.lexer.line)) 208 | || (new Token('-', token_type.sub, hc.lexer.line)) 209 | }, 210 | { 211 | char: "*", render: (hc) => hc.files.stdin[hc.lexer.index] === "=" 212 | && hc.lexer.index++ 213 | && (new Token('*=', token_type.assingmul, hc.lexer.line)) 214 | || (new Token('*', token_type.mul, hc.lexer.line)) 215 | }, 216 | { 217 | char: ";", render: (hc) => new Token(';', token_type.semi, hc.lexer.line) 218 | }, 219 | { 220 | char: ".", render: (hc) => new Token('.', token_type.dot, hc.lexer.line) 221 | }, 222 | { 223 | char: "{", render: (hc) => new Token('{', token_type.rbrace, hc.lexer.line) 224 | }, 225 | { 226 | char: "}", render: (hc) => new Token('}', token_type.lbrace, hc.lexer.line) 227 | }, 228 | { 229 | char: "(", render: (hc) => new Token('(', token_type.rparen, hc.lexer.line) 230 | }, 231 | { 232 | char: ")", render: (hc) => new Token(')', token_type.lparen, hc.lexer.line) 233 | }, 234 | { 235 | char: ",", render: (hc) => new Token(',', token_type.comma, hc.lexer.line) 236 | }, 237 | { 238 | char: "!", render: (hc) => new Token('!', token_type.not, hc.lexer.line) 239 | }, 240 | { 241 | char: "=", render: (hc) => hc.files.stdin[hc.lexer.index] === "=" 242 | && hc.lexer.index++ 243 | && (new Token('==', token_type.equal, hc.lexer.line)) 244 | || (new Token('=', token_type.assig, hc.lexer.line)) 245 | }, 246 | { 247 | char: "<", render: (hc) => hc.files.stdin[hc.lexer.index] === "=" 248 | && hc.lexer.index++ 249 | && (new Token('<=', token_type.lessequal, hc.lexer.line)) 250 | || (new Token('<', token_type.less, hc.lexer.line)) 251 | }, 252 | { 253 | char: ">", render: (hc) => hc.files.stdin[hc.lexer.index] === "=" 254 | && hc.lexer.index++ 255 | && (new Token('>=', token_type.bigequal, hc.lexer.line)) 256 | || (new Token('>', token_type.big, hc.lexer.line)) 257 | }, 258 | { 259 | char: "&", render: (hc) => hc.files.stdin[hc.lexer.index] === "&" 260 | && hc.lexer.index++ 261 | && (new Token('&&', token_type.and, hc.lexer.line)) 262 | }, 263 | { 264 | char: "|", render: (hc) => hc.files.stdin[hc.lexer.index] === "|" 265 | && hc.lexer.index++ 266 | && (new Token('||', token_type.or, hc.lexer.line)) 267 | }, 268 | ] 269 | 270 | const token_keywords = [ 271 | { id: "for", type: token_type.for }, 272 | { id: "class", type: token_type.class }, 273 | { id: "if", type: token_type.if }, 274 | { id: "else", type: token_type.else }, 275 | { id: "return", type: token_type.return }, 276 | { id: "TRUE", type: token_type.true }, 277 | { id: "FALSE", type: token_type.false }, 278 | { id: "Bool", type: token_type.bool }, 279 | { id: "I0", type: token_type.i0 }, 280 | { id: "U0", type: token_type.u0 }, 281 | { id: "I8", type: token_type.i8 }, 282 | { id: "U8", type: token_type.u8 }, 283 | { id: "I16", type: token_type.i16 }, 284 | { id: "U16", type: token_type.u16 }, 285 | { id: "I32", type: token_type.i32 }, 286 | { id: "U32", type: token_type.u32 }, 287 | { id: "I64", type: token_type.i64 }, 288 | { id: "U64", type: token_type.u64 }, 289 | { id: "F64", type: token_type.f64 }, 290 | { id: "#define", type: token_type.define }, 291 | { id: "#include", type: token_type.include }, 292 | { id: "js", type: token_type.js }, 293 | ] 294 | 295 | const is_alpha = (char) => { 296 | return /^[A-Z0-9_#]$/i.test(char); 297 | }; 298 | 299 | const is_number = (char) => { 300 | return /^[0-9]$/.test(char); 301 | }; 302 | 303 | const is_ignored = (char) => { 304 | char === '\n' && hc.lexer.line++; 305 | return /[ \n\t]/.test(char); 306 | } 307 | 308 | const lex_include = async (hc) => { 309 | const token = lex_alpha(hc) 310 | if (token.id !== '#include') return token 311 | 312 | hc.lexer.index += 2 313 | hc.files.stdin = await fetch(lex_string(hc).id) 314 | .then(response => response.text()) 315 | .then(text => text) + hc.files.stdin.slice(hc.lexer.index, hc.files.stdin.length); 316 | hc.lexer.index = 0; 317 | 318 | return "{include}" 319 | } 320 | 321 | const lex_alpha = (hc) => { 322 | let id = ''; 323 | do { 324 | id += hc.lexer.char 325 | } 326 | while (is_alpha(hc.lexer.char = hc.files.stdin[hc.lexer.index++])); 327 | hc.lexer.index--; 328 | return new Token(id, token_keywords.find(e => e.id === id)?.type || token_type.id, hc.lexer.line); 329 | } 330 | 331 | const lex_number = (hc) => { 332 | let id = ''; 333 | do { 334 | id += hc.lexer.char 335 | } 336 | while (is_number(hc.lexer.char = hc.files.stdin[hc.lexer.index++])); 337 | hc.lexer.index--; 338 | return new Token(id, token_type.number, hc.lexer.line); 339 | } 340 | 341 | const lex_simple_quote_string = (hc) => { 342 | hc.lexer.char = hc.files.stdin[hc.lexer.index++] 343 | let id = ''; 344 | do { 345 | !hc.lexer.char && lexer_error({ id: "EOF", line: hc.lexer.line }) 346 | id += hc.lexer.char 347 | } 348 | while ((hc.lexer.char = hc.files.stdin[hc.lexer.index++]) !== "'"); 349 | return new Token(id, token_type.str, hc.lexer.line); 350 | } 351 | 352 | const lex_string = (hc) => { 353 | hc.lexer.char = hc.files.stdin[hc.lexer.index++] 354 | let id = ''; 355 | do { 356 | !hc.lexer.char && lexer_error({ id: "EOF", line: hc.lexer.line }) 357 | id += hc.lexer.char 358 | } 359 | while ((hc.lexer.char = hc.files.stdin[hc.lexer.index++]) !== '"'); 360 | return new Token(id, token_type.str, hc.lexer.line); 361 | } 362 | 363 | const lexer_lex = async (hc) => { 364 | while ((hc.lexer.char = hc.files.stdin[hc.lexer.index++]) 365 | && hc.lexer.char) { 366 | let token = '' 367 | if (is_ignored(hc.lexer.char)) continue; 368 | if (hc.lexer.char === '#') { 369 | token = await lex_include(hc); 370 | if (token === '{include}') continue 371 | else return token 372 | } 373 | if (is_number(hc.lexer.char)) return lex_number(hc); 374 | if (is_alpha(hc.lexer.char)) return lex_alpha(hc); 375 | token = token_cases.find(e => e.char === hc.lexer.char)?.render(hc) 376 | if (token === "{comment}") continue; 377 | else if (token) return token; 378 | else lexer_error({ id: hc.lexer.char, line: hc.lexer.line }) 379 | } 380 | } 381 | 382 | const init_hc = (inpStdin) => { 383 | stderr && stderr('') 384 | stdin()?.value && (hc.files.stdin = stdin().value) || (hc.files.stdin = inpStdin) 385 | 386 | hc.files.stdout = ''; 387 | hc.files.stderr = ''; 388 | hc.lexer.char = ''; 389 | hc.lexer.index = 0; 390 | hc.lexer.line = 1; 391 | hc.parser.index = 0; 392 | !hc.symtab.idle && (hc.symtab.types = []); 393 | !hc.symtab.idle && (hc.symtab.global = []); 394 | !hc.symtab.idle && (hc.symtab.prototypes = []); 395 | !hc.symtab.idle && (hc.symtab.class = []); 396 | !hc.symtab.idle && (hc.symtab.scoped = []); 397 | 398 | if (!hc.files.stdin) { 399 | hc.files.stderr += "Compile failure\nLexer: nothing to compile\n"; 400 | try { 401 | throw stderr(hc.files.stderr); 402 | } catch { 403 | throw new Error(hc.files.stderr); 404 | } 405 | } 406 | 407 | return hc; 408 | } 409 | 410 | // TODO: create a one step lexer 411 | const lexer = async (hc) => { 412 | let token_list = []; 413 | 414 | while (1) { 415 | const token = await lexer_lex(hc) 416 | if (!token) break; 417 | token_list.push(token) 418 | 419 | if (token.type === token_type.js && hc.files.stdin[hc.lexer.index + 1] === '{') { 420 | hc.lexer.index++ 421 | token_list.push(new Token('{', token_type.rbrace, hc.lexer.line)) 422 | hc.lexer.index++ 423 | 424 | hc.lexer.char = hc.files.stdin[hc.lexer.index] 425 | let id = ''; 426 | do { 427 | hc.lexer.char = hc.files.stdin[hc.lexer.index++] 428 | !hc.lexer.char && lexer_error({ id: "EOF", line: hc.lexer.line }) 429 | id += hc.lexer.char 430 | } 431 | while (hc?.files?.stdin.substring(hc.lexer.index, hc.lexer.index + 2) !== '};'); 432 | 433 | token_list.push(new Token(id, token_type.jscode, hc.lexer.line)) 434 | token_list.push(new Token('}', token_type.lbrace, hc.lexer.line)) 435 | token_list.push(new Token(';', token_type.semi, hc.lexer.line)) 436 | 437 | hc.lexer.index += 2 438 | } 439 | } 440 | 441 | return token_list; 442 | } 443 | 444 | /** 445 | * get index of a symbol in symbol table 446 | */ 447 | const get_symtab = (token) => { 448 | for (let i = 0; i < hc.symtab.global.length; ++i) { 449 | if (hc.symtab.global[i].id === token.id) return i; 450 | } 451 | }; 452 | 453 | /** 454 | * get index of prototype 455 | */ 456 | const get_prototype = (token) => { 457 | for (let i = 0; i < hc.symtab.prototypes.length; ++i) { 458 | if (hc.symtab.prototypes[i].id === token.id) return i; 459 | } 460 | }; 461 | 462 | /** 463 | * get index of class 464 | */ 465 | const get_class = (token) => { 466 | for (let i = 0; i < hc.symtab.class.length; ++i) { 467 | if (hc.symtab.class[i].id === token.id) return i; 468 | } 469 | return -1; 470 | }; 471 | 472 | /** 473 | * throw lexer error 474 | * @arg {object} token 475 | */ 476 | const lexer_error = (token) => { 477 | hc.files.stderr = `Compile failure\nLexer: '${token.id}' unexpected token in line ${token.line}\n`; 478 | try { 479 | throw stderr(hc.files.stderr) 480 | } catch { 481 | throw new Error(hc.files.stderr) 482 | } 483 | }; 484 | 485 | /** 486 | * throw parser error 487 | * @arg {object} token 488 | */ 489 | const internal_error = (err) => { 490 | hc.files.stderr = `Compile failure\nInternal error: '${err}'`; 491 | try { 492 | throw stderr(hc.files.stderr) 493 | } catch { 494 | throw new Error(hc.files.stderr) 495 | } 496 | }; 497 | 498 | /** 499 | * throw parser error 500 | * @arg {object} token 501 | */ 502 | const parser_error = (token) => { 503 | hc.files.stderr = `Compile failure\nParser: '${token.id}' unexpected token in line ${token.line}\n`; 504 | try { 505 | throw stderr(hc.files.stderr) 506 | } catch { 507 | throw new Error(hc.files.stderr) 508 | } 509 | }; 510 | 511 | /** 512 | * check if symbol is in symbol table or not 513 | * @arg {array} tokenList 514 | * @arg {boolean} isin 515 | */ 516 | const check_symtab = (tokenList, isin) => { 517 | if (symtab_contain(tokenList[hc.parser.index]) !== isin) { 518 | parser_error(tokenList[hc.parser.index]); 519 | } 520 | }; 521 | 522 | /** 523 | * throw parser error 524 | * @arg {array} tokenList 525 | * @arg {number} expectedType 526 | */ 527 | const list_eat = (tokenList, expectedType) => { 528 | try { 529 | if (tokenList[hc.parser.index].type !== expectedType) { 530 | throw new Error(); 531 | } 532 | hc.parser.index++; 533 | } catch { 534 | parser_error(tokenList[hc.parser.index] ? tokenList[hc.parser.index] : tokenList[hc.parser.index - 1]); 535 | } 536 | }; 537 | 538 | /** 539 | * check if token is in symbol table 540 | * @arg {object} token 541 | */ 542 | const symtab_contain = (token) => { 543 | if (hc.symtab.global.filter((e) => e.id === token.id).length) { 544 | return true; 545 | } 546 | return false; 547 | }; 548 | 549 | /** 550 | * check token type 551 | * @arg {array} tokenList 552 | * @arg {number} index 553 | * @arg {number} expectedType 554 | */ 555 | const check_token = (tokenList, index, expectedType) => { 556 | try { 557 | return tokenList[index].type === expectedType ? true : false; 558 | } catch { 559 | parser_error(tokenList[index - 1]); 560 | } 561 | }; 562 | 563 | /** 564 | * check ast type 565 | * @arg {number} type 566 | * @arg {string} expectedType 567 | */ 568 | const check_ast_type = (type, expectedType) => { 569 | switch (expectedType) { 570 | case "id": 571 | return type === token_type.id ? true : false; 572 | case "data_type": 573 | return type === token_type.i0 || 574 | type === token_type.u0 || 575 | type === token_type.i8 || 576 | type === token_type.u8 || 577 | type === token_type.i16 || 578 | type === token_type.u16 || 579 | type === token_type.i32 || 580 | type === token_type.u32 || 581 | type === token_type.i64 || 582 | type === token_type.u64 || 583 | type === token_type.f64 || 584 | type === token_type.bool 585 | ? true 586 | : false; 587 | case "assignment_operator": 588 | return type === token_type.assingdiv || 589 | type === token_type.assingmul || 590 | type === token_type.assingsub || 591 | type === token_type.assingsum 592 | ? true 593 | : false; 594 | } 595 | }; 596 | 597 | /** 598 | * check if token type is a data type 599 | * @arg {array} tokenList 600 | * @arg {number} index 601 | */ 602 | const is_dtype = (tokenList, index) => { 603 | try { 604 | let type = tokenList[index].type; 605 | return type === token_type.i0 || 606 | type === token_type.u0 || 607 | type === token_type.i8 || 608 | type === token_type.u8 || 609 | type === token_type.i16 || 610 | type === token_type.u16 || 611 | type === token_type.i32 || 612 | type === token_type.u32 || 613 | type === token_type.i64 || 614 | type === token_type.u64 || 615 | type === token_type.f64 || 616 | type === token_type.bool || 617 | hc.symtab.types.findIndex(e => e.id === tokenList[index].id) > -1 618 | ? true 619 | : false; 620 | 621 | } catch { 622 | parser_error(tokenList[index - 1]); 623 | } 624 | }; 625 | 626 | /** 627 | * check if token type is a logical operator 628 | * @arg {array} tokenList 629 | * @arg {number} index 630 | */ 631 | const is_logicalop = (tokenList, index) => { 632 | try { 633 | let type = tokenList[index].type; 634 | return type === token_type.big || 635 | type === token_type.less || 636 | type === token_type.or || 637 | type === token_type.and || 638 | type === token_type.not || 639 | type === token_type.equal || 640 | type === token_type.bigequal || 641 | type === token_type.lessequal 642 | ? true 643 | : false; 644 | } catch { 645 | parser_error(tokenList[index - 1]); 646 | } 647 | }; 648 | 649 | /** 650 | * check if token type is a mathematical operator 651 | * @arg {array} tokenList 652 | * @arg {number} index 653 | */ 654 | const is_mathop = (tokenList, index) => { 655 | try { 656 | let type = tokenList[index].type; 657 | return type === token_type.add || 658 | type === token_type.sub || 659 | type === token_type.div || 660 | type === token_type.mul || 661 | type === token_type.increment || 662 | type === token_type.decrement 663 | ? true 664 | : false; 665 | } catch { 666 | parser_error(tokenList[index - 1]); 667 | } 668 | }; 669 | 670 | /** 671 | * check if token type is a compound assignment operator 672 | * @arg {array} tokenList 673 | * @arg {number} index 674 | */ 675 | const is_assingop = (tokenList, index) => { 676 | try { 677 | let type = tokenList[index].type; 678 | return type === token_type.assingdiv || 679 | type === token_type.assingmul || 680 | type === token_type.assingsub || 681 | type === token_type.assingsum || 682 | type === token_type.assig 683 | ? true 684 | : false; 685 | } catch { 686 | parser_error(tokenList[index - 1]); 687 | } 688 | }; 689 | 690 | /** 691 | * increment hc.parser.index to next node in token list if token type is data type 692 | * @arg {array} tokenList 693 | */ 694 | const list_eat_type = (tokenList) => { 695 | is_dtype(tokenList, hc.parser.index) ? hc.parser.index++ : parser_error(tokenList[hc.parser.index]); 696 | }; 697 | 698 | /** 699 | * increment hc.parser.index to next node in token list if token type is logical operator 700 | * @arg {array} tokenList 701 | */ 702 | const list_eat_logical = (tokenList) => { 703 | is_logicalop(tokenList, hc.parser.index) ? hc.parser.index++ : parser_error(tokenList[hc.parser.index]); 704 | }; 705 | 706 | /** 707 | * increment hc.parser.index to next node in token list if token type is mathematical operator 708 | * @arg {array} tokenList 709 | */ 710 | const list_eat_math = (tokenList) => { 711 | is_mathop(tokenList, hc.parser.index) ? hc.parser.index++ : parser_error(tokenList[hc.parser.index]); 712 | }; 713 | 714 | /** 715 | * increment hc.parser.index to next node in token list if token type is compound assignment operator 716 | * @arg {array} tokenList 717 | */ 718 | const list_eat_compassing = (tokenList) => { 719 | is_assingop(tokenList, hc.parser.index) ? hc.parser.index++ : parser_error(tokenList[hc.parser.index]); 720 | }; 721 | 722 | const parser_parse_class = (tokenList) => { 723 | let ast = new AstNode(token_type.class); 724 | ast.token = tokenList[hc.parser.index]; 725 | list_eat(tokenList, token_type.class); 726 | 727 | check_symtab(tokenList, false); 728 | 729 | if (get_class(tokenList[hc.parser.index]) > -1) { 730 | lexer_error(tokenList[hc.parser.index]) 731 | } else { 732 | hc.symtab.class.push(tokenList[hc.parser.index]); 733 | } 734 | const classId = get_class(tokenList[hc.parser.index]); 735 | hc.symtab.class[classId].content = []; 736 | 737 | ast.left = new AstNode(token_type.id); 738 | ast.left.token = tokenList[hc.parser.index]; 739 | list_eat(tokenList, token_type.id); 740 | 741 | list_eat(tokenList, token_type.rbrace); 742 | let varsIndex = hc.parser.index; 743 | 744 | ast.right = parser_parse_class_vars(tokenList, classId + 1); 745 | 746 | list_eat(tokenList, token_type.lbrace); 747 | 748 | if (check_token(tokenList, hc.parser.index, token_type.semi)) { 749 | list_eat(tokenList, token_type.semi); 750 | } else { 751 | let classValue = []; 752 | while (tokenList[varsIndex++] 753 | && tokenList[varsIndex].type !== token_type.lbrace) { 754 | if (check_token(tokenList, varsIndex, token_type.id)) { 755 | classValue.push({ ...tokenList[varsIndex], value: 0 }); 756 | } 757 | } 758 | hc.symtab.types.findIndex(e => e.id === tokenList[hc.parser.index].id) > -1 759 | && lexer_error(tokenList[hc.parser.index]) 760 | hc.symtab.types.push({ 761 | ...tokenList[hc.parser.index], 762 | value: classValue, 763 | classType: true 764 | }); 765 | 766 | ast.left.left = new AstNode(token_type.id); 767 | ast.left.left.token = tokenList[hc.parser.index]; 768 | list_eat(tokenList, token_type.id); 769 | 770 | list_eat(tokenList, token_type.semi); 771 | } 772 | 773 | return ast; 774 | } 775 | 776 | /** 777 | * semantic analysis of logical expresions 778 | * @arg {array} tokenList 779 | */ 780 | const parser_parse_logical_exp = (tokenList) => { 781 | if ( 782 | check_token(tokenList, hc.parser.index, token_type.semi) || 783 | check_token(tokenList, hc.parser.index, token_type.comma) || 784 | check_token(tokenList, hc.parser.index, token_type.lparen) 785 | ) 786 | return null; 787 | 788 | let ast; 789 | 790 | if ( 791 | check_token(tokenList, hc.parser.index - 1, token_type.id) || 792 | check_token(tokenList, hc.parser.index - 1, token_type.number) || 793 | check_token(tokenList, hc.parser.index - 1, token_type.true) || 794 | check_token(tokenList, hc.parser.index - 1, token_type.false) 795 | ) { 796 | if (is_logicalop(tokenList, hc.parser.index)) { 797 | ast = new AstNode(tokenList[hc.parser.index]?.type); 798 | ast.token = tokenList[hc.parser.index]; 799 | list_eat_logical(tokenList); 800 | } else { 801 | if ( 802 | check_token(tokenList, hc.parser.index - 1, token_type.id) || 803 | check_token(tokenList, hc.parser.index - 1, token_type.number) || 804 | check_token(tokenList, hc.parser.index - 1, token_type.true) || 805 | check_token(tokenList, hc.parser.index - 1, token_type.false) 806 | ) { 807 | ast = new AstNode(tokenList[hc.parser.index]?.type); 808 | ast.token = tokenList[hc.parser.index]; 809 | list_eat_math(tokenList); 810 | } 811 | } 812 | } else if (is_logicalop(tokenList, hc.parser.index - 1)) { 813 | if (check_token(tokenList, hc.parser.index, token_type.not)) { 814 | ast = new AstNode(token_type.not); 815 | ast.token = tokenList[hc.parser.index]; 816 | list_eat(tokenList, token_type.not); 817 | } else if (check_token(tokenList, hc.parser.index, token_type.id)) { 818 | check_symtab(tokenList, true); 819 | 820 | ast = new AstNode(token_type.id); 821 | ast.token = tokenList[hc.parser.index]; 822 | list_eat(tokenList, token_type.id); 823 | } else if (check_token(tokenList, hc.parser.index, token_type.true)) { 824 | ast = new AstNode(token_type.true); 825 | ast.token = { 826 | id: 1, 827 | line: tokenList[hc.parser.index].line, 828 | type: token_type.number, 829 | }; 830 | list_eat(tokenList, token_type.true); 831 | } else if (check_token(tokenList, hc.parser.index, token_type.false)) { 832 | ast = new AstNode(token_type.number); 833 | ast.token = { 834 | id: 0, 835 | line: tokenList[hc.parser.index].line, 836 | type: token_type.number, 837 | }; 838 | list_eat(tokenList, token_type.false); 839 | } else { 840 | ast = new AstNode(token_type.number); 841 | ast.token = tokenList[hc.parser.index]; 842 | list_eat(tokenList, token_type.number); 843 | } 844 | } else if (check_token(tokenList, hc.parser.index, token_type.id)) { 845 | check_symtab(tokenList, true); 846 | 847 | ast = new AstNode(token_type.id); 848 | ast.token = tokenList[hc.parser.index]; 849 | list_eat(tokenList, token_type.id); 850 | } else if (check_token(tokenList, hc.parser.index, token_type.true)) { 851 | ast = new AstNode(token_type.true); 852 | ast.token = { 853 | id: 1, 854 | line: tokenList[hc.parser.index].line, 855 | type: token_type.number, 856 | }; 857 | list_eat(tokenList, token_type.true); 858 | } else if (check_token(tokenList, hc.parser.index, token_type.false)) { 859 | ast = new AstNode(token_type.number); 860 | ast.token = { 861 | id: 0, 862 | line: tokenList[hc.parser.index].line, 863 | type: token_type.number, 864 | }; 865 | list_eat(tokenList, token_type.false); 866 | } else { 867 | ast = new AstNode(token_type.number); 868 | ast.token = tokenList[hc.parser.index]; 869 | list_eat(tokenList, token_type.number); 870 | } 871 | 872 | ast.right = parser_parse_logical_exp(tokenList); 873 | 874 | return ast; 875 | }; 876 | 877 | /** 878 | * semantic analysis of expresions 879 | * @arg {array} tokenList 880 | */ 881 | const parser_parse_exp = (tokenList, arg, prototypeIndex, inClass) => { 882 | if ( 883 | check_token(tokenList, hc.parser.index, token_type.semi) || 884 | check_token(tokenList, hc.parser.index, token_type.comma) || 885 | (arg && check_token(tokenList, hc.parser.index, token_type.lparen)) 886 | ) 887 | return null; 888 | 889 | let ast; 890 | 891 | if (hc.symtab.prototypes.find(e => e.id === tokenList[hc.parser.index].id) 892 | && !check_token(tokenList, hc.parser.index - 1, token_type.number) 893 | && !check_token(tokenList, hc.parser.index - 1, token_type.id)) { 894 | return parser_parse_call(tokenList) 895 | } else if ( 896 | check_token(tokenList, hc.parser.index - 1, token_type.id) || 897 | check_token(tokenList, hc.parser.index - 1, token_type.number) || 898 | check_token(tokenList, hc.parser.index - 1, token_type.classExp) 899 | ) { 900 | if (is_mathop(tokenList, hc.parser.index)) { 901 | if ( 902 | check_token(tokenList, hc.parser.index + 1, token_type.id) || 903 | check_token(tokenList, hc.parser.index + 1, token_type.number) 904 | ) { 905 | ast = new AstNode(tokenList[hc.parser.index]?.type); 906 | ast.token = tokenList[hc.parser.index]; 907 | list_eat_math(tokenList); 908 | } 909 | } else if (is_assingop(tokenList, hc.parser.index)) { 910 | ast = new AstNode(tokenList[hc.parser.index]?.type); 911 | ast.token = tokenList[hc.parser.index]; 912 | list_eat_compassing(tokenList); 913 | } else { 914 | ast = new AstNode(token_type.assig); 915 | ast.token = tokenList[hc.parser.index]; 916 | list_eat(tokenList, token_type.assig); 917 | } 918 | } else if ( 919 | check_token(tokenList, hc.parser.index - 1, token_type.assig) || 920 | is_assingop(tokenList, hc.parser.index - 1) 921 | ) { 922 | if (check_token(tokenList, hc.parser.index, token_type.id)) { 923 | let procedureArg; 924 | if (prototypeIndex) { 925 | procedureArg = hc.symtab.prototypes[prototypeIndex - 1].args.find(e => e.id === tokenList[hc.parser.index].id) 926 | if (!procedureArg) { 927 | procedureArg = hc.symtab.scoped[prototypeIndex - 1]?.find(e => e.id === tokenList[hc.parser.index].id) 928 | } 929 | } else { 930 | procedureArg = hc.symtab.prototypes.find(e => e.id === tokenList[hc.parser.index].id) 931 | } 932 | 933 | !procedureArg && check_symtab(tokenList, true); 934 | 935 | if (check_token(tokenList, hc.parser.index + 1, token_type.dot)) { 936 | let ids = []; 937 | 938 | while (tokenList[hc.parser.index].type 939 | === token_type.id) { 940 | tokenList[hc.parser.index].type === token_type.id 941 | && ids.push(tokenList[hc.parser.index].id) 942 | hc.parser.index += 2; 943 | } 944 | hc.parser.index -= 2; 945 | tokenList[hc.parser.index].id = ids.join('.') 946 | tokenList[hc.parser.index].type = token_type.classExp 947 | 948 | ast = new AstNode(token_type.classExp); 949 | ast.token = tokenList[hc.parser.index]; 950 | list_eat(tokenList, token_type.classExp); 951 | } else { 952 | ast = new AstNode(token_type.id); 953 | ast.token = tokenList[hc.parser.index]; 954 | list_eat(tokenList, token_type.id); 955 | } 956 | } else { 957 | if (!prototypeIndex) { 958 | const index = get_symtab(tokenList[hc.parser.index - 2]) 959 | if (index != undefined) { 960 | hc.symtab.global[index] = { 961 | id: tokenList[hc.parser.index - 2].id, 962 | line: tokenList[hc.parser.index - 2].line, 963 | value: tokenList[hc.parser.index].id, 964 | }; 965 | } 966 | } 967 | 968 | if (check_token(tokenList, hc.parser.index, token_type.true)) { 969 | hc.symtab.global[get_symtab(tokenList[hc.parser.index - 2])].value = 1; 970 | tokenList[hc.parser.index].type = token_type.number 971 | tokenList[hc.parser.index].id = 1; 972 | 973 | ast = new AstNode(token_type.number); 974 | ast.token = tokenList[hc.parser.index]; 975 | list_eat(tokenList, token_type.number); 976 | } else if (check_token(tokenList, hc.parser.index, token_type.false)) { 977 | hc.symtab.global[get_symtab(tokenList[hc.parser.index - 2])].value = 0; 978 | tokenList[hc.parser.index].type = token_type.number 979 | tokenList[hc.parser.index].id = 0; 980 | 981 | ast = new AstNode(token_type.number); 982 | ast.token = tokenList[hc.parser.index]; 983 | list_eat(tokenList, token_type.number); 984 | } else { 985 | ast = new AstNode(token_type.number); 986 | ast.token = tokenList[hc.parser.index]; 987 | list_eat(tokenList, token_type.number); 988 | } 989 | } 990 | } else if (is_mathop(tokenList, hc.parser.index - 1) 991 | || check_token(tokenList, hc.parser.index - 1, token_type.return)) { 992 | if (check_token(tokenList, hc.parser.index, token_type.id)) { 993 | let procedureArg; 994 | if (prototypeIndex) { 995 | procedureArg = hc.symtab.prototypes[prototypeIndex - 1].args.find(e => e.id === tokenList[hc.parser.index].id) 996 | if (!procedureArg) { 997 | procedureArg = hc.symtab.scoped[prototypeIndex - 1]?.find(e => e.id === tokenList[hc.parser.index].id) 998 | } 999 | } else { 1000 | procedureArg = hc.symtab.prototypes.find(e => e.id === tokenList[hc.parser.index].id) 1001 | } 1002 | !procedureArg && check_symtab(tokenList, true); 1003 | 1004 | ast = new AstNode(token_type.id); 1005 | ast.token = tokenList[hc.parser.index]; 1006 | list_eat(tokenList, token_type.id); 1007 | } else { 1008 | ast = new AstNode(token_type.number); 1009 | ast.token = tokenList[hc.parser.index]; 1010 | list_eat(tokenList, token_type.number); 1011 | } 1012 | } else if (check_token(tokenList, hc.parser.index, token_type.id)) { 1013 | let procedureArg; 1014 | if (prototypeIndex) { 1015 | procedureArg = hc.symtab.prototypes[prototypeIndex - 1].args.find(e => e.id === tokenList[hc.parser.index].id) 1016 | if (!procedureArg) { 1017 | procedureArg = hc.symtab.scoped[prototypeIndex - 1]?.find(e => e.id === tokenList[hc.parser.index].id) 1018 | } 1019 | } else { 1020 | procedureArg = hc.symtab.prototypes.find(e => e.id === tokenList[hc.parser.index].id) 1021 | } 1022 | !procedureArg && check_symtab(tokenList, true); 1023 | 1024 | ast = new AstNode(token_type.id); 1025 | ast.token = tokenList[hc.parser.index]; 1026 | list_eat(tokenList, token_type.id); 1027 | } else if (!check_token(tokenList, hc.parser.index, token_type.semi)) { 1028 | ast = new AstNode(token_type.comma); 1029 | ast.token = tokenList[hc.parser.index]; 1030 | list_eat(tokenList, token_type.comma); 1031 | } else { 1032 | parser_error(tokenList[hc.parser.index]); 1033 | } 1034 | 1035 | ast.right = parser_parse_exp(tokenList, arg, prototypeIndex); 1036 | 1037 | return ast; 1038 | }; 1039 | 1040 | /** 1041 | * semantic analysis of string arguments 1042 | * @arg {array} tokenList 1043 | */ 1044 | const parser_parse_str_args = (tokenList) => { 1045 | if (check_token(tokenList, hc.parser.index, token_type.semi)) return null; 1046 | 1047 | let ast; 1048 | 1049 | if ( 1050 | check_token(tokenList, hc.parser.index, token_type.id) || 1051 | check_token(tokenList, hc.parser.index, token_type.number) 1052 | ) { 1053 | if (check_token(tokenList, hc.parser.index + 1, token_type.dot)) { 1054 | let ids = []; 1055 | 1056 | while (tokenList[hc.parser.index].type 1057 | !== token_type.semi) { 1058 | tokenList[hc.parser.index].type === token_type.id 1059 | && ids.push(tokenList[hc.parser.index].id) 1060 | hc.parser.index++; 1061 | } 1062 | 1063 | hc.parser.index--; 1064 | tokenList[hc.parser.index].id = ids.join('.') 1065 | tokenList[hc.parser.index].type = token_type.classExp 1066 | 1067 | ast = new AstNode(token_type.classExp); 1068 | ast.token = tokenList[hc.parser.index]; 1069 | list_eat(tokenList, token_type.classExp); 1070 | } else { 1071 | ast = new AstNode(tokenList[hc.parser.index]?.type); 1072 | ast.token = tokenList[hc.parser.index]; 1073 | list_eat(tokenList, tokenList[hc.parser.index].type); 1074 | } 1075 | } else if (check_token(tokenList, hc.parser.index, token_type.str)) { 1076 | ast = new AstNode(token_type.str); 1077 | ast.token = tokenList[hc.parser.index]; 1078 | list_eat(tokenList, token_type.str); 1079 | } else { 1080 | ast = parser_parse_exp(tokenList, false); 1081 | } 1082 | 1083 | if (check_token(tokenList, hc.parser.index, token_type.assig)) { 1084 | ast.left = new AstNode(token_type.assig); 1085 | ast.left.token = tokenList[hc.parser.index]; 1086 | list_eat(tokenList, token_type.assig); 1087 | 1088 | ast.left.right = parser_parse_exp(tokenList, false); 1089 | } else if (!check_token(tokenList, hc.parser.index, token_type.semi)) { 1090 | ast.left = new AstNode(token_type.comma); 1091 | ast.left.token = tokenList[hc.parser.index]; 1092 | list_eat(tokenList, token_type.comma); 1093 | } 1094 | 1095 | ast.right = parser_parse_str_args(tokenList); 1096 | 1097 | return ast; 1098 | }; 1099 | 1100 | /** 1101 | * semantic analysis of inline strings 1102 | * @arg {array} tokenList 1103 | */ 1104 | const parser_parse_inline_str = (tokenList) => { 1105 | if (check_token(tokenList, hc.parser.index, token_type.semi)) return null; 1106 | 1107 | let ast = new AstNode(token_type.str); 1108 | ast.token = tokenList[hc.parser.index]; 1109 | list_eat(tokenList, token_type.str); 1110 | 1111 | if (!check_token(tokenList, hc.parser.index, token_type.semi)) { 1112 | ast.next = new AstNode(token_type.comma); 1113 | ast.next.token = tokenList[hc.parser.index]; 1114 | list_eat(tokenList, token_type.comma); 1115 | 1116 | if (!check_token(tokenList, hc.parser.index, token_type.str)) { 1117 | parser_error(tokenList[hc.parser.index]); 1118 | } 1119 | } 1120 | 1121 | ast.right = parser_parse_inline_str(tokenList); 1122 | 1123 | return ast; 1124 | }; 1125 | 1126 | /** 1127 | * semantic analysis of procedure return 1128 | * @arg {array} tokenList 1129 | */ 1130 | const parser_parse_return = (tokenList, prototypeIndex) => { 1131 | let ast = new AstNode(token_type.return); 1132 | ast.token = tokenList[hc.parser.index]; 1133 | list_eat(tokenList, token_type.return); 1134 | 1135 | ast.right = parser_parse_exp(tokenList, false, prototypeIndex) 1136 | 1137 | ast.left = new AstNode(token_type.semi); 1138 | ast.left.token = tokenList[hc.parser.index]; 1139 | list_eat(tokenList, token_type.semi); 1140 | 1141 | return ast; 1142 | }; 1143 | 1144 | /** 1145 | * semantic analysis of strings 1146 | * @arg {array} tokenList 1147 | */ 1148 | const parser_parse_str = (tokenList) => { 1149 | let ast = new AstNode(token_type.str); 1150 | ast.token = tokenList[hc.parser.index]; 1151 | list_eat(tokenList, token_type.str); 1152 | 1153 | if (check_token(tokenList, hc.parser.index, token_type.semi)) { 1154 | ast.left = new AstNode(token_type.semi); 1155 | ast.left.token = tokenList[hc.parser.index]; 1156 | list_eat(tokenList, token_type.semi); 1157 | } else { 1158 | ast.left = new AstNode(token_type.comma); 1159 | ast.left.token = tokenList[hc.parser.index]; 1160 | list_eat(tokenList, token_type.comma); 1161 | 1162 | if (check_token(tokenList, hc.parser.index, token_type.str)) { 1163 | ast.right = parser_parse_inline_str(tokenList); 1164 | 1165 | ast.left.left = new AstNode(token_type.semi); 1166 | ast.left.left.token = tokenList[hc.parser.index]; 1167 | list_eat(tokenList, token_type.semi); 1168 | } else { 1169 | ast.left.right = parser_parse_str_args(tokenList); 1170 | 1171 | ast.left.left = new AstNode(token_type.semi); 1172 | ast.left.left.token = tokenList[hc.parser.index]; 1173 | list_eat(tokenList, token_type.semi); 1174 | } 1175 | } 1176 | 1177 | return ast; 1178 | }; 1179 | 1180 | /** 1181 | * semantic analysis of procedures arguments 1182 | * @arg {array} tokenList 1183 | */ 1184 | const parser_parse_args = (tokenList = []) => { 1185 | if (check_token(tokenList, hc.parser.index, token_type.lparen)) return null; 1186 | 1187 | let ast = new AstNode(tokenList[hc.parser.index]?.type); 1188 | ast.token = tokenList[hc.parser.index]; 1189 | list_eat_type(tokenList); 1190 | 1191 | ast.next = new AstNode(token_type.id); 1192 | ast.next.token = tokenList[hc.parser.index]; 1193 | list_eat(tokenList, token_type.id); 1194 | 1195 | if (check_token(tokenList, hc.parser.index, token_type.assig)) { 1196 | ast.next.next = new AstNode(token_type.assig); 1197 | ast.next.next.token = tokenList[hc.parser.index]; 1198 | list_eat(tokenList, token_type.assig); 1199 | 1200 | if (check_token(tokenList, hc.parser.index, token_type.number)) { 1201 | ast.next.next.next = new AstNode(token_type.number); 1202 | ast.next.next.next.token = tokenList[hc.parser.index]; 1203 | list_eat(tokenList, token_type.number); 1204 | } else { 1205 | ast.next.next.next = new AstNode(token_type.str); 1206 | ast.next.next.next.token = tokenList[hc.parser.index]; 1207 | list_eat(tokenList, token_type.str); 1208 | } 1209 | } 1210 | 1211 | if (!check_token(tokenList, hc.parser.index, token_type.lparen)) { 1212 | ast.next.next = new AstNode(token_type.comma); 1213 | ast.next.next.token = tokenList[hc.parser.index]; 1214 | list_eat(tokenList, token_type.comma); 1215 | } 1216 | 1217 | ast.right = parser_parse_args(tokenList); 1218 | 1219 | return ast; 1220 | }; 1221 | 1222 | /** 1223 | * semantic analysis of procedures call arguments 1224 | * @arg {array} tokenList 1225 | * @arg {number} prototype 1226 | * @arg {number} i - number of arguments 1227 | */ 1228 | const parser_parse_call_args = (tokenList, prototype, notAssigArgs, i) => { 1229 | if (hc.symtab.prototypes[prototype].args.length === i) return null; 1230 | 1231 | let ast; 1232 | 1233 | if (hc.symtab.prototypes[prototype].args[i]?.id) { 1234 | if (check_token(tokenList, hc.parser.index, token_type.id)) { 1235 | check_symtab(tokenList, true); 1236 | 1237 | ast = new AstNode(token_type.id); 1238 | ast.token = tokenList[hc.parser.index]; 1239 | list_eat(tokenList, token_type.id); 1240 | 1241 | if (is_mathop(tokenList, hc.parser.index)) ast 1242 | 1243 | } else if (check_token(tokenList, hc.parser.index, token_type.number)) { 1244 | ast = new AstNode(token_type.number); 1245 | ast.token = tokenList[hc.parser.index]; 1246 | list_eat(tokenList, token_type.number); 1247 | } 1248 | } else { 1249 | if (check_token(tokenList, hc.parser.index, token_type.id)) { 1250 | check_symtab(tokenList, true); 1251 | 1252 | ast = new AstNode(token_type.id); 1253 | ast.token = tokenList[hc.parser.index]; 1254 | list_eat(tokenList, token_type.id); 1255 | } else { 1256 | ast = new AstNode(token_type.number); 1257 | ast.token = tokenList[hc.parser.index]; 1258 | list_eat(tokenList, token_type.number); 1259 | } 1260 | } 1261 | 1262 | if (i < hc.symtab.prototypes[prototype].args.length - 1) { 1263 | if (ast) { 1264 | if (check_token(tokenList, hc.parser.index, token_type.comma)) { 1265 | ast.left = new AstNode(token_type.comma); 1266 | ast.left.token = tokenList[hc.parser.index]; 1267 | list_eat(tokenList, token_type.comma); 1268 | } else { 1269 | return ast; 1270 | } 1271 | } else { 1272 | if (notAssigArgs || check_token(tokenList, hc.parser.index, token_type.comma)) { 1273 | ast = new AstNode(token_type.comma); 1274 | ast.token = tokenList[hc.parser.index]; 1275 | list_eat(tokenList, token_type.comma); 1276 | } else if (!notAssigArgs || check_token(tokenList, hc.parser.index, token_type.rparen)) { 1277 | return ast; 1278 | } 1279 | } 1280 | } else { 1281 | return ast; 1282 | } 1283 | 1284 | ast.right = parser_parse_call_args(tokenList, prototype, notAssigArgs, ++i); 1285 | 1286 | return ast; 1287 | }; 1288 | 1289 | /** 1290 | * semantic analysis of procedures call 1291 | * @arg {array} tokenList 1292 | */ 1293 | const parser_parse_call = (tokenList) => { 1294 | if (hc.symtab.prototypes.findIndex(e => e.id === tokenList[hc.parser.index].id) < 0) { 1295 | check_symtab(tokenList, true); 1296 | } 1297 | 1298 | const prototype = get_prototype(tokenList[hc.parser.index]); 1299 | 1300 | let ast = new AstNode(token_type.call); 1301 | ast.token = tokenList[hc.parser.index]; 1302 | list_eat(tokenList, token_type.id); 1303 | 1304 | if (hc.symtab.prototypes[prototype].args.length) { 1305 | const notAssigArgs = hc.symtab.prototypes[prototype].args.filter(e => { return e.id === undefined ? true : false }).length 1306 | 1307 | if (check_token(tokenList, hc.parser.index, token_type.semi) && !notAssigArgs) { 1308 | ast.left = new AstNode(token_type.semi); 1309 | ast.left.token = tokenList[hc.parser.index]; 1310 | list_eat(tokenList, token_type.semi); 1311 | 1312 | return ast; 1313 | } 1314 | 1315 | ast.left = new AstNode(token_type.rparen); 1316 | ast.left.token = tokenList[hc.parser.index]; 1317 | list_eat(tokenList, token_type.rparen); 1318 | 1319 | if (check_token(tokenList, hc.parser.index, token_type.str) && !notAssigArgs && tokenList[hc.parser.index].id === "*") { 1320 | ast.left.left = new AstNode(token_type.str); 1321 | ast.left.left.token = tokenList[hc.parser.index]; 1322 | list_eat(tokenList, token_type.str); 1323 | } else { 1324 | if (hc.symtab.prototypes[prototype].args.length) { 1325 | ast.right = parser_parse_call_args(tokenList, prototype, notAssigArgs, 0); 1326 | } 1327 | } 1328 | 1329 | ast.left.left = new AstNode(token_type.lparen); 1330 | ast.left.left.token = tokenList[hc.parser.index]; 1331 | list_eat(tokenList, token_type.lparen); 1332 | 1333 | } else if (check_token(tokenList, hc.parser.index, token_type.rparen)) { 1334 | ast.left = new AstNode(token_type.rparen); 1335 | ast.left.token = tokenList[hc.parser.index]; 1336 | list_eat(tokenList, token_type.rparen); 1337 | 1338 | if (check_token(tokenList, hc.parser.index, token_type.str) && tokenList[hc.parser.index].id === "*") { 1339 | ast.left.next = new AstNode(token_type.str); 1340 | ast.left.next.token = tokenList[hc.parser.index]; 1341 | list_eat(tokenList, token_type.str); 1342 | } 1343 | 1344 | ast.left.left = new AstNode(token_type.lparen); 1345 | ast.left.left.token = tokenList[hc.parser.index]; 1346 | list_eat(tokenList, token_type.lparen); 1347 | } 1348 | 1349 | return ast; 1350 | }; 1351 | 1352 | /** 1353 | * semantic analysis of inline variables declaration 1354 | * @arg {array} tokenList 1355 | */ 1356 | const parser_parse_inline_vars = (tokenList, classId, prototypeIndex) => { 1357 | if ( 1358 | check_token(tokenList, hc.parser.index, token_type.semi) && 1359 | !check_token(tokenList, hc.parser.index - 1, token_type.comma) 1360 | ) 1361 | return null; 1362 | 1363 | check_symtab(tokenList, false); 1364 | 1365 | let symtabNode = tokenList[hc.parser.index]; 1366 | 1367 | let ast = new AstNode(token_type.id); 1368 | ast.token = tokenList[hc.parser.index]; 1369 | list_eat(tokenList, token_type.id); 1370 | 1371 | if (check_token(tokenList, hc.parser.index, token_type.assig)) { 1372 | if (classId) { 1373 | hc.symtab.class[classId - 1].content.push({ ...symtabNode, value: 0 }); 1374 | } else if (prototypeIndex) { 1375 | hc.symtab.scoped[prototypeIndex - 1].push({ ...symtabNode, value: 0 }); 1376 | } else { 1377 | hc.symtab.global.push({ ...symtabNode, value: 0 }); 1378 | } 1379 | 1380 | ast.left = parser_parse_exp(tokenList, false, prototypeIndex); 1381 | 1382 | if (!check_token(tokenList, hc.parser.index, token_type.semi)) { 1383 | ast.left.left = new AstNode(token_type.comma); 1384 | ast.left.left.token = tokenList[hc.parser.index]; 1385 | list_eat(tokenList, token_type.comma); 1386 | } 1387 | } else if (!check_token(tokenList, hc.parser.index, token_type.semi)) { 1388 | if (classId) { 1389 | hc.symtab.class[classId - 1].content.push({ ...symtabNode, value: 0 }); 1390 | } else if (prototypeIndex) { 1391 | hc.symtab.scoped[prototypeIndex - 1].push({ ...symtabNode, value: 0 }); 1392 | } else { 1393 | hc.symtab.global.push({ ...symtabNode, value: 0 }); 1394 | } 1395 | 1396 | ast.left = new AstNode(token_type.comma); 1397 | ast.left.token = tokenList[hc.parser.index]; 1398 | list_eat(tokenList, token_type.comma); 1399 | } else { 1400 | if (classId) { 1401 | hc.symtab.class[classId - 1].content.push({ ...symtabNode, value: 0 }); 1402 | } else if (prototypeIndex) { 1403 | hc.symtab.scoped[prototypeIndex - 1].push({ ...symtabNode, value: 0 }); 1404 | } else { 1405 | hc.symtab.global.push({ ...symtabNode, value: 0 }); 1406 | } 1407 | } 1408 | 1409 | ast.right = parser_parse_inline_vars(tokenList, classId, prototypeIndex); 1410 | 1411 | return ast; 1412 | }; 1413 | 1414 | const parser_parse_class_vars = (tokenList, classId) => { 1415 | if (check_token(tokenList, hc.parser.index, token_type.lbrace)) 1416 | return null; 1417 | 1418 | let ast = new AstNode(tokenList[hc.parser.index]?.type); 1419 | ast.token = tokenList[hc.parser.index]; 1420 | list_eat_type(tokenList); 1421 | 1422 | ast.left = parser_parse_inline_vars(tokenList, classId); 1423 | 1424 | ast.left.left = new AstNode(token_type.semi); 1425 | ast.left.left.token = tokenList[hc.parser.index]; 1426 | list_eat(tokenList, token_type.semi); 1427 | 1428 | ast.right = parser_parse_class_vars(tokenList, classId); 1429 | 1430 | return ast; 1431 | } 1432 | 1433 | const parser_parse_class_var_exp = (tokenList) => { 1434 | let ast = new AstNode(token_type.id); 1435 | ast.token = tokenList[hc.parser.index]; 1436 | list_eat(tokenList, token_type.id); 1437 | 1438 | if (check_token(tokenList, hc.parser.index, token_type.assig)) return ast; 1439 | 1440 | ast.right = new AstNode(token_type.dot); 1441 | ast.right.token = tokenList[hc.parser.index]; 1442 | list_eat(tokenList, token_type.dot); 1443 | 1444 | ast.right.right = parser_parse_class_var_exp(tokenList); 1445 | return ast; 1446 | } 1447 | 1448 | const parser_parse_class_assig = (tokenList, index) => { 1449 | let vars = hc.parser.index; 1450 | let ids = []; 1451 | while (tokenList[vars].type 1452 | !== token_type.assig 1453 | && tokenList[++vars]) { 1454 | tokenList[vars].type === token_type.id 1455 | && ids.push(tokenList[vars].id) 1456 | } 1457 | 1458 | let isGlobal = false; 1459 | if (index 1460 | && hc.symtab.prototypes[prototypeIndex]?.args?.findIndex(e => e.id === tokenList[hc.parser.index].id) < 0) { 1461 | parser_error(tokenList[hc.parser.index]) 1462 | } else { 1463 | check_symtab(tokenList, true); 1464 | index = hc.symtab.global.findIndex(e => e.id === tokenList[hc.parser.index].id); 1465 | isGlobal = true; 1466 | } 1467 | 1468 | let ast = parser_parse_class_var_exp(tokenList); 1469 | 1470 | ast.left = new AstNode(token_type.assig); 1471 | ast.left.token = tokenList[hc.parser.index]; 1472 | list_eat(tokenList, token_type.assig); 1473 | 1474 | if (isGlobal) { 1475 | ast.left.left = parser_parse_exp(tokenList, false) 1476 | } else { 1477 | ast.left.left = parser_parse_exp(tokenList, false, index) 1478 | } 1479 | 1480 | ast.left.left.left = new AstNode(token_type.semi); 1481 | ast.left.left.left.token = tokenList[hc.parser.index]; 1482 | list_eat(tokenList, token_type.semi); 1483 | 1484 | return ast; 1485 | } 1486 | 1487 | /** 1488 | * semantic analysis of identifiers 1489 | * @arg {array} tokenList 1490 | */ 1491 | const parser_parse_id = (tokenList, prototypeIndex) => { 1492 | if (!hc.symtab.types.find(e => e.id === tokenList[hc.parser.index].id) 1493 | && check_token(tokenList, hc.parser.index, token_type.id)) { 1494 | if (is_assingop(tokenList, hc.parser.index + 1)) { 1495 | let ast = parser_parse_exp(tokenList, false, prototypeIndex); 1496 | 1497 | ast.left = new AstNode(token_type.semi); 1498 | ast.left.token = tokenList[hc.parser.index]; 1499 | list_eat(tokenList, token_type.semi); 1500 | 1501 | return ast; 1502 | } else if ( 1503 | check_token(tokenList, hc.parser.index + 1, token_type.increment) || 1504 | check_token(tokenList, hc.parser.index + 1, token_type.decrement) 1505 | ) { 1506 | return parser_parse_prepostfix(tokenList, false); 1507 | } else if (check_token(tokenList, hc.parser.index + 1, token_type.dot)) { 1508 | return parser_parse_class_assig(tokenList, prototypeIndex); 1509 | } else { 1510 | let ast = parser_parse_call(tokenList); 1511 | list_eat(tokenList, token_type.semi); 1512 | return ast 1513 | } 1514 | } 1515 | 1516 | let ast = new AstNode(tokenList[hc.parser.index]?.type); 1517 | ast.token = tokenList[hc.parser.index]; 1518 | list_eat_type(tokenList); 1519 | 1520 | if (prototypeIndex && hc.symtab.prototypes[prototypeIndex - 1]?.args?.find(e => e.id === tokenList[hc.parser.index].id) 1521 | || prototypeIndex && hc.symtab.scoped[prototypeIndex - 1]?.find(e => e.id === tokenList[hc.parser.index].id)) { 1522 | parser_error(tokenList[hc.parser.index]); 1523 | } else { 1524 | check_symtab(tokenList, false); 1525 | } 1526 | 1527 | const symtabNode = tokenList[hc.parser.index]; 1528 | 1529 | ast.left = new AstNode(token_type.id); 1530 | ast.left.token = tokenList[hc.parser.index]; 1531 | list_eat(tokenList, token_type.id); 1532 | 1533 | if (check_token(tokenList, hc.parser.index, token_type.semi)) { 1534 | if (prototypeIndex) { 1535 | !hc.symtab.scoped[prototypeIndex - 1] && (hc.symtab.scoped[prototypeIndex - 1] = []); 1536 | hc.symtab.scoped[prototypeIndex - 1].push({ ...symtabNode, value: 0 }); 1537 | } else { 1538 | hc.symtab.global.push({ ...symtabNode, value: 0 }); 1539 | } 1540 | 1541 | ast.left.left = new AstNode(token_type.semi); 1542 | ast.left.left.token = tokenList[hc.parser.index]; 1543 | list_eat(tokenList, token_type.semi); 1544 | } else if (check_token(tokenList, hc.parser.index, token_type.comma)) { 1545 | if (prototypeIndex) { 1546 | !hc.symtab.scoped[prototypeIndex - 1] && (hc.symtab.scoped[prototypeIndex - 1] = []); 1547 | hc.symtab.scoped[prototypeIndex - 1].push({ ...symtabNode, value: 0 }); 1548 | } else { 1549 | hc.symtab.global.push({ ...symtabNode, value: 0 }); 1550 | } 1551 | 1552 | ast.left.left = new AstNode(token_type.comma); 1553 | ast.left.left.token = tokenList[hc.parser.index]; 1554 | list_eat(tokenList, token_type.comma); 1555 | 1556 | ast.right = parser_parse_inline_vars(tokenList); 1557 | 1558 | ast.left.left.left = new AstNode(token_type.semi); 1559 | ast.left.left.left.token = tokenList[hc.parser.index]; 1560 | list_eat(tokenList, token_type.semi); 1561 | } else if (check_token(tokenList, hc.parser.index, token_type.assig)) { 1562 | if (prototypeIndex) { 1563 | !hc.symtab.scoped[prototypeIndex - 1] && (hc.symtab.scoped[prototypeIndex - 1] = []); 1564 | hc.symtab.scoped[prototypeIndex - 1].push({ ...symtabNode, value: 0 }); 1565 | } else { 1566 | hc.symtab.global.push({ ...symtabNode, value: 0 }); 1567 | } 1568 | 1569 | ast.right = parser_parse_exp(tokenList, false, prototypeIndex); 1570 | 1571 | if (check_token(tokenList, hc.parser.index, token_type.comma)) { 1572 | ast.left.left = new AstNode(token_type.comma); 1573 | ast.left.left.token = tokenList[hc.parser.index]; 1574 | list_eat(tokenList, token_type.comma); 1575 | 1576 | ast.left.right = parser_parse_inline_vars(tokenList, null, prototypeIndex); 1577 | 1578 | ast.left.left.left = new AstNode(token_type.semi); 1579 | ast.left.left.left.token = tokenList[hc.parser.index]; 1580 | list_eat(tokenList, token_type.semi); 1581 | } else { 1582 | ast.left.left = new AstNode(token_type.semi); 1583 | ast.left.left.token = tokenList[hc.parser.index]; 1584 | list_eat(tokenList, token_type.semi); 1585 | } 1586 | } else if (!prototypeIndex) { 1587 | /** 1588 | * parse procedures 1589 | */ 1590 | const prototypeId = tokenList[hc.parser.index - 1].id 1591 | let prototype = { 1592 | id: prototypeId, 1593 | type: tokenList[hc.parser.index - 2].type, 1594 | args: undefined, 1595 | return: undefined, 1596 | } 1597 | 1598 | ast.left.left = new AstNode(token_type.rparen); 1599 | ast.left.left.token = tokenList[hc.parser.index]; 1600 | list_eat(tokenList, token_type.rparen); 1601 | 1602 | let priorWalk = hc.parser.index; 1603 | 1604 | ast.left.left.right = parser_parse_args(tokenList); 1605 | 1606 | let args = []; 1607 | while (priorWalk < hc.parser.index) { 1608 | let argProt = { 1609 | id: tokenList[priorWalk + 1].id, 1610 | type: tokenList[priorWalk].type, 1611 | value: undefined, 1612 | } 1613 | if (tokenList[priorWalk + 2].type === token_type.assig) { 1614 | argProt.value = tokenList[priorWalk + 3].id; 1615 | priorWalk += 2; 1616 | } 1617 | priorWalk += 2; 1618 | if (tokenList[priorWalk].type === token_type.comma) { 1619 | priorWalk++; 1620 | } 1621 | args.push(argProt) 1622 | } 1623 | prototype.args = args; 1624 | 1625 | hc.symtab.prototypes.push(prototype) 1626 | 1627 | const prototypeIndex = hc.symtab.prototypes.findIndex(e => e.id === prototypeId) + 1; 1628 | 1629 | ast.left.left.left = new AstNode(token_type.lparen); 1630 | ast.left.left.left.token = tokenList[hc.parser.index]; 1631 | list_eat(tokenList, token_type.lparen); 1632 | 1633 | ast.left.left.left.left = new AstNode(token_type.rbrace); 1634 | ast.left.left.left.left.token = tokenList[hc.parser.index]; 1635 | list_eat(tokenList, token_type.rbrace); 1636 | 1637 | ast.right = parser_parse_block(tokenList, prototypeIndex); 1638 | 1639 | ast.left.left.left.left.left = new AstNode(token_type.lbrace); 1640 | ast.left.left.left.left.left.token = tokenList[hc.parser.index]; 1641 | list_eat(tokenList, token_type.lbrace); 1642 | } 1643 | 1644 | return ast; 1645 | }; 1646 | 1647 | /** 1648 | * semantic analysis of variable with pre/post fix operator 1649 | * @arg {array} tokenList 1650 | * @arg {boolean} block 1651 | */ 1652 | const parser_parse_prepostfix = (tokenList, block, prototypeIndex) => { 1653 | let ast; 1654 | 1655 | if (is_mathop(tokenList, hc.parser.index)) { 1656 | if (check_token(tokenList, hc.parser.index, token_type.increment)) { 1657 | ast = new AstNode(token_type.increment); 1658 | ast.token = tokenList[hc.parser.index]; 1659 | list_eat_math(tokenList); 1660 | } else { 1661 | ast = new AstNode(token_type.decrement); 1662 | ast.token = tokenList[hc.parser.index]; 1663 | list_eat_math(tokenList); 1664 | } 1665 | 1666 | if (prototypeIndex) { 1667 | let token = hc.symtab.prototypes[prototypeIndex]?.args?.findIndex(e => e.id === tokenList[hc.parser.index]) 1668 | if (token < 0) { 1669 | token = hc.symtab.scoped[prototypeIndex].findIndex(e => e.id === tokenList[hc.parser.index]) 1670 | if (token < 0) { 1671 | parser_error(tokenList[hc.parser.index]) 1672 | } 1673 | } 1674 | } else { 1675 | check_symtab(tokenList, true); 1676 | } 1677 | 1678 | ast.right = new AstNode(token_type.id); 1679 | ast.right.token = tokenList[hc.parser.index]; 1680 | list_eat(tokenList, token_type.id); 1681 | } else { 1682 | if (prototypeIndex) { 1683 | let token = hc.symtab.prototypes[prototypeIndex]?.args?.findIndex(e => e.id === tokenList[hc.parser.index]) 1684 | if (token < 0) { 1685 | token = hc.symtab.scoped[prototypeIndex].findIndex(e => e.id === tokenList[hc.parser.index]) 1686 | if (token < 0) { 1687 | parser_error(tokenList[hc.parser.index]) 1688 | } 1689 | } 1690 | } else { 1691 | check_symtab(tokenList, true); 1692 | } 1693 | 1694 | ast = new AstNode(token_type.id); 1695 | ast.token = tokenList[hc.parser.index]; 1696 | list_eat(tokenList, token_type.id); 1697 | 1698 | if (check_token(tokenList, hc.parser.index, token_type.increment)) { 1699 | ast.right = new AstNode(token_type.increment); 1700 | ast.right.token = tokenList[hc.parser.index]; 1701 | list_eat_math(tokenList); 1702 | } else { 1703 | ast.right = new AstNode(token_type.decrement); 1704 | ast.right.token = tokenList[hc.parser.index]; 1705 | list_eat_math(tokenList); 1706 | } 1707 | } 1708 | 1709 | if (!block) { 1710 | ast.left = new AstNode(token_type.semi); 1711 | ast.left.token = tokenList[hc.parser.index]; 1712 | list_eat(tokenList, token_type.semi); 1713 | } 1714 | 1715 | return ast; 1716 | }; 1717 | 1718 | /** 1719 | * semantic analysis of if else statement 1720 | * @arg {array} tokenList 1721 | */ 1722 | const parser_parse_ifelse = (tokenList, prototypeIndex) => { 1723 | let ast = new AstNode(token_type.if); 1724 | ast.token = tokenList[hc.parser.index]; 1725 | list_eat(tokenList, token_type.if); 1726 | 1727 | ast.left = new AstNode(token_type.rparen); 1728 | ast.left.token = tokenList[hc.parser.index]; 1729 | list_eat(tokenList, token_type.rparen); 1730 | 1731 | if (check_token(tokenList, hc.parser.index, token_type.number)) { 1732 | ast.left.left = new AstNode(token_type.number); 1733 | ast.left.left.token = tokenList[hc.parser.index]; 1734 | list_eat(tokenList, token_type.number); 1735 | } else if (check_token(tokenList, hc.parser.index, token_type.true)) { 1736 | ast.left.left = new AstNode(token_type.true); 1737 | ast.left.left.token = { 1738 | id: 1, 1739 | line: tokenList[hc.parser.index].line, 1740 | type: token_type.number, 1741 | }; 1742 | list_eat(tokenList, token_type.true); 1743 | } else if (check_token(tokenList, hc.parser.index, token_type.false)) { 1744 | ast.left.left = new AstNode(token_type.number); 1745 | ast.left.left.token = { 1746 | id: 0, 1747 | line: tokenList[hc.parser.index].line, 1748 | type: token_type.number, 1749 | }; 1750 | list_eat(tokenList, token_type.false); 1751 | } else { 1752 | if (!hc.symtab.prototypes[prototypeIndex - 1]?.args?.find(e => e.id === tokenList[hc.parser.index].id) 1753 | && !hc.symtab.scoped[prototypeIndex - 1]?.find(e => e.id === tokenList[hc.parser.index].id)) { 1754 | check_symtab(tokenList, true); 1755 | } 1756 | 1757 | ast.left.left = new AstNode(token_type.id); 1758 | ast.left.left.token = tokenList[hc.parser.index]; 1759 | list_eat(tokenList, token_type.id); 1760 | } 1761 | 1762 | ast.right = parser_parse_logical_exp(tokenList, prototypeIndex); 1763 | 1764 | ast.left.left.left = new AstNode(token_type.lparen); 1765 | ast.left.left.left.token = tokenList[hc.parser.index]; 1766 | list_eat(tokenList, token_type.lparen); 1767 | 1768 | ast.left.left.next = new AstNode(token_type.rbrace); 1769 | ast.left.left.next.token = tokenList[hc.parser.index]; 1770 | list_eat(tokenList, token_type.rbrace); 1771 | 1772 | ast.left.left.left.right = parser_parse_block(tokenList, prototypeIndex); 1773 | 1774 | ast.left.left.next.next = new AstNode(token_type.lbrace); 1775 | ast.left.left.next.next.token = tokenList[hc.parser.index]; 1776 | list_eat(tokenList, token_type.lbrace); 1777 | 1778 | if ( 1779 | hc.parser.index < tokenList.length && 1780 | check_token(tokenList, hc.parser.index, token_type.else) 1781 | ) { 1782 | ast.left.left.left.left = new AstNode(token_type.else); 1783 | ast.left.left.left.left.token = tokenList[hc.parser.index]; 1784 | list_eat(tokenList, token_type.else); 1785 | 1786 | if (check_token(tokenList, hc.parser.index, token_type.if)) { 1787 | ast.left.left.left.next = parser_parse_ifelse(tokenList); 1788 | } else { 1789 | ast.left.left.left.next = new AstNode(token_type.rbrace); 1790 | ast.left.left.left.next.token = tokenList[hc.parser.index]; 1791 | list_eat(tokenList, token_type.rbrace); 1792 | 1793 | ast.left.left.left.left.right = parser_parse_block(tokenList, prototypeIndex); 1794 | 1795 | ast.left.left.left.next.next = new AstNode(token_type.lbrace); 1796 | ast.left.left.left.next.next.token = tokenList[hc.parser.index]; 1797 | list_eat(tokenList, token_type.lbrace); 1798 | } 1799 | } 1800 | 1801 | return ast; 1802 | }; 1803 | 1804 | /** 1805 | * semantic analysis of for statement 1806 | * @arg {array} tokenList 1807 | */ 1808 | const parser_parse_for = (tokenList, prototypeIndex) => { 1809 | let ast = new AstNode(token_type.for); 1810 | ast.token = tokenList[hc.parser.index]; 1811 | list_eat(tokenList, token_type.for); 1812 | 1813 | ast.next = new AstNode(token_type.rparen); 1814 | ast.next.token = tokenList[hc.parser.index]; 1815 | list_eat(tokenList, token_type.rparen); 1816 | 1817 | if (prototypeIndex) { 1818 | let token = hc.symtab.prototypes[prototypeIndex]?.args?.findIndex(e => e.id === tokenList[hc.parser.index]) 1819 | if (token < 0) { 1820 | token = hc.symtab.scoped[prototypeIndex].findIndex(e => e.id === tokenList[hc.parser.index]) 1821 | if (token < 0) { 1822 | parser_error(tokenList[hc.parser.index]) 1823 | } 1824 | } 1825 | } else { 1826 | check_symtab(tokenList, true); 1827 | } 1828 | 1829 | ast.left = new AstNode(token_type.id); 1830 | ast.left.token = tokenList[hc.parser.index]; 1831 | list_eat(tokenList, token_type.id); 1832 | 1833 | ast.right = parser_parse_exp(tokenList, false, prototypeIndex); 1834 | 1835 | ast.left.left = new AstNode(token_type.semi); 1836 | ast.left.left.token = tokenList[hc.parser.index]; 1837 | list_eat(tokenList, token_type.semi); 1838 | 1839 | if (prototypeIndex) { 1840 | let token = hc.symtab.prototypes[prototypeIndex]?.args?.findIndex(e => e.id === tokenList[hc.parser.index]) 1841 | if (token < 0) { 1842 | token = hc.symtab.scoped[prototypeIndex].findIndex(e => e.id === tokenList[hc.parser.index]) 1843 | if (token < 0) { 1844 | parser_error(tokenList[hc.parser.index]) 1845 | } 1846 | } 1847 | } else { 1848 | check_symtab(tokenList, true); 1849 | } 1850 | 1851 | ast.left.left.left = new AstNode(token_type.id); 1852 | ast.left.left.left.token = tokenList[hc.parser.index]; 1853 | list_eat(tokenList, token_type.id); 1854 | 1855 | ast.left.left.left.left = new AstNode(tokenList[hc.parser.index]?.type); 1856 | ast.left.left.left.left.token = tokenList[hc.parser.index]; 1857 | list_eat_logical(tokenList); 1858 | 1859 | ast.left.left.left.left.left = new AstNode(token_type.number); 1860 | ast.left.left.left.left.left.token = tokenList[hc.parser.index]; 1861 | list_eat(tokenList, token_type.number); 1862 | 1863 | ast.left.left.left.left.left.left = new AstNode(token_type.semi); 1864 | ast.left.left.left.left.left.left.token = tokenList[hc.parser.index]; 1865 | list_eat(tokenList, token_type.semi); 1866 | 1867 | if ( 1868 | check_token(tokenList, hc.parser.index, token_type.increment) || 1869 | check_token(tokenList, hc.parser.index, token_type.decrement) || 1870 | check_token(tokenList, hc.parser.index + 1, token_type.decrement) || 1871 | check_token(tokenList, hc.parser.index + 1, token_type.increment) 1872 | ) { 1873 | ast.left.left.left.left.left.left.left = parser_parse_prepostfix( 1874 | tokenList, 1875 | true, 1876 | prototypeIndex 1877 | ); 1878 | } else { 1879 | check_symtab(tokenList, true); 1880 | 1881 | ast.left.left.left.left.left.left.next = new AstNode(token_type.id); 1882 | ast.left.left.left.left.left.left.next.token = tokenList[hc.parser.index]; 1883 | list_eat(tokenList, token_type.id); 1884 | 1885 | ast.left.left.left.left.left.left.left = parser_parse_exp( 1886 | tokenList, 1887 | true, 1888 | prototypeIndex 1889 | ); 1890 | } 1891 | 1892 | ast.left.left.left.left.left.left.left.next = new AstNode(token_type.lparen); 1893 | ast.left.left.left.left.left.left.left.next.token = tokenList[hc.parser.index]; 1894 | list_eat(tokenList, token_type.lparen); 1895 | 1896 | ast.left.left.left.left.left.left.next = new AstNode(token_type.rbrace); 1897 | ast.left.left.left.left.left.left.next.token = tokenList[hc.parser.index]; 1898 | list_eat(tokenList, token_type.rbrace); 1899 | 1900 | ast.left.left.left.left.left.left.right = parser_parse_block(tokenList, prototypeIndex); 1901 | 1902 | ast.left.left.left.left.left.left.next.next = new AstNode(token_type.lbrace); 1903 | ast.left.left.left.left.left.left.next.next.token = tokenList[hc.parser.index]; 1904 | list_eat(tokenList, token_type.lbrace); 1905 | 1906 | return ast; 1907 | }; 1908 | 1909 | const parser_parse_include = (tokenList) => { 1910 | let ast = new AstNode(token_type.include); 1911 | ast.token = tokenList[hc.parser.index]; 1912 | list_eat(tokenList, token_type.include); 1913 | 1914 | ast.left = new AstNode(token_type.str); 1915 | ast.left.token = tokenList[hc.parser.index]; 1916 | list_eat(tokenList, token_type.str); 1917 | 1918 | return ast; 1919 | }; 1920 | 1921 | const parser_parse_js = (tokenList) => { 1922 | let ast = new AstNode(token_type.js); 1923 | ast.token = tokenList[hc.parser.index]; 1924 | list_eat(tokenList, token_type.js); 1925 | 1926 | ast.left = new AstNode(token_type.rbrace); 1927 | ast.left.token = tokenList[hc.parser.index]; 1928 | list_eat(tokenList, token_type.rbrace); 1929 | 1930 | ast.left.left = new AstNode(token_type.jscode); 1931 | ast.left.left.token = tokenList[hc.parser.index]; 1932 | list_eat(tokenList, token_type.jscode); 1933 | 1934 | ast.left.left.left = new AstNode(token_type.lbrace); 1935 | ast.left.left.left.token = tokenList[hc.parser.index]; 1936 | list_eat(tokenList, token_type.lbrace); 1937 | 1938 | ast.left.left.left.left = new AstNode(token_type.semi); 1939 | ast.left.left.left.left.token = tokenList[hc.parser.index]; 1940 | list_eat(tokenList, token_type.semi); 1941 | 1942 | return ast; 1943 | }; 1944 | 1945 | const parser_parse_define = (tokenList) => { 1946 | let ast = new AstNode(token_type.define); 1947 | ast.token = tokenList[hc.parser.index]; 1948 | list_eat(tokenList, token_type.define); 1949 | 1950 | let symtabNode = tokenList[hc.parser.index] 1951 | check_symtab(tokenList, false); 1952 | ast.left = new AstNode(token_type.id); 1953 | ast.left.token = tokenList[hc.parser.index]; 1954 | list_eat(tokenList, token_type.id); 1955 | 1956 | let value; 1957 | if (check_token(tokenList, hc.parser.index, token_type.number)) { 1958 | value = tokenList[hc.parser.index].id 1959 | ast.left.left = new AstNode(token_type.number); 1960 | ast.left.left.token = tokenList[hc.parser.index]; 1961 | list_eat(tokenList, token_type.number); 1962 | } else if (check_token(tokenList, hc.parser.index, token_type.true)) { 1963 | value = tokenList[hc.parser.index].id 1964 | ast.left.left = new AstNode(token_type.true); 1965 | ast.left.left.token = { 1966 | id: 1, 1967 | line: tokenList[hc.parser.index].line, 1968 | type: token_type.number, 1969 | }; 1970 | list_eat(tokenList, token_type.true); 1971 | } else if (check_token(tokenList, hc.parser.index, token_type.false)) { 1972 | value = tokenList[hc.parser.index].id 1973 | ast.left.left = new AstNode(token_type.number); 1974 | ast.left.left.token = { 1975 | id: 0, 1976 | line: tokenList[hc.parser.index].line, 1977 | type: token_type.number, 1978 | }; 1979 | list_eat(tokenList, token_type.false); 1980 | } else { 1981 | value = tokenList[hc.parser.index].id 1982 | ast.left.left = new AstNode(token_type.str); 1983 | ast.left.left.token = tokenList[hc.parser.index]; 1984 | list_eat(tokenList, token_type.str); 1985 | } 1986 | 1987 | hc.symtab.global.push({ ...symtabNode, value: value }); 1988 | 1989 | return ast; 1990 | }; 1991 | 1992 | /** 1993 | * semantic analysis of blocks 1994 | * @arg {array} tokenList 1995 | */ 1996 | const parser_parse_block = (tokenList, prototypeIndex) => { 1997 | if (check_token(tokenList, hc.parser.index, token_type.lbrace)) return null; 1998 | 1999 | let ast; 2000 | 2001 | switch (tokenList[hc.parser.index].type) { 2002 | case token_type.bool: 2003 | case token_type.i0: 2004 | case token_type.u0: 2005 | case token_type.i8: 2006 | case token_type.u8: 2007 | case token_type.i16: 2008 | case token_type.u16: 2009 | case token_type.i32: 2010 | case token_type.u32: 2011 | case token_type.i64: 2012 | case token_type.u64: 2013 | case token_type.f64: 2014 | case token_type.id: 2015 | ast = parser_parse_id(tokenList, prototypeIndex); 2016 | break; 2017 | case token_type.increment: 2018 | case token_type.decrement: 2019 | ast = parser_parse_prepostfix(tokenList, false); 2020 | break; 2021 | case token_type.str: 2022 | ast = parser_parse_str(tokenList); 2023 | break; 2024 | case token_type.for: 2025 | ast = parser_parse_for(tokenList, prototypeIndex); 2026 | break; 2027 | case token_type.if: 2028 | ast = parser_parse_ifelse(tokenList, prototypeIndex); 2029 | break; 2030 | case token_type.return: 2031 | ast = parser_parse_return(tokenList, prototypeIndex); 2032 | break; 2033 | case token_type.class: 2034 | ast = parser_parse_class(tokenList); 2035 | break; 2036 | default: 2037 | parser_error(tokenList[hc.parser.index]); 2038 | } 2039 | 2040 | ast.next = parser_parse_block(tokenList, prototypeIndex); 2041 | 2042 | return ast; 2043 | }; 2044 | 2045 | /** 2046 | * @arg {array} tokenList 2047 | */ 2048 | const parser_parse = (tokenList) => { 2049 | if (!tokenList[hc.parser.index]) return null; 2050 | 2051 | let expList = new Ast(); 2052 | 2053 | const type = tokenList[hc.parser.index].type; 2054 | 2055 | switch (type) { 2056 | case token_type.bool: 2057 | case token_type.i0: 2058 | case token_type.u0: 2059 | case token_type.i8: 2060 | case token_type.u8: 2061 | case token_type.i16: 2062 | case token_type.u16: 2063 | case token_type.i32: 2064 | case token_type.u32: 2065 | case token_type.i64: 2066 | case token_type.u64: 2067 | case token_type.f64: 2068 | case token_type.id: 2069 | expList.ast = parser_parse_id(tokenList); 2070 | break; 2071 | case token_type.define: 2072 | expList.ast = parser_parse_define(tokenList); 2073 | break; 2074 | case token_type.js: 2075 | expList.ast = parser_parse_js(tokenList); 2076 | break; 2077 | case token_type.include: 2078 | expList.ast = parser_parse_include(tokenList); 2079 | break; 2080 | case token_type.increment: 2081 | case token_type.decrement: 2082 | expList.ast = parser_parse_prepostfix(tokenList, false); 2083 | break; 2084 | case token_type.str: 2085 | expList.ast = parser_parse_str(tokenList); 2086 | break; 2087 | case token_type.for: 2088 | expList.ast = parser_parse_for(tokenList); 2089 | break; 2090 | case token_type.if: 2091 | expList.ast = parser_parse_ifelse(tokenList); 2092 | break; 2093 | case token_type.class: 2094 | expList.ast = parser_parse_class(tokenList); 2095 | break; 2096 | default: 2097 | parser_error(tokenList[hc.parser.index]); 2098 | } 2099 | 2100 | expList.next = parser_parse(tokenList); 2101 | 2102 | return expList; 2103 | }; 2104 | 2105 | /** 2106 | * Semantic analysis 2107 | * @arg {array} tokenList 2108 | */ 2109 | const parser = (tokenList) => tokenList && parser_parse(tokenList); 2110 | 2111 | /** 2112 | * code generation of mathematical expresions 2113 | * @arg {object} ast 2114 | */ 2115 | const output_out_math_exp = (first, ast) => { 2116 | let value = first; 2117 | while (ast) { 2118 | switch (ast.type) { 2119 | case token_type.div: 2120 | if (ast.right.token.type === token_type.number) { 2121 | value /= parseInt(ast.right.token.id); 2122 | } else { 2123 | value /= parseInt(hc.symtab.global[get_symtab(ast.right.token)].value); 2124 | } 2125 | break; 2126 | case token_type.mul: 2127 | if (ast.right.token.type === token_type.number) { 2128 | value *= parseInt(ast.right.token.id); 2129 | } else { 2130 | value *= parseInt(hc.symtab.global[get_symtab(ast.right.token)].value); 2131 | } 2132 | break; 2133 | case token_type.add: 2134 | if (ast.right.token.type === token_type.number) { 2135 | value += parseInt(ast.right.token.id); 2136 | } else { 2137 | value += parseInt(hc.symtab.global[get_symtab(ast.right.token)].value); 2138 | } 2139 | break; 2140 | case token_type.sub: 2141 | if (ast.right.token.type === token_type.number) { 2142 | value -= parseInt(ast.right.token.id); 2143 | } else { 2144 | value -= parseInt(hc.symtab.global[get_symtab(ast.right.token)].value); 2145 | } 2146 | break; 2147 | } 2148 | if (ast?.right?.right) { 2149 | ast = ast.right.right; 2150 | } else { 2151 | break; 2152 | } 2153 | if ( 2154 | ast.type !== token_type.add && 2155 | ast.type !== token_type.sub && 2156 | ast.type !== token_type.div && 2157 | ast.type !== token_type.mul 2158 | ) { 2159 | break; 2160 | } 2161 | } 2162 | return value; 2163 | }; 2164 | 2165 | /** 2166 | * code generation of logical expresions 2167 | * @arg {object} ast 2168 | * @arg {boolean} inside 2169 | */ 2170 | const output_out_logical_exp = (ast, inside, prototypeIndex) => { 2171 | let first; 2172 | let walk; 2173 | let value = { 2174 | number: 0, 2175 | boolean: false, 2176 | }; 2177 | 2178 | if (!inside && ast.left.left.token.type === token_type.not) { 2179 | first = ast.right.token; 2180 | walk = ast.right.right; 2181 | } else if (!inside) { 2182 | first = ast.left.left.token; 2183 | walk = ast.right; 2184 | } else if (inside && ast.right.token.type === token_type.not) { 2185 | first = ast.right.right.token; 2186 | walk = ast.right.right.right; 2187 | } else if (inside) { 2188 | first = ast.right.token; 2189 | walk = ast.right.right; 2190 | } 2191 | 2192 | if (first.type === token_type.id) { 2193 | let token = ''; 2194 | 2195 | if ((token = hc.symtab.prototypes[prototypeIndex]?.args?.find(e => e.id === first.id))) { 2196 | value.number = parseInt(token.value); 2197 | } else if ((token = hc.symtab.scoped[prototypeIndex]?.find(e => e.id === first.id))) { 2198 | value.number = parseInt(token.value); 2199 | } else { 2200 | value.number = parseInt(hc.symtab.global[get_symtab(first)].value); 2201 | } 2202 | } else { 2203 | value.number = parseInt(first.id); 2204 | } 2205 | 2206 | if ( 2207 | walk?.token.type === token_type.add || 2208 | walk?.token.type === token_type.sub || 2209 | walk?.token.type === token_type.div || 2210 | walk?.token.type === token_type.mul 2211 | ) { 2212 | value.number = output_out_math_exp(value.number, walk); 2213 | while (walk) { 2214 | walk = walk.right; 2215 | if (walk) { 2216 | if ( 2217 | walk.token.type === token_type.or || 2218 | walk.token.type === token_type.and || 2219 | walk.token.type === token_type.big || 2220 | walk.token.type === token_type.less || 2221 | walk.token.type === token_type.bigequal || 2222 | walk.token.type === token_type.lessequal || 2223 | walk.token.type === token_type.equal 2224 | ) { 2225 | break; 2226 | } 2227 | } 2228 | } 2229 | } 2230 | 2231 | if (value.number) { 2232 | value.boolean = true; 2233 | } 2234 | 2235 | let tokenValue; 2236 | while (walk) { 2237 | if ( 2238 | walk.right?.right?.token.type === token_type.add || 2239 | walk.right?.right?.token.type === token_type.sub || 2240 | walk.right?.right?.token.type === token_type.div || 2241 | walk.right?.right?.token.type === token_type.mul || 2242 | walk.right?.right?.token.type === token_type.not 2243 | ) { 2244 | let mathFirst; 2245 | if (walk.right.token.type === token_type.id) { 2246 | mathFirst = parseInt(hc.symtab.global[get_symtab(walk.right.token)].value); 2247 | } else { 2248 | mathFirst = parseInt(walk.right.token.id); 2249 | } 2250 | tokenValue = output_out_math_exp(mathFirst, walk.right.right); 2251 | } else if (walk.right.token.type === token_type.id) { 2252 | tokenValue = parseInt(hc.symtab.global[get_symtab(walk.right.token)].value); 2253 | } else if (walk.right.token.type === token_type.number) { 2254 | tokenValue = parseInt(walk.right.token.id); 2255 | } else if (walk.right.token.type === token_type.not) { 2256 | if (walk.right.right.token.type === token_type.id) { 2257 | tokenValue = parseInt( 2258 | hc.symtab.global[get_symtab(walk.right.right.token)].value 2259 | ); 2260 | } else if (walk.right.right.token.type === token_type.number) { 2261 | tokenValue = parseInt(walk.right.right.token.id); 2262 | } 2263 | } 2264 | 2265 | switch (walk.type) { 2266 | case token_type.less: 2267 | value.boolean = value.number < tokenValue ? true : false; 2268 | value.number = tokenValue; 2269 | break; 2270 | case token_type.lessequal: 2271 | value.boolean = value.number <= tokenValue ? true : false; 2272 | value.number = tokenValue; 2273 | break; 2274 | case token_type.big: 2275 | value.boolean = value.number > tokenValue ? true : false; 2276 | value.number = tokenValue; 2277 | break; 2278 | case token_type.bigequal: 2279 | value.boolean = value.number >= tokenValue ? true : false; 2280 | value.number = tokenValue; 2281 | break; 2282 | case token_type.or: 2283 | const inside = output_out_logical_exp(walk, true, prototypeIndex); 2284 | value.boolean = value.boolean || inside ? true : false; 2285 | while (walk) { 2286 | walk = walk.right; 2287 | if (walk && walk.token.type === token_type.or) break; 2288 | } 2289 | break; 2290 | case token_type.and: 2291 | value.boolean = value.boolean && (tokenValue ? true : false); 2292 | break; 2293 | case token_type.not: 2294 | value.boolean = !value.boolean; 2295 | break; 2296 | case token_type.equal: 2297 | value.boolean = value.number === tokenValue ? true : false; 2298 | break; 2299 | } 2300 | if (!walk) break; 2301 | walk = walk.right.right; 2302 | if (walk && walk?.token?.type === token_type.not) { 2303 | walk = walk.right; 2304 | } 2305 | } 2306 | 2307 | return value.boolean; 2308 | }; 2309 | 2310 | /** 2311 | * code generation of expresions 2312 | * @arg {object} ast 2313 | * @arg {boolean} left 2314 | */ 2315 | const output_out_exp = (ast, expList, left, prototypeIndex, procedureReturn) => { 2316 | if (ast?.left?.left?.token.id === "(") return; 2317 | let symTabI = -1; 2318 | let prototypeArgIndex = -1; 2319 | let procedureToken; 2320 | 2321 | if (check_ast_type(ast?.token.type, "data_type")) { 2322 | procedureToken = ast.left.token 2323 | if (prototypeIndex + 1 && hc.symtab.prototypes[prototypeIndex]?.args?.find(e => e.id === procedureToken.id) 2324 | || hc.symtab.scoped[prototypeIndex]?.find(e => e.id === procedureToken.id)) { 2325 | prototypeArgIndex = hc.symtab.prototypes[prototypeIndex]?.args.findIndex(e => e.id === procedureToken.id) 2326 | if (prototypeArgIndex < 0) { 2327 | prototypeArgIndex = hc.symtab.scoped[prototypeIndex].findIndex(e => e.id === procedureToken.id) 2328 | } 2329 | } else { 2330 | symTabI = get_symtab(ast.left.token); 2331 | } 2332 | } else if (ast?.token) { 2333 | procedureToken = ast.token 2334 | if (prototypeIndex + 1 && hc.symtab.prototypes[prototypeIndex]?.args.find(e => e.id === procedureToken.id) 2335 | || hc.symtab.scoped[prototypeIndex]?.find(e => e.id === procedureToken.id)) { 2336 | prototypeArgIndex = hc.symtab.prototypes[prototypeIndex]?.args.findIndex(e => e.id === procedureToken.id) 2337 | if (prototypeArgIndex < 0) { 2338 | prototypeArgIndex = hc.symtab.scoped[prototypeIndex].findIndex(e => e.id === procedureToken.id) 2339 | } 2340 | } else { 2341 | if (ast.token.type === token_type.id) { 2342 | symTabI = get_symtab(ast.token); 2343 | } 2344 | } 2345 | } else { 2346 | return; 2347 | } 2348 | 2349 | let value; 2350 | let procedureArg; 2351 | if (prototypeIndex + 1 && (procedureArg = hc.symtab.prototypes[prototypeIndex]?.args.find(e => e.id === procedureToken.id))) { 2352 | value = parseInt(procedureArg.value); 2353 | } else { 2354 | if (symTabI > -1) { 2355 | value = parseInt(hc.symtab.global[symTabI].value); 2356 | } else { 2357 | value = parseInt(procedureToken.id) 2358 | } 2359 | } 2360 | 2361 | let walk; 2362 | if (left) { 2363 | walk = ast.left; 2364 | } else if (procedureReturn) { 2365 | walk = ast.right 2366 | } else { 2367 | walk = ast; 2368 | } 2369 | 2370 | let classItems = []; 2371 | let lastWalk = null; 2372 | let first = walk; 2373 | 2374 | while (walk) { 2375 | switch (walk.type) { 2376 | case token_type.dot: 2377 | !lastWalk.left && classItems.push(lastWalk.token) 2378 | if (!walk.right?.right) { 2379 | classItems.push(walk.right.token) 2380 | 2381 | value = 0; 2382 | while (first) { 2383 | switch (first.type) { 2384 | case token_type.assig: 2385 | if (first.left.token.type === token_type.number) { 2386 | value = parseInt(first.left.token.id); 2387 | } 2388 | break 2389 | case token_type.div: 2390 | if (first.left.token.type === token_type.number) { 2391 | value /= parseInt(first.left.token.id); 2392 | } 2393 | break; 2394 | } 2395 | first = first.left; 2396 | } 2397 | 2398 | 2399 | if (!procedureReturn) { 2400 | if (symTabI >= 0) { 2401 | if (!Array.isArray(hc.symtab.global[symTabI].value)) { 2402 | hc.symtab.global[symTabI].value = [] 2403 | } 2404 | hc.symtab.global[symTabI].value.push({ 2405 | id: classItems.map(e => e.id).join('.'), 2406 | value: value 2407 | }) 2408 | } else if (hc.symtab.prototypes[prototypeIndex].args[prototypeArgIndex]?.id === procedureToken.id) { 2409 | if (!Array.isArray(hc.symtab.prototypes[prototypeIndex].args[prototypeArgIndex].value)) { 2410 | hc.symtab.prototypes[prototypeIndex].args[prototypeArgIndex].value = [] 2411 | } 2412 | hc.symtab.prototypes[prototypeIndex].args[prototypeArgIndex].value.push({ 2413 | id: classItems.map(e => e.id).join('.'), 2414 | value: value 2415 | }) 2416 | } else { 2417 | if (!Array.isArray(hc.symtab.scoped[prototypeIndex][prototypeArgIndex].value)) { 2418 | hc.symtab.scoped[prototypeIndex][prototypeArgIndex].value = [] 2419 | } 2420 | hc.symtab.scoped[prototypeIndex][prototypeArgIndex].value.push({ 2421 | id: classItems.map(e => e.id).join('.'), 2422 | value: value 2423 | }) 2424 | } 2425 | } 2426 | } 2427 | break 2428 | case token_type.div: 2429 | case token_type.assingdiv: 2430 | if (walk.right.token.type === token_type.number) { 2431 | value /= parseInt(walk.right.token.id); 2432 | } else { 2433 | let procedureArg; 2434 | if (prototypeIndex + 1 && (procedureArg = hc.symtab.prototypes[prototypeIndex]?.args.find(e => e.id === walk.right.token.id)) 2435 | || (procedureArg = hc.symtab.scoped[prototypeIndex]?.find(e => e.id === walk.right.token.id))) { 2436 | if (walk.right.token.type === token_type.classExp) { 2437 | value /= procedureArg.value.find(e => e.id === classExpIds.join('.'))?.value 2438 | } else { 2439 | value /= parseInt(procedureArg.value); 2440 | } 2441 | } else if ((procedureArg = hc.symtab.prototypes.find(e => e.id === walk.right.token.id))) { 2442 | output_out_procedures(walk.right, expList) 2443 | value /= procedureArg.return; 2444 | } else { 2445 | value /= parseInt(hc.symtab.global[get_symtab(walk.right.token)].value); 2446 | } 2447 | } 2448 | if (!procedureReturn) { 2449 | if (symTabI >= 0) { 2450 | hc.symtab.global[symTabI].value = value 2451 | } else if (hc.symtab.prototypes[prototypeIndex].args[prototypeArgIndex]?.id === procedureToken.id) { 2452 | hc.symtab.prototypes[prototypeIndex].args[prototypeArgIndex].value = value 2453 | } else { 2454 | hc.symtab.scoped[prototypeIndex][prototypeArgIndex].value = value 2455 | } 2456 | } 2457 | break; 2458 | case token_type.mul: 2459 | case token_type.assingmul: 2460 | if (walk.right.token.type === token_type.number) { 2461 | value *= parseInt(walk.right.token.id); 2462 | } else { 2463 | let procedureArg; 2464 | if (prototypeIndex + 1 && (procedureArg = hc.symtab.prototypes[prototypeIndex]?.args.find(e => e.id === walk.right.token.id)) 2465 | || (procedureArg = hc.symtab.scoped[prototypeIndex]?.find(e => e.id === walk.right.token.id))) { 2466 | if (walk.right.token.type === token_type.classExp) { 2467 | value *= procedureArg.value.find(e => e.id === classExpIds.join('.'))?.value 2468 | } else { 2469 | value *= parseInt(procedureArg.value); 2470 | } 2471 | } else if ((procedureArg = hc.symtab.prototypes.find(e => e.id === walk.right.token.id))) { 2472 | output_out_procedures(walk.right, expList) 2473 | value *= procedureArg.return; 2474 | } else { 2475 | value *= parseInt(hc.symtab.global[get_symtab(walk.right.token)].value); 2476 | } 2477 | } 2478 | if (!procedureReturn) { 2479 | if (symTabI >= 0) { 2480 | hc.symtab.global[symTabI].value = value 2481 | } else if (hc.symtab.prototypes[prototypeIndex].args[prototypeArgIndex]?.id === procedureToken.id) { 2482 | hc.symtab.prototypes[prototypeIndex].args[prototypeArgIndex].value = value 2483 | } else { 2484 | hc.symtab.scoped[prototypeIndex][prototypeArgIndex].value = value 2485 | } 2486 | } 2487 | break; 2488 | case token_type.assig: 2489 | let classExpIds = '' 2490 | if (walk.right.token.type === token_type.classExp) { 2491 | classExpIds = walk.right.token.id.split('.') 2492 | walk.right.token.id = classExpIds[0] 2493 | classExpIds.shift() 2494 | } 2495 | if (walk.right.token.type === token_type.number) { 2496 | value = parseInt(walk.right.token.id); 2497 | } else { 2498 | let procedureArg; 2499 | if (prototypeIndex + 1 2500 | && (procedureArg = hc.symtab.prototypes[prototypeIndex]?.args.find(e => e.id === walk.right.token.id)) 2501 | || (procedureArg = hc.symtab.scoped[prototypeIndex]?.find(e => e.id === walk.right.token.id))) { 2502 | 2503 | if (walk.right.token.type === token_type.classExp) { 2504 | value = procedureArg.value.find(e => e.id === classExpIds.join('.'))?.value 2505 | } else { 2506 | value = parseInt(procedureArg.value); 2507 | } 2508 | 2509 | } else if ((procedureArg = hc.symtab.prototypes.find(e => e.id === walk.right.token.id))) { 2510 | output_out_procedures(walk.right, expList) 2511 | value = procedureArg.return; 2512 | } else { 2513 | if (walk.right.token.type === token_type.classExp) { 2514 | value = parseInt(hc.symtab.global[get_symtab(walk.right.token)].value.find(e => e.id === classExpIds.join('.'))?.value) 2515 | } else { 2516 | value = parseInt(hc.symtab.global[get_symtab(walk.right.token)].value); 2517 | } 2518 | } 2519 | } 2520 | 2521 | if (!procedureReturn) { 2522 | if (symTabI >= 0) { 2523 | hc.symtab.global[symTabI].value = value 2524 | } else if (hc.symtab.prototypes[prototypeIndex].args[prototypeArgIndex]?.id === procedureToken.id) { 2525 | hc.symtab.prototypes[prototypeIndex].args[prototypeArgIndex].value = value 2526 | } else { 2527 | hc.symtab.scoped[prototypeIndex][prototypeArgIndex].value = value 2528 | } 2529 | } 2530 | break; 2531 | case token_type.assingsum: 2532 | case token_type.add: 2533 | if (walk.right.token.type === token_type.number) { 2534 | value += parseInt(walk.right.token.id); 2535 | } else { 2536 | let procedureArg; 2537 | if (prototypeIndex + 1 && (procedureArg = hc.symtab.prototypes[prototypeIndex]?.args.find(e => e.id === walk.right.token.id)) 2538 | || (procedureArg = hc.symtab.scoped[prototypeIndex]?.find(e => e.id === walk.right.token.id))) { 2539 | if (walk.right.token.type === token_type.classExp) { 2540 | value += procedureArg.value.find(e => e.id === classExpIds.join('.'))?.value 2541 | } else { 2542 | value += parseInt(procedureArg.value); 2543 | } 2544 | } else if ((procedureArg = hc.symtab.prototypes.find(e => e.id === walk.right.token.id))) { 2545 | output_out_procedures(walk.right, expList) 2546 | value += procedureArg.return; 2547 | } else { 2548 | value += parseInt(hc.symtab.global[get_symtab(walk.right.token)].value); 2549 | } 2550 | } 2551 | if (!procedureReturn) { 2552 | if (symTabI >= 0) { 2553 | hc.symtab.global[symTabI].value = value 2554 | } else if (hc.symtab.prototypes[prototypeIndex].args[prototypeArgIndex]?.id === procedureToken.id) { 2555 | hc.symtab.prototypes[prototypeIndex].args[prototypeArgIndex].value = value 2556 | } else { 2557 | hc.symtab.scoped[prototypeIndex][prototypeArgIndex].value = value 2558 | } 2559 | } 2560 | break; 2561 | case token_type.assingsub: 2562 | case token_type.sub: 2563 | if (walk.right.token.type === token_type.number) { 2564 | value -= parseInt(walk.right.token.id); 2565 | } else { 2566 | let procedureArg; 2567 | if (prototypeIndex + 1 && (procedureArg = hc.symtab.prototypes[prototypeIndex]?.args.find(e => e.id === walk.right.token.id)) 2568 | || (procedureArg = hc.symtab.scoped[prototypeIndex]?.find(e => e.id === walk.right.token.id))) { 2569 | if (walk.right.token.type === token_type.classExp) { 2570 | value -= procedureArg.value.find(e => e.id === classExpIds.join('.'))?.value 2571 | } else { 2572 | value -= parseInt(procedureArg.value); 2573 | } 2574 | } else if ((procedureArg = hc.symtab.prototypes.find(e => e.id === walk.right.token.id))) { 2575 | output_out_procedures(walk.right, expList) 2576 | value -= procedureArg.return; 2577 | } else { 2578 | value -= parseInt(hc.symtab.global[get_symtab(walk.right.token)].value); 2579 | } 2580 | } 2581 | if (!procedureReturn) { 2582 | if (symTabI >= 0) { 2583 | hc.symtab.global[symTabI].value = value 2584 | } else if (hc.symtab.prototypes[prototypeIndex].args[prototypeArgIndex]?.id === procedureToken.id) { 2585 | hc.symtab.prototypes[prototypeIndex].args[prototypeArgIndex].value = value 2586 | } else { 2587 | hc.symtab.scoped[prototypeIndex][prototypeArgIndex].value = value 2588 | } 2589 | } 2590 | break 2591 | case token_type.semi: 2592 | return walk; 2593 | } 2594 | lastWalk = walk 2595 | walk = walk.right; 2596 | } 2597 | 2598 | if (check_ast_type(ast?.left?.right?.token.type, "id")) { 2599 | output_out_exp(ast.left.right, expList, true, prototypeIndex, procedureReturn); 2600 | } else if (check_ast_type(ast?.right?.token.type, "id")) { 2601 | output_out_exp(ast.right, expList, true, prototypeIndex, procedureReturn); 2602 | } 2603 | 2604 | if (procedureReturn) { 2605 | hc.symtab.prototypes[prototypeIndex].return = value; 2606 | } 2607 | 2608 | return walk; 2609 | }; 2610 | 2611 | /** 2612 | * code generation check procedure ast 2613 | * @arg {object} ast 2614 | * @arg {object} id 2615 | */ 2616 | const output_out_get_ast_check = (ast, id) => { 2617 | if (ast.left.token.id === id.id) return true; 2618 | return false; 2619 | }; 2620 | 2621 | /** 2622 | * code generation get ast procedure 2623 | * @arg {object} ast 2624 | * @arg {object} id 2625 | */ 2626 | const output_out_get_ast = (expList, id) => { 2627 | if (!expList) return null; 2628 | if (check_ast_type(expList.ast.type, "data_type")) { 2629 | let ret = output_out_get_ast_check(expList.ast, id); 2630 | if (ret) { 2631 | return expList.ast; 2632 | } 2633 | } 2634 | return output_out_get_ast(expList.next, id); 2635 | }; 2636 | 2637 | /** 2638 | * code generation of blocks 2639 | * @arg {object} walk 2640 | * @arg {array} expList 2641 | */ 2642 | const output_out_block = (walk, expList, prototypeIndex) => { 2643 | if (!walk) return; 2644 | 2645 | switch (walk.type) { 2646 | case token_type.bool: 2647 | case token_type.i0: 2648 | case token_type.u0: 2649 | case token_type.i8: 2650 | case token_type.u8: 2651 | case token_type.i16: 2652 | case token_type.u16: 2653 | case token_type.i32: 2654 | case token_type.u32: 2655 | case token_type.i64: 2656 | case token_type.u64: 2657 | case token_type.f64: 2658 | case token_type.id: 2659 | if (walk.left) { // fix two entries in exp 2660 | output_out_exp(walk, expList, false, prototypeIndex); 2661 | } 2662 | break; 2663 | case token_type.if: 2664 | const ifElseVal = output_out_ifelse(walk, expList, prototypeIndex); 2665 | if (ifElseVal === "{RETURN}") return ifElseVal; 2666 | break; 2667 | case token_type.for: 2668 | const forVal = output_out_for(walk, expList, prototypeIndex); 2669 | if (forVal === "{RETURN}") return forVal; 2670 | break; 2671 | case token_type.str: 2672 | printf(walk, prototypeIndex); 2673 | break; 2674 | case token_type.call: 2675 | output_out_procedures(walk, expList) 2676 | break; 2677 | case token_type.return: 2678 | output_out_return(walk, expList, prototypeIndex); 2679 | return "{RETURN}"; 2680 | default: 2681 | break; 2682 | } 2683 | 2684 | output_out_block(walk.right, expList, prototypeIndex); 2685 | return output_out_block(walk.next, expList, prototypeIndex); 2686 | }; 2687 | 2688 | /** 2689 | * code generation of if statement 2690 | * @arg {object} ast 2691 | */ 2692 | const output_out_ifelse = (ast, expList, prototypeIndex) => { 2693 | const logical = output_out_logical_exp(ast, false, prototypeIndex); 2694 | 2695 | let elseBlock; 2696 | if (ast.left?.left?.left?.left) { 2697 | elseBlock = ast.left.left.left.left.right; 2698 | } 2699 | 2700 | let outBlockVal = ''; 2701 | if (logical) { 2702 | outBlockVal = output_out_block(ast.left.left.left.right, expList, prototypeIndex); 2703 | } else if (elseBlock) { 2704 | outBlockVal = output_out_block(elseBlock, expList, prototypeIndex); 2705 | } 2706 | 2707 | if (!logical && ast?.left?.left?.left?.next?.token.type === token_type.if) { 2708 | output_out_ifelse(ast.left.left.left.next, expList); 2709 | } 2710 | 2711 | return outBlockVal; 2712 | }; 2713 | 2714 | const output_out_js = (ast) => { 2715 | eval(ast.left.left.token.id) 2716 | } 2717 | 2718 | /** 2719 | * code generation of for statement 2720 | * @arg {object} ast 2721 | * @arg {array} expList 2722 | */ 2723 | const output_out_for = (ast, expList, prototypeIndex) => { 2724 | let symTabI = get_symtab(ast.left.token); 2725 | if (!symTabI) { 2726 | symTabI = hc.symtab.prototypes[prototypeIndex]?.args?.findIndex(e => e.id === ast.left.token.id) 2727 | if (symTabI < 0) { 2728 | symTabI = hc.symtab.scoped[prototypeIndex]?.findIndex(e => e.id === ast.left.token.id) 2729 | } 2730 | } 2731 | const val = parseInt(ast.right.right.token.id); 2732 | const cond = ast.left.left.left.left.token; 2733 | const condVal = parseInt(ast.left.left.left.left.left.token.id); 2734 | let iterate = ast.left.left.left.left.left.left.left; 2735 | 2736 | if (iterate.type === token_type.id) { 2737 | iterate = ast.left.left.left.left.left.left.left.right; 2738 | } 2739 | 2740 | let iterateValue; 2741 | if (check_ast_type(iterate.type, "assignment_operator")) { 2742 | iterateValue = parseInt(iterate.right.token.id); 2743 | } else { 2744 | iterateValue = 1; 2745 | } 2746 | 2747 | let blockVal = ''; 2748 | switch (cond.type) { 2749 | case token_type.less: 2750 | for (let i = val; i < condVal; i += iterateValue) { 2751 | blockVal = output_out_block(ast.left.left.left.left.left.left.right, expList, prototypeIndex); 2752 | if (blockVal === "{RETURN}") return blockVal; 2753 | if (hc.symtab.global[symTabI]?.id === ast.left.token.id) { 2754 | hc.symtab.global[symTabI].value = 2755 | parseInt(hc.symtab.global[symTabI].value) + iterateValue; 2756 | } else if (hc.symtab.prototypes[prototypeIndex]?.args[symTabI]?.id === ast.left.token.id) { 2757 | hc.symtab.prototypes[prototypeIndex].args[symTabI].value = 2758 | parseInt(hc.symtab.prototypes[prototypeIndex].args[symTabI].value) + iterateValue; 2759 | } else { 2760 | hc.symtab.scoped[prototypeIndex][symTabI].value = 2761 | parseInt(hc.symtab.scoped[prototypeIndex][symTabI].value) + iterateValue; 2762 | } 2763 | } 2764 | break; 2765 | case token_type.big: 2766 | for (let i = val; i > condVal; i += iterateValue) { 2767 | blockVal = output_out_block(ast.left.left.left.left.left.left.right, expList, prototypeIndex); 2768 | if (blockVal === "{RETURN}") return blockVal; 2769 | if (hc.symtab.global[symTabI]?.id === ast.left.token.id) { 2770 | hc.symtab.global[symTabI].value = 2771 | parseInt(hc.symtab.global[symTabI].value) + iterateValue; 2772 | } else if (hc.symtab.prototypes[prototypeIndex]?.args[symTabI]?.id === ast.left.token.id) { 2773 | hc.symtab.prototypes[prototypeIndex].args[symTabI].value = 2774 | parseInt(hc.symtab.prototypes[prototypeIndex].args[symTabI].value) + iterateValue; 2775 | } else { 2776 | hc.symtab.scoped[prototypeIndex][symTabI].value = 2777 | parseInt(hc.symtab.scoped[prototypeIndex][symTabI].value) + iterateValue; 2778 | } 2779 | } 2780 | break; 2781 | } 2782 | 2783 | return blockVal; 2784 | }; 2785 | 2786 | /** 2787 | * code generation of procedures 2788 | * @arg {object} expListAux 2789 | * @arg {array} expList 2790 | */ 2791 | const output_out_procedures = (ast, expList) => { 2792 | const procedureAst = output_out_get_ast(expList, ast.token) 2793 | const prototypeIndex = get_prototype(procedureAst?.left?.token) 2794 | let aux = ast.right; 2795 | let i = 0; 2796 | 2797 | while (hc.symtab.prototypes[prototypeIndex].args[i] && aux) { 2798 | if (aux.token.type === token_type.id) { 2799 | hc.symtab.prototypes[prototypeIndex].args[i].value = hc.symtab.global[get_symtab(aux.token)].value 2800 | } else if (aux.token.type === token_type.number) { 2801 | hc.symtab.prototypes[prototypeIndex].args[i].value = aux.token.id 2802 | } 2803 | i++; 2804 | aux = aux.right; 2805 | } 2806 | 2807 | output_out_block(procedureAst.right, expList, prototypeIndex) 2808 | }; 2809 | 2810 | /** 2811 | * code generation of return procedures 2812 | * @arg {object} expListAux 2813 | * @arg {number} prototypeIndex 2814 | */ 2815 | const output_out_return = (ast, expList, prototypeIndex) => { 2816 | output_out_exp(ast.right, expList, false, prototypeIndex, true); 2817 | return; 2818 | }; 2819 | 2820 | /** 2821 | * output generation 2822 | * @arg {array} expList 2823 | */ 2824 | const output = async (expList) => { 2825 | if (!expList) return hc.files.stdout; 2826 | 2827 | let expListAux = expList; 2828 | 2829 | do { 2830 | switch (expListAux.ast.type) { 2831 | case token_type.bool: 2832 | case token_type.i0: 2833 | case token_type.u0: 2834 | case token_type.i8: 2835 | case token_type.u8: 2836 | case token_type.i16: 2837 | case token_type.u16: 2838 | case token_type.i32: 2839 | case token_type.u32: 2840 | case token_type.i64: 2841 | case token_type.u64: 2842 | case token_type.f64: 2843 | case token_type.id: 2844 | output_out_exp(expListAux.ast, expList); 2845 | break; 2846 | case token_type.if: 2847 | output_out_ifelse(expListAux.ast, expList); 2848 | break; 2849 | case token_type.for: 2850 | output_out_for(expListAux.ast, expList); 2851 | break; 2852 | case token_type.js: 2853 | output_out_js(expListAux.ast); 2854 | break; 2855 | case token_type.str: 2856 | let walk = expListAux.ast; 2857 | do { 2858 | printf(walk); 2859 | walk = walk.right; 2860 | } while (walk); 2861 | break; 2862 | case token_type.call: 2863 | output_out_procedures(expListAux.ast, expList) 2864 | break; 2865 | default: 2866 | break; 2867 | } 2868 | 2869 | expListAux = expListAux.next; 2870 | } while (expListAux); 2871 | 2872 | return hc.files.stdout; 2873 | }; 2874 | 2875 | /** 2876 | * STDIO 2877 | */ 2878 | 2879 | /** 2880 | * printf function 2881 | * @arg {object} ast 2882 | */ 2883 | const printf = (ast, prototypeIndex) => { 2884 | let str = ast.token.id; 2885 | if (ast.token.id.includes("%")) { 2886 | let procedureArg; 2887 | if (prototypeIndex + 1 && (procedureArg = hc.symtab.prototypes[prototypeIndex]?.args.find(e => e.id === ast.left.right.token.id)) 2888 | || (procedureArg = hc.symtab.scoped[prototypeIndex]?.find(e => e.id === ast.left.right.token.id))) { 2889 | str = str.replace(/%d/g, procedureArg.value); 2890 | } else { 2891 | if (ast.left.right.token.type === token_type.number) { 2892 | str = str.replace(/%d/g, ast.left.right.token.id); 2893 | } else { 2894 | if (ast.left.right.token.type === token_type.classExp) { 2895 | let classexp = ast.left.right.token.id.split('.') 2896 | let args = ast.left.right.token; 2897 | 2898 | args.id = classexp.shift() 2899 | str = str.replace(/%d/g, hc.symtab.global[get_symtab(args)].value.find(e => e.id === classexp.join('.'))?.value); 2900 | } else { 2901 | str = str.replace(/%d/g, hc.symtab.global[get_symtab(ast.left.right.token)].value); 2902 | } 2903 | } 2904 | } 2905 | } 2906 | str = str.replace(/undefined/g, 'NULL'); 2907 | str = str.replace(/null/g, 'NULL'); 2908 | hc.files.stdout += str.replace(/\\n|\\t/g, (e) => { 2909 | switch (e) { 2910 | case "\\r": 2911 | case "\\n": 2912 | return "\n"; 2913 | case "\\t": 2914 | return " "; 2915 | default: 2916 | return e; 2917 | } 2918 | }); 2919 | }; 2920 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | HolyC Interpreter 8 | 82 | 180 | 181 | 182 | 183 | 193 | 194 |
195 |
196 | 197 | 198 | "HolyC 199 | Interpreter" 200 | 201 | 202 |
203 |
204 | 205 |

206 | (Interpreter & site by 207 | Leonardo) 208 |

209 | 210 |
211 | 212 | 213 | 214 | 226 | 227 | 228 | 229 |
230 |
231 |
232 | 233 |
234 | 235 |
236 | 237 |
238 | 239 |
240 |
241 |
242 |
243 | 244 |

245 | A small sandboxed handwritten HolyC interpreter in JavaScript; 246 | All current features 247 | are exemplified in the Examples select. New releases and updates are shared on r/TempleOS_Official. 249 |

250 | 251 |
252 | 253 |

254 | If you want to learn more about HolyC, you can use this documentation. 255 |

256 | 257 |
258 | 259 |

260 | This site is under MIT license; 262 | Converted TempleOS font by Gaven Rendell; 263 | HolyC and TempleOS are creations of Terry Davis. 264 |

265 | 266 |
267 | 268 |

269 | If you want to support this project you can star the repository. 270 |

271 |
272 | 273 | 274 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | minify: 4 | minify --output holy.js holyc-interpreter.js 5 | 6 | -------------------------------------------------------------------------------- /nix/shell.nix: -------------------------------------------------------------------------------- 1 | with import {}; 2 | 3 | pkgs.mkShell { 4 | name = "nix-holy-interpreter-environment"; 5 | version = "1.0.0"; 6 | 7 | nativeBuildInputs = [ 8 | bun 9 | minify 10 | script 11 | ]; 12 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "name": "holyc-interpreter", 4 | "version": "1.0.0", 5 | "description": "", 6 | "main": "index.js", 7 | "scripts": { 8 | "start:nix": "steam-run $(which bun) dev", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC" 14 | } 15 | -------------------------------------------------------------------------------- /templeos_font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leozamboni/HolyC-interpreter/12314c86b08ace50ca32a46999618e8e3280b05a/templeos_font.ttf --------------------------------------------------------------------------------