class GenMachine::SpecParser

This is a quick and dirty parser used for bootstrapping. Which means it'll eventually be replaced when the real parser is written as a genmachine table.

Constants

ESCAPES

Public Class Methods

new(files,opts) click to toggle source
# File lib/genmachine/spec_parser.rb, line 6
def initialize(files,opts)
  @table = []
  @files = files
  @opts = opts
end

Public Instance Methods

build() click to toggle source
# File lib/genmachine/spec_parser.rb, line 12
def build
  c_name = c_type = c_args = c_cmds = c_first_state = c_states = nil
  new_fun = false
  @files.each do |fname|
    File.new(fname,'r').each_with_index do |line, line_no|
      line = line.strip
      det = line[0..0]
      if det == '|' or det == ':'
        re = (det=='|' ? '\|' : det) + '(?: |$)'
        cols = line.split(/#{re}/,-1)[1..-1].map(&:strip)
        if det=='|' && cols[0].include?('(')
          new_fun = true
          unless c_name.nil?
            @table << [c_name, c_type, c_args, c_cmds, c_first_state, process_states(c_states)]
          end
          parts = cols[0].split('(')
          c_name = parts.shift.to_underscored
          c_args, c_type = parts.join('(').split(/::(U|\[\]|\{\})$/)
          c_args = c_args[0..-2].split(',')
          c_type ||= '[]'
          c_states = []
          c_cmds = (cols[3]||'').split(';')
          c_first_state = cols[4]
          # TODO: error if cols[1] or cols[2] have anything
        elsif det == ':' && new_fun
          c_args += cols[0].sub(/\)$/,'').split(',')
          c_cmds += (cols[3]||'').split(';')
          c_first_state += (cols[4]||'')
        elsif det == '|'
          new_fun = false
          conditionals, inputs = parse_input(cols[1])
          c_states << {:name  => cols[0],
                       :input => inputs,
                       :cond  => conditionals,
                       :acc   => cols[2],
                       :exprs => (cols[3]||'').split(';').map(&:strip),
                       :next  => cols[4]}
        elsif det == ':' && (c_states.size > 0)
          conditionals, inputs = parse_input(cols[1],c_states[-1][:input])
          c_states[-1][:name] += (cols[0]||'')
          c_states[-1][:input] = inputs
          c_states[-1][:cond] += conditionals
          c_states[-1][:acc]  += cols[2]
          c_states[-1][:exprs]+= (cols[3]||'').split(';').map(&:strip)
          c_states[-1][:next] += cols[4]
        end
      end
    end
    unless c_name.nil?
      @table << [c_name, c_type, c_args, c_cmds, c_first_state, process_states(c_states)]
    end
  end
  if @opts[:debug]
    require 'pp'
    pp @table
  end
  return @table
end
parse_combine_ranges(raw, input) click to toggle source
# File lib/genmachine/spec_parser.rb, line 118
def parse_combine_ranges(raw, input)
  raw.gsub!(/\\[tnrfbaes\\]/){|m| ESCAPES[m]}
  raw.gsub!('<left-square-bracket>', '[')
  raw.gsub!('<right-square-bracket>', ']')
  if raw =~ /((?:.-.)*)((?:.)*)/um
    ranges = $1
    singles = $2
    if ranges.length > 0
      _, range, ranges = ranges.partition /.-./um
      input << range
    end while ranges.length > 0
    singles.scan(/./um).each{|s| input << s}
  end
end
parse_input(val,inputs=nil) click to toggle source
# File lib/genmachine/spec_parser.rb, line 83
def parse_input(val,inputs=nil)
  iters = 0
  conds = []
  val.gsub! /([^\\])\\\[/, '\1<left-square-bracket>'
  val.gsub! /([^\\])\\\]/, '\1<right-square-bracket>'
  while val.strip.length > 0 && iters < 100
    case
    when val =~ /--+/um
      val.sub!($&,'')
    when val =~ /\s*\{([^\}]+)\}\s*/um
      conds << $1
      val.sub!($&, '')
    when val =~ /\s*\[\^([^\]]+)\]\s*/um
      inputs ||= CharSet.new(:exclude)
      parse_combine_ranges($1, inputs)
      val.sub!($&, '')
    when val =~ /\s*\[([^\]]+)\]\s*/um
      inputs ||= CharSet.new(:include)
      parse_combine_ranges($1, inputs)
      val.sub!($&, '')
    when val =~ /^\s*\./um
      inputs ||= CharSet.new(:include)
      inputs << :any
      val.sub!($&, '')
    end
    iters += 1
  end
  return conds, inputs
end
process_states(instates) click to toggle source

consolidate same-name states and (eventually) combine / optimize where appropriate.

# File lib/genmachine/spec_parser.rb, line 73
def process_states(instates)
  outstates = {}
  instates.each do |inst|
    name = inst.delete(:name)
    outstates[name] ||= []
    outstates[name] << inst
  end
  return outstates
end