|
|
1.1 root 1: (* Copyright 1989 by AT&T Bell Laboratories *)
2: (* importer.sml 608567950 46 20 100444 17742 `*)
3: (* Importer: Isolation of Mads' original code from Interact() into a separate
4: functor. Numerous extensions, Make system, etc. etc. (NICK) *)
5:
6: functor Importer(structure FilePaths: FILEPATHS
7: val fileExtension: string
8: structure ModuleComp: MODULE_COMPILER
9: sharing ModuleComp.Lambda = Lambda
10: and ModuleComp.Absyn = BareAbsyn
11: and type ModuleComp.lvar = Access.lvar)
12: : IMPORTER =
13: struct
14:
15: open PrintUtil Access Basics Stampset Env
16:
17: val gcmessages = System.Control.Runtime.gcmessages
18: (* The message "Major collection... abandoned" is
19: annoying me, so I'm hosing it. NICK *)
20:
21: fun all pred list =
22: fold (fn (this, res) => pred this andalso res) list true
23:
24: val DEBUG = false
25: val debug =
26: if DEBUG then fn str => output std_out ("<" ^ str ^ ">\n")
27: else fn _ => ()
28:
29: val TRACE_IO = false
30: val open_in =
31: if TRACE_IO
32: then fn name => (debug("open_in \\" ^ name ^ "\\"); open_in name)
33: else open_in
34:
35: val open_out =
36: if TRACE_IO
37: then (fn name => (debug("open_out \\" ^ name ^ "\\"); open_out name))
38: else open_out
39:
40: exception Import of string
41: (* A single exception for any failure to
42: import (barring compiler bugs). compSource
43: requires a protective coating so that it
44: doesn't leave the global static environment
45: in a funny state. *)
46:
47:
48: (* Feedback messages. If anybody's interested, files which may
49: cause failures, or may cause nested reads, are done as:
50:
51: [reading fred.sml]
52: [closing fred.sml]
53:
54: Ones which shouldn't (eg. reading from a binary) produce:
55:
56: [reading fred.bin... done]
57: *)
58:
59: fun reading(file, indent) =
60: (tab indent; print("[reading " ^ file ^ "]\n"))
61: fun reading1(file, indent) =
62: (tab indent; print("[reading " ^ file ^ "... "); flush_out std_out)
63: fun writing(file, indent) =
64: (tab indent; print("[writing " ^ file ^ "]\n"))
65: fun writing1(file, indent) =
66: (tab indent; print("[writing " ^ file ^ "... "); flush_out std_out)
67: fun closing(file, indent) =
68: (tab indent; print("[closing " ^ file ^ "]\n"))
69: fun done() = print "done]\n"
70:
71: fun fail(msg, verdict) =
72: (print("import: " ^ msg ^ "\n"); raise Import verdict)
73:
74: (* impliedPath: derived from FilePaths.impliedPath, but catches
75: ImpliedPath if a "~"-filename fails to translate. *)
76:
77: fun impliedPath(oldPath, oldName) =
78: FilePaths.impliedPath(oldPath, oldName)
79: handle FilePaths.ImpliedPath =>
80: fail("couldn't translate path in: " ^ oldName, "open")
81:
82: type LambDynModule = ModuleComp.LambDynModule
83: type CodeDynModule = ModuleComp.CodeDynModule
84: type lvar = Access.lvar
85:
86: datatype ToplevelFns =
87: TOPLEVEL_FNS of
88: {bind: lvar * System.Unsafe.object -> unit,
89: lookup: lvar -> System.Unsafe.object,
90: parse: Lex.lexer -> BareAbsyn.dec,
91: getvars: BareAbsyn.dec -> lvar list,
92: opt: Lambda.lexp -> Lambda.lexp}
93:
94: datatype statModule =
95: STATmodule of {table: symtable, lvars: Basics.Access.lvar list}
96:
97:
98: (* Rename the lvars, and shift the stamps, of the static module.
99: Only signature and functor bindings are accepted.
100: For each functor binding, a fresh lvar will be chosen; hence
101: at run-time, several imports of the same functor will presumably
102: lead to a new copy of the code of that functor *)
103:
104: fun importModule(STATmodule{table,lvars}) : lvar list =
105: let val newlvars = map (fn _ => mkLvar()) lvars
106: fun lookup x =
107: let fun f(a::ar, b::br) = if a=x then b else f(ar,br)
108: | f _ = ErrorMsg.impossible "importModule 1"
109: in f(lvars,newlvars)
110: end
111: fun renBinding(SIGbind(SIGvar{name,binding})) =
112: SIGbind(SIGvar
113: {name=name,
114: binding=ModUtil.shiftSigStamps(newStampsets(),binding)})
115: | renBinding(FCTbind(FCTvar{name,access=LVAR lvar, binding})) =
116: FCTbind(FCTvar{name = name, access= LVAR(lookup lvar),
117: binding = ModUtil.shiftFctStamps binding})
118: | renBinding _ = ErrorMsg.impossible "importModule 2"
119: in IntStrMap.app (fn (i,s,b) => add(i,s,renBinding b)) table;
120: newlvars
121: end
122:
123: (* New code (NICK) - I store the static information (StatModule) and
124: dynamic information (CodeDynModule) in one object, so that I can blast
125: out the entire thing as a single object into a file. Foo.sml now gets
126: compiled into Foo.vax/Foo.m68/..., which contains
127: both. The object stored in the file is a pair: the first element is a
128: "version number" for the data structures, the second is whatever needs
129: storing (currently a record of {statModule, dynModule, imports}).
130: If this version number changes, I have to recompile. *)
131:
132: type BinFormat = {statModule: statModule,
133: dynModule: CodeDynModule,
134: imports: string list}
135:
136: val blastRead: instream -> BinFormat = System.Unsafe.blast_read
137: val blastWrite: (outstream * BinFormat) -> unit = System.Unsafe.blast_write
138:
139: val blastWrite = (* Silent version. *)
140: fn (stream, obj) =>
141: let val oldmsgs = !gcmessages
142: in gcmessages := 0;
143: blastWrite(stream, obj);
144: gcmessages := oldmsgs
145: end
146:
147: val BIN_VERSION = System.version ^ " - LAMBDA v0 " ^ fileExtension^ "\n"
148: (* This is stored as the first line of the
149: binary file. Be sure to increment it whenever the structure
150: of any of the stored data objects changes. It cannot
151: contain any \n characters, except at the end where
152: one is required. *)
153:
154: fun tryOpenIn filename: instream option =
155: SOME(open_in filename) handle Io _ => NONE
156:
157: fun createBinary(indent, filename,
158: statModule: statModule,
159: dynModule: CodeDynModule,
160: imports: string list) : unit =
161: let val fullName = filename ^ ".bin"
162: val outstream =
163: open_out fullName
164: handle Io _ => fail("couldn't open " ^ fullName ^ " for output",
165: "open")
166: in writing1(fullName, indent);
167: output outstream BIN_VERSION;
168: blastWrite(outstream,
169: {statModule=statModule,
170: dynModule=dynModule,
171: imports=imports});
172: close_out outstream;
173: done()
174: end
175:
176: val statPrinter: statModule -> string = (fn _ => "")
177: (* this definition causes createTextual to have no effect *)
178:
179: fun createTextual(indent, filename, statModule): unit =
180: case statPrinter statModule (* currently always "" *)
181: of "" => () (* Do NOTHING if the print function is a dummy *)
182: | text =>
183: let val fullName = filename ^ ".lstat"
184: val outstream =
185: open_out fullName
186: handle Io _ =>
187: fail("couldn't open " ^ fullName ^ " for output", "open")
188: in writing1(fullName, indent);
189: output outstream text;
190: close_out outstream;
191: done()
192: end
193:
194: (* We must do a syntactic check that the source declarations in a module
195: are just functor and signature declarations (or sequences thereof),
196: otherwise the renaming routines will fall over later. Importer is the
197: place to do it, where we still have a fighting chance of a putting
198: out a decent diagnostic message. We don't allow IMPORT - that should
199: have been dealt with earlier. *)
200:
201: fun kosherModuleDecl dec =
202: case dec
203: of BareAbsyn.FCTdec _ => true
204: | BareAbsyn.SIGdec _ => true
205: | BareAbsyn.SEQdec decs => (* ALL must be kosher. *)
206: all kosherModuleDecl decs
207: | _ => false
208:
209: fun badModuleDecl() = ErrorMsg.condemn "expecting SIGNATURE/FUNCTOR/IMPORT"
210:
211: (* uptodate should be memo'd sometime, since it's quite expensive. *)
212: fun uptodate (path, myBinTime) name =
213: let val {newPath, validName} = impliedPath(path, name)
214: val _ = debug("uptodate(quotedName=" ^ name
215: ^ ", validName=" ^ name ^ ")?")
216: val trySml = tryOpenIn(validName ^ ".sml")
217: val tryBin = tryOpenIn(validName ^ ".bin")
218: in case (trySml, tryBin)
219: of (SOME source, SOME binary) =>
220: let val srcTime = mtime source
221: val binTime = mtime binary
222: val _ = debug("uptodate(" ^ validName ^ "):\
223: \ src time = " ^ makestring srcTime
224: ^ ", bin time = " ^ makestring binTime)
225: in if srcTime >= binTime (* binary out of date *)
226: orelse binTime >= myBinTime
227: (* Some other branch of the Make
228: task compiled this under me...? *)
229: then (close_in source; close_in binary; false)
230: else (* source is older; check imports *)
231: let val _ = close_in source
232: val fullName = validName ^ ".bin"
233: val binVersion = input_line binary
234: in if binVersion <> BIN_VERSION
235: then (close_in binary; false)
236: (* can't trust "imports" : chicken out *)
237: else let val {imports, ...} =
238: blastRead binary before close_in binary
239: in all (uptodate (newPath, myBinTime)) imports
240: end
241: end
242: end
243:
244: | (SOME source, NONE) => (* No bin: force recompile *)
245: (close_in source; false)
246:
247: | (NONE, SOME binary) => (* No source: trust for now... *)
248: (close_in binary; true)
249:
250: | (NONE, NONE) =>
251: fail("cannot find source or binary\
252: \ of required module " ^ validName,
253: "open")
254: end (* uptodate *)
255:
256: fun getModule(name,pervasives,TOPLEVEL_FNS{bind,lookup,parse,getvars,opt})
257: : statModule * CodeDynModule =
258: let fun getModule'(parents, indent, path, name) =
259: (* "parents" is a depth-first list of filenames used for
260: a circularity check. "indent" is for cosmetic purposes. *)
261: let val {validName as filename, newPath as path} = impliedPath(path, name)
262: val _ = if exists (fn x => x = filename) parents
263: then fail("self-referential import of " ^ validName, "open")
264: else ()
265: val parents = filename :: parents
266: val _ = debug("getModule'(name=" ^ name ^ ")")
267:
268: fun compSource0(source: instream) : statModule * CodeDynModule =
269: let val lex = Lex.mkLex{stream=source,interactive=false}
270: fun loop(dynModule, lvars, imports)
271: : LambDynModule * lvar list * string list =
272: (case parse lex (* (Lex.toplevel := true; parse()) *)
273: of BareAbsyn.IMPORTdec names =>
274: let fun loop'([], dynMod, lvars, imports) =
275: (dynMod, lvars, imports)
276: | loop'(name::rest, dynMod, lvars, imports)=
277: let val {newPath, ...} = impliedPath(path, name)
278: val (stat, codeDyn) =
279: getModule'(parents, indent+2,
280: newPath, name)
281: val newLvars = importModule stat
282: val lambDyn = ModuleComp.abstractDynModule
283: (codeDyn, newLvars)
284: val dynMod' = ModuleComp.importDynModule
285: (lambDyn, dynMod)
286: in loop'(rest, dynMod', lvars @ newLvars,
287: name :: imports)
288: end
289: in loop(loop'(names, dynModule, lvars, imports))
290: end
291:
292: | absyn => (* normal program *)
293: if kosherModuleDecl absyn
294: then let val newLvars = getvars absyn
295: val newMod = ModuleComp.addDeclaration
296: (absyn, newLvars, dynModule)
297: handle ModuleComp.AddDeclaration =>
298: fail("error during translate",
299: "translate")
300: in loop(newMod, lvars @ newLvars, imports)
301: end
302: else badModuleDecl())
303:
304: handle Parse.Eof => (dynModule, lvars, imports)
305: | Import x => raise Import x
306: (* Resignal nested Import probs. *)
307: | Io x => raise Import("unexpected: Io(" ^ x ^ ")")
308: | exn => raise Import("compile-time exception: "
309: ^ System.exn_name exn)
310:
311: val (lambDynModule, lvars, imports) =
312: loop(ModuleComp.emptyDynModule, [], [])
313: val statModule= STATmodule{table=Env.popModule(pervasives),
314: lvars=lvars}
315: val dynModule = ModuleComp.compileDynModule opt lambDynModule
316: handle ModuleComp.CompileDynModule =>
317: fail("code generation failed", "codegen")
318: in createBinary(indent, filename, statModule,
319: dynModule, imports)
320: handle Import _ => (); (* make failed writes nonfatal... *)
321: createTextual(indent, filename, statModule) (* no-op *)
322: handle Import _ => ();
323: (statModule, dynModule)
324: end (* fun compSource *)
325:
326: fun compSource(source) =
327: let val _ = debug(filename ^ ": source only")
328: val fullName = filename ^ ".sml"
329: val _ = reading(fullName, indent)
330:
331: val oldfile = !ErrorMsg.fileName
332: val oldlinenum = !ErrorMsg.lineNum
333: val oldinteractive = !System.interactive
334: val savedEnv = Env.current()
335: fun cleanup () =
336: (closing(fullName, indent);
337: close_in source;
338: ErrorMsg.fileName := oldfile;
339: ErrorMsg.lineNum := oldlinenum;
340: System.interactive := oldinteractive;
341: Env.resetEnv savedEnv)
342: in ErrorMsg.fileName := fullName;
343: ErrorMsg.lineNum := 1;
344: System.interactive := false;
345: Env.resetEnv pervasives;
346: (compSource0(source) before cleanup())
347: handle exp => (cleanup(); raise exp)
348: end
349:
350: in case (tryOpenIn(filename ^ ".sml"), tryOpenIn(filename ^ ".bin"))
351: of (SOME source, NONE) => (* Source only: Compile! *)
352: compSource(source)
353: | (SOME source, SOME binary) =>
354: let val srcTime = mtime source
355: val binTime = mtime binary
356: val _ = debug(filename ^ ": src dated " ^ makestring srcTime
357: ^ ", bin dated " ^ makestring binTime)
358: in if srcTime >= binTime (* (">=" for safety) *)
359: then (* binary out of date? *)
360: (tab indent;
361: print("[" ^ filename ^ ".bin is out of date;\
362: \ recompiling]\n");
363: close_in binary;
364: compSource(source))
365: else (* bin is newer: what about the things imported? *)
366: let val _ = debug(filename ^ ": checking imports")
367: val fullName = filename ^ ".bin"
368: val _ = reading1(fullName, indent)
369: val binVersion = input_line binary
370: in if (binVersion <> BIN_VERSION)
371: then (print "]\n";
372: tab indent;
373: print("[" ^ fullName ^ " is the wrong format;\
374: \ recompiling]\n");
375: closing(fullName, indent);
376: close_in binary;
377: compSource(source))
378: else let val {statModule, dynModule, imports} =
379: blastRead binary
380: fun allOk imports =
381: all (uptodate (path, binTime)) imports
382: handle exn =>
383: (print "]\n";
384: closing(fullName, indent);
385: close_in binary;
386: close_in source;
387: raise exn)
388: in if not(allOk imports)
389: then (print "]\n";
390: tab indent;
391: print("[import(s) of " ^ filename
392: ^ " are out of date; recompiling]\n");
393: closing(fullName, indent);
394: close_in binary;
395: compSource(source))
396: else (* All OK: use the binary. *)
397: (debug(filename ^ ": all up to date");
398: close_in source;
399: close_in binary;
400: done();
401: (statModule, dynModule))
402: end
403: end
404: end
405:
406: | (NONE, SOME binary) =>
407: let val _ = debug(filename ^ ": binary only")
408: val fullName = filename ^ ".bin"
409: val _ = reading1(fullName, indent)
410: val binVersion = input_line binary
411: in if binVersion = BIN_VERSION
412: then let val {statModule, dynModule, ...} = blastRead binary
413: in close_in binary;
414: done();
415: (statModule, dynModule)
416: end
417: else (print "]\n"; (* Outstanding message... *)
418: tab indent;
419: print("[" ^ fullName ^ " is the wrong format;\
420: \ recompiling]\n");
421: closing(fullName, indent);
422: close_in binary;
423: case tryOpenIn(filename ^ ".sml")
424: of SOME source =>
425: compSource(source)
426: | NONE =>
427: fail(fullName ^ " is out of date, and can't\
428: \ open " ^ filename ^ ".sml",
429: "open"))
430: end
431:
432: | (NONE, NONE) => fail("cannot open " ^ filename ^ ".sml", "open")
433: end (* getModule' *)
434: in getModule'([],0,FilePaths.defaultPath,name)
435: end (* getModule *)
436:
437: fun getAndExecModule(filename, pervasives,
438: toplevelFns as TOPLEVEL_FNS{bind, lookup, ...}) : unit =
439: let val (statModule as STATmodule{table, ...}, compDynModule) =
440: getModule(filename, pervasives, toplevelFns)
441: in let val newlvars = importModule statModule
442: (* adds the static bindings of the module to the
443: static environment*)
444:
445: val result =
446: ModuleComp.executeDynModule compDynModule lookup
447: handle exn => (* Local handle for module execution (NICK). *)
448: fail("execution of module raised exception "
449: ^ System.exn_name exn
450: ^ "\n\t(static bindings of module take no effect)\n",
451: "uncaught exception")
452:
453: fun bindlvars (i,v::r) =
454: (bind(v,result sub i);
455: bindlvars (i+1,r))
456: | bindlvars (_,nil) = ()
457:
458: in bindlvars(0,newlvars); (* add new runtime bindings *)
459: Env.commit(); (* accept static bindings *)
460: PrintDec.printBindingTbl table
461: end
462: handle (* Exceptions other than ones raised through module execution. *)
463: Interrupt => raise Interrupt
464: | exn => ErrorMsg.impossible("addAndExecModule: exn ("
465: ^ System.exn_name exn ^ ")??")
466: end
467:
468: end (* functor Importer *)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.