From 1fea444b1ae1157a98ac187f58952080a7a63b6e Mon Sep 17 00:00:00 2001 From: Li Jin Date: Wed, 3 Dec 2025 11:05:52 +0800 Subject: Improved syntax error messages with contextual details. --- src/yuescript/yue_ast.cpp | 3 - src/yuescript/yue_ast.h | 9 +- src/yuescript/yue_compiler.cpp | 8 +- src/yuescript/yue_parser.cpp | 350 +++++++++++++++++++++++++---------------- src/yuescript/yue_parser.h | 28 +++- src/yuescript/yuescript.cpp | 2 +- 6 files changed, 247 insertions(+), 153 deletions(-) (limited to 'src') diff --git a/src/yuescript/yue_ast.cpp b/src/yuescript/yue_ast.cpp index b225acc..278d992 100644 --- a/src/yuescript/yue_ast.cpp +++ b/src/yuescript/yue_ast.cpp @@ -210,9 +210,6 @@ std::string MultilineCommentInner_t::to_string(void* ud) const { std::string Variable_t::to_string(void* ud) const { return name->to_string(ud); } -std::string LabelName_t::to_string(void* ud) const { - return name->to_string(ud); -} std::string LuaKeyword_t::to_string(void* ud) const { return name->to_string(ud); } diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h index 5f04fa0..3aca702 100644 --- a/src/yuescript/yue_ast.h +++ b/src/yuescript/yue_ast.h @@ -103,11 +103,6 @@ AST_NODE(Variable) AST_MEMBER(Variable, &name) AST_END(Variable) -AST_NODE(LabelName) - ast_ptr name; - AST_MEMBER(LabelName, &name) -AST_END(LabelName) - AST_NODE(LuaKeyword) ast_ptr name; AST_MEMBER(LuaKeyword, &name) @@ -246,12 +241,12 @@ AST_NODE(Import) AST_END(Import) AST_NODE(Label) - ast_ptr label; + ast_ptr label; AST_MEMBER(Label, &label) AST_END(Label) AST_NODE(Goto) - ast_ptr label; + ast_ptr label; AST_MEMBER(Goto, &label) AST_END(Goto) diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index 55ab4ec..84b990f 100644 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp @@ -78,7 +78,7 @@ static std::unordered_set Metamethods = { "close"s // Lua 5.4 }; -const std::string_view version = "0.29.9"sv; +const std::string_view version = "0.30.0"sv; const std::string_view extension = "yue"sv; class CompileError : public std::logic_error { @@ -9207,7 +9207,7 @@ private: } if (_config.lintGlobalVariable && !isLocal(name)) { auto key = name + ':' + std::to_string(pair->name->m_begin.m_line) + ':' + std::to_string(pair->name->m_begin.m_col); - if (_globals.find(key) != _globals.end()) { + if (_globals.find(key) == _globals.end()) { _globals[key] = {name, pair->name->m_begin.m_line, pair->name->m_begin.m_col, _funcLevel > 1 ? AccessType::Capture : AccessType::Read, isSolidDefined(name)}; } } @@ -12054,7 +12054,7 @@ private: if (getLuaTarget(label) < 502) { throw CompileError("label statement is not available when not targeting Lua version 5.2 or higher"sv, label); } - auto labelStr = unicodeVariableFrom(label->label->name); + auto labelStr = unicodeVariableFrom(label->label); int currentScope = _gotoScopes.top(); if (static_cast(_labels.size()) <= currentScope) { _labels.resize(currentScope + 1, std::nullopt); @@ -12075,7 +12075,7 @@ private: if (getLuaTarget(gotoNode) < 502) { throw CompileError("goto statement is not available when not targeting Lua version 5.2 or higher"sv, gotoNode); } - auto labelStr = unicodeVariableFrom(gotoNode->label->name); + auto labelStr = unicodeVariableFrom(gotoNode->label); gotos.push_back({gotoNode, labelStr, _gotoScopes.top(), static_cast(_scopes.size())}); out.push_back(indent() + "goto "s + labelStr + nll(gotoNode)); } diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp index cbba115..5a54690 100644 --- a/src/yuescript/yue_parser.cpp +++ b/src/yuescript/yue_parser.cpp @@ -41,6 +41,15 @@ public: int col; }; +#define RaiseError(msg, item) \ + do { \ + if (reinterpret_cast(item.user_data)->lax) { \ + return false; \ + } else { \ + throw ParserError(msg, item.begin); \ + } \ + } while (false) + // clang-format off YueParser::YueParser() { plain_space = *set(" \t"); @@ -61,14 +70,15 @@ YueParser::YueParser() { not_alpha_num = not_(alpha_num); Name = (range('a', 'z') | range('A', 'Z') | '_') >> *alpha_num >> not_(larger(255)); UnicodeName = (range('a', 'z') | range('A', 'Z') | '_' | larger(255)) >> *(larger(255) | alpha_num); - num_expo = set("eE") >> -set("+-") >> num_char; - num_expo_hex = set("pP") >> -set("+-") >> num_char; + must_num_char = num_char | invalid_number_literal_error; + num_expo = set("eE") >> -set("+-") >> must_num_char; + num_expo_hex = set("pP") >> -set("+-") >> must_num_char; lj_num = -set("uU") >> set("lL") >> set("lL"); num_char = range('0', '9') >> *(range('0', '9') | '_' >> and_(range('0', '9'))); num_char_hex = range('0', '9') | range('a', 'f') | range('A', 'F'); num_lit = num_char_hex >> *(num_char_hex | '_' >> and_(num_char_hex)); num_bin_lit = set("01") >> *(set("01") | '_' >> and_(set("01"))); - Num = + Num = ( '0' >> ( set("xX") >> ( num_lit >> ( @@ -78,50 +88,119 @@ YueParser::YueParser() { true_() ) | ( '.' >> num_lit >> -num_expo_hex - ) + ) | invalid_number_literal_error ) | - set("bB") >> num_bin_lit + set("bB") >> (num_bin_lit | invalid_number_literal_error) ) | num_char >> ( - '.' >> num_char >> -num_expo | + '.' >> must_num_char >> -num_expo | num_expo | lj_num | true_() - ) | - '.' >> num_char >> -num_expo; + ) + ) >> -(and_(alpha_num) >> invalid_number_literal_error) | + '.' >> num_char >> -num_expo >> -(and_(alpha_num) >> invalid_number_literal_error); cut = false_(); Seperator = true_(); - empty_block_error = pl::user(true_(), [](const item_t& item) { - throw ParserError("must be followed by a statement or an indented block"sv, item.begin); - return false; - }); - - export_expression_error = pl::user(true_(), [](const item_t& item) { - throw ParserError("invalid export expression"sv, item.begin); - return false; - }); - - invalid_interpolation_error = pl::user(true_(), [](const item_t& item) { - throw ParserError("invalid string interpolation"sv, item.begin); - return false; - }); - - confusing_unary_not_error = pl::user(true_(), [](const item_t& item) { - throw ParserError("deprecated use for unary operator 'not' to be here"sv, item.begin); - return false; - }); - - table_key_pair_error = pl::user(true_(), [](const item_t& item) { - throw ParserError("can not put hash pair in a list"sv, item.begin); - return false; - }); + auto expect_error = [](std::string_view msg) { + return pl::user(true_(), [msg](const item_t& item) { + RaiseError(msg, item); + return false; + }); + }; - assignment_expression_syntax_error = pl::user(true_(), [](const item_t& item) { - throw ParserError("use := for assignment expression"sv, item.begin); - return false; - }); + empty_block_error = expect_error( + "must be followed by a statement or an indented block"sv + ); + export_expression_error = expect_error( + "invalid export expression"sv + ); + invalid_interpolation_error = expect_error( + "invalid string interpolation"sv + ); + confusing_unary_not_error = expect_error( + "deprecated use for unary operator 'not' to be here"sv + ); + table_key_pair_error = expect_error( + "can not put hash pair in a list"sv + ); + assignment_expression_syntax_error = expect_error( + "use := for assignment expression"sv + ); + braces_expression_error = expect_error( + "syntax error in brace expression"sv + ); + brackets_expression_error = expect_error( + "unclosed bracket expression"sv + ); + slice_expression_error = expect_error( + "syntax error in slice expression"sv + ); + unclosed_single_string_error = expect_error( + "unclosed single-quoted string"sv + ); + unclosed_double_string_error = expect_error( + "unclosed double-quoted string"sv + ); + unclosed_lua_string_error = expect_error( + "unclosed Lua string"sv + ); + unexpected_comma_error = expect_error( + "got unexpected comma"sv + ); + parenthesis_error = expect_error( + "expected only one expression in parenthesis"sv + ); + dangling_clause_error = expect_error( + "dangling control clause"sv + ); + keyword_as_label_error = expect_error( + "keyword cannot be used as a label name"sv + ); + vararg_position_error = expect_error( + "vararg '...' must be the last parameter in function argument list"sv + ); + invalid_import_syntax_error = expect_error( + "invalid import syntax, expected `import \"X.mod\"`, `import \"X.mod\" as {:name}`, `from mod import name` or `import mod.name`"sv + ); + invalid_import_as_syntax_error = expect_error( + "invalid import syntax, expected `import \"X.mod\" as modname` or `import \"X.mod\" as {:name}`"sv + ); + expected_expression_error = expect_error( + "expected expression"sv + ); + invalid_from_import_error = expect_error( + "invalid import syntax, expected `from \"X.mod\" import name` or `from mod import name`"sv + ); + invalid_export_syntax_error = expect_error( + "invalid export syntax, expected `export item`, `export item = x`, `export.item = x` or `export default item`"sv + ); + invalid_macro_definition_error = expect_error( + "invalid macro definition, expected `macro Name = -> body` or `macro Name = $Name(...)`"sv + ); + invalid_global_declaration_error = expect_error( + "invalid global declaration, expected `global name`, `global name = ...`, `global *`, `global ^` or `global class ...`"sv + ); + invalid_local_declaration_error = expect_error( + "invalid local declaration, expected `local name`, `local name = ...`, `local *` or `local ^`"sv + ); + invalid_with_syntax_error = expect_error( + "invalid 'with' statement"sv + ); + invalid_try_syntax_error = expect_error( + "invalid 'try' expression, expected `try expr` or `try block` optionally followed by `catch err` with a handling block"sv + ); + keyword_as_identifier_syntax_error = expect_error( + "can not use keyword as identifier"sv + ); + invalid_number_literal_error = expect_error( + "invalid numeric literal"sv + ); + invalid_import_literal_error = expect_error( + "invalid import path literal, expected a dotted path like X.Y.Z"sv + ); #define ensure(patt, finally) ((patt) >> (finally) | (finally) >> cut) @@ -201,20 +280,6 @@ YueParser::YueParser() { return isValid; }); - LabelName = pl::user(UnicodeName, [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - for (auto it = item.begin->m_it; it != item.end->m_it; ++it) { - if (*it > 255) { - st->buffer.clear(); - return true; - } - st->buffer += static_cast(*it); - } - auto isValid = LuaKeywords.find(st->buffer) == LuaKeywords.end(); - st->buffer.clear(); - return isValid; - }); - LuaKeyword = pl::user(Name, [](const item_t& item) { State* st = reinterpret_cast(item.user_data); for (auto it = item.begin->m_it; it != item.end->m_it; ++it) st->buffer += static_cast(*it); @@ -247,14 +312,14 @@ YueParser::YueParser() { for (input_it i = item.begin->m_it; i != item.end->m_it; ++i) { switch (*i) { case '\t': indent += 4; break; - default: throw ParserError("can not mix the use of tabs and spaces as indents"sv, item.begin); break; + default: RaiseError("can not mix the use of tabs and spaces as indents"sv, item); break; } } } else { for (input_it i = item.begin->m_it; i != item.end->m_it; ++i) { switch (*i) { case ' ': indent++; break; - default: throw ParserError("can not mix the use of tabs and spaces as indents"sv, item.begin); break; + default: RaiseError("can not mix the use of tabs and spaces as indents"sv, item); break; } } } @@ -308,8 +373,8 @@ YueParser::YueParser() { in_block = space_break >> *(*set(" \t") >> line_break) >> advance_match >> ensure(Block, pop_indent); LocalFlag = expr('*') | '^'; - LocalValues = NameList >> -(space >> '=' >> space >> (TableBlock | ExpListLow)); - Local = key("local") >> space >> (LocalFlag | LocalValues); + LocalValues = NameList >> -(space >> '=' >> space >> (TableBlock | ExpListLow | expected_expression_error)); + Local = key("local") >> space >> (LocalFlag | LocalValues | invalid_local_declaration_error); ConstAttrib = key("const"); CloseAttrib = key("close"); @@ -322,17 +387,25 @@ YueParser::YueParser() { ColonImportName = '\\' >> space >> Variable; import_name = ColonImportName | Variable; import_name_list = Seperator >> *space_break >> space >> import_name >> *((+space_break | space >> ',' >> *space_break) >> space >> import_name); - ImportFrom = import_name_list >> *space_break >> space >> key("from") >> space >> (ImportLiteral | not_(String) >> Exp); + ImportFrom = import_name_list >> *space_break >> space >> key("from") >> space >> (ImportLiteral | not_(String) >> must_exp); from_import_name_list_line = import_name >> *(space >> ',' >> space >> import_name); from_import_name_in_block = +space_break >> advance_match >> ensure(space >> from_import_name_list_line >> *(-(space >> ',') >> +space_break >> check_indent_match >> space >> from_import_name_list_line), pop_indent); - FromImport = key("from") >> space >> (ImportLiteral | not_(String) >> Exp) >> *space_break >> space >> key("import") >> space >> Seperator >> (from_import_name_list_line >> -(space >> ',') >> -from_import_name_in_block | from_import_name_in_block); + FromImport = key("from") >> space >> ( + ImportLiteral | not_(String) >> Exp | invalid_from_import_error + ) >> *space_break >> space >> ( + key("import") | invalid_from_import_error + ) >> space >> Seperator >> ( + from_import_name_list_line >> -(space >> ',') >> -from_import_name_in_block | + from_import_name_in_block | + invalid_from_import_error + ); ImportLiteralInner = (range('a', 'z') | range('A', 'Z') | set("_-") | larger(255)) >> *(alpha_num | '-' | larger(255)); import_literal_chain = Seperator >> ImportLiteralInner >> *('.' >> ImportLiteralInner); ImportLiteral = ( - '\'' >> import_literal_chain >> '\'' + '\'' >> import_literal_chain >> -(not_('\'') >> invalid_import_literal_error) >> '\'' ) | ( - '"' >> import_literal_chain >> '"' + '"' >> import_literal_chain >> -(not_('"') >> invalid_import_literal_error) >> '"' ); MacroNamePair = MacroName >> ':' >> space >> MacroName; @@ -363,15 +436,15 @@ YueParser::YueParser() { Seperator >> import_tab_key_value >> *(space >> ',' >> space >> import_tab_key_value) ); - ImportAs = ImportLiteral >> -(space >> key("as") >> space >> (ImportTabLit | Variable | ImportAllMacro)); + ImportAs = ImportLiteral >> -(space >> key("as") >> space >> (ImportTabLit | Variable | ImportAllMacro | invalid_import_as_syntax_error)); ImportGlobal = Seperator >> UnicodeName >> *('.' >> UnicodeName) >> -(space >> key("as") >> space >> Variable); - Import = key("import") >> space >> (ImportAs | ImportFrom | ImportGlobal) | FromImport; + Import = key("import") >> space >> (ImportAs | ImportFrom | ImportGlobal | invalid_import_syntax_error) | FromImport; - Label = "::" >> LabelName >> "::"; + Label = "::" >> (and_(LuaKeyword >> "::") >> keyword_as_label_error | UnicodeName >> "::"); - Goto = key("goto") >> space >> LabelName; + Goto = key("goto") >> space >> (and_(LuaKeyword >> not_alpha_num) >> keyword_as_label_error | UnicodeName); ShortTabAppending = "[]" >> space >> Assign; @@ -381,9 +454,14 @@ YueParser::YueParser() { Return = key("return") >> -(space >> (TableBlock | ExpListLow)); - with_exp = ExpList >> -(space >> (':' >> Assign | and_('=') >> assignment_expression_syntax_error)); + must_exp = Exp | expected_expression_error; - With = key("with") >> -ExistentialOp >> space >> disable_do_chain_arg_table_block_rule(with_exp) >> space >> body_with("do"); + with_exp = ExpList >> -(space >> (':' >> Assign | and_('=') >> assignment_expression_syntax_error)) | expected_expression_error; + + With = key("with") >> -ExistentialOp >> space >> ( + disable_do_chain_arg_table_block_rule(with_exp) >> space >> body_with("do") | + invalid_with_syntax_error + ); SwitchCase = key("when") >> space >> disable_chain_rule(disable_arg_table_block_rule(SwitchList)) >> space >> body_with("then"); switch_else = key("else") >> space >> body; @@ -395,10 +473,11 @@ YueParser::YueParser() { SwitchList = Seperator >> ( and_(SimpleTable | TableLit) >> Exp | - exp_not_tab >> *(space >> ',' >> space >> exp_not_tab) + exp_not_tab >> *(space >> ',' >> space >> exp_not_tab) | + expected_expression_error ); Switch = key("switch") >> space >> - Exp >> -(space >> Assignment) >> + must_exp >> -(space >> Assignment) >> space >> Seperator >> ( SwitchCase >> space >> ( switch_block | @@ -408,7 +487,7 @@ YueParser::YueParser() { ); Assignment = -(',' >> space >> ExpList >> space) >> (':' >> Assign | and_('=') >> assignment_expression_syntax_error); - IfCond = disable_chain_rule(disable_arg_table_block_rule(Exp >> -(space >> Assignment))); + IfCond = disable_chain_rule(disable_arg_table_block_rule(Exp >> -(space >> Assignment))) | expected_expression_error; if_else_if = -(line_break >> *space_break >> check_indent_match) >> space >> key("elseif") >> space >> IfCond >> space >> body_with("then"); if_else = -(line_break >> *space_break >> check_indent_match) >> space >> key("else") >> space >> body; IfType = (expr("if") | "unless") >> not_alpha_num; @@ -418,22 +497,22 @@ YueParser::YueParser() { State* st = reinterpret_cast(item.user_data); return st->noUntilStack.empty() || !st->noUntilStack.back(); })) >> not_alpha_num; - While = key(WhileType) >> space >> disable_do_chain_arg_table_block_rule(Exp >> -(space >> Assignment)) >> space >> opt_body_with("do"); + While = key(WhileType) >> space >> (disable_do_chain_arg_table_block_rule(Exp >> -(space >> Assignment)) | expected_expression_error) >> space >> opt_body_with("do"); Repeat = key("repeat") >> space >> ( in_block >> line_break >> *space_break >> check_indent_match | disable_until_rule(Statement) - ) >> space >> key("until") >> space >> Exp; + ) >> space >> key("until") >> space >> must_exp; for_key = pl::user(key("for"), [](const item_t& item) { State* st = reinterpret_cast(item.user_data); return st->noForStack.empty() || !st->noForStack.back(); }); - ForStepValue = ',' >> space >> Exp; - for_args = Variable >> space >> '=' >> space >> Exp >> space >> ',' >> space >> Exp >> space >> -ForStepValue; + ForStepValue = ',' >> space >> must_exp; + for_args = Variable >> space >> '=' >> space >> must_exp >> space >> ',' >> space >> must_exp >> space >> -ForStepValue; For = for_key >> space >> disable_do_chain_arg_table_block_rule(for_args) >> space >> opt_body_with("do"); - for_in = StarExp | ExpList; + for_in = StarExp | ExpList | expected_expression_error; ForEach = for_key >> space >> AssignableNameList >> space >> key("in") >> space >> disable_do_chain_arg_table_block_rule(for_in) >> space >> opt_body_with("do"); @@ -519,8 +598,8 @@ YueParser::YueParser() { return true; }); - CatchBlock = line_break >> *space_break >> check_indent_match >> space >> key("catch") >> space >> Variable >> space >> in_block; - Try = key("try") >> -ExistentialOp >> space >> (in_block | Exp) >> -CatchBlock; + CatchBlock = line_break >> *space_break >> check_indent_match >> space >> key("catch") >> space >> (Variable >> space >> in_block | invalid_try_syntax_error); + Try = key("try") >> -ExistentialOp >> space >> (in_block | Exp | invalid_try_syntax_error) >> -CatchBlock; list_value = and_( @@ -556,14 +635,15 @@ YueParser::YueParser() { TblComprehension = and_('{') >> ('{' >> space >> disable_for_rule(Exp >> space >> -(CompValue >> space)) >> CompInner >> space >> '}' | braces_expression_error); CompInner = Seperator >> (CompForEach | CompFor) >> *(space >> comp_clause); - StarExp = '*' >> space >> Exp; - CompForEach = key("for") >> space >> AssignableNameList >> space >> key("in") >> space >> (StarExp | Exp); - CompFor = key("for") >> space >> Variable >> space >> '=' >> space >> Exp >> space >> ',' >> space >> Exp >> -ForStepValue; - comp_clause = CompFor | CompForEach | key("when") >> space >> Exp; + StarExp = '*' >> space >> must_exp; + CompForEach = key("for") >> space >> AssignableNameList >> space >> key("in") >> space >> (StarExp | must_exp); + CompFor = key("for") >> space >> Variable >> space >> '=' >> space >> must_exp >> space >> ',' >> space >> must_exp >> -ForStepValue; + comp_clause = CompFor | CompForEach | key("when") >> space >> must_exp; Assign = '=' >> space >> Seperator >> ( With | If | Switch | TableBlock | - (SpreadListExp | Exp) >> *(space >> set(",;") >> space >> (SpreadListExp | Exp)) + (SpreadListExp | Exp) >> *(space >> set(",;") >> space >> (SpreadListExp | Exp)) | + expected_expression_error ); UpdateOp = @@ -571,7 +651,7 @@ YueParser::YueParser() { ">>" | "<<" | "??" | set("+-*/%&|^"); - Update = UpdateOp >> '=' >> space >> Exp; + Update = UpdateOp >> '=' >> space >> must_exp; Assignable = AssignableChain | Variable | SelfItem; @@ -592,18 +672,21 @@ YueParser::YueParser() { UnaryExp = *(UnaryOperator >> space) >> expo_exp >> -(space >> In); pipe_operator = "|>"; - pipe_value = pipe_operator >> *space_break >> space >> UnaryExp; + pipe_value = pipe_operator >> *space_break >> space >> must_unary_exp; pipe_exp = UnaryExp >> *(space >> pipe_value); - BinaryOperator = + BinaryOperator = ( key("or") | key("and") | "<=" | ">=" | "~=" | "!=" | "==" | ".." | "<<" | ">>" | "//" | - set("+-*/%><|&~"); + set("+*/%>|&~") | + '-' >> not_('>') | + '<' >> not_('-') + ) >> not_('='); - ExpOpValue = BinaryOperator >> *space_break >> space >> pipe_exp; - Exp = Seperator >> pipe_exp >> *(space >> ExpOpValue) >> -(space >> "??" >> space >> Exp); + ExpOpValue = BinaryOperator >> *space_break >> space >> (pipe_exp | expected_expression_error); + Exp = Seperator >> pipe_exp >> *(space >> ExpOpValue) >> -(space >> "??" >> not_('=') >> *space_break >> space >> must_exp); disable_chain = pl::user(true_(), [](const item_t& item) { State* st = reinterpret_cast(item.user_data); @@ -635,7 +718,7 @@ YueParser::YueParser() { st->expLevel++; const int max_exp_level = 100; if (st->expLevel > max_exp_level) { - throw ParserError("nesting expressions exceeds 100 levels"sv, item.begin); + RaiseError("nesting expressions exceeds 100 levels"sv, item); } return true; }); @@ -650,13 +733,13 @@ YueParser::YueParser() { Value = inc_exp_level >> ensure(SimpleValue | SimpleTable | ChainValue | String, dec_exp_level); single_string_inner = '\\' >> set("'\\") | not_('\'') >> any_char; - SingleString = '\'' >> *single_string_inner >> '\''; + SingleString = '\'' >> *single_string_inner >> ('\'' | unclosed_single_string_error); interp = "#{" >> space >> (Exp >> space >> '}' | invalid_interpolation_error); double_string_plain = '\\' >> set("\"\\#") | not_('"') >> any_char; DoubleStringInner = +(not_("#{") >> double_string_plain); DoubleStringContent = DoubleStringInner | interp; - DoubleString = '"' >> Seperator >> *DoubleStringContent >> '"'; + DoubleString = '"' >> Seperator >> *DoubleStringContent >> ('"' | unclosed_double_string_error); YAMLIndent = +set(" \t"); YAMLLineInner = +('\\' >> set("\"\\#") | not_("#{" | stop) >> any_char); @@ -685,9 +768,9 @@ YueParser::YueParser() { LuaStringContent = *(not_(LuaStringClose) >> any_char); - LuaString = LuaStringOpen >> -line_break >> LuaStringContent >> LuaStringClose; + LuaString = LuaStringOpen >> -line_break >> LuaStringContent >> (LuaStringClose | unclosed_lua_string_error); - Parens = '(' >> *space_break >> space >> Exp >> *space_break >> space >> ')'; + Parens = '(' >> (*space_break >> space >> Exp >> *space_break >> space >> ')' | parenthesis_error); Callable = Variable | SelfItem | MacroName | Parens; fn_args_value_list = Exp >> *(space >> ',' >> space >> Exp); @@ -703,7 +786,7 @@ YueParser::YueParser() { fn_args = '(' >> -(space >> fn_args_value_list >> -(space >> ',')) >> -fn_args_lit_lines >> - white >> ')' | space >> '!' >> not_('='); + white >> -(and_(',') >> unexpected_comma_error) >>')' | space >> '!' >> not_('='); meta_index = Name | index | String; Metatable = '<' >> space >> '>'; @@ -735,7 +818,7 @@ YueParser::YueParser() { chain_with_colon = +chain_item >> -colon_chain; chain_items = chain_with_colon | colon_chain; - index = '[' >> not_('[') >> space >> (ReversedIndex >> and_(space >> ']') | Exp) >> space >> ']'; + index = '[' >> not_('[') >> space >> (ReversedIndex >> and_(space >> ']') | Exp) >> space >> (']' | brackets_expression_error); ReversedIndex = '#' >> space >> -('-' >> space >> Exp); chain_item = Invoke >> -ExistentialOp | @@ -808,13 +891,18 @@ YueParser::YueParser() { ClassDecl = key("class") >> not_(':') >> disable_arg_table_block_rule( -(space >> Assignable) >> - -(space >> key("extends") >> prevent_indent >> space >> ensure(Exp, pop_indent)) >> - -(space >> key("using") >> prevent_indent >> space >> ensure(ExpList, pop_indent)) + -(space >> key("extends") >> prevent_indent >> space >> ensure(must_exp, pop_indent)) >> + -(space >> key("using") >> prevent_indent >> space >> ensure(ExpList | expected_expression_error, pop_indent)) ) >> -ClassBlock; - GlobalValues = NameList >> -(space >> '=' >> space >> (TableBlock | ExpListLow)); + GlobalValues = NameList >> -(space >> '=' >> space >> (TableBlock | ExpListLow | expected_expression_error)); GlobalOp = expr('*') | '^'; - Global = key("global") >> space >> (-(ConstAttrib >> space) >> ClassDecl | GlobalOp | -(ConstAttrib >> space) >> GlobalValues); + Global = key("global") >> space >> ( + -(ConstAttrib >> space) >> ClassDecl | + GlobalOp | + -(ConstAttrib >> space) >> GlobalValues | + invalid_global_declaration_error + ); ExportDefault = key("default"); @@ -826,10 +914,10 @@ YueParser::YueParser() { pl::user(space >> ExportDefault >> space >> Exp, [](const item_t& item) { State* st = reinterpret_cast(item.user_data); if (st->exportDefault) { - throw ParserError("export default has already been declared"sv, item.begin); + RaiseError("export default has already been declared"sv, item); } if (st->exportCount > 1) { - throw ParserError("there are items already being exported"sv, item.begin); + RaiseError("there are items already being exported"sv, item); } st->exportDefault = true; return true; @@ -837,17 +925,17 @@ YueParser::YueParser() { not_(space >> ExportDefault) >> pl::user(true_(), [](const item_t& item) { State* st = reinterpret_cast(item.user_data); if (st->exportDefault && st->exportCount > 1) { - throw ParserError("can not export any more items when 'export default' is declared"sv, item.begin); + RaiseError("can not export any more items when 'export default' is declared"sv, item); } return true; }) >> ( and_(set(".[")) >> ((pl::user(and_('.' >> Metatable), [](const item_t& item) { State* st = reinterpret_cast(item.user_data); if (st->exportMetatable) { - throw ParserError("module metatable duplicated"sv, item.begin); + RaiseError("module metatable duplicated"sv, item); } if (st->exportMetamethod) { - throw ParserError("metatable should be exported before metamethod"sv, item.begin); + RaiseError("metatable should be exported before metamethod"sv, item); } st->exportMetatable = true; return true; @@ -862,10 +950,13 @@ YueParser::YueParser() { State* st = reinterpret_cast(item.user_data); st->exportMacro = true; return true; - }) + }) | + invalid_export_syntax_error ) >> not_(space >> StatementAppendix); - VariablePair = ':' >> Variable; + check_keyword_as_identifier = and_(LuaKeyword >> not_alpha_num) >> keyword_as_identifier_syntax_error; + + VariablePair = ':' >> (Variable | check_keyword_as_identifier); NormalPair = ( @@ -873,12 +964,12 @@ YueParser::YueParser() { '[' >> not_('[') >> space >> Exp >> space >> ']' | String ) >> ':' >> not_(':') >> space >> - (Exp | TableBlock | +space_break >> space >> Exp); + (Exp | TableBlock | +space_break >> space >> Exp | check_keyword_as_identifier); MetaVariablePair = ":<" >> space >> Variable >> space >> '>'; MetaNormalPair = '<' >> space >> -meta_index >> space >> ">:" >> space >> - (Exp | TableBlock | +space_break >> space >> Exp); + (Exp | TableBlock | +space_break >> space >> Exp | check_keyword_as_identifier); destruct_def = -(space >> '=' >> space >> Exp); VariablePairDef = VariablePair >> destruct_def; @@ -911,14 +1002,16 @@ YueParser::YueParser() { FnArgDef = (Variable | SelfItem >> -ExistentialOp) >> -(space >> '`' >> space >> Name) >> -(space >> '=' >> space >> Exp) | TableLit | SimpleTable; + check_vararg_position = and_(white >> ')') | white >> -(',' >> white) >> vararg_position_error; + FnArgDefList = Seperator >> ( - fn_arg_def_lit_lines >> -(-(space >> ',') >> white >> VarArg >> -(space >> '`' >> space >> Name)) | - white >> VarArg >> -(space >> '`' >> space >> Name) + fn_arg_def_lit_lines >> -(-(space >> ',') >> white >> VarArg >> -(space >> '`' >> space >> Name) >> check_vararg_position) | + white >> VarArg >> -(space >> '`' >> space >> Name) >> check_vararg_position ); OuterVarShadow = key("using") >> space >> (NameList | key("nil")); - FnArgsDef = '(' >> *space_break >> -FnArgDefList >> -(white >> OuterVarShadow) >> white >> ')'; + FnArgsDef = '(' >> *space_break >> -FnArgDefList >> -(white >> OuterVarShadow) >> white >> -(and_(',') >> unexpected_comma_error) >> ')'; FnArrow = expr("->") | "=>"; FunLit = pl::user(true_(), [](const item_t& item) { State* st = reinterpret_cast(item.user_data); @@ -930,10 +1023,13 @@ YueParser::YueParser() { ) >> space >> FnArrow >> -(space >> Body); MacroName = '$' >> UnicodeName; - macro_args_def = '(' >> white >> -FnArgDefList >> white >> ')'; + macro_args_def = '(' >> white >> -FnArgDefList >> white >> -(and_(',') >> unexpected_comma_error) >> ')'; MacroLit = -(macro_args_def >> space) >> "->" >> space >> Body; MacroFunc = MacroName >> (Invoke | InvokeArgs); - Macro = key("macro") >> space >> UnicodeName >> space >> '=' >> space >> (MacroLit | MacroFunc); + Macro = key("macro") >> space >> ( + UnicodeName >> space >> '=' >> space >> (MacroLit | MacroFunc) | + invalid_macro_definition_error + ); MacroInPlace = '$' >> space >> "->" >> space >> Body; NameList = Seperator >> Variable >> *(space >> ',' >> space >> Variable); @@ -944,9 +1040,11 @@ YueParser::YueParser() { Backcall = -(FnArgsDef >> space) >> FnArrowBack >> space >> ChainValue; SubBackcall = FnArrowBack >> space >> ChainValue; + must_unary_exp = UnaryExp | expected_expression_error; + PipeBody = Seperator >> - pipe_operator >> space >> UnaryExp >> - *(+space_break >> check_indent_match >> space >> pipe_operator >> space >> UnaryExp); + pipe_operator >> space >> must_unary_exp >> + *(+space_break >> check_indent_match >> space >> pipe_operator >> space >> must_unary_exp); ExpList = Seperator >> Exp >> *(space >> ',' >> space >> Exp); ExpListLow = Seperator >> Exp >> *(space >> set(",;") >> space >> Exp); @@ -965,35 +1063,14 @@ YueParser::YueParser() { space_break >> advance_match >> arg_block >> -(-(space >> ',') >> arg_table_block) ) | arg_table_block; - leading_spaces_error = pl::user(+space_one >> '(' >> space >> Exp >> +(space >> ',' >> space >> Exp) >> space >> ')', [](const item_t& item) { - throw ParserError("write invoke arguments in parentheses without leading spaces or just leading spaces without parentheses"sv, item.begin); - return false; - }); - InvokeArgs = not_(set("-~") | "[]") >> space >> Seperator >> ( Exp >> *(space >> ',' >> space >> Exp) >> -(space >> invoke_args_with_table) | - arg_table_block | - leading_spaces_error + arg_table_block ); ConstValue = (expr("nil") | "true" | "false") >> not_alpha_num; - braces_expression_error = pl::user(true_(), [](const item_t& item) { - throw ParserError("syntax error in brace expression"sv, item.begin); - return false; - }); - - brackets_expression_error = pl::user(true_(), [](const item_t& item) { - throw ParserError("syntax error in bracket expression"sv, item.begin); - return false; - }); - - slice_expression_error = pl::user(true_(), [](const item_t& item) { - throw ParserError("syntax error in slice expression"sv, item.begin); - return false; - }); - SimpleValue = TableLit | ConstValue | If | Switch | Try | With | ClassDecl | ForEach | For | While | Repeat | Do | @@ -1033,7 +1110,8 @@ YueParser::YueParser() { Return | Local | Global | Export | Macro | MacroInPlace | BreakLoop | Label | Goto | ShortTabAppending | LocalAttrib | Backcall | PipeBody | ExpListAssign | ChainAssign | - StatementAppendix >> empty_block_error + StatementAppendix >> empty_block_error | + and_(key("else") | key("elseif") | key("when")) >> dangling_clause_error ) >> space >> -StatementAppendix; @@ -1048,7 +1126,7 @@ YueParser::YueParser() { plain_space >> and_(line_break); indentation_error = pl::user(not_(pipe_operator | eof()), [](const item_t& item) { - throw ParserError("unexpected indent"sv, item.begin); + RaiseError("unexpected indent"sv, item); return false; }); diff --git a/src/yuescript/yue_parser.h b/src/yuescript/yue_parser.h index d5c37e2..3aca4ef 100644 --- a/src/yuescript/yue_parser.h +++ b/src/yuescript/yue_parser.h @@ -149,7 +149,6 @@ private: } NONE_AST_RULE(empty_block_error); - NONE_AST_RULE(leading_spaces_error); NONE_AST_RULE(indentation_error); NONE_AST_RULE(braces_expression_error); NONE_AST_RULE(brackets_expression_error); @@ -159,11 +158,35 @@ private: NONE_AST_RULE(confusing_unary_not_error); NONE_AST_RULE(table_key_pair_error); NONE_AST_RULE(assignment_expression_syntax_error); + NONE_AST_RULE(unclosed_single_string_error); + NONE_AST_RULE(unclosed_double_string_error); + NONE_AST_RULE(unclosed_lua_string_error); + NONE_AST_RULE(unexpected_comma_error); + NONE_AST_RULE(parenthesis_error); + NONE_AST_RULE(dangling_clause_error); + NONE_AST_RULE(keyword_as_label_error); + NONE_AST_RULE(check_vararg_position); + NONE_AST_RULE(vararg_position_error); + NONE_AST_RULE(invalid_import_syntax_error); + NONE_AST_RULE(invalid_import_as_syntax_error); + NONE_AST_RULE(expected_expression_error); + NONE_AST_RULE(invalid_from_import_error); + NONE_AST_RULE(invalid_export_syntax_error); + NONE_AST_RULE(invalid_macro_definition_error); + NONE_AST_RULE(invalid_global_declaration_error); + NONE_AST_RULE(invalid_local_declaration_error); + NONE_AST_RULE(invalid_with_syntax_error); + NONE_AST_RULE(invalid_try_syntax_error); + NONE_AST_RULE(keyword_as_identifier_syntax_error); + NONE_AST_RULE(check_keyword_as_identifier); + NONE_AST_RULE(invalid_number_literal_error); + NONE_AST_RULE(invalid_import_literal_error); NONE_AST_RULE(inc_exp_level); NONE_AST_RULE(dec_exp_level); NONE_AST_RULE(num_char); + NONE_AST_RULE(must_num_char); NONE_AST_RULE(num_char_hex); NONE_AST_RULE(num_lit); NONE_AST_RULE(num_bin_lit); @@ -282,6 +305,8 @@ private: NONE_AST_RULE(expo_value); NONE_AST_RULE(expo_exp); NONE_AST_RULE(exp_not_tab); + NONE_AST_RULE(must_exp); + NONE_AST_RULE(must_unary_exp); NONE_AST_RULE(local_const_item); NONE_AST_RULE(empty_line_break); NONE_AST_RULE(yue_comment); @@ -294,7 +319,6 @@ private: AST_RULE(Name); AST_RULE(UnicodeName); AST_RULE(Variable); - AST_RULE(LabelName); AST_RULE(LuaKeyword); AST_RULE(Self); AST_RULE(SelfName); diff --git a/src/yuescript/yuescript.cpp b/src/yuescript/yuescript.cpp index 2be74be..52236bf 100644 --- a/src/yuescript/yuescript.cpp +++ b/src/yuescript/yuescript.cpp @@ -255,7 +255,7 @@ static int yuecheck(lua_State* L) { lua_rawseti(L, -2, ++i); } } - if (result.error) { + if (!config.lax && result.error) { lua_pushboolean(L, 0); lua_insert(L, -2); return 2; -- cgit v1.2.3-55-g6feb