File: FinalPolyML.sml

package info (click to toggle)
polyml 5.7.1-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid
  • size: 40,616 kB
  • sloc: cpp: 44,142; ansic: 26,963; sh: 22,002; asm: 13,486; makefile: 602; exp: 525; python: 253; awk: 91
file content (2214 lines) | stat: -rw-r--r-- 105,967 bytes parent folder | download | duplicates (3)
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
(*
    Title:      Nearly final version of the PolyML structure
    Author:     David Matthews
    Copyright   David Matthews 2008-9, 2014, 2015-17

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License version 2.1 as published by the Free Software Foundation.
    
    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
    
    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*)

(*
Based on:

    Title:      Poly Make Program.
    Author:     Dave Matthews, Cambridge University Computer Laboratory
    Copyright   Cambridge University 1985
*)


(*
This is the version of the PolyML structure that can be compiled after we
have the rest of the basis library.  In particular it binds in TextIO.stdIn
and TextIO.stdOut.

This contains the top-level read-eval-print loop as well as "use" and
Poly/ML's "make".

The rootFunction has now been pulled out into a separate file and is added on
after this. 
*)
local
    (* A hash table with a mutex that protects against multiple threads
       rehashing the table by entering values at the same time. *)
    structure ProtectedTable :>
    sig
        type 'a ptable
        val create: unit -> 'a ptable
        val lookup: 'a ptable -> string -> 'a option
        val enter: 'a ptable -> string * 'a -> unit
        val all: 'a ptable -> unit -> (string * 'a) list
        val delete: 'a ptable -> string -> unit
    end
    =
    struct
        open HashArray Thread.Mutex LibraryIOSupport
        type 'a ptable = 'a hash * mutex

        fun create () = (hash 10, mutex())
        and lookup(tab, mutx) = protect mutx (fn s => sub(tab, s))
        and enter(tab, mutx) = protect mutx (fn (s, v) => update(tab, s, v))
        and all(tab, mutx) = protect mutx (fn () => fold (fn (s, v, l) => ((s, v) :: l)) [] tab)
        and delete(tab, mutx) = protect mutx (fn s => HashArray.delete (tab, s))
    end

    fun quickSort _                      ([]:'a list)      = []
    |   quickSort _                      ([h]:'a list)     = [h]
    |   quickSort (leq:'a -> 'a -> bool) ((h::t) :'a list) =
    let
        val (after, befor) = List.partition (leq h) t
    in
        quickSort leq befor @ (h :: quickSort leq after)
    end

    open PolyML.NameSpace
 
    local
        open ProtectedTable
        val fixTable = create() and sigTable = create() and valTable = create()
        and typTable = create() and fncTable = create() and strTable = create()
    in
        val globalNameSpace: PolyML.NameSpace.nameSpace =
        {
            lookupFix    = lookup fixTable,
            lookupSig    = lookup sigTable,
            lookupVal    = lookup valTable,
            lookupType   = lookup typTable,
            lookupFunct  = lookup fncTable,
            lookupStruct = lookup strTable,
            enterFix     = enter fixTable,
            enterSig     = enter sigTable,
            enterVal     = enter valTable,
            enterType    = enter typTable,
            enterFunct   = enter fncTable,
            enterStruct  = enter strTable,
            allFix       = all fixTable,
            allSig       = all sigTable,
            allVal       = all valTable,
            allType      = all typTable,
            allFunct     = all fncTable,
            allStruct    = all strTable
        }

        val forgetFix    = delete fixTable
        and forgetSig    = delete sigTable
        and forgetVal    = delete valTable
        and forgetType   = delete typTable
        and forgetFunct  = delete fncTable
        and forgetStruct = delete strTable
    end

    local
        open PolyML (* For prettyprint datatype *)

        (* Install a pretty printer for parsetree properties.  This isn't done in
           the compiler. *)
        fun prettyProps depth _ l =
            if depth <= 0 then PrettyString "..."
            else prettyProp(l, depth-1)
        
        (* Use prettyRepresentation to print most of the arguments *)
        and prettyProp(PTbreakPoint b, d) =     blockArg("PTbreakPoint", prettyRepresentation(b, d))
        |   prettyProp(PTcompletions s, d) =    blockArg("PTcompletions", prettyRepresentation(s, d))
        |   prettyProp(PTdeclaredAt l, d) =     blockArg("PTdeclaredAt", prettyRepresentation(l, d))
        |   prettyProp(PTdefId i, d) =          blockArg("PTdefId", prettyRepresentation(i, d))
        |   prettyProp(PTfirstChild _, _) =     blockArg("PTfirstChild", PrettyString "fn")
        |   prettyProp(PTnextSibling _, _) =    blockArg("PTnextSibling", PrettyString "fn")
        |   prettyProp(PTopenedAt f, d) =       blockArg("PTopenedAt", prettyRepresentation(f, d))
        |   prettyProp(PTparent _, _) =         blockArg("PTparent", PrettyString "fn")
        |   prettyProp(PTpreviousSibling _, _)= blockArg("PTpreviousSibling", PrettyString "fn")
        |   prettyProp(PTprint _, _) =          blockArg("PTprint", PrettyString "fn")
        |   prettyProp(PTreferences f, d) =     blockArg("PTreferences", prettyRepresentation(f, d))
        |   prettyProp(PTrefId f, d) =          blockArg("PTrefId", prettyRepresentation(f, d))
        |   prettyProp(PTstructureAt f, d) =    blockArg("PTstructureAt", prettyRepresentation(f, d))
        |   prettyProp(PTtype f, d) =           blockArg("PTtype", prettyRepresentation(f, d))
        
        and blockArg (s, arg) =
            PrettyBlock(3, true, [], [PrettyString s, PrettyBreak(1, 1), parenthesise arg])
        
        and parenthesise(p as PrettyBlock(_, _, _, PrettyString "(" :: _)) = p
        |   parenthesise(p as PrettyBlock(_, _, _, PrettyString "{" :: _)) = p
        |   parenthesise(p as PrettyBlock(_, _, _, PrettyString "[" :: _)) = p
        |   parenthesise(p as PrettyBlock(_, _, _, _ :: _)) =
                PrettyBlock(3, true, [], [ PrettyString "(", PrettyBreak(0, 0), p, PrettyBreak(0, 0), PrettyString ")" ])
        |   parenthesise p = p

    in
        val () = addPrettyPrinter prettyProps
    end

    (* PolyML.compiler takes a list of these parameter values.  They all
       default so it's possible to pass only those that are actually
       needed. *)
    datatype compilerParameters =
        CPOutStream of string->unit
        (* Output stream for debugging and other output from the compiler.
           Provides a default stream for other output.  Default: TextIO.print *)
    |   CPNameSpace of PolyML.NameSpace.nameSpace
        (* Name space to look up and enter results.  Default: globalNameSpace *)
    |   CPErrorMessageProc of
            { message: PolyML.pretty, hard: bool, location: PolyML.location, context: PolyML.pretty option } -> unit
        (* Called by the compiler to generate error messages.
           Arguments (message, isHard, lineNo, context).  message is the message.
           isHard is true if this is an error, false if a warning. 
           location is the file-name, line number and position.  context is an
           optional extra piece of information showing the part of the parse tree
           where the error was detected.
           Default: print this to CPOutStream value using CPLineNo and CPFileName. *)
    |   CPLineNo of unit -> int
        (* Called by the compiler to get the current "line number".  This is passed
           to CPErrorMessageProc and the debugger.  It may actually be a more general
           location than a source line.  Default: fn () => 0 i.e. no line numbering. *)
    |   CPLineOffset of unit -> int
        (* Called by the compiler to get the current "offset".  This is passed
           to CPErrorMessageProc and the debugger.  This may either be an offset on
           the current file, a byte offset or simply zero.
           Default: fn () => 0 i.e. no line offset. *)
    |   CPFileName of string
        (* The current file being compiled.  This is used by the default CPErrorMessageProc
           and the debugger.  Default: "" i.e. interactive stream. *)
    |   CPPrintInAlphabeticalOrder of bool
        (* Whether to sort the results by alphabetical order before printing them.  Applies
           only to the default CPResultFun.  Default value of printInAlphabeticalOrder. *)
    |   CPResultFun of {
            fixes: (string * Infixes.fixity) list, values: (string * Values.value) list,
            structures: (string * Structures.structureVal) list, signatures: (string * Signatures.signatureVal) list,
            functors: (string * Functors.functorVal) list, types: (string * TypeConstrs.typeConstr) list} -> unit
        (* Function to apply to the result of compiling and running the code.
           Default: print and enter the values into CPNameSpace. *)
    |   CPCompilerResultFun of
            PolyML.parseTree option *
            ( unit -> {
                fixes: (string * Infixes.fixity) list, values: (string * Values.value) list,
                structures: (string * Structures.structureVal) list, signatures: (string * Signatures.signatureVal) list,
                functors: (string * Functors.functorVal) list, types: (string * TypeConstrs.typeConstr) list}) option -> unit -> unit
        (* Function to process the result of compilation.  This can be used to capture the
           parse tree even if type-checking fails.
           Default: Execute the code and call the result function if the compilation
           succeeds.  Raise an exception if the compilation failed. *)
    |   CPProfiling of int
        (* Deprecated: No longer used. *)
    |   CPTiming of bool
        (* Deprecated: No longer used.  *)
    |   CPDebug of bool
        (* Control whether calls to the debugger should be inserted into the compiled
           code.  This allows breakpoints to be set, values to be examined and printed
           and functions to be traced at the cost of extra run-time overhead.
           Default: value of PolyML.Compiler.debug *)
    |   CPPrintDepth of unit->int
        (* This controls the depth of printing if the default CPResultFun is used.  It
           is also bound into any use of PolyML.print in the compiled code and will
           be called to get the print depth whenever that code is executed.
           Default: Get the current value of PolyML.print_depth. *)
    |   CPPrintStream of string->unit
        (* This is bound into any occurrence of PolyML.print and is used to produce
           the outut.  Default: CPOutStream. *)
    |   CPErrorDepth of int
        (* Controls the depth of context to produce in error messages.
           Default : value of PolyML.error_depth. *)
    |   CPLineLength of int
        (* Bound into any occurrences of PolyML.print.  This is the length of a line
           used in the pretty printer.  Default: value of PolyML.line_length. *)
    |   CPRootTree of
        {
            parent: (unit -> PolyML.parseTree) option,
            next: (unit -> PolyML.parseTree) option,
            previous: (unit -> PolyML.parseTree) option
        }
        (* This can be used to provide a parent for parse trees created by the
           compiler.  This appears as a PTparent property in the tree.
           The default is NONE which does not to provide a parent.  *)
    |   CPAllocationProfiling of int
        (* Controls whether to add profiling information to each allocation.  Currently
           zero means no profiling and one means add the allocating function. *)

    |   CPDebuggerFunction of int * Values.value * int * string * string * nameSpace -> unit
        (* Deprecated: No longer used. *)

    |   CPBindingSeq of unit -> int
        (* Used to create a sequence no for PTdefId properties.  This can be used in an IDE
           to allocate a unique Id for an identifier.  Default fn _ => 0. *)

    (* References for control and debugging. *)
    val timing = ref false
    and printDepth: int ref = ref 0
    and errorDepth: int ref = ref 6
    and lineLength: int ref = ref 77
    and allocationProfiling = ref false
    
    val assemblyCode = ref false
    and codetree = ref false
    and codetreeAfterOpt = ref false
    and icode = ref false
    and parsetree = ref false
    and reportUnreferencedIds = ref false
    and reportExhaustiveHandlers = ref false
    and narrowOverloadFlexRecord = ref false
    and createPrintFunctions = ref true
    and reportDiscardFunction = ref true
    and reportDiscardNonUnit = ref false
    val lowlevelOptimise = ref true
    
    val debug = ref false
    val inlineFunctors = ref true
    val maxInlineSize: int ref = ref 80
    val printInAlphabeticalOrder = ref true
    val traceCompiler = ref false (* No longer used. *)
    
    fun prettyPrintWithIDEMarkup(stream : string -> unit, lineWidth : int): PolyML.pretty -> unit =
    let
        open PolyML
        val openDeclaration = "\u001bD"
        val closeDeclaration = "\u001bd"
        val separator = "\u001b,"
        val finalSeparator = "\u001b;"
        
        fun beginMarkup context =
            case List.find (fn ContextLocation _ => true | _ => false) context of
                SOME (ContextLocation{file,startLine,startPosition,endPosition, ...}) =>
                let
                    (* In the unlikely event there's an escape character in the
                       file name convert it to ESC-ESC. *)
                    fun escapeEscapes #"\u001b" = "\u001b\u001b"
                    |   escapeEscapes c = str c
                in
                    stream openDeclaration;
                    stream(String.translate escapeEscapes file);
                    stream separator;
                    stream(FixedInt.toString startLine);
                    stream separator;
                    stream(FixedInt.toString startPosition);
                    stream separator;
                    stream(FixedInt.toString endPosition);
                    stream finalSeparator
                end
            |   _ => ()
            
        fun endMarkup context =
            List.app (fn ContextLocation _ => stream closeDeclaration | _ => ()) context
    in
        prettyMarkup (beginMarkup, endMarkup) (stream, lineWidth)
    end;

    (* useMarkupInOutput is set according to the setting of *)
    val useMarkupInOutput = ref false
    fun prettyPrintWithOptionalMarkup(stream, lineWidth) =
        if ! useMarkupInOutput then prettyPrintWithIDEMarkup(stream, lineWidth)
        else PolyML.prettyPrint(stream, lineWidth)

    (* Top-level prompts. *)
    val prompt1 = ref "> " and prompt2 = ref "# ";

    fun printOut s =
        TextIO.print s
        (* If we get an exception while writing to stdOut we've got
           a big problem and can't continue.  It could happen if
           we have closed stdOut.  Try reporting the error through
           stdErr and exit. *)
        handle Thread.Thread.Interrupt => raise Thread.Thread.Interrupt
        |     exn =>
            (
                (
                    TextIO.output(TextIO.stdErr,
                        concat["Exception ", exnName exn,
                               " raised while writing to stdOut.\n"]);
                    TextIO.flushOut TextIO.stdErr (* probably unnecessary. *)
                ) handle _ => ();
                (* Get out without trying to do anything else. *)
                OS.Process.terminate OS.Process.failure
            )

    (* Default function to print and enter a value. *)
    fun printAndEnter (inOrder: bool, space: PolyML.NameSpace.nameSpace,
                       stream: string->unit, depth: int)
        { fixes: (string * Infixes.fixity) list, values: (string * Values.value) list,
          structures: (string * Structures.structureVal) list, signatures: (string * Signatures.signatureVal) list,
          functors: (string * Functors.functorVal) list, types: (string * TypeConstrs.typeConstr) list}: unit =
    let
        (* We need to merge the lists to sort them alphabetically. *)
        datatype decKind =
            FixStatusKind of Infixes.fixity
        |   TypeConstrKind of TypeConstrs.typeConstr
        |   SignatureKind of Signatures.signatureVal
        |   StructureKind of Structures.structureVal
        |   FunctorKind of Functors.functorVal
        |   ValueKind of Values.value

        val decList =
            map (fn (s, f) => (s, FixStatusKind f)) fixes @
            map (fn (s, f) => (s, TypeConstrKind f)) types @
            map (fn (s, f) => (s, SignatureKind f)) signatures @
            map (fn (s, f) => (s, StructureKind f)) structures @
            map (fn (s, f) => (s, FunctorKind f)) functors @
            map (fn (s, f) => (s, ValueKind f)) values

        fun kindToInt(FixStatusKind _) = 0
        |   kindToInt(TypeConstrKind _) = 1
        |   kindToInt(SignatureKind _) = 2
        |   kindToInt(StructureKind _) = 3
        |   kindToInt(FunctorKind _) = 4
        |   kindToInt(ValueKind _) = 5

        fun order (s1: string, k1) (s2, k2) =
                if s1 = s2 then kindToInt k1 <= kindToInt k2
                else s1 <= s2

        (* Don't sort the declarations if we want them in declaration order. *)
        val sortedDecs =
            if inOrder then quickSort order decList else decList

        fun enterDec(n, FixStatusKind f) = #enterFix space (n,f)
        |   enterDec(n, TypeConstrKind t) = #enterType space (n,t)
        |   enterDec(n, SignatureKind s) = #enterSig space (n,s)
        |   enterDec(n, StructureKind s) = #enterStruct space (n,s)
        |   enterDec(n, FunctorKind f) = #enterFunct space (n,f)
        |   enterDec(n, ValueKind v) = #enterVal space (n,v)

        fun printDec(_, FixStatusKind f) =
                prettyPrintWithOptionalMarkup (stream, !lineLength) (Infixes.print f)

        |   printDec(_, TypeConstrKind t) =
                prettyPrintWithOptionalMarkup (stream, !lineLength) (TypeConstrs.print(t, FixedInt.fromInt depth, SOME space))

        |   printDec(_, SignatureKind s) =
                prettyPrintWithOptionalMarkup (stream, !lineLength) (Signatures.print(s, FixedInt.fromInt depth, SOME space))

        |   printDec(_, StructureKind s) =
                prettyPrintWithOptionalMarkup (stream, !lineLength) (Structures.print(s, FixedInt.fromInt depth, SOME space))

        |   printDec(_, FunctorKind f) =
                prettyPrintWithOptionalMarkup (stream, !lineLength) (Functors.print(f, FixedInt.fromInt depth, SOME space))

        |   printDec(_, ValueKind v) =
                if Values.isConstructor v andalso not (Values.isException v)
                then () (* Value constructors are printed with the datatype. *)
                else prettyPrintWithOptionalMarkup (stream, !lineLength) (Values.printWithType(v, FixedInt.fromInt depth, SOME space))

    in
        (* First add the declarations to the name space and then print them.  Doing it this way
           improves the printing of types since these require look-ups in the name space.  For
           instance the constructors of a datatype from an opened structure should not include
           the structure name but that will only work once the datatype itself is in the global
           name-space. *)
        List.app enterDec sortedDecs;
        if depth > 0 then List.app printDec sortedDecs else ()
    end

    local
        open Bootstrap Bootstrap.Universal
        (* To allow for the possibility of changing the representation we don't make Universal
           be the same as Bootstrap.Universal. *)

        (* Default error message function. *)
        fun defaultErrorProc printString
            {message: PolyML.pretty, hard: bool,
             location={startLine, startPosition, endPosition, file, ...}: PolyML.location,
             context: PolyML.pretty option} =
        let
            open PolyML
            val fullMessage =
                case context of
                    NONE => message
                |   SOME ctxt =>
                        PrettyBlock(0, true, [],
                            [ message, PrettyBreak(1, 0),
                                PrettyBlock(2, false, [], [PrettyString "Found near", PrettyBreak(1, 0), ctxt])
                            ])
        in
            if ! useMarkupInOutput
            then (* IDE mark-up of error messages.  This is actually the same as within the IDE. *)
            let
                val openError = "\u001bE"
                val closeError = "\u001be"
                val separator = "\u001b,"
                val finalSeparator = "\u001b;"
            in
                printString(
                    concat
                        [
                            openError,
                            if hard then "E" else "W", separator,
                            file, (* TODO double any escapes. *) separator,
                            FixedInt.toString startLine, separator,
                            FixedInt.toString startPosition, separator,
                            FixedInt.toString endPosition, finalSeparator
                         ]
                    );
                prettyPrintWithIDEMarkup(printString, !lineLength) fullMessage;
                printString closeError
            end
            else (* Plain text form. *)
            (
                printString(concat
                   ( (if file = "" then ["poly: "] else [file, ":"]) @
                     (if startLine = 0 then [] else [FixedInt.toString startLine]) @
                     (if startPosition = 0 then [": "] else [".", FixedInt.toString startPosition, "-", FixedInt.toString endPosition, ": "]) @
                     (if hard then ["error: "] else ["warning: "]) ));
(*                   ( (if hard then ["Error-"] else ["Warning-"]) @
                     (if file = "" then [] else [" in '", file, "',"]) @
                     (if startLine = 0 then [] else [" line ", Int.toString startLine]) @
                     (if startLine = 0 andalso file = "" then [] else [".\n"]))); *)
                PolyML.prettyPrint(printString, !lineLength) fullMessage
            )
        end
    in
        (* This function ends up as PolyML.compiler.  *)
        fun polyCompiler (getChar: unit->char option, parameters: compilerParameters list) =
        let
            (* Find the first item that matches or return the default. *)
            fun find _ def [] = def
              | find f def (hd::tl) =
                  case f hd of
                      SOME s => s
                  |   NONE => find f def tl
        
            val outstream = find (fn CPOutStream s => SOME s | _ => NONE) TextIO.print parameters
            val nameSpace = find (fn CPNameSpace n => SOME n | _ => NONE) globalNameSpace parameters
            val lineNo = find (fn CPLineNo l => SOME l | _ => NONE) (fn () => 0) parameters
            val lineOffset = find (fn CPLineOffset l => SOME l | _ => NONE) (fn () => 0) parameters
            val fileName = find (fn CPFileName s => SOME s | _ => NONE) "" parameters
            val printInOrder = find (fn CPPrintInAlphabeticalOrder t => SOME t | _ => NONE)
                                (! printInAlphabeticalOrder) parameters
            val printDepth = find (fn CPPrintDepth f => SOME f | _ => NONE) (fn () => !printDepth) parameters
            val resultFun = find (fn CPResultFun f => SOME f | _ => NONE)
               (printAndEnter(printInOrder, nameSpace, outstream, printDepth())) parameters
            val printString = find (fn CPPrintStream s => SOME s | _ => NONE) outstream parameters
            val errorProc =  find (fn CPErrorMessageProc f => SOME f | _ => NONE) (defaultErrorProc printString) parameters
            val debugging = find (fn CPDebug t => SOME t | _ => NONE) (! debug) parameters
            val allocProfiling = find(fn CPAllocationProfiling l  => SOME l | _ => NONE) (if !allocationProfiling then 1 else 0) parameters
            val bindingSeq = find(fn CPBindingSeq l  => SOME l | _ => NONE) (fn () => 0) parameters
            local
                (* Default is to filter the parse tree argument. *)
                fun defaultCompilerResultFun (_, NONE) = raise Fail "Static Errors"
                |   defaultCompilerResultFun (_, SOME code) = fn () => resultFun(code()) 
            in
                val compilerResultFun = find (fn CPCompilerResultFun f => SOME f | _ => NONE)
                    defaultCompilerResultFun parameters
            end

            (* TODO: Make this available as a parameter. *)
            val prettyOut = prettyPrintWithOptionalMarkup(printString, !lineLength)
            
            val compilerOut = prettyPrintWithOptionalMarkup(outstream, !lineLength)

            (* Parent tree defaults to empty. *)
            val parentTree =
                find (fn CPRootTree f => SOME f | _ => NONE)
                    { parent = NONE, next = NONE, previous = NONE } parameters

            (* Pass all the settings.  Some of these aren't included in the parameters datatype (yet?). *)
            val treeAndCode =
                PolyML.compiler(nameSpace, getChar,
                    [
                    tagInject errorMessageProcTag errorProc,
                    tagInject compilerOutputTag compilerOut,
                    tagInject lineNumberTag (FixedInt.fromInt o lineNo),
                    tagInject offsetTag (FixedInt.fromInt o lineOffset),
                    tagInject fileNameTag fileName,
                    tagInject bindingCounterTag (FixedInt.fromInt o bindingSeq),
                    tagInject inlineFunctorsTag (! inlineFunctors),
                    tagInject maxInlineSizeTag (FixedInt.fromInt(! maxInlineSize)),
                    tagInject parsetreeTag (! parsetree),
                    tagInject codetreeTag (! codetree),
                    tagInject icodeTag (! icode),
                    tagInject lowlevelOptimiseTag (! lowlevelOptimise),
                    tagInject assemblyCodeTag (! assemblyCode),
                    tagInject codetreeAfterOptTag (! codetreeAfterOpt),
                    tagInject profileAllocationTag (FixedInt.fromInt allocProfiling),
                    tagInject errorDepthTag (FixedInt.fromInt(! errorDepth)),
                    tagInject printDepthFunTag (FixedInt.fromInt o printDepth),
                    tagInject lineLengthTag (FixedInt.fromInt(! lineLength)),
                    tagInject debugTag debugging,
                    tagInject printOutputTag prettyOut,
                    tagInject rootTreeTag parentTree,
                    tagInject reportUnreferencedIdsTag (! reportUnreferencedIds),
                    tagInject reportExhaustiveHandlersTag (! reportExhaustiveHandlers),
                    tagInject narrowOverloadFlexRecordTag (! narrowOverloadFlexRecord),
                    tagInject createPrintFunctionsTag (! createPrintFunctions),
                    tagInject reportDiscardedValuesTag
                        (if ! reportDiscardNonUnit then 2 else if ! reportDiscardFunction then 1 else 0)
                    ])
        in
            compilerResultFun treeAndCode
        end

        (* Top-level read-eval-print loop.  This is the normal top-level loop and is
           also used for the debugger. *)
        fun topLevel {isDebug, nameSpace, exitLoop, exitOnError, isInteractive, startExec, endExec } =
        let
            (* This is used as the main read-eval-print loop.  It is also invoked
               by running code that has been compiled with the debug option on
               when it stops at a breakpoint.  In that case debugEnv contains an
               environment formed from the local variables.  This is placed in front
               of the normal top-level environment. *)
           
            (* Don't use the end_of_stream because it may have been set by typing
               EOT to the command we were running. *)
            val endOfFile    = ref false;
            val realDataRead = ref false;
            val lastWasEol   = ref true;
    
            (* Each character typed is fed into the compiler but leading
               blank lines result in the prompt remaining as firstPrompt until
               significant characters are typed. *)
            fun readin () : char option =
            let
                val () =
                    if isInteractive andalso !lastWasEol (* Start of line *)
                    then if !realDataRead
                    then printOut (if isDebug then "debug " ^ !prompt2 else !prompt2)
                    else printOut (if isDebug then "debug " ^ !prompt1 else !prompt1)
                    else ();
             in
                case TextIO.input1 TextIO.stdIn of
                    NONE => (endOfFile := true; NONE)
                |   SOME #"\n" => ( lastWasEol := true; SOME #"\n" )
                |   SOME ch =>
                       (
                           lastWasEol := false;
                           if ch <> #" "
                           then realDataRead := true
                           else ();
                           SOME ch
                       )
            end; (* readin *)
    
            (* Remove all buffered but unread input. *)
            fun flushInput () =
                case TextIO.canInput(TextIO.stdIn, 1) of
                    SOME 1 => (TextIO.inputN(TextIO.stdIn, 1); flushInput())
                |   _ => (* No input waiting or we're at EOF. *) ()
    
            fun readEvalPrint () : unit =
            let
            in
                realDataRead := false;
                (* Compile and then run the code. *)
                let
                    val startCompile = Timer.startCPUTimer()

                    (* Compile a top-level declaration/expression. *)
                    val code =
                        polyCompiler (readin, [CPNameSpace nameSpace, CPOutStream printOut])
                            (* Don't print any times if this raises an exception. *)
                        handle exn as Fail s =>
                        (
                            printOut(s ^ "\n");
                            flushInput();
                            lastWasEol := true;
                            PolyML.Exception.reraise exn
                        )
                        
                    val endCompile = Timer.checkCPUTimer startCompile

                    (* Run the code *)
                    val startRun = Timer.startCPUTimer()
                    val () = startExec() (* Enable any debugging *)
                    (* Run the code and capture any exception (temporarily). *)
                    val finalResult = (code(); NONE) handle exn => SOME exn
                    val () = endExec() (* Turn off debugging *)
                    (* Print the times if required. *)
                    val endRun = Timer.checkCPUTimer startRun
                    val () =
                        if !timing
                        then printOut(
                                concat["Timing - compile: ", Time.fmt 1 (#usr endCompile + #sys endCompile),
                                       " run: ", Time.fmt 1 (#usr endRun + #sys endRun), "\n"])
                        else ()
                in
                    case finalResult of
                        NONE => () (* No exceptions raised. *)
                    |   SOME exn => (* Report exceptions in running code. *)
                        let
                            open PolyML PolyML.Exception
                            val exLoc =
                                case exceptionLocation exn of
                                    NONE => []
                                |   SOME loc => [ContextLocation loc]
                        in
                            prettyPrintWithOptionalMarkup(TextIO.print, ! lineLength)
                                (PrettyBlock(0, false, [],
                                    [
                                        PrettyBlock(0, false, exLoc, [PrettyString "Exception-"]),
                                        PrettyBreak(1, 3),
                                        prettyRepresentation(exn, FixedInt.fromInt(! printDepth)),
                                        PrettyBreak(1, 3),
                                        PrettyString "raised"
                                    ]));
                            PolyML.Exception.reraise exn
                        end
                end
            end; (* readEvalPrint *)
            
            fun handledLoop () : unit =
            (
                (* Process a single top-level command. *)
                readEvalPrint()
                    handle Thread.Thread.Interrupt =>
                        (* Allow ^C to terminate the debugger and raise Interrupt in
                           the called program. *)
                        if exitOnError then OS.Process.exit OS.Process.failure
                        else if isDebug then (flushInput(); raise Thread.Thread.Interrupt)
                        else ()
                    |   _ =>
                        if exitOnError
                        then OS.Process.exit OS.Process.failure
                        else ();
                (* Exit if we've seen end-of-file or we're in the debugger
                   and we've run "continue". *)
                if !endOfFile orelse exitLoop() then ()
                else handledLoop ()
            )
        in
            handledLoop ()  
        end
    end

    val suffixes = ref ["", ".ML", ".sml"]
 

    (*****************************************************************************)
    (*                  "use": compile from a file.                              *)
    (*****************************************************************************)
   
    val useFileTag: string option Universal.tag = Universal.tag()
    fun getUseFileName(): string option = Option.join (Thread.Thread.getLocal useFileTag)

    fun use (originalName: string): unit =
    let
        (* use "f" first tries to open "f" but if that fails it tries "f.ML", "f.sml" etc. *)
        (* We use the functional layer and a reference here rather than TextIO.input1 because
           that requires locking round every read to make it thread-safe.  We know there's
           only one thread accessing the stream so we don't need it here. *)
        fun trySuffixes [] =
            (* Not found - attempt to open the original and pass back the
               exception. *)
            (TextIO.getInstream(TextIO.openIn originalName), originalName)
         |  trySuffixes (s::l) =
            (TextIO.getInstream(TextIO.openIn (originalName ^ s)), originalName ^ s)
                handle IO.Io _ => trySuffixes l
        (* First in list is the name with no suffix. *)
        val (inStream, fileName) = trySuffixes("" :: ! suffixes)
        val stream = ref inStream
        (* Record the file name.  This allows nested calls to "use" to set the
           correct path. *)
        val oldName = getUseFileName()
        val () = Thread.Thread.setLocal(useFileTag, SOME fileName)

        val lineNo   = ref 1;
        fun getChar () : char option =
            case TextIO.StreamIO.input1 (! stream) of
                NONE => NONE
            |   SOME (eoln as #"\n", strm) =>
                (
                    lineNo := !lineNo + 1;
                    stream := strm;
                    SOME eoln
                )
            |   SOME(c, strm) => (stream := strm; SOME c)
    in
        while not (TextIO.StreamIO.endOfStream(!stream)) do
        let
            val code = polyCompiler(getChar, [CPFileName fileName, CPLineNo(fn () => !lineNo)])
                handle exn =>
                    ( TextIO.StreamIO.closeIn(!stream); PolyML.Exception.reraise exn )
        in
            code() handle exn =>
            (
                (* Report exceptions in running code. *)
                TextIO.print ("Exception- " ^ exnMessage exn ^ " raised\n");
                TextIO.StreamIO.closeIn (! stream);
                Thread.Thread.setLocal(useFileTag, oldName);
                PolyML.Exception.reraise exn
            )
        end;
        (* Normal termination: close the stream. *)
        TextIO.StreamIO.closeIn (! stream);
        Thread.Thread.setLocal(useFileTag, oldName)

    end (* use *)
 
    local
        open Time
    in
        fun maxTime (x : time, y : time): time = 
            if x < y then y else x
    end

    exception ObjNotFile;
    
    type 'a tag = 'a Universal.tag;
  
    fun splitFilename (name: string) : string * string =
    let
         val {dir, file } = OS.Path.splitDirFile name
    in
         (dir, file)
    end

    (* Make *)
    (* There are three possible states - The object may have been checked,
     it may be currently being compiled, or it may not have been
     processed yet. *)
    datatype compileState = NotProcessed | Searching | Checked;

    fun longName (directory, file) = OS.Path.joinDirFile{dir=directory, file = file}
    
    local
        fun fileReadable (fileTuple as (directory, object)) =
            (* Use OS.FileSys.isDir just to test if the file/directory exists. *)
            if (OS.FileSys.isDir (longName fileTuple); false) handle OS.SysErr _ => true
            then false
            else
            let
                (* Check that the object is present in the directory with the name
                 given and not a case-insensitive version of it.  This avoids
                 problems with "make" attempting to recursively make Array etc
                 because they contain signatures ARRAY. *)
                open OS.FileSys
                val d = openDir (if directory = "" then "." else directory)
                fun searchDir () =
                  case readDir d of
                     NONE => false
                  |  SOME f => f = object orelse searchDir ()
                val present = searchDir()
            in
                closeDir d;
                present
            end
    
        fun findFileTuple _                   [] = NONE
        |   findFileTuple (directory, object) (suffix :: suffixes) =
        let
            val fileName  = object ^ suffix
            val fileTuple = (directory, fileName)
        in
            if fileReadable fileTuple
            then SOME fileTuple
            else findFileTuple (directory, object) suffixes
        end
    
    in
        fun filePresent (directory : string, kind: string option, object : string) =
        let
            (* Construct suffixes with the architecture and version number in so
               we can compile architecture- and version-specific code. *)
            val archSuffix = "." ^ String.map Char.toLower (PolyML.architecture())
            val versionSuffix = "." ^ Int.toString Bootstrap.compilerVersionNumber
            val extraSuffixes =
                case kind of
                    NONE => [archSuffix, versionSuffix, ""]
                |   SOME k => ["." ^ k ^ archSuffix, "." ^ k ^ versionSuffix, "." ^ k, archSuffix, versionSuffix, ""]
            val addedSuffixes =
                List.foldr(fn (i, l) => (List.map (fn s => s ^ i) extraSuffixes) @ l) [] (!suffixes)
        in
            (* For each of the suffixes in the list try it. *)
            findFileTuple (directory, object) addedSuffixes
        end
    end
    
    (* See if the corresponding file is there and if it is a directory. *)
    fun testForDirectory (name: string) : bool =
        OS.FileSys.isDir name handle OS.SysErr _ => false (* No such file. *)

    (* Time stamps. *)
    type timeStamp = Time.time;
    val firstTimeStamp : timeStamp = Time.zeroTime;
    
    local
        open ProtectedTable
        (* Global tables to hold information about entities that have been made using "make". *)
        val timeStampTable: timeStamp ptable = create()
        and dependencyTable: string list ptable = create()
    in
        (* When was the entity last built?  Returns zeroTime if it hasn't. *)
        fun lastMade (objectName : string) : timeStamp =
            getOpt(lookup timeStampTable objectName, firstTimeStamp)

        (* Get the dependencies as an option type. *)
        val getMakeDependencies = lookup dependencyTable

        (* Set the time stamp and dependencies. *)
        fun updateMakeData(objectName, times, depends) =
        (
            enter timeStampTable (objectName, times);
            enter dependencyTable (objectName, depends)
        )
    end

    (* Main make function *)
    fun make (targetName: string) : unit =
    let
        local
            val sourceDateEpochEnv : string option = OS.Process.getEnv "SOURCE_DATE_EPOCH";
        in
            val sourceDateEpoch : timeStamp option =
                case sourceDateEpochEnv of
                     NONE => NONE
                   | SOME s =>
                       (case LargeInt.fromString s of
                             NONE => NONE
                           | SOME t => SOME(Time.fromSeconds t) handle Time.Time => NONE)
        end;

        (* Get the current time. *)
        val newTimeStamp : unit -> timeStamp = case sourceDateEpoch of
                                                    NONE => Time.now
                                                  | SOME t => fn _ => t;
        (* Get the date of a file. *)
        val fileTimeStamp : string -> timeStamp = case sourceDateEpoch of
                                                    NONE => OS.FileSys.modTime
                                                  | SOME t => fn _ => t;

        (* This serves two purposes. It provides a list of objects which have been
           re-made to prevent them being made more than once, and it also prevents
           circular dependencies from causing infinite loops (e.g. let x = f(x)) *)
            local
                open HashArray;
                val htab : compileState hash = hash 10;
            in
                fun lookupStatus (name: string) : compileState =
                    getOpt(sub (htab, name), NotProcessed);
                  
                fun setStatus (name: string, cs: compileState) : unit =
                    update (htab, name, cs)
            end;

        (* Remove leading directory names to get the name of the object itself.
           e.g. "../compiler/parsetree/gencode" yields simply "gencode". *)
        val (dirName,objectName) = splitFilename targetName;
 
        (* Looks to see if the file is in the current directory. If it is and
           the file is newer than the corresponding object then it must be
           remade. If it is a directory then we attempt to remake the
           directory by compiling the "bind" file. This will only actually be
           executed if it involves some identifier which is newer than the
           result object. *)
        fun remakeObj (objName: string, kind: string option, findDirectory: string option -> string -> string) =
        let
        (* Find a directory that contains this object. An exception will be
             raised if it is not there. *)
            val directory = findDirectory kind objName
            val fullName  =
                if directory = "" (* Work around for bug. *)
                then objName
                else OS.Path.joinDirFile{dir=directory, file=objName}

            val objIsDir  = testForDirectory fullName
            val here      = fullName
      
            (* Look to see if the file exists, possibly with an extension,
               and get the extended version. *)
            val fileTuple =
                let
                    (* If the object is a directory the source is in the bind file. *)
                    val (dir : string, file : string) =
                        if objIsDir
                        then (here,"ml_bind")
                        else (directory, objName);
                in
                    case filePresent (dir, kind, file) of
                        SOME res' => res'
                    |   NONE      => raise Fail ("No such file or directory ("^file^","^dir^")")
                end ;
            
            val fileName = longName fileTuple;

            val newFindDirectory : string option -> string -> string =
                if objIsDir
                then
                let
                    (* Look in this directory then in the ones above. *)
                    fun findDirectoryHere kind (name: string) : string =
                        case filePresent (here, kind, name) of
                          NONE => findDirectory kind name (* not in this directory *)
                        | _    => here;
                in
                    findDirectoryHere
                end
                else findDirectory
    
            (* Compiles a file. *)
            fun remakeCurrentObj () =
            let
                val () = print ("Making " ^ objName ^ "\n");
                local
                    (* Keep a list of the dependencies. *)
                    val deps : bool HashArray.hash = HashArray.hash 10;
                    
                    fun addDep name =
                        if getOpt(HashArray.sub (deps, name), true)
                        then HashArray.update(deps, name, true)
                        else ();
                    
                    (* Called by the compiler to look-up a global identifier. *)
                    fun lookupMakeEnv (globalLook, kind: string option) (name: string) : 'a option =
                    let
                        (* Have we re-declared it ? *)
                        val res = lookupStatus name;
                    in
                        case res of
                            NotProcessed  =>
                            (
                                (* Compile the dependency. *)
                                remakeObj (name, kind, newFindDirectory);
                                (* Add this to the dependencies. *)
                                addDep name
                            )

                        |  Searching => (* In the process of making it *)
                           print("Circular dependency: " ^ name ^  " depends on itself\n")

                        | Checked => addDep name; (* Add this to the dependencies. *)

                        (* There was previously a comment about returning NONE here if
                           we had a problem remaking a dependency. *)
                        globalLook name
                    end (* lookupMakeEnv *)

                    (* Enter the declared value in the table. Usually this will be the
                        target we are making. Also set the state to "Checked". The
                        state is set to checked when we finish making the object but
                        setting it now suppresses messages about circular dependencies
                        if we use the identifier within the file. *)
                    fun enterMakeEnv (kind : string, enterGlobal) (name: string, v: 'a) : unit =
                    (
                        (* Put in the value. *)
                        enterGlobal (name, v);
                        print ("Created " ^ kind ^ " " ^ name ^ "\n");
                        
                        (* The name we're declaring may appear to be a dependency
                           but isn't, so don't include it in the list. *)
                        HashArray.update (deps, name, false);
                        
                        if name = objName
                        then
                        let
                            (* Put in the dependencies i.e. those names set to true in the table. *)
                            val depends =
                                HashArray.fold (fn (s, v, l) => if v then s :: l else l) [] deps;
                            
                            (* Put in a time stamp for the new object.  We need to make
                               sure that it is no older than the newest object it depends
                               on.  In theory that should not be a problem but clocks on
                               different machines can get out of step leading to objects
                               made later having earlier time stamps. *)
                            val newest =
                                List.foldl (fn (s: string, t: timeStamp) =>
                                    maxTime (lastMade s, t)) (fileTimeStamp fileName) depends;
                            
                            val timeStamp = maxTime(newest, newTimeStamp());
                        in         
                            setStatus (name, Checked);
                            updateMakeData(name, timeStamp, depends)
                        end
                        else ()
                    ) (* enterMakeEnv *);
     
                in
                    val makeEnv =
                        { 
                            lookupFix    = #lookupFix globalNameSpace,
                            lookupVal    = #lookupVal globalNameSpace,
                            lookupType   = #lookupType globalNameSpace,
                            lookupSig    = lookupMakeEnv (#lookupSig globalNameSpace, SOME "signature"),
                            lookupStruct = lookupMakeEnv (#lookupStruct globalNameSpace, SOME "structure"),
                            lookupFunct  = lookupMakeEnv (#lookupFunct globalNameSpace, SOME "functor"),
                            enterFix     = #enterFix globalNameSpace,
                            enterVal     = #enterVal globalNameSpace,
                            enterType    = #enterType globalNameSpace,
                            enterStruct  = enterMakeEnv ("structure", #enterStruct globalNameSpace),
                            enterSig     = enterMakeEnv ("signature", #enterSig globalNameSpace),
                            enterFunct   = enterMakeEnv ("functor", #enterFunct globalNameSpace),
                            allFix       = #allFix globalNameSpace,
                            allVal       = #allVal globalNameSpace,
                            allType      = #allType globalNameSpace,
                            allSig       = #allSig globalNameSpace,
                            allStruct    = #allStruct globalNameSpace,
                            allFunct     = #allFunct globalNameSpace
                        };
                end; (* local for makeEnv *)

                val inputFile = OS.Path.joinDirFile{dir= #1 fileTuple, file= #2 fileTuple}
    
                val inStream = TextIO.openIn inputFile;
    
                val () =
                let (* scope of exception handler to close inStream *)
                    val endOfStream = ref false;
                    val lineNo     = ref 1;
        
                    fun getChar () : char option =
                        case TextIO.input1 inStream of
                            NONE => (endOfStream := true; NONE) (* End of file *)
                        |   eoln as SOME #"\n" => (lineNo := !lineNo + 1; eoln)
                        |   c => c
                 in
                    while not (!endOfStream) do
                    let
                        val code = polyCompiler(getChar,
                            [CPNameSpace makeEnv, CPFileName fileName, CPLineNo(fn () => !lineNo)])
                    in
                        code ()
                            handle exn as Fail _ => PolyML.Exception.reraise exn
                            |  exn =>
                            (
                                print ("Exception- " ^ exnMessage exn ^ " raised\n");
                                PolyML.Exception.reraise exn
                            )
                    end
                end (* body of scope of inStream *)
                    handle exn => (* close inStream if an error occurs *)
                    (
                        TextIO.closeIn inStream;
                        PolyML.Exception.reraise exn
                    )
            in (* remake normal termination *)
                TextIO.closeIn inStream 
            end (* remakeCurrentObj *)
            
        in (* body of remakeObj *)
            setStatus (objName, Searching);
         
             (* If the file is newer than the object then we definitely must remake it.
               Otherwise we look at the dependency list and check those. If the result
               of that check is that one of the dependencies is newer than the object
               (probably because it has just been recompiled) we have to recompile
               the file. Compiling a file also checks the dependencies and recompiles
               them, generating a new dependency list. That is why we don't check the
               dependency list if the object is out of date with the file. Also if the
               file has been changed it may no longer depend on the things it used to
               depend on. *)
 
            let
                val objDate = lastMade objName
       
                fun maybeRemake (s:string) : unit =
                case lookupStatus s of
                    NotProcessed => (* see if it's a file. *)
                        (* Compile the dependency. *)
                        remakeObj(s, kind, newFindDirectory)
                    
                    | Searching => (* In the process of making it *)
                        print ("Circular dependency: " ^ s ^ " depends on itself\n")
                    
                    |  Checked => () (* do nothing *)

                open Time
                (* Process each entry and return true if
                   any is newer than the target. *)
                val processChildren =
                    List.foldl
                    (fn (child:string, parentNeedsMake:bool) =>
                        (
                            maybeRemake child;
                            (* Find its date and see if it is newer. *)
                            parentNeedsMake orelse lastMade child > objDate
                        )
                    )
                    false;
            in
                if objDate < fileTimeStamp fileName orelse
                    (
                        (* Get the dependency list. There may not be one if
                           this object has not been compiled with "make". *) 
                        case getMakeDependencies objName of
                            SOME d => processChildren d
                        |   NONE => true (* No dependency list - must use "make" on it. *)
                    )       
                then remakeCurrentObj ()
                else ()
            end;

            (* Mark it as having been checked. *)
            setStatus (objName, Checked)
        end (* body of remakeObj *)
  
        (* If the object is not a file mark it is checked. It may be a
           pervasive or it may be missing. In either case mark it as checked
           to save searching for it again. *)
        handle
                ObjNotFile => setStatus (objName, Checked)
            
            |   exn => (* Compilation (or execution) error. *)
                (
                    (* Mark as checked to prevent spurious messages. *)
                    setStatus (objName, Checked);
                    raise exn
                )
    in (*  body of make *)
        (* Check that the target exists. *)
        case filePresent (dirName, NONE, objectName) of
            NONE =>
            let
                val dir =
                    if dirName = "" then ""
                    else " (directory "^dirName^")";
                val s = "File "^objectName^" not found" ^ dir
            in
                print (s ^ "\n");
                raise Fail s
            end
        
        | _ =>
        let
            val targetIsDir = testForDirectory targetName;
            
            (* If the target we are making is a directory all the objects
               must be in the directory. If it is a file we allow references
               to other objects in the same directory. Objects not found must
               be pervasive. *)
            fun findDirectory kind (s: string) : string =
                if (not targetIsDir orelse s = objectName) andalso
                    isSome(filePresent(dirName, kind, s))
                then dirName
                else raise ObjNotFile;
        in
            remakeObj (objectName, NONE, findDirectory)
                handle exn  => 
                (
                    print (targetName ^ " was not declared\n");
                    PolyML.Exception.reraise exn
                )
        end
    end (* make *)

in
    structure PolyML =
    struct
        open PolyML
        (* We must not have a signature on the result otherwise print and makestring
           will be given polymorphic types and will only produce "?" *)

        val globalNameSpace = globalNameSpace

        val use = use and make = make
        val suffixes = suffixes and getUseFileName = getUseFileName
        val compiler = polyCompiler

        val prettyPrintWithIDEMarkup = prettyPrintWithIDEMarkup

        structure Compiler =
        struct
            datatype compilerParameters = datatype compilerParameters

            val compilerVersion = Bootstrap.compilerVersion
            val compilerVersionNumber = Bootstrap.compilerVersionNumber

            val forgetSignature: string -> unit = forgetSig
            and forgetStructure: string -> unit = forgetStruct
            and forgetFunctor: string -> unit = forgetFunct
            and forgetValue: string -> unit = forgetVal
            and forgetType: string -> unit = forgetType
            and forgetFixity: string -> unit = forgetFix

            fun signatureNames (): string list = #1(ListPair.unzip (#allSig globalNameSpace ()))
            and structureNames (): string list = #1(ListPair.unzip (#allStruct globalNameSpace ()))
            and functorNames (): string list = #1(ListPair.unzip (#allFunct globalNameSpace ()))
            and valueNames (): string list = #1(ListPair.unzip (#allVal globalNameSpace ()))
            and typeNames (): string list = #1(ListPair.unzip (#allType globalNameSpace ()))
            and fixityNames (): string list = #1(ListPair.unzip (#allFix globalNameSpace ()))

            val prompt1 = prompt1 and prompt2 = prompt2
            and timing = timing and printDepth = printDepth
            and errorDepth = errorDepth and lineLength = lineLength
            and allocationProfiling = allocationProfiling
            
            val assemblyCode = assemblyCode and codetree = codetree
            and codetreeAfterOpt = codetreeAfterOpt and icode = icode
            and parsetree = parsetree and reportUnreferencedIds = reportUnreferencedIds
            and lowlevelOptimise = lowlevelOptimise and reportExhaustiveHandlers = reportExhaustiveHandlers
            and narrowOverloadFlexRecord = narrowOverloadFlexRecord
            and createPrintFunctions = createPrintFunctions
            and reportDiscardFunction = reportDiscardFunction
            and reportDiscardNonUnit = reportDiscardNonUnit
            
            val debug = debug
            val inlineFunctors = inlineFunctors
            val maxInlineSize = maxInlineSize
            val printInAlphabeticalOrder = printInAlphabeticalOrder
            val traceCompiler = traceCompiler
        end
        
        (* Debugger control.  Extend DebuggerInterface set up by INITIALISE.  Replaces the original DebuggerInterface. *)
        structure DebuggerInterface:
        sig
            type debugState
            val debugFunction: debugState -> string
            val debugFunctionArg: debugState -> PolyML.NameSpace.Values.value
            val debugFunctionResult: debugState -> PolyML.NameSpace.Values.value
            val debugLocation: debugState -> PolyML.location
            val debugNameSpace: debugState -> PolyML.NameSpace.nameSpace
            val debugLocalNameSpace: debugState -> PolyML.NameSpace.nameSpace
            val debugState: Thread.Thread.thread -> debugState list
            
            val setOnBreakPoint: (PolyML.location * bool ref -> unit) option -> unit
            val setOnEntry: (string * PolyML.location -> unit) option -> unit
            val setOnExit: (string * PolyML.location -> unit) option -> unit
            val setOnExitException: (string * PolyML.location -> exn -> unit) option -> unit
        end =
        struct
            open PolyML.DebuggerInterface
 
            fun debugState(t: Thread.Thread.thread): debugState list =
            let
                val stack = RunCall.loadWord(t, 0w5)
                and static = RunCall.loadWord(t, 0w6)
                and dynamic = RunCall.loadWord(t, 0w7)
                and locationInfo = RunCall.loadWord(t, 0w8)

                (* Turn the chain of saved entries along with the current top entry
                   into a list.  The bottom entry will generally be the state from
                   non-debugging code and needs to be filtered out. *)
                fun toList r =
                    if RunCall.isShort r
                    then []
                    else
                    let
                        val s = RunCall.loadWordFromImmutable(r, 0w0)
                        and d = RunCall.loadWordFromImmutable(r, 0w1)
                        and l = RunCall.loadWordFromImmutable(r, 0w2)
                        and n = RunCall.loadWordFromImmutable(r, 0w3)
                    in
                        if RunCall.isShort s orelse
                           RunCall.isShort l
                        then toList n
                        else (s, d, l) :: toList n
                    end
            in
                if RunCall.isShort static orelse RunCall.isShort locationInfo
                then toList stack
                else (static, dynamic, locationInfo) :: toList stack
            end

            fun searchEnvs match (staticEntry :: statics, dlist as dynamicEntry :: dynamics) =
            (
                case (match (staticEntry, dynamicEntry), staticEntry) of
                    (SOME result, _) => SOME result
                |   (NONE, EnvTypeid _) => searchEnvs match (statics, dynamics)
                |   (NONE, EnvVConstr _) => searchEnvs match (statics, dynamics)
                |   (NONE, EnvValue _) => searchEnvs match (statics, dynamics)
                |   (NONE, EnvException _) => searchEnvs match (statics, dynamics)
                |   (NONE, EnvStructure _) => searchEnvs match (statics, dynamics)
                |   (NONE, EnvStartFunction _) => searchEnvs match (statics, dynamics)
                |   (NONE, EnvEndFunction _) => searchEnvs match (statics, dynamics)
                        (* EnvTConstr doesn't have an entry in the dynamic list *)
                |   (NONE, EnvTConstr _) => searchEnvs match (statics, dlist)
        
            )
    
            |   searchEnvs _ _ = NONE
            (* N.B.  It is possible to have ([EnvTConstr ...], []) in the arguments so we can't assume
               that if either the static or dynamic list is nil and the other non-nil it's an error. *)

            (* Function argument.  This should always be present but if
               it isn't just return unit.  That's probably better than
               an exception here. *)
            fun debugFunctionArg (state: debugState as (cList, rList, _)) =
            let
                val d = (cList, rList)
                fun match (EnvStartFunction(_, _, ty), valu) =
                    SOME(makeAnonymousValue state (ty, valu))
                |   match _ = NONE
            in
                getOpt(searchEnvs match d, unitValue) 
            end

            (* Function result - only valid in exit function. *)
            and debugFunctionResult (state: debugState as (cList, rList, _)) =
            let
                val d = (cList, rList)
                fun match (EnvEndFunction(_, _, ty), valu) =
                    SOME(makeAnonymousValue state(ty, valu))
                |   match _ = NONE
            in
                getOpt(searchEnvs match d, unitValue)
            end

            (* debugFunction just looks at the static data.
               There should always be an EnvStartFunction entry. *)
            fun debugFunction ((cList, _, _): debugState): string =
            (
                case List.find(fn (EnvStartFunction _) => true | _ => false) cList of
                    SOME(EnvStartFunction(s, _, _)) => s
                |   _ => "?"
            )

            fun debugLocation ((_, _, locn): debugState) = locn

            fun nameSpace localOnly (state: debugState as (clist, rlist, _)) : nameSpace =
            let
                val debugEnviron = (clist, rlist)

                (* Lookup and "all" functions for the environment.  We can't easily use a general
                   function for the lookup because we have dynamic entries for values and structures
                   but not for type constructors. *)
                fun lookupValues (EnvValue(name, ty, location) :: ntl, valu :: vl) s =
                        if name = s
                        then SOME(makeValue state (name, ty, location, valu))
                        else lookupValues(ntl, vl) s

                |   lookupValues (EnvException(name, ty, location) :: ntl, valu :: vl) s =
                        if name = s
                        then SOME(makeException state (name, ty, location, valu))
                        else lookupValues(ntl, vl) s

                |   lookupValues (EnvVConstr(name, ty, nullary, count, location) :: ntl, valu :: vl) s =
                        if name = s
                        then SOME(makeConstructor state (name, ty, nullary, count, location, valu))
                        else lookupValues(ntl, vl) s

                |   lookupValues (EnvTConstr _ :: ntl, vl) s = lookupValues(ntl, vl) s
                
                |   lookupValues (EnvStartFunction _ :: ntl, _ :: vl) s =
                        if localOnly then NONE else lookupValues(ntl, vl) s
        
                |   lookupValues (_ :: ntl, _ :: vl) s = lookupValues(ntl, vl) s

                |   lookupValues _ _ =
                     (* The name we are looking for isn't in
                        the environment.
                        The lists should be the same length. *)
                     NONE

                fun allValues (EnvValue(name, ty, location) :: ntl, valu :: vl) =
                        (name, makeValue state (name, ty, location, valu)) :: allValues(ntl, vl)

                |   allValues (EnvException(name, ty, location) :: ntl, valu :: vl) =
                        (name, makeException state (name, ty, location, valu)) :: allValues(ntl, vl)

                |   allValues (EnvVConstr(name, ty, nullary, count, location) :: ntl, valu :: vl) =
                        (name, makeConstructor state (name, ty, nullary, count, location, valu)) :: allValues(ntl, vl)

                |   allValues (EnvTConstr _ :: ntl, vl) = allValues(ntl, vl)

                |   allValues (EnvStartFunction _ :: ntl, _ :: vl) =
                        if localOnly then [] else allValues(ntl, vl)

                |   allValues (_ :: ntl, _ :: vl) = allValues(ntl, vl)
                |   allValues _ = []

                fun lookupTypes (EnvTConstr (name, tCons) :: ntl, vl) s =
                        if name = s
                        then SOME (makeTypeConstr state tCons)
                        else lookupTypes(ntl, vl) s

                |   lookupTypes (EnvStartFunction _ :: ntl, _ :: vl) s =
                        if localOnly then NONE else lookupTypes(ntl, vl) s

                |   lookupTypes (_ :: ntl, _ :: vl) s = lookupTypes(ntl, vl) s
                |   lookupTypes _ _ = NONE

                fun allTypes (EnvTConstr(name, tCons) :: ntl, vl) =
                        (name, makeTypeConstr state tCons) :: allTypes(ntl, vl)
                |   allTypes (EnvStartFunction _ :: ntl, _ :: vl) =
                        if localOnly then [] else allTypes(ntl, vl)
                |   allTypes (_ :: ntl, _ :: vl) = allTypes(ntl, vl)
                |   allTypes _ = []

                fun lookupStructs (EnvStructure (name, rSig, locations) :: ntl, valu :: vl) s =
                        if name = s
                        then SOME(makeStructure state (name, rSig, locations, valu))
                        else lookupStructs(ntl, vl) s

                |   lookupStructs (EnvTConstr _ :: ntl, vl) s = lookupStructs(ntl, vl) s

                |   lookupStructs (EnvStartFunction _ :: ntl, _ :: vl) s =
                        if localOnly then NONE else lookupStructs(ntl, vl) s
                |   lookupStructs (_ :: ntl, _ :: vl) s = lookupStructs(ntl, vl) s
                |   lookupStructs _ _ = NONE

                fun allStructs (EnvStructure (name, rSig, locations) :: ntl, valu :: vl) =
                        (name, makeStructure state (name, rSig, locations, valu)) :: allStructs(ntl, vl)

                |   allStructs (EnvTypeid _ :: ntl, _ :: vl) = allStructs(ntl, vl)
                |   allStructs (EnvStartFunction _ :: ntl, _ :: vl) =
                        if localOnly then [] else allStructs(ntl, vl)
                |   allStructs (_ :: ntl, vl) = allStructs(ntl, vl)
                |   allStructs _ = []

                (* We have a full environment here for future expansion but at
                   the moment only some of the entries are used. *)
                fun noLook _ = NONE
                and noEnter _ = raise Fail "Cannot update this name space"
                and allEmpty _ = []
           in
               {
                    lookupVal = lookupValues debugEnviron,
                    lookupType = lookupTypes debugEnviron,
                    lookupFix = noLook,
                    lookupStruct = lookupStructs debugEnviron,
                    lookupSig = noLook, lookupFunct = noLook, enterVal = noEnter,
                    enterType = noEnter, enterFix = noEnter, enterStruct = noEnter,
                    enterSig = noEnter, enterFunct = noEnter,
                    allVal = fn () => allValues debugEnviron,
                    allType = fn () => allTypes debugEnviron,
                    allFix = allEmpty,
                    allStruct = fn () => allStructs debugEnviron,
                    allSig = allEmpty,
                    allFunct = allEmpty }
            end

            val debugNameSpace = nameSpace false and debugLocalNameSpace = nameSpace true
        end

        local
            open DebuggerInterface

            fun debugLocation(d: debugState): string * PolyML.location =
                (debugFunction d, DebuggerInterface.debugLocation d)

            fun getStack() = debugState(Thread.Thread.self())
            (* These are only relevant when we are stopped at the debugger but
               we need to use globals here so that the debug functions such
               as "variables" and "continue" will work. *)
            val inDebugger = ref false
            (* Current stack and debug level. *)
            val currentStack = ref []
            fun getCurrentStack() =
                if !inDebugger then !currentStack else raise Fail "Not stopped in debugger"
            val debugLevel = ref 0
            (* Set to true to exit the debug loop.  Set by commands such as "continue". *)
            val exitLoop = ref false
            (* Exception packet sent if this was continueWithEx. *)
            val debugExPacket: exn option ref = ref NONE

            (* Call tracing. *)
            val tracing = ref false
            val breakNext = ref false
            (* Single stepping. *)
            val stepDebug = ref false
            val stepDepth = ref ~1 (* Only break at a stack size less than this. *)
            (* Break points.  We have three breakpoint lists: a list of file-line
               pairs, a list of function names and a list of exceptions. *)
            val lineBreakPoints = ref []
            and fnBreakPoints = ref []
            and exBreakPoints = ref []

            fun checkLineBreak (file, line) =
                let
                    fun findBreak [] = false
                     |  findBreak ((f, l) :: rest) =
                          (l = line andalso f = file) orelse findBreak rest
                in
                    findBreak (! lineBreakPoints)
                end

            fun checkFnBreak exact name =
            let
                (* When matching a function name we allow match if the name
                   we're looking for matches the last component of the name
                   we have.  e.g. if we set a break for "f" we match F().S.f . *)
                fun matchName n =
                    if name = n then true
                    else if exact then false
                    else
                    let
                        val nameLen = size name
                        and nLen = size n
                        fun isSeparator #"-" = true
                         |  isSeparator #")" = true
                         |  isSeparator #"." = true
                         |  isSeparator _    = false
                    in
                        nameLen > nLen andalso String.substring(name, nameLen - nLen, nLen) = n
                        andalso isSeparator(String.sub(name, nameLen - nLen - 1))
                    end
            in
                List.exists matchName (! fnBreakPoints)
            end

            (* Get the exception id from an exception packet.  The id is
               the first word in the packet.  It's a mutable so treat it
               as an int ref here.  The packet, though, is immutable. *)
            fun getExnId(ex: exn): int ref = RunCall.loadWordFromImmutable (ex, 0w0)

            fun checkExnBreak(ex: exn) =
                let val exnId = getExnId ex in List.exists (fn n => n = exnId) (! exBreakPoints) end

            fun getArgResult stack get =
                case stack of
                    hd :: _ => Values.print(get hd, FixedInt.fromInt(!printDepth))
                |   _ => PrettyString "?"

            fun printTrace (funName, location, stack, argsAndResult) =
            let
                (* This prints a block with the argument and, if we're exiting the result.
                   The function name is decorated with the location.
                   TODO: This works fine so long as the recursion depth is not too deep
                   but once it gets too wide the pretty-printer starts breaking the lines. *)
                val block =
                    PrettyBlock(0, false, [],
                        [
                            PrettyBreak(FixedInt.fromInt(length stack), 0),
                            PrettyBlock(0, false, [],
                            [
                                PrettyBlock(0, false, [ContextLocation location], [PrettyString funName]),
                                PrettyBreak(1, 3)
                            ] @ argsAndResult)
                        ])
            in
                prettyPrintWithOptionalMarkup (TextIO.print, !lineLength) block
            end

            (* Try to print the appropriate line from the file.*)
            fun printSourceLine(prefix, fileName: string, line: FixedInt.int, funName: string, justLocation) =
            let
                open TextIO
                open PolyML
                (* Use the pretty printer here because that allows us to provide a link to the
                   function in the markup so the IDE can go straight to it. *)
                val prettyOut = prettyPrintWithOptionalMarkup (printOut, !lineLength)
                val lineInfo =
                    concat(
                        [prefix] @
                        (if fileName = "" then [] else [fileName, " "]) @
                        (if line = 0 then [] else [" line:", FixedInt.toString line, " "]) @
                        ["function:", funName])
            in
                (* First just print where we are. *)
                prettyOut(
                    PrettyBlock(0, true,
                        [ContextLocation{file=fileName,startLine=line, endLine=line,startPosition=0,endPosition=0}],
                        [PrettyString lineInfo]));
                (* Try to print it.  This may fail if the file name was not a full path
                   name and we're not in the correct directory. *)
                if justLocation orelse fileName = "" then ()
                else
                let
                    val fd = openIn fileName
                    fun pLine n =
                        case inputLine fd of
                            NONE => ()
                        |   SOME s => if n = 1 then printOut s else pLine(n-1)
                in
                    pLine line;
                    closeIn fd
                end handle IO.Io _ => () (* If it failed simply ignore the error. *)
            end

            (* These functions are installed as global callbacks if necessary. *)
            fun onEntry (funName, location as {file, startLine, ...}: PolyML.location) =
            (
                if ! tracing
                then
                let
                    val stack = getStack()
                    val arg = getArgResult stack debugFunctionArg
                in
                    printTrace(funName, location, stack, [arg])
                end
                else ();
                (* We don't actually break here because at this stage we don't
                   have any variables declared. *)
                (* TODO: If for whatever reason we fail to find the breakpoint we need to cancel
                   the pending break in the exit code.  Otherwise we could try and break
                   in some other code. *)
                if checkLineBreak (file, startLine) orelse checkFnBreak false funName
                then (breakNext := true; setOnBreakPoint(SOME onBreakPoint))
                else ()
            )
            
            and onExit (funName, location) =
            (
                if ! tracing
                then
                let
                    val stack = getStack()
                    val arg = getArgResult stack debugFunctionArg
                    val res = getArgResult stack debugFunctionResult
                in
                    printTrace(funName, location, stack,
                        [arg, PrettyBreak(1, 3), PrettyString "=", PrettyBreak(1, 3), res])
                end
                else ()
            )

            and onExitException(funName, location) exn =
            (
                if ! tracing
                then
                let
                    val stack = getStack()
                    val arg = getArgResult stack debugFunctionArg
                in
                    printTrace(funName, location, stack,
                        [arg, PrettyBreak(1, 3), PrettyString "=", PrettyBreak(1, 3),
                         PrettyString "raised", PrettyBreak(1, 3), PrettyString(exnName exn)])
                end
                else ();
                if checkExnBreak exn
                then enterDebugger ()
                else ()
            )
            
            and onBreakPoint({file, startLine, ...}: PolyML.location, _) =
            (
                if (!stepDebug andalso (!stepDepth < 0 orelse List.length(getStack()) <= !stepDepth)) orelse
                   checkLineBreak (file, startLine) orelse ! breakNext
                then enterDebugger ()
                else () 
            )
            
            (* Set the callbacks when beginning to run some code. *)
            and setCallBacks () =
            (
                setOnEntry(if !tracing orelse not(null(! fnBreakPoints)) then SOME onEntry else NONE);
                setOnExit(if !tracing then SOME onExit else NONE);
                setOnExitException(if !tracing orelse not(null(! exBreakPoints)) then SOME onExitException else NONE);
                setOnBreakPoint(if !tracing orelse ! stepDebug orelse not(null(! lineBreakPoints)) then SOME onBreakPoint else NONE)
            )
            
            (* Clear all callbacks when exiting debuggable code. *)
            and clearCallBacks () =
            (
                setOnEntry NONE;
                setOnExit NONE;
                setOnExitException NONE;
                setOnBreakPoint NONE;
                (* Clear all stepping. *)
                breakNext := false;
                stepDebug := false;
                stepDepth := ~1;
                (* Clear the debugger state *)
                debugLevel := 0;
                currentStack := []
            )

            and enterDebugger () =
            let
                (* Clear the onXXX functions to prevent any recursion. *)
                val () = clearCallBacks ()
                val () = inDebugger := true
                (* Remove any type-ahead. *)
                fun flushInput () =
                    case TextIO.canInput(TextIO.stdIn, 1) of
                        SOME 1 => (TextIO.inputN(TextIO.stdIn, 1); flushInput())
                    |   _ => ()
                val () = flushInput ()

                val () = exitLoop := false
                (* Save the stack on entry.  If we execute any code with
                   debugging enabled while we're in the debugger we could
                   change this. *)
                val () = currentStack := getStack()

                val () =
                    case !currentStack of
                        hd :: _ =>
                            let
                                val (funName, {file, startLine, ...}) = debugLocation hd
                            in
                                printSourceLine("", file, startLine, funName, false)
                            end
                    |   [] => () (* Shouldn't happen. *)

                val compositeNameSpace =
                (* Compose any debugEnv with the global environment.  Create a new temporary environment
                   to contain any bindings made within the shell.  They are discarded when we continue
                   from the break-point.  Previously, bindings were made in the global environment but
                   that is problematic.  It is possible to capture local types in the bindings which
                   could actually be different at the next breakpoint. *)
                let
                    val fixTab = ProtectedTable.create() and sigTab = ProtectedTable.create()
                    and valTab = ProtectedTable.create() and typTab = ProtectedTable.create()
                    and fncTab = ProtectedTable.create() and strTab = ProtectedTable.create()
                    (* The debugging environment depends on the currently selected stack frame. *)
                    fun debugEnv() = debugNameSpace (List.nth(!currentStack, !debugLevel))
                    fun dolookup f t s =
                        case ProtectedTable.lookup t s of NONE => (case f (debugEnv()) s of NONE => f globalNameSpace s | v => v) | v => v
                    fun getAll f t () = ProtectedTable.all t () @ f (debugEnv()) () @ f globalNameSpace ()
                in
                    {
                    lookupFix    = dolookup #lookupFix fixTab,
                    lookupSig    = dolookup #lookupSig sigTab,
                    lookupVal    = dolookup #lookupVal valTab,
                    lookupType   = dolookup #lookupType typTab,
                    lookupFunct  = dolookup #lookupFunct fncTab,
                    lookupStruct = dolookup #lookupStruct strTab,
                    enterFix     = ProtectedTable.enter fixTab,
                    enterSig     = ProtectedTable.enter sigTab,
                    enterVal     = ProtectedTable.enter valTab,
                    enterType    = ProtectedTable.enter typTab,
                    enterFunct   = ProtectedTable.enter fncTab,
                    enterStruct  = ProtectedTable.enter strTab,
                    allFix       = getAll #allFix fixTab,
                    allSig       = getAll #allSig sigTab,
                    allVal       = getAll #allVal valTab,
                    allType      = getAll #allType typTab,
                    allFunct     = getAll #allFunct fncTab,
                    allStruct    = getAll #allStruct strTab
                    }
                end
            in
                topLevel
                    { isDebug = true, nameSpace = compositeNameSpace, exitLoop = fn _ => ! exitLoop,
                      exitOnError = false, isInteractive = true,
                      (* Don't enable debugging for anything run within the debug level. *)
                      startExec = fn () => (), endExec = fn () => () }
                      (* If we type control-C to the debugger we exit it and
                         raise Interrupt within the debuggee without re-enabling
                         any breakpoints. *)
                    handle exn => (inDebugger := false; raise exn);

                inDebugger := false;
                setCallBacks(); (* Re-enable debugging. *)

                (* If this was continueWithEx raise the exception. *)
                case ! debugExPacket of
                    NONE => ()
                |   SOME exn => (debugExPacket := NONE; raise exn)
            end
        in
            (* Normal, non-debugging top-level loop. *)
            fun shell () =
            let
                val argList = CommandLine.arguments()
                fun switchOption option = List.exists(fn s => s = option) argList
                (* Generate mark-up in IDE code when printing if the option has been given
                   on the command line. *)
                val () = useMarkupInOutput := switchOption "--with-markup"
                val exitOnError = switchOption"--error-exit"
                val interactive =
                    switchOption "-i" orelse
                    let
                        open TextIO OS
                        open StreamIO TextPrimIO IO
                        val s = getInstream stdIn
                        val (r, v) = getReader s
                        val RD { ioDesc, ...} = r
                    in
                        setInstream(stdIn, mkInstream(r,v));
                        case ioDesc of
                            SOME io => (kind io = Kind.tty handle SysErr _ => false)
                        |   _  => false
                    end
            in
                topLevel
                    { isDebug = false, nameSpace = globalNameSpace, exitLoop = fn _ => false,
                      isInteractive = interactive, exitOnError = exitOnError,
                      startExec = setCallBacks, endExec = clearCallBacks }
            end

            structure Debug =
            struct
                (* Functions that are only relevant when called from the debugger.  These
                   check the debugging state using getCurrentStack which raises an
                   exception if we're not in the debugger. *)
                (* "step" causes the debugger to be entered on the next call.
                   "stepOver" enters the debugger on the next call when the stack is no larger
                   than it is at present.
                   "stepOut" enters the debugger on the next call when the stack is smaller
                   than it is at present. *)
                fun step () = 
                let
                    val _ = getCurrentStack()
                in
                    stepDebug := true; stepDepth := ~1; exitLoop := true
                end

                and stepOver() =
                let
                    val stack = getCurrentStack()
                in
                    stepDebug := true; stepDepth := List.length stack; exitLoop := true
                end

                and stepOut() =
                let
                    val stack = getCurrentStack()
                in
                    stepDebug := true; stepDepth := List.length stack - 1; exitLoop := true
                end

                and continue () =
                let
                    val _ = getCurrentStack()
                in
                    stepDebug := false; stepDepth := ~1; exitLoop := true
                end

                and continueWithEx exn =
                let
                    val _ = getCurrentStack()
                in
                    stepDebug := false; stepDepth := ~1; exitLoop := true; debugExPacket := SOME exn
                end

                (* Stack traversal. *)
                fun up () =
                let
                    val stack = getCurrentStack()
                in
                    if !debugLevel < List.length stack -1
                    then
                    let
                        val _ = debugLevel := !debugLevel + 1;
                        val (funName, {startLine, file, ...}) =
                            debugLocation(List.nth(stack, !debugLevel))
                    in
                        printSourceLine("", file, startLine, funName, false)
                    end
                    else TextIO.print "Top of stack.\n"
                end
        
                and down () =
                let
                    val stack = getCurrentStack()
                in
                    if !debugLevel = 0
                    then TextIO.print "Bottom of stack.\n"
                    else
                    let
                        val () = debugLevel := !debugLevel - 1;
                        val (funName, {startLine, file, ...}) =
                            debugLocation(List.nth(stack, !debugLevel))
                    in
                        printSourceLine("", file, startLine, funName, false)
                    end
                end

                (* Just print the functions without any other context. *)
                fun stack () : unit =
                let
                    fun printTrace(d, n) =
                    let
                        val (funName, {file, startLine, ...}) = debugLocation d
                        (* If this is the current level prefix it with > *)
                        val prefix = if n = !debugLevel then "> " else "  "
                    in
                        printSourceLine(prefix, file, startLine, funName, true);
                        n+1
                    end
                in
                    ignore (List.foldl printTrace 0 (getCurrentStack()))
                end

                local
                    fun printVal v =
                        prettyPrintWithOptionalMarkup(TextIO.print, !lineLength)
                            (NameSpace.Values.printWithType(v, FixedInt.fromInt(!printDepth), SOME globalNameSpace))
                    fun printStack (stack: debugState) =
                        List.app (fn (_,v) => printVal v) (#allVal (debugNameSpace stack) ())
                in
                    (* Print all variables at the current level. *)
                    fun variables() =
                        printStack (List.nth(getCurrentStack(), !debugLevel))
                    (* Print all the levels. *)
                    and dump() =
                    let
                        fun printLevel stack =
                        let
                            val (funName, _) = debugLocation stack
                        in
                            TextIO.print(concat["Function ", funName, ":"]);
                            printStack stack;
                            TextIO.print "\n"
                        end
                    in
                        List.app printLevel (getCurrentStack())
                    end
                    (* Print local variables at the current level. *)
                    and locals() =
                    let
                        val stack = List.nth(getCurrentStack(), !debugLevel)
                    in
                        List.app (fn (_,v) => printVal v) (#allVal (debugLocalNameSpace stack) ())
                    end
                end

                (* Functions to adjust tracing and breakpointing.  May be called
                   either within or outside the debugger. *)
                fun trace b = tracing := b

                fun breakAt (file, line) =
                    if checkLineBreak(file, line) then () (* Already there. *)
                    else lineBreakPoints := (file, line) :: ! lineBreakPoints
        
                fun clearAt (file, line) =
                let
                    fun findBreak [] = (TextIO.print "No such breakpoint.\n"; [])
                     |  findBreak ((f, l) :: rest) =
                          if l = line andalso f = file
                          then rest else (f, l) :: findBreak rest
                in
                    lineBreakPoints := findBreak (! lineBreakPoints)
                end
         
                fun breakIn name =
                    if checkFnBreak true name then () (* Already there. *)
                    else fnBreakPoints := name :: ! fnBreakPoints
        
                fun clearIn name =
                let
                    fun findBreak [] = (TextIO.print "No such breakpoint.\n"; [])
                     |  findBreak (n :: rest) =
                          if name = n then rest else n :: findBreak rest
                in
                    fnBreakPoints := findBreak (! fnBreakPoints)
                end

                fun breakEx exn =
                    if checkExnBreak exn then  () (* Already there. *)
                    else exBreakPoints := getExnId exn :: ! exBreakPoints

                fun clearEx exn =
                let
                    val exnId = getExnId exn
                    fun findBreak [] = (TextIO.print "No such breakpoint.\n"; [])
                     |  findBreak (n :: rest) =
                          if exnId = n then rest else n :: findBreak rest
                in
                    exBreakPoints := findBreak (! exBreakPoints)
                end

            end
        end
        
        structure CodeTree =
        struct
            open PolyML.CodeTree
            (* Add options to the code-generation phase. *)
            val genCode =
                fn (code, numLocals) =>
                let
                    open Bootstrap Bootstrap.Universal
                    val compilerOut = prettyPrintWithOptionalMarkup(TextIO.print, !lineLength)
                in
                    genCode(code,
                        [
                            tagInject compilerOutputTag compilerOut,
                            tagInject maxInlineSizeTag (FixedInt.fromInt(! maxInlineSize)),
                            tagInject codetreeTag (! codetree),
                            tagInject icodeTag (! icode),
                            tagInject lowlevelOptimiseTag (! lowlevelOptimise),
                            tagInject assemblyCodeTag (! assemblyCode),
                            tagInject codetreeAfterOptTag (! codetreeAfterOpt)
                        ], numLocals)
                end
        end

        (* Original print_depth etc functions. *)
        fun timing      b = Compiler.timing := b
        and print_depth i = Compiler.printDepth := i
        and error_depth i = Compiler.errorDepth := i
        and line_length i = Compiler.lineLength := i

        (* Legacy exception_trace. *)
        structure Exception =
        struct
            open Exception
            fun exception_trace f = f() (* Backwards compatibility *)
        end
        
        (* Include it in the PolyML structure for backwards compatibility. *)
        val exception_trace = Exception.exception_trace

        local
            val systemProfile : int -> (int * string) list =
                RunCall.rtsCallFull1 "PolyProfiling"

            fun printProfile profRes =
            let
                (* Sort in ascending order. *)
                val sorted = quickSort (fn (a, _) => fn (b, _) => a <= b) profRes

                fun doPrint (count, name) =
                let
                    val cPrint = Int.toString count
                    val prefix =
                        CharVector.tabulate(Int.max(0, 10-size cPrint), fn _ => #" ")
                in
                    TextIO.output(TextIO.stdOut, concat[prefix, cPrint, " ", name, "\n"])
                end

                val total = List.foldl (fn ((c,_),s) => c+s) 0 profRes
            in
                List.app doPrint sorted;
                if total = 0 then ()
                else TextIO.print(concat["Total ", Int.toString total, "\n"])
            end
        in

            structure Profiling =
            struct
                datatype profileMode =
                    ProfileTime             (* old mode 1 *)
                |   ProfileAllocations      (* old mode 2 *)
                |   ProfileLongIntEmulation (* old mode 3  - No longer used*)
                |   ProfileTimeThisThread   (* old mode 6 *)
                |   ProfileMutexContention
            
                fun profileStream (stream: (int * string) list -> unit) mode f arg =
                let
                    (* Control profiling.  This may raise Fail if profiling is turned on when it
                       is already on or if there is insufficient memory. *)
                    val code =
                        case mode of
                            ProfileTime =>              1
                        |   ProfileAllocations =>       2
                        |   ProfileLongIntEmulation =>  3
                        |   ProfileTimeThisThread =>    6
                        |   ProfileMutexContention =>   7
                    val _ = systemProfile code (* Discard the result *)
                    val result =
                        f arg handle exn => (stream(systemProfile 0); PolyML.Exception.reraise exn)
                in
                    stream(systemProfile 0);
                    result
                end
            
                fun profile mode f arg = profileStream printProfile mode f arg

                (* Live data profiles show the current state.  We need to run the
                   GC to produce the counts. *)
                datatype profileDataMode =
                    ProfileLiveData
                |   ProfileLiveMutableData

                fun profileDataStream(stream: (int * string) list -> unit) mode =
                let
                    val code =
                        case mode of
                            ProfileLiveData => 4
                        |   ProfileLiveMutableData => 5
                    val _ = systemProfile code (* Discard the result *)
                    val () = PolyML.fullGC()
                in
                    stream(systemProfile 0)
                end
                
                val profileData = profileDataStream printProfile
            end
        end

        (* Saving and loading state. *)
        local
            val polySpecificGeneralCall = RunCall.rtsCallFull2 "PolySpecificGeneral"
            fun polySpecificGeneral(code: int, arg:'a):'b = RunCall.unsafeCast(polySpecificGeneralCall(RunCall.unsafeCast(code, arg)))
        in
        structure SaveState =
        struct
            local
                val getOS: int = LibrarySupport.getOSType()
                fun loadMod (args: string): Universal.universal list = polySpecificGeneral (32, args)
                and systemDir(): string = polySpecificGeneral (34, ())
            in
                fun loadModuleBasic (fileName: string): Universal.universal list =
                (* If there is a path separator use the name and don't search further. *)
                if OS.Path.dir fileName <> ""
                then loadMod fileName
                else
                let
                    (* Path elements are separated by semicolons in Windows but colons in Unix. *)
                    val sepInPathList = if getOS = 1 then #";" else #":"
                    val pathList =
                        case OS.Process.getEnv "POLYMODPATH" of
                            NONE => []
                        |   SOME s => String.fields (fn ch => ch = sepInPathList) s

                    fun findFile [] = NONE
                    |   findFile (hd::tl) =
                        (* Try actually loading the file.  That way we really check we have a module. *)
                        SOME(loadMod (OS.Path.joinDirFile{dir=hd, file=fileName}))
                            handle Fail _ => findFile tl | OS.SysErr _ => findFile tl      
                in
                    case findFile pathList of
                        SOME l => l (* Found *)
                    |   NONE => 
                        let
                            val sysDir = systemDir()
                            val inSysDir =
                                if sysDir = "" then NONE else findFile[sysDir]
                        in
                            case inSysDir of
                                SOME l => l
                            |   NONE => raise Fail("Unable to find module ``" ^ fileName ^ "''")
                        end
                end
            end

            fun saveChild(f: string, depth: int): unit = polySpecificGeneral (20, (f, depth))
            fun saveState f = saveChild (f, 0);
            fun showHierarchy(): string list = polySpecificGeneral (22, ())
            fun renameParent{ child: string, newParent: string }: unit = polySpecificGeneral (23, (child, newParent))
            fun showParent(child: string): string option = polySpecificGeneral (24, child)

            fun loadState (f: string): unit = polySpecificGeneral (21, f)
            and loadHierarchy (s: string list): unit =
                (* Load hierarchy takes a list of file names in order with the parents
                   before the children.  It's easier for the RTS if this is reversed. *)
                polySpecificGeneral (33, List.rev s)
            
            (* Module loading and storing. *)
            structure Tags =
            struct
                val structureTag: (string * PolyML.NameSpace.Structures.structureVal) Universal.tag = Universal.tag()
                val functorTag: (string * PolyML.NameSpace.Functors.functorVal) Universal.tag = Universal.tag()
                val signatureTag: (string * PolyML.NameSpace.Signatures.signatureVal) Universal.tag = Universal.tag()
                val valueTag: (string * PolyML.NameSpace.Values.value) Universal.tag = Universal.tag()
                val typeTag: (string * PolyML.NameSpace.TypeConstrs.typeConstr) Universal.tag = Universal.tag()
                val fixityTag: (string * PolyML.NameSpace.Infixes.fixity) Universal.tag = Universal.tag()
                val startupTag: (unit -> unit) Universal.tag = Universal.tag()
            end
            
            val saveModuleBasic: string * Universal.universal list -> unit =
                fn (_, nil) => raise Fail "Cannot create an empty module"
                |  args => polySpecificGeneral (31, args)

            fun saveModule(s, {structs, functors, sigs, onStartup}) =
            let
                fun dolookup (look, tag, kind) s =
                    case look globalNameSpace s of
                        SOME v => Universal.tagInject tag (s, v)
                    |   NONE => raise Fail (concat[kind, " ", s, " has not been declared"])
                val structVals = map (dolookup(#lookupStruct, Tags.structureTag, "Structure")) structs
                val functorVals = map (dolookup(#lookupFunct, Tags.functorTag, "Functor")) functors
                val sigVals = map (dolookup(#lookupSig, Tags.signatureTag, "Signature")) sigs
                val startVal =
                    case onStartup of
                        SOME f => [Universal.tagInject Tags.startupTag f]
                    |   NONE => []
            in
                saveModuleBasic(s, structVals @ functorVals @ sigVals @ startVal)
            end
            
            fun loadModule s =
            let
                val ulist = loadModuleBasic s
                (* Find and run the start-up function.  If it raises an exception we
                   don't go further. *)
                val startFn = List.find (Universal.tagIs Tags.startupTag) ulist
                val () =
                    case startFn of SOME f => (Universal.tagProject Tags.startupTag f) () | NONE => ()
                fun extract (tag:'a Universal.tag): Universal.universal list -> 'a list =
                    List.mapPartial(
                        fn s => if Universal.tagIs tag s then SOME(Universal.tagProject tag s) else NONE)
            in
                (* Add the entries and print them in the same way as top-level bindings. *)
                printAndEnter(! printInAlphabeticalOrder, globalNameSpace, TextIO.print, !printDepth)
                {
                    fixes = extract Tags.fixityTag ulist,
                    values = extract Tags.valueTag ulist,
                    structures = extract Tags.structureTag ulist,
                    signatures = extract Tags.signatureTag ulist,
                    functors = extract Tags.functorTag ulist,
                    types = extract Tags.typeTag ulist
                }
            end
        end
        end
        
        val loadModule = SaveState.loadModule

    end
end (* PolyML. *);