Import Upstream version 20180207
[hcoop/debian/mlton.git] / mlyacc / src / mklrtable.sml
CommitLineData
7f918cf1
CE
1(* ML-Yacc Parser Generator (c) 1989 Andrew W. Appel, David R. Tarditi *)
2
3functor mkMakeLrTable (structure IntGrammar : INTGRAMMAR
4 structure LrTable : LR_TABLE
5 sharing type LrTable.term = IntGrammar.Grammar.term
6 sharing type LrTable.nonterm = IntGrammar.Grammar.nonterm
7 ) : MAKE_LR_TABLE =
8 struct
9 val sub = Array.sub
10 infix 9 sub
11 structure Core = mkCore(structure IntGrammar = IntGrammar)
12 structure CoreUtils = mkCoreUtils(structure IntGrammar = IntGrammar
13 structure Core = Core)
14 structure Graph = mkGraph(structure IntGrammar = IntGrammar
15 structure Core = Core
16 structure CoreUtils = CoreUtils)
17 structure Look = mkLook(structure IntGrammar = IntGrammar)
18 structure Lalr = mkLalr(structure IntGrammar = IntGrammar
19 structure Core = Core
20 structure Graph = Graph
21 structure Look = Look)
22 structure LrTable = LrTable
23 structure IntGrammar = IntGrammar
24 structure Grammar = IntGrammar.Grammar
25 structure GotoList = ListOrdSet
26 (struct
27 type elem = Grammar.nonterm * LrTable.state
28 val eq = fn ((Grammar.NT a,_),(Grammar.NT b,_)) => a=b
29 val gt = fn ((Grammar.NT a,_),(Grammar.NT b,_)) => a>b
30 end)
31 structure Errs : LR_ERRS =
32 struct
33 structure LrTable = LrTable
34 datatype err = RR of LrTable.term * LrTable.state * int * int
35 | SR of LrTable.term * LrTable.state * int
36 | NOT_REDUCED of int
37 | NS of LrTable.term * int
38 | START of int
39
40 val summary = fn l =>
41 let val numRR = ref 0
42 val numSR = ref 0
43 val numSTART = ref 0
44 val numNOT_REDUCED = ref 0
45 val numNS = ref 0
46 fun loop (h::t) =
47 (case h
48 of RR _ => numRR := !numRR+1
49 | SR _ => numSR := !numSR+1
50 | START _ => numSTART := !numSTART+1
51 | NOT_REDUCED _ => numNOT_REDUCED := !numNOT_REDUCED+1
52 | NS _ => numNS := !numNS+1; loop t)
53 | loop nil = {rr = !numRR, sr = !numSR,
54 start = !numSTART,
55 not_reduced = !numNOT_REDUCED,
56 nonshift = !numNS}
57 in loop l
58 end
59
60 val printSummary = fn say => fn l =>
61 let val {rr,sr,start,
62 not_reduced,nonshift} = summary l
63 val say_plural = fn (i,s) =>
64 (say (Int.toString i); say " ";
65 case i
66 of 1 => (say s)
67 | _ => (say s; say "s"))
68 val say_error = fn (args as (i,s)) =>
69 case i
70 of 0 => ()
71 | i => (say_plural args; say "\n")
72 in say_error(rr,"reduce/reduce conflict");
73 say_error(sr,"shift/reduce conflict");
74 if nonshift<>0 then
75 (say "non-shiftable terminal used on the rhs of ";
76 say_plural(start,"rule"); say "\n")
77 else ();
78 if start<>0 then (say "start symbol used on the rhs of ";
79 say_plural(start,"rule"); say "\n")
80 else ();
81 if not_reduced<>0 then (say_plural(not_reduced,"rule");
82 say " not reduced\n")
83 else ()
84 end
85 end
86
87
88 open IntGrammar Grammar Errs LrTable Core
89
90(* rules for resolving conflicts:
91
92 shift/reduce:
93
94 If either the terminal or the rule has no
95 precedence, a shift/reduce conflict is reported.
96 A shift is chosen for the table.
97
98 If both have precedences, the action with the
99 higher precedence is chosen.
100
101 If the precedences are equal, neither the
102 shift nor the reduce is chosen.
103
104 reduce/reduce:
105
106 A reduce/reduce conflict is reported. The lowest
107 numbered rule is chosen for reduction.
108*)
109
110
111(* method for filling tables - first compute the reductions called for in a
112 state, then add the shifts for the state to this information.
113
114How to compute the reductions:
115
116 A reduction initially is given as an item and a lookahead set calling
117for reduction by that item. The first reduction is mapped to a list of
118terminal * rule pairs. Each additional reduction is then merged into this
119list and reduce/reduce conflicts are resolved according to the rule
120given.
121
122Missed Errors:
123
124 This method misses some reduce/reduce conflicts that exist because
125some reductions are removed from the list before conflicting reductions
126can be compared against them. All reduce/reduce conflicts, however,
127can be generated given a list of the reduce/reduce conflicts generated
128by this method.
129
130 This can be done by taking the transitive closure of the relation given
131by the list. If reduce/reduce (a,b) and reduce/reduce (b,c) are true,
132then reduce/reduce (a,c) is true. The relation is symmetric and transitive.
133
134Adding shifts:
135
136 Finally scan the list merging in shifts and resolving conflicts
137according to the rule given.
138
139Missed Shift/Reduce Errors:
140
141 Some errors may be missed by this method because some reductions were
142removed as the result of reduce/reduce conflicts. For a shift/reduce
143conflict of term a, reduction by rule n, shift/reduce conficts exist
144for all rules y such that reduce/reduce (x,y) or reduce/reduce (y,x)
145is true.
146*)
147
148 fun unREDUCE (REDUCE num) = num
149 | unREDUCE _ = raise Fail "bug: unexpected action (expected REDUCE)"
150
151 val mergeReduces =
152 let val merge = fn state =>
153 let fun f (j as (pair1 as (T t1,action1)) :: r1,
154 k as (pair2 as (T t2,action2)) :: r2,result,errs) =
155 if t1 < t2 then f(r1,k,pair1::result,errs)
156 else if t1 > t2 then f(j,r2,pair2::result,errs)
157 else let val num1 = unREDUCE action1
158 val num2 = unREDUCE action2
159 val errs = RR(T t1,state,num1,num2) :: errs
160 val action = if num1 < num2 then pair1 else pair2
161 in f(r1,r2,action::result,errs)
162 end
163 | f (nil,nil,result,errs) = (rev result,errs)
164 | f (pair1::r,nil,result,errs) = f(r,nil,pair1::result,errs)
165 | f (nil,pair2 :: r,result,errs) = f(nil,r,pair2::result,errs)
166 in f
167 end
168 in fn state => fn ((ITEM {rule=RULE {rulenum,...},...}, lookahead),
169 (reduces,errs)) =>
170 let val action = REDUCE rulenum
171 val actions = map (fn a=>(a,action)) lookahead
172 in case reduces
173 of nil => (actions,errs)
174 | _ => merge state (reduces,actions,nil,errs)
175 end
176 end
177
178 val computeActions = fn (rules,precedence,graph,defaultReductions) =>
179
180 let val rulePrec =
181 let val precData = Array.array(length rules,NONE : int option)
182 in app (fn RULE {rulenum=r,precedence=p,...} => Array.update(precData,r,p))
183 rules;
184 fn i => precData sub i
185 end
186
187 fun mergeShifts(state,shifts,nil) = (shifts,nil)
188 | mergeShifts(state,nil,reduces) = (reduces,nil)
189 | mergeShifts(state,shifts,reduces) =
190 let fun f(shifts as (pair1 as (T t1,_)) :: r1,
191 reduces as (pair2 as (T t2,action)) :: r2,
192 result,errs) =
193 if t1 < t2 then f(r1,reduces,pair1 :: result,errs)
194 else if t1 > t2 then f(shifts,r2,pair2 :: result,errs)
195 else let val rulenum = unREDUCE action
196 val (term1,_) = pair1
197 in case (precedence term1,rulePrec rulenum)
198 of (SOME i,SOME j) =>
199 if i>j then f(r1,r2,pair1 :: result,errs)
200 else if j>i then f(r1,r2,pair2 :: result,errs)
201 else f(r1,r2,(T t1, ERROR)::result,errs)
202 | (_,_) =>
203 f(r1,r2,pair1 :: result,
204 SR (term1,state,rulenum)::errs)
205 end
206 | f (nil,nil,result,errs) = (rev result,errs)
207 | f (nil,h::t,result,errs) =
208 f (nil,t,h::result,errs)
209 | f (h::t,nil,result,errs) =
210 f (t,nil,h::result,errs)
211 in f(shifts,reduces,nil,nil)
212 end
213
214 fun mapCore ({edge=symbol,to=CORE (_,state)}::r,shifts,gotos) =
215 (case symbol
216 of (TERM t) => mapCore (r,(t,SHIFT(STATE state))::shifts,gotos)
217 | (NONTERM nt) => mapCore(r,shifts,(nt,STATE state)::gotos)
218 )
219 | mapCore (nil,shifts,gotos) = (rev shifts,rev gotos)
220
221 fun pruneError ((_,ERROR)::rest) = pruneError rest
222 | pruneError (a::rest) = a :: pruneError rest
223 | pruneError nil = nil
224
225 in fn (Lalr.LCORE (reduceItems,state),c as CORE (shiftItems,state')) =>
226 if DEBUG andalso (state <> state') then
227 let exception MkTable in raise MkTable end
228 else
229 let val (shifts,gotos) = mapCore (Graph.edges(c,graph),nil,nil)
230 val tableState = STATE state
231 in case reduceItems
232 of nil => ((shifts,ERROR),gotos,nil)
233 | h :: nil =>
234 let val (ITEM {rule=RULE {rulenum,...},...}, l) = h
235 val (reduces,_) = mergeReduces tableState (h,(nil,nil))
236 val (actions,errs) = mergeShifts(tableState,
237 shifts,reduces)
238 val actions' = pruneError actions
239 val (actions,default) =
240 let fun hasReduce (nil,actions) =
241 (rev actions,REDUCE rulenum)
242 | hasReduce ((a as (_,SHIFT _)) :: r,actions) =
243 hasReduce(r,a::actions)
244 | hasReduce (_ :: r,actions) =
245 hasReduce(r,actions)
246 fun loop (nil,actions) = (rev actions,ERROR)
247 | loop ((a as (_,SHIFT _)) :: r,actions) =
248 loop(r,a::actions)
249 | loop ((a as (_,REDUCE _)) :: r,actions) =
250 hasReduce(r,actions)
251 | loop (_ :: r,actions) = loop(r,actions)
252 in if defaultReductions
253 andalso length actions = length actions'
254 then loop(actions,nil)
255 else (actions',ERROR)
256 end
257 in ((actions,default), gotos,errs)
258 end
259 | l =>
260 let val (reduces,errs1) =
261 List.foldr (mergeReduces tableState) (nil,nil) l
262 val (actions,errs2) =
263 mergeShifts(tableState,shifts,reduces)
264 in ((pruneError actions,ERROR),gotos,errs1@errs2)
265 end
266 end
267 end
268
269 val mkTable = fn (grammar as GRAMMAR{rules,terms,nonterms,start,
270 precedence,termToString,noshift,
271 nontermToString,eop},defaultReductions) =>
272 let val symbolToString = fn (TERM t) => termToString t
273 | (NONTERM nt) => nontermToString nt
274 val {rules,graph,produces,epsProds,...} = Graph.mkGraph grammar
275 val {nullable,first} =
276 Look.mkFuncs{rules=rules,produces=produces,nonterms=nonterms}
277 val lcores = Lalr.addLookahead
278 {graph=graph,
279 nullable=nullable,
280 produces=produces,
281 eop=eop,
282 nonterms=nonterms,
283 first=first,
284 rules=rules,
285 epsProds=epsProds,
286 print=(fn s=>TextIO.output(TextIO.stdOut,s)),
287 termToString = termToString,
288 nontermToString = nontermToString}
289
290 fun zip (h::t,h'::t') = (h,h') :: zip(t,t')
291 | zip (nil,nil) = nil
292 | zip _ = let exception MkTable in raise MkTable end
293
294 fun unzip l =
295 let fun f ((a,b,c)::r,j,k,l) = f(r,a::j,b::k,c::l)
296 | f (nil,j,k,l) = (rev j,rev k,rev l)
297 in f(l,nil,nil,nil)
298 end
299
300 val (actions,gotos,errs) =
301 let val doState =
302 computeActions(rules,precedence,graph,
303 defaultReductions)
304 in unzip (map doState (zip(lcores,Graph.nodes graph)))
305 end
306
307 (* add goto from state 0 to a new state. The new state
308 has accept actions for all of the end-of-parse symbols *)
309
310 val (actions,gotos,errs) =
311 case gotos
312 of nil => (actions,gotos,errs)
313 | h :: t =>
314 let val newStateActions =
315 (map (fn t => (t,ACCEPT)) (Look.make_set eop),ERROR)
316 val state0Goto =
317 GotoList.insert((start,STATE (length actions)),h)
318 in (actions @ [newStateActions],
319 state0Goto :: (t @ [nil]),
320 errs @ [nil])
321 end
322
323 val startErrs =
324 List.foldr (fn (RULE {rhs,rulenum,...},r) =>
325 if (List.exists (fn NONTERM a => a=start
326 | _ => false) rhs)
327 then START rulenum :: r
328 else r) [] rules
329
330 val nonshiftErrs =
331 List.foldr (fn (RULE {rhs,rulenum,...},r) =>
332 (List.foldr (fn (nonshift,r) =>
333 if (List.exists (fn TERM a => a=nonshift
334 | _ => false) rhs)
335 then NS(nonshift,rulenum) :: r
336 else r) r noshift)
337 ) [] rules
338
339 val notReduced =
340 let val ruleReduced = Array.array(length rules,false)
341 val test = fn REDUCE i => Array.update(ruleReduced,i,true)
342 | _ => ()
343 val _ = app (fn (actions,default) =>
344 (app (fn (_,r) => test r) actions;
345 test default)
346 ) actions;
347 fun scan (i,r) =
348 if i >= 0 then
349 scan(i-1, if ruleReduced sub i then r
350 else NOT_REDUCED i :: r)
351 else r
352 in scan(Array.length ruleReduced-1,nil)
353 end handle Subscript =>
354 (if DEBUG then
355 print "rules not numbered correctly!"
356 else (); nil)
357
358 val numstates = length actions
359
360 val allErrs = startErrs @ notReduced @ nonshiftErrs @
361 (List.concat errs)
362
363 fun convert_to_pairlist(nil : ('a * 'b) list): ('a,'b) pairlist =
364 EMPTY
365 | convert_to_pairlist ((a,b) :: r) =
366 PAIR(a,b,convert_to_pairlist r)
367
368 in (mkLrTable {actions=Array.fromList(map (fn (a,b) =>
369 (convert_to_pairlist a,b)) actions),
370 gotos=Array.fromList (map convert_to_pairlist gotos),
371 numRules=length rules,numStates=length actions,
372 initialState=STATE 0},
373 let val errArray = Array.fromList errs
374 in fn (STATE state) => errArray sub state
375 end,
376
377 fn print =>
378 let val printCore =
379 prCore(symbolToString,nontermToString,print)
380 val core = Graph.core graph
381 in fn STATE state =>
382 printCore (if state=(numstates-1) then
383 Core.CORE (nil,state)
384 else (core state))
385 end,
386 allErrs)
387 end
388end;