Reach pattern-matching.

This commit is contained in:
Dmitry Boulytchev 2018-05-02 22:36:27 +03:00
parent 40afee26cc
commit de17bdc3c4
6 changed files with 132 additions and 16 deletions

View file

@ -7,9 +7,9 @@ RC=../src/rc.opt
check: $(TESTS)
$(TESTS): %: %.expr
@$(RC) $< && cat $@.input | ./$@ > $@.log && diff $@.log orig/$@.log
#@$(RC) $< && cat $@.input | ./$@ > $@.log && diff $@.log orig/$@.log
cat $@.input | $(RC) -i $< > $@.log && diff $@.log orig/$@.log
cat $@.input | $(RC) -s $< > $@.log && diff $@.log orig/$@.log
#cat $@.input | $(RC) -s $< > $@.log && diff $@.log orig/$@.log
clean:
rm -f test*.log *.s *~ $(TESTS)

View file

@ -0,0 +1,12 @@
> 1
2
3
4
1
2
3
4
3
4
1
2

24
regression/test038.expr Normal file
View file

@ -0,0 +1,24 @@
fun append (x, y) {
case x of
`nil -> return y
| `cons (h, t) -> return `cons (h, append (t, y))
esac
}
fun printList (x) {
case x of
`nil -> skip
| `cons (h, t) -> write (h); printList (t)
esac
}
n := read ();
x := `cons (1, `cons (2, `nil));
y := `cons (3, `cons (4, `nil));
printList (x);
printList (y);
printList (append (x, y));
printList (append (y, x))

1
regression/test038.input Normal file
View file

@ -0,0 +1 @@
0

View file

@ -73,16 +73,29 @@ module State =
| L (scope, s, enclosing) -> if List.mem x scope then s x else eval enclosing x
(* Creates a new scope, based on a given state *)
let enter st xs =
let rec enter st xs =
match st with
| G _ -> L (xs, undefined, st)
| L (_, _, e) -> L (xs, undefined, e)
| L (_, _, e) -> enter e xs
(* Drops a scope *)
let leave (L (_, _, e)) st' =
match st' with
| L (scope, s, _) -> L (scope, s, e)
| G _ -> e
let leave st st' =
let rec get = function
| G _ as st -> st
| L (_, _, e) -> get e
in
let g = get st in
let rec recurse = function
| L (scope, s, e) -> L (scope, s, recurse e)
| G _ -> g
in
recurse st'
(* Push a new local scope *)
let push st s xs = L (xs, s, st)
(* Drop a local scope *)
let drop (L (_, _, e)) = e
end
@ -258,7 +271,7 @@ module Stmt =
(* array *) | Array of t list
(* arbitrary array *) | IsArray
(* arbitrary string *) | IsString
with show
with show, foldl
(* Pattern parser *)
ostap (
@ -273,6 +286,10 @@ module Stmt =
| "#" {IsString}
)
let vars p =
let module S = Set.Make (String) in
S.elements @@ transform(t) (object inherit [S.t] @t[foldl] method c_Ident s _ name = S.add name s end) S.empty p
end
(* The type for statements *)
@ -285,7 +302,8 @@ module Stmt =
(* loop with a post-condition *) | Repeat of t * Expr.t
(* pattern-matching *) | Case of Expr.t * (Pattern.t * Expr.t option * t) list
(* return statement *) | Return of Expr.t option
(* call a procedure *) | Call of string * Expr.t list with show
(* call a procedure *) | Call of string * Expr.t list
(* leave a scope *) | Leave with show
(* Statement evaluator
@ -309,6 +327,7 @@ module Stmt =
let rec eval env ((st, i, o, r) as conf) k stmt =
let seq x = function Skip -> x | y -> Seq (x, y) in
match stmt with
| Leave -> eval env (State.drop st, i, o, r) Skip k
| Assign (x, is, e) ->
let (st, i, o, is) = Expr.eval_list env conf is in
let (st, i, o, Some v) = Expr.eval env (st, i, o, None) e in
@ -324,6 +343,46 @@ module Stmt =
| Repeat (s, e) -> eval env conf (seq (While (Expr.Binop ("==", e, Expr.Const 0), s)) k) s
| Return e -> (match e with None -> (st, i, o, None) | Some e -> Expr.eval env conf e)
| Call (f, args) -> eval env (Expr.eval env conf (Expr.Call (f, args))) k Skip
| Case (e, bs) ->
let (_, _, _, Some v) as conf' = Expr.eval env conf e in
let rec branch ((st, i, o, _) as conf) = function
| [] -> failwith (Printf.sprintf "Pattern matching failed: no branch is selected while matching %s\n" (show(Value.t) v))
| (patt, con, body)::tl ->
let rec match_patt patt v st =
let update x v = function
| None -> None
| Some s -> Some (fun y -> if y = x then v else s y)
in
match patt, v with
| Pattern.Ident x , v -> update x v st
| Pattern.Wildcard , _ -> st
| Pattern.Const n , Value.Int n' when n = n' -> st
| Pattern.String s , Value.String s' when s = s' -> st
| Pattern.Array p , Value.Array p' -> match_list p p' st
| Pattern.IsArray , Value.Array _ -> st
| Pattern.IsString , Value.String _ -> st
| Pattern.Sexp (t, ps), Value.Sexp (t', vs) when t = t' -> match_list ps vs st
| _ -> None
and match_list ps vs s =
match ps, vs with
| [], [] -> s
| p::ps, v::vs -> match_list ps vs (match_patt p v s)
| _ -> None
in
match match_patt patt v (Some State.undefined) with
| None -> branch conf tl
| Some st' ->
let st'' = State.push st st' (Pattern.vars patt) in
let (st''', i', o', Some c) =
match con with
| None -> (st'', i, o, Some (Value.of_int 1))
| Some c -> Expr.eval env (st'', i, o, None) c
in
if Value.to_int c <> 0
then eval env (st''', i', o', None) k (Seq (body, Leave))
else branch (st''', i', o', None) tl
in
branch conf' bs
(* Statement parser *)
ostap (
@ -350,7 +409,7 @@ module Stmt =
}
| %"repeat" s:parse %"until" e:!(Expr.parse) {Repeat (s, e)}
| %"return" e:!(Expr.parse)? {Return e}
| %"case" e:!(Expr.parse) %"of" bs:!(Util.listBy)[ostap ("|")][ostap (!(Pattern.parse) (-"when" !(Expr.parse))? parse)] %"esac" {Case (e, bs)}
| %"case" e:!(Expr.parse) %"of" bs:!(Util.listBy)[ostap ("|")][ostap (!(Pattern.parse) (-"when" !(Expr.parse))? -"->" parse)] %"esac" {Case (e, bs)}
| x:IDENT
s:(is:(-"[" !(Expr.parse) -"]")* ":=" e :!(Expr.parse) {Assign (x, is, e)} |
"(" args:!(Util.list0)[Expr.parse] ")" {Call (x, args)}

View file

@ -16,7 +16,8 @@ open Language
(* begins procedure definition *) | BEGIN of string * string list * string list
(* end procedure definition *) | END
(* calls a function/procedure *) | CALL of string * int * bool
(* returns from a function *) | RET of bool with show
(* returns from a function *) | RET of bool
| DROP | DUP | OVER with show
(* The type for the stack machine program *)
type prg = insn list
@ -116,6 +117,25 @@ let compile (defs, p) =
let rec call f args p =
let args_code = List.concat @@ List.map expr args in
args_code @ [CALL (label f, List.length args, p)]
and pattern = function
| Stmt.Pattern.Wildcard -> [DROP; CONST 1]
| Stmt.Pattern.Const n -> [CONST n; BINOP "=="]
| Stmt.Pattern.String s -> [STRING s; CALL ("strcmp", 2, false)]
| Stmt.Pattern.Ident n -> [DROP; CONST 1]
| Stmt.Pattern.Array ps -> [DUP;
CALL ("isArray", 1, false);
OVER;
CALL (".length", 1, false);
CONST (List.length ps);
BINOP "==";
BINOP "&&";
]
| Stmt.Pattern.IsArray -> [CALL ("isArray", 1, false)]
| Stmt.Pattern.IsString -> [CALL ("isString", 1, false)]
| Stmt.Pattern.Sexp (t, ps) -> []
and patterns = function
| [] -> []
| (e, p)::ps -> expr e @ pattern p @ [BINOP "&&"] @ patterns ps
and expr = function
| Expr.Var x -> [LD x]
| Expr.Const n -> [CONST n]