78 lines
1.8 KiB
PHP
78 lines
1.8 KiB
PHP
|
<?php
|
||
|
|
||
|
namespace Program;
|
||
|
|
||
|
use RuntimeException;
|
||
|
|
||
|
class ProgramParser
|
||
|
{
|
||
|
public function parseFile(string $filename): Program
|
||
|
{
|
||
|
$lines = explode(PHP_EOL, file_get_contents($filename));
|
||
|
$lines = array_filter($lines);
|
||
|
|
||
|
return $this->parse($lines);
|
||
|
}
|
||
|
|
||
|
|
||
|
public function parse(array $lines): Program
|
||
|
{
|
||
|
// In the first parsing stage, all words are parsed from the file.
|
||
|
/** @var Word[] $words */
|
||
|
$words = [];
|
||
|
foreach ($lines as $line) {
|
||
|
$characters = str_split(trim($line) . ' ');
|
||
|
$state = 'NONE';
|
||
|
$wordBuffer = [];
|
||
|
foreach ($characters as $character) {
|
||
|
if ('READING' === $state) {
|
||
|
if ('-' === $character || is_numeric($character)) {
|
||
|
$wordBuffer['value'] .= $character;
|
||
|
continue;
|
||
|
}
|
||
|
// Word is finished
|
||
|
$words[] = new Word($wordBuffer['register'], $wordBuffer['value']);
|
||
|
$wordBuffer = [];
|
||
|
$state = 'NONE';
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ('NONE' === $state && ctype_alpha($character)) {
|
||
|
$state = 'READING';
|
||
|
$wordBuffer = ['register' => $character, 'value' => ''];
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (stripos($line, 'EOF') !== false) {
|
||
|
break 2; // End of program reached
|
||
|
}
|
||
|
|
||
|
if (';' === $character) {
|
||
|
break; // Rest of line is a comment
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// In the second stage, the words are ordered by N numbers
|
||
|
// and shaped into a program
|
||
|
$firstWord = array_shift($words);
|
||
|
if (!preg_match('/^N9\d\d\d$/i', $firstWord)) {
|
||
|
throw new RuntimeException('Expected program number, got ' . $firstWord);
|
||
|
}
|
||
|
$program = new Program($firstWord->value);
|
||
|
$lineBuffer = null;
|
||
|
foreach ($words as $word) {
|
||
|
if ($word->register === 'N') {
|
||
|
if (!is_null($lineBuffer)) {
|
||
|
$program->addBlock($lineBuffer);
|
||
|
}
|
||
|
$lineBuffer = new Block($word->value);
|
||
|
continue;
|
||
|
}
|
||
|
$lineBuffer->addWord($word);
|
||
|
}
|
||
|
|
||
|
return $program;
|
||
|
}
|
||
|
}
|