diff --git a/go.mod b/go.mod index b1fed6ea..c2c3db74 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/go-fft/fft v0.0.0-20260620110530-0e3ca1747acb github.com/go-images/images v0.0.0-20260620184442-aa6cd1c0beb7 github.com/go-ndarray/ndarray v0.0.0-20260620170009-555bfc31e7a3 + github.com/go-ruby-erb/erb v0.0.0-20260629074717-0999ae4dd529 github.com/go-ruby-marshal/marshal v0.0.0-20260622114304-27ed1baddd9f github.com/go-ruby-parser/parser v0.0.0-20260626192347-1bbe8b4672d0 github.com/go-ruby-regexp/regexp v0.0.0-20260628133338-5a3a8b96feac diff --git a/go.sum b/go.sum index 7c7fd6ef..70e2cf79 100644 --- a/go.sum +++ b/go.sum @@ -26,6 +26,8 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-ndarray/ndarray v0.0.0-20260620170009-555bfc31e7a3 h1:oqUg3BkpFqPqIhLG1RXFW4gpBGWN4mq5FyOqOvjc6gg= github.com/go-ndarray/ndarray v0.0.0-20260620170009-555bfc31e7a3/go.mod h1:CZPf0P3FjwYlphONFD0ZAjE+/eaixCZYvYhm6QqmqsU= +github.com/go-ruby-erb/erb v0.0.0-20260629074717-0999ae4dd529 h1:md/l6ifEY6S0Iy1nBk5hVt5BhnWJRqhB08gVEsVvS28= +github.com/go-ruby-erb/erb v0.0.0-20260629074717-0999ae4dd529/go.mod h1:HEJVI7jQ/BnjiSiu2Wj934QboEAgdv47RNOqvZb+nRw= github.com/go-ruby-marshal/marshal v0.0.0-20260622114304-27ed1baddd9f h1:IIBuHgiQJbssGPhrUbagvP74febWhmKxiNk7IQq1//g= github.com/go-ruby-marshal/marshal v0.0.0-20260622114304-27ed1baddd9f/go.mod h1:UpjSgWGcQSNt0ARWuHKwf0P55LkwZ65vuqoRPSKctZ8= github.com/go-ruby-parser/parser v0.0.0-20260626192347-1bbe8b4672d0 h1:Q3ryvK9hPcbvX6uBw4ZpTKFIDkaGBVEtjLtxXaruP6Y= diff --git a/internal/vm/builtins.go b/internal/vm/builtins.go index 92c8f028..871830c3 100644 --- a/internal/vm/builtins.go +++ b/internal/vm/builtins.go @@ -418,6 +418,7 @@ func (vm *VM) bootstrap() { vm.registerRipper() // ripper loadable shell (Ripper.sexp etc. raise); needs StandardError vm.registerSyslog() // Syslog loadable shell (feature probe) vm.registerCGI() // CGI.escape/unescape (real over net/url) + HTML helpers + vm.registerERB() // ERB class skeleton + ERB::Util (backed by go-ruby-erb); prelude adds the Ruby API vm.registerMonitor() // Monitor/MonitorMixin (single-thread synchronize); needs StandardError vm.registerZlib() // needs the exception hierarchy (Zlib::Error < StandardError) vm.registerFiber() // needs the exception hierarchy (FiberError < StandardError) diff --git a/internal/vm/erb.go b/internal/vm/erb.go new file mode 100644 index 00000000..02a45d20 --- /dev/null +++ b/internal/vm/erb.go @@ -0,0 +1,90 @@ +// Copyright (c) the go-embedded-ruby/ruby authors +// +// SPDX-License-Identifier: BSD-3-Clause + +package vm + +import ( + "github.com/go-embedded-ruby/ruby/internal/object" + erb "github.com/go-ruby-erb/erb" +) + +// registerERB installs the native backbone of the ERB standard library: the ERB +// class with a private native compiler hook and the ERB::Util module of escaping +// helpers. The template scanner/compiler lives entirely in the pure-Go +// github.com/go-ruby-erb/erb library (mirroring MRI's ERB::Compiler byte for +// byte); the interpreter-bound pieces — initialize / result / result_with_hash — +// stay in Ruby (prelude.rb) because they need a Binding and eval. ERB::Util's +// pure functions (html_escape / url_encode and their h / u aliases) bind +// straight through to the library so the escaping output also comes from one +// authoritative source. +// +// The class and module are created here so the native hook __compile can be a +// private instance method; the prelude reopens ERB to add the public API. +func (vm *VM) registerERB() { + cERB := newClass("ERB", vm.cObject) + vm.consts["ERB"] = cERB + + // __compile(template, trim_mode, eoutvar) -> src. The single bridge into the + // library: it returns the Ruby source the compiled template evaluates to + // (already carrying the "#coding:UTF-8\n" magic prefix). A nil trim_mode is + // the no-trim default; eoutvar names the output buffer. The library never + // fails on template syntax, but a malformed-option error is surfaced as an + // ArgumentError so the contract stays total on the Ruby side. + cERB.methods["__compile"] = &Method{name: "__compile", owner: cERB, native: func(_ *VM, _ object.Value, args []object.Value, _ *Proc) object.Value { + template := strArg(args[0]) + trimMode := optStrArg(args[1]) + eoutvar := strArg(args[2]) + src, _, err := erbCompile(template, erb.Options{TrimMode: trimMode, EOutVar: eoutvar}) + if err != nil { + raise("ArgumentError", "%s", err.Error()) + } + return object.NewString(src) + }} + + // ERB::Util — html_escape / url_encode (with the one-letter h / u aliases) + // bound to the library's pure functions, callable as module functions + // (ERB::Util.h(...)). MRI coerces non-strings with to_s first; the conversion + // happens here via toS so the library sees a String. + util := newClass("Util", nil) + util.isModule = true + cERB.consts["Util"] = util + udef := func(name string, fn NativeFn) { util.smethods[name] = &Method{name: name, owner: util, native: fn} } + + htmlEscape := func(vm *VM, _ object.Value, args []object.Value, _ *Proc) object.Value { + return object.NewString(erb.HTMLEscape(erbToS(vm, args[0]))) + } + urlEncode := func(vm *VM, _ object.Value, args []object.Value, _ *Proc) object.Value { + return object.NewString(erb.URLEncode(erbToS(vm, args[0]))) + } + udef("html_escape", htmlEscape) + udef("h", htmlEscape) + udef("url_encode", urlEncode) + udef("u", urlEncode) +} + +// erbCompile is the seam over the go-ruby-erb compiler. The library never fails +// on a well-formed template (Compile's err is reserved for genuinely malformed +// options, unreachable through this binding), so the error branch is exercised +// only by swapping this var in a fault-injection test. +var erbCompile = erb.Compile + +// optStrArg returns the String value of v, or "" when v is nil — ERB's trim_mode +// keyword defaults to nil (no trimming), which the library spells as the empty +// trim string. +func optStrArg(v object.Value) string { + if _, isNil := v.(object.Nil); isNil { + return "" + } + return strArg(v) +} + +// erbToS coerces an ERB::Util argument to its String form the way MRI does, +// invoking #to_s on non-strings (so nil -> "", 123 -> "123"). A String is used +// verbatim. +func erbToS(vm *VM, v object.Value) string { + if s, ok := v.(*object.String); ok { + return string(s.B) + } + return string(vm.send(v, "to_s", nil, nil).(*object.String).B) +} diff --git a/internal/vm/erb_internal_test.go b/internal/vm/erb_internal_test.go new file mode 100644 index 00000000..b736bfb9 --- /dev/null +++ b/internal/vm/erb_internal_test.go @@ -0,0 +1,29 @@ +// Copyright (c) the go-embedded-ruby/ruby authors +// +// SPDX-License-Identifier: BSD-3-Clause + +package vm + +import ( + "errors" + "testing" + + erb "github.com/go-ruby-erb/erb" +) + +// TestERBCompileError covers the otherwise-unreachable Compile-error branch of +// the native __compile hook. go-ruby-erb never fails on a well-formed template, +// so the failure is injected through the erbCompile seam; the binding must +// surface it as a Ruby ArgumentError carrying the error's message. +func TestERBCompileError(t *testing.T) { + saved := erbCompile + defer func() { erbCompile = saved }() + erbCompile = func(string, erb.Options) (string, string, error) { + return "", "", errors.New("boom") + } + + got := runFS(t, `require "erb"; begin; ERB.new("x"); rescue => e; p [e.class, e.message]; end`) + if want := "[ArgumentError, \"boom\"]\n"; got != want { + t.Errorf("got=%q want=%q", got, want) + } +} diff --git a/internal/vm/prelude.rb b/internal/vm/prelude.rb index 9381bb57..2742229f 100644 --- a/internal/vm/prelude.rb +++ b/internal/vm/prelude.rb @@ -1732,242 +1732,20 @@ def prune module_function :find, :prune end -# ERB is a pure-Ruby embedded-Ruby template engine (require "erb"), written from -# scratch to match MRI 4.0.5's observable behavior rather than copied from MRI's -# erb.rb. A template mixes literal text with three tag kinds — +# ERB is a pure-Go embedded-Ruby template engine (require "erb"), matching MRI +# 4.0.5's observable behavior. A template mixes literal text with three tag kinds — # # <% code %> run Ruby, emit nothing # <%= expr %> run Ruby, append expr.to_s to the output buffer # <%# comment %> ignored entirely # -# and the literals <%% / %%> stand for a single <% / %>. ERB#new scans the -# template once and compiles it to a Ruby string that builds up an output buffer; -# #result evals that string in a caller-supplied binding, so the template sees the -# caller's locals (the same approach MRI takes). Trim modes control how newlines -# adjacent to tags are handled. +# and the literals <%% / %%> stand for a single <% / %>. The template scan/compile +# (and the trim-mode handling) is done by the go-ruby-erb library, reached through +# the native ERB.__compile below; #result evals the compiled source in a +# caller-supplied binding, so the template sees the caller's locals (the same +# approach MRI takes). The ERB class and ERB::Util are created natively (erb.go); +# this reopens ERB to add the interpreter-bound public API. class ERB - # Compiler turns a template string into the Ruby source that, when evaluated, - # produces the rendered output. Each tag contributes one statement appending to - # the buffer named by eoutvar; literal runs are appended verbatim. - class Compiler - # A token of the scanned template: a kind (:text/:code/:expr/:comment) and its - # literal contents (for :text) or Ruby source (for :code/:expr). - Token = Struct.new(:kind, :content, :stop) - - attr_reader :trim_mode, :percent - - def initialize(trim_mode) - # Normalize the trim mode into two flags: the explicit set of newline-trimming - # behaviors, and whether "%"-prefixed lines are treated as code lines. MRI - # accepts a String combining several characters (e.g. ">", "<>", "-", "%"). - mode = trim_mode.to_s - @percent = mode.include?("%") - @trim_gt = mode.include?(">") # trim newline after a tag that ends the line - @trim_lgt = mode.include?("<>") # trim only when tag both starts and ends line - @trim_dash = mode.include?("-") # honor -%> to trim the immediately-following newline - end - - # compile returns [src, enc_string]: the Ruby source that builds the buffer, and - # the magic encoding comment line, matching MRI's two-value contract. - def compile(str) - out = +'' - tokens = scan(str) - tokens.each do |tok| - case tok.kind - when :text - next if tok.content.empty? - out << " #{@put_cmd}(#{quote(tok.content)}.freeze);" - when :expr - out << " #{@insert_cmd}((#{tok.content}).to_s);" - when :code - out << " #{tok.content};" - when :comment - # contributes nothing to the output - end - end - src = "#{@eoutvar} = +'';#{out} #{@eoutvar}" - ["#coding:UTF-8\n", src] - end - - # add_put_cmd / add_insert_cmd record the buffer name and the append commands - # used when compiling, matching MRI's Compiler accessors that ERB#new sets. - attr_accessor :put_cmd, :insert_cmd, :pre_cmd, :post_cmd - attr_accessor :eoutvar - - private - - # scan walks the template and yields tokens, applying tag detection, the <%% / - # %%> literals, trim handling and (optionally) "%"-line code lines. This is the - # heart of the engine; the trim rules reproduce MRI's exactly: - # -%> (dash mode) strips one immediately-following "\n" - # > strips the "\n" after any tag that ends its line - # <> strips the "\n" only when the tag also starts its line - def scan(str) - tokens = [] - # Percent-line mode: a line beginning with "%" is a code line; "%%" at the - # start is a literal "%". Rewrite such lines into <% ... %> / literal text - # before the main tag scan so the rest of the logic is uniform. - str = expand_percent_lines(str) if @percent - - i = 0 - n = str.length - pending_text = +'' - while i < n - open = str.index("<%", i) - unless open - pending_text << str[i..-1] - break - end - pending_text << str[i...open] - # Literal "<%%" -> emit a literal "<%" and continue past it. - if str[open + 2] == "%" - pending_text << "<%" - i = open + 3 - next - end - # Determine tag kind from the character after "<%". - marker = str[open + 2] - kind = - case marker - when "=" then :expr - when "#" then :comment - else :code - end - body_start = open + 2 - body_start += 1 if marker == "=" || marker == "#" - close, dash = find_close(str, body_start) - # An unterminated tag (no closing "%>") is not an error in MRI: the opening - # "<%" (and any "="/"#" marker) is dropped and the remainder of the string - # is emitted as literal text. - unless close - pending_text << str[body_start..-1] - break - end - content = str[body_start...(dash ? close - 1 : close)] - # A "%%>" within the body is the escape for a literal "%>"; unescape it. - content = content.gsub("%%>", "%>") - # The text accumulated so far becomes a token; record whether the tag sits on - # its own line for <> trimming. - starts_line = pending_text.empty? || pending_text.end_with?("\n") - flush_text(tokens, pending_text) - pending_text = +'' - tok = Token.new(kind, content.dup, nil) - tokens << tok - i = close + 2 # past "%>" - # Trim the trailing newline per the active mode. - i = apply_trim(str, i, dash, starts_line, tokens) - end - flush_text(tokens, pending_text) - tokens - end - - # flush_text appends a :text token for the accumulated literal run if non-empty. - def flush_text(tokens, text) - tokens << Token.new(:text, text.dup, nil) unless text.empty? - end - - # find_close locates the "%>" terminating a tag started at from, honoring the - # "%%>" literal (which stands for "%>" in the body and does not close the tag) - # and detecting the "-%>" dash terminator. Returns [index_of_%>, dash?]. - def find_close(str, from) - i = from - n = str.length - while i < n - pct = str.index("%", i) - return [nil, false] unless pct - # "%%>" -> literal "%>" inside the tag body; skip both percents. - if str[pct + 1] == "%" && str[pct + 2] == ">" - i = pct + 3 - next - end - if str[pct + 1] == ">" - dash = pct > from && str[pct - 1] == "-" - return [pct, dash] - end - i = pct + 1 - end - [nil, false] - end - - # apply_trim consumes a trailing newline after a closing tag when the active trim - # mode calls for it, returning the new scan index. The dash flag is the -%> - # terminator; starts_line says the tag began its own line (for <> mode). - def apply_trim(str, i, dash, starts_line, tokens) - # "-%>" (dash mode): strip exactly one immediately-following newline. - if dash && @trim_dash && str[i] == "\n" - return i + 1 - end - # ">" / "<>" only trim code/comment-bearing tag lines whose newline is the - # very next character (spaces in between defeat the trim, matching MRI). - if (@trim_gt || @trim_lgt) && str[i] == "\n" - if @trim_lgt - return i + 1 if starts_line - else - return i + 1 - end - end - i - end - - # expand_percent_lines rewrites "%"-prefixed lines into tags for percent mode: - # a leading "%" introduces a code line (the rest becomes <% ... %>), and a - # leading "%%" is an escaped literal "%". - def expand_percent_lines(str) - out = +'' - str.each_line do |line| - if line.start_with?("%%") - out << line[1..-1] - elsif line.start_with?("%") - # A "%" code line — including its terminating newline — produces no output, - # so the whole line collapses into a bare <% ... %> tag with no trailing - # literal newline. - code = line.end_with?("\n") ? line[1...-1] : line[1..-1] - out << "<% #{code} %>" - else - out << line - end - end - out - end - - # quote renders a literal text run as a Ruby double-quoted string with the - # backslash, quote, and newline metacharacters escaped so the compiled source - # round-trips the text exactly. - def quote(text) - escaped = text.gsub("\\", "\\\\\\\\") - .gsub('"', '\\"') - .gsub("\n", '\\n') - .gsub("\r", '\\r') - .gsub("\t", '\\t') - "\"#{escaped}\"" - end - end - - # Util provides the HTML/URL escaping helpers ERB exposes as module functions - # (and their one-letter aliases h / u), matching MRI's ERB::Util output exactly. - module Util - # html_escape replaces the five HTML-significant characters with their entity - # references; non-strings are coerced with to_s first, matching MRI. - def html_escape(s) - s.to_s.gsub(/[&<>"']/, HTML_ESCAPE) - end - alias h html_escape - - # url_encode percent-encodes every byte except the RFC-3986 unreserved set - # (A-Z a-z 0-9 - _ . ~), upper-casing the hex digits like MRI. - def url_encode(s) - s.to_s.b.gsub(/[^a-zA-Z0-9_\-.~]/) { |c| format("%%%02X", c.ord) } - end - alias u url_encode - - module_function :html_escape, :h, :url_encode, :u - end - - # HTML_ESCAPE is the entity table used by html_escape; defined once and frozen. - HTML_ESCAPE = { - "&" => "&", "<" => "<", ">" => ">", - '"' => """, "'" => "'" - }.freeze attr_reader :src, :encoding attr_accessor :filename, :lineno @@ -1979,13 +1757,11 @@ def url_encode(s) def initialize(str, trim_mode: nil, eoutvar: "_erbout") @filename = nil @lineno = 0 - compiler = Compiler.new(trim_mode) - compiler.eoutvar = eoutvar - compiler.put_cmd = "#{eoutvar}.<<" - compiler.insert_cmd = "#{eoutvar}.<<" - enc, code = compiler.compile(str) @encoding = "UTF-8" - @src = "#{enc}#{code}" + # The go-ruby-erb library scans and compiles the template, returning the Ruby + # source (already carrying the "#coding:UTF-8\n" magic prefix) that, when + # eval'd in a binding, builds and returns the rendered string. + @src = __compile(str, trim_mode, eoutvar) end # result evaluates the compiled source in binding b and returns the rendered diff --git a/internal/vm/prelude_frozen_gen.go b/internal/vm/prelude_frozen_gen.go index a39956a4..790f30d6 100644 --- a/internal/vm/prelude_frozen_gen.go +++ b/internal/vm/prelude_frozen_gen.go @@ -4429,371 +4429,29 @@ func embeddedPrelude() *bytecode.ISeq { }, &bytecode.ISeq{ Name: "", - Insns: []bytecode.Instr{{Op: 42}, {Op: 9}, {Op: 43, A: 1, B: 1}, {Op: 9}, {Op: 1}, {Op: 1, A: 1}, {Op: 1, A: 2}, {Op: 1, A: 3}, {Op: 1, A: 4}, {Op: 1, A: 5}, {Op: 1, A: 6}, {Op: 1, A: 7}, {Op: 1, A: 8}, {Op: 1, A: 9}, {Op: 7, A: 5}, {Op: 30, A: 2, Flags: 1}, {Op: 41, A: 3}, {Op: 9}, {Op: 5}, {Op: 1, A: 10}, {Op: 1, A: 11}, {Op: 30, A: 4, B: 2}, {Op: 9}, {Op: 5}, {Op: 1, A: 12}, {Op: 1, A: 13}, {Op: 30, A: 5, B: 2}, {Op: 9}, {Op: 44, A: 6, B: 2}, {Op: 9}, {Op: 44, A: 7, B: 3}, {Op: 9}, {Op: 44, A: 8, B: 4}, {Op: 9}, {Op: 44, A: 9, B: 5}, {Op: 9}, {Op: 5}, {Op: 30, A: 10}, {Op: 9}, {Op: 44, A: 11, B: 6}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 5}, {Op: 1}, {Op: 1, A: 1}, {Op: 30, B: 2}, {Op: 9}, {Op: 5}, {Op: 1, A: 2}, {Op: 1, A: 3}, {Op: 30, A: 1, B: 2}, {Op: 9}, {Op: 44, A: 2}, {Op: 9}, {Op: 44, A: 3, B: 1}, {Op: 9}, {Op: 44, A: 4, B: 2}, {Op: 9}, {Op: 44, A: 5, B: 3}, {Op: 9}, {Op: 5}, {Op: 30, A: 6}, {Op: 9}, {Op: 44, A: 7, B: 4}, {Op: 53}}, Consts: []object.Value{ - object.NewString("&"), - object.NewString("&"), - object.NewString("<"), - object.NewString("<"), - object.NewString(">"), - object.NewString(">"), - object.NewString("\""), - object.NewString("""), - object.NewString("'"), - object.NewString("'"), object.Symbol("src"), object.Symbol("encoding"), object.Symbol("filename"), object.Symbol("lineno"), }, - Names: []string{"Compiler", "Util", "freeze", "HTML_ESCAPE", "attr_reader", "attr_accessor", "initialize", "result", "result_with_hash", "run", "private", "new_toplevel"}, + Names: []string{"attr_reader", "attr_accessor", "initialize", "result", "result_with_hash", "run", "private", "new_toplevel"}, NumRequired: 0, SplatIndex: -1, KwRestSlot: -1, BlockSlot: -1, NumLocals: 0, Children: []*bytecode.ISeq{ - &bytecode.ISeq{ - Name: "", - Insns: []bytecode.Instr{{Op: 33}, {Op: 1}, {Op: 1, A: 1}, {Op: 1, A: 2}, {Op: 30, A: 1, B: 3, Flags: 1}, {Op: 41, A: 2}, {Op: 9}, {Op: 5}, {Op: 1, A: 3}, {Op: 1, A: 4}, {Op: 30, A: 3, B: 2}, {Op: 9}, {Op: 44, A: 4}, {Op: 9}, {Op: 44, A: 5, B: 1}, {Op: 9}, {Op: 5}, {Op: 1, A: 5}, {Op: 1, A: 6}, {Op: 1, A: 7}, {Op: 1, A: 8}, {Op: 30, A: 6, B: 4}, {Op: 9}, {Op: 5}, {Op: 1, A: 9}, {Op: 30, A: 6, B: 1}, {Op: 9}, {Op: 5}, {Op: 30, A: 7}, {Op: 9}, {Op: 44, A: 8, B: 2}, {Op: 9}, {Op: 44, A: 9, B: 3}, {Op: 9}, {Op: 44, A: 10, B: 4}, {Op: 9}, {Op: 44, A: 11, B: 5}, {Op: 9}, {Op: 44, A: 12, B: 6}, {Op: 9}, {Op: 44, A: 13, B: 7}, {Op: 53}}, - Consts: []object.Value{ - object.Symbol("kind"), - object.Symbol("content"), - object.Symbol("stop"), - object.Symbol("trim_mode"), - object.Symbol("percent"), - object.Symbol("put_cmd"), - object.Symbol("insert_cmd"), - object.Symbol("pre_cmd"), - object.Symbol("post_cmd"), - object.Symbol("eoutvar"), - }, - Names: []string{"Struct", "new", "Token", "attr_reader", "initialize", "compile", "attr_accessor", "private", "scan", "flush_text", "find_close", "apply_trim", "expand_percent_lines", "quote"}, - NumRequired: 0, - SplatIndex: -1, - KwRestSlot: -1, - BlockSlot: -1, - NumLocals: 0, - Children: []*bytecode.ISeq{ - &bytecode.ISeq{ - Name: "initialize", - Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 12, A: 1}, {Op: 9}, {Op: 11, A: 1}, {Op: 1}, {Op: 30, A: 1, B: 1, Flags: 1}, {Op: 32, A: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 1, A: 1}, {Op: 30, A: 1, B: 1, Flags: 1}, {Op: 32, A: 3}, {Op: 9}, {Op: 11, A: 1}, {Op: 1, A: 2}, {Op: 30, A: 1, B: 1, Flags: 1}, {Op: 32, A: 4}, {Op: 9}, {Op: 11, A: 1}, {Op: 1, A: 3}, {Op: 30, A: 1, B: 1, Flags: 1}, {Op: 32, A: 5}, {Op: 53}}, - Consts: []object.Value{ - object.NewString("%"), - object.NewString(">"), - object.NewString("<>"), - object.NewString("-"), - }, - Names: []string{"to_s", "include?", "@percent", "@trim_gt", "@trim_lgt", "@trim_dash"}, - Params: []string{"trim_mode"}, - NumRequired: 1, - SplatIndex: -1, - KwRestSlot: -1, - BlockSlot: -1, - NumLocals: 2, - Locals: []string{"trim_mode", "mode"}, - Super: "", - }, - &bytecode.ISeq{ - Name: "compile", - Insns: []bytecode.Instr{{Op: 1}, {Op: 12, A: 1}, {Op: 9}, {Op: 5}, {Op: 11}, {Op: 30, B: 1}, {Op: 12, A: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 30, A: 1, C: 1, Flags: 1}, {Op: 9}, {Op: 1, A: 1}, {Op: 1, A: 2}, {Op: 30, A: 2}, {Op: 13}, {Op: 31, A: 3}, {Op: 30, A: 2}, {Op: 13}, {Op: 1, A: 3}, {Op: 30, A: 2}, {Op: 13}, {Op: 11, A: 1}, {Op: 30, A: 2}, {Op: 13}, {Op: 1, A: 4}, {Op: 30, A: 2}, {Op: 13}, {Op: 31, A: 3}, {Op: 30, A: 2}, {Op: 13}, {Op: 1, A: 5}, {Op: 30, A: 2}, {Op: 13}, {Op: 12, A: 3}, {Op: 9}, {Op: 1, A: 6}, {Op: 11, A: 3}, {Op: 6, A: 2}, {Op: 53}}, - Consts: []object.Value{ - object.NewString(""), - object.NewString(""), - object.NewString(""), - object.NewString(" = +'';"), - object.NewString(" "), - object.NewString(""), - object.NewString("#coding:UTF-8\n"), - }, - Names: []string{"scan", "each", "to_s", "@eoutvar"}, - Params: []string{"str"}, - NumRequired: 1, - SplatIndex: -1, - KwRestSlot: -1, - BlockSlot: -1, - NumLocals: 4, - Locals: []string{"str", "out", "tokens", "src"}, - Children: []*bytecode.ISeq{ - &bytecode.ISeq{ - Name: "", - Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 12, A: 1}, {Op: 9}, {Op: 1}, {Op: 11, A: 1}, {Op: 30, A: 1, B: 1}, {Op: 27, A: 9}, {Op: 26, A: 40}, {Op: 11}, {Op: 30, A: 2, Flags: 1}, {Op: 30, A: 3, Flags: 1}, {Op: 28, A: 16}, {Op: 2}, {Op: 53}, {Op: 26, A: 17}, {Op: 2}, {Op: 9}, {Op: 11, A: 1, B: 1}, {Op: 1, A: 1}, {Op: 1, A: 2}, {Op: 30, A: 4}, {Op: 13}, {Op: 31, A: 5}, {Op: 30, A: 4}, {Op: 13}, {Op: 1, A: 3}, {Op: 30, A: 4}, {Op: 13}, {Op: 5}, {Op: 11}, {Op: 30, A: 2, Flags: 1}, {Op: 30, A: 6, B: 1}, {Op: 30, A: 4}, {Op: 13}, {Op: 1, A: 4}, {Op: 30, A: 4}, {Op: 13}, {Op: 30, A: 7, B: 1}, {Op: 26, A: 92}, {Op: 1, A: 5}, {Op: 11, A: 1}, {Op: 30, A: 1, B: 1}, {Op: 27, A: 45}, {Op: 26, A: 65}, {Op: 11, A: 1, B: 1}, {Op: 1, A: 6}, {Op: 1, A: 7}, {Op: 30, A: 4}, {Op: 13}, {Op: 31, A: 8}, {Op: 30, A: 4}, {Op: 13}, {Op: 1, A: 8}, {Op: 30, A: 4}, {Op: 13}, {Op: 11}, {Op: 30, A: 2, Flags: 1}, {Op: 30, A: 4}, {Op: 13}, {Op: 1, A: 9}, {Op: 30, A: 4}, {Op: 13}, {Op: 30, A: 7, B: 1}, {Op: 26, A: 92}, {Op: 1, A: 10}, {Op: 11, A: 1}, {Op: 30, A: 1, B: 1}, {Op: 27, A: 70}, {Op: 26, A: 84}, {Op: 11, A: 1, B: 1}, {Op: 1, A: 11}, {Op: 1, A: 12}, {Op: 30, A: 4}, {Op: 13}, {Op: 11}, {Op: 30, A: 2, Flags: 1}, {Op: 30, A: 4}, {Op: 13}, {Op: 1, A: 13}, {Op: 30, A: 4}, {Op: 13}, {Op: 30, A: 7, B: 1}, {Op: 26, A: 92}, {Op: 1, A: 14}, {Op: 11, A: 1}, {Op: 30, A: 1, B: 1}, {Op: 27, A: 89}, {Op: 26, A: 91}, {Op: 2}, {Op: 26, A: 92}, {Op: 2}, {Op: 53}}, - Consts: []object.Value{ - object.Symbol("text"), - object.NewString(""), - object.NewString(" "), - object.NewString("("), - object.NewString(".freeze);"), - object.Symbol("expr"), - object.NewString(""), - object.NewString(" "), - object.NewString("(("), - object.NewString(").to_s);"), - object.Symbol("code"), - object.NewString(""), - object.NewString(" "), - object.NewString(";"), - object.Symbol("comment"), - }, - Names: []string{"kind", "===", "content", "empty?", "to_s", "@put_cmd", "quote", "<<", "@insert_cmd"}, - Params: []string{"tok"}, - NumRequired: 1, - SplatIndex: -1, - KwRestSlot: -1, - BlockSlot: -1, - NumLocals: 2, - Locals: []string{"tok", ""}, - Super: "", - }, - }, - Super: "", - }, - &bytecode.ISeq{ - Name: "scan", - Insns: []bytecode.Instr{{Op: 6}, {Op: 12, A: 1}, {Op: 9}, {Op: 31}, {Op: 28, A: 10}, {Op: 5}, {Op: 11}, {Op: 30, A: 1, B: 1}, {Op: 12}, {Op: 26, A: 11}, {Op: 2}, {Op: 9}, {Op: 1}, {Op: 12, A: 2}, {Op: 9}, {Op: 11}, {Op: 30, A: 2, Flags: 1}, {Op: 12, A: 3}, {Op: 9}, {Op: 1, A: 1}, {Op: 12, A: 4}, {Op: 9}, {Op: 11, A: 2}, {Op: 11, A: 3}, {Op: 18}, {Op: 28, A: 216}, {Op: 11}, {Op: 1, A: 2}, {Op: 11, A: 2}, {Op: 30, A: 3, B: 2, Flags: 1}, {Op: 12, A: 5}, {Op: 9}, {Op: 11, A: 5}, {Op: 25}, {Op: 28, A: 45}, {Op: 11, A: 4}, {Op: 11}, {Op: 11, A: 2}, {Op: 1, A: 3}, {Op: 8}, {Op: 30, A: 4, B: 1, Flags: 1}, {Op: 30, A: 5, B: 1}, {Op: 9}, {Op: 26, A: 216}, {Op: 26, A: 46}, {Op: 2}, {Op: 9}, {Op: 11, A: 4}, {Op: 11}, {Op: 11, A: 2}, {Op: 11, A: 5}, {Op: 8, A: 1}, {Op: 30, A: 4, B: 1, Flags: 1}, {Op: 30, A: 5, B: 1}, {Op: 9}, {Op: 11}, {Op: 11, A: 5}, {Op: 1, A: 4}, {Op: 13}, {Op: 30, A: 4, B: 1, Flags: 1}, {Op: 1, A: 5}, {Op: 22}, {Op: 28, A: 74}, {Op: 11, A: 4}, {Op: 1, A: 6}, {Op: 30, A: 5, B: 1}, {Op: 9}, {Op: 11, A: 5}, {Op: 1, A: 7}, {Op: 13}, {Op: 12, A: 2}, {Op: 9}, {Op: 26, A: 22}, {Op: 26, A: 75}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 11, A: 5}, {Op: 1, A: 4}, {Op: 13}, {Op: 30, A: 4, B: 1, Flags: 1}, {Op: 12, A: 6}, {Op: 9}, {Op: 11, A: 6}, {Op: 12, A: 7}, {Op: 9}, {Op: 1, A: 8}, {Op: 11, A: 7}, {Op: 30, A: 6, B: 1}, {Op: 27, A: 91}, {Op: 26, A: 93}, {Op: 1, A: 9}, {Op: 26, A: 101}, {Op: 1, A: 10}, {Op: 11, A: 7}, {Op: 30, A: 6, B: 1}, {Op: 27, A: 98}, {Op: 26, A: 100}, {Op: 1, A: 11}, {Op: 26, A: 101}, {Op: 1, A: 12}, {Op: 12, A: 8}, {Op: 9}, {Op: 11, A: 5}, {Op: 1, A: 4}, {Op: 13}, {Op: 12, A: 9}, {Op: 9}, {Op: 11, A: 6}, {Op: 1, A: 13}, {Op: 22}, {Op: 10}, {Op: 27, A: 117}, {Op: 9}, {Op: 11, A: 6}, {Op: 1, A: 14}, {Op: 22}, {Op: 28, A: 123}, {Op: 11, A: 9}, {Op: 1, A: 15}, {Op: 13}, {Op: 12, A: 9}, {Op: 26, A: 124}, {Op: 2}, {Op: 9}, {Op: 5}, {Op: 11}, {Op: 11, A: 9}, {Op: 30, A: 7, B: 2}, {Op: 60}, {Op: 10}, {Op: 59, A: 2}, {Op: 12, A: 10}, {Op: 9}, {Op: 12, A: 11}, {Op: 9}, {Op: 9}, {Op: 11, A: 10}, {Op: 25}, {Op: 28, A: 150}, {Op: 11, A: 4}, {Op: 11}, {Op: 11, A: 9}, {Op: 1, A: 3}, {Op: 8}, {Op: 30, A: 4, B: 1, Flags: 1}, {Op: 30, A: 5, B: 1}, {Op: 9}, {Op: 26, A: 216}, {Op: 26, A: 151}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 11, A: 9}, {Op: 11, A: 11}, {Op: 28, A: 160}, {Op: 11, A: 10}, {Op: 1, A: 15}, {Op: 14}, {Op: 26, A: 161}, {Op: 11, A: 10}, {Op: 8, A: 1}, {Op: 30, A: 4, B: 1, Flags: 1}, {Op: 12, A: 12}, {Op: 9}, {Op: 11, A: 12}, {Op: 1, A: 16}, {Op: 1, A: 17}, {Op: 30, A: 8, B: 2, Flags: 1}, {Op: 12, A: 12}, {Op: 9}, {Op: 11, A: 4}, {Op: 30, A: 9, Flags: 1}, {Op: 10}, {Op: 27, A: 179}, {Op: 9}, {Op: 11, A: 4}, {Op: 1, A: 18}, {Op: 30, A: 10, B: 1, Flags: 1}, {Op: 12, A: 13}, {Op: 9}, {Op: 5}, {Op: 11, A: 1}, {Op: 11, A: 4}, {Op: 30, A: 11, B: 2}, {Op: 9}, {Op: 1, A: 19}, {Op: 12, A: 4}, {Op: 9}, {Op: 33, A: 12}, {Op: 11, A: 8}, {Op: 11, A: 12}, {Op: 30, A: 13, Flags: 1}, {Op: 2}, {Op: 30, A: 14, B: 3, Flags: 1}, {Op: 12, A: 14}, {Op: 9}, {Op: 11, A: 1}, {Op: 11, A: 14}, {Op: 30, A: 5, B: 1}, {Op: 9}, {Op: 11, A: 10}, {Op: 1, A: 4}, {Op: 13}, {Op: 12, A: 2}, {Op: 9}, {Op: 5}, {Op: 11}, {Op: 11, A: 2}, {Op: 11, A: 11}, {Op: 11, A: 13}, {Op: 11, A: 1}, {Op: 30, A: 15, B: 5}, {Op: 12, A: 2}, {Op: 9}, {Op: 26, A: 22}, {Op: 2}, {Op: 9}, {Op: 5}, {Op: 11, A: 1}, {Op: 11, A: 4}, {Op: 30, A: 11, B: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 53}}, - Consts: []object.Value{ - object.Integer(0), - object.NewString(""), - object.NewString("<%"), - object.Integer(-1), - object.Integer(2), - object.NewString("%"), - object.NewString("<%"), - object.Integer(3), - object.NewString("="), - object.Symbol("expr"), - object.NewString("#"), - object.Symbol("comment"), - object.Symbol("code"), - object.NewString("="), - object.NewString("#"), - object.Integer(1), - object.NewString("%%>"), - object.NewString("%>"), - object.NewString("\n"), - object.NewString(""), - }, - Names: []string{"@percent", "expand_percent_lines", "length", "index", "[]", "<<", "===", "find_close", "gsub", "empty?", "end_with?", "flush_text", "Token", "dup", "new", "apply_trim"}, - Params: []string{"str"}, - NumRequired: 1, - SplatIndex: -1, - KwRestSlot: -1, - BlockSlot: -1, - NumLocals: 15, - Locals: []string{"str", "tokens", "i", "n", "pending_text", "open", "marker", "", "kind", "body_start", "close", "dash", "content", "starts_line", "tok"}, - Super: "", - }, - &bytecode.ISeq{ - Name: "flush_text", - Insns: []bytecode.Instr{{Op: 11, A: 1}, {Op: 30, Flags: 1}, {Op: 25}, {Op: 28, A: 13}, {Op: 11}, {Op: 33, A: 1}, {Op: 1}, {Op: 11, A: 1}, {Op: 30, A: 2, Flags: 1}, {Op: 2}, {Op: 30, A: 3, B: 3, Flags: 1}, {Op: 30, A: 4, B: 1}, {Op: 26, A: 14}, {Op: 2}, {Op: 53}}, - Consts: []object.Value{ - object.Symbol("text"), - }, - Names: []string{"empty?", "Token", "dup", "new", "<<"}, - Params: []string{"tokens", "text"}, - NumRequired: 2, - SplatIndex: -1, - KwRestSlot: -1, - BlockSlot: -1, - NumLocals: 2, - Locals: []string{"tokens", "text"}, - Super: "", - }, - &bytecode.ISeq{ - Name: "find_close", - Insns: []bytecode.Instr{{Op: 11, A: 1}, {Op: 12, A: 2}, {Op: 9}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 12, A: 3}, {Op: 9}, {Op: 11, A: 2}, {Op: 11, A: 3}, {Op: 18}, {Op: 28, A: 90}, {Op: 11}, {Op: 1}, {Op: 11, A: 2}, {Op: 30, A: 1, B: 2, Flags: 1}, {Op: 12, A: 4}, {Op: 9}, {Op: 11, A: 4}, {Op: 25}, {Op: 28, A: 25}, {Op: 2}, {Op: 4}, {Op: 6, A: 2}, {Op: 53}, {Op: 26, A: 26}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 11, A: 4}, {Op: 1, A: 1}, {Op: 13}, {Op: 30, A: 2, B: 1, Flags: 1}, {Op: 1, A: 2}, {Op: 22}, {Op: 10}, {Op: 28, A: 44}, {Op: 9}, {Op: 11}, {Op: 11, A: 4}, {Op: 1, A: 3}, {Op: 13}, {Op: 30, A: 2, B: 1, Flags: 1}, {Op: 1, A: 4}, {Op: 22}, {Op: 28, A: 52}, {Op: 11, A: 4}, {Op: 1, A: 5}, {Op: 13}, {Op: 12, A: 2}, {Op: 9}, {Op: 26, A: 7}, {Op: 26, A: 53}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 11, A: 4}, {Op: 1, A: 1}, {Op: 13}, {Op: 30, A: 2, B: 1, Flags: 1}, {Op: 1, A: 6}, {Op: 22}, {Op: 28, A: 82}, {Op: 11, A: 4}, {Op: 11, A: 1}, {Op: 19}, {Op: 10}, {Op: 28, A: 75}, {Op: 9}, {Op: 11}, {Op: 11, A: 4}, {Op: 1, A: 1}, {Op: 14}, {Op: 30, A: 2, B: 1, Flags: 1}, {Op: 1, A: 7}, {Op: 22}, {Op: 12, A: 5}, {Op: 9}, {Op: 11, A: 4}, {Op: 11, A: 5}, {Op: 6, A: 2}, {Op: 53}, {Op: 26, A: 83}, {Op: 2}, {Op: 9}, {Op: 11, A: 4}, {Op: 1, A: 1}, {Op: 13}, {Op: 12, A: 2}, {Op: 9}, {Op: 26, A: 7}, {Op: 2}, {Op: 9}, {Op: 2}, {Op: 4}, {Op: 6, A: 2}, {Op: 53}}, - Consts: []object.Value{ - object.NewString("%"), - object.Integer(1), - object.NewString("%"), - object.Integer(2), - object.NewString(">"), - object.Integer(3), - object.NewString(">"), - object.NewString("-"), - }, - Names: []string{"length", "index", "[]"}, - Params: []string{"str", "from"}, - NumRequired: 2, - SplatIndex: -1, - KwRestSlot: -1, - BlockSlot: -1, - NumLocals: 6, - Locals: []string{"str", "from", "i", "n", "pct", "dash"}, - Super: "", - }, - &bytecode.ISeq{ - Name: "apply_trim", - Insns: []bytecode.Instr{{Op: 11, A: 2}, {Op: 10}, {Op: 28, A: 5}, {Op: 9}, {Op: 31}, {Op: 10}, {Op: 28, A: 13}, {Op: 9}, {Op: 11}, {Op: 11, A: 1}, {Op: 30, A: 1, B: 1, Flags: 1}, {Op: 1}, {Op: 22}, {Op: 28, A: 19}, {Op: 11, A: 1}, {Op: 1, A: 1}, {Op: 13}, {Op: 53}, {Op: 26, A: 20}, {Op: 2}, {Op: 9}, {Op: 31, A: 2}, {Op: 10}, {Op: 27, A: 26}, {Op: 9}, {Op: 31, A: 3}, {Op: 10}, {Op: 28, A: 34}, {Op: 9}, {Op: 11}, {Op: 11, A: 1}, {Op: 30, A: 1, B: 1, Flags: 1}, {Op: 1, A: 2}, {Op: 22}, {Op: 28, A: 51}, {Op: 31, A: 3}, {Op: 28, A: 46}, {Op: 11, A: 3}, {Op: 28, A: 44}, {Op: 11, A: 1}, {Op: 1, A: 1}, {Op: 13}, {Op: 53}, {Op: 26, A: 45}, {Op: 2}, {Op: 26, A: 50}, {Op: 11, A: 1}, {Op: 1, A: 1}, {Op: 13}, {Op: 53}, {Op: 26, A: 52}, {Op: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 53}}, - Consts: []object.Value{ - object.NewString("\n"), - object.Integer(1), - object.NewString("\n"), - }, - Names: []string{"@trim_dash", "[]", "@trim_gt", "@trim_lgt"}, - Params: []string{"str", "i", "dash", "starts_line", "tokens"}, - NumRequired: 5, - SplatIndex: -1, - KwRestSlot: -1, - BlockSlot: -1, - NumLocals: 5, - Locals: []string{"str", "i", "dash", "starts_line", "tokens"}, - Super: "", - }, - &bytecode.ISeq{ - Name: "expand_percent_lines", - Insns: []bytecode.Instr{{Op: 1}, {Op: 12, A: 1}, {Op: 9}, {Op: 11}, {Op: 30, C: 1, Flags: 1}, {Op: 9}, {Op: 11, A: 1}, {Op: 53}}, - Consts: []object.Value{ - object.NewString(""), - }, - Names: []string{"each_line"}, - Params: []string{"str"}, - NumRequired: 1, - SplatIndex: -1, - KwRestSlot: -1, - BlockSlot: -1, - NumLocals: 2, - Locals: []string{"str", "out"}, - Children: []*bytecode.ISeq{ - &bytecode.ISeq{ - Name: "", - Insns: []bytecode.Instr{{Op: 11}, {Op: 1}, {Op: 30, B: 1, Flags: 1}, {Op: 28, A: 12}, {Op: 11, A: 1, B: 1}, {Op: 11}, {Op: 1, A: 1}, {Op: 1, A: 2}, {Op: 8}, {Op: 30, A: 1, B: 1, Flags: 1}, {Op: 30, A: 2, B: 1}, {Op: 26, A: 49}, {Op: 11}, {Op: 1, A: 3}, {Op: 30, B: 1, Flags: 1}, {Op: 28, A: 46}, {Op: 11}, {Op: 1, A: 4}, {Op: 30, A: 3, B: 1, Flags: 1}, {Op: 28, A: 26}, {Op: 11}, {Op: 1, A: 1}, {Op: 1, A: 2}, {Op: 8, A: 1}, {Op: 30, A: 1, B: 1, Flags: 1}, {Op: 26, A: 31}, {Op: 11}, {Op: 1, A: 1}, {Op: 1, A: 2}, {Op: 8}, {Op: 30, A: 1, B: 1, Flags: 1}, {Op: 12, A: 1}, {Op: 9}, {Op: 11, A: 1, B: 1}, {Op: 1, A: 5}, {Op: 1, A: 6}, {Op: 30, A: 4}, {Op: 13}, {Op: 11, A: 1}, {Op: 30, A: 4}, {Op: 13}, {Op: 1, A: 7}, {Op: 30, A: 4}, {Op: 13}, {Op: 30, A: 2, B: 1}, {Op: 26, A: 49}, {Op: 11, A: 1, B: 1}, {Op: 11}, {Op: 30, A: 2, B: 1}, {Op: 53}}, - Consts: []object.Value{ - object.NewString("%%"), - object.Integer(1), - object.Integer(-1), - object.NewString("%"), - object.NewString("\n"), - object.NewString(""), - object.NewString("<% "), - object.NewString(" %>"), - }, - Names: []string{"start_with?", "[]", "<<", "end_with?", "to_s"}, - Params: []string{"line"}, - NumRequired: 1, - SplatIndex: -1, - KwRestSlot: -1, - BlockSlot: -1, - NumLocals: 2, - Locals: []string{"line", "code"}, - Super: "", - }, - }, - Super: "", - }, - &bytecode.ISeq{ - Name: "quote", - Insns: []bytecode.Instr{{Op: 11}, {Op: 1}, {Op: 1, A: 1}, {Op: 30, B: 2, Flags: 1}, {Op: 1, A: 2}, {Op: 1, A: 3}, {Op: 30, B: 2, Flags: 1}, {Op: 1, A: 4}, {Op: 1, A: 5}, {Op: 30, B: 2, Flags: 1}, {Op: 1, A: 6}, {Op: 1, A: 7}, {Op: 30, B: 2, Flags: 1}, {Op: 1, A: 8}, {Op: 1, A: 9}, {Op: 30, B: 2, Flags: 1}, {Op: 12, A: 1}, {Op: 9}, {Op: 1, A: 10}, {Op: 1, A: 11}, {Op: 30, A: 1}, {Op: 13}, {Op: 11, A: 1}, {Op: 30, A: 1}, {Op: 13}, {Op: 1, A: 12}, {Op: 30, A: 1}, {Op: 13}, {Op: 53}}, - Consts: []object.Value{ - object.NewString("\\"), - object.NewString("\\\\\\\\"), - object.NewString("\""), - object.NewString("\\\""), - object.NewString("\n"), - object.NewString("\\n"), - object.NewString("\r"), - object.NewString("\\r"), - object.NewString("\t"), - object.NewString("\\t"), - object.NewString(""), - object.NewString("\""), - object.NewString("\""), - }, - Names: []string{"gsub", "to_s"}, - Params: []string{"text"}, - NumRequired: 1, - SplatIndex: -1, - KwRestSlot: -1, - BlockSlot: -1, - NumLocals: 2, - Locals: []string{"text", "escaped"}, - Super: "", - }, - }, - Super: "", - }, - &bytecode.ISeq{ - Name: "", - Insns: []bytecode.Instr{{Op: 44}, {Op: 9}, {Op: 78, A: 1}, {Op: 9}, {Op: 44, A: 2, B: 1}, {Op: 9}, {Op: 78, A: 3, B: 2}, {Op: 9}, {Op: 5}, {Op: 1}, {Op: 1, A: 1}, {Op: 1, A: 2}, {Op: 1, A: 3}, {Op: 30, A: 4, B: 4}, {Op: 53}}, - Consts: []object.Value{ - object.Symbol("html_escape"), - object.Symbol("h"), - object.Symbol("url_encode"), - object.Symbol("u"), - }, - Names: []string{"html_escape", "h", "url_encode", "u", "module_function"}, - NumRequired: 0, - SplatIndex: -1, - KwRestSlot: -1, - BlockSlot: -1, - NumLocals: 0, - Children: []*bytecode.ISeq{ - &bytecode.ISeq{ - Name: "html_escape", - Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 68, A: 1, B: 2}, {Op: 33, A: 3}, {Op: 30, A: 4, B: 2, Flags: 1}, {Op: 53}}, - Names: []string{"to_s", "[&<>\"']", "", "HTML_ESCAPE", "gsub"}, - Params: []string{"s"}, - NumRequired: 1, - SplatIndex: -1, - KwRestSlot: -1, - BlockSlot: -1, - NumLocals: 1, - Locals: []string{"s"}, - Super: "", - }, - &bytecode.ISeq{ - Name: "url_encode", - Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 30, A: 1, Flags: 1}, {Op: 68, A: 2, B: 3}, {Op: 30, A: 4, B: 1, C: 1, Flags: 1}, {Op: 53}}, - Names: []string{"to_s", "b", "[^a-zA-Z0-9_\\-.~]", "", "gsub"}, - Params: []string{"s"}, - NumRequired: 1, - SplatIndex: -1, - KwRestSlot: -1, - BlockSlot: -1, - NumLocals: 1, - Locals: []string{"s"}, - Children: []*bytecode.ISeq{ - &bytecode.ISeq{ - Name: "", - Insns: []bytecode.Instr{{Op: 5}, {Op: 1}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 30, A: 1, B: 2}, {Op: 53}}, - Consts: []object.Value{ - object.NewString("%%%02X"), - }, - Names: []string{"ord", "format"}, - Params: []string{"c"}, - NumRequired: 1, - SplatIndex: -1, - KwRestSlot: -1, - BlockSlot: -1, - NumLocals: 1, - Locals: []string{"c"}, - Super: "", - }, - }, - Super: "", - }, - }, - Super: "", - }, &bytecode.ISeq{ Name: "initialize", - Insns: []bytecode.Instr{{Op: 63}, {Op: 27, A: 5}, {Op: 2}, {Op: 12, A: 1}, {Op: 9}, {Op: 63, A: 1}, {Op: 27, A: 10}, {Op: 1}, {Op: 12, A: 2}, {Op: 9}, {Op: 2}, {Op: 32}, {Op: 9}, {Op: 1, A: 1}, {Op: 32, A: 1}, {Op: 9}, {Op: 33, A: 2}, {Op: 11, A: 1}, {Op: 30, A: 3, B: 1, Flags: 1}, {Op: 12, A: 3}, {Op: 9}, {Op: 11, A: 3}, {Op: 11, A: 2}, {Op: 12, A: 4}, {Op: 30, A: 4, B: 1, Flags: 1}, {Op: 9}, {Op: 11, A: 4}, {Op: 9}, {Op: 11, A: 3}, {Op: 1, A: 2}, {Op: 1, A: 3}, {Op: 30, A: 5}, {Op: 13}, {Op: 11, A: 2}, {Op: 30, A: 5}, {Op: 13}, {Op: 1, A: 4}, {Op: 30, A: 5}, {Op: 13}, {Op: 12, A: 5}, {Op: 30, A: 6, B: 1, Flags: 1}, {Op: 9}, {Op: 11, A: 5}, {Op: 9}, {Op: 11, A: 3}, {Op: 1, A: 5}, {Op: 1, A: 6}, {Op: 30, A: 5}, {Op: 13}, {Op: 11, A: 2}, {Op: 30, A: 5}, {Op: 13}, {Op: 1, A: 7}, {Op: 30, A: 5}, {Op: 13}, {Op: 12, A: 6}, {Op: 30, A: 7, B: 1, Flags: 1}, {Op: 9}, {Op: 11, A: 6}, {Op: 9}, {Op: 11, A: 3}, {Op: 11}, {Op: 30, A: 8, B: 1, Flags: 1}, {Op: 60}, {Op: 10}, {Op: 59, A: 2}, {Op: 12, A: 7}, {Op: 9}, {Op: 12, A: 8}, {Op: 9}, {Op: 9}, {Op: 1, A: 8}, {Op: 32, A: 9}, {Op: 9}, {Op: 1, A: 9}, {Op: 1, A: 10}, {Op: 30, A: 5}, {Op: 13}, {Op: 11, A: 7}, {Op: 30, A: 5}, {Op: 13}, {Op: 1, A: 11}, {Op: 30, A: 5}, {Op: 13}, {Op: 11, A: 8}, {Op: 30, A: 5}, {Op: 13}, {Op: 1, A: 12}, {Op: 30, A: 5}, {Op: 13}, {Op: 32, A: 10}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 63}, {Op: 27, A: 5}, {Op: 2}, {Op: 12, A: 1}, {Op: 9}, {Op: 63, A: 1}, {Op: 27, A: 10}, {Op: 1}, {Op: 12, A: 2}, {Op: 9}, {Op: 2}, {Op: 32}, {Op: 9}, {Op: 1, A: 1}, {Op: 32, A: 1}, {Op: 9}, {Op: 1, A: 2}, {Op: 32, A: 2}, {Op: 9}, {Op: 5}, {Op: 11}, {Op: 11, A: 1}, {Op: 11, A: 2}, {Op: 30, A: 3, B: 3}, {Op: 32, A: 4}, {Op: 53}}, Consts: []object.Value{ object.NewString("_erbout"), object.Integer(0), - object.NewString(""), - object.NewString(""), - object.NewString(".<<"), - object.NewString(""), - object.NewString(""), - object.NewString(".<<"), object.NewString("UTF-8"), - object.NewString(""), - object.NewString(""), - object.NewString(""), - object.NewString(""), }, - Names: []string{"@filename", "@lineno", "Compiler", "new", "eoutvar=", "to_s", "put_cmd=", "insert_cmd=", "compile", "@encoding", "@src"}, + Names: []string{"@filename", "@lineno", "@encoding", "__compile", "@src"}, Params: []string{"str"}, NumRequired: 1, SplatIndex: -1, @@ -4801,8 +4459,8 @@ func embeddedPrelude() *bytecode.ISeq { KwRequired: []bool{false, false}, KwRestSlot: -1, BlockSlot: -1, - NumLocals: 9, - Locals: []string{"str", "trim_mode", "eoutvar", "compiler", "", "", "", "enc", "code"}, + NumLocals: 3, + Locals: []string{"str", "trim_mode", "eoutvar"}, Super: "", }, &bytecode.ISeq{