diff --git a/.idea/php.xml b/.idea/php.xml
index e769432..2f2d64b 100644
--- a/.idea/php.xml
+++ b/.idea/php.xml
@@ -15,6 +15,8 @@
+
+
diff --git a/.idea/webResources.xml b/.idea/webResources.xml
index 6f8e6ec..cf25a90 100644
--- a/.idea/webResources.xml
+++ b/.idea/webResources.xml
@@ -6,6 +6,7 @@
+
diff --git a/InstructionInterface.png b/InstructionInterface.png
new file mode 100644
index 0000000..778ec23
Binary files /dev/null and b/InstructionInterface.png differ
diff --git a/README.md b/README.md
index 9df26f1..7cbbc02 100644
--- a/README.md
+++ b/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
+
+
+* [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)
+
+
+-----------------------------------------------------------------------------
+
## 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
diff --git a/config_example.yaml b/config_example.yaml
new file mode 100644
index 0000000..5a7010b
--- /dev/null
+++ b/config_example.yaml
@@ -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
\ No newline at end of file
diff --git a/instructions/get_var.json b/instructions/get_var.json
deleted file mode 100644
index 928e166..0000000
--- a/instructions/get_var.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "type": "get",
- "_": {
- "variable": "foo"
- }
-}
\ No newline at end of file
diff --git a/instructions/set_var.json b/instructions/set_var.json
deleted file mode 100644
index 83b586e..0000000
--- a/instructions/set_var.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "type": "set",
- "_": {
- "variable": "foo",
- "value": "bar"
- }
-}
\ No newline at end of file
diff --git a/project.json b/project.json
index 1ba78bb..7241b47 100644
--- a/project.json
+++ b/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": [
diff --git a/src/RTEX/Abstracts/InstructionType.php b/src/RTEX/Abstracts/InstructionType.php
index 00c5548..27a5589 100644
--- a/src/RTEX/Abstracts/InstructionType.php
+++ b/src/RTEX/Abstracts/InstructionType.php
@@ -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,
];
}
\ No newline at end of file
diff --git a/src/RTEX/Classes/RedisHookInstance.php b/src/RTEX/Classes/RedisHookInstance.php
new file mode 100644
index 0000000..a6cda24
--- /dev/null
+++ b/src/RTEX/Classes/RedisHookInstance.php
@@ -0,0 +1,27 @@
+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
+ ]);
+ }
+ }
+ }
\ No newline at end of file
diff --git a/src/RTEX/Classes/Utilities.php b/src/RTEX/Classes/Utilities.php
index bcbbd86..6df52dd 100644
--- a/src/RTEX/Classes/Utilities.php
+++ b/src/RTEX/Classes/Utilities.php
@@ -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();
diff --git a/src/RTEX/Engine.php b/src/RTEX/Engine.php
index 58ab570..ab4f676 100644
--- a/src/RTEX/Engine.php
+++ b/src/RTEX/Engine.php
@@ -1,13 +1,14 @@
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;
+ }
}
\ No newline at end of file
diff --git a/src/RTEX/Exceptions/Core/MalformedInstructionException.php b/src/RTEX/Exceptions/Core/MalformedInstructionException.php
deleted file mode 100644
index 361b92f..0000000
--- a/src/RTEX/Exceptions/Core/MalformedInstructionException.php
+++ /dev/null
@@ -1,16 +0,0 @@
- 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;
+ }
\ No newline at end of file
diff --git a/src/RTEX/Interfaces/MethodInterface.php b/src/RTEX/Interfaces/MethodInterface.php
new file mode 100644
index 0000000..daccda0
--- /dev/null
+++ b/src/RTEX/Interfaces/MethodInterface.php
@@ -0,0 +1,18 @@
+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;
+ }
+ }
\ No newline at end of file
diff --git a/src/RTEX/Objects/Engine/Configuration/RedisHook.php b/src/RTEX/Objects/Engine/Configuration/RedisHook.php
new file mode 100644
index 0000000..6f1c07e
--- /dev/null
+++ b/src/RTEX/Objects/Engine/Configuration/RedisHook.php
@@ -0,0 +1,172 @@
+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;
+ }
+ }
\ No newline at end of file
diff --git a/src/RTEX/Objects/Engine/Configuration/Runtime.php b/src/RTEX/Objects/Engine/Configuration/Runtime.php
new file mode 100644
index 0000000..d16b51d
--- /dev/null
+++ b/src/RTEX/Objects/Engine/Configuration/Runtime.php
@@ -0,0 +1,211 @@
+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]);
+ }
+
+ }
\ No newline at end of file
diff --git a/src/RTEX/Objects/Environment.php b/src/RTEX/Objects/Engine/Environment.php
similarity index 64%
rename from src/RTEX/Objects/Environment.php
rename to src/RTEX/Objects/Engine/Environment.php
index 5fd9535..d683d93 100644
--- a/src/RTEX/Objects/Environment.php
+++ b/src/RTEX/Objects/Engine/Environment.php
@@ -1,8 +1,11 @@
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
*
diff --git a/src/RTEX/Objects/Program/Script.php b/src/RTEX/Objects/Program/Script.php
index 1b185fa..6d9eead 100644
--- a/src/RTEX/Objects/Program/Script.php
+++ b/src/RTEX/Objects/Program/Script.php
@@ -1,10 +1,11 @@
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);
+ }
}
\ No newline at end of file
diff --git a/src/RTEX/RTEX.php b/src/RTEX/RTEX.php
index 5c63be0..199f79d 100644
--- a/src/RTEX/RTEX.php
+++ b/src/RTEX/RTEX.php
@@ -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);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/RTEX/bin/main b/src/RTEX/bin/main
new file mode 100644
index 0000000..890d659
--- /dev/null
+++ b/src/RTEX/bin/main
@@ -0,0 +1,14 @@
+getMessage() . PHP_EOL);
+ exit(1);
+ }
diff --git a/tests/pure_runner.php b/tests/pure_runner.php
new file mode 100644
index 0000000..a87d105
--- /dev/null
+++ b/tests/pure_runner.php
@@ -0,0 +1,24 @@
+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();
\ No newline at end of file
diff --git a/tests/script_builder.php b/tests/script_builder.php
new file mode 100644
index 0000000..71d0e2b
--- /dev/null
+++ b/tests/script_builder.php
@@ -0,0 +1,35 @@
+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);
\ No newline at end of file
diff --git a/tests/syntax_parse.php b/tests/syntax_parse.php
new file mode 100644
index 0000000..bd03089
--- /dev/null
+++ b/tests/syntax_parse.php
@@ -0,0 +1,72 @@
+