Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PoC] @prec attribute to overwrite precedences in a rule #254

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
22 changes: 22 additions & 0 deletions lib/lrama/grammar.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require "lrama/grammar/attribute"
require "lrama/grammar/auxiliary"
require "lrama/grammar/code"
require "lrama/grammar/counter"
Expand Down Expand Up @@ -155,6 +156,7 @@ def prepare
fill_nterm_type
fill_symbol_printer
fill_symbol_error_token
set_attributes_precedences
@symbols.sort_by!(&:number)
compute_nullable
compute_first_set
Expand Down Expand Up @@ -494,6 +496,26 @@ def replace_token_with_symbol
end
end

def set_attributes_precedences
@rules.each do |rule|
if rule.attributes && !rule.attributes.empty?
rule.attributes_precedences = rule.attributes.select do |attribute|
attribute.id.s_value == "@prec"
end.map do |attribute|
token = attribute.args[0]
prec_id = attribute.args[1]

sym = find_symbol_by_id!(token)
prec = terms.find do |term|
term.id == prec_id
end.precedence

[sym, prec]
end.to_h
end
end
end

def token_to_symbol(token)
case token
when Lrama::Lexer::Token
Expand Down
12 changes: 12 additions & 0 deletions lib/lrama/grammar/attribute.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module Lrama
class Grammar
class Attribute
attr_reader :id, :args

def initialize(id, args)
@id = id
@args = args
end
end
end
end
4 changes: 2 additions & 2 deletions lib/lrama/grammar/rule.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
module Lrama
class Grammar
# _rhs holds original RHS element. Use rhs to refer to Symbol.
class Rule < Struct.new(:id, :_lhs, :lhs, :_rhs, :rhs, :token_code, :position_in_original_rule_rhs, :nullable, :precedence_sym, :lineno, keyword_init: true)
attr_accessor :original_rule
class Rule < Struct.new(:id, :_lhs, :lhs, :_rhs, :rhs, :token_code, :attributes, :position_in_original_rule_rhs, :nullable, :precedence_sym, :lineno, keyword_init: true)
attr_accessor :original_rule, :attributes_precedences

def ==(other)
self.class == other.class &&
Expand Down
4 changes: 2 additions & 2 deletions lib/lrama/grammar/rule_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module Lrama
class Grammar
class RuleBuilder
attr_accessor :lhs, :line
attr_accessor :lhs, :line, :attributes
attr_reader :rhs, :user_code, :precedence_sym

def initialize(rule_counter, midrule_action_counter, position_in_original_rule_rhs = nil, skip_preprocess_references: false)
Expand Down Expand Up @@ -87,7 +87,7 @@ def build_rules
@midrule_action_rules = []
else
rule = Rule.new(
id: @rule_counter.increment, _lhs: lhs, _rhs: tokens, token_code: user_code,
id: @rule_counter.increment, _lhs: lhs, _rhs: tokens, token_code: user_code, attributes: attributes,
position_in_original_rule_rhs: @position_in_original_rule_rhs, precedence_sym: precedence_sym, lineno: line
)
@rules = [rule]
Expand Down
2 changes: 2 additions & 0 deletions lib/lrama/lexer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ def lex_token
return [:STRING, %Q(#{@scanner.matched})]
when @scanner.scan(/\d+/)
return [:INTEGER, Integer(@scanner.matched)]
when @scanner.scan(/(@[a-zA-Z_.][-a-zA-Z0-9_.]*)/)
return [:AT_IDENTIFIER, Lrama::Lexer::Token::Ident.new(s_value: @scanner.matched, location: location)]
when @scanner.scan(/([a-zA-Z_.][-a-zA-Z0-9_.]*)/)
token = Lrama::Lexer::Token::Ident.new(s_value: @scanner.matched, location: location)
type =
Expand Down
683 changes: 388 additions & 295 deletions lib/lrama/parser.rb

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions lib/lrama/states.rb
Original file line number Diff line number Diff line change
Expand Up @@ -431,8 +431,11 @@ def compute_shift_reduce_conflicts
next if !reduce.look_ahead.include?(sym)

# Shift/Reduce conflict
shift_prec = sym.precedence
reduce_prec = reduce.item.rule.precedence
precedences = reduce.item.rule.attributes_precedences || {}
reduce_sym = reduce.item.rule.precedence_sym

shift_prec = precedences[sym] || sym.precedence
reduce_prec = reduce_sym && (precedences[reduce_sym] || reduce_sym.precedence)

# Can resolve only when both have prec
unless shift_prec && reduce_prec
Expand Down
30 changes: 25 additions & 5 deletions parser.y
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class Lrama::Parser
expect 0

token C_DECLARATION CHARACTER IDENT_COLON IDENTIFIER INTEGER STRING TAG
token C_DECLARATION CHARACTER IDENT_COLON IDENTIFIER AT_IDENTIFIER INTEGER STRING TAG

rule

Expand Down Expand Up @@ -290,17 +290,19 @@ rule
end
}

rhs_list: rhs
rhs_list: attributes_opt rhs
{
builder = val[0]
builder = val[1]
builder.attributes = val[0]
if !builder.line
builder.line = @lexer.line - 1
end
result = [builder]
}
| rhs_list "|" rhs
| rhs_list "|" attributes_opt rhs
{
builder = val[2]
builder = val[3]
builder.attributes = val[2]
if !builder.line
builder.line = @lexer.line - 1
end
Expand Down Expand Up @@ -383,6 +385,24 @@ rule
named_ref_opt: # empty
| '[' IDENTIFIER ']' { result = val[1].s_value }

attributes_opt: /* empty */ { result = [] }
| attributes { result = val[0] }

attributes: attribute { result = [val[0]] }
| attributes attribute { result = val[0].append(val[1]) }

attribute: '[' AT_IDENTIFIER args_opt ']' { result = Grammar::Attribute.new(val[1], val[2]) }

args_opt: # empty
| args

args: arg { result = [val[0]] }
| args arg { result = val[0].append(val[1]) }

arg: INTEGER
| IDENTIFIER
| CHARACTER

id_colon: IDENT_COLON

epilogue_opt: # empty
Expand Down
2 changes: 1 addition & 1 deletion sig/lrama/grammar/rule.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Lrama

def initialize: (
?id: untyped, ?_lhs: untyped, ?lhs: untyped, ?_rhs: untyped, ?rhs: untyped,
?token_code: untyped, ?position_in_original_rule_rhs: untyped, ?nullable: untyped,
?token_code: untyped, ?attributes: untyped, ?position_in_original_rule_rhs: untyped, ?nullable: untyped,
?precedence_sym: untyped, ?lineno: untyped
) -> void
end
Expand Down
1 change: 1 addition & 0 deletions sig/lrama/grammar/rule_builder.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module Lrama
class RuleBuilder
attr_accessor lhs: Lexer::Token
attr_accessor line: Integer?
attr_accessor attributes: untyped?
attr_reader rhs: Array[Lexer::Token]
attr_reader user_code: Lexer::Token::UserCode?
attr_reader precedence_sym: Lexer::Token?
Expand Down
2 changes: 1 addition & 1 deletion spec/fixtures/common/unexpected_token.y
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
* This is comment for this file.
*/

@invalid
@@invalid
45 changes: 45 additions & 0 deletions spec/fixtures/integration/attributes.l
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
%option noinput nounput noyywrap never-interactive yylineno bison-bridge bison-locations

%{

#include <stdio.h>
#include <stdlib.h>
#include "attributes.h"

int yycolumn = 0;

#define YY_USER_ACTION \
yylloc->first_line = yylloc->last_line = yylineno; \
yylloc->first_column = yycolumn; \
yylloc->last_column = yycolumn + yyleng; \
yycolumn += yyleng; \

%}

NUMBER [0-9]+

%%

{NUMBER} {
yylval->i = atoi(yytext);
return NUM;
}

[+\-\*\/\(\)\{\}] {
return yytext[0];
}

[\n|\r\n] {}

[[:space:]] {}

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

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

%%
66 changes: 66 additions & 0 deletions spec/fixtures/integration/attributes.y
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
%{

#define YYDEBUG 1

#include <stdio.h>
#include "attributes.h"
#include "attributes-lexer.h"

static int yyerror(YYLTYPE *loc, const char *str);

%}

%union {
int i;
char *str;
}

%expect 0

%token <i> NUM

%type <i> expr expr_brace expr2

/*
* precedence table
*/

%left '+'
%left '*'

%%

program : expr { printf("=> %d", $1); }
| expr_brace { printf("=> %d", $1); }
;

expr : NUM
| expr '+' expr { $$ = $1 + $3; }
| expr '*' expr { $$ = $1 * $3; }
;

expr_brace : '{' expr2 '}' { $$ = $2; }
;

expr2 : NUM
| [@prec '+' '*'] [@prec '*' '+'] expr2 '+' expr2 { $$ = $1 + $3; }
| [@prec '+' '*'] [@prec '*' '+'] expr2 '*' expr2 { $$ = $1 * $3; }
;

%%

static int yyerror(YYLTYPE *loc, const char *str) {
fprintf(stderr, "parse error: %s\\n", str);
return 0;
}

int main(int argc, char *argv[]) {
yydebug = 1;

if (argc == 2) {
yy_scan_string(argv[1]);
}

yyparse();
return 0;
}
9 changes: 9 additions & 0 deletions spec/lrama/integration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,15 @@ def generate_object(grammar_file_path, c_path, obj_path, command_args: [])
end
end

describe "named references" do
it "changes precedences within {}" do
test_parser("attributes", "2 + 3 * 4", "=> 14")
test_parser("attributes", "2 * 3 + 4", "=> 10")
test_parser("attributes", "{ 2 + 3 * 4 }", "=> 20")
test_parser("attributes", "{ 2 * 3 + 4 }", "=> 14")
end
end

describe "sample files" do
let(:c_path) { Dir.tmpdir + "/test.c" }
let(:obj_path) { Dir.tmpdir + "/test" }
Expand Down
2 changes: 1 addition & 1 deletion spec/lrama/lexer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@
it do
text = File.read(fixture_path("common/unexpected_token.y"))
lexer = Lrama::Lexer.new(text)
expect { lexer.next_token }.to raise_error(ParseError, "Unexpected token: @invalid.")
expect { lexer.next_token }.to raise_error(ParseError, "Unexpected token: @@invalid.")
end
end

Expand Down