src/eMacros/Parser.php
public static function parseExpression($form, &$offset) {
		static $parentheses = null;
		
		if (is_null($parentheses)) {
			$_parentheses = self::PARENTHESES;
			$parentheses = [];
			
			for ($i = 0, $len = strlen($_parentheses); $i < $len; $i += 2)
				$parentheses[$_parentheses[$i]] = $_parentheses[$i + 1];
			
			unset($_parentheses);
		}
		
		if (isset($form[0], $parentheses[$form[0]])) {
			$end = $parentheses[$form[0]];
			$values = [];
			$i = 1;
			$len = strlen($form);
			
			while ($i < $len && $form[$i] != $end) {
				if (strpos(self::WHITESPACES, $form[$i]) !== false) {
					++$i;
					continue;
				}
				
				try {
					$values[] = self::parseExpression(substr($form, $i), $_offset);
					$i += $_offset;
				}
 				catch (ParseException $e) {
 					throw new ParseException($form, $i + $e->offset);
 				}
			}
			
			if (isset($form[$i]) && $form[$i] == $end) {
				$offset = $i + 1;
				return new GenericList($values);
			}

			throw new ParseException($form, $i);
		}
		elseif (isset($form[0]) && $form[0] == self::COMMENT_PREFIX) {
			$offset = strlen(strtok($form, "\n"));
			return;
		}
		elseif (preg_match(self::REAL_PATTERN, $form, $matches)) {
			$offset = strlen($matches[0]);
			return new Literal((float) $matches[0]);
		}
		elseif (preg_match(self::INTEGER_PATTERN, $form, $matches)) {
			$offset = strlen($matches[0]);
			$sign = $matches[1] == '-' ? -1 : 1;
			$value = !empty($matches[3]) ? hexdec($matches[3]) : (!empty($matches[4]) ? octdec($matches[4]) : $matches[2]);
			return new Literal($sign * $value);
		}
		elseif (preg_match(self::STRING_PATTERN, $form, $matches)) {
			list($parsed) = $matches;
			$offset = strlen($parsed);
			return new Literal(preg_replace_callback(self::STRING_ESCAPE_PATTERN, [__CLASS__, 'unescapeString'], substr($parsed, 1, -1)));
		}
		elseif (preg_match(self::SYMBOL_PATTERN, $form, $matches)) {
			$symbol = $matches[0];
			$offset = strlen($matches[0]);
			
			//store validated symbols in order to reduce checks
			if (array_key_exists($symbol, self::$map))
				return self::$map[$symbol];
			
			return self::$map[$symbol] = new Symbol($symbol);
		}
		
		throw new ParseException($form, 0);
	}
public static function parseExpression($form, &$offset) {
		static $parentheses = null;
		
		if (is_null($parentheses)) {
			$_parentheses = self::PARENTHESES;
			$parentheses = [];
			
			for ($i = 0, $len = strlen($_parentheses); $i < $len; $i += 2)
				$parentheses[$_parentheses[$i]] = $_parentheses[$i + 1];
			
			unset($_parentheses);
		}
		
		if (isset($form[0], $parentheses[$form[0]])) {
			$end = $parentheses[$form[0]];
			$values = [];
			$i = 1;
			$len = strlen($form);
			
			while ($i < $len && $form[$i] != $end) {
				if (strpos(self::WHITESPACES, $form[$i]) !== false) {
					++$i;
					continue;
				}
				
				try {
					$values[] = self::parseExpression(substr($form, $i), $_offset);
					$i += $_offset;
				}
 				catch (ParseException $e) {
 					throw new ParseException($form, $i + $e->offset);
 				}
			}
			
			if (isset($form[$i]) && $form[$i] == $end) {
				$offset = $i + 1;
				return new GenericList($values);
			}

			throw new ParseException($form, $i);
		}
		elseif (isset($form[0]) && $form[0] == self::COMMENT_PREFIX) {
			$offset = strlen(strtok($form, "\n"));
			return;
		}
		elseif (preg_match(self::REAL_PATTERN, $form, $matches)) {
			$offset = strlen($matches[0]);
			return new Literal((float) $matches[0]);
		}
		elseif (preg_match(self::INTEGER_PATTERN, $form, $matches)) {
			$offset = strlen($matches[0]);
			$sign = $matches[1] == '-' ? -1 : 1;
			$value = !empty($matches[3]) ? hexdec($matches[3]) : (!empty($matches[4]) ? octdec($matches[4]) : $matches[2]);
			return new Literal($sign * $value);
		}
		elseif (preg_match(self::STRING_PATTERN, $form, $matches)) {
			list($parsed) = $matches;
			$offset = strlen($parsed);
			return new Literal(preg_replace_callback(self::STRING_ESCAPE_PATTERN, [__CLASS__, 'unescapeString'], substr($parsed, 1, -1)));
		}
		elseif (preg_match(self::SYMBOL_PATTERN, $form, $matches)) {
			$symbol = $matches[0];
			$offset = strlen($matches[0]);
			
			//store validated symbols in order to reduce checks
			if (array_key_exists($symbol, self::$map))
				return self::$map[$symbol];
			
			return self::$map[$symbol] = new Symbol($symbol);
		}
		
		throw new ParseException($form, 0);
	}
Parses an inner expression
@param string $form
@param int $offset
@throws ParseException
@return mixed