public function numberedArgs( $args, $context, $line ) {
		return WSData::newFromPHPVar( $context->mFrame->getNumberedArguments() );
	}
	/**
	 * The core interpreter method. Evaluates a single AST node.
	 * The $rec parameter must be increated by 1 each time the function is called
	 * recursively.
	 */
	public function evaluateNode( $node, $rec ) {
		if( !$node instanceof WSParserTreeNode ) {
			throw new WSException( 'evaluateNode() accepts only nonterminals' );
		}

		if( !$this->mInterpreter->checkRecursionLimit( $rec ) ) {
			throw new WSUserVisibleException( 'recoverflow', $this->mModuleName, $this->getLine( $node ) );
		}

		$c = $node->getChildren();
		switch( $node->getType() ) {
			case 'stmts':
				$stmts = array();
				while( isset( $c[1] ) ) {
					array_unshift( $stmts, $c[1] );
					$c = $c[0]->getChildren();
				}
				array_unshift( $stmts, $c[0] );
				foreach( $stmts as $stmt )
					$res = $this->evaluateNode( $stmt, $rec + 1 );
				return $res;
			case 'stmt':
				if( $c[0] instanceof WSToken ) {
					switch( $c[0]->type ) {
						case 'leftcurly':
							return $this->evaluateNode( $c[1], $rec + 1 );
						case 'if':
							$cond = $this->evaluateNode( $c[2], $rec + 1 );
							if( $cond->toBool() ) {
								return $this->evaluateNode( $c[4], $rec + 1 );
							} else {
								if( isset( $c[6] ) ) {
									return $this->evaluateNode( $c[6], $rec + 1 );
								} else {
									return new WSData();
								}
							}
						case 'for':
							$array = $this->evaluateNode( $c[4], $rec + 1 );
							if( !$array->isArray() )
								throw new WSUserVisibleException( 'invalidforeach', $this->mModuleName, $c[0]->line );
							$last = new WSData();
							$lvalues =  $c[2]->getChildren();

							foreach( $array->data as $key => $item ) {
								// <forlvalue> ::= <lvalue> | <lvalue> colon <lvalue>
								if( count( $lvalues ) > 1 ) {
									$this->setVar( $lvalues[0], WSData::newFromPHPVar( $key ), $rec );
									$this->setVar( $lvalues[2], $item, $rec );
								} else {
									$this->setVar( $lvalues[0], $item, $rec );
								}
								try {
									$last = $this->evaluateNode( $c[6], $rec + 1 );
								} catch( WSUserVisibleException $e ) {
									if( $e->getExceptionID() == 'break' )
										break;
									elseif( $e->getExceptionID() == 'continue' )
										continue;
									else
										throw $e;
								}
							}
							return $last;
						case 'try':
							try {
								return $this->evaluateNode( $c[1], $rec + 1 );
							} catch( WSUserVisibleException $e ) {
								if( $e instanceof WSControlException ) {
									throw $e;
								} else {
									$this->setVar( $c[4], new WSData( WSData::DString, $e->getExceptionID() ), $rec );
									return $this->evaluateNode( $c[6], $rec + 1 );
								}
							}
						default:
							throw new WSException( "Unknown keyword: {$c[0]->type}" );
					}
				} else {
					return $this->evaluateNode( $c[0], $rec + 1 );
				}
			case 'exprreturn':
				switch( $c[0]->value ) {
					case 'return':
						if( isset( $c[1] ) ) {
							$retval = $this->evaluateNode( $c[1], $rec + 1 );
							$empty = false;
						} else {
							$retval = new WSData();
							$empty = true;
						}
						throw new WSReturnException( $retval, $empty );
					case 'append':
						if( $this->mListOutput ) {
							throw new WSUserVisibleException( 'appendyield', $this->mModuleName, $c[0]->line );
						}

						$this->mOutput = WSData::sum(
							$this->mOutput,
							$this->evaluateNode( $c[1], $rec + 1 ),
							$this->mModuleName,
							$c[0]->line
						);
						break 2;
					case 'yield':
						if( $this->mOutput->type != WSData::DNull ) {
							throw new WSUserVisibleException( 'appendyield', $this->mModuleName, $c[0]->line );
						}

						$this->mListOutput[] = $this->evaluateNode( $c[1], $rec + 1 );
						break 2;
					default:
						throw new WSException( "Unknown return keyword: {$c[0]->value}" );
				}
			case 'exprset':
				$this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[1]->line );
				if( $c[1]->value == '=' ) {
					$new = $this->evaluateNode( $c[2], $rec + 1 );
					$this->setVar( $c[0], $new, $rec );
					return $new;
				} else {
					$old = $this->getVar( $c[0], $rec, false );
					$new = $this->evaluateNode( $c[2], $rec + 1 );
					$new = $this->getValueForSetting( $old, $new,
						$c[1]->value, $c[1]->line );
					$this->setVar( $c[0], $new, $rec );
					return $new;
				}
			case 'exprtrinary':
				$cond = $this->evaluateNode( $c[0], $rec + 1 );
				if( $cond->toBool() ) {
					return $this->evaluateNode( $c[2], $rec + 1 );
				} else {
					return $this->evaluateNode( $c[4], $rec + 1 );
				}
			case 'exprlogical':
				$this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[1]->line );
				$arg1 = $this->evaluateNode( $c[0], $rec + 1 );
				switch( $c[1]->value ) {
					case '&':
						if( !$arg1->toBool() )
							return new WSData( WSData::DBool, false );
						else
							return $this->evaluateNode( $c[2], $rec + 1 );
					case '|':
						if( $arg1->toBool() )
							return new WSData( WSData::DBool, true );
						else
							return $this->evaluateNode( $c[2], $rec + 1 );
					case '^':
						$arg2 = $this->evaluateNode( $c[2], $rec + 1 );
						return new WSData( WSData::DBool, $arg1->toBool() xor $arg2->toBool() );
					default:
						throw new WSException( "Invalid logical operation: {$c[1]->value}" );
				}
			case 'exprequals':
			case 'exprcompare':
				$this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[1]->line );
				$arg1 = $this->evaluateNode( $c[0], $rec + 1 );
				$arg2 = $this->evaluateNode( $c[2], $rec + 1 );
				return WSData::compareOp( $arg1, $arg2, $c[1]->value );
			case 'exprsum':
				$this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[1]->line );
				$arg1 = $this->evaluateNode( $c[0], $rec + 1 );
				$arg2 = $this->evaluateNode( $c[2], $rec + 1 );
				switch( $c[1]->value ) {
					case '+':
						return WSData::sum( $arg1, $arg2, $this->mModuleName, $c[1]->line );
					case '-':
						return WSData::sub( $arg1, $arg2 );
				}
			case 'exprmul':
				$this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[1]->line );
				$arg1 = $this->evaluateNode( $c[0], $rec + 1 );
				$arg2 = $this->evaluateNode( $c[2], $rec + 1 );
				return WSData::mulRel( $arg1, $arg2, $c[1]->value, $this->mModuleName, $c[1]->line );
			case 'exprpow':
				$this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[1]->line );
				$arg1 = $this->evaluateNode( $c[0], $rec + 1 );
				$arg2 = $this->evaluateNode( $c[2], $rec + 1 );
				return WSData::pow( $arg1, $arg2 );
			case 'exprkeyword':
				$this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[1]->line );
				$arg1 = $this->evaluateNode( $c[0], $rec + 1 );
				$arg2 = $this->evaluateNode( $c[2], $rec + 1 );
				switch( $c[1]->value ) {
					case 'in':
						return WSData::keywordIn( $arg1, $arg2 );
					case 'contains':
						return WSData::keywordIn( $arg2, $arg1 );
					default:
						throw new WSException( "Invalid keyword: {$c[1]->value}" );
				}
			case 'exprinvert':
				$this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[0]->line );
				$arg = $this->evaluateNode( $c[1], $rec + 1 );
				return WSData::boolInvert( $arg );
			case 'exprunary':
				$this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[0]->line );
				$arg = $this->evaluateNode( $c[1], $rec + 1 );
				if( $c[0]->value == '-' )
					return WSData::unaryMinus( $arg );
				else
					return $arg;
			case 'exprfunction':
				// <exprFunction> ::= <funcid> leftbracket <commaListPlain> rightbracket | <funcid> leftbracket rightbracket
				// <exprFunction> ::= <varfunc> leftbracket <lvalue> rightbracket | <exprAtom>
				// <varfunc> ::= isset | delete
				// <funcid> ::= id | <exprAtom> doublecolon id | self doublecolon id

				$this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[1]->line );
				if( $c[0]->getType() == 'funcid' ) {
					if( $c[2] instanceof WSParserTreeNode ) {
						$args = $this->parseArray( $c[2], $rec, $dummy );
					} else {
						$args = array();
					}

					$idch = $c[0]->getChildren();
					if( count( $idch ) == 1 ) {
						$funcname = $idch[0]->value;
						if( isset( self::$mBuiltInFunctions[$funcname] ) )  {
							$func = self::$mBuiltInFunctions[$funcname];
							return $this->$func( $args, $idch[0]->line );
						} else {
							return WSLibrary::callFunction( $funcname, $args, $this, $idch[0]->line );
						}
					} else {
						$funcname = $idch[2]->value;
						if( $idch[0] instanceof WSToken ) {
							// self::function()
							$module = $this->mModule;
						} else {
							// "ModuleName"::function()
							$module = $this->evaluateNode( $idch[0], $rec + 1 )->toString();
						}
						return $this->mInterpreter->invokeUserFunctionFromModule(
							$module, $funcname, $args, $this, $idch[1]->line );
					}
				} else {
					$type = $c[0]->mChildren[0]->value;
					switch( $type ) {
						case 'isset':
							$val = $this->getVar( $c[2], $rec, true );
							return new WSData( WSData::DBool, $val !== null );
						case 'delete':
							$this->deleteVar( $c[2], $rec );
							return new WSData();
						default:
							throw new WSException( "Unknown keyword: {$type}" );
					}
				}
			case 'expratom':
				if( $c[0] instanceof WSParserTreeNode ) {
					if( $c[0]->getType() == 'atom' ) {
						list( $val ) = $c[0]->getChildren();
						switch( $val->type ) {
							case 'string':
								return new WSData( WSData::DString, $val->value );
							case 'int':
								return new WSData( WSData::DInt, $val->value );
							case 'float':
								return new WSData( WSData::DFloat, $val->value );
							case 'true':
								return new WSData( WSData::DBool, true );
							case 'false':
								return new WSData( WSData::DBool, false );
							case 'null':
								return new WSData();
						}
					} else {
						return $this->getVar( $c[0], $rec );
					}
				} else {
					switch( $c[0]->type ) {
						case 'leftbracket':
							return $this->evaluateNode( $c[1], $rec + 1 );
						case 'leftsquare':
						case 'leftcurly':
							$arraytype = null;
							$array = $this->parseArray( $c[1], $rec + 1, $arraytype );
							return new WSData( $arraytype, $array );
						case 'break':
							throw new WSControlException( 'break', $this->mModuleName, $c[0]->line );
						case 'continue':
							throw new WSControlException( 'continue', $this->mModuleName, $c[0]->line );
					}
				}
			default:
				$type = $node->getType();
				throw new WSException( "Invalid node type passed to evaluateNode(): {$type}" );
		}
	}
	public function split( $args, $context, $line ) {
		$list = explode( $args[1]->toString(), $args[0]->toString() );
		return WSData::newFromPHPVar( $list );
	}