diff --git a/assets/parser.d.erb b/assets/parser.d.erb index 9794031..398c071 100644 --- a/assets/parser.d.erb +++ b/assets/parser.d.erb @@ -118,7 +118,7 @@ class <%= @classname %> } <% transition_table, state_table = @lexer.build_tables %> - private static const Transition transitions[] = [ + private static immutable Transition transitions[] = [ <% transition_table.each do |transition_table_entry| %> Transition(<%= transition_table_entry[:first] %>u, <%= transition_table_entry[:last] %>u, <%= transition_table_entry[:destination] %>u), <% end %> @@ -251,4 +251,54 @@ class <%= @classname %> return cast(uint)-1; } } + + static class Parser + { + private struct Shift + { + uint token_id; + uint state_id; + } + + private struct Reduce + { + uint token_id; + uint rule_id; + uint rule_set_id; + } + + private struct State + { + uint shift_table_index; + uint n_shift_entries; + uint reduce_table_index; + uint n_reduce_entries; + } + +<% state_table, shift_table, reduce_table = @parser.build_tables %> + private static immutable Shift shifts[] = [ +<% shift_table.each do |shift| %> + Shift(<%= shift[:token_id] %>u, <%= shift[:state_id] %>u), +<% end %> + ]; + + private static immutable Reduce reduces[] = [ +<% reduce_table.each do |reduce| %> + Reduce(<%= reduce[:token_id] %>u, <%= reduce[:rule_id] %>u, <%= reduce[:rule_set_id] %>u), +<% end %> + ]; + + private static immutable State states[] = [ +<% state_table.each do |state| %> + State(<%= state[:shift_index] %>u, <%= state[:n_shifts] %>u, <%= state[:reduce_index] %>u, <%= state[:n_reduces] %>u), +<% end %> + ]; + + private Lexer m_lexer; + + this(const(ubyte) * input, size_t input_length) + { + m_lexer = new Lexer(input, input_length); + } + } } diff --git a/lib/propane/generator.rb b/lib/propane/generator.rb index 06b9450..ef5953b 100644 --- a/lib/propane/generator.rb +++ b/lib/propane/generator.rb @@ -33,7 +33,9 @@ class Propane if tokens_by_name.include?(rule.name) raise Error.new("Rule name collides with token name #{rule.name.inspect}") end - rule_sets[rule.name] ||= RuleSet.new(rule.name) + @_rule_set_id ||= @grammar.tokens.size + rule_sets[rule.name] ||= RuleSet.new(rule.name, @_rule_set_id) + @_rule_set_id += 1 rule.rule_set = rule_sets[rule.name] rule_sets[rule.name] << rule end diff --git a/lib/propane/parser.rb b/lib/propane/parser.rb index bec27c7..fbca27d 100644 --- a/lib/propane/parser.rb +++ b/lib/propane/parser.rb @@ -60,22 +60,43 @@ class Propane def build_tables shift_table = [] state_table = [] + reduce_table = [] @item_sets.each do |item_set| shift_entries = item_set.following_symbols.select do |following_symbol| following_symbol.is_a?(Token) end.map do |following_symbol| + state_id = + if following_symbol == @eof_token + 0 + else + item_set.following_item_set[following_symbol].id + end { token_id: following_symbol.id, - state_id: item_set.following_item_set[following_symbol].id, + state_id: state_id, } end + reduce_entries = + case ra = item_set.reduce_actions + when Rule + [{token_id: TOKEN_NONE, rule_id: ra.id, rule_set_id: ra.rule_set.id}] + when Hash + ra.map do |token, rule| + {token_id: token.id, rule_id: rule.id, rule_set_id: rule.rule_set.id} + end + else + [] + end state_table << { shift_index: shift_table.size, n_shifts: shift_entries.size, + reduce_index: reduce_table.size, + n_reduces: reduce_entries.size, } shift_table += shift_entries + reduce_table += reduce_entries end - [state_table, shift_table] + [state_table, shift_table, reduce_table] end private diff --git a/lib/propane/rule_set.rb b/lib/propane/rule_set.rb index fb6c101..0437481 100644 --- a/lib/propane/rule_set.rb +++ b/lib/propane/rule_set.rb @@ -2,6 +2,10 @@ class Propane class RuleSet + # @return [Integer] + # ID of the RuleSet. + attr_accessor :id + # @return [String] # Name of the RuleSet. attr_reader :name @@ -18,7 +22,10 @@ class Propane # # @param name [String] # Name of the RuleSet. - def initialize(name) + # @param id [Integer] + # ID of the RuleSet. + def initialize(name, id) + @id = id @name = name @rules = [] @could_be_empty = false