[
  {
    "path": ".State",
    "content": "finalized\n"
  },
  {
    "path": ".gitignore",
    "content": "/vendor/\n/composer.lock\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: php\n\nbranches:\n  only:\n    - staging\n    - trying\n    - master\n\nmatrix:\n  include:\n    - php: 5.5\n    - php: 5.6\n    - php: 7.0\n    - php: 7.1\n      env:\n        - ENABLE_XDEBUG=true\n    - php: 7.1\n      env:\n        - ENABLE_DEVTOOLS=true\n    - php: nightly\n  allow_failures:\n    - php: nightly\n  fast_finish: true\n\nos:\n  - linux\n\nnotifications:\n  irc: \"chat.freenode.net#hoaproject\"\n\nsudo: false\n\nenv:\n  global:\n    - secure: \"AAAAB3NzaC1yc2EAAAADAQABAAAAgQDzZax7/VFMmTnePlw4PQmD7pVaJQDbLaMXfIIuV/h51m0g8dYfWiGytsblv+/tW37b3TKaGVLMP3vL9jGU73V4P64Ytafj0UV3UKSzHR4atrAPjsCjsFMzlIvKLCZf2FmADRspv/pAg1loQWRzXAiZ9pqCSTxx32x20uLAJmLucQ==\"\n\ncache:\n  directories:\n    - vendor/\n\nbefore_script:\n  - export PATH=\"$PATH:$HOME/.composer/vendor/bin\"\n  - if [[ ! $ENABLE_XDEBUG ]]; then\n      phpenv config-rm xdebug.ini || echo \"ext-xdebug is not available, cannot remove it.\";\n    fi\n\nscript:\n  - composer install\n  - vendor/bin/hoa test:run\n  - if [[ $ENABLE_DEVTOOLS ]]; then\n      composer global require friendsofphp/php-cs-fixer;\n      vendor/bin/hoa devtools:cs --diff --dry-run .;\n    fi\n"
  },
  {
    "path": "Bin/Assert.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Bin;\n\nuse Hoa\\Console;\nuse Hoa\\Ruler;\n\n/**\n * Class \\Hoa\\Ruler\\Bin\\Assert.\n *\n * Assert rules.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Assert extends Console\\Dispatcher\\Kit\n{\n    /**\n     * Options description.\n     *\n     * @var array\n     */\n    protected $options = [\n        ['help', Console\\GetOption::NO_ARGUMENT, 'h'],\n        ['help', Console\\GetOption::NO_ARGUMENT, '?']\n    ];\n\n\n\n    /**\n     * The entry method.\n     *\n     * @return  int\n     */\n    public function main()\n    {\n        $ruler   = new Ruler();\n        $context = new Ruler\\Context();\n\n        while (false !== $c = $this->getOption($v)) {\n            switch ($c) {\n                case '__ambiguous':\n                    $context[$v['option']] = $v['value'];\n\n                    break;\n\n                case 'h':\n                case '?':\n                    return $this->usage();\n            }\n        }\n\n        $this->parser->listInputs($rule);\n\n        if (empty($rule)) {\n            return $this->usage();\n        }\n\n        return (int) (!$ruler->assert($rule, $context));\n    }\n\n    /**\n     * The command usage.\n     *\n     * @return  int\n     */\n    public function usage()\n    {\n        echo\n            'Usage   : ruler:assert <options> rule', \"\\n\",\n            'Options :', \"\\n\",\n            $this->makeUsageOptionsList([\n                'help' => 'This help.'\n            ]), \"\\n\",\n            'Example : -x=2 -y=6 \\'x in [1, 2, 4] and x < y\\'.', \"\\n\",\n            'See $? to see the result (0 for true, > 0 for false).', \"\\n\";\n\n        return;\n    }\n}\n\n__halt_compiler();\nAssert rules.\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# 2.17.05.16\n\n  * Asserter: Can visit array-like dimensions. (Arne Groskurth, 2017-05-16T09:42:29+02:00)\n\n# 2.17.04.26\n\n  * Grammar: Logical operators are left-associative. (Ivan Enderlin, 2017-03-24T14:39:19+01:00)\n  * CS: Fix copyright. (Ivan Enderlin, 2017-03-13T14:59:05+01:00)\n  * Test: Support PHP 5.x syntax. (Ivan Enderlin, 2017-03-13T14:44:33+01:00)\n  * CI: Set up Travis. (Ivan Enderlin, 2017-03-13T14:16:45+01:00)\n\n# 2.17.01.13\n\n  * Quality: Happy new year! (Alexis von Glasow, 2017-01-09T21:37:11+01:00)\n  * Test: Add the `Decorrelated` interface. (Ivan Enderlin, 2016-10-25T07:57:09+02:00)\n\n# 2.16.10.24\n\n  * Documentation: New `README.md` file. (Ivan Enderlin, 2016-10-14T23:10:14+02:00)\n  * Grammar: Chain dimensions on function. (Ivan Enderlin, 2016-10-14T08:37:12+02:00)\n  * Documentation: Update `support` properties. (Ivan Enderlin, 2016-10-11T11:54:40+02:00)\n  * Test: Update according to previous commit. (Ivan Enderlin, 2016-09-09T16:55:21+02:00)\n  * Disassembly: Always add parenthesis around operators. (Ivan Enderlin, 2016-01-16T08:09:28+01:00)\n  * Test: Add test cases for `…uler\\Visitor\\Asserter`. (Ivan Enderlin, 2016-09-09T08:04:49+02:00)\n\n# 2.16.09.07\n\n  * Test: Write `…\\Ruler\\Visitor\\Asserter` test suite. (Ivan Enderlin, 2016-09-07T15:06:46+02:00)\n  * Asserter: Fix an exception message. (Ivan Enderlin, 2016-09-07T15:04:19+02:00)\n  * Test: Write `…ler\\Visitor\\Interpreter` test suite. (Ivan Enderlin, 2016-09-06T08:54:00+02:00)\n  * Quality: Rename an internal variable. (Ivan Enderlin, 2016-09-06T08:02:30+02:00)\n  * Test: Parameterized cases are usually protected. (Ivan Enderlin, 2016-09-06T08:00:43+02:00)\n  * Disassembly: Escape the escaping symbol. (Ivan Enderlin, 2016-09-05T17:23:26+02:00)\n  * Test: Write `…ler\\Visitor\\Disassembly` test suite. (Ivan Enderlin, 2016-09-05T17:22:16+02:00)\n  * Test: Write `…\\Ruler\\Visitor\\Compiler` test suite. (Ivan Enderlin, 2016-09-05T17:15:41+02:00)\n  * Test: Write `Hoa\\Ruler\\Ruler` test suite. (Ivan Enderlin, 2016-09-05T08:56:36+02:00)\n  * Ruler: Rename a namespace alias. (Ivan Enderlin, 2016-09-05T08:19:19+02:00)\n  * Ruler: Remove the `interprete` method. (Ivan Enderlin, 2016-09-05T08:14:21+02:00)\n  * Test: Update name for `Issue`. (Ivan Enderlin, 2016-09-05T08:10:18+02:00)\n  * Quality: Run `hoa devtools:cs`. (Ivan Enderlin, 2016-09-05T08:09:13+02:00)\n  * Test: Write `…r\\Exception\\Interpreter` test suite. (Ivan Enderlin, 2016-09-05T08:08:20+02:00)\n  * Test: Write `…uler\\Exception\\Asserter` test suite. (Ivan Enderlin, 2016-09-05T08:07:31+02:00)\n  * Test: Write `…ler\\Exception\\Exception` test suite. (Ivan Enderlin, 2016-09-05T08:05:36+02:00)\n  * Test: Format to standard vocabulary. (Ivan Enderlin, 2016-09-05T08:04:39+02:00)\n  * Test: Rename `CUT` to `SUT`. (Ivan Enderlin, 2016-09-05T08:02:49+02:00)\n  * Test: Move `Documentation` as integration suite. (Ivan Enderlin, 2016-09-05T08:01:25+02:00)\n  * Test: Write `Hoa\\Ruler\\Model\\Model` test suite. (Ivan Enderlin, 2016-09-02T17:40:35+02:00)\n  * Visitor: If the model is empty, compile to `''`. (Ivan Enderlin, 2016-09-02T17:34:27+02:00)\n  * Test: Ensure recursivity applies onto array items. (Ivan Enderlin, 2016-09-02T17:19:51+02:00)\n  * Test: Write `Hoa\\Ruler\\Model\\Operator` test suite. (Ivan Enderlin, 2016-09-02T17:09:31+02:00)\n  * Model: Use the public `getName` method. (Ivan Enderlin, 2016-09-02T17:08:48+02:00)\n  * Model: Move set auto laziness to `setName`. (Ivan Enderlin, 2016-09-02T17:08:02+02:00)\n  * Test: Move `…erator` as unit to integration suite. (Ivan Enderlin, 2016-09-02T07:49:40+02:00)\n  * Documentation: Fix API documentation. (Ivan Enderlin, 2016-09-02T07:47:03+02:00)\n  * Test: Write `…Ruler\\Model\\Bag\\Context` test suite. (Ivan Enderlin, 2016-09-02T07:46:09+02:00)\n  * Quality: Remove an unnecessary namespace alias. (Ivan Enderlin, 2016-08-30T17:03:58+02:00)\n  * Test: Write `…er\\Model\\Bag\\RulerArray` test suite. (Ivan Enderlin, 2016-08-30T17:03:38+02:00)\n  * Test: Write `…\\Ruler\\Model\\Bag\\Scalar` test suite. (Ivan Enderlin, 2016-08-29T16:29:37+02:00)\n  * Documentation: Fix API documentation. (Ivan Enderlin, 2016-08-29T16:29:16+02:00)\n  * Test: Write `Hoa\\Ruler\\Model\\Bag` test suite. (Ivan Enderlin, 2016-08-29T15:51:28+02:00)\n  * Test: Use the `::class` constant. (Ivan Enderlin, 2016-08-29T15:49:09+02:00)\n\n# 2.16.04.06\n\n  * Asserter: Fix a wrong namespace access. (jroenf, 2016-04-06T09:09:43+02:00)\n\n# 2.16.03.15\n\n  * Composer: `hoa/protocol` is explicitly required. (Ivan Enderlin, 2016-01-18T22:14:18+01:00)\n  * Grammar: Update copyright. (Ivan Enderlin, 2016-01-17T14:22:07+01:00)\n\n# 2.16.01.15\n\n  * Composer: Remove a useless dependency. (Ivan Enderlin, 2016-01-14T22:42:15+01:00)\n\n# 2.16.01.14\n\n  * Composer: New stable libraries. (Ivan Enderlin, 2016-01-14T22:13:39+01:00)\n\n# 2.16.01.11\n\n  * Quality: Drop PHP5.4. (Ivan Enderlin, 2016-01-11T09:15:26+01:00)\n  * Quality: Run devtools:cs. (Ivan Enderlin, 2016-01-09T09:08:44+01:00)\n  * Core: Remove `Hoa\\Core`. (Ivan Enderlin, 2016-01-09T08:24:06+01:00)\n  * Consistency: Use `Hoa\\Consistency`. (Ivan Enderlin, 2015-12-08T21:50:12+01:00)\n  * Exception: Use `Hoa\\Exception`. (Ivan Enderlin, 2015-11-20T13:10:38+01:00)\n\n# 1.15.11.09\n\n  * Fix CS. (Ivan Enderlin, 2015-09-23T11:31:44+02:00)\n\n# 1.15.09.22\n\n  * Fix bad evaluation of `null` as an array key in the asserter. (Grummfy, 2015-09-22T15:47:23+02:00)\n\n# 1.15.09.08\n\n  * Add a `.gitignore` file. (Stéphane HULARD, 2015-08-03T11:45:52+02:00)\n  * Add the `matches` operator. (Kévin Gomez, 2015-07-18T19:44:24+02:00)\n\n# 1.15.07.28\n\n  * Auto-box the expression to its bag representation. (Ivan Enderlin, 2015-07-13T11:37:00+02:00)\n  * Fix a typo. (Ivan Enderlin, 2015-06-27T16:08:06+02:00)\n\n# 1.15.05.29\n\n  * Move to PSR-1 and PSR-2. (Ivan Enderlin, 2015-04-20T10:21:13+02:00)\n\n# 1.15.04.13\n\n  * Add the English documentation. (Ivan Enderlin, 2015-03-19T10:32:47+01:00)\n  * Add the French documentation. (Ivan Enderlin, 2015-03-19T10:29:59+01:00)\n  * Add the `CHANGELOG.md` file. (Ivan Enderlin, 2015-02-16T14:08:39+01:00)\n  * Fix CS and API documentation. (Ivan Enderlin, 2015-02-06T10:37:23+01:00)\n  * Add lazy operator support. (Alexis von Glasow, 2014-12-15T23:42:34+01:00)\n  * Add tests for the dynamic callable. (Ivan Enderlin, 2015-02-05T17:13:12+01:00)\n\n# 1.15.02.05\n\n  * Sandbox function calls in the context. (Ivan Enderlin, 2015-02-05T16:50:13+01:00)\n  * Add tests for the context. (Ivan Enderlin, 2015-02-05T16:49:30+01:00)\n\n# 1.15.02.02\n\n  * s/interprete/interpret/ (Ivan Enderlin, 2015-02-02T11:31:29+01:00)\n  * `Ruler::interprete` is an alias to `Ruler::interpret` (simkimsia, 2015-01-16T22:22:18+08:00)\n  * Improve type-hints in `Visitor\\Asserter` (Alexis von Glasow, 2015-01-15T13:34:30+01:00)\n  * Happy new year! (Ivan Enderlin, 2015-01-05T14:47:59+01:00)\n\n# 1.14.12.10\n\n  * Move to PSR-4. (Ivan Enderlin, 2014-12-09T18:45:18+01:00)\n\n# 1.14.12.09\n\n  * Fix a bug in the `Visitor\\Compiler` when function has no argument (Catalin Criste, 2014-12-09T18:25:25+01:00)\n  * Format namespace. (Ivan Enderlin, 2014-12-08T14:04:08+01:00)\n  * Require `hoa/test`. (Alexis von Glasow, 2014-11-26T13:21:41+01:00)\n  * `Hoa\\Visitor` has been finalized. (Ivan Enderlin, 2014-11-15T22:28:07+01:00)\n\n# 1.14.11.10\n\n  * Avoid collisions with user-defined operators… (Ivan Enderlin, 2014-11-10T15:43:04+01:00)\n\n# 1.14.11.09\n\n  * Split the visitor into several methods (Stéphane PY, 2014-11-07T09:29:55+01:00)\n  * Add tests for the documentation. (Ivan Enderlin, 2014-09-26T09:23:44+02:00)\n\n# 1.14.09.25\n\n  * Fix Fatal error. (Stéphane PY, 2014-09-25T12:22:18+02:00)\n  * Add `branch-alias` (Stéphane PY, 2014-09-23T16:06:06+02:00)\n  * `Hoa\\Core` was missing (Ivan Enderlin, 2014-09-23T15:58:55+02:00)\n\n# 1.14.09.23\n\n  * First tag :-). (Ivan Enderlin, 2014-09-23T15:41:11+02:00)\n  * Finalized! (Ivan Enderlin, 2014-09-23T15:37:04+02:00)\n  * Remove `from`/`import` and update to PHP5.4. (Ivan Enderlin, 2014-09-23T15:32:36+02:00)\n  * Declare array with […] and not (…). (Ivan Enderlin, 2014-09-23T14:58:18+02:00)\n\n# 0.14.09.17\n\n  * Drop PHP5.3. (Ivan Enderlin, 2014-09-17T17:13:16+02:00)\n  * Add the installation section. (Ivan Enderlin, 2014-09-17T17:13:05+02:00)\n\n(first snapshot)\n"
  },
  {
    "path": "Context.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler;\n\n/**\n * Class \\Hoa\\Ruler\\Context.\n *\n * Context of a rule, it contains data to instanciate the rule.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Context implements \\ArrayAccess\n{\n    /**\n     * Ruler.\n     *\n     * @var \\Hoa\\Ruler\n     */\n    protected $_ruler = null;\n\n    /**\n     * Data.\n     *\n     * @var array\n     */\n    protected $_data  = [];\n\n\n\n    /**\n     * Constructor.\n     *\n     * @param   array  $data    Initial data.\n     */\n    public function __construct(array $data = [])\n    {\n        $this->_data = $data;\n\n        return;\n    }\n\n    /**\n     * Set a data.\n     *\n     * @param   string  $id       ID.\n     * @param   mixed   $value    Value.\n     * @return  void\n     */\n    public function offsetSet($id, $value)\n    {\n        $this->_data[$id] = $value;\n\n        return;\n    }\n\n    /**\n     * Get a data.\n     *\n     * @param   string  $id    ID.\n     * @return  mixed\n     * @throws  \\Hoa\\Ruler\\Exception\n     */\n    public function offsetGet($id)\n    {\n        if (false === array_key_exists($id, $this->_data)) {\n            throw new Exception(\n                'Identifier %s does not exist in the context.',\n                0,\n                $id\n            );\n        }\n\n        $value = $this->_data[$id];\n\n        if ($value instanceof DynamicCallable) {\n            return $value($this);\n        }\n\n        if (true === is_callable($value)) {\n            if (true  === is_string($value) &&\n                false === in_array(strtolower($value), get_defined_functions()['user'])) {\n                return $value;\n            }\n\n            $value = $this->_data[$id] = $value($this);\n        }\n\n        return $value;\n    }\n\n    /**\n     * Check if a data exists.\n     *\n     * @return  bool\n     */\n    public function offsetExists($id)\n    {\n        return true === array_key_exists($id, $this->_data);\n    }\n\n    /**\n     * Unset a data.\n     *\n     * @param   string  $id    ID.\n     * @return  void\n     */\n    public function offsetUnset($id)\n    {\n        unset($this->_data[$id]);\n\n        return;\n    }\n\n    /**\n     * Get a data as context property\n     *\n     * @param   string $name\n     * @return  mixed\n     * @throws  \\Hoa\\Ruler\\Exception\n     */\n    public function __get($name)\n    {\n        return $this->offsetGet($name);\n    }\n\n    /**\n     * Set a data as context property\n     *\n     * @param   string  $id       ID.\n     * @param   mixed   $value    Value.\n     * @return  void\n     */\n    public function __set($id, $value)\n    {\n        $this->offsetSet($id, $value);\n    }\n}\n"
  },
  {
    "path": "Documentation/En/Index.xyl",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<overlay xmlns=\"http://hoa-project.net/xyl/xylophone\">\n<yield id=\"chapter\">\n\n  <p><strong>Business rules</strong> (like “<q>all the customers that have\n  reached 100€ in one time receive a voucher of 10% on the next purchase</q>”)\n  are most of the time defined <strong>outside</strong> the application. They\n  are even often written in a <strong>different language</strong> than the used\n  programming language. The <code>Hoa\\Ruler</code> library provides an engine\n  allowing to simply execute business rules while being\n  <strong>efficient</strong> and very <strong>extensible</strong>.</p>\n\n  <h2 id=\"Table_of_contents\">Table of contents</h2>\n\n  <tableofcontents id=\"main-toc\" />\n\n  <h2 id=\"Introduction\" for=\"main-toc\">Introduction</h2>\n\n  <p>The business logic is very different from the Computer logic. “<q>All the\n  customers that have reached 100€ in one time receive a voucher of 10% on the\n  next purchase</q>”. This rule allows to access to certain parts of the program\n  if <strong>valids</strong>. However, it can <strong>change</strong> at any\n  moment. Most of the time, in a team, it is not the role of the developer to\n  implement this rule. It will probably come from a <strong>business rules\n  repository</strong>, which have been written by other persons, either\n  <strong>manually</strong> or thanks to a <strong>third-party program</strong>.\n  This implies that the language used to express a rule is not the language used\n  to develop the program. An even more obvious example is the use of a rule to\n  filter elements: An element is accepted if “<q>its group is <em>customer</em>\n  or <em>guest</em> and its number of points is greater than 30</q>”. This rule\n  can be written by a user via a command line interface to filter results from a\n  database or logs.</p>\n  <p>This is important to understand that rules must be written in a\n  <strong>dedicated language</strong>. Nevertheless, the way we use rules is\n  very <strong>vast</strong> and <strong>unpredictable</strong>. This is why it\n  is primordial to have flexible and <strong>extensible</strong> rules in the\n  syntax. For instance, it should be allowed to add operators and functions:\n  “<q>All the customers from the hotel with a Gold pass will receive a voucher\n  of 10%”</q>. The “Gold pass” can be an operator or a function specific to the\n  current business.</p>\n  <p>The language the <code>Hoa\\Ruler</code> library uses to describe rules\n  respects these constraints of extensibility. The rules will not be close to\n  the human language but they will stay <strong>natural</strong> when reading.\n  If we take the example of the “<q>its group is <em>customer</em> or\n  <em>guest</em> and its number of points is greater than 30</q>”, it will be\n  written: <code><em>group</em> in [\"customer\", \"guest\"] and <em>points</em> >\n  30</code>. The <code><em>group</em></code> and <code><em>points</em></code>\n  elements are variables of the rule. Their values will be defined in a\n  context.</p>\n  <p>From a more formal point of view, a rule is a predicate, it means that its\n  result is always a boolean: <code class=\"language-php\">true</code> or\n  <code class=\"language-php\">false</code>. Because these rules are likely to be\n  manipulated (modified) and executed, the <code>Hoa\\Ruler</code> library\n  provides several tools to work efficiently with these constraints, presented\n  in the following sections.</p>\n\n  <h2 id=\"Global_workflow\" for=\"main-toc\">Global workflow</h2>\n\n  <p>The global workflow of the <code>Hoa\\Ruler</code> library follows\n  3 steps:</p>\n  <ol>\n    <li>Defining a <strong>rule</strong>,</li>\n    <li>Defining a <strong>context</strong>,</li>\n    <li>Use of an <strong>asserter</strong> for the execution.</li>\n  </ol>\n  <p>The <strong>rule</strong> is a string matching a specific syntax, which is\n  described by the grammar of the language defined by the <code>Hoa\\Ruler</code>\n  library (detailed hereinafter). This rule contains variables whose values are\n  defined by the <strong>context</strong>. The context can contain scalar\n  values, arrays or even functions or objects. Finally, the\n  <strong>asserter</strong> associates the context to the rule in order to\n  execute it and to obtain a result. We remind you about the result which is\n  necessarily a boolean. This is therefore a predicate.</p>\n  <p>The context is represented by the <code>Hoa\\Ruler\\Context</code> class. The\n  asserter is represented by the <code>Hoa\\Ruler\\Visitor\\Asserter</code> class.\n  We can use the <code>Hoa\\Ruler\\Ruler::assert</code> method to ease its usage.\n  Thus:</p>\n  <pre><code class=\"language-php\">$ruler = new Hoa\\Ruler\\Ruler();\n\n// 1. Write a rule.\n$rule  = 'group in [\"customer\", \"guest\"] and points > 30';\n\n// 2. Create a context.\n$context           = new Hoa\\Ruler\\Context();\n$context['group']  = 'customer';\n$context['points'] = function () {\n    return 42;\n};\n\n// 3. Assert!\nvar_dump(\n    $ruler->assert($rule, $context)\n);\n\n/**\n * Will output:\n *     bool(true)\n */</code></pre>\n  <p>The rule is defined in the <code class=\"language-php\">$rule</code>\n  variable. The context, as far as it is concerned, is defined in the\n  <code class=\"language-php\">$context</code> variable. It contains 2 variables:\n  <code>group</code> and <code>point</code>, respectively with the\n  <code class=\"language-php\">'customer'</code> and\n  <code class=\"language-php\">42</code> (returned by a function) values. Finally,\n  the last step uses the <code>Hoa\\Ruler\\Ruler::assert</code> method to execute\n  the <code class=\"language-php\">$rule</code> rule with the\n  <code class=\"language-php\">$context</code> context (this latter is optional).\n  The result is <code class=\"language-php\">true</code> because\n  <code>group</code> belongs to the list <code>customer</code> or\n  <code>guest</code>, and <code>point</code> is greater than 30. Change the\n  values in the context or the rule to observe a different result.</p>\n  <p>The following sections detail the behavior of each step but the classical\n  usage remains as simple as that!</p>\n\n  <h3 id=\"Grammar\" for=\"main-toc\">Grammar (through examples)</h3>\n\n  <p>The grammar of the rules language is described in\n  <a href=\"@central_resource:path=Library/Ruler/Grammar.pp\">the\n  <code>hoa://Library/Ruler/Grammar.pp</code> file</a>. This grammar is\n  expressed with the PP language. To get more information, please see\n  <a href=\"@hack:chapter=Compiler\">the <code>Hoa\\Compiler</code> library</a>.\n  We clarify that the language supports Unicode. We are not going to explain the\n  language whilst the grammar provides all the necessary\n  <strong>details</strong>. Nevertheless, we are going to give several syntax\n  examples.</p>\n\n  <table>\n    <caption>Language of <code>Hoa\\Ruler</code> through examples.</caption>\n    <thead>\n      <tr>\n        <th>syntax</th>\n        <th>semantics</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td><code>'foo'</code>, <code>\"foo\"</code>, <code>'f\\'oo'</code></td>\n        <td>strings</td>\n      </tr>\n      <tr>\n        <td><code>true</code>, <code>false</code>, <code>null</code></td>\n        <td>pre-defined constants</td>\n      </tr>\n      <tr>\n        <td><code>4.2</code></td>\n        <td>a real</td>\n      </tr>\n      <tr>\n        <td><code>42</code></td>\n        <td>an integer</td>\n      </tr>\n      <tr>\n        <td><code>['foo', true, 4.2, 42]</code></td>\n        <td>an array (heterogeneous)</td>\n      </tr>\n      <tr>\n        <td><code>sum(1, 2, 3)</code></td>\n        <td>a call to the <code>sum</code> function with 3 arguments</td>\n      </tr>\n      <tr>\n        <td><code><em>points</em></code></td>\n        <td>a variable</td>\n      </tr>\n      <tr>\n        <td><code><em>points</em>['x']</code></td>\n        <td>an array access</td>\n      </tr>\n      <tr>\n        <td><code><em>line</em>.pointA</code></td>\n        <td>an object access (attribute)</td>\n      </tr>\n      <tr>\n        <td><code><em>line</em>.length()</code></td>\n        <td>a call to a method</td>\n      </tr>\n      <tr>\n        <td><code>and</code>, <code>or</code>, <code>xor</code>, <code>not</code></td>\n        <td>logical operators</td>\n      </tr>\n      <tr>\n        <td><code>=</code>, <code>!=</code>, <code>></code>, <code>&amp;lt;</code>,\n            <code>>=</code>, <code>&amp;lt;=</code></td>\n        <td>comparison operators</td>\n      </tr>\n      <tr>\n        <td><code>is</code>, <code>in</code></td>\n        <td>membership operators</td>\n      </tr>\n    </tbody>\n  </table>\n\n  <p>Of course, these examples represent atomic parts of the grammar that we can\n  combine. Thus:\n  <code><em>userA</em>.allows(<em>groups</em>[<em>groupId</em>][<em>userB</em>])</code>\n  is valid. Just like <code>f(<em>user</em>, <em>points</em> > 7 and\n  <em>points</em> &amp;lt; 42)</code> which is also valid.</p>\n  <p>In actual fact, functions, comparison operators and membership operators\n  are not defined by the grammar but by the asserter (detailed hereinafter).\n  <code>Hoa\\Ruler</code> does not make any difference between an operator and a\n  function. <strong>Operators are considered as functions</strong>; an operator\n  only being a function with an arity of 1 or 2. Thus, we can write <code>2 =\n  2</code> or <code>=(2, 2)</code>, this will strictly produce the same result.\n  Just like the name of functions that is not defined in the grammar, the name\n  of operators is neither defined by the grammar, excepted for the logical\n  operators that have a particular processing (because of the operator\n  precedence). The immediate result is that we can <strong>create</strong> our\n  own operators or functions. We can imagine <code><em>a</em> ∈\n  <em>A</em></code>, <code>√(42)</code> or even <code><em>userA</em> allows\n  <em>userB</em></code> being valid expressions.</p>\n\n  <h3 id=\"Context\" for=\"main-toc\">Context</h3>\n\n  <p>The context defines values of variables present in rules. These values can\n  be of kind:</p>\n  <ul>\n    <li><strong>Constants</strong>, like <code class=\"language-php\">42</code> or\n    <code class=\"language-php\">'foo'</code> which are scalars, or\n    <code class=\"language-php\">[1, 1, 2, 3, 5]</code> or an object which are\n    structured types,</li>\n    <li><strong>Computed values</strong>, it means returned by a function or a\n    method.</li>\n  </ul>\n  <p>By default, the computed values are computed only once. Indeed, they are\n  stored in a cache for performance reasons. If we would like to recompute them\n  at each read, an encapsulation in an object of kind\n  <code>Hoa\\Ruler\\DynamicCallable</code> is required.</p>\n  <p>Before detailing this part, let's present the context. A context is an\n  instance of the <code>Hoa\\Ruler\\Context</code> class which implements\n  <a href=\"http://php.net/arrayaccess\">the <code>ArrayAccess</code>\n  interface</a>. Thus, we use the context like an array:</p>\n  <pre><code class=\"language-php\">$context        = new Hoa\\Ruler\\Context();\n$context['key'] = 'value';\n\nvar_dump(\n    isset($context['key']),\n    $context['key']\n);\n\n/**\n * Will output:\n *     bool(true)\n *     string(5) \"value\"\n */</code></pre>\n  <p>To register computed values, we can use a function; thus:</p>\n  <pre><code class=\"language-php\">$context['computed'] = function () {\n    return 42;\n};\n\nvar_dump($context['computed']);\n\n/**\n * Will output:\n *     int(42)\n */</code></pre>\n  <p>We said computed values are stored in a <strong>cache</strong> on the first\n  read. To illustrate this we will use a function incrementing an integer at\n  each call:</p>\n  <pre><code class=\"language-php\">$i              = 0;\n$context['int'] = function () use (&amp;amp;$i) {\n    return ++$i;\n};\n\nvar_dump(\n    $context['int'],\n    $context['int'],\n    $context['int'],\n    $i\n);\n\n/**\n * Will output:\n *     int(1)\n *     int(1)\n *     int(1)\n *     int(1)\n */</code></pre>\n  <p>The <code class=\"language-php\">$i</code> variable has been incremented once\n  to go from 0 to 1, and then, has been stored in a cache. Now, if we\n  encapsulate this function in an instance of the\n  <code>Hoa\\Ruler\\DynamicCallable</code> class, let's observe what happens:</p>\n  <pre><code class=\"language-php\">$i              = 0;\n$context['int'] = new Hoa\\Ruler\\DynamicCallable(\n    function () use (&amp;amp;$i) {\n        return ++$i;\n    }\n);\n\nvar_dump(\n    $context['int'],\n    $context['int'],\n    $context['int'],\n    $i\n);\n\n/**\n * Will output:\n *     int(1)\n *     int(2)\n *     int(3)\n *     int(3)\n */</code></pre>\n  <p>The result is no longer stored in a cache.</p>\n  <p>We can also use a declared function thanks to its name. Attention, it is\n  not possible to call native PHP functions for security reasons. The context\n  has not such a <strong>scope</strong>. Thus:</p>\n  <pre><code class=\"language-php\">function answer()\n{\n    return 42;\n}\n\n$context['the_answer'] = 'answer';\n\nvar_dump($context['the_answer']);\n\n/**\n * Will output:\n *     int(42)\n */</code></pre>\n  <p>Nothing more to know about the context. It is not that complicated!</p>\n\n  <h3 id=\"Asserter\" for=\"main-toc\">Asserter</h3>\n\n  <p>Given a rule and a context, the asserter is responsible to compute the\n  result of a rule, including the values of variables which are in the\n  context.</p>\n  <p>The rule can have two different forms:</p>\n  <ul>\n    <li>a <strong>string</strong> or</li>\n    <li>an <strong>object</strong> model.</li>\n  </ul>\n  <p>If it is a string, it will be transformed into an object model\n  automatically by the <code>Hoa\\Ruler\\Ruler::assert</code> method. This object\n  model implements the interfaces of <a href=\"@hack:chapter=Visitor\">the\n  <code>Hoa\\Visitor</code> library</a> and thus can be visited. This is why the\n  <code>Hoa\\Ruler\\Visitor\\Asserter</code> asserter is a\n  <strong>visitor</strong>. Finally, the context is defined on the asserter with\n  the <code>Hoa\\Ruler\\Visitor\\Asserter::setContext</code> method. Thus:</p>\n  <pre><code class=\"language-php\">$ruler             = new Hoa\\Ruler\\Ruler();\n$rule              = 'points > 30';\n$context           = new Hoa\\Ruler\\Context();\n$context['points'] = 42;\n\n// Define an asserter.\n$asserter          = new Hoa\\Ruler\\Visitor\\Asserter();\n\n// Set this asserter on the ruler.\n$ruler->setAsserter($asserter);\n\n// Assert!\nvar_dump(\n    $ruler->assert($rule, $context)\n);\n\n/**\n * Will output:\n *     bool(true)\n */</code></pre>\n  <p>The <code>Hoa\\Ruler\\Ruler::assert</code> method will automatically define\n  the context on the asserter.</p>\n\n  <h4 id=\"Add_functions\" for=\"main-toc\">Add functions</h4>\n\n  <p>We said the names of the operators and of the functions in the rules are\n  <strong>free</strong>. Therefore, we supposed the ability to define our own\n  operators and functions. Let's add the <code>logged</code> function that tests\n  if an object of kind <code>User</code> is connected. Here is this object:</p>\n  <pre><code class=\"language-php\">class User\n{\n    const DISCONNECTED = 0;\n    const CONNECTED    = 1;\n    protected $_status = 1;\n\n    public function getStatus()\n    {\n        return $this->_status;\n    }\n}</code></pre>\n  <p>The implementation of the <code>logged</code> function might be the\n  following:</p>\n  <pre><code class=\"language-php\">$logged = function (User $user) {\n    return $user::CONNECTED === $user->getStatus();\n};</code></pre>\n  <p>Finally, to declare this function, we will use the\n  <code>Hoa\\Ruler\\Visitor\\Asserter::setOperator</code> method. We can also cite\n  the <code>operatorExists</code>, <code>getOperator</code> and\n  <code>getOperators</code> methods which respectively allow to test if an\n  operator exists, to get a previously declared operator and to get all the\n  declared operators. Thus:</p>\n  <pre><code class=\"language-php\">$ruler             = new Hoa\\Ruler\\Ruler();\n$rule              = 'logged(user) and points > 30';\n$context           = new Hoa\\Ruler\\Context();\n$context['user']   = new User();\n$context['points'] = 42;\n\n// Declare the `logged` function.\n$asserter = new Hoa\\Ruler\\Visitor\\Asserter();\n$asserter->setOperator('logged', $logged);\n\n$ruler->setAsserter($asserter);\n\n// Assert!\nvar_dump(\n    $ruler->assert($rule, $context)\n);\n\n/**\n * Will output:\n *     bool(true)\n */</code></pre>\n  <p>The <code>Hoa\\Ruler\\Ruler</code> class only contains methods to work faster\n  and to hide the underlying mechanism (detailed hereinafter). One of them is\n  the <code>getDefaultAsserter</code> static method which returns a unique\n  instance of the <code>Hoa\\Ruler\\Visitor\\Asserter</code> class. We can use this\n  unique instance to define new operators for <strong>all</strong> the rules.\n  Its usage is very similar to what we saw previously:</p>\n  <pre><code class=\"language-php\">$ruler             = new Hoa\\Ruler\\Ruler();\n$rule              = 'logged(user) and points > 30';\n$context           = new Hoa\\Ruler\\Context();\n$context['user']   = new User();\n$context['points'] = 42;\n\n// Declare the `logged` function.\n$ruler->getDefaultAsserter()->setOperator('logged', $logged);\n\n// Assert!\nvar_dump(\n    $ruler->assert($rule, $context)\n);\n\n/**\n * Will output:\n *     bool(true)\n */</code></pre>\n  <p>The <code>Hoa\\Ruler\\Visitor\\Asserter::setOperator</code> method accepts any\n  valid callable.</p>\n  <p>The <code>and</code>, <code>or</code>, <code>xor</code>, <code>not</code>,\n  <code>=</code>, <code>!=</code> and <code>sum</code> etc. operators are\n  defined in\n  <a href=\"@central_resource:path=Library/Ruler/Visitor/Asserter.php\">the\n  <code>Hoa\\Ruler\\Visitor\\Asserter</code> class</a>. Feel free to read it to\n  get inspired!</p>\n\n  <h2 id=\"Language_transformation\" for=\"main-toc\">Language transformation</h2>\n\n  <p>The underlying mechanism hidden by the <code>Hoa\\Ruler\\Ruler</code> class\n  is simple and very <strong>modular</strong>. The following sections detail the\n  possible <strong>transformations</strong>, and associated usage, of the\n  language.</p>\n\n  <div id=\"transformation\" class=\"schema\"></div>\n  <script>\n  Hoa.Document.onReady(function ( ) {\n\n      var paper        = Hoa.Graph(Hoa.$('#transformation'), 800, 300);\n      var grid         = paper.grid(0, 0, 800, 300, 5, 3);\n      var rule         = grid.push(paper.rect(0, 0, 110, 80, 3, 'rule'), 0, 1);\n      var vinterpreter = grid.push(paper.rect(0, 0, 110, 80, 3, 'interpreter'), 1, 1);\n      var model        = grid.push(paper.rect(0, 0, 110, 80, 3, 'model'), 2, 1);\n      var vasserter    = grid.push(paper.rect(0, 0, 110, 80, 3, 'asserter'), 4, 0);\n      var vcompiler    = grid.push(paper.rect(0, 0, 110, 80, 3, 'PHP code'), 4, 1);\n      var vdisassembly = grid.push(paper.rect(0, 0, 110, 80, 3, 'rule'), 4, 2);\n\n      paper.link.between(rule, vinterpreter, '');\n      paper.link.between(vinterpreter, model, '');\n      paper.link.between(model, vasserter, '');\n      paper.link.between(model, vcompiler, '');\n      paper.link.between(model, vdisassembly, '');\n  });\n  </script>\n\n  <p>First of all, the rule is interpreted by an interpreter to be transformed\n  into an object model. Then, this object model is used by the asserter, or can\n  be transformed into PHP code or transformed into a rule. This object model is\n  the central point of the language, this is its most advanced form.</p>\n\n  <h3 id=\"Interpreter_language_to_object_model\" for=\"main-toc\">Interpreter:\n  Language to object model</h3>\n\n  <p>A rule is a string. To transform it into an object model, we will use\n  <a href=\"@hack:chapter=Compiler\">the <code>Hoa\\Compiler</code>\n  library</a>.</p>\n\n  <div id=\"transformation_interpreter\" class=\"schema\"></div>\n  <script>\n  Hoa.Document.onReady(function ( ) {\n\n      var paper        = Hoa.Graph(Hoa.$('#transformation_interpreter'), 800, 300);\n      var grid         = paper.grid(0, 0, 800, 300, 5, 3);\n      var rule         = grid.push(paper.rect(0, 0, 110, 80, 3, 'rule'), 0, 1);\n      var vinterpreter = grid.push(paper.rect(0, 0, 110, 80, 3, 'interpreter'), 1, 1);\n      var model        = grid.push(paper.rect(0, 0, 110, 80, 3, 'model'), 2, 1);\n      var vasserter    = grid.push(paper.rect(0, 0, 110, 80, 3, 'asserter').attr({opacity: .3}), 4, 0);\n      var vcompiler    = grid.push(paper.rect(0, 0, 110, 80, 3, 'PHP code').attr({opacity: .3}), 4, 1);\n      var vdisassembly = grid.push(paper.rect(0, 0, 110, 80, 3, 'rule').attr({opacity: .3}), 4, 2);\n\n      paper.link.between(rule, vinterpreter, '');\n      paper.link.between(vinterpreter, model, '');\n      paper.link.between(model, vasserter, '');\n      paper.link.between(model, vcompiler, '');\n      paper.link.between(model, vdisassembly, '');\n  });\n  </script>\n\n  <p>Thanks to the grammar of rules (defined in\n  <a href=\"@central_resource:path=Library/Ruler/Grammar.pp\">the\n  <code>hoa://Library/Ruler/Grammar.pp</code> file</a>), we will get an AST: An\n  abstract syntax tree. For instance, for the rule <code>points > 30</code>, its\n  AST is:</p>\n  <pre><code class=\"language-shell\">$ echo 'points > 30' | hoa compiler:pp hoa://Library/Ruler/Grammar.pp 0 --visitor dump\n>  #expression\n>  >  #operation\n>  >  >  token(identifier, points)\n>  >  >  token(identifier, >)\n>  >  >  token(integer, 30)</code></pre>\n  <p>In order to be exploitable, this tree will be transformed into an\n  <strong>object model</strong>. This transformation is ensured by the\n  <code>Hoa\\Ruler\\Visitor\\Interpreter</code> visitor. Thus, if we should apply\n  it manually:</p>\n  <pre><code class=\"language-php\">$compiler    = Hoa\\Compiler\\Llk::load(\n    new Hoa\\File\\Read('hoa://Library/Ruler/Grammar.pp')\n);\n$ast         = $compiler->parse('points > 30');\n$interpreter = new Hoa\\Ruler\\Visitor\\Interpreter();\n$model       = $interpreter->visit($ast);\n\nvar_dump(\n    get_class($model)\n);\n\n/**\n * Will output:\n *     string(21) \"Hoa\\Ruler\\Model\\Model\"\n */</code></pre>\n  <p>We learn that the model is represented by classes belonging to the\n  <code>Hoa\\Ruler\\Model</code> namespace.</p>\n  <p>All these operations are replaced by the\n  <code>Hoa\\Ruler\\Ruler::interpret</code> static method.</p>\n  <pre><code class=\"language-php\">$model = Hoa\\Ruler\\Ruler::interpret('points > 30');</code></pre>\n  <p>We can get the compiler with the <code>Hoa\\Ruler\\Ruler::getCompiler</code>\n  method.</p>\n  <p>We will see how this step can be important to get better performances.</p>\n\n  <h3 id=\"Compiler_object_model_to_PHP\" for=\"main-toc\">Compiler: Object model to\n  PHP</h3>\n\n  <p>The object model can be created manually by instantiating all the objects\n  of kind <code>Hoa\\Ruler\\Model\\<em>*</em></code> and by combining them\n  together.</p>\n\n  <div id=\"transformation_compiler\" class=\"schema\"></div>\n  <script>\n  Hoa.Document.onReady(function ( ) {\n\n      var paper        = Hoa.Graph(Hoa.$('#transformation_compiler'), 800, 300);\n      var grid         = paper.grid(0, 0, 800, 300, 5, 3);\n      var rule         = grid.push(paper.rect(0, 0, 110, 80, 3, 'rule').attr({opacity: .3}), 0, 1);\n      var vinterpreter = grid.push(paper.rect(0, 0, 110, 80, 3, 'interpreter').attr({opacity: .3}), 1, 1);\n      var model        = grid.push(paper.rect(0, 0, 110, 80, 3, 'model'), 2, 1);\n      var vasserter    = grid.push(paper.rect(0, 0, 110, 80, 3, 'asserter').attr({opacity: .3}), 4, 0);\n      var vcompiler    = grid.push(paper.rect(0, 0, 110, 80, 3, 'PHP code'), 4, 1);\n      var vdisassembly = grid.push(paper.rect(0, 0, 110, 80, 3, 'rule').attr({opacity: .3}), 4, 2);\n\n      paper.link.between(rule, vinterpreter, '');\n      paper.link.between(vinterpreter, model, '');\n      paper.link.between(model, vasserter, '');\n      paper.link.between(model, vcompiler, '');\n      paper.link.between(model, vdisassembly, '');\n  });\n  </script>\n\n  <p>The PHP code required for this operation can be automatically generated\n  thanks to the <code>Hoa\\Ruler\\Visitor\\Compiler</code> class. Thus:</p>\n  <pre><code class=\"language-php\">$compiler = new Hoa\\Ruler\\Visitor\\Compiler();\necho $compiler->visit($model);\n\n/**\n * Will output:\n *     $model = new \\Hoa\\Ruler\\Model();\n *     $model->expression =\n *         $model->{'>'}(\n *             $model->variable('points'),\n *             30\n *         );\n */</code></pre>\n  <p>The generated code is simplified and optimized to be as short as possible\n  whilst staying readable for a human.</p>\n  <p>We will see how this step can be important to get better performances.</p>\n\n  <h3 id=\"Disassembler_object_model_to_language\" for=\"main-toc\">Disassembler:\n  Object model to language</h3>\n\n  <p>So far, we have seen how to jump from a rule to its object model. The\n  disassembler applies the <strong>opposite</strong> operation: It transforms an\n  object model into a string.</p>\n\n  <div id=\"transformation_disassembler\" class=\"schema\"></div>\n  <script>\n  Hoa.Document.onReady(function ( ) {\n\n      var paper        = Hoa.Graph(Hoa.$('#transformation_disassembler'), 800, 300);\n      var grid         = paper.grid(0, 0, 800, 300, 5, 3);\n      var rule         = grid.push(paper.rect(0, 0, 110, 80, 3, 'rule').attr({opacity: .3}), 0, 1);\n      var vinterpreter = grid.push(paper.rect(0, 0, 110, 80, 3, 'interpreter').attr({opacity: .3}), 1, 1);\n      var model        = grid.push(paper.rect(0, 0, 110, 80, 3, 'model'), 2, 1);\n      var vasserter    = grid.push(paper.rect(0, 0, 110, 80, 3, 'asserter').attr({opacity: .3}), 4, 0);\n      var vcompiler    = grid.push(paper.rect(0, 0, 110, 80, 3, 'PHP code').attr({opacity: .3}), 4, 1);\n      var vdisassembly = grid.push(paper.rect(0, 0, 110, 80, 3, 'rule'), 4, 2);\n\n      paper.link.between(rule, vinterpreter, '');\n      paper.link.between(vinterpreter, model, '');\n      paper.link.between(model, vasserter, '');\n      paper.link.between(model, vcompiler, '');\n      paper.link.between(model, vdisassembly, '');\n  });\n  </script>\n\n  <p>The generated rule can differ a little bit from the original one in term of\n  syntax (parenthesis, quotes, spacing…) but never in term of semantics. Thus:</p>\n  <pre><code class=\"language-php\">$disassembly = new Hoa\\Ruler\\Visitor\\Disassembly();\necho $disassembly->visit($model);\n\n/**\n * Will output:\n *     (points > 30)\n */</code></pre>\n\n  <h2 id=\"Performances\" for=\"main-toc\">Performances</h2>\n\n  <p>Transforming a rule into an object model is not a low-cost operation. It\n  becomes significant when applied thousand times per minute. Nevertheless,\n  applying an asserter on an object model is a low-cost operation. We will\n  present two ways to avoid the transformation of a rule into an object\n  model.</p>\n\n  <h3 id=\"Serialize_the_object_model\" for=\"main-toc\">Serialize the object\n  model</h3>\n\n  <p>Once the object model is present, we can serialize it with the help of\n  <a href=\"http://php.net/serialize\">the <code>serialize</code> PHP\n  function</a>. We will get a string representing instances of objects forming\n  the object model. To get back to the object model and being able to apply an\n  asserter, we will use <a href=\"http://php.net/unserialize\">the\n  <code>unserialize</code> PHP function</a>. The result of this serialization\n  can be stored in a database instead of rules. This requires a little bit more\n  space but let's remind that we can transform an object model to its rule\n  easily thanks to the <code>Hoa\\Ruler\\Visitor\\Disassembly</code> class,\n  consequently this information is not lost. Thus:</p>\n  <pre><code class=\"language-php\">$ruler             = new Hoa\\Ruler\\Ruler();\n$rule              = 'points > 30';\n$context           = new Hoa\\Ruler\\Context();\n$context['points'] = 42;\n\n// Nothing in the database.\nif (null === $serialized = $database->get($ruleId)) {\n    // We transform the rule into an object model.\n    $model = Hoa\\Ruler\\Ruler::interpret($rule);\n\n    // We serialize and save the object model.\n    $database->save($ruleId, serialize($model));\n} else {\n    // We have a serialization! We unserialize it to get the object model.\n    $model = unserialize($serialized);\n}\n\n// We can assert by using a model instead of a rule!\nvar_dump(\n    $ruler->assert($model, $context)\n);\n\n/**\n * Will output:\n *     bool(true)\n */</code></pre>\n  <p>This way, the rule is transformed into an object model <strong>only\n  once</strong>!</p>\n\n  <h3 id=\"Save_and_execute_PHP_code\" for=\"main-toc\">Save and execute PHP\n  code</h3>\n\n  <p>Another way to avoid the transformation of a rule into its object model is\n  to save the PHP code allowing to build the object model thanks to the\n  <code>Hoa\\Ruler\\Visitor\\Compiler</code> class. Once this PHP code stored and\n  executed, we get back our object model.</p>\n  <p>However, executing such a PHP code will prove to be slightly slower and\n  more difficult to deploy than the previous technique.</p>\n\n  <h2 id=\"Conclusion\" for=\"main-toc\">Conclusion</h2>\n\n  <p>The <code>Hoa\\Ruler</code> library defines a <strong>language</strong> of\n  <strong>simple</strong> business rules inspired by the SQL language for the\n  syntax. The language can be <strong>transformed</strong> in many ways: To an\n  object model in order to be executed, or from this object model, into PHP code\n  or into the original language. The instantiation of the variables present in\n  the language relies on a <strong>context</strong>. All these operations are\n  hidden through a simple and clear interface.</p>\n  <p>The <strong>performance</strong> aspect has been addressed and two\n  solutions have been proposed. Today it is used by the industry on important\n  projects, <code>Hoa\\Ruler</code> is able to support heavy loads if these\n  simple methodologies are applied.</p>\n\n</yield>\n</overlay>\n"
  },
  {
    "path": "Documentation/Fr/Index.xyl",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<overlay xmlns=\"http://hoa-project.net/xyl/xylophone\">\n<yield id=\"chapter\">\n\n  <p>Les <strong>règles métiers</strong> (comme « <q>tous les clients qui ont\n  dépensé plus de 100€ en une fois reçoivent une réduction de 10% sur leur\n  prochain achat</q> ») sont la plupart du temps définies à\n  l'<strong>extérieur</strong> de l'application.  Elles sont même souvent\n  exprimées dans un <strong>langage différent</strong> que le langage de\n  programmation utilisé.  La bibliothèque <code>Hoa\\Ruler</code> offre un moteur\n  permettant d'exécuter des règles métiers simplement et de manière\n  <strong>performante</strong> tout en restant très\n  <strong>extensible</strong>.</p>\n\n  <h2 id=\"Table_of_contents\">Table des matières</h2>\n\n  <tableofcontents id=\"main-toc\" />\n\n  <h2 id=\"Introduction\" for=\"main-toc\">Introduction</h2>\n\n  <p>La logique métier est très différente de la logique Informatique. « <q>Tous\n  les clients qui ont dépensé plus de 100€ en une fois reçoivent une réduction\n  de 10% sur leur prochain achat</q> ». Cette règle permet d'accéder à certaines\n  parties du programme si elle est <strong>validée</strong>.  Toutefois, elle\n  peut <strong>changer</strong> à n'importe quel moment. Très souvent, dans une\n  équipe, ce ne sera pas au développeur d'implémenter cette règle. Elle\n  proviendra probablement d'un <strong>dépôt de règles métiers</strong>, qui\n  auront été écrites par d'autres personnes, soit <strong>manuellement</strong>,\n  soit à l'aide d'un <strong>programme tiers</strong>. Cela implique que le\n  langage utilisé pour exprimer une règle n'est pas le langage utilisé pour\n  développer le programme. Un exemple encore plus évident avec l'utilisation\n  d'une règle pour filtrer des éléments : un élément est accepté si « <q>son\n  groupe est <em lang=\"en\">customer</em> ou <em lang=\"en\">guest</em> et son\n  nombre de points est supérieur à 30</q> ». Cette règle peut très bien être\n  écrite par un utilisateur via une interface en ligne de commande pour filtrer\n  des résultats d'une base de données ou de logs.</p>\n  <p>Il est important de comprendre que les règles doivent être écrites dans un\n  <strong>langage dédié</strong>. Néanmoins, l'usage qui est fait des règles est\n  très <strong>vaste</strong> et <strong>imprédictible</strong>. C'est pourquoi\n  il est primordiale d'avoir des règles souples et <strong>extensibles</strong>\n  dans leur syntaxe. Par exemple, il doit être permis d'ajouter des opérateurs\n  ou des fonctions : « <q>tous les clients de l'hôtel avec un pass Gold auront\n  une réduction de 10%</q> ». Le « pass Gold » peut être un opérateur ou une\n  fonction spécifique au métier du programme concerné.</p>\n  <p>Le langage utilisé par la bibliothèque <code>Hoa\\Ruler</code> pour décrire\n  des règles respecte ces contraintes d'extensibilité. Les règles ne seront pas\n  proches du langage humain mais resterons <strong>naturelles</strong> à lire.\n  Si nous reprenons l'exemple de la règle « <q>son groupe est\n  <em lang=\"en\">customer</em> ou <em lang=\"en\">guest</em> et son nombre de\n  points est supérieur à 30</q> », elle s'écrira : <code><em>group</em> in\n  [\"customer\", \"guest\"] and <em>points</em> > 30</code>.  Les éléments\n  <code><em>group</em></code> et <code><em>points</em></code> sont des variables\n  de la règle. Leurs valeurs seront définies par un contexte.</p>\n  <p>D'un point de vue plus formel, une règle est un prédicat, c'est à dire que\n  son résultat est toujours un booléen : <code class=\"language-php\">true</code>\n  ou <code class=\"language-php\">false</code>. Comme ces règles sont destinées à\n  être manipulées (modifiées) et exécutées, la bibliothèque\n  <code>Hoa\\Ruler</code> propose plusieurs outils pour travailler efficacement\n  avec ces contraintes, présentés dans les sections suivantes.</p>\n\n  <h2 id=\"Global_workflow\" for=\"main-toc\">Fonctionnement général</h2>\n\n  <p>Le fonctionnement général de la bibliothèque <code>Hoa\\Ruler</code> se\n  déroule en 3 étapes :</p>\n  <ol>\n    <li>définition d'une <strong>règle</strong> ;</li>\n    <li>définition d'un <strong>contexte</strong> ;</li>\n    <li>usage d'un <strong>asserteur</strong> pour l'exécution.</li>\n  </ol>\n  <p>La <strong>règle</strong> est une chaîne de caractères respectant une\n  syntaxe précise, décrite par la grammaire du langage définie par la\n  bibliothèque <code>Hoa\\Ruler</code> (détaillée ci-après). Cette règle contient\n  des variables dont les valeurs sont définies par le <strong>contexte</strong>.\n  Le contexte peut contenir des valeurs scalaires, des tableaux ou même des\n  fonctions et des objets. Enfin, l'<strong>asserteur</strong> associe le\n  contexte à la règle pour pouvoir l'exécuter et obtenir un résultat. Nous\n  rappelons que ce résultat est nécessairement un booléen. C'est alors un\n  prédicat.</p>\n  <p>Le contexte est représenté par la classe <code>Hoa\\Ruler\\Context</code>.\n  L'asserteur est représenté par la classe\n  <code>Hoa\\Ruler\\Visitor\\Asserter</code>. Nous pouvons employer la méthode\n  <code>Hoa\\Ruler\\Ruler::assert</code> qui facilite son utilisation. Ainsi :</p>\n  <pre><code class=\"language-php\">$ruler = new Hoa\\Ruler\\Ruler();\n\n// 1. Write a rule.\n$rule  = 'group in [\"customer\", \"guest\"] and points > 30';\n\n// 2. Create a context.\n$context           = new Hoa\\Ruler\\Context();\n$context['group']  = 'customer';\n$context['points'] = function () {\n    return 42;\n};\n\n// 3. Assert!\nvar_dump(\n    $ruler->assert($rule, $context)\n);\n\n/**\n * Will output:\n *     bool(true)\n */</code></pre>\n  <p>La règle est définie dans la variable\n  <code class=\"language-php\">$rule</code>. Le contexte, quant à lui, est défini\n  dans la variable <code class=\"language-php\">$context</code>. Il contient\n  2 variables : <code>group</code> et <code>points</code>, respectivement avec\n  les valeurs <code class=\"language-php\">'customer'</code> et\n  <code class=\"language-php\">42</code> (retournée par une fonction). Enfin, la\n  dernière étape utilise la méthode <code>Hoa\\Ruler\\Ruler::assert</code> pour\n  exécuter la règle <code class=\"language-php\">$rule</code> avec le contexte\n  <code class=\"language-php\">$context</code> (ce dernier est optionnel). Le\n  résultat est <code class=\"language-php\">true</code> car <code>group</code> est\n  bien dans la liste <code>customer</code> ou <code>guest</code>, et\n  <code>point</code> est bien supérieur à 30. Changez les valeurs dans le\n  contexte ou la règle pour observer un résultat différent.</p>\n  <p>Les sections suivantes détaillent le fonctionnement de chaque partie mais\n  l'usage classique reste aussi simple que ça !</p>\n\n  <h3 id=\"Grammar\" for=\"main-toc\">Grammaire (par l'exemple)</h3>\n\n  <p>La grammaire du langage des règles est décrite dans\n  <a href=\"@central_resource:path=Library/Ruler/Grammar.pp\">le fichier\n  <code>hoa://Library/Ruler/Grammar.pp</code></a>. Cette grammaire est exprimée\n  avec le langage PP. Pour plus d'informations, voir\n  <a href=\"@hack:chapter=Compiler\">la bibliothèque\n  <code>Hoa\\Compiler</code></a>. Nous précisons que le langage supporte Unicode.\n  Nous n'allons pas expliquer le langage alors que la grammaire donne tous les\n  <strong>détails</strong> nécessaires. En revanche, nous allons donner\n  plusieurs exemples de syntaxe.</p>\n\n  <table>\n    <caption>Langage de <code>Hoa\\Ruler</code> par l'exemple.</caption>\n    <thead>\n      <tr>\n        <th>syntaxe</th>\n        <th>sémantique</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td><code>'foo'</code>, <code>\"foo\"</code>, <code>'f\\'oo'</code></td>\n        <td>des chaînes de caractères</td>\n      </tr>\n      <tr>\n        <td><code>true</code>, <code>false</code>, <code>null</code></td>\n        <td>des constantes pré-définies</td>\n      </tr>\n      <tr>\n        <td><code>4.2</code></td>\n        <td>un réel</td>\n      </tr>\n      <tr>\n        <td><code>42</code></td>\n        <td>un entier</td>\n      </tr>\n      <tr>\n        <td><code>['foo', true, 4.2, 42]</code></td>\n        <td>un tableau (hétérogène)</td>\n      </tr>\n      <tr>\n        <td><code>sum(1, 2, 3)</code></td>\n        <td>un appel de la fonction <code>sum</code> avec 3 arguments</td>\n      </tr>\n      <tr>\n        <td><code><em>points</em></code></td>\n        <td>une variable</td>\n      </tr>\n      <tr>\n        <td><code><em>points</em>['x']</code></td>\n        <td>un accès tableau</td>\n      </tr>\n      <tr>\n        <td><code><em>line</em>.pointA</code></td>\n        <td>un accès objet (attribut)</td>\n      </tr>\n      <tr>\n        <td><code><em>line</em>.length()</code></td>\n        <td>un appel à une méthode</td>\n      </tr>\n      <tr>\n        <td><code>and</code>, <code>or</code>, <code>xor</code>, <code>not</code></td>\n        <td>des opérateurs logiques</td>\n      </tr>\n      <tr>\n        <td><code>=</code>, <code>!=</code>, <code>></code>, <code>&amp;lt;</code>,\n            <code>>=</code>, <code>&amp;lt;=</code></td>\n        <td>des opérateurs de comparaisons</td>\n      </tr>\n      <tr>\n        <td><code>is</code>, <code>in</code></td>\n        <td>des opérateurs d'appartenance</td>\n      </tr>\n    </tbody>\n  </table>\n\n  <p>Bien sûr, ces exemples représentent des parties atomiques de la grammaire\n  que nous pouvons combiner. Ainsi :\n  <code><em>userA</em>.allows(<em>groups</em>[<em>groupId</em>][<em>userB</em>])</code>\n  est valide. De même que <code>f(<em>user</em>, <em>points</em> > 7 and\n  <em>points</em> &amp;lt; 42)</code> est également valide.</p>\n  <p>En réalité, les fonctions, les opérateurs de comparaisons et les opérateurs\n  d'appartenance ne sont pas définis par la grammaire mais par l'asserteur\n  (détaillé ci-après). <code>Hoa\\Ruler</code> ne fait pas la différence entre un\n  opérateur et une fonction. <strong>Les opérateurs sont considérés comme des\n  fonctions</strong> ; un opérateur n'étant qu'une fonction d'arité 1 ou 2.\n  Ainsi, nous pouvons écrire <code>2 = 2</code> ou <code>=(2, 2)</code>, cela\n  produira strictement le même résultat. Tout comme le nom des fonctions qui\n  n'est pas défini par la grammaire, le nom des opérateurs n'est lui non plus\n  pas défini par la grammaire, excepté pour les opérateurs logiques qui ont un\n  traitement particuliers (à cause de la précédence des opérateurs). Cela a pour\n  effet de pouvoir <strong>créer</strong> nos propres opérateurs ou fonctions.\n  Nous pouvons imaginer <code><em>a</em> ∈ <em>A</em></code>, <code>√(42)</code>\n  ou encore <code><em>userA</em> allows <em>userB</em></code> comme étant des\n  expressions valides.</p>\n\n  <h3 id=\"Context\" for=\"main-toc\">Contexte</h3>\n\n  <p>Le contexte définit les valeurs des variables présentes dans des\n  règles. Ces valeurs peuvent être :</p>\n  <ul>\n    <li>des <strong>constantes</strong>, comme\n    <code class=\"language-php\">42</code> ou\n    <code class=\"language-php\">'foo'</code> qui sont des scalaires, ou\n    <code class=\"language-php\">[1, 1, 2, 3, 5]</code> ou un objet qui sont des\n    types structurés ;</li>\n    <li>des <strong>valeurs calculées</strong>, c'est à dire retournée par une\n    fonction ou une méthode.</li>\n  </ul>\n  <p>Par défaut, les valeurs calculées ne le sont qu'une seule fois. En effet,\n  elles sont placées dans un cache pour des raisons de performance. Si nous\n  voulons les recalculer à chaque lecture, il faudra les encapsuler dans un\n  objet de type <code>Hoa\\Ruler\\DynamicCallable</code>.</p>\n  <p>Avant de détailler cette partie, présentons le contexte. Un contexte est\n  une instance de la classe <code>Hoa\\Ruler\\Context</code> qui implémente\n  <a href=\"http://php.net/arrayaccess\">l'interface <code>ArrayAccess</code></a>.\n  Ainsi nous utilisons le contexte comme un tableau :</p>\n  <pre><code class=\"language-php\">$context        = new Hoa\\Ruler\\Context();\n$context['key'] = 'value';\n\nvar_dump(\n    isset($context['key']),\n    $context['key']\n);\n\n/**\n * Will output:\n *     bool(true)\n *     string(5) \"value\"\n */</code></pre>\n  <p>Pour y déposer des valeurs calculées, nous pouvons le faire via une\n  fonction ; ainsi :</p>\n  <pre><code class=\"language-php\">$context['computed'] = function () {\n    return 42;\n};\n\nvar_dump($context['computed']);\n\n/**\n * Will output:\n *     int(42)\n */</code></pre>\n  <p>Nous avons dit que les valeurs calculées sont placées en\n  <strong>cache</strong> dès la première lecture. Pour l'illustrer, nous allons\n  utiliser une fonction qui incrémente un entier à chaque appel :</p>\n  <pre><code class=\"language-php\">$i              = 0;\n$context['int'] = function () use (&amp;amp;$i) {\n    return ++$i;\n};\n\nvar_dump(\n    $context['int'],\n    $context['int'],\n    $context['int'],\n    $i\n);\n\n/**\n * Will output:\n *     int(1)\n *     int(1)\n *     int(1)\n *     int(1)\n */</code></pre>\n  <p>La variable <code class=\"language-php\">$i</code> a été incrémentée une\n  seule fois pour passer de 0 à 1, puis elle a été placée en cache. Maintenant,\n  si nous encapsulons cette fonction dans une instance de la classe\n  <code>Hoa\\Ruler\\DynamicCallable</code>, observons ce qu'il se passe :</p>\n  <pre><code class=\"language-php\">$i              = 0;\n$context['int'] = new Hoa\\Ruler\\DynamicCallable(\n    function () use (&amp;amp;$i) {\n        return ++$i;\n    }\n);\n\nvar_dump(\n    $context['int'],\n    $context['int'],\n    $context['int'],\n    $i\n);\n\n/**\n * Will output:\n *     int(1)\n *     int(2)\n *     int(3)\n *     int(3)\n */</code></pre>\n  <p>Le résultat n'est plus mis en cache.</p>\n  <p>Nous pouvons également utiliser une fonction déclarée grâce à son nom.\n  Attention toutefois, il sera impossible d'appeler les fonctions natives de PHP\n  pour des raisons de sécurité. Le contexte n'a pas une telle\n  <strong>portée</strong>. Ainsi :</p>\n  <pre><code class=\"language-php\">function answer()\n{\n    return 42;\n}\n\n$context['the_answer'] = 'answer';\n\nvar_dump($context['the_answer']);\n\n/**\n * Will output:\n *     int(42)\n */</code></pre>\n  <p>C'est tout ce qu'il faut savoir sur le contexte. Ce n'est pas plus\n  compliqué que ça !</p>\n\n  <h3 id=\"Asserter\" for=\"main-toc\">Asserteur</h3>\n\n  <p>Étant donné une règle et un contexte, l'asserteur est chargé de calculer le\n  résultat de cette règle, dont les valeurs des variables sont dans le\n  contexte.</p>\n  <p>La règle peut avoir deux formes possibles :</p>\n  <ul>\n    <li>une <strong>chaîne de caractère</strong> ou</li>\n    <li>un modèle <strong>objet</strong>.</li>\n  </ul>\n  <p>Si c'est une chaîne de caractère, elle sera transformée en modèle objet\n  automatiquement par la méthode <code>Hoa\\Ruler\\Ruler::assert</code>. Ce modèle\n  objet implémente les interfaces de <a href=\"@hack:chapter=Visitor\">la\n  bibliothèque <code>Hoa\\Visitor</code></a> et peut donc être visité. C'est\n  pourquoi l'asserteur <code>Hoa\\Ruler\\Visitor\\Asserter</code> est un\n  <strong>visiteur</strong>. Le contexte quant à lui est défini auprès de\n  l'asserteur avec la méthode\n  <code>Hoa\\Ruler\\Visitor\\Asserter::setContext</code>. Ainsi :</p>\n  <pre><code class=\"language-php\">$ruler             = new Hoa\\Ruler\\Ruler();\n$rule              = 'points > 30';\n$context           = new Hoa\\Ruler\\Context();\n$context['points'] = 42;\n\n// Define an asserter.\n$asserter          = new Hoa\\Ruler\\Visitor\\Asserter();\n\n// Set this asserter on the ruler.\n$ruler->setAsserter($asserter);\n\n// Assert!\nvar_dump(\n    $ruler->assert($rule, $context)\n);\n\n/**\n * Will output:\n *     bool(true)\n */</code></pre>\n  <p>La méthode <code>Hoa\\Ruler\\Ruler::assert</code> va automatiquement définir\n  le contexte auprès de l'asserteur.</p>\n\n  <h4 id=\"Add_functions\" for=\"main-toc\">Ajout de fonctions</h4>\n\n  <p>Nous avons précisé que les noms des opérateurs et des fonctions dans les\n  règles sont <strong>libres</strong>. Ainsi, nous avons évoqué la possibilité\n  de définir nos propres opérateurs et fonctions. Ajoutons la fonction\n  <code>logged</code> qui teste si un objet de type <code>User</code> est\n  connecté. Voici cet objet :</p>\n  <pre><code class=\"language-php\">class User\n{\n    const DISCONNECTED = 0;\n    const CONNECTED    = 1;\n    protected $_status = 1;\n\n    public function getStatus()\n    {\n        return $this->_status;\n    }\n}</code></pre>\n  <p>L'implémentation de la fonction <code>logged</code> serait alors :</p>\n  <pre><code class=\"language-php\">$logged = function (User $user) {\n    return $user::CONNECTED === $user->getStatus();\n};</code></pre>\n  <p>Enfin, pour déclarer cette fonction, nous allons utiliser la méthode\n  <code>Hoa\\Ruler\\Visitor\\Asserter::setOperator</code>. Nous pouvons aussi citer\n  les méthodes <code>operatorExists</code>, <code>getOperator</code> et\n  <code>getOperators</code> qui permettent respectivement de tester si un\n  opérateur existe, d'obtenir un opérateur précédemment déclaré et d'obtenir\n  tous les opérateurs déclarés. Ainsi :</p>\n  <pre><code class=\"language-php\">$ruler             = new Hoa\\Ruler\\Ruler();\n$rule              = 'logged(user) and points > 30';\n$context           = new Hoa\\Ruler\\Context();\n$context['user']   = new User();\n$context['points'] = 42;\n\n// Declare the `logged` function.\n$asserter = new Hoa\\Ruler\\Visitor\\Asserter();\n$asserter->setOperator('logged', $logged);\n\n$ruler->setAsserter($asserter);\n\n// Assert!\nvar_dump(\n    $ruler->assert($rule, $context)\n);\n\n/**\n * Will output:\n *     bool(true)\n */</code></pre>\n  <p>La classe <code>Hoa\\Ruler\\Ruler</code> ne contient que des méthodes pour\n  travailler plus vite et cacher le mécanisme sous-jacent (détaillé ci-après).\n  Elle contient entre autre la méthode statique <code>getDefaultAsserter</code>\n  qui retourne une instance unique de la classe\n  <code>Hoa\\Ruler\\Visitor\\Asserter</code>. Nous pouvons utiliser cette instance\n  unique pour définir des nouveaux opérateurs pour <strong>toutes</strong> les\n  règles. Son utilisation est très similaire à ce que nous avons vu\n  précédemment :</p>\n  <pre><code class=\"language-php\">$ruler             = new Hoa\\Ruler\\Ruler();\n$rule              = 'logged(user) and points > 30';\n$context           = new Hoa\\Ruler\\Context();\n$context['user']   = new User();\n$context['points'] = 42;\n\n// Declare the `logged` function.\n$ruler->getDefaultAsserter()->setOperator('logged', $logged);\n\n// Assert!\nvar_dump(\n    $ruler->assert($rule, $context)\n);\n\n/**\n * Will output:\n *     bool(true)\n */</code></pre>\n  <p>La méthode <code>Hoa\\Ruler\\Visitor\\Asserter::setOperator</code> utilise\n  n'importe quel <em lang=\"en\">callable</em> valide.</p>\n  <p>Les opérateurs <code>and</code>, <code>or</code>, <code>xor</code>,\n  <code>not</code>, <code>=</code>, <code>!=</code>, <code>sum</code> etc. sont\n  définis dans\n  <a href=\"@central_resource:path=Library/Ruler/Visitor/Asserter.php\">la classe\n  <code>Hoa\\Ruler\\Visitor\\Asserter</code></a>.  N'hésitez pas à vous en\n  inspirer !</p>\n\n  <h2 id=\"Language_transformation\" for=\"main-toc\">Transformation du langage</h2>\n\n  <p>Le mécanisme sous-jacent caché par la classe <code>Hoa\\Ruler\\Ruler</code>\n  est simple et très <strong>modulaire</strong>. Les sections suivantes\n  détaillent les <strong>transformations</strong> possibles du langage et leur\n  utilisation.</p>\n\n  <div id=\"transformation\" class=\"schema\"></div>\n  <script>\n  Hoa.Document.onReady(function ( ) {\n\n      var paper        = Hoa.Graph(Hoa.$('#transformation'), 800, 300);\n      var grid         = paper.grid(0, 0, 800, 300, 5, 3);\n      var rule         = grid.push(paper.rect(0, 0, 110, 80, 3, 'règle'), 0, 1);\n      var vinterpreter = grid.push(paper.rect(0, 0, 110, 80, 3, 'interpréteur'), 1, 1);\n      var model        = grid.push(paper.rect(0, 0, 110, 80, 3, 'modèle'), 2, 1);\n      var vasserter    = grid.push(paper.rect(0, 0, 110, 80, 3, 'asserteur'), 4, 0);\n      var vcompiler    = grid.push(paper.rect(0, 0, 110, 80, 3, 'code PHP'), 4, 1);\n      var vdisassembly = grid.push(paper.rect(0, 0, 110, 80, 3, 'règle'), 4, 2);\n\n      paper.link.between(rule, vinterpreter, '');\n      paper.link.between(vinterpreter, model, '');\n      paper.link.between(model, vasserter, '');\n      paper.link.between(model, vcompiler, '');\n      paper.link.between(model, vdisassembly, '');\n  });\n  </script>\n\n  <p>Tout d'abord, la règle est interprétée par un interpréteur pour être\n  transformée en modèle objet. Ensuite, ce modèle objet est utilisé par\n  l'asserteur, ou peut être transformé en code PHP ou transformé en une règle.\n  Le modèle objet est le point central du langage, c'est sa forme la plus\n  avancée.</p>\n\n  <h3 id=\"Interpreter_language_to_object_model\" for=\"main-toc\">Interpréteur :\n  langage vers modèle objet</h3>\n\n  <p>Une règle est une chaîne de caractères. Pour la transformer en modèle\n  objet, nous utilisons <a href=\"@hack:chapter=Compiler\">la bibliothèque\n  <code>Hoa\\Compiler</code></a>.</p>\n\n  <div id=\"transformation_interpreter\" class=\"schema\"></div>\n  <script>\n  Hoa.Document.onReady(function ( ) {\n\n      var paper        = Hoa.Graph(Hoa.$('#transformation_interpreter'), 800, 300);\n      var grid         = paper.grid(0, 0, 800, 300, 5, 3);\n      var rule         = grid.push(paper.rect(0, 0, 110, 80, 3, 'règle'), 0, 1);\n      var vinterpreter = grid.push(paper.rect(0, 0, 110, 80, 3, 'interpréteur'), 1, 1);\n      var model        = grid.push(paper.rect(0, 0, 110, 80, 3, 'modèle'), 2, 1);\n      var vasserter    = grid.push(paper.rect(0, 0, 110, 80, 3, 'asserteur').attr({opacity: .3}), 4, 0);\n      var vcompiler    = grid.push(paper.rect(0, 0, 110, 80, 3, 'code PHP').attr({opacity: .3}), 4, 1);\n      var vdisassembly = grid.push(paper.rect(0, 0, 110, 80, 3, 'règle').attr({opacity: .3}), 4, 2);\n\n      paper.link.between(rule, vinterpreter, '');\n      paper.link.between(vinterpreter, model, '');\n      paper.link.between(model, vasserter, '');\n      paper.link.between(model, vcompiler, '');\n      paper.link.between(model, vdisassembly, '');\n  });\n  </script>\n\n  <p>Grâce à la grammaire des règles (définie dans \n  <a href=\"@central_resource:path=Library/Ruler/Grammar.pp\">le fichier\n  <code>hoa://Library/Ruler/Grammar.pp</code></a>), nous allons obtenir un AST :\n  un arbre abstrait. Par exemple, pour la règle <code>points > 30</code>, son\n  AST est :</p>\n  <pre><code class=\"language-shell\">$ echo 'points > 30' | hoa compiler:pp hoa://Library/Ruler/Grammar.pp 0 --visitor dump\n>  #expression\n>  >  #operation\n>  >  >  token(identifier, points)\n>  >  >  token(identifier, >)\n>  >  >  token(integer, 30)</code></pre>\n  <p>Pour que cet arbre soit exploitable plus facilement, il va être transformé\n  en <strong>modèle objet</strong>. Cette transformation est assurée par la\n  classe <code>Hoa\\Ruler\\Visitor\\Interpreter</code>. Ainsi, si nous devions le\n  faire manuellement :</p>\n  <pre><code class=\"language-php\">$compiler    = Hoa\\Compiler\\Llk::load(\n    new Hoa\\File\\Read('hoa://Library/Ruler/Grammar.pp')\n);\n$ast         = $compiler->parse('points > 30');\n$interpreter = new Hoa\\Ruler\\Visitor\\Interpreter();\n$model       = $interpreter->visit($ast);\n\nvar_dump(\n    get_class($model)\n);\n\n/**\n * Will output:\n *     string(21) \"Hoa\\Ruler\\Model\\Model\"\n */</code></pre>\n  <p>Nous apprenons alors que le modèle est représenté par les classes\n  appartenant à l'espace de nom <code>Hoa\\Ruler\\Model</code>.</p>\n  <p>Toutes ces opérations sont remplacées par la méthode statique\n  <code>Hoa\\Ruler\\Ruler::interpret</code> :</p>\n  <pre><code class=\"language-php\">$model = Hoa\\Ruler\\Ruler::interpret('points > 30');</code></pre>\n  <p>Nous pouvons obtenir le compilateur avec la méthode\n  <code>Hoa\\Ruler\\Ruler::getCompiler</code>.</p>\n  <p>Nous verrons comment cette étape peut être importante pour améliorer les\n  performances.</p>\n\n  <h3 id=\"Compiler_object_model_to_PHP\" for=\"main-toc\">Compilateur : modèle\n  objet vers PHP</h3>\n\n  <p>Le modèle objet peut être créé manuellement en instanciant tous les objets\n  de type <code>Hoa\\Ruler\\Model\\<em>*</em></code> et en les combinant\n  ensemble.</p>\n\n  <div id=\"transformation_compiler\" class=\"schema\"></div>\n  <script>\n  Hoa.Document.onReady(function ( ) {\n\n      var paper        = Hoa.Graph(Hoa.$('#transformation_compiler'), 800, 300);\n      var grid         = paper.grid(0, 0, 800, 300, 5, 3);\n      var rule         = grid.push(paper.rect(0, 0, 110, 80, 3, 'règle').attr({opacity: .3}), 0, 1);\n      var vinterpreter = grid.push(paper.rect(0, 0, 110, 80, 3, 'interpréteur').attr({opacity: .3}), 1, 1);\n      var model        = grid.push(paper.rect(0, 0, 110, 80, 3, 'modèle'), 2, 1);\n      var vasserter    = grid.push(paper.rect(0, 0, 110, 80, 3, 'asserteur').attr({opacity: .3}), 4, 0);\n      var vcompiler    = grid.push(paper.rect(0, 0, 110, 80, 3, 'code PHP'), 4, 1);\n      var vdisassembly = grid.push(paper.rect(0, 0, 110, 80, 3, 'règle').attr({opacity: .3}), 4, 2);\n\n      paper.link.between(rule, vinterpreter, '');\n      paper.link.between(vinterpreter, model, '');\n      paper.link.between(model, vasserter, '');\n      paper.link.between(model, vcompiler, '');\n      paper.link.between(model, vdisassembly, '');\n  });\n  </script>\n\n  <p>Le code PHP nécessaire à cette opération peut être automatiquement généré\n  grâce à la classe <code>Hoa\\Ruler\\Visitor\\Compiler</code>. Ainsi :</p>\n  <pre><code class=\"language-php\">$compiler = new Hoa\\Ruler\\Visitor\\Compiler();\necho $compiler->visit($model);\n\n/**\n * Will output:\n *     $model = new \\Hoa\\Ruler\\Model();\n *     $model->expression =\n *         $model->{'>'}(\n *             $model->variable('points'),\n *             30\n *         );\n */</code></pre>\n  <p>Le code généré est simplifié et optimisé pour être le plus court possible\n  tout en restant lisible par un être humain.</p>\n  <p>Nous verrons comment cette étape peut être importante pour améliorer les\n  performances.</p>\n\n  <h3 id=\"Disassembler_object_model_to_language\" for=\"main-toc\">Désassembleur :\n  modèle objet vers langage</h3>\n\n  <p>Jusqu'à maintenant, nous avons vu comment passer d'une règle vers son\n  modèle objet. Le désassembleur applique l'opération <strong>inverse</strong> :\n  il transforme un modèle objet vers une chaîne de caractères.</p>\n\n  <div id=\"transformation_disassembler\" class=\"schema\"></div>\n  <script>\n  Hoa.Document.onReady(function ( ) {\n\n      var paper        = Hoa.Graph(Hoa.$('#transformation_disassembler'), 800, 300);\n      var grid         = paper.grid(0, 0, 800, 300, 5, 3);\n      var rule         = grid.push(paper.rect(0, 0, 110, 80, 3, 'règle').attr({opacity: .3}), 0, 1);\n      var vinterpreter = grid.push(paper.rect(0, 0, 110, 80, 3, 'interpréteur').attr({opacity: .3}), 1, 1);\n      var model        = grid.push(paper.rect(0, 0, 110, 80, 3, 'modèle'), 2, 1);\n      var vasserter    = grid.push(paper.rect(0, 0, 110, 80, 3, 'asserteur').attr({opacity: .3}), 4, 0);\n      var vcompiler    = grid.push(paper.rect(0, 0, 110, 80, 3, 'code PHP').attr({opacity: .3}), 4, 1);\n      var vdisassembly = grid.push(paper.rect(0, 0, 110, 80, 3, 'règle'), 4, 2);\n\n      paper.link.between(rule, vinterpreter, '');\n      paper.link.between(vinterpreter, model, '');\n      paper.link.between(model, vasserter, '');\n      paper.link.between(model, vcompiler, '');\n      paper.link.between(model, vdisassembly, '');\n  });\n  </script>\n\n  <p>La règle générée peut différer un peu de la règle originale syntaxiquement\n  (parenthèses, guillemets, espacements…) mais jamais sémantiquement.\n  Ainsi :</p>\n  <pre><code class=\"language-php\">$disassembly = new Hoa\\Ruler\\Visitor\\Disassembly();\necho $disassembly->visit($model);\n\n/**\n * Will output:\n *     (points > 30)\n */</code></pre>\n\n  <h2 id=\"Performances\" for=\"main-toc\">Performances</h2>\n\n  <p>Transformer une règle vers un modèle objet a un coût. Ce coût devient\n  significatif si l'opération est appliquée des milliers de fois à la minute.\n  En revanche, appliquer un asserteur sur un modèle objet n'est pas coûteux.\n  Nous allons donc présenter deux façons d'éviter la transformation d'une règle\n  vers un modèle objet.</p>\n\n  <h3 id=\"Serialize_the_object_model\" for=\"main-toc\">Sérialiser le modèle\n  objet</h3>\n\n  <p>Une fois le modèle objet obtenu, nous pouvons le sérialiser à l'aide de\n  <a href=\"http://php.net/serialize\">la fonction PHP <code>serialize</code></a>.\n  Nous allons obtenir une chaîne de caractères représentant les instances des\n  objets constituant le modèle objet. Pour obtenir à nouveau le modèle objet et\n  être capable d'y appliquer un asserteur, nous utiliserons\n  <a href=\"http://php.net/unserialize\">la fonction PHP\n  <code>unserialize</code></a>. Le résultat de la sérialisation peut être\n  stocké en base de données à la place des règles. Cela nécessite un peu plus\n  de place mais rappelons que nous pouvons transformer le modèle objet vers sa\n  règle facilement grâce à la classe\n  <code>Hoa\\Ruler\\Visitor\\Disassembly</code>, cette information n'est donc pas\n  perdue. Ainsi :</p>\n  <pre><code class=\"language-php\">$ruler             = new Hoa\\Ruler\\Ruler();\n$rule              = 'points > 30';\n$context           = new Hoa\\Ruler\\Context();\n$context['points'] = 42;\n\n// Nothing in the database.\nif (null === $serialized = $database->get($ruleId)) {\n    // We transform the rule into an object model.\n    $model = Hoa\\Ruler\\Ruler::interpret($rule);\n\n    // We serialize and save the object model.\n    $database->save($ruleId, serialize($model));\n} else {\n    // We have a serialization! We unserialize it to get the object model.\n    $model = unserialize($serialized);\n}\n\n// We can assert by using a model instead of a rule!\nvar_dump(\n    $ruler->assert($model, $context)\n);\n\n/**\n * Will output:\n *     bool(true)\n */</code></pre>\n  <p>De cette manière, la règle n'est transformée en modèle objet\n  qu'<strong>une seule fois</strong> !</p>\n\n  <h3 id=\"Save_and_execute_PHP_code\" for=\"main-toc\">Enregistrer et exécute du\n  code PHP</h3>\n\n  <p>Une autre façon d'éviter la transformation d'une règle en modèle objet est\n  d'enregistrer le code PHP permettant de construire le modèle objet grâce à la\n  classe <code>Hoa\\Ruler\\Visitor\\Compiler</code>. Une fois ce code PHP\n  enregistré et exécuté, nous retrouverons notre modèle objet.</p>\n  <p>Toutefois, exécuter un tel code PHP s'avèrera légèrement plus lent et plus\n  difficile à mettre en œuvre que la technique précédente.</p>\n\n  <h2 id=\"Conclusion\" for=\"main-toc\">Conclusion</h2>\n\n  <p>La bibliothèque <code>Hoa\\Ruler</code> définit un <strong>langage</strong>\n  de règles métiers <strong>simple</strong> et inspiré du langage SQL dans sa\n  syntaxe. Le langage peut être <strong>transformé</strong> de plusieurs\n  façons : vers un modèle objet pour qu'il puisse être exécuté, ou à partir de\n  ce modèle, vers du code PHP ou vers le langage d'origine. L'instanciation des\n  variables dans ce langage se fait à travers un <strong>contexte</strong>.\n  Toutes ces opérations sont cachées à travers une interface simple et\n  claire.</p>\n  <p>La question des <strong>performances</strong> a été abordée et deux\n  solutions sont proposées. Aujourd'hui utilisé dans l'industrie sur de gros\n  projets, <code>Hoa\\Ruler</code> est capable de supporter de lourdes charges si\n  ces pratiques simples sont mises en œuvre.</p>\n\n</yield>\n</overlay>\n"
  },
  {
    "path": "DynamicCallable.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler;\n\nuse Hoa\\Consistency;\n\n/**\n * Class \\Hoa\\Ruler\\DynamicCallable.\n *\n * A callable without cache. Useful for the Context.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass DynamicCallable extends Consistency\\Xcallable\n{\n}\n"
  },
  {
    "path": "Exception/Asserter.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Exception;\n\n/**\n * Class \\Hoa\\Ruler\\Asserter.\n *\n * Extending the \\Hoa\\Ruler\\Exception class.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Asserter extends Exception\n{\n}\n"
  },
  {
    "path": "Exception/Exception.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Exception;\n\nuse Hoa\\Consistency;\nuse Hoa\\Exception as HoaException;\n\n/**\n * Class \\Hoa\\Ruler\\Exception.\n *\n * Extending the \\Hoa\\Exception\\Exception class.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Exception extends HoaException\n{\n}\n\n/**\n * Flex entity.\n */\nConsistency::flexEntity('Hoa\\Ruler\\Exception\\Exception');\n"
  },
  {
    "path": "Exception/Interpreter.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Exception;\n\n/**\n * Class \\Hoa\\Ruler\\Interpreter.\n *\n * Extending the \\Hoa\\Ruler\\Exception class.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Interpreter extends Exception\n{\n}\n"
  },
  {
    "path": "Grammar.pp",
    "content": "//\n// Hoa\n//\n//\n// @license\n//\n// New BSD License\n//\n// Copyright © 2007-2017, Hoa community. All rights reserved.\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are met:\n//     * Redistributions of source code must retain the above copyright\n//       notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above copyright\n//       notice, this list of conditions and the following disclaimer in the\n//       documentation and/or other materials provided with the distribution.\n//     * Neither the name of the Hoa nor the names of its contributors may be\n//       used to endorse or promote products derived from this software without\n//       specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n// POSSIBILITY OF SUCH DAMAGE.\n//\n// Grammar \\Hoa\\Ruler\\Grammar.\n//\n// Provide grammar for ruler.\n//\n// @copyright  Copyright © 2007-2017 Hoa community.\n// @license    New BSD License\n//\n\n\n%skip   space         \\s\n\n// Scalars.\n%token  true          (?i)true\n%token  false         (?i)false\n%token  null          (?i)null\n\n// Logical operators\n%token  not           (?i)not\\b\n%token  and           (?i)and\\b\n%token  or            (?i)or\\b\n%token  xor           (?i)xor\\b\n\n// Value\n%token  string        (\"|')(.*?)(?<!\\\\)\\1\n%token  float         \\d+\\.\\d+\n%token  integer       \\d+\n%token  parenthesis_  \\(\n%token _parenthesis   \\)\n%token  bracket_      \\[\n%token _bracket       \\]\n%token  comma          ,\n%token  dot           \\.\n\n%token  identifier    [^\\s\\(\\)\\[\\],\\.]+\n\n#expression:\n    logical_operation_primary()\n\nlogical_operation_primary:\n    logical_operation_secondary()\n    ( ( ::or:: #or | ::xor:: #xor ) logical_operation_primary() )?\n\nlogical_operation_secondary:\n    operation()\n    ( ::and:: #and logical_operation_secondary() )?\n\noperation:\n    operand() ( <identifier> logical_operation_primary() #operation )?\n\noperand:\n    ::parenthesis_:: logical_operation_primary() ::_parenthesis::\n  | value()\n\nvalue:\n    ::not:: logical_operation_primary() #not\n  | <true> | <false> | <null> | <float> | <integer> | <string>\n  | array_declaration()\n  | chain()\n\nchain:\n    ( variable() | function_call() )\n    ( ( array_access() | object_access() ) #variable_access )*\n\nvariable:\n    <identifier>\n\n#array_access:\n    ::bracket_:: value() ::_bracket::\n\nobject_access:\n    ::dot:: ( <identifier> #attribute_access | function_call() #method_access )\n\n#array_declaration:\n    ::bracket_:: value() ( ::comma:: value() )* ::_bracket::\n\n#function_call:\n    <identifier> ::parenthesis_::\n    ( logical_operation_primary() ( ::comma:: logical_operation_primary() )* )?\n    ::_parenthesis::\n"
  },
  {
    "path": "Model/Bag/Bag.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Model\\Bag;\n\nuse Hoa\\Consistency;\nuse Hoa\\Visitor;\n\n/**\n * Class \\Hoa\\Ruler\\Model\\Bag.\n *\n * Generic bag.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nabstract class Bag implements Visitor\\Element\n{\n    /**\n     * Accept a visitor.\n     *\n     * @param   \\Hoa\\Visitor\\Visit  $visitor    Visitor.\n     * @param   mixed               &$handle    Handle (reference).\n     * @param   mixed               $eldnah     Handle (no reference).\n     * @return  mixed\n     */\n    public function accept(\n        Visitor\\Visit $visitor,\n        &$handle = null,\n        $eldnah = null\n    ) {\n        return $visitor->visit($this, $handle, $eldnah);\n    }\n}\n\n/**\n * Flex entity.\n */\nConsistency::flexEntity('Hoa\\Ruler\\Model\\Bag\\Bag');\n"
  },
  {
    "path": "Model/Bag/Context.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Model\\Bag;\n\nuse Hoa\\Ruler;\n\n/**\n * Class \\Hoa\\Ruler\\Model\\Bag\\Context.\n *\n * Bag for context, i.e. a variable.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Context extends Bag\n{\n    /**\n     * Access type.\n     *\n     * @const int\n     */\n    const ACCESS_TYPE      = 0;\n\n    /**\n     * Access value.\n     *\n     * @const int\n     */\n    const ACCESS_VALUE     = 1;\n\n    /**\n     * Type: array access.\n     *\n     * @const int\n     */\n    const ARRAY_ACCESS     = 0;\n\n    /**\n     * Type: attribute access.\n     *\n     * @const int\n     */\n    const ATTRIBUTE_ACCESS = 1;\n\n    /**\n     * Type: method access.\n     *\n     * @const int\n     */\n    const METHOD_ACCESS    = 2;\n\n    /**\n     * ID.\n     *\n     * @var string\n     */\n    protected $_id         = null;\n\n    /**\n     * Index and object accesses.\n     *\n     * @var array\n     */\n    protected $_dimensions = [];\n\n\n\n    /**\n     * Constructor.\n     *\n     * @param   string  $id    ID.\n     */\n    public function __construct($id)\n    {\n        $this->_id = $id;\n\n        return;\n    }\n\n    /**\n     * Call an index (variable[indexA][indexB][indexC]).\n     *\n     * @param   mixed  $index    Index (a bag, a scalar or an array).\n     * @return  \\Hoa\\Ruler\\Model\\Bag\\Context\n     */\n    public function index($index)\n    {\n        if (is_scalar($index) || null === $index) {\n            $index = new Scalar($index);\n        } elseif (is_array($index)) {\n            $index = new RulerArray($index);\n        }\n\n        $this->_dimensions[] = [\n            static::ACCESS_TYPE  => static::ARRAY_ACCESS,\n            static::ACCESS_VALUE => $index\n        ];\n\n        return $this;\n    }\n\n    /**\n     * Call an attribute (variable.attrA.attrB).\n     *\n     * @param   string  $attribute    Attribute name.\n     * @return  \\Hoa\\Ruler\\Model\\Bag\\Context\n     */\n    public function attribute($attribute)\n    {\n        $this->_dimensions[] = [\n            static::ACCESS_TYPE  => static::ATTRIBUTE_ACCESS,\n            static::ACCESS_VALUE => $attribute\n        ];\n\n        return $this;\n    }\n\n    /**\n     * Call a method (variable.foo().bar().baz()).\n     *\n     * @param   \\Hoa\\Ruler\\Model\\Operator  $method    Method to call.\n     * @return  \\Hoa\\Ruler\\Model\\Bag\\Context\n     */\n    public function call(Ruler\\Model\\Operator $method)\n    {\n        $this->_dimensions[] = [\n            static::ACCESS_TYPE  => static::METHOD_ACCESS,\n            static::ACCESS_VALUE => $method\n        ];\n\n        return $this;\n    }\n\n    /**\n     * Get all dimensions.\n     *\n     * @return  array\n     */\n    public function getDimensions()\n    {\n        return $this->_dimensions;\n    }\n\n    /**\n     * Get ID.\n     *\n     * @return  string\n     */\n    public function getId()\n    {\n        return $this->_id;\n    }\n}\n"
  },
  {
    "path": "Model/Bag/RulerArray.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Model\\Bag;\n\n/**\n * Class \\Hoa\\Ruler\\Model\\Bag\\RulerArray.\n *\n * Bag for an array.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass RulerArray extends Bag\n{\n    /**\n     * Array.\n     *\n     * @var array\n     */\n    protected $_array = null;\n\n\n\n    /**\n     * Constructor.\n     *\n     * @param   array  $data    Data.\n     */\n    public function __construct(array $data)\n    {\n        foreach ($data as &$datum) {\n            if ($datum instanceof Bag) {\n                continue;\n            }\n\n            if (is_scalar($datum) || null === $datum) {\n                $datum = new Scalar($datum);\n            } elseif (is_array($datum)) {\n                $datum = new static($datum);\n            }\n        }\n\n        $this->_array = $data;\n\n        return;\n    }\n\n    /**\n     * Get array.\n     *\n     * @return  array\n     */\n    public function getArray()\n    {\n        return $this->_array;\n    }\n}\n"
  },
  {
    "path": "Model/Bag/Scalar.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Model\\Bag;\n\n/**\n * Class \\Hoa\\Ruler\\Model\\Bag\\Scalar.\n *\n * Bag for a scalar.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Scalar extends Bag\n{\n    /**\n     * Value.\n     *\n     * @var scalar\n     */\n    protected $_value = null;\n\n\n\n    /**\n     * Constructor.\n     *\n     * @param   string  $value    Value.\n     */\n    public function __construct($value)\n    {\n        $this->_value = $value;\n\n        return;\n    }\n\n    /**\n     * Get content of the bag.\n     *\n     * @return  scalar\n     */\n    public function getValue()\n    {\n        return $this->_value;\n    }\n}\n"
  },
  {
    "path": "Model/Model.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Model;\n\nuse Hoa\\Consistency;\nuse Hoa\\Ruler;\nuse Hoa\\Visitor;\n\n/**\n * Class \\Hoa\\Ruler\\Model.\n *\n * Root of the model, allow to declare everything.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Model implements Visitor\\Element\n{\n    /**\n     * Root.\n     *\n     * @var \\Hoa\\Ruler\\Model\\Operator\n     */\n    protected $_root            = null;\n\n    /**\n     * Compiler.\n     *\n     * @var \\Hoa\\Ruler\\Visitor\\Compiler\n     */\n    protected static $_compiler = null;\n\n\n\n    /**\n     * Set the expression with $name = 'expression'.\n     *\n     * @param   string  $name     Name.\n     * @param   mixed   $value    Value.\n     * @return  void\n     */\n    public function __set($name, $value)\n    {\n        if ('expression' !== $name) {\n            return $this->$name = $value;\n        }\n\n        if (is_scalar($value)) {\n            $value = new Bag\\Scalar($value);\n        } elseif (is_array($value)) {\n            $value = new Bag\\RulerArray($value);\n        }\n\n        $this->_root = $value;\n\n        return;\n    }\n\n    /**\n     * Get the expression.\n     *\n     * @return  \\Hoa\\Ruler\\Model\\Operator\n     */\n    public function getExpression()\n    {\n        return $this->_root;\n    }\n\n    /**\n     * Declare a function.\n     *\n     * @param   string  $name         Name.\n     * @param   mixed   …\n     * @return  \\Hoa\\Ruler\\Model\\Operator\n     */\n    public function func()\n    {\n        $arguments = func_get_args();\n        $name      = array_shift($arguments);\n\n        return $this->_operator($name, $arguments, true);\n    }\n\n    /**\n     * Declare an operation.\n     *\n     * @param   string  $name         Name.\n     * @param   array   $arguments    Arguments.\n     * @return  \\Hoa\\Ruler\\Model\\Operator\n     */\n    public function operation($name, array $arguments)\n    {\n        return $this->_operator($name, $arguments, false);\n    }\n\n    /**\n     * Create an operator object.\n     *\n     * @param   string  $name          Name.\n     * @param   array   $arguments     Arguments.\n     * @param   bool    $isFunction    Whether it is a function or not.\n     * @return  \\Hoa\\Ruler\\Model\\Operator\n     */\n    public function _operator($name, array $arguments, $isFunction)\n    {\n        return new Operator(mb_strtolower($name), $arguments, $isFunction);\n    }\n\n    /**\n     * Declare an operation.\n     *\n     * @param   string  $name         Name.\n     * @param   array   $arguments    Arguments.\n     * @return  \\Hoa\\Ruler\\Model\\Operator\n     */\n    public function __call($name, array $arguments)\n    {\n        return $this->operation($name, $arguments);\n    }\n\n    /**\n     * Declare a variable.\n     *\n     * @parma   string  $id    ID.\n     * @return  \\Hoa\\Ruler\\Model\\Bag\\Context\n     */\n    public function variable($id)\n    {\n        return new Bag\\Context($id);\n    }\n\n    /**\n     * Accept a visitor.\n     *\n     * @param   \\Hoa\\Visitor\\Visit  $visitor    Visitor.\n     * @param   mixed               &$handle    Handle (reference).\n     * @param   mixed               $eldnah     Handle (no reference).\n     * @return  mixed\n     */\n    public function accept(\n        Visitor\\Visit $visitor,\n        &$handle = null,\n        $eldnah  = null\n    ) {\n        return $visitor->visit($this, $handle, $eldnah);\n    }\n\n    /**\n     * Transform the object as a string.\n     *\n     * @return  string\n     */\n    public function __toString()\n    {\n        if (null === static::$_compiler) {\n            static::$_compiler = new Ruler\\Visitor\\Compiler();\n        }\n\n        return static::$_compiler->visit($this);\n    }\n}\n\n/**\n * Flex entity.\n */\nConsistency::flexEntity('Hoa\\Ruler\\Model\\Model');\n"
  },
  {
    "path": "Model/Operator.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Model;\n\nuse Hoa\\Ruler;\nuse Hoa\\Visitor;\n\n/**\n * Class \\Hoa\\Ruler\\Model\\Operator.\n *\n * Represent an operator or a function (in prefixed notation).\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass          Operator\n    extends    Ruler\\Model\\Bag\\Context\n    implements Visitor\\Element\n{\n    /**\n     * Lazy evaluation should break.\n     *\n     * @const bool\n     */\n    const LAZY_BREAK    = false;\n\n    /**\n     * Lazy evaluation should continue.\n     *\n     * @const bool\n     */\n    const LAZY_CONTINUE = true;\n\n    /**\n     * Name.\n     *\n     * @var string\n     */\n    protected $_name      = null;\n\n    /**\n     * Arguments.\n     *\n     * @var array\n     */\n    protected $_arguments = null;\n\n    /**\n     * Whether the operator is a function.\n     *\n     * @var bool\n     */\n    protected $_function  = true;\n\n    /**\n     * Whether the operator is lazy or not.\n     *\n     * @var bool\n     */\n    protected $_laziness = false;\n\n    /**\n     * Constructor.\n     *\n     * @param   string  $name          Name.\n     * @param   array   $arguments     Arguments.\n     * @param   bool    $isFunction    Whether it is a function.\n     */\n    public function __construct(\n        $name,\n        array $arguments = [],\n        $isFunction      = true\n    ) {\n        $this->setName($name);\n        $this->setArguments($arguments);\n        $this->setFunction($isFunction);\n\n        return;\n    }\n\n    /**\n     * Set name.\n     *\n     * @param   string  $name    Name.\n     * @return  string\n     */\n    protected function setName($name)\n    {\n        $old         = $this->_name;\n        $this->_name = $name;\n\n        $this->setLaziness('and' === $name || 'or' === $name);\n\n        return $old;\n    }\n\n    /**\n     * Get name.\n     *\n     * @return  string\n     */\n    public function getName()\n    {\n        return $this->_name;\n    }\n\n    /**\n     * Set arguments.\n     *\n     * @param   array  $arguments    Arguments.\n     * @return  array\n     */\n    protected function setArguments(array $arguments)\n    {\n        foreach ($arguments as &$argument) {\n            if (is_scalar($argument) || null === $argument) {\n                $argument = new Bag\\Scalar($argument);\n            } elseif (is_array($argument)) {\n                $argument = new Bag\\RulerArray($argument);\n            }\n        }\n\n        $old              = $this->_arguments;\n        $this->_arguments = $arguments;\n\n        return $old;\n    }\n\n    /**\n     * Get arguments.\n     *\n     * @return  array\n     */\n    public function getArguments()\n    {\n        return $this->_arguments;\n    }\n\n    /**\n     * Set whether the operator is a function or not.\n     *\n     * @param   bool  $isFunction    Is a function or not.\n     * @return  bool\n     */\n    protected function setFunction($isFunction)\n    {\n        $old             = $this->_function;\n        $this->_function = $isFunction;\n\n        return $old;\n    }\n\n    /**\n     * Check if the operator is a function or not.\n     *\n     * @return  bool\n     */\n    public function isFunction()\n    {\n        return $this->_function;\n    }\n\n    /**\n     * Set whether the operator is lazy or not.\n     *\n     * @param   bool  $isLazy    Is a lazy operator or not.\n     * @return  bool\n     */\n    protected function setLaziness($isLazy)\n    {\n        $old             = $this->_laziness;\n        $this->_laziness = $isLazy;\n\n        return $old;\n    }\n\n    /**\n     * Check if the operator is lazy or not.\n     *\n     * @return  bool\n     */\n    public function isLazy()\n    {\n        return $this->_laziness;\n    }\n\n    /**\n     * Check whether we should break the lazy evaluation or not.\n     *\n     * @param  mixed  $value    Value to check.\n     * @return bool\n     */\n    public function shouldBreakLazyEvaluation($value)\n    {\n        switch ($this->getName()) {\n            case 'and':\n                if (false === $value) {\n                    return self::LAZY_BREAK;\n                }\n\n                break;\n\n            case 'or':\n                if (true === $value) {\n                    return self::LAZY_BREAK;\n                }\n\n                break;\n        }\n\n        return self::LAZY_CONTINUE;\n    }\n\n    /**\n     * Check if the operator is a token of the grammar or not.\n     *\n     * @param   string  $operator    Operator.\n     * @return  bool\n     */\n    public static function isToken($operator)\n    {\n        static $_tokens = ['not', 'and', 'or', 'xor'];\n\n        return true === in_array($operator, $_tokens);\n    }\n\n    /**\n     * Accept a visitor.\n     *\n     * @param   \\Hoa\\Visitor\\Visit  $visitor    Visitor.\n     * @param   mixed               &$handle    Handle (reference).\n     * @param   mixed               $eldnah     Handle (no reference).\n     * @return  mixed\n     */\n    public function accept(\n        Visitor\\Visit $visitor,\n        &$handle = null,\n        $eldnah  = null\n    ) {\n        return $visitor->visit($this, $handle, $eldnah);\n    }\n}\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <img src=\"https://static.hoa-project.net/Image/Hoa.svg\" alt=\"Hoa\" width=\"250px\" />\n</p>\n\n---\n\n<p align=\"center\">\n  <a href=\"https://travis-ci.org/hoaproject/Ruler\"><img src=\"https://img.shields.io/travis/hoaproject/Ruler/master.svg\" alt=\"Build status\" /></a>\n  <a href=\"https://coveralls.io/github/hoaproject/Ruler?branch=master\"><img src=\"https://img.shields.io/coveralls/hoaproject/Ruler/master.svg\" alt=\"Code coverage\" /></a>\n  <a href=\"https://packagist.org/packages/hoa/ruler\"><img src=\"https://img.shields.io/packagist/dt/hoa/ruler.svg\" alt=\"Packagist\" /></a>\n  <a href=\"https://hoa-project.net/LICENSE\"><img src=\"https://img.shields.io/packagist/l/hoa/ruler.svg\" alt=\"License\" /></a>\n</p>\n<p align=\"center\">\n  Hoa is a <strong>modular</strong>, <strong>extensible</strong> and\n  <strong>structured</strong> set of PHP libraries.<br />\n  Moreover, Hoa aims at being a bridge between industrial and research worlds.\n</p>\n\n# Hoa\\Ruler\n\n[![Help on IRC](https://img.shields.io/badge/help-%23hoaproject-ff0066.svg)](https://webchat.freenode.net/?channels=#hoaproject)\n[![Help on Gitter](https://img.shields.io/badge/help-gitter-ff0066.svg)](https://gitter.im/hoaproject/central)\n[![Documentation](https://img.shields.io/badge/documentation-hack_book-ff0066.svg)](https://central.hoa-project.net/Documentation/Library/Ruler)\n[![Board](https://img.shields.io/badge/organisation-board-ff0066.svg)](https://waffle.io/hoaproject/ruler)\n\nThis library allows to manipulate a rule engine. Rules can be written\nby using a dedicated language, very close to SQL. Therefore, they can\nbe written by a user and saved in a database.\n\nSuch rules are useful, for example, for commercial solutions that need\nto manipulate promotion or special offer rules written by a user. To\nquote [Wikipedia](https://en.wikipedia.org/wiki/Business_rules_engine):\n\n> A business rules engine is a software system that executes one or more\n> business rules in a runtime production environment. The rules might come from\n> legal regulation (“An employee can be fired for any reason or no reason but\n> not for an illegal reason”), company policy (“All customers that spend more\n> than $100 at one time will receive a 10% discount”), or other sources. A\n> business rule system enables these company policies and other operational\n> decisions to be defined, tested, executed and maintained separately from\n> application code.\n\n[Learn more](https://central.hoa-project.net/Documentation/Library/Ruler).\n\n## Installation\n\nWith [Composer](https://getcomposer.org/), to include this library into\nyour dependencies, you need to\nrequire [`hoa/ruler`](https://packagist.org/packages/hoa/ruler):\n\n```sh\n$ composer require hoa/ruler '~2.0'\n```\n\nFor more installation procedures, please read [the Source\npage](https://hoa-project.net/Source.html).\n\n## Testing\n\nBefore running the test suites, the development dependencies must be installed:\n\n```sh\n$ composer install\n```\n\nThen, to run all the test suites:\n\n```sh\n$ vendor/bin/hoa test:run\n```\n\nFor more information, please read the [contributor\nguide](https://hoa-project.net/Literature/Contributor/Guide.html).\n\n## Quick usage\n\nAs a quick overview, we propose to see a very simple example that manipulates a\nsimple rule with a simple context. After, we will add a new operator in the\nrule. And finally, we will see how to save a rule in a database.\n\n### Three steps\n\nSo first, we create a context with two variables: `group` and `points`, and we\nthen assert a rule. A context holds values to concretize a rule. A value can\nalso be the result of a callable. Thus:\n\n```php\n$ruler = new Hoa\\Ruler\\Ruler();\n\n// 1. Write a rule.\n$rule  = 'group in [\"customer\", \"guest\"] and points > 30';\n\n// 2. Create a context.\n$context           = new Hoa\\Ruler\\Context();\n$context['group']  = 'customer';\n$context['points'] = function () {\n    return 42;\n};\n\n// 3. Assert!\nvar_dump(\n    $ruler->assert($rule, $context)\n);\n\n/**\n * Will output:\n *     bool(true)\n */\n```\n\nIn the next example, we have a `User` object and a context that is populated\ndynamically (when the `user` variable is concretized, two new variables, `group`\nand `points` are created). Moreover, we will create a new operator/function\ncalled `logged`. There is no difference between an operator and a function\nexcept that an operator has two operands (so arguments).\n\n### Adding operators and functions\n\nFor now, we have the following operators/functions by default: `and`, `or`,\n`xor`, `not`, `=` (`is` as an alias), `!=`, `>`, `>=`, `<`, `<=`, `in` and\n`sum`. We can add our own by different way. The simplest and volatile one is\ngiven in the following example. Thus:\n\n```php\n// The User object.\nclass User\n{\n    const DISCONNECTED = 0;\n    const CONNECTED    = 1;\n\n    public $group      = 'customer';\n    public $points     = 42;\n    protected $_status = 1;\n\n    public function getStatus()\n    {\n        return $this->_status;\n    }\n}\n\n$ruler = new Hoa\\Ruler\\Ruler();\n\n// New rule.\n$rule  = 'logged(user) and group in [\"customer\", \"guest\"] and points > 30';\n\n// New context.\n$context         = new Hoa\\Ruler\\Context();\n$context['user'] = function () use ($context) {\n    $user              = new User();\n    $context['group']  = $user->group;\n    $context['points'] = $user->points;\n\n    return $user;\n};\n\n// We add the logged() operator.\n$ruler->getDefaultAsserter()->setOperator('logged', function (User $user) {\n    return $user::CONNECTED === $user->getStatus();\n});\n\n// Finally, we assert the rule.\nvar_dump(\n    $ruler->assert($rule, $context)\n);\n\n/**\n * Will output:\n *     bool(true)\n */\n```\n\nAlso, if a variable in the context is an array, we can access to its values from\na rule with the same syntax as PHP. For example, if the `a` variable is an\narray, we can write `a[0]` to access to the value associated to the `0` key. It\nworks as an hashmap (PHP array implementation), so we can have strings & co. as\nkeys. In the same way, if a variable is an object, we can call a method on it.\nFor example, if the `a` variable is an array where the value associated to the\nfirst key is an object with a `foo` method, we can write: `a[0].foo(b)` where\n`b` is another variable in the context. Also, we can access to the public\nattributes of an object. Obviously, we can mixe array and object accesses.\nPlease, take a look at the grammar (`hoa://Library/Ruler/Grammar.pp`) to see all\nthe possible constructions.\n\n### Saving a rule\n\nNow, we have two options to save the rule, for example, in a database. Either we\nsave the rule as a string directly, or we will save the serialization of the\nrule which will avoid further interpretations. In the next example, we see how\nto serialize and unserialize a rule by using the `Hoa\\Ruler\\Ruler::interpret`\nstatic method:\n\n```php\n$database->save(\n    serialize(\n        Hoa\\Ruler\\Ruler::interpret(\n            'logged(user) and group in [\"customer\", \"guest\"] and points > 30'\n        )\n    )\n);\n```\n\nAnd for next executions:\n\n```php\n$rule = unserialize($database->read());\nvar_dump(\n    $ruler->assert($rule, $context)\n);\n```\n\nWhen a rule is interpreted, its object model is created. We serialize and\nunserialize this model. To see the PHP code needed to create such a model, we\ncan print the model itself (as an example). Thus:\n\n```php\necho Hoa\\Ruler\\Ruler::interpret(\n    'logged(user) and group in [\"customer\", \"guest\"] and points > 30'\n);\n\n/**\n * Will output:\n *     $model = new \\Hoa\\Ruler\\Model();\n *     $model->expression =\n *         $model->and(\n *             $model->func(\n *                 'logged',\n *                 $model->variable('user')\n *             ),\n *             $model->and(\n *                 $model->in(\n *                     $model->variable('group'),\n *                     [\n *                         'customer',\n *                         'guest'\n *                     ]\n *                 ),\n *                 $model->{'>'}(\n *                     $model->variable('points'),\n *                     30\n *                 )\n *             )\n *         );\n */\n```\n\nHave fun!\n\n## Documentation\n\nThe\n[hack book of `Hoa\\Ruler`](https://central.hoa-project.net/Documentation/Library/Ruler) contains\ndetailed information about how to use this library and how it works.\n\nTo generate the documentation locally, execute the following commands:\n\n```sh\n$ composer require --dev hoa/devtools\n$ vendor/bin/hoa devtools:documentation --open\n```\n\nMore documentation can be found on the project's website:\n[hoa-project.net](https://hoa-project.net/).\n\n## Getting help\n\nThere are mainly two ways to get help:\n\n  * On the [`#hoaproject`](https://webchat.freenode.net/?channels=#hoaproject)\n    IRC channel,\n  * On the forum at [users.hoa-project.net](https://users.hoa-project.net).\n\n## Contribution\n\nDo you want to contribute? Thanks! A detailed [contributor\nguide](https://hoa-project.net/Literature/Contributor/Guide.html) explains\neverything you need to know.\n\n## License\n\nHoa is under the New BSD License (BSD-3-Clause). Please, see\n[`LICENSE`](https://hoa-project.net/LICENSE) for details.\n\n## Related projects\n\nThe following projects are using this library:\n\n  * [RulerZ](https://github.com/K-Phoen/rulerz), Powerful implementation of the\n    Specification pattern in PHP,\n  * [ownCloud](https://owncloud.org/), A safe home for all your data,\n  * [PhpMetrics](http://www.phpmetrics.org/), Static analysis tool for PHP,\n  * [`hiqdev/php-billing`](https://github.com/hiqdev/php-billing), a billing library in PHP \n  * [`atoum/ruler-extension`](https://github.com/atoum/ruler-extension), This\n    extension allows to filter test results in [atoum](http://atoum.org/).\n"
  },
  {
    "path": "Ruler.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler;\n\nuse Hoa\\Compiler;\nuse Hoa\\Consistency;\nuse Hoa\\File;\nuse Hoa\\Visitor as HoaVisitor;\n\n/**\n * Class \\Hoa\\Ruler.\n *\n * Ruler helpers.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Ruler\n{\n    /**\n     * Compiler.\n     *\n     * @var \\Hoa\\Compiler\\Llk\\Parser\n     */\n    protected static $_compiler        = null;\n\n    /**\n     * Interpreter.\n     *\n     * @var \\Hoa\\Ruler\\Visitor\\Interpreter\n     */\n    protected static $_interpreter     = null;\n\n    /**\n     * Default asserter.\n     *\n     * @var \\Hoa\\Visitor\\Visit\n     */\n    protected $_asserter               = null;\n\n    /**\n     * Default asserter.\n     *\n     * @var \\Hoa\\Ruler\\Visitor\\Asserter\n     */\n    protected static $_defaultAsserter = null;\n\n\n\n    /**\n     * Assert.\n     *\n     * @param   mixed                 $rule       Rule (string or model)\n     * @param   \\Hoa\\Ruler\\Context    $context    Context.\n     * @return  bool\n     * @throws  \\Hoa\\Ruler\\Exception\n     */\n    public function assert($rule, Context $context = null)\n    {\n        if (is_string($rule)) {\n            $rule = static::interpret($rule);\n        }\n\n        if (null === $context) {\n            $context = new Context();\n        }\n\n        return $this->getAsserter($context)->visit($rule);\n    }\n\n    /**\n     * Short interpreter.\n     *\n     * @param   string  $rule    Rule.\n     * @return  \\Hoa\\Ruler\\Model\n     * @throws  \\Hoa\\Ruler\\Exception\n     */\n    public static function interpret($rule)\n    {\n        return static::getInterpreter()->visit(\n            static::getCompiler()->parse($rule)\n        );\n    }\n\n    /**\n     * Get interpreter.\n     *\n     * @return  \\Hoa\\Ruler\\Visitor\\Interpreter\n     */\n    public static function getInterpreter()\n    {\n        if (null === static::$_interpreter) {\n            static::$_interpreter = new Visitor\\Interpreter();\n        }\n\n        return static::$_interpreter;\n    }\n\n    /**\n     * Set current asserter.\n     *\n     * @param   \\Hoa\\Visitor\\Visit  $visitor    Visitor.\n     * @return  \\Hoa\\Visitor\\Visit\n     */\n    public function setAsserter(HoaVisitor\\Visit $visitor)\n    {\n        $old             = $this->_asserter;\n        $this->_asserter = $visitor;\n\n        return $old;\n    }\n\n    /**\n     * Get asserter.\n     *\n     * @param   \\Hoa\\Ruler\\Context  $context    Context.\n     * @return  \\Hoa\\Visitor\\Visit\n     */\n    public function getAsserter(Context $context = null)\n    {\n        if (null === $asserter = $this->_asserter) {\n            return static::getDefaultAsserter($context);\n        }\n\n        if (null !== $context) {\n            $asserter->setContext($context);\n        }\n\n        return $asserter;\n    }\n\n    /**\n     * Get default asserter.\n     *\n     * @param   \\Hoa\\Ruler\\Context    $context    Context.\n     * @return  \\Hoa\\Ruler\\Visitor\\Asserter\n     */\n    public static function getDefaultAsserter(Context $context = null)\n    {\n        if (null === static::$_defaultAsserter) {\n            static::$_defaultAsserter = new Visitor\\Asserter($context);\n        }\n\n        if (null !== $context) {\n            static::$_defaultAsserter->setContext($context);\n        }\n\n        return static::$_defaultAsserter;\n    }\n\n    /**\n     * Get compiler.\n     *\n     * @return  \\Hoa\\Compiler\\Llk\\Parser\n     */\n    public static function getCompiler()\n    {\n        if (null === static::$_compiler) {\n            static::$_compiler = Compiler\\Llk::load(\n                new File\\Read('hoa://Library/Ruler/Grammar.pp')\n            );\n        }\n\n        return static::$_compiler;\n    }\n}\n\n/**\n * Flex entity.\n */\nConsistency::flexEntity('Hoa\\Ruler\\Ruler');\n"
  },
  {
    "path": "Test/Integration/Documentation.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Test\\Integration;\n\nuse Hoa\\Ruler as LUT;\nuse Hoa\\Test;\n\n/**\n * Class \\Hoa\\Ruler\\Test\\Integration\\Documentation.\n *\n * Test suite of the examples in the documentation.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Documentation extends Test\\Integration\\Suite implements Test\\Decorrelated\n{\n    public function case_classical()\n    {\n        $this\n            ->given(\n                $ruler = new LUT(),\n                $rule  = 'group in [\"customer\", \"guest\"] and points > 30'\n            );\n\n        $this->next_case_classical($ruler, $rule);\n    }\n\n    public function next_case_classical($ruler, $rule)\n    {\n        $this\n            ->given(\n                $context           = new LUT\\Context(),\n                $context['group']  = $this->sample(\n                    $this->realdom->regex('/customer|guest/')\n                ),\n                $context['points'] = function () {\n                    return 42;\n                }\n            )\n            ->when($result = $ruler->assert($rule, $context))\n            ->then\n                ->boolean($result)\n                    ->isTrue()\n\n            ->given($context['points'] = 29)\n            ->when($result = $ruler->assert($rule, $context))\n            ->then\n                ->boolean($result)\n                    ->isFalse();\n    }\n\n    public function case_new_operators()\n    {\n        $this\n            ->given(\n                $user            = new \\Mock\\StdClass(),\n                $user->group     = 'customer',\n                $user->points    = 42,\n                $user->status    = true,\n                $ruler           = new LUT(),\n                $rule            = 'logged(user) and group in [\"customer\", \"guest\"] and points > 30',\n                $context         = new LUT\\Context(),\n                $context['user'] = function () use ($user, $context) {\n                    $context['group']  = $user->group;\n                    $context['points'] = $user->points;\n\n                    return $user;\n                }\n            )\n            ->when(\n                $ruler->getDefaultAsserter()->setOperator('logged', function ($user) {\n                    return $user->status;\n                }),\n                $result = $ruler->assert($rule, $context)\n            )\n            ->then\n                ->boolean($result)\n                    ->isTrue()\n\n            ->given($user->status = false)\n            ->when($result = $ruler->assert($rule, $context))\n            ->then\n                ->boolean($result)\n                    ->isFalse();\n    }\n\n    public function case_interpret()\n    {\n        $this\n            ->given(\n                $model = LUT::interpret('group in [\"customer\", \"guest\"] and points > 30')\n            )\n            ->when($ledom = unserialize(serialize($model)))\n            ->then\n                ->object($model)\n                    ->isEqualTo($ledom);\n\n        $this->next_case_classical(new LUT(), $model);\n    }\n\n    public function case_compile()\n    {\n        $expectedResult = <<<'RESULT'\n$model = new \\Hoa\\Ruler\\Model();\n$model->expression =\n    $model->and(\n        $model->func(\n            'logged',\n            $model->variable('user')\n        ),\n        $model->and(\n            $model->in(\n                $model->variable('group'),\n                [\n                    'customer',\n                    'guest'\n                ]\n            ),\n            $model->{'>'}(\n                $model->variable('points'),\n                30\n            )\n        )\n    );\nRESULT;\n\n        $this\n            ->when($result = LUT::interpret(\n                'logged(user) and group in [\"customer\", \"guest\"] and points > 30'\n            ) . '')\n            ->then\n                ->string($result)\n                    ->isEqualTo($expectedResult);\n    }\n}\n"
  },
  {
    "path": "Test/Integration/Model/Operator.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Test\\Integration\\Model;\n\nuse Hoa\\Ruler as LUT;\nuse Hoa\\Test;\n\n/**\n * Class \\Hoa\\Ruler\\Test\\Integration\\Model\\Operator.\n *\n * Test suite of the operator object of the model.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Operator extends Test\\Integration\\Suite\n{\n    public function case_lazy_and()\n    {\n        $this\n            ->given(\n                $ruler     = new LUT(),\n                $fExecuted = false,\n                $gExecuted = false,\n                $asserter  = $ruler->getDefaultAsserter(),\n                $asserter->setOperator(\n                    'f',\n                    function ($a = false) use (&$fExecuted) {\n                        $fExecuted = true;\n\n                        return $a;\n                    }\n                ),\n                $asserter->setOperator(\n                    'g',\n                    function ($b = false) use (&$gExecuted) {\n                        $gExecuted = true;\n\n                        return $b;\n                    }\n                ),\n                $rule = 'f(false) and g(true)'\n            )\n            ->when($result = $ruler->assert($rule, new LUT\\Context()))\n            ->then\n                ->boolean($result)\n                    ->isFalse()\n                ->boolean($fExecuted)\n                    ->isTrue()\n                ->boolean($gExecuted)\n                    ->isFalse()\n\n            ->given(\n                $fExecuted = false,\n                $gExecuted = false,\n                $rule      = 'f(true) and g(true)'\n            )\n            ->when($result = $ruler->assert($rule, new LUT\\Context()))\n            ->then\n                ->boolean($result)\n                    ->isTrue()\n                ->boolean($fExecuted)\n                    ->isTrue()\n                ->boolean($gExecuted)\n                    ->isTrue()\n\n            ->given(\n                $fExecuted = false,\n                $gExecuted = false,\n                $rule      = 'f(true) and g(false)'\n            )\n            ->when($result = $ruler->assert($rule, new LUT\\Context()))\n            ->then\n                ->boolean($result)\n                    ->isFalse()\n                ->boolean($fExecuted)\n                    ->isTrue()\n                ->boolean($gExecuted)\n                    ->isTrue()\n\n            ->given(\n                $fExecuted = false,\n                $gExecuted = false,\n                $rule      = 'f(false) and g(false)'\n            )\n            ->when($result = $ruler->assert($rule, new LUT\\Context()))\n            ->then\n                ->boolean($result)\n                    ->isFalse()\n                ->boolean($fExecuted)\n                    ->isTrue()\n                ->boolean($gExecuted)\n                    ->isFalse();\n    }\n\n    public function case_lazy_or()\n    {\n        $this\n            ->given(\n                $ruler     = new LUT(),\n                $fExecuted = false,\n                $gExecuted = false,\n                $asserter  = $ruler->getDefaultAsserter(),\n                $asserter->setOperator(\n                    'f',\n                    function ($a) use (&$fExecuted) {\n                        $fExecuted = true;\n\n                        return $a;\n                    }\n                ),\n                $asserter->setOperator(\n                    'g',\n                    function ($b) use (&$gExecuted) {\n                        $gExecuted = true;\n\n                        return $b;\n                    }\n                ),\n                $rule = 'f(false) or g(true)'\n            )\n            ->when($result = $ruler->assert($rule, new LUT\\Context()))\n            ->then\n                ->boolean($result)\n                    ->isTrue()\n                ->boolean($fExecuted)\n                    ->isTrue()\n                ->boolean($gExecuted)\n                    ->isTrue()\n\n            ->given(\n                $fExecuted = false,\n                $gExecuted = false,\n                $rule      = 'f(true) or g(true)'\n            )\n            ->when($result = $ruler->assert($rule, new LUT\\Context()))\n            ->then\n                ->boolean($result)\n                    ->isTrue()\n                ->boolean($fExecuted)\n                    ->isTrue()\n                ->boolean($gExecuted)\n                    ->isFalse()\n\n            ->given(\n                $fExecuted = false,\n                $gExecuted = false,\n                $rule      = 'f(true) or g(false)'\n            )\n            ->when($result = $ruler->assert($rule, new LUT\\Context()))\n            ->then\n                ->boolean($result)\n                    ->isTrue()\n                ->boolean($fExecuted)\n                    ->isTrue()\n                ->boolean($gExecuted)\n                    ->isFalse()\n\n            ->given(\n                $fExecuted = false,\n                $gExecuted = false,\n                $rule      = 'f(false) or g(false)'\n            )\n            ->when($result = $ruler->assert($rule, new LUT\\Context()))\n            ->then\n                ->boolean($result)\n                    ->isFalse()\n                ->boolean($fExecuted)\n                    ->isTrue()\n                ->boolean($gExecuted)\n                    ->isTrue();\n    }\n}\n"
  },
  {
    "path": "Test/Unit/Context.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Test\\Unit;\n\nuse Hoa\\Ruler as LUT;\nuse Hoa\\Ruler\\Context as CUT;\nuse Hoa\\Test;\n\n/**\n * Class \\Hoa\\Ruler\\Test\\Unit\\Context.\n *\n * Test suite of the context.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Context extends Test\\Unit\\Suite\n{\n    public function case_array_access()\n    {\n        $this\n            ->when($context = new CUT())\n            ->then\n                ->object($context)\n                    ->isInstanceOf('ArrayAccess');\n    }\n\n    public function case_exists_set_get()\n    {\n        $this\n            ->given(\n                $key     = 'foo',\n                $value   = 'bar',\n                $context = new CUT()\n            )\n            ->then\n                ->boolean(isset($context[$key]))\n                    ->isFalse()\n\n            ->when($context[$key] = $value)\n            ->then\n                ->boolean(isset($context[$key]))\n                    ->isTrue()\n                ->string($context[$key])\n                    ->isEqualTo($value);\n    }\n\n    public function case_exception_when_getting_unspecified_key()\n    {\n        $this\n            ->given($context = new CUT())\n            ->exception(function () use ($context) {\n                $context['foo'];\n            })\n                ->isInstanceOf('Hoa\\Ruler\\Exception');\n    }\n\n    public function case_unset()\n    {\n        $this\n            ->given(\n                $key     = 'foo',\n                $value   = 'bar',\n                $context = new CUT()\n            )\n            ->then\n                ->boolean(isset($context[$key]))\n                    ->isFalse()\n\n            ->when($context[$key] = $value)\n            ->then\n                ->boolean(isset($context[$key]))\n                    ->isTrue()\n\n            ->when(function () use ($context, $key) {\n                unset($context[$key]);\n            })\n            ->then\n                ->boolean(isset($context[$key]))\n                    ->isFalse();\n    }\n\n    public function case_callable_closure()\n    {\n        $this\n            ->given(\n                $context        = new CUT(),\n                $context['foo'] = function () {\n                    return fakeCallable();\n                }\n            )\n            ->when($result = $context['foo'])\n            ->then\n                ->boolean($result)\n                    ->isTrue();\n    }\n\n    public function case_callable_user_function()\n    {\n        $this\n            ->given(\n                $context        = new CUT(),\n                $context['foo'] = __NAMESPACE__ . '\\fakeCallable'\n            )\n            ->when($result = $context['foo'])\n            ->then\n                ->boolean($result)\n                    ->isTrue();\n    }\n\n    public function case_callable_internal_function()\n    {\n        $this\n            ->given(\n                $context        = new CUT(),\n                $context['foo'] = 'var_dump'\n            )\n            ->when($result = $context['foo'])\n            ->then\n                ->string($result)\n                    ->isEqualTo('var_dump');\n    }\n\n    public function case_callable_method()\n    {\n        $this\n            ->given(\n                $context        = new CUT(),\n                $context['foo'] = [$this, 'fakeCallable']\n            )\n            ->when($result = $context['foo'])\n            ->then\n                ->boolean($result)\n                    ->isTrue();\n    }\n\n    public function case_callable_xcallable()\n    {\n        $this\n            ->given(\n                $context        = new CUT(),\n                $context['foo'] = xcallable($this, 'fakeCallable')\n            )\n            ->when($result = $context['foo'])\n            ->then\n                ->boolean($result)\n                    ->isTrue();\n    }\n\n    public function case_callable_cache()\n    {\n        $this\n            ->given(\n                $context        = new CUT(),\n                $context['foo'] = function () {\n                    static $i = 0;\n\n                    return $i++;\n                }\n            )\n            ->when($result = $context['foo'])\n            ->then\n                ->integer($result)\n                    ->isEqualTo(0)\n\n            ->when($result = $context['foo'])\n            ->then\n                ->integer($result)\n                    ->isEqualTo(0);\n    }\n\n    public function case_callable_no_cache()\n    {\n        $this\n            ->given(\n                $context        = new CUT(),\n                $context['foo'] = new LUT\\DynamicCallable(function () {\n                    static $i = 0;\n\n                    return $i++;\n                })\n            )\n            ->when($result = $context['foo'])\n            ->then\n                ->integer($result)\n                    ->isEqualTo(0)\n\n            ->when($result = $context['foo'])\n            ->then\n                ->integer($result)\n                    ->isEqualTo(1);\n    }\n\n    public function case_callable_argument()\n    {\n        $this\n            ->given(\n                $self           = $this,\n                $context        = new CUT(),\n                $context['foo'] = function () use ($self, $context) {\n                    $arguments = func_get_args();\n\n                    $self\n                        ->integer(count($arguments))\n                            ->isEqualTo(1)\n                        ->object($arguments[0])\n                            ->isIdenticalTo($context);\n                }\n            )\n            ->when($result = $context['foo']);\n    }\n\n    public function case_access_as_properties()\n    {\n        $this\n            ->given(\n                $self = $this,\n                $context = new CUT(),\n                $context['foo'] = 42\n            )\n            ->when($result = $context->foo)\n            ->then\n                ->integer($result)\n                    ->isEqualTo(42)\n            ->when($context->bar = 24)\n            ->then\n                ->integer($context['bar'])\n                    ->isEqualTo(24);\n    }\n\n    public function fakeCallable()\n    {\n        return fakeCallable();\n    }\n}\n\nfunction fakeCallable()\n{\n    return true;\n}\n"
  },
  {
    "path": "Test/Unit/DynamicCallable.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Test\\Unit;\n\nuse Hoa\\Consistency;\nuse Hoa\\Ruler\\DynamicCallable as SUT;\nuse Hoa\\Test;\n\n/**\n * Class \\Hoa\\Ruler\\Test\\Unit\\DynamicCallable.\n *\n * Test suite of the dynamic callable.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass DynamicCallable extends Test\\Unit\\Suite\n{\n    public function case_is_a_xcallable()\n    {\n        $this\n            ->when($result = new SUT(function () {}))\n            ->then\n                ->object($result)\n                    ->isInstanceOf(Consistency\\Xcallable::class);\n    }\n}\n"
  },
  {
    "path": "Test/Unit/Exception/Asserter.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Test\\Unit\\Exception;\n\nuse Hoa\\Ruler as LUT;\nuse Hoa\\Ruler\\Exception\\Asserter as SUT;\nuse Hoa\\Test;\n\n/**\n * Class \\Hoa\\Ruler\\Test\\Unit\\Exception\\Asserter.\n *\n * Test suite of the asserter exception.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Asserter extends Test\\Unit\\Suite\n{\n    public function case_is_an_exception()\n    {\n        $this\n            ->when($result = new SUT('foo'))\n            ->then\n                ->object($result)\n                    ->isInstanceOf(LUT\\Exception::class);\n    }\n}\n"
  },
  {
    "path": "Test/Unit/Exception/Exception.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Test\\Unit\\Exception;\n\nuse Hoa\\Exception as HoaException;\nuse Hoa\\Ruler\\Exception as SUT;\nuse Hoa\\Test;\n\n/**\n * Class \\Hoa\\Ruler\\Test\\Unit\\Exception\\Exception.\n *\n * Test suite of the main exception class.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Exception extends Test\\Unit\\Suite\n{\n    public function case_is_a_hoa_exception()\n    {\n        $this\n            ->when($result = new SUT('foo'))\n            ->then\n                ->object($result)\n                    ->isInstanceOf(HoaException::class);\n    }\n}\n"
  },
  {
    "path": "Test/Unit/Exception/Interpreter.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Test\\Unit\\Exception;\n\nuse Hoa\\Ruler as LUT;\nuse Hoa\\Ruler\\Exception\\Interpreter as SUT;\nuse Hoa\\Test;\n\n/**\n * Class \\Hoa\\Ruler\\Test\\Unit\\Exception\\Interpreter.\n *\n * Test suite of the asserter exception.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Interpreter extends Test\\Unit\\Suite\n{\n    public function case_is_an_exception()\n    {\n        $this\n            ->when($result = new SUT('foo'))\n            ->then\n                ->object($result)\n                    ->isInstanceOf(LUT\\Exception::class);\n    }\n}\n"
  },
  {
    "path": "Test/Unit/Issue.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Test\\Unit;\n\nuse Hoa\\Ruler as LUT;\nuse Hoa\\Test;\n\n/**\n * Class \\Hoa\\Ruler\\Test\\Unit\\Issue.\n *\n * Test suite of detected issues.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Issue extends Test\\Unit\\Suite implements Test\\Decorrelated\n{\n    public function case_github_50()\n    {\n        $this\n            ->given(\n                $ruler               = new LUT(),\n                $rule                = 'variable',\n                $context             = new LUT\\Context(),\n                $context['variable'] = 'file'\n            )\n            ->when(function () use ($ruler, $rule, $context) {\n                $ruler->assert($rule, $context);\n            })\n                ->error()\n                    ->notExists();\n    }\n\n    public function case_github_70()\n    {\n        $this\n            ->given(\n                $ruler               = new LUT(),\n                $rule                = 'variable[\"foo\"] is null',\n                $context             = new LUT\\Context(),\n                $context['variable'] = ['foo' => null]\n            )\n            ->when($result = $ruler->assert($rule, $context))\n            ->then\n                ->boolean($result)\n                    ->isTrue();\n    }\n\n    public function case_github_100_1()\n    {\n        $this\n            ->given(\n                $ruler = new LUT(),\n                $rule  = '(false and true) or true'\n            )\n            ->when($result = $ruler->assert($rule))\n            ->then\n                ->boolean($result)\n                    ->isTrue();\n    }\n\n    public function case_github_100_2()\n    {\n        $this\n            ->given(\n                $ruler = new LUT(),\n                $rule  = 'false and true or true'\n            )\n            ->when($result = $ruler->assert($rule))\n            ->then\n                ->boolean($result)\n                    ->isTrue();\n    }\n\n    public function case_github_100_3()\n    {\n        $this\n            ->given(\n                $ruler = new LUT(),\n                $rule  = 'true or true and false'\n            )\n            ->when($result = $ruler->assert($rule))\n            ->then\n                ->boolean($result)\n                    ->isTrue();\n    }\n}\n"
  },
  {
    "path": "Test/Unit/Model/Bag/Bag.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Test\\Unit\\Model\\Bag;\n\nuse Hoa\\Test;\nuse Hoa\\Visitor;\nuse Mock\\Hoa\\Ruler\\Model\\Bag as SUT;\n\n/**\n * Class \\Hoa\\Ruler\\Test\\Unit\\Model\\Bag\\Bag.\n *\n * Test suite of the bag generic class.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Bag extends Test\\Unit\\Suite\n{\n    public function case_is_a_visitor()\n    {\n        $this\n            ->when($result = new SUT())\n            ->then\n                ->object($result)\n                    ->isInstanceOf(Visitor\\Element::class);\n    }\n}\n"
  },
  {
    "path": "Test/Unit/Model/Bag/Context.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSE DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTER HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISIY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Test\\Unit\\Model\\Bag;\n\nuse Hoa\\Ruler as LUT;\nuse Hoa\\Ruler\\Model\\Bag\\Context as SUT;\nuse Hoa\\Test;\n\n/**\n * Class \\Hoa\\Ruler\\Test\\Unit\\Model\\Bag\\Context.\n *\n * Test suite of the context bag class.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Context extends Test\\Unit\\Suite\n{\n    public function case_is_a_bag()\n    {\n        $this\n            ->when($result = new SUT('foobar'))\n            ->then\n                ->object($result)\n                    ->isInstanceOf(LUT\\Model\\Bag::class);\n    }\n\n    public function case_constructor()\n    {\n        $this\n            ->given($id = 'foobar')\n            ->when($result = new SUT($id))\n            ->then\n                ->string($result->getId())\n                    ->isEqualTo($id)\n                ->array($result->getDimensions())\n                    ->isEmpty();\n    }\n\n    public function case_scalar_index_from_root()\n    {\n        return $this->_case_index_from_root(\n            'baz',\n            new LUT\\Model\\Bag\\Scalar('baz')\n        );\n    }\n\n    public function case_array_index_from_root()\n    {\n        return $this->_case_index_from_root(\n            ['baz'],\n            new LUT\\Model\\Bag\\RulerArray(['baz'])\n        );\n    }\n\n    public function case_bag_index_from_root()\n    {\n        return $this->_case_index_from_root(\n            new LUT\\Model\\Bag\\Scalar('baz'),\n            new LUT\\Model\\Bag\\Scalar('baz')\n        );\n    }\n\n    protected function _case_index_from_root($index, $expectedIndex)\n    {\n        $this\n            ->given(\n                $id      = 'foobar',\n                $context = new SUT($id)\n            )\n            ->when($result = $context->index($index))\n            ->then\n                ->object($result)\n                    ->isIdenticalTo($context)\n                ->array($result->getDimensions())\n                    ->isEqualTo([\n                        [\n                            SUT::ACCESS_TYPE  => SUT::ARRAY_ACCESS,\n                            SUT::ACCESS_VALUE => $expectedIndex\n                        ]\n                    ]);\n    }\n\n    public function case_scalar_index()\n    {\n        return $this->_case_index(\n            'baz',\n            new LUT\\Model\\Bag\\Scalar('baz')\n        );\n    }\n\n    public function case_array_index()\n    {\n        return $this->_case_index(\n            ['baz'],\n            new LUT\\Model\\Bag\\RulerArray(['baz'])\n        );\n    }\n\n    public function case_bag_index()\n    {\n        return $this->_case_index(\n            new LUT\\Model\\Bag\\Scalar('baz'),\n            new LUT\\Model\\Bag\\Scalar('baz')\n        );\n    }\n\n    protected function _case_index($index, $expectedIndex)\n    {\n        $this\n            ->given(\n                $id      = 'foobar',\n                $context = new SUT($id),\n                $context->index(new LUT\\Model\\Bag\\Scalar('qux'))\n            )\n            ->when($result = $context->index($index))\n            ->then\n                ->object($result)\n                    ->isIdenticalTo($context)\n                ->array($result->getDimensions())\n                    ->isEqualTo([\n                        [\n                            SUT::ACCESS_TYPE  => SUT::ARRAY_ACCESS,\n                            SUT::ACCESS_VALUE => new LUT\\Model\\Bag\\Scalar('qux')\n                        ],\n                        [\n                            SUT::ACCESS_TYPE  => SUT::ARRAY_ACCESS,\n                            SUT::ACCESS_VALUE => $expectedIndex\n                        ]\n                    ]);\n    }\n\n    public function case_attribute()\n    {\n        $this\n            ->given(\n                $id        = 'foobar',\n                $attribute = 'bazqux',\n                $context   = new SUT($id)\n            )\n            ->when($result = $context->attribute($attribute))\n            ->then\n                ->object($result)\n                    ->isIdenticalTo($context)\n                ->array($result->getDimensions())\n                    ->isEqualTo([\n                        [\n                            SUT::ACCESS_TYPE  => SUT::ATTRIBUTE_ACCESS,\n                            SUT::ACCESS_VALUE => $attribute\n                        ]\n                    ]);\n    }\n\n    public function case_call()\n    {\n        $this\n            ->given(\n                $id      = 'foobar',\n                $method  = new LUT\\Model\\Operator('f'),\n                $context = new SUT($id)\n            )\n            ->when($result = $context->call($method))\n            ->then\n                ->object($result)\n                    ->isIdenticalTo($context)\n                ->array($result->getDimensions())\n                    ->isEqualTo([\n                        [\n                            SUT::ACCESS_TYPE  => SUT::METHOD_ACCESS,\n                            SUT::ACCESS_VALUE => $method\n                        ]\n                    ]);\n    }\n\n    public function case_multiple_dimension_types()\n    {\n        $this\n            ->given(\n                $id        = 'foobar',\n                $index     = 'baz',\n                $attribute = 'qux',\n                $method    = new LUT\\Model\\Operator('f'),\n                $context   = new SUT($id),\n                $context->attribute($attribute),\n                $context->index($index),\n                $context->call($method)\n            )\n            ->when($result = $context->getDimensions())\n            ->then\n                ->array($result)\n                    ->isEqualTo([\n                        [\n                            SUT::ACCESS_TYPE  => SUT::ATTRIBUTE_ACCESS,\n                            SUT::ACCESS_VALUE => $attribute\n                        ],\n                        [\n                            SUT::ACCESS_TYPE  => SUT::ARRAY_ACCESS,\n                            SUT::ACCESS_VALUE => new LUT\\Model\\Bag\\Scalar($index)\n                        ],\n                        [\n                            SUT::ACCESS_TYPE  => SUT::METHOD_ACCESS,\n                            SUT::ACCESS_VALUE => $method\n                        ]\n                    ]);\n    }\n}\n"
  },
  {
    "path": "Test/Unit/Model/Bag/RulerArray.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Test\\Unit\\Model\\Bag;\n\nuse Hoa\\Ruler as LUT;\nuse Hoa\\Ruler\\Model\\Bag\\RulerArray as SUT;\nuse Hoa\\Test;\n\n/**\n * Class \\Hoa\\Ruler\\Test\\Unit\\Model\\Bag\\RulerArray.\n *\n * Test suite of the array bag class.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass RulerArray extends Test\\Unit\\Suite\n{\n    public function case_is_a_bag()\n    {\n        $this\n            ->when($result = new SUT(['foobar']))\n            ->then\n                ->object($result)\n                    ->isInstanceOf(LUT\\Model\\Bag::class);\n    }\n\n    public function case_constructor()\n    {\n        $this\n            ->given($data = ['foo', ['bar'], new LUT\\Model\\Bag\\Scalar('baz')])\n            ->when($result = new SUT($data))\n            ->then\n                ->let($array = $result->getArray())\n                ->array($array)\n                    ->hasSize(count($data))\n                    ->isEqualTo([\n                        new LUT\\Model\\Bag\\Scalar('foo'),\n                        new SUT([new LUT\\Model\\Bag\\Scalar('bar')]),\n                        new LUT\\Model\\Bag\\Scalar('baz')\n                    ]);\n    }\n}\n"
  },
  {
    "path": "Test/Unit/Model/Bag/Scalar.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Test\\Unit\\Model\\Bag;\n\nuse Hoa\\Ruler as LUT;\nuse Hoa\\Ruler\\Model\\Bag\\Scalar as SUT;\nuse Hoa\\Test;\n\n/**\n * Class \\Hoa\\Ruler\\Test\\Unit\\Model\\Bag\\Scalar.\n *\n * Test suite of the scalar bag class.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Scalar extends Test\\Unit\\Suite\n{\n    public function case_is_a_bag()\n    {\n        $this\n            ->when($result = new SUT('foobar'))\n            ->then\n                ->object($result)\n                    ->isInstanceOf(LUT\\Model\\Bag::class);\n    }\n\n    public function case_constructor()\n    {\n        $this\n            ->given($scalar = 'foobar')\n            ->when($result = new SUT($scalar))\n            ->then\n                ->string($result->getValue())\n                    ->isEqualTo($scalar);\n    }\n}\n"
  },
  {
    "path": "Test/Unit/Model/Model.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Test\\Unit\\Model;\n\nuse Hoa\\Ruler as LUT;\nuse Hoa\\Ruler\\Model as SUT;\nuse Hoa\\Test;\nuse Hoa\\Visitor;\n\n/**\n * Class \\Hoa\\Ruler\\Test\\Unit\\Model\\Model.\n *\n * Test suite of the model root object.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Model extends Test\\Unit\\Suite\n{\n    public function case_is_a_visitor_element()\n    {\n        $this\n            ->when($result = new SUT())\n            ->then\n                ->object($result)\n                    ->isInstanceOf(Visitor\\Element::class);\n    }\n\n    public function case_set_default()\n    {\n        $this\n            ->given($model = new SUT())\n            ->when($result = $model->foo = 'bar')\n            ->then\n                ->string($result)\n                    ->isEqualTo('bar')\n                ->string($model->foo)\n                    ->isEqualTo('bar');\n    }\n\n    public function case_set_expression()\n    {\n        $this\n            ->given($model = new SUT())\n            ->when($result = $model->expression = 'foo')\n            ->then\n                ->string($result)\n                    ->isEqualTo('foo')\n                ->boolean(isset($model->expression))\n                    ->isFalse();\n    }\n\n    public function case_get_expression_is_a_scalar()\n    {\n        $this\n            ->given(\n                $model = new SUT(),\n                $model->expression = 'foo'\n            )\n            ->when($result = $model->getExpression())\n            ->then\n                ->object($result)\n                    ->isEqualTo(new LUT\\Model\\Bag\\Scalar('foo'));\n    }\n\n    public function case_get_expression_is_an_array()\n    {\n        $this\n            ->given(\n                $model = new SUT(),\n                $model->expression = ['foo']\n            )\n            ->when($result = $model->getExpression())\n            ->then\n                ->object($result)\n                    ->isEqualTo(new LUT\\Model\\Bag\\RulerArray(['foo']));\n    }\n\n    public function case_get_expression()\n    {\n        $this\n            ->given(\n                $model = new SUT(),\n                $model->expression = new LUT\\Model\\Operator('f')\n            )\n            ->when($result = $model->getExpression())\n            ->then\n                ->object($result)\n                    ->isEqualTo(new LUT\\Model\\Operator('f'));\n    }\n\n    public function case_func()\n    {\n        $this\n            ->given($model = new SUT())\n            ->when($result = $model->func('f', 'x', 42))\n            ->then\n                ->object($result)\n                    ->isInstanceOf(LUT\\Model\\Operator::class)\n                ->string($result->getName())\n                    ->isEqualTo('f')\n                ->array($result->getArguments())\n                    ->isEqualTo([\n                        new LUT\\Model\\Bag\\Scalar('x'),\n                        new LUT\\Model\\Bag\\Scalar(42)\n                    ])\n                ->boolean($result->isFunction())\n                    ->isTrue()\n                ->boolean($result->isLazy())\n                    ->isFalse();\n    }\n\n    public function case_operation()\n    {\n        $this\n            ->given($model = new SUT())\n            ->when($result = $model->operation('f', ['x', 42]))\n            ->then\n                ->object($result)\n                    ->isInstanceOf(LUT\\Model\\Operator::class)\n                ->string($result->getName())\n                    ->isEqualTo('f')\n                ->array($result->getArguments())\n                    ->isEqualTo([\n                        new LUT\\Model\\Bag\\Scalar('x'),\n                        new LUT\\Model\\Bag\\Scalar(42)\n                    ])\n                ->boolean($result->isFunction())\n                    ->isFalse()\n                ->boolean($result->isLazy())\n                    ->isFalse();\n    }\n\n    public function case_operator()\n    {\n        $this\n            ->given($model = new SUT())\n            ->when($result = $model->_operator('f', ['x', 42], true))\n            ->then\n                ->object($result)\n                    ->isInstanceOf(LUT\\Model\\Operator::class)\n                ->string($result->getName())\n                    ->isEqualTo('f')\n                ->array($result->getArguments())\n                    ->isEqualTo([\n                        new LUT\\Model\\Bag\\Scalar('x'),\n                        new LUT\\Model\\Bag\\Scalar(42)\n                    ])\n                ->boolean($result->isFunction())\n                    ->isTrue()\n                ->boolean($result->isLazy())\n                    ->isFalse();\n    }\n\n    public function case_call()\n    {\n        $this\n            ->given($model = new SUT())\n            ->when($result = $model->f('x', 42))\n            ->then\n                ->object($result)\n                    ->isInstanceOf(LUT\\Model\\Operator::class)\n                ->string($result->getName())\n                    ->isEqualTo('f')\n                ->array($result->getArguments())\n                    ->isEqualTo([\n                        new LUT\\Model\\Bag\\Scalar('x'),\n                        new LUT\\Model\\Bag\\Scalar(42)\n                    ])\n                ->boolean($result->isFunction())\n                    ->isFalse()\n                ->boolean($result->isLazy())\n                    ->isFalse();\n    }\n\n    public function case_variable()\n    {\n        $this\n            ->given($model = new SUT())\n            ->when($result = $model->variable('x'))\n            ->then\n                ->object($result)\n                    ->isEqualTo(new LUT\\Model\\Bag\\Context('x'));\n    }\n\n    public function case_to_string()\n    {\n        $this\n            ->given(\n                $model = new SUT(),\n                $model->expression = $model->f('x', 42)\n            )\n            ->when($result = $model->__toString())\n            ->then\n                ->string($result)\n                ->isEqualTo(\n                    '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n                    '$model->expression =' . \"\\n\" .\n                    '    $model->f(' . \"\\n\" .\n                    '        \\'x\\',' . \"\\n\" .\n                    '        42' . \"\\n\" .\n                    '    );'\n                );\n    }\n}\n"
  },
  {
    "path": "Test/Unit/Model/Operator.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Test\\Unit\\Model;\n\nuse Hoa\\Ruler as LUT;\nuse Hoa\\Ruler\\Model\\Operator as SUT;\nuse Hoa\\Test;\nuse Hoa\\Visitor;\n\n/**\n * Class \\Hoa\\Ruler\\Test\\Unit\\Model\\Operator.\n *\n * Test suite of the operator object of the model.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Operator extends Test\\Unit\\Suite\n{\n    public function case_is_a_visitor_element()\n    {\n        $this\n            ->given($name = 'foo')\n            ->when($result = new SUT($name))\n            ->then\n                ->object($result)\n                    ->isInstanceOf(Visitor\\Element::class);\n    }\n\n    public function case_is_a_context()\n    {\n        $this\n            ->given($name = 'foo')\n            ->when($result = new SUT($name))\n            ->then\n                ->object($result)\n                    ->isInstanceOf(LUT\\Model\\Bag\\Context::class);\n    }\n\n    public function case_constructor()\n    {\n        $this\n            ->given($name = 'foo')\n            ->when($result = new SUT($name))\n            ->then\n                ->string($result->getName())\n                    ->isEqualTo($name)\n                ->array($result->getArguments())\n                    ->isEmpty()\n                ->boolean($result->isFunction())\n                    ->isTrue()\n                ->boolean($result->isLazy())\n                    ->isFalse();\n    }\n\n    public function case_constructor_with_arguments()\n    {\n        $this\n            ->given(\n                $name      = 'foo',\n                $arguments = [new LUT\\Model\\Bag\\Scalar(42)]\n            )\n            ->when($result = new SUT($name, $arguments))\n            ->then\n                ->string($result->getName())\n                    ->isEqualTo($name)\n                ->array($result->getArguments())\n                    ->isEqualTo($arguments)\n                ->boolean($result->isFunction())\n                    ->isTrue()\n                ->boolean($result->isLazy())\n                    ->isFalse();\n    }\n\n    public function case_constructor_with_arguments_and_function_flag()\n    {\n        $this\n            ->given(\n                $name       = 'foo',\n                $arguments  = [new LUT\\Model\\Bag\\Scalar(42)],\n                $isFunction = false\n            )\n            ->when($result = new SUT($name, $arguments, $isFunction))\n            ->then\n                ->string($result->getName())\n                    ->isEqualTo($name)\n                ->array($result->getArguments())\n                    ->isEqualTo($arguments)\n                ->boolean($result->isFunction())\n                    ->isFalse()\n                ->boolean($result->isLazy())\n                    ->isFalse();\n    }\n\n    public function case_set_name()\n    {\n        $this\n            ->given(\n                $oldName  = 'foo',\n                $name     = 'bar',\n                $operator = new SUT('foo')\n            )\n            ->when($result = $this->invoke($operator)->setName($name))\n            ->then\n                ->string($result)\n                    ->isEqualTo($oldName)\n                ->boolean($operator->isLazy())\n                    ->isFalse();\n    }\n\n    public function case_set_name_with_the_and_operator_for_auto_laziness()\n    {\n        return $this->_case_set_name_with_auto_laziness('and');\n    }\n\n    public function case_set_name_with_the_or_operator_for_auto_laziness()\n    {\n        return $this->_case_set_name_with_auto_laziness('or');\n    }\n\n    protected function _case_set_name_with_auto_laziness($name)\n    {\n        $this\n            ->given($operator = new SUT('foo'))\n            ->when($result = $this->invoke($operator)->setName($name))\n            ->then\n                ->string($result)\n                    ->isEqualTo('foo')\n                ->boolean($operator->isLazy())\n                    ->isTrue();\n    }\n\n    public function case_get_name()\n    {\n        $this\n            ->given(\n                $name     = 'bar',\n                $operator = new SUT('foo'),\n                $this->invoke($operator)->setName($name)\n            )\n            ->when($result = $operator->getName())\n            ->then\n                ->string($result)\n                    ->isEqualTo($name);\n    }\n\n    public function case_set_arguments()\n    {\n        $this\n            ->given(\n                $operator  = new SUT('foo'),\n                $arguments = ['foo', [42], new LUT\\Model\\Bag\\Scalar('baz')]\n            )\n            ->when($result = $this->invoke($operator)->setArguments($arguments))\n            ->then\n                ->array($result)\n                    ->isEmpty();\n    }\n\n    public function case_set_arguments_not_additive()\n    {\n        $this\n            ->given(\n                $operator   = new SUT('foo'),\n                $argumentsA = [new LUT\\Model\\Bag\\Scalar('foo')],\n                $argumentsB = [new LUT\\Model\\Bag\\Scalar('bar')],\n                $this->invoke($operator)->setArguments($argumentsA)\n            )\n            ->when($result = $this->invoke($operator)->setArguments($argumentsB))\n            ->then\n                ->array($result)\n                    ->isEqualTo($argumentsA)\n                ->array($operator->getArguments())\n                    ->isEqualTo($argumentsB);\n    }\n\n    public function case_get_arguments()\n    {\n        $this\n            ->given(\n                $operator  = new SUT('foo'),\n                $arguments = ['foo', [42], new LUT\\Model\\Bag\\Scalar('baz')],\n                $this->invoke($operator)->setArguments($arguments)\n            )\n            ->when($result = $operator->getArguments())\n            ->then\n                ->array($result)\n                    ->isEqualTo([\n                        new LUT\\Model\\Bag\\Scalar('foo'),\n                        new LUT\\Model\\Bag\\RulerArray([42]),\n                        new LUT\\Model\\Bag\\Scalar('baz')\n                    ]);\n    }\n\n    public function case_set_function()\n    {\n        $this\n            ->given($operator = new SUT('foo'))\n            ->when($result = $this->invoke($operator)->setFunction(false))\n            ->then\n                ->boolean($result)\n                    ->isTrue();\n    }\n\n    public function case_is_function()\n    {\n        $this\n            ->given(\n                $operator = new SUT('foo'),\n                $this->invoke($operator)->setFunction(true)\n            )\n            ->when($result = $operator->isFunction())\n            ->then\n                ->boolean($result)\n                    ->isTrue();\n    }\n\n    public function case_is_not_function()\n    {\n        $this\n            ->given(\n                $operator = new SUT('foo'),\n                $this->invoke($operator)->setFunction(false)\n            )\n            ->when($result = $operator->isFunction())\n            ->then\n                ->boolean($result)\n                    ->isFalse();\n    }\n\n    public function case_set_laziness()\n    {\n        $this\n            ->given($operator = new SUT('foo'))\n            ->when($result = $this->invoke($operator)->setLaziness(false))\n            ->then\n                ->boolean($result)\n                    ->isFalse();\n    }\n\n    public function case_is_lazy()\n    {\n        $this\n            ->given(\n                $operator = new SUT('foo'),\n                $this->invoke($operator)->setLaziness(true)\n            )\n            ->when($result = $operator->isLazy())\n            ->then\n                ->boolean($result)\n                    ->isTrue();\n    }\n\n    public function case_is_not_lazy()\n    {\n        $this\n            ->given(\n                $operator = new SUT('foo'),\n                $this->invoke($operator)->setLaziness(false)\n            )\n            ->when($result = $operator->isLazy())\n            ->then\n                ->boolean($result)\n                    ->isFalse();\n    }\n\n    public function case_should_break_lazy_evaluation_with_and_operator()\n    {\n        return $this->_case_should_break_lazy_evaluation_with_x_operator(\n            'and',\n            false,\n            SUT::LAZY_BREAK\n        );\n    }\n\n    public function case_should_not_break_lazy_evaluation_with_and_operator()\n    {\n        return $this->_case_should_break_lazy_evaluation_with_x_operator(\n            'and',\n            true,\n            SUT::LAZY_CONTINUE\n        );\n    }\n\n    public function case_should_break_lazy_evaluation_with_or_operator()\n    {\n        return $this->_case_should_break_lazy_evaluation_with_x_operator(\n            'or',\n            true,\n            SUT::LAZY_BREAK\n        );\n    }\n\n    public function case_should_not_break_lazy_evaluation_with_or_operator()\n    {\n        return $this->_case_should_break_lazy_evaluation_with_x_operator(\n            'or',\n            false,\n            SUT::LAZY_CONTINUE\n        );\n    }\n\n    public function case_should_not_break_lazy_evaluation_with_any_operator()\n    {\n        return $this->_case_should_break_lazy_evaluation_with_x_operator(\n            'foo',\n            42,\n            SUT::LAZY_CONTINUE\n        );\n    }\n\n    protected function _case_should_break_lazy_evaluation_with_x_operator($name, $value, $expect)\n    {\n        $this\n            ->given($operator = new SUT($name))\n            ->when($result = $operator->shouldBreakLazyEvaluation($value))\n            ->then\n                ->boolean($result)\n                    ->isEqualTo($expect);\n    }\n\n    public function case_is_token()\n    {\n        $this\n            ->when(function () {\n                foreach (['not', 'and', 'or', 'xor'] as $token) {\n                    $this\n                        ->when($result = SUT::isToken($token))\n                        ->then\n                            ->boolean($result)\n                                ->isTrue();\n                }\n            });\n    }\n\n    public function case_is_not_token()\n    {\n        $this\n            ->when($result = SUT::isToken('foo'))\n            ->then\n                ->boolean($result)\n                    ->isFalse();\n    }\n}\n"
  },
  {
    "path": "Test/Unit/Ruler.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Test\\Unit;\n\nuse Hoa\\Compiler;\nuse Hoa\\Ruler as LUT;\nuse Hoa\\Ruler\\Ruler as SUT;\nuse Hoa\\Test;\n\n/**\n * Class \\Hoa\\Ruler\\Test\\Unit\\Ruler.\n *\n * Test suite of the ruler class.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Ruler extends Test\\Unit\\Suite\n{\n    public function case_assert()\n    {\n        $this\n            ->given(\n                $rule  = '7 < 42',\n                $ruler = new SUT()\n            )\n            ->when($result = $ruler->assert($rule))\n            ->then\n                ->boolean($result)\n                    ->isTrue();\n    }\n\n    public function case_assert_with_a_context()\n    {\n        $this\n            ->given(\n                $rule         = 'x < 42',\n                $ruler        = new SUT(),\n                $context      = new LUT\\Context(),\n                $context['x'] = 7\n            )\n            ->when($result = $ruler->assert($rule, $context))\n            ->then\n                ->boolean($result)\n                    ->isTrue();\n    }\n\n    public function case_assert_with_rule_as_a_model()\n    {\n        $this\n            ->given(\n                $rule         = SUT::interpret('x < 42'),\n                $ruler        = new SUT(),\n                $context      = new LUT\\Context(),\n                $context['x'] = 7\n            )\n            ->when($result = $ruler->assert($rule, $context))\n            ->then\n                ->boolean($result)\n                    ->isTrue();\n    }\n\n    public function case_interpret()\n    {\n        $this\n            ->when($result = SUT::interpret('x < 42'))\n            ->then\n                ->object($result)\n                    ->isInstanceOf(LUT\\Model::class);\n    }\n\n    public function case_get_interpreter()\n    {\n        $this\n            ->when($result = SUT::getInterpreter())\n            ->then\n                ->object($result)\n                    ->isInstanceOf(LUT\\Visitor\\Interpreter::class);\n    }\n\n    public function case_set_asserter()\n    {\n        $this\n            ->given(\n                $ruler    = new SUT(),\n                $asserter = new LUT\\Visitor\\Asserter()\n            )\n            ->when($result = $ruler->setAsserter($asserter))\n            ->then\n                ->variable($result)\n                    ->isNull();\n    }\n\n    public function case_get_asserter()\n    {\n        $this\n            ->given(\n                $asserter = new LUT\\Visitor\\Asserter(),\n                $ruler    = new SUT(),\n                $context  = new LUT\\Context(),\n                $ruler->setAsserter($asserter),\n                $asserter->setContext($context)\n            )\n            ->when($result = $ruler->getAsserter())\n            ->then\n                ->object($result)\n                    ->isIdenticalTo($asserter)\n                ->object($result->getContext())\n                    ->isIdenticalTo($context);\n    }\n\n    public function case_get_asserter_with_a_specific_context()\n    {\n        $this\n            ->given(\n                $asserter = new LUT\\Visitor\\Asserter(),\n                $contextA = new LUT\\Context(),\n                $contextB = new LUT\\Context(),\n                $ruler    = new SUT(),\n                $ruler->setAsserter($asserter),\n                $asserter->setContext($contextA)\n            )\n            ->when($result = $ruler->getAsserter($contextB))\n            ->then\n                ->object($result)\n                    ->isIdenticalTo($asserter)\n                ->object($result->getContext())\n                    ->isIdenticalTo($contextB);\n    }\n\n    public function case_get_asserter_the_default_one()\n    {\n        $this\n            ->given($ruler = new SUT())\n            ->when($result = $ruler->getAsserter())\n            ->then\n                ->object($result)\n                    ->isInstanceOf(LUT\\Visitor\\Asserter::class)\n                ->variable($result->getContext())\n                    ->isNull()\n                ->object($ruler->getAsserter())\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_get_asserter_the_default_one_with_a_specific_context()\n    {\n        $this\n            ->given(\n                $ruler   = new SUT(),\n                $context = new LUT\\Context()\n            )\n            ->when($result = $ruler->getAsserter($context))\n            ->then\n                ->object($result)\n                    ->isInstanceOf(LUT\\Visitor\\Asserter::class)\n                ->object($result->getContext())\n                    ->isIdenticalTo($context)\n                ->object($ruler->getAsserter($context))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_get_default_asserter()\n    {\n        $this\n            ->when($result = SUT::getDefaultAsserter())\n            ->then\n                ->object($result)\n                    ->isInstanceOf(LUT\\Visitor\\Asserter::class)\n                ->variable($result->getContext())\n                    ->isNull()\n                ->object(SUT::getDefaultAsserter())\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_get_default_asserter_with_a_specific_context()\n    {\n        $this\n            ->given($context = new LUT\\Context())\n            ->when($result = SUT::getDefaultAsserter($context))\n            ->then\n                ->object($result)\n                    ->isInstanceOf(LUT\\Visitor\\Asserter::class)\n                ->object($result->getContext())\n                    ->isIdenticalTo($context)\n                ->object(SUT::getDefaultAsserter($context))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_get_compiler()\n    {\n        $this\n            ->when($result = SUT::getCompiler())\n            ->then\n                ->object($result)\n                    ->isInstanceOf(Compiler\\Llk\\Parser::class)\n                ->object(SUT::getCompiler())\n                    ->isIdenticalTo($result);\n    }\n}\n"
  },
  {
    "path": "Test/Unit/Visitor/Asserter.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Test\\Unit\\Visitor;\n\nuse ArrayObject;\nuse Hoa\\Ruler as LUT;\nuse Hoa\\Ruler\\Visitor\\Asserter as SUT;\nuse Hoa\\Test;\nuse Hoa\\Visitor;\n\n/**\n * Class \\Hoa\\Ruler\\Test\\Unit\\Visitor\\Asserter.\n *\n * Test suite of the compiler visitor.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Asserter extends Test\\Unit\\Suite\n{\n    public function case_is_a_visitor()\n    {\n        $this\n            ->when($result = new SUT())\n            ->then\n                ->object($result)\n                    ->isInstanceOf(Visitor\\Visit::class);\n    }\n\n    public function case_constructor()\n    {\n        $this\n            ->when($result = new SUT())\n            ->then\n                ->array($result->getOperators())\n                    ->hasSize(14)\n                    ->hasKey('and')\n                    ->hasKey('or')\n                    ->hasKey('xor')\n                    ->hasKey('not')\n                    ->hasKey('=')\n                    ->hasKey('is')\n                    ->hasKey('!=')\n                    ->hasKey('>')\n                    ->hasKey('>=')\n                    ->hasKey('<')\n                    ->hasKey('<=')\n                    ->hasKey('in')\n                    ->hasKey('sum')\n                    ->hasKey('matches')\n                ->variable($result->getContext())\n                    ->isNull();\n    }\n\n    public function case_constructor_with_a_context()\n    {\n        $this\n            ->given($context = new LUT\\Context())\n            ->when($result = new SUT($context))\n            ->then\n                ->object($result->getContext())\n                    ->isIdenticalTo($context);\n    }\n\n    public function case_operator_and_arity_0()\n    {\n        return $this->_case_boolean_operator('and', [], false);\n    }\n\n    public function case_operator_and_true()\n    {\n        return $this->_case_boolean_operator('and', [true], false);\n    }\n\n    public function case_operator_and_false()\n    {\n        return $this->_case_boolean_operator('and', [false], false);\n    }\n\n    public function case_operator_and_true_true()\n    {\n        return $this->_case_boolean_operator('and', [true, true], true);\n    }\n\n    public function case_operator_and_true_false()\n    {\n        return $this->_case_boolean_operator('and', [true, false], false);\n    }\n\n    public function case_operator_and_false_true()\n    {\n        return $this->_case_boolean_operator('and', [false, true], false);\n    }\n\n    public function case_operator_and_false_false()\n    {\n        return $this->_case_boolean_operator('and', [false, false], false);\n    }\n\n    public function case_operator_or_arity_0()\n    {\n        return $this->_case_boolean_operator('or', [], false);\n    }\n\n    public function case_operator_or_true()\n    {\n        return $this->_case_boolean_operator('or', [true], true);\n    }\n\n    public function case_operator_or_false()\n    {\n        return $this->_case_boolean_operator('or', [false], false);\n    }\n\n    public function case_operator_or_true_true()\n    {\n        return $this->_case_boolean_operator('or', [true, true], true);\n    }\n\n    public function case_operator_or_true_false()\n    {\n        return $this->_case_boolean_operator('or', [true, false], true);\n    }\n\n    public function case_operator_or_false_true()\n    {\n        return $this->_case_boolean_operator('or', [false, true], true);\n    }\n\n    public function case_operator_or_false_false()\n    {\n        return $this->_case_boolean_operator('or', [false, false], false);\n    }\n\n    public function case_operator_xor_true_true()\n    {\n        return $this->_case_boolean_operator('xor', [true, true], false);\n    }\n\n    public function case_operator_xor_true_false()\n    {\n        return $this->_case_boolean_operator('xor', [true, false], true);\n    }\n\n    public function case_operator_xor_false_true()\n    {\n        return $this->_case_boolean_operator('xor', [false, true], true);\n    }\n\n    public function case_operator_xor_false_false()\n    {\n        return $this->_case_boolean_operator('xor', [false, false], false);\n    }\n\n    public function case_operator_not_true()\n    {\n        return $this->_case_boolean_operator('not', [true], false);\n    }\n\n    public function case_operator_not_false()\n    {\n        return $this->_case_boolean_operator('not', [false], true);\n    }\n\n    public function case_operator_equal_7_42()\n    {\n        return $this->_case_boolean_operator('=', [7, 42], false);\n    }\n\n    public function case_operator_equal_7_7()\n    {\n        return $this->_case_boolean_operator('=', [7, 7], true);\n    }\n\n    public function case_operator_equal_7_7_casting()\n    {\n        return $this->_case_boolean_operator('=', [7, '7'], true);\n    }\n\n    public function case_operator_is_7_42()\n    {\n        return $this->_case_boolean_operator('is', [7, 42], false);\n    }\n\n    public function case_operator_is_7_7()\n    {\n        return $this->_case_boolean_operator('is', [7, 7], true);\n    }\n\n    public function case_operator_is_7_7_casting()\n    {\n        return $this->_case_boolean_operator('is', [7, '7'], true);\n    }\n\n    public function case_operator_not_equal_7_42()\n    {\n        return $this->_case_boolean_operator('!=', [7, 42], true);\n    }\n\n    public function case_operator_not_equal_7_7()\n    {\n        return $this->_case_boolean_operator('!=', [7, 7], false);\n    }\n\n    public function case_operator_not_equal_7_7_casting()\n    {\n        return $this->_case_boolean_operator('!=', [7, '7'], false);\n    }\n\n    public function case_operator_greater_than_7_42()\n    {\n        return $this->_case_boolean_operator('>', [7, 42], false);\n    }\n\n    public function case_operator_greater_than_42_7()\n    {\n        return $this->_case_boolean_operator('>', [42, 7], true);\n    }\n\n    public function case_operator_greater_than_7_7()\n    {\n        return $this->_case_boolean_operator('>', [7, 7], false);\n    }\n\n    public function case_operator_greater_than_or_equal_to_7_42()\n    {\n        return $this->_case_boolean_operator('>=', [7, 42], false);\n    }\n\n    public function case_operator_greater_than_or_equal_to_42_7()\n    {\n        return $this->_case_boolean_operator('>=', [42, 7], true);\n    }\n\n    public function case_operator_greater_than_or_equal_to_7_7()\n    {\n        return $this->_case_boolean_operator('>=', [7, 7], true);\n    }\n\n    public function case_operator_lower_than_7_42()\n    {\n        return $this->_case_boolean_operator('<', [7, 42], true);\n    }\n\n    public function case_operator_lower_than_42_7()\n    {\n        return $this->_case_boolean_operator('<', [42, 7], false);\n    }\n\n    public function case_operator_lower_than_7_7()\n    {\n        return $this->_case_boolean_operator('<', [7, 7], false);\n    }\n\n    public function case_operator_lower_than_or_equal_to_7_42()\n    {\n        return $this->_case_boolean_operator('<=', [7, 42], true);\n    }\n\n    public function case_operator_lower_than_or_equal_to_42_7()\n    {\n        return $this->_case_boolean_operator('<=', [42, 7], false);\n    }\n\n    public function case_operator_lower_than_or_equal_to_7_7()\n    {\n        return $this->_case_boolean_operator('<=', [7, 7], true);\n    }\n\n    public function case_operator_in_empty_array()\n    {\n        return $this->_case_boolean_operator('in', [7, []], false);\n    }\n\n    public function case_operator_in()\n    {\n        return $this->_case_boolean_operator('in', [7, [1, 3, 5, 7, 9]], true);\n    }\n\n    public function case_operator_in_falsy()\n    {\n        return $this->_case_boolean_operator('in', [42, [1, 3, 5, 7, 9]], false);\n    }\n\n    protected function _case_boolean_operator($operator, array $parameters, $expected)\n    {\n        $this\n            ->given(\n                $asserter = new SUT(),\n                $operator = $asserter->getOperator($operator)\n            )\n            ->when($result = call_user_func_array($operator, $parameters))\n            ->then\n                ->boolean($result)\n                    ->isEqualTo($expected);\n    }\n\n    public function case_operator_sum_arity_0()\n    {\n        return $this->_case_operator_sum([], 0);\n    }\n\n    public function case_operator_sum_arity_1()\n    {\n        return $this->_case_operator_sum([7], 7);\n    }\n\n    public function case_operator_sum()\n    {\n        return $this->_case_operator_sum([1, 2, 3, 4, 5, 6, 7, 8, 9], 45);\n    }\n\n    protected function _case_operator_sum(array $parameters, $expected)\n    {\n        $this\n            ->given(\n                $asserter = new SUT(),\n                $operator = $asserter->getOperator('sum')\n            )\n            ->when($result = call_user_func_array($operator, $parameters))\n            ->then\n                ->integer($result)\n                    ->isEqualTo($expected);\n    }\n\n    public function case_operator_matches()\n    {\n        return $this->_case_boolean_operator('matches', ['foo', '\\w+'], true);\n    }\n\n    public function case_operator_matches_falsy()\n    {\n        return $this->_case_boolean_operator('matches', ['foo', '\\d+'], false);\n    }\n\n    public function case_operator_matches_escaped_delimiter()\n    {\n        return $this->_case_boolean_operator('matches', ['`foo`', '`\\w+`'], true);\n    }\n\n    public function case_set_context()\n    {\n        $this\n            ->given(\n                $context  = new LUT\\Context(),\n                $asserter = new SUT()\n            )\n            ->when($result = $asserter->setContext($context))\n            ->then\n                ->variable($result)\n                    ->isNull();\n    }\n\n    public function case_get_context()\n    {\n        $this\n            ->given(\n                $context  = new LUT\\Context(),\n                $asserter = new SUT(),\n                $asserter->setContext($context)\n            )\n            ->when($result = $asserter->getContext())\n            ->then\n                ->object($result)\n                    ->isIdenticalTo($context);\n    }\n\n    public function case_set_operator()\n    {\n        $this\n            ->given(\n                $asserter     = new SUT(),\n                $oldOperators = $asserter->getOperators(),\n                $operator     = function () {}\n            )\n            ->when($result = $asserter->setOperator('_foo_', $operator))\n            ->then\n                ->object($result)\n                    ->isIdenticalTo($asserter)\n                ->integer(count($asserter->getOperators()))\n                    ->isEqualTo(count($oldOperators) + 1)\n                ->boolean($asserter->operatorExists('_foo_'))\n                    ->isTrue()\n                ->object($asserter->getOperator('_foo_'))\n                    ->isEqualTo(xcallable($operator));\n    }\n\n    public function case_set_operator_overwrite()\n    {\n        $this\n            ->given(\n                $asserter = new SUT(),\n                $asserter->setOperator('_foo_', function () {}),\n                $oldOperators = $asserter->getOperators(),\n                $operator = function () {}\n            )\n            ->when($result = $asserter->setOperator('_foo_', $operator))\n            ->then\n                ->object($result)\n                    ->isIdenticalTo($asserter)\n                ->integer(count($asserter->getOperators()))\n                    ->isEqualTo(count($oldOperators))\n                ->boolean($asserter->operatorExists('_foo_'))\n                    ->isTrue()\n                ->object($asserter->getOperator('_foo_'))\n                    ->isEqualTo(xcallable($operator));\n    }\n\n    public function case_operator_exists()\n    {\n        $this\n            ->given(\n                $asserter = new SUT(),\n                $asserter->setOperator('_foo_', function () {})\n            )\n            ->when($result = $asserter->operatorExists('_foo_'))\n            ->then\n                ->boolean($result)\n                    ->isTrue();\n    }\n\n    public function case_operator_does_not_exist()\n    {\n        $this\n            ->given($asserter = new SUT())\n            ->when($result = $asserter->operatorExists('_foo_'))\n            ->then\n                ->boolean($result)\n                    ->isFalse();\n    }\n\n    public function case_get_operator()\n    {\n        $this\n            ->given(\n                $asserter = new SUT(),\n                $operator = function () {},\n                $asserter->setOperator('_foo_', $operator)\n            )\n            ->when($result = $asserter->getOperator('_foo_'))\n            ->then\n                ->object($result)\n                    ->isEqualTo(xcallable($operator));\n    }\n\n    public function case_get_undefined_operator()\n    {\n        $this\n            ->given($asserter = new SUT())\n            ->when($result = $asserter->getOperator('_foo_'))\n            ->then\n                ->variable($result)\n                    ->isNull();\n    }\n\n    public function case_get_operators()\n    {\n        $this\n            ->given($asserter = new SUT())\n            ->when($result = $asserter->getOperators())\n            ->then\n                ->array($result)\n                    ->hasSize(14);\n    }\n\n    public function case_visit_model()\n    {\n        $this\n            ->given(\n                $model             = new LUT\\Model(),\n                $model->expression = 42,\n                $asserter          = new SUT()\n            )\n            ->when($result = $asserter->visitModel($model))\n            ->then\n                ->boolean($result)\n                    ->isTrue()\n                ->boolean($asserter->visit($model))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_operator()\n    {\n        $this\n            ->given(\n                $operator = new LUT\\Model\\Operator('sum', [7, 35]),\n                $asserter = new SUT()\n            )\n            ->when($result = $this->invoke($asserter)->visitOperator($operator))\n            ->then\n                ->integer($result)\n                    ->isEqualTo(42)\n                ->integer($asserter->visit($operator))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_operator_does_not_exist()\n    {\n        $this\n            ->given(\n                $operator = new LUT\\Model\\Operator('_foo_', [7, 35]),\n                $asserter = new SUT()\n            )\n            ->exception(function () use ($asserter, $operator) {\n                $this->invoke($asserter)->visitOperator($operator);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Operator _foo_ does not exist.')\n            ->exception(function () use ($asserter, $operator) {\n                $asserter->visit($operator);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Operator _foo_ does not exist.');\n    }\n\n    public function case_visit_operator_array_dimension_1()\n    {\n        $this\n            ->given(\n                $operator = new LUT\\Model\\Operator('c', [7]),\n                $operator->index('x'),\n                $asserter = new SUT(),\n                $asserter->setOperator(\n                    'c',\n                    function ($x) {\n                        return ['x' => $x * 6];\n                    }\n                )\n            )\n            ->when($result = $this->invoke($asserter)->visitOperator($operator))\n            ->then\n                ->integer($result)\n                    ->isEqualTo(42)\n                ->integer($asserter->visit($operator))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_operator_array_dimension_2()\n    {\n        $this\n            ->given(\n                $operator = new LUT\\Model\\Operator('c', [7]),\n                $operator->index('x'),\n                $operator->index('y'),\n                $asserter = new SUT(),\n                $asserter->setOperator(\n                    'c',\n                    function ($x) {\n                        return ['x' => ['y' => $x * 6]];\n                    }\n                )\n            )\n            ->when($result = $this->invoke($asserter)->visitOperator($operator))\n            ->then\n                ->integer($result)\n                    ->isEqualTo(42)\n                ->integer($asserter->visit($operator))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_operator_array_like_dimension_1()\n    {\n        $this\n            ->given(\n                $operator = new LUT\\Model\\Operator('c', [7]),\n                $operator->index('x'),\n                $asserter = new SUT(),\n                $asserter->setOperator(\n                    'c',\n                    function ($x) {\n                        return new ArrayObject(['x' => $x * 6]);\n                    }\n                )\n            )\n            ->when($result = $this->invoke($asserter)->visitOperator($operator))\n            ->then\n                ->integer($result)\n                    ->isEqualTo(42)\n                ->integer($asserter->visit($operator))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_operator_array_like_dimension_2()\n    {\n        $this\n            ->given(\n                $operator = new LUT\\Model\\Operator('c', [7]),\n                $operator->index('x'),\n                $operator->index('y'),\n                $asserter = new SUT(),\n                $asserter->setOperator(\n                    'c',\n                    function ($x) {\n                        return new ArrayObject(['x' => new ArrayObject(['y' => $x * 6])]);\n                    }\n                )\n            )\n            ->when($result = $this->invoke($asserter)->visitOperator($operator))\n            ->then\n                ->integer($result)\n                    ->isEqualTo(42)\n                ->integer($asserter->visit($operator))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_operator_array_dimension_1_undefined_index()\n    {\n        $this\n            ->given(\n                $operator = new LUT\\Model\\Operator('c', [7]),\n                $operator->index('z'),\n                $asserter = new SUT(),\n                $asserter->setOperator(\n                    'c',\n                    function ($x) {\n                        return ['x' => 42];\n                    }\n                )\n            )\n            ->exception(function () use ($asserter, $operator) {\n                $this->invoke($asserter)->visitOperator($operator);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to access to an undefined index: z (dimension number 1 of c()).')\n            ->exception(function () use ($asserter, $operator) {\n                $asserter->visit($operator);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to access to an undefined index: z (dimension number 1 of c()).');\n    }\n\n    public function case_visit_operator_array_dimension_1_not_an_array()\n    {\n        $this\n            ->given(\n                $operator = new LUT\\Model\\Operator('c', [7]),\n                $operator->index('y'),\n                $asserter = new SUT(),\n                $asserter->setOperator(\n                    'c',\n                    function ($x) {\n                        return 42;\n                    }\n                )\n            )\n            ->exception(function () use ($asserter, $operator) {\n                $this->invoke($asserter)->visitOperator($operator);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to access to an undefined index: y (dimension number 1 of c()), because it is not an array.')\n            ->exception(function () use ($asserter, $operator) {\n                $asserter->visit($operator);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to access to an undefined index: y (dimension number 1 of c()), because it is not an array.');\n    }\n\n    public function case_visit_operator_array_like_dimension_1_undefined_index()\n    {\n        $this\n            ->given(\n                $operator = new LUT\\Model\\Operator('c', [7]),\n                $operator->index('z'),\n                $asserter = new SUT(),\n                $asserter->setOperator(\n                    'c',\n                    function ($x) {\n                        return new ArrayObject(['x' => 42]);\n                    }\n                )\n            )\n            ->exception(function () use ($asserter, $operator) {\n                $this->invoke($asserter)->visitOperator($operator);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to access to an undefined index: z (dimension number 1 of c()).')\n            ->exception(function () use ($asserter, $operator) {\n                $asserter->visit($operator);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to access to an undefined index: z (dimension number 1 of c()).');\n    }\n\n    public function case_visit_operator_attribute_dimension_1()\n    {\n        $this\n            ->given(\n                $operator = new LUT\\Model\\Operator('c', [7]),\n                $operator->attribute('x'),\n                $asserter = new SUT(),\n                $asserter->setOperator(\n                    'c',\n                    function ($x) {\n                        return (object) ['x' => $x * 6];\n                    }\n                )\n            )\n            ->when($result = $this->invoke($asserter)->visitOperator($operator))\n            ->then\n                ->integer($result)\n                    ->isEqualTo(42)\n                ->integer($asserter->visit($operator))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_operator_attribute_dimension_2()\n    {\n        $this\n            ->given(\n                $operator = new LUT\\Model\\Operator('c', [7]),\n                $operator->attribute('x'),\n                $operator->attribute('y'),\n                $asserter = new SUT(),\n                $asserter->setOperator(\n                    'c',\n                    function ($x) {\n                        return (object) ['x' => (object) ['y' => $x * 6]];\n                    }\n                )\n            )\n            ->when($result = $this->invoke($asserter)->visitOperator($operator))\n            ->then\n                ->integer($result)\n                    ->isEqualTo(42)\n                ->integer($asserter->visit($operator))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_operator_attribute_dimension_1_undefined_name()\n    {\n        $this\n            ->given(\n                $operator = new LUT\\Model\\Operator('c', [7]),\n                $operator->attribute('y'),\n                $asserter = new SUT(),\n                $asserter->setOperator(\n                    'c',\n                    function ($x) {\n                        return (object) ['x' => $x * 6];\n                    }\n                )\n            )\n            ->exception(function () use ($asserter, $operator) {\n                $this->invoke($asserter)->visitOperator($operator);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to read an undefined attribute: y (dimension number 1 of c()).')\n            ->exception(function () use ($asserter, $operator) {\n                $asserter->visit($operator);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to read an undefined attribute: y (dimension number 1 of c()).');\n    }\n\n    public function case_visit_operator_attribute_dimension_1_not_an_object()\n    {\n        $this\n            ->given(\n                $operator = new LUT\\Model\\Operator('c', [7]),\n                $operator->attribute('x'),\n                $asserter = new SUT(),\n                $asserter->setOperator(\n                    'c',\n                    function ($x) {\n                        return $x * 6;\n                    }\n                )\n            )\n            ->exception(function () use ($asserter, $operator) {\n                $this->invoke($asserter)->visitOperator($operator);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to read an undefined attribute: x (dimension number 1 of c()), because it is not an object.')\n            ->exception(function () use ($asserter, $operator) {\n                $asserter->visit($operator);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to read an undefined attribute: x (dimension number 1 of c()), because it is not an object.');\n    }\n\n    public function case_visit_operator_method_dimension_1()\n    {\n        $this\n            ->given(\n                $operator = new LUT\\Model\\Operator('c'),\n                $operator->call(new LUT\\Model\\Operator('f', [7, 35])),\n                $context  = new LUT\\Context(['x' => new C()]),\n                $asserter = new SUT($context),\n                $asserter->setOperator(\n                    'c',\n                    function () {\n                        return new C();\n                    }\n                )\n            )\n            ->when($result = $this->invoke($asserter)->visitOperator($operator))\n            ->then\n                ->integer($result)\n                    ->isEqualTo(42)\n                ->integer($asserter->visit($operator))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_operator_method_dimension_2()\n    {\n        $this\n            ->given(\n                $operator = new LUT\\Model\\Operator('c'),\n                $operator->call(new LUT\\Model\\Operator('newMe')),\n                $operator->call(new LUT\\Model\\Operator('f', [7, 35])),\n                $asserter = new SUT(),\n                $asserter->setOperator(\n                    'c',\n                    function () {\n                        return new C();\n                    }\n                )\n            )\n            ->when($result = $this->invoke($asserter)->visitOperator($operator))\n            ->then\n                ->integer($result)\n                    ->isEqualTo(42)\n                ->integer($asserter->visit($operator))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_operator_method_dimension_1_undefined_method()\n    {\n        $this\n            ->given(\n                $operator = new LUT\\Model\\Operator('c'),\n                $operator->call(new LUT\\Model\\Operator('h')),\n                $asserter = new SUT(),\n                $asserter->setOperator(\n                    'c',\n                    function () {\n                        return new C();\n                    }\n                )\n            )\n            ->exception(function () use ($asserter, $operator) {\n                $this->invoke($asserter)->visitOperator($operator);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to call an undefined method: h (dimension number 1 of c()).')\n            ->exception(function () use ($asserter, $operator) {\n                $asserter->visit($operator);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to call an undefined method: h (dimension number 1 of c()).');\n    }\n\n    public function case_visit_operator_method_dimension_1_not_an_object()\n    {\n        $this\n            ->given(\n                $operator = new LUT\\Model\\Operator('c'),\n                $operator->call(new LUT\\Model\\Operator('f', [7, 35])),\n                $asserter = new SUT(),\n                $asserter->setOperator(\n                    'c',\n                    function () {\n                        return 42;\n                    }\n                )\n            )\n            ->exception(function () use ($asserter, $operator) {\n                $this->invoke($asserter)->visitOperator($operator);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to call an undefined method: f (dimension number 1 of c()), because it is not an object.')\n            ->exception(function () use ($asserter, $operator) {\n                $asserter->visit($operator);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to call an undefined method: f (dimension number 1 of c()), because it is not an object.');\n    }\n\n    public function case_visit_operator_mixed_dimensions()\n    {\n        $this\n            ->given(\n                $operator = new LUT\\Model\\Operator('c'),\n                $operator->index('x'),\n                $operator->attribute('y'),\n                $operator->call(new LUT\\Model\\Operator('f', [7, 35])),\n                $asserter = new SUT(),\n                $asserter->setOperator(\n                    'c',\n                    function () {\n                        return ['x' => (object) ['y' => new C()]];\n                    }\n                )\n            )\n            ->when($result = $this->invoke($asserter)->visitOperator($operator))\n            ->then\n                ->integer($result)\n                    ->isEqualTo(42)\n                ->integer($asserter->visit($operator))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_scalar_null()\n    {\n        return $this->_case_visit_scalar(null);\n    }\n\n    public function case_visit_scalar_boolean()\n    {\n        return $this->_case_visit_scalar(true);\n    }\n\n    public function case_visit_scalar_integer()\n    {\n        return $this->_case_visit_scalar(7);\n    }\n\n    public function case_visit_scalar_float()\n    {\n        return $this->_case_visit_scalar(4.2);\n    }\n\n    public function case_visit_scalar_string()\n    {\n        return $this->_case_visit_scalar('foo');\n    }\n\n    protected function _case_visit_scalar($scalar)\n    {\n        $this\n            ->given(\n                $bag      = new LUT\\Model\\Bag\\Scalar($scalar),\n                $asserter = new SUT()\n            )\n            ->when($result = $this->invoke($asserter)->visitScalar($bag))\n            ->then\n                ->variable($result)\n                    ->isIdenticalTo($scalar)\n                ->variable($asserter->visit($bag))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_array()\n    {\n        $this\n            ->given(\n                $bag      = new LUT\\Model\\Bag\\RulerArray(['foo']),\n                $asserter = new SUT()\n            )\n            ->when($result = $this->invoke($asserter)->visitArray($bag))\n            ->then\n                ->array($result)\n                    ->isEqualTo(['foo'])\n                ->variable($asserter->visit($bag))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_undefined_context()\n    {\n        $this\n            ->given(\n                $bag      = new LUT\\Model\\Bag\\Context('x'),\n                $asserter = new SUT()\n            )\n            ->exception(function () use ($asserter, $bag) {\n                $this->invoke($asserter)->visitContext($bag);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Assert needs a context to work properly.')\n            ->exception(function () use ($asserter, $bag) {\n                $asserter->visit($bag);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Assert needs a context to work properly.');\n    }\n\n    public function case_visit_context_undefined_reference()\n    {\n        $this\n            ->given(\n                $bag      = new LUT\\Model\\Bag\\Context('x'),\n                $context  = new LUT\\Context(),\n                $asserter = new SUT($context)\n            )\n            ->exception(function () use ($asserter, $bag) {\n                $this->invoke($asserter)->visitContext($bag);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Context reference x does not exist.')\n            ->exception(function () use ($asserter, $bag) {\n                $asserter->visit($bag);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Context reference x does not exist.');\n    }\n\n    public function case_visit_context()\n    {\n        $this\n            ->given(\n                $bag      = new LUT\\Model\\Bag\\Context('x'),\n                $context  = new LUT\\Context(['x' => 42]),\n                $asserter = new SUT($context)\n            )\n            ->when($result = $this->invoke($asserter)->visitContext($bag))\n            ->then\n                ->integer($result)\n                    ->isEqualTo(42)\n                ->integer($asserter->visit($bag))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_context_array_dimension_1()\n    {\n        $this\n            ->given(\n                $bag = new LUT\\Model\\Bag\\Context('x'),\n                $bag->index('y'),\n                $context  = new LUT\\Context(['x' => ['y' => 42]]),\n                $asserter = new SUT($context)\n            )\n            ->when($result = $this->invoke($asserter)->visitContext($bag))\n            ->then\n                ->integer($result)\n                    ->isEqualTo(42)\n                ->integer($asserter->visit($bag))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_context_array_dimension_2()\n    {\n        $this\n            ->given(\n                $bag = new LUT\\Model\\Bag\\Context('x'),\n                $bag->index('y'),\n                $bag->index('z'),\n                $context  = new LUT\\Context(['x' => ['y' => ['z' => 42]]]),\n                $asserter = new SUT($context)\n            )\n            ->when($result = $this->invoke($asserter)->visitContext($bag))\n            ->then\n                ->integer($result)\n                    ->isEqualTo(42)\n                ->integer($asserter->visit($bag))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_context_array_dimension_1_undefined_index()\n    {\n        $this\n            ->given(\n                $bag = new LUT\\Model\\Bag\\Context('x'),\n                $bag->index('z'),\n                $context  = new LUT\\Context(['x' => ['y' => 42]]),\n                $asserter = new SUT($context)\n            )\n            ->exception(function () use ($asserter, $bag) {\n                $this->invoke($asserter)->visitContext($bag);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to access to an undefined index: z (dimension number 1 of x).')\n            ->exception(function () use ($asserter, $bag) {\n                $asserter->visit($bag);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to access to an undefined index: z (dimension number 1 of x).');\n    }\n\n    public function case_visit_context_array_dimension_1_not_an_array()\n    {\n        $this\n            ->given(\n                $bag = new LUT\\Model\\Bag\\Context('x'),\n                $bag->index('y'),\n                $context  = new LUT\\Context(['x' => 42]),\n                $asserter = new SUT($context)\n            )\n            ->exception(function () use ($asserter, $bag) {\n                $this->invoke($asserter)->visitContext($bag);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to access to an undefined index: y (dimension number 1 of x), because it is not an array.')\n            ->exception(function () use ($asserter, $bag) {\n                $asserter->visit($bag);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to access to an undefined index: y (dimension number 1 of x), because it is not an array.');\n    }\n\n    public function case_visit_context_attribute_dimension_1()\n    {\n        $this\n            ->given(\n                $bag = new LUT\\Model\\Bag\\Context('x'),\n                $bag->attribute('y'),\n                $context  = new LUT\\Context(['x' => (object) ['y' => 42]]),\n                $asserter = new SUT($context)\n            )\n            ->when($result = $this->invoke($asserter)->visitContext($bag))\n            ->then\n                ->integer($result)\n                    ->isEqualTo(42)\n                ->integer($asserter->visit($bag))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_context_attribute_dimension_2()\n    {\n        $this\n            ->given(\n                $bag = new LUT\\Model\\Bag\\Context('x'),\n                $bag->attribute('y'),\n                $bag->attribute('z'),\n                $context  = new LUT\\Context(['x' => (object) ['y' => (object) ['z' => 42]]]),\n                $asserter = new SUT($context)\n            )\n            ->when($result = $this->invoke($asserter)->visitContext($bag))\n            ->then\n                ->integer($result)\n                    ->isEqualTo(42)\n                ->integer($asserter->visit($bag))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_context_attribute_dimension_1_undefined_name()\n    {\n        $this\n            ->given(\n                $bag = new LUT\\Model\\Bag\\Context('x'),\n                $bag->attribute('z'),\n                $context  = new LUT\\Context(['x' => (object) ['y' => 42]]),\n                $asserter = new SUT($context)\n            )\n            ->exception(function () use ($asserter, $bag) {\n                $this->invoke($asserter)->visitContext($bag);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to read an undefined attribute: z (dimension number 1 of x).')\n            ->exception(function () use ($asserter, $bag) {\n                $asserter->visit($bag);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to read an undefined attribute: z (dimension number 1 of x).');\n    }\n\n    public function case_visit_context_attribute_dimension_1_not_an_object()\n    {\n        $this\n            ->given(\n                $bag = new LUT\\Model\\Bag\\Context('x'),\n                $bag->attribute('y'),\n                $context  = new LUT\\Context(['x' => 42]),\n                $asserter = new SUT($context)\n            )\n            ->exception(function () use ($asserter, $bag) {\n                $this->invoke($asserter)->visitContext($bag);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to read an undefined attribute: y (dimension number 1 of x), because it is not an object.')\n            ->exception(function () use ($asserter, $bag) {\n                $asserter->visit($bag);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to read an undefined attribute: y (dimension number 1 of x), because it is not an object.');\n    }\n\n    public function case_visit_context_method_dimension_1()\n    {\n        $this\n            ->given(\n                $bag = new LUT\\Model\\Bag\\Context('x'),\n                $bag->call(new LUT\\Model\\Operator('f', [7, 35])),\n                $context  = new LUT\\Context(['x' => new C()]),\n                $asserter = new SUT($context)\n            )\n            ->when($result = $this->invoke($asserter)->visitContext($bag))\n            ->then\n                ->integer($result)\n                    ->isEqualTo(42)\n                ->integer($asserter->visit($bag))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_context_method_dimension_2()\n    {\n        $this\n            ->given(\n                $bag = new LUT\\Model\\Bag\\Context('x'),\n                $bag->call(new LUT\\Model\\Operator('newMe')),\n                $bag->call(new LUT\\Model\\Operator('f', [7, 35])),\n                $context  = new LUT\\Context(['x' => new C()]),\n                $asserter = new SUT($context)\n            )\n            ->when($result = $this->invoke($asserter)->visitContext($bag))\n            ->then\n                ->integer($result)\n                    ->isEqualTo(42)\n                ->integer($asserter->visit($bag))\n                    ->isIdenticalTo($result);\n    }\n\n    public function case_visit_context_method_dimension_1_undefined_method()\n    {\n        $this\n            ->given(\n                $bag = new LUT\\Model\\Bag\\Context('x'),\n                $bag->call(new LUT\\Model\\Operator('h')),\n                $context  = new LUT\\Context(['x' => new C()]),\n                $asserter = new SUT($context)\n            )\n            ->exception(function () use ($asserter, $bag) {\n                $this->invoke($asserter)->visitContext($bag);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to call an undefined method: h (dimension number 1 of x).')\n            ->exception(function () use ($asserter, $bag) {\n                $asserter->visit($bag);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to call an undefined method: h (dimension number 1 of x).');\n    }\n\n    public function case_visit_context_method_dimension_1_not_an_object()\n    {\n        $this\n            ->given(\n                $bag = new LUT\\Model\\Bag\\Context('x'),\n                $bag->call(new LUT\\Model\\Operator('f', [7, 35])),\n                $context  = new LUT\\Context(['x' => 42]),\n                $asserter = new SUT($context)\n            )\n            ->exception(function () use ($asserter, $bag) {\n                $this->invoke($asserter)->visitContext($bag);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to call an undefined method: f (dimension number 1 of x), because it is not an object.')\n            ->exception(function () use ($asserter, $bag) {\n                $asserter->visit($bag);\n            })\n                ->isInstanceOf(LUT\\Exception\\Asserter::class)\n                ->hasMessage('Try to call an undefined method: f (dimension number 1 of x), because it is not an object.');\n    }\n\n    public function case_visit_context_mixed_dimensions()\n    {\n        $this\n            ->given(\n                $bag = new LUT\\Model\\Bag\\Context('x'),\n                $bag->index('y'),\n                $bag->attribute('z'),\n                $bag->call(new LUT\\Model\\Operator('f', [7, 35])),\n                $context  = new LUT\\Context(['x' => ['y' => (object) ['z' => new C()]]]),\n                $asserter = new SUT($context)\n            )\n            ->when($result = $this->invoke($asserter)->visitContext($bag))\n            ->then\n                ->integer($result)\n                    ->isEqualTo(42)\n                ->integer($asserter->visit($bag))\n                    ->isIdenticalTo($result);\n    }\n}\n\nclass C\n{\n    public function f($x, $y)\n    {\n        return $x + $y;\n    }\n\n    public function newMe()\n    {\n        return new self();\n    }\n}\n"
  },
  {
    "path": "Test/Unit/Visitor/Compiler.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Test\\Unit\\Visitor;\n\nuse Hoa\\Ruler as LUT;\nuse Hoa\\Ruler\\Visitor\\Compiler as SUT;\nuse Hoa\\Test;\nuse Hoa\\Visitor;\n\n/**\n * Class \\Hoa\\Ruler\\Test\\Unit\\Visitor\\Compiler.\n *\n * Test suite of the compiler visitor.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Compiler extends Test\\Unit\\Suite\n{\n    public function case_is_a_visitor()\n    {\n        $this\n            ->when($result = new SUT())\n            ->then\n                ->object($result)\n                    ->isInstanceOf(Visitor\\Visit::class);\n    }\n\n    public function case_model()\n    {\n        return $this->_case(\n            'true',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    true;'\n        );\n    }\n\n    public function case_operator()\n    {\n        return $this->_case(\n            '7 < 42',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    $model->{\\'<\\'}(' . \"\\n\" .\n            '        7,' . \"\\n\" .\n            '        42' . \"\\n\" .\n            '    );'\n        );\n    }\n\n    public function case_operator_is_an_identifier()\n    {\n        return $this->_case(\n            'true and false',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    $model->and(' . \"\\n\" .\n            '        true,' . \"\\n\" .\n            '        false' . \"\\n\" .\n            '    );'\n        );\n    }\n\n    public function case_function()\n    {\n        return $this->_case(\n            'f(7, 42)',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    $model->func(' . \"\\n\" .\n            '        \\'f\\',' . \"\\n\" .\n            '        7,' . \"\\n\" .\n            '        42' . \"\\n\" .\n            '    );'\n        );\n    }\n\n    public function case_function_of_arity_1()\n    {\n        return $this->_case(\n            'f(7)',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    $model->func(' . \"\\n\" .\n            '        \\'f\\',' . \"\\n\" .\n            '        7' . \"\\n\" .\n            '    );'\n        );\n    }\n\n    public function case_function_with_array_dimension()\n    {\n        return $this->_case(\n            'x(7)[42]',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    $model->func(' . \"\\n\" .\n            '        \\'x\\',' . \"\\n\" .\n            '        7' . \"\\n\" .\n            '    )' . \"\\n\" .\n            '        ->index(' . \"\\n\" .\n            '            42' . \"\\n\" .\n            '        );'\n        );\n    }\n\n    public function case_function_with_attribute_dimension()\n    {\n        return $this->_case(\n            'x(7).y',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    $model->func(' . \"\\n\" .\n            '        \\'x\\',' . \"\\n\" .\n            '        7' . \"\\n\" .\n            '    )' . \"\\n\" .\n            '        ->attribute(\\'y\\');'\n        );\n    }\n\n    public function case_function_with_call_dimension()\n    {\n        return $this->_case(\n            'x(7).y(42)',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    $model->func(' . \"\\n\" .\n            '        \\'x\\',' . \"\\n\" .\n            '        7' . \"\\n\" .\n            '    )' . \"\\n\" .\n            '        ->call(' . \"\\n\" .\n            '            $model->func(' . \"\\n\" .\n            '                \\'y\\',' . \"\\n\" .\n            '                42' . \"\\n\" .\n            '            )' . \"\\n\" .\n            '        );'\n        );\n    }\n\n    public function case_function_with_many_dimensions()\n    {\n        return $this->_case(\n            'x(7).y(42).z[153]',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    $model->func(' . \"\\n\" .\n            '        \\'x\\',' . \"\\n\" .\n            '        7' . \"\\n\" .\n            '    )' . \"\\n\" .\n            '        ->call(' . \"\\n\" .\n            '            $model->func(' . \"\\n\" .\n            '                \\'y\\',' . \"\\n\" .\n            '                42' . \"\\n\" .\n            '            )' . \"\\n\" .\n            '        )' . \"\\n\" .\n            '        ->attribute(\\'z\\')' . \"\\n\" .\n            '        ->index(' . \"\\n\" .\n            '            153' . \"\\n\" .\n            '        );'\n        );\n    }\n\n    public function case_scalar_true()\n    {\n        return $this->_case(\n            'true',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    true;'\n        );\n    }\n\n    public function case_scalar_false()\n    {\n        return $this->_case(\n            'false',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    false;'\n        );\n    }\n\n    public function case_scalar_null()\n    {\n        return $this->_case(\n            'null and true',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    $model->and(' . \"\\n\" .\n            '        null,' . \"\\n\" .\n            '        true' . \"\\n\" .\n            '    );'\n        );\n    }\n\n    public function case_scalar_numeric()\n    {\n        return $this->_case(\n            '7',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    7;'\n        );\n    }\n\n    public function case_scalar_string()\n    {\n        return $this->_case(\n            \"'Hello, World!'\",\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    \\'Hello, World!\\';'\n        );\n    }\n\n    public function case_scalar_escaped_string()\n    {\n        return $this->_case(\n            \"'He\\llo, \\'World\\'!'\",\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    \\'He\\llo, \\\\\\'World\\\\\\'!\\';'\n        );\n    }\n\n    public function case_array()\n    {\n        return $this->_case(\n            '[7, true, \\'foo\\']',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    [' . \"\\n\" .\n            '        7,' . \"\\n\" .\n            '        true,' . \"\\n\" .\n            '        \\'foo\\'' . \"\\n\" .\n            '    ];'\n        );\n    }\n\n    public function case_context()\n    {\n        return $this->_case(\n            'x',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    $model->variable(\\'x\\');'\n        );\n    }\n\n    public function case_context_with_array_dimension()\n    {\n        return $this->_case(\n            'x[7]',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    $model->variable(\\'x\\')' . \"\\n\" .\n            '        ->index(' . \"\\n\" .\n            '            7' . \"\\n\" .\n            '        );'\n        );\n    }\n\n    public function case_context_with_array_dimensions()\n    {\n        return $this->_case(\n            'x[7][42]',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    $model->variable(\\'x\\')' . \"\\n\" .\n            '        ->index(' . \"\\n\" .\n            '            7' . \"\\n\" .\n            '        )' . \"\\n\" .\n            '        ->index(' . \"\\n\" .\n            '            42' . \"\\n\" .\n            '        );'\n        );\n    }\n\n    public function case_context_with_attribute_dimension()\n    {\n        return $this->_case(\n            'x.y',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    $model->variable(\\'x\\')' . \"\\n\" .\n            '        ->attribute(\\'y\\');'\n        );\n    }\n\n    public function case_context_with_attribute_dimensions()\n    {\n        return $this->_case(\n            'x.y.z',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    $model->variable(\\'x\\')' . \"\\n\" .\n            '        ->attribute(\\'y\\')' . \"\\n\" .\n            '        ->attribute(\\'z\\');'\n        );\n    }\n\n    public function case_context_with_call_dimension()\n    {\n        return $this->_case(\n            'x.y(7)',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    $model->variable(\\'x\\')' . \"\\n\" .\n            '        ->call(' . \"\\n\" .\n            '            $model->func(' . \"\\n\" .\n            '                \\'y\\',' . \"\\n\" .\n            '                7' . \"\\n\" .\n            '            )' . \"\\n\" .\n            '        );'\n        );\n    }\n\n    public function case_context_with_call_dimensions()\n    {\n        return $this->_case(\n            'x.y(7).z(42)',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    $model->variable(\\'x\\')' . \"\\n\" .\n            '        ->call(' . \"\\n\" .\n            '            $model->func(' . \"\\n\" .\n            '                \\'y\\',' . \"\\n\" .\n            '                7' . \"\\n\" .\n            '            )' . \"\\n\" .\n            '        )' . \"\\n\" .\n            '        ->call(' . \"\\n\" .\n            '            $model->func(' . \"\\n\" .\n            '                \\'z\\',' . \"\\n\" .\n            '                42' . \"\\n\" .\n            '            )' . \"\\n\" .\n            '        );'\n        );\n    }\n\n    public function case_context_with_many_dimensions()\n    {\n        return $this->_case(\n            'a.b(7).c[42].d.e(153).f',\n            '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n            '$model->expression =' . \"\\n\" .\n            '    $model->variable(\\'a\\')' . \"\\n\" .\n            '        ->call(' . \"\\n\" .\n            '            $model->func(' . \"\\n\" .\n            '                \\'b\\',' . \"\\n\" .\n            '                7' . \"\\n\" .\n            '            )' . \"\\n\" .\n            '        )' . \"\\n\" .\n            '        ->attribute(\\'c\\')' . \"\\n\" .\n            '        ->index(' . \"\\n\" .\n            '            42' . \"\\n\" .\n            '        )' . \"\\n\" .\n            '        ->attribute(\\'d\\')' . \"\\n\" .\n            '        ->call(' . \"\\n\" .\n            '            $model->func(' . \"\\n\" .\n            '                \\'e\\',' . \"\\n\" .\n            '                153' . \"\\n\" .\n            '            )' . \"\\n\" .\n            '        )' . \"\\n\" .\n            '        ->attribute(\\'f\\');'\n        );\n    }\n\n    protected function _case($rule, $compiled)\n    {\n        $this\n            ->given($compiler = new SUT())\n            ->when($result = $compiler->visit(LUT::interpret($rule)))\n            ->then\n                ->string($result)\n                    ->isEqualTo($compiled);\n    }\n}\n"
  },
  {
    "path": "Test/Unit/Visitor/Disassembly.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Test\\Unit\\Visitor;\n\nuse Hoa\\Ruler as LUT;\nuse Hoa\\Ruler\\Visitor\\Disassembly as SUT;\nuse Hoa\\Test;\nuse Hoa\\Visitor;\n\n/**\n * Class \\Hoa\\Ruler\\Test\\Unit\\Visitor\\Disassembly.\n *\n * Test suite of the disassembly visitor.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Disassembly extends Test\\Unit\\Suite\n{\n    public function case_is_a_visitor()\n    {\n        $this\n            ->when($result = new SUT())\n            ->then\n                ->object($result)\n                    ->isInstanceOf(Visitor\\Visit::class);\n    }\n\n    public function case_model()\n    {\n        return $this->_case(\n            'true',\n            'true'\n        );\n    }\n\n    public function case_operator()\n    {\n        return $this->_case(\n            '7 < 42',\n            '(7 < 42)'\n        );\n    }\n\n    public function case_operator_is_an_identifier()\n    {\n        return $this->_case(\n            'true and false',\n            '(true and false)'\n        );\n    }\n\n    public function case_function()\n    {\n        return $this->_case(\n            'f(7, 42)',\n            'f(7, 42)'\n        );\n    }\n\n    public function case_function_of_arity_1()\n    {\n        return $this->_case(\n            'f(7)',\n            'f(7)'\n        );\n    }\n\n    public function case_function_with_array_dimensions()\n    {\n        return $this->_case(\n            'x(7)[42]',\n            'x(7)[42]'\n        );\n    }\n\n    public function case_function_with_attribute_dimensions()\n    {\n        return $this->_case(\n            'x(7).y',\n            'x(7).y'\n        );\n    }\n\n    public function case_function_with_call_dimensions()\n    {\n        return $this->_case(\n            'x(7).y(42)',\n            'x(7).y(42)'\n        );\n    }\n\n    public function case_function_with_many_dimensions()\n    {\n        return $this->_case(\n            'x(7).y(42).z[153]',\n            'x(7).y(42).z[153]'\n        );\n    }\n\n    public function case_scalar_true()\n    {\n        return $this->_case(\n            'true',\n            'true'\n        );\n    }\n\n    public function case_scalar_false()\n    {\n        return $this->_case(\n            'false',\n            'false'\n        );\n    }\n\n    public function case_scalar_null()\n    {\n        return $this->_case(\n            'null and true',\n            '(null and true)'\n        );\n    }\n\n    public function case_scalar_numeric()\n    {\n        return $this->_case(\n            '7',\n            '7'\n        );\n    }\n\n    public function case_scalar_string()\n    {\n        return $this->_case(\n            \"'Hello, World!'\",\n            \"'Hello, World!'\"\n        );\n    }\n\n    public function case_scalar_escaped_string()\n    {\n        return $this->_case(\n            \"'He\\llo, \\'World\\'!'\",\n            \"'He\\llo, \\'World\\'!'\"\n        );\n    }\n\n    public function case_array()\n    {\n        return $this->_case(\n            '[7, true, \\'foo\\']',\n            '[7, true, \\'foo\\']'\n        );\n    }\n\n    public function case_context()\n    {\n        return $this->_case(\n            'x',\n            'x'\n        );\n    }\n\n    public function case_context_with_array_dimension()\n    {\n        return $this->_case(\n            'x[7]',\n            'x[7]'\n        );\n    }\n\n    public function case_context_with_attribute_dimension()\n    {\n        return $this->_case(\n            'x.y',\n            'x.y'\n        );\n    }\n\n    public function case_context_with_call_dimension()\n    {\n        return $this->_case(\n            'x.y(7)',\n            'x.y(7)'\n        );\n    }\n\n    public function case_context_with_many_dimensions()\n    {\n        return $this->_case(\n            'x.y(7).z[42]',\n            'x.y(7).z[42]'\n        );\n    }\n\n    protected function _case($rule, $disassembled)\n    {\n        $this\n            ->given($compiler = new SUT())\n            ->when($result = $compiler->visit(LUT::interpret($rule)))\n            ->then\n                ->string($result)\n                    ->isEqualTo($disassembled);\n    }\n}\n"
  },
  {
    "path": "Test/Unit/Visitor/Interpreter.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Test\\Unit\\Visitor;\n\nuse Hoa\\Ruler as LUT;\nuse Hoa\\Ruler\\Visitor\\Interpreter as SUT;\nuse Hoa\\Test;\nuse Hoa\\Visitor;\n\n/**\n * Class \\Hoa\\Ruler\\Test\\Unit\\Visitor\\Interpreter.\n *\n * Test suite of the interpreter visitor.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Interpreter extends Test\\Unit\\Suite\n{\n    public function case_is_a_visitor()\n    {\n        $this\n            ->when($result = new SUT())\n            ->then\n                ->object($result)\n                    ->isInstanceOf(Visitor\\Visit::class);\n    }\n\n    public function case_model()\n    {\n        return $this->_case(\n            'true',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression = true;\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_operator()\n    {\n        return $this->_case(\n            '7 < 42',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    $model->{'<'}(\n                        7,\n                        42\n                    );\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_operator_is_an_identifier()\n    {\n        return $this->_case(\n            'true and false',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    $model->and(\n                        true,\n                        false\n                    );\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_operator_and()\n    {\n        return $this->_case(\n            'true and false',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    $model->and(\n                        true,\n                        false\n                    );\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_operator_or()\n    {\n        return $this->_case(\n            'true or false',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    $model->or(\n                        true,\n                        false\n                    );\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_operator_xor()\n    {\n        return $this->_case(\n            'true xor false',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    $model->xor(\n                        true,\n                        false\n                    );\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_operator_not()\n    {\n        return $this->_case(\n            'not true',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    $model->not(\n                        true\n                    );\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_function()\n    {\n        return $this->_case(\n            'f(7, 42)',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    $model->func(\n                        'f',\n                        7,\n                        42\n                    );\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_function_of_arity_1()\n    {\n        return $this->_case(\n            'f(7)',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    $model->func(\n                        'f',\n                        7\n                    );\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_function_with_array_dimension()\n    {\n        return $this->_case(\n            'x(7)[42]',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    $model->func(\n                        'x',\n                        7\n                    )\n                        ->index(\n                            42\n                        );\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_function_with_attribute_dimension()\n    {\n        return $this->_case(\n            'x(7).y',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    $model->func(\n                        'x',\n                        7\n                    )\n                        ->attribute('y');\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_function_with_call_dimension()\n    {\n        return $this->_case(\n            'x(7).y(42)',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    $model->func(\n                        'x',\n                        7\n                    )\n                        ->call(\n                            $model->func(\n                                'y',\n                                42\n                            )\n                        );\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_function_with_many_dimensions()\n    {\n        return $this->_case(\n            'x(7).y(42).z[153]',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    $model->func(\n                        'x',\n                        7\n                    )\n                        ->call(\n                            $model->func(\n                                'y',\n                                42\n                            )\n                        )\n                        ->attribute('z')\n                        ->index(\n                            153\n                        );\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_scalar_true()\n    {\n        return $this->_case(\n            'true',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    true;\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_scalar_false()\n    {\n        return $this->_case(\n            'false',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    false;\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_scalar_null()\n    {\n        return $this->_case(\n            'null and true',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    $model->and(\n                        null,\n                        true\n                    );\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_scalar_float()\n    {\n        return $this->_case(\n            '4.2',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    4.2;\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_scalar_integer()\n    {\n        return $this->_case(\n            '7',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    7;\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_scalar_string()\n    {\n        return $this->_case(\n            \"'Hello, World!'\",\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    'Hello, World!';\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_scalar_escaped_string()\n    {\n        return $this->_case(\n            \"'He\\llo, \\'World\\'!'\",\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    'He\\llo, \\'World\\'!';\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_array()\n    {\n        return $this->_case(\n            '[7, true, \\'foo\\']',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    [\n                        7,\n                        true,\n                        'foo'\n                    ];\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_context()\n    {\n        return $this->_case(\n            'x',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    $model->variable('x');\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_context_with_array_dimension()\n    {\n        return $this->_case(\n            'x[7]',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    $model->variable('x')\n                        ->index(\n                            7\n                        );\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_context_with_array_dimensions()\n    {\n        return $this->_case(\n            'x[7][42]',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    $model->variable('x')\n                        ->index(\n                            7\n                        )\n                        ->index(\n                            42\n                        );\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_context_with_attribute_dimension()\n    {\n        return $this->_case(\n            'x.y',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    $model->variable('x')\n                        ->attribute('y');\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_context_with_attribute_dimensions()\n    {\n        return $this->_case(\n            'x.y.z',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    $model->variable('x')\n                        ->attribute('y')\n                        ->attribute('z');\n\n                return $model;\n            }\n        );\n    }\n\n    public function case_context_with_many_dimensions()\n    {\n        return $this->_case(\n            'a.b(7).c[42].d.e(153).f',\n            function () {\n                $model = new LUT\\Model();\n                $model->expression =\n                    $model->variable('a')\n                        ->call(\n                            $model->func(\n                                'b',\n                                7\n                            )\n                        )\n                        ->attribute('c')\n                        ->index(\n                            42\n                        )\n                        ->attribute('d')\n                        ->call(\n                            $model->func(\n                                'e',\n                                153\n                            )\n                        )\n                        ->attribute('f');\n\n                return $model;\n            }\n        );\n    }\n\n    protected function _case($rule, \\Closure $expected)\n    {\n        $this\n            ->given(\n                $interpreter = new SUT(),\n                $ast         = LUT::getCompiler()->parse($rule)\n            )\n            ->when($result = $interpreter->visit($ast))\n            ->then\n                ->object($result)\n                    ->isEqualTo($expected());\n    }\n}\n"
  },
  {
    "path": "Visitor/Asserter.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Visitor;\n\nuse ArrayAccess;\nuse Hoa\\Consistency;\nuse Hoa\\Ruler;\nuse Hoa\\Visitor;\n\n/**\n * Class \\Hoa\\Ruler\\Visitor\\Asserter.\n *\n * Asserter: evaluate a model representing a rule.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Asserter implements Visitor\\Visit\n{\n    /**\n     * Context.\n     *\n     * @var \\Hoa\\Ruler\\Context\n     */\n    protected $_context   = null;\n\n    /**\n     * List of operators.\n     *\n     * @var array\n     */\n    protected $_operators = [];\n\n\n\n    /**\n     * Constructor.\n     *\n     * @param   \\Hoa\\Ruler\\Context  $context    Context.\n     */\n    public function __construct(Ruler\\Context $context = null)\n    {\n        if (null !== $context) {\n            $this->setContext($context);\n        }\n\n        $this->setOperator('and', function ($a = false, $b = false) { return $a && $b; });\n        $this->setOperator('or',  function ($a = false, $b = false) { return $a || $b; });\n        $this->setOperator('xor', function ($a, $b) { return (bool) ($a ^ $b); });\n        $this->setOperator('not', function ($a) { return !$a; });\n        $this->setOperator('=',   function ($a, $b) { return $a == $b; });\n        $this->setOperator('is',  $this->getOperator('='));\n        $this->setOperator('!=',  function ($a, $b) { return $a != $b; });\n        $this->setOperator('>',   function ($a, $b) { return $a >  $b; });\n        $this->setOperator('>=',  function ($a, $b) { return $a >= $b; });\n        $this->setOperator('<',   function ($a, $b) { return $a <  $b; });\n        $this->setOperator('<=',  function ($a, $b) { return $a <= $b; });\n        $this->setOperator('in',  function ($a, array $b) { return in_array($a, $b); });\n        $this->setOperator('sum', function () { return array_sum(func_get_args()); });\n        $this->setOperator('matches', function ($subject, $pattern) {\n            $escapedPattern = preg_replace('/(?<!\\\\\\)`/', '\\`', $pattern);\n\n            return 0 !== preg_match('`' . $escapedPattern . '`', $subject);\n        });\n\n        return;\n    }\n\n    /**\n     * Visit an element.\n     *\n     * @param   \\Hoa\\Visitor\\Element  $element    Element to visit.\n     * @param   mixed                 &$handle    Handle (reference).\n     * @param   mixed                 $eldnah     Handle (not reference).\n     * @return  mixed\n     * @throws  \\Hoa\\Ruler\\Exception\\Asserter\n     */\n    public function visit(Visitor\\Element $element, &$handle = null, $eldnah = null)\n    {\n        if ($element instanceof Ruler\\Model) {\n            return $this->visitModel($element, $handle, $eldnah);\n        }\n\n        if ($element instanceof Ruler\\Model\\Operator) {\n            return $this->visitOperator($element, $handle, $eldnah);\n        }\n\n        if ($element instanceof Ruler\\Model\\Bag\\Scalar) {\n            return $this->visitScalar($element, $handle, $eldnah);\n        }\n\n        if ($element instanceof Ruler\\Model\\Bag\\RulerArray) {\n            return $this->visitArray($element, $handle, $eldnah);\n        }\n\n        if ($element instanceof Ruler\\Model\\Bag\\Context) {\n            return $this->visitContext($element, $handle, $eldnah);\n        }\n    }\n\n    /**\n     * Visit a model\n     *\n     * @param   \\Hoa\\Visitor\\Element  $element    Element to visit.\n     * @param   mixed                 &$handle    Handle (reference).\n     * @param   mixed                 $eldnah     Handle (not reference).\n     * @return  mixed\n     */\n    public function visitModel(Ruler\\Model $element, &$handle = null, $eldnah = null)\n    {\n        return (bool) $element->getExpression()->accept($this, $handle, $eldnah);\n    }\n\n    /**\n     * Visit an operator\n     *\n     * @param   \\Hoa\\Visitor\\Element  $element    Element to visit.\n     * @param   mixed                 &$handle    Handle (reference).\n     * @param   mixed                 $eldnah     Handle (not reference).\n     * @return  mixed\n     */\n    protected function visitOperator(Ruler\\Model\\Operator $element, &$handle = null, $eldnah = null)\n    {\n        $name      = $element->getName();\n        $arguments = [];\n\n        foreach ($element->getArguments() as $argument) {\n            $value       = $argument->accept($this, $handle, $eldnah);\n            $arguments[] = $value;\n\n            if ($element::LAZY_BREAK === $element->shouldBreakLazyEvaluation($value)) {\n                break;\n            }\n        }\n\n        if (false === $this->operatorExists($name)) {\n            throw new Ruler\\Exception\\Asserter(\n                'Operator %s does not exist.',\n                0,\n                $name\n            );\n        }\n\n        $value = $this->getOperator($name)->distributeArguments($arguments);\n\n        return $this->visitDimensions(\n            $element,\n            $name . '()',\n            $value,\n            $handle,\n            $eldnah\n        );\n    }\n\n    /**\n     * Visit a scalar\n     *\n     * @param   \\Hoa\\Visitor\\Element  $element    Element to visit.\n     * @param   mixed                 &$handle    Handle (reference).\n     * @param   mixed                 $eldnah     Handle (not reference).\n     * @return  mixed\n     */\n    protected function visitScalar(Ruler\\Model\\Bag\\Scalar $element, &$handle = null, $eldnah = null)\n    {\n        return $element->getValue();\n    }\n\n    /**\n     * Visit an array\n     *\n     * @param   \\Hoa\\Visitor\\Element  $element    Element to visit.\n     * @param   mixed                 &$handle    Handle (reference).\n     * @param   mixed                 $eldnah     Handle (not reference).\n     * @return  array\n     */\n    protected function visitArray(Ruler\\Model\\Bag\\RulerArray $element, &$handle = null, $eldnah = null)\n    {\n        $out = [];\n\n        foreach ($element->getArray() as $key => $data) {\n            $out[$key] = $data->accept($this, $handle, $eldnah);\n        }\n\n        return $out;\n    }\n\n    /**\n     * Visit a context\n     *\n     * @param   \\Hoa\\Visitor\\Element  $element    Element to visit.\n     * @param   mixed                 &$handle    Handle (reference).\n     * @param   mixed                 $eldnah     Handle (not reference).\n     * @return  mixed\n     */\n    protected function visitContext(Ruler\\Model\\Bag\\Context $element, &$handle = null, $eldnah = null)\n    {\n        $context = $this->getContext();\n\n        if (null === $context) {\n            throw new Ruler\\Exception\\Asserter(\n                'Assert needs a context to work properly.',\n                1\n            );\n        }\n\n        $id = $element->getId();\n\n        if (!isset($context[$id])) {\n            throw new Ruler\\Exception\\Asserter(\n                'Context reference %s does not exist.',\n                2,\n                $id\n            );\n        }\n\n        return $this->visitDimensions(\n            $element,\n            $id,\n            $context[$id],\n            $handle,\n            $eldnah\n        );\n    }\n\n    /**\n     * Visit dimensions of a context.\n     *\n     * @param   \\Hoa\\Visitor\\Element  $element      Element to visit.\n     * @param   string                $elementId    Element name.\n     * @param   mixed                 $subject      Current root to apply the dimensions.\n     * @param   mixed                 &$handle      Handle (reference).\n     * @param   mixed                 $eldnah       Handle (not reference).\n     * @return  mixed\n     */\n    protected function visitDimensions(\n        Ruler\\Model\\Bag\\Context $element,\n        $elementId,\n        $subject,\n        &$handle = null,\n        $eldnah  = null\n    ) {\n        $pointer = $subject;\n\n        foreach ($element->getDimensions() as $dimensionNumber => $dimension) {\n            ++$dimensionNumber;\n\n            switch ($dimension[Ruler\\Model\\Bag\\Context::ACCESS_TYPE]) {\n                case Ruler\\Model\\Bag\\Context::ARRAY_ACCESS:\n                    $this->visitArrayDimension(\n                        $pointer,\n                        $dimension,\n                        $dimensionNumber,\n                        $elementId,\n                        $handle,\n                        $eldnah\n                    );\n\n                    break;\n\n                case Ruler\\Model\\Bag\\Context::ATTRIBUTE_ACCESS:\n                    $this->visitAttributeDimension(\n                        $pointer,\n                        $dimension,\n                        $dimensionNumber,\n                        $elementId,\n                        $handle,\n                        $eldnah\n                    );\n\n                    break;\n\n                case Ruler\\Model\\Bag\\Context::METHOD_ACCESS:\n                    $this->visitMethodDimension(\n                        $pointer,\n                        $dimension,\n                        $dimensionNumber,\n                        $elementId,\n                        $handle,\n                        $eldnah\n                    );\n\n                    break;\n            }\n        }\n\n        return $pointer;\n    }\n\n    /**\n     * Visit an array dimension.\n     *\n     * @param   mixed   &$contextPointer    Pointer to the current context.\n     * @param   array   $dimension          Dimension bucket.\n     * @param   int     $dimensionNumber    Dimension number.\n     * @param   string  $elementId          Element name.\n     * @param   mixed   &$handle            Handle (reference).\n     * @param   mixed   $eldnah             Handle (not reference).\n     * @return  void\n     */\n    protected function visitArrayDimension(\n        &$contextPointer,\n        array $dimension,\n        $dimensionNumber,\n        $elementId,\n        &$handle = null,\n        $eldnah  = null\n    ) {\n        $value = $dimension[Ruler\\Model\\Bag\\Context::ACCESS_VALUE];\n        $key   = $value->accept($this, $handle, $eldnah);\n\n        $isArray     = is_array($contextPointer);\n        $isArrayLike = !$isArray && $contextPointer instanceof ArrayAccess;\n\n        if (false === $isArray && false === $isArrayLike) {\n            throw new Ruler\\Exception\\Asserter(\n                'Try to access to an undefined index: %s ' .\n                '(dimension number %d of %s), because it is ' .\n                'not an array.',\n                3,\n                [$key, $dimensionNumber, $elementId]\n            );\n        }\n\n        if ((true === $isArray     && false === array_key_exists($key, $contextPointer)) ||\n            (true === $isArrayLike && false === $contextPointer->offsetExists($key))) {\n            throw new Ruler\\Exception\\Asserter(\n                'Try to access to an undefined index: %s ' .\n                '(dimension number %d of %s).',\n                4,\n                [$key, $dimensionNumber, $elementId]\n            );\n        }\n\n        $contextPointer = $contextPointer[$key];\n\n        return;\n    }\n\n\n    /**\n     * Visit an attribute dimension.\n     *\n     * @param   mixed   &$contextPointer    Pointer to the current context.\n     * @param   array   $dimension          Dimension bucket.\n     * @param   int     $dimensionNumber    Dimension number.\n     * @param   string  $elementId          Element name.\n     * @param   mixed   &$handle            Handle (reference).\n     * @param   mixed   $eldnah             Handle (not reference).\n     * @return  void\n     */\n    protected function visitAttributeDimension(\n        &$contextPointer,\n        array $dimension,\n        $dimensionNumber,\n        $elementId,\n        &$handle = null,\n        $eldnah  = null)\n    {\n        $attribute = $dimension[Ruler\\Model\\Bag\\Context::ACCESS_VALUE];\n\n        if (!is_object($contextPointer)) {\n            throw new Ruler\\Exception\\Asserter(\n                'Try to read an undefined attribute: %s ' .\n                '(dimension number %d of %s), because it is ' .\n                'not an object.',\n                5,\n                [$attribute, $dimensionNumber, $elementId]\n            );\n        }\n\n        if (!property_exists($contextPointer, $attribute)) {\n            throw new Ruler\\Exception\\Asserter(\n                'Try to read an undefined attribute: %s ' .\n                '(dimension number %d of %s).',\n                6,\n                [$attribute, $dimensionNumber, $elementId]\n            );\n        }\n\n        $contextPointer = $contextPointer->$attribute;\n\n        return;\n    }\n\n    /**\n     * Visit a method dimension.\n     *\n     * @param   mixed   &$contextPointer    Pointer to the current context.\n     * @param   array   $dimension          Dimension bucket.\n     * @param   int     $dimensionNumber    Dimension number.\n     * @param   string  $elementId          Element name.\n     * @param   mixed   &$handle            Handle (reference).\n     * @param   mixed   $eldnah             Handle (not reference).\n     * @return  void\n     */\n    protected function visitMethodDimension(\n        &$contextPointer,\n        array $dimension,\n        $dimensionNumber,\n        $elementId,\n        &$handle = null,\n        $eldnah  = null\n    ) {\n        $value  = $dimension[Ruler\\Model\\Bag\\Context::ACCESS_VALUE];\n        $method = $value->getName();\n\n        if (!is_object($contextPointer)) {\n            throw new Ruler\\Exception\\Asserter(\n                'Try to call an undefined method: %s ' .\n                '(dimension number %d of %s), because it is ' .\n                'not an object.',\n                7,\n                [$method, $dimensionNumber, $elementId]\n            );\n        }\n\n        if (!method_exists($contextPointer, $method)) {\n            throw new Ruler\\Exception\\Asserter(\n                'Try to call an undefined method: %s ' .\n                '(dimension number %d of %s).',\n                8,\n                [$method, $dimensionNumber, $elementId]\n            );\n        }\n\n        $arguments = [];\n\n        foreach ($value->getArguments() as $argument) {\n            $arguments[] = $argument->accept($this, $handle, $eldnah);\n        }\n\n        $contextPointer = call_user_func_array(\n            [$contextPointer, $method],\n            $arguments\n        );\n\n        return;\n    }\n\n    /**\n     * Set context.\n     *\n     * @param   \\Hoa\\Ruler\\Context  $context    Context.\n     * @return  \\Hoa\\Ruler\\Context\n     */\n    public function setContext(Ruler\\Context $context)\n    {\n        $old            = $this->_context;\n        $this->_context = $context;\n\n        return $old;\n    }\n\n    /**\n     * Get context.\n     *\n     * @return  \\Hoa\\Ruler\\Context\n     */\n    public function getContext()\n    {\n        return $this->_context;\n    }\n\n    /**\n     * Set an operator.\n     *\n     * @param   string    $operator    Operator.\n     * @param   callable  $callable    Callable.\n     * @return  Ruler\\Visitor\\Asserter\n     */\n    public function setOperator($operator, $callable)\n    {\n        $this->_operators[$operator] = $callable;\n\n        return $this;\n    }\n\n    /**\n     * Check if an operator exists.\n     *\n     * @param   string  $operator    Operator.\n     * @return  bool\n     */\n    public function operatorExists($operator)\n    {\n        return true === array_key_exists($operator, $this->_operators);\n    }\n\n    /**\n     * Get an operator.\n     *\n     * @param   string  $operator    Operator.\n     * @return  string\n     */\n    public function getOperator($operator)\n    {\n        if (false === $this->operatorExists($operator)) {\n            return null;\n        }\n\n        $handle = &$this->_operators[$operator];\n\n        if (!($handle instanceof Consistency\\Xcallable)) {\n            $handle = xcallable($handle);\n        }\n\n        return $this->_operators[$operator];\n    }\n\n    /**\n     * Get all operators.\n     *\n     * @return  array\n     */\n    public function getOperators()\n    {\n        foreach ($this->_operators as &$operator) {\n            if (!($operator instanceof Consistency\\Xcallable)) {\n                $operator = xcallable($operator);\n            }\n        }\n\n        return $this->_operators;\n    }\n}\n"
  },
  {
    "path": "Visitor/Compiler.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Visitor;\n\nuse Hoa\\Consistency;\nuse Hoa\\Ruler;\nuse Hoa\\Visitor;\n\n/**\n * Class \\Hoa\\Ruler\\Visitor\\Compiler.\n *\n * Compiler: rule model to PHP.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Compiler implements Visitor\\Visit\n{\n    /**\n     * Indentation level.\n     *\n     * @var int\n     */\n    protected $_indentation = 0;\n\n\n\n    /**\n     * Visit an element.\n     *\n     * @param   \\Hoa\\Visitor\\Element  $element    Element to visit.\n     * @param   mixed                 &$handle    Handle (reference).\n     * @param   mixed                 $eldnah     Handle (not reference).\n     * @return  mixed\n     */\n    public function visit(Visitor\\Element $element, &$handle = null, $eldnah = null)\n    {\n        $out = null;\n        $_   = str_repeat('    ', $this->_indentation);\n\n        if ($element instanceof Ruler\\Model) {\n            $expression = $element->getExpression();\n\n            if (null === $expression) {\n                $out = '';\n            } else {\n                $this->_indentation = 1;\n\n                $out =\n                    '$model = new \\Hoa\\Ruler\\Model();' . \"\\n\" .\n                    '$model->expression =' . \"\\n\" .\n                    $expression->accept($this, $handle, $eldnah) .\n                    ';';\n            }\n        } elseif ($element instanceof Ruler\\Model\\Operator) {\n            $out     = $_ . '$model->';\n            $name    = $element->getName();\n            $_handle = [];\n\n            if (false === $element->isFunction()) {\n                if (true === Consistency::isIdentifier($name)) {\n                    $out .= $name;\n                } else {\n                    $out .= '{\\'' . $name . '\\'}';\n                }\n\n                $out .= '(' . \"\\n\";\n            } else {\n                $out       .= 'func(' . \"\\n\" . $_ . '    ';\n                $_handle[]  = '\\'' . $name . '\\'';\n            }\n\n            ++$this->_indentation;\n\n            foreach ($element->getArguments() as $argument) {\n                $_handle[] = $argument->accept($this, $handle, $eldnah);\n            }\n\n            $out .=\n                implode(',' . \"\\n\", $_handle) . \"\\n\" . $_ . ')' .\n                $this->visitContext($element, $handle, $eldnah, $_);\n\n            --$this->_indentation;\n        } elseif ($element instanceof Ruler\\Model\\Bag\\Scalar) {\n            $value = $element->getValue();\n            $out   = $_;\n\n            if (true === $value) {\n                $out .= 'true';\n            } elseif (false === $value) {\n                $out .= 'false';\n            } elseif (null === $value) {\n                $out .= 'null';\n            } elseif (is_numeric($value)) {\n                $out .= (string) $value;\n            } else {\n                $out .= '\\'' . str_replace(['\\'', '\\\\\\\\'], ['\\\\\\'', '\\\\'], $value) . '\\'';\n            }\n        } elseif ($element instanceof Ruler\\Model\\Bag\\RulerArray) {\n            $values = [];\n            ++$this->_indentation;\n\n            foreach ($element->getArray() as $value) {\n                $values[] = $value->accept($this, $handle, $eldnah);\n            }\n\n            --$this->_indentation;\n            $out =\n                $_ . '[' . \"\\n\" .\n                implode(',' . \"\\n\", $values) . \"\\n\" .\n                $_ . ']';\n        } elseif ($element instanceof Ruler\\Model\\Bag\\Context) {\n            ++$this->_indentation;\n\n            $out =\n                $_ . '$model->variable(\\'' . $element->getId() . '\\')' .\n                $this->visitContext($element, $handle, $eldnah, $_);\n\n            --$this->_indentation;\n        }\n\n        return $out;\n    }\n\n    /**\n     * Visit a context.\n     *\n     * @param   \\Hoa\\Ruler\\Model\\Bag\\Context  $context    Context.\n     * @param   mixed                         &$handle    Handle (reference).\n     * @param   mixed                         $eldnah     Handle (not reference).\n     * @param   string                        $_          Indentation.\n     * @return  mixed\n     */\n    protected function visitContext(Ruler\\Model\\Bag\\Context $context, &$handle, $eldnah, $_)\n    {\n        $out = null;\n\n        foreach ($context->getDimensions() as $dimension) {\n            ++$this->_indentation;\n\n            $value  = $dimension[Ruler\\Model\\Bag\\Context::ACCESS_VALUE];\n            $out   .= \"\\n\" . $_ . '    ->';\n\n            switch ($dimension[Ruler\\Model\\Bag\\Context::ACCESS_TYPE]) {\n                case Ruler\\Model\\Bag\\Context::ARRAY_ACCESS:\n                    $out .=\n                        'index(' . \"\\n\" .\n                        $value->accept($this, $handle, $eldnah) . \"\\n\" .\n                        $_ . '    )';\n\n                    break;\n\n                case Ruler\\Model\\Bag\\Context::ATTRIBUTE_ACCESS:\n                    $out .= 'attribute(\\'' . $value . '\\')';\n\n                    break;\n\n                case Ruler\\Model\\Bag\\Context::METHOD_ACCESS:\n                    $out .=\n                        'call(' . \"\\n\" .\n                        $value->accept($this, $handle, $eldnah) . \"\\n\" .\n                        $_ . '    )';\n\n                    break;\n            }\n\n            --$this->_indentation;\n        }\n\n        return $out;\n    }\n}\n"
  },
  {
    "path": "Visitor/Disassembly.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Visitor;\n\nuse Hoa\\Ruler;\nuse Hoa\\Visitor;\n\n/**\n * Class \\Hoa\\Ruler\\Visitor\\Disassembly.\n *\n * Disassembly: rule model to rule as a regular string.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Disassembly implements Visitor\\Visit\n{\n    /**\n     * Visit an element.\n     *\n     * @param   \\Hoa\\Visitor\\Element  $element    Element to visit.\n     * @param   mixed                 &$handle    Handle (reference).\n     * @param   mixed                 $eldnah     Handle (not reference).\n     * @return  mixed\n     */\n    public function visit(Visitor\\Element $element, &$handle = null, $eldnah = null)\n    {\n        $out = null;\n\n        if ($element instanceof Ruler\\Model) {\n            $out .= $element->getExpression()->accept($this, $handle, $eldnah);\n        } elseif ($element instanceof Ruler\\Model\\Operator) {\n            $name      = $element->getName();\n            $arguments = [];\n\n            foreach ($element->getArguments() as $argument) {\n                $arguments[] = $argument->accept($this, $handle, $eldnah);\n            }\n\n            if (true === $element->isFunction()) {\n                $out .= $name . '(' . implode(', ', $arguments) . ')';\n            } else {\n                if (!isset($arguments[1])) {\n                    $_out = $name . ' ' . $arguments[0];\n                } else {\n                    $_out = '(' . $arguments[0] . ' ' . $name . ' ' . $arguments[1] . ')';\n                }\n\n                $out .= $_out;\n            }\n\n            $out .= $this->visitContext($element, $handle, $eldnah);\n        } elseif ($element instanceof Ruler\\Model\\Bag\\Scalar) {\n            $value = $element->getValue();\n\n            if (true === $value) {\n                $out .= 'true';\n            } elseif (false === $value) {\n                $out .= 'false';\n            } elseif (null === $value) {\n                $out .= 'null';\n            } elseif (is_numeric($value)) {\n                $out .= (string) $value;\n            } else {\n                $out .= '\\'' . str_replace(['\\\\', '\\''], ['\\\\', '\\\\\\''], $value) . '\\'';\n            }\n        } elseif ($element instanceof Ruler\\Model\\Bag\\RulerArray) {\n            $values = [];\n\n            foreach ($element->getArray() as $value) {\n                $values[] = $value->accept($this, $handle, $eldnah);\n            }\n\n            $out .= '[' . implode(', ', $values) . ']';\n        } elseif ($element instanceof Ruler\\Model\\Bag\\Context) {\n            $out .= $element->getId() . $this->visitContext($element, $handle, $eldnah);\n        }\n\n        return $out;\n    }\n\n    /**\n     * Visit a context.\n     *\n     * @param   \\Hoa\\Ruler\\Model\\Bag\\Context  $context    Context.\n     * @param   mixed                         &$handle    Handle (reference).\n     * @param   mixed                         $eldnah     Handle (not reference).\n     * @return  mixed\n     */\n    protected function visitContext(Ruler\\Model\\Bag\\Context $context, &$handle, $eldnah)\n    {\n        $out = null;\n\n        foreach ($context->getDimensions() as $dimension) {\n            $value = $dimension[Ruler\\Model\\Bag\\Context::ACCESS_VALUE];\n\n            switch ($dimension[Ruler\\Model\\Bag\\Context::ACCESS_TYPE]) {\n                case Ruler\\Model\\Bag\\Context::ARRAY_ACCESS:\n                    $out .=\n                        '[' .\n                        $value->accept($this, $handle, $eldnah) .\n                        ']';\n\n                    break;\n\n                case Ruler\\Model\\Bag\\Context::ATTRIBUTE_ACCESS:\n                    $out .= '.' . $value;\n\n                    break;\n\n                case Ruler\\Model\\Bag\\Context::METHOD_ACCESS:\n                    $out .= '.' . $value->accept($this, $handle, $eldnah);\n\n                    break;\n            }\n        }\n\n        return $out;\n    }\n}\n"
  },
  {
    "path": "Visitor/Interpreter.php",
    "content": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the Hoa nor the names of its contributors may be\n *       used to endorse or promote products derived from this software without\n *       specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\nnamespace Hoa\\Ruler\\Visitor;\n\nuse Hoa\\Ruler;\nuse Hoa\\Visitor;\n\n/**\n * Class \\Hoa\\Ruler\\Visitor\\Interpreter.\n *\n * Interpreter: rule to model.\n *\n * @copyright  Copyright © 2007-2017 Hoa community\n * @license    New BSD License\n */\nclass Interpreter implements Visitor\\Visit\n{\n    /**\n     * Root.\n     *\n     * @var \\Hoa\\Ruler\\Model\n     */\n    protected $_root    = null;\n\n    /**\n     * Current node.\n     *\n     * @var \\Hoa\\Ruler\\Visitor\\Interpreter\n     */\n    protected $_current = null;\n\n\n\n    /**\n     * Visit an element.\n     *\n     * @param   \\Hoa\\Visitor\\Element  $element    Element to visit.\n     * @param   mixed                 &$handle    Handle (reference).\n     * @param   mixed                 $eldnah     Handle (not reference).\n     * @return  mixed\n     * @throws  \\Hoa\\Ruler\\Exception\\Interpreter\n     */\n    public function visit(Visitor\\Element $element, &$handle = null, $eldnah = null)\n    {\n        $id       = $element->getId();\n        $variable = false !== $eldnah;\n\n        switch ($id) {\n            case '#expression':\n                $this->_root             = new Ruler\\Model();\n                $this->_root->expression = $element->getChild(0)->accept(\n                    $this,\n                    $handle,\n                    $eldnah\n                );\n\n                return $this->_root;\n\n            case '#operation':\n                $children = $element->getChildren();\n                $left     = $children[0]->accept($this, $handle, $eldnah);\n                $right    = $children[2]->accept($this, $handle, $eldnah);\n                $name     = $children[1]->accept($this, $handle, false);\n\n                return $this->_root->_operator(\n                    $name,\n                    [$left, $right],\n                    false\n                );\n\n            case '#variable_access':\n                $children = $element->getChildren();\n                $name     = $children[0]->accept($this, $handle, $eldnah);\n                array_shift($children);\n\n                foreach ($children as $child) {\n                    $_child = $child->accept($this, $handle, $eldnah);\n\n                    switch ($child->getId()) {\n                        case '#array_access':\n                            $name->index($_child);\n\n                            break;\n\n                        case '#attribute_access':\n                            $name->attribute($_child);\n\n                            break;\n\n                        case '#method_access':\n                            $name->call($_child);\n\n                            break;\n                    }\n                }\n\n                return $name;\n\n            case '#array_access':\n                return $element->getChild(0)->accept($this, $handle, $eldnah);\n\n            case '#attribute_access':\n                return $element->getChild(0)->accept($this, $handle, false);\n\n            case '#method_access':\n                return $element->getChild(0)->accept($this, $handle, $eldnah);\n\n            case '#array_declaration':\n                $out = [];\n\n                foreach ($element->getChildren() as $child) {\n                    $out[] = $child->accept($this, $handle, $eldnah);\n                }\n\n                return $out;\n\n            case '#function_call':\n                $children = $element->getChildren();\n                $name     = $children[0]->accept($this, $handle, false);\n                array_shift($children);\n\n                $arguments = [];\n\n                foreach ($children as $child) {\n                    $arguments[] = $child->accept($this, $handle, $eldnah);\n                }\n\n                return $this->_root->_operator(\n                    $name,\n                    $arguments,\n                    true\n                );\n\n            case '#and':\n            case '#or':\n            case '#xor':\n                $name     = substr($id, 1);\n                $children = $element->getChildren();\n                $left     = $children[0]->accept($this, $handle, $eldnah);\n                $right    = $children[1]->accept($this, $handle, $eldnah);\n\n                return $this->_root->operation(\n                    $name,\n                    [$left, $right]\n                );\n\n            case '#not':\n                return $this->_root->operation(\n                    'not',\n                    [$element->getChild(0)->accept($this, $handle, $eldnah)]\n                );\n\n            case 'token':\n                $token = $element->getValueToken();\n                $value = $element->getValueValue();\n\n                switch ($token) {\n                    case 'identifier':\n                        return\n                            true === $variable\n                                ? $this->_root->variable($value)\n                                : $value;\n\n                    case 'true':\n                        return true;\n\n                    case 'false':\n                        return false;\n\n                    case 'null':\n                        return null;\n\n                    case 'float':\n                        return floatval($value);\n\n                    case 'integer':\n                        return intval($value);\n\n                    case 'string':\n                        return str_replace(\n                            '\\\\' . $value[0],\n                            $value[0],\n                            substr($value, 1, -1)\n                        );\n\n                    default:\n                        throw new Ruler\\Exception\\Interpreter(\n                            'Token %s is unknown.',\n                            0,\n                            $token\n                        );\n                }\n\n                break;\n\n            default:\n                throw new Ruler\\Exception\\Interpreter(\n                    'Element %s is unknown.',\n                    1,\n                    $id\n                );\n        }\n\n        return;\n    }\n\n    /**\n     * Get root.\n     *\n     * @return  \\Hoa\\Ruler\\Model\n     */\n    public function getRoot()\n    {\n        return $this->_root;\n    }\n}\n"
  },
  {
    "path": "bors.toml",
    "content": "status = [\n  \"continuous-integration/travis-ci/push\"\n]\ntimeout_sec = 1800\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\"       : \"hoa/ruler\",\n    \"description\": \"The Hoa\\\\Ruler library.\",\n    \"type\"       : \"library\",\n    \"keywords\"   : [\"library\", \"ruler\"],\n    \"homepage\"   : \"https://hoa-project.net/\",\n    \"license\"    : \"BSD-3-Clause\",\n    \"authors\"    : [\n        {\n            \"name\" : \"Ivan Enderlin\",\n            \"email\": \"ivan.enderlin@hoa-project.net\"\n        },\n        {\n            \"name\"    : \"Hoa community\",\n            \"homepage\": \"https://hoa-project.net/\"\n        }\n    ],\n    \"support\": {\n        \"email\" : \"support@hoa-project.net\",\n        \"irc\"   : \"irc://chat.freenode.net/hoaproject\",\n        \"forum\" : \"https://users.hoa-project.net/\",\n        \"docs\"  : \"https://central.hoa-project.net/Documentation/Library/Ruler\",\n        \"source\": \"https://central.hoa-project.net/Resource/Library/Ruler\"\n    },\n    \"require\": {\n        \"php\"            : \">=5.5.0\",\n        \"hoa/compiler\"   : \"~3.0\",\n        \"hoa/consistency\": \"~1.0\",\n        \"hoa/exception\"  : \"~1.0\",\n        \"hoa/file\"       : \"~1.0\",\n        \"hoa/protocol\"   : \"~1.0\",\n        \"hoa/visitor\"    : \"~2.0\"\n    },\n    \"require-dev\": {\n        \"hoa/test\": \"~2.0\"\n    },\n    \"autoload\": {\n        \"psr-4\": {\n            \"Hoa\\\\Ruler\\\\\": \".\"\n        }\n    },\n    \"extra\": {\n        \"branch-alias\": {\n            \"dev-master\": \"2.x-dev\"\n        }\n    }\n}\n"
  }
]