diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index bd0fc3a..61cd8b5 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(lexer) add_subdirectory(preprocessor) +add_subdirectory(parser) add_subdirectory(compiler_ui) diff --git a/lib/parser/CMakeLists.txt b/lib/parser/CMakeLists.txt new file mode 100644 index 0000000..dcdd52e --- /dev/null +++ b/lib/parser/CMakeLists.txt @@ -0,0 +1,33 @@ +file(GLOB_RECURSE PARSER_SOURCES + "*.cpp" +) + +add_library(parser STATIC + ${PARSER_SOURCES} +) + +target_include_directories(parser + PUBLIC + ${CMAKE_SOURCE_DIR} +) + +target_include_directories(parser + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_link_libraries(parser PUBLIC + lexer + preprocessor + tokens +) + +# Add OvumVM execution_tree if available +if(EXISTS ${CMAKE_SOURCE_DIR}/OvumVM/lib/execution_tree/CMakeLists.txt) + add_subdirectory(${CMAKE_SOURCE_DIR}/OvumVM/lib/execution_tree ${CMAKE_BINARY_DIR}/OvumVM/lib/execution_tree) + target_link_libraries(parser PUBLIC execution_tree) + target_include_directories(parser PUBLIC ${CMAKE_SOURCE_DIR}/OvumVM) +endif() + +target_include_directories(parser PUBLIC ${PROJECT_SOURCE_DIR}) + diff --git a/lib/parser/IParser.hpp b/lib/parser/IParser.hpp new file mode 100644 index 0000000..50bb764 --- /dev/null +++ b/lib/parser/IParser.hpp @@ -0,0 +1,20 @@ +#ifndef PARSER_IPARSER_HPP_ +#define PARSER_IPARSER_HPP_ + +#include + +#include "ast/nodes/decls/Module.hpp" +#include "diagnostics/IDiagnosticSink.hpp" +#include "tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +class IParser { +public: + virtual ~IParser() = default; + virtual std::unique_ptr Parse(ITokenStream& ts, IDiagnosticSink& diags) = 0; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_IPARSER_HPP_ diff --git a/lib/parser/ParserFsm.cpp b/lib/parser/ParserFsm.cpp new file mode 100644 index 0000000..79fa76e --- /dev/null +++ b/lib/parser/ParserFsm.cpp @@ -0,0 +1,65 @@ +#include "ParserFsm.hpp" + +#include +#include + +#include "context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/IState.hpp" +#include "lib/parser/states/base/StateError.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" +#include "recovery/SimpleRecovery.hpp" + +namespace ovum::compiler::parser { + +ParserFsm::ParserFsm(std::unique_ptr expr, + std::unique_ptr typep, + std::unique_ptr factory) : + expr_parser_(std::move(expr)), type_parser_(std::move(typep)), factory_(std::move(factory)) { +} + +std::unique_ptr ParserFsm::Parse(ITokenStream& token_stream, IDiagnosticSink& diagnostics) { + ContextParser context; + context.SetDiagnostics(&diagnostics); + context.SetExpr(expr_parser_.get()); + context.SetTypeParser(type_parser_.get()); + context.SetFactory(factory_.get()); + + SimpleRecovery recovery; + + context.PushState(StateRegistry::Module()); + + while (const IState* state = context.CurrentState()) { + auto step = state->TryStep(context, token_stream); + + if (!step.has_value()) { + const auto& message = step.error().Message(); + diagnostics.Error("P0001", message.empty() ? "parse error" : message); + recovery.SyncToStatementEnd(token_stream); + context.PopState(); + continue; + } + + if (!*step) { + context.PopState(); + } + } + + std::unique_ptr root = context.PopNode(); + + if (!root) { + return std::make_unique(); + } + + auto* as_module = dynamic_cast(root.get()); + + if (as_module != nullptr) { + root.release(); + return std::unique_ptr(as_module); + } + + return std::make_unique(); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ParserFsm.hpp b/lib/parser/ParserFsm.hpp new file mode 100644 index 0000000..a4cba9a --- /dev/null +++ b/lib/parser/ParserFsm.hpp @@ -0,0 +1,31 @@ +#ifndef PARSER_PARSERFSM_HPP_ +#define PARSER_PARSERFSM_HPP_ + +#include + +#include "IParser.hpp" +#include "ast/IAstFactory.hpp" +#include "pratt/IExpressionParser.hpp" +#include "type_parser/ITypeParser.hpp" + +namespace ovum::compiler::parser { + +class ParserFsm : public IParser { +public: + ParserFsm(std::unique_ptr expr, + std::unique_ptr typep, + std::unique_ptr factory); + + ~ParserFsm() override = default; + + std::unique_ptr Parse(ITokenStream& ts, IDiagnosticSink& diags) override; + +private: + std::unique_ptr expr_parser_; + std::unique_ptr type_parser_; + std::unique_ptr factory_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_PARSERFSM_HPP_ diff --git a/lib/parser/ast/AstVisitor.hpp b/lib/parser/ast/AstVisitor.hpp new file mode 100644 index 0000000..a260ca1 --- /dev/null +++ b/lib/parser/ast/AstVisitor.hpp @@ -0,0 +1,98 @@ +#ifndef PARSER_ASTVISITOR_HPP_ +#define PARSER_ASTVISITOR_HPP_ + +#include "nodes/class_members/CallDecl.hpp" +#include "nodes/class_members/DestructorDecl.hpp" +#include "nodes/class_members/FieldDecl.hpp" +#include "nodes/class_members/MethodDecl.hpp" +#include "nodes/class_members/StaticFieldDecl.hpp" +#include "nodes/decls/ClassDecl.hpp" +#include "nodes/decls/FunctionDecl.hpp" +#include "nodes/decls/GlobalVarDecl.hpp" +#include "nodes/decls/InterfaceDecl.hpp" +#include "nodes/decls/Module.hpp" +#include "nodes/decls/TypeAliasDecl.hpp" +#include "nodes/exprs/Assign.hpp" +#include "nodes/exprs/Binary.hpp" +#include "nodes/exprs/Call.hpp" +#include "nodes/exprs/CastAs.hpp" +#include "nodes/exprs/Elvis.hpp" +#include "nodes/exprs/FieldAccess.hpp" +#include "nodes/exprs/IdentRef.hpp" +#include "nodes/exprs/IndexAccess.hpp" +#include "nodes/exprs/NamespaceRef.hpp" +#include "nodes/exprs/SafeCall.hpp" +#include "nodes/exprs/TypeTestIs.hpp" +#include "nodes/exprs/Unary.hpp" +#include "nodes/exprs/literals/BoolLit.hpp" +#include "nodes/exprs/literals/CharLit.hpp" +#include "nodes/exprs/literals/FloatLit.hpp" +#include "nodes/exprs/literals/IntLit.hpp" +#include "nodes/exprs/literals/NullLit.hpp" +#include "nodes/exprs/literals/StringLit.hpp" +#include "nodes/stmts/BreakStmt.hpp" +#include "nodes/stmts/ContinueStmt.hpp" +#include "nodes/stmts/ExprStmt.hpp" +#include "nodes/stmts/ForStmt.hpp" +#include "nodes/stmts/IfStmt.hpp" +#include "nodes/stmts/ReturnStmt.hpp" +#include "nodes/stmts/UnsafeBlock.hpp" +#include "nodes/stmts/VarDeclStmt.hpp" +#include "nodes/stmts/WhileStmt.hpp" + +namespace ovum::compiler::parser { + +class AstVisitor { +public: + virtual ~AstVisitor() = default; + + // Decls + virtual void Visit(Module&) = 0; + virtual void Visit(FunctionDecl&) = 0; + virtual void Visit(ClassDecl&) = 0; + virtual void Visit(InterfaceMethod&) = 0; + virtual void Visit(InterfaceDecl&) = 0; + virtual void Visit(TypeAliasDecl&) = 0; + virtual void Visit(GlobalVarDecl&) = 0; + virtual void Visit(FieldDecl&) = 0; + virtual void Visit(StaticFieldDecl&) = 0; + virtual void Visit(MethodDecl&) = 0; + virtual void Visit(CallDecl&) = 0; + virtual void Visit(DestructorDecl&) = 0; + + // Stmts + virtual void Visit(Block&) = 0; + virtual void Visit(VarDeclStmt&) = 0; + virtual void Visit(ExprStmt&) = 0; + virtual void Visit(ReturnStmt&) = 0; + virtual void Visit(BreakStmt&) = 0; + virtual void Visit(ContinueStmt&) = 0; + virtual void Visit(IfStmt&) = 0; + virtual void Visit(WhileStmt&) = 0; + virtual void Visit(ForStmt&) = 0; + virtual void Visit(UnsafeBlock&) = 0; + + // Exprs + virtual void Visit(Binary&) = 0; + virtual void Visit(Unary&) = 0; + virtual void Visit(Assign&) = 0; + virtual void Visit(Call&) = 0; + virtual void Visit(FieldAccess&) = 0; + virtual void Visit(IndexAccess&) = 0; + virtual void Visit(NamespaceRef&) = 0; + virtual void Visit(SafeCall&) = 0; + virtual void Visit(Elvis&) = 0; + virtual void Visit(CastAs&) = 0; + virtual void Visit(TypeTestIs&) = 0; + virtual void Visit(IdentRef&) = 0; + virtual void Visit(IntLit&) = 0; + virtual void Visit(FloatLit&) = 0; + virtual void Visit(StringLit&) = 0; + virtual void Visit(CharLit&) = 0; + virtual void Visit(BoolLit&) = 0; + virtual void Visit(NullLit&) = 0; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_ASTVISITOR_HPP_ diff --git a/lib/parser/ast/BuilderAstFactory.cpp b/lib/parser/ast/BuilderAstFactory.cpp new file mode 100644 index 0000000..59672e8 --- /dev/null +++ b/lib/parser/ast/BuilderAstFactory.cpp @@ -0,0 +1,428 @@ +#include "BuilderAstFactory.hpp" + +#include + +#include "nodes/builders/base/ParserBuilder.hpp" + +namespace ovum::compiler::parser { + +// Decls / Module + +std::unique_ptr BuilderAstFactory::MakeModule(std::string name, + SourceId source_id, + std::vector> decls, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithName(std::move(name)) + .WithSource(source_id) + .WithDecls(std::move(decls)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeFunction(bool is_pure, + std::string name, + std::vector params, + std::unique_ptr return_type, + std::unique_ptr body, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithIsPure(is_pure) + .WithName(std::move(name)) + .WithParams(std::move(params)) + .WithReturnType(std::move(return_type)) + .WithBody(std::move(body)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeClass(std::string name, + std::vector implements, + std::vector> members, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithName(std::move(name)) + .WithImplements(std::move(implements)) + .WithMembers(std::move(members)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeInterface(std::string name, + std::vector> methods, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithName(std::move(name)) + .WithMethods(std::move(methods)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeInterfaceMethod( + std::string name, + std::vector params, + std::unique_ptr return_type, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithName(std::move(name)) + .WithParams(std::move(params)) + .WithReturnType(std::move(return_type)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeTypeAlias(std::string name, + TypeReference aliased_type, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithName(std::move(name)) + .WithAliasedType(std::move(aliased_type)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeGlobalVar(bool is_var, + std::string name, + TypeReference type, + std::unique_ptr init, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithIsVar(is_var) + .WithName(std::move(name)) + .WithType(std::move(type)) + .WithInit(std::move(init)); + return b.Build(); +} + +// Class members + +std::unique_ptr BuilderAstFactory::MakeField(bool is_public, + bool is_var, + std::string name, + TypeReference type, + std::unique_ptr init, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithIsPublic(is_public) + .WithIsVar(is_var) + .WithName(std::move(name)) + .WithType(std::move(type)) + .WithInit(std::move(init)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeStaticField(bool is_public, + bool is_var, + std::string name, + TypeReference type, + std::unique_ptr init, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithIsPublic(is_public) + .WithIsVar(is_var) + .WithName(std::move(name)) + .WithType(std::move(type)) + .WithInit(std::move(init)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeMethod(bool is_public, + bool is_override, + bool is_static, + bool is_pure, + std::string name, + std::vector params, + std::unique_ptr ret_type, + std::unique_ptr body, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithIsPublic(is_public) + .WithIsOverride(is_override) + .WithIsStatic(is_static) + .WithIsPure(is_pure) + .WithName(std::move(name)) + .WithParams(std::move(params)) + .WithReturnType(std::move(ret_type)) + .WithBody(std::move(body)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeCallDecl(bool is_public, + std::vector params, + std::unique_ptr ret_type, + std::unique_ptr body, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithIsPublic(is_public) + .WithParams(std::move(params)) + .WithReturnType(std::move(ret_type)) + .WithBody(std::move(body)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeDestructor(bool is_public, + std::unique_ptr body, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithIsPublic(is_public) + .WithBody(std::move(body)); + return b.Build(); +} + +// Statements + +std::unique_ptr BuilderAstFactory::MakeBlock(std::vector> stmts, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span).WithStatements(std::move(stmts)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeVarDeclStmt(bool is_var, + std::string name, + TypeReference type, + std::unique_ptr init, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithIsVar(is_var) + .WithName(std::move(name)) + .WithType(std::move(type)) + .WithInit(std::move(init)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeExprStmt(std::unique_ptr expr, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span).WithExpr(std::move(expr)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeReturnStmt(std::unique_ptr value, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span).WithValue(std::move(value)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeBreakStmt(SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeContinueStmt(SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeIfStmt(std::vector branches, + std::unique_ptr else_block, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithBranches(std::move(branches)) + .WithElse(std::move(else_block)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeWhileStmt(std::unique_ptr cond, + std::unique_ptr body, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithCondition(std::move(cond)) + .WithBody(std::move(body)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeForStmt(std::string iter_name, + std::unique_ptr iter_expr, + std::unique_ptr body, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithIteratorName(std::move(iter_name)) + .WithIteratorExpr(std::move(iter_expr)) + .WithBody(std::move(body)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeUnsafeBlock(std::unique_ptr body, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span).WithBody(std::move(body)); + return b.Build(); +} + +// Expressions + +std::unique_ptr BuilderAstFactory::MakeBinary(const IBinaryOpTag& op, + std::unique_ptr lhs, + std::unique_ptr rhs, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithOp(op) + .WithLhs(std::move(lhs)) + .WithRhs(std::move(rhs)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeUnary(const IUnaryOpTag& op, + std::unique_ptr operand, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithOp(op) + .WithOperand(std::move(operand)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeAssign(const IAssignOpTag& op, + std::unique_ptr target, + std::unique_ptr value, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithKind(op) + .WithTarget(std::move(target)) + .WithValue(std::move(value)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeCall(std::unique_ptr callee, + std::vector> args, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithCallee(std::move(callee)) + .WithArgs(std::move(args)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeFieldAccess(std::unique_ptr object, + std::string name, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithObject(std::move(object)) + .WithName(std::move(name)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeIndexAccess(std::unique_ptr object, + std::unique_ptr index, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithObject(std::move(object)) + .WithIndex(std::move(index)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeNamespaceRef(std::unique_ptr ns, + std::string name, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithNamespace(std::move(ns)) + .WithName(std::move(name)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeSafeCall(std::unique_ptr object, + std::string method, + std::vector> args, + std::optional inferred_type, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithObject(std::move(object)) + .WithMethod(std::move(method)) + .WithArgs(std::move(args)) + .WithInferredType(std::move(inferred_type)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeElvis(std::unique_ptr lhs, + std::unique_ptr rhs, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithLhs(std::move(lhs)) + .WithRhs(std::move(rhs)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeCastAs(std::unique_ptr expr, + TypeReference type, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithExpr(std::move(expr)) + .WithType(std::move(type)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeTypeTestIs(std::unique_ptr expr, + TypeReference type, + SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span) + .WithExpr(std::move(expr)) + .WithType(std::move(type)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeIdent(std::string name, SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span).WithName(std::move(name)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeInt(long long v, SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span).WithValue(v); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeFloat(long double v, SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span).WithValue(v); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeString(std::string v, SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span).WithValue(std::move(v)); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeChar(char v, SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span).WithValue(v); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeBool(bool v, SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span).WithValue(v); + return b.Build(); +} + +std::unique_ptr BuilderAstFactory::MakeNull(SourceSpan span) { + auto b = ParserBuilder::Make(); + b.WithSpan(span); + return b.Build(); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/BuilderAstFactory.hpp b/lib/parser/ast/BuilderAstFactory.hpp new file mode 100644 index 0000000..1da7dc2 --- /dev/null +++ b/lib/parser/ast/BuilderAstFactory.hpp @@ -0,0 +1,160 @@ +#ifndef PARSER_BUILDERASTFACTORY_HPP_ +#define PARSER_BUILDERASTFACTORY_HPP_ + +#include +#include +#include +#include + +#include "IAstFactory.hpp" + +namespace ovum::compiler::parser { + +class BuilderAstFactory : public IAstFactory { +public: + ~BuilderAstFactory() override = default; + + // Module / Decls + std::unique_ptr MakeModule(std::string name, + SourceId source_id, + std::vector> decls, + SourceSpan span) override; + + std::unique_ptr MakeFunction(bool is_pure, + std::string name, + std::vector params, + std::unique_ptr return_type, + std::unique_ptr body, + SourceSpan span) override; + + std::unique_ptr MakeClass(std::string name, + std::vector implements, + std::vector> members, + SourceSpan span) override; + + std::unique_ptr MakeInterface(std::string name, + std::vector> methods, + SourceSpan span) override; + + std::unique_ptr MakeInterfaceMethod(std::string name, + std::vector params, + std::unique_ptr return_type, + SourceSpan span) override; + + std::unique_ptr MakeTypeAlias(std::string name, TypeReference aliased_type, SourceSpan span) override; + + std::unique_ptr MakeGlobalVar( + bool is_var, std::string name, TypeReference type, std::unique_ptr init, SourceSpan span) override; + + // Class members + std::unique_ptr MakeField(bool is_public, + bool is_var, + std::string name, + TypeReference type, + std::unique_ptr init, + SourceSpan span) override; + + std::unique_ptr MakeStaticField(bool is_public, + bool is_var, + std::string name, + TypeReference type, + std::unique_ptr init, + SourceSpan span) override; + + std::unique_ptr MakeMethod(bool is_public, + bool is_override, + bool is_static, + bool is_pure, + std::string name, + std::vector params, + std::unique_ptr ret_type, + std::unique_ptr body, + SourceSpan span) override; + + std::unique_ptr MakeCallDecl(bool is_public, + std::vector params, + std::unique_ptr ret_type, + std::unique_ptr body, + SourceSpan span) override; + + std::unique_ptr MakeDestructor(bool is_public, std::unique_ptr body, SourceSpan span) override; + + // Statements + std::unique_ptr MakeBlock(std::vector> stmts, SourceSpan span) override; + + std::unique_ptr MakeVarDeclStmt( + bool is_var, std::string name, TypeReference type, std::unique_ptr init, SourceSpan span) override; + + std::unique_ptr MakeExprStmt(std::unique_ptr expr, SourceSpan span) override; + + std::unique_ptr MakeReturnStmt(std::unique_ptr value, SourceSpan span) override; + + std::unique_ptr MakeBreakStmt(SourceSpan span) override; + std::unique_ptr MakeContinueStmt(SourceSpan span) override; + + std::unique_ptr MakeIfStmt(std::vector branches, + std::unique_ptr else_block, + SourceSpan span) override; + + std::unique_ptr MakeWhileStmt(std::unique_ptr cond, + std::unique_ptr body, + SourceSpan span) override; + + std::unique_ptr MakeForStmt(std::string iter_name, + std::unique_ptr iter_expr, + std::unique_ptr body, + SourceSpan span) override; + + std::unique_ptr MakeUnsafeBlock(std::unique_ptr body, SourceSpan span) override; + + // Expressions + std::unique_ptr MakeBinary(const IBinaryOpTag& op, + std::unique_ptr lhs, + std::unique_ptr rhs, + SourceSpan span) override; + + std::unique_ptr MakeUnary(const IUnaryOpTag& op, std::unique_ptr operand, SourceSpan span) override; + + std::unique_ptr MakeAssign(const IAssignOpTag& op, + std::unique_ptr target, + std::unique_ptr value, + SourceSpan span) override; + + std::unique_ptr MakeCall(std::unique_ptr callee, + std::vector> args, + SourceSpan span) override; + + std::unique_ptr MakeFieldAccess(std::unique_ptr object, + std::string name, + SourceSpan span) override; + + std::unique_ptr MakeIndexAccess(std::unique_ptr object, + std::unique_ptr index, + SourceSpan span) override; + + std::unique_ptr MakeNamespaceRef(std::unique_ptr ns, std::string name, SourceSpan span) override; + + std::unique_ptr MakeSafeCall(std::unique_ptr object, + std::string method, + std::vector> args, + std::optional inferred_type, + SourceSpan span) override; + + std::unique_ptr MakeElvis(std::unique_ptr lhs, std::unique_ptr rhs, SourceSpan span) override; + + std::unique_ptr MakeCastAs(std::unique_ptr expr, TypeReference type, SourceSpan span) override; + + std::unique_ptr MakeTypeTestIs(std::unique_ptr expr, TypeReference type, SourceSpan span) override; + + std::unique_ptr MakeIdent(std::string name, SourceSpan span) override; + std::unique_ptr MakeInt(long long v, SourceSpan span) override; + std::unique_ptr MakeFloat(long double v, SourceSpan span) override; + std::unique_ptr MakeString(std::string v, SourceSpan span) override; + std::unique_ptr MakeChar(char v, SourceSpan span) override; + std::unique_ptr MakeBool(bool v, SourceSpan span) override; + std::unique_ptr MakeNull(SourceSpan span) override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_BUILDERASTFACTORY_HPP_ diff --git a/lib/parser/ast/IAstFactory.hpp b/lib/parser/ast/IAstFactory.hpp new file mode 100644 index 0000000..1121465 --- /dev/null +++ b/lib/parser/ast/IAstFactory.hpp @@ -0,0 +1,213 @@ +#ifndef PARSER_IASTFACTORY_HPP_ +#define PARSER_IASTFACTORY_HPP_ + +#include +#include +#include +#include + +#include "nodes/class_members/CallDecl.hpp" +#include "nodes/class_members/DestructorDecl.hpp" +#include "nodes/class_members/FieldDecl.hpp" +#include "nodes/class_members/MethodDecl.hpp" +#include "nodes/class_members/StaticFieldDecl.hpp" +#include "nodes/decls/ClassDecl.hpp" +#include "nodes/decls/FunctionDecl.hpp" +#include "nodes/decls/GlobalVarDecl.hpp" +#include "nodes/decls/InterfaceDecl.hpp" +#include "nodes/decls/Module.hpp" +#include "nodes/decls/TypeAliasDecl.hpp" +#include "nodes/exprs/Assign.hpp" +#include "nodes/exprs/Binary.hpp" +#include "nodes/exprs/Call.hpp" +#include "nodes/exprs/CastAs.hpp" +#include "nodes/exprs/Elvis.hpp" +#include "nodes/exprs/FieldAccess.hpp" +#include "nodes/exprs/IdentRef.hpp" +#include "nodes/exprs/IndexAccess.hpp" +#include "nodes/exprs/NamespaceRef.hpp" +#include "nodes/exprs/SafeCall.hpp" +#include "nodes/exprs/TypeTestIs.hpp" +#include "nodes/exprs/Unary.hpp" +#include "nodes/exprs/literals/BoolLit.hpp" +#include "nodes/exprs/literals/CharLit.hpp" +#include "nodes/exprs/literals/FloatLit.hpp" +#include "nodes/exprs/literals/IntLit.hpp" +#include "nodes/exprs/literals/NullLit.hpp" +#include "nodes/exprs/literals/StringLit.hpp" +#include "nodes/stmts/Block.hpp" +#include "nodes/stmts/BreakStmt.hpp" +#include "nodes/stmts/ContinueStmt.hpp" +#include "nodes/stmts/ExprStmt.hpp" +#include "nodes/stmts/ForStmt.hpp" +#include "nodes/stmts/IfStmt.hpp" +#include "nodes/stmts/ReturnStmt.hpp" +#include "nodes/stmts/UnsafeBlock.hpp" +#include "nodes/stmts/VarDeclStmt.hpp" +#include "nodes/stmts/WhileStmt.hpp" + +#include "lib/parser/ast/nodes/exprs/tags/IAssignOpTag.hpp" +#include "lib/parser/ast/nodes/exprs/tags/IBinaryOpTag.hpp" +#include "lib/parser/ast/nodes/exprs/tags/IUnaryOpTag.hpp" +#include "lib/parser/tokens/SourceSpan.hpp" +#include "lib/parser/types/Param.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class IAstFactory { +public: + virtual ~IAstFactory() = default; + + // Module / Decls + virtual std::unique_ptr MakeModule(std::string name, + SourceId source_id, + std::vector> decls, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeFunction(bool is_pure, + std::string name, + std::vector params, + std::unique_ptr return_type, + std::unique_ptr body, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeClass(std::string name, + std::vector implements, + std::vector> members, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeInterface(std::string name, + std::vector> methods, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeInterfaceMethod(std::string name, + std::vector params, + std::unique_ptr return_type, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeTypeAlias(std::string name, + TypeReference aliased_type, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeGlobalVar( + bool is_var, std::string name, TypeReference type, std::unique_ptr init, SourceSpan span) = 0; + + // Class members + virtual std::unique_ptr MakeField(bool is_public, + bool is_var, + std::string name, + TypeReference type, + std::unique_ptr init, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeStaticField(bool is_public, + bool is_var, + std::string name, + TypeReference type, + std::unique_ptr init, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeMethod(bool is_public, + bool is_override, + bool is_static, + bool is_pure, + std::string name, + std::vector params, + std::unique_ptr ret_type, + std::unique_ptr body, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeCallDecl(bool is_public, + std::vector params, + std::unique_ptr ret_type, + std::unique_ptr body, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeDestructor(bool is_public, + std::unique_ptr body, + SourceSpan span) = 0; + + // Statements + virtual std::unique_ptr MakeBlock(std::vector> stmts, SourceSpan span) = 0; + + virtual std::unique_ptr MakeVarDeclStmt( + bool is_var, std::string name, TypeReference type, std::unique_ptr init, SourceSpan span) = 0; + + virtual std::unique_ptr MakeExprStmt(std::unique_ptr expr, SourceSpan span) = 0; + + virtual std::unique_ptr MakeReturnStmt(std::unique_ptr value, SourceSpan span) = 0; + + virtual std::unique_ptr MakeBreakStmt(SourceSpan span) = 0; + virtual std::unique_ptr MakeContinueStmt(SourceSpan span) = 0; + + virtual std::unique_ptr MakeIfStmt(std::vector branches, + std::unique_ptr else_block, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeWhileStmt(std::unique_ptr cond, + std::unique_ptr body, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeForStmt(std::string iter_name, + std::unique_ptr iter_expr, + std::unique_ptr body, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeUnsafeBlock(std::unique_ptr body, SourceSpan span) = 0; + + // Expressions + virtual std::unique_ptr MakeBinary(const IBinaryOpTag& op, + std::unique_ptr lhs, + std::unique_ptr rhs, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeUnary(const IUnaryOpTag& op, std::unique_ptr operand, SourceSpan span) = 0; + + virtual std::unique_ptr MakeAssign(const IAssignOpTag& op, + std::unique_ptr target, + std::unique_ptr value, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeCall(std::unique_ptr callee, + std::vector> args, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeFieldAccess(std::unique_ptr object, + std::string name, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeIndexAccess(std::unique_ptr object, + std::unique_ptr index, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeNamespaceRef(std::unique_ptr ns, + std::string name, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeSafeCall(std::unique_ptr object, + std::string method, + std::vector> args, + std::optional inferred_type, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeElvis(std::unique_ptr lhs, std::unique_ptr rhs, SourceSpan span) = 0; + + virtual std::unique_ptr MakeCastAs(std::unique_ptr expr, TypeReference type, SourceSpan span) = 0; + + virtual std::unique_ptr MakeTypeTestIs(std::unique_ptr expr, + TypeReference type, + SourceSpan span) = 0; + + virtual std::unique_ptr MakeIdent(std::string name, SourceSpan span) = 0; + virtual std::unique_ptr MakeInt(long long v, SourceSpan span) = 0; + virtual std::unique_ptr MakeFloat(long double v, SourceSpan span) = 0; + virtual std::unique_ptr MakeString(std::string v, SourceSpan span) = 0; + virtual std::unique_ptr MakeChar(char v, SourceSpan span) = 0; + virtual std::unique_ptr MakeBool(bool v, SourceSpan span) = 0; + virtual std::unique_ptr MakeNull(SourceSpan span) = 0; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_IASTFACTORY_HPP_ diff --git a/lib/parser/ast/nodes/base/AstNode.cpp b/lib/parser/ast/nodes/base/AstNode.cpp new file mode 100644 index 0000000..da2c826 --- /dev/null +++ b/lib/parser/ast/nodes/base/AstNode.cpp @@ -0,0 +1,20 @@ +#include "lib/parser/ast/nodes/base/AstNode.hpp" + +namespace ovum::compiler::parser { +const SourceSpan& AstNode::Span() const noexcept { + return span_; +} + +void AstNode::SetSpan(SourceSpan span) { + span_ = std::move(span); +} + +void AstNode::SetSpanParts(SourceId id, TokenPosition begin, TokenPosition end) { + span_ = SourceSpan(std::move(id), begin, end); +} + +void AstNode::UnionSpan(const SourceSpan& other) { + span_ = SourceSpan::Union(span_, other); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/base/AstNode.hpp b/lib/parser/ast/nodes/base/AstNode.hpp new file mode 100644 index 0000000..ab57997 --- /dev/null +++ b/lib/parser/ast/nodes/base/AstNode.hpp @@ -0,0 +1,27 @@ +#ifndef PARSER_ASTNODE_HPP_ +#define PARSER_ASTNODE_HPP_ + +#include "lib/parser/tokens/SourceSpan.hpp" + +namespace ovum::compiler::parser { + +class AstVisitor; // forward + +class AstNode { +public: + virtual ~AstNode() = default; + + const SourceSpan& Span() const noexcept; + void SetSpan(SourceSpan span); + void SetSpanParts(SourceId id, TokenPosition begin, TokenPosition end); + void UnionSpan(const SourceSpan& other); + + virtual void Accept(AstVisitor& visitor) = 0; + +private: + SourceSpan span_{}; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_ASTNODE_HPP_ diff --git a/lib/parser/ast/nodes/base/Decl.hpp b/lib/parser/ast/nodes/base/Decl.hpp new file mode 100644 index 0000000..26ac67e --- /dev/null +++ b/lib/parser/ast/nodes/base/Decl.hpp @@ -0,0 +1,12 @@ +#ifndef PARSER_DECL_HPP_ +#define PARSER_DECL_HPP_ + +#include "AstNode.hpp" + +namespace ovum::compiler::parser { + +class Decl : public AstNode {}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_DECL_HPP_ diff --git a/lib/parser/ast/nodes/base/Expr.hpp b/lib/parser/ast/nodes/base/Expr.hpp new file mode 100644 index 0000000..2d1917f --- /dev/null +++ b/lib/parser/ast/nodes/base/Expr.hpp @@ -0,0 +1,12 @@ +#ifndef PARSER_EXPR_HPP_ +#define PARSER_EXPR_HPP_ + +#include "AstNode.hpp" + +namespace ovum::compiler::parser { + +class Expr : public AstNode {}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_EXPR_HPP_ diff --git a/lib/parser/ast/nodes/base/Stmt.hpp b/lib/parser/ast/nodes/base/Stmt.hpp new file mode 100644 index 0000000..19a7b2d --- /dev/null +++ b/lib/parser/ast/nodes/base/Stmt.hpp @@ -0,0 +1,12 @@ +#ifndef PARSER_STMT_HPP_ +#define PARSER_STMT_HPP_ + +#include "AstNode.hpp" + +namespace ovum::compiler::parser { + +class Stmt : public AstNode {}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STMT_HPP_ diff --git a/lib/parser/ast/nodes/builders/base/BuilderTraits.hpp b/lib/parser/ast/nodes/builders/base/BuilderTraits.hpp new file mode 100644 index 0000000..3e762c8 --- /dev/null +++ b/lib/parser/ast/nodes/builders/base/BuilderTraits.hpp @@ -0,0 +1,313 @@ +#ifndef PARSER_BUILDERTRAITS_HPP_ +#define PARSER_BUILDERTRAITS_HPP_ + +#include + +#include "lib/parser/ast/nodes/builders/class_members/CallDeclBuilder.hpp" +#include "lib/parser/ast/nodes/builders/class_members/DestructorDeclBuilder.hpp" +#include "lib/parser/ast/nodes/builders/class_members/FieldDeclBuilder.hpp" +#include "lib/parser/ast/nodes/builders/class_members/MethodDeclBuilder.hpp" +#include "lib/parser/ast/nodes/builders/class_members/StaticFieldDeclBuilder.hpp" + +#include "lib/parser/ast/nodes/builders/decls/ClassDeclBuilder.hpp" +#include "lib/parser/ast/nodes/builders/decls/FunctionDeclBuilder.hpp" +#include "lib/parser/ast/nodes/builders/decls/GlobalVarDeclBuilder.hpp" +#include "lib/parser/ast/nodes/builders/decls/InterfaceDeclBuilder.hpp" +#include "lib/parser/ast/nodes/builders/decls/InterfaceMethodBuilder.hpp" +#include "lib/parser/ast/nodes/builders/decls/ModuleBuilder.hpp" +#include "lib/parser/ast/nodes/builders/decls/TypeAliasDeclBuilder.hpp" + +#include "lib/parser/ast/nodes/builders/exprs/AssignBuilder.hpp" +#include "lib/parser/ast/nodes/builders/exprs/BinaryBuilder.hpp" +#include "lib/parser/ast/nodes/builders/exprs/CallExprBuilder.hpp" +#include "lib/parser/ast/nodes/builders/exprs/CastAsBuilder.hpp" +#include "lib/parser/ast/nodes/builders/exprs/ElvisBuilder.hpp" +#include "lib/parser/ast/nodes/builders/exprs/FieldAccessBuilder.hpp" +#include "lib/parser/ast/nodes/builders/exprs/IdentRefBuilder.hpp" +#include "lib/parser/ast/nodes/builders/exprs/IndexAccessBuilder.hpp" +#include "lib/parser/ast/nodes/builders/exprs/NamespaceRefBuilder.hpp" +#include "lib/parser/ast/nodes/builders/exprs/SafeCallBuilder.hpp" +#include "lib/parser/ast/nodes/builders/exprs/TypeTestIsBuilder.hpp" +#include "lib/parser/ast/nodes/builders/exprs/UnaryBuilder.hpp" +#include "lib/parser/ast/nodes/builders/exprs/literals/BoolLitBuilder.hpp" +#include "lib/parser/ast/nodes/builders/exprs/literals/CharLitBuilder.hpp" +#include "lib/parser/ast/nodes/builders/exprs/literals/FloatLitBuilder.hpp" +#include "lib/parser/ast/nodes/builders/exprs/literals/IntLitBuilder.hpp" +#include "lib/parser/ast/nodes/builders/exprs/literals/NullLitBuilder.hpp" +#include "lib/parser/ast/nodes/builders/exprs/literals/StringLitBuilder.hpp" + +#include "lib/parser/ast/nodes/builders/stmts/BlockBuilder.hpp" +#include "lib/parser/ast/nodes/builders/stmts/BreakStmtBuilder.hpp" +#include "lib/parser/ast/nodes/builders/stmts/ContinueStmtBuilder.hpp" +#include "lib/parser/ast/nodes/builders/stmts/ExprStmtBuilder.hpp" +#include "lib/parser/ast/nodes/builders/stmts/ForStmtBuilder.hpp" +#include "lib/parser/ast/nodes/builders/stmts/IfStmtBuilder.hpp" +#include "lib/parser/ast/nodes/builders/stmts/ReturnStmtBuilder.hpp" +#include "lib/parser/ast/nodes/builders/stmts/UnsafeBlockBuilder.hpp" +#include "lib/parser/ast/nodes/builders/stmts/VarDeclStmtBuilder.hpp" +#include "lib/parser/ast/nodes/builders/stmts/WhileStmtBuilder.hpp" + +#include "lib/parser/ast/nodes/class_members/CallDecl.hpp" +#include "lib/parser/ast/nodes/class_members/DestructorDecl.hpp" +#include "lib/parser/ast/nodes/class_members/FieldDecl.hpp" +#include "lib/parser/ast/nodes/class_members/MethodDecl.hpp" +#include "lib/parser/ast/nodes/class_members/StaticFieldDecl.hpp" + +#include "lib/parser/ast/nodes/decls/ClassDecl.hpp" +#include "lib/parser/ast/nodes/decls/FunctionDecl.hpp" +#include "lib/parser/ast/nodes/decls/GlobalVarDecl.hpp" +#include "lib/parser/ast/nodes/decls/InterfaceDecl.hpp" +#include "lib/parser/ast/nodes/decls/InterfaceMethod.hpp" +#include "lib/parser/ast/nodes/decls/Module.hpp" +#include "lib/parser/ast/nodes/decls/TypeAliasDecl.hpp" + +#include "lib/parser/ast/nodes/exprs/Assign.hpp" +#include "lib/parser/ast/nodes/exprs/Binary.hpp" +#include "lib/parser/ast/nodes/exprs/Call.hpp" +#include "lib/parser/ast/nodes/exprs/CastAs.hpp" +#include "lib/parser/ast/nodes/exprs/Elvis.hpp" +#include "lib/parser/ast/nodes/exprs/FieldAccess.hpp" +#include "lib/parser/ast/nodes/exprs/IdentRef.hpp" +#include "lib/parser/ast/nodes/exprs/IndexAccess.hpp" +#include "lib/parser/ast/nodes/exprs/NamespaceRef.hpp" +#include "lib/parser/ast/nodes/exprs/SafeCall.hpp" +#include "lib/parser/ast/nodes/exprs/TypeTestIs.hpp" +#include "lib/parser/ast/nodes/exprs/Unary.hpp" +#include "lib/parser/ast/nodes/exprs/literals/BoolLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/CharLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/FloatLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/IntLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/NullLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/StringLit.hpp" + +#include "lib/parser/ast/nodes/stmts/BreakStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ContinueStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ExprStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ForStmt.hpp" +#include "lib/parser/ast/nodes/stmts/IfStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ReturnStmt.hpp" +#include "lib/parser/ast/nodes/stmts/UnsafeBlock.hpp" +#include "lib/parser/ast/nodes/stmts/VarDeclStmt.hpp" +#include "lib/parser/ast/nodes/stmts/WhileStmt.hpp" + +namespace ovum::compiler::parser { + +template +struct BuilderFor; + +// Decls +template<> +struct BuilderFor { + using type = ModuleBuilder; +}; + +template<> +struct BuilderFor { + using type = FunctionDeclBuilder; +}; + +template<> +struct BuilderFor { + using type = ClassDeclBuilder; +}; + +template<> +struct BuilderFor { + using type = GlobalVarDeclBuilder; +}; + +template<> +struct BuilderFor { + using type = InterfaceDeclBuilder; +}; + +template<> +struct BuilderFor { + using type = InterfaceMethodBuilder; +}; + +template<> +struct BuilderFor { + using type = TypeAliasDeclBuilder; +}; + +template<> +struct BuilderFor { + using type = FieldDeclBuilder; +}; + +template<> +struct BuilderFor { + using type = StaticFieldDeclBuilder; +}; + +template<> +struct BuilderFor { + using type = MethodDeclBuilder; +}; + +template<> +struct BuilderFor { + using type = CallDeclBuilder; +}; + +template<> +struct BuilderFor { + using type = DestructorDeclBuilder; +}; + +// Stmts +template<> +struct BuilderFor { + using type = BlockBuilder; +}; + +template<> +struct BuilderFor { + using type = BreakStmtBuilder; +}; + +template<> +struct BuilderFor { + using type = ContinueStmtBuilder; +}; + +template<> +struct BuilderFor { + using type = ExprStmtBuilder; +}; + +template<> +struct BuilderFor { + using type = ForStmtBuilder; +}; + +template<> +struct BuilderFor { + using type = IfStmtBuilder; +}; + +template<> +struct BuilderFor { + using type = ReturnStmtBuilder; +}; + +template<> +struct BuilderFor { + using type = UnsafeBlockBuilder; +}; + +template<> +struct BuilderFor { + using type = VarDeclStmtBuilder; +}; + +template<> +struct BuilderFor { + using type = WhileStmtBuilder; +}; + +// Exprs +template<> +struct BuilderFor { + using type = AssignBuilder; +}; + +template<> +struct BuilderFor { + using type = BinaryBuilder; +}; + +template<> +struct BuilderFor { + using type = CallExprBuilder; +}; + +template<> +struct BuilderFor { + using type = CastAsBuilder; +}; + +template<> +struct BuilderFor { + using type = ElvisBuilder; +}; + +template<> +struct BuilderFor { + using type = FieldAccessBuilder; +}; + +template<> +struct BuilderFor { + using type = IdentRefBuilder; +}; + +template<> +struct BuilderFor { + using type = IndexAccessBuilder; +}; + +template<> +struct BuilderFor { + using type = NamespaceRefBuilder; +}; + +template<> +struct BuilderFor { + using type = SafeCallBuilder; +}; + +template<> +struct BuilderFor { + using type = TypeTestIsBuilder; +}; + +template<> +struct BuilderFor { + using type = UnaryBuilder; +}; + +// Literals +template<> +struct BuilderFor { + using type = BoolLitBuilder; +}; + +template<> +struct BuilderFor { + using type = CharLitBuilder; +}; + +template<> +struct BuilderFor { + using type = FloatLitBuilder; +}; + +template<> +struct BuilderFor { + using type = IntLitBuilder; +}; + +template<> +struct BuilderFor { + using type = NullLitBuilder; +}; + +template<> +struct BuilderFor { + using type = StringLitBuilder; +}; + +template +struct HasBuilder : std::false_type {}; + +template +struct HasBuilder::type>> : std::true_type {}; + +template +inline constexpr bool kHasBuilderV = HasBuilder::value; + +} // namespace ovum::compiler::parser + +#endif // PARSER_BUILDERTRAITS_HPP_ diff --git a/lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp b/lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp new file mode 100644 index 0000000..f8ee0e4 --- /dev/null +++ b/lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp @@ -0,0 +1,48 @@ +#ifndef PARSER_NODEBUILDERBASE_HPP_ +#define PARSER_NODEBUILDERBASE_HPP_ + +#include + +#include "TokenPosition.hpp" +#include "lib/parser/tokens/SourceId.hpp" +#include "lib/parser/tokens/SourceSpan.hpp" + +namespace ovum::compiler::parser { + +template +class NodeBuilderBase { +public: + NodeBuilderBase() : node_(std::make_unique()) { + } + + explicit NodeBuilderBase(std::unique_ptr node) : node_(std::move(node)) { + } + + NodeBuilderBase& WithSpan(const SourceSpan& span) { + node_->SetSpan(span); + return *this; + } + + NodeBuilderBase& WithSpanParts(SourceId id, TokenPosition begin, TokenPosition end) { + node_->SetSpanParts(std::move(id), begin, end); + return *this; + } + + std::unique_ptr Finish() { + return std::move(node_); + } + + std::unique_ptr Build() { + return Finish(); + } + + T* Raw() { + return node_.get(); + } + +protected: + std::unique_ptr node_; +}; +} // namespace ovum::compiler::parser + +#endif // PARSER_NODEBUILDERBASE_HPP_ diff --git a/lib/parser/ast/nodes/builders/base/ParserBuilder.hpp b/lib/parser/ast/nodes/builders/base/ParserBuilder.hpp new file mode 100644 index 0000000..a55951f --- /dev/null +++ b/lib/parser/ast/nodes/builders/base/ParserBuilder.hpp @@ -0,0 +1,24 @@ +#ifndef PARSER_PARSERBUILDER_HPP_ +#define PARSER_PARSERBUILDER_HPP_ + +#include "BuilderTraits.hpp" + +namespace ovum::compiler::parser { + +class ParserBuilder { +public: + ParserBuilder() = delete; + + template + using BuilderType = typename BuilderFor::type; + + template + static BuilderType Make() { + static_assert(kHasBuilderV, "No builder registered for this AST node type"); + return BuilderType{}; + } +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_PARSERBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/class_members/CallDeclBuilder.cpp b/lib/parser/ast/nodes/builders/class_members/CallDeclBuilder.cpp new file mode 100644 index 0000000..2a693c1 --- /dev/null +++ b/lib/parser/ast/nodes/builders/class_members/CallDeclBuilder.cpp @@ -0,0 +1,36 @@ +#include "CallDeclBuilder.hpp" + +namespace ovum::compiler::parser { + +CallDeclBuilder& CallDeclBuilder::WithPublic(bool is_public) { + node_->SetPublic(is_public); + return *this; +} + +CallDeclBuilder& CallDeclBuilder::WithIsPublic(bool is_public) { + return WithPublic(is_public); +} + +CallDeclBuilder& CallDeclBuilder::AddParam(Param parameter) { + node_->MutableParams().push_back(std::move(parameter)); + return *this; +} + +CallDeclBuilder& CallDeclBuilder::WithParams(std::vector params) { + for (auto& param : params) { + node_->MutableParams().push_back(std::move(param)); + } + return *this; +} + +CallDeclBuilder& CallDeclBuilder::WithReturnType(std::unique_ptr type) { + node_->SetReturnType(std::move(type)); + return *this; +} + +CallDeclBuilder& CallDeclBuilder::WithBody(std::unique_ptr body) { + node_->SetBody(std::move(body)); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/class_members/CallDeclBuilder.hpp b/lib/parser/ast/nodes/builders/class_members/CallDeclBuilder.hpp new file mode 100644 index 0000000..bdc9e06 --- /dev/null +++ b/lib/parser/ast/nodes/builders/class_members/CallDeclBuilder.hpp @@ -0,0 +1,26 @@ +#ifndef PARSER_CALLDECLBUILDER_HPP_ +#define PARSER_CALLDECLBUILDER_HPP_ + +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/class_members/CallDecl.hpp" +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/types/Param.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class CallDeclBuilder : public NodeBuilderBase { +public: + CallDeclBuilder& WithPublic(bool is_public); + CallDeclBuilder& WithIsPublic(bool is_public); + CallDeclBuilder& AddParam(Param parameter); + CallDeclBuilder& WithParams(std::vector params); + CallDeclBuilder& WithReturnType(std::unique_ptr type); + CallDeclBuilder& WithBody(std::unique_ptr body); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_CALLDECLBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/class_members/DestructorDeclBuilder.cpp b/lib/parser/ast/nodes/builders/class_members/DestructorDeclBuilder.cpp new file mode 100644 index 0000000..9261243 --- /dev/null +++ b/lib/parser/ast/nodes/builders/class_members/DestructorDeclBuilder.cpp @@ -0,0 +1,19 @@ +#include "DestructorDeclBuilder.hpp" + +namespace ovum::compiler::parser { + +DestructorDeclBuilder& DestructorDeclBuilder::WithPublic(bool is_public) { + node_->SetPublic(is_public); + return *this; +} + +DestructorDeclBuilder& DestructorDeclBuilder::WithBody(std::unique_ptr body) { + node_->SetBody(std::move(body)); + return *this; +} + +DestructorDeclBuilder& DestructorDeclBuilder::WithIsPublic(bool is_public) { + return WithPublic(is_public); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/class_members/DestructorDeclBuilder.hpp b/lib/parser/ast/nodes/builders/class_members/DestructorDeclBuilder.hpp new file mode 100644 index 0000000..4a3ab96 --- /dev/null +++ b/lib/parser/ast/nodes/builders/class_members/DestructorDeclBuilder.hpp @@ -0,0 +1,21 @@ +#ifndef PARSER_DESTRUCTORDECLBUILDER_HPP_ +#define PARSER_DESTRUCTORDECLBUILDER_HPP_ + +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/class_members/DestructorDecl.hpp" +#include "lib/parser/ast/nodes/stmts/Block.hpp" + +namespace ovum::compiler::parser { + +class DestructorDeclBuilder : public NodeBuilderBase { +public: + DestructorDeclBuilder& WithPublic(bool is_public); + DestructorDeclBuilder& WithIsPublic(bool is_public); + DestructorDeclBuilder& WithBody(std::unique_ptr body); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_DESTRUCTORDECLBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/class_members/FieldDeclBuilder.cpp b/lib/parser/ast/nodes/builders/class_members/FieldDeclBuilder.cpp new file mode 100644 index 0000000..4c85b90 --- /dev/null +++ b/lib/parser/ast/nodes/builders/class_members/FieldDeclBuilder.cpp @@ -0,0 +1,38 @@ +#include "FieldDeclBuilder.hpp" + +namespace ovum::compiler::parser { + +FieldDeclBuilder& FieldDeclBuilder::WithPublic(bool is_public) { + node_->SetPublic(is_public); + return *this; +} + +FieldDeclBuilder& FieldDeclBuilder::WithVar(bool is_var) { + node_->SetVar(is_var); + return *this; +} + +FieldDeclBuilder& FieldDeclBuilder::WithName(std::string name) { + node_->SetName(std::move(name)); + return *this; +} + +FieldDeclBuilder& FieldDeclBuilder::WithType(TypeReference type) { + node_->SetType(std::move(type)); + return *this; +} + +FieldDeclBuilder& FieldDeclBuilder::WithInit(std::unique_ptr init) { + node_->SetInit(std::move(init)); + return *this; +} + +FieldDeclBuilder& FieldDeclBuilder::WithIsPublic(bool is_public) { + return WithPublic(is_public); +} + +FieldDeclBuilder& FieldDeclBuilder::WithIsVar(bool is_var) { + return WithVar(is_var); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/class_members/FieldDeclBuilder.hpp b/lib/parser/ast/nodes/builders/class_members/FieldDeclBuilder.hpp new file mode 100644 index 0000000..d06c471 --- /dev/null +++ b/lib/parser/ast/nodes/builders/class_members/FieldDeclBuilder.hpp @@ -0,0 +1,27 @@ +#ifndef PARSER_FIELDDECLBUILDER_HPP_ +#define PARSER_FIELDDECLBUILDER_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/class_members/FieldDecl.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class FieldDeclBuilder : public NodeBuilderBase { +public: + FieldDeclBuilder& WithPublic(bool is_public); + FieldDeclBuilder& WithIsPublic(bool is_public); + FieldDeclBuilder& WithVar(bool is_var); + FieldDeclBuilder& WithIsVar(bool is_var); + FieldDeclBuilder& WithName(std::string name); + FieldDeclBuilder& WithType(TypeReference type); + FieldDeclBuilder& WithInit(std::unique_ptr init); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_FIELDDECLBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/class_members/MethodDeclBuilder.cpp b/lib/parser/ast/nodes/builders/class_members/MethodDeclBuilder.cpp new file mode 100644 index 0000000..ba3f60e --- /dev/null +++ b/lib/parser/ast/nodes/builders/class_members/MethodDeclBuilder.cpp @@ -0,0 +1,68 @@ +#include "MethodDeclBuilder.hpp" + +namespace ovum::compiler::parser { + +MethodDeclBuilder& MethodDeclBuilder::WithPublic(bool is_public) { + node_->SetPublic(is_public); + return *this; +} + +MethodDeclBuilder& MethodDeclBuilder::WithOverride(bool is_override) { + node_->SetOverride(is_override); + return *this; +} + +MethodDeclBuilder& MethodDeclBuilder::WithStatic(bool is_static) { + node_->SetStatic(is_static); + return *this; +} + +MethodDeclBuilder& MethodDeclBuilder::WithPure(bool is_pure) { + node_->SetPure(is_pure); + return *this; +} + +MethodDeclBuilder& MethodDeclBuilder::WithIsPure(bool is_pure) { + return WithPure(is_pure); +} + +MethodDeclBuilder& MethodDeclBuilder::WithName(std::string name) { + node_->SetName(std::move(name)); + return *this; +} + +MethodDeclBuilder& MethodDeclBuilder::AddParam(Param parameter) { + node_->MutableParams().push_back(std::move(parameter)); + return *this; +} + +MethodDeclBuilder& MethodDeclBuilder::WithParams(std::vector params) { + for (auto& param : params) { + node_->MutableParams().push_back(std::move(param)); + } + return *this; +} + +MethodDeclBuilder& MethodDeclBuilder::WithReturnType(std::unique_ptr type) { + node_->SetReturnType(std::move(type)); + return *this; +} + +MethodDeclBuilder& MethodDeclBuilder::WithBody(std::unique_ptr body) { + node_->SetBody(std::move(body)); + return *this; +} + +MethodDeclBuilder& MethodDeclBuilder::WithIsPublic(bool is_public) { + return WithPublic(is_public); +} + +MethodDeclBuilder& MethodDeclBuilder::WithIsOverride(bool is_override) { + return WithOverride(is_override); +} + +MethodDeclBuilder& MethodDeclBuilder::WithIsStatic(bool is_static) { + return WithStatic(is_static); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/class_members/MethodDeclBuilder.hpp b/lib/parser/ast/nodes/builders/class_members/MethodDeclBuilder.hpp new file mode 100644 index 0000000..36ff113 --- /dev/null +++ b/lib/parser/ast/nodes/builders/class_members/MethodDeclBuilder.hpp @@ -0,0 +1,34 @@ +#ifndef PARSER_METHODDECLBUILDER_HPP_ +#define PARSER_METHODDECLBUILDER_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/class_members/MethodDecl.hpp" +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/types/Param.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class MethodDeclBuilder : public NodeBuilderBase { +public: + MethodDeclBuilder& WithPublic(bool is_public); + MethodDeclBuilder& WithIsPublic(bool is_public); + MethodDeclBuilder& WithOverride(bool is_override); + MethodDeclBuilder& WithIsOverride(bool is_override); + MethodDeclBuilder& WithStatic(bool is_static); + MethodDeclBuilder& WithIsStatic(bool is_static); + MethodDeclBuilder& WithPure(bool is_pure); + MethodDeclBuilder& WithIsPure(bool is_pure); + MethodDeclBuilder& WithName(std::string name); + MethodDeclBuilder& AddParam(Param parameter); + MethodDeclBuilder& WithParams(std::vector params); + MethodDeclBuilder& WithReturnType(std::unique_ptr type); + MethodDeclBuilder& WithBody(std::unique_ptr body); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_METHODDECLBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/class_members/StaticFieldDeclBuilder.cpp b/lib/parser/ast/nodes/builders/class_members/StaticFieldDeclBuilder.cpp new file mode 100644 index 0000000..b24aea7 --- /dev/null +++ b/lib/parser/ast/nodes/builders/class_members/StaticFieldDeclBuilder.cpp @@ -0,0 +1,38 @@ +#include "StaticFieldDeclBuilder.hpp" + +namespace ovum::compiler::parser { + +StaticFieldDeclBuilder& StaticFieldDeclBuilder::WithPublic(bool is_public) { + node_->SetPublic(is_public); + return *this; +} + +StaticFieldDeclBuilder& StaticFieldDeclBuilder::WithVar(bool is_var) { + node_->SetVar(is_var); + return *this; +} + +StaticFieldDeclBuilder& StaticFieldDeclBuilder::WithName(std::string name) { + node_->SetName(std::move(name)); + return *this; +} + +StaticFieldDeclBuilder& StaticFieldDeclBuilder::WithType(TypeReference type) { + node_->SetType(std::move(type)); + return *this; +} + +StaticFieldDeclBuilder& StaticFieldDeclBuilder::WithInit(std::unique_ptr init) { + node_->SetInit(std::move(init)); + return *this; +} + +StaticFieldDeclBuilder& StaticFieldDeclBuilder::WithIsPublic(bool is_public) { + return WithPublic(is_public); +} + +StaticFieldDeclBuilder& StaticFieldDeclBuilder::WithIsVar(bool is_var) { + return WithVar(is_var); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/class_members/StaticFieldDeclBuilder.hpp b/lib/parser/ast/nodes/builders/class_members/StaticFieldDeclBuilder.hpp new file mode 100644 index 0000000..473ddb6 --- /dev/null +++ b/lib/parser/ast/nodes/builders/class_members/StaticFieldDeclBuilder.hpp @@ -0,0 +1,26 @@ +#ifndef PARSER_STATICFIELDDECLBUILDER_HPP_ +#define PARSER_STATICFIELDDECLBUILDER_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/class_members/StaticFieldDecl.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class StaticFieldDeclBuilder : public NodeBuilderBase { +public: + StaticFieldDeclBuilder& WithPublic(bool is_public); + StaticFieldDeclBuilder& WithIsPublic(bool is_public); + StaticFieldDeclBuilder& WithVar(bool is_var); + StaticFieldDeclBuilder& WithIsVar(bool is_var); + StaticFieldDeclBuilder& WithName(std::string name); + StaticFieldDeclBuilder& WithType(TypeReference type); + StaticFieldDeclBuilder& WithInit(std::unique_ptr init); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATICFIELDDECLBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/decls/ClassDeclBuilder.cpp b/lib/parser/ast/nodes/builders/decls/ClassDeclBuilder.cpp new file mode 100644 index 0000000..67e870d --- /dev/null +++ b/lib/parser/ast/nodes/builders/decls/ClassDeclBuilder.cpp @@ -0,0 +1,34 @@ +#include "ClassDeclBuilder.hpp" + +namespace ovum::compiler::parser { + +ClassDeclBuilder& ClassDeclBuilder::WithName(std::string name) { + node_->SetName(std::move(name)); + return *this; +} + +ClassDeclBuilder& ClassDeclBuilder::AddImplements(TypeReference iface) { + node_->MutableImplements().push_back(std::move(iface)); + return *this; +} + +ClassDeclBuilder& ClassDeclBuilder::AddMember(std::unique_ptr member) { + node_->MutableMembers().push_back(std::move(member)); + return *this; +} + +ClassDeclBuilder& ClassDeclBuilder::WithImplements(std::vector implements) { + for (auto& impl : implements) { + node_->AddImplements(std::move(impl)); + } + return *this; +} + +ClassDeclBuilder& ClassDeclBuilder::WithMembers(std::vector> members) { + for (auto& member : members) { + node_->AddMember(std::move(member)); + } + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/decls/ClassDeclBuilder.hpp b/lib/parser/ast/nodes/builders/decls/ClassDeclBuilder.hpp new file mode 100644 index 0000000..f530635 --- /dev/null +++ b/lib/parser/ast/nodes/builders/decls/ClassDeclBuilder.hpp @@ -0,0 +1,24 @@ +#ifndef PARSER_CLASSDECLBUILDER_HPP_ +#define PARSER_CLASSDECLBUILDER_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/decls/ClassDecl.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class ClassDeclBuilder : public NodeBuilderBase { +public: + ClassDeclBuilder& WithName(std::string name); + ClassDeclBuilder& AddImplements(TypeReference iface); + ClassDeclBuilder& AddMember(std::unique_ptr member); + ClassDeclBuilder& WithImplements(std::vector implements); + ClassDeclBuilder& WithMembers(std::vector> members); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_CLASSDECLBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/decls/FunctionDeclBuilder.cpp b/lib/parser/ast/nodes/builders/decls/FunctionDeclBuilder.cpp new file mode 100644 index 0000000..732cb50 --- /dev/null +++ b/lib/parser/ast/nodes/builders/decls/FunctionDeclBuilder.cpp @@ -0,0 +1,37 @@ +#include "FunctionDeclBuilder.hpp" + +namespace ovum::compiler::parser { + +FunctionDeclBuilder& FunctionDeclBuilder::WithPure(bool is_pure) { + node_->SetPure(is_pure); + return *this; +} + +FunctionDeclBuilder& FunctionDeclBuilder::WithName(std::string name) { + node_->SetName(std::move(name)); + return *this; +} + +FunctionDeclBuilder& FunctionDeclBuilder::AddParam(Param parameter) { + node_->MutableParams().push_back(std::move(parameter)); + return *this; +} + +FunctionDeclBuilder& FunctionDeclBuilder::WithParams(std::vector params) { + for (auto& param : params) { + node_->MutableParams().push_back(std::move(param)); + } + return *this; +} + +FunctionDeclBuilder& FunctionDeclBuilder::WithReturnType(std::unique_ptr type) { + node_->SetReturnType(std::move(type)); + return *this; +} + +FunctionDeclBuilder& FunctionDeclBuilder::WithBody(std::unique_ptr body) { + node_->SetBody(std::move(body)); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/decls/FunctionDeclBuilder.hpp b/lib/parser/ast/nodes/builders/decls/FunctionDeclBuilder.hpp new file mode 100644 index 0000000..244eef3 --- /dev/null +++ b/lib/parser/ast/nodes/builders/decls/FunctionDeclBuilder.hpp @@ -0,0 +1,27 @@ +#ifndef PARSER_FUNCTIONDECLBUILDER_HPP_ +#define PARSER_FUNCTIONDECLBUILDER_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/decls/FunctionDecl.hpp" +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/types/Param.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class FunctionDeclBuilder : public NodeBuilderBase { +public: + FunctionDeclBuilder& WithPure(bool is_pure); + FunctionDeclBuilder& WithName(std::string name); + FunctionDeclBuilder& AddParam(Param parameter); + FunctionDeclBuilder& WithParams(std::vector params); + FunctionDeclBuilder& WithReturnType(std::unique_ptr type); + FunctionDeclBuilder& WithBody(std::unique_ptr body); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_FUNCTIONDECLBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/decls/GlobalVarDeclBuilder.cpp b/lib/parser/ast/nodes/builders/decls/GlobalVarDeclBuilder.cpp new file mode 100644 index 0000000..c77bc16 --- /dev/null +++ b/lib/parser/ast/nodes/builders/decls/GlobalVarDeclBuilder.cpp @@ -0,0 +1,29 @@ +#include "GlobalVarDeclBuilder.hpp" + +namespace ovum::compiler::parser { + +GlobalVarDeclBuilder& GlobalVarDeclBuilder::WithVar(bool is_var) { + node_->SetVar(is_var); + return *this; +} + +GlobalVarDeclBuilder& GlobalVarDeclBuilder::WithName(std::string name) { + node_->SetName(std::move(name)); + return *this; +} + +GlobalVarDeclBuilder& GlobalVarDeclBuilder::WithType(TypeReference type) { + node_->SetType(std::move(type)); + return *this; +} + +GlobalVarDeclBuilder& GlobalVarDeclBuilder::WithInit(std::unique_ptr init) { + node_->SetInit(std::move(init)); + return *this; +} + +GlobalVarDeclBuilder& GlobalVarDeclBuilder::WithIsVar(bool is_var) { + return WithVar(is_var); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/decls/GlobalVarDeclBuilder.hpp b/lib/parser/ast/nodes/builders/decls/GlobalVarDeclBuilder.hpp new file mode 100644 index 0000000..71764aa --- /dev/null +++ b/lib/parser/ast/nodes/builders/decls/GlobalVarDeclBuilder.hpp @@ -0,0 +1,25 @@ +#ifndef PARSER_GLOBALVARDECLBUILDER_HPP_ +#define PARSER_GLOBALVARDECLBUILDER_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/decls/GlobalVarDecl.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class GlobalVarDeclBuilder : public NodeBuilderBase { +public: + GlobalVarDeclBuilder& WithVar(bool is_var); + GlobalVarDeclBuilder& WithIsVar(bool is_var); + GlobalVarDeclBuilder& WithName(std::string name); + GlobalVarDeclBuilder& WithType(TypeReference type); + GlobalVarDeclBuilder& WithInit(std::unique_ptr init); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_GLOBALVARDECLBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/decls/InterfaceDeclBuilder.cpp b/lib/parser/ast/nodes/builders/decls/InterfaceDeclBuilder.cpp new file mode 100644 index 0000000..43516b6 --- /dev/null +++ b/lib/parser/ast/nodes/builders/decls/InterfaceDeclBuilder.cpp @@ -0,0 +1,22 @@ +#include "InterfaceDeclBuilder.hpp" + +namespace ovum::compiler::parser { + +InterfaceDeclBuilder& InterfaceDeclBuilder::WithName(std::string name) { + node_->SetName(std::move(name)); + return *this; +} + +InterfaceDeclBuilder& InterfaceDeclBuilder::AddMethod(std::unique_ptr method) { + node_->AddMember(std::move(method)); + return *this; +} + +InterfaceDeclBuilder& InterfaceDeclBuilder::WithMethods(std::vector> methods) { + for (auto& method : methods) { + node_->AddMethod(std::move(method)); + } + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/decls/InterfaceDeclBuilder.hpp b/lib/parser/ast/nodes/builders/decls/InterfaceDeclBuilder.hpp new file mode 100644 index 0000000..bedd52f --- /dev/null +++ b/lib/parser/ast/nodes/builders/decls/InterfaceDeclBuilder.hpp @@ -0,0 +1,22 @@ +#ifndef PARSER_INTERFACEDECLBUILDER_HPP_ +#define PARSER_INTERFACEDECLBUILDER_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/decls/InterfaceDecl.hpp" +#include "lib/parser/ast/nodes/decls/InterfaceMethod.hpp" + +namespace ovum::compiler::parser { + +class InterfaceDeclBuilder : public NodeBuilderBase { +public: + InterfaceDeclBuilder& WithName(std::string name); + InterfaceDeclBuilder& AddMethod(std::unique_ptr method); + InterfaceDeclBuilder& WithMethods(std::vector> methods); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_INTERFACEDECLBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/decls/InterfaceMethodBuilder.cpp b/lib/parser/ast/nodes/builders/decls/InterfaceMethodBuilder.cpp new file mode 100644 index 0000000..6dd5b08 --- /dev/null +++ b/lib/parser/ast/nodes/builders/decls/InterfaceMethodBuilder.cpp @@ -0,0 +1,27 @@ +#include "InterfaceMethodBuilder.hpp" + +namespace ovum::compiler::parser { + +InterfaceMethodBuilder& InterfaceMethodBuilder::WithName(std::string name) { + node_->SetName(std::move(name)); + return *this; +} + +InterfaceMethodBuilder& InterfaceMethodBuilder::AddParam(InterfaceMethod::Param parameter) { + node_->MutableParams().push_back(std::move(parameter)); + return *this; +} + +InterfaceMethodBuilder& InterfaceMethodBuilder::WithParams(std::vector params) { + for (auto& param : params) { + node_->MutableParams().push_back(std::move(param)); + } + return *this; +} + +InterfaceMethodBuilder& InterfaceMethodBuilder::WithReturnType(std::unique_ptr type) { + node_->SetReturnType(std::move(type)); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/decls/InterfaceMethodBuilder.hpp b/lib/parser/ast/nodes/builders/decls/InterfaceMethodBuilder.hpp new file mode 100644 index 0000000..c8f8f56 --- /dev/null +++ b/lib/parser/ast/nodes/builders/decls/InterfaceMethodBuilder.hpp @@ -0,0 +1,23 @@ +#ifndef PARSER_INTERFACEMETHODBUILDER_HPP_ +#define PARSER_INTERFACEMETHODBUILDER_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/decls/InterfaceMethod.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class InterfaceMethodBuilder : public NodeBuilderBase { +public: + InterfaceMethodBuilder& WithName(std::string name); + InterfaceMethodBuilder& AddParam(InterfaceMethod::Param parameter); + InterfaceMethodBuilder& WithParams(std::vector params); + InterfaceMethodBuilder& WithReturnType(std::unique_ptr type); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_INTERFACEMETHODBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/decls/ModuleBuilder.cpp b/lib/parser/ast/nodes/builders/decls/ModuleBuilder.cpp new file mode 100644 index 0000000..2b75aa5 --- /dev/null +++ b/lib/parser/ast/nodes/builders/decls/ModuleBuilder.cpp @@ -0,0 +1,27 @@ +#include "ModuleBuilder.hpp" + +namespace ovum::compiler::parser { + +ModuleBuilder& ModuleBuilder::WithName(std::string name) { + node_->SetName(std::move(name)); + return *this; +} + +ModuleBuilder& ModuleBuilder::WithSource(SourceId id) { + node_->SetSource(std::move(id)); + return *this; +} + +ModuleBuilder& ModuleBuilder::AddDecl(std::unique_ptr declaration) { + node_->AddDecl(std::move(declaration)); + return *this; +} + +ModuleBuilder& ModuleBuilder::WithDecls(std::vector> decls) { + for (auto& decl : decls) { + node_->AddDecl(std::move(decl)); + } + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/decls/ModuleBuilder.hpp b/lib/parser/ast/nodes/builders/decls/ModuleBuilder.hpp new file mode 100644 index 0000000..0dd23b6 --- /dev/null +++ b/lib/parser/ast/nodes/builders/decls/ModuleBuilder.hpp @@ -0,0 +1,24 @@ +#ifndef PARSER_MODULEBUILDER_HPP_ +#define PARSER_MODULEBUILDER_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/base/Decl.hpp" +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/decls/Module.hpp" +#include "lib/parser/tokens/SourceId.hpp" + +namespace ovum::compiler::parser { + +class ModuleBuilder : public NodeBuilderBase { +public: + ModuleBuilder& WithName(std::string name); + ModuleBuilder& WithSource(SourceId id); + ModuleBuilder& AddDecl(std::unique_ptr declaration); + ModuleBuilder& WithDecls(std::vector> decls); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_MODULEBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/decls/TypeAliasDeclBuilder.cpp b/lib/parser/ast/nodes/builders/decls/TypeAliasDeclBuilder.cpp new file mode 100644 index 0000000..7d1db13 --- /dev/null +++ b/lib/parser/ast/nodes/builders/decls/TypeAliasDeclBuilder.cpp @@ -0,0 +1,15 @@ +#include "TypeAliasDeclBuilder.hpp" + +namespace ovum::compiler::parser { + +TypeAliasDeclBuilder& TypeAliasDeclBuilder::WithName(std::string name) { + node_->SetName(std::move(name)); + return *this; +} + +TypeAliasDeclBuilder& TypeAliasDeclBuilder::WithAliasedType(TypeReference type) { + node_->SetAliasedType(std::move(type)); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/decls/TypeAliasDeclBuilder.hpp b/lib/parser/ast/nodes/builders/decls/TypeAliasDeclBuilder.hpp new file mode 100644 index 0000000..4a76dde --- /dev/null +++ b/lib/parser/ast/nodes/builders/decls/TypeAliasDeclBuilder.hpp @@ -0,0 +1,20 @@ +#ifndef PARSER_TYPEALIASDECLBUILDER_HPP_ +#define PARSER_TYPEALIASDECLBUILDER_HPP_ + +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/decls/TypeAliasDecl.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class TypeAliasDeclBuilder : public NodeBuilderBase { +public: + TypeAliasDeclBuilder& WithName(std::string name); + TypeAliasDeclBuilder& WithAliasedType(TypeReference type); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_TYPEALIASDECLBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/exprs/AssignBuilder.cpp b/lib/parser/ast/nodes/builders/exprs/AssignBuilder.cpp new file mode 100644 index 0000000..31fe680 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/AssignBuilder.cpp @@ -0,0 +1,20 @@ +#include "AssignBuilder.hpp" + +namespace ovum::compiler::parser { + +AssignBuilder& AssignBuilder::WithKind(const IAssignOpTag& kind) { + node_->SetKind(kind); + return *this; +} + +AssignBuilder& AssignBuilder::WithTarget(std::unique_ptr target) { + node_->SetTarget(std::move(target)); + return *this; +} + +AssignBuilder& AssignBuilder::WithValue(std::unique_ptr value) { + node_->SetValue(std::move(value)); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/exprs/AssignBuilder.hpp b/lib/parser/ast/nodes/builders/exprs/AssignBuilder.hpp new file mode 100644 index 0000000..8515f87 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/AssignBuilder.hpp @@ -0,0 +1,21 @@ +#ifndef PARSER_ASSIGNBUILDER_HPP_ +#define PARSER_ASSIGNBUILDER_HPP_ + +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/exprs/Assign.hpp" +#include "lib/parser/ast/nodes/exprs/tags/IAssignOpTag.hpp" + +namespace ovum::compiler::parser { + +class AssignBuilder : public NodeBuilderBase { +public: + AssignBuilder& WithKind(const IAssignOpTag& kind); + AssignBuilder& WithTarget(std::unique_ptr target); + AssignBuilder& WithValue(std::unique_ptr value); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_ASSIGNBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/exprs/BinaryBuilder.cpp b/lib/parser/ast/nodes/builders/exprs/BinaryBuilder.cpp new file mode 100644 index 0000000..85a4ab3 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/BinaryBuilder.cpp @@ -0,0 +1,20 @@ +#include "BinaryBuilder.hpp" + +namespace ovum::compiler::parser { + +BinaryBuilder& BinaryBuilder::WithOp(const IBinaryOpTag& op) { + node_->SetOp(op); + return *this; +} + +BinaryBuilder& BinaryBuilder::WithLhs(std::unique_ptr lhs) { + node_->SetLhs(std::move(lhs)); + return *this; +} + +BinaryBuilder& BinaryBuilder::WithRhs(std::unique_ptr rhs) { + node_->SetRhs(std::move(rhs)); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/exprs/BinaryBuilder.hpp b/lib/parser/ast/nodes/builders/exprs/BinaryBuilder.hpp new file mode 100644 index 0000000..5c401d1 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/BinaryBuilder.hpp @@ -0,0 +1,20 @@ +#ifndef PARSER_BINARYBUILDER_HPP_ +#define PARSER_BINARYBUILDER_HPP_ + +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/exprs/Binary.hpp" + +namespace ovum::compiler::parser { + +class BinaryBuilder : public NodeBuilderBase { +public: + BinaryBuilder& WithOp(const IBinaryOpTag& op); + BinaryBuilder& WithLhs(std::unique_ptr lhs); + BinaryBuilder& WithRhs(std::unique_ptr rhs); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_BINARYBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/exprs/CallExprBuilder.cpp b/lib/parser/ast/nodes/builders/exprs/CallExprBuilder.cpp new file mode 100644 index 0000000..e7aa578 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/CallExprBuilder.cpp @@ -0,0 +1,22 @@ +#include "CallExprBuilder.hpp" + +namespace ovum::compiler::parser { + +CallExprBuilder& CallExprBuilder::WithCallee(std::unique_ptr callee) { + node_->SetCallee(std::move(callee)); + return *this; +} + +CallExprBuilder& CallExprBuilder::AddArg(std::unique_ptr argument) { + node_->AddArg(std::move(argument)); + return *this; +} + +CallExprBuilder& CallExprBuilder::WithArgs(std::vector> args) { + for (auto& arg : args) { + node_->AddArg(std::move(arg)); + } + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/exprs/CallExprBuilder.hpp b/lib/parser/ast/nodes/builders/exprs/CallExprBuilder.hpp new file mode 100644 index 0000000..5cd8206 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/CallExprBuilder.hpp @@ -0,0 +1,20 @@ +#ifndef PARSER_CALLEXPRBUILDER_HPP_ +#define PARSER_CALLEXPRBUILDER_HPP_ + +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/exprs/Call.hpp" + +namespace ovum::compiler::parser { + +class CallExprBuilder : public NodeBuilderBase { +public: + CallExprBuilder& WithCallee(std::unique_ptr callee); + CallExprBuilder& AddArg(std::unique_ptr argument); + CallExprBuilder& WithArgs(std::vector> args); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_CALLEXPRBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/exprs/CastAsBuilder.cpp b/lib/parser/ast/nodes/builders/exprs/CastAsBuilder.cpp new file mode 100644 index 0000000..f9bcb73 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/CastAsBuilder.cpp @@ -0,0 +1,15 @@ +#include "CastAsBuilder.hpp" + +namespace ovum::compiler::parser { + +CastAsBuilder& CastAsBuilder::WithExpr(std::unique_ptr expression) { + node_->SetExpression(std::move(expression)); + return *this; +} + +CastAsBuilder& CastAsBuilder::WithType(TypeReference type) { + node_->SetType(std::move(type)); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/exprs/CastAsBuilder.hpp b/lib/parser/ast/nodes/builders/exprs/CastAsBuilder.hpp new file mode 100644 index 0000000..d821e19 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/CastAsBuilder.hpp @@ -0,0 +1,20 @@ +#ifndef PARSER_CASTASBUILDER_HPP_ +#define PARSER_CASTASBUILDER_HPP_ + +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/exprs/CastAs.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class CastAsBuilder : public NodeBuilderBase { +public: + CastAsBuilder& WithExpr(std::unique_ptr expression); + CastAsBuilder& WithType(TypeReference type); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_CASTASBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/exprs/ElvisBuilder.cpp b/lib/parser/ast/nodes/builders/exprs/ElvisBuilder.cpp new file mode 100644 index 0000000..4e85dd9 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/ElvisBuilder.cpp @@ -0,0 +1,15 @@ +#include "ElvisBuilder.hpp" + +namespace ovum::compiler::parser { + +ElvisBuilder& ElvisBuilder::WithLhs(std::unique_ptr lhs) { + node_->SetLhs(std::move(lhs)); + return *this; +} + +ElvisBuilder& ElvisBuilder::WithRhs(std::unique_ptr rhs) { + node_->SetRhs(std::move(rhs)); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/exprs/ElvisBuilder.hpp b/lib/parser/ast/nodes/builders/exprs/ElvisBuilder.hpp new file mode 100644 index 0000000..27a5267 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/ElvisBuilder.hpp @@ -0,0 +1,19 @@ +#ifndef PARSER_ELVISBUILDER_HPP_ +#define PARSER_ELVISBUILDER_HPP_ + +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/exprs/Elvis.hpp" + +namespace ovum::compiler::parser { + +class ElvisBuilder : public NodeBuilderBase { +public: + ElvisBuilder& WithLhs(std::unique_ptr lhs); + ElvisBuilder& WithRhs(std::unique_ptr rhs); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_ELVISBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/exprs/FieldAccessBuilder.cpp b/lib/parser/ast/nodes/builders/exprs/FieldAccessBuilder.cpp new file mode 100644 index 0000000..2c3419b --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/FieldAccessBuilder.cpp @@ -0,0 +1,15 @@ +#include "FieldAccessBuilder.hpp" + +namespace ovum::compiler::parser { + +FieldAccessBuilder& FieldAccessBuilder::WithObject(std::unique_ptr object) { + node_->SetObject(std::move(object)); + return *this; +} + +FieldAccessBuilder& FieldAccessBuilder::WithName(std::string name) { + node_->SetName(std::move(name)); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/exprs/FieldAccessBuilder.hpp b/lib/parser/ast/nodes/builders/exprs/FieldAccessBuilder.hpp new file mode 100644 index 0000000..3822db0 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/FieldAccessBuilder.hpp @@ -0,0 +1,20 @@ +#ifndef PARSER_FIELDACCESSBUILDER_HPP_ +#define PARSER_FIELDACCESSBUILDER_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/exprs/FieldAccess.hpp" + +namespace ovum::compiler::parser { + +class FieldAccessBuilder : public NodeBuilderBase { +public: + FieldAccessBuilder& WithObject(std::unique_ptr object); + FieldAccessBuilder& WithName(std::string name); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_FIELDACCESSBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/exprs/IdentRefBuilder.cpp b/lib/parser/ast/nodes/builders/exprs/IdentRefBuilder.cpp new file mode 100644 index 0000000..8b586d9 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/IdentRefBuilder.cpp @@ -0,0 +1,10 @@ +#include "IdentRefBuilder.hpp" + +namespace ovum::compiler::parser { + +IdentRefBuilder& IdentRefBuilder::WithName(std::string name) { + node_->SetName(std::move(name)); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/exprs/IdentRefBuilder.hpp b/lib/parser/ast/nodes/builders/exprs/IdentRefBuilder.hpp new file mode 100644 index 0000000..f8d6710 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/IdentRefBuilder.hpp @@ -0,0 +1,18 @@ +#ifndef PARSER_IDENTREFBUILDER_HPP_ +#define PARSER_IDENTREFBUILDER_HPP_ + +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/exprs/IdentRef.hpp" + +namespace ovum::compiler::parser { + +class IdentRefBuilder : public NodeBuilderBase { +public: + IdentRefBuilder& WithName(std::string name); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_IDENTREFBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/exprs/IndexAccessBuilder.cpp b/lib/parser/ast/nodes/builders/exprs/IndexAccessBuilder.cpp new file mode 100644 index 0000000..d809b5e --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/IndexAccessBuilder.cpp @@ -0,0 +1,15 @@ +#include "IndexAccessBuilder.hpp" + +namespace ovum::compiler::parser { + +IndexAccessBuilder& IndexAccessBuilder::WithObject(std::unique_ptr object) { + node_->SetObject(std::move(object)); + return *this; +} + +IndexAccessBuilder& IndexAccessBuilder::WithIndex(std::unique_ptr index_expr) { + node_->SetIndexExpr(std::move(index_expr)); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/exprs/IndexAccessBuilder.hpp b/lib/parser/ast/nodes/builders/exprs/IndexAccessBuilder.hpp new file mode 100644 index 0000000..c8836ab --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/IndexAccessBuilder.hpp @@ -0,0 +1,19 @@ +#ifndef PARSER_INDEXACCESSBUILDER_HPP_ +#define PARSER_INDEXACCESSBUILDER_HPP_ + +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/exprs/IndexAccess.hpp" + +namespace ovum::compiler::parser { + +class IndexAccessBuilder : public NodeBuilderBase { +public: + IndexAccessBuilder& WithObject(std::unique_ptr object); + IndexAccessBuilder& WithIndex(std::unique_ptr index_expr); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_INDEXACCESSBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/exprs/NamespaceRefBuilder.cpp b/lib/parser/ast/nodes/builders/exprs/NamespaceRefBuilder.cpp new file mode 100644 index 0000000..553a87d --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/NamespaceRefBuilder.cpp @@ -0,0 +1,15 @@ +#include "NamespaceRefBuilder.hpp" + +namespace ovum::compiler::parser { + +NamespaceRefBuilder& NamespaceRefBuilder::WithNamespace(std::unique_ptr ns) { + node_->SetNamespaceExpr(std::move(ns)); + return *this; +} + +NamespaceRefBuilder& NamespaceRefBuilder::WithName(std::string name) { + node_->SetName(std::move(name)); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/exprs/NamespaceRefBuilder.hpp b/lib/parser/ast/nodes/builders/exprs/NamespaceRefBuilder.hpp new file mode 100644 index 0000000..7c15f88 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/NamespaceRefBuilder.hpp @@ -0,0 +1,20 @@ +#ifndef PARSER_NAMESPACEREFBUILDER_HPP_ +#define PARSER_NAMESPACEREFBUILDER_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/exprs/NamespaceRef.hpp" + +namespace ovum::compiler::parser { + +class NamespaceRefBuilder : public NodeBuilderBase { +public: + NamespaceRefBuilder& WithNamespace(std::unique_ptr ns); + NamespaceRefBuilder& WithName(std::string name); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_NAMESPACEREFBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/exprs/SafeCallBuilder.cpp b/lib/parser/ast/nodes/builders/exprs/SafeCallBuilder.cpp new file mode 100644 index 0000000..6cbf1f7 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/SafeCallBuilder.cpp @@ -0,0 +1,40 @@ +#include "SafeCallBuilder.hpp" + +namespace ovum::compiler::parser { + +SafeCallBuilder& SafeCallBuilder::WithObject(std::unique_ptr object) { + node_->SetObject(std::move(object)); + return *this; +} + +SafeCallBuilder& SafeCallBuilder::WithMethod(std::string method) { + node_->SetMethod(std::move(method)); + return *this; +} + +SafeCallBuilder& SafeCallBuilder::AddArg(std::unique_ptr argument) { + node_->AddArg(std::move(argument)); + return *this; +} + +SafeCallBuilder& SafeCallBuilder::WithArgs(std::vector> args) { + node_->ClearArgs(); + for (auto& arg : args) { + node_->AddArg(std::move(arg)); + } + return *this; +} + +SafeCallBuilder& SafeCallBuilder::WithInferredType(std::optional inferred_type) { + if (inferred_type.has_value()) { + node_->SetInferredType(inferred_type.value()); + } + return *this; +} + +SafeCallBuilder& SafeCallBuilder::ClearArgs() { + node_->ClearArgs(); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/exprs/SafeCallBuilder.hpp b/lib/parser/ast/nodes/builders/exprs/SafeCallBuilder.hpp new file mode 100644 index 0000000..8a823ea --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/SafeCallBuilder.hpp @@ -0,0 +1,24 @@ +#ifndef PARSER_SAFECALLBUILDER_HPP_ +#define PARSER_SAFECALLBUILDER_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/exprs/SafeCall.hpp" + +namespace ovum::compiler::parser { + +class SafeCallBuilder : public NodeBuilderBase { +public: + SafeCallBuilder& WithObject(std::unique_ptr object); + SafeCallBuilder& WithMethod(std::string method); + SafeCallBuilder& AddArg(std::unique_ptr argument); + SafeCallBuilder& WithArgs(std::vector> args); + SafeCallBuilder& WithInferredType(std::optional inferred_type); + SafeCallBuilder& ClearArgs(); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_SAFECALLBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/exprs/TypeTestIsBuilder.cpp b/lib/parser/ast/nodes/builders/exprs/TypeTestIsBuilder.cpp new file mode 100644 index 0000000..de95fac --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/TypeTestIsBuilder.cpp @@ -0,0 +1,15 @@ +#include "TypeTestIsBuilder.hpp" + +namespace ovum::compiler::parser { + +TypeTestIsBuilder& TypeTestIsBuilder::WithExpr(std::unique_ptr expression) { + node_->SetExpression(std::move(expression)); + return *this; +} + +TypeTestIsBuilder& TypeTestIsBuilder::WithType(TypeReference type) { + node_->SetType(std::move(type)); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/exprs/TypeTestIsBuilder.hpp b/lib/parser/ast/nodes/builders/exprs/TypeTestIsBuilder.hpp new file mode 100644 index 0000000..54f4436 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/TypeTestIsBuilder.hpp @@ -0,0 +1,20 @@ +#ifndef PARSER_TYPETESTISBUILDER_HPP_ +#define PARSER_TYPETESTISBUILDER_HPP_ + +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/exprs/TypeTestIs.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class TypeTestIsBuilder : public NodeBuilderBase { +public: + TypeTestIsBuilder& WithExpr(std::unique_ptr expression); + TypeTestIsBuilder& WithType(TypeReference type); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_TYPETESTISBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/exprs/UnaryBuilder.cpp b/lib/parser/ast/nodes/builders/exprs/UnaryBuilder.cpp new file mode 100644 index 0000000..e9dd79e --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/UnaryBuilder.cpp @@ -0,0 +1,15 @@ +#include "UnaryBuilder.hpp" + +namespace ovum::compiler::parser { + +UnaryBuilder& UnaryBuilder::WithOp(const IUnaryOpTag& op) { + node_->SetOp(op); + return *this; +} + +UnaryBuilder& UnaryBuilder::WithOperand(std::unique_ptr operand) { + node_->SetOperand(std::move(operand)); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/exprs/UnaryBuilder.hpp b/lib/parser/ast/nodes/builders/exprs/UnaryBuilder.hpp new file mode 100644 index 0000000..f397133 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/UnaryBuilder.hpp @@ -0,0 +1,19 @@ +#ifndef PARSER_UNARYBUILDER_HPP_ +#define PARSER_UNARYBUILDER_HPP_ + +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/exprs/Unary.hpp" + +namespace ovum::compiler::parser { + +class UnaryBuilder : public NodeBuilderBase { +public: + UnaryBuilder& WithOp(const IUnaryOpTag& op); + UnaryBuilder& WithOperand(std::unique_ptr operand); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_UNARYBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/exprs/literals/BoolLitBuilder.cpp b/lib/parser/ast/nodes/builders/exprs/literals/BoolLitBuilder.cpp new file mode 100644 index 0000000..50f2f0e --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/literals/BoolLitBuilder.cpp @@ -0,0 +1,10 @@ +#include "BoolLitBuilder.hpp" + +namespace ovum::compiler::parser { + +BoolLitBuilder& BoolLitBuilder::WithValue(bool value) { + node_->SetValue(value); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/exprs/literals/BoolLitBuilder.hpp b/lib/parser/ast/nodes/builders/exprs/literals/BoolLitBuilder.hpp new file mode 100644 index 0000000..663e072 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/literals/BoolLitBuilder.hpp @@ -0,0 +1,16 @@ +#ifndef PARSER_BOOLLITBUILDER_HPP_ +#define PARSER_BOOLLITBUILDER_HPP_ + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/exprs/literals/BoolLit.hpp" + +namespace ovum::compiler::parser { + +class BoolLitBuilder : public NodeBuilderBase { +public: + BoolLitBuilder& WithValue(bool value); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_BOOLLITBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/exprs/literals/CharLitBuilder.cpp b/lib/parser/ast/nodes/builders/exprs/literals/CharLitBuilder.cpp new file mode 100644 index 0000000..b62f3ee --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/literals/CharLitBuilder.cpp @@ -0,0 +1,10 @@ +#include "CharLitBuilder.hpp" + +namespace ovum::compiler::parser { + +CharLitBuilder& CharLitBuilder::WithValue(char value) { + node_->SetValue(value); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/exprs/literals/CharLitBuilder.hpp b/lib/parser/ast/nodes/builders/exprs/literals/CharLitBuilder.hpp new file mode 100644 index 0000000..1e3f303 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/literals/CharLitBuilder.hpp @@ -0,0 +1,16 @@ +#ifndef PARSER_CHARLITBUILDER_HPP_ +#define PARSER_CHARLITBUILDER_HPP_ + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/exprs/literals/CharLit.hpp" + +namespace ovum::compiler::parser { + +class CharLitBuilder : public NodeBuilderBase { +public: + CharLitBuilder& WithValue(char value); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_CHARLITBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/exprs/literals/FloatLitBuilder.cpp b/lib/parser/ast/nodes/builders/exprs/literals/FloatLitBuilder.cpp new file mode 100644 index 0000000..253b8ae --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/literals/FloatLitBuilder.cpp @@ -0,0 +1,15 @@ +#include "FloatLitBuilder.hpp" + +namespace ovum::compiler::parser { + +FloatLitBuilder& FloatLitBuilder::WithValue(double value) { + node_->SetValue(value); + return *this; +} + +FloatLitBuilder& FloatLitBuilder::WithValue(long double value) { + node_->SetValue(static_cast(value)); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/exprs/literals/FloatLitBuilder.hpp b/lib/parser/ast/nodes/builders/exprs/literals/FloatLitBuilder.hpp new file mode 100644 index 0000000..a68d429 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/literals/FloatLitBuilder.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_FLOATLITBUILDER_HPP_ +#define PARSER_FLOATLITBUILDER_HPP_ + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/exprs/literals/FloatLit.hpp" + +namespace ovum::compiler::parser { + +class FloatLitBuilder : public NodeBuilderBase { +public: + FloatLitBuilder& WithValue(double value); + FloatLitBuilder& WithValue(long double value); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_FLOATLITBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/exprs/literals/IntLitBuilder.cpp b/lib/parser/ast/nodes/builders/exprs/literals/IntLitBuilder.cpp new file mode 100644 index 0000000..386b960 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/literals/IntLitBuilder.cpp @@ -0,0 +1,10 @@ +#include "IntLitBuilder.hpp" + +namespace ovum::compiler::parser { + +IntLitBuilder& IntLitBuilder::WithValue(long long value) { + node_->SetValue(value); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/exprs/literals/IntLitBuilder.hpp b/lib/parser/ast/nodes/builders/exprs/literals/IntLitBuilder.hpp new file mode 100644 index 0000000..fb9171a --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/literals/IntLitBuilder.hpp @@ -0,0 +1,16 @@ +#ifndef PARSER_INTLITBUILDER_HPP_ +#define PARSER_INTLITBUILDER_HPP_ + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/exprs/literals/IntLit.hpp" + +namespace ovum::compiler::parser { + +class IntLitBuilder : public NodeBuilderBase { +public: + IntLitBuilder& WithValue(long long value); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_INTLITBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/exprs/literals/NullLitBuilder.cpp b/lib/parser/ast/nodes/builders/exprs/literals/NullLitBuilder.cpp new file mode 100644 index 0000000..5152319 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/literals/NullLitBuilder.cpp @@ -0,0 +1,9 @@ +#include "NullLitBuilder.hpp" + +namespace ovum::compiler::parser { + +NullLitBuilder& NullLitBuilder::Noop() { + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/exprs/literals/NullLitBuilder.hpp b/lib/parser/ast/nodes/builders/exprs/literals/NullLitBuilder.hpp new file mode 100644 index 0000000..fa26b81 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/literals/NullLitBuilder.hpp @@ -0,0 +1,16 @@ +#ifndef PARSER_NULLLITBUILDER_HPP_ +#define PARSER_NULLLITBUILDER_HPP_ + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/exprs/literals/NullLit.hpp" + +namespace ovum::compiler::parser { + +class NullLitBuilder : public NodeBuilderBase { +public: + NullLitBuilder& Noop(); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_NULLLITBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/exprs/literals/StringLitBuilder.cpp b/lib/parser/ast/nodes/builders/exprs/literals/StringLitBuilder.cpp new file mode 100644 index 0000000..02d018b --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/literals/StringLitBuilder.cpp @@ -0,0 +1,10 @@ +#include "StringLitBuilder.hpp" + +namespace ovum::compiler::parser { + +StringLitBuilder& StringLitBuilder::WithValue(std::string value) { + node_->SetValue(std::move(value)); + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/exprs/literals/StringLitBuilder.hpp b/lib/parser/ast/nodes/builders/exprs/literals/StringLitBuilder.hpp new file mode 100644 index 0000000..8e93528 --- /dev/null +++ b/lib/parser/ast/nodes/builders/exprs/literals/StringLitBuilder.hpp @@ -0,0 +1,19 @@ +#ifndef PARSER_STRINGLITBUILDER_HPP_ +#define PARSER_STRINGLITBUILDER_HPP_ + +#include + +#include "NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/exprs/literals/StringLit.hpp" + +namespace ovum::compiler::parser { + +class StringLitBuilder : public NodeBuilderBase { +public: + StringLitBuilder& WithValue(std::string value); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STRINGLITBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/stmts/BlockBuilder.cpp b/lib/parser/ast/nodes/builders/stmts/BlockBuilder.cpp new file mode 100644 index 0000000..c3cf58c --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/BlockBuilder.cpp @@ -0,0 +1,17 @@ +#include "BlockBuilder.hpp" + +namespace ovum::compiler::parser { + +BlockBuilder& BlockBuilder::Append(std::unique_ptr statement) { + node_->Append(std::move(statement)); + return *this; +} + +BlockBuilder& BlockBuilder::WithStatements(std::vector> statements) { + for (auto& stmt : statements) { + node_->Append(std::move(stmt)); + } + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/stmts/BlockBuilder.hpp b/lib/parser/ast/nodes/builders/stmts/BlockBuilder.hpp new file mode 100644 index 0000000..0b877cb --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/BlockBuilder.hpp @@ -0,0 +1,19 @@ +#ifndef PARSER_BLOCKBUILDER_HPP_ +#define PARSER_BLOCKBUILDER_HPP_ + +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/stmts/Block.hpp" + +namespace ovum::compiler::parser { + +class BlockBuilder : public NodeBuilderBase { +public: + BlockBuilder& Append(std::unique_ptr statement); + BlockBuilder& WithStatements(std::vector> statements); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_BLOCKBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/stmts/BreakStmtBuilder.cpp b/lib/parser/ast/nodes/builders/stmts/BreakStmtBuilder.cpp new file mode 100644 index 0000000..9b8b08f --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/BreakStmtBuilder.cpp @@ -0,0 +1,9 @@ +#include "BreakStmtBuilder.hpp" + +namespace ovum::compiler::parser { + +BreakStmtBuilder& BreakStmtBuilder::Noop() { + return *this; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/builders/stmts/BreakStmtBuilder.hpp b/lib/parser/ast/nodes/builders/stmts/BreakStmtBuilder.hpp new file mode 100644 index 0000000..a5feab6 --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/BreakStmtBuilder.hpp @@ -0,0 +1,16 @@ +#ifndef PARSER_BREAKSTMTBUILDER_HPP_ +#define PARSER_BREAKSTMTBUILDER_HPP_ + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/stmts/BreakStmt.hpp" + +namespace ovum::compiler::parser { + +class BreakStmtBuilder : public NodeBuilderBase { +public: + BreakStmtBuilder& Noop(); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_BREAKSTMTBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/stmts/ContinueStmtBuilder.cpp b/lib/parser/ast/nodes/builders/stmts/ContinueStmtBuilder.cpp new file mode 100644 index 0000000..dcba2e0 --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/ContinueStmtBuilder.cpp @@ -0,0 +1,9 @@ +#include "ContinueStmtBuilder.hpp" + +namespace ovum::compiler::parser { + +ContinueStmtBuilder& ContinueStmtBuilder::Noop() { + return *this; +} + +} // namespace ovum::compiler::parser \ No newline at end of file diff --git a/lib/parser/ast/nodes/builders/stmts/ContinueStmtBuilder.hpp b/lib/parser/ast/nodes/builders/stmts/ContinueStmtBuilder.hpp new file mode 100644 index 0000000..16a573b --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/ContinueStmtBuilder.hpp @@ -0,0 +1,16 @@ +#ifndef PARSER_CONTINUESTMTBUILDER_HPP_ +#define PARSER_CONTINUESTMTBUILDER_HPP_ + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/stmts/ContinueStmt.hpp" + +namespace ovum::compiler::parser { + +class ContinueStmtBuilder : public NodeBuilderBase { +public: + ContinueStmtBuilder& Noop(); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_CONTINUESTMTBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/stmts/ExprStmtBuilder.cpp b/lib/parser/ast/nodes/builders/stmts/ExprStmtBuilder.cpp new file mode 100644 index 0000000..ddc7a55 --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/ExprStmtBuilder.cpp @@ -0,0 +1,10 @@ +#include "ExprStmtBuilder.hpp" + +namespace ovum::compiler::parser { + +ExprStmtBuilder& ExprStmtBuilder::WithExpr(std::unique_ptr expr) { + node_->SetExpression(std::move(expr)); + return *this; +} + +} // namespace ovum::compiler::parser \ No newline at end of file diff --git a/lib/parser/ast/nodes/builders/stmts/ExprStmtBuilder.hpp b/lib/parser/ast/nodes/builders/stmts/ExprStmtBuilder.hpp new file mode 100644 index 0000000..a9f64e8 --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/ExprStmtBuilder.hpp @@ -0,0 +1,19 @@ +#ifndef PARSER_EXPRSTMTBUILDER_HPP_ +#define PARSER_EXPRSTMTBUILDER_HPP_ + +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/stmts/ExprStmt.hpp" + +namespace ovum::compiler::parser { + +class ExprStmtBuilder : public NodeBuilderBase { +public: + ExprStmtBuilder& WithExpr(std::unique_ptr expr); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_EXPRSTMTBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/stmts/ForStmtBuilder.cpp b/lib/parser/ast/nodes/builders/stmts/ForStmtBuilder.cpp new file mode 100644 index 0000000..bfb72c9 --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/ForStmtBuilder.cpp @@ -0,0 +1,20 @@ +#include "ForStmtBuilder.hpp" + +namespace ovum::compiler::parser { + +ForStmtBuilder& ForStmtBuilder::WithIteratorName(std::string name) { + node_->SetIteratorName(std::move(name)); + return *this; +} + +ForStmtBuilder& ForStmtBuilder::WithIteratorExpr(std::unique_ptr expr) { + node_->SetIteratorExpr(std::move(expr)); + return *this; +} + +ForStmtBuilder& ForStmtBuilder::WithBody(std::unique_ptr body) { + node_->SetBody(std::move(body)); + return *this; +} + +} // namespace ovum::compiler::parser \ No newline at end of file diff --git a/lib/parser/ast/nodes/builders/stmts/ForStmtBuilder.hpp b/lib/parser/ast/nodes/builders/stmts/ForStmtBuilder.hpp new file mode 100644 index 0000000..cc8c8af --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/ForStmtBuilder.hpp @@ -0,0 +1,23 @@ +#ifndef PARSER_FORSTMTBUILDER_HPP_ +#define PARSER_FORSTMTBUILDER_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/ast/nodes/stmts/ForStmt.hpp" + +namespace ovum::compiler::parser { + +class ForStmtBuilder : public NodeBuilderBase { +public: + ForStmtBuilder& WithIteratorName(std::string name); + ForStmtBuilder& WithIteratorExpr(std::unique_ptr expr); + ForStmtBuilder& WithBody(std::unique_ptr body); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_FORSTMTBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/stmts/IfStmtBuilder.cpp b/lib/parser/ast/nodes/builders/stmts/IfStmtBuilder.cpp new file mode 100644 index 0000000..b8a56a1 --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/IfStmtBuilder.cpp @@ -0,0 +1,22 @@ +#include "IfStmtBuilder.hpp" + +namespace ovum::compiler::parser { + +IfStmtBuilder& IfStmtBuilder::AddBranch(Branch branch) { + node_->AddBranch(std::move(branch)); + return *this; +} + +IfStmtBuilder& IfStmtBuilder::WithBranches(std::vector branches) { + for (auto& branch : branches) { + node_->AddBranch(std::move(branch)); + } + return *this; +} + +IfStmtBuilder& IfStmtBuilder::WithElse(std::unique_ptr else_block) { + node_->SetElseBlock(std::move(else_block)); + return *this; +} + +} // namespace ovum::compiler::parser \ No newline at end of file diff --git a/lib/parser/ast/nodes/builders/stmts/IfStmtBuilder.hpp b/lib/parser/ast/nodes/builders/stmts/IfStmtBuilder.hpp new file mode 100644 index 0000000..89d9fe5 --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/IfStmtBuilder.hpp @@ -0,0 +1,23 @@ +#ifndef PARSER_IFSTMTBUILDER_HPP_ +#define PARSER_IFSTMTBUILDER_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/stmts/Branch.hpp" +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/ast/nodes/stmts/IfStmt.hpp" + +namespace ovum::compiler::parser { + +class IfStmtBuilder : public NodeBuilderBase { +public: + IfStmtBuilder& AddBranch(Branch branch); + IfStmtBuilder& WithBranches(std::vector branches); + IfStmtBuilder& WithElse(std::unique_ptr else_block); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_IFSTMTBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/stmts/ReturnStmtBuilder.cpp b/lib/parser/ast/nodes/builders/stmts/ReturnStmtBuilder.cpp new file mode 100644 index 0000000..2b4bcb6 --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/ReturnStmtBuilder.cpp @@ -0,0 +1,10 @@ +#include "ReturnStmtBuilder.hpp" + +namespace ovum::compiler::parser { + +ReturnStmtBuilder& ReturnStmtBuilder::WithValue(std::unique_ptr value) { + node_->SetValue(std::move(value)); + return *this; +} + +} // namespace ovum::compiler::parser \ No newline at end of file diff --git a/lib/parser/ast/nodes/builders/stmts/ReturnStmtBuilder.hpp b/lib/parser/ast/nodes/builders/stmts/ReturnStmtBuilder.hpp new file mode 100644 index 0000000..67a4d11 --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/ReturnStmtBuilder.hpp @@ -0,0 +1,19 @@ +#ifndef PARSER_RETURNSTMTBUILDER_HPP_ +#define PARSER_RETURNSTMTBUILDER_HPP_ + +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/stmts/ReturnStmt.hpp" + +namespace ovum::compiler::parser { + +class ReturnStmtBuilder : public NodeBuilderBase { +public: + ReturnStmtBuilder& WithValue(std::unique_ptr value); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_RETURNSTMTBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/stmts/UnsafeBlockBuilder.cpp b/lib/parser/ast/nodes/builders/stmts/UnsafeBlockBuilder.cpp new file mode 100644 index 0000000..dc75b84 --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/UnsafeBlockBuilder.cpp @@ -0,0 +1,10 @@ +#include "UnsafeBlockBuilder.hpp" + +namespace ovum::compiler::parser { + +UnsafeBlockBuilder& UnsafeBlockBuilder::WithBody(std::unique_ptr body) { + node_->SetBody(std::move(body)); + return *this; +} + +} // namespace ovum::compiler::parser \ No newline at end of file diff --git a/lib/parser/ast/nodes/builders/stmts/UnsafeBlockBuilder.hpp b/lib/parser/ast/nodes/builders/stmts/UnsafeBlockBuilder.hpp new file mode 100644 index 0000000..ff3a6d5 --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/UnsafeBlockBuilder.hpp @@ -0,0 +1,19 @@ +#ifndef PARSER_UNSAFEBLOCKBUILDER_HPP_ +#define PARSER_UNSAFEBLOCKBUILDER_HPP_ + +#include + +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/ast/nodes/stmts/UnsafeBlock.hpp" + +namespace ovum::compiler::parser { + +class UnsafeBlockBuilder : public NodeBuilderBase { +public: + UnsafeBlockBuilder& WithBody(std::unique_ptr body); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_UNSAFEBLOCKBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/stmts/VarDeclStmtBuilder.cpp b/lib/parser/ast/nodes/builders/stmts/VarDeclStmtBuilder.cpp new file mode 100644 index 0000000..e185b64 --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/VarDeclStmtBuilder.cpp @@ -0,0 +1,29 @@ +#include "VarDeclStmtBuilder.hpp" + +namespace ovum::compiler::parser { + +VarDeclStmtBuilder& VarDeclStmtBuilder::WithVar(bool is_var) { + node_->SetVar(is_var); + return *this; +} + +VarDeclStmtBuilder& VarDeclStmtBuilder::WithIsVar(bool is_var) { + return WithVar(is_var); +} + +VarDeclStmtBuilder& VarDeclStmtBuilder::WithName(std::string name) { + node_->SetName(std::move(name)); + return *this; +} + +VarDeclStmtBuilder& VarDeclStmtBuilder::WithType(TypeReference type) { + node_->SetType(std::move(type)); + return *this; +} + +VarDeclStmtBuilder& VarDeclStmtBuilder::WithInit(std::unique_ptr init) { + node_->SetInit(std::move(init)); + return *this; +} + +} // namespace ovum::compiler::parser \ No newline at end of file diff --git a/lib/parser/ast/nodes/builders/stmts/VarDeclStmtBuilder.hpp b/lib/parser/ast/nodes/builders/stmts/VarDeclStmtBuilder.hpp new file mode 100644 index 0000000..368c034 --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/VarDeclStmtBuilder.hpp @@ -0,0 +1,25 @@ +#ifndef PARSER_VARDECLSTMTBUILDER_HPP_ +#define PARSER_VARDECLSTMTBUILDER_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/stmts/VarDeclStmt.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class VarDeclStmtBuilder : public NodeBuilderBase { +public: + VarDeclStmtBuilder& WithVar(bool is_var); + VarDeclStmtBuilder& WithIsVar(bool is_var); + VarDeclStmtBuilder& WithName(std::string name); + VarDeclStmtBuilder& WithType(TypeReference type); + VarDeclStmtBuilder& WithInit(std::unique_ptr init); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_VARDECLSTMTBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/builders/stmts/WhileStmtBuilder.cpp b/lib/parser/ast/nodes/builders/stmts/WhileStmtBuilder.cpp new file mode 100644 index 0000000..5d8406f --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/WhileStmtBuilder.cpp @@ -0,0 +1,15 @@ +#include "WhileStmtBuilder.hpp" + +namespace ovum::compiler::parser { + +WhileStmtBuilder& WhileStmtBuilder::WithCondition(std::unique_ptr cond) { + node_->SetCondition(std::move(cond)); + return *this; +} + +WhileStmtBuilder& WhileStmtBuilder::WithBody(std::unique_ptr body) { + node_->SetBody(std::move(body)); + return *this; +} + +} // namespace ovum::compiler::parser \ No newline at end of file diff --git a/lib/parser/ast/nodes/builders/stmts/WhileStmtBuilder.hpp b/lib/parser/ast/nodes/builders/stmts/WhileStmtBuilder.hpp new file mode 100644 index 0000000..a49a079 --- /dev/null +++ b/lib/parser/ast/nodes/builders/stmts/WhileStmtBuilder.hpp @@ -0,0 +1,21 @@ +#ifndef PARSER_WHILESTMTBUILDER_HPP_ +#define PARSER_WHILESTMTBUILDER_HPP_ + +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/ast/nodes/builders/base/NodeBuilderBase.hpp" +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/ast/nodes/stmts/WhileStmt.hpp" + +namespace ovum::compiler::parser { + +class WhileStmtBuilder : public NodeBuilderBase { +public: + WhileStmtBuilder& WithCondition(std::unique_ptr cond); + WhileStmtBuilder& WithBody(std::unique_ptr body); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_WHILESTMTBUILDER_HPP_ diff --git a/lib/parser/ast/nodes/class_members/CallDecl.cpp b/lib/parser/ast/nodes/class_members/CallDecl.cpp new file mode 100644 index 0000000..a84e922 --- /dev/null +++ b/lib/parser/ast/nodes/class_members/CallDecl.cpp @@ -0,0 +1,65 @@ +#include "lib/parser/ast/nodes/class_members/CallDecl.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void CallDecl::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +bool CallDecl::IsPublic() const noexcept { + return is_public_; +} + +void CallDecl::SetPublic(bool v) noexcept { + is_public_ = v; +} + +const std::vector& CallDecl::Params() const noexcept { + return params_; +} + +std::vector& CallDecl::MutableParams() noexcept { + return params_; +} + +void CallDecl::AddParam(Param param) { + params_.emplace_back(std::move(param)); +} + +const TypeReference* CallDecl::ReturnType() const noexcept { + return ret_type_.get(); +} + +TypeReference* CallDecl::MutableReturnType() noexcept { + return ret_type_.get(); +} + +void CallDecl::SetReturnType(std::unique_ptr type) { + ret_type_ = std::move(type); +} + +std::unique_ptr CallDecl::ReleaseReturnType() { + return std::move(ret_type_); +} + +const Block* CallDecl::Body() const noexcept { + return body_.get(); +} + +Block* CallDecl::MutableBody() noexcept { + return body_.get(); +} + +void CallDecl::SetBody(std::unique_ptr block) { + body_ = std::move(block); +} + +std::unique_ptr CallDecl::ReleaseBody() { + return std::move(body_); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/class_members/CallDecl.hpp b/lib/parser/ast/nodes/class_members/CallDecl.hpp new file mode 100644 index 0000000..6082620 --- /dev/null +++ b/lib/parser/ast/nodes/class_members/CallDecl.hpp @@ -0,0 +1,44 @@ +#ifndef PARSER_CALLDECL_HPP_ +#define PARSER_CALLDECL_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/base/Decl.hpp" +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/types/Param.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class CallDecl : public Decl { +public: + void Accept(AstVisitor& visitor) override; + + bool IsPublic() const noexcept; + void SetPublic(bool v) noexcept; + + const std::vector& Params() const noexcept; + std::vector& MutableParams() noexcept; + void AddParam(Param param); + + const TypeReference* ReturnType() const noexcept; + TypeReference* MutableReturnType() noexcept; + void SetReturnType(std::unique_ptr type); + std::unique_ptr ReleaseReturnType(); + + const Block* Body() const noexcept; + Block* MutableBody() noexcept; + void SetBody(std::unique_ptr block); + std::unique_ptr ReleaseBody(); + +private: + bool is_public_ = true; + std::vector params_; + std::unique_ptr ret_type_; + std::unique_ptr body_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_CALLDECL_HPP_ diff --git a/lib/parser/ast/nodes/class_members/DestructorDecl.cpp b/lib/parser/ast/nodes/class_members/DestructorDecl.cpp new file mode 100644 index 0000000..f965f0a --- /dev/null +++ b/lib/parser/ast/nodes/class_members/DestructorDecl.cpp @@ -0,0 +1,37 @@ +#include "lib/parser/ast/nodes/class_members/DestructorDecl.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void DestructorDecl::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +bool DestructorDecl::IsPublic() const noexcept { + return is_public_; +} + +void DestructorDecl::SetPublic(bool value) noexcept { + is_public_ = value; +} + +const Block* DestructorDecl::Body() const noexcept { + return body_.get(); +} + +Block* DestructorDecl::MutableBody() noexcept { + return body_.get(); +} + +void DestructorDecl::SetBody(std::unique_ptr block) { + body_ = std::move(block); +} + +std::unique_ptr DestructorDecl::ReleaseBody() { + return std::move(body_); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/class_members/DestructorDecl.hpp b/lib/parser/ast/nodes/class_members/DestructorDecl.hpp new file mode 100644 index 0000000..24f780a --- /dev/null +++ b/lib/parser/ast/nodes/class_members/DestructorDecl.hpp @@ -0,0 +1,30 @@ +#ifndef PARSER_DESTRUCTORDECL_HPP_ +#define PARSER_DESTRUCTORDECL_HPP_ + +#include + +#include "lib/parser/ast/nodes/base/Decl.hpp" +#include "lib/parser/ast/nodes/stmts/Block.hpp" + +namespace ovum::compiler::parser { + +class DestructorDecl : public Decl { +public: + void Accept(AstVisitor& visitor) override; + + bool IsPublic() const noexcept; + void SetPublic(bool value) noexcept; + + const Block* Body() const noexcept; + Block* MutableBody() noexcept; + void SetBody(std::unique_ptr block); + std::unique_ptr ReleaseBody(); + +private: + bool is_public_ = true; + std::unique_ptr body_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_DESTRUCTORDECL_HPP_ diff --git a/lib/parser/ast/nodes/class_members/FieldDecl.cpp b/lib/parser/ast/nodes/class_members/FieldDecl.cpp new file mode 100644 index 0000000..e073caa --- /dev/null +++ b/lib/parser/ast/nodes/class_members/FieldDecl.cpp @@ -0,0 +1,65 @@ +#include "lib/parser/ast/nodes/class_members/FieldDecl.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void FieldDecl::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +bool FieldDecl::IsPublic() const noexcept { + return is_public_; +} + +void FieldDecl::SetPublic(bool value) noexcept { + is_public_ = value; +} + +bool FieldDecl::IsVar() const noexcept { + return is_var_; +} + +void FieldDecl::SetVar(bool var) noexcept { + is_var_ = var; +} + +const std::string& FieldDecl::Name() const noexcept { + return name_; +} + +void FieldDecl::SetName(std::string name) { + name_ = std::move(name); +} + +const TypeReference& FieldDecl::Type() const noexcept { + return type_; +} + +TypeReference& FieldDecl::MutableType() noexcept { + return type_; +} + +void FieldDecl::SetType(TypeReference type) { + type_ = std::move(type); +} + +const Expr* FieldDecl::Init() const noexcept { + return init_.get(); +} + +Expr* FieldDecl::MutableInit() noexcept { + return init_.get(); +} + +void FieldDecl::SetInit(std::unique_ptr expr) { + init_ = std::move(expr); +} + +std::unique_ptr FieldDecl::ReleaseInit() { + return std::move(init_); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/class_members/FieldDecl.hpp b/lib/parser/ast/nodes/class_members/FieldDecl.hpp new file mode 100644 index 0000000..aa63002 --- /dev/null +++ b/lib/parser/ast/nodes/class_members/FieldDecl.hpp @@ -0,0 +1,45 @@ +#ifndef PARSER_FIELDDECL_HPP_ +#define PARSER_FIELDDECL_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/base/Decl.hpp" +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class FieldDecl : public Decl { +public: + void Accept(AstVisitor& visitor) override; + + bool IsPublic() const noexcept; + void SetPublic(bool value) noexcept; + + bool IsVar() const noexcept; + void SetVar(bool var) noexcept; + + const std::string& Name() const noexcept; + void SetName(std::string name); + + const TypeReference& Type() const noexcept; + TypeReference& MutableType() noexcept; + void SetType(TypeReference type); + + const Expr* Init() const noexcept; + Expr* MutableInit() noexcept; + void SetInit(std::unique_ptr expr); + std::unique_ptr ReleaseInit(); + +private: + bool is_public_ = true; + bool is_var_ = false; + std::string name_; + TypeReference type_; + std::unique_ptr init_; // optional +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_FIELDDECL_HPP_ diff --git a/lib/parser/ast/nodes/class_members/MethodDecl.cpp b/lib/parser/ast/nodes/class_members/MethodDecl.cpp new file mode 100644 index 0000000..8689d25 --- /dev/null +++ b/lib/parser/ast/nodes/class_members/MethodDecl.cpp @@ -0,0 +1,96 @@ +#include "lib/parser/ast/AstVisitor.hpp" +#include "lib/parser/ast/nodes/class_members/MethodDecl.hpp" + +#include + +namespace ovum::compiler::parser { + +void MethodDecl::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +bool MethodDecl::IsPublic() const noexcept { + return is_public_; +} + +void MethodDecl::SetPublic(bool is_public) noexcept { + is_public_ = is_public; +} + +bool MethodDecl::IsOverride() const noexcept { + return is_override_; +} + +void MethodDecl::SetOverride(bool is_override) noexcept { + is_override_ = is_override; +} + +bool MethodDecl::IsStatic() const noexcept { + return is_static_; +} + +void MethodDecl::SetStatic(bool is_static) noexcept { + is_static_ = is_static; +} + +bool MethodDecl::IsPure() const noexcept { + return is_pure_; +} + +void MethodDecl::SetPure(bool is_pure) noexcept { + is_pure_ = is_pure; +} + +const std::string& MethodDecl::Name() const noexcept { + return name; +} + +void MethodDecl::SetName(std::string new_name) { + name = std::move(new_name); +} + +const std::vector& MethodDecl::Params() const noexcept { + return params; +} + +std::vector& MethodDecl::MutableParams() noexcept { + return params; +} + +void MethodDecl::AddParam(Param param) { + params.emplace_back(std::move(param)); +} + +const TypeReference* MethodDecl::ReturnType() const noexcept { + return ret_type.get(); +} + +TypeReference* MethodDecl::MutableReturnType() noexcept { + return ret_type.get(); +} + +void MethodDecl::SetReturnType(std::unique_ptr type) { + ret_type = std::move(type); +} + +std::unique_ptr MethodDecl::ReleaseReturnType() { + return std::move(ret_type); +} + +const Block* MethodDecl::Body() const noexcept { + return body.get(); +} + +Block* MethodDecl::MutableBody() noexcept { + return body.get(); +} + +void MethodDecl::SetBody(std::unique_ptr block) { + body = std::move(block); +} + +std::unique_ptr MethodDecl::ReleaseBody() { + return std::move(body); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/class_members/MethodDecl.hpp b/lib/parser/ast/nodes/class_members/MethodDecl.hpp new file mode 100644 index 0000000..7702c00 --- /dev/null +++ b/lib/parser/ast/nodes/class_members/MethodDecl.hpp @@ -0,0 +1,62 @@ +#ifndef PARSER_METHODDECL_HPP_ +#define PARSER_METHODDECL_HPP_ + +#include +#include +#include + +#include "lib/parser/ast/nodes/base/Decl.hpp" +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/types/Param.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class MethodDecl : public Decl { +public: + void Accept(AstVisitor& visitor) override; + + bool IsPublic() const noexcept; + void SetPublic(bool is_public) noexcept; + + bool IsOverride() const noexcept; + void SetOverride(bool is_override) noexcept; + + bool IsStatic() const noexcept; + void SetStatic(bool is_static) noexcept; + + bool IsPure() const noexcept; + void SetPure(bool is_pure) noexcept; + + const std::string& Name() const noexcept; + void SetName(std::string new_name); + + const std::vector& Params() const noexcept; + std::vector& MutableParams() noexcept; + void AddParam(Param param); + + const TypeReference* ReturnType() const noexcept; + TypeReference* MutableReturnType() noexcept; + void SetReturnType(std::unique_ptr type); + std::unique_ptr ReleaseReturnType(); + + const Block* Body() const noexcept; + Block* MutableBody() noexcept; + void SetBody(std::unique_ptr block); + std::unique_ptr ReleaseBody(); + +private: + bool is_public_ = true; + bool is_override_ = false; + bool is_static_ = false; + bool is_pure_ = false; + + std::string name; + std::vector params; + std::unique_ptr ret_type; + std::unique_ptr body; // optional +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_METHODDECL_HPP_ diff --git a/lib/parser/ast/nodes/class_members/StaticFieldDecl.cpp b/lib/parser/ast/nodes/class_members/StaticFieldDecl.cpp new file mode 100644 index 0000000..a2470b4 --- /dev/null +++ b/lib/parser/ast/nodes/class_members/StaticFieldDecl.cpp @@ -0,0 +1,65 @@ +#include "lib/parser/ast/nodes/class_members/StaticFieldDecl.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void StaticFieldDecl::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +bool StaticFieldDecl::IsPublic() const noexcept { + return is_public_; +} + +void StaticFieldDecl::SetPublic(bool is_public) noexcept { + is_public_ = is_public; +} + +bool StaticFieldDecl::IsVar() const noexcept { + return is_var_; +} + +void StaticFieldDecl::SetVar(bool is_var) noexcept { + is_var_ = is_var; +} + +const std::string& StaticFieldDecl::Name() const noexcept { + return name_; +} + +void StaticFieldDecl::SetName(std::string new_name) { + name_ = std::move(new_name); +} + +const TypeReference& StaticFieldDecl::Type() const noexcept { + return type_; +} + +TypeReference& StaticFieldDecl::MutableType() noexcept { + return type_; +} + +void StaticFieldDecl::SetType(TypeReference type) { + type_ = std::move(type); +} + +const Expr* StaticFieldDecl::Init() const noexcept { + return init_.get(); +} + +Expr* StaticFieldDecl::MutableInit() noexcept { + return init_.get(); +} + +void StaticFieldDecl::SetInit(std::unique_ptr expr) { + init_ = std::move(expr); +} + +std::unique_ptr StaticFieldDecl::ReleaseInit() { + return std::move(init_); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/class_members/StaticFieldDecl.hpp b/lib/parser/ast/nodes/class_members/StaticFieldDecl.hpp new file mode 100644 index 0000000..09b5cd7 --- /dev/null +++ b/lib/parser/ast/nodes/class_members/StaticFieldDecl.hpp @@ -0,0 +1,45 @@ +#ifndef PARSER_STATICFIELDDECL_HPP_ +#define PARSER_STATICFIELDDECL_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/base/Decl.hpp" +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class StaticFieldDecl : public Decl { +public: + void Accept(AstVisitor& visitor) override; + + bool IsPublic() const noexcept; + void SetPublic(bool is_public) noexcept; + + bool IsVar() const noexcept; + void SetVar(bool is_var) noexcept; + + const std::string& Name() const noexcept; + void SetName(std::string new_name); + + const TypeReference& Type() const noexcept; + TypeReference& MutableType() noexcept; + void SetType(TypeReference type); + + const Expr* Init() const noexcept; + Expr* MutableInit() noexcept; + void SetInit(std::unique_ptr expr); + std::unique_ptr ReleaseInit(); + +private: + bool is_public_ = true; + bool is_var_ = false; + std::string name_; + TypeReference type_; + std::unique_ptr init_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATICFIELDDECL_HPP_ diff --git a/lib/parser/ast/nodes/decls/ClassDecl.cpp b/lib/parser/ast/nodes/decls/ClassDecl.cpp new file mode 100644 index 0000000..5f1f049 --- /dev/null +++ b/lib/parser/ast/nodes/decls/ClassDecl.cpp @@ -0,0 +1,53 @@ +#include "ClassDecl.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +namespace ovum::compiler::parser { + +void ClassDecl::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const std::string& ClassDecl::Name() const noexcept { + return name_; +} + +void ClassDecl::SetName(std::string name) { + name_ = std::move(name); +} + +const std::vector& ClassDecl::Implements() const noexcept { + return implements_; +} + +std::vector& ClassDecl::MutableImplements() noexcept { + return implements_; +} + +void ClassDecl::AddImplements(TypeReference type) { + implements_.emplace_back(std::move(type)); +} + +const std::vector>& ClassDecl::Members() const noexcept { + return members_; +} + +std::vector>& ClassDecl::MutableMembers() noexcept { + return members_; +} + +void ClassDecl::AddMember(std::unique_ptr decl) { + members_.emplace_back(std::move(decl)); +} + +std::unique_ptr ClassDecl::ReleaseMember(std::size_t index) { + if (index >= members_.size()) { + return nullptr; + } + + auto old = std::move(members_[index]); + members_.erase(members_.begin() + static_cast(index)); + return old; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/decls/ClassDecl.hpp b/lib/parser/ast/nodes/decls/ClassDecl.hpp new file mode 100644 index 0000000..6e93ebe --- /dev/null +++ b/lib/parser/ast/nodes/decls/ClassDecl.hpp @@ -0,0 +1,37 @@ +#ifndef PARSER_CLASSDECL_HPP_ +#define PARSER_CLASSDECL_HPP_ + +#include +#include +#include + +#include "lib/parser/ast/nodes/base/Decl.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class ClassDecl : public Decl { +public: + void Accept(AstVisitor& visitor) override; + + const std::string& Name() const noexcept; + void SetName(std::string name); + + const std::vector& Implements() const noexcept; + std::vector& MutableImplements() noexcept; + void AddImplements(TypeReference type); + + const std::vector>& Members() const noexcept; + std::vector>& MutableMembers() noexcept; + void AddMember(std::unique_ptr decl); + std::unique_ptr ReleaseMember(std::size_t index); + +private: + std::string name_; + std::vector implements_; + std::vector> members_; // Field/StaticField/Method/Call/Destructor +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_CLASSDECL_HPP_ diff --git a/lib/parser/ast/nodes/decls/FunctionDecl.cpp b/lib/parser/ast/nodes/decls/FunctionDecl.cpp new file mode 100644 index 0000000..12afb59 --- /dev/null +++ b/lib/parser/ast/nodes/decls/FunctionDecl.cpp @@ -0,0 +1,70 @@ +#include "FunctionDecl.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +namespace ovum::compiler::parser { + +void FunctionDecl::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +bool FunctionDecl::IsPure() const noexcept { + return is_pure_; +} + +void FunctionDecl::SetPure(bool is_pure) noexcept { + is_pure_ = is_pure; +} + +const std::string& FunctionDecl::Name() const noexcept { + return name_; +} + +void FunctionDecl::SetName(std::string new_name) { + name_ = std::move(new_name); +} + +const std::vector& FunctionDecl::Params() const noexcept { + return params_; +} + +std::vector& FunctionDecl::MutableParams() noexcept { + return params_; +} + +void FunctionDecl::AddParam(Param param) { + params_.emplace_back(std::move(param)); +} + +const TypeReference* FunctionDecl::ReturnType() const noexcept { + return return_type_.get(); +} +TypeReference* FunctionDecl::MutableReturnType() noexcept { + return return_type_.get(); +} + +void FunctionDecl::SetReturnType(std::unique_ptr type) { + return_type_ = std::move(type); +} + +std::unique_ptr FunctionDecl::ReleaseReturnType() { + return std::move(return_type_); +} + +const Block* FunctionDecl::Body() const noexcept { + return body_.get(); +} + +Block* FunctionDecl::MutableBody() noexcept { + return body_.get(); +} + +void FunctionDecl::SetBody(std::unique_ptr block) { + body_ = std::move(block); +} + +std::unique_ptr FunctionDecl::ReleaseBody() { + return std::move(body_); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/decls/FunctionDecl.hpp b/lib/parser/ast/nodes/decls/FunctionDecl.hpp new file mode 100644 index 0000000..198e234 --- /dev/null +++ b/lib/parser/ast/nodes/decls/FunctionDecl.hpp @@ -0,0 +1,50 @@ +#ifndef PARSER_FUNCTIONDECL_HPP_ +#define PARSER_FUNCTIONDECL_HPP_ + +#include +#include +#include + +#include "lib/parser/ast/nodes/base/Decl.hpp" +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/types/Param.hpp" + +namespace ovum::compiler::parser { + +class TypeReference; + +class FunctionDecl : public Decl { +public: + void Accept(AstVisitor& visitor) override; + + bool IsPure() const noexcept; + void SetPure(bool is_pure) noexcept; + + const std::string& Name() const noexcept; + void SetName(std::string new_name); + + const std::vector& Params() const noexcept; + std::vector& MutableParams() noexcept; + void AddParam(Param param); + + const TypeReference* ReturnType() const noexcept; + TypeReference* MutableReturnType() noexcept; + void SetReturnType(std::unique_ptr type); + std::unique_ptr ReleaseReturnType(); + + const Block* Body() const noexcept; + Block* MutableBody() noexcept; + void SetBody(std::unique_ptr block); + std::unique_ptr ReleaseBody(); + +private: + bool is_pure_ = false; + std::string name_; + std::vector params_; + std::unique_ptr return_type_; // optional + std::unique_ptr body_; // optional +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_FUNCTIONDECL_HPP_ diff --git a/lib/parser/ast/nodes/decls/GlobalVarDecl.cpp b/lib/parser/ast/nodes/decls/GlobalVarDecl.cpp new file mode 100644 index 0000000..97964dd --- /dev/null +++ b/lib/parser/ast/nodes/decls/GlobalVarDecl.cpp @@ -0,0 +1,55 @@ +#include "GlobalVarDecl.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +namespace ovum::compiler::parser { + +void GlobalVarDecl::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +bool GlobalVarDecl::IsVar() const noexcept { + return is_var_; +} + +void GlobalVarDecl::SetVar(bool is_var) noexcept { + is_var_ = is_var; +} + +const std::string& GlobalVarDecl::Name() const noexcept { + return name_; +} + +void GlobalVarDecl::SetName(std::string new_name) { + name_ = std::move(new_name); +} + +const TypeReference& GlobalVarDecl::Type() const noexcept { + return type_; +} + +TypeReference& GlobalVarDecl::MutableType() noexcept { + return type_; +} + +void GlobalVarDecl::SetType(TypeReference type) { + type_ = std::move(type); +} + +const Expr* GlobalVarDecl::Init() const noexcept { + return init_.get(); +} + +Expr* GlobalVarDecl::MutableInit() noexcept { + return init_.get(); +} + +void GlobalVarDecl::SetInit(std::unique_ptr expr) { + init_ = std::move(expr); +} + +std::unique_ptr GlobalVarDecl::ReleaseInit() { + return std::move(init_); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/decls/GlobalVarDecl.hpp b/lib/parser/ast/nodes/decls/GlobalVarDecl.hpp new file mode 100644 index 0000000..ebcba68 --- /dev/null +++ b/lib/parser/ast/nodes/decls/GlobalVarDecl.hpp @@ -0,0 +1,41 @@ +#ifndef PARSER_GLOBALVARDECL_HPP_ +#define PARSER_GLOBALVARDECL_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/base/Decl.hpp" +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class GlobalVarDecl : public Decl { +public: + void Accept(AstVisitor& visitor) override; + + bool IsVar() const noexcept; + void SetVar(bool is_var) noexcept; + + const std::string& Name() const noexcept; + void SetName(std::string new_name); + + const TypeReference& Type() const noexcept; + TypeReference& MutableType() noexcept; + void SetType(TypeReference type); + + const Expr* Init() const noexcept; + Expr* MutableInit() noexcept; + void SetInit(std::unique_ptr expr); + std::unique_ptr ReleaseInit(); + +private: + bool is_var_ = false; // var=true, val=false + std::string name_; + TypeReference type_; + std::unique_ptr init_; // optional +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_GLOBALVARDECL_HPP_ diff --git a/lib/parser/ast/nodes/decls/InterfaceDecl.cpp b/lib/parser/ast/nodes/decls/InterfaceDecl.cpp new file mode 100644 index 0000000..8a36942 --- /dev/null +++ b/lib/parser/ast/nodes/decls/InterfaceDecl.cpp @@ -0,0 +1,41 @@ +#include "InterfaceDecl.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +namespace ovum::compiler::parser { + +void InterfaceDecl::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const std::string& InterfaceDecl::Name() const noexcept { + return name_; +} + +void InterfaceDecl::SetName(std::string new_name) { + name_ = std::move(new_name); +} + +const std::vector>& InterfaceDecl::Members() const noexcept { + return methods_; +} + +std::vector>& InterfaceDecl::MutableMembers() noexcept { + return methods_; +} + +void InterfaceDecl::AddMember(std::unique_ptr method) { + methods_.emplace_back(std::move(method)); +} + +std::unique_ptr InterfaceDecl::ReleaseMember(std::size_t index) { + if (index >= methods_.size()) { + return nullptr; + } + + auto old = std::move(methods_[index]); + methods_.erase(methods_.begin() + static_cast(index)); + return old; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/decls/InterfaceDecl.hpp b/lib/parser/ast/nodes/decls/InterfaceDecl.hpp new file mode 100644 index 0000000..752810a --- /dev/null +++ b/lib/parser/ast/nodes/decls/InterfaceDecl.hpp @@ -0,0 +1,33 @@ +#ifndef PARSER_INTERFACEDECL_HPP_ +#define PARSER_INTERFACEDECL_HPP_ + +#include +#include +#include + +#include "InterfaceMethod.hpp" +#include "lib/parser/ast/nodes/base/Decl.hpp" + +namespace ovum::compiler::parser { + +class InterfaceDecl : public Decl { +public: + void Accept(AstVisitor& visitor) override; + + const std::string& Name() const noexcept; + void SetName(std::string new_name); + + const std::vector>& Members() const noexcept; + std::vector>& MutableMembers() noexcept; + + void AddMember(std::unique_ptr method); + std::unique_ptr ReleaseMember(std::size_t index); + +private: + std::string name_; + std::vector> methods_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_INTERFACEDECL_HPP_ diff --git a/lib/parser/ast/nodes/decls/InterfaceMethod.cpp b/lib/parser/ast/nodes/decls/InterfaceMethod.cpp new file mode 100644 index 0000000..a5d49bc --- /dev/null +++ b/lib/parser/ast/nodes/decls/InterfaceMethod.cpp @@ -0,0 +1,37 @@ +#include "lib/parser/ast/nodes/decls/InterfaceMethod.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void InterfaceMethod::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const std::string& InterfaceMethod::Name() const noexcept { + return name_; +} + +void InterfaceMethod::SetName(std::string new_name) { + name_ = std::move(new_name); +} + +const std::vector& InterfaceMethod::Params() const noexcept { + return params_; +} + +std::vector& InterfaceMethod::MutableParams() noexcept { + return params_; +} + +const TypeReference* InterfaceMethod::ReturnType() const noexcept { + return ret_type_.get(); +} + +void InterfaceMethod::SetReturnType(std::unique_ptr type) { + ret_type_ = std::move(type); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/decls/InterfaceMethod.hpp b/lib/parser/ast/nodes/decls/InterfaceMethod.hpp new file mode 100644 index 0000000..b94dfbc --- /dev/null +++ b/lib/parser/ast/nodes/decls/InterfaceMethod.hpp @@ -0,0 +1,39 @@ +#ifndef PARSER_INTERFACEMETHOD_HPP_ +#define PARSER_INTERFACEMETHOD_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/base/AstNode.hpp" +#include "lib/parser/ast/nodes/base/Decl.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class InterfaceMethod : public Decl { +public: + struct Param { + std::string name; + TypeReference type; + }; + + void Accept(AstVisitor& visitor) override; + + const std::string& Name() const noexcept; + void SetName(std::string new_name); + + const std::vector& Params() const noexcept; + std::vector& MutableParams() noexcept; + + const TypeReference* ReturnType() const noexcept; + void SetReturnType(std::unique_ptr type); + +private: + std::string name_; + std::vector params_; + std::unique_ptr ret_type_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_INTERFACEMETHOD_HPP_ diff --git a/lib/parser/ast/nodes/decls/Module.cpp b/lib/parser/ast/nodes/decls/Module.cpp new file mode 100644 index 0000000..1cfbb33 --- /dev/null +++ b/lib/parser/ast/nodes/decls/Module.cpp @@ -0,0 +1,49 @@ +#include "Module.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +namespace ovum::compiler::parser { + +void Module::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const std::string& Module::Name() const noexcept { + return name_; +} + +void Module::SetName(std::string new_name) { + name_ = std::move(new_name); +} + +const SourceId& Module::Source() const noexcept { + return source_; +} + +void Module::SetSource(SourceId id) { + source_ = std::move(id); +} + +const std::vector>& Module::Decls() const noexcept { + return decls_; +} + +std::vector>& Module::MutableDecls() noexcept { + return decls_; +} + +void Module::AddDecl(std::unique_ptr decl) { + decls_.emplace_back(std::move(decl)); +} + +std::unique_ptr Module::ReleaseDecl(std::size_t index) { + if (index >= decls_.size()) { + return nullptr; + } + + auto old = std::move(decls_[index]); + decls_.erase(decls_.begin() + static_cast(index)); + return std::move(old); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/decls/Module.hpp b/lib/parser/ast/nodes/decls/Module.hpp new file mode 100644 index 0000000..341b246 --- /dev/null +++ b/lib/parser/ast/nodes/decls/Module.hpp @@ -0,0 +1,37 @@ +#ifndef PARSER_MODULE_HPP_ +#define PARSER_MODULE_HPP_ + +#include +#include +#include + +#include "lib/parser/ast/nodes/base/AstNode.hpp" +#include "lib/parser/ast/nodes/base/Decl.hpp" +#include "lib/parser/tokens/SourceId.hpp" + +namespace ovum::compiler::parser { + +class Module : public AstNode { +public: + void Accept(AstVisitor& visitor) override; + + const std::string& Name() const noexcept; + void SetName(std::string new_name); + + const SourceId& Source() const noexcept; + void SetSource(SourceId id); + + const std::vector>& Decls() const noexcept; + std::vector>& MutableDecls() noexcept; + void AddDecl(std::unique_ptr decl); + std::unique_ptr ReleaseDecl(std::size_t index); + +private: + std::string name_; + SourceId source_; + std::vector> decls_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_MODULE_HPP_ diff --git a/lib/parser/ast/nodes/decls/TypeAliasDecl.cpp b/lib/parser/ast/nodes/decls/TypeAliasDecl.cpp new file mode 100644 index 0000000..7cdc767 --- /dev/null +++ b/lib/parser/ast/nodes/decls/TypeAliasDecl.cpp @@ -0,0 +1,33 @@ +#include "lib/parser/ast/nodes/decls/TypeAliasDecl.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void TypeAliasDecl::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const std::string& TypeAliasDecl::Name() const noexcept { + return name_; +} + +void TypeAliasDecl::SetName(std::string new_name) { + name_ = std::move(new_name); +} + +const TypeReference& TypeAliasDecl::AliasedType() const noexcept { + return aliased_type_; +} + +TypeReference& TypeAliasDecl::MutableAliasedType() noexcept { + return aliased_type_; +} + +void TypeAliasDecl::SetAliasedType(TypeReference type) { + aliased_type_ = std::move(type); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/decls/TypeAliasDecl.hpp b/lib/parser/ast/nodes/decls/TypeAliasDecl.hpp new file mode 100644 index 0000000..352a355 --- /dev/null +++ b/lib/parser/ast/nodes/decls/TypeAliasDecl.hpp @@ -0,0 +1,29 @@ +#ifndef PARSER_TYPEALIASDECL_HPP_ +#define PARSER_TYPEALIASDECL_HPP_ + +#include + +#include "lib/parser/ast/nodes/base/Decl.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class TypeAliasDecl : public Decl { +public: + void Accept(AstVisitor& visitor) override; + + const std::string& Name() const noexcept; + void SetName(std::string new_name); + + const TypeReference& AliasedType() const noexcept; + TypeReference& MutableAliasedType() noexcept; + void SetAliasedType(TypeReference type); + +private: + std::string name_; + TypeReference aliased_type_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_TYPEALIASDECL_HPP_ diff --git a/lib/parser/ast/nodes/exprs/Assign.cpp b/lib/parser/ast/nodes/exprs/Assign.cpp new file mode 100644 index 0000000..c38d05e --- /dev/null +++ b/lib/parser/ast/nodes/exprs/Assign.cpp @@ -0,0 +1,57 @@ +#include "lib/parser/ast/nodes/exprs/Assign.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void Assign::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const IAssignOpTag& Assign::Kind() const noexcept { + return *kind_; +} + +void Assign::SetKind(const IAssignOpTag& new_kind) noexcept { + kind_ = &new_kind; +} + +const Expr& Assign::Target() const noexcept { + return *target_; +} + +Expr& Assign::MutableTarget() noexcept { + return *target_; +} + +void Assign::SetTarget(std::unique_ptr new_target) { + target_ = std::move(new_target); +} + +std::unique_ptr Assign::ReplaceTarget(std::unique_ptr new_target) { + auto old_target = std::move(target_); + target_ = std::move(new_target); + return old_target; +} + +const Expr& Assign::Value() const noexcept { + return *value_; +} + +Expr& Assign::MutableValue() noexcept { + return *value_; +} + +void Assign::SetValue(std::unique_ptr new_value) { + value_ = std::move(new_value); +} + +std::unique_ptr Assign::ReplaceValue(std::unique_ptr new_value) { + auto old_value = std::move(value_); + value_ = std::move(new_value); + return old_value; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/exprs/Assign.hpp b/lib/parser/ast/nodes/exprs/Assign.hpp new file mode 100644 index 0000000..a49ae85 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/Assign.hpp @@ -0,0 +1,37 @@ +#ifndef PARSER_ASSIGN_HPP_ +#define PARSER_ASSIGN_HPP_ + +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "tags/IAssignOpTag.hpp" +#include "tags/OpTags.hpp" + +namespace ovum::compiler::parser { + +class Assign : public Expr { +public: + void Accept(AstVisitor& visitor) override; + + const IAssignOpTag& Kind() const noexcept; + void SetKind(const IAssignOpTag& new_kind) noexcept; + + const Expr& Target() const noexcept; + Expr& MutableTarget() noexcept; + void SetTarget(std::unique_ptr new_target); + std::unique_ptr ReplaceTarget(std::unique_ptr new_target); + + const Expr& Value() const noexcept; + Expr& MutableValue() noexcept; + void SetValue(std::unique_ptr new_value); + std::unique_ptr ReplaceValue(std::unique_ptr new_value); + +private: + const IAssignOpTag* kind_ = &OpTags::RefAssign(); + std::unique_ptr target_; + std::unique_ptr value_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_ASSIGN_HPP_ diff --git a/lib/parser/ast/nodes/exprs/Binary.cpp b/lib/parser/ast/nodes/exprs/Binary.cpp new file mode 100644 index 0000000..c6e843e --- /dev/null +++ b/lib/parser/ast/nodes/exprs/Binary.cpp @@ -0,0 +1,57 @@ +#include "lib/parser/ast/nodes/exprs/Binary.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void Binary::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const IBinaryOpTag& Binary::Op() const noexcept { + return *op_; +} + +void Binary::SetOp(const IBinaryOpTag& new_op) noexcept { + op_ = &new_op; +} + +const Expr& Binary::Lhs() const noexcept { + return *lhs_; +} + +Expr& Binary::MutableLhs() noexcept { + return *lhs_; +} + +void Binary::SetLhs(std::unique_ptr new_lhs) { + lhs_ = std::move(new_lhs); +} + +std::unique_ptr Binary::ReplaceLhs(std::unique_ptr new_lhs) { + auto old_lhs = std::move(lhs_); + lhs_ = std::move(new_lhs); + return old_lhs; +} + +const Expr& Binary::Rhs() const noexcept { + return *rhs_; +} + +Expr& Binary::MutableRhs() noexcept { + return *rhs_; +} + +void Binary::SetRhs(std::unique_ptr new_rhs) { + rhs_ = std::move(new_rhs); +} + +std::unique_ptr Binary::ReplaceRhs(std::unique_ptr new_rhs) { + auto old_rhs = std::move(rhs_); + rhs_ = std::move(new_rhs); + return old_rhs; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/exprs/Binary.hpp b/lib/parser/ast/nodes/exprs/Binary.hpp new file mode 100644 index 0000000..ac2dcf6 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/Binary.hpp @@ -0,0 +1,37 @@ +#ifndef PARSER_BINARY_HPP_ +#define PARSER_BINARY_HPP_ + +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "tags/IBinaryOpTag.hpp" +#include "tags/OpTags.hpp" + +namespace ovum::compiler::parser { + +class Binary : public Expr { +public: + void Accept(AstVisitor& visitor) override; + + const IBinaryOpTag& Op() const noexcept; + void SetOp(const IBinaryOpTag& new_op) noexcept; + + const Expr& Lhs() const noexcept; + Expr& MutableLhs() noexcept; + void SetLhs(std::unique_ptr new_lhs); + std::unique_ptr ReplaceLhs(std::unique_ptr new_lhs); + + const Expr& Rhs() const noexcept; + Expr& MutableRhs() noexcept; + void SetRhs(std::unique_ptr new_rhs); + std::unique_ptr ReplaceRhs(std::unique_ptr new_rhs); + +private: + const IBinaryOpTag* op_ = &OpTags::Add(); + std::unique_ptr lhs_; + std::unique_ptr rhs_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_BINARY_HPP_ diff --git a/lib/parser/ast/nodes/exprs/Call.cpp b/lib/parser/ast/nodes/exprs/Call.cpp new file mode 100644 index 0000000..12f4417 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/Call.cpp @@ -0,0 +1,46 @@ +#include "lib/parser/ast/AstVisitor.hpp" +#include "lib/parser/ast/nodes/exprs/Call.hpp" + +#include + +namespace ovum::compiler::parser { + +void Call::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const Expr& Call::Callee() const noexcept { + return *callee_; +} + +Expr& Call::MutableCallee() noexcept { + return *callee_; +} + +void Call::SetCallee(std::unique_ptr new_callee) { + callee_ = std::move(new_callee); +} + +std::unique_ptr Call::ReplaceCallee(std::unique_ptr new_callee) { + auto old_callee = std::move(callee_); + callee_ = std::move(new_callee); + return old_callee; +} + +const std::vector>& Call::Args() const noexcept { + return args_; +} + +std::vector>& Call::MutableArgs() noexcept { + return args_; +} + +void Call::AddArg(std::unique_ptr new_arg) { + args_.emplace_back(std::move(new_arg)); +} + +void Call::ClearArgs() { + args_.clear(); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/exprs/Call.hpp b/lib/parser/ast/nodes/exprs/Call.hpp new file mode 100644 index 0000000..6217740 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/Call.hpp @@ -0,0 +1,32 @@ +#ifndef PARSER_CALL_HPP_ +#define PARSER_CALL_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" + +namespace ovum::compiler::parser { + +class Call : public Expr { +public: + void Accept(AstVisitor& visitor) override; + + const Expr& Callee() const noexcept; + Expr& MutableCallee() noexcept; + void SetCallee(std::unique_ptr new_callee); + std::unique_ptr ReplaceCallee(std::unique_ptr new_callee); + + const std::vector>& Args() const noexcept; + std::vector>& MutableArgs() noexcept; + void AddArg(std::unique_ptr new_arg); + void ClearArgs(); + +private: + std::unique_ptr callee_; + std::vector> args_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_CALL_HPP_ diff --git a/lib/parser/ast/nodes/exprs/CastAs.cpp b/lib/parser/ast/nodes/exprs/CastAs.cpp new file mode 100644 index 0000000..9bf495f --- /dev/null +++ b/lib/parser/ast/nodes/exprs/CastAs.cpp @@ -0,0 +1,43 @@ +#include "lib/parser/ast/nodes/exprs/CastAs.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void CastAs::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const Expr& CastAs::Expression() const noexcept { + return *expr_; +} + +Expr& CastAs::MutableExpression() noexcept { + return *expr_; +} + +void CastAs::SetExpression(std::unique_ptr new_expression) { + expr_ = std::move(new_expression); +} + +std::unique_ptr CastAs::ReplaceExpression(std::unique_ptr new_expression) { + auto old_expr = std::move(expr_); + expr_ = std::move(new_expression); + return old_expr; +} + +const TypeReference& CastAs::Type() const noexcept { + return type_; +} + +TypeReference& CastAs::MutableType() noexcept { + return type_; +} + +void CastAs::SetType(TypeReference new_type) { + type_ = std::move(new_type); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/exprs/CastAs.hpp b/lib/parser/ast/nodes/exprs/CastAs.hpp new file mode 100644 index 0000000..fc9c88a --- /dev/null +++ b/lib/parser/ast/nodes/exprs/CastAs.hpp @@ -0,0 +1,31 @@ +#ifndef PARSER_CASTAS_HPP_ +#define PARSER_CASTAS_HPP_ + +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class CastAs : public Expr { +public: + void Accept(AstVisitor& visitor) override; + + const Expr& Expression() const noexcept; + Expr& MutableExpression() noexcept; + void SetExpression(std::unique_ptr new_expression); + std::unique_ptr ReplaceExpression(std::unique_ptr new_expression); + + const TypeReference& Type() const noexcept; + TypeReference& MutableType() noexcept; + void SetType(TypeReference new_type); + +private: + std::unique_ptr expr_; + TypeReference type_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_CASTAS_HPP_ diff --git a/lib/parser/ast/nodes/exprs/Elvis.cpp b/lib/parser/ast/nodes/exprs/Elvis.cpp new file mode 100644 index 0000000..c19b6d3 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/Elvis.cpp @@ -0,0 +1,49 @@ +#include "lib/parser/ast/nodes/exprs/Elvis.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void Elvis::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const Expr& Elvis::Lhs() const noexcept { + return *lhs_; +} + +Expr& Elvis::MutableLhs() noexcept { + return *lhs_; +} + +void Elvis::SetLhs(std::unique_ptr new_lhs) { + lhs_ = std::move(new_lhs); +} + +std::unique_ptr Elvis::ReplaceLhs(std::unique_ptr new_lhs) { + auto old_lhs = std::move(lhs_); + lhs_ = std::move(new_lhs); + return old_lhs; +} + +const Expr& Elvis::Rhs() const noexcept { + return *rhs_; +} + +Expr& Elvis::MutableRhs() noexcept { + return *rhs_; +} + +void Elvis::SetRhs(std::unique_ptr new_rhs) { + rhs_ = std::move(new_rhs); +} + +std::unique_ptr Elvis::ReplaceRhs(std::unique_ptr new_rhs) { + auto old_rhs = std::move(rhs_); + rhs_ = std::move(new_rhs); + return old_rhs; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/exprs/Elvis.hpp b/lib/parser/ast/nodes/exprs/Elvis.hpp new file mode 100644 index 0000000..0511bb8 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/Elvis.hpp @@ -0,0 +1,31 @@ +#ifndef PARSER_ELVIS_HPP_ +#define PARSER_ELVIS_HPP_ + +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" + +namespace ovum::compiler::parser { + +class Elvis : public Expr { +public: + void Accept(AstVisitor& visitor) override; + + const Expr& Lhs() const noexcept; + Expr& MutableLhs() noexcept; + void SetLhs(std::unique_ptr new_lhs); + std::unique_ptr ReplaceLhs(std::unique_ptr new_lhs); + + const Expr& Rhs() const noexcept; + Expr& MutableRhs() noexcept; + void SetRhs(std::unique_ptr new_rhs); + std::unique_ptr ReplaceRhs(std::unique_ptr new_rhs); + +private: + std::unique_ptr lhs_; + std::unique_ptr rhs_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_ELVIS_HPP_ diff --git a/lib/parser/ast/nodes/exprs/FieldAccess.cpp b/lib/parser/ast/nodes/exprs/FieldAccess.cpp new file mode 100644 index 0000000..67395ae --- /dev/null +++ b/lib/parser/ast/nodes/exprs/FieldAccess.cpp @@ -0,0 +1,39 @@ +#include "lib/parser/ast/nodes/exprs/FieldAccess.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void FieldAccess::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const Expr& FieldAccess::Object() const noexcept { + return *object_; +} + +Expr& FieldAccess::MutableObject() noexcept { + return *object_; +} + +void FieldAccess::SetObject(std::unique_ptr new_object) { + object_ = std::move(new_object); +} + +std::unique_ptr FieldAccess::ReplaceObject(std::unique_ptr new_object) { + auto old_object = std::move(object_); + object_ = std::move(new_object); + return old_object; +} + +const std::string& FieldAccess::Name() const noexcept { + return name_; +} + +void FieldAccess::SetName(std::string new_name) { + name_ = std::move(new_name); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/exprs/FieldAccess.hpp b/lib/parser/ast/nodes/exprs/FieldAccess.hpp new file mode 100644 index 0000000..96cbed9 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/FieldAccess.hpp @@ -0,0 +1,30 @@ +#ifndef PARSER_FIELDACCESS_HPP_ +#define PARSER_FIELDACCESS_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" + +namespace ovum::compiler::parser { + +class FieldAccess : public Expr { +public: + void Accept(AstVisitor& visitor) override; + + const Expr& Object() const noexcept; + Expr& MutableObject() noexcept; + void SetObject(std::unique_ptr new_object); + std::unique_ptr ReplaceObject(std::unique_ptr new_object); + + const std::string& Name() const noexcept; + void SetName(std::string new_name); + +private: + std::unique_ptr object_; + std::string name_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_FIELDACCESS_HPP_ diff --git a/lib/parser/ast/nodes/exprs/IdentRef.cpp b/lib/parser/ast/nodes/exprs/IdentRef.cpp new file mode 100644 index 0000000..37cb13c --- /dev/null +++ b/lib/parser/ast/nodes/exprs/IdentRef.cpp @@ -0,0 +1,21 @@ +#include "lib/parser/ast/nodes/exprs/IdentRef.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void IdentRef::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const std::string& IdentRef::Name() const noexcept { + return name_; +} + +void IdentRef::SetName(std::string new_name) { + name_ = std::move(new_name); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/exprs/IdentRef.hpp b/lib/parser/ast/nodes/exprs/IdentRef.hpp new file mode 100644 index 0000000..288aeb6 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/IdentRef.hpp @@ -0,0 +1,23 @@ +#ifndef PARSER_IDENTREF_HPP_ +#define PARSER_IDENTREF_HPP_ + +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" + +namespace ovum::compiler::parser { + +class IdentRef : public Expr { +public: + void Accept(AstVisitor& visitor) override; + + const std::string& Name() const noexcept; + void SetName(std::string new_name); + +private: + std::string name_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_IDENTREF_HPP_ diff --git a/lib/parser/ast/nodes/exprs/IndexAccess.cpp b/lib/parser/ast/nodes/exprs/IndexAccess.cpp new file mode 100644 index 0000000..2f59348 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/IndexAccess.cpp @@ -0,0 +1,49 @@ +#include "lib/parser/ast/nodes/exprs/IndexAccess.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void IndexAccess::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const Expr& IndexAccess::Object() const noexcept { + return *object_; +} + +Expr& IndexAccess::MutableObject() noexcept { + return *object_; +} + +void IndexAccess::SetObject(std::unique_ptr new_object) { + object_ = std::move(new_object); +} + +std::unique_ptr IndexAccess::ReplaceObject(std::unique_ptr new_object) { + auto old_object = std::move(object_); + object_ = std::move(new_object); + return old_object; +} + +const Expr& IndexAccess::IndexExpr() const noexcept { + return *index_; +} + +Expr& IndexAccess::MutableIndexExpr() noexcept { + return *index_; +} + +void IndexAccess::SetIndexExpr(std::unique_ptr new_index) { + index_ = std::move(new_index); +} + +std::unique_ptr IndexAccess::ReplaceIndexExpr(std::unique_ptr new_index) { + auto old_index = std::move(index_); + index_ = std::move(new_index); + return old_index; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/exprs/IndexAccess.hpp b/lib/parser/ast/nodes/exprs/IndexAccess.hpp new file mode 100644 index 0000000..694c789 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/IndexAccess.hpp @@ -0,0 +1,31 @@ +#ifndef PARSER_INDEXACCESS_HPP_ +#define PARSER_INDEXACCESS_HPP_ + +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" + +namespace ovum::compiler::parser { + +class IndexAccess : public Expr { +public: + void Accept(AstVisitor& visitor) override; + + const Expr& Object() const noexcept; + Expr& MutableObject() noexcept; + void SetObject(std::unique_ptr new_object); + std::unique_ptr ReplaceObject(std::unique_ptr new_object); + + const Expr& IndexExpr() const noexcept; + Expr& MutableIndexExpr() noexcept; + void SetIndexExpr(std::unique_ptr new_index); + std::unique_ptr ReplaceIndexExpr(std::unique_ptr new_index); + +private: + std::unique_ptr object_; + std::unique_ptr index_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_INDEXACCESS_HPP_ diff --git a/lib/parser/ast/nodes/exprs/NamespaceRef.cpp b/lib/parser/ast/nodes/exprs/NamespaceRef.cpp new file mode 100644 index 0000000..7443147 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/NamespaceRef.cpp @@ -0,0 +1,39 @@ +#include "lib/parser/ast/nodes/exprs/NamespaceRef.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void NamespaceRef::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const Expr& NamespaceRef::NamespaceExpr() const noexcept { + return *namespace_; +} + +Expr& NamespaceRef::MutableNamespaceExpr() noexcept { + return *namespace_; +} + +void NamespaceRef::SetNamespaceExpr(std::unique_ptr new_namespace_expr) { + namespace_ = std::move(new_namespace_expr); +} + +std::unique_ptr NamespaceRef::ReplaceNamespaceExpr(std::unique_ptr new_namespace_expr) { + auto old_ns = std::move(namespace_); + namespace_ = std::move(new_namespace_expr); + return old_ns; +} + +const std::string& NamespaceRef::Name() const noexcept { + return name_; +} + +void NamespaceRef::SetName(std::string new_name) { + name_ = std::move(new_name); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/exprs/NamespaceRef.hpp b/lib/parser/ast/nodes/exprs/NamespaceRef.hpp new file mode 100644 index 0000000..0f0eb72 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/NamespaceRef.hpp @@ -0,0 +1,30 @@ +#ifndef PARSER_NAMESPACEREF_HPP_ +#define PARSER_NAMESPACEREF_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" + +namespace ovum::compiler::parser { + +class NamespaceRef : public Expr { +public: + void Accept(AstVisitor& visitor) override; + + const Expr& NamespaceExpr() const noexcept; + Expr& MutableNamespaceExpr() noexcept; + void SetNamespaceExpr(std::unique_ptr new_namespace_expr); + std::unique_ptr ReplaceNamespaceExpr(std::unique_ptr new_namespace_expr); + + const std::string& Name() const noexcept; + void SetName(std::string new_name); + +private: + std::unique_ptr namespace_; + std::string name_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_NAMESPACEREF_HPP_ diff --git a/lib/parser/ast/nodes/exprs/SafeCall.cpp b/lib/parser/ast/nodes/exprs/SafeCall.cpp new file mode 100644 index 0000000..a0a158a --- /dev/null +++ b/lib/parser/ast/nodes/exprs/SafeCall.cpp @@ -0,0 +1,67 @@ +#include "lib/parser/ast/nodes/exprs/SafeCall.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void SafeCall::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const Expr& SafeCall::Object() const noexcept { + return *object_; +} + +Expr& SafeCall::MutableObject() noexcept { + return *object_; +} + +void SafeCall::SetObject(std::unique_ptr object_expr) { + object_ = std::move(object_expr); +} + +std::unique_ptr SafeCall::ReplaceObject(std::unique_ptr new_object) { + auto old_object = std::move(object_); + object_ = std::move(new_object); + return old_object; +} + +const std::string& SafeCall::Method() const noexcept { + return method_; +} + +void SafeCall::SetMethod(std::string method_name) { + method_ = std::move(method_name); +} + +const std::vector>& SafeCall::Args() const noexcept { + return args_; +} + +std::vector>& SafeCall::MutableArgs() noexcept { + return args_; +} + +void SafeCall::AddArg(std::unique_ptr argument_expr) { + args_.emplace_back(std::move(argument_expr)); +} + +void SafeCall::ClearArgs() { + args_.clear(); +} + +bool SafeCall::IsNullPropagating() const noexcept { + return true; +} + +void SafeCall::SetInferredType(TypeReference inferred) { + inferred_type_ = std::move(inferred); +} + +const std::optional& SafeCall::InferredType() const noexcept { + return inferred_type_; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/exprs/SafeCall.hpp b/lib/parser/ast/nodes/exprs/SafeCall.hpp new file mode 100644 index 0000000..1052a7d --- /dev/null +++ b/lib/parser/ast/nodes/exprs/SafeCall.hpp @@ -0,0 +1,47 @@ +#ifndef PARSER_SAFECALL_HPP_ +#define PARSER_SAFECALL_HPP_ + +#include +#include +#include +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class AstVisitor; + +class SafeCall : public Expr { +public: + void Accept(AstVisitor& v) override; + + const Expr& Object() const noexcept; + Expr& MutableObject() noexcept; + void SetObject(std::unique_ptr object_expr); + std::unique_ptr ReplaceObject(std::unique_ptr new_object); + + const std::string& Method() const noexcept; + void SetMethod(std::string method_name); + + const std::vector>& Args() const noexcept; + std::vector>& MutableArgs() noexcept; + void AddArg(std::unique_ptr argument_expr); + void ClearArgs(); + + bool IsNullPropagating() const noexcept; + + void SetInferredType(TypeReference inferred); + const std::optional& InferredType() const noexcept; + +private: + std::unique_ptr object_; + std::string method_; + std::vector> args_; + std::optional inferred_type_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_SAFECALL_HPP_ diff --git a/lib/parser/ast/nodes/exprs/TypeTestIs.cpp b/lib/parser/ast/nodes/exprs/TypeTestIs.cpp new file mode 100644 index 0000000..9b360d8 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/TypeTestIs.cpp @@ -0,0 +1,43 @@ +#include "lib/parser/ast/nodes/exprs/TypeTestIs.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void TypeTestIs::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const Expr& TypeTestIs::Expression() const noexcept { + return *expr; +} + +Expr& TypeTestIs::MutableExpression() noexcept { + return *expr; +} + +void TypeTestIs::SetExpression(std::unique_ptr new_expression) { + expr = std::move(new_expression); +} + +std::unique_ptr TypeTestIs::ReplaceExpression(std::unique_ptr new_expression) { + auto old_expr = std::move(expr); + expr = std::move(new_expression); + return old_expr; +} + +const TypeReference& TypeTestIs::Type() const noexcept { + return type; +} + +TypeReference& TypeTestIs::MutableType() noexcept { + return type; +} + +void TypeTestIs::SetType(TypeReference new_type) { + type = std::move(new_type); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/exprs/TypeTestIs.hpp b/lib/parser/ast/nodes/exprs/TypeTestIs.hpp new file mode 100644 index 0000000..4cff1ce --- /dev/null +++ b/lib/parser/ast/nodes/exprs/TypeTestIs.hpp @@ -0,0 +1,31 @@ +#ifndef PARSER_TYPETESTIS_HPP_ +#define PARSER_TYPETESTIS_HPP_ + +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class TypeTestIs : public Expr { +public: + void Accept(AstVisitor& visitor) override; + + const Expr& Expression() const noexcept; + Expr& MutableExpression() noexcept; + void SetExpression(std::unique_ptr new_expression); + std::unique_ptr ReplaceExpression(std::unique_ptr new_expression); + + const TypeReference& Type() const noexcept; + TypeReference& MutableType() noexcept; + void SetType(TypeReference new_type); + +private: + std::unique_ptr expr; + TypeReference type; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_TYPETESTIS_HPP_ diff --git a/lib/parser/ast/nodes/exprs/Unary.cpp b/lib/parser/ast/nodes/exprs/Unary.cpp new file mode 100644 index 0000000..e7f5edd --- /dev/null +++ b/lib/parser/ast/nodes/exprs/Unary.cpp @@ -0,0 +1,39 @@ +#include "lib/parser/ast/nodes/exprs/Unary.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void Unary::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const IUnaryOpTag& Unary::Op() const noexcept { + return *op_; +} + +void Unary::SetOp(const IUnaryOpTag& new_op) noexcept { + op_ = &new_op; +} + +const Expr& Unary::Operand() const noexcept { + return *operand_; +} + +Expr& Unary::MutableOperand() noexcept { + return *operand_; +} + +void Unary::SetOperand(std::unique_ptr new_operand) { + operand_ = std::move(new_operand); +} + +std::unique_ptr Unary::ReplaceOperand(std::unique_ptr new_operand) { + auto old_operand = std::move(operand_); + operand_ = std::move(new_operand); + return old_operand; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/exprs/Unary.hpp b/lib/parser/ast/nodes/exprs/Unary.hpp new file mode 100644 index 0000000..60319cc --- /dev/null +++ b/lib/parser/ast/nodes/exprs/Unary.hpp @@ -0,0 +1,31 @@ +#ifndef PARSER_UNARY_HPP_ +#define PARSER_UNARY_HPP_ + +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "tags/IUnaryOpTag.hpp" +#include "tags/OpTags.hpp" + +namespace ovum::compiler::parser { + +class Unary : public Expr { +public: + void Accept(AstVisitor& visitor) override; + + const IUnaryOpTag& Op() const noexcept; + void SetOp(const IUnaryOpTag& new_op) noexcept; + + const Expr& Operand() const noexcept; + Expr& MutableOperand() noexcept; + void SetOperand(std::unique_ptr new_operand); + std::unique_ptr ReplaceOperand(std::unique_ptr new_operand); + +private: + const IUnaryOpTag* op_ = &OpTags::Neg(); + std::unique_ptr operand_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_UNARY_HPP_ diff --git a/lib/parser/ast/nodes/exprs/literals/BoolLit.cpp b/lib/parser/ast/nodes/exprs/literals/BoolLit.cpp new file mode 100644 index 0000000..51a7488 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/literals/BoolLit.cpp @@ -0,0 +1,19 @@ +#include "lib/parser/ast/nodes/exprs/literals/BoolLit.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +namespace ovum::compiler::parser { + +void BoolLit::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +bool BoolLit::Value() const noexcept { + return value; +} + +void BoolLit::SetValue(bool new_value) noexcept { + value = new_value; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/exprs/literals/BoolLit.hpp b/lib/parser/ast/nodes/exprs/literals/BoolLit.hpp new file mode 100644 index 0000000..399b701 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/literals/BoolLit.hpp @@ -0,0 +1,21 @@ +#ifndef PARSER_BOOLLIT_HPP_ +#define PARSER_BOOLLIT_HPP_ + +#include "lib/parser/ast/nodes/base/Expr.hpp" + +namespace ovum::compiler::parser { + +class BoolLit : public Expr { +public: + void Accept(AstVisitor& visitor) override; + + bool Value() const noexcept; + void SetValue(bool new_value) noexcept; + +private: + bool value = false; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_BOOLLIT_HPP_ diff --git a/lib/parser/ast/nodes/exprs/literals/CharLit.cpp b/lib/parser/ast/nodes/exprs/literals/CharLit.cpp new file mode 100644 index 0000000..4c45367 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/literals/CharLit.cpp @@ -0,0 +1,19 @@ +#include "lib/parser/ast/nodes/exprs/literals/CharLit.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +namespace ovum::compiler::parser { + +void CharLit::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +char CharLit::Value() const noexcept { + return value_; +} + +void CharLit::SetValue(char new_value) noexcept { + value_ = new_value; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/exprs/literals/CharLit.hpp b/lib/parser/ast/nodes/exprs/literals/CharLit.hpp new file mode 100644 index 0000000..6393dd8 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/literals/CharLit.hpp @@ -0,0 +1,21 @@ +#ifndef PARSER_CHARLIT_HPP_ +#define PARSER_CHARLIT_HPP_ + +#include "lib/parser/ast/nodes/base/Expr.hpp" + +namespace ovum::compiler::parser { + +class CharLit : public Expr { +public: + void Accept(AstVisitor& visitor) override; + + char Value() const noexcept; + void SetValue(char new_value) noexcept; + +private: + char value_ = '\0'; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_CHARLIT_HPP_ diff --git a/lib/parser/ast/nodes/exprs/literals/FloatLit.cpp b/lib/parser/ast/nodes/exprs/literals/FloatLit.cpp new file mode 100644 index 0000000..99631d6 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/literals/FloatLit.cpp @@ -0,0 +1,19 @@ +#include "lib/parser/ast/nodes/exprs/literals/FloatLit.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +namespace ovum::compiler::parser { + +void FloatLit::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +double FloatLit::Value() const noexcept { + return value; +} + +void FloatLit::SetValue(double new_value) noexcept { + value = new_value; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/exprs/literals/FloatLit.hpp b/lib/parser/ast/nodes/exprs/literals/FloatLit.hpp new file mode 100644 index 0000000..4dba68a --- /dev/null +++ b/lib/parser/ast/nodes/exprs/literals/FloatLit.hpp @@ -0,0 +1,21 @@ +#ifndef PARSER_FLOATLIT_HPP_ +#define PARSER_FLOATLIT_HPP_ + +#include "lib/parser/ast/nodes/base/Expr.hpp" + +namespace ovum::compiler::parser { + +class FloatLit : public Expr { +public: + void Accept(AstVisitor& visitor) override; + + double Value() const noexcept; + void SetValue(double new_value) noexcept; + +private: + double value = 0.0; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_FLOATLIT_HPP_ diff --git a/lib/parser/ast/nodes/exprs/literals/IntLit.cpp b/lib/parser/ast/nodes/exprs/literals/IntLit.cpp new file mode 100644 index 0000000..8c9d681 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/literals/IntLit.cpp @@ -0,0 +1,19 @@ +#include "lib/parser/ast/nodes/exprs/literals/IntLit.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +namespace ovum::compiler::parser { + +void IntLit::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +int64_t IntLit::Value() const noexcept { + return value; +} + +void IntLit::SetValue(int64_t new_value) noexcept { + value = new_value; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/exprs/literals/IntLit.hpp b/lib/parser/ast/nodes/exprs/literals/IntLit.hpp new file mode 100644 index 0000000..3ec55e5 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/literals/IntLit.hpp @@ -0,0 +1,22 @@ +#ifndef PARSER_INTLIT_HPP_ +#define PARSER_INTLIT_HPP_ + +#include +#include "lib/parser/ast/nodes/base/Expr.hpp" + +namespace ovum::compiler::parser { + +class IntLit : public Expr { +public: + void Accept(AstVisitor& visitor) override; + + int64_t Value() const noexcept; + void SetValue(int64_t new_value) noexcept; + +private: + int64_t value = 0; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_INTLIT_HPP_ diff --git a/lib/parser/ast/nodes/exprs/literals/NullLit.cpp b/lib/parser/ast/nodes/exprs/literals/NullLit.cpp new file mode 100644 index 0000000..1af96fd --- /dev/null +++ b/lib/parser/ast/nodes/exprs/literals/NullLit.cpp @@ -0,0 +1,11 @@ +#include "lib/parser/ast/nodes/exprs/literals/NullLit.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +namespace ovum::compiler::parser { + +void NullLit::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/exprs/literals/NullLit.hpp b/lib/parser/ast/nodes/exprs/literals/NullLit.hpp new file mode 100644 index 0000000..21ac8a4 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/literals/NullLit.hpp @@ -0,0 +1,15 @@ +#ifndef PARSER_NULLLIT_HPP_ +#define PARSER_NULLLIT_HPP_ + +#include "lib/parser/ast/nodes/base/Expr.hpp" + +namespace ovum::compiler::parser { + +class NullLit : public Expr { +public: + void Accept(AstVisitor& visitor) override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_NULLLIT_HPP_ diff --git a/lib/parser/ast/nodes/exprs/literals/StringLit.cpp b/lib/parser/ast/nodes/exprs/literals/StringLit.cpp new file mode 100644 index 0000000..db667f2 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/literals/StringLit.cpp @@ -0,0 +1,21 @@ +#include "lib/parser/ast/nodes/exprs/literals/StringLit.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void StringLit::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const std::string& StringLit::Value() const noexcept { + return value_; +} + +void StringLit::SetValue(std::string new_value) { + value_ = std::move(new_value); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/exprs/literals/StringLit.hpp b/lib/parser/ast/nodes/exprs/literals/StringLit.hpp new file mode 100644 index 0000000..e6a1ed5 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/literals/StringLit.hpp @@ -0,0 +1,23 @@ +#ifndef PARSER_STRINGLIT_HPP_ +#define PARSER_STRINGLIT_HPP_ + +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" + +namespace ovum::compiler::parser { + +class StringLit : public Expr { +public: + void Accept(AstVisitor& visitor) override; + + const std::string& Value() const noexcept; + void SetValue(std::string new_value); + +private: + std::string value_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STRINGLIT_HPP_ diff --git a/lib/parser/ast/nodes/exprs/tags/IAssignOpTag.hpp b/lib/parser/ast/nodes/exprs/tags/IAssignOpTag.hpp new file mode 100644 index 0000000..a7f6116 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/tags/IAssignOpTag.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_IASSIGNOPTAG_HPP_ +#define PARSER_IASSIGNOPTAG_HPP_ + +#include + +namespace ovum::compiler::parser { + +class IAssignOpTag { +public: + virtual ~IAssignOpTag() = default; + + virtual std::string_view Name() const = 0; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_IASSIGNOPTAG_HPP_ diff --git a/lib/parser/ast/nodes/exprs/tags/IBinaryOpTag.hpp b/lib/parser/ast/nodes/exprs/tags/IBinaryOpTag.hpp new file mode 100644 index 0000000..0032c50 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/tags/IBinaryOpTag.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_IBINARYOPTAG_HPP_ +#define PARSER_IBINARYOPTAG_HPP_ + +#include + +namespace ovum::compiler::parser { + +class IBinaryOpTag { +public: + virtual ~IBinaryOpTag() = default; + + virtual std::string_view Name() const = 0; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_IBINARYOPTAG_HPP_ diff --git a/lib/parser/ast/nodes/exprs/tags/IUnaryOpTag.hpp b/lib/parser/ast/nodes/exprs/tags/IUnaryOpTag.hpp new file mode 100644 index 0000000..d5d42ee --- /dev/null +++ b/lib/parser/ast/nodes/exprs/tags/IUnaryOpTag.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_IUNARYOPTAG_HPP_ +#define PARSER_IUNARYOPTAG_HPP_ + +#include + +namespace ovum::compiler::parser { + +class IUnaryOpTag { +public: + virtual ~IUnaryOpTag() = default; + + virtual std::string_view Name() const = 0; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_IUNARYOPTAG_HPP_ diff --git a/lib/parser/ast/nodes/exprs/tags/OpTags.cpp b/lib/parser/ast/nodes/exprs/tags/OpTags.cpp new file mode 100644 index 0000000..53c8c87 --- /dev/null +++ b/lib/parser/ast/nodes/exprs/tags/OpTags.cpp @@ -0,0 +1,224 @@ +#include "OpTags.hpp" + +#include + +namespace ovum::compiler::parser { + +namespace { + +struct BinaryAdd : IBinaryOpTag { + [[nodiscard]] std::string_view Name() const override { + return "add"; + } +}; + +struct BinarySub : IBinaryOpTag { + [[nodiscard]] std::string_view Name() const override { + return "sub"; + } +}; + +struct BinaryMul : IBinaryOpTag { + [[nodiscard]] std::string_view Name() const override { + return "mul"; + } +}; + +struct BinaryDiv : IBinaryOpTag { + [[nodiscard]] std::string_view Name() const override { + return "div"; + } +}; + +struct BinaryMod : IBinaryOpTag { + [[nodiscard]] std::string_view Name() const override { + return "mod"; + } +}; + +struct BinaryLt : IBinaryOpTag { + [[nodiscard]] std::string_view Name() const override { + return "lt"; + } +}; + +struct BinaryLe : IBinaryOpTag { + [[nodiscard]] std::string_view Name() const override { + return "le"; + } +}; + +struct BinaryGt : IBinaryOpTag { + [[nodiscard]] std::string_view Name() const override { + return "gt"; + } +}; + +struct BinaryGe : IBinaryOpTag { + [[nodiscard]] std::string_view Name() const override { + return "ge"; + } +}; + +struct BinaryEq : IBinaryOpTag { + [[nodiscard]] std::string_view Name() const override { + return "eq"; + } +}; + +struct BinaryNe : IBinaryOpTag { + [[nodiscard]] std::string_view Name() const override { + return "ne"; + } +}; + +struct BinaryAnd : IBinaryOpTag { + [[nodiscard]] std::string_view Name() const override { + return "and"; + } +}; + +struct BinaryOr : IBinaryOpTag { + [[nodiscard]] std::string_view Name() const override { + return "or"; + } +}; + +struct BinaryXor : IBinaryOpTag { + [[nodiscard]] std::string_view Name() const override { + return "xor"; + } +}; + +struct UnaryNeg : IUnaryOpTag { + [[nodiscard]] std::string_view Name() const override { + return "neg"; + } +}; + +struct UnaryPlus : IUnaryOpTag { + [[nodiscard]] std::string_view Name() const override { + return "plus"; + } +}; + +struct UnaryNot : IUnaryOpTag { + [[nodiscard]] std::string_view Name() const override { + return "not"; + } +}; + +struct AssignRef : IAssignOpTag { + [[nodiscard]] std::string_view Name() const override { + return "="; + } +}; + +struct AssignCopy : IAssignOpTag { + [[nodiscard]] std::string_view Name() const override { + return ":="; + } +}; + +} // namespace + +namespace OpTags { + +const IBinaryOpTag& Add() { + static BinaryAdd t; + return t; +} + +const IBinaryOpTag& Sub() { + static BinarySub t; + return t; +} + +const IBinaryOpTag& Mul() { + static BinaryMul t; + return t; +} + +const IBinaryOpTag& Div() { + static BinaryDiv t; + return t; +} + +const IBinaryOpTag& Mod() { + static BinaryMod t; + return t; +} + +const IBinaryOpTag& Lt() { + static BinaryLt t; + return t; +} + +const IBinaryOpTag& Le() { + static BinaryLe t; + return t; +} + +const IBinaryOpTag& Gt() { + static BinaryGt t; + return t; +} + +const IBinaryOpTag& Ge() { + static BinaryGe t; + return t; +} + +const IBinaryOpTag& Eq() { + static BinaryEq t; + return t; +} + +const IBinaryOpTag& Ne() { + static BinaryNe t; + return t; +} + +const IBinaryOpTag& And() { + static BinaryAnd t; + return t; +} + +const IBinaryOpTag& Or() { + static BinaryOr t; + return t; +} + +const IBinaryOpTag& Xor() { + static BinaryXor t; + return t; +} + +const IUnaryOpTag& Neg() { + static UnaryNeg t; + return t; +} + +const IUnaryOpTag& Plus() { + static UnaryPlus t; + return t; +} + +const IUnaryOpTag& Not() { + static UnaryNot t; + return t; +} + +const IAssignOpTag& RefAssign() { + static AssignRef t; + return t; +} + +const IAssignOpTag& CopyAssign() { + static AssignCopy t; + return t; +} + +} // namespace OpTags + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/exprs/tags/OpTags.hpp b/lib/parser/ast/nodes/exprs/tags/OpTags.hpp new file mode 100644 index 0000000..7879e2e --- /dev/null +++ b/lib/parser/ast/nodes/exprs/tags/OpTags.hpp @@ -0,0 +1,39 @@ +#ifndef PARSER_OPTAGS_HPP_ +#define PARSER_OPTAGS_HPP_ + +#include "IAssignOpTag.hpp" +#include "IBinaryOpTag.hpp" +#include "IUnaryOpTag.hpp" + +namespace ovum::compiler::parser { + +namespace OpTags { +// binary +const IBinaryOpTag& Add(); +const IBinaryOpTag& Sub(); +const IBinaryOpTag& Mul(); +const IBinaryOpTag& Div(); +const IBinaryOpTag& Mod(); +const IBinaryOpTag& Lt(); +const IBinaryOpTag& Le(); +const IBinaryOpTag& Gt(); +const IBinaryOpTag& Ge(); +const IBinaryOpTag& Eq(); +const IBinaryOpTag& Ne(); +const IBinaryOpTag& And(); +const IBinaryOpTag& Or(); +const IBinaryOpTag& Xor(); + +// unary +const IUnaryOpTag& Neg(); +const IUnaryOpTag& Plus(); +const IUnaryOpTag& Not(); + +// assign +const IAssignOpTag& RefAssign(); // = +const IAssignOpTag& CopyAssign(); // := +} // namespace OpTags + +} // namespace ovum::compiler::parser + +#endif // PARSER_OPTAGS_HPP_ diff --git a/lib/parser/ast/nodes/stmts/Block.cpp b/lib/parser/ast/nodes/stmts/Block.cpp new file mode 100644 index 0000000..e572b0a --- /dev/null +++ b/lib/parser/ast/nodes/stmts/Block.cpp @@ -0,0 +1,51 @@ +#include "lib/parser/ast/nodes/stmts/Block.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void Block::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +std::vector>& Block::GetStatements() { + return stmts_; +} + +const std::vector>& Block::GetStatements() const { + return stmts_; +} + +void Block::Append(std::unique_ptr statement) { + stmts_.emplace_back(std::move(statement)); +} + +void Block::Insert(std::size_t index, std::unique_ptr statement) { + if (index >= stmts_.size()) { + stmts_.emplace_back(std::move(statement)); + } else { + stmts_.insert(stmts_.begin() + static_cast(index), std::move(statement)); + } +} + +std::unique_ptr Block::ReleaseAt(std::size_t index) { + if (index >= stmts_.size()) { + return nullptr; + } + + auto old_stmt = std::move(stmts_[index]); + stmts_.erase(stmts_.begin() + static_cast(index)); + return old_stmt; +} + +void Block::Clear() noexcept { + stmts_.clear(); +} + +std::size_t Block::Size() const noexcept { + return stmts_.size(); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/stmts/Block.hpp b/lib/parser/ast/nodes/stmts/Block.hpp new file mode 100644 index 0000000..046ba3e --- /dev/null +++ b/lib/parser/ast/nodes/stmts/Block.hpp @@ -0,0 +1,32 @@ +#ifndef PARSER_BLOCK_HPP_ +#define PARSER_BLOCK_HPP_ + +#include +#include +#include + +#include "lib/parser/ast/nodes/base/AstNode.hpp" +#include "lib/parser/ast/nodes/base/Stmt.hpp" + +namespace ovum::compiler::parser { + +class Block : public Stmt { +public: + void Accept(AstVisitor& visitor) override; + + std::vector>& GetStatements(); + const std::vector>& GetStatements() const; + void Append(std::unique_ptr statement); + + void Insert(std::size_t index, std::unique_ptr statement); + std::unique_ptr ReleaseAt(std::size_t index); + void Clear() noexcept; + std::size_t Size() const noexcept; + +private: + std::vector> stmts_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_BLOCK_HPP_ diff --git a/lib/parser/ast/nodes/stmts/Branch.cpp b/lib/parser/ast/nodes/stmts/Branch.cpp new file mode 100644 index 0000000..8dbdecf --- /dev/null +++ b/lib/parser/ast/nodes/stmts/Branch.cpp @@ -0,0 +1,43 @@ +#include "lib/parser/ast/nodes/stmts/Branch.hpp" + +#include + +namespace ovum::compiler::parser { + +Branch::Branch(std::unique_ptr condition, std::unique_ptr then_block) : + condition_(std::move(condition)), then_block_(std::move(then_block)) { +} + +const Expr* Branch::Condition() const noexcept { + return condition_.get(); +} + +Expr* Branch::MutableCondition() noexcept { + return condition_.get(); +} + +void Branch::SetCondition(std::unique_ptr expression) { + condition_ = std::move(expression); +} + +std::unique_ptr Branch::ReleaseCondition() { + return std::move(condition_); +} + +const Block* Branch::Then() const noexcept { + return then_block_.get(); +} + +Block* Branch::MutableThen() noexcept { + return then_block_.get(); +} + +void Branch::SetThen(std::unique_ptr then_body) { + then_block_ = std::move(then_body); +} + +std::unique_ptr Branch::ReleaseThen() { + return std::move(then_block_); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/stmts/Branch.hpp b/lib/parser/ast/nodes/stmts/Branch.hpp new file mode 100644 index 0000000..cc679ff --- /dev/null +++ b/lib/parser/ast/nodes/stmts/Branch.hpp @@ -0,0 +1,37 @@ +#ifndef PARSER_BRANCH_HPP_ +#define PARSER_BRANCH_HPP_ + +#include + +#include "Block.hpp" +#include "lib/parser/ast/nodes/base/Expr.hpp" + +namespace ovum::compiler::parser { + +class Branch { +public: + Branch(std::unique_ptr condition, std::unique_ptr then_block); + Branch(const Branch&) = delete; + Branch& operator=(const Branch&) = delete; + Branch(Branch&&) noexcept = default; + Branch& operator=(Branch&&) noexcept = default; + ~Branch() = default; + + const Expr* Condition() const noexcept; + Expr* MutableCondition() noexcept; + void SetCondition(std::unique_ptr expression); + std::unique_ptr ReleaseCondition(); + + const Block* Then() const noexcept; + Block* MutableThen() noexcept; + void SetThen(std::unique_ptr then_body); + std::unique_ptr ReleaseThen(); + +private: + std::unique_ptr condition_; + std::unique_ptr then_block_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_BRANCH_HPP_ diff --git a/lib/parser/ast/nodes/stmts/BreakStmt.cpp b/lib/parser/ast/nodes/stmts/BreakStmt.cpp new file mode 100644 index 0000000..1685231 --- /dev/null +++ b/lib/parser/ast/nodes/stmts/BreakStmt.cpp @@ -0,0 +1,11 @@ +#include "lib/parser/ast/nodes/stmts/BreakStmt.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +namespace ovum::compiler::parser { + +void BreakStmt::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/stmts/BreakStmt.hpp b/lib/parser/ast/nodes/stmts/BreakStmt.hpp new file mode 100644 index 0000000..bf3fbf5 --- /dev/null +++ b/lib/parser/ast/nodes/stmts/BreakStmt.hpp @@ -0,0 +1,15 @@ +#ifndef PARSER_BREAKSTMT_HPP_ +#define PARSER_BREAKSTMT_HPP_ + +#include "lib/parser/ast/nodes/base/Stmt.hpp" + +namespace ovum::compiler::parser { + +class BreakStmt : public Stmt { +public: + void Accept(AstVisitor& visitor) override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_BREAKSTMT_HPP_ diff --git a/lib/parser/ast/nodes/stmts/ContinueStmt.cpp b/lib/parser/ast/nodes/stmts/ContinueStmt.cpp new file mode 100644 index 0000000..aa19fc7 --- /dev/null +++ b/lib/parser/ast/nodes/stmts/ContinueStmt.cpp @@ -0,0 +1,11 @@ +#include "lib/parser/ast/nodes/stmts/ContinueStmt.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +namespace ovum::compiler::parser { + +void ContinueStmt::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/stmts/ContinueStmt.hpp b/lib/parser/ast/nodes/stmts/ContinueStmt.hpp new file mode 100644 index 0000000..11ce141 --- /dev/null +++ b/lib/parser/ast/nodes/stmts/ContinueStmt.hpp @@ -0,0 +1,15 @@ +#ifndef PARSER_CONTINUESTMT_HPP_ +#define PARSER_CONTINUESTMT_HPP_ + +#include "lib/parser/ast/nodes/base/Stmt.hpp" + +namespace ovum::compiler::parser { + +class ContinueStmt : public Stmt { +public: + void Accept(AstVisitor& visitor) override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_CONTINUESTMT_HPP_ diff --git a/lib/parser/ast/nodes/stmts/ExprStmt.cpp b/lib/parser/ast/nodes/stmts/ExprStmt.cpp new file mode 100644 index 0000000..80424ef --- /dev/null +++ b/lib/parser/ast/nodes/stmts/ExprStmt.cpp @@ -0,0 +1,29 @@ +#include "lib/parser/ast/nodes/stmts/ExprStmt.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void ExprStmt::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const Expr* ExprStmt::Expression() const noexcept { + return expr_.get(); +} + +Expr* ExprStmt::MutableExpression() noexcept { + return expr_.get(); +} + +void ExprStmt::SetExpression(std::unique_ptr expression) { + expr_ = std::move(expression); +} + +std::unique_ptr ExprStmt::ReleaseExpression() { + return std::move(expr_); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/stmts/ExprStmt.hpp b/lib/parser/ast/nodes/stmts/ExprStmt.hpp new file mode 100644 index 0000000..420b611 --- /dev/null +++ b/lib/parser/ast/nodes/stmts/ExprStmt.hpp @@ -0,0 +1,26 @@ +#ifndef PARSER_EXPRSTMT_HPP_ +#define PARSER_EXPRSTMT_HPP_ + +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/ast/nodes/base/Stmt.hpp" + +namespace ovum::compiler::parser { + +class ExprStmt : public Stmt { +public: + void Accept(AstVisitor& visitor) override; + + const Expr* Expression() const noexcept; + Expr* MutableExpression() noexcept; + void SetExpression(std::unique_ptr expression); + std::unique_ptr ReleaseExpression(); + +private: + std::unique_ptr expr_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_EXPRSTMT_HPP_ diff --git a/lib/parser/ast/nodes/stmts/ForStmt.cpp b/lib/parser/ast/nodes/stmts/ForStmt.cpp new file mode 100644 index 0000000..3767dbc --- /dev/null +++ b/lib/parser/ast/nodes/stmts/ForStmt.cpp @@ -0,0 +1,53 @@ +#include "lib/parser/ast/nodes/stmts/ForStmt.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void ForStmt::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const std::string& ForStmt::IteratorName() const noexcept { + return iter_name_; +} + +void ForStmt::SetIteratorName(std::string new_name) { + iter_name_ = std::move(new_name); +} + +const Expr* ForStmt::IteratorExpr() const noexcept { + return iter_expr_.get(); +} + +Expr* ForStmt::MutableIteratorExpr() noexcept { + return iter_expr_.get(); +} + +void ForStmt::SetIteratorExpr(std::unique_ptr expression) { + iter_expr_ = std::move(expression); +} + +std::unique_ptr ForStmt::ReleaseIteratorExpr() { + return std::move(iter_expr_); +} + +const Block* ForStmt::Body() const noexcept { + return body_.get(); +} + +Block* ForStmt::MutableBody() noexcept { + return body_.get(); +} + +void ForStmt::SetBody(std::unique_ptr body_block) { + body_ = std::move(body_block); +} + +std::unique_ptr ForStmt::ReleaseBody() { + return std::move(body_); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/stmts/ForStmt.hpp b/lib/parser/ast/nodes/stmts/ForStmt.hpp new file mode 100644 index 0000000..954f0e4 --- /dev/null +++ b/lib/parser/ast/nodes/stmts/ForStmt.hpp @@ -0,0 +1,37 @@ +#ifndef PARSER_FORSTMT_HPP_ +#define PARSER_FORSTMT_HPP_ + +#include +#include + +#include "Block.hpp" +#include "lib/parser/ast/nodes/base/Expr.hpp" + +namespace ovum::compiler::parser { + +class ForStmt : public Stmt { +public: + void Accept(AstVisitor& visitor) override; + + const std::string& IteratorName() const noexcept; + void SetIteratorName(std::string new_name); + + const Expr* IteratorExpr() const noexcept; + Expr* MutableIteratorExpr() noexcept; + void SetIteratorExpr(std::unique_ptr expression); + std::unique_ptr ReleaseIteratorExpr(); + + const Block* Body() const noexcept; + Block* MutableBody() noexcept; + void SetBody(std::unique_ptr body_block); + std::unique_ptr ReleaseBody(); + +private: + std::string iter_name_; + std::unique_ptr iter_expr_; + std::unique_ptr body_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_FORSTMT_HPP_ diff --git a/lib/parser/ast/nodes/stmts/IfStmt.cpp b/lib/parser/ast/nodes/stmts/IfStmt.cpp new file mode 100644 index 0000000..dd768dd --- /dev/null +++ b/lib/parser/ast/nodes/stmts/IfStmt.cpp @@ -0,0 +1,53 @@ +#include "lib/parser/ast/nodes/stmts/IfStmt.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void IfStmt::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const std::vector& IfStmt::Branches() const noexcept { + return branches_; +} + +std::vector& IfStmt::MutableBranches() noexcept { + return branches_; +} + +void IfStmt::AddBranch(Branch branch_value) { + branches_.emplace_back(std::move(branch_value)); +} + +void IfStmt::EmplaceBranch(std::unique_ptr cond, std::unique_ptr then_blk) { + branches_.emplace_back(std::move(cond), std::move(then_blk)); +} + +const Block* IfStmt::ElseBlock() const noexcept { + return else_block_.get(); +} + +Block* IfStmt::MutableElseBlock() noexcept { + return else_block_.get(); +} + +void IfStmt::SetElseBlock(std::unique_ptr else_body) { + else_block_ = std::move(else_body); +} + +std::unique_ptr IfStmt::ReleaseElseBlock() { + return std::move(else_block_); +} + +bool IfStmt::HasElse() const noexcept { + return static_cast(else_block_); +} + +bool IfStmt::Empty() const noexcept { + return branches_.empty() && !else_block_; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/stmts/IfStmt.hpp b/lib/parser/ast/nodes/stmts/IfStmt.hpp new file mode 100644 index 0000000..e272bc3 --- /dev/null +++ b/lib/parser/ast/nodes/stmts/IfStmt.hpp @@ -0,0 +1,38 @@ +#ifndef PARSER_IFSTMT_HPP_ +#define PARSER_IFSTMT_HPP_ + +#include +#include + +#include "Block.hpp" +#include "Branch.hpp" +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/ast/nodes/base/Stmt.hpp" + +namespace ovum::compiler::parser { + +class IfStmt : public Stmt { +public: + void Accept(AstVisitor& visitor) override; + + const std::vector& Branches() const noexcept; + std::vector& MutableBranches() noexcept; + void AddBranch(Branch branch_value); + void EmplaceBranch(std::unique_ptr cond, std::unique_ptr then_blk); + + const Block* ElseBlock() const noexcept; + Block* MutableElseBlock() noexcept; + void SetElseBlock(std::unique_ptr else_body); + std::unique_ptr ReleaseElseBlock(); + + bool HasElse() const noexcept; + bool Empty() const noexcept; + +private: + std::vector branches_; + std::unique_ptr else_block_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_IFSTMT_HPP_ diff --git a/lib/parser/ast/nodes/stmts/ReturnStmt.cpp b/lib/parser/ast/nodes/stmts/ReturnStmt.cpp new file mode 100644 index 0000000..0ad75a8 --- /dev/null +++ b/lib/parser/ast/nodes/stmts/ReturnStmt.cpp @@ -0,0 +1,43 @@ +#include "lib/parser/ast/nodes/stmts/ReturnStmt.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void ReturnStmt::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +bool ReturnStmt::HasValue() const noexcept { + return value_.has_value() && static_cast(value_.value()); +} + +const Expr* ReturnStmt::Value() const noexcept { + return value_.has_value() ? value_.value().get() : nullptr; +} + +Expr* ReturnStmt::MutableValue() noexcept { + return value_.has_value() ? value_.value().get() : nullptr; +} + +void ReturnStmt::SetValue(std::unique_ptr new_value) { + value_.emplace(std::move(new_value)); +} + +void ReturnStmt::ResetValue() { + value_.reset(); +} + +std::unique_ptr ReturnStmt::ReleaseValue() { + if (!value_.has_value()) { + return nullptr; + } + + auto out = std::move(value_.value()); + value_.reset(); + return out; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/stmts/ReturnStmt.hpp b/lib/parser/ast/nodes/stmts/ReturnStmt.hpp new file mode 100644 index 0000000..3437e5a --- /dev/null +++ b/lib/parser/ast/nodes/stmts/ReturnStmt.hpp @@ -0,0 +1,29 @@ +#ifndef PARSER_RETURNSTMT_HPP_ +#define PARSER_RETURNSTMT_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/ast/nodes/base/Stmt.hpp" + +namespace ovum::compiler::parser { + +class ReturnStmt : public Stmt { +public: + void Accept(AstVisitor& visitor) override; + + bool HasValue() const noexcept; + const Expr* Value() const noexcept; + Expr* MutableValue() noexcept; + void SetValue(std::unique_ptr new_value); + void ResetValue(); + std::unique_ptr ReleaseValue(); + +private: + std::optional> value_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_RETURNSTMT_HPP_ diff --git a/lib/parser/ast/nodes/stmts/UnsafeBlock.cpp b/lib/parser/ast/nodes/stmts/UnsafeBlock.cpp new file mode 100644 index 0000000..e15365d --- /dev/null +++ b/lib/parser/ast/nodes/stmts/UnsafeBlock.cpp @@ -0,0 +1,29 @@ +#include "lib/parser/ast/nodes/stmts/UnsafeBlock.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void UnsafeBlock::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const Block* UnsafeBlock::Body() const noexcept { + return body_.get(); +} + +Block* UnsafeBlock::MutableBody() noexcept { + return body_.get(); +} + +void UnsafeBlock::SetBody(std::unique_ptr body_block) { + body_ = std::move(body_block); +} + +std::unique_ptr UnsafeBlock::ReleaseBody() { + return std::move(body_); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/stmts/UnsafeBlock.hpp b/lib/parser/ast/nodes/stmts/UnsafeBlock.hpp new file mode 100644 index 0000000..76a8d92 --- /dev/null +++ b/lib/parser/ast/nodes/stmts/UnsafeBlock.hpp @@ -0,0 +1,26 @@ +#ifndef PARSER_UNSAFEBLOCK_HPP_ +#define PARSER_UNSAFEBLOCK_HPP_ + +#include + +#include "Block.hpp" +#include "lib/parser/ast/nodes/base/Stmt.hpp" + +namespace ovum::compiler::parser { + +class UnsafeBlock : public Stmt { +public: + void Accept(AstVisitor& visitor) override; + + const Block* Body() const noexcept; + Block* MutableBody() noexcept; + void SetBody(std::unique_ptr body_block); + std::unique_ptr ReleaseBody(); + +private: + std::unique_ptr body_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_UNSAFEBLOCK_HPP_ diff --git a/lib/parser/ast/nodes/stmts/VarDeclStmt.cpp b/lib/parser/ast/nodes/stmts/VarDeclStmt.cpp new file mode 100644 index 0000000..2d3e9a1 --- /dev/null +++ b/lib/parser/ast/nodes/stmts/VarDeclStmt.cpp @@ -0,0 +1,57 @@ +#include "lib/parser/ast/nodes/stmts/VarDeclStmt.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void VarDeclStmt::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +bool VarDeclStmt::IsVar() const noexcept { + return is_var_; +} + +void VarDeclStmt::SetVar(bool is_var) noexcept { + is_var_ = is_var; +} + +const std::string& VarDeclStmt::Name() const noexcept { + return name_; +} + +void VarDeclStmt::SetName(std::string new_name) { + name_ = std::move(new_name); +} + +const TypeReference& VarDeclStmt::Type() const noexcept { + return type_; +} + +TypeReference& VarDeclStmt::MutableType() noexcept { + return type_; +} + +void VarDeclStmt::SetType(TypeReference new_type) { + type_ = std::move(new_type); +} + +const Expr* VarDeclStmt::Init() const noexcept { + return init_.get(); +} + +Expr* VarDeclStmt::MutableInit() noexcept { + return init_.get(); +} + +void VarDeclStmt::SetInit(std::unique_ptr init_expr) { + init_ = std::move(init_expr); +} + +std::unique_ptr VarDeclStmt::ReleaseInit() { + return std::move(init_); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/stmts/VarDeclStmt.hpp b/lib/parser/ast/nodes/stmts/VarDeclStmt.hpp new file mode 100644 index 0000000..6c86680 --- /dev/null +++ b/lib/parser/ast/nodes/stmts/VarDeclStmt.hpp @@ -0,0 +1,41 @@ +#ifndef PARSER_VARDECLSTMT_HPP_ +#define PARSER_VARDECLSTMT_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/ast/nodes/base/Stmt.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class VarDeclStmt : public Stmt { +public: + void Accept(AstVisitor& visitor) override; + + bool IsVar() const noexcept; + void SetVar(bool is_var) noexcept; + + const std::string& Name() const noexcept; + void SetName(std::string new_name); + + const TypeReference& Type() const noexcept; + TypeReference& MutableType() noexcept; + void SetType(TypeReference new_type); + + const Expr* Init() const noexcept; + Expr* MutableInit() noexcept; + void SetInit(std::unique_ptr init_expr); + std::unique_ptr ReleaseInit(); + +private: + bool is_var_ = false; + std::string name_; + TypeReference type_; + std::unique_ptr init_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_VARDECLSTMT_HPP_ diff --git a/lib/parser/ast/nodes/stmts/WhileStmt.cpp b/lib/parser/ast/nodes/stmts/WhileStmt.cpp new file mode 100644 index 0000000..952b0f8 --- /dev/null +++ b/lib/parser/ast/nodes/stmts/WhileStmt.cpp @@ -0,0 +1,45 @@ +#include "lib/parser/ast/nodes/stmts/WhileStmt.hpp" + +#include "lib/parser/ast/AstVisitor.hpp" + +#include + +namespace ovum::compiler::parser { + +void WhileStmt::Accept(AstVisitor& visitor) { + visitor.Visit(*this); +} + +const Expr* WhileStmt::Condition() const noexcept { + return cond_.get(); +} + +Expr* WhileStmt::MutableCondition() noexcept { + return cond_.get(); +} + +void WhileStmt::SetCondition(std::unique_ptr condition_expr) { + cond_ = std::move(condition_expr); +} + +std::unique_ptr WhileStmt::ReleaseCondition() { + return std::move(cond_); +} + +const Block* WhileStmt::Body() const noexcept { + return body_.get(); +} + +Block* WhileStmt::MutableBody() noexcept { + return body_.get(); +} + +void WhileStmt::SetBody(std::unique_ptr body_block) { + body_ = std::move(body_block); +} + +std::unique_ptr WhileStmt::ReleaseBody() { + return std::move(body_); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/nodes/stmts/WhileStmt.hpp b/lib/parser/ast/nodes/stmts/WhileStmt.hpp new file mode 100644 index 0000000..8a4c65b --- /dev/null +++ b/lib/parser/ast/nodes/stmts/WhileStmt.hpp @@ -0,0 +1,33 @@ +#ifndef PARSER_WHILESTMT_HPP_ +#define PARSER_WHILESTMT_HPP_ + +#include + +#include "Block.hpp" +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/ast/nodes/base/Stmt.hpp" + +namespace ovum::compiler::parser { + +class WhileStmt : public Stmt { +public: + void Accept(AstVisitor& visitor) override; + + const Expr* Condition() const noexcept; + Expr* MutableCondition() noexcept; + void SetCondition(std::unique_ptr condition_expr); + std::unique_ptr ReleaseCondition(); + + const Block* Body() const noexcept; + Block* MutableBody() noexcept; + void SetBody(std::unique_ptr body_block); + std::unique_ptr ReleaseBody(); + +private: + std::unique_ptr cond_; + std::unique_ptr body_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_WHILESTMT_HPP_ diff --git a/lib/parser/ast/visitors/BytecodeVisitor.cpp b/lib/parser/ast/visitors/BytecodeVisitor.cpp new file mode 100644 index 0000000..28c3a68 --- /dev/null +++ b/lib/parser/ast/visitors/BytecodeVisitor.cpp @@ -0,0 +1,745 @@ +#include "BytecodeVisitor.hpp" + +#include +#include +#include +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/ast/nodes/base/Stmt.hpp" +#include "lib/parser/ast/nodes/class_members/CallDecl.hpp" +#include "lib/parser/ast/nodes/class_members/DestructorDecl.hpp" +#include "lib/parser/ast/nodes/class_members/FieldDecl.hpp" +#include "lib/parser/ast/nodes/class_members/MethodDecl.hpp" +#include "lib/parser/ast/nodes/class_members/StaticFieldDecl.hpp" +#include "lib/parser/ast/nodes/decls/ClassDecl.hpp" +#include "lib/parser/ast/nodes/decls/FunctionDecl.hpp" +#include "lib/parser/ast/nodes/decls/GlobalVarDecl.hpp" +#include "lib/parser/ast/nodes/decls/InterfaceDecl.hpp" +#include "lib/parser/ast/nodes/decls/InterfaceMethod.hpp" +#include "lib/parser/ast/nodes/decls/Module.hpp" +#include "lib/parser/ast/nodes/decls/TypeAliasDecl.hpp" +#include "lib/parser/ast/nodes/exprs/Binary.hpp" +#include "lib/parser/ast/nodes/exprs/Unary.hpp" +#include "lib/parser/ast/nodes/exprs/Assign.hpp" +#include "lib/parser/ast/nodes/exprs/Call.hpp" +#include "lib/parser/ast/nodes/exprs/FieldAccess.hpp" +#include "lib/parser/ast/nodes/exprs/IndexAccess.hpp" +#include "lib/parser/ast/nodes/exprs/NamespaceRef.hpp" +#include "lib/parser/ast/nodes/exprs/SafeCall.hpp" +#include "lib/parser/ast/nodes/exprs/Elvis.hpp" +#include "lib/parser/ast/nodes/exprs/CastAs.hpp" +#include "lib/parser/ast/nodes/exprs/TypeTestIs.hpp" +#include "lib/parser/ast/nodes/exprs/IdentRef.hpp" +#include "lib/parser/ast/nodes/exprs/literals/IntLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/FloatLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/StringLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/CharLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/BoolLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/NullLit.hpp" +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/ast/nodes/stmts/VarDeclStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ExprStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ReturnStmt.hpp" +#include "lib/parser/ast/nodes/stmts/BreakStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ContinueStmt.hpp" +#include "lib/parser/ast/nodes/stmts/IfStmt.hpp" +#include "lib/parser/ast/nodes/stmts/WhileStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ForStmt.hpp" +#include "lib/parser/ast/nodes/stmts/UnsafeBlock.hpp" +#include "lib/parser/ast/nodes/exprs/tags/OpTags.hpp" +#include "lib/parser/types/TypeReference.hpp" +#include "lib/execution_tree/Block.hpp" +#include "lib/execution_tree/Command.hpp" +#include "lib/execution_tree/Function.hpp" +#include "lib/execution_tree/command_factory.hpp" +#include "lib/execution_tree/BytecodeCommands.hpp" +#include "lib/execution_tree/IFunctionExecutable.hpp" +#include "lib/execution_tree/ConditionalExecution.hpp" +#include "lib/execution_tree/IfMultibranch.hpp" +#include "lib/execution_tree/WhileExecution.hpp" +#include "lib/runtime/FunctionId.hpp" + +namespace ovum::compiler::parser { + +BytecodeVisitor::BytecodeVisitor(ovum::vm::execution_tree::FunctionRepository& function_repository) + : function_repository_(function_repository) { +} + +void BytecodeVisitor::Visit(Module& node) { + current_namespace_.clear(); + for (auto& decl : node.MutableDecls()) { + decl->Accept(*this); + } +} + +void BytecodeVisitor::Visit(FunctionDecl& node) { + ResetLocalVariables(); + + // Register parameters as local variables + size_t param_index = 0; + for (const auto& param : node.Params()) { + local_variables_[param.Name()] = param_index++; + } + + // Create function body block + auto body_block = CreateBlock(); + SetCurrentBlock(std::move(body_block)); + + if (node.Body() != nullptr) { + node.Body()->Accept(*this); + } + + // Create function + std::string func_id = GenerateFunctionId(node.Name(), node.Params()); + auto func_body = std::move(current_block_); + auto func = std::make_unique( + ovum::vm::runtime::FunctionId(func_id), node.Params().size(), std::move(func_body)); + + // Register function in repository (takes ownership) + function_repository_.Add(std::move(func)); +} + +void BytecodeVisitor::Visit(ClassDecl& node) { + std::string prev_class = current_class_name_; + current_class_name_ = node.Name(); + + for (auto& member : node.MutableMembers()) { + member->Accept(*this); + } + + current_class_name_ = prev_class; +} + +void BytecodeVisitor::Visit(InterfaceMethod& node) { + // Interface methods are just declarations, no implementation +} + +void BytecodeVisitor::Visit(InterfaceDecl& node) { + // Interfaces are just declarations +} + +void BytecodeVisitor::Visit(TypeAliasDecl& node) { + // Type aliases are compile-time only +} + +void BytecodeVisitor::Visit(GlobalVarDecl& node) { + // Global variables are handled separately + if (node.Init() != nullptr) { + node.Init()->Accept(*this); + // Store in static variable + size_t static_index = GetStaticIndex(node.Name()); + auto cmd = ovum::vm::execution_tree::CreateIntegerCommandByName("SetStatic", static_cast(static_index)); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } +} + +void BytecodeVisitor::Visit(FieldDecl& node) { + // Fields are part of class structure, handled during class construction +} + +void BytecodeVisitor::Visit(StaticFieldDecl& node) { + // Static fields are handled like global variables + if (node.Init() != nullptr) { + node.Init()->Accept(*this); + size_t static_index = GetStaticIndex(node.Name()); + auto cmd = ovum::vm::execution_tree::CreateIntegerCommandByName("SetStatic", static_cast(static_index)); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } +} + +void BytecodeVisitor::Visit(MethodDecl& node) { + ResetLocalVariables(); + + // Register 'this' as local 0 if not static + if (!node.IsStatic()) { + local_variables_["this"] = 0; + } + + // Register parameters + size_t param_index = node.IsStatic() ? 0 : 1; + for (const auto& param : node.Params()) { + local_variables_[param.Name()] = param_index++; + } + + // Create method body block + auto body_block = CreateBlock(); + SetCurrentBlock(std::move(body_block)); + + if (node.Body() != nullptr) { + node.Body()->Accept(*this); + } + + // Create method function + std::string method_id = GenerateMethodId(current_class_name_, node.Name(), node.Params(), false, false); + auto method_body = std::move(current_block_); + auto method = std::make_unique( + ovum::vm::runtime::FunctionId(method_id), node.Params().size() + (node.IsStatic() ? 0 : 1), + std::move(method_body)); + + // Register method in repository (takes ownership) + function_repository_.Add(std::move(method)); +} + +void BytecodeVisitor::Visit(CallDecl& node) { + ResetLocalVariables(); + + // Register 'this' as local 0 + local_variables_["this"] = 0; + + // Register parameters + size_t param_index = 1; + for (const auto& param : node.Params()) { + local_variables_[param.Name()] = param_index++; + } + + // Create call body block + auto body_block = CreateBlock(); + SetCurrentBlock(std::move(body_block)); + + if (node.Body() != nullptr) { + node.Body()->Accept(*this); + } + + // Create call function + std::string call_id = GenerateMethodId(current_class_name_, "call", node.Params(), false, false); + auto call_body = std::move(current_block_); + auto call_func = std::make_unique( + ovum::vm::runtime::FunctionId(call_id), node.Params().size() + 1, std::move(call_body)); + + // Register call function in repository (takes ownership) + function_repository_.Add(std::move(call_func)); +} + +void BytecodeVisitor::Visit(DestructorDecl& node) { + ResetLocalVariables(); + + // Register 'this' as local 0 + local_variables_["this"] = 0; + + // Create destructor body block + auto body_block = CreateBlock(); + SetCurrentBlock(std::move(body_block)); + + if (node.Body() != nullptr) { + node.Body()->Accept(*this); + } + + // Create destructor function + std::string destructor_id = GenerateDestructorId(current_class_name_); + auto destructor_body = std::move(current_block_); + auto destructor = std::make_unique( + ovum::vm::runtime::FunctionId(destructor_id), 1, std::move(destructor_body)); + + // Register destructor in repository (takes ownership) + function_repository_.Add(std::move(destructor)); +} + +void BytecodeVisitor::Visit(Block& node) { + for (auto& stmt : node.MutableStatements()) { + stmt->Accept(*this); + } +} + +void BytecodeVisitor::Visit(VarDeclStmt& node) { + if (node.Init() != nullptr) { + node.Init()->Accept(*this); + size_t local_index = GetLocalIndex(node.Name()); + auto cmd = ovum::vm::execution_tree::CreateIntegerCommandByName("SetLocal", static_cast(local_index)); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } +} + +void BytecodeVisitor::Visit(ExprStmt& node) { + if (node.Expr() != nullptr) { + node.Expr()->Accept(*this); + // Pop result if expression has side effects + auto pop_cmd = ovum::vm::execution_tree::CreateSimpleCommandByName("Pop"); + if (pop_cmd.has_value()) { + PushCommand(std::move(pop_cmd.value())); + } + } +} + +void BytecodeVisitor::Visit(ReturnStmt& node) { + if (node.Value() != nullptr) { + node.Value()->Accept(*this); + } + auto return_cmd = ovum::vm::execution_tree::CreateSimpleCommandByName("Return"); + if (return_cmd.has_value()) { + PushCommand(std::move(return_cmd.value())); + } +} + +void BytecodeVisitor::Visit(BreakStmt& node) { + // Break is handled by loop constructs + // TODO: Implement break handling +} + +void BytecodeVisitor::Visit(ContinueStmt& node) { + // Continue is handled by loop constructs + // TODO: Implement continue handling +} + +void BytecodeVisitor::Visit(IfStmt& node) { + auto if_multibranch = std::make_unique(); + + // Process all branches + for (const auto& branch : node.Branches()) { + // Create condition block + auto cond_block = CreateBlock(); + auto prev_block = std::move(current_block_); + SetCurrentBlock(std::move(cond_block)); + branch.Condition()->Accept(*this); + auto cond_block_final = std::move(current_block_); + + // Create body block + auto body_block = CreateBlock(); + SetCurrentBlock(std::move(body_block)); + branch.Body()->Accept(*this); + auto body_block_final = std::move(current_block_); + + // Create conditional execution + auto conditional = std::make_unique( + std::move(cond_block_final), std::move(body_block_final)); + if_multibranch->AddBranch(std::move(conditional)); + + SetCurrentBlock(std::move(prev_block)); + } + + // Process else block if present + if (node.ElseBlock() != nullptr) { + auto else_block = CreateBlock(); + auto prev_block = std::move(current_block_); + SetCurrentBlock(std::move(else_block)); + node.ElseBlock()->Accept(*this); + auto else_block_final = std::move(current_block_); + if_multibranch->SetElseBlock(std::move(else_block_final)); + SetCurrentBlock(std::move(prev_block)); + } + + // Add if statement to current block + PushCommand(std::move(if_multibranch)); +} + +void BytecodeVisitor::Visit(WhileStmt& node) { + // Create condition block + auto cond_block = CreateBlock(); + auto prev_block = std::move(current_block_); + SetCurrentBlock(std::move(cond_block)); + if (node.Condition() != nullptr) { + node.Condition()->Accept(*this); + } + auto cond_block_final = std::move(current_block_); + + // Create body block + auto body_block = CreateBlock(); + SetCurrentBlock(std::move(body_block)); + if (node.Body() != nullptr) { + node.Body()->Accept(*this); + } + auto body_block_final = std::move(current_block_); + + // Create while execution + auto while_exec = std::make_unique( + std::move(cond_block_final), std::move(body_block_final)); + + SetCurrentBlock(std::move(prev_block)); + PushCommand(std::move(while_exec)); +} + +void BytecodeVisitor::Visit(ForStmt& node) { + // TODO: Implement for loop + if (node.IterExpr() != nullptr) { + node.IterExpr()->Accept(*this); + } + if (node.Body() != nullptr) { + node.Body()->Accept(*this); + } +} + +void BytecodeVisitor::Visit(UnsafeBlock& node) { + if (node.Body() != nullptr) { + node.Body()->Accept(*this); + } +} + +void BytecodeVisitor::Visit(Binary& node) { + // Push operands right-to-left + node.Rhs().Accept(*this); + node.Lhs().Accept(*this); + + // Determine operation based on operator and type + std::string op_name = node.Op().Name(); + + // Map operator to bytecode command + // This is simplified - in real implementation, we'd need type information + if (op_name == "+") { + auto cmd = ovum::vm::execution_tree::CreateSimpleCommandByName("IntAdd"); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } else if (op_name == "-") { + auto cmd = ovum::vm::execution_tree::CreateSimpleCommandByName("IntSubtract"); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } else if (op_name == "*") { + auto cmd = ovum::vm::execution_tree::CreateSimpleCommandByName("IntMultiply"); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } else if (op_name == "/") { + auto cmd = ovum::vm::execution_tree::CreateSimpleCommandByName("IntDivide"); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } else if (op_name == "%") { + auto cmd = ovum::vm::execution_tree::CreateSimpleCommandByName("IntModulo"); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } else if (op_name == "<") { + auto cmd = ovum::vm::execution_tree::CreateSimpleCommandByName("IntLessThan"); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } else if (op_name == "<=") { + auto cmd = ovum::vm::execution_tree::CreateSimpleCommandByName("IntLessEqual"); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } else if (op_name == ">") { + auto cmd = ovum::vm::execution_tree::CreateSimpleCommandByName("IntGreaterThan"); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } else if (op_name == ">=") { + auto cmd = ovum::vm::execution_tree::CreateSimpleCommandByName("IntGreaterEqual"); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } else if (op_name == "==") { + auto cmd = ovum::vm::execution_tree::CreateSimpleCommandByName("IntEqual"); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } else if (op_name == "!=") { + auto cmd = ovum::vm::execution_tree::CreateSimpleCommandByName("IntNotEqual"); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } else if (op_name == "&&") { + auto cmd = ovum::vm::execution_tree::CreateSimpleCommandByName("BoolAnd"); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } else if (op_name == "||") { + auto cmd = ovum::vm::execution_tree::CreateSimpleCommandByName("BoolOr"); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } +} + +void BytecodeVisitor::Visit(Unary& node) { + node.Operand().Accept(*this); + + std::string op_name = node.Op().Name(); + + if (op_name == "-") { + auto cmd = ovum::vm::execution_tree::CreateSimpleCommandByName("IntNegate"); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } else if (op_name == "!") { + auto cmd = ovum::vm::execution_tree::CreateSimpleCommandByName("BoolNot"); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } +} + +void BytecodeVisitor::Visit(Assign& node) { + // Evaluate value first + node.Value().Accept(*this); + + // Then assign to target + // TODO: Handle different assignment types (reference vs copy) + if (auto* ident = dynamic_cast(&node.Target())) { + size_t local_index = GetLocalIndex(ident->Name()); + auto cmd = ovum::vm::execution_tree::CreateIntegerCommandByName("SetLocal", static_cast(local_index)); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } +} + +void BytecodeVisitor::Visit(Call& node) { + // Push arguments right-to-left + for (auto it = node.Args().rbegin(); it != node.Args().rend(); ++it) { + (*it)->Accept(*this); + } + + // Call function + // TODO: Determine function name from callee + if (auto* ident = dynamic_cast(node.Callee())) { + std::string func_name = ident->Name(); + auto cmd = ovum::vm::execution_tree::CreateStringCommandByName("Call", func_name); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } +} + +void BytecodeVisitor::Visit(FieldAccess& node) { + node.Object().Accept(*this); + // TODO: Implement field access +} + +void BytecodeVisitor::Visit(IndexAccess& node) { + node.IndexExpr().Accept(*this); + node.Object().Accept(*this); + // TODO: Implement index access +} + +void BytecodeVisitor::Visit(NamespaceRef& node) { + node.NamespaceExpr().Accept(*this); + // TODO: Implement namespace reference +} + +void BytecodeVisitor::Visit(SafeCall& node) { + // Push arguments right-to-left + for (auto it = node.Args().rbegin(); it != node.Args().rend(); ++it) { + (*it)->Accept(*this); + } + + node.Object().Accept(*this); + + auto cmd = ovum::vm::execution_tree::CreateStringCommandByName("SafeCall", node.Method()); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } +} + +void BytecodeVisitor::Visit(Elvis& node) { + node.Lhs().Accept(*this); + // TODO: Check for null and use rhs if null + node.Rhs().Accept(*this); + auto cmd = ovum::vm::execution_tree::CreateSimpleCommandByName("NullCoalesce"); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } +} + +void BytecodeVisitor::Visit(CastAs& node) { + node.Expr().Accept(*this); + // TODO: Implement type casting +} + +void BytecodeVisitor::Visit(TypeTestIs& node) { + node.Expr().Accept(*this); + std::string type_name = TypeToMangledName(node.Type()); + auto cmd = ovum::vm::execution_tree::CreateStringCommandByName("IsType", type_name); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } +} + +void BytecodeVisitor::Visit(IdentRef& node) { + // Check if it's a local variable first + if (local_variables_.find(node.Name()) != local_variables_.end()) { + size_t local_index = GetLocalIndex(node.Name()); + auto cmd = ovum::vm::execution_tree::CreateIntegerCommandByName("LoadLocal", static_cast(local_index)); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } else { + // Assume it's a global/static variable + size_t static_index = GetStaticIndex(node.Name()); + auto cmd = ovum::vm::execution_tree::CreateIntegerCommandByName("LoadStatic", static_cast(static_index)); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } + } +} + +void BytecodeVisitor::Visit(IntLit& node) { + auto cmd = ovum::vm::execution_tree::CreateIntegerCommandByName("PushInt", node.Value()); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } +} + +void BytecodeVisitor::Visit(FloatLit& node) { + auto cmd = ovum::vm::execution_tree::CreateFloatCommandByName("PushFloat", node.Value()); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } +} + +void BytecodeVisitor::Visit(StringLit& node) { + auto cmd = ovum::vm::execution_tree::CreateStringCommandByName("PushString", node.Value()); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } +} + +void BytecodeVisitor::Visit(CharLit& node) { + auto cmd = ovum::vm::execution_tree::CreateIntegerCommandByName("PushChar", static_cast(node.Value())); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } +} + +void BytecodeVisitor::Visit(BoolLit& node) { + auto cmd = ovum::vm::execution_tree::CreateBooleanCommandByName("PushBool", node.Value()); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } +} + +void BytecodeVisitor::Visit(NullLit& node) { + auto cmd = ovum::vm::execution_tree::CreateSimpleCommandByName("PushNull"); + if (cmd.has_value()) { + PushCommand(std::move(cmd.value())); + } +} + +std::vector> BytecodeVisitor::ReleaseFunctions() { + // Functions are owned by function_repository_, so return empty vector + // In a real implementation, you might want to extract functions from repository + return {}; +} + +void BytecodeVisitor::PushCommand(std::unique_ptr cmd) { + if (current_block_ != nullptr) { + current_block_->AddStatement(std::move(cmd)); + } +} + +std::string BytecodeVisitor::GenerateFunctionId(const std::string& name, const std::vector& params) { + std::ostringstream oss; + oss << name << "("; + for (size_t i = 0; i < params.size(); ++i) { + if (i > 0) { + oss << ","; + } + oss << TypeToMangledName(params[i].Type()); + } + oss << ")"; + return oss.str(); +} + +std::string BytecodeVisitor::GenerateMethodId(const std::string& class_name, const std::string& method_name, + const std::vector& params, bool is_constructor, + bool is_destructor) { + std::ostringstream oss; + oss << class_name << "::" << method_name << "("; + for (size_t i = 0; i < params.size(); ++i) { + if (i > 0) { + oss << ","; + } + oss << TypeToMangledName(params[i].Type()); + } + oss << ")"; + return oss.str(); +} + +std::string BytecodeVisitor::GenerateConstructorId(const std::string& class_name, const std::vector& params) { + return GenerateMethodId(class_name, class_name, params, true, false); +} + +std::string BytecodeVisitor::GenerateDestructorId(const std::string& class_name) { + return class_name + "::~" + class_name + "()"; +} + +std::string BytecodeVisitor::TypeToMangledName(const TypeReference& type) { + std::ostringstream oss; + const auto& qname = type.QualifiedName(); + for (size_t i = 0; i < qname.size(); ++i) { + if (i > 0) { + oss << "."; + } + oss << qname[i]; + } + + if (type.Arity() > 0) { + oss << "<"; + for (size_t i = 0; i < type.Arity(); ++i) { + if (i > 0) { + oss << ","; + } + oss << TypeToMangledName(type.TypeArguments()[i]); + } + oss << ">"; + } + + if (type.IsNullable()) { + oss << "?"; + } + + return oss.str(); +} + +void BytecodeVisitor::VisitExpression(Expr* expr) { + if (expr != nullptr) { + expr->Accept(*this); + } +} + +void BytecodeVisitor::VisitStatement(Stmt* stmt) { + if (stmt != nullptr) { + stmt->Accept(*this); + } +} + +void BytecodeVisitor::VisitBlock(Block* block) { + if (block != nullptr) { + block->Accept(*this); + } +} + +std::unique_ptr BytecodeVisitor::CreateBlock() { + return std::make_unique(); +} + +void BytecodeVisitor::SetCurrentBlock(std::unique_ptr block) { + current_block_ = std::move(block); +} + +size_t BytecodeVisitor::GetLocalIndex(const std::string& name) { + auto it = local_variables_.find(name); + if (it != local_variables_.end()) { + return it->second; + } + // If not found, allocate new index + size_t index = next_local_index_++; + local_variables_[name] = index; + return index; +} + +size_t BytecodeVisitor::GetStaticIndex(const std::string& name) { + auto it = static_variables_.find(name); + if (it != static_variables_.end()) { + return it->second; + } + // If not found, allocate new index + size_t index = next_static_index_++; + static_variables_[name] = index; + return index; +} + +void BytecodeVisitor::ResetLocalVariables() { + local_variables_.clear(); + next_local_index_ = 0; +} + +} // namespace ovum::compiler::parser + diff --git a/lib/parser/ast/visitors/BytecodeVisitor.hpp b/lib/parser/ast/visitors/BytecodeVisitor.hpp new file mode 100644 index 0000000..97aa547 --- /dev/null +++ b/lib/parser/ast/visitors/BytecodeVisitor.hpp @@ -0,0 +1,109 @@ +#ifndef PARSER_BYTECODEVISITOR_HPP_ +#define PARSER_BYTECODEVISITOR_HPP_ + +#include +#include +#include +#include + +#include "lib/parser/ast/AstVisitor.hpp" + +namespace ovum::vm::execution_tree { +class Block; +class Function; +class FunctionRepository; +class IExecutable; +} // namespace ovum::vm::execution_tree + +namespace ovum::compiler::parser { + +class BytecodeVisitor : public AstVisitor { +public: + explicit BytecodeVisitor(ovum::vm::execution_tree::FunctionRepository& function_repository); + + ~BytecodeVisitor() override = default; + + // Decls + void Visit(Module& node) override; + void Visit(FunctionDecl& node) override; + void Visit(ClassDecl& node) override; + void Visit(InterfaceMethod& node) override; + void Visit(InterfaceDecl& node) override; + void Visit(TypeAliasDecl& node) override; + void Visit(GlobalVarDecl& node) override; + void Visit(FieldDecl& node) override; + void Visit(StaticFieldDecl& node) override; + void Visit(MethodDecl& node) override; + void Visit(CallDecl& node) override; + void Visit(DestructorDecl& node) override; + + // Stmts + void Visit(Block& node) override; + void Visit(VarDeclStmt& node) override; + void Visit(ExprStmt& node) override; + void Visit(ReturnStmt& node) override; + void Visit(BreakStmt& node) override; + void Visit(ContinueStmt& node) override; + void Visit(IfStmt& node) override; + void Visit(WhileStmt& node) override; + void Visit(ForStmt& node) override; + void Visit(UnsafeBlock& node) override; + + // Exprs + void Visit(Binary& node) override; + void Visit(Unary& node) override; + void Visit(Assign& node) override; + void Visit(Call& node) override; + void Visit(FieldAccess& node) override; + void Visit(IndexAccess& node) override; + void Visit(NamespaceRef& node) override; + void Visit(SafeCall& node) override; + void Visit(Elvis& node) override; + void Visit(CastAs& node) override; + void Visit(TypeTestIs& node) override; + void Visit(IdentRef& node) override; + void Visit(IntLit& node) override; + void Visit(FloatLit& node) override; + void Visit(StringLit& node) override; + void Visit(CharLit& node) override; + void Visit(BoolLit& node) override; + void Visit(NullLit& node) override; + + // Get generated functions + [[nodiscard]] std::vector> ReleaseFunctions(); + +private: + ovum::vm::execution_tree::FunctionRepository& function_repository_; + + std::unique_ptr current_block_; + std::string current_class_name_; + std::vector current_namespace_; + + // Variable tracking + std::unordered_map local_variables_; + std::unordered_map static_variables_; + size_t next_local_index_{0}; + size_t next_static_index_{0}; + + // Helper methods + void PushCommand(std::unique_ptr cmd); + std::string GenerateFunctionId(const std::string& name, const std::vector& params); + std::string GenerateMethodId(const std::string& class_name, const std::string& method_name, + const std::vector& params, bool is_constructor, bool is_destructor); + std::string GenerateConstructorId(const std::string& class_name, const std::vector& params); + std::string GenerateDestructorId(const std::string& class_name); + std::string TypeToMangledName(const TypeReference& type); + void VisitExpression(Expr* expr); + void VisitStatement(Stmt* stmt); + void VisitBlock(Block* block); + std::unique_ptr CreateBlock(); + void SetCurrentBlock(std::unique_ptr block); + size_t GetLocalIndex(const std::string& name); + size_t GetStaticIndex(const std::string& name); + void ResetLocalVariables(); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_BYTECODEVISITOR_HPP_ + diff --git a/lib/parser/ast/visitors/ConstWalkVisitor.cpp b/lib/parser/ast/visitors/ConstWalkVisitor.cpp new file mode 100644 index 0000000..37f244e --- /dev/null +++ b/lib/parser/ast/visitors/ConstWalkVisitor.cpp @@ -0,0 +1,274 @@ +#include "ConstWalkVisitor.hpp" + +#include "lib/parser/ast/nodes/decls/ClassDecl.hpp" +#include "lib/parser/ast/nodes/decls/FunctionDecl.hpp" +#include "lib/parser/ast/nodes/decls/GlobalVarDecl.hpp" +#include "lib/parser/ast/nodes/decls/InterfaceDecl.hpp" +#include "lib/parser/ast/nodes/decls/InterfaceMethod.hpp" +#include "lib/parser/ast/nodes/decls/Module.hpp" +#include "lib/parser/ast/nodes/decls/TypeAliasDecl.hpp" + +#include "lib/parser/ast/nodes/class_members/CallDecl.hpp" +#include "lib/parser/ast/nodes/class_members/DestructorDecl.hpp" +#include "lib/parser/ast/nodes/class_members/FieldDecl.hpp" +#include "lib/parser/ast/nodes/class_members/MethodDecl.hpp" +#include "lib/parser/ast/nodes/class_members/StaticFieldDecl.hpp" + +#include "lib/parser/ast/nodes/exprs/Assign.hpp" +#include "lib/parser/ast/nodes/exprs/Binary.hpp" +#include "lib/parser/ast/nodes/exprs/Call.hpp" +#include "lib/parser/ast/nodes/exprs/CastAs.hpp" +#include "lib/parser/ast/nodes/exprs/Elvis.hpp" +#include "lib/parser/ast/nodes/exprs/FieldAccess.hpp" +#include "lib/parser/ast/nodes/exprs/IdentRef.hpp" +#include "lib/parser/ast/nodes/exprs/IndexAccess.hpp" +#include "lib/parser/ast/nodes/exprs/NamespaceRef.hpp" +#include "lib/parser/ast/nodes/exprs/SafeCall.hpp" +#include "lib/parser/ast/nodes/exprs/TypeTestIs.hpp" +#include "lib/parser/ast/nodes/exprs/Unary.hpp" + +#include "lib/parser/ast/nodes/exprs/literals/BoolLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/CharLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/FloatLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/IntLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/NullLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/StringLit.hpp" + +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/ast/nodes/stmts/BreakStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ContinueStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ExprStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ForStmt.hpp" +#include "lib/parser/ast/nodes/stmts/IfStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ReturnStmt.hpp" +#include "lib/parser/ast/nodes/stmts/UnsafeBlock.hpp" +#include "lib/parser/ast/nodes/stmts/VarDeclStmt.hpp" +#include "lib/parser/ast/nodes/stmts/WhileStmt.hpp" + +namespace ovum::compiler::parser { + +void ConstWalkVisitor::Visit(Module& node) { + for (auto& decl_ptr : node.MutableDecls()) { + decl_ptr->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(FunctionDecl& node) { + if (auto* body = node.MutableBody()) { + body->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(ClassDecl& node) { + for (auto& member : node.MutableMembers()) { + member->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(InterfaceMethod& node) { + (void) node; +} + +void ConstWalkVisitor::Visit(InterfaceDecl& node) { + for (auto& m : node.MutableMembers()) { + m->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(TypeAliasDecl& node) { + (void) node; +} + +void ConstWalkVisitor::Visit(GlobalVarDecl& node) { + if (auto* init = node.MutableInit()) { + init->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(FieldDecl& node) { + if (auto* init = node.MutableInit()) { + init->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(StaticFieldDecl& node) { + if (auto* init = node.MutableInit()) { + init->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(MethodDecl& node) { + if (auto* body = node.MutableBody()) { + body->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(CallDecl& node) { + if (auto* body = node.MutableBody()) { + body->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(DestructorDecl& node) { + if (auto* body = node.MutableBody()) { + body->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(Block& node) { + for (auto& stmt : node.GetStatements()) { + stmt->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(VarDeclStmt& node) { + if (auto* init = node.MutableInit()) { + init->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(ExprStmt& node) { + if (auto* e = node.MutableExpression()) { + e->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(ReturnStmt& node) { + if (auto* v = node.MutableValue()) { + v->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(BreakStmt& node) { + (void) node; +} + +void ConstWalkVisitor::Visit(ContinueStmt& node) { + (void) node; +} + +void ConstWalkVisitor::Visit(IfStmt& node) { + for (auto& br : node.MutableBranches()) { + if (auto* c = br.MutableCondition()) { + c->Accept(*this); + } + + if (auto* t = br.MutableThen()) { + t->Accept(*this); + } + } + + if (auto* eb = node.MutableElseBlock()) { + eb->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(WhileStmt& node) { + if (auto* c = node.MutableCondition()) { + c->Accept(*this); + } + + if (auto* b = node.MutableBody()) { + b->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(ForStmt& node) { + if (auto* it = node.MutableIteratorExpr()) { + it->Accept(*this); + } + + if (auto* b = node.MutableBody()) { + b->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(UnsafeBlock& node) { + if (auto* b = node.MutableBody()) { + b->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(Binary& node) { + node.MutableLhs().Accept(*this); + node.MutableRhs().Accept(*this); +} + +void ConstWalkVisitor::Visit(Unary& node) { + node.MutableOperand().Accept(*this); +} + +void ConstWalkVisitor::Visit(Assign& node) { + node.MutableTarget().Accept(*this); + node.MutableValue().Accept(*this); +} + +void ConstWalkVisitor::Visit(Call& node) { + node.MutableCallee().Accept(*this); + for (auto& a : node.MutableArgs()) { + a->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(FieldAccess& node) { + node.MutableObject().Accept(*this); +} + +void ConstWalkVisitor::Visit(IndexAccess& node) { + node.MutableObject().Accept(*this); + node.MutableIndexExpr().Accept(*this); +} + +void ConstWalkVisitor::Visit(NamespaceRef& node) { + node.MutableNamespaceExpr().Accept(*this); +} + +void ConstWalkVisitor::Visit(SafeCall& node) { + node.MutableObject().Accept(*this); + for (auto& a : node.MutableArgs()) { + a->Accept(*this); + } +} + +void ConstWalkVisitor::Visit(Elvis& node) { + node.MutableLhs().Accept(*this); + node.MutableRhs().Accept(*this); +} + +void ConstWalkVisitor::Visit(CastAs& node) { + node.MutableExpression().Accept(*this); +} + +void ConstWalkVisitor::Visit(TypeTestIs& node) { + node.MutableExpression().Accept(*this); +} + +void ConstWalkVisitor::Visit(IdentRef& node) { + (void) node; +} + +void ConstWalkVisitor::Visit(IntLit& node) { + (void) node; +} + +void ConstWalkVisitor::Visit(FloatLit& node) { + (void) node; +} + +void ConstWalkVisitor::Visit(StringLit& node) { + (void) node; +} + +void ConstWalkVisitor::Visit(CharLit& node) { + (void) node; +} + +void ConstWalkVisitor::Visit(BoolLit& node) { + (void) node; +} + +void ConstWalkVisitor::Visit(NullLit& node) { + (void) node; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/visitors/ConstWalkVisitor.hpp b/lib/parser/ast/visitors/ConstWalkVisitor.hpp new file mode 100644 index 0000000..c1b3f6d --- /dev/null +++ b/lib/parser/ast/visitors/ConstWalkVisitor.hpp @@ -0,0 +1,58 @@ +#ifndef PARSER_CONSTWALKVISITOR_HPP_ +#define PARSER_CONSTWALKVISITOR_HPP_ + +#include "lib/parser/ast/AstVisitor.hpp" + +namespace ovum::compiler::parser { + +class ConstWalkVisitor : public AstVisitor { +public: + ~ConstWalkVisitor() override = default; + + void Visit(Module& node) override; + void Visit(FunctionDecl& node) override; + void Visit(ClassDecl& node) override; + void Visit(InterfaceMethod& node) override; + void Visit(InterfaceDecl& node) override; + void Visit(TypeAliasDecl& node) override; + void Visit(GlobalVarDecl& node) override; + void Visit(FieldDecl& node) override; + void Visit(StaticFieldDecl& node) override; + void Visit(MethodDecl& node) override; + void Visit(CallDecl& node) override; + void Visit(DestructorDecl& node) override; + + void Visit(Block& node) override; + void Visit(VarDeclStmt& node) override; + void Visit(ExprStmt& node) override; + void Visit(ReturnStmt& node) override; + void Visit(BreakStmt& node) override; + void Visit(ContinueStmt& node) override; + void Visit(IfStmt& node) override; + void Visit(WhileStmt& node) override; + void Visit(ForStmt& node) override; + void Visit(UnsafeBlock& node) override; + + void Visit(Binary& node) override; + void Visit(Unary& node) override; + void Visit(Assign& node) override; + void Visit(Call& node) override; + void Visit(FieldAccess& node) override; + void Visit(IndexAccess& node) override; + void Visit(NamespaceRef& node) override; + void Visit(SafeCall& node) override; + void Visit(Elvis& node) override; + void Visit(CastAs& node) override; + void Visit(TypeTestIs& node) override; + void Visit(IdentRef& node) override; + void Visit(IntLit& node) override; + void Visit(FloatLit& node) override; + void Visit(StringLit& node) override; + void Visit(CharLit& node) override; + void Visit(BoolLit& node) override; + void Visit(NullLit& node) override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_CONSTWALKVISITOR_HPP_ diff --git a/lib/parser/ast/visitors/LintVisitor.cpp b/lib/parser/ast/visitors/LintVisitor.cpp new file mode 100644 index 0000000..f7f5d82 --- /dev/null +++ b/lib/parser/ast/visitors/LintVisitor.cpp @@ -0,0 +1,327 @@ +#include "LintVisitor.hpp" + +#include + +#include "lib/parser/diagnostics/severity/Severity.hpp" + +#include "lib/parser/ast/nodes/class_members/CallDecl.hpp" +#include "lib/parser/ast/nodes/class_members/DestructorDecl.hpp" +#include "lib/parser/ast/nodes/class_members/FieldDecl.hpp" +#include "lib/parser/ast/nodes/class_members/MethodDecl.hpp" +#include "lib/parser/ast/nodes/class_members/StaticFieldDecl.hpp" + +#include "lib/parser/ast/nodes/decls/ClassDecl.hpp" +#include "lib/parser/ast/nodes/decls/FunctionDecl.hpp" +#include "lib/parser/ast/nodes/decls/GlobalVarDecl.hpp" +#include "lib/parser/ast/nodes/decls/Module.hpp" + +#include "lib/parser/ast/nodes/exprs/Assign.hpp" +#include "lib/parser/ast/nodes/exprs/Call.hpp" +#include "lib/parser/ast/nodes/exprs/SafeCall.hpp" + +#include "lib/parser/ast/nodes/exprs/literals/BoolLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/StringLit.hpp" + +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/ast/nodes/stmts/BreakStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ContinueStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ExprStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ForStmt.hpp" +#include "lib/parser/ast/nodes/stmts/IfStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ReturnStmt.hpp" +#include "lib/parser/ast/nodes/stmts/UnsafeBlock.hpp" +#include "lib/parser/ast/nodes/stmts/WhileStmt.hpp" + +namespace ovum::compiler::parser { + +void LintVisitor::EnterBody() { + ++nesting_depth_; +} + +void LintVisitor::LeaveBody() { + if (nesting_depth_ > 0) { + --nesting_depth_; + } +} + +void LintVisitor::EnterLoop() { + ++loop_depth_; +} + +void LintVisitor::LeaveLoop() { + if (loop_depth_ > 0) { + --loop_depth_; + } +} + +void LintVisitor::CheckNestingDepth(const SourceSpan&) const { + if (opts_.warn_deep_nesting && nesting_depth_ > opts_.max_nesting) { + sink_.Warn("W0201", "deep nesting"); + } +} + +bool LintVisitor::IsPureExpr(Expr& expression) const { + if (dynamic_cast(&expression)) { + return false; + } + + if (dynamic_cast(&expression)) { + return false; + } + + if (dynamic_cast(&expression)) { + return false; + } + + return true; +} + +void LintVisitor::Visit(Module& node) { + if (opts_.warn_module_without_decls && node.MutableDecls().empty()) { + sink_.Warn("W0001", "module has no declarations"); + } + + WalkVisitor::Visit(node); +} + +void LintVisitor::Visit(ClassDecl& node) { + if (opts_.warn_large_class && node.MutableMembers().size() > opts_.max_class_members) { + sink_.Warn("W0101", "class has too many members"); + } + + WalkVisitor::Visit(node); +} + +void LintVisitor::Visit(FunctionDecl& node) { + if (opts_.warn_empty_bodies) { + if (auto* b = node.MutableBody()) { + if (b->Size() == 0) { + sink_.Warn("W0102", "function body is empty"); + } + } + } + + WalkVisitor::Visit(node); +} + +void LintVisitor::Visit(MethodDecl& node) { + if (opts_.warn_empty_bodies) { + if (!node.IsPure()) { + if (auto* b = node.MutableBody()) { + if (b->Size() == 0) { + sink_.Warn("W0103", "method body is empty"); + } + } + } + } + + WalkVisitor::Visit(node); +} + +void LintVisitor::Visit(CallDecl& node) { + if (opts_.warn_empty_bodies) { + if (auto* b = node.MutableBody()) { + if (b->Size() == 0) { + sink_.Warn("W0104", "call body is empty"); + } + } + } + + WalkVisitor::Visit(node); +} + +void LintVisitor::Visit(DestructorDecl& node) { + if (opts_.warn_empty_bodies) { + if (auto* b = node.MutableBody()) { + if (b->Size() == 0) { + sink_.Warn("W0105", "destructor body is empty"); + } + } + } + + WalkVisitor::Visit(node); +} + +void LintVisitor::Visit(Block& node) { + EnterBody(); + + if (opts_.warn_empty_blocks && node.GetStatements().empty()) { + sink_.Warn("W0202", "empty block"); + } + + CheckNestingDepth(); + + bool terminated = false; + if (opts_.warn_unreachable) { + for (const auto& stmt : node.GetStatements()) { + if (terminated) { + sink_.Warn("W0301", "unreachable statement"); + continue; + } + + if (dynamic_cast(stmt.get()) || dynamic_cast(stmt.get()) || + dynamic_cast(stmt.get())) { + terminated = true; + } + } + } + + if (opts_.max_block_len > 0 && node.GetStatements().size() > opts_.max_block_len) { + sink_.Warn("W0203", "block is too long"); + } + + for (auto& stmt : node.GetStatements()) { + stmt->Accept(*this); + } + + LeaveBody(); +} + +void LintVisitor::Visit(ExprStmt& node) { + if (opts_.warn_pure_expr_stmt) { + if (auto* e = node.MutableExpression()) { + if (IsPureExpr(*e)) { + sink_.Warn("W0401", "expression statement has no effect"); + } + } + } + WalkVisitor::Visit(node); +} + +void LintVisitor::Visit(ReturnStmt& node) { + WalkVisitor::Visit(node); +} + +void LintVisitor::Visit(BreakStmt& node) { + if (opts_.warn_break_continue_outside_loop && loop_depth_ == 0) { + sink_.Error("E0301", "break outside of loop"); + } + WalkVisitor::Visit(node); +} + +void LintVisitor::Visit(ContinueStmt& node) { + if (opts_.warn_break_continue_outside_loop && loop_depth_ == 0) { + sink_.Error("E0302", "continue outside of loop"); + } + + WalkVisitor::Visit(node); +} + +void LintVisitor::Visit(IfStmt& node) { + EnterBody(); + + if (opts_.warn_if_without_branches && node.MutableBranches().empty() && !node.HasElse()) { + sink_.Warn("W0501", "if statement has no branches"); + } + + for (auto& br : node.MutableBranches()) { + if (auto* then_blk = br.MutableThen()) { + if (opts_.warn_empty_blocks && then_blk->GetStatements().empty()) { + sink_.Warn("W0502", "then-branch is empty"); + } + } + } + + if (opts_.warn_empty_else && node.HasElse()) { + if (auto* eb = node.MutableElseBlock()) { + if (eb->GetStatements().empty()) { + sink_.Warn("W0503", "else-branch is empty"); + } + } + } + + WalkVisitor::Visit(node); + + LeaveBody(); +} + +void LintVisitor::Visit(WhileStmt& node) { + EnterBody(); + + if (opts_.warn_missing_loop_cond_or_iterable && node.MutableCondition() == nullptr) { + sink_.Error("E0401", "while loop without condition"); + } + + if (opts_.warn_while_true) { + if (auto* cond = node.MutableCondition()) { + if (auto* bl = dynamic_cast(cond)) { + if (bl->Value()) { + sink_.Warn("W0601", "while(true) loop"); + } + } + } + } + + EnterLoop(); + WalkVisitor::Visit(node); + LeaveLoop(); + + LeaveBody(); +} + +void LintVisitor::Visit(ForStmt& node) { + EnterBody(); + + if (opts_.warn_missing_loop_cond_or_iterable && node.MutableIteratorExpr() == nullptr) { + sink_.Error("E0402", "for loop without iterable expression"); + } + + EnterLoop(); + WalkVisitor::Visit(node); + LeaveLoop(); + + LeaveBody(); +} + +void LintVisitor::Visit(UnsafeBlock& node) { + EnterBody(); + if (opts_.warn_empty_blocks) { + if (auto* b = node.MutableBody()) { + if (b->GetStatements().empty()) { + sink_.Warn("W0701", "empty unsafe block"); + } + } + } + + WalkVisitor::Visit(node); + LeaveBody(); +} + +void LintVisitor::Visit(GlobalVarDecl& node) { + if (opts_.warn_mutable_globals && node.IsVar()) { + sink_.Warn("W0801", "mutable global variable"); + } + + WalkVisitor::Visit(node); +} + +void LintVisitor::Visit(FieldDecl& node) { + if (opts_.warn_public_fields && node.IsPublic()) { + sink_.Warn("W0802", "public field"); + } + + WalkVisitor::Visit(node); +} + +void LintVisitor::Visit(StaticFieldDecl& node) { + if (opts_.warn_static_mutable_fields && node.IsVar()) { + sink_.Warn("W0803", "static mutable field"); + } + + WalkVisitor::Visit(node); +} + +void LintVisitor::Visit(StringLit& node) { + if (opts_.warn_empty_string_literal && node.Value().empty()) { + sink_.Warn("W0901", "empty string literal"); + } + + WalkVisitor::Visit(node); +} + +void LintVisitor::Visit(BoolLit& node) { + (void) node; + WalkVisitor::Visit(node); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/visitors/LintVisitor.hpp b/lib/parser/ast/visitors/LintVisitor.hpp new file mode 100644 index 0000000..22cd71c --- /dev/null +++ b/lib/parser/ast/visitors/LintVisitor.hpp @@ -0,0 +1,80 @@ +#ifndef PARSER_LINTVISITOR_HPP_ +#define PARSER_LINTVISITOR_HPP_ + +#include +#include + +#include "WalkVisitor.hpp" + +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" + +namespace ovum::compiler::parser { + +struct LintOptions { + std::size_t max_block_len = 200; + std::size_t max_nesting = 4; + std::size_t max_class_members = 64; + bool warn_empty_blocks = true; + bool warn_public_fields = true; + bool warn_mutable_globals = true; + bool warn_static_mutable_fields = true; + bool warn_unreachable = true; + bool warn_pure_expr_stmt = true; + bool warn_break_continue_outside_loop = true; + bool warn_empty_bodies = true; + bool warn_empty_else = true; + bool warn_missing_loop_cond_or_iterable = true; + bool warn_module_without_decls = true; + bool warn_empty_string_literal = true; + bool warn_deep_nesting = true; + bool warn_large_class = true; + bool warn_if_without_branches = true; + bool warn_while_true = true; +}; + +class LintVisitor : public WalkVisitor { +public: + explicit LintVisitor(IDiagnosticSink& sink, LintOptions options = {}) : sink_(sink), opts_(options) { + } + + void Visit(Module& node) override; + void Visit(ClassDecl& node) override; + void Visit(FunctionDecl& node) override; + void Visit(MethodDecl& node) override; + void Visit(CallDecl& node) override; + void Visit(DestructorDecl& node) override; + + void Visit(Block& node) override; + void Visit(ExprStmt& node) override; + void Visit(ReturnStmt& node) override; + void Visit(BreakStmt& node) override; + void Visit(ContinueStmt& node) override; + void Visit(IfStmt& node) override; + void Visit(WhileStmt& node) override; + void Visit(ForStmt& node) override; + void Visit(UnsafeBlock& node) override; + + void Visit(GlobalVarDecl& node) override; + void Visit(FieldDecl& node) override; + void Visit(StaticFieldDecl& node) override; + + void Visit(StringLit& node) override; + void Visit(BoolLit& node) override; + +private: + bool IsPureExpr(Expr& expression) const; + void EnterBody(); + void LeaveBody(); + void EnterLoop(); + void LeaveLoop(); + void CheckNestingDepth(const SourceSpan& where_hint = {}) const; + + IDiagnosticSink& sink_; + LintOptions opts_; + std::size_t loop_depth_ = 0; + std::size_t nesting_depth_ = 0; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_LINTVISITOR_HPP_ diff --git a/lib/parser/ast/visitors/PrintVisitor.cpp b/lib/parser/ast/visitors/PrintVisitor.cpp new file mode 100644 index 0000000..9f47964 --- /dev/null +++ b/lib/parser/ast/visitors/PrintVisitor.cpp @@ -0,0 +1,421 @@ +#include "PrintVisitor.hpp" + +#include "lib/parser/ast/nodes/class_members/CallDecl.hpp" +#include "lib/parser/ast/nodes/class_members/DestructorDecl.hpp" +#include "lib/parser/ast/nodes/class_members/FieldDecl.hpp" +#include "lib/parser/ast/nodes/class_members/MethodDecl.hpp" +#include "lib/parser/ast/nodes/class_members/StaticFieldDecl.hpp" + +#include "lib/parser/ast/nodes/decls/ClassDecl.hpp" +#include "lib/parser/ast/nodes/decls/FunctionDecl.hpp" +#include "lib/parser/ast/nodes/decls/GlobalVarDecl.hpp" +#include "lib/parser/ast/nodes/decls/InterfaceDecl.hpp" +#include "lib/parser/ast/nodes/decls/InterfaceMethod.hpp" +#include "lib/parser/ast/nodes/decls/Module.hpp" +#include "lib/parser/ast/nodes/decls/TypeAliasDecl.hpp" + +#include "lib/parser/ast/nodes/stmts/Block.hpp" + +namespace ovum::compiler::parser { + +PrintVisitor::PrintVisitor(std::ostream& output) : out_(&output) { +} + +PrintVisitor::PrintVisitor() : out_(&buffer_), use_buffer_(true) { +} + +std::string PrintVisitor::Str() const { + return buffer_.str(); +} + +void PrintVisitor::WriteIndent() { + for (int i = 0; i < indent_; ++i) { + *out_ << " "; + } +} + +void PrintVisitor::WriteLine(const std::string& text) { + WriteIndent(); + *out_ << text << '\n'; +} + +void PrintVisitor::Open(const std::string& header) { + WriteLine(header + " {"); + ++indent_; +} + +void PrintVisitor::Close() { + --indent_; + WriteLine("}"); +} + +void PrintVisitor::Visit(Module& node) { + Open("Module name=\"" + node.Name() + "\""); + for (auto& declaration : node.MutableDecls()) { + declaration->Accept(*this); + } + Close(); +} + +void PrintVisitor::Visit(FunctionDecl& node) { + Open(std::string("FunctionDecl name=\"") + node.Name() + "\"" + (node.IsPure() ? " pure" : "")); + if (auto* body = node.MutableBody()) { + body->Accept(*this); + } + Close(); +} + +void PrintVisitor::Visit(ClassDecl& node) { + Open(std::string("ClassDecl name=\"") + node.Name() + "\""); + for (auto& member : node.MutableMembers()) { + member->Accept(*this); + } + Close(); +} + +void PrintVisitor::Visit(InterfaceMethod& node) { + WriteLine(std::string("InterfaceMethod name=\"") + node.Name() + "\""); +} + +void PrintVisitor::Visit(InterfaceDecl& node) { + Open(std::string("InterfaceDecl name=\"") + node.Name() + "\""); + for (auto& method_ptr : node.MutableMembers()) { + method_ptr->Accept(*this); + } + Close(); +} + +void PrintVisitor::Visit(TypeAliasDecl& node) { + WriteLine(std::string("TypeAliasDecl name=\"") + node.Name() + "\""); +} + +void PrintVisitor::Visit(GlobalVarDecl& node) { + Open(std::string("GlobalVarDecl ") + (node.IsVar() ? "var" : "val") + " name=\"" + node.Name() + "\""); + if (auto* init_expr = node.MutableInit()) { + init_expr->Accept(*this); + } + + Close(); +} + +void PrintVisitor::Visit(FieldDecl& node) { + Open(std::string("FieldDecl ") + (node.IsVar() ? "var" : "val") + " name=\"" + node.Name() + "\"" + + (node.IsPublic() ? " public" : "")); + if (auto* init_expr = node.MutableInit()) { + init_expr->Accept(*this); + } + + Close(); +} + +void PrintVisitor::Visit(StaticFieldDecl& node) { + Open(std::string("StaticFieldDecl ") + (node.IsVar() ? "var" : "val") + " name=\"" + node.Name() + "\"" + + (node.IsPublic() ? " public" : "")); + if (auto* init_expr = node.MutableInit()) { + init_expr->Accept(*this); + } + + Close(); +} + +void PrintVisitor::Visit(MethodDecl& node) { + std::string flags; + if (node.IsPublic()) { + flags += " public"; + } + + if (node.IsStatic()) { + flags += " static"; + } + + if (node.IsOverride()) { + flags += " override"; + } + + if (node.IsPure()) { + flags += " pure"; + } + + Open("MethodDecl name=\"" + node.Name() + "\"" + flags); + if (auto* body = node.MutableBody()) { + body->Accept(*this); + } + + Close(); +} + +void PrintVisitor::Visit(CallDecl& node) { + Open(std::string("CallDecl") + (node.IsPublic() ? " public" : "")); + + if (auto* body = node.MutableBody()) { + body->Accept(*this); + } + + Close(); +} + +void PrintVisitor::Visit(DestructorDecl& node) { + Open(std::string("DestructorDecl") + (node.IsPublic() ? " public" : "")); + + if (auto* body = node.MutableBody()) { + body->Accept(*this); + } + + Close(); +} + +void PrintVisitor::Visit(Block& node) { + Open("Block"); + + for (auto& statement : node.GetStatements()) { + statement->Accept(*this); + } + + Close(); +} + +void PrintVisitor::Visit(VarDeclStmt& node) { + Open(std::string("VarDeclStmt ") + (node.IsVar() ? "var" : "val") + " name=\"" + node.Name() + "\""); + + if (auto* init_expr = node.MutableInit()) { + init_expr->Accept(*this); + } + + Close(); +} + +void PrintVisitor::Visit(ExprStmt& node) { + Open("ExprStmt"); + + if (auto* expression = node.MutableExpression()) { + expression->Accept(*this); + } + + Close(); +} + +void PrintVisitor::Visit(ReturnStmt& node) { + if (!node.HasValue()) { + WriteLine("ReturnStmt"); + return; + } + + Open("ReturnStmt"); + if (auto* value_expr = node.MutableValue()) { + value_expr->Accept(*this); + } + + Close(); +} + +void PrintVisitor::Visit(BreakStmt& node) { + WriteLine("BreakStmt"); + (void) node; +} + +void PrintVisitor::Visit(ContinueStmt& node) { + WriteLine("ContinueStmt"); + (void) node; +} + +void PrintVisitor::Visit(IfStmt& node) { + Open("IfStmt"); + for (auto& branch_value : node.MutableBranches()) { + Open("Branch"); + + if (auto* cond_expr = branch_value.MutableCondition()) { + Open("Condition"); + cond_expr->Accept(*this); + Close(); + } + + if (auto* then_block = branch_value.MutableThen()) { + Open("Then"); + then_block->Accept(*this); + Close(); + } + + Close(); + } + + if (auto* else_block = node.MutableElseBlock()) { + Open("Else"); + else_block->Accept(*this); + Close(); + } + + Close(); +} + +void PrintVisitor::Visit(WhileStmt& node) { + Open("WhileStmt"); + + if (auto* cond_expr = node.MutableCondition()) { + Open("Condition"); + cond_expr->Accept(*this); + Close(); + } + + if (auto* body_block = node.MutableBody()) { + Open("Body"); + body_block->Accept(*this); + Close(); + } + + Close(); +} + +void PrintVisitor::Visit(ForStmt& node) { + Open(std::string("ForStmt iterator=\"") + node.IteratorName() + "\""); + + if (auto* iter_expr = node.MutableIteratorExpr()) { + Open("Iterable"); + iter_expr->Accept(*this); + Close(); + } + + if (auto* body_block = node.MutableBody()) { + Open("Body"); + body_block->Accept(*this); + Close(); + } + + Close(); +} + +void PrintVisitor::Visit(UnsafeBlock& node) { + Open("UnsafeBlock"); + + if (auto* body_block = node.MutableBody()) { + body_block->Accept(*this); + } + + Close(); +} + +void PrintVisitor::Visit(Binary& node) { + Open(std::string("Binary op=\"") + std::string(node.Op().Name()) + "\""); + node.MutableLhs().Accept(*this); + node.MutableRhs().Accept(*this); + Close(); +} + +void PrintVisitor::Visit(Unary& node) { + Open(std::string("Unary op=\"") + std::string(node.Op().Name()) + "\""); + node.MutableOperand().Accept(*this); + Close(); +} + +void PrintVisitor::Visit(Assign& node) { + Open(std::string("Assign kind=\"") + std::string(node.Kind().Name()) + "\""); + Open("Target"); + node.MutableTarget().Accept(*this); + Close(); + Open("Value"); + node.MutableValue().Accept(*this); + Close(); + Close(); +} + +void PrintVisitor::Visit(Call& node) { + Open("Call"); + Open("Callee"); + node.MutableCallee().Accept(*this); + Close(); + Open("Args"); + + for (auto& argument : node.MutableArgs()) { + argument->Accept(*this); + } + + Close(); + Close(); +} + +void PrintVisitor::Visit(FieldAccess& node) { + Open(std::string("FieldAccess name=\"") + node.Name() + "\""); + node.MutableObject().Accept(*this); + Close(); +} + +void PrintVisitor::Visit(IndexAccess& node) { + Open("IndexAccess"); + Open("Object"); + node.MutableObject().Accept(*this); + Close(); + Open("Index"); + node.MutableIndexExpr().Accept(*this); + Close(); + Close(); +} + +void PrintVisitor::Visit(NamespaceRef& node) { + Open(std::string("NamespaceRef name=\"") + node.Name() + "\""); + node.MutableNamespaceExpr().Accept(*this); + Close(); +} + +void PrintVisitor::Visit(SafeCall& node) { + Open(std::string("SafeCall method=\"") + node.Method() + "\""); + Open("Object"); + node.MutableObject().Accept(*this); + Close(); + Open("Args"); + + for (auto& argument : node.MutableArgs()) { + argument->Accept(*this); + } + + Close(); + Close(); +} + +void PrintVisitor::Visit(Elvis& node) { + Open("Elvis"); + node.MutableLhs().Accept(*this); + node.MutableRhs().Accept(*this); + Close(); +} + +void PrintVisitor::Visit(CastAs& node) { + Open("CastAs"); + node.MutableExpression().Accept(*this); + Close(); +} + +void PrintVisitor::Visit(TypeTestIs& node) { + Open("TypeTestIs"); + node.MutableExpression().Accept(*this); + Close(); +} + +void PrintVisitor::Visit(IdentRef& node) { + WriteLine(std::string("IdentRef name=\"") + node.Name() + "\""); +} + +void PrintVisitor::Visit(IntLit& node) { + WriteLine("IntLit value=" + std::to_string(node.Value())); +} + +void PrintVisitor::Visit(FloatLit& node) { + WriteLine("FloatLit value=" + std::to_string(node.Value())); +} + +void PrintVisitor::Visit(StringLit& node) { + WriteLine(std::string("StringLit value=\"") + node.Value() + "\""); +} + +void PrintVisitor::Visit(CharLit& node) { + std::string printable(1, node.Value()); + WriteLine(std::string("CharLit value='") + printable + "'"); +} + +void PrintVisitor::Visit(BoolLit& node) { + WriteLine(std::string("BoolLit value=") + (node.Value() ? "true" : "false")); +} + +void PrintVisitor::Visit(NullLit& node) { + WriteLine("NullLit"); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/visitors/PrintVisitor.hpp b/lib/parser/ast/visitors/PrintVisitor.hpp new file mode 100644 index 0000000..fef2fce --- /dev/null +++ b/lib/parser/ast/visitors/PrintVisitor.hpp @@ -0,0 +1,78 @@ +#ifndef PARSER_PRINTVISITOR_HPP_ +#define PARSER_PRINTVISITOR_HPP_ + +#include +#include +#include + +#include "lib/parser/ast/AstVisitor.hpp" + +namespace ovum::compiler::parser { + +class PrintVisitor : public AstVisitor { +public: + explicit PrintVisitor(std::ostream& output); + PrintVisitor(); + + ~PrintVisitor() override = default; + + std::string Str() const; + + void Visit(Module& node) override; + void Visit(FunctionDecl& node) override; + void Visit(ClassDecl& node) override; + void Visit(InterfaceMethod& node) override; + void Visit(InterfaceDecl& node) override; + void Visit(TypeAliasDecl& node) override; + void Visit(GlobalVarDecl& node) override; + void Visit(FieldDecl& node) override; + void Visit(StaticFieldDecl& node) override; + void Visit(MethodDecl& node) override; + void Visit(CallDecl& node) override; + void Visit(DestructorDecl& node) override; + + void Visit(Block& node) override; + void Visit(VarDeclStmt& node) override; + void Visit(ExprStmt& node) override; + void Visit(ReturnStmt& node) override; + void Visit(BreakStmt& node) override; + void Visit(ContinueStmt& node) override; + void Visit(IfStmt& node) override; + void Visit(WhileStmt& node) override; + void Visit(ForStmt& node) override; + void Visit(UnsafeBlock& node) override; + + void Visit(Binary& node) override; + void Visit(Unary& node) override; + void Visit(Assign& node) override; + void Visit(Call& node) override; + void Visit(FieldAccess& node) override; + void Visit(IndexAccess& node) override; + void Visit(NamespaceRef& node) override; + void Visit(SafeCall& node) override; + void Visit(Elvis& node) override; + void Visit(CastAs& node) override; + void Visit(TypeTestIs& node) override; + void Visit(IdentRef& node) override; + void Visit(IntLit& node) override; + void Visit(FloatLit& node) override; + void Visit(StringLit& node) override; + void Visit(CharLit& node) override; + void Visit(BoolLit& node) override; + void Visit(NullLit& node) override; + +private: + void WriteIndent(); + void WriteLine(const std::string& text); + void Open(const std::string& header); + void Close(); + + std::ostream* out_ = nullptr; + std::ostringstream buffer_; + int indent_ = 0; + bool use_buffer_ = false; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_PRINTVISITOR_HPP_ diff --git a/lib/parser/ast/visitors/StructuralValidator.cpp b/lib/parser/ast/visitors/StructuralValidator.cpp new file mode 100644 index 0000000..0a9b5b4 --- /dev/null +++ b/lib/parser/ast/visitors/StructuralValidator.cpp @@ -0,0 +1,118 @@ +#include "StructuralValidator.hpp" + +#include "lib/parser/diagnostics/severity/Severity.hpp" + +#include "lib/parser/ast/nodes/class_members/CallDecl.hpp" +#include "lib/parser/ast/nodes/class_members/DestructorDecl.hpp" +#include "lib/parser/ast/nodes/class_members/MethodDecl.hpp" + +#include "lib/parser/ast/nodes/decls/ClassDecl.hpp" +#include "lib/parser/ast/nodes/decls/FunctionDecl.hpp" +#include "lib/parser/ast/nodes/decls/Module.hpp" + +#include "lib/parser/ast/nodes/exprs/Binary.hpp" +#include "lib/parser/ast/nodes/exprs/Call.hpp" +#include "lib/parser/ast/nodes/exprs/FieldAccess.hpp" +#include "lib/parser/ast/nodes/exprs/IndexAccess.hpp" +#include "lib/parser/ast/nodes/exprs/NamespaceRef.hpp" +#include "lib/parser/ast/nodes/exprs/SafeCall.hpp" +#include "lib/parser/ast/nodes/exprs/Unary.hpp" + +namespace ovum::compiler::parser { + +void StructuralValidator::Visit(Module& node) { + if (node.Name().empty()) { + sink_.Error("E0001", "module name must not be empty"); + } + + WalkVisitor::Visit(node); +} + +void StructuralValidator::Visit(FunctionDecl& node) { + if (!node.IsPure() && node.MutableBody() == nullptr) { + sink_.Error("E1001", "function must have a body"); + } + + WalkVisitor::Visit(node); +} + +void StructuralValidator::Visit(ClassDecl& node) { + if (node.Name().empty()) { + sink_.Error("E1002", "class name must not be empty"); + } + + WalkVisitor::Visit(node); +} + +void StructuralValidator::Visit(CallDecl& node) { + if (node.MutableBody() == nullptr) { + sink_.Error("E1101", "call declaration must have a body"); + } + + WalkVisitor::Visit(node); +} + +void StructuralValidator::Visit(MethodDecl& node) { + if (!node.IsPure() && node.MutableBody() == nullptr) { + sink_.Error("E1201", "method must have a body"); + } + + WalkVisitor::Visit(node); +} + +void StructuralValidator::Visit(DestructorDecl& node) { + if (node.MutableBody() == nullptr) { + sink_.Error("E1301", "destructor must have a body"); + } + + WalkVisitor::Visit(node); +} + +void StructuralValidator::Visit(Call& node) { + if (&node.MutableCallee() == nullptr) { + sink_.Error("E2001", "call must have callee"); + } + + WalkVisitor::Visit(node); +} + +void StructuralValidator::Visit(Binary& node) { + (void) node.MutableLhs(); + (void) node.MutableRhs(); + WalkVisitor::Visit(node); +} + +void StructuralValidator::Visit(Unary& node) { + (void) node.MutableOperand(); + WalkVisitor::Visit(node); +} + +void StructuralValidator::Visit(FieldAccess& node) { + (void) node.MutableObject(); + WalkVisitor::Visit(node); +} + +void StructuralValidator::Visit(IndexAccess& node) { + (void) node.MutableObject(); + (void) node.MutableIndexExpr(); + WalkVisitor::Visit(node); +} + +void StructuralValidator::Visit(NamespaceRef& node) { + if (node.Name().empty()) { + sink_.Error("E2101", "namespace reference must have name"); + } + + WalkVisitor::Visit(node); +} + +void StructuralValidator::Visit(SafeCall& node) { + if (node.Method().empty()) { + sink_.Error("E2201", "safecall must have method name"); + } + + (void) node.MutableObject(); + WalkVisitor::Visit(node); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/visitors/StructuralValidator.hpp b/lib/parser/ast/visitors/StructuralValidator.hpp new file mode 100644 index 0000000..7079927 --- /dev/null +++ b/lib/parser/ast/visitors/StructuralValidator.hpp @@ -0,0 +1,35 @@ +#ifndef PARSER_STRUCTURALVALIDATOR_HPP_ +#define PARSER_STRUCTURALVALIDATOR_HPP_ + +#include "WalkVisitor.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" + +namespace ovum::compiler::parser { + +class StructuralValidator : public WalkVisitor { +public: + explicit StructuralValidator(IDiagnosticSink& sink) : sink_(sink) { + } + + void Visit(Module& node) override; + void Visit(FunctionDecl& node) override; + void Visit(ClassDecl& node) override; + void Visit(CallDecl& node) override; + void Visit(MethodDecl& node) override; + void Visit(DestructorDecl& node) override; + + void Visit(Call& node) override; + void Visit(Binary& node) override; + void Visit(Unary& node) override; + void Visit(FieldAccess& node) override; + void Visit(IndexAccess& node) override; + void Visit(NamespaceRef& node) override; + void Visit(SafeCall& node) override; + +private: + IDiagnosticSink& sink_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STRUCTURALVALIDATOR_HPP_ diff --git a/lib/parser/ast/visitors/WalkVisitor.cpp b/lib/parser/ast/visitors/WalkVisitor.cpp new file mode 100644 index 0000000..f9d4216 --- /dev/null +++ b/lib/parser/ast/visitors/WalkVisitor.cpp @@ -0,0 +1,274 @@ +#include "WalkVisitor.hpp" + +#include "lib/parser/ast/nodes/decls/ClassDecl.hpp" +#include "lib/parser/ast/nodes/decls/FunctionDecl.hpp" +#include "lib/parser/ast/nodes/decls/GlobalVarDecl.hpp" +#include "lib/parser/ast/nodes/decls/InterfaceDecl.hpp" +#include "lib/parser/ast/nodes/decls/InterfaceMethod.hpp" +#include "lib/parser/ast/nodes/decls/Module.hpp" +#include "lib/parser/ast/nodes/decls/TypeAliasDecl.hpp" + +#include "lib/parser/ast/nodes/class_members/CallDecl.hpp" +#include "lib/parser/ast/nodes/class_members/DestructorDecl.hpp" +#include "lib/parser/ast/nodes/class_members/FieldDecl.hpp" +#include "lib/parser/ast/nodes/class_members/MethodDecl.hpp" +#include "lib/parser/ast/nodes/class_members/StaticFieldDecl.hpp" + +#include "lib/parser/ast/nodes/exprs/Assign.hpp" +#include "lib/parser/ast/nodes/exprs/Binary.hpp" +#include "lib/parser/ast/nodes/exprs/Call.hpp" +#include "lib/parser/ast/nodes/exprs/CastAs.hpp" +#include "lib/parser/ast/nodes/exprs/Elvis.hpp" +#include "lib/parser/ast/nodes/exprs/FieldAccess.hpp" +#include "lib/parser/ast/nodes/exprs/IdentRef.hpp" +#include "lib/parser/ast/nodes/exprs/IndexAccess.hpp" +#include "lib/parser/ast/nodes/exprs/NamespaceRef.hpp" +#include "lib/parser/ast/nodes/exprs/SafeCall.hpp" +#include "lib/parser/ast/nodes/exprs/TypeTestIs.hpp" +#include "lib/parser/ast/nodes/exprs/Unary.hpp" + +#include "lib/parser/ast/nodes/exprs/literals/BoolLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/CharLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/FloatLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/IntLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/NullLit.hpp" +#include "lib/parser/ast/nodes/exprs/literals/StringLit.hpp" + +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/ast/nodes/stmts/BreakStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ContinueStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ExprStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ForStmt.hpp" +#include "lib/parser/ast/nodes/stmts/IfStmt.hpp" +#include "lib/parser/ast/nodes/stmts/ReturnStmt.hpp" +#include "lib/parser/ast/nodes/stmts/UnsafeBlock.hpp" +#include "lib/parser/ast/nodes/stmts/VarDeclStmt.hpp" +#include "lib/parser/ast/nodes/stmts/WhileStmt.hpp" + +namespace ovum::compiler::parser { + +void WalkVisitor::Visit(Module& node) { + for (auto& decl_ptr : node.MutableDecls()) { + decl_ptr->Accept(*this); + } +} + +void WalkVisitor::Visit(FunctionDecl& node) { + if (auto* body = node.MutableBody()) { + body->Accept(*this); + } +} + +void WalkVisitor::Visit(ClassDecl& node) { + for (auto& member : node.MutableMembers()) { + member->Accept(*this); + } +} + +void WalkVisitor::Visit(InterfaceMethod& node) { + (void) node; +} + +void WalkVisitor::Visit(InterfaceDecl& node) { + for (auto& m : node.MutableMembers()) { + m->Accept(*this); + } +} + +void WalkVisitor::Visit(TypeAliasDecl& node) { + (void) node; +} + +void WalkVisitor::Visit(GlobalVarDecl& node) { + if (auto* init = node.MutableInit()) { + init->Accept(*this); + } +} + +void WalkVisitor::Visit(FieldDecl& node) { + if (auto* init = node.MutableInit()) { + init->Accept(*this); + } +} + +void WalkVisitor::Visit(StaticFieldDecl& node) { + if (auto* init = node.MutableInit()) { + init->Accept(*this); + } +} + +void WalkVisitor::Visit(MethodDecl& node) { + if (auto* body = node.MutableBody()) { + body->Accept(*this); + } +} + +void WalkVisitor::Visit(CallDecl& node) { + if (auto* body = node.MutableBody()) { + body->Accept(*this); + } +} + +void WalkVisitor::Visit(DestructorDecl& node) { + if (auto* body = node.MutableBody()) { + body->Accept(*this); + } +} + +void WalkVisitor::Visit(Block& node) { + for (auto& stmt : node.GetStatements()) { + stmt->Accept(*this); + } +} + +void WalkVisitor::Visit(VarDeclStmt& node) { + if (auto* init = node.MutableInit()) { + init->Accept(*this); + } +} + +void WalkVisitor::Visit(ExprStmt& node) { + if (auto* e = node.MutableExpression()) { + e->Accept(*this); + } +} + +void WalkVisitor::Visit(ReturnStmt& node) { + if (auto* v = node.MutableValue()) { + v->Accept(*this); + } +} + +void WalkVisitor::Visit(BreakStmt& node) { + (void) node; +} + +void WalkVisitor::Visit(ContinueStmt& node) { + (void) node; +} + +void WalkVisitor::Visit(IfStmt& node) { + for (auto& br : node.MutableBranches()) { + if (auto* c = br.MutableCondition()) { + c->Accept(*this); + } + + if (auto* t = br.MutableThen()) { + t->Accept(*this); + } + } + + if (auto* eb = node.MutableElseBlock()) { + eb->Accept(*this); + } +} + +void WalkVisitor::Visit(WhileStmt& node) { + if (auto* c = node.MutableCondition()) { + c->Accept(*this); + } + + if (auto* b = node.MutableBody()) { + b->Accept(*this); + } +} + +void WalkVisitor::Visit(ForStmt& node) { + if (auto* it = node.MutableIteratorExpr()) { + it->Accept(*this); + } + + if (auto* b = node.MutableBody()) { + b->Accept(*this); + } +} + +void WalkVisitor::Visit(UnsafeBlock& node) { + if (auto* b = node.MutableBody()) { + b->Accept(*this); + } +} + +void WalkVisitor::Visit(Binary& node) { + node.MutableLhs().Accept(*this); + node.MutableRhs().Accept(*this); +} + +void WalkVisitor::Visit(Unary& node) { + node.MutableOperand().Accept(*this); +} + +void WalkVisitor::Visit(Assign& node) { + node.MutableTarget().Accept(*this); + node.MutableValue().Accept(*this); +} + +void WalkVisitor::Visit(Call& node) { + node.MutableCallee().Accept(*this); + for (auto& a : node.MutableArgs()) { + a->Accept(*this); + } +} + +void WalkVisitor::Visit(FieldAccess& node) { + node.MutableObject().Accept(*this); +} + +void WalkVisitor::Visit(IndexAccess& node) { + node.MutableObject().Accept(*this); + node.MutableIndexExpr().Accept(*this); +} + +void WalkVisitor::Visit(NamespaceRef& node) { + node.MutableNamespaceExpr().Accept(*this); +} + +void WalkVisitor::Visit(SafeCall& node) { + node.MutableObject().Accept(*this); + for (auto& a : node.MutableArgs()) { + a->Accept(*this); + } +} + +void WalkVisitor::Visit(Elvis& node) { + node.MutableLhs().Accept(*this); + node.MutableRhs().Accept(*this); +} + +void WalkVisitor::Visit(CastAs& node) { + node.MutableExpression().Accept(*this); +} + +void WalkVisitor::Visit(TypeTestIs& node) { + node.MutableExpression().Accept(*this); +} + +void WalkVisitor::Visit(IdentRef& node) { + (void) node; +} + +void WalkVisitor::Visit(IntLit& node) { + (void) node; +} + +void WalkVisitor::Visit(FloatLit& node) { + (void) node; +} + +void WalkVisitor::Visit(StringLit& node) { + (void) node; +} + +void WalkVisitor::Visit(CharLit& node) { + (void) node; +} + +void WalkVisitor::Visit(BoolLit& node) { + (void) node; +} + +void WalkVisitor::Visit(NullLit& node) { + (void) node; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/ast/visitors/WalkVisitor.hpp b/lib/parser/ast/visitors/WalkVisitor.hpp new file mode 100644 index 0000000..448217a --- /dev/null +++ b/lib/parser/ast/visitors/WalkVisitor.hpp @@ -0,0 +1,58 @@ +#ifndef PARSER_WALKVISITOR_HPP_ +#define PARSER_WALKVISITOR_HPP_ + +#include "lib/parser/ast/AstVisitor.hpp" + +namespace ovum::compiler::parser { + +class WalkVisitor : public AstVisitor { +public: + ~WalkVisitor() override = default; + + void Visit(Module& node) override; + void Visit(FunctionDecl& node) override; + void Visit(ClassDecl& node) override; + void Visit(InterfaceMethod& node) override; + void Visit(InterfaceDecl& node) override; + void Visit(TypeAliasDecl& node) override; + void Visit(GlobalVarDecl& node) override; + void Visit(FieldDecl& node) override; + void Visit(StaticFieldDecl& node) override; + void Visit(MethodDecl& node) override; + void Visit(CallDecl& node) override; + void Visit(DestructorDecl& node) override; + + void Visit(Block& node) override; + void Visit(VarDeclStmt& node) override; + void Visit(ExprStmt& node) override; + void Visit(ReturnStmt& node) override; + void Visit(BreakStmt& node) override; + void Visit(ContinueStmt& node) override; + void Visit(IfStmt& node) override; + void Visit(WhileStmt& node) override; + void Visit(ForStmt& node) override; + void Visit(UnsafeBlock& node) override; + + void Visit(Binary& node) override; + void Visit(Unary& node) override; + void Visit(Assign& node) override; + void Visit(Call& node) override; + void Visit(FieldAccess& node) override; + void Visit(IndexAccess& node) override; + void Visit(NamespaceRef& node) override; + void Visit(SafeCall& node) override; + void Visit(Elvis& node) override; + void Visit(CastAs& node) override; + void Visit(TypeTestIs& node) override; + void Visit(IdentRef& node) override; + void Visit(IntLit& node) override; + void Visit(FloatLit& node) override; + void Visit(StringLit& node) override; + void Visit(CharLit& node) override; + void Visit(BoolLit& node) override; + void Visit(NullLit& node) override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_WALKVISITOR_HPP_ diff --git a/lib/parser/context/ContextParser.cpp b/lib/parser/context/ContextParser.cpp new file mode 100644 index 0000000..c205a19 --- /dev/null +++ b/lib/parser/context/ContextParser.cpp @@ -0,0 +1,90 @@ +#include "ContextParser.hpp" + +#include + +namespace ovum::compiler::parser { + +std::vector& ContextParser::StateStack() { + return state_stack_; +} + +std::vector& ContextParser::NodeStack() { + return node_stack_; +} + +void ContextParser::SetDiagnostics(IDiagnosticSink* diagnostics) { + diags_ = diagnostics; +} + +IDiagnosticSink* ContextParser::Diags() const { + return diags_; +} + +void ContextParser::SetExpr(IExpressionParser* parser) { + expr_ = parser; +} + +IExpressionParser* ContextParser::Expr() const { + return expr_; +} + +void ContextParser::SetTypeParser(ITypeParser* parser) { + typep_ = parser; +} + +ITypeParser* ContextParser::TypeParser() const { + return typep_; +} + +void ContextParser::PushState(const IState& state) { + state_stack_.push_back(&state); +} + +void ContextParser::PopState() { + if (!state_stack_.empty()) { + state_stack_.pop_back(); + } +} + +const IState* ContextParser::CurrentState() const { + if (state_stack_.empty()) { + return nullptr; + } + return state_stack_.back(); +} + +void ContextParser::PushNode(std::unique_ptr node) { + node_stack_.emplace_back(std::move(node)); +} + +std::unique_ptr ContextParser::PopNode() { + if (node_stack_.empty()) { + return nullptr; + } + auto node = node_stack_.back().ReleaseNode(); + node_stack_.pop_back(); + return node; +} + +bool ContextParser::HasStates() const noexcept { + return !state_stack_.empty(); +} + +bool ContextParser::HasNodes() const noexcept { + return !node_stack_.empty(); +} + +void ContextParser::Clear() { + state_stack_.clear(); + node_stack_.clear(); +} + +void ContextParser::SetFactory(IAstFactory* factory) { + factory_ = factory; +} + +IAstFactory* ContextParser::Factory() const { + return factory_; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/context/ContextParser.hpp b/lib/parser/context/ContextParser.hpp new file mode 100644 index 0000000..095ce9d --- /dev/null +++ b/lib/parser/context/ContextParser.hpp @@ -0,0 +1,70 @@ +#ifndef PARSER_CONTEXTPARSER_HPP_ +#define PARSER_CONTEXTPARSER_HPP_ + +#include +#include +#include + +#include "NodeEntry.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/IState.hpp" + +namespace ovum::compiler::parser { + +class AstNode; +class IExpressionParser; +class ITypeParser; +class IAstFactory; // forward + +template +concept AstNodeDerived = std::is_base_of_v; + +class ContextParser { +public: + std::vector& StateStack(); + std::vector& NodeStack(); + + void SetDiagnostics(IDiagnosticSink* diagnostics); + IDiagnosticSink* Diags() const; + + void SetExpr(IExpressionParser* parser); + IExpressionParser* Expr() const; + + void SetTypeParser(ITypeParser* parser); + ITypeParser* TypeParser() const; + + void SetFactory(IAstFactory* factory); + IAstFactory* Factory() const; + + void PushState(const IState& state); + void PopState(); + const IState* CurrentState() const; + + template + T* TopNodeAs() { + if (node_stack_.empty()) { + return nullptr; + } + + return dynamic_cast(node_stack_.back().MutableNode()); + } + + void PushNode(std::unique_ptr node); + std::unique_ptr PopNode(); + + bool HasStates() const noexcept; + bool HasNodes() const noexcept; + void Clear(); + +private: + std::vector state_stack_; + std::vector node_stack_; + IDiagnosticSink* diags_ = nullptr; + IExpressionParser* expr_ = nullptr; + ITypeParser* typep_ = nullptr; + IAstFactory* factory_ = nullptr; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_CONTEXTPARSER_HPP_ diff --git a/lib/parser/context/NodeEntry.cpp b/lib/parser/context/NodeEntry.cpp new file mode 100644 index 0000000..659e4d7 --- /dev/null +++ b/lib/parser/context/NodeEntry.cpp @@ -0,0 +1,26 @@ +#include "NodeEntry.hpp" + +#include + +namespace ovum::compiler::parser { + +NodeEntry::NodeEntry(std::unique_ptr node) : node_(std::move(node)) { +} + +const AstNode* NodeEntry::GetNode() const noexcept { + return node_.get(); +} + +AstNode* NodeEntry::MutableNode() noexcept { + return node_.get(); +} + +void NodeEntry::SetNode(std::unique_ptr node) { + node_ = std::move(node); +} + +std::unique_ptr NodeEntry::ReleaseNode() { + return std::move(node_); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/context/NodeEntry.hpp b/lib/parser/context/NodeEntry.hpp new file mode 100644 index 0000000..0261c30 --- /dev/null +++ b/lib/parser/context/NodeEntry.hpp @@ -0,0 +1,26 @@ +#ifndef PARSER_NODEENTRY_HPP_ +#define PARSER_NODEENTRY_HPP_ + +#include + +#include "lib/parser/ast/nodes/base/AstNode.hpp" + +namespace ovum::compiler::parser { + +class NodeEntry { +public: + NodeEntry() = default; + explicit NodeEntry(std::unique_ptr node); + + const AstNode* GetNode() const noexcept; + AstNode* MutableNode() noexcept; + void SetNode(std::unique_ptr node); + std::unique_ptr ReleaseNode(); + +private: + std::unique_ptr node_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_NODEENTRY_HPP_ diff --git a/lib/parser/diagnostics/Diagnostic.cpp b/lib/parser/diagnostics/Diagnostic.cpp new file mode 100644 index 0000000..21bdf3f --- /dev/null +++ b/lib/parser/diagnostics/Diagnostic.cpp @@ -0,0 +1,129 @@ +#include "Diagnostic.hpp" + +#include + +namespace ovum::compiler::parser { + +Diagnostic::Diagnostic() = default; + +Diagnostic::Diagnostic(std::shared_ptr sev, std::string code, std::string message) : + severity_(std::move(sev)), code_(std::move(code)), message_(std::move(message)) { +} + +Diagnostic::Diagnostic(const Diagnostic& other) = default; + +Diagnostic::Diagnostic(Diagnostic&& other) noexcept : + severity_(std::move(other.severity_)), code_(std::move(other.code_)), message_(std::move(other.message_)), + category_(std::move(other.category_)), where_(std::move(other.where_)), notes_(std::move(other.notes_)), + fixes_(std::move(other.fixes_)), is_fatal_(other.is_fatal_), is_suppressed_(other.is_suppressed_) { +} + +Diagnostic& Diagnostic::operator=(const Diagnostic& other) { + if (this == &other) { + return *this; + } + + severity_ = other.severity_; + code_ = other.code_; + message_ = other.message_; + category_ = other.category_; + where_ = other.where_; + notes_ = other.notes_; + fixes_ = other.fixes_; + is_fatal_ = other.is_fatal_; + is_suppressed_ = other.is_suppressed_; + return *this; +} + +Diagnostic& Diagnostic::operator=(Diagnostic&& other) noexcept { + if (this == &other) { + return *this; + } + + severity_ = std::move(other.severity_); + code_ = std::move(other.code_); + message_ = std::move(other.message_); + category_ = std::move(other.category_); + where_ = std::move(other.where_); + notes_ = std::move(other.notes_); + fixes_ = std::move(other.fixes_); + is_fatal_ = other.is_fatal_; + is_suppressed_ = other.is_suppressed_; + return *this; +} + +Diagnostic::~Diagnostic() = default; + +void Diagnostic::SetSeverity(std::shared_ptr sev) { + severity_ = std::move(sev); +} +const std::shared_ptr& Diagnostic::GetSeverity() const noexcept { + return severity_; +} + +void Diagnostic::SetCode(std::string c) { + code_ = std::move(c); +} +const std::string& Diagnostic::GetCode() const noexcept { + return code_; +} + +void Diagnostic::SetMessage(std::string m) { + message_ = std::move(m); +} +const std::string& Diagnostic::GetMessage() const noexcept { + return message_; +} + +void Diagnostic::SetCategory(std::string cat) { + category_ = std::move(cat); +} +const std::string& Diagnostic::GetCategory() const noexcept { + return category_; +} + +void Diagnostic::SetWhere(SourceSpan sp) { + where_ = std::move(sp); +} +void Diagnostic::ResetWhere() { + where_.reset(); +} +const std::optional& Diagnostic::GetWhere() const noexcept { + return where_; +} + +void Diagnostic::AddNote(RelatedInfo note) { + notes_.emplace_back(std::move(note)); +} +void Diagnostic::ClearNotes() { + notes_.clear(); +} +const std::vector& Diagnostic::GetNotes() const noexcept { + return notes_; +} + +void Diagnostic::AddFix(FixIt fix) { + fixes_.emplace_back(std::move(fix)); +} +void Diagnostic::ClearFixes() { + fixes_.clear(); +} +const std::vector& Diagnostic::GetFixes() const noexcept { + return fixes_; +} + +void Diagnostic::SetFatal(bool on) { + is_fatal_ = on; +} +bool Diagnostic::IsFatal() const noexcept { + return is_fatal_; +} + +void Diagnostic::SetSuppressed(bool on) { + is_suppressed_ = on; +} +bool Diagnostic::IsSuppressed() const noexcept { + return is_suppressed_; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/diagnostics/Diagnostic.hpp b/lib/parser/diagnostics/Diagnostic.hpp new file mode 100644 index 0000000..d4b766d --- /dev/null +++ b/lib/parser/diagnostics/Diagnostic.hpp @@ -0,0 +1,70 @@ +#ifndef PARSER_DIAGNOSTIC_HPP_ +#define PARSER_DIAGNOSTIC_HPP_ + +#include +#include +#include +#include + +#include "FixIt.hpp" +#include "RelatedInfo.hpp" +#include "lib/parser/tokens/SourceSpan.hpp" +#include "severity/ISeverity.hpp" + +namespace ovum::compiler::parser { + +class Diagnostic { +public: + Diagnostic(); + Diagnostic(std::shared_ptr sev, std::string code, std::string message); + Diagnostic(const Diagnostic& other); + Diagnostic(Diagnostic&& other) noexcept; + Diagnostic& operator=(const Diagnostic& other); + Diagnostic& operator=(Diagnostic&& other) noexcept; + ~Diagnostic(); + + void SetSeverity(std::shared_ptr sev); + [[nodiscard]] const std::shared_ptr& GetSeverity() const noexcept; + + void SetCode(std::string c); + [[nodiscard]] const std::string& GetCode() const noexcept; + + void SetMessage(std::string m); + [[nodiscard]] const std::string& GetMessage() const noexcept; + + void SetCategory(std::string cat); + [[nodiscard]] const std::string& GetCategory() const noexcept; + + void SetWhere(SourceSpan sp); + void ResetWhere(); + [[nodiscard]] const std::optional& GetWhere() const noexcept; + + void AddNote(RelatedInfo note); + void ClearNotes(); + [[nodiscard]] const std::vector& GetNotes() const noexcept; + + void AddFix(FixIt fix); + void ClearFixes(); + [[nodiscard]] const std::vector& GetFixes() const noexcept; + + void SetFatal(bool on); + [[nodiscard]] bool IsFatal() const noexcept; + + void SetSuppressed(bool on); + [[nodiscard]] bool IsSuppressed() const noexcept; + +private: + std::shared_ptr severity_; + std::string code_; + std::string message_; + std::string category_; + std::optional where_; + std::vector notes_; + std::vector fixes_; + bool is_fatal_ = false; + bool is_suppressed_ = false; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_DIAGNOSTIC_HPP_ diff --git a/lib/parser/diagnostics/DiagnosticCollector.cpp b/lib/parser/diagnostics/DiagnosticCollector.cpp new file mode 100644 index 0000000..43991ff --- /dev/null +++ b/lib/parser/diagnostics/DiagnosticCollector.cpp @@ -0,0 +1,158 @@ +#include "lib/parser/diagnostics/DiagnosticCollector.hpp" + +#include + +#include "lib/parser/diagnostics/severity/Severity.hpp" + +namespace ovum::compiler::parser { + +void DiagnosticCollector::Report(Diagnostic d) { + bool suppressed = !ShouldKeep(d); + d.SetSuppressed(suppressed); + if (suppressed) { + return; + } + + if (dedup_ && IsDuplicate(d)) { + return; + } + + if (capacity_ && diags_.size() >= *capacity_) { + return; + } + + int err_level = Severity::Error()->Level(); + int warn_level = Severity::Warning()->Level(); + int level = d.GetSeverity() ? d.GetSeverity()->Level() : 0; + + if (level >= err_level) { + if (error_limit_ && errors_ >= *error_limit_) { + return; + } + + ++errors_; + } else if (level >= warn_level) { + if (warning_limit_ && warnings_ >= *warning_limit_) { + return; + } + + ++warnings_; + } + + diags_.emplace_back(std::move(d)); +} + +bool DiagnosticCollector::HasErrors() const { + return errors_ > 0; +} +std::size_t DiagnosticCollector::Count() const { + return diags_.size(); +} +std::size_t DiagnosticCollector::ErrorCount() const { + return errors_; +} +std::size_t DiagnosticCollector::WarningCount() const { + return warnings_; +} + +void DiagnosticCollector::Note(std::string_view code, std::string_view msg, std::optional where) { + Diagnostic d{Severity::Note(), std::string{code}, std::string{msg}}; + if (where) { + d.SetWhere(*where); + } + + Report(std::move(d)); +} + +void DiagnosticCollector::Warn(std::string_view code, std::string_view msg, std::optional where) { + Diagnostic d{Severity::Warning(), std::string{code}, std::string{msg}}; + if (where) { + d.SetWhere(*where); + } + + Report(std::move(d)); +} + +void DiagnosticCollector::Error(std::string_view code, std::string_view msg, std::optional where) { + Diagnostic d{Severity::Error(), std::string{code}, std::string{msg}}; + if (where) { + d.SetWhere(*where); + } + + Report(std::move(d)); +} + +const std::vector& DiagnosticCollector::All() const { + return diags_; +} + +void DiagnosticCollector::Clear() { + diags_.clear(); + errors_ = 0; + warnings_ = 0; +} + +void DiagnosticCollector::SuppressCode(std::string code) { + suppressed_codes_.insert(std::move(code)); +} + +void DiagnosticCollector::SuppressCategory(std::string category) { + suppressed_categories_.insert(std::move(category)); +} + +void DiagnosticCollector::SetGlobalFilter(Predicate p) { + global_filter_ = std::move(p); +} + +void DiagnosticCollector::ResetGlobalFilter() { + global_filter_.reset(); +} + +void DiagnosticCollector::EnableDeduplication(bool on) { + dedup_ = on; +} + +void DiagnosticCollector::SetCapacity(std::optional max_total) { + capacity_ = max_total; +} + +void DiagnosticCollector::SetErrorLimit(std::optional max_errors) { + error_limit_ = max_errors; +} + +void DiagnosticCollector::SetWarningLimit(std::optional max_warnings) { + warning_limit_ = max_warnings; +} + +bool DiagnosticCollector::IsSuppressed(const Diagnostic& d) const { + if (!d.GetCode().empty() && suppressed_codes_.count(d.GetCode()) > 0) { + return true; + } + + if (!d.GetCategory().empty() && suppressed_categories_.count(d.GetCategory()) > 0) { + return true; + } + + if (global_filter_ && !(*global_filter_)(d)) { + return true; + } + + return false; +} + +bool DiagnosticCollector::ShouldKeep(const Diagnostic& d) const { + return !IsSuppressed(d); +} + +bool DiagnosticCollector::IsDuplicate(const Diagnostic& d) const { + for (const auto& prev : diags_) { + if (prev.GetCode() == d.GetCode() && prev.GetMessage() == d.GetMessage() && + ((prev.GetSeverity() && d.GetSeverity()) ? prev.GetSeverity()->Level() == d.GetSeverity()->Level() + : prev.GetSeverity() == d.GetSeverity())) { + return true; + } + } + return false; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/diagnostics/DiagnosticCollector.hpp b/lib/parser/diagnostics/DiagnosticCollector.hpp new file mode 100644 index 0000000..9ed4f7c --- /dev/null +++ b/lib/parser/diagnostics/DiagnosticCollector.hpp @@ -0,0 +1,67 @@ +#ifndef PARSER_DIAGNOSTICCOLLECTOR_HPP_ +#define PARSER_DIAGNOSTICCOLLECTOR_HPP_ + +#include +#include +#include +#include +#include +#include +#include + +#include "IDiagnosticSink.hpp" + +namespace ovum::compiler::parser { + +class DiagnosticCollector : public IDiagnosticSink { +public: + using Predicate = std::function; + + ~DiagnosticCollector() override = default; + + void Report(Diagnostic d) override; + bool HasErrors() const override; + std::size_t Count() const override; + std::size_t ErrorCount() const override; + std::size_t WarningCount() const override; + + void Note(std::string_view code, std::string_view msg, std::optional where = std::nullopt) override; + void Warn(std::string_view code, std::string_view msg, std::optional where = std::nullopt) override; + void Error(std::string_view code, std::string_view msg, std::optional where = std::nullopt) override; + + const std::vector& All() const; + void Clear(); + + void SuppressCode(std::string code); + void SuppressCategory(std::string category); + void SetGlobalFilter(Predicate p); + void ResetGlobalFilter(); + void EnableDeduplication(bool on); + + void SetCapacity(std::optional max_total); + void SetErrorLimit(std::optional max_errors); + void SetWarningLimit(std::optional max_warnings); + + bool IsSuppressed(const Diagnostic& d) const; + +private: + bool ShouldKeep(const Diagnostic& d) const; + bool IsDuplicate(const Diagnostic& d) const; + + std::vector diags_; + std::unordered_set suppressed_codes_; + std::unordered_set suppressed_categories_; + std::optional global_filter_; + + bool dedup_ = true; + std::optional capacity_; + std::optional error_limit_; + std::optional warning_limit_; + + std::size_t errors_ = 0; + std::size_t warnings_ = 0; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_DIAGNOSTICCOLLECTOR_HPP_ diff --git a/lib/parser/diagnostics/FixIt.cpp b/lib/parser/diagnostics/FixIt.cpp new file mode 100644 index 0000000..f110531 --- /dev/null +++ b/lib/parser/diagnostics/FixIt.cpp @@ -0,0 +1,11 @@ +#include "lib/parser/diagnostics/FixIt.hpp" + +#include + +namespace ovum::compiler::parser { + +FixIt::FixIt(SourceSpan source_span, std::string replacement) : + where_(std::move(source_span)), replacement_(std::move(replacement)) { +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/diagnostics/FixIt.hpp b/lib/parser/diagnostics/FixIt.hpp new file mode 100644 index 0000000..fcd23f2 --- /dev/null +++ b/lib/parser/diagnostics/FixIt.hpp @@ -0,0 +1,21 @@ +#ifndef PARSER_FIXIT_HPP_ +#define PARSER_FIXIT_HPP_ + +#include + +#include "lib/parser/tokens/SourceSpan.hpp" + +namespace ovum::compiler::parser { + +class FixIt { +public: + FixIt(SourceSpan source_span, std::string replacement); + +private: + SourceSpan where_; + std::string replacement_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_FIXIT_HPP_ diff --git a/lib/parser/diagnostics/IDiagnosticSink.hpp b/lib/parser/diagnostics/IDiagnosticSink.hpp new file mode 100644 index 0000000..a9bfe62 --- /dev/null +++ b/lib/parser/diagnostics/IDiagnosticSink.hpp @@ -0,0 +1,31 @@ +#ifndef PARSER_IDIAGNOSTICSINK_HPP_ +#define PARSER_IDIAGNOSTICSINK_HPP_ + +#include +#include +#include + +#include "Diagnostic.hpp" +#include "lib/parser/tokens/SourceSpan.hpp" + +namespace ovum::compiler::parser { + +class IDiagnosticSink { +public: + virtual ~IDiagnosticSink() = default; + + virtual void Report(Diagnostic d) = 0; + + virtual bool HasErrors() const = 0; + virtual std::size_t Count() const = 0; + virtual std::size_t ErrorCount() const = 0; + virtual std::size_t WarningCount() const = 0; + + virtual void Note(std::string_view code, std::string_view msg, std::optional where = std::nullopt) = 0; + virtual void Warn(std::string_view code, std::string_view msg, std::optional where = std::nullopt) = 0; + virtual void Error(std::string_view code, std::string_view msg, std::optional where = std::nullopt) = 0; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_IDIAGNOSTICSINK_HPP_ diff --git a/lib/parser/diagnostics/RelatedInfo.cpp b/lib/parser/diagnostics/RelatedInfo.cpp new file mode 100644 index 0000000..6878cad --- /dev/null +++ b/lib/parser/diagnostics/RelatedInfo.cpp @@ -0,0 +1,11 @@ +#include "lib/parser/diagnostics/RelatedInfo.hpp" + +#include + +namespace ovum::compiler::parser { + +RelatedInfo::RelatedInfo(std::string&& message, SourceSpan&& span) : + message_(std::move(message)), where_(std::move(span)) { +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/diagnostics/RelatedInfo.hpp b/lib/parser/diagnostics/RelatedInfo.hpp new file mode 100644 index 0000000..7ec6707 --- /dev/null +++ b/lib/parser/diagnostics/RelatedInfo.hpp @@ -0,0 +1,22 @@ +#ifndef PARSER_RELATEDINFO_HPP_ +#define PARSER_RELATEDINFO_HPP_ + +#include +#include + +#include "lib/parser/tokens/SourceSpan.hpp" + +namespace ovum::compiler::parser { + +class RelatedInfo { +public: + RelatedInfo(std::string&& message, SourceSpan&& span); + +private: + std::string message_; + std::optional where_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_RELATEDINFO_HPP_ diff --git a/lib/parser/diagnostics/severity/ISeverity.hpp b/lib/parser/diagnostics/severity/ISeverity.hpp new file mode 100644 index 0000000..1738dcf --- /dev/null +++ b/lib/parser/diagnostics/severity/ISeverity.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_ISEVERITY_HPP_ +#define PARSER_ISEVERITY_HPP_ + +#include + +namespace ovum::compiler::parser { + +class ISeverity { +public: + virtual ~ISeverity() = default; + virtual std::string_view Name() const = 0; + virtual int Level() const = 0; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_ISEVERITY_HPP_ diff --git a/lib/parser/diagnostics/severity/Severity.cpp b/lib/parser/diagnostics/severity/Severity.cpp new file mode 100644 index 0000000..860ef58 --- /dev/null +++ b/lib/parser/diagnostics/severity/Severity.cpp @@ -0,0 +1,49 @@ +#include "lib/parser/diagnostics/severity/Severity.hpp" + +#include +#include +#include +#include + +namespace ovum::compiler::parser { + +namespace { +class SimpleSeverity : public ISeverity { +public: + SimpleSeverity(std::string name, int level) : name_(std::move(name)), level_(level) { + } + + [[nodiscard]] std::string_view Name() const override { + return name_; + } + + [[nodiscard]] int Level() const override { + return level_; + } + +private: + std::string name_; + int level_; +}; +} // namespace + +const std::shared_ptr& Severity::Note() { + static const auto kInst = std::make_shared("note", 10); + return kInst; +} + +const std::shared_ptr& Severity::Warning() { + static const auto kInst = std::make_shared("warning", 20); + return kInst; +} + +const std::shared_ptr& Severity::Error() { + static const auto kInst = std::make_shared("error", 30); + return kInst; +} + +std::shared_ptr Severity::Custom(std::string_view name, int level) { + return std::make_shared(std::string{name}, level); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/diagnostics/severity/Severity.hpp b/lib/parser/diagnostics/severity/Severity.hpp new file mode 100644 index 0000000..c7bfa8b --- /dev/null +++ b/lib/parser/diagnostics/severity/Severity.hpp @@ -0,0 +1,22 @@ +#ifndef PARSER_SEVERITY_HPP_ +#define PARSER_SEVERITY_HPP_ + +#include +#include + +#include "ISeverity.hpp" + +namespace ovum::compiler::parser { + +class Severity { +public: + static const std::shared_ptr& Note(); + static const std::shared_ptr& Warning(); + static const std::shared_ptr& Error(); + + static std::shared_ptr Custom(std::string_view name, int level); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_SEVERITY_HPP_ diff --git a/lib/parser/pratt/DefaultOperatorResolver.cpp b/lib/parser/pratt/DefaultOperatorResolver.cpp new file mode 100644 index 0000000..d5ccde1 --- /dev/null +++ b/lib/parser/pratt/DefaultOperatorResolver.cpp @@ -0,0 +1,145 @@ +#include "lib/parser/pratt/DefaultOperatorResolver.hpp" + +#include +#include + +#include "lib/parser/ast/nodes/exprs/tags/OpTags.hpp" + +namespace ovum::compiler::parser { + +namespace { +bool LexIs(const Token& t, std::string_view s) { + return t.GetLexeme() == s; +} + +constexpr int kBP_Assign = 10; +constexpr int kBP_Elvis = 20; // right-assoc +constexpr int kBP_Or = 30; +constexpr int kBP_And = 40; +constexpr int kBP_Xor = 50; +constexpr int kBP_Eq = 60; +constexpr int kBP_Rel = 70; +constexpr int kBP_Add = 80; +constexpr int kBP_Mul = 90; +constexpr int kBP_Post = 100; + +std::vector& InfixTable() { + static std::vector specs; + if (!specs.empty()) { + return specs; + } + + auto add = [&](std::string_view lex, int bp, const IBinaryOpTag& tag) { + InfixSpec s(bp, bp, false, const_cast(&tag), false); + s.SetMatcher([lex](const Token& t) { return LexIs(t, lex); }); + specs.push_back(std::move(s)); + }; + + add("+", kBP_Add, OpTags::Add()); + add("-", kBP_Add, OpTags::Sub()); + add("*", kBP_Mul, OpTags::Mul()); + add("/", kBP_Mul, OpTags::Div()); + add("%", kBP_Mul, OpTags::Mod()); + + add("==", kBP_Eq, OpTags::Eq()); + add("!=", kBP_Eq, OpTags::Ne()); + + add("<", kBP_Rel, OpTags::Lt()); + add("<=", kBP_Rel, OpTags::Le()); + add(">", kBP_Rel, OpTags::Gt()); + add(">=", kBP_Rel, OpTags::Ge()); + + add("&&", kBP_And, OpTags::And()); + add("||", kBP_Or, OpTags::Or()); + add("^", kBP_Xor, OpTags::Xor()); + + { + InfixSpec elvis(kBP_Elvis, kBP_Elvis - 1, true, nullptr, true); + elvis.SetMatcher([](const Token& t) { return LexIs(t, "?:"); }); + specs.push_back(std::move(elvis)); + } + + return specs; +} + +std::vector& PostfixTable() { + static std::vector specs; + if (!specs.empty()) { + return specs; + } + + specs.emplace_back([](const Token& t) { return LexIs(t, "("); }, kBP_Post, false); + specs.emplace_back([](const Token& t) { return LexIs(t, "."); }, kBP_Post, false); + specs.emplace_back([](const Token& t) { return LexIs(t, "?."); }, kBP_Post, false); + specs.emplace_back([](const Token& t) { return LexIs(t, "as"); }, kBP_Post, true); + specs.emplace_back([](const Token& t) { return LexIs(t, "is"); }, kBP_Post, true); + specs.emplace_back([](const Token& t) { return LexIs(t, "::"); }, kBP_Post, false); + + return specs; +} + +const IUnaryOpTag* MatchPrefix(const Token& t) { + if (LexIs(t, "-")) { + return &OpTags::Neg(); + } + + if (LexIs(t, "+")) { + return &OpTags::Plus(); + } + + if (LexIs(t, "!")) { + return &OpTags::Not(); + } + + return nullptr; +} + +} // namespace + +std::optional> DefaultOperatorResolver::FindInfix(const Token& t) const { + auto& tbl = InfixTable(); + for (const auto& s : tbl) { + if (s.TryMatch(t)) { + return std::cref(s); + } + } + + return std::nullopt; +} + +std::optional> DefaultOperatorResolver::FindPostfix(const Token& t) const { + auto& tbl = PostfixTable(); + for (const auto& s : tbl) { + if (s.TryMatch(t)) { + return std::cref(s); + } + } + + return std::nullopt; +} + +std::optional> DefaultOperatorResolver::FindPrefix(const Token& t) const { + if (auto* tag = MatchPrefix(t)) { + return std::cref(*tag); + } + + return std::nullopt; +} + +bool DefaultOperatorResolver::IsContinuation(const Token& t) const { + if (FindInfix(t).has_value()) { + return true; + } + + if (FindPostfix(t).has_value()) { + return true; + } + + const auto& lx = t.GetLexeme(); + if (lx == "(" || lx == "[") { + return true; + } + return false; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/pratt/DefaultOperatorResolver.hpp b/lib/parser/pratt/DefaultOperatorResolver.hpp new file mode 100644 index 0000000..30aa0ec --- /dev/null +++ b/lib/parser/pratt/DefaultOperatorResolver.hpp @@ -0,0 +1,32 @@ +#ifndef PARSER_DEFAULTOPERATORRESOLVER_HPP_ +#define PARSER_DEFAULTOPERATORRESOLVER_HPP_ + +#include +#include +#include + +#include "IOperatorResolver.hpp" +#include "lib/parser/ast/nodes/exprs/tags/IUnaryOpTag.hpp" +#include "specifications/InfixSpec.hpp" +#include "specifications/PostfixSpec.hpp" +#include "tokens/Token.hpp" + +namespace ovum::compiler::parser { + +class DefaultOperatorResolver : public IOperatorResolver { +public: + ~DefaultOperatorResolver() override = default; + + std::optional> FindInfix(const Token& t) const override; + std::optional> FindPostfix(const Token& t) const override; + std::optional> FindPrefix(const Token& t) const override; + bool IsContinuation(const Token& t) const override; + +private: + std::vector infix_; + std::vector postfix_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_DEFAULTOPERATORRESOLVER_HPP_ diff --git a/lib/parser/pratt/IExpressionParser.hpp b/lib/parser/pratt/IExpressionParser.hpp new file mode 100644 index 0000000..4cdc336 --- /dev/null +++ b/lib/parser/pratt/IExpressionParser.hpp @@ -0,0 +1,21 @@ +#ifndef PARSER_IEXPRESSIONPARSER_HPP_ +#define PARSER_IEXPRESSIONPARSER_HPP_ + +#include + +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +class IExpressionParser { +public: + virtual ~IExpressionParser() = default; + + virtual std::unique_ptr Parse(ITokenStream& ts, IDiagnosticSink& diags) = 0; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_IEXPRESSIONPARSER_HPP_ diff --git a/lib/parser/pratt/IOperatorResolver.hpp b/lib/parser/pratt/IOperatorResolver.hpp new file mode 100644 index 0000000..35346b9 --- /dev/null +++ b/lib/parser/pratt/IOperatorResolver.hpp @@ -0,0 +1,25 @@ +#ifndef PARSER_IOPERATORRESOLVER_HPP_ +#define PARSER_IOPERATORRESOLVER_HPP_ + +#include +#include + +#include "lib/parser/ast/nodes/exprs/tags/IUnaryOpTag.hpp" +#include "specifications/InfixSpec.hpp" +#include "specifications/PostfixSpec.hpp" + +namespace ovum::compiler::parser { + +class IOperatorResolver { +public: + virtual ~IOperatorResolver() = default; + + virtual std::optional> FindInfix(const Token& t) const = 0; + virtual std::optional> FindPostfix(const Token& t) const = 0; + virtual std::optional> FindPrefix(const Token& t) const = 0; + virtual bool IsContinuation(const Token& t) const = 0; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_IOPERATORRESOLVER_HPP_ diff --git a/lib/parser/pratt/PrattExpressionParser.cpp b/lib/parser/pratt/PrattExpressionParser.cpp new file mode 100644 index 0000000..8b79220 --- /dev/null +++ b/lib/parser/pratt/PrattExpressionParser.cpp @@ -0,0 +1,496 @@ +#include "PrattExpressionParser.hpp" + +#include +#include +#include +#include +#include +#include + +#include + +#include "lib/parser/ast/nodes/exprs/tags/OpTags.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/pratt/specifications/InfixSpec.hpp" +#include "lib/parser/pratt/specifications/PostfixSpec.hpp" +#include "lib/parser/tokens/SourceSpan.hpp" + +namespace ovum::compiler::parser { + +namespace { + +constexpr int kBP_Assign = 5; // right-assoc +constexpr int kBP_Elvis = 20; // right-assoc +constexpr int kBP_Post = 100; // postfix + +bool Lex(const Token& t, std::string_view s) { + return t.GetLexeme() == s; +} + +bool IsIdentifier(const Token& t) { + return t.GetStringType() == "IDENT"; +} + +bool IsLiteral(const Token& t) { + const std::string ty = t.GetStringType(); + return ty.rfind("LITERAL", 0) == 0; // "LITERAL:*" +} + +SourceSpan SpanFrom(const Token& token) { + const auto& pos = token.GetPosition(); + return SourceSpan(SourceId{}, pos, pos); +} + +SourceSpan Union(const SourceSpan& a, const SourceSpan& b) { + return SourceSpan::Union(a, b); +} + +bool ParseInteger(const std::string& text, long long* out) { + if (out == nullptr) { + return false; + } + const char* begin = text.data(); + const char* end = begin + text.size(); + auto res = std::from_chars(begin, end, *out, 10); + return res.ec == std::errc{} && res.ptr == end; +} + +bool ParseFloat(const std::string& text, long double* out) { + try { + if (out == nullptr) { + return false; + } + *out = std::stold(text); + return true; + } catch (...) { + return false; + } +} + +std::string Unquote(std::string_view lexeme) { + if (lexeme.size() >= 2 && ((lexeme.front() == '"' && lexeme.back() == '"') || + (lexeme.front() == '\'' && lexeme.back() == '\''))) { + return std::string(lexeme.substr(1, lexeme.size() - 2)); + } + return std::string(lexeme); +} + +std::unique_ptr MakeLiteralFromToken(const Token& token, IAstFactory& factory, IDiagnosticSink& diags) { + const std::string ty = token.GetStringType(); + const auto span = SpanFrom(token); + + if (ty == "LITERAL:Int") { + long long value = 0; + if (!ParseInteger(token.GetLexeme(), &value)) { + diags.Error("E_LITERAL_INT", "invalid integer literal", span); + return nullptr; + } + return factory.MakeInt(value, span); + } + + if (ty == "LITERAL:Float") { + long double value = 0.0; + if (!ParseFloat(token.GetLexeme(), &value)) { + diags.Error("E_LITERAL_FLOAT", "invalid float literal", span); + return nullptr; + } + return factory.MakeFloat(value, span); + } + + if (ty == "LITERAL:Bool") { + const auto lex = token.GetLexeme(); + const bool value = lex == "true" || lex == "True" || lex == "TRUE"; + return factory.MakeBool(value, span); + } + + if (ty == "LITERAL:Char") { + const auto raw = Unquote(token.GetLexeme()); + char value = raw.empty() ? '\0' : raw.front(); + return factory.MakeChar(value, span); + } + + if (ty == "LITERAL:String") { + const auto raw = Unquote(token.GetLexeme()); + return factory.MakeString(raw, span); + } + + if (ty == "LITERAL:Null") { + return factory.MakeNull(span); + } + + const std::string lex = token.GetLexeme(); + if (lex == "null") { + return factory.MakeNull(span); + } + if (lex == "true" || lex == "false") { + return factory.MakeBool(lex == "true", span); + } + + diags.Error("E_LITERAL_UNKNOWN", "unsupported literal token", span); + return nullptr; +} + +} // namespace + +PrattExpressionParser::PrattExpressionParser(std::unique_ptr resolver, + std::shared_ptr factory) : + resolver_(std::move(resolver)), factory_(std::move(factory)) { +} + +std::unique_ptr PrattExpressionParser::Parse(ITokenStream& ts, IDiagnosticSink& diags) { + return ParseExpr(ts, diags, 0); +} + +std::unique_ptr PrattExpressionParser::ParseExpr(ITokenStream& ts, IDiagnosticSink& diags, int min_bp) { + std::unique_ptr left = ParsePrefix(ts, diags); + if (!left) { + return nullptr; + } + + while (!ts.IsEof()) { + const Token& look = ts.Peek(); + const auto post = resolver_->FindPostfix(look); + if (!post.has_value()) { + break; + } + + const int bp = post->get().BindingPower(); + if (bp < min_bp) { + break; + } + + left = ParsePostfix(ts, diags, std::move(left)); + if (!left) { + return nullptr; + } + } + + while (!ts.IsEof()) { + const Token& look = ts.Peek(); + + const bool is_ref_assign = Lex(look, "="); + const bool is_copy_assign = Lex(look, ":="); + if (is_ref_assign || is_copy_assign) { + if (kBP_Assign < min_bp) { + break; + } + + ts.Consume(); + + std::unique_ptr rhs = ParseExpr(ts, diags, kBP_Assign - 1); // right-assoc + if (!rhs) { + return nullptr; + } + + SourceSpan span = Union(left->Span(), rhs->Span()); + left = factory_->MakeAssign( + is_ref_assign ? OpTags::RefAssign() : OpTags::CopyAssign(), std::move(left), std::move(rhs), span); + + while (!ts.IsEof()) { + const Token& next = ts.Peek(); + const auto post2 = resolver_->FindPostfix(next); + if (!post2.has_value() || post2->get().BindingPower() < min_bp) { + break; + } + + left = ParsePostfix(ts, diags, std::move(left)); + if (!left) { + return nullptr; + } + } + continue; + } + + const auto inf = resolver_->FindInfix(look); + if (!inf.has_value()) { + break; + } + + const int lbp = inf->get().Lbp(); + if (lbp < min_bp) { + break; + } + + if (inf->get().IsElvis()) { + ts.Consume(); // ?: + std::unique_ptr rhs = ParseExpr(ts, diags, inf->get().Rbp()); + if (!rhs) { + return nullptr; + } + + SourceSpan span = Union(left->Span(), rhs->Span()); + left = factory_->MakeElvis(std::move(left), std::move(rhs), span); + } else { + const IBinaryOpTag* tag = inf->get().Tag(); + if (tag == nullptr) { + diags.Error("E_EXPR_OP", "internal: infix without tag"); + return nullptr; + } + + ts.Consume(); + + const int next_min = inf->get().IsRightAssociative() ? inf->get().Rbp() : inf->get().Rbp() + 1; + + std::unique_ptr rhs = ParseExpr(ts, diags, next_min); + if (!rhs) { + return nullptr; + } + + SourceSpan span = Union(left->Span(), rhs->Span()); + left = factory_->MakeBinary(*tag, std::move(left), std::move(rhs), span); + } + + while (!ts.IsEof()) { + const Token& next2 = ts.Peek(); + const auto post3 = resolver_->FindPostfix(next2); + if (!post3.has_value() || post3->get().BindingPower() < min_bp) { + break; + } + + left = ParsePostfix(ts, diags, std::move(left)); + if (!left) { + return nullptr; + } + } + } + + return left; +} + +std::unique_ptr PrattExpressionParser::ParsePrefix(ITokenStream& ts, IDiagnosticSink& diags) { + if (ts.IsEof()) { + diags.Error("E_EXPR_EOF", "unexpected end of input"); + return nullptr; + } + + const Token& look = ts.Peek(); + + if (auto pre = resolver_->FindPrefix(look)) { + ts.Consume(); + std::unique_ptr operand = ParseExpr(ts, diags, kBP_Post - 1); + if (!operand) { + return nullptr; + } + + SourceSpan span = SpanFrom(look); + if (operand) { + span = Union(span, operand->Span()); + } + return factory_->MakeUnary(pre->get(), std::move(operand), span); + } + + if (Lex(look, "(")) { + ts.Consume(); + std::unique_ptr inner = ParseExpr(ts, diags, 0); + if (!inner) { + return nullptr; + } + + if (ts.IsEof() || !Lex(ts.Peek(), ")")) { + diags.Error("E_EXPR_GROUP", "expected ')'"); + return nullptr; + } + + ts.Consume(); + return inner; + } + + if (IsIdentifier(look)) { + std::string name = look.GetLexeme(); + ts.Consume(); + return factory_->MakeIdent(std::move(name), SpanFrom(look)); + } + + if (IsLiteral(look)) { + std::unique_ptr lit = MakeLiteralFromToken(look, *factory_, diags); + ts.Consume(); + return lit; + } + + if (Lex(look, "null")) { + ts.Consume(); + return factory_->MakeNull(SpanFrom(look)); + } + + if (Lex(look, "true") || Lex(look, "false")) { + bool value = Lex(look, "true"); + ts.Consume(); + return factory_->MakeBool(value, SpanFrom(look)); + } + + diags.Error("E_EXPR_PRIMARY", "expected primary expression"); + return nullptr; +} + +std::unique_ptr PrattExpressionParser::ParsePostfix(ITokenStream& ts, + IDiagnosticSink& diags, + std::unique_ptr base) { + if (!base) { + return nullptr; + } + + if (ts.IsEof()) { + return base; + } + + const Token& look = ts.Peek(); + + if (Lex(look, "(")) { + ts.Consume(); + const SourceSpan start_span = base ? base->Span() : SpanFrom(look); + auto args = ParseArgList(ts, diags, ')'); + if (args.empty() && (ts.IsEof() || !Lex(ts.Peek(), ")"))) { + diags.Error("E_CALL_CLOSE", "expected ')'"); + return nullptr; + } + + if (!ts.IsEof() && Lex(ts.Peek(), ")")) { + ts.Consume(); + } + + SourceSpan end_span = start_span; + if (!args.empty()) { + end_span = args.back()->Span(); + } else if (const Token* last = ts.LastConsumed()) { + end_span = SpanFrom(*last); + } + return factory_->MakeCall(std::move(base), std::move(args), Union(start_span, end_span)); + } + + if (Lex(look, ".")) { + ts.Consume(); + + if (ts.IsEof() || !IsIdentifier(ts.Peek())) { + diags.Error("E_DOT_IDENT", "expected identifier after '.'"); + return nullptr; + } + + std::string member = ts.Peek().GetLexeme(); + ts.Consume(); + + if (!ts.IsEof() && Lex(ts.Peek(), "(")) { + ts.Consume(); + auto args = ParseArgList(ts, diags, ')'); + if (args.empty() && (ts.IsEof() || !Lex(ts.Peek(), ")"))) { + diags.Error("E_CALL_CLOSE", "expected ')'"); + return nullptr; + } + + if (!ts.IsEof() && Lex(ts.Peek(), ")")) { + ts.Consume(); + } + + auto callee = factory_->MakeFieldAccess(std::move(base), std::move(member), SpanFrom(look)); + SourceSpan end_span = callee->Span(); + if (!args.empty()) { + end_span = args.back()->Span(); + } else if (const Token* last = ts.LastConsumed()) { + end_span = SpanFrom(*last); + } + return factory_->MakeCall(std::move(callee), std::move(args), end_span); + } + + SourceSpan span = base ? base->Span() : SpanFrom(look); + return factory_->MakeFieldAccess(std::move(base), std::move(member), span); + } + + if (Lex(look, "?.")) { + ts.Consume(); + + if (ts.IsEof() || !IsIdentifier(ts.Peek())) { + diags.Error("E_SAFECALL_IDENT", "expected identifier after '?.'"); + return nullptr; + } + + std::string method = ts.Peek().GetLexeme(); + ts.Consume(); + + std::vector> args; + if (!ts.IsEof() && Lex(ts.Peek(), "(")) { + ts.Consume(); + args = ParseArgList(ts, diags, ')'); + + if (args.empty() && (ts.IsEof() || !Lex(ts.Peek(), ")"))) { + diags.Error("E_CALL_CLOSE", "expected ')'"); + return nullptr; + } + + if (!ts.IsEof() && Lex(ts.Peek(), ")")) { + ts.Consume(); + } + } + + SourceSpan end_span = base ? base->Span() : SpanFrom(look); + if (!args.empty()) { + end_span = args.back()->Span(); + } else if (const Token* last = ts.LastConsumed()) { + end_span = SpanFrom(*last); + } + + return factory_->MakeSafeCall(std::move(base), + std::move(method), + std::move(args), + std::nullopt, + end_span); + } + + if (Lex(look, "::")) { + ts.Consume(); + if (ts.IsEof() || !IsIdentifier(ts.Peek())) { + diags.Error("E_NS_IDENT", "expected identifier after '::'"); + return nullptr; + } + + std::string ident = ts.Peek().GetLexeme(); + ts.Consume(); + SourceSpan span = base ? base->Span() : SpanFrom(look); + return factory_->MakeNamespaceRef(std::move(base), std::move(ident), span); + } + + if (Lex(look, "as") || Lex(look, "is")) { + diags.Error("E_TYPE_POSTFIX", "type postfix ('as'/'is') requires type parser"); + return nullptr; + } + + return base; +} + +std::vector> PrattExpressionParser::ParseArgList(ITokenStream& ts, + IDiagnosticSink& diags, + char closing) { + std::vector> result; + + if (!ts.IsEof() && ts.Peek().GetLexeme().size() == 1 && ts.Peek().GetLexeme()[0] == closing) { + return result; + } + + if (auto arg = ParseExpr(ts, diags, 0)) { + result.push_back(std::move(arg)); + } else { + return {}; + } + + while (!ts.IsEof() && ts.Peek().GetLexeme() == ",") { + ts.Consume(); + auto arg = ParseExpr(ts, diags, 0); + + if (!arg) { + return {}; + } + + result.push_back(std::move(arg)); + } + + return result; +} + +std::unique_ptr PrattExpressionParser::MakeInfix(const InfixSpec& spec, + std::unique_ptr lhs, + std::unique_ptr rhs) { + if (spec.IsElvis()) { + return factory_->MakeElvis(std::move(lhs), std::move(rhs)); + } + return factory_->MakeBinary(*spec.Tag(), std::move(lhs), std::move(rhs)); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/pratt/PrattExpressionParser.hpp b/lib/parser/pratt/PrattExpressionParser.hpp new file mode 100644 index 0000000..daadd0b --- /dev/null +++ b/lib/parser/pratt/PrattExpressionParser.hpp @@ -0,0 +1,38 @@ +#ifndef PARSER_PRATTEXPRESSIONPARSER_HPP_ +#define PARSER_PRATTEXPRESSIONPARSER_HPP_ + +#include +#include + +#include "IExpressionParser.hpp" +#include "IOperatorResolver.hpp" +#include "lib/parser/ast/IAstFactory.hpp" +#include "lib/parser/ast/nodes/base/Expr.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +class PrattExpressionParser : public IExpressionParser { +public: + explicit PrattExpressionParser(std::unique_ptr resolver, std::shared_ptr factory); + ~PrattExpressionParser() override = default; + + std::unique_ptr Parse(ITokenStream& ts, IDiagnosticSink& diags) override; + + std::unique_ptr ParseExpr(ITokenStream& ts, IDiagnosticSink& diags, int min_bp); + std::unique_ptr ParsePrefix(ITokenStream& ts, IDiagnosticSink& diags); + std::unique_ptr ParsePostfix(ITokenStream& ts, IDiagnosticSink& diags, std::unique_ptr base); + + std::vector> ParseArgList(ITokenStream& ts, IDiagnosticSink& diags, char closing); + +private: + std::unique_ptr MakeInfix(const InfixSpec& spec, std::unique_ptr lhs, std::unique_ptr rhs); + + std::unique_ptr resolver_; + std::shared_ptr factory_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_PRATTEXPRESSIONPARSER_HPP_ diff --git a/lib/parser/pratt/specifications/InfixSpec.cpp b/lib/parser/pratt/specifications/InfixSpec.cpp new file mode 100644 index 0000000..e13c1f4 --- /dev/null +++ b/lib/parser/pratt/specifications/InfixSpec.cpp @@ -0,0 +1,49 @@ +#include "InfixSpec.hpp" + +namespace ovum::compiler::parser { + +InfixSpec::InfixSpec(int left_bp, int right_bp, bool right_associative, IBinaryOpTag* tag, bool is_elvis) : + lbp_(left_bp), rbp_(right_bp), right_associative_(right_associative), tag_(tag), is_elvis_(is_elvis) { +} + +void InfixSpec::SetMatcher(std::function matcher) { + match_ = std::move(matcher); +} + +bool InfixSpec::TryMatch(const Token& token) const { + return match_ ? match_(token) : false; +} + +int InfixSpec::Lbp() const noexcept { + return lbp_; +} +int InfixSpec::Rbp() const noexcept { + return rbp_; +} +bool InfixSpec::IsRightAssociative() const noexcept { + return right_associative_; +} +const IBinaryOpTag* InfixSpec::Tag() const noexcept { + return tag_; +} +bool InfixSpec::IsElvis() const noexcept { + return is_elvis_; +} + +void InfixSpec::SetLbp(int v) noexcept { + lbp_ = v; +} +void InfixSpec::SetRbp(int v) noexcept { + rbp_ = v; +} +void InfixSpec::SetRightAssociative(bool v) noexcept { + right_associative_ = v; +} +void InfixSpec::SetTag(const IBinaryOpTag* t) noexcept { + tag_ = t; +} +void InfixSpec::SetElvis(bool v) noexcept { + is_elvis_ = v; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/pratt/specifications/InfixSpec.hpp b/lib/parser/pratt/specifications/InfixSpec.hpp new file mode 100644 index 0000000..cdad620 --- /dev/null +++ b/lib/parser/pratt/specifications/InfixSpec.hpp @@ -0,0 +1,41 @@ +#ifndef PARSER_INFIXSPEC_HPP_ +#define PARSER_INFIXSPEC_HPP_ + +#include + +#include "lib/parser/ast/nodes/exprs/tags/IBinaryOpTag.hpp" +#include "tokens/Token.hpp" + +namespace ovum::compiler::parser { + +class InfixSpec { +public: + InfixSpec(int left_bp, int right_bp, bool right_associative, IBinaryOpTag* tag, bool is_elvis); + + void SetMatcher(std::function matcher); + bool TryMatch(const Token& token) const; + + int Lbp() const noexcept; + int Rbp() const noexcept; + bool IsRightAssociative() const noexcept; + const IBinaryOpTag* Tag() const noexcept; + bool IsElvis() const noexcept; + + void SetLbp(int v) noexcept; + void SetRbp(int v) noexcept; + void SetRightAssociative(bool v) noexcept; + void SetTag(const IBinaryOpTag* t) noexcept; + void SetElvis(bool v) noexcept; + +private: + std::function match_; + int lbp_ = 0; + int rbp_ = 0; + bool right_associative_ = false; + const IBinaryOpTag* tag_ = nullptr; + bool is_elvis_ = false; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_INFIXSPEC_HPP_ diff --git a/lib/parser/pratt/specifications/PostfixSpec.cpp b/lib/parser/pratt/specifications/PostfixSpec.cpp new file mode 100644 index 0000000..b506eb6 --- /dev/null +++ b/lib/parser/pratt/specifications/PostfixSpec.cpp @@ -0,0 +1,27 @@ +#include "PostfixSpec.hpp" + +namespace ovum::compiler::parser { + +PostfixSpec::PostfixSpec(std::function match, int bp, bool keyword) : + match_(std::move(match)), bp_(bp), keyword_(keyword) { +} + +bool PostfixSpec::TryMatch(const Token& token) const { + return match_ ? match_(token) : false; +} + +int PostfixSpec::BindingPower() const noexcept { + return bp_; +} +bool PostfixSpec::IsKeyword() const noexcept { + return keyword_; +} + +void PostfixSpec::SetBindingPower(int v) noexcept { + bp_ = v; +} +void PostfixSpec::SetKeyword(bool v) noexcept { + keyword_ = v; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/pratt/specifications/PostfixSpec.hpp b/lib/parser/pratt/specifications/PostfixSpec.hpp new file mode 100644 index 0000000..806bbc6 --- /dev/null +++ b/lib/parser/pratt/specifications/PostfixSpec.hpp @@ -0,0 +1,30 @@ +#ifndef PARSER_POSTFIXSPEC_HPP_ +#define PARSER_POSTFIXSPEC_HPP_ + +#include + +#include "tokens/Token.hpp" + +namespace ovum::compiler::parser { + +class PostfixSpec { +public: + PostfixSpec(std::function match, int bp, bool keyword); + + bool TryMatch(const Token& token) const; + + int BindingPower() const noexcept; + bool IsKeyword() const noexcept; + + void SetBindingPower(int v) noexcept; + void SetKeyword(bool v) noexcept; + +private: + std::function match_; + int bp_ = 0; + bool keyword_ = false; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_POSTFIXSPEC_HPP_ diff --git a/lib/parser/recovery/IRecoveryStrategy.hpp b/lib/parser/recovery/IRecoveryStrategy.hpp new file mode 100644 index 0000000..8df9f3e --- /dev/null +++ b/lib/parser/recovery/IRecoveryStrategy.hpp @@ -0,0 +1,18 @@ +#ifndef PARSER_IRECOVERYSTRATEGY_HPP_ +#define PARSER_IRECOVERYSTRATEGY_HPP_ + +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +class IRecoveryStrategy { +public: + virtual ~IRecoveryStrategy() { + } + virtual void SyncToStmtEnd(ITokenStream& ts) = 0; + virtual void SyncToBlockEnd(ITokenStream& ts) = 0; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_IRECOVERYSTRATEGY_HPP_ diff --git a/lib/parser/recovery/SimpleRecovery.cpp b/lib/parser/recovery/SimpleRecovery.cpp new file mode 100644 index 0000000..9d07422 --- /dev/null +++ b/lib/parser/recovery/SimpleRecovery.cpp @@ -0,0 +1,58 @@ +#include "lib/parser/recovery/SimpleRecovery.hpp" + +#include +#include + +namespace ovum::compiler::parser { +namespace { +bool IsLex(const Token& tok, std::string_view lex) { + return tok.GetLexeme() == lex; +} +} // namespace + +void SimpleRecovery::SyncToStmtEnd(ITokenStream& ts) { + while (!ts.IsEof()) { + const auto* look = ts.TryPeek(); + if (look == nullptr) { + break; + } + if (IsLex(*look, "\n")) { + ts.Consume(); + break; + } + if (IsLex(*look, ";")) { + ts.Consume(); + break; + } + if (IsLex(*look, "}")) { + break; + } + ts.Consume(); + } +} + +void SimpleRecovery::SyncToBlockEnd(ITokenStream& ts) { + int depth = 0; + while (!ts.IsEof()) { + const auto* look = ts.TryPeek(); + if (look == nullptr) { + break; + } + if (IsLex(*look, "{")) { + ++depth; + ts.Consume(); + continue; + } + if (IsLex(*look, "}")) { + ts.Consume(); + if (depth == 0) { + break; + } + --depth; + continue; + } + ts.Consume(); + } +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/recovery/SimpleRecovery.hpp b/lib/parser/recovery/SimpleRecovery.hpp new file mode 100644 index 0000000..737662a --- /dev/null +++ b/lib/parser/recovery/SimpleRecovery.hpp @@ -0,0 +1,22 @@ +#ifndef PARSER_SIMPLERECOVERY_HPP_ +#define PARSER_SIMPLERECOVERY_HPP_ + +#include "IRecoveryStrategy.hpp" + +namespace ovum::compiler::parser { + +class SimpleRecovery : public IRecoveryStrategy { +public: + ~SimpleRecovery() override = default; + + void SyncToStmtEnd(ITokenStream& ts) override; + void SyncToBlockEnd(ITokenStream& ts) override; + + void SyncToStatementEnd(ITokenStream& ts) { + SyncToStmtEnd(ts); + } // alias +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_SIMPLERECOVERY_HPP_ diff --git a/lib/parser/states/StateBlock.cpp b/lib/parser/states/StateBlock.cpp new file mode 100644 index 0000000..fc3e260 --- /dev/null +++ b/lib/parser/states/StateBlock.cpp @@ -0,0 +1,206 @@ +#include "StateBlock.hpp" + +#include + +#include "lib/parser/ast/nodes/class_members/CallDecl.hpp" +#include "lib/parser/ast/nodes/class_members/DestructorDecl.hpp" +#include "lib/parser/ast/nodes/class_members/MethodDecl.hpp" +#include "lib/parser/ast/nodes/decls/ClassDecl.hpp" +#include "lib/parser/ast/nodes/decls/FunctionDecl.hpp" +#include "lib/parser/ast/nodes/decls/Module.hpp" +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/ast/nodes/stmts/ForStmt.hpp" +#include "lib/parser/ast/nodes/stmts/IfStmt.hpp" +#include "lib/parser/ast/nodes/stmts/UnsafeBlock.hpp" +#include "lib/parser/ast/nodes/stmts/WhileStmt.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" +#include "lib/parser/tokens/token_traits/MatchLexeme.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +} // namespace + +std::string_view StateBlock::Name() const { + return "Block"; +} + +IState::StepResult StateBlock::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in block")); + } + + Block* block = ctx.TopNodeAs(); + if (block == nullptr) { + return std::unexpected(StateError("expected Block node on stack")); + } + + const Token& tok = ts.Peek(); + if (tok.GetLexeme() == "}") { + ts.Consume(); + + // Check if there's a parent node that needs this block + if (ctx.NodeStack().size() >= 2) { + auto parent_node = ctx.NodeStack()[ctx.NodeStack().size() - 2].MutableNode(); + + // Check if parent is FunctionDecl + FunctionDecl* func = dynamic_cast(parent_node); + if (func != nullptr && func->Body() == nullptr) { + auto block_node = ctx.PopNode(); + Block* block = dynamic_cast(block_node.get()); + if (block != nullptr) { + func->SetBody(std::unique_ptr(block)); + block_node.release(); + + // Pop function and add to module + auto func_node = ctx.PopNode(); + Module* module = ctx.TopNodeAs(); + if (module != nullptr) { + module->AddDecl(std::unique_ptr(dynamic_cast(func_node.release()))); + } + } + return false; + } + + // Check if parent is IfStmt (for else block) + IfStmt* if_stmt = dynamic_cast(parent_node); + if (if_stmt != nullptr && if_stmt->ElseBlock() == nullptr) { + auto block_node = ctx.PopNode(); + Block* else_block = dynamic_cast(block_node.get()); + if (else_block != nullptr) { + if_stmt->SetElseBlock(std::unique_ptr(else_block)); + block_node.release(); + + // Pop IfStmt and add to parent block + auto if_node = ctx.PopNode(); + Block* parent_block = ctx.TopNodeAs(); + if (parent_block != nullptr) { + parent_block->Append(std::unique_ptr(dynamic_cast(if_node.release()))); + } + } + return false; + } + + // Check if parent is WhileStmt + WhileStmt* while_stmt = dynamic_cast(parent_node); + if (while_stmt != nullptr && while_stmt->Body() == nullptr) { + auto block_node = ctx.PopNode(); + Block* body_block = dynamic_cast(block_node.get()); + if (body_block != nullptr) { + while_stmt->SetBody(std::unique_ptr(body_block)); + block_node.release(); + } + return false; + } + + // Check if parent is ForStmt + ForStmt* for_stmt = dynamic_cast(parent_node); + if (for_stmt != nullptr && for_stmt->Body() == nullptr) { + auto block_node = ctx.PopNode(); + Block* body_block = dynamic_cast(block_node.get()); + if (body_block != nullptr) { + for_stmt->SetBody(std::unique_ptr(body_block)); + block_node.release(); + } + return false; + } + + // Check if parent is UnsafeBlock + UnsafeBlock* unsafe_stmt = dynamic_cast(parent_node); + if (unsafe_stmt != nullptr && unsafe_stmt->Body() == nullptr) { + auto block_node = ctx.PopNode(); + Block* body_block = dynamic_cast(block_node.get()); + if (body_block != nullptr) { + unsafe_stmt->SetBody(std::unique_ptr(body_block)); + block_node.release(); + } + return false; + } + + // Check if parent is MethodDecl + MethodDecl* method = dynamic_cast(parent_node); + if (method != nullptr && method->Body() == nullptr) { + auto block_node = ctx.PopNode(); + Block* body_block = dynamic_cast(block_node.get()); + if (body_block != nullptr) { + method->SetBody(std::unique_ptr(body_block)); + block_node.release(); + + // Pop method and add to class + auto method_node = ctx.PopNode(); + ClassDecl* class_decl = ctx.TopNodeAs(); + if (class_decl != nullptr) { + class_decl->AddMember(std::unique_ptr(dynamic_cast(method_node.release()))); + } + } + return false; + } + + // Check if parent is CallDecl + CallDecl* call = dynamic_cast(parent_node); + if (call != nullptr && call->Body() == nullptr) { + auto block_node = ctx.PopNode(); + Block* body_block = dynamic_cast(block_node.get()); + if (body_block != nullptr) { + call->SetBody(std::unique_ptr(body_block)); + block_node.release(); + + // Pop call and add to class + auto call_node = ctx.PopNode(); + ClassDecl* class_decl = ctx.TopNodeAs(); + if (class_decl != nullptr) { + class_decl->AddMember(std::unique_ptr(dynamic_cast(call_node.release()))); + } + } + return false; + } + + // Check if parent is DestructorDecl + DestructorDecl* destructor = dynamic_cast(parent_node); + if (destructor != nullptr && destructor->Body() == nullptr) { + auto block_node = ctx.PopNode(); + Block* body_block = dynamic_cast(block_node.get()); + if (body_block != nullptr) { + destructor->SetBody(std::unique_ptr(body_block)); + block_node.release(); + + // Pop destructor and add to class + auto destructor_node = ctx.PopNode(); + ClassDecl* class_decl = ctx.TopNodeAs(); + if (class_decl != nullptr) { + class_decl->AddMember(std::unique_ptr(dynamic_cast(destructor_node.release()))); + } + } + return false; + } + } + + return false; // Block complete + } + + ctx.PushState(StateRegistry::Stmt()); + return true; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateBlock.hpp b/lib/parser/states/StateBlock.hpp new file mode 100644 index 0000000..861dff6 --- /dev/null +++ b/lib/parser/states/StateBlock.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATEBLOCK_HPP_ +#define PARSER_STATEBLOCK_HPP_ + +#include "base/StateBase.hpp" +#include "lib/parser/context/ContextParser.hpp" + +namespace ovum::compiler::parser { + +class StateBlock : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEBLOCK_HPP_ diff --git a/lib/parser/states/StateCallDeclHdr.cpp b/lib/parser/states/StateCallDeclHdr.cpp new file mode 100644 index 0000000..25b7c96 --- /dev/null +++ b/lib/parser/states/StateCallDeclHdr.cpp @@ -0,0 +1,85 @@ +#include "StateCallDeclHdr.hpp" + +#include + +#include "lib/parser/ast/nodes/class_members/CallDecl.hpp" +#include "lib/parser/ast/nodes/decls/ClassDecl.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/states/func/StateFuncParams.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +} // namespace + +std::string_view StateCallDeclHdr::Name() const { + return "CallDeclHdr"; +} + +IState::StepResult StateCallDeclHdr::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + ClassDecl* class_decl = ctx.TopNodeAs(); + if (class_decl == nullptr) { + return std::unexpected(StateError("expected ClassDecl node on stack")); + } + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in call declaration")); + } + + const Token& start = ts.Peek(); + + // Check for access modifier + bool is_public = true; + if (start.GetLexeme() == "public" || start.GetLexeme() == "private") { + is_public = (start.GetLexeme() == "public"); + ts.Consume(); + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "call") { + return std::unexpected(StateError("expected 'call' after access modifier")); + } + } + + if (ts.Peek().GetLexeme() != "call") { + return std::unexpected(StateError("expected 'call' keyword")); + } + ts.Consume(); + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "(") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_CALL_PARAMS_OPEN", "expected '(' after 'call'"); + } + return std::unexpected(StateError("expected '(' after 'call'")); + } + + SourceSpan span = StateBase::SpanFrom(start); + auto call = ctx.Factory()->MakeCallDecl(is_public, {}, nullptr, nullptr, span); + ctx.PushNode(std::unique_ptr(call.get())); + + ctx.PushState(StateRegistry::FuncParams()); + return true; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateCallDeclHdr.hpp b/lib/parser/states/StateCallDeclHdr.hpp new file mode 100644 index 0000000..57d1ad9 --- /dev/null +++ b/lib/parser/states/StateCallDeclHdr.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATECALLDECLHDR_HPP_ +#define PARSER_STATECALLDECLHDR_HPP_ + +#include "base/StateBase.hpp" +#include "lib/parser/context/ContextParser.hpp" + +namespace ovum::compiler::parser { + +class StateCallDeclHdr : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATECALLDECLHDR_HPP_ diff --git a/lib/parser/states/StateDestructorDecl.cpp b/lib/parser/states/StateDestructorDecl.cpp new file mode 100644 index 0000000..1d70bf4 --- /dev/null +++ b/lib/parser/states/StateDestructorDecl.cpp @@ -0,0 +1,125 @@ +#include "StateDestructorDecl.hpp" + +#include + +#include "lib/parser/ast/nodes/class_members/DestructorDecl.hpp" +#include "lib/parser/ast/nodes/decls/ClassDecl.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +} // namespace + +std::string_view StateDestructorDecl::Name() const { + return "DestructorDecl"; +} + +IState::StepResult StateDestructorDecl::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + ClassDecl* class_decl = ctx.TopNodeAs(); + if (class_decl == nullptr) { + return std::unexpected(StateError("expected ClassDecl node on stack")); + } + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in destructor")); + } + + const Token& start = ts.Peek(); + + // Check for access modifier + bool is_public = true; + if (start.GetLexeme() == "public" || start.GetLexeme() == "private") { + is_public = (start.GetLexeme() == "public"); + ts.Consume(); + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "destructor") { + return std::unexpected(StateError("expected 'destructor' after access modifier")); + } + } + + if (ts.Peek().GetLexeme() != "destructor") { + return std::unexpected(StateError("expected 'destructor' keyword")); + } + ts.Consume(); + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "(") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_DESTRUCTOR_PARAMS", "expected '(' after 'destructor'"); + } + return std::unexpected(StateError("expected '(' after 'destructor'")); + } + ts.Consume(); + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ")") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_DESTRUCTOR_PARAMS_CLOSE", "expected ')' after '(' in destructor"); + } + return std::unexpected(StateError("expected ')' after '(' in destructor")); + } + ts.Consume(); + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ":") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_DESTRUCTOR_RETURN", "expected ':' after destructor parameters"); + } + return std::unexpected(StateError("expected ':' after destructor parameters")); + } + ts.Consume(); + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "Void") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_DESTRUCTOR_VOID", "expected 'Void' return type for destructor"); + } + return std::unexpected(StateError("expected 'Void' return type for destructor")); + } + ts.Consume(); + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "{") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_DESTRUCTOR_BODY", "expected '{' for destructor body"); + } + return std::unexpected(StateError("expected '{' for destructor body")); + } + + ts.Consume(); + auto body = ctx.Factory()->MakeBlock({}, SourceSpan{}); + ctx.PushNode(std::unique_ptr(body.get())); + + SourceSpan span = StateBase::SpanFrom(start); + auto destructor = ctx.Factory()->MakeDestructor(is_public, std::unique_ptr(body), span); + body.release(); + ctx.PushNode(std::unique_ptr(destructor.get())); + + ctx.PushState(StateRegistry::Block()); + return true; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateDestructorDecl.hpp b/lib/parser/states/StateDestructorDecl.hpp new file mode 100644 index 0000000..acac97d --- /dev/null +++ b/lib/parser/states/StateDestructorDecl.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATEDESTRUCTORDECL_HPP_ +#define PARSER_STATEDESTRUCTORDECL_HPP_ + +#include "base/StateBase.hpp" +#include "lib/parser/context/ContextParser.hpp" + +namespace ovum::compiler::parser { + +class StateDestructorDecl : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEDESTRUCTORDECL_HPP_ diff --git a/lib/parser/states/StateExpr.cpp b/lib/parser/states/StateExpr.cpp new file mode 100644 index 0000000..b3aceb2 --- /dev/null +++ b/lib/parser/states/StateExpr.cpp @@ -0,0 +1,29 @@ +#include "StateExpr.hpp" + +#include + +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +std::string_view StateExpr::Name() const { + return "Expr"; +} + +IState::StepResult StateExpr::TryStep(ContextParser& ctx, ITokenStream& ts) const { + if (ctx.Expr() == nullptr) { + return std::unexpected(StateError("expression parser not available")); + } + + auto expr = ctx.Expr()->Parse(ts, *ctx.Diags()); + if (expr == nullptr) { + return std::unexpected(StateError("failed to parse expression")); + } + + ctx.PushNode(std::move(expr)); + return false; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateExpr.hpp b/lib/parser/states/StateExpr.hpp new file mode 100644 index 0000000..44a3647 --- /dev/null +++ b/lib/parser/states/StateExpr.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATEEXPR_HPP_ +#define PARSER_STATEEXPR_HPP_ + +#include "base/StateBase.hpp" +#include "lib/parser/context/ContextParser.hpp" + +namespace ovum::compiler::parser { + +class StateExpr : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEEXPR_HPP_ diff --git a/lib/parser/states/StateFieldDecl.cpp b/lib/parser/states/StateFieldDecl.cpp new file mode 100644 index 0000000..7a55623 --- /dev/null +++ b/lib/parser/states/StateFieldDecl.cpp @@ -0,0 +1,17 @@ +#include "StateFieldDecl.hpp" + +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +std::string_view StateFieldDecl::Name() const { + return "FieldDecl"; +} + +IState::StepResult StateFieldDecl::TryStep(ContextParser& ctx, ITokenStream& ts) const { + // This state is not used - field declarations are handled directly in StateClassMember + return std::unexpected(StateError("StateFieldDecl should not be called")); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateFieldDecl.hpp b/lib/parser/states/StateFieldDecl.hpp new file mode 100644 index 0000000..aab6a9a --- /dev/null +++ b/lib/parser/states/StateFieldDecl.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATEFIELDDECL_HPP_ +#define PARSER_STATEFIELDDECL_HPP_ + +#include "base/StateBase.hpp" +#include "lib/parser/context/ContextParser.hpp" + +namespace ovum::compiler::parser { + +class StateFieldDecl : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEFIELDDECL_HPP_ diff --git a/lib/parser/states/StateForHead.cpp b/lib/parser/states/StateForHead.cpp new file mode 100644 index 0000000..66bcd11 --- /dev/null +++ b/lib/parser/states/StateForHead.cpp @@ -0,0 +1,153 @@ +#include "StateForHead.hpp" + +#include +#include + +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/ast/nodes/stmts/ForStmt.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" +#include "lib/parser/tokens/token_traits/MatchIdentifier.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +bool IsIdentifier(const Token& token) { + MatchIdentifier matcher; + return matcher.TryMatch(token); +} + +std::string ReadIdentifier(ContextParser& ctx, ITokenStream& ts, + std::string_view code, std::string_view message) { + SkipTrivia(ts); + if (ts.IsEof() || !IsIdentifier(ts.Peek())) { + if (ctx.Diags() != nullptr) { + const Token* tok = ts.TryPeek(); + if (tok != nullptr) { + SourceSpan span = StateBase::SpanFrom(*tok); + ctx.Diags()->Error(code, message, span); + } else { + ctx.Diags()->Error(code, message); + } + } + return ""; + } + std::string name = ts.Consume()->GetLexeme(); + return name; +} + +std::unique_ptr ParseExpression(ContextParser& ctx, ITokenStream& ts) { + if (ctx.Expr() == nullptr) { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_EXPR_PARSER", "expression parser not available"); + } + return nullptr; + } + return ctx.Expr()->Parse(ts, *ctx.Diags()); +} + +} // namespace + +std::string_view StateForHead::Name() const { + return "ForHead"; +} + +IState::StepResult StateForHead::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + Block* block = ctx.TopNodeAs(); + if (block == nullptr) { + return std::unexpected(StateError("expected Block node on stack")); + } + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in for statement")); + } + + const Token& start = ts.Peek(); + if (start.GetLexeme() != "for") { + return std::unexpected(StateError("expected 'for' keyword")); + } + ts.Consume(); + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "(") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_FOR_OPEN", "expected '(' after 'for'"); + } + return std::unexpected(StateError("expected '(' after 'for'")); + } + ts.Consume(); + + SkipTrivia(ts); + std::string iter_name = ReadIdentifier(ctx, ts, "P_FOR_ITER", "expected iterator name"); + if (iter_name.empty()) { + return std::unexpected(StateError("expected iterator name")); + } + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "in") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_FOR_IN", "expected 'in' after iterator name"); + } + return std::unexpected(StateError("expected 'in' after iterator name")); + } + ts.Consume(); + + SkipTrivia(ts); + auto iter_expr = ParseExpression(ctx, ts); + if (iter_expr == nullptr) { + return std::unexpected(StateError("failed to parse iterator expression")); + } + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ")") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_FOR_CLOSE", "expected ')' after iterator expression"); + } + return std::unexpected(StateError("expected ')' after iterator expression")); + } + ts.Consume(); + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "{") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_FOR_BLOCK", "expected '{' for for body"); + } + return std::unexpected(StateError("expected '{' for for body")); + } + + ts.Consume(); + auto body = ctx.Factory()->MakeBlock({}, SourceSpan{}); + ctx.PushNode(std::unique_ptr(body.get())); + + SourceSpan span = StateBase::Union(StateBase::SpanFrom(start), iter_expr->Span()); + auto for_stmt = ctx.Factory()->MakeForStmt(std::move(iter_name), std::move(iter_expr), + std::unique_ptr(body), span); + body.release(); + + block->Append(std::move(for_stmt)); + ctx.PushState(StateRegistry::Block()); + return true; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateForHead.hpp b/lib/parser/states/StateForHead.hpp new file mode 100644 index 0000000..c536be3 --- /dev/null +++ b/lib/parser/states/StateForHead.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATEFORHEAD_HPP_ +#define PARSER_STATEFORHEAD_HPP_ + +#include "base/StateBase.hpp" +#include "lib/parser/context/ContextParser.hpp" + +namespace ovum::compiler::parser { + +class StateForHead : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEFORHEAD_HPP_ diff --git a/lib/parser/states/StateGlobalVarDecl.cpp b/lib/parser/states/StateGlobalVarDecl.cpp new file mode 100644 index 0000000..186cea8 --- /dev/null +++ b/lib/parser/states/StateGlobalVarDecl.cpp @@ -0,0 +1,17 @@ +#include "StateGlobalVarDecl.hpp" + +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +std::string_view StateGlobalVarDecl::Name() const { + return "GlobalVarDecl"; +} + +IState::StepResult StateGlobalVarDecl::TryStep(ContextParser& ctx, ITokenStream& ts) const { + // This state is not used - global variable declarations are handled directly in StateTopDecl + return std::unexpected(StateError("StateGlobalVarDecl should not be called")); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateGlobalVarDecl.hpp b/lib/parser/states/StateGlobalVarDecl.hpp new file mode 100644 index 0000000..a79b2cc --- /dev/null +++ b/lib/parser/states/StateGlobalVarDecl.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATEGLOBALVARDECL_HPP_ +#define PARSER_STATEGLOBALVARDECL_HPP_ + +#include "base/StateBase.hpp" +#include "lib/parser/context/ContextParser.hpp" + +namespace ovum::compiler::parser { + +class StateGlobalVarDecl : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEGLOBALVARDECL_HPP_ diff --git a/lib/parser/states/StateIfHead.cpp b/lib/parser/states/StateIfHead.cpp new file mode 100644 index 0000000..430f145 --- /dev/null +++ b/lib/parser/states/StateIfHead.cpp @@ -0,0 +1,123 @@ +#include "StateIfHead.hpp" + +#include + +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/ast/nodes/stmts/IfStmt.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +std::unique_ptr ParseExpression(ContextParser& ctx, ITokenStream& ts) { + if (ctx.Expr() == nullptr) { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_EXPR_PARSER", "expression parser not available"); + } + return nullptr; + } + return ctx.Expr()->Parse(ts, *ctx.Diags()); +} + +} // namespace + +std::string_view StateIfHead::Name() const { + return "IfHead"; +} + +IState::StepResult StateIfHead::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + Block* block = ctx.TopNodeAs(); + if (block == nullptr) { + return std::unexpected(StateError("expected Block node on stack")); + } + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in if statement")); + } + + const Token& start = ts.Peek(); + + // Check if we already have an IfStmt on stack + IfStmt* if_stmt = ctx.TopNodeAs(); + if (if_stmt == nullptr) { + // Create new IfStmt + auto new_if = ctx.Factory()->MakeIfStmt({}, nullptr, StateBase::SpanFrom(start)); + ctx.PushNode(std::unique_ptr(new_if.get())); + if_stmt = new_if.get(); + } + + // Parse condition + if (start.GetLexeme() == "if") { + ts.Consume(); + SkipTrivia(ts); + + if (ts.IsEof() || ts.Peek().GetLexeme() != "(") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_IF_COND_OPEN", "expected '(' after 'if'"); + } + return std::unexpected(StateError("expected '(' after 'if'")); + } + ts.Consume(); + + SkipTrivia(ts); + auto condition = ParseExpression(ctx, ts); + if (condition == nullptr) { + return std::unexpected(StateError("failed to parse if condition")); + } + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ")") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_IF_COND_CLOSE", "expected ')' after condition"); + } + return std::unexpected(StateError("expected ')' after condition")); + } + ts.Consume(); + + SkipTrivia(ts); + + // Parse then block + if (ts.IsEof() || ts.Peek().GetLexeme() != "{") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_IF_BLOCK", "expected '{' for if body"); + } + return std::unexpected(StateError("expected '{' for if body")); + } + + ts.Consume(); + auto then_block = ctx.Factory()->MakeBlock({}, SourceSpan{}); + ctx.PushNode(std::unique_ptr(then_block.get())); + ctx.PushState(StateRegistry::Block()); + + // Store condition for later + ctx.PushNode(std::unique_ptr(condition.release())); + ctx.PushState(StateRegistry::IfTail()); + return true; + } + + return std::unexpected(StateError("expected 'if' keyword")); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateIfHead.hpp b/lib/parser/states/StateIfHead.hpp new file mode 100644 index 0000000..2f629db --- /dev/null +++ b/lib/parser/states/StateIfHead.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATEIFHEAD_HPP_ +#define PARSER_STATEIFHEAD_HPP_ + +#include "base/StateBase.hpp" +#include "lib/parser/context/ContextParser.hpp" + +namespace ovum::compiler::parser { + +class StateIfHead : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEIFHEAD_HPP_ diff --git a/lib/parser/states/StateIfTail.cpp b/lib/parser/states/StateIfTail.cpp new file mode 100644 index 0000000..be07cd0 --- /dev/null +++ b/lib/parser/states/StateIfTail.cpp @@ -0,0 +1,131 @@ +#include "StateIfTail.hpp" + +#include + +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/ast/nodes/stmts/Branch.hpp" +#include "lib/parser/ast/nodes/stmts/IfStmt.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +std::unique_ptr ParseExpression(ContextParser& ctx, ITokenStream& ts) { + if (ctx.Expr() == nullptr) { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_EXPR_PARSER", "expression parser not available"); + } + return nullptr; + } + return ctx.Expr()->Parse(ts, *ctx.Diags()); +} + +} // namespace + +std::string_view StateIfTail::Name() const { + return "IfTail"; +} + +IState::StepResult StateIfTail::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + // Stack should have: [IfStmt, condition Expr, then Block] + if (ctx.NodeStack().size() < 3) { + return std::unexpected(StateError("expected IfStmt, condition, and block on stack")); + } + + // Pop then block + auto then_block_node = ctx.PopNode(); + Block* then_block = dynamic_cast(then_block_node.get()); + if (then_block == nullptr) { + return std::unexpected(StateError("expected Block on stack")); + } + + // Pop condition + auto condition_node = ctx.PopNode(); + Expr* condition = dynamic_cast(condition_node.get()); + if (condition == nullptr) { + return std::unexpected(StateError("expected Expr on stack")); + } + + // Get IfStmt + IfStmt* if_stmt = ctx.TopNodeAs(); + if (if_stmt == nullptr) { + return std::unexpected(StateError("expected IfStmt on stack")); + } + + // Add branch + Branch branch(std::unique_ptr(condition), std::unique_ptr(then_block)); + condition_node.release(); + then_block_node.release(); + if_stmt->AddBranch(std::move(branch)); + + // Check for else if or else + if (ts.IsEof()) { + // IfStmt complete, add to parent block + auto if_node = ctx.PopNode(); + Block* parent_block = ctx.TopNodeAs(); + if (parent_block != nullptr) { + parent_block->Append(std::unique_ptr(dynamic_cast(if_node.release()))); + } + return false; + } + + const Token& tok = ts.Peek(); + if (tok.GetLexeme() == "else") { + ts.Consume(); + SkipTrivia(ts); + + if (!ts.IsEof() && ts.Peek().GetLexeme() == "if") { + // else if - create new branch + ctx.PushState(StateRegistry::IfHead()); + return true; + } + + // else block + if (ts.IsEof() || ts.Peek().GetLexeme() != "{") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_ELSE_BLOCK", "expected '{' for else body"); + } + return std::unexpected(StateError("expected '{' for else body")); + } + + ts.Consume(); + auto else_block = ctx.Factory()->MakeBlock({}, SourceSpan{}); + ctx.PushNode(std::unique_ptr(else_block.get())); + ctx.PushState(StateRegistry::Block()); + + // IfStmt will be handled when else block completes + return true; + } + + // IfStmt complete, add to parent block + auto if_node = ctx.PopNode(); + Block* parent_block = ctx.TopNodeAs(); + if (parent_block != nullptr) { + parent_block->Append(std::unique_ptr(dynamic_cast(if_node.release()))); + } + return false; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateIfTail.hpp b/lib/parser/states/StateIfTail.hpp new file mode 100644 index 0000000..ed502bd --- /dev/null +++ b/lib/parser/states/StateIfTail.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATEIFTAIL_HPP_ +#define PARSER_STATEIFTAIL_HPP_ + +#include "base/StateBase.hpp" +#include "lib/parser/context/ContextParser.hpp" + +namespace ovum::compiler::parser { + +class StateIfTail : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEIFTAIL_HPP_ diff --git a/lib/parser/states/StateMethodHdr.cpp b/lib/parser/states/StateMethodHdr.cpp new file mode 100644 index 0000000..867e405 --- /dev/null +++ b/lib/parser/states/StateMethodHdr.cpp @@ -0,0 +1,154 @@ +#include "StateMethodHdr.hpp" + +#include +#include + +#include "lib/parser/ast/nodes/class_members/MethodDecl.hpp" +#include "lib/parser/ast/nodes/decls/ClassDecl.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/states/func/StateFuncParams.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" +#include "lib/parser/tokens/token_traits/MatchIdentifier.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +bool IsIdentifier(const Token& token) { + MatchIdentifier matcher; + return matcher.TryMatch(token); +} + +std::string ReadIdentifier(ContextParser& ctx, ITokenStream& ts, + std::string_view code, std::string_view message) { + SkipTrivia(ts); + if (ts.IsEof() || !IsIdentifier(ts.Peek())) { + if (ctx.Diags() != nullptr) { + const Token* tok = ts.TryPeek(); + if (tok != nullptr) { + SourceSpan span = StateBase::SpanFrom(*tok); + ctx.Diags()->Error(code, message, span); + } else { + ctx.Diags()->Error(code, message); + } + } + return ""; + } + std::string name = ts.Consume()->GetLexeme(); + return name; +} + +} // namespace + +std::string_view StateMethodHdr::Name() const { + return "MethodHdr"; +} + +IState::StepResult StateMethodHdr::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + ClassDecl* class_decl = ctx.TopNodeAs(); + if (class_decl == nullptr) { + return std::unexpected(StateError("expected ClassDecl node on stack")); + } + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in method header")); + } + + const Token& start = ts.Peek(); + + // Check for access modifier, override, pure, static + bool is_public = true; + bool is_override = false; + bool is_pure = false; + bool is_static = false; + + std::string lex = start.GetLexeme(); + if (lex == "public" || lex == "private") { + is_public = (lex == "public"); + ts.Consume(); + SkipTrivia(ts); + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file after access modifier")); + } + lex = ts.Peek().GetLexeme(); + } + + if (lex == "override") { + is_override = true; + ts.Consume(); + SkipTrivia(ts); + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file after 'override'")); + } + lex = ts.Peek().GetLexeme(); + } + + if (lex == "pure") { + is_pure = true; + ts.Consume(); + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "fun") { + return std::unexpected(StateError("expected 'fun' after 'pure'")); + } + lex = "fun"; + } + + if (lex == "static") { + is_static = true; + ts.Consume(); + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "fun") { + return std::unexpected(StateError("expected 'fun' after 'static'")); + } + lex = "fun"; + } + + if (lex != "fun") { + return std::unexpected(StateError("expected 'fun' keyword")); + } + ts.Consume(); + + SkipTrivia(ts); + std::string name = ReadIdentifier(ctx, ts, "P_METHOD_NAME", "expected method name"); + if (name.empty()) { + return std::unexpected(StateError("expected method name")); + } + + SourceSpan span = StateBase::SpanFrom(start); + auto method = ctx.Factory()->MakeMethod(is_public, is_override, is_static, is_pure, + std::move(name), {}, nullptr, nullptr, span); + ctx.PushNode(std::unique_ptr(method.get())); + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "(") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_METHOD_PARAMS_OPEN", "expected '(' after method name"); + } + return std::unexpected(StateError("expected '(' after method name")); + } + + ctx.PushState(StateRegistry::FuncParams()); + return true; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateMethodHdr.hpp b/lib/parser/states/StateMethodHdr.hpp new file mode 100644 index 0000000..e53ca24 --- /dev/null +++ b/lib/parser/states/StateMethodHdr.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATEMETHODHDR_HPP_ +#define PARSER_STATEMETHODHDR_HPP_ + +#include "base/StateBase.hpp" +#include "lib/parser/context/ContextParser.hpp" + +namespace ovum::compiler::parser { + +class StateMethodHdr : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEMETHODHDR_HPP_ diff --git a/lib/parser/states/StateModule.cpp b/lib/parser/states/StateModule.cpp new file mode 100644 index 0000000..6789999 --- /dev/null +++ b/lib/parser/states/StateModule.cpp @@ -0,0 +1,30 @@ +#include "lib/parser/states/StateModule.hpp" + +#include + +#include "lib/parser/ast/nodes/decls/Module.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +std::string_view StateModule::Name() const { + return "Module"; +} + +IState::StepResult StateModule::TryStep(ContextParser& context, ITokenStream& token_stream) const { + if (context.NodeStack().empty()) { + auto module = std::make_unique(); + context.PushNode(std::move(module)); + } + + if (token_stream.IsEof()) { + return false; + } + + context.PushState(StateRegistry::TopDecl()); + return true; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateModule.hpp b/lib/parser/states/StateModule.hpp new file mode 100644 index 0000000..ec15748 --- /dev/null +++ b/lib/parser/states/StateModule.hpp @@ -0,0 +1,16 @@ +#ifndef PARSER_STATEMODULE_HPP_ +#define PARSER_STATEMODULE_HPP_ + +#include "lib/parser/states/base/StateBase.hpp" + +namespace ovum::compiler::parser { + +class StateModule : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& context, ITokenStream& token_stream) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEMODULE_HPP_ diff --git a/lib/parser/states/StateParseType.cpp b/lib/parser/states/StateParseType.cpp new file mode 100644 index 0000000..4e0620f --- /dev/null +++ b/lib/parser/states/StateParseType.cpp @@ -0,0 +1,30 @@ +#include "StateParseType.hpp" + +#include + +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +std::string_view StateParseType::Name() const { + return "ParseType"; +} + +IState::StepResult StateParseType::TryStep(ContextParser& ctx, ITokenStream& ts) const { + if (ctx.TypeParser() == nullptr) { + return std::unexpected(StateError("type parser not available")); + } + + auto type = ctx.TypeParser()->ParseType(ts, *ctx.Diags()); + if (type == nullptr) { + return std::unexpected(StateError("failed to parse type")); + } + + // TypeReference is not an AstNode, so we can't push it to the stack + // This state is typically used in contexts where the type is consumed immediately + return false; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateParseType.hpp b/lib/parser/states/StateParseType.hpp new file mode 100644 index 0000000..eaf7814 --- /dev/null +++ b/lib/parser/states/StateParseType.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATEPARSETYPE_HPP_ +#define PARSER_STATEPARSETYPE_HPP_ + +#include "base/StateBase.hpp" +#include "lib/parser/context/ContextParser.hpp" + +namespace ovum::compiler::parser { + +class StateParseType : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEPARSETYPE_HPP_ diff --git a/lib/parser/states/StateReturnTail.cpp b/lib/parser/states/StateReturnTail.cpp new file mode 100644 index 0000000..de3d51d --- /dev/null +++ b/lib/parser/states/StateReturnTail.cpp @@ -0,0 +1,103 @@ +#include "StateReturnTail.hpp" + +#include + +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/ast/nodes/stmts/ReturnStmt.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +void ConsumeTerminators(ITokenStream& ts) { + SkipTrivia(ts, false); + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "NEWLINE") { + ts.Consume(); + continue; + } + if (t.GetLexeme() == ";") { + ts.Consume(); + continue; + } + break; + } +} + +std::unique_ptr ParseExpression(ContextParser& ctx, ITokenStream& ts) { + if (ctx.Expr() == nullptr) { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_EXPR_PARSER", "expression parser not available"); + } + return nullptr; + } + return ctx.Expr()->Parse(ts, *ctx.Diags()); +} + +} // namespace + +std::string_view StateReturnTail::Name() const { + return "ReturnTail"; +} + +IState::StepResult StateReturnTail::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + Block* block = ctx.TopNodeAs(); + if (block == nullptr) { + return std::unexpected(StateError("expected Block node on stack")); + } + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in return statement")); + } + + const Token& start = ts.Peek(); + SourceSpan span = StateBase::SpanFrom(start); + + SkipTrivia(ts); + + // Check if there's a return value + if (ts.IsEof() || (ts.Peek().GetLexeme() == ";" || ts.Peek().GetStringType() == "NEWLINE")) { + // Return without value + auto stmt = ctx.Factory()->MakeReturnStmt(nullptr, span); + block->Append(std::move(stmt)); + ConsumeTerminators(ts); + return false; + } + + // Return with value + auto expr = ParseExpression(ctx, ts); + if (expr == nullptr) { + return std::unexpected(StateError("failed to parse return expression")); + } + + span = StateBase::Union(span, expr->Span()); + auto stmt = ctx.Factory()->MakeReturnStmt(std::move(expr), span); + block->Append(std::move(stmt)); + ConsumeTerminators(ts); + return false; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateReturnTail.hpp b/lib/parser/states/StateReturnTail.hpp new file mode 100644 index 0000000..968bb8a --- /dev/null +++ b/lib/parser/states/StateReturnTail.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATERETURNTAIL_HPP_ +#define PARSER_STATERETURNTAIL_HPP_ + +#include "base/StateBase.hpp" +#include "lib/parser/context/ContextParser.hpp" + +namespace ovum::compiler::parser { + +class StateReturnTail : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATERETURNTAIL_HPP_ diff --git a/lib/parser/states/StateStmt.cpp b/lib/parser/states/StateStmt.cpp new file mode 100644 index 0000000..01d44c8 --- /dev/null +++ b/lib/parser/states/StateStmt.cpp @@ -0,0 +1,230 @@ +#include "StateStmt.hpp" + +#include + +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" +#include "lib/parser/tokens/token_traits/MatchIdentifier.hpp" +#include "lib/parser/tokens/token_traits/MatchLexeme.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +bool IsIdentifier(const Token& token) { + MatchIdentifier matcher; + return matcher.TryMatch(token); +} + +void ReportUnexpected(IDiagnosticSink* diags, std::string_view code, + std::string_view message, const Token* tok) { + if (diags == nullptr || tok == nullptr) { + return; + } + SourceSpan span = StateBase::SpanFrom(*tok); + diags->Error(code, message, span); +} + +std::string ReadIdentifier(ContextParser& ctx, ITokenStream& ts, + std::string_view code, std::string_view message) { + SkipTrivia(ts); + if (ts.IsEof() || !IsIdentifier(ts.Peek())) { + const Token* tok = ts.TryPeek(); + ReportUnexpected(ctx.Diags(), code, message, tok); + return ""; + } + std::string name = ts.Consume()->GetLexeme(); + return name; +} + +std::unique_ptr ParseType(ContextParser& ctx, ITokenStream& ts) { + if (ctx.TypeParser() == nullptr) { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_TYPE_PARSER", "type parser not available"); + } + return nullptr; + } + return ctx.TypeParser()->ParseType(ts, *ctx.Diags()); +} + +std::unique_ptr ParseExpression(ContextParser& ctx, ITokenStream& ts) { + if (ctx.Expr() == nullptr) { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_EXPR_PARSER", "expression parser not available"); + } + return nullptr; + } + return ctx.Expr()->Parse(ts, *ctx.Diags()); +} + +void ConsumeTerminators(ITokenStream& ts) { + SkipTrivia(ts, false); + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "NEWLINE") { + ts.Consume(); + continue; + } + if (t.GetLexeme() == ";") { + ts.Consume(); + continue; + } + break; + } +} + +} // namespace + +std::string_view StateStmt::Name() const { + return "Stmt"; +} + +IState::StepResult StateStmt::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + if (ts.IsEof()) { + return false; + } + + Block* block = ctx.TopNodeAs(); + if (block == nullptr) { + return std::unexpected(StateError("expected Block node on stack")); + } + + const Token& tok = ts.Peek(); + std::string lex = tok.GetLexeme(); + + if (lex == "if") { + ctx.PushState(StateRegistry::IfHead()); + return true; + } + + if (lex == "while") { + ctx.PushState(StateRegistry::WhileHead()); + return true; + } + + if (lex == "for") { + ctx.PushState(StateRegistry::ForHead()); + return true; + } + + if (lex == "return") { + ctx.PushState(StateRegistry::ReturnTail()); + return true; + } + + if (lex == "break") { + ts.Consume(); + SourceSpan span = StateBase::SpanFrom(tok); + auto stmt = ctx.Factory()->MakeBreakStmt(span); + block->Append(std::move(stmt)); + ConsumeTerminators(ts); + return false; + } + + if (lex == "continue") { + ts.Consume(); + SourceSpan span = StateBase::SpanFrom(tok); + auto stmt = ctx.Factory()->MakeContinueStmt(span); + block->Append(std::move(stmt)); + ConsumeTerminators(ts); + return false; + } + + if (lex == "unsafe") { + ctx.PushState(StateRegistry::UnsafeBlock()); + return true; + } + + if (lex == "{") { + ctx.PushState(StateRegistry::Block()); + return true; + } + + // Try variable declaration: [var] identifier : type = expression + bool is_var = false; + if (lex == "var" || lex == "val") { + is_var = (lex == "var"); + ts.Consume(); + SkipTrivia(ts); + if (ts.IsEof()) { + ReportUnexpected(ctx.Diags(), "P_VAR_NAME", "expected variable name", ts.TryPeek()); + return std::unexpected(StateError("expected variable name")); + } + lex = ts.Peek().GetLexeme(); + } + + if (IsIdentifier(ts.Peek())) { + std::string name = ts.Consume()->GetLexeme(); + SkipTrivia(ts); + + if (!ts.IsEof() && ts.Peek().GetLexeme() == ":") { + // Variable declaration + ts.Consume(); + SkipTrivia(ts); + + auto type = ParseType(ctx, ts); + if (type == nullptr) { + return std::unexpected(StateError("failed to parse type")); + } + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "=") { + ReportUnexpected(ctx.Diags(), "P_VAR_INIT", "expected '=' for variable", ts.TryPeek()); + return std::unexpected(StateError("expected '=' for variable")); + } + ts.Consume(); + + SkipTrivia(ts); + auto init = ParseExpression(ctx, ts); + if (init == nullptr) { + return std::unexpected(StateError("failed to parse initialization expression")); + } + + SourceSpan span = StateBase::Union(StateBase::SpanFrom(tok), init->Span()); + auto stmt = ctx.Factory()->MakeVarDeclStmt(is_var, std::move(name), std::move(*type), + std::move(init), span); + block->Append(std::move(stmt)); + ConsumeTerminators(ts); + return false; + } + + // Expression statement - put identifier back + ts.Rewind(1); + } + + // Expression statement + auto expr = ParseExpression(ctx, ts); + if (expr == nullptr) { + return std::unexpected(StateError("failed to parse expression")); + } + + SourceSpan span = expr->Span(); + auto stmt = ctx.Factory()->MakeExprStmt(std::move(expr), span); + block->Append(std::move(stmt)); + ConsumeTerminators(ts); + return false; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateStmt.hpp b/lib/parser/states/StateStmt.hpp new file mode 100644 index 0000000..c30a9ff --- /dev/null +++ b/lib/parser/states/StateStmt.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATESTMT_HPP_ +#define PARSER_STATESTMT_HPP_ + +#include "base/StateBase.hpp" +#include "lib/parser/context/ContextParser.hpp" + +namespace ovum::compiler::parser { + +class StateStmt : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATESTMT_HPP_ diff --git a/lib/parser/states/StateSyncToBlockEnd.cpp b/lib/parser/states/StateSyncToBlockEnd.cpp new file mode 100644 index 0000000..d46c0fe --- /dev/null +++ b/lib/parser/states/StateSyncToBlockEnd.cpp @@ -0,0 +1,19 @@ +#include "StateSyncToBlockEnd.hpp" + +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/recovery/SimpleRecovery.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +std::string_view StateSyncToBlockEnd::Name() const { + return "SyncToBlockEnd"; +} + +IState::StepResult StateSyncToBlockEnd::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SimpleRecovery recovery; + recovery.SyncToBlockEnd(ts); + return false; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateSyncToBlockEnd.hpp b/lib/parser/states/StateSyncToBlockEnd.hpp new file mode 100644 index 0000000..75f95f2 --- /dev/null +++ b/lib/parser/states/StateSyncToBlockEnd.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATESYNCTOBLOCKEND_HPP_ +#define PARSER_STATESYNCTOBLOCKEND_HPP_ + +#include "base/StateBase.hpp" +#include "lib/parser/context/ContextParser.hpp" + +namespace ovum::compiler::parser { + +class StateSyncToBlockEnd : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATESYNCTOBLOCKEND_HPP_ diff --git a/lib/parser/states/StateSyncToStmtEnd.cpp b/lib/parser/states/StateSyncToStmtEnd.cpp new file mode 100644 index 0000000..0f9f863 --- /dev/null +++ b/lib/parser/states/StateSyncToStmtEnd.cpp @@ -0,0 +1,19 @@ +#include "StateSyncToStmtEnd.hpp" + +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/recovery/SimpleRecovery.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +std::string_view StateSyncToStmtEnd::Name() const { + return "SyncToStmtEnd"; +} + +IState::StepResult StateSyncToStmtEnd::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SimpleRecovery recovery; + recovery.SyncToStmtEnd(ts); + return false; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateSyncToStmtEnd.hpp b/lib/parser/states/StateSyncToStmtEnd.hpp new file mode 100644 index 0000000..900a00f --- /dev/null +++ b/lib/parser/states/StateSyncToStmtEnd.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATESYNCTOSTMTEND_HPP_ +#define PARSER_STATESYNCTOSTMTEND_HPP_ + +#include "base/StateBase.hpp" +#include "lib/parser/context/ContextParser.hpp" + +namespace ovum::compiler::parser { + +class StateSyncToStmtEnd : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATESYNCTOSTMTEND_HPP_ diff --git a/lib/parser/states/StateTopDecl.cpp b/lib/parser/states/StateTopDecl.cpp new file mode 100644 index 0000000..a92ce5c --- /dev/null +++ b/lib/parser/states/StateTopDecl.cpp @@ -0,0 +1,312 @@ +#include "StateTopDecl.hpp" + +#include +#include +#include + +#include "lib/parser/ast/nodes/decls/ClassDecl.hpp" +#include "lib/parser/ast/nodes/decls/FunctionDecl.hpp" +#include "lib/parser/ast/nodes/decls/GlobalVarDecl.hpp" +#include "lib/parser/ast/nodes/decls/InterfaceDecl.hpp" +#include "lib/parser/ast/nodes/decls/Module.hpp" +#include "lib/parser/ast/nodes/decls/TypeAliasDecl.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" +#include "lib/parser/tokens/token_traits/MatchIdentifier.hpp" +#include "lib/parser/tokens/token_traits/MatchLexeme.hpp" +#include "lib/parser/tokens/token_traits/MatchType.hpp" +#include "lib/parser/types/Param.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +SourceSpan SpanFrom(const Token& token) { + return StateBase::SpanFrom(token); +} + +SourceSpan Union(const SourceSpan& a, const SourceSpan& b) { + return StateBase::Union(a, b); +} + +bool IsIdentifier(const Token& token) { + MatchIdentifier matcher; + return matcher.TryMatch(token); +} + +bool MatchLexeme(ITokenStream& ts, std::string_view lex) { + if (ts.IsEof()) { + return false; + } + MatchLexeme matcher(lex); + if (matcher.TryMatch(ts.Peek())) { + ts.Consume(); + return true; + } + return false; +} + +void ReportUnexpected(IDiagnosticSink* diags, std::string_view code, + std::string_view message, const Token* tok) { + if (diags == nullptr) { + return; + } + std::optional span; + if (tok != nullptr) { + span = SpanFrom(*tok); + } + diags->Error(code, message, span); +} + +std::string ReadIdentifier(ContextParser& ctx, ITokenStream& ts, + std::string_view code, std::string_view message) { + SkipTrivia(ts); + if (ts.IsEof() || !IsIdentifier(ts.Peek())) { + const Token* tok = ts.TryPeek(); + ReportUnexpected(ctx.Diags(), code, message, tok); + return ""; + } + std::string name = ts.Consume()->GetLexeme(); + return name; +} + +std::unique_ptr ParseType(ContextParser& ctx, ITokenStream& ts) { + if (ctx.TypeParser() == nullptr) { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_TYPE_PARSER", "type parser not available"); + } + return nullptr; + } + return ctx.TypeParser()->ParseType(ts, *ctx.Diags()); +} + +std::unique_ptr ParseExpression(ContextParser& ctx, ITokenStream& ts) { + if (ctx.Expr() == nullptr) { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_EXPR_PARSER", "expression parser not available"); + } + return nullptr; + } + return ctx.Expr()->Parse(ts, *ctx.Diags()); +} + +void ConsumeTerminators(ITokenStream& ts) { + SkipTrivia(ts, false); + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "NEWLINE") { + ts.Consume(); + continue; + } + if (t.GetLexeme() == ";") { + ts.Consume(); + continue; + } + break; + } +} + +std::vector ParseParamList(ContextParser& ctx, ITokenStream& ts) { + std::vector params; + SkipTrivia(ts); + + if (ts.IsEof() || ts.Peek().GetLexeme() == ")") { + return params; + } + + while (true) { + SkipTrivia(ts); + bool is_var = false; + if (!ts.IsEof() && ts.Peek().GetLexeme() == "var") { + ts.Consume(); + is_var = true; + SkipTrivia(ts); + } + + std::string name = ReadIdentifier(ctx, ts, "P_PARAM_NAME", "expected parameter name"); + if (name.empty()) { + break; + } + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ":") { + ReportUnexpected(ctx.Diags(), "P_PARAM_COLON", "expected ':' after parameter name", + ts.TryPeek()); + break; + } + ts.Consume(); + + SkipTrivia(ts); + auto type = ParseType(ctx, ts); + if (type == nullptr) { + break; + } + + params.emplace_back(name, std::move(*type)); + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ",") { + break; + } + ts.Consume(); + } + + return params; +} + +std::unique_ptr ParseBlock(ContextParser& ctx, ITokenStream& ts) { + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "{") { + const Token* tok = ts.TryPeek(); + ReportUnexpected(ctx.Diags(), "P_BLOCK_OPEN", "expected '{'", tok); + return nullptr; + } + ts.Consume(); + + auto block = ctx.Factory()->MakeBlock({}, SourceSpan{}); + ctx.PushNode(std::unique_ptr(block.get())); + ctx.PushState(StateRegistry::Block()); + return nullptr; // Will be popped later +} + +std::unique_ptr WrapSingleStmt(ContextParser& ctx, std::unique_ptr stmt) { + std::vector> stmts; + stmts.push_back(std::move(stmt)); + SourceSpan span = stmt ? stmt->Span() : SourceSpan{}; + return ctx.Factory()->MakeBlock(std::move(stmts), span); +} + +} // namespace + +std::string_view StateTopDecl::Name() const { + return "TopDecl"; +} + +IState::StepResult StateTopDecl::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + if (ts.IsEof()) { + return false; + } + + Module* module = ctx.TopNodeAs(); + if (module == nullptr) { + return std::unexpected(StateError("expected Module node on stack")); + } + + const Token& start = ts.Peek(); + std::string lex = start.GetLexeme(); + SourceSpan span = SpanFrom(start); + + // Check for pure fun + bool is_pure = false; + if (lex == "pure") { + ts.Consume(); + SkipTrivia(ts); + if (ts.IsEof()) { + ReportUnexpected(ctx.Diags(), "P_PURE_FUN", "expected 'fun' after 'pure'", ts.TryPeek()); + return std::unexpected(StateError("expected 'fun' after 'pure'")); + } + if (ts.Peek().GetLexeme() != "fun") { + ReportUnexpected(ctx.Diags(), "P_PURE_FUN", "expected 'fun' after 'pure'", &ts.Peek()); + return std::unexpected(StateError("expected 'fun' after 'pure'")); + } + is_pure = true; + lex = "fun"; + } + + if (lex == "fun") { + ctx.PushState(StateRegistry::FuncHdr()); + return true; + } + + if (lex == "class") { + ctx.PushState(StateRegistry::ClassHdr()); + return true; + } + + if (lex == "interface") { + ctx.PushState(StateRegistry::InterfaceHdr()); + return true; + } + + if (lex == "typealias") { + ctx.PushState(StateRegistry::TypeAliasDecl()); + return true; + } + + if (lex == "var" || lex == "val") { + bool is_var = (lex == "var"); + ts.Consume(); + SkipTrivia(ts); + + std::string name = ReadIdentifier(ctx, ts, "P_GLOBAL_VAR_NAME", "expected variable name"); + if (name.empty()) { + return std::unexpected(StateError("expected variable name")); + } + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ":") { + ReportUnexpected(ctx.Diags(), "P_GLOBAL_VAR_COLON", "expected ':' after variable name", + ts.TryPeek()); + return std::unexpected(StateError("expected ':' after variable name")); + } + ts.Consume(); + + SkipTrivia(ts); + auto type = ParseType(ctx, ts); + if (type == nullptr) { + return std::unexpected(StateError("failed to parse type")); + } + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "=") { + ReportUnexpected(ctx.Diags(), "P_GLOBAL_VAR_INIT", "expected '=' for global variable", + ts.TryPeek()); + return std::unexpected(StateError("expected '=' for global variable")); + } + ts.Consume(); + + SkipTrivia(ts); + auto init = ParseExpression(ctx, ts); + if (init == nullptr) { + return std::unexpected(StateError("failed to parse initialization expression")); + } + + span = Union(span, init->Span()); + auto decl = ctx.Factory()->MakeGlobalVar(is_var, std::move(name), std::move(*type), + std::move(init), span); + module->AddDecl(std::move(decl)); + + ConsumeTerminators(ts); + return true; + } + + const Token* tok = ts.TryPeek(); + ReportUnexpected(ctx.Diags(), "P_TOP_DECL", "expected top-level declaration", tok); + if (tok != nullptr) { + ts.Consume(); + } + return true; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateTopDecl.hpp b/lib/parser/states/StateTopDecl.hpp new file mode 100644 index 0000000..937fa0d --- /dev/null +++ b/lib/parser/states/StateTopDecl.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATETOPDECL_HPP_ +#define PARSER_STATETOPDECL_HPP_ + +#include "base/StateBase.hpp" +#include "lib/parser/context/ContextParser.hpp" + +namespace ovum::compiler::parser { + +class StateTopDecl : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATETOPDECL_HPP_ diff --git a/lib/parser/states/StateTypeAliasDecl.cpp b/lib/parser/states/StateTypeAliasDecl.cpp new file mode 100644 index 0000000..0b0ff52 --- /dev/null +++ b/lib/parser/states/StateTypeAliasDecl.cpp @@ -0,0 +1,130 @@ +#include "StateTypeAliasDecl.hpp" + +#include +#include + +#include "lib/parser/ast/nodes/decls/Module.hpp" +#include "lib/parser/ast/nodes/decls/TypeAliasDecl.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" +#include "lib/parser/tokens/token_traits/MatchIdentifier.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +bool IsIdentifier(const Token& token) { + MatchIdentifier matcher; + return matcher.TryMatch(token); +} + +std::string ReadIdentifier(ContextParser& ctx, ITokenStream& ts, + std::string_view code, std::string_view message) { + SkipTrivia(ts); + if (ts.IsEof() || !IsIdentifier(ts.Peek())) { + if (ctx.Diags() != nullptr) { + const Token* tok = ts.TryPeek(); + if (tok != nullptr) { + SourceSpan span = StateBase::SpanFrom(*tok); + ctx.Diags()->Error(code, message, span); + } else { + ctx.Diags()->Error(code, message); + } + } + return ""; + } + std::string name = ts.Consume()->GetLexeme(); + return name; +} + +void ConsumeTerminators(ITokenStream& ts) { + SkipTrivia(ts, false); + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "NEWLINE") { + ts.Consume(); + continue; + } + if (t.GetLexeme() == ";") { + ts.Consume(); + continue; + } + break; + } +} + +} // namespace + +std::string_view StateTypeAliasDecl::Name() const { + return "TypeAliasDecl"; +} + +IState::StepResult StateTypeAliasDecl::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + Module* module = ctx.TopNodeAs(); + if (module == nullptr) { + return std::unexpected(StateError("expected Module node on stack")); + } + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in type alias")); + } + + const Token& start = ts.Peek(); + if (start.GetLexeme() != "typealias") { + return std::unexpected(StateError("expected 'typealias' keyword")); + } + ts.Consume(); + + SkipTrivia(ts); + std::string name = ReadIdentifier(ctx, ts, "P_TYPEALIAS_NAME", "expected type alias name"); + if (name.empty()) { + return std::unexpected(StateError("expected type alias name")); + } + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "=") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_TYPEALIAS_EQ", "expected '=' after type alias name"); + } + return std::unexpected(StateError("expected '=' after type alias name")); + } + ts.Consume(); + + SkipTrivia(ts); + auto aliased_type = ctx.TypeParser()->ParseType(ts, *ctx.Diags()); + if (aliased_type == nullptr) { + return std::unexpected(StateError("failed to parse aliased type")); + } + + SourceSpan span = StateBase::SpanFrom(start); + if (ts.LastConsumed() != nullptr) { + span = StateBase::Union(span, StateBase::SpanFrom(*ts.LastConsumed())); + } + auto type_alias = ctx.Factory()->MakeTypeAlias(std::move(name), std::move(*aliased_type), span); + module->AddDecl(std::move(type_alias)); + + ConsumeTerminators(ts); + return false; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateTypeAliasDecl.hpp b/lib/parser/states/StateTypeAliasDecl.hpp new file mode 100644 index 0000000..818386d --- /dev/null +++ b/lib/parser/states/StateTypeAliasDecl.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATETYPEALIASDECL_HPP_ +#define PARSER_STATETYPEALIASDECL_HPP_ + +#include "lib/parser/states/base/StateBase.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +class StateTypeAliasDecl : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATETYPEALIASDECL_HPP_ diff --git a/lib/parser/states/StateUnsafeBlock.cpp b/lib/parser/states/StateUnsafeBlock.cpp new file mode 100644 index 0000000..231d776 --- /dev/null +++ b/lib/parser/states/StateUnsafeBlock.cpp @@ -0,0 +1,77 @@ +#include "StateUnsafeBlock.hpp" + +#include + +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/ast/nodes/stmts/UnsafeBlock.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +} // namespace + +std::string_view StateUnsafeBlock::Name() const { + return "UnsafeBlock"; +} + +IState::StepResult StateUnsafeBlock::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + Block* block = ctx.TopNodeAs(); + if (block == nullptr) { + return std::unexpected(StateError("expected Block node on stack")); + } + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in unsafe block")); + } + + const Token& start = ts.Peek(); + if (start.GetLexeme() != "unsafe") { + return std::unexpected(StateError("expected 'unsafe' keyword")); + } + ts.Consume(); + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "{") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_UNSAFE_BLOCK", "expected '{' after 'unsafe'"); + } + return std::unexpected(StateError("expected '{' after 'unsafe'")); + } + + ts.Consume(); + auto unsafe_body = ctx.Factory()->MakeBlock({}, SourceSpan{}); + ctx.PushNode(std::unique_ptr(unsafe_body.get())); + + SourceSpan span = StateBase::SpanFrom(start); + auto unsafe_stmt = ctx.Factory()->MakeUnsafeBlock(std::unique_ptr(unsafe_body), span); + unsafe_body.release(); + + block->Append(std::move(unsafe_stmt)); + ctx.PushState(StateRegistry::Block()); + return true; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateUnsafeBlock.hpp b/lib/parser/states/StateUnsafeBlock.hpp new file mode 100644 index 0000000..4913b0b --- /dev/null +++ b/lib/parser/states/StateUnsafeBlock.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATEUNSAFEBLOCK_HPP_ +#define PARSER_STATEUNSAFEBLOCK_HPP_ + +#include "base/StateBase.hpp" +#include "lib/parser/context/ContextParser.hpp" + +namespace ovum::compiler::parser { + +class StateUnsafeBlock : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEUNSAFEBLOCK_HPP_ diff --git a/lib/parser/states/StateVarDeclTail.cpp b/lib/parser/states/StateVarDeclTail.cpp new file mode 100644 index 0000000..77fe769 --- /dev/null +++ b/lib/parser/states/StateVarDeclTail.cpp @@ -0,0 +1,17 @@ +#include "StateVarDeclTail.hpp" + +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +std::string_view StateVarDeclTail::Name() const { + return "VarDeclTail"; +} + +IState::StepResult StateVarDeclTail::TryStep(ContextParser& ctx, ITokenStream& ts) const { + // This state is not used - variable declarations are handled directly in StateStmt + return std::unexpected(StateError("StateVarDeclTail should not be called")); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateVarDeclTail.hpp b/lib/parser/states/StateVarDeclTail.hpp new file mode 100644 index 0000000..b7fe7fe --- /dev/null +++ b/lib/parser/states/StateVarDeclTail.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATEVARDECLTAIL_HPP_ +#define PARSER_STATEVARDECLTAIL_HPP_ + +#include "base/StateBase.hpp" +#include "lib/parser/context/ContextParser.hpp" + +namespace ovum::compiler::parser { + +class StateVarDeclTail : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEVARDECLTAIL_HPP_ diff --git a/lib/parser/states/StateWhileHead.cpp b/lib/parser/states/StateWhileHead.cpp new file mode 100644 index 0000000..37fb739 --- /dev/null +++ b/lib/parser/states/StateWhileHead.cpp @@ -0,0 +1,111 @@ +#include "StateWhileHead.hpp" + +#include + +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/ast/nodes/stmts/WhileStmt.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +std::unique_ptr ParseExpression(ContextParser& ctx, ITokenStream& ts) { + if (ctx.Expr() == nullptr) { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_EXPR_PARSER", "expression parser not available"); + } + return nullptr; + } + return ctx.Expr()->Parse(ts, *ctx.Diags()); +} + +} // namespace + +std::string_view StateWhileHead::Name() const { + return "WhileHead"; +} + +IState::StepResult StateWhileHead::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + Block* block = ctx.TopNodeAs(); + if (block == nullptr) { + return std::unexpected(StateError("expected Block node on stack")); + } + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in while statement")); + } + + const Token& start = ts.Peek(); + if (start.GetLexeme() != "while") { + return std::unexpected(StateError("expected 'while' keyword")); + } + ts.Consume(); + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "(") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_WHILE_COND_OPEN", "expected '(' after 'while'"); + } + return std::unexpected(StateError("expected '(' after 'while'")); + } + ts.Consume(); + + SkipTrivia(ts); + auto condition = ParseExpression(ctx, ts); + if (condition == nullptr) { + return std::unexpected(StateError("failed to parse while condition")); + } + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ")") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_WHILE_COND_CLOSE", "expected ')' after condition"); + } + return std::unexpected(StateError("expected ')' after condition")); + } + ts.Consume(); + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "{") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_WHILE_BLOCK", "expected '{' for while body"); + } + return std::unexpected(StateError("expected '{' for while body")); + } + + ts.Consume(); + auto body = ctx.Factory()->MakeBlock({}, SourceSpan{}); + ctx.PushNode(std::unique_ptr(body.get())); + + SourceSpan span = StateBase::Union(StateBase::SpanFrom(start), condition->Span()); + auto while_stmt = ctx.Factory()->MakeWhileStmt(std::move(condition), std::unique_ptr(body), span); + body.release(); + + block->Append(std::move(while_stmt)); + ctx.PushState(StateRegistry::Block()); + return true; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/StateWhileHead.hpp b/lib/parser/states/StateWhileHead.hpp new file mode 100644 index 0000000..2298047 --- /dev/null +++ b/lib/parser/states/StateWhileHead.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATEWHILEHEAD_HPP_ +#define PARSER_STATEWHILEHEAD_HPP_ + +#include "base/StateBase.hpp" +#include "lib/parser/context/ContextParser.hpp" + +namespace ovum::compiler::parser { + +class StateWhileHead : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEWHILEHEAD_HPP_ diff --git a/lib/parser/states/base/IState.hpp b/lib/parser/states/base/IState.hpp new file mode 100644 index 0000000..85e06cd --- /dev/null +++ b/lib/parser/states/base/IState.hpp @@ -0,0 +1,24 @@ +#ifndef PARSER_ISTATE_HPP_ +#define PARSER_ISTATE_HPP_ + +#include + +#include "StateError.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +class ContextParser; // forward + +class IState { +public: + using StepResult = std::expected; + + virtual ~IState() = default; + virtual std::string_view Name() const = 0; + virtual StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const = 0; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_ISTATE_HPP_ diff --git a/lib/parser/states/base/StateBase.cpp b/lib/parser/states/base/StateBase.cpp new file mode 100644 index 0000000..f00ebbe --- /dev/null +++ b/lib/parser/states/base/StateBase.cpp @@ -0,0 +1,15 @@ +#include "lib/parser/states/base/StateBase.hpp" +#include "lib/parser/tokens/SourceId.hpp" + +namespace ovum::compiler::parser { + +SourceSpan StateBase::SpanFrom(const Token& token) { + const auto& pos = token.GetPosition(); + return SourceSpan(SourceId{}, pos, pos); +} + +SourceSpan StateBase::Union(const SourceSpan& left, const SourceSpan& right) { + return SourceSpan::Union(left, right); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/base/StateBase.hpp b/lib/parser/states/base/StateBase.hpp new file mode 100644 index 0000000..c249839 --- /dev/null +++ b/lib/parser/states/base/StateBase.hpp @@ -0,0 +1,20 @@ +#ifndef PARSER_STATEBASE_HPP_ +#define PARSER_STATEBASE_HPP_ + +#include "IState.hpp" +#include "lib/parser/tokens/SourceSpan.hpp" + +namespace ovum::compiler::parser { + +class StateBase : public IState { +public: + using StepResult = IState::StepResult; + ~StateBase() override = default; + + static SourceSpan SpanFrom(const Token& token); + static SourceSpan Union(const SourceSpan& lhs, const SourceSpan& rhs); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEBASE_HPP_ diff --git a/lib/parser/states/base/StateError.cpp b/lib/parser/states/base/StateError.cpp new file mode 100644 index 0000000..16d3b7f --- /dev/null +++ b/lib/parser/states/base/StateError.cpp @@ -0,0 +1,17 @@ +#include "StateError.hpp" + +namespace ovum::compiler::parser { + +StateError::StateError() = default; + +StateError::StateError(std::string message) : message_(std::move(message)) { +} + +StateError::StateError(std::string_view message) : message_(message) { +} + +const std::string& StateError::Message() const noexcept { + return message_; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/base/StateError.hpp b/lib/parser/states/base/StateError.hpp new file mode 100644 index 0000000..c53e6ea --- /dev/null +++ b/lib/parser/states/base/StateError.hpp @@ -0,0 +1,23 @@ +#ifndef PARSER_STATEERROR_HPP_ +#define PARSER_STATEERROR_HPP_ + +#include +#include + +namespace ovum::compiler::parser { + +class StateError { +public: + StateError(); + explicit StateError(std::string message); + explicit StateError(std::string_view message); + + const std::string& Message() const noexcept; + +private: + std::string message_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEERROR_HPP_ diff --git a/lib/parser/states/base/StateRegistry.cpp b/lib/parser/states/base/StateRegistry.cpp new file mode 100644 index 0000000..e36b179 --- /dev/null +++ b/lib/parser/states/base/StateRegistry.cpp @@ -0,0 +1,161 @@ +#include "StateRegistry.hpp" + +#include "lib/parser/states/StateBlock.hpp" +#include "lib/parser/states/StateCallDeclHdr.hpp" +#include "lib/parser/states/StateDestructorDecl.hpp" +#include "lib/parser/states/StateExpr.hpp" +#include "lib/parser/states/StateFieldDecl.hpp" +#include "lib/parser/states/StateForHead.hpp" +#include "lib/parser/states/StateGlobalVarDecl.hpp" +#include "lib/parser/states/StateIfHead.hpp" +#include "lib/parser/states/StateIfTail.hpp" +#include "lib/parser/states/StateMethodHdr.hpp" +#include "lib/parser/states/StateModule.hpp" +#include "lib/parser/states/StateParseType.hpp" +#include "lib/parser/states/StateReturnTail.hpp" +#include "lib/parser/states/StateStmt.hpp" +#include "lib/parser/states/StateSyncToBlockEnd.hpp" +#include "lib/parser/states/StateSyncToStmtEnd.hpp" +#include "lib/parser/states/StateTopDecl.hpp" +#include "lib/parser/states/StateTypeAliasDecl.hpp" +#include "lib/parser/states/StateUnsafeBlock.hpp" +#include "lib/parser/states/StateVarDeclTail.hpp" +#include "lib/parser/states/StateWhileHead.hpp" +#include "lib/parser/states/class/StateClassBody.hpp" +#include "lib/parser/states/class/StateClassHdr.hpp" +#include "lib/parser/states/class/StateClassMember.hpp" +#include "lib/parser/states/func/StateFuncBody.hpp" +#include "lib/parser/states/func/StateFuncHdr.hpp" +#include "lib/parser/states/func/StateFuncParams.hpp" +#include "lib/parser/states/interface/StateInterfaceBody.hpp" +#include "lib/parser/states/interface/StateInterfaceDecl.hpp" +#include "lib/parser/states/interface/StateInterfaceHdr.hpp" + +namespace ovum::compiler::parser { + +const IState& StateRegistry::Module() { + static StateModule s; + return s; +} +const IState& StateRegistry::TopDecl() { + static StateTopDecl s; + return s; +} +const IState& StateRegistry::FuncHdr() { + static StateFuncHdr s; + return s; +} +const IState& StateRegistry::FuncParams() { + static StateFuncParams s; + return s; +} +const IState& StateRegistry::FuncBody() { + static StateFuncBody s; + return s; +} +const IState& StateRegistry::ClassHdr() { + static StateClassHdr s; + return s; +} +const IState& StateRegistry::ClassBody() { + static StateClassBody s; + return s; +} +const IState& StateRegistry::ClassMember() { + static StateClassMember s; + return s; +} +const IState& StateRegistry::InterfaceHdr() { + static StateInterfaceHdr s; + return s; +} +const IState& StateRegistry::InterfaceBody() { + static StateInterfaceBody s; + return s; +} +const IState& StateRegistry::InterfaceDecl() { + static StateInterfaceDecl s; + return s; +} + +const IState& StateRegistry::SignatureDecl() { + return InterfaceDecl(); // Alias for InterfaceDecl +} +const IState& StateRegistry::TypeAliasDecl() { + static StateTypeAliasDecl s; + return s; +} +const IState& StateRegistry::GlobalVarDecl() { + static StateGlobalVarDecl s; + return s; +} +const IState& StateRegistry::DestructorDecl() { + static StateDestructorDecl s; + return s; +} +const IState& StateRegistry::CallDeclHdr() { + static StateCallDeclHdr s; + return s; +} +const IState& StateRegistry::MethodHdr() { + static StateMethodHdr s; + return s; +} +const IState& StateRegistry::FieldDecl() { + static StateFieldDecl s; + return s; +} +const IState& StateRegistry::Block() { + static StateBlock s; + return s; +} +const IState& StateRegistry::Stmt() { + static StateStmt s; + return s; +} +const IState& StateRegistry::IfHead() { + static StateIfHead s; + return s; +} +const IState& StateRegistry::IfTail() { + static StateIfTail s; + return s; +} +const IState& StateRegistry::WhileHead() { + static StateWhileHead s; + return s; +} +const IState& StateRegistry::ForHead() { + static StateForHead s; + return s; +} +const IState& StateRegistry::ReturnTail() { + static StateReturnTail s; + return s; +} +const IState& StateRegistry::VarDeclTail() { + static StateVarDeclTail s; + return s; +} +const IState& StateRegistry::UnsafeBlock() { + static StateUnsafeBlock s; + return s; +} +const IState& StateRegistry::ParseType() { + static StateParseType s; + return s; +} +const IState& StateRegistry::Expr() { + static StateExpr s; + return s; +} +const IState& StateRegistry::SyncToStmtEnd() { + static StateSyncToStmtEnd s; + return s; +} +const IState& StateRegistry::SyncToBlockEnd() { + static StateSyncToBlockEnd s; + return s; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/base/StateRegistry.hpp b/lib/parser/states/base/StateRegistry.hpp new file mode 100644 index 0000000..5a2b5f1 --- /dev/null +++ b/lib/parser/states/base/StateRegistry.hpp @@ -0,0 +1,52 @@ +#ifndef PARSER_STATEREGISTRY_HPP_ +#define PARSER_STATEREGISTRY_HPP_ + +#include "IState.hpp" + +namespace ovum::compiler::parser { + +class StateRegistry { +public: + static const IState& Module(); + static const IState& TopDecl(); + + static const IState& FuncHdr(); + static const IState& FuncParams(); + static const IState& FuncBody(); + + static const IState& ClassHdr(); + static const IState& ClassBody(); + static const IState& ClassMember(); + + static const IState& InterfaceHdr(); + static const IState& InterfaceBody(); + static const IState& InterfaceDecl(); + static const IState& SignatureDecl(); // Alias for InterfaceDecl + + static const IState& TypeAliasDecl(); + static const IState& GlobalVarDecl(); + + static const IState& DestructorDecl(); + static const IState& CallDeclHdr(); + static const IState& MethodHdr(); + static const IState& FieldDecl(); + + static const IState& Block(); + static const IState& Stmt(); + static const IState& IfHead(); + static const IState& IfTail(); + static const IState& WhileHead(); + static const IState& ForHead(); + static const IState& ReturnTail(); + static const IState& VarDeclTail(); + static const IState& UnsafeBlock(); + + static const IState& ParseType(); + static const IState& Expr(); + static const IState& SyncToStmtEnd(); + static const IState& SyncToBlockEnd(); +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEREGISTRY_HPP_ diff --git a/lib/parser/states/class/StateClassBody.cpp b/lib/parser/states/class/StateClassBody.cpp new file mode 100644 index 0000000..e8b6039 --- /dev/null +++ b/lib/parser/states/class/StateClassBody.cpp @@ -0,0 +1,66 @@ +#include "StateClassBody.hpp" + +#include + +#include "lib/parser/ast/nodes/decls/ClassDecl.hpp" +#include "lib/parser/ast/nodes/decls/Module.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +} // namespace + +std::string_view StateClassBody::Name() const { + return "ClassBody"; +} + +IState::StepResult StateClassBody::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + ClassDecl* class_decl = ctx.TopNodeAs(); + if (class_decl == nullptr) { + return std::unexpected(StateError("expected ClassDecl node on stack")); + } + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in class body")); + } + + const Token& tok = ts.Peek(); + if (tok.GetLexeme() == "}") { + ts.Consume(); + // Pop class and add to module + auto class_node = ctx.PopNode(); + Module* module = ctx.TopNodeAs(); + if (module != nullptr) { + module->AddDecl(std::unique_ptr(dynamic_cast(class_node.release()))); + } + return false; + } + + ctx.PushState(StateRegistry::ClassMember()); + return true; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/class/StateClassBody.hpp b/lib/parser/states/class/StateClassBody.hpp new file mode 100644 index 0000000..8b1adb9 --- /dev/null +++ b/lib/parser/states/class/StateClassBody.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATECLASSBODY_HPP_ +#define PARSER_STATECLASSBODY_HPP_ + +#include "lib/parser/states/base/StateBase.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +class StateClassBody : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATECLASSBODY_HPP_ diff --git a/lib/parser/states/class/StateClassHdr.cpp b/lib/parser/states/class/StateClassHdr.cpp new file mode 100644 index 0000000..2d87d22 --- /dev/null +++ b/lib/parser/states/class/StateClassHdr.cpp @@ -0,0 +1,133 @@ +#include "StateClassHdr.hpp" + +#include +#include + +#include "lib/parser/ast/nodes/decls/ClassDecl.hpp" +#include "lib/parser/ast/nodes/decls/Module.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" +#include "lib/parser/tokens/token_traits/MatchIdentifier.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +bool IsIdentifier(const Token& token) { + MatchIdentifier matcher; + return matcher.TryMatch(token); +} + +std::string ReadIdentifier(ContextParser& ctx, ITokenStream& ts, + std::string_view code, std::string_view message) { + SkipTrivia(ts); + if (ts.IsEof() || !IsIdentifier(ts.Peek())) { + if (ctx.Diags() != nullptr) { + const Token* tok = ts.TryPeek(); + if (tok != nullptr) { + SourceSpan span = StateBase::SpanFrom(*tok); + ctx.Diags()->Error(code, message, span); + } else { + ctx.Diags()->Error(code, message); + } + } + return ""; + } + std::string name = ts.Consume()->GetLexeme(); + return name; +} + +std::unique_ptr ParseType(ContextParser& ctx, ITokenStream& ts) { + if (ctx.TypeParser() == nullptr) { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_TYPE_PARSER", "type parser not available"); + } + return nullptr; + } + return ctx.TypeParser()->ParseType(ts, *ctx.Diags()); +} + +} // namespace + +std::string_view StateClassHdr::Name() const { + return "ClassHdr"; +} + +IState::StepResult StateClassHdr::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in class header")); + } + + const Token& start = ts.Peek(); + if (start.GetLexeme() != "class") { + return std::unexpected(StateError("expected 'class' keyword")); + } + ts.Consume(); + + SkipTrivia(ts); + std::string name = ReadIdentifier(ctx, ts, "P_CLASS_NAME", "expected class name"); + if (name.empty()) { + return std::unexpected(StateError("expected class name")); + } + + SourceSpan span = StateBase::SpanFrom(start); + + // Check for implements clause + std::vector implements; + SkipTrivia(ts); + if (!ts.IsEof() && ts.Peek().GetLexeme() == "implements") { + ts.Consume(); + SkipTrivia(ts); + + while (true) { + auto type = ParseType(ctx, ts); + if (type == nullptr) { + break; + } + implements.push_back(std::move(*type)); + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ",") { + break; + } + ts.Consume(); + SkipTrivia(ts); + } + } + + auto class_decl = ctx.Factory()->MakeClass(std::move(name), std::move(implements), {}, span); + ctx.PushNode(std::unique_ptr(class_decl.get())); + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "{") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_CLASS_BODY", "expected '{' for class body"); + } + return std::unexpected(StateError("expected '{' for class body")); + } + + ctx.PushState(StateRegistry::ClassBody()); + return true; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/class/StateClassHdr.hpp b/lib/parser/states/class/StateClassHdr.hpp new file mode 100644 index 0000000..29f17aa --- /dev/null +++ b/lib/parser/states/class/StateClassHdr.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATECLASSHDR_HPP_ +#define PARSER_STATECLASSHDR_HPP_ + +#include "lib/parser/states/base/StateBase.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +class StateClassHdr : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATECLASSHDR_HPP_ diff --git a/lib/parser/states/class/StateClassMember.cpp b/lib/parser/states/class/StateClassMember.cpp new file mode 100644 index 0000000..ddd7e49 --- /dev/null +++ b/lib/parser/states/class/StateClassMember.cpp @@ -0,0 +1,259 @@ +#include "StateClassMember.hpp" + +#include +#include + +#include "lib/parser/ast/nodes/class_members/CallDecl.hpp" +#include "lib/parser/ast/nodes/class_members/DestructorDecl.hpp" +#include "lib/parser/ast/nodes/class_members/FieldDecl.hpp" +#include "lib/parser/ast/nodes/class_members/MethodDecl.hpp" +#include "lib/parser/ast/nodes/class_members/StaticFieldDecl.hpp" +#include "lib/parser/ast/nodes/decls/ClassDecl.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" +#include "lib/parser/tokens/token_traits/MatchIdentifier.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +bool IsIdentifier(const Token& token) { + MatchIdentifier matcher; + return matcher.TryMatch(token); +} + +std::string ReadIdentifier(ContextParser& ctx, ITokenStream& ts, + std::string_view code, std::string_view message) { + SkipTrivia(ts); + if (ts.IsEof() || !IsIdentifier(ts.Peek())) { + if (ctx.Diags() != nullptr) { + const Token* tok = ts.TryPeek(); + if (tok != nullptr) { + SourceSpan span = StateBase::SpanFrom(*tok); + ctx.Diags()->Error(code, message, span); + } else { + ctx.Diags()->Error(code, message); + } + } + return ""; + } + std::string name = ts.Consume()->GetLexeme(); + return name; +} + +void ConsumeTerminators(ITokenStream& ts) { + SkipTrivia(ts, false); + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "NEWLINE") { + ts.Consume(); + continue; + } + if (t.GetLexeme() == ";") { + ts.Consume(); + continue; + } + break; + } +} + +} // namespace + +std::string_view StateClassMember::Name() const { + return "ClassMember"; +} + +IState::StepResult StateClassMember::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + ClassDecl* class_decl = ctx.TopNodeAs(); + if (class_decl == nullptr) { + return std::unexpected(StateError("expected ClassDecl node on stack")); + } + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in class member")); + } + + const Token& start = ts.Peek(); + std::string lex = start.GetLexeme(); + SourceSpan span = StateBase::SpanFrom(start); + + // Check for access modifier + bool is_public = true; + if (lex == "public" || lex == "private") { + is_public = (lex == "public"); + ts.Consume(); + SkipTrivia(ts); + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file after access modifier")); + } + lex = ts.Peek().GetLexeme(); + } + + // Check for static field + if (lex == "static") { + ts.Consume(); + SkipTrivia(ts); + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file after 'static'")); + } + + // Access modifier after static + if (ts.Peek().GetLexeme() == "public" || ts.Peek().GetLexeme() == "private") { + is_public = (ts.Peek().GetLexeme() == "public"); + ts.Consume(); + SkipTrivia(ts); + } + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in static field")); + } + + bool is_var = false; + if (ts.Peek().GetLexeme() == "var" || ts.Peek().GetLexeme() == "val") { + is_var = (ts.Peek().GetLexeme() == "var"); + ts.Consume(); + SkipTrivia(ts); + } + + std::string name = ReadIdentifier(ctx, ts, "P_STATIC_FIELD_NAME", "expected static field name"); + if (name.empty()) { + return std::unexpected(StateError("expected static field name")); + } + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ":") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_STATIC_FIELD_COLON", "expected ':' after static field name"); + } + return std::unexpected(StateError("expected ':' after static field name")); + } + ts.Consume(); + + SkipTrivia(ts); + auto type = ctx.TypeParser()->ParseType(ts, *ctx.Diags()); + if (type == nullptr) { + return std::unexpected(StateError("failed to parse static field type")); + } + + std::unique_ptr init = nullptr; + SkipTrivia(ts); + if (!ts.IsEof() && ts.Peek().GetLexeme() == "=") { + ts.Consume(); + SkipTrivia(ts); + init = ctx.Expr()->Parse(ts, *ctx.Diags()); + } + + span = StateBase::Union(span, ts.LastConsumed() ? StateBase::SpanFrom(*ts.LastConsumed()) : span); + auto field = ctx.Factory()->MakeStaticField(is_public, is_var, std::move(name), std::move(*type), + std::move(init), span); + class_decl->AddMember(std::move(field)); + ConsumeTerminators(ts); + return false; + } + + // Check for destructor + if (lex == "destructor") { + ctx.PushState(StateRegistry::DestructorDecl()); + return true; + } + + // Check for call + if (lex == "call") { + ctx.PushState(StateRegistry::CallDeclHdr()); + return true; + } + + // Check for method (fun) + bool is_override = false; + bool is_pure = false; + if (lex == "override") { + is_override = true; + ts.Consume(); + SkipTrivia(ts); + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file after 'override'")); + } + lex = ts.Peek().GetLexeme(); + } + if (lex == "pure") { + is_pure = true; + ts.Consume(); + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "fun") { + return std::unexpected(StateError("expected 'fun' after 'pure'")); + } + lex = "fun"; + } + if (lex == "fun") { + ctx.PushState(StateRegistry::MethodHdr()); + return true; + } + + // Field declaration + bool is_var = false; + if (lex == "var" || lex == "val") { + is_var = (lex == "var"); + ts.Consume(); + SkipTrivia(ts); + } else { + return std::unexpected(StateError("expected class member")); + } + + std::string name = ReadIdentifier(ctx, ts, "P_FIELD_NAME", "expected field name"); + if (name.empty()) { + return std::unexpected(StateError("expected field name")); + } + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ":") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_FIELD_COLON", "expected ':' after field name"); + } + return std::unexpected(StateError("expected ':' after field name")); + } + ts.Consume(); + + SkipTrivia(ts); + auto type = ctx.TypeParser()->ParseType(ts, *ctx.Diags()); + if (type == nullptr) { + return std::unexpected(StateError("failed to parse field type")); + } + + std::unique_ptr init = nullptr; + SkipTrivia(ts); + if (!ts.IsEof() && ts.Peek().GetLexeme() == "=") { + ts.Consume(); + SkipTrivia(ts); + init = ctx.Expr()->Parse(ts, *ctx.Diags()); + } + + span = StateBase::Union(span, ts.LastConsumed() ? StateBase::SpanFrom(*ts.LastConsumed()) : span); + auto field = ctx.Factory()->MakeField(is_public, is_var, std::move(name), std::move(*type), + std::move(init), span); + class_decl->AddMember(std::move(field)); + ConsumeTerminators(ts); + return false; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/class/StateClassMember.hpp b/lib/parser/states/class/StateClassMember.hpp new file mode 100644 index 0000000..b069929 --- /dev/null +++ b/lib/parser/states/class/StateClassMember.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATECLASSMEMBER_HPP_ +#define PARSER_STATECLASSMEMBER_HPP_ + +#include "lib/parser/states/base/StateBase.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +class StateClassMember : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATECLASSMEMBER_HPP_ diff --git a/lib/parser/states/func/StateFuncBody.cpp b/lib/parser/states/func/StateFuncBody.cpp new file mode 100644 index 0000000..3e1ea05 --- /dev/null +++ b/lib/parser/states/func/StateFuncBody.cpp @@ -0,0 +1,98 @@ +#include "StateFuncBody.hpp" + +#include + +#include "lib/parser/ast/nodes/class_members/CallDecl.hpp" +#include "lib/parser/ast/nodes/class_members/MethodDecl.hpp" +#include "lib/parser/ast/nodes/decls/ClassDecl.hpp" +#include "lib/parser/ast/nodes/decls/FunctionDecl.hpp" +#include "lib/parser/ast/nodes/decls/Module.hpp" +#include "lib/parser/ast/nodes/stmts/Block.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" +#include "lib/parser/tokens/token_traits/MatchLexeme.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +SourceSpan Union(const SourceSpan& a, const SourceSpan& b) { + return StateBase::Union(a, b); +} + +} // namespace + +std::string_view StateFuncBody::Name() const { + return "FuncBody"; +} + +IState::StepResult StateFuncBody::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + // Check for FunctionDecl, MethodDecl, or CallDecl + FunctionDecl* func = ctx.TopNodeAs(); + MethodDecl* method = ctx.TopNodeAs(); + CallDecl* call = ctx.TopNodeAs(); + + if (func == nullptr && method == nullptr && call == nullptr) { + return std::unexpected(StateError("expected FunctionDecl, MethodDecl, or CallDecl node on stack")); + } + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in function body")); + } + + const Token& tok = ts.Peek(); + if (tok.GetLexeme() == ";") { + // Declaration without body + ts.Consume(); + auto decl_node = ctx.PopNode(); + + if (func != nullptr) { + Module* module = ctx.TopNodeAs(); + if (module != nullptr) { + module->AddDecl(std::unique_ptr(dynamic_cast(decl_node.release()))); + } + } else if (method != nullptr || call != nullptr) { + ClassDecl* class_decl = ctx.TopNodeAs(); + if (class_decl != nullptr) { + class_decl->AddMember(std::unique_ptr(dynamic_cast(decl_node.release()))); + } + } + return false; + } + + if (tok.GetLexeme() == "{") { + // Function with body - create block and push it + ts.Consume(); + auto block = ctx.Factory()->MakeBlock({}, StateBase::SpanFrom(tok)); + ctx.PushNode(std::unique_ptr(block.get())); + ctx.PushState(StateRegistry::Block()); + return true; + } + + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_FUN_BODY", "expected '{' or ';' for function body"); + } + return std::unexpected(StateError("expected '{' or ';' for function body")); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/func/StateFuncBody.hpp b/lib/parser/states/func/StateFuncBody.hpp new file mode 100644 index 0000000..0369d14 --- /dev/null +++ b/lib/parser/states/func/StateFuncBody.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATEFUNCBODY_HPP_ +#define PARSER_STATEFUNCBODY_HPP_ + +#include "lib/parser/states/base/StateBase.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +class StateFuncBody : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEFUNCBODY_HPP_ diff --git a/lib/parser/states/func/StateFuncHdr.cpp b/lib/parser/states/func/StateFuncHdr.cpp new file mode 100644 index 0000000..1d716e3 --- /dev/null +++ b/lib/parser/states/func/StateFuncHdr.cpp @@ -0,0 +1,126 @@ +#include "StateFuncHdr.hpp" + +#include +#include + +#include "lib/parser/ast/nodes/decls/FunctionDecl.hpp" +#include "lib/parser/ast/nodes/decls/Module.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" +#include "lib/parser/tokens/token_traits/MatchIdentifier.hpp" +#include "lib/parser/tokens/token_traits/MatchLexeme.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +bool IsIdentifier(const Token& token) { + MatchIdentifier matcher; + return matcher.TryMatch(token); +} + +std::string ReadIdentifier(ContextParser& ctx, ITokenStream& ts, + std::string_view code, std::string_view message) { + SkipTrivia(ts); + if (ts.IsEof() || !IsIdentifier(ts.Peek())) { + if (ctx.Diags() != nullptr) { + const Token* tok = ts.TryPeek(); + if (tok != nullptr) { + SourceSpan span = StateBase::SpanFrom(*tok); + ctx.Diags()->Error(code, message, span); + } else { + ctx.Diags()->Error(code, message); + } + } + return ""; + } + std::string name = ts.Consume()->GetLexeme(); + return name; +} + +} // namespace + +std::string_view StateFuncHdr::Name() const { + return "FuncHdr"; +} + +IState::StepResult StateFuncHdr::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in function header")); + } + + // Check if we already have a function node + FunctionDecl* func = ctx.TopNodeAs(); + if (func == nullptr) { + // Create new function node + bool is_pure = false; + const Token& start = ts.Peek(); + + // Check for 'pure' keyword (should have been consumed by StateTopDecl) + // But we handle it here too for safety + if (start.GetLexeme() == "pure") { + ts.Consume(); + is_pure = true; + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "fun") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_PURE_FUN", "expected 'fun' after 'pure'"); + } + return std::unexpected(StateError("expected 'fun' after 'pure'")); + } + } + + if (ts.Peek().GetLexeme() != "fun") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_FUN_KEYWORD", "expected 'fun' keyword"); + } + return std::unexpected(StateError("expected 'fun' keyword")); + } + ts.Consume(); + + SkipTrivia(ts); + std::string name = ReadIdentifier(ctx, ts, "P_FUN_NAME", "expected function name"); + if (name.empty()) { + return std::unexpected(StateError("expected function name")); + } + + SourceSpan span = StateBase::SpanFrom(start); + auto func_node = ctx.Factory()->MakeFunction(is_pure, std::move(name), {}, nullptr, nullptr, + span); + ctx.PushNode(std::unique_ptr(func_node.get())); + func = func_node.get(); + } + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "(") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_FUN_PARAMS_OPEN", "expected '(' after function name"); + } + return std::unexpected(StateError("expected '(' after function name")); + } + + ctx.PushState(StateRegistry::FuncParams()); + return true; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/func/StateFuncHdr.hpp b/lib/parser/states/func/StateFuncHdr.hpp new file mode 100644 index 0000000..c4997cc --- /dev/null +++ b/lib/parser/states/func/StateFuncHdr.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATEFUNCHDR_HPP_ +#define PARSER_STATEFUNCHDR_HPP_ + +#include "lib/parser/states/base/StateBase.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +class StateFuncHdr : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEFUNCHDR_HPP_ diff --git a/lib/parser/states/func/StateFuncParams.cpp b/lib/parser/states/func/StateFuncParams.cpp new file mode 100644 index 0000000..ae2aa3d --- /dev/null +++ b/lib/parser/states/func/StateFuncParams.cpp @@ -0,0 +1,200 @@ +#include "StateFuncParams.hpp" + +#include + +#include "lib/parser/ast/nodes/class_members/CallDecl.hpp" +#include "lib/parser/ast/nodes/class_members/MethodDecl.hpp" +#include "lib/parser/ast/nodes/decls/FunctionDecl.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" +#include "lib/parser/tokens/token_traits/MatchIdentifier.hpp" +#include "lib/parser/tokens/token_traits/MatchLexeme.hpp" +#include "lib/parser/types/Param.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +bool IsIdentifier(const Token& token) { + MatchIdentifier matcher; + return matcher.TryMatch(token); +} + +std::string ReadIdentifier(ContextParser& ctx, ITokenStream& ts, + std::string_view code, std::string_view message) { + SkipTrivia(ts); + if (ts.IsEof() || !IsIdentifier(ts.Peek())) { + if (ctx.Diags() != nullptr) { + const Token* tok = ts.TryPeek(); + if (tok != nullptr) { + SourceSpan span = StateBase::SpanFrom(*tok); + ctx.Diags()->Error(code, message, span); + } else { + ctx.Diags()->Error(code, message); + } + } + return ""; + } + std::string name = ts.Consume()->GetLexeme(); + return name; +} + +std::unique_ptr ParseType(ContextParser& ctx, ITokenStream& ts) { + if (ctx.TypeParser() == nullptr) { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_TYPE_PARSER", "type parser not available"); + } + return nullptr; + } + return ctx.TypeParser()->ParseType(ts, *ctx.Diags()); +} + +} // namespace + +std::string_view StateFuncParams::Name() const { + return "FuncParams"; +} + +IState::StepResult StateFuncParams::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + // Check for FunctionDecl, MethodDecl, or CallDecl + FunctionDecl* func = ctx.TopNodeAs(); + MethodDecl* method = ctx.TopNodeAs(); + CallDecl* call = ctx.TopNodeAs(); + + if (func == nullptr && method == nullptr && call == nullptr) { + return std::unexpected(StateError("expected FunctionDecl, MethodDecl, or CallDecl node on stack")); + } + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in function parameters")); + } + + const Token& tok = ts.Peek(); + if (tok.GetLexeme() == ")") { + ts.Consume(); + SkipTrivia(ts); + + // Check for return type + if (!ts.IsEof() && ts.Peek().GetLexeme() == ":") { + ts.Consume(); + SkipTrivia(ts); + auto return_type = ParseType(ctx, ts); + if (return_type != nullptr) { + if (func != nullptr) { + func->SetReturnType(std::move(return_type)); + } else if (method != nullptr) { + method->SetReturnType(std::move(return_type)); + } else if (call != nullptr) { + call->SetReturnType(std::move(return_type)); + } + } + } + + SkipTrivia(ts); + ctx.PushState(StateRegistry::FuncBody()); + return true; + } + + // Parse parameter list + std::vector params; + while (true) { + SkipTrivia(ts); + bool is_var = false; + if (!ts.IsEof() && ts.Peek().GetLexeme() == "var") { + ts.Consume(); + is_var = true; + SkipTrivia(ts); + } + + std::string name = ReadIdentifier(ctx, ts, "P_PARAM_NAME", "expected parameter name"); + if (name.empty()) { + break; + } + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ":") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_PARAM_COLON", "expected ':' after parameter name"); + } + break; + } + ts.Consume(); + + SkipTrivia(ts); + auto type = ParseType(ctx, ts); + if (type == nullptr) { + break; + } + + params.emplace_back(name, std::move(*type)); + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ",") { + break; + } + ts.Consume(); + } + + for (auto& param : params) { + if (func != nullptr) { + func->AddParam(std::move(param)); + } else if (method != nullptr) { + method->AddParam(std::move(param)); + } else if (call != nullptr) { + call->AddParam(std::move(param)); + } + } + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ")") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_FUN_PARAMS_CLOSE", "expected ')' after parameters"); + } + return std::unexpected(StateError("expected ')' after parameters")); + } + + ts.Consume(); + SkipTrivia(ts); + + // Check for return type + if (!ts.IsEof() && ts.Peek().GetLexeme() == ":") { + ts.Consume(); + SkipTrivia(ts); + auto return_type = ParseType(ctx, ts); + if (return_type != nullptr) { + if (func != nullptr) { + func->SetReturnType(std::move(return_type)); + } else if (method != nullptr) { + method->SetReturnType(std::move(return_type)); + } else if (call != nullptr) { + call->SetReturnType(std::move(return_type)); + } + } + } + + SkipTrivia(ts); + ctx.PushState(StateRegistry::FuncBody()); + return true; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/func/StateFuncParams.hpp b/lib/parser/states/func/StateFuncParams.hpp new file mode 100644 index 0000000..fbd7145 --- /dev/null +++ b/lib/parser/states/func/StateFuncParams.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATEFUNCPARAMS_HPP_ +#define PARSER_STATEFUNCPARAMS_HPP_ + +#include "lib/parser/states/base/StateBase.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +class StateFuncParams : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEFUNCPARAMS_HPP_ diff --git a/lib/parser/states/interface/StateInterfaceBody.cpp b/lib/parser/states/interface/StateInterfaceBody.cpp new file mode 100644 index 0000000..9ca9ce2 --- /dev/null +++ b/lib/parser/states/interface/StateInterfaceBody.cpp @@ -0,0 +1,66 @@ +#include "StateInterfaceBody.hpp" + +#include + +#include "lib/parser/ast/nodes/decls/InterfaceDecl.hpp" +#include "lib/parser/ast/nodes/decls/Module.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +} // namespace + +std::string_view StateInterfaceBody::Name() const { + return "InterfaceBody"; +} + +IState::StepResult StateInterfaceBody::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + InterfaceDecl* interface_decl = ctx.TopNodeAs(); + if (interface_decl == nullptr) { + return std::unexpected(StateError("expected InterfaceDecl node on stack")); + } + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in interface body")); + } + + const Token& tok = ts.Peek(); + if (tok.GetLexeme() == "}") { + ts.Consume(); + // Pop interface and add to module + auto interface_node = ctx.PopNode(); + Module* module = ctx.TopNodeAs(); + if (module != nullptr) { + module->AddDecl(std::unique_ptr(dynamic_cast(interface_node.release()))); + } + return false; + } + + ctx.PushState(StateRegistry::InterfaceDecl()); + return true; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/interface/StateInterfaceBody.hpp b/lib/parser/states/interface/StateInterfaceBody.hpp new file mode 100644 index 0000000..ac7d041 --- /dev/null +++ b/lib/parser/states/interface/StateInterfaceBody.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATEINTERFACEBODY_HPP_ +#define PARSER_STATEINTERFACEBODY_HPP_ + +#include "lib/parser/states/base/StateBase.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +class StateInterfaceBody : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEINTERFACEBODY_HPP_ diff --git a/lib/parser/states/interface/StateInterfaceDecl.cpp b/lib/parser/states/interface/StateInterfaceDecl.cpp new file mode 100644 index 0000000..dd5e5f5 --- /dev/null +++ b/lib/parser/states/interface/StateInterfaceDecl.cpp @@ -0,0 +1,280 @@ +#include "StateInterfaceDecl.hpp" + +#include +#include + +#include "lib/parser/ast/nodes/decls/InterfaceDecl.hpp" +#include "lib/parser/ast/nodes/decls/InterfaceMethod.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" +#include "lib/parser/tokens/token_traits/MatchIdentifier.hpp" +#include "lib/parser/types/Param.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +bool IsIdentifier(const Token& token) { + MatchIdentifier matcher; + return matcher.TryMatch(token); +} + +std::string ReadIdentifier(ContextParser& ctx, ITokenStream& ts, + std::string_view code, std::string_view message) { + SkipTrivia(ts); + if (ts.IsEof() || !IsIdentifier(ts.Peek())) { + if (ctx.Diags() != nullptr) { + const Token* tok = ts.TryPeek(); + if (tok != nullptr) { + SourceSpan span = StateBase::SpanFrom(*tok); + ctx.Diags()->Error(code, message, span); + } else { + ctx.Diags()->Error(code, message); + } + } + return ""; + } + std::string name = ts.Consume()->GetLexeme(); + return name; +} + +std::unique_ptr ParseType(ContextParser& ctx, ITokenStream& ts) { + if (ctx.TypeParser() == nullptr) { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_TYPE_PARSER", "type parser not available"); + } + return nullptr; + } + return ctx.TypeParser()->ParseType(ts, *ctx.Diags()); +} + +void ConsumeTerminators(ITokenStream& ts) { + SkipTrivia(ts, false); + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "NEWLINE") { + ts.Consume(); + continue; + } + if (t.GetLexeme() == ";") { + ts.Consume(); + continue; + } + break; + } +} + +} // namespace + +std::string_view StateInterfaceDecl::Name() const { + return "InterfaceDecl"; +} + +IState::StepResult StateInterfaceDecl::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + InterfaceDecl* interface_decl = ctx.TopNodeAs(); + if (interface_decl == nullptr) { + return std::unexpected(StateError("expected InterfaceDecl node on stack")); + } + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in interface declaration")); + } + + const Token& start = ts.Peek(); + std::string lex = start.GetLexeme(); + SourceSpan span = StateBase::SpanFrom(start); + + // Check for call + if (lex == "call") { + ts.Consume(); + SkipTrivia(ts); + + if (ts.IsEof() || ts.Peek().GetLexeme() != "(") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_CALL_PARAMS_OPEN", "expected '(' after 'call'"); + } + return std::unexpected(StateError("expected '(' after 'call'")); + } + ts.Consume(); + + // Parse parameters + std::vector params; + SkipTrivia(ts); + if (!ts.IsEof() && ts.Peek().GetLexeme() != ")") { + while (true) { + bool is_var = false; + if (!ts.IsEof() && ts.Peek().GetLexeme() == "var") { + ts.Consume(); + is_var = true; + SkipTrivia(ts); + } + + std::string name = ReadIdentifier(ctx, ts, "P_PARAM_NAME", "expected parameter name"); + if (name.empty()) { + break; + } + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ":") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_PARAM_COLON", "expected ':' after parameter name"); + } + break; + } + ts.Consume(); + + SkipTrivia(ts); + auto type = ParseType(ctx, ts); + if (type == nullptr) { + break; + } + + params.push_back({name, std::move(*type)}); + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ",") { + break; + } + ts.Consume(); + SkipTrivia(ts); + } + } + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ")") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_CALL_PARAMS_CLOSE", "expected ')' after parameters"); + } + return std::unexpected(StateError("expected ')' after parameters")); + } + ts.Consume(); + + std::unique_ptr return_type = nullptr; + SkipTrivia(ts); + if (!ts.IsEof() && ts.Peek().GetLexeme() == ":") { + ts.Consume(); + SkipTrivia(ts); + return_type = ParseType(ctx, ts); + } + + auto method = ctx.Factory()->MakeInterfaceMethod("call", std::move(params), std::move(return_type), span); + interface_decl->AddMember(std::move(method)); + ConsumeTerminators(ts); + return false; + } + + // Method declaration (fun) + if (lex != "fun") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_INTERFACE_METHOD", "expected 'fun' or 'call' in interface"); + } + return std::unexpected(StateError("expected 'fun' or 'call' in interface")); + } + ts.Consume(); + + SkipTrivia(ts); + std::string name = ReadIdentifier(ctx, ts, "P_METHOD_NAME", "expected method name"); + if (name.empty()) { + return std::unexpected(StateError("expected method name")); + } + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "(") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_METHOD_PARAMS_OPEN", "expected '(' after method name"); + } + return std::unexpected(StateError("expected '(' after method name")); + } + ts.Consume(); + + // Parse parameters + std::vector params; + SkipTrivia(ts); + if (!ts.IsEof() && ts.Peek().GetLexeme() != ")") { + while (true) { + bool is_var = false; + if (!ts.IsEof() && ts.Peek().GetLexeme() == "var") { + ts.Consume(); + is_var = true; + SkipTrivia(ts); + } + + std::string param_name = ReadIdentifier(ctx, ts, "P_PARAM_NAME", "expected parameter name"); + if (param_name.empty()) { + break; + } + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ":") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_PARAM_COLON", "expected ':' after parameter name"); + } + break; + } + ts.Consume(); + + SkipTrivia(ts); + auto type = ParseType(ctx, ts); + if (type == nullptr) { + break; + } + + params.push_back({param_name, std::move(*type)}); + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ",") { + break; + } + ts.Consume(); + SkipTrivia(ts); + } + } + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != ")") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_METHOD_PARAMS_CLOSE", "expected ')' after parameters"); + } + return std::unexpected(StateError("expected ')' after parameters")); + } + ts.Consume(); + + std::unique_ptr return_type = nullptr; + SkipTrivia(ts); + if (!ts.IsEof() && ts.Peek().GetLexeme() == ":") { + ts.Consume(); + SkipTrivia(ts); + return_type = ParseType(ctx, ts); + } + + if (ts.LastConsumed() != nullptr) { + span = StateBase::Union(span, StateBase::SpanFrom(*ts.LastConsumed())); + } + auto method = ctx.Factory()->MakeInterfaceMethod(std::move(name), std::move(params), + std::move(return_type), span); + interface_decl->AddMember(std::move(method)); + ConsumeTerminators(ts); + return false; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/interface/StateInterfaceDecl.hpp b/lib/parser/states/interface/StateInterfaceDecl.hpp new file mode 100644 index 0000000..626e095 --- /dev/null +++ b/lib/parser/states/interface/StateInterfaceDecl.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATEINTERFACEDECL_HPP_ +#define PARSER_STATEINTERFACEDECL_HPP_ + +#include "lib/parser/states/base/StateBase.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +class StateInterfaceDecl : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEINTERFACEDECL_HPP_ diff --git a/lib/parser/states/interface/StateInterfaceHdr.cpp b/lib/parser/states/interface/StateInterfaceHdr.cpp new file mode 100644 index 0000000..68628ac --- /dev/null +++ b/lib/parser/states/interface/StateInterfaceHdr.cpp @@ -0,0 +1,99 @@ +#include "StateInterfaceHdr.hpp" + +#include +#include + +#include "lib/parser/ast/nodes/decls/InterfaceDecl.hpp" +#include "lib/parser/ast/nodes/decls/Module.hpp" +#include "lib/parser/context/ContextParser.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/states/base/StateRegistry.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" +#include "lib/parser/tokens/token_traits/MatchIdentifier.hpp" + +namespace ovum::compiler::parser { + +namespace { + +void SkipTrivia(ITokenStream& ts, bool skip_newlines = true) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT") { + ts.Consume(); + continue; + } + if (skip_newlines && type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +bool IsIdentifier(const Token& token) { + MatchIdentifier matcher; + return matcher.TryMatch(token); +} + +std::string ReadIdentifier(ContextParser& ctx, ITokenStream& ts, + std::string_view code, std::string_view message) { + SkipTrivia(ts); + if (ts.IsEof() || !IsIdentifier(ts.Peek())) { + if (ctx.Diags() != nullptr) { + const Token* tok = ts.TryPeek(); + if (tok != nullptr) { + SourceSpan span = StateBase::SpanFrom(*tok); + ctx.Diags()->Error(code, message, span); + } else { + ctx.Diags()->Error(code, message); + } + } + return ""; + } + std::string name = ts.Consume()->GetLexeme(); + return name; +} + +} // namespace + +std::string_view StateInterfaceHdr::Name() const { + return "InterfaceHdr"; +} + +IState::StepResult StateInterfaceHdr::TryStep(ContextParser& ctx, ITokenStream& ts) const { + SkipTrivia(ts); + + if (ts.IsEof()) { + return std::unexpected(StateError("unexpected end of file in interface header")); + } + + const Token& start = ts.Peek(); + if (start.GetLexeme() != "interface") { + return std::unexpected(StateError("expected 'interface' keyword")); + } + ts.Consume(); + + SkipTrivia(ts); + std::string name = ReadIdentifier(ctx, ts, "P_INTERFACE_NAME", "expected interface name"); + if (name.empty()) { + return std::unexpected(StateError("expected interface name")); + } + + SourceSpan span = StateBase::SpanFrom(start); + auto interface_decl = ctx.Factory()->MakeInterface(std::move(name), {}, span); + ctx.PushNode(std::unique_ptr(interface_decl.get())); + + SkipTrivia(ts); + if (ts.IsEof() || ts.Peek().GetLexeme() != "{") { + if (ctx.Diags() != nullptr) { + ctx.Diags()->Error("P_INTERFACE_BODY", "expected '{' for interface body"); + } + return std::unexpected(StateError("expected '{' for interface body")); + } + + ctx.PushState(StateRegistry::InterfaceBody()); + return true; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/states/interface/StateInterfaceHdr.hpp b/lib/parser/states/interface/StateInterfaceHdr.hpp new file mode 100644 index 0000000..9a6e634 --- /dev/null +++ b/lib/parser/states/interface/StateInterfaceHdr.hpp @@ -0,0 +1,17 @@ +#ifndef PARSER_STATEINTERFACEHDR_HPP_ +#define PARSER_STATEINTERFACEHDR_HPP_ + +#include "lib/parser/states/base/StateBase.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" + +namespace ovum::compiler::parser { + +class StateInterfaceHdr : public StateBase { +public: + std::string_view Name() const override; + StepResult TryStep(ContextParser& ctx, ITokenStream& ts) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_STATEINTERFACEHDR_HPP_ diff --git a/lib/parser/tokens/SourceId.cpp b/lib/parser/tokens/SourceId.cpp new file mode 100644 index 0000000..987885d --- /dev/null +++ b/lib/parser/tokens/SourceId.cpp @@ -0,0 +1,32 @@ +#include "SourceId.hpp" + +#include + +namespace ovum::compiler::parser { + +std::string_view SourceId::Path() const noexcept { + return path_; +} + +std::string SourceId::Basename() const { + if (path_.empty()) { + return {}; + } + + std::filesystem::path p(path_); + return p.filename().string(); +} + +bool SourceId::IsValid() const noexcept { + return !path_.empty(); +} + +bool SourceId::operator==(const SourceId& other) const noexcept { + return path_ == other.path_; +} + +bool SourceId::operator!=(const SourceId& other) const noexcept { + return !(*this == other); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/tokens/SourceId.hpp b/lib/parser/tokens/SourceId.hpp new file mode 100644 index 0000000..4d2dea3 --- /dev/null +++ b/lib/parser/tokens/SourceId.hpp @@ -0,0 +1,30 @@ +#ifndef PARSER_SOURCEID_HPP_ +#define PARSER_SOURCEID_HPP_ + +#include +#include +#include + +namespace ovum::compiler::parser { + +class SourceId { +public: + SourceId() = default; + explicit SourceId(std::string path) : path_(std::move(path)) { + } + + [[nodiscard]] std::string_view Path() const noexcept; + + [[nodiscard]] std::string Basename() const; + + [[nodiscard]] bool IsValid() const noexcept; + [[nodiscard]] bool operator==(const SourceId& other) const noexcept; + [[nodiscard]] bool operator!=(const SourceId& other) const noexcept; + +private: + std::string path_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_SOURCEID_HPP_ diff --git a/lib/parser/tokens/SourceSpan.cpp b/lib/parser/tokens/SourceSpan.cpp new file mode 100644 index 0000000..68df9e7 --- /dev/null +++ b/lib/parser/tokens/SourceSpan.cpp @@ -0,0 +1,53 @@ +#include "SourceSpan.hpp" + +#include + +namespace ovum::compiler::parser { + +SourceSpan::SourceSpan(SourceId id, TokenPosition start, TokenPosition end) : + id_(std::move(id)), begin_(start), end_(end) { + Normalize(); +} + +const SourceId& SourceSpan::GetSourceId() const noexcept { + return id_; +} +TokenPosition SourceSpan::GetStart() const noexcept { + return begin_; +} +TokenPosition SourceSpan::GetEnd() const noexcept { + return end_; +} + +bool SourceSpan::IsValid() const noexcept { + return id_.IsValid(); +} + +void SourceSpan::Normalize() noexcept { + if (end_ < begin_) { + std::swap(begin_, end_); + } +} + +SourceSpan SourceSpan::SinglePoint(SourceId id, TokenPosition point) { + return SourceSpan(std::move(id), point, point); +} + +SourceSpan SourceSpan::Union(const SourceSpan& a, const SourceSpan& b) { + if (!a.IsValid()) { + return b; + } + + if (!b.IsValid()) { + return a; + } + + if (a.id_ != b.id_) { + return a; + } + TokenPosition start = a.begin_ < b.begin_ ? a.begin_ : b.begin_; + TokenPosition end = a.end_ < b.end_ ? b.end_ : a.end_; + return SourceSpan(a.id_, start, end); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/tokens/SourceSpan.hpp b/lib/parser/tokens/SourceSpan.hpp new file mode 100644 index 0000000..3441b46 --- /dev/null +++ b/lib/parser/tokens/SourceSpan.hpp @@ -0,0 +1,32 @@ +#ifndef PARSER_SOURCESPAN_HPP_ +#define PARSER_SOURCESPAN_HPP_ + +#include "SourceId.hpp" +#include "tokens/TokenPosition.hpp" + +namespace ovum::compiler::parser { + +class SourceSpan { +public: + SourceSpan() = default; + SourceSpan(SourceId id, TokenPosition start, TokenPosition end); + + [[nodiscard]] const SourceId& GetSourceId() const noexcept; + [[nodiscard]] TokenPosition GetStart() const noexcept; + [[nodiscard]] TokenPosition GetEnd() const noexcept; + + [[nodiscard]] bool IsValid() const noexcept; + void Normalize() noexcept; + + [[nodiscard]] static SourceSpan SinglePoint(SourceId id, TokenPosition point); + [[nodiscard]] static SourceSpan Union(const SourceSpan& a, const SourceSpan& b); + +private: + SourceId id_; + TokenPosition begin_; + TokenPosition end_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_SOURCESPAN_HPP_ diff --git a/lib/parser/tokens/token_streams/ITokenStream.hpp b/lib/parser/tokens/token_streams/ITokenStream.hpp new file mode 100644 index 0000000..37eaeba --- /dev/null +++ b/lib/parser/tokens/token_streams/ITokenStream.hpp @@ -0,0 +1,27 @@ +#ifndef PARSER_ITOKENSTREAM_HPP_ +#define PARSER_ITOKENSTREAM_HPP_ + +#include + +#include + +namespace ovum::compiler::parser { + +class ITokenStream { +public: + virtual ~ITokenStream() = default; + + virtual const Token& Peek(size_t k = 0) = 0; + virtual TokenPtr Consume() = 0; + + virtual size_t Position() const = 0; + virtual void Rewind(size_t n) = 0; + virtual bool IsEof() const = 0; + + virtual const Token* LastConsumed() const = 0; + virtual const Token* TryPeek(size_t k = 0) = 0; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_ITOKENSTREAM_HPP_ diff --git a/lib/parser/tokens/token_streams/VectorTokenStream.cpp b/lib/parser/tokens/token_streams/VectorTokenStream.cpp new file mode 100644 index 0000000..6fafb4f --- /dev/null +++ b/lib/parser/tokens/token_streams/VectorTokenStream.cpp @@ -0,0 +1,74 @@ +#include "lib/parser/tokens/token_streams/VectorTokenStream.hpp" + +#include +#include + +namespace ovum::compiler::parser { + +VectorTokenStream::VectorTokenStream(std::vector tokens) : tokens_(std::move(tokens)) { +} + +const Token& VectorTokenStream::Peek(size_t k) { + const Token* token = TryPeek(k); + + if (token != nullptr) { + return *token; + } + + if (last_ != nullptr) { + return *last_; + } + + if (!tokens_.empty()) { + return *tokens_.back(); + } + + throw std::out_of_range("VectorTokenStream::Peek out of range"); +} + +TokenPtr VectorTokenStream::Consume() { + if (index_ < tokens_.size()) { + last_ = tokens_[index_].get(); + return std::move(tokens_[index_++]); + } + + last_ = nullptr; + return nullptr; +} + +size_t VectorTokenStream::Position() const { + return index_; +} + +void VectorTokenStream::Rewind(size_t n) { + if (n > index_) { + index_ = 0; + } else { + index_ -= n; + } + + last_ = nullptr; +} + +bool VectorTokenStream::IsEof() const { + return index_ >= tokens_.size(); +} + +const Token* VectorTokenStream::LastConsumed() const { + return last_; +} + +const Token* VectorTokenStream::TryPeek(size_t k) { + const size_t pos = index_ + k; + if (pos < tokens_.size()) { + return tokens_[pos].get(); + } + + return nullptr; +} + +size_t VectorTokenStream::Size() const { + return tokens_.size(); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/tokens/token_streams/VectorTokenStream.hpp b/lib/parser/tokens/token_streams/VectorTokenStream.hpp new file mode 100644 index 0000000..a5caae6 --- /dev/null +++ b/lib/parser/tokens/token_streams/VectorTokenStream.hpp @@ -0,0 +1,42 @@ +#ifndef PARSER_VECTORTOKENSTREAM_HPP_ +#define PARSER_VECTORTOKENSTREAM_HPP_ + +#include +#include +#include + +#include + +#include "ITokenStream.hpp" + +namespace ovum::compiler::parser { + +class VectorTokenStream : public ITokenStream { +public: + explicit VectorTokenStream(std::vector tokens); + + const Token& Peek(size_t k = 0) override; + + TokenPtr Consume() override; + + size_t Position() const override; + + void Rewind(size_t n) override; + + bool IsEof() const override; + + const Token* LastConsumed() const override; + + const Token* TryPeek(size_t k = 0) override; + + size_t Size() const; + +private: + std::vector tokens_; + size_t index_ = 0; + const Token* last_ = nullptr; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_VECTORTOKENSTREAM_HPP_ diff --git a/lib/parser/tokens/token_traits/ITokenMatcher.hpp b/lib/parser/tokens/token_traits/ITokenMatcher.hpp new file mode 100644 index 0000000..57febee --- /dev/null +++ b/lib/parser/tokens/token_traits/ITokenMatcher.hpp @@ -0,0 +1,16 @@ +#ifndef PARSER_ITOKENMATCHER_HPP_ +#define PARSER_ITOKENMATCHER_HPP_ + +#include "tokens/Token.hpp" + +namespace ovum::compiler::parser { + +class ITokenMatcher { +public: + virtual ~ITokenMatcher() = default; + virtual bool TryMatch(const Token& token) const = 0; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_ITOKENMATCHER_HPP_ diff --git a/lib/parser/tokens/token_traits/MatchIdentifier.cpp b/lib/parser/tokens/token_traits/MatchIdentifier.cpp new file mode 100644 index 0000000..a6a25fb --- /dev/null +++ b/lib/parser/tokens/token_traits/MatchIdentifier.cpp @@ -0,0 +1,10 @@ +#include "MatchIdentifier.hpp" + +namespace ovum::compiler::parser { + +bool MatchIdentifier::TryMatch(const Token& token) const { + const std::string type = token.GetStringType(); + return type == "IDENT"; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/tokens/token_traits/MatchIdentifier.hpp b/lib/parser/tokens/token_traits/MatchIdentifier.hpp new file mode 100644 index 0000000..52899d8 --- /dev/null +++ b/lib/parser/tokens/token_traits/MatchIdentifier.hpp @@ -0,0 +1,16 @@ +#ifndef PARSER_MATCHIDENTIFIER_HPP_ +#define PARSER_MATCHIDENTIFIER_HPP_ + +#include "tokens/Token.hpp" +#include "ITokenMatcher.hpp" + +namespace ovum::compiler::parser { + +class MatchIdentifier : public ITokenMatcher { +public: + bool TryMatch(const Token& token) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_MATCHIDENTIFIER_HPP_ diff --git a/lib/parser/tokens/token_traits/MatchLexeme.cpp b/lib/parser/tokens/token_traits/MatchLexeme.cpp new file mode 100644 index 0000000..5cb7280 --- /dev/null +++ b/lib/parser/tokens/token_traits/MatchLexeme.cpp @@ -0,0 +1,12 @@ +#include "MatchLexeme.hpp" + +namespace ovum::compiler::parser { + +MatchLexeme::MatchLexeme(std::string_view lexeme) : lexeme_(lexeme) { +} + +bool MatchLexeme::TryMatch(const Token& token) const { + return token.GetLexeme() == lexeme_; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/tokens/token_traits/MatchLexeme.hpp b/lib/parser/tokens/token_traits/MatchLexeme.hpp new file mode 100644 index 0000000..d1133ee --- /dev/null +++ b/lib/parser/tokens/token_traits/MatchLexeme.hpp @@ -0,0 +1,22 @@ +#ifndef PARSER_MATCHLEXEME_HPP_ +#define PARSER_MATCHLEXEME_HPP_ + +#include + +#include "ITokenMatcher.hpp" +#include "tokens/Token.hpp" + +namespace ovum::compiler::parser { + +class MatchLexeme : public ITokenMatcher { +public: + explicit MatchLexeme(std::string_view lexeme); + bool TryMatch(const Token& token) const override; + +private: + std::string_view lexeme_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_MATCHLEXEME_HPP_ diff --git a/lib/parser/tokens/token_traits/MatchLiteral.cpp b/lib/parser/tokens/token_traits/MatchLiteral.cpp new file mode 100644 index 0000000..134966e --- /dev/null +++ b/lib/parser/tokens/token_traits/MatchLiteral.cpp @@ -0,0 +1,13 @@ +#include "MatchLiteral.hpp" + +#include + +namespace ovum::compiler::parser { + +bool MatchLiteral::TryMatch(const Token& token) const { + const std::string type = token.GetStringType(); + // LiteralToken::GetStringType() -> "LITERAL:" + return type.rfind("LITERAL:", 0) == 0; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/tokens/token_traits/MatchLiteral.hpp b/lib/parser/tokens/token_traits/MatchLiteral.hpp new file mode 100644 index 0000000..79d889b --- /dev/null +++ b/lib/parser/tokens/token_traits/MatchLiteral.hpp @@ -0,0 +1,16 @@ +#ifndef PARSER_MATCHLITERAL_HPP_ +#define PARSER_MATCHLITERAL_HPP_ + +#include "tokens/Token.hpp" +#include "ITokenMatcher.hpp" + +namespace ovum::compiler::parser { + +class MatchLiteral : public ITokenMatcher { +public: + bool TryMatch(const Token& token) const override; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_MATCHLITERAL_HPP_ diff --git a/lib/parser/tokens/token_traits/MatchManyOf.cpp b/lib/parser/tokens/token_traits/MatchManyOf.cpp new file mode 100644 index 0000000..31e560c --- /dev/null +++ b/lib/parser/tokens/token_traits/MatchManyOf.cpp @@ -0,0 +1,17 @@ +#include "MatchManyOf.hpp" + +namespace ovum::compiler::parser { + +MatchAnyOf::MatchAnyOf(std::vector> matchers) : matchers_(std::move(matchers)) { +} + +bool MatchAnyOf::TryMatch(const Token& token) const { + for (const auto& m : matchers_) { + if (m && m->TryMatch(token)) { + return true; + } + } + return false; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/tokens/token_traits/MatchManyOf.hpp b/lib/parser/tokens/token_traits/MatchManyOf.hpp new file mode 100644 index 0000000..dbaf30a --- /dev/null +++ b/lib/parser/tokens/token_traits/MatchManyOf.hpp @@ -0,0 +1,23 @@ +#ifndef PARSER_MATCHMANYOF_HPP_ +#define PARSER_MATCHMANYOF_HPP_ + +#include +#include + +#include "ITokenMatcher.hpp" +#include "tokens/Token.hpp" + +namespace ovum::compiler::parser { + +class MatchAnyOf : public ITokenMatcher { +public: + explicit MatchAnyOf(std::vector> matchers); + bool TryMatch(const Token& token) const override; + +private: + std::vector> matchers_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_MATCHMANYOF_HPP_ diff --git a/lib/parser/tokens/token_traits/MatchType.cpp b/lib/parser/tokens/token_traits/MatchType.cpp new file mode 100644 index 0000000..c35dc94 --- /dev/null +++ b/lib/parser/tokens/token_traits/MatchType.cpp @@ -0,0 +1,12 @@ +#include "MatchType.hpp" + +namespace ovum::compiler::parser { + +MatchType::MatchType(std::string_view type_name) : type_(type_name) { +} + +bool MatchType::TryMatch(const Token& token) const { + return token.GetStringType() == type_; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/tokens/token_traits/MatchType.hpp b/lib/parser/tokens/token_traits/MatchType.hpp new file mode 100644 index 0000000..c480d6e --- /dev/null +++ b/lib/parser/tokens/token_traits/MatchType.hpp @@ -0,0 +1,22 @@ +#ifndef PARSER_MATCHTYPE_HPP_ +#define PARSER_MATCHTYPE_HPP_ + +#include + +#include "ITokenMatcher.hpp" +#include "tokens/Token.hpp" + +namespace ovum::compiler::parser { + +class MatchType : public ITokenMatcher { +public: + explicit MatchType(std::string_view type_name); + bool TryMatch(const Token& token) const override; + +private: + std::string_view type_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_MATCHTYPE_HPP_ diff --git a/lib/parser/type_parser/ITypeParser.hpp b/lib/parser/type_parser/ITypeParser.hpp new file mode 100644 index 0000000..fabbbd9 --- /dev/null +++ b/lib/parser/type_parser/ITypeParser.hpp @@ -0,0 +1,20 @@ +#ifndef PARSER_ITYPEPARSER_HPP_ +#define PARSER_ITYPEPARSER_HPP_ + +#include + +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class ITypeParser { +public: + virtual ~ITypeParser() = default; + virtual std::unique_ptr ParseType(ITokenStream& ts, IDiagnosticSink& diags) = 0; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_ITYPEPARSER_HPP_ diff --git a/lib/parser/type_parser/QNameTypeParser.cpp b/lib/parser/type_parser/QNameTypeParser.cpp new file mode 100644 index 0000000..3a92ad7 --- /dev/null +++ b/lib/parser/type_parser/QNameTypeParser.cpp @@ -0,0 +1,152 @@ +#include "QNameTypeParser.hpp" + +#include +#include +#include + +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/tokens/token_traits/MatchIdentifier.hpp" +#include "lib/parser/tokens/token_traits/MatchLexeme.hpp" +#include "lib/parser/tokens/token_traits/MatchType.hpp" + +namespace ovum::compiler::parser { + +namespace { + +bool IsIdentifier(const Token& token) { + MatchIdentifier matcher; + return matcher.TryMatch(token); +} + +bool IsTypeNameToken(const Token& token) { + // Built-in types are lexed as keywords, user types as identifiers. + if (IsIdentifier(token)) { + return true; + } + + static const std::array kBuiltins{ + "Int", "Float", "Bool", "Char", "String", "Void", "Object", "Null"}; + + const std::string lex = token.GetLexeme(); + return std::find(kBuiltins.begin(), kBuiltins.end(), lex) != kBuiltins.end(); +} + +void SkipTrivia(ITokenStream& ts) { + while (!ts.IsEof()) { + const Token& t = ts.Peek(); + const std::string type = t.GetStringType(); + if (type == "COMMENT" || type == "NEWLINE") { + ts.Consume(); + continue; + } + break; + } +} + +} // namespace + +QNameTypeParser::QNameTypeParser(IAstFactory& factory) noexcept : factory_(std::shared_ptr(&factory, [](auto*) {})) { +} + +std::unique_ptr QNameTypeParser::ParseType(ITokenStream& ts, IDiagnosticSink& diags) { + SkipTrivia(ts); + + if (ts.IsEof()) { + diags.Error("E_TYPE_EOF", "expected type, reached end of input"); + return nullptr; + } + + const Token& start = ts.Peek(); + if (!IsTypeNameToken(start)) { + diags.Error("E_TYPE_NAME", "expected type name"); + return nullptr; + } + + std::vector parts; + parts.emplace_back(start.GetLexeme()); + ts.Consume(); + + SkipTrivia(ts); + while (!ts.IsEof()) { + const Token& sep = ts.Peek(); + const std::string lex = sep.GetLexeme(); + if (lex != "." && lex != "::") { + break; + } + + ts.Consume(); + SkipTrivia(ts); + + if (ts.IsEof() || !IsTypeNameToken(ts.Peek())) { + diags.Error("E_TYPE_QUAL", "expected identifier after namespace separator"); + return nullptr; + } + + parts.emplace_back(ts.Peek().GetLexeme()); + ts.Consume(); + SkipTrivia(ts); + } + + auto type = std::make_unique(parts); + + // Parse type arguments: List + SkipTrivia(ts); + if (!ts.IsEof() && ts.Peek().GetLexeme() == "<") { + ts.Consume(); + SkipTrivia(ts); + + while (true) { + SkipTrivia(ts); + if (ts.IsEof()) { + diags.Error("E_TYPE_ARGS_EOF", "unexpected end of input in type arguments"); + return nullptr; + } + + // Check for closing bracket + if (ts.Peek().GetLexeme() == ">") { + ts.Consume(); + break; + } + + // Parse type argument (recursive call) + auto arg_type = ParseType(ts, diags); + if (arg_type == nullptr) { + return nullptr; + } + type->AddTypeArgument(std::move(*arg_type)); + + SkipTrivia(ts); + if (ts.IsEof()) { + diags.Error("E_TYPE_ARGS_EOF", "unexpected end of input in type arguments"); + return nullptr; + } + + const std::string lex = ts.Peek().GetLexeme(); + if (lex == ">") { + ts.Consume(); + break; + } + if (lex != ",") { + diags.Error("E_TYPE_ARGS_SEP", "expected ',' or '>' in type arguments"); + return nullptr; + } + ts.Consume(); + } + } + + // Parse nullability: Type? + SkipTrivia(ts); + bool nullable = false; + if (!ts.IsEof() && ts.Peek().GetLexeme() == "?") { + nullable = true; + ts.Consume(); + } + + if (nullable) { + type->MakeNullable(); + } + + return type; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/type_parser/QNameTypeParser.hpp b/lib/parser/type_parser/QNameTypeParser.hpp new file mode 100644 index 0000000..f54cbed --- /dev/null +++ b/lib/parser/type_parser/QNameTypeParser.hpp @@ -0,0 +1,33 @@ +#ifndef PARSER_QNAMETYPEPARSER_HPP_ +#define PARSER_QNAMETYPEPARSER_HPP_ + +#include + +#include "ITypeParser.hpp" +#include "lib/parser/ast/IAstFactory.hpp" +#include "lib/parser/diagnostics/IDiagnosticSink.hpp" +#include "lib/parser/tokens/token_streams/ITokenStream.hpp" +#include "lib/parser/types/TypeReference.hpp" + +namespace ovum::compiler::parser { + +class QNameTypeParser final : public ITypeParser { +public: + explicit QNameTypeParser(IAstFactory& factory) noexcept; + ~QNameTypeParser() override = default; + + [[nodiscard]] std::unique_ptr ParseType(ITokenStream& ts, IDiagnosticSink& diags) override; + + QNameTypeParser(const QNameTypeParser&) = delete; + QNameTypeParser& operator=(const QNameTypeParser&) = delete; + + QNameTypeParser(QNameTypeParser&&) noexcept = default; + QNameTypeParser& operator=(QNameTypeParser&&) noexcept = delete; + +private: + std::shared_ptr factory_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_QNAMETYPEPARSER_HPP_ diff --git a/lib/parser/types/Nullable.cpp b/lib/parser/types/Nullable.cpp new file mode 100644 index 0000000..b231e1b --- /dev/null +++ b/lib/parser/types/Nullable.cpp @@ -0,0 +1,24 @@ +#include "Nullable.hpp" + +namespace ovum::compiler::parser { + +Nullable::Nullable(bool on) noexcept : on_(on) { +} + +bool Nullable::IsOn() const noexcept { + return on_; +} + +void Nullable::Set(bool on) noexcept { + on_ = on; +} + +void Nullable::Enable() noexcept { + on_ = true; +} + +void Nullable::Disable() noexcept { + on_ = false; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/types/Nullable.hpp b/lib/parser/types/Nullable.hpp new file mode 100644 index 0000000..ffc8c3e --- /dev/null +++ b/lib/parser/types/Nullable.hpp @@ -0,0 +1,26 @@ +#ifndef PARSER_NULLABLE_HPP_ +#define PARSER_NULLABLE_HPP_ + +namespace ovum::compiler::parser { + +class Nullable { +public: + Nullable() noexcept = default; + + explicit Nullable(bool on) noexcept; + + bool IsOn() const noexcept; + + void Set(bool on) noexcept; + + void Enable() noexcept; + + void Disable() noexcept; + +private: + bool on_ = false; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_NULLABLE_HPP_ diff --git a/lib/parser/types/Param.cpp b/lib/parser/types/Param.cpp new file mode 100644 index 0000000..9e4d022 --- /dev/null +++ b/lib/parser/types/Param.cpp @@ -0,0 +1,17 @@ +#include "lib/parser/types/Param.hpp" + +namespace ovum::compiler::parser { + +Param::Param(std::string name, TypeReference typeReference) : + name_(std::move(name)), reference_(std::move(typeReference)) { +} + +const std::string& Param::GetName() { + return name_; +} + +const TypeReference& Param::GetType() { + return reference_; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/types/Param.hpp b/lib/parser/types/Param.hpp new file mode 100644 index 0000000..ceb3f68 --- /dev/null +++ b/lib/parser/types/Param.hpp @@ -0,0 +1,25 @@ +#ifndef PARSER_PARAM_HPP_ +#define PARSER_PARAM_HPP_ + +#include + +#include "TypeReference.hpp" + +namespace ovum::compiler::parser { + +class Param { +public: + Param(std::string name, TypeReference typeReference); + + [[nodiscard]] const std::string& GetName(); + + [[nodiscard]] const TypeReference& GetType(); + +private: + std::string name_; + TypeReference reference_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_PARAM_HPP_ diff --git a/lib/parser/types/ResolvedTypeHandle.cpp b/lib/parser/types/ResolvedTypeHandle.cpp new file mode 100644 index 0000000..b228d66 --- /dev/null +++ b/lib/parser/types/ResolvedTypeHandle.cpp @@ -0,0 +1,27 @@ +#include "lib/parser/types/ResolvedTypeHandle.hpp" + +#include + +namespace ovum::compiler::parser { + +ResolvedTypeHandle::ResolvedTypeHandle(const void* decl, std::string mangled, bool is_interface) : + decl_(decl), mangled_(std::move(mangled)), is_interface_(is_interface) { +} + +const void* ResolvedTypeHandle::Decl() const noexcept { + return decl_; +} + +std::string_view ResolvedTypeHandle::Mangled() const noexcept { + return mangled_; +} + +bool ResolvedTypeHandle::IsInterface() const noexcept { + return is_interface_; +} + +bool ResolvedTypeHandle::IsValid() const noexcept { + return decl_ != nullptr; +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/types/ResolvedTypeHandle.hpp b/lib/parser/types/ResolvedTypeHandle.hpp new file mode 100644 index 0000000..74b9f57 --- /dev/null +++ b/lib/parser/types/ResolvedTypeHandle.hpp @@ -0,0 +1,33 @@ +#ifndef PARSER_RESOLVEDTYPEHANDLE_HPP_ +#define PARSER_RESOLVEDTYPEHANDLE_HPP_ + +#include + +namespace ovum::compiler::parser { + +class ResolvedTypeHandle { +public: + ResolvedTypeHandle() = default; + ResolvedTypeHandle(const void* decl, std::string mangled, bool is_interface); + + ResolvedTypeHandle(const ResolvedTypeHandle& other) = default; + ResolvedTypeHandle(ResolvedTypeHandle&& other) noexcept = default; + ~ResolvedTypeHandle() = default; + + ResolvedTypeHandle& operator=(const ResolvedTypeHandle& other) = default; + ResolvedTypeHandle& operator=(ResolvedTypeHandle&& other) noexcept = default; + + const void* Decl() const noexcept; + std::string_view Mangled() const noexcept; + bool IsInterface() const noexcept; + bool IsValid() const noexcept; + +private: + const void* decl_ = nullptr; + std::string mangled_; + bool is_interface_ = false; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_RESOLVEDTYPEHANDLE_HPP_ diff --git a/lib/parser/types/TypeReference.cpp b/lib/parser/types/TypeReference.cpp new file mode 100644 index 0000000..9e5bfc2 --- /dev/null +++ b/lib/parser/types/TypeReference.cpp @@ -0,0 +1,258 @@ +#include "lib/parser/types/TypeReference.hpp" + +#include +#include +#include +#include + +namespace ovum::compiler::parser { + +TypeReference::TypeReference() = default; + +TypeReference::TypeReference(std::string name) { + qname_.clear(); + qname_.push_back(std::move(name)); +} + +TypeReference::TypeReference(std::vector qname) : qname_(std::move(qname)) { +} + +TypeReference::TypeReference(const TypeReference& ref) : + qname_(ref.qname_), type_args_(ref.type_args_), nullable_(ref.nullable_) { + if (ref.resolved_) { + resolved_ = std::make_unique(*ref.resolved_); + } +} + +TypeReference::TypeReference(TypeReference&& ref) noexcept : + qname_(std::move(ref.qname_)), type_args_(std::move(ref.type_args_)), nullable_(ref.nullable_), + resolved_(std::move(ref.resolved_)) { +} + +TypeReference::~TypeReference() = default; + +TypeReference& TypeReference::operator=(const TypeReference& ref) { + if (this == &ref) { + return *this; + } + + qname_ = ref.qname_; + type_args_ = ref.type_args_; + nullable_ = ref.nullable_; + + if (ref.resolved_) { + resolved_ = std::make_unique(*ref.resolved_); + } else { + resolved_.reset(); + } + + return *this; +} + +TypeReference& TypeReference::operator=(TypeReference&& ref) noexcept { + if (this == &ref) { + return *this; + } + + qname_ = std::move(ref.qname_); + type_args_ = std::move(ref.type_args_); + nullable_ = ref.nullable_; + resolved_ = std::move(ref.resolved_); + return *this; +} + +TypeReference& TypeReference::operator=(std::string name) { + qname_.clear(); + qname_.push_back(std::move(name)); + InvalidateResolution(); + return *this; +} + +bool TypeReference::StructurallyEquals(const TypeReference& other) const noexcept { + if (nullable_.IsOn() != other.nullable_.IsOn()) { + return false; + } + + if (qname_ != other.qname_) { + return false; + } + + if (type_args_.size() != other.type_args_.size()) { + return false; + } + + for (std::size_t i = 0; i < type_args_.size(); ++i) { + if (!type_args_[i].StructurallyEquals(other.type_args_[i])) { + return false; + } + } + return true; +} + +std::string TypeReference::StableKey() const { + std::ostringstream out; + out << JoinQualified(qname_); + if (!type_args_.empty()) { + out << '<' << ArgsToString(type_args_) << '>'; + } + + if (nullable_.IsOn()) { + out << '?'; + } + + return out.str(); +} + +std::string TypeReference::ToStringHuman() const { + return StableKey(); +} + +const std::vector& TypeReference::QualifiedName() const noexcept { + return qname_; +} + +std::string_view TypeReference::SimpleName() const noexcept { + if (qname_.empty()) { + return {}; + } + + return qname_.back(); +} + +void TypeReference::SetQualifiedName(std::vector qname) { + qname_ = std::move(qname); + InvalidateResolution(); +} + +void TypeReference::SetSimpleName(std::string name) { + if (qname_.empty()) { + qname_.push_back(std::move(name)); + } else { + qname_.back() = std::move(name); + } + + InvalidateResolution(); +} + +void TypeReference::PushQualifier(std::string qualifier) { + qname_.push_back(std::move(qualifier)); + InvalidateResolution(); +} + +bool TypeReference::PopFrontQualifier() { + if (qname_.empty()) { + return false; + } + + qname_.erase(qname_.begin()); + InvalidateResolution(); + return true; +} + +bool TypeReference::PopBackQualifier() { + if (qname_.empty()) { + return false; + } + + qname_.pop_back(); + InvalidateResolution(); + return true; +} + +const std::vector& TypeReference::TypeArguments() const noexcept { + return type_args_; +} + +std::vector& TypeReference::MutableTypeArguments() noexcept { + return type_args_; +} + +void TypeReference::ClearTypeArguments() { + type_args_.clear(); + InvalidateResolution(); +} + +void TypeReference::AddTypeArgument(TypeReference arg) { + type_args_.emplace_back(std::move(arg)); + InvalidateResolution(); +} + +std::size_t TypeReference::Arity() const noexcept { + return type_args_.size(); +} + +const Nullable& TypeReference::Nullability() const noexcept { + return nullable_; +} + +bool TypeReference::IsNullable() const noexcept { + return nullable_.IsOn(); +} + +void TypeReference::SetNullable(bool on) noexcept { + nullable_.Set(on); +} + +void TypeReference::MakeNullable() noexcept { + nullable_.Enable(); +} + +void TypeReference::MakeNonNullable() noexcept { + nullable_.Disable(); +} + +TypeReference TypeReference::WithoutNullable() const { + TypeReference copy{*this}; + copy.MakeNonNullable(); + copy.InvalidateResolution(); + return copy; +} + +bool TypeReference::IsResolved() const noexcept { + return static_cast(resolved_); +} + +const ResolvedTypeHandle* TypeReference::Resolved() const noexcept { + return resolved_ ? resolved_.get() : nullptr; +} + +void TypeReference::SetResolvedHandle(const void* decl, std::string mangled, bool is_interface) { + resolved_ = std::make_unique(decl, std::move(mangled), is_interface); +} + +void TypeReference::ResetResolvedHandle() noexcept { + resolved_.reset(); +} + +std::string TypeReference::JoinQualified(const std::vector& parts) { + if (parts.empty()) { + return {}; + } + + std::ostringstream out; + for (std::size_t i = 0; i < parts.size(); ++i) { + if (i) { + out << "."; + } + out << parts[i]; + } + return out.str(); +} + +std::string TypeReference::ArgsToString(const std::vector& args) { + std::ostringstream out; + for (std::size_t i = 0; i < args.size(); ++i) { + if (i) { + out << ", "; + } + + out << args[i].StableKey(); + } + return out.str(); +} + +void TypeReference::InvalidateResolution() noexcept { + resolved_.reset(); +} + +} // namespace ovum::compiler::parser diff --git a/lib/parser/types/TypeReference.hpp b/lib/parser/types/TypeReference.hpp new file mode 100644 index 0000000..3aaf354 --- /dev/null +++ b/lib/parser/types/TypeReference.hpp @@ -0,0 +1,79 @@ +#ifndef PARSER_TYPEREFERENCE_HPP_ +#define PARSER_TYPEREFERENCE_HPP_ + +#include +#include +#include +#include + +#include "Nullable.hpp" +#include "ResolvedTypeHandle.hpp" + +namespace ovum::compiler::parser { + +class TypeReference { +public: + TypeReference(); + explicit TypeReference(std::string name); + explicit TypeReference(std::vector qname); + + TypeReference(const TypeReference& ref); + TypeReference(TypeReference&& ref) noexcept; + + ~TypeReference(); + + TypeReference& operator=(const TypeReference& ref); + TypeReference& operator=(TypeReference&& ref) noexcept; + + TypeReference& operator=(std::string name); + + bool StructurallyEquals(const TypeReference& other) const noexcept; + + std::string StableKey() const; + std::string ToStringHuman() const; + + const std::vector& QualifiedName() const noexcept; + std::string_view SimpleName() const noexcept; + + void SetQualifiedName(std::vector qname); + void SetSimpleName(std::string name); + void PushQualifier(std::string qualifier); + bool PopFrontQualifier(); + bool PopBackQualifier(); + + const std::vector& TypeArguments() const noexcept; + std::vector& MutableTypeArguments() noexcept; + + void ClearTypeArguments(); + void AddTypeArgument(TypeReference arg); + std::size_t Arity() const noexcept; + + const Nullable& Nullability() const noexcept; + bool IsNullable() const noexcept; + void SetNullable(bool on) noexcept; + void MakeNullable() noexcept; + void MakeNonNullable() noexcept; + + TypeReference WithoutNullable() const; + + bool IsResolved() const noexcept; + const ResolvedTypeHandle* Resolved() const noexcept; + + void SetResolvedHandle(const void* decl, std::string mangled, bool is_interface); + void ResetResolvedHandle() noexcept; + +private: + static std::string JoinQualified(const std::vector& parts); + static std::string ArgsToString(const std::vector& args); + + void InvalidateResolution() noexcept; + + std::vector qname_; + std::vector type_args_; + Nullable nullable_; + std::unique_ptr resolved_; +}; + +} // namespace ovum::compiler::parser + +#endif // PARSER_TYPEREFERENCE_HPP_ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ba3726d..e519018 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,6 +10,7 @@ add_executable( lexer_tests.cpp test_suites/PreprocessorUnitTestSuite.cpp preprocessor_tests.cpp + parser_bytecode_tests.cpp ) target_link_libraries( @@ -17,6 +18,7 @@ target_link_libraries( compiler_ui lexer preprocessor + parser GTest::gtest_main ) diff --git a/tests/parser_bytecode_tests.cpp b/tests/parser_bytecode_tests.cpp new file mode 100644 index 0000000..f969dfa --- /dev/null +++ b/tests/parser_bytecode_tests.cpp @@ -0,0 +1,177 @@ +#include +#include +#include + +#include "lib/lexer/Lexer.hpp" +#include "lib/preprocessor/Preprocessor.hpp" +#include "lib/parser/ParserFsm.hpp" +#include "lib/parser/ast/BuilderAstFactory.hpp" +#include "lib/parser/diagnostics/DiagnosticCollector.hpp" +#include "lib/parser/pratt/PrattExpressionParser.hpp" +#include "lib/parser/pratt/DefaultOperatorResolver.hpp" +#include "lib/parser/type_parser/QNameTypeParser.hpp" +#include "lib/parser/tokens/token_streams/VectorTokenStream.hpp" + +namespace ovum::compiler::parser { + +class ParserBytecodeTest : public ::testing::Test { +protected: + void SetUp() override { + factory_ = std::make_shared(); + type_parser_ = std::make_unique(*factory_); + auto resolver = std::make_unique(); + expr_parser_ = std::make_unique(std::move(resolver), factory_); + parser_ = std::make_unique(std::move(expr_parser_), std::move(type_parser_), + std::shared_ptr(factory_)); + } + + std::unique_ptr ParseCode(const std::string& code) { + // Tokenize + auto tokens = lexer_.Tokenize(code); + if (tokens.empty()) { + return nullptr; + } + + // Preprocess + preprocessor_.Process(tokens, diags_); + + // Parse + VectorTokenStream stream(tokens); + return parser_->Parse(stream, diags_); + } + + Lexer lexer_; + Preprocessor preprocessor_; + DiagnosticCollector diags_; + std::unique_ptr parser_; + std::unique_ptr expr_parser_; + std::unique_ptr type_parser_; + std::shared_ptr factory_; +}; + +TEST_F(ParserBytecodeTest, ParseSimpleFunction) { + const std::string code = R"( +fun main(): Void { + return; +} +)"; + + auto module = ParseCode(code); + ASSERT_NE(module, nullptr); + EXPECT_EQ(diags_.ErrorCount(), 0); + EXPECT_GT(module->Decls().size(), 0); +} + +TEST_F(ParserBytecodeTest, ParseFunctionWithReturn) { + const std::string code = R"( +fun add(a: Int, b: Int): Int { + return a + b; +} +)"; + + auto module = ParseCode(code); + ASSERT_NE(module, nullptr); + EXPECT_EQ(diags_.ErrorCount(), 0); +} + +TEST_F(ParserBytecodeTest, ParseTypeWithArguments) { + const std::string code = R"( +fun process(list: List): Void { + return; +} +)"; + + auto module = ParseCode(code); + ASSERT_NE(module, nullptr); + EXPECT_EQ(diags_.ErrorCount(), 0); +} + +TEST_F(ParserBytecodeTest, ParseNestedTypeArguments) { + const std::string code = R"( +fun process(map: Map>): Void { + return; +} +)"; + + auto module = ParseCode(code); + ASSERT_NE(module, nullptr); + EXPECT_EQ(diags_.ErrorCount(), 0); +} + +TEST_F(ParserBytecodeTest, ParseNullableType) { + const std::string code = R"( +fun getValue(): String? { + return null; +} +)"; + + auto module = ParseCode(code); + ASSERT_NE(module, nullptr); + EXPECT_EQ(diags_.ErrorCount(), 0); +} + +TEST_F(ParserBytecodeTest, ParseClass) { + const std::string code = R"( +class Point { + val x: Int; + val y: Int; +} +)"; + + auto module = ParseCode(code); + ASSERT_NE(module, nullptr); + EXPECT_EQ(diags_.ErrorCount(), 0); +} + +TEST_F(ParserBytecodeTest, ParseInterface) { + const std::string code = R"( +interface Drawable { + fun draw(): Void; +} +)"; + + auto module = ParseCode(code); + ASSERT_NE(module, nullptr); + EXPECT_EQ(diags_.ErrorCount(), 0); +} + +TEST_F(ParserBytecodeTest, ParseGlobalVariable) { + const std::string code = R"( +val global: Int = 42; +)"; + + auto module = ParseCode(code); + ASSERT_NE(module, nullptr); + EXPECT_EQ(diags_.ErrorCount(), 0); +} + +TEST_F(ParserBytecodeTest, ParseIfStatement) { + const std::string code = R"( +fun test(x: Int): Void { + if (x > 0) { + return; + } +} +)"; + + auto module = ParseCode(code); + ASSERT_NE(module, nullptr); + EXPECT_EQ(diags_.ErrorCount(), 0); +} + +TEST_F(ParserBytecodeTest, ParseWhileLoop) { + const std::string code = R"( +fun loop(): Void { + while (true) { + break; + } +} +)"; + + auto module = ParseCode(code); + ASSERT_NE(module, nullptr); + EXPECT_EQ(diags_.ErrorCount(), 0); +} + +} // namespace ovum::compiler::parser +