Source code: com/voytechs/jnetstream/codec/Linker.java
1 /*
2 * File: Linker.java
3 * Auth: Mark Bednarczyk
4 * Date: DATE
5 * Id: $Id: Linker.java,v 1.1.1.1 2003/09/22 16:32:15 voytechs Exp $
6 ********************************************
7 Copyright (C) 2003 Mark Bednarczyk
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 ********************************************
23 * $Log: Linker.java,v $
24 * Revision 1.1.1.1 2003/09/22 16:32:15 voytechs
25 * Initial import.
26 *
27 */
28 package com.voytechs.jnetstream.codec;
29
30 import com.voytechs.jnetstream.primitive.*;
31 import com.voytechs.jnetstream.codec.Packet;
32 import com.voytechs.jnetstream.npl.*;
33
34 import java.lang.*;
35 import java.util.*;
36 import java.io.*;
37
38 /**
39 *
40 */
41 public class Linker
42 extends Visitor {
43
44 /* Internal attributes */
45 private static final boolean debug = false;
46
47 private static final int F_RECORD_SYMTABLE = 1;
48 private static final int F_LINK_TYPES = 2;
49 private static final int F_VARIABLE_REFERENCES = 3;
50 private static final int F_LINK_HEADERS = 4;
51 private static final int F_LINK_HEADERS_STAGE1 = 4;
52 private static final int F_LINK_HEADERS_STAGE2 = 5;
53
54 private SymTable global;
55 private SymTable packet;
56 private SymTable types; // Special offline symtable for types.
57 private int stage = 0;
58 private StatementContainer defaultCandidate = null;
59
60
61 private Hashtable symTables = new Hashtable();
62
63
64
65
66
67 /**
68 *
69 * @param
70 * @exception
71 */
72 public Linker(SymTable sym) {
73 this.global = sym;
74
75 packet = new SymTable("packet", global);
76 types = new SymTable("types");
77 }
78
79 public void link(NodeList block)
80 throws NodeException {
81
82 recordDefinitions(block);
83 loadTypePrimitives();
84 resolveTypes(block);
85 resolveReferences(block);
86 linkHeaders(block);
87 }
88
89 public void resolveTypes(NodeList block) throws NodeException { stage = F_LINK_TYPES; visit(block, null, null); }
90 public void resolveReferences(NodeList block) throws NodeException { stage = F_VARIABLE_REFERENCES; visit(block, packet, null); }
91 public void linkHeaders(NodeList block) throws NodeException {
92 stage = F_LINK_HEADERS_STAGE1;
93 visit(block, null, null);
94
95 stage = F_LINK_HEADERS_STAGE2;
96 visit(block, null, null);
97
98 }
99
100 protected void exportProperty(String name, SymTable sym) {
101
102 if(debug)
103 System.out.println("Exporting " + name + " to " + sym.getName());
104
105 MutableReferenceNode ref = new MutableReferenceNode(name);
106 ref.setSymTable(sym);
107 sym.addSymbol(name ,ref);
108 }
109
110
111 /**
112 * traverses the top level nodes and records their
113 * definitions in the SymTable. Only statement are
114 * expected at this level. If any non-statement nodes
115 * ie. expression are found, an error message is reported.
116 */
117 public void recordDefinitions(NodeList block)
118 throws NodeException {
119
120 stage = F_RECORD_SYMTABLE;
121
122 exportProperty(Packet.PACKET_LENGTH, packet);
123 exportProperty(Packet.PACKET_SNAPLEN, packet);
124 exportProperty(Packet.PACKET_START, packet);
125 exportProperty(Packet.PACKET_END, packet);
126 exportProperty(Packet.PACKET_REMAINING, packet);
127 exportProperty(Packet.PACKET_REMAINING, packet);
128 exportProperty(Packet.CAPTURE_TIMESTAMP, packet);
129
130 exportProperty(Packet.CAPTURE_DEVICE_IP, global);
131 exportProperty(Packet.CAPTURE_DEVICE_ARCH, global);
132 exportProperty(Packet.CAPTURE_DEVICE_FILENAME, global);
133 exportProperty(Packet.CAPTURE_DEVICE_OS, global);
134 exportProperty(Packet.FIRST_HEADER, global);
135
136 /**
137 * Traverse top level statements.
138 *
139 * Top level statements are added to the "global.packet" symtable.
140 */
141 for(int i = 0; i < block.size(); i ++) {
142 Node node = block.get(i);
143 if( (node instanceof StatementNode) == false)
144 throw new NodeException("Expected a statement here. Only statements are allowed top-level.", node);
145
146 visit(node, packet, null); // Implemented in Visitor class
147 }
148
149 }
150
151 /**
152 *
153 *
154 *
155 *
156 *
157 */
158 public void loadTypePrimitives()
159 throws NodeException {
160
161 Enumeration keys = types.getSymbols();
162 while(keys.hasMoreElements()) {
163 String k = (String)keys.nextElement();
164 SymTable.Symbol s = types.lookupSymbol(k);
165 if(s.getType() != SymTable.Symbol.PRIMITIVE)
166 continue;
167
168 if(k.equals("int"))
169 s.setImplementation(new IntPrimitive());
170
171 else if(k.equals("short"))
172 s.setImplementation(new ShortPrimitive());
173
174 else if(k.equals("byte"))
175 s.setImplementation(new BytePrimitive());
176
177 else if(k.equals("long"))
178 s.setImplementation(new LongPrimitive());
179
180 else if(k.equals("address"))
181 s.setImplementation(new AddressPrimitive());
182
183 else if(k.equals("ipaddress"))
184 s.setImplementation(new IpAddressPrimitive());
185
186 else if(k.equals("ipnetmask"))
187 s.setImplementation(new IpNetmaskPrimitive());
188
189 else if(k.equals("macaddress"))
190 s.setImplementation(new MacAddressPrimitive());
191
192 else if(k.equals("string"))
193 s.setImplementation(new StringPrimitive());
194
195 else if(k.equals("hexdump"))
196 s.setImplementation(new HexdumpPrimitive());
197
198 else if(k.equals("time"))
199 s.setImplementation(new TimePrimitive());
200
201 else if(k.equals(DNSNamePrimitive.NAME))
202 s.setImplementation(new DNSNamePrimitive());
203
204 else {
205 throw new NodeException("Unknown type " + k + ". External primtive loader not implemented yet.", s.getDefinition());
206 }
207 }
208 }
209
210
211
212
213 /**
214 *
215 *
216 *
217 *
218 *
219 */
220 public boolean traverse(FamilyStatement stat, Object user1, Object user2)
221 throws NodeException {
222
223 if(debug)
224 System.out.println("Visiting Family");
225
226 if(stage == F_LINK_HEADERS_STAGE1 || stage == F_LINK_HEADERS_STAGE2) {
227 visit(stat.getCode(), stat, null);
228 }
229
230 if(stage == F_RECORD_SYMTABLE) {
231 SymTable table = (SymTable)user1;
232
233 /**
234 * Record the statement in our parents symtable.
235 */
236 table.addSymbol(stat.getName(), stat);
237
238 /**
239 * If we have code, which every header must have,
240 * then allocate a sub symtable so our children can
241 * record in it.
242 */
243 if(stat.getCode() != null) {
244 stat.setSymTable(new SymTable(stat.getName(), table));
245
246 /**
247 * Now pass on the table to our children.
248 */
249 visit(stat.getCode(), stat.getSymTable(), user2);
250 }
251 }
252
253 return true;
254 }
255
256
257
258 /**
259 *
260 *
261 *
262 *
263 *
264 */
265 public boolean traverse(HeaderStatement stat, Object user1, Object user2)
266 throws NodeException {
267
268
269 if(debug)
270 System.out.println("Visiting Header");
271
272 if(stage == F_RECORD_SYMTABLE) {
273 SymTable table = (SymTable)user1;
274
275 if(debug)
276 System.out.println("Recording header " + stat.getName() + " into SymTable(" + table.getName() + ")");
277
278 /**
279 * Record the statement in our parents symtable.
280 */
281 table.addSymbol(stat.getName(), stat);
282
283 /**
284 * If we have code, which every header must have,
285 * then allocate a sub symtable so our children can
286 * record in it.
287 */
288 if(stat.getCode() != null) {
289 stat.setSymTable(new SymTable(stat.getName(), table));
290
291 /**
292 * Now pass on the table to our children.
293 */
294 visit(stat.getCode(), stat.getSymTable(), user2);
295 }
296 if(stat.getArrayDimension() != null) {
297 visit(stat.getArrayDimension(), user1, user2);
298 }
299 }
300
301 else if(stage == F_LINK_TYPES) {
302 if(stat.getCode() != null) {
303 visit(stat.getCode(), user1, user2);
304 }
305 if(stat.getArrayDimension() != null) {
306 visit(stat.getArrayDimension(), user1, user2);
307 }
308 }
309
310
311 else if(stage == F_VARIABLE_REFERENCES) {
312 if(stat.getCode() != null) {
313 visit(stat.getCode(), stat.getSymTable(), stat);
314 }
315 else {
316 throw new NodeException("Empty header " + stat.getName(), stat);
317 }
318 if(stat.getArrayDimension() != null) {
319 visit(stat.getArrayDimension(), user1, user2);
320 }
321 }
322
323 else if(stage == F_LINK_HEADERS_STAGE1) {
324 if(stat.isDefaultCandidate()) {
325 if(defaultCandidate != null)
326 throw new NodeException("Only 1 default link candidate can be declared", stat);
327
328 defaultCandidate = stat;
329 }
330 visit(stat.getCode(), stat, null);
331 }
332 else if( stage == F_LINK_HEADERS_STAGE2) {
333 visit(stat.getCode(), stat, null);
334
335 /**
336 * Now set the default candidate in every header.
337 * With the exception of the default header its self. This would
338 * create a loop. Loop terminates because default candidate usually
339 * suck all of the available data so Snaplen Exception is thrown when
340 * end of packet is reached to break the loop. But we want to do this cleanly.
341 */
342 if(defaultCandidate != null && stat != defaultCandidate)
343 stat.setDefaultCandidate(defaultCandidate);
344 }
345
346 else {
347 throw new NodeException("Unimplemented stage " + stage + " in header. Not a good idea", stat);
348 }
349 return true;
350 }
351
352
353
354
355
356 /**
357 *
358 *
359 *
360 *
361 *
362 */
363 public boolean traverse(FieldStatement stat, Object user1, Object user2)
364 throws NodeException {
365
366 if(debug)
367 System.out.println("Visiting Field stage=" + stage);
368
369 if(stage == F_VARIABLE_REFERENCES) {
370 SymTable table = (SymTable)user1;
371
372 if(debug)
373 System.out.println("Linking Field References");
374
375
376 if(stat.hasArray()) {
377
378 // MutableArrayReferenceNode ref = new MutableArrayReferenceNode(stat.getName(), stat.getArrayDimension());
379 // ref.setSymTable(table);
380 // table.addSymbol(stat.getName(), SymTable.Symbol.REFERENCE, ref);
381 }
382
383 else {
384
385 MutableReferenceNode ref = new MutableReferenceNode(stat.getName());
386 ref.setSymTable(table);
387 table.addSymbol(stat.getName(), SymTable.Symbol.REFERENCE, ref);
388 }
389
390 if(stat.getCode() != null) {
391 visit(stat.getCode(), stat.getSymTable(), stat);
392 }
393
394 if(stat.getSizeCode() != null) {
395 visit(stat.getSizeCode(), user1, user2);
396 }
397
398 if(stat.getSkipCode() != null) {
399 visit(stat.getSkipCode(), user1, user2);
400 }
401 if(stat.getArrayDimension() != null) {
402 visit(stat.getArrayDimension(), user1, user2);
403 }
404 }
405 /**
406 * Make sure that the field type is recorded
407 * in the SymTable so the field can be linked
408 * later.
409 */
410
411 else if(stage == F_RECORD_SYMTABLE) {
412
413
414 SymTable table = (SymTable)user1;
415
416 if(debug)
417 System.out.println("Recording symtable Field References");
418
419 /**
420 * First record the type to a primitive. Types are recorded in the
421 * special symtable.
422 */
423 if(types.contains(stat.getType()) == false)
424 types.addSymbol(stat.getType(), SymTable.Symbol.PRIMITIVE, null);
425
426 /**
427 * Now check for code.
428 * Allocate new symtable for subfields.
429 */
430 SymTable subFieldSymTable = new SymTable(stat.getName(), table);
431 stat.setSymTable(subFieldSymTable);
432
433 /**
434 * Export builtin properties
435 */
436 exportProperty(FieldStatement.SIZE, subFieldSymTable);
437 exportProperty(Field.VALUE_NAME, subFieldSymTable);
438
439 if(stat.getCode() != null) {
440 visit(stat.getCode(), stat.getSymTable(), user2);
441 }
442
443 if(stat.getSizeCode() != null) {
444 visit(stat.getSizeCode(), user1, user2);
445 }
446
447 if(stat.getSkipCode() != null) {
448 visit(stat.getSkipCode(), user1, user2);
449 }
450
451 if(stat.getArrayDimension() != null) {
452 visit(stat.getArrayDimension(), user1, user2);
453 }
454 }
455
456 else if(stage == F_LINK_TYPES) {
457
458 if(debug)
459 System.out.println("Linking types in Field");
460
461 SymTable.Symbol s = types.lookupSymbol(stat.getType());
462 if(s == null || s.getImplementation() == null)
463 throw new NodeException("Unresolved link " + stat.getType(), stat);
464
465 stat.setTypePrimitive((ProtocolPrimitiveFactory)s.getImplementation());
466
467 if(stat.getCode() != null) {
468 visit(stat.getCode(), user1, user2);
469 }
470
471 if(stat.getSizeCode() != null) {
472 visit(stat.getSizeCode(), user1, user2);
473 }
474
475 if(stat.getSkipCode() != null) {
476 visit(stat.getSkipCode(), user1, user2);
477 }
478
479 if(stat.getArrayDimension() != null) {
480 visit(stat.getArrayDimension(), user1, user2);
481 }
482 }
483 else if(stage == F_LINK_HEADERS_STAGE1) {
484 if(stat.isLinkable()) {
485 HeaderStatement header = (HeaderStatement)user1;
486 SymTable.Symbol s = header.getSymTable().lookupSymbol(stat.getName());
487 header.setLinkVariable((ReferenceNode)s.getDefinition());
488
489 if(debug)
490 System.out.println("Recording link variable " + stat.getName() + " in header(" + header.getName() + ")");
491 }
492 }
493
494 return true;
495 }
496
497
498
499
500
501 /**
502 *
503 *
504 *
505 *
506 *
507 */
508 public boolean traverse(AssertStatement stat, Object user1, Object user2)
509 throws NodeException {
510
511 if(debug)
512 System.out.println("Visiting Assert");
513
514 if(stat.getCode() != null) {
515 visit(stat.getCode(), user1, user2);
516 }
517 return true;
518 }
519
520
521
522
523 /**
524 *
525 *
526 *
527 *
528 *
529 */
530 public boolean traverse(LinkStatement stat, Object user1, Object user2)
531 throws NodeException {
532
533 if(debug)
534 System.out.println("Visiting Link");
535
536 if(stage == F_VARIABLE_REFERENCES) {
537
538 if(debug)
539 System.out.println("Linking link References");
540
541 /**
542 * For an Link statement variable references need to be
543 * resolved in the target header not the current header.
544 * We therefore have to find and substitute the target's
545 * symtable.
546 */
547
548 SymTable.Symbol s = packet.lookupSymbol(stat.getName());
549
550 if(s == null || (s.getDefinition() instanceof StatementContainer) == false)
551 throw new NodeException("Link target header [" + stat.getName() + "] not found", stat);
552
553 StatementContainer target = (StatementContainer)s.getDefinition();
554
555 if(stat.getCode() != null) {
556 visit(stat.getCode(), target.getSymTable(), user2);
557 }
558 }
559
560
561 else if(stage == F_LINK_HEADERS_STAGE2) {
562 StatementContainer header = (StatementContainer)user1;
563 String headerToLinkTo = stat.getName();
564 SymTable.Symbol s = packet.lookupSymbol(headerToLinkTo);
565 if(s == null || (s.getDefinition() instanceof StatementContainer) == false)
566 throw new NodeException("Link target header [" + headerToLinkTo + "] not found", stat);
567
568 StatementContainer target = (StatementContainer)s.getDefinition();
569
570 Node code = stat.getCode(); // Code can be null
571
572 if(debug)
573 System.out.println("Linking links: " + header.getName() + " to " + headerToLinkTo);
574
575 if(code == null || code instanceof OpNode) {
576 target.addLinkAssertion((OpNode)code, header);
577 }
578 else if(code instanceof IntNode) {
579 if(target.getLinkVariable() == null)
580 throw new NodeException("Link target does not have a linked variable set.", code);
581
582 target.setLinkAssertion(((IntNode)code).getInt(), header);
583 }
584
585 if(debug)
586 System.out.println("Resolving link: " + header.getName() + " to " + headerToLinkTo);
587
588 }
589 return true;
590 }
591
592
593
594
595 /**
596 *
597 *
598 *
599 *
600 *
601 */
602 public boolean traverse(MutableReferenceNode node, Object user1, Object user2)
603 throws NodeException {
604
605 if(debug)
606 System.out.println("Visiting MutableReferenceNode");
607
608 /**
609 * Replace Reference node found in the tree with reference from the SymTable.
610 */
611 if(stage == F_VARIABLE_REFERENCES) {
612 SymTable table = (SymTable)user1;
613
614 SymTable.Symbol s = table.lookupSymbol(node.getName());
615 Node refNode;
616 if(s == null) {
617 throw new NodeException("Undefined variable reference \"" + node.getName() + "\" in sym table " + table.getName(), node);
618 }
619
620
621 refNode = s.getDefinition();
622
623 if( user2 instanceof OpNode) {
624 OpNode op = (OpNode)user2;
625
626 if(op.getLeft() == node) {
627 op.setLeft(refNode);
628 }
629 else if(op.getRight() == node) {
630 op.setRight(refNode);
631 }
632 else {
633 throw new NodeException("Can not dereference node " + node.getName() + ". Invalid parent.", node);
634 }
635 }
636 else {
637 node.setReference((ReferenceNode)refNode);
638 // node.setSymTable(((MutableReferenceNode)refNode).getSymTable());
639 // throw new NodeException("Can not dereference node " + node.getName() + ". Parent unknown.", node);
640 }
641 }
642 return true;
643 }
644
645 /**
646 *
647 *
648 *
649 *
650 *
651 */
652 public boolean traverse(VariableStatement stat, Object user1, Object user2)
653 throws NodeException {
654
655 if(debug)
656 System.out.println("Visiting VariableStatement " + stat.getName() + " stage=" + stage);
657
658 if(stage == F_LINK_TYPES) {
659 SymTable table = (SymTable)user1;
660
661 SymTable.Symbol s = types.lookupSymbol(stat.getType());
662 if(s == null || s.getImplementation() == null)
663 throw new NodeException("Unresolved link " + stat.getType(), stat);
664
665 stat.setTypePrimitive((ProtocolPrimitiveFactory)s.getImplementation());
666 }
667
668
669 if(stage == F_RECORD_SYMTABLE) {
670 /**
671 * First record the type to a primitive. Types are recorded in the
672 * special symtable.
673 */
674 if(types.contains(stat.getType()) == false)
675 types.addSymbol(stat.getType(), SymTable.Symbol.PRIMITIVE, null);
676 }
677
678 if(stage == F_VARIABLE_REFERENCES) {
679 SymTable table = (SymTable)user1;
680 MutableReferenceNode ref = new MutableReferenceNode(stat.getName());
681 ref.setSymTable(table);
682 table.addSymbol(stat.getName(), SymTable.Symbol.REFERENCE, ref);
683 }
684
685
686 return true;
687 }
688
689
690 public boolean traverse(ConstReferenceNode node, Object user1, Object user2)
691 throws NodeException {
692
693 if(debug)
694 System.out.println("Visiting ConstReferenceNode " + node.getName() + " stage=" + stage);
695
696 if(stage == F_VARIABLE_REFERENCES) {
697 SymTable table = (SymTable)user1;
698 table.addSymbol(node.getName(), SymTable.Symbol.REFERENCE, node);
699 }
700
701
702 return true;
703 }
704
705
706 public boolean traverse(EnumReferenceNode node, Object user1, Object user2)
707 throws NodeException {
708
709 if(debug)
710 System.out.println("Visiting EnumReferenceNode " + node.getName() + " stage=" + stage);
711
712 if(stage == F_VARIABLE_REFERENCES) {
713 SymTable table = (SymTable)user1;
714 table.addSymbol(node.getName(), SymTable.Symbol.REFERENCE, node);
715
716 if( (user2 instanceof EnumStatement) == false)
717 throw new NodeException("Enum properties can only be declared within 'enum' statement", node);
718
719
720 EnumStatement enum = (EnumStatement)user2;
721 enum.setEnumValue(node.getPrimitive(), node.getString());
722
723 }
724
725
726 return true;
727 }
728
729
730
731
732
733 /**
734 * Special OpNode traversal routine that records the last node visited.
735 *
736 * @param op OpNode to be traversed.
737 * @param user1 Depends at what stage of linking. Usually SymTable.
738 * @param user2 Parent node that called this.
739 */
740 public boolean traverse(OpNode op, Object user1, Object user2)
741 throws NodeException {
742
743 if(stage == F_VARIABLE_REFERENCES) {
744 if(op.getLeft() != null)
745 visit(op.getLeft(), user1, op);
746
747 if(op.getRight() != null)
748 visit(op.getRight(), user1, op);
749 }
750
751 return true;
752 }
753
754
755 /**
756 *
757 *
758 *
759 *
760 *
761 */
762 public boolean traverse(IfStatement stat, Object user1, Object user2)
763 throws NodeException {
764
765 if(debug)
766 System.out.println("Visiting If stage=" + stage);
767
768
769 if(debug)
770 System.out.println("Linking If References");
771
772 if(stat.getCondition() != null) {
773 visit((Node)stat.getCondition(), user1, user2);
774 }
775
776 if(stat.getCode() != null) {
777 visit(stat.getCode(), user1, user2);
778 }
779
780 if(stat.getElseCode() != null) {
781 visit(stat.getElseCode(), user1, user2);
782 }
783 return true;
784 }
785
786 /**
787 *
788 *
789 *
790 *
791 *
792 */
793 public boolean traverse(ForStatement stat, Object user1, Object user2)
794 throws NodeException {
795
796 if(debug)
797 System.out.println("Visiting For stage=" + stage);
798
799
800 if(stat.getInitCode() != null) {
801 visit(stat.getInitCode(), user1, user2);
802 }
803
804 if(stat.getCondition() != null) {
805 visit((Node)stat.getCondition(), user1, user2);
806 }
807
808 if(stat.getForCode() != null) {
809 visit(stat.getForCode(), user1, user2);
810 }
811
812 if(stat.getCode() != null) {
813 visit(stat.getCode(), user1, user2);
814 }
815 return true;
816 }
817
818 public boolean traverse(WhileStatement stat, Object user1, Object user2)
819 throws NodeException {
820
821 if(debug)
822 System.out.println("Visiting For stage=" + stage);
823
824
825 if(stat.getCondition() != null) {
826 visit((Node)stat.getCondition(), user1, user2);
827 }
828
829 if(stat.getCode() != null) {
830 visit(stat.getCode(), user1, user2);
831 }
832
833 return true;
834 }
835
836 public boolean traverse(PrintStatement stat, Object user1, Object user2)
837 throws NodeException {
838
839 if(debug)
840 System.out.println("Visiting Print stage=" + stage);
841
842
843 if(stat.getStringExpression() != null) {
844 visit((Node)stat.getStringExpression(), user1, user2);
845 }
846
847 return true;
848 }
849
850 public boolean traverse(EnumStatement stat, Object user1, Object user2)
851 throws NodeException {
852
853 if(debug)
854 System.out.println("Visiting Print stage=" + stage);
855
856
857
858 if(stage == F_VARIABLE_REFERENCES) {
859 visit(stat.getEnumCode(), user1, stat); // records enum values in stat.
860
861 if( (user2 instanceof FieldStatement) == false)
862 throw new NodeException("'enum' statement can only be associated with a field.", stat);
863
864 FieldStatement field = (FieldStatement)user2;
865 field.setEnumTable(stat);
866 }
867 else if(stat.getEnumCode() != null) {
868 visit(stat.getEnumCode(), user1, user2);
869 }
870
871 return true;
872 }
873
874 public boolean traverse(PropertyStatement stat, Object user1, Object user2)
875 throws NodeException {
876
877 if(debug)
878 System.out.println("Visiting Property stage=" + stage);
879
880
881 visit(stat.getExpressionCode(), user1, stat);
882
883 if(stage == F_VARIABLE_REFERENCES) {
884 String n = stat.getName();
885 SymTable sym = (SymTable)user1;
886 SymTable targetSym = sym.lookupSymTable(stat.getName(), true);
887
888 if( targetSym != null && targetSym != sym) {
889 if(stat.isTargetPermTable())
890 throw new NodeException("Not allowed to set \"perm\" property \"" + stat.getName() + "\" in non-local context", stat);
891
892 if(debug)
893 System.out.println(stat.getName() + " targetSym = " + targetSym.getName());
894
895 sym = targetSym;
896
897 String[] path = n.split(SymTable.DELIMITER);
898 n = path[path.length -1];
899
900 }
901
902 stat.setSymTable(sym);
903
904 if(stat.isTargetPermTable() == false) {
905
906 if(stat.getName().equals(FieldStatement.SIZE) && (user2 instanceof FieldStatement) == false)
907 throw new NodeException("\"" + FieldStatement.SIZE + "\" property can only be applied to 'field' statements.", stat);
908
909 MutableReferenceNode ref = new MutableReferenceNode(n);
910 ref.setSymTable(sym);
911
912 sym.addSymbol(n, ref);
913 return true;
914 }
915
916 /**
917 * Verify that properties are being applied in proper context.
918 */
919 if(stat.getName().equals(FieldStatement.SIZE))
920 throw new NodeException("\"" + FieldStatement.SIZE + "\" property can only be applied in 'local' context.", stat);
921
922
923 if( (user2 instanceof HeaderStatement) || (user2 instanceof FieldStatement) ) {
924 StatementNode node = (StatementNode)user2;
925 Hashtable perm = node.getPermTable();
926 StringNode exp = (StringNode)stat.getExpressionCode();
927 String e = exp.getString();
928
929 /**
930 * First set the property in the PERM table in the statement. Any Headers created will
931 * have a reference to the same table.
932 */
933 StringPrimitive p = new StringPrimitive();
934 p.setValue(e);
935 perm.put(n, p);
936
937 /**
938 * Next we add the variable to the sym table as a primitive so it can be referenced
939 * from within NPL.
940 */
941
942 ConstReferenceNode ref = new ConstReferenceNode(n, p);
943 ref.setSymTable(sym);
944
945 sym.addSymbol(n, ref);
946
947 }
948 else
949 throw new NodeException("property statement only allowed inside a header or a field. (" + user2.getClass().getName() + ")", stat);
950 }
951
952 return true;
953 }
954
955
956 /**
957 * Test function for Linker
958 * @param args command line arguments
959 */
960 public static void main(String [] args) {
961
962 ExpTokenizer tokens = null;
963
964 if(args.length == 1) {
965 tokens = new ExpTokenizer(new StringReader(args[0]));
966 }
967
968 else if(args.length == 2) {
969 try {tokens = new ExpTokenizer(new FileReader(args[1]));} catch(FileNotFoundException f) { f.printStackTrace(); return;}
970 }
971 else
972 tokens = new ExpTokenizer();
973
974
975
976 StatementParser parser;
977 parser = new StatementParser();
978
979 NodeList block = new NodeList();
980 Node node;
981
982 try {
983 while(tokens.hasMoreTokens()) {
984 block.add(node = parser.parseInstruction(tokens));
985 System.out.println(node);
986 }
987
988 Linker linker = new Linker(new SymTable("test"));
989 linker.recordDefinitions(block);
990 linker.loadTypePrimitives();
991 linker.resolveTypes(block);
992
993 System.out.println("SYMTABLE=======================================");
994 System.out.println(linker.global);
995 }
996 catch(SyntaxError se) {
997 se.printStackTrace();
998 }
999 catch(NodeException ne) {
1000 ne.printStackTrace();
1001 }
1002
1003
1004
1005 }
1006
1007} /* END OF: Linker */