openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit c3abad3747ecad38601e9f7c8e452dd7cdfe7a69
parent c4489b3949b269256ac878722d15747171e11529
Author: Jan Dankert <develop@jandankert.de>
Date:   Fri,  1 Jul 2022 18:09:05 +0200

New: Bugfixes and much more string and array functions for the DSL.

Diffstat:
Mmodules/dsl/DslLexer.class.php | 11+++++++----
Mmodules/dsl/DslToken.class.php | 3+++
Mmodules/dsl/ast/DslAssignment.class.php | 3+--
Mmodules/dsl/ast/DslExpression.class.php | 51++++++++++++++++++++++++++++++++++++++++++---------
Amodules/dsl/ast/DslFalse.class.php | 20++++++++++++++++++++
Mmodules/dsl/ast/DslFor.class.php | 11++++++++---
Mmodules/dsl/ast/DslFunctionCall.class.php | 6+++---
Mmodules/dsl/ast/DslOperation.class.php | 9+++++++++
Mmodules/dsl/ast/DslProperty.class.php | 13+++++--------
Mmodules/dsl/ast/DslStatementList.class.php | 13+++++++++++++
Amodules/dsl/ast/DslTrue.class.php | 20++++++++++++++++++++
Mmodules/dsl/ast/DslVariable.class.php | 29++++++++++++++++++++++++++++-
Mmodules/dsl/context/BaseScriptableObject.class.php | 12++++++++++++
Mmodules/dsl/executor/DslInterpreter.class.php | 18+++++++++++++-----
Amodules/dsl/standard/Boolean.class.php | 38++++++++++++++++++++++++++++++++++++++
Mmodules/dsl/standard/Date.class.php | 2+-
Amodules/dsl/standard/Number.class.php | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmodules/dsl/standard/Script.class.php | 11++++-------
Mmodules/dsl/standard/StandardArray.class.php | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mmodules/dsl/standard/StandardMath.class.php | 17+++++++++++------
Amodules/dsl/standard/StandardString.class.php | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmodules/dsl/standard/System.class.php | 6+++---
Mmodules/dsl/standard/Write.class.php | 6+++++-
23 files changed, 536 insertions(+), 57 deletions(-)

diff --git a/modules/dsl/DslLexer.class.php b/modules/dsl/DslLexer.class.php @@ -17,12 +17,12 @@ class DslLexer 'return' => DslToken::T_RETURN, 'new' => DslToken::T_NEW, 'throw' => DslToken::T_THROW, + 'null' => DslToken::T_NULL, + 'true' => DslToken::T_TRUE, + 'false' => DslToken::T_FALSE, ]; const UNUSED_KEYWORDS = [ - 'null', - 'true', - 'false', 'implements', 'interface', 'package', @@ -180,12 +180,15 @@ class DslLexer } // Numbers + // TODO we have a problem with + // - "-" is an operator, so we cannot parse negative numbers + // - "." is the property char, so we cannot parse decimal values if ( $char >= '0' && $char <= '9' ) { $value = $char; while( true ) { $char = array_shift( $chars ); if ( ( $char >= '0' && $char <= '9') || - $char == '.' || $char == '_' ) { + $char == '_' ) { $value .= $char; } else { $this->addToken( $line,DslToken::T_NUMBER,str_replace('_','',$value )); diff --git a/modules/dsl/DslToken.class.php b/modules/dsl/DslToken.class.php @@ -26,6 +26,9 @@ class DslToken const T_COMMA = 19; const T_NEW = 20; const T_THROW = 21; + const T_TRUE = 22; + const T_FALSE = 23; + const T_NULL = 24; public $lineNumber; public $type; diff --git a/modules/dsl/ast/DslAssignment.class.php b/modules/dsl/ast/DslAssignment.class.php @@ -35,7 +35,7 @@ class DslAssignment implements DslStatement $value = $this->value->execute( $context ); // if the variable is not already bound in this context it will be created. - // there is no need for a "var" or "let". they are completely obsolet. + // there is no need for a "var" or "let". they are completely obsolete. //if ( ! array_key_exists( $this->target->name,$context ) ) // throw new DslRuntimeException('variable \''.$this->target->name.'\' does not exist'); @@ -46,7 +46,6 @@ class DslAssignment implements DslStatement public function parse($tokens) { - //echo "<h2>Assignment-Parser</h2><pre>"; var_dump( $tokens ); echo "</pre>"; $this->target = new DslExpression(); $this->value = new DslExpression(); diff --git a/modules/dsl/ast/DslExpression.class.php b/modules/dsl/ast/DslExpression.class.php @@ -5,8 +5,13 @@ namespace dsl\ast; define('LEFT', 0); define('RIGHT', 1); +use dsl\context\BaseScriptableObject; use dsl\DslParserException; use dsl\DslToken; +use dsl\executor\DslInterpreter; +use dsl\standard\Number; +use dsl\standard\StandardArray; +use dsl\standard\StandardString; class DslExpression extends DslElement implements DslStatement { @@ -32,14 +37,12 @@ class DslExpression extends DslElement implements DslStatement */ public function parse($tokens) { - //echo "<h5>Expression:</h5><pre>"; var_export( $tokens ); echo "</pre>"; if ( ! $tokens ) { $this->value = new DslNull(); return; } $this->parseExpression( $tokens ); - // Groups } @@ -97,7 +100,7 @@ class DslExpression extends DslElement implements DslStatement '^' => RIGHT, '!' => RIGHT, '**' => RIGHT, - '.' => RIGHT, + '.' => LEFT, '$' => LEFT, ]; @@ -166,13 +169,8 @@ class DslExpression extends DslElement implements DslStatement array_pop($operator_stack); } - elseif // if the token is a number, then push it to the output queue. - (true) { + else { $output_queue[] = $this->tokenToStatement( $token ); - - // if the token is an operator, then: - } else { - throw new DslParserException( 'Unexpected token '.$token->value, $token->lineNumber); } } // if there are no more tokens to read: @@ -222,6 +220,12 @@ class DslExpression extends DslElement implements DslStatement switch( $token->type ) { case DslToken::T_NONE: return new DslInteger( 0 ); + case DslToken::T_NULL: + return new DslNull(); + case DslToken::T_TRUE: + return new DslTrue(); + case DslToken::T_FALSE: + return new DslFalse(); case DslToken::T_NUMBER: return new DslInteger( $token->value ); case DslToken::T_TEXT: @@ -234,4 +238,33 @@ class DslExpression extends DslElement implements DslStatement throw new DslParserException('Unknown token '.$token->value,$token->lineNumber); } } + + + public static function convertValueToStandardObject($value ) { + + if ( is_object( $value ) ) { + + if ( $value instanceof BaseScriptableObject ) { + return $value; + } + + if ( DslInterpreter::isSecure() ) + // Secured Sandbox, external objects are not evaluated. + return new StandardString( 'ProtectedObject' ); + else + return $value; // Unsecured, but wanted. + } + elseif ( is_array( $value ) ) { + + return new StandardArray($value); + } + elseif ( is_int( $value ) ) { + + return new Number($value); + } + elseif ( is_string( $value ) ) { + + return new StandardString($value); + } + } } \ No newline at end of file diff --git a/modules/dsl/ast/DslFalse.class.php b/modules/dsl/ast/DslFalse.class.php @@ -0,0 +1,19 @@ +<?php + +namespace dsl\ast; + +use dsl\DslParserException; +use dsl\DslToken; + +class DslFalse implements DslStatement +{ + public function execute( & $context) + { + return false; + } + + + public function parse($tokens) + { + } +} +\ No newline at end of file diff --git a/modules/dsl/ast/DslFor.class.php b/modules/dsl/ast/DslFor.class.php @@ -4,6 +4,7 @@ namespace dsl\ast; use dsl\DslRuntimeException; use dsl\DslToken; +use dsl\standard\StandardArray; class DslFor implements DslStatement { @@ -30,12 +31,16 @@ class DslFor implements DslStatement $list = $this->list->execute( $context ); - if ( !is_array( $list ) ) - throw new DslRuntimeException('for value is not a list'); + if ( ! $list instanceof StandardArray ) + throw new DslRuntimeException('for value is not an array'); $copiedContext = $context; - foreach( $list as $loopVar ) { + foreach( $list->getInternalValue() as $loopVar ) { + + // copy loop var to current loop context $copiedContext[ $this->name ] = $loopVar; + + // Execute "for" block $this->statements->execute( $copiedContext ); } } diff --git a/modules/dsl/ast/DslFunctionCall.class.php b/modules/dsl/ast/DslFunctionCall.class.php @@ -4,6 +4,7 @@ namespace dsl\ast; use dsl\DslRuntimeException; use dsl\DslToken; +use dsl\executor\DslInterpreter; class DslFunctionCall implements DslStatement { @@ -44,7 +45,7 @@ class DslFunctionCall implements DslStatement if ( $function instanceof DslFunction ) { - + // inscript custom function $parameters = $function->parameters; if ( sizeof($parameters) > sizeof($parameterValues) ) @@ -59,8 +60,7 @@ class DslFunctionCall implements DslStatement } elseif ( is_callable($function) ) { - //var_export( call_user_func_array( $function, $parameterValues) ); - return call_user_func_array( $function, $parameterValues); + return DslExpression::convertValueToStandardObject( call_user_func_array( $function, $parameterValues) ); } else throw new DslRuntimeException('function is not callable'.var_export($function)); diff --git a/modules/dsl/ast/DslOperation.class.php b/modules/dsl/ast/DslOperation.class.php @@ -5,6 +5,9 @@ namespace dsl\ast; use dsl\DslParserException; use dsl\DslRuntimeException; use dsl\DslToken; +use dsl\standard\Number; +use dsl\standard\StandardArray; +use dsl\standard\StandardString; class DslOperation implements DslStatement { @@ -38,6 +41,12 @@ class DslOperation implements DslStatement case '+': if ( is_string($left) ) return $left . (string)$right; + if ($left instanceof StandardArray) + return $left->concat( $right ); + if ($left instanceof StandardString) + return $left->__toString() . (string)$right; + if ($left instanceof Number) + return $left->toNumber() + intval($right); else return intval($left) + intval($right); diff --git a/modules/dsl/ast/DslProperty.class.php b/modules/dsl/ast/DslProperty.class.php @@ -6,6 +6,9 @@ use cms\generator\dsl\DslObject; use dsl\context\Scriptable; use dsl\DslRuntimeException; use dsl\executor\DslInterpreter; +use dsl\standard\Number; +use dsl\standard\StandardArray; +use dsl\standard\StandardString; class DslProperty implements DslStatement { @@ -51,18 +54,12 @@ class DslProperty implements DslStatement }; } } - elseif ( is_array( $object ) ) { - - $objectContext = $object; - - } else { - - throw new DslRuntimeException('not an object'); + else { + $objectContext = DslExpression::convertValueToStandardObject($object); } $prop = $this->property->execute( $objectContext ); - return $prop; } diff --git a/modules/dsl/ast/DslStatementList.class.php b/modules/dsl/ast/DslStatementList.class.php @@ -106,6 +106,18 @@ class DslStatementList extends DslElement implements DslStatement case DslToken::T_LET: break; + case DslToken::T_NULL: + $this->statements[] = new DslNull(); + break; + + case DslToken::T_TRUE: + $this->statements[] = new DslTrue(); + break; + + case DslToken::T_FALSE: + $this->statements[] = new DslFalse(); + break; + case DslToken::T_FOR: $forGroup = $this->getGroup( $tokens ); $forBlock = $this->getStatementOrBlock( $tokens ); @@ -133,6 +145,7 @@ class DslStatementList extends DslElement implements DslStatement break; case DslToken::T_TEXT: + case DslToken::T_NUMBER: case DslToken::T_STRING: array_unshift( $tokens, $token ); $statementTokens = $this->getSingleStatement( $tokens ); diff --git a/modules/dsl/ast/DslTrue.class.php b/modules/dsl/ast/DslTrue.class.php @@ -0,0 +1,19 @@ +<?php + +namespace dsl\ast; + +use dsl\DslParserException; +use dsl\DslToken; + +class DslTrue implements DslStatement +{ + public function execute( & $context) + { + return true; + } + + + public function parse($tokens) + { + } +} +\ No newline at end of file diff --git a/modules/dsl/ast/DslVariable.class.php b/modules/dsl/ast/DslVariable.class.php @@ -2,7 +2,9 @@ namespace dsl\ast; +use dsl\context\Scriptable; use dsl\DslRuntimeException; +use dsl\executor\DslInterpreter; class DslVariable implements DslStatement { @@ -19,8 +21,33 @@ class DslVariable implements DslStatement public function execute( & $context ) { + if ( is_object( $context) ) { + + // copy object methods to the object context to make them callable. + $property = $this->name; + + if ( property_exists( $context,$property ) ) { + + return $context->$property; + } + + if ( method_exists( $context,$this->name ) ) { + + return function() use ($property, $context) { + + // For Security: Do not expose all available objects, they must implement a marker interface. + if ( DslInterpreter::isSecure() && ! $context instanceof Scriptable ) + throw new DslRuntimeException('Object '.get_class($context).' is not marked as scriptable and therefore not available in secure mode'); + + return call_user_func_array( array($context,$property),func_get_args() ); + }; + } + + throw new DslRuntimeException('method or property \''.$property.'\' does not exist' ); + } + if ( ! array_key_exists( $this->name, $context ) ) - throw new DslRuntimeException('\''.$this->name.'\' does not exist'); + throw new DslRuntimeException('variable or property \''.$this->name.'\' does not exist'); return $context[ $this->name ]; } diff --git a/modules/dsl/context/BaseScriptableObject.class.php b/modules/dsl/context/BaseScriptableObject.class.php @@ -31,4 +31,16 @@ class BaseScriptableObject implements Scriptable { return Helper::getHelp($this); } + + + public function getClass() { + return get_class($this); + } + + public function values() { + return array_values(get_object_vars($this)); + } + public function keys() { + return array_keys(get_object_vars($this)); + } } \ No newline at end of file diff --git a/modules/dsl/executor/DslInterpreter.class.php b/modules/dsl/executor/DslInterpreter.class.php @@ -5,10 +5,12 @@ namespace dsl\executor; use dsl\DslAstParser; use dsl\DslException; use dsl\DslLexer; +use dsl\standard\Number; use dsl\standard\Script; use dsl\standard\StandardArray; use dsl\standard\StandardDate; use dsl\standard\StandardMath; +use dsl\standard\StandardString; use dsl\standard\System; use dsl\standard\Write; @@ -44,11 +46,17 @@ class DslInterpreter // Standard-Globals $this->addContext( [ - 'System'=> new System(), - 'Math' => new StandardMath(), - 'Array' => new StandardArray(), - 'Date' => new StandardDate(), - 'write' => $this->writer = new Write(), + + // Standard JS objects + 'Math' => new StandardMath(), + 'Array' => new StandardArray(), + 'String' => new StandardString(), + 'Number' => new Number(), + 'Date' => new StandardDate(), + + // Custom Scriptbox objects + 'System' => new System(), + 'write' => $this->writer = new Write(), ] ); } diff --git a/modules/dsl/standard/Boolean.class.php b/modules/dsl/standard/Boolean.class.php @@ -0,0 +1,37 @@ +<?php + + +namespace dsl\standard; + + +use dsl\context\BaseScriptableObject; + +class Boolean extends BaseScriptableObject +{ + private $value; + + /** + * Number constructor. + * @param $value + */ + public function __construct($value) + { + $this->value = boolval($value); + } + + public function __toString() + { + return $this->value?'true':'false'; + } + + + public function __invoke( $value ) + { + return new Boolean( $value ); + } + public function length() + { + return 1; + } + +} +\ No newline at end of file diff --git a/modules/dsl/standard/Date.class.php b/modules/dsl/standard/Date.class.php @@ -53,7 +53,7 @@ class Date extends BaseScriptableObject /** - * @return string + * @return StandardString */ public function help() { diff --git a/modules/dsl/standard/Number.class.php b/modules/dsl/standard/Number.class.php @@ -0,0 +1,56 @@ +<?php + + +namespace dsl\standard; + + +use dsl\context\BaseScriptableObject; + +class Number extends BaseScriptableObject +{ + private $value; + + public $MAX_SAFE_INTEGER = PHP_INT_MAX; + public $MIN_SAFE_INTEGER = PHP_INT_MIN; + + /** + * Number constructor. + * @param $value + */ + public function __construct($value=null) + { + $this->value = $value; + } + + public function __toString() + { + return "" . $this->value; + } + + + public function __invoke( $value ) + { + return new Number( $value ); + } + public function parseFloat() + { + } + public function parseInt() + { + } + + public function toFixed( $digits ) + { + return number_format($this->value,$digits); + } + + public function valueOf( $val ) + { + return new Number( $val ); + } + + public function toNumber() { + return $this->value; + } + +} +\ No newline at end of file diff --git a/modules/dsl/standard/Script.class.php b/modules/dsl/standard/Script.class.php @@ -54,16 +54,13 @@ class Script extends BaseScriptableObject return print_r($this->ast,true); } - public function __toString() + public function dump( $value ) { - return "Script Info, call help() for help."; + return print_r($value,true); } - /** - * @return string - */ - public function help() + public function __toString() { - return Helper::getHelp($this); + return "Script Info, call help() for help."; } } \ No newline at end of file diff --git a/modules/dsl/standard/StandardArray.class.php b/modules/dsl/standard/StandardArray.class.php @@ -5,6 +5,18 @@ use dsl\context\BaseScriptableObject; class StandardArray extends BaseScriptableObject { + private $value; + + /** + * StandardArray constructor. + * @param $value + */ + public function __construct($value=null) + { + $this->value = $value; + } + + public function of() { return func_get_args(); @@ -13,14 +25,92 @@ class StandardArray extends BaseScriptableObject public function __toString() { - return "Arrays:Object"; + return '['.implode(',',$this->value).']'; + } + + /** + * @return mixed + */ + protected function getValue() + { + return $this->value; } /** - * @return string + * @return array|null */ - public function help() + public function getInternalValue() { - return Helper::getHelp($this); + return $this->value; } + + public function concat( $concat ) + { + return new StandardArray( array_merge($this->value,(array)$concat)); + } + public function fill( $value,$start,$count) + { + return array_fill( $value,$start,$count ); + } + public function forEach( $func ) + { + return array_walk( $this->value, $func ); + } + public function includes( $search ) + { + return array_search( $search, $this->value ) !== false; + } + public function indexOf( $search ) + { + return array_search( $search, $this->value ); + } + public function lastIndexOf( $search ) + { + $found = array_keys( $this->value,$search ); + return end( $found ); + } + public function isArray( $val ) + { + return is_array( $val ); + } + public function join( $split = ',' ) + { + return implode( $split,$this->value ); + } + public function keys() + { + return array_keys($this->value); + } + public function values() { + return array_values($this->value); + } + public function pop() + { + return array_pop( $this->value ); + } + public function push( $value ) + { + return array_push( $this->value,$value ); + } + public function reverse() + { + return array_reverse($this->value); + } + public function shift() + { + return array_shift( $this->value ); + } + public function unshift($value) + { + return array_unshift( $this->value,$value ); + } + public function slice($from,$end) + { + return array_slice( $this->value,$from,$end-$from); + } + public function sort() + { + return asort( $this->value ); + } + } \ No newline at end of file diff --git a/modules/dsl/standard/StandardMath.class.php b/modules/dsl/standard/StandardMath.class.php @@ -13,7 +13,10 @@ class StandardMath extends BaseScriptableObject public $SQRT2 = M_SQRT2; public function sqrt($x) { return sqrt( $x ); } + public function abs($x) { return abs($x);} + public function neg($x) { return abs($x) * -1; } + public function acos($x) { return ($x); } public function acosh($x) { return ($x); } public function asin($x) { return ($x); } @@ -22,12 +25,18 @@ class StandardMath extends BaseScriptableObject public function atanh($x) { return atanh($x); } public function atan2($y, $x) { return atan2($y,$x); } public function cbrt($x) { return ($x); } - public function ceil($x) { return ceil($x); } public function cos($x) { return cos($x); } public function cosh($x) { return cosh($x); } + public function sin($x) { return sin($x); } + public function sinh($x) { return sinh($x); } + public function tan($x) { return tan($x); } + public function tanh($x) { return tanh($x); } + + public function ceil($x) { return ceil($x); } + public function floor($x) { return floor($x); } + public function exp($x) { return exp($x); } public function expm1($x) { return expm1($x); } - public function floor($x) { return floor($x); } public function log($x) { return ($x); } public function log1p($x) { return log($x); } public function log10($x) { return log10($x); } @@ -36,8 +45,4 @@ class StandardMath extends BaseScriptableObject public function pow($x,$y) { return pow($x,$y); } public function random() { return rand(); } public function round($x) { return round($x); } - public function sin($x) { return sin($x); } - public function sinh($x) { return sinh($x); } - public function tan($x) { return tan($x); } - public function tanh($x) { return tanh($x); } } \ No newline at end of file diff --git a/modules/dsl/standard/StandardString.class.php b/modules/dsl/standard/StandardString.class.php @@ -0,0 +1,138 @@ +<?php + + +namespace dsl\standard; + + +use dsl\context\BaseScriptableObject; + +class StandardString extends BaseScriptableObject +{ + private $value; + public $length; + public $test = 1; + + /** + * Number constructor. + * @param $value + */ + public function __construct($value=null) + { + if ( $value instanceof BaseScriptableObject ) + $this->value = $value->__toString(); + + elseif ( is_object( $value ) ) + $this->value = get_class($value); + + elseif ( is_callable( $value ) ) + $this->value = 'function'; + + else + $this->value = (string) $value; + + $this->length = strlen( $this->value ); + } + + public function __toString() + { + return (string)$this->value; + } + + + public function __invoke( $value ) + { + return new StandardString( $value ); + } + + + public function split( $splitChar = null ) { + if ( $splitChar == '' ) + return str_split( $this->value ); + if ( $splitChar == null ) + return array( $this->value ); + + return explode( $splitChar, $this->value ); + } + + + public function trim() { + return trim( $this->value); + } + public function trimEnd() { + return rtrim( $this->value); + + } + public function trimStart() { + return ltrim( $this->value); + } + + public function valueOf( $val ) { + return new StandardString( $val ); + } + + public function charAt( $pos ) { + return substr( $this->value,$pos,1 ); + } + + public function concat( $str ) { + return $this->value.$str; + } + + public function startsWith($str) { + return substr( $this->value, 0,strlen($str) ); + } + + public function endsWith($str ) { + return substr( $this->value,strlen($str)*-1,strlen($str)) == $str; + } + + public function indexOf( $search ) { + return strpos( $this->value, $search ); + } + + public function lastIndexOf( $search) { + return strrpos( $this->value, $search ); + } + + public function padStart($length,$pad=null) { + $this->value = str_pad( $this->value,$length, STR_PAD_LEFT); + } + + public function padEnd( $length,$pad=null) { + $this->value = str_pad( $this->value,$length, STR_PAD_RIGHT); + } + + public function repeat( $count) { + return str_repeat( $this->value,$count); + } + + public function replace( $search,$replaceWith ) { + $c = 1; + return str_replace( $search,$replaceWith,$this->value,$c ); + } + + public function replaceAll( $search,$replaceWith ) { + return str_replace( $search,$replaceWith,$this->value ); + } + + public function slice( $begin,$end=null) { + return array_slice( $this->value,$begin,$end-$begin); + } + + public function substring( $start,$end) { + return substr( $this->value, $start,$end-$start+1 ); + } + + public function toLowerCase() { + return strtolower($this->value); + } + + public function toUpperCase() { + return strtoupper($this->value); + } + + public function length() { + return strlen($this->value); + } + +} +\ No newline at end of file diff --git a/modules/dsl/standard/System.class.php b/modules/dsl/standard/System.class.php @@ -8,13 +8,13 @@ class System extends BaseScriptableObject { /** * runtime - * @var string + * @var StandardString */ public $version; /** * Operating system - * @var string + * @var StandardString */ public $os; @@ -26,7 +26,7 @@ class System extends BaseScriptableObject /** * @param $name - * @return array|false|string + * @return array|false|StandardString */ public function env( $name ) { diff --git a/modules/dsl/standard/Write.class.php b/modules/dsl/standard/Write.class.php @@ -8,6 +8,10 @@ class Write public function __invoke( $text ) { - $this->buffer .= $text; + if ( is_object($text ) && !method_exists($text, '__toString') ) + // Workaround for external objects, that do not implement __toString() + $this->buffer .= get_class($text); + else + $this->buffer .= $text; } } \ No newline at end of file