├── NoodleScript.exe ├── README.md ├── exampleScript.ns ├── hdr ├── Terminal.hpp ├── backend │ ├── AST.hpp │ ├── Lexer.hpp │ └── Parser.hpp ├── runtime │ ├── Interpreter.hpp │ ├── Scope.hpp │ ├── Values.hpp │ └── eval │ │ ├── Expressions.hpp │ │ └── Statements.hpp └── util │ ├── Error.hpp │ └── Memory.hpp └── src ├── Terminal.cpp ├── backend ├── AST.cpp ├── Lexer.cpp └── Parser.cpp ├── main.cpp ├── runtime ├── Interpreter.cpp ├── Scope.cpp ├── Values.cpp └── eval │ ├── Expressions.cpp │ └── Statements.cpp └── util ├── Error.cpp └── Memory.cpp /NoodleScript.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficialCodeNoodles/NoodleScript/81087550c25ae4ceb7a39490990fad5e69d9fc59/NoodleScript.exe -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NoodleScript 2 | This is a simple interpreted programming language that has syntax and features heavily inspired by Python, but with an Italian twist! 3 | 4 | # Syntax and Features 5 | ## Comments 6 | ``` 7 | # This is a comment 8 | ``` 9 | ## Variables 10 | ``` 11 | noodle number = 10 12 | noodle bool = true 13 | noodle string = "Hello" 14 | noodle list = [10 true "Hello"] 15 | ``` 16 | ## Constant Variables 17 | ``` 18 | frozen noodle x = 10 19 | x = 10 # Doesn't work 20 | ``` 21 | ## IO 22 | ### serve() 23 | ``` 24 | serve("Hello World!\n") # Outputs `Hello World!` to the console 25 | ``` 26 | ### receive() 27 | ``` 28 | input = receive() # Parses input from user as a string 29 | ``` 30 | ### If Statements 31 | ``` 32 | if condition 33 | # Body 34 | eat 35 | ``` 36 | ### Else Statements 37 | ``` 38 | if condition 39 | # Body 40 | else 41 | # Body 42 | eat 43 | ``` 44 | ### While Loops 45 | ``` 46 | while condition 47 | # Body 48 | eat 49 | ``` 50 | ## Functions 51 | ``` 52 | recipe add(noodle num1 noodle num2) 53 | num1 + num2 # notice how no return statement is needed 54 | eat 55 | ``` 56 | ## Nested Functions 57 | ``` 58 | recipe div(noodle num noodle den) 59 | recipe safeToDiv(noodle den) 60 | den != 0 61 | eat 62 | 63 | if safeToDiv(den) 64 | num / den 65 | else 66 | serve("Cannot divide by zero\n") 67 | eat 68 | eat 69 | ``` 70 | ## Built-In Functions 71 | ``` 72 | serve() # Outputs to console 73 | serveRaw() # Outputs the runtime value of an expression 74 | receive() # Parses input as string 75 | abs(num) # Absolute value 76 | floor(num) # Rounds down value 77 | round(num) # Rounds value 78 | ceil(num) # Rounds value up 79 | sqrt(num) # Takes square root of value 80 | pow(num num) # Raises value to power 81 | num(any) # Converts value to a number 82 | bool(any) # Converts a value to a bool 83 | str(any) # Converts a value to a string 84 | at(list num) # Accesses an element inside of a list 85 | set(list num any) # Sets an element in a list with a new value 86 | append(list any) # Adds an element to a list 87 | pop(list num) # Removes and returns an element from a list 88 | ``` 89 | # Anything Else? 90 | Nope, just enjoy the debugging nightmare if you attempt using this language :) 91 | -------------------------------------------------------------------------------- /exampleScript.ns: -------------------------------------------------------------------------------- 1 | recipe isNumber(noodle string) 2 | frozen noodle stringSize = len(string) 3 | frozen noodle numberChrs = ["0" "1" "2" "3" "4" "5" "6" "7" "8" "9"] 4 | noodle chrFound = false 5 | noodle chrIndex = 0 6 | noodle currentChr 7 | noodle numberChrIndex 8 | noodle foundNumberChr 9 | 10 | while chrIndex < stringSize && !chrFound 11 | currentChr = at(string chrIndex) 12 | numberChrIndex = 0 13 | foundNumberChr = false 14 | 15 | while numberChrIndex < 10 && !foundNumberChr 16 | if currentChr == at(numberChrs numberChrIndex) 17 | foundNumberChr = true 18 | eat 19 | 20 | numberChrIndex++ 21 | eat 22 | 23 | if !foundNumberChr 24 | chrFound = true 25 | eat 26 | 27 | chrIndex++ 28 | eat 29 | 30 | !chrFound 31 | eat 32 | 33 | recipe sort(noodle array) 34 | frozen noodle arraySize = len(array) 35 | noodle arrayIndex = 0 36 | noodle currentElem 37 | noodle nextElem 38 | noodle sorted = false 39 | 40 | while !sorted 41 | arrayIndex = 0 42 | sorted = true 43 | 44 | while arrayIndex < arraySize - 1 45 | currentElem = at(array arrayIndex) 46 | nextElem = at(array arrayIndex+1) 47 | 48 | if currentElem > nextElem 49 | set(array arrayIndex nextElem) 50 | set(array arrayIndex+1 currentElem) 51 | sorted = false 52 | else 53 | arrayIndex++ 54 | eat 55 | eat 56 | eat 57 | 58 | array 59 | eat 60 | 61 | recipe main() 62 | noodle input = "" 63 | noodle numbers = [] 64 | 65 | serve("Welecome to the number sorter! Type `sort` to sort numbers.\n\n") 66 | 67 | while input != "sort" 68 | serve("Type a number to add to the list: ") 69 | input = receive() 70 | 71 | if isNumber(input) 72 | append(numbers num(input)) 73 | else 74 | if input != "sort" 75 | serve(" " input " is not a valid number\n") 76 | eat 77 | eat 78 | eat 79 | 80 | serve("\n Original number list: " numbers "\n") 81 | serve(" Sorted number list: " sort(numbers) "\n\n") 82 | eat 83 | 84 | main() -------------------------------------------------------------------------------- /hdr/Terminal.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Dependencies 4 | #include "runtime/Interpreter.hpp" 5 | 6 | namespace ns { 7 | extern Interpreter interpreter; 8 | extern std::string sourceCode; 9 | extern std::string terminalInput; 10 | extern bool terminalOpen; 11 | extern bool insideConsole; 12 | extern bool jumpToConsole; 13 | 14 | void initNoodleScriptMsg(); 15 | void initTerminalMsg(); 16 | void initConsoleMsg(); 17 | 18 | void prepareTerminalMsg(); 19 | void prepareConsoleMsg(); 20 | 21 | void executeTerminal(const std::string& input); 22 | void executeConsole(const std::string& input); 23 | 24 | void initTerminal(int argc, const char** argv); 25 | bool isTerminalOpen(); 26 | void runSourceFile(const std::string& filepath); 27 | void updateTerminal(); 28 | void closeTerminal(); 29 | } -------------------------------------------------------------------------------- /hdr/backend/AST.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Dependencies 4 | #include "Lexer.hpp" 5 | 6 | namespace ns { 7 | enum class NodeType { 8 | // Statements 9 | Program, 10 | VarDeclaration, 11 | FuncDeclaration, 12 | IfStatement, 13 | WhileStatement, 14 | 15 | // Expressions 16 | NullLiteral, 17 | NumLiteral, 18 | StringLiteral, 19 | ListLiteral, 20 | Identifier, 21 | ListAccesser, 22 | UnaryExpr, 23 | BinaryExpr, 24 | AssignmentExpr, 25 | FuncCall 26 | }; 27 | enum class ValueType { 28 | Null, 29 | Number, 30 | Bool, 31 | String, 32 | List, 33 | FuncValue 34 | }; 35 | 36 | struct Statement { 37 | NodeType nodeType; 38 | 39 | virtual ~Statement() {} 40 | }; 41 | struct Expr : public Statement {}; 42 | 43 | struct Program : public Statement { 44 | std::vector statements; 45 | 46 | Program(); 47 | }; 48 | struct VarDeclaration : public Statement { 49 | bool constant; 50 | ValueType valueType; 51 | std::string identifier; 52 | Expr* expr; 53 | 54 | VarDeclaration(); 55 | }; 56 | struct FuncDeclaration : public Statement { 57 | std::string name; 58 | bool nativeFunc; 59 | std::vector parameters; 60 | std::vector statements; 61 | 62 | FuncDeclaration(); 63 | }; 64 | struct IfStatement : public Statement { 65 | Expr* condition; 66 | std::vector ifStatements; 67 | std::vector elseStatements; 68 | 69 | IfStatement(); 70 | }; 71 | struct WhileStatement : public Statement { 72 | Expr* condition; 73 | std::vector statements; 74 | 75 | WhileStatement(); 76 | }; 77 | 78 | struct NullLiteral : public Expr { 79 | NullLiteral(); 80 | }; 81 | struct NumLiteral : public Expr { 82 | double value; 83 | 84 | NumLiteral(); 85 | }; 86 | struct StringLiteral : public Expr { 87 | std::string value; 88 | 89 | StringLiteral(); 90 | }; 91 | struct ListLiteral : public Expr { 92 | std::vector elements; 93 | 94 | ListLiteral(); 95 | }; 96 | struct Identifier : public Expr { 97 | std::string name; 98 | 99 | Identifier(); 100 | }; 101 | struct ListAccesser : public Expr { 102 | std::string name; 103 | int index; 104 | 105 | ListAccesser(); 106 | }; 107 | struct UnaryExpr : public Expr { 108 | Expr* expr; 109 | std::string operation; 110 | 111 | UnaryExpr(); 112 | }; 113 | struct BinaryExpr : public Expr { 114 | Expr* leftExpr; 115 | Expr* rightExpr; 116 | std::string operation; 117 | 118 | BinaryExpr(); 119 | }; 120 | struct AssignmentExpr : public Expr { 121 | Expr* assigne; 122 | Expr* value; 123 | 124 | AssignmentExpr(); 125 | }; 126 | struct FuncCall : public Expr { 127 | std::string caller; 128 | std::vector arguments; 129 | 130 | FuncCall(); 131 | }; 132 | 133 | std::ostream& operator<<(std::ostream& ostream, const Statement* statement); 134 | } -------------------------------------------------------------------------------- /hdr/backend/Lexer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Depdencencies 4 | #include 5 | #include 6 | #include 7 | 8 | namespace ns { 9 | enum class TokenType { 10 | Number, 11 | Bool, 12 | String, 13 | Const, 14 | Var, 15 | Func, 16 | EndStatement, 17 | If, 18 | Else, 19 | While, 20 | Equals, 21 | UnaryOperator, 22 | BinaryOperator, 23 | OpenParen, 24 | CloseParen, 25 | OpenBracket, 26 | CloseBracket, 27 | Identifier, 28 | Comment, 29 | EndOfLine, 30 | EndOfFile, 31 | Invalid 32 | }; 33 | struct TokenIdentifier { 34 | std::vector identifiers; 35 | 36 | static TokenIdentifier tokenIdentifiers[static_cast(TokenType::Invalid)]; 37 | }; 38 | struct Token { 39 | TokenType type; 40 | std::string string; 41 | }; 42 | 43 | void processEscapeCharacters(std::string& string); 44 | void unprocessEscapeCharacters(std::string& string); 45 | std::string extractNextIdentifier(std::string& line, char seperator = ' '); 46 | std::vector& extractWords(const std::string& string); 47 | Token determineTokenType(const std::string& nextIdentifier); 48 | std::vector& tokenize(const std::string& sourceCode); 49 | 50 | std::ostream& operator<<(std::ostream& ostream, Token token); 51 | std::ostream& operator<<(std::ostream& ostream, const std::vector& tokens); 52 | } -------------------------------------------------------------------------------- /hdr/backend/Parser.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Dependencies 4 | #include "../util/Memory.hpp" 5 | #include "AST.hpp" 6 | 7 | namespace ns { 8 | class Parser { 9 | public: 10 | std::vector* tokens; 11 | 12 | Parser(); 13 | 14 | std::vector* lexSourceCode(const std::string& sourceCode); 15 | Program* produceAST(); 16 | private: 17 | Token prvsToken; 18 | 19 | Statement* parseStatement(); 20 | Statement* parseVarDeclaration(); 21 | Statement* parseFuncDeclaration(); 22 | Statement* parseIfStatement(); 23 | Statement* parseWhileStatement(); 24 | 25 | Expr* parseExpr(); 26 | Expr* parseAssignmentExpr(); 27 | Expr* parseConditionalExpr(); 28 | Expr* parsePrimaryConditionalExpr(); 29 | Expr* parseAdditiveExpr(); 30 | Expr* parseMultiplicativeExpr(); 31 | Expr* parseAdditiveUnaryExpr(); 32 | Expr* parseFuncCallExpr(); 33 | Expr* parsePrimaryExpr(); 34 | 35 | Token getToken(int index = 0) const; 36 | Token popToken(int index = 0); 37 | Token expectToken(TokenType tokenType, int index = 0); 38 | bool atEOF() const; 39 | }; 40 | } -------------------------------------------------------------------------------- /hdr/runtime/Interpreter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Dependencies 4 | #include "eval/Expressions.hpp" 5 | 6 | namespace ns { 7 | class Interpreter { 8 | public: 9 | Interpreter(); 10 | ~Interpreter(); 11 | 12 | void generateProgram(const std::string& sourceCode); 13 | void runProgram(); 14 | void deleteProgramMemory(); 15 | void logTokens() const; 16 | void logAST() const; 17 | 18 | void enableTokenLogging(bool shouldLogTokens); 19 | void enableASTLogging(bool shouldLogAST); 20 | void enableRuntimeValueLogging(bool shouldLogRuntimeValue); 21 | 22 | bool isTokenLoggingEnabled() const; 23 | bool isASTLoggingEnabled() const; 24 | bool isRuntimeValueLoggingEnabled() const; 25 | private: 26 | Memory memory; 27 | Parser parser; 28 | Program* program; 29 | Scope* globalScope; 30 | RuntimeValue* runtimeValue; 31 | std::vector tokens; 32 | bool shouldLogTokens; 33 | bool shouldLogAST; 34 | bool shouldLogRuntimeValue; 35 | 36 | void initGlobalScope(bool reallocate = true); 37 | void copyTokens(const std::vector* tokens); 38 | void logRuntimeValue(); 39 | }; 40 | } -------------------------------------------------------------------------------- /hdr/runtime/Scope.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Dependencies 4 | #include 5 | 6 | #include "Values.hpp" 7 | 8 | namespace ns { 9 | class Scope { 10 | public: 11 | Scope(); 12 | 13 | Scope* assignParent(Scope* parent); 14 | RuntimeValue* declareVariable( 15 | const std::string& variableName, RuntimeValue* runtimeValue, bool constant = false 16 | ); 17 | RuntimeValue* assignVariable(const std::string& variableName, RuntimeValue* runtimeValue); 18 | Scope* resolveVariable(const std::string& variableName); 19 | RuntimeValue* getVariableValue(const std::string& variableName); 20 | bool isVariableConst(const std::string& variableName); 21 | private: 22 | struct Variable { 23 | bool constant; 24 | RuntimeValue* runtimeValue; 25 | }; 26 | 27 | Scope* parent; 28 | std::unordered_map variables; 29 | }; 30 | } -------------------------------------------------------------------------------- /hdr/runtime/Values.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Dependencies 4 | #include "../backend/AST.hpp" 5 | 6 | namespace ns { 7 | constexpr int numOfNativeFuncs = 17; 8 | extern const char* nativeFuncNames[numOfNativeFuncs]; 9 | class Scope; 10 | 11 | struct RuntimeValue { 12 | ValueType valueType; 13 | 14 | virtual ~RuntimeValue() {}; 15 | }; 16 | struct NullValue : public RuntimeValue { 17 | std::string value; 18 | 19 | NullValue(); 20 | }; 21 | struct NumValue : public RuntimeValue { 22 | double value; 23 | 24 | NumValue(); 25 | NumValue(double value); 26 | }; 27 | struct BoolValue : public RuntimeValue { 28 | bool state; 29 | 30 | BoolValue(); 31 | BoolValue(bool state); 32 | }; 33 | struct StringValue : public RuntimeValue { 34 | std::string value; 35 | 36 | StringValue(); 37 | StringValue(const std::string& value); 38 | }; 39 | struct ListValue : public RuntimeValue { 40 | std::vector elements; 41 | 42 | ListValue(); 43 | }; 44 | struct FuncValue : public RuntimeValue { 45 | std::string name; 46 | std::vector parameters; 47 | std::vector statements; 48 | Scope* scope; 49 | bool nativeFunc; 50 | 51 | FuncValue(); 52 | }; 53 | 54 | RuntimeValue* cloneRuntimeValue(RuntimeValue* runtimeValue); 55 | 56 | std::ostream& operator<<(std::ostream& ostream, const RuntimeValue* runtimeValue); 57 | } -------------------------------------------------------------------------------- /hdr/runtime/eval/Expressions.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Dependencies 4 | #include "Statements.hpp" 5 | 6 | namespace ns { 7 | RuntimeValue* evaluateFuncCallExpr(FuncCall* funcCall, Scope* scope); 8 | RuntimeValue* evaluateNativeFuncCallExpr( 9 | FuncValue* funcValue, FuncCall* funcCall, Scope* scope 10 | ); 11 | RuntimeValue* evaluateAssignmentExpr(AssignmentExpr* assignmentExpr, Scope* scope); 12 | RuntimeValue* evaluateUnaryExprNode(UnaryExpr* unaryExpr, Scope* scope); 13 | RuntimeValue* evaluateNumericUnaryExpr(NumValue* numberValue, const std::string& operation); 14 | RuntimeValue* evaluateConditionalUnaryExpr(BoolValue* boolValue, const std::string& operation); 15 | RuntimeValue* evaluateBinaryExprNode(BinaryExpr* binaryExpr, Scope* scope); 16 | RuntimeValue* evaluateNumericBinaryExpr( 17 | NumValue* leftNumValue, NumValue* rightNumValue, const std::string& operation 18 | ); 19 | RuntimeValue* evaluateConditionalBinaryExpr( 20 | BoolValue* leftBoolValue, BoolValue* rightBoolValue, const std::string& operation 21 | ); 22 | RuntimeValue* evaluateStringBinaryExpr( 23 | StringValue* leftStringValue, StringValue* rightStringValue, const std::string& operation 24 | ); 25 | RuntimeValue* evaluateIdentifier(Identifier* identifier, Scope* scope); 26 | } -------------------------------------------------------------------------------- /hdr/runtime/eval/Statements.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Dependencies 4 | #include "../Scope.hpp" 5 | #include "../../backend/Parser.hpp" 6 | 7 | namespace ns { 8 | RuntimeValue* evaluateProgramNode(Program* program, Scope* scope); 9 | RuntimeValue* evaluateVarDeclarationNode(VarDeclaration* varDeclaration, Scope* scope); 10 | RuntimeValue* evaluateFuncDeclaration(FuncDeclaration* funcDeclaration, Scope* scope); 11 | RuntimeValue* evaluateIfStatementNode(IfStatement* ifStatement, Scope* scope); 12 | RuntimeValue* evaluateWhileStatementNode(WhileStatement* whileStatement, Scope* scope); 13 | RuntimeValue* evaluateASTNode(Statement* astNode, Scope* scope); 14 | } -------------------------------------------------------------------------------- /hdr/util/Error.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Dependencies 4 | #include 5 | 6 | namespace ns { 7 | class Error { 8 | public: 9 | enum class Location { Lexer, Parser, Interpreter, End } location; 10 | int code; 11 | std::string message; 12 | 13 | Error(); 14 | Error(Location location, int code, std::string message); 15 | 16 | friend std::ostream& operator<<(std::ostream& ostream, const Error& error); 17 | private: 18 | static const std::string locations[static_cast(Location::End)]; 19 | }; 20 | 21 | std::ostream& operator<<(std::ostream& ostream, const Error& error); 22 | } -------------------------------------------------------------------------------- /hdr/util/Memory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Dependencies 4 | #include 5 | 6 | namespace ns { 7 | class Memory { 8 | public: 9 | Memory(); 10 | ~Memory(); 11 | 12 | template 13 | Type* create() { 14 | Type* pointer = new Type(); 15 | pointers.push_back(pointer); 16 | return pointer; 17 | } 18 | void clear(); 19 | 20 | int getPointerCount() const; 21 | private: 22 | std::vector pointers; 23 | }; 24 | 25 | extern Memory* globalMemory; 26 | } -------------------------------------------------------------------------------- /src/Terminal.cpp: -------------------------------------------------------------------------------- 1 | #include "../hdr/Terminal.hpp" 2 | 3 | #include 4 | 5 | namespace ns { 6 | Interpreter interpreter; 7 | std::string sourceCode; 8 | std::string terminalInput; 9 | bool terminalOpen = false; 10 | bool insideConsole = false; 11 | bool jumpToConsole = false; 12 | 13 | void initNoodleScriptMsg() { 14 | std::cout << "\nNoodleScript Terminal - Created by CodeNoodles (2024)\n\n"; 15 | 16 | std::cout << " @@@@@@@@@@@@@@@@@@@@@\n"; 17 | std::cout << " @@@@@@@@@@@@@@@@@@@@@\n"; 18 | std::cout << " @@@@@@@@@@@@@@@@@@@@@\n"; 19 | std::cout << " @@@@@@@@@@@@@@@@@@@@@\n"; 20 | std::cout << " @@@@@@@@@@@@@@@@@@@@@\n"; 21 | std::cout << " @@@@@@@@ @@@ @@@ @@\n"; 22 | std::cout << " @@@@@@@@ @@ @@ @@@@\n"; 23 | std::cout << " @@@@@@@@ @ @ @@@ @@@\n"; 24 | std::cout << " @@@@@@@@ @@ @@@@ @@\n"; 25 | std::cout << " @@@@@@@@ @@@ @@ @@@\n"; 26 | std::cout << " @@@@@@@@@@@@@@@@@@@@@\n\n"; 27 | 28 | std::cout << "type `help` to view a list of commands.\n\n"; 29 | } 30 | void initTerminalMsg() { 31 | std::cout << "\nNoodleScript Terminal\n\n"; 32 | insideConsole = false; 33 | } 34 | void initConsoleMsg() { 35 | std::cout << "\nNoodleScript Console\n\n"; 36 | insideConsole = true; 37 | } 38 | 39 | void prepareTerminalMsg() { 40 | std::cout << "> "; 41 | } 42 | void prepareConsoleMsg() { 43 | if (!jumpToConsole) 44 | std::cout << ">>> "; 45 | } 46 | 47 | void executeTerminal(const std::string& input) { 48 | const std::vector words = extractWords(input); 49 | const int wordCount = words.size(); 50 | 51 | if (wordCount == 0) 52 | return; 53 | 54 | if (words[0] == "help") { 55 | std::cout << "\nHere are a list of commands:\n\n" 56 | " console -> Enters a console where code can be written\n" 57 | " terminal -> Reenters the terminal\n" 58 | " run [string] -> Runs a .ns script, if the file path is given\n" 59 | " set -> Allows for the modification of enviroment variables\n" 60 | " logTokens [true | false] -> Specifies whether lexed tokens should be outputed\n" 61 | " logAST [true | false] -> Specifies whether to output the AST\n" 62 | " logRuntimeValue [true | false] -> Specifies whether to ouput the program value\n" 63 | " show -> Shows the state of certain enviroment variables\n" 64 | " logTokens -> Shows whether lexed tokens should be outputed\n" 65 | " logAST -> Shows whether to output the AST\n" 66 | " logRuntimeValue -> Shows whether to ouput the program value\n" 67 | " globalMemorySize -> Shows how many runtime values are currently allocated\n" 68 | " clear -> Clears the terminal\n" 69 | " exit -> Closes the terminal\n\n"; 70 | } 71 | else if (words[0] == "console") 72 | initConsoleMsg(); 73 | else if (words[0] == "run") { 74 | if (wordCount < 2) 75 | return; 76 | 77 | runSourceFile(words[1]); 78 | } 79 | else if (words[0] == "set") { 80 | if (wordCount < 3) 81 | return; 82 | 83 | const bool condition = words[2] == "true"; 84 | 85 | if (words[1] == "logTokens") 86 | interpreter.enableTokenLogging(condition); 87 | else if (words[1] == "logAST") 88 | interpreter.enableASTLogging(condition); 89 | else if (words[1] == "logRuntimeValue") 90 | interpreter.enableRuntimeValueLogging(condition); 91 | } 92 | else if (words[0] == "show") { 93 | auto showEnviromentVariable = [](const std::string& variableName, 94 | const std::string& value) -> void 95 | { 96 | std::cout << " Enviroment variable `" << variableName << "` is currently set to " 97 | << value << "\n"; 98 | }; 99 | 100 | if (wordCount < 2) 101 | return; 102 | 103 | if (words[1] == "logTokens") 104 | showEnviromentVariable( 105 | "shouldLogTokens", interpreter.isTokenLoggingEnabled() ? "true" : "false" 106 | ); 107 | else if (words[1] == "logAST") 108 | showEnviromentVariable( 109 | "shouldLogAST", interpreter.isASTLoggingEnabled() ? "true" : "false" 110 | ); 111 | else if (words[1] == "logRuntimeValue") 112 | showEnviromentVariable( 113 | "shouldLogRuntimeValue", interpreter.isRuntimeValueLoggingEnabled() 114 | ? "true" : "false" 115 | ); 116 | else if (words[1] == "globalMemorySize") { 117 | const int allocatedRuntimeValues = globalMemory->getPointerCount(); 118 | std::cout << " Currently, there are " << allocatedRuntimeValues 119 | << " runtimeValues allocated\n"; 120 | } 121 | } 122 | else if (words[0] == "clear") { 123 | // Clears terminal in Windows 124 | system("cls"); 125 | initNoodleScriptMsg(); 126 | } 127 | } 128 | void executeConsole(const std::string& input) { 129 | if (input == "terminal") { 130 | initTerminalMsg(); 131 | return; 132 | } 133 | 134 | if (sourceCode == "") 135 | sourceCode = input; 136 | 137 | interpreter.generateProgram(sourceCode); 138 | interpreter.runProgram(); 139 | 140 | sourceCode = ""; 141 | jumpToConsole = false; 142 | } 143 | 144 | void initTerminal(int argc, const char** argv) { 145 | terminalOpen = true; 146 | 147 | // Attempts running a source file if an extra parameter is passed into the command prompt 148 | if (argc == 2) { 149 | const std::string filepath = argv[1]; 150 | runSourceFile(filepath); 151 | } 152 | else 153 | initNoodleScriptMsg(); 154 | } 155 | bool isTerminalOpen() { 156 | return terminalOpen; 157 | } 158 | void runSourceFile(const std::string& filepath) { 159 | insideConsole = true; 160 | 161 | std::ifstream sourceFile(filepath); 162 | std::string line; 163 | 164 | while (std::getline(sourceFile, line)) { 165 | if (line.empty()) 166 | continue; 167 | 168 | // Adds \n to ensure an EOL token is lexed 169 | sourceCode += line + " \n"; 170 | } 171 | 172 | sourceFile.close(); 173 | initConsoleMsg(); 174 | jumpToConsole = true; 175 | } 176 | void updateTerminal() { 177 | if (insideConsole) prepareConsoleMsg(); 178 | else prepareTerminalMsg(); 179 | 180 | // Skips input handling when a script is ready to be run 181 | if (!jumpToConsole) { 182 | std::getline(std::cin, terminalInput); 183 | 184 | // Exits terminal when `exit` is typed 185 | if (terminalInput == "exit") { 186 | terminalOpen = false; 187 | return; 188 | } 189 | } 190 | 191 | if (insideConsole) executeConsole(terminalInput); 192 | else executeTerminal(terminalInput); 193 | } 194 | void closeTerminal() { 195 | } 196 | } -------------------------------------------------------------------------------- /src/backend/AST.cpp: -------------------------------------------------------------------------------- 1 | #include "../../hdr/backend/AST.hpp" 2 | #include "../../hdr/runtime/Values.hpp" 3 | 4 | namespace ns { 5 | Program::Program() { 6 | nodeType = NodeType::Program; 7 | } 8 | VarDeclaration::VarDeclaration() : expr(nullptr) { 9 | nodeType = NodeType::VarDeclaration; 10 | } 11 | FuncDeclaration::FuncDeclaration() : nativeFunc(false) { 12 | nodeType = NodeType::FuncDeclaration; 13 | } 14 | IfStatement::IfStatement() { 15 | nodeType = NodeType::IfStatement; 16 | } 17 | WhileStatement::WhileStatement() { 18 | nodeType = NodeType::WhileStatement; 19 | } 20 | 21 | NullLiteral::NullLiteral() { 22 | nodeType = NodeType::NullLiteral; 23 | } 24 | NumLiteral::NumLiteral() { 25 | nodeType = NodeType::NumLiteral; 26 | } 27 | StringLiteral::StringLiteral() { 28 | nodeType = NodeType::StringLiteral; 29 | } 30 | ListLiteral::ListLiteral() { 31 | nodeType = NodeType::ListLiteral; 32 | } 33 | Identifier::Identifier() { 34 | nodeType = NodeType::Identifier; 35 | } 36 | ListAccesser::ListAccesser() : index(0) { 37 | nodeType = NodeType::ListAccesser; 38 | } 39 | UnaryExpr::UnaryExpr() { 40 | nodeType = NodeType::UnaryExpr; 41 | } 42 | BinaryExpr::BinaryExpr() { 43 | nodeType = NodeType::BinaryExpr; 44 | } 45 | AssignmentExpr::AssignmentExpr() { 46 | nodeType = NodeType::AssignmentExpr; 47 | } 48 | FuncCall::FuncCall() { 49 | nodeType = NodeType::FuncCall; 50 | } 51 | 52 | std::ostream& operator<<(std::ostream& ostream, const Statement* statement) { 53 | static int indentSize = 0; 54 | static bool skipNodeEnd = false; 55 | 56 | auto indent = [&]() -> void { 57 | ostream << std::string(indentSize * 2, ' '); 58 | }; 59 | auto printNodeStart = [&](const std::string& nodeType, bool multiLine = false) -> void { 60 | ostream << (multiLine ? "{\n" : "{ "); 61 | 62 | if (multiLine) { 63 | indentSize++; 64 | indent(); 65 | } 66 | 67 | ostream << "NodeType: \"" << nodeType << "\""; 68 | }; 69 | auto printAttribute = [&](const std::string& attribute, const std::string& value, 70 | bool multiLine = false) -> void 71 | { 72 | ostream << ","; 73 | ostream << (multiLine ? "\n" : " "); 74 | if (multiLine) indent(); 75 | ostream << attribute << ": " << value; 76 | }; 77 | auto printSubStatement = [&](const std::string& attribute, const Statement* statement, 78 | bool multiLine = false) -> void 79 | { 80 | ostream << ","; 81 | ostream << (multiLine ? "\n" : " "); 82 | if (multiLine) indent(); 83 | ostream << attribute << ": " << statement; 84 | }; 85 | auto printNodeEnd = [&](bool multiLine = false) -> void { 86 | if (skipNodeEnd) { 87 | ostream << " }"; 88 | skipNodeEnd = false; 89 | return; 90 | } 91 | 92 | if (multiLine) { 93 | indentSize--; 94 | indent(); 95 | ostream << "},\n"; 96 | } 97 | else ostream << " },\n"; 98 | }; 99 | 100 | if (statement == nullptr) return ostream; 101 | 102 | switch (statement->nodeType) { 103 | case NodeType::Program: 104 | { 105 | const Program* program = dynamic_cast(statement); 106 | 107 | printNodeStart("Program", true); 108 | 109 | ostream << ",\n"; 110 | indent(); 111 | ostream << "Statements: [\n"; 112 | indentSize++; 113 | 114 | for (auto& statement : program->statements) { 115 | indent(); 116 | ostream << statement; 117 | } 118 | 119 | indentSize--; 120 | indent(); 121 | ostream << "]\n"; 122 | 123 | printNodeEnd(true); 124 | break; 125 | } 126 | case NodeType::VarDeclaration: 127 | { 128 | const VarDeclaration* varDeclaration = dynamic_cast(statement); 129 | 130 | printNodeStart("NoodleDeclaration", true); 131 | printAttribute("Constant", (varDeclaration->constant ? "true" : "false"), true); 132 | printAttribute( 133 | "ValueType", std::to_string(static_cast(varDeclaration->valueType)), true 134 | ); 135 | printAttribute("Identifier", "\"" + varDeclaration->identifier + "\"", true); 136 | printSubStatement("Expr", varDeclaration->expr, true); 137 | printNodeEnd(true); 138 | break; 139 | } 140 | case NodeType::FuncDeclaration: 141 | { 142 | const FuncDeclaration* funcDeclaration = dynamic_cast(statement); 143 | 144 | printNodeStart("RecipeDeclaration", true); 145 | printAttribute("Name", "\"" + funcDeclaration->name + "\"", true); 146 | 147 | ostream << ",\n"; 148 | indent(); 149 | ostream << "Parameters: [\n"; 150 | indentSize++; 151 | 152 | for (auto& parameter : funcDeclaration->parameters) { 153 | indent(); 154 | ostream << "Name: \"" << parameter << "\",\n"; 155 | } 156 | 157 | indentSize--; 158 | indent(); 159 | ostream << "]"; 160 | 161 | ostream << ",\n"; 162 | indent(); 163 | ostream << "Statements: [\n"; 164 | indentSize++; 165 | 166 | for (auto& statement : funcDeclaration->statements) { 167 | indent(); 168 | ostream << statement; 169 | } 170 | 171 | indentSize--; 172 | indent(); 173 | ostream << "]\n"; 174 | 175 | printNodeEnd(true); 176 | break; 177 | } 178 | case NodeType::IfStatement: 179 | { 180 | const IfStatement* ifStatement = dynamic_cast(statement); 181 | 182 | printNodeStart("IfStatement", true); 183 | printSubStatement("Condition", ifStatement->condition, true); 184 | 185 | indent(); 186 | ostream << "IfStatements: [\n"; 187 | indentSize++; 188 | 189 | for (auto& statement : ifStatement->ifStatements) { 190 | indent(); 191 | ostream << statement; 192 | } 193 | 194 | indentSize--; 195 | indent(); 196 | ostream << "],\n"; 197 | 198 | indent(); 199 | ostream << "ElseStatements: [\n"; 200 | indentSize++; 201 | 202 | for (auto& statement : ifStatement->elseStatements) { 203 | indent(); 204 | ostream << statement; 205 | } 206 | 207 | indentSize--; 208 | indent(); 209 | ostream << "]\n"; 210 | 211 | printNodeEnd(true); 212 | break; 213 | } 214 | case NodeType::WhileStatement: 215 | { 216 | const WhileStatement* whileStatement = dynamic_cast(statement); 217 | 218 | printNodeStart("WhileStatement", true); 219 | printSubStatement("Condition", whileStatement->condition, true); 220 | 221 | indent(); 222 | ostream << "Statements: [\n"; 223 | indentSize++; 224 | 225 | for (auto& statement : whileStatement->statements) { 226 | indent(); 227 | ostream << statement; 228 | } 229 | 230 | indentSize--; 231 | indent(); 232 | ostream << "]\n"; 233 | 234 | printNodeEnd(true); 235 | break; 236 | } 237 | case NodeType::NullLiteral: 238 | { 239 | const NullLiteral* nullLiteral = dynamic_cast(statement); 240 | 241 | printNodeStart("NullLiteral"); 242 | printNodeEnd(); 243 | break; 244 | } 245 | case NodeType::NumLiteral: 246 | { 247 | const NumLiteral* numLiteral = dynamic_cast(statement); 248 | 249 | printNodeStart("NumLiteral"); 250 | printAttribute("Value", std::to_string(numLiteral->value)); 251 | printNodeEnd(); 252 | break; 253 | } 254 | case NodeType::StringLiteral: 255 | { 256 | const StringLiteral* stringLiteral = dynamic_cast(statement); 257 | 258 | std::string value = stringLiteral->value; 259 | unprocessEscapeCharacters(value); 260 | 261 | printNodeStart("StringLiteral"); 262 | printAttribute("Value", value + "\""); 263 | printNodeEnd(); 264 | break; 265 | } 266 | case NodeType::ListLiteral: 267 | { 268 | const ListLiteral* listLiteral = dynamic_cast(statement); 269 | 270 | printNodeStart("ListLiteral", true); 271 | 272 | ostream << ",\n"; 273 | indent(); 274 | ostream << "Elements: [\n"; 275 | indentSize++; 276 | 277 | for (auto& element : listLiteral->elements) { 278 | indent(); 279 | ostream << element; 280 | } 281 | 282 | indentSize--; 283 | indent(); 284 | ostream << "]"; 285 | ostream << "\n"; 286 | 287 | printNodeEnd(true); 288 | break; 289 | } 290 | case NodeType::Identifier: 291 | { 292 | const Identifier* identifier = dynamic_cast(statement); 293 | 294 | printNodeStart("Identifier"); 295 | printAttribute("Name", "\"" + identifier->name + "\""); 296 | printNodeEnd(); 297 | break; 298 | } 299 | case NodeType::ListAccesser: 300 | { 301 | const ListAccesser* listAccesser = dynamic_cast(statement); 302 | 303 | printNodeStart("ListAccesser"); 304 | printAttribute("Name", "\"" + listAccesser->name + "\""); 305 | printAttribute("Index", std::to_string(listAccesser->index)); 306 | printNodeEnd(); 307 | break; 308 | } 309 | case NodeType::UnaryExpr: 310 | { 311 | const UnaryExpr* unaryExpr = dynamic_cast(statement); 312 | 313 | printNodeStart("UnaryExpr", true); 314 | skipNodeEnd = true; 315 | printSubStatement("Expr", unaryExpr->expr, true); 316 | printAttribute("Operator", "\"" + unaryExpr->operation + "\"", true); 317 | ostream << "\n"; 318 | printNodeEnd(true); 319 | break; 320 | } 321 | case NodeType::BinaryExpr: 322 | { 323 | const BinaryExpr* binaryExpr = dynamic_cast(statement); 324 | 325 | printNodeStart("BinaryExpr", true); 326 | skipNodeEnd = true; 327 | printSubStatement("LeftExpr", binaryExpr->leftExpr, true); 328 | skipNodeEnd = true; 329 | printSubStatement("RightExpr", binaryExpr->rightExpr, true); 330 | printAttribute("Operator", "\"" + binaryExpr->operation + "\"", true); 331 | ostream << "\n"; 332 | printNodeEnd(true); 333 | break; 334 | } 335 | case NodeType::AssignmentExpr: 336 | { 337 | const AssignmentExpr* assignmentExpr = dynamic_cast(statement); 338 | 339 | printNodeStart("AssignmentExpr", true); 340 | skipNodeEnd = true; 341 | printSubStatement("Assigne", assignmentExpr->assigne, true); 342 | skipNodeEnd = true; 343 | printSubStatement("Value", assignmentExpr->value, true); 344 | ostream << "\n"; 345 | printNodeEnd(true); 346 | break; 347 | } 348 | case NodeType::FuncCall: 349 | { 350 | const FuncCall* funcCall = dynamic_cast(statement); 351 | 352 | printNodeStart("RecipeCall", true); 353 | printAttribute("Name", funcCall->caller, true); 354 | 355 | ostream << ",\n"; 356 | indent(); 357 | ostream << "Arguments: [\n"; 358 | indentSize++; 359 | 360 | for (auto& argument : funcCall->arguments) { 361 | indent(); 362 | ostream << argument; 363 | } 364 | 365 | indentSize--; 366 | indent(); 367 | ostream << "]"; 368 | ostream << "\n"; 369 | 370 | printNodeEnd(true); 371 | } 372 | break; 373 | default: 374 | ostream << "Unrecognized statement " << static_cast(statement->nodeType); 375 | ostream << "\n"; 376 | break; 377 | } 378 | 379 | return ostream; 380 | } 381 | } -------------------------------------------------------------------------------- /src/backend/Lexer.cpp: -------------------------------------------------------------------------------- 1 | #include "../../hdr/backend/Lexer.hpp" 2 | #include "../../hdr/util/Error.hpp" 3 | 4 | namespace ns { 5 | TokenIdentifier TokenIdentifier::tokenIdentifiers[static_cast(TokenType::Invalid)] = { 6 | { {} }, 7 | { {} }, 8 | { {} }, 9 | { { "frozen" } }, 10 | { { "noodle" } }, 11 | { { "recipe" } }, 12 | { { "eat" } }, 13 | { { "if" } }, 14 | { { "else" } }, 15 | { { "while" } }, 16 | { { "=" } }, 17 | { { "++", "--", "!" } }, 18 | { { "+", "-", "*", "/", "%", "==", "!=", ">", "<", ">=", "<=", "**", "&&", "||" } }, 19 | { { "(" } }, 20 | { { ")" } }, 21 | { { "[" } }, 22 | { { "]" } }, 23 | { {} }, 24 | { { "#" } }, 25 | { { "\n", "\r", ";" } } 26 | }; 27 | 28 | void processEscapeCharacters(std::string& string) { 29 | for (int chrIndex = 0; chrIndex < string.size(); chrIndex++) { 30 | const char chr = string[chrIndex]; 31 | 32 | // Checks for potential escape characters if a slash is found 33 | if (chr == '\\' && chrIndex < string.size() - 1) { 34 | const char nextChr = string[chrIndex + 1]; 35 | 36 | char replacementChr = ' '; 37 | 38 | // Replace specific characters with escape characters 39 | switch (nextChr) { 40 | case 'n': 41 | replacementChr = '\n'; 42 | break; 43 | case 't': 44 | replacementChr = '\t'; 45 | break; 46 | } 47 | 48 | if (replacementChr != ' ') { 49 | // Set the slash character to the escape character 50 | string[chrIndex] = replacementChr; 51 | // Erase the extra character 52 | string.erase(string.begin() + chrIndex + 1); 53 | } 54 | } 55 | } 56 | } 57 | void unprocessEscapeCharacters(std::string& string) { 58 | for (int chrIndex = 0; chrIndex < string.size(); chrIndex++) { 59 | const char chr = string[chrIndex]; 60 | 61 | std::string insertionString; 62 | 63 | // Replace specific escape characters with their visual text representation 64 | switch (chr) { 65 | case '\n': 66 | insertionString = "\\n"; 67 | break; 68 | case '\t': 69 | insertionString = "\\t"; 70 | break; 71 | } 72 | 73 | if (!insertionString.empty()) { 74 | // Erase the escape character 75 | string.erase(string.begin() + chrIndex); 76 | // Insert new string 77 | string.insert(chrIndex, insertionString); 78 | chrIndex++; 79 | } 80 | } 81 | } 82 | std::string extractNextIdentifier(std::string& line, char seperator) { 83 | // Characters that are irrelevent to lexing, and are thus skipped over 84 | const char skippableChrs[] = { ' ', '\t', '\0' }; 85 | // Charactes that cannot be chained with other characters 86 | const char nonChainableChrs[] = { '(', ')', '[', ']' }; 87 | enum class IdentifierType { Alpha, Numeric, Symbolic, Unknown }; 88 | 89 | auto isSkippableChar = [&skippableChrs](char chr) -> bool { 90 | for (auto skippableChr : skippableChrs) { 91 | if (skippableChr == chr) 92 | return true; 93 | } 94 | 95 | return false; 96 | }; 97 | auto isUnchainableChar = [&nonChainableChrs](char chr) -> bool { 98 | for (auto nonChainableChr : nonChainableChrs) { 99 | if (nonChainableChr == chr) 100 | return true; 101 | } 102 | 103 | return false; 104 | }; 105 | auto determineIdentifierType = [](char chr) -> IdentifierType { 106 | return (chr == '_' || (chr >= 'A' && chr <= 'Z') // _ A-Z a-z 107 | || (chr >= 'a' && chr <= 'z')) ? IdentifierType::Alpha 108 | : ((chr == '.'|| chr == '-' || (chr >= '0' && chr <= '9') // . 0-9 109 | ? IdentifierType::Numeric : IdentifierType::Symbolic)); 110 | }; 111 | 112 | IdentifierType identifierType = IdentifierType::Unknown; 113 | std::string nextIdentifier; 114 | size_t nextIdentifierEnd = 0; 115 | bool isString = false; 116 | bool isComment = false; 117 | 118 | for (auto chr : line) { 119 | if (isComment) { 120 | nextIdentifierEnd++; 121 | 122 | if (chr == '\n') 123 | break; 124 | 125 | continue; 126 | } 127 | 128 | if (isUnchainableChar(chr)) { 129 | if (nextIdentifierEnd == 0) { 130 | nextIdentifierEnd++; 131 | nextIdentifier += chr; 132 | } 133 | 134 | break; 135 | } 136 | 137 | if (!isSkippableChar(chr) || isString) { 138 | if (!isComment) { 139 | if (chr == '\"') { 140 | if (isString) { 141 | nextIdentifierEnd++; 142 | break; 143 | } 144 | 145 | isString = true; 146 | } 147 | // Handle comment token 148 | else if (chr == '#') { 149 | isComment = true; 150 | 151 | if (nextIdentifier.size() > 0) 152 | break; 153 | else 154 | nextIdentifierEnd++; 155 | 156 | continue; 157 | } 158 | 159 | if (isString) { 160 | nextIdentifierEnd++; 161 | nextIdentifier += chr; 162 | continue; 163 | } 164 | } 165 | 166 | IdentifierType currentIdentifierType = determineIdentifierType(chr); 167 | 168 | if (identifierType == IdentifierType::Unknown) 169 | identifierType = currentIdentifierType; 170 | else { 171 | if (identifierType == IdentifierType::Numeric) { 172 | // Handles -- operator 173 | if (nextIdentifier[0] == '-' && chr == '-') { 174 | nextIdentifierEnd++; 175 | nextIdentifier += chr; 176 | break; 177 | } 178 | else if (nextIdentifier.size() > 0 && (chr == '-' || currentIdentifierType 179 | != IdentifierType::Numeric)) 180 | break; 181 | } 182 | 183 | if ((identifierType == IdentifierType::Symbolic 184 | && currentIdentifierType != IdentifierType::Symbolic) 185 | || (identifierType != IdentifierType::Symbolic 186 | && (currentIdentifierType == IdentifierType::Symbolic || chr == '-'))) 187 | break; 188 | } 189 | 190 | nextIdentifier += chr; 191 | } 192 | else if (nextIdentifier.size() > 0) 193 | break; 194 | 195 | nextIdentifierEnd++; 196 | } 197 | 198 | line = line.substr(nextIdentifierEnd); 199 | 200 | return nextIdentifier; 201 | } 202 | std::vector& extractWords(const std::string& string) { 203 | static std::vector words; 204 | 205 | words.clear(); 206 | 207 | using WordPair = std::pair; 208 | auto seperate = [](const std::string& string, char seperator) -> WordPair { 209 | const size_t seperatorIndex = string.find(seperator); 210 | return seperatorIndex == std::string::npos ? WordPair(string, "") 211 | : WordPair( 212 | string.substr(0, seperatorIndex), 213 | string.substr(seperatorIndex + 1) 214 | ); 215 | }; 216 | 217 | const char seperator = ' '; 218 | WordPair wordPair = seperate(string, seperator); 219 | 220 | // Keeps seperating words until empty 221 | while (wordPair.first != "") { 222 | words.push_back(wordPair.first); 223 | wordPair = seperate(wordPair.second, seperator); 224 | } 225 | 226 | return words; 227 | } 228 | Token determineTokenType(const std::string& nextIdentifier) { 229 | TokenType tokenType = static_cast(0); 230 | Token nextToken{ TokenType::Invalid, nextIdentifier}; 231 | 232 | enum class TokenIdentifyMethod { Number, ReservedKeyword }; 233 | const int firstChr = nextIdentifier[0]; 234 | const int secondChr = nextIdentifier.size() > 1 ? nextIdentifier[1] : ' '; 235 | 236 | // Immediately returns strings 237 | if (firstChr == '\"') return { TokenType::String, nextIdentifier }; 238 | 239 | const TokenIdentifyMethod tokenIdentificationMethod = 240 | (firstChr == '-' && std::isdigit(secondChr)) || std::isdigit(firstChr) 241 | ? TokenIdentifyMethod::Number : 242 | TokenIdentifyMethod::ReservedKeyword; 243 | 244 | switch (tokenIdentificationMethod) { 245 | case TokenIdentifyMethod::Number: 246 | nextToken.type = TokenType::Number; 247 | goto ReturnToken; 248 | case TokenIdentifyMethod::ReservedKeyword: 249 | for (auto& tokenIdentifier : TokenIdentifier::tokenIdentifiers) { 250 | // Checks if identifier is reserved in the tokenIdentifiers array 251 | for (auto& identifier : tokenIdentifier.identifiers) { 252 | if (nextIdentifier == identifier) { 253 | nextToken = { tokenType, nextIdentifier }; 254 | goto ReturnToken; 255 | } 256 | } 257 | 258 | tokenType = static_cast(static_cast(tokenType) + 1); 259 | } 260 | 261 | nextToken.type = TokenType::Identifier; 262 | break; 263 | } 264 | 265 | ReturnToken: 266 | return nextToken; 267 | } 268 | std::vector& tokenize(const std::string& sourceCode) { 269 | static std::vector tokens; 270 | 271 | tokens.clear(); 272 | 273 | // New source string that be modified 274 | std::string remainingSource = sourceCode; 275 | 276 | // Inserts escape characters 277 | processEscapeCharacters(remainingSource); 278 | 279 | // Continues processing tokens while source code still remains 280 | while (remainingSource.size() > 0) { 281 | std::string nextIdentifier = extractNextIdentifier(remainingSource); 282 | 283 | if (nextIdentifier.empty()) continue; 284 | 285 | Token nextToken = determineTokenType(nextIdentifier); 286 | 287 | if (nextToken.type == TokenType::Invalid) { 288 | throw Error( 289 | Error::Location::Lexer, 290 | 10, 291 | "Invalid identifier " + nextToken.string + " found in source code" 292 | ); 293 | } 294 | 295 | tokens.push_back(nextToken); 296 | } 297 | 298 | if (tokens.size() > 0) { 299 | // Adds EOL token if not placed at end of tokens vector 300 | if (tokens.back().type != TokenType::EndOfLine) 301 | tokens.push_back({ TokenType::EndOfLine, "EOL" }); 302 | } 303 | 304 | // Add EOF token to notify the parser when the tokens vector is over 305 | tokens.push_back({ TokenType::EndOfFile, "EOF" }); 306 | return tokens; 307 | } 308 | 309 | std::ostream& operator<<(std::ostream& ostream, Token token) { 310 | std::string value = (token.type == TokenType::EndOfLine) ? "EOL" : token.string; 311 | unprocessEscapeCharacters(value); 312 | ostream << " { Value: " << value << ", Type: " 313 | << static_cast(token.type) << " }\n"; 314 | return ostream; 315 | } 316 | std::ostream& operator<<(std::ostream& ostream, const std::vector& tokens) { 317 | for (auto& token : tokens) 318 | ostream << token; 319 | return ostream; 320 | } 321 | } -------------------------------------------------------------------------------- /src/backend/Parser.cpp: -------------------------------------------------------------------------------- 1 | #include "../../hdr/backend/Parser.hpp" 2 | #include "../../hdr/util/Error.hpp" 3 | #include "../../hdr/runtime/Values.hpp" 4 | 5 | namespace ns { 6 | Parser::Parser() : 7 | tokens(nullptr), 8 | prvsToken({ TokenType::EndOfLine }) 9 | { 10 | } 11 | 12 | std::vector* Parser::lexSourceCode(const std::string& sourceCode) { 13 | tokens = &tokenize(sourceCode); 14 | return tokens; 15 | } 16 | Program* Parser::produceAST() { 17 | if (tokens == nullptr) 18 | throw Error( 19 | Error::Location::Parser, 20 | 1, 21 | "Cannot create AST without generating tokens first." 22 | ); 23 | 24 | Program* program = new Program(); 25 | 26 | // Continues parsing program until EOF 27 | while (!atEOF()) { 28 | Statement* statement = parseStatement(); 29 | 30 | // Don't add empty statements to program 31 | if (statement != nullptr) 32 | program->statements.push_back(statement); 33 | } 34 | 35 | return program; 36 | } 37 | 38 | Statement* Parser::parseStatement() { 39 | switch (getToken().type) { 40 | case TokenType::Var: // noodle ... 41 | return parseVarDeclaration(); 42 | case TokenType::Const: // frozen noodle ... 43 | return parseVarDeclaration(); 44 | case TokenType::Func: // recipe ... 45 | return parseFuncDeclaration(); 46 | case TokenType::If: // if ... 47 | return parseIfStatement(); 48 | case TokenType::While: // while ... 49 | return parseWhileStatement(); 50 | default: 51 | // Parse everything else 52 | return parseExpr(); 53 | } 54 | } 55 | Statement* Parser::parseVarDeclaration() { 56 | const Token firstToken = popToken(); 57 | const bool isConstant = firstToken.type == TokenType::Const; 58 | 59 | auto determineValueType = [](NodeType nodeType) -> ValueType { 60 | switch (nodeType) { 61 | case NodeType::NumLiteral: 62 | return ValueType::Number; 63 | default: 64 | return ValueType::Null; 65 | } 66 | }; 67 | 68 | const Token typeToken = isConstant ? expectToken(TokenType::Var) : firstToken; 69 | const std::string identifier = expectToken(TokenType::Identifier).string; 70 | 71 | // Stops evaluation early if EOF token found 72 | if (getToken().type == TokenType::EndOfLine) { 73 | popToken(); 74 | 75 | if (isConstant) 76 | throw "Must assign a value to constant."; 77 | 78 | VarDeclaration* varDeclaration = globalMemory->create(); 79 | 80 | varDeclaration->constant = isConstant; 81 | varDeclaration->valueType = ValueType::Null; 82 | varDeclaration->identifier = identifier; 83 | varDeclaration->expr = globalMemory->create(); 84 | 85 | return varDeclaration; 86 | } 87 | 88 | // Continue evaluation 89 | expectToken(TokenType::Equals); 90 | 91 | VarDeclaration* varDeclaration = globalMemory->create(); 92 | 93 | varDeclaration->constant = isConstant; 94 | varDeclaration->identifier = identifier; 95 | varDeclaration->expr = parseExpr(); 96 | varDeclaration->valueType = determineValueType(varDeclaration->expr->nodeType); 97 | 98 | expectToken(TokenType::EndOfLine); 99 | return varDeclaration; 100 | } 101 | Statement* Parser::parseFuncDeclaration() { 102 | popToken(); 103 | 104 | FuncDeclaration* funcDeclaration = globalMemory->create(); 105 | 106 | funcDeclaration->name = expectToken(TokenType::Identifier).string; 107 | 108 | expectToken(TokenType::OpenParen); 109 | 110 | // Enum that handles the state of the parameter parsing 111 | enum class ParameterPosition { Specifier, Identifier, End } 112 | parameterPosition = ParameterPosition::Specifier; 113 | std::string parameter; 114 | 115 | while (true) { 116 | Token nextToken = getToken(); 117 | 118 | if (nextToken.type == TokenType::CloseParen) { 119 | popToken(); 120 | break; 121 | } 122 | else if (nextToken.type == TokenType::EndOfFile) { 123 | popToken(); 124 | throw Error( 125 | Error::Location::Parser, 126 | 15, 127 | "Parameter list for recipe " + funcDeclaration->name + " incomplete" 128 | ); 129 | } 130 | 131 | switch (parameterPosition) { 132 | case ParameterPosition::Specifier: 133 | expectToken(TokenType::Var); 134 | break; 135 | case ParameterPosition::Identifier: 136 | nextToken = expectToken(TokenType::Identifier); 137 | parameter = nextToken.string; 138 | funcDeclaration->parameters.push_back(parameter); 139 | break; 140 | } 141 | 142 | // Increments the parameter position 143 | parameterPosition = static_cast( 144 | (static_cast(parameterPosition) + 1) 145 | % static_cast(ParameterPosition::End) 146 | ); 147 | } 148 | 149 | std::vector& statements = funcDeclaration->statements; 150 | 151 | // Parse body statements until EOF or eat token is found 152 | while (!atEOF() && getToken().type != TokenType::EndStatement) { 153 | Statement* statement = parseStatement(); 154 | 155 | if (statement != nullptr) 156 | statements.push_back(statement); 157 | } 158 | 159 | expectToken(TokenType::EndStatement); 160 | return funcDeclaration; 161 | } 162 | Statement* Parser::parseIfStatement() { 163 | popToken(); 164 | 165 | IfStatement* ifStatement = globalMemory->create(); 166 | 167 | ifStatement->condition = parseExpr(); 168 | 169 | std::vector& ifStatements = ifStatement->ifStatements; 170 | 171 | // Parse statements until EOF, eat, or else token is found 172 | while (!atEOF() && getToken().type != TokenType::EndStatement 173 | && getToken().type != TokenType::Else) 174 | { 175 | Statement* statement = parseStatement(); 176 | 177 | if (statement != nullptr) 178 | ifStatements.push_back(statement); 179 | } 180 | 181 | // Check for eat token 182 | if (getToken().type == TokenType::EndStatement) { 183 | popToken(); 184 | return ifStatement; 185 | } 186 | 187 | // Otherwise, continue with else statement 188 | expectToken(TokenType::Else); 189 | 190 | std::vector& elseStatements = ifStatement->elseStatements; 191 | 192 | // Parse statements until EOF or eat token is found 193 | while (!atEOF() && getToken().type != TokenType::EndStatement) { 194 | Statement* statement = parseStatement(); 195 | 196 | if (statement != nullptr) 197 | elseStatements.push_back(statement); 198 | } 199 | 200 | expectToken(TokenType::EndStatement); 201 | return ifStatement; 202 | } 203 | Statement* Parser::parseWhileStatement() { 204 | popToken(); 205 | 206 | WhileStatement* whileStatement = globalMemory->create(); 207 | 208 | whileStatement->condition = parseExpr(); 209 | 210 | std::vector& statements = whileStatement->statements; 211 | 212 | // Parse statements until EOF or eat token is found 213 | while (!atEOF() && getToken().type != TokenType::EndStatement) { 214 | Statement* statement = parseStatement(); 215 | 216 | if (statement != nullptr) 217 | statements.push_back(statement); 218 | } 219 | 220 | expectToken(TokenType::EndStatement); 221 | return whileStatement; 222 | } 223 | 224 | Expr* Parser::parseExpr() { 225 | return parseAssignmentExpr(); 226 | } 227 | Expr* Parser::parseAssignmentExpr() { 228 | Expr* leftExpr = parseConditionalExpr(); 229 | 230 | if (getToken().type == TokenType::Equals) { 231 | popToken(); 232 | 233 | Expr* value = parseAssignmentExpr(); 234 | AssignmentExpr* assignmentExpr = globalMemory->create(); 235 | 236 | assignmentExpr->assigne = leftExpr; 237 | assignmentExpr->value = value; 238 | 239 | return assignmentExpr; 240 | } 241 | 242 | return leftExpr; 243 | } 244 | Expr* Parser::parseConditionalExpr() { 245 | Expr* leftExpr = parsePrimaryConditionalExpr(); 246 | 247 | const std::string tokenString = getToken().string; 248 | 249 | if (tokenString == "&&" || tokenString == "||") { 250 | const std::string operation = popToken().string; 251 | 252 | Expr* rightExpr = parseConditionalExpr(); 253 | BinaryExpr* binaryExpr = globalMemory->create(); 254 | 255 | binaryExpr->leftExpr = leftExpr; 256 | binaryExpr->rightExpr = rightExpr; 257 | binaryExpr->operation = operation; 258 | leftExpr = binaryExpr; 259 | } 260 | 261 | return leftExpr; 262 | } 263 | Expr* Parser::parsePrimaryConditionalExpr() { 264 | if (getToken().string == "!") { 265 | const std::string operation = popToken().string; 266 | 267 | Expr* expr = parsePrimaryConditionalExpr(); 268 | UnaryExpr* unaryExpr = globalMemory->create(); 269 | 270 | unaryExpr->expr = expr; 271 | unaryExpr->operation = operation; 272 | return unaryExpr; 273 | } 274 | 275 | Expr* leftExpr = parseAdditiveExpr(); 276 | 277 | const std::string tokenString = getToken().string; 278 | 279 | if (tokenString == "==" || tokenString == "!=" || tokenString == ">" || tokenString == "<" 280 | || tokenString == ">=" || tokenString == "<=") 281 | { 282 | const std::string operation = popToken().string; 283 | 284 | Expr* rightExpr = parsePrimaryConditionalExpr(); 285 | BinaryExpr* binaryExpr = globalMemory->create(); 286 | 287 | binaryExpr->leftExpr = leftExpr; 288 | binaryExpr->rightExpr = rightExpr; 289 | binaryExpr->operation = operation; 290 | leftExpr = binaryExpr; 291 | } 292 | 293 | return leftExpr; 294 | } 295 | Expr* Parser::parseAdditiveExpr() { 296 | Expr* leftExpr = parseMultiplicativeExpr(); 297 | 298 | if (getToken().string == "+" || getToken().string == "-") { 299 | const std::string operation = popToken().string; 300 | 301 | Expr* rightExpr = parseMultiplicativeExpr(); 302 | BinaryExpr* binaryExpr = globalMemory->create(); 303 | 304 | binaryExpr->leftExpr = leftExpr; 305 | binaryExpr->rightExpr = rightExpr; 306 | binaryExpr->operation = operation; 307 | leftExpr = binaryExpr; 308 | } 309 | 310 | return leftExpr; 311 | } 312 | Expr* Parser::parseMultiplicativeExpr() { 313 | Expr* leftExpr = parseAdditiveUnaryExpr(); 314 | 315 | if (getToken().string == "*" || getToken().string == "/" || getToken().string == "%" 316 | || getToken().string == "**") 317 | { 318 | const std::string operation = popToken().string; 319 | 320 | Expr* rightExpr = parseAdditiveUnaryExpr(); 321 | BinaryExpr* binaryExpr = globalMemory->create(); 322 | 323 | binaryExpr->leftExpr = leftExpr; 324 | binaryExpr->rightExpr = rightExpr; 325 | binaryExpr->operation = operation; 326 | leftExpr = binaryExpr; 327 | } 328 | 329 | return leftExpr; 330 | } 331 | Expr* Parser::parseAdditiveUnaryExpr() { 332 | Expr* expr; 333 | 334 | if (getToken().string == "++" || getToken().string == "--") { 335 | const std::string operation = popToken().string; 336 | 337 | UnaryExpr* unaryExpr = globalMemory->create(); 338 | Identifier* identifier = globalMemory->create(); 339 | 340 | identifier->name = expectToken(TokenType::Identifier).string; 341 | unaryExpr->expr = identifier; 342 | unaryExpr->operation = operation; 343 | expr = unaryExpr; 344 | } 345 | else if (getToken().type == TokenType::Identifier && (getToken(1).string == "++" 346 | || getToken(1).string == "--")) 347 | { 348 | const std::string identifierName = popToken().string; 349 | 350 | UnaryExpr* unaryExpr = globalMemory->create(); 351 | Identifier* identifier = globalMemory->create(); 352 | 353 | identifier->name = identifierName; 354 | unaryExpr->expr = identifier; 355 | unaryExpr->operation = popToken().string; 356 | expr = unaryExpr; 357 | } 358 | else 359 | expr = parseFuncCallExpr(); 360 | 361 | return expr; 362 | } 363 | Expr* Parser::parseFuncCallExpr() { 364 | if (prvsToken.type != TokenType::Func 365 | && getToken().type == TokenType::Identifier 366 | && getToken(1).type == TokenType::OpenParen) 367 | { 368 | FuncCall* funcCall = globalMemory->create(); 369 | 370 | funcCall->caller = popToken().string; 371 | popToken(); 372 | 373 | // Parse arguments until EOF or closed paren token is found 374 | while (!atEOF() && getToken().type != TokenType::CloseParen) 375 | funcCall->arguments.push_back(parseExpr()); 376 | 377 | expectToken(TokenType::CloseParen); 378 | 379 | return funcCall; 380 | } 381 | 382 | return parsePrimaryExpr(); 383 | } 384 | Expr* Parser::parsePrimaryExpr() { 385 | const Token token = getToken(); 386 | 387 | switch (token.type) { 388 | case TokenType::Number: 389 | { 390 | NumLiteral* numLiteral = globalMemory->create(); 391 | numLiteral->value = std::stod(popToken().string); 392 | return numLiteral; 393 | } 394 | case TokenType::String: 395 | { 396 | StringLiteral* stringLiteral = globalMemory->create(); 397 | stringLiteral->value = popToken().string; 398 | return stringLiteral; 399 | } 400 | case TokenType::BinaryOperator: 401 | if (token.string == "-") { 402 | popToken(); 403 | 404 | if (getToken().type == TokenType::Identifier) { 405 | UnaryExpr* unaryExpr = globalMemory->create(); 406 | unaryExpr->expr = parseExpr(); 407 | unaryExpr->operation = "-"; 408 | return unaryExpr; 409 | } 410 | } 411 | 412 | goto UnexpectedToken; 413 | case TokenType::OpenParen: 414 | { 415 | popToken(); 416 | Expr* value = parseExpr(); 417 | expectToken(TokenType::CloseParen); 418 | return value; 419 | } 420 | case TokenType::OpenBracket: 421 | { 422 | popToken(); 423 | ListLiteral* listLiteral = globalMemory->create(); 424 | 425 | // Parse elements until EOF or closed bracket token is found 426 | while (!atEOF() && getToken().type != TokenType::CloseBracket) 427 | listLiteral->elements.push_back(parseExpr()); 428 | 429 | expectToken(TokenType::CloseBracket); 430 | return listLiteral; 431 | } 432 | case TokenType::Identifier: 433 | { 434 | const std::string identifierName = popToken().string; 435 | 436 | if (getToken().type == TokenType::OpenBracket) { 437 | ListAccesser* listAccessor = globalMemory->create(); 438 | 439 | listAccessor->name = identifierName; 440 | 441 | const Expr* insideExpr = parseExpr(); 442 | 443 | if (insideExpr->nodeType == NodeType::NumLiteral) { 444 | const NumLiteral* indexExpr = dynamic_cast(insideExpr); 445 | listAccessor->index = indexExpr->value; 446 | } 447 | 448 | expectToken(TokenType::CloseBracket); 449 | return listAccessor; 450 | } 451 | 452 | Identifier* identifier = globalMemory->create(); 453 | identifier->name = identifierName; 454 | return identifier; 455 | } 456 | case TokenType::EndOfLine: 457 | popToken(); 458 | return nullptr; 459 | default: 460 | UnexpectedToken: 461 | throw Error( 462 | Error::Location::Parser, 463 | 12, 464 | "Unexpected token " + getToken().string + " found during parsing." 465 | ); 466 | } 467 | } 468 | 469 | Token Parser::getToken(int index) const { 470 | return (*tokens)[index]; 471 | } 472 | Token Parser::popToken(int index) { 473 | prvsToken = getToken(index); 474 | tokens->erase(tokens->begin() + index); 475 | return prvsToken; 476 | } 477 | Token Parser::expectToken(TokenType tokenType, int index) { 478 | const Token token = popToken(index); 479 | 480 | if (token.type != tokenType) { 481 | throw Error( 482 | Error::Location::Parser, 483 | 12, 484 | "Unexpected token " + getToken().string + " found during parsing." 485 | ); 486 | } 487 | 488 | return token; 489 | } 490 | bool Parser::atEOF() const { 491 | return getToken().type == TokenType::EndOfFile; 492 | } 493 | } -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../hdr/Terminal.hpp" 2 | 3 | int main(int argc, const char** argv) { 4 | ns::initTerminal(argc, argv); 5 | 6 | while (ns::isTerminalOpen()) 7 | ns::updateTerminal(); 8 | 9 | ns::closeTerminal(); 10 | return 0; 11 | } -------------------------------------------------------------------------------- /src/runtime/Interpreter.cpp: -------------------------------------------------------------------------------- 1 | #include "../../hdr/runtime/Interpreter.hpp" 2 | #include "../../hdr/util/Error.hpp" 3 | 4 | namespace ns { 5 | Interpreter::Interpreter() : 6 | program(nullptr), 7 | globalScope(nullptr), 8 | runtimeValue(nullptr), 9 | shouldLogTokens(false), 10 | shouldLogAST(false), 11 | shouldLogRuntimeValue(false) 12 | { 13 | globalMemory = &memory; 14 | 15 | try { 16 | initGlobalScope(); 17 | } 18 | catch (const Error& error) { 19 | std::cout << error; 20 | } 21 | } 22 | Interpreter::~Interpreter() { 23 | if (program != nullptr) 24 | delete program; 25 | } 26 | 27 | void Interpreter::generateProgram(const std::string& sourceCode) { 28 | if (program != nullptr) { 29 | delete program; 30 | program = nullptr; 31 | } 32 | 33 | try { 34 | const std::vector* tokens = parser.lexSourceCode(sourceCode); 35 | copyTokens(tokens); 36 | 37 | if (shouldLogTokens) logTokens(); 38 | program = parser.produceAST(); 39 | 40 | if (shouldLogAST) logAST(); 41 | } 42 | catch (const Error& error) { 43 | std::cout << error; 44 | 45 | delete program; 46 | program = nullptr; 47 | } 48 | } 49 | void Interpreter::runProgram() { 50 | try { 51 | if (program == nullptr) 52 | throw Error( 53 | Error::Location::Interpreter, 54 | 11, 55 | "Unable to run program, as it does not exist." 56 | ); 57 | 58 | runtimeValue = evaluateASTNode(program, globalScope); 59 | 60 | if (shouldLogRuntimeValue) logRuntimeValue(); 61 | } 62 | catch (const Error& error) { 63 | std::cout << error; 64 | } 65 | } 66 | void Interpreter::deleteProgramMemory() { 67 | memory.clear(); 68 | } 69 | void Interpreter::logTokens() const { 70 | std::cout << "\nLexed Tokens:\n"; 71 | std::cout << tokens; 72 | } 73 | void Interpreter::logAST() const { 74 | if (program == nullptr) 75 | throw Error( 76 | Error::Location::Interpreter, 77 | 7, 78 | "Cannot log AST, as it is undefined." 79 | ); 80 | 81 | std::cout << "\nGenerated AST:\n"; 82 | std::cout << program; 83 | std::cout << "\n"; 84 | } 85 | 86 | void Interpreter::enableTokenLogging(bool shouldLogTokens) { 87 | std::cout << " Updated interpreter variable `shouldLogTokens` to "; 88 | std::cout << (shouldLogTokens ? "true" : "false") << "\n"; 89 | this->shouldLogTokens = shouldLogTokens; 90 | } 91 | void Interpreter::enableASTLogging(bool shouldLogAST) { 92 | std::cout << " Updated interpreter variable `shouldLogAST` to "; 93 | std::cout << (shouldLogAST ? "true" : "false") << "\n"; 94 | this->shouldLogAST = shouldLogAST; 95 | } 96 | void Interpreter::enableRuntimeValueLogging(bool shouldLogRuntimeValue) { 97 | std::cout << " Updated interpreter variable `shouldLogRuntimeValue` to "; 98 | std::cout << (shouldLogRuntimeValue ? "true" : "false") << "\n"; 99 | this->shouldLogRuntimeValue = shouldLogRuntimeValue; 100 | } 101 | 102 | bool Interpreter::isTokenLoggingEnabled() const { 103 | return shouldLogTokens; 104 | } 105 | bool Interpreter::isASTLoggingEnabled() const { 106 | return shouldLogAST; 107 | } 108 | bool Interpreter::isRuntimeValueLoggingEnabled() const { 109 | return shouldLogRuntimeValue; 110 | } 111 | 112 | void Interpreter::initGlobalScope(bool reallocate) { 113 | globalScope = memory.create(); 114 | globalScope->declareVariable("null", memory.create(), true); 115 | 116 | BoolValue* trueValue = memory.create(); 117 | trueValue->state = true; 118 | BoolValue* falseValue = memory.create(); 119 | falseValue->state = false; 120 | 121 | globalScope->declareVariable("true", trueValue, true); 122 | globalScope->declareVariable("false", falseValue, true); 123 | 124 | // Create all of the native functions 125 | for (int nativeFuncNameIndex = 0; nativeFuncNameIndex < numOfNativeFuncs; 126 | nativeFuncNameIndex++) 127 | { 128 | const char* nativeFuncName = nativeFuncNames[nativeFuncNameIndex]; 129 | 130 | FuncValue* funcValue = memory.create(); 131 | 132 | funcValue->name = nativeFuncName; 133 | globalScope->declareVariable(funcValue->name, funcValue, true); 134 | } 135 | } 136 | void Interpreter::copyTokens(const std::vector* tokens) { 137 | if (tokens == nullptr) 138 | throw Error( 139 | Error::Location::Interpreter, 140 | 8, 141 | "Cannot log tokens, as they are undefined." 142 | ); 143 | 144 | this->tokens.clear(); 145 | 146 | for (auto& token : *tokens) 147 | this->tokens.push_back(token); 148 | } 149 | void Interpreter::logRuntimeValue() { 150 | if (runtimeValue == nullptr) 151 | throw Error( 152 | Error::Location::Interpreter, 153 | 9, 154 | "Cannot log runtime value, as it is undefined. " 155 | ); 156 | 157 | std::cout << runtimeValue; 158 | std::cout << "\n"; 159 | } 160 | } -------------------------------------------------------------------------------- /src/runtime/Scope.cpp: -------------------------------------------------------------------------------- 1 | #include "../../hdr/runtime/Scope.hpp" 2 | #include "../../hdr/util/Error.hpp" 3 | 4 | namespace ns { 5 | Scope::Scope() : parent(nullptr) { 6 | } 7 | 8 | Scope* Scope::assignParent(Scope* parent) { 9 | this->parent = parent; 10 | return parent; 11 | } 12 | RuntimeValue* Scope::declareVariable( 13 | const std::string& variableName, RuntimeValue* runtimeValue, bool constant) 14 | { 15 | // Checks if variable already exists 16 | if (variables.find(variableName) == variables.end()) 17 | variables[variableName] = { constant, runtimeValue }; 18 | else { 19 | throw Error( 20 | Error::Location::Interpreter, 21 | 2, 22 | "Cannot create noodle " + variableName + " as it is already defined in this scope" 23 | ); 24 | } 25 | 26 | return runtimeValue; 27 | } 28 | RuntimeValue* Scope::assignVariable( 29 | const std::string& variableName, RuntimeValue* runtimeValue) 30 | { 31 | Scope* scope = resolveVariable(variableName); 32 | Variable& variable = scope->variables[variableName]; 33 | 34 | // Ensures constants cannot be reassigned 35 | if (variable.constant) 36 | throw Error( 37 | Error::Location::Interpreter, 38 | 3, 39 | "Cannot redeclare noodle " + variableName + " as it is a frozen noodle." 40 | ); 41 | 42 | variable.runtimeValue = runtimeValue; 43 | return runtimeValue; 44 | } 45 | Scope* Scope::resolveVariable(const std::string& variableName) { 46 | // Returns current scope if variable is found in variables map 47 | if (variables.find(variableName) != variables.end()) 48 | return this; 49 | 50 | if (parent == nullptr) 51 | throw Error( 52 | Error::Location::Interpreter, 53 | 4, 54 | "Undefined noodle " + variableName + " referenced in scope." 55 | ); 56 | 57 | return parent->resolveVariable(variableName); 58 | } 59 | RuntimeValue* Scope::getVariableValue(const std::string& variableName) { 60 | Scope* scope = resolveVariable(variableName); 61 | return scope->variables[variableName].runtimeValue; 62 | } 63 | bool Scope::isVariableConst(const std::string& variableName) { 64 | Scope* scope = resolveVariable(variableName); 65 | const Variable variable = scope->variables[variableName]; 66 | return variable.constant; 67 | } 68 | } -------------------------------------------------------------------------------- /src/runtime/Values.cpp: -------------------------------------------------------------------------------- 1 | #include "../../hdr/runtime/Values.hpp" 2 | #include "../../hdr/util/Memory.hpp" 3 | 4 | namespace ns { 5 | const char* nativeFuncNames[numOfNativeFuncs] = { 6 | "serve", 7 | "serveRaw", 8 | "receive", 9 | "floor", 10 | "ceil", 11 | "round", 12 | "num", 13 | "bool", 14 | "str", 15 | "pow", 16 | "sqrt", 17 | "len", 18 | "append", 19 | "at", 20 | "set", 21 | "pop", 22 | "abs" 23 | }; 24 | 25 | NullValue::NullValue() { 26 | valueType = ValueType::Null; 27 | } 28 | NumValue::NumValue() { 29 | valueType = ValueType::Number; 30 | } 31 | NumValue::NumValue(double value) : value(value) { 32 | valueType = ValueType::Number; 33 | } 34 | BoolValue::BoolValue() { 35 | valueType = ValueType::Bool; 36 | } 37 | BoolValue::BoolValue(bool state) : state(state) { 38 | valueType = ValueType::Bool; 39 | } 40 | StringValue::StringValue() { 41 | valueType = ValueType::String; 42 | } 43 | StringValue::StringValue(const std::string& value) : value(value) { 44 | valueType = ValueType::String; 45 | } 46 | ListValue::ListValue() { 47 | valueType = ValueType::List; 48 | } 49 | FuncValue::FuncValue() { 50 | valueType = ValueType::FuncValue; 51 | } 52 | 53 | RuntimeValue* cloneRuntimeValue(RuntimeValue* runtimeValue) { 54 | switch (runtimeValue->valueType) { 55 | case ValueType::Number: 56 | { 57 | const NumValue* numValue = dynamic_cast(runtimeValue); 58 | NumValue* clonedNumValue = globalMemory->create(); 59 | clonedNumValue->value = numValue->value; 60 | return clonedNumValue; 61 | } 62 | case ValueType::Bool: 63 | { 64 | const BoolValue* boolValue = dynamic_cast(runtimeValue); 65 | BoolValue* clonedBoolValue = globalMemory->create(); 66 | clonedBoolValue->state = boolValue->state; 67 | return clonedBoolValue; 68 | } 69 | case ValueType::String: 70 | { 71 | const StringValue* stringValue = dynamic_cast(runtimeValue); 72 | StringValue* clonedStringValue = globalMemory->create(); 73 | clonedStringValue->value = stringValue->value; 74 | return clonedStringValue; 75 | } 76 | default: 77 | return runtimeValue; 78 | } 79 | } 80 | 81 | std::ostream& operator<<(std::ostream& ostream, const RuntimeValue* runtimeValue) { 82 | switch (runtimeValue->valueType) { 83 | case ValueType::Null: 84 | { 85 | const NullValue* nullValue = dynamic_cast(runtimeValue); 86 | 87 | ostream << " { ValueType: Null, Value: " << nullValue->value; 88 | ostream << " }\n"; 89 | break; 90 | } 91 | case ValueType::Number: 92 | { 93 | const NumValue* numberValue = dynamic_cast(runtimeValue); 94 | 95 | ostream << " { ValueType: Number, Value: " << numberValue->value; 96 | ostream << " }\n"; 97 | break; 98 | } 99 | case ValueType::Bool: 100 | { 101 | const BoolValue* boolValue = dynamic_cast(runtimeValue); 102 | 103 | ostream << " { ValueType: Bool, Value: " << (boolValue->state ? "true" : "false"); 104 | ostream << " }\n"; 105 | break; 106 | } 107 | case ValueType::String: 108 | { 109 | const StringValue* stringValue = dynamic_cast(runtimeValue); 110 | 111 | ostream << " { ValueType: String, Value: " << stringValue->value; 112 | ostream << " }\n"; 113 | break; 114 | } 115 | case ValueType::List: 116 | { 117 | const ListValue* listValue = dynamic_cast(runtimeValue); 118 | 119 | ostream << " { ValueType: List }\n"; 120 | break; 121 | } 122 | case ValueType::FuncValue: 123 | { 124 | const FuncValue* funcValue = dynamic_cast(runtimeValue); 125 | 126 | ostream << " { ValueType: FuncValue, Name: " << funcValue->name << " }\n"; 127 | break; 128 | } 129 | } 130 | 131 | return ostream; 132 | } 133 | } -------------------------------------------------------------------------------- /src/runtime/eval/Expressions.cpp: -------------------------------------------------------------------------------- 1 | #include "../../../hdr/runtime/eval/Expressions.hpp" 2 | #include "../../../hdr/util/Error.hpp" 3 | 4 | namespace ns { 5 | RuntimeValue* evaluateFuncCallExpr(FuncCall* funcCall, Scope* scope) { 6 | FuncValue* funcValue = dynamic_cast( 7 | scope->getVariableValue(funcCall->caller) 8 | ); 9 | 10 | // Handle native functions seperately 11 | if (funcValue->nativeFunc) 12 | return evaluateNativeFuncCallExpr(funcValue, funcCall, scope); 13 | 14 | Scope* funcScope = globalMemory->create(); 15 | 16 | funcScope->assignParent(scope); 17 | 18 | // Declare all of the parameters in the function scope 19 | for (int parameterIndex = 0; parameterIndex < funcValue->parameters.size(); 20 | parameterIndex++) 21 | { 22 | const std::string& parameter = funcValue->parameters[parameterIndex]; 23 | Expr* argument = funcCall->arguments[parameterIndex]; 24 | 25 | funcScope->declareVariable(parameter, evaluateASTNode(argument, scope)); 26 | } 27 | 28 | RuntimeValue* result = globalMemory->create(); 29 | 30 | // Evaluate each function statement 31 | for (auto& statement : funcValue->statements) 32 | result = evaluateASTNode(statement, funcScope); 33 | 34 | return result; 35 | } 36 | RuntimeValue* evaluateNativeFuncCallExpr( 37 | FuncValue* funcValue, FuncCall* funcCall, Scope* scope) 38 | { 39 | auto verifyArgumentCount = [&](int count) -> void { 40 | const int argumentCount = funcCall->arguments.size(); 41 | 42 | // Throw error if argument counts aren't equal 43 | if (argumentCount != count) { 44 | throw Error( 45 | Error::Location::Interpreter, 46 | 14, 47 | "Cannot call recipe " + funcCall->caller + " with " 48 | + std::to_string(argumentCount) 49 | + (argumentCount == 1 ? " argument" : " arguments") 50 | ); 51 | } 52 | }; 53 | 54 | RuntimeValue* result = globalMemory->create(); 55 | 56 | if (funcValue->name == nativeFuncNames[0]) { // serve 57 | auto printEvaluatedArgument = [](const RuntimeValue* evaluatedArgument) -> void { 58 | auto printEvaluatedArgumentImpl = [](const RuntimeValue* evaluatedArgument, 59 | auto& caller) -> void 60 | { 61 | switch (evaluatedArgument->valueType) { 62 | case ValueType::Number: 63 | { 64 | const NumValue* numberValue = dynamic_cast( 65 | evaluatedArgument 66 | ); 67 | std::cout << numberValue->value; 68 | break; 69 | } 70 | case ValueType::Bool: 71 | { 72 | const BoolValue* boolValue = dynamic_cast(evaluatedArgument); 73 | std::cout << (boolValue->state ? "true" : "false"); 74 | break; 75 | } 76 | case ValueType::String: 77 | { 78 | const StringValue* stringValue = dynamic_cast( 79 | evaluatedArgument 80 | ); 81 | std::cout << stringValue->value; 82 | break; 83 | } 84 | case ValueType::List: 85 | { 86 | const ListValue* listValue = dynamic_cast(evaluatedArgument); 87 | 88 | std::cout << "["; 89 | 90 | for (int elementIndex = 0; elementIndex < listValue->elements.size(); 91 | elementIndex++) 92 | { 93 | const RuntimeValue* element = listValue->elements[elementIndex]; 94 | caller(element, caller); 95 | 96 | if (elementIndex < listValue->elements.size() - 1) 97 | std::cout << " "; 98 | } 99 | 100 | std::cout << "]"; 101 | break; 102 | } 103 | } 104 | }; 105 | 106 | printEvaluatedArgumentImpl(evaluatedArgument, printEvaluatedArgumentImpl); 107 | }; 108 | 109 | // Loop through the argument list and call local lambda 110 | for (auto* argument : funcCall->arguments) { 111 | const RuntimeValue* evaluatedArgument = evaluateASTNode(argument, scope); 112 | printEvaluatedArgument(evaluatedArgument); 113 | } 114 | } 115 | else if (funcValue->name == nativeFuncNames[1]) { // serveRaw 116 | for (auto* argument : funcCall->arguments) 117 | std::cout << argument; 118 | } 119 | else if (funcValue->name == nativeFuncNames[2]) { // receive 120 | std::string input; 121 | std::getline(std::cin, input); 122 | 123 | StringValue* stringValue = globalMemory->create(); 124 | 125 | stringValue->value = input; 126 | result = stringValue; 127 | } 128 | else if (funcValue->name == nativeFuncNames[3]) { // floor 129 | verifyArgumentCount(1); 130 | 131 | const RuntimeValue* evaluatedArgument = evaluateASTNode(funcCall->arguments[0], scope); 132 | 133 | switch (evaluatedArgument->valueType) { 134 | case ValueType::Number: 135 | { 136 | const NumValue* evaluatedNumValue = dynamic_cast( 137 | evaluatedArgument 138 | ); 139 | 140 | NumValue* numberValue = globalMemory->create(); 141 | 142 | numberValue->value = std::floor(evaluatedNumValue->value); 143 | result = numberValue; 144 | } 145 | break; 146 | } 147 | } 148 | else if (funcValue->name == nativeFuncNames[4]) { // ceil 149 | verifyArgumentCount(1); 150 | 151 | const RuntimeValue* evaluatedArgument = evaluateASTNode(funcCall->arguments[0], scope); 152 | 153 | switch (evaluatedArgument->valueType) { 154 | case ValueType::Number: 155 | { 156 | const NumValue* evaluatedNumValue = dynamic_cast( 157 | evaluatedArgument 158 | ); 159 | 160 | NumValue* numberValue = globalMemory->create(); 161 | 162 | numberValue->value = std::ceil(evaluatedNumValue->value); 163 | result = numberValue; 164 | } 165 | break; 166 | } 167 | } 168 | else if (funcValue->name == nativeFuncNames[5]) { // round 169 | verifyArgumentCount(1); 170 | 171 | const RuntimeValue* evaluatedArgument = evaluateASTNode(funcCall->arguments[0], scope); 172 | 173 | switch (evaluatedArgument->valueType) { 174 | case ValueType::Number: 175 | { 176 | const NumValue* evaluatedNumValue = dynamic_cast( 177 | evaluatedArgument 178 | ); 179 | 180 | NumValue* numberValue = globalMemory->create(); 181 | 182 | numberValue->value = std::round(evaluatedNumValue->value); 183 | result = numberValue; 184 | } 185 | break; 186 | } 187 | } 188 | else if (funcValue->name == nativeFuncNames[6]) { // int 189 | verifyArgumentCount(1); 190 | 191 | const RuntimeValue* evaluatedArgument = evaluateASTNode(funcCall->arguments[0], scope); 192 | 193 | switch (evaluatedArgument->valueType) { 194 | case ValueType::Number: 195 | { 196 | const NumValue* evaluatedNumValue = dynamic_cast( 197 | evaluatedArgument 198 | ); 199 | 200 | NumValue* numberValue = globalMemory->create(); 201 | 202 | numberValue->value = evaluatedNumValue->value; 203 | result = numberValue; 204 | break; 205 | } 206 | case ValueType::String: 207 | { 208 | const StringValue* evaluatedStringValue = dynamic_cast( 209 | evaluatedArgument 210 | ); 211 | 212 | NumValue* numberValue = globalMemory->create(); 213 | numberValue->value = 0; 214 | result = numberValue; 215 | 216 | try { 217 | numberValue->value = std::stod(evaluatedStringValue->value); 218 | } 219 | catch (...) {} 220 | 221 | break; 222 | } 223 | } 224 | } 225 | else if (funcValue->name == nativeFuncNames[7]) { // bool 226 | verifyArgumentCount(1); 227 | 228 | const RuntimeValue* evaluatedArgument = evaluateASTNode(funcCall->arguments[0], scope); 229 | 230 | switch (evaluatedArgument->valueType) { 231 | case ValueType::Number: 232 | { 233 | const NumValue* evaluatedNumValue = dynamic_cast( 234 | evaluatedArgument 235 | ); 236 | 237 | BoolValue* boolValue = globalMemory->create(); 238 | 239 | boolValue->state = evaluatedNumValue->value > 0; 240 | result = boolValue; 241 | break; 242 | } 243 | case ValueType::String: 244 | { 245 | const StringValue* evaluatedStringValue = dynamic_cast( 246 | evaluatedArgument 247 | ); 248 | 249 | BoolValue* boolValue = globalMemory->create(); 250 | 251 | boolValue->state = evaluatedStringValue->value == "true"; 252 | result = boolValue; 253 | break; 254 | } 255 | } 256 | } 257 | else if (funcValue->name == nativeFuncNames[8]) { // str 258 | verifyArgumentCount(1); 259 | 260 | const RuntimeValue* evaluatedArgument = evaluateASTNode(funcCall->arguments[0], scope); 261 | 262 | switch (evaluatedArgument->valueType) { 263 | case ValueType::Number: 264 | { 265 | const NumValue* evaluatedNumValue = dynamic_cast( 266 | evaluatedArgument 267 | ); 268 | 269 | StringValue* stringValue = globalMemory->create(); 270 | 271 | stringValue->value = std::to_string(evaluatedNumValue->value); 272 | result = stringValue; 273 | break; 274 | } 275 | case ValueType::Bool: 276 | { 277 | const BoolValue* evaluatedBoolValue = dynamic_cast( 278 | evaluatedArgument 279 | ); 280 | 281 | StringValue* stringValue = globalMemory->create(); 282 | 283 | stringValue->value= evaluatedBoolValue->state ? "true" : "false"; 284 | result = stringValue; 285 | break; 286 | } 287 | } 288 | } 289 | else if (funcValue->name == nativeFuncNames[9]) { // pow 290 | verifyArgumentCount(2); 291 | 292 | const RuntimeValue* evaluatedArgument1 = evaluateASTNode(funcCall->arguments[0], scope); 293 | const RuntimeValue* evaluatedArgument2 = evaluateASTNode(funcCall->arguments[1], scope); 294 | 295 | if (evaluatedArgument1->valueType == ValueType::Number 296 | && evaluatedArgument2->valueType == ValueType::Number) 297 | { 298 | const NumValue* evaluatedNumValue1 = dynamic_cast( 299 | evaluatedArgument1 300 | ); 301 | const NumValue* evaluatedNumValue2 = dynamic_cast( 302 | evaluatedArgument2 303 | ); 304 | 305 | NumValue* numberValue = globalMemory->create(); 306 | 307 | numberValue->value = std::pow( 308 | evaluatedNumValue1->value, evaluatedNumValue2->value 309 | ); 310 | result = numberValue; 311 | } 312 | } 313 | else if (funcValue->name == nativeFuncNames[10]) { // sqrt 314 | verifyArgumentCount(1); 315 | 316 | const RuntimeValue* evaluatedArgument = evaluateASTNode(funcCall->arguments[0], scope); 317 | 318 | if (evaluatedArgument->valueType == ValueType::Number) { 319 | const NumValue* evaluatedNumValue = dynamic_cast( 320 | evaluatedArgument 321 | ); 322 | 323 | NumValue* numberValue = globalMemory->create(); 324 | 325 | numberValue->value = std::sqrt(evaluatedNumValue->value); 326 | result = numberValue; 327 | } 328 | } 329 | else if (funcValue->name == nativeFuncNames[11]) { // len 330 | verifyArgumentCount(1); 331 | 332 | const RuntimeValue* evaluatedArgument = evaluateASTNode(funcCall->arguments[0], scope); 333 | 334 | if (evaluatedArgument->valueType == ValueType::String) { 335 | const StringValue* evaluatedStringValue = dynamic_cast( 336 | evaluatedArgument 337 | ); 338 | 339 | NumValue* numberValue = globalMemory->create(); 340 | 341 | numberValue->value = evaluatedStringValue->value.size(); 342 | result = numberValue; 343 | } 344 | else if (evaluatedArgument->valueType == ValueType::List) { 345 | const ListValue* evaluatedListValue = dynamic_cast( 346 | evaluatedArgument 347 | ); 348 | 349 | NumValue* numberValue = globalMemory->create(); 350 | 351 | numberValue->value = evaluatedListValue->elements.size(); 352 | result = numberValue; 353 | } 354 | } 355 | else if (funcValue->name == nativeFuncNames[12]) { // append 356 | verifyArgumentCount(2); 357 | 358 | RuntimeValue* evaluatedArgument1 = evaluateASTNode(funcCall->arguments[0], scope); 359 | RuntimeValue* evaluatedArgument2 = evaluateASTNode(funcCall->arguments[1], scope); 360 | 361 | if (evaluatedArgument1->valueType == ValueType::String) { 362 | StringValue* evaluatedStringValue1 = dynamic_cast( 363 | evaluatedArgument1 364 | ); 365 | 366 | if (evaluatedArgument2->valueType == ValueType::String) { 367 | const StringValue* evaluatedStringValue2 = dynamic_cast( 368 | evaluatedArgument2 369 | ); 370 | 371 | evaluatedStringValue1->value += evaluatedStringValue2->value; 372 | result = evaluatedArgument1; 373 | } 374 | } 375 | else if (evaluatedArgument1->valueType == ValueType::List) { 376 | ListValue* evaluatedListValue = dynamic_cast( 377 | evaluatedArgument1 378 | ); 379 | 380 | evaluatedListValue->elements.push_back(evaluatedArgument2); 381 | result = evaluatedArgument1; 382 | } 383 | } 384 | else if (funcValue->name == nativeFuncNames[13]) { // at 385 | verifyArgumentCount(2); 386 | 387 | RuntimeValue* evaluatedArgument1 = evaluateASTNode(funcCall->arguments[0], scope); 388 | RuntimeValue* evaluatedArgument2 = evaluateASTNode(funcCall->arguments[1], scope); 389 | 390 | if (evaluatedArgument1->valueType == ValueType::String) { 391 | StringValue* evaluatedStringValue = dynamic_cast( 392 | evaluatedArgument1 393 | ); 394 | 395 | if (evaluatedArgument2->valueType == ValueType::Number) { 396 | const NumValue* evaluatedNumValue = dynamic_cast( 397 | evaluatedArgument2 398 | ); 399 | 400 | StringValue* resultStringValue = globalMemory->create(); 401 | 402 | resultStringValue->value = evaluatedStringValue->value[evaluatedNumValue->value]; 403 | result = resultStringValue; 404 | } 405 | } 406 | else if (evaluatedArgument1->valueType == ValueType::List) { 407 | ListValue* evaluatedListValue = dynamic_cast( 408 | evaluatedArgument1 409 | ); 410 | 411 | if (evaluatedArgument2->valueType == ValueType::Number) { 412 | const NumValue* evaluatedNumValue = dynamic_cast( 413 | evaluatedArgument2 414 | ); 415 | 416 | return evaluatedListValue->elements[evaluatedNumValue->value]; 417 | } 418 | } 419 | } 420 | else if (funcValue->name == nativeFuncNames[14]) { // set 421 | verifyArgumentCount(3); 422 | 423 | if (funcCall->arguments[0]->nodeType == NodeType::Identifier) { 424 | const Identifier* identifier = dynamic_cast( 425 | funcCall->arguments[0] 426 | ); 427 | 428 | // If the variable is const, try to assign a value to it to throw an error 429 | if (scope->isVariableConst(identifier->name)) 430 | scope->assignVariable(identifier->name, nullptr); 431 | } 432 | 433 | RuntimeValue* evaluatedArgument1 = evaluateASTNode(funcCall->arguments[0], scope); 434 | RuntimeValue* evaluatedArgument2 = evaluateASTNode(funcCall->arguments[1], scope); 435 | RuntimeValue* evaluatedArgument3 = evaluateASTNode(funcCall->arguments[2], scope); 436 | 437 | if (evaluatedArgument1->valueType == ValueType::String) { 438 | StringValue* evaluatedStringValue = dynamic_cast( 439 | evaluatedArgument1 440 | ); 441 | 442 | if (evaluatedArgument2->valueType == ValueType::Number) { 443 | const NumValue* evaluatedNumValue = dynamic_cast( 444 | evaluatedArgument2 445 | ); 446 | 447 | if (evaluatedArgument3->valueType == ValueType::String) { 448 | const StringValue* evaluatedStringValue2 = dynamic_cast( 449 | evaluatedArgument3 450 | ); 451 | 452 | evaluatedStringValue->value[evaluatedNumValue->value] = 453 | evaluatedStringValue2->value[0]; 454 | } 455 | } 456 | } 457 | else if (evaluatedArgument1->valueType == ValueType::List) { 458 | ListValue* evaluatedListValue = dynamic_cast(evaluatedArgument1); 459 | 460 | if (evaluatedArgument2->valueType == ValueType::Number) { 461 | const NumValue* evaluatedNumValue = dynamic_cast( 462 | evaluatedArgument2 463 | ); 464 | 465 | evaluatedListValue->elements[evaluatedNumValue->value] = 466 | cloneRuntimeValue(evaluatedArgument3); 467 | } 468 | } 469 | } 470 | else if (funcValue->name == nativeFuncNames[15]) { // pop 471 | verifyArgumentCount(2); 472 | 473 | if (funcCall->arguments[0]->nodeType == NodeType::Identifier) { 474 | const Identifier* identifier = dynamic_cast( 475 | funcCall->arguments[0] 476 | ); 477 | 478 | // If the variable is const, try to assign a value to it to throw an error 479 | if (scope->isVariableConst(identifier->name)) 480 | scope->assignVariable(identifier->name, nullptr); 481 | } 482 | 483 | RuntimeValue* evaluatedArgument1 = evaluateASTNode(funcCall->arguments[0], scope); 484 | RuntimeValue* evaluatedArgument2 = evaluateASTNode(funcCall->arguments[1], scope); 485 | 486 | if (evaluatedArgument1->valueType == ValueType::String) { 487 | StringValue* evaluatedStringValue = dynamic_cast( 488 | evaluatedArgument1 489 | ); 490 | 491 | if (evaluatedArgument2->valueType == ValueType::Number) { 492 | const NumValue* evaluatedNumValue = dynamic_cast( 493 | evaluatedArgument2 494 | ); 495 | const int popIndex = evaluatedNumValue->value; 496 | 497 | StringValue* resultStringValue = globalMemory->create(); 498 | resultStringValue->value = evaluatedStringValue->value[popIndex]; 499 | evaluatedStringValue->value.erase( 500 | evaluatedStringValue->value.begin() + popIndex 501 | ); 502 | result = resultStringValue; 503 | } 504 | } 505 | else if (evaluatedArgument1->valueType == ValueType::List) { 506 | ListValue* evaluatedListValue = dynamic_cast(evaluatedArgument1); 507 | 508 | if (evaluatedArgument2->valueType == ValueType::Number) { 509 | const NumValue* evaluatedNumValue = dynamic_cast( 510 | evaluatedArgument2 511 | ); 512 | const int popIndex = evaluatedNumValue->value; 513 | 514 | result = evaluatedListValue->elements[popIndex]; 515 | evaluatedListValue->elements.erase( 516 | evaluatedListValue->elements.begin() + popIndex 517 | ); 518 | } 519 | } 520 | } 521 | else if (funcValue->name == nativeFuncNames[16]) { // abs 522 | verifyArgumentCount(1); 523 | 524 | const RuntimeValue* evaluatedArgument = evaluateASTNode(funcCall->arguments[0], scope); 525 | 526 | if (evaluatedArgument->valueType == ValueType::Number) { 527 | const NumValue* evaluatedNumValue = dynamic_cast( 528 | evaluatedArgument 529 | ); 530 | 531 | NumValue* resultNumValue = globalMemory->create(); 532 | 533 | resultNumValue->value = std::abs(evaluatedNumValue->value); 534 | result = resultNumValue; 535 | } 536 | } 537 | 538 | return result; 539 | } 540 | RuntimeValue* evaluateAssignmentExpr(AssignmentExpr* assignmentExpr, Scope* scope) { 541 | if (assignmentExpr->assigne->nodeType != NodeType::Identifier) 542 | throw Error( 543 | Error::Location::Interpreter, 544 | 6, 545 | "Invalid assigne referenced in assignment expression." 546 | ); 547 | 548 | const Identifier* identifier = dynamic_cast(assignmentExpr->assigne); 549 | return scope->assignVariable( 550 | identifier->name, cloneRuntimeValue(evaluateASTNode(assignmentExpr->value, scope)) 551 | ); 552 | } 553 | RuntimeValue* evaluateUnaryExprNode(UnaryExpr* unaryExpr, Scope* scope) { 554 | RuntimeValue* nodeEvaluated = evaluateASTNode(unaryExpr->expr, scope); 555 | 556 | if (nodeEvaluated->valueType == ValueType::Number) { 557 | Identifier* identifier = dynamic_cast(unaryExpr->expr); 558 | NumValue* numberValue = dynamic_cast( 559 | scope->getVariableValue(identifier->name) 560 | ); 561 | 562 | return evaluateNumericUnaryExpr(numberValue, unaryExpr->operation); 563 | } 564 | else if (nodeEvaluated->valueType == ValueType::Bool) { 565 | BoolValue* boolValue = dynamic_cast(nodeEvaluated); 566 | 567 | return evaluateConditionalUnaryExpr(boolValue, unaryExpr->operation); 568 | } 569 | 570 | return globalMemory->create(); 571 | } 572 | RuntimeValue* evaluateNumericUnaryExpr(NumValue* numberValue, const std::string& operation) { 573 | double& result = numberValue->value; 574 | 575 | if (operation == "-") { 576 | NumValue* numberValueResult = globalMemory->create(); 577 | numberValueResult->value = -result; 578 | return numberValueResult; 579 | } 580 | else if (operation == "++") 581 | result += 1.0; 582 | else if (operation == "--") 583 | result -= 1.0; 584 | 585 | return numberValue; 586 | } 587 | RuntimeValue* evaluateConditionalUnaryExpr(BoolValue* boolValue, const std::string& operation) { 588 | bool& state = boolValue->state; 589 | 590 | if (operation == "!") { 591 | BoolValue* boolValueResult = globalMemory->create(); 592 | boolValueResult->state = !state; 593 | return boolValueResult; 594 | } 595 | 596 | return globalMemory->create(); 597 | } 598 | RuntimeValue* evaluateBinaryExprNode(BinaryExpr* binaryExpr, Scope* scope) { 599 | RuntimeValue* leftNodeEvaluated = evaluateASTNode(binaryExpr->leftExpr, scope); 600 | RuntimeValue* rightNodeEvaluated = evaluateASTNode(binaryExpr->rightExpr, scope); 601 | 602 | const ValueType leftNodeValueType = leftNodeEvaluated->valueType; 603 | const ValueType rightNodeValueType = rightNodeEvaluated->valueType; 604 | 605 | // Evaluates numeric binary expressions 606 | if (leftNodeValueType == ValueType::Number && rightNodeValueType == ValueType::Number) { 607 | NumValue* leftNumValue = dynamic_cast(leftNodeEvaluated); 608 | NumValue* rightNumValue = dynamic_cast(rightNodeEvaluated); 609 | 610 | return evaluateNumericBinaryExpr( 611 | leftNumValue, rightNumValue, binaryExpr->operation 612 | ); 613 | } 614 | // Evaluates conditional binary expressions 615 | else if (leftNodeValueType == ValueType::Bool && rightNodeValueType == ValueType::Bool) { 616 | BoolValue* leftBoolValue = dynamic_cast(leftNodeEvaluated); 617 | BoolValue* rightBoolValue = dynamic_cast(rightNodeEvaluated); 618 | 619 | return evaluateConditionalBinaryExpr( 620 | leftBoolValue, rightBoolValue, binaryExpr->operation 621 | ); 622 | } 623 | // Evaluates string binary expressions 624 | else if (leftNodeValueType == ValueType::String 625 | && rightNodeValueType == ValueType::String) 626 | { 627 | StringValue* leftStringValue = dynamic_cast(leftNodeEvaluated); 628 | StringValue* rightStringValue = dynamic_cast(rightNodeEvaluated); 629 | 630 | return evaluateStringBinaryExpr( 631 | leftStringValue, rightStringValue, binaryExpr->operation 632 | ); 633 | } 634 | 635 | return globalMemory->create(); 636 | } 637 | RuntimeValue* evaluateNumericBinaryExpr( 638 | NumValue* leftNumValue, NumValue* rightNumValue, const std::string& operation) 639 | { 640 | RuntimeValue* result = globalMemory->create(); 641 | 642 | if (operation == "+") { 643 | NumValue* numberResult = globalMemory->create(); 644 | numberResult->value = leftNumValue->value + rightNumValue->value; 645 | result = numberResult; 646 | } 647 | else if (operation == "-") { 648 | NumValue* numberResult = globalMemory->create(); 649 | numberResult->value = leftNumValue->value - rightNumValue->value; 650 | result = numberResult; 651 | } 652 | else if (operation == "*") { 653 | NumValue* numberResult = globalMemory->create(); 654 | numberResult->value = leftNumValue->value * rightNumValue->value; 655 | result = numberResult; 656 | } 657 | else if (operation == "/") { 658 | NumValue* numberResult = globalMemory->create(); 659 | 660 | // Check if denominator is zero. If so, just return zero 661 | if (rightNumValue->value == 0.0) { 662 | numberResult = globalMemory->create(); 663 | numberResult->value = 0.0; 664 | } 665 | else { 666 | numberResult = globalMemory->create(); 667 | numberResult->value = leftNumValue->value / rightNumValue->value; 668 | } 669 | 670 | result = numberResult; 671 | } 672 | else if (operation == "%") { 673 | NumValue* numberResult = globalMemory->create(); 674 | 675 | // Check if denominator is zero. If so, just return zero 676 | if (rightNumValue->value == 0.0) numberResult->value = 0.0; 677 | else numberResult->value = std::fmod(leftNumValue->value, rightNumValue->value); 678 | 679 | result = numberResult; 680 | } 681 | else if (operation == "==") { 682 | BoolValue* boolResult = globalMemory->create(); 683 | boolResult->state = leftNumValue->value == rightNumValue->value; 684 | result = boolResult; 685 | } 686 | else if (operation == "!=") { 687 | BoolValue* boolResult = globalMemory->create(); 688 | boolResult->state = leftNumValue->value != rightNumValue->value; 689 | result = boolResult; 690 | } 691 | else if (operation == ">") { 692 | BoolValue* boolResult = globalMemory->create(); 693 | boolResult->state = leftNumValue->value > rightNumValue->value; 694 | result = boolResult; 695 | } 696 | else if (operation == "<") { 697 | BoolValue* boolResult = globalMemory->create(); 698 | boolResult->state = leftNumValue->value < rightNumValue->value; 699 | result = boolResult; 700 | } 701 | else if (operation == ">=") { 702 | BoolValue* boolResult = globalMemory->create(); 703 | boolResult->state = leftNumValue->value >= rightNumValue->value; 704 | result = boolResult; 705 | } 706 | else if (operation == "<=") { 707 | BoolValue* boolResult = globalMemory->create(); 708 | boolResult->state = leftNumValue->value <= rightNumValue->value; 709 | result = boolResult; 710 | } 711 | else if (operation == "**") { 712 | NumValue* numberResult = globalMemory->create(); 713 | numberResult->value = std::pow(leftNumValue->value, rightNumValue->value); 714 | result = numberResult; 715 | } 716 | 717 | return result; 718 | } 719 | RuntimeValue* evaluateConditionalBinaryExpr( 720 | BoolValue* leftBoolValue, BoolValue* rightBoolValue, const std::string& operation) 721 | { 722 | BoolValue* result = globalMemory->create(); 723 | 724 | if (operation == "&&") 725 | result->state = leftBoolValue->state && rightBoolValue->state; 726 | else if (operation == "||") 727 | result->state = leftBoolValue->state || rightBoolValue->state; 728 | 729 | return result; 730 | } 731 | RuntimeValue* evaluateStringBinaryExpr( 732 | StringValue* leftStringValue, StringValue* rightStringValue, 733 | const std::string& operation) 734 | { 735 | RuntimeValue* result = globalMemory->create(); 736 | 737 | if (operation == "+") { 738 | StringValue* stringResult = globalMemory->create(); 739 | stringResult->value = leftStringValue->value + rightStringValue->value; 740 | result = stringResult; 741 | } 742 | else if (operation == "==") { 743 | BoolValue* boolResult = globalMemory->create(); 744 | boolResult->state = leftStringValue->value == rightStringValue->value; 745 | result = boolResult; 746 | } 747 | else if (operation == "!=") { 748 | BoolValue* boolResult = globalMemory->create(); 749 | boolResult->state = leftStringValue->value != rightStringValue->value; 750 | result = boolResult; 751 | } 752 | 753 | return result; 754 | } 755 | RuntimeValue* evaluateIdentifier(Identifier* identifier, Scope* scope) { 756 | RuntimeValue* value = scope->getVariableValue(identifier->name); 757 | return value; 758 | } 759 | } -------------------------------------------------------------------------------- /src/runtime/eval/Statements.cpp: -------------------------------------------------------------------------------- 1 | #include "../../../hdr/runtime/eval/Statements.hpp" 2 | #include "../../../hdr/runtime/eval/Expressions.hpp" 3 | #include "../../../hdr/util/Error.hpp" 4 | 5 | namespace ns { 6 | RuntimeValue* evaluateProgramNode(Program* program, Scope* scope) { 7 | RuntimeValue* lastNodeEvaluated = globalMemory->create(); 8 | 9 | // Evaluate each statement and keep track of the previous runtime value 10 | for (auto& statement : program->statements) 11 | lastNodeEvaluated = evaluateASTNode(statement, scope); 12 | 13 | return lastNodeEvaluated; 14 | } 15 | RuntimeValue* evaluateVarDeclarationNode(VarDeclaration* varDeclaration, Scope* scope) { 16 | RuntimeValue* value = varDeclaration->expr->nodeType != NodeType::NullLiteral 17 | ? evaluateASTNode(varDeclaration->expr, scope) : globalMemory->create(); 18 | return scope->declareVariable(varDeclaration->identifier, value, varDeclaration->constant); 19 | } 20 | RuntimeValue* evaluateFuncDeclaration(FuncDeclaration* funcDeclaration, Scope* scope) { 21 | FuncValue* funcValue = globalMemory->create(); 22 | 23 | funcValue->name = funcDeclaration->name; 24 | funcValue->nativeFunc = funcDeclaration->nativeFunc; 25 | funcValue->parameters = funcDeclaration->parameters; 26 | funcValue->statements = funcDeclaration->statements; 27 | funcValue->scope = scope; 28 | 29 | scope->declareVariable(funcValue->name, funcValue, true); 30 | 31 | return funcValue; 32 | } 33 | RuntimeValue* evaluateIfStatementNode(IfStatement* ifStatement, Scope* scope) { 34 | const RuntimeValue* evaluatedCondition = evaluateASTNode(ifStatement->condition, scope); 35 | 36 | RuntimeValue* result = globalMemory->create(); 37 | 38 | // Make sure the evaluated condition is a bool 39 | if (evaluatedCondition->valueType == ValueType::Bool) { 40 | const BoolValue* condition = dynamic_cast(evaluatedCondition); 41 | 42 | // If condition is true, evaluate its if statements 43 | if (condition->state) { 44 | for (auto& statement : ifStatement->ifStatements) 45 | result = evaluateASTNode(statement, scope); 46 | } 47 | // Otherwise, evaluate its else statements 48 | else { 49 | for (auto& statement : ifStatement->elseStatements) 50 | result = evaluateASTNode(statement, scope); 51 | } 52 | } 53 | 54 | return result; 55 | } 56 | RuntimeValue* evaluateWhileStatementNode(WhileStatement* whileStatement, Scope* scope) { 57 | RuntimeValue* result = globalMemory->create(); 58 | 59 | while (true) { 60 | const RuntimeValue* evaluatedCondition = evaluateASTNode(whileStatement->condition, scope); 61 | 62 | // Make sure the evaluated condition is a bool 63 | if (evaluatedCondition->valueType == ValueType::Bool) { 64 | const BoolValue* condition = dynamic_cast(evaluatedCondition); 65 | 66 | // If condition is true, evaluate its statements 67 | if (condition->state) { 68 | for (auto& statement : whileStatement->statements) 69 | result = evaluateASTNode(statement, scope); 70 | } 71 | else break; 72 | } 73 | } 74 | 75 | return result; 76 | } 77 | RuntimeValue* evaluateASTNode(Statement* astNode, Scope* scope) { 78 | switch (astNode->nodeType) { 79 | case NodeType::Program: 80 | { 81 | Program* program = dynamic_cast(astNode); 82 | return evaluateProgramNode(program, scope); 83 | } 84 | case NodeType::VarDeclaration: 85 | { 86 | VarDeclaration* varDeclaration = dynamic_cast(astNode); 87 | return evaluateVarDeclarationNode(varDeclaration, scope); 88 | } 89 | case NodeType::FuncDeclaration: 90 | { 91 | FuncDeclaration* funcDeclaration = dynamic_cast(astNode); 92 | return evaluateFuncDeclaration(funcDeclaration, scope); 93 | } 94 | case NodeType::IfStatement: 95 | { 96 | IfStatement* ifStatement = dynamic_cast(astNode); 97 | return evaluateIfStatementNode(ifStatement, scope); 98 | } 99 | case NodeType::WhileStatement: 100 | { 101 | WhileStatement* whileStatement = dynamic_cast(astNode); 102 | return evaluateWhileStatementNode(whileStatement, scope); 103 | } 104 | case NodeType::NullLiteral: 105 | { 106 | const NullLiteral* nullLiteral = dynamic_cast(astNode); 107 | return globalMemory->create(); 108 | } 109 | case NodeType::NumLiteral: 110 | { 111 | const NumLiteral* numLiteral = dynamic_cast(astNode); 112 | NumValue* numberValue = globalMemory->create(); 113 | numberValue->value = numLiteral->value; 114 | return numberValue; 115 | } 116 | case NodeType::StringLiteral: 117 | { 118 | const StringLiteral* stringLiteral = dynamic_cast(astNode); 119 | StringValue* stringValue = globalMemory->create(); 120 | stringValue->value = stringLiteral->value.substr(1); 121 | return stringValue; 122 | } 123 | case NodeType::ListLiteral: 124 | { 125 | const ListLiteral* listLiteral = dynamic_cast(astNode); 126 | ListValue* listValue = globalMemory->create(); 127 | 128 | // Add each expression to the list once evaluated 129 | for (auto runtimeValue : listLiteral->elements) 130 | listValue->elements.push_back(evaluateASTNode(runtimeValue, scope)); 131 | 132 | return listValue; 133 | } 134 | case NodeType::FuncCall: 135 | { 136 | FuncCall* funcCall = dynamic_cast(astNode); 137 | return evaluateFuncCallExpr(funcCall, scope); 138 | } 139 | case NodeType::Identifier: 140 | { 141 | Identifier* identifier = dynamic_cast(astNode); 142 | return evaluateIdentifier(identifier, scope); 143 | } 144 | case NodeType::UnaryExpr: 145 | { 146 | UnaryExpr* unaryExpr = dynamic_cast(astNode); 147 | return evaluateUnaryExprNode(unaryExpr, scope); 148 | } 149 | case NodeType::BinaryExpr: 150 | { 151 | BinaryExpr* binaryExpr = dynamic_cast(astNode); 152 | return evaluateBinaryExprNode(binaryExpr, scope); 153 | } 154 | case NodeType::AssignmentExpr: 155 | { 156 | AssignmentExpr* assignmentExpr = dynamic_cast(astNode); 157 | return evaluateAssignmentExpr(assignmentExpr, scope); 158 | } 159 | default: 160 | throw Error( 161 | Error::Location::Interpreter, 162 | 5, 163 | "Unimplemented AST node " + std::to_string(static_cast(astNode->nodeType)) 164 | + " found." 165 | ); 166 | } 167 | } 168 | } -------------------------------------------------------------------------------- /src/util/Error.cpp: -------------------------------------------------------------------------------- 1 | #include "../../hdr/util/Error.hpp" 2 | 3 | namespace ns { 4 | Error::Error() : 5 | location(Location::Lexer), 6 | code(-1) 7 | { 8 | } 9 | Error::Error(Location location, int code, std::string message) : 10 | location(location), 11 | code(code), 12 | message(message) 13 | { 14 | } 15 | 16 | const std::string Error::locations[static_cast(Location::End)] = { 17 | "Lexer", "Parser", "Interpreter" 18 | }; 19 | 20 | std::ostream& operator<<(std::ostream& ostream, const Error& error) { 21 | ostream << Error::locations[static_cast(error.location)]; 22 | ostream << " Error (" << error.code << "): "; 23 | ostream << error.message << "\n"; 24 | return ostream; 25 | } 26 | } -------------------------------------------------------------------------------- /src/util/Memory.cpp: -------------------------------------------------------------------------------- 1 | #include "../../hdr/util/Memory.hpp" 2 | 3 | namespace ns { 4 | Memory::Memory() { 5 | } 6 | Memory::~Memory() { 7 | clear(); 8 | } 9 | 10 | void Memory::clear() { 11 | while (pointers.size() > 0) { 12 | delete pointers.back(); 13 | pointers.pop_back(); 14 | } 15 | } 16 | 17 | int Memory::getPointerCount() const { 18 | return pointers.size(); 19 | } 20 | 21 | Memory* globalMemory = nullptr; 22 | } --------------------------------------------------------------------------------