Minor changes
This commit is contained in:
parent
05cc358eea
commit
e9d033044c
27 changed files with 949 additions and 94 deletions
2
.idea/php.xml
generated
2
.idea/php.xml
generated
|
@ -15,6 +15,8 @@
|
|||
<path value="/etc/ncc" />
|
||||
<path value="/var/ncc/packages/net.nosial.optslib=1.0.1" />
|
||||
<path value="/var/ncc/packages/net.nosial.loglib=1.0.0" />
|
||||
<path value="/var/ncc/packages/net.nosial.rtex_filesystem=1.0.0" />
|
||||
<path value="/var/ncc/packages/com.cheprasov.php_redis_client=1.10.0" />
|
||||
</include_path>
|
||||
</component>
|
||||
<component name="PhpProjectSharedConfiguration" php_language_level="8.2" />
|
||||
|
|
1
.idea/webResources.xml
generated
1
.idea/webResources.xml
generated
|
@ -6,6 +6,7 @@
|
|||
<entryData>
|
||||
<resourceRoots>
|
||||
<path value="file://$PROJECT_DIR$/instructions" />
|
||||
<path value="file://$PROJECT_DIR$/types" />
|
||||
</resourceRoots>
|
||||
</entryData>
|
||||
</entry>
|
||||
|
|
BIN
InstructionInterface.png
Normal file
BIN
InstructionInterface.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 339 KiB |
32
README.md
32
README.md
|
@ -10,13 +10,28 @@ or you are executing programs written by users,
|
|||
but you don't want to give them the ability to
|
||||
execute arbitrary code on your system
|
||||
|
||||
## Table of Contents
|
||||
|
||||
<!-- TOC -->
|
||||
* [RTEX Engine](#rtex-engine)
|
||||
* [Table of Contents](#table-of-contents)
|
||||
* [How does it work?](#how-does-it-work)
|
||||
* [Instructions](#instructions)
|
||||
* [Methods & Namespaces](#methods--namespaces)
|
||||
* [Builtin Instruction Sets](#builtin-instruction-sets)
|
||||
* [License](#license)
|
||||
* [Contributing](#contributing)
|
||||
<!-- TOC -->
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
## How does it work?
|
||||
|
||||
RTEX Engine operates on the simple principle of executing
|
||||
instructions in order, and then moving on to the next
|
||||
instruction.
|
||||
|
||||
|
||||
[README.md](README.md)
|
||||
### Instructions
|
||||
|
||||
Instructions are simply associative arrays that contain
|
||||
|
@ -35,8 +50,6 @@ functions do not provide a way to interact with the host
|
|||
system or the filesystem, but you can extend the engine
|
||||
with your own methods and namespaces
|
||||
|
||||
#### JSON Representation
|
||||
|
||||
Since instructions are associative arrays, they can be
|
||||
represented in JSON, which is a common format for data
|
||||
transfer. The following is an example of a set of
|
||||
|
@ -86,6 +99,7 @@ instructions that is shown above in JSON format:
|
|||
]
|
||||
```
|
||||
|
||||
|
||||
### Methods & Namespaces
|
||||
|
||||
Methods are different to instructions in the terms
|
||||
|
@ -106,6 +120,18 @@ when they are invoked.
|
|||
***Note:*** This functionality is a WIP, more
|
||||
information will be added as it is implemented
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
## Builtin Instruction Sets
|
||||
|
||||
RTEX Engine comes with a limited set of instructions by default,
|
||||
but you can extend the engine with your own methods and namespaces
|
||||
|
||||
- base
|
||||
- [get](docs/instructions/base/get.md)
|
||||
- [set](docs/instructions/base/get.md)
|
||||
- math
|
||||
- [add](docs/instructions/add.md)
|
||||
|
||||
# License
|
||||
|
||||
|
|
81
config_example.yaml
Normal file
81
config_example.yaml
Normal file
|
@ -0,0 +1,81 @@
|
|||
# Runtime configuration for RTEX, this section defines the configuration
|
||||
# values used for the engine. Allowing you to set a maximum number of
|
||||
# resources a program may use, or disable certain instructions.
|
||||
# you can also import other NCC packages designed to extend RTEX
|
||||
# such as the 'com.nosia.rtex_filesystem' package which adds IO functionality to RTEX.
|
||||
runtime:
|
||||
# The maximum value size in bytes, this is the maximum size of a value
|
||||
# that can be stored in memory. This is used to prevent programs from
|
||||
# using too much memory.
|
||||
# (default: 0)
|
||||
max_variable_size: 0
|
||||
|
||||
# The maximum number of variables that can be stored in memory at once.
|
||||
# (default: 0)
|
||||
max_variables: 0
|
||||
|
||||
# The maximum number of instructions that can be executed before the
|
||||
# program is terminated.
|
||||
# (default: 0)
|
||||
max_stack_size: 0
|
||||
|
||||
# A list of instructions that are disabled, this is used to prevent
|
||||
# programs from using certain instructions.
|
||||
# (default: [])
|
||||
instruction_blacklist:
|
||||
- "eq"
|
||||
- "neq"
|
||||
- "gt"
|
||||
- "gte"
|
||||
|
||||
# A list of packages to import, this is used to import packages that
|
||||
# extend the functionality of RTEX.
|
||||
# (default: [])
|
||||
import_namespaces:
|
||||
- "com.nosial.rtex_filesystem"
|
||||
|
||||
# Enabling supervisor mode will allow the runtime to spawn the program
|
||||
# as a child process and monitor the resource usage of the child process.
|
||||
# This is useful to terminate badly behaving programs. (eg; infinite loops)
|
||||
# The supervisor will terminate the child process if it exceeds the
|
||||
# resource limits set in the runtime section.
|
||||
supervisor:
|
||||
|
||||
# If true, the supervisor will be enabled
|
||||
enabled: true
|
||||
|
||||
# The maximum number of seconds a program can run for
|
||||
max_execution_time: 100 # in seconds
|
||||
|
||||
# The maximum number of seconds a program can use the CPU for
|
||||
max_cpu_time: 100 # in seconds
|
||||
|
||||
# The maximum number of bytes a program can use for memory
|
||||
max_memory: 1000000 # in bytes
|
||||
|
||||
|
||||
# Enabling a Redis hook will allow for easier debugging of programs.
|
||||
# This will allow you to view the state of the program at any time
|
||||
# This works by providing a reference ID to the program as a command-line
|
||||
# argument.
|
||||
#
|
||||
# The engine will then connect to a Redis server and store the state of
|
||||
# the program in a Redis hash. This hash will be updated every time the
|
||||
# program executes an instruction.
|
||||
#
|
||||
# Libraries can also use this hook to store data in the Redis hash.
|
||||
redis_hook:
|
||||
# If true, the Redis hook will be enabled
|
||||
enabled: True
|
||||
|
||||
# The host of the Redis server
|
||||
host: "127.0.0.1"
|
||||
|
||||
# The port of the Redis server
|
||||
port: 6379
|
||||
|
||||
# The password of the Redis server (optional)
|
||||
password: null
|
||||
|
||||
# If the information should be destroyed when the program exits
|
||||
destroy_on_exit: False
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"type": "get",
|
||||
"_": {
|
||||
"variable": "foo"
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"type": "set",
|
||||
"_": {
|
||||
"variable": "foo",
|
||||
"value": "bar"
|
||||
}
|
||||
}
|
11
project.json
11
project.json
|
@ -38,7 +38,10 @@
|
|||
"build": {
|
||||
"source_path": "src",
|
||||
"default_configuration": "release",
|
||||
"main_execution": "main",
|
||||
"main": {
|
||||
"policy": "main",
|
||||
"create_symlink": true
|
||||
},
|
||||
"define_constants": {
|
||||
"ASSEMBLY_NAME": "%ASSEMBLY.NAME%",
|
||||
"ASSEMBLY_PACKAGE": "%ASSEMBLY.PACKAGE%",
|
||||
|
@ -57,6 +60,12 @@
|
|||
"version": "latest",
|
||||
"source_type": "remote",
|
||||
"source": "nosial/libs.log=latest@n64"
|
||||
},
|
||||
{
|
||||
"name": "com.cheprasov.php_redis_client",
|
||||
"version": "latest",
|
||||
"source_type": "remote",
|
||||
"source": "cheprasov/php-redis-client=latest@composer"
|
||||
}
|
||||
],
|
||||
"configurations": [
|
||||
|
|
|
@ -27,9 +27,9 @@
|
|||
const Equals = 'eq';
|
||||
const NotEquals = 'neq';
|
||||
const GreaterThan = 'gt';
|
||||
const GreaterThanOrEquals = 'gte';
|
||||
const GreaterThanOrEqual = 'gte';
|
||||
const LessThan = 'lt';
|
||||
const LessThanOrEquals = 'lte';
|
||||
const LessThanOrEqual = 'lte';
|
||||
|
||||
|
||||
const All = [
|
||||
|
@ -52,9 +52,9 @@
|
|||
|
||||
self::Equals,
|
||||
self::GreaterThan,
|
||||
self::GreaterThanOrEquals,
|
||||
self::GreaterThanOrEqual,
|
||||
self::LessThan,
|
||||
self::LessThanOrEquals,
|
||||
self::LessThanOrEqual,
|
||||
self::NotEquals,
|
||||
];
|
||||
}
|
27
src/RTEX/Classes/RedisHookInstance.php
Normal file
27
src/RTEX/Classes/RedisHookInstance.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php /** @noinspection PhpMissingFieldTypeInspection */
|
||||
|
||||
namespace RTEX\Classes;
|
||||
|
||||
use RedisClient\RedisClient;
|
||||
|
||||
class RedisHookInstance
|
||||
{
|
||||
public function __construct(string $host, int $port, ?string $password=null)
|
||||
{
|
||||
if(extension_loaded('redis'))
|
||||
{
|
||||
$this->Redis = new \Redis();
|
||||
$this->Redis->connect($host, $port);
|
||||
if($password !== null)
|
||||
$this->Redis->auth($password);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->Redis = new RedisClient([
|
||||
'host' => $host,
|
||||
'port' => $port,
|
||||
'password' => $password
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,10 +12,11 @@
|
|||
* Determines the type of variable, throws an exception if the type is not supported
|
||||
*
|
||||
* @param $input
|
||||
* @param bool $return_unknown
|
||||
* @return string
|
||||
* @throws TypeException
|
||||
*/
|
||||
public static function getType($input): string
|
||||
public static function getType($input, bool $return_unknown=false): string
|
||||
{
|
||||
if ($input instanceof InstructionInterface)
|
||||
return VariableType::Instruction;
|
||||
|
@ -33,6 +34,8 @@
|
|||
return VariableType::Array;
|
||||
if (is_null($input))
|
||||
return VariableType::Null;
|
||||
if ($return_unknown)
|
||||
return VariableType::Unknown;
|
||||
|
||||
throw new TypeException(gettype($input));
|
||||
}
|
||||
|
@ -43,9 +46,8 @@
|
|||
*
|
||||
* @param $input
|
||||
* @return array|mixed
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
*/
|
||||
public static function toArray($input)
|
||||
public static function toArray($input): mixed
|
||||
{
|
||||
if($input instanceof InstructionInterface)
|
||||
return $input->toArray();
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
<?php
|
||||
|
||||
/** @noinspection PhpMissingFieldTypeInspection */
|
||||
|
||||
namespace RTEX;
|
||||
|
||||
use Exception;
|
||||
use LogLib\Log;
|
||||
use RTEX\Abstracts\VariableTypes;
|
||||
use RTEX\Classes\Utilities;
|
||||
use RTEX\Exceptions\Core\UnsupportedVariableType;
|
||||
use RTEX\Exceptions\EvaluationException;
|
||||
use RTEX\Interfaces\InstructionInterface;
|
||||
use RTEX\Objects\Environment;
|
||||
use RTEX\Objects\Engine\Environment;
|
||||
use RTEX\Objects\Program;
|
||||
|
||||
class Engine
|
||||
|
@ -32,9 +33,9 @@
|
|||
* Executes the program by running the main script of the program
|
||||
*
|
||||
* @return void
|
||||
* @throws UnsupportedVariableType
|
||||
* @throws EvaluationException
|
||||
*/
|
||||
public function run()
|
||||
public function run(): void
|
||||
{
|
||||
foreach($this->Program->getMain()->getInstructions() as $instruction)
|
||||
{
|
||||
|
@ -47,20 +48,24 @@
|
|||
*
|
||||
* @param $input
|
||||
* @return mixed
|
||||
* @throws UnsupportedVariableType
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
* @throws EvaluationException
|
||||
*/
|
||||
public function eval($input)
|
||||
public function eval($input): mixed
|
||||
{
|
||||
switch(Utilities::determineType($input))
|
||||
try
|
||||
{
|
||||
case VariableTypes::Instruction:
|
||||
/** @var InstructionInterface $input */
|
||||
if($input instanceof InstructionInterface)
|
||||
{
|
||||
Log::debug('net.nosial.rtex', $input);
|
||||
return $input->eval($this);
|
||||
|
||||
default:
|
||||
return $input;
|
||||
}
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
throw new EvaluationException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,4 +91,18 @@
|
|||
{
|
||||
return $this->Environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the method
|
||||
*
|
||||
* @param string $namespace
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function callMethod(string $namespace, string $method, array $arguments)
|
||||
{
|
||||
// TODO: Implement callMethod() method.
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RTEX\Exceptions\Core;
|
||||
|
||||
class MalformedInstructionException extends \Exception
|
||||
{
|
||||
/**
|
||||
* MalformedInstructionException constructor.
|
||||
*
|
||||
* @param string $message
|
||||
*/
|
||||
public function __construct(string $message)
|
||||
{
|
||||
parent::__construct($message);
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RTEX\Exceptions\Core;
|
||||
|
||||
class UnsupportedInstructionException extends \Exception
|
||||
{
|
||||
/**
|
||||
* UnsupportedInstructionException constructor.
|
||||
*
|
||||
* @param string $message
|
||||
*/
|
||||
public function __construct(string $message)
|
||||
{
|
||||
parent::__construct($message);
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RTEX\Exceptions\Core;
|
||||
|
||||
use Exception;
|
||||
|
||||
class UnsupportedVariableType extends Exception
|
||||
{
|
||||
public function __construct($type)
|
||||
{
|
||||
parent::__construct("Unsupported variable type: $type");
|
||||
}
|
||||
}
|
21
src/RTEX/Interfaces/DefinedNamespaceInterface.php
Normal file
21
src/RTEX/Interfaces/DefinedNamespaceInterface.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace RTEX\Interfaces;
|
||||
|
||||
interface DefinedNamespaceInterface
|
||||
{
|
||||
/**
|
||||
* Returns an array of available methods with their names as keys
|
||||
* and their class names as values (e.g. ['clear' => ClearMethod::class])
|
||||
*
|
||||
* @return MethodInterface[]
|
||||
*/
|
||||
public static function getMethods(): array;
|
||||
|
||||
/**
|
||||
* Returns the name of the namespace (e.g. 'console')
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getName(): string;
|
||||
}
|
18
src/RTEX/Interfaces/MethodInterface.php
Normal file
18
src/RTEX/Interfaces/MethodInterface.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace RTEX\Interfaces;
|
||||
|
||||
use RTEX\Engine;
|
||||
|
||||
interface MethodInterface
|
||||
{
|
||||
/**
|
||||
* Invokes the method with the given parameters and returns the result
|
||||
* of the invocation
|
||||
*
|
||||
* @param Engine $engine
|
||||
* @param array $parameters
|
||||
* @return mixed|void
|
||||
*/
|
||||
public static function invoke(Engine $engine, array $parameters);
|
||||
}
|
57
src/RTEX/Objects/Engine/Configuration.php
Normal file
57
src/RTEX/Objects/Engine/Configuration.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/** @noinspection PhpMissingFieldTypeInspection */
|
||||
|
||||
namespace RTEX\Objects\Engine;
|
||||
|
||||
use RTEX\Objects\Engine\Configuration\RedisHook;
|
||||
|
||||
class Configuration
|
||||
{
|
||||
/**
|
||||
* The redis hook configuration
|
||||
*
|
||||
* @var RedisHook
|
||||
*/
|
||||
private $RedisHook;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->RedisHook = new RedisHook();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the redis hook configuration
|
||||
*
|
||||
* @return RedisHook
|
||||
*/
|
||||
public function getRedisHook(): RedisHook
|
||||
{
|
||||
return $this->RedisHook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array representation of the configuration
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'redis_hook' => $this->RedisHook->toArray()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a configuration object from an array
|
||||
*
|
||||
* @param array $data
|
||||
* @return static
|
||||
*/
|
||||
public static function fromArray(array $data): self
|
||||
{
|
||||
$instance = new self();
|
||||
$instance->RedisHook = RedisHook::fromArray(($data['redis_hook'] ?? []));
|
||||
return $instance;
|
||||
}
|
||||
}
|
172
src/RTEX/Objects/Engine/Configuration/RedisHook.php
Normal file
172
src/RTEX/Objects/Engine/Configuration/RedisHook.php
Normal file
|
@ -0,0 +1,172 @@
|
|||
<?php
|
||||
|
||||
/** @noinspection PhpMissingFieldTypeInspection */
|
||||
|
||||
namespace RTEX\Objects\Engine\Configuration;
|
||||
|
||||
class RedisHook
|
||||
{
|
||||
/**
|
||||
* If the hook is enabled or not
|
||||
* (default: false)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $Enabled;
|
||||
|
||||
/**
|
||||
* The host of the redis server
|
||||
* (default: 127.0.0.1)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $Host;
|
||||
|
||||
/**
|
||||
* The port of the redis server
|
||||
* (default: 6379)
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $Port;
|
||||
|
||||
/**
|
||||
* Optional. Use it only if Redis server requires password (AUTH)
|
||||
* (default: null)
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
private $Password;
|
||||
|
||||
/**
|
||||
* Public Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->Enabled = false;
|
||||
$this->Host = '127.0.0.1';
|
||||
$this->Port = 6379;
|
||||
$this->Password = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the hook is enabled or not
|
||||
*
|
||||
* @return false|string
|
||||
*/
|
||||
public function isEnabled(): false|string
|
||||
{
|
||||
return $this->Enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the hook
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
public function enable(): void
|
||||
{
|
||||
$this->Enabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the hook
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function disable(): void
|
||||
{
|
||||
$this->Enabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the host of the redis server
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHost(): string
|
||||
{
|
||||
return $this->Host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the host of the redis server
|
||||
*
|
||||
* @param string $Host
|
||||
*/
|
||||
public function setHost(string $Host): void
|
||||
{
|
||||
$this->Host = $Host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the port of the redis server
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPort(): int
|
||||
{
|
||||
return $this->Port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the port of the redis server
|
||||
*
|
||||
* @param int $Port
|
||||
*/
|
||||
public function setPort(int $Port): void
|
||||
{
|
||||
$this->Port = $Port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the password of the redis server
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPassword(): ?string
|
||||
{
|
||||
return $this->Password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the password of the redis server
|
||||
*
|
||||
* @param string|null $Password
|
||||
*/
|
||||
public function setPassword(?string $Password): void
|
||||
{
|
||||
$this->Password = $Password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array representation of the configuration
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'enabled' => $this->Enabled,
|
||||
'host' => $this->Host,
|
||||
'port' => $this->Port,
|
||||
'password' => $this->Password
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance from an array
|
||||
*
|
||||
* @param array $data
|
||||
* @return RedisHook
|
||||
*/
|
||||
public static function fromArray(array $data): self
|
||||
{
|
||||
$instance = new self();
|
||||
$instance->Enabled = ($data['enabled'] ?? false);
|
||||
$instance->Host = ($data['host'] ?? '127.0.0.1');
|
||||
$instance->Port = ($data['port'] ?? 6379);
|
||||
$instance->Password = ($data['password'] ?? null);
|
||||
return $instance;
|
||||
}
|
||||
}
|
211
src/RTEX/Objects/Engine/Configuration/Runtime.php
Normal file
211
src/RTEX/Objects/Engine/Configuration/Runtime.php
Normal file
|
@ -0,0 +1,211 @@
|
|||
<?php
|
||||
|
||||
/** @noinspection PhpMissingFieldTypeInspection */
|
||||
|
||||
namespace RTEX\Objects\Engine\Configuration;
|
||||
|
||||
class Runtime
|
||||
{
|
||||
/**
|
||||
* The maximum size a variable value can have (in bytes)
|
||||
* (default: 0) (no limit)
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $MaxVariableSize;
|
||||
|
||||
/**
|
||||
* The maximum number of variables that can be defined
|
||||
* (default: 0) (no limit)
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $MaxVariables;
|
||||
|
||||
/**
|
||||
* The maximum number of instructions that can be executed
|
||||
* (default: 0) (no limit)
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $MaxStackSize;
|
||||
|
||||
/**
|
||||
* An array of instructions that are not allowed to be executed
|
||||
* if the script attempts to execute one of these instructions
|
||||
* the engine will treat it as a fatal error and stop the execution
|
||||
*
|
||||
* For production environments it is recommended to disable dangerous
|
||||
* instructions such as regex if you aren't supervising the engine
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $InstructionBlacklist;
|
||||
|
||||
/**
|
||||
* An array of ncc packages to import as a namespace into the engine
|
||||
* (The engine will look for a file named 'rtex.conf' in the package
|
||||
* source directory and register the defined methods for that namespace)
|
||||
*
|
||||
* This will only work for packages designed for the engine and will
|
||||
* not work for regular PHP packages (unless they include support for RTEX)
|
||||
*
|
||||
* Values must be in the format of 'com.example.package' (The same format as
|
||||
* importing packages via the import() function)
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $ImportNamespaces;
|
||||
|
||||
/**
|
||||
* Public Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->MaxVariableSize = 0;
|
||||
$this->MaxVariables = 0;
|
||||
$this->MaxStackSize = 0;
|
||||
$this->InstructionBlacklist = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getMaxVariableSize(): int
|
||||
{
|
||||
return $this->MaxVariableSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $MaxVariableSize
|
||||
*/
|
||||
public function setMaxVariableSize(int $MaxVariableSize): void
|
||||
{
|
||||
$this->MaxVariableSize = $MaxVariableSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getMaxVariables(): int
|
||||
{
|
||||
return $this->MaxVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $MaxVariables
|
||||
*/
|
||||
public function setMaxVariables(int $MaxVariables): void
|
||||
{
|
||||
$this->MaxVariables = $MaxVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getMaxStackSize(): int
|
||||
{
|
||||
return $this->MaxStackSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $MaxStackSize
|
||||
*/
|
||||
public function setMaxStackSize(int $MaxStackSize): void
|
||||
{
|
||||
$this->MaxStackSize = $MaxStackSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instruction blacklist
|
||||
*
|
||||
* @return array|string[]
|
||||
*/
|
||||
public function getInstructionBlacklist(): array
|
||||
{
|
||||
return $this->InstructionBlacklist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the instruction blacklist
|
||||
*
|
||||
* @param array|string[] $InstructionBlacklist
|
||||
*/
|
||||
public function setInstructionBlacklist(array $InstructionBlacklist): void
|
||||
{
|
||||
$this->InstructionBlacklist = $InstructionBlacklist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an instruction to the blacklist
|
||||
*
|
||||
* @param string $instruction
|
||||
* @return void
|
||||
*/
|
||||
public function addInstructionToBlacklist(string $instruction): void
|
||||
{
|
||||
if (in_array($instruction, $this->InstructionBlacklist))
|
||||
return;
|
||||
|
||||
$this->InstructionBlacklist[] = $instruction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an instruction from the blacklist
|
||||
*
|
||||
* @param string $instruction
|
||||
* @return void
|
||||
*/
|
||||
public function removeInstructionFromBlacklist(string $instruction): void
|
||||
{
|
||||
if (!in_array($instruction, $this->InstructionBlacklist))
|
||||
return;
|
||||
|
||||
$this->InstructionBlacklist = array_diff($this->InstructionBlacklist, [$instruction]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getImportNamespaces(): array
|
||||
{
|
||||
return $this->ImportNamespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $ImportNamespaces
|
||||
*/
|
||||
public function setImportNamespaces(array $ImportNamespaces): void
|
||||
{
|
||||
$this->ImportNamespaces = $ImportNamespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a namespace to the import list
|
||||
*
|
||||
* @param string $namespace
|
||||
* @return void
|
||||
*/
|
||||
public function addNamespaceToImport(string $namespace): void
|
||||
{
|
||||
if (in_array($namespace, $this->ImportNamespaces))
|
||||
return;
|
||||
|
||||
$this->ImportNamespaces[] = $namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a namespace from the import list
|
||||
*
|
||||
* @param string $namespace
|
||||
* @return void
|
||||
*/
|
||||
public function removeNamespaceFromImport(string $namespace): void
|
||||
{
|
||||
if (!in_array($namespace, $this->ImportNamespaces))
|
||||
return;
|
||||
|
||||
$this->ImportNamespaces = array_diff($this->ImportNamespaces, [$namespace]);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace RTEX\Objects;
|
||||
/** @noinspection PhpMissingFieldTypeInspection */
|
||||
|
||||
namespace RTEX\Objects\Engine;
|
||||
|
||||
use LogLib\Log;
|
||||
use RTEX\Exceptions\Runtime\NameException;
|
||||
|
||||
class Environment
|
||||
{
|
||||
|
@ -24,10 +27,15 @@
|
|||
*
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
* @throws NameException
|
||||
*/
|
||||
public function getRuntimeVariable(string $name)
|
||||
public function getRuntimeVariable(string $name): mixed
|
||||
{
|
||||
Log::debug('net.nosial.rtex', $name);
|
||||
|
||||
if (!$this->variableExists($name))
|
||||
throw new NameException("Variable '$name' is not defined");
|
||||
|
||||
return $this->RuntimeVariables[$name];
|
||||
}
|
||||
|
||||
|
@ -37,22 +45,42 @@
|
|||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setRuntimeVariable(string $name, $value): void
|
||||
public function setRuntimeVariable(string $name, mixed $value): void
|
||||
{
|
||||
Log::debug('net.nosial.rtex', $name);
|
||||
$this->RuntimeVariables[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function variableExists(string $name): bool
|
||||
{
|
||||
return array_key_exists($name, $this->RuntimeVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the value of the specified variable
|
||||
*
|
||||
* @return void
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function clearRuntimeVariables(): void
|
||||
{
|
||||
$this->RuntimeVariables = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of variables in the environment
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function countRuntimeVariables(): int
|
||||
{
|
||||
return count($this->RuntimeVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array representation of the object
|
||||
*
|
|
@ -1,10 +1,11 @@
|
|||
<?php
|
||||
|
||||
/** @noinspection PhpMissingFieldTypeInspection */
|
||||
|
||||
namespace RTEX\Objects\Program;
|
||||
|
||||
use RTEX\Classes\Utilities;
|
||||
use RTEX\Exceptions\Core\MalformedInstructionException;
|
||||
use RTEX\Exceptions\Core\UnsupportedVariableType;
|
||||
use RTEX\Classes\InstructionBuilder;
|
||||
use RTEX\Exceptions\InstructionException;
|
||||
use RTEX\Interfaces\InstructionInterface;
|
||||
|
||||
class Script
|
||||
|
@ -50,6 +51,7 @@
|
|||
* Returns the number of instructions in the script
|
||||
*
|
||||
* @return int
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function getInstructionCount(): int
|
||||
{
|
||||
|
@ -73,18 +75,34 @@
|
|||
* @param int $index
|
||||
* @param InstructionInterface $instruction
|
||||
* @return void
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function replaceInstruction(int $index, InstructionInterface $instruction): void
|
||||
{
|
||||
$this->Instructions[$index] = $instruction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorders the instructions in the script
|
||||
*
|
||||
* @param int $index
|
||||
* @param int $newIndex
|
||||
* @return void
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function reorderInstruction(int $index, int $newIndex): void
|
||||
{
|
||||
$instruction = $this->getInstruction($index);
|
||||
$this->deleteInstruction($index);
|
||||
array_splice($this->Instructions, $newIndex, 0, [$instruction]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all instructions from the script
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clear()
|
||||
public function clear(): void
|
||||
{
|
||||
$this->Instructions = [];
|
||||
}
|
||||
|
@ -110,15 +128,14 @@
|
|||
*
|
||||
* @param array $data
|
||||
* @return Script
|
||||
* @throws MalformedInstructionException
|
||||
* @throws UnsupportedVariableType
|
||||
* @throws InstructionException
|
||||
*/
|
||||
public static function fromArray(array $data): Script
|
||||
{
|
||||
$script = new Script();
|
||||
|
||||
foreach ($data as $instruction)
|
||||
$script->addInstruction(Utilities::constructInstruction($instruction));
|
||||
$script->addInstruction(InstructionBuilder::fromRaw($instruction));
|
||||
|
||||
return $script;
|
||||
}
|
||||
|
@ -130,4 +147,18 @@
|
|||
{
|
||||
return $this->Instructions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the script (for debugging)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
$results = [];
|
||||
foreach ($this->Instructions as $instruction)
|
||||
$results[] = $instruction->__toString();
|
||||
|
||||
return implode("\n", $results);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,69 @@
|
|||
|
||||
namespace RTEX;
|
||||
|
||||
use OptsLib\Parse;
|
||||
|
||||
class RTEX
|
||||
{
|
||||
/**
|
||||
* The main CLI entry point for the RTEX program
|
||||
*
|
||||
* @return void
|
||||
* @noinspection PhpNoReturnAttributeCanBeAddedInspection
|
||||
*/
|
||||
public static function main(): void
|
||||
{
|
||||
$args = Parse::getArguments();
|
||||
|
||||
$file = $args['path'] ?? $args['p'] ?? null;
|
||||
|
||||
if(($args['version'] ?? $args['v'] ?? false))
|
||||
{
|
||||
self::displayVersion(true);
|
||||
}
|
||||
|
||||
if($file == null || ($args['help'] ?? $args['h'] ?? false))
|
||||
{
|
||||
self::displayHelp(true);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the version of the RTEX program and optionally exits the program
|
||||
*
|
||||
* @param bool $exit
|
||||
* @return void
|
||||
*/
|
||||
private static function displayVersion(bool $exit=false): void
|
||||
{
|
||||
print('RTEX 0.1.0' . PHP_EOL);
|
||||
|
||||
if($exit)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the help menu and optionally exits the program
|
||||
*
|
||||
* @param bool $exit
|
||||
* @return void
|
||||
*/
|
||||
private static function displayHelp(bool $exit=false): void
|
||||
{
|
||||
print('RTEX - Runtime Execution' . PHP_EOL);
|
||||
print('Usage: rtex [options] [file]' . PHP_EOL);
|
||||
print('Options:' . PHP_EOL);
|
||||
print(' -h, --help Display this help message' . PHP_EOL);
|
||||
print(' -p, --path Specify the path to the program file' . PHP_EOL);
|
||||
print(' -v, --version Display the version of RTEX' . PHP_EOL);
|
||||
|
||||
if($exit)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
14
src/RTEX/bin/main
Normal file
14
src/RTEX/bin/main
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
require 'ncc';
|
||||
import('net.nosial.rtex', 'latest');
|
||||
|
||||
try
|
||||
{
|
||||
\RTEX\RTEX::main();
|
||||
}
|
||||
catch(\Exception $e)
|
||||
{
|
||||
print($e->getMessage() . PHP_EOL);
|
||||
exit(1);
|
||||
}
|
24
tests/pure_runner.php
Normal file
24
tests/pure_runner.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
use RTEX\Classes\InstructionBuilder;
|
||||
use RTEX\Engine;
|
||||
use RTEX\Objects\Program;
|
||||
|
||||
require 'ncc';
|
||||
|
||||
import('net.nosial.rtex', 'latest');
|
||||
|
||||
$program = new Program();
|
||||
|
||||
$program->getMain()->addInstruction(InstructionBuilder::set('foo', 'bar'));
|
||||
$program->getMain()->addInstruction(InstructionBuilder::set('bar', 500));
|
||||
|
||||
$program->getMain()->addInstruction(InstructionBuilder::set('results',
|
||||
InstructionBuilder::sum(
|
||||
500,
|
||||
InstructionBuilder::get('bar')
|
||||
)
|
||||
));
|
||||
|
||||
$engine = new Engine($program);
|
||||
$engine->run();
|
35
tests/script_builder.php
Normal file
35
tests/script_builder.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
use RTEX\Classes\InstructionBuilder;
|
||||
use RTEX\Objects\Program;
|
||||
|
||||
require('ncc');
|
||||
import('net.nosial.rtex', 'latest');
|
||||
|
||||
$program = new Program();
|
||||
|
||||
$program->getMain()->addInstruction(InstructionBuilder::abs(-2));
|
||||
$program->getMain()->addInstruction(InstructionBuilder::div(2, 2));
|
||||
$program->getMain()->addInstruction(InstructionBuilder::floor(2.5));
|
||||
$program->getMain()->addInstruction(InstructionBuilder::mod(2, 2));
|
||||
$program->getMain()->addInstruction(InstructionBuilder::mul(2, 2));
|
||||
$program->getMain()->addInstruction(InstructionBuilder::pow(2, 2));
|
||||
$program->getMain()->addInstruction(InstructionBuilder::round(2.5));
|
||||
$program->getMain()->addInstruction(InstructionBuilder::sqrt(2));
|
||||
$program->getMain()->addInstruction(InstructionBuilder::sub(2, 2));
|
||||
$program->getMain()->addInstruction(InstructionBuilder::sum(2, 2));
|
||||
|
||||
$program->getMain()->addInstruction(InstructionBuilder::array_set(["test"=>["foo"=>"bar"]], 'test.foo', 'baz'));
|
||||
$program->getMain()->addInstruction(InstructionBuilder::array_get(["test"=>["foo"=>"bar"]], 'test.foo'));
|
||||
$program->getMain()->addInstruction(InstructionBuilder::set('test', 'foo'));
|
||||
$program->getMain()->addInstruction(InstructionBuilder::get('test'));
|
||||
|
||||
$program->getMain()->addInstruction(InstructionBuilder::eq(2, 2));
|
||||
$program->getMain()->addInstruction(InstructionBuilder::gt(2, 2));
|
||||
$program->getMain()->addInstruction(InstructionBuilder::gte(2, 2));
|
||||
$program->getMain()->addInstruction(InstructionBuilder::lt(2, 2));
|
||||
$program->getMain()->addInstruction(InstructionBuilder::lte(2, 2));
|
||||
$program->getMain()->addInstruction(InstructionBuilder::neq(2, 2));
|
||||
|
||||
print(json_encode($program->toArray(), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL);
|
||||
print((string)$program->getMain() . PHP_EOL);
|
72
tests/syntax_parse.php
Normal file
72
tests/syntax_parse.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
function parseSyntax($syntax) {
|
||||
// Initialize an empty array to hold the parsed instructions
|
||||
$instructions = array();
|
||||
|
||||
// Split the syntax into an array of tokens
|
||||
$tokens = preg_split('/[\s,()]+/', $syntax, -1, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
// Get the type of instruction
|
||||
$type = array_shift($tokens);
|
||||
$instructions['type'] = $type;
|
||||
|
||||
// Initialize an empty array to hold the instruction parameters
|
||||
$params = array();
|
||||
|
||||
// Loop through the tokens
|
||||
while (!empty($tokens)) {
|
||||
// Get the next token
|
||||
$token = array_shift($tokens);
|
||||
|
||||
// Check if the token is a key
|
||||
if (preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $token) && !empty($tokens) && $tokens[0] == ':') {
|
||||
// Remove the colon
|
||||
array_shift($tokens);
|
||||
|
||||
// Get the value of the key-value pair
|
||||
$value = array_shift($tokens);
|
||||
|
||||
// Check if the value is a string
|
||||
if (preg_match('/^["\'].*["\']$/', $value)) {
|
||||
// Strip the quotes from the string value
|
||||
$value = substr($value, 1, -1);
|
||||
}
|
||||
|
||||
// Add the key-value pair to the instruction parameters
|
||||
$params[$token] = $value;
|
||||
} else {
|
||||
// The token is not a key-value pair.
|
||||
// Check if the token is a value for the previous key.
|
||||
$last_key = array_key_last($params);
|
||||
if ($last_key !== NULL) {
|
||||
// The token is a value for the previous key.
|
||||
// Add it to the instruction parameters as an array.
|
||||
$params[$last_key] = array($params[$last_key], $token);
|
||||
} else {
|
||||
// The token is not a value for the previous key.
|
||||
// Add it as a separate element to the instruction parameters.
|
||||
$params[] = $token;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Add the instruction parameters to the instructions array
|
||||
$instructions['_'] = $params;
|
||||
|
||||
// Return the parsed instructions
|
||||
return $instructions;
|
||||
}
|
||||
|
||||
$syntax = 'equals(1, 2)';
|
||||
$instructions = parseSyntax($syntax);
|
||||
print_r($instructions);
|
||||
|
||||
$syntax = 'equals(1, get("foo"))';
|
||||
$instructions = parseSyntax($syntax);
|
||||
print_r($instructions);
|
||||
|
||||
$syntax = 'invoke(namespace: "std", method: "print", continue_on_error: false, params: {"value":"Hello World","second_value":{"type":"get","_":"foo"}})';
|
||||
$instructions = parseSyntax($syntax);
|
||||
print_r($instructions);
|
Loading…
Add table
Reference in a new issue