├── nyx_test ├── tiresome │ ├── empty.nyx │ ├── bitwise.nyx │ ├── assert.nyx │ ├── hello.nyx │ ├── sideeffect.nyx │ ├── compound_assign.nyx │ ├── chars.nyx │ ├── stubborn.nyx │ ├── call_member.nyx │ ├── str.nyx │ ├── condition.nyx │ ├── arithmetic.nyx │ ├── builtin.nyx │ ├── array.nyx │ ├── statement.nyx │ ├── pattern_matching.nyx │ ├── closure.nyx │ └── function.nyx └── example │ ├── 9x9table.nyx │ ├── ast.nyx │ ├── fibonacci.nyx │ ├── love.nyx │ ├── prime.nyx │ ├── reverse_array.nyx │ ├── narcissistic_number.nyx │ ├── quick_sort.nyx │ └── yang_hui_san_jiao.nyx ├── .clang-format ├── .gitignore ├── docs ├── development.md └── reference.md ├── CMakeLists.txt ├── LICENSE ├── nyx ├── Main.cpp ├── Utils.hpp ├── Interpreter.h ├── Builtin.h ├── Utils.cpp ├── Debug.hpp ├── Parser.h ├── Runtime.hpp ├── Object.hpp ├── Builtin.cpp ├── Runtime.cpp ├── Debug.cpp ├── Ast.h ├── Object.cpp ├── Interpreter.cpp └── Parser.cpp └── README.md /nyx_test/tiresome/empty.nyx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nyx_test/tiresome/bitwise.nyx: -------------------------------------------------------------------------------- 1 | println(3&5) 2 | println(4|66) 3 | println(~43) -------------------------------------------------------------------------------- /nyx_test/tiresome/assert.nyx: -------------------------------------------------------------------------------- 1 | assert(true, "ok") 2 | assert(3==3 && 4==3+1,"aaa bbb") -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | 3 | BasedOnStyle: Chromium 4 | AccessModifierOffset: -4 5 | IndentWidth: 4 6 | TabWidth: 4 -------------------------------------------------------------------------------- /nyx_test/tiresome/hello.nyx: -------------------------------------------------------------------------------- 1 | a = "hello world" 2 | b = 32.1234 3 | c =235423 4 | 5 | d = println("hello world",3.14,true,"another",a,b,c,null) 6 | println(d) -------------------------------------------------------------------------------- /nyx_test/example/9x9table.nyx: -------------------------------------------------------------------------------- 1 | for(a:range(1,10)){ 2 | for(b=1;b<=a;b+=1){ 3 | print(b+"x"+a+"="+a*b+" ") 4 | } 5 | println() 6 | } 7 | 8 | -------------------------------------------------------------------------------- /nyx_test/example/ast.nyx: -------------------------------------------------------------------------------- 1 | func fibonacci_rec(num){ 2 | if(num<=2){ 3 | return 1 4 | } 5 | return fibonacci_rec(num-2) + fibonacci_rec(num-1) 6 | } 7 | dump_ast(fibonacci_rec) -------------------------------------------------------------------------------- /nyx_test/tiresome/sideeffect.nyx: -------------------------------------------------------------------------------- 1 | func sideeffect(arr, i){ 2 | println(arr) 3 | if(i==arr.length()){return} 4 | arr[i] = 0 5 | sideeffect(arr,i+1) 6 | } 7 | 8 | arr=[ 3,2,6,7,87,2] 9 | assert(arr==[ 3,2,6,7,87,2]) 10 | sideeffect(arr,0) 11 | assert(arr==[0,0,0,0,0,0]) 12 | println(arr) 13 | -------------------------------------------------------------------------------- /nyx_test/example/fibonacci.nyx: -------------------------------------------------------------------------------- 1 | # use recursion rather than for-loop 2 | func fibonacci_rec(num){ 3 | if(num<=2){ 4 | return 1 5 | } 6 | return fibonacci_rec(num-2) + fibonacci_rec(num-1) 7 | } 8 | 9 | for(i=1;i<20;i+=1){ 10 | print("fibonacci("+i+")=") 11 | println(fibonacci_rec(i)) 12 | } -------------------------------------------------------------------------------- /nyx_test/tiresome/compound_assign.nyx: -------------------------------------------------------------------------------- 1 | a = 1 2 | a += 1 3 | assert(a==2) 4 | a -= 3 5 | assert(a==-1) 6 | a *= 4 7 | assert(a==-4) 8 | a /= 2 9 | assert(a==-2) 10 | a %= 2 11 | assert(a==0) 12 | a = 2.718 13 | a += 1 14 | a -= 3 15 | a *= 4 16 | a /= 2 17 | println(a) 18 | b = [1,2,4] 19 | b += 3 20 | assert(b==[1,2,4,3]) 21 | -------------------------------------------------------------------------------- /nyx_test/example/love.nyx: -------------------------------------------------------------------------------- 1 | a=0 2 | 3 | for (y = 1.5; y > -1.5; y -= 0.1) { 4 | for (x = -1.5; x < 1.5; x += 0.05) { 5 | a = x * x + y * y - 1 6 | ch = '' 7 | if(a * a * a - x * x * y * y * y <= 0.0) { ch = '*' } else { ch = ' '} 8 | print(ch) 9 | } 10 | println() 11 | } 12 | 13 | 14 | -------------------------------------------------------------------------------- /nyx_test/tiresome/chars.nyx: -------------------------------------------------------------------------------- 1 | println('a'+3) 2 | b = 'c' -'a' 3 | println(b) 4 | println(b+'a') 5 | println(("test"+'a')=="testa") 6 | println("atest"==('a'+"test")) 7 | println(("test"*3)=="testtesttest") 8 | println("testtesttest"==(3*"test")) 9 | println(true==('a'=='a')) 10 | println(('a'!='b')==true) 11 | println(typeof(b)=="char") -------------------------------------------------------------------------------- /nyx_test/tiresome/stubborn.nyx: -------------------------------------------------------------------------------- 1 | func weird(num){ 2 | closure_arr = [] 3 | for(i:range(num)){ 4 | closure_arr += func(){ 5 | return i 6 | } 7 | } 8 | println(closure_arr) 9 | return closure_arr 10 | } 11 | for(closure:weird(3)){ 12 | println(closure+" produces the result:"+closure()) 13 | } -------------------------------------------------------------------------------- /nyx_test/tiresome/call_member.nyx: -------------------------------------------------------------------------------- 1 | 2 | c = [3,[],'c'] 3 | assert(c.length()==3) 4 | assert("".length()==0) 5 | c="abc" 6 | assert(3==c.length()) 7 | c=[1,[2,3],[],6] 8 | d = c.length() 9 | assert(d==4) 10 | assert(c[1].length()==2) 11 | assert(0==c[2].length()) 12 | p=[2,3,5,6,7] 13 | c[1]=p 14 | assert(c[1].length() == p.length()) 15 | assert(p.length()!= 2) -------------------------------------------------------------------------------- /nyx_test/example/prime.nyx: -------------------------------------------------------------------------------- 1 | # general solution 2 | func prime(x){ 3 | for(i=2;i3 && 6<10 && (14>=13||13<=15)) 15 | assert('c'=='c'&&'p'!='q') -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | .idea 35 | cmake-build-debug/ 36 | .vs 37 | /docs/_build 38 | -------------------------------------------------------------------------------- /nyx_test/tiresome/arithmetic.nyx: -------------------------------------------------------------------------------- 1 | d = (3+2)*4+(6*5)-8/2+(3+2*(5-4)) 2 | f = -7 3 | a = 3+2-5 4 | b = 3+5*2 5 | println(f,a,b,b+12,d) 6 | println(3+2-5*5) 7 | println(d+a+b+f) 8 | println(d+a+10+b+f) 9 | q = (((((((((((((((((((1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1 10 | m =5%2 11 | m1 = 5%3 + 5%5 + 5%6 12 | m3 = 10%2+10+10%3 13 | println(m,m1,m3) 14 | println(q,q/21,q/22,q/1.0) 15 | println((((((((((((((((1+1)))))))))))))))) 16 | println((((((((((((((((ff=15&5|12)))))))))))))))) -------------------------------------------------------------------------------- /nyx_test/tiresome/builtin.nyx: -------------------------------------------------------------------------------- 1 | a = 123 2 | b = 3.14 3 | c = "hello" 4 | d = null 5 | e = true 6 | f = [] 7 | g='x' 8 | assert(typeof(a) == "int") 9 | assert(typeof(b) == "double") 10 | assert(typeof(c)=="string") 11 | assert(typeof(d)=="null") 12 | assert(typeof(e)=="bool") 13 | assert(typeof(f)=="array") 14 | assert(typeof(g)=="char") 15 | assert(length(f)==0) 16 | assert(length(f)==0) 17 | assert(length([1,2,3])==3) 18 | assert(typeof([1,2,3])=="array") 19 | assert(length("test")==4) 20 | assert(typeof("test")=="string") 21 | #println(input()) -------------------------------------------------------------------------------- /nyx_test/example/reverse_array.nyx: -------------------------------------------------------------------------------- 1 | func reverse_array(arr) 2 | { 3 | left = 0 4 | right = arr.length()-1 5 | 6 | while(leftright) { 3 | return 4 | } 5 | i = left 6 | j = right 7 | pivot = arr[left] 8 | while(ipivot){ 10 | j-=1 11 | } 12 | if(i " + arr) -------------------------------------------------------------------------------- /nyx_test/example/yang_hui_san_jiao.nyx: -------------------------------------------------------------------------------- 1 | # console printing: 2 | # n = 1 [1] 3 | # n = 2 [1,1] 4 | # n = 3 [1,2,1] 5 | # n = 4 [1,3,3,1] 6 | # n = 5 [1,4,6,4,1] 7 | # n = 6 [1,5,10,10,5,1] 8 | # n = 7 [1,6,15,20,15,6,1] 9 | # n = 8 [1,7,21,35,35,21,7,1] 10 | # n = 9 [1,8,28,56,70,56,28,8,1] 11 | 12 | func yang_hui_san_jiao(limit){ 13 | row = [1] 14 | i = 1 15 | while(i=-100;i-=1){ 55 | arr+=i 56 | } 57 | return arr 58 | } 59 | 60 | for(t: test()){ 61 | println(t) 62 | } 63 | for(i:range(10)){ 64 | println(i) 65 | } 66 | for(i:range(-1)){ 67 | println(i) 68 | } -------------------------------------------------------------------------------- /nyx_test/tiresome/pattern_matching.nyx: -------------------------------------------------------------------------------- 1 | a = 3 2 | 3 | match(a){ 4 | 5 | } 6 | 7 | match(a+1){ 8 | 3 => println("3!") 9 | _ => println("otherwise") 10 | 4 => println("4!") 11 | 5 => println("5!") 12 | 13 | } 14 | 15 | match(a+1){ 16 | 11 => {println("3!") 17 | println("4!")} 18 | 5 => println("5!") 19 | _ => println("otherwise") 20 | } 21 | 22 | match([1,2,3]){ 23 | [1,2]=>{ 24 | println("impossible") 25 | } 26 | 27 | [1,3,2]=>{ 28 | println("impossible") 29 | } 30 | 31 | [1,2,3]=>{ 32 | println("ook") 33 | } 34 | 35 | _=> println("impossible") 36 | } 37 | 38 | for(i : range(10)){ 39 | match(i){ 40 | 60=>{ 41 | println("I need it") 42 | break 43 | } 44 | _=> println("undesired "+i) 45 | } 46 | } 47 | 48 | match(a){} 49 | 50 | match{ 51 | } 52 | 53 | for(i=0;i<10;i+=1){ 54 | match{ 55 | _=> println("stop") 56 | true=>{println("new form:"+i)} 57 | } 58 | } 59 | 60 | for(i=0;i<10;i+=1){ 61 | match{ 62 | i%5==0 =>println(i+" could be divided by 5") 63 | i%3==0 =>println(i+" could be divided by 3") 64 | i%2==0 =>println(i+" could be divided by 2") 65 | _=> println("mismatched!") 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /nyx/Main.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2018-2023 y1yang0 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | 24 | #include "Debug.hpp" 25 | #include "Interpreter.h" 26 | #include "Utils.hpp" 27 | 28 | int main(int argc, char* argv[]) { 29 | if (argc < 2) { 30 | panic("Feed your *.nyx source file to interpreter!\n"); 31 | } 32 | 33 | auto* rt = new Runtime; 34 | 35 | Parser parser(argv[1]); 36 | #if NYX_DEBUG 37 | printLex(argv[1]); 38 | #endif 39 | parser.parse(rt); 40 | Interpreter nyx; 41 | nyx.execute(rt); 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /nyx_test/tiresome/closure.nyx: -------------------------------------------------------------------------------- 1 | func add(a,b){ 2 | return a+b 3 | } 4 | 5 | func add3(b){ 6 | return func(){ 7 | return add(3,b) 8 | } 9 | } 10 | func twice(){ 11 | return func(){ 12 | return 5+5 13 | } 14 | } 15 | 16 | func trible(p){ 17 | original = p 18 | 19 | a = func(){ 20 | return original 21 | } 22 | b = func(){ 23 | return a()+p 24 | } 25 | c= func(){ 26 | return b()+p 27 | } 28 | return c 29 | } 30 | t = trible(3) 31 | assert(t()==9) 32 | assert(add(1,2)==3) 33 | a = add3(4) 34 | println(typeof(a)=="closure") 35 | assert(a()==7) 36 | q=twice() 37 | assert(q()==10) 38 | assert(q()==10) 39 | println(q) 40 | func weird(num){ 41 | closure_arr = [] 42 | for(i:range(num)){ 43 | closure_arr += func(){ 44 | return i 45 | } 46 | } 47 | return closure_arr 48 | } 49 | for(closure:weird(3)){ 50 | println(closure+" produces the result:"+closure()) 51 | } 52 | 53 | func currying(f){ 54 | return func(a){ 55 | return f(1,a) 56 | } 57 | } 58 | 59 | currying_demo = currying(func(a,b){return a+b}) 60 | println(currying_demo(2)==3) 61 | 62 | func hof(f1,f2){ 63 | return f1(f2) 64 | } 65 | 66 | res = hof(func(f1)=> return func() => return f1()+5 ,func()=>return 8) 67 | assert(res()==13) 68 | 69 | func nest_closures(){ 70 | top = 100 71 | a = func(){ 72 | b = 10 73 | c = b+20 74 | d = func(){ 75 | e = top+c 76 | f = func(){ 77 | h = 100 78 | g = e+h 79 | return g 80 | } 81 | return f() 82 | } 83 | return d() 84 | } 85 | return a() 86 | } 87 | 88 | assert(nest_closures()==230) -------------------------------------------------------------------------------- /nyx_test/tiresome/function.nyx: -------------------------------------------------------------------------------- 1 | func repeat(a,str){ 2 | i = 0 3 | while(i 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | 24 | #pragma once 25 | 26 | #include 27 | #include 28 | #include 29 | #include "Ast.h" 30 | #include "Object.hpp" 31 | #include "Runtime.hpp" 32 | 33 | std::string repeatString(int count, const std::string& str); 34 | 35 | template 36 | inline bool anyone(DesireType k, ArgumentType... args) { 37 | return ((args == k) || ...); 38 | } 39 | 40 | [[noreturn]] void panic(char const* const format, ...); 41 | 42 | std::string type2String(ValueType type); 43 | 44 | void checkArgsCount(int expectedCount, ObjectArray* args); 45 | void checkArgsType(int idx, ObjectArray* args, ValueType expectedType); 46 | 47 | void checkObjectType(const Object* object, ValueType t); 48 | -------------------------------------------------------------------------------- /nyx/Interpreter.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2018-2023 y1yang0 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | #pragma once 24 | 25 | #include 26 | #include "Object.hpp" 27 | #include "Parser.h" 28 | #include "Runtime.hpp" 29 | 30 | //===----------------------------------------------------------------------===// 31 | // Interpret AST nodes with execution context 32 | //===----------------------------------------------------------------------===// 33 | class Interpreter { 34 | public: 35 | Interpreter() : ctxChain(new ContextChain) {} 36 | 37 | void execute(Runtime* rt); 38 | 39 | public: 40 | static void newContext(ContextChain* ctxChain); 41 | 42 | static Object* callFunc(Runtime* rt, 43 | Func* f, 44 | ContextChain* lastCtxChain, 45 | std::vector args); 46 | 47 | static Object* evalBinaryExpr(Object* lhs, Token opt, Object* rhs); 48 | 49 | static Object* evalUnaryExpr(Object* lhs, Token opt); 50 | 51 | static Object* assignment(Token opt, Object* lhs, Object* rhs); 52 | 53 | private: 54 | ContextChain* ctxChain; 55 | }; 56 | -------------------------------------------------------------------------------- /nyx/Builtin.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2018-2023 y1yang0 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | #pragma once 24 | 25 | #include 26 | #include 27 | #include "Object.hpp" 28 | #include "Runtime.hpp" 29 | 30 | Object* nyx_builtin_print(Runtime* rt, 31 | ContextChain* ctxChain, 32 | ObjectArray args); 33 | 34 | Object* nyx_builtin_println(Runtime* rt, 35 | ContextChain* ctxChain, 36 | ObjectArray args); 37 | 38 | Object* nyx_builtin_input(Runtime* rt, 39 | ContextChain* ctxChain, 40 | ObjectArray args); 41 | 42 | Object* nyx_builtin_typeof(Runtime* rt, 43 | ContextChain* ctxChain, 44 | ObjectArray args); 45 | 46 | Object* nyx_builtin_length(Runtime* rt, 47 | ContextChain* ctxChain, 48 | ObjectArray args); 49 | 50 | Object* nyx_builtin_to_int(Runtime* rt, 51 | ContextChain* ctxChain, 52 | ObjectArray args); 53 | 54 | Object* nyx_builtin_to_double(Runtime* rt, 55 | ContextChain* ctxChain, 56 | ObjectArray args); 57 | 58 | Object* nyx_builtin_range(Runtime* rt, 59 | ContextChain* ctxChain, 60 | ObjectArray args); 61 | 62 | Object* nyx_builtin_assert(Runtime* rt, 63 | ContextChain* ctxChain, 64 | ObjectArray args); 65 | 66 | Object* nyx_builtin_dump_ast(Runtime* rt, 67 | ContextChain* ctxChain, 68 | ObjectArray args); 69 | -------------------------------------------------------------------------------- /nyx/Utils.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2018-2023 y1yang0 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | 24 | #include "Utils.hpp" 25 | #include 26 | #include 27 | #include 28 | #include "Debug.hpp" 29 | #include "Object.hpp" 30 | #include "Runtime.hpp" 31 | 32 | std::string repeatString(int count, const std::string& str) { 33 | std::string result; 34 | for (int i = 0; i < count; i++) { 35 | result += str; 36 | } 37 | return result; 38 | } 39 | 40 | [[noreturn]] void panic(char const* const format, ...) { 41 | va_list args; 42 | va_start(args, format); 43 | vfprintf(stderr, format, args); 44 | va_end(args); 45 | exit(EXIT_FAILURE); 46 | } 47 | 48 | std::string type2String(ValueType type) { 49 | switch (type) { 50 | case Bool: 51 | return "bool"; 52 | case Double: 53 | return "double"; 54 | case Int: 55 | return "int"; 56 | case String: 57 | return "string"; 58 | case Null: 59 | return "null"; 60 | case Char: 61 | return "char"; 62 | case Array: 63 | return "array"; 64 | case Closure: 65 | return "closure"; 66 | default: 67 | panic("arguments with unknown type passed into %s", __func__); 68 | } 69 | return ""; 70 | } 71 | void checkArgsCount(int expectedCount, ObjectArray* args) { 72 | if (args->size() < expectedCount) { 73 | panic("expect %d arguments but received %d", expectedCount, 74 | args->size()); 75 | } 76 | } 77 | void checkArgsType(int idx, ObjectArray* args, ValueType expectedType) { 78 | if (args->size() <= idx) { 79 | panic("missing arguments"); 80 | } 81 | if (!args->at(idx)->isType(expectedType)) { 82 | panic("argument at %d has unexpected type", idx); 83 | } 84 | } 85 | void checkObjectType(const Object* object, ValueType t) { 86 | if (object == nullptr || object->getType() != t) { 87 | panic("object(%p) is expected %d but got %d", object, object->getType(), 88 | t); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /nyx/Debug.hpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2023 y1yang0 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | #ifndef NYX_DEBUG_HPP 24 | #define NYX_DEBUG_HPP 25 | 26 | #include 27 | #include 28 | #include 29 | #include "Ast.h" 30 | #include "Object.hpp" 31 | #include "Runtime.hpp" 32 | 33 | //===----------------------------------------------------------------------===// 34 | // Print ast tree from root node to human-readable text 35 | //===----------------------------------------------------------------------===// 36 | struct AstDumper : public AstVisitor { 37 | int ident = 0; 38 | 39 | explicit AstDumper(int ident) : ident(ident) {} 40 | explicit AstDumper() : ident(0) {} 41 | 42 | void printPadding() const; 43 | void visitExpression(Expression* node) override {} 44 | void visitBoolExpr(BoolExpr* node) override; 45 | void visitCharExpr(CharExpr* node) override; 46 | void visitNullExpr(NullExpr* node) override; 47 | void visitIntExpr(IntExpr* node) override; 48 | void visitDoubleExpr(DoubleExpr* node) override; 49 | void visitStringExpr(StringExpr* node) override; 50 | void visitArrayExpr(ArrayExpr* node) override; 51 | void visitNameExpr(NameExpr* node) override; 52 | void visitIndexExpr(IndexExpr* node) override; 53 | void visitBinaryExpr(BinaryExpr* node) override; 54 | void visitFunCallExpr(FunCallExpr* node) override; 55 | void visitAssignExpr(AssignExpr* node) override; 56 | void visitClosureExpr(ClosureExpr* node) override; 57 | void visitStatement(Statement* node) override {} 58 | void visitBreakStmt(BreakStmt* node) override; 59 | void visitContinueStmt(ContinueStmt* node) override; 60 | void visitSimpleStmt(SimpleStmt* node) override; 61 | void visitReturnStmt(ReturnStmt* node) override; 62 | void visitIfStmt(IfStmt* node) override; 63 | void visitWhileStmt(WhileStmt* node) override; 64 | void visitForStmt(ForStmt* node) override; 65 | void visitForEachStmt(ForEachStmt* node) override; 66 | void visitMatchStmt(MatchStmt* node) override; 67 | }; 68 | 69 | void printLex(const std::string& fileName); 70 | 71 | #endif // NYX_DEBUG_HPP 72 | -------------------------------------------------------------------------------- /nyx/Parser.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2018-2023 y1yang0 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | 24 | #pragma once 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "Ast.h" 34 | #include "Runtime.hpp" 35 | 36 | //===----------------------------------------------------------------------===// 37 | // Parse source file to AST nodes 38 | //===----------------------------------------------------------------------===// 39 | class Parser { 40 | public: 41 | explicit Parser(const std::string& fileName); 42 | 43 | ~Parser(); 44 | 45 | public: 46 | void parse(Runtime* rt); 47 | std::tuple next(); 48 | 49 | private: 50 | Expression* parsePrimaryExpr(); 51 | 52 | Expression* parseUnaryExpr(); 53 | 54 | // default to lowest precedence(0) + 1 55 | Expression* parseExpression(short oldPrecedence = 1); 56 | 57 | SimpleStmt* parseExpressionStmt(); 58 | 59 | IfStmt* parseIfStmt(); 60 | 61 | WhileStmt* parseWhileStmt(); 62 | 63 | Statement* parseForStmt(); 64 | 65 | MatchStmt* parseMatchStmt(); 66 | 67 | ReturnStmt* parseReturnStmt(); 68 | 69 | Statement* parseStatement(); 70 | 71 | std::vector parseStatementList(); 72 | 73 | Block* parseBlock(); 74 | 75 | std::vector parseParameterList(); 76 | 77 | Func* parseFuncDef(Context* context); 78 | 79 | private: 80 | short precedence(Token op); 81 | 82 | inline char getNextChar() { 83 | column++; 84 | return static_cast(fs.get()); 85 | } 86 | 87 | inline char peekNextChar() { return static_cast(fs.peek()); } 88 | 89 | inline Token getCurrentToken() const { 90 | return std::get(currentToken); 91 | } 92 | 93 | inline std::string getCurrentLexeme() const { 94 | return std::get(currentToken); 95 | } 96 | 97 | private: 98 | std::tuple currentToken; 99 | 100 | std::fstream fs; 101 | 102 | int line = 1; 103 | 104 | int column = 0; 105 | }; 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NYX Language 2 | **nyx** is yet another a dynamically typed scripting language with flexible syntax and strict type system. 3 | Its syntax resembles Python,Java and C family languages. If you've already familiar with any other popular languages, 4 | you can seamlessly write code after skimming its language reference manual. 5 | In addition, nyx attempts to detect sorts of erroneous program forms at compile time that benefited from its underlying 6 | strict type system, this could tremendously save your time for debugging type relevant bugs. 7 | 8 | # Build 9 | ```bash 10 | $ mkdir build && cd build 11 | $ cmake .. 12 | $ make 13 | $ nyx 14 | ``` 15 | All tests passed on *Windows* 16 | 17 | # Code Examples 18 | + [nyx_test/example/9x9table.nyx](nyx_test/example/9x9table.nyx) 19 | ``` 20 | 1x1=1 21 | 1x2=2 2x2=4 22 | 1x3=3 2x3=6 3x3=9 23 | 1x4=4 2x4=8 3x4=12 4x4=16 24 | 1x5=5 2x5=10 3x5=15 4x5=20 5x5=25 25 | 1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36 26 | 1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49 27 | 1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64 28 | 1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81 29 | ``` 30 | + [nyx_test/example/yang_hui_san_jiao.nyx](nyx_test/example/yang_hui_san_jiao.nyx) 31 | ``` 32 | n = 1 [1] 33 | n = 2 [1,1] 34 | n = 3 [1,2,1] 35 | n = 4 [1,3,3,1] 36 | n = 5 [1,4,6,4,1] 37 | n = 6 [1,5,10,10,5,1] 38 | n = 7 [1,6,15,20,15,6,1] 39 | n = 8 [1,7,21,35,35,21,7,1] 40 | n = 9 [1,8,28,56,70,56,28,8,1] 41 | ``` 42 | 43 | + [nyx_test/example/quick_sort.nyx](nyx_test/example/quick_sort.nyx) 44 | ``` 45 | [4,-5,9,15,-6,-2,0] => [-6,-5,-2,0,4,9,15] 46 | ``` 47 | + [nyx_test/example/love.nyx](nyx_test/example/love.nyx) 48 | ``` 49 | ********* ********* 50 | ***************** ***************** 51 | **************************************** 52 | ******************************************* 53 | ********************************************* 54 | ********************************************* 55 | ********************************************* 56 | ********************************************* 57 | ********************************************* 58 | ********************************************* 59 | ******************************************* 60 | ***************************************** 61 | **************************************** 62 | ************************************* 63 | *********************************** 64 | ********************************* 65 | ***************************** 66 | ************************* 67 | ********************* 68 | *************** 69 | ********* 70 | *** 71 | ``` 72 | + [nyx_test/example/ast.nyx](nyx_test/example/ast.nyx) 73 | ``` 74 | -Func[fibonacci_rec] 75 | -IfStmt 76 | -BinaryExpr[23] 77 | -NameExpr[num] 78 | -IntExpr[2] 79 | -ReturnStmt 80 | -IntExpr[1] 81 | -ReturnStmt 82 | -BinaryExpr[13] 83 | -FunCallExpr[fibonacci_rec] 84 | -BinaryExpr[14] 85 | -NameExpr[num] 86 | -IntExpr[2] 87 | -FunCallExpr[fibonacci_rec] 88 | -BinaryExpr[14] 89 | -NameExpr[num] 90 | -IntExpr[1] 91 | ``` 92 | ## Language Reference 93 | See [reference.md](./docs/reference.md) for detailed language reference, 94 | [`nyx_test/*`](./nyx_test/) contains various nyx code snippets, they are probably the best way to learn how to program in **nyx**. 95 | 96 | # License 97 | **nyx** is licensed under the [MIT LICENSE](LICENSE)。 98 | -------------------------------------------------------------------------------- /nyx/Runtime.hpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2018-2023 y1yang0 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | 24 | #pragma once 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | struct Statement; 32 | struct Expression; 33 | struct Context; 34 | class Object; 35 | 36 | using ObjectArray = std::vector; 37 | using ContextChain = std::deque; 38 | 39 | enum ExecutionResultType { ExecNormal, ExecReturn, ExecBreak, ExecContinue }; 40 | 41 | struct Block { 42 | explicit Block() = default; 43 | 44 | std::vector stmts; 45 | }; 46 | 47 | struct Func { 48 | explicit Func() = default; 49 | 50 | std::string name; 51 | ContextChain* outerContext{}; 52 | std::vector params; 53 | Block* block{}; 54 | }; 55 | 56 | struct ExecResult { 57 | explicit ExecResult(ExecutionResultType execType) 58 | : execType(execType), retValue(nullptr) {} 59 | 60 | explicit ExecResult(ExecutionResultType execType, Object* retValue) 61 | : execType(execType), retValue(retValue) {} 62 | 63 | ExecutionResultType execType; 64 | Object* retValue; 65 | }; 66 | 67 | struct Variable { 68 | explicit Variable() = default; 69 | 70 | std::string name; 71 | Object* value; 72 | }; 73 | 74 | class Context { 75 | public: 76 | explicit Context() = default; 77 | 78 | virtual ~Context(); 79 | 80 | bool hasVariable(const std::string& identName); 81 | 82 | void createVariable(const std::string& identName, Object* value); 83 | 84 | Variable* getVariable(const std::string& identName); 85 | 86 | void addFunction(const std::string& name, Func* f); 87 | 88 | bool hasFunction(const std::string& name); 89 | 90 | Func* getFunction(const std::string& name); 91 | 92 | private: 93 | std::unordered_map vars; 94 | std::unordered_map funcs; 95 | }; 96 | 97 | class Runtime : public Context { 98 | using BuiltinFuncType = Object* (*)(Runtime*, ContextChain*, ObjectArray); 99 | 100 | public: 101 | explicit Runtime(); 102 | 103 | bool hasBuiltinFunction(const std::string& name); 104 | 105 | BuiltinFuncType getBuiltinFunction(const std::string& name); 106 | 107 | void addStatement(Statement* stmt); 108 | 109 | std::vector& getStatements(); 110 | 111 | Object* newObject(int data); 112 | Object* newObject(double data); 113 | Object* newObject(std::string data); 114 | Object* newObject(bool data); 115 | Object* newObject(char c); 116 | Object* newObject(ObjectArray data); 117 | Object* newObject(Func data); 118 | Object* newObject(); 119 | Object* cloneObject(Object* object); 120 | 121 | template 122 | void resetObject(Object* object, T data); 123 | 124 | private: 125 | std::unordered_map builtin; 126 | std::vector stmts; 127 | // TODO: create object in managed heap and support GC to make it a "real 128 | // heap" 129 | ObjectArray heap; 130 | }; 131 | 132 | extern Runtime* runtime; -------------------------------------------------------------------------------- /nyx/Object.hpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2023 y1yang0 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | 24 | #ifndef NYX_OBJECT_HPP 25 | #define NYX_OBJECT_HPP 26 | 27 | enum ValueType { Int, Double, String, Bool, Char, Null, Array, Closure }; 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "Runtime.hpp" 35 | 36 | struct Statement; 37 | struct Context; 38 | 39 | //===----------------------------------------------------------------------===// 40 | // Runtime object 41 | //===----------------------------------------------------------------------===// 42 | class Object { 43 | friend class Runtime; 44 | 45 | public: 46 | int asInt() const { return *(int*)(data); } 47 | double asDouble() const { return *(double*)(data); } 48 | std::string asString() const { return *(std::string*)(data); } 49 | bool asBool() const { return *(bool*)(data); } 50 | char asChar() const { return *(char*)(data); } 51 | std::nullptr_t asNull() const { return nullptr; } 52 | ObjectArray asArray() const { return *(ObjectArray*)(data); } 53 | Func asClosure() const { return *(Func*)(data); } 54 | 55 | bool isInt() const { return type == Int; } 56 | bool isDouble() const { return type == Double; } 57 | bool isString() const { return type == String; } 58 | bool isBool() const { return type == Bool; } 59 | bool isChar() const { return type == Char; } 60 | bool isNull() const { return type == Null; } 61 | bool isArray() const { return type == Array; } 62 | bool isClosure() const { return type == Closure; } 63 | bool isType(ValueType t) const { return t == type; } 64 | 65 | Object* operator+(Object* rhs) const; 66 | 67 | Object* operator-(Object* rhs) const; 68 | 69 | Object* operator*(Object* rhs) const; 70 | 71 | Object* operator/(Object* rhs) const; 72 | 73 | Object* operator%(Object* rhs) const; 74 | 75 | Object* operator&&(Object* rhs) const; 76 | 77 | Object* operator||(Object* rhs) const; 78 | 79 | Object* operator==(Object* rhs) const; 80 | 81 | Object* operator!=(Object* rhs) const; 82 | 83 | Object* operator>(Object* rhs) const; 84 | 85 | Object* operator>=(Object* rhs) const; 86 | 87 | Object* operator<(Object* rhs) const; 88 | 89 | Object* operator<=(Object* rhs) const; 90 | 91 | Object* operator&(Object* rhs) const; 92 | 93 | Object* operator|(Object* rhs) const; 94 | 95 | Object* operator-() const; 96 | 97 | Object* operator!() const; 98 | 99 | Object* operator~() const; 100 | 101 | bool equalsDeep(Object* b) const; 102 | 103 | std::string toString() const; 104 | 105 | bool isPrimitive() const; 106 | 107 | ValueType getType() const { return type; } 108 | 109 | template 110 | void resetObject(T data) { 111 | *(T*)(this->data) = data; 112 | } 113 | 114 | private: 115 | explicit Object() = default; 116 | 117 | explicit Object(ValueType type, void* data) : type(type), data(data) {} 118 | 119 | ValueType type; 120 | void* data; 121 | }; 122 | 123 | #endif // NYX_OBJECT_HPP 124 | -------------------------------------------------------------------------------- /nyx/Builtin.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2018-2023 y1yang0 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | #include "Builtin.h" 24 | #include 25 | #include 26 | #include "Ast.h" 27 | #include "Debug.hpp" 28 | #include "Object.hpp" 29 | #include "Runtime.hpp" 30 | #include "Utils.hpp" 31 | 32 | Object* nyx_builtin_print(Runtime* rt, 33 | ContextChain* ctxChain, 34 | ObjectArray args) { 35 | for (auto arg : args) { 36 | std::cout << arg->toString(); 37 | } 38 | return rt->newObject((int)args.size()); 39 | } 40 | 41 | Object* nyx_builtin_println(Runtime* rt, 42 | ContextChain* ctxChain, 43 | ObjectArray args) { 44 | if (!args.empty()) { 45 | for (auto arg : args) { 46 | std::cout << arg->toString() << "\n"; 47 | } 48 | } else { 49 | std::cout << "\n"; 50 | } 51 | 52 | return rt->newObject((int)args.size()); 53 | } 54 | 55 | Object* nyx_builtin_input(Runtime* rt, 56 | ContextChain* ctxChain, 57 | ObjectArray args) { 58 | checkArgsCount(0, &args); 59 | 60 | std::string str; 61 | std::cin >> str; 62 | return rt->newObject(str); 63 | } 64 | 65 | Object* nyx_builtin_typeof(Runtime* rt, 66 | ContextChain* ctxChain, 67 | ObjectArray args) { 68 | checkArgsCount(1, &args); 69 | return rt->newObject(type2String(args[0]->getType())); 70 | } 71 | 72 | Object* nyx_builtin_length(Runtime* rt, 73 | ContextChain* ctxChain, 74 | ObjectArray args) { 75 | checkArgsCount(1, &args); 76 | 77 | if (args[0]->isString()) { 78 | return rt->newObject((int)args[0]->asString().length()); 79 | } 80 | if (args[0]->isArray()) { 81 | return rt->newObject((int)args[0]->asArray().size()); 82 | } 83 | 84 | panic( 85 | "unexpected type of arguments,function %s requires string " 86 | "type or " 87 | "array type", 88 | __func__); 89 | } 90 | 91 | Object* nyx_builtin_to_int(Runtime* rt, 92 | ContextChain* ctxChain, 93 | ObjectArray args) { 94 | checkArgsCount(1, &args); 95 | checkArgsType(0, &args, Double); 96 | 97 | return rt->newObject((int)args[0]->asDouble()); 98 | } 99 | 100 | Object* nyx_builtin_to_double(Runtime* rt, 101 | ContextChain* ctxChain, 102 | ObjectArray args) { 103 | checkArgsCount(1, &args); 104 | checkArgsType(0, &args, Int); 105 | 106 | return rt->newObject((double)args[0]->asInt()); 107 | } 108 | 109 | Object* nyx_builtin_range(Runtime* rt, 110 | ContextChain* ctxChain, 111 | ObjectArray args) { 112 | checkArgsCount(1, &args); 113 | 114 | ObjectArray vals; 115 | if (args[0]->asInt() <= 0) { 116 | return rt->newObject(vals); 117 | } 118 | int start = 0, stop = 0; 119 | if (args.size() == 1) { 120 | start = 0; 121 | stop = args[0]->asInt(); 122 | } else { 123 | start = args[0]->asInt(); 124 | stop = args[1]->asInt(); 125 | } 126 | for (; start < stop; start++) { 127 | vals.push_back(rt->newObject(start)); 128 | } 129 | return rt->newObject(vals); 130 | } 131 | 132 | Object* nyx_builtin_assert(Runtime* rt, 133 | ContextChain* ctxChain, 134 | ObjectArray args) { 135 | checkArgsType(0, &args, Bool); 136 | if (!args[0]->asBool()) { 137 | if (args.size() == 2) { 138 | std::cerr << "AssertionFailure: " << args[1]->asString() 139 | << std::endl; 140 | } else { 141 | std::cerr << "AssertionFailure" << std::endl; 142 | } 143 | 144 | std::abort(); 145 | } 146 | return rt->newObject(); 147 | } 148 | 149 | Object* nyx_builtin_dump_ast(Runtime* rt, 150 | ContextChain* ctxChain, 151 | ObjectArray args) { 152 | checkArgsCount(1, &args); 153 | checkArgsType(0, &args, Closure); 154 | auto func = args[0]->asClosure(); 155 | 156 | std::cout << "-Func[" << func.name << "]" << std::endl; 157 | AstDumper d(2); 158 | if (func.block != nullptr) { 159 | for (const auto& item : func.block->stmts) { 160 | item->visit(&d); 161 | } 162 | } 163 | 164 | return nullptr; 165 | } 166 | -------------------------------------------------------------------------------- /nyx/Runtime.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2018-2023 y1yang0 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | 24 | #include "Runtime.hpp" 25 | 26 | #include 27 | #include "Builtin.h" 28 | #include "Object.hpp" 29 | #include "Utils.hpp" 30 | 31 | Runtime* runtime = new Runtime(); 32 | 33 | Context::~Context() { 34 | for (const auto& v : vars) { 35 | delete v.second; 36 | } 37 | } 38 | 39 | Runtime::Runtime() { 40 | builtin["print"] = &nyx_builtin_print; 41 | builtin["println"] = &nyx_builtin_println; 42 | builtin["typeof"] = &nyx_builtin_typeof; 43 | builtin["input"] = &nyx_builtin_input; 44 | builtin["length"] = &nyx_builtin_length; 45 | builtin["to_int"] = &nyx_builtin_to_int; 46 | builtin["to_double"] = &nyx_builtin_to_double; 47 | builtin["range"] = &nyx_builtin_range; 48 | builtin["assert"] = &nyx_builtin_assert; 49 | builtin["dump_ast"] = &nyx_builtin_dump_ast; 50 | } 51 | 52 | bool Runtime::hasBuiltinFunction(const std::string& name) { 53 | return builtin.count(name) == 1; 54 | } 55 | 56 | Runtime::BuiltinFuncType Runtime::getBuiltinFunction(const std::string& name) { 57 | if (auto res = builtin.find(name); res != builtin.end()) { 58 | return res->second; 59 | } 60 | return nullptr; 61 | } 62 | 63 | void Runtime::addStatement(Statement* stmt) { 64 | stmts.push_back(stmt); 65 | } 66 | 67 | std::vector& Runtime::getStatements() { 68 | return stmts; 69 | } 70 | 71 | Object* Runtime::newObject() { 72 | auto* object = new Object(Null, nullptr); 73 | heap.push_back(object); 74 | return object; 75 | } 76 | 77 | Object* Runtime::newObject(int data) { 78 | int* mem = new int; 79 | *mem = data; 80 | auto* object = new Object(Int, mem); 81 | heap.push_back(object); 82 | return object; 83 | } 84 | Object* Runtime::newObject(double data) { 85 | auto* mem = new double; 86 | *mem = data; 87 | auto* object = new Object(Double, mem); 88 | heap.push_back(object); 89 | return object; 90 | } 91 | 92 | Object* Runtime::newObject(std::string data) { 93 | auto* mem = new std::string; 94 | *mem = std::move(data); 95 | auto* object = new Object(String, mem); 96 | heap.push_back(object); 97 | return object; 98 | } 99 | 100 | Object* Runtime::newObject(bool data) { 101 | bool* mem = new bool; 102 | *mem = data; 103 | auto* object = new Object(Bool, mem); 104 | heap.push_back(object); 105 | return object; 106 | } 107 | 108 | Object* Runtime::newObject(char data) { 109 | char* mem = new char; 110 | *mem = data; 111 | auto* object = new Object(Char, mem); 112 | heap.push_back(object); 113 | return object; 114 | } 115 | Object* Runtime::newObject(ObjectArray data) { 116 | auto* mem = new ObjectArray; 117 | *mem = data; 118 | auto* object = new Object(Array, mem); 119 | heap.push_back(object); 120 | return object; 121 | } 122 | Object* Runtime::newObject(Func data) { 123 | auto* mem = new Func; 124 | mem[0] = data; 125 | auto* object = new Object(Closure, mem); 126 | heap.push_back(object); 127 | return object; 128 | } 129 | Object* Runtime::cloneObject(Object* object) { 130 | switch (object->getType()) { 131 | case Int: 132 | return newObject(object->asInt()); 133 | case Double: 134 | return newObject(object->asDouble()); 135 | case String: 136 | return newObject(object->asString()); 137 | case Bool: 138 | return newObject(object->asBool()); 139 | case Char: 140 | return newObject(object->asChar()); 141 | case Array: 142 | return newObject(object->asArray()); 143 | case Closure: 144 | return newObject(object->asClosure()); 145 | default: 146 | panic("unknown object type (%p)", object->type, object->data); 147 | } 148 | } 149 | 150 | bool Context::hasVariable(const std::string& identName) { 151 | return vars.count(identName) == 1; 152 | } 153 | 154 | void Context::createVariable(const std::string& identName, Object* value) { 155 | auto* var = new Variable(); 156 | var->name = identName; 157 | var->value = value; 158 | vars.emplace(identName, var); 159 | } 160 | 161 | Variable* Context::getVariable(const std::string& identName) { 162 | if (auto res = vars.find(identName); res != vars.end()) { 163 | return res->second; 164 | } 165 | return nullptr; 166 | } 167 | 168 | void Context::addFunction(const std::string& name, Func* f) { 169 | funcs.insert(std::make_pair(name, f)); 170 | } 171 | 172 | bool Context::hasFunction(const std::string& name) { 173 | return funcs.count(name) == 1; 174 | } 175 | 176 | Func* Context::getFunction(const std::string& name) { 177 | if (auto f = funcs.find(name); f != funcs.end()) { 178 | return f->second; 179 | } 180 | return nullptr; 181 | } 182 | 183 | template 184 | void Runtime::resetObject(Object* object, T data) { 185 | *(T*)(object->data) = data; 186 | } 187 | -------------------------------------------------------------------------------- /docs/reference.md: -------------------------------------------------------------------------------- 1 | ## 1.基础 2 | ### 1.1注释 3 | 使用`#`可以注释一行代码。**nyx**不支持多行注释,对于多行需要重复`#` 4 | 5 | ### 1.2数据类型 6 | **int**表示整数类型,如`3`,`100000`,`1024` 7 | 8 | **double** 表示小数类型,如`3.1415926`,`2.232`,`4.4` 9 | 10 | **string** 表示字符串类型,如`"string"`,`"test"`,`""`。 11 | 12 | **bool** 布尔类型,值域只有字面值`true`和`false` 13 | 14 | **null** 空值类型,用于指示该变量不具有值,值域只有字面值`null` 15 | 16 | **char** 字符类型,表示单个字符,如`'a'`,`'Y'` 17 | 18 | **array** 数组类型,用于创建一个数组,数组元素可以是**任意类型**,如`[2.718,"hell",null,false,'u']` 19 | 20 | **closure** 闭包类型。创建可以捕获外部环境的匿名函数,如`func(a){ return a + 1 +b }` 21 | 22 | ### 1.3 变量 23 | `name = value`即定义名为**name**的变量,具有**value**值。 24 | 如果`name`是索引表达式,相应的就是更新数组索引值而不是添加它,也就是说,向数组中一个不存在的索引赋值是错误。 25 | 由于赋值是**表达式**而不是**语句**,所以它也可以出现在任何表达式可以出现的地方: 26 | ```nyx 27 | print(ff=15&5|12) # print the result of 15&5|12, that is, 13 28 | a = [1,3,4] 29 | println(a) # print [1,3,4] 30 | a[0] = 5 31 | println(a) # print [5,3,4] 32 | a = 1 33 | a += 1 34 | println(a==2) 35 | a -= 3 36 | println(a==-1) 37 | a *= 4 38 | println(a==-4) 39 | a /= 2 40 | println(a==-2) 41 | a %= 2 42 | println(a==0) 43 | ``` 44 | 45 | ## 2.运算符 46 | ### 2.1 计算基石 47 | **nyx**中`+,-,*,/,%`运算的优先级和运算规则与其它语言一致: 48 | ```nyx 49 | d = (3+2)*4+(6*5)-8/2+(3+2*(5-4)) 50 | c = -7 51 | a = 3+2-5 52 | b = 3+5*2%2 53 | print(a,b,c,d) 54 | q = (((((((((((((((((((1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1 55 | print(q) 56 | ``` 57 | 58 | ### 2.2 操作符重载 59 | 除了数值类型外,`+`,`*`符号对一些类型还有特殊效果。 60 | 61 | 对两个字符串进行`+`运算得到的是拼接后的结果; 62 | `*`表示N次重复字符串: 63 | ```nyx 64 | print("hello,"+"world") # will print hello,world 65 | print("test" * 3 ) # will print testtesttest 66 | ``` 67 | 对数组进行`+`运算会将另一个操作数放入数组中: 68 | ```nyx 69 | a = [1,2,'c'] 70 | b = a+[4,5] 71 | println(b) # print [1,2,[4,5]] 72 | println(3+[4,5]) # print [3,4,5] 73 | println([3]+[4,5]) # print [[3],4,5] 74 | ``` 75 | 76 | ### 2.3 逻辑运算 77 | `&&`表示逻辑与运算,`||`表示逻辑或运算,`!`表示逻辑非运算,这些运算也有[**短路求值**](https://en.wikipedia.org/wiki/Short-circuit_evaluation)特性。 78 | ```nyx 79 | true&&false 80 | false||true 81 | !(false||false||false||(true||false)) 82 | ``` 83 | ### 2.4 条件运算 84 | 除了逻辑运算外,**nyx**也有完备的条件运算支持:`==`,`!=`,`>`,`>=`,`<`,`<=`: 85 | ```nyx 86 | print((true&&false)==false) 87 | print((false&&true)==false) 88 | print((true||false)==true) 89 | print((true||true)==true) 90 | print(5>3 && 6<10 && (14>=13||13<=15)) 91 | ``` 92 | 注意`==`,`!=`运算符也支持`null`的条件比较: 93 | + `null==null`总是为`true` 94 | + `null!=null`总是为`false`。 95 | 96 | ### 2.5 位运算 97 | 位运算类似于C系语言: 98 | ``` 99 | # 位与 100 | print(3&5) # 0011 & 0101 => 1 101 | 102 | # 位或 103 | print(4|66) # 00000100 & 01000010 => 70 104 | 105 | # 位反 106 | print(~43) # 00101011 =>11010100 => -44 107 | ``` 108 | 109 | ## 3. 流程控制 110 | ## 3.1 if-else分支跳转 111 | `if`语句可以根据条件进行分支跳转。单个`if`分支跳转和`if-else`分支跳转都是允许的: 112 | ```nyx 113 | a = input() 114 | if(a+1 == "whatsup"){ 115 | print("fine") 116 | } 117 | b = 10 118 | if(b <10){ 119 | print("b is less than 10") 120 | }else{ 121 | print("b is greater equal than 10") 122 | } 123 | ``` 124 | 125 | ## 3.2 for与foreach循环 126 | `for`循环类似于C系语言,由初始化表达式,条件表达式,后置表达式组成: 127 | ```nyx 128 | for(i=2;i println("3") 198 | 4 => { 199 | println("4") 200 | println("i don't like this number") 201 | } 202 | _ => println("any other number") 203 | } 204 | ``` 205 | 一旦匹配成功则进入分支执行相应动作。最后的`_`表示任意条件(**any**),即只要出现改语句必定进入分支;另外如果只有一条语句可以省略`{}` 206 | 207 | 208 | ## 4.函数 209 | ### 4.1 函数定义 210 | 使用`func`关键字引导函数定义: 211 | ```nyx 212 | # 重复输出a次str 213 | func repeat(a,str){ 214 | i = 0 215 | while(i 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | 24 | #include "Debug.hpp" 25 | #include 26 | #include "Parser.h" 27 | #include "Runtime.hpp" 28 | #include "Utils.hpp" 29 | 30 | void printLex(const std::string& fileName) { 31 | Parser p(fileName); 32 | std::tuple tk; 33 | do { 34 | tk = p.next(); 35 | std::cout << "[" << std::get<0>(tk) << "," << std::get<1>(tk) << "]\n"; 36 | } while (std::get<0>(tk) != TK_EOF); 37 | } 38 | 39 | void AstDumper::visitBoolExpr(BoolExpr* node) { 40 | printPadding(); 41 | std::cout << "-BoolExpr[" << node->literal << "]" << std::endl; 42 | } 43 | void AstDumper::visitCharExpr(CharExpr* node) { 44 | printPadding(); 45 | std::cout << "-CharExpr[" << node->literal << "]" << std::endl; 46 | } 47 | void AstDumper::visitNullExpr(NullExpr* node) { 48 | printPadding(); 49 | std::cout << "-NullExpr[null]" << std::endl; 50 | } 51 | void AstDumper::visitNameExpr(NameExpr* node) { 52 | printPadding(); 53 | std::cout << "-NameExpr[" << node->identName << "]" << std::endl; 54 | } 55 | void AstDumper::visitIntExpr(IntExpr* node) { 56 | printPadding(); 57 | std::cout << "-IntExpr[" << node->literal << "]" << std::endl; 58 | } 59 | void AstDumper::visitDoubleExpr(DoubleExpr* node) { 60 | printPadding(); 61 | std::cout << "-DoubleExpr[" << node->literal << "]" << std::endl; 62 | } 63 | void AstDumper::visitStringExpr(StringExpr* node) { 64 | printPadding(); 65 | std::cout << "-StringExpr[" << node->literal << "]" << std::endl; 66 | } 67 | void AstDumper::visitArrayExpr(ArrayExpr* node) { 68 | printPadding(); 69 | std::cout << "-ArrayExpr" << std::endl; 70 | ident += 2; 71 | for (const auto& item : node->literal) { 72 | item->visit(this); 73 | } 74 | ident -= 2; 75 | } 76 | void AstDumper::visitIndexExpr(IndexExpr* node) { 77 | printPadding(); 78 | std::cout << "-IndexExpr[" << node->identName << "]" << std::endl; 79 | ident += 2; 80 | node->index->visit(this); 81 | ident -= 2; 82 | } 83 | void AstDumper::visitBinaryExpr(BinaryExpr* node) { 84 | printPadding(); 85 | std::cout << "-BinaryExpr[" << node->opt << "]" << std::endl; 86 | ident += 2; 87 | if (node->lhs != nullptr) { 88 | node->lhs->visit(this); 89 | } 90 | if (node->rhs != nullptr) { 91 | node->rhs->visit(this); 92 | } 93 | ident -= 2; 94 | } 95 | void AstDumper::visitFunCallExpr(FunCallExpr* node) { 96 | printPadding(); 97 | std::cout << "-FunCallExpr[" << node->funcName << "]" << std::endl; 98 | ident += 2; 99 | if (node->receiver != nullptr) { 100 | node->receiver->visit(this); 101 | } 102 | for (const auto& item : node->args) { 103 | item->visit(this); 104 | } 105 | ident -= 2; 106 | } 107 | void AstDumper::visitAssignExpr(AssignExpr* node) { 108 | printPadding(); 109 | std::cout << "-AssignExpr[" << node->opt << "]" << std::endl; 110 | ident += 2; 111 | if (node->lhs != nullptr) { 112 | node->lhs->visit(this); 113 | } 114 | if (node->rhs != nullptr) { 115 | node->rhs->visit(this); 116 | } 117 | ident -= 2; 118 | } 119 | void AstDumper::visitClosureExpr(ClosureExpr* node) { 120 | printPadding(); 121 | std::cout << "-ClosureExpr[" << node->params.size() << "]" << std::endl; 122 | ident += 2; 123 | if (node->block != nullptr) { 124 | for (const auto& item : node->block->stmts) { 125 | item->visit(this); 126 | } 127 | } 128 | ident -= 2; 129 | } 130 | void AstDumper::visitBreakStmt(BreakStmt* node) { 131 | printPadding(); 132 | std::cout << "-BreakStmt" << std::endl; 133 | } 134 | void AstDumper::visitContinueStmt(ContinueStmt* node) { 135 | printPadding(); 136 | std::cout << "-ContinueStmt" << std::endl; 137 | } 138 | void AstDumper::visitSimpleStmt(SimpleStmt* node) { 139 | printPadding(); 140 | std::cout << "-SimpleStmt" << std::endl; 141 | ident += 2; 142 | if (node->expr != nullptr) { 143 | node->expr->visit(this); 144 | } 145 | ident -= 2; 146 | } 147 | void AstDumper::printPadding() const { 148 | for (int i = 0; i < ident; i++) { 149 | std::cout << " "; 150 | } 151 | } 152 | void AstDumper::visitReturnStmt(ReturnStmt* node) { 153 | printPadding(); 154 | std::cout << "-ReturnStmt" << std::endl; 155 | ident += 2; 156 | if (node->ret != nullptr) { 157 | node->ret->visit(this); 158 | } 159 | ident -= 2; 160 | } 161 | void AstDumper::visitIfStmt(IfStmt* node) { 162 | printPadding(); 163 | std::cout << "-IfStmt" << std::endl; 164 | ident += 2; 165 | if (node->cond != nullptr) { 166 | node->cond->visit(this); 167 | } 168 | if (node->block != nullptr) { 169 | for (const auto& item : node->block->stmts) { 170 | item->visit(this); 171 | } 172 | } 173 | if (node->elseBlock != nullptr) { 174 | for (const auto& item : node->elseBlock->stmts) { 175 | item->visit(this); 176 | } 177 | } 178 | ident -= 2; 179 | } 180 | void AstDumper::visitWhileStmt(WhileStmt* node) { 181 | printPadding(); 182 | std::cout << "-WhileStmt" << std::endl; 183 | ident += 2; 184 | if (node->cond != nullptr) { 185 | node->cond->visit(this); 186 | } 187 | if (node->block != nullptr) { 188 | for (const auto& item : node->block->stmts) { 189 | item->visit(this); 190 | } 191 | } 192 | ident -= 2; 193 | } 194 | void AstDumper::visitForStmt(ForStmt* node) { 195 | printPadding(); 196 | std::cout << "-ForStmt" << std::endl; 197 | ident += 2; 198 | if (node->cond != nullptr) { 199 | node->cond->visit(this); 200 | } 201 | if (node->init != nullptr) { 202 | node->init->visit(this); 203 | } 204 | if (node->post != nullptr) { 205 | node->post->visit(this); 206 | } 207 | if (node->block != nullptr) { 208 | for (const auto& item : node->block->stmts) { 209 | item->visit(this); 210 | } 211 | } 212 | ident -= 2; 213 | } 214 | void AstDumper::visitForEachStmt(ForEachStmt* node) { 215 | printPadding(); 216 | std::cout << "-ForEachStmt" << std::endl; 217 | ident += 2; 218 | if (node->list != nullptr) { 219 | node->list->visit(this); 220 | } 221 | if (node->block != nullptr) { 222 | for (const auto& item : node->block->stmts) { 223 | item->visit(this); 224 | } 225 | } 226 | ident -= 2; 227 | } 228 | void AstDumper::visitMatchStmt(MatchStmt* node) { 229 | printPadding(); 230 | std::cout << "-MatchStmt" << std::endl; 231 | ident += 2; 232 | if (node->cond != nullptr) { 233 | node->cond->visit(this); 234 | } 235 | for (const auto& [theCase, theBranch, isAny] : node->matches) { 236 | theCase->visit(this); 237 | ident += 2; 238 | if (theBranch != nullptr) { 239 | for (const auto& item : theBranch->stmts) { 240 | item->visit(this); 241 | } 242 | } 243 | ident -= 2; 244 | } 245 | ident -= 2; 246 | } 247 | -------------------------------------------------------------------------------- /nyx/Ast.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2018-2023 y1yang0 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | 24 | #pragma once 25 | 26 | #include 27 | #include 28 | #include "Object.hpp" 29 | #include "Runtime.hpp" 30 | 31 | //===----------------------------------------------------------------------===// 32 | // Token definitions of nyx 33 | //===----------------------------------------------------------------------===// 34 | enum Token { 35 | INVALID = 0, // 36 | TK_IDENT, // 37 | TK_EOF, // 38 | LIT_INT, // 39 | LIT_STR, // 40 | LIT_DOUBLE, // 41 | LIT_CHAR, // 42 | 43 | TK_BITAND, // & 44 | TK_BITOR, // | 45 | TK_BITNOT, // ~ 46 | TK_LOGAND, // && 47 | TK_LOGOR, // || 48 | TK_LOGNOT, // ! 49 | 50 | TK_PLUS, // + 51 | TK_MINUS, // - 52 | TK_TIMES, // * 53 | TK_DIV, // / 54 | TK_MOD, // % 55 | 56 | TK_EQ, // == 57 | TK_NE, // != 58 | TK_GT, // > 59 | TK_GE, // >= 60 | TK_LT, // < 61 | TK_LE, // <= 62 | 63 | TK_ASSIGN, // = 64 | TK_PLUS_AGN, // += 65 | TK_MINUS_AGN, // -= 66 | TK_TIMES_AGN, // *= 67 | TK_DIV_AGN, // /= 68 | TK_MOD_AGN, // %= 69 | TK_MATCH, // => 70 | TK_COMMA, // , 71 | TK_LPAREN, // ( 72 | TK_RPAREN, // ) 73 | TK_LBRACE, // { 74 | TK_RBRACE, // } 75 | TK_LBRACKET, // [ 76 | TK_RBRACKET, // ] 77 | TK_SEMICOLON, // ; 78 | TK_COLON, // : 79 | TK_DOT, // . 80 | 81 | KW_IF, // if 82 | KW_ELSE, // else 83 | KW_TRUE, // true 84 | KW_FALSE, // false 85 | KW_WHILE, // while 86 | KW_FOR, // for 87 | KW_NULL, // null 88 | KW_FUNC, // func 89 | KW_RETURN, // return 90 | KW_BREAK, // break 91 | KW_CONTINUE, // continue 92 | KW_MATCH, // match 93 | }; 94 | 95 | struct Expression; 96 | struct Statement; 97 | struct AstNode; 98 | 99 | struct AstVisitor; 100 | struct BoolExpr; 101 | struct CharExpr; 102 | struct NullExpr; 103 | struct IntExpr; 104 | struct DoubleExpr; 105 | struct StringExpr; 106 | struct ArrayExpr; 107 | struct NameExpr; 108 | struct IndexExpr; 109 | struct BinaryExpr; 110 | struct FunCallExpr; 111 | struct AssignExpr; 112 | struct ClosureExpr; 113 | struct Statement; 114 | struct BreakStmt; 115 | struct ContinueStmt; 116 | struct SimpleStmt; 117 | struct ReturnStmt; 118 | struct IfStmt; 119 | struct WhileStmt; 120 | struct ForStmt; 121 | struct ForEachStmt; 122 | struct MatchStmt; 123 | 124 | struct AstVisitor { 125 | virtual void visitExpression(Expression* node) {} 126 | virtual void visitBoolExpr(BoolExpr* node) {} 127 | virtual void visitCharExpr(CharExpr* node) {} 128 | virtual void visitNullExpr(NullExpr* node) {} 129 | virtual void visitIntExpr(IntExpr* node) {} 130 | virtual void visitDoubleExpr(DoubleExpr* node) {} 131 | virtual void visitStringExpr(StringExpr* node) {} 132 | virtual void visitArrayExpr(ArrayExpr* node) {} 133 | virtual void visitNameExpr(NameExpr* node) {} 134 | virtual void visitIndexExpr(IndexExpr* node) {} 135 | virtual void visitBinaryExpr(BinaryExpr* node) {} 136 | virtual void visitFunCallExpr(FunCallExpr* node) {} 137 | virtual void visitAssignExpr(AssignExpr* node) {} 138 | virtual void visitClosureExpr(ClosureExpr* node) {} 139 | virtual void visitStatement(Statement* node) {} 140 | virtual void visitBreakStmt(BreakStmt* node) {} 141 | virtual void visitContinueStmt(ContinueStmt* node) {} 142 | virtual void visitSimpleStmt(SimpleStmt* node) {} 143 | virtual void visitReturnStmt(ReturnStmt* node) {} 144 | virtual void visitIfStmt(IfStmt* node) {} 145 | virtual void visitWhileStmt(WhileStmt* node) {} 146 | virtual void visitForStmt(ForStmt* node) {} 147 | virtual void visitForEachStmt(ForEachStmt* node) {} 148 | virtual void visitMatchStmt(MatchStmt* node) {} 149 | }; 150 | 151 | //===----------------------------------------------------------------------===// 152 | // Expression 153 | //===----------------------------------------------------------------------===// 154 | struct AstNode { 155 | explicit AstNode(int line, int column) : line(line), column(column) {} 156 | 157 | virtual ~AstNode() = default; 158 | 159 | virtual void visit(AstVisitor* visitor) = 0; 160 | 161 | int line = -1; 162 | int column = -1; 163 | }; 164 | 165 | struct Expression : public AstNode { 166 | using AstNode::AstNode; 167 | 168 | ~Expression() override = default; 169 | virtual Object* eval(Runtime* rt, ContextChain* ctxChain); 170 | 171 | void visit(AstVisitor* visitor) override { visitor->visitExpression(this); } 172 | }; 173 | 174 | struct BoolExpr : public Expression { 175 | using Expression::Expression; 176 | 177 | bool literal; 178 | 179 | Object* eval(Runtime* rt, ContextChain* ctxChain) override; 180 | 181 | void visit(AstVisitor* visitor) override { visitor->visitBoolExpr(this); } 182 | }; 183 | 184 | struct CharExpr : public Expression { 185 | using Expression::Expression; 186 | 187 | char literal; 188 | 189 | Object* eval(Runtime* rt, ContextChain* ctxChain) override; 190 | 191 | void visit(AstVisitor* visitor) override { visitor->visitCharExpr(this); } 192 | }; 193 | 194 | struct NullExpr : public Expression { 195 | using Expression::Expression; 196 | 197 | Object* eval(Runtime* rt, ContextChain* ctxChain) override; 198 | }; 199 | 200 | struct IntExpr : public Expression { 201 | using Expression::Expression; 202 | 203 | int literal; 204 | 205 | Object* eval(Runtime* rt, ContextChain* ctxChain) override; 206 | 207 | void visit(AstVisitor* visitor) override { visitor->visitIntExpr(this); } 208 | }; 209 | 210 | struct DoubleExpr : public Expression { 211 | using Expression::Expression; 212 | 213 | double literal; 214 | 215 | Object* eval(Runtime* rt, ContextChain* ctxChain) override; 216 | 217 | void visit(AstVisitor* visitor) override { visitor->visitDoubleExpr(this); } 218 | }; 219 | 220 | struct StringExpr : public Expression { 221 | using Expression::Expression; 222 | 223 | std::string literal; 224 | 225 | Object* eval(Runtime* rt, ContextChain* ctxChain) override; 226 | void visit(AstVisitor* visitor) override { visitor->visitStringExpr(this); } 227 | }; 228 | 229 | struct ArrayExpr : public Expression { 230 | using Expression::Expression; 231 | 232 | std::vector literal; 233 | 234 | Object* eval(Runtime* rt, ContextChain* ctxChain) override; 235 | void visit(AstVisitor* visitor) override { visitor->visitArrayExpr(this); } 236 | }; 237 | 238 | struct NameExpr : public Expression { 239 | using Expression::Expression; 240 | 241 | std::string identName; 242 | 243 | Object* eval(Runtime* rt, ContextChain* ctxChain) override; 244 | void visit(AstVisitor* visitor) override { visitor->visitNameExpr(this); } 245 | }; 246 | 247 | struct IndexExpr : public Expression { 248 | using Expression::Expression; 249 | 250 | std::string identName; 251 | Expression* index{}; 252 | 253 | Object* eval(Runtime* rt, ContextChain* ctxChain) override; 254 | void visit(AstVisitor* visitor) override { visitor->visitIndexExpr(this); } 255 | }; 256 | 257 | struct BinaryExpr : public Expression { 258 | using Expression::Expression; 259 | 260 | Expression* lhs{}; 261 | Token opt{}; 262 | Expression* rhs{}; 263 | 264 | Object* eval(Runtime* rt, ContextChain* ctxChain) override; 265 | 266 | void visit(AstVisitor* visitor) override { visitor->visitBinaryExpr(this); } 267 | }; 268 | 269 | struct FunCallExpr : public Expression { 270 | using Expression::Expression; 271 | 272 | Expression* receiver{}; 273 | std::string funcName; 274 | std::vector args; 275 | 276 | Object* eval(Runtime* rt, ContextChain* ctxChain) override; 277 | void visit(AstVisitor* visitor) override { 278 | visitor->visitFunCallExpr(this); 279 | } 280 | }; 281 | 282 | struct AssignExpr : public Expression { 283 | using Expression::Expression; 284 | 285 | Expression* lhs{}; 286 | Token opt; 287 | Expression* rhs{}; 288 | 289 | Object* eval(Runtime* rt, ContextChain* ctxChain) override; 290 | void visit(AstVisitor* visitor) override { visitor->visitAssignExpr(this); } 291 | }; 292 | 293 | struct ClosureExpr : public Expression { 294 | using Expression::Expression; 295 | 296 | std::vector params; 297 | Block* block{}; 298 | 299 | Object* eval(Runtime* rt, ContextChain* ctxChain) override; 300 | void visit(AstVisitor* visitor) override { 301 | visitor->visitClosureExpr(this); 302 | } 303 | }; 304 | 305 | //===----------------------------------------------------------------------===// 306 | // Statement 307 | //===----------------------------------------------------------------------===// 308 | struct Statement : public AstNode { 309 | using AstNode::AstNode; 310 | 311 | virtual ~Statement() = default; 312 | 313 | virtual ExecResult interpret(Runtime* rt, ContextChain* ctxChain); 314 | void visit(AstVisitor* visitor) override { visitor->visitStatement(this); } 315 | }; 316 | 317 | struct BreakStmt : public Statement { 318 | using Statement::Statement; 319 | 320 | ExecResult interpret(Runtime* rt, ContextChain* ctxChain) override; 321 | void visit(AstVisitor* visitor) override { visitor->visitBreakStmt(this); } 322 | }; 323 | 324 | struct ContinueStmt : public Statement { 325 | using Statement::Statement; 326 | 327 | ExecResult interpret(Runtime* rt, ContextChain* ctxChain) override; 328 | void visit(AstVisitor* visitor) override { 329 | visitor->visitContinueStmt(this); 330 | } 331 | }; 332 | 333 | struct SimpleStmt : public Statement { 334 | using Statement::Statement; 335 | 336 | Expression* expr{}; 337 | 338 | ExecResult interpret(Runtime* rt, ContextChain* ctxChain) override; 339 | void visit(AstVisitor* visitor) override { visitor->visitSimpleStmt(this); } 340 | }; 341 | 342 | struct ReturnStmt : public Statement { 343 | using Statement::Statement; 344 | 345 | Expression* ret{}; 346 | 347 | ExecResult interpret(Runtime* rt, ContextChain* ctxChain) override; 348 | void visit(AstVisitor* visitor) override { visitor->visitReturnStmt(this); } 349 | }; 350 | 351 | struct IfStmt : public Statement { 352 | using Statement::Statement; 353 | 354 | Expression* cond{}; 355 | Block* block{}; 356 | Block* elseBlock{}; 357 | 358 | ExecResult interpret(Runtime* rt, ContextChain* ctxChain) override; 359 | void visit(AstVisitor* visitor) override { visitor->visitIfStmt(this); } 360 | }; 361 | 362 | struct WhileStmt : public Statement { 363 | using Statement::Statement; 364 | 365 | Expression* cond{}; 366 | Block* block{}; 367 | 368 | ExecResult interpret(Runtime* rt, ContextChain* ctxChain) override; 369 | void visit(AstVisitor* visitor) override { visitor->visitWhileStmt(this); } 370 | }; 371 | 372 | struct ForStmt : public Statement { 373 | using Statement::Statement; 374 | 375 | Expression* init{}; 376 | Expression* cond{}; 377 | Expression* post{}; 378 | Block* block{}; 379 | 380 | ExecResult interpret(Runtime* rt, ContextChain* ctxChain) override; 381 | void visit(AstVisitor* visitor) override { visitor->visitForStmt(this); } 382 | }; 383 | 384 | struct ForEachStmt : public Statement { 385 | using Statement::Statement; 386 | 387 | std::string identName; 388 | Expression* list{}; 389 | Block* block{}; 390 | 391 | ExecResult interpret(Runtime* rt, ContextChain* ctxChain) override; 392 | void visit(AstVisitor* visitor) override { 393 | visitor->visitForEachStmt(this); 394 | } 395 | }; 396 | 397 | struct MatchStmt : public Statement { 398 | using Statement::Statement; 399 | 400 | Expression* cond{}; 401 | std::vector> matches; 402 | 403 | ExecResult interpret(Runtime* rt, ContextChain* ctxChain) override; 404 | void visit(AstVisitor* visitor) override { visitor->visitMatchStmt(this); } 405 | }; 406 | -------------------------------------------------------------------------------- /nyx/Object.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2023 y1yang0 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | 24 | #include "Object.hpp" 25 | #include "Builtin.h" 26 | #include "Runtime.hpp" 27 | #include "Utils.hpp" 28 | 29 | bool Object::equalsDeep(Object* b) const { 30 | if (type != b->type) { 31 | return false; 32 | } 33 | switch (type) { 34 | case Bool: 35 | return asBool() == b->asBool(); 36 | case Double: 37 | return asDouble() == b->asDouble(); 38 | case Int: 39 | return asInt() == b->asInt(); 40 | case Null: 41 | return true; 42 | case String: 43 | return asString() == b->asString(); 44 | case Char: 45 | return asChar() == b->asChar(); 46 | case Array: { 47 | auto elements1 = asArray(); 48 | auto elements2 = b->asArray(); 49 | if (elements1.size() != elements2.size()) { 50 | return false; 51 | } 52 | for (int i = 0; i < elements1.size(); i++) { 53 | if (!elements1[i]->equalsDeep(elements2[i])) { 54 | return false; 55 | } 56 | } 57 | return true; 58 | } 59 | } 60 | return false; 61 | } 62 | 63 | std::string Object::toString() const { 64 | switch (type) { 65 | case Bool: 66 | return asBool() ? "true" : "false"; 67 | case Double: 68 | return std::to_string(asDouble()); 69 | case Int: 70 | return std::to_string(asInt()); 71 | case Null: 72 | return "null"; 73 | case String: 74 | return asString(); 75 | case Char: { 76 | std::string str; 77 | str += asChar(); 78 | return str; 79 | } 80 | case Array: { 81 | std::string str = "["; 82 | auto elements = asArray(); 83 | for (int i = 0; i < elements.size(); i++) { 84 | str += elements[i]->toString(); 85 | 86 | if (i != elements.size() - 1) { 87 | str += ","; 88 | } 89 | } 90 | str += "]"; 91 | return str; 92 | } 93 | case Closure: { 94 | return "closure"; 95 | } 96 | } 97 | return ""; 98 | } 99 | 100 | bool Object::isPrimitive() const { 101 | if (anyone(type, Int, Double, String, Bool, Char)) { 102 | return true; 103 | } 104 | return false; 105 | } 106 | 107 | Object* Object::operator+(Object* rhs) const { 108 | // Basic 109 | if (isInt() && rhs->isInt()) { 110 | int result = asInt() + rhs->asInt(); 111 | return runtime->newObject(result); 112 | } else if (isDouble() && rhs->isDouble()) { 113 | double result = asDouble() + rhs->asDouble(); 114 | return runtime->newObject(result); 115 | } else if (isInt() && rhs->isDouble()) { 116 | double result = asInt() + rhs->asDouble(); 117 | return runtime->newObject(result); 118 | } else if (isDouble() && rhs->isInt()) { 119 | double result = asDouble() + rhs->asInt(); 120 | return runtime->newObject(result); 121 | } else if (isChar() && rhs->isInt()) { 122 | char result = static_cast(asChar() + rhs->asInt()); 123 | return runtime->newObject(result); 124 | } else if (isInt() && rhs->isChar()) { 125 | char result = static_cast(asInt() + rhs->asChar()); 126 | return runtime->newObject(result); 127 | } else if (isChar() && rhs->isChar()) { 128 | char result = static_cast(asChar() + rhs->asChar()); 129 | return runtime->newObject(result); 130 | } 131 | // String 132 | // One of operands has string type, we say the result value was a string 133 | else if (isString() || rhs->isString()) { 134 | std::string result = this->toString() + rhs->toString(); 135 | return runtime->newObject(result); 136 | } 137 | // Array 138 | else if (isArray()) { 139 | auto result = this->asArray(); 140 | result.push_back(rhs); 141 | return runtime->newObject(result); 142 | } else if (rhs->isArray()) { 143 | auto result = rhs->asArray(); 144 | result.push_back(const_cast(this)); 145 | return runtime->newObject(result); 146 | } 147 | // Invalid 148 | else { 149 | panic("unexpected arguments of operator +"); 150 | } 151 | return nullptr; 152 | } 153 | 154 | Object* Object::operator-(Object* rhs) const { 155 | if (isInt() && rhs->isInt()) { 156 | int result = asInt() - rhs->asInt(); 157 | return runtime->newObject(result); 158 | } else if (isDouble() && rhs->isDouble()) { 159 | double result = asDouble() - rhs->asDouble(); 160 | return runtime->newObject(result); 161 | } else if (isInt() && rhs->isDouble()) { 162 | double result = asInt() - rhs->asDouble(); 163 | return runtime->newObject(result); 164 | } else if (isDouble() && rhs->isInt()) { 165 | double result = asDouble() - rhs->asInt(); 166 | return runtime->newObject(result); 167 | } else if (isChar() && rhs->isInt()) { 168 | char result = static_cast(asChar() - rhs->asInt()); 169 | return runtime->newObject(result); 170 | } else if (isInt() && rhs->isChar()) { 171 | char result = static_cast(asInt() - rhs->asChar()); 172 | return runtime->newObject(result); 173 | } else if (isChar() && rhs->isChar()) { 174 | char result = static_cast(asChar() - rhs->asChar()); 175 | return runtime->newObject(result); 176 | } else { 177 | panic("unexpected arguments of operator -"); 178 | } 179 | 180 | return nullptr; 181 | } 182 | 183 | Object* Object::operator*(Object* rhs) const { 184 | // Basic 185 | if (isInt() && rhs->isInt()) { 186 | int result = asInt() * rhs->asInt(); 187 | return runtime->newObject(result); 188 | } else if (isDouble() && rhs->isDouble()) { 189 | double result = asDouble() * rhs->asDouble(); 190 | return runtime->newObject(result); 191 | } else if (isInt() && rhs->isDouble()) { 192 | double result = asInt() * rhs->asDouble(); 193 | return runtime->newObject(result); 194 | } else if (isDouble() && rhs->isInt()) { 195 | double result = asDouble() * rhs->asInt(); 196 | return runtime->newObject(result); 197 | } 198 | // String 199 | else if (isString() && rhs->isInt()) { 200 | std::string result = repeatString(rhs->asInt(), asString()); 201 | return runtime->newObject(result); 202 | } else if (isInt() && rhs->isString()) { 203 | std::string result = repeatString(asInt(), rhs->asString()); 204 | return runtime->newObject(result); 205 | } 206 | // Invalid 207 | else { 208 | panic("unexpected arguments of operator *"); 209 | } 210 | return nullptr; 211 | } 212 | 213 | Object* Object::operator/(Object* rhs) const { 214 | if (isInt() && rhs->isInt()) { 215 | int result = asInt() / rhs->asInt(); 216 | return runtime->newObject(result); 217 | } else if (isDouble() && rhs->isDouble()) { 218 | double result = asDouble() / rhs->asDouble(); 219 | return runtime->newObject(result); 220 | } else if (isInt() && rhs->isDouble()) { 221 | double result = asInt() / rhs->asDouble(); 222 | return runtime->newObject(result); 223 | } else if (isDouble() && rhs->isInt()) { 224 | double result = asDouble() / rhs->asInt(); 225 | return runtime->newObject(result); 226 | } else { 227 | panic("unexpected arguments of operator /"); 228 | } 229 | return nullptr; 230 | } 231 | 232 | Object* Object::operator%(Object* rhs) const { 233 | checkObjectType(this, Int); 234 | checkObjectType(rhs, Int); 235 | int result = (int)asInt() % rhs->asInt(); 236 | return runtime->newObject(result); 237 | } 238 | 239 | Object* Object::operator&&(Object* rhs) const { 240 | checkObjectType(this, Bool); 241 | checkObjectType(rhs, Bool); 242 | bool result = (asBool() && rhs->asBool()); 243 | return runtime->newObject(result); 244 | } 245 | 246 | Object* Object::operator||(Object* rhs) const { 247 | checkObjectType(this, Bool); 248 | checkObjectType(rhs, Bool); 249 | bool result = (asBool() || rhs->asBool()); 250 | return runtime->newObject(result); 251 | } 252 | 253 | Object* Object::operator==(Object* rhs) const { 254 | bool result = false; 255 | if (isInt() && rhs->isInt()) { 256 | result = (asInt() == rhs->asInt()); 257 | return runtime->newObject(result); 258 | } else if (isDouble() && rhs->isDouble()) { 259 | result = (asDouble() == rhs->asDouble()); 260 | return runtime->newObject(result); 261 | } else if (isString() && rhs->isString()) { 262 | std::string lhsStr, rhsStr; 263 | lhsStr = this->toString(); 264 | rhsStr = rhs->toString(); 265 | result = (lhsStr == rhsStr); 266 | return runtime->newObject(result); 267 | } else if (isBool() && rhs->isBool()) { 268 | result = (asBool() == rhs->asBool()); 269 | return runtime->newObject(result); 270 | } else if (this->type == Null && rhs->type == Null) { 271 | result = true; 272 | return runtime->newObject(result); 273 | } else if (isChar() && rhs->isChar()) { 274 | result = (asChar() == rhs->asChar()); 275 | return runtime->newObject(result); 276 | } else if (isArray() && rhs->isArray()) { 277 | result = this->equalsDeep(rhs); 278 | return runtime->newObject(result); 279 | } else { 280 | panic("unexpected arguments of operator =="); 281 | } 282 | return nullptr; 283 | } 284 | 285 | Object* Object::operator!=(Object* rhs) const { 286 | bool result = false; 287 | if (isInt() && rhs->isInt()) { 288 | result = (asInt() != rhs->asInt()); 289 | return runtime->newObject(result); 290 | } else if (isDouble() && rhs->isDouble()) { 291 | result = (asDouble() != rhs->asDouble()); 292 | return runtime->newObject(result); 293 | } else if (isString() && rhs->isString()) { 294 | std::string lhsStr, rhsStr; 295 | lhsStr = this->toString(); 296 | rhsStr = rhs->toString(); 297 | result = (lhsStr != rhsStr); 298 | return runtime->newObject(result); 299 | } else if (isBool() && rhs->isBool()) { 300 | result = (asBool() != rhs->asBool()); 301 | return runtime->newObject(result); 302 | } else if (this->type == Null && rhs->type == Null) { 303 | result = false; 304 | return runtime->newObject(result); 305 | } else if (isChar() && rhs->isChar()) { 306 | result = (asChar() != rhs->asChar()); 307 | return runtime->newObject(result); 308 | } else if (isArray() && rhs->isArray()) { 309 | result = !this->equalsDeep(rhs); 310 | return runtime->newObject(result); 311 | } else { 312 | panic("unexpected arguments of operator !="); 313 | } 314 | return nullptr; 315 | } 316 | 317 | Object* Object::operator>(Object* rhs) const { 318 | bool result = false; 319 | if (isInt() && rhs->isInt()) { 320 | result = (asInt() > rhs->asInt()); 321 | return runtime->newObject(result); 322 | } else if (isDouble() && rhs->isDouble()) { 323 | result = (asDouble() > rhs->asDouble()); 324 | return runtime->newObject(result); 325 | } else if (isString() && rhs->isString()) { 326 | std::string lhsStr, rhsStr; 327 | lhsStr = this->toString(); 328 | rhsStr = rhs->toString(); 329 | result = (lhsStr > rhsStr); 330 | return runtime->newObject(result); 331 | } else if (isChar() && rhs->isChar()) { 332 | result = (asChar() > rhs->asChar()); 333 | return runtime->newObject(result); 334 | } else { 335 | panic("unexpected arguments of operator <="); 336 | } 337 | return nullptr; 338 | } 339 | 340 | Object* Object::operator>=(Object* rhs) const { 341 | bool result = false; 342 | if (isInt() && rhs->isInt()) { 343 | result = (asInt() >= rhs->asInt()); 344 | return runtime->newObject(result); 345 | } else if (isDouble() && rhs->isDouble()) { 346 | result = (asDouble() >= rhs->asDouble()); 347 | return runtime->newObject(result); 348 | } else if (isString() && rhs->isString()) { 349 | std::string lhsStr, rhsStr; 350 | lhsStr = this->toString(); 351 | rhsStr = rhs->toString(); 352 | result = (lhsStr >= rhsStr); 353 | return runtime->newObject(result); 354 | } else if (isChar() && rhs->isChar()) { 355 | result = (asChar() >= rhs->asChar()); 356 | return runtime->newObject(result); 357 | } else { 358 | panic("unexpected arguments of operator <="); 359 | } 360 | return nullptr; 361 | } 362 | 363 | Object* Object::operator<(Object* rhs) const { 364 | bool result = false; 365 | if (isInt() && rhs->isInt()) { 366 | result = (asInt() < rhs->asInt()); 367 | return runtime->newObject(result); 368 | } else if (isDouble() && rhs->isDouble()) { 369 | result = (asDouble() < rhs->asDouble()); 370 | return runtime->newObject(result); 371 | } else if (isString() && rhs->isString()) { 372 | std::string lhsStr, rhsStr; 373 | lhsStr = this->toString(); 374 | rhsStr = rhs->toString(); 375 | result = (lhsStr < rhsStr); 376 | return runtime->newObject(result); 377 | } else if (isChar() && rhs->isChar()) { 378 | result = (asChar() < rhs->asChar()); 379 | return runtime->newObject(result); 380 | } else { 381 | panic("unexpected arguments of operator <="); 382 | } 383 | return nullptr; 384 | } 385 | 386 | Object* Object::operator<=(Object* rhs) const { 387 | bool result = false; 388 | if (isInt() && rhs->isInt()) { 389 | result = (asInt() <= rhs->asInt()); 390 | return runtime->newObject(result); 391 | } else if (isDouble() && rhs->isDouble()) { 392 | result = (asDouble() <= rhs->asDouble()); 393 | return runtime->newObject(result); 394 | } else if (isString() && rhs->isString()) { 395 | std::string lhsStr, rhsStr; 396 | lhsStr = this->toString(); 397 | rhsStr = rhs->toString(); 398 | result = (lhsStr <= rhsStr); 399 | return runtime->newObject(result); 400 | } else if (isChar() && rhs->isChar()) { 401 | result = (asChar() <= rhs->asChar()); 402 | return runtime->newObject(result); 403 | } else { 404 | panic("unexpected arguments of operator <="); 405 | } 406 | return nullptr; 407 | } 408 | 409 | Object* Object::operator&(Object* rhs) const { 410 | checkObjectType(this, Int); 411 | checkObjectType(rhs, Int); 412 | 413 | int result = (asInt() & rhs->asInt()); 414 | return runtime->newObject(result); 415 | } 416 | 417 | Object* Object::operator|(Object* rhs) const { 418 | checkObjectType(this, Int); 419 | checkObjectType(rhs, Int); 420 | 421 | int result = (asInt() | rhs->asInt()); 422 | return runtime->newObject(result); 423 | } 424 | 425 | Object* Object::operator-() const { 426 | switch (type) { 427 | case Int: 428 | return runtime->newObject(-(*(int*)data)); 429 | case Double: 430 | return runtime->newObject(-(*(double*)data)); 431 | default: 432 | panic("invalid operand type for operator -(negative)"); 433 | } 434 | return nullptr; 435 | } 436 | 437 | Object* Object::operator!() const { 438 | checkObjectType(this, Bool); 439 | return runtime->newObject(!(*(bool*)data)); 440 | } 441 | 442 | Object* Object::operator~() const { 443 | checkObjectType(this, Int); 444 | return runtime->newObject(-(*(int*)data)); 445 | } -------------------------------------------------------------------------------- /nyx/Interpreter.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2018-2023 y1yang0 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | #include "Interpreter.h" 24 | #include 25 | #include 26 | #include 27 | #include "Ast.h" 28 | #include "Debug.hpp" 29 | #include "Object.hpp" 30 | #include "Runtime.hpp" 31 | #include "Utils.hpp" 32 | 33 | void Interpreter::execute(Runtime* rt) { 34 | Interpreter::newContext(ctxChain); 35 | 36 | AstDumper dumper; 37 | for (auto stmt : rt->getStatements()) { 38 | #if NYX_DEBUG 39 | stmt->visit(&dumper); 40 | #endif 41 | stmt->interpret(rt, ctxChain); 42 | } 43 | } 44 | 45 | void Interpreter::newContext(ContextChain* ctxChain) { 46 | auto* tempContext = new Context; 47 | ctxChain->push_back(tempContext); 48 | } 49 | 50 | Object* Interpreter::callFunc(Runtime* rt, 51 | Func* f, 52 | ContextChain* lastCtxChain, 53 | std::vector args) { 54 | ContextChain* funcCtxChain = nullptr; 55 | if (!f->name.empty() || f->outerContext == nullptr) { 56 | funcCtxChain = new ContextChain(); 57 | } else { 58 | funcCtxChain = f->outerContext; 59 | } 60 | Interpreter::newContext(funcCtxChain); 61 | 62 | auto* funcCtx = funcCtxChain->back(); 63 | for (int i = 0; i < f->params.size(); i++) { 64 | std::string paramName = f->params[i]; 65 | // Evaluate argument values from previous context chain and push them 66 | // into newly created context chain 67 | Object* argValue = args[i]->eval(rt, lastCtxChain); 68 | if (argValue->isPrimitive()) { 69 | // Pass by value 70 | funcCtx->createVariable(paramName, rt->cloneObject(argValue)); 71 | } else { 72 | // Pass by reference 73 | funcCtx->createVariable(paramName, argValue); 74 | } 75 | } 76 | 77 | // Execute user defined function 78 | ExecResult ret(ExecNormal); 79 | for (auto& stmt : f->block->stmts) { 80 | ret = stmt->interpret(rt, funcCtxChain); 81 | if (ret.execType == ExecReturn) { 82 | break; 83 | } 84 | } 85 | // Do not free context memory deliberately since we are not ready yet... 86 | return ret.retValue; 87 | } 88 | 89 | Object* Interpreter::evalUnaryExpr(Object* lhs, Token opt) { 90 | switch (opt) { 91 | case TK_MINUS: 92 | return lhs->operator-(); 93 | case TK_LOGNOT: 94 | return lhs->operator!(); 95 | case TK_BITNOT: 96 | return lhs->operator~(); 97 | default: 98 | panic("unexpected token %d", opt); 99 | } 100 | 101 | return lhs; 102 | } 103 | 104 | Object* Interpreter::evalBinaryExpr(Object* lhs, Token opt, Object* rhs) { 105 | switch (opt) { 106 | case TK_PLUS: 107 | return lhs->operator+(rhs); 108 | case TK_MINUS: 109 | return lhs->operator-(rhs); 110 | case TK_TIMES: 111 | return lhs->operator*(rhs); 112 | case TK_DIV: 113 | return lhs->operator/(rhs); 114 | case TK_MOD: 115 | return lhs->operator%(rhs); 116 | case TK_LOGAND: 117 | return lhs->operator&&(rhs); 118 | case TK_LOGOR: 119 | return lhs->operator||(rhs); 120 | case TK_EQ: 121 | return lhs->operator==(rhs); 122 | case TK_NE: 123 | return lhs->operator!=(rhs); 124 | break; 125 | case TK_GT: 126 | return lhs->operator>(rhs); 127 | case TK_GE: 128 | return lhs->operator>=(rhs); 129 | case TK_LT: 130 | return lhs->operator<(rhs); 131 | case TK_LE: 132 | return lhs->operator<=(rhs); 133 | case TK_BITAND: 134 | return lhs->operator&(rhs); 135 | case TK_BITOR: 136 | return lhs->operator|(rhs); 137 | default: 138 | panic("unexpected token %d", opt); 139 | } 140 | return nullptr; 141 | } 142 | 143 | Object* Interpreter::assignment(Token opt, Object* lhs, Object* rhs) { 144 | switch (opt) { 145 | case TK_ASSIGN: 146 | return rhs; 147 | case TK_PLUS_AGN: 148 | return lhs->operator+(rhs); 149 | case TK_MINUS_AGN: 150 | return lhs->operator-(rhs); 151 | case TK_TIMES_AGN: 152 | return lhs->operator*(rhs); 153 | case TK_DIV_AGN: 154 | return lhs->operator/(rhs); 155 | case TK_MOD_AGN: 156 | return lhs->operator%(rhs); 157 | default: 158 | panic("unexpected branch reached"); 159 | } 160 | } 161 | 162 | //===----------------------------------------------------------------------===// 163 | // Interpret various statements within given runtime and context chain. Runtime 164 | // holds all necessary data that widely used in every context. Context chain 165 | // saves a linked contexts of current execution flow. 166 | //===----------------------------------------------------------------------===// 167 | ExecResult IfStmt::interpret(Runtime* rt, ContextChain* ctxChain) { 168 | ExecResult ret(ExecNormal); 169 | Object* condition = this->cond->eval(rt, ctxChain); 170 | if (!condition->isBool()) { 171 | panic( 172 | "expects bool type in while condition at line %d, " 173 | "col %d\n", 174 | line, column); 175 | } 176 | if (condition->asBool()) { 177 | Interpreter::newContext(ctxChain); 178 | for (auto& stmt : block->stmts) { 179 | ret = stmt->interpret(rt, ctxChain); 180 | if (ret.execType == ExecReturn) { 181 | break; 182 | } else if (ret.execType == ExecBreak) { 183 | break; 184 | } else if (ret.execType == ExecContinue) { 185 | break; 186 | } 187 | } 188 | 189 | } else { 190 | if (elseBlock != nullptr) { 191 | Interpreter::newContext(ctxChain); 192 | for (auto& elseStmt : elseBlock->stmts) { 193 | ret = elseStmt->interpret(rt, ctxChain); 194 | if (ret.execType == ExecReturn) { 195 | break; 196 | } else if (ret.execType == ExecBreak) { 197 | break; 198 | } else if (ret.execType == ExecContinue) { 199 | break; 200 | } 201 | } 202 | } 203 | } 204 | return ret; 205 | } 206 | 207 | ExecResult WhileStmt::interpret(Runtime* rt, ContextChain* ctxChain) { 208 | ExecResult ret{ExecNormal}; 209 | 210 | Interpreter::newContext(ctxChain); 211 | Object* condition = this->cond->eval(rt, ctxChain); 212 | 213 | while (condition->asBool()) { 214 | for (auto& stmt : block->stmts) { 215 | ret = stmt->interpret(rt, ctxChain); 216 | if (ret.execType == ExecReturn) { 217 | goto outside; 218 | } else if (ret.execType == ExecBreak) { 219 | // Disable propagating through the whole chain 220 | ret.execType = ExecNormal; 221 | goto outside; 222 | } else if (ret.execType == ExecContinue) { 223 | // Disable propagating through the whole chain 224 | ret.execType = ExecNormal; 225 | break; 226 | } 227 | } 228 | condition = this->cond->eval(rt, ctxChain); 229 | if (!condition->isBool()) { 230 | panic( 231 | "expects bool type in while condition at line %d, " 232 | "col %d\n", 233 | line, column); 234 | } 235 | } 236 | 237 | outside: 238 | 239 | return ret; 240 | } 241 | 242 | ExecResult ForStmt::interpret(Runtime* rt, ContextChain* ctxChain) { 243 | ExecResult ret{ExecNormal}; 244 | 245 | Interpreter::newContext(ctxChain); 246 | this->init->eval(rt, ctxChain); 247 | Object* condition = this->cond->eval(rt, ctxChain); 248 | 249 | while (condition->asBool()) { 250 | for (auto& stmt : block->stmts) { 251 | ret = stmt->interpret(rt, ctxChain); 252 | if (ret.execType == ExecReturn) { 253 | goto outside; 254 | } else if (ret.execType == ExecBreak) { 255 | ret.execType = ExecNormal; 256 | goto outside; 257 | } else if (ret.execType == ExecContinue) { 258 | ret.execType = ExecNormal; 259 | break; 260 | } 261 | } 262 | 263 | this->post->eval(rt, ctxChain); 264 | condition = this->cond->eval(rt, ctxChain); 265 | if (!condition->isBool()) { 266 | panic( 267 | "expects bool type in while condition at line %d, " 268 | "col %d\n", 269 | line, column); 270 | } 271 | } 272 | 273 | outside: 274 | 275 | return ret; 276 | } 277 | 278 | ExecResult ForEachStmt::interpret(Runtime* rt, ContextChain* ctxChain) { 279 | ExecResult ret{ExecNormal}; 280 | 281 | Interpreter::newContext(ctxChain); 282 | 283 | // Save current context for further iterator updating, we should not expect 284 | // to call deque.back() to get this since later statement interpretation 285 | // might push new context into context chain 286 | auto& currentCtx = ctxChain->back(); 287 | currentCtx->createVariable(this->identName, rt->newObject()); 288 | Object* listV = this->list->eval(rt, ctxChain); 289 | if (!listV->isArray()) { 290 | panic( 291 | "expects array type within foreach statement at line " 292 | "%d, col %d\n", 293 | line, column); 294 | } 295 | auto listValues = listV->asArray(); 296 | for (auto val : listValues) { 297 | currentCtx->getVariable(identName)->value = val; 298 | 299 | for (auto stmt : this->block->stmts) { 300 | ret = stmt->interpret(rt, ctxChain); 301 | if (ret.execType == ExecReturn) { 302 | goto outside; 303 | } else if (ret.execType == ExecBreak) { 304 | ret.execType = ExecNormal; 305 | goto outside; 306 | } else if (ret.execType == ExecContinue) { 307 | ret.execType = ExecNormal; 308 | break; 309 | } 310 | } 311 | } 312 | 313 | outside: 314 | 315 | return ret; 316 | } 317 | 318 | ExecResult MatchStmt::interpret(Runtime* rt, ContextChain* ctxChain) { 319 | ExecResult ret{ExecNormal}; 320 | 321 | Object* condition = 322 | cond != nullptr ? this->cond->eval(rt, ctxChain) : rt->newObject(true); 323 | 324 | for (const auto& [theCase, theBranch, isAny] : this->matches) { 325 | // We must first check if it's an any(_) match because the later one 326 | // will actually evaluate the value of case expression, that is, the 327 | // identifier _ will be evaluated and might cause undefined variable 328 | // error. 329 | if (isAny || condition->equalsDeep(theCase->eval(rt, ctxChain))) { 330 | Interpreter::newContext(ctxChain); 331 | for (auto stmt : theBranch->stmts) { 332 | ret = stmt->interpret(rt, ctxChain); 333 | } 334 | 335 | // Stop matching and clean up context, it will propagate execution 336 | // type to upper statement 337 | goto finish; 338 | } 339 | } 340 | 341 | finish: 342 | return ret; 343 | } 344 | 345 | ExecResult SimpleStmt::interpret(Runtime* rt, ContextChain* ctxChain) { 346 | this->expr->eval(rt, ctxChain); 347 | return ExecResult(ExecNormal); 348 | } 349 | 350 | ExecResult ReturnStmt::interpret(Runtime* rt, ContextChain* ctxChain) { 351 | if (this->ret != nullptr) { 352 | Object* retVal = this->ret->eval(rt, ctxChain); 353 | return ExecResult(ExecReturn, retVal); 354 | } else { 355 | return ExecResult(ExecReturn, nullptr); 356 | } 357 | } 358 | 359 | ExecResult BreakStmt::interpret(Runtime* rt, ContextChain* ctxChain) { 360 | return ExecResult(ExecBreak); 361 | } 362 | 363 | ExecResult ContinueStmt::interpret(Runtime* rt, ContextChain* ctxChain) { 364 | return ExecResult(ExecContinue); 365 | } 366 | 367 | //===----------------------------------------------------------------------===// 368 | // Evaluate all expressions and return a Object structure, this object 369 | // contains evaluated data and corresponding data type, it represents sorts 370 | // of(also all) data type in nyx and can get value by interpreter directly. 371 | //===----------------------------------------------------------------------===// 372 | Object* NullExpr::eval(Runtime* rt, ContextChain* ctxChain) { 373 | return rt->newObject(); 374 | } 375 | 376 | Object* BoolExpr::eval(Runtime* rt, ContextChain* ctxChain) { 377 | return rt->newObject(this->literal); 378 | } 379 | 380 | Object* CharExpr::eval(Runtime* rt, ContextChain* ctxChain) { 381 | return rt->newObject(this->literal); 382 | } 383 | 384 | Object* IntExpr::eval(Runtime* rt, ContextChain* ctxChain) { 385 | return rt->newObject(this->literal); 386 | } 387 | 388 | Object* DoubleExpr::eval(Runtime* rt, ContextChain* ctxChain) { 389 | return rt->newObject(this->literal); 390 | } 391 | 392 | Object* StringExpr::eval(Runtime* rt, ContextChain* ctxChain) { 393 | return rt->newObject(this->literal); 394 | } 395 | 396 | Object* ArrayExpr::eval(Runtime* rt, ContextChain* ctxChain) { 397 | ObjectArray elements; 398 | for (auto& e : this->literal) { 399 | elements.push_back(e->eval(rt, ctxChain)); 400 | } 401 | 402 | return rt->newObject(elements); 403 | } 404 | 405 | Object* ClosureExpr::eval(Runtime* rt, ContextChain* ctxChain) { 406 | auto* f = new Func; 407 | f->params = std::move(this->params); 408 | f->block = this->block; 409 | f->outerContext = ctxChain; // Save outer context for closure 410 | return rt->newObject(*f); 411 | } 412 | 413 | Object* NameExpr::eval(Runtime* rt, ContextChain* ctxChain) { 414 | for (auto p = ctxChain->crbegin(); p != ctxChain->crend(); ++p) { 415 | auto* ctx = *p; 416 | if (auto* var = ctx->getVariable(this->identName); var != nullptr) { 417 | return var->value; 418 | } 419 | if (auto* var = ctx->getFunction(this->identName); var != nullptr) { 420 | return rt->newObject(*var); 421 | } 422 | } 423 | // Lookup function in global scope 424 | auto* globalFunc = rt->getFunction(this->identName); 425 | if (globalFunc != nullptr) { 426 | return rt->newObject(*globalFunc); 427 | } 428 | 429 | panic( 430 | "use of undefined variable \"%s\" at line %d, col " 431 | "%d\n", 432 | identName.c_str(), this->line, this->column); 433 | } 434 | 435 | Object* IndexExpr::eval(Runtime* rt, ContextChain* ctxChain) { 436 | for (auto p = ctxChain->crbegin(); p != ctxChain->crend(); ++p) { 437 | auto* ctx = *p; 438 | if (auto* var = ctx->getVariable(this->identName); var != nullptr) { 439 | auto idx = this->index->eval(rt, ctxChain); 440 | if (!idx->isInt()) { 441 | panic( 442 | "expects int type within indexing " 443 | "expression at " 444 | "line %d, col %d\n", 445 | line, column); 446 | } 447 | if (idx->asInt() >= var->value->asArray().size()) { 448 | panic( 449 | "index %d out of range at line %d, col " 450 | "%d\n", 451 | idx->asInt(), line, column); 452 | } 453 | return var->value->asArray()[idx->asInt()]; 454 | } 455 | } 456 | panic( 457 | "use of undefined variable \"%s\" at line %d, col " 458 | "%d\n", 459 | identName.c_str(), this->line, this->column); 460 | } 461 | 462 | Object* AssignExpr::eval(Runtime* rt, ContextChain* ctxChain) { 463 | Object* rhs = this->rhs->eval(rt, ctxChain); 464 | if (typeid(*lhs) == typeid(NameExpr)) { 465 | std::string identName = dynamic_cast(lhs)->identName; 466 | 467 | for (auto p = ctxChain->crbegin(); p != ctxChain->crend(); ++p) { 468 | if (auto* var = (*p)->getVariable(identName); var != nullptr) { 469 | var->value = 470 | Interpreter::assignment(this->opt, var->value, rhs); 471 | return rhs; 472 | } 473 | } 474 | 475 | (ctxChain->back())->createVariable(identName, rhs); 476 | } else if (typeid(*lhs) == typeid(IndexExpr)) { 477 | std::string identName = dynamic_cast(lhs)->identName; 478 | Object* index = 479 | dynamic_cast(lhs)->index->eval(rt, ctxChain); 480 | if (!index->isInt()) { 481 | panic( 482 | "expects int type when applying indexing " 483 | "to variable %s at line %d, col %d\n", 484 | identName.c_str(), line, column); 485 | } 486 | for (auto p = ctxChain->crbegin(); p != ctxChain->crend(); ++p) { 487 | if (auto* var = (*p)->getVariable(identName); var != nullptr) { 488 | if (!var->value->isArray()) { 489 | panic( 490 | "expects array type of variable %s " 491 | "at line %d, col %d\n", 492 | identName.c_str(), line, column); 493 | } 494 | auto temp = var->value->asArray(); 495 | temp[index->asInt()] = Interpreter::assignment( 496 | this->opt, temp[index->asInt()], rhs); 497 | var->value->resetObject(temp); 498 | return rhs; 499 | } 500 | } 501 | 502 | (ctxChain->back())->createVariable(identName, rhs); 503 | } else { 504 | panic("can not assign to %s at line %d, col %d\n", typeid(lhs).name(), 505 | line, column); 506 | } 507 | return rhs; 508 | } 509 | 510 | Object* FunCallExpr::eval(Runtime* rt, ContextChain* ctxChain) { 511 | if (this->receiver != nullptr) { 512 | // A method call, find method from receiver type 513 | Object* recv = receiver->eval(rt, ctxChain); 514 | // TODO: this dirty hack should be refactor out once we implement OOP 515 | // mechanism 516 | if (recv->isArray()) { 517 | if (funcName == "length") { 518 | return rt->newObject((int)(recv->asArray().size())); 519 | } 520 | } else if (recv->isString()) { 521 | if (funcName == "length") { 522 | return rt->newObject((int)(recv->asString().length())); 523 | } 524 | } 525 | } 526 | // Otherwise, it's a function call, find it as the builtin-in function 527 | // firstly then find it as user defined function, the lookup order implies 528 | // builtin function has higher priority when we have the same name of user 529 | // defined ones 530 | if (auto* builtinFunc = rt->getBuiltinFunction(this->funcName); 531 | builtinFunc != nullptr) { 532 | ObjectArray arguments; 533 | for (auto e : this->args) { 534 | arguments.push_back(e->eval(rt, ctxChain)); 535 | } 536 | return builtinFunc(rt, ctxChain, arguments); 537 | } 538 | 539 | // Find it as a user defined function 540 | if (auto* normalFunc = rt->getFunction(this->funcName); 541 | normalFunc != nullptr) { 542 | if (normalFunc->params.size() != this->args.size()) { 543 | panic( 544 | "expects %d arguments but got %d at line %d, " 545 | "col %d\n", 546 | normalFunc->params.size(), this->args.size(), line, column); 547 | } 548 | return Interpreter::callFunc(rt, normalFunc, ctxChain, this->args); 549 | } 550 | 551 | // Find it as a closure function 552 | for (auto ctx = ctxChain->crbegin(); ctx != ctxChain->crend(); ++ctx) { 553 | if (auto* closure = (*ctx)->getVariable(this->funcName); 554 | closure != nullptr && closure->value->isClosure()) { 555 | auto closureFunc = closure->value->asClosure(); 556 | if (closureFunc.params.size() != this->args.size()) { 557 | panic( 558 | "expects %d arguments but got %d at line " 559 | "%d, col %d\n", 560 | closureFunc.params.size(), this->args.size(), line, column); 561 | } 562 | return Interpreter::callFunc(rt, &closureFunc, ctxChain, 563 | this->args); 564 | } 565 | } 566 | 567 | // Panicking since this function was not found 568 | panic("can not find function %s at line %d, col %d", this->funcName.c_str(), 569 | line, column); 570 | } 571 | 572 | Object* BinaryExpr::eval(Runtime* rt, ContextChain* ctxChain) { 573 | Object* lhsObject = 574 | this->lhs ? this->lhs->eval(rt, ctxChain) : rt->newObject(); 575 | Object* rhsObject = 576 | this->rhs ? this->rhs->eval(rt, ctxChain) : rt->newObject(); 577 | Token exprOpt = this->opt; 578 | 579 | if (!lhsObject->isNull() && rhsObject->isNull()) { 580 | return Interpreter::evalUnaryExpr(lhsObject, exprOpt); 581 | } 582 | 583 | return Interpreter::evalBinaryExpr(lhsObject, exprOpt, rhsObject); 584 | } 585 | 586 | Object* Expression::eval(Runtime* rt, ContextChain* ctxChain) { 587 | panic( 588 | "abstract expression at line %d, " 589 | "col " 590 | "%d\n", 591 | line, column); 592 | } 593 | 594 | ExecResult Statement::interpret(Runtime* rt, ContextChain* ctxChain) { 595 | panic( 596 | "abstract statement at line %d, " 597 | "col" 598 | "%d\n", 599 | line, column); 600 | } 601 | -------------------------------------------------------------------------------- /nyx/Parser.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2018-2023 y1yang0 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | 24 | #include "Parser.h" 25 | #include 26 | #include "Runtime.hpp" 27 | #include "Utils.hpp" 28 | 29 | static const std::unordered_map KEYWORDS = { 30 | {"if", KW_IF}, {"else", KW_ELSE}, {"while", KW_WHILE}, 31 | {"null", KW_NULL}, {"true", KW_TRUE}, {"false", KW_FALSE}, 32 | {"for", KW_FOR}, {"func", KW_FUNC}, {"return", KW_RETURN}, 33 | {"break", KW_BREAK}, {"continue", KW_CONTINUE}, {"match", KW_MATCH}}; 34 | 35 | Parser::Parser(const std::string& fileName) { 36 | fs.open(fileName); 37 | if (!fs.is_open()) { 38 | panic("can not open source file"); 39 | } 40 | } 41 | 42 | Parser::~Parser() { 43 | fs.close(); 44 | } 45 | 46 | //===----------------------------------------------------------------------===// 47 | // Parse expressions 48 | //===----------------------------------------------------------------------===// 49 | Expression* Parser::parsePrimaryExpr() { 50 | switch (getCurrentToken()) { 51 | case TK_IDENT: { 52 | auto ident = getCurrentLexeme(); 53 | currentToken = next(); 54 | switch (getCurrentToken()) { 55 | case TK_LPAREN: { 56 | currentToken = next(); 57 | auto* val = new FunCallExpr(line, column); 58 | val->funcName = ident; 59 | while (getCurrentToken() != TK_RPAREN) { 60 | val->args.push_back(parseExpression()); 61 | if (getCurrentToken() == TK_COMMA) { 62 | currentToken = next(); 63 | } 64 | } 65 | assert(getCurrentToken() == TK_RPAREN); 66 | currentToken = next(); 67 | return val; 68 | } 69 | case TK_LBRACKET: { 70 | currentToken = next(); 71 | auto* val = new IndexExpr(line, column); 72 | val->identName = ident; 73 | val->index = parseExpression(); 74 | assert(val->index != nullptr); 75 | assert(getCurrentToken() == TK_RBRACKET); 76 | currentToken = next(); 77 | return val; 78 | } 79 | default: { 80 | auto* node = new NameExpr(line, column); 81 | node->identName = ident; 82 | return node; 83 | } 84 | } 85 | } 86 | case TK_LBRACKET: { 87 | currentToken = next(); 88 | auto* ret = new ArrayExpr(line, column); 89 | if (getCurrentToken() != TK_RBRACKET) { 90 | while (getCurrentToken() != TK_RBRACKET) { 91 | ret->literal.push_back(parseExpression()); 92 | if (getCurrentToken() == TK_COMMA) { 93 | currentToken = next(); 94 | } 95 | } 96 | assert(getCurrentToken() == TK_RBRACKET); 97 | currentToken = next(); 98 | return ret; 99 | } else { 100 | currentToken = next(); 101 | // It's an empty array literal 102 | return ret; 103 | } 104 | } 105 | case KW_FUNC: { 106 | currentToken = next(); 107 | assert(getCurrentToken() == TK_LPAREN); 108 | auto* ret = new ClosureExpr(line, column); 109 | ret->params = parseParameterList(); 110 | if (getCurrentToken() == TK_LBRACE) { 111 | ret->block = parseBlock(); 112 | } else if (getCurrentToken() == TK_MATCH) { 113 | currentToken = next(); 114 | ret->block = new Block; 115 | ret->block->stmts.push_back(parseStatement()); 116 | } else { 117 | panic("expects => or { after closure declaration"); 118 | } 119 | return ret; 120 | } 121 | case LIT_INT: { 122 | auto val = atoi(getCurrentLexeme().c_str()); 123 | currentToken = next(); 124 | auto* ret = new IntExpr(line, column); 125 | ret->literal = val; 126 | return ret; 127 | } 128 | case LIT_DOUBLE: { 129 | auto val = atof(getCurrentLexeme().c_str()); 130 | currentToken = next(); 131 | auto* ret = new DoubleExpr(line, column); 132 | ret->literal = val; 133 | return ret; 134 | } 135 | case LIT_STR: { 136 | auto val = getCurrentLexeme(); 137 | currentToken = next(); 138 | auto* ret = new StringExpr(line, column); 139 | ret->literal = val; 140 | return ret; 141 | } 142 | case LIT_CHAR: { 143 | auto val = getCurrentLexeme(); 144 | currentToken = next(); 145 | auto* ret = new CharExpr(line, column); 146 | ret->literal = val[0]; 147 | return ret; 148 | } 149 | case KW_TRUE: 150 | case KW_FALSE: { 151 | auto val = (KW_TRUE == getCurrentToken()); 152 | currentToken = next(); 153 | auto* ret = new BoolExpr(line, column); 154 | ret->literal = val; 155 | return ret; 156 | } 157 | case KW_NULL: { 158 | currentToken = next(); 159 | return new NullExpr(line, column); 160 | } 161 | case TK_LPAREN: { 162 | currentToken = next(); 163 | auto val = parseExpression(); 164 | assert(getCurrentToken() == TK_RPAREN); 165 | currentToken = next(); 166 | return val; 167 | } 168 | } 169 | return nullptr; 170 | } 171 | 172 | Expression* Parser::parseUnaryExpr() { 173 | // !expr 174 | if (anyone(getCurrentToken(), TK_MINUS, TK_LOGNOT, TK_BITNOT)) { 175 | auto val = new BinaryExpr(line, column); 176 | val->opt = getCurrentToken(); 177 | currentToken = next(); 178 | val->lhs = parseUnaryExpr(); 179 | return val; 180 | } 181 | // 3.14,32,"foo",'c',name,[],true,null,func(){},name.foo() 182 | else if (anyone(getCurrentToken(), LIT_DOUBLE, LIT_INT, LIT_STR, LIT_CHAR, 183 | TK_IDENT, TK_LPAREN, TK_LBRACKET, KW_TRUE, KW_FALSE, 184 | KW_NULL, KW_FUNC)) { 185 | Expression* prim = parsePrimaryExpr(); 186 | if (getCurrentToken() == TK_DOT) { 187 | currentToken = next(); 188 | Expression* call = parsePrimaryExpr(); 189 | if (typeid(*call) != typeid(FunCallExpr)) { 190 | panic("expected a object member function call"); 191 | } 192 | ((FunCallExpr*)call)->receiver = prim; 193 | return call; 194 | } else { 195 | return prim; 196 | } 197 | } 198 | return nullptr; 199 | } 200 | 201 | Expression* Parser::parseExpression(short oldPrecedence) { 202 | auto* p = parseUnaryExpr(); 203 | 204 | // expr += expr 205 | if (anyone(getCurrentToken(), TK_ASSIGN, TK_PLUS_AGN, TK_MINUS_AGN, 206 | TK_TIMES_AGN, TK_DIV_AGN, TK_MOD_AGN)) { 207 | if (typeid(*p) != typeid(NameExpr) && typeid(*p) != typeid(IndexExpr)) { 208 | panic("can not assign to %s", typeid(*p).name()); 209 | } 210 | auto* assignExpr = new AssignExpr(line, column); 211 | assignExpr->opt = getCurrentToken(); 212 | assignExpr->lhs = p; 213 | currentToken = next(); 214 | assignExpr->rhs = parseExpression(); 215 | return assignExpr; 216 | } 217 | 218 | // expr % expr 219 | while (anyone(getCurrentToken(), TK_BITOR, TK_BITAND, TK_BITNOT, TK_LOGOR, 220 | TK_LOGAND, TK_LOGNOT, TK_EQ, TK_NE, TK_GT, TK_GE, TK_LT, 221 | TK_LE, TK_PLUS, TK_MINUS, TK_MOD, TK_TIMES, TK_DIV)) { 222 | short currentPrecedence = Parser::precedence(getCurrentToken()); 223 | if (oldPrecedence > currentPrecedence) { 224 | return p; 225 | } 226 | auto tmp = new BinaryExpr(line, column); 227 | tmp->lhs = p; 228 | tmp->opt = getCurrentToken(); 229 | currentToken = next(); 230 | tmp->rhs = parseExpression(currentPrecedence + 1); 231 | p = tmp; 232 | } 233 | return p; 234 | } 235 | 236 | //===----------------------------------------------------------------------===// 237 | // Parse statements and save results to runtime 238 | //===----------------------------------------------------------------------===// 239 | SimpleStmt* Parser::parseExpressionStmt() { 240 | SimpleStmt* node = nullptr; 241 | if (auto p = parseExpression(); p != nullptr) { 242 | node = new SimpleStmt(line, column); 243 | node->expr = p; 244 | } 245 | return node; 246 | } 247 | 248 | IfStmt* Parser::parseIfStmt() { 249 | auto* node = new IfStmt(line, column); 250 | currentToken = next(); 251 | node->cond = parseExpression(); 252 | assert(getCurrentToken() == TK_RPAREN); 253 | currentToken = next(); 254 | node->block = parseBlock(); 255 | if (getCurrentToken() == KW_ELSE) { 256 | currentToken = next(); 257 | node->elseBlock = parseBlock(); 258 | } 259 | return node; 260 | } 261 | 262 | WhileStmt* Parser::parseWhileStmt() { 263 | auto* node = new WhileStmt(line, column); 264 | currentToken = next(); 265 | node->cond = parseExpression(); 266 | assert(getCurrentToken() == TK_RPAREN); 267 | currentToken = next(); 268 | node->block = parseBlock(); 269 | return node; 270 | } 271 | 272 | Statement* Parser::parseForStmt() { 273 | currentToken = next(); 274 | auto init = parseExpression(); 275 | if (typeid(*init) == typeid(NameExpr) && getCurrentToken() == TK_COLON) { 276 | auto* node = new ForEachStmt(line, column); 277 | node->identName = dynamic_cast(init)->identName; 278 | currentToken = next(); 279 | node->list = parseExpression(); 280 | assert(getCurrentToken() == TK_RPAREN); 281 | currentToken = next(); 282 | node->block = parseBlock(); 283 | return node; 284 | } else { 285 | auto* node = new ForStmt(line, column); 286 | node->init = init; 287 | assert(getCurrentToken() == TK_SEMICOLON); 288 | currentToken = next(); 289 | node->cond = parseExpression(); 290 | assert(getCurrentToken() == TK_SEMICOLON); 291 | currentToken = next(); 292 | node->post = parseExpression(); 293 | assert(getCurrentToken() == TK_RPAREN); 294 | currentToken = next(); 295 | node->block = parseBlock(); 296 | return node; 297 | } 298 | } 299 | 300 | MatchStmt* Parser::parseMatchStmt() { 301 | auto* node = new MatchStmt(line, column); 302 | 303 | // If we met "{" after "match" keyword, we will skip consuming condition 304 | // expression and the match statement degenerated to normaml multi 305 | // conditonal checkings. 306 | if (getCurrentToken() == TK_LPAREN) { 307 | currentToken = next(); 308 | node->cond = parseExpression(); 309 | assert(getCurrentToken() == TK_RPAREN); 310 | currentToken = next(); 311 | } 312 | 313 | assert(getCurrentToken() == TK_LBRACE); 314 | currentToken = next(); 315 | 316 | if (getCurrentToken() != TK_RBRACE) { 317 | Expression* theCase = nullptr; 318 | Block* block = nullptr; 319 | do { 320 | theCase = parseExpression(); 321 | assert(getCurrentToken() == TK_MATCH); 322 | currentToken = next(); 323 | if (getCurrentToken() == TK_LBRACE) { 324 | block = parseBlock(); 325 | } else { 326 | block = new Block; 327 | block->stmts.push_back(parseExpressionStmt()); 328 | } 329 | 330 | if (typeid(*theCase) == typeid(NameExpr) && 331 | dynamic_cast(theCase)->identName == "_") { 332 | node->matches.emplace_back(theCase, block, true); 333 | } else { 334 | node->matches.emplace_back(theCase, block, false); 335 | } 336 | } while (getCurrentToken() != TK_RBRACE); 337 | } 338 | currentToken = next(); 339 | return node; 340 | } 341 | 342 | ReturnStmt* Parser::parseReturnStmt() { 343 | auto* node = new ReturnStmt(line, column); 344 | node->ret = parseExpression(); 345 | return node; 346 | } 347 | 348 | Statement* Parser::parseStatement() { 349 | Statement* node; 350 | switch (getCurrentToken()) { 351 | case KW_IF: 352 | currentToken = next(); 353 | node = parseIfStmt(); 354 | break; 355 | case KW_WHILE: 356 | currentToken = next(); 357 | node = parseWhileStmt(); 358 | break; 359 | case KW_RETURN: 360 | currentToken = next(); 361 | node = parseReturnStmt(); 362 | break; 363 | case KW_BREAK: 364 | currentToken = next(); 365 | node = new BreakStmt(line, column); 366 | break; 367 | case KW_CONTINUE: 368 | currentToken = next(); 369 | node = new ContinueStmt(line, column); 370 | break; 371 | case KW_FOR: 372 | currentToken = next(); 373 | node = parseForStmt(); 374 | break; 375 | case KW_MATCH: 376 | currentToken = next(); 377 | node = parseMatchStmt(); 378 | break; 379 | default: 380 | node = parseExpressionStmt(); 381 | break; 382 | } 383 | return node; 384 | } 385 | 386 | std::vector Parser::parseStatementList() { 387 | std::vector node; 388 | Statement* p; 389 | while ((p = parseStatement()) != nullptr) { 390 | node.push_back(p); 391 | } 392 | return node; 393 | } 394 | 395 | Block* Parser::parseBlock() { 396 | Block* node{new Block}; 397 | currentToken = next(); 398 | node->stmts = parseStatementList(); 399 | assert(getCurrentToken() == TK_RBRACE); 400 | currentToken = next(); 401 | return node; 402 | } 403 | 404 | std::vector Parser::parseParameterList() { 405 | std::vector node; 406 | currentToken = next(); 407 | if (getCurrentToken() == TK_RPAREN) { 408 | currentToken = next(); 409 | return std::move(node); 410 | } 411 | 412 | while (getCurrentToken() != TK_RPAREN) { 413 | if (getCurrentToken() == TK_IDENT) { 414 | node.push_back(getCurrentLexeme()); 415 | } else { 416 | assert(getCurrentToken() == TK_COMMA); 417 | } 418 | currentToken = next(); 419 | } 420 | assert(getCurrentToken() == TK_RPAREN); 421 | currentToken = next(); 422 | return move(node); 423 | } 424 | 425 | Func* Parser::parseFuncDef(Context* context) { 426 | assert(getCurrentToken() == KW_FUNC); 427 | currentToken = next(); 428 | 429 | // Check if function was already be defined 430 | if (context->hasFunction(getCurrentLexeme())) { 431 | panic("multiply function definitions of %s found", 432 | getCurrentLexeme().c_str()); 433 | } 434 | 435 | auto* node = new Func; 436 | node->name = getCurrentLexeme(); 437 | currentToken = next(); 438 | assert(getCurrentToken() == TK_LPAREN); 439 | node->params = parseParameterList(); 440 | node->block = parseBlock(); 441 | 442 | return node; 443 | } 444 | 445 | void Parser::parse(Runtime* rt) { 446 | currentToken = next(); 447 | if (getCurrentToken() == TK_EOF) { 448 | return; 449 | } 450 | do { 451 | if (getCurrentToken() == KW_FUNC) { 452 | auto* f = parseFuncDef(rt); 453 | rt->addFunction(f->name, f); 454 | } else { 455 | rt->addStatement(parseStatement()); 456 | } 457 | } while (getCurrentToken() != TK_EOF); 458 | } 459 | 460 | //===----------------------------------------------------------------------===// 461 | // Implementation of lexer within simple next() function 462 | //===----------------------------------------------------------------------===// 463 | std::tuple Parser::next() { 464 | char c = getNextChar(); 465 | 466 | if (c == EOF) { 467 | return std::make_tuple(TK_EOF, ""); 468 | } 469 | if (anyone(c, ' ', '\n', '\r', '\t')) { 470 | while (anyone(c, ' ', '\n', '\r', '\t')) { 471 | if (c == '\n') { 472 | line++; 473 | column = 0; 474 | } 475 | c = getNextChar(); 476 | } 477 | if (c == EOF) { 478 | return std::make_tuple(TK_EOF, ""); 479 | } 480 | } 481 | 482 | if (c == '#') { 483 | another_comment: 484 | while (c != '\n' && c != EOF) { 485 | c = getNextChar(); 486 | } 487 | // consume newlines 488 | while (c == '\n') { 489 | line++; 490 | column = 0; 491 | c = getNextChar(); 492 | } 493 | if (c == '#') { 494 | goto another_comment; 495 | } 496 | if (c == EOF) { 497 | return std::make_tuple(TK_EOF, ""); 498 | } 499 | } 500 | 501 | if (c >= '0' && c <= '9') { 502 | std::string lexeme{c}; 503 | bool isDouble = false; 504 | char cn = peekNextChar(); 505 | while ((cn >= '0' && cn <= '9') || (!isDouble && cn == '.')) { 506 | if (c == '.') { 507 | isDouble = true; 508 | } 509 | c = getNextChar(); 510 | cn = peekNextChar(); 511 | lexeme += c; 512 | } 513 | return !isDouble ? make_tuple(LIT_INT, lexeme) 514 | : make_tuple(LIT_DOUBLE, lexeme); 515 | } 516 | if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_') { 517 | std::string lexeme{c}; 518 | char cn = peekNextChar(); 519 | while ((cn >= 'a' && cn <= 'z') || (cn >= 'A' && cn <= 'Z') || 520 | (cn >= '0' && cn <= '9') || cn == '_') { 521 | c = getNextChar(); 522 | lexeme += c; 523 | cn = peekNextChar(); 524 | } 525 | auto result = KEYWORDS.find(lexeme); 526 | return result != KEYWORDS.end() 527 | ? std::make_tuple(result->second, lexeme) 528 | : std::make_tuple(TK_IDENT, lexeme); 529 | } 530 | 531 | switch (c) { 532 | case '\'': { 533 | std::string lexeme; 534 | char nextChar = getNextChar(); 535 | if (nextChar != '\'') { 536 | lexeme += nextChar; 537 | if (peekNextChar() != '\'') { 538 | panic( 539 | "a character literal should surround with " 540 | "single-quote"); 541 | } 542 | c = getNextChar(); 543 | } 544 | return std::make_tuple(LIT_CHAR, lexeme); 545 | } 546 | case '"': { 547 | std::string lexeme; 548 | char cn = peekNextChar(); 549 | while (cn != '"') { 550 | c = getNextChar(); 551 | lexeme += c; 552 | cn = peekNextChar(); 553 | } 554 | c = getNextChar(); 555 | return std::make_tuple(LIT_STR, lexeme); 556 | } 557 | case '[': { 558 | return std::make_tuple(TK_LBRACKET, "["); 559 | } 560 | case ']': { 561 | return std::make_tuple(TK_RBRACKET, "]"); 562 | } 563 | case '{': { 564 | return std::make_tuple(TK_LBRACE, "{"); 565 | } 566 | case '}': { 567 | return std::make_tuple(TK_RBRACE, "}"); 568 | } 569 | case '(': { 570 | return std::make_tuple(TK_LPAREN, "("); 571 | } 572 | case ')': { 573 | return std::make_tuple(TK_RPAREN, ")"); 574 | } 575 | case ',': { 576 | return std::make_tuple(TK_COMMA, ","); 577 | } 578 | case ';': { 579 | return std::make_tuple(TK_SEMICOLON, ";"); 580 | } 581 | case ':': { 582 | return std::make_tuple(TK_COLON, ":"); 583 | } 584 | case '+': { 585 | if (peekNextChar() == '=') { 586 | c = getNextChar(); 587 | return std::make_tuple(TK_PLUS_AGN, "+="); 588 | } 589 | return std::make_tuple(TK_PLUS, "+"); 590 | } 591 | case '-': { 592 | if (peekNextChar() == '=') { 593 | c = getNextChar(); 594 | return std::make_tuple(TK_MINUS_AGN, "-="); 595 | } 596 | return std::make_tuple(TK_MINUS, "-"); 597 | } 598 | case '*': { 599 | if (peekNextChar() == '=') { 600 | c = getNextChar(); 601 | return std::make_tuple(TK_TIMES_AGN, "*="); 602 | } 603 | return std::make_tuple(TK_TIMES, "*"); 604 | } 605 | case '/': { 606 | if (peekNextChar() == '=') { 607 | c = getNextChar(); 608 | return std::make_tuple(TK_DIV_AGN, "/="); 609 | } 610 | return std::make_tuple(TK_DIV, "/"); 611 | } 612 | case '%': { 613 | if (peekNextChar() == '=') { 614 | c = getNextChar(); 615 | return std::make_tuple(TK_MOD_AGN, "%="); 616 | } 617 | return std::make_tuple(TK_MOD, "%"); 618 | } 619 | case '~': { 620 | return std::make_tuple(TK_BITNOT, "~"); 621 | } 622 | case '.': { 623 | return std::make_tuple(TK_DOT, "."); 624 | } 625 | case '=': { 626 | if (peekNextChar() == '=') { 627 | c = getNextChar(); 628 | return std::make_tuple(TK_EQ, "=="); 629 | } else if (peekNextChar() == '>') { 630 | c = getNextChar(); 631 | return std::make_tuple(TK_MATCH, "=>"); 632 | } 633 | return std::make_tuple(TK_ASSIGN, "="); 634 | } 635 | case '!': { 636 | if (peekNextChar() == '=') { 637 | c = getNextChar(); 638 | return std::make_tuple(TK_NE, "!="); 639 | } 640 | return std::make_tuple(TK_LOGNOT, "!"); 641 | } 642 | case '|': { 643 | if (peekNextChar() == '|') { 644 | c = getNextChar(); 645 | return std::make_tuple(TK_LOGOR, "||"); 646 | } 647 | return std::make_tuple(TK_BITOR, "|"); 648 | } 649 | case '&': { 650 | if (peekNextChar() == '&') { 651 | c = getNextChar(); 652 | return std::make_tuple(TK_LOGAND, "&&"); 653 | } 654 | return std::make_tuple(TK_BITAND, "&"); 655 | } 656 | case '>': { 657 | if (peekNextChar() == '=') { 658 | c = getNextChar(); 659 | return std::make_tuple(TK_GE, ">="); 660 | } 661 | return std::make_tuple(TK_GT, ">"); 662 | } 663 | case '<': { 664 | if (peekNextChar() == '=') { 665 | c = getNextChar(); 666 | return std::make_tuple(TK_LE, "<="); 667 | } 668 | return std::make_tuple(TK_LT, "<"); 669 | } 670 | default: { 671 | panic("unknown token %c", c); 672 | } 673 | } 674 | } 675 | 676 | short Parser::precedence(Token op) { 677 | switch (op) { 678 | case TK_LOGOR: 679 | return 1; 680 | case TK_LOGAND: 681 | return 2; 682 | case TK_EQ: 683 | case TK_NE: 684 | case TK_GT: 685 | case TK_GE: 686 | case TK_LT: 687 | case TK_LE: 688 | return 3; 689 | case TK_PLUS: 690 | case TK_MINUS: 691 | case TK_BITOR: 692 | return 4; 693 | case TK_TIMES: 694 | case TK_MOD: 695 | case TK_DIV: 696 | case TK_BITAND: 697 | return 5; 698 | default: 699 | // Lowest precedence 700 | return 0; 701 | } 702 | } 703 | --------------------------------------------------------------------------------