<% # This file is interpreted by $(BASERUBY) and miniruby. # $(BASERUBY) is used for miniprelude.c. # miniruby is used for prelude.c. # Since $(BASERUBY) may be older than Ruby 1.9, # Ruby 1.9 feature should not be used. require_relative '../tool/ruby_vm/helpers/c_escape' class Prelude include RubyVM::CEscape LINE_LIMIT = 509 # by C89 def prelude_base(filename) filename.chomp(".rb") end def prelude_name(filename) "" end def initialize(output, preludes, vpath) @output = output @vpath = vpath @prelude_count = 0 @builtin_count = 0 @preludes = {} @mains = preludes.map do |filename| if prelude = filename.end_with?("golf_prelude.rb") @prelude_count += 1 else @builtin_count += 1 end translate(filename, (filename unless prelude))[0] end @preludes.delete_if {|_, (_, _, lines, sub)| sub && lines.empty?} end def translate(filename, sub = false) idx = @preludes[filename] return idx if idx lines = [] start_line = nil lineno = 0 result = [@preludes.size, @vpath.strip(filename), lines, sub] @vpath.foreach(filename) do |line| line.force_encoding("ASCII-8BIT") if line.respond_to?(:force_encoding) line.rstrip! lineno += 1 @preludes[filename] ||= result comment = ($1 || '' if line.sub!(/(?:^|\s+)\#(?!\{)(?:$|(?:[#\s]|(?=\W))(.*))/, '')) if !line.empty? or start_line line << "\n" start_line ||= lineno end if line.size > LINE_LIMIT raise "#{filename}:#{lines.size+1}: too long line" end line.sub!(/require(_relative)?\s*\(?\s*(["'])(.*?)(?:\.rb)?\2\)?/) do orig, rel, path = $&, $2, $3 if rel path = File.join(File.dirname(filename), path) nil while path.gsub!(%r'(\A|/)(?!\.\.?/)[^/]+/\.\.(?:/|\z)', '') end path = translate("#{path}.rb", true) rescue nil if path # This library will be loaded before this, # the order cannot be preserved comment = "#{orig} #{comment}".rstrip "" else orig end end lines << [line, comment] end result << (start_line || 1) result end end Prelude.new(output, ARGV, vpath).instance_eval do -%> /* -*-c-*- THIS FILE WAS AUTOGENERATED BY template/prelude.c.tmpl. DO NOT EDIT. sources: <%= @preludes.map {|n,*| prelude_base(n)}.join(', ') %><%=%> */ %unless @preludes.empty? #include "internal.h" #include "internal/warnings.h" #include "iseq.h" #include "ruby/ruby.h" #include "vm_core.h" COMPILER_WARNING_PUSH #if __has_warning("-Wstring-concatenation") COMPILER_WARNING_IGNORED(-Wstring-concatenation) #endif % preludes = @preludes.values.sort % preludes.each {|i, prelude, lines, sub| % name = prelude_name(*prelude) static const char prelude_name<%=i%><%=%>[] = <%=rstring2cstr(name)%>; static const struct { % size = beg = 0 % lines.each_with_index {|(line, comment), n| % if size + line.size < Prelude::LINE_LIMIT % size += line.size % next % end char L<%=beg%><%=%>[<%=size%><%=%>]; /* <%=beg+1%>..<%=n%> */ % size = line.size % beg = n % } % if size > 0 char L<%=beg%><%=%>[<%=size%><%=%>]; /* <%=beg+1%>..<%=lines.size+1%> */ % end } prelude_code<%=i%><%=%> = { % size = 0 #line 1 <%=rstring2cstr(prelude)%> % lines.each_with_index {|(line, comment), n| % if size + line.size >= Prelude::LINE_LIMIT % size = 0 , #line <%=n+1%> <%=rstring2cstr(prelude)%> % end % size += line.size <%=rstring2cstr(line)%><%if comment%><%=commentify(comment)%><%end%> % } #line <%=_erbout.count("\n")+2%> "<%=@output%>" }; % } COMPILER_WARNING_POP % unless preludes.empty? #define PRELUDE_NAME(n) rb_usascii_str_new_static(prelude_name##n, sizeof(prelude_name##n)-1) #define PRELUDE_CODE(n) rb_utf8_str_new_static(prelude_code##n.L0, sizeof(prelude_code##n)) static rb_ast_t * prelude_ast(VALUE name, VALUE code, int line) { rb_ast_t *ast = rb_parser_compile_string_path(rb_parser_new(), name, code, line); if (!ast || !ast->body.root) { if (ast) rb_ast_dispose(ast); rb_exc_raise(rb_errinfo()); } return ast; } % end % if @builtin_count > 0 #define PRELUDE_AST(n, name_str, start_line) \ (((sizeof(prelude_name<%='##'%><%=%>n) - prefix_len - 2) == namelen) && \ (strncmp(prelude_name<%='##'%><%=%>n + prefix_len, feature_name, namelen) == 0) ? \ prelude_ast((name_str) = PRELUDE_NAME(n), PRELUDE_CODE(n), start_line) : 0) rb_ast_t * rb_builtin_ast(const char *feature_name, VALUE *name_str) { const size_t prefix_len = rb_strlen_lit("<%=%>, *name_str, <%=start_line%>)) != 0) return ast; % end % end return ast; } % end % if @prelude_count > 0 COMPILER_WARNING_PUSH #if GCC_VERSION_SINCE(4, 2, 0) COMPILER_WARNING_ERROR(-Wmissing-field-initializers) #endif static void prelude_eval(VALUE code, VALUE name, int line) { static const rb_compile_option_t optimization = { TRUE, /* int inline_const_cache; */ TRUE, /* int peephole_optimization; */ FALSE,/* int tailcall_optimization; */ TRUE, /* int specialized_instruction; */ TRUE, /* int operands_unification; */ TRUE, /* int instructions_unification; */ TRUE, /* int stack_caching; */ TRUE, /* int frozen_string_literal; */ FALSE, /* int debug_frozen_string_literal; */ FALSE, /* unsigned int coverage_enabled; */ 0, /* int debug_level; */ }; rb_ast_t *ast = prelude_ast(name, code, line); rb_iseq_eval(rb_iseq_new_with_opt(&ast->body, name, name, Qnil, line, NULL, 0, ISEQ_TYPE_TOP, &optimization)); rb_ast_dispose(ast); } COMPILER_WARNING_POP % end %end % init_name = @output && @output[/\w+(?=_prelude.c\b)/] || 'prelude' void Init_<%=init_name%><%=%>(void) { %unless @prelude_count.zero? % preludes.each do |i, prelude, lines, sub, start_line| % next if sub prelude_eval(PRELUDE_CODE(<%=i%><%=%>), PRELUDE_NAME(<%=i%><%=%>), <%=start_line%><%=%>); % end #if 0 % preludes.length.times {|i| printf("%.*s", (int)sizeof(prelude_code<%=i%><%=%>), prelude_code<%=i%><%=%>.L0); % } #endif %end } <%end -%>