Search code examples
functional-programminggrammarsmlsmlnjparentheses

Add priority to parenthesis in calculator grammar


I'm writing a simple calculator in SML , and my code need to support 3 kinds of parentheses :

( ) 

[ ] 

{ } 

Where inside { } , can appear { } , [ ] , ( ) expressions ,

Inside [ ] , can appear [ ] , ( ) expressions ,

And inside ( ) , can appear only ( ) expressions .

Meaning is , that { } has the highest priority , the [ ] - the middle priority , and ( ) has the lowest priority .

What would be the best approach for accomplishing that ?

I've written a big method with too much loops and recursions that runs on separate cases , but I don't think this is the best approach .

Any suggestions would be greatly appreciated

Regards

EDIT :

The relevant code :

signature CalculatorOperation = sig
  datatype token = 
                    (* paranthesis *)

                   Lpar3             (* { *)
                 | Rpar3             (* } *)
                 | Lpar2             (* [ *)
                 | Rpar2             (* ] *)  
                 | Lpar              (* ( *)
                 | Rpar              (* ) *)

the structure :

structure CalculatorOperation : CalculatorOperation = struct 
  datatype token =
                    (* paranthesis *)

                   Lpar3             (* { *)
                 | Rpar3             (* } *)
                 | Lpar2             (* [ *)
                 | Rpar2             (* ] *)  
                 | Lpar              (* ( *)
                 | Rpar              (* ) *)

Scanner :

 fun stringScanner s [] = (toToken s,[])
    | stringScanner s (c::l)
      = case c::l of
          #" "::r => if s = "" then (stringScanner "" l) else (toToken s,l)
        (* paranthesis *)

        | #"{"::r => if s = "" then (Lpar3,r) else (toToken s,c::l)
        | #"}"::r => if s = "" then (Rpar3,r) else (toToken s,c::l)     
        | #"["::r => if s = "" then (Lpar2,r) else (toToken s,c::l)
        | #"]"::r => if s = "" then (Rpar2,r) else (toToken s,c::l)
        | #"("::r => if s = "" then (Lpar,r) else (toToken s,c::l)
        | #")"::r => if s = "" then (Rpar,r) else (toToken s,c::l)

Parser :

structure CalculatorParser : CalculatorParser = struct
  open CalculatorOperation

  exception CalculatorParser

  datatype expr = NumNode of int
               | UminusNode of expr
               | MultiplyNode of expr * expr
               | DivNode of expr * expr
               | PlusNode of expr * expr
               | MinusNode of expr * expr
               | ModuloNode of expr * expr
               | PowerNode of expr * expr                          





  fun parserBrackets l = parserHelper2 l
  and parserHelper l
      = case l of 
          (Num n)::l1 => (NumNode n,l1)
        | Lpar3::l1 => let val (en,l2) = parserBrackets l1  in case l2 of Rpar3::l3 => (en,l3)
        | _ => raise CalculatorParser end

        | Lpar2::l1 => let val (en,l2) = parserBrackets l1 in case l2 of Rpar2::l3 => (en,l3)
        | _ => raise CalculatorParser end   

        | Lpar::l1 => let val (en,l2) = parserBrackets l1 in case l2 of Rpar::l3 => (en,l3)
        | _ => raise CalculatorParser end   

Solution

  • I am not an SML expert, but from your description I gather that the syntactical rules you are looking for could be expressed in BNF as follows:

    <expr1> ::= '{' ( <expr1> | <expr2> ) '}'
    
    <expr2> ::= '[' ( <expr2> | <expr3> ) ']'
    
    <expr3> ::= '(' ( <expr3> | <expr> ) ')' 
    

    When I look at your definition of the datatype expr it seems to me that you could define similar types for expr1, expr2 and expr3 as follows:

    datatype expr3 = E3Node of expr3
                  | ENode of expr
    
    datatype expr2 = E2Node of expr2
                  | E3Node of expr3
    
    datatype expr1 = E1Node of expr1
                  | E2Node of expr2
    

    Honestly, I don't even know if this is valid SML, but I am sure you would be able to fix that ... and fill in the gaps.