1  #
   2  # $Id: jncalculator.y,v 1.8 2005/08/22 09:13:51 aamine Exp $
   3  #
   4  # Japanese Numeric Calculator
   5  #
   6  
   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  #! ruby -Ke
  30  
  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