KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > kawa > standard > require


1 // Copyright (C) 2005, 2006 Per M.A. Bothner.
2
// This is free software; for terms and warranty disclaimer see ../../COPYING.
3

4 package kawa.standard;
5 import kawa.lang.*;
6 import gnu.mapping.*;
7 import gnu.mapping.Location; // As opposed to gnu.bytecode.Location
8
import gnu.lists.*;
9 import gnu.bytecode.*;
10 import gnu.expr.*;
11 import gnu.kawa.reflect.*;
12 import gnu.kawa.functions.Convert;
13 import gnu.text.*;
14 import java.util.*;
15
16 public class require extends Syntax
17 {
18   public static final require require = new require();
19   static { require.setName("require"); }
20
21   /* NOTE on handling mutually recursive modules:
22
23      How can Kawa compile two or more modules that mutually require
24      each other? Kawa separates the "scan" stage (top-level
25      scanning of a module, looking for definitions), and "rewrite"
26      (expand macros and resolve names) makes this possible.
27
28      If module A sees a (require <B>), it needs to suspend scanning A,
29      and import the definitions exported by B. If B has not been
30      compiled yet, it must parse and scan B. If while scanning B, it
31      sees a (require <A>), it must wait to import the definitions of A
32      until we've done scanning B, returned to A, and finished scanning
33      A. At that point we can add to B the definitions exported from
34      A. Thus the (require <A>) in B.has to *lazily* imports A's
35      definitions, using some kind of placeholder.
36
37      One complication is knowing whether a (require <B>) refers to a
38      source file to be compiled. It is not enough to check if a class
39      B exists, since if we're compiljng B we want to use the current
40      source B.scm, not an older B.class. This is complicated by the
41      (module-name B) declaration: We don't know whether source file
42      B.scm provides the B class until we've parsed B.scm. A solution
43      to this problem is that we first parse all the source files (as
44      listed on the command line),
45      yielding their S-expression form. We then check for module-name
46      forms. However, the possibility of macros does complicate this:
47      There could be a macro that re-defines module-name, and there
48      could be a macro that expands to module-name. Also, we could
49      have commands that change the reader or read-table. Arguably worrying
50      about these possibilities may be overkill. However, it can be
51      handled thus: Parse each source file to S-expressions. Scan each
52      source file's S-expression until the first require (if any).
53      Then go back to each source file, process the require, and scan
54      the rest of the file. If we see a require for one of the source
55      files later in the compilation list, skip it until the end. At
56      the end process any deferred require's. Finally, do the
57      "rewrite" step and the rest of compilation.
58    */

59   static java.util.Hashtable JavaDoc featureMap = new java.util.Hashtable JavaDoc();
60
61   static void map(String JavaDoc featureName, String JavaDoc className)
62   {
63     featureMap.put(featureName, className);
64   }
65
66   private static final String JavaDoc SLIB_PREFIX = "gnu.kawa.slib.";
67
68   static
69   {
70     map("generic-write", SLIB_PREFIX + "genwrite");
71     map("pretty-print", SLIB_PREFIX + "pp");
72     map("pprint-file", SLIB_PREFIX + "ppfile");
73     map("printf", SLIB_PREFIX + "printf");
74     map("xml", SLIB_PREFIX + "XML");
75     map("readtable", SLIB_PREFIX + "readtable");
76     map("srfi-10", SLIB_PREFIX + "readtable");
77     map("http", "gnu.kawa.servlet.HTTP");
78     map("srfi-1", SLIB_PREFIX + "srfi1");
79     map("list-lib", SLIB_PREFIX + "srfi1");
80     map("srfi-34", SLIB_PREFIX + "srfi34");
81     map("srfi-35", SLIB_PREFIX + "conditions");
82     map("condition", SLIB_PREFIX + "conditions");
83     map("conditions", SLIB_PREFIX + "conditions");
84     map("srfi-37", SLIB_PREFIX + "srfi37");
85     map("args-fold", SLIB_PREFIX + "srfi37");
86     map("srfi-64", SLIB_PREFIX + "testing");
87     map("testing", SLIB_PREFIX + "testing");
88     map("srfi-69", SLIB_PREFIX + "srfi69");
89     map("hash-table", SLIB_PREFIX + "srfi69");
90     map("gui", SLIB_PREFIX + "gui");
91     map("swing-gui", SLIB_PREFIX + "swing");
92   }
93
94   public static String JavaDoc mapFeature(String JavaDoc featureName)
95   {
96     return (String JavaDoc) featureMap.get(featureName);
97   }
98
99   public static Object JavaDoc find(String JavaDoc typeName)
100   {
101     return ModuleInfo.find(typeName).getInstance();
102   }
103
104   public boolean scanForDefinitions (Pair st, Vector forms,
105                                      ScopeExp defs, Translator tr)
106   {
107     if (tr.getState() == Compilation.PROLOG_PARSING)
108       {
109         tr.setState(Compilation.PROLOG_PARSED);
110         tr.pendingForm = st;
111         // FIXME - we want to call 'run' here anyway, rather than have
112
// it be emitted at the end of the 'body'.
113
return true;
114       }
115     Pair args = (Pair) st.cdr;
116     Object JavaDoc name = args.car;
117     Type type = null;
118     Pair p;
119     if (name instanceof Pair
120         && tr.matches((p = (Pair) name).car, Scheme.quote_sym))
121       {
122     name = p.cdr;
123     if (! (name instanceof Pair)
124         || (p = (Pair) name).cdr != LList.Empty
125         || ! (p.car instanceof String JavaDoc))
126       {
127         tr.error('e', "invalid quoted symbol for 'require'");
128         return false;
129       }
130     name = mapFeature((String JavaDoc) p.car);
131     if (name == null)
132       {
133         tr.error('e', "unknown feature name '"+p.car+"' for 'require'");
134         return false;
135       }
136     type = ClassType.make((String JavaDoc) name);
137       }
138     else if (name instanceof FString)
139       {
140         String JavaDoc sourceName = name.toString();
141         ModuleInfo info = lookupModuleFromSourcePath(sourceName, defs);
142         if (info == null)
143           {
144             tr.error('e', "malformed URL: "+sourceName);
145             return false;
146           }
147         return importDefinitions(null, info, null, forms, defs, tr);
148       }
149     else
150       {
151     if (name instanceof String JavaDoc)
152       {
153         String JavaDoc str = (String JavaDoc) name;
154         int len = str.length();
155         if (len > 2
156         && str.charAt(0) == '<'
157         && str.charAt(len-1) == '>')
158           {
159         str = str.substring(1, len-1);
160         if (str.indexOf('.') < 0)
161           str = tr.classPrefix + str;
162                 if (args.cdr instanceof Pair
163                     && ((Pair) args.cdr).car instanceof FString)
164                   {
165                     String JavaDoc sourceName = ((Pair) args.cdr).car.toString();
166                     ModuleInfo info = lookupModuleFromSourcePath(sourceName, defs);
167                     if (info == null)
168                       {
169                         tr.error('e', "malformed URL: "+sourceName);
170                         return false;
171                       }
172                     return importDefinitions(str,
173                                              info,
174                                              null, forms, defs, tr);
175                   }
176         type = Scheme.string2Type(str);
177           }
178       }
179       }
180     if (type == null)
181       {
182     tr.error('e', "invalid specifier for 'require'");
183     return false;
184       }
185     importDefinitions(null, ModuleInfo.find(type), null, forms, defs, tr);
186     return true;
187   }
188
189   public static ModuleInfo lookupModuleFromSourcePath (String JavaDoc sourceName, ScopeExp defs)
190   {
191     ModuleManager manager = ModuleManager.getInstance();
192     try
193       {
194         sourceName = URI_utils.resolve(sourceName, defs.getFileName()).toString();
195       }
196     catch (java.net.URISyntaxException JavaDoc ex)
197       {
198         return null;
199       }
200     return manager.findWithSourcePath(sourceName);
201   }
202
203   /** Import a module with a known source path.
204    * @param className Optional fully-qualified name of module's class,
205    * or null if unknown.
206    */

207   public static boolean
208   importDefinitions (String JavaDoc className, ModuleInfo info, String JavaDoc uri,
209                      Vector forms,
210                      ScopeExp defs, Compilation tr)
211   {
212     ModuleManager manager = ModuleManager.getInstance();
213     String JavaDoc sourceName = info.sourcePath;
214     long now;
215     if ((info.getState() & 1) == 0
216         && sourceName != null
217         && ! info.checkCurrent(manager, (now = System.currentTimeMillis())))
218       {
219         SourceMessages messages = tr.getMessages();
220         Language language = Language.getDefaultLanguage();
221         Compilation comp;
222         try
223           {
224             InPort fstream = InPort.openFile(info.sourceAbsPath);
225             info.clearClass();
226             info.className = className;
227             comp = language.parse(fstream, messages, info);
228             comp.immediate = tr.immediate;
229           }
230         catch (java.io.FileNotFoundException JavaDoc ex)
231           {
232             tr.error('e', "not found: "+ex.getMessage());
233             return false;
234           }
235         catch (java.io.IOException JavaDoc ex)
236           {
237             tr.error('e', "caught "+ex);
238             return false;
239           }
240         catch (SyntaxException ex)
241           {
242             if (ex.getMessages() != messages)
243               throw new RuntimeException JavaDoc ("confussing syntax error: "+ex);
244             // otherwise ignore it - it's already been recorded in messages.
245
return false;
246           }
247         ModuleExp mexp = comp.getModule();
248         ClassType ctype = mexp.classFor(comp);
249         info.className = ctype.getName();
250       }
251
252     if (tr.minfo != null && tr.getState() < Compilation.BODY_PARSED)
253       {
254         tr.minfo.addDependency(info);
255
256         if (! info.loadEager(Compilation.COMPILED)
257             && info.getState() < Compilation.RESOLVED)
258           {
259             // Oops. We found a cycle.
260
tr.pushPendingImport(info, defs);
261             return true;
262           }
263       }
264
265     ClassType type = info.getClassType();
266     String JavaDoc tname = info.className;
267     boolean immediate = tr.immediate && defs instanceof ModuleExp;
268     boolean isRunnable = info.getState() < Compilation.RESOLVED
269       || type.isSubtype(Compilation.typeRunnable);
270     Declaration decl = null;
271     ClassType thisType = ClassType.make("kawa.standard.require");
272     Expression[] args = { new QuoteExp(tname) };
273     Expression dofind = Invoke.makeInvokeStatic(thisType, "find", args);
274     Field instanceField = null;
275     Language language = tr.getLanguage();
276     dofind.setLine(tr);
277     int formsStart = forms.size();
278
279     ModuleExp mod = info.setupModuleExp();
280
281     Vector declPairs = new Vector();
282     for (Declaration fdecl = mod.firstDecl();
283          fdecl != null; fdecl = fdecl.nextDecl())
284       {
285         Object JavaDoc fdname = fdecl.getSymbol();
286         boolean isStatic = fdecl.getFlag(Declaration.STATIC_SPECIFIED);
287         if (! isStatic && decl == null)
288           {
289             String JavaDoc iname = tname.replace('.', '$') + "$instance";
290             decl = new Declaration(iname.intern(), type);
291             if (! immediate)
292               decl.setPrivate(true);
293             decl.setFlag(Declaration.IS_CONSTANT
294                          |Declaration.MODULE_REFERENCE);
295             defs.addDeclaration(decl);
296
297             decl.noteValue(dofind);
298             SetExp sexp = new SetExp(decl, dofind);
299             sexp.setLine(tr);
300             sexp.setDefining(true);
301             forms.addElement(sexp);
302             formsStart = forms.size();
303             decl.setFlag(Declaration.EARLY_INIT);
304             // If Runnable, we need to set decl value in initializer,
305
// and later 'run' it, so it needs to be stored in a field.
306
if (isRunnable)
307               decl.setSimple(false);
308
309             decl.setFlag(Declaration.TYPE_SPECIFIED);
310           }
311
312         if (fdecl.isPrivate())
313           continue;
314
315         if (fdecl.field != null)
316           {
317             String JavaDoc fname = fdecl.field.getName();
318             if (fname.equals("$instance"))
319               {
320                 instanceField = fdecl.field;
321                 continue;
322               }
323           }
324
325         // We create an alias in the current context that points
326
// a dummy declaration in the exported module. Normally,
327
// followAliases will skip the alias, so we use the latter.
328
// But if the binding is re-exported (or EXTERNAL_ACCESS
329
// gets set), then we need a separate declaration.
330
// (If EXTERNAL_ACCESS, the field gets PRIVATE_PREFIX.)
331
Object JavaDoc aname;
332
333         if (fdname instanceof Symbol)
334           aname = fdname;
335         else
336           {
337             String JavaDoc sname = fdname.toString();
338             if (uri == null)
339               aname = sname.intern();
340             else
341               aname = Symbol.make(uri, sname);
342           }
343         boolean isImportedInstance
344           = fdecl.field != null && fdecl.field.getName().endsWith("$instance");
345
346         Declaration adecl;
347         Declaration old = defs.lookup(aname, language, language.getNamespaceOf(fdecl));
348         if (isImportedInstance)
349           {
350             if (old != null)
351               continue;
352             adecl = defs.addDeclaration(aname);
353             adecl.setFlag(Declaration.IS_CONSTANT
354                           |Declaration.MODULE_REFERENCE);
355             adecl.setType(fdecl.getType());
356             adecl.setFlag(Declaration.TYPE_SPECIFIED);
357           }
358         else if (old != null
359                  && ! old.getFlag(Declaration.NOT_DEFINING)
360                  && (Declaration.followAliases(old)
361                      == Declaration.followAliases(fdecl)))
362           continue;
363         else
364           {
365             if (old != null
366                 && (old.getFlag(Declaration.NOT_DEFINING | Declaration.IS_UNKNOWN)))
367               {
368                 old.setFlag(false, Declaration.NOT_DEFINING|Declaration.IS_UNKNOWN);
369                 adecl = old;
370               }
371             else
372               {
373                 adecl = defs.addDeclaration(aname);
374                 if (old != null)
375                   ScopeExp.duplicateDeclarationError(old, adecl, tr);
376               }
377             adecl.setAlias(true);
378             adecl.setIndirectBinding(true);
379           }
380         adecl.setLocation(tr);
381         ReferenceExp fref = new ReferenceExp(fdecl);
382         fref.setContextDecl(decl);
383         if (! isImportedInstance)
384           {
385             fref.setDontDereference(true);
386             fref.setFlag(ReferenceExp.CREATE_FIELD_REFERENCE);
387             if (! immediate)
388               adecl.setPrivate(true);
389           }
390         if (fdecl.getFlag(Declaration.IS_CONSTANT))
391          adecl.setFlag(Declaration.IS_CONSTANT);
392         if (fdecl.getFlag(Declaration.IS_SYNTAX))
393           adecl.setFlag(Declaration.IS_SYNTAX);
394         if (fdecl.isProcedureDecl())
395           adecl.setProcedureDecl(true);
396         if (isStatic)
397           adecl.setFlag(Declaration.STATIC_SPECIFIED);
398
399         SetExp sexp = new SetExp(adecl, fref);
400         adecl.setFlag(Declaration.EARLY_INIT);
401         sexp.setDefining(true);
402         if (isImportedInstance)
403           {
404             // Make sure the "MODULE$instance" declarations are
405
// initialized first, since we may need then for
406
// imported declarations that are re-exported. (The
407
// instance may be needed for FieldLocation values.)
408
forms.insertElementAt(sexp, formsStart);
409             formsStart++;
410           }
411         else
412           forms.addElement(sexp);
413
414         declPairs.add(adecl);
415         declPairs.add(fdecl);
416
417         adecl.noteValue(fref);
418         adecl.setFlag(Declaration.IS_IMPORTED);
419         tr.push(adecl); // Add to translation env.
420
}
421
422     // This needs to be a second pass, because a Declaration might need to
423
// look for a context MOD$instance that is provided by a following field.
424
int ndecls = declPairs.size();
425     for (int i = 0; i < ndecls; i += 2)
426       {
427         Declaration adecl = (Declaration) declPairs.elementAt(i);
428         Declaration fdecl = (Declaration) declPairs.elementAt(i+1);
429         Expression fval = fdecl.getValue();
430         if (fdecl.isIndirectBinding() && fval instanceof ReferenceExp)
431           {
432             ReferenceExp aref = (ReferenceExp) adecl.getValue();
433             Declaration xdecl = ((ReferenceExp) fval).getBinding();
434             aref.setBinding(xdecl);
435             if (xdecl.needsContext())
436               {
437                 String JavaDoc iname
438                   = (xdecl.field.getDeclaringClass().getName().replace('.', '$')
439                      + "$instance");
440                 Declaration cdecl = defs.lookup(iname.intern());
441                 cdecl.setFlag(Declaration.EXPORT_SPECIFIED);
442                 aref.setContextDecl(cdecl);
443               }
444           }
445       }
446
447     if (isRunnable)
448       {
449         Method run = Compilation.typeRunnable.getDeclaredMethod("run", 0);
450         if (decl != null) // Need to make sure 'run' is invoked.
451
dofind = new ReferenceExp(decl);
452         else
453           {
454             if (instanceField != null)
455               { //Optimization
456
args = new Expression[]
457                   { new QuoteExp(type), new QuoteExp("$instance") };
458                 dofind = new ApplyExp(SlotGet.staticField, args);
459               }
460           }
461         dofind = new ApplyExp(run, new Expression[] { dofind });
462         dofind.setLine(tr);
463         forms.addElement(dofind);
464       }
465     tr.mustCompileHere();
466     return true;
467   }
468
469   public Expression rewriteForm (Pair form, Translator tr)
470   {
471     return null;
472   }
473 }
474
Popular Tags