StringReader部分

This commit is contained in:
Jerry Yan 2020-12-18 18:12:47 +08:00
parent 9484b3c098
commit 9a0e925f6e
13 changed files with 338 additions and 28 deletions

2
.gitignore vendored
View File

@ -2,4 +2,4 @@
composer.phar
vendor/
composer.lock
.phpunit*

View File

@ -13,9 +13,18 @@
"php": ">=7.1",
"ext-mbstring": "*"
},
"require-dev": {
"phpunit/phpunit": "^9.0",
"mockery/mockery": "^1.4"
},
"autoload": {
"psr-4": {
"JerryYan\\DSL\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"JerryYan\\DSL\\Test\\": "tests"
}
}
}

View File

@ -9,7 +9,10 @@
namespace JerryYan\DSL\Reader;
class FileReader extends ReaderInterface
class FileReader /** extends ReaderInterface */
{
public function __construct(string $fileName)
{
}
}

View File

@ -17,13 +17,42 @@ namespace JerryYan\DSL\Reader;
*/
abstract class ReaderInterface
{
protected $currentLine = 0;
protected $currentLine = 1;
protected $currentPosition = 0;
protected $currentLinePosition = 0;
#abstract public function getNextChar(): ?string;
#abstract public function getCurrentToken(): ?string;
#abstract public function getNextToken(): ?string;
#abstract public function moveToNextToken(): ?string;
protected $nextPosition = 0;
/**
* 获取下一个字符
* @return string
* @author Jerry Yan <792602257@qq.com>
* @date 2020/12/18 12:06
*/
abstract public function getNextChar(): string;
/**
* 获取当前识别符
* @return string
* @author Jerry Yan <792602257@qq.com>
* @date 2020/12/18 12:06
*/
abstract public function getCurrentToken(): string;
/**
* 获取下一个识别符
* @return string
* @author Jerry Yan <792602257@qq.com>
* @date 2020/12/18 12:06
*/
abstract public function getNextToken(): string;
/**
* 移动至下一个识别符
* @return bool
* @author Jerry Yan <792602257@qq.com>
* @date 2020/12/18 12:06
*/
abstract public function moveToNextToken(): bool;
/**
* 跳过当前行
@ -31,7 +60,7 @@ abstract class ReaderInterface
* @author Jerry Yan <792602257@qq.com>
* @date 2020/12/17 15:43
*/
#abstract public function skipCurrentLine(): bool;
abstract public function skipCurrentLine(): bool;
/**
* 从当前位置跳到结束位置
@ -40,5 +69,28 @@ abstract class ReaderInterface
* @author Jerry Yan <792602257@qq.com>
* @date 2020/12/17 15:43
*/
#abstract public function skipUntil(string $end="*/"): bool;
abstract public function skipUntil(string $end="*/"): bool;
/**
* 重置读取器
* @author Jerry Yan <792602257@qq.com>
* @date 2020/12/18 12:01
*/
public function reset(): void
{
$this->currentLine = 1;
$this->currentPosition = 0;
$this->nextPosition = 0;
$this->moveToNextToken();
}
/**
* 重置读取器
* @author Jerry Yan <792602257@qq.com>
* @date 2020/12/18 12:01
*/
public function resetCursor(): void
{
$this->reset();
}
}

View File

@ -1,15 +0,0 @@
<?php
/**
* @filename StreamReader.php
* @author Jerry Yan <792602257@qq.com>
* @date 2020/12/17 14:58
*/
namespace JerryYan\DSL\Reader;
class StreamReader extends ReaderInterface
{
}

124
src/Reader/StringReader.php Normal file
View File

@ -0,0 +1,124 @@
<?php
/**
* @filename StringReader.php
* @author Jerry Yan <792602257@qq.com>
* @date 2020/12/17 14:58
*/
namespace JerryYan\DSL\Reader;
class StringReader extends ReaderInterface
{
protected $string;
protected $currentToken;
public function __construct(string $string)
{
$this->string = $string;
$this->moveToNextToken();
}
/**
* @inheritDoc
*/
public function getNextChar(int $startAt = null): string
{
if ($startAt === null) $startAt = $this->currentPosition;
return mb_substr($this->string, $startAt, 1);
}
/**
* @inheritDoc
*/
public function getCurrentToken(): string
{
return $this->currentToken;
}
/**
* @inheritDoc
*/
public function getNextToken(): string
{
$curToken = "";
$curPos = $this->nextPosition;
while ($curChar = $this->getNextChar($curPos)) {
switch ($curChar) {
case " ":
// 如果开始的时候就有空白,跳过它
if (empty($curToken)) {
continue 2;
}
// 否则就结束(已经匹配完成)
break 2;
case "\r":
case "\n":
break 2;
default:
$curToken .= $curChar;
}
}
return $curToken;
}
/**
* @inheritDoc
*/
public function moveToNextToken(): bool
{
$curToken = "";
$this->currentPosition = $this->nextPosition;
while ($curChar = $this->getNextChar($this->nextPosition)) {
$this->nextPosition++;
$this->currentLinePosition++;
switch ($curChar) {
case " ":
// 如果开始的时候就有空白,跳过它
if (empty($curToken)) {
$this->currentPosition++;
continue 2;
}
// 否则就结束(已经匹配完成)
break 2;
case "\r":
if ($this->getNextChar($this->nextPosition+1) === "\n") {
// CRLF换行
$this->nextPosition+=2;
}
// CR换行
$this->currentLine++;
$this->currentLinePosition=0;
break 2;
case "\n":
// LF换行
$this->currentLine++;
$this->currentLinePosition=0;
break 2;
default:
$curToken .= $curChar;
}
}
$this->currentToken = $curToken;
return true;
}
/**
* @inheritDoc
*/
public function skipCurrentLine(): bool
{
// TODO: Implement skipCurrentLine() method.
return true;
}
/**
* @inheritDoc
*/
public function skipUntil(string $end = "*/"): bool
{
// TODO: Implement skipUntil() method.
return true;
}
}

View File

@ -9,7 +9,7 @@
namespace JerryYan\DSL\Token;
class TokenAnd
class TokenAnd extends TokenInterface
{
}

View File

@ -12,8 +12,24 @@ namespace JerryYan\DSL\Token;
class TokenFactory
{
/** @var array<string, string> Token类型及映射类 */
#private $tokenMap = [
# Token::AND => TokenAnd::class,
#];
private $tokenMap = [
Token::AND => TokenAnd::class,
Token::OR => TokenOr::class,
];
protected $tokenNameMap = [
];
public function getTokenByName(string $name): TokenInterface
{
if (!isset($this->tokenNameMap[$name])) {
return new TokenUndefined();
}
$tokenType = $this->tokenNameMap[$name];
if (!isset($this->tokenMap[$tokenType])) {
return new TokenUndefined();
}
return new $this->tokenMap[$tokenType];
}
}

15
src/Token/TokenOr.php Normal file
View File

@ -0,0 +1,15 @@
<?php
/**
* @filename TokenAnd.php
* @author Jerry Yan <792602257@qq.com>
* @date 2020/12/17 15:20
*/
namespace JerryYan\DSL\Token;
class TokenOr extends TokenInterface
{
}

View File

@ -0,0 +1,15 @@
<?php
/**
* @filename TokenUndefined.php
* @author Jerry Yan <792602257@qq.com>
* @date 2020/12/18 12:15
*/
namespace JerryYan\DSL\Token;
class TokenUndefined extends TokenInterface
{
}

View File

@ -9,7 +9,31 @@
namespace JerryYan\DSL\Tokenizer;
use ArrayIterator;
use JerryYan\DSL\Reader\ReaderInterface;
use JerryYan\DSL\Token\TokenFactory;
class Tokenizer extends TokenizerInterface
{
public function __construct(TokenFactory $tokenFactory)
{
$this->tokenFactory = $tokenFactory;
}
/**
* @inheritDoc
*/
function tokenize(ReaderInterface $reader): ArrayIterator
{
$reader->resetCursor();
$tokens = [];
while($reader->moveToNextToken())
{
$currentTokenName = $reader->getCurrentToken();
$currentToken = $this->tokenFactory->getTokenByName($currentTokenName);
$tokens[] = $currentToken;
}
return new ArrayIterator($tokens);
}
}

View File

@ -9,7 +9,20 @@
namespace JerryYan\DSL\Tokenizer;
use ArrayIterator;
use JerryYan\DSL\Reader\ReaderInterface;
use JerryYan\DSL\Token\TokenInterface;
abstract class TokenizerInterface
{
protected $tokenFactory;
/**
*
* @param ReaderInterface $reader
* @return ArrayIterator<TokenInterface>
* @author Jerry Yan <792602257@qq.com>
* @date 2020/12/18 11:43
*/
abstract function tokenize(ReaderInterface $reader): ArrayIterator;
}

View File

@ -0,0 +1,54 @@
<?php
/**
* @filename StringReaderTest.php
* @author Jerry Yan <792602257@qq.com>
* @date 2020/12/18 12:19
*/
namespace JerryYan\DSL\Test\Reader;
use JerryYan\DSL\Reader\StringReader;
use PHPUnit\Framework\TestCase;
class StringReaderTest extends TestCase
{
protected $readerWithCn;
protected $reader;
protected function setUp(): void
{
$this->reader = new StringReader(" Ahhh This Is 一个 新的 TOken");
$this->readerWithCn = new StringReader(" 中文 这是 Is 一个 新的 TOken");
}
public function testGetNextChar()
{
$this->reader->reset();
$this->assertEquals('A', $this->reader->getNextChar(), "不匹配");
$this->readerWithCn->reset();
$this->assertEquals('中', $this->readerWithCn->getNextChar(), "不匹配");
}
public function testGetCurrentToken()
{
$this->reader->reset();
$this->assertEquals('Ahhh', $this->reader->getCurrentToken(), "不匹配");
$this->readerWithCn->reset();
$this->assertEquals('中文', $this->readerWithCn->getCurrentToken(), "不匹配");
}
/**
* @author Jerry Yan <792602257@qq.com>
* @date 2020/12/18 14:16
* @depends testGetNextChar
* @depends testGetCurrentToken
*/
public function testMoveToNextToken()
{
$this->reader->reset();
$this->reader->moveToNextToken();
$this->assertEquals('This', $this->reader->getCurrentToken(), "不匹配");
$this->readerWithCn->reset();
$this->readerWithCn->moveToNextToken();
$this->assertEquals('这是', $this->readerWithCn->getCurrentToken(), "不匹配");
}
}