| @@ -110,7 +110,7 @@ private: | |||||
| X(if_, "if") X(else_, "else") X(do_, "do") \ | X(if_, "if") X(else_, "else") X(do_, "do") \ | ||||
| X(while_, "while") X(for_, "for") X(break_, "break") X(continue_, "continue") \ | X(while_, "while") X(for_, "for") X(break_, "break") X(continue_, "continue") \ | ||||
| X(void_, "void") X(int_, "int") X(float_, "float") X(bool_, "bool") \ | X(void_, "void") X(int_, "int") X(float_, "float") X(bool_, "bool") \ | ||||
| X(return_, "return") X(true_, "true") X(false_, "false") | |||||
| X(return_, "return") X(true_, "true") X(false_, "false") X(const_, "const") | |||||
| #define LITTLEFOOT_OPERATORS(X) \ | #define LITTLEFOOT_OPERATORS(X) \ | ||||
| X(semicolon, ";") X(dot, ".") X(comma, ",") X(hash, "#") \ | X(semicolon, ";") X(dot, ".") X(comma, ",") X(hash, "#") \ | ||||
| @@ -144,7 +144,7 @@ private: | |||||
| CodeLocation (const String& code) noexcept : program (code), location (program.getCharPointer()) {} | CodeLocation (const String& code) noexcept : program (code), location (program.getCharPointer()) {} | ||||
| CodeLocation (const CodeLocation& other) noexcept : program (other.program), location (other.location) {} | CodeLocation (const CodeLocation& other) noexcept : program (other.program), location (other.location) {} | ||||
| void throwError (const String& message) const | |||||
| [[noreturn]] void throwError (const String& message) const | |||||
| { | { | ||||
| int col = 1, line = 1; | int col = 1, line = 1; | ||||
| @@ -205,7 +205,7 @@ private: | |||||
| { | { | ||||
| if (isIdentifierStart (*p)) | if (isIdentifierStart (*p)) | ||||
| { | { | ||||
| String::CharPointerType end (p); | |||||
| auto end = p; | |||||
| while (isIdentifierBody (*++end)) {} | while (isIdentifierBody (*++end)) {} | ||||
| const size_t len = (size_t) (end - p); | const size_t len = (size_t) (end - p); | ||||
| @@ -329,7 +329,7 @@ private: | |||||
| bool parseOctalLiteral() | bool parseOctalLiteral() | ||||
| { | { | ||||
| String::CharPointerType t (p); | |||||
| auto t = p; | |||||
| int64 v = *t - '0'; | int64 v = *t - '0'; | ||||
| if (v != 0) return false; // first digit of octal must be 0 | if (v != 0) return false; // first digit of octal must be 0 | ||||
| @@ -380,6 +380,8 @@ private: | |||||
| continue; | continue; | ||||
| } | } | ||||
| const bool isConstVariable = matchIf (Token::const_); | |||||
| if (! matchesAnyTypeOrVoid()) | if (! matchesAnyTypeOrVoid()) | ||||
| throwErrorExpecting ("a global variable or function"); | throwErrorExpecting ("a global variable or function"); | ||||
| @@ -388,6 +390,9 @@ private: | |||||
| if (matchIf (Token::openParen)) | if (matchIf (Token::openParen)) | ||||
| { | { | ||||
| if (isConstVariable) | |||||
| location.throwError ("Return type of a function cannot be const"); | |||||
| parseFunctionDeclaration (type, name); | parseFunctionDeclaration (type, name); | ||||
| continue; | continue; | ||||
| } | } | ||||
| @@ -395,19 +400,7 @@ private: | |||||
| if (type == Type::void_) | if (type == Type::void_) | ||||
| location.throwError ("A variable type cannot be 'void'"); | location.throwError ("A variable type cannot be 'void'"); | ||||
| int arraySize = matchIf (Token::openBracket) ? parseIntegerLiteral() : 0; | |||||
| if (arraySize > 0) | |||||
| location.throwError ("Arrays not yet implemented!"); | |||||
| while (matchIf (Token::comma)) | |||||
| { | |||||
| blockBeingParsed->addVariable (name, type, location); | |||||
| name = parseIdentifier(); | |||||
| } | |||||
| blockBeingParsed->addVariable (name, type, location); | |||||
| match (Token::semicolon); | |||||
| parseGlobalVariableDeclaraion (isConstVariable, type, name); | |||||
| } | } | ||||
| } | } | ||||
| @@ -462,6 +455,54 @@ private: | |||||
| location.throwError ("Unknown compiler directive"); | location.throwError ("Unknown compiler directive"); | ||||
| } | } | ||||
| void parseGlobalVariableDeclaraion (bool isConst, Type type, String name) | |||||
| { | |||||
| for (;;) | |||||
| { | |||||
| int arraySize = matchIf (Token::openBracket) ? parseIntegerLiteral() : 0; | |||||
| if (arraySize > 0) | |||||
| location.throwError ("Arrays not yet implemented!"); | |||||
| var constantInitialiser; | |||||
| if (isConst) | |||||
| constantInitialiser = parseConstantExpressionInitialiser (type); | |||||
| blockBeingParsed->addVariable ({ name, type, true, isConst, constantInitialiser }, location); | |||||
| if (matchIf (Token::comma)) | |||||
| { | |||||
| name = parseIdentifier(); | |||||
| continue; | |||||
| } | |||||
| match (Token::semicolon); | |||||
| break; | |||||
| } | |||||
| } | |||||
| var parseConstantExpressionInitialiser (Type expectedType) | |||||
| { | |||||
| var result; | |||||
| match (Token::assign); | |||||
| auto e = parseExpression(); | |||||
| if (auto literal = dynamic_cast<LiteralValue*> (e->simplify (*this))) | |||||
| { | |||||
| result = literal->value; | |||||
| if (getTypeOfVar (result) != expectedType) | |||||
| location.throwError ("Expected a constant expression of type " + getTypeName (expectedType)); | |||||
| } | |||||
| else | |||||
| { | |||||
| location.throwError ("Expected a constant expression"); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| void parseFunctionDeclaration (Type returnType, const String& name) | void parseFunctionDeclaration (Type returnType, const String& name) | ||||
| { | { | ||||
| auto f = allocate<Function>(); | auto f = allocate<Function>(); | ||||
| @@ -469,7 +510,7 @@ private: | |||||
| while (matchesAnyType()) | while (matchesAnyType()) | ||||
| { | { | ||||
| auto type = tokenToType (skip()); | auto type = tokenToType (skip()); | ||||
| f->arguments.add ({ parseIdentifier(), type }); | |||||
| f->arguments.add ({ parseIdentifier(), type, false, false, 0 }); | |||||
| if (f->arguments.size() > 127) | if (f->arguments.size() > 127) | ||||
| location.throwError ("Too many function arguments"); | location.throwError ("Too many function arguments"); | ||||
| @@ -547,7 +588,8 @@ private: | |||||
| if (matchIf (Token::plusplus)) return matchEndOfStatement (parsePreIncDec (Token::plus)); | if (matchIf (Token::plusplus)) return matchEndOfStatement (parsePreIncDec (Token::plus)); | ||||
| if (matchIf (Token::minusminus)) return matchEndOfStatement (parsePreIncDec (Token::minus)); | if (matchIf (Token::minusminus)) return matchEndOfStatement (parsePreIncDec (Token::minus)); | ||||
| if (matchesAny (Token::openParen)) return matchEndOfStatement (parseFactor()); | if (matchesAny (Token::openParen)) return matchEndOfStatement (parseFactor()); | ||||
| if (matchesAnyType()) return parseVariableDeclaration (tokenToType (skip())); | |||||
| if (matchIf (Token::const_)) return parseVariableDeclaration (true); | |||||
| if (matchesAnyType()) return parseVariableDeclaration (false); | |||||
| if (matchesAny (Token::identifier, Token::literal, Token::minus)) | if (matchesAny (Token::identifier, Token::literal, Token::minus)) | ||||
| return matchEndOfStatement (parseExpression()); | return matchEndOfStatement (parseExpression()); | ||||
| @@ -758,43 +800,57 @@ private: | |||||
| return returnStatement; | return returnStatement; | ||||
| } | } | ||||
| StatementPtr parseVariableDeclaration (Type type) | |||||
| StatementPtr parseVariableDeclaration (bool isConst) | |||||
| { | { | ||||
| if (isConst && ! matchesAnyType()) | |||||
| throwErrorExpecting ("a type"); | |||||
| auto type = tokenToType (skip()); | |||||
| for (StatementPtr result = nullptr;;) | for (StatementPtr result = nullptr;;) | ||||
| { | { | ||||
| auto name = parseIdentifier(); | auto name = parseIdentifier(); | ||||
| auto loc = location; | auto loc = location; | ||||
| blockBeingParsed->addVariable (name, type, loc); | |||||
| auto assignedValue = matchIf (Token::assign) ? parseExpression() : nullptr; | |||||
| if (auto literal = dynamic_cast<LiteralValue*> (assignedValue)) | |||||
| if (static_cast<double> (literal->value) == 0) | |||||
| assignedValue = nullptr; | |||||
| if (assignedValue != nullptr || ! blockBeingParsed->isMainBlockOfFunction) // no need to assign 0 for variables in the outer scope | |||||
| if (isConst) | |||||
| { | |||||
| auto constantValue = parseConstantExpressionInitialiser (type); | |||||
| blockBeingParsed->addVariable ({ name, type, false, true, constantValue }, loc); | |||||
| } | |||||
| else | |||||
| { | { | ||||
| if (assignedValue == nullptr) | |||||
| assignedValue = allocate<LiteralValue> (loc, blockBeingParsed, (int) 0); | |||||
| blockBeingParsed->addVariable ({ name, type, false, false, {} }, loc); | |||||
| auto assignment = allocate<Assignment> (loc, blockBeingParsed, name, assignedValue, false); | |||||
| auto assignedValue = matchIf (Token::assign) ? parseExpression() : nullptr; | |||||
| if (result == nullptr) | |||||
| { | |||||
| result = assignment; | |||||
| } | |||||
| else | |||||
| if (auto literal = dynamic_cast<LiteralValue*> (assignedValue)) | |||||
| if (static_cast<double> (literal->value) == 0) | |||||
| assignedValue = nullptr; | |||||
| if (assignedValue != nullptr || ! blockBeingParsed->isMainBlockOfFunction) // no need to assign 0 for variables in the outer scope | |||||
| { | { | ||||
| auto block = dynamic_cast<BlockPtr> (result); | |||||
| if (assignedValue == nullptr) | |||||
| assignedValue = allocate<LiteralValue> (loc, blockBeingParsed, (int) 0); | |||||
| if (block == nullptr) | |||||
| auto assignment = allocate<Assignment> (loc, blockBeingParsed, name, assignedValue, false); | |||||
| if (result == nullptr) | |||||
| { | { | ||||
| block = allocate<BlockStatement> (loc, blockBeingParsed, functions.getLast(), false); | |||||
| block->statements.add (result); | |||||
| result = block; | |||||
| result = assignment; | |||||
| } | } | ||||
| else | |||||
| { | |||||
| auto block = dynamic_cast<BlockPtr> (result); | |||||
| if (block == nullptr) | |||||
| { | |||||
| block = allocate<BlockStatement> (loc, blockBeingParsed, functions.getLast(), false); | |||||
| block->statements.add (result); | |||||
| result = block; | |||||
| } | |||||
| block->statements.add (assignment); | |||||
| block->statements.add (assignment); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -848,7 +904,7 @@ private: | |||||
| String parseIdentifier() | String parseIdentifier() | ||||
| { | { | ||||
| String name = currentValue.toString(); | |||||
| auto name = currentValue.toString(); | |||||
| match (Token::identifier); | match (Token::identifier); | ||||
| return name; | return name; | ||||
| } | } | ||||
| @@ -1104,12 +1160,15 @@ private: | |||||
| { | { | ||||
| Expression (const CodeLocation& l, BlockPtr parent) noexcept : Statement (l, parent) {} | Expression (const CodeLocation& l, BlockPtr parent) noexcept : Statement (l, parent) {} | ||||
| virtual Type getType (CodeGenerator&) const = 0; | virtual Type getType (CodeGenerator&) const = 0; | ||||
| virtual ExpPtr simplify (SyntaxTreeBuilder&) override { return this; } | |||||
| }; | }; | ||||
| struct Variable | struct Variable | ||||
| { | { | ||||
| String name; | String name; | ||||
| Type type; | Type type; | ||||
| bool isGlobal, isConst; | |||||
| var constantValue; | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -1266,38 +1325,43 @@ private: | |||||
| + parentBlock->variables.size()); | + parentBlock->variables.size()); | ||||
| } | } | ||||
| const Array<Variable>& getGlobalVariables() const noexcept | |||||
| { | |||||
| return parentBlock != nullptr ? parentBlock->getGlobalVariables() : variables; | |||||
| } | |||||
| const Array<Variable>& getGlobalVariables() const noexcept { return parentBlock != nullptr ? parentBlock->getGlobalVariables() : variables; } | |||||
| const Array<Variable>& getGlobalConstants() const noexcept { return parentBlock != nullptr ? parentBlock->getGlobalConstants() : constants; } | |||||
| Type getVariableType (const String& name, const CodeLocation& locationForError) const | |||||
| const Variable& getVariable (const String& name, const CodeLocation& locationForError) const | |||||
| { | { | ||||
| for (auto& v : constants) | |||||
| if (v.name == name) | |||||
| return v; | |||||
| for (auto& v : variables) | for (auto& v : variables) | ||||
| if (v.name == name) | if (v.name == name) | ||||
| return v.type; | |||||
| return v; | |||||
| if (! isMainBlockOfFunction) | if (! isMainBlockOfFunction) | ||||
| return parentBlock->getVariableType (name, locationForError); | |||||
| return parentBlock->getVariable (name, locationForError); | |||||
| for (auto& v : function->arguments) | for (auto& v : function->arguments) | ||||
| if (v.name == name) | if (v.name == name) | ||||
| return v.type; | |||||
| return v; | |||||
| for (auto& v : getGlobalConstants()) | |||||
| if (v.name == name) | |||||
| return v; | |||||
| for (auto& v : getGlobalVariables()) | for (auto& v : getGlobalVariables()) | ||||
| if (v.name == name) | if (v.name == name) | ||||
| return v.type; | |||||
| return v; | |||||
| locationForError.throwError ("Unknown variable '" + name + "'"); | locationForError.throwError ("Unknown variable '" + name + "'"); | ||||
| return {}; | |||||
| } | } | ||||
| void addVariable (const String& name, Type type, const CodeLocation& locationForError) | |||||
| void addVariable (Variable v, const CodeLocation& locationForError) | |||||
| { | { | ||||
| if (indexOf (variables, name) >= 0) | |||||
| locationForError.throwError ("Variable '" + name + "' already exists"); | |||||
| if (indexOf (variables, v.name) >= 0 || indexOf (constants, v.name) >= 0) | |||||
| locationForError.throwError ("Variable '" + v.name + "' already exists"); | |||||
| variables.add ({ name, type }); | |||||
| (v.isConst ? constants : variables).add (v); | |||||
| } | } | ||||
| static int indexOf (const Array<Variable>& vars, const String& name) noexcept | static int indexOf (const Array<Variable>& vars, const String& name) noexcept | ||||
| @@ -1311,7 +1375,7 @@ private: | |||||
| Function* function; | Function* function; | ||||
| Array<StatementPtr> statements; | Array<StatementPtr> statements; | ||||
| Array<Variable> variables; | |||||
| Array<Variable> variables, constants; | |||||
| bool isMainBlockOfFunction; | bool isMainBlockOfFunction; | ||||
| }; | }; | ||||
| @@ -1356,7 +1420,7 @@ private: | |||||
| Statement* simplify (SyntaxTreeBuilder& stb) override | Statement* simplify (SyntaxTreeBuilder& stb) override | ||||
| { | { | ||||
| condition = dynamic_cast<ExpPtr> (condition->simplify (stb)); | |||||
| condition = condition->simplify (stb); | |||||
| trueBranch = trueBranch->simplify (stb); | trueBranch = trueBranch->simplify (stb); | ||||
| falseBranch = falseBranch != nullptr ? falseBranch->simplify (stb) : nullptr; | falseBranch = falseBranch != nullptr ? falseBranch->simplify (stb) : nullptr; | ||||
| @@ -1402,11 +1466,11 @@ private: | |||||
| visit (condition); visit (trueBranch); visit (falseBranch); | visit (condition); visit (trueBranch); visit (falseBranch); | ||||
| } | } | ||||
| Statement* simplify (SyntaxTreeBuilder& stb) override | |||||
| ExpPtr simplify (SyntaxTreeBuilder& stb) override | |||||
| { | { | ||||
| condition = dynamic_cast<ExpPtr> (condition->simplify (stb)); | |||||
| trueBranch = dynamic_cast<ExpPtr> (trueBranch->simplify (stb)); | |||||
| falseBranch = dynamic_cast<ExpPtr> (falseBranch->simplify (stb)); | |||||
| condition = condition->simplify (stb); | |||||
| trueBranch = trueBranch->simplify (stb); | |||||
| falseBranch = falseBranch->simplify (stb); | |||||
| if (auto literal = dynamic_cast<LiteralValue*> (condition)) | if (auto literal = dynamic_cast<LiteralValue*> (condition)) | ||||
| return literal->value ? trueBranch : falseBranch; | return literal->value ? trueBranch : falseBranch; | ||||
| @@ -1569,7 +1633,17 @@ private: | |||||
| Type getType (CodeGenerator&) const override | Type getType (CodeGenerator&) const override | ||||
| { | { | ||||
| return parentBlock->getVariableType (name, location); | |||||
| return parentBlock->getVariable (name, location).type; | |||||
| } | |||||
| ExpPtr simplify (SyntaxTreeBuilder& stb) override | |||||
| { | |||||
| auto& v = parentBlock->getVariable (name, location); | |||||
| if (v.isConst) | |||||
| return stb.allocate<LiteralValue> (location, parentBlock, v.constantValue); | |||||
| return this; | |||||
| } | } | ||||
| String name; | String name; | ||||
| @@ -1626,9 +1700,9 @@ private: | |||||
| visit (source); | visit (source); | ||||
| } | } | ||||
| Statement* simplify (SyntaxTreeBuilder& stb) override | |||||
| ExpPtr simplify (SyntaxTreeBuilder& stb) override | |||||
| { | { | ||||
| source = dynamic_cast<ExpPtr> (source->simplify (stb)); | |||||
| source = source->simplify (stb); | |||||
| if (auto literal = dynamic_cast<LiteralValue*> (source)) | if (auto literal = dynamic_cast<LiteralValue*> (source)) | ||||
| { | { | ||||
| @@ -1745,7 +1819,7 @@ private: | |||||
| visit (lhs); visit (rhs); | visit (lhs); visit (rhs); | ||||
| } | } | ||||
| Statement* simplifyFloat (double a, double b, LiteralValue* literal) | |||||
| ExpPtr simplifyFloat (double a, double b, LiteralValue* literal) | |||||
| { | { | ||||
| if (operation == Token::plus) { literal->value = a + b; return literal; } | if (operation == Token::plus) { literal->value = a + b; return literal; } | ||||
| if (operation == Token::minus) { literal->value = a - b; return literal; } | if (operation == Token::minus) { literal->value = a - b; return literal; } | ||||
| @@ -1760,14 +1834,14 @@ private: | |||||
| return this; | return this; | ||||
| } | } | ||||
| Statement* simplifyBool (bool a, bool b, LiteralValue* literal) | |||||
| ExpPtr simplifyBool (bool a, bool b, LiteralValue* literal) | |||||
| { | { | ||||
| if (operation == Token::logicalOr) { literal->value = a || b; return literal; } | if (operation == Token::logicalOr) { literal->value = a || b; return literal; } | ||||
| if (operation == Token::logicalAnd) { literal->value = a && b; return literal; } | if (operation == Token::logicalAnd) { literal->value = a && b; return literal; } | ||||
| return this; | return this; | ||||
| } | } | ||||
| Statement* simplifyInt (int a, int b, LiteralValue* literal) | |||||
| ExpPtr simplifyInt (int a, int b, LiteralValue* literal) | |||||
| { | { | ||||
| if (operation == Token::plus) { literal->value = a + b; return literal; } | if (operation == Token::plus) { literal->value = a + b; return literal; } | ||||
| if (operation == Token::minus) { literal->value = a - b; return literal; } | if (operation == Token::minus) { literal->value = a - b; return literal; } | ||||
| @@ -1790,10 +1864,10 @@ private: | |||||
| return this; | return this; | ||||
| } | } | ||||
| Statement* simplify (SyntaxTreeBuilder& stb) override | |||||
| ExpPtr simplify (SyntaxTreeBuilder& stb) override | |||||
| { | { | ||||
| lhs = dynamic_cast<ExpPtr> (lhs->simplify (stb)); | |||||
| rhs = dynamic_cast<ExpPtr> (rhs->simplify (stb)); | |||||
| lhs = lhs->simplify (stb); | |||||
| rhs = rhs->simplify (stb); | |||||
| if (auto literal1 = dynamic_cast<LiteralValue*> (lhs)) | if (auto literal1 = dynamic_cast<LiteralValue*> (lhs)) | ||||
| { | { | ||||
| @@ -1858,7 +1932,7 @@ private: | |||||
| Type getType (CodeGenerator&) const override | Type getType (CodeGenerator&) const override | ||||
| { | { | ||||
| return parentBlock->getVariableType (target, location); | |||||
| return parentBlock->getVariable (target, location).type; | |||||
| } | } | ||||
| void visitSubStatements (Statement::Visitor& visit) const override | void visitSubStatements (Statement::Visitor& visit) const override | ||||
| @@ -1866,9 +1940,9 @@ private: | |||||
| visit (newValue); | visit (newValue); | ||||
| } | } | ||||
| Statement* simplify (SyntaxTreeBuilder& stb) override | |||||
| ExpPtr simplify (SyntaxTreeBuilder& stb) override | |||||
| { | { | ||||
| newValue = dynamic_cast<ExpPtr> (newValue->simplify (stb)); | |||||
| newValue = newValue->simplify (stb); | |||||
| return this; | return this; | ||||
| } | } | ||||
| @@ -2015,6 +2089,14 @@ private: | |||||
| visit (arg); | visit (arg); | ||||
| } | } | ||||
| ExpPtr simplify (SyntaxTreeBuilder& stb) override | |||||
| { | |||||
| for (auto& arg : arguments) | |||||
| arg = arg->simplify (stb); | |||||
| return this; | |||||
| } | |||||
| String functionName; | String functionName; | ||||
| Array<ExpPtr> arguments; | Array<ExpPtr> arguments; | ||||
| }; | }; | ||||
| @@ -2033,10 +2115,10 @@ private: | |||||
| visit (object); visit (index); | visit (object); visit (index); | ||||
| } | } | ||||
| Statement* simplify (SyntaxTreeBuilder& stb) override | |||||
| ExpPtr simplify (SyntaxTreeBuilder& stb) override | |||||
| { | { | ||||
| object = dynamic_cast<ExpPtr> (object->simplify (stb)); | |||||
| index = dynamic_cast<ExpPtr> (index->simplify (stb)); | |||||
| object = object->simplify (stb); | |||||
| index = index->simplify (stb); | |||||
| return this; | return this; | ||||
| } | } | ||||