File modules/dsl/ast/DslExpression.class.php

Last commit: Tue Jul 19 00:10:00 2022 +0200	Jan Dankert	New: Scripting language: Ignore Keyword "new"; Support for Calling object constructors; Splitting objects into an instance and a wrapper.
1 <?php 2 3 namespace dsl\ast; 4 5 define('LEFT', 0); 6 define('RIGHT', 1); 7 8 use dsl\context\BaseScriptableObject; 9 use dsl\DslParserException; 10 use dsl\DslToken; 11 use dsl\executor\DslInterpreter; 12 use dsl\standard\NumberInstance; 13 use dsl\standard\ArrayInstance; 14 use dsl\standard\StringInstance; 15 16 class DslExpression extends DslElement implements DslStatement 17 { 18 private $value; 19 20 public function __construct( $valueTokens ) 21 { 22 $this->parse( $valueTokens ); 23 } 24 25 public function execute( & $context ) { 26 27 if ( is_array( $this->value ) ) 28 foreach( $this->value as $v) 29 $v->execute( $context ); 30 else 31 return $this->value->execute( $context ); 32 } 33 34 /** 35 * @param DslToken[] $tokens 36 * @throws DslParserException 37 */ 38 public function parse($tokens) 39 { 40 if ( ! $tokens ) { 41 $this->value = new DslNull(); 42 return; 43 } 44 45 $this->parseExpression( $tokens ); 46 } 47 48 49 /** 50 * Parsing an expression using the shunting yard algorithm. 51 * 52 * Precedences see https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#assoziativit%C3%A4t 53 * @param DslToken[] $tokens 54 * @throws DslParserException 55 */ 56 private function parseExpression( $tokens ) 57 { 58 $precedence = [ 59 ',' => 2, 60 '=' => 3, 61 '+=' => 3, 62 '-=' => 3, 63 '||' => 5, 64 '&&' => 6, 65 '==' => 10, 66 '!=' => 10, 67 '<' => 11, 68 '<=' => 11, 69 '>' => 11, 70 '>=' => 11, 71 '+' => 13, 72 '-' => 13, 73 '/' => 14, 74 '*' => 14, 75 '%' => 14, 76 '**' => 15, 77 '!' => 16, 78 '$' => 19, // function call, provided by the lexer 79 '.' => 19, 80 ]; 81 82 $assoc = [ 83 ',' => LEFT, 84 '=' => RIGHT, 85 '-=' => RIGHT, 86 '+=' => RIGHT, 87 '||' => LEFT, 88 '&&' => LEFT, 89 '==' => LEFT, 90 '!=' => LEFT, 91 '<' => LEFT, 92 '<=' => LEFT, 93 '> ' => LEFT, 94 '>=' => LEFT, 95 '+' => LEFT, 96 '-' => LEFT, 97 '/' => LEFT, 98 '*' => LEFT, 99 '%' => LEFT, 100 '^' => RIGHT, 101 '!' => RIGHT, 102 '**' => RIGHT, 103 '.' => LEFT, 104 '$' => LEFT, 105 ]; 106 107 108 // for the purpose of comparing only; it's forced to top priority explicitly 109 $precedence['('] = 0; 110 $precedence[')'] = 0; 111 112 $output_queue = []; 113 $operator_stack = []; 114 115 if ( $tokens instanceof DslStatement ) { 116 117 $this->value = $tokens; 118 return; 119 } 120 121 if ( $tokens instanceof DslToken ) 122 $tokens = [$tokens]; 123 124 if ( ! is_array($tokens)) 125 throw new DslParserException("tokens must be an array, but it is ".get_class($tokens)); 126 127 // while there are tokens to be read: 128 while ( $tokens ) { 129 // read a token. 130 $token = array_shift($tokens); 131 132 if ($token->isOperator() ) { 133 134 // while there is an operator at the top of the operator stack with 135 // greater than or equal to precedence: 136 while ($operator_stack && 137 $precedence[end($operator_stack)->value] >= $precedence[$token->value] + $assoc[$token->value]) { 138 // pop operators from the operator stack, onto the output queue. 139 140 $left = array_pop( $output_queue ); 141 $right = array_pop( $output_queue ); 142 $output_queue[] = $this->createNode( array_pop($operator_stack),$left,$right ); 143 } 144 // push the read operator onto the operator stack. 145 $operator_stack[] = $token; 146 147 // if the token is a left bracket (i.e. "("), then: 148 } elseif ($token->value === '(') { 149 // push it onto the operator stack. 150 $operator_stack[] = $token; 151 152 // if the token is a right bracket (i.e. ")"), then: 153 } elseif ($token->value === ')') { 154 // while the operator at the top of the operator stack is not a left bracket: 155 while (end($operator_stack)->value !== '(') { 156 // pop operators from the operator stack onto the output queue. 157 $left = array_pop( $output_queue ); 158 $right = array_pop( $output_queue ); 159 $output_queue[] = $this->createNode( array_pop($operator_stack),$left,$right ); 160 161 // /* if the stack runs out without finding a left bracket, then there are 162 // mismatched parentheses. */ 163 if (!$operator_stack) { 164 throw new DslParserException("Mismatched ')' parentheses"); 165 } 166 } 167 168 // pop the left bracket from the stack. 169 array_pop($operator_stack); 170 171 } 172 else { 173 $output_queue[] = $this->tokenToStatement( $token ); 174 } 175 } // if there are no more tokens to read: 176 177 // while there are still operator tokens on the stack: 178 while ($operator_stack) { 179 $token = array_pop($operator_stack); 180 181 // if the operator token on the top of the stack is a bracket, then 182 // there are mismatched parentheses. 183 if ($token->type == DslToken::T_OPERATOR && $token->value == '(') { 184 throw new DslParserException( "Mismatched '(' parentheses"); 185 } 186 // pop the operator onto the output queue. 187 $left = array_pop( $output_queue ); 188 $right = array_pop( $output_queue ); 189 $output_queue[] = $this->createNode( $token,$left,$right ); 190 } 191 192 $this->value = $output_queue[0]; 193 } 194 195 /** 196 * @param $op DslToken 197 * @param $left 198 * @param $right 199 * @throws DslParserException 200 */ 201 private function createNode($op, $left, $right) 202 { 203 if ( $op->value == '=' ) 204 return new DslAssignment( $right,$left ); 205 if ( $op->value == ',' ) 206 return new DslSequence( $right,$left ); 207 if ( $op->value == '.' ) 208 return new DslProperty( $right, $left ); 209 if ( $op->value == '$' ) 210 return new DslFunctionCall( $right, $left ); 211 else 212 return new DslOperation( $op->value,$right,$left ); 213 } 214 215 /** 216 * @param $token DslToken 217 */ 218 private function tokenToStatement($token) 219 { 220 switch( $token->type ) { 221 case DslToken::T_NONE: 222 return new DslInteger( 0 ); 223 case DslToken::T_NULL: 224 return new DslNull(); 225 case DslToken::T_TRUE: 226 return new DslTrue(); 227 case DslToken::T_FALSE: 228 return new DslFalse(); 229 case DslToken::T_NUMBER: 230 return new DslInteger( $token->value ); 231 case DslToken::T_TEXT: 232 return new DslString( $token->value ); 233 case DslToken::T_STRING: 234 return new DslVariable( $token->value ); 235 case DslToken::T_DOT: 236 return new DslProperty( $token->value ); 237 default: 238 throw new DslParserException('Unknown token '.$token->value,$token->lineNumber); 239 } 240 } 241 242 243 public static function convertValueToStandardObject($value ) { 244 245 if ( is_object( $value ) ) { 246 247 if ( $value instanceof BaseScriptableObject ) { 248 return $value; 249 } 250 251 if ( DslInterpreter::isSecure() ) 252 // Secured Sandbox, external objects are not evaluated. 253 return new StringInstance( 'ProtectedObject' ); 254 else 255 return $value; // Unsecured, but wanted. 256 } 257 elseif ( is_array( $value ) ) { 258 259 return new ArrayInstance($value); 260 } 261 elseif ( is_int( $value ) ) { 262 263 return new NumberInstance($value); 264 } 265 elseif ( is_float( $value ) ) { 266 267 return new NumberInstance($value); 268 } 269 elseif ( is_string( $value ) ) { 270 271 return new StringInstance($value); 272 } 273 } 274 }
Download modules/dsl/ast/DslExpression.class.php
History Tue, 19 Jul 2022 00:10:00 +0200 Jan Dankert New: Scripting language: Ignore Keyword "new"; Support for Calling object constructors; Splitting objects into an instance and a wrapper. Fri, 1 Jul 2022 18:26:28 +0200 Jan Dankert New: length properties for DSL objects; support for float values. Fri, 1 Jul 2022 18:09:05 +0200 Jan Dankert New: Bugfixes and much more string and array functions for the DSL. Sun, 26 Jun 2022 15:46:54 +0200 Jan Dankert Fix: Another, little better, hack for parameterless functions. Shunting yard seems to be unable to handle empty parentheses. Sun, 29 May 2022 16:56:40 +0200 Jan Dankert New: DSL with support for functions with return values, full arithmetic, object properties Sun, 29 May 2022 01:13:27 +0200 Jan Dankert New: DSL with support for functions Sat, 28 May 2022 18:00:13 +0200 Jan Dankert New: DSL with a much better syntax parsing and support for assignments, conditions, ... Wed, 25 May 2022 22:47:17 +0200 Jan Dankert New: DSL (domain specific language) for code elements. The old way with PHP code ist not sandboxed and unsecure. This approach is a minimalistic, javascript-like, scripting engine. For now only simple function calls are possible, for example: alert("example");