diff --git a/include/serene/exprs/def.h b/include/serene/exprs/def.h index 25e64cb..45c34c2 100644 --- a/include/serene/exprs/def.h +++ b/include/serene/exprs/def.h @@ -55,8 +55,12 @@ public: static bool classof(const Expression *e); /// Create a Def node out a list. The list should contain the - /// correct `def` form like `(def blah value)`. - static maybe_node make(List *); + /// correct `def` form like `(def blah value)`. This function + /// is supposed to be used in the semantic analysis phase. + /// + /// \param ctx The semantic analysis context object. + /// \param list the list containing the `def` form + static maybe_node make(reader::SemanticContext &ctx, List *list); ~Def() = default; }; diff --git a/include/serene/exprs/fn.h b/include/serene/exprs/fn.h index 5c2cf8e..05c8572 100644 --- a/include/serene/exprs/fn.h +++ b/include/serene/exprs/fn.h @@ -60,8 +60,13 @@ public: /// Creates a function node out of a function definition /// in a list. the list has to contain the correct definition - /// of a function, for exmaple: `(fn (args1 arg2) body)` - static maybe_node make(List *); + /// of a function, for exmaple: `(fn (args1 arg2) body)`.This function + /// is supposed to be used in the semantic analysis phase. + /// + /// \param ctx The semantic analysis context object. + /// \param list the list containing the `fn` form + static maybe_node make(reader::SemanticContext &ctx, List *list); + ~Fn() = default; }; diff --git a/src/serene/exprs/def.cpp b/src/serene/exprs/def.cpp index dfed383..03016fb 100644 --- a/src/serene/exprs/def.cpp +++ b/src/serene/exprs/def.cpp @@ -46,26 +46,48 @@ bool Def::classof(const Expression *e) { return e->getType() == ExprType::Def; }; -maybe_node Def::make(List *list) { +maybe_node Def::make(reader::SemanticContext &ctx, List *list) { // TODO: Add support for docstring as the 3rd argument (4th element) + if (list->count() != 3) { std::string msg = llvm::formatv("Expected 3 got {0}", list->count()); return Result::success(makeAndCast( &errors::DefWrongNumberOfArgs, list->elements[0], msg)); } + // Make sure that the list starts with a `def` Symbol *defSym = llvm::dyn_cast(list->elements[0].get()); assert((defSym && defSym->name == "def") && "The first element of the list should be a 'def'."); + // Make sure that the first argument is a Symbol Symbol *binding = llvm::dyn_cast(list->elements[1].get()); - if (!binding) { return Result::success(makeAndCast( &errors::DefExpectSymbol, list->elements[1], "")); } - node def = exprs::make(list->location, binding->name, list->elements[2]); + // Analyze the value + maybe_node value = list->elements[2]->analyze(ctx); + node analyzedValue; + + if (value) { + // Success value + auto &valueNode = value.getValue(); + + if (valueNode) { + // A rewrite is necessary + analyzedValue = valueNode; + } else { + // no rewrite + analyzedValue = list->elements[2]; + } + } else { + // Error value + return value; + } + + node def = exprs::make(list->location, binding->name, analyzedValue); return Result::success(def); }; } // namespace exprs diff --git a/src/serene/exprs/fn.cpp b/src/serene/exprs/fn.cpp index 41d969e..336ff90 100644 --- a/src/serene/exprs/fn.cpp +++ b/src/serene/exprs/fn.cpp @@ -47,7 +47,7 @@ maybe_node Fn::analyze(reader::SemanticContext &ctx) { bool Fn::classof(const Expression *e) { return e->getType() == ExprType::Fn; }; -maybe_node Fn::make(List *list) { +maybe_node Fn::make(reader::SemanticContext &ctx, List *list) { // TODO: Add support for docstring as the 3rd argument (4th element) if (list->count() < 2) { std::string msg = diff --git a/src/serene/exprs/list.cpp b/src/serene/exprs/list.cpp index 8bb1369..984bcff 100644 --- a/src/serene/exprs/list.cpp +++ b/src/serene/exprs/list.cpp @@ -64,11 +64,11 @@ maybe_node List::analyze(reader::SemanticContext &ctx) { if (sym) { if (sym->name == "def") { - return Def::make(this); + return Def::make(ctx, this); } if (sym->name == "fn") { - return Fn::make(this); + return Fn::make(ctx, this); } } diff --git a/src/tests/exprs/list_tests.cpp.inc b/src/tests/exprs/list_tests.cpp.inc index fc693fa..788a7dd 100644 --- a/src/tests/exprs/list_tests.cpp.inc +++ b/src/tests/exprs/list_tests.cpp.inc @@ -100,6 +100,12 @@ TEST_CASE("List semantic analysis of 'def'", "[semantic]") { afterAst = analyzer.analyze(ast.getValue()); REQUIRE(afterAst); CHECK(astToString(&afterAst.getValue()) == " >"); + + ast = reader::read("(def a (fn () a))"); + afterAst = analyzer.analyze(ast.getValue()); + REQUIRE(afterAst); + CHECK(astToString(&afterAst.getValue()) == + " to >>"); } TEST_CASE("List semantic analysis for 'fn'", "[semantic]") { @@ -107,22 +113,25 @@ TEST_CASE("List semantic analysis for 'fn'", "[semantic]") { auto ast = reader::read("(fn)"); auto afterAst = analyzer.analyze(ast.getValue()); REQUIRE(afterAst); - CHECK(astToString(&afterAst.getValue()) == ""); + CHECK(astToString(&afterAst.getValue()) == + ""); ast = reader::read("(fn ())"); afterAst = analyzer.analyze(ast.getValue()); REQUIRE(afterAst); - CHECK(astToString(&afterAst.getValue()) == " to <>>"); + CHECK(astToString(&afterAst.getValue()) == " to <>>"); ast = reader::read("(fn (a b c) a a a)"); afterAst = analyzer.analyze(ast.getValue()); REQUIRE(afterAst); - CHECK(astToString(&afterAst.getValue()) == " to >"); + CHECK(astToString(&afterAst.getValue()) == + " to >"); ast = reader::read("(fn () a b)"); afterAst = analyzer.analyze(ast.getValue()); REQUIRE(afterAst); - CHECK(astToString(&afterAst.getValue()) == " to >"); + CHECK(astToString(&afterAst.getValue()) == + " to >"); } } // namespace exprs } // namespace serene