/**
  * Calls the function and returns the value
  * @param String $paramSpace Space name 
  * @param String $paramName function name
  * @param array $paramArgs args (optional)
  * @return OrongoVariable orongovariable(null) if it didnt return anything or any error occured else the OrongoVariable returned
  */
 public function execFunction($paramSpace, $paramName, $paramArgs = null)
 {
     if (!is_array($paramArgs)) {
         $args = array();
     } else {
         $args = $paramArgs;
     }
     //you should prevent this..
     if (!$this->isFunction($paramSpace, $paramName)) {
         return new OrongoVariable(null);
     }
     if (isset($this->functions[$paramSpace][$paramName])) {
         $func =& $this->functions[$paramSpace][$paramName];
         if ($func instanceof OrongoFunction == false) {
             return new OrongoVariable(null);
         }
         $return = $func($args);
         if ($return instanceof OrongoVariable == false && !is_array($return)) {
             return new OrongoVariable(null);
         }
         return $return;
     } else {
         $func =& $this->customFunctions[$paramSpace][$paramName];
         $argCount = empty($args[0]) ? 0 : count($args);
         if ($func['param_count'] > $argCount) {
             throw new OrongoScriptParseException("Invalid function call to " . $paramName . " (parameter count)", -10);
         }
         $p = new OrongoScriptParser($func['logic']);
         $arguments = array();
         if ($func['param_count'] > 0 && isset($func['params']) && is_array($func['params'])) {
             $i = 0;
             foreach ($func['params'] as $paramName) {
                 $arguments[$paramName] = $args[$i];
                 $i++;
             }
         }
         $return = $p->startParser(clone $this, null, $arguments);
         $this->variables = $p->getRuntime()->getVars();
         return new OrongoVariable($return);
     }
 }
 /**
  * Parses current line
  */
 private function parseLine()
 {
     if ($this->lineparsed) {
         return;
     }
     $line = trim($this->lines[$this->getCurrentLine()]);
     if (!empty($this->ifs)) {
         if ($this->lineStartsWith("if")) {
             $this->ifs[count($this->ifs) - 1]['ifs_passed']++;
         }
         if ($line == "end if" && $this->ifs[count($this->ifs) - 1]['ifs_passed'] != 0) {
             $this->ifs[count($this->ifs) - 1]['logic'] .= "end if;";
             $this->ifs[count($this->ifs) - 1]['ifs_passed']--;
             return;
         } else {
             if ($line == "end if" && $this->ifs[count($this->ifs) - 1]['ifs_passed'] == 0) {
             } else {
                 $this->ifs[count($this->ifs) - 1]['logic'] .= $line . ";";
                 return;
             }
         }
     }
     if (!empty($this->foreachs)) {
         if ($this->lineStartsWith("foreach")) {
             $this->foreachs[count($this->foreachs) - 1]['foreachs_passed']++;
         }
         if ($line == "end foreach" && $this->foreachs[count($this->foreachs) - 1]['foreachs_passed'] != 0) {
             $this->foreachs[count($this->foreachs) - 1]['logic'] .= "end foreach;";
             $this->foreachs[count($this->foreachs) - 1]['foreachs_passed']--;
             return;
         } else {
             if ($line == "end foreach" && $this->foreachs[count($this->foreachs) - 1]['foreachs_passed'] == 0) {
             } else {
                 $this->foreachs[count($this->foreachs) - 1]['logic'] .= $line . ";";
                 return;
             }
         }
     }
     if ($this->definingFunction != null && $line != "end function") {
         $this->definingFunction["logic"] .= $line . ";";
     } else {
         if ($this->runtime->getCurrentSpace() == null && !$this->lineStartsWith("import") && !$this->lineStartsWith("space") && !$this->lineStartsWith("use")) {
             throw new OrongoScriptParseException("Can not execute: " . $line . " while not in space!");
         } else {
             if ($this->lineStartsWith("space")) {
                 if ($this->runtime->getCurrentSpace() != null) {
                     throw new OrongoScriptParseException("Can't create a new space if you are still in a space!");
                 }
                 $spaceName = trim(preg_replace("/space/", "", $line, 1));
                 $this->runtime->registerSpace($spaceName);
                 $this->runtime->setCurrentSpace($spaceName);
             } else {
                 if ($line == "end space") {
                     if (!empty($this->ifs) || $this->definingFunction != null) {
                         throw new OrongoScriptParseException("Can't end the space if you are still in a function/if.");
                     }
                     if ($this->runtime->getCurrentSpace() == null) {
                         throw new OrongoScriptParseException("Can't end the space if you are not in a space!");
                     }
                     $this->runtime->setCurrentSpace(null);
                 } else {
                     if ($this->lineStartsWith("if")) {
                         if ($this->definingFunction == null && $this->onlyFunctionSpaces) {
                             throw new OrongoScriptParseException("Can't perform: " . $line . "  outside function when using the space as a function space.");
                         }
                         $f = preg_replace("/if/", "", $line, 1);
                         $f = trim($f);
                         if ($f[0] != "(" || $f[strlen($f) - 1] != ")") {
                             throw new OrongoScriptParseException("Invalid if statement");
                         }
                         $f[0] = "";
                         $f[strlen($f) - 1] = "";
                         $bool = $this->parseIf($f);
                         $this->ifs[count($this->ifs)] = array('logic' => '', 'execute' => $bool, 'ifs_passed' => 0, 'new' => true);
                     } else {
                         if ($line == "end if") {
                             if (empty($this->ifs)) {
                                 throw new OrongoScriptParseException("Can not exit an if you're not in an if.");
                             }
                             $currentIfNo = count($this->ifs) - 1;
                             $curIf = $this->ifs[$currentIfNo];
                             if ($curIf['execute']) {
                                 $p = new OrongoScriptParser($curIf['logic']);
                                 $p->startParser($this->runtime, null, null, true);
                             }
                             unset($this->ifs[$currentIfNo]);
                         } else {
                             if ($this->lineStartsWith("import")) {
                                 $imp = trim(preg_replace("/import/", "", $line, 1));
                                 $this->runtime->import($imp);
                             } else {
                                 if ($this->lineStartsWith("let")) {
                                     $line = trim(preg_replace("/let/", "", $line, 1));
                                     if (!stristr($line, "=")) {
                                         throw new OrongoScriptParseException("Invalid let at line " . $this->getCurrentLine(true));
                                     }
                                     $toLet = explode("=", $line, 2);
                                     foreach ($toLet as &$var) {
                                         $var = trim($var);
                                     }
                                     if (count($toLet) < 2) {
                                         throw new OrongoScriptParseException("Invalid let at line " . $this->getCurrentLine(true));
                                     }
                                     if (empty($toLet[0])) {
                                         throw new OrongoScriptParseException("Invalid let (empty variable name) at line " . $this->getCurrentLine(true));
                                     }
                                     if (empty($toLet[1])) {
                                         throw new OrongoScriptParseException("Invalid let (empty value) at line " . $this->getCurrentLine(true));
                                     }
                                     $toLet[0] = stristr($toLet[0], ":") ? $toLet[0] : $toLet[0] . ":__main__";
                                     $field = explode(":", strrev($toLet[0]), 2);
                                     $name = trim(strrev($field[1]));
                                     $field = trim(strrev($field[0]));
                                     $var = $this->parseVar($toLet[1]);
                                     $var = $var instanceof OrongoVariable ? $var : is_array($var) ? $var : new OrongoVariable(null);
                                     $this->runtime->letVar($name, $var, $field);
                                 } else {
                                     if ($this->lineStartsWith("do")) {
                                         if ($this->definingFunction == null && $this->onlyFunctionSpaces) {
                                             throw new OrongoScriptParseException("Can't perform: " . $line . "  outside function when using the space as a function space.");
                                         }
                                         $line[0] = "";
                                         $line[1] = "";
                                         $func = $this->parseFunction(trim($line));
                                         $this->runtime->execFunction($func['space'], $func['function_name'], $func['args']);
                                     } else {
                                         if ($this->lineStartsWith("function")) {
                                             $line = trim(preg_replace("/function/", "", $line, 1));
                                             if (substr_count($line, ")") != 1 || substr_count($line, "(") != 1 || strpos(")", $line) < strpos("(", $line)) {
                                                 throw new OrongoScriptParseException("Invalid function declaration.");
                                             }
                                             $l = explode("(", $line);
                                             if (empty($l[0])) {
                                                 throw new OrongoScriptParseException("Invalid function declaration: no function name!");
                                             }
                                             $funcName = trim($l[0]);
                                             $l[1] = trim(str_replace(")", "", $l[1]));
                                             $rawParams = str_replace(" ", "", $l[1]);
                                             $params = empty($rawParams) ? null : explode(",", $rawParams);
                                             $this->definingFunction = array('name' => $funcName, 'param_count' => $params == null ? 0 : count($params), 'logic' => '', 'params' => $params);
                                         } else {
                                             if ($line == "end function") {
                                                 if ($this->definingFunction == null) {
                                                     throw OrongoScriptParseException("Invalid statement end function, no function is being defined!");
                                                 }
                                                 $this->runtime->registerFunction($this->definingFunction);
                                                 $this->definingFunction = null;
                                             } else {
                                                 if ($this->lineStartsWith("use")) {
                                                     $path = trim(preg_replace("/use/", "", $line, 1));
                                                     $path = $this->parseVar($path)->get();
                                                     if (!file_exists($path)) {
                                                         throw new OrongoScriptParseException("Couldn't use:  " . $path . " (file doesn't exists)");
                                                     }
                                                     $p = new OrongoScriptParser(file_get_contents($path));
                                                     $p->startParser($this->getRuntime(), null, null, true, true);
                                                 } else {
                                                     if ($this->lineStartsWith("return")) {
                                                         if ($this->definingFunction == null && $this->onlyFunctionSpaces) {
                                                             throw new OrongoScriptParseException("Can't perform: " . $line . "  outside function when using the space as a function space.");
                                                         }
                                                         $line = trim(preg_replace("/return/", "", $line, 1));
                                                         return array('action' => 'terminated', 'value' => $this->parseVar($line)->get());
                                                     } else {
                                                         if ($line == "return") {
                                                             if ($this->definingFunction == null && $this->onlyFunctionSpaces) {
                                                                 throw new OrongoScriptParseException("Can't perform: " . $line . "  outside function when using the space as a function space.");
                                                             }
                                                             return array('action' => 'terminated', 'value' => 1);
                                                         } else {
                                                             if ($this->lineStartsWith("foreach")) {
                                                                 $line = trim(preg_replace("/foreach/", "", $line, 1));
                                                                 if (!stristr($line, ")") || !stristr($line, "(")) {
                                                                     throw new OrongoScriptParseException("Invalid foreach loop: " . $line);
                                                                 }
                                                                 if ($line[0] != "(" || $line[strlen($line) - 1] != ")") {
                                                                     throw new OrongoScriptParseException("Invalid foreach loop: " . $line);
                                                                 }
                                                                 $line[strlen($line) - 1] = "";
                                                                 $line[0] = "";
                                                                 if (!stristr($line, "as")) {
                                                                     throw new OrongoScriptParseException("Invalid foreach loop: " . $line);
                                                                 }
                                                                 $exp = explode("as", $line, 2);
                                                                 $var = trim($exp[0]);
                                                                 $list = false;
                                                                 if (stristr($var, ":")) {
                                                                     $list = true;
                                                                     $var = $this->parseVar($var)->get();
                                                                 }
                                                                 if ($list && $var instanceof OrongoList == false) {
                                                                     throw new OrongoScriptParseException("List expected in foreach loop: " . $line);
                                                                 }
                                                                 if (!$list) {
                                                                     $varTemp = $this->runtime->getRawVar($var);
                                                                     if ($varTemp['__main__'] instanceof OrongoList) {
                                                                         $var = $varTemp['__main__'];
                                                                         $list = true;
                                                                     } else {
                                                                         $var = $varTemp;
                                                                     }
                                                                 }
                                                                 $asVar = trim($exp[1]);
                                                                 $var = $list ? $var->getArray() : $var;
                                                                 $this->foreachs[end($this->foreachs)] = array('var' => $var, 'asVar' => $asVar, 'logic' => "", 'foreachs_passed' => 0, 'new' => true);
                                                             } else {
                                                                 if ($line == "end foreach") {
                                                                     if (empty($this->foreachs)) {
                                                                         throw new OrongoScriptParseException("Can not end a foreach loop when you're not in a loop.");
                                                                     }
                                                                     $currentForeach = end($this->foreachs);
                                                                     unset($this->foreachs[count($this->foreachs) - 1]);
                                                                     foreach ($currentForeach['var'] as $as) {
                                                                         $p = new OrongoScriptParser($currentForeach['logic']);
                                                                         $p->startParser($this->runtime, null, array($currentForeach['asVar'] => $as->get()), true);
                                                                         $this->runtime->setVars($p->getRuntime()->getVars());
                                                                     }
                                                                 } else {
                                                                     $done = false;
                                                                     foreach (self::$registeredLineHandlers as $statement => $pM) {
                                                                         if ($this->lineStartsWith($statement)) {
                                                                             call_user_method_array($pM[0], $pM[1], array($line, &$this));
                                                                             $done = true;
                                                                             break;
                                                                         }
                                                                     }
                                                                     if (!$done) {
                                                                         throw new OrongoScriptParseException("Invalid characters at line: " . $line);
                                                                     }
                                                                 }
                                                             }
                                                         }
                                                     }
                                                 }
                                             }
                                         }
                                     }
                                 }
                             }
                         }
                     }
                 }
             }
         }
     }
 }