PageRenderTime 123ms CodeModel.GetById 16ms app.highlight 69ms RepoModel.GetById 11ms app.codeStats 1ms

/adela/src/net/sourceforge/adela/interpreter/ADELAParseTree.java

https://github.com/hce/govm
Java | 1561 lines | 1207 code | 84 blank | 270 comment | 191 complexity | d5fe8c88a6beec28da26038cd98153e6 MD5 | raw file
   1/**
   2 * adela
   3 * An embeddable, secure scripting language
   4 * for java applications
   5 *
   6 * (C) 2007, Hans-Christian Esperer
   7 * hc at hcespererorg
   8 * All rights reserved.
   9 *
  10 * Redistribution and use in source and binary forms, with or without
  11 * modification, are permitted provided that the following conditions
  12 * are met:
  13 *
  14 * * Redistributions of source code must retain the above copyright
  15 *   notice, this list of conditions and the following disclaimer.
  16 * * Redistributions in binary form must reproduce the above
  17 *   copyright notice, this list of conditions and the following
  18 *   disclaimer in the documentation and/or other materials provided
  19 *   with the distribution.
  20 * * Neither the name of the H. Ch. Esperer nor the names of his
  21 *   contributors may be used to endorse or promote products derived
  22 *   from this software without specific prior written permission.
  23 *
  24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  28 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  35 * POSSIBILITY OF SUCH DAMAGE
  36 **************************************************************************/
  37
  38package net.sourceforge.adela.interpreter;
  39
  40import java.io.DataInput;
  41import java.io.DataOutput;
  42import java.io.Externalizable;
  43import java.io.IOException;
  44import java.io.ObjectInput;
  45import java.io.ObjectOutput;
  46import java.io.PrintStream;
  47import java.io.UnsupportedEncodingException;
  48import java.util.ArrayList;
  49import java.util.Iterator;
  50import java.util.Map;
  51import java.util.Random;
  52import java.util.Vector;
  53
  54import net.sourceforge.adela.enums.EValueType;
  55import net.sourceforge.adela.exceptions.ADELASyntaxException;
  56import net.sourceforge.adela.exceptions.EvaluatorIsNullException;
  57import net.sourceforge.adela.exceptions.FunctionNotFoundException;
  58import net.sourceforge.adela.exceptions.WrongParameterCountException;
  59import net.sourceforge.adela.govm.GOVMWriter;
  60import net.sourceforge.adela.govm.Opcode;
  61import net.sourceforge.adela.govm.StatementResolver;
  62import net.sourceforge.adela.interfaces.IEvaluator;
  63
  64/**
  65 * A parse tree containing the parsing instructions for one statement
  66 * 
  67 * @author hc
  68 */
  69public class ADELAParseTree implements Externalizable {
  70	/**
  71	 * 
  72	 */
  73	private static final long serialVersionUID = -3172093318940768196L;
  74
  75	/**
  76	 * Left part
  77	 */
  78	private ADELAParseTree leftTree;
  79
  80	/**
  81	 * Right part
  82	 */
  83	private ADELAParseTree rightTree;
  84
  85	/**
  86	 * The operator to use
  87	 */
  88	private char operator;
  89
  90	/**
  91	 * Statement to evaluate
  92	 */
  93	private String statement;
  94
  95	/**
  96	 * Cached int
  97	 */
  98	private int intValue;
  99
 100	/**
 101	 * Cached long
 102	 */
 103	private long longValue;
 104
 105	/**
 106	 * cached double
 107	 */
 108	private double doubleValue;
 109
 110	/**
 111	 * cached string
 112	 */
 113	private String stringValue;
 114
 115	/**
 116	 * Function to call on evaluation
 117	 */
 118	private String functionToCall;
 119
 120	/**
 121	 * Parameters to call function with
 122	 */
 123	private ADELAParseTree[] functionParameters;
 124
 125	/**
 126	 * Type of value we're storing
 127	 */
 128	private EValueType valueType;
 129
 130	/**
 131	 * True if the result of this parse tree should be returned if used in a
 132	 * function block
 133	 */
 134	private boolean returnStatement;
 135
 136	/**
 137	 * A PRNG for random NOP insertion to prevent cheating.
 138	 */
 139	private Random prng = new Random();
 140
 141	/**
 142	 * Instanciiate a leaf with an int value
 143	 * 
 144	 * @param value
 145	 *            int value
 146	 */
 147	protected ADELAParseTree(int value) {
 148		this.valueType = EValueType.intValue;
 149		this.intValue = value;
 150	}
 151
 152	/**
 153	 * Instanciiate a leaf with a double value
 154	 * 
 155	 * @param value
 156	 *            double value
 157	 */
 158	protected ADELAParseTree(double value) {
 159		this.valueType = EValueType.floatValue;
 160		this.doubleValue = value;
 161	}
 162
 163	/**
 164	 * Instanciiate a leaf with a long value
 165	 * 
 166	 * @param value
 167	 *            long value
 168	 */
 169	protected ADELAParseTree(long value) {
 170		this.valueType = EValueType.longValue;
 171		this.longValue = value;
 172	}
 173
 174	/**
 175	 * Instanciiate a leaf that calls a function
 176	 * 
 177	 * @param functionToCall
 178	 *            name of function to call
 179	 * @param arguments
 180	 *            parse trees of arguments to pass to that function
 181	 */
 182	protected ADELAParseTree(String functionToCall, ADELAParseTree[] arguments) {
 183		this.functionToCall = functionToCall;
 184		this.functionParameters = arguments;
 185		this.valueType = EValueType.functioncall;
 186	}
 187
 188	/**
 189	 * Instanciiate a leaf with a string
 190	 * 
 191	 * @param value
 192	 *            string to use
 193	 * @param evaluate
 194	 *            evaluate string?
 195	 */
 196	protected ADELAParseTree(String value, boolean evaluate) {
 197		if (evaluate) {
 198			this.statement = value;
 199			this.valueType = EValueType.statement;
 200		} else {
 201			this.valueType = EValueType.stringValue;
 202			this.stringValue = value;
 203		}
 204	}
 205
 206	/**
 207	 * Instanciiate a node that performs an operation
 208	 * 
 209	 * @param leftTree
 210	 *            left sub-tree to evaluate
 211	 * @param rightTree
 212	 *            right sub-tree to evaluate
 213	 * @param operator
 214	 *            operator to use for operation
 215	 */
 216	protected ADELAParseTree(ADELAParseTree leftTree, ADELAParseTree rightTree,
 217			char operator) {
 218		this.leftTree = leftTree;
 219		this.rightTree = rightTree;
 220		this.operator = operator;
 221		this.valueType = EValueType.operation;
 222	}
 223
 224	/**
 225	 * Instanciiate a node that calls a method
 226	 * 
 227	 * @param leftTree
 228	 *            tree that gives us the class to call into
 229	 * @param method
 230	 *            method to call
 231	 * @throws ADELASyntaxException
 232	 */
 233	private ADELAParseTree(ADELAParseTree leftTree, String method)
 234			throws ADELASyntaxException {
 235		this.leftTree = leftTree;
 236		this.operator = '.';
 237		this.handleFunction(method);
 238		this.valueType = EValueType.operation;
 239	}
 240
 241	public ADELAParseTree() {
 242	}
 243
 244	/**
 245	 * Evaluate this subtree
 246	 * 
 247	 * @param locals
 248	 *            map of local variables to use for evaluation
 249	 * @param evaluator
 250	 *            evaluator to use
 251	 * @return evaluation result
 252	 * @throws FunctionNotFoundException
 253	 * @throws WrongParameterCountException
 254	 * @throws ADELAException
 255	 */
 256	public Object doEvaluate(Map<String, Object> locals, IEvaluator evaluator)
 257			throws WrongParameterCountException, FunctionNotFoundException,
 258			ADELAException {
 259		if (evaluator == null) {
 260			throw new EvaluatorIsNullException();
 261		}
 262		return evaluate(locals, evaluator);
 263	}
 264
 265	/**
 266	 * @param locals
 267	 * @param evaluator
 268	 * @return
 269	 * @throws WrongParameterCountException
 270	 * @throws FunctionNotFoundException
 271	 * @throws ADELAException
 272	 */
 273	@SuppressWarnings("unchecked")
 274	private Object evaluate(Map<String, Object> locals, IEvaluator evaluator)
 275			throws WrongParameterCountException, FunctionNotFoundException,
 276			ADELAException {
 277		switch (valueType) {
 278		case floatValue:
 279			return Double.valueOf(doubleValue);
 280		case intValue:
 281			return Integer.valueOf(intValue);
 282		case longValue:
 283			return Long.valueOf(longValue);
 284		case stringValue:
 285		case variable:
 286			return stringValue;
 287		case statement:
 288			return evaluator.evaluate(locals, statement);
 289		case functioncall:
 290			return evaluator.callFunction(null, this.functionToCall, locals,
 291					evaluateParameters(this.functionParameters, locals,
 292							evaluator));
 293		default:
 294			;
 295		}
 296
 297		Object leftValue = leftTree.evaluate(locals, evaluator);
 298		Object rightValue;
 299		if (rightTree != null) {
 300			if (operator != '.') {
 301				if (operator == 'o') {
 302					if (((Boolean) leftValue) == false) {
 303						rightValue = rightTree.evaluate(locals, evaluator);
 304					} else {
 305						rightValue = null;
 306					}
 307				} else if (operator == 'a') {
 308					if (((Boolean) leftValue) == true) {
 309						rightValue = rightTree.evaluate(locals, evaluator);
 310					} else {
 311						rightValue = null;
 312					}
 313				} else {
 314					rightValue = rightTree.evaluate(locals, evaluator);
 315				}
 316			} else {
 317				rightValue = null;
 318			}
 319		} else {
 320			rightValue = null;
 321		}
 322
 323		Comparable cLeft, cRight;
 324		switch (operator) {
 325		case '+': {
 326			if (leftValue.getClass() == Integer.class) {
 327				return (Integer) leftValue + (Integer) rightValue;
 328			} else if (leftValue.getClass() == Double.class) {
 329				return (Double) leftValue + (Double) rightValue;
 330			} else if (leftValue.getClass() == String.class) {
 331				return (String) leftValue + (String) rightValue;
 332			} else if (leftValue.getClass() == Long.class) {
 333				return (Long) leftValue + (Long) rightValue;
 334			}
 335		}
 336			// substraction
 337		case '-':
 338			if (leftValue.getClass() == Integer.class) {
 339				return (Integer) leftValue - (Integer) rightValue;
 340			} else if (leftValue.getClass() == Double.class) {
 341				return (Double) leftValue - (Double) rightValue;
 342			} else if (leftValue.getClass() == Long.class) {
 343				return (Long) leftValue - (Long) rightValue;
 344			}
 345			// division
 346		case '/':
 347			if (leftValue.getClass() == Integer.class) {
 348				return (Integer) leftValue / (Integer) rightValue;
 349			} else if (leftValue.getClass() == Double.class) {
 350				return (Double) leftValue / (Double) rightValue;
 351			} else if (leftValue.getClass() == Long.class) {
 352				return (Long) leftValue / (Long) rightValue;
 353			}
 354			// multiplication
 355		case '*':
 356			if (leftValue.getClass() == Integer.class) {
 357				return (Integer) leftValue * (Integer) rightValue;
 358			} else if (leftValue.getClass() == Double.class) {
 359				return (Double) leftValue * (Double) rightValue;
 360			} else if (leftValue.getClass() == Long.class) {
 361				return (Long) leftValue * (Long) rightValue;
 362			}
 363			// power
 364		case '^':
 365			return Math.pow((Double) leftValue, (Double) rightValue);
 366			// shift right
 367		case 'x':
 368			return ((Integer) leftValue) >> ((Integer) rightValue);
 369			// shift left
 370		case 'y':
 371			return ((Integer) leftValue) << ((Integer) rightValue);
 372			// comparision: smaller than
 373		case '<':
 374			cLeft = (Comparable) leftValue;
 375			cRight = (Comparable) rightValue;
 376			return cLeft.compareTo(cRight) < 0;
 377			// comparision: greater than
 378		case '>':
 379			cLeft = (Comparable) leftValue;
 380			cRight = (Comparable) rightValue;
 381			return cLeft.compareTo(cRight) > 0;
 382		case 'l':
 383			cLeft = (Comparable) leftValue;
 384			cRight = (Comparable) rightValue;
 385			return (cLeft.compareTo(cRight) < 0)
 386					|| (leftValue.equals(rightValue));
 387		case 'g':
 388			cLeft = (Comparable) leftValue;
 389			cRight = (Comparable) rightValue;
 390			return (cLeft.compareTo(cRight) > 0)
 391					|| (leftValue.equals(rightValue));
 392		case '=':
 393			// assignment
 394			locals.put((String) leftValue, rightValue);
 395			break;
 396		// comparision: equals
 397		case 'e':
 398			return leftValue.equals(rightValue);
 399			// equals not
 400		case 'n':
 401			return !leftValue.equals(rightValue);
 402			// and
 403		case 'a':
 404			return ((Boolean) leftValue && (Boolean) rightValue);
 405			// or
 406		case 'o':
 407			return ((Boolean) leftValue || (Boolean) rightValue);
 408			// someone screwed up
 409		case '&':
 410			return ((Integer) leftValue & (Integer) rightValue);
 411			// or
 412		case '|':
 413			return ((Integer) leftValue | (Integer) rightValue);
 414			// method invocatoin
 415		case '.':
 416			return evaluator.callFunction(leftValue, this.functionToCall,
 417					locals, evaluateParameters(this.functionParameters, locals,
 418							evaluator));
 419			// someone screwed up
 420		default:
 421			System.out.format("Operator %c unknown\n", operator);
 422		}
 423		return null;
 424	}
 425
 426	private Object[] evaluateParameters(ADELAParseTree[] functionParameters,
 427			Map<String, Object> locals, IEvaluator evaluator)
 428			throws WrongParameterCountException, FunctionNotFoundException,
 429			ADELAException {
 430		Object[] params = new Object[functionParameters.length];
 431		for (int i = 0; i < params.length; i++) {
 432			params[i] = functionParameters[i].doEvaluate(locals, evaluator);
 433		}
 434		return params;
 435	}
 436
 437	/**
 438	 * The Parser.
 439	 * 
 440	 * @param s
 441	 *            String to parse
 442	 * @return Parsing result
 443	 * @throws ADELASyntaxException
 444	 * @throws ADELAException
 445	 *             On shit
 446	 * @throws FunctionNotFoundException
 447	 * @throws WrongParameterCountException
 448	 */
 449	public static ADELAParseTree Parse(String s) throws ADELASyntaxException {
 450		char elem;
 451		char operator;
 452		int i;
 453		int iLen;
 454		int iBrackets;
 455		Boolean bFound;
 456		String sLeft;
 457		String sRight;
 458		ADELAParseTree leftTree;
 459		ADELAParseTree rightTree;
 460
 461		boolean bNegate = false;
 462
 463		if (s == null) {
 464			return null;
 465		}
 466
 467		if (s.length() == 0) {
 468			return null;
 469		}
 470
 471		/*
 472		 * trim us
 473		 */
 474		s = s.trim();
 475
 476		/*
 477		 * our length
 478		 */
 479
 480		iLen = s.length();
 481
 482		/*
 483		 * Empty string; return null
 484		 */
 485		if (iLen == 0) {
 486			return null;
 487		}
 488
 489		if (s.startsWith("return ")) {
 490			ADELAParseTree returnTree = Parse(s.substring(7));
 491			returnTree.returnStatement = true;
 492			return returnTree;
 493		}
 494
 495		/*
 496		 * special cases
 497		 */
 498		// string? pass to evaluate
 499		if (s.charAt(0) == '"') {
 500			return PreEvaluate(s, false);
 501		}
 502		// negation? mark it, then strip the negation sign
 503		else if (s.charAt(0) == '-') {
 504			bNegate = true;
 505			s = s.substring(1);
 506			iLen--;
 507			if (iLen < 1)
 508				throw new RuntimeException("Error: invalid expression!");
 509		}
 510
 511		/*
 512		 * Find the first operator that does not reside within brackets
 513		 */
 514		bFound = false;
 515		iBrackets = 0;
 516		for (i = 0; i < iLen; i++) {
 517			elem = s.charAt(i);
 518
 519			switch (elem) {
 520			case '*':
 521			case '/':
 522			case '+':
 523			case '^':
 524			case '-':
 525			case '<':
 526			case '>':
 527			case '&':
 528			case '|':
 529			case '=':
 530			case ':':
 531			case '!':
 532				if (iBrackets == 0) {
 533					bFound = true;
 534				}
 535
 536				break;
 537			case '.':
 538				if ((s.charAt(i + 1) < '0') || (s.charAt(i + 1) > '9')) {
 539					if (iBrackets == 0) {
 540						bFound = true;
 541					}
 542				}
 543				break;
 544			case '(':
 545			case '[':
 546				iBrackets++;
 547				break;
 548			case ')':
 549			case ']':
 550				iBrackets--;
 551				break;
 552			}
 553
 554			if (bFound)
 555				break;
 556		}
 557
 558		/*
 559		 * Equal number of opening and closing brackets!?
 560		 */
 561		assert (iBrackets == 0);
 562
 563		/*
 564		 * Get the part left of the operator
 565		 */
 566		sLeft = s.substring(0, i).trim();
 567
 568		// hack:
 569		// if we have _one_ equals sign ('='), the
 570		// left part is a string, never mind the
 571		// fact it isn't quoted ('"')
 572		if (bFound && ((s.length() - i) >= 2) && (s.charAt(i) == '=')
 573				&& (s.charAt(i + 1) != '=')) {
 574			leftTree = new ADELAParseTree(sLeft, false);
 575			leftTree.valueType = EValueType.variable;
 576		} else {
 577			leftTree = PreEvaluate(sLeft, bNegate);
 578		}
 579
 580		/*
 581		 * We have a right part
 582		 */
 583		if (bFound) {
 584			operator = s.charAt(i);
 585			switch (operator) {
 586			case '&':
 587				if (s.charAt(i + 1) == '&') {
 588					operator = 'a';
 589					i++;
 590				}
 591				break;
 592
 593			case '|':
 594				if (s.charAt(i + 1) == '|') {
 595					operator = 'o';
 596					i++;
 597				}
 598				break;
 599
 600			case '=':
 601				if (s.charAt(i + 1) == '=') {
 602					i++;
 603					operator = 'e';
 604				}
 605				break;
 606
 607			case '<':
 608				if (s.charAt(i + 1) == '=') {
 609					i++;
 610					operator = 'l';
 611				} else if (s.charAt(i + 1) == '<') {
 612					i++;
 613					operator = 'y';
 614				}
 615				break;
 616
 617			case '>':
 618				if (s.charAt(i + 1) == '=') {
 619					i++;
 620					operator = 'g';
 621				} else if (s.charAt(i + 1) == '>') {
 622					i++;
 623					operator = 'x';
 624				}
 625				break;
 626
 627			case '!':
 628				if (s.charAt(i + 1) == '=') {
 629					i++;
 630					operator = 'n';
 631				}
 632				break;
 633
 634			default:
 635				;
 636			}
 637
 638			// get the right term
 639			sRight = s.substring(i + 1);
 640
 641			if (operator == '.') {
 642				// function call
 643				Object[] methodCallParts = getMethodCallParts(sRight);
 644				if (methodCallParts.length == 3) {
 645
 646					rightTree = Parse((String) methodCallParts[2]);
 647				} else {
 648					rightTree = null;
 649				}
 650				String method = (String) methodCallParts[0];
 651				Iterator<String> methods = getMethods(method).iterator();
 652				while (methods.hasNext()) {
 653					leftTree = new ADELAParseTree(leftTree, methods.next());
 654				}
 655
 656				if (rightTree != null) {
 657					return new ADELAParseTree(leftTree, rightTree,
 658							(Character) methodCallParts[1]);
 659				}
 660				return leftTree;
 661			} else {
 662				// parse it
 663				rightTree = Parse(sRight);
 664
 665				return new ADELAParseTree(leftTree, rightTree, operator);
 666			}
 667		}
 668
 669		return leftTree;
 670	}
 671
 672	private static ArrayList<String> getMethods(String s) {
 673		char elem;
 674		int i;
 675		int iLen;
 676		int iBrackets;
 677		Boolean bFound;
 678		ArrayList<String> methods = new ArrayList<String>();
 679
 680		if (s == null) {
 681			return null;
 682		}
 683		if (s.length() == 0) {
 684			return null;
 685		}
 686
 687		bFound = true;
 688		while (bFound) {
 689			s = s.trim();
 690			iLen = s.length();
 691
 692			/*
 693			 * Find the first operator that does not reside within brackets
 694			 */
 695			bFound = false;
 696			iBrackets = 0;
 697			for (i = 0; i < iLen; i++) {
 698				elem = s.charAt(i);
 699
 700				switch (elem) {
 701				case '.':
 702
 703					if (iBrackets == 0) {
 704						methods.add(s.substring(0, i));
 705						s = s.substring(i + 1);
 706						bFound = true;
 707					}
 708
 709					break;
 710				case '(':
 711					iBrackets++;
 712					break;
 713				case ')':
 714					iBrackets--;
 715					break;
 716				}
 717				if (bFound) {
 718					break;
 719				}
 720			}
 721
 722		}
 723
 724		s = s.trim();
 725		if (s.length() != 0) {
 726			methods.add(s);
 727		}
 728		return methods;
 729	}
 730
 731	private static Object[] getMethodCallParts(String s) {
 732		char elem;
 733		char operator;
 734		int i;
 735		int iLen;
 736		int iBrackets;
 737		Boolean bFound;
 738
 739		if (s == null) {
 740			return null;
 741		}
 742		if (s.length() == 0) {
 743			return null;
 744		}
 745
 746		s = s.trim();
 747		iLen = s.length();
 748
 749		/*
 750		 * Find the first operator that does not reside within brackets
 751		 */
 752		bFound = false;
 753		iBrackets = 0;
 754		for (i = 0; i < iLen; i++) {
 755			elem = s.charAt(i);
 756
 757			switch (elem) {
 758			case '*':
 759			case '/':
 760			case '+':
 761			case '-':
 762			case '^':
 763			case '<':
 764			case '>':
 765			case '&':
 766			case '|':
 767			case '=':
 768			case ':':
 769			case '!':
 770				if (iBrackets == 0) {
 771					bFound = true;
 772				}
 773
 774				break;
 775			case '(':
 776				iBrackets++;
 777				break;
 778			case ')':
 779				iBrackets--;
 780				break;
 781			}
 782
 783			if (bFound)
 784				break;
 785		}
 786
 787		/*
 788		 * Equal number of opening and closing brackets!?
 789		 */
 790		assert (iBrackets == 0);
 791
 792		if (bFound) {
 793			Object[] res = new Object[3];
 794			res[0] = s.substring(0, i);
 795			operator = s.charAt(i);
 796			if (operator == '>') {
 797				if (s.charAt(i + 1) == '=') {
 798					operator = 'g';
 799					i++;
 800				}
 801			}
 802			if (operator == '<') {
 803				if (s.charAt(i + 1) == '=') {
 804					operator = 'l';
 805					i++;
 806				}
 807			}
 808			if (operator == '=') {
 809				if (s.charAt(i + 1) == '=') {
 810					operator = 'e';
 811					i++;
 812				}
 813			}
 814			if (operator == '!') {
 815				if (s.charAt(i + 1) == '=') {
 816					operator = 'n';
 817					i++;
 818				}
 819			}
 820			if (operator == '&') {
 821				if (s.charAt(i + 1) == '&') {
 822					operator = 'a';
 823					i++;
 824				}
 825			}
 826			if (operator == '|') {
 827				if (s.charAt(i + 1) == '|') {
 828					operator = 'o';
 829					i++;
 830				}
 831			}
 832			res[1] = Character.valueOf(operator);
 833			res[2] = s.substring(i + 1);
 834			return res;
 835		} else {
 836			Object[] res = new Object[1];
 837			res[0] = s;
 838			return res;
 839		}
 840	}
 841
 842	/**
 843	 * Evaulate a function string
 844	 * 
 845	 * @param s
 846	 *            String to evaluate
 847	 * @param locals
 848	 *            Local variables
 849	 * @param negate
 850	 *            negate value?
 851	 * @return Evaluation result
 852	 * @throws ADELASyntaxException
 853	 * @throws FunctionNotFoundException
 854	 * @throws WrongParameterCountException
 855	 */
 856	private static ADELAParseTree PreEvaluate(String s, boolean negate)
 857			throws ADELASyntaxException {
 858		int iPos;
 859		char cAT;
 860		String sFuncName;
 861		Vector<String> sParams;
 862		Iterator<String> iParams;
 863		ADELAParseTree[] vParams;
 864
 865		// are we zero-length?
 866		if (s.length() == 0) {
 867			// return null
 868			return null;
 869		}
 870
 871		/*
 872		 * Determine the type of term by looking at the very first character
 873		 */
 874		cAT = s.charAt(0);
 875		/*
 876		 * Are we a bracket? Find the last bracket, then strip them and parse
 877		 * the rest
 878		 */
 879		if (cAT == '(') {
 880			iPos = s.lastIndexOf(')');
 881			assert (iPos != -1);
 882
 883			return Parse(s.substring(1, iPos));
 884		}
 885		/*
 886		 * Are we a string? Return a string class
 887		 */
 888		else if (cAT == '"') {
 889			iPos = s.lastIndexOf('"');
 890			assert (iPos != -1);
 891
 892			return new ADELAParseTree(s.substring(1, iPos), false);
 893		}
 894
 895		/*
 896		 * Are we a number?
 897		 */
 898		else if ((cAT >= '0') && (cAT <= '9')) {
 899			/*
 900			 * Int or Double? The '.' says it all...
 901			 */
 902			if (s.indexOf('.') == -1) {
 903				String trimmedS = s.trim();
 904				if (trimmedS.endsWith("L")) {
 905					if (!negate) {
 906						return new ADELAParseTree(Long.parseLong(trimmedS
 907								.substring(0, trimmedS.length() - 1)));
 908					} else {
 909						return new ADELAParseTree(Long.valueOf(0 - Long
 910								.parseLong(trimmedS.substring(0, trimmedS
 911										.length() - 1))));
 912					}
 913				} else {
 914					if (!negate) {
 915						return new ADELAParseTree(Integer.parseInt(trimmedS
 916								.trim()));
 917					} else {
 918						return new ADELAParseTree(Integer.valueOf(0 - Integer
 919								.parseInt(trimmedS.trim())));
 920					}
 921				}
 922			} else {
 923				if (!negate) {
 924					return new ADELAParseTree(Double.parseDouble(s.trim()));
 925				} else {
 926					return new ADELAParseTree(Double.valueOf(0 - Double
 927							.parseDouble(s.trim())));
 928				}
 929			}
 930		}
 931		/*
 932		 * We're neither. We must be a function or a variable
 933		 */
 934		else {
 935			iPos = s.indexOf('(');
 936			sFuncName = s;
 937			/*
 938			 * function?
 939			 */
 940			if (iPos != -1) {
 941				/*
 942				 * yes, collect parameters, then jump into the callback
 943				 * interface
 944				 */
 945				sFuncName = s.substring(0, iPos);
 946				s = s.substring(iPos);
 947
 948				iPos = s.lastIndexOf(')');
 949				if (iPos == -1) {
 950					throw new ADELASyntaxException("Invalid function call: "
 951							+ s);
 952				}
 953
 954				// sParams = s.substring(1, iPos).split(",");
 955				sParams = GetParams(s.substring(1, iPos));
 956				iParams = sParams.iterator();
 957				vParams = new ADELAParseTree[sParams.size()];
 958
 959				for (int i = 0; i < vParams.length; i++) {
 960					vParams[i] = Parse(iParams.next());
 961				}
 962
 963				return new ADELAParseTree(sFuncName, vParams);
 964				// return CallFunction(sFuncName, locals, vParams);
 965			} else {
 966				/*
 967				 * Variable, get our value
 968				 */
 969				// if (!mVariables.containsKey(sFuncName.trim()))
 970				String sVarName = sFuncName.trim();
 971				return new ADELAParseTree(sVarName, true);
 972			}
 973		}
 974	}
 975
 976	/**
 977	 * Get function call parameters
 978	 * 
 979	 * @param s
 980	 *            parameter string, params must be separated by commas.
 981	 * @return param string array
 982	 */
 983	public static Vector<String> GetParams(String s) {
 984		int i;
 985		boolean bInString;
 986		int iBR;
 987		boolean bFound;
 988		Vector<String> sParams;
 989
 990		sParams = new Vector<String>();
 991
 992		// iterate linewise
 993		while (s.length() != 0) {
 994			bInString = false;
 995			iBR = 0;
 996			bFound = false;
 997			for (i = 0; i < s.length(); i++) {
 998				switch (s.charAt(i)) {
 999				case '"':
1000					bInString = !bInString;
1001					break;
1002				case '(':
1003				case '{':
1004					iBR++;
1005					break;
1006				case ')':
1007				case '}':
1008					iBR--;
1009					break;
1010				case ',':
1011					if ((!bInString) && (iBR == 0)) {
1012						sParams.add(s.substring(0, i));
1013						s = s.substring(i + 1);
1014						bFound = true;
1015					}
1016					break;
1017				default:
1018					break;
1019				}
1020
1021				if (bFound)
1022					break;
1023			}
1024
1025			if (!bFound) {
1026				sParams.add(s);
1027				s = "";
1028			}
1029		}
1030
1031		// return the result
1032		return sParams;
1033	}
1034
1035	public void compile(GOVMWriter gw, StatementResolver sr,
1036			PrintStream logStream) throws ADELACompilerException {
1037		if (prng.nextBoolean()) {
1038			gw.writeOpcode(Opcode.NOP);
1039		}
1040		switch (this.valueType) {
1041		case floatValue:
1042			break;
1043		case functioncall:
1044			if (this.leftTree == null) {
1045				// syscall
1046				if ("putc".equals(this.functionToCall)) {
1047					this.functionParameters[0].compile(gw, sr, logStream);
1048					gw.writeOpcode(Opcode.LI);
1049					gw.writeShort((short) 1);
1050					gw.writeOpcode(Opcode.SYSCALL);
1051					gw.writeOpcode(Opcode.POP);
1052					gw.writeOpcode(Opcode.POP);
1053				} else if ("open".equals(this.functionToCall)) {
1054					this.functionParameters[0].compile(gw, sr, logStream);
1055					this.functionParameters[1].compile(gw, sr, logStream);
1056					gw.writeOpcode(Opcode.LI);
1057					gw.writeShort((short) 5);
1058					gw.writeOpcode(Opcode.SYSCALL);
1059				} else if ("close".equals(this.functionToCall)) {
1060					this.functionParameters[0].compile(gw, sr, logStream);
1061					gw.writeOpcode(Opcode.LI);
1062					gw.writeShort((short) 6);
1063					gw.writeOpcode(Opcode.SYSCALL);
1064				} else if ("fputc".equals(this.functionToCall)) {
1065					this.functionParameters[0].compile(gw, sr, logStream);
1066					this.functionParameters[1].compile(gw, sr, logStream);
1067					gw.writeOpcode(Opcode.LI);
1068					gw.writeShort((short) 8);
1069					gw.writeOpcode(Opcode.SYSCALL);
1070				} else if ("fgetc".equals(this.functionToCall)) {
1071					this.functionParameters[0].compile(gw, sr, logStream);
1072					gw.writeOpcode(Opcode.LI);
1073					gw.writeShort((short) 7);
1074					gw.writeOpcode(Opcode.SYSCALL);
1075				} else if ("halt".equals(this.functionToCall)) {
1076					gw.writeOpcode(Opcode.LI);
1077					gw.writeShort((short) 0);
1078					gw.writeOpcode(Opcode.SYSCALL);
1079				} else if ("getc".equals(this.functionToCall)) {
1080					gw.writeOpcode(Opcode.LI);
1081					gw.writeShort((short) 2);
1082					gw.writeOpcode(Opcode.SYSCALL);
1083					gw.writeOpcode(Opcode.ROT);
1084					gw.writeOpcode(Opcode.POP);
1085				} else if ("info".equals(this.functionToCall)) {
1086					gw.writeOpcode(Opcode.LI);
1087					gw.writeShort((short) 3);
1088					gw.writeOpcode(Opcode.SYSCALL);
1089					gw.writeOpcode(Opcode.POP);
1090				} else if ("peekw".equals(this.functionToCall)) {
1091					this.functionParameters[0].compile(gw, sr, logStream);
1092					gw.writeOpcode(Opcode.LW);
1093					gw.writeOpcode(Opcode.ROT);
1094					gw.writeOpcode(Opcode.POP);
1095				} else if ("peekb".equals(this.functionToCall)) {
1096					this.functionParameters[0].compile(gw, sr, logStream);
1097					gw.writeOpcode(Opcode.LB);
1098					gw.writeOpcode(Opcode.ROT);
1099					gw.writeOpcode(Opcode.POP);
1100				} else if ("pokew".equals(this.functionToCall)) {
1101					this.functionParameters[0].compile(gw, sr, logStream);
1102					this.functionParameters[1].compile(gw, sr, logStream);
1103					gw.writeOpcode(Opcode.SW);
1104					gw.writeOpcode(Opcode.POP);
1105					gw.writeOpcode(Opcode.POP);
1106				} else if ("pokeb".equals(this.functionToCall)) {
1107					this.functionParameters[0].compile(gw, sr, logStream);
1108					this.functionParameters[1].compile(gw, sr, logStream);
1109					gw.writeOpcode(Opcode.SB);
1110					gw.writeOpcode(Opcode.POP);
1111					gw.writeOpcode(Opcode.POP);
1112				} else if ("JMPABSOLUTE".equals(this.functionToCall)) {
1113					logStream
1114							.println("WARNING: using JMPABSOLUTE, which is undocumented.");
1115					this.functionParameters[0].compile(gw, sr, logStream);
1116					gw.writeOpcode(Opcode.JMP);
1117				} else if ("CALLABSOLUTE".equals(this.functionToCall)) {
1118					logStream
1119							.println("WARNING: using CALLABSOLUTE, which is undocumented.");
1120					this.functionParameters[0].compile(gw, sr, logStream);
1121					gw.writeOpcode(Opcode.CALL);
1122				} else if ("POKES".equals(this.functionToCall)) {
1123					logStream
1124							.println("WARNING: using POKES, which is undocumented.");
1125					this.functionParameters[0].compile(gw, sr, logStream);
1126					this.functionParameters[1].compile(gw, sr, logStream);
1127					gw.writeOpcode(Opcode.SWS);
1128				} else if ("POP".equals(this.functionToCall)) {
1129					logStream
1130							.println("WARNING: using POP, which is undocumented.");
1131					gw.writeOpcode(Opcode.POP);
1132				} else {
1133					for (ADELAParseTree apt : this.functionParameters) {
1134						apt.compile(gw, sr, logStream);
1135					}
1136					gw.setGoto("__FUNCTION__" + this.functionToCall,
1137							Opcode.CALL);
1138					// Save retval
1139					gw.writeOpcode(Opcode.MOVA);
1140					// Remove parameters from stack
1141					for (int i = 0; i < this.functionParameters.length; i++) {
1142						gw.writeOpcode(Opcode.POP);
1143					}
1144					// Push retval back on the stack
1145					gw.writeOpcode(Opcode.AMOV);
1146				}
1147			}
1148			break;
1149		case intValue:
1150			gw.writeOpcode(Opcode.LI);
1151			gw.writeShort((short) this.intValue);
1152			break;
1153		case longValue:
1154			break;
1155		case operation:
1156			this.leftTree.compile(gw, sr, logStream);
1157			this.rightTree.compile(gw, sr, logStream);
1158			switch (this.operator) {
1159			case '+':
1160				gw.writeOpcode(Opcode.ADD);
1161				break;
1162			case '-':
1163				gw.writeOpcode(Opcode.SUB);
1164				break;
1165			case '=': {
1166				String[] varParts = leftTree.stringValue.split("[]\\[]");
1167				String varName = varParts[0];
1168				if (sr.globorloc(varName) < 0) {
1169					gw.writeOpcode(Opcode.SW);
1170				} else {
1171					gw.writeOpcode(Opcode.ROT);
1172					gw.writeOpcode(Opcode.FMOV);
1173					gw.writeOpcode(Opcode.ADD);
1174					gw.writeOpcode(Opcode.ROT);
1175					gw.writeOpcode(Opcode.SWS);
1176				}
1177				gw.writeOpcode(Opcode.POP);
1178				gw.writeOpcode(Opcode.POP);
1179			}
1180				break;
1181			case 'e':
1182				gw.writeOpcode(Opcode.EQU);
1183				break;
1184			case 'n':
1185				gw.writeOpcode(Opcode.EQU);
1186				gw.writeOpcode(Opcode.NOT);
1187				break;
1188			case 'l':
1189				gw.writeOpcode(Opcode.LOE);
1190				break;
1191			case 'g':
1192				gw.writeOpcode(Opcode.GOE);
1193				break;
1194			case '<':
1195				gw.writeOpcode(Opcode.LT);
1196				break;
1197			case '^':
1198				gw.writeOpcode(Opcode.XOR);
1199				break;
1200			case '>':
1201				gw.writeOpcode(Opcode.GT);
1202				break;
1203			case '*':
1204				gw.writeOpcode(Opcode.MUL);
1205				break;
1206			case '/':
1207				gw.writeOpcode(Opcode.DIV);
1208				break;
1209			case 'y':
1210				gw.writeOpcode(Opcode.SHL);
1211				break;
1212			case 'x':
1213				gw.writeOpcode(Opcode.SHR);
1214				break;
1215			case '&':
1216				gw.writeOpcode(Opcode.AND);
1217				break;
1218			case '|':
1219				gw.writeOpcode(Opcode.OR);
1220				break;
1221			}
1222			break;
1223		case statement:
1224		case variable:
1225			/*
1226			 * A variable.
1227			 * 
1228			 * Reserves or resolves the symbol on the (initialized) data
1229			 * segment. It emits a load instant opcode to push the address onto
1230			 * the stack. In theory, not all symbols have to point to the
1231			 * initialized part of the data segment, but this allows for simpler
1232			 * code :-) And, it's for a ctf service only, anyway
1233			 */
1234			gw.writeOpcode(Opcode.LI);
1235			String[] varParts = ((this.valueType == EValueType.statement) ? this.statement
1236					: this.stringValue).split("[]\\[]");
1237			String varName = varParts[0];
1238			int addr = sr.globorloc(varName);
1239			if (addr < 0) {
1240				gw.writeShort((short) (-addr));
1241			} else {
1242				gw.writeShort((short) addr);
1243			}
1244			if (varParts.length > 1) {
1245				ADELAParseTree valOffset;
1246				try {
1247					valOffset = ADELAParseTree.Parse(varParts[1]);
1248				} catch (ADELASyntaxException e) {
1249					throw new ADELACompilerException(e);/* ugly */
1250				}
1251				valOffset.compile(gw, sr, logStream);
1252				gw.writeOpcode(Opcode.ADD);
1253			}
1254			if (this.valueType == EValueType.statement) {
1255				/*
1256				 * A statement. Do an LW here. The alternative is to do a SW
1257				 * later.
1258				 */
1259				if (addr < 0) {
1260					gw.writeOpcode(Opcode.LW);
1261				} else {
1262					// PUSH BP
1263					gw.writeOpcode(Opcode.FMOV);
1264					gw.writeOpcode(Opcode.ADD);
1265					gw.writeOpcode(Opcode.LWS);
1266				}
1267				gw.writeOpcode(Opcode.ROT);
1268				gw.writeOpcode(Opcode.POP);
1269			}
1270			break;
1271		case stringValue:
1272			byte[] bytes;
1273			try {
1274				bytes = (this.stringValue + "\0").getBytes("utf-8");
1275			} catch (UnsupportedEncodingException e) {
1276				throw new ADELACompilerException(e);
1277			}
1278			if (bytes.length > 65530) {
1279				throw new ADELACompilerException(
1280						"WTF!? Who needs strings of that length? No one will ever need string constants longer than 65530 bytes!");
1281			}
1282			int addrpos = sr.reserveBytes(null, bytes);
1283			logStream.println("String constant \"" + this.stringValue + "\" ("
1284					+ bytes.length + " bytes) at " + addrpos);
1285			gw.writeOpcode(Opcode.LI);
1286			gw.writeShort((short) addrpos);
1287			break;
1288		}
1289	}
1290
1291	/*
1292	 * (non-Javadoc)
1293	 * 
1294	 * @see java.lang.Object#toString()
1295	 */
1296	@Override
1297	public String toString() {
1298		switch (this.valueType) {
1299		case floatValue:
1300			return Double.valueOf(this.doubleValue).toString();
1301		case intValue:
1302			return Integer.valueOf(this.intValue).toString();
1303		case longValue:
1304			return Long.valueOf(this.longValue).toString();
1305		case functioncall:
1306			String parameters = "";
1307			for (int i = 0; i < this.functionParameters.length; i++) {
1308				if (parameters.length() != 0) {
1309					parameters = parameters + ", ";
1310				}
1311				parameters = parameters + this.functionParameters[i].toString();
1312			}
1313			return this.functionToCall + "(" + parameters + ")";
1314		case operation:
1315			if (this.functionToCall == null) {
1316				return "(" + this.leftTree.toString()
1317						+ Helpers.opToString(this.operator)
1318						+ this.rightTree.toString() + ")";
1319			}
1320			if (this.rightTree != null) {
1321				return "" + this.leftTree.toString()
1322						+ Helpers.opToString(this.operator) + this.stringValue
1323						+ this.rightTree.toString() + "";
1324			} else {
1325				String params = "(";
1326				for (int i = 0; i < this.functionParameters.length; i++) {
1327					if (params.length() > 1) {
1328						params = params + "; ";
1329					}
1330					params = params + this.functionParameters[i].toString();
1331				}
1332				params = params + ")";
1333				return "" + this.leftTree.toString()
1334						+ Helpers.opToString(this.operator)
1335						+ this.functionToCall + params + "";
1336			}
1337		case statement:
1338			return this.statement;
1339		case stringValue:
1340			return "\"" + this.stringValue + "\"";
1341		case variable:
1342			return this.stringValue + ":";
1343		}
1344		return null;
1345	}
1346
1347	/**
1348	 * Like toString, but tries to draw an ASCII-art tree ;-)
1349	 * 
1350	 * @return ascii art tree
1351	 */
1352	public String toStringNonFlat() {
1353		String leftPart = null, rightPart = null;
1354		String mePart;
1355		switch (this.valueType) {
1356		case floatValue:
1357			mePart = Double.valueOf(this.doubleValue).toString();
1358			break;
1359		case intValue:
1360			mePart = Integer.valueOf(this.intValue).toString();
1361			break;
1362		case longValue:
1363			mePart = Long.valueOf(this.longValue).toString();
1364			break;
1365		case functioncall:
1366			String parameters = "";
1367			for (int i = 0; i < this.functionParameters.length; i++) {
1368				if (parameters.length() != 0) {
1369					parameters = Helpers.joinTwo(parameters, ", ");
1370				}
1371				parameters = Helpers.joinTwo(parameters,
1372						this.functionParameters[i].toStringNonFlat());
1373			}
1374			mePart = Helpers.joinTwo(Helpers.joinTwo(this.functionToCall + "(",
1375					parameters), ")");
1376			break;
1377		case operation:
1378			if (this.functionToCall == null) {
1379				mePart = Helpers.opToString(this.operator);
1380				leftPart = "\n" + this.leftTree.toStringNonFlat();
1381				rightPart = "\n" + this.rightTree.toStringNonFlat();
1382				break;
1383			}
1384			if (this.rightTree != null) {
1385				leftPart = "\n" + this.leftTree.toStringNonFlat();
1386				mePart = this.operator + this.stringValue;
1387				rightPart = "\n" + this.rightTree.toStringNonFlat();
1388				break;
1389			} else {
1390				String params = "(";
1391				for (int i = 0; i < this.functionParameters.length; i++) {
1392					if (params.length() > 1) {
1393						params = Helpers.joinTwo(params, ", ");
1394					}
1395					params = Helpers.joinTwo(params, this.functionParameters[i]
1396							.toStringNonFlat());
1397				}
1398				leftPart = "\n" + this.leftTree.toStringNonFlat();
1399				mePart = ".";
1400				rightPart = Helpers.joinTwo(Helpers.joinTwo(
1401						this.functionToCall, params), ")");
1402				break;
1403			}
1404		case statement:
1405			mePart = this.statement;
1406			break;
1407		case stringValue:
1408			mePart = "\"" + this.stringValue + "\"";
1409			break;
1410		case variable:
1411			mePart = this.stringValue;
1412			break;
1413		default:
1414			mePart = "";
1415		}
1416
1417		return Helpers.makeTree(leftPart, mePart, rightPart);
1418	}
1419
1420	public boolean isReturnStatement() {
1421		return returnStatement;
1422	}
1423
1424	public void saveTree(DataOutput out) throws IOException {
1425		out.writeInt(this.valueType.ordinal());
1426		out.writeBoolean(this.returnStatement);
1427		switch (this.valueType) {
1428		case floatValue:
1429			out.writeDouble(this.doubleValue);
1430			break;
1431		case intValue:
1432			out.writeInt(this.intValue);
1433			break;
1434		case longValue:
1435			out.writeLong(this.longValue);
1436			break;
1437		case stringValue:
1438			Helpers.writeString(out, this.stringValue);
1439			break;
1440		case functioncall:
1441			Helpers.writeString(out, this.functionToCall);
1442			out.writeInt(functionParameters.length);
1443			for (int i = 0; i < functionParameters.length; i++) {
1444				functionParameters[i].saveTree(out);
1445			}
1446			break;
1447		case operation:
1448			leftTree.saveTree(out);
1449			out.writeChar(this.operator);
1450			if (this.operator == '.') {
1451				// method calls are special
1452				Helpers.writeString(out, this.functionToCall);
1453				out.writeInt(functionParameters.length);
1454				for (int i = 0; i < functionParameters.length; i++) {
1455					functionParameters[i].saveTree(out);
1456				}
1457			} else {
1458				rightTree.saveTree(out);
1459			}
1460			break;
1461		case statement:
1462			Helpers.writeString(out, this.statement);
1463			break;
1464		}
1465	}
1466
1467	public static ADELAParseTree loadTree(DataInput in) throws IOException {
1468		ADELAParseTree tree = new ADELAParseTree();
1469		loadTree(in, tree);
1470		return tree;
1471	}
1472
1473	private static void loadTree(DataInput in, ADELAParseTree tree)
1474			throws IOException {
1475		tree.valueType = EValueType.values()[in.readInt()];
1476		tree.returnStatement = in.readBoolean();
1477		switch (tree.valueType) {
1478		case floatValue:
1479			tree.doubleValue = in.readDouble();
1480			break;
1481		case intValue:
1482			tree.intValue = in.readInt();
1483			break;
1484		case longValue:
1485			tree.longValue = in.readLong();
1486			break;
1487		case stringValue:
1488			tree.stringValue = Helpers.readString(in);
1489			break;
1490		case functioncall:
1491			tree.functionToCall = Helpers.readString(in);
1492			tree.functionParameters = new ADELAParseTree[in.readInt()];
1493			for (int i = 0; i < tree.functionParameters.length; i++) {
1494				tree.functionParameters[i] = loadTree(in);
1495			}
1496			break;
1497		case operation:
1498			tree.leftTree = loadTree(in);
1499			tree.operator = in.readChar();
1500			if (tree.operator == '.') {
1501				// method calls are special
1502				tree.functionToCall = Helpers.readString(in);
1503				tree.functionParameters = new ADELAParseTree[in.readInt()];
1504				for (int i = 0; i < tree.functionParameters.length; i++) {
1505					tree.functionParameters[i] = loadTree(in);
1506				}
1507			} else {
1508				tree.rightTree = loadTree(in);
1509			}
1510			break;
1511
1512		case statement:
1513			tree.statement = Helpers.readString(in);
1514			break;
1515
1516		}
1517	}
1518
1519	private void handleFunction(String s) throws ADELASyntaxException {
1520		int iPos;
1521
1522		iPos = s.indexOf('(');
1523		/*
1524		 * function?
1525		 */
1526		if (iPos != -1) {
1527			/*
1528			 * yes, collect parameters, then jump into the callback interface
1529			 */
1530			this.functionToCall = s.substring(0, iPos);
1531			s = s.substring(iPos);
1532
1533			iPos = s.lastIndexOf(')');
1534			if (iPos == -1) {
1535				throw new ADELASyntaxException("Invalid function call: " + s);
1536			}
1537
1538			// sParams = s.substring(1, iPos).split(",");
1539			Vector<String> sParams = GetParams(s.substring(1, iPos));
1540			Iterator<String> iParams = sParams.iterator();
1541			this.functionParameters = new ADELAParseTree[sParams.size()];
1542
1543			for (int i = 0; i < this.functionParameters.length; i++) {
1544				String nextParameter = iParams.next();
1545				this.functionParameters[i] = Parse(nextParameter);
1546			}
1547		} else {
1548			this.functionParameters = new ADELAParseTree[0];
1549			this.functionToCall = "get" + s;
1550		}
1551	}
1552
1553	public void readExternal(ObjectInput in) throws IOException,
1554			ClassNotFoundException {
1555		loadTree(in, this);
1556	}
1557
1558	public void writeExternal(ObjectOutput out) throws IOException {
1559		saveTree(out);
1560	}
1561}