#! /usr/local/bin/ruby
# This is a snapshot of an old perl6-on-ruby implementation.
# See README.
# Uses emacs ruby and outline mode. setq at bottom colorizes heredoc code.
# Ruby 1.9.0 (or higher?) required (for real lambdas).
# Disclaimer - most of this code was intended to be thown away on a
#  timescale of a couple of weeks.  But the project terminated first.

###* RedSix

###** Dual boot, ruby/perl6.

 #(
$ruby_init_code = <<'_CODE'
  def caller_file_line(n=1)
    file,line = caller[n].split(/:/); line = line.to_i
    [file,line+1]
  end
  def source_ruby(s)
    file,line = caller_file_line
    s_len = s.split(/\n/).size
    startline = line # - s_len
    eval(s,nil,file,startline)
  end
#  def source_perl(*a) end
#  def source_perl_finish(*a) end
_CODE
eval($ruby_init_code)
 #)
source_ruby(<<'_END'
  require 'digest/md5'
  module Kernel; def chksum(s) Digest::MD5.hexdigest(s) end end
  $perlcode_to_eval = []
  def source_perl_compiled(sum,rubycode,perlcode)
    file,line = *caller_file_line
    eval(rubycode,$P.P6_binding,file,line)
    #File.open("deleteme_02","w"){|f|f.write perlcode.split(//).join("\n")}
    if chksum(perlcode) != sum
      print "# Perl source #{caller_file_line.join(':')} changed.  I will eval() it later.\n"
      #p sum; p perlcode.length,chksum(perlcode)
      linepl = line + rubycode.split(/\n/).size
      $perlcode_to_eval.push([perlcode,[file,linepl]])
    end
  end
  def source_perl(perlcode)
    fileline = caller_file_line
    perl_eval(perlcode,fileline)
  end
  def source_perl_finish
    $perlcode_to_eval.each{|(c,fileline)| perl_eval(c,fileline)}
  end
  def perl_eval(perlcode,fileline=nil)
    $P.eval6q(perlcode,fileline) if
      not (ENV['TEST_ONLY_PARSE'] && ENV['TEST_ONLY_PARSE'] != 0)
  end
_END
);
;
source_ruby(<<'_END'

$LogP = File.new("deletemeparse.rb","w")

###** Regex bootstrap

###*** Engine

require 'strscan'
class Engine
  attr_accessor :scanner,:choicepoints,:rest,:stack,:match,:grammar
  def initialize(scanner,pat,mat,grammar)
    @scanner = scanner
    @choicepoints = []
    @rest = pat.dup
    @stack = []
    @match = mat.skim_state_copy
    @grammar = grammar
  end
  def run
    backtracking = false
    failed = false
    top_cp = nil
    debug = nil #||true
    parse_debug = nil #||true
    $LogP.print "Engine IN\n" if debug
    while true
      if @rest.empty?
        $LogP.print "Engine OUT - Success\n" if debug
        return @match
      end
      top = @rest.pop
      $LogP.print("===============================\n",
            @rest[-1].inspect.slice(0..50),"\n",
            top.inspect.slice(0..150),"\n") if debug
      case top
      when :apat
      when Regexp
        if @scanner.scan(top) || (@scanner.eos? && "" =~ top)
          if nil || parse_debug #||true
            @stack.each{|e| $LogP.print(e,"/") if e.class == String};$LogP.print "\n"
            p1 = @scanner.pos
            p0 = p1 - 20; p0 = 0 if p0 < 0
            s0 = @scanner.string.slice(p0,p1-p0)
            s1 = @scanner.string.slice(p1,50)
            s0.gsub!(/\n/){|w|'\n'};s0.gsub!(/\t/){|w|'\t'}
            s1.gsub!(/\n/){|w|'\n'};s1.gsub!(/\t/){|w|'\t'}
            $LogP.print "\033[0;7;34m#{s0}\033[0m#{s1}\n"
          end
        else
          if nil || parse_debug #||true
            path = ''
            @stack.each{|e| path += (e + "/") if e.class == String}
            if path != ''
              $LogP.print "\033[0;31m#{path}\033[0m"
              $LogP.print " #{top.inspect}\n"
            end
          end
          failed = true
        end
        $LogP.print top.inspect,' ',(failed ? 'failed' : 'ok'),"\n" if debug
      when Array
        case top[0]
        when :alt
          if not backtracking
            n = 0
            if top[1].size > 1
              @choicepoints.push([@scanner.pos,(@rest+[top]),@stack,@match,n])
            end
            fail "assert" if top[1].size < 1
            @rest += top[1][n].reverse
          else
            n = top_cp[-1]
            n += 1
            fail "bug" if n >= top[1].size
            if n < (top[1].size - 1)
              @choicepoints.push([@scanner.pos,(@rest+[top]),@stack,@match,n])
            end
            if n < top[1].size
              @rest += top[1][n].reverse
            end
          end
        when :rep_bstar
          _op,p,left=*top; if left > 0 then @rest += [[:alt,[p+[[:rep_bstar,p,left-1]],[] ] ]] end
        when :rep_bstar_ng
          _op,p,left=*top; if left > 0 then @rest += [[:alt,[[],p+[[:rep_bstar_ng,p,left-1]] ] ]] end
        when :rep
          _op,p,min,max,ng=*top
          @rest += ((p * min)+(ng ? [[:rep_bstar_ng,p,max-min]] : [[:rep_bstar,p,max-min]])).reverse
        when :seq
          @rest += top[1].reverse
        when :match_open
          #$LogP.print "------open\n"
          _op,rul,nam=*top
          @match = Match.new_from(@scanner.string,@scanner.pos)
          @match.rule = rul
          @stack = @stack.dup
          @stack.push("#{nam}")
        when :match_close
          #$LogP.print "------close\n"
          fail("splat\n#{caller.join("\n")}") if !@match
          @match = @match.skim_state_copy.close!(@scanner.pos)
          @stack = @stack.dup
          fail "assert" if not @stack.pop.class == String
        when :match_push
          @stack = @stack.dup
          @stack.push(@match.skim_state_copy)
        when :match_pop
          _op,nam=*top
          @stack = @stack.dup
          prev = @stack.pop.skim_state_copy
          if nam
            val = @match.skim_state_copy
            nam = val.rule if nam == true; fail "assert" if not nam
            if prev
              crnt = prev.hash[nam]
              if crnt
                crnt = [crnt] if crnt.class != Array
                val = crnt+[val]
              end
            end
            prev.hash[nam] =  val if nam
          end
          @match = prev
        when :skimC
          _op,name,argl=*top
          argl ||= []
          r = @grammar.method(name).call
          out = r.skimC(*argl)
          if out then @rest += out.reverse else failed = true end
        when :skimCo
          _op,obj,argl=*top
          argl ||= []
          out = obj.skimC(*argl)
          if out then @rest += out.reverse else failed = true end
        when :full_skimCf
          _op,out=*top
          res = out.skimCf(top,@scanner,@rest,@stack,@match,@choicepoints)
          if res then top,@scanner,@rest,@stack,@match,@choicepoints = *res else failed = true end
        when :proc_full
          _op,out=*top
          res = out.call(top,@scanner,@rest,@stack,@match,@choicepoints)
          if res then top,@scanner,@rest,@stack,@match,@choicepoints = *res else failed = true end
        else fail "bug '#{top[0]}'" end  
      else fail "bug >#{top}<" end
      backtracking=false
      $LogP.print "Pass #{failed ? 'failed' : 'worked'}\n" if debug
      $LogP.print "Choicepoints #{choicepoints.empty? ? 'empty' : @choicepoints.size}\n" if debug
      if failed
        if @choicepoints.empty?
          $LogP.print "Engine OUT - Failure\n" if debug
          return nil
        end
        top_cp = @choicepoints.pop
        @scanner.pos,@rest,@stack,@match = *top_cp
        $LogP.print "Backtracking to pos ",@scanner.pos," cp ",@choicepoints.size,"\n  ",top_cp.inspect,"\n  ",@rest[-1].inspect,"\n" if debug
        backtracking=true
        failed=false
      end
    end
  end
end

###*** Match

module MatchDescribe
  def match_describe(seen=nil)
    seen ||= {}
    return ("LOOP***"+match_describe_name) if seen.member?(self.object_id); seen[self.object_id] = true
    indent            = ->(s){ s ? s.gsub(/(?m)^(?!\Z)/,'  ') : '*nil*' }
    indent_except_top = ->(s){ s ? s.gsub(/(?m)^(?!\Z)/,'  ').sub(/^  /,'') : '*nil*' }
    n = match_describe_name
    b = as_b ? 'true' : 'false'
    s = "'"+indent_except_top.call(as_s).gsub(/([\\'])/){|w|"\\#{w}"}+"'"
    a = as_a.map{|m| "\n"+indent.call(m.match_describe(seen))+"," }.join("")
    a += "\n" if a != ""
    h = as_h.map{|k,v|
      vs = if v.instance_of?(Array)
             "[\n" + indent.call(v.map{|m|m.match_describe(seen)}.join(",\n"))+"\n]"
           else
             v.match_describe(seen)
           end
      "\n  #{k} => #{indent_except_top.call(vs)},"
    }.join("")
    h += "\n" if h != ""
    f = match_beg
    t = match_end
    "#{n}<#{b},#{s},#{f}-#{t},[#{a}],{#{h}}>"
  end
  def match_describe_name()
    "#{self.class}:#{object_id.to_s(36)}"
  end
end
class Match
  attr_accessor :on_str,:from,:to,:bool,:hash
  attr_writer :str
  attr_accessor :rule
  def initialize(on_str,from,to,bool,hash,str,rule=nil)
    @on_str,@from,@to,@bool,@hash,@str,@rule=on_str,from,to,bool,hash,str,rule
  end
  def self.new_from(on_str,from=0,to=nil)
    self.new(on_str,from,to,true,{},nil)
  end
  def skim_state_copy
    self.class.new(@on_str,@from,@to,@bool,@hash.skim_state_copy,@str,@rule)
  end
  def str(to=nil)
    @str || @on_str.slice(@from,(@to||to)-@from)
  end
  def close!(to)
    if @from # otherwise already .failed!
      @to = to
      @str = @on_str.slice(@from,@to-@from) if @from
    end
    self
  end
  def failed!
    @bool,@str,@hash,@from,@to = false,'',{},nil,nil
    self
  end
  def closer_proc
    SkimProc.new{|s,c| self.close!(s.pos); c.cont0(s)}
  end

  include MatchDescribe
  def as_b; @bool end; def as_s; @str end; def as_a; [] end; def as_h; @hash end
  def match_beg; @from end; def match_end; @to end
  def match_describe_name; "#{super}:#{rule ? rule.name : 'nil'}" end
  def inspect; match_describe end
end

class Object
  def skim_state_copy; self end
end
class Hash
  def skim_state_copy; cp = self.class.new; self.each{|k,v| cp[k]=v.skim_state_copy}; cp end
end
class Array
  def skim_state_copy; cp = dup; cp.map!{|e|e.skim_state_copy}; cp end
end

###*** Rule

class Rule
  attr_accessor :name,:raw_patA
  def initialize(*args) @name,@raw_patA=*args end
  def search(str,pos=0,can_skip=true)
    (pos..str.length).each{|p|
      scanner = StringScanner.new(str)
      scanner.pos = pos
      ev = Engine.new(scanner,[:apat,[:skimCo,self]],nil,$grammar).run
      return ev if ev && ev.bool
      break if !can_skip
    }
    return Match.new_from(str,pos).failed!
  end
  def skimC
    [:apat,[:seq,[
     [:match_open,self,@name],
     [:seq,@raw_patA],
     [:match_close]
    ]]]
  end    
end
class String
  def search(rule,pos=0,can_skip=true) rule.search(self,pos,can_skip) end
end

###*** OperatorPrecedenceParser

require 'digest/md5'

class OperatorPrecedenceParser
  attr_accessor :tokens,:ws
  def initialize(tokens,ws) @tokens,@ws=tokens,ws end
  def skimC(token_filter=nil)
    [:apat,[:full_skimCf,OperatorPrecedenceParser::Parsing.new(@tokens,@ws,token_filter)]]
  end
end
class OperatorPrecedenceParser::Parsing
  @@depth = 0
  def log(msg)
    if nil || true
      sp = "    " * @@depth
      id = Digest::MD5.hexdigest(object_id.to_s).slice(0,6)
      $LogP.print "#{sp}OPP #{id} #{msg}\n"
    end
  end
  class Edge;end
  BOS = Edge.new
  EOS = Edge.new
  attr_accessor :tokens,:ws,:s,:token_filter
  def initialize(tokens,ws,token_filter=nil)
    @tokens,@ws,@token_filter=tokens,ws,token_filter
  end
  def skimCf(*args)
    @@depth += 1
    scanner=args[1]
    @s=scanner
    ok = parse
    if ok
      log "OPP-PARSE succeeded\n"
      @@depth -= 1
      args[4] = ok
      args
    else 
      log "OPP-PARSE failed\n"
      @@depth -= 1
      false
    end
  end
  attr_accessor :saw_ws
  def eat_ws
    m = @ws.search(@s.string,@s.pos,false)
    if m.bool
      @s.pos = m.to
      m
    else false end
  end
  def lexeme_compatible_with_context?(l,prepost,ws)
    if ws != :dontcare
      lws = l.whitespace_on_left
      return false if not case lws
                          when nil; true
                          when :prohibit; !ws
                          when :require; !!ws
                          when :ws_irrelevant; true
                          else fail("assert: #{lws}") end
    end
    if prepost != nil
      lexp = l.expression_on_left
      return false if not case lexp
                          when :need_expr
                            case prepost
                            when :pre; nil
                            when :post; true
                            when :post_commalike; l.is_commalike
                            else fail('assert') end
                          when :no_expr_needed; prepost == :pre
                          else fail("assert: #{lexp}") end
    end
    return true
  end
  attr_accessor :prepost
  def lex(current_lexemes)
    log "LEX"; log "Lex'ing at #{@s.pos} >#{@s.string.slice(@s.pos,20)}"
    @saw_ws = eat_ws
    log "Lex saw ws" if @saw_ws
    log "Lex context: #{@prepost} #{@saw_ws ? 'ws' : 'nows'}"
    matches = []
    ss = StringScanner.new(@s.string); ss.pos = @s.pos
#    log "BEGIN ================================="
    current_lexemes.each{|l|
      key = l.competition_key
#      log "x# #{key.inspect} \t#{l.name}"
      len = nil
      if key && key.is_a?(Regexp)
        x = (ss.scan(key) || (ss.eos? && "".match(key) && ""))
#  log l.name.inspect+' '+(x ? 'OK' : "FAILED #{key.inspect}")+""
        next if not x
        ss.pos = @s.pos
        len = x.length
      end
      next if not lexeme_compatible_with_context?(l,nil,@saw_ws)
      m = l.search(@s.string,@s.pos,false)
      next if not m.bool
      len = m.hash[key].to - m.from if !len && key.is_a?(Symbol)
      len = m.to - m.from if !len
      matches.push([m,l,len])
    }
    log "Lex #{matches.size} candidates"
    ms = matches.sort{|a,b| b[2] <=> a[2]}
    if not ms.empty?
      at = ms[0][2]
      log "Lex target #{at}"
#      p "=================== ",at,ms[0][0].inspect,'-----'
      ms = ms.find_all{|a|
        log "...#{a[2]}   #{a[1].name}"
        at == a[2] and lexeme_compatible_with_context?(a[1],@prepost,:dontcare)
      }
    end
    log "Lex #{ms.size} matches"
    if ms.size > 1 then
      STDERR.print "\nWARNING: ambiguous parse: #{ms[0][0].as_s}\n"
      ms.each{|a| STDERR.print "  #{a[1].name}\n"}
    end
    m = ms.empty? ? nil : ms[0][0]
    log "Lex RESULT: #{m.inspect}"
    return nil if not m
    @s.pos = m.to
    log "Lex now at #{@s.pos}"
    @saw_ws = false
    m
  end
  attr_accessor :ops,:opands,:ends,:ends_sz
  attr_accessor :tok
  attr_accessor :lexemes
  def init
    @lexemes = @tokens.map{|t|t.first_lexeme}
    @lexemes_at_top_level = @lexemes
    if @token_filter
      top_level_tokens = @tokens.find_all{|t| @token_filter.call(t)}
      @lexemes_at_top_level = top_level_tokens.map{|t|t.first_lexeme}
    end
    @prepost = :pre
    @saw_ws = false

    @ops = [BOS]
    @opands = []
    @ends = []; @ends_sz = []
    @tok = nil
  end
  def parse
    log "OPP-PARSE"
    init
    catch(:parse_fail) {parser_loop}
  end
  def log_state
    log "Currently:\n\topands: #{@opands.inspect}\n\tops: #{@ops.inspect}\n\ttok: #{@tok.inspect}"
  end
  def parser_loop
    while true
      log "Loop"
      get_token
      log_state if nil #||true
      if @tok == EOS
        break
      end
      log @tok.rule.inspect if nil #||true
      if not @tok.rule.token.is_operator?
        operand
      elsif @ops.size == 1
        shift
      elsif @tok.rule == @ends[-1]
        crunch
      elsif (@ops[-1].rule.expression_on_right == :no_expr_needed &&
             !@ops[-1].rule.next_lexeme)
        reduce
      elsif @tok.rule.expression_on_left == :no_expr_needed
        shift
      else
        st = @ops[-1].rule.precedence_on_right
        tk = @tok.rule.precedence_on_left
        if    st < tk; shift
        elsif st > tk; reduce
        elsif st == 0 and tk == 0
          shift # eg, prelist with circumfix
        elsif @tok.rule.associativity == :right
          shift
        else
          reduce
        end
      end
    end
    log "EOS processing"
    reduce while @ops.size > 1
    error if not (@ops == [BOS] and @opands.size == 1 and @ends == [] and @tok == EOS)
    @s.pos = @saw_ws.from if @saw_ws
    @opands[0]
  end
  def get_token
    return if @tok
    close = @ends[-1]
    current_lexemes = close ? @lexemes + [close] : @lexemes_at_top_level
    @tok = lex(current_lexemes) || EOS
  end
  def operand
    log "operand"
    m = @tok; @tok = nil
    @opands.push(m)
    currently_argument_list_top_level = @token_filter && !@ends[-1]
    @prepost = m.rule.token.operand_post
    @prepost = :post if (@prepost == :post_commalike &&
                         !currently_argument_list_top_level)
  end
  def shift
    log "shift"
    m = @tok; @tok = nil
    @ops.push(m)
    eme = m.rule.next_lexeme
    if eme
      @ends.push(eme)
      @ends_sz.push(@ops.size)
    end
    @prepost = prepost_from_rule(@ops[-1].rule)
  end
  def reduce
    log "reduce"
    m0 = @ops.pop
    t = m0.rule.token
    count = t.part_count - 1 # all but m0 are on @opands
    if @opands.size < count
      log "reduce problem - not enough opands (#{@opands.size} vs #{count})"
      log @opands.inspect
      error 
    end
    parts = @opands.slice!(0-count,count)
    m = t.create_match([m0,*parts])
    @opands.push(m)
    @prepost = prepost_from_rule(@ops[-1].rule) if @ops[-1] != BOS
  end
  def crunch
    log "crunch"
    fail('assert') if @tok.rule != @ends[-1]
    m = @tok
    t = m.rule.token
    reduce while @ops.size > @ends_sz[-1]
    fail('assert') if @ops[-1].rule.token != @ends[-1].token
    @ends.pop; @ends_sz.pop
    @opands.push(@ops.pop)
    shift
  end
  def prepost_from_rule(r)
    r.expression_on_right == :need_expr ? :pre : :post
  end
  def error
    log "ERROR"
    log " op  #{@ops[-1].inspect}"
    log " tok #{@tok.inspect}"
    throw :parse_fail,nil
  end
end

###**** Tokens

class Lexeme < Rule
  attr_accessor :token
  attr_accessor :competition_key
  attr_accessor :whitespace_on_left
  attr_accessor :expression_on_left,:expression_on_right
  attr_accessor :precedence_on_left,:precedence_on_right
  attr_accessor :associativity
  attr_accessor :next_lexeme
  attr_accessor :is_commalike
  def initialize(name,f,len=nil)
    super(name,f)
    @competition_key = len
  end
end
class Token_Operand
  attr_accessor :first_lexeme
  attr_accessor :operand_post
  def is_operator?; false end
  def initialize(name,f,len=nil)
    @first_lexeme = l = Lexeme.new(name,f,len)
    l.token = self
    l.whitespace_on_left = :ws_irrelevant
    l.expression_on_left = :no_expr_needed
    l.expression_on_right = :no_expr_needed
    l.is_commalike = false
    @operand_post = :post
  end
end
class Token_Operator
  attr_accessor :fixity,:strings,:precedence,:assoc,:ws_policy
  attr_accessor :arity,:part_count
  attr_accessor :name
  attr_accessor :first_lexeme
  attr_accessor :fixity_extra
  def is_operator?; true end
  def initialize(*args)
    @name,@fixity,@fixity_extra,@strings,@precedence,@assoc,@ws_policy,@is_commalike=*args
    @fixity=@fixity.to_sym
    @precedence ||= @@precgen.default_precedence
    @name = @name || "#{@fixity}:#{@strings.join(' ')}"
    # build lexemes
    lexemes = [];
    regexps = @strings.each_with_index{|str_or_re,i|
      re=nil
      native_re = nil
      if str_or_re.is_a?(String)
        pat = str_or_re.gsub(/(\W)/){|w|"\\#{w}"};
        native_re = /#{pat}/
        re = [:apat,/#{pat}/]
      else
        re = str_or_re
      end
      lexemes.push(Lexeme.new("#{name}:#{i}",re,native_re))
    }
    # ... and connect them
    @first_lexeme = lexemes[0]
    lexemes[0].next_lexeme = lexemes[1] || nil
    lexemes[1].next_lexeme = nil if lexemes[1]
    # ... and set defaults
    lexemes.each{|l|
      l.token = self
      l.whitespace_on_left = nil
      l.associativity = @assoc
      l.is_commalike = @is_commalike
    }
    lexemes[0].whitespace_on_left = @ws_policy
    # finish up
    set = ->(n,el,pl,er,pr){
      lexemes[n].expression_on_left = el ? :need_expr : :no_expr_needed
      lexemes[n].expression_on_right = er ? :need_expr : :no_expr_needed
      lexemes[n].precedence_on_left = pl ? @precedence : 0
      lexemes[n].precedence_on_right = pr ? @precedence : 0
    }
    case @fixity
    when :prefix
      @arity = 1; @part_count = 2
      set[0, nil,nil, :e,:p]
    when :postfix
      @arity = 1; @part_count = 2
      set[0, :e,:p, nil,nil]
    when :circumfix
      @arity = 1; @part_count = 3
      set[0, nil,nil, :e,nil]
      set[1,:e,nil, nil,nil]
    when :infix
      @arity = 2; @part_count = 3
      set[0, :e,:p, :e,:p]
    when :ternary
      @arity = 3; @part_count = 5
      set[0, :e,:p, :e,nil]
      set[1, :e,nil, :e,:p]
    when :postcircumfix
      @arity = 2; @part_count = 4
      set[0, :e,:p, :e,nil]
      set[1, :e,nil, nil,nil]
    else fail "assert #{@fixity}"
    end
    lexemes[-1].precedence_on_right = 0 if @fixity_extra == :list
  end

  def create_match(parts)
$LogP.print parts.inspect
    parts.sort!{|a,b| a.from <=> b.from }
    m = Match.new_from(parts[0].on_str)
    m.rule = self
    m.hash[:parts] = parts
    m.from = parts[0].from
    m.close!(parts[-1].to)
    m
  end

  class PrecedenceGenerator
    attr :delta
    def initialize() @delta = 0.5 end
    def consume_a_delta() delta = @delta; @delta /= 2; delta end
    def default_precedence() 1 end
    def new_precedence_tighter_than(prec) prec + consume_a_delta end
    def new_precedence_looser_than (prec) prec - consume_a_delta end
  end
  @@precgen = PrecedenceGenerator.new
  def tighter; @@precgen.new_precedence_tighter_than(@precedence) end
  def looser;  @@precgen.new_precedence_looser_than(@precedence) end
  def equiv;   @precedence end
end

###** Six

###*** Grammar

class Grammar
  def self.abbrev(arg) arg.map{|a|a.is_a?(Symbol) ? sr(a) : a.is_a?(String) ? sym(a) : a.is_a?(Regexp) ? [:apat,a] : a } end
  def self.mangle(s) s.to_s.gsub(/z/,'zz').gsub(/([^a-z0-9])/i){|w|sprintf("z%0Xz",w[0])} end

  def self.alt(*specs) [:apat,[:alt,abbrev(specs)]] end
  def self.seq(*spec) p=[:apat];abbrev(spec).map{|e|p.push(*e)}; p end
  def self.rep(r,min=0,max=2**24,ng=nil) [:apat,[:rep,abbrev([r])[0],min,max,ng]] end
  def self.star(*args) r = args.size > 1 ? seq(*args) : args[0]; rep(r,0) end
  def self.plus(*args) r = args.size > 1 ? seq(*args) : args[0]; rep(r,1) end
  def self.ques(*args) r = args.size > 1 ? seq(*args) : args[0]; rep(r,0,1) end
  def self.sr(rn,bindname=nil,argl=[])
    bindname = rn if bindname == nil # but not false
    [:apat,[:seq,[
     [:match_push],
     [:skimC,rn,argl],
     [:match_pop,bindname]
    ]]]
  end
  def self.sr?(rn,argl=[]) sr(rn,false,argl) end
  def self.sr!(rn,argl=[])
    fail "sr! unimplemented"
  end
  def self.sym(x)
    qx = x.class == Regexp ? "#{x}" : x.gsub(/(\W)/){|w|"\\#{w}"}
    qx += '\b' if qx =~ /\w\z/
    seq(ques(sr?(:ws)),ncap([/#{qx}/],:symbol),ques(sr?(:ws)))
  end
  def self.ncap(spec,bindname)
    [:apat,[:seq,[
     [:match_push],
     [:match_open,nil,:fake_symbol_rule],
     [:seq,seq(spec)],
     [:match_close],
     [:match_pop,bindname]
    ]]]
  end

  def self.defineA(name,f)
    vname = name.to_s.gsub(/\?/,'Q').gsub(/\!/,'B')
    eval "def #{name}; end" # essential magic (else action-at-a-distance bugs)
    eval %{ define_method(name) { @#{vname} ||= Rule.new(name,f) } },nil,"line #{__LINE__}, '#{name}'"
#    define_method(name) { Rule.new(name,f) }
  end
  def self.defineB(name,r)
    vname = name.to_s.gsub(/\?/,'Q').gsub(/\!/,'B')
    eval "def #{name}; end" # precautionary magic
    eval %{ define_method(name) { @#{vname} ||= r } },nil,"line #{__LINE__}, '#{name}'"
  end

  def self.named(name,*spec)
#     print "#{name} => ",seq(*spec).inspect,"\n\n"
    defineA(name,seq(*spec))
  end
  def self.term(name,len,*spec)
    defineB("tokens__#{name}",Token_Operand.new(name,seq(*spec),len))
  end
  def self.nterm(name,len,*spec)
    named(name,*spec)
    term(name,len,*spec)
  end
  def self.def_operator(fix,oppat,opdef)
    mangled = mangle(oppat.gsub(/\s+/,'_'))
    defineB("tokens__#{fix}_#{mangled}",opdef)
  end
  def self.option(n1,n2,*spec)
    named("#{n1}__#{n2}",*spec)
  end
end

class SixGrammar < Grammar
  def method_missing(method, *args)
    print "FAKING #{method}\n"
    Rule.new(method,[:apat,/(?!)/])
  end
  def method(method,*args)
    return super if self.class.method_defined?(method)
    print "FAKING #{method}\n"
    proc{Rule.new(method,[:apat,/(?!)/])}
  end

  def self.trait_name(trait)
    @trait_name_cache ||= {}
    @trait_name_cache[trait] ||= seq('is',trait,:qualified_identifier)
  end
  def self.bare_trait(trait)
    @bare_trait_cache ||= {}
    @bare_trait_cache[trait] ||= alt(trait_name(trait),
                                     seq(trait,:qualified_identifier,
                                         star(:ws,'of',:qualified_identifier)))
  end
  def self.comma_list(r)
    seq(r,star(',',r))
  end
  def self.reassembled_delimited_identifier(delim)
    seq(/\w+/,star(delim,/\w+/))
  end
  def self.maybeParens(*spec)
    r = seq(*spec)
    alt(seq('(',r,')'),r)
  end
  def self.verbatimBlah(o,r,c) seq(/#{"\\"+o}/,r,:ws?,/#{"\\"+c}/) end
  def self.verbatimBraces(r) verbatimBlah('{',r,'}') end
  def self.verbatimBrackets(r) verbatimBlah('[',r,']') end
  def self.verbatimParens(r) verbatimBlah('(',r,')') end


  # def ws; @ws ||= Rule.new('ws',SixGrammar.plus(SixGrammar.alt(*(methods.grep(/^ws__/).map{|m| self.class.sr(m)})))) end
  # option :ws,:simple,/\s+/
  # option :ws,:comment,/\#[^\n]*/
  # option :ws,:doc_block,/^=\w[^\n]*(?:\n(?!=cut)(?>[^\n]*))*/,alt(/\z/,seq(/\n=cut/,:whiteSpaceLine))
  # def ws!; ws end
  # named :ws?,ques(:ws)
  _paren = /\(\)/
  _paren = /(?:[^()]|\(#{_paren}\))*/
  _paren = /(?:[^()]|\(#{_paren}\))*/
  _paren = /(?:[^()]|\(#{_paren}\))*/
    ws_re = /(?:(?>\s+)|(?>(?<!\A|\n)\#\(#{_paren}\))|(?>\#[^\n]*)|^=\w(?>[^\n]*)(?>(?:\n(?!=cut)(?>[^\n]*))*)(?:\z|\n=cut[\ \t\r]*(?:\n|\z)))+/
  named :ws, ws_re
  named :ws!, ws_re
  named :ws?, /#{ws_re}?/
  named :before_ws, /(?=#{ws_re})/
  def tokens; @tokens ||= methods.grep(/^tokens__/).map{|m|method(m).call} end
  def expr; @expr ||= Rule.new('expr',[:apat,[:seq,[
                                       [:match_push],
                                       [:skimCo,OperatorPrecedenceParser.new(tokens,ws)],
                                       [:match_pop,:opp]
                                      ]]]) end
  def expr_opp_bare
    @expr_opp_bare ||= OperatorPrecedenceParser.new(tokens,ws)
  end
  named :expr_above_comma, sr(:expr_opp_bare,nil,[->(t){ if !t.is_operator? then true else t.precedence > $level__comma.precedence end}])

  #===========================================================================
  #-- Whitespace -------------------------------------------------------------

  named :whiteSpaceLine,/(?:(?!\n)\s)*$/

#  def self.colon_comma_list(r) seq(r,star(/,:/,r)) end
#  named :argument_list,colon_comma_list(:expr)
  named :argument_list,:expr
  optional_argument_list = ques(:argument_list)
  optional_expr_above_comma = ques(:expr_above_comma)
  acx = optional_argument_list
  ac1 = seq(:expr_above_comma)
  acq = optional_expr_above_comma
  ac0 = seq()
  term :apply_sub_notFolded,(:sub_name_bare),:sub_name_bare,/(?!\()/,
  [[:proc_full, ->(*args){
        rest = args[2] = args[2].dup
        m = args[4]
        n = m.hash[:sub_name_bare] || fail("assert")
        n = n.as_s.to_sym
        # Completely kludge arity, which has become critical. :/
        #ac = {:f1=>1,:fq=>:q,:f0=>0}[n]
        ac = {:ref=>1,:pi=>0,:shift=>1,:pop=>1,:try=>1}[n]
        case ac
        when nil; rest.push(*acx)
        when :s;  rest.push(*acx)
        when 1;   rest.push(*ac1)
        when :q;  rest.push(*acq)
        when 0;   rest.push(*ac0)
        else fail("assert") end
        args
      }]]
  term  :sub_name_term,(nil), :sub_name_bare,/(?=\()/

  named :method_args,ques(alt(seq(/:/,:argument_list),
                              seq(/\(/,optional_argument_list,:ws?,/\)/)))

  named :implicit_invocant,/(?=\.)/
  term :apply_method_term,(nil),:implicit_invocant,/\./,:qualified_identifier,:method_args

  #-- Declarations ------------------------------------------------
  named :scope, alt('state','my','our','let','temp','env')
  named :type,/(?:\w|::)[\w:&|?]*/  # related to :type_var and/or :type_literal?
  named :trait, alt('is','does'),ques(/::/),:qualified_identifier,ques(/\([^\)]*\)/)
  named :block,:ws?,/\{/,:statement_list,:ws?,/\}/
#  named :standalone_block,:block,:whiteSpaceLine
#  term  :code_block,(/\{/),:block
  term  :code_block,(/\{/),:block,ques(:whiteSpaceLine)
  #
  named :sub_declaration, seq(ques(:scope,ques(:identifier)),:sub_head,
                              ques('handles',:expr),
                              ques(bare_trait('returns')),
                              ques(:sub_parameters__ParensMandatory),
                              ques(bare_trait('returns')),
                              star(:trait),
                              :block)
  named :sub_head,alt(seq(ques('multi'),alt('sub','coro','submethod','method','macro')),'multi'),:sub_name_no_amp
  named :sub_parameters__ParensMandatory,'(',ques(comma_list(:formal_param)),')'
  named :sub_parameters__ParensOptional,ques(comma_list(:formal_param))
  named :param_name, alt(seq(/\&/,:sub_name_no_amp),seq(alt(/[\$\@\%]/,/::/),:twigil_opt,/\w+/))
  named :formal_param, seq(ques(:type,:ws),ques(/\\/),
                           ques(:param_foretaste),
                           :param_name,
                           ques(:param_optness),
                           star(:trait),
                           ques(:param_default),
                           ques('-->',:param_list__ParensOptional,alt(:formal_param,:type)))
  named :param_default, '=',:expr_above_comma
  named :param_foretaste, /\:|\*/
  named :param_optness, /\?|\!/
  
  #
  named :trusts_declaration, 'trusts', :qualified_identifier
  named :trait_declaration, :trait, :ws,/\z|(?=[;}])/
  named :member_declaration, seq('has',ques(:qualified_identifier),:var_name,
                                 star(:trait),ques('handles',:expr)),ques('=',:expr)
  #
  named :rule_declaration,'rule',:identifier,:adverb_hash,sr(:balanced,nil,['{'])
  #   rx_literal($<adverb_hash>,'{')
  #
  named :var_declaration, seq(:scope,ques(:qualified_identifier),
                              alt(:var_name,
                                  seq(/\(/,comma_list(alt(:var_name,:undef_literal)),/\)/)),
                              star(:trait),
                              ques(alt('=','.=',':=','::='), :ws?,
                                   alt(:var_declaration,:expr)))
  #
  named :package_block_declaration, :package_head,:block
  named :package_declaration, :package_head
  named :package_head, ques(:scope),alt('package','module','class','role','grammar'),:qualified_identifier,ques(:version_part,ques(:author_part)),:ws,star(:trait)
  #
  named :no_declaration, 'no',alt(:no_version,:use_package)
  named :use_declaration, 'use',alt(:use_version,:use_package)
  named :perl_version, /(?:v|Perl-)[\d\.]+/,ques(:author_part)
  named :use_version, :perl_version
  named :no_version, :perl_version
  named :use_package, alt(seq(/jsan:(?!:)/,:use_JSAN_module),
                          seq(/jsperl5:(?!:)/,:use_JSPerl5_module),
                          seq(ques(:identifier,/:(?!:)/),:use_perl_package))
  named :use_perl_package, :package_full_name,ques(alt(seq(/\(/,:ws,/\)/),
                                                       :expr))
  named :use_JSAN_module, seq(alt(:package_full_name,
                                  reassembled_delimited_identifier(".")),
                              alt(seq(/\(/,:ws,/\)/),
                                  ques(:expr)))
  named :use_JSPerl5_module,seq(alt(:package_full_name,
                                    reassembled_delimited_identifier("::")),
                                alt(seq(/\(/,:ws,/\)/),
                                    ques(:expr)))
  named :package_full_name, reassembled_delimited_identifier("::"),ques(:version_part),ques(:author_part)
  named :version_part, /-[\d\.\(\)]+/
  named :author_part, /-[\w\(\)]+/
  #
  named :inline_declaration, 'inline',:expr
  named :require_declaration, 'require',:package_full_name

  #-- Expressions / Terms ------------------------------------------------
  #   terms               42 "eek" $x /abc/ (1+2) a(1) :by(2) .meth listop

  term :fakestring,(/\'/), /\'(?:[^\'\\]|\\.)*\'/
  term :fakierstring,(/\"/), /\"(?:[^\"\\]|\\.)*\"/
  term :fakeEND,(/<<'_END'/), /<<'_END' *\n(.+?)\n_END *\n/m
  #
  named :dereference, /[\$\@\%\&]/,alt(:dereference,:sigiled_var,verbatimBraces(:expr))
  #
  nterm :angle_bracket_literal,(/<<?|\xab/),
  /<<(?:[^>\\]|\\.|>(?!>))*>>
  |<(?!<)(?:[^>\\]|\\.)*>
  |\xab(?:[^\xbb\\]|\\.)*\xbb/x
  named :hash_subscript_qw, :angle_bracket_literal
  named :hash_subscript_braces, /\{/,ques(:expr),/\}/
  named :hash_subscript, alt(:hash_subscript_braces,:hash_subscript_qw)
  fixity_re = /#{
'prefix: postfix: infix: circumfix: coerce: self: term:
 postcircumfix: rule_modifier: trait_verb: trait_auxiliary:
 scope_declarator: statement_control: infix_postfix_meta_operator:
 postfix_prefix_meta_operator: prefix_postfix_meta_operator:
 infix_circumfix_meta_operator:'.split(/\s+/).join("|")}/
  named :fixity, fixity_re
  named :operator_name, :fixity,alt(:identifier,:hash_subscript)
  named :sub_name_bare, /(?![A-Z])(?!(?:sub|coro|macro)\b)(?!(?:do)\b)/,alt(:operator_name,:qualified_identifier)
  named :sub_name_no_amp, :twigil_opt,alt(:operator_name,:qualified_identifier)
  named :sub_name_full, /\&/,:sub_name_no_amp
  named :identifier, /[a-z_]\w*/i
  named :qualified_identifier,/[a-z_]\w*(?:\:\:[a-z_]\w*)*/i
  named :twigil_opt,/[\^*?\.!+;]?/
  term :var_symbolic_deref,(/[\$\@\%\&]/), /[\$\@\%\&]/,plus(/::/,alt(/!|\//,seq(:twigil_opt,/\w+/)))
  nterm :var_sub,(nil), /\&/,:sub_name_no_amp # (nil) else prefix:& wins :/
  nterm :var_simple,(nil), /[\$\@\%]/,:twigil_opt,:qualified_identifier
  term :var_error,(/\$!/), /\$!/
  term :var_match_numbered,(/\$\d+/), /\$\d+/
  term :var_match_named,(/\$</), /\$<[^>]*>/
  term :var_match,(/\$\//), /\$\//
  named :var_name, alt(:var_sub,:var_simple)
  #
  #                       :apply__True,    #-- Folded metaoperators
  #
  term :do_block,(/do\b/), 'do',alt(:block, :statement)
  #
  nterm :block_formal_pointy,(/->/), /->/,:ws?,:sub_parameters__ParensOptional,star(:trait),:block
  nterm :block_formal_standard,(/(?:sub|coro|macro)\b/), sym(/sub|coro|macro/),ques(:sub_parameters__ParensMandatory),star(:trait),:block
  #
  fraction = /\.[\d_]+/
  expo = /[eE][-+]?\d+/
  number_re = /0(?:b[0-1]+|o[0-7]+|d[0-9]+|x[0-9a-fA-F]+)
              |[0-9][0-9_]*#{fraction}?#{expo}?
              |[-+]?(?:Inf|NaN)\b /x
  term :number,(number_re), number_re
  #
  term :empty_list_literal,(nil), verbatimParens(:ws?)
  # was /\(/, but that lost to postcircumfix_paren_empty(len 2),
  #  which needs to be 2 to compete with postcircumfix:( ):0(len 1).

#  term :empty_array_literal,(/\[/), verbatimBrackets(:ws?)
  #
  nterm :array_literal,(/\[/), verbatimBrackets(ques(:expr))
  #
  # :pair_literal is now unneeded -- alt(:pair_arrow,:pair_adverb)
  # :pair_arrow is now an op -- :identifier,'=>',:parseExpWithTightOps #infix:=>
  nterm :pair_adverb,(nil), /:/,alt(:shortcut_pair,:regular_pair)
  named :shortcut_pair, :var_name
  named :regular_pair_name, /\w+/
  named :regular_pair, :regular_pair_name,ques(alt(:valueDot,:noValue,:valueExp))
  named :valueDot, :ws,'.',ques(:valueExp)
  named :noValue, :ws
  named :valueExp, alt(verbatimParens(:expr),:array_literal,:angle_bracket_literal)
  #
  named :undef_literal, /undef\b/
  # 
  term :yada_literal,(/\.\.\.|\?\?\?|!!!/), alt('...','???','!!!')
  #
  #:q_literal,
  #
  named :adverb_hash,star(:pair_adverb)
  named :rx_pattern, seq(:ws?,alt(/\/(?:[^\/\\]|\\.)*\//,
                                  /\{(?:[^\}\\]|\\.)*\}/))
  # the \b_dot_ is to win against apply sub.
  term :rx_literal,(/(?:rx|m|rule)\b./), sym(/rx|m|rule/),:adverb_hash,:rx_pattern
  term :rx_literal_bare,(/\//), /(?=\/)/,:rx_pattern
  term :subst_literal,(/s\b/), 's',:adverb_hash,:rx_pattern,:q_literal1
  #
  #:nullary_literal,
  #:bareword_method,
  #
  nterm :closure_trait,(/(?:BEGIN|CHECK|INIT|FIRST|END)\b/), sym(/BEGIN|CHECK|INIT|FIRST|END/),:block # also stmt?!?
  #
  term :code_quotation,(/q:code/), /q:code/,ques('(:COMPILING)'),:block
  #
  term :type_var,(/::/), plus(/::/,alt(seq(/\(/,:expr,/\)/),
                                       seq(:twigil_opt,/\w+/)))
  #
  tl = /(?!Inf\b|NaN\b)(?!(?:BEGIN|CHECK|INIT|FIRST|END)\b)[A-Z]\w*(?:\:\:\w+)*/
  term :type_literal,(tl), tl
  #
  #:apply__False,   #-- Normal application
  #
  #term :parens,(/\(/), /\(/,ques(:expr),:ws?,/\)/
  ###

  #-- Constructs ------------------------------------------------

  named :for_construct,'for', maybeParens(:expr),ques(','),:expr
  named :loop_construct, 'loop',alt(:semi_loop_construct,:post_loop_construct)
  named :semi_loop_construct, maybeParens(ques(:expr),';',ques(:expr),';',ques(:expr)),:block
  named :post_loop_construct,:block,sym(/while|unitl/),:expr
  #
  named :cond_construct, sym(/if|unless/),:cond_body
  named :elsif_construct, 'elsif',:cond_body
  named :else_construct, 'else',:block
  named :cond_body, :cond_part,:block,ques(alt(:elsif_construct,:else_construct))
  named :cond_part, maybeParens(alt(:type_var,:type_literal,:expr))
  #
  named :while_until_construct,sym(/while|until/),:cond_part,:block
  named :given_construct, 'given',:cond_part,:block
  named :when_construct, 'when',:cond_part,:block
  named :default_construct, 'default',:block

  #-- :expr_statement ----------------------------------------

  named :expr_statement,:expr,ques(alt(:post_conditional,:post_loop,:post_iterate))
  named :post_conditional, sym(/if|unless/),:expr
  named :post_loop, sym(/while|until/),:expr
  named :post_iterate, 'for',:expr

  #-- Statements ----------------------------------------
  named :prog, :statement_list,:ws?
  named :statement_list,star(seq(:ws?,:statement,star(';')))

  def statement; @statement ||= Rule.new('statement',SixGrammar.alt(*(SixGrammar.statement_rules.map{|m| self.method(m).call.raw_patA}))) end
  def self.statement_rules; @statement_rules end
  def self.stmt(n)
    option :statement,n,n
    @statement_rules ||= []
    @statement_rules.push("statement__#{n}".to_sym)
  end
  # block_declaration
  stmt :sub_declaration
  stmt :closure_trait
  stmt :rule_declaration
  stmt :package_block_declaration
  # declaration
  stmt :package_declaration
  stmt :var_declaration
  stmt :member_declaration
  stmt :trait_declaration
  stmt :use_declaration
  stmt :no_declaration
  stmt :inline_declaration
  stmt :require_declaration
  stmt :trusts_declaration
  # construct
  stmt :for_construct
  stmt :loop_construct
  stmt :cond_construct
  stmt :while_until_construct
#  stmt :standalone_block
  stmt :given_construct
  stmt :when_construct
  stmt :default_construct
  # expr
  stmt :expr_statement

end

$grammar = SixGrammar.new

$grammar.tokens__code_block.operand_post = :post_commalike #

###*** Operator declarations

$level__last = nil
$level__eq = nil
$level__dot = nil
$level__comma = nil
def level(*args)
  fix,fixity_extra,assoc,ws_policy,commalike=nil
  level_equiv = nil
  args.each{|a|
    a = a.to_s
    case a
    when /\A(prefix|postfix|circumfix|infix|postcircumfix|ternary)\Z/; fix = $1
    when /\A(left|right|nonchain|chain)\Z/; assoc = $1.to_sym
    when /\A(list)\Z/; fixity_extra = $1.to_sym
    when /\A(nows)\Z/; ws_policy = :prohibit
    when /\A(needws)\Z/; ws_policy = :require
    when /\A(commalike)\Z/; commalike = true
    else
      a.split(/\s+/).each{|op|
        if fix =~ /\A(circumfix|postcircumfix|ternary)\Z/ then
          toks = op =~ /__/ ? op.split(/__/) : [op.slice(0..-2),op.slice(-1,1)]
        else
          toks = [op]
        end
        prec = (level_equiv ? level_equiv.precedence :
                $level__last ? $level__last.looser : nil)
        opdef = Token_Operator.new(nil,fix,fixity_extra,toks,prec,assoc,ws_policy,commalike)
        SixGrammar.def_operator(fix,op,opdef)
        if not level_equiv then
          level_equiv = opdef
          $level__last = opdef
          $level__eq = opdef if op == '='
          $level__dot = opdef if op == '.'
          $level__comma = opdef if op == ','
        end
      }
    end
  }
end
def define_assign_binops(opstr)
  prec = $level__eq.precedence
  opstr.split(/\s+/).each{|subop|
    op = "#{subop}="
    opdef = Token_Operator.new(nil,:infix,nil,[op],prec)
    SixGrammar.def_operator(:infix,op,opdef)
  }
end
def define_method_call
  op = SixGrammar.seq(/\./,:qualified_identifier,:method_args)
  prec = $level__dot.precedence
  opdef = Token_Operator.new(:apply_method.to_s,:postfix,nil,[op],prec)
  SixGrammar.def_operator(:postfix,'method_call',opdef)
end
def define_more_crud
  op = SixGrammar.seq(/\(/,:ws?,/\)/)
  prec = $level__dot.precedence
  opdef = Token_Operator.new(:postcircumfix_paren_empty.to_s,:postfix,nil,[op],prec)
  SixGrammar.def_operator(:postfix,'empty_paren_call',opdef)
end

# S03 - 7 Apr 2006
#   terms               42 "eek" $x /abc/ (1+2) a(1) :by(2) .meth listop
#   method postfix      . .+ .? .* .() .[] .{} .«» .=
#   autoincrement       ++ --
#   exponentiation      **
#   symbolic unary      ! + - ~ ? $ @ % & * ** +^ ~^ ?^ \ ^ =
#   multiplicative      * / % x xx +& +< +> ~& ~< ~>
#   additive            + - ~ +| +^ ~| ~^
#   junctive and (all)  &
#   junctive or (any)   | ^
#   named unary         rand sleep abs etc.
#   nonchaining binary  but does cmp <=> .. ^.. ..^ ^..^ ff ^ff ff^ ^ff^ fff ^fff etc.
#   chaining binary     != == < <= > >= ~~ !~ eq ne lt le gt ge =:= ===
#   tight and           &&
#   tight or            || ^^ //
#   ternary             ?? !!
#   assignment          = := ::= += -= **= xx= etc. (and also =>)
#   list item separator , ¥
#   list op (rightward) <== print push any all true not etc.
#   pipe forward        ==>
#   loose and           and
#   loose or            or xor err
#   expr terminator     ; {} as control block, statement modifiers

#   terms               42 "eek" $x /abc/ (1+2) a(1) :by(2) .meth listop
level :circumfix, '(__)'
#   method postfix      . .+ .? .* .() .[] .{} .«» .=  
# .=
level :nows,:infix, '. .+ .? .*', :postcircumfix, '.() .[] .{} .«» .<>',
'() [] {} «» <>' # bogus?
;
#   autoincrement       ++ --
level :prefix, '++ --', :postfix, '++ --';
#   exponentiation      **
level :infix, '**';
#   symbolic unary      ! + - ~ ? $ @ % & * ** +^ ~^ ?^ \ ^ =
level :prefix, '! + - ~ ? $ @ % & * ** +^ ~^ ?^ \ ^ =';
#   multiplicative      * / % x xx +& +< +> ~& ~< ~>
level :infix, :left, '* / % x xx +& +< +> ~& ~< ~>';
#   additive            + - ~ +| +^ ~| ~^
level :infix, :left, '+ - ~ +| +^ ~| ~^';
#   junctive and (all)  &
level :infix, :left, '&';
#   junctive or (any)   | ^
level :infix, :left, '| ^';
#   named unary         rand sleep abs etc.
#XXX -------
#   nonchaining binary  but does cmp <=> .. ^.. ..^ ^..^ ff ^ff ff^ ^ff^ fff ^fff etc.
level :infix, :nonchain, 'but does cmp .. ^.. ..^ ^..^ ff ^ff ff^ ^ff^ fff ^fff fff^ ^fff^',:needws,'<=>';
#   chaining binary     != == < <= > >= ~~ !~ eq ne lt le gt ge =:= ===
level :infix, :chain, '!= == ~~ !~ eq ne lt le gt ge =:= ===',:needws,'< <= > >=';
#   tight and           &&
level :infix, '&&';
#   tight or            || ^^ //
level :infix, '|| ^^ //';
#   ternary             ?? !!
level :ternary, '??__!!';
#   assignment          = := ::= += -= **= xx= etc. (and also =>)
level :infix, :right, '= := ::=',:commalike,'=>';
#   list item separator , ¥
level :infix, :commalike, ', ¥';
#   list op (rightward) <== print push any all true not etc.
level :infix, :list, :needws, '<=='; #X?
#   pipe forward        ==>
level :infix, '==>';
#   loose and           and
level :infix, 'and';
#   loose or            or xor err
level :infix, 'or xor err';
#   expr terminator     ; {} as control block, statement modifiers
#XXX -------
#   end of table

define_assign_binops '. * / % x xx +& +< +> ~& ~< ~>   + - ~ +| +^ ~| ~^   &   | ^   || ^^ //';
define_method_call
define_more_crud


###** Past

###*** PastFromParse

class PastFromParse
  @@map = {}
  def self.mangle(s) s.to_s.gsub(/z/,'zz').gsub(/([^a-z0-9])/i){|w|sprintf("z%0Xz",w[0])} end
  def self.node(name,p) @@map[mangle(name).to_sym] = p end
  node :rx_literal_bare,->(m){
    pat = m.hash[:rx_pattern].as_s
    mods = {}
    Past::Rx.new(m,mods,pat)
  }
  node :rx_literal,->(m){
    pat = m.hash[:rx_pattern].as_s
    mods = pair_list_to_hash(emit(m.hash[:adverb_hash]))
    Past::Rx.new(m,mods,pat)
  }
  node :adverb_hash,->(m){
    a = m.hash[:pair_adverb]
    return [] if not a
    a = [a] if not a === Array
    a.map{|p|emit(p)}
  }
  def self.pair_list_to_hash(pl)
    h = {}
    pl.each{|p|
      if p.is_a?(Past::PPair)
        h[p.name] = p.value
      elsif p.is_a?(Past::Infix) && p.fun.as_s == '=>'
        h[p.fargs[0]] = p.fargs[1]
      else fail("assert - pair_list_to_hash not given pair list") end
    }
    h
  end
  node :pair_adverb,->(m){
    rph = m.hash[:regular_pair].hash
    n = rph[:regular_pair_name].as_s
    v = ((rph[:valueExp] && (rph[:valueExp].hash[:expr] ||
                             rph[:valueExp].hash[:array_literal] ||
                             rph[:valueExp].hash[:angle_bracket_literal])
          ) ||
         nil)
    v = emit(v) if v
    if n =~ /^\d+\z/
      base = n.to_i
      num = case base
            when 16; eval(v.src.as_s).hex
            when 8; eval(v.src.as_s).oct
            else fail(":#{n}() unimplemented") end
      Past::Number.new(m,num)
    else
      Past::PPair.new(m,n,v)
    end
  }
  node :array_literal,->(m){
    l = m.hash[:expr] ? unpack_comma_list(m.hash[:expr].hash[:opp]) : []
    Past::ArrayLiteral.new(m,l)
  }
  node :type_literal,->(m){
    Past::TypeLiteral.new(m)
#    Past::String.new(m,m.as_s)
  }
  node :package_block_declaration,->(m){
    kind = m.hash[:package_head].hash[:symbol].as_s
    name = m.hash[:package_head].hash[:qualified_identifier].as_s
    body = emit(m.hash[:block])
    case kind
    when 'role'; Past::PRole.new(m,name,body)
    when 'class'; Past::PClass.new(m,name,body)
    when 'module'; Past::PModule.new(m,name,body)
    when 'package'; Past::PPackage.new(m,name,body)
    else fail("assert")
    end
  }
  node :member_declaration,->(m){
    var = emit(m.hash[:var_name])
    default = emitopt(m.hash[:expr])
    Past::MemberDeclaration.new(m,var,default)
  }
  node :var_error,->(m){
    Past::VarError.new(m)
  }
  node :closure_trait,->(m){
    Past::ClosureTrait.new(m,m.hash[:symbol].as_s,emit(m.hash[:block]))
  }
  node :do_block,->(m){
    b = emit(m.hash[:block])
    Past::DoBlock.new(m,b)
  }
#  node :standalone_block,->(m){
#    b = emit(m.hash[:block])
#    b.is_a?(Past::HashLiteral) ? b : Past::StandaloneBlock.new(m,b)
#  }
  node :code_block,->(m){
    b = emit(m.hash[:block])
    if b.is_a?(Past::HashLiteral)
      b
    elsif m.hash[:whiteSpaceLine]
      Past::StandaloneBlock.new(m,b)
    else
      Past::CodeBlock.new(m,b)
    end
  }
  node :block,->(m){
    begin
      e = m.hash[:statement_list].hash[:statement].hash[:expr_statement].hash[:expr].hash[:opp]
      h = pair_list_to_hash(unpack_comma_list(e))
      Past::HashLiteral.new(m,h)
    rescue
      Past::Block.new(m,emit(m.hash[:statement_list]))
    end
  }
  node :use_declaration,->(m){
    emit(m.hash[:use_version] || m.hash[:use_package])
  }
  node :use_version,->(m){
    Past::Comment.new(m,m.as_s)
  }
  node :use_package,->(m){
    Past::UsePackage.new(m,m.as_s)
  }
  node :undef_literal,->(m){ Past::UndefLiteral.new(m)}
  node :empty_list_literal,->(m){ Past::ListLiteral.new(m,[]) }
  node :prog,->(m){ emit(m.hash[:statement_list])}
  node :expr,->(m){
    if m.hash[:opp].rule.name == :sub_name_bare
      f = emit(m.hash.values[0])
      cap = make_capture(nil)
      Past::Apply.new(m,f,cap)
    else
      emit(m.hash.values[0])
    end
  }
  node :statement_list,->(m){
    lst = m.hash[:statement] || []
    lst = [lst] if !lst.kind_of?(Array)
    Past::StatementList.new(m,lst.map{|x|emit(x)}) }
  node :implicit_invocant,->(m){m.str = '$_'; Past::Variable.new(m)}
  node :apply_method_term,->(m){
    o = emit(m.hash[:implicit_invocant])
    f = emit(m.hash[:qualified_identifier])
    cap = emit(m.hash[:method_args])
    Past::ApplyMethod.new(m,o,f,cap)
  }
  node :apply_method,->(m){
    o = emit(m.hash[:parts][0])
    f = emit(m.hash[:parts][1].hash[:qualified_identifier])
    cap = emit(m.hash[:parts][1].hash[:method_args])
    Past::ApplyMethod.new(m,o,f,cap)
  }
  node :method_args,->(m){
    p = m.hash[:argument_list]
    make_capture(p)
  }
  node :apply_sub_notFolded,->(m){
    f = emit(m.hash[:sub_name_bare])
    p = (m.hash[:argument_list] ||
         (m.hash[:expr_above_comma] &&
          m.hash[:expr_above_comma].hash[:expr_opp_bare]))
    cap = make_capture(p)
    Past::Apply.new(m,f,cap)
  }
  node :parseParenParamList,->(m){ emitopt(m.hash.values[0]) }
  node :parseHasParenParamList,->(m){ emit(m.hash.values[0]) }
#  node :parens,->(m){ m.hash[:expr] ? emit(m.hash[:expr]) : Past::ListLiteral.new(m,[]) } #
  node :var_name,->(m){ emit(m.hash.values[0]) }
  node :var_simple,->(m){ Past::Variable.new(m) }
  node :var_declaration,->(m){
    vs = m.hash[:var_name]
    vs = [vs] if !vs.is_a?(Array)
    vars = vs.map{|v|emit(v)}
    vals = []
    if m.hash[:expr]
      vals = unpack_comma_list(m.hash[:expr].hash[:opp])
    elsif m.hash[:var_declaration]
      vals = [emit(m.hash[:var_declaration])]
    end
    sym = m.hash[:symbol]
    sym = sym[-1] if sym.is_a?(Array)
    sym = sym.as_s if sym
    sym = nil if sym == ','
    op = sym
    Past::VarDecl.new(m,m.hash[:scope].hash[:symbol].as_s,nil,vars,
                      emitopt(m.hash[:trait]),
                      op, vals)
  }
  node :for_construct,->(m){
    im = m.hash[:expr][0]
    l = unpack_comma_list(im.hash[:opp])
    iter = l.size == 1 ? l[0] : Past::ArrayLiteral.new(im,l)
    body = emit(m.hash[:expr][1])
    Past::For.new(m,iter,body)
  }
  def self.get_firstsense(m)
    sym = m.hash[:symbol].as_s
    firstsense = case sym
                 when 'if'; true; when 'unless'; false; else fail("assert") end
  end
  node :post_conditional,->(m){
    firstsense = get_firstsense(m)
    expr = emit(m.hash[:expr])
    [firstsense,expr]
  }
  node :cond_construct,->(m){
    firstsense = get_firstsense(m)
    body = emit(m.hash[:cond_body])
    Past::Cond.new(m,firstsense,body)
  }
  node :cond_body,->(m){
    this = [emit(m.hash[:cond_part]),
            emit(m.hash[:block])]
    rest = emitopt(m.hash[:elsif_construct] || m.hash[:else_construct])
    [this] + (rest ? rest : [])
  }
  node :elsif_construct,->(m){ emit(m.hash[:cond_body]) }
  node :else_construct,->(m){ [[emit(m.hash[:block])]] }
  node :cond_part,->(m){ emit(m.hash[:expr]) }
  def self.create_sig(m,kind)
    param = []
    tmp = (m.hash[:sub_parameters__ParensMandatory] ||
           m.hash[:sub_parameters__ParensOptional])
    if tmp
      param = tmp.hash[:formal_param]
      param = [] if not param
      param = [param] if param.class != Array
      param = param.map{|p|emit(p)}
    else
      t = nil
      slurpy = true
      n = Past::Variable.new('@_')
      optness = nil
      d = nil
      param = [Past::Parameter.new(nil,t,false,slurpy,false,n,optness,nil,d)]
    end
    Past::Signature.new(m,param,nil,kind)
  end
  node :sub_declaration,->(m){
    scope = m.hash[:scope] ? m.hash[:scope].hash[:symbol].as_s : nil
    kind = m.hash[:sub_head].hash[:symbol].as_s
    name = emit(m.hash[:sub_head].hash[:sub_name_no_amp])
    body = emit(m.hash[:block])
    sig = create_sig(m,kind)
    Past::SubDecl.new(m,scope,kind,name,sig,body)
  }
  node :block_formal_pointy,->(m){
    body = emit(m.hash[:block])
    sig = create_sig(m)
    Past::SubDecl.new(m,nil,'sub',nil,sig,body)
  }
  node :block_formal_standard,->(m){
    body = emit(m.hash[:block])
    sig = create_sig(m)
    Past::SubDecl.new(m,nil,'sub',nil,sig,body)
  }
  node :formal_param,->(m){
    t = emitopt(m.hash[:type])
    n = emit(m.hash[:param_name])
    d = emitopt(m.hash[:param_default])
    optness = emitopt(m.hash[:param_optness])
    foretaste = emitopt(m.hash[:param_foretaste])
    slurpy = foretaste =~ /\*/
    Past::Parameter.new(m,t,false,slurpy,false,n,optness,nil,d)
  }
  node :type,->(m){
    m.as_s
  }
  node :param_name,->(m){
    Past::Variable.new(m)
  }
  node :param_default,->(m){
    emit(m.hash[:expr_above_comma].hash[:expr_opp_bare])
  }
  node :param_foretaste,->(m){m.as_s}
  node :param_optness,->(m){m.as_s}
  node :sub_name_term,->(m){
    emit(m.hash[:sub_name_bare])
  }
  node :sub_name_bare,->(m){
    Past::Variable.new(m)
#    emit(m.hash[:qualified_identifier])
  }
  node :sub_name_no_amp,->(m){
    emit(m.hash[:qualified_identifier])
  }
  node :var_sub,->(m){
    Past::Variable.new(m)
#    Past::VarSub.new(m,m.hash[:sub_name_no_amp].as_s)
  }
  node :qualified_identifier,->(m){ Past::Identifier.new(m) }
#  node :bare_identifier,->(m){ emit(m.hash[:qualified_identifier]) }
  node :sub_parameters__ParensOptional,->(m){
    Past::Signature.new(tmp,param,nil,nil)
  }
  node :number,->(m){ Past::Number.new(m,m.as_s) }
  node :fakestring,->(m){
    md = m.as_s.match(/\A'(.*)'\z/m) or fail "bug\n#{m.as_s.inspect}"
    s = md[1]
    s.gsub!(/\\(.)/){|c|c}
    Past::String.new(m,s)
  }
  node :fakeEND,->(m){
    m.as_s =~ /^<<'_END' *\n(.+?)\n_END *\n\z/m or fail('assert')
    Past::String.new(m,$1)
  }
  node :fakierstring,->(m){
    md = m.as_s.match(/\A"(.*)"\z/m) or fail "bug #{m.inspect}"
    s = md[1]
    return Past::String.new(m,'') if s == ''
    s.gsub!(/\\(.)/){|c|c}
    ss = StringScanner.new(s)
    subs = []
    s0 = nil
    while not ss.eos?
      str = ss.scan(/(?:[^\\\$]|\\.)+/)
      if str
        str.gsub!(/\\n/,"\n")
        str.gsub!(/\\t/,"\t")
        s0 = str
        subs.push('"'+str+'"')
        next
      end
      code = ss.scan(/\$\w+/)
      if code
        subs.push(code)
        next
      end
      fail "assert"
    end
    if subs.size == 1 and s0
    then Past::String.new(m,s0)
    else
      subs.push('""') if subs.size == 1
      emit($grammar.expr.search(subs.join(" ~ "))) # offset
    end
  }
  node :angle_bracket_literal,->(m){
    s = m.as_s
    q = s.match(/^(?:<<?|\xab)/)[0]
    s = s.slice(q.length .. (-1 - q.length))
    Past::Quote.new(m,q,s)
  }
  node :term,->(m){ emit(m.hash.values[0]) }
  node :word,->(m){ Past::Word.new(m) }
  node :token,->(m){ emit(m.hash.values[0]) }
  node :statement,->(m){ emit(m.hash.values[0]) }
#  node :statement__expr_statement,->(m){ emit(m.hash.values[0]) }
  node :expr_statement,->(m){
    expr = emit(m.hash[:expr])
    post = emitopt(m.hash[:post_conditional] ||
                   m.hash[:post_loop] ||
                   m.hash[:post_iterate])
    if post
      firstsense,post_expr = *post
      Past::Cond.new(m,firstsense,[[post_expr,expr]])
    else
      expr
    end
  }
  node :variable,->(m){ Past::Variable.new(m) }
  node 'infix:,',->(m){
    l = unpack_comma_list(m)
    Past::ListLiteral.new(m,l)
  }
  node 'infix:+',->(m){ Past::Infix.new(m,m.hash[:parts][1],[0,2].map{|i|emit(m.hash[:parts][i])})}
  node 'prefix:+',->(m){ Past::Prefix.new(m,m.hash[:parts][0],[1].map{|i|emit(m.hash[:parts][i])})}
  node 'postfix:+',->(m){ Past::Postfix.new(m,m.hash[:parts][1],[0].map{|i|emit(m.hash[:parts][i])})}
  node 'postcircumfix:+',->(m){ Past::Postcircumfix.new(m,"#{m.hash[:parts][1].as_s} #{m.hash[:parts][3].as_s}",[0,2].map{|i|emit(m.hash[:parts][i])})}
  node 'postcircumfix:( )',->(m){
    f = emit(m.hash[:parts][0])
    p = m.hash[:parts][2]
    cap = make_capture(p)
    Past::Apply.new(m,f,cap)
  }
  node :postcircumfix_paren_empty,->(m){
    f = emit(m.hash[:parts][0])
    if m.hash[:parts][0].rule.name.to_sym == :apply_sub_notFolded
      f
    else
      cap = make_capture(nil)
      Past::Apply.new(m,f,cap)
    end
  }
  node 'ternary:?? !!',->(m){
    t = emit(m.hash[:parts][0])
    c = emit(m.hash[:parts][2])
    a = emit(m.hash[:parts][4])
    Past::Cond.new(m,true,[[t,c],[a]])
  }
  node 'circumfix:( )',->(m){
    e = emit(m.hash[:parts][1])
    if e.is_a?(Past::PPair) || (e.is_a?(Past::Infix) && e.fun.as_s == '=>')
    then Past::WrappedPPair.new(m,e)
    else e end
  }

  def self.emitopt(m)
    return nil if !m
    emit(m)
  end
  def self.emit(m)
    fail("boom\n#{caller.join("\n")}") if !m
    fail("thud\n#{caller.join("\n")}") if !m.bool
    name = m.rule.name.to_sym
    mname = mangle(name).to_sym
    mname2 = mname
    mname2 = mangle('infix:+').to_sym if name.to_s =~ /^infix:/
    mname2 = mangle('prefix:+').to_sym if name.to_s =~ /^prefix:/
    mname2 = mangle('postfix:+').to_sym if name.to_s =~ /^postfix:/
    mname2 = mangle('postcircumfix:+').to_sym if name.to_s =~ /^postcircumfix:/
    hand = @@map[mname] || @@map[mname2]
    if hand
      hand.call(m)
    else
      warn("#{self} FAILED: no handler for rules named '#{name}'")
      Past::SimpleString.new('THIS IS INVALID')
    end
  end
  def self.unpack_comma_list(m)
    name = m.rule.name.to_s
    if name == 'infix:,'
      unpack_comma_list(m.hash[:parts][0]) + [emit(m.hash[:parts][2])]
    else
      [emit(m)]
    end
  end
  def self.make_capture(m0)
    return Past::Capture.new(m0,[]) if not m0
    m1 = m0
    if m1.hash[:expr]
      m1 = m1.hash[:expr].hash[:opp]
    end
    return Past::Capture.new(m0,[]) if m1.rule.name.to_sym == :empty_list_literal
    #if m1.rule.name.to_sym == :parens
    #  m1 = m1.hash[:expr]
    #  m1 = m1.hash[:opp] if m1
    #end
    a = m1 ? unpack_comma_list(m1) : []
    Past::Capture.new(m0,a)
  end
end

###*** PastNodes

class Object; def as_s; to_s end end

module Past
  class PastObject
    attr_accessor :src
    attr_accessor :whiteboard
    def initialize(src) @src=src; @whiteboard={} end
    def inspect; src=@src;@src=@src.as_s;ret=super;@src=src;ret end
    def gensym; "v#{sprintf("%x",rand(1000000000))}" end
    def indent(s)
      s ? s.gsub(/(?m)^(?!\Z)/,'  ') : '*nil*'
    end
    def indent_except_top(s)
      s ? s.gsub(/(?m)^(?!\Z)/o,'  ').sub(/^  /,'') : '*nil*'
    end
    def down(m, *args, &block)
      if respond_to?(m)
        send(m,*args, &block)
      else
        down_not_self(m,*args,&block)
      end
    end
    def down_not_self(m, *args, &block)
      ms = m.to_s
      ms =~ /^(all|any|walk)_/ || fail("assert: #{ms}")
      any = $1 == 'any'
      all = $1 == 'all'
      ret = []
      instance_variables.each{|iv|
        n = iv.sub(/^@/,'')
        next if !self.respond_to?(n)
        node = self.send(n)
        nodes = nil
        if node.is_a?(PastObject)
          nodes = [node]
        elsif node.is_a?(Array)
          nodes = node.find_all{|e| e.is_a?(PastObject)}
        else next end
        nodes.each{|nd|
          res = nd.down(m,*args,&block)
          return res if any && res
          ret = ret + res if all
        }
      }
      ret
    end
    def walk_past_init(h=nil)
      h ||= {}
      down_not_self(:walk_past_init,h)
    end
  end
  def self.frob(s)
    s.split(/\s*\n/).each{|l|
      next if l =~ /^#/ or l =~ /^\s*$/
      l.gsub!(/^\s*/,'');
      cl,_fs=l.split(/\s+/);
      fs = _fs.split(/,\s*/); fs.shift if fs[0] == '-'
      as = ['src']+fs
      varcode = fs.empty? ? '' : <<-END
          attr_accessor #{fs.map{|f|":#{f}"}.join(',')}
          def initialize(#{as.join(',')})
            super(src)
            #{fs.map{|f|"@#{f}"}.join(',')} = #{fs.join(',')}
          end
      END
      code = <<-END 
        class #{cl} < PastObject #{"\n"+varcode}
        end
      END
      begin eval code; rescue Exception; fail "#{$!}:\n#{code}" end
    }
  end
  frob(%q{
Rx modifiers,pattern
PPair name,value
ArrayLiteral array
HashLiteral thehash
PRole name,body
PClass name,body
PModule name,body
PPackage name,body
UsePackage pkg
TypeLiteral -
MemberDeclaration var,default
VarError -
ClosureTrait flavor,block
DoBlock block
StandaloneBlock block
CodeBlock block
Comment comment
UndefLiteral -
ListLiteral list
Block statementlist
StatementList statements
ApplyMethod obj,f,arggen
Apply f,arggen
Capture argl
Identifier -
Number num
SimpleString -
String str
Quote quote,str
Variable -
VarSub name
VarDecl scope,mumble,vars,trait,op,vals
Cond sense_of_first_test,body
For enu,body
Word -
SubDecl scope,kind,name,sig,body
Signature params,returns,kind
Arguments theargs
Infix fun,fargs
Prefix fun,fargs
Postfix fun,fargs
Postcircumfix fun,fargs
WrappedPPair pair
})
# Parameter
end

###**** Past methods

module Past
  class VarDecl
    def my?;    scope == 'my' end
    def our?;   scope == 'our' end
    def state?; scope == 'state' end
    def let?;   scope == 'let' end
    def temp?;  scope == 'temp' end
    def env?;   scope == 'env' end
    def all_toplevel_vardecls; [self] end
  end
  module InitDecls
    def init_decls(h)
      decls = (h[:decls] || {}).dup
      down(:all_toplevel_vardecls).each{|vd|
        vars = vd.vars
        vars = [vars] if !vars.is_a?(Array)
        vars.each{|v|
          decls[v.perl] = vd
        }
      }
      h[:decls] = decls
    end
  end
  class StatementList
    include InitDecls
    def walk_past_init(h=nil)
      h ||= {}
      init_decls(h) if !h[:decls]
      down_not_self(:walk_past_init,h)
    end
  end
  class Block
    def all_toplevel_vardecls; [] end
    def block_toplevel_vardecls
      @statementlist.down_not_self(:all_toplevel_vardecls)
    end
    include InitDecls
    def walk_past_init(h)
      old_blk = h[:current_block]; h[:current_block] = self
      old_decls = h[:decls]; init_decls(h)
      block_toplevel_vardecls.each{|vd|
        vars = vd.vars
        vars = [vars] if !vars.is_a?(Array)
        vars.each{|v|
          h[:decls][v.perl] = vd
        }
      }
      @statementlist.down(:walk_past_init,h)
      h[:decls] = old_decls
      h[:current_block] = old_blk
    end
  end
  class Variable
    SIGIL_RE = /[$@%&]|::|@@/
    TWIGIL_RE = /[\.^*+?=!<]/ # $<foo>
    VARIABLE_RE = /^(?:(#{SIGIL_RE})(#{TWIGIL_RE})?)?(\w+(?:::\w+)*)$/
    attr_accessor :type,:sigil_actual,:twigil,:path
    def initialize(src,type=nil)
      super(src)
      @type = type
      initialize_from_string(@src.as_s)
    end
    def initialize_from_string(code)
      m = code.match(VARIABLE_RE) || fail("invalid variable: #{code}")
      @sigil_actual,@twigil,name = m[1..3]
      @path = name.split(/::/)
    end
    def sigil; @sigil_actual || '&' end
    def perl
      t = type ? type.perl+' ' : ''
      close = twigil == '<' ? '>' : ''
      "#{t}#{sigil_actual||''}#{twigil||''}#{path.join('::')}#{close}"
    end
    def pathname; path.join('::') end
    def leafname; path[-1] end
    def has_path; path.size > 1 end
    def scalar?;     sigil == '$' end
    def array?;      sigil == '@' end
    def hash?;       sigil == '%' end
    def exe?;        sigil == '&' end
    def type?;       sigil == '::' end
    def multislice?; sigil == '@@' end
    def accessor?;      twigil == '.' end # object attribute accessor
    def self_declared_parameter?; twigil == '^' end
    def global?;        twigil == '*' end
    def environmental?; twigil == '+' end
    def compiler?;      twigil == '?' end
    def pod?;           twigil == '=' end
    def match_capture?; twigil == '<' end
    def private?;       twigil == '!' end # aka explicitly_private?

    def walk_past_init(h)
      @whiteboard[:declaration] = h[:decls][self.perl] #|| fail("assert")
      down_not_self(:walk_past_init,h)
    end
    def declaration; @whiteboard[:declaration] end
  end
  class Parameter < PastObject
    attr_accessor :is_invocant,:is_slurpy,:is_name_only,
      :variable,:explicit_optness,:traits,:default
    attr_accessor :opt
    def initialize(src,*args)
      super(src)
      raise ArgumentError if args.size != 8
      type,@is_invocant,@is_slurpy,@is_name_only,
        variable,@explicit_optness,@traits,@default=*args
      @variable = variable #Variable.new(variable,type)
      @variable.type = type
      @opt = (@explicit_optness == '?' ||
              (@explicit_optness != '!' && @default))
    end
    def optional?; @opt end
    def required?; !@opt end
    def slurpy?; @is_slurpy end
    def positional?; !name_only? end
    def name_only?; @is_name_only end
    def invocant?; @is_invocant end
    def perl
      s = slurpy? ? '*' : ''
      n = name_only? ? ':' : ''
      o_concise = ''
      o_concise = '?' if optional? && !default
      o_concise = '!' if required? && default
      o_explicit = @explicit_optness
      o = o_explicit
      t = traits ? " "+traits.perl : ''
      d = default ? " = "+default.perl : ''
      "#{s}#{n}#{variable.perl}#{o}#{t}#{d}"
    end
    def slurpy_scalar?; slurpy? && scalar? end
    def slurpy_array?; slurpy? && array? end
    def slurpy_hash?; slurpy? && hash? end
    alias :method_missing_alias_Parameter :method_missing
    def method_missing(m,*args,&block)
      if @variable.respond_to?(m)
      then @variable.send(m,*args)
      else method_missing_alias_Parameter(m,*args,&block) end
    end
  end
  class Signature
    alias :initialize_Signature :initialize
    def initialize(*a)
      initialize_Signature(*a)
      add_implicit_self if kind == 'method' && invocants.empty?
    end
    def add_implicit_self
      implicit = Past::Parameter.new(nil,nil,
                                     true,false,false,
                                     Past::Variable.new('$self'),
                                     false,nil,nil)
      @params = [implicit] + (@params || [])
    end
    def perl
      r = returns ? ' --> '+returns.perl : ''
      ip = invocants.map{|p|p.perl}.join(', ')
      op = noninvocants.map{|p|p.perl}.join(', ')
      ip += ':' if ip
      "(#{ip}#{op}#{r})"
    end
    def invocants; ; params.select{|p| p.invocant?} end
    def noninvocants;params.select{|p| !p.invocant?} end
    def positionals; params.select{|p| p.positional?} end
    def name_onlys;  params.select{|p| p.name_only?} end
    def slurpys;     params.select{|p| p.slurpy?} end
    def types;       params.select{|p| p.type?} end
    def exes;        params.select{|p| p.exe?} end
    def slurpy_hash; params.select{|p| p.slurpy_hash?}[0] end
    def slurpy_positionals; params.select{|p| p.slurpy? && (p.scalar? || p.array?)} end
    def slurpy_scalar; params.select{|p| p.slurpy_scalar?}[0] end
    def slurpy_array; params.select{|p| p.slurpy_array?}[0] end
#    def named_names; nameds.map{|p|p.variable.leafname} end
    def positional_arity_min; positionals.select{|p| !p.optional?}.size end
    def positional_arity_max; slurpy_array ? Inf : positionals.size end
#    def nameds_required; nameds.select{|p| p.required?} end
  end
end


###*** PastToRb

module Past
  class PastObject
    def rb_type_mangle(tn)
      tn =~ /^Rb::/ ? tn.slice(4..-1) : ''+tn
    end
  end
  class Rx
    def emit_rb;
      pat = @pattern.dup
      if pat =~ /^{/
        pat = pat.gsub(/^./,'').gsub(/.$/,'').gsub(/\//,'\\/')
        pat = "/#{pat}/"
      end
      "#{pat}x"
    end
  end
  class PPair
    def emit_rb;
      v = @value ? @value.emit_rb : "1"
      "Pair.new(#{@name.inspect},#{v})"
    end
  end
  class WrappedPPair
    def emit_rb;
      "WrappedPair.new(#{pair.emit_rb})"
    end
  end
  class ArrayLiteral
    def emit_rb; '['+@array.map{|e|e.emit_rb}.join(', ')+'].delisty' end
  end
  class HashLiteral
    def emit_rb; '{'+@thehash.map{|k,v|"#{k.emit_rb}=>#{v.emit_rb}"}.join(', ')+'}' end
  end
  class PClass
    def emit_rb
      n = rb_type_mangle(@name)
      b = @body.emit_rb
      "class #{n}\n  init_class(self,Module.nesting)\n#{indent(b)}\nend\n"
    end
  end
  class MemberDeclaration
    def emit_rb
      vm = "#{var.emit_rb_def}m"
      code = "attr_accessor :#{vm}"
      va = "#{var.leafname}C"
      v = var.emit_rb
      code += "\ndef #{va}() ->(_S,selfS){ #{v} }end"
      gs = gensym
      sigex = var.rb_sigex
      code += "\nalias :initialize_#{gs} :initialize\ndef initialize(*a) initialize_#{gs}(*a); @#{vm} ||= BindBox#{sigex}.new end"
      code
    end
  end
  module NotClassHlp
    def emit_rb
      n = rb_type_mangle(@name)
      b = @body.emit_rb
      "module #{n}\n  init_nonclass(self,Module.nesting)\n#{indent(b)}\nend\n"
    end
  end
  class PRole; include NotClassHlp end
  class PModule; include NotClassHlp end
  class PPackage; include NotClassHlp end
  class UsePackage
    def emit_rb; "useC.call(nil,#{@pkg.inspect})\n" end
  end
  class TypeLiteral
    def emit_rb
      rb_type_mangle(@src.as_s)
    end
  end
  class VarError
    def emit_rb; '$scalar_ERROR' end
  end
  class ClosureTrait
    def emit_rb; "#{@flavor} {\n#{indent(@block.emit_rb)}\n}" end
  end
  class DoBlock
    def emit_rb; "proc{\n#{indent(@block.emit_rb)}\n}.call()" end
  end
  class StandaloneBlock
    def emit_rb; "proc{\n#{indent(@block.emit_rb)}\n}.call()" end
  end
  class CodeBlock
    def emit_rb
      a = @whiteboard[:need_an_arg] ? '_S' : ''
      a = a == '' ? "_S" : "_E,#{a}" 
      "->(#{a}){\n#{indent(@block.emit_rb)}\n}"
    end
  end
  class Comment
    def emit_rb; @comment.gsub(/^/,'# ').sub(/\n?\z/,"\n") end
  end
  class UndefLiteral
    def emit_rb; "pundef" end
  end
  class ListLiteral
    def emit_rb; 'Listy['+@list.map{|e|e.emit_rb}.join(",")+']' end
  end
  class Block
    def emit_rb
      b = @statementlist.emit_rb
      vs = []
      down(:block_toplevel_vardecls).map{|vd|
        next if !vd.my?
        vars = vd.vars
        vars.is_a?(Array) ? vs += vars.map{|v|v.emit_rb} : vs += [vars.emit_rb]
      }
      vs = vs.sort.uniq
      if vs.empty?
        b
      else
        vl = vs.join(',')
        nl = (['nil'] * vs.size).join(',')
        "->(#{vl}){\n#{indent(b)}\n}.call(#{nl})"
      end
    end
  end
  class StatementList
    def emit_rb; @statements.map{|x|x.emit_rb}.join(";\n") end
  end
  class ApplyMethod
    def emit_rb
      f = @f.emit_rb
      if f =~ /^rUBY/
        n = f.slice(4..-1)
        n.sub!(/_P$/,'?')
        "#{@obj.emit_rb}.#{n}(#{@arggen ? @arggen.emit_rb : ''})"
      else
        o = gensym
        n = "#{f}C"
        a = "#{@arggen ? @arggen.emit_rb : ''}"
        a = a == '' ? "#{o},#{o}" : "#{o},#{o},#{a}" 
        "(#{o}=#{@obj.emit_rb}).#{n}.call(#{a})"
      end
    end
  end
  class Apply
    def emit_rb
      f = @f.src.as_s
      if f =~ /^rUBY/
        n = f.slice(4..-1)
        n.sub!(/_P$/,'?')
        "#{n}(#{@arggen ? @arggen.emit_rb : ''})"
      elsif f == 'raw_rUBY'
        argl = @arggen.argl.dup
        code = argl.shift.str
        code = "#{code}.call(#{argl.map{|a|a.emit_rb}.join(',')})" if !argl.empty?
        code
      elsif f == 'source_ruby'
        argl = @arggen.argl.dup
        code = argl.shift.str
        code = "source_ruby(<<'_END'\n#{code}\n_END\n);\n"
        code
      elsif f == 'source_perl'
        argl = @arggen.argl.dup
        code6 = argl.shift.str
        rest = ''
        "source_perl(<<'_END'\n#{code6}\n_END\n#{rest});\n"
      elsif f == 'source_perl_compiled'
        argl = @arggen.argl.dup
        perlcode = argl.pop.str
        sum = chksum(perlcode+"\n")
        #print "Comp"+"iled: ",perlcode.length," ",sum,"\n"
        #File.open("deleteme_01","w"){|f|f.write perlcode.split(//).join("\n")}
        rubycode = $P.compile6(perlcode)
        coderb = rubycode.gsub(/\\/,'\\\\').gsub(/\'/,'\\rbbug').gsub(/rbbug/,'\'')
        coderb = "'\n"+coderb+"\n'"
        rest = "'#{sum}',#{coderb},"
        "source_perl_compiled(#{rest}<<'_END'\n#{perlcode}\n_END\n);\n"
      elsif f == 'source_perl_finish'
        "source_perl_finish()"
      else
        n = "#{f}C"
        a = "#{@arggen ? @arggen.emit_rb : ''}"
        a = a == '' ? "_S" : "_S,#{a}" 
        "#{@f.emit_rb}.call(#{a})"
      end
    end
  end
  class Capture
    def emit_rb; @argl.map{|a|a.emit_rb}.join(",") end
  end

  class VarDecl
    def emit_rb
      ini = []
      @vars.each_with_index{|v,i|
        bb = case v.sigil 
             when '$'; 'BindBox'
             when '@'; 'BindBoxa'
             when '%'; 'BindBoxh'
             when '&'; 'BindBoxc'
             else fail("assert") end
        vi = @vals[i]
        val1 = vi ? vi.emit_rb : ''
        val2 = vi ? val1 : 'pundef'
        r = case @op
            when nil; "#{bb}.new()"
            when '='; "#{bb}.new(#{val1})"
            when ':='; val2
            when '::='; val2
            else fail("assert #{op}") end
        ini.push(r)
      }
      lhs = if our?
              @vars.map{|v|v.emit_rb_symtab}.join(',')
            else
              @vars.map{|v|v.emit_rb}.join(',')
            end
      rhs = ini.empty? ? '' : " = "+ini.join(',')
      "#{lhs}#{rhs}"
    end
  end
  class Cond
    def emit_rb
      s = nil
      @body.each{|tc|
        test,cons=*tc
        if not s
          sym = @sense_of_first_test ? 'if' : 'unless'
          s = "#{sym} (#{test.emit_rb}).as_b\n#{indent(cons.emit_rb)}"
        elsif cons
          s += "\nelsif (#{test.emit_rb}).as_b\n#{indent(cons.emit_rb)}"
        else
          cons = test
          s += "\nelse\n#{indent(cons.emit_rb)}"
        end
      }
      s += "\nend"
      s
    end
  end
  class For
    def emit_rb;
      arity = 1
      arity = @body.sig.positional_arity_min if @body.respond_to?(:sig)
      if arity == 1
        @body.whiteboard[:need_an_arg] = 1
        "(#{@enu.emit_rb}).each{|__e__|(#{@body.emit_rb}).call(__e__,__e__)}"
      else
        a=gensym;i=gensym;v=gensym;
        "(#{a} = (#{@enu.emit_rb}); #{i}=0; while #{a}.exists?(#{i}); #{v} = (#{@body.emit_rb}).call(:deadbeef,*(#{a}.slice(#{i},#{arity}))); #{i} += #{arity}; #{v} end)"
      end
    end
  end
  class Identifier
    def emit_rb; @src.as_s end
  end
  class Number
    def emit_rb; "#{@num}" end
  end
  class SimpleString; def emit_rb; @src.as_s.inspect end end
  class String
    def emit_rb; @str.inspect end
  end
  class Quote
    def emit_rb
      case @quote
      when '<';
        s = @str
        "Listy[*('#{s}'.split(/\\s+/))]"
      else fail("assert or unimplemented") end
    end
  end
  class VarSub
    def emit_rb;
      "method('#{@name}')"
    end
  end
  class Variable
    def emit_rb_symtab
      tab = global? ? "symtab['::GLOBAL'].symtab" : "symtab"
      "#{tab}['#{sigil+leafname}']"
    end
    def rb_sigex
      {"\$"=>'S','@'=>'A','%'=>'H','&'=>'C'}[sigil]
    end
    def emit_rb_def
      v = @src.as_s
      decl = declaration
      if !decl || decl.my?
        return "_E" if v == '$+_'
        sigex = rb_sigex
        ns = pathname+sigex
        if twigil == '*'
          return "\$_#{ns}g"
        end
        ns
      else
        emit_rb_symtab
      end
    end
    def emit_rb
      c = emit_rb_def
      case twigil
      when '.'; "selfS.#{c}m"
      when '!'; "selfS.#{c}m"
      else c end
    end
  end
  class Infix
    def emit_rb
      a0 = fargs[0].emit_rb
      a1 = fargs[1].emit_rb
      fs = fun.as_s
      case fs
      when '~'; "(\"\#\{#{a0}}\" + \"\#\{#{a1}}\")"
      when ','; "#{a0} #{fs} #{a1}"
      when '.'; a1hack = fargs[1].src.as_s; "#{a0}#{fs}#{a1hack}"
      when '='; "(#{a0})._(#{a1})"
      when ':=';  "#{a0} = #{a1}"
      when '::='; "#{a0} = #{a1}"
      when '~~'; "smartmatch(#{a0},#{a1})"
      when '=>';
        s0 = fargs[0].src.as_s
        a0 = "'#{s0}'" if s0 =~ /^\w+$/ #ws
        "Pair.new(#{a0},#{a1})"
      when 'eq'; "#{a0}.as_s == #{a1}.as_s"
      when '=:='; "#{a0}.object_id == #{a1}.object_id"
      when '|'; "#{a0}.as_s + '_BOGUS_JUNCTION_' + #{a1}.as_s"
      #when '.='; '"UNIMPLEMENTED .= OP"'
      when '.='; "#{a0}._(#{a0}.#{a1})"
      when '~='; "#{a0}._(#{a0}.as_s + #{a1}.as_s)"
      when /^([-+*\/])=$/; "#{a0}._(#{a0} #{$1} #{a1})"
      else
        fs = {'eq'=>'==','ne'=>'!='}[fs] || fs
        "(#{a0} #{fs} #{a1})"
      end
    end
  end
  class Prefix
    def emit_rb
      as = fargs[0].emit_rb
      fs = fun.as_s
      case fs
      when '?'; "#{as}.as_b"
      when '!'; "!#{as}.as_b"
      when '+'; "#{as}.as_n"
      when '~'; "#{as}.as_s"
      when '%'; "#{as}.as_h0"
      when '++'; y=gensym;"(#{y}=#{as};#{as}._(#{y}+1);#{y})"
      when '--'; y=gensym;"(#{y}=#{as};#{as}._(#{y}-1);#{y})"
      when '\\'; "#{as}" #X
      else "#{fs}#{as}" end
    end
  end
  class Postfix
    def emit_rb
      as = fargs[0].emit_rb
      fs = fun.as_s
      case fs
      when '++'; y=gensym;"(#{y}=#{as};#{as}._(#{y}+1))"
      when '--'; y=gensym;"(#{y}=#{as};#{as}._(#{y}-1))"
      else "#{as}#{fs}" end
    end
  end
  class Postcircumfix
    def emit_rb
      a0 = fargs[0].emit_rb
      a1 = fargs[1].emit_rb
      fs = fun.as_s
      case fs
      when '[ ]'; "#{a0}.get(#{a1})"
      when '{ }'; "#{a0}.get(#{a1})"
      when '< >'; a1 = fargs[1].src.as_s; "#{a0}.get('#{a1}')"
      else "'INVALID POSTCIRCUMFIX #{fs}'" end
    end
  end
  class Word
    def emit_rb; @src.as_s end
  end
  class Parameter
  end
  class Signature
    def emit_rb_multi_pattern
      @params.map{|p|
        p.type ? p.type.src.as_s : 'Object'
      }.join(',')
    end
    def emit_rb_param_list
      @params.map{|p|
        n = p.variable.emit_rb_def
        n = "*#{n}" if p.slurpy?
        n
      }.join(',')
    end
    def emit_rb_param_list_with_nils
      @params.map{|p|
        n = p.variable.emit_rb_def
        if p.optional?
        then "#{n} = nil"
        else n end
      }.join(',')
    end
    def emit_rb_args_processing
      code = ""
      code += "_argl_,_argn_=_args_sep_(_args_);_arglsz_=_argl_.size\n"

      amin = positional_arity_min
      amax = positional_arity_max.as_s #Inf
      code += "fail(\"Incorrect argument count \#{_arglsz_} vs #{amin}..#{amax}\") if _arglsz_ > #{amax}\n"

      code += @params.size > 0 ? "#{emit_rb_param_list}=*_argl_.delisty\n" : ""

      @params.each{|p|
        v = p.variable.emit_rb_def
        n = p.variable.leafname
        code += "#{v} = ((x = _argn_.delete('#{n}')) && x[-1]) || #{v}\n"
      }
      if slurpy_hash
        code += "#{slurpy_hash.variable.emit_rb} = _argn_\n"
      else
        code += "fail(\"Unexpected named arguments \#{_argn_.keys.join(',')}\") if not _argn_.empty?\n"
      end

      @params.map{|p|
        v = p.variable.emit_rb_def
        n = p.variable.leafname
        if p.default
          code += "#{v} = #{p.default.emit_rb} if #{v} == nil\n"
        elsif p.optional?
          code += "#{v} = pundef if #{v} == nil\n"
        else
          code += "#{v} == nil && fail(\"Missing argument for required parameter #{n}\")\n"
        end
      }
      code
    end
    def arity; @params.size end
  end
  class SubDecl
    def emit_rb
      k = @kind.gsub(/\s+/,' ')
      k = "multi sub" if k == "multi"
      argsprocess = "#{@sig.emit_rb_args_processing}"
      lam = "->(*_args_){\n  ->(_S,_E){\n  _E=_args_.shift\n#{indent(argsprocess+@body.emit_rb)}\n}[nil,nil]\n}"
      lam2 = "->(*_args_){\n  ->(_S,_E,selfC){\n  _E=_args_.shift\n#{indent(argsprocess)}\n  selfC = ->(_S){selfS}\n#{indent(@body.emit_rb)}\n}[nil,nil,nil]\n}"
      if @scope == 'my'
        case k
        when 'sub'
          fail("assert") if not @name
          n = "#{@name.emit_rb}C"
          "#{n} = #{lam}\n"
        else
          fail("my @k not implemented")
        end
      else
        case k
        when 'sub'
          if @name
            n = "#{@name.emit_rb}C"
            "current_class.def_pkg_var(#{n.to_sym.inspect},#{lam})\n"
          else
            "#{lam}\n"
          end
        when 'method'
          n = "#{@name.emit_rb}C"
          "current_class.def_pkg_var(#{n.to_sym.inspect},#{lam2})\n"
        when 'submethod'
          n = "#{@name.emit_rb}Csm"
          "current_class.def_pkg_var(#{n.to_sym.inspect},#{lam})\n"
        when 'multi sub'
          n = "#{@name.emit_rb}C"
          nm = "#{@name.emit_rb}M"
          spc = @sig.emit_rb_multi_pattern
          "multi(#{nm.to_sym.inspect},Object,#{spc},#{lam})\ncurrent_class.def_pkg_var(#{n.to_sym.inspect},->(*args){#{nm}(*args)})\n"
        else fail("#{k} not implemented")
        end
      end
    end
  end
end
#class Module; public :define_method end

###** Runtime

###*** Needed by P6

class SymbolTable
  attr_accessor :hash,:up
  def initialize(up) @hash,@up = {},up end
  def [](i)
    if @hash.member?(i); @hash[i]
    elsif @up; @up[i]
    else fail("Undefined #{i}") end
  end
  def []=(i,v) @hash[i] = v end
end

module HasSymbolTableM
  attr_accessor :pkgname
  def init_symtab(n,up=nil)
    uptable = up ? up.symtab : nil
    @table = SymbolTable.new(uptable)
    @pkgname = n
    @up = up
  end
  def symtab; @table end
  def path; (up ? up.path : []) + [n] end
  def fullname
    if up
      '::*::' + path.join('::')
    else
      fail('assert') if @n != 'GLOBAL'
      '::*'
    end
  end
end

class RPackage
  def self.make_GLOBAL
    g = self.new('GLOBAL')
    g.symtab['::GLOBAL'] = g
    m = self.new('Main',g)
    g.symtab['::Main'] = m
  end
  def initialize(n,up=nil)
    init_symtab(n,up)
  end
  include HasSymbolTableM
  def thebinding; binding end
end

class Class
  include HasSymbolTableM
  def thebinding; binding end
  def init_class(cls,nest)
    cls.name =~ /([^:]+)\z/
    init_symtab($1,nest[1]) if !symtab
  end
end
class Module
  include HasSymbolTableM
  def thebinding; binding end
  def init_nonclass(cls,nest)
    cls.name =~ /([^:]+)\z/
    init_symtab($1,nest[1]) if !symtab
  end
end


###*** P6

require 'readline'
class P6
  attr_accessor :pkgspace
  attr_accessor :verbose
  def initialize
    @verbose=true
    @pkgspace = RPackage.make_GLOBAL
  end
  def P6_binding; @pkgspace.thebinding end
  def P6_binding_Main; @pkgspace.symtab['::Main'].thebinding end

  def note(head,val,insp=true)
    print "__________\n#{head}\n#{insp ? val.inspect : val}\n----------\n" if @verbose
  end
  def number_lines(s)
    cnt = 0
    s.split(/\n/).map{|l|cnt+=1; "#{cnt}\t#{l}\n"}.join("")
  end
  def file_prelude
    "\n \#(\n\$ruby_init_code = <<'_CODE'\n#{$ruby_init_code}_CODE\neval(\$ruby_init_code)\n #)\n"
  end
  def compile_file(src,dest)
    src = get_file(src)
    rb = compile6(src)
    f = File.open(dest,"w")
    f.write(file_prelude)
    f.write(rb)
    f.close_write
    rb
  end
  def get_file(fn)
    src = `cat #{fn}`
  end
  def parse(src)
    note :src,src
    tree = $grammar.prog.search(src,0,false)
    note :tree,tree
    tree
  end
  def clean
    (ENV['CLEAN'] && ENV['CLEAN'].to_i > 0) ? ENV['CLEAN'].to_i : nil
  end
  require 'digest/md5'
  def compile6(src,fileline=nil)
    if ENV['TEST_ONLY_PARSE'] && ENV['TEST_ONLY_PARSE'] != '0'
      print "1..2\n"
      tree = parse(src)
      if tree.bool
      then print "ok Parsed something...\n"
      else print "notok Parse failed.\n"
      end
      if tree.to == src.length
      then print "ok Parsed. #{tree.to} vs #{src.length}\n"
      else print "notok Partial\n"
      end
      exit
    end
    print "Parsing #{fileline[0]} line #{fileline[1]}.\n" if fileline
    k = Digest::MD5.hexdigest(src)
    if (!clean||clean==2) && File.exists?("cache/#{k}.rb")
      print "Using cache/#{k}.rb\n"
      return `cat cache/#{k}.rb`
    end
    tree = parse(src)
    if not tree.bool
      print "Parse failed.\n"
      return
    end
    if not tree.to == src.length
      print "Parse partial.(#{tree.to} of #{src.length})\n"
    else
      print "Parsed.\n" if @verbose
    end
    ast = PastFromParse::emit(tree)
    ast.walk_past_init
    note :ast,ast
    rbc = ast.emit_rb
    note :rbc,number_lines(rbc),false
    if !clean
      File.open("cache/#{k}.rb","w"){|f|f.print(rbc)}
    end
    rbc
  end    
  def eval6_file(fn)
    fnc = "#{fn}.rb"
    if FileTest.exist?(fnc) && !clean
      print "Using precompiled #{fnc}\n"
      rbc = `cat #{fnc}`
      eval_safe(rbc,nil,fnc,0)
    else
      print "Loading #{fn}\n"
      src = get_file(fn)
      eval6(src,[fn,1])
    end
  end
  def eval6q(src,fileline=nil)
    v = @verbose; @verbose = false;
    res = eval6(src,fileline); @verbose = v;
    res
  end
  def eval6(src,fileline=nil)
    rbc = compile6(src,fileline)
    print "====================\n" if @verbose
    res = eval_safe(rbc,
                    nil,"compiled:#{fileline ? fileline[0] : ''}",
                    fileline ? fileline[1] : 0)
    print "=> ",res.inspect,"\n" if @verbose
    res
  end
  def eval_safe(rbc,binding=nil,file=nil,line=nil)
    binding ||= self.P6_binding_Main
    eval('_S = BindBox.new(pundef); _E = BindBox.new(pundef)',binding)
    begin res = eval(rbc,binding,file,line)
    rescue NameError => boom
      print number_lines(rbc),boom,"\n",boom.backtrace.join("\n"),"\n"
    rescue SyntaxError => boom
      print number_lines(rbc),boom,"\n",boom.backtrace.join("\n"),"\n"
    end
    res
  end
  def repl
    begin
      histfile = File::expand_path("deleteme_hist")
      if File::exists?(histfile)
        Readline::HISTORY.push(*(eval(IO.read(histfile))||[]))
      end
      while true
        #src = gets
        src = Readline.readline("\033[0;31m6\033[0m: ",true)
        #src = begin readline rescue "" end
        if src == "" || !src;print "\n"; break end
        if src == "\n"; break end
        eval6(src,['repl-input',1])
      end
    rescue Interrupt
      exit
    ensure
      h = Readline::HISTORY.to_a.reverse.uniq.slice(0,100).reverse.inspect
      open(histfile,"w"){|io|io.puts(h)}
    end
  end
end
$P = P6.new
def repl; $P.repl end

###*** Force include() to work with dynamic modules.

$module_included_by = {}
class Module
  alias :append_features_pre_ModuleIncludeGraph :append_features
  def append_features(m)
    d = $module_included_by[self] ||= []
    d.push(m)
    append_features_pre_ModuleIncludeGraph(m)
  end
  def self.refresh_includes_of(m0)
    todo = [m0]
    while !todo.empty?
      m = todo.shift
      d = $module_included_by[m] || []
      d.each{|m1|
        m.funcall(:append_features_pre_ModuleIncludeGraph,m1)
      }
      todo += d
    end
  end
  alias :append_features_pre_ModuleIncludeDynamic :append_features
  def append_features(m)
    append_features_pre_ModuleIncludeDynamic(m)
    Module.refresh_includes_of(m)
  end
end

###*** Multiple inheritance

$clasule_of = {}
module ClasuleInstanceM
  def list_is;   @@list_is   ||= [] end
  def list_does; @@list_does ||= [] end
end
module ClasuleM
  def _clasule_new
    m = Module.new
    m.module_eval('include ClasuleInstanceM')
    $clasule_of[self] = m
    include m
    def_is(superclass) if superclass
    m
  end
  def clasule; $clasule_of[self] || _clasule_new end
  def def_is(k)
    self.clasule.append_features(k.clasule)
    self.clasule.list_is.push(k)
  end
  def def_does(k)
    self.clasule.append_features(k.clasule)
    self.clasule.list_does.push(k)
  end
end
class Class; include ClasuleM end
class Module; include ClasuleM end
class Object
  def obj_clasule; (class << self; self end).clasule end
end
class Module
  def defc(sym,b) clasule.funcall(:define_method,sym,b) end
end


###*** Multi-method dispatch
#  Derived from Christopher Cyll's multi-0.1. (md5 ...30826914ae  multi.rb)
# Very not the right thing.

def multi(method_name, *patterns, &body)
  body ||= patterns.pop
  Multi::DISPATCHER.add(Multi::Dispatch, self, method_name, patterns, body)
end

module Multi
  class Dispatch
    def initialize(patterns, body)
      @patterns = patterns
      @body = body
    end
    
    def match?(params)
      pairs = params.zip(@patterns)
      return pairs.all? do |param, pattern|
        if pattern.kind_of?(Class)
          param.kind_of?(pattern)
        elsif pattern.instance_of?(Proc)
          begin
            pattern.call(param)
          rescue
            false
          end
        elsif pattern.instance_of?(Regexp)
          pattern.match(param)
        else
          param == pattern
        end
      end
    end
    
    def call(params, block)
      @body.call(*params, &block)
    end
  end

  class Dispatcher
    def initialize
      @map = {}
    end

    def add(type, obj, method_name, patterns, body)
      method_name = method_name.id2name if method_name.kind_of?(Symbol)
      body = patterns.pop if body.nil?
      klass = obj.kind_of?(Module) ? obj : class << obj; self end

      key = [klass, method_name]
      @map[key] ||= []
      @map[key].push(type.new(patterns, body))

      if ! obj.methods.include?(method_name)
        klass.funcall(:define_method,method_name.to_sym,proc{|*params, &block|
                          Multi::DISPATCHER.dispatch(klass, method_name, params, block)
                      })
      end
    end

    def dispatch(klass, method_name, params, block)
      candidates = @map[[klass, method_name]]
      handler = candidates.find{|candidate| candidate.match?(params) }
      if handler.nil?
        printed_params = params.map{|param| param.inspect}.join(', ')
        raise "No match for #{obj}.#{method_name}(#{printed_params})"
      end
      handler.call(params, block)
    end
  end

  DISPATCHER = Dispatcher.new()
end

###*** Dynamic env
# thanks to Tanaka Akira 

module Kernel
  def with_context(params)
    Thread.current[:dynamic] ||= []
    Thread.current[:dynamic].push params
    begin
      yield
    ensure
      Thread.current[:dynamic].pop
    end
  end

  def find_in_context(name)
    Thread.current[:dynamic].reverse_each {|params|
      return params[name] if params.has_key? name
    }
    raise "Can't find context value for #{name}"
  end
end

###*** Basics

module Kernel
  def current_class; self.is_a?(Class) ? self : self.class end #X
end

module Kernel
  def _args_sep_(args)
    pa = []; na = {}
    args.each do |a|
      if a.pair?
        (na[a.name] ||= []).push(a.value)
      else
        a = a.unwrap if a.wrapped_pair?
        pa.push(a)
      end
    end
    [pa,na]
  end
end
class Object
  def pair?; false end
  def wrapped_pair?; false end
end
class Pair; def pair?; true end end

class Module
  def def_pkg_var(sym,val)
#    p self,self.class,methods.grep(/#{sym}/)
#    p begin eval "#{sym}" rescue nil end
    class_eval %{
      def #{sym}; @@#{sym} end
      def #{sym}=(v); @@#{sym} = v end
    }
#    p self,self.class,methods.grep(/#{sym}/),methods.grep(/#{sym}/)
    class_eval %{
      @@#{sym} = ObjectSpace._id2ref(#{val.object_id})
    }
  end
end

module Kernel
  def _S; pundef end
end

class Object; def listy?; false end end

###*** BindBox

require 'delegate'
class BetterDelegator < Delegator; end
class << Object
  alias :pre_BetterDelegator_method_added :method_added
  def method_added(id)
    #print "method_added(#{id.id2name}) on #{self}\n"
    if self == Object
      #print "punting #{id.id2name}\n"
      BetterDelegator.funcall(:undef_method,id)
    end
    pre_BetterDelegator_method_added(id)
  end
end

class Object; def __getobj__; self end end
module ScalarSetM
  def _(*opt)
    o = *opt
    o = *o.to_a if o.listy?
    o ||= pundef
    __setobj__(o)
  end
end

class BindBoxKeyed < BetterDelegator
  attr_accessor :aggregate,:key
  def initialize(aggregate,key) @aggregate,@key=aggregate,key end
  def __getobj__; @aggregate[@key] || pundef end
  def __setobj__(o) @aggregate[@key] = o end
  include ScalarSetM
end
class BindBox < BetterDelegator
  attr_accessor :__getobj__
  def initialize(*args)
    super(nil)
    _(*args)
  end
  def __setobj__(o)
    @__getobj__= o
  end
  include ScalarSetM
end
class BindBoxa < BindBox
  def _(*args)
    a = []
    args.each{|e|
      if e.listy? || e.is_a?(Array) || e.is_a?(Range)
      then a.push(*e.to_a) 
      else a.push(e) end
    }
    super(a)
  end
end
class BindBoxh < BindBox
  def self._fudge_init(a)
    o = {}
    a.pop if a == [nil]
    a = a.delisty
    while not a.empty?
      k = a.shift
      v = nil
      if k.isaC.call(nil,"Pair").as_b
        k,v = k.name,k.value
      elsif k.wrapped_pair?
        p = k.unwrap
        k,v = p.name,p.value
      else
        fail("Odd number of elements initializing hash") if a.empty?
        v = a.shift
      end
      o[k] = v
    end
    o
  end
  def _(*a)
    o = BindBoxh._fudge_init(a)
    super(o)
  end
end
class BindBoxc < BindBox
end
BindBoxS = BindBox
BindBoxA = BindBoxa
BindBoxH = BindBoxh
BindBoxC = BindBoxc

###*** some subs
class Undef; end
module Kernel; def pundef; Undef.new end end

def df(sym,b)
  sym = "#{sym}C".to_sym
  bb = BindBox.new(b)
  eval %{
    module Kernel
      def_pkg_var(#{sym.to_sym.inspect},ObjectSpace._id2ref(#{bb.object_id}))
    end
  }
end

df :say,->(_E,*args){ print args.map{|a|a.as_s},"\n"}
df :defined,->(_E,x){ x.is_defined}
df :substr,->(_E,s,off,len){ s.slice(off,len)}
df :split,->(_E,re,s){ s.split(re)}
df :die,->(_E,*args){ fail(*args)}
df :try,->(_E,b){
  $scalar_ERROR = pundef
  begin
    b.call(_E)
  rescue Exception
    $scalar_ERROR = "#{$!}"
    pundef
  end
}
df :use,->(_E,c){
  puse(c)
}
def puse(n)
  fn = "#{n}.pm"
  $P.eval6_file(fn)
end

###*** misc
###**** is_defined
class BindBoxKeyed; def is_defined; __getobj__.is_defined end end
class BindBox; def is_defined; @__getobj__.is_defined end end
class Object; def is_defined; true end end
class Undef;  def is_defined; false end end
class Object; def definedC;->(_S){self.is_defined}end end
class NilClass; def is_defined; false end end #X
###**** as_s
class BindBoxKeyed; def as_s; __getobj__.as_s end end
class BindBox; def as_s; @__getobj__.as_s end end
# class Object; def as_s; is defined earlier.
class Array; def as_s; map{|e|e.as_s}.join(" ") end end
class Undef; def as_s; "" end end
class Pair; def as_s; "#{name.as_s}\t#{value.as_s}" end end
# class Float; def as_s; is defined elsewhere.
###**** as_i
class BindBoxKeyed; def as_i; __getobj__.as_i end end
class BindBox; def as_i; @__getobj__.as_i end end
class Object; def as_i; to_i end end
class Array;  def as_i; size end end
class Listy;  def as_i; size end end
###**** as_b
class BindBoxKeyed; def as_b; __getobj__.as_b end end
class BindBox; def as_b; @__getobj__.as_b end end
class Object; def as_b; true end end
class Fixnum; def as_b; self == 0 ? false : true end end
class FalseClass; def as_b; false end end
class NilClass; def as_b; false end end
class Undef; def as_b; false end end
###**** as_h
class Hash; def as_h0; self end end
class Array;
  def as_h0;
    fail("Odd number of elements in array") if size % 2 != 0
    Hash[*self]
  end
end
###**** as_n
# is elsewhere
###**** ref
class BindBox; def refC;proc{ @__getobj__.refC.call }end end
class Object; def refC;proc{ "#{self.class}" }end end
class String; def refC;proc{ "Str" }end end
class TrueClass;  def refC;proc{ "Bool" }end end
class FalseClass; def refC;proc{ "Bool" }end end
class Proc; def refC;proc{ "Code" }end end
###**** true false
class Object
  class Blah
    attr_accessor :trueC,:falseC
    def initialize
      @trueC = BindBox.new(->(_E){ 1 == 1})
      @falseC = BindBox.new(->(_E){ 1 == 2})
    end
    self
  end
  BLAH = Blah.new
  def bool; BLAH end
end
###**** isa
module Any; end
class Object; include Any; end
module Bool; end
class TrueClass; include Bool end
class FalseClass; include Bool end
module Str; end
class String; include Str; end
module Num; end
module Int; include Num end
class Fixnum; include Int; end
class Float;  include Num; end
module Sub; end
module Block; end
module Code; end
class Proc;  include Code; end
module NotAModuleHack; end
class Object
  # also used by Array/List isaC
  def isa_hlp(t) t.is_a?(Module) ? t : begin eval("#{t}")
                                       rescue Exception; NotAModuleHack end 
  end
  def isaC;->(_E,s){ self.is_a?(isa_hlp(s)) }end
end
class BindBox; def isaC;->(_E,s){ @__getobj__.isaC.call(_E,s) }end end
###**** goto
class Method; def gotoC;->(*args){ call(*args) }end end
class Proc; def gotoC;->(*args){ call(*args) }end end
$scalar_ERROR = pundef
###**** arity
module Code; def arityC;->(_E){ self.arity }end end
 #... but sub() procs all have *args so -1 arity.
 # So need to wrap and label them.  Something for another time.
###**** Range
class Range
  def [](i) self.to_a[i] end
  def to_i; self.to_a.size end
end
###**** List
# Object#listy? is above.
class Array
  def exists?(n) n >= 0 && n < self.size end
  def listyconcat(*as) ListyThing.new(self,*as) end
  def delisty
    if find{|e| e.listy?} 
      a = []
      each{|e| e.listy? ? a.push(*e.to_a) : a.push(e)}
      a
    else self end
  end
end
module ListyThingM; end
class Listy
  def listy?; true end
  include ListyThingM
  def self.[](*elems) new(*elems) end
  attr_accessor :parts,:offsets
  def initialize(*elems)
    @parts = []
    normal = nil
    elems.each{|e|
      if e.kind_of?(ListyThingM)
        @parts.push(normal) if normal; normal = nil
        @parts.push(e)
      else
        normal ||= []
        normal.push(e)
      end
    }
    @parts.push(normal) if normal
    @parts.push([]) if @parts.empty?
    @offsets = [0]
  end
  def [](n)
    pi,off = _find_index(n)
    pi ? parts[pi][off] : nil
  end
  def []=(n,v)
    pi,off = _find_index(n)
    pi ? (parts[pi][off] = v) : nil
  end
  def size
    if parts.size > offsets.size
      off = offsets[-1]
      (offsets.size..(parts.size-1)).each{|pi|
        off = offsets[pi] = off + parts[pi-1].size
      }
    end
    offsets[-1] + parts[-1].size
  end
  def _find_index(n)
    if n < 0
      npos = size+n
      return npos < 0 ? nil : self._find_index(size+n)
    end
    pi = @offsets.size-1
    pi -= 1 while @offsets[pi] > n
    while true
      off = n - @offsets[pi]
      return [pi,off] if (@parts.size == @offsets.size ||
                          @parts[pi].exists?(off))
      @offsets[pi+1] = @offsets[pi] + @parts[pi].size
      pi += 1
    end
  end
  def exists?(n) n >= 0 && n < size end #X
  def listyconcat(*as) self.class.new(self,*as) end
  def each(&b) parts.each{|p| p.each(&b)};self end
  def to_a; a=[]; each{|e| a.push(e)}; a end
  def map(&b) to_a.map(&b) end
end
###**** Array
module ArrayM
  def get(k) BindBoxKeyed.new(self,k) end
  def elemsC;->(_E){ self.size }end
  def endC;->(_E){ self.size-1 }end
  def chompC;->(_E){ self.map{|e|e.chomp} }end
  def pushC;->(_E,*args){
      a = args.delisty
      a = a[0] if a.size == 1 && a[0].is_a?(Array) #X!
      self.push(*a)
    }end
  def unshiftC;->(_E,*args){
      a = args.delisty
      a = a[0] if a.size == 1 && a[0].is_a?(Array) #X!
      self.unshift(*a)
    }end
  def shiftC;->(_E){ self.shift() || pundef }end
  def popC;->(_E){ self.pop() || pundef }end
  def isaC;->(_E,s){
      (s == List || s == 'List' ||
       s == Array || s == 'Array' ||
       self.is_a?(isa_hlp(s)))
    }end
  def existsC;->(_E,k){
      k = size+k if k < 0
      k >= 0 && exists?(k)
    }end
  def sortC;->(_E){
      sort{|a,b| a.as_s <=> b.as_s}
    }end
  def joinC;->(_E,s){
      map{|e|e.as_s}.join(s.as_s)
    }end
  def mapC;->(_E,b){
      map{|e| b.call(e)} #X?
    }end
  def deleteC;->(_E,*ks){
      ks.map{|k|
        e = self[k];
        (k == -1 || k == size - 1) ? delete_at(k) : (self[k] = pundef);
        e
      }
    }end
  def keysC;->(_E){(0..(size-1)).to_a}end
  def valuesC;->(_E){dup}end
  def kvC;->(_E){a=[];each_with_index{|v,k|a.push(k,v)};a}end
  def pairsC;->(_E){a=[];each_with_index{|v,k|a.push(Pair.new(k,v))};a}end
end
List = Listy
class Listy
  include ArrayM
end
class Array
  include ArrayM
end
class Range
  include ArrayM
  def size; self.to_a.size end
end
###**** Hash
class Hash
  def get(k) BindBoxKeyed.new(self,k) end
  def existsC;->(_E,k){
      self[k] && true
    }end
  def pairsC;->(_E){
      map{|k,v|Pair.new(k,v)}
    }end
  def deleteC;->(_E,*ks){
      ks.map{|k| e = delete(k); e || pundef}
    }end
  def keysC;->(_E){keys}end
  def valuesC;->(_E){values}end
  def kvC;->(_E){a=[];each{|k,v|a.push(k,v)};a}end
  def sortC;->(_E){
      sort{|a,b| a[0].as_s <=> b[0].as_s}
    }end
end
###**** Pair
class Pair
  attr_accessor :name,:value
  def initialize(name,value) @name,@value=name,value end
  def nameC;->(_E){ @name }end
  def keyC;->(_E){ @name }end
  def valueC;->(_E){ @value }end
  def keysC;->(_E){ [@name] }end
  def valuesC;->(_E){ [@value] }end
  def kvC;->(_E){ [@name,@value] }end
  def pairsC;->(_E){[self]}end
  def <=>(o) c = name <=> o.name; c != 0 ? c : value <=> o.value end
end
class WrappedPair
  attr_accessor :pair
  def initialize(pair) @pair=pair end
  def unwrap; @pair end
  def wrapped_pair?; true end
  def __getobj__; @pair end
end
###**** smartmatch
class P6
#  multi(:smartmatch,Object,Object){|a,b| a == b}
#  multi(:aaa,Object){ 42 }
#  fail('assert') if aaa(3) != 42
#  p self,"===================="
end
###**** ...

class Fixnum; def hex; "#{self}".hex end end

class Fixnum; def as_n; self end end
class Float; def as_n; self end end
class String
  def as_n
    case self
    when /^\+?Inf(inity)?$/; Inf
    when /^\-Inf(inity)?$/; -Inf
    when 'NaN'; NaN
    when /^(0x[0-9a-f]+)/i; eval($1)
    when /^(0o[0-7]+)/i; eval($1)
    when /\.|^[0-9]+e/; f = to_f; f == f.floor ? f.to_i : f
    else to_i end
  end
end
class Float
  def as_s
    if nan?; 'NaN'
    elsif infinite?; self < 0 ? '-Inf' : 'Inf'
    else self == floor ? to_i.to_s : to_s
    end
  end
end
class Fixnum
  def infinite?; false end
  def nan?; false end
end
Kernel::Inf = 1.0/0
Kernel::NaN = 0.0/0
class Range; def as_n; to_a.size end end
class Array; def as_n; size end end
class Listy; def as_n; size end end
class Hash; def as_n; size end end
  
class Object; def asC;->(_S,f){sprintf(f,self)}end end
class String
  def asC;->(_S,f){
      begin
        sprintf(f,self)
      rescue ArgumentError
        sprintf(f,self.to_f)
      end
    }
  end
  def lcfirstC;->(_S){gsub(/^./){|c|c.downcase}} end
  def lcC;->(_S){downcase} end
  def ucfirstC;->(_S){gsub(/^./){|c|c.upcase}} end
  def ucC;->(_S){upcase} end
  def capitalizeC;->(_S){gsub(/\w+/){|w|w.capitalize}} end
  def bytesC;->(_S){length} end
  def charsC;->(_S){length} end
  def indexC;->(_S,ss,from=0){index(ss,from)||-1} end
  def rindexC;->(_S,ss,from=0){rindex(ss,from)||-1} end
  def chompC;->(_S){chomp} end
  def transC;->(_S,*m){s=self;m.each{|p|s = s.tr(p.name,p.value)};s} end
end
class Array; def asC;->(_S,f,j){ self.map{|e|e.asC.call(_S,f)}.join(j) }end end
class Listy; def asC;->(_S,f,j){ self.map{|e|e.asC.call(_S,f)}.join(j) }end end
class Hash; def asC;->(_S,f,j){ self.map{|k,v|sprintf(f,k,v)}.join(j) }end end
class Pair; def asC;->(_S,f){ sprintf(f,name,value) }end end

require 'rbconfig' # for Config::CONFIG

class Object; def self.newC;->(_S,_self,*a){ new(*a) }end end

###** main
#print $grammar.parse.search((" "*1000)+"4").inspect

# $P.eval6q($six_rt) if not (ENV['TEST_ONLY_PARSE'] && ENV['TEST_ONLY_PARSE'] != 0)

def main
  if ARGV.empty?
    $P.repl
  else
    #STDERR.print ARGV.inspect,"\n\n"
    exec("perl",*ARGV) if ARGV[0] == '-le' # pugs "make test" :/
    ARGV.shift if ARGV[0] == '-w'
    if ARGV[0] == '-c'
      $P.compile_file(ARGV[1],ARGV[2])
    elsif ARGV[0] == '-e'
      $P.eval6(ARGV[1])
    else
      print "Parseing #{ARGV[0]}\n"
      $P.eval6_file(ARGV[0])
    end
  end
end

_END
);
;
source_perl_compiled(0,'',<<'_END'
#say("Welcome to red clam.")
sub abs($n=$+_) { $n.rUBYabs }
sub sign($n) { $n <=> 0 }
sub rand($n=1) { rUBYrand($n.rUBYas_i) }
sub exp($n) { Rb::Math.rUBYexp($n.rUBYto_f) }
sub sqrt($n) { Rb::Math.rUBYsqrt($n.rUBYto_f) }
sub log($n) { Rb::Math.rUBYlog($n.rUBYto_f) }
sub log10($n) { Rb::Math.rUBYlog10($n.rUBYto_f) }
sub int($x) {
  my $n = $x.rUBYas_n;
  if $n.rUBYinfinite_P { $n }
  elsif $n.rUBYnan_P { $n }
  elsif $n >= 0 { $n.rUBYfloor }
  else { $n.rUBYceil }
}
sub shift($o){$o.shift}
sub pop($o){$o.pop}
sub isa($o,$t){$o.isa($t)}
sub ref($o){$o.ref}
sub push($o,*@a){$o.push(*@a)}
sub unshift($o,*@a){$o.unshift(*@a)}
sub eval($code){rUBYeval6q($code)}
sub sprintf($f,*@a){rUBYsprintf($f,*@a)}
sub hash(*@a){ my %h = *@a; %h }
sub elems($a){ $a.elems }
sub end($a){ $a.end }
sub undefine($x){$x = undef}
sub undef(){rUBYpundef}
sub lcfirst($s=$+_){$s.lcfirst}
sub lc($s=$+_){$s.lc}
sub ucfirst($s=$+_){$s.ucfirst}
sub uc($s=$+_){$s.uc}
sub capitalize($s=$+_){$s.capitalize}
sub bytes($s=$+_){$s.bytes}
sub chars($s=$+_){$s.chars}
sub index($s=$+_,$ss,$from=0){$s.index($ss,$from)}
sub rindex($s=$+_,$ss,$from=0){$s.rindex($ss,$from)}
#sub chomp($a){ $a.chomp }
sub chomp($s=$+_){$s.chomp}
sub trans($s,*@m){$s.trans(*@m)}
sub keys($o){$o.keys}
sub values($o){$o.values}
sub kv($o){$o.kv}
sub sort($o){$o.sort}
sub pi(){raw_rUBY('Math::PI')}
sub sin($n){raw_rUBY('Math.sin(nS.as_n)')}
sub cos($n){raw_rUBY('Math.cos(nS.as_n)')}
sub tan($n){raw_rUBY('Math.tan(nS.as_n)')}
sub asin($n){raw_rUBY('Math.asin(nS.as_n)')}
sub acos($n){raw_rUBY('Math.acos(nS.as_n)')}
sub atan($n,$n2=undef){
  $n2 ?? raw_rUBY('Math.atan2(nS.as_n,n2S.as_n)') !! raw_rUBY('Math.atan(nS.as_n)') }
our $*OS = raw_rUBY('Config::CONFIG["host_os"]');
our $*OSVER = undef;
our $*PERVER = "5.13.0";
our $*PUGS_BACKEND = "BACKEND_PUGS"; # lie for tests :(
our @*ARGS = raw_rUBY('ARGV');
#our %*ENV = raw_rUBY('ENV');
our $*IN  = raw_rUBY('STDIN');
our $*OUT = raw_rUBY('STDOUT');
our $*ERR = raw_rUBY('STDERR');
our $*PID = raw_rUBY('Process.pid');
our $*UID = raw_rUBY('Process.uid');
our $*GID = raw_rUBY('Process.gid');
say "Welcome! :)"
_END
);
;
source_perl_finish();
source_ruby(<<'_END'
main
_END
);
;
source_ruby(<<'_END'
# (setq ruby-here-doc-beg-re "<<\\(-\\)?\\(\\([a-zA-Z0-9][a-zA-Z0-9_]*\\)\\|[\"]\\([^\"]+\\)[\"]\\|[']\\([^'_][^']*\\)[']\\)")
###* Footer
#; Local Variables:
#; mode: outline-minor
#; outline-regexp: "###[*]+"
#; ruby-indent-level: 2
#; perl-indent-level: 2
#; perl-continued-statement-offset: 2
#; perl-continued-brace-offset: -2
#; indent-tabs-mode: nil
#; End:
#; vim: shiftwidth=2:
_END
);
