Doug Stewart avatar Doug Stewart committed 391dd1d

Updating to the latest wp-less.

Comments (0)

Files changed (7)

includes/wp-less/README.md

 
 If you are using git to clone the repository do the following:
 
-    git clone git://github.com/sanchothefat/wp-less.git wp-less
-    git submodule update --init
+    git clone --recursive git://github.com/sanchothefat/wp-less.git wp-less
 
 If you are downloading the zip or tar don't forget to download the lessphp
 dependency too https://github.com/leafo/lessphp and copy it into the `lessc`
     return $vars;
 }
 
-// pass variables into a specific .less file
-// in this case 'style' is the handle we enqueued the .less file with
-add_filter( 'less_vars_style', 'style_less_vars' );
-function style_less_vars( $vars ) {
-    $vars[ 'color' ] = '#ffffff';
-    return $vars;
-}
-
 ?>
 ```
 
 
 ## License
 
-The software is licensed under [WTFPL](http://sam.zoy.org/wtfpl/)
+The software is licensed under the [MIT Licence](http://www.opensource.org/licenses/mit-license.php)

includes/wp-less/lessc/README.md

-# lessphp v0.3.0
-#### <http://leafo.net/lessphp>
+# lessphp v0.3.8
+### <http://leafo.net/lessphp>
+
+[![Build Status](https://secure.travis-ci.org/leafo/lessphp.png)](http://travis-ci.org/leafo/lessphp)
 
 `lessphp` is a compiler for LESS written in PHP. The documentation is great,
-so check it out: <http://leafo.net/lessphp/docs/>. Here's a quick tutorial:
+so check it out: <http://leafo.net/lessphp/docs/>.
 
-### How to use in your php project
+Here's a quick tutorial:
 
-Copy `lessc.inc.php` to your include directory and include it into your project.
+### How to use in your PHP project
 
-There are a few ways to interface with the compiler. The easiest is to have it
-compile a LESS file when the page is requested. The static function 
-`lessc::ccompile`, checked compile, will compile the input LESS file only when it
-is newer than the output file.
+The only file required is `lessc.inc.php`, so copy that to your include directory.
 
-	try {
-		lessc::ccompile('input.less', 'output.css');
-	catch (exception $ex) {
-		exit($ex->getMessage());
-	}
+The typical flow of **lessphp** is to create a new instance of `lessc`,
+configure it how you like, then tell it to compile something using one built in
+compile methods.
 
-Note that all failures with lessc are reported through exceptions.
-If you need more control you can make your own instance of lessc.
+The `compile` method compiles a string of LESS code to CSS.
 
-	$input = 'mystyle.less';
+```php
+<?php
+require "lessc.inc.php";
 
-	$lc = new lessc($input);
+$less = new lessc;
+echo $less->compile(".block { padding: 3 + 4px }");
+```
 
-	try {
-		file_put_contents('mystyle.css', $lc->parse());
-	} catch (exception $ex) { ... }
+The `compileFile` method reads and compiles a file. It will either return the
+result or write it to the path specified by an optional second argument.
 
-In addition to loading from file, you can also parse from a string like so:
+```php
+<?php
+echo $less->compileFile("input.less");
+```
 
-	$lc = new lessc();
-	$lesscode = 'body { ... }';
-	$out = $lc->parse($lesscode);
+The `compileChecked` method is like `compileFile`, but it only compiles if the output
+file doesn't exist or it's older than the input file:
+
+```php
+<?php
+$less->checkedCompile("input.less", "output.css");
+```
+
+If there any problem compiling your code, an exception is thrown with a helpful message:
+
+```php
+<?php
+try {
+  $less->compile("invalid LESS } {");
+} catch (exception $e) {
+  echo "fatal error: " . $e->getMessage();
+}
+```
+
+The `lessc` object can be configured through an assortment of instance methods.
+Some possible configuration options include [changing the output format][1],
+[setting variables from PHP][2], and [controlling the preservation of
+comments][3], writing [custom functions][4] and much more. It's all described
+in [the documentation][0].
+
+
+ [0]: http://leafo.net/lessphp/docs/
+ [1]: http://leafo.net/lessphp/docs/#output_formatting
+ [2]: http://leafo.net/lessphp/docs/#setting_variables_from_php
+ [3]: http://leafo.net/lessphp/docs/#preserving_comments
+ [4]: http://leafo.net/lessphp/docs/#custom_functions
+
 
 ### How to use from the command line
 
 line. In the simplest invocation, you specify an input file and the compiled
 css is written to standard out:
 
-	~> plessc input.less > output.css
+    $ plessc input.less > output.css
 
 Using the -r flag, you can specify LESS code directly as an argument or, if 
 the argument is left off, from standard in:
 
-	~> plessc -r "my less code here"
+    $ plessc -r "my less code here"
 
 Finally, by using the -w flag you can watch a specified input file and have it 
-compile as needed to the output file
+compile as needed to the output file:
 
-	~> plessc -w input-file output-file
+    $ plessc -w input-file output-file
 
 Errors from watch mode are written to standard out.
 
+The -f flag sets the [output formatter][1]. For example, to compress the
+output run this:
 
+    $ plessc -f=compressed myfile.less
+
+For more help, run `plessc --help`
+

includes/wp-less/lessc/docs/docs.md

-    title: v0.3.0 documentation
+    title: v0.3.8 documentation
     link_to_home: true
 --
 
-<h2 skip="true">Documentation v0.3.0</h2>
+<h2 skip="true">Documentation v0.3.8</h2>
 
 <div style="margin-bottom: 1em;">$index</div>
 
 Variables are only visible for use from their current scope, or any enclosed
 scopes.
 
+If you have a string or keyword in a variable, you can reference another
+variable by that name by repeating the `@`:
+
+    ```less
+    @value: 20px;
+    @value_name: "value";
+
+    width: @@value_name;
+    ```
+
 ### Expressions
 
 Expressions let you combine values and variables in meaningful ways. For
     margin: 10px - 5px;
     ```
 
-Division has a special quirk. Due to CSS font shorthand syntax, we need to be
-careful about how we place spaces. In the following example we are using font
-size and lineheight shorthand. No division should take place:
+Division has a special quirk. There are certain CSS properties that use the `/`
+operator as part of their value's syntax. Namely, the [font][4] shorthand and
+[border-radius][3].
+
+  [3]: https://developer.mozilla.org/en/CSS/border-radius
+  [4]: https://developer.mozilla.org/en/CSS/font
+
+
+Thus, **lessphp** will ignore any division in these properties unless it is
+wrapped in parentheses. For example, no division will take place here:
 
     ```less
     .font {
     }
     ```
 
-In order to force division we can surround the `/` by spaces, or we can wrap
-the expression in parentheses:
+In order to force division we must wrap the expression in parentheses:
 
     ```less
     .font {
-      // these two will evaluate
-      font: 20px / 80px "Times New Roman";
       font: (20px/80px) "Times New Roman";
     }
     ```
 
+If you want to write a literal `/` expression without dividing in another
+property (or a variable), you can use [string unquoting](#string_unquoting):
+
+    ```less
+    .var {
+      @size: ~"20px/80px";
+      font: @size sans-serif;
+    }
+    ```
+
 ### Nested Blocks
 
 By nesting blocks we can build up a chain of CSS selectors through scope
     ```less
     ol.list {
       li.special {
-        border: 1px solid red; 
+        border: 1px solid red;
       }
 
       li.plain {
 
 The canonical example is to create a rounded corners mixin that works across
 browsers:
-    
+
     ```less
     .rounded-corners(@radius: 5px) {
       border-radius: @radius;
     .secret() {
       font-size: 6000px;
     }
-    
+
     .div {
       .secret;
     }
     }
     ```
 
+#### `@arguments` Variable
+
+Within an mixin there is a special variable named `@arguments` that contains
+all the arguments passed to the mixin along with any remaining arguments that
+have default values. The value of the variable has all the values separated by
+spaces.
+
+This useful for quickly assigning all the arguments:
+
+    ```less
+    .box-shadow(@x, @y, @blur, @color) {
+      box-shadow: @arguments;
+      -webkit-box-shadow: @arguments;
+      -moz-box-shadow: @arguments;
+    }
+    .menu {
+      .box-shadow(1px, 1px, 5px, #aaa);
+    }
+    ```
+
+In addition to the arguments passed to the mixin, `@arguments` will also include
+remaining default values assigned by the mixin:
+
+
+    ```less
+    .border-mixin(@width, @style: solid, @color: black) {
+      border: @arguments;
+    }
+
+    pre {
+      .border-mixin(4px, dotted);
+    }
+
+    ```
+
+
+#### Pattern Matching
+
+When you *mix in* a mixin, all the available mixins of that name in the current
+scope are checked to see if they match based on what was passed to the mixin
+and how it was declared.
+
+The simplest case is matching by number of arguments. Only the mixins that
+match the number of arguments passed in are used.
+
+    ```less
+    .simple() { // matches no arguments
+      height: 10px;
+    }
+
+    .simple(@a, @b) { // matches two arguments
+      color: red;
+    }
+
+    .simple(@a) { // matches one argument
+      color: blue;
+    }
+
+    div {
+      .simple(10);
+    }
+
+    span {
+      .simple(10, 20);
+    }
+    ```
+
+Whether an argument has default values is also taken into account when matching
+based on number of arguments:
+
+    ```less
+    // matches one or two arguments
+    .hello(@a, @b: blue) {
+      height: @a;
+      color: @b;
+    }
+
+    .hello(@a, @b) { // matches only two
+      width: @a;
+      border-color: @b;
+    }
+
+    .hello(@a) { // matches only one
+      padding: 1em;
+    }
+
+    div {
+      .hello(10px);
+    }
+
+    pre {
+      .hello(10px, yellow);
+    }
+    ```
+
+Additionally, a *vararg* value can be used to further control how things are
+matched.  A mixin's argument list can optionally end in the special argument
+named `...`.  The `...` may match any number of arguments, including 0.
+
+    ```less
+    // this will match any number of arguments
+    .first(...) {
+      color: blue;
+    }
+
+    // matches at least 1 argument
+    .second(@arg, ...) {
+      height: 200px + @arg;
+    }
+
+    div { .first("some", "args"); }
+    pre { .second(10px); }
+    ```
+
+If you want to capture the values that get captured by the *vararg* you can
+give it a variable name by putting it directly before the `...`. This variable
+must be the last argument defined. It's value is just like the special
+[`@arguments` variable](#arguments_variable), a space separated list.
+
+
+    ```less
+    .hello(@first, @rest...) {
+      color: @first;
+      text-shadow: @rest;
+    }
+
+    span {
+      .hello(red, 1px, 1px, 0px, white);
+    }
+
+    ```
+
+Another way of controlling whether a mixin matches is by specifying a value in
+place of an argument name when declaring the mixin:
+
+    ```less
+    .style(old, @size) {
+      font: @size serif;
+    }
+
+    .style(new, @size) {
+      font: @size sans-serif;
+    }
+
+    .style(@_, @size) {
+      letter-spacing: floor(@size / 6px);
+    }
+
+    em {
+      @switch: old;
+      .style(@switch, 15px);
+    }
+    ```
+
+Notice that two of the three mixins were matched. The mixin with a matching
+first argument, and the generic mixin that matches two arguments. It's common
+to use `@_` as the name of a variable we intend to not use. It has no special
+meaning to LESS, just to the reader of the code.
+
+#### Guards
+
+Another way of restricting when a mixin is mixed in is by using guards. A guard
+is a special expression that is associated with a mixin declaration that is
+evaluated during the mixin process. It must evaluate to true before the mixin
+can be used.
+
+We use the `when` keyword to begin describing a list of guard expressions.
+
+Here's a simple example:
+
+    ```less
+    .guarded(@arg) when (@arg = hello) {
+      color: blue;
+    }
+
+    div {
+      .guarded(hello); // match
+    }
+
+    span {
+      .guarded(world); // no match
+    }
+    ```
+Only the `div`'s mixin will match in this case, because the guard expression
+requires that `@arg` is equal to `hello`.
+
+We can include many different guard expressions by separating them by commas.
+Only one of them needs to match to trigger the mixin:
+
+    ```less
+    .x(@a, @b) when (@a = hello), (@b = world) {
+      width: 960px;
+    }
+
+    div {
+      .x(hello, bar); // match
+    }
+
+    span {
+      .x(foo, world); // match
+    }
+
+    pre {
+      .x(foo, bar); // no match
+    }
+    ```
+
+Instead of a comma, we can use `and` keyword to make it so all of the guards
+must match in order to trigger the mixin. `and` has higher precedence than the
+comma.
+
+    ```less
+    .y(@a, @b) when (@a = hello) and (@b = world) {
+      height: 600px;
+    }
+
+    div {
+      .y(hello, world); // match
+    }
+
+    span {
+      .y(hello, bar); // no match
+    }
+    ```
+
+Commas and `and`s can be mixed and matched.
+
+You can also negate a guard expression by using `not` in from of the parentheses:
+
+    ```less
+    .x(@a) when not (@a = hello) {
+      color: blue;
+    }
+
+    div {
+      .x(hello); // no match
+    }
+    ```
+
+The `=` operator is used to check equality between any two values. For numbers
+the following comparison operators are also defined:
+
+`<`, `>`, `=<`, `>=`
+
+There is also a collection of predicate functions that can be used to test the
+type of a value.
+
+These are `isnumber`, `iscolor`, `iskeyword`, `isstring`, `ispixel`,
+`ispercentage` and `isem`.
+
+    ```less
+    .mix(@a) when (ispercentage(@a)) {
+      height: 500px * @a;
+    }
+    .mix(@a) when (ispixel(@a)) {
+      height: @a;
+    }
+
+    div.a {
+      .mix(50%);
+    }
+
+    div.a {
+      .mix(350px);
+    }
+    ```
+
+#### !important
+
+If you want to apply the `!important` suffix to every property when mixing in a
+mixin, just append `!important` to the end of the call to the mixin:
+
+    ```less
+    .make_bright {
+      color: red;
+      font-weight: bold;
+    }
+
+    .color {
+      color: green;
+    }
+
+    body {
+      .make_bright() !important;
+      .color();
+    }
+
+    ```
+
+### Selector Expressions
+
+Sometimes we want to dynamically generate the selector of a block based on some
+variable or expression. We can do this by using *selector expressions*. Selector
+expressions are CSS selectors that are evaluated in the current scope before
+being written out.
+
+A simple example is a mixin that dynamically creates a selector named after the
+mixin's argument:
+
+    ```less
+    .create-selector(@name) {
+      (e(@name)) {
+        color: red;
+      }
+    }
+
+    .create-selector("hello");
+    .create-selector("world");
+    ```
+
+Any selector that is enclosed in `()` will have it's contents evaluated and
+directly written to output. The value is not changed any way before being
+outputted, thats why we use the `e` function. If you're not familiar, the `e`
+function strips quotes off a string value. If we didn't have it, then the
+selector would have quotes around it, and that's not valid CSS!
+
+Any value can be used in a selector expression, but it works best when using
+strings and things like [String Interpolation](#string_interpolation).
+
+Here's an interesting example adapted from Twitter Bootstrap. A couple advanced
+things are going on. We are using [Guards](#guards) along with a recursive
+mixin to work like a loop to generate a series of CSS blocks.
+
+
+    ```less
+    // create our recursive mixin:
+    .spanX (@index) when (@index > 0) {
+      (~".span@{index}") {
+        width: @index * 100px;
+      }
+      .spanX(@index - 1);
+    }
+    .spanX (0) {}
+
+    // mix it into the global scopee:
+    .spanX(4);
+    ```
+
 ### Import
 
 Multiple LESS files can be compiled into a single CSS file by using the
 * `floor(number)` -- returns the floor of a numerical input
 * `round(number)` -- returns the rounded value of numerical input
 
-* `lighten(color, percent)` -- lightens color by percent and returns it
-* `darken(color, percent)` -- darkens color by percent and returns it
+* `lighten(color, percent)` -- lightens `color` by `percent` and returns it
+* `darken(color, percent)` -- darkens `color` by `percent` and returns it
 
-* `saturate(color, percent)` -- saturates color by percent and returns it
-* `desaturate(color, percent)` -- desaturates color by percent and returns it
+* `saturate(color, percent)` -- saturates `color` by `percent` and returns it
+* `desaturate(color, percent)` -- desaturates `color` by `percent` and returns it
 
-* `fadein(color, percent)` -- makes color less transparent by percent and returns it
-* `fadeout(color, percent)` -- makes color more transparent by percent and returns it
+* `fadein(color, percent)` -- makes `color` less transparent by `percent` and returns it
+* `fadeout(color, percent)` -- makes `color` more transparent by `percent` and returns it
 
-* `spin(color, amount)` -- returns a color with amount degrees added to hue
+* `spin(color, amount)` -- returns a color with `amount` degrees added to hue
+
+* `fade(color, amount)` -- returns a color with the alpha set to `amount`
+
+* `hue(color)` -- returns the hue of `color`
+
+* `saturation(color)` -- returns the saturation of `color`
+
+* `lightness(color)` -- returns the lightness of `color`
+
+* `alpha(color)` -- returns the alpha value of `color` or 1.0 if it doesn't have an alpha
+
+* `percentage(number)` -- converts a floating point number to a percentage, e.g. `0.65` -> `65%`
+
+* `mix(color1, color1, percent)` -- mixes two colors by percentage where 100%
+  keeps all of `color1`, and 0% keeps all of `color2`. Will take into account
+  the alpha of the colors if it exists. See
+  <http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#mix-instance_method>.
 
 * `rgbahex(color)` -- returns a string containing 4 part hex color.
-   
+
    This is used to convert a CSS color into the hex format that IE's filter
    method expects when working with an alpha component.
-   
+
        ```less
        .class {
           @start: rgbahex(rgba(25, 34, 23, .5));
 
 ## PHP Interface
 
-The PHP interface lets you control the compiler from your PHP scripts. There is
-only one file to include to get access to everything:
+When working with **lessphp** from PHP, the typical flow is to create a new
+instance of `lessc`, configure it how you like, then tell it to compile
+something using one built in compile methods.
+
+Methods:
+
+* [`compile($string)`](#compiling[) -- Compile a string
+
+* [`compileFile($inFile, [$outFile])`](#compiling) -- Compile a file to another or return it
+
+* [`checkedCompile($inFile, $outFile)`](#compiling) -- Compile a file only if it's newer
+
+* [`cachedCompile($cacheOrFile, [$force])`](#compiling_automatically) -- Conditionally compile while tracking imports
+
+* [`setFormatter($formatterName)`](#output_formatting) -- Change how CSS output looks
+
+* [`setPreserveComments($keepComments)`](#preserving_comments) -- Change if comments are kept in output
+
+* [`registerFunction($name, $callable)`](#custom_functions) -- Add a custom function
+
+* [`unregisterFunction($name)`](#custom_functions) -- Remove a registered function
+
+* [`setVariables($vars)`](#setting_variables_from_php) -- Set a variable from PHP
+
+* [`unsetVariable($name)`](#setting_variables_from_php) -- Remove a PHP variable
+
+* [`setImportDir($dirs)`](#import_directory) -- Set the search path for imports
+
+* [`addImportDir($dir)`](#import_directory) -- Append directory to search path for imports
+
+
+### Compiling
+
+The `compile` method compiles a string of LESS code to CSS.
 
     ```php
     <?php
-    include "lessc.inc.php";
+    require "lessc.inc.php";
+
+    $less = new lessc;
+    echo $less->compile(".block { padding: 3 + 4px }");
     ```
 
-To compile a file to a string (of CSS code):
+The `compileFile` method reads and compiles a file. It will either return the
+result or write it to the path specified by an optional second argument.
 
     ```php
-    $less = new lessc("myfile.less");
-    $css = $less->parse();
+    echo $less->compileFile("input.less");
     ```
 
-To compile a string to a string:
+The `compileChecked` method is like `compileFile`, but it only compiles if the output
+file doesn't exist or it's older than the input file:
 
     ```php
-    $less = new lessc(); // a blank lessc
-    $css = $less->parse("body { a { color: red } }");
+    $less->checkedCompile("input.less", "output.css");
     ```
 
+See [Compiling Automatically](#compiling_automatically) for a description of
+the more advanced `cachedCompile` method.
+
+### Output Formatting
+
+Output formatting controls the indentation of the output CSS. Besides the
+default formatter, two additional ones are included and it's also easy to make
+your own.
+
+To use a formatter, the method `setFormatter` is used. Just
+pass the name of the formatter:
+
+    ```php
+    $less = new lessc;
+
+    $less->setFormatter("compressed");
+    echo $less->compile("div { color: lighten(blue, 10%) }");
+    ```
+
+In this example, the `compressed` formatter is used. The formatters are:
+
+ * `lessjs` *(default)* -- Same style used in LESS for JavaScript
+
+ * `compressed` -- Compresses all the unrequired whitespace
+
+ * `classic` -- **lessphp**'s original formatter
+
+To revert to the default formatter, call `setFormatter` with a value of `null`.
+
+#### Custom Formatter
+
+The easiest way to customize the formatter is to create your own instance of an
+existing formatter and alter its public properties before passing it off to
+**lessphp**. The `setFormatter` method can also take an instance of a
+formatter.
+
+Each of the formatter names corresponds to a class with `lessc_formatter_`
+prepended in front of it. Here the classic formatter is customized to use tabs
+instead of spaces:
+
+
+    ```php
+    $formatter = new lessc_formatter_classic;
+    $formatter->indentChar = "\t";
+
+    $less = new lessc;
+    $less->setFormatter($formatter);
+    echo $less->compileFile("myfile.less");
+    ```
+
+For more information about what can be configured with the formatter consult
+the source code.
+
+### Preserving Comments
+
+By default, all comments in the source LESS file are stripped when compiling.
+You might want to keep the `/* */` comments in the output though. For
+example, bundling a license in the file.
+
+Enable or disable comment preservation by calling `setPreserveComments`:
+
+    ```php
+    $less = new lessc;
+    $less->setPreserveComments(true);
+    echo $less->compile("/* hello! */");
+    ```
+
+Comments are disabled by default because there is additional overhead, and more
+often than not they aren't needed.
+
+
 ### Compiling Automatically
 
-Often, you want to write the compiled CSS to a file, and only recompile when
-the original LESS file has changed. The following function will check if the
-modification date of the LESS file is more recent than the CSS file.  The LESS
-file will be compiled if it is. If the CSS file doesn't exist yet, then it will
-also compile the LESS file.
+Often, you want to only compile a LESS file only if it has been modified since
+last compile. This is very important because compiling is performance intensive
+and you should avoid a recompile if it possible.
+
+The `checkedCompile` compile method will do just that. It will check if the
+input file is newer than the output file, or if the output file doesn't exist
+yet, and compile only then.
 
     ```php
-    lessc::ccompile('myfile.less', 'mystyle.css');
+    $less->checkedCompile("input.less", "output.css");
     ```
 
-`ccompile` is very basic, it only checks if the input file's modification time.
-It is not of any files that are brought in using `@import`.
+There's a problem though. `checkedCompile` is very basic, it only checks the
+input file's modification time. It is unaware of any files from `@import`.
 
-For this reason we also have `lessc::cexecute`. It functions slightly
-differently, but gives us the ability to check changes to all files used during
-the compile. It takes one argument, either the name of the file we want to
-compile, or an existing *cache object*. Its return value is an updated cache
-object.
+
+For this reason we also have `cachedCompile`. It's slightly more complex, but
+gives us the ability to check changes to all files including those imported. It
+takes one argument, either the name of the file we want to compile, or an
+existing *cache object*. Its return value is an updated cache object.
 
 If we don't have a cache object, then we call the function with the name of the
 file to get the initial cache object. If we do have a cache object, then we
 determine if a rebuild is required.
 
 The cache object is a plain PHP `array`. It stores the last time it compiled in
-`$cache['updated']` and output of the compile in `$cache['compiled']`.
+`$cache["updated"]` and output of the compile in `$cache["compiled"]`.
 
 Here we demonstrate creating an new cache object, then using it to see if we
 have a recompiled version available to be written:
 
 
     ```php
-    $less_file = 'myfile.less';
-    $css_file = 'myfile.css';
+    $inputFile = "myfile.less";
+    $outputFile = "myfile.css";
+
+    $less = new lessc;
 
     // create a new cache object, and compile
-    $cache = lessc::cexecute('myfile.less');
-    file_put_contents($css_file, $cache['compiled']);
+    $cache = $less->cachedCompile($inputFile);
+
+    file_put_contents($outputFile, $cache["compiled"]);
 
     // the next time we run, write only if it has updated
-    $last_updated = $cache['updated'];
-    $cache = lessc::cexecute($cache);
-    if ($cache['updated'] > $last_updated) {
-        file_put_contents($css_file, $cache['compiled']);
+    $last_updated = $cache["updated"];
+    $cache = $less->cachedCompile($cache);
+    if ($cache["updated"] > $last_updated) {
+        file_put_contents($outputFile, $cache["compiled"]);
     }
 
     ```
 An example with saving cache object to a file:
 
     ```php
-    function auto_compile_less($less_fname, $css_fname) {
+    function autoCompileLess($inputFile, $outputFile) {
       // load the cache
-      $cache_fname = $less_fname.".cache";
-      if (file_exists($cache_fname)) {
-        $cache = unserialize(file_get_contents($cache_fname));
+      $cacheFile = $inputFile.".cache";
+
+      if (file_exists($cacheFile)) {
+        $cache = unserialize(file_get_contents($cacheFile));
       } else {
-        $cache = $less_fname;
+        $cache = $inputFile;
       }
 
-      $new_cache = lessc::cexecute($cache);
-      if (!is_array($cache) || $new_cache['updated'] > $cache['updated']) {
-        file_put_contents($cache_fname, serialize($new_cache));
-        file_put_contents($css_fname, $new_cache['compiled']);
+      $less = new lessc;
+      $newCache = $less->cachedCompile($cache);
+
+      if (!is_array($cache) || $newCache["updated"] > $cache["updated"]) {
+        file_put_contents($cacheFile, serialize($newCache));
+        file_put_contents($outputFile, $newCache['compiled']);
       }
     }
 
-    auto_compile_less('myfile.less', 'myfile.css')
-
+    autoCompileLess('myfile.less', 'myfile.css');
     ```
 
-`lessc:cexecute` takes an optional second argument, `$force`. Passing in true
-will cause the input to always be recompiled.
+`cachedCompile` method takes an optional second argument, `$force`. Passing in
+true will cause the input to always be recompiled.
 
 ### Error Handling
 
-All of the following methods will throw an `Exception` if the parsing fails:
+All of the compile methods will throw an `Exception` if the parsing fails or
+there is a compile time error. Compile time errors include things like passing
+incorrectly typed values for functions that expect specific things, like the
+color manipulation functions.
 
     ```php
-    $less = new lessc();
+    $less = new lessc;
     try {
-        $less->parse("} invalid LESS }}}");
+        $less->compile("} invalid LESS }}}");
     } catch (Exception $ex) {
         echo "lessphp fatal error: ".$ex->getMessage();
     }
     ```
 ### Setting Variables From PHP
 
-The `parse` function takes a second optional argument. If you want to
-initialize variables from outside the LESS file then you can pass in an
-associative array of names and values. The values will parsed as CSS values:
+Before compiling any code you can set initial LESS variables from PHP. The
+`setVariables` method lets us do this. It takes an associative array of names
+to values. The values must be strings, and will be parsed into correct CSS
+values.
+
 
     ```php
-    $less = new lessc();
-    echo $less->parse(".magic { color: @color;  width: @base - 200; }", 
-        array(
-            'color' => 'red';
-            'base' => '960px';
-        ));
+    $less = new lessc;
+
+    $less->setVariables(array(
+      "color" => "red",
+      "base" => "960px"
+    ));
+
+    echo $less->compile(".magic { color: @color;  width: @base - 200; }");
     ```
 
-You can also do this when loading from a file, but remember to set the first
-argument of the parse function to `null`, otherwise it will try to compile that
-instead of the file:
+If you need to unset a variable, the `unsetVariable` method is available. It
+takes the name of the variable to unset.
 
     ```php
-    $less = new lessc("myfile.less");
-    echo $less->parse(null, array('color' => 'blue'));
+    $less->unsetVariable("color");
+    ```
+
+Be aware that the value of the variable is a string containing a CSS value. So
+if you want to pass a LESS string in, you're going to need two sets of quotes.
+One for PHP and one for LESS.
+
+
+    ```php
+    $less->setVariables(array(
+      "url" => "'http://example.com.com/'"
+    ));
+
+    echo $less->compile("body { background: url("@{url}/bg.png"); }");
+    ```
+
+### Import Directory
+
+When running the `@import` directive, an array of directories called the import
+search path is searched through to find the file being asked for.
+
+By default, when using `compile`, the import search path just contains `""`,
+which is equivalent to the current directory of the script. If `compileFile` is
+used, then the directory of the file being compiled is used as the starting
+import search path.
+
+Two methods are available for configuring the search path.
+
+`setImportDir` will overwrite the search path with its argument. If the value
+isn't an array it will be converted to one.
+
+
+In this example, `@import "colors";` will look for either
+`assets/less/colors.less` or `assets/bootstrap/colors.less` in that order:
+
+    ```php
+    $less->setImportDir(array("assets/less/", "assets/bootstrap");
+
+    echo $less->compile('@import "colors";');
+    ```
+
+`addImportDir` will append a single path to the import search path instead of
+overwritting the whole thing.
+
+    ```php
+    $less->addImportDir("public/stylesheets");
     ```
 
 ### Custom Functions
 
 **lessphp** has a simple extension interface where you can implement user
 functions that will be exposed in LESS code during the compile. They can be a
-little tricky though because you need to work with the  **lessphp** type system.
+little tricky though because you need to work with the **lessphp** type system.
 
-By sub-classing `lessc`, and creating specially named methods we can extend
-**lessphp**. In order for a function to be visible in LESS, its name must
-start with `lib_`.
+The two methods we are interested in are `registerFunction` and
+`unregisterFunction`. `registerFunction` takes two arguments, a name and a
+callable value. `unregisterFunction` just takes the name of an existing
+function to remove.
 
-Let's make a function that doubles any numeric argument.
+Here's an example that adds a function called `double` that doubles any numeric
+argument:
 
     ```php
     <?php
     include "lessc.inc.php";
 
-    class myless extends lessc {
-        function lib_double($arg) {
-            list($type, $value) = $arg;
-            return array($type, $value*2);
-        }
+    function lessphp_double($arg) {
+        list($type, $value) = $arg;
+        return array($type, $value*2);
     }
 
-    $myless = new myless();
-    echo $myless->parse("div { width: double(400px); }");
+    $less = new lessc;
+    $less->registerFunction("double", "lessphp_double");
+
+    // gives us a width of 800px
+    echo $less->compile("div { width: double(400px); }");
     ```
 
-Although a little verbose, the implementation of `lib_double` gives us some
-insight on the type system. All values are stored in an array where the 0th
-element is a string representing the type, and the other elements make up the
+The second argument to `registerFunction` is any *callable value* that is
+understood by [`call_user_func`](http://php.net/call_user_func).
+
+If we are using PHP 5.3 or above then we are free to pass a function literal
+like so:
+
+    ```php
+    $less->registerFunction("double", function($arg) {
+        list($type, $value) = $arg;
+        return array($type, $value*2);
+    });
+    ```
+
+Now let's talk about the `double` function itself.
+
+Although a little verbose, the implementation gives us some insight on the type
+system. All values in **lessphp** are stored in an array where the 0th element
+is a string representing the type, and the other elements make up the
 associated data for that value.
 
-The best way to get an understanding of the system is to make a dummy `lib_`
-function which does a `vardump` on the argument. Try passing the function
+The best way to get an understanding of the system is to register is dummy
+function which does a `var_dump` on the argument. Try passing the function
 different values from LESS and see what the results are.
 
-The return value of the `lib_` function must also be a LESS type, but if it is
-a string or numeric value, it will automatically be coerced into an appropriate
-typed value. In our example, we reconstruct the value with our modifications
-while making sure that we preserve the type.
+The return value of the registered function must also be a **lessphp** type,
+but if it is a string or numeric value, it will automatically be coerced into
+an appropriate typed value. In our example, we reconstruct the value with our
+modifications while making sure that we preserve the original type.
 
-All of the built in functions are implemented in this manner within the `lessc`
-class.
+The instance of **lessphp** itself is sent to the registered function as the
+second argument in addition to the arguments array.
 
 ## Command Line Interface
 
 
 ## License
 
-Copyright (c) 2010 Leaf Corcoran, <http://leafo.net/lessphp>
- 
+Copyright (c) 2012 Leaf Corcoran, <http://leafo.net/lessphp>
+
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the
 "Software"), to deal in the Software without restriction, including
 distribute, sublicense, and/or sell copies of the Software, and to
 permit persons to whom the Software is furnished to do so, subject to
 the following conditions:
- 
+
 The above copyright notice and this permission notice shall be
 included in all copies or substantial portions of the Software.
- 
+
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND

includes/wp-less/lessc/lessc.inc.php

 <?php
 
 /**
- * lessphp v0.3.0
+ * lessphp v0.3.8
  * http://leafo.net/lessphp
  *
  * LESS css compiler, adapted from http://lesscss.org
  *
- * Copyright 2011, Leaf Corcoran <leafot@gmail.com>
+ * Copyright 2012, Leaf Corcoran <leafot@gmail.com>
  * Licensed under MIT or GPLv3, see LICENSE
  */
 
 /**
  * The less compiler and parser.
  *
- * Converting LESS to CSS is a two stage process. First the incoming document
- * must be parsed. Parsing creates a tree in memory that represents the 
- * structure of the document. Then, the tree of the document is recursively
- * compiled into the CSS text. The compile step has an implicit step called
- * reduction, where values are brought to their lowest form before being
- * turned to text, eg. mathematical equations are solved, and variables are
- * dereferenced.
+ * Converting LESS to CSS is a three stage process. The incoming file is parsed
+ * by `lessc_parser` into a syntax tree, then it is compiled into another tree
+ * representing the CSS structure by `lessc`. The CSS tree is fed into a
+ * formatter, like `lessc_formatter` which then outputs CSS as a string.
  *
- * The parsing stage produces the final structure of the document, for this
- * reason mixins are mixed in and attribute accessors are referenced during
- * the parse step. A reduction is done on the mixed in block as it is mixed in.
+ * During the first compile, all values are *reduced*, which means that their
+ * types are brought to the lowest form before being dump as strings. This
+ * handles math equations, variable dereferences, and the like.
  *
- *  See the following:
- *    - entry point for parsing and compiling: lessc::parse()
- *    - parsing: lessc::parseChunk()
- *    - compiling: lessc::compileBlock()
+ * The `parse` function of `lessc` is the entry point.
  *
+ * In summary:
+ *
+ * The `lessc` class creates an intstance of the parser, feeds it LESS code,
+ * then transforms the resulting tree to a CSS tree. This class also holds the
+ * evaluation context, such as all available mixins and variables at any given
+ * time.
+ *
+ * The `lessc_parser` class is only concerned with parsing its input.
+ *
+ * The `lessc_formatter` takes a CSS tree, and dumps it to a formatted string,
+ * handling things like indentation.
  */
 class lessc {
-	protected $buffer;
-	protected $count;
-	protected $line;
-
-	public $indentLevel;
-	public $indentChar = '  ';
-
-	protected $env = null;
-
-	protected $allParsedFiles = array();
+	static public $VERSION = "v0.3.8";
+	static protected $TRUE = array("keyword", "true");
+	static protected $FALSE = array("keyword", "false");
+
+	protected $libFunctions = array();
+	protected $registeredVars = array();
+	protected $preserveComments = false;
 
 	public $vPrefix = '@'; // prefix of abstract properties
 	public $mPrefix = '$'; // prefix of abstract blocks
-	public $imPrefix = '!'; // special character to add !important
 	public $parentSelector = '&';
 
-	static protected $precedence = array(
-		'+' => 0,
-		'-' => 0,
-		'*' => 1,
-		'/' => 1,
-		'%' => 1,
-	);
-	static protected $operatorString; // regex string to match any of the operators
-
-	// types that have delayed computation
-	static protected $dtypes = array('expression', 'variable',
-		'function', 'negative', 'list', 'lookup');
-
-	/**
-	 * @link http://www.w3.org/TR/css3-values/
-	 */
-	static protected $units=array(
-		'em', 'ex', 'px', 'gd', 'rem', 'vw', 'vh', 'vm', 'ch', // Relative length units
-		'in', 'cm', 'mm', 'pt', 'pc', // Absolute length units
-		'%', // Percentages
-		'deg', 'grad', 'rad', 'turn', // Angles
-		'ms', 's', // Times
-		'Hz', 'kHz', //Frequencies
-	);
-    
 	public $importDisabled = false;
 	public $importDir = '';
 
-	public $compat = false; // lessjs compatibility mode, does nothing right now
-
-	/**
-	 * if we are in an expression then we don't need to worry about parsing font shorthand
-	 * $inExp becomes true after the first value in an expression, or if we enter parens
-	 */
-	protected $inExp = false;
-
-	/**
-	 * if we are in parens we can be more liberal with whitespace around operators because 
-	 * it must evaluate to a single value and thus is less ambiguous.
-	 *
-	 * Consider:
-	 *     property1: 10 -5; // is two numbers, 10 and -5
-	 *     property2: (10 -5); // should evaluate to 5
-	 */
-	protected $inParens = false;
-
-	/**
-	 * Parse a single chunk off the head of the buffer and place it.
-	 * @return false when the buffer is empty, or there is an error
-	 *
-	 * This functions is called repeatedly until the entire document is
-	 * parsed.
-	 *
-	 * This parser is most similar to a recursive descent parser. Single
-	 * functions represent discrete grammatical rules for the language, and
-	 * they are able to capture the text that represents those rules.
-	 *
-	 * Consider the function lessc::keyword(). (all parse functions are
-	 * structured the same)
-	 *
-	 * The function takes a single reference argument. When calling the the
-	 * function it will attempt to match a keyword on the head of the buffer.
-	 * If it is successful, it will place the keyword in the referenced
-	 * argument, advance the position in the buffer, and return true. If it
-	 * fails then it won't advance the buffer and it will return false.
-	 *
-	 * All of these parse functions are powered by lessc::match(), which behaves
-	 * the same way, but takes a literal regular expression. Sometimes it is
-	 * more convenient to use match instead of creating a new function.
-	 *
-	 * Because of the format of the functions, to parse an entire string of
-	 * grammatical rules, you can chain them together using &&.
-	 *
-	 * But, if some of the rules in the chain succeed before one fails, then
-	 * then buffer position will be left at an invalid state. In order to 
-	 * avoid this, lessc::seek() is used to remember and set buffer positions.
-	 *
-	 * Before doing a chain, use $s = $this->seek() to remember the current
-	 * position into $s. Then if a chain fails, use $this->seek($s) to 
-	 * go back where we started.
-	 */
-	function parseChunk() {
-		if (empty($this->buffer)) return false;
-		$s = $this->seek();
-		
-		// setting a property
-		if ($this->keyword($key) && $this->assign() &&
-			$this->propertyValue($value) && $this->end())
-		{
-			$this->append(array('assign', $key, $value));
-			return true;
-		} else {
-			$this->seek($s);
-		}
-
-		// look for special css blocks
-		if ($this->env->parent == null && $this->literal('@', false)) {
-			$this->count--;
-
-			// a font-face block
-			if ($this->literal('@font-face') && $this->literal('{')) {
-				$b = $this->pushSpecialBlock('@font-face');
-				return true;
-			} else {
-				$this->seek($s);
-			}
-
-			// charset
-			if ($this->literal('@charset') && $this->propertyValue($value) &&
-				$this->end())
-			{
-				$this->append(array('charset', $value));
-				return true;
-			} else {
-				$this->seek($s);
-			}
-
-
-			// media
-			if ($this->literal('@media') && $this->mediaTypes($types) &&
-				$this->literal('{'))
-			{
-				$b = $this->pushSpecialBlock('@media');
-				$b->media = $types;
-				return true;
-			} else {
-				$this->seek($s);
-			}
-
-			// css animations
-			if ($this->match('(@(-[a-z]+-)?keyframes)', $m) &&
-				$this->propertyValue($value) && $this->literal('{'))
-			{
-				$b = $this->pushSpecialBlock(trim($m[0]));
-				$b->keyframes = $value;
-				return true;
-			} else {
-				$this->seek($s);
-			}
-		}
-
-		if (isset($this->env->keyframes)) {
-			if ($this->match("(to|from|[0-9]+%)", $m) && $this->literal('{')) {
-				$this->pushSpecialBlock($m[1]);
-				return true;
-			} else {
-				$this->seek($s);
-			}
-		}
-
-		// setting a variable
-		if ($this->variable($name) && $this->assign() &&
-			$this->propertyValue($value) && $this->end())
-		{
-			$this->append(array('assign', $this->vPrefix.$name, $value));
-			return true;
-		} else {
-			$this->seek($s);
-		}
-
-		if ($this->import($url, $media)) {
-			// don't check .css files
-			if (empty($media) && substr_compare($url, '.css', -4, 4) !== 0) {
-				if ($this->importDisabled) {
-					$this->append(array('raw', '/* import disabled */'));
-				} else {
-					$path = $this->findImport($url);
-					if (!is_null($path)) {
-						$this->append(array('import', $path));
-						return true;
-					}
-				}
-			}
-
-			$this->append(array('raw', '@import url("'.$url.'")'.
-				($media ? ' '.$media : '').';'));
-			return true;
-		}
-
-		// opening parametric mixin
-		if ($this->tag($tag, true) && $this->argumentDef($args) &&
-			$this->literal('{'))
-		{
-			$block = $this->pushBlock($this->fixTags(array($tag)));
-			$block->args = $args;
-			return true;
-		} else {
-			$this->seek($s);
-		}
-
-		// opening a simple block
-		if ($this->tags($tags) && $this->literal('{')) {
-			$tags = $this->fixTags($tags);
-			$this->pushBlock($tags);
-			return true;
-		} else {
-			$this->seek($s);
-		}
-
-		// closing a block
-		if ($this->literal('}')) {
-			try {
-				$block = $this->pop();
-			} catch (exception $e) {
-				$this->seek($s);
-				$this->throwParseError($e->getMessage());
-			}
-
-			$hidden = true;
-			if (!isset($block->args)) foreach ($block->tags as $tag) {
-				if ($tag{0} != $this->mPrefix) {
-					$hidden = false;
-					break;
-				}
-			}
-
-			if (!$hidden) $this->append(array('block', $block));
-			foreach ($block->tags as $tag) {
-				$this->env->children[$tag] = $block;
-			}
-
-			return true;
-		}
-
-
-		// mixin 
-		if ($this->mixinTags($tags) &&
-			($this->argumentValues($argv) || true) && $this->end())
-		{
-			$tags = $this->fixTags($tags);
-			$this->append(array('mixin', $tags, $argv));
-			return true;
-		} else {
-			$this->seek($s);
-		}
-		// spare ;
-		if ($this->literal(';')) return true;
-
-		return false; // got nothing, throw error
-	}
-
-	function fixTags($tags) {
-		// move @ tags out of variable namespace
-		foreach ($tags as &$tag) {
-			if ($tag{0} == $this->vPrefix) $tag[0] = $this->mPrefix;
-		}
-		return $tags;
-	}
+	protected $numberPrecision = null;
+
+	// set to the parser that generated the current line when compiling
+	// so we know how to create error messages
+	protected $sourceParser = null;
+	protected $sourceLoc = null;
+
+	static public $defaultValue = array("keyword", "");
+
+	static protected $nextImportId = 0; // uniquely identify imports
 
 	// attempts to find the path of an import url, returns null for css files
-	function findImport($url) {
+	protected function findImport($url) {
 		foreach ((array)$this->importDir as $dir) {
 			$full = $dir.(substr($dir, -1) != '/' ? '/' : '').$url;
 			if ($this->fileExists($file = $full.'.less') || $this->fileExists($file = $full)) {
 		return null;
 	}
 
-	function fileExists($name) {
-		// sym link workaround
-		return file_exists($name) || file_exists(realpath(preg_replace('/\w+\/\.\.\//', '', $name)));
+	protected function fileExists($name) {
+		return is_file($name);
 	}
 
-	// a list of expressions
-	function expressionList(&$exps) {
-		$values = array();	
-
-		while ($this->expression($exp)) {
-			$values[] = $exp;
+	static public function compressList($items, $delim) {
+		if (!isset($items[1]) && isset($items[0])) return $items[0];
+		else return array('list', $delim, $items);
+	}
+
+	static public function preg_quote($what) {
+		return preg_quote($what, '/');
+	}
+
+	protected function tryImport($importPath, $parentBlock, $out) {
+		if ($importPath[0] == "function" && $importPath[1] == "url") {
+			$importPath = $this->flattenList($importPath[2]);
 		}
-		
-		if (count($values) == 0) return false;
-
-		$exps = $this->compressList($values, ' ');
-		return true;
-	}
-
-	/**
-	 * Attempt to consume an expression.
-	 * @link http://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudo-code
-	 */
-	function expression(&$out) {
-		$s = $this->seek();
-		if ($this->literal('(') && ($this->inExp = $this->inParens = true) && $this->expression($exp) && $this->literal(')')) {
-			$lhs = $exp;
-		} elseif ($this->seek($s) && $this->value($val)) {
-			$lhs = $val;
-		} else {
-			$this->inParens = $this->inExp = false;
-			$this->seek($s);
-			return false;
+
+		$str = $this->coerceString($importPath);
+		if ($str === null) return false;
+
+		$url = $this->compileValue($this->lib_e($str));
+
+		// don't import if it ends in css
+		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 */");
 		}
 
-		$out = $this->expHelper($lhs, 0);
-		$this->inParens = $this->inExp = false;
-		return true;
-	}
-
-	/**
-	 * recursively parse infix equation with $lhs at precedence $minP
-	 */
-	function expHelper($lhs, $minP) {
-		$this->inExp = true;
-		$ss = $this->seek();
-
-		// if the if there was whitespace before the operator, then we require whitespace after
-		// the operator for it to be a mathematical operator.
-
-		$needWhite = false;
-		if (!$this->inParens && preg_match('/\s/', $this->buffer{$this->count - 1})) {
-			$needWhite = true;
-		}
-
-		// try to find a valid operator
-		while ($this->match(self::$operatorString.($needWhite ? '\s' : ''), $m) && self::$precedence[$m[1]] >= $minP) {
-			// get rhs
-			$s = $this->seek();
-			$p = $this->inParens;
-			if ($this->literal('(') && ($this->inParens = true) && $this->expression($exp) && $this->literal(')')) {
-				$this->inParens = $p;
-				$rhs = $exp;
-			} else {
-				$this->inParens = $p;
-				if ($this->seek($s) && $this->value($val)) {
-					$rhs = $val;
-				} else {
-					break;
-				}
-			}
-
-			// peek for next operator to see what to do with rhs
-			if ($this->peek(self::$operatorString, $next) && self::$precedence[$next[1]] > $minP) {
-				$rhs = $this->expHelper($rhs, self::$precedence[$next[1]]);
-			}
-
-			// don't evaluate yet if it is dynamic
-			if (in_array($rhs[0], self::$dtypes) || in_array($lhs[0], self::$dtypes))
-				$lhs = array('expression', $m[1], $lhs, $rhs);
-			else
-				$lhs = $this->evaluate($m[1], $lhs, $rhs);
-
-			$ss = $this->seek();
-
-			$needWhite = false;
-			if (!$this->inParens && preg_match('/\s/', $this->buffer{$this->count - 1})) {
-				$needWhite = true;
+		$this->addParsedFile($realPath);
+		$parser = $this->makeParser($realPath);
+		$root = $parser->parse(file_get_contents($realPath));
+
+		// set the parents of all the block props
+		foreach ($root->props as $prop) {
+			if ($prop[0] == "block") {
+				$prop[1]->parent = $parentBlock;
 			}
 		}
-		$this->seek($ss);
-
-		return $lhs;
-	}
-
-	// consume a list of values for a property
-	function propertyValue(&$value) {
-		$values = array();	
-		
-		$s = null;
-		while ($this->expressionList($v)) {
-			$values[] = $v;
-			$s = $this->seek();
-			if (!$this->literal(',')) break;
-		}
-
-		if ($s) $this->seek($s);
-
-		if (count($values) == 0) return false;
-
-		$value = $this->compressList($values, ', ');
-		return true;
-	}
-
-	// a single value
-	function value(&$value) {
-		// try a unit
-		if ($this->unit($value)) return true;	
-
-		// see if there is a negation
-		$s = $this->seek();
-		if ($this->literal('-', false) && $this->variable($vname)) {
-			$value = array('negative', array('variable', $this->vPrefix.$vname));
-			return true;
-		} else {
-			$this->seek($s);
-		}
-
-		// accessor 
-		// must be done before color
-		// this needs negation too
-		if ($this->accessor($a)) {
-			$a[1] = $this->fixTags($a[1]);
-			$value = $a;
-			return true;
-		}
-		
-		// color
-		if ($this->color($value)) return true;
-
-		// css function
-		// must be done after color
-		if ($this->func($value)) return true;
-
-		// string
-		if ($this->string($tmp, $d)) {
-			$value = array('string', $d.$tmp.$d);
-			return true;
-		}
-
-		// try a keyword
-		if ($this->keyword($word)) {
-			$value = array('keyword', $word);
-			return true;
-		}
-
-		// try a variable
-		if ($this->variable($vname)) {
-			$value = array('variable', $this->vPrefix.$vname);
-			return true;
-		}
-
-		// unquote string
-		if ($this->literal("~") && $this->string($value, $d)) {
-			$value = array("keyword", $value);
-			return true;
-		} else {
-			$this->seek($s);
-		}
-
-		// css hack: \0
-		if ($this->literal('\\') && $this->match('([0-9]+)', $m)) {
-			$value = array('keyword', '\\'.$m[1]);
-			return true;
-		} else {
-			$this->seek($s);
-		}
-
-		return false;
-	}
-
-	// an import statement
-	function import(&$url, &$media) {
-		$s = $this->seek();
-		if (!$this->literal('@import')) return false;
-
-		// @import "something.css" media;
-		// @import url("something.css") media;
-		// @import url(something.css) media; 
-
-		if ($this->literal('url(')) $parens = true; else $parens = false;
-
-		if (!$this->string($url)) {
-			if ($parens && $this->to(')', $url)) {
-				$parens = false; // got em
+
+		// copy mixins into scope, set their parents
+		// bring blocks from import into current block
+		// TODO: need to mark the source parser	these came from this file
+		foreach ($root->children as $childName => $child) {
+			if (isset($parentBlock->children[$childName])) {
+				$parentBlock->children[$childName] = array_merge(
+					$parentBlock->children[$childName],
+					$child);
 			} else {
-				$this->seek($s);
-				return false;
+				$parentBlock->children[$childName] = $child;
 			}
 		}
 
-		if ($parens && !$this->literal(')')) {
-			$this->seek($s);
-			return false;
+		$pi = pathinfo($realPath);
+		$dir = $pi["dirname"];
+
+		list($top, $bottom) = $this->sortProps($root->props, true);
+		$this->compileImportedProps($top, $parentBlock, $out, $parser, $dir);
+
+		return array(true, $bottom, $parser, $dir);
+	}
+
+	protected function compileImportedProps($props, $block, $out, $sourceParser, $importDir) {
+		$oldSourceParser = $this->sourceParser;
+
+		$oldImport = $this->importDir;
+
+		// TODO: this is because the importDir api is stupid
+		$this->importDir = (array)$this->importDir;
+		array_unshift($this->importDir, $importDir);
+
+		foreach ($props as $prop) {
+			$this->compileProp($prop, $block, $out);
 		}
 
-		// now the rest is media
-		return $this->to(';', $media, false, true);
+		$this->importDir = $oldImport;
+		$this->sourceParser = $oldSourceParser;
 	}
 
-	// a list of media types, very lenient
-	function mediaTypes(&$types) {
-		if ($this->to('{', $rest, true, true)) {
-			$types = trim($rest);
-			return true;
-		}
-
-		return false;
-	}
-
-	// a scoped value accessor
-	// .hello > @scope1 > @scope2['value'];
-	function accessor(&$var) {
-		$s = $this->seek();
-
-		if (!$this->tags($scope, true, '>') || !$this->literal('[')) {
-			$this->seek($s);
-			return false;
-		}
-
-		// either it is a variable or a property
-		// why is a property wrapped in quotes, who knows!
-		if ($this->variable($name)) {
-			$name = $this->vPrefix.$name;
-		} elseif ($this->literal("'") && $this->keyword($name) && $this->literal("'")) {
-			// .. $this->count is messed up if we wanted to test another access type
-		} else {
-			$this->seek($s);
-			return false;
-		}
-
-		if (!$this->literal(']')) {
-			$this->seek($s);
-			return false;
-		}
-
-		$var = array('lookup', $scope, $name);
-		return true;
-	}
-
-	// a string 
-	function string(&$string, &$d = null) {
-		$s = $this->seek();
-		if ($this->literal('"', false)) {
-			$delim = '"';
-		} elseif ($this->literal("'", false)) {
-			$delim = "'";
-		} else {
-			return false;
-		}
-
-		if (!$this->to($delim, $string)) {
-			$this->seek($s);
-			return false;
-		}
-		
-		$d = $delim;
-		return true;
-	}
-
 	/**
-	 * Consume a number and optionally a unit.
-	 * Can also consume a font shorthand if it is a simple case.
-	 * $allowed restricts the types that are matched.
-	 */
-	function unit(&$unit, $allowed = null) {
-		$simpleCase = $allowed == null;
-		if (!$allowed) $allowed = self::$units;
-
-		if ($this->match('(-?[0-9]*(\.)?[0-9]+)('.implode('|', $allowed).')?', $m, !$simpleCase)) {
-			if (!isset($m[3])) $m[3] = 'number';
-			$unit = array($m[3], $m[1]);
-
-			// check for size/height font unit.. should this even be here?
-			if ($simpleCase) {
-				$s = $this->seek();
-				if (!$this->inExp && $this->literal('/', false) && $this->unit($right, self::$units)) {
-					$unit = array('keyword', $this->compileValue($unit).'/'.$this->compileValue($right));
-				} else {
-					// get rid of whitespace
-					$this->seek($s);
-					$this->match('', $_);
-				}
-			}
-
-			return true;
-		}
-
-		return false;
-	}
-
-	// a # color
-	function color(&$out) {
-		$color = array('color');
-
-		if ($this->match('(#([0-9a-f]{6})|#([0-9a-f]{3}))', $m)) {
-			if (isset($m[3])) {
-				$num = $m[3];
-				$width = 16;
-			} else {
-				$num = $m[2];
-				$width = 256;
-			}
-
-			$num = hexdec($num);
-			foreach (array(3,2,1) as $i) {
-				$t = $num % $width;
-				$num /= $width;
-
-				$color[$i] = $t * (256/$width) + $t * floor(16/$width);
-			}
-			
-			$out = $color;
-			return true;
-		} 
-
-		return false;
-	}
-
-	// consume a list of property values delimited by ; and wrapped in ()
-	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
-	function argumentDef(&$args, $delim = ',') {
-		$s = $this->seek();
-		if (!$this->literal('(')) return false;
-
-		$values = array();
-		while ($this->variable($vname)) {
-			$arg = array($vname);
-			if ($this->assign() && $this->expressionList($value)) {
-				$arg[] = $value;
-				// let the : slide if there is no value
-			}
-
-			$values[] = $arg;
-			if (!$this->literal($delim)) break;
-		}
-
-		if (!$this->literal(')')) {
-			$this->seek($s);
-			return false;
-		}
-
-		$args = $values;
-		return true;
-	}
-
-	// consume a list of tags
-	// this accepts a hanging delimiter
-	function tags(&$tags, $simple = false, $delim = ',') {
-		$tags = array();
-		while ($this->tag($tt, $simple)) {
-			$tags[] = $tt;
-			if (!$this->literal($delim)) break;
-		}
-		if (count($tags) == 0) return false;
-
-		return true;
-	}
-
-	// list of tags of specifying mixin path
-	// optionally separated by > (lazy, accepts extra >)
-	function mixinTags(&$tags) {
-		$s = $this->seek();
-		$tags = array();
-		while ($this->tag($tt, true)) {
-			$tags[] = $tt;
-			$this->literal(">");
-		}
-
-		if (count($tags) == 0) return false;
-
-		return true;
-	}
-
-	// a bracketed value (contained within in a tag definition)
-	function tagBracket(&$value) {
-		$s = $this->seek();
-		if ($this->literal('[') && $this->to(']', $c, true) && $this->literal(']', false)) {
-			$value = '['.$c.']';
-			// whitespace?
-			if ($this->match('', $_)) $value .= $_[0];
-
-			// escape parent selector
-			$value = str_replace($this->parentSelector, "&&", $value);
-			return true;
-		}
-
-		$this->seek($s);
-		return false;
-	}
-
-	// a single tag
-	function tag(&$tag, $simple = false) {
-		if ($simple)
-			$chars = '^,:;{}\][>\(\) ';
-		else
-			$chars = '^,;{}[';
-
-		$tag = '';
-		while ($this->tagBracket($first)) $tag .= $first;
-		while ($this->match('(['.$chars.'0-9]['.$chars.']*)', $m)) {
-			$tag .= $m[1];
-			if ($simple) break;
-
-			while ($this->tagBracket($brack)) $tag .= $brack;
-		}
-		$tag = trim($tag);
-		if ($tag == '') return false;
-
-		return true;
-	}
-
-	// a css function
-	function func(&$func) {
-		$s = $this->seek();
-
-		if ($this->match('(%|[\w\-_][\w\-_:\.]*)', $m) && $this->literal('(')) {
-			$fname = $m[1];
-			if ($fname == 'url') {
-				$this->to(')', $content, true);
-				$args = array('string', $content);
-			} else {
-				$args = array();
-				while (true) {
-					$ss = $this->seek();
-					if ($this->keyword($name) && $this->literal('=') && $this->expressionList($value)) {
-						$args[] = array('list', '=', array(array('keyword', $name), $value));
-					} else {
-						$this->seek($ss);
-						if ($this->expressionList($value)) {
-							$args[] = $value;
-						}
-					}
-
-					if (!$this->literal(',')) break;
-				}
-				$args = array('list', ',', $args);
-			}
-
-			if ($this->literal(')')) {
-				$func = array('function', $fname, $args);
-				return true;
-			}
-		}
-
-		$this->seek($s);
-		return false;
-	}
-
-	// consume a less variable
-	function variable(&$name) {
-		$s = $this->seek();
-		if ($this->literal($this->vPrefix, false) && $this->keyword($name)) {
-			return true;	
-		}
-
-		return false;
-	}
-
-	/**
-	 * Consume an assignment operator
-	 * Can optionally take a name that will be set to the current property name
-	 */
-	function assign($name = null) {
-		if ($name) $this->currentProperty = $name;
-		return $this->literal(':') || $this->literal('=');
-	}
-
-	// consume a keyword
-	function keyword(&$word) {
-		if ($this->match('([\w_\-\*!"][\w\-_"]*)', $m)) {
-			$word = $m[1];
-			return true;
-		}
-		return false;
-	}
-
-	// consume an end of statement delimiter
-	function end() {
-		if ($this->literal(';'))
-			return true;
-		elseif ($this->count == strlen($this->buffer) || $this->buffer{$this->count} == '}') {
-			// if there is end of file or a closing block next then we don't need a ;
-			return true;
-		}
-		return false;
-	}
-
-	function compressList($items, $delim) {
-		if (count($items) == 1) return $items[0];	
-		else return array('list', $delim, $items);
-	}
-
-	/**
-	 * Recursively compiles a block. 
-	 * @param $block the block
-	 * @param $parentTags the tags of the block that contained this one
+	 * Recursively compiles a block.
 	 *
-	 * A block is analogous to a CSS block in most cases. A single less document
+	 * A block is analogous to a CSS block in most cases. A single LESS document
 	 * is encapsulated in a block when parsed, but it does not have parent tags
 	 * so all of it's children appear on the root level when compiled.
 	 *
 	 * to be taken, eg. write a property, set a variable, mixin a block.
 	 *
 	 * The children of a block are just all the blocks that are defined within.
+	 * This is used to look up mixins when performing a mixin.
 	 *
 	 * Compiling the block involves pushing a fresh environment on the stack,
 	 * and iterating through the props, compiling each one.
 	 * See lessc::compileProp()
 	 *
 	 */
-	function compileBlock($block, $parent_tags = null) {
-		$isRoot = $parent_tags == null && $block->tags == null;
-
-		$indent = str_repeat($this->indentChar, $this->indentLevel);
-
-		if (!empty($block->no_multiply)) {
-			$special_block = true;
-			$this->indentLevel++;
-			$tags = array();
+	protected function compileBlock($block) {
+		switch ($block->type) {
+		case "root":
+			$this->compileRoot($block);
+			break;
+		case null:
+			$this->compileCSSBlock($block);
+			break;
+		case "media":
+			$this->compileMedia($block);
+			break;
+		case "directive":
+			$name = "@" . $block->name;
+			if (!empty($block->value)) {
+				$name .= " " . $this->compileValue($this->reduce($block->value));
+			}
+
+			$this->compileNestedBlock($block, array($name));
+			break;
+		default:
+			$this->throwError("unknown block type: $block->type\n");
+		}
+	}
+
+	protected function compileCSSBlock($block) {
+		$env = $this->pushEnv();
+
+		$selectors = $this->compileSelectors($block->tags);
+		$env->selectors = $this->multiplySelectors($selectors);
+		$out = $this->makeOutputBlock(null, $env->selectors);
+
+		$this->scope->children[] = $out;
+		$this->compileProps($block, $out);
+
+		$block->scope = $env; // mixins carry scope with them!
+		$this->popEnv();
+	}
+
+	protected function compileMedia($media) {
+		$env = $this->pushEnv($media);
+		$parentScope = $this->mediaParent($this->scope);
+
+		$query = $this->compileMediaQuery($this->multiplyMedia($env));
+
+		$this->scope = $this->makeOutputBlock($media->type, array($query));
+		$parentScope->children[] = $this->scope;
+