001: // Copyright (C) 2005, 2006 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ../../COPYING.
003:
004: package kawa.standard;
005:
006: import kawa.lang.*;
007: import gnu.mapping.*;
008: import gnu.mapping.Location; // As opposed to gnu.bytecode.Location
009: import gnu.lists.*;
010: import gnu.bytecode.*;
011: import gnu.expr.*;
012: import gnu.kawa.reflect.*;
013: import gnu.kawa.functions.Convert;
014: import gnu.text.*;
015: import java.util.*;
016:
017: public class require extends Syntax {
018: public static final require require = new require();
019: static {
020: require.setName("require");
021: }
022:
023: /* NOTE on handling mutually recursive modules:
024:
025: How can Kawa compile two or more modules that mutually require
026: each other? Kawa separates the "scan" stage (top-level
027: scanning of a module, looking for definitions), and "rewrite"
028: (expand macros and resolve names) makes this possible.
029:
030: If module A sees a (require <B>), it needs to suspend scanning A,
031: and import the definitions exported by B. If B has not been
032: compiled yet, it must parse and scan B. If while scanning B, it
033: sees a (require <A>), it must wait to import the definitions of A
034: until we've done scanning B, returned to A, and finished scanning
035: A. At that point we can add to B the definitions exported from
036: A. Thus the (require <A>) in B.has to *lazily* imports A's
037: definitions, using some kind of placeholder.
038:
039: One complication is knowing whether a (require <B>) refers to a
040: source file to be compiled. It is not enough to check if a class
041: B exists, since if we're compiljng B we want to use the current
042: source B.scm, not an older B.class. This is complicated by the
043: (module-name B) declaration: We don't know whether source file
044: B.scm provides the B class until we've parsed B.scm. A solution
045: to this problem is that we first parse all the source files (as
046: listed on the command line),
047: yielding their S-expression form. We then check for module-name
048: forms. However, the possibility of macros does complicate this:
049: There could be a macro that re-defines module-name, and there
050: could be a macro that expands to module-name. Also, we could
051: have commands that change the reader or read-table. Arguably worrying
052: about these possibilities may be overkill. However, it can be
053: handled thus: Parse each source file to S-expressions. Scan each
054: source file's S-expression until the first require (if any).
055: Then go back to each source file, process the require, and scan
056: the rest of the file. If we see a require for one of the source
057: files later in the compilation list, skip it until the end. At
058: the end process any deferred require's. Finally, do the
059: "rewrite" step and the rest of compilation.
060: */
061: static java.util.Hashtable featureMap = new java.util.Hashtable();
062:
063: static void map(String featureName, String className) {
064: featureMap.put(featureName, className);
065: }
066:
067: private static final String SLIB_PREFIX = "gnu.kawa.slib.";
068:
069: static {
070: map("generic-write", SLIB_PREFIX + "genwrite");
071: map("pretty-print", SLIB_PREFIX + "pp");
072: map("pprint-file", SLIB_PREFIX + "ppfile");
073: map("printf", SLIB_PREFIX + "printf");
074: map("xml", SLIB_PREFIX + "XML");
075: map("readtable", SLIB_PREFIX + "readtable");
076: map("srfi-10", SLIB_PREFIX + "readtable");
077: map("http", "gnu.kawa.servlet.HTTP");
078: map("srfi-1", SLIB_PREFIX + "srfi1");
079: map("list-lib", SLIB_PREFIX + "srfi1");
080: map("srfi-34", SLIB_PREFIX + "srfi34");
081: map("srfi-35", SLIB_PREFIX + "conditions");
082: map("condition", SLIB_PREFIX + "conditions");
083: map("conditions", SLIB_PREFIX + "conditions");
084: map("srfi-37", SLIB_PREFIX + "srfi37");
085: map("args-fold", SLIB_PREFIX + "srfi37");
086: map("srfi-64", SLIB_PREFIX + "testing");
087: map("testing", SLIB_PREFIX + "testing");
088: map("srfi-69", SLIB_PREFIX + "srfi69");
089: map("hash-table", SLIB_PREFIX + "srfi69");
090: map("gui", SLIB_PREFIX + "gui");
091: map("swing-gui", SLIB_PREFIX + "swing");
092: }
093:
094: public static String mapFeature(String featureName) {
095: return (String) featureMap.get(featureName);
096: }
097:
098: public static Object find(String typeName) {
099: return ModuleInfo.find(typeName).getInstance();
100: }
101:
102: public boolean scanForDefinitions(Pair st, Vector forms,
103: ScopeExp defs, Translator tr) {
104: if (tr.getState() == Compilation.PROLOG_PARSING) {
105: tr.setState(Compilation.PROLOG_PARSED);
106: tr.pendingForm = st;
107: // FIXME - we want to call 'run' here anyway, rather than have
108: // it be emitted at the end of the 'body'.
109: return true;
110: }
111: Pair args = (Pair) st.cdr;
112: Object name = args.car;
113: Type type = null;
114: Pair p;
115: if (name instanceof Pair
116: && tr.matches((p = (Pair) name).car, Scheme.quote_sym)) {
117: name = p.cdr;
118: if (!(name instanceof Pair)
119: || (p = (Pair) name).cdr != LList.Empty
120: || !(p.car instanceof String)) {
121: tr.error('e', "invalid quoted symbol for 'require'");
122: return false;
123: }
124: name = mapFeature((String) p.car);
125: if (name == null) {
126: tr.error('e', "unknown feature name '" + p.car
127: + "' for 'require'");
128: return false;
129: }
130: type = ClassType.make((String) name);
131: } else if (name instanceof FString) {
132: String sourceName = name.toString();
133: ModuleInfo info = lookupModuleFromSourcePath(sourceName,
134: defs);
135: if (info == null) {
136: tr.error('e', "malformed URL: " + sourceName);
137: return false;
138: }
139: return importDefinitions(null, info, null, forms, defs, tr);
140: } else {
141: if (name instanceof String) {
142: String str = (String) name;
143: int len = str.length();
144: if (len > 2 && str.charAt(0) == '<'
145: && str.charAt(len - 1) == '>') {
146: str = str.substring(1, len - 1);
147: if (str.indexOf('.') < 0)
148: str = tr.classPrefix + str;
149: if (args.cdr instanceof Pair
150: && ((Pair) args.cdr).car instanceof FString) {
151: String sourceName = ((Pair) args.cdr).car
152: .toString();
153: ModuleInfo info = lookupModuleFromSourcePath(
154: sourceName, defs);
155: if (info == null) {
156: tr.error('e', "malformed URL: "
157: + sourceName);
158: return false;
159: }
160: return importDefinitions(str, info, null,
161: forms, defs, tr);
162: }
163: type = Scheme.string2Type(str);
164: }
165: }
166: }
167: if (type == null) {
168: tr.error('e', "invalid specifier for 'require'");
169: return false;
170: }
171: importDefinitions(null, ModuleInfo.find(type), null, forms,
172: defs, tr);
173: return true;
174: }
175:
176: public static ModuleInfo lookupModuleFromSourcePath(
177: String sourceName, ScopeExp defs) {
178: ModuleManager manager = ModuleManager.getInstance();
179: String baseName = defs.getFileName();
180: if (baseName != null)
181: sourceName = Path.valueOf(baseName).resolve(sourceName)
182: .toString();
183: return manager.findWithSourcePath(sourceName);
184: }
185:
186: /** Import a module with a known source path.
187: * @param className Optional fully-qualified name of module's class,
188: * or null if unknown.
189: */
190: public static boolean importDefinitions(String className,
191: ModuleInfo info, String uri, Vector forms, ScopeExp defs,
192: Compilation tr) {
193: ModuleManager manager = ModuleManager.getInstance();
194: String sourceName = info.sourcePath;
195: long now;
196: if ((info.getState() & 1) == 0
197: && sourceName != null
198: && !info.checkCurrent(manager, (now = System
199: .currentTimeMillis()))) {
200: SourceMessages messages = tr.getMessages();
201: Language language = Language.getDefaultLanguage();
202: Compilation comp;
203: try {
204: InPort fstream = InPort.openFile(info
205: .getSourceAbsPath());
206: info.clearClass();
207: info.className = className;
208: comp = language.parse(fstream, messages, info);
209: comp.immediate = tr.immediate;
210: } catch (java.io.FileNotFoundException ex) {
211: tr.error('e', "not found: " + ex.getMessage());
212: return false;
213: } catch (java.io.IOException ex) {
214: tr.error('e', "caught " + ex);
215: return false;
216: } catch (SyntaxException ex) {
217: if (ex.getMessages() != messages)
218: throw new RuntimeException(
219: "confussing syntax error: " + ex);
220: // otherwise ignore it - it's already been recorded in messages.
221: return false;
222: }
223: ModuleExp mexp = comp.getModule();
224: ClassType ctype = mexp.classFor(comp);
225: info.className = ctype.getName();
226: }
227:
228: if (tr.minfo != null && tr.getState() < Compilation.BODY_PARSED) {
229: tr.minfo.addDependency(info);
230:
231: if (!info.loadEager(Compilation.COMPILED)
232: && info.getState() < Compilation.RESOLVED) {
233: // Oops. We found a cycle.
234: tr.pushPendingImport(info, defs);
235: return true;
236: }
237: }
238:
239: ClassType type = info.getClassType();
240: String tname = info.className;
241: boolean immediate = tr.immediate && defs instanceof ModuleExp;
242: boolean isRunnable = info.getState() < Compilation.RESOLVED
243: || type.isSubtype(Compilation.typeRunnable);
244: Declaration decl = null;
245: ClassType this Type = ClassType.make("kawa.standard.require");
246: Expression[] args = { new QuoteExp(tname) };
247: Expression dofind = Invoke.makeInvokeStatic(this Type, "find",
248: args);
249: Field instanceField = null;
250: Language language = tr.getLanguage();
251: dofind.setLine(tr);
252: int formsStart = forms.size();
253:
254: ModuleExp mod = info.setupModuleExp();
255:
256: Vector declPairs = new Vector();
257: for (Declaration fdecl = mod.firstDecl(); fdecl != null; fdecl = fdecl
258: .nextDecl()) {
259: Object fdname = fdecl.getSymbol();
260: boolean isStatic = fdecl
261: .getFlag(Declaration.STATIC_SPECIFIED);
262: if (!isStatic && decl == null) {
263: String iname = tname.replace('.', '$') + "$instance";
264: decl = new Declaration(iname.intern(), type);
265: if (!immediate)
266: decl.setPrivate(true);
267: decl.setFlag(Declaration.IS_CONSTANT
268: | Declaration.MODULE_REFERENCE);
269: defs.addDeclaration(decl);
270:
271: decl.noteValue(dofind);
272: SetExp sexp = new SetExp(decl, dofind);
273: sexp.setLine(tr);
274: sexp.setDefining(true);
275: forms.addElement(sexp);
276: formsStart = forms.size();
277: decl.setFlag(Declaration.EARLY_INIT);
278: // If Runnable, we need to set decl value in initializer,
279: // and later 'run' it, so it needs to be stored in a field.
280: if (isRunnable)
281: decl.setSimple(false);
282:
283: decl.setFlag(Declaration.TYPE_SPECIFIED);
284: }
285:
286: if (fdecl.isPrivate())
287: continue;
288:
289: if (fdecl.field != null) {
290: String fname = fdecl.field.getName();
291: if (fname.equals("$instance")) {
292: instanceField = fdecl.field;
293: continue;
294: }
295: }
296:
297: // We create an alias in the current context that points
298: // a dummy declaration in the exported module. Normally,
299: // followAliases will skip the alias, so we use the latter.
300: // But if the binding is re-exported (or EXTERNAL_ACCESS
301: // gets set), then we need a separate declaration.
302: // (If EXTERNAL_ACCESS, the field gets PRIVATE_PREFIX.)
303: Object aname;
304:
305: if (fdname instanceof Symbol)
306: aname = fdname;
307: else {
308: String sname = fdname.toString();
309: if (uri == null)
310: aname = sname.intern();
311: else
312: aname = Symbol.make(uri, sname);
313: }
314: boolean isImportedInstance = fdecl.field != null
315: && fdecl.field.getName().endsWith("$instance");
316:
317: Declaration adecl;
318: Declaration old = defs.lookup(aname, language, language
319: .getNamespaceOf(fdecl));
320: if (isImportedInstance) {
321: if (old != null)
322: continue;
323: adecl = defs.addDeclaration(aname);
324: adecl.setFlag(Declaration.IS_CONSTANT
325: | Declaration.MODULE_REFERENCE);
326: adecl.setType(fdecl.getType());
327: adecl.setFlag(Declaration.TYPE_SPECIFIED);
328: } else if (old != null
329: && !old.getFlag(Declaration.NOT_DEFINING)
330: && (Declaration.followAliases(old) == Declaration
331: .followAliases(fdecl)))
332: continue;
333: else {
334: if (old != null
335: && (old.getFlag(Declaration.NOT_DEFINING
336: | Declaration.IS_UNKNOWN))) {
337: old.setFlag(false, Declaration.NOT_DEFINING
338: | Declaration.IS_UNKNOWN);
339: adecl = old;
340: } else {
341: adecl = defs.addDeclaration(aname);
342: if (old != null)
343: ScopeExp.duplicateDeclarationError(old, adecl,
344: tr);
345: }
346: adecl.setAlias(true);
347: adecl.setIndirectBinding(true);
348: }
349: adecl.setLocation(tr);
350: ReferenceExp fref = new ReferenceExp(fdecl);
351: fref.setContextDecl(decl);
352: if (!isImportedInstance) {
353: fref.setDontDereference(true);
354: fref.setFlag(ReferenceExp.CREATE_FIELD_REFERENCE);
355: if (!immediate)
356: adecl.setPrivate(true);
357: }
358: if (fdecl.getFlag(Declaration.IS_CONSTANT))
359: adecl.setFlag(Declaration.IS_CONSTANT);
360: if (fdecl.getFlag(Declaration.IS_SYNTAX))
361: adecl.setFlag(Declaration.IS_SYNTAX);
362: if (fdecl.isProcedureDecl())
363: adecl.setProcedureDecl(true);
364: if (isStatic)
365: adecl.setFlag(Declaration.STATIC_SPECIFIED);
366:
367: SetExp sexp = new SetExp(adecl, fref);
368: adecl.setFlag(Declaration.EARLY_INIT);
369: sexp.setDefining(true);
370: if (isImportedInstance) {
371: // Make sure the "MODULE$instance" declarations are
372: // initialized first, since we may need then for
373: // imported declarations that are re-exported. (The
374: // instance may be needed for FieldLocation values.)
375: forms.insertElementAt(sexp, formsStart);
376: formsStart++;
377: } else
378: forms.addElement(sexp);
379:
380: declPairs.add(adecl);
381: declPairs.add(fdecl);
382:
383: adecl.noteValue(fref);
384: adecl.setFlag(Declaration.IS_IMPORTED);
385: tr.push(adecl); // Add to translation env.
386: }
387:
388: // This needs to be a second pass, because a Declaration might need to
389: // look for a context MOD$instance that is provided by a following field.
390: int ndecls = declPairs.size();
391: for (int i = 0; i < ndecls; i += 2) {
392: Declaration adecl = (Declaration) declPairs.elementAt(i);
393: Declaration fdecl = (Declaration) declPairs
394: .elementAt(i + 1);
395: Expression fval = fdecl.getValue();
396: if (fdecl.isIndirectBinding()
397: && fval instanceof ReferenceExp) {
398: ReferenceExp aref = (ReferenceExp) adecl.getValue();
399: Declaration xdecl = ((ReferenceExp) fval).getBinding();
400: aref.setBinding(xdecl);
401: if (xdecl.needsContext()) {
402: String iname = (xdecl.field.getDeclaringClass()
403: .getName().replace('.', '$') + "$instance");
404: Declaration cdecl = defs.lookup(iname.intern());
405: cdecl.setFlag(Declaration.EXPORT_SPECIFIED);
406: aref.setContextDecl(cdecl);
407: }
408: }
409: }
410:
411: if (isRunnable) {
412: Method run = Compilation.typeRunnable.getDeclaredMethod(
413: "run", 0);
414: if (decl != null) // Need to make sure 'run' is invoked.
415: dofind = new ReferenceExp(decl);
416: else {
417: if (instanceField != null) { //Optimization
418: args = new Expression[] { new QuoteExp(type),
419: new QuoteExp("$instance") };
420: dofind = new ApplyExp(SlotGet.staticField, args);
421: }
422: }
423: dofind = new ApplyExp(run, new Expression[] { dofind });
424: dofind.setLine(tr);
425: forms.addElement(dofind);
426: }
427: tr.mustCompileHere();
428: return true;
429: }
430:
431: public Expression rewriteForm(Pair form, Translator tr) {
432: return null;
433: }
434: }
|