diff --git a/lib/propane/rule_set.rb b/lib/propane/rule_set.rb index d5911f0..ccdd5b0 100644 --- a/lib/propane/rule_set.rb +++ b/lib/propane/rule_set.rb @@ -119,6 +119,8 @@ class Propane def build_ast_fields(grammar) field_ast_node_indexes = {} field_indexes_across_all_rules = {} + # Stores the index into @ast_fields by field alias name. + field_aliases = {} @ast_fields = [] @rules.each do |rule| rule.components.each_with_index do |component, i| @@ -136,6 +138,16 @@ class Propane field_ast_node_indexes[field_name] = @ast_fields.size @ast_fields << {field_name => struct_name} end + rule.aliases.each do |alias_name, index| + if index == i + alias_ast_fields_index = field_ast_node_indexes[field_name] + if field_aliases[alias_name] && field_aliases[alias_name] != alias_ast_fields_index + raise Error.new("Error: conflicting AST node field positions for alias `#{alias_name}` in rule #{rule.name} defined on line #{rule.line_number}") + end + field_aliases[alias_name] = alias_ast_fields_index + @ast_fields[alias_ast_fields_index][alias_name] = @ast_fields[alias_ast_fields_index].first[1] + end + end field_indexes_across_all_rules[node_name] ||= Set.new field_indexes_across_all_rules[node_name] << field_ast_node_indexes[field_name] rule.rule_set_node_field_index_map[i] = field_ast_node_indexes[field_name] @@ -150,18 +162,6 @@ class Propane "#{grammar.ast_prefix}#{node_name}#{grammar.ast_suffix}" end end - # Now merge in the field aliases as given by the user in the - # grammar. - field_aliases = {} - @rules.each do |rule| - rule.aliases.each do |alias_name, index| - if field_aliases[alias_name] && field_aliases[alias_name] != index - raise Error.new("Error: conflicting AST node field positions for alias `#{alias_name}` in rule #{rule.name} defined on line #{rule.line_number}") - end - field_aliases[alias_name] = index - @ast_fields[index][alias_name] = @ast_fields[index].first[1] - end - end end end diff --git a/spec/propane_spec.rb b/spec/propane_spec.rb index 6964bac..cc26092 100644 --- a/spec/propane_spec.rb +++ b/spec/propane_spec.rb @@ -1219,6 +1219,29 @@ EOF expect(results.status).to eq 0 end + it "aliases the correct field when multiple rules are in a rule set in AST mode" do + write_grammar < a; +Start -> Foo; +Start -> T:first T:second T:third; +Foo -> b; +T -> a; +T -> b; +T -> c; +EOF + run_propane(language: language) + compile("spec/test_ast_field_aliases.#{language}", language: language) + results = run_test + expect(results.stderr).to eq "" + expect(results.status).to eq 0 + end + it "allows specifying field aliases when AST mode is not enabled" do if language == "d" write_grammar < id:first id:second << printf("first is %s\\n", ${first}); printf("second is %s\\n", ${second}); >> +EOF + end + run_propane(language: language) + compile("spec/test_field_aliases.#{language}", language: language) + results = run_test + expect(results.stderr).to eq "" + expect(results.status).to eq 0 + expect(results.stdout).to match /first is foo1.*second is bar2/m + end + + it "aliases the correct field when multiple rules are in a rule set when AST mode is not enabled" do + if language == "d" + write_grammar <> +ptype string; +token id /[a-zA-Z_][a-zA-Z0-9_]*/ << + $$ = match; +>> +drop /\\s+/; +Start -> id; +Start -> Foo; +Start -> id:first id:second << + writeln("first is ", ${first}); + writeln("second is ", ${second}); +>> +Foo -> ; +EOF + else + write_grammar < +#include +>> +ptype char const *; +token id /[a-zA-Z_][a-zA-Z0-9_]*/ << + char * s = malloc(match_length + 1); + strncpy(s, (char const *)match, match_length); + s[match_length] = 0; + $$ = s; +>> +drop /\\s+/; +Start -> id; +Start -> Foo; +Start -> id:first id:second << + printf("first is %s\\n", ${first}); + printf("second is %s\\n", ${second}); +>> +Foo -> ; EOF end run_propane(language: language)