KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > neu > ccs > jmk > Loader


1 // $Id: Loader.java,v 1.2 2001/12/07 11:41:24 ramsdell Exp $
2

3 // The loader class for reading and parsing makefiles
4

5 /*
6  * Copyright 1999 by John D. Ramsdell
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  */

22
23 package edu.neu.ccs.jmk;
24
25 import java.io.*;
26 import java.util.Vector JavaDoc;
27
28 /**
29  * The loader class for reading and parsing makefiles.
30  * This class is rather large because it contains the
31  * complete makefile parser and most of an expression evaluator.
32  * @see edu.neu.ccs.jmk.Make
33  * @version May 1999
34  * @author John D. Ramsdell
35  */

36 final class Loader
37 {
38   private Make make;
39   private File file; // For error messages
40
private LineNumberReader in;
41   private GlobalTable table;// Identifiers are looked up here.
42
private PrintWriter out;
43   private boolean verbose;
44
45   Loader(Make make, Reader in) {
46     this.make = make;
47     out = make.getOut();
48     verbose = make.isVerbose();
49     file = make.getFile();
50     this.in = new LineNumberReader(in);
51     table = new GlobalTable();
52   }
53
54   private Loader(Make make, // Used for processing an
55
File file, // include directive
56
Reader in,
57          GlobalTable table) {
58     this.make = make;
59     out = make.getOut();
60     verbose = make.isVerbose();
61     this.file = file;
62     this.in = new LineNumberReader(in);
63     this.table = table;
64   }
65
66   // This flag is used to implement conditionals.
67
private boolean conditionTrue = true;
68
69   /*
70    * Loads a makefile from a reader.
71    * @param in the source of the makefile
72    * @exception ParseError if an error occurs during loading
73    */

74   void load()
75        throws ParseError
76   {
77     try {
78       int token = scan();
79       if (token == EOF_TOKEN) {
80     String JavaDoc msg = "No input found";
81     throw new ParseError(file, in.getLineNumber(), msg);
82       }
83       do {
84     decl(token);
85     token = scan();
86       }
87       while (token != EOF_TOKEN);
88     }
89     finally { // Tidy up.
90
try {
91     in.close();
92       }
93       catch (IOException ex) {
94       }
95     }
96   }
97
98   // A declaration
99
private void decl(int token)
100        throws ParseError
101   {
102     switch (token) {
103     case IDENTIFIER_TOKEN: // Could be a rule or an assignment
104
Global var = table.get(string);
105       int saw = peek();
106       if (saw == '=') {
107     drop(); // Clear out token
108
assign(var);
109       }
110       else
111     rule(append(var, null, null));
112       break;
113     case INCLUDE_TOKEN:
114       include();
115       break;
116     case IF_TOKEN:
117       conditional();
118       break;
119     default:
120       if (tokenStartsItem(token))
121     rule(list(token, null, null));
122       else {
123     String JavaDoc msg = "Expecting an item, "
124       + "an include, or a conditional, but found ";
125     throw new ParseError(file, in.getLineNumber(),
126                  msg + token2string(token));
127       }
128     }
129   }
130
131   // An assignment
132
private void assign(Global var)
133        throws ParseError
134   {
135     int token = scan();
136     Expression exp;
137     if (token == ';')
138       exp = new Nil();
139     else if (token == FUNCTION_TOKEN) {
140       String JavaDoc name = var.getIdentifier();
141       CompEnv env = new CompEnv(null, new String JavaDoc[] { name });
142       exp = new Letrec(funct(name, env), new Lexical(name, 0, 0));
143       token = scan();
144     }
145     else {
146       exp = list(token, var.getIdentifier(), null);
147       token = scan();
148     }
149     if (token != ';')
150       expecting(';', token);
151     if (conditionTrue) {
152       Value val;
153       try {
154     val = exp.eval(null, null);
155       }
156       catch (Exception JavaDoc ex) {
157     String JavaDoc msg = "Exception: " + ex.getMessage();
158     throw new ParseError(file, in.getLineNumber(), msg);
159       }
160       var.setValue(val);
161       if (verbose) {
162     out.print(var.getIdentifier() + " =");
163     showValue(val);
164     out.println(";");
165       }
166     }
167   }
168
169   // Used to generate debugging output
170
private void showValue(Value v) {
171     if (StringList.isStringList(v))
172       for (StringList sl = (StringList)v; sl != null; sl = sl.getRest()) {
173     String JavaDoc term = unscanString(sl.getString());
174     out.print(" " + term);
175       }
176     else if (v instanceof Function) {
177       out.print(" [function");
178       String JavaDoc name = ((Function)v).getName();
179       if (name != null)
180     out.print(" " + name);
181       out.print("]");
182     }
183     else
184       out.print(" " + v);
185   }
186
187   // Routines for parsing sequences of items
188

189   // Parse list until end token is found
190
private Expression listEnd(int token, String JavaDoc name,
191                  CompEnv env, int end)
192        throws ParseError
193   {
194     if (token == end)
195       return new Nil();
196     else {
197       Expression exp = list(token, name, env);
198       token = scan();
199       if (token == end)
200     return exp;
201       else
202     return expecting(end, token);
203     }
204   }
205
206   // Parse list starting with token
207
private Expression list(int token, String JavaDoc name, CompEnv env)
208        throws ParseError
209   {
210     return append(item(token, name, env), name, env);
211   }
212
213   // Parse list starting with an expression
214
private Expression append(Expression exp, String JavaDoc name, CompEnv env)
215        throws ParseError
216   {
217     int token = peek();
218     if (!tokenStartsItem(token))
219       return exp;
220     else {
221       Vector JavaDoc v = new Vector JavaDoc();
222       v.addElement(exp);
223       do {
224     drop();
225     v.addElement(item(token, name, env));
226     token = peek();
227       }
228       while (tokenStartsItem(token));
229       Expression[] exps = new Expression[v.size()];
230       v.copyInto(exps);
231       return new Append(exps);
232     }
233   }
234
235   // Item parsing
236

237   // Classify item
238
private Expression item(int token, String JavaDoc name, CompEnv env)
239        throws ParseError
240   {
241     switch(token) {
242     case STRING_TOKEN:
243       return new Constant(string);
244     case IDENTIFIER_TOKEN:
245       return ident(string, env);
246     case '(':
247       return call(name, env);
248     case '{':
249       Expression exp = block(name, env);
250       token = scan();
251       if (token == '}')
252     return exp;
253       else
254     return expecting('}', token);
255     case IF_TOKEN:
256       return cond(name, env);
257     case FUNCTION_TOKEN:
258       return funct(name, env);
259     case DO_TOKEN:
260       return loop(name, env);
261     default:
262       String JavaDoc msg = "Expecting an item, but found " + token2string(token);
263       throw new ParseError(file, in.getLineNumber(), msg);
264     }
265   }
266
267   private boolean tokenStartsItem(int token) {
268     switch(token) {
269     case STRING_TOKEN:
270     case IDENTIFIER_TOKEN:
271     case '(':
272     case '{':
273     case IF_TOKEN:
274     case FUNCTION_TOKEN:
275     case DO_TOKEN:
276       return true;
277     default:
278       return false;
279     }
280   }
281
282   // Classify identifiers as global or lexical
283
private Expression ident(String JavaDoc identifier, CompEnv env) {
284     int up = 0;
285     for (; env != null; env = env.parent) {
286       for (int offset = 0; offset < env.rib.length; offset++)
287     if (identifier == env.rib[offset])
288       return new Lexical(identifier, up, offset);
289       up++;
290     }
291     return table.get(identifier);
292   }
293
294   // A function application
295
private Expression call(String JavaDoc name, CompEnv env)
296        throws ParseError
297   {
298     Expression op = item(scan(), name, env);
299     Vector JavaDoc v = new Vector JavaDoc();
300     for (;;) {
301       int token = scan();
302       if (tokenStartsItem(token)) {
303     v.addElement(list(token, name, env));
304     token = scan();
305       }
306       else
307     v.addElement(new Nil());
308       if (token == ')') {
309     Expression exps[] = new Expression[v.size()];
310     v.copyInto(exps);
311     return new Call(op, exps);
312       }
313       else if (token != ',')
314     return expecting(',', token);
315     }
316   }
317
318   // A sequence of bindings followed by a list
319
private Expression block(String JavaDoc name, CompEnv env)
320        throws ParseError
321   {
322     int token = peek();
323     if (!tokenStartsItem(token))
324       return new Nil();
325     drop();
326     if (token == IDENTIFIER_TOKEN) {
327       String JavaDoc identifier = string;
328       token = peek();
329       if (token == '=') {
330     drop();
331     token = scan();
332     CompEnv newenv = new CompEnv(env, new String JavaDoc[] { identifier });
333     if (token == ';')
334       return new Let(new Nil(), block(name, newenv));
335     else if (token == FUNCTION_TOKEN) {
336       Expression exp = funct(identifier, newenv);
337       token = scan();
338       if (token != ';')
339         return expecting(';', token);
340       else
341         return new Letrec(exp, block(name, newenv));
342     }
343     else {
344       Expression exp = list(token, identifier, env);
345       token = scan();
346       if (token != ';')
347         return expecting(';', token);
348       else
349         return new Let(exp, block(name, newenv));
350     }
351       }
352       else
353     return append(ident(identifier, env), name, env);
354     }
355     else
356       return list(token, name, env);
357   }
358
359   // Conditional expressions
360
private Expression cond(String JavaDoc name, CompEnv env)
361        throws ParseError
362   {
363     Expression exp = listEnd(scan(), name, env, THEN_TOKEN);
364     Expression conseq = block(name, env);
365     int token = scan();
366     if (token != ELSE_TOKEN)
367       return expecting(ELSE_TOKEN, token);
368     Expression altern = block(name, env);
369     token = scan();
370     if (token == END_TOKEN)
371       return new If(exp, conseq, altern);
372     else
373       return expecting(END_TOKEN, token);
374   }
375
376   // function constants
377
private Expression funct(String JavaDoc name, CompEnv env)
378        throws ParseError
379   {
380     int token = scan();
381     if (token != '(')
382       return expecting('(', token);
383     token = scan();
384     if (token != IDENTIFIER_TOKEN)
385       return expecting(IDENTIFIER_TOKEN, token);
386     Vector JavaDoc v = new Vector JavaDoc();
387     v.addElement(string);
388     for (;;) {
389       token = scan();
390       if (token == ')') {
391     String JavaDoc ids[] = new String JavaDoc[v.size()];
392     v.copyInto(ids);
393     CompEnv newenv = new CompEnv(env, ids);
394     Expression body = block(name, newenv);
395     token = scan();
396     if (token != END_TOKEN)
397       return expecting(END_TOKEN, token);
398     else
399       return new Lambda(name, ids.length, body);
400       }
401       else if (token != ',')
402     return expecting(',', token);
403       token = scan();
404       if (token != IDENTIFIER_TOKEN)
405     return expecting(IDENTIFIER_TOKEN, token);
406       v.addElement(string);
407     }
408   }
409
410   // The DO loop
411
private Expression loop(String JavaDoc name, CompEnv env)
412        throws ParseError
413   {
414     int token = scan();
415     if (token != IDENTIFIER_TOKEN)
416       return expecting(IDENTIFIER_TOKEN, token);
417     String JavaDoc label = string;
418     CompEnv callEnv = new CompEnv(env, new String JavaDoc[] { label });
419     token = scan();
420     if (token != '(')
421       return expecting('(', token);
422     Vector JavaDoc str = new Vector JavaDoc();
423     Vector JavaDoc inits = new Vector JavaDoc();
424     token = scan();
425     if (token != IDENTIFIER_TOKEN)
426       return expecting(IDENTIFIER_TOKEN, token);
427     str.addElement(string);
428     token = scan();
429     if (token != '=')
430       return expecting('=', token);
431     token = scan();
432     if (token == ')' || token == ',')
433       inits.addElement(new Nil());
434     else {
435       inits.addElement(list(token, name, callEnv));
436       token = scan();
437     }
438     for (;;) {
439       if (token == ')') {
440     String JavaDoc ids[] = new String JavaDoc[str.size()];
441     str.copyInto(ids);
442     CompEnv newenv = new CompEnv(callEnv, ids);
443     Expression body = block(name, newenv);
444     token = scan();
445     if (token != END_TOKEN)
446       return expecting(END_TOKEN, token);
447     Expression exp = new Lambda(label, ids.length, body);
448     Expression exps[] = new Expression[inits.size()];
449     inits.copyInto(exps);
450     return new Letrec(exp, new Call(new Lexical(name, 0, 0), exps));
451       }
452       else if (token != ',')
453     return expecting(',', token);
454       token = scan();
455       if (token != IDENTIFIER_TOKEN)
456     return expecting(IDENTIFIER_TOKEN, token);
457       str.addElement(string);
458       token = scan();
459       if (token != '=')
460     return expecting('=', token);
461       token = scan();
462       if (token == ')' || token == ',')
463     inits.addElement(new Nil());
464       else {
465     inits.addElement(list(token, name, callEnv));
466     token = scan();
467       }
468     }
469   }
470
471   private Expression expecting(int wanted, int got)
472        throws ParseError
473   {
474     String JavaDoc msg = "Expecting " + token2string(wanted)
475       + ", but found " + token2string(got);
476     throw new ParseError(file, in.getLineNumber(), msg);
477   }
478
479   private StringList evalStringList(Expression exp)
480        throws ParseError
481   {
482     Value value;
483     try {
484       value = exp.eval(null, null);
485     }
486     catch (Exception JavaDoc ex) {
487       String JavaDoc msg = "Exception: " + ex.getMessage();
488       throw new ParseError(file, in.getLineNumber(), msg);
489     }
490     if (StringList.isStringList(value))
491       return (StringList)value;
492     else {
493       String JavaDoc msg = "Expression did not evaluate to a string list";
494       throw new ParseError(file, in.getLineNumber(), msg);
495     }
496   }
497
498   // A rule
499
private void rule(Expression exp)
500        throws ParseError
501   {
502     int lineNumber = in.getLineNumber();
503     StringList targets = evalStringList(exp);
504     int token = scan();
505     if (token != ':')
506       expecting(':', token);
507     String JavaDoc[] prerequisites = prerequisiteList();
508     Command[] commands = commandList();
509
510     if (conditionTrue)
511       for (; targets != null; targets = targets.getRest())
512     make.addRule(targets.getString(),
513              prerequisites,
514              commands,
515              lineNumber);
516   }
517
518   // Prerequisites
519
private String JavaDoc[] prerequisiteList()
520        throws ParseError
521   {
522     Expression exp = listEnd(scan(), null, null, ';');
523     StringList val = evalStringList(exp);
524     return StringList.list2array(val);
525   }
526
527   // Commands
528
private Command[] commandList()
529        throws ParseError
530   {
531     int token = peek();
532     if (token != '{')
533       return new Command[0];
534     drop();
535     Vector JavaDoc v = new Vector JavaDoc(); // A vector of commands
536
for (;;) {
537       token = scan();
538       switch (token) {
539       case '}': // End of commands
540
Command[] cmds = new Command[v.size()];
541     v.copyInto(cmds);
542     return cmds;
543       case IDENTIFIER_TOKEN: // Next command
544
v.addElement(commandItem(string));
545     break;
546       case '-':
547     token = scan();
548     if (token == IDENTIFIER_TOKEN) {
549       Command cmd = commandItem(string);
550       cmd.setIgnore(true);
551       v.addElement(cmd);
552       break;
553     }
554     // fall thru okay
555
default:
556     String JavaDoc msg = "Expecting an identifier, ";
557     throw new ParseError(file, in.getLineNumber(),
558                  msg + token2string(token));
559       }
560     }
561   }
562
563   // Various operators available for selection.
564

565   private final static ExecOperator exec = new ExecOperator();
566
567   private final static FileOperator delete = new FileOperator() {
568     boolean exec(File arg, PrintWriter out) {
569       return arg.delete();
570     }
571     public String JavaDoc getName() {
572       return "delete";
573     }
574   };
575
576   // recursive delete
577
private final static FileOperator delall = new FileOperator() {
578     boolean exec(File arg, PrintWriter out) {
579       if (arg.isDirectory()) {
580     String JavaDoc[] file = arg.list();
581     for (int i = 0; i < file.length; i++) {
582       File f = new File(arg, file[i]);
583       if (!exec(f, out))
584         return false;
585     }
586       }
587       return arg.delete();
588     }
589     public String JavaDoc getName() {
590       return "delall";
591     }
592   };
593
594   private final static FileOperator mkdir = new FileOperator() {
595     boolean exec(File arg, PrintWriter out) {
596       return arg.mkdir();
597     }
598     public String JavaDoc getName() {
599       return "mkdir";
600     }
601   };
602
603   private final static FileOperator mkdirs = new FileOperator() {
604     boolean exec(File arg, PrintWriter out) {
605       return arg.mkdirs();
606     }
607     public String JavaDoc getName() {
608       return "mkdirs";
609     }
610   };
611
612   private final static BinaryFileOperator rename = new BinaryFileOperator() {
613     boolean exec(File arg1, File arg2, PrintWriter out) {
614       if (arg1.equals(arg2))
615     return false;
616       return arg1.renameTo(arg2);
617     }
618     public String JavaDoc getName() {
619       return "rename";
620     }
621   };
622
623   private final static BinaryFileOperator copy = new BinaryFileOperator() {
624     boolean exec(File arg1, File arg2, PrintWriter out) {
625       if (arg1.equals(arg2))
626     return false;
627       try {
628     int bufferSize = 8192;
629     byte[] buffer = new byte[bufferSize];
630     FileInputStream src = new FileInputStream(arg1);
631     FileOutputStream dst = new FileOutputStream(arg2);
632     for (;;) {
633       int n = src.read(buffer);
634       if (n == -1) {
635         src.close();
636         dst.close();
637         return true;
638       }
639       dst.write(buffer, 0, n);
640     }
641       }
642       catch (FileNotFoundException ex) {
643     out.println("Cannot open " + ex.getMessage());
644     return false;
645       }
646       catch (IOException ex) {
647     out.println(ex.toString());
648     return false;
649       }
650     }
651     public String JavaDoc getName() {
652       return "copy";
653     }
654   };
655
656   private final static CreateOperator create = new CreateOperator();
657
658   private final static NoteOperator note = new NoteOperator();
659
660   private final static ClassOperator classOperator = new ClassOperator();
661
662   // A single command
663
private Command commandItem(String JavaDoc operatorName)
664        throws ParseError
665   {
666     Operator operator;
667     if (operatorName.equals(exec.getName()))
668       operator = exec;
669     else if (operatorName.equals(delete.getName()))
670       operator = delete;
671     else if (operatorName.equals(delall.getName()))
672       operator = delall;
673     else if (operatorName.equals(mkdir.getName()))
674       operator = mkdir;
675     else if (operatorName.equals(mkdirs.getName()))
676       operator = mkdirs;
677     else if (operatorName.equals(rename.getName()))
678       operator = rename;
679     else if (operatorName.equals(copy.getName()))
680       operator = copy;
681     else if (operatorName.equals(create.getName()))
682       operator = create;
683     else if (operatorName.equals(note.getName()))
684       operator = note;
685     else if (operatorName.equals(classOperator.getName()))
686       operator = classOperator;
687     else {
688       String JavaDoc msg = "Unrecognized command operator: " + operatorName;
689       throw new ParseError(file, in.getLineNumber(), msg);
690     }
691     // Construct function that produces the operands
692
CompEnv commandEnv = new CompEnv(null, Command.commandArgs);
693     Expression exp = listEnd(scan(), null, commandEnv, ';');
694     Function fun = new Closure(null, Command.commandArgs.length, exp, null);
695     return new Command(operator, fun);
696   }
697
698   // Inclusion of the makefiles given by a list of strings.
699
private void include()
700        throws ParseError
701   {
702     Expression exp = listEnd(scan(), null, null, ';');
703     StringList val = evalStringList(exp);
704     if (conditionTrue) {
705       if (verbose) {
706     out.print(INCLUDE_WORD);
707     showValue(val);
708     out.println(";");
709       }
710       String JavaDoc parent = file.getParent(); // Used for relative file names
711
for (; val != null; val = val.getRest()) {
712     String JavaDoc incName = StringUtils.localizePaths(val.getString());
713     File incFile = new File(incName);
714     String JavaDoc incDir = null;
715     if (parent != null && !incFile.isAbsolute()) {
716       incDir = parent;
717       incFile = new File(incDir, incName);
718     }
719     try {
720       FileReader incIn = new FileReader(incFile);
721       new Loader(make, incFile, incIn, table).load();
722     }
723     catch (FileNotFoundException ex) {
724       String JavaDoc msg ="Cannot find include file " + incName;
725       if (incDir != null)
726         msg += " in directory " + incDir;
727       throw new ParseError(file, in.getLineNumber(), msg);
728     }
729       }
730     }
731   }
732
733   // Conditional makefile reading.
734
// This sets the instance variable conditionTrue.
735
private void conditional()
736        throws ParseError
737   {
738     boolean changeSense = conditionTrue;
739     boolean elseSeen = false;
740     Expression exp = listEnd(scan(), null, null, THEN_TOKEN);
741     StringList val = evalStringList(exp);
742     String JavaDoc msg;
743     if (verbose && changeSense) {
744       out.print(IF_WORD);
745       showValue(val);
746       out.println(" " + THEN_WORD);
747     }
748     if (changeSense)
749       conditionTrue = val != null; // Make the test
750
for (;;) {
751       int token = scan();
752       switch (token) {
753       case EOF_TOKEN:
754     if (elseSeen)
755       msg = "Expecting " + END_WORD + ", ";
756     else
757       msg = "Expecting " + END_WORD + " or " + ELSE_WORD + ", ";
758     throw new ParseError(file, in.getLineNumber(),
759                  msg + token2string(token));
760       case END_TOKEN: // Found conditional closer
761
conditionTrue = changeSense;
762     if (verbose && changeSense)
763       out.println(END_WORD);
764     return;
765       case ELSE_TOKEN: // Found alternative
766
if (elseSeen) {
767       msg = "Expecting " + END_WORD + ", ";
768       throw new ParseError(file, in.getLineNumber(),
769                    msg + token2string(token));
770     }
771     elseSeen = true;
772     if (verbose && changeSense)
773       out.println(ELSE_WORD);
774     if (changeSense)
775       conditionTrue = !conditionTrue;
776     break;
777       default:
778     decl(token); // Found content
779
}
780     }
781   }
782
783   // For error messages
784
private String JavaDoc token2string(int token) {
785     switch (token) {
786     case EOF_TOKEN:
787       return "[EOF]";
788     case STRING_TOKEN:
789       return "string " + unscanString(string);
790     case IDENTIFIER_TOKEN:
791       return "identifier " + string;
792     case INCLUDE_TOKEN:
793       return "the reserved word " + INCLUDE_WORD;
794     case IF_TOKEN:
795       return "the reserved word " + IF_WORD;
796     case THEN_TOKEN:
797       return "the reserved word " + THEN_WORD;
798     case ELSE_TOKEN:
799       return "the reserved word " + ELSE_WORD;
800     case END_TOKEN:
801       return "the reserved word " + END_WORD;
802     case FUNCTION_TOKEN:
803       return "the reserved word " + FUNCTION_WORD;
804     case DO_TOKEN:
805       return "the reserved word " + DO_WORD;
806     default: // must be a character
807
return "'" + (char)token + "'";
808     }
809   }
810
811   private static String JavaDoc unscanString(String JavaDoc s) {
812     if (s.indexOf('\\') < 0 && s.indexOf('"') < 0)
813       return "\"" + s + "\"";
814     // This adds in the escape character.
815
StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
816     sb.append('"');
817     for (int i = 0; i < s.length(); i++) {
818       char ch = s.charAt(i);
819       switch (ch) {
820       case '\\':
821       case '"':
822     sb.append('\\');
823     // fall thru okay
824
default:
825     sb.append(ch);
826       }
827     }
828     sb.append('"');
829     return sb.toString();
830   }
831
832   /* * * * * *
833    * The Makefile lexical analyzer
834    * * * * * */

835
836   // This uses a common implementation method for lexical analyzers
837
// written in C like languages. Tokens are ints, and you can
838
// guess rest.
839

840   // Character tokens are the character values.
841
private final static int EOF_TOKEN = -1;
842   private final static int STRING_TOKEN = -2;
843   private final static int IDENTIFIER_TOKEN = -3;
844   private final static int INCLUDE_TOKEN = -4;
845   private final static int IF_TOKEN = -5;
846   private final static int THEN_TOKEN = -6;
847   private final static int ELSE_TOKEN = -7;
848   private final static int END_TOKEN = -8;
849   private final static int FUNCTION_TOKEN = -9;
850   private final static int DO_TOKEN = -10;
851
852   // Reserved words
853
private final static String JavaDoc INCLUDE_WORD = "include";
854   private final static String JavaDoc IF_WORD = "if";
855   private final static String JavaDoc THEN_WORD = "then";
856   private final static String JavaDoc ELSE_WORD = "else";
857   private final static String JavaDoc END_WORD = "end";
858   private final static String JavaDoc FUNCTION_WORD = "function";
859   private final static String JavaDoc DO_WORD = "do";
860
861   // Token values are strings
862
// When string is an identifier, it must be interned!
863
private String JavaDoc string;
864
865   private int savedToken;
866   private boolean isTokenSaved = false;
867
868   /**
869    * Returns a token without removing it from the input stream of tokens.
870    * @return first token
871    * @exception ParseError if scan throws one
872    */

873   private int peek()
874        throws ParseError
875   {
876     if (!isTokenSaved) {
877       savedToken = scan();
878       isTokenSaved = true;
879     }
880     return savedToken;
881   }
882
883   /**
884    * Drops a saved token.
885    */

886   private void drop() {
887     isTokenSaved = false;
888   }
889
890   // Characters can be pushed back into the input stream of chars.
891
// This happens after reading an identifier.
892
private boolean isCharPushedBack = false;
893   private int pushedBackChar;
894
895   /**
896    * Returns a token after removing it from the input stream of tokens.
897    * @return first token
898    * @exception ParseError if syntax error occurs
899    */

900   private int scan()
901        throws ParseError
902   {
903     try {
904       if (isTokenSaved) { // Token was saved
905
isTokenSaved = false; // because someone
906
return savedToken; // was peeking.
907
}
908
909       int ch;
910       if (isCharPushedBack) { // Char pushed back after
911
ch = pushedBackChar; // reading an identifier.
912
isCharPushedBack = false; // Use it instead of reading.
913
}
914       else
915     ch = in.read();
916
917       for (;;) { // Skip leading spaces
918
if (ch == -1)
919       return EOF_TOKEN;
920     if (!Character.isWhitespace((char)ch))
921       break;
922     ch = in.read();
923       }
924
925       switch (ch) {
926       case ',':
927       case ';':
928       case ':':
929       case '-':
930       case '=':
931       case '(':
932       case ')':
933       case '{':
934       case '}':
935     return ch;
936       case '@':
937     string = "@";
938     return IDENTIFIER_TOKEN;
939       case '<':
940     string = "<";
941     return IDENTIFIER_TOKEN;
942       case '?':
943     string = "?";
944     return IDENTIFIER_TOKEN;
945       case '%':
946     string = "%";
947     return IDENTIFIER_TOKEN;
948       case '#': // Found comment character
949
if (in.readLine() == null) // Ignore rest of line
950
return EOF_TOKEN;
951     else
952       return scan(); // Try again...
953
case '"': // Found a string
954
return readString();
955       default: // It must be an identifier.
956
if (!Character.isJavaIdentifierStart((char)ch))
957       throw new ParseError(file, in.getLineNumber(),
958                    "Unrecognized character " + (char)ch);
959     return readIdentifier(ch);
960       }
961     }
962     catch (IOException ex) {
963       String JavaDoc msg = "I/O exception: " + ex.getMessage();
964       throw new ParseError(file, in.getLineNumber(), msg);
965     }
966   }
967
968   private int readString()
969        throws IOException, ParseError
970   {
971     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
972     for (;;) {
973       int ch = in.read();
974       switch (ch) {
975       case -1:
976     throw new ParseError(file, in.getLineNumber(),
977                  "EOF in string");
978       case '"':
979     string = sb.toString();
980     return STRING_TOKEN;
981       case '\n':
982     throw new ParseError(file, in.getLineNumber(),
983                  "newline in string");
984                 
985       case '\\': // Escaped string character
986
ch = in.read(); // Only " and \ can be escaped.
987
switch (ch) {
988     case '"':
989       break; // Add to string
990
case '\\':
991       break;
992     case -1: // Error cases follow
993
throw new ParseError(file, in.getLineNumber(),
994                    "EOF in string");
995     case '\n':
996       throw new ParseError(file, in.getLineNumber(),
997                    "newline in string");
998     default:
999       throw new ParseError(file, in.getLineNumber(),
1000                   "Cannot escape " + (char)ch);
1001    }
1002    // Fall thru okay
1003
default:
1004    sb.append((char)ch);
1005      }
1006    }
1007  }
1008
1009  private int readIdentifier(int ch)
1010       throws IOException
1011  {
1012    StringBuffer JavaDoc ib = new StringBuffer JavaDoc();
1013    ib.append((char)ch);
1014    for (;;) {
1015      ch = in.read();
1016      if (ch != -1 && Character.isJavaIdentifierPart((char)ch))
1017    ib.append((char)ch);
1018      else {
1019    isCharPushedBack = true;
1020    pushedBackChar = ch; // Intern identifiers
1021
string = ib.toString().intern();
1022    if (string == INCLUDE_WORD) // Identify reserved words
1023
return INCLUDE_TOKEN;
1024    else if (string == IF_WORD)
1025      return IF_TOKEN;
1026    else if (string == THEN_WORD)
1027      return THEN_TOKEN;
1028    else if (string == ELSE_WORD)
1029      return ELSE_TOKEN;
1030    else if (string == END_WORD)
1031      return END_TOKEN;
1032    else if (string == FUNCTION_WORD)
1033      return FUNCTION_TOKEN;
1034    else if (string == DO_WORD)
1035      return DO_TOKEN;
1036    else
1037      return IDENTIFIER_TOKEN;
1038      }
1039    }
1040  }
1041
1042  /* * * * * *
1043   * The Makefile expression evaluator
1044   * * * * * */

1045
1046  // This implementation is much more efficient on Java VMs
1047
// that are tail recursive.
1048

1049  // Compile time environments
1050
// Used only during parsing
1051
private static class CompEnv
1052  {
1053    final CompEnv parent;
1054    final String JavaDoc[] rib;
1055
1056    CompEnv(CompEnv parent, String JavaDoc[] rib) {
1057      this.parent = parent;
1058      this.rib = rib;
1059    }
1060  }
1061
1062  /**
1063   * Expressions other than Globals.
1064   */

1065
1066  // A string constant
1067
private static class Constant
1068  extends Expression
1069  {
1070    private final String JavaDoc string;
1071
1072    Constant(String JavaDoc string) {
1073      this.string = string;
1074    }
1075
1076    Value eval(Environment env, StringList list) {
1077      return new StringList(string, list);
1078    }
1079  }
1080
1081  // A reference to a lexical variable
1082
private static class Lexical
1083  extends Expression
1084  {
1085    private final String JavaDoc identifier;
1086    private final int up;
1087    private final int offset;
1088
1089    Lexical (String JavaDoc identifier, int up, int offset) {
1090      this.identifier = identifier;
1091      this.up = up;
1092      this.offset = offset;
1093    }
1094
1095    Value eval(Environment env, StringList list)
1096     throws StringListCastException
1097    {
1098      Value value = Environment.lookup(env, up, offset);
1099      if (list == null)
1100    return value;
1101      else if (StringList.isStringList(value))
1102    return StringList.append((StringList)value, list);
1103      else {
1104    String JavaDoc msg = "Append error: the value in " + identifier +
1105      " is not a string list";
1106    throw new StringListCastException(msg);
1107      }
1108    }
1109  }
1110
1111  // A function application
1112
private static class Call
1113  extends Expression
1114  {
1115    private final Expression operator;
1116    private final Expression[] operands;
1117
1118    Call(Expression operator, Expression[] operands) {
1119      this.operator = operator;
1120      this.operands = operands;
1121    }
1122
1123    Value eval(Environment env, StringList list)
1124     throws Exception JavaDoc
1125    {
1126      int nargs = operands.length;
1127      Value[] args = new Value[nargs];
1128      for (int i = 0; i < nargs; i++)
1129    args[i] = operands[i].eval(env, null);
1130      Value op = operator.eval(env, null);
1131      if (op instanceof Function)
1132    return ((Function)op).invoke(args, list);
1133      else {
1134    String JavaDoc msg = "Call error: operator not a function";
1135    throw new FunctionCastException(msg);
1136      }
1137    }
1138  }
1139
1140  // A local binding of a variable
1141
private static class Let
1142  extends Expression
1143  {
1144    private final Expression exp;
1145    private final Expression body;
1146
1147    Let(Expression exp, Expression body) {
1148      this.exp = exp;
1149      this.body = body;
1150    }
1151
1152    Value eval(Environment env, StringList list)
1153     throws Exception JavaDoc
1154    {
1155      Value value = exp.eval(env, null);
1156      Value[] rib = new Value[] { value };
1157      Environment newenv = new Environment(env, rib);
1158      return body.eval(newenv, list);
1159    }
1160  }
1161
1162  // A recursive binding of a variable
1163
private static class Letrec
1164  extends Expression
1165  {
1166    private final Expression exp;
1167    private final Expression body;
1168
1169    Letrec(Expression exp, Expression body) {
1170      this.exp = exp;
1171      this.body = body;
1172    }
1173
1174    Value eval(Environment env, StringList list)
1175     throws Exception JavaDoc
1176    {
1177      Value[] rib = new Value[1];
1178      Environment newenv = new Environment(env, rib);
1179      rib[0] = exp.eval(newenv, null);
1180      return body.eval(newenv, list);
1181    }
1182  }
1183
1184  // A conditional
1185
private static class If
1186  extends Expression
1187  {
1188    private final Expression condition;
1189    private final Expression consequent;
1190    private final Expression alternative;
1191
1192    If(Expression condition, Expression consequent, Expression alternative) {
1193      this.condition = condition;
1194      this.consequent = consequent;
1195      this.alternative = alternative;
1196    }
1197
1198    Value eval(Environment env, StringList list)
1199     throws Exception JavaDoc
1200    {
1201      Value cond = condition.eval(env, null);
1202      if (StringList.isStringList(cond)) {
1203    if (cond != null)
1204      return consequent.eval(env, list);
1205    else
1206      return alternative.eval(env, list);
1207      }
1208      else {
1209    String JavaDoc msg = "If error: the condition is not a string list";
1210    throw new StringListCastException(msg);
1211      }
1212    }
1213  }
1214
1215  // An expression that produces a function
1216
private static class Lambda
1217  extends Expression
1218  {
1219    private final String JavaDoc name;
1220    private final int nargs;
1221    private final Expression body;
1222
1223    Lambda(String JavaDoc name, int nargs, Expression body) {
1224      this.name = name;
1225      this.nargs = nargs;
1226      this.body = body;
1227    }
1228
1229    Value eval(Environment env, StringList list)
1230     throws StringListCastException
1231    {
1232      if (list == null)
1233    return new Closure(name, nargs, body, env);
1234      else {
1235    String JavaDoc msg =
1236      "Append error: a function cannot be appended to a string list";
1237    throw new StringListCastException(msg);
1238      }
1239    }
1240  }
1241
1242  // The function produced by a Lambda expression
1243
private static class Closure
1244  implements Function
1245  {
1246    private final String JavaDoc name;
1247    private final int nargs;
1248    private final Expression body;
1249    private final Environment env;
1250
1251    Closure(String JavaDoc name, int nargs, Expression body, Environment env) {
1252      this.name = name;
1253      this.nargs = nargs;
1254      this.body = body;
1255      this.env = env;
1256    }
1257
1258    public String JavaDoc getName() {
1259      return name;
1260    }
1261
1262    public Value invoke(Value[] args, StringList list)
1263     throws Exception JavaDoc
1264    {
1265      if (nargs == args.length) {
1266    Environment newenv = new Environment(env, args);
1267    return body.eval(newenv, list);
1268      }
1269      else {
1270    String JavaDoc msg = "Arg count error: expecting " + nargs +
1271      " but got " + args.length + " arguments";
1272    throw new WrongArgCountException(msg);
1273      }
1274    }
1275  }
1276
1277  // The expression for the empty list
1278
private static class Nil
1279  extends Expression
1280  {
1281    Nil() {
1282    }
1283
1284    Value eval(Environment env, StringList list)
1285    {
1286      return list;
1287    }
1288  }
1289
1290  // The expression that appends the values produced by multiple expressions
1291
private static class Append
1292  extends Expression
1293  {
1294    private final Expression[] exps;
1295
1296    Append(Expression[] exps) {
1297      this.exps = exps;
1298    }
1299
1300    Value eval(Environment env, StringList list)
1301     throws Exception JavaDoc
1302    {
1303      int nexps = exps.length;
1304      switch (nexps) {
1305      case 0:
1306    return list;
1307      case 1:
1308    return exps[0].eval(env, list);
1309      default: // Must check every result
1310
for (nexps--; nexps >= 0; nexps--) {
1311      Value value = exps[nexps].eval(env, list);
1312      if (StringList.isStringList(value))
1313        list = (StringList)value;
1314      else {
1315        String JavaDoc msg = "Append error: a value in a list was not a string";
1316        throw new StringListCastException(msg);
1317      }
1318    }
1319    return list;
1320      }
1321    }
1322  }
1323}
1324
Popular Tags