commit 1b73c202256637c6303cd254a3d69084a7a16572
parent 75378f112c75786bda393387d6aa1dfabd90345e
Author: Jan Dankert <develop@jandankert.de>
Date: Sun, 26 Jun 2022 15:47:05 +0200
Fetched from upstream.
Diffstat:
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>