1(*
2    Copyright (c) 2013, 2016 David C.J. Matthews
3
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License version 2.1 as published by the Free Software Foundation.
7    
8    This library is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11    Lesser General Public License for more details.
12    
13    You should have received a copy of the GNU Lesser General Public
14    License along with this library; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
16*)
17
18(*
19    Derived from the original parse-tree
20
21    Copyright (c) 2000
22        Cambridge University Technical Services Limited
23
24    Further development:
25    Copyright (c) 2000-13 David C.J. Matthews
26
27    Title:      Parse Tree Structure and Operations.
28    Author:     Dave Matthews, Cambridge University Computer Laboratory
29    Copyright   Cambridge University 1985
30
31*)
32
33functor EXPORT_PARSETREE (
34
35    structure BASEPARSETREE : BaseParseTreeSig
36    structure PRINTTREE: PrintParsetreeSig
37    structure LEX : LEXSIG
38    structure STRUCTVALS : STRUCTVALSIG
39    structure EXPORTTREE: EXPORTTREESIG
40    structure TYPETREE : TYPETREESIG
41    structure DEBUGGER : DEBUGGER
42
43    sharing LEX.Sharing = TYPETREE.Sharing = STRUCTVALS.Sharing
44           = EXPORTTREE.Sharing = BASEPARSETREE.Sharing = PRINTTREE.Sharing
45           = DEBUGGER.Sharing
46
47): ExportParsetreeSig
48=
49struct 
50    open LEX
51    open STRUCTVALS
52    open EXPORTTREE
53    open TYPETREE
54    open BASEPARSETREE
55    open PRINTTREE
56
57    fun getExportTree(navigation, p: parsetree) =
58    let
59        (* Common properties for navigation and printing. *)
60        val commonProps = exportNavigationProps navigation @ [PTprint(fn d => displayParsetree(p, d))]
61
62        fun asParent () = getExportTree(navigation, p)
63
64         (* Put all these into a common list.  That simplifies navigation between
65            the various groups in abstypes and datatypes. *)
66        datatype lType = DataT of datatypebind | TypeB of typebind | Decl of parsetree * breakPoint option ref
67       
68        (* Common code for datatypes, abstypes and type bindings. *)
69        fun exportTypeBinding(navigation, this as DataT(DatatypeBind{name, nameLoc, fullLoc, constrs, tcon=ref(TypeConstrSet(tcon, _)), ...})) =
70            let
71                fun asParent () = exportTypeBinding(navigation, this)
72                (* Ignore any type variables before the type name. *)
73                fun getName () =
74                    getStringAsTree({parent=SOME asParent, previous=NONE, next=SOME getConstrs}, name, nameLoc, 
75                                    definingLocationProps(tcLocations tcon))
76                and getConstrs () =
77                    let
78                        fun exportConstrs(navigation, {constrName, idLocn, constrVal=ref(Value{locations, ...}), ... }) =
79                            (* TODO: the constructor type. *)
80                            getStringAsTree(navigation, constrName, idLocn, definingLocationProps locations)
81                    in
82                        (fullLoc, (* TODO: We need a separate location for the constrs. *)
83                            exportList(exportConstrs, SOME asParent) constrs @    
84                                exportNavigationProps {parent=SOME asParent, previous=SOME getName, next=NONE})
85                    end
86            in
87                (fullLoc, PTfirstChild getName :: exportNavigationProps navigation)
88            end
89
90        |   exportTypeBinding(navigation,
91                this as TypeB(TypeBind{name, nameLoc, decType = SOME decType, fullLoc, tcon=ref(TypeConstrSet(tcon, _)), ...})) =
92            let
93                fun asParent () = exportTypeBinding(navigation, this)
94                (* Ignore any type variables before the type name. *)
95                fun getName () =
96                    getStringAsTree({parent=SOME asParent, previous=NONE, next=SOME getType}, name, nameLoc,
97                        definingLocationProps(tcLocations tcon))
98                and getType () =
99                    typeExportTree({parent=SOME asParent, previous=SOME getName, next=NONE}, decType)
100            in
101                (fullLoc, PTfirstChild getName :: exportNavigationProps navigation)
102            end
103
104           (* TypeBind is also used in a signature in which case decType could be NONE. *)
105        |   exportTypeBinding(navigation,
106                this as TypeB(TypeBind{name, nameLoc, decType = NONE, fullLoc, tcon=ref(TypeConstrSet(tcon, _)), ...})) =
107            let
108                fun asParent () = exportTypeBinding(navigation, this)
109                (* Ignore any type variables before the type name. *)
110                (* Retain this as a child entry in case we decide to add the type vars later. *)
111                fun getName () =
112                    getStringAsTree({parent=SOME asParent, previous=NONE, next=NONE}, name, nameLoc,
113                        definingLocationProps(tcLocations tcon))
114            in
115                (fullLoc, PTfirstChild getName :: exportNavigationProps navigation)
116            end
117
118        |   exportTypeBinding(navigation, Decl dec) =
119                (* Value declarations in an abstype. *) exportTreeWithBpt(navigation, dec)
120
121        (* In a couple of cases we can have a breakpoint associated with an entry. *)
122        and exportTreeWithBpt(nav, (p, ref NONE)) = getExportTree (nav, p)
123        |   exportTreeWithBpt(nav, (p, ref (SOME bpt))) =
124            let
125                val (loc, props) = getExportTree (nav, p)
126            in
127                (loc, PTbreakPoint bpt :: props)
128            end
129        
130        fun exportMatch(navigation,
131                p as MatchTree{location, vars, exp, resType = ref rtype, argType = ref atype, breakPoint = ref bpt, ...}) =
132        let
133            fun asParent () = exportMatch(navigation, p)
134            val debugProp =
135                case bpt of
136                    NONE => []
137                |   SOME bpt => [PTbreakPoint bpt]
138        in
139            (location,
140                [PTprint(fn d => displayMatch(p, d)), PTtype (mkFunctionType (atype, rtype))] @ 
141                exportList(getExportTree, SOME asParent) [vars, exp] @
142                exportNavigationProps navigation @ debugProp
143                )
144        end
145    in
146        case p of
147            Ident{location, expType=ref expType, value, possible, name, ...} =>
148            let
149                (* Include the type and declaration properties if these
150                   have been set. *)
151                val (decProp, references, possProp) =
152                    case value of
153                        ref (Value{name = "<undefined>", ...}) =>
154                        let
155                            (* Generate possible completions.  For the moment just consider
156                               simple prefixes. *)
157                            val completions = List.filter (String.isPrefix name) (! possible ())
158                        in
159                            ([], NONE, [PTcompletions completions])
160                        end
161                    |   ref (Value{locations, references, ...}) =>
162                        let
163                            (* If this is in a pattern it could be the defining location of the id.
164                               It's complicated trying to find out exactly which is the defining location
165                               so we check to see if this is the DeclaredAt location. *)
166                            val locProps =
167                                case List.find (fn DeclaredAt l => l = location | _ => false) locations of
168                                    SOME _ => definingLocationProps locations
169                                |   NONE => mapLocationProps locations
170                        in
171                            (locProps, references, [])
172                        end
173                val refProp =
174                    case references of
175                        NONE => []
176                    |   SOME {exportedRef=ref exp, localRef=ref locals, recursiveRef=ref recs} =>
177                            [PTreferences(exp, List.map #1 recs @ locals)]
178            in
179                (location, PTtype expType :: decProp @ commonProps @ refProp @ possProp)
180            end
181
182        |   Literal {location, expType=ref expType, ...} => (location, PTtype expType :: commonProps)
183
184            (* Infixed application.  For the purposes of navigation we treat this as
185               three entries in order. *)
186        |   Applic{location, f, arg = TupleTree{fields=[left, right], ...}, isInfix = true, expType=ref expType, ...} =>
187                (location,
188                    PTtype expType :: exportList(getExportTree, SOME asParent) [left, f, right] @ commonProps)
189
190            (* Non-infixed application. *)
191        |   Applic{location, f, arg, expType=ref expType, ...} =>
192                (location, PTtype expType :: exportList(getExportTree, SOME asParent) [f, arg] @ commonProps)
193
194        |   Cond{location, test, thenpt, elsept, thenBreak, elseBreak, ...} =>
195                (location,
196                    exportList(exportTreeWithBpt, SOME asParent)
197                        [(test, ref NONE), (thenpt, thenBreak), (elsept, elseBreak)] @ commonProps)
198
199        |   TupleTree{fields, location, expType=ref expType, ...}=>
200                (location, PTtype expType :: exportList(getExportTree, SOME asParent) fields @ commonProps)
201
202        |   ValDeclaration{location, dec, ...}  =>
203            let
204                fun exportVB(navigation, vb as ValBind{dec, exp, line, ...}) =
205                    let
206                        val vbProps = exportNavigationProps navigation
207                        (* First child should give the pattern *)
208                        (* Second child should give the expression *)
209                        fun exportThis () = exportVB(navigation, vb)
210                        val asChild = exportList(getExportTree, SOME exportThis) [dec, exp]
211                    in
212                        (line, asChild @ vbProps)
213                    end
214
215                val expChild = exportList(exportVB, SOME asParent) dec
216            in
217                (* We need a special case for a top-level expression.  This has been converted
218                   by the parser into val it = exp but the "val it = " takes up no space.
219                   We need to go directly to the expression in that case. *)
220                case dec of
221                    [ValBind{dec=Ident{name="it", location=itLoc, ...}, exp, ...}]
222                    => if #startPosition itLoc = #endPosition itLoc andalso
223                          #startLine itLoc = #endLine itLoc
224                       then getExportTree(navigation, exp)
225                       else (location, expChild @ commonProps)
226                | _ => (location, expChild @ commonProps)
227            end
228
229        |   FunDeclaration{location, dec, ...}  =>
230            let
231                (* It's easiest to put these all together into a single list. *)
232                datatype funEntry =
233                    FunIdent of { name: string, expType: types ref, location: location } * values
234                |   FunPtree of parsetree
235                |   FunConstraint of typeParsetree
236                |   FunInfixed of funEntry list * location
237
238                fun exportFunEntry(navigation, FunIdent({expType=ref expType, location, ...}, Value{references, locations, ...})) =
239                    let
240                        val refProp =
241                            case references of
242                                NONE => []
243                            |   SOME {exportedRef=ref exp, localRef=ref locals, recursiveRef=ref recs} =>
244                                    [PTreferences(exp, List.map #1 recs @ locals)] 
245                    in
246                        (location, refProp @ definingLocationProps locations @ (PTtype expType :: exportNavigationProps navigation))
247                    end
248                |   exportFunEntry(navigation, FunPtree pt) = getExportTree(navigation, pt)
249                |   exportFunEntry(navigation, FunConstraint typ) = typeExportTree(navigation, typ)
250
251                |   exportFunEntry(navigation, this as FunInfixed(inf, location)) =
252                    let
253                        fun asParent () = exportFunEntry(navigation, this)
254                        val expChild = exportList(exportFunEntry, SOME asParent) inf
255                    in
256                        (location, expChild @ exportNavigationProps navigation)
257                    end
258
259                fun exportAClause(
260                        FValClause{dec = {ident, isInfix, args, constraint}, exp, breakPoint = ref bpt, ...}, idVal, exportThis) =
261                let
262                    (* The effect of this is to have all the elements of the clause as
263                       a single level except that if we have an infixed application of
264                       the function (e.g. fun f o g = ...) then this is a subnode. *)
265                    val funAndArgs =
266                        case (isInfix, args) of
267                            (true, TupleTree{fields=[left, right], location, ...} :: otherArgs) => (* Infixed. *)
268                                FunInfixed([FunPtree left, FunIdent(ident, idVal), FunPtree right], location)
269                                    :: map FunPtree otherArgs
270                        |   (_, args) => (* Normal prefixed form. *)
271                                FunIdent(ident, idVal) :: map FunPtree args
272
273                    val constraint = case constraint of NONE => [] |SOME typ => [FunConstraint typ]
274                    
275                    val debugProp =
276                        case bpt of
277                            NONE => []
278                        |   SOME bpt => [PTbreakPoint bpt]
279                in
280                    exportList(exportFunEntry, SOME exportThis) (funAndArgs @ constraint @ [FunPtree exp]) @ debugProp
281                end
282
283                fun exportFB(navigation,
284                        fb as FValBind{clauses=[clause], location, functVar = ref idVal, ...}) =
285                    (* If there's just one clause go straight to it.  Otherwise we have an
286                       unnecessary level of navigation. *)
287                    let
288                        val fbProps = exportNavigationProps navigation
289                        val asChild = exportAClause(clause, idVal, fn () => exportFB(navigation, fb))
290                    in
291                        (location, asChild @ fbProps)
292                    end
293                
294                |   exportFB(navigation, fb as FValBind{clauses, location, functVar = ref idVal, ...}) =
295                    let
296                        val fbProps = exportNavigationProps navigation
297                        (* Each child gives a clause. *)
298                        (* First child should give the pattern *)
299                        (* Second child should give the expression *)
300                        fun exportThis () = exportFB(navigation, fb)
301                        
302                        fun exportClause(navigation, clause as FValClause{ line, ...}) =
303                        let
304                            val clProps = exportNavigationProps navigation
305                            val asChild = exportAClause(clause, idVal, fn () => exportClause(navigation, clause))
306                        in
307                            (line, asChild @ clProps)    
308                        end
309                            
310                        val asChild = exportList(exportClause, SOME exportThis) clauses
311                    in
312                        (location, asChild @ fbProps)
313                    end
314
315                val expChild = exportList(exportFB, SOME asParent) dec
316            in
317                (location, expChild @ commonProps)
318            end
319
320        |   OpenDec{location, decs, ...} =>
321            let
322                fun exportStructIdent(navigation, { value, location, ...} ) =
323                    let
324                        (* Include the declaration properties if it has been set. *)
325                        val locProps =
326                            case !value of
327                                SOME(Struct{locations, ...}) => mapLocationProps locations
328                            |   NONE => []
329                        val siProps = exportNavigationProps navigation @ locProps
330                    in
331                        (location, siProps)
332                    end
333
334                val expChild = exportList(exportStructIdent, SOME asParent) decs
335            in
336                (location, expChild @ commonProps)
337            end
338
339        |   Constraint{location, value, given, ...} =>
340            let
341                (* The first position is the expression, the second the type *)
342                fun getExpr () =
343                    getExportTree({parent=SOME asParent, previous=NONE, next=SOME getType}, value)
344                and getType () =
345                    typeExportTree({parent=SOME asParent, previous=SOME getExpr, next=NONE}, given)
346            in
347                (location, PTfirstChild getExpr :: commonProps)
348            end
349
350        |   Layered{location, var, pattern, ...} =>
351                (location, exportList(getExportTree, SOME asParent) [var, pattern] @ commonProps)
352
353        |   Fn {matches, location, expType = ref expType, ...} =>
354                (location, PTtype expType :: exportList(exportMatch, SOME asParent) matches @ commonProps)
355
356        |   Localdec{location, decs, body, ...} =>
357                (location, exportList(exportTreeWithBpt, SOME asParent) (decs @ body) @ commonProps)
358
359        |   TypeDeclaration(tbl, location) =>
360            let
361                val allItems = List.map TypeB tbl
362            in
363                (location, exportList(exportTypeBinding, SOME asParent) allItems @ commonProps)
364            end
365
366        |   AbsDatatypeDeclaration { location, typelist, withtypes, declist, ... } =>
367            let
368                val allItems =
369                    List.map DataT typelist @ List.map TypeB withtypes @ List.map Decl declist
370            in
371                (location, exportList(exportTypeBinding, SOME asParent) allItems @ commonProps)
372            end
373
374        |   DatatypeReplication{location, ...} => (* TODO *) (location, commonProps)
375
376        |   ExpSeq(ptl, location) =>
377                (location, exportList(exportTreeWithBpt, SOME asParent) ptl @ commonProps)
378
379        |   Directive{location, ...} =>
380                (* No need to process the individual identifiers. *)
381                (location, commonProps)
382
383        |   ExDeclaration(exbinds, location) =>
384            let
385                (* There are three possibilities here.  exception exc; exception exc of ty; exception exc = exc' *)
386                fun exportExdec(navigation, ExBind{name, previous=EmptyTree, ofType=NONE, nameLoc, value=ref(Value{locations, ...}), ...}) =
387                        (* Simple, generative exception with no type. *)
388                        getStringAsTree(navigation, name, nameLoc, PTtype exnType :: definingLocationProps locations)
389
390                |   exportExdec(navigation,
391                        eb as ExBind{name, previous=EmptyTree, ofType=SOME ofType, nameLoc, fullLoc,
392                                     value=ref(Value{locations, ...}), ...}) =
393                        (* exception exc of type. *)
394                    let
395                        fun asParent () = exportExdec (navigation, eb)
396                        fun getName () =
397                            getStringAsTree({parent=SOME asParent, next=SOME getOfType, previous=NONE},
398                                name, nameLoc, (* Type could be in here? *)definingLocationProps locations)
399                        and getOfType () =
400                            typeExportTree({parent=SOME asParent, previous=SOME getName, next=NONE}, ofType)
401                    in
402                        (fullLoc, PTfirstChild getName :: exportNavigationProps navigation)
403                    end
404
405                |   exportExdec(navigation,
406                        eb as ExBind{name, previous, (* ofType=NONE, *) nameLoc, fullLoc, value=ref(Value{locations, ...}), ...}) =
407                    let
408                        fun asParent () = exportExdec (navigation, eb)
409                        fun getName () =
410                            getStringAsTree({parent=SOME asParent, next=SOME getPreviousExc, previous=NONE},
411                                name, nameLoc, (* Type could be in here? *)definingLocationProps locations)
412                        and getPreviousExc () =
413                            getExportTree({parent=SOME asParent, previous=SOME getName, next=NONE}, previous)
414                    in
415                        (fullLoc, PTfirstChild getName :: exportNavigationProps navigation)
416                    end
417
418                val expChild = exportList(exportExdec, SOME asParent) exbinds
419            in
420                (location, expChild @ commonProps)
421            end
422
423        |   Raise(raiseExp, location) =>
424            let
425                fun getExp () = getExportTree({parent=SOME asParent, next=NONE, previous=NONE}, raiseExp)
426            in
427               (location, [PTfirstChild getExp] @ commonProps)
428            end
429
430        |   HandleTree{location, exp, hrules, listLocation, ...} =>
431            let
432                (* The first position is the expression, the second the matches *)
433                fun getExpr () = getExportTree({parent=SOME asParent, previous=NONE, next=SOME getMatches}, exp)
434                and getMatches () =
435                    (listLocation,
436                        exportList(exportMatch, SOME getMatches) hrules @
437                            exportNavigationProps{parent=SOME asParent, previous=SOME getExpr, next=NONE})
438            in
439                (location, [PTfirstChild getExpr] @ commonProps)
440            end
441
442        |   While{location, test, body, breakPoint, ...}           =>
443                (location,
444                    exportList(exportTreeWithBpt, SOME asParent)
445                        [(test, ref NONE), (body, breakPoint)] @ commonProps)
446
447        |   Case{location, test, match, listLocation, expType=ref expType, ...}            =>
448            let
449                (* The first position is the expression, the second the matches *)
450                fun getExpr () = getExportTree({parent=SOME asParent, previous=NONE, next=SOME getMatches}, test)
451                and getMatches () =
452                    (listLocation,
453                        exportList(exportMatch, SOME getMatches) match @
454                            exportNavigationProps{parent=SOME asParent, previous=SOME getExpr, next=NONE})
455            in
456                (location, [PTfirstChild getExpr, PTtype expType] @ commonProps)
457            end
458
459        |   Andalso {location, first, second, ...} =>
460                (location, exportList(getExportTree, SOME asParent) [first, second] @ commonProps)
461
462        |   Orelse{location, first, second, ...} =>
463                (location, exportList(getExportTree, SOME asParent) [first, second] @ commonProps)
464
465        |   Labelled{location, expType=ref expType, recList, ...} =>
466            let
467                (* It's convenient to be able to click on the label part and get
468                   the type of the expression or pattern on the right of the '='. *)
469                fun exportField(navigation,
470                        label as {name, nameLoc, valOrPat, expType=ref expType, fullLocation, ...}) =
471                let
472                    val patTree as (patLocation, _) = getExportTree(navigation, valOrPat)
473                in
474                    if patLocation = fullLocation
475                    then
476                        (* The parser rewrites { name, ...} as { name=name, ... } (more generally
477                           { name: ty as pat, ...} as { name = name: ty as pat).
478                           To avoid having nodes that overlap we return only the pattern part here. *)
479                        patTree
480                    else
481                    let
482                        (* The first position is the label, the second the type *)
483                        fun asParent () = exportField (navigation, label)
484                        fun getLab () =
485                            getStringAsTree({parent=SOME asParent, next=SOME getExp, previous=NONE},
486                                name, nameLoc, [PTtype expType])
487                        and getExp () =
488                            getExportTree({parent=SOME asParent, previous=SOME getLab, next=NONE}, valOrPat)
489                    in
490                        (fullLocation, PTfirstChild getLab :: exportNavigationProps navigation)
491                    end
492                end
493
494                val expChild = exportList(exportField, SOME asParent) recList
495            in
496                (location, PTtype expType :: (expChild @ commonProps))
497            end
498
499        |   Selector{location, typeof, ...} => (location, PTtype typeof :: commonProps)
500
501        |   List{elements, location, expType = ref expType, ...} =>
502                (location,
503                    PTtype expType :: exportList(getExportTree, SOME asParent) elements @ commonProps)
504
505        |   EmptyTree                      => (nullLocation, commonProps)
506
507        |   WildCard location              => (location, commonProps)
508
509        |   Unit location                  => (location, PTtype unitType :: commonProps)
510
511        |   Parenthesised(p, _) => getExportTree(navigation, p)
512    end
513    
514    fun getLocation c = #1 (getExportTree({parent=NONE, next=NONE, previous=NONE}, c))
515
516    (* Extract the declaration location from the location list. *)
517    fun declaredAt [] = LEX.nullLocation
518    |   declaredAt (DeclaredAt loc :: _) = loc
519    |   declaredAt (_::l) = declaredAt l
520
521    (* Types that can be shared. *)
522    structure Sharing =
523    struct
524        type lexan = lexan
525        and  parsetree = parsetree
526        and  matchtree = matchtree
527        and  locationProp = locationProp
528        and  pretty = pretty
529        and  ptProperties = ptProperties
530    end
531
532end;
533
534