1(*
2    Title:      Root function for the PolyML structure
3    Author:     David Matthews
4    Copyright   David Matthews 2009, 2015-17
5
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License version 2.1 as published by the Free Software Foundation.
9    
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14    
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18*)
19
20(* This contains the code for the IDE protocol as well as the normal
21   Poly/ML top-level loop. *)
22
23local
24    val parseTree = ref ("", []) (* Parsetree ID and parsetrees as a list. *)
25
26    fun runIDEProtocol () =
27    let
28        (* Save the last parsetree here. *)
29        val lastParsetree =
30            ref (case parseTree of ref(_, hd::_) => SOME hd | _ => NONE)
31
32        val parseLock = Thread.Mutex.mutex()
33
34        (* Access the parse tree and other information with the lock held. *)
35        fun withLock f =
36        let
37            open Thread.Thread Thread.Mutex
38            val originalState = getAttributes()
39            val () = setAttributes[InterruptState InterruptDefer]
40            val () = lock parseLock
41            val result = f ()
42            val () = unlock parseLock
43            val () = setAttributes originalState
44        in
45            result
46        end
47
48        type basicLoc = (* Locations in request packets. *) { startOffset: FixedInt.int, endOffset: FixedInt.int }
49        type compileError = { hardError: bool, location: PolyML.location, message: PolyML.pretty }
50
51        datatype request =
52            (* Requests sent by the IDE to Poly/ML. *)
53            PropertyRequest (* O *)
54                of { requestId: string, parseTreeId: string, location: basicLoc }
55        |   MoveRequest (* M *)
56                of { requestId: string, parseTreeId: string, location: basicLoc, direction: direction }
57        |   TypeRequest (* T *)
58                of { requestId: string, parseTreeId: string, location: basicLoc }
59        |   DecRequest (* I *)
60                of { requestId: string, parseTreeId: string, location: basicLoc, decType: dectype }
61        |   RefRequest (* V *)
62                of { requestId: string, parseTreeId: string, location: basicLoc }
63        |   CompileRequest (* R *)
64                of { requestId: string, fileName: string, startPosition: FixedInt.int,
65                     preludeCode: string, sourceCode: string }
66        |   KillRequest (* K *)
67                of { requestId: string }
68        |   UnknownRequest (* Provided for upwards compatibility. *)
69                of { request: int, requestId: string}
70
71        and direction = DirUp | DirLeft | DirRight | DirDown
72        
73        and dectype = DecLocal | DecOpen | DecParent
74
75        and response =
76            (* Replies sent from Poly/ML to the IDE. *)
77            PropertyResponse (* O *)
78                of { requestId: string, parseTreeId: string, location: basicLoc, commands: string list }
79        |   MoveResponse  (* M *)
80                of { requestId: string, parseTreeId: string, location: basicLoc }
81        |   TypeResponse (* T *)
82                of { requestId: string, parseTreeId: string, location: basicLoc, typeRes: PolyML.pretty option }
83        |   DecResponse (* I *)
84                of { requestId: string, parseTreeId: string, location: basicLoc,
85                     decLocation: PolyML.location option }
86        |   RefResponse (* V *)
87                of { requestId: string, parseTreeId: string, location: basicLoc, references: basicLoc list }
88        |   CompilerResponse (* R *)
89                of { requestId: string, parseTreeId: string, finalOffset: FixedInt.int, result: compileResult }
90        |   UnknownResponse (* Provided for upwards compatibility. *)
91                of { request: int, requestId: string }
92
93        and compileResult =
94            Succeeded of compileError list
95        |   RuntimeException of PolyML.pretty * compileError list
96        |   PreludeFail of string
97        |   CompileFail of compileError list
98        |   CompileCancelled of compileError list
99
100
101        val outputLock = Thread.Mutex.mutex()
102        
103        val (readRequest, sendStartedMessage, sendResponse) =
104            case OS.Process.getEnv "POLYIDESOCKET" of
105                NONE => (* Version 1 protocol - backwards compatibility - use stdIn/stdOut *)
106                let
107                    (* Separate out the output stream.  We need to interlock access to stdOut
108                       to avoid user code outputing within a packet. *)
109                    open TextIO TextIO.StreamIO
110                    val outStream = getOutstream stdOut
111                    val (writer, buffMode) = getWriter outStream
112                    val TextPrimIO.WR
113                        { name, chunkSize, writeVec, writeArr, block, canOutput, ioDesc, ... } = writer
114                    (* Create a version of the stream that locks before actually sending output. *)
115                    val lockedWriteVec =
116                        case writeVec of
117                            NONE => NONE
118                        |   SOME writeVec =>
119                                SOME(fn a => ThreadLib.protect outputLock writeVec a)
120                    val lockedWriteArray =
121                        case writeArr of
122                            NONE => NONE
123                        |   SOME writeArr =>
124                                SOME(fn a => ThreadLib.protect outputLock writeArr a)
125                    val lockedWriter =
126                        TextPrimIO.WR { name = name, chunkSize = chunkSize,
127                                        writeVec = lockedWriteVec, writeArr = lockedWriteArray,
128                                        writeVecNB = NONE, writeArrNB = NONE, block = block, canOutput = canOutput,
129                                        getPos = NONE, setPos = NONE, endPos = NONE, verifyPos = NONE,
130                                        close = fn () => raise Fail "stdOut must not be closed", ioDesc = ioDesc }
131                    (* Use this locked version for normal stdOut. *)
132                    val () = setOutstream(stdOut,
133                                StreamIO.mkOutstream(TextPrimIO.augmentWriter lockedWriter, buffMode))
134                    (* Create an unlocked version for use within the IDE code.  When writing to this
135                       stream the IDE code will first get a lock, then output the whole packet before
136                       releasing the lock.  Because mutexes are not recursive we can't use the locking
137                       version. *)
138                    val unLockedWriter =
139                        TextPrimIO.WR { name = name, chunkSize = chunkSize, writeVec = writeVec, writeArr = writeArr,
140                                        writeVecNB = NONE, writeArrNB = NONE, block = block, canOutput = canOutput,
141                                        getPos = NONE, setPos = NONE, endPos = NONE, verifyPos = NONE,
142                                        close = fn () => raise Fail "stdOut must not be closed", ioDesc = ioDesc }
143
144                    val inStream = stdIn
145
146                    val outStream = StreamIO.mkOutstream(TextPrimIO.augmentWriter unLockedWriter, buffMode)
147
148                    fun protocolError error =
149                    let
150                        open OS.Process
151                    in
152                        TextIO.print ("Protocol error: " ^ error) handle _ => ();
153                        exit failure;
154                        raise Fail "bad" (* Never called but sets return type as 'a *)
155                    end
156
157                    (* Reads a request.  Calls OS.Process.exit at end-of-file or on a protocol error. *)
158                    fun readRequest (): request =
159                    let
160                        open TextIO
161
162                        (* Returns the string as far as the next ESC and the terminator. *)
163                        fun readToEscape (soFar: string, terminator) : string =
164                        case input1 inStream of
165                            SOME #"\u001b" =>
166                            (
167                                case input1 inStream of
168                                    NONE => protocolError "End of file"
169                                |   SOME ch =>
170                                        if ch = terminator
171                                        then soFar
172                                        else if ch = #"\u001b" (* Escaped ESC. *)
173                                        then readToEscape(soFar ^ str #"\u001b", terminator)
174                                        else protocolError(str ch ^ " not " ^ str terminator)
175                            )
176                        |   SOME ch => readToEscape(soFar ^ str ch, terminator)
177                        |   NONE => protocolError "End of file"
178
179                        (* Parse an integer.  Returns zero if it isn't a valid int. *)
180                        fun getInt termCh : FixedInt.int =
181                            case FixedInt.fromString (readToEscape("", termCh)) of
182                                NONE => 0
183                            |   SOME i => i
184
185                        val () =
186                            case input1 inStream of
187                                NONE => OS.Process.exit OS.Process.success (* Close down. *)
188                            |   SOME #"\u001b" => () (* Escape- start of packet. *)
189                            |   SOME ch => protocolError(str ch ^ " not ESCAPE at start of packet")
190                        val startCh = (* Request code *)
191                            case input1 inStream of
192                                NONE => protocolError "End of file"
193                            |   SOME ch => ch
194                    in
195                        case startCh of
196                            #"R" =>
197                            let (* Compile request. *)
198                                (* Begin a new compilation. *)
199                                val requestId = readToEscape("", #",")
200                                val fileName = readToEscape("", #",")
201                                val startPosition = getInt #","
202                                (* The next two are the lengths *)
203                                val preludeLength = getInt #","
204                                val sourceLength = getInt #","
205                                (* *)
206                                val preludeCode = TextIO.inputN(inStream, FixedInt.toInt preludeLength)
207                                val _ = readToEscape("", #",") (* Should be empty - check? *)
208                                val sourceText = TextIO.inputN(inStream, FixedInt.toInt sourceLength)
209                                val _ = readToEscape("", #"r") (* Should be empty - check? *)
210                            in
211                                CompileRequest { requestId = requestId, fileName = fileName, startPosition = startPosition,
212                                         preludeCode = preludeCode, sourceCode = sourceText }
213                            end
214
215                            (* Navigation functions. *)
216            
217                        |   #"M" =>
218                            let
219                                val requestId = readToEscape("", #",")
220                                val parseTreeId = readToEscape("", #",")
221                                val startOffset = getInt #","
222                                val endOffset = getInt #","
223                                val requestType =
224                                    case readToEscape("", #"m") of
225                                        "N" => DirRight
226                                    |   "P" => DirLeft
227                                    |   "U" => DirUp
228                                    |   _(*"C"*) => DirDown
229                            in
230                                MoveRequest{
231                                    requestId = requestId, parseTreeId = parseTreeId, direction= requestType,
232                                    location = { startOffset = startOffset, endOffset = endOffset }
233                                    }
234                            end
235
236                            (* Print the type of the selected node. *)
237                        |   #"T" =>
238                            let
239                                val requestId = readToEscape("", #",")
240                                val parseTreeId = readToEscape("", #",")
241                                val startOffset = getInt #","
242                                val endOffset = getInt #"t"
243                            in
244                                TypeRequest{
245                                    requestId = requestId, parseTreeId = parseTreeId,
246                                    location = { startOffset = startOffset, endOffset = endOffset }
247                                    }
248                            end
249
250                            (* Print the declaration location of the selected node. *)
251                        |   #"I" =>
252                            let
253                                val requestId = readToEscape("", #",")
254                                val parseTreeId = readToEscape("", #",")
255                                val startOffset = getInt #","
256                                val endOffset = getInt #","
257                                val decType =
258                                    case readToEscape("", #"i") of
259                                        "J" => DecOpen
260                                    |   "S" => DecParent
261                                    |   _ (*"I"*) => DecLocal
262                            in
263                                DecRequest{
264                                    requestId = requestId, parseTreeId = parseTreeId, decType = decType,
265                                    location = { startOffset = startOffset, endOffset = endOffset }
266                                    }
267                            end
268
269                            (* Return the local references to the given identifier. *)
270                        |   #"V" =>
271                            let
272                                val requestId = readToEscape("", #",")
273                                val parseTreeId = readToEscape("", #",")
274                                val startOffset = getInt #","
275                                val endOffset = getInt #"v"
276                            in
277                                RefRequest{
278                                    requestId = requestId, parseTreeId = parseTreeId,
279                                    location = { startOffset = startOffset, endOffset = endOffset }
280                                    }
281                            end
282
283                        |   #"O" => (* Print list of valid commands. *)
284                            let
285                                val requestId = readToEscape("", #",")
286                                val parseTreeId = readToEscape("", #",")
287                                val startOffset = getInt #","
288                                val endOffset = getInt #"o"
289                            in
290                                PropertyRequest{
291                                    requestId = requestId, parseTreeId = parseTreeId,
292                                    location = { startOffset = startOffset, endOffset = endOffset }
293                                    }
294                            end
295
296                        |   #"K" => (* Cancel request. *)
297                                KillRequest { requestId = readToEscape ("", #"k") }
298
299                        |   ch => (* Something else.  Reply with empty response. *)
300                            let
301                                (* Unlike the other cases we don't know what may follow ESCAPE. *)
302                                val terminator = Char.toLower ch
303                                fun skipToTerminator () =
304                                case input1 inStream of
305                                    SOME #"\u001b" =>
306                                    (
307                                        case input1 inStream of
308                                            NONE => protocolError "End of file"
309                                        |   SOME ch =>
310                                                if ch = terminator
311                                                then () (* Found the end. *)
312                                                else (* Some internal escape code. *) skipToTerminator()
313                                    )
314                                |   SOME _ => skipToTerminator ()
315                                |   NONE => protocolError "End of file"
316                            in
317                                skipToTerminator ();
318                                UnknownRequest { request = Char.ord ch, requestId = "" }
319                            end
320                    end (* readRequest *)
321
322                    fun sendStartedMessage () = 
323                    let 
324                        fun print s = TextIO.StreamIO.output(outStream, s)
325                        fun printEsc ch = print (String.concat["\u001b", String.str ch])
326                        fun sendResponse () =
327                        ( (* send the version number of the protocol *)
328                            printEsc #"H"; print "1.0.0"; printEsc #"h";
329                            TextIO.StreamIO.flushOut outStream
330                        )
331                    in
332                        ThreadLib.protect outputLock sendResponse ()
333                    end
334
335                    (* Send a reply packet. *)
336                    fun sendResponse response =
337                    let
338                        fun print s = TextIO.StreamIO.output(outStream, s)
339                        fun printEsc ch = print (String.concat["\u001b", String.str ch])
340
341                        fun printLocation {startOffset, endOffset } =
342                            print (String.concat[FixedInt.toString startOffset, "\u001b,", FixedInt.toString endOffset])
343
344                        and printFullLocation { file, startLine, startPosition, endPosition, ...} =
345                        (
346                            print file; (* TODO double any escapes. *) printEsc #",";
347                            print (FixedInt.toString startLine); printEsc #",";
348                            print (FixedInt.toString startPosition); printEsc #",";
349                            print (FixedInt.toString endPosition)
350                        )
351
352                        fun makeResponse (PropertyResponse { requestId, parseTreeId, location, commands }) =
353                            let
354                                fun printCommand comm = (printEsc #","; print comm)
355                            in
356                                printEsc #"O";
357                                print requestId; printEsc #",";
358                                print parseTreeId; printEsc #",";
359                                printLocation location;
360                                List.app printCommand commands;
361                                printEsc #"o"
362                            end
363
364                        |   makeResponse (MoveResponse { requestId, parseTreeId, location }) =
365                            (
366                                printEsc #"M";
367                                print requestId; printEsc #",";
368                                print parseTreeId; printEsc #",";
369                                printLocation location;
370                                printEsc #"m"
371                            )
372
373                        |   makeResponse (TypeResponse { requestId, parseTreeId, location, typeRes }) =
374                            let
375                                fun prettyAsString message =
376                                let
377                                    val result = ref []
378                                    fun doPrint s = result := s :: ! result
379                                    val () = PolyML.prettyPrint(doPrint, !PolyML.Compiler.lineLength) message
380                                in
381                                    String.concat(List.rev(! result))
382                                end
383                            in
384                                printEsc #"T";
385                                print requestId; printEsc #",";
386                                print parseTreeId; printEsc #",";
387                                printLocation location;
388                                case typeRes of
389                                    NONE => ()
390                                |   SOME typeRes =>
391                                    (
392                                        printEsc #",";
393                                        print(prettyAsString typeRes)
394                                    );
395                                printEsc #"t"
396                            end
397
398                        |   makeResponse (DecResponse { requestId, parseTreeId, location, decLocation }) =
399                            (
400                                printEsc #"I";
401                                print requestId; printEsc #",";
402                                print parseTreeId; printEsc #",";
403                                printLocation location;
404                                case decLocation of
405                                    SOME location => (printEsc #","; printFullLocation location)
406                                |   NONE => ();
407                                printEsc #"i"
408                            )
409
410                        |   makeResponse (RefResponse { requestId, parseTreeId, location, references }) =
411                            (
412                                printEsc #"V";
413                                print requestId; printEsc #",";
414                                print parseTreeId;  printEsc #",";
415                                printLocation location;
416                                List.app (fn loc => (printEsc #","; printLocation loc)) references;
417                                printEsc #"v"
418                            )
419
420                        |   makeResponse (CompilerResponse { requestId, parseTreeId, finalOffset, result }) =
421                            let
422                                (* Pretty print a message and return the output string. *)
423                                fun prettyMarkupAsString message =
424                                let
425                                    val result = ref []
426                                    fun doPrint s = result := s :: ! result
427                                    val () = PolyML.prettyPrintWithIDEMarkup(doPrint, !PolyML.Compiler.lineLength) message
428                                in
429                                    String.concat(List.rev(! result))
430                                end
431
432                                fun printError { hardError, location, message } =
433                                (
434                                    printEsc #"E";
435                                    if hardError then print "E" else print "W";
436                                    printEsc #",";
437                                    printFullLocation location;
438                                    printEsc #";"; (* N.B. Semicolon here, not comma. *)
439                                    print (prettyMarkupAsString message); (* May include markup *)
440                                    printEsc #"e"
441                                )
442                                fun printOffset() = (printEsc #","; print (FixedInt.toString finalOffset))
443                                fun printErrors errors = (List.app printError errors)
444                            in
445                                printEsc #"R";
446                                print requestId; printEsc #",";
447                                print parseTreeId; printEsc #",";
448                                case result of
449                                    Succeeded errors => (print "S"; printOffset(); printEsc #";"; printErrors errors)
450                                |   RuntimeException (s, errors) =>
451                                    (
452                                        print "X"; printOffset(); 
453                                        printEsc #";";
454                                        printEsc #"X"; print(prettyMarkupAsString s); (* May include markup *)
455                                        printEsc #"x"; 
456                                        printErrors errors
457                                    )
458                                |   PreludeFail s =>
459                                    ( print "L"; printOffset(); printEsc #";"; print s (* May include markup *) )
460                                |   CompileFail errors =>
461                                    ( print "F"; printOffset(); printEsc #";"; printErrors errors )
462                                |   CompileCancelled errors =>
463                                    ( print "C"; printOffset(); printEsc #";"; printErrors errors );
464                                printEsc #"r"
465                             end
466
467                        |   makeResponse (UnknownResponse { request, ... }) =
468                            let
469                                val startCh = Char.chr request
470                            in
471                                (* Response to unknown command - return empty result. *)
472                                ( printEsc startCh; printEsc (Char.toLower startCh))
473                            end
474
475                        fun sendResponse () =
476                        (
477                            makeResponse response handle _ => protocolError "Exception";
478                            TextIO.StreamIO.flushOut outStream
479                        )
480                    in
481                        (* Sending the response packet must be atomic with respect to any other
482                           output to stdOut. *)
483                        ThreadLib.protect outputLock sendResponse ()
484                    end (* sendResponse *)
485
486                in
487                    (readRequest, sendStartedMessage, sendResponse)
488                end
489
490            |   SOME portNo =>
491                (* Version 2 protocol - uses ASN1 binary over a socket.*)
492                let
493                    val prefBuffSize = 4096 (* Get this from somewhere? *)
494                    val socket = INetSock.TCP.socket(): Socket.active INetSock.stream_sock
495                    (* We don't have a stream to produce error messages so simply fail if
496                       we get an exception here. *)
497                    val localhost = NetHostDB.addr (valOf(NetHostDB.getByName "localhost"))
498                    val port = valOf(Int.fromString portNo)
499                    val () = Socket.connect(socket, INetSock.toAddr(localhost, port))
500                    (* Construct the readers and writers. *)
501
502                    fun sendASN1(v: Word8Vector.vector list) =
503                    let
504                        open Word8VectorSlice
505                        (* Write the whole data, in chunks if necessary. *)
506                        fun sendSlice slice =
507                            if length slice = 0
508                            then ()
509                            else sendSlice(subslice(slice, Socket.sendVec(socket, slice), NONE))
510                    in
511                        sendSlice(Word8VectorSlice.full(Word8Vector.concat v))
512                    end
513
514                    fun readVecFromSocket(n: int): Word8Vector.vector = Socket.recvVec(socket, n)
515
516                    open TextIO Asn1
517
518                    local (* Interlocked writer for TextIO.stdOut *)
519                        (* Whenever we write plain text we package it as an ASN1 packet. *)
520                        fun writeVecToSocket(v: CharVectorSlice.slice) =
521                            (
522                                sendASN1(encodeItem(Application(1, Primitive), [encodeString(CharVectorSlice.vector v)]));
523                                CharVectorSlice.length v (* It's written it all. *)
524                            )
525                        val lockedWriteVec = ThreadLib.protect outputLock writeVecToSocket
526                        val lockedWriter =
527                            TextPrimIO.WR {
528                                name = "TextIO.stdOut", chunkSize = prefBuffSize,
529                                writeVec = SOME lockedWriteVec, writeArr = NONE,
530                                writeVecNB = NONE, writeArrNB = NONE, block = NONE, canOutput = NONE,
531                                getPos = NONE, setPos = NONE, endPos = NONE, verifyPos = NONE,
532                                close = fn () => raise Fail "stdOut must not be closed",
533                                ioDesc = SOME(Socket.ioDesc socket) }
534                    in
535                        (* Use this locked version for normal stdOut. *)
536                        val () = setOutstream(stdOut,
537                                    StreamIO.mkOutstream(TextPrimIO.augmentWriter lockedWriter, IO.LINE_BUF))
538                    end
539
540                    local
541                        (* Create a functional binary stream *)
542                        val reader =
543                            BinPrimIO.RD {
544                                name = "socket", chunkSize = prefBuffSize,
545                                readVec = SOME readVecFromSocket, readArr = NONE, readVecNB = NONE,
546                                readArrNB = NONE, block = NONE,
547                                canInput = NONE, avail = fn _ => NONE,
548                                getPos = NONE, setPos = NONE, endPos = NONE, verifyPos = NONE,
549                                close = fn _ => (), ioDesc = SOME(Socket.ioDesc socket)
550                            }
551                        val binStream =
552                            BinIO.StreamIO.mkInstream(BinPrimIO.augmentReader reader, Word8Vector.fromList [])
553                    in
554                        val inStream = ref binStream
555                    end
556
557                    fun protocolError error =
558                    let
559                        open OS.Process
560                    in
561                        TextIO.print ("Protocol error: " ^ error) handle _ => ();
562                        exit failure
563                    end
564
565                    (* Reads a request.  Calls OS.Process.exit at end-of-file or on a protocol error. *)
566                    fun readRequest (): request =
567                    let
568                        open Asn1
569
570                        (* Read the ASN1 header to get the tag and then read the data.
571                           Position the stream ready to read the next request. *)
572                        val (requestTag, data) =
573                            case readHeader BinIO.StreamIO.input1 (!inStream) of
574                                NONE => (* If we had EOF here it's probably because we've closed. *)
575                                    OS.Process.exit OS.Process.success (* Close down. *)
576                            |   SOME((tag, length), afterHdr) =>
577                                let
578                                    val (vector, afterBlock) =
579                                        BinIO.StreamIO.inputN(afterHdr, length)
580                                in
581                                    if Word8Vector.length vector = length
582                                    then ()
583                                    else protocolError "Stream closed";
584                                    inStream := afterBlock;
585                                    (tag, vector)
586                                end
587
588                        fun splitSequence v =
589                            case decodeItem v of
590                                SOME{tag, data, remainder} =>
591                                    (tag, data) :: splitSequence remainder
592                            |   NONE => []
593
594                        (* See if an item is present and return it if it is. *)
595                        fun findData tag list =
596                            Option.map #2 (List.find (fn (t, _) => t = tag) list)
597
598                        fun findString tag list =
599                            Option.map decodeString (findData tag list)
600                        and findInt tag list =
601                            Option.map (FixedInt.fromInt o decodeInt) (findData tag list)
602                    in
603                        case requestTag of
604                            Application(3, _) => (* Compilation request. *)
605                            let
606                                val tdList = splitSequence(Word8VectorSlice.full data)
607                                (* Request id *)
608                                val reqId = findString (Application(1, Primitive)) tdList
609                                (* File name - optional, default "" *)
610                                val fileName = getOpt(findString (Context(1, Primitive)) tdList, "")
611                                (* Start position - optional, default 0 *)
612                                val startPosition = getOpt(findInt (Context(2, Primitive)) tdList, 0)
613                                (* Prelude code - optional, default "" *)
614                                val preludeCode = getOpt(findString (Context(3, Primitive)) tdList, "")
615                                (* Source code *)
616                                val source = findString (Context(4, Primitive)) tdList
617                            in
618                                case (reqId, source) of
619                                    (SOME requestId, SOME sourceText) =>
620                                        CompileRequest { requestId = requestId, fileName = fileName, startPosition = startPosition,
621                                                 preludeCode = preludeCode, sourceCode = sourceText }
622                                |   (SOME requestId, _) => UnknownRequest { request = 3, requestId = requestId }
623                                |   _ => UnknownRequest { request = 3, requestId = "" } (* Malformed *)
624                            end
625
626                        |   Application(4, _) => (* Return the type of the selected node. *)
627                            let
628                                val tdList = splitSequence(Word8VectorSlice.full data)
629                                (* Request id *)
630                                val reqId = findString (Application(1, Primitive)) tdList
631                                (* Parse id *)
632                                val parseId = findString (Application(2, Primitive)) tdList
633                                (* Start offset *)
634                                val startOff = findInt (Context(1, Primitive)) tdList
635                                (* End offset *)
636                                val endOff = findInt (Context(2, Primitive)) tdList
637                            in
638                                case (reqId, parseId, startOff, endOff) of
639                                    (SOME requestId, SOME parseTreeId, SOME startOffset, SOME endOffset) =>
640                                        TypeRequest{
641                                            requestId = requestId, parseTreeId = parseTreeId,
642                                            location = { startOffset = startOffset, endOffset = endOffset }
643                                            }
644                                |   (SOME requestId, _, _, _) => UnknownRequest { request = 4, requestId = requestId }
645                                |   _ => UnknownRequest { request = 4, requestId = "" } (* Malformed *)
646                            end
647
648                        |   Application(5, _) => (* Move request. *)
649                            let
650                                val tdList = splitSequence(Word8VectorSlice.full data)
651                                (* Request id *)
652                                val reqId = findString (Application(1, Primitive)) tdList
653                                (* Parse id *)
654                                val parseId = findString (Application(2, Primitive)) tdList
655                                (* Start offset *)
656                                val startOff = findInt (Context(1, Primitive)) tdList
657                                (* End offset *)
658                                val endOff = findInt (Context(2, Primitive)) tdList
659                                (* Move direction *)
660                                val dir = findInt (Context(3, Primitive)) tdList
661                            in
662                                case (reqId, parseId, startOff, endOff, dir) of
663                                    (SOME requestId, SOME parseTreeId, SOME startOffset,
664                                     SOME endOffset, SOME dir) =>
665                                    let
666                                        val dirn =
667                                            case dir of
668                                                1 => DirUp
669                                            |   2 => DirLeft
670                                            |   3 => DirRight
671                                            |   _ (*4*) => DirDown
672                                    in
673                                        MoveRequest{
674                                            requestId = requestId, parseTreeId = parseTreeId, direction = dirn,
675                                            location = { startOffset = startOffset, endOffset = endOffset }
676                                            }
677                                    end
678                                |   (SOME requestId, _, _, _, _) => UnknownRequest { request = 5, requestId = requestId }
679                                |   _ => UnknownRequest { request = 5, requestId = "" } (* Malformed *)
680
681                            end
682
683                        |   Application(6, _) => (* Declaration location for variables. *)
684                            let
685                                val tdList = splitSequence(Word8VectorSlice.full data)
686                                (* Request id *)
687                                val reqId = findString (Application(1, Primitive)) tdList
688                                (* Parse id *)
689                                val parseId = findString (Application(2, Primitive)) tdList
690                                (* Start offset *)
691                                val startOff = findInt (Context(1, Primitive)) tdList
692                                (* End offset *)
693                                val endOff = findInt (Context(2, Primitive)) tdList
694                                (* Dec type *)
695                                val dt = findInt (Context(3, Primitive)) tdList
696                            in
697                                case (reqId, parseId, startOff, endOff, dt) of
698                                    (SOME requestId, SOME parseTreeId, SOME startOffset,
699                                     SOME endOffset, SOME dect) =>
700                                    let
701                                        val decType =
702                                            case dect of
703                                                2 => DecOpen
704                                            |   3 => DecParent
705                                            |   _ (*1*) => DecLocal
706                                    in
707                                        DecRequest{
708                                            requestId = requestId, parseTreeId = parseTreeId, decType = decType,
709                                            location = { startOffset = startOffset, endOffset = endOffset }
710                                            }
711                                    end
712                                |   (SOME requestId, _, _, _, _) => UnknownRequest { request = 6, requestId = requestId }
713                                |   _ => UnknownRequest { request = 6, requestId = "" } (* Malformed *)
714                            end
715
716                        |   Application(7, _) => (* List the references to a variable. *)
717                            let
718                                val tdList = splitSequence(Word8VectorSlice.full data)
719                                (* Request id *)
720                                val reqId = findString (Application(1, Primitive)) tdList
721                                (* Parse id *)
722                                val parseId = findString (Application(2, Primitive)) tdList
723                                (* Start offset *)
724                                val startOff = findInt (Context(1, Primitive)) tdList
725                                (* End offset *)
726                                val endOff = findInt (Context(2, Primitive)) tdList
727                            in
728                                case (reqId, parseId, startOff, endOff) of
729                                    (SOME requestId, SOME parseTreeId, SOME startOffset, SOME endOffset) =>
730                                        RefRequest{
731                                            requestId = requestId, parseTreeId = parseTreeId,
732                                            location = { startOffset = startOffset, endOffset = endOffset }
733                                            }
734                                |   (SOME requestId, _, _, _) => UnknownRequest { request = 7, requestId = requestId }
735                                |   _ => UnknownRequest { request = 7, requestId = "" } (* Malformed *)
736                            end
737
738                        |   Universal(tagNo, _) => UnknownRequest { request = tagNo, requestId = "" }
739                        |   Application(tagNo, _) => UnknownRequest { request = tagNo, requestId = "" }
740                        |   Context(tagNo, _) => UnknownRequest { request = tagNo, requestId = "" }
741                        |   Private(tagNo, _) => UnknownRequest { request = tagNo, requestId = "" }
742                        (*case startCh of
743                        |   #"O" => (* Print list of valid commands. *)
744                            let
745                                val requestId = readToEscape("", #",")
746                                val parseTreeId = readToEscape("", #",")
747                                val startOffset = getInt #","
748                                val endOffset = getInt #"o"
749                            in
750                                PropertyRequest{
751                                    requestId = requestId, parseTreeId = parseTreeId,
752                                    location = { startOffset = startOffset, endOffset = endOffset }
753                                    }
754                            end
755
756                        |   #"K" => (* Cancel request. *)
757                                KillRequest { requestId = readToEscape ("", #"k") }
758                        *)
759                    end (* readRequest *)
760
761                    fun sendStartedMessage () = 
762                    let
763                        fun sendResponse () =
764                            sendASN1(encodeItem(Application(2, Primitive), [encodeString "1.0.0"]))
765                    in
766                        ThreadLib.protect outputLock sendResponse ()
767                    end
768
769                    (* Send a reply packet. *)
770                    fun sendResponse response =
771                    let
772                        fun encodeFullLocation { file, startLine, startPosition, endPosition, ...} =
773                        let
774                            val encFile =
775                                if file = "" then [] else encodeItem(Context(1, Primitive), [encodeString file])
776                            val encLine =
777                                if startLine = 0 then [] else encodeItem(Context(2, Primitive), [encodeInt(FixedInt.toInt startLine)])
778                            val encStart =
779                                if startPosition = 0 then [] else encodeItem(Context(3, Primitive), [encodeInt(FixedInt.toInt startPosition)])
780                            val encEnd =
781                                if endPosition = 0 then [] else encodeItem(Context(4, Primitive), [encodeInt(FixedInt.toInt endPosition)])
782                        in
783                            encFile @ encLine @ encStart @ encEnd
784                        end
785
786                        and encodeLocation {startOffset, endOffset } =
787                                encodeItem(Context(3, Primitive), [encodeInt(FixedInt.toInt startOffset)]) @
788                                encodeItem(Context(4, Primitive), [encodeInt(FixedInt.toInt endOffset)])
789
790                        and encodeRequestId requestId =
791                            encodeItem(Application(20, Primitive), [encodeString requestId])
792                            
793                        and encodeParseId parseId =
794                            encodeItem(Application(21, Primitive), [encodeString parseId])
795
796                        fun mapEnc _ [] = []
797                        |   mapEnc f (hd :: tl) = f hd @ mapEnc f tl
798
799                        (* Turn a pretty-print structure into text, stripping out mark-up. *)
800                        (* TODO: We could return the "pretty" structure and have the IDE format it. *)
801                        fun prettyAsString message =
802                        let
803                            val result = ref []
804                            fun doPrint s = result := s :: ! result
805                            val () = PolyML.prettyPrint(doPrint, 120(*!PolyML.Compiler.lineLength*)) message
806                        in
807                            String.concat(List.rev(! result))
808                        end
809
810                        fun makeResponse (CompilerResponse { requestId, parseTreeId, finalOffset, result }) =
811                            let
812                                fun encodeError { hardError, location, message } =
813                                    encodeItem(Context(4, Constructed),
814                                        encodeItem(Context(1, Primitive), [encodeBool hardError]) @
815                                        encodeItem(Context(3, Constructed), encodeFullLocation location) @
816                                        encodeItem(Context(2, Primitive), [encodeString(prettyAsString message)])
817                                        )
818
819                                val (resultCode, resultData) =
820                                    case result of
821                                        Succeeded errors =>
822                                            (0, mapEnc encodeError errors)
823                                    |   RuntimeException (s, errors) =>
824                                            (1, encodeItem(Context(3, Primitive),
825                                                [encodeString(prettyAsString s)]) @ mapEnc encodeError errors)
826                                    |   PreludeFail s =>
827                                            (2, encodeItem(Context(3, Primitive), [encodeString s]))
828                                    |   CompileFail errors =>
829                                            (3, mapEnc encodeError errors)
830                                    |   CompileCancelled errors =>
831                                            (4, mapEnc encodeError errors)
832                            in
833                                sendASN1(encodeItem(Application(3, Constructed),
834                                    encodeRequestId requestId @ encodeParseId parseTreeId @
835                                    encodeItem(Context(1, Primitive), [encodeInt(FixedInt.toInt finalOffset)]) @
836                                    encodeItem(Context(2, Primitive), [encodeInt(FixedInt.toInt resultCode)]) @
837                                    resultData))
838                            end
839
840                        |   makeResponse (PropertyResponse { requestId, parseTreeId, location, commands }) =
841                            let
842                                fun encCommand c = encodeItem(Context(2, Primitive), [encodeString c])
843                            in
844                                sendASN1(encodeItem(Application(4, Constructed),
845                                    encodeRequestId requestId @ encodeParseId parseTreeId @
846                                    encodeItem(Context(1, Constructed), encodeLocation location) @
847                                    mapEnc encCommand commands))
848                            end
849
850                        |   makeResponse (MoveResponse { requestId, parseTreeId, location }) =
851                                sendASN1(encodeItem(Application(7, Constructed),
852                                    encodeRequestId requestId @ encodeParseId parseTreeId @
853                                    encodeItem(Context(1, Constructed), encodeLocation location)))
854
855                        |   makeResponse (TypeResponse { requestId, parseTreeId, location, typeRes }) =
856                            let
857                                val typeData =
858                                    case typeRes of
859                                        NONE => []
860                                    |   SOME t => encodeItem(Context(2, Primitive), [encodeString(prettyAsString t)])
861                            in
862                                sendASN1(encodeItem(Application(8, Constructed),
863                                    encodeRequestId requestId @ encodeParseId parseTreeId @
864                                    encodeItem(Context(1, Constructed), encodeLocation location) @ typeData))
865                            end
866
867                        |   makeResponse (DecResponse { requestId, parseTreeId, location, decLocation }) =
868                            let
869                                val decData =
870                                    case decLocation of
871                                        NONE => []
872                                    |   SOME l => encodeItem(Context(2, Constructed), encodeFullLocation l)
873                            in
874                                sendASN1(encodeItem(Application(9, Constructed),
875                                    encodeRequestId requestId @ encodeParseId parseTreeId @
876                                    encodeItem(Context(1, Constructed), encodeLocation location) @ decData))
877                            end
878
879                        |   makeResponse (RefResponse { requestId, parseTreeId, location, references }) =
880                            let
881                                fun encLoc l = encodeItem(Context(2, Constructed), encodeLocation l)
882                            in
883                                sendASN1(encodeItem(Application(10, Constructed),
884                                    encodeRequestId requestId  @ encodeParseId parseTreeId @
885                                    encodeItem(Context(1, Constructed), encodeLocation location) @
886                                    mapEnc encLoc references))
887                            end
888
889                        |   makeResponse (UnknownResponse { requestId, ... }) =
890                                (* Send an Error packet. *)
891                                sendASN1(encodeItem(Application(0, Constructed),
892                                    if requestId = "" then []
893                                    else encodeRequestId requestId))
894
895                        fun sendResponse () =
896                        (
897                            makeResponse response handle _ => protocolError "Exception"
898                        )
899                    in
900                        (* Sending the response packet must be atomic with respect to any other
901                           output to stdOut. *)
902                        ThreadLib.protect outputLock sendResponse ()
903                    end (* sendResponse *)
904                in
905                    (readRequest, sendStartedMessage, sendResponse)
906                end
907
908        (* Get the current parse tree and identifier. *)
909        fun getCurrentParse() =
910            withLock (fn () => let val (id, trees) = ! parseTree in (trees, ! lastParsetree, id) end)
911        (* Update lastParsetree if the id is still valid. *)
912        fun updateLastParse(id, pt) =
913        let
914            fun f () =
915            if id = #1 (! parseTree) then lastParsetree := pt else ()
916        in
917            withLock f
918        end
919        (* Set parse tree and ID as a result of a compilation.  Sets lastParsetree to the
920           head of the updated parse tree. *)
921        fun setParseTree(pt, id) =
922        let
923            fun f () =
924            (
925                parseTree := (id, pt); 
926                case pt of
927                    [] => lastParsetree := NONE
928                |   hd :: _ => lastParsetree := SOME hd
929            )
930        in
931            withLock f
932        end
933
934        (* The source text may consist of several "programs".  When we compile a "program" we
935           have to provide a way for the parsetree for this "program" to navigate to others
936           even though they won't have been compiled yet.  This enables it to work. *)
937        (* We have to return functions for the parent, for the next sibling even if there
938           isn't one and for the previous sibling. *)
939        fun toplevelParseTree (parseRootRef as ref currentList) =
940        let
941            open PolyML
942            (* This is called when we have processed the previous "programs" but
943               not yet processed this one. *)
944            fun makelist([], _) = (* Shouldn't happen *) raise Fail "Null list"
945            |   makelist(l as (locn, props) :: tl, previous) =
946                let
947                    fun this () = makelist(l, previous)
948                    (* If there is another item in the list we need a
949                       property that moves there whose "previous" property
950                       comes here. *)
951                    val next =
952                        case tl of
953                            [] => []
954                        |   _ => [PTnextSibling(
955                                    fn () => makelist(tl, [PTpreviousSibling this]))]
956                in
957                    (locn, previous @ next @ props)
958                end
959            fun parent () =
960                case ! parseRootRef of
961                    [] => raise Fail "Empty Tree"
962                |   trees as (hd :: _) =>
963                    let
964                        (* Navigation for one or more topdecs. *)
965                        val fullLoc =
966                            case (hd, List.last trees) of
967                                (({ file, startLine, startPosition, ... }, _),
968                                 ({ endLine, endPosition, ... }, _)) =>
969                                {
970                                    file=file, startLine=startLine,
971                                    startPosition=startPosition,
972                                    endLine=endLine, endPosition=endPosition
973                                }
974                    in
975                        (fullLoc, [PTfirstChild(fn () => makelist(trees, []))])
976                    end
977
978            val itemCount = List.length currentList
979
980            fun moveToNth n =
981            let
982                fun move (tree, 0) = tree
983                |   move ((loc, opts), n) =
984                    case List.find(fn PTnextSibling _ => true | _ => false) opts of
985                        NONE =>
986                        let
987                            (* We have to put a dummy item in at the end since when we
988                               created the parent properties for the last "program" we will
989                               have passed in a "next" entry even though there wasn't
990                               actually a "next". *)
991                            val { file, startLine, startPosition, ... } = loc
992                            val lastPos =
993                                { file = file, startLine = startLine, endLine = startLine,
994                                  startPosition = startPosition, endPosition = startPosition }
995                            val opts =
996                                List.filter(fn PTparent _ => true | PTpreviousSibling _ => true | _ => false) opts
997                        in
998                            (lastPos, opts)
999                        end
1000                    |   SOME (PTnextSibling f) => move(f(), n-1)
1001                    |   SOME _ => raise Match (* Shouldn't happen *)
1002            in
1003                case ! parseRootRef of
1004                    [] => raise Fail "Empty Tree"
1005                |   trees => move(makelist(trees, []), n)
1006            end
1007            val previous =
1008                case currentList of
1009                    [] => NONE (* This is the first. *)
1010                |   _ => SOME(fn () => moveToNth(itemCount-1))
1011            fun next () = moveToNth(itemCount+1)
1012        in
1013            { parent = SOME parent, next = SOME next, previous = previous }
1014        end
1015
1016        (* Move in the selected direction.  Returns the tree as the result of the move. *)
1017        fun navigateTo(searchLocation as {startOffset:FixedInt.int, endOffset:FixedInt.int}, lastParsetree) =
1018        case lastParsetree of
1019            NONE => NONE
1020        |   SOME({ startPosition, endPosition, ... }, tree) =>
1021            let
1022                open PolyML
1023                datatype direction = Up | Down | Left | Right
1024                fun find([], _) = NONE (* No change *)
1025                |   find(PTparent p :: _, Up) = SOME p
1026                |   find(PTpreviousSibling p :: _, Left) = SOME p
1027                |   find(PTnextSibling p :: _, Right) = SOME p
1028                |   find(PTfirstChild p :: _, Down) = SOME p
1029                |   find(_ :: tl, dir) = find (tl, dir)
1030            in
1031                if startOffset = startPosition andalso endOffset = endPosition
1032                then (* We're there already. *) lastParsetree
1033                else if startOffset >= startPosition andalso endOffset <= endPosition
1034                then (* It's this node or a child. *)
1035                    let
1036                        val child = find(tree, Down)
1037                    in
1038                        (* See if the element we want is actually a child. *)
1039                        case child of
1040                            SOME child =>
1041                            let
1042                                (* See which child it is. *)
1043                                fun findChild(location as {startPosition, endPosition, ...}, child) =
1044                                    if startOffset >= startPosition andalso endOffset <= endPosition
1045                                    then SOME (location, child)
1046                                    else
1047                                    case find(child, Right) of
1048                                        NONE => NONE
1049                                    |   SOME next => findChild(next())
1050                            in
1051                                case findChild(child()) of
1052                                    NONE => lastParsetree (* In this *)
1053                                |   SOME child => navigateTo(searchLocation, SOME child)
1054                            end
1055                        |   NONE => lastParsetree (* No children. *)
1056                    end
1057                else (* Must go out. *)
1058                (
1059                    case find(tree, Up) of
1060                        SOME p => navigateTo(searchLocation, SOME(p()))
1061                    |   NONE => NONE (* Not found *)
1062                )
1063            end
1064
1065        (* Main protocol loop. *)
1066        fun runProtocol currentCompilation =
1067        let
1068            (* Return the location of the given tree. *)
1069            fun treeLocation NONE = {startOffset = 0, endOffset = 0}
1070            |   treeLocation (SOME ({startPosition, endPosition, ...}, _)) =
1071                        {startOffset = startPosition, endOffset = endPosition}
1072        in
1073            case readRequest () of
1074                PropertyRequest { requestId: string, parseTreeId: string, location } =>
1075                let (* Properties of selected node. *)
1076                    (* Get the current parse tree and check the ID matches *)
1077                    val (_, lastParsetree, currentParseID) = getCurrentParse()
1078                    val (commands, location) =
1079                        if parseTreeId = currentParseID
1080                        then
1081                        let
1082                            val newTree = navigateTo(location, lastParsetree)
1083                            (* Update the last tree if it's still valid. *)
1084                            val () = updateLastParse(currentParseID, newTree)
1085                            val commands =
1086                                case newTree of
1087                                    NONE => []
1088                                |   (SOME(_, tree)) =>
1089                                    let
1090                                        open PolyML
1091                                        fun printCode(PTparent _, rest) = "U" :: rest
1092                                        |   printCode(PTpreviousSibling _, rest) = "P" :: rest
1093                                        |   printCode(PTnextSibling _, rest) = "N" :: rest
1094                                        |   printCode(PTfirstChild _, rest) = "C" :: rest
1095                                        |   printCode(PTtype _, rest) = "T" :: rest
1096                                        |   printCode(PTdeclaredAt _, rest) = "I" :: rest
1097                                        |   printCode(PTopenedAt _, rest) = "J" :: rest
1098                                        |   printCode(PTstructureAt _, rest) = "S" :: rest
1099                                        |   printCode(PTreferences(_, _::_), rest) = "V" :: rest
1100                                                (* Only include references if there is at least one
1101                                                   local reference. *)
1102                                        |   printCode(PTreferences(_, []), rest) = rest
1103                                        |   printCode(PTprint _, rest) = rest
1104                                        |   printCode(PTbreakPoint _, rest) = rest
1105                                        |   printCode(PTcompletions _, rest) = rest
1106                                        |   printCode(PTdefId _, rest) = rest
1107                                        |   printCode(PTrefId _, rest) = rest
1108                                    in
1109                                        List.foldl printCode [] tree
1110                                    end
1111                        in
1112                            (commands, treeLocation newTree)
1113                        end
1114                        else ([], { startOffset = 0, endOffset = 0 }) (* Wrong ID. *)
1115                in
1116                    sendResponse(
1117                        PropertyResponse {
1118                            requestId = requestId, parseTreeId = currentParseID,
1119                            location = location, commands = commands
1120                        });
1121                    runProtocol currentCompilation
1122                end
1123
1124            |   MoveRequest { requestId, parseTreeId, location, direction } =>
1125                let  (* Get location after a move relative to a selected node. *)
1126                    val (_, lastParsetree, currentParseID) = getCurrentParse()
1127                    val newLocation =
1128                        if parseTreeId = currentParseID
1129                        then
1130                        let
1131                            (* Move to the given location, then move in the required direction. *)
1132                            val newTree = 
1133                                case navigateTo(location, lastParsetree) of
1134                                    NONE => NONE
1135                                |   SOME(location, tree) =>
1136                                    let
1137                                        open PolyML
1138                                        fun find([], _) = (location, tree) (* No change *)
1139                                        |   find(PTparent p :: _, DirUp) = p()
1140                                        |   find(PTpreviousSibling p :: _, DirLeft) = p()
1141                                        |   find(PTnextSibling p :: _, DirRight) = p()
1142                                        |   find(PTfirstChild p :: _, DirDown) = p()
1143                                        |   find(_ :: tl, dir) = find (tl, dir)
1144                
1145                                    in
1146                                        SOME(find(tree, direction))
1147                                    end
1148                            (* Update the last tree if it's still valid. *)
1149                            val () = updateLastParse(currentParseID, newTree)
1150                        in
1151                            treeLocation newTree (* Return the location of the updated tree. *)
1152                        end
1153                        else { startOffset = 0, endOffset = 0 } (* *)
1154                in
1155                    sendResponse(
1156                        MoveResponse {
1157                            requestId = requestId, parseTreeId = currentParseID, location = newLocation
1158                        });
1159                    runProtocol currentCompilation
1160                end
1161
1162            |   TypeRequest { requestId, parseTreeId, location } =>
1163                let (* Type of value at selected node. *)
1164                    val (_, lastParsetree, currentParseID) = getCurrentParse()
1165                    val (typeRes, location) =
1166                        if parseTreeId = currentParseID
1167                        then
1168                        let
1169                            (* Move to the required location. *)
1170                            val newTree = navigateTo(location, lastParsetree)
1171                            val () = updateLastParse(currentParseID, newTree)
1172                            (* If it has a type return it. *)
1173                            val typeRes =
1174                                case newTree of
1175                                    NONE => NONE
1176                                |   (SOME(_, tree)) =>
1177                                    (
1178                                        (* Print the type if it's there.  Don't include any mark-up. *)
1179                                        (* TODO: This uses the global name space to find types and structures.
1180                                           It really should use the local name space but that requires adding
1181                                           an environment to the parse tree. *)
1182                                        case List.find (fn (PolyML.PTtype _) => true | _ => false) tree of
1183                                            SOME(PolyML.PTtype t) =>
1184                                                SOME(PolyML.NameSpace.Values.printType(t, 100, SOME PolyML.globalNameSpace))
1185                                        |   _ => NONE
1186                                    )
1187                        in
1188                           (typeRes, treeLocation newTree)
1189                        end
1190                        else (NONE, { startOffset = 0, endOffset = 0 })
1191                in
1192                    sendResponse(
1193                        TypeResponse {
1194                            requestId = requestId, parseTreeId = currentParseID,
1195                            location = location, typeRes = typeRes
1196                        });
1197                    runProtocol currentCompilation
1198                end
1199
1200            |   DecRequest { requestId, parseTreeId, location, decType } =>
1201                let (* Information about declaration location of identifier at selected node. *)
1202                    val (_, lastParsetree, currentParseID) = getCurrentParse()
1203                    val (decLocation, location) =
1204                        if parseTreeId = currentParseID
1205                        then
1206                        let
1207                            (* Move to the required location. *)
1208                            val newTree = navigateTo(location, lastParsetree)
1209                            val () = updateLastParse(currentParseID, newTree)
1210                            val decLocation =
1211                                (* If it has the right kind of property return it. *)
1212                                case newTree of
1213                                    NONE => NONE
1214                                |   (SOME(_, tree)) =>
1215                                    let
1216                                        open PolyML
1217                                        val getLoc =
1218                                            case decType of
1219                                                DecLocal => (fn (PTdeclaredAt p) => SOME p | _ => NONE)
1220                                            |   DecOpen => (fn (PTopenedAt p) => SOME p | _ => NONE)
1221                                            |   DecParent => (fn (PTstructureAt p) => SOME p | _ => NONE)
1222                                        (* Seatch in the properties of the current node for the property we want. *)
1223                                        fun findLoc [] = NONE
1224                                        |   findLoc (hd::tl) =
1225                                            case getLoc hd of
1226                                                SOME location => SOME location
1227                                            |   NONE => (* Keep trying. *) findLoc tl
1228                                    in
1229                                        findLoc tree
1230                                    end
1231                        in
1232                            (decLocation, treeLocation newTree)
1233                        end
1234                        else (NONE, { startOffset = 0, endOffset = 0 })
1235                in
1236                    sendResponse(
1237                        DecResponse {
1238                            requestId = requestId, parseTreeId = currentParseID,
1239                            location = location, decLocation = decLocation
1240                        });
1241                    runProtocol currentCompilation
1242                end
1243
1244            |   RefRequest { requestId, parseTreeId, location } =>
1245                let (* Type of value at selected node. *)
1246                    val (_, lastParsetree, currentParseID) = getCurrentParse()
1247                    val (references, location) =
1248                        if parseTreeId = currentParseID
1249                        then
1250                        let
1251                            (* Move to the required location. *)
1252                            val newTree = navigateTo(location, lastParsetree)
1253                            val () = updateLastParse(currentParseID, newTree)
1254                            (* Find the local references. *)
1255                            val references =
1256                                case newTree of
1257                                    NONE => []
1258                                |   SOME(_, tree) =>
1259                                    (
1260                                        case List.find (fn (PolyML.PTreferences _) => true | _ => false) tree of
1261                                            SOME(PolyML.PTreferences(_, l)) =>
1262                                                List.map (fn {startPosition, endPosition, ...} =>
1263                                                            { startOffset=startPosition, endOffset=endPosition}) l
1264                                        |   _ => []
1265                                    )
1266                        in
1267                           (references, treeLocation newTree)
1268                        end
1269                        else ([], { startOffset = 0, endOffset = 0 })
1270                in
1271                    sendResponse(
1272                        RefResponse {
1273                            requestId = requestId, parseTreeId = currentParseID,
1274                            location = location, references = references
1275                        });
1276                    runProtocol currentCompilation
1277                end
1278
1279            |   CompileRequest { requestId, fileName, startPosition, preludeCode, sourceCode } =>
1280                (* Unlike the other requests this is done asynchronously. *)
1281                let
1282                    fun compileThread () =
1283                    let
1284                        type errorMsg =
1285                            { message: PolyML.pretty, hard: bool, location: PolyML.location,
1286                              context: PolyML.pretty option }
1287                        (* Even success may include warning messages. *)
1288                        datatype compileResult =
1289                            Success
1290                        |   Exception of exn
1291                        |   Interrupted
1292                        |   Errors
1293
1294                        local
1295                            open PolyML.NameSpace
1296                            (* Put in the results without printing. *)
1297                            fun resultFun
1298                                { fixes: (string * Infixes.fixity) list, values: (string * Values.value) list,
1299                                  structures: (string * Structures.structureVal) list, signatures: (string * Signatures.signatureVal) list,
1300                                  functors: (string * Functors.functorVal) list, types: (string * TypeConstrs.typeConstr) list} =
1301                            let
1302                                open PolyML
1303                            in
1304                                List.app (#enterFix globalNameSpace) fixes;
1305                                List.app (#enterType globalNameSpace) types;
1306                                List.app (#enterSig globalNameSpace) signatures;
1307                                List.app (#enterStruct globalNameSpace) structures;
1308                                List.app (#enterFunct globalNameSpace) functors;
1309                                List.app (#enterVal globalNameSpace) values
1310                            end
1311                        in
1312                            (* Compile the prelude.  Simply returns true if it succeeded and false on any error.
1313                               Note: Unlike the main compilation this is run with the interlock held and
1314                               interrupts deferred. *)
1315                            fun compilePreludeString stringInput: string option =
1316                            let
1317                                val stringStream = TextIO.openString stringInput
1318
1319                                fun compilerResultFun (_, codeOpt) =
1320                                    case codeOpt of
1321                                        SOME code => (fn () => resultFun(code()))
1322                                     |  NONE => raise Fail "Static Errors"
1323
1324                                fun compilerLoop () =
1325                                (* Compile each "program" until either we get to the end or an exception. *)
1326                                if TextIO.endOfStream stringStream
1327                                then NONE (* Reached the end of the input without error. *)
1328                                else 
1329                                let
1330                                    (* Compile the code and get the result. *)
1331                                    open PolyML PolyML.Compiler
1332                                    val (code, result) =
1333                                        (PolyML.compiler(fn () => TextIO.input1 stringStream,
1334                                            [CPOutStream TextIO.print, CPCompilerResultFun compilerResultFun]),
1335                                         NONE)
1336                                         handle exn => (fn() => (), SOME(exnMessage exn))
1337                                 in
1338                                    case result of
1339                                        NONE =>
1340                                        (
1341                                            (* No exception in compiler: run the code and check that it
1342                                               runs successfully. *)
1343                                            case ((code(); NONE) handle exn => SOME(exnMessage exn)) of
1344                                                NONE => compilerLoop () (* Continue. *)
1345                                            |   exn => exn
1346                                        )
1347                                    |   error => error
1348                                end
1349                                
1350                                fun runloop () =
1351                                let
1352                                    val res = compilerLoop()
1353                                in
1354                                    (* The prelude may update the current parse tree. *)
1355                                    case !parseTree of
1356                                        (_, []) => lastParsetree := NONE
1357                                    |   (_, hd :: _) => lastParsetree := SOME hd;
1358                                    res
1359                                end
1360                            in
1361                                (* This is run with the lock held. *)
1362                                withLock runloop
1363                            end
1364
1365                            (* Compile the main source code. *)
1366                            fun compileString(stringInput, startPosition: int) =
1367                            let
1368                                val errorList = ref []
1369                                val stringPosition = ref 0
1370                                val stringSize = String.size stringInput
1371                                val resultTrees : PolyML.parseTree list ref = ref []
1372                                val lastTreePosition = ref 0
1373                                fun readIn () =
1374                                let
1375                                    val posn = ! stringPosition
1376                                in
1377                                    if posn >= stringSize
1378                                    then NONE
1379                                    else SOME(String.sub(stringInput, posn)) before (stringPosition := posn+1)
1380                                end
1381                                (* We need to define our own compilerResultFun in order to capture the parse trees. *)
1382                                fun compilerResultFun (parsetree, codeOpt) =
1383                                (
1384                                    (* Add the parsetree to the list.  Record this as the position of the last valid tree. *)
1385                                    case parsetree of
1386                                        SOME pt =>
1387                                            (resultTrees := ! resultTrees @ [pt]; lastTreePosition := !stringPosition)
1388                                    |   NONE => (); (* Not if parse failed. *)
1389                                    case codeOpt of
1390                                        SOME code => (fn () => resultFun(code()))
1391                                     |  NONE => raise Fail "Static Errors"
1392                                )
1393
1394                                fun compilerLoop () =
1395                                (* Compile each "program" until either we get to the end or an exception. *)
1396                                if ! stringPosition >= stringSize
1397                                then Success (* Reached the end of the input without error. *)
1398                                else
1399                                let
1400                                    open PolyML PolyML.Compiler
1401                                    val (code, result) =
1402                                        (PolyML.compiler(readIn,
1403                                            [CPOutStream TextIO.print, CPLineOffset (fn () => startPosition + !stringPosition),
1404                                             CPErrorMessageProc (fn msg => errorList := !errorList @ [msg]),
1405                                             CPCompilerResultFun compilerResultFun, CPFileName fileName,
1406                                             CPRootTree (toplevelParseTree resultTrees)]),
1407                                         Success)
1408                                         handle Fail _ => (fn() => (), Errors)
1409                                         |  _ (* E.g. Interrupted *) => (fn() => (), Interrupted)
1410                                in
1411                                    case result of
1412                                        Success => (* Compilation succeeded. *)
1413                                        (
1414                                            (* Run the code.  If it raised an exception pass that back. *)
1415                                            case (code(); Success) handle exn => Exception exn of
1416                                                Success => compilerLoop () (* Continue. *)
1417                                            |   fault => fault
1418                                        )
1419                                    |   error => error
1420                                end
1421                            in
1422                                (compilerLoop (), startPosition + !lastTreePosition,
1423                                 ! resultTrees, ! errorList)
1424                            end
1425                        end
1426                    in
1427                        if
1428                        (* First run the prelude.  If there are any errors report them and stop. *)
1429                            case compilePreludeString preludeCode of
1430                                NONE => true (* Succeeded - continue *)
1431                            |   SOME preludeError =>  (* Error - stop *)
1432                                let
1433                                    (* Leave the parse tree unchanged. *)
1434                                    val (_, _, currentId) = getCurrentParse()
1435                                in
1436                                    sendResponse(
1437                                        CompilerResponse {
1438                                            requestId = requestId, parseTreeId = currentId,
1439                                            finalOffset = startPosition, result = PreludeFail preludeError
1440                                        });
1441                                    false
1442                                end
1443                        then (* We can do the main compilation. *)
1444                        let
1445                            local
1446                                open Thread.Thread
1447                            in
1448                                (* The rest of this code is interruptible
1449                                   TODO: Multiple interrupts could result in not sending a
1450                                   result packet. *)
1451                                val () =
1452                                    setAttributes [EnableBroadcastInterrupt true, InterruptState InterruptAsynch]
1453                            end;
1454                            val (result, finalPosition, resultTrees, errors) =
1455                                compileString(sourceCode, FixedInt.toInt startPosition)
1456                            fun makeErrorPacket
1457                                {message: PolyML.pretty, hard: bool, location, ...} =
1458                                {
1459                                    hardError = hard,
1460                                    location = location,
1461                                    message = message
1462                                }
1463                            val errorPackets = List.map makeErrorPacket errors
1464                            val compileResult  =
1465                                case result of
1466                                    Success => Succeeded errorPackets (* May be warning messages. *)
1467                                |   Exception exn =>
1468                                    let
1469                                        open PolyML
1470                                        val exLoc =
1471                                            case exceptionLocation exn of
1472                                                SOME loc => [ContextLocation loc]
1473                                            |   NONE => []
1474                                        val exceptionString =
1475                                            (PrettyBlock(0, false, exLoc,
1476                                                [ prettyRepresentation(exn, FixedInt.fromInt(!PolyML.Compiler.printDepth)) ]))
1477                                    in
1478                                        RuntimeException(exceptionString, errorPackets)
1479                                    end
1480                                |   Interrupted => CompileCancelled errorPackets
1481                                |   Errors => CompileFail errorPackets
1482                            (* Update the tree unless parsing failed and we don't have one. *)
1483                            val parseTreeId =
1484                                case resultTrees of
1485                                    [] => #3 (getCurrentParse()) (* Return existing tree. *)
1486                                |   _ => (setParseTree(resultTrees, requestId); requestId)
1487                        in
1488                            (* Send the response. *)
1489                            sendResponse(
1490                                CompilerResponse {
1491                                    requestId = requestId, parseTreeId = parseTreeId,
1492                                    finalOffset = FixedInt.fromInt finalPosition, result = compileResult
1493                                })
1494                        end
1495                        else () (* Prelude failed. *)
1496                    end (* compileThread *)
1497
1498                    open Thread.Thread
1499
1500                    (* First see if the last compilation has terminated.  Starting a new
1501                       compilation before the previous one has finished is really a
1502                       protocol error. *)
1503                    val isStillRunning =
1504                        case currentCompilation of
1505                            NONE => false
1506                        |   SOME (_, lastCompileThread) => isActive lastCompileThread
1507                in
1508                    if isStillRunning
1509                    then sendResponse(
1510                            CompilerResponse {
1511                                requestId = requestId, parseTreeId = #3 (getCurrentParse()),
1512                                finalOffset = startPosition, result = PreludeFail "Thread still running"
1513                            })
1514                    else
1515                    let
1516                        (* The compile thread is run with interrupts deferred initially. *)
1517                        val thread = fork(compileThread, [InterruptState InterruptDefer])
1518                    in
1519                        runProtocol (SOME(requestId, thread))
1520                    end
1521                end
1522
1523            |   KillRequest { requestId: string } => (* Kill compilation. *)
1524                (
1525                    case currentCompilation of
1526                        NONE => () (* No compilation. *)
1527                    |   SOME (id, thread) =>
1528                        if requestId = id
1529                        then Thread.Thread.interrupt thread
1530                        else () (* Different ID running. *);
1531                    runProtocol currentCompilation
1532                )
1533
1534            |   UnknownRequest req => (* Respond with an empty response. *)
1535                (
1536                    sendResponse(UnknownResponse req);
1537                    runProtocol currentCompilation
1538                )
1539        end
1540    in
1541        let
1542            (* Turn off interrupts for the interface thread. *)
1543            open Thread.Thread
1544        in
1545            setAttributes[EnableBroadcastInterrupt false, InterruptState InterruptDefer]
1546        end;
1547        sendStartedMessage(); 
1548        runProtocol NONE (* No compilation. *)
1549    end (* runIDEProtocol. *)
1550
1551    local
1552        val polySpecificGeneralCall = RunCall.rtsCallFull2 "PolySpecificGeneral"
1553    in
1554        fun polySpecificGeneral(code: int, arg:'a):'b = RunCall.unsafeCast(polySpecificGeneralCall(RunCall.unsafeCast(code, arg)))
1555    end
1556
1557in
1558    structure PolyML =
1559    struct
1560        (* This is the root function to run the Poly/ML top level. *)
1561        fun rootFunction () : unit =
1562        let
1563            val argList = CommandLine.arguments()
1564            fun rtsRelease() = polySpecificGeneral (10, ())
1565            fun rtsHelp() = polySpecificGeneral (19, ())
1566            val gitVersion =
1567                case polySpecificGeneral (9, ()) of
1568                   "" => ""
1569                |   s => " (Git version " ^ s ^ ")"
1570            
1571            fun switchOption option = List.exists(fn s => s = option) argList
1572        in
1573            if switchOption "-v"
1574            then (* -v option : Print version information and exit *)
1575                print (String.concat ["Poly/ML ", PolyML.Compiler.compilerVersion, 
1576                                     "    RTS version: ", rtsRelease(), gitVersion, "\n"])
1577
1578            else if switchOption "--help"
1579            then (* --help option: Print argument information and exit. *)
1580               (
1581                print (String.concat ["Poly/ML ", PolyML.Compiler.compilerVersion, gitVersion, "\n"]);
1582                print "Compiler arguments:\n";
1583                print "\n";
1584                print "-v                   Print the version of Poly/ML and exit\n";
1585                print "--help               Print this message and exit\n";
1586                print "-q                   Suppress the start-up message and turn off printing of results\n";
1587                print "-i                   Interactive mode.  Default if input is from a terminal\n";
1588                print "--use FILE           Executes 'use \"FILE\";' before the ML shell starts\n";
1589                print "--eval STRING        Compiles and executes STRING as ML before the ML shell starts\n";
1590                print "--error-exit         Exit shell on unhandled exception\n";
1591                print "--with-markup        Include extra mark-up information when printing\n";
1592                print "--ideprotocol[=v2]   Run the IDE communications protocol\n";
1593                print "--script FILE        The input is a script.  Skips the first line if it begins with #!.";
1594                print "\nRun time system arguments:\n";
1595                print (rtsHelp())
1596               )
1597
1598            else if switchOption "--ideprotocol"
1599            then runIDEProtocol () (* Run the IDE communication protocol. *)
1600
1601            else if switchOption "--script"
1602            then
1603            let
1604                (* The next argument is the file name.  Open it but skip 
1605                   the first line if it's #!.   The rest of this code is
1606                   largely copied from PolyML.use.  *)
1607                fun getFileName("--script" :: fileName :: _) = fileName
1608                |   getFileName [] = (print "Missing file name after --script\n"; OS.Process.exit OS.Process.failure)
1609                |   getFileName(_ :: tail) = getFileName tail
1610                val fileName = getFileName argList
1611                open TextIO
1612                val inStream = getInstream(TextIO.openIn fileName)
1613                open StreamIO
1614                val stream = ref inStream
1615
1616                val lineNo   = ref 1
1617                val (start, _) = inputN(inStream, 2)
1618                fun getChar () =
1619                    case input1 (! stream) of
1620                        NONE => NONE
1621                    |   SOME (eoln as #"\n", strm) =>
1622                        (
1623                            lineNo := !lineNo + 1;
1624                            stream := strm;
1625                            SOME eoln
1626                        )
1627                    |   SOME(c, strm) => (stream := strm; SOME c)
1628                val () =
1629                    if start = "#!"
1630                    then while (case getChar () of NONE => false | SOME #"\n" => false | SOME _ => true) do ()
1631                    else ()
1632                val () = PolyML.print_depth 0 (* Quieten. *)
1633            in
1634                while not (endOfStream(!stream)) do
1635                let
1636                    open PolyML.Compiler
1637                    val code = PolyML.compiler(getChar, [CPFileName fileName, CPLineNo(fn () => !lineNo)])
1638                        handle exn =>
1639                            ( closeIn(!stream); PolyML.Exception.reraise exn )
1640                in
1641                    code() handle exn =>
1642                    (
1643                        (* Report exceptions in running code. *)
1644                        TextIO.print ("Exception- " ^ exnMessage exn ^ " raised\n");
1645                        input1 (! stream);
1646                        PolyML.Exception.reraise exn
1647                    )
1648                end;
1649                (* Normal termination: close the stream. *)
1650                closeIn (! stream)
1651            end
1652
1653            else (* Enter normal Poly/ML top-level. *)
1654            let
1655                open Signal
1656                val () =
1657                    if switchOption "-q"
1658                    then PolyML.print_depth 0
1659                    else print (String.concat ["Poly/ML ", PolyML.Compiler.compilerVersion, gitVersion, "\n"]);
1660                (* Set up a handler for SIGINT if that is currently set to SIG_DFL.
1661                   If a handler has been set up by an initialisation function don't replace it. *)
1662                val () =
1663                    case signal(2, SIG_IGN) of
1664                       SIG_IGN => ()
1665                    |  SIG_DFL => (signal(2, SIG_HANDLE(fn _ => Thread.Thread.broadcastInterrupt())); ())
1666                    |  oldHandle => (signal(2, oldHandle); ())
1667
1668                fun tryUseFileArguments [] = () (* done successfully *)
1669
1670                |   tryUseFileArguments ["--use"] =
1671                    (
1672                        print "'--use' requires a filename to be given as the next argument.\n";
1673                        OS.Process.exit OS.Process.failure
1674                    )
1675
1676                |   tryUseFileArguments ("--use" :: filenameArg :: moreArgs) =
1677                    (
1678                        PolyML.use filenameArg
1679                            handle _ =>
1680                            (
1681                                print("Error trying to use the file: '" ^ filenameArg ^ "'\n");
1682                                OS.Process.exit OS.Process.failure
1683                            );
1684                        tryUseFileArguments moreArgs
1685                    )
1686
1687                |   tryUseFileArguments ["--eval"] =
1688                    (
1689                        print "'--eval' requires a string to be given as the next argument.\n";
1690                        OS.Process.exit OS.Process.failure
1691                    )
1692
1693                |   tryUseFileArguments ("--eval" :: useString :: moreArgs) =
1694                    let
1695                        (* Compile and execute commands from the string. *)
1696                        val p = ref 0
1697                    in
1698                        while !p < size useString do
1699                        let
1700                            fun getChar() =
1701                                if !p >= size useString
1702                                then NONE
1703                                else SOME(String.sub(useString, !p)) before p := !p+1
1704                            val code =
1705                                PolyML.compiler(getChar, [])
1706                                    handle _ => OS.Process.exit OS.Process.failure
1707                        in
1708                            code() handle exn =>
1709                            (
1710                                (* Report exceptions in running code. *)
1711                                print ("Exception- " ^ exnMessage exn ^ " raised\n");
1712                                OS.Process.exit OS.Process.failure
1713                            )
1714                        end;
1715                        tryUseFileArguments moreArgs
1716                    end
1717
1718                |   tryUseFileArguments (_ :: args) = tryUseFileArguments args
1719
1720            in
1721                tryUseFileArguments argList;
1722                PolyML.shell (); 
1723                OS.Process.exit OS.Process.success (* Run any "atExit" functions and then quit. *)
1724            end
1725        end;
1726
1727        structure IDEInterface =
1728        struct
1729            val parseTree = parseTree
1730            val runIDEProtocol = runIDEProtocol
1731        end;
1732
1733        open PolyML (* Add this to the PolyML structure. *)
1734    end
1735end;
1736