scriptbox

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

commit 1b73c202256637c6303cd254a3d69084a7a16572
parent 75378f112c75786bda393387d6aa1dfabd90345e
Author: Jan Dankert <develop@jandankert.de>
Date:   Sun, 26 Jun 2022 15:47:05 +0200

Fetched from upstream.

Diffstat:
Mdsl/DslAstParser.class.php | 4----
Mdsl/DslLexer.class.php | 11++++++++---
Mdsl/DslToken.class.php | 15+++++++++++++++
Mdsl/ast/DslExpression.class.php | 19++++++++++---------
Mdsl/ast/DslFunction.class.php | 5++---
Mdsl/ast/DslFunctionCall.class.php | 15+--------------
Mdsl/ast/DslOperation.class.php | 5++---
Mdsl/ast/DslStatementList.class.php | 6+++++-
Mdsl/executor/DslInterpreter.class.php | 37++++++++++++++++++++++++++++++++-----
Mindex.php | 5+++--
10 files changed, 78 insertions(+), 44 deletions(-)

diff --git a/dsl/DslAstParser.class.php b/dsl/DslAstParser.class.php @@ -18,11 +18,7 @@ class DslAstParser */ public function parse($tokens ) { - //echo "<h1>Token:</h1><pre>"; var_export( $tokens ); echo "</pre>"; - $this->rootStatement = new DslStatementList( $tokens ); - - //echo "<h1>AST:</h1><pre>"; var_export( $this->rootStatement ); echo "</pre>"; } diff --git a/dsl/DslLexer.class.php b/dsl/DslLexer.class.php @@ -175,7 +175,7 @@ class DslLexer } // Numbers - if ( $char >= '0' && $char <= '9') { + if ( $char >= '0' && $char <= '9' ) { $value = $char; while( true ) { $char = array_shift( $chars ); @@ -222,11 +222,16 @@ class DslLexer elseif ( $char == '(' ) { if ( end( $this->token)->type == DslToken::T_STRING) + // if string is followed by "(" it is a function or a function call $this->addToken( $line, DslToken::T_OPERATOR,'$'); // function call $this->addToken( $line,DslToken::T_BRACKET_OPEN,$char); } - elseif ( $char == ')' ) - $this->addToken( $line,DslToken::T_BRACKET_CLOSE,$char); + elseif ( $char == ')' ) { + if (end($this->token)->type == DslToken::T_BRACKET_OPEN) + // if there is an empty parenthesis, make it contain something, otherwise the shunting yard algo will fail. + $this->addToken($line, DslToken::T_NONE ); // + $this->addToken($line, DslToken::T_BRACKET_CLOSE, $char); + } elseif ( $char == '{' ) $this->addToken( $line,DslToken::T_BLOCK_BEGIN,$char); elseif ( $char == '}' ) diff --git a/dsl/DslToken.class.php b/dsl/DslToken.class.php @@ -44,4 +44,19 @@ class DslToken $this->value = $value; } + public function __toString() + { + return '#'.$this->lineNumber.':'.$this->type.':"'.$this->value.'"'; + } + + + /** + * @return bool + */ + public function isOperator( $value = null ) { + if ( ! $value ) + return $this->type == self::T_OPERATOR; + + return $this->type == self::T_OPERATOR && $this->value == $value; + } } \ No newline at end of file diff --git a/dsl/ast/DslExpression.class.php b/dsl/ast/DslExpression.class.php @@ -106,8 +106,8 @@ class DslExpression extends DslElement implements DslStatement $precedence['('] = 0; $precedence[')'] = 0; - $output_queue = array(); - $operator_stack = array(); + $output_queue = []; + $operator_stack = []; if ( $tokens instanceof DslStatement ) { @@ -118,14 +118,15 @@ class DslExpression extends DslElement implements DslStatement if ( $tokens instanceof DslToken ) $tokens = [$tokens]; - if ( ! is_array($tokens)) echo "tokens ist kein array, aber ".get_class($tokens); + if ( ! is_array($tokens)) + throw new DslParserException("tokens must be an array, but it is ".get_class($tokens)); // while there are tokens to be read: - while ($tokens) { + while ( $tokens ) { // read a token. $token = array_shift($tokens); - if ($token->type == DslToken::T_OPERATOR) { + if ($token->isOperator() ) { // while there is an operator at the top of the operator stack with // greater than or equal to precedence: @@ -157,7 +158,7 @@ class DslExpression extends DslElement implements DslStatement // /* if the stack runs out without finding a left bracket, then there are // mismatched parentheses. */ if (!$operator_stack) { - throw new DslParserException("Mismatched parentheses!"); + throw new DslParserException("Mismatched ')' parentheses"); } } @@ -182,7 +183,7 @@ class DslExpression extends DslElement implements DslStatement // if the operator token on the top of the stack is a bracket, then // there are mismatched parentheses. if ($token->type == DslToken::T_OPERATOR && $token->value == '(') { - throw new DslParserException( "Mismatched parentheses"); + throw new DslParserException( "Mismatched '(' parentheses"); } // pop the operator onto the output queue. $left = array_pop( $output_queue ); @@ -190,8 +191,6 @@ class DslExpression extends DslElement implements DslStatement $output_queue[] = $this->createNode( $token,$left,$right ); } - - //echo "<h5>Output queue:</h5><pre>"; var_export( $output_queue ); echo "</pre>"; $this->value = $output_queue[0]; } @@ -221,6 +220,8 @@ class DslExpression extends DslElement implements DslStatement private function tokenToStatement($token) { switch( $token->type ) { + case DslToken::T_NONE: + return new DslInteger( 0 ); case DslToken::T_NUMBER: return new DslInteger( $token->value ); case DslToken::T_TEXT: diff --git a/dsl/ast/DslFunction.class.php b/dsl/ast/DslFunction.class.php @@ -40,14 +40,13 @@ class DslFunction extends DslElement implements DslStatement */ public function __construct( $functionParameter, $functionBody ) { - //$this->parameters = $functionParameter; - $this->parameters = []; - //var_export($this->splitByComma( $functionParameter )); foreach( $this->splitByComma( $functionParameter ) as $parameter ) { if ( sizeof($parameter) != 1 ) throw new DslParserException('function parameter must be a single name'); $nameToken = $parameter[0]; + if ( $nameToken->type == DslToken::T_NONE ) // no parameter + continue; if ( $nameToken->type != DslToken::T_STRING ) throw new DslParserException('function parameter must be a name'); diff --git a/dsl/ast/DslFunctionCall.class.php b/dsl/ast/DslFunctionCall.class.php @@ -17,14 +17,6 @@ class DslFunctionCall implements DslStatement */ public function __construct($name, $parameters) { - //echo "name:";var_export( $name ); - //echo "params:";var_export( $parameters ); - - // Parameterless function calls are not correctly detected by the AST parser - if ( $name==null) { - $name = $parameters; - $parameters = null; - } $this->name = $name; $this->parameters = $parameters; } @@ -35,15 +27,11 @@ class DslFunctionCall implements DslStatement */ public function execute(& $context ) { - //var_export($this->name); $function = $this->name->execute( $context ); - //echo "name is $name"; //if ( ! array_key_exists( $name, $context ) ) // throw new DslRuntimeException('function \''.$this->name.'\' does not exist.'); -// -// $function = $context[$name]; if ( $this->parameters == null ) $parameterValues = []; // parameterless functions. @@ -63,9 +51,8 @@ class DslFunctionCall implements DslStatement elseif ( $function instanceof DslFunction ) { $parameters = $function->parameters; - //var_export( $function->parameters); - if ( sizeof($parameters) != sizeof($parameterValues) ) + if ( sizeof($parameters) > sizeof($parameterValues) ) throw new DslRuntimeException('function call has '.sizeof($parameterValues).' parameters but the function has '.sizeof($parameters).' parameters'); // Put all function parameters to the function context. diff --git a/dsl/ast/DslOperation.class.php b/dsl/ast/DslOperation.class.php @@ -21,8 +21,8 @@ class DslOperation implements DslStatement public function __construct($operator, $left, $right) { $this->operator = $operator; - $this->left = new DslExpression( $left ); - $this->right = new DslExpression($right ); + $this->left = new DslExpression( $left ); + $this->right = new DslExpression( $right ); } @@ -83,6 +83,5 @@ class DslOperation implements DslStatement public function parse($tokens) { - // TODO: Implement parse() method. } } \ No newline at end of file diff --git a/dsl/ast/DslStatementList.class.php b/dsl/ast/DslStatementList.class.php @@ -80,10 +80,14 @@ class DslStatementList extends DslElement implements DslStatement $name = $nameToken->value; $functionCallOp = array_shift($tokens); + if ( $functionCallOp->type != DslToken::T_OPERATOR || $functionCallOp->value != '$' ) + throw new DslParserException('function \''.$name.'\' must have a function signature'); + $functionParameter = $this->getGroup($tokens); - $functionBlock = $this->getBlock($tokens); + $functionBlock = $this->getBlock($tokens); $this->functions[ $name ] = new DslFunction( $functionParameter, $functionBlock ); + break; case DslToken::T_IF: diff --git a/dsl/executor/DslInterpreter.class.php b/dsl/executor/DslInterpreter.class.php @@ -5,6 +5,7 @@ namespace dsl\executor; use dsl\DslAstParser; use dsl\DslException; use dsl\DslLexer; +use dsl\standard\Script; use dsl\standard\StandardArray; use dsl\standard\StandardDate; use dsl\standard\StandardMath; @@ -24,9 +25,17 @@ class DslInterpreter * @var Write */ private $writer; + private $flags; - public function __construct() + const FLAG_SHOW_ERROR = 1; + const FLAG_SHOW_TRACE = 2; + const FLAG_THROW_ERROR = 4; + const FLAG_DEBUG = 8; + + public function __construct( $flags = self::FLAG_SHOW_ERROR ) { + $this->flags = $flags; + // Standard-Globals $this->addContext( [ 'Math' => new StandardMath(), @@ -60,11 +69,29 @@ class DslInterpreter $token = $lexer->tokenize( $code ); // Step 2: Creating a syntax tree (abstract syntax tree, AST). - $parser = new DslAstParser(); - $parser->parse( $token ); + try { + + $parser = new DslAstParser(); + $parser->parse( $token ); + + //if ( $this->flags & self::FLAG_DEBUG ) + // it has no security impact, so lets do it always. + $this->addContext( + [ 'Script' => new Script( $token,$parser->rootStatement ) ] + ); - // Step 3: Executing the syntax tree. - return $parser->execute( $this->context ); + // Step 3: Executing the syntax tree. + return $parser->execute( $this->context ); + } catch ( \Exception $e ) { + if ( $this->flags & self::FLAG_SHOW_ERROR ) { + if ( $this->flags & self::FLAG_SHOW_TRACE ) + $this->writer->buffer .= $e->__toString(); + else + $this->writer->buffer .= $e->getMessage(); + } + if ( $this->flags & self::FLAG_THROW_ERROR ) + throw $e; + } } diff --git a/index.php b/index.php @@ -38,11 +38,12 @@ DEF <?php try { error_reporting( E_ALL ); - $interpreter = new DslInterpreter(); + $interpreter = new DslInterpreter( DslInterpreter::FLAG_SHOW_ERROR + DslInterpreter::FLAG_SHOW_TRACE ); $interpreter->runCode( $code ); echo htmlentities( $interpreter->getOutput() ); } catch( Exception $e ) { - echo htmlentities( $e->getMessage() ); + // should never happen because the interpreter catches all. + echo "Unexcepted error while running the script: \n".htmlentities( $e->getMessage() )."\n"; } ?> </pre></legend>