1 /**
2 * AUTOHIT 2003
3 * Copyright Erich P Gatejen (c) 1989,1997,2003,2004
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or (at
8 * your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Additional license information can be found in the documentation.
19 * @author Erich P Gatejen
20 */
21 package autohit.creator.compiler;
22
23 //import org.apache.commons.collections.ExtendedProperties;
24 import java.util.HashMap;
25 import java.util.ListIterator;
26 import java.util.Stack;
27
28 import org.w3c.dom.CharacterData;
29 import org.w3c.dom.Document;
30 import org.w3c.dom.Element;
31 import org.w3c.dom.Node;
32 import org.w3c.dom.NodeList;
33
34 import autohit.common.AutohitErrorCodes;
35 import autohit.common.NOPair;
36 import autohit.common.NVPair;
37 import autohit.creator.SimLanguage;
38 import autohit.server.SystemContext;
39 import autohit.vm.VMExecutableWrapper;
40 import autohit.vm.i.VMICall;
41 import autohit.vm.i.VMIMethod;
42 import autohit.vm.i.VMIClear;
43 import autohit.vm.i.VMIEval;
44 import autohit.vm.i.VMIExec;
45 import autohit.vm.i.VMIFault;
46 import autohit.vm.i.VMIFetch;
47 import autohit.vm.i.VMIGoto;
48 import autohit.vm.i.VMIIf;
49 import autohit.vm.i.VMIJump;
50 import autohit.vm.i.VMILoad;
51 import autohit.vm.i.VMIMath;
52 import autohit.vm.i.VMIMerge;
53 import autohit.vm.i.VMINew;
54 import autohit.vm.i.VMINop;
55 import autohit.vm.i.VMIRScope;
56 import autohit.vm.i.VMIReduce;
57 import autohit.vm.i.VMIRight;
58 import autohit.vm.i.VMIScope;
59 import autohit.vm.i.VMIStore;
60 import autohit.vm.i.VMISubr;
61 import autohit.vm.i.VMIAssert;
62
63 /**
64 * This is the a Sim compiler. It will compile xml documents that conform
65 * to the "%autohit.dtd.location%/sim.dtd" dtd into a Sim object.
66 * <p>
67 * WARNING!!! For the compiler to work, the sim.dtd must be at the location
68 * specified in the system property autohit.dtd.location.
69 * <p>
70 * The private methods translate Token() matches the textual tokens, as defined in the
71 * dtd to numerics that are used by the rest of the compiler. Any new tokens
72 * need to be added to that method.
73 * <p>
74 * I can think of several more elegant approaches to making this compiler, but until the
75 * XML parsers settle a bit, I'm not going to bother...
76 * <p>
77 * IMPORTANT NOTE!!!! The XML parser WITH DTD ensure instructions are
78 * coded in the proper order. ALL of the code below assumes this!!!
79 * If instructions come out of order or in disallowed places
80 * (like a seek outside of a get), I GUARANTEE you'll get a runaway
81 * compiler...
82 * <p>
83 * Logging will be done to the autohit.creator.sim namespace. It is set for
84 * pretty and
85 *
86 * @author Erich P. Gatejen
87 * @version 1.1
88 * <i>Version History</i>
89 * <code>EPG - Initial - 14Apr03<br>
90 * EPG - Update to add goto and references - 13Jul03</code>
91 *
92 */
93 public class SimCompiler extends XmlCompiler implements SimLanguage {
94
95 private final static String DTD_NAME = "sim.dtd";
96 private final static String MY_TYPE_OF_EXEC = "sim1";
97
98 // We'll do subtractive compares for IF. This might change.
99
100 // Lexical elements and attriutes
101
102 // parse tokens
103
104 /**
105 * The work-in-progress object code
106 */
107 protected VMExecutableWrapper ob;
108
109 /**
110 * Symbol table for lookups
111 */
112 protected HashMap symboltable;
113
114 /**
115 * Stack for fixups
116 */
117 protected Stack fixupstack;
118
119 /**
120 * Local name - UID
121 */
122 private String localname = "UNKNOWN";
123
124
125 /**
126 * Default Constructor. This is the only and default constructor.
127 *
128 * @throws Exception any exception invalidates the compiler.
129 */
130 public SimCompiler() throws Exception {
131 super();
132
133 }
134
135 /**
136 * Constructor. This is the only and default constructor.
137 *
138 * @throws Exception any exception invalidates the compiler.
139 */
140 public SimCompiler(SystemContext sc) throws Exception {
141 super(DTD_NAME, sc);
142 }
143
144 /**
145 * Compile the xml tree into an VMExecutable object.
146 *
147 * We will create a new log for each run, so that we can uniquely
148 * identify them.
149 *
150 * @param xd A parsed XML document.
151 * @return a reference to the target object, in this case it will be a VMExecutableWrapper, or null if it failed.
152 * @see autohit.vm.VMExecutableWrapper
153 */
154 public Object build(Document xd) {
155
156 int idx;
157 NodeList rootChildren;
158 Element itemTree = null;
159 Element codeTree = null;
160 int numNodes;
161 Node scratchNode;
162 String scratchString;
163
164 // Any exception or verification check aborts the compile
165 try {
166
167 // Ok, build our working Sim object
168 ob = new VMExecutableWrapper();
169 ob.create();
170
171 // Create our symbol table and fixup stack
172 symboltable = new HashMap();
173 fixupstack = new Stack();
174
175 // set defaults attributes
176 ob.exec.major = 0;
177
178 // set any default attributes
179 ob.exec.type = MY_TYPE_OF_EXEC;
180 ob.exec.minor = 0;
181 ob.exec.output = null; // assume there is nothign to return
182
183 // Get the root element and normalize
184 Element root = (Element) xd.getDocumentElement();
185 root.normalize();
186
187 // Peal out the <info> and <code> sub-trees
188 rootChildren = (NodeList) root.getChildNodes();
189 numNodes = rootChildren.getLength();
190 while (numNodes > 0) {
191 scratchNode = rootChildren.item(numNodes - 1);
192 if (scratchNode instanceof Element) {
193 scratchString = scratchNode.getNodeName();
194 if (scratchString.charAt(0) == 'i') {
195 itemTree = (Element) scratchNode;
196 } else if (scratchString.charAt(0) == 'c') {
197 codeTree = (Element) scratchNode;
198 }
199 }
200 numNodes--;
201 }
202
203 if (itemTree == null) {
204 runtimeError("Missing infomation <info> block.");
205 }
206
207 if (codeTree == null) {
208 runtimeError("Missing infomation <code> block.");
209 throw new Exception();
210 }
211
212 // Deal with the <info> tree
213 NodeList itemTreeChildren = itemTree.getChildNodes();
214 for (idx = 0; idx < itemTreeChildren.getLength(); idx++) {
215 scratchNode = itemTreeChildren.item(idx);
216
217 // pull only Elements
218 if (scratchNode instanceof Element) {
219 processItem((Element) scratchNode);
220 }
221
222 }
223
224 // Deal with the <code> tree
225 // Basicall, I'm gonna go wtih recursion. I don't think it should
226 // get very deep.
227 try {
228 processCode(codeTree);
229
230 // Put a NOP on the end of the executable
231 ob.emit(new VMINop());
232 ob.clean();
233
234 // fixup goto symbols
235 ListIterator li = fixupstack.listIterator();
236 VMIGoto jcandidate;
237 NOPair nocandidate;
238 Integer currentgoto;
239 while (li.hasNext()) {
240 nocandidate = (NOPair) li.next();
241 if (symboltable.containsKey(nocandidate.n)) {
242 jcandidate = (VMIGoto) nocandidate.o;
243 currentgoto = (Integer) symboltable.get(nocandidate.n);
244 jcandidate.t = currentgoto.intValue();
245 runtimeDebug(
246 "Fixup GOTO for label="
247 + nocandidate.n
248 + " target="
249 + jcandidate.t);
250 } else {
251 runtimeError(
252 "Broken GOTO. No label for "
253 + nocandidate.n
254 + ".");
255 }
256 }
257
258 } catch (Exception e) {
259 // an otherwise uncaught exception. A runaway compiler...
260 runtimeError("FATAL ERROR. Runaway compilation errors. Stopping compile.");
261 ob = null;
262 }
263
264 } catch (Exception e) {
265 myLog.error(
266 "CRITICAL ERROR encountered. Stopping compile of " + localname + ". "
267 + e.toString(),
268 AutohitErrorCodes.CODE_COMPILE_ERROR);
269 myLog.error(e.toString());
270 ob = null; // leave the objectCode as null;
271 }
272
273 // ditch data as it falls out of scope
274 symboltable = null;
275 fixupstack = null;
276
277 // clean up logs
278 int err = numberErrors();
279 runtimeLog.error("Total errors for " + localname + " : " + err);
280 runtimeLog.warning("Total errors for " + localname + " : " + numberWarnings());
281 if (err > 0) {
282 runtimeLog.info("COMPILE FAILED " + localname + " DUE TO ERRORS.");
283 ob = null;
284 }
285 return ob;
286 }
287
288 /**
289 * Processes info section tags.
290 */
291 private void processItem(Element en) throws Exception {
292 String tempText;
293 String name;
294 //runtimeDebug("ENTER --- " + en.getTagName());
295 name = en.getTagName().toLowerCase();
296 NodeList itemTreeChildren;
297 int idx;
298 Node sNode;
299
300 // Parse the token and call a handler
301 if (name.charAt(0) == 'n') {
302 if (name.charAt(1) == 'a') {
303 //NAME
304 tempText = getText(en.getFirstChild());
305 if (tempText == null) {
306 runtimeError("Empty <name> tag.");
307 } else {
308 ob.exec.name = tempText;
309 runtimeDebug("TAG <name> name= " + ob.exec.name);
310 }
311 // optional uid attribute
312 if (en.hasAttribute(ATTR_UID)) {
313 ob.exec.uid = en.getAttribute(ATTR_UID);
314 localname = ob.exec.uid;
315 runtimeDebug("TAG <name> uid= " + ob.exec.uid);
316 }
317 } else {
318 // NOTE - optional
319 tempText = getText(en.getFirstChild());
320 ob.exec.note = tempText;
321 }
322 } else if (name.charAt(0) == 'v') {
323 // VERSION
324 ob.exec.major = 0;
325 try {
326 if (en.hasAttribute(ATTR_VERSIONNUMBER)) {
327 tempText = en.getAttribute(ATTR_VERSIONNUMBER);
328 ob.exec.major = Integer.parseInt(tempText);
329 runtimeDebug("TAG <version> num= " + ob.exec.major);
330 } else {
331 runtimeError("ERROR: TAG<version> Attr num not present.");
332 }
333 } catch (Exception e) {
334 runtimeError("ERROR: TAG<version> Could not parse value for Attr num.");
335 }
336
337 } else if (name.charAt(0) == 'i') {
338 if (name.charAt(1) == 'o') {
339 // IO - Recurse with it's children.
340 itemTreeChildren = en.getChildNodes();
341 for (idx = 0; idx < itemTreeChildren.getLength(); idx++) {
342 sNode = itemTreeChildren.item(idx);
343 if (sNode instanceof Element) {
344 processItem((Element) sNode);
345 }
346 }
347 } else {
348 // INPUT - NOT SUPPORTED
349 }
350
351 } else if (name.charAt(0) == 'b') {
352 // BUFFER - NOT SUPPORTED
353
354 } else if (name.charAt(0) == 'o') {
355 ob.exec.output = new NVPair();
356 // OUTPUT - Specifies the output variable
357 ob.exec.output.name = en.getAttribute(ATTR_NAME);
358 if (en.hasAttribute(ATTR_TYPE)) {
359 ob.exec.output.value = en.getAttribute(ATTR_TYPE);
360 }
361
362 } else {
363 runtimeError(
364 "Software Detected Fault: creator.compiler.SimCompiler.processItem(). The textual token ["
365 + en.getNodeName()
366 + "] should have NOT reached this code. Check the XML DTD associated with the SimCompiler.");
367 throw (
368 new Exception("Software Detected Fault in creator.compiler.SimCompiler.processItem()."));
369 }
370
371 //DEBUG
372 //runtimeDebug("EXIT --- " + en.getTagName());
373 }
374
375 /**
376 * Every raw element will enter here. Bascially, it dispatches it to it's specific
377 * handler. It will do so for every child in the passed node.
378 */
379 private void processCode(Element en) throws Exception {
380
381 Element child;
382 String name;
383 int idx;
384 Node scratchNode;
385 NodeList itemTreeChildren = en.getChildNodes();
386
387 //DEBUG
388 //runtimeDebug("ENTER --- " + en.getTagName());
389
390 for (idx = 0; idx < itemTreeChildren.getLength(); idx++) {
391
392 scratchNode = itemTreeChildren.item(idx);
393
394 // Only process elements
395 if (!(scratchNode instanceof Element))
396 continue;
397
398 // Clean it
399 child = (Element) scratchNode;
400 name = child.getTagName().toLowerCase();
401
402 // Parse the token and call a handler
403 // Just not enough tokens to justify a state translation
404 if (name.charAt(0) == 'a') {
405 handleAssert(child);
406 } else if (name.charAt(0) == 'b') {
407 if (name.charAt(1) == 'l') {
408 handleBlock(child);
409 } else {
410 handleBuffer(child);
411 }
412 } else if (name.charAt(0) == 'c') {
413 handleCall(child);
414 } else if (name.charAt(0) == 'e') {
415 handleExec(child);
416 } else if (name.charAt(0) == 'f') {
417 handleFor(child);
418 } else if (name.charAt(0) == 'g') {
419 handleGoto(child);
420 } else if (name.charAt(0) == 'i') {
421 if (name.charAt(1) == 'n') {
422 handleInput(child);
423 } else {
424 handleIf(child);
425 }
426 } else if (name.charAt(0) == 'l') {
427 handleLabel(child);
428 } else if (name.charAt(0) == 'm') {
429 if (name.charAt(1) == 'e') {
430 handleMethod(child);
431 } else {
432 handleMath(child);
433 }
434 } else if (name.charAt(0) == 's') {
435 if (name.charAt(1) == 'e') {
436 handleSet(child);
437 } else {
438 handleSubroutine(child);
439 }
440 } else if (name.charAt(0) == 'w') {
441 handleWhile(child);
442 } else if (name.charAt(0) == 'r') {
443 handleReturn(child);
444 } else {
445 runtimeError(
446 "Software Detected Fault: autohit.creator.compiler.SimCompiler(). The textual token ["
447 + en.getNodeName()
448 + "] should have NOT reached this code. Check the XML DTD associated with the SimCompiler.");
449 throw (
450 new Exception("Software Detected Fault in creator.compiler.SimCompiler.processItem()."));
451 }
452 }
453 //DEBUG
454 //runtimeDebug("EXIT --- " + en.getTagName());
455 }
456
457 // == ===============================================================================
458 // == = TOKEN HANDLERS =
459 // == ===============================================================================
460
461 /**
462 * handle a block. easy enough. just scope it!.
463 * MICROCODE
464 * 1- i.scope
465 * 2- ANY
466 * 3- i.rscope
467 */
468 private void handleBlock(Element en) {
469
470 //runtimeDebug("handleBlock.");
471
472 // 1- i.scope
473 this.emitScope();
474
475 // 2- ANY
476 try {
477 processCode(en);
478
479 } catch (Exception e) {
480 // Stop an error unravelling here....
481 }
482
483 // 3- i.rscope
484 this.emitRScope();
485 }
486
487 /**
488 * handle a Buffer.
489 * MICROCODE
490 * 1- if (clear exists) i.clear(name), ALREADY = true
491 * 2- if (eval exists) i.eval(eval), i.merge(name)
492 * 3- else if (value exists) i.left(value), i.merge(name)
493 * 4- else if (buffer exists)i.reduce(buffer), i.merge(name)
494 * 5- else CLEAR = true
495 * 6- if (cdata exists) i.load(cdata), i.merge(name)
496 * 7- else if (CLEAR true, ALREADY false) i.clear(name)
497 */
498 private void handleBuffer(Element en) {
499
500 String name = en.getAttribute(ATTR_NAME);
501 String cdata;
502 boolean clearFlag = false;
503 boolean clearAlready = false;
504
505 //runtimeDebug("handleBuffer. name=" + name);
506
507 // 1- if (clear exists) i.clear(name)
508 if (en.hasAttribute(ATTR_CLEAR)) {
509 this.emitClear(name);
510 clearAlready = true;
511 }
512
513 // 2- if (eval exists) i.eval(eval), i.merge(name)
514 if (en.hasAttribute(ATTR_EVALUATOR)) {
515 this.emitEval(en.getAttribute(ATTR_EVALUATOR));
516 this.emitMerge(en.getAttribute(ATTR_NAME));
517
518 // 3- else if (value exists) i.load(value), i.merge(name)
519 } else if (en.hasAttribute(ATTR_VALUE)) {
520 this.emitLoad(en.getAttribute(ATTR_VALUE));
521 this.emitMerge(en.getAttribute(ATTR_NAME));
522
523 // 4- else if (buffer exists)i.reduce(buffer), i.merge(name)
524 } else if (en.hasAttribute(ATTR_BUFFER)) {
525 this.emitReduce(en.getAttribute(ATTR_BUFFER));
526 this.emitMerge(en.getAttribute(ATTR_NAME));
527
528 // 5- else i.clear(name), done
529 } else {
530 clearFlag = true;
531 }
532
533 // 6- if (cdata exists) i.load(cdata), i.merge(name)
534 try {
535 cdata = getText(en.getFirstChild());
536 } catch (Exception e) {
537 cdata = null;
538 }
539
540 if (cdata != null) {
541 this.emitLoad(cdata);
542 this.emitMerge(en.getAttribute(ATTR_NAME));
543
544 // 7- else if (CLEAR true, ALREADY false) i.clear(name)
545 } else if ((clearFlag == true) && (clearAlready == false)) {
546 this.emitClear(name);
547 }
548 }
549
550 /**
551 * handle call.
552 * MICROCODE
553 * 1- i.scope
554 * 2- (SET)*
555 * 3- i.call(name)
556 * 4- i.rscope
557 * 5- if (result exist) i.store(result)
558 */
559 private void handleCall(Element en) {
560
561 String name = en.getAttribute(ATTR_NAME);
562
563 runtimeDebug("handleCall. call=" + name);
564
565 // 1- i.scope
566 this.emitScope();
567
568 // 2- (SET)*
569 try {
570 // recurse into the children
571 processCode(en);
572
573 // 3- i.call(name)
574 this.emitCall(name);
575
576 } catch (Exception e) {
577 // Stop an error unravelling here. Close the scope and move on
578 runtimeError("ERROR handleCall. Broken call." + name);
579 runtimeError(e.toString());
580 this.emitRScope();
581 return;
582 }
583
584 // 4- i.rscope
585 this.emitRScope();
586
587 // 5- if (result exist) i.store(result)
588 if (en.hasAttribute(ATTR_RESULT)) {
589 this.emitStore(en.getAttribute(ATTR_RESULT));
590 }
591 }
592
593 /**
594 * handle call.
595 * MICROCODE
596 * 1- i.scope
597 * 2- (SET)*
598 * 3- i.eval(name)
599 * 4- i.method(method)
600 * 5- i.rscope
601 * 6- if (result exist) i.store(result)
602 */
603 private void handleMethod(Element en) {
604
605 String name = en.getAttribute(ATTR_NAME);
606 String method = en.getAttribute(ATTR_METHOD);
607
608 runtimeDebug("handleMethod. name=" + name + " method=" + method);
609
610 // 1- i.scope
611 this.emitScope();
612
613 // 2- (SET)*
614 try {
615 // recurse into the children
616 processCode(en);
617
618 // 3- i.call(name)
619 this.emitEval(name);
620
621 // 4- i.call(name)
622 this.emitMethod(method);
623
624 } catch (Exception e) {
625 // Stop an error unravelling here. Close the scope and move on
626 runtimeError("ERROR handleMethod. Broken call." + name);
627 runtimeError(e.toString());
628 this.emitRScope();
629 return;
630 }
631
632 // 5- i.rscope
633 this.emitRScope();
634
635 // 5- if (result exist) i.store(result)
636 if (en.hasAttribute(ATTR_RESULT)) {
637 this.emitStore(en.getAttribute(ATTR_RESULT));
638 }
639 }
640
641 /**
642 * handle exec.
643 * MICROCODE
644 * 1- i.scope
645 * 2- (INPUT)*
646 * 3- i.exec(name)
647 * 4- i.rscope
648 * 5- if (result exist) i.store(result)
649 */
650 private void handleExec(Element en) {
651
652 String name = en.getAttribute(ATTR_NAME);
653
654 runtimeDebug("handleExec. exec=" + name);
655
656 // 1- i.scope
657 this.emitScope();
658
659 // 2- (INPUT)*
660 try {
661 // recurse into the children
662 processCode(en);
663
664 // 3- i.call(name)
665 this.emitExec(name);
666
667 } catch (Exception e) {
668 // Stop an error unravelling here. Close the scope and move on
669 runtimeError("ERROR handleExec. Broken exec." + name);
670 runtimeError(e.toString());
671 this.emitRScope();
672 return;
673 }
674
675 // 4- i.rscope
676 this.emitRScope();
677
678 // 5- if (result exist) i.store(result)
679 if (en.hasAttribute(ATTR_RESULT)) {
680 this.emitStore(en.getAttribute(ATTR_RESULT));
681 }
682
683 }
684
685 /**
686 * handle a FOR
687 * MICROCODE
688 * 1- TOP: i.scope
689 * 2- if (eval exists) i.eval(eval)
690 * 3- else if (value exists) i.load(value)
691 * 4- else !!ERROR
692 * 5- i.new(count)
693 * 6- LOOP: i.if(DONE)
694 * 7- DO: (ANY)*
695 * 8- i.load("1")
696 * 9- i.right
697 * 10- i.fetch(count)
698 * 11- i.math("-") // LEFT RESULT will stay in LEFT
699 * 12- i.store(count)
700 * 13- i.jump(LOOP)
701 * 14- DONE: i.rscope
702 */
703 private void handleFor(Element en) {
704
705 int loopstop;
706
707 VMIIf myif;
708 VMIJump myjump;
709
710 //runtimeDebug("handleFor");
711
712 // 1- i.scope
713 this.emitScope();
714
715 // 2- if (eval exists) i.eval(eval)
716 if (en.hasAttribute(ATTR_EVALUATOR)) {
717 this.emitEval(en.getAttribute(ATTR_EVALUATOR));
718
719 // 3- else if (value exists) i.load(value)
720 } else if (en.hasAttribute(ATTR_VALUE)) {
721 this.emitLoad(en.getAttribute(ATTR_VALUE));
722
723 // 4- else !!ERROR
724 } else {
725 runtimeError("ERROR Broken for. No count value defined.");
726 this.emitLoad(en.getAttribute(LITERAL_ZERO));
727 // use 0 so we can finish compile
728 }
729
730 // 5- i.new(count)
731 this.emitNew(en.getAttribute("count"));
732
733 // 6- LOOP: i.if(DONE)
734 loopstop = ob.nextIP(); // loop stop points to #6
735 myif = new VMIIf();
736 // FIXUP the IF later
737 ob.emit(myif);
738 runtimeDebug("EMIT(" + loopstop + ") i.if target= FIXUP LATER");
739
740 // 7- DO: (ANY)*
741 try {
742 processCode(en);
743 } catch (Exception e) {
744 // Stop an error unravelling here. Close the scope and move on
745 runtimeError("ERROR Broken FOR inner.");
746 runtimeError(e.toString());
747 }
748
749 // 8- i.load(LITERAL_ONE)
750 this.emitLoad(LITERAL_ONE);
751
752 // 9- i.right
753 this.emitRight();
754
755 // 10- i.fetch(count)
756 this.emitFetch(en.getAttribute(ATTR_COUNT));
757
758 // 11- i.math("-")
759 this.emitMath(MINUS_OPERATION);
760
761 // 12- i.store(count)
762 this.emitStore(en.getAttribute(ATTR_COUNT));
763
764 // 13- i.jump(LOOP)
765 this.emitJump(loopstop);
766
767 // 14- DONE: i.rscope
768 // First fixup the jump
769 myif.t = ob.nextIP();
770 runtimeDebug("FIXUP IF=" + myif.t);
771 this.emitRScope();
772 }
773
774 /**
775 * handle a Set
776 * MICROCODE
777 * 1- if (eval exists) i.eval(eval)
778 * 2- else if (ref exists) i.fetch(ref)
779 * 3- else if (value exists) i.load(value)
780 * 4- else (buffer exists) i.reduce(buffer)
781 * 5- else !!ERROR
782 * 6- if (new exists) i.new(name)
783 * 7- else i.store(name)
784 */
785 private void handleSet(Element en) {
786
787 String name = en.getAttribute(ATTR_NAME);
788
789 //runtimeDebug("handleSet name=" + name);
790
791 // 1- if (eval exists) i.eval(eval)
792 if (en.hasAttribute(ATTR_EVALUATOR)) {
793 this.emitEval(en.getAttribute(ATTR_EVALUATOR));
794
795 // 2- else if (ref exists) i.fetch(ref)
796 } else if (en.hasAttribute(ATTR_REFERENCE)) {
797 this.emitFetch(en.getAttribute(ATTR_REFERENCE));
798
799 // 3- else if (value exists) i.load(value)
800 } else if (en.hasAttribute(ATTR_VALUE)) {
801 this.emitLoad(en.getAttribute(ATTR_VALUE));
802
803 // 4- else (buffer exists) i.reduce(buffer)
804 } else if (en.hasAttribute(ATTR_BUFFER)) {
805 this.emitReduce(en.getAttribute(ATTR_BUFFER));
806
807 // 5- else !!ERROR
808 } else {
809 runtimeError("ERROR. No source given for SET.");
810 return;
811 }
812
813 // 6- if (new exists) i.new(name)
814 if (en.hasAttribute(ATTR_NEW)) {
815 this.emitNew(name);
816
817 // 7- else i.store(name)
818 } else {
819 this.emitStore(name);
820 }
821 }
822
823 /**
824 * handle a Input
825 * MICROCODE
826 * 1- if (eval exists) i.eval(eval)
827 * 2- else if (value exists) i.load(value)
828 * 3- else (buffer exists) i.reduce(buffer)
829 * 4- else !!ERROR
830 * 5- i.new(name)
831 */
832 private void handleInput(Element en) {
833
834 String name = en.getAttribute(ATTR_NAME);
835
836 //runtimeDebug("handleInput name=" + name);
837
838 // 1- if (eval exists) i.eval(eval)
839 if (en.hasAttribute(ATTR_EVALUATOR)) {
840 this.emitEval(en.getAttribute(ATTR_EVALUATOR));
841
842 // 2- else if (value exists) i.load(value)
843 } else if (en.hasAttribute(ATTR_VALUE)) {
844 this.emitLoad(en.getAttribute(ATTR_VALUE));
845
846 // 3- else (buffer exists) i.reduce(buffer)
847 } else if (en.hasAttribute(ATTR_BUFFER)) {
848 this.emitReduce(en.getAttribute(ATTR_BUFFER));
849
850 // 4- else !!ERROR
851 } else {
852 runtimeError("ERROR. No source given for INPUT.");
853 return;
854 }
855
856 // 5- i.new(name)
857 this.emitNew(name);
858 }
859
860 /**
861 * handle goto
862 * Emit the goto. Put it in the fixup stack.
863 * MICROCODE
864 * 1- i.goto(label)
865 */
866 private void handleGoto(Element en) {
867
868 String label = en.getAttribute(ATTR_LABEL);
869
870 //runtimeDebug("handleGoto label=" + label);
871
872 // 1- i.goto(label)
873 fixupstack.push(new NOPair(label, (Object) this.emitGoto(0)));
874 }
875
876 /**
877 * handle a IF
878 * <code>MICROCODE
879 * 1- if (oper exists) OP = oper
880 * 2- else OP = '=' (default)
881 * 3- if (eval exists) i.eval(eval)
882 * 4- else if (value exists) i.load(value)
883 * 5- else !!ERROR
884 * 6- i.right
885 * 7- i.fetch(item)
886 * 8- i.math(operation)
887 * 9- i.if(OUTER,OP)
888 * 10- INNER: i.scope
889 * 11- (ANY)*
890 * 12- i.rscope
891 * 13- OUTER: i.nop </code>
892 */
893 private void handleIf(Element en) {
894
895 VMIIf myif;
896 int opthang = EQ; // default is =
897
898 //runtimeDebug("handleIf");
899
900 // 1- if (oper exists) OP = oper
901 if (en.hasAttribute(ATTR_OPERATOR)) {
902 String ops = en.getAttribute(ATTR_OPERATOR);
903 if (ops.equals(EQ_STRING)) {
904 opthang = EQ;
905 } else if (ops.equals(LT_STRING)) {
906 opthang = LT;
907 } else if (ops.equals(GT_STRING)) {
908 opthang = GT;
909 } else if (ops.equals(NOT_STRING)) {
910 opthang = NOT;
911 } else {
912 runtimeWarning("WARNING If has unrecognized operation. Using default \"eq\"");
913 opthang = EQ;
914 }
915 }
916 // 2- else OP = '=' (default)
917 // Implied since it is the default.
918
919 // 3- if (eval exists) i.eval(eval)
920 if (en.hasAttribute(ATTR_EVALUATOR)) {
921 this.emitEval(en.getAttribute(ATTR_EVALUATOR));
922
923 // 4- else if (value exists) i.load(value)
924 } else if (en.hasAttribute(ATTR_VALUE)) {
925 this.emitLoad(en.getAttribute(ATTR_VALUE));
926
927 // 5- else !!ERROR
928 } else {
929 runtimeError("ERROR Broken IF. No right value defined defined.");
930 this.emitLoad(en.getAttribute(LITERAL_ZERO));
931 // use 0 so we can finish compile
932 }
933
934 // 6- i.right
935 this.emitRight();
936
937 // 7- i.fetch(item)
938 this.emitFetch(en.getAttribute(ATTR_ITEM));
939
940 // 8- i.math(operation)
941 this.emitMath(IF_OPERATION);
942
943 // 9- i.if(OUTER, OP)
944 myif = new VMIIf();
945 myif.operFlag = opthang;
946 ob.emit(myif);
947 //runtimeDebug(
948 // "EMIT(" + (ob.nextIP() - 1) + ") i.if target= FIXUP LATER");
949
950 // 10- INNER: i.scope
951 this.emitScope();
952
953 // 11- (ANY)*
954 try {
955 processCode(en);
956 } catch (Exception e) {
957 // Stop an error unravelling here. Close the scope and move on
958 runtimeError("ERROR Broken IF inner.");
959 runtimeError(e.toString());
960 }
961
962 // 12- i.rscope
963 this.emitRScope();
964
965 // 13- OUTER: i.nop
966 // First fixup the if. Put a NOP for safety, so there will always be a target
967 myif.t = ob.nextIP();
968 runtimeDebug("FIXUP if target=" + myif.t);
969 this.emitNOP();
970 }
971
972 /**
973 * handle a ASSERT
974 * <code>MICROCODE
975 *
976 * 1- if (oper NOT exists) OP = NOT
977 * 2- else OP = EQ
978 * 3- i.fetch(item)
979 * 4- i.assert(OUTER,OP)
980 * 5- INNER: i.scope
981 * 6- (ANY)*
982 * 7- i.rscope
983 * 8- OUTER: i.nop
984 * </code>
985 */
986 private void handleAssert(Element en) {
987
988 VMIAssert myassert;
989 int opthang = EQ; // default is =
990
991 //runtimeDebug("handleAssert");
992
993 // 1- if (oper exists) OP = oper
994 if (en.hasAttribute(ATTR_OPERATOR)) {
995 String ops = en.getAttribute(ATTR_OPERATOR);
996 if (ops.equals(EQ_STRING)) {
997 opthang = EQ;
998 } else if (ops.equals(NOT_STRING)) {
999 opthang = NOT;
1000 } else {
1001 runtimeWarning("WARNING If has unrecognized operation. Using default \"eq\"");
1002 opthang = EQ;
1003 }
1004 }
1005 // 2- else OP = '=' (default)
1006 // Implied since it is the default.
1007
1008 // 3- i.fetch(item)
1009 this.emitFetch(en.getAttribute(ATTR_ITEM));
1010
1011 // 4- i.assert(OUTER,OP)
1012 myassert = new VMIAssert();
1013 myassert.operFlag = opthang;
1014 ob.emit(myassert);
1015 //runtimeDebug(
1016 // "EMIT(" + (ob.nextIP() - 1) + ") i.assert target= FIXUP LATER");
1017
1018 // 5- INNER: i.scope
1019 this.emitScope();
1020
1021 // 6- (ANY)*
1022 try {
1023 processCode(en);
1024 } catch (Exception e) {
1025 // Stop an error unravelling here. Close the scope and move on
1026 runtimeError("ERROR Broken ASSERT inner.");
1027 runtimeError(e.toString());
1028 }
1029
1030 // 7- i.rscope
1031 this.emitRScope();
1032
1033 // 8- OUTER: i.nop
1034 // First fixup the assert. Put a NOP for safety, so there will always be a target
1035 myassert.t = ob.nextIP();
1036 runtimeDebug("FIXUP assert target=" + myassert.t);
1037 this.emitNOP();
1038 }
1039
1040 /**
1041 * handle subroutine.
1042 * MICROCODE
1043 * 1- i.scope
1044 * 2- (SET)*
1045 * 3- i.subr(name)
1046 * 4- i.rscope
1047 * 5- if (result exist) i.store(result)
1048 */
1049 private void handleSubroutine(Element en) {
1050
1051 String name = en.getAttribute(ATTR_NAME);
1052
1053 runtimeDebug("handleSubroutine. name=" + name);
1054
1055 // 1- i.scope
1056 this.emitScope();
1057
1058 // 2- (SET)*
1059 try {
1060 // recurse into the children
1061 processCode(en);
1062
1063 // 3- i.call(name)
1064 this.emitSubr(name);
1065
1066 } catch (Exception e) {
1067 // Stop an error unravelling here. Close the scope and move on
1068 runtimeError("ERROR handleCall. Broken call." + name);
1069 runtimeError(e.toString());
1070 ob.emit(new VMIRScope());
1071 return;
1072 }
1073
1074 // 4- i.rscope
1075 this.emitRScope();
1076
1077 // 5- if (result exist) i.store(result)
1078 if (en.hasAttribute(ATTR_RESULT)) {
1079 this.emitStore(en.getAttribute(ATTR_RESULT));
1080 }
1081 }
1082
1083 /**
1084 * handle a label
1085 * MICROCODE
1086 * 1- {mark label}
1087 */
1088 private void handleLabel(Element en) {
1089
1090 String name = en.getAttribute(ATTR_NAME);
1091
1092 //runtimeDebug("handleLabel. name=" + name);
1093
1094 // 1- {mark label}. Put it in the symboltable with current IP
1095 // See if there is a duplicate.
1096 if (symboltable.containsKey(name)) {
1097 runtimeError("Duplicate label declared. label=" + name);
1098 } else {
1099 symboltable.put(name, new Integer(ob.nextIP()));
1100 }
1101 }
1102
1103 /**
1104 * handle a WHILE
1105 * MICROCODE
1106 * 1- i.scope
1107 * 2- DO: i.load(value)
1108 * 3- i.right
1109 * 4- i.fetch(name)
1110 * 5- i.math("=")
1111 * 6- i.if(DONE)
1112 * 7- (ANY)*
1113 * 8- i.jump(DO)
1114 * 9- DONE: i.rscope
1115 */
1116 private void handleWhile(Element en) {
1117
1118 int loopdo;
1119 VMIIf myif;
1120 VMIJump myjump;
1121
1122 //runtimeDebug("handleWhile");
1123
1124 // 1- i.scope
1125 this.emitScope();
1126
1127 // 2- DO: i.load(value)
1128 loopdo = ob.nextIP(); // DO points at #2
1129 this.emitLoad(en.getAttribute(ATTR_VALUE));
1130
1131 // 3- i.right
1132 this.emitRight();
1133
1134 // 4- i.fetch(name)
1135 this.emitFetch(en.getAttribute(ATTR_NAME));
1136
1137 // 5- i.math("=")
1138 this.emitMath(EQ_OPERATION);
1139
1140 // 6- i.if(DONE)
1141 myif = new VMIIf();
1142 ob.emit(myif);
1143 //runtimeDebug(
1144 // "EMIT(" + (ob.nextIP() - 1) + ") i.if target= FIXUP LATER");
1145
1146 // 7- (ANY)*
1147 try {
1148 processCode(en);
1149 } catch (Exception e) {
1150 // Stop an error unravelling here. Close the scope and move on
1151 runtimeError("ERROR Broken FOR inner.");
1152 runtimeError(e.toString());
1153 }
1154
1155 // 8- i.jump(DO)
1156 myjump = new VMIJump();
1157 myjump.t = loopdo;
1158 ob.emit(myjump);
1159 runtimeDebug("EMIT(" + (ob.nextIP() - 1) + ") i.jump target=" + loopdo);
1160
1161 // 9- DONE: i.rscope
1162 // First fixup the if
1163 myif.t = ob.nextIP();
1164 this.emitRScope();
1165 }
1166
1167 /**
1168 * handle a MATH
1169 * MICROCODE
1170 * 1- if (eval exists) i.eval(eval)
1171 * 2- else if (value exists) i.load(value)
1172 * 3- else !!ERROR
1173 * 4- i.right()
1174 * 5- i.fetch(left)
1175 * 6- i.math(oper)
1176 * 7- if (output exists) i.store(output)
1177 * 8- else i.store(left)
1178 */
1179 private void handleMath(Element en) {
1180
1181 //runtimeDebug("handleMath");
1182
1183 // 1- if (eval exists) i.eval(eval)
1184 if (en.hasAttribute(ATTR_EVALUATOR)) {
1185 this.emitEval(en.getAttribute(ATTR_EVALUATOR));
1186
1187 // 2- else if (value exists) i.load(value)
1188 } else if (en.hasAttribute(ATTR_VALUE)) {
1189 this.emitLoad(en.getAttribute(ATTR_VALUE));
1190
1191 // 3- else !!ERROR
1192 } else {
1193 runtimeError("ERROR Broken Math. No right value defined.");
1194 this.emitLoad(LITERAL_ZERO);
1195 // use 0 so we can finish compile
1196 }
1197
1198 // 4- i.right
1199 this.emitRight();
1200
1201 // 5- i.fetch(item)
1202 this.emitFetch(en.getAttribute(ATTR_LEFT));
1203
1204 // 6- i.math(operation)
1205 this.emitMath(en.getAttribute(ATTR_OPERATOR));
1206
1207 // 7- if (output exists) i.store(output)
1208 if (en.hasAttribute(ATTR_OUTPUT)) {
1209 this.emitStore(en.getAttribute(ATTR_OUTPUT));
1210
1211 // else i.store(left)
1212 } else {
1213 this.emitStore(en.getAttribute(ATTR_LEFT));
1214 }
1215
1216 }
1217
1218 /**
1219 * handle a return. It just emits a fault
1220 * MICROCODE
1221 * 1- i.fault
1222 */
1223 private void handleReturn(Element en) {
1224
1225 //runtimeDebug("handleReturn");
1226
1227 // 1- i.fault
1228 this.emitFault();
1229 }
1230
1231 // == ===============================================================================
1232 // == = HELPERS =
1233 // == ===============================================================================
1234
1235 /**
1236 * Valid string.
1237 *
1238 * Checks if the reference is not null and the string contains characters.
1239 *
1240 * @param s the string to check.
1241 * @return true if valid, otherwise false
1242 */
1243 private boolean isValid(String s) {
1244 if ((s == null) || (s.length() < 1))
1245 return false;
1246 else
1247 return true;
1248 }
1249
1250 /**
1251 * Get the text out of an XML node.
1252 *
1253 * @param cdn XML node.
1254 * @return the text.
1255 */
1256 private String getText(Node cdn) {
1257
1258 try {
1259 if ((cdn.getNodeType() == Node.TEXT_NODE)
1260 || (cdn.getNodeType() != Node.CDATA_SECTION_NODE)) {
1261 CharacterData cdnc = (CharacterData) cdn;
1262 return cdnc.getData();
1263 }
1264 } catch (Exception e) {
1265 } // ignore. re are returning empty anyway
1266 return null;
1267 }
1268
1269 /**
1270 * Get the text out of an XML node.
1271 *
1272 * @param where where it was bad.
1273 * @param which which attribute was bad.
1274 * @param tag which tag.
1275 * @return the text.
1276 */
1277 private void errBadAttribute(String where, String which, String tag) {
1278 runtimeError(
1279 "ERROR: Invalid '"
1280 + which
1281 + "' attribute for <"
1282 + tag
1283 + ">. @"
1284 + where);
1285 }
1286
1287 /**
1288 * emitClear
1289 */
1290 private void emitClear(String name) {
1291 VMIClear ic;
1292 ic = new VMIClear();
1293 ic.t = name;
1294 ob.emit(ic);
1295 //runtimeDebug("EMIT(" + (ob.nextIP() - 1) + ") i.clear name= " + name);
1296 }
1297
1298 /**
1299 * emitEval
1300 */
1301 private void emitEval(String eval) {
1302 VMIEval ic;
1303 ic = new VMIEval();
1304 ic.e = eval;
1305 ob.emit(ic);
1306 //runtimeDebug("EMIT(" + (ob.nextIP() - 1) + ") i.eval eval= " + eval);
1307 }
1308
1309 /**
1310 * emitReduce
1311 */
1312 private void emitReduce(String buffer) {
1313 VMIReduce ic;
1314 ic = new VMIReduce();
1315 ic.b = buffer;
1316 ob.emit(ic);
1317 //runtimeDebug(
1318 // "EMIT(" + (ob.nextIP() - 1) + ") i.reduce name= " + buffer);
1319 }
1320
1321 /**
1322 * emitReduce
1323 */
1324 private void emitLoad(String value) {
1325 VMILoad ic;
1326 ic = new VMILoad();
1327 ic.l = value;
1328 ob.emit(ic);
1329 //runtimeDebug("EMIT(" + (ob.nextIP() - 1) + ") i.load value= " + value);
1330 }
1331
1332 /**
1333 * emitMerge
1334 */
1335 private void emitMerge(String buffer) {
1336 VMIMerge ic;
1337 ic = new VMIMerge();
1338 ic.b = buffer;
1339 ob.emit(ic);
1340 //runtimeDebug(
1341 // "EMIT(" + (ob.nextIP() - 1) + ") i.merge buffer= " + buffer);
1342 }
1343
1344 /**
1345 * emitCall
1346 */
1347 private void emitCall(String target) {
1348 VMICall ic;
1349 ic = new VMICall();
1350 ic.t = target;
1351 ob.emit(ic);
1352 //runtimeDebug(
1353 // "EMIT(" + (ob.nextIP() - 1) + ") i.call target= " + target);
1354 }
1355
1356 /**
1357 * emitMethod
1358 */
1359 private void emitMethod(String m) {
1360 VMIMethod ic;
1361 ic = new VMIMethod();
1362 ic.m = m;
1363 ob.emit(ic);
1364 //runtimeDebug(
1365 // "EMIT(" + (ob.nextIP() - 1) + ") i.method method=" + m);
1366 }
1367
1368 /**
1369 * emitSubr
1370 */
1371 private void emitSubr(String target) {
1372 VMISubr ic;
1373 ic = new VMISubr();
1374 ic.t = target;
1375 ob.emit(ic);
1376 //runtimeDebug(
1377 // "EMIT(" + (ob.nextIP() - 1) + ") i.subr target= " + target);
1378 }
1379
1380 /**
1381 * emitExec
1382 */
1383 private void emitExec(String target) {
1384 VMIExec ic;
1385 ic = new VMIExec();
1386 ic.c = target;
1387 ob.emit(ic);
1388 //runtimeDebug(
1389 // "EMIT(" + (ob.nextIP() - 1) + ") i.exec target= " + target);
1390 }
1391
1392 /**
1393 * emitNew
1394 */
1395 private void emitNew(String var) {
1396 VMINew ic;
1397 ic = new VMINew();
1398 ic.v = var;
1399 ob.emit(ic);
1400 //runtimeDebug("EMIT(" + (ob.nextIP() - 1) + ") i.new variable= " + var);
1401 }
1402
1403 /**
1404 * emitRight
1405 */
1406 private void emitRight() {
1407 ob.emit(new VMIRight());
1408 //runtimeDebug("EMIT(" + (ob.nextIP() - 1) + ") i.right");
1409 }
1410
1411 /**
1412 * emitFetch
1413 */
1414 private void emitFetch(String var) {
1415 VMIFetch ic;
1416 ic = new VMIFetch();
1417 ic.v = var;
1418 ob.emit(ic);
1419 //runtimeDebug(
1420 // "EMIT(" + (ob.nextIP() - 1) + ") i.fetch variable= " + var);
1421 }
1422
1423 /**
1424 * emitMath
1425 */
1426 private void emitMath(String oper) {
1427 VMIMath ic;
1428 ic = new VMIMath();
1429 ic.o = oper;
1430 ob.emit(ic);
1431 //runtimeDebug(
1432 // "EMIT(" + (ob.nextIP() - 1) + ") i.math operation= " + oper);
1433 }
1434
1435 /**
1436 * emitStore
1437 */
1438 private void emitStore(String var) {
1439 VMIStore ic;
1440 ic = new VMIStore();
1441 ic.v = var;
1442 ob.emit(ic);
1443 //runtimeDebug(
1444 // "EMIT(" + (ob.nextIP() - 1) + ") i.store variable= " + var);
1445 }
1446
1447 /**
1448 * emitJump
1449 */
1450 private VMIJump emitJump(int target) {
1451 VMIJump ic;
1452 ic = new VMIJump();
1453 ic.t = target;
1454 ob.emit(ic);
1455 //runtimeDebug(
1456 // "EMIT(" + (ob.nextIP() - 1) + ") i.Jump target= " + target);
1457 return ic;
1458 }
1459
1460 /**
1461 * emitGoto
1462 */
1463 private VMIGoto emitGoto(int target) {
1464 VMIGoto ic;
1465 ic = new VMIGoto();
1466 ic.t = target;
1467 ob.emit(ic);
1468 //runtimeDebug(
1469 // "EMIT(" + (ob.nextIP() - 1) + ") i.goto target= " + target);
1470 return ic;
1471 }
1472
1473 /**
1474 * emitScope
1475 */
1476 private void emitScope() {
1477 ob.emit(new VMIScope());
1478 //runtimeDebug("EMIT(" + (ob.nextIP() - 1) + ") i.scope");
1479 }
1480
1481 /**
1482 * emitRScope
1483 */
1484 private void emitRScope() {
1485 ob.emit(new VMIRScope());
1486 //runtimeDebug("EMIT(" + (ob.nextIP() - 1) + ") i.rscope");
1487 }
1488
1489 /**
1490 * emitNOP
1491 */
1492 private void emitNOP() {
1493 ob.emit(new VMINop());
1494 //runtimeDebug("EMIT(" + (ob.nextIP() - 1) + ") i.nop");
1495 }
1496
1497 /**
1498 * emitNOP
1499 */
1500 private void emitFault() {
1501 ob.emit(new VMIFault());
1502 //runtimeDebug("EMIT(" + (ob.nextIP() - 1) + ") i.fault");
1503 }
1504
1505 }
|