mirror of
https://github.com/peklaiho/madlisp.git
synced 2024-11-23 05:45:06 +00:00
add support for experimental safe-mode which disables some functionality
This commit is contained in:
parent
02e5176612
commit
35224bce8a
@ -6,14 +6,16 @@ class Evaller
|
|||||||
protected Tokenizer $tokenizer;
|
protected Tokenizer $tokenizer;
|
||||||
protected Reader $reader;
|
protected Reader $reader;
|
||||||
protected Printer $printer;
|
protected Printer $printer;
|
||||||
|
protected bool $safemode;
|
||||||
|
|
||||||
protected bool $debug = false;
|
protected bool $debug = false;
|
||||||
|
|
||||||
public function __construct(Tokenizer $tokenizer, Reader $reader, Printer $printer)
|
public function __construct(Tokenizer $tokenizer, Reader $reader, Printer $printer, bool $safemode)
|
||||||
{
|
{
|
||||||
$this->tokenizer = $tokenizer;
|
$this->tokenizer = $tokenizer;
|
||||||
$this->reader = $reader;
|
$this->reader = $reader;
|
||||||
$this->printer = $printer;
|
$this->printer = $printer;
|
||||||
|
$this->safemode = $safemode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function eval($ast, Env $env)
|
public function eval($ast, Env $env)
|
||||||
@ -116,7 +118,7 @@ class Evaller
|
|||||||
|
|
||||||
$ast = $astData[$astLength - 1];
|
$ast = $astData[$astLength - 1];
|
||||||
continue; // tco
|
continue; // tco
|
||||||
} elseif ($symbolName == 'env') {
|
} elseif (!$this->safemode && $symbolName == 'env') {
|
||||||
if ($astLength >= 2) {
|
if ($astLength >= 2) {
|
||||||
if (!($astData[1] instanceof Symbol)) {
|
if (!($astData[1] instanceof Symbol)) {
|
||||||
throw new MadLispException("first argument to env is not symbol");
|
throw new MadLispException("first argument to env is not symbol");
|
||||||
@ -126,7 +128,7 @@ class Evaller
|
|||||||
} else {
|
} else {
|
||||||
return $env;
|
return $env;
|
||||||
}
|
}
|
||||||
} elseif ($symbolName == 'eval') {
|
} elseif (!$this->safemode && $symbolName == 'eval') {
|
||||||
if ($astLength == 1) {
|
if ($astLength == 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -207,10 +209,13 @@ class Evaller
|
|||||||
$ast = $astData[2];
|
$ast = $astData[2];
|
||||||
$env = $newEnv;
|
$env = $newEnv;
|
||||||
continue; // tco
|
continue; // tco
|
||||||
} elseif ($symbolName == 'load') {
|
} elseif (!$this->safemode && $symbolName == 'load') {
|
||||||
// Load is here because we want to load into
|
// Load is here because we want to load into
|
||||||
// current $env which is hard otherwise.
|
// current $env which is hard otherwise.
|
||||||
|
|
||||||
|
// This is disabled now for safe-mode, but some
|
||||||
|
// use (maybe restricted) might need to be allowed.
|
||||||
|
|
||||||
if ($astLength != 2) {
|
if ($astLength != 2) {
|
||||||
throw new MadLispException("load requires exactly 1 argument");
|
throw new MadLispException("load requires exactly 1 argument");
|
||||||
}
|
}
|
||||||
|
110
src/Lib/Core.php
110
src/Lib/Core.php
@ -20,30 +20,36 @@ class Core implements ILib
|
|||||||
protected Reader $reader;
|
protected Reader $reader;
|
||||||
protected Printer $printer;
|
protected Printer $printer;
|
||||||
protected Evaller $evaller;
|
protected Evaller $evaller;
|
||||||
|
protected bool $safemode;
|
||||||
|
|
||||||
public function __construct(Tokenizer $tokenizer, Reader $reader, Printer $printer, Evaller $evaller)
|
public function __construct(Tokenizer $tokenizer, Reader $reader, Printer $printer, Evaller $evaller, bool $safemode)
|
||||||
{
|
{
|
||||||
$this->tokenizer = $tokenizer;
|
$this->tokenizer = $tokenizer;
|
||||||
$this->reader = $reader;
|
$this->reader = $reader;
|
||||||
$this->printer = $printer;
|
$this->printer = $printer;
|
||||||
$this->evaller = $evaller;
|
$this->evaller = $evaller;
|
||||||
|
$this->safemode = $safemode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function register(Env $env): void
|
public function register(Env $env): void
|
||||||
{
|
{
|
||||||
// Register special constants
|
// Register special constants
|
||||||
$env->set('__FILE__', null);
|
if (!$this->safemode) {
|
||||||
$env->set('__DIR__', null);
|
$env->set('__FILE__', null);
|
||||||
|
$env->set('__DIR__', null);
|
||||||
|
}
|
||||||
|
|
||||||
$env->set('debug', new CoreFunc('debug', 'Toggle debug mode.', 0, 0,
|
if (!$this->safemode) {
|
||||||
function () {
|
$env->set('debug', new CoreFunc('debug', 'Toggle debug mode.', 0, 0,
|
||||||
$val = !$this->evaller->getDebug();
|
function () {
|
||||||
$this->evaller->setDebug($val);
|
$val = !$this->evaller->getDebug();
|
||||||
return $val;
|
$this->evaller->setDebug($val);
|
||||||
}
|
return $val;
|
||||||
));
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
$env->set('doc', new CoreFunc('doc', 'Get or set documentation for a function.', 1, 2,
|
$env->set('doc', new CoreFunc('doc', 'Get or set documentation string for a function.', 1, 2,
|
||||||
function (Func $a, ?string $str = null) {
|
function (Func $a, ?string $str = null) {
|
||||||
if (func_num_args() == 1) {
|
if (func_num_args() == 1) {
|
||||||
return $a->getDoc();
|
return $a->getDoc();
|
||||||
@ -63,53 +69,63 @@ class Core implements ILib
|
|||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
$env->set('meta', new CoreFunc('meta', 'Read meta information of an entity.', 2, 2,
|
if (!$this->safemode) {
|
||||||
function ($obj, $attribute) {
|
$env->set('meta', new CoreFunc('meta', 'Read meta information of an entity.', 2, 2,
|
||||||
if ($obj instanceof Env) {
|
function ($obj, $attribute) {
|
||||||
if ($attribute == 'name') {
|
if ($obj instanceof Env) {
|
||||||
return $obj->getFullName();
|
if ($attribute == 'name') {
|
||||||
} elseif ($attribute == 'parent') {
|
return $obj->getFullName();
|
||||||
return $obj->getParent();
|
} elseif ($attribute == 'parent') {
|
||||||
|
return $obj->getParent();
|
||||||
|
} else {
|
||||||
|
throw new MadLispException('unknown attribute for meta');
|
||||||
|
}
|
||||||
|
} elseif ($obj instanceof UserFunc) {
|
||||||
|
if ($attribute == 'args') {
|
||||||
|
return $obj->getBindings();
|
||||||
|
} elseif ($attribute == 'body') {
|
||||||
|
return $obj->getAst();
|
||||||
|
} elseif ($attribute == 'code') {
|
||||||
|
return new MList([new Symbol('fn'), $obj->getBindings(), $obj->getAst()]);
|
||||||
|
} else {
|
||||||
|
throw new MadLispException('unknown attribute for meta');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new MadLispException('unknown attribute for meta');
|
throw new MadLispException('unknown entity for meta');
|
||||||
}
|
}
|
||||||
} elseif ($obj instanceof UserFunc) {
|
|
||||||
if ($attribute == 'args') {
|
|
||||||
return $obj->getBindings();
|
|
||||||
} elseif ($attribute == 'body') {
|
|
||||||
return $obj->getAst();
|
|
||||||
} elseif ($attribute == 'code') {
|
|
||||||
return new MList([new Symbol('fn'), $obj->getBindings(), $obj->getAst()]);
|
|
||||||
} else {
|
|
||||||
throw new MadLispException('unknown attribute for meta');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new MadLispException('unknown entity for meta');
|
|
||||||
}
|
}
|
||||||
}
|
));
|
||||||
));
|
}
|
||||||
|
|
||||||
$env->set('read', new CoreFunc('read', 'Read string as code.', 1, 1,
|
if (!$this->safemode) {
|
||||||
fn (string $a) => $this->reader->read($this->tokenizer->tokenize($a))
|
$env->set('read', new CoreFunc('read', 'Read string as code.', 1, 1,
|
||||||
));
|
fn (string $a) => $this->reader->read($this->tokenizer->tokenize($a))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
$env->set('print', new CoreFunc('print', 'Print argument. Give second argument as true to show strings in readable format.', 1, 2,
|
if (!$this->safemode) {
|
||||||
function ($a, bool $readable = false) {
|
$env->set('print', new CoreFunc('print', 'Print argument. Give second argument as true to show strings in readable format.', 1, 2,
|
||||||
$this->printer->print($a, $readable);
|
function ($a, bool $readable = false) {
|
||||||
return null;
|
$this->printer->print($a, $readable);
|
||||||
}
|
return null;
|
||||||
));
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is allowed in safe-mode, because the evaluation should be wrapped in a try-catch in embedded use.
|
||||||
$env->set('error', new CoreFunc('error', 'Throw an exception using argument (string) as message.', 1, 1,
|
$env->set('error', new CoreFunc('error', 'Throw an exception using argument (string) as message.', 1, 1,
|
||||||
function (string $error) {
|
function (string $error) {
|
||||||
|
// We should probably use another exception type to distinguish user-thrown errors from built-in errors.
|
||||||
throw new MadLispException($error);
|
throw new MadLispException($error);
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
$env->set('exit', new CoreFunc('exit', 'Terminate the script with given exit code.', 0, 1,
|
if (!$this->safemode) {
|
||||||
function (int $status = 0) {
|
$env->set('exit', new CoreFunc('exit', 'Terminate the script with given exit code.', 0, 1,
|
||||||
exit($status);
|
function (int $status = 0) {
|
||||||
}
|
exit($status);
|
||||||
));
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,25 +3,22 @@ namespace MadLisp;
|
|||||||
|
|
||||||
class LispFactory
|
class LispFactory
|
||||||
{
|
{
|
||||||
public function make(array $coreLibs = [], array $userLibs = []): Lisp
|
public function make(bool $safemode = false): Lisp
|
||||||
{
|
{
|
||||||
$tokenizer = new Tokenizer();
|
$tokenizer = new Tokenizer();
|
||||||
$reader = new Reader();
|
$reader = new Reader();
|
||||||
$printer = new Printer();
|
$printer = new Printer();
|
||||||
$eval = new Evaller($tokenizer, $reader, $printer);
|
$eval = new Evaller($tokenizer, $reader, $printer, $safemode);
|
||||||
|
|
||||||
// Root environment
|
// Root environment
|
||||||
$env = new Env('root');
|
$env = new Env('root');
|
||||||
|
|
||||||
// Register core functions
|
// Register core functions
|
||||||
(new Lib\Core($tokenizer, $reader, $printer, $eval))->register($env);
|
(new Lib\Core($tokenizer, $reader, $printer, $eval, $safemode))->register($env);
|
||||||
|
|
||||||
// Register core libraries
|
// Register core libraries
|
||||||
(new Lib\Collections())->register($env);
|
(new Lib\Collections())->register($env);
|
||||||
(new Lib\Compare())->register($env);
|
(new Lib\Compare())->register($env);
|
||||||
(new Lib\Database())->register($env);
|
|
||||||
(new Lib\Http())->register($env);
|
|
||||||
(new Lib\IO())->register($env);
|
|
||||||
(new Lib\Json())->register($env);
|
(new Lib\Json())->register($env);
|
||||||
(new Lib\Math())->register($env);
|
(new Lib\Math())->register($env);
|
||||||
(new Lib\Regex())->register($env);
|
(new Lib\Regex())->register($env);
|
||||||
@ -29,19 +26,16 @@ class LispFactory
|
|||||||
(new Lib\Time())->register($env);
|
(new Lib\Time())->register($env);
|
||||||
(new Lib\Types())->register($env);
|
(new Lib\Types())->register($env);
|
||||||
|
|
||||||
// Register additional libs for root env
|
// Register unsafe libraries if not in safemode
|
||||||
foreach ($coreLibs as $lib) {
|
if (!$safemode) {
|
||||||
$lib->register($env);
|
(new Lib\Database())->register($env);
|
||||||
|
(new Lib\Http())->register($env);
|
||||||
|
(new Lib\IO())->register($env);
|
||||||
}
|
}
|
||||||
|
|
||||||
// User environment
|
// User environment
|
||||||
$env = new Env('user', $env);
|
$env = new Env('user', $env);
|
||||||
|
|
||||||
// Register additional libs for user env
|
|
||||||
foreach ($userLibs as $lib) {
|
|
||||||
$lib->register($env);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Lisp($tokenizer, $reader, $eval, $printer, $env);
|
return new Lisp($tokenizer, $reader, $eval, $printer, $env);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user