root/misc/STD_red/prelude.rb

Revision 22332, 12.6 kB (checked in by putter, 4 months ago)

[STD_red] First rough draft of a dump format for Common Lisp.

  • Property svn:mime-type set to text/plain; charset=UTF-8
  • Property svn:eol-style set to native
Line 
1# -*- encoding: utf-8 -*-
2
3require 'strscan'
4
5require 'match'
6
7
8# Perl grammar helpers
9
10def say(*args); print *args;print "\n"; end
11
12class Object
13  def bool; true; end
14end
15class FalseClass; def bool; false; end; end
16class NilClass; def bool; false; end; end
17
18module Kernel
19  def my(v); end
20end
21
22class Grammar
23  attr_accessor :permit_partial_parse
24  def initialize(orig,at=0)
25    @scanner = StringScanner.new(orig)
26    @str = orig
27    @eat_cache = {}
28    @ws_from = @ws_to = false
29    @scanner.pos = at
30    if at != 0
31      @permit_partial_parse = true
32    end
33
34    # StringScanner uses byte offsets, not character offsets
35    # So we have to kludge around that...
36    @offset_of_pos = []
37    @offset_of_pos[0] = 0
38    s = StringScanner.new(orig)
39    chr = 1
40    while s.scan(/./um)
41      @offset_of_pos[s.pos] = chr
42      chr += 1
43    end
44  end
45  def pos; @scanner.pos; end
46  def pos=(to); @scanner.pos = to; end
47  def fail_at(n); @scanner.pos = n; false; end
48  def scan(re); @scanner.scan(re); end
49  def eat(str); @scanner.scan((@eat_cache[str] ||= Regexp.new(Regexp.quote(str)))); end
50  def let_pos(&blk)
51    b = @scanner.pos
52    v = blk.call()
53    @scanner.pos = b if not v
54    v
55  end
56  def rul(&blk)
57    let_pos{ wsp and v= blk.call() and wsp and v }
58  end
59   
60
61
62  def _match_from(from,h=nil,rule=nil)
63    h ||= {}
64    Match.new(@str,
65              (@offset_of_pos[from] or raise "bug"),
66              (@offset_of_pos[@scanner.pos] or raise "bug"),
67              true,h,nil,rule)
68  end
69  def _match_pat(re,rule=nil)
70    b = @scanner.pos
71    return false if !@scanner.scan(re)
72    _match_from(b,nil,rule)
73  end
74
75  def _line_and_indent_of_offset(off)
76    lines = @str.slice(0,off).split(/\n/)
77    sz = lines.length
78    if sz == 0
79      [1,0]
80    else
81      [sz,lines[-1].length]
82    end
83  end
84  def _picture_of_offset(off)
85    off = @offset_of_pos[off]
86    bot = off - 30;  bot = 0 if bot < 0
87    below = @str.slice(bot,off-bot)
88    above = @str.slice(off,30)
89    below = below.gsub(/\t/,'\t').gsub(/\n/,'\n')
90    above = above.gsub(/\t/,'\t').gsub(/\n/,'\n')
91    prefix = "WHERE:"
92    prefix1 = " "
93    indent = below.size > 0 ? prefix1+(" " * (below.size-1)) : ""
94    prefix+prefix1+below+above+"\n"+prefix+indent+"/\\<-- HERE\n"
95  end
96  def panic(msg)
97    line_num,char_in_line = _line_and_indent_of_offset(pos)
98    picture = _picture_of_offset(pos)
99    raise "panic at line #{line_num} column #{char_in_line} (pos #{pos}): #{msg}\n#{picture}"
100  end
101
102  def null; true; end
103
104  def before(re=nil,&blk)
105    if re
106      @scanner.check(re)
107    else
108      b = @scanner.pos
109      v = blk.call()
110      @scanner.pos = b
111      v ? true : false
112    end
113  end
114  def after(re)
115    # no look-behind in 1.8 :(
116    s = @str.slice(0,(@offset_of_pos[pos] or raise "bug"))
117    Regexp.new("#{re}\\z").match(s) ? true : false
118  end
119
120  def seqTOK(f=nil,&blk)
121    fun = f || blk
122    b = pos
123    v = fun.call() ? v : fail_at(b)
124  end
125
126  def quesTOK(f=nil,&blk)
127    fun = f || blk
128    v = fun.call()
129    v ? [v] : []
130  end
131  def starTOK(f=nil,&blk)
132    fun = f || blk
133    a = []
134    while v = fun.call(); a.push(v); end; a
135  end
136  def plusTOK(f=nil,&blk)
137    fun = f || blk
138    v = fun.call() or return false
139    a = [v]
140    while v = fun.call(); a.push(v); end; a
141  end
142
143  def quesRULE(f=nil,&blk)
144    fun = f || blk
145    wsp
146    v = fun.call(); wsp
147    v ? [v] : []
148  end
149  def starRULE(f=nil,&blk)
150    fun = f || blk
151    a = []
152    p = pos
153    wsp
154    while v = fun.call()
155      a.push(v); wsp
156      p1 = pos; break if not p < p1; p = p1
157    end
158    a
159  end
160  def plusRULE(f=nil,&blk)
161    fun = f || blk
162    wsp
163    v = fun.call() or return false
164    a = [v]
165    p = pos
166    wsp
167    while v = fun.call()
168      a.push(v); wsp
169      p1 = pos; break if not p < p1; p = p1
170    end
171    a
172  end
173  def interleaveRULE(between,&blk)
174    a = []
175    while v = blk.call()
176      if v
177        a.push(v)
178        wsp
179        break if not scan(between)
180      end
181    end
182    a.empty? ? false : a
183  end
184
185  def quesRX(fun,&more)
186    before_fun = pos
187    v = fun.call()
188    if not v
189      if not more
190        [[]]
191      else
192        result = more.call() or return false
193        result.unshift([])
194      end
195    else
196      if not more
197        [[v]]
198      else
199        result = more.call()
200        if result
201          result.unshift([v])
202        else
203          @scanner.pos = before_fun
204          result = more.call() or return false
205          result.unshift([])
206        end
207      end
208    end
209  end
210  def starRX(fun,&more)
211    result = plusRX(fun,more) and return result
212    if not more
213      [[]]
214    else
215      result = more.call() or return false
216      result.unshift([])
217    end
218  end
219  def plusRX(fun,&more)
220    before_fun = pos
221    v = fun.call() or return false
222    if result = starRX(fun,more)
223      result[0].unshift(v)
224      result
225    else
226      fail_at(before_fun)
227    end
228  end
229end
230
231
232# Tokens
233
234class Grammar
235
236  def self.def_precedence(precedence,precedence_hash)
237    @@__precedence_hashes__ ||= {}
238    @@__precedence_hashes__[precedence] = precedence_hash
239    eval("H#{precedence} = precedence_hash")
240  end
241  def self.def_precedence_alias(precedence,defequiv_precedence)
242    precedence_hash = @@__precedence_hashes__[defequiv_precedence]
243    def_precedence(precedence,precedence_hash)
244  end
245
246  def self.token_category(category,*args)
247    eval "@@matcher_for_#{category} = CategoryMatcher.new"
248    _help_def_category(category,args)
249    if category != :category
250      def_tokens_simple(:category,false,[category.to_s])
251    end
252  end
253#  def self.rule_category(category,*args)
254#    eval "@@matcher_for_#{category} = CategoryMatcher.new(true)"
255#    _help_def_category(category,args)
256#  end
257  def self._help_def_category(category,args)
258
259    # endsym's
260    if not args.empty?
261      if args[0].instance_of?(Hash)
262        k = args[0].keys[0]
263        v = args[0].values[0]
264      else
265        k = args[0]
266        v = nil
267      end
268#      case k
269#      when 'nofat' # / >> <nofat> /
270#        print "# caveat: #{category} endsym nofat unimplemented\n" if not $quiet
271#      when 'nofat_space' # / \s+ <nofat> /
272#        print "# caveat: #{category} endsym nofat_space unimplemented\n" if not $quiet
273#      when 'unspacey' # / <.unsp>? /
274#        print "# caveat: #{category} endsym unspacey unimplemented\n" if not $quiet
275#      when 'endsym'
276#        print "# caveat: #{category} endsym unimplemented\n" if not $quiet
277#      else
278#        p category, k
279#        raise "bug"
280#      end
281    end
282
283    eval "@@__precedences_for_#{category}_symbols__ = {}"
284
285    eval <<-END
286      def #{category}
287        b = @scanner.pos
288        sym,v = @@matcher_for_#{category}.longest_token_match(self,@scanner)
289        sym or return false
290
291        if v.instance_of?(Match)
292          if not(v.rule and v.rule =~ /:/)
293            v.rule = "#{category}:\#{v.rule||"kludge_node"}"
294          end
295          if not v[:sym]
296            v[:sym] = sym
297          end
298          m = v
299        else
300          h = v.is_a?(TrueClass) ? {} : {:kludge_name =>v}
301          h[:sym] = sym
302          m = _match_from(b,h,'#{category}')
303        end
304
305        if precedence = @@__precedences_for_#{category}_symbols__[sym]
306          if precedence_hash = @@__precedence_hashes__[precedence]
307            precop_method(m,precedence_hash)
308          else
309            precedence_hash.is_a?(FalseClass) or
310              raise "bug: unknown return precedence class: \#{precedence.capitalize}"
311          end
312        end
313        m
314      end
315    END
316
317  end
318   
319
320
321  def self.def_tokens_simple(category, precedence, syms)
322    syms.each{|sym| _def_token(category, sym, nil, precedence) }
323  end
324  def self.def_rules_rest(category, left_syms, rest_code)
325    def_tokens_rest(category, false, left_syms, 'wsp; '+rest_code)
326  end
327  def self.def_tokens_rest(category, precedence, left_syms, common_rest_code)
328    left_syms.each{|sym|
329      rest = _methodify_rest_code(category,sym,common_rest_code)
330      _def_token(category, sym, nil, precedence, rest)
331    }
332  end
333  def self.def_tokens_before(category, precedence, syms, common_rest_code=nil)
334    syms.each{|sym|
335      re = Regexp.new("(?=#{Regexp.quote(sym)})")
336      rest = _methodify_rest_code(category,sym,common_rest_code)
337      _def_token(category, sym, re, precedence, rest)
338    }
339  end
340  def self.def_token_full(category, precedence, name, leading_re, common_rest_code)
341      rest = _methodify_rest_code(category,name,common_rest_code)
342      _def_token(category, name, leading_re, precedence, rest)
343  end
344
345  def self._methodify_rest_code(category,sym,common_rest_code)
346    return true if not common_rest_code
347    rest_code = common_rest_code.gsub(/<sym>/,sym)
348    rest_method_name = "__#{category}_#{rand(10000000)}"
349    code = "def #{rest_method_name}(start); #{rest_code}; end"
350    begin
351      eval code
352    rescue Exception
353      STDERR.print code,"\n" if not $quiet
354      raise
355    end
356    rest = rest_method_name.to_sym
357  end
358  def self._def_token(category,name,leading_re,precedence,rest=true)
359    name = name.join(" ") if name.is_a? Array
360    leading_re ||= Regexp.new(Regexp.quote(name))
361    _def_token_precedence(category,name,precedence)
362    _def_category_member(category,name,leading_re,rest)
363  end
364  def self._def_token_precedence(category,sym,precedence)
365    (eval "@@__precedences_for_#{category}_symbols__")[sym] = precedence
366  end
367  def self._def_category_member(category,name,leading_re,rest)
368    if (eval "@@matcher_for_#{category}").declared?(name)
369      print ";# WARNING: #{category}:#{name} redefined.\n" if not $quiet
370    end
371    (eval "@@matcher_for_#{category}").declare(name,leading_re,rest)
372  end
373end
374
375class CategoryMatcher
376  def initialize(is_rule_category=false)
377    @is_rule_category=is_rule_category
378    @member_index = {}
379  end
380  class CategoryMember
381    attr_accessor :name,:leading_re,:rest
382    def initialize(name,leading_re,rest)
383      @name,@leading_re,@rest=name,leading_re,rest
384    end
385  end
386  def declare(name,leading_re,rest)
387    cm = CategoryMember.new(name,leading_re,rest)
388    @member_index[name] = cm
389    @member_cache = nil
390  end
391  def declared?(name)
392    @member_index.key? name
393  end
394  def longest_token_match(gram,scanr)
395    @member_cache ||= @member_index.values.sort{|a,b|
396      b.leading_re.to_s.length <=> a.leading_re.to_s.length}
397    b0 = b1 = scanr.pos
398    #if @is_rule_category
399    #  gram.wsp or return false
400    #  b1 = scanr.pos
401    #end
402    @member_cache.each{|cm|
403      scanr.scan(cm.leading_re) or next
404      rest = cm.rest
405      if rest.is_a?(Symbol)
406        if u = gram.send(rest,b1) #and ((not @is_rule_category) or gram.wsp)
407          return [cm.name,u]
408        end
409      #elsif rest.respond_to?(:call)
410      else
411        if true #and ((not @is_rule_category) or gram.wsp)
412          return [cm.name,rest]
413        end
414      end
415      scanr.pos = b1
416    }
417    scanr.pos = b0
418    false
419  end
420end
421
422
423# Dynamically scoped $+foo environment variables
424
425class EnvVars
426  def initialize
427    @stack = []
428  end
429  def scope_enter(*vars)
430    @stack.unshift({})
431    vars.each{|var|def_var(var)}
432  end
433  def scope_leave
434    @stack.shift or raise "bug"
435  end
436  def def_var(var)
437    @stack[-1][var] = nil
438  end
439  def _find_defining_env(k)
440    @stack.each{|e| return e if e.key? k}
441    nil
442  end
443  def defined?(k)
444    _find_defining_env(k) ? true : false
445  end
446  def [](k)
447    e = _find_defining_env(k)
448    return nil if not e
449    e[k]
450  end
451  def []=(k,v)
452    e = _find_defining_env(k)
453    raise("Environment variable #{k} was not declared") if not e
454    e[k] = v
455  end
456end
457
458$env_vars = EnvVars.new
459
460
461# Interactive REPL
462
463require 'readline'
464class Repl
465  def initialize(history_filename="deleteme_hist")
466    @histfile = File::expand_path(history_filename)
467    if File::exists?(@histfile)
468      Readline::HISTORY.push(*(eval(IO.read(@histfile))||[]))
469    end
470  end
471  def save_history
472    h = Readline::HISTORY.to_a.reverse.uniq.slice(0,100).reverse.inspect
473    open(@histfile,"w"){|io|io.puts(h)}
474  end
475  def ruby
476    while true
477      s = Readline.readline("rb: ",true)
478      break if not s or s == ""
479      p Object.module_eval(s)
480    end
481    save_history
482  end
483  def expr
484    while true
485      s = Readline.readline("expr: ",true)
486      break if not s or s == ""
487      p Perl.new(s)._EXPR(false)
488    end
489    save_history
490  end
491  def parser_rule
492    while true
493      print "Example rules: _UNIT  _EXPR  infix  integer\n"
494      s = Readline.readline("rule: ",true)
495      break if not s or s == ""
496      rule = s
497      while true
498        s = Readline.readline("input: ",true)
499        break if not s or s == ""
500        eval("p Perl.new(s).#{rule}")
501      end
502    end
503    save_history
504  end
505  def parser_input
506    while true
507      s = Readline.readline("input: ",true)
508      break if not s or s == ""
509      input = s
510      while true
511        s = Readline.readline("rule: ",true)
512        break if not s or s == ""
513        rule = s
514        eval("p Perl.new(input).#{rule}")
515      end
516    end
517    save_history
518  end
519end
Note: See TracBrowser for help on using the browser.