diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3a102fe..684b870 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -47,6 +47,7 @@ add_llvm_executable(norac environment.cpp utils/idpool.cpp utils/upcast.cpp + utils/downcast.cpp parser/parse.cpp interpreter/interpreter.cpp ast/arithplus.cpp @@ -61,5 +62,6 @@ add_llvm_executable(norac ast/application.cpp ast/setbang.cpp ast/ifcond.cpp + ast/letvalues.cpp ) target_link_libraries(norac PRIVATE ${LIBS}) \ No newline at end of file diff --git a/src/ast/letvalues.cpp b/src/ast/letvalues.cpp new file mode 100644 index 0000000..13a823c --- /dev/null +++ b/src/ast/letvalues.cpp @@ -0,0 +1,38 @@ +#include "ast/letvalues.h" + +#include + +#include "exprnode_inc.h" + +using namespace nir; + +LetValues::LetValues(const LetValues &DV) { + for (auto const &Id : DV.Ids) { + Ids.emplace_back(Id); + } + for (auto const &Expr : DV.Exprs) { + Exprs.emplace_back(std::make_unique(*Expr)); + } + for (auto const &Expr : DV.Body) { + Body.emplace_back(std::make_unique(*Expr)); + } +} + +void LetValues::appendBinding(std::vector &&Ids, + std::unique_ptr Expr) { + this->Ids.emplace_back(std::move(Ids)); + this->Exprs.emplace_back(std::move(Expr)); +} + +void LetValues::appendBody(std::unique_ptr Expr) { + Body.emplace_back(std::move(Expr)); +} +nir::LetValues::IdRange LetValues::getBindingIds(size_t Idx) const { + assert(Idx < Ids.size()); + return IdRange{Ids[Idx]}; +} +ExprNode const &LetValues::getBindingExpr(size_t Idx) const { + return *Exprs[Idx]; +} +ExprNode const &LetValues::getBodyExpr(size_t Idx) const { return *Body[Idx]; } +size_t nir::LetValues::exprsCount() const { return Exprs.size(); } diff --git a/src/dumper.cpp b/src/dumper.cpp index 3682120..c3fd849 100644 --- a/src/dumper.cpp +++ b/src/dumper.cpp @@ -20,9 +20,29 @@ void Dumper::operator()(nir::Values const &V) { } } -void Dumper::operator()(nir::DefineValues const &DV) {} +void Dumper::operator()(nir::DefineValues const &DV) { + Dumper Dump; + std::cout << "(define-values ("; -void Dumper::operator()(nir::ArithPlus const &AP) {} + for (const auto &Id : DV.getIds()) { + Dump(Id); + std::cout << " "; + } + + std::cout << ") "; + std::visit(Dump, DV.getBody()); + std::cout << ")"; +} + +void Dumper::operator()(nir::ArithPlus const &AP) { + Dumper Dump; + std::cout << "(+ "; + for (const auto &Arg : AP.getArgs()) { + std::visit(Dump, *Arg); + std::cout << " "; + } + std::cout << ")"; +} void Dumper::operator()(nir::Void const &Vd) { // do nothing @@ -162,4 +182,40 @@ void Dumper::operator()(nir::IfCond const &If) { void Dumper::operator()(nir::BooleanLiteral const &Bool) { std::cout << (Bool.value() ? "#t" : "#f"); +} + +void Dumper::operator()(nir::LetValues const &Let) { + Dumper Dump; + std::cout << "(let-values ("; + + for (size_t I = 0; I < Let.bindingCount(); ++I) { + // Starts binding + std::cout << "["; + + // Starts binding Id List + std::cout << "("; + + for (const auto &Id : Let.getBindingIds(I)) { + Dump(Id); + std::cout << " "; + } + + // Closes binding Id List + std::cout << ")"; + + std::cout << " "; + std::visit(Dump, Let.getBindingExpr(I)); + + // Closes binding + std::cout << "]"; + } + + // Close bindings + std::cout << ") "; + + for (size_t I = 0; I < Let.bodyCount(); ++I) { + std::visit(Dump, Let.getBodyExpr(I)); + std::cout << "\n"; + } + std::cout << ")"; } \ No newline at end of file diff --git a/src/exprnode.cpp b/src/exprnode.cpp index e3a1c80..365b729 100644 --- a/src/exprnode.cpp +++ b/src/exprnode.cpp @@ -44,4 +44,8 @@ std::unique_ptr nir::ToTopLevelNode::operator()(nir::IfCond &&If) { std::unique_ptr nir::ToTopLevelNode::operator()(nir::BooleanLiteral &&Bool) { return std::make_unique(std::move(Bool)); +} +std::unique_ptr +nir::ToTopLevelNode::operator()(nir::LetValues &&LV) { + return std::make_unique(std::move(LV)); } \ No newline at end of file diff --git a/src/include/README.md b/src/include/README.md index e253a8c..5cc37eb 100644 --- a/src/include/README.md +++ b/src/include/README.md @@ -18,6 +18,7 @@ ASTNodes |- +Application |- +SetBang |- +IfCond + |- +LetValues |- Value |- +Integer |- +Values diff --git a/src/include/ast/definevalues.h b/src/include/ast/definevalues.h index 4dbeb73..36ad61e 100644 --- a/src/include/ast/definevalues.h +++ b/src/include/ast/definevalues.h @@ -27,6 +27,7 @@ class DefineValues { : BeginIt(Ids.cbegin()), EndIt(Ids.cend()) {} [[nodiscard]] auto begin() const { return BeginIt; } [[nodiscard]] auto end() const { return EndIt; } + const Identifier &operator[](size_t Idx) const { return *(BeginIt + Idx); } private: std::vector::const_iterator BeginIt, EndIt; diff --git a/src/include/ast/letvalues.h b/src/include/ast/letvalues.h new file mode 100644 index 0000000..1518a33 --- /dev/null +++ b/src/include/ast/letvalues.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +#include "../exprnode.h" +#include "identifier.h" + +namespace nir { + +class LetValues { +public: + LetValues() = default; + LetValues(const LetValues &DV); + LetValues(LetValues &&DV) = default; + LetValues &operator=(const LetValues &DV) = delete; + LetValues &operator=(LetValues &&DV) = default; + ~LetValues() = default; + + // View over the Ids + // FIXME: we should be able to use C++20 view_interface here + // although my initial attempt failed. + class IdRange { + public: + IdRange() = delete; + IdRange(const std::vector &Ids) + : BeginIt(Ids.cbegin()), EndIt(Ids.cend()) {} + [[nodiscard]] auto begin() const { return BeginIt; } + [[nodiscard]] auto end() const { return EndIt; } + [[nodiscard]] Identifier const &operator[](size_t Idx) const { + return *(BeginIt + Idx); + } + + private: + std::vector::const_iterator BeginIt, EndIt; + }; + + IdRange getBindingIds(size_t Idx) const; + ExprNode const &getBindingExpr(size_t Idx) const; + ExprNode const &getBodyExpr(size_t Idx) const; + + void appendBinding(std::vector &&Ids, + std::unique_ptr Expr); + void appendBody(std::unique_ptr Expr); + + size_t bindingCount() const { + assert(Ids.size() == Exprs.size()); + return Ids.size(); + } + size_t idsCount() const { return Ids.size(); } + size_t exprsCount() const; + size_t bodyCount() const { return Body.size(); } + +private: + std::vector> Ids; + std::vector> Exprs; + std::vector> Body; +}; + +}; // namespace nir \ No newline at end of file diff --git a/src/include/astnode.h b/src/include/astnode.h index 143642e..8f7895d 100644 --- a/src/include/astnode.h +++ b/src/include/astnode.h @@ -18,9 +18,11 @@ class Application; class SetBang; class IfCond; class BooleanLiteral; +class LetValues; -using ASTNode = std::variant; +using ASTNode = + std::variant; }; // namespace nir \ No newline at end of file diff --git a/src/include/dumper.h b/src/include/dumper.h index 2525740..43fcc91 100644 --- a/src/include/dumper.h +++ b/src/include/dumper.h @@ -22,4 +22,5 @@ struct Dumper { void operator()(nir::SetBang const &SB); void operator()(nir::IfCond const &If); void operator()(nir::BooleanLiteral const &Bool); + void operator()(nir::LetValues const &LV); }; diff --git a/src/include/exprnode.h b/src/include/exprnode.h index 13c4625..63f2ada 100644 --- a/src/include/exprnode.h +++ b/src/include/exprnode.h @@ -19,10 +19,11 @@ class Application; class SetBang; class IfCond; class BooleanLiteral; +class LetValues; using ExprNode = std::variant; + List, Application, SetBang, IfCond, BooleanLiteral, LetValues>; struct ToTopLevelNode { std::unique_ptr operator()(nir::Identifier &&Id); @@ -37,6 +38,7 @@ struct ToTopLevelNode { std::unique_ptr operator()(nir::SetBang &&SB); std::unique_ptr operator()(nir::IfCond &&If); std::unique_ptr operator()(nir::BooleanLiteral &&Bool); + std::unique_ptr operator()(nir::LetValues &&LV); }; }; // namespace nir \ No newline at end of file diff --git a/src/include/exprnode_inc.h b/src/include/exprnode_inc.h index 5560cf4..65ea8e9 100644 --- a/src/include/exprnode_inc.h +++ b/src/include/exprnode_inc.h @@ -8,5 +8,6 @@ #include "ast/begin.h" #include "ast/identifier.h" #include "ast/ifcond.h" +#include "ast/letvalues.h" #include "ast/setbang.h" #include "valuenode_inc.h" \ No newline at end of file diff --git a/src/include/interpreter.h b/src/include/interpreter.h index 38b7494..ff2efb7 100644 --- a/src/include/interpreter.h +++ b/src/include/interpreter.h @@ -17,6 +17,8 @@ class Interpreter { public: + Interpreter(); + std::unique_ptr operator()(nir::Identifier const &Id); std::unique_ptr operator()(nir::Integer const &Int); std::unique_ptr operator()(nir::Linklet const &Linklet); @@ -31,6 +33,7 @@ class Interpreter { std::unique_ptr operator()(nir::SetBang const &SB); std::unique_ptr operator()(nir::IfCond const &If); std::unique_ptr operator()(nir::BooleanLiteral const &Bool); + std::unique_ptr operator()(nir::LetValues const &LV); private: // Environment map for identifiers. diff --git a/src/include/toplevelnode.h b/src/include/toplevelnode.h index 2da20a3..e6a0ab0 100644 --- a/src/include/toplevelnode.h +++ b/src/include/toplevelnode.h @@ -17,9 +17,10 @@ class Application; class SetBang; class IfCond; class BooleanLiteral; +class LetValues; using TLNode = std::variant; + SetBang, IfCond, BooleanLiteral, LetValues>; }; // namespace nir \ No newline at end of file diff --git a/src/include/utils/downcast.h b/src/include/utils/downcast.h new file mode 100644 index 0000000..777b642 --- /dev/null +++ b/src/include/utils/downcast.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +#include "exprnode.h" +#include "valuenode.h" + +std::unique_ptr +downcastExprToValueNode(std::unique_ptr &&E); \ No newline at end of file diff --git a/src/interpreter/interpreter.cpp b/src/interpreter/interpreter.cpp index c461141..c7be5b4 100644 --- a/src/interpreter/interpreter.cpp +++ b/src/interpreter/interpreter.cpp @@ -10,13 +10,21 @@ #include "ast/booleanliteral.h" #include "ast/formal.h" #include "ast/linklet.h" +#include "dumper.h" #include "toplevelnode_inc.h" +#include "utils/downcast.h" #include "utils/overloaded.h" #include "utils/upcast.h" #include "valuenode.h" #include +// Constructor for interpreter sets up initial environment. +Interpreter::Interpreter() { + // Add initial environment. + Envs.emplace_back(); +} + std::unique_ptr Interpreter::operator()(nir::Identifier const &Id) { // Check if there's a binding for Id in environment, @@ -33,10 +41,13 @@ Interpreter::operator()(nir::Identifier const &Id) { // does not work here? for (auto Env = Envs.rbegin(); Env != Envs.rend(); ++Env) { auto V = Env->lookup(Id); - return V; + if (V) { + return V; + } } // FIXME: error here UndefineIdentifier. + std::wcerr << "Undefined Identifier: " << Id.getName() << std::endl; return nullptr; } @@ -68,6 +79,14 @@ std::unique_ptr Interpreter::operator()(nir::Values const &V) { std::unique_ptr D = std::visit(*this, *Expr); ValuesVec.emplace_back(upcastNode(D)); } + + // If values contains a single value, it evaluates to that value. + if (ValuesVec.size() == 1) { + std::unique_ptr V = + downcastExprToValueNode(std::move(ValuesVec[0])); + return V; + } + std::unique_ptr Values = std::make_unique(nir::Values(std::move(ValuesVec))); return Values; @@ -84,6 +103,11 @@ Interpreter::operator()(nir::DefineValues const &DV) { } // 2. Check number of values and number of identifiers match. + // If there's only one identifier, the variable is assigned the value. + if (DV.countIds() == 1) { + Envs.back().add(DV.getIds()[0], std::move(D)); + return std::make_unique(nir::Void()); + } // Check if the expression is a Values. if (!std::holds_alternative(*D)) { @@ -100,7 +124,6 @@ Interpreter::operator()(nir::DefineValues const &DV) { } // 2. Add bindings to the environment. - Environment Env; size_t Idx = 0; for (const auto &Id : DV.getIds()) { const nir::ExprNode &E = V.getExprs()[Idx++]; @@ -128,11 +151,9 @@ Interpreter::operator()(nir::DefineValues const &DV) { }}, E); - Env.add(Id, std::move(Val)); + Envs.back().add(Id, std::move(Val)); } - Envs.push_back(Env); - // Return void. return std::make_unique(nir::Void()); } @@ -161,6 +182,7 @@ Interpreter::operator()(nir::ArithPlus const &AP) { } std::unique_ptr Interpreter::operator()(nir::Lambda const &L) { + PLOGD << "Interpreting Lambda: " << std::endl; return std::make_unique(L); } @@ -275,13 +297,13 @@ Interpreter::operator()(nir::Application const &A) { // current environment and return void. std::unique_ptr Interpreter::operator()(nir::SetBang const &SB) { - PLOGD << "Interpreting SetBang" - << "\n"; + PLOGD << "Interpreting SetBang\n"; // 1. Evaluate the expression. std::unique_ptr D = std::visit(*this, SB.getExpr()); // 2. Set the value of the identifier in the current environment. + assert(Envs.size() > 0); Environment &Env = Envs.back(); const nir::Identifier &Id = SB.getIdentifier(); if (!Env.lookup(Id)) { @@ -295,8 +317,7 @@ Interpreter::operator()(nir::SetBang const &SB) { } std::unique_ptr Interpreter::operator()(nir::IfCond const &I) { - PLOGD << "Interpreting If" - << "\n"; + PLOGD << "Interpreting If\n"; // 1. Evaluate the predicate. std::unique_ptr D = std::visit(*this, I.getCond()); @@ -313,5 +334,107 @@ std::unique_ptr Interpreter::operator()(nir::IfCond const &I) { std::unique_ptr Interpreter::operator()(nir::BooleanLiteral const &Bool) { + PLOGD << "Interpreting BooleanLiteral\n"; return std::make_unique(Bool); +} + +std::unique_ptr +Interpreter::operator()(nir::LetValues const &L) { + PLOGD << "Interpreting LetValues" + << "\n"; + IF_PLOG(plog::debug) { + Dumper Dump; + Dump(L); + PLOGD << "\n"; + } + + // 1. Evaluate each of the expressions in order. + std::vector> ExprValues; + ExprValues.reserve(L.exprsCount()); + for (size_t Idx = 0; Idx < L.exprsCount(); ++Idx) { + ExprValues.emplace_back(std::visit(*this, L.getBindingExpr(Idx))); + } + + // 2. Create an environment where each identifier is bound to the + // corresponding value. Then evaluate the body in this environment. + // If the binding variable list has a single identifier, it's simply assigned + // to the value of the expression. If on the other hand, it's a list of + // identifiers, then it should have the same length as the values list and + // each identifier is assigned to the corresponding value. + PLOGD << "Creating environment for LetValues\n"; + Environment Env; + for (size_t Idx = 0; Idx < ExprValues.size(); ++Idx) { + if (std::ranges::size(L.getBindingIds(Idx)) == 1) { + Env.add(*L.getBindingIds(Idx).begin(), std::move(ExprValues[Idx])); + } else { + std::unique_ptr V = std::move(ExprValues[Idx]); + + if (nir::Values *VsPtr = std::get_if(V.get())) { + std::unique_ptr Vs(VsPtr); + V.release(); + + if (std::ranges::size(L.getBindingIds(Idx)) != Vs->countExprs()) { + std::cerr << "Expected " << std::ranges::size(L.getBindingIds(Idx)) + << " values, got " << ExprValues.size() << std::endl; + return nullptr; + } + + const auto &IdsExprRange = L.getBindingIds(Idx); + const auto &ValuesExprRange = Vs->getExprs(); + + for (size_t Idx = 0; Idx < Vs->countExprs(); ++Idx) { + // All the elements in ValuesExprRange are Value, not Expr but we + // need to downcast them to add them to the environment. + const auto &E = ValuesExprRange[Idx]; + std::unique_ptr Val = std::visit( + overloaded{ + [](nir::Void const &V) { + return std::make_unique(V); + }, + [](nir::Integer const &I) { + return std::make_unique(I); + }, + [](nir::Values const &V) { + return std::make_unique(V); + }, + [](nir::Lambda const &L) { + return std::make_unique(L); + }, + [](nir::BooleanLiteral const &Bool) { + return std::make_unique(Bool); + }, + [](auto const &Err) { + std::cerr + << "Unexpected value in interpretation of let-values." + << std::endl; + return std::unique_ptr(); + }}, + E); + + PLOGD << "Adding " << std::wstring(IdsExprRange[Idx].getName()) + << " to environment\n"; + Env.add(IdsExprRange[Idx], std::move(Val)); + } + + } else { + Dumper Dump; + std::cerr << "Expected a values node, got "; + std::visit(Dump, *V); + return nullptr; + } + } + } + PLOGD << " Pushing new environment for LetValues\n"; + Envs.push_back(Env); + + PLOGD << "Evaluating body of LetValues\n"; + std::unique_ptr Result = nullptr; + for (size_t I = 0; I < L.exprsCount(); ++I) { + Result = std::visit(*this, L.getBodyExpr(I)); + } + + Envs.pop_back(); + + // 3. Return the result of the let-values expression. + return Result; } \ No newline at end of file diff --git a/src/parser/parse.cpp b/src/parser/parse.cpp index bc89d7b..285f4b1 100644 --- a/src/parser/parse.cpp +++ b/src/parser/parse.cpp @@ -62,7 +62,7 @@ // | (if ) - parseIfCond // | (begin ...+) - parseBegin // | (begin0 ...+) - parseBegin -// | (let-values ([ ...) ] ...) ) +// | (let-values ([ ...) ] ...) ) - parseLetValues // | (letrec-values ([( ...) ] ...) ) // | (set! ) - parseSetBang // | (quote ) @@ -847,6 +847,7 @@ std::unique_ptr parseApplication(Stream &S); std::unique_ptr parseSetBang(Stream &S); std::unique_ptr parseIfCond(Stream &S); std::unique_ptr parseBooleanLiteral(Stream &S); +std::unique_ptr parseLetValues(Stream &S); std::vector parseLinkletExports(Stream &S) { // parse a sequence of @@ -972,6 +973,11 @@ std::unique_ptr parseExpr(Stream &S) { return std::make_unique(std::move(*IC)); } + std::unique_ptr LV = parseLetValues(S); + if (LV) { + return std::make_unique(std::move(*LV)); + } + std::unique_ptr A = parseApplication(S); if (A) { return std::make_unique(std::move(*A)); @@ -1479,4 +1485,102 @@ std::unique_ptr parseBooleanLiteral(Stream &S) { S.rewindTo(Start); return nullptr; +} + +// Parse a let-values form. +// (let-values ([(id ...) val-expr] ...) body ...+) +std::unique_ptr parseLetValues(Stream &S) { + size_t Start = S.getPosition(); + + Tok T = gettok(S); + if (T.tok != Tok::TokType::LPAREN) { + S.rewindTo(Start); + return nullptr; + } + + T = gettok(S); + if (T.tok != Tok::TokType::LET_VALUES) { + S.rewindTo(Start); + return nullptr; + } + + auto Let = std::make_unique(); + + T = gettok(S); + if (T.tok != Tok::TokType::LPAREN) { + S.rewindTo(Start); + return nullptr; + } + + while (true) { + T = gettok(S); + if (T.tok == Tok::TokType::RPAREN) { + break; + } + + // Parse the binding. + if (T.tok != Tok::TokType::LPAREN) { + S.rewindTo(Start); + return nullptr; + } + + // Parse list of identifiers. + T = gettok(S); + if (T.tok != Tok::TokType::LPAREN) { + S.rewindTo(Start); + return nullptr; + } + + std::vector Ids; + while (true) { + T = gettok(S); + if (T.tok == Tok::TokType::RPAREN) { + break; + } + + S.rewind(T); + std::unique_ptr Id = parseIdentifier(S); + if (!Id) { + S.rewindTo(Start); + return nullptr; + } + Ids.push_back(*Id); + } + + // Parse the value expression. + std::unique_ptr Val = parseExpr(S); + if (!Val) { + S.rewindTo(Start); + return nullptr; + } + + T = gettok(S); + if (T.tok != Tok::TokType::RPAREN) { + S.rewindTo(Start); + return nullptr; + } + + Let->appendBinding(std::move(Ids), std::move(Val)); + } + + while (true) { + std::unique_ptr Exp = parseExpr(S); + if (!Exp) { + break; + } + Let->appendBody(std::move(Exp)); + } + + T = gettok(S); + if (T.tok != Tok::TokType::RPAREN) { + S.rewindTo(Start); + return nullptr; + } + + if (Let->bodyCount() == 0) { + S.rewindTo(Start); + return nullptr; + } + + return Let; } \ No newline at end of file diff --git a/src/utils/downcast.cpp b/src/utils/downcast.cpp new file mode 100644 index 0000000..dab91b8 --- /dev/null +++ b/src/utils/downcast.cpp @@ -0,0 +1,38 @@ +#include "utils/downcast.h" + +#include +#include +#include + +#include "exprnode_inc.h" +#include "utils/overloaded.h" +#include "valuenode_inc.h" + +std::unique_ptr +downcastExprToValueNode(std::unique_ptr &&E) { + nir::ExprNode *Expr = E.release(); + std::unique_ptr Val = std::visit( + overloaded{ + [](nir::Void const &V) { + return std::make_unique(V); + }, + [](nir::Integer const &I) { + return std::make_unique(I); + }, + [](nir::Values const &V) { + return std::make_unique(V); + }, + [](nir::Lambda const &L) { + return std::make_unique(L); + }, + [](nir::BooleanLiteral const &Bool) { + return std::make_unique(Bool); + }, + [](auto const &Err) { + std::cerr << "Error: Cannot downcast expression that's not a value." + << std::endl; + return std::unique_ptr(); + }}, + *Expr); + return Val; +} \ No newline at end of file diff --git a/test/integration/define-values2.rkt b/test/integration/define-values2.rkt new file mode 100644 index 0000000..1b2f902 --- /dev/null +++ b/test/integration/define-values2.rkt @@ -0,0 +1,5 @@ +;; RUN: norac %s | FileCheck %s +;; CHECK: 3 +(linklet () () + (define-values (x y) (values 1 2)) + (+ x y)) \ No newline at end of file diff --git a/test/integration/let-values.rkt b/test/integration/let-values.rkt new file mode 100644 index 0000000..8b93d6f --- /dev/null +++ b/test/integration/let-values.rkt @@ -0,0 +1,5 @@ +;; RUN: norac %s | FileCheck %s +;; CHECK: 3 +(linklet () () + (let-values (((x y) (values 1 2))) + (+ x y))) diff --git a/test/integration/let-values1.rkt b/test/integration/let-values1.rkt new file mode 100644 index 0000000..e464d0c --- /dev/null +++ b/test/integration/let-values1.rkt @@ -0,0 +1,5 @@ +;; RUN: norac %s | FileCheck %s +;; CHECK: 6 +(linklet () () + (let-values (((x y z) (values 1 2 3))) + (+ x (+ y z)))) diff --git a/test/integration/let-values2.rkt b/test/integration/let-values2.rkt new file mode 100644 index 0000000..ea2bcd0 --- /dev/null +++ b/test/integration/let-values2.rkt @@ -0,0 +1,6 @@ +;; RUN: norac %s | FileCheck %s +;; CHECK: 30 +(linklet () () + (let-values (((x) (values 10))) + (let-values (((y) (values 20))) + (+ x y)))) diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index a087abe..1fe597d 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -28,8 +28,10 @@ add_executable(test_parse ${PROJECT_SOURCE_DIR}/src/ast/application.cpp ${PROJECT_SOURCE_DIR}/src/ast/setbang.cpp ${PROJECT_SOURCE_DIR}/src/ast/ifcond.cpp + ${PROJECT_SOURCE_DIR}/src/ast/letvalues.cpp ${PROJECT_SOURCE_DIR}/src/utils/idpool.cpp ${PROJECT_SOURCE_DIR}/src/utils/upcast.cpp + ${PROJECT_SOURCE_DIR}/src/utils/downcast.cpp ${PROJECT_SOURCE_DIR}/src/exprnode.cpp ${PROJECT_SOURCE_DIR}/src/valuenode.cpp )