├── .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 | RUN(▶)
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 | * RUN(▶)
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 |
184 |
185 | WARNING: JavaScript is Disabled!
186 |
187 |
188 |
189 | HOLYC Interpreter requires JavaScript. Please enable JavaScript for the
190 | interpreter to work.
191 |
192 |
193 |
194 |
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
--------------------------------------------------------------------------------