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:
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)))");
}