PHP Classes

Xpresion PHP Parser Engine: Parse expressions that may contain variables

Recommend this page to a friend!
  Info   View files Example   Demos   Screenshots Screenshots   View files View files (15)   DownloadInstall with Composer Download .zip   Reputation   Support forum   Blog    
Ratings Unique User Downloads Download Rankings
Not yet rated by the usersTotal: 65 This week: 1All time: 10,347 This week: 571Up
Version License PHP version Categories
xpresion 1.0.1Artistic License5PHP 5, Math, Parsers
Description 

Author

This package implements an engine to parse expressions that may contain variables.

It can take an expression and parses it to extract it structure of values, operators and values.

The package may also evaluate the expression performing any operations defined in the expression, eventually assigning values to any variables that are used in the expression.

It supports arithmetic operations, several types of math expressions and array operations.

Besides the PHP version, the package provides equivalent versions in JavaScript and Python.

Picture of Nikos M.
Name: Nikos M. is available for providing paid consulting. Contact Nikos M. .
Classes: 17 packages by
Country: Greece Greece
Age: 47
All time rank: 8609 in Greece Greece
Week rank: 13 Up2 in Greece Greece Up
Innovation award
Innovation award
Nominee: 7x

Winner: 2x

Example

<?php
require('../src/php/Xpresion.php');
function
echo_($s='')
{
    echo
$s . PHP_EOL;
}

function
test_expr($expr, $evaluate=false)
{
   
echo_('==========================================');
    try {
       
// uses defaultConfiguration by default
       
$xpr = Xpresion::_($expr);
    } catch (\
Exception $err ) {
       
$xpr = null;
       
echo_($err->getMessage());
    }
    if (
$xpr )
    {
       
$debug = $evaluate ? $xpr->debug(is_array($evaluate) ? $evaluate : array()) : $xpr->debug();
       
echo_($debug);
    }
}

echo_( 'Xpresion.VERSION ' . Xpresion::VERSION . "\n" );

test_expr('(123+456)', true);
test_expr('("123"+456)', true);
test_expr('("123"+"456")', true);
test_expr('len("123"+456)', true);
test_expr('len("123"+"456")', true);
test_expr('sum(1,2+3,4)', true);
test_expr('sum(1,(2+3),4)', true);
test_expr('array("string")',true);
test_expr('array(["ar","ra","y"])',true);
test_expr('str(2)',true);
test_expr('str("2")',true);
test_expr('int(2)',true);
test_expr('int("2")',true);
test_expr('hex2dec($this)');
test_expr('13');
test_expr('1.32');
test_expr('-0.12');
test_expr('-3');
test_expr('("1,2,3")+3',true);
test_expr('"1,2,3"+3',true);
test_expr('"1,2,3"+3+4',true);
test_expr('"1,2,3"+3+len("123")',true);
test_expr('"1,2,3"+3+sum(1,2,3)',true);
test_expr('sum(1,len("123"+456),2)',true);
test_expr('[1,2,3]+3',true);
test_expr('-3+2',true);
test_expr('1-3+2',true);
test_expr('1+-3',true);
test_expr('+1+3',true);
test_expr('2*-1');
test_expr('2*(-1)');
test_expr('2^-1');
test_expr('2^(-1)');
test_expr('2^-1^3');
test_expr('-2^-1^3');
test_expr('2^(-1)^3');
test_expr('sqrt(2)', true);
test_expr('$v');
test_expr('$v.key.0.key', array('v'=>(object)array('key'=>array((object)array('key'=>'correct'),'foo'))));
test_expr('True');
test_expr('"string"');
test_expr('["a","rra","y"]');
test_expr('`^regex?`i');
test_expr('0 == 1');
test_expr('TRUE == False');
test_expr('TRUE is False');
test_expr('1+2');
test_expr('1+2+3');
test_expr('1+2*3');
test_expr('1*2+3');
test_expr('1*2*3');
test_expr('1+2/3');
test_expr('1*2/3');
test_expr('1^2');
test_expr('1^2^3');
test_expr('1^(2^3)');
test_expr('(1^2)^3');
test_expr('((1^2))^3');
test_expr('`^regex?`i matches "string"');
test_expr('`^regex?`i matches "string" and `^regex?`i matches "string2"');
test_expr('$v in ["a","b","c"]');
test_expr('1 ? : (1+2) (3+4)');
test_expr('1 ? sum(1,2) : (3+4)');
test_expr('1 ? 1+2 : (3+4)');
test_expr('1 ? (2+3) : 2 ? (3+4) : (4+5)');
test_expr('date("Y-m-d H:i:s")', true);
test_expr('time()', true);
test_expr('date("Y-m-d H:i:s", time())');
test_expr('pow(1,pow(2,3))');
test_expr('pow(pow(2,3),4)');
test_expr('pow(pow(1,2),pow(2,3))');


Details

Xpr3s10n

A simple, fast and flexible eXpression Parser Engine (with custom functions and variables support) for PHP, Python, Node.js and Browser

Xpresion

light-weight (~29kB minified, ~10kB zipped)

version 1.0.1 Xpresion.js, Xpresion.min.js

see also:

  • ModelView a simple, fast, powerful and flexible MVVM framework for JavaScript
  • tico a tiny, super-simple MVC framework for PHP
  • LoginManager a simple, barebones agnostic login manager for PHP, JavaScript, Python
  • SimpleCaptcha a simple, image-based, mathematical captcha with increasing levels of difficulty for PHP, JavaScript, Python
  • Dromeo a flexible, and powerful agnostic router for PHP, JavaScript, Python
  • PublishSubscribe a simple and flexible publish-subscribe pattern implementation for PHP, JavaScript, Python
  • Importer simple class &amp; dependency manager and loader for PHP, JavaScript, Python
  • Contemplate a fast and versatile isomorphic template engine for PHP, JavaScript, Python
  • HtmlWidget html widgets, made as simple as possible, both client and server, both desktop and mobile, can be used as (template) plugins and/or standalone for PHP, JavaScript, Python (can be used as plugins for Contemplate)
  • Paginator simple and flexible pagination controls generator for PHP, JavaScript, Python
  • Formal a simple and versatile (Form) Data validation framework based on Rules for PHP, JavaScript, Python
  • Dialect a cross-vendor &amp; cross-platform SQL Query Builder, based on GrammarTemplate, for PHP, JavaScript, Python
  • DialectORM an Object-Relational-Mapper (ORM) and Object-Document-Mapper (ODM), based on Dialect, for PHP, JavaScript, Python
  • Unicache a simple and flexible agnostic caching framework, supporting various platforms, for PHP, JavaScript, Python
  • Xpresion a simple and flexible eXpression parser engine (with custom functions and variables support), based on GrammarTemplate, for PHP, JavaScript, Python
  • Regex Analyzer/Composer Regular Expression Analyzer and Composer for PHP, JavaScript, Python

Contents

Xpresion Live

Features

(v. 1.0.0+)

Xpresion is not an expression parser, it does not parse expressions. It is a parser engine. It builds parsers, which parse (custom) expressions. Effectively it is a small rewrite system

This is accomplished by configuring the Xpresion engine to fit the needed expression syntax. Configuration is intutive, easy and flexible (see examples).

However, since Xpresion is a parser engine, adding a default configuration, it can itself be a parser as well (out-of-the-box).

NOTE: Xpresion (v.1.0.0+) uses Grammar Templates for operator output. This is more flexible than simply using String Templates as previously, since it supports blocks of optional code, default values, optional placeholders and many other things. Only make sure to escape (with \ character) all "<",">","[","]" literal characters inside the output you define for operators and functions (see default configuration example below)

Features:

  1. simple, intuitive, flexible, fast
  2. support an adequate expression syntax out of the box (i.e numbers, strings, regexes, reserved tokens, arithmetical/logical operators, ternary operators, some advanced functions, variables)
  3. highly flexible and configurable (regexes, outputs, functions, operators, aliases, runtime functions, etc..)
  4. support (user-defined) variables, (user-defined) functions
  5. define custom operators (prefix, infix, postfix, left/right association, etc..) seamlessly
  6. define polymorphic operators / operator overloading seamlessly (e.g subtraction and unary negation)
  7. define n-ary operators seamlessly (e.g ternary if-then-else, etc..)
  8. define aliases of operators/functions seamlessly
  9. define new custom expression syntaxes easily (e.g via another `configuration` or by extending `defaultConfiguration` )
  10. implementations for `Node.js` and `Browser`, `PHP`, `Python` (only one class per implementation)

If you use Xpresion in your application and you want to share it, feel free to submit an example link

Live Examples

Examples

(see test/test.js)

javascript

var echo = console.log, 
    Xpresion = require(__dirname+'/../src/js/Xpresion.js')
;

function test_expr(expr, evaluate)
{
    evaluate = evaluate || false;
    echo('==========================================');
    var xpr = null;
    try {
        // uses defaultConfiguration by default
        xpr = Xpresion(expr);
    } catch (err ) {
        xpr = null;
        echo(err.toString());
    }
    if ( xpr )
    {
        var debug = evaluate ? xpr.debug(evaluate instanceof Object ? evaluate : {}) : xpr.debug();
        echo(debug);
    }
}

echo( 'Xpresion.VERSION ' + Xpresion.VERSION + "\n" );

test_expr('13');
test_expr('1.32');
test_expr('-0.12');
test_expr('-3');
test_expr('("1,2,3")+3',true);
test_expr('"1,2,3"+3',true);
test_expr('"1,2,3"+3+4',true);
test_expr('[1,2,3]+3',true);
test_expr('-3+2',true);
test_expr('1-3+2',true);
test_expr('1+-3',true);
test_expr('+1+3',true);
test_expr('2*-1');
test_expr('2*(-1)');
test_expr('2^-1');
test_expr('2^(-1)');
test_expr('2^-1^3');
test_expr('-2^-1^3');
test_expr('2^(-1)^3');
test_expr('sqrt(2)', true);
test_expr('$v');
test_expr('$v.key.0.key', {'v':{'key':[{'key':'correct'},'foo']}});
test_expr('True');
test_expr('"string"');
test_expr('["a","rra","y"]');
test_expr('`^regex?`i');
test_expr('0 == 1');
test_expr('TRUE == False');
test_expr('TRUE is False');
test_expr('1+2');
test_expr('1+2+3');
test_expr('1+2*3');
test_expr('1*2+3');
test_expr('123');
test_expr('1+2/3');
test_expr('1*2/3');
test_expr('1^2');
test_expr('1^2^3');
test_expr('1^(2^3)');
test_expr('(1^2)^3');
test_expr('((1^2))^3');
test_expr('`^regex?`i matches "string"');
test_expr('`^regex?`i matches "string" and `^regex?`i matches "string2"');
test_expr('$v in ["a","b","c"]');
test_expr('1 ? : (1+2) (3+4)');
test_expr('1 ? sum(1,2) : (3+4)');
test_expr('1 ? 1+2 : (3+4)');
test_expr('1 ? (2+3) : 2 ? (3+4) : (4+5)');
test_expr('date("Y-m-d H:i:s")', true);
test_expr('time()', true);
test_expr('date("Y-m-d H:i:s", time())');
test_expr('pow(1,pow(2,3))');
test_expr('pow(pow(2,3),4)');
test_expr('pow(pow(1,2),pow(2,3))');

output

Xpresion.VERSION 1.0.0

==========================================
Expression: 13
Variables : []
Evaluator : 13
==========================================
Expression: 1.32
Variables : []
Evaluator : 1.32
==========================================
Expression: -0.12
Variables : []
Evaluator : (-0.12)
==========================================
Expression: -3
Variables : []
Evaluator : (-3)
==========================================
Expression: ("1,2,3")+3
Variables : []
Evaluator : ("1,2,3"+String(3))
Data      : {}
Result    : "1,2,33"
==========================================
Expression: "1,2,3"+3
Variables : []
Evaluator : ("1,2,3"+String(3))
Data      : {}
Result    : "1,2,33"
==========================================
Expression: "1,2,3"+3+4
Variables : []
Evaluator : (("1,2,3"+String(3))+String(4))
Data      : {}
Result    : "1,2,334"
==========================================
Expression: [1,2,3]+3
Variables : []
Evaluator : Fn.ary_merge([1,2,3],3)
Data      : {}
Result    : [1,2,3,3]
==========================================
Expression: -3+2
Variables : []
Evaluator : ((-3)+2)
Data      : {}
Result    : -1
==========================================
Expression: 1-3+2
Variables : []
Evaluator : ((1-3)+2)
Data      : {}
Result    : 0
==========================================
Expression: 1+-3
Variables : []
Evaluator : (1+(-3))
Data      : {}
Result    : -2
==========================================
Expression: +1+3
Variables : []
Evaluator : (1+3)
Data      : {}
Result    : 4
==========================================
Expression: 2*-1
Variables : []
Evaluator : (2*(-1))
==========================================
Expression: 2*(-1)
Variables : []
Evaluator : (2*(-1))
==========================================
Expression: 2^-1
Variables : []
Evaluator : Math.pow(2,(-1))
==========================================
Expression: 2^(-1)
Variables : []
Evaluator : Math.pow(2,(-1))
==========================================
Expression: 2^-1^3
Variables : []
Evaluator : Math.pow(2,Math.pow((-1),3))
==========================================
Expression: -2^-1^3
Variables : []
Evaluator : Math.pow((-2),Math.pow((-1),3))
==========================================
Expression: 2^(-1)^3
Variables : []
Evaluator : Math.pow(2,Math.pow((-1),3))
==========================================
Expression: sqrt(2)
Variables : []
Evaluator : Math.sqrt(2)
Data      : {}
Result    : 1.4142135623730951
==========================================
Expression: $v
Variables : [v]
Evaluator : Var["v"]
==========================================
Expression: $v.key.0.key
Variables : [v]
Evaluator : Xpresion.GET(Var["v"],["key","0","key"])
Data      : {
    "v": {
        "key": [
            {
                "key": "correct"
            },
            "foo"
        ]
    }
}
Result    : "correct"
==========================================
Expression: True
Variables : []
Evaluator : true
==========================================
Expression: "string"
Variables : []
Evaluator : "string"
==========================================
Expression: ["a","rra","y"]
Variables : []
Evaluator : ["a","rra","y"]
==========================================
Expression: `^regex?`i
Variables : []
Evaluator : Cache.re_1
==========================================
Expression: 0 == 1
Variables : []
Evaluator : (0==1)
==========================================
Expression: TRUE == False
Variables : []
Evaluator : (true==false)
==========================================
Expression: TRUE is False
Variables : []
Evaluator : (true===false)
==========================================
Expression: 1+2
Variables : []
Evaluator : (1+2)
==========================================
Expression: 1+2+3
Variables : []
Evaluator : ((1+2)+3)
==========================================
Expression: 1+2*3
Variables : []
Evaluator : (1+(2*3))
==========================================
Expression: 1*2+3
Variables : []
Evaluator : ((1*2)+3)
==========================================
Expression: 123
Variables : []
Evaluator : ((12)3)
==========================================
Expression: 1+2/3
Variables : []
Evaluator : (1+(2/3))
==========================================
Expression: 1*2/3
Variables : []
Evaluator : ((1*2)/3)
==========================================
Expression: 1^2
Variables : []
Evaluator : Math.pow(1,2)
==========================================
Expression: 1^2^3
Variables : []
Evaluator : Math.pow(1,Math.pow(2,3))
==========================================
Expression: 1^(2^3)
Variables : []
Evaluator : Math.pow(1,Math.pow(2,3))
==========================================
Expression: (1^2)^3
Variables : []
Evaluator : Math.pow(Math.pow(1,2),3)
==========================================
Expression: ((1^2))^3
Variables : []
Evaluator : Math.pow(Math.pow(1,2),3)
==========================================
Expression: `^regex?`i matches "string"
Variables : []
Evaluator : Fn.match("string",Cache.re_1)
==========================================
Expression: `^regex?`i matches "string" and `^regex?`i matches "string2"
Variables : []
Evaluator : (Fn.match("string",Cache.re_1)&&Fn.match("string2",Cache.re_1))
==========================================
Expression: $v in ["a","b","c"]
Variables : [v]
Evaluator : Fn.contains(["a","b","c"],Var["v"])
==========================================
Error: Xpresion Error: Operator ":" expecting 1 prior argument(s) at "1 ? : (1+2) (3+4)"
==========================================
Expression: 1 ? sum(1,2) : (3+4)
Variables : []
Evaluator : (1?Fn.sum(1,2):(3+4))
==========================================
Expression: 1 ? 1+2 : (3+4)
Variables : []
Evaluator : (1?(1+2):(3+4))
==========================================
Expression: 1 ? (2+3) : 2 ? (3+4) : (4+5)
Variables : []
Evaluator : (1?(2+3):(2?(3+4):(4+5)))
==========================================
Expression: date("Y-m-d H:i:s")
Variables : []
Evaluator : Fn.date("Y-m-d H:i:s")
Data      : {}
Result    : "2019-03-19 13:00:31"
==========================================
Expression: time()
Variables : []
Evaluator : Fn.time()
Data      : {}
Result    : 1552993231
==========================================
Expression: date("Y-m-d H:i:s", time())
Variables : []
Evaluator : Fn.date("Y-m-d H:i:s",Fn.time())
==========================================
Expression: pow(1,pow(2,3))
Variables : []
Evaluator : Math.pow(1,Math.pow(2,3))
==========================================
Expression: pow(pow(2,3),4)
Variables : []
Evaluator : Math.pow(Math.pow(2,3),4)
==========================================
Expression: pow(pow(1,2),pow(2,3))
Variables : []
Evaluator : Math.pow(Math.pow(1,2),Math.pow(2,3))

API


// define a new configuration
var conf = Xpresion.Configuration();
// or extend default configuration
var conf = Xpresion.defaultConfiguration();

// define an operator
conf.defOp({
    '===' :{
             'input'        : [1,'===',1]           // input
            ,'output'       : '(<$.0>===<$.1>)'     // output (GrammarTemplate)
            ,'otype'        : Xpresion.T_BOL        // output type BOOLEAN, default = Xpresion.T_MIX = mixed
            ,'fixity'       : Xpresion.INFIX        // operator fixity`
            ,'associativity': Xpresion.LEFT         // operator associativity
            ,'priority'     : 40                    // operator priority
        }
});

// define an alias
conf.defOp({
    'is': Xpresion.Alias('===')
});

// define a function (functional operator)
conf.defFunc({
    'len': {
             'input'    : 'len',
            ,'output'   : 'Fn.len(<$.0>)' // <$.0> matches all arguments passed not only one, separated by comma
            ,'otype'    : Xpresion.T_NUM
        }
});

// define a runtime function (called during evaluation) which implements the 'len' function
conf.defRuntimeFunc({
    'len'    :   function( v ) { 
                    if ( null==v ) return 0;
                    if ( v instanceof String || v instanceof Array ) return v.length;
                    if ( v instanceof Object ) return Object.keys(v).length;
                    return 1;
                }
});

// new Xpresion(..) will also work
var expr = Xpresion( "1+2+$v" [, conf=Xpresion.defaultConfiguration()] );

// !note! will trow an error if parsing the expression fails, so better to catch it and handle it

try {
    expr = Xpresion( "1+2+$v", conf );
} catch(e) {
    console.log(e.toString());
}


console.log(expr.debug({'v': 3}));

// class methods==============================

// get or set the Xpresion.defaultConfiguration()
var defaultConf = Xpresion.defaultConfiguration();
Xpresion.defaultConfiguration(newConf);

// methods====================================

// returns debug information about the expresion, plus the evaluated result if data given
expr.debug([Object data]);

// evaluates the expresion based on given data (as variables) and returns the result
expr.evaluate(Object data );

// properties=================================

expr.variables; // array of variable names referenced in the expression
expr.evaluatorString; // evaluator function as string
expr.evaluator; // actual function that evaluates the expression

default Xpresion Configuration

// default out-of-the-box Xpresion parser configuration
// e.g https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence

Xpresion.defaultConfiguration(Configuration({
// regular expressions for tokens
// ===============================
're' : {
 't_spc'        :  /^(\s+)/
,'t_nonspc'     :  /^(\S+)/
,'t_special'    :  /^([*.\\\-+\/\^\$\(\)\[\]|?<:>&~%!#@=_,;{}]+)/
,'t_num'        :  /^(\d+(\.\d+)?)/
,'t_ident'      :  /^([a-zA-Z_][a-zA-Z0-9_]*)\b/
,'t_var'        :  /^\$([a-zA-Z0-9_][a-zA-Z0-9_.]*)\b/
}

// block-type tokens (eg strings and regexes)
// ==========================================
,'blocks' : {
 '\'': {
    'type': T_STR,
    'parse': Xpresion.parse_delimited_block
 }
,'"': Alias('\'')
,'`': {
    'type': T_REX,
    'parse': Xpresion.parse_delimited_block,
    'rest': parse_re_flags
}
}

// reserved keywords and literals
// ===============================
,'reserved' : {
 'null'     : Tok(T_IDE, 'null', 'null')
,'false'    : Tok(T_BOL, 'false', 'false')
,'true'     : Tok(T_BOL, 'true', 'true')
,'infinity' : Tok(T_NUM, 'Infinity', 'Infinity')
,'nan'      : Tok(T_NUM, 'NaN', 'NaN')
// aliases
,'none'     : Alias('null')
,'inf'      : Alias('infinity')
}

// operators
// ==========
,'operators' : {
// bra-kets as n-ary operators
// negative number of arguments, indicate optional arguments (experimental)
 '('    :   {
                'input'         : ['(',-1,')']
                ,'output'       : '<$.0>'
                ,'otype'        : T_DUM
                ,'fixity'       : POSTFIX
                ,'associativity': RIGHT
                ,'priority'     : 0
            }
,')'    :   {'input':[-1,')']}
,'['    :   {
                'input'         : ['[',-1,']']
                ,'output'       : '\\[<$.0>\\]'
                ,'otype'        : T_ARY
                ,'fixity'       : POSTFIX
                ,'associativity': RIGHT
                ,'priority'     : 2
            }
,']'    :   {'input':[-1,']']}
,','    :   {
                'input'         : [1,',',1]
                ,'output'       : '<$.0>,<$.1>'
                ,'otype'        : T_DFT
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 103
            }
// n-ary (ternary) if-then-else operator
,'?'    :   {
                'input'         : [1,'?',1,':',1]
                ,'output'       : '(<$.0>?<$.1>:<$.2>)'
                ,'otype'        : T_BOL
                ,'fixity'       : INFIX
                ,'associativity': RIGHT
                ,'priority'     : 100
            }
,':'    :   {'input':[1,':',1]}

,'!'    :   {
                'input'         : ['!',1]
                ,'output'       : '!<$.0>'
                ,'otype'        : T_BOL
                ,'fixity'       : PREFIX
                ,'associativity': RIGHT
                ,'priority'     : 10
            }
,'~'    :   {
                'input'         : ['~',1]
                ,'output'       : '~<$.0>'
                ,'otype'        : T_NUM
                ,'fixity'       : PREFIX
                ,'associativity': RIGHT
                ,'priority'     : 10
            }
,'^'    :   {
                'input'         : [1,'^',1]
                ,'output'       : 'Math.pow(<$.0>,<$.1>)'
                ,'otype'        : T_NUM
                ,'fixity'       : INFIX
                ,'associativity': RIGHT
                ,'priority'     : 11
            }
,'*'    :   {
                'input'         : [1,'*',1]
                ,'output'       : '(<$.0>*<$.1>)'
                ,'otype'        : T_NUM
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 20
            }
,'/'    :   {
                'input'         : [1,'/',1]
                ,'output'       : '(<$.0>/<$.1>)'
                ,'otype'        : T_NUM
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 20
            }
,'%'    :   {
                'input'         : [1,'%',1]
                ,'output'       : '(<$.0>%<$.1>)'
                ,'otype'        : T_NUM
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 20
            }
// addition/concatenation/unary plus as polymorphic operators
,'+'    :   {'polymorphic':[
            // array concatenation
            [
            function(curr,Xpresion){return curr.TOK && (!curr.PREV_IS_OP) && (curr.DEDUCED_TYPE===Xpresion.T_ARY);},
            {
                'input'         : [1,'+',1]
                ,'output'       : 'Fn.ary_merge(<$.0>,<$.1>)'
                ,'otype'        : T_ARY
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 25
            }
            ]
            // string concatenation
            ,[
            function(curr,Xpresion){return curr.TOK && (!curr.PREV_IS_OP) && (curr.DEDUCED_TYPE===Xpresion.T_STR);},
            {
                'input'         : [1,'+',1]
                ,'output'       : '(<$.0>+String(<$.1>))'
                ,'otype'        : T_STR
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 25
            }
            ]
            // numeric addition
            ,[
            function(curr,Xpresion){return curr.TOK && !curr.PREV_IS_OP;},
            {
                'input'         : [1,'+',1]
                ,'output'       : '(<$.0>+<$.1>)'
                ,'otype'        : T_NUM
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 25
            }
            ]
            // unary plus
            ,[
            function(curr,Xpresion){return (!curr.TOK) || curr.PREV_IS_OP;},
            {
                'input'         : ['+',1]
                ,'output'       : '<$.0>'
                ,'otype'        : T_NUM
                ,'fixity'       : PREFIX
                ,'associativity': RIGHT
                ,'priority'     : 4
            }
            ]
            ]}
,'-'    :   {'polymorphic':[
            // numeric subtraction
            [
            function(curr,Xpresion){return curr.TOK && !curr.PREV_IS_OP;},
            {
                'input'         : [1,'-',1]
                ,'output'       : '(<$.0>-<$.1>)'
                ,'otype'        : T_NUM
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 25
            }
            ]
            // unary negation
            ,[
            function(curr,Xpresion){return (!curr.TOK) || curr.PREV_IS_OP;},
            {
                'input'         : ['-',1]
                ,'output'       : '(-<$.0>)'
                ,'otype'        : T_NUM
                ,'fixity'       : PREFIX
                ,'associativity': RIGHT
                ,'priority'     : 4
            }
            ]
            ]}
,'>>'   :   {
                'input'         : [1,'>>',1]
                ,'output'       : '(<$.0>\\>\\><$.1>)'
                ,'otype'        : T_NUM
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 30
            }
,'<<'   :   {
                'input'         : [1,'<<',1]
                ,'output'       : '(<$.0>\\<\\<<$.1>)'
                ,'otype'        : T_NUM
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 30
            }
,'>'    :   {
                'input'         : [1,'>',1]
                ,'output'       : '(<$.0>\\><$.1>)'
                ,'otype'        : T_BOL
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 35
            }
,'<'    :   {
                'input'         : [1,'<',1]
                ,'output'       : '(<$.0>\\<<$.1>)'
                ,'otype'        : T_BOL
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 35
            }
,'>='   :   {
                'input'         : [1,'>=',1]
                ,'output'       : '(<$.0>\\>=<$.1>)'
                ,'otype'        : T_BOL
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 35
            }
,'<='   :   {
                'input'         : [1,'<=',1]
                ,'output'       : '(<$.0>\\<=<$.1>)'
                ,'otype'        : T_BOL
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 35
            }
,'=='   :   {'polymorphic':[
            // array equivalence
            [
            function(curr,Xpresion){return curr.DEDUCED_TYPE===Xpresion.T_ARY;},
            {
                'input'         : [1,'==',1]
                ,'output'       : 'Fn.ary_eq(<$.0>,<$.1>)'
                ,'otype'        : T_BOL
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 40
            }
            ]
            // default equivalence
            ,[
            function(curr,Xpresion){return true;},
            {
                'input'         : [1,'==',1]
                ,'output'       : '(<$.0>==<$.1>)'
                ,'otype'        : T_BOL
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 40
            }
            ]
            ]}
,'!='   :   {
                'input'         : [1,'!=',1]
                ,'output'       : '(<$.0>!=<$.1>)'
                ,'otype'        : T_BOL
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 40
            }
,'is'   :   {
                'input'         : [1,'is',1]
                ,'output'       : '(<$.0>===<$.1>)'
                ,'otype'        : T_BOL
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 40
            }
,'matches': {
                'input'         : [1,'matches',1]
                ,'output'       : 'Fn.match(<$.1>,<$.0>)'
                ,'otype'        : T_BOL
                ,'fixity'       : INFIX
                ,'associativity': NONE
                ,'priority'     : 40
            }
,'in'   :   {
                'input'         : [1,'in',1]
                ,'output'       : 'Fn.contains(<$.1>,<$.0>)'
                ,'otype'        : T_BOL
                ,'fixity'       : INFIX
                ,'associativity': NONE
                ,'priority'     : 40
            }
,'&'    :   {
                'input'         : [1,'&',1]
                ,'output'       : '(<$.0>&<$.1>)'
                ,'otype'        : T_NUM
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 45
            }
,'|'    :   {
                'input'         : [1,'|',1]
                ,'output'       : '(<$.0>|<$.1>)'
                ,'otype'        : T_NUM
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 46
            }
,'&&'   :   {
                'input'         : [1,'&&',1]
                ,'output'       : '(<$.0>&&<$.1>)'
                ,'otype'        : T_BOL
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 47
            }
,'||'   :   {
                'input'         : [1,'||',1]
                ,'output'       : '(<$.0>||<$.1>)'
                ,'otype'        : T_BOL
                ,'fixity'       : INFIX
                ,'associativity': LEFT
                ,'priority'     : 48
            }
//------------------------------------------
//                aliases
//-------------------------------------------
,'or'    :  Alias( '||' )
,'and'   :  Alias( '&&' )
,'not'   :  Alias( '!' )
}

// functional operators
// ====================
,'functions' : {
 'min'      : {
                'input'     : 'min'
                ,'output'   : 'Math.min(<$.0>)'
                ,'otype'    : T_NUM
            }
,'max'      : {
                'input'     : 'max'
                ,'output'   : 'Math.max(<$.0>)'
                ,'otype'    : T_NUM
            }
,'pow'      : {
                'input'     : 'pow'
                ,'output'   : 'Math.pow(<$.0>)'
                ,'otype'    : T_NUM
            }
,'sqrt'     : {
                'input'     : 'sqrt'
                ,'output'   : 'Math.sqrt(<$.0>)'
                ,'otype'    : T_NUM
            }
,'len'      : {
                'input'     : 'len'
                ,'output'   : 'Fn.len(<$.0>)'
                ,'otype'    : T_NUM
            }
,'int'      : {
                'input'     : 'int'
                ,'output'   : 'parseInt(<$.0>)'
                ,'otype'    : T_NUM
            }
,'float'    : {
                'input'     : 'float'
                ,'output'   : 'parseFloat(<$.0>)'
                ,'otype'    : T_NUM
            }
,'str'      : {
                'input'     : 'str'
                ,'output'   : 'String(<$.0>)'
                ,'otype'    : T_STR
            }
,'array'    : {
                'input'     : 'array'
                ,'output'   : 'Fn.ary(<$.0>)'
                ,'otype'    : T_ARY
            }
,'clamp'    : {
                'input'     : 'clamp'
                ,'output'   : 'Fn.clamp(<$.0>)'
                ,'otype'    : T_NUM
            }
,'sum'      : {
                'input'     : 'sum'
                ,'output'   : 'Fn.sum(<$.0>)'
                ,'otype'    : T_NUM
            }
,'avg'      : {
                'input'     : 'avg'
                ,'output'   : 'Fn.avg(<$.0>)'
                ,'otype'    : T_NUM
            }
,'time'     : {
                'input'     : 'avg'
                ,'output'   : 'Fn.time()'
                ,'otype'    : T_NUM
                ,'arity'    : 0
            }
,'date'     : {
                'input'     : 'date'
                ,'output'   : 'Fn.date(<$.0>)'
                ,'otype'    : T_STR
            }
//---------------------------------------
//                aliases
//----------------------------------------
 // ...
}

// runtime (implementation) functions
// ==================================
,'runtime' : {
'clamp'        : function( v, m, M ) {
                    if ( m > M ) return v > m ? m : (v < M ? M : v);
                    else return v > M ? M : (v < m ? m : v);
                }
,'len'          : function( v ) {
                    if ( v )
                    {
                        if ( is_array(v) || is_string(v) ) return v.length;
                        if ( is_object(v) ) return Keys(v).length;
                        return 1;
                    }
                    return 0;
                }
,'sum'          : function( ) {
                    var args = arguments, i, l, s = 0;
                    if (args[0] && is_array(args[0]) ) args = args[0];
                    l = args.length;
                    if ( l > 0 ) { for(i=0; i<l; i++) s += args[i]; }
                    return s;
                }
,'avg'          : function( ) {
                    var args = arguments, i, l, s = 0;
                    if (args[0] && is_array(args[0]) ) args = args[0];
                    l = args.length;
                    if ( l > 0 ) { for(i=0; i<l; i++) s += args[i]; s = s/l;}
                    return s;
                }
,'ary'          : function( x ) {
                    return is_array(x) ? x : [x];
                }
,'ary_eq'       : function( a1, a2 ) {
                    var l = a1.length, i;
                    if ( l===a2.length )
                    {
                        for (i=0; i<l; i++)
                            if ( a1[i]!=a2[i] ) return false;
                    }
                    else return false;
                    return true;
                }
,'ary_merge'    : function(a1, a2) {
                    return [ ].concat( a1 ).concat( a2 );
                }
,'match'        : function( str, regex ) {
                    return regex.test( str );
                }
,'contains'     : function( o, i ) {
                    return is_array(o) || is_string(o) ? -1 < o.indexOf( i ) : hasOwnProperty.call(o, i);
                }
,'time'         : time
,'date'         : date
}
}));

Platform Support

  • `PHP` 5.3.0+
  • `Python` 2.x or 3.x
  • `Node` 0.8+
  • `Browser` all

Performance

expression parsing algorithm can be seen as a variation and generalisation of Shunting-Yard algorithm, running in linear-time ( O(n) ) in the input sequence

TODO

  • use Grammar Template for more powerful and flexible rewrite output [DONE]
  • add full support for optional arguments in `operators`/`functions` [ALMOST DONE]
  • add support for (`xml`-like) `tags` in `expressions`
  • performance/unit tests [DONE PARTIALLY]

  Live DemoExternal page  

Open in a separate window

Screenshots  
  • xpresion.png
  Files folder image Files  
File Role Description
Files folder imagesrc (5 directories)
Files folder imagetest (6 files)
Accessible without login Plain text file README.md Doc. Documentation
Accessible without login Image file xpresion.jpg Icon Icon image

  Files folder image Files  /  src  
File Role Description
Files folder imageactionscript (1 file)
Files folder imagec (1 file)
Files folder imagejs (2 files)
Files folder imagephp (1 file)
Files folder imagepython (2 files)

  Files folder image Files  /  src  /  actionscript  
File Role Description
  Accessible without login Plain text file todo.txt Doc. Documentation

  Files folder image Files  /  src  /  c  
File Role Description
  Accessible without login Plain text file todo.txt Doc. Documentation

  Files folder image Files  /  src  /  js  
File Role Description
  Accessible without login Plain text file Xpresion.js Data Auxiliary data
  Accessible without login Plain text file Xpresion.min.js Data Auxiliary data

  Files folder image Files  /  src  /  php  
File Role Description
  Plain text file Xpresion.php Class Class source

  Files folder image Files  /  src  /  python  
File Role Description
  Accessible without login Plain text file Xpresion.py Data Auxiliary data
  Accessible without login Plain text file __init__.py Data Auxiliary data

  Files folder image Files  /  test  
File Role Description
  Accessible without login Plain text file out-js Data Auxiliary data
  Accessible without login Plain text file out-php Data Auxiliary data
  Accessible without login Plain text file out-py Data Auxiliary data
  Accessible without login Plain text file test.js Data Auxiliary data
  Accessible without login Plain text file test.php Example Example script
  Accessible without login Plain text file test.py Data Auxiliary data

 Version Control Unique User Downloads Download Rankings  
 100%
Total:65
This week:1
All time:10,347
This week:571Up