Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 23 additions & 0 deletions lib/lrama/grammar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
4 changes: 4 additions & 0 deletions lib/lrama/output.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
9 changes: 9 additions & 0 deletions sig/generated/lrama/grammar.rbs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 46 additions & 0 deletions spec/fixtures/integration/lac.y
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
%{
#include <stdio.h>
#include <stdlib.h>

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();
}
48 changes: 48 additions & 0 deletions spec/fixtures/integration/lac_accurate_tokens.l
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
%option noinput nounput noyywrap never-interactive bison-bridge bison-locations

%{
#include <stdio.h>
#include <stdlib.h>
#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:]] {}

<<EOF>> {
return(YYEOF);
}

. {
fprintf(stderr, "Illegal character '%s'\n", yytext);
return(YYEOF);
}

%%
65 changes: 65 additions & 0 deletions spec/fixtures/integration/lac_accurate_tokens.y
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
%{
#include <stdio.h>
#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;
}
42 changes: 42 additions & 0 deletions spec/fixtures/integration/lac_default_reduction.l
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
%option noinput nounput noyywrap never-interactive bison-bridge bison-locations

%{
#include <stdio.h>
#include <stdlib.h>
#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:]] {}

<<EOF>> {
return(YYEOF);
}

. {
fprintf(stderr, "Illegal character '%s'\n", yytext);
return(YYEOF);
}

%%
48 changes: 48 additions & 0 deletions spec/fixtures/integration/lac_default_reduction.y
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
%{
#include <stdio.h>
#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;
}
Loading