class Rexical::Generator


Constants

REX_HEADER

REX_STUB
REX_UTIL

Attributes

class_name[RW]
debug[RW]
exclusive_states[RW]
grammar_file[RW]

grammar_lines[RW]
ignorecase[RW]
independent[RW]
lineno[RW]
module_name[RW]
rules[RW]
scanner_file[RW]

Public Class Methods

new(opts) click to toggle source

# File lib/rexical/generator.rb, line 34
def initialize(opts)
  @lineno  =  0
  @macro  =  {}
  @rules  =  []
  @exclusive_states = [nil]
  @grammar_lines  =  nil
  @scanner_header  =  ""
  @scanner_footer  =  ""
  @scanner_inner  =  ""
  @opt  =  opts
end

Public Instance Methods

add_header( st ) click to toggle source

# File lib/rexical/generator.rb, line 47
def add_header( st )
  @scanner_header  +=  "#{st}\n"
end
add_inner( st ) click to toggle source

# File lib/rexical/generator.rb, line 57
def add_inner( st )
  @scanner_inner  +=  "#{st}\n"
end
add_macro( st ) click to toggle source

# File lib/rexical/generator.rb, line 77
def add_macro( st )
  ss  =  StringScanner.new(st)
  ss.scan(%r\s+/)
  key = ss.scan(%r\S+/)
  ss.scan(%r\s+/)
  st = ss.post_match
  len  =  st.size
  ndx  =  0
  while ndx <= len
    c  =  st[ndx,1]
    ndx  +=  1
    case  c
    when '\'
      ndx  +=  1
      next
    when '#', ' '
      ndx  -=  1
      break
    end
  end
  expr = st[0,ndx]
  expr.gsub!('\ ', ' ')
  key  =  '{' + key + '}'
  @macro.each_pair do |k, e|
    expr.gsub!(k) { |m| e }
  end
  @macro[key]  =  expr
rescue
  raise ParseError, "parse error in add_macro:'#{st}'"
end
add_option( st ) click to toggle source

# File lib/rexical/generator.rb, line 62
def add_option( st )
  opts = st.split
  opts.each do |opt|
    case opt
    when %rignorecase/
      @opt['--ignorecase'] = true
    when %rstub/
      @opt['--stub'] = true
    when %rindependent/
      @opt['--independent'] = true
    end
  end
end
add_rule( rule_state, rule_expr, rule_action=nil ) click to toggle source

# File lib/rexical/generator.rb, line 109
def add_rule( rule_state, rule_expr, rule_action=nil )
  st = rule_expr.dup
  @macro.each_pair do |k, e|
    rule_expr.gsub!(k) { |m| e }
  end
  if rule_state.to_s[1,1] =~ %r[A-Z]/
    @exclusive_states << rule_state  unless @exclusive_states.include?(rule_state)
    exclusive_state = rule_state
    start_state = nil
  else
    exclusive_state = nil
    start_state = rule_state
  end
  rule = [exclusive_state, start_state, rule_expr, rule_action]
  @rules << rule
rescue
  raise ParseError, "parse error in add_rule:'#{st}'"
end
next_line() click to toggle source
# File lib/rexical/generator.rb, line 132
def next_line
  @lineno += 1
  @grammar_lines.scan_until(%r\n/).chomp
rescue
  nil
end
parse() click to toggle source
# File lib/rexical/generator.rb, line 139
def parse
  state1  =  :HEAD
  state2  =  nil
  state3  =  nil
  lastmodes  =  []
  while st = next_line
    case state1
    when :FOOT
      add_footer  st

    when :HEAD
      ss  =  StringScanner.new(st)
      if ss.scan(%rclass/)
        state1  =  :CLASS
        st  =  ss.post_match.strip
        @class_name  =  st
      else
        add_header  st
      end

    when :CLASS
      s = st.strip
      next  if s.size == 0 or s[0,1] == '#'

      ss  =  StringScanner.new(st)
      if ss.scan(%roption.*$/)
        state2 = :OPTION
        next
      end
      if ss.scan(%rinner.*$/)
        state2 = :INNER
        next
      end
      if ss.scan(%rmacro.*$/)
        state2 = :MACRO
        next
      end
      if ss.scan(%rrule.*$/)
        state2 = :RULE
        next
      end
      if ss.scan(%rend.*$/)
        state1 = :FOOT
        next
      end

      case state2
      when :OPTION
        add_option  st

      when :INNER
        add_inner  st

      when :MACRO
        add_macro  st

      when :RULE
        case state3
        when nil
          rule_state, rule_expr, rule_action  =  parse_rule(st)
          if rule_action =~ %r\s*\{/
            lastmodes = parse_action(rule_action, lastmodes)
            if lastmodes.empty?
              add_rule  rule_state, rule_expr, rule_action
            else
              state3  =  :CONT
              rule_action  +=  "\n"
            end
          else
            add_rule  rule_state, rule_expr
          end

        when :CONT
          rule_action  +=  "#{st}\n"
          lastmodes = parse_action(st, lastmodes)
          if lastmodes.empty?
            state3  =  nil
            add_rule  rule_state, rule_expr, rule_action
          else
          end

        end # case state3

      end # case state2

    end # case state1

  end # while

end
parse_action(st, lastmodes=[]) click to toggle source

# File lib/rexical/generator.rb, line 244
def parse_action(st, lastmodes=[])
  modes  =  lastmodes
  mode  =  lastmodes[-1]
  ss  =  StringScanner.new(st)
  until ss.eos?
    c  =  ss.scan(%r./)
    case  c
    when '#'
      if (mode == :brace) or (mode == nil)
        #p [c, mode, modes]
        return  modes
      end
    when '{'
      if (mode == :brace) or (mode == nil)
        mode = :brace
        modes.push  mode
      end
    when '}'
      if (mode == :brace)
        modes.pop
        mode = modes[0]
      end
    when "'"
      if (mode == :brace)
        mode = :quote
        modes.push  mode
      elsif (mode == :quote)
        modes.pop
        mode = modes[0]
      end
    when '"'
      if (mode == :brace)
        mode = :doublequote
        modes.push  mode
      elsif (mode == :doublequote)
        modes.pop
        mode = modes[0]
      end
    when '`'
      if (mode == :brace)
        mode = :backquote
        modes.push  mode
      elsif (mode == :backquote)
        modes.pop
        mode = modes[0]
      end
    end
  end
  #p [c, mode, modes]
  return  modes
end
parse_rule(st) click to toggle source

# File lib/rexical/generator.rb, line 231
def parse_rule(st)
  st.strip!
  return  if st.size == 0 or st[0,1] == '#'
  ss  =  StringScanner.new(st)
  ss.scan(%r\s+/)
  rule_state  =  ss.scan(%r\:\S+/)
  ss.scan(%r\s+/)
  rule_expr  =  ss.scan(%r\S+/)
  ss.scan(%r\s+/)
  [rule_state, rule_expr, ss.post_match]
end
read_grammar() click to toggle source
# File lib/rexical/generator.rb, line 128
def read_grammar
  @grammar_lines = StringScanner.new File.read(grammar_file)
end
write_scanner(f = scanner_io) click to toggle source
# File lib/rexical/generator.rb, line 374
    def write_scanner f = scanner_io
      ## scan flag
      flag = ""
      flag += "i"  if @opt['--ignorecase']
      ## header
      f.printf REX_HEADER, Rexical::VERSION, grammar_file

      unless @opt['--independent']
        f.printf "require 'racc/parser'\n"
      end

      @scanner_header.each_line do |s|
        f.print s
      end
      if @opt['--independent']
        f.puts "class #{@class_name}"
      else
        f.puts "class #{@class_name} < Racc::Parser"
      end

      ## utility method
      f.print REX_UTIL

      ## scanner method

      f.print "
  def next_token
    return if @ss.eos?
    
    # skips empty actions
    until token = _next_token or @ss.eos?; end
    token
  end

  def _next_token
    text = @ss.peek(1)
    @lineno  +=  1  if text == "\\n"
    token = case @state
"

    exclusive_states.each do |es|
      f.printf "    when #{es ? es.to_s : "nil"}
      case
"
      rules.each do |rule|
        exclusive_state, start_state, rule_expr, rule_action = *rule
        if es == exclusive_state

          if rule_action
            if start_state
              f.print "      when((state == #{start_state}) and (text = @ss.scan(/#{rule_expr}/#{flag})))
         action #{rule_action}

"
            else
              f.print "      when (text = @ss.scan(/#{rule_expr}/#{flag}))
         action #{rule_action}

"
            end
          else
            if start_state
              f.print "      when (state == #{start_state}) and (text = @ss.scan(/#{rule_expr}/#{flag}))
        ;

"
            else
              f.print "      when (text = @ss.scan(/#{rule_expr}/#{flag}))
        ;

"
            end
          end

        end
      end
      f.print "      else
        text = @ss.string[@ss.pos .. -1]
        raise  ScanError, "can not match: '" + text + "'"
      end  # if

"
    end
    f.print "    else
      raise  ScanError, "undefined state: '" + state.to_s + "'"
    end  # case state
"
    if @opt['--debug']
      f.print "    p token
"
    end
    f.print "    token
  end  # def _next_token

"

      ## inner method
      @scanner_inner.each_line do |s|
        f.print s
      end
      f.puts "end # class"

      ## footer
      @scanner_footer.each_line do |s|
        f.print s
      end # case

      ## stub main
      f.printf REX_STUB, @class_name, '"%s:%d:%s\n"'  if @opt['--stub']
      f.close

    end