1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
module Resolver
open System
open Forro
exception ResolutionError of string
let ResolutionError(s: string) =
raise (ResolutionError s)
type VarKind = InParam | OutParam | Local
type Context(procedures: Collections.Generic.IDictionary<string,Procedure>) =
let mutable locals = null
let mutable ProcName = ""
member c.Procedures = procedures
member c.StartNewProcedure procName =
ProcName <- procName
locals <- new Collections.Generic.Dictionary<string,VarKind>()
member c.AddLocal v kind =
let name = VarName v
if locals.ContainsKey name then ResolutionError ("duplicate variable '" + name + "' in procedure '" + ProcName + "'") else ()
locals.Add(name, kind)
member c.HasLocal v =
locals.ContainsKey (VarName v)
member c.IncludeAssignmentTarget v =
let name = VarName v
if locals.ContainsKey name then
let kind = locals.Item name
if kind = VarKind.InParam then ResolutionError ("variable '"+ name + "' is an in-parameter, which cannot be used as an assignment target") else ()
else
locals.Add(name, VarKind.Local)
member v.GetLocals = locals
let rec ResolveExpr (ctx: Context) expr twoState specContext =
match expr with
| Constant(x) -> ()
| Null -> ()
| Identifier(v) ->
if ctx.HasLocal v then () else ResolutionError ("undefined variable: " + VarName v)
| Not(e) -> ResolveExpr ctx e twoState specContext
| Binary(op,a,b) ->
ResolveExpr ctx a twoState specContext
ResolveExpr ctx b twoState specContext
| Select(e,f) ->
ResolveExpr ctx e twoState specContext
match f with
| Valid -> if specContext then () else ResolutionError "valid can only be used in specification contexts"
| _ -> ()
| Old(e) ->
if twoState then () else ResolutionError "old expressions can only be used in two-state contexts"
ResolveExpr ctx e twoState specContext
let rec ResolveStmt ctx s =
match s with
| Assign(v, e) ->
ResolveExpr ctx e false false
ctx.IncludeAssignmentTarget v
| Update(obj,f,rhs) ->
ResolveExpr ctx obj false false
match f with
| Valid -> ResolutionError "valid can only be used in specification contexts (in particular, it cannot be assigned to)"
| _ -> ()
ResolveExpr ctx rhs false false
| Alloc(v,hd,tl) ->
ResolveExpr ctx hd false false
ResolveExpr ctx tl false false
ctx.IncludeAssignmentTarget v
| IfStmt(guard,thn,els) ->
ResolveExpr ctx guard false false
ResolveStmtList ctx thn
ResolveStmtList ctx els
| WhileStmt(guard,invs,body) ->
ResolveExpr ctx guard false false
List.iter (fun inv -> ResolveExpr ctx inv true true) invs
ResolveStmtList ctx body
| CallStmt(outs,name,ins) ->
if ctx.Procedures.ContainsKey name then () else ResolutionError ("call to undefined procedure: " + name)
match ctx.Procedures.Item name with
| Proc(_,fIns,fOuts,_,_,_) ->
if fIns.Length = ins.Length then () else ResolutionError ("call to " + name + " has wrong number of in-parameters (got " + ins.Length.ToString() + ", expected " + fIns.Length.ToString() + ")")
if fOuts.Length = outs.Length then () else ResolutionError ("call to " + name + " has wrong number of out-parameters (got " + outs.Length.ToString() + ", expected " + fOuts.Length.ToString() + ")")
List.iter (fun e -> ResolveExpr ctx e false false) ins
let outnames = new Collections.Generic.Dictionary<string,Variable>()
List.iter (fun v ->
ctx.IncludeAssignmentTarget v
let name = VarName v
if outnames.ContainsKey name then ResolutionError ("an actual out-parameter is allowed only once for a call: " + name) else ()
outnames.Add(name, v)
) outs
| Assert(e) ->
ResolveExpr ctx e true true
and ResolveStmtList ctx slist =
match slist with
| Block(ss) -> List.iter (fun s -> ResolveStmt ctx s) ss
let ProcedureName p =
match p with Proc(id,_,_,_,_,_) -> id
let ResolveProc (ctx: Context) p =
match p with
| Proc(name, ins, outs, req, ens, body) ->
// look up 'name' in ctx.Procedures, report an error if it is not 'p'
let q = ctx.Procedures.Item name
if p <> q then ResolutionError ("duplicate procedure: " + name) else ()
ctx.StartNewProcedure name
// look for duplicates in ins+outs
List.iter (fun v -> ctx.AddLocal v VarKind.InParam) ins
List.iter (fun v -> ctx.AddLocal v VarKind.OutParam) outs
// resolve specification
ResolveExpr ctx req false true
ResolveExpr ctx ens true true
// resolve body
ResolveStmtList ctx body
ctx.GetLocals
let Resolve prog =
match prog with
| Prog(procs) ->
let procedures = dict [ for p in procs -> ProcedureName p, p ]
let ctx = Context(procedures)
List.map (fun p -> p, ResolveProc ctx p) procs
|