SyntaxTemplate.java 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. package kawa.lang;
  2. import gnu.lists.*;
  3. import java.io.*;
  4. import gnu.mapping.*;
  5. import gnu.expr.*;
  6. import gnu.kawa.functions.DisplayFormat;
  7. import gnu.kawa.io.OutPort;
  8. import java.util.*;
  9. import gnu.kawa.lispexpr.LispLanguage;
  10. /** The translated form of a <code>(syntax <var>template</var>)</code>. */
  11. public class SyntaxTemplate implements Externalizable {
  12. /** A <code>syntax</code> or <code>syntax-rules</code> template is
  13. * translated into a "template program."
  14. * The template program is a simple bytecode stored in a string.
  15. * The encoding is designed so that instructions are normally
  16. * in the range 1..127, which makes the <code>CONSTANT_Utf8</code> encoding
  17. * used in <code>.class</code> files compact.
  18. * The following <code>BUILD_XXX</code> are the "opcode" of the encoding,
  19. * stored in the low-order 3 bits of a <code>char</code>.
  20. */
  21. String template_program;
  22. ScopeExp savedScope;
  23. /** Template instructions that don't have an operand value. */
  24. static final int BUILD_MISC = 0;
  25. /** Make following operand into a 1-element list. */
  26. static final int BUILD_LIST1 = (1<<3)+BUILD_MISC;
  27. static final int BUILD_NIL = (2<<3)+BUILD_MISC;
  28. /** Wrap following sub-expression in a SyntaxForm. */
  29. static final int BUILD_SYNTAX = (3<<3)+BUILD_MISC;
  30. /** Build a vector (an <code>FVector</code>) from following sub-expression.
  31. * The latter must evaluate to a list. */
  32. static final int BUILD_VECTOR = (5<<3)+BUILD_MISC;
  33. /** Instruction to create a <code>Pair</code> from sub-expressions.
  34. * Instruction <code>BUILD_CONS+8*delta</code> is followed by a
  35. * sub-expression for the <code>car</code>
  36. * (whose length is <code>delta</code> chars),
  37. * followed by the expression for the <code>cdr</code>. */
  38. static final int BUILD_CONS = 1;
  39. /** Instruction BUILD_VAR+8*i pushes vars[i].
  40. * This array contains the values of pattern variables. */
  41. final static int BUILD_VAR = 2; // Must be an even number.
  42. /** Instruction BUILD_VAR_CAR+8*i pushes car(vars[i]).
  43. * It assumes that vars[i] is actually a pair whose car was the
  44. * matched pattern variable. (This is done so we can preserve
  45. * <code>PairWithPosition</code> source positions). */
  46. final static int BUILD_VAR_CAR = BUILD_VAR+1;
  47. /** Instruction BUILD_LITERAL+8*i pushes literal_values[i]. */
  48. final static int BUILD_LITERAL = 4;
  49. /** Instruction <code>BUILD_DOTS+8*i</code> repeats a sub-expression.
  50. * The value <code>i</code> is a variable index of a pattern variable
  51. * of at least the needed depth. The result is spliced in. */
  52. final static int BUILD_DOTS = 5;
  53. /** Unfinished support for "operand" values that need more than 13 bits. */
  54. final static int BUILD_WIDE = 7;
  55. /** Map variable to ellipsis nesting depth.
  56. * The nesting depth of the <code>i</code>'th pattern variable
  57. * is <code>(int) patternNesting.charAt(i)/2</code>.
  58. * The low-order bit indicates that if matched value is the <code>car</code>
  59. * of the value saved in the <code>vars</code> array.
  60. * (We use a <code>String</code> because it is compact both at runtime
  61. * and in <code>.class</code> files. */
  62. String patternNesting;
  63. int max_nesting;
  64. Object[] literal_values;
  65. public static final SimpleSymbol dots3Symbol = LispLanguage.dots3_sym;
  66. /* DEBUGGING:
  67. void print_template_program(java.util.Vector patternNames,
  68. java.io.PrintWriter ps) {
  69. print_template_program(patternNames, ps,
  70. 0, template_program.length());
  71. ps.flush();
  72. }
  73. void print_template_program(java.util.Vector patternNames,
  74. java.io.PrintWriter ps,
  75. int start, int limit) {
  76. for (int i = start; i < limit; i++) {
  77. char ch = template_program.charAt(i);
  78. ps.print(" " + i + ": " + (int)ch);
  79. if (ch == BUILD_LIST1)
  80. ps.println (" - LIST1");
  81. else if (ch == BUILD_NIL)
  82. ps.println (" - NIL");
  83. else if (ch == BUILD_SYNTAX)
  84. ps.println (" - SYNTAX");
  85. else if ((ch & 7) == BUILD_DOTS) {
  86. int var_num = ch >> 3;
  87. ps.print(" - DOTS (var: ");
  88. ps.print(var_num);
  89. if (patternNames != null
  90. && var_num >= 0 && var_num < patternNames.size()) {
  91. ps.print(" = ");
  92. ps.print(patternNames.elementAt(var_num));
  93. }
  94. ps.println(')');
  95. } else if (ch == BUILD_VECTOR)
  96. ps.println (" - VECTOR");
  97. else if ((ch & 7) == BUILD_CONS)
  98. ps.println (" - CONS "+(ch >> 3));
  99. else if ((ch & 7) == BUILD_LITERAL) {
  100. int lit_num = ch >> 3;
  101. ps.print (" - literal[" + lit_num + "]: ");
  102. if (literal_values == null || literal_values.length <= lit_num
  103. || lit_num < 0)
  104. ps.print("??");
  105. else
  106. DisplayFormat.schemeWriteFormat.writeObject(literal_values[lit_num], (Consumer) ps);
  107. ps.println();
  108. } else if ((ch & 6) == BUILD_VAR) { // Also catches BUILD_VAR_CAR.
  109. int var_num = ch >> 3;
  110. ps.print(((ch & 7) == BUILD_VAR ? " - VAR[" : " - VAR_CAR[")
  111. + var_num + "]");
  112. if (patternNames != null
  113. && var_num >= 0 && var_num < patternNames.size())
  114. ps.print(": " + patternNames.elementAt(var_num));
  115. ps.println();
  116. } else
  117. ps.println (" - ???");
  118. }
  119. }
  120. END DEBUGGING */
  121. protected SyntaxTemplate() {
  122. }
  123. public SyntaxTemplate(String patternNesting, String template_program,
  124. Object[] literal_values, int max_nesting) {
  125. this.patternNesting = patternNesting;
  126. this.template_program = template_program;
  127. this.literal_values = literal_values;
  128. this.max_nesting = max_nesting;
  129. }
  130. public SyntaxTemplate(Object template, SyntaxForm syntax,
  131. Object ellipsis, Translator tr) {
  132. this.patternNesting = tr == null || tr.patternScope == null ? ""
  133. : tr.patternScope.patternNesting.toString();
  134. savedScope = tr.currentScope();
  135. if (savedScope instanceof PatternScope)
  136. savedScope = savedScope.getOuter();
  137. StringBuilder program = new StringBuilder();
  138. java.util.Vector literals_vector = new java.util.Vector ();
  139. IdentityHashMap seen = new IdentityHashMap();
  140. convert_template(template, syntax,
  141. program, 0, literals_vector, seen, false, ellipsis, tr);
  142. this.template_program = program.toString();
  143. this.literal_values = new Object[literals_vector.size ()];
  144. literals_vector.copyInto (this.literal_values);
  145. /* DEBUGGING:
  146. OutPort err = OutPort.errDefault();
  147. err.print("{translated template");
  148. Macro macro = tr.currentMacroDefinition;
  149. if (macro != null) {
  150. err.print(" for ");
  151. err.print(macro);
  152. }
  153. if (tr != null && tr.patternScope != null) {
  154. err.println(" - ");
  155. print_template_program (tr.patternScope.pattern_names, err);
  156. }
  157. err.println ('}');
  158. */
  159. }
  160. /** Recursively translate a syntax-rule template to a template program.
  161. * @param form the template from the syntax-rule
  162. * @param syntax if non-null, the closest surrounding <code>SyntaxForm</code>
  163. * @param template_program (output) the translated template
  164. * @param nesting the depth of ... we are inside
  165. * @param literals_vector (output) the literal data in the template
  166. * @param tr the current Translator
  167. * @return the index of a pattern variable (in <code>pattern_names</code>)
  168. * that is nested at least as much as <code>nesting</code>;
  169. * if there is none such, -1 if there is any pattern variable or ellipsis;
  170. * and -2 if the is no pattern variable or ellipsis.
  171. */
  172. private int convert_template(Object form,
  173. SyntaxForm syntax,
  174. StringBuilder template_program,
  175. int nesting,
  176. java.util.Vector literals_vector,
  177. IdentityHashMap seen,
  178. boolean isVector,
  179. Object ellipsis,
  180. Translator tr) {
  181. Object unseeNeeded = null;
  182. if (form instanceof Pair || form instanceof FVector) {
  183. if (seen.containsKey(form)) {
  184. // FIXME cycles are OK if data are literal.
  185. // Cycles in non-literal data could probably also be
  186. // made to work with appropriate BUILD opcodes.
  187. // However, that seems more trouble than it's worth.
  188. tr.syntaxError("self-referential (cyclic) syntax template - "+form);
  189. return -2;
  190. }
  191. seen.put(form, form);
  192. unseeNeeded = form;
  193. }
  194. int r = xconvert_template(form, syntax, template_program, nesting,
  195. literals_vector, seen,
  196. isVector, ellipsis, tr);
  197. if (unseeNeeded != null)
  198. seen.remove(unseeNeeded);
  199. return r;
  200. }
  201. private int xconvert_template(Object form,
  202. SyntaxForm syntax,
  203. StringBuilder template_program,
  204. int nesting,
  205. java.util.Vector literals_vector,
  206. IdentityHashMap seen,
  207. boolean isVector,
  208. Object ellipsis,
  209. Translator tr) {
  210. while (form instanceof SyntaxForm) {
  211. syntax = (SyntaxForm) form;
  212. form = syntax.getDatum();
  213. }
  214. if (form instanceof Pair) {
  215. Pair pair = (Pair) form;
  216. int save_pc = template_program.length();
  217. Object car = pair.getCar();
  218. // Look for (... XXX) and translate that to XXX
  219. if (SyntaxPattern.literalIdentifierEq(car,
  220. syntax==null?null:syntax.getScope(), ellipsis, null)) {
  221. Object cdr = pair.getCdr();
  222. if (cdr instanceof Pair) {
  223. Pair cdr_pair = (Pair) cdr;
  224. if (cdr_pair.getCdr() == LList.Empty) {
  225. convert_template(cdr_pair.getCar(), syntax,
  226. template_program, nesting,
  227. literals_vector, seen,
  228. false, null, tr);
  229. return -1;
  230. }
  231. }
  232. }
  233. int save_literals = literals_vector.size();
  234. // This may get patched to a BUILD_CONS.
  235. template_program.append((char) BUILD_LIST1);
  236. int num_dots3 = 0;
  237. Object rest = pair.getCdr();
  238. while (rest instanceof Pair) {
  239. Pair p = (Pair) rest;
  240. if (! SyntaxPattern.literalIdentifierEq(p.getCar(), null,
  241. ellipsis, null))
  242. break;
  243. num_dots3++;
  244. rest = p.getCdr();
  245. template_program.append((char) BUILD_DOTS); // to be patched.
  246. }
  247. int ret_car = convert_template(car, syntax, template_program,
  248. nesting + num_dots3,
  249. literals_vector, seen, false, ellipsis, tr);
  250. int ret_cdr = -2;
  251. if (rest != LList.Empty) {
  252. int delta = template_program.length() - save_pc - 1;
  253. template_program.setCharAt(save_pc,
  254. (char)((delta<<3)+BUILD_CONS));
  255. ret_cdr = convert_template (rest, syntax,
  256. template_program, nesting,
  257. literals_vector, seen, isVector, ellipsis, tr);
  258. }
  259. if (num_dots3 > 0) {
  260. if (ret_car < 0)
  261. tr.syntaxError ("... follows template with no suitably-nested pattern variable");
  262. for (int i = num_dots3; --i >= 0; ) {
  263. char op = (char) ((ret_car << 3) + BUILD_DOTS);
  264. template_program.setCharAt(save_pc+i + 1, op);
  265. int n = nesting+num_dots3;
  266. if (n >= max_nesting)
  267. max_nesting = n;
  268. }
  269. }
  270. if (ret_car >= 0)
  271. return ret_car;
  272. if (ret_cdr >= 0)
  273. return ret_cdr;
  274. if (ret_car == -1 || ret_cdr == -1)
  275. return -1;
  276. if (isVector)
  277. return -2;
  278. // There is no pattern variable in 'form', so treat it as literal.
  279. // This is optimization to group non-substrituted "chunks"
  280. // as a single literal and a single SyntaxForm value.
  281. literals_vector.setSize(save_literals);
  282. template_program.setLength(save_pc);
  283. } else if (form instanceof FVector) {
  284. template_program.append((char) BUILD_VECTOR);
  285. return convert_template(LList.makeList((FVector) form), syntax,
  286. template_program, nesting,
  287. literals_vector, seen, true, ellipsis, tr);
  288. } else if (form == LList.Empty) {
  289. template_program.append((char) BUILD_NIL);
  290. return -2;
  291. } else if (form instanceof Symbol
  292. && tr != null && tr.patternScope != null) {
  293. int pattern_var_num = indexOf(tr.patternScope.pattern_names, form);
  294. if (pattern_var_num >= 0) {
  295. int var_nesting = patternNesting.charAt(pattern_var_num);
  296. int op = (var_nesting & 1) != 0 ? BUILD_VAR_CAR : BUILD_VAR;
  297. var_nesting >>= 1;
  298. // R4RS requires that the nesting be equal.
  299. // We allow an extension here, since it allows potentially-useful
  300. // rules like (x (y ...) ...) => (((x y) ...) ...)
  301. if (var_nesting > nesting)
  302. tr.syntaxError ("inconsistent ... nesting of " + form);
  303. template_program.append((char) (op + 8 * pattern_var_num));
  304. return var_nesting == nesting ? pattern_var_num : -1;
  305. }
  306. // else treated quoted symbol as literal:
  307. }
  308. Object xform = tr.namespaceResolve(form);
  309. Macro macro = tr.currentMacroDefinition;
  310. if (xform instanceof Symbol && macro != null
  311. && macro.capturedScope instanceof ModuleExp) {
  312. tr.noteAccess(xform, tr.currentScope());
  313. }
  314. form = SyntaxForms.makeWithTemplate(syntax, form); // Usually a no-op.
  315. if (template_program.length() == 0
  316. && form instanceof PairWithPosition) {
  317. // If the top-level result is a PairWithPosition, that conflicts
  318. // with setting the application-site line number in Macro#expand.
  319. PairWithPosition pform = (PairWithPosition) form;
  320. form = new Pair(pform.getCar(), pform.getCdr());
  321. }
  322. int literals_index = indexOf(literals_vector, form);
  323. if (literals_index < 0) {
  324. literals_index = literals_vector.size ();
  325. literals_vector.addElement(form);
  326. }
  327. if (! (form instanceof SyntaxForm) && form != ellipsis
  328. && ! (form instanceof CharSequence
  329. || form instanceof Number
  330. || form instanceof Boolean))
  331. template_program.append((char) (BUILD_SYNTAX));
  332. template_program.append((char) (BUILD_LITERAL + 8 * literals_index));
  333. return form == ellipsis ? -1 : -2;
  334. }
  335. /** Similar to vec.indexOf(elem), but uses == (not equals) to compare. */
  336. static int indexOf(java.util.Vector vec, Object elem) {
  337. int len = vec.size();
  338. for (int i = 0; i < len; i++) {
  339. if (vec.elementAt(i) == elem)
  340. return i;
  341. }
  342. return -1;
  343. }
  344. /** The the current repeat count. */
  345. private int get_count(Object var, int nesting, int[] indexes) {
  346. for (int level = 0; level < nesting; level++)
  347. var = ((Object[]) var) [indexes[level]];
  348. Object[] var_array = (Object[]) var;
  349. return var_array.length;
  350. }
  351. /** Expand this template
  352. * The compiler translates <code>(syntax <var>template</var>)</code>
  353. * to a call to this method.
  354. */
  355. public Object execute(Object[] vars, TemplateScope templateScope) {
  356. if (false) { // DEBUGGING
  357. OutPort err = OutPort.errDefault();
  358. err.print("{Expand template in ");
  359. err.print(((Translator) Compilation.getCurrent()).getCurrentSyntax());
  360. err.print(" tscope: ");
  361. err.print(templateScope);
  362. if (false && vars != null) {
  363. err.print(" vars: ");
  364. for (int i = 0; i < vars.length; i++) {
  365. err.println();
  366. err.print(" " + i +" : ");
  367. DisplayFormat.schemeWriteFormat.writeObject(vars[i], err);
  368. }
  369. }
  370. err.println('}');
  371. }
  372. Object result = execute(0, vars, 0, new int[max_nesting],
  373. (Translator) Compilation.getCurrent(),
  374. templateScope);
  375. if (false) { // DEBUGGING:
  376. OutPort err = OutPort.errDefault();
  377. err.startLogicalBlock("", false, "}");
  378. err.print("{Expansion of syntax template ");
  379. err.print(((Translator) Compilation.getCurrent()).getCurrentSyntax());
  380. err.print(": ");
  381. err.writeBreakLinear();
  382. DisplayFormat.schemeWriteFormat.writeObject(result, err);
  383. err.endLogicalBlock("}");
  384. err.println();
  385. err.flush();
  386. }
  387. return result;
  388. }
  389. public Object execute(Object[] vars, Translator tr) {
  390. return execute(0, vars, 0, new int[max_nesting], tr, TemplateScope.make(tr, savedScope));
  391. }
  392. Object get_var(int var_num, Object[] vars, int[] indexes, Translator tr) {
  393. Object var = vars [var_num];
  394. if (var_num < patternNesting.length()) {
  395. int var_nesting = (int) patternNesting.charAt(var_num) >> 1;
  396. for (int level = 0; level < var_nesting; level++) {
  397. Object[] varr = (Object[]) var;
  398. int ind = indexes[level];
  399. if (ind >= varr.length) {
  400. Syntax macro = tr.getCurrentSyntax();
  401. String mname = macro == null ? null : macro.getName();
  402. if (mname == null)
  403. mname = "<unknown>";
  404. tr.syntaxError("inconsistent use of ellipsis variable in macro "+mname);
  405. return LList.list1(var);
  406. }
  407. var = varr[ind];
  408. }
  409. }
  410. return var;
  411. }
  412. /** Similar to execute, but return is wrapped in a list.
  413. * Normally the result is a single Pair, BUILD_DOTS can return zero
  414. * or many Pairs. */
  415. LList executeToList(int pc, Object[] vars, int nesting, int[] indexes,
  416. Translator tr, TemplateScope templateScope) {
  417. int pc0 = pc;
  418. int ch = template_program.charAt(pc);
  419. while ((ch & 7) == BUILD_WIDE)
  420. ch = ((ch - BUILD_WIDE) << 13) | template_program.charAt(++pc);
  421. if ((ch & 7) == BUILD_VAR_CAR) {
  422. Pair p = (Pair) get_var(ch >> 3, vars, indexes, tr);
  423. return Translator.makePair(p, p.getCar(), LList.Empty);
  424. } else if ((ch & 7) == BUILD_DOTS) {
  425. int var_num = (int) (ch >> 3);
  426. Object var = vars[var_num];
  427. int count = get_count(var, nesting, indexes);
  428. LList result = LList.Empty;
  429. Pair last = null; // Final Pair of result list, or null.
  430. pc++;
  431. for (int j = 0; j < count; j++) {
  432. indexes[nesting] = j;
  433. LList list
  434. = executeToList(pc, vars, nesting + 1, indexes, tr, templateScope);
  435. if (last == null)
  436. result = list;
  437. else
  438. last.setCdrBackdoor(list);
  439. // Normally list is a single Pair, but if it is multiple Pairs,
  440. // find the last Pair so we can properly splice everything.
  441. while (list instanceof Pair)
  442. {
  443. last = (Pair) list;
  444. list = (LList) last.getCdr();
  445. }
  446. }
  447. return result;
  448. }
  449. Object v = execute(pc0, vars, nesting, indexes, tr, templateScope);
  450. return new Pair(v, LList.Empty);
  451. }
  452. /**
  453. * @param nesting number of levels of ... we are nested inside
  454. * @param indexes element i (where i in [0 .. nesting-1] specifies
  455. * the iteration index for the i'level of nesting
  456. */
  457. Object execute(int pc, Object[] vars, int nesting, int[] indexes,
  458. Translator tr, TemplateScope templateScope) {
  459. int ch = template_program.charAt(pc);
  460. /* DEBUGGING:
  461. System.err.print ("{execute template pc:"+pc
  462. + " ch:"+(int)ch+" nesting:[");
  463. for (int level=0; level < nesting; level++)
  464. System.err.print ((level > 0 ? " " : "") + indexes[level]);
  465. System.err.println("]}");
  466. */
  467. while ((ch & 7) == BUILD_WIDE)
  468. ch = ((ch - BUILD_WIDE) << 13) | template_program.charAt(++pc);
  469. if (ch == BUILD_LIST1) {
  470. return executeToList(pc+1, vars, nesting, indexes, tr, templateScope);
  471. } else if (ch == BUILD_NIL)
  472. return LList.Empty;
  473. else if (ch == BUILD_SYNTAX) {
  474. Object v = execute(pc+1, vars, nesting, indexes, tr, templateScope);
  475. return SyntaxForms.makeForm(v, templateScope);
  476. } else if ((ch & 7) == BUILD_CONS) {
  477. Pair p = null;
  478. Object result = null;
  479. for (;;) {
  480. pc++;
  481. Object q
  482. = executeToList(pc, vars, nesting, indexes, tr, templateScope);
  483. if (p == null)
  484. result = q;
  485. else
  486. p.setCdrBackdoor(q);
  487. while (q instanceof Pair) {
  488. p = (Pair) q;
  489. q = p.getCdr();
  490. }
  491. pc += ch >> 3;
  492. ch = template_program.charAt(pc);
  493. if ((ch & 7) != BUILD_CONS)
  494. break;
  495. }
  496. Object cdr = execute(pc, vars, nesting, indexes, tr, templateScope);
  497. if (p == null)
  498. result = cdr;
  499. else
  500. p.setCdrBackdoor(cdr);
  501. return result;
  502. } else if (ch == BUILD_VECTOR) {
  503. Object el = execute(pc+1, vars, nesting, indexes, tr, templateScope);
  504. return new FVector((LList) el);
  505. } else if ((ch & 7) == BUILD_LITERAL) {
  506. int lit_no = ch >> 3;
  507. /* DEBUGGING:
  508. System.err.println("-- insert literal#"+lit_no
  509. +": "+literal_values[lit_no]);
  510. */
  511. return literal_values[lit_no];
  512. } else if ((ch & 6) == BUILD_VAR) { // Also handles BUILD_VAR_CAR.
  513. Object var = get_var(ch >> 3, vars, indexes, tr);
  514. if ((ch & 7) == BUILD_VAR_CAR)
  515. var = ((Pair) var).getCar();
  516. return var;
  517. } else
  518. throw new Error("unknown template code: "+((int) ch)+" at "+pc);
  519. }
  520. /**
  521. * @serialData
  522. */
  523. public void writeExternal(ObjectOutput out) throws IOException {
  524. out.writeObject(patternNesting);
  525. out.writeObject(template_program);
  526. out.writeObject(literal_values);
  527. out.writeInt(max_nesting);
  528. }
  529. public void readExternal(ObjectInput in)
  530. throws IOException, ClassNotFoundException {
  531. patternNesting = (String) in.readObject();
  532. template_program = (String) in.readObject();
  533. literal_values = (Object[]) in.readObject();
  534. max_nesting = in.readInt();
  535. }
  536. }