From 8412c68e90892c045b5f1805b60104d07851062f Mon Sep 17 00:00:00 2001 From: yui-knk Date: Sun, 19 Nov 2023 13:14:12 +0900 Subject: [PATCH] Extract functions --- lib/lrama/grammar/parser_state.rb | 96 ++++++++++++++++ lib/lrama/output.rb | 65 +---------- sig/lrama/grammar/parser_state.rbs | 11 ++ spec/lrama/grammar/parser_state_spec.rb | 144 ++++++++++++++++++++++++ template/bison/yacc.c | 7 +- 5 files changed, 259 insertions(+), 64 deletions(-) diff --git a/lib/lrama/grammar/parser_state.rb b/lib/lrama/grammar/parser_state.rb index ec26a916..3ca71729 100644 --- a/lib/lrama/grammar/parser_state.rb +++ b/lib/lrama/grammar/parser_state.rb @@ -8,10 +8,106 @@ def initialize(state_id:, state_list:) @state_list = state_list end + def enum_definition + <<~ENUM + enum #{enum_name} + { + #{enum_body} + }; + typedef enum #{enum_name} #{enum_type}; + + static const char *const #{enum_name_table_name}[] = { + #{int_to_name.join(", ")} + }; + + YY_ATTRIBUTE_UNUSED + static const char * + #{enum_name}_name (#{enum_type} num) + { + return #{enum_name_table_name}[num]; + } + + # define YY_STATE_#{state_name.upcase}_NAME #{enum_name}_name (*#{stack_prefix}_p) + ENUM + end + + def states_functions + # TODO: Stack check for push + <<~FUNC + # define YYPUSH_STATE_#{state_name.upcase}(value) \\ + do \\ + { \\ + YYDPRINTF ((stderr, "Push #{state_name}\\n")); \\ + *++#{stack_prefix}_p = value; \\ + } \\ + while (0) + + # define YYPOP_STATE_#{state_name.upcase}() \\ + do \\ + { \\ + YYDPRINTF ((stderr, "Pop #{state_name}\\n")); \\ + if (#{stack_prefix}_p != #{stack_prefix}) \\ + { \\ + #{stack_prefix}_p -= 1; \\ + } \\ + else \\ + { \\ + YYDPRINTF ((stderr, "Try to pop empty #{state_name} stack\\n")); \\ + } \\ + } \\ + while (0) + + # define YYSET_STATE_#{state_name.upcase}(value) \\ + do \\ + { \\ + YYDPRINTF ((stderr, "Set #{state_name}\\n")); \\ + *#{stack_prefix}_p = value; \\ + } \\ + while (0) + + # define YY_STATE_#{state_name.upcase} #{stack_prefix}_p + FUNC + end + + def states_stacks + <<~STACKS + /* The parser state stack (#{stack_prefix}): array, bottom, top. */ + int #{stack_prefix}_a[YYINITDEPTH]; + int *#{stack_prefix} = #{stack_prefix}_a; + int *#{stack_prefix}_p = #{stack_prefix}; + STACKS + end + + def state_name + state_id.s_value + end + def enum_name + "yyparser_state_#{state_name}" end def enum_type + "#{enum_name}_t" + end + + def enum_body + state_list.map do |state| + state.s_value + end.join(",\n ") + end + + def int_to_name + state_list.map do |state| + "\"#{state.s_value}\"" + end << "YY_NULLPTR" + end + + def enum_name_table_name + "#{enum_name}_names" + end + + def stack_prefix + "yyparser_state_#{state_name}" end end end diff --git a/lib/lrama/output.rb b/lib/lrama/output.rb index d2fdb1b0..e4305f0d 100644 --- a/lib/lrama/output.rb +++ b/lib/lrama/output.rb @@ -359,73 +359,20 @@ def percent_code(name) end def parser_states_enums - @grammar.parser_states.map do |parser_state| - enum_name = "yyparser_state_#{parser_state.state_id.s_value}" - - enum_body = parser_state.state_list.map do |state| - state.s_value - end.join(",\n ") - - int_to_name = parser_state.state_list.map do |state| - "\"#{state.s_value}\"" - end << "YY_NULLPTR" - - <<~ENUM - enum #{enum_name} - { - #{enum_body} - }; - typedef enum #{enum_name} #{enum_name}_t; - - static const char *const #{enum_name}name[] = { - #{int_to_name.join(", ")} - }; - - YY_ATTRIBUTE_UNUSED - static const char * - #{enum_name}_name (#{enum_name}_t num) - { - return #{enum_name}name[num]; - } - - # define YY_STATE_#{parser_state.state_id.s_value.upcase}_NAME #{enum_name}_name (*yyparser_state_#{parser_state.state_id.s_value}_p) - ENUM + @grammar.parser_states.map do |ps| + ps.enum_definition end end def parser_states_stacks - @grammar.parser_states.map do |parser_state| - "yyparser_state_#{parser_state.state_id.s_value}" + @grammar.parser_states.map do |ps| + ps.states_stacks end end def parser_states_functions - # TODO: Stack check for push and pop - @grammar.parser_states.map do |parser_state| - <<~FUNC - # define YYPUSH_STATE_#{parser_state.state_id.s_value.upcase}(value) \\ - do \\ - { \\ - *++yyparser_state_#{parser_state.state_id.s_value}_p = value; \\ - } \\ - while (0) - - # define YYPOP_STATE_#{parser_state.state_id.s_value.upcase}() \\ - do \\ - { \\ - yyparser_state_#{parser_state.state_id.s_value}_p -= 1; \\ - } \\ - while (0) - - # define YYSET_STATE_#{parser_state.state_id.s_value.upcase}(value) \\ - do \\ - { \\ - *yyparser_state_#{parser_state.state_id.s_value}_p = value; \\ - } \\ - while (0) - - # define YY_STATE_#{parser_state.state_id.s_value.upcase} yyparser_state_#{parser_state.state_id.s_value}_p - FUNC + @grammar.parser_states.map do |ps| + ps.states_functions end end diff --git a/sig/lrama/grammar/parser_state.rbs b/sig/lrama/grammar/parser_state.rbs index 47a40ff3..9fc80c46 100644 --- a/sig/lrama/grammar/parser_state.rbs +++ b/sig/lrama/grammar/parser_state.rbs @@ -5,6 +5,17 @@ module Lrama attr_reader state_list: Array[Lexer::Token::Ident] def initialize: (state_id: Lexer::Token::Ident, state_list: Array[Lexer::Token::Ident]) -> void + + def enum_definition: () -> String + def states_functions: () -> String + def states_stacks: () -> String + def state_name: () -> String + def enum_name: () -> String + def enum_type: () -> String + def enum_body: () -> String + def int_to_name: () -> Array[String] + def enum_name_table_name: () -> String + def stack_prefix: () -> String end end end diff --git a/spec/lrama/grammar/parser_state_spec.rb b/spec/lrama/grammar/parser_state_spec.rb index e69de29b..07792c8d 100644 --- a/spec/lrama/grammar/parser_state_spec.rb +++ b/spec/lrama/grammar/parser_state_spec.rb @@ -0,0 +1,144 @@ +RSpec.describe Lrama::Grammar::ParserState do + let(:location) { Lrama::Lexer::Location.new(first_line: 1, first_column: 0, last_line: 1, last_column: 0) } + let(:state_id) { Lrama::Lexer::Token::Ident.new(s_value: "in_rescue", location: location) } + let(:state_list) do + [ + Lrama::Lexer::Token::Ident.new(s_value: "before_rescue", location: location), + Lrama::Lexer::Token::Ident.new(s_value: "after_rescue", location: location), + Lrama::Lexer::Token::Ident.new(s_value: "after_else", location: location), + Lrama::Lexer::Token::Ident.new(s_value: "after_ensure", location: location) + ] + end + let(:parser_state) { Lrama::Grammar::ParserState.new(state_id: state_id, state_list: state_list) } + + describe "#enum_definition" do + it "returns enum definition" do + expect(parser_state.enum_definition).to eq <<~ENUM + enum yyparser_state_in_rescue + { + before_rescue, + after_rescue, + after_else, + after_ensure + }; + typedef enum yyparser_state_in_rescue yyparser_state_in_rescue_t; + + static const char *const yyparser_state_in_rescue_names[] = { + "before_rescue", "after_rescue", "after_else", "after_ensure", YY_NULLPTR + }; + + YY_ATTRIBUTE_UNUSED + static const char * + yyparser_state_in_rescue_name (yyparser_state_in_rescue_t num) + { + return yyparser_state_in_rescue_names[num]; + } + + # define YY_STATE_IN_RESCUE_NAME yyparser_state_in_rescue_name (*yyparser_state_in_rescue_p) + ENUM + end + end + + describe "#states_functions" do + it "returns states functions" do + expect(parser_state.states_functions).to eq <<~FUNC + # define YYPUSH_STATE_IN_RESCUE(value) \\ + do \\ + { \\ + YYDPRINTF ((stderr, "Push in_rescue\\n")); \\ + *++yyparser_state_in_rescue_p = value; \\ + } \\ + while (0) + + # define YYPOP_STATE_IN_RESCUE() \\ + do \\ + { \\ + YYDPRINTF ((stderr, "Pop in_rescue\\n")); \\ + if (yyparser_state_in_rescue_p != yyparser_state_in_rescue) \\ + { \\ + yyparser_state_in_rescue_p -= 1; \\ + } \\ + else \\ + { \\ + YYDPRINTF ((stderr, "Try to pop empty in_rescue stack\\n")); \\ + } \\ + } \\ + while (0) + + # define YYSET_STATE_IN_RESCUE(value) \\ + do \\ + { \\ + YYDPRINTF ((stderr, "Set in_rescue\\n")); \\ + *yyparser_state_in_rescue_p = value; \\ + } \\ + while (0) + + # define YY_STATE_IN_RESCUE yyparser_state_in_rescue_p + FUNC + end + end + + describe "#states_stacks" do + it "returns states stacks" do + expect(parser_state.states_stacks).to eq <<~STACKS + /* The parser state stack (yyparser_state_in_rescue): array, bottom, top. */ + int yyparser_state_in_rescue_a[YYINITDEPTH]; + int *yyparser_state_in_rescue = yyparser_state_in_rescue_a; + int *yyparser_state_in_rescue_p = yyparser_state_in_rescue; + STACKS + end + end + + describe "#state_name" do + it "returns state name" do + expect(parser_state.state_name).to eq "in_rescue" + end + end + + describe "#enum_name" do + it "returns enum name" do + expect(parser_state.enum_name).to eq "yyparser_state_in_rescue" + end + end + + describe "#enum_type" do + it "returns enum type" do + expect(parser_state.enum_type).to eq "yyparser_state_in_rescue_t" + end + end + + describe "#enum_body" do + it "returns enum body" do + expect(parser_state.enum_body).to eq <<~BODY.chomp + before_rescue, + after_rescue, + after_else, + after_ensure + BODY + end + end + + describe "#int_to_name" do + it "returns int to name table" do + expect(parser_state.int_to_name).to eq [ + "\"before_rescue\"", + "\"after_rescue\"", + "\"after_else\"", + "\"after_ensure\"", + "YY_NULLPTR" + ] + end + end + + describe "#enum_name_table_name" do + it "returns table name" do + expect(parser_state.enum_name_table_name).to eq "yyparser_state_in_rescue_names" + end + end + + describe "#stack_prefix" do + it "returns prefix" do + expect(parser_state.stack_prefix).to eq "yyparser_state_in_rescue" + end + end +end diff --git a/template/bison/yacc.c b/template/bison/yacc.c index 8e8dd28b..a5733718 100644 --- a/template/bison/yacc.c +++ b/template/bison/yacc.c @@ -1519,11 +1519,8 @@ YYLTYPE yylloc = yyloc_default; YYLTYPE *yyls = yylsa; YYLTYPE *yylsp = yyls; - <%- output.parser_states_stacks.each do |parser_state| -%> - /* The parser state stack (<%= parser_state %>): array, bottom, top. */ - int <%= parser_state %>_a[YYINITDEPTH]; - int *<%= parser_state %> = <%= parser_state %>_a; - int *<%= parser_state %>_p = <%= parser_state %>; + <%- output.parser_states_stacks.each do |states_stacks| -%> + <%= states_stacks %> <%- end -%> int yyn;