Files
abcParser/src/Parser.php
richard 0cc8361929 Fix the failing unit tests
refactor the codestyle for the tests we wanted to keep
2017-10-17 06:41:19 -04:00

151 lines
5.3 KiB
PHP

<?php
namespace XaiCorp\AbcParser;
use XaiCorp\AbcParser\Interfaces\Builder;
class Parser {
/**
* the abc passed to the parser might be a single tune or
* it might be a collection of tunes, with extra data outside
* of each tune. This external data should be applied to each
* tune and the collection.
*
* When reading the abc, the parser should create a new collection
* and a new Tune object for each tune encountered in the abc.
* External tools can then deal with that collection as they like.
*
*/
//regexp patterns for different lines
const LN_VERSION = '/^%abc(-\d\.\d)?$/';
const LN_BLANK = '/^\s*$/';
const LN_FIELD = '/^\w:(.+)$/';
const MODE_NO_DATA = 0;
const MODE_FILE_HEADER = 1;
const MODE_TUNE_HEADER = 2;
const MODE_TUNE_BODY = 3;
const SINGLE_VALUE_KEYS = ['P','Q','L','M','K'];
public function __construct(Builder $builder)
{
$this->builder = $builder;
}
/**
* extract the attributes from the raw abc
* @param <string> $abc string containing the raw abc
*
* @return
*/
public function parseABC($abc)
{
//tune is either new or existing data has been loaded
//create new setting
$mode = self::MODE_FILE_HEADER;
$this->builder->newCollection();
$tune = null;
$setting = null;
$inHistory = false;
$inTunes = false;
$music = "";
$keys_for_Settings = '/K|L|M|P|Q|B|N|D|S|W|Z|F/';
$keys_for_Tunes = '/H|A|C|G|R|T|O/';
//scan abc by line and see what the first char is
foreach (preg_split("/(\r?\n)/", $abc) as $line) {
if (preg_match(self::LN_BLANK, $line)) {
if ($mode === self::MODE_TUNE_BODY || $mode === self::MODE_TUNE_HEADER) {
$this->builder->storeTune($music);
$music = '';
}
$mode = self::MODE_NO_DATA;
} elseif ($mode === self::MODE_TUNE_BODY) {
$music .= $line . PHP_EOL;
} elseif (preg_match(self::LN_VERSION, $line)) {
//this line might contain the abc version.
//What do we want to do with it?
} elseif (preg_match(self::LN_FIELD, $line) || $inHistory) {
if (preg_match(self::LN_FIELD, $line)) { $inHistory = FALSE; }
//split the line "key:data"
if($inHistory) {
$key = 'H';
$data = $line;
} else {
$key = substr($line, 0, 1);
$data = trim(substr($line, 2));
}
if ($key === 'X') {
$mode = self::MODE_TUNE_HEADER;
$inTunes = true;
$this->builder->newTune();
$this->builder->newSetting();
$music = '';
$this->builder->setOnTune('X', $data);
} elseif (preg_match($keys_for_Tunes, $key)) {
if ($key === 'H') {
$inHistory = TRUE;
} else {
$inHistory = FALSE;
}
if ($key === 'A' || $key === 'C') {
$data = $this->builder->newPerson(['name'=>$data]);
}
if ($mode === self::MODE_FILE_HEADER) {
if (in_array($key, self::SINGLE_VALUE_KEYS)) {
$this->builder->setOnCollection($key, $data);
} else {
$this->builder->appendToCollection($key, $data);
}
} elseif($inTunes) {
if (in_array($key, self::SINGLE_VALUE_KEYS)) {
$this->builder->setOnTune($key, $data);
} else {
$this->builder->appendToTune($key, $data);
}
}
} elseif (preg_match($keys_for_Settings, $key)) {
if ($key === 'K' && $mode === self::MODE_TUNE_HEADER) {
$mode = self::MODE_TUNE_BODY;
}
if ($key === 'Z') {
$data = $this->builder->newPerson(['name'=>$data]);
}
if ($mode == self::MODE_FILE_HEADER) {
if (in_array($key, self::SINGLE_VALUE_KEYS)) {
$this->builder->setOnCollection($key, $data);
} else {
$this->builder->appendToCollection($key, $data);
}
} elseif($inTunes){
if (in_array($key, self::SINGLE_VALUE_KEYS)) {
$this->builder->setOnSetting($key, $data);
} else {
$this->builder->appendToSetting($key, $data);
}
}
}
}
}
return $this->builder->getCollection();
}
// private static function _storeTune($music, &$setting, &$tune, &$tuneCollection) {
// $setting -> set('music', trim($music));
// $tune -> append('collection', $setting);
// $tuneCollection -> append('collection', $tune);
// }
}