Calculatrice

Travaille en RPN : empiler les nombres et effectuer les opérations.
/!\ nécessite les module lex random conv
Téléchargement du binaire wp (clic-droit et Enregistrer le lien sous…)

calc.w
; Calculatrice RPN
begin
   sub displayStack(stack)
      declare size, one, minusOne, elem
      let size = dcount(stack)
      if size = 0 then
         echonl "<empty stack>"
         return
      endif
      let one = 1
      let minusOne = neg(one)
      foreach elem in size, one, minusOne
         echonl format(elem, @right, '0', 2):'> ':format(stack{elem}, @right, ' ', 20)
      endfor
   except
   endsub
   ;
   declare rules, crules, Rule  ; lex rules and compiled rules, Rule (pour echapper # 2 fois => #23#32#33)
   let crules = ''
   let rules = 'integer: int '
   let rules{2} = 'fractionnal: frac '
   let rules{3} = 'absolute: abs '
   let rules{4} = 'negation: neg '
   let rules{5} = 'round0: round0 '
   let rules{6} = 'round1: round1 ' 
   let rules{7} = 'round2: round2 '
   let rules{8} = 'round3: round3 '
   let rules{9} = 'round4: round4 '
   let rules{10} = 'random: rnd ; random '
   let rules{11} = 'varsys:     @ (1,*,a-z) '
   let rules{12} = 'number: (0,1,#23#32#332D) (1,13,0-9) . (0,5,0-9) '  ; corriger le double 'escape' car - est un car reserve lex (- => #2D => #23#32#33#23#33#32#23#34#34) car sinon compil/assembleur decode mal le -
   let rules{13} = 'integernumber:  (0,1,#23#32#332D) (1,13,0-9) '
   let rules{14} = 'minus:      (1,1,#23#32#332D)   ; - '
   let rules{15} = 'plus:       (1,1,#2B)   ; + '
   let rules{16} = 'multiply:   (1,1,#2A)   ; * '
   let rules{17} = 'divide:     (1,1,#2F)   ; / '
   let rules{18} = 'integerdivide:   (1,1,#5C)   ; \ '
   let rules{19} = 'modulus:     (1,1,#25)   ; % '
   let rules{20} = 'quit:       quit '
   let rules{21} = 'dup:        dup '
   let rules{22} = 'pop:        pop '
   let rules{23} = 'clear:      clear '
   let rules{24} = 'space:      (1,1,#23#32#3320) '
   let rules{25} = 'squareroot: sqrt '
   let rules{26} = 'exponent:   exp '
   let rules{27} = 'other:      (1,1,#23#32#3300-#23#32#33FF)'
   let rules = change(rules, @fm, @lf)
   part 1
   let Rule = {}
   invoke lex&compile(rules, crules, Rule)
   part 2
   ; todo mettre la sub qui suit dans le module lex (? pbm il faut 4/5 params)
   declare tok
   let tok = {}
   sub getToken(tokenList, tokenIndex, tokenNumber, value)
      let tokenNumber = tokenList{2, tokenIndex}
      if tokenNumber # '' then
         declare hexa
         let hexa = ''
         let hexa = tokenList{1, tokenIndex}
         invoke conv&fromHexa(hexa, value)
      endif
      ; si besoin
      invoke lex&getToken(tokenList, tokenIndex, tok)
   except
   endsub
   ;
   echonl "wcalc => + - * / \ % int frac abs neg sqrt exp rnd round0 round1 round2 round3 round4 @pi pop dup clear quit"
   ;
   declare value, stack, token, ind, ruleNumber, ruleValue
   let value = ''
   let token = ''
   let stack = ''
   loop
      do displayStack(stack)
      echo 'wcalc: '
      let value = ''
      input value
      invoke lex&analyze(crules, value, token)
      ; -----------------------------------------------
      ; token{1/2/3} en MV : chaine hexa, no token, ordinal_position
      let ind = 0
      let ruleValue = ''
      let ruleNumber = 0
      loop
         let ind = ind + 1
         do getToken(token, ind, ruleNumber, ruleValue)
         breakon ruleNumber = ''
         continueon ruleNumber = Rule.space
         ;
         if ruleNumber = Rule.other then
            echonl 'Syntax error'
            break;
            ; -----------------------------------------------
         elseif ruleNumber = Rule.dup then
            if dcount(stack) < 1 then
               echonl "Stack underflow error"
            else
               let stack = insert(stack, stack{1}, 1)
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.pop then
            if dcount(stack) < 1 then
               echonl "Stack underflow error"
            else
               let stack = remove(stack, 1)
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.clear then
            let stack = ''
            ; -----------------------------------------------
         elseif ruleNumber = Rule.number or ruleNumber = Rule.integernumber then
            if dcount(stack) > 15 then
               echonl "Stack full error"
            else
               let stack = insert(stack, ruleValue, 1)
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.random then
            if dcount(stack) > 15 then
               echonl "Stack full error"
            else
               declare rnd
               let rnd = 0
               invoke random&get(rnd)
               let stack = insert(stack, rnd, 1)
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.varsys then
            if dcount(stack) > 15 then
               echonl "Stack full error"
            else
               if ruleValue = '@pi' then
                  let stack = insert(stack, @pi, 1)
               else
                  echonl "Unknown constant"
               endif
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.plus then
            if dcount(stack) < 2 then
               echonl "Stack underflow error"
            else
               let stack{2} = stack{2} + stack{1}
               let stack = remove(stack, 1)
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.minus then
            if dcount(stack) < 2 then
               echonl "Stack underflow error"
            else
               let stack{2} = stack{2} - stack{1}
               let stack = remove(stack, 1)
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.divide then
            if dcount(stack) < 2 then
               echonl "Stack underflow error"
            elseif stack{1} = 0 then
               echonl "Division by zero error"
            else
               let stack{2} = stack{2} / stack{1}
               let stack = remove(stack, 1)
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.integerdivide then
            if dcount(stack) < 2 then
               echonl "Stack underflow error"
            elseif stack{1} = 0 then
               echonl "Division by zero error"
            else
               let stack{2} = stack{2} \ stack{1}
               let stack = remove(stack, 1)
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.modulus then
            if dcount(stack) < 2 then
               echonl "Stack underflow error"
            elseif stack{1} = 0 then
               echonl "Division by zero error"
            else
               let stack{2} = stack{2} % stack{1}
               let stack = remove(stack, 1)
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.multiply then
            if dcount(stack) < 2 then
               echonl "Stack underflow error"
            else
               let stack{2} = stack{2} * stack{1}
               let stack = remove(stack, 1)
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.exponent then
            if dcount(stack) < 2 then
               echonl "Stack underflow error"
            else
               let stack{2} = exp(stack{2}, stack{1})
               let stack = remove(stack, 1)
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.squareroot then
            if dcount(stack) < 1 then
               echonl "Stack underflow error"
            else
               let stack{1} = sqrt(stack{1})
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.negation then
            if dcount(stack) < 1 then
               echonl "Stack underflow error"
            else
               let stack{1} = neg(stack{1})
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.integer then
            if dcount(stack) < 1 then
               echonl "Stack underflow error"
            else
               let stack{1} = int(stack{1})
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.fractionnal then
            if dcount(stack) < 1 then
               echonl "Stack underflow error"
            else
               let stack{1} = frac(stack{1})
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.absolute then
            if dcount(stack) < 1 then
               echonl "Stack underflow error"
            else
               let stack{1} = abs(stack{1})
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.round0 then
            if dcount(stack) < 1 then
               echonl "Stack underflow error"
            else
               let stack{1} = round(stack{1}, 0)
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.round1 then
            if dcount(stack) < 1 then
               echonl "Stack underflow error"
            else
               let stack{1} = round(stack{1}, 1)
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.round2 then
            if dcount(stack) < 1 then
               echonl "Stack underflow error"
            else
               let stack{1} = round(stack{1}, 2)
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.round3 then
            if dcount(stack) < 1 then
               echonl "Stack underflow error"
            else
               let stack{1} = round(stack{1}, 3)
            endif
            ; -----------------------------------------------
         elseif ruleNumber = Rule.round4 then
            if dcount(stack) < 1 then
               echonl "Stack underflow error"
            else
               let stack{1} = round(stack{1}, 4)
            endif
         endif
         ; -----------------------------------------------
         breakon ruleNumber = Rule.quit
      endloop
      breakon ruleNumber = Rule.quit
   endloop
   echonl "Bye :)"
except
   echonl "Exception line ":@exceptline
   if @part = 1 then
      declare err
      let err = 0
      invoke lex&error(err)
      echonl "lex error : ":err
   endif
end