summaryrefslogtreecommitdiff
path: root/tool/lrama/lib/lrama/output.rb
diff options
context:
space:
mode:
Diffstat (limited to 'tool/lrama/lib/lrama/output.rb')
-rw-r--r--tool/lrama/lib/lrama/output.rb370
1 files changed, 370 insertions, 0 deletions
diff --git a/tool/lrama/lib/lrama/output.rb b/tool/lrama/lib/lrama/output.rb
new file mode 100644
index 0000000000..eaefbd04dc
--- /dev/null
+++ b/tool/lrama/lib/lrama/output.rb
@@ -0,0 +1,370 @@
+require "erb"
+require "forwardable"
+require "lrama/report"
+
+module Lrama
+ class Output
+ extend Forwardable
+ include Report::Duration
+
+ attr_reader :grammar_file_path, :context, :grammar
+
+ def_delegators "@context", :yyfinal, :yylast, :yyntokens, :yynnts, :yynrules, :yynstates,
+ :yymaxutok, :yypact_ninf, :yytable_ninf
+
+ def_delegators "@grammar", :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol
+
+ def initialize(out:, output_file_path:, template_name:, grammar_file_path:, header_out: nil, header_file_path: nil, context:, grammar:)
+ @out = out
+ @output_file_path = output_file_path
+ @template_name = template_name
+ @grammar_file_path = grammar_file_path
+ @header_out = header_out
+ @header_file_path = header_file_path
+ @context = context
+ @grammar = grammar
+ end
+
+ if ERB.instance_method(:initialize).parameters.last.first == :key
+ def self.erb(input)
+ ERB.new(input, trim_mode: '-')
+ end
+ else
+ def self.erb(input)
+ ERB.new(input, nil, '-')
+ end
+ end
+
+ def eval_template(file, path)
+ erb = self.class.erb(File.read(file))
+ erb.filename = file
+ tmp = erb.result_with_hash(context: @context, output: self)
+ replace_special_variables(tmp, path)
+ end
+
+ def render
+ report_duration(:render) do
+ tmp = eval_template(template_file, @output_file_path)
+ @out << tmp
+
+ if @header_file_path
+ tmp = eval_template(header_template_file, @header_file_path)
+
+ if @header_out
+ @header_out << tmp
+ else
+ File.open(@header_file_path, "w+") do |f|
+ f << tmp
+ end
+ end
+ end
+ end
+ end
+
+ # A part of b4_token_enums
+ def token_enums
+ str = ""
+
+ @context.yytokentype.each do |s_value, token_id, display_name|
+ s = sprintf("%s = %d%s", s_value, token_id, token_id == yymaxutok ? "" : ",")
+
+ if display_name
+ str << sprintf(" %-30s /* %s */\n", s, display_name)
+ else
+ str << sprintf(" %s\n", s)
+ end
+ end
+
+ str
+ end
+
+ # b4_symbol_enum
+ def symbol_enum
+ str = ""
+
+ last_sym_number = @context.yysymbol_kind_t.last[1]
+ @context.yysymbol_kind_t.each do |s_value, sym_number, display_name|
+ s = sprintf("%s = %d%s", s_value, sym_number, (sym_number == last_sym_number) ? "" : ",")
+
+ if display_name
+ str << sprintf(" %-40s /* %s */\n", s, display_name)
+ else
+ str << sprintf(" %s\n", s)
+ end
+ end
+
+ str
+ end
+
+ def yytranslate
+ int_array_to_string(@context.yytranslate)
+ end
+
+ def yyrline
+ int_array_to_string(@context.yyrline)
+ end
+
+ def yytname
+ string_array_to_string(@context.yytname) + " YY_NULLPTR"
+ end
+
+ # b4_int_type_for
+ def int_type_for(ary)
+ min = ary.min
+ max = ary.max
+
+ case
+ when (-127 <= min && min <= 127) && (-127 <= max && max <= 127)
+ "yytype_int8"
+ when (0 <= min && min <= 255) && (0 <= max && max <= 255)
+ "yytype_uint8"
+ when (-32767 <= min && min <= 32767) && (-32767 <= max && max <= 32767)
+ "yytype_int16"
+ when (0 <= min && min <= 65535) && (0 <= max && max <= 65535)
+ "yytype_uint16"
+ else
+ "int"
+ end
+ end
+
+ def symbol_actions_for_printer
+ str = ""
+
+ @grammar.symbols.each do |sym|
+ next unless sym.printer
+
+ str << <<-STR
+ case #{sym.enum_name}: /* #{sym.comment} */
+#line #{sym.printer.lineno} "#{@grammar_file_path}"
+ #{sym.printer.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
+
+ <<-STR
+ #{comment}
+#line #{@grammar.initial_action.line} "#{@grammar_file_path}"
+ #{@grammar.initial_action.translated_code}
+ STR
+ end
+
+ # b4_user_actions
+ def user_actions
+ str = ""
+
+ @context.states.rules.each do |rule|
+ next unless rule.code
+
+ rule = rule
+ code = rule.code
+ spaces = " " * (code.column - 1)
+
+ str << <<-STR
+ case #{rule.id + 1}: /* #{rule.as_comment} */
+#line #{code.line} "#{@grammar_file_path}"
+#{spaces}#{rule.translated_code}
+#line [@oline@] [@ofile@]
+ break;
+
+ STR
+ end
+
+ str << <<-STR
+
+#line [@oline@] [@ofile@]
+ STR
+
+ str
+ end
+
+ def omit_braces_and_blanks(param)
+ param[1..-2].strip
+ end
+
+ # b4_parse_param
+ def parse_param
+ if @grammar.parse_param
+ omit_braces_and_blanks(@grammar.parse_param)
+ else
+ ""
+ end
+ end
+
+ def lex_param
+ if @grammar.lex_param
+ omit_braces_and_blanks(@grammar.lex_param)
+ else
+ ""
+ end
+ end
+
+ # b4_user_formals
+ def user_formals
+ if @grammar.parse_param
+ ", #{parse_param}"
+ else
+ ""
+ end
+ end
+
+ # b4_user_args
+ def user_args
+ if @grammar.parse_param
+ ", #{parse_param_name}"
+ else
+ ""
+ end
+ end
+
+ def extract_param_name(param)
+ /\A(.)+([a-zA-Z0-9_]+)\z/.match(param)[2]
+ end
+
+ def parse_param_name
+ if @grammar.parse_param
+ extract_param_name(parse_param)
+ else
+ ""
+ end
+ end
+
+ def lex_param_name
+ if @grammar.lex_param
+ extract_param_name(lex_param)
+ else
+ ""
+ end
+ end
+
+ # b4_parse_param_use
+ def parse_param_use(val, loc)
+ str = <<-STR
+ YY_USE (#{val});
+ YY_USE (#{loc});
+ STR
+
+ if @grammar.parse_param
+ str << " YY_USE (#{parse_param_name});"
+ end
+
+ str
+ end
+
+ # b4_yylex_formals
+ def yylex_formals
+ ary = ["&yylval", "&yylloc"]
+
+ if @grammar.lex_param
+ ary << lex_param_name
+ end
+
+ "(#{ary.join(', ')})"
+ end
+
+ # b4_table_value_equals
+ def table_value_equals(table, value, literal, symbol)
+ if literal < table.min || table.max < literal
+ "0"
+ else
+ "((#{value}) == #{symbol})"
+ end
+ end
+
+ # b4_yyerror_args
+ def yyerror_args
+ ary = ["&yylloc"]
+
+ if @grammar.parse_param
+ ary << parse_param_name
+ end
+
+ "#{ary.join(', ')}"
+ end
+
+ def template_basename
+ File.basename(template_file)
+ end
+
+ def aux
+ @grammar.aux
+ end
+
+ def int_array_to_string(ary)
+ last = ary.count - 1
+
+ s = ary.each_with_index.each_slice(10).map do |slice|
+ str = " "
+
+ slice.each do |e, i|
+ str << sprintf("%6d%s", e, (i == last) ? "" : ",")
+ end
+
+ str
+ end
+
+ s.join("\n")
+ end
+
+ def spec_mapped_header_file
+ @header_file_path
+ end
+
+ def b4_cpp_guard__b4_spec_mapped_header_file
+ if @header_file_path
+ "YY_YY_" + @header_file_path.gsub(/[^a-zA-Z_0-9]+/, "_").upcase + "_INCLUDED"
+ else
+ ""
+ end
+ end
+
+ private
+
+ def template_file
+ File.join(template_dir, @template_name)
+ end
+
+ def header_template_file
+ File.join(template_dir, "bison/yacc.h")
+ end
+
+ def template_dir
+ File.expand_path("../../../template", __FILE__)
+ end
+
+ def string_array_to_string(ary)
+ str = ""
+ tmp = " "
+
+ ary.each do |s|
+ s = s.gsub('\\', '\\\\\\\\')
+ s = s.gsub('"', '\\"')
+
+ if (tmp + s + " \"\",").length > 75
+ str << tmp << "\n"
+ tmp = " \"#{s}\","
+ else
+ tmp << " \"#{s}\","
+ end
+ end
+
+ str << tmp
+ end
+
+ def replace_special_variables(str, ofile)
+ str.each_line.with_index(1).map do |line, i|
+ line.gsub!("[@oline@]", (i + 1).to_s)
+ line.gsub!("[@ofile@]", "\"#{ofile}\"")
+ line
+ end.join
+ end
+ end
+end