From 94f1aa10e54f2df7f98a1ad8e4165de4182a13a1 Mon Sep 17 00:00:00 2001 From: ydah Date: Mon, 29 Dec 2025 03:00:03 +0900 Subject: [PATCH] Add LAC (Lookahead Correction) support ## Motivation LALR parsers with default reductions can report inaccurate syntax error messages. When a default reduction is performed, the parser may consume tokens before detecting an error, leading to confusing "expected token" lists that include tokens not actually acceptable at the error point. Additionally, `%nonassoc` operators can cause similar issues where the parser reports unexpected tokens in error messages. LAC (Lookahead Correction) solves these problems by performing exploratory parsing to verify token acceptability before committing to reductions. refs: https://www.gnu.org/software/bison/manual/html_node/LAC.html --- NEWS.md | 21 ++ README.md | 5 +- lib/lrama/grammar.rb | 23 ++ lib/lrama/output.rb | 4 + sig/generated/lrama/grammar.rbs | 9 + spec/fixtures/integration/lac.y | 46 ++++ .../integration/lac_accurate_tokens.l | 48 ++++ .../integration/lac_accurate_tokens.y | 65 +++++ .../integration/lac_default_reduction.l | 42 ++++ .../integration/lac_default_reduction.y | 48 ++++ spec/fixtures/integration/lac_nonassoc.l | 36 +++ spec/fixtures/integration/lac_nonassoc.y | 44 ++++ spec/lrama/grammar_spec.rb | 52 ++++ spec/lrama/integration_spec.rb | 48 ++++ template/bison/yacc.c | 232 +++++++++++++++++- 15 files changed, 721 insertions(+), 2 deletions(-) create mode 100644 spec/fixtures/integration/lac.y create mode 100644 spec/fixtures/integration/lac_accurate_tokens.l create mode 100644 spec/fixtures/integration/lac_accurate_tokens.y create mode 100644 spec/fixtures/integration/lac_default_reduction.l create mode 100644 spec/fixtures/integration/lac_default_reduction.y create mode 100644 spec/fixtures/integration/lac_nonassoc.l create mode 100644 spec/fixtures/integration/lac_nonassoc.y diff --git a/NEWS.md b/NEWS.md index 5a828805..b186e376 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,27 @@ ## Lrama 0.8.0 (2026-xx-xx) +### LAC (Lookahead Correction) Support + +Added support for LAC (Lookahead Correction), which improves syntax error reporting by providing more accurate error messages. LAC can be enabled using the `%define parse.lac full` directive in grammar files. + +Key features: +- More precise "expected token" lists in syntax error messages +- Early error detection (before default reductions) +- Better handling of `%nonassoc` operators +- Compatible with existing LALR parser generation + +Example usage: +```yacc +%define parse.lac full +%define parse.error verbose + +%% +expr: expr '+' expr + | NUMBER + ; +``` + ## Lrama 0.7.1 (2025-12-24) ### Optimize IELR diff --git a/README.md b/README.md index 0aff3d93..0e0723c0 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,10 @@ Lrama (pronounced in the same way as the noun “llama” in English) is LALR (1 * b4_locations_if is always true * b4_pure_if is always true * b4_pull_if is always false - * b4_lac_if is always false +* LAC (Lookahead Correction) + * Improves syntax error reporting with more accurate "expected token" messages + * Enable with `%define parse.lac full` + * Compatible with LALR parser generation * Error Tolerance parser * Subset of [Repairing Syntax Errors in LR Parsers (Corchuelo et al.)](https://idus.us.es/bitstream/handle/11441/65631/Repairing%20syntax%20errors.pdf) algorithm is supported * Parameterizing rules diff --git a/lib/lrama/grammar.rb b/lib/lrama/grammar.rb index 95a80bb0..b885200c 100644 --- a/lib/lrama/grammar.rb +++ b/lib/lrama/grammar.rb @@ -277,6 +277,7 @@ def validate! validate_no_precedence_for_nterm! validate_rule_lhs_is_nterm! validate_duplicated_precedence! + validate_parse_lac! end # @rbs (Grammar::Symbol sym) -> Array[Rule] @@ -304,6 +305,16 @@ def ielr_defined? @define.key?('lr.type') && @define['lr.type'] == 'ielr' end + # @rbs () -> String + def parse_lac + @define['parse.lac'] || 'none' + end + + # @rbs () -> bool + def lac_enabled? + parse_lac == 'full' + end + private # @rbs () -> void @@ -599,5 +610,17 @@ def validate_duplicated_precedence! def set_locations @locations = @locations || @rules.any? {|rule| rule.contains_at_reference? } end + + # @rbs () -> void + def validate_parse_lac! + return unless @define.key?('parse.lac') + + value = @define['parse.lac'] + valid_values = ['none', 'full'] + + unless valid_values.include?(value) + raise "Invalid value for parse.lac: '#{value}'. Expected 'none' or 'full'." + end + end end end diff --git a/lib/lrama/output.rb b/lib/lrama/output.rb index d527be8b..d41dedd4 100644 --- a/lib/lrama/output.rb +++ b/lib/lrama/output.rb @@ -372,6 +372,10 @@ def aux @grammar.aux end + def lac_enabled? + @grammar.lac_enabled? + end + def int_array_to_string(ary) last = ary.count - 1 diff --git a/sig/generated/lrama/grammar.rbs b/sig/generated/lrama/grammar.rbs index faab4f04..8984d7eb 100644 --- a/sig/generated/lrama/grammar.rbs +++ b/sig/generated/lrama/grammar.rbs @@ -227,6 +227,12 @@ module Lrama # @rbs () -> bool def ielr_defined?: () -> bool + # @rbs () -> String + def parse_lac: () -> String + + # @rbs () -> bool + def lac_enabled?: () -> bool + private # @rbs () -> void @@ -285,5 +291,8 @@ module Lrama # @rbs () -> void def set_locations: () -> void + + # @rbs () -> void + def validate_parse_lac!: () -> void end end diff --git a/spec/fixtures/integration/lac.y b/spec/fixtures/integration/lac.y new file mode 100644 index 00000000..cbc777bc --- /dev/null +++ b/spec/fixtures/integration/lac.y @@ -0,0 +1,46 @@ +%{ +#include +#include + +int yylex(YYSTYPE *yylval, YYLTYPE *yylloc); +void yyerror(YYLTYPE *yylloc, const char *s); +%} + +%define parse.lac full +%define parse.error verbose +%locations + +%token NUMBER + +%left '+' '-' +%left '*' '/' + +%% + +program: expr + ; + +expr: expr '+' expr + | expr '-' expr + | expr '*' expr + | expr '/' expr + | '(' expr ')' + | NUMBER + ; + +%% + +void yyerror(YYLTYPE *yylloc, const char *s) { + (void)yylloc; + fprintf(stderr, "Error: %s\n", s); +} + +int yylex(YYSTYPE *yylval, YYLTYPE *yylloc) { + (void)yylval; + (void)yylloc; + return 0; +} + +int main() { + return yyparse(); +} diff --git a/spec/fixtures/integration/lac_accurate_tokens.l b/spec/fixtures/integration/lac_accurate_tokens.l new file mode 100644 index 00000000..c61cef70 --- /dev/null +++ b/spec/fixtures/integration/lac_accurate_tokens.l @@ -0,0 +1,48 @@ +%option noinput nounput noyywrap never-interactive bison-bridge bison-locations + +%{ +#include +#include +#include "lac_accurate_tokens.h" +%} + +%% + +"if" { return IF; } +"then" { return THEN; } +"else" { return ELSE; } +"while" { return WHILE; } +"do" { return DO; } + +[a-zA-Z_][a-zA-Z0-9_]* { + (void)yylval; + (void)yylloc; + return ID; +} + +[0-9]+ { + (void)yylval; + (void)yylloc; + return NUM; +} + +[+\-*/] { + return yytext[0]; +} + +[\n|\r\n] { + return(YYEOF); +} + +[[:space:]] {} + +<> { + return(YYEOF); +} + +. { + fprintf(stderr, "Illegal character '%s'\n", yytext); + return(YYEOF); +} + +%% diff --git a/spec/fixtures/integration/lac_accurate_tokens.y b/spec/fixtures/integration/lac_accurate_tokens.y new file mode 100644 index 00000000..7ad42302 --- /dev/null +++ b/spec/fixtures/integration/lac_accurate_tokens.y @@ -0,0 +1,65 @@ +%{ +#include +#include "lac_accurate_tokens.h" +#include "lac_accurate_tokens-lexer.h" + +static int yyerror(YYLTYPE *loc, const char *str); +%} + +%define parse.lac full +%define parse.error verbose + +%token IF THEN ELSE WHILE DO ID NUM + +%left '+' '-' +%left '*' '/' + +%locations + +%% + +program: stmt_list + ; + +stmt_list: stmt + | stmt_list stmt + ; + +stmt: if_stmt + | while_stmt + | expr + ; + +if_stmt: IF expr THEN stmt + | IF expr THEN stmt ELSE stmt + ; + +while_stmt: WHILE expr DO stmt + ; + +expr: expr '+' expr + | expr '-' expr + | expr '*' expr + | expr '/' expr + | NUM + | ID + ; + +%% + +static int yyerror(YYLTYPE *loc, const char *str) { + (void)loc; + printf("Error: %s\n", str); + return 0; +} + +int main(int argc, char *argv[]) { + if (argc == 2) { + yy_scan_string(argv[1]); + } + + if (yyparse()) { + return 1; + } + return 0; +} diff --git a/spec/fixtures/integration/lac_default_reduction.l b/spec/fixtures/integration/lac_default_reduction.l new file mode 100644 index 00000000..08c26b59 --- /dev/null +++ b/spec/fixtures/integration/lac_default_reduction.l @@ -0,0 +1,42 @@ +%option noinput nounput noyywrap never-interactive bison-bridge bison-locations + +%{ +#include +#include +#include "lac_default_reduction.h" +%} + +%% + +[a-zA-Z_][a-zA-Z0-9_]* { + (void)yylval; + (void)yylloc; + return ID; +} + +[0-9]+ { + (void)yylval; + (void)yylloc; + return NUM; +} + +[=+*;] { + return yytext[0]; +} + +[\n|\r\n] { + return(YYEOF); +} + +[[:space:]] {} + +<> { + return(YYEOF); +} + +. { + fprintf(stderr, "Illegal character '%s'\n", yytext); + return(YYEOF); +} + +%% diff --git a/spec/fixtures/integration/lac_default_reduction.y b/spec/fixtures/integration/lac_default_reduction.y new file mode 100644 index 00000000..dc7d2a8a --- /dev/null +++ b/spec/fixtures/integration/lac_default_reduction.y @@ -0,0 +1,48 @@ +%{ +#include +#include "lac_default_reduction.h" +#include "lac_default_reduction-lexer.h" + +static int yyerror(YYLTYPE *loc, const char *str); +%} + +%define parse.lac full +%define parse.error verbose + +%token ID NUM + +%locations + +%% + +program: stmt + ; + +stmt: ID '=' expr ';' + | expr ';' + ; + +expr: expr '+' expr + | expr '*' expr + | NUM + | ID + ; + +%% + +static int yyerror(YYLTYPE *loc, const char *str) { + (void)loc; + printf("Error: %s\n", str); + return 0; +} + +int main(int argc, char *argv[]) { + if (argc == 2) { + yy_scan_string(argv[1]); + } + + if (yyparse()) { + return 1; + } + return 0; +} diff --git a/spec/fixtures/integration/lac_nonassoc.l b/spec/fixtures/integration/lac_nonassoc.l new file mode 100644 index 00000000..97caa5ed --- /dev/null +++ b/spec/fixtures/integration/lac_nonassoc.l @@ -0,0 +1,36 @@ +%option noinput nounput noyywrap never-interactive bison-bridge bison-locations + +%{ +#include +#include +#include "lac_nonassoc.h" +%} + +%% + +[0-9]+ { + (void)yylval; + (void)yylloc; + return NUMBER; +} + +"<" { + return '<'; +} + +[\n|\r\n] { + return(YYEOF); +} + +[[:space:]] {} + +<> { + return(YYEOF); +} + +. { + fprintf(stderr, "Illegal character '%s'\n", yytext); + return(YYEOF); +} + +%% diff --git a/spec/fixtures/integration/lac_nonassoc.y b/spec/fixtures/integration/lac_nonassoc.y new file mode 100644 index 00000000..699d5108 --- /dev/null +++ b/spec/fixtures/integration/lac_nonassoc.y @@ -0,0 +1,44 @@ +%{ +#include +#include "lac_nonassoc.h" +#include "lac_nonassoc-lexer.h" + +static int yyerror(YYLTYPE *loc, const char *str); +%} + +%define parse.lac full +%define parse.error verbose + +%token NUMBER + +%nonassoc '<' + +%locations + +%% + +program: expr + ; + +expr: expr '<' expr + | NUMBER + ; + +%% + +static int yyerror(YYLTYPE *loc, const char *str) { + (void)loc; + printf("Error: %s\n", str); + return 0; +} + +int main(int argc, char *argv[]) { + if (argc == 2) { + yy_scan_string(argv[1]); + } + + if (yyparse()) { + return 1; + } + return 0; +} diff --git a/spec/lrama/grammar_spec.rb b/spec/lrama/grammar_spec.rb index 3be8eab4..c5a6fc21 100644 --- a/spec/lrama/grammar_spec.rb +++ b/spec/lrama/grammar_spec.rb @@ -242,5 +242,57 @@ end end end + + context 'when parse.lac has an invalid value' do + it 'raises an error' do + grammar.define['parse.lac'] = 'invalid' + expect { grammar.validate! } + .to raise_error(RuntimeError, "Invalid value for parse.lac: 'invalid'. Expected 'none' or 'full'.") + end + end + end + + describe '#parse_lac' do + context 'when parse.lac is not set' do + it 'returns "none" as default' do + expect(grammar.parse_lac).to eq('none') + end + end + + context 'when parse.lac is set to "none"' do + it 'returns "none"' do + grammar.define['parse.lac'] = 'none' + expect(grammar.parse_lac).to eq('none') + end + end + + context 'when parse.lac is set to "full"' do + it 'returns "full"' do + grammar.define['parse.lac'] = 'full' + expect(grammar.parse_lac).to eq('full') + end + end + end + + describe '#lac_enabled?' do + context 'when parse.lac is not set (default)' do + it 'returns false' do + expect(grammar.lac_enabled?).to be false + end + end + + context 'when parse.lac is "none"' do + it 'returns false' do + grammar.define['parse.lac'] = 'none' + expect(grammar.lac_enabled?).to be false + end + end + + context 'when parse.lac is "full"' do + it 'returns true' do + grammar.define['parse.lac'] = 'full' + expect(grammar.lac_enabled?).to be true + end + end end end diff --git a/spec/lrama/integration_spec.rb b/spec/lrama/integration_spec.rb index 52922a0f..952cd0e0 100644 --- a/spec/lrama/integration_spec.rb +++ b/spec/lrama/integration_spec.rb @@ -269,6 +269,54 @@ def generate_object(grammar_file_path, c_path, obj_path, command_args: []) end end + describe "LAC (Lookahead Correction)" do + describe "%nonassoc operators" do + it "provides accurate error messages for chained comparisons" do + test_parser("lac_nonassoc", "1 < 2 < 3", "Error: syntax error, unexpected '<', expecting end of file\n", expect_success: false) + end + + it "accepts valid single comparison" do + test_parser("lac_nonassoc", "1 < 2", "", expect_success: true) + end + end + + describe "default reductions" do + it "detects errors early before default reductions" do + test_parser("lac_default_reduction", "x = 1 + *", "Error: syntax error, unexpected '*', expecting ID or NUM\n", expect_success: false) + end + + it "accepts valid assignment" do + test_parser("lac_default_reduction", "x = 1 + 2;", "", expect_success: true) + end + + it "accepts valid expression statement" do + test_parser("lac_default_reduction", "1 + 2 * 3;", "", expect_success: true) + end + end + + describe "accurate expected token lists" do + it "detects invalid tokens in control flow statements" do + test_parser("lac_accurate_tokens", "if x do y", "Error: syntax error, unexpected DO\n", expect_success: false) + end + + it "accepts valid if-then statement" do + test_parser("lac_accurate_tokens", "if x then y", "", expect_success: true) + end + + it "accepts valid if-then-else statement" do + test_parser("lac_accurate_tokens", "if x then y else z", "", expect_success: true) + end + + it "accepts valid while-do statement" do + test_parser("lac_accurate_tokens", "while x do y", "", expect_success: true) + end + + it "accepts nested control flow" do + test_parser("lac_accurate_tokens", "if x then while y do z", "", expect_success: true) + end + end + end + describe "sample files" do let(:c_path) { Dir.tmpdir + "/test#{file_extension}" } let(:obj_path) { Dir.tmpdir + "/test" } diff --git a/template/bison/yacc.c b/template/bison/yacc.c index 6edd59a0..30f42108 100644 --- a/template/bison/yacc.c +++ b/template/bison/yacc.c @@ -858,15 +858,185 @@ int yydebug; # define YYMAXDEPTH 10000 #endif +<%- if output.lac_enabled? -%> + +/* LAC (Lookahead Correction) */ + +/* YYLAC_ENABLED -- LAC is enabled. */ +#define YYLAC_ENABLED 1 + +/* Initial capacity of state stack for LAC exploratory stack. */ +#ifndef YYLAC_STACK_INITIAL_SIZE +# define YYLAC_STACK_INITIAL_SIZE 20 +#endif + +/* Maximum capacity of state stack for LAC exploratory stack. */ +#ifndef YYLAC_STACK_MAX_SIZE +# define YYLAC_STACK_MAX_SIZE YYMAXDEPTH +#endif + +<%- else -%> +#define YYLAC_ENABLED 0 +<%- end -%> + /* Context of a parse error. */ typedef struct { +<%- if output.lac_enabled? -%> + yy_state_t *yyss; +<%- end -%> yy_state_t *yyssp; yysymbol_kind_t yytoken; YYLTYPE *yylloc; } yypcontext_t; +<%- if output.lac_enabled? -%> + +/* Perform LAC (Lookahead Correction) to determine if the given token + is syntactically acceptable in the current parser state. + + This function simulates parsing with the given token without modifying + the actual parser state. It copies the parser stack to an exploratory + stack and performs reductions until a shift or error is determined. + + Arguments: + - yyesa: Static array for exploratory stack (initial storage) + - yyes: Pointer to exploratory stack pointer (may be reallocated) + - yyes_capacity: Pointer to capacity of exploratory stack + - yyss: Base of the parser state stack + - yyssp: Top of the parser state stack + - yytoken: Token to check for acceptability + + Returns: + - 0: Token is acceptable (can be shifted) + - 1: Token is not acceptable (syntax error) + - 2: Memory exhausted during stack reallocation +*/ +static int +yy_lac (yy_state_t *yyesa, yy_state_t **yyes, + YYPTRDIFF_T *yyes_capacity, yy_state_t *yyss, yy_state_t *yyssp, + yysymbol_kind_t yytoken) +{ + yy_state_t *yyesp; + + /* Reset exploratory stack to static array. */ + *yyes = yyesa; + + /* Copy the parser stack onto the exploratory stack. */ + { + YYPTRDIFF_T yysize = (YYPTRDIFF_T) (yyssp - yyss + 1); + if (*yyes_capacity < yysize) + { + YYPTRDIFF_T yyalloc = 2 * yysize; + if (YYLAC_STACK_MAX_SIZE < yyalloc) + yyalloc = YYLAC_STACK_MAX_SIZE; + if (*yyes_capacity < yyalloc) + { + *yyes_capacity = yyalloc; + { + yy_state_t *yyptr = *yyes; + *yyes = YY_CAST (yy_state_t *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, + *yyes_capacity + * sizeof **yyes))); + if (!*yyes) + { + *yyes = yyptr; + return 2; + } + } + } + } + { + yy_state_t *yyp1 = yyss; + yy_state_t *yyp2 = *yyes; + /* Copy from base to top including the initial state. */ + do + *yyp2++ = *yyp1++; + while (yyp1 <= yyssp); + yyesp = yyp2 - 1; + } + } + + /* Explore the parse tree using the exploratory stack. */ + for (;;) + { + int yyn = yypact[+*yyesp]; + if (yypact_value_is_default (yyn)) + goto yydefault_lac; + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault_lac; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + return 1; /* Syntax error. */ + yyn = -yyn; + goto yyreduce_lac; + } + /* Token is syntactically acceptable. */ + return 0; + + yydefault_lac: + yyn = yydefact[+*yyesp]; + if (yyn == 0) + return 1; /* Syntax error. */ + + yyreduce_lac: + { + YYPTRDIFF_T yylen = yyr2[yyn]; + yysymbol_kind_t yylhsNonterm = YY_CAST (yysymbol_kind_t, yyr1[yyn]); + yyesp -= yylen; + yyn = yypgoto[yylhsNonterm - YYNTOKENS] + *yyesp; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != *yyesp) + yyn = yydefgoto[yylhsNonterm - YYNTOKENS]; + else + yyn = yytable[yyn]; + ++yyesp; + if (yyesp == *yyes + *yyes_capacity) + { + YYPTRDIFF_T yysize = (YYPTRDIFF_T) (yyesp - *yyes); + if (YYLAC_STACK_MAX_SIZE <= yysize) + return 2; + { + YYPTRDIFF_T yyalloc = 2 * yysize; + if (YYLAC_STACK_MAX_SIZE < yyalloc) + yyalloc = YYLAC_STACK_MAX_SIZE; + { + yy_state_t *yyptr = *yyes; + yy_state_t *yynewptr = + YY_CAST (yy_state_t *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, + yyalloc * sizeof *yynewptr))); + if (!yynewptr) + { + YYSTACK_FREE (*yyes); + *yyes = yyptr; + return 2; + } + *yyes = yynewptr; + yyesp = *yyes + yysize - 1; + *yyes_capacity = yyalloc; + { + yy_state_t *yyp1 = yyptr; + yy_state_t *yyp2 = *yyes; + while (yyp1 != yyptr + yysize) + *yyp2++ = *yyp1++; + } + if (yyptr != yyesa) + YYSTACK_FREE (yyptr); + } + } + } + *yyesp = YY_CAST (yy_state_t, yyn); + } + } +} + +<%- end -%> + /* Put in YYARG at most YYARGN of the expected tokens given the current YYCTX, and return the number of tokens stored in YYARG. If YYARG is null, return the number of expected tokens (guaranteed to @@ -879,6 +1049,43 @@ yypcontext_expected_tokens (const yypcontext_t *yyctx, { /* Actual size of YYARG. */ int yycount = 0; +<%- if output.lac_enabled? -%> + /* Use LAC to compute the set of expected tokens accurately. */ + yy_state_t yyesa_local[YYLAC_STACK_INITIAL_SIZE]; + yy_state_t *yyes_local = yyesa_local; + YYPTRDIFF_T yyes_capacity_local = YYLAC_STACK_INITIAL_SIZE; + int yyx; + for (yyx = 0; yyx < YYNTOKENS; ++yyx) + { + yysymbol_kind_t yysym = YY_CAST (yysymbol_kind_t, yyx); + if (yysym != YYSYMBOL_YYerror && yysym != YYSYMBOL_YYUNDEF) + { + int yylac_status = yy_lac (yyesa_local, &yyes_local, + &yyes_capacity_local, yyctx->yyss, yyctx->yyssp, yysym); + if (yylac_status == 2) + { + if (yyes_local != yyesa_local) + YYSTACK_FREE (yyes_local); + return YYENOMEM; + } + if (yylac_status == 0) + { + if (!yyarg) + ++yycount; + else if (yycount == yyargn) + { + if (yyes_local != yyesa_local) + YYSTACK_FREE (yyes_local); + return 0; + } + else + yyarg[yycount++] = yysym; + } + } + } + if (yyes_local != yyesa_local) + YYSTACK_FREE (yyes_local); +<%- else -%> int yyn = yypact[+*yyctx->yyssp]; if (!yypact_value_is_default (yyn)) { @@ -902,6 +1109,7 @@ yypcontext_expected_tokens (const yypcontext_t *yyctx, yyarg[yycount++] = YY_CAST (yysymbol_kind_t, yyx); } } +<%- end -%> if (yyarg && yycount == 0 && 0 < yyargn) yyarg[0] = YYSYMBOL_YYEMPTY; return yycount; @@ -1521,6 +1729,13 @@ YYLTYPE yylloc = yyloc_default; YYLTYPE *yyls = yylsa; YYLTYPE *yylsp = yyls; +<%- if output.lac_enabled? -%> + /* LAC: exploratory state stack. */ + yy_state_t yyesa[YYLAC_STACK_INITIAL_SIZE]; + yy_state_t *yyes = yyesa; + YYPTRDIFF_T yyes_capacity = YYLAC_STACK_INITIAL_SIZE; +<%- end -%> + int yyn; /* The return value of yyparse. */ int yyresult; @@ -1736,6 +1951,17 @@ YYLTYPE yylloc = yyloc_default; YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc<%= output.user_args %>); } +<%- if output.lac_enabled? -%> + /* LAC: Check if this token is syntactically acceptable. */ + { + int yylac_status = yy_lac (yyesa, &yyes, &yyes_capacity, yyss, yyssp, yytoken); + if (yylac_status == 2) + goto yyexhaustedlab; + if (yylac_status == 1) + goto yyerrlab; + } +<%- end -%> + /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; @@ -1854,7 +2080,7 @@ YYLTYPE yylloc = yyloc_default; ++yynerrs; { yypcontext_t yyctx - = {yyssp, yytoken, &yylloc}; + = {<%- if output.lac_enabled? -%>yyss, <%- end -%>yyssp, yytoken, &yylloc}; char const *yymsgp = YY_("syntax error"); int yysyntax_error_status; yysyntax_error_status = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx<%= output.user_args %>); @@ -2055,6 +2281,10 @@ YYLTYPE yylloc = yyloc_default; if (yyss != yyssa) YYSTACK_FREE (yyss); #endif +<%- if output.lac_enabled? -%> + if (yyes != yyesa) + YYSTACK_FREE (yyes); +<%- end -%> if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); return yyresult;