1
7 class JNCalculator
8 options no_result_var
9
10 prechigh
11 nonassoc UMINUS
12 left '*' '/'
13 left '+' '-'
14 preclow
15 rule
16 stmt : expr
17 | /* empty */ { nil }
18
19 expr : expr '+' expr { val[0] + val[2] }
20 | expr '-' expr { val[0] - val[2] }
21 | expr '*' expr { val[0] * val[2] }
22 | expr '/' expr { val[0] / val[2] }
23 | '(' expr ')' { val[1] }
24 | NUM
25 | '-' NUM =UMINUS { -(val[1]) }
26 end
27
28 ---- header
29
31 require 'rational'
32 require 'strscan'
33
34 SPACES = /[\s¡¡]+/
35
36 ---- inner
37
38 def initialize(numdef)
39 @numdef = numdef
40 end
41
42 def parse(str)
43 @source = str
44 yyparse(self, :yylex)
45 end
46
47 private
48
49 def yylex
50 s = StringScanner.new(@source)
51 until s.eos?
52 case
53 when s.scan(::SPACES)
54 ;
55 when tok = s.scan(/[0-9£°-£¹]+/)
56 yield :NUM, Rational(tok.to_i)
57 when tok = s.scan(@numdef.chars_re)
58 yield :NUM, Rational(@numdef.parse(tok))
59 when tok = s.scan(/[\+\-\*\/\(\)¡Ü¡Ý¡ß¡à¡Ê¡Ë]/)
60 yield to_rubyop(tok), tok
61 else
62 raise ParseError, "parse error on char #{s.getch.inspect}"
63 end
64 end
65 yield nil
66 end
67
68 def to_rubyop(tok)
69 { '¡Ü' => '+',
70 '¡Ý' => '-',
71 '¡ß' => '*',
72 '¡à' => '/',
73 '¡Ê' => '(',
74 '¡Ë' => ')' }[tok] || tok
75 end
76
77 ---- footer
78
79 require 'component'
80
81 class String
82 def mbstrip
83 sub(/\A#{::SPACES}/o, '').sub(/#{::SPACES}\z/o, '')
84 end
85 end
86
87 def jncalculator_main
88 while line = prompt('JCALC> ')
89 exit if /q(u(it?)?)?/i =~ line.mbstrip
90 next if line.mbstrip.empty?
91 begin
92 puts lldn_eval(line.mbstrip)
93 rescue => err
94 puts "Error: #{err.message}"
95 end
96 end
97 end
98
99 def lldn_eval(str)
100 @parser ||= JNCalculator.new(Component.LLDN)
101 Component.LLDN.string_expr(@parser.parse(str.mbstrip))
102 end
103
104 begin
105 require 'readline'
106 def prompt(str)
107 Readline.readline(str)
108 end
109 rescue LoadError
110 def prompt(str)
111 print str; $stdout.flush
112 return $stdin.gets
113 end
114 end
115
116 if $0 == __FILE__
117 jncalculator_main
118 end