Notes about the architecture or the Forth Interpreter in PHP 5.3
what will it?
I often have to conduct interviews, hiring PHP programmers. 2-3 people a day is quite normal, although on the verge of endurance.
All candidates are different, some just gorgeous, someone is worse. But those who are worse, always the same error.
First they are not interested in the development of the language in which they write, and the question "what is new in PHP 5.3" puts them into a dead end, and even offer to speculate on "what would You add To future versions of the language" is just scary.
Secondly they categorically have no idea what PHP can be something other than "scripts for sites". This is especially sad in light of the fact that the work they have to do not sites, and even not likely sites.
And third candidates that are worse, hard to imagine the process of constructing the architecture of the program. Patterns know much, but as a pattern to lay down a coherent system, that it was not painfully a shame — this is hard.
Such candidates and is dedicated to this topic. With respect to their plight (and being a programmer is not easy) and hope that he will push them forward on the path of self-improvement.
What do we do?
Let's try to make the interpreter of the Fort on PHP!
The Fort has always attracted me, like a distant galaxy of antimatter. Stack words in the dictionary, reverse Polish notation... It just seems complicated but is actually very simple, but cool "illuminating," and broadens the mind.
Let's make our console app, no browsers and servers. Let's write it on PHP 5.3, trying to use the language 100%. And, of course, try to make initially so that came after us did not hang out in our code the first day.
application Architecture
From the very beginning we agreed that I write on PHP 5.3 and this greatly facilitates the work of system architect. Adopt several principles, which will be followed during the development:
-
the
- To separate the levels of the app using namespaces the
- Each class name must uniquely specify its location in the file system the
- One class — one file the
- All classes are included only via the autoloader the
- All errors are handled by exceptions, exception classes obey the General rules
Take as the root namespace for our application \FORTH. The major classes of Fort machine put in \FORTH\SYSTEM, and exception will be placed in \FORTH\EXCEPTIONS, which itself is also divided into several, for example — \FORTH\EXCEPTIONS\SYSTEM exceptions that occur in the Fort system and, say, \FORTH\EXCEPTIONS\STACK for the exception related to the stack.
In accordance with the 2 principle of the architecture will be the namespace unambiguously converted to directories in FS, and class names to file names in these directories.
It turns out about such code:
the
<?php
$autoload = function ($class) {
$path = explode('\\', $class);
if ( 'FORTH' != array_shift($path) )
throw new \FORTH\EXCEPTIONS\SYSTEM\NamespaceIsWrong();
$filename = array_pop($path);
require __DIR__ . '/' .
implode('/', array_map('strtolower', $path)) . '/' .
$filename . '.php';
};
spl_autoload_register($autoload);
We use all modern possibilities of language: the function of the startup we have an anonymous (and really, why does she have a name?), register it using SPL, to avoid conflict with other functions startup (which of course in our case is unlikely, but is good form).
Take this code and put it in the file autoload.php at the root of the project.
Custom start
Before you write the real code Fort machine, we need an entry point. It will provide three files — forth.php will initialize the application and to implement the main cycle, and forth.bat and forth.sh will play a supporting role in helping to run our script from the command line in different operating systems.
Under Windows forth file.bat might look like this:
the
@echo off
SET PHP_PATH=Z:/usr/local/bin
%PHP_PATH%/php -q ./forth.php %1 > output.txt
type output.txt | more
pause
I am sure that readers of this topic you will improve and will be happy to create an analogue to run our application under sh/csh/bash or any other shell.
The application entry point, file forth.php leave until almost empty, fill it only two lines — the namespace Declaration and the initialization of the startup
the
namespace FORTH;
require __DIR__ . '/autoload.php';
Stack and queue commands
The time has come to implement the two main parts of our Fort machine: stack and queue of commands.
A stack is a basic concept of a classical Fort. On the stack we will store the number which will operate the machine, there are placed the results of the operations over them (words).
PHP stack LIFO well-implemented arrays and operations array_pop and array_push, so not to reinvent the wheel and will take advantage of these funds. Consider that in this implementation of a stack we can have only one, so make the stack class a singleton. We get code like this:
the
<?php
namespace FORTH\SYSTEM;
class Stack extends Singleton {
private $stack = array();
public function push($obj) {
array_push(
$this->stack,
$obj
);
}
public function pop() {
if ( $this->isEmpty() )
throw new \FORTH\EXCEPTIONS\STACK\StackIsEmpty();
return array_pop(
$this->stack
);
}
public function isEmpty() {
return empty($this->stack);
}
}
Let me tell you — where is the singleton? Where a private constructor, the usual getInstance () method?
It's simple — we carried all the major pattern in system the abstract class \FORTH\SYSTEM\Singleton. If You look closely at the code in this class, you will understand the basic idea — as LSB introduced in PHP 5.3 allows you to divide an abstract description of the pattern and its concrete implementation.
It is similar to using an array to organize the queue, only using the functions array_push and array_shift.
Dictionary
There comes a time dictionary. In the dictionary Fort the machine are stored the definitions of the words. For simplicity of implementation (and to exclude hard links in architecture) will store in the dictionary the following information: the word, the dimension of the operation (how many numbers from the top of the stack should be the word as arguments), the effect on the stack (how many numbers from the stack removes a word) and the code word that returns the word that would put on a stack.
The word will be presented in the form of an object of class \FORTH\SYSTEM\Word, and the dictionary — \FORTH\SYSTEM\Dictionary, the implementation of these classes you will find in the source code of the application.
The dictionary must have at least two public methods — one to look up a word in the dictionary and return it to us in case of successful search, and the second is to make a new word and add it to the dictionary.
It is worth noting that the class dictionary is also certainly needs to be implemented as a singleton, additionally, the dictionary is already at the start of the Fort-the machine must contain a minimum standard set of words, so in addition to direct class \FORTH\SYSTEM\Dictionary will need to implement the class StandartDictionary performing the initialization of the dictionary of standard words, and add the initialization of the standard dictionary at the entry point to the application.
To begin implementing the standard words on the four basic arithmetic operations, "DUP" — doubling the number on top of the stack, "SWAP" — a permutation of the two numbers from the stack and "." — output the numbers from the top of the stack. Special complexity is not, here is an example for adding two numbers:
the
/*
* Addition of the two numbers, putting the result into the stack
*/
$dict->addWord(
new Word(
a '+',
2,
2,
function ($a, $b) {
return (array)($a+$b);
}
)
);
Communicate with the outside world
It's time to transfer our application from outside some Fort program. For ease of implementation we assume that the program is in the file that is specified directly on the command line.
Sketching the following code in forth.php:
the
$data = \file_get_contents($argv[1]);
$parser = SYSTEM\Parser::getInstance();
$parser->loadRawData($data);
$dataForQueue = $parser->makeQueue();
$queue = SYSTEM\Queue::getInstance();
$queue- > loadArray($dataForQueue);
$stack = SYSTEM\Stack::getInstance();
$executor = SYSTEM\Executor::getInstance();
$executor- > setStack($stack);
$executor- > execute($queue);
It is obvious that we now need to implement a parser that will accept a string as an input, which is a Fort and take it apart into components — either numbers or words of the dictionary of the Fort. The output of the parser should return us an array with a near complete command queue for Fort car we will give the system the command queue. Well, the last line — in fact, the execution of commands from queue queue))
Nothing complicated in the code, "parser" no, our mission is simple — split the string on whitespace, and further processing. If we meet the number, then just put it in the queue, and if a character literal is to ask the dictionary whether it is word, and if so to queue the object-word.
Much more interesting code "executioner." The contractor in fact is the nucleus of the Fort machine that he operates with the stack and executes the word. We will give to him the object of our system stack, then load the command queue and ask the queue on the stack to execute.
The task of the executor is to determine what element of the queue: the number that needs to be put on the stack, or word that you want to perform. If the numbers are all simple, then the word requires somewhat more mental effort — you need to remove from the stack the specified number of digits, to form them into an array of arguments and call the code word, passing it these arguments. The result may need to be placed on the stack.
It looks like this:
the
$args = array();
for ( $i = 1; $i <= $word->getStackPopCount(); $i++ )
$args[] = $this->stack->pop();
$args = \array_slice($args, 0, $word->getOperandsCount());
$result = \call_user_func_array($word- > getCallback(), $args);
if ( !\is_null($result) ) {
foreach ( $result as $res ) {
$this->stack->push($res);
}
}
What happened?
As a result, we got a forth interpreter in PHP — Fort machine. Let code gaping big gaps (for example, we have not yet catch exception, completely missed the point with tests), though a lot can be improved, but this improvement will not be difficult, the main work is done.
For someone this topic, you may discover something new — I'll be glad.
Someone will read and say, "well, it's elementary, what's new." — fine.
And if someone knows how to do better, you could come to our team, we love and appreciate professionals who, in turn, love and appreciate the language in which to program.
I went to sleep -))
Source code
As of this writing, I laid out what turned out, in a specially created project on Google Code where you can get the source codes: code.google.com/p/php-forth/source/browse/trunk
Code licensed under GPL V. 3
bibliography
-
the
- ru.wikipedia.org/wiki/%D0%A4%D0%BE%D1%80%D1%82_%28%D1%8F%D0%B7%D1%8B%D0%BA_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%29 the
- ru.wikipedia.org/wiki/%D0%A1%D1%82%D0%B5%D0%BA the
- ru.wikipedia.org/wiki/%D0%9E%D0%B1%D1%80%D0%B0%D1%82%D0%BD%D0%B0%D1%8F_%D0%BF%D0%BE%D0%BB%D1%8C%D1%81%D0%BA%D0%B0%D1%8F_%D0%BD%D0%BE%D1%82%D0%B0%D1%86%D0%B8%D1%8F the
- Inspired me a Soviet book about the Fort, purple, such, for children of secondary school age, I do not remember neither the name nor the output...
Комментарии
Отправить комментарий