123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 |
- package kawa.lang;
- import gnu.lists.*;
- import java.io.*;
- import gnu.mapping.*;
- import gnu.expr.*;
- import gnu.kawa.functions.DisplayFormat;
- import gnu.kawa.io.OutPort;
- import java.util.*;
- import gnu.kawa.lispexpr.LispLanguage;
- /** The translated form of a <code>(syntax <var>template</var>)</code>. */
- public class SyntaxTemplate implements Externalizable {
- /** A <code>syntax</code> or <code>syntax-rules</code> template is
- * translated into a "template program."
- * The template program is a simple bytecode stored in a string.
- * The encoding is designed so that instructions are normally
- * in the range 1..127, which makes the <code>CONSTANT_Utf8</code> encoding
- * used in <code>.class</code> files compact.
- * The following <code>BUILD_XXX</code> are the "opcode" of the encoding,
- * stored in the low-order 3 bits of a <code>char</code>.
- */
- String template_program;
- ScopeExp savedScope;
- /** Template instructions that don't have an operand value. */
- static final int BUILD_MISC = 0;
- /** Make following operand into a 1-element list. */
- static final int BUILD_LIST1 = (1<<3)+BUILD_MISC;
- static final int BUILD_NIL = (2<<3)+BUILD_MISC;
- /** Wrap following sub-expression in a SyntaxForm. */
- static final int BUILD_SYNTAX = (3<<3)+BUILD_MISC;
- /** Build a vector (an <code>FVector</code>) from following sub-expression.
- * The latter must evaluate to a list. */
- static final int BUILD_VECTOR = (5<<3)+BUILD_MISC;
- /** Instruction to create a <code>Pair</code> from sub-expressions.
- * Instruction <code>BUILD_CONS+8*delta</code> is followed by a
- * sub-expression for the <code>car</code>
- * (whose length is <code>delta</code> chars),
- * followed by the expression for the <code>cdr</code>. */
- static final int BUILD_CONS = 1;
- /** Instruction BUILD_VAR+8*i pushes vars[i].
- * This array contains the values of pattern variables. */
- final static int BUILD_VAR = 2; // Must be an even number.
- /** Instruction BUILD_VAR_CAR+8*i pushes car(vars[i]).
- * It assumes that vars[i] is actually a pair whose car was the
- * matched pattern variable. (This is done so we can preserve
- * <code>PairWithPosition</code> source positions). */
- final static int BUILD_VAR_CAR = BUILD_VAR+1;
- /** Instruction BUILD_LITERAL+8*i pushes literal_values[i]. */
- final static int BUILD_LITERAL = 4;
- /** Instruction <code>BUILD_DOTS+8*i</code> repeats a sub-expression.
- * The value <code>i</code> is a variable index of a pattern variable
- * of at least the needed depth. The result is spliced in. */
- final static int BUILD_DOTS = 5;
- /** Unfinished support for "operand" values that need more than 13 bits. */
- final static int BUILD_WIDE = 7;
- /** Map variable to ellipsis nesting depth.
- * The nesting depth of the <code>i</code>'th pattern variable
- * is <code>(int) patternNesting.charAt(i)/2</code>.
- * The low-order bit indicates that if matched value is the <code>car</code>
- * of the value saved in the <code>vars</code> array.
- * (We use a <code>String</code> because it is compact both at runtime
- * and in <code>.class</code> files. */
- String patternNesting;
- int max_nesting;
- Object[] literal_values;
- public static final SimpleSymbol dots3Symbol = LispLanguage.dots3_sym;
- /* DEBUGGING:
- void print_template_program(java.util.Vector patternNames,
- java.io.PrintWriter ps) {
- print_template_program(patternNames, ps,
- 0, template_program.length());
- ps.flush();
- }
- void print_template_program(java.util.Vector patternNames,
- java.io.PrintWriter ps,
- int start, int limit) {
- for (int i = start; i < limit; i++) {
- char ch = template_program.charAt(i);
- ps.print(" " + i + ": " + (int)ch);
- if (ch == BUILD_LIST1)
- ps.println (" - LIST1");
- else if (ch == BUILD_NIL)
- ps.println (" - NIL");
- else if (ch == BUILD_SYNTAX)
- ps.println (" - SYNTAX");
- else if ((ch & 7) == BUILD_DOTS) {
- int var_num = ch >> 3;
- ps.print(" - DOTS (var: ");
- ps.print(var_num);
- if (patternNames != null
- && var_num >= 0 && var_num < patternNames.size()) {
- ps.print(" = ");
- ps.print(patternNames.elementAt(var_num));
- }
- ps.println(')');
- } else if (ch == BUILD_VECTOR)
- ps.println (" - VECTOR");
- else if ((ch & 7) == BUILD_CONS)
- ps.println (" - CONS "+(ch >> 3));
- else if ((ch & 7) == BUILD_LITERAL) {
- int lit_num = ch >> 3;
- ps.print (" - literal[" + lit_num + "]: ");
- if (literal_values == null || literal_values.length <= lit_num
- || lit_num < 0)
- ps.print("??");
- else
- DisplayFormat.schemeWriteFormat.writeObject(literal_values[lit_num], (Consumer) ps);
- ps.println();
- } else if ((ch & 6) == BUILD_VAR) { // Also catches BUILD_VAR_CAR.
- int var_num = ch >> 3;
- ps.print(((ch & 7) == BUILD_VAR ? " - VAR[" : " - VAR_CAR[")
- + var_num + "]");
- if (patternNames != null
- && var_num >= 0 && var_num < patternNames.size())
- ps.print(": " + patternNames.elementAt(var_num));
- ps.println();
- } else
- ps.println (" - ???");
- }
- }
- END DEBUGGING */
- protected SyntaxTemplate() {
- }
- public SyntaxTemplate(String patternNesting, String template_program,
- Object[] literal_values, int max_nesting) {
- this.patternNesting = patternNesting;
- this.template_program = template_program;
- this.literal_values = literal_values;
- this.max_nesting = max_nesting;
- }
- public SyntaxTemplate(Object template, SyntaxForm syntax,
- Object ellipsis, Translator tr) {
- this.patternNesting = tr == null || tr.patternScope == null ? ""
- : tr.patternScope.patternNesting.toString();
- savedScope = tr.currentScope();
- if (savedScope instanceof PatternScope)
- savedScope = savedScope.getOuter();
- StringBuilder program = new StringBuilder();
- java.util.Vector literals_vector = new java.util.Vector ();
- IdentityHashMap seen = new IdentityHashMap();
- convert_template(template, syntax,
- program, 0, literals_vector, seen, false, ellipsis, tr);
- this.template_program = program.toString();
- this.literal_values = new Object[literals_vector.size ()];
- literals_vector.copyInto (this.literal_values);
- /* DEBUGGING:
- OutPort err = OutPort.errDefault();
- err.print("{translated template");
- Macro macro = tr.currentMacroDefinition;
- if (macro != null) {
- err.print(" for ");
- err.print(macro);
- }
- if (tr != null && tr.patternScope != null) {
- err.println(" - ");
- print_template_program (tr.patternScope.pattern_names, err);
- }
- err.println ('}');
- */
- }
- /** Recursively translate a syntax-rule template to a template program.
- * @param form the template from the syntax-rule
- * @param syntax if non-null, the closest surrounding <code>SyntaxForm</code>
- * @param template_program (output) the translated template
- * @param nesting the depth of ... we are inside
- * @param literals_vector (output) the literal data in the template
- * @param tr the current Translator
- * @return the index of a pattern variable (in <code>pattern_names</code>)
- * that is nested at least as much as <code>nesting</code>;
- * if there is none such, -1 if there is any pattern variable or ellipsis;
- * and -2 if the is no pattern variable or ellipsis.
- */
- private int convert_template(Object form,
- SyntaxForm syntax,
- StringBuilder template_program,
- int nesting,
- java.util.Vector literals_vector,
- IdentityHashMap seen,
- boolean isVector,
- Object ellipsis,
- Translator tr) {
- Object unseeNeeded = null;
- if (form instanceof Pair || form instanceof FVector) {
- if (seen.containsKey(form)) {
- // FIXME cycles are OK if data are literal.
- // Cycles in non-literal data could probably also be
- // made to work with appropriate BUILD opcodes.
- // However, that seems more trouble than it's worth.
- tr.syntaxError("self-referential (cyclic) syntax template - "+form);
- return -2;
- }
- seen.put(form, form);
- unseeNeeded = form;
- }
- int r = xconvert_template(form, syntax, template_program, nesting,
- literals_vector, seen,
- isVector, ellipsis, tr);
- if (unseeNeeded != null)
- seen.remove(unseeNeeded);
- return r;
- }
- private int xconvert_template(Object form,
- SyntaxForm syntax,
- StringBuilder template_program,
- int nesting,
- java.util.Vector literals_vector,
- IdentityHashMap seen,
- boolean isVector,
- Object ellipsis,
- Translator tr) {
- while (form instanceof SyntaxForm) {
- syntax = (SyntaxForm) form;
- form = syntax.getDatum();
- }
- if (form instanceof Pair) {
- Pair pair = (Pair) form;
- int save_pc = template_program.length();
- Object car = pair.getCar();
- // Look for (... XXX) and translate that to XXX
- if (SyntaxPattern.literalIdentifierEq(car,
- syntax==null?null:syntax.getScope(), ellipsis, null)) {
- Object cdr = pair.getCdr();
- if (cdr instanceof Pair) {
- Pair cdr_pair = (Pair) cdr;
- if (cdr_pair.getCdr() == LList.Empty) {
- convert_template(cdr_pair.getCar(), syntax,
- template_program, nesting,
- literals_vector, seen,
- false, null, tr);
- return -1;
- }
- }
- }
- int save_literals = literals_vector.size();
-
- // This may get patched to a BUILD_CONS.
- template_program.append((char) BUILD_LIST1);
- int num_dots3 = 0;
- Object rest = pair.getCdr();
- while (rest instanceof Pair) {
- Pair p = (Pair) rest;
- if (! SyntaxPattern.literalIdentifierEq(p.getCar(), null,
- ellipsis, null))
- break;
- num_dots3++;
- rest = p.getCdr();
- template_program.append((char) BUILD_DOTS); // to be patched.
- }
- int ret_car = convert_template(car, syntax, template_program,
- nesting + num_dots3,
- literals_vector, seen, false, ellipsis, tr);
- int ret_cdr = -2;
- if (rest != LList.Empty) {
- int delta = template_program.length() - save_pc - 1;
- template_program.setCharAt(save_pc,
- (char)((delta<<3)+BUILD_CONS));
- ret_cdr = convert_template (rest, syntax,
- template_program, nesting,
- literals_vector, seen, isVector, ellipsis, tr);
- }
- if (num_dots3 > 0) {
- if (ret_car < 0)
- tr.syntaxError ("... follows template with no suitably-nested pattern variable");
- for (int i = num_dots3; --i >= 0; ) {
- char op = (char) ((ret_car << 3) + BUILD_DOTS);
- template_program.setCharAt(save_pc+i + 1, op);
- int n = nesting+num_dots3;
- if (n >= max_nesting)
- max_nesting = n;
- }
- }
- if (ret_car >= 0)
- return ret_car;
- if (ret_cdr >= 0)
- return ret_cdr;
- if (ret_car == -1 || ret_cdr == -1)
- return -1;
- if (isVector)
- return -2;
- // There is no pattern variable in 'form', so treat it as literal.
- // This is optimization to group non-substrituted "chunks"
- // as a single literal and a single SyntaxForm value.
- literals_vector.setSize(save_literals);
- template_program.setLength(save_pc);
- } else if (form instanceof FVector) {
- template_program.append((char) BUILD_VECTOR);
- return convert_template(LList.makeList((FVector) form), syntax,
- template_program, nesting,
- literals_vector, seen, true, ellipsis, tr);
- } else if (form == LList.Empty) {
- template_program.append((char) BUILD_NIL);
- return -2;
- } else if (form instanceof Symbol
- && tr != null && tr.patternScope != null) {
- int pattern_var_num = indexOf(tr.patternScope.pattern_names, form);
- if (pattern_var_num >= 0) {
- int var_nesting = patternNesting.charAt(pattern_var_num);
- int op = (var_nesting & 1) != 0 ? BUILD_VAR_CAR : BUILD_VAR;
- var_nesting >>= 1;
- // R4RS requires that the nesting be equal.
- // We allow an extension here, since it allows potentially-useful
- // rules like (x (y ...) ...) => (((x y) ...) ...)
- if (var_nesting > nesting)
- tr.syntaxError ("inconsistent ... nesting of " + form);
- template_program.append((char) (op + 8 * pattern_var_num));
- return var_nesting == nesting ? pattern_var_num : -1;
- }
- // else treated quoted symbol as literal:
- }
- Object xform = tr.namespaceResolve(form);
- Macro macro = tr.currentMacroDefinition;
- if (xform instanceof Symbol && macro != null
- && macro.capturedScope instanceof ModuleExp) {
- tr.noteAccess(xform, tr.currentScope());
- }
- form = SyntaxForms.makeWithTemplate(syntax, form); // Usually a no-op.
- if (template_program.length() == 0
- && form instanceof PairWithPosition) {
- // If the top-level result is a PairWithPosition, that conflicts
- // with setting the application-site line number in Macro#expand.
- PairWithPosition pform = (PairWithPosition) form;
- form = new Pair(pform.getCar(), pform.getCdr());
- }
- int literals_index = indexOf(literals_vector, form);
- if (literals_index < 0) {
- literals_index = literals_vector.size ();
- literals_vector.addElement(form);
- }
- if (! (form instanceof SyntaxForm) && form != ellipsis
- && ! (form instanceof CharSequence
- || form instanceof Number
- || form instanceof Boolean))
- template_program.append((char) (BUILD_SYNTAX));
- template_program.append((char) (BUILD_LITERAL + 8 * literals_index));
- return form == ellipsis ? -1 : -2;
- }
- /** Similar to vec.indexOf(elem), but uses == (not equals) to compare. */
- static int indexOf(java.util.Vector vec, Object elem) {
- int len = vec.size();
- for (int i = 0; i < len; i++) {
- if (vec.elementAt(i) == elem)
- return i;
- }
- return -1;
- }
- /** The the current repeat count. */
- private int get_count(Object var, int nesting, int[] indexes) {
- for (int level = 0; level < nesting; level++)
- var = ((Object[]) var) [indexes[level]];
- Object[] var_array = (Object[]) var;
- return var_array.length;
- }
- /** Expand this template
- * The compiler translates <code>(syntax <var>template</var>)</code>
- * to a call to this method.
- */
- public Object execute(Object[] vars, TemplateScope templateScope) {
- if (false) { // DEBUGGING
- OutPort err = OutPort.errDefault();
- err.print("{Expand template in ");
- err.print(((Translator) Compilation.getCurrent()).getCurrentSyntax());
- err.print(" tscope: ");
- err.print(templateScope);
- if (false && vars != null) {
- err.print(" vars: ");
- for (int i = 0; i < vars.length; i++) {
- err.println();
- err.print(" " + i +" : ");
- DisplayFormat.schemeWriteFormat.writeObject(vars[i], err);
- }
- }
- err.println('}');
- }
- Object result = execute(0, vars, 0, new int[max_nesting],
- (Translator) Compilation.getCurrent(),
- templateScope);
- if (false) { // DEBUGGING:
- OutPort err = OutPort.errDefault();
- err.startLogicalBlock("", false, "}");
- err.print("{Expansion of syntax template ");
- err.print(((Translator) Compilation.getCurrent()).getCurrentSyntax());
- err.print(": ");
- err.writeBreakLinear();
- DisplayFormat.schemeWriteFormat.writeObject(result, err);
- err.endLogicalBlock("}");
- err.println();
- err.flush();
- }
- return result;
- }
- public Object execute(Object[] vars, Translator tr) {
- return execute(0, vars, 0, new int[max_nesting], tr, TemplateScope.make(tr, savedScope));
- }
- Object get_var(int var_num, Object[] vars, int[] indexes, Translator tr) {
- Object var = vars [var_num];
- if (var_num < patternNesting.length()) {
- int var_nesting = (int) patternNesting.charAt(var_num) >> 1;
- for (int level = 0; level < var_nesting; level++) {
- Object[] varr = (Object[]) var;
- int ind = indexes[level];
- if (ind >= varr.length) {
- Syntax macro = tr.getCurrentSyntax();
- String mname = macro == null ? null : macro.getName();
- if (mname == null)
- mname = "<unknown>";
- tr.syntaxError("inconsistent use of ellipsis variable in macro "+mname);
- return LList.list1(var);
- }
- var = varr[ind];
- }
- }
- return var;
- }
- /** Similar to execute, but return is wrapped in a list.
- * Normally the result is a single Pair, BUILD_DOTS can return zero
- * or many Pairs. */
- LList executeToList(int pc, Object[] vars, int nesting, int[] indexes,
- Translator tr, TemplateScope templateScope) {
- int pc0 = pc;
- int ch = template_program.charAt(pc);
- while ((ch & 7) == BUILD_WIDE)
- ch = ((ch - BUILD_WIDE) << 13) | template_program.charAt(++pc);
- if ((ch & 7) == BUILD_VAR_CAR) {
- Pair p = (Pair) get_var(ch >> 3, vars, indexes, tr);
- return Translator.makePair(p, p.getCar(), LList.Empty);
- } else if ((ch & 7) == BUILD_DOTS) {
- int var_num = (int) (ch >> 3);
- Object var = vars[var_num];
- int count = get_count(var, nesting, indexes);
- LList result = LList.Empty;
- Pair last = null; // Final Pair of result list, or null.
- pc++;
- for (int j = 0; j < count; j++) {
- indexes[nesting] = j;
- LList list
- = executeToList(pc, vars, nesting + 1, indexes, tr, templateScope);
- if (last == null)
- result = list;
- else
- last.setCdrBackdoor(list);
- // Normally list is a single Pair, but if it is multiple Pairs,
- // find the last Pair so we can properly splice everything.
- while (list instanceof Pair)
- {
- last = (Pair) list;
- list = (LList) last.getCdr();
- }
- }
- return result;
- }
- Object v = execute(pc0, vars, nesting, indexes, tr, templateScope);
- return new Pair(v, LList.Empty);
- }
- /**
- * @param nesting number of levels of ... we are nested inside
- * @param indexes element i (where i in [0 .. nesting-1] specifies
- * the iteration index for the i'level of nesting
- */
- Object execute(int pc, Object[] vars, int nesting, int[] indexes,
- Translator tr, TemplateScope templateScope) {
- int ch = template_program.charAt(pc);
- /* DEBUGGING:
- System.err.print ("{execute template pc:"+pc
- + " ch:"+(int)ch+" nesting:[");
- for (int level=0; level < nesting; level++)
- System.err.print ((level > 0 ? " " : "") + indexes[level]);
- System.err.println("]}");
- */
- while ((ch & 7) == BUILD_WIDE)
- ch = ((ch - BUILD_WIDE) << 13) | template_program.charAt(++pc);
- if (ch == BUILD_LIST1) {
- return executeToList(pc+1, vars, nesting, indexes, tr, templateScope);
- } else if (ch == BUILD_NIL)
- return LList.Empty;
- else if (ch == BUILD_SYNTAX) {
- Object v = execute(pc+1, vars, nesting, indexes, tr, templateScope);
- return SyntaxForms.makeForm(v, templateScope);
- } else if ((ch & 7) == BUILD_CONS) {
- Pair p = null;
- Object result = null;
- for (;;) {
- pc++;
- Object q
- = executeToList(pc, vars, nesting, indexes, tr, templateScope);
- if (p == null)
- result = q;
- else
- p.setCdrBackdoor(q);
- while (q instanceof Pair) {
- p = (Pair) q;
- q = p.getCdr();
- }
- pc += ch >> 3;
- ch = template_program.charAt(pc);
- if ((ch & 7) != BUILD_CONS)
- break;
- }
- Object cdr = execute(pc, vars, nesting, indexes, tr, templateScope);
- if (p == null)
- result = cdr;
- else
- p.setCdrBackdoor(cdr);
- return result;
- } else if (ch == BUILD_VECTOR) {
- Object el = execute(pc+1, vars, nesting, indexes, tr, templateScope);
- return new FVector((LList) el);
- } else if ((ch & 7) == BUILD_LITERAL) {
- int lit_no = ch >> 3;
- /* DEBUGGING:
- System.err.println("-- insert literal#"+lit_no
- +": "+literal_values[lit_no]);
- */
- return literal_values[lit_no];
- } else if ((ch & 6) == BUILD_VAR) { // Also handles BUILD_VAR_CAR.
- Object var = get_var(ch >> 3, vars, indexes, tr);
- if ((ch & 7) == BUILD_VAR_CAR)
- var = ((Pair) var).getCar();
- return var;
- } else
- throw new Error("unknown template code: "+((int) ch)+" at "+pc);
- }
- /**
- * @serialData
- */
- public void writeExternal(ObjectOutput out) throws IOException {
- out.writeObject(patternNesting);
- out.writeObject(template_program);
- out.writeObject(literal_values);
- out.writeInt(max_nesting);
- }
- public void readExternal(ObjectInput in)
- throws IOException, ClassNotFoundException {
- patternNesting = (String) in.readObject();
- template_program = (String) in.readObject();
- literal_values = (Object[]) in.readObject();
- max_nesting = in.readInt();
- }
- }
|