root/lang/ruby/RedLisp/trunk/redlisp.rb @ 31335

Revision 31335, 8.3 kB (checked in by kiyoka, 4 years ago)

Added Printer class and Nil class.

  • Property svn:executable set to *
Line 
1#!/usr/bin/env ruby
2#
3# RedLisp
4#   "Principle of Least Surprise (for Rubyist)"
5#
6#
7require 'stringio'
8require 'pp'
9
10class Nil
11end
12
13class Cell
14  include Enumerable
15
16  def initialize( car = Nil.new, cdr = Nil.new )
17    @car = car
18    @cdr = cdr
19  end
20  attr_accessor :car, :cdr
21
22  def each                    # Supporting iterator
23    it = self
24    begin
25      yield it
26      it = it.cdr
27    end while Nil != it.class
28  end
29
30  def length
31    count = 0
32    p = self
33    begin
34      p = p.car
35      count += 1
36    end while Nil != it.class
37  end
38
39  def isDotted
40    ((Cell != @cdr.class) and (Nil != @cdr.class))
41  end
42   
43end
44
45
46class Token
47  def initialize( type, str )
48    @type = type
49    @str  = str
50  end
51  attr_accessor :type, :str
52end
53
54
55class Reader
56  ## tokens
57  T_EOF       = :t_eof
58  T_LPAREN    = :t_lparen
59  T_RPAREN    = :t_rparen
60  T_SYMBOL    = :t_symbol
61  T_NUM       = :t_num
62  T_QUOTE     = :t_quote
63  T_DOT       = :t_dot
64
65  # inport is IO class
66  def initialize( inport )
67    @inport   = inport
68    @curtoken = nil
69  end
70
71  def skipspace
72    ch = @inport.getc
73    if !@inport.eof?
74      while ch.chr.match( /[ \t\r\n]/ )
75        ch = @inport.getc
76      end
77      @inport.ungetc( ch )
78    end
79  end
80
81  def readwhile( exp )
82    ret = ""
83    while true
84      ch = @inport.getc
85      if @inport.eof?
86        break
87      end
88      if ch.chr.match( exp )
89        ret += ch.chr
90      else
91        @inport.ungetc( ch )
92        break
93      end
94    end
95    ret
96  end
97
98  def token
99    skipspace
100    ch = @inport.getc
101    if @inport.eof?
102      @curtoken = Token.new( T_EOF, "" )
103    else
104      str = ch.chr
105      type =
106        case str
107        when '('
108          T_LPAREN
109        when ')'
110          T_RPAREN
111        when '.'
112          T_DOT
113        when /[a-zA-Z+*\/=!-]/ # symbol
114          str += readwhile( /[a-zA-Z+*\/=!-]/ )
115          T_SYMBOL
116        when /[0-9]/        # number
117          str += readwhile( /[0-9.]/ )
118          T_NUM
119        when /[\']/
120          T_QUOTE
121        end
122      p "  [" + str + "] : " + type.to_s
123      @curtoken = Token.new( type, str )
124    end
125  end
126
127  def curtoken
128    @curtoken
129  end
130
131  def atom( tok )
132    case tok.type
133    when T_SYMBOL
134      tok.str
135    when T_NUM
136      tok.str.to_i
137    when T_QUOTE
138      "quote"
139    end
140  end
141
142  def list( tok )
143    dotted = false
144    cells = []
145    while true
146      case tok.type
147      when T_EOF
148        print "Error: unbalanced paren(2)\n"
149        raise ExceptionParen
150      when T_LPAREN
151        cells << Cell.new( list( token ))
152        tok = curtoken
153      when T_RPAREN
154        token
155        break
156      else
157        if tok.type == T_DOT
158          if 1 == cells.size
159            dotted = true
160            tok = token
161          else
162            print "Error : illegal dotted pair syntax"
163            break
164          end
165        elsif tok.type == T_QUOTE
166          cells << Cell.new( Cell.new( atom( tok ), Cell.new( sexp( token ))))
167          tok = curtoken
168        else
169          cells << Cell.new( atom( tok ))
170          tok = token
171        end
172      end
173    end
174    if dotted
175      ## dotted list
176      Cell.new( cells[0].car, cells[1].car )
177    else
178      ## setup list
179      if 1 < cells.size
180        ptr = cells.pop
181        cells.reverse.each { |x|
182          x.cdr = ptr
183          ptr = x
184        }
185      end
186      cells.first
187    end
188  end
189  def sexp( tok )
190    case tok.type
191    when T_EOF
192      break
193    when T_LPAREN
194      list( token )
195    when T_RPAREN
196      print "Error: unbalanced paren(1)\n"
197      raise ExceptionParen
198    when T_QUOTE
199      Cell.new( Cell.new( tom( tok ), Cell.new( sexp( token ))))
200    else
201      atom( tok )
202    end
203  end
204
205  def _read
206    sexp( token )
207  end
208end
209
210
211class Evaluator
212  def initialize
213    @sym = {
214      '+' => getIFunc( '+' ),
215      '-' => getIFunc( '-' ),
216      '*' => getIFunc( '*' ),
217      '/' => getIFunc( '/' ),
218      '%' => getIFunc( '%' ),
219
220      'car'    => getIFunc( 'car' ),
221      'cdr'    => getIFunc( 'cdr' ),
222    }
223  end
224
225  def argCheck( args )
226    if 0 == args.length
227      nil
228    else
229      args.each { |x|
230        if x.class != Fixnum
231          print "Error: + - * / % operator got illegal argument. "
232          return nil
233        end
234      }
235    end
236  end
237   
238  def getIFunc( name )
239    case name
240    when '+'
241      lambda {|args|
242        argCheck( args )
243        args.inject(0) {|x,y| x + y}
244      }
245    when '-'
246      lambda {|args|
247        argCheck( args )
248        if 1 == args.length
249          - args[0]
250        else
251          args[1..-1].inject(args[0]) {|x,y| x - y}
252        end
253      }
254    when '*'
255      lambda {|args|
256        argCheck( args )
257        args.inject(1) {|x,y| x * y}
258      }
259    when '/'
260      lambda {|args|
261        argCheck( args )
262        if 1 == args.length
263          1 / args[0]
264        else
265          args[1..-1].inject(args[0]) {|x,y| x / y}
266        end
267      }
268    when '%'
269      lambda {|args|
270        argCheck( args )
271        if 1 == args.length
272          1 % args[0]
273        else
274          args[1..-1].inject(args[0]) {|x,y| x % y}
275        end
276      }
277    when 'car'
278      lambda {|args|
279        p "function car():", args.car
280        if Cell == args.class
281          args.car.car
282        else
283          print "Error: car argument error\n"
284          raise ExceptionArgument
285        end
286      }
287    when 'cdr'
288      lambda {|args|
289        p "function cdr():", args.car
290        if Cell == args.class
291          args.car.cdr
292        else
293          print "Error: car argument error\n"
294          raise ExceptionArgument
295        end
296      }
297    end
298  end
299
300  def execFunc( funcname, args )
301    printf( "execFunc( %s, %s )\n", funcname, "xxx" )
302    pp args
303    if @sym[ funcname ]
304      @sym[ funcname ].call( args )
305    else
306      printf( "Error: no such function %s\n", funcname )
307      raise ExceptionNoSymbol
308    end
309  end
310
311  def apply( car, cdr )
312    p "apply() car.class.to_s: " + car.class.to_s + ":" + car
313    ptr = cdr
314    while Nil != ptr.class
315      print "ptr.car: "
316      pp ptr.car
317      ptr.car = _eval( ptr.car )
318      ptr = ptr.cdr
319    end
320    execFunc( car, cdr )
321  end
322
323  def _eval( sexp )
324    case sexp.class.to_s
325    when "Cell"
326      if "quote" == sexp.car
327        sexp.cdr.car
328      elsif sexp.isDotted
329        print "Error: can't eval dotted pair"
330        raise ExceptionNoSymbol       
331      elsif Cell == sexp.car.class
332        _eval( sexp.car )
333      else
334        self.apply( sexp.car, sexp.cdr )
335      end
336    else
337      sexp
338    end
339  end
340end
341
342
343class Printer
344  def _print( sexp, str = "" )
345    case sexp.class.to_s
346    when "Cell"
347      str += "("
348      str += _print( sexp.car )
349      ptr = sexp.cdr
350      while Nil != ptr.class
351        str += _print( ptr.car )
352        ptr = ptr.cdr
353      end
354      str += ")"
355    else
356      str += sprintf( "%s ", sexp )
357    end
358    str
359  end
360end
361
362
363class Core
364  def initialize( io )
365    @reader       = Reader.new( io )
366    @evaluator    = Evaluator.new
367    @printer      = Printer.new
368  end
369 
370  def repl
371    while true
372      @printer._print( @evaluator._eval( @reader._read ))
373    end
374  end
375end
376
377
378# test
379def readerTest( sexp )
380  sio = StringIO.open( sexp )
381  reader = Reader.new( sio )
382  p "--- " + sexp + " --- is "
383  pp reader._read
384end
385
386#test
387def readerPrinterTest( sexp )
388  sio = StringIO.open( sexp )
389  reader = Reader.new( sio )
390  print "--- " + sexp + " --- is       \n"
391  s = reader._read
392  printer = Printer.new
393  print "--- " + sexp + " --- prints   \n"
394  printf( "   %s\n\n", printer._print( s ))
395end
396
397#test
398def replTest( sexp )
399  sio = StringIO.open( sexp )
400  reader = Reader.new( sio )
401  print "--- " + sexp + " --- is       \n"
402  s = reader._read
403  printer = Printer.new
404  print "--- " + sexp + " --- prints   \n"
405  printf( "   %s\n", printer._print( s ))
406  evaluator = Evaluator.new
407  print "--- " + sexp + " --- evals    \n"
408  printf( "   %s\n", printer._print( evaluator._eval( s )))
409end
410
411replTest( " + " )
412replTest( " 123 " )
413replTest( " (car '(a b c)) " )
414replTest( " (cdr '(a b c)) " )
415#replTest( " (+ 1 2 3 4 ) " )
416#replTest( " (* 1 2 3 4 ) " )
417#replTest( " (- 10 5 1 ) " )
418#replTest( " (/ 10 4 2 ) " )
419#replTest( " (+ (+ 1 2) (+ 3 4 )) " )
420#replTest( " (- (* 3 3) (* 2 2 )) " )
421#replTest( " ( 1 . 2 ) " )
422#replTest( " ( 1 ) " )
423#replTest( " (car '(1 . 2)) " )
424#replTest( " (cdr '(1 . 2)) " )
425#replTest( " '( 1 2 3 '4 ) " )
426#replTest( " (( 1 )) " )
427#replTest( " ( 1 2 ( 3 4 )) " )
428#replTest( " ( " )
429#replTest( " ) " )
430
431
432#def main
433#  sio = StringIO.open( " (car 1 2 3) " )
434#  core = Core.new( sio )
435#  while true
436#    core.repl
437#  end
438#end
Note: See TracBrowser for help on using the browser.