From ad884e4f87f338604f4601967d7f9cb6a3edfabd Mon Sep 17 00:00:00 2001 From: yui-knk Date: Wed, 20 Mar 2024 13:04:09 +0900 Subject: [PATCH] Support `%destructor` declaration --- Steepfile | 2 + lib/lrama/grammar.rb | 9 +- lib/lrama/grammar/code.rb | 1 + lib/lrama/grammar/code/destructor_code.rb | 40 +++++ lib/lrama/grammar/destructor.rb | 9 + lib/lrama/grammar/symbol.rb | 6 +- lib/lrama/grammar/symbols/resolver.rb | 17 ++ lib/lrama/lexer.rb | 1 + lib/lrama/output.rb | 19 +++ lib/lrama/parser.rb | 174 +++++++++++--------- parser.y | 7 + sig/lrama/grammar/code/destructor_code.rbs | 15 ++ sig/lrama/grammar/destructor.rbs | 11 ++ sig/lrama/grammar/symbol.rbs | 1 + spec/fixtures/integration/destructors.l | 47 ++++++ spec/fixtures/integration/destructors.y | 74 +++++++++ spec/lrama/grammar/code_spec.rb | 36 ++++ spec/lrama/grammar/symbols/resolver_spec.rb | 11 ++ spec/lrama/integration_spec.rb | 25 ++- template/bison/yacc.c | 7 +- 20 files changed, 424 insertions(+), 88 deletions(-) create mode 100644 lib/lrama/grammar/code/destructor_code.rb create mode 100644 lib/lrama/grammar/destructor.rb create mode 100644 sig/lrama/grammar/code/destructor_code.rbs create mode 100644 sig/lrama/grammar/destructor.rbs create mode 100644 spec/fixtures/integration/destructors.l create mode 100644 spec/fixtures/integration/destructors.y diff --git a/Steepfile b/Steepfile index fb24b13b..a44c3e4e 100644 --- a/Steepfile +++ b/Steepfile @@ -5,6 +5,7 @@ target :lib do signature "sig" check "lib/lrama/grammar/binding.rb" + check "lib/lrama/grammar/code/destructor_code.rb" check "lib/lrama/grammar/code/printer_code.rb" check "lib/lrama/grammar/code.rb" check "lib/lrama/grammar/counter.rb" @@ -14,6 +15,7 @@ target :lib do check "lib/lrama/grammar/symbols" check "lib/lrama/grammar/percent_code.rb" check "lib/lrama/grammar/precedence.rb" + check "lib/lrama/grammar/destructor.rb" check "lib/lrama/grammar/printer.rb" check "lib/lrama/grammar/reference.rb" check "lib/lrama/grammar/rule_builder.rb" diff --git a/lib/lrama/grammar.rb b/lib/lrama/grammar.rb index 7ccde1aa..a816b826 100644 --- a/lib/lrama/grammar.rb +++ b/lib/lrama/grammar.rb @@ -3,6 +3,7 @@ require "lrama/grammar/binding" require "lrama/grammar/code" require "lrama/grammar/counter" +require "lrama/grammar/destructor" require "lrama/grammar/error_token" require "lrama/grammar/parameterizing_rule" require "lrama/grammar/percent_code" @@ -34,7 +35,7 @@ class Grammar def_delegators "@symbols_resolver", :symbols, :nterms, :terms, :add_nterm, :add_term, :find_symbol_by_number!, :find_symbol_by_id!, :token_to_symbol, :find_symbol_by_s_value!, :fill_symbol_number, :fill_nterm_type, - :fill_printer, :fill_error_token, :sort_by_number! + :fill_printer, :fill_destructor, :fill_error_token, :sort_by_number! def initialize(rule_counter) @@ -43,6 +44,7 @@ def initialize(rule_counter) # Code defined by "%code" @percent_codes = [] @printers = [] + @destructors = [] @error_tokens = [] @symbols_resolver = Grammar::Symbols::Resolver.new @types = [] @@ -65,6 +67,10 @@ def add_percent_code(id:, code:) @percent_codes << PercentCode.new(id.s_value, code.s_value) end + def add_destructor(ident_or_tags:, token_code:, lineno:) + @destructors << Destructor.new(ident_or_tags: ident_or_tags, token_code: token_code, lineno: lineno) + end + def add_printer(ident_or_tags:, token_code:, lineno:) @printers << Printer.new(ident_or_tags: ident_or_tags, token_code: token_code, lineno: lineno) end @@ -345,6 +351,7 @@ def fill_symbols fill_symbol_number fill_nterm_type(@types) fill_printer(@printers) + fill_destructor(@destructors) fill_error_token(@error_tokens) sort_by_number! end diff --git a/lib/lrama/grammar/code.rb b/lib/lrama/grammar/code.rb index e108d91c..3bad599d 100644 --- a/lib/lrama/grammar/code.rb +++ b/lib/lrama/grammar/code.rb @@ -1,4 +1,5 @@ require "forwardable" +require "lrama/grammar/code/destructor_code" require "lrama/grammar/code/initial_action_code" require "lrama/grammar/code/no_reference_code" require "lrama/grammar/code/printer_code" diff --git a/lib/lrama/grammar/code/destructor_code.rb b/lib/lrama/grammar/code/destructor_code.rb new file mode 100644 index 00000000..70360eb9 --- /dev/null +++ b/lib/lrama/grammar/code/destructor_code.rb @@ -0,0 +1,40 @@ +module Lrama + class Grammar + class Code + class DestructorCode < Code + def initialize(type:, token_code:, tag:) + super(type: type, token_code: token_code) + @tag = tag + end + + private + + # * ($$) *yyvaluep + # * (@$) *yylocationp + # * ($:$) error + # * ($1) error + # * (@1) error + # * ($:1) error + def reference_to_c(ref) + case + when ref.type == :dollar && ref.name == "$" # $$ + member = @tag.member + "((*yyvaluep).#{member})" + when ref.type == :at && ref.name == "$" # @$ + "(*yylocationp)" + when ref.type == :index && ref.name == "$" # $:$ + raise "$:#{ref.value} can not be used in #{type}." + when ref.type == :dollar # $n + raise "$#{ref.value} can not be used in #{type}." + when ref.type == :at # @n + raise "@#{ref.value} can not be used in #{type}." + when ref.type == :index # $:n + raise "$:#{ref.value} can not be used in #{type}." + else + raise "Unexpected. #{self}, #{ref}" + end + end + end + end + end +end diff --git a/lib/lrama/grammar/destructor.rb b/lib/lrama/grammar/destructor.rb new file mode 100644 index 00000000..4b7059e9 --- /dev/null +++ b/lib/lrama/grammar/destructor.rb @@ -0,0 +1,9 @@ +module Lrama + class Grammar + class Destructor < Struct.new(:ident_or_tags, :token_code, :lineno, keyword_init: true) + def translated_code(tag) + Code::DestructorCode.new(type: :destructor, token_code: token_code, tag: tag).translated_code + end + end + end +end diff --git a/lib/lrama/grammar/symbol.rb b/lib/lrama/grammar/symbol.rb index 21765226..deb67ad9 100644 --- a/lib/lrama/grammar/symbol.rb +++ b/lib/lrama/grammar/symbol.rb @@ -7,11 +7,12 @@ module Lrama class Grammar class Symbol - attr_accessor :id, :alias_name, :tag, :number, :token_id, :nullable, :precedence, :printer, :error_token, :first_set, :first_set_bitmap + attr_accessor :id, :alias_name, :tag, :number, :token_id, :nullable, :precedence, + :printer, :destructor, :error_token, :first_set, :first_set_bitmap attr_reader :term attr_writer :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol - def initialize(id:, term:, alias_name: nil, number: nil, tag: nil, token_id: nil, nullable: nil, precedence: nil, printer: nil) + def initialize(id:, term:, alias_name: nil, number: nil, tag: nil, token_id: nil, nullable: nil, precedence: nil, printer: nil, destructor: nil) @id = id @alias_name = alias_name @number = number @@ -21,6 +22,7 @@ def initialize(id:, term:, alias_name: nil, number: nil, tag: nil, token_id: nil @nullable = nullable @precedence = precedence @printer = printer + @destructor = destructor end def term? diff --git a/lib/lrama/grammar/symbols/resolver.rb b/lib/lrama/grammar/symbols/resolver.rb index 4e9e12ea..1788ed63 100644 --- a/lib/lrama/grammar/symbols/resolver.rb +++ b/lib/lrama/grammar/symbols/resolver.rb @@ -118,6 +118,23 @@ def fill_printer(printers) end end + def fill_destructor(destructors) + symbols.each do |sym| + destructors.each do |destructor| + destructor.ident_or_tags.each do |ident_or_tag| + case ident_or_tag + when Lrama::Lexer::Token::Ident + sym.destructor = destructor if sym.id == ident_or_tag + when Lrama::Lexer::Token::Tag + sym.destructor = destructor if sym.tag == ident_or_tag + else + raise "Unknown token type. #{destructor}" + end + end + end + end + end + def fill_error_token(error_tokens) symbols.each do |sym| error_tokens.each do |token| diff --git a/lib/lrama/lexer.rb b/lib/lrama/lexer.rb index 33f37eb6..db8f384f 100644 --- a/lib/lrama/lexer.rb +++ b/lib/lrama/lexer.rb @@ -21,6 +21,7 @@ class Lexer %define %require %printer + %destructor %lex-param %parse-param %initial-action diff --git a/lib/lrama/output.rb b/lib/lrama/output.rb index 29bf1e69..642c8b47 100644 --- a/lib/lrama/output.rb +++ b/lib/lrama/output.rb @@ -150,6 +150,25 @@ def symbol_actions_for_printer str end + def symbol_actions_for_destructor + str = "" + + @grammar.symbols.each do |sym| + next unless sym.destructor + + str << <<-STR + case #{sym.enum_name}: /* #{sym.comment} */ +#line #{sym.destructor.lineno} "#{@grammar_file_path}" + {#{sym.destructor.translated_code(sym.tag)}} +#line [@oline@] [@ofile@] + break; + + STR + end + + str + end + # b4_user_initial_action def user_initial_action(comment = "") return "" unless @grammar.initial_action diff --git a/lib/lrama/parser.rb b/lib/lrama/parser.rb index 148457f1..404b7c27 100644 --- a/lib/lrama/parser.rb +++ b/lib/lrama/parser.rb @@ -658,7 +658,7 @@ def token_to_str(t) module Lrama class Parser < Racc::Parser -module_eval(<<'...end parser.y/module_eval...', 'parser.y', 521) +module_eval(<<'...end parser.y/module_eval...', 'parser.y', 528) include Lrama::Report::Duration @@ -933,7 +933,7 @@ def raise_parse_error(error_message, location) 1, 63, :_reduce_none, 0, 76, :_reduce_29, 0, 77, :_reduce_30, - 7, 63, :_reduce_none, + 7, 63, :_reduce_31, 0, 78, :_reduce_32, 0, 79, :_reduce_33, 7, 63, :_reduce_34, @@ -1424,9 +1424,19 @@ def _reduce_30(val, _values, result) end .,., -# reduce 31 omitted +module_eval(<<'.,.,', 'parser.y', 100) + def _reduce_31(val, _values, result) + @grammar.add_destructor( + ident_or_tags: val[6], + token_code: val[3], + lineno: val[3].line + ) -module_eval(<<'.,.,', 'parser.y', 101) + result + end +.,., + +module_eval(<<'.,.,', 'parser.y', 108) def _reduce_32(val, _values, result) begin_c_declaration("}") @@ -1434,7 +1444,7 @@ def _reduce_32(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 105) +module_eval(<<'.,.,', 'parser.y', 112) def _reduce_33(val, _values, result) end_c_declaration @@ -1442,7 +1452,7 @@ def _reduce_33(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 109) +module_eval(<<'.,.,', 'parser.y', 116) def _reduce_34(val, _values, result) @grammar.add_printer( ident_or_tags: val[6], @@ -1454,7 +1464,7 @@ def _reduce_34(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 117) +module_eval(<<'.,.,', 'parser.y', 124) def _reduce_35(val, _values, result) begin_c_declaration("}") @@ -1462,7 +1472,7 @@ def _reduce_35(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 121) +module_eval(<<'.,.,', 'parser.y', 128) def _reduce_36(val, _values, result) end_c_declaration @@ -1470,7 +1480,7 @@ def _reduce_36(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 125) +module_eval(<<'.,.,', 'parser.y', 132) def _reduce_37(val, _values, result) @grammar.add_error_token( ident_or_tags: val[6], @@ -1482,7 +1492,7 @@ def _reduce_37(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 133) +module_eval(<<'.,.,', 'parser.y', 140) def _reduce_38(val, _values, result) @grammar.after_shift = val[1] @@ -1490,7 +1500,7 @@ def _reduce_38(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 137) +module_eval(<<'.,.,', 'parser.y', 144) def _reduce_39(val, _values, result) @grammar.before_reduce = val[1] @@ -1498,7 +1508,7 @@ def _reduce_39(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 141) +module_eval(<<'.,.,', 'parser.y', 148) def _reduce_40(val, _values, result) @grammar.after_reduce = val[1] @@ -1506,7 +1516,7 @@ def _reduce_40(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 145) +module_eval(<<'.,.,', 'parser.y', 152) def _reduce_41(val, _values, result) @grammar.after_shift_error_token = val[1] @@ -1514,7 +1524,7 @@ def _reduce_41(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 149) +module_eval(<<'.,.,', 'parser.y', 156) def _reduce_42(val, _values, result) @grammar.after_pop_stack = val[1] @@ -1524,7 +1534,7 @@ def _reduce_42(val, _values, result) # reduce 43 omitted -module_eval(<<'.,.,', 'parser.y', 155) +module_eval(<<'.,.,', 'parser.y', 162) def _reduce_44(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| @@ -1536,7 +1546,7 @@ def _reduce_44(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 163) +module_eval(<<'.,.,', 'parser.y', 170) def _reduce_45(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| @@ -1550,7 +1560,7 @@ def _reduce_45(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 173) +module_eval(<<'.,.,', 'parser.y', 180) def _reduce_46(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| @@ -1564,7 +1574,7 @@ def _reduce_46(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 183) +module_eval(<<'.,.,', 'parser.y', 190) def _reduce_47(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| @@ -1578,7 +1588,7 @@ def _reduce_47(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 193) +module_eval(<<'.,.,', 'parser.y', 200) def _reduce_48(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| @@ -1592,7 +1602,7 @@ def _reduce_48(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 204) +module_eval(<<'.,.,', 'parser.y', 211) def _reduce_49(val, _values, result) val[0].each {|token_declaration| @grammar.add_term(id: token_declaration[0], alias_name: token_declaration[2], token_id: token_declaration[1], tag: nil, replace: true) @@ -1602,7 +1612,7 @@ def _reduce_49(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 210) +module_eval(<<'.,.,', 'parser.y', 217) def _reduce_50(val, _values, result) val[1].each {|token_declaration| @grammar.add_term(id: token_declaration[0], alias_name: token_declaration[2], token_id: token_declaration[1], tag: val[0], replace: true) @@ -1612,7 +1622,7 @@ def _reduce_50(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 216) +module_eval(<<'.,.,', 'parser.y', 223) def _reduce_51(val, _values, result) val[2].each {|token_declaration| @grammar.add_term(id: token_declaration[0], alias_name: token_declaration[2], token_id: token_declaration[1], tag: val[1], replace: true) @@ -1622,28 +1632,28 @@ def _reduce_51(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 221) +module_eval(<<'.,.,', 'parser.y', 228) def _reduce_52(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'parser.y', 222) +module_eval(<<'.,.,', 'parser.y', 229) def _reduce_53(val, _values, result) result = val[0].append(val[1]) result end .,., -module_eval(<<'.,.,', 'parser.y', 224) +module_eval(<<'.,.,', 'parser.y', 231) def _reduce_54(val, _values, result) result = val result end .,., -module_eval(<<'.,.,', 'parser.y', 228) +module_eval(<<'.,.,', 'parser.y', 235) def _reduce_55(val, _values, result) rule = Grammar::ParameterizingRule::Rule.new(val[1].s_value, val[3], val[6]) @grammar.add_parameterizing_rule(rule) @@ -1652,21 +1662,21 @@ def _reduce_55(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 232) +module_eval(<<'.,.,', 'parser.y', 239) def _reduce_56(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'parser.y', 233) +module_eval(<<'.,.,', 'parser.y', 240) def _reduce_57(val, _values, result) result = val[0].append(val[2]) result end .,., -module_eval(<<'.,.,', 'parser.y', 237) +module_eval(<<'.,.,', 'parser.y', 244) def _reduce_58(val, _values, result) builder = val[0] result = [builder] @@ -1675,7 +1685,7 @@ def _reduce_58(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 242) +module_eval(<<'.,.,', 'parser.y', 249) def _reduce_59(val, _values, result) builder = val[2] result = val[0].append(builder) @@ -1684,7 +1694,7 @@ def _reduce_59(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 248) +module_eval(<<'.,.,', 'parser.y', 255) def _reduce_60(val, _values, result) reset_precs result = Grammar::ParameterizingRule::Rhs.new @@ -1693,7 +1703,7 @@ def _reduce_60(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 253) +module_eval(<<'.,.,', 'parser.y', 260) def _reduce_61(val, _values, result) reset_precs result = Grammar::ParameterizingRule::Rhs.new @@ -1702,7 +1712,7 @@ def _reduce_61(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 258) +module_eval(<<'.,.,', 'parser.y', 265) def _reduce_62(val, _values, result) token = val[1] token.alias_name = val[2] @@ -1714,7 +1724,7 @@ def _reduce_62(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 266) +module_eval(<<'.,.,', 'parser.y', 273) def _reduce_63(val, _values, result) builder = val[0] builder.symbols << Lrama::Lexer::Token::InstantiateRule.new(s_value: val[2], location: @lexer.location, args: [val[1]]) @@ -1724,7 +1734,7 @@ def _reduce_63(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 272) +module_eval(<<'.,.,', 'parser.y', 279) def _reduce_64(val, _values, result) builder = val[0] builder.symbols << Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, location: @lexer.location, args: val[3]) @@ -1734,7 +1744,7 @@ def _reduce_64(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 278) +module_eval(<<'.,.,', 'parser.y', 285) def _reduce_65(val, _values, result) if @prec_seen on_action_error("multiple User_code after %prec", val[0]) if @code_after_prec @@ -1746,7 +1756,7 @@ def _reduce_65(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 286) +module_eval(<<'.,.,', 'parser.y', 293) def _reduce_66(val, _values, result) end_c_declaration @@ -1754,7 +1764,7 @@ def _reduce_66(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 290) +module_eval(<<'.,.,', 'parser.y', 297) def _reduce_67(val, _values, result) user_code = val[3] user_code.alias_name = val[6] @@ -1766,7 +1776,7 @@ def _reduce_67(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 298) +module_eval(<<'.,.,', 'parser.y', 305) def _reduce_68(val, _values, result) sym = @grammar.find_symbol_by_id!(val[2]) @prec_seen = true @@ -1786,7 +1796,7 @@ def _reduce_68(val, _values, result) # reduce 72 omitted -module_eval(<<'.,.,', 'parser.y', 313) +module_eval(<<'.,.,', 'parser.y', 320) def _reduce_73(val, _values, result) result = [{tag: nil, tokens: val[0]}] @@ -1794,7 +1804,7 @@ def _reduce_73(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 317) +module_eval(<<'.,.,', 'parser.y', 324) def _reduce_74(val, _values, result) result = [{tag: val[0], tokens: val[1]}] @@ -1802,7 +1812,7 @@ def _reduce_74(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 321) +module_eval(<<'.,.,', 'parser.y', 328) def _reduce_75(val, _values, result) result = val[0].append({tag: val[1], tokens: val[2]}) @@ -1810,14 +1820,14 @@ def _reduce_75(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 324) +module_eval(<<'.,.,', 'parser.y', 331) def _reduce_76(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'parser.y', 325) +module_eval(<<'.,.,', 'parser.y', 332) def _reduce_77(val, _values, result) result = val[0].append(val[1]) result @@ -1828,7 +1838,7 @@ def _reduce_77(val, _values, result) # reduce 79 omitted -module_eval(<<'.,.,', 'parser.y', 332) +module_eval(<<'.,.,', 'parser.y', 339) def _reduce_80(val, _values, result) begin_c_declaration("}") @@ -1836,7 +1846,7 @@ def _reduce_80(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 336) +module_eval(<<'.,.,', 'parser.y', 343) def _reduce_81(val, _values, result) end_c_declaration @@ -1844,7 +1854,7 @@ def _reduce_81(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 340) +module_eval(<<'.,.,', 'parser.y', 347) def _reduce_82(val, _values, result) result = val[0].append(val[3]) @@ -1852,7 +1862,7 @@ def _reduce_82(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 344) +module_eval(<<'.,.,', 'parser.y', 351) def _reduce_83(val, _values, result) begin_c_declaration("}") @@ -1860,7 +1870,7 @@ def _reduce_83(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 348) +module_eval(<<'.,.,', 'parser.y', 355) def _reduce_84(val, _values, result) end_c_declaration @@ -1868,7 +1878,7 @@ def _reduce_84(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 352) +module_eval(<<'.,.,', 'parser.y', 359) def _reduce_85(val, _values, result) result = [val[2]] @@ -1876,7 +1886,7 @@ def _reduce_85(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 357) +module_eval(<<'.,.,', 'parser.y', 364) def _reduce_86(val, _values, result) result = [{tag: nil, tokens: val[0]}] @@ -1884,7 +1894,7 @@ def _reduce_86(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 361) +module_eval(<<'.,.,', 'parser.y', 368) def _reduce_87(val, _values, result) result = [{tag: val[0], tokens: val[1]}] @@ -1892,7 +1902,7 @@ def _reduce_87(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 365) +module_eval(<<'.,.,', 'parser.y', 372) def _reduce_88(val, _values, result) result = val[0].append({tag: val[1], tokens: val[2]}) @@ -1900,14 +1910,14 @@ def _reduce_88(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 368) +module_eval(<<'.,.,', 'parser.y', 375) def _reduce_89(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'parser.y', 369) +module_eval(<<'.,.,', 'parser.y', 376) def _reduce_90(val, _values, result) result = val[0].append(val[1]) result @@ -1916,14 +1926,14 @@ def _reduce_90(val, _values, result) # reduce 91 omitted -module_eval(<<'.,.,', 'parser.y', 373) +module_eval(<<'.,.,', 'parser.y', 380) def _reduce_92(val, _values, result) on_action_error("ident after %prec", val[0]) if @prec_seen result end .,., -module_eval(<<'.,.,', 'parser.y', 374) +module_eval(<<'.,.,', 'parser.y', 381) def _reduce_93(val, _values, result) on_action_error("char after %prec", val[0]) if @prec_seen result @@ -1938,7 +1948,7 @@ def _reduce_93(val, _values, result) # reduce 97 omitted -module_eval(<<'.,.,', 'parser.y', 384) +module_eval(<<'.,.,', 'parser.y', 391) def _reduce_98(val, _values, result) lhs = val[0] lhs.alias_name = val[1] @@ -1952,7 +1962,7 @@ def _reduce_98(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 395) +module_eval(<<'.,.,', 'parser.y', 402) def _reduce_99(val, _values, result) builder = val[0] if !builder.line @@ -1964,7 +1974,7 @@ def _reduce_99(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 403) +module_eval(<<'.,.,', 'parser.y', 410) def _reduce_100(val, _values, result) builder = val[2] if !builder.line @@ -1978,7 +1988,7 @@ def _reduce_100(val, _values, result) # reduce 101 omitted -module_eval(<<'.,.,', 'parser.y', 413) +module_eval(<<'.,.,', 'parser.y', 420) def _reduce_102(val, _values, result) reset_precs result = Grammar::RuleBuilder.new(@rule_counter, @midrule_action_counter) @@ -1987,7 +1997,7 @@ def _reduce_102(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 418) +module_eval(<<'.,.,', 'parser.y', 425) def _reduce_103(val, _values, result) reset_precs result = Grammar::RuleBuilder.new(@rule_counter, @midrule_action_counter) @@ -1996,7 +2006,7 @@ def _reduce_103(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 423) +module_eval(<<'.,.,', 'parser.y', 430) def _reduce_104(val, _values, result) token = val[1] token.alias_name = val[2] @@ -2008,7 +2018,7 @@ def _reduce_104(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 431) +module_eval(<<'.,.,', 'parser.y', 438) def _reduce_105(val, _values, result) token = Lrama::Lexer::Token::InstantiateRule.new(s_value: val[2], location: @lexer.location, args: [val[1]], lhs_tag: val[3]) builder = val[0] @@ -2020,7 +2030,7 @@ def _reduce_105(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 439) +module_eval(<<'.,.,', 'parser.y', 446) def _reduce_106(val, _values, result) token = Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, location: @lexer.location, args: val[3], lhs_tag: val[5]) builder = val[0] @@ -2032,7 +2042,7 @@ def _reduce_106(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 447) +module_eval(<<'.,.,', 'parser.y', 454) def _reduce_107(val, _values, result) if @prec_seen on_action_error("multiple User_code after %prec", val[0]) if @code_after_prec @@ -2044,7 +2054,7 @@ def _reduce_107(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 455) +module_eval(<<'.,.,', 'parser.y', 462) def _reduce_108(val, _values, result) end_c_declaration @@ -2052,7 +2062,7 @@ def _reduce_108(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 459) +module_eval(<<'.,.,', 'parser.y', 466) def _reduce_109(val, _values, result) user_code = val[3] user_code.alias_name = val[6] @@ -2064,7 +2074,7 @@ def _reduce_109(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 467) +module_eval(<<'.,.,', 'parser.y', 474) def _reduce_110(val, _values, result) sym = @grammar.find_symbol_by_id!(val[2]) @prec_seen = true @@ -2076,49 +2086,49 @@ def _reduce_110(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 474) +module_eval(<<'.,.,', 'parser.y', 481) def _reduce_111(val, _values, result) result = "option" result end .,., -module_eval(<<'.,.,', 'parser.y', 475) +module_eval(<<'.,.,', 'parser.y', 482) def _reduce_112(val, _values, result) result = "nonempty_list" result end .,., -module_eval(<<'.,.,', 'parser.y', 476) +module_eval(<<'.,.,', 'parser.y', 483) def _reduce_113(val, _values, result) result = "list" result end .,., -module_eval(<<'.,.,', 'parser.y', 478) +module_eval(<<'.,.,', 'parser.y', 485) def _reduce_114(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'parser.y', 479) +module_eval(<<'.,.,', 'parser.y', 486) def _reduce_115(val, _values, result) result = val[0].append(val[2]) result end .,., -module_eval(<<'.,.,', 'parser.y', 480) +module_eval(<<'.,.,', 'parser.y', 487) def _reduce_116(val, _values, result) result = [Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, location: @lexer.location, args: val[0])] result end .,., -module_eval(<<'.,.,', 'parser.y', 481) +module_eval(<<'.,.,', 'parser.y', 488) def _reduce_117(val, _values, result) result = [Lrama::Lexer::Token::InstantiateRule.new(s_value: val[0].s_value, location: @lexer.location, args: val[2])] result @@ -2127,7 +2137,7 @@ def _reduce_117(val, _values, result) # reduce 118 omitted -module_eval(<<'.,.,', 'parser.y', 484) +module_eval(<<'.,.,', 'parser.y', 491) def _reduce_119(val, _values, result) result = val[1].s_value result @@ -2138,7 +2148,7 @@ def _reduce_119(val, _values, result) # reduce 121 omitted -module_eval(<<'.,.,', 'parser.y', 491) +module_eval(<<'.,.,', 'parser.y', 498) def _reduce_122(val, _values, result) begin_c_declaration('\Z') @grammar.epilogue_first_lineno = @lexer.line + 1 @@ -2147,7 +2157,7 @@ def _reduce_122(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 496) +module_eval(<<'.,.,', 'parser.y', 503) def _reduce_123(val, _values, result) end_c_declaration @grammar.epilogue = val[2].s_value @@ -2166,14 +2176,14 @@ def _reduce_123(val, _values, result) # reduce 128 omitted -module_eval(<<'.,.,', 'parser.y', 507) +module_eval(<<'.,.,', 'parser.y', 514) def _reduce_129(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'parser.y', 508) +module_eval(<<'.,.,', 'parser.y', 515) def _reduce_130(val, _values, result) result = val[0].append(val[1]) result @@ -2184,7 +2194,7 @@ def _reduce_130(val, _values, result) # reduce 132 omitted -module_eval(<<'.,.,', 'parser.y', 513) +module_eval(<<'.,.,', 'parser.y', 520) def _reduce_133(val, _values, result) result = Lrama::Lexer::Token::Ident.new(s_value: val[0]) result diff --git a/parser.y b/parser.y index 1aedc75e..9819495c 100644 --- a/parser.y +++ b/parser.y @@ -97,6 +97,13 @@ rule end_c_declaration } "}" generic_symlist + { + @grammar.add_destructor( + ident_or_tags: val[6], + token_code: val[3], + lineno: val[3].line + ) + } | "%printer" "{" { begin_c_declaration("}") diff --git a/sig/lrama/grammar/code/destructor_code.rbs b/sig/lrama/grammar/code/destructor_code.rbs new file mode 100644 index 00000000..8dc4784f --- /dev/null +++ b/sig/lrama/grammar/code/destructor_code.rbs @@ -0,0 +1,15 @@ +module Lrama + class Grammar + class Code + class DestructorCode < Code + @tag: untyped + def initialize: (type: untyped, token_code: untyped, tag: untyped) -> void + + private + + # ref: Lrama::Grammar::Code.token_code.references + def reference_to_c: (untyped ref) -> untyped + end + end + end +end diff --git a/sig/lrama/grammar/destructor.rbs b/sig/lrama/grammar/destructor.rbs new file mode 100644 index 00000000..b00f3308 --- /dev/null +++ b/sig/lrama/grammar/destructor.rbs @@ -0,0 +1,11 @@ +module Lrama + class Grammar + class Destructor + attr_accessor ident_or_tags: Array[Lexer::Token::Ident|Lexer::Token::Tag] + attr_accessor token_code: Grammar::Code + attr_accessor lineno: Integer + + def translated_code: (Lexer::Token member) -> String + end + end +end diff --git a/sig/lrama/grammar/symbol.rbs b/sig/lrama/grammar/symbol.rbs index 0d066934..aa68e596 100644 --- a/sig/lrama/grammar/symbol.rbs +++ b/sig/lrama/grammar/symbol.rbs @@ -10,6 +10,7 @@ module Lrama attr_accessor nullable: bool attr_accessor precedence: Precedence? attr_accessor printer: Printer? + attr_accessor destructor: Destructor? attr_accessor error_token: ErrorToken attr_accessor first_set: Set[Array[Symbol]] diff --git a/spec/fixtures/integration/destructors.l b/spec/fixtures/integration/destructors.l new file mode 100644 index 00000000..c9ad7dc2 --- /dev/null +++ b/spec/fixtures/integration/destructors.l @@ -0,0 +1,47 @@ +%option noinput nounput noyywrap never-interactive yylineno bison-bridge bison-locations + +%{ + +#include +#include +#include "printers.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->val1 = atoi(yytext); + 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/destructors.y b/spec/fixtures/integration/destructors.y new file mode 100644 index 00000000..024a6949 --- /dev/null +++ b/spec/fixtures/integration/destructors.y @@ -0,0 +1,74 @@ +%{ + +#define YYDEBUG 1 + +#include +#include "printers.h" +#include "printers-lexer.h" + +static int yyerror(YYLTYPE *loc, const char *str); + +%} + +%union { + int val1; + int val2; + int val3; +} + +%token NUM +%type expr2 +%type expr +%left '+' '-' +%left '*' '/' + +%destructor { + printf("destructor for val1: %d\n", $$); +} // printer for TAG + +%destructor { + printf("destructor for val2: %d\n", $$); +} + +%destructor { + printf("destructor for expr: %d\n", $$); +} expr // printer for symbol + +%% + +program : /* empty */ + | expr { printf("=> %d\n", $1); } + | expr2 '+' + ; + +expr2: '+' NUM { $$ = $2; } + ; + +expr : NUM + | expr '+' expr { $$ = $1 + $3; } + | expr '-' expr { $$ = $1 - $3; } + | expr '*' expr { $$ = $1 * $3; } + | expr '/' expr { $$ = $1 / $3; } + | '(' expr ')' { $$ = $2; } + ; + +%% + +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]); + } + + if (yyparse()) { + fprintf(stderr, "syntax error\n"); + return 1; + } + return 0; +} diff --git a/spec/lrama/grammar/code_spec.rb b/spec/lrama/grammar/code_spec.rb index dfd35ba7..f175d866 100644 --- a/spec/lrama/grammar/code_spec.rb +++ b/spec/lrama/grammar/code_spec.rb @@ -75,6 +75,42 @@ end end + describe Lrama::Grammar::Code::DestructorCode do + describe "#translated_code" do + let(:tag) { token_class::Tag.new(s_value: '') } + + it "translats '$$' to '((*yyvaluep).val)'" do + code = described_class.new(type: :destructor, token_code: user_code_dollar_dollar, tag: tag) + expect(code.translated_code).to eq("print(((*yyvaluep).val));") + end + + it "translats '@$' to '(*yylocationp)'" do + code = described_class.new(type: :destructor, token_code: user_code_at_dollar, tag: tag) + expect(code.translated_code).to eq("print((*yylocationp));") + end + + it "raises error for '$:$'" do + code = described_class.new(type: :destructor, token_code: user_code_index_dollar, tag: tag) + expect { code.translated_code }.to raise_error("$:$ can not be used in destructor.") + end + + it "raises error for '$n'" do + code = described_class.new(type: :destructor, token_code: user_code_dollar_n, tag: tag) + expect { code.translated_code }.to raise_error("$n can not be used in destructor.") + end + + it "raises error for '@n'" do + code = described_class.new(type: :destructor, token_code: user_code_at_n, tag: tag) + expect { code.translated_code }.to raise_error("@n can not be used in destructor.") + end + + it "raises error for '$:n'" do + code = described_class.new(type: :destructor, token_code: user_code_index_n, tag: tag) + expect { code.translated_code }.to raise_error("$:1 can not be used in destructor.") + end + end + end + describe Lrama::Grammar::Code::PrinterCode do describe "#translated_code" do let(:tag) { token_class::Tag.new(s_value: '') } diff --git a/spec/lrama/grammar/symbols/resolver_spec.rb b/spec/lrama/grammar/symbols/resolver_spec.rb index d307ffca..bcbcac19 100644 --- a/spec/lrama/grammar/symbols/resolver_spec.rb +++ b/spec/lrama/grammar/symbols/resolver_spec.rb @@ -180,6 +180,17 @@ end end + describe "#fill_destructor" do + it "fills destructor" do + term = resolver.add_term(id: Lrama::Lexer::Token::Ident.new(s_value: "term")) + destructor = Lrama::Grammar::Destructor.new( + ident_or_tags: [Lrama::Lexer::Token::Ident.new(s_value: "term")] + ) + resolver.fill_destructor([destructor]) + expect(term.destructor).to eq(destructor) + end + end + describe "#fill_printer" do it "fills printer" do term = resolver.add_term(id: Lrama::Lexer::Token::Ident.new(s_value: "term")) diff --git a/spec/lrama/integration_spec.rb b/spec/lrama/integration_spec.rb index 9f62eb4c..6cb3925d 100644 --- a/spec/lrama/integration_spec.rb +++ b/spec/lrama/integration_spec.rb @@ -9,7 +9,7 @@ def exec_command(command) raise "#{command} failed." unless $?.success? end - def test_parser(parser_name, input, expected, lrama_command_args: [], debug: false) + def test_parser(parser_name, input, expected, expect_success: true, lrama_command_args: [], debug: false) tmpdir = Dir.tmpdir grammar_file_path = fixture_path("integration/#{parser_name}.y") lexer_file_path = fixture_path("integration/#{parser_name}.l") @@ -41,7 +41,8 @@ def test_parser(parser_name, input, expected, lrama_command_args: [], debug: fal STDERR.puts out STDERR.puts err end - expect(status.success?).to be(true), status.to_s + + expect(status.success?).to be(expect_success), status.to_s expect(out).to eq(expected) end @@ -130,6 +131,26 @@ def generate_object(grammar_file_path, c_path, obj_path, command_args: []) end end + describe "%destructor" do + it "prints messages when symbol is discarded" do + expected = <<~STR + destructor for expr: 1 + STR + test_parser("destructors", "1 +", expected, expect_success: false) + + expected = <<~STR + destructor for val2: 1 + STR + test_parser("destructors", "+ 1 -", expected, expect_success: false) + + expected = <<~STR + => 3 + destructor for val1: 3 + STR + test_parser("destructors", "1 + 2 3", expected, expect_success: false) + end + end + describe "__LINE__ of each place" do it "prints line number of each place" do expected = <<~STR diff --git a/template/bison/yacc.c b/template/bison/yacc.c index 6145a950..2d6753c2 100644 --- a/template/bison/yacc.c +++ b/template/bison/yacc.c @@ -1145,7 +1145,12 @@ yydestruct (const char *yymsg, YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp<%= output.user_args %>); YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - YY_USE (yykind); + switch (yykind) + { +<%= output.symbol_actions_for_destructor -%> + default: + break; + } YY_IGNORE_MAYBE_UNINITIALIZED_END }