diff --git a/language/CMakeLists.txt b/language/CMakeLists.txt index 900252c..5ac8a13 100644 --- a/language/CMakeLists.txt +++ b/language/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.0) include_directories(..) -add_definitions("-std=c++1y") +add_definitions("-std=c++1y" -DPEGLIB_HAS_CONSTEXPR_SUPPORT) add_executable(culebra main.cc repl.cc interpreter.cc parser.cc) diff --git a/language/interpreter.cc b/language/interpreter.cc index a13a196..47a7abf 100644 --- a/language/interpreter.cc +++ b/language/interpreter.cc @@ -9,25 +9,26 @@ struct Eval { static Value eval(const Ast& ast, shared_ptr env) { switch (ast.tag) { - case Statements: return eval_statements(ast, env); - case While: return eval_while(ast, env); - case If: return eval_if(ast, env); - case Function: return eval_function(ast, env); - case Call: return eval_call(ast, env); - case Assignment: return eval_assignment(ast, env); - case LogicalOr: return eval_logical_or(ast, env); - case LogicalAnd: return eval_logical_and(ast, env); - case Condition: return eval_condition(ast, env); - case UnaryPlus: return eval_unary_plus(ast, env); - case UnaryMinus: return eval_unary_minus(ast, env); - case UnaryNot: return eval_unary_not(ast, env); - case BinExpresion: return eval_bin_expression(ast, env); - case Identifier: return eval_identifier(ast, env); - case Object: return eval_object(ast, env); - case Array: return eval_array(ast, env); - case Number: return eval_number(ast, env); - case Boolean: return eval_bool(ast, env); - case InterpolatedString: return eval_interpolated_string(ast, env); + case "STATEMENTS"_: return eval_statements(ast, env); + case "WHILE"_: return eval_while(ast, env); + case "IF"_: return eval_if(ast, env); + case "FUNCTION"_: return eval_function(ast, env); + case "CALL"_: return eval_call(ast, env); + case "ASSIGNMENT"_: return eval_assignment(ast, env); + case "LOGICAL_OR"_: return eval_logical_or(ast, env); + case "LOGICAL_AND"_: return eval_logical_and(ast, env); + case "CONDITION"_: return eval_condition(ast, env); + case "UNARY_PLUS"_: return eval_unary_plus(ast, env); + case "UNARY_MINUS"_: return eval_unary_minus(ast, env); + case "UNARY_NOT"_: return eval_unary_not(ast, env); + case "ADDITIVE"_: + case "MULTIPLICATIVE"_: return eval_bin_expression(ast, env); + case "IDENTIFIER"_: return eval_identifier(ast, env); + case "OBJECT"_: return eval_object(ast, env); + case "ARRAY"_: return eval_array(ast, env); + case "NUMBER"_: return eval_number(ast, env); + case "BOOLEAN"_: return eval_bool(ast, env); + case "INTERPOLATED_STRING"_: return eval_interpolated_string(ast, env); } if (ast.is_token) { @@ -107,7 +108,7 @@ private: for (auto i = 1u; i < ast.nodes.size(); i++) { const auto& n = *ast.nodes[i]; - if (n.original_tag == AstTag::Arguments) { + if (n.original_tag == "ARGUMENTS"_) { // Function call const auto& f = val.to_function(); const auto& params = f.data->params; @@ -132,14 +133,14 @@ private: string msg = "arguments error..."; throw runtime_error(msg); } - } else if (n.original_tag == AstTag::Index) { + } else if (n.original_tag == "INDEX"_) { // Array reference const auto& arr = val.to_array(); auto idx = eval(n, env).to_long(); if (0 <= idx && idx < static_cast(arr.values->size())) { val = arr.values->at(idx); } - } else if (n.original_tag == AstTag::Dot) { + } else if (n.original_tag == "DOT"_) { // Property auto name = n.token; auto prop = val.get_property(name); diff --git a/language/parser.cc b/language/parser.cc index 4a669d2..5fd9a56 100644 --- a/language/parser.cc +++ b/language/parser.cc @@ -3,7 +3,7 @@ using namespace peglib; using namespace std; -static auto g_grammar = R"( +const auto g_grammar = R"( PROGRAM <- _ STATEMENTS @@ -83,36 +83,7 @@ peg& get_parser() throw logic_error("invalid peg grammar"); } - parser.enable_ast( - true, // Optimize AST nodes - { - /* Definition Tag Optimize - ----------------------- ------------------ ---------- */ - { "STATEMENTS", Statements, true }, - { "WHILE", While, true }, - { "ASSIGNMENT", Assignment, true }, - { "IF", If, true }, - { "FUNCTION", Function, true }, - { "PARAMETERS", Default, false }, - { "CALL", Call, true }, - { "ARGUMENTS", Arguments, false }, - { "INDEX", Index, true }, - { "DOT", Dot, true }, - { "LOGICAL_OR", LogicalOr, true }, - { "LOGICAL_AND", LogicalAnd, true }, - { "CONDITION", Condition, true }, - { "ADDITIVE", BinExpresion, true }, - { "UNARY_PLUS", UnaryPlus, true }, - { "UNARY_MINUS", UnaryMinus, true }, - { "UNARY_NOT", UnaryNot, true }, - { "MULTIPLICATIVE", BinExpresion, true }, - { "OBJECT", Object, true }, - { "ARRAY", Array, true }, - { "NUMBER", Number, true }, - { "BOOLEAN", Boolean, true }, - { "IDENTIFIER", Identifier, true }, - { "INTERPOLATED_STRING", InterpolatedString, true }, - }); + parser.enable_ast(true, { "PARAMETERS", "ARGUMENTS" }); } return parser; diff --git a/language/parser.hpp b/language/parser.hpp index 1dfbb11..2a60e51 100644 --- a/language/parser.hpp +++ b/language/parser.hpp @@ -1,39 +1,5 @@ #include -enum AstTag -{ - Default = peglib::AstDefaultTag, - - Statements, - While, - If, - Call, - Assignment, - - Arguments, - Index, - Dot, - - LogicalOr, - LogicalAnd, - Condition, - UnaryPlus, - UnaryMinus, - UnaryNot, - BinExpresion, - - Identifier, - - Object, - Array, - Function, - - InterpolatedString, - - Number, - Boolean, -}; - peglib::peg& get_parser(); // vim: et ts=4 sw=4 cin cino={1s ff=unix diff --git a/language/vc14/culebra.vcxproj b/language/vc14/culebra.vcxproj index dd82cbd..c0e3d6a 100644 --- a/language/vc14/culebra.vcxproj +++ b/language/vc14/culebra.vcxproj @@ -64,7 +64,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + PEGLIB_HAS_CONSTEXPR_SUPPORT;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../.. @@ -81,7 +81,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + PEGLIB_HAS_CONSTEXPR_SUPPORT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../.. diff --git a/peglib.h b/peglib.h index 1e776d1..f2d9e79 100644 --- a/peglib.h +++ b/peglib.h @@ -1941,16 +1941,39 @@ private: const int AstDefaultTag = -1; +#ifdef PEGLIB_HAS_CONSTEXPR_SUPPORT +inline constexpr unsigned int str2tag(const char* str, int h = 0) { + return !str[h] ? 5381 : (str2tag(str, h + 1) * 33) ^ str[h]; +} + +inline constexpr unsigned int operator "" _(const char* s, size_t) { + return str2tag(s); +} +#endif + struct Ast { - Ast(size_t _line, size_t _column, const char* _name, int _tag, const std::vector>& _nodes) - : line(_line), column(_column), name(_name), tag(_tag), original_tag(_tag), is_token(false), nodes(_nodes) {} + Ast(size_t _line, size_t _column, const char* _name, const std::vector>& _nodes) + : line(_line), column(_column), name(_name), original_name(name), is_token(false), nodes(_nodes) +#ifdef PEGLIB_HAS_CONSTEXPR_SUPPORT + , tag(str2tag(_name)), original_tag(tag) +#endif + {} - Ast(size_t _line, size_t _column, const char* _name, int _tag, const std::string& _token) - : line(_line), column(_column), name(_name), tag(_tag), original_tag(_tag), is_token(true), token(_token) {} + Ast(size_t _line, size_t _column, const char* _name, const std::string& _token) + : line(_line), column(_column), name(_name), original_name(name), is_token(true), token(_token) +#ifdef PEGLIB_HAS_CONSTEXPR_SUPPORT + , tag(str2tag(_name)), original_tag(tag) +#endif + {} - Ast(const Ast& ast, int original_tag) - : line(ast.line), column(ast.column), name(ast.name), tag(ast.tag), original_tag(original_tag), is_token(ast.is_token), token(ast.token), nodes(ast.nodes) {} + Ast(const Ast& ast, const char* _original_name) + : line(ast.line), column(ast.column), name(ast.name), original_name(_original_name) + , is_token(ast.is_token), token(ast.token), nodes(ast.nodes) +#ifdef PEGLIB_HAS_CONSTEXPR_SUPPORT + , tag(ast.tag), original_tag(str2tag(_original_name)) +#endif + {} const Ast& get_smallest_ancestor() const; @@ -1959,11 +1982,14 @@ struct Ast const size_t line; const size_t column; const std::string name; - const int tag; - const int original_tag; + const std::string original_name; const bool is_token; const std::string token; const std::vector> nodes; +#ifdef PEGLIB_HAS_CONSTEXPR_SUPPORT + const unsigned int tag; + const unsigned int original_tag; +#endif }; struct AstPrint @@ -2153,22 +2179,30 @@ public: } } - struct AstNodeInfo { - const char* name; - int tag; // TODO: It should be calculated at compile-time from 'name' with constexpr hash function. - bool optimize_nodes; - }; + peg& enable_ast(bool optimize_nodes, const std::initializer_list& filters) { + for (auto& x: *grammar_) { + const auto& name = x.first; + auto& rule = x.second; - peg& enable_ast(bool optimize_nodes, std::initializer_list list) { - for (const auto& info: list) { - ast_node(info); - } - ast_end(optimize_nodes); - return *this; - } + auto found = std::find(filters.begin(), filters.end(), name) != filters.end(); + bool opt = optimize_nodes ? !found : found; - peg& enable_ast(bool optimize_nodes) { - ast_end(optimize_nodes); + if (!rule.action) { + auto is_token = rule.is_token; + rule.action = [=](const SemanticValues& sv) { + if (is_token) { + auto line = line_info(sv.ss, sv.s); + return std::make_shared(line.first, line.second, name.c_str(), std::string(sv.s, sv.n)); + } + if (opt && sv.size() == 1) { + auto ast = std::make_shared(*sv[0].get>(), name.c_str()); + return ast; + } + auto line = line_info(sv.ss, sv.s); + return std::make_shared(line.first, line.second, name.c_str(), sv.transform>()); + }; + } + } return *this; } @@ -2188,45 +2222,6 @@ private: } } - void ast_node(const AstNodeInfo& info) { - auto& rule = (*this)[info.name]; - auto is_token = rule.is_token; - rule = [info, is_token](const SemanticValues& sv) { - if (is_token) { - auto line = line_info(sv.ss, sv.s); - return std::make_shared(line.first, line.second, info.name, info.tag, std::string(sv.s, sv.n)); - } - if (info.optimize_nodes && sv.size() == 1) { - auto ast = std::make_shared(*sv[0].get>(), info.tag); - return ast; - } - auto line = line_info(sv.ss, sv.s); - return std::make_shared(line.first, line.second, info.name, info.tag, sv.transform>()); - }; - } - - void ast_end(bool optimize_nodes) { - for (auto& x: *grammar_) { - const auto& name = x.first; - auto& rule = x.second; - if (!rule.action) { - auto is_token = rule.is_token; - rule.action = [=](const SemanticValues& sv) { - if (is_token) { - auto line = line_info(sv.ss, sv.s); - return std::make_shared(line.first, line.second, name.c_str(), AstDefaultTag, std::string(sv.s, sv.n)); - } - if (optimize_nodes && sv.size() == 1) { - auto ast = std::make_shared(*sv[0].get>(), AstDefaultTag); - return ast; - } - auto line = line_info(sv.ss, sv.s); - return std::make_shared(line.first, line.second, name.c_str(), AstDefaultTag, sv.transform>()); - }; - } - } - } - std::shared_ptr grammar_; std::string start_; };