class Yagg::Generator

Attributes

result[RW]

Public Class Methods

new(input) click to toggle source
# File lib/yagg/gen.rb, line 7
def initialize(input)
  @input = input
  @indent = 0
  self.result = ""
end

Public Instance Methods

`(text) click to toggle source
# File lib/yagg/gen.rb, line 35
def `(text)
  @result << make_indent(@indent, text)
end
generate(under = "GG") click to toggle source
# File lib/yagg/gen.rb, line 185
def generate(under = "GG")
  `module #{under}\n  Scope = self\n`      
  indent 2
    generate_header
    generate_lexels
    generate_rules
  indent -2
  `end\n`
end
generate_header() click to toggle source
# File lib/yagg/gen.rb, line 47
    def generate_header
      <<~`EOF`
        class Lexel
          attr_accessor :value
          def values
            [value]
          end
          def typeid
            -1
          end
          def initialize(value)
            raise TypeError unless self.class.check(value)
            if self.class === value
               self.value = value.value
            elsif Lexel === value || Rule === value
               raise TypeError   
            else
               self.value = value
            end
          end 
          def self.check(value)
            if Lexel === value || Rule === value
               return self === value
            end
            true
          end
          def to_text
            value.to_s
          end
        end

        Unchecked = Struct.new(:values, :typeid)
            
        class Rule
          attr_accessor :typeid, :values
          def to_text
            values.map{|x| x.to_text}.join
          end
          def initialize(*values)
            if values.length == 1 && Unchecked === values[0]
              self.values   = values[0].values
              self.typeid = values[0].typeid
            else
              old = values
              typeid, values = self.class.check(values)
              raise TypeError, "\#{self.class.to_s}:can't find rule for \#{old}" unless typeid
              if typeid == -1
                 self.values = values[0].values
                 self.typeid = values[0].typeid
              else
                 self.values   = values
                 self.typeid = typeid
              end
            end
          end 

          def self.grammel(a)
             case a
             when /^[A-Z]/ then Scope.const_get(a.upcase)
             when /^[a-z]/ then Scope.const_get(a.capitalize.gsub(/_([a-z])/){ $1.upcase })
             when /^'(.)'/ then $1
             else
               raise TypeError, "unknown grammar token \#{a}"
             end
          end

          def self.check_single(pattern, values)
             i = 0
             j = 0
             output = []
             while i < values.length && j < pattern.length
               u = grammel(pattern[j])
               if Class === u
                 output << u.new(values[i])
               else
                 return nil if u != values[i]
                 output << values[i]
               end
               i += 1
               j += 1               
             end
             output
          rescue TypeError
          end

          def to_text
            self.values.map{|x| x.to_text}.join       
          end

          def self.check_one(value, memo = {})
             self::Includes.each{|x|
               next if memo[x]
               u = grammel(x)
               case u
               when String
                  return value if u == value
               when lambda{|x| x < Lexel}
                    return u.new(value) if u.check(value)
               else
                  if u === value
                    return u.new(Unchecked.new([value], -1))
                  else
                    r = u.check_one(value, memo.update({u: 1}))
                    return self.new(Unchecked.new([r], -1)) if r
                  end
               end
             }
             nil
          end

          def self.check(values)
            if values.length == 1 
              if self === values[0]
                return -1, values
              elsif (r = check_one(values[0], {}))
                if self === r
                  return -1, r.values
                else
                  return -1, [r]
                end
              else 
                return nil
              end
              return nil
            end
            
            self::Rules.each_with_index{|k, i|
               next if k.size != values.size
               r = check_single(k, values)
               return i, r if r
            }
               
            nil
          end
        end
      EOF
    end
generate_lexels() click to toggle source
# File lib/yagg/gen.rb, line 13
def generate_lexels
  @input.tokens.each{|x|
    `#{x.upcase} = Class.new(Lexel)\n`
  }
end
generate_rules() click to toggle source
# File lib/yagg/gen.rb, line 26
def generate_rules
  @input.rules.each{|k, v|
    `class #{rule_name_case(k)} < Rule\n`
    `  Rules      = #{v.select{|x| x.size != 1}.inspect}\n`
    `  Includes   = #{v.select{|x| x.size == 1}.flatten}\n`
    `end\n`
  }
end
indent(a = 0) click to toggle source
# File lib/yagg/gen.rb, line 43
def indent(a = 0)
  @indent += a
end
make_indent(num, text) click to toggle source
# File lib/yagg/gen.rb, line 39
def make_indent(num, text)
  text.split(/(\n)/).map{|x| " "*num << x}.join
end

Private Instance Methods

rule_name_case(str) click to toggle source
# File lib/yagg/gen.rb, line 20
def rule_name_case(str)
  str.capitalize.gsub(/_([a-z])/){ $1.upcase }
end