diff options
Diffstat (limited to '')
| -rw-r--r-- | lib/less.php/Parser.php | 2816 | 
1 files changed, 0 insertions, 2816 deletions
| diff --git a/lib/less.php/Parser.php b/lib/less.php/Parser.php deleted file mode 100644 index 2c9d879..0000000 --- a/lib/less.php/Parser.php +++ /dev/null @@ -1,2816 +0,0 @@ -<?php - -require_once( dirname(__FILE__).'/Cache.php'); - -/** - * Class for parsing and compiling less files into css - * - * @package Less - * @subpackage parser - * - */ -class Less_Parser{ - - -	/** -	 * Default parser options -	 */ -	public static $default_options = array( -		'compress'				=> false,			// option - whether to compress -		'strictUnits'			=> false,			// whether units need to evaluate correctly -		'strictMath'			=> false,			// whether math has to be within parenthesis -		'relativeUrls'			=> true,			// option - whether to adjust URL's to be relative -		'urlArgs'				=> '',				// whether to add args into url tokens -		'numPrecision'			=> 8, - -		'import_dirs'			=> array(), -		'import_callback'		=> null, -		'cache_dir'				=> null, -		'cache_method'			=> 'php', 			// false, 'serialize', 'php', 'var_export', 'callback'; -		'cache_callback_get'	=> null, -		'cache_callback_set'	=> null, - -		'sourceMap'				=> false,			// whether to output a source map -		'sourceMapBasepath'		=> null, -		'sourceMapWriteTo'		=> null, -		'sourceMapURL'			=> null, - -		'indentation' 			=> '  ', - -		'plugins'				=> array(), - -	); - -	public static $options = array(); - - -	private $input;					// Less input string -	private $input_len;				// input string length -	private $pos;					// current index in `input` -	private $saveStack = array();	// holds state for backtracking -	private $furthest; -	private $mb_internal_encoding = ''; // for remember exists value of mbstring.internal_encoding - -	/** -	 * @var Less_Environment -	 */ -	private $env; - -	protected $rules = array(); - -	private static $imports = array(); - -	public static $has_extends = false; - -	public static $next_id = 0; - -	/** -	 * Filename to contents of all parsed the files -	 * -	 * @var array -	 */ -	public static $contentsMap = array(); - - -	/** -	 * @param Less_Environment|array|null $env -	 */ -	public function __construct( $env = null ){ - -		// Top parser on an import tree must be sure there is one "env" -		// which will then be passed around by reference. -		if( $env instanceof Less_Environment ){ -			$this->env = $env; -		}else{ -			$this->SetOptions(Less_Parser::$default_options); -			$this->Reset( $env ); -		} - -		// mbstring.func_overload > 1 bugfix -		// The encoding value must be set for each source file, -		// therefore, to conserve resources and improve the speed of this design is taken here -		if (ini_get('mbstring.func_overload')) { -			$this->mb_internal_encoding = ini_get('mbstring.internal_encoding'); -			@ini_set('mbstring.internal_encoding', 'ascii'); -		} - -	} - - -	/** -	 * Reset the parser state completely -	 * -	 */ -	public function Reset( $options = null ){ -		$this->rules = array(); -		self::$imports = array(); -		self::$has_extends = false; -		self::$imports = array(); -		self::$contentsMap = array(); - -		$this->env = new Less_Environment($options); - -		//set new options -		if( is_array($options) ){ -			$this->SetOptions(Less_Parser::$default_options); -			$this->SetOptions($options); -		} - -		$this->env->Init(); -	} - -	/** -	 * Set one or more compiler options -	 *  options: import_dirs, cache_dir, cache_method -	 * -	 */ -	public function SetOptions( $options ){ -		foreach($options as $option => $value){ -			$this->SetOption($option,$value); -		} -	} - -	/** -	 * Set one compiler option -	 * -	 */ -	public function SetOption($option,$value){ - -		switch($option){ - -			case 'import_dirs': -				$this->SetImportDirs($value); -			return; - -			case 'cache_dir': -				if( is_string($value) ){ -					Less_Cache::SetCacheDir($value); -					Less_Cache::CheckCacheDir(); -				} -			return; -		} - -		Less_Parser::$options[$option] = $value; -	} - -	/** -	 * Registers a new custom function -	 * -	 * @param  string   $name     function name -	 * @param  callable $callback callback -	 */ -	public function registerFunction($name, $callback) { -		$this->env->functions[$name] = $callback; -	} - -	/** -	 * Removed an already registered function -	 * -	 * @param  string $name function name -	 */ -	public function unregisterFunction($name) { -		if( isset($this->env->functions[$name]) ) -			unset($this->env->functions[$name]); -	} - - -	/** -	 * Get the current css buffer -	 * -	 * @return string -	 */ -	public function getCss(){ - -		$precision = ini_get('precision'); -		@ini_set('precision',16); -		$locale = setlocale(LC_NUMERIC, 0); -		setlocale(LC_NUMERIC, "C"); - -		try { - -	 		$root = new Less_Tree_Ruleset(array(), $this->rules ); -			$root->root = true; -			$root->firstRoot = true; - - -			$this->PreVisitors($root); - -			self::$has_extends = false; -			$evaldRoot = $root->compile($this->env); - - - -			$this->PostVisitors($evaldRoot); - -			if( Less_Parser::$options['sourceMap'] ){ -				$generator = new Less_SourceMap_Generator($evaldRoot, Less_Parser::$contentsMap, Less_Parser::$options ); -				// will also save file -				// FIXME: should happen somewhere else? -				$css = $generator->generateCSS(); -			}else{ -				$css = $evaldRoot->toCSS(); -			} - -			if( Less_Parser::$options['compress'] ){ -				$css = preg_replace('/(^(\s)+)|((\s)+$)/', '', $css); -			} - -		} catch (Exception $exc) { -			// Intentional fall-through so we can reset environment -		} - -		//reset php settings -		@ini_set('precision',$precision); -		setlocale(LC_NUMERIC, $locale); - -		// If you previously defined $this->mb_internal_encoding -		// is required to return the encoding as it was before -		if ($this->mb_internal_encoding != '') { -			@ini_set("mbstring.internal_encoding", $this->mb_internal_encoding); -			$this->mb_internal_encoding = ''; -		} - -		// Rethrow exception after we handled resetting the environment -		if (!empty($exc)) { -			throw $exc; -		} - -		return $css; -	} - -	public function findValueOf($varName) -	{ -		foreach($this->rules as $rule){ -			if(isset($rule->variable) && ($rule->variable == true) && (str_replace("@","",$rule->name) == $varName)){ -				return $this->getVariableValue($rule); -			} -		} -		return null; -	} - -	/** -	 * -	 * this function gets the private rules variable and returns an array of the found variables -	 * it uses a helper method getVariableValue() that contains the logic ot fetch the value from the rule object -	 * -	 * @return array -	 */ -	public function getVariables() -	{ -		$variables = array(); - -		$not_variable_type = array( -			'Comment',   // this include less comments ( // ) and css comments (/* */) -			'Import',    // do not search variables in included files @import -			'Ruleset',   // selectors (.someclass, #someid, …) -			'Operation', // -		); - -		// @TODO run compilation if not runned yet -		foreach ($this->rules as $key => $rule) { -			if (in_array($rule->type, $not_variable_type)) { -				continue; -			} - -			// Note: it seems rule->type is always Rule when variable = true -			if ($rule->type == 'Rule' && $rule->variable) { -				$variables[$rule->name] = $this->getVariableValue($rule); -			} else { -				if ($rule->type == 'Comment') { -					$variables[] = $this->getVariableValue($rule); -				} -			} -		} -		return $variables; -	} - -	public function findVarByName($var_name) -	{ -		foreach($this->rules as $rule){ -			if(isset($rule->variable) && ($rule->variable == true)){ -				if($rule->name == $var_name){ -					return $this->getVariableValue($rule); -				} -			} -		} -		return null; -	} - -	/** -	 * -	 * This method gets the value of the less variable from the rules object. -	 * Since the objects vary here we add the logic for extracting the css/less value. -	 * -	 * @param $var -	 * -	 * @return bool|string -	 */ -	private function getVariableValue($var) -	{ -		if (!is_a($var, 'Less_Tree')) { -			throw new Exception('var is not a Less_Tree object'); -		} - -		switch ($var->type) { -			case 'Color': -				return $this->rgb2html($var->rgb); -			case 'Unit': -				return $var->value. $var->unit->numerator[0]; -			case 'Variable': -				return $this->findVarByName($var->name); -			case 'Keyword': -				return $var->value; -			case 'Rule': -				return $this->getVariableValue($var->value); -			case 'Value': -				$value = ''; -				foreach ($var->value as $sub_value) { -					$value .= $this->getVariableValue($sub_value).' '; -				} -				return $value; -			case 'Quoted': -				return $var->quote.$var->value.$var->quote; -			case 'Dimension': -				$value = $var->value; -				if ($var->unit && $var->unit->numerator) { -					$value .= $var->unit->numerator[0]; -				} -				return $value; -			case 'Expression': -				$value = ""; -				foreach($var->value as $item) { -					$value .= $this->getVariableValue($item)." "; -				} -				return $value; -			case 'Operation': -				throw new Exception('getVariables() require Less to be compiled. please use $parser->getCss() before calling getVariables()'); -			case 'Comment': -			case 'Import': -			case 'Ruleset': -			default: -				throw new Exception("type missing in switch/case getVariableValue for ".$var->type); -		} -		return false; -	} - -	private function rgb2html($r, $g=-1, $b=-1) -	{ -		if (is_array($r) && sizeof($r) == 3) -			list($r, $g, $b) = $r; - -		$r = intval($r); $g = intval($g); -		$b = intval($b); - -		$r = dechex($r<0?0:($r>255?255:$r)); -		$g = dechex($g<0?0:($g>255?255:$g)); -		$b = dechex($b<0?0:($b>255?255:$b)); - -		$color = (strlen($r) < 2?'0':'').$r; -		$color .= (strlen($g) < 2?'0':'').$g; -		$color .= (strlen($b) < 2?'0':'').$b; -		return '#'.$color; -	} - -	/** -	 * Run pre-compile visitors -	 * -	 */ -	private function PreVisitors($root){ - -		if( Less_Parser::$options['plugins'] ){ -			foreach(Less_Parser::$options['plugins'] as $plugin){ -				if( !empty($plugin->isPreEvalVisitor) ){ -					$plugin->run($root); -				} -			} -		} -	} - - -	/** -	 * Run post-compile visitors -	 * -	 */ -	private function PostVisitors($evaldRoot){ - -		$visitors = array(); -		$visitors[] = new Less_Visitor_joinSelector(); -		if( self::$has_extends ){ -			$visitors[] = new Less_Visitor_processExtends(); -		} -		$visitors[] = new Less_Visitor_toCSS(); - - -		if( Less_Parser::$options['plugins'] ){ -			foreach(Less_Parser::$options['plugins'] as $plugin){ -				if( property_exists($plugin,'isPreEvalVisitor') && $plugin->isPreEvalVisitor ){ -					continue; -				} - -				if( property_exists($plugin,'isPreVisitor') && $plugin->isPreVisitor ){ -					array_unshift( $visitors, $plugin); -				}else{ -					$visitors[] = $plugin; -				} -			} -		} - - -		for($i = 0; $i < count($visitors); $i++ ){ -			$visitors[$i]->run($evaldRoot); -		} - -	} - - -	/** -	 * Parse a Less string into css -	 * -	 * @param string $str The string to convert -	 * @param string $uri_root The url of the file -	 * @return Less_Tree_Ruleset|Less_Parser -	 */ -	public function parse( $str, $file_uri = null ){ - -		if( !$file_uri ){ -			$uri_root = ''; -			$filename = 'anonymous-file-'.Less_Parser::$next_id++.'.less'; -		}else{ -			$file_uri = self::WinPath($file_uri); -			$filename = $file_uri; -			$uri_root = dirname($file_uri); -		} - -		$previousFileInfo = $this->env->currentFileInfo; -		$uri_root = self::WinPath($uri_root); -		$this->SetFileInfo($filename, $uri_root); - -		$this->input = $str; -		$this->_parse(); - -		if( $previousFileInfo ){ -			$this->env->currentFileInfo = $previousFileInfo; -		} - -		return $this; -	} - - -	/** -	 * Parse a Less string from a given file -	 * -	 * @throws Less_Exception_Parser -	 * @param string $filename The file to parse -	 * @param string $uri_root The url of the file -	 * @param bool $returnRoot Indicates whether the return value should be a css string a root node -	 * @return Less_Tree_Ruleset|Less_Parser -	 */ -	public function parseFile( $filename, $uri_root = '', $returnRoot = false){ - -		if( !file_exists($filename) ){ -			$this->Error(sprintf('File `%s` not found.', $filename)); -		} - - -		// fix uri_root? -		// Instead of The mixture of file path for the first argument and directory path for the second argument has bee -		if( !$returnRoot && !empty($uri_root) && basename($uri_root) == basename($filename) ){ -			$uri_root = dirname($uri_root); -		} - - -		$previousFileInfo = $this->env->currentFileInfo; - - -		if( $filename ){ -			$filename = self::AbsPath($filename, true); -		} -		$uri_root = self::WinPath($uri_root); - -		$this->SetFileInfo($filename, $uri_root); - -		self::AddParsedFile($filename); - -		if( $returnRoot ){ -			$rules = $this->GetRules( $filename ); -			$return = new Less_Tree_Ruleset(array(), $rules ); -		}else{ -			$this->_parse( $filename ); -			$return = $this; -		} - -		if( $previousFileInfo ){ -			$this->env->currentFileInfo = $previousFileInfo; -		} - -		return $return; -	} - - -	/** -	 * Allows a user to set variables values -	 * @param array $vars -	 * @return Less_Parser -	 */ -	public function ModifyVars( $vars ){ - -		$this->input = Less_Parser::serializeVars( $vars ); -		$this->_parse(); - -		return $this; -	} - - -	/** -	 * @param string $filename -	 */ -	public function SetFileInfo( $filename, $uri_root = ''){ - -		$filename = Less_Environment::normalizePath($filename); -		$dirname = preg_replace('/[^\/\\\\]*$/','',$filename); - -		if( !empty($uri_root) ){ -			$uri_root = rtrim($uri_root,'/').'/'; -		} - -		$currentFileInfo = array(); - -		//entry info -		if( isset($this->env->currentFileInfo) ){ -			$currentFileInfo['entryPath'] = $this->env->currentFileInfo['entryPath']; -			$currentFileInfo['entryUri'] = $this->env->currentFileInfo['entryUri']; -			$currentFileInfo['rootpath'] = $this->env->currentFileInfo['rootpath']; - -		}else{ -			$currentFileInfo['entryPath'] = $dirname; -			$currentFileInfo['entryUri'] = $uri_root; -			$currentFileInfo['rootpath'] = $dirname; -		} - -		$currentFileInfo['currentDirectory'] = $dirname; -		$currentFileInfo['currentUri'] = $uri_root.basename($filename); -		$currentFileInfo['filename'] = $filename; -		$currentFileInfo['uri_root'] = $uri_root; - - -		//inherit reference -		if( isset($this->env->currentFileInfo['reference']) && $this->env->currentFileInfo['reference'] ){ -			$currentFileInfo['reference'] = true; -		} - -		$this->env->currentFileInfo = $currentFileInfo; -	} - - -	/** -	 * @deprecated 1.5.1.2 -	 * -	 */ -	public function SetCacheDir( $dir ){ - -		if( !file_exists($dir) ){ -			if( mkdir($dir) ){ -				return true; -			} -			throw new Less_Exception_Parser('Less.php cache directory couldn\'t be created: '.$dir); - -		}elseif( !is_dir($dir) ){ -			throw new Less_Exception_Parser('Less.php cache directory doesn\'t exist: '.$dir); - -		}elseif( !is_writable($dir) ){ -			throw new Less_Exception_Parser('Less.php cache directory isn\'t writable: '.$dir); - -		}else{ -			$dir = self::WinPath($dir); -			Less_Cache::$cache_dir = rtrim($dir,'/').'/'; -			return true; -		} -	} - - -	/** -	 * Set a list of directories or callbacks the parser should use for determining import paths -	 * -	 * @param array $dirs -	 */ -	public function SetImportDirs( $dirs ){ -		Less_Parser::$options['import_dirs'] = array(); - -		foreach($dirs as $path => $uri_root){ - -			$path = self::WinPath($path); -			if( !empty($path) ){ -				$path = rtrim($path,'/').'/'; -			} - -			if ( !is_callable($uri_root) ){ -				$uri_root = self::WinPath($uri_root); -				if( !empty($uri_root) ){ -					$uri_root = rtrim($uri_root,'/').'/'; -				} -			} - -			Less_Parser::$options['import_dirs'][$path] = $uri_root; -		} -	} - -	/** -	 * @param string $file_path -	 */ -	private function _parse( $file_path = null ){ -		$this->rules = array_merge($this->rules, $this->GetRules( $file_path )); -	} - - -	/** -	 * Return the results of parsePrimary for $file_path -	 * Use cache and save cached results if possible -	 * -	 * @param string|null $file_path -	 */ -	private function GetRules( $file_path ){ - -		$this->SetInput($file_path); - -		$cache_file = $this->CacheFile( $file_path ); -		if( $cache_file ){ -			if( Less_Parser::$options['cache_method'] == 'callback' ){ -				if( is_callable(Less_Parser::$options['cache_callback_get']) ){ -					$cache = call_user_func_array( -						Less_Parser::$options['cache_callback_get'], -						array($this, $file_path, $cache_file) -					); - -					if( $cache ){ -						$this->UnsetInput(); -						return $cache; -					} -				} - -			}elseif( file_exists($cache_file) ){ -				switch(Less_Parser::$options['cache_method']){ - -					// Using serialize -					// Faster but uses more memory -					case 'serialize': -						$cache = unserialize(file_get_contents($cache_file)); -						if( $cache ){ -							touch($cache_file); -							$this->UnsetInput(); -							return $cache; -						} -						break; - - -						// Using generated php code -					case 'var_export': -					case 'php': -						$this->UnsetInput(); -						return include($cache_file); -				} -			} -		} - -		$rules = $this->parsePrimary(); - -		if( $this->pos < $this->input_len ){ -			throw new Less_Exception_Chunk($this->input, null, $this->furthest, $this->env->currentFileInfo); -		} - -		$this->UnsetInput(); - - -		//save the cache -		if( $cache_file ){ -			if( Less_Parser::$options['cache_method'] == 'callback' ){ -				if( is_callable(Less_Parser::$options['cache_callback_set']) ){ -					call_user_func_array( -						Less_Parser::$options['cache_callback_set'], -						array($this, $file_path, $cache_file, $rules) -					); -				} - -			}else{ -				//msg('write cache file'); -				switch(Less_Parser::$options['cache_method']){ -					case 'serialize': -						file_put_contents( $cache_file, serialize($rules) ); -						break; -					case 'php': -						file_put_contents( $cache_file, '<?php return '.self::ArgString($rules).'; ?>' ); -						break; -					case 'var_export': -						//Requires __set_state() -						file_put_contents( $cache_file, '<?php return '.var_export($rules,true).'; ?>' ); -						break; -				} - -				Less_Cache::CleanCache(); -			} -		} - -		return $rules; -	} - - -	/** -	 * Set up the input buffer -	 * -	 */ -	public function SetInput( $file_path ){ - -		if( $file_path ){ -			$this->input = file_get_contents( $file_path ); -		} - -		$this->pos = $this->furthest = 0; - -		// Remove potential UTF Byte Order Mark -		$this->input = preg_replace('/\\G\xEF\xBB\xBF/', '', $this->input); -		$this->input_len = strlen($this->input); - - -		if( Less_Parser::$options['sourceMap'] && $this->env->currentFileInfo ){ -			$uri = $this->env->currentFileInfo['currentUri']; -			Less_Parser::$contentsMap[$uri] = $this->input; -		} - -	} - - -	/** -	 * Free up some memory -	 * -	 */ -	public function UnsetInput(){ -		unset($this->input, $this->pos, $this->input_len, $this->furthest); -		$this->saveStack = array(); -	} - - -	public function CacheFile( $file_path ){ - -		if( $file_path && $this->CacheEnabled() ){ - -			$env = get_object_vars($this->env); -			unset($env['frames']); - -			$parts = array(); -			$parts[] = $file_path; -			$parts[] = filesize( $file_path ); -			$parts[] = filemtime( $file_path ); -			$parts[] = $env; -			$parts[] = Less_Version::cache_version; -			$parts[] = Less_Parser::$options['cache_method']; -			return Less_Cache::$cache_dir . Less_Cache::$prefix . base_convert( sha1(json_encode($parts) ), 16, 36) . '.lesscache'; -		} -	} - - -	static function AddParsedFile($file){ -		self::$imports[] = $file; -	} - -	static function AllParsedFiles(){ -		return self::$imports; -	} - -	/** -	 * @param string $file -	 */ -	static function FileParsed($file){ -		return in_array($file,self::$imports); -	} - - -	function save() { -		$this->saveStack[] = $this->pos; -	} - -	private function restore() { -		$this->pos = array_pop($this->saveStack); -	} - -	private function forget(){ -		array_pop($this->saveStack); -	} - -	/** -	 * Determine if the character at the specified offset from the current position is a white space. -	 * -	 * @param int $offset -	 * -	 * @return bool -	 */ -	private function isWhitespace($offset = 0) { -		return strpos(" \t\n\r\v\f", $this->input[$this->pos + $offset]) !== false; -	} - -	/** -	 * Parse from a token, regexp or string, and move forward if match -	 * -	 * @param array $toks -	 * @return array -	 */ -	private function match($toks){ - -		// The match is confirmed, add the match length to `this::pos`, -		// and consume any extra white-space characters (' ' || '\n') -		// which come after that. The reason for this is that LeSS's -		// grammar is mostly white-space insensitive. -		// - -		foreach($toks as $tok){ - -			$char = $tok[0]; - -			if( $char === '/' ){ -				$match = $this->MatchReg($tok); - -				if( $match ){ -					return count($match) === 1 ? $match[0] : $match; -				} - -			}elseif( $char === '#' ){ -				$match = $this->MatchChar($tok[1]); - -			}else{ -				// Non-terminal, match using a function call -				$match = $this->$tok(); - -			} - -			if( $match ){ -				return $match; -			} -		} -	} - -	/** -	 * @param string[] $toks -	 * -	 * @return string -	 */ -	private function MatchFuncs($toks){ - -		if( $this->pos < $this->input_len ){ -			foreach($toks as $tok){ -				$match = $this->$tok(); -				if( $match ){ -					return $match; -				} -			} -		} - -	} - -	// Match a single character in the input, -	private function MatchChar($tok){ -		if( ($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok) ){ -			$this->skipWhitespace(1); -			return $tok; -		} -	} - -	// Match a regexp from the current start point -	private function MatchReg($tok){ - -		if( preg_match($tok, $this->input, $match, 0, $this->pos) ){ -			$this->skipWhitespace(strlen($match[0])); -			return $match; -		} -	} - - -	/** -	 * Same as match(), but don't change the state of the parser, -	 * just return the match. -	 * -	 * @param string $tok -	 * @return integer -	 */ -	public function PeekReg($tok){ -		return preg_match($tok, $this->input, $match, 0, $this->pos); -	} - -	/** -	 * @param string $tok -	 */ -	public function PeekChar($tok){ -		//return ($this->input[$this->pos] === $tok ); -		return ($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok ); -	} - - -	/** -	 * @param integer $length -	 */ -	public function skipWhitespace($length){ - -		$this->pos += $length; - -		for(; $this->pos < $this->input_len; $this->pos++ ){ -			$c = $this->input[$this->pos]; - -			if( ($c !== "\n") && ($c !== "\r") && ($c !== "\t") && ($c !== ' ') ){ -				break; -			} -		} -	} - - -	/** -	 * @param string $tok -	 * @param string|null $msg -	 */ -	public function expect($tok, $msg = NULL) { -		$result = $this->match( array($tok) ); -		if (!$result) { -			$this->Error( $msg	? "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'" : $msg ); -		} else { -			return $result; -		} -	} - -	/** -	 * @param string $tok -	 */ -	public function expectChar($tok, $msg = null ){ -		$result = $this->MatchChar($tok); -		if( !$result ){ -			$msg = $msg ? $msg : "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'"; -			$this->Error( $msg ); -		}else{ -			return $result; -		} -	} - -	// -	// Here in, the parsing rules/functions -	// -	// The basic structure of the syntax tree generated is as follows: -	// -	//   Ruleset ->  Rule -> Value -> Expression -> Entity -	// -	// Here's some LESS code: -	// -	//	.class { -	//	  color: #fff; -	//	  border: 1px solid #000; -	//	  width: @w + 4px; -	//	  > .child {...} -	//	} -	// -	// And here's what the parse tree might look like: -	// -	//	 Ruleset (Selector '.class', [ -	//		 Rule ("color",  Value ([Expression [Color #fff]])) -	//		 Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]])) -	//		 Rule ("width",  Value ([Expression [Operation "+" [Variable "@w"][Dimension 4px]]])) -	//		 Ruleset (Selector [Element '>', '.child'], [...]) -	//	 ]) -	// -	//  In general, most rules will try to parse a token with the `$()` function, and if the return -	//  value is truly, will return a new node, of the relevant type. Sometimes, we need to check -	//  first, before parsing, that's when we use `peek()`. -	// - -	// -	// The `primary` rule is the *entry* and *exit* point of the parser. -	// The rules here can appear at any level of the parse tree. -	// -	// The recursive nature of the grammar is an interplay between the `block` -	// rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule, -	// as represented by this simplified grammar: -	// -	//	 primary  →  (ruleset | rule)+ -	//	 ruleset  →  selector+ block -	//	 block	→  '{' primary '}' -	// -	// Only at one point is the primary rule not called from the -	// block rule: at the root level. -	// -	private function parsePrimary(){ -		$root = array(); - -		while( true ){ - -			if( $this->pos >= $this->input_len ){ -				break; -			} - -			$node = $this->parseExtend(true); -			if( $node ){ -				$root = array_merge($root,$node); -				continue; -			} - -			//$node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseDirective')); -			$node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseNameValue', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseRulesetCall', 'parseDirective')); - -			if( $node ){ -				$root[] = $node; -			}elseif( !$this->MatchReg('/\\G[\s\n;]+/') ){ -				break; -			} - -			if( $this->PeekChar('}') ){ -				break; -			} -		} - -		return $root; -	} - - - -	// We create a Comment node for CSS comments `/* */`, -	// but keep the LeSS comments `//` silent, by just skipping -	// over them. -	private function parseComment(){ - -		if( $this->input[$this->pos] !== '/' ){ -			return; -		} - -		if( $this->input[$this->pos+1] === '/' ){ -			$match = $this->MatchReg('/\\G\/\/.*/'); -			return $this->NewObj4('Less_Tree_Comment',array($match[0], true, $this->pos, $this->env->currentFileInfo)); -		} - -		//$comment = $this->MatchReg('/\\G\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/'); -		$comment = $this->MatchReg('/\\G\/\*(?s).*?\*+\/\n?/');//not the same as less.js to prevent fatal errors -		if( $comment ){ -			return $this->NewObj4('Less_Tree_Comment',array($comment[0], false, $this->pos, $this->env->currentFileInfo)); -		} -	} - -	private function parseComments(){ -		$comments = array(); - -		while( $this->pos < $this->input_len ){ -			$comment = $this->parseComment(); -			if( !$comment ){ -				break; -			} - -			$comments[] = $comment; -		} - -		return $comments; -	} - - - -	// -	// A string, which supports escaping " and ' -	// -	//	 "milky way" 'he\'s the one!' -	// -	private function parseEntitiesQuoted() { -		$j = $this->pos; -		$e = false; -		$index = $this->pos; - -		if( $this->input[$this->pos] === '~' ){ -			$j++; -			$e = true; // Escaped strings -		} - -		$char = $this->input[$j]; -		if( $char !== '"' && $char !== "'" ){ -			return; -		} - -		if ($e) { -			$this->MatchChar('~'); -		} - - -		$matched = $this->MatchQuoted($char, $j+1); -		if( $matched === false ){ -			return; -		} - -		$quoted = $char.$matched.$char; -		return $this->NewObj5('Less_Tree_Quoted',array($quoted, $matched, $e, $index, $this->env->currentFileInfo) ); -	} - - -	/** -	 * When PCRE JIT is enabled in php, regular expressions don't work for matching quoted strings -	 * -	 *	$regex	= '/\\G\'((?:[^\'\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)\'/'; -	 *	$regex	= '/\\G"((?:[^"\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)"/'; -	 * -	 */ -	private function MatchQuoted($quote_char, $i){ - -		$matched = ''; -		while( $i < $this->input_len ){ -			$c = $this->input[$i]; - -			//escaped character -			if( $c === '\\' ){ -				$matched .= $c . $this->input[$i+1]; -				$i += 2; -				continue; -			} - -			if( $c === $quote_char ){ -				$this->pos = $i+1; -				$this->skipWhitespace(0); -				return $matched; -			} - -			if( $c === "\r" || $c === "\n" ){ -				return false; -			} - -			$i++; -			$matched .= $c; -		} - -		return false; -	} - - -	// -	// A catch-all word, such as: -	// -	//	 black border-collapse -	// -	private function parseEntitiesKeyword(){ - -		//$k = $this->MatchReg('/\\G[_A-Za-z-][_A-Za-z0-9-]*/'); -		$k = $this->MatchReg('/\\G%|\\G[_A-Za-z-][_A-Za-z0-9-]*/'); -		if( $k ){ -			$k = $k[0]; -			$color = $this->fromKeyword($k); -			if( $color ){ -				return $color; -			} -			return $this->NewObj1('Less_Tree_Keyword',$k); -		} -	} - -	// duplicate of Less_Tree_Color::FromKeyword -	private function FromKeyword( $keyword ){ -		$keyword = strtolower($keyword); - -		if( Less_Colors::hasOwnProperty($keyword) ){ -			// detect named color -			return $this->NewObj1('Less_Tree_Color',substr(Less_Colors::color($keyword), 1)); -		} - -		if( $keyword === 'transparent' ){ -			return $this->NewObj3('Less_Tree_Color', array( array(0, 0, 0), 0, true)); -		} -	} - -	// -	// A function call -	// -	//	 rgb(255, 0, 255) -	// -	// We also try to catch IE's `alpha()`, but let the `alpha` parser -	// deal with the details. -	// -	// The arguments are parsed with the `entities.arguments` parser. -	// -	private function parseEntitiesCall(){ -		$index = $this->pos; - -		if( !preg_match('/\\G([\w-]+|%|progid:[\w\.]+)\(/', $this->input, $name,0,$this->pos) ){ -			return; -		} -		$name = $name[1]; -		$nameLC = strtolower($name); - -		if ($nameLC === 'url') { -			return null; -		} - -		$this->pos += strlen($name); - -		if( $nameLC === 'alpha' ){ -			$alpha_ret = $this->parseAlpha(); -			if( $alpha_ret ){ -				return $alpha_ret; -			} -		} - -		$this->MatchChar('('); // Parse the '(' and consume whitespace. - -		$args = $this->parseEntitiesArguments(); - -		if( !$this->MatchChar(')') ){ -			return; -		} - -		if ($name) { -			return $this->NewObj4('Less_Tree_Call',array($name, $args, $index, $this->env->currentFileInfo) ); -		} -	} - -	/** -	 * Parse a list of arguments -	 * -	 * @return array -	 */ -	private function parseEntitiesArguments(){ - -		$args = array(); -		while( true ){ -			$arg = $this->MatchFuncs( array('parseEntitiesAssignment','parseExpression') ); -			if( !$arg ){ -				break; -			} - -			$args[] = $arg; -			if( !$this->MatchChar(',') ){ -				break; -			} -		} -		return $args; -	} - -	private function parseEntitiesLiteral(){ -		return $this->MatchFuncs( array('parseEntitiesDimension','parseEntitiesColor','parseEntitiesQuoted','parseUnicodeDescriptor') ); -	} - -	// Assignments are argument entities for calls. -	// They are present in ie filter properties as shown below. -	// -	//	 filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) -	// -	private function parseEntitiesAssignment() { - -		$key = $this->MatchReg('/\\G\w+(?=\s?=)/'); -		if( !$key ){ -			return; -		} - -		if( !$this->MatchChar('=') ){ -			return; -		} - -		$value = $this->parseEntity(); -		if( $value ){ -			return $this->NewObj2('Less_Tree_Assignment',array($key[0], $value)); -		} -	} - -	// -	// Parse url() tokens -	// -	// We use a specific rule for urls, because they don't really behave like -	// standard function calls. The difference is that the argument doesn't have -	// to be enclosed within a string, so it can't be parsed as an Expression. -	// -	private function parseEntitiesUrl(){ - - -		if( $this->input[$this->pos] !== 'u' || !$this->matchReg('/\\Gurl\(/') ){ -			return; -		} - -		$value = $this->match( array('parseEntitiesQuoted','parseEntitiesVariable','/\\Gdata\:.*?[^\)]+/','/\\G(?:(?:\\\\[\(\)\'"])|[^\(\)\'"])+/') ); -		if( !$value ){ -			$value = ''; -		} - - -		$this->expectChar(')'); - - -		if( isset($value->value) || $value instanceof Less_Tree_Variable ){ -			return $this->NewObj2('Less_Tree_Url',array($value, $this->env->currentFileInfo)); -		} - -		return $this->NewObj2('Less_Tree_Url', array( $this->NewObj1('Less_Tree_Anonymous',$value), $this->env->currentFileInfo) ); -	} - - -	// -	// A Variable entity, such as `@fink`, in -	// -	//	 width: @fink + 2px -	// -	// We use a different parser for variable definitions, -	// see `parsers.variable`. -	// -	private function parseEntitiesVariable(){ -		$index = $this->pos; -		if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G@@?[\w-]+/'))) { -			return $this->NewObj3('Less_Tree_Variable', array( $name[0], $index, $this->env->currentFileInfo)); -		} -	} - - -	// A variable entity using the protective {} e.g. @{var} -	private function parseEntitiesVariableCurly() { -		$index = $this->pos; - -		if( $this->input_len > ($this->pos+1) && $this->input[$this->pos] === '@' && ($curly = $this->MatchReg('/\\G@\{([\w-]+)\}/')) ){ -			return $this->NewObj3('Less_Tree_Variable',array('@'.$curly[1], $index, $this->env->currentFileInfo)); -		} -	} - -	// -	// A Hexadecimal color -	// -	//	 #4F3C2F -	// -	// `rgb` and `hsl` colors are parsed through the `entities.call` parser. -	// -	private function parseEntitiesColor(){ -		if ($this->PeekChar('#') && ($rgb = $this->MatchReg('/\\G#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/'))) { -			return $this->NewObj1('Less_Tree_Color',$rgb[1]); -		} -	} - -	// -	// A Dimension, that is, a number and a unit -	// -	//	 0.5em 95% -	// -	private function parseEntitiesDimension(){ - -		$c = @ord($this->input[$this->pos]); - -		//Is the first char of the dimension 0-9, '.', '+' or '-' -		if (($c > 57 || $c < 43) || $c === 47 || $c == 44){ -			return; -		} - -		$value = $this->MatchReg('/\\G([+-]?\d*\.?\d+)(%|[a-z]+)?/'); -		if( $value ){ - -			if( isset($value[2]) ){ -				return $this->NewObj2('Less_Tree_Dimension', array($value[1],$value[2])); -			} -			return $this->NewObj1('Less_Tree_Dimension',$value[1]); -		} -	} - - -	// -	// A unicode descriptor, as is used in unicode-range -	// -	// U+0?? or U+00A1-00A9 -	// -	function parseUnicodeDescriptor() { -		$ud = $this->MatchReg('/\\G(U\+[0-9a-fA-F?]+)(\-[0-9a-fA-F?]+)?/'); -		if( $ud ){ -			return $this->NewObj1('Less_Tree_UnicodeDescriptor', $ud[0]); -		} -	} - - -	// -	// JavaScript code to be evaluated -	// -	//	 `window.location.href` -	// -	private function parseEntitiesJavascript(){ -		$e = false; -		$j = $this->pos; -		if( $this->input[$j] === '~' ){ -			$j++; -			$e = true; -		} -		if( $this->input[$j] !== '`' ){ -			return; -		} -		if( $e ){ -			$this->MatchChar('~'); -		} -		$str = $this->MatchReg('/\\G`([^`]*)`/'); -		if( $str ){ -			return $this->NewObj3('Less_Tree_Javascript', array($str[1], $this->pos, $e)); -		} -	} - - -	// -	// The variable part of a variable definition. Used in the `rule` parser -	// -	//	 @fink: -	// -	private function parseVariable(){ -		if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*:/'))) { -			return $name[1]; -		} -	} - - -	// -	// The variable part of a variable definition. Used in the `rule` parser -	// -	// @fink(); -	// -	private function parseRulesetCall(){ - -		if( $this->input[$this->pos] === '@' && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*\(\s*\)\s*;/')) ){ -			return $this->NewObj1('Less_Tree_RulesetCall', $name[1] ); -		} -	} - - -	// -	// extend syntax - used to extend selectors -	// -	function parseExtend($isRule = false){ - -		$index = $this->pos; -		$extendList = array(); - - -		if( !$this->MatchReg( $isRule ? '/\\G&:extend\(/' : '/\\G:extend\(/' ) ){ return; } - -		do{ -			$option = null; -			$elements = array(); -			while( true ){ -				$option = $this->MatchReg('/\\G(all)(?=\s*(\)|,))/'); -				if( $option ){ break; } -				$e = $this->parseElement(); -				if( !$e ){ break; } -				$elements[] = $e; -			} - -			if( $option ){ -				$option = $option[1]; -			} - -			$extendList[] = $this->NewObj3('Less_Tree_Extend', array( $this->NewObj1('Less_Tree_Selector',$elements), $option, $index )); - -		}while( $this->MatchChar(",") ); - -		$this->expect('/\\G\)/'); - -		if( $isRule ){ -			$this->expect('/\\G;/'); -		} - -		return $extendList; -	} - - -	// -	// A Mixin call, with an optional argument list -	// -	//	 #mixins > .square(#fff); -	//	 .rounded(4px, black); -	//	 .button; -	// -	// The `while` loop is there because mixins can be -	// namespaced, but we only support the child and descendant -	// selector for now. -	// -	private function parseMixinCall(){ - -		$char = $this->input[$this->pos]; -		if( $char !== '.' && $char !== '#' ){ -			return; -		} - -		$index = $this->pos; -		$this->save(); // stop us absorbing part of an invalid selector - -		$elements = $this->parseMixinCallElements(); - -		if( $elements ){ - -			if( $this->MatchChar('(') ){ -				$returned = $this->parseMixinArgs(true); -				$args = $returned['args']; -				$this->expectChar(')'); -			}else{ -				$args = array(); -			} - -			$important = $this->parseImportant(); - -			if( $this->parseEnd() ){ -				$this->forget(); -				return $this->NewObj5('Less_Tree_Mixin_Call', array( $elements, $args, $index, $this->env->currentFileInfo, $important)); -			} -		} - -		$this->restore(); -	} - - -	private function parseMixinCallElements(){ -		$elements = array(); -		$c = null; - -		while( true ){ -			$elemIndex = $this->pos; -			$e = $this->MatchReg('/\\G[#.](?:[\w-]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/'); -			if( !$e ){ -				break; -			} -			$elements[] = $this->NewObj4('Less_Tree_Element', array($c, $e[0], $elemIndex, $this->env->currentFileInfo)); -			$c = $this->MatchChar('>'); -		} - -		return $elements; -	} - - - -	/** -	 * @param boolean $isCall -	 */ -	private function parseMixinArgs( $isCall ){ -		$expressions = array(); -		$argsSemiColon = array(); -		$isSemiColonSeperated = null; -		$argsComma = array(); -		$expressionContainsNamed = null; -		$name = null; -		$returner = array('args'=>array(), 'variadic'=> false); - -		$this->save(); - -		while( true ){ -			if( $isCall ){ -				$arg = $this->MatchFuncs( array( 'parseDetachedRuleset','parseExpression' ) ); -			} else { -				$this->parseComments(); -				if( $this->input[ $this->pos ] === '.' && $this->MatchReg('/\\G\.{3}/') ){ -					$returner['variadic'] = true; -					if( $this->MatchChar(";") && !$isSemiColonSeperated ){ -						$isSemiColonSeperated = true; -					} - -					if( $isSemiColonSeperated ){ -						$argsSemiColon[] = array('variadic'=>true); -					}else{ -						$argsComma[] = array('variadic'=>true); -					} -					break; -				} -				$arg = $this->MatchFuncs( array('parseEntitiesVariable','parseEntitiesLiteral','parseEntitiesKeyword') ); -			} - -			if( !$arg ){ -				break; -			} - - -			$nameLoop = null; -			if( $arg instanceof Less_Tree_Expression ){ -				$arg->throwAwayComments(); -			} -			$value = $arg; -			$val = null; - -			if( $isCall ){ -				// Variable -				if( property_exists($arg,'value') && count($arg->value) == 1 ){ -					$val = $arg->value[0]; -				} -			} else { -				$val = $arg; -			} - - -			if( $val instanceof Less_Tree_Variable ){ - -				if( $this->MatchChar(':') ){ -					if( $expressions ){ -						if( $isSemiColonSeperated ){ -							$this->Error('Cannot mix ; and , as delimiter types'); -						} -						$expressionContainsNamed = true; -					} - -					// we do not support setting a ruleset as a default variable - it doesn't make sense -					// However if we do want to add it, there is nothing blocking it, just don't error -					// and remove isCall dependency below -					$value = null; -					if( $isCall ){ -						$value = $this->parseDetachedRuleset(); -					} -					if( !$value ){ -						$value = $this->parseExpression(); -					} - -					if( !$value ){ -						if( $isCall ){ -							$this->Error('could not understand value for named argument'); -						} else { -							$this->restore(); -							$returner['args'] = array(); -							return $returner; -						} -					} - -					$nameLoop = ($name = $val->name); -				}elseif( !$isCall && $this->MatchReg('/\\G\.{3}/') ){ -					$returner['variadic'] = true; -					if( $this->MatchChar(";") && !$isSemiColonSeperated ){ -						$isSemiColonSeperated = true; -					} -					if( $isSemiColonSeperated ){ -						$argsSemiColon[] = array('name'=> $arg->name, 'variadic' => true); -					}else{ -						$argsComma[] = array('name'=> $arg->name, 'variadic' => true); -					} -					break; -				}elseif( !$isCall ){ -					$name = $nameLoop = $val->name; -					$value = null; -				} -			} - -			if( $value ){ -				$expressions[] = $value; -			} - -			$argsComma[] = array('name'=>$nameLoop, 'value'=>$value ); - -			if( $this->MatchChar(',') ){ -				continue; -			} - -			if( $this->MatchChar(';') || $isSemiColonSeperated ){ - -				if( $expressionContainsNamed ){ -					$this->Error('Cannot mix ; and , as delimiter types'); -				} - -				$isSemiColonSeperated = true; - -				if( count($expressions) > 1 ){ -					$value = $this->NewObj1('Less_Tree_Value', $expressions); -				} -				$argsSemiColon[] = array('name'=>$name, 'value'=>$value ); - -				$name = null; -				$expressions = array(); -				$expressionContainsNamed = false; -			} -		} - -		$this->forget(); -		$returner['args'] = ($isSemiColonSeperated ? $argsSemiColon : $argsComma); -		return $returner; -	} - - - -	// -	// A Mixin definition, with a list of parameters -	// -	//	 .rounded (@radius: 2px, @color) { -	//		... -	//	 } -	// -	// Until we have a finer grained state-machine, we have to -	// do a look-ahead, to make sure we don't have a mixin call. -	// See the `rule` function for more information. -	// -	// We start by matching `.rounded (`, and then proceed on to -	// the argument list, which has optional default values. -	// We store the parameters in `params`, with a `value` key, -	// if there is a value, such as in the case of `@radius`. -	// -	// Once we've got our params list, and a closing `)`, we parse -	// the `{...}` block. -	// -	private function parseMixinDefinition(){ -		$cond = null; - -		$char = $this->input[$this->pos]; -		if( ($char !== '.' && $char !== '#') || ($char === '{' && $this->PeekReg('/\\G[^{]*\}/')) ){ -			return; -		} - -		$this->save(); - -		$match = $this->MatchReg('/\\G([#.](?:[\w-]|\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/'); -		if( $match ){ -			$name = $match[1]; - -			$argInfo = $this->parseMixinArgs( false ); -			$params = $argInfo['args']; -			$variadic = $argInfo['variadic']; - - -			// .mixincall("@{a}"); -			// looks a bit like a mixin definition.. -			// also -			// .mixincall(@a: {rule: set;}); -			// so we have to be nice and restore -			if( !$this->MatchChar(')') ){ -				$this->furthest = $this->pos; -				$this->restore(); -				return; -			} - - -			$this->parseComments(); - -			if ($this->MatchReg('/\\Gwhen/')) { // Guard -				$cond = $this->expect('parseConditions', 'Expected conditions'); -			} - -			$ruleset = $this->parseBlock(); - -			if( is_array($ruleset) ){ -				$this->forget(); -				return $this->NewObj5('Less_Tree_Mixin_Definition', array( $name, $params, $ruleset, $cond, $variadic)); -			} - -			$this->restore(); -		}else{ -			$this->forget(); -		} -	} - -	// -	// Entities are the smallest recognized token, -	// and can be found inside a rule's value. -	// -	private function parseEntity(){ - -		return $this->MatchFuncs( array('parseEntitiesLiteral','parseEntitiesVariable','parseEntitiesUrl','parseEntitiesCall','parseEntitiesKeyword','parseEntitiesJavascript','parseComment') ); -	} - -	// -	// A Rule terminator. Note that we use `peek()` to check for '}', -	// because the `block` rule will be expecting it, but we still need to make sure -	// it's there, if ';' was omitted. -	// -	private function parseEnd(){ -		return $this->MatchChar(';') || $this->PeekChar('}'); -	} - -	// -	// IE's alpha function -	// -	//	 alpha(opacity=88) -	// -	private function parseAlpha(){ - -		if ( ! $this->MatchReg('/\\G\(opacity=/i')) { -			return; -		} - -		$value = $this->MatchReg('/\\G[0-9]+/'); -		if( $value ){ -			$value = $value[0]; -		}else{ -			$value = $this->parseEntitiesVariable(); -			if( !$value ){ -				return; -			} -		} - -		$this->expectChar(')'); -		return $this->NewObj1('Less_Tree_Alpha',$value); -	} - - -	// -	// A Selector Element -	// -	//	 div -	//	 + h1 -	//	 #socks -	//	 input[type="text"] -	// -	// Elements are the building blocks for Selectors, -	// they are made out of a `Combinator` (see combinator rule), -	// and an element name, such as a tag a class, or `*`. -	// -	private function parseElement(){ -		$c = $this->parseCombinator(); -		$index = $this->pos; - -		$e = $this->match( array('/\\G(?:\d+\.\d+|\d+)%/', '/\\G(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/', -			'#*', '#&', 'parseAttribute', '/\\G\([^()@]+\)/', '/\\G[\.#](?=@)/', 'parseEntitiesVariableCurly') ); - -		if( is_null($e) ){ -			$this->save(); -			if( $this->MatchChar('(') ){ -				if( ($v = $this->parseSelector()) && $this->MatchChar(')') ){ -					$e = $this->NewObj1('Less_Tree_Paren',$v); -					$this->forget(); -				}else{ -					$this->restore(); -				} -			}else{ -				$this->forget(); -			} -		} - -		if( !is_null($e) ){ -			return $this->NewObj4('Less_Tree_Element',array( $c, $e, $index, $this->env->currentFileInfo)); -		} -	} - -	// -	// Combinators combine elements together, in a Selector. -	// -	// Because our parser isn't white-space sensitive, special care -	// has to be taken, when parsing the descendant combinator, ` `, -	// as it's an empty space. We have to check the previous character -	// in the input, to see if it's a ` ` character. -	// -	private function parseCombinator(){ -		if( $this->pos < $this->input_len ){ -			$c = $this->input[$this->pos]; -			if ($c === '>' || $c === '+' || $c === '~' || $c === '|' || $c === '^' ){ - -				$this->pos++; -				if( $this->input[$this->pos] === '^' ){ -					$c = '^^'; -					$this->pos++; -				} - -				$this->skipWhitespace(0); - -				return $c; -			} - -			if( $this->pos > 0 && $this->isWhitespace(-1) ){ -				return ' '; -			} -		} -	} - -	// -	// A CSS selector (see selector below) -	// with less extensions e.g. the ability to extend and guard -	// -	private function parseLessSelector(){ -		return $this->parseSelector(true); -	} - -	// -	// A CSS Selector -	// -	//	 .class > div + h1 -	//	 li a:hover -	// -	// Selectors are made out of one or more Elements, see above. -	// -	private function parseSelector( $isLess = false ){ -		$elements = array(); -		$extendList = array(); -		$condition = null; -		$when = false; -		$extend = false; -		$e = null; -		$c = null; -		$index = $this->pos; - -		while( ($isLess && ($extend = $this->parseExtend())) || ($isLess && ($when = $this->MatchReg('/\\Gwhen/') )) || ($e = $this->parseElement()) ){ -			if( $when ){ -				$condition = $this->expect('parseConditions', 'expected condition'); -			}elseif( $condition ){ -				//error("CSS guard can only be used at the end of selector"); -			}elseif( $extend ){ -				$extendList = array_merge($extendList,$extend); -			}else{ -				//if( count($extendList) ){ -				//error("Extend can only be used at the end of selector"); -				//} -				if( $this->pos < $this->input_len ){ -					$c = $this->input[ $this->pos ]; -				} -				$elements[] = $e; -				$e = null; -			} - -			if( $c === '{' || $c === '}' || $c === ';' || $c === ',' || $c === ')') { break; } -		} - -		if( $elements ){ -			return $this->NewObj5('Less_Tree_Selector',array($elements, $extendList, $condition, $index, $this->env->currentFileInfo)); -		} -		if( $extendList ) { -			$this->Error('Extend must be used to extend a selector, it cannot be used on its own'); -		} -	} - -	private function parseTag(){ -		return ( $tag = $this->MatchReg('/\\G[A-Za-z][A-Za-z-]*[0-9]?/') ) ? $tag : $this->MatchChar('*'); -	} - -	private function parseAttribute(){ - -		$val = null; - -		if( !$this->MatchChar('[') ){ -			return; -		} - -		$key = $this->parseEntitiesVariableCurly(); -		if( !$key ){ -			$key = $this->expect('/\\G(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\\\.)+/'); -		} - -		$op = $this->MatchReg('/\\G[|~*$^]?=/'); -		if( $op ){ -			$val = $this->match( array('parseEntitiesQuoted','/\\G[0-9]+%/','/\\G[\w-]+/','parseEntitiesVariableCurly') ); -		} - -		$this->expectChar(']'); - -		return $this->NewObj3('Less_Tree_Attribute',array( $key, $op[0], $val)); -	} - -	// -	// The `block` rule is used by `ruleset` and `mixin.definition`. -	// It's a wrapper around the `primary` rule, with added `{}`. -	// -	private function parseBlock(){ -		if( $this->MatchChar('{') ){ -			$content = $this->parsePrimary(); -			if( $this->MatchChar('}') ){ -				return $content; -			} -		} -	} - -	private function parseBlockRuleset(){ -		$block = $this->parseBlock(); - -		if( $block ){ -			$block = $this->NewObj2('Less_Tree_Ruleset',array( null, $block)); -		} - -		return $block; -	} - -	private function parseDetachedRuleset(){ -		$blockRuleset = $this->parseBlockRuleset(); -		if( $blockRuleset ){ -			return $this->NewObj1('Less_Tree_DetachedRuleset',$blockRuleset); -		} -	} - -	// -	// div, .class, body > p {...} -	// -	private function parseRuleset(){ -		$selectors = array(); - -		$this->save(); - -		while( true ){ -			$s = $this->parseLessSelector(); -			if( !$s ){ -				break; -			} -			$selectors[] = $s; -			$this->parseComments(); - -			if( $s->condition && count($selectors) > 1 ){ -				$this->Error('Guards are only currently allowed on a single selector.'); -			} - -			if( !$this->MatchChar(',') ){ -				break; -			} -			if( $s->condition ){ -				$this->Error('Guards are only currently allowed on a single selector.'); -			} -			$this->parseComments(); -		} - - -		if( $selectors ){ -			$rules = $this->parseBlock(); -			if( is_array($rules) ){ -				$this->forget(); -				return $this->NewObj2('Less_Tree_Ruleset',array( $selectors, $rules)); //Less_Environment::$strictImports -			} -		} - -		// Backtrack -		$this->furthest = $this->pos; -		$this->restore(); -	} - -	/** -	 * Custom less.php parse function for finding simple name-value css pairs -	 * ex: width:100px; -	 * -	 */ -	private function parseNameValue(){ - -		$index = $this->pos; -		$this->save(); - - -		//$match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*((?:\'")?[a-zA-Z0-9\-% \.,!]+?(?:\'")?)\s*([;}])/'); -		$match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*([\'"]?[#a-zA-Z0-9\-%\.,]+?[\'"]?) *(! *important)?\s*([;}])/'); -		if( $match ){ - -			if( $match[4] == '}' ){ -				$this->pos = $index + strlen($match[0])-1; -			} - -			if( $match[3] ){ -				$match[2] .= ' !important'; -			} - -			return $this->NewObj4('Less_Tree_NameValue',array( $match[1], $match[2], $index, $this->env->currentFileInfo)); -		} - -		$this->restore(); -	} - - -	private function parseRule( $tryAnonymous = null ){ - -		$merge = false; -		$startOfRule = $this->pos; - -		$c = $this->input[$this->pos]; -		if( $c === '.' || $c === '#' || $c === '&' ){ -			return; -		} - -		$this->save(); -		$name = $this->MatchFuncs( array('parseVariable','parseRuleProperty')); - -		if( $name ){ - -			$isVariable = is_string($name); - -			$value = null; -			if( $isVariable ){ -				$value = $this->parseDetachedRuleset(); -			} - -			$important = null; -			if( !$value ){ - -				// prefer to try to parse first if its a variable or we are compressing -				// but always fallback on the other one -				//if( !$tryAnonymous && is_string($name) && $name[0] === '@' ){ -				if( !$tryAnonymous && (Less_Parser::$options['compress'] || $isVariable) ){ -					$value = $this->MatchFuncs( array('parseValue','parseAnonymousValue')); -				}else{ -					$value = $this->MatchFuncs( array('parseAnonymousValue','parseValue')); -				} - -				$important = $this->parseImportant(); - -				// a name returned by this.ruleProperty() is always an array of the form: -				// [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"] -				// where each item is a tree.Keyword or tree.Variable -				if( !$isVariable && is_array($name) ){ -					$nm = array_pop($name); -					if( $nm->value ){ -						$merge = $nm->value; -					} -				} -			} - - -			if( $value && $this->parseEnd() ){ -				$this->forget(); -				return $this->NewObj6('Less_Tree_Rule',array( $name, $value, $important, $merge, $startOfRule, $this->env->currentFileInfo)); -			}else{ -				$this->furthest = $this->pos; -				$this->restore(); -				if( $value && !$tryAnonymous ){ -					return $this->parseRule(true); -				} -			} -		}else{ -			$this->forget(); -		} -	} - -	function parseAnonymousValue(){ - -		if( preg_match('/\\G([^@+\/\'"*`(;{}-]*);/',$this->input, $match, 0, $this->pos) ){ -			$this->pos += strlen($match[1]); -			return $this->NewObj1('Less_Tree_Anonymous',$match[1]); -		} -	} - -	// -	// An @import directive -	// -	//	 @import "lib"; -	// -	// Depending on our environment, importing is done differently: -	// In the browser, it's an XHR request, in Node, it would be a -	// file-system operation. The function used for importing is -	// stored in `import`, which we pass to the Import constructor. -	// -	private function parseImport(){ - -		$this->save(); - -		$dir = $this->MatchReg('/\\G@import?\s+/'); - -		if( $dir ){ -			$options = $this->parseImportOptions(); -			$path = $this->MatchFuncs( array('parseEntitiesQuoted','parseEntitiesUrl')); - -			if( $path ){ -				$features = $this->parseMediaFeatures(); -				if( $this->MatchChar(';') ){ -					if( $features ){ -						$features = $this->NewObj1('Less_Tree_Value',$features); -					} - -					$this->forget(); -					return $this->NewObj5('Less_Tree_Import',array( $path, $features, $options, $this->pos, $this->env->currentFileInfo)); -				} -			} -		} - -		$this->restore(); -	} - -	private function parseImportOptions(){ - -		$options = array(); - -		// list of options, surrounded by parens -		if( !$this->MatchChar('(') ){ -			return $options; -		} -		do{ -			$optionName = $this->parseImportOption(); -			if( $optionName ){ -				$value = true; -				switch( $optionName ){ -					case "css": -						$optionName = "less"; -						$value = false; -						break; -					case "once": -						$optionName = "multiple"; -						$value = false; -						break; -				} -				$options[$optionName] = $value; -				if( !$this->MatchChar(',') ){ break; } -			} -		}while( $optionName ); -		$this->expectChar(')'); -		return $options; -	} - -	private function parseImportOption(){ -		$opt = $this->MatchReg('/\\G(less|css|multiple|once|inline|reference|optional)/'); -		if( $opt ){ -			return $opt[1]; -		} -	} - -	private function parseMediaFeature() { -		$nodes = array(); - -		do{ -			$e = $this->MatchFuncs(array('parseEntitiesKeyword','parseEntitiesVariable')); -			if( $e ){ -				$nodes[] = $e; -			} elseif ($this->MatchChar('(')) { -				$p = $this->parseProperty(); -				$e = $this->parseValue(); -				if ($this->MatchChar(')')) { -					if ($p && $e) { -						$r = $this->NewObj7('Less_Tree_Rule', array( $p, $e, null, null, $this->pos, $this->env->currentFileInfo, true)); -						$nodes[] = $this->NewObj1('Less_Tree_Paren',$r); -					} elseif ($e) { -						$nodes[] = $this->NewObj1('Less_Tree_Paren',$e); -					} else { -						return null; -					} -				} else -					return null; -			} -		} while ($e); - -		if ($nodes) { -			return $this->NewObj1('Less_Tree_Expression',$nodes); -		} -	} - -	private function parseMediaFeatures() { -		$features = array(); - -		do{ -			$e = $this->parseMediaFeature(); -			if( $e ){ -				$features[] = $e; -				if (!$this->MatchChar(',')) break; -			}else{ -				$e = $this->parseEntitiesVariable(); -				if( $e ){ -					$features[] = $e; -					if (!$this->MatchChar(',')) break; -				} -			} -		} while ($e); - -		return $features ? $features : null; -	} - -	private function parseMedia() { -		if( $this->MatchReg('/\\G@media/') ){ -			$features = $this->parseMediaFeatures(); -			$rules = $this->parseBlock(); - -			if( is_array($rules) ){ -				return $this->NewObj4('Less_Tree_Media',array( $rules, $features, $this->pos, $this->env->currentFileInfo)); -			} -		} -	} - - -	// -	// A CSS Directive -	// -	// @charset "utf-8"; -	// -	private function parseDirective(){ - -		if( !$this->PeekChar('@') ){ -			return; -		} - -		$rules = null; -		$index = $this->pos; -		$hasBlock = true; -		$hasIdentifier = false; -		$hasExpression = false; -		$hasUnknown = false; - - -		$value = $this->MatchFuncs(array('parseImport','parseMedia')); -		if( $value ){ -			return $value; -		} - -		$this->save(); - -		$name = $this->MatchReg('/\\G@[a-z-]+/'); - -		if( !$name ) return; -		$name = $name[0]; - - -		$nonVendorSpecificName = $name; -		$pos = strpos($name,'-', 2); -		if( $name[1] == '-' && $pos > 0 ){ -			$nonVendorSpecificName = "@" . substr($name, $pos + 1); -		} - - -		switch( $nonVendorSpecificName ){ -			/* -			case "@font-face": -			case "@viewport": -			case "@top-left": -			case "@top-left-corner": -			case "@top-center": -			case "@top-right": -			case "@top-right-corner": -			case "@bottom-left": -			case "@bottom-left-corner": -			case "@bottom-center": -			case "@bottom-right": -			case "@bottom-right-corner": -			case "@left-top": -			case "@left-middle": -			case "@left-bottom": -			case "@right-top": -			case "@right-middle": -			case "@right-bottom": -			hasBlock = true; -			break; -			*/ -			case "@charset": -				$hasIdentifier = true; -				$hasBlock = false; -				break; -			case "@namespace": -				$hasExpression = true; -				$hasBlock = false; -				break; -			case "@keyframes": -				$hasIdentifier = true; -				break; -			case "@host": -			case "@page": -			case "@document": -			case "@supports": -				$hasUnknown = true; -				break; -		} - -		if( $hasIdentifier ){ -			$value = $this->parseEntity(); -			if( !$value ){ -				$this->error("expected " . $name . " identifier"); -			} -		} else if( $hasExpression ){ -			$value = $this->parseExpression(); -			if( !$value ){ -				$this->error("expected " . $name. " expression"); -			} -		} else if ($hasUnknown) { - -			$value = $this->MatchReg('/\\G[^{;]+/'); -			if( $value ){ -				$value = $this->NewObj1('Less_Tree_Anonymous',trim($value[0])); -			} -		} - -		if( $hasBlock ){ -			$rules = $this->parseBlockRuleset(); -		} - -		if( $rules || (!$hasBlock && $value && $this->MatchChar(';'))) { -			$this->forget(); -			return $this->NewObj5('Less_Tree_Directive',array($name, $value, $rules, $index, $this->env->currentFileInfo)); -		} - -		$this->restore(); -	} - - -	// -	// A Value is a comma-delimited list of Expressions -	// -	//	 font-family: Baskerville, Georgia, serif; -	// -	// In a Rule, a Value represents everything after the `:`, -	// and before the `;`. -	// -	private function parseValue(){ -		$expressions = array(); - -		do{ -			$e = $this->parseExpression(); -			if( $e ){ -				$expressions[] = $e; -				if (! $this->MatchChar(',')) { -					break; -				} -			} -		}while($e); - -		if( $expressions ){ -			return $this->NewObj1('Less_Tree_Value',$expressions); -		} -	} - -	private function parseImportant (){ -		if( $this->PeekChar('!') && $this->MatchReg('/\\G! *important/') ){ -			return ' !important'; -		} -	} - -	private function parseSub (){ - -		if( $this->MatchChar('(') ){ -			$a = $this->parseAddition(); -			if( $a ){ -				$this->expectChar(')'); -				return $this->NewObj2('Less_Tree_Expression',array( array($a), true) ); //instead of $e->parens = true so the value is cached -			} -		} -	} - - -	/** -	 * Parses multiplication operation -	 * -	 * @return Less_Tree_Operation|null -	 */ -	function parseMultiplication(){ - -		$return = $m = $this->parseOperand(); -		if( $return ){ -			while( true ){ - -				$isSpaced = $this->isWhitespace( -1 ); - -				if( $this->PeekReg('/\\G\/[*\/]/') ){ -					break; -				} - -				$op = $this->MatchChar('/'); -				if( !$op ){ -					$op = $this->MatchChar('*'); -					if( !$op ){ -						break; -					} -				} - -				$a = $this->parseOperand(); - -				if(!$a) { break; } - -				$m->parensInOp = true; -				$a->parensInOp = true; -				$return = $this->NewObj3('Less_Tree_Operation',array( $op, array( $return, $a ), $isSpaced) ); -			} -		} -		return $return; - -	} - - -	/** -	 * Parses an addition operation -	 * -	 * @return Less_Tree_Operation|null -	 */ -	private function parseAddition (){ - -		$return = $m = $this->parseMultiplication(); -		if( $return ){ -			while( true ){ - -				$isSpaced = $this->isWhitespace( -1 ); - -				$op = $this->MatchReg('/\\G[-+]\s+/'); -				if( $op ){ -					$op = $op[0]; -				}else{ -					if( !$isSpaced ){ -						$op = $this->match(array('#+','#-')); -					} -					if( !$op ){ -						break; -					} -				} - -				$a = $this->parseMultiplication(); -				if( !$a ){ -					break; -				} - -				$m->parensInOp = true; -				$a->parensInOp = true; -				$return = $this->NewObj3('Less_Tree_Operation',array($op, array($return, $a), $isSpaced)); -			} -		} - -		return $return; -	} - - -	/** -	 * Parses the conditions -	 * -	 * @return Less_Tree_Condition|null -	 */ -	private function parseConditions() { -		$index = $this->pos; -		$return = $a = $this->parseCondition(); -		if( $a ){ -			while( true ){ -				if( !$this->PeekReg('/\\G,\s*(not\s*)?\(/') ||  !$this->MatchChar(',') ){ -					break; -				} -				$b = $this->parseCondition(); -				if( !$b ){ -					break; -				} - -				$return = $this->NewObj4('Less_Tree_Condition',array('or', $return, $b, $index)); -			} -			return $return; -		} -	} - -	private function parseCondition() { -		$index = $this->pos; -		$negate = false; -		$c = null; - -		if ($this->MatchReg('/\\Gnot/')) $negate = true; -		$this->expectChar('('); -		$a = $this->MatchFuncs(array('parseAddition','parseEntitiesKeyword','parseEntitiesQuoted')); - -		if( $a ){ -			$op = $this->MatchReg('/\\G(?:>=|<=|=<|[<=>])/'); -			if( $op ){ -				$b = $this->MatchFuncs(array('parseAddition','parseEntitiesKeyword','parseEntitiesQuoted')); -				if( $b ){ -					$c = $this->NewObj5('Less_Tree_Condition',array($op[0], $a, $b, $index, $negate)); -				} else { -					$this->Error('Unexpected expression'); -				} -			} else { -				$k = $this->NewObj1('Less_Tree_Keyword','true'); -				$c = $this->NewObj5('Less_Tree_Condition',array('=', $a, $k, $index, $negate)); -			} -			$this->expectChar(')'); -			return $this->MatchReg('/\\Gand/') ? $this->NewObj3('Less_Tree_Condition',array('and', $c, $this->parseCondition())) : $c; -		} -	} - -	/** -	 * An operand is anything that can be part of an operation, -	 * such as a Color, or a Variable -	 * -	 */ -	private function parseOperand (){ - -		$negate = false; -		$offset = $this->pos+1; -		if( $offset >= $this->input_len ){ -			return; -		} -		$char = $this->input[$offset]; -		if( $char === '@' || $char === '(' ){ -			$negate = $this->MatchChar('-'); -		} - -		$o = $this->MatchFuncs(array('parseSub','parseEntitiesDimension','parseEntitiesColor','parseEntitiesVariable','parseEntitiesCall')); - -		if( $negate ){ -			$o->parensInOp = true; -			$o = $this->NewObj1('Less_Tree_Negative',$o); -		} - -		return $o; -	} - - -	/** -	 * Expressions either represent mathematical operations, -	 * or white-space delimited Entities. -	 * -	 *	 1px solid black -	 *	 @var * 2 -	 * -	 * @return Less_Tree_Expression|null -	 */ -	private function parseExpression (){ -		$entities = array(); - -		do{ -			$e = $this->MatchFuncs(array('parseAddition','parseEntity')); -			if( $e ){ -				$entities[] = $e; -				// operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here -				if( !$this->PeekReg('/\\G\/[\/*]/') ){ -					$delim = $this->MatchChar('/'); -					if( $delim ){ -						$entities[] = $this->NewObj1('Less_Tree_Anonymous',$delim); -					} -				} -			} -		}while($e); - -		if( $entities ){ -			return $this->NewObj1('Less_Tree_Expression',$entities); -		} -	} - - -	/** -	 * Parse a property -	 * eg: 'min-width', 'orientation', etc -	 * -	 * @return string -	 */ -	private function parseProperty (){ -		$name = $this->MatchReg('/\\G(\*?-?[_a-zA-Z0-9-]+)\s*:/'); -		if( $name ){ -			return $name[1]; -		} -	} - - -	/** -	 * Parse a rule property -	 * eg: 'color', 'width', 'height', etc -	 * -	 * @return string -	 */ -	private function parseRuleProperty(){ -		$offset = $this->pos; -		$name = array(); -		$index = array(); -		$length = 0; - - -		$this->rulePropertyMatch('/\\G(\*?)/', $offset, $length, $index, $name ); -		while( $this->rulePropertyMatch('/\\G((?:[\w-]+)|(?:@\{[\w-]+\}))/', $offset, $length, $index, $name )); // ! - -		if( (count($name) > 1) && $this->rulePropertyMatch('/\\G\s*((?:\+_|\+)?)\s*:/', $offset, $length, $index, $name) ){ -			// at last, we have the complete match now. move forward, -			// convert name particles to tree objects and return: -			$this->skipWhitespace($length); - -			if( $name[0] === '' ){ -				array_shift($name); -				array_shift($index); -			} -			foreach($name as $k => $s ){ -				if( !$s || $s[0] !== '@' ){ -					$name[$k] = $this->NewObj1('Less_Tree_Keyword',$s); -				}else{ -					$name[$k] = $this->NewObj3('Less_Tree_Variable',array('@' . substr($s,2,-1), $index[$k], $this->env->currentFileInfo)); -				} -			} -			return $name; -		} - - -	} - -	private function rulePropertyMatch( $re, &$offset, &$length,  &$index, &$name ){ -		preg_match($re, $this->input, $a, 0, $offset); -		if( $a ){ -			$index[] = $this->pos + $length; -			$length += strlen($a[0]); -			$offset += strlen($a[0]); -			$name[] = $a[1]; -			return true; -		} -	} - -	public static function serializeVars( $vars ){ -		$s = ''; - -		foreach($vars as $name => $value){ -			$s .= (($name[0] === '@') ? '' : '@') . $name .': '. $value . ((substr($value,-1) === ';') ? '' : ';'); -		} - -		return $s; -	} - - -	/** -	 * Some versions of php have trouble with method_exists($a,$b) if $a is not an object -	 * -	 * @param string $b -	 */ -	public static function is_method($a,$b){ -		return is_object($a) && method_exists($a,$b); -	} - - -	/** -	 * Round numbers similarly to javascript -	 * eg: 1.499999 to 1 instead of 2 -	 * -	 */ -	public static function round($i, $precision = 0){ - -		$precision = pow(10,$precision); -		$i = $i*$precision; - -		$ceil = ceil($i); -		$floor = floor($i); -		if( ($ceil - $i) <= ($i - $floor) ){ -			return $ceil/$precision; -		}else{ -			return $floor/$precision; -		} -	} - - -	/** -	 * Create Less_Tree_* objects and optionally generate a cache string -	 * -	 * @return mixed -	 */ -	public function NewObj0($class){ -		$obj = new $class(); -		if( $this->CacheEnabled() ){ -			$obj->cache_string = ' new '.$class.'()'; -		} -		return $obj; -	} - -	public function NewObj1($class, $arg){ -		$obj = new $class( $arg ); -		if( $this->CacheEnabled() ){ -			$obj->cache_string = ' new '.$class.'('.Less_Parser::ArgString($arg).')'; -		} -		return $obj; -	} - -	public function NewObj2($class, $args){ -		$obj = new $class( $args[0], $args[1] ); -		if( $this->CacheEnabled() ){ -			$this->ObjCache( $obj, $class, $args); -		} -		return $obj; -	} - -	public function NewObj3($class, $args){ -		$obj = new $class( $args[0], $args[1], $args[2] ); -		if( $this->CacheEnabled() ){ -			$this->ObjCache( $obj, $class, $args); -		} -		return $obj; -	} - -	public function NewObj4($class, $args){ -		$obj = new $class( $args[0], $args[1], $args[2], $args[3] ); -		if( $this->CacheEnabled() ){ -			$this->ObjCache( $obj, $class, $args); -		} -		return $obj; -	} - -	public function NewObj5($class, $args){ -		$obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4] ); -		if( $this->CacheEnabled() ){ -			$this->ObjCache( $obj, $class, $args); -		} -		return $obj; -	} - -	public function NewObj6($class, $args){ -		$obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5] ); -		if( $this->CacheEnabled() ){ -			$this->ObjCache( $obj, $class, $args); -		} -		return $obj; -	} - -	public function NewObj7($class, $args){ -		$obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6] ); -		if( $this->CacheEnabled() ){ -			$this->ObjCache( $obj, $class, $args); -		} -		return $obj; -	} - -	//caching -	public function ObjCache($obj, $class, $args=array()){ -		$obj->cache_string = ' new '.$class.'('. self::ArgCache($args).')'; -	} - -	public function ArgCache($args){ -		return implode(',',array_map( array('Less_Parser','ArgString'),$args)); -	} - - -	/** -	 * Convert an argument to a string for use in the parser cache -	 * -	 * @return string -	 */ -	public static function ArgString($arg){ - -		$type = gettype($arg); - -		if( $type === 'object'){ -			$string = $arg->cache_string; -			unset($arg->cache_string); -			return $string; - -		}elseif( $type === 'array' ){ -			$string = ' Array('; -			foreach($arg as $k => $a){ -				$string .= var_export($k,true).' => '.self::ArgString($a).','; -			} -			return $string . ')'; -		} - -		return var_export($arg,true); -	} - -	public function Error($msg){ -		throw new Less_Exception_Parser($msg, null, $this->furthest, $this->env->currentFileInfo); -	} - -	public static function WinPath($path){ -		return str_replace('\\', '/', $path); -	} - -	public static function AbsPath($path, $winPath = false){ -		if (strpos($path, '//') !== false && preg_match('_^(https?:)?//\\w+(\\.\\w+)+/\\w+_i', $path)) { -			return $winPath ? '' : false; -		} else { -			$path = realpath($path); -			if ($winPath) { -				$path = self::WinPath($path); -			} -			return $path; -		} -	} - -	public function CacheEnabled(){ -		return (Less_Parser::$options['cache_method'] && (Less_Cache::$cache_dir || (Less_Parser::$options['cache_method'] == 'callback'))); -	} - -} | 
