有什么想法?批评?饼干?
我对设计的评论更感兴趣,但与任何评论一样,一切都是公平的游戏。代码很多,所以我删除了注释,一些空格和一个或两个接口。您可以在github上查看完整代码和测试。
示例
$source = "5,2,10,1!!>>,[>>,]<<[[-<+<]>[>[>>]<[.[-]<[[>>+<<-]<]>>]>]<<]";
$bf = new \Brainfart\Brainfart();
$output = $bf->run($source);
$output
将是:array (size=4)
0 => int 1
1 => int 2
2 => int 5
3 => int 10
解释器
namespace Brainfart;
use Brainfart\VM\Output;
use Brainfart\Parser\Parser;
use Brainfart\VM\VM;
class Brainfart {
private $optimize = true;
private $vm;
private $parser;
private $output;
public function __construct($loopLimit = 100, $optimize = true) {
$this->vm = new VM(array(), $loopLimit);
$this->parser = new Parser();
$this->setOptimize($optimize);
}
public function setOptimize($optimize = true) {
$this->optimize = ($optimize === true);
return $this;
}
public function run($source, $input = "", $fetchMode = Output::FETCH_ARRAY) {
$this->parser->loadSource($source);
if ($this->parser->getFlag("string_output") === true) $fetchMode = Output::FETCH_STRING;
$appLoop = $this->parser->parse($this->optimize);
$appInput = $this->parser->getInput();
if (!empty($appInput)) $input = $appInput;
$this->vm->init($input);
$appLoop->execute($this->vm);
return $this->vm->getOutput()->fetch($fetchMode);
}
}
虚拟机
VM
namespace Brainfart\VM;
class VM {
private $input;
private $output;
private $memory;
private $loopLimit = 0;
public function __construct($input = array(), $loopLimit = 0) {
$this->init($input, $loopLimit);
}
public function init($input = array(), $loopLimit = 0) {
$this->input = new Input($input);
$this->output = new Output();
$this->memory = new Memory();
$this->setLoopLimit($loopLimit);
return $this;
}
public function getInput() { return $this->input; }
public function getOutput() { return $this->output; }
public function getMemory() { return $this->memory; }
public function setLoopLimit($loopLimit = 100) {
$this->loopLimit = is_numeric($loopLimit) && $loopLimit > 0 ? (int) $loopLimit : 0;
return $this;
}
public function getLoopLimit() { return $this->loopLimit; }
}
内存
namespace Brainfart\VM;
class Memory {
private $memory = array();
private $pointer = 0;
public function move($value) {
$this->pointer += is_numeric($value) ? (int) $value : 0;
return $this;
}
public function fetch() {
return isset($this->memory[$this->pointer]) ? $this->memory[$this->pointer] : 0;
}
public function store($value) {
$this->memory[$this->pointer] = $this->fetch() + (is_numeric($value) ? (int) $value : 0);
return $this;
}
}
输入
namespace Brainfart\VM;
class Input {
private $input = array();
public function __construct($input) {
$this->store($input);
}
public function store($input) {
if (is_scalar($input)) $input = str_split(trim($input));
if (!is_array($input)) throw new \InvalidArgumentException();
foreach ($input as $key => $value) $input[$key] = is_numeric($value) ? (int) $value : ord($value);
$this->input = $input;
return $this;
}
public function fetch() {
return
!(empty($this->input))
? array_shift($this->input)
: 0;
}
}
输出
namespace Brainfart\VM;
class Output {
const FETCH_ARRAY = 0;
const FETCH_STRING = 1;
private $output = array();
public function store($value) {
$this->output[] = $value;
return $this;
}
public function fetch($fetchMode = self::FETCH_ARRAY) {
return
($fetchMode === self::FETCH_STRING)
? implode("", array_map("chr", $this->output))
: $this->output;
}
}
虚拟机操作
ChangeOperation
namespace Brainfart\Operations;
use Brainfart\VM\VM;
class ChangeOperation implements OperationInterface, MutableInterface {
use MutableTrait;
public function execute(VM $vm) {
$vm->getMemory()->store($this->getValue());
}
}
InputOperation
namespace Brainfart\Operations;
use Brainfart\VM\VM;
class InputOperation implements OperationInterface {
public function execute(VM $vm) {
$vm->getMemory()->store($vm->getInput()->fetch());
}
}
LoopOperation
namespace Brainfart\Operations;
use Brainfart\VM\VM;
class LoopOperation implements OperationInterface {
private $master = false;
private $operations = array();
public function __construct(array $operations, $master = false) {
$this->setOperations($operations)->setMaster($master);
}
public function setOperations(array $operations) {
$this->operations = $operations;
return $this;
}
public function getOperations() { return $this->operations; }
public function setMaster($master) {
$this->master = ($master === true);
return $this;
}
public function getMaster() { return $this->master; }
public function execute(VM $vm) {
$operations = $this->getOperations();
$limit = $vm->getLoopLimit();
$i = 0;
while (
$this->getMaster() // master loop is the whole app, runs regardless of memory value
|| ($vm->getMemory()->fetch() != 0)
) {
foreach ($operations as $operation)
$operation->execute($vm);
if ($this->getMaster()) break;
$i++;
if ($limit > 0 && $limit < $i) throw new \RuntimeException("Limit of {$limit} operations per loop reached.");
}
}
}
MoveOperation
namespace Brainfart\Operations;
use Brainfart\VM\VM;
class MoveOperation implements OperationInterface, MutableInterface {
use MutableTrait;
public function execute(VM $vm) {
$vm->getMemory()->move($this->getValue());
}
}
OutputOperation
namespace Brainfart\Operations;
use Brainfart\VM\VM;
class OutputOperation implements OperationInterface {
public function execute(VM $vm) {
$vm->getOutput()->store($vm->getMemory()->fetch());
}
}
SleepOperation
这就是你的转动方式好笑话变成坏话。
namespace Brainfart\Operations;
use Brainfart\VM\VM;
class SleepOperation implements OperationInterface {
public function execute(VM $vm) { sleep($vm->getMemory()->fetch()); }
}
MutableTrait
namespace Brainfart\Operations;
trait MutableTrait {
private $value;
public function __construct($value) { $this->setValue($value); }
public function setValue($value) {
$this->value = is_numeric($value) ? (int) $value : 0;
return $this;
}
public function getValue() { return $this->value; }
public function combine(MutableInterface $operation) {
$class = get_class($this);
if ($operation instanceof $class) {
$this->setValue($this->getValue() + $operation->getValue());
return $this;
}
return false;
}
}
>解析器
Loader
namespace Brainfart\Parser;
class Loader {
private $input;
private $source = "";
private $flags = array();
public function __construct($source = null) {
if (!is_null($source)) $this->loadSource($source);
$this->setFlag("no_optimization", false)->setFlag("string_output", false);
}
public function loadSource($source) {
if (is_file($source)) $source = @ file_get_contents($source);
if (!is_string($source)) throw new \InvalidArgumentException();
$source = $this->prepare($source);
$source = $this->skintoad($source);
$source = $this->cleanup($source);
return $this->source = $source;
}
public function getSource() { return $this->source; }
public function getInput() { return $this->input; }
public function getFlag($flag = null) {
if (is_null($flag) || !is_scalar($flag)) return $this->flags;
$flag = strtolower(trim($flag));
return isset($this->flags[$flag]) ? $this->flags[$flag] : null;
}
protected function setFlag($flag, $value = null) {
$flag = (!is_scalar($flag)) ? "unknown" : strtolower(trim($flag));
if (!is_null($value)) $value = ($value === true);
$this->flags[$flag] = $value;
return $this;
}
public function setInput($input) {
if (!is_scalar($input)) throw new \InvalidArgumentException();
$input = (string) $input;
$input = trim($input, ", ");
$input = explode(",", $input);
$input = array_map("trim", $input);
$this->input = $input;
return $this;
}
private function prepare($source) {
$flags = array("@@" => "no_optimization", "$$" => "string_output");
foreach ($flags as $operator => $flag) {
if (strpos($source, $operator) !== false) {
$this->setFlag($flag, true);
$source = str_replace($operator, "", $source);
}
}
$pos = strpos($source, "!!");
if ($pos !== false) {
$input = substr($source, 0, $pos);
$source = substr($source, $pos + 2);
$this->setInput($input);
}
return preg_replace('/\s+/', "", strtolower($source));
}
private function skintoad($source) {
if (!preg_match_all('/:(.*?);/', $source, $matches)) return $source;
foreach ($matches[0] as $match) {
$source = str_replace($match, "", $source);
$match = trim($match, ":;");
if (preg_match('/^[a-zA-Z0-9_]*/', $match, $identifier)) {
$identifier = $identifier[0];
$sequence = str_replace($identifier, "", $match);
$source = str_replace($identifier, $sequence, $source);
}
}
return $source;
}
private function cleanup($source) {
return preg_replace('/[^<|>|\-|\+|\.|\~|\,|\]|\[]/', "", $source);
}
}
解析器
namespace Brainfart\Parser;
use Brainfart\Operations\LoopOperation;
use Brainfart\Operations\SleepOperation;
use Brainfart\Operations\ChangeOperation;
use Brainfart\Operations\MoveOperation;
use Brainfart\Operations\InputOperation;
use Brainfart\Operations\OutputOperation;
use Brainfart\Operations\MutableInterface;
class Parser extends Loader {
private $operations;
public function parse($optimize = true) {
if ($this->getFlag("no_optimization") === true) $optimize = false;
$operations = $this->tokenize($this->getSource(), $optimize);
return $this->operations = new LoopOperation($operations, true);
}
public function getOperations() { return $this->operations; }
private function tokenize($source, $optimize) {
$result = array();
$optimize = $optimize === true;
$length = strlen($source);
for ($i = 0; $i < $length; $i++) {
$token = isset($source[$i]) ? $source[$i] : false;
if (!$token) break;
if ($token == "[") {
$loopEnd = $this->findLoopEnd(substr($source, $i + 1));
if (!$loopEnd) throw new \LogicException("Neverending loop.");
$loopSource = substr($source, $i + 1, $loopEnd);
$loopTokens = $this->tokenize($loopSource, $optimize);
$operation = new LoopOperation($loopTokens);
$i += $loopEnd + 1;
} else {
$operation = $this->getOperation($token);
if (!$operation) continue;
if ($optimize && ($operation instanceof MutableInterface)) {
$index = count($result) - 1;
$previous = isset($result[$index]) ? $result[$index] : false;
$combined = ($previous instanceof MutableInterface) ? $previous->combine($operation) : false;
if ($combined) {
$result[$index] = $combined;
continue;
}
}
}
$result[] = $operation;
}
return $result;
}
private function findLoopEnd($source) {
$posCloseBracket = strpos($source, "]");
$posOpenBracket = strpos($source, "[");
if ($posOpenBracket === false || $posCloseBracket < $posOpenBracket) return $posCloseBracket;
$source[$posOpenBracket] = $source[$posCloseBracket] = "_";
return $this->findLoopEnd($source);
}
private function getOperation($token) {
$operation = false;
switch ($token) {
case ">":
$operation = new MoveOperation(1);
break;
case "<":
$operation = new MoveOperation(-1);
break;
case "+":
$operation = new ChangeOperation(1);
break;
case "-":
$operation = new ChangeOperation(-1);
break;
case ".":
$operation = new OutputOperation();
break;
case ",":
$operation = new InputOperation();
break;
case "~":
$operation = new SleepOperation();
}
return $operation;
}
}
#1 楼
因此,对于初学者来说,让操作变得懒惰(是的,令人惊讶的是,Haskeller说让它变得懒惰)。:
class LoopOperation implements OperationInterface {
private $master = false;
private $operations = array();
public function __construct(array $operations, $master = false) {
$this->setOperations($operations)->setMaster($master);
}
然后,您可以在解释公共数组时将其添加到操作中,或者创建addOperation方法(这两种方法都可以构造LoopOperation和然后随着解析的继续,向其中添加单独的操作。
然后是一些解析器组合器,它们的错误monad为ya(我不知道PHP,所以请多多包涵):
class LoopOperation implements OperationInterface {
public $master = false;
public $operations = array();
仔细研究,您会发现添加了所有操作的循环操作对象在循环解释完成后会丢失,因此,我建议使用而不是我使用线程的方式所有功能的源代码,实际上创建了一个具有源代码和VM的结构,因此这些函数可以解析源代码并将操作推送到VM上,结果代码将使您能够o编写如下内容:
abstract class M
{
abstract public function then($f); // Formally this is 'bind', the f is the function to bind to the action
abstract public function otherwise($f); // Formally this would be an application of an `alternative` or monoid
public $a; // The a is for 'action'
}
class Success extends M
{
public function __construct($a) {
$this->a = $a;
}
public function then($f) {
return $f($this->a);
}
public function otherwise($f) {
return $this;
}
}
class Failure extends M
{
public function __construct($a) {
$this->a = $a;
}
public function then($f) {
return $this;
}
public function otherwise($f) {
return new Success($this->a)->then($f);
}
}
public function pop($a) {
return new Success(substr($a, 1));
}
public function charIs($char) {
return function($a) use($char) {
if ($a[0] == $char) {
return new Success($a);
}
return new Failure($a);
}
}
public function charExists($char) {
return function($a) use($char) {
if (strpos($a, $char) === false) {
return new Failure($a);
}
return new Success($a);
}
}
public function addLoopOperation($operation) {
return function($a) user ($operation) {
$operation->addOperation(strpos($a, 0, 1));
return new Success($a);
}
}
public function throwException($ex) {
return function($a) use ($ex) {
throw $ex;
}
}
public function addLoop($operation = new LoopOperation()) {
return function($a) use (&$operation) {
return new Success($a)
->then(charExists("]"))->otherwise(throwException(new LogicException("Neverending loop."))
->then(addLoopOperation($operation))
->then(pop)
->then(charIs("]"))->then(pop)->otherwise(addLoop($operation));
}
// Because it's recursed the way it is, it will continue adding operations until it hits the char "]"
}