module Yagg class ::String alias to_text to_s end class Generator attr_accessor :result def initialize(input) @input = input @indent = 0 self.result = "" end def generate_lexels @input.tokens.each{|x| `#{x.upcase} = Class.new(Lexel)\n` } end private def rule_name_case(str) str.capitalize.gsub(/_([a-z])/){ $1.upcase } end public 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 def `(text) @result << make_indent(@indent, text) end def make_indent(num, text) text.split(/(\n)/).map{|x| " "*num << x}.join end def indent(a = 0) @indent += a end 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 def generate(under = "GG") `module #{under}\n Scope = self\n` indent 2 generate_header generate_lexels generate_rules indent -2 `end\n` end end end