Skip to content

Instantly share code, notes, and snippets.

@pumbur
Last active August 29, 2015 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pumbur/9752245 to your computer and use it in GitHub Desktop.
Save pumbur/9752245 to your computer and use it in GitHub Desktop.
henna origin
require 'pp'
require 'oj'
class Object
def ary?; self.is_a?(Array); end
def hsh?; self.is_a?(Hash); end
def sym?; self.is_a?(Symbol); end
def str?; self.is_a?(String); end
def prc?; self.is_a?(Proc); end
def num?; self.is_a?(Numeric); end
def lap; yield self; end
def laps(*b); b.inject(self){|s,b| b[s] }; end
def to_sym; self.to_s.to_sym; end
def to_f; self.to_s.to_f; end
end
class Numeric
def neg?; self < 0; end
def pos?; self > 0; end
def int?; (self%1).zero?; end
def inv; self * -1; end
def neg; self.pos? ? self.inv : self; end
def pos; self.neg? ? self.inv : self; end
def inc; self + 1; end
def dec; self - 1; end
end
class Array
def head; self[0]; end
def rest; self[1..-1]; end
def products; empty? ? [] : self.head.product(*rest); end
def slices(n=2); self.each_slice(n).to_a; end
end
class Hash
def map(&b); Hash[super{|k,v| [b[k,false], b[v,true]] }]; end
def select(&b); Hash[super(&b)]; end
def -(o); self.select{|k,v| o.hsh? ? (o[k] != v) : (not o.include?(k)) }; end
def vats(*a); self.values_at(*a); end
end
require "#{__dir__}/common.rb"
require 'parslet'
EasyParser =->(q){
sym =->(i){ i.gsub(/#([^#\n]+)/," ").gsub(/([()])/, ' \1 ').split }
ast =->(i,*o){ (t=i.pop).nil? ? o : t=='(' ? [i,o] : ast[*(t==')' ? ast[i] : [i,t.to_sym])+o] }
ast[sym[q]]
}
class Parser < Parslet::Parser
TokenRegex = /[^ \t\r\n\f\\.…()\[\]{}<>#"\'~,=]/
root :root
rule(:eol ){ str("\n") }
rule(:eof ){ any.absent? }
rule(:spc ){ match('\s').repeat(1) }
rule(:spc?){ match('\s').repeat }
rule(:cmt_){ (cmt2 | cmt1).as(:cmt) }
rule(:cmt1){ str('#').as(:type) >> ((eol | str('#')).absent? >> any).repeat.as(:data) >> str('#').maybe }
rule(:cmt2){ str('###').as(:type) >> match('[ \t]*\n').maybe >> (str('###').absent? >> any).repeat.as(:data) >> match('\n[ \t]*').maybe >> (eof | str('###')) }
rule(:str_){ (str1 | str2).as(:str) }
rule(:str1){ str("'").as(:type) >> ( str('\\') >> any | str("'").absent? >> any ).repeat.as(:data) >> str("'") }
rule(:str2){ str('"').as(:type) >> ( str('\\') >> any | str('"').absent? >> any ).repeat.as(:data) >> str('"') }
rule(:tok ){ ((str('\\') >> any) | match(TokenRegex)).repeat(1).as(:tok) }
rule(:exp ){ (str('(') >> all >> str(')')).as(:exp) }
rule(:lst ){ (str('[') >> all >> str(']')).as(:lst) }
rule(:act ){ (str('{') >> all >> str('}')).as(:act) }
rule(:kin ){ (str('<') >> all >> str('>')).as(:kin) }
rule(:esc ){ (lv1 >> str('~')).as(:esc) }
rule(:bat ){ lv2.repeat(2).as(:bat) }
rule(:rng ){ (lv3.maybe.as(:fr) >> (str('...') | str('..') | str('…')).as(:type) >> lv3.maybe.as(:to)).as(:rng) }
rule(:tau ){ (lv4 >> (str(",") >> lv4).repeat(1)).as(:tau) }
rule(:rel ){ ((tau | lv4).maybe.as(:key) >> str('=') >> (tau | lv4).maybe.as(:val)).as(:rel) }
rule(:lv1 ){ str_ | exp | lst | act | kin | tok }
rule(:lv2 ){ esc | lv1 }
rule(:lv3 ){ bat | lv2 }
rule(:lv4 ){ rng | lv3 }
rule(:lv5 ){ cmt_ | rel | lv4 }
rule(:all ){ spc? >> (lv5 >> (spc >> lv5).repeat).maybe.as(:data) >> spc? }
rule(:root){ (spc >> lv5).repeat >> spc? }
end
class Transformer < Parslet::Transform
Act = :'act:syn'
rule(:rng => subtree(:x)){ [:range, (x[:fr] ? x[:fr] : :nil), (x[:to] ? x[:to] : :nil).lap{|q| (x[:type].size == 2) ? [[:dec, q]] : q }] }
rule(:cmt => subtree(:x)){ [:comment, [:str, x[:data].to_s]] } # x[:type]
rule(:str => subtree(:x)){ [:str, x[:data].to_s] } # x[:type]
rule(:tok => simple(:x) ){ x.to_s.gsub(/\\([^\\])/,'\1').to_sym }
rule(:exp => subtree(:x)){ x[:data].nil? ? :nil : x[:data] }
rule(:kin => subtree(:x)){ [:kin, *x[:data]] }
rule(:lst => subtree(:x)){ [:lst, *(x[:data].nil? ? [:nil] : x[:data])] }
rule(:esc => subtree(:x)){ [:escape, x] }
rule(:bat => subtree(:x)){ x.inject(){|a,b| (b.is_a?(Symbol) || (b[0] == :kin)) ? [b, a] : (b[0] == :lst) ? [:at, a, *b[1..-1]] : (b[0] == :str) ? [:mold, b, a] : (b[0] == Act) ? [Act, b[1].insert(1, a)] : b.insert(1, a) }}
rule(:tau => subtree(:x)){ [:enum, *x] }
rule(:rel => subtree(:x)){ [:pair, (x[:key].nil? ? :nil : x[:key]), (x[:val].nil? ? :nil : x[:val])] }
rule(:act => subtree(:x)){ x[:data].nil? ? [:act] : [Act, x[:data]] }
end
FineParser =->(files){
parser =->(data){ Transformer.new.apply(Parser.new.parse("\n#{data}")) }
getnam =->(q){ q.ary? ? getnam[q[0]] : q }
dooify =->(i){ (i.size == 1) ? i[0] : [:do, *i] }
enumfy =->(i){ (i.ary? && (i[0] == :enum)) ? i[1..-1] : [i] }
parify =->(i,o={}){
return [[i], o] if not i.ary?
return [[i.inject([]){|r,n| parify[n].lap{|a,b| o.merge!(b); r += a }}], o] if i[0] != :pair
a, b = enumfy[i[1]], enumfy[i[2]]; b, a = b.fill(b[-1], b.size...a.size), a[0..b.size]
[a, Hash[[a,b].transpose.map{|a,b| (a.ary? && (a[0] == :escape)) ? [a[1], [:kin,:lst,[:pair,:of,b]]] : [a, b] }]]
}
cmntls =->(a){ a.ary? ? a.reject{|q| q[0] == :comment }.map(&cmntls) : a }
synact =->(a,o=nil){
if a.ary? && (getnam[a] == :'act:syn') then [a[1..-1].map{|q| synact[q,{}] }.transpose.lap{|a,b| [:act, [:lst, *b.inject(&:merge).sort{|a,b| a[0] <=> b[0] }.map{|a| [:pair, *a] }], dooify[a]] }]
elsif o.nil? then [(a.ary? ? a.map{|q| synact[q,nil][0] } : a), o]
elsif not a.ary? then a.to_s.start_with?('$') ? [a, {a=>:any}] : [a,o]
else a.map{|q| synact[q,{}] }.transpose.lap{|a,b| [a, b.inject(&:merge)] }
end
}
files.flat_map{|f|
open(f).read.laps(parser, cmntls, synact)[0].flat_map{|n|
((n[0] == :let) ? [[n[1], dooify[n[2..-1]]]] : (n[0] == :lets) ? n.rest.slices : []).map{|a,b|
parify[a].lap{|a,h| [(a[0].ary? ? a[0] : a), h, (b.ary? ? b : [b])] }
}}}
}
#File.write("#{__dir__}/data.json", Oj.dump(FineParser[Dir['**/**.hna'].reject{|f| (not File.file?(f)) || (f =~ /^(bin|usr|tmp)/) }]))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment