0

I'm trying to make the function which takes string and transforms it to boolean expression. For example:for input ((x10+~1)*x132) it should give times(plus(var 10,compl 1),var 132), but it gives times(plus(var #,compl #),var 132). Where do hashtags come from??

datatype boolexp = zero
                 | one 
                 | plus of boolexp * boolexp 
                 | times of boolexp * boolexp
                 | var of int
                 | compl of boolexp

exception InvalidInput

(* takes the first n elements *)
fun take (_, 0) = nil
  | take (nil, _) = raise InvalidInput
  | take (h::t, n) = if n < 0
                     then raise InvalidInput
                     else h::take(t, n-1)

(* drops the frist n elements *)
fun drop (xs, 0) = xs
  | drop (nil, _) = raise InvalidInput
  | drop (h::t,n) = if n < 0
                    then raise InvalidInput
                    else drop (t, n-1)

(* converts string to integer *)
fun charlist_to_int (nil) = raise InvalidInput 
  | charlist_to_int (xs) =
    let fun helper_int_rev (nil) = 0
          | helper_int_rev (h::t) = if h >= #"0" andalso h <= #"9"
                                    then helper_int_rev t * 10 + (ord h - ord #"0")
                                    else raise InvalidInput
    in helper_int_rev (rev xs) end;

(* finds the operator and its position *)
fun searchfor_oper (nil,_,_) = raise InvalidInput
  | searchfor_oper (#"("::t, np, pos) = searchfor_oper (t, np+1, pos+1)
  | searchfor_oper (#")"::t, np, pos) = searchfor_oper(t, np-1, pos+1)
  | searchfor_oper (#"+"::t, 0, pos) = (pos, #"+")
  | searchfor_oper (#"*"::t, 0, pos) = (pos, #"*")
  | searchfor_oper (h::t, np, pos) = searchfor_oper (t, np, pos+1)

fun beparse_helper (nil) = raise InvalidInput
  | beparse_helper (h::t) =
    if h = #"x" then if hd(t)= #"0" then raise InvalidInput
    else var (charlist_to_int (t))
    else if h = #"0" then if t = nil then zero else raise InvalidInput
    else if h = #"1" then if t = nil then one else raise InvalidInput
    else if h = #"~" then compl(beparse_helper(t))
    else if h = #"(" then
    let
        val lst = if hd (rev t)= #")" then take(t, length(t)-1) else raise InvalidInput
        val (pos, oper) = searchfor_oper (lst, 0, 1)
    in
        if oper = (#"+")
        then plus(beparse_helper(take(lst,pos-1)), beparse_helper(drop(lst,pos)))
        else if oper = (#"*")
             then times(beparse_helper(take(lst,pos-1)),beparse_helper(drop(lst,pos)))
             else raise InvalidInput 
    end
    else raise InvalidInput;

fun beparse(s) = beparse_helper(explode(s));

(*TESTING*)
beparse ("((x10+~1)*x132)");
sshine
  • 15,635
  • 1
  • 41
  • 66
Tim
  • 37
  • 1
  • 6

2 Answers2

4

I don't think that it is SML per se so much as SML/NJ default REPL output. In the grand scheme of things the REPL is for development/debugging -- not the environment for the finished program to run it. Recursive data structures such as trees can give quickly give rise to values which are too big to conveniently print. The REPL truncates the output in a couple of ways. For lists it will print around a dozen elements and then use .... For things like trees it will print down a few levels and then use a # to indicate that you have reached the level where the truncation happens. If you find this inconvenient you can do one of two things:

1) The logical printing depth in SML/NJ can be altered by evaluating Control.Print.printDepth := 20; (or however much you want) in the REPL

2) Perhaps a little more principled but more work -- write a custom pretty-printer (say pprint) for your values and evaluate e.g. pprint v; in the REPL rather than just v;

John Coleman
  • 51,337
  • 7
  • 54
  • 119
0

Also, consider using pattern matching more:

fun beparse_helper [] = raise InvalidInput
  | beparse_helper (#"x"::cs) = var (charlist_to_int cs)
  | beparse_helper (#"0"::[]) = zero
  | beparse_helper (#"1"::[]) = one
  | beparse_helper (#"~"::cs) = compl (beparse_helper cs)
  | beparse_helper (#"("::(cs as (_::_))) =
    let val lst = if List.last cs = #")"
                  then take (cs, length cs - 1)
                  else raise InvalidInput
    in case searchfor_oper (lst, 0, 1) of
          (pos, #"+") => plus (beparse_helper (take (lst, pos-1)),
                               beparse_helper(drop (lst, pos)))
        | (pos, #"*") => times (beparse_helper (take (lst, pos-1)),
                                beparse_helper(drop (lst, pos)))
    end
  | beparse_helper _ = raise InvalidInput
sshine
  • 15,635
  • 1
  • 41
  • 66
  • What is 'as' in the 6th line? – Tim Nov 24 '15 at 19:42
  • `as` is an operator that works in patterns. For `fun foo (xs as (y::ys)) = ...` it binds `xs` to the full input list, `y` to the head of the list and `ys` to the tail. It is useful if you want the full restriction of the pattern match, but you still want to bind the full value (rather than its components). In this case, `#"("::(cs as (_::_))` matches a list with at least two elements in it that begins with the character `(` and where the rest of the list (including the second element) is called `cs`. That way I am sure that `List.last` does not fail (because it requires at least one element). – sshine Nov 26 '15 at 11:05