llvm-journey

LLVM Journey
git clone git://0xff.ir/g/llvm-journey.git
Log | Files | Refs | README | LICENSE

commit aa0b48a376aa8b07b5e909a01c549ae4a8dd1b22
parent f4d34a86af992ecea9b4ea519c0891c696b46118
Author: Mohammad-Reza Nabipoor <m.nabipoor@yahoo.com>
Date:   Sat,  5 Sep 2020 05:45:47 +0430

kaleidoscope_parser: Change reporting interface

Instead of writing on an output iterator, call a function object
to report the parsed entity.
The interface is now much more like `kal::lex`. Function
declarations and definitions are handled distinctly from statements.

Diffstat:
Mkaleidoscope_parser.hpp | 18+++++++++++++-----
Mtests/kaleidoscope_parser.test.cpp | 196+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
2 files changed, 145 insertions(+), 69 deletions(-)

diff --git a/kaleidoscope_parser.hpp b/kaleidoscope_parser.hpp @@ -285,9 +285,16 @@ parse_expr(FwdIt f, FwdIt l, ErrOutIt error) } // namespace detail -template<typename FwdIt, typename OutIt, typename ErrOutIt> +enum class ParsedEntityType +{ + FuncDecl, // function declaration + FuncDef, // function definition + Stmt, // statement +}; + +template<typename FwdIt, typename ParsedOp, typename ErrOutIt> FwdIt -parse(FwdIt f, FwdIt l, OutIt nodes, ErrOutIt error) +parse(FwdIt f, FwdIt l, ParsedOp parsed, ErrOutIt error) { static_assert(std::is_assignable<decltype(*error), ParseError<FwdIt>>::value, ""); @@ -310,7 +317,7 @@ parse(FwdIt f, FwdIt l, OutIt nodes, ErrOutIt error) if (r.parsed == f) // failure return err("parsing prototype failed"); f = r.parsed; - *nodes++ = std::move(r.node); + parsed(ParsedEntityType::FuncDecl, std::move(r.node)); } break; case TkType::Def: { @@ -350,7 +357,8 @@ parse(FwdIt f, FwdIt l, OutIt nodes, ErrOutIt error) if (r.parsed == f) // failure return err("parsing expression failed"); f = r.parsed; - *nodes++ = kal::Function{ std::move(proto), std::move(r.node) }; + parsed(ParsedEntityType::FuncDef, + kal::Function{ std::move(proto), std::move(r.node) }); } } break; @@ -367,7 +375,7 @@ parse(FwdIt f, FwdIt l, OutIt nodes, ErrOutIt error) if (r.parsed == f) // failure return err("parsing expression failed"); f = r.parsed; - *nodes++ = std::move(r.node); + parsed(ParsedEntityType::Stmt, std::move(r.node)); } break; case TkType::END: diff --git a/tests/kaleidoscope_parser.test.cpp b/tests/kaleidoscope_parser.test.cpp @@ -12,13 +12,21 @@ #include "kaleidoscope_ast.hpp" #include "kaleidoscope_tokens.hpp" +using kal::cast; using kal::to_string; +struct Parsed +{ + std::vector<kal::Prototype> decls; + std::vector<kal::Function> defs; + std::vector<kal::ASTNode> stmts; +}; + TEST_CASE("parse simple programs", "[simple]") { auto p = [](const std::string& s) { std::vector<kal::Token> tk; - std::vector<kal::ASTNode> nd; + Parsed parsed; kal::tokenize(s.cbegin(), s.cend(), std::back_inserter(tk)); tk.erase(std::remove_if( @@ -31,10 +39,38 @@ TEST_CASE("parse simple programs", "[simple]") auto tke = tk.cend(); std::vector<kal::ParseError<decltype(tkb)>> errs; - kal::parse(tkb, tke, std::back_inserter(nd), std::back_inserter(errs)); + kal::parse( + tkb, + tke, + [&](auto type, const auto& node) { + switch (type) { + case kal::ParsedEntityType::FuncDecl: { + kal::Prototype proto; + auto ok = cast(node, proto); + + assert(ok && "invalid cast: expects kal::Prototype"); + + parsed.decls.emplace_back(std::move(proto)); + } break; + + case kal::ParsedEntityType::FuncDef: { + kal::Function func; + auto ok = cast(node, func); + + assert(ok && "invalid cast: expects kal::Function"); + + parsed.defs.emplace_back(std::move(func)); + } break; + + case kal::ParsedEntityType::Stmt: + parsed.stmts.emplace_back(std::move(node)); + break; + } + }, + std::back_inserter(errs)); if (errs.empty()) - return nd; + return parsed; std::ostringstream oss; @@ -46,75 +82,87 @@ TEST_CASE("parse simple programs", "[simple]") } FAIL("PARSER ERROR\n" << oss.str()); - return nd; // dummy + return parsed; // dummy }; SECTION("extern") { { - auto nd = p("extern sin(x)"); + auto pd = p("extern sin(x)"); - REQUIRE(nd.size() == 1); - REQUIRE(to_string(nd[0]) == "(prototype sin x)"); + REQUIRE(pd.decls.size() == 1); + REQUIRE(pd.defs.empty()); + REQUIRE(pd.stmts.empty()); + REQUIRE(to_string(pd.decls[0]) == "(prototype sin x)"); } { - auto nd = p("extern tan2 ( arg0 arg1 )"); + auto pd = p("extern tan2 ( arg0 arg1 )"); - REQUIRE(nd.size() == 1); - REQUIRE(to_string(nd[0]) == "(prototype tan2 arg0 arg1)"); + REQUIRE(pd.decls.size() == 1); + REQUIRE(pd.defs.empty()); + REQUIRE(pd.stmts.empty()); + REQUIRE(to_string(pd.decls[0]) == "(prototype tan2 arg0 arg1)"); } { - auto nd = p("extern cos(realInput);extern atan2(arg0 arg1);"); + auto pd = p("extern cos(realInput);extern atan2(arg0 arg1);"); - REQUIRE(nd.size() == 2); - REQUIRE(to_string(nd[0]) == "(prototype cos realInput)"); - REQUIRE(to_string(nd[1]) == "(prototype atan2 arg0 arg1)"); + REQUIRE(pd.decls.size() == 2); + REQUIRE(pd.defs.empty()); + REQUIRE(pd.stmts.empty()); + REQUIRE(to_string(pd.decls[0]) == "(prototype cos realInput)"); + REQUIRE(to_string(pd.decls[1]) == "(prototype atan2 arg0 arg1)"); } } SECTION("def") { { - auto nd = p(R"( + auto pd = p(R"( def one(x) 1 )"); - REQUIRE(nd.size() == 1); - REQUIRE(to_string(nd[0]) == "(function (prototype one x) (number 1))"); + REQUIRE(pd.decls.empty()); + REQUIRE(pd.defs.size() == 1); + REQUIRE(to_string(pd.defs[0]) == + "(function (prototype one x) (number 1))"); } { - auto nd = p("extern sin(x) def pi2() 1.5708"); + auto pd = p("extern sin(x) def pi2() 1.5708"); - REQUIRE(nd.size() == 2); - REQUIRE(to_string(nd[0]) == "(prototype sin x)"); - REQUIRE(to_string(nd[1]) == "(function (prototype pi2) (number 1.5708))"); + REQUIRE(pd.decls.size() == 1); + REQUIRE(pd.defs.size() == 1); + REQUIRE(to_string(pd.decls[0]) == "(prototype sin x)"); + REQUIRE(to_string(pd.defs[0]) == + "(function (prototype pi2) (number 1.5708))"); } { - auto nd = p("extern sin(x) def sinPi2() sin(1.5708)"); + auto pd = p("extern sin(x) def sinPi2() sin(1.5708)"); - REQUIRE(nd.size() == 2); - REQUIRE(to_string(nd[0]) == "(prototype sin x)"); - REQUIRE(to_string(nd[1]) == + REQUIRE(pd.decls.size() == 1); + REQUIRE(pd.defs.size() == 1); + REQUIRE(to_string(pd.decls[0]) == "(prototype sin x)"); + REQUIRE(to_string(pd.defs[0]) == "(function (prototype sinPi2) (call sin (number 1.5708)))"); } { - auto nd = + auto pd = p("extern tan2(x y);extern sin(x);def fun()tan2(sin(1.5708), 1);" "def gun(x y z)tan2(0.1,gun(1,sin(1.5),1.0));"); - REQUIRE(nd.size() == 4); - REQUIRE(to_string(nd[0]) == "(prototype tan2 x y)"); - REQUIRE(to_string(nd[1]) == "(prototype sin x)"); - REQUIRE(to_string(nd[2]) == + REQUIRE(pd.decls.size() == 2); + REQUIRE(pd.defs.size() == 2); + REQUIRE(to_string(pd.decls[0]) == "(prototype tan2 x y)"); + REQUIRE(to_string(pd.decls[1]) == "(prototype sin x)"); + REQUIRE(to_string(pd.defs[0]) == "(function (prototype fun) (call tan2 (call sin (number 1.5708)) " "(number 1)))"); - REQUIRE(to_string(nd[3]) == + REQUIRE(to_string(pd.defs[1]) == "(function (prototype gun x y z) (call tan2 (number 0.1) (call " "gun (number 1) (call sin (number 1.5)) (number 1))))"); } @@ -127,51 +175,64 @@ def one(x) using Var = kal::Variable; { - auto nd = p("2 + 3"); + auto pd = p("2 + 3"); - REQUIRE(nd.size() == 1); - REQUIRE(nd[0] == BOp{ '+', Num{ 2 }, Num{ 3 } }); - REQUIRE(to_string(nd[0]) == "(binop '+' (number 2) (number 3))"); + REQUIRE(pd.decls.empty()); + REQUIRE(pd.defs.empty()); + REQUIRE(pd.stmts.size() == 1); + REQUIRE(pd.stmts[0] == BOp{ '+', Num{ 2 }, Num{ 3 } }); + REQUIRE(to_string(pd.stmts[0]) == "(binop '+' (number 2) (number 3))"); } { - auto nd = p("x * y"); + auto pd = p("x * y"); - REQUIRE(nd.size() == 1); - REQUIRE(nd[0] == BOp{ '*', Var{ "x" }, Var{ "y" } }); + REQUIRE(pd.decls.empty()); + REQUIRE(pd.defs.empty()); + REQUIRE(pd.stmts.size() == 1); + REQUIRE(pd.stmts[0] == BOp{ '*', Var{ "x" }, Var{ "y" } }); } { - auto nd = p("x * y * z"); + auto pd = p("x * y * z"); - REQUIRE(nd.size() == 1); - REQUIRE(nd[0] == + REQUIRE(pd.decls.empty()); + REQUIRE(pd.defs.empty()); + REQUIRE(pd.stmts.size() == 1); + REQUIRE(pd.stmts[0] == BOp{ '*', BOp{ '*', Var{ "x" }, Var{ "y" } }, Var{ "z" } }); } { - auto nd = p("4 * 5 + 3"); + auto pd = p("4 * 5 + 3"); - REQUIRE(nd.size() == 1); - REQUIRE(nd[0] == BOp{ '+', BOp{ '*', Num{ 4 }, Num{ 5 } }, Num{ 3 } }); + REQUIRE(pd.decls.empty()); + REQUIRE(pd.defs.empty()); + REQUIRE(pd.stmts.size() == 1); + REQUIRE(pd.stmts[0] == + BOp{ '+', BOp{ '*', Num{ 4 }, Num{ 5 } }, Num{ 3 } }); } { - auto nd = p("4+2.15*3"); - - REQUIRE(nd.size() == 1); - REQUIRE(nd[0] == BOp{ - '+', - Num{ 4 }, - BOp{ '*', Num{ 2.15 }, Num{ 3 } }, - }); + auto pd = p("4+2.15*3"); + + REQUIRE(pd.decls.empty()); + REQUIRE(pd.defs.empty()); + REQUIRE(pd.stmts.size() == 1); + REQUIRE(pd.stmts[0] == BOp{ + '+', + Num{ 4 }, + BOp{ '*', Num{ 2.15 }, Num{ 3 } }, + }); } { - auto nd = p("4+2.15*3/2-4.4/2/3#expr"); + auto pd = p("4+2.15*3/2-4.4/2/3#expr"); - REQUIRE(nd.size() == 1); - REQUIRE(nd[0] == + REQUIRE(pd.decls.empty()); + REQUIRE(pd.defs.empty()); + REQUIRE(pd.stmts.size() == 1); + REQUIRE(pd.stmts[0] == BOp{ '-', BOp{ '+', @@ -182,11 +243,13 @@ def one(x) } { - auto nd = p("sin(3.14/4)-2.3-3.4-(3*4+1)*3"); + auto pd = p("sin(3.14/4)-2.3-3.4-(3*4+1)*3"); - REQUIRE(nd.size() == 1); + REQUIRE(pd.decls.empty()); + REQUIRE(pd.defs.empty()); + REQUIRE(pd.stmts.size() == 1); REQUIRE( - to_string(nd[0]) == + to_string(pd.stmts[0]) == "(binop '-' (binop '-' (binop '-' (call sin (binop '/' (number 3.14) " "(number 4))) (number 2.3)) (number 3.4)) (binop '*' (binop '+' (binop " "'*' (number 3) (number 4)) (number 1)) (number 3)))"); @@ -196,7 +259,7 @@ def one(x) SECTION("extern, def, call") { { - auto nd = p(R"( + auto pd = p(R"( extern tan2(x y) def formula(x y z) @@ -207,15 +270,20 @@ extern sin(x) sin(2.72) + formula(1, 2, 3) )"); - REQUIRE(nd.size() == 4); - REQUIRE(to_string(nd[0]) == "(prototype tan2 x y)"); - REQUIRE(to_string(nd[1]) == + REQUIRE(pd.decls.size() == 2); + REQUIRE(pd.defs.size() == 1); + REQUIRE(pd.stmts.size() == 1); + + REQUIRE(to_string(pd.decls[0]) == "(prototype tan2 x y)"); + REQUIRE(to_string(pd.decls[1]) == "(prototype sin x)"); + + REQUIRE(to_string(pd.defs[0]) == "(function (prototype formula x y z) (binop '+' (number 3.14) " "(binop '/' (binop '*' (number 2) (binop '+' (call tan2 " "(variable x) (variable y)) (binop '*' (variable x) (variable " "y)))) (variable z))))"); - REQUIRE(to_string(nd[2]) == "(prototype sin x)"); - REQUIRE(to_string(nd[3]) == + + REQUIRE(to_string(pd.stmts[0]) == "(binop '+' (call sin (number 2.72)) (call formula (number 1) " "(number 2) (number 3)))"); }