Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

348 lines
9.1 KiB

5 years ago
  1. (* swank-mlworks.sml -- SWANK server for MLWorks
  2. *
  3. * This code has been placed in the Public Domain.
  4. *)
  5. (* This is an experiment to see how the interfaces/modules would look
  6. * in a language with a supposedly "good" module system.
  7. *
  8. * MLWorks is probably the only SML implementation that tries to
  9. * support "interactive programming". Since MLWorks wasn't maintained
  10. * the last 15 or so years, big chunks of the SML Basis Library are
  11. * missing or not the way as required by the standard. That makes it
  12. * rather hard to do anything; it also shows that MLWorks hasn't been
  13. * "used in anger" for a long time.
  14. *)
  15. structure Swank = struct
  16. structure Util = struct
  17. fun utf8ToString (v:Word8Vector.vector) : string = Byte.bytesToString v
  18. fun stringToUtf8 s = Byte.stringToBytes s
  19. end
  20. structure Map = struct
  21. datatype ('a, 'b) map = Alist of {list: ('a * 'b) list ref,
  22. eq: ('a * 'a) -> bool}
  23. fun stringMap () =
  24. Alist {list = ref [],
  25. eq = (fn (x:string,y:string) => x = y)}
  26. fun lookup (Alist {list, eq}, key) =
  27. let fun search [] = NONE
  28. | search ((key', value) :: xs) =
  29. if eq (key', key) then SOME value
  30. else search xs
  31. in search (!list)
  32. end
  33. fun put (Alist {list, eq}, key, value) =
  34. let val l = (key, value) :: (!list)
  35. in list := l
  36. end
  37. end
  38. structure CharBuffer = struct
  39. local
  40. structure C = CharArray
  41. datatype buffer = B of {array : C.array ref,
  42. index: int ref}
  43. in
  44. fun new hint = B {array = ref (C.array (hint, #"\000")),
  45. index = ref 0}
  46. fun append (buffer as B {array, index}, char) =
  47. let val a = !array
  48. val i = !index
  49. val len = C.length a
  50. in if i < len then
  51. (C.update (a, i, char);
  52. index := i + 1;
  53. ())
  54. else let val aa = C.array (2 * len, #"\000")
  55. fun copy (src, dst) =
  56. let val len = C.length src
  57. fun loop i =
  58. if i = len then ()
  59. else (C.update (dst, i, C.sub (src, i));
  60. loop (i + 1))
  61. in loop 0 end
  62. in copy (a, aa);
  63. C.update (aa, i, char);
  64. array := aa;
  65. index := i + 1;
  66. ()
  67. end
  68. end
  69. fun toString (B {array, index}) =
  70. let val a = !array
  71. val i = !index
  72. in CharVector.tabulate (i, fn i => C.sub (a, i)) end
  73. end
  74. end
  75. structure Sexp = struct
  76. structure Type = struct
  77. datatype sexp = Int of int
  78. | Str of string
  79. | Lst of sexp list
  80. | Sym of string
  81. | QSym of string * string
  82. | T
  83. | Nil
  84. | Quote
  85. end
  86. open Type
  87. exception ReadError
  88. fun fromUtf8 v =
  89. let val len = Word8Vector.length v
  90. val index = ref 0
  91. fun getc () =
  92. case getc' () of
  93. SOME c => c
  94. | NONE => raise ReadError
  95. and getc' () =
  96. let val i = !index
  97. in if i = len then NONE
  98. else (index := i + 1;
  99. SOME (Byte.byteToChar (Word8Vector.sub (v, i))))
  100. end
  101. and ungetc () = index := !index - 1
  102. and sexp () : sexp =
  103. case getc () of
  104. #"\"" => string (CharBuffer.new 100)
  105. | #"(" => lst ()
  106. | #"'" => Lst [Quote, sexp ()]
  107. | _ => (ungetc(); token ())
  108. and string buf : sexp =
  109. case getc () of
  110. #"\"" => Str (CharBuffer.toString buf)
  111. | #"\\" => (CharBuffer.append (buf, getc ()); string buf)
  112. | c => (CharBuffer.append (buf, c); string buf)
  113. and lst () =
  114. let val x = sexp ()
  115. in case getc () of
  116. #")" => Lst [x]
  117. | #" " => let val Lst y = lst () in Lst (x :: y) end
  118. | _ => raise ReadError
  119. end
  120. and token () =
  121. let val tok = token' (CharBuffer.new 50)
  122. val c0 = String.sub (tok, 0)
  123. in if Char.isDigit c0 then (case Int.fromString tok of
  124. SOME i => Int i
  125. | NONE => raise ReadError)
  126. else
  127. Sym (tok)
  128. end
  129. and token' buf : string =
  130. case getc' () of
  131. NONE => CharBuffer.toString buf
  132. | SOME #"\\" => (CharBuffer.append (buf, getc ());
  133. token' buf)
  134. | SOME #" " => (ungetc (); CharBuffer.toString buf)
  135. | SOME #")" => (ungetc (); CharBuffer.toString buf)
  136. | SOME c => (CharBuffer.append (buf, c); token' buf)
  137. in
  138. sexp ()
  139. end
  140. fun toString sexp =
  141. case sexp of
  142. (Str s) => "\"" ^ String.toCString s ^ "\""
  143. | (Lst []) => "nil"
  144. | (Lst xs) => "(" ^ String.concatWith " " (map toString xs) ^ ")"
  145. | Sym (name) => name
  146. | QSym (pkg, name) => pkg ^ ":" ^ name
  147. | Quote => "quote"
  148. | T => "t"
  149. | Nil => "nil"
  150. | Int i => Int.toString i
  151. fun toUtf8 sexp = Util.stringToUtf8 (toString sexp)
  152. end
  153. structure Net = struct
  154. local
  155. structure S = Socket
  156. structure I = INetSock
  157. structure W = Word8Vector
  158. fun createSocket (port) =
  159. let val sock : S.passive I.stream_sock = I.TCP.socket ()
  160. val SOME localhost = NetHostDB.fromString "127.0.0.1"
  161. in
  162. S.Ctl.setREUSEADDR (sock, true);
  163. S.bind (sock, I.toAddr (localhost, port));
  164. S.listen (sock, 2);
  165. sock
  166. end
  167. fun addrToString sockAddr =
  168. let val (ip, port) = I.fromAddr sockAddr
  169. in NetHostDB.toString ip ^ ":" ^ Int.toString port
  170. end
  171. exception ShortRead of W.vector
  172. exception InvalidHexString of string
  173. in
  174. fun acceptConnection port =
  175. let val sock = createSocket port
  176. val addr = S.Ctl.getSockName sock
  177. val _ = print ("Listening on: " ^ addrToString addr ^ "\n")
  178. val (peer, addr) = S.accept sock
  179. in
  180. S.close sock;
  181. print ("Connection from: " ^ addrToString addr ^ "\n");
  182. peer
  183. end
  184. fun receivePacket socket =
  185. let val v = S.recvVec (socket, 6)
  186. val _ = if W.length v = 6 then ()
  187. else raise ShortRead v
  188. val s = Util.utf8ToString v
  189. val _ = print ("s = " ^ s ^ "\n")
  190. val len =
  191. case StringCvt.scanString (Int.scan StringCvt.HEX) s of
  192. SOME len => len
  193. | NONE => raise InvalidHexString s
  194. val _ = print ("len = " ^ Int.toString len ^ "\n")
  195. val payload = S.recvVec (socket, len)
  196. val plen = W.length payload
  197. val _ = print ("plen = " ^ Int.toString plen ^ "\n")
  198. val _ = if plen = len then ()
  199. else raise ShortRead payload
  200. in
  201. payload
  202. end
  203. fun nibbleToHex i:string = Int.fmt StringCvt.HEX i
  204. fun loadNibble i pos =
  205. Word32.toInt (Word32.andb (Word32.>> (Word32.fromInt i,
  206. Word.fromInt (pos * 4)),
  207. 0wxf))
  208. fun hexDigit i pos = nibbleToHex (loadNibble i pos)
  209. fun lenToHex i =
  210. concat [hexDigit i 5,
  211. hexDigit i 4,
  212. hexDigit i 3,
  213. hexDigit i 2,
  214. hexDigit i 1,
  215. hexDigit i 0]
  216. fun sendPacket (payload:W.vector, socket) =
  217. let val len = W.length payload
  218. val header = Util.stringToUtf8 (lenToHex len)
  219. val packet = W.concat [header, payload]
  220. in print ("len = " ^ Int.toString len ^ "\n"
  221. ^ "header = " ^ lenToHex len ^ "\n"
  222. ^ "paylad = " ^ Util.utf8ToString payload ^ "\n");
  223. S.sendVec (socket, {buf = packet, i = 0, sz = NONE})
  224. end
  225. end
  226. end
  227. structure Rpc = struct
  228. open Sexp.Type
  229. val funTable : (string, sexp list -> sexp) Map.map
  230. = Map.stringMap ()
  231. fun define name f = Map.put (funTable, name, f)
  232. exception UnknownFunction of string
  233. fun call (name, args) =
  234. (print ("call: " ^ name ^ "\n");
  235. case Map.lookup (funTable, name) of
  236. SOME f => f args
  237. | NONE => raise UnknownFunction name)
  238. local fun getpid () =
  239. Word32.toInt (Posix.Process.pidToWord (Posix.ProcEnv.getpid ()))
  240. in
  241. fun connectionInfo [] =
  242. Lst [Sym ":pid", Int (getpid ()),
  243. Sym ":lisp-implementation", Lst [Sym ":type", Str "MLWorks",
  244. Sym ":name", Str "mlworks",
  245. Sym ":version", Str "2.x"],
  246. Sym ":machine", Lst [Sym ":instance", Str "",
  247. Sym ":type", Str "",
  248. Sym ":version", Str ""],
  249. Sym ":features", Nil,
  250. Sym ":package", Lst [Sym ":name", Str "root",
  251. Sym ":prompt", Str "-"]]
  252. end
  253. fun nyi _ = Nil
  254. local structure D = Shell.Dynamic
  255. in
  256. fun interactiveEval [Str string] =
  257. let val x = D.eval string
  258. in Str (concat [D.printValue x, " : ", D.printType (D.getType x)])
  259. end
  260. end
  261. val _ =
  262. (define "swank:connection-info" connectionInfo;
  263. define "swank:swank-require" nyi;
  264. define "swank:interactive-eval" interactiveEval;
  265. ())
  266. end
  267. structure EventLoop = struct
  268. open Sexp.Type
  269. fun execute (sexp, pkg) =
  270. (print ("sexp = " ^ (Sexp.toString sexp) ^ "\n");
  271. case sexp of
  272. Lst (Sym name :: args) => Rpc.call (name, args))
  273. fun emacsRex (sexp, pkg, id as Int _, sock) =
  274. let val result = (Lst [Sym (":ok"), execute (sexp, pkg)]
  275. handle exn => (Lst [Sym ":abort",
  276. Str (exnName exn ^ ": "
  277. ^ exnMessage exn)]))
  278. val reply = Lst [Sym ":return", result, id]
  279. in Net.sendPacket (Sexp.toUtf8 reply, sock)
  280. end
  281. fun dispatch (Lst ((Sym key) :: args), sock) =
  282. case key of
  283. ":emacs-rex" => let val [sexp, pkg, _, id] = args
  284. in emacsRex (sexp, pkg, id, sock)
  285. end
  286. fun processRequests socket:unit =
  287. let val sexp = Sexp.fromUtf8 (Net.receivePacket socket)
  288. in print ("request: "
  289. ^ Util.utf8ToString (Sexp.toUtf8 sexp)
  290. ^ "\n");
  291. dispatch (sexp, socket);
  292. processRequests socket
  293. end
  294. end
  295. (* val _ = EventLoop.processRequests (Net.acceptConnection 4005) *)
  296. val _ = ()
  297. end
  298. (* (Swank.EventLoop.processRequests (Swank.Net.acceptConnection 4005)) *)