summaryrefslogtreecommitdiff
path: root/inc
diff options
context:
space:
mode:
authorAndreas Gohr <andi@splitbrain.org>2013-08-14 21:33:58 +0200
committerAndreas Gohr <andi@splitbrain.org>2013-08-14 21:33:58 +0200
commit60ce17ac379e80c5ca137c559f2fce048516818d (patch)
treebc32075033750d5b1769c6cee6703968e150951e /inc
parent4fd8e66e02f7a66eb77799483d5ed3c894300362 (diff)
downloadrpg-60ce17ac379e80c5ca137c559f2fce048516818d.tar.gz
rpg-60ce17ac379e80c5ca137c559f2fce048516818d.tar.bz2
updated phpless to 0.4.0
Diffstat (limited to 'inc')
-rw-r--r--inc/lessc.inc.php398
1 files changed, 296 insertions, 102 deletions
diff --git a/inc/lessc.inc.php b/inc/lessc.inc.php
index 5c81ad2a9..0699de52f 100644
--- a/inc/lessc.inc.php
+++ b/inc/lessc.inc.php
@@ -1,7 +1,7 @@
<?php
/**
- * lessphp v0.3.9
+ * lessphp v0.4.0
* http://leafo.net/lessphp
*
* LESS css compiler, adapted from http://lesscss.org
@@ -38,7 +38,7 @@
* handling things like indentation.
*/
class lessc {
- static public $VERSION = "v0.3.9";
+ static public $VERSION = "v0.4.0";
static protected $TRUE = array("keyword", "true");
static protected $FALSE = array("keyword", "false");
@@ -55,6 +55,8 @@ class lessc {
protected $numberPrecision = null;
+ protected $allParsedFiles = array();
+
// set to the parser that generated the current line when compiling
// so we know how to create error messages
protected $sourceParser = null;
@@ -103,12 +105,17 @@ class lessc {
if (substr_compare($url, '.css', -4, 4) === 0) return false;
$realPath = $this->findImport($url);
+
if ($realPath === null) return false;
if ($this->importDisabled) {
return array(false, "/* import disabled */");
}
+ if (isset($this->allParsedFiles[realpath($realPath)])) {
+ return array(false, null);
+ }
+
$this->addParsedFile($realPath);
$parser = $this->makeParser($realPath);
$root = $parser->parse(file_get_contents($realPath));
@@ -276,6 +283,8 @@ class lessc {
foreach ($this->sortProps($block->props) as $prop) {
$this->compileProp($prop, $block, $out);
}
+
+ $out->lines = array_values(array_unique($out->lines));
}
protected function sortProps($props, $split = false) {
@@ -450,7 +459,7 @@ class lessc {
return $left == $right;
}
- protected function patternMatch($block, $callingArgs) {
+ protected function patternMatch($block, $orderedArgs, $keywordArgs) {
// match the guards if it has them
// any one of the groups must have all its guards pass for a match
if (!empty($block->guards)) {
@@ -458,7 +467,7 @@ class lessc {
foreach ($block->guards as $guardGroup) {
foreach ($guardGroup as $guard) {
$this->pushEnv();
- $this->zipSetArgs($block->args, $callingArgs);
+ $this->zipSetArgs($block->args, $orderedArgs, $keywordArgs);
$negate = false;
if ($guard[0] == "negate") {
@@ -487,24 +496,34 @@ class lessc {
}
}
- $numCalling = count($callingArgs);
-
if (empty($block->args)) {
- return $block->isVararg || $numCalling == 0;
+ return $block->isVararg || empty($orderedArgs) && empty($keywordArgs);
+ }
+
+ $remainingArgs = $block->args;
+ if ($keywordArgs) {
+ $remainingArgs = array();
+ foreach ($block->args as $arg) {
+ if ($arg[0] == "arg" && isset($keywordArgs[$arg[1]])) {
+ continue;
+ }
+
+ $remainingArgs[] = $arg;
+ }
}
$i = -1; // no args
// try to match by arity or by argument literal
- foreach ($block->args as $i => $arg) {
+ foreach ($remainingArgs as $i => $arg) {
switch ($arg[0]) {
case "lit":
- if (empty($callingArgs[$i]) || !$this->eq($arg[1], $callingArgs[$i])) {
+ if (empty($orderedArgs[$i]) || !$this->eq($arg[1], $orderedArgs[$i])) {
return false;
}
break;
case "arg":
// no arg and no default value
- if (!isset($callingArgs[$i]) && !isset($arg[2])) {
+ if (!isset($orderedArgs[$i]) && !isset($arg[2])) {
return false;
}
break;
@@ -519,14 +538,19 @@ class lessc {
} else {
$numMatched = $i + 1;
// greater than becuase default values always match
- return $numMatched >= $numCalling;
+ return $numMatched >= count($orderedArgs);
}
}
- protected function patternMatchAll($blocks, $callingArgs) {
+ protected function patternMatchAll($blocks, $orderedArgs, $keywordArgs, $skip=array()) {
$matches = null;
foreach ($blocks as $block) {
- if ($this->patternMatch($block, $callingArgs)) {
+ // skip seen blocks that don't have arguments
+ if (isset($skip[$block->id]) && !isset($block->args)) {
+ continue;
+ }
+
+ if ($this->patternMatch($block, $orderedArgs, $keywordArgs)) {
$matches[] = $block;
}
}
@@ -535,7 +559,7 @@ class lessc {
}
// attempt to find blocks matched by path and args
- protected function findBlocks($searchIn, $path, $args, $seen=array()) {
+ protected function findBlocks($searchIn, $path, $orderedArgs, $keywordArgs, $seen=array()) {
if ($searchIn == null) return null;
if (isset($seen[$searchIn->id])) return null;
$seen[$searchIn->id] = true;
@@ -545,7 +569,7 @@ class lessc {
if (isset($searchIn->children[$name])) {
$blocks = $searchIn->children[$name];
if (count($path) == 1) {
- $matches = $this->patternMatchAll($blocks, $args);
+ $matches = $this->patternMatchAll($blocks, $orderedArgs, $keywordArgs, $seen);
if (!empty($matches)) {
// This will return all blocks that match in the closest
// scope that has any matching block, like lessjs
@@ -555,7 +579,7 @@ class lessc {
$matches = array();
foreach ($blocks as $subBlock) {
$subMatches = $this->findBlocks($subBlock,
- array_slice($path, 1), $args, $seen);
+ array_slice($path, 1), $orderedArgs, $keywordArgs, $seen);
if (!is_null($subMatches)) {
foreach ($subMatches as $sm) {
@@ -567,39 +591,51 @@ class lessc {
return count($matches) > 0 ? $matches : null;
}
}
-
if ($searchIn->parent === $searchIn) return null;
- return $this->findBlocks($searchIn->parent, $path, $args, $seen);
+ return $this->findBlocks($searchIn->parent, $path, $orderedArgs, $keywordArgs, $seen);
}
// sets all argument names in $args to either the default value
// or the one passed in through $values
- protected function zipSetArgs($args, $values) {
- $i = 0;
+ protected function zipSetArgs($args, $orderedValues, $keywordValues) {
$assignedValues = array();
- foreach ($args as $a) {
+
+ $i = 0;
+ foreach ($args as $a) {
if ($a[0] == "arg") {
- if ($i < count($values) && !is_null($values[$i])) {
- $value = $values[$i];
+ if (isset($keywordValues[$a[1]])) {
+ // has keyword arg
+ $value = $keywordValues[$a[1]];
+ } elseif (isset($orderedValues[$i])) {
+ // has ordered arg
+ $value = $orderedValues[$i];
+ $i++;
} elseif (isset($a[2])) {
+ // has default value
$value = $a[2];
- } else $value = null;
+ } else {
+ $this->throwError("Failed to assign arg " . $a[1]);
+ $value = null; // :(
+ }
$value = $this->reduce($value);
$this->set($a[1], $value);
$assignedValues[] = $value;
+ } else {
+ // a lit
+ $i++;
}
- $i++;
}
// check for a rest
$last = end($args);
if ($last[0] == "rest") {
- $rest = array_slice($values, count($args) - 1);
+ $rest = array_slice($orderedValues, count($args) - 1);
$this->set($last[1], $this->reduce(array("list", " ", $rest)));
}
- $this->env->arguments = $assignedValues;
+ // wow is this the only true use of PHP's + operator for arrays?
+ $this->env->arguments = $assignedValues + $orderedValues;
}
// compile a prop and update $lines or $blocks appropriately
@@ -624,8 +660,28 @@ class lessc {
case 'mixin':
list(, $path, $args, $suffix) = $prop;
- $args = array_map(array($this, "reduce"), (array)$args);
- $mixins = $this->findBlocks($block, $path, $args);
+ $orderedArgs = array();
+ $keywordArgs = array();
+ foreach ((array)$args as $arg) {
+ $argval = null;
+ switch ($arg[0]) {
+ case "arg":
+ if (!isset($arg[2])) {
+ $orderedArgs[] = $this->reduce(array("variable", $arg[1]));
+ } else {
+ $keywordArgs[$arg[1]] = $this->reduce($arg[2]);
+ }
+ break;
+
+ case "lit":
+ $orderedArgs[] = $this->reduce($arg[1]);
+ break;
+ default:
+ $this->throwError("Unknown arg type: " . $arg[0]);
+ }
+ }
+
+ $mixins = $this->findBlocks($block, $path, $orderedArgs, $keywordArgs);
if ($mixins === null) {
// fwrite(STDERR,"failed to find block: ".implode(" > ", $path)."\n");
@@ -633,7 +689,7 @@ class lessc {
}
foreach ($mixins as $mixin) {
- if ($mixin === $block && !$args) {
+ if ($mixin === $block && !$orderedArgs) {
continue;
}
@@ -648,7 +704,7 @@ class lessc {
if (isset($mixin->args)) {
$haveArgs = true;
$this->pushEnv();
- $this->zipSetArgs($mixin->args, $args);
+ $this->zipSetArgs($mixin->args, $orderedArgs, $keywordArgs);
}
$oldParent = $mixin->parent;
@@ -705,7 +761,9 @@ class lessc {
list(,$importId) = $prop;
$import = $this->env->imports[$importId];
if ($import[0] === false) {
- $out->lines[] = $import[1];
+ if (isset($import[1])) {
+ $out->lines[] = $import[1];
+ }
} else {
list(, $bottom, $parser, $importDir) = $import;
$this->compileImportedProps($bottom, $block, $out, $parser, $importDir);
@@ -793,6 +851,60 @@ class lessc {
}
}
+ protected function lib_pow($args) {
+ list($base, $exp) = $this->assertArgs($args, 2, "pow");
+ return pow($this->assertNumber($base), $this->assertNumber($exp));
+ }
+
+ protected function lib_pi() {
+ return pi();
+ }
+
+ protected function lib_mod($args) {
+ list($a, $b) = $this->assertArgs($args, 2, "mod");
+ return $this->assertNumber($a) % $this->assertNumber($b);
+ }
+
+ protected function lib_tan($num) {
+ return tan($this->assertNumber($num));
+ }
+
+ protected function lib_sin($num) {
+ return sin($this->assertNumber($num));
+ }
+
+ protected function lib_cos($num) {
+ return cos($this->assertNumber($num));
+ }
+
+ protected function lib_atan($num) {
+ $num = atan($this->assertNumber($num));
+ return array("number", $num, "rad");
+ }
+
+ protected function lib_asin($num) {
+ $num = asin($this->assertNumber($num));
+ return array("number", $num, "rad");
+ }
+
+ protected function lib_acos($num) {
+ $num = acos($this->assertNumber($num));
+ return array("number", $num, "rad");
+ }
+
+ protected function lib_sqrt($num) {
+ return sqrt($this->assertNumber($num));
+ }
+
+ protected function lib_extract($value) {
+ list($list, $idx) = $this->assertArgs($value, 2, "extract");
+ $idx = $this->assertNumber($idx);
+ // 1 indexed
+ if ($list[0] == "list" && isset($list[2][$idx - 1])) {
+ return $list[2][$idx - 1];
+ }
+ }
+
protected function lib_isnumber($value) {
return $this->toBool($value[0] == "number");
}
@@ -1085,6 +1197,25 @@ class lessc {
$this->throwError($error);
}
+ protected function assertArgs($value, $expectedArgs, $name="") {
+ if ($expectedArgs == 1) {
+ return $value;
+ } else {
+ if ($value[0] !== "list" || $value[1] != ",") $this->throwError("expecting list");
+ $values = $value[2];
+ $numValues = count($values);
+ if ($expectedArgs != $numValues) {
+ if ($name) {
+ $name = $name . ": ";
+ }
+
+ $this->throwError("${name}expecting $expectedArgs arguments, got $numValues");
+ }
+
+ return $values;
+ }
+ }
+
protected function toHSL($color) {
if ($color[0] == 'hsl') return $color;
@@ -1229,6 +1360,10 @@ class lessc {
$var = $this->compileValue($reduced);
$res = $this->reduce(array("variable", $this->vPrefix . $var));
+ if ($res[0] == "raw_color") {
+ $res = $this->coerceColor($res);
+ }
+
if (empty($value[2])) $res = $this->lib_e($res);
return $res;
@@ -1690,7 +1825,6 @@ class lessc {
$this->importDir = (array)$this->importDir;
$this->importDir[] = $pi['dirname'].'/';
- $this->allParsedFiles = array();
$this->addParsedFile($fname);
$out = $this->compile(file_get_contents($fname), $fname);
@@ -2313,7 +2447,7 @@ class lessc_parser {
// mixin
if ($this->mixinTags($tags) &&
- ($this->argumentValues($argv) || true) &&
+ ($this->argumentDef($argv, $isVararg) || true) &&
($this->keyword($suffix) || true) && $this->end())
{
$tags = $this->fixTags($tags);
@@ -2781,38 +2915,18 @@ class lessc_parser {
return false;
}
- // consume a list of property values delimited by ; and wrapped in ()
- protected function argumentValues(&$args, $delim = ',') {
- $s = $this->seek();
- if (!$this->literal('(')) return false;
-
- $values = array();
- while (true) {
- if ($this->expressionList($value)) $values[] = $value;
- if (!$this->literal($delim)) break;
- else {
- if ($value == null) $values[] = null;
- $value = null;
- }
- }
-
- if (!$this->literal(')')) {
- $this->seek($s);
- return false;
- }
-
- $args = $values;
- return true;
- }
-
// consume an argument definition list surrounded by ()
// each argument is a variable name with optional value
// or at the end a ... or a variable named followed by ...
- protected function argumentDef(&$args, &$isVararg, $delim = ',') {
+ // arguments are separated by , unless a ; is in the list, then ; is the
+ // delimiter.
+ protected function argumentDef(&$args, &$isVararg) {
$s = $this->seek();
if (!$this->literal('(')) return false;
$values = array();
+ $delim = ",";
+ $method = "expressionList";
$isVararg = false;
while (true) {
@@ -2821,28 +2935,77 @@ class lessc_parser {
break;
}
- if ($this->variable($vname)) {
- $arg = array("arg", $vname);
- $ss = $this->seek();
- if ($this->assign() && $this->expressionList($value)) {
- $arg[] = $value;
- } else {
- $this->seek($ss);
- if ($this->literal("...")) {
- $arg[0] = "rest";
- $isVararg = true;
+ if ($this->$method($value)) {
+ if ($value[0] == "variable") {
+ $arg = array("arg", $value[1]);
+ $ss = $this->seek();
+
+ if ($this->assign() && $this->$method($rhs)) {
+ $arg[] = $rhs;
+ } else {
+ $this->seek($ss);
+ if ($this->literal("...")) {
+ $arg[0] = "rest";
+ $isVararg = true;
+ }
}
+
+ $values[] = $arg;
+ if ($isVararg) break;
+ continue;
+ } else {
+ $values[] = array("lit", $value);
}
- $values[] = $arg;
- if ($isVararg) break;
- continue;
}
- if ($this->value($literal)) {
- $values[] = array("lit", $literal);
- }
- if (!$this->literal($delim)) break;
+ if (!$this->literal($delim)) {
+ if ($delim == "," && $this->literal(";")) {
+ // found new delim, convert existing args
+ $delim = ";";
+ $method = "propertyValue";
+
+ // transform arg list
+ if (isset($values[1])) { // 2 items
+ $newList = array();
+ foreach ($values as $i => $arg) {
+ switch($arg[0]) {
+ case "arg":
+ if ($i) {
+ $this->throwError("Cannot mix ; and , as delimiter types");
+ }
+ $newList[] = $arg[2];
+ break;
+ case "lit":
+ $newList[] = $arg[1];
+ break;
+ case "rest":
+ $this->throwError("Unexpected rest before semicolon");
+ }
+ }
+
+ $newList = array("list", ", ", $newList);
+
+ switch ($values[0][0]) {
+ case "arg":
+ $newArg = array("arg", $values[0][1], $newList);
+ break;
+ case "lit":
+ $newArg = array("lit", $newList);
+ break;
+ }
+
+ } elseif ($values) { // 1 item
+ $newArg = $values[0];
+ }
+
+ if ($newArg) {
+ $values = array($newArg);
+ }
+ } else {
+ break;
+ }
+ }
}
if (!$this->literal(')')) {
@@ -2884,32 +3047,69 @@ class lessc_parser {
}
// a bracketed value (contained within in a tag definition)
- protected function tagBracket(&$value) {
+ protected function tagBracket(&$parts, &$hasExpression) {
// speed shortcut
if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "[") {
return false;
}
$s = $this->seek();
- if ($this->literal('[') && $this->to(']', $c, true) && $this->literal(']', false)) {
- $value = '['.$c.']';
- // whitespace?
- if ($this->whitespace()) $value .= " ";
- // escape parent selector, (yuck)
- $value = str_replace($this->lessc->parentSelector, "$&$", $value);
- return true;
- }
+ $hasInterpolation = false;
- $this->seek($s);
- return false;
- }
+ if ($this->literal("[", false)) {
+ $attrParts = array("[");
+ // keyword, string, operator
+ while (true) {
+ if ($this->literal("]", false)) {
+ $this->count--;
+ break; // get out early
+ }
- protected function tagExpression(&$value) {
- $s = $this->seek();
- if ($this->literal("(") && $this->expression($exp) && $this->literal(")")) {
- $value = array('exp', $exp);
- return true;
+ if ($this->match('\s+', $m)) {
+ $attrParts[] = " ";
+ continue;
+ }
+ if ($this->string($str)) {
+ // escape parent selector, (yuck)
+ foreach ($str[2] as &$chunk) {
+ $chunk = str_replace($this->lessc->parentSelector, "$&$", $chunk);
+ }
+
+ $attrParts[] = $str;
+ $hasInterpolation = true;
+ continue;
+ }
+
+ if ($this->keyword($word)) {
+ $attrParts[] = $word;
+ continue;
+ }
+
+ if ($this->interpolation($inter, false)) {
+ $attrParts[] = $inter;
+ $hasInterpolation = true;
+ continue;
+ }
+
+ // operator, handles attr namespace too
+ if ($this->match('[|-~\$\*\^=]+', $m)) {
+ $attrParts[] = $m[0];
+ continue;
+ }
+
+ break;
+ }
+
+ if ($this->literal("]", false)) {
+ $attrParts[] = "]";
+ foreach ($attrParts as $part) {
+ $parts[] = $part;
+ }
+ $hasExpression = $hasExpression || $hasInterpolation;
+ return true;
+ }
+ $this->seek($s);
}
$this->seek($s);
@@ -2925,13 +3125,9 @@ class lessc_parser {
$s = $this->seek();
- if (!$simple && $this->tagExpression($tag)) {
- return true;
- }
-
$hasExpression = false;
$parts = array();
- while ($this->tagBracket($first)) $parts[] = $first;
+ while ($this->tagBracket($parts, $hasExpression));
$oldWhite = $this->eatWhiteDefault;
$this->eatWhiteDefault = false;
@@ -2941,9 +3137,7 @@ class lessc_parser {
$parts[] = $m[1];
if ($simple) break;
- while ($this->tagBracket($brack)) {
- $parts[] = $brack;
- }
+ while ($this->tagBracket($parts, $hasExpression));
continue;
}
@@ -3334,7 +3528,7 @@ class lessc_parser {
break;
case '"':
case "'":
- if (preg_match('/'.$min[0].'.*?'.$min[0].'/', $text, $m, 0, $count))
+ if (preg_match('/'.$min[0].'.*?(?<!\\\\)'.$min[0].'/', $text, $m, 0, $count))
$count += strlen($m[0]) - 1;
break;
case '//':