diff --git a/src/ast.cpp b/src/ast.cpp index 7039c07..3c5faec 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -45,6 +45,10 @@ Expression &FunctionCallExpression::getFunction() { return *function; } +void FunctionCallExpression::addFirstArgument(std::unique_ptr argument) { + arguments.insert(arguments.begin(), std::move(argument)); +} + void FunctionCallExpression::forEachArgument( const std::function &argumentHandler) { for (auto &argument : arguments) { @@ -52,6 +56,14 @@ void FunctionCallExpression::forEachArgument( } } +void FunctionCallExpression::setVariant(FunctionVariant variant) { + this->variant = variant; +} + +FunctionVariant FunctionCallExpression::getVariant() const { + return variant; +} + void FunctionCallExpression::accept(ASTVisitor &visitor) { visitor.visit(*this); } @@ -174,6 +186,14 @@ int BlockExpression::getSymbolType(std::string_view symbol) { return std::get<0>(symbols[symbol]); } +void BlockExpression::setSymbolType(std::string_view symbol, int typeID) { + if (symbols.find(symbol) == symbols.end()) { + std::cerr << "Symbol not found"; + exit(EXIT_FAILURE); + } + std::get<0>(symbols[symbol]) = typeID; +} + SymbolSource BlockExpression::getSymbolSource(std::string_view symbol) { if (symbols.find(symbol) == symbols.end()) { return SymbolSource::Unknown; @@ -266,6 +286,40 @@ IfElseExpression::IfElseExpression(std::unique_ptr condition, thenBlock(std::move(thenBlock)), elseExpression(std::move(elseExpression)) { } +PathExpression::PathExpression(std::vector> path) + : Expression(Slice::merge(path[0]->getSlice(), path.back()->getSlice())), + path(std::move(path)) { +} + +void PathExpression::forEachSymbol( + const std::function &symbolHandler) { + for (auto &symbol : path) { + symbolHandler(*symbol); + } +} + +void PathExpression::accept(ASTVisitor &visitor) { + visitor.visit(*this); +} + +FieldAccessExpression::FieldAccessExpression(std::unique_ptr object, + std::unique_ptr field) + : Expression(Slice::merge(object->getSlice(), field->getSlice())), + object(std::move(object)), field(std::move(field)) { +} + +Expression &FieldAccessExpression::getObject() { + return *object; +} + +Expression &FieldAccessExpression::getField() { + return *field; +} + +void FieldAccessExpression::accept(ASTVisitor &visitor) { + visitor.visit(*this); +} + WhileExpression::WhileExpression(const Keyword &whileKeyword, std::unique_ptr condition, std::unique_ptr body) : Expression(Slice::merge(whileKeyword.s, body->getSlice())), @@ -333,6 +387,12 @@ LetStatement::LetStatement(const Keyword &let, std::unique_ptr symbol, equalSign(std::move(equalSign)), expression(std::move(expression)) { } +LetStatement::LetStatement(std::unique_ptr symbol, + std::unique_ptr typeAnnotation, Punctuation *semicolon) + : Statement(Slice::merge(symbol->s, semicolon->s)), symbol(std::move(symbol)), + typeAnnotation(std::move(typeAnnotation)), isFieldDeclaration(true) { +} + LetStatement::LetStatement(std::unique_ptr symbol, std::unique_ptr expression) : Statement(Slice("", "", 0, 0)), symbol(std::move(symbol)), typeAnnotation(nullptr), @@ -363,22 +423,28 @@ int LetStatement::getSymbolTypeID() const { return symbolTypeID; } +bool LetStatement::getIsFieldDeclaration() const { + return isFieldDeclaration; +} + void LetStatement::accept(ASTVisitor &visitor) { visitor.visit(*this); } Function::Function( std::vector, std::unique_ptr>> parameters, - std::unique_ptr returnTypeAnnotation, std::unique_ptr body) + std::unique_ptr returnTypeAnnotation, std::unique_ptr body, + FunctionVariant variant) : parameters(std::move(parameters)), - returnTypeAnnotation(std::move(returnTypeAnnotation)), body(std::move(body)) { + returnTypeAnnotation(std::move(returnTypeAnnotation)), body(std::move(body)), + variant(variant) { } Function::Function( std::vector, std::unique_ptr>> parameters, - std::unique_ptr body) + std::unique_ptr body, FunctionVariant variant) : parameters(std::move(parameters)), returnTypeAnnotation(nullptr), - body(std::move(body)) { + body(std::move(body)), variant(variant) { } void Function::forEachParameter( @@ -404,27 +470,79 @@ void Function::setTypeID(int typeID) { this->typeID = typeID; } +void Function::setVariant(FunctionVariant variant) { + this->variant = variant; +} + +FunctionVariant Function::getVariant() const { + return variant; +} + void Function::accept(ASTVisitor &visitor) { visitor.visit(*this); } -Type::Type(int id, int parentID, std::string_view name) - : id(id), parentID(parentID), name(name) { +Class::Class(std::vector> fieldDeclarations) + : fieldDeclarations(std::move(fieldDeclarations)) { +} + +Class::Class(std::unique_ptr scope) : scope(std::move(scope)) { +} + +void Class::forEachFieldDeclaration( + const std::function &fieldHandler) { + for (auto &field : fieldDeclarations) { + fieldHandler(*field); + } +} + +BlockExpression &Class::getScope() { + return *scope; +} + +void Class::accept(ASTVisitor &visitor) { + visitor.visit(*this); +} + +Impl::Impl(std::unordered_map> methods) + : methods(std::move(methods)) { +} + +void Impl::forEachMethod( + const std::function &methodHandler) { + for (auto &[name, method] : methods) { + methodHandler(name, *method); + } +} + +Function *Impl::getMethod(std::string_view name) { + if (methods.find(name) == methods.end()) { + return nullptr; + } + return methods[name].get(); +} + +void Impl::accept(ASTVisitor &visitor) { + visitor.visit(*this); +} + +Type::Type(int id, int parentID, std::string_view name, bool isClass) + : id(id), parentID(parentID), isClass(isClass), name(name) { } Module::Module(std::filesystem::path source) : source(std::move(source)) { - insertType("()"); - insertType("!"); - insertType("i8"); - insertType("i16"); - insertType("i32"); - insertType("i64"); - insertType("u8"); - insertType("u16"); - insertType("u32"); - insertType("u64"); - insertType("bool"); - insertType("char"); + insertType("()", false); + insertType("!", false); + insertType("i8", false); + insertType("i16", false); + insertType("i32", false); + insertType("i64", false); + insertType("u8", false); + insertType("u16", false); + insertType("u32", false); + insertType("u64", false); + insertType("bool", false); + insertType("char", false); } Module::Module(const Module &module) @@ -445,25 +563,50 @@ void Module::forEachFunction( } } +void Module::addClass(std::unique_ptr name, std::unique_ptr cls, + bool isBuiltin) { + classes[name->s.contents] = {std::move(cls), isBuiltin}; +} + +void Module::addImpl(std::unique_ptr className, std::unique_ptr impl, + bool isBuiltin) { + impls[className->s.contents] = {std::move(impl), isBuiltin}; +} + +void Module::forEachClass( + const std::function &classHandler) { + for (auto &[name, cls] : classes) { + classHandler(name, *std::get<0>(cls), std::get<1>(cls)); + } +} + +void Module::forEachImpl( + const std::function &implHandler) { + for (auto &[name, impl] : impls) { + implHandler(name, *std::get<0>(impl), std::get<1>(impl)); + } +} + Type Module::getType(std::string_view typeName) { if (typeTableByName.find(typeName) == typeTableByName.end()) { - return Type(-1, -1, ""); + return Type(-1, -1, "", false); } return typeTableByName.at(typeName); } Type Module::getType(int id) { if (typeTableByID.find(id) == typeTableByID.end()) { - return Type(-1, -1, ""); + return Type(-1, -1, "", false); } return typeTableByID.at(id); } -void Module::insertType(std::string_view typeName) { +void Module::insertType(std::string_view typeName, bool isClass) { if (typeTableByName.find(typeName) == typeTableByName.end()) { - typeTableByName.insert({typeName, Type(typeTableByName.size(), -1, typeName)}); + typeTableByName.insert( + {typeName, Type(typeTableByName.size(), -1, typeName, isClass)}); typeTableByID.insert( - {typeTableByID.size(), Type(typeTableByID.size(), -1, typeName)}); + {typeTableByID.size(), Type(typeTableByID.size(), -1, typeName, isClass)}); } else { std::cerr << "Type already exists"; exit(EXIT_FAILURE); @@ -490,7 +633,7 @@ Type Module::getCommonTypeAncestor(int type1, int type2) { if (type2 == getType("!").id) { return getType(type1); } - return Type(-1, -1, ""); + return Type(-1, -1, "", false); } void Module::addUnaryOperator(Operator::Type op, int operandType, int resultType) { @@ -539,6 +682,33 @@ Function *Module::getFunction(std::string_view name) { return std::get<0>(functions[name]).get(); } +Impl *Module::getImpl(std::string_view name) { + if (impls.find(name) == impls.end()) { + return nullptr; + } + return std::get<0>(impls[name]).get(); +} + +std::pair Module::getImpl(int typeID) { + for (auto &[name, impl] : impls) { + int implTypeID = getType(name).id; + if (implTypeID == typeID) { + return {name, std::get<0>(impl).get()}; + } + } + return {{}, nullptr}; +} + +std::pair Module::getClass(int typeID) { + for (auto &[name, cls] : classes) { + int implTypeID = getType(name).id; + if (implTypeID == typeID) { + return {name, std::get<0>(cls).get()}; + } + } + return {{}, nullptr}; +} + std::filesystem::path Module::getSource() { return source; } @@ -619,6 +789,23 @@ void ASTPrinter::visit(ParenthesizedExpression &node) { node.getExpression().accept(*this); } +void ASTPrinter::visit(PathExpression &node) { + bool first = true; + node.forEachSymbol([this, &first](SymbolExpression &symbol) { + symbol.accept(*this); + if (!first) { + std::cerr << "::"; + } + first = false; + }); +} + +void ASTPrinter::visit(FieldAccessExpression &node) { + node.getObject().accept(*this); + std::cerr << '.'; + node.getField().accept(*this); +} + void ASTPrinter::visit(IfElseExpression &node) { std::cerr << "if "; node.getCondition().accept(*this); @@ -670,6 +857,12 @@ void ASTPrinter::visit(LetStatement &node) { void ASTPrinter::visit([[maybe_unused]] Function &node) { } +void ASTPrinter::visit([[maybe_unused]] Class &node) { +} + +void ASTPrinter::visit([[maybe_unused]] Impl &node) { +} + void ASTPrinter::visit(Module &node) { node.forEachFunction( [this](std::string_view name, Function &function, bool /*unused*/) { @@ -678,4 +871,5 @@ void ASTPrinter::visit(Module &node) { function.getBody().accept(*this); std::cerr << '\n'; }); + // TODO classes } diff --git a/src/ast.h b/src/ast.h index bbc4741..f27a70e 100644 --- a/src/ast.h +++ b/src/ast.h @@ -43,16 +43,26 @@ class Statement : public ASTComponent { virtual ~Statement() = default; }; +enum class FunctionVariant { + FUNCTION, + METHOD, + CONSTRUCTOR, +}; + class FunctionCallExpression : public Expression { std::unique_ptr function; std::vector> arguments; + FunctionVariant variant; public: FunctionCallExpression(std::unique_ptr function, const Punctuation &open, std::vector> arguments, const Punctuation &close); FunctionCallExpression(std::unique_ptr function, std::vector> arguments); Expression &getFunction(); + void addFirstArgument(std::unique_ptr argument); void forEachArgument(const std::function &argumentHandler); + void setVariant(FunctionVariant variant); + FunctionVariant getVariant() const; void accept(ASTVisitor &visitor) override; virtual ~FunctionCallExpression() = default; }; @@ -70,7 +80,6 @@ class BinaryExpression : public Expression { Operator &getOperator(); void accept(ASTVisitor &visitor) override; virtual ~BinaryExpression() = default; - friend class ASTVisitor; }; class UnaryExpression : public Expression { @@ -129,10 +138,12 @@ enum class SymbolSource { Unknown, LetStatement, FunctionParameter, + FieldDeclaration, GENERATED_Block, GENERATED_IfElse, GENERATED_While, GENERATED_Argument, + GENERATED_Temporary, }; class BlockExpression : public Expression { @@ -148,6 +159,7 @@ class BlockExpression : public Expression { void forEachStatement(const std::function &statementHandler); Expression *getFinalExpression(); int getSymbolType(std::string_view symbol); + void setSymbolType(std::string_view symbol, int typeID); SymbolSource getSymbolSource(std::string_view symbol); void pushSymbol(std::string_view symbol, int typeID, SymbolSource source); void forEachSymbol( @@ -180,6 +192,29 @@ class ParenthesizedExpression : public Expression { virtual ~ParenthesizedExpression() = default; }; +class PathExpression : public Expression { +private: + std::vector> path; +public: + PathExpression(std::vector> path); + void forEachSymbol(const std::function &symbolHandler); + void accept(ASTVisitor &visitor) override; + virtual ~PathExpression() = default; +}; + +class FieldAccessExpression : public Expression { +private: + std::unique_ptr object; + std::unique_ptr field; +public: + FieldAccessExpression(std::unique_ptr object, + std::unique_ptr field); + Expression &getObject(); + Expression &getField(); + void accept(ASTVisitor &visitor) override; + virtual ~FieldAccessExpression() = default; +}; + class IfElseExpression : public Expression { private: std::unique_ptr condition; @@ -234,11 +269,13 @@ class LetStatement : public Statement { std::unique_ptr typeAnnotation; std::unique_ptr equalSign; std::unique_ptr expression; + bool isFieldDeclaration = false; int symbolTypeID = -1; public: LetStatement(const Keyword &let, std::unique_ptr symbol, std::unique_ptr typeAnnotation, std::unique_ptr equalSign, std::unique_ptr expression, Punctuation *semicolon); + LetStatement(std::unique_ptr symbol, std::unique_ptr typeAnnotation, Punctuation *semicolon); LetStatement(std::unique_ptr symbol, std::unique_ptr expression); Symbol &getSymbol(); Expression *getExpression(); @@ -246,6 +283,7 @@ class LetStatement : public Statement { Operator &getEqualSign(); void setSymbolTypeID(int typeID); int getSymbolTypeID() const; + bool getIsFieldDeclaration() const; void accept(ASTVisitor &visitor) override; virtual ~LetStatement() = default; }; @@ -255,30 +293,59 @@ class Function : public ASTComponent { std::vector, std::unique_ptr>> parameters; std::unique_ptr returnTypeAnnotation; std::unique_ptr body; + FunctionVariant variant; int typeID = -1; public: Function(std::vector, std::unique_ptr>> parameters, std::unique_ptr returnTypeAnnotation, - std::unique_ptr body); + std::unique_ptr body, FunctionVariant variant); Function(std::vector, std::unique_ptr>> parameters, - std::unique_ptr body); + std::unique_ptr body, FunctionVariant variant); void forEachParameter( const std::function ¶meterHandler); Symbol *getReturnTypeAnnotation(); BlockExpression &getBody(); int getTypeID() const; void setTypeID(int typeID); + void setVariant(FunctionVariant variant); + FunctionVariant getVariant() const; void accept(ASTVisitor &visitor); ~Function() = default; }; +class Class : public ASTComponent { +private: + std::vector> fieldDeclarations; + std::unique_ptr scope = std::make_unique(); +public: + Class(std::vector> fieldDeclarations); + Class(std::unique_ptr scope); + void forEachFieldDeclaration(const std::function &fieldHandler); + BlockExpression &getScope(); + void accept(ASTVisitor &visitor); + ~Class() = default; +}; + +class Impl : public ASTComponent { +private: + std::unordered_map> methods; +public: + Impl(std::unordered_map> methods); + void forEachMethod( + const std::function &methodHandler); + Function *getMethod(std::string_view name); + void accept(ASTVisitor &visitor); + ~Impl() = default; +}; + struct Type { int id; int parentID; + bool isClass; std::string_view name; - Type(int id, int parentID, std::string_view name); + Type(int id, int parentID, std::string_view name, bool isClass); Type(const Type &) = default; }; @@ -292,6 +359,9 @@ class Module : public ASTComponent { std::unordered_map>> binaryOperators; std::filesystem::path source; + std::unordered_map, bool>> + classes; + std::unordered_map, bool>> impls; public: std::list ownedStrings; explicit Module(std::filesystem::path source); @@ -300,9 +370,17 @@ class Module : public ASTComponent { bool isBuiltin = false); void forEachFunction( const std::function &functionHandler); + void addClass(std::unique_ptr name, std::unique_ptr cls, + bool isBuiltin = false); + void addImpl(std::unique_ptr className, std::unique_ptr impl, + bool isBuiltin = false); + void forEachClass( + const std::function &classHandler); + void forEachImpl( + const std::function &implHandler); Type getType(std::string_view typeName); Type getType(int id); - void insertType(std::string_view typeName); + void insertType(std::string_view typeName, bool isClass); bool isTypeConvertible(int from, int to); Type getCommonTypeAncestor(int type1, int type2); void addUnaryOperator(Operator::Type op, int operandType, int resultType); @@ -311,6 +389,9 @@ class Module : public ASTComponent { int resultType); int getBinaryOperator(Operator::Type op, int leftType, int rightType); Function *getFunction(std::string_view name); + Impl *getImpl(std::string_view name); + std::pair getImpl(int typeID); + std::pair getClass(int typeID); std::filesystem::path getSource(); void accept(ASTVisitor &visitor); ~Module() = default; @@ -328,11 +409,15 @@ class ASTVisitor { virtual void visit(BlockExpression &node) = 0; virtual void visit(ReturnExpression &node) = 0; virtual void visit(ParenthesizedExpression &node) = 0; + virtual void visit(PathExpression &node) = 0; + virtual void visit(FieldAccessExpression &node) = 0; virtual void visit(IfElseExpression &node) = 0; virtual void visit(WhileExpression &node) = 0; virtual void visit(ExpressionStatement &node) = 0; virtual void visit(LetStatement &node) = 0; virtual void visit(Function &node) = 0; + virtual void visit(Class &node) = 0; + virtual void visit(Impl &node) = 0; virtual void visit(Module &node) = 0; virtual ~ASTVisitor() = default; }; @@ -350,11 +435,15 @@ class ASTPrinter : public ASTVisitor { void visit(BlockExpression &node) override; void visit(ReturnExpression &node) override; void visit(ParenthesizedExpression &node) override; + void visit(PathExpression &node) override; + void visit(FieldAccessExpression &node) override; void visit(IfElseExpression &node) override; void visit(WhileExpression &node) override; void visit(ExpressionStatement &node) override; void visit(LetStatement &node) override; void visit(Function &node) override; + void visit(Class &node) override; + void visit(Impl &node) override; void visit(Module &node) override; virtual ~ASTPrinter() = default; }; diff --git a/src/ccodeadapter.cpp b/src/ccodeadapter.cpp index fb1d276..56b3943 100644 --- a/src/ccodeadapter.cpp +++ b/src/ccodeadapter.cpp @@ -21,17 +21,85 @@ std::unique_ptr CCodeAdapter::transform() { void CCodeAdapter::visit(FunctionCallExpression &node) { Expression &oldFunction = node.getFunction(); auto *oldSymbol = dynamic_cast(&oldFunction); - if (oldSymbol == nullptr) { - std::cerr << "Function call target is not a symbol" << std::endl; + auto *oldPath = dynamic_cast(&oldFunction); + auto *oldFieldAccess = dynamic_cast(&oldFunction); + if (oldSymbol == nullptr && oldPath == nullptr && oldFieldAccess == nullptr) { + std::cerr << "Function call target is not a symbol, path, or method" << std::endl; exit(EXIT_FAILURE); } - generatedStrings->push_back( - "CANYON_FUNCTION_" + std::string(oldSymbol->getSymbol().s.contents)); - std::string_view newName = generatedStrings->back(); - std::unique_ptr newSymbol - = std::make_unique(Slice(newName, inputModule->getSource(), 0, 0)); - std::unique_ptr newSymbolExpression - = std::make_unique(std::move(newSymbol)); + std::unique_ptr newWhichFunction = nullptr; + std::string_view constructedObjectName = ""; + if (oldSymbol != nullptr) { + generatedStrings->push_back( + "CANYON_FUNCTION_" + std::string(oldSymbol->getSymbol().s.contents)); + std::string_view newName = generatedStrings->back(); + std::unique_ptr newSymbol + = std::make_unique(Slice(newName, inputModule->getSource(), 0, 0)); + newWhichFunction = std::make_unique(std::move(newSymbol)); + } else if (oldPath != nullptr) { + if (node.getVariant() == FunctionVariant::CONSTRUCTOR) { + std::unique_ptr mallocSymbol + = std::make_unique(std::make_unique( + Slice("malloc", inputModule->getSource(), 0, 0))); + std::vector> mallocArguments; + + std::string_view className = ""; + inputModule->forEachClass([this, &node, &className](std::string_view name, + Class & /*unused*/, bool /*unused*/) { + int classTypeID = inputModule->getType(std::string(name)).id; + if (classTypeID == node.getTypeID()) { + className = name; + } + }); + generatedStrings->push_back( + "sizeof(struct CANYON_CLASS_" + std::string(className) + ")"); + std::unique_ptr mallocArgumentSymbol = std::make_unique( + Slice(generatedStrings->back(), inputModule->getSource(), 0, 0)); + mallocArguments.push_back( + std::make_unique(std::move(mallocArgumentSymbol))); + std::unique_ptr mallocFunctionCall + = std::make_unique(std::move(mallocSymbol), + std::move(mallocArguments)); + + generatedStrings->push_back("CANYON_TEMP_" + std::to_string(blockCount++)); + blockTemporaryVariables.push(generatedStrings->back()); + constructedObjectName = generatedStrings->back(); + std::unique_ptr instanceSymbol = std::make_unique( + Slice(generatedStrings->back(), inputModule->getSource(), 0, 0)); + + std::unique_ptr mallocLetStatement + = std::make_unique(std::move(instanceSymbol), + std::move(mallocFunctionCall)); + scopeStack.back()->pushSymbol(generatedStrings->back(), node.getTypeID(), + SymbolSource::GENERATED_Temporary); + mallocLetStatement->setSymbolTypeID(node.getTypeID()); + scopeStack.back()->pushStatement(std::move(mallocLetStatement)); + + std::unique_ptr arg + = std::make_unique(std::make_unique( + Slice(constructedObjectName, inputModule->getSource(), 0, 0))); + arg->setTypeID(node.getTypeID()); + node.addFirstArgument(std::move(arg)); + } + std::string functionName; + bool first = true; + oldPath->forEachSymbol([&functionName, &first](SymbolExpression &symbol) { + functionName += symbol.getSymbol().s.contents; + if (first) { + functionName += "_METHOD_"; + } + first = false; + }); + generatedStrings->push_back("CANYON_IMPL_" + functionName); + std::string_view newName = generatedStrings->back(); + std::unique_ptr newSymbol + = std::make_unique(Slice(newName, inputModule->getSource(), 0, 0)); + newWhichFunction = std::make_unique(std::move(newSymbol)); + } else { + oldFieldAccess->accept(*this); + newWhichFunction = std::unique_ptr( + dynamic_cast(returnValue.release())); + } std::vector> newArguments; node.forEachArgument([this, &newArguments](Expression &argument) { @@ -52,10 +120,24 @@ void CCodeAdapter::visit(FunctionCallExpression &node) { }); std::unique_ptr newFunctionCall - = std::make_unique(std::move(newSymbolExpression), + = std::make_unique(std::move(newWhichFunction), std::move(newArguments)); - newFunctionCall->setTypeID(oldFunction.getTypeID()); - returnValue = std::move(newFunctionCall); + newFunctionCall->setTypeID(node.getTypeID()); + newFunctionCall->setVariant(node.getVariant()); + + if (node.getVariant() == FunctionVariant::CONSTRUCTOR) { + std::unique_ptr constructorStatement + = std::make_unique(std::move(newFunctionCall)); + scopeStack.back()->pushStatement(std::move(constructorStatement)); + std::unique_ptr instanceSymbol = std::make_unique( + Slice(constructedObjectName, inputModule->getSource(), 0, 0)); + std::unique_ptr instance + = std::make_unique(std::move(instanceSymbol)); + instance->setTypeID(node.getTypeID()); + returnValue = std::move(instance); + } else { + returnValue = std::move(newFunctionCall); + } } void CCodeAdapter::visit(BinaryExpression &node) { @@ -132,8 +214,10 @@ void CCodeAdapter::visit(SymbolExpression &node) { if (source == SymbolSource::FunctionParameter) { generatedStrings->push_back( "CANYON_PARAMETER_" + std::string(oldSymbol.s.contents)); - } else { + } else if (source != SymbolSource::GENERATED_Temporary) { generatedStrings->push_back("CANYON_LOCAL_" + std::string(oldSymbol.s.contents)); + } else { + generatedStrings->emplace_back(oldSymbol.s.contents); } std::string_view newName = generatedStrings->back(); std::unique_ptr newSymbol @@ -221,6 +305,63 @@ void CCodeAdapter::visit(ParenthesizedExpression &node) { returnValue = std::move(newParenthesizedExpression); } +void CCodeAdapter::visit([[maybe_unused]] PathExpression &node) { + // TODO +} + +void CCodeAdapter::visit(FieldAccessExpression &node) { + node.getObject().accept(*this); + std::unique_ptr newObject = std::unique_ptr( + dynamic_cast(returnValue.release())); + // don't want to visit the field because otherwise it will get mangled; instead just + // make a new one with the same name + SymbolExpression *field = dynamic_cast(&node.getField()); + FunctionCallExpression *method + = dynamic_cast(&node.getField()); + if (field == nullptr && method == nullptr) { + std::cerr << "Field access target is not a symbol or method call" << std::endl; + exit(EXIT_FAILURE); + } + if (field != nullptr) { + generatedStrings->push_back(std::string(field->getSlice().contents)); + std::unique_ptr newField + = std::make_unique(std::make_unique( + Slice(generatedStrings->back(), inputModule->getSource(), 0, 0))); + std::unique_ptr newFieldAccessExpression + = std::make_unique(std::move(newObject), + std::move(newField)); + newFieldAccessExpression->setTypeID(node.getTypeID()); + returnValue = std::move(newFieldAccessExpression); + } else { + std::vector> newArguments; + generatedStrings->push_back( + std::string(newObject->getSlice().contents) + ".object"); + std::unique_ptr self + = std::make_unique(std::make_unique( + Slice(generatedStrings->back(), inputModule->getSource(), 0, 0))); + newArguments.push_back(std::move(self)); + method->forEachArgument([this, &newArguments](Expression &argument) { + argument.accept(*this); + newArguments.push_back(std::unique_ptr( + dynamic_cast(returnValue.release()))); + }); + generatedStrings->push_back( + std::string(method->getFunction().getSlice().contents)); + std::unique_ptr newMethod + = std::make_unique(std::make_unique( + Slice(generatedStrings->back(), inputModule->getSource(), 0, 0))); + newMethod->setTypeID(node.getTypeID()); + std::unique_ptr newMethodCall + = std::make_unique(std::move(newMethod), + std::move(newArguments)); + std::unique_ptr newFieldAccessExpression + = std::make_unique(std::move(newObject), + std::move(newMethodCall)); + newFieldAccessExpression->setTypeID(node.getTypeID()); + returnValue = std::move(newFieldAccessExpression); + } +} + void CCodeAdapter::visit(IfElseExpression &node) { if (node.getTypeID() != inputModule->getType("()").id && node.getTypeID() != inputModule->getType("!").id) { @@ -420,15 +561,22 @@ void CCodeAdapter::visit(LetStatement &node) { void CCodeAdapter::visit(Function &node) { std::vector, std::unique_ptr>> newParameters; - node.forEachParameter([this, &newParameters](Symbol ¶meter, Symbol &type) { - generatedStrings->push_back( - "CANYON_PARAMETER_" + std::string(parameter.s.contents)); - std::string_view newParameterName = generatedStrings->back(); - std::unique_ptr newParameter = std::make_unique( - Slice(newParameterName, inputModule->getSource(), 0, 0)); - std::unique_ptr newType = std::make_unique(type); - newParameters.emplace_back(std::move(newParameter), std::move(newType)); - }); + size_t i = 0; + int typeId = -1; + node.forEachParameter( + [this, &newParameters, &i, &node, &typeId](Symbol ¶meter, Symbol &type) { + generatedStrings->push_back( + "CANYON_PARAMETER_" + std::string(parameter.s.contents)); + std::string_view newParameterName = generatedStrings->back(); + std::unique_ptr newParameter = std::make_unique( + Slice(newParameterName, inputModule->getSource(), 0, 0)); + std::unique_ptr newType = std::make_unique(type); + newParameters.emplace_back(std::move(newParameter), std::move(newType)); + if (node.getVariant() == FunctionVariant::CONSTRUCTOR && i == 0) { + typeId = node.getBody().getSymbolType(parameter.s.contents); + } + i++; + }); Expression &oldBody = node.getBody(); std::unique_ptr enclosingScope = std::make_unique(); @@ -440,23 +588,92 @@ void CCodeAdapter::visit(Function &node) { std::unique_ptr bodyStatement = std::make_unique(std::move(newBody)); enclosingScope->pushStatement(std::move(bodyStatement)); - returnValue = std::make_unique(std::move(newParameters), - std::move(enclosingScope)); + + if (node.getVariant() == FunctionVariant::CONSTRUCTOR) { + std::unique_ptr selfParameter = std::make_unique( + Slice("CANYON_PARAMETER_self", inputModule->getSource(), 0, 0)); + std::unique_ptr selfSymbolExpression + = std::make_unique(std::move(selfParameter)); + std::unique_ptr returnExpression + = std::make_unique(std::move(selfSymbolExpression)); + std::unique_ptr returnStatement + = std::make_unique(std::move(returnExpression)); + enclosingScope->pushStatement(std::move(returnStatement)); + } + + std::unique_ptr newFunction = std::make_unique( + std::move(newParameters), std::move(enclosingScope), node.getVariant()); + if (node.getVariant() == FunctionVariant::CONSTRUCTOR) { + newFunction->setTypeID(typeId); + } + returnValue = std::move(newFunction); +} + +void CCodeAdapter::visit(Class &node) { + std::unique_ptr newScope = std::make_unique(); + node.getScope().forEachSymbol( + [this, &newScope](std::string_view name, int typeID, SymbolSource source) { + generatedStrings->push_back(std::string(name)); + std::string_view newName = generatedStrings->back(); + std::unique_ptr newSymbol = std::make_unique( + Slice(newName, inputModule->getSource(), 0, 0)); + newScope->pushSymbol(newName, typeID, source); + }); + returnValue = std::make_unique(std::move(newScope)); +} + +void CCodeAdapter::visit(Impl &node) { + std::unordered_map> newMethods; + node.forEachMethod([this, &newMethods](std::string_view name, Function &method) { + generatedStrings->push_back(std::string(name)); + std::string_view newName = generatedStrings->back(); + method.accept(*this); + std::unique_ptr newMethod = std::unique_ptr( + dynamic_cast(returnValue.release())); + if (method.getVariant() != FunctionVariant::CONSTRUCTOR) { + newMethod->setTypeID(method.getTypeID()); + } + newMethods.emplace(newName, std::move(newMethod)); + }); + returnValue = std::make_unique(std::move(newMethods)); } void CCodeAdapter::visit(Module &node) { node.forEachFunction([this](std::string_view name, Function &oldFunction, bool isBuiltin) { + generatedStrings->push_back("CANYON_FUNCTION_" + std::string(name)); + std::string_view newName = generatedStrings->back(); oldFunction.accept(*this); std::unique_ptr newFunction = std::unique_ptr( dynamic_cast(returnValue.release())); - generatedStrings->push_back("CANYON_FUNCTION_" + std::string(name)); - std::string_view newName = generatedStrings->back(); - newFunction->setTypeID(oldFunction.getTypeID()); + if (oldFunction.getVariant() != FunctionVariant::CONSTRUCTOR) { + newFunction->setTypeID(oldFunction.getTypeID()); + } outputModule->addFunction( std::make_unique(Slice(newName, inputModule->getSource(), 0, 0)), std::move(newFunction), isBuiltin); }); + node.forEachClass([this](std::string_view name, Class &oldClass, bool isBuiltin) { + generatedStrings->push_back(std::string(name)); + std::string_view newName = generatedStrings->back(); + currentClassName = &generatedStrings->back(); + oldClass.accept(*this); + std::unique_ptr newClass + = std::unique_ptr(dynamic_cast(returnValue.release())); + outputModule->addClass( + std::make_unique(Slice(newName, inputModule->getSource(), 0, 0)), + std::move(newClass), isBuiltin); + }); + node.forEachImpl([this](std::string_view name, Impl &oldImpl, bool isBuiltin) { + generatedStrings->push_back(std::string(name)); + std::string_view newName = generatedStrings->back(); + oldImpl.accept(*this); + std::unique_ptr newImpl + = std::unique_ptr(dynamic_cast(returnValue.release())); + outputModule->addImpl( + std::make_unique(Slice(newName, inputModule->getSource(), 0, 0)), + std::move(newImpl), isBuiltin); + }); } void CCodeAdapter::visitExpression(Expression &node) { diff --git a/src/ccodeadapter.h b/src/ccodeadapter.h index 9a24164..bae7068 100644 --- a/src/ccodeadapter.h +++ b/src/ccodeadapter.h @@ -22,6 +22,7 @@ class CCodeAdapter : ASTVisitor { std::stack blockTemporaryVariables; std::vector scopeStack; std::list *generatedStrings; + std::string *currentClassName = nullptr; public: CCodeAdapter(Module *module, std::list *generatedStrings); std::unique_ptr transform(); @@ -35,11 +36,15 @@ class CCodeAdapter : ASTVisitor { void visit(BlockExpression &node) override; void visit(ReturnExpression &node) override; void visit(ParenthesizedExpression &node) override; + void visit(PathExpression &node) override; + void visit(FieldAccessExpression &node) override; void visit(IfElseExpression &node) override; void visit(WhileExpression &node) override; void visit(ExpressionStatement &node) override; void visit(LetStatement &node) override; void visit(Function &node) override; + void visit(Class &node) override; + void visit(Impl &node) override; void visit(Module &node) override; virtual ~CCodeAdapter() = default; private: diff --git a/src/ccodegenerator.cpp b/src/ccodegenerator.cpp index 1d5d684..930cd65 100644 --- a/src/ccodegenerator.cpp +++ b/src/ccodegenerator.cpp @@ -23,6 +23,15 @@ CCodeGenerator::CCodeGenerator(Module *module, std::ostream *os) cTypes[this->module->getType("u64").id] = "uint64_t"; cTypes[this->module->getType("bool").id] = "bool"; cTypes[this->module->getType("char").id] = "char"; + + module->forEachClass( + [this, &module](std::string_view name, Class & /*unused*/, bool isBuiltin) { + if (!isBuiltin) { + int typeID = module->getType(std::string(name)).id; + cTypes[typeID] = "struct CANYON_INSTANCE_" + std::string(name); + classNames[typeID] = std::string(name); + } + }); } void CCodeGenerator::generate() { @@ -37,19 +46,34 @@ void CCodeGenerator::generateIncludes() { "#include \n" "#include \n" "#include \n" + "#include \n" "\n"; } void CCodeGenerator::visit(FunctionCallExpression &node) { node.getFunction().accept(*this); *os << '('; + + if (node.getVariant() == FunctionVariant::METHOD) { + FieldAccessExpression &fieldAccess + = dynamic_cast(node.getFunction()); + *os << "(struct CANYON_CLASS_" << classNames[fieldAccess.getObject().getTypeID()] + << " *)"; + fieldAccess.getObject().accept(*this); + *os << ".object"; + } bool first = true; - node.forEachArgument([this, &first](Expression &argument) { + node.forEachArgument([this, &first, &node](Expression &argument) { if (!first) { *os << ", "; } + if (first && node.getVariant() == FunctionVariant::CONSTRUCTOR) { + argument.accept(*this); + *os << ".object"; + } else { + argument.accept(*this); + } first = false; - argument.accept(*this); }); *os << ')'; } @@ -153,9 +177,9 @@ void CCodeGenerator::visit(BlockExpression &node) { } void CCodeGenerator::visit(ReturnExpression &node) { - // Invariant: the return expression will only ever be in an ExpressionStatement (not - // nested inside another expression) Otherwise it would trigger unreachable code error - // before reaching the codegen phase + // Invariant: the return expression will only ever be in an ExpressionStatement + // (not nested inside another expression) Otherwise it would trigger unreachable + // code error before reaching the codegen phase *os << "return"; Expression *expression = node.getExpression(); if (expression != nullptr) { @@ -168,6 +192,39 @@ void CCodeGenerator::visit(ParenthesizedExpression &node) { node.getExpression().accept(*this); } +void CCodeGenerator::visit([[maybe_unused]] PathExpression &node) { + // TODO +} + +void CCodeGenerator::visit(FieldAccessExpression &node) { + SymbolExpression *field = dynamic_cast(&node.getField()); + FunctionCallExpression *method + = dynamic_cast(&node.getField()); + if (field != nullptr) { + if (node.getObject().getSlice().contents == "CANYON_PARAMETER_self") { + *os << "CANYON_PARAMETER_self->"; + } else { + *os << "((struct CANYON_CLASS_" << classNames[node.getObject().getTypeID()] + << " *)"; + node.getObject().accept(*this); + *os << ".object)->"; + } + node.getField().accept(*this); + return; + } + if (method != nullptr) { + node.getObject().accept(*this); + *os << ".vt->"; + Status old = status; + status = Status::IN_METHOD; + node.getField().accept(*this); + status = old; + return; + } + std::cerr << "Field access target is not a symbol or method call" << std::endl; + exit(EXIT_FAILURE); +} + void CCodeGenerator::visit(IfElseExpression &node) { *os << "if ("; node.getCondition().accept(*this); @@ -199,9 +256,20 @@ void CCodeGenerator::visit(LetStatement &node) { const std::string &cType = cTypes[node.getSymbolTypeID()]; Expression *expression = node.getExpression(); if (expression != nullptr) { - *os << cType << ' ' << node.getSymbol().s << " = "; - node.getExpression()->accept(*this); - *os << ";\n"; + FunctionCallExpression *functionCall + = dynamic_cast(expression); + if (module->getType(node.getSymbolTypeID()).isClass && functionCall != nullptr + && functionCall->getFunction().getSlice().contents == "malloc") { + *os << cType << ' ' << node.getSymbol().s << " = {"; + expression->accept(*this); + std::string_view className = classNames[node.getSymbolTypeID()]; + *os << ", &" << className << "_VT_INSTANCE"; + *os << "};\n"; + } else { + *os << cType << ' ' << node.getSymbol().s << " = "; + node.getExpression()->accept(*this); + *os << ";\n"; + } } else { *os << cType << ' ' << node.getSymbol().s << ";\n"; } @@ -211,8 +279,26 @@ void CCodeGenerator::visit([[maybe_unused]] Function &node) { // Currently being handled in the Module visitor } +void CCodeGenerator::visit([[maybe_unused]] Class &node) { + // Currently being handled in the Module visitor +} + +void CCodeGenerator::visit([[maybe_unused]] Impl &node) { + // TODO +} + void CCodeGenerator::visit(Module &node) { - // Forward declarations + // Instance structs + node.forEachImpl([this](std::string_view name, Impl & /*unused*/, bool /*unused*/) { + *os << "struct CANYON_IMPL_" << name << "_VT;\n"; + *os << "struct CANYON_INSTANCE_" << name << " {\n" + << "\tvoid *object;\n" + << "\tstruct CANYON_IMPL_" << name << "_VT *vt;\n" + << "};\n"; + }); + *os << '\n'; + + // Function prototypes node.forEachFunction( [this](std::string_view name, Function &function, bool /*unused*/) { Type functionType = module->getType(function.getTypeID()); @@ -230,11 +316,110 @@ void CCodeGenerator::visit(Module &node) { *os << ");\n"; }); *os << '\n'; - generateMain(); + + // Method prototypes + node.forEachImpl([this](std::string_view name, Impl &impl, bool /*unused*/) { + impl.forEachMethod([this, &name](std::string_view methodName, Function &method) { + Type functionType = module->getType(method.getTypeID()); + if (method.getVariant() == FunctionVariant::CONSTRUCTOR) { + *os << "struct CANYON_CLASS_" << classNames[functionType.id] << " *"; + } else { + const std::string &cType = cTypes[functionType.id]; + *os << cType; + } + *os << " CANYON_IMPL_" << name << "_METHOD_" << methodName << '('; + bool first = true; + method.forEachParameter( + [this, &first](Symbol ¶meter, Symbol &typeSymbol) { + Type type = module->getType(typeSymbol.s.contents); + if (first && type.isClass + && parameter.s.contents == "CANYON_PARAMETER_self") { + *os << "struct CANYON_CLASS_" << classNames[type.id] + << " *CANYON_PARAMETER_self"; + first = false; + return; + } + const std::string &cType + = cTypes[module->getType(typeSymbol.s.contents).id]; + if (!first) { + *os << ", "; + } + first = false; + *os << cType << ' ' << parameter.s; + }); + *os << ");\n"; + }); + }); + *os << '\n'; + + // Virtual tables + node.forEachImpl([this](std::string_view name, Impl &impl, bool /*unused*/) { + *os << "struct CANYON_IMPL_" << name << "_VT {\n"; + tabLevel++; + impl.forEachMethod([this](std::string_view methodName, Function &method) { + if (method.getVariant() != FunctionVariant::METHOD) { + return; + } + Type methodType = module->getType(method.getTypeID()); + const std::string &cType = cTypes[methodType.id]; + *os << std::string(tabLevel, '\t') << cType << " (*" << methodName << ")("; + bool first = true; + method.forEachParameter( + [this, &first](Symbol ¶meter, Symbol &typeSymbol) { + Type type = module->getType(typeSymbol.s.contents); + if (first && type.isClass + && parameter.s.contents == "CANYON_PARAMETER_self") { + *os << "struct CANYON_CLASS_" << classNames[type.id] << '*'; + first = false; + return; + } + const std::string &cType + = cTypes[module->getType(typeSymbol.s.contents).id]; + if (!first) { + *os << ", "; + } + first = false; + *os << cType; + }); + *os << ");\n"; + }); + tabLevel--; + *os << "} " << name << "_VT_INSTANCE = {\n"; + tabLevel++; + impl.forEachMethod([this, &name](std::string_view methodName, Function &method) { + if (method.getVariant() != FunctionVariant::METHOD) { + return; + } + *os << std::string(tabLevel, '\t') << "CANYON_IMPL_" << name << "_METHOD_" + << methodName << ",\n"; + }); + tabLevel--; + *os << "};\n"; + }); + *os << '\n'; + + // Class structs + node.forEachClass([this](std::string_view name, Class &cls, bool /*unused*/) { + *os << "struct CANYON_CLASS_" << name << " {\n"; + tabLevel++; + cls.getScope().forEachSymbol( + [this](std::string_view fieldName, int typeId, SymbolSource /*unused*/) { + *os << std::string(tabLevel, '\t'); + const std::string &cType = cTypes[typeId]; + *os << cType << ' ' << fieldName << ";\n"; + }); + tabLevel--; + *os << "};\n"; + }); + *os << '\n'; // Function definitions + generateBuiltinFunctions(); node.forEachFunction( [this](std::string_view name, Function &function, bool isBuiltin) { + if (isBuiltin) { + return; + } Type functionType = module->getType(function.getTypeID()); const std::string &cType = cTypes[functionType.id]; *os << cType << ' ' << name << '('; @@ -248,63 +433,84 @@ void CCodeGenerator::visit(Module &node) { *os << cType << ' ' << parameter.s; }); *os << ") "; - if (!isBuiltin) { - function.getBody().accept(*this); - *os << "\n"; - return; - } - if (name == "CANYON_FUNCTION_printI8") { - *os << "{\n" - " printf(\"%\" PRId8, CANYON_PARAMETER_value);\n" - "}\n"; - } else if (name == "CANYON_FUNCTION_printI16") { - *os << "{\n" - " printf(\"%\" PRId16, CANYON_PARAMETER_value);\n" - "}\n"; - } else if (name == "CANYON_FUNCTION_printI32") { - *os << "{\n" - " printf(\"%\" PRId32, CANYON_PARAMETER_value);\n" - "}\n"; - } else if (name == "CANYON_FUNCTION_printI64") { - *os << "{\n" - " printf(\"%\" PRId64, CANYON_PARAMETER_value);\n" - "}\n"; - } else if (name == "CANYON_FUNCTION_printU8") { - *os << "{\n" - " printf(\"%\" PRIu8, CANYON_PARAMETER_value);\n" - "}\n"; - } else if (name == "CANYON_FUNCTION_printU16") { - *os << "{\n" - " printf(\"%\" PRIu16, CANYON_PARAMETER_value);\n" - "}\n"; - } else if (name == "CANYON_FUNCTION_printU32") { - *os << "{\n" - " printf(\"%\" PRIu32, CANYON_PARAMETER_value);\n" - "}\n"; - } else if (name == "CANYON_FUNCTION_printU64") { - *os << "{\n" - " printf(\"%\" PRIu64, CANYON_PARAMETER_value);\n" - "}\n"; - } else if (name == "CANYON_FUNCTION_printBool") { - *os << "{\n" - " printf(CANYON_PARAMETER_value ? \"true\" : \"false\");\n" - "}\n"; - } else if (name == "CANYON_FUNCTION_printChar") { - *os << "{\n" - " printf(\"%c\", CANYON_PARAMETER_value);\n" - "}\n"; - } else { - std::cerr << "Unknown builtin function: " << name << '\n'; - exit(EXIT_FAILURE); - } + function.getBody().accept(*this); *os << "\n"; }); + *os << '\n'; + + // Method definitions + node.forEachImpl([this](std::string_view name, Impl &impl, bool isBuiltin) { + if (isBuiltin) { + return; + } + impl.forEachMethod([this, &name](std::string_view methodName, Function &method) { + Type functionType = module->getType(method.getTypeID()); + if (method.getVariant() == FunctionVariant::CONSTRUCTOR) { + *os << "struct CANYON_CLASS_" << classNames[functionType.id] << " *"; + } else { + const std::string &cType = cTypes[functionType.id]; + *os << cType; + } + *os << " CANYON_IMPL_" << name << "_METHOD_" << methodName << '('; + bool first = true; + method.forEachParameter( + [this, &first](Symbol ¶meter, Symbol &typeSymbol) { + Type type = module->getType(typeSymbol.s.contents); + if (first && type.isClass + && parameter.s.contents == "CANYON_PARAMETER_self") { + *os << "struct CANYON_CLASS_" << classNames[type.id] + << " *CANYON_PARAMETER_self"; + first = false; + return; + } + const std::string &cType + = cTypes[module->getType(typeSymbol.s.contents).id]; + if (!first) { + *os << ", "; + } + first = false; + *os << cType << ' ' << parameter.s; + }); + *os << ") "; + method.getBody().accept(*this); + *os << "\n"; + }); + }); } -void CCodeGenerator::generateMain() { +void CCodeGenerator::generateBuiltinFunctions() { *os << "int main() {\n" - " CANYON_FUNCTION_main();\n" - " return 0;\n" + "\tCANYON_FUNCTION_main();\n" + "\treturn 0;\n" "}\n" - "\n"; + "void CANYON_FUNCTION_printI8(int8_t CANYON_PARAMETER_value) {\n" + "\tprintf(\"%\" PRId8, CANYON_PARAMETER_value);\n" + "}\n" + "void CANYON_FUNCTION_printI16(int16_t CANYON_PARAMETER_value) {\n" + "\tprintf(\"%\" PRId16, CANYON_PARAMETER_value);\n" + "}\n" + "void CANYON_FUNCTION_printI32(int32_t CANYON_PARAMETER_value) {\n" + "\tprintf(\"%\" PRId32, CANYON_PARAMETER_value);\n" + "}\n" + "void CANYON_FUNCTION_printI64(int64_t CANYON_PARAMETER_value) {\n" + "\tprintf(\"%\" PRId64, CANYON_PARAMETER_value);\n" + "}\n" + "void CANYON_FUNCTION_printU8(uint8_t CANYON_PARAMETER_value) {\n" + "\tprintf(\"%\" PRIu8, CANYON_PARAMETER_value);\n" + "}\n" + "void CANYON_FUNCTION_printU16(uint16_t CANYON_PARAMETER_value) {\n" + "\tprintf(\"%\" PRIu16, CANYON_PARAMETER_value);\n" + "}\n" + "void CANYON_FUNCTION_printU32(uint32_t CANYON_PARAMETER_value) {\n" + "\tprintf(\"%\" PRIu32, CANYON_PARAMETER_value);\n" + "}\n" + "void CANYON_FUNCTION_printU64(uint64_t CANYON_PARAMETER_value) {\n" + "\tprintf(\"%\" PRIu64, CANYON_PARAMETER_value);\n" + "}\n" + "void CANYON_FUNCTION_printBool(bool CANYON_PARAMETER_value) {\n" + "\tprintf(CANYON_PARAMETER_value ? \"true\" : \"false\");\n" + "}\n" + "void CANYON_FUNCTION_printChar(char CANYON_PARAMETER_value) {\n" + "\tprintf(\"%c\", CANYON_PARAMETER_value);\n" + "}\n"; } diff --git a/src/ccodegenerator.h b/src/ccodegenerator.h index 3485021..76cffeb 100644 --- a/src/ccodegenerator.h +++ b/src/ccodegenerator.h @@ -17,8 +17,18 @@ class CCodeGenerator : public ASTVisitor { Module *module; std::ostream *os; std::unordered_map cTypes; + std::unordered_map classNames; int tabLevel = 0; std::list generatedStrings; + enum class Status { + NORMAL, + IN_FUNCTION, + IN_METHOD, + IN_CLASS, + IN_IMPL, + IN_CALL, + }; + Status status = Status::NORMAL; public: CCodeGenerator(Module *module, std::ostream *os); void generate(); @@ -32,16 +42,20 @@ class CCodeGenerator : public ASTVisitor { void visit(BlockExpression &node) override; void visit(ReturnExpression &node) override; void visit(ParenthesizedExpression &node) override; + void visit(PathExpression &node) override; + void visit(FieldAccessExpression &node) override; void visit(IfElseExpression &node) override; void visit(WhileExpression &node) override; void visit(ExpressionStatement &node) override; void visit(LetStatement &node) override; void visit(Function &node) override; + void visit(Class &node) override; + void visit(Impl &node) override; void visit(Module &node) override; ~CCodeGenerator() = default; private: void generateIncludes(); - void generateMain(); + void generateBuiltinFunctions(); }; #endif diff --git a/src/lexer.cpp b/src/lexer.cpp index 6bf0098..b35c47e 100644 --- a/src/lexer.cpp +++ b/src/lexer.cpp @@ -234,6 +234,15 @@ std::unique_ptr Lexer::createKeyword(const Slice &s) { if (s.contents == "while") { return std::make_unique(s, Keyword::Type::WHILE); } + if (s.contents == "class") { + return std::make_unique(s, Keyword::Type::CLASS); + } + if (s.contents == "constructor") { + return std::make_unique(s, Keyword::Type::CONSTRUCTOR); + } + if (s.contents == "impl") { + return std::make_unique(s, Keyword::Type::IMPL); + } return nullptr; } diff --git a/src/parser.cpp b/src/parser.cpp index 55684c2..a464d3f 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -14,9 +14,79 @@ Parser::Parser(std::filesystem::path source, std::vector> std::unique_ptr Parser::parse() { auto mod = std::make_unique(source); while (!isAtEnd()) { - std::pair, std::unique_ptr> func - = parseFunction(); - if (func.second == nullptr) { + auto *keyword = dynamic_cast(tokens[i].get()); + if (keyword != nullptr && keyword->type == Keyword::Type::FUN) { + std::pair, std::unique_ptr> func + = parseFunction(); + if (func.second == nullptr) { + synchronize(); + mustSynchronize = false; + auto *punc = dynamic_cast(tokens[i].get()); + while (punc != nullptr) { + if (punc->type == Punctuation::Type::Semicolon) { + i++; + synchronize(); + mustSynchronize = false; + punc = dynamic_cast(tokens[i].get()); + } else if (punc->type == Punctuation::Type::CloseBrace) { + i++; + break; + } else { + std::cerr << "Unexpected token in parse" << std::endl; + exit(EXIT_FAILURE); + } + } + continue; + } + mod->addFunction(std::move(func.first), std::move(func.second)); + } else if (keyword != nullptr && keyword->type == Keyword::Type::CLASS) { + std::pair, std::unique_ptr> cls = parseClass(); + if (cls.second == nullptr) { + synchronize(); + mustSynchronize = false; + auto *punc = dynamic_cast(tokens[i].get()); + while (punc != nullptr) { + if (punc->type == Punctuation::Type::Semicolon) { + i++; + synchronize(); + mustSynchronize = false; + punc = dynamic_cast(tokens[i].get()); + } else if (punc->type == Punctuation::Type::CloseBrace) { + i++; + break; + } else { + std::cerr << "Unexpected token in parse" << std::endl; + exit(EXIT_FAILURE); + } + } + continue; + } + mod->addClass(std::move(cls.first), std::move(cls.second)); + } else if (keyword != nullptr && keyword->type == Keyword::Type::IMPL) { + std::pair, std::unique_ptr> impl = parseImpl(); + if (impl.second == nullptr) { + synchronize(); + mustSynchronize = false; + auto *punc = dynamic_cast(tokens[i].get()); + while (punc != nullptr) { + if (punc->type == Punctuation::Type::Semicolon) { + i++; + synchronize(); + mustSynchronize = false; + punc = dynamic_cast(tokens[i].get()); + } else if (punc->type == Punctuation::Type::CloseBrace) { + i++; + break; + } else { + std::cerr << "Unexpected token in parse" << std::endl; + exit(EXIT_FAILURE); + } + } + continue; + } + mod->addImpl(std::move(impl.first), std::move(impl.second)); + } else { + errorHandler->error(*tokens[i], "Expected keyword `fun` or `class`"); synchronize(); mustSynchronize = false; auto *punc = dynamic_cast(tokens[i].get()); @@ -36,21 +106,32 @@ std::unique_ptr Parser::parse() { } continue; } - mod->addFunction(std::move(func.first), std::move(func.second)); } return mod; } -std::pair, std::unique_ptr> Parser::parseFunction() { +std::pair, std::unique_ptr> Parser::parseFunction( + bool isConstructor) { auto *keyword = dynamic_cast(tokens[i].get()); - if (keyword == nullptr || keyword->type != Keyword::Type::FUN) { - errorHandler->error(*tokens[i], "Expected keyword `fun`"); - return {nullptr, nullptr}; + if (!isConstructor) { + if (keyword == nullptr || keyword->type != Keyword::Type::FUN) { + errorHandler->error(*tokens[i], "Expected keyword `fun`"); + return {nullptr, nullptr}; + } + } else { + if (keyword == nullptr || keyword->type != Keyword::Type::CONSTRUCTOR) { + errorHandler->error(*tokens[i], "Expected keyword `constructor`"); + return {nullptr, nullptr}; + } } i++; auto *symbol = dynamic_cast(tokens[i].get()); if (symbol == nullptr) { - errorHandler->error(*tokens[i], "Expected symbol following `fun`"); + if (!isConstructor) { + errorHandler->error(*tokens[i], "Expected symbol following `fun`"); + } else { + errorHandler->error(*tokens[i], "Expected symbol following `constructor`"); + } return {nullptr, nullptr}; } symbol = dynamic_cast(tokens[i++].release()); @@ -126,82 +207,224 @@ std::pair, std::unique_ptr> Parser::parseFunct return {std::unique_ptr(symbol), std::make_unique(std::move(parameters), std::unique_ptr(type), - std::move(block))}; + std::move(block), + isConstructor ? FunctionVariant::CONSTRUCTOR + : FunctionVariant::FUNCTION)}; } -std::unique_ptr Parser::parseStatement() { +std::pair, std::unique_ptr> Parser::parseClass() { auto *keyword = dynamic_cast(tokens[i].get()); - if (keyword != nullptr && keyword->type == Keyword::Type::LET) { - i++; - auto *symbol = dynamic_cast(tokens[i].get()); - if (symbol == nullptr) { - errorHandler->error(*tokens[i], "Expected symbol following `let`"); + if (keyword == nullptr || keyword->type != Keyword::Type::CLASS) { + errorHandler->error(*tokens[i], "Expected keyword `class`"); + return {nullptr, nullptr}; + } + i++; + auto *symbol = dynamic_cast(tokens[i].get()); + if (symbol == nullptr) { + errorHandler->error(*tokens[i], "Expected symbol following `class`"); + return {nullptr, nullptr}; + } + symbol = dynamic_cast(tokens[i++].release()); + auto *punc = dynamic_cast(tokens[i].get()); + if (punc == nullptr || punc->type != Punctuation::Type::OpenBrace) { + errorHandler->error(*tokens[i], + "Expected '{' following symbol in class definition"); + mustSynchronize = true; + return {nullptr, nullptr}; + } + i++; + + std::vector> fields; + while (true) { + auto *p1 = dynamic_cast(tokens[i].get()); + if (p1 != nullptr && p1->type == Punctuation::Type::CloseBrace) { + break; + } + auto *field = dynamic_cast(tokens[i].get()); + if (field == nullptr) { + errorHandler->error(*tokens[i], + "Expected symbol for class field declaration"); mustSynchronize = true; - return nullptr; + return {nullptr, nullptr}; } - symbol = dynamic_cast(tokens[i++].release()); - Symbol *type = nullptr; - auto *punc = dynamic_cast(tokens[i].get()); - if (punc != nullptr && punc->type == Punctuation::Type::Colon) { - i++; - type = dynamic_cast(tokens[i].get()); - if (type == nullptr) { - errorHandler->error(*tokens[i], - "Expected type following ':' in `let` statement"); - mustSynchronize = true; - return nullptr; - } - type = dynamic_cast(tokens[i++].release()); - } else { - type = nullptr; + field = dynamic_cast(tokens[i++].release()); + auto *p2 = dynamic_cast(tokens[i].get()); + if (p2 == nullptr || p2->type != Punctuation::Type::Colon) { + errorHandler->error(*tokens[i], + "Expected ':' following symbol in class field declaration"); + mustSynchronize = true; + return {nullptr, nullptr}; } - punc = dynamic_cast(tokens[i].get()); - if (punc != nullptr && punc->type == Punctuation::Type::Semicolon) { - i++; - return std::make_unique(*keyword, - std::unique_ptr(symbol), std::unique_ptr(type), nullptr, - nullptr, punc); + i++; + Symbol *type = dynamic_cast(tokens[i].get()); + if (type == nullptr) { + errorHandler->error(*tokens[i], + "Expected type following ':' in `let` statement"); + mustSynchronize = true; + return {nullptr, nullptr}; } - auto *op = dynamic_cast(tokens[i].get()); - if (op == nullptr || op->type != Operator::Type::Assignment) { + type = dynamic_cast(tokens[i++].release()); + auto *p3 = dynamic_cast(tokens[i].get()); + if (p3 == nullptr || p3->type != Punctuation::Type::Semicolon) { errorHandler->error(*tokens[i], - "Expected assignment expression in `let` statement"); + "Expected ';' following type in class field declaration"); mustSynchronize = true; - return nullptr; + return {nullptr, nullptr}; } - op = dynamic_cast(tokens[i].release()); i++; - std::unique_ptr expr = parseExpression(); - if (expr == nullptr) { - synchronize(); - mustSynchronize = false; - auto *punc = dynamic_cast(tokens[i].get()); - if (punc != nullptr) { - if (punc->type == Punctuation::Type::Semicolon) { - i++; - return nullptr; - } - if (punc->type == Punctuation::Type::CloseBrace) { - return nullptr; - } - std::cerr << "Unexpected token in parseExpression" << std::endl; - exit(EXIT_FAILURE); + + fields.emplace_back(std::make_unique(std::unique_ptr(field), + std::unique_ptr(type), punc)); + } + + auto *p2 = dynamic_cast(tokens[i].get()); + if (p2 == nullptr || p2->type != Punctuation::Type::CloseBrace) { + errorHandler->error(*tokens[i], "Expected '}' after class definition"); + return {nullptr, nullptr}; + } + i++; + + return {std::unique_ptr(symbol), std::make_unique(std::move(fields))}; +} + +std::pair, std::unique_ptr> Parser::parseImpl() { + auto *keyword = dynamic_cast(tokens[i].get()); + if (keyword == nullptr || keyword->type != Keyword::Type::IMPL) { + errorHandler->error(*tokens[i], "Expected keyword `impl`"); + return {nullptr, nullptr}; + } + i++; + auto *symbol = dynamic_cast(tokens[i].get()); + if (symbol == nullptr) { + errorHandler->error(*tokens[i], "Expected symbol following `impl`"); + return {nullptr, nullptr}; + } + symbol = dynamic_cast(tokens[i++].release()); + auto *punc = dynamic_cast(tokens[i].get()); + if (punc == nullptr || punc->type != Punctuation::Type::OpenBrace) { + errorHandler->error(*tokens[i], "Expected '{' following symbol in impl block"); + mustSynchronize = true; + return {nullptr, nullptr}; + } + i++; + + std::unordered_map> methods; + while (true) { + auto *keyword2 = dynamic_cast(tokens[i].get()); + if (keyword2 != nullptr && keyword2->type == Keyword::Type::FUN) { + std::pair, std::unique_ptr> method + = parseFunction(); + if (method.second == nullptr) { + return {nullptr, nullptr}; } + methods.emplace(method.first->s.contents, std::move(method.second)); + } else if (keyword2 != nullptr && keyword2->type == Keyword::Type::CONSTRUCTOR) { + std::pair, std::unique_ptr> method + = parseFunction(true); + if (method.second == nullptr) { + return {nullptr, nullptr}; + } + methods.emplace(method.first->s.contents, std::move(method.second)); + } else if (keyword2 != nullptr) { + errorHandler->error(*tokens[i], "Unexpected keyword in impl block"); + mustSynchronize = true; + return {nullptr, nullptr}; + } else { + break; } - punc = dynamic_cast(tokens[i].get()); - if (punc == nullptr || punc->type != Punctuation::Type::Semicolon) { + } + + auto *p2 = dynamic_cast(tokens[i].get()); + if (p2 == nullptr || p2->type != Punctuation::Type::CloseBrace) { + errorHandler->error(*tokens[i], "Expected '}' after impl block"); + return {nullptr, nullptr}; + } + i++; + + return {std::unique_ptr(symbol), std::make_unique(std::move(methods))}; +} + +std::unique_ptr Parser::parseStatement() { + auto *keyword = dynamic_cast(tokens[i].get()); + if (keyword != nullptr && keyword->type == Keyword::Type::LET) { + return parseLet(); + } + return nullptr; +} + +std::unique_ptr Parser::parseLet() { + auto *keyword = dynamic_cast(tokens[i].get()); + if (keyword == nullptr || keyword->type != Keyword::Type::LET) { + errorHandler->error(*tokens[i], "Expected keyword `let`"); + mustSynchronize = true; + return nullptr; + } + i++; + auto *symbol = dynamic_cast(tokens[i].get()); + if (symbol == nullptr) { + errorHandler->error(*tokens[i], "Expected symbol following `let`"); + mustSynchronize = true; + return nullptr; + } + symbol = dynamic_cast(tokens[i++].release()); + Symbol *type = nullptr; + auto *punc = dynamic_cast(tokens[i].get()); + if (punc != nullptr && punc->type == Punctuation::Type::Colon) { + i++; + type = dynamic_cast(tokens[i].get()); + if (type == nullptr) { errorHandler->error(*tokens[i], - "Expected ';' following expression in `let` statement"); + "Expected type following ':' in `let` statement"); mustSynchronize = true; return nullptr; } + type = dynamic_cast(tokens[i++].release()); + } else { + type = nullptr; + } + punc = dynamic_cast(tokens[i].get()); + if (punc != nullptr && punc->type == Punctuation::Type::Semicolon) { i++; return std::make_unique(*keyword, std::unique_ptr(symbol), - std::unique_ptr(type), std::unique_ptr(op), - std::move(expr), punc); + std::unique_ptr(type), nullptr, nullptr, punc); } - - return nullptr; + auto *op = dynamic_cast(tokens[i].get()); + if (op == nullptr || op->type != Operator::Type::Assignment) { + errorHandler->error(*tokens[i], + "Expected assignment expression in `let` statement"); + mustSynchronize = true; + return nullptr; + } + op = dynamic_cast(tokens[i].release()); + i++; + std::unique_ptr expr = parseExpression(); + if (expr == nullptr) { + synchronize(); + mustSynchronize = false; + auto *punc = dynamic_cast(tokens[i].get()); + if (punc != nullptr) { + if (punc->type == Punctuation::Type::Semicolon) { + i++; + return nullptr; + } + if (punc->type == Punctuation::Type::CloseBrace) { + return nullptr; + } + std::cerr << "Unexpected token in parseExpression" << std::endl; + exit(EXIT_FAILURE); + } + } + punc = dynamic_cast(tokens[i].get()); + if (punc == nullptr || punc->type != Punctuation::Type::Semicolon) { + errorHandler->error(*tokens[i], + "Expected ';' following expression in `let` statement"); + mustSynchronize = true; + return nullptr; + } + i++; + return std::make_unique(*keyword, std::unique_ptr(symbol), + std::unique_ptr(type), std::unique_ptr(op), std::move(expr), + punc); } std::unique_ptr Parser::parseExpression() { @@ -628,11 +851,37 @@ std::unique_ptr Parser::parseUnaryExpression() { return std::make_unique(std::unique_ptr(op), std::move(expr)); } - return parseFunctionCallExpression(); + return parseFieldAccessExpression(); +} + +std::unique_ptr Parser::parseFieldAccessExpression() { + std::unique_ptr expr = parseFunctionCallExpression(); + if (expr == nullptr) { + return nullptr; + } + while (true) { + auto *punc = dynamic_cast(tokens[i].get()); + if (punc != nullptr && punc->type == Punctuation::Type::Period) { + i++; + std::unique_ptr expr2 = parseFunctionCallExpression(); + if (expr2 == nullptr) { + return nullptr; + } + if (!dynamic_cast(expr2.get()) + && !dynamic_cast(expr2.get())) { + errorHandler->error(*tokens[i], "Expected symbol or method call"); + return nullptr; + } + expr = std::make_unique(std::move(expr), + std::move(expr2)); + } else { + return expr; + } + } } std::unique_ptr Parser::parseFunctionCallExpression() { - std::unique_ptr expr = parsePrimaryExpression(); + std::unique_ptr expr = parsePathExpression(); if (expr == nullptr) { return nullptr; } @@ -668,6 +917,43 @@ std::unique_ptr Parser::parseFunctionCallExpression() { return expr; } +std::unique_ptr Parser::parsePathExpression() { + std::unique_ptr expr = parsePrimaryExpression(); + if (expr == nullptr) { + return nullptr; + } + if (!dynamic_cast(expr.get())) { + return expr; + } + std::unique_ptr symExpr( + dynamic_cast(expr.release())); + std::vector> symbols; + symbols.push_back(std::move(symExpr)); + while (true) { + auto *op = dynamic_cast(tokens[i].get()); + if (op != nullptr && op->type == Operator::Type::Scope) { + op = dynamic_cast(tokens[i++].release()); + std::unique_ptr expr2 = parsePrimaryExpression(); + if (expr2 == nullptr) { + return nullptr; + } + if (!dynamic_cast(expr2.get())) { + errorHandler->error(*tokens[i], "Expected symbol"); + return nullptr; + } + symExpr = std::unique_ptr( + dynamic_cast(expr2.release())); + symbols.push_back(std::move(symExpr)); + } else { + if (symbols.size() == 1) { + return std::move(symbols[0]); + } else { + return std::make_unique(std::move(symbols)); + } + } + } +} + std::unique_ptr Parser::parsePrimaryExpression() { auto *integerLiteral = dynamic_cast(tokens[i].get()); if (integerLiteral != nullptr) { diff --git a/src/parser.h b/src/parser.h index 3184787..484d2d1 100644 --- a/src/parser.h +++ b/src/parser.h @@ -25,8 +25,12 @@ class Parser { std::unique_ptr parse(); ~Parser() = default; private: - std::pair, std::unique_ptr> parseFunction(); + std::pair, std::unique_ptr> parseFunction( + bool isConstructor = false); + std::pair, std::unique_ptr> parseClass(); + std::pair, std::unique_ptr> parseImpl(); std::unique_ptr parseStatement(); + std::unique_ptr parseLet(); std::unique_ptr parseExpression(); std::unique_ptr parseBlock(); std::unique_ptr parseIfElse(); @@ -43,7 +47,9 @@ class Parser { std::unique_ptr parseAdditiveExpression(); std::unique_ptr parseMultiplicativeExpression(); std::unique_ptr parseUnaryExpression(); + std::unique_ptr parseFieldAccessExpression(); std::unique_ptr parseFunctionCallExpression(); + std::unique_ptr parsePathExpression(); std::unique_ptr parsePrimaryExpression(); void synchronize(); bool isAtEnd() const; diff --git a/src/semanticanalyzer.cpp b/src/semanticanalyzer.cpp index b6544a6..5f7ff4f 100644 --- a/src/semanticanalyzer.cpp +++ b/src/semanticanalyzer.cpp @@ -37,16 +37,55 @@ void SemanticAnalyzer::analyze() { void SemanticAnalyzer::visit(FunctionCallExpression &node) { Expression &functionCall = node.getFunction(); auto *symbol = dynamic_cast(&functionCall); - if (symbol == nullptr) { + auto *path = dynamic_cast(&functionCall); + if (symbol == nullptr && path == nullptr) { errorHandler->error(functionCall.getSlice(), - "Function call target is not a symbol"); + "Function call target is not a symbol, path, or method"); return; } - Function *function = module->getFunction(symbol->getSymbol().s.contents); - if (function == nullptr) { - errorHandler->error(symbol->getSymbol().s, - "Function " + std::string(symbol->getSymbol().s.contents) + " not found"); - return; + Function *function = nullptr; + if (symbol != nullptr) { + function = module->getFunction(symbol->getSymbol().s.contents); + if (function == nullptr) { + errorHandler->error(symbol->getSymbol().s, + "Function " + std::string(symbol->getSymbol().s.contents) + + " not found"); + return; + } + } else if (path != nullptr) { + Impl *impl = nullptr; + bool pathLookupGood = true; + size_t i = 0; + path->forEachSymbol( + [this, &impl, &function, &pathLookupGood, &i](SymbolExpression &symbol) { + if (impl == nullptr && pathLookupGood) { + impl = module->getImpl(symbol.getSymbol().s.contents); + if (impl == nullptr) { + errorHandler->error(symbol.getSymbol().s, + "Impl " + std::string(symbol.getSymbol().s.contents) + + " not found"); + pathLookupGood = false; + return; + } + i++; + } else if (pathLookupGood) { + function = impl->getMethod(symbol.getSymbol().s.contents); + if (function == nullptr) { + errorHandler->error(symbol.getSymbol().s, + "Method " + std::string(symbol.getSymbol().s.contents) + + " not found"); + pathLookupGood = false; + return; + } + i++; + } else if (i >= 2) { + errorHandler->error(symbol.getSymbol().s, "Path too long"); + pathLookupGood = false; + } + }); + if (!pathLookupGood) { + return; + } } std::vector> parameters; function->forEachParameter( @@ -59,6 +98,11 @@ void SemanticAnalyzer::visit(FunctionCallExpression &node) { size_t i = 0; bool unreachableArgument = false; bool unreachableHandled = false; + FunctionVariant variant = function->getVariant(); + if (variant == FunctionVariant::CONSTRUCTOR || variant == FunctionVariant::METHOD) { + // Implicit self argument + i++; + } node.forEachArgument([this, ¶meters, &i, &unreachableArgument, &unreachableHandled](Expression &argument) { if (i == parameters.size()) { @@ -100,7 +144,12 @@ void SemanticAnalyzer::visit(FunctionCallExpression &node) { return; } - node.setTypeID(function->getTypeID()); + node.setVariant(function->getVariant()); + if (function->getVariant() == FunctionVariant::CONSTRUCTOR) { + node.setTypeID(parameters[0].second); + } else { + node.setTypeID(function->getTypeID()); + } } void SemanticAnalyzer::visit(BinaryExpression &node) { @@ -117,9 +166,10 @@ void SemanticAnalyzer::visit(BinaryExpression &node) { return; } if (node.getOperator().type == Operator::Type::Assignment - && (dynamic_cast(&left) == nullptr)) { + && (dynamic_cast(&left) == nullptr) + && (dynamic_cast(&left) == nullptr)) { errorHandler->error(left.getSlice(), - "Left side of assignment must be a variable"); + "Left side of assignment must be a variable or field access"); return; } if (left.getTypeID() == -1 || right.getTypeID() == -1) { @@ -225,6 +275,80 @@ void SemanticAnalyzer::visit(ParenthesizedExpression &node) { node.setTypeID(expr.getTypeID()); } +void SemanticAnalyzer::visit([[maybe_unused]] PathExpression &node) { + // TODO +} + +void SemanticAnalyzer::visit(FieldAccessExpression &node) { + node.getObject().accept(*this); + if (inUnreachableCode) { + errorHandler->error(node.getSlice(), "Unreachable code"); + return; + } + int objectTypeID = node.getObject().getTypeID(); + if (objectTypeID == -1) { + return; + } + std::pair cls = module->getClass(objectTypeID); + if (cls.second == nullptr) { + errorHandler->error(node.getSlice(), "Field access on non-class type"); + return; + } + cls.second->getScope().forEachSymbol([this, &node, &cls](std::string_view fieldName, + int /*unused*/, SymbolSource /*unused*/) { + SymbolExpression *field = dynamic_cast(&node.getField()); + FunctionCallExpression *method + = dynamic_cast(&node.getField()); + if (field == nullptr && method == nullptr) { + errorHandler->error(node.getField().getSlice(), + "Field access is not a symbol or method"); + return; + } + if (field != nullptr) { + if (fieldName == field->getSlice().contents) { + node.setTypeID(cls.second->getScope().getSymbolType(fieldName)); + } + } else { + node.getObject().accept(*this); + if (inUnreachableCode) { + errorHandler->error(node.getSlice(), "Unreachable code"); + return; + } + int objectTypeId = node.getObject().getTypeID(); + if (objectTypeId == -1) { + return; + } + std::pair impl = module->getImpl(objectTypeId); + if (impl.second == nullptr) { + errorHandler->error(node.getSlice(), "Field access on non-class type"); + return; + } + FunctionCallExpression *methodCall + = dynamic_cast(&node.getField()); + if (methodCall == nullptr) { + errorHandler->error(node.getField().getSlice(), + "Field access is not a method"); + return; + } + SymbolExpression *functionName + = dynamic_cast(&methodCall->getFunction()); + if (functionName == nullptr) { + errorHandler->error(methodCall->getFunction().getSlice(), + "Method name is not a symbol"); + return; + } + Function *function + = impl.second->getMethod(functionName->getSymbol().s.contents); + if (function == nullptr) { + errorHandler->error(functionName->getSymbol(), + "Method " + std::string(functionName->getSymbol().s.contents) + + " not found in impl for " + std::string(impl.first)); + return; + } + } + }); +} + void SemanticAnalyzer::visit(IfElseExpression &node) { Expression &condition = node.getCondition(); condition.accept(*this); @@ -301,21 +425,26 @@ void SemanticAnalyzer::visit(LetStatement &node) { return; } int typeID = module->getType(typeAnnotation->s.contents).id; - Expression *value = node.getExpression(); - if (!value) { - errorHandler->error(node.getSlice(), "Expression required for let statement"); - return; - } - value->accept(*this); - if (inUnreachableCode) { - errorHandler->error(node.getEqualSign().s, "Unreachable code"); - return; - } - scopeStack.back()->pushSymbol(node.getSymbol().s.contents, typeID, - SymbolSource::LetStatement); - if (typeID != value->getTypeID() && value->getTypeID() != -1) { - errorHandler->error(node.getExpression()->getSlice(), - "Type mismatch in let statement"); + if (!node.getIsFieldDeclaration()) { + Expression *value = node.getExpression(); + if (!value) { + errorHandler->error(node.getSlice(), "Expression required for let statement"); + return; + } + value->accept(*this); + if (inUnreachableCode) { + errorHandler->error(node.getEqualSign().s, "Unreachable code"); + return; + } + scopeStack.back()->pushSymbol(node.getSymbol().s.contents, typeID, + SymbolSource::LetStatement); + if (typeID != value->getTypeID() && value->getTypeID() != -1) { + errorHandler->error(node.getExpression()->getSlice(), + "Type mismatch in let statement"); + } + } else { + scopeStack.back()->pushSymbol(node.getSymbol().s.contents, typeID, + SymbolSource::FieldDeclaration); } node.setSymbolTypeID(typeID); } @@ -353,9 +482,35 @@ void SemanticAnalyzer::visit(Function &node) { inUnreachableCode = false; } +void SemanticAnalyzer::visit(Class &node) { + scopeStack.push_back(&node.getScope()); + node.forEachFieldDeclaration([this](LetStatement &declaration) { + declaration.accept(*this); + }); +} + +void SemanticAnalyzer::visit(Impl &node) { + node.forEachMethod([this](std::string_view /*unused*/, Function &method) { + method.accept(*this); + }); +} + void SemanticAnalyzer::visit(Module &node) { addDefaultOperators(&node); addRuntimeFunctions(&node, builtinApiJsonFile); + int unitTypeID = node.getType("()").id; + node.forEachClass([this, unitTypeID](std::string_view name, Class & /*unused*/, + bool /*unused*/) { + module->insertType(name, true); + int typeID = module->getType(name).id; + module->addBinaryOperator(Operator::Type::Assignment, typeID, typeID, unitTypeID); + }); + node.forEachClass([this](std::string_view /*unused*/, Class &cls, bool isBuiltin) { + if (isBuiltin) { + return; + } + cls.accept(*this); + }); node.forEachFunction([this]([[maybe_unused]] std::string_view name, Function &function, bool /*unused*/) { @@ -376,6 +531,50 @@ void SemanticAnalyzer::visit(Module &node) { function.setTypeID(module->getType(type->s.contents).id); } }); + node.forEachImpl([this](std::string_view implName, Impl &impl, bool /*unused*/) { + impl.forEachMethod([this, &implName](std::string_view /*unused*/, + Function &method) { + bool first = true; + method.forEachParameter([this, &method, &first, &implName](Symbol ¶meter, + Symbol &type) { + int typeID = module->getType(type.s.contents).id; + if (typeID == -1) { + errorHandler->error(type.s, "Unknown type"); + return; + } + if (first && method.getVariant() == FunctionVariant::CONSTRUCTOR + && parameter.s.contents != "self") { + errorHandler->error(parameter.s, + "First parameter of constructor must be self"); + } + if (first && method.getVariant() == FunctionVariant::CONSTRUCTOR + && type.s.contents != implName) { + errorHandler->error(type.s, "Constructor self parameter type must be " + "the same as impl name"); + } + if (first && method.getVariant() == FunctionVariant::FUNCTION) { + if (parameter.s.contents == "self") { + method.setVariant(FunctionVariant::METHOD); + if (type.s.contents != implName) { + errorHandler->error(type.s, "Method self parameter type must " + "be the same as impl name"); + } + } + } + first = false; + method.getBody().pushSymbol(parameter.s.contents, typeID, + SymbolSource::FunctionParameter); + }); + + Symbol *type = method.getReturnTypeAnnotation(); + if (type == nullptr) { + method.setTypeID(module->getType("()").id); + } else { + method.setTypeID(module->getType(type->s.contents).id); + } + }); + }); + bool hasMain = false; node.forEachFunction( [this, &hasMain](std::string_view name, Function &function, bool isBuiltin) { @@ -392,6 +591,12 @@ void SemanticAnalyzer::visit(Module &node) { } } }); + node.forEachImpl([this](std::string_view /*unused*/, Impl &impl, bool isBuiltin) { + if (isBuiltin) { + return; + } + impl.accept(*this); + }); if (!hasMain) { errorHandler->error(module->getSource(), "No main function"); } @@ -515,9 +720,9 @@ static void addRuntimeFunctions(Module *module, std::istream &builtinApiJsonFile std::string_view returnType = module->ownedStrings.back(); std::unique_ptr returnTypeAnnotation = std::make_unique(Slice(returnType, "", 0, 0)); - std::unique_ptr builtin - = std::make_unique(std::move(parameters), - std::move(returnTypeAnnotation), std::make_unique()); + std::unique_ptr builtin = std::make_unique( + std::move(parameters), std::move(returnTypeAnnotation), + std::make_unique(), FunctionVariant::FUNCTION); module->addFunction(std::make_unique(Slice(functionName, "", 0, 0)), std::move(builtin), true); } diff --git a/src/semanticanalyzer.h b/src/semanticanalyzer.h index 8ac9742..810f61f 100644 --- a/src/semanticanalyzer.h +++ b/src/semanticanalyzer.h @@ -33,11 +33,15 @@ class SemanticAnalyzer : public ASTVisitor { void visit(BlockExpression &node) override; void visit(ReturnExpression &node) override; void visit(ParenthesizedExpression &node) override; + void visit(PathExpression &node) override; + void visit(FieldAccessExpression &node) override; void visit(IfElseExpression &node) override; void visit(WhileExpression &node) override; void visit(ExpressionStatement &node) override; void visit(LetStatement &node) override; void visit(Function &node) override; + void visit(Class &node) override; + void visit(Impl &node) override; void visit(Module &node) override; virtual ~SemanticAnalyzer() = default; }; diff --git a/src/tokens.cpp b/src/tokens.cpp index 4f1d2dc..8a4b09e 100644 --- a/src/tokens.cpp +++ b/src/tokens.cpp @@ -59,6 +59,18 @@ void Keyword::print(std::ostream &os) const { os << "while"; break; } + case Type::CLASS: { + os << "class"; + break; + } + case Type::CONSTRUCTOR: { + os << "constructor"; + break; + } + case Type::IMPL: { + os << "impl"; + break; + } } } diff --git a/src/tokens.h b/src/tokens.h index d9df019..dd17dc6 100644 --- a/src/tokens.h +++ b/src/tokens.h @@ -50,6 +50,9 @@ struct Keyword : public Token { IF, ELSE, WHILE, + CLASS, + CONSTRUCTOR, + IMPL, }; Type type; diff --git a/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_binary_expression/expected/stderr b/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_binary_expression/expected/stderr index 2ddbb26..8100567 100644 --- a/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_binary_expression/expected/stderr +++ b/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_binary_expression/expected/stderr @@ -1 +1 @@ -Error at {main}:4:5: Left side of assignment must be a variable +Error at {main}:4:5: Left side of assignment must be a variable or field access diff --git a/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_function_call/expected/stderr b/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_function_call/expected/stderr index 7cc7bb1..e9e704c 100644 --- a/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_function_call/expected/stderr +++ b/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_function_call/expected/stderr @@ -1 +1 @@ -Error at {main}:2:5: Left side of assignment must be a variable +Error at {main}:2:5: Left side of assignment must be a variable or field access diff --git a/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_integer/expected/stderr b/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_integer/expected/stderr index 7cc7bb1..e9e704c 100644 --- a/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_integer/expected/stderr +++ b/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_integer/expected/stderr @@ -1 +1 @@ -Error at {main}:2:5: Left side of assignment must be a variable +Error at {main}:2:5: Left side of assignment must be a variable or field access diff --git a/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_parenthesized_expression/expected/stderr b/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_parenthesized_expression/expected/stderr index 17b653b..be8fc24 100644 --- a/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_parenthesized_expression/expected/stderr +++ b/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_parenthesized_expression/expected/stderr @@ -1 +1 @@ -Error at {main}:3:5: Left side of assignment must be a variable +Error at {main}:3:5: Left side of assignment must be a variable or field access diff --git a/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_unary_expression/expected/stderr b/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_unary_expression/expected/stderr index 17b653b..be8fc24 100644 --- a/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_unary_expression/expected/stderr +++ b/test/end_to_end_tests/tests/canyon_failure/semantics/assignment_to_unary_expression/expected/stderr @@ -1 +1 @@ -Error at {main}:3:5: Left side of assignment must be a variable +Error at {main}:3:5: Left side of assignment must be a variable or field access diff --git a/test/end_to_end_tests/tests/canyon_failure/semantics/non_symbol_expression_function_call/expected/stderr b/test/end_to_end_tests/tests/canyon_failure/semantics/non_symbol_expression_function_call/expected/stderr index a38245a..cf9ff8a 100644 --- a/test/end_to_end_tests/tests/canyon_failure/semantics/non_symbol_expression_function_call/expected/stderr +++ b/test/end_to_end_tests/tests/canyon_failure/semantics/non_symbol_expression_function_call/expected/stderr @@ -1 +1 @@ -Error at {main}:2:5: Function call target is not a symbol +Error at {main}:2:5: Function call target is not a symbol, path, or method diff --git a/test/end_to_end_tests/tests/canyon_failure/semantics/non_symbol_integer_function_call/expected/stderr b/test/end_to_end_tests/tests/canyon_failure/semantics/non_symbol_integer_function_call/expected/stderr index a38245a..cf9ff8a 100644 --- a/test/end_to_end_tests/tests/canyon_failure/semantics/non_symbol_integer_function_call/expected/stderr +++ b/test/end_to_end_tests/tests/canyon_failure/semantics/non_symbol_integer_function_call/expected/stderr @@ -1 +1 @@ -Error at {main}:2:5: Function call target is not a symbol +Error at {main}:2:5: Function call target is not a symbol, path, or method diff --git a/test/end_to_end_tests/tests/canyon_failure/syntax/class_missing_keyword/expected/stderr b/test/end_to_end_tests/tests/canyon_failure/syntax/class_missing_keyword/expected/stderr new file mode 100644 index 0000000..36e658c --- /dev/null +++ b/test/end_to_end_tests/tests/canyon_failure/syntax/class_missing_keyword/expected/stderr @@ -0,0 +1 @@ +Error at {main}:1:1: Expected keyword `fun` or `class` diff --git a/test/end_to_end_tests/tests/canyon_failure/syntax/class_missing_keyword/main.canyon b/test/end_to_end_tests/tests/canyon_failure/syntax/class_missing_keyword/main.canyon new file mode 100644 index 0000000..495cee6 --- /dev/null +++ b/test/end_to_end_tests/tests/canyon_failure/syntax/class_missing_keyword/main.canyon @@ -0,0 +1 @@ +Foo {} diff --git a/test/end_to_end_tests/tests/canyon_failure/syntax/class_only_data/expected/stderr b/test/end_to_end_tests/tests/canyon_failure/syntax/class_only_data/expected/stderr new file mode 100644 index 0000000..3dd0858 --- /dev/null +++ b/test/end_to_end_tests/tests/canyon_failure/syntax/class_only_data/expected/stderr @@ -0,0 +1,2 @@ +Error at {main}:11:5: Expected symbol for class field declaration +Error at {main}:12:1: Expected keyword `fun` or `class` diff --git a/test/end_to_end_tests/tests/canyon_failure/syntax/class_only_data/main.canyon b/test/end_to_end_tests/tests/canyon_failure/syntax/class_only_data/main.canyon new file mode 100644 index 0000000..1876799 --- /dev/null +++ b/test/end_to_end_tests/tests/canyon_failure/syntax/class_only_data/main.canyon @@ -0,0 +1,12 @@ +fun main() {} + +/* The error on this one is a little weird due to how the parser tries to get itself back + * into a good state after the bad keyword. It looks for the first closing brace, not + * realizing that to the programmer it appears part of the function, not the closing brace + * of the class. So two errors are reported, one for the bad keyword, and one for the + * extra closing brace. + */ + +class Foo { + fun shouldFail() {} +} \ No newline at end of file diff --git a/test/end_to_end_tests/tests/canyon_failure/syntax/function_missing_fun_keyword/expected/stderr b/test/end_to_end_tests/tests/canyon_failure/syntax/function_missing_fun_keyword/expected/stderr index 2ec2831..36e658c 100644 --- a/test/end_to_end_tests/tests/canyon_failure/syntax/function_missing_fun_keyword/expected/stderr +++ b/test/end_to_end_tests/tests/canyon_failure/syntax/function_missing_fun_keyword/expected/stderr @@ -1 +1 @@ -Error at {main}:1:1: Expected keyword `fun` +Error at {main}:1:1: Expected keyword `fun` or `class` diff --git a/test/end_to_end_tests/tests/success/semantics/class_constructor_simple/main.canyon b/test/end_to_end_tests/tests/success/semantics/class_constructor_simple/main.canyon new file mode 100644 index 0000000..9e0e457 --- /dev/null +++ b/test/end_to_end_tests/tests/success/semantics/class_constructor_simple/main.canyon @@ -0,0 +1,9 @@ +fun main() { + let foo: Foo = Foo::new(); +} + +class Foo {} + +impl Foo { + constructor new(self: Foo) {} +} diff --git a/test/end_to_end_tests/tests/success/semantics/class_field_access/main.canyon b/test/end_to_end_tests/tests/success/semantics/class_field_access/main.canyon new file mode 100644 index 0000000..5399916 --- /dev/null +++ b/test/end_to_end_tests/tests/success/semantics/class_field_access/main.canyon @@ -0,0 +1,14 @@ +fun main() { + let foo: Foo = Foo::new(); + let result: i32 = foo.x; +} + +class Foo { + x: i32; +} + +impl Foo { + constructor new(self: Foo) { + self.x = 1; + } +} diff --git a/test/end_to_end_tests/tests/success/semantics/class_instance_method/main.canyon b/test/end_to_end_tests/tests/success/semantics/class_instance_method/main.canyon new file mode 100644 index 0000000..7085927 --- /dev/null +++ b/test/end_to_end_tests/tests/success/semantics/class_instance_method/main.canyon @@ -0,0 +1,18 @@ +fun main() { + let foo: Foo = Foo::new(); + let result: i32 = foo.bar(); +} + +class Foo { + x: i32; +} + +impl Foo { + constructor new(self: Foo) { + self.x = 1; + } + + fun bar(self: Foo): i32 { + self.x + } +} diff --git a/test/end_to_end_tests/tests/success/semantics/class_method_called_after_constructor/main.canyon b/test/end_to_end_tests/tests/success/semantics/class_method_called_after_constructor/main.canyon new file mode 100644 index 0000000..63d755d --- /dev/null +++ b/test/end_to_end_tests/tests/success/semantics/class_method_called_after_constructor/main.canyon @@ -0,0 +1,17 @@ +fun main() { + let result: i32 = Foo::new().bar(); +} + +class Foo { + x: i32; +} + +impl Foo { + constructor new(self: Foo) { + self.x = 1; + } + + fun bar(self: Foo): i32 { + self.x + } +} diff --git a/test/end_to_end_tests/tests/success/semantics/class_nested_field_access/main.canyon b/test/end_to_end_tests/tests/success/semantics/class_nested_field_access/main.canyon new file mode 100644 index 0000000..402efa6 --- /dev/null +++ b/test/end_to_end_tests/tests/success/semantics/class_nested_field_access/main.canyon @@ -0,0 +1,29 @@ +fun main() { + let foo: Foo = Foo::new(5); + let bar: Bar = Bar::new(foo); + let result: i32 = bar.f.x; +} + +class Foo { + x: i32; +} + +class Bar { + f: Foo; +} + +impl Foo { + constructor new(self: Foo, x: i32) { + self.x = x; + } + + fun bar(self: Foo): i32 { + self.x + } +} + +impl Bar { + constructor new(self: Bar, f: Foo) { + self.f = f; + } +} diff --git a/test/end_to_end_tests/tests/success/semantics/method_static/main.canyon b/test/end_to_end_tests/tests/success/semantics/method_static/main.canyon new file mode 100644 index 0000000..6b89a41 --- /dev/null +++ b/test/end_to_end_tests/tests/success/semantics/method_static/main.canyon @@ -0,0 +1,11 @@ +fun main() { + let result: i32 = Foo::bar(1); +} + +class Foo {} + +impl Foo { + fun bar(x: i32): i32 { + x + } +} \ No newline at end of file diff --git a/test/end_to_end_tests/tests/success/syntax/class_simple/main.canyon b/test/end_to_end_tests/tests/success/syntax/class_simple/main.canyon new file mode 100644 index 0000000..b3df0eb --- /dev/null +++ b/test/end_to_end_tests/tests/success/syntax/class_simple/main.canyon @@ -0,0 +1,7 @@ +fun main() { +} + +class Foo { + x: i32; + y: i32; +} diff --git a/test/end_to_end_tests/tests/success/syntax/impl/main.canyon b/test/end_to_end_tests/tests/success/syntax/impl/main.canyon new file mode 100644 index 0000000..213daa5 --- /dev/null +++ b/test/end_to_end_tests/tests/success/syntax/impl/main.canyon @@ -0,0 +1,8 @@ +fun main() {} + +class Foo { +} + +impl Foo { + fun bar() {} +} diff --git a/test/unit_tests/test_lexer.cpp b/test/unit_tests/test_lexer.cpp index 6a09013..3f8087a 100644 --- a/test/unit_tests/test_lexer.cpp +++ b/test/unit_tests/test_lexer.cpp @@ -534,7 +534,10 @@ INSTANTIATE_TEST_SUITE_P(testKeywords, TestLexerKeywords, testing::Values(std::pair{"return", Keyword::Type::RETURN}, std::pair{"let", Keyword::Type::LET}, std::pair{"fun", Keyword::Type::FUN}, std::pair{"if", Keyword::Type::IF}, std::pair{"else", Keyword::Type::ELSE}, - std::pair{"while", Keyword::Type::WHILE})); + std::pair{"while", Keyword::Type::WHILE}, + std::pair{"class", Keyword::Type::CLASS}, + std::pair{"constructor", Keyword::Type::CONSTRUCTOR}, + std::pair{"impl", Keyword::Type::IMPL})); TEST_P(TestLexerSymbols, testSymbols) { const std::string symbol = GetParam();