Minor changes

This commit is contained in:
Netkas 2023-03-05 13:57:57 -05:00
parent 05cc358eea
commit e9d033044c
27 changed files with 949 additions and 94 deletions

2
.idea/php.xml generated
View file

@ -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" />

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

View file

@ -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
View 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

View file

@ -1,6 +0,0 @@
{
"type": "get",
"_": {
"variable": "foo"
}
}

View file

@ -1,7 +0,0 @@
{
"type": "set",
"_": {
"variable": "foo",
"value": "bar"
}
}

View file

@ -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": [

View file

@ -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,
];
}

View 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
]);
}
}
}

View file

@ -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();

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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");
}
}

View 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;
}

View 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);
}

View 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;
}
}

View 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;
}
}

View 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]);
}
}

View file

@ -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
*

View file

@ -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);
}
}

View file

@ -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
View 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
View 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
View 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
View 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);