| @@ -110,7 +110,7 @@ private: | |||
| X(if_, "if") X(else_, "else") X(do_, "do") \ | |||
| 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(return_, "return") X(true_, "true") X(false_, "false") | |||
| X(return_, "return") X(true_, "true") X(false_, "false") X(const_, "const") | |||
| #define LITTLEFOOT_OPERATORS(X) \ | |||
| 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 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; | |||
| @@ -205,7 +205,7 @@ private: | |||
| { | |||
| if (isIdentifierStart (*p)) | |||
| { | |||
| String::CharPointerType end (p); | |||
| auto end = p; | |||
| while (isIdentifierBody (*++end)) {} | |||
| const size_t len = (size_t) (end - p); | |||
| @@ -329,7 +329,7 @@ private: | |||
| bool parseOctalLiteral() | |||
| { | |||
| String::CharPointerType t (p); | |||
| auto t = p; | |||
| int64 v = *t - '0'; | |||
| if (v != 0) return false; // first digit of octal must be 0 | |||
| @@ -380,6 +380,8 @@ private: | |||
| continue; | |||
| } | |||
| const bool isConstVariable = matchIf (Token::const_); | |||
| if (! matchesAnyTypeOrVoid()) | |||
| throwErrorExpecting ("a global variable or function"); | |||
| @@ -388,6 +390,9 @@ private: | |||
| if (matchIf (Token::openParen)) | |||
| { | |||
| if (isConstVariable) | |||
| location.throwError ("Return type of a function cannot be const"); | |||
| parseFunctionDeclaration (type, name); | |||
| continue; | |||
| } | |||
| @@ -395,19 +400,7 @@ private: | |||
| if (type == Type::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"); | |||
| } | |||
| 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) | |||
| { | |||
| auto f = allocate<Function>(); | |||
| @@ -469,7 +510,7 @@ private: | |||
| while (matchesAnyType()) | |||
| { | |||
| auto type = tokenToType (skip()); | |||
| f->arguments.add ({ parseIdentifier(), type }); | |||
| f->arguments.add ({ parseIdentifier(), type, false, false, 0 }); | |||
| if (f->arguments.size() > 127) | |||
| location.throwError ("Too many function arguments"); | |||
| @@ -547,7 +588,8 @@ private: | |||
| if (matchIf (Token::plusplus)) return matchEndOfStatement (parsePreIncDec (Token::plus)); | |||
| if (matchIf (Token::minusminus)) return matchEndOfStatement (parsePreIncDec (Token::minus)); | |||
| 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)) | |||
| return matchEndOfStatement (parseExpression()); | |||
| @@ -758,43 +800,57 @@ private: | |||
| return returnStatement; | |||
| } | |||
| StatementPtr parseVariableDeclaration (Type type) | |||
| StatementPtr parseVariableDeclaration (bool isConst) | |||
| { | |||
| if (isConst && ! matchesAnyType()) | |||
| throwErrorExpecting ("a type"); | |||
| auto type = tokenToType (skip()); | |||
| for (StatementPtr result = nullptr;;) | |||
| { | |||
| auto name = parseIdentifier(); | |||
| 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 name = currentValue.toString(); | |||
| auto name = currentValue.toString(); | |||
| match (Token::identifier); | |||
| return name; | |||
| } | |||
| @@ -1104,12 +1160,15 @@ private: | |||
| { | |||
| Expression (const CodeLocation& l, BlockPtr parent) noexcept : Statement (l, parent) {} | |||
| virtual Type getType (CodeGenerator&) const = 0; | |||
| virtual ExpPtr simplify (SyntaxTreeBuilder&) override { return this; } | |||
| }; | |||
| struct Variable | |||
| { | |||
| String name; | |||
| Type type; | |||
| bool isGlobal, isConst; | |||
| var constantValue; | |||
| }; | |||
| //============================================================================== | |||
| @@ -1266,38 +1325,43 @@ private: | |||
| + 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) | |||
| if (v.name == name) | |||
| return v.type; | |||
| return v; | |||
| if (! isMainBlockOfFunction) | |||
| return parentBlock->getVariableType (name, locationForError); | |||
| return parentBlock->getVariable (name, locationForError); | |||
| for (auto& v : function->arguments) | |||
| if (v.name == name) | |||
| return v.type; | |||
| return v; | |||
| for (auto& v : getGlobalConstants()) | |||
| if (v.name == name) | |||
| return v; | |||
| for (auto& v : getGlobalVariables()) | |||
| if (v.name == name) | |||
| return v.type; | |||
| return v; | |||
| 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 | |||
| @@ -1311,7 +1375,7 @@ private: | |||
| Function* function; | |||
| Array<StatementPtr> statements; | |||
| Array<Variable> variables; | |||
| Array<Variable> variables, constants; | |||
| bool isMainBlockOfFunction; | |||
| }; | |||
| @@ -1356,7 +1420,7 @@ private: | |||
| Statement* simplify (SyntaxTreeBuilder& stb) override | |||
| { | |||
| condition = dynamic_cast<ExpPtr> (condition->simplify (stb)); | |||
| condition = condition->simplify (stb); | |||
| trueBranch = trueBranch->simplify (stb); | |||
| falseBranch = falseBranch != nullptr ? falseBranch->simplify (stb) : nullptr; | |||
| @@ -1402,11 +1466,11 @@ private: | |||
| 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)) | |||
| return literal->value ? trueBranch : falseBranch; | |||
| @@ -1569,7 +1633,17 @@ private: | |||
| 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; | |||
| @@ -1626,9 +1700,9 @@ private: | |||
| 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)) | |||
| { | |||
| @@ -1745,7 +1819,7 @@ private: | |||
| 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::minus) { literal->value = a - b; return literal; } | |||
| @@ -1760,14 +1834,14 @@ private: | |||
| 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::logicalAnd) { literal->value = a && b; return literal; } | |||
| 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::minus) { literal->value = a - b; return literal; } | |||
| @@ -1790,10 +1864,10 @@ private: | |||
| 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)) | |||
| { | |||
| @@ -1858,7 +1932,7 @@ private: | |||
| Type getType (CodeGenerator&) const override | |||
| { | |||
| return parentBlock->getVariableType (target, location); | |||
| return parentBlock->getVariable (target, location).type; | |||
| } | |||
| void visitSubStatements (Statement::Visitor& visit) const override | |||
| @@ -1866,9 +1940,9 @@ private: | |||
| 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; | |||
| } | |||
| @@ -2015,6 +2089,14 @@ private: | |||
| visit (arg); | |||
| } | |||
| ExpPtr simplify (SyntaxTreeBuilder& stb) override | |||
| { | |||
| for (auto& arg : arguments) | |||
| arg = arg->simplify (stb); | |||
| return this; | |||
| } | |||
| String functionName; | |||
| Array<ExpPtr> arguments; | |||
| }; | |||
| @@ -2033,10 +2115,10 @@ private: | |||
| 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; | |||
| } | |||