Skip to content

Commit

Permalink
Change/fix position handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
nielssp committed Jan 29, 2016
1 parent a525913 commit b27a844
Show file tree
Hide file tree
Showing 15 changed files with 189 additions and 42 deletions.
7 changes: 4 additions & 3 deletions .travis.yml
Expand Up @@ -13,7 +13,8 @@ matrix:

- php: 7.0
env: PHPCS=1

allow_failures:
- env: COVERALLS=1

before_script:
- composer self-update
Expand All @@ -27,6 +28,6 @@ before_script:
script:
- if [[ "$PHPCS" == "1" ]]; then vendor/bin/phpcs -p --standard=PSR2 ./src ./tests; fi

- phpunit
- if [[ "$PHPCS" != "1" ]]; then phpunit; fi

- if [[ "$COVERALLS" == "1" ]]; then vendor/bin/coveralls -v; fi
- if [[ "$COVERALLS" == "1" ]]; then vendor/bin/coveralls -v; fi
14 changes: 9 additions & 5 deletions README.md
Expand Up @@ -220,12 +220,16 @@ An example of a parser error handler:
```php
$result = $myParser($input);
if (! $result->successful) {
$lines = explode("\n", $json);
$line = $result->getInputLine($lines);
$column = $result->getInputColumn($lines);
echo 'Syntax Error: ' . $result->message
. ' on line ' . $result->line()
. ' column ' . $result->column() . PHP_EOL;
$lines = explode("\n", $input);
echo $lines[$result->line() - 1] . PHP_EOL;
echo str_repeat('-', $result->column() - 1) . '^';
. ' on line ' . $line
. ' column ' . $column . PHP_EOL;
if ($line > 0) {
echo $lines[$line - 1] . PHP_EOL;
echo str_repeat('-', $column - 1) . '^';
}
}
```
Which produces output such as:
Expand Down
10 changes: 7 additions & 3 deletions examples/json.php
Expand Up @@ -146,9 +146,13 @@ public function __invoke($input)
try {
var_dump($decoder($json));
} catch (\Parco\ParseException $e) {
echo 'Syntax Error: ' . $e->getMessage() . ' on line ' . $e->line() . ' column ' . $e->column() . PHP_EOL;
$lines = explode("\n", $json);
echo $lines[$e->line() - 1] . PHP_EOL;
echo str_repeat('-', $e->column() - 1) . '^';
$line = $e->getInputLine($lines);
$column = $e->getInputColumn($lines);
echo 'Syntax Error: ' . $e->getMessage() . ' on line ' . $line . ' column ' . $column . PHP_EOL;
if ($line > 0) {
echo $lines[$line - 1] . PHP_EOL;
echo str_repeat('-', $column - 1) . '^';
}
}
echo '</pre>';
10 changes: 7 additions & 3 deletions examples/lexer.php
Expand Up @@ -149,9 +149,13 @@ public function __invoke($input)
foreach ($tokens as $token)
echo ' ' . $token;
} catch (\Parco\ParseException $e) {
echo 'Syntax Error: ' . $e->getMessage() . ' on line ' . $e->line() . ' column ' . $e->column() . PHP_EOL;
echo $input . PHP_EOL;
echo str_repeat('-', $e->column() - 1) . '^';
$line = $e->getInputLine([$input]);
$column = $e->getInputColumn([$input]);
echo 'Syntax Error: ' . $e->getMessage() . ' on line ' . $line . ' column ' . $column . PHP_EOL;
if ($line > 0) {
echo $input . PHP_EOL;
echo str_repeat('-', $column - 1) . '^';
}
}


Expand Down
10 changes: 7 additions & 3 deletions examples/tokens.php
Expand Up @@ -242,9 +242,13 @@ public function __invoke($input)
echo 'Abstract syntax tree:' . PHP_EOL;
print_r($ast);
} catch (\Parco\ParseException $e) {
echo 'Syntax Error: ' . $e->getMessage() . ' on line ' . $e->line() . ' column ' . $e->column() . PHP_EOL;
echo $input . PHP_EOL;
echo str_repeat('-', $e->column() - 1) . '^';
$line = $e->getInputLine([$input]);
$column = $e->getInputColumn([$input]);
echo 'Syntax Error: ' . $e->getMessage() . ' on line ' . $line . ' column ' . $column . PHP_EOL;
if ($line > 0) {
echo $input . PHP_EOL;
echo str_repeat('-', $column - 1) . '^';
}
}

echo '</pre>';
6 changes: 4 additions & 2 deletions src/Combinator/Parsers.php
Expand Up @@ -52,10 +52,12 @@ abstract protected function head($input);
* The input sequence.
* @param array $pos
* Current position as a 2-element array consisting of a line
* number and a column number.
* number and a column number. See {@see Positional}.
* @return array A two-element array consisting of the remaining input
* sequence and the position of the first element in the remaining
* input sequence.
* input sequence. If the remaining input sequence is empty, the
* position returned should be `array(-1, -1)`. See
* {@see Positional}.
*/
abstract protected function tail($input, array $pos);

Expand Down
2 changes: 2 additions & 0 deletions src/Combinator/PositionalParsers.php
Expand Up @@ -40,6 +40,8 @@ protected function tail($input, array $pos)
$tail = array_slice($input, 1);
if (isset($tail[0])) {
$pos = $tail[0]->getPosition();
} else {
$pos = array(-1, -1);
}
return array($tail, $pos);
}
Expand Down
18 changes: 16 additions & 2 deletions src/Combinator/RegexParsers.php
Expand Up @@ -52,7 +52,9 @@ protected function tail($input, array $pos)
{
$head = $input[0];
$tail = array_slice($input, 1);
if ($head === "\n") {
if (! isset($tail[0])) {
$pos = array(-1, -1);
} elseif ($head === "\n") {
$pos[0]++;
$pos[1] = 1;
} else {
Expand Down Expand Up @@ -122,7 +124,7 @@ public function whitespace()
$nextPos = $pos;
while (true) {
if (! isset($input[$i])) {
return new Success(null, $pos, array(), $nextPos);
return new Success(null, $pos, array(), array(-1, -1));
}
switch ($input[$i]) {
case "\x0A":
Expand All @@ -138,6 +140,9 @@ public function whitespace()
break;
default:
$input = array_slice($input, $i);
if (! count($input)) {
$nextPos = array(-1, -1);
}
return new Success(null, $pos, $input, $nextPos);
}
$i++;
Expand Down Expand Up @@ -250,6 +255,9 @@ public function string($s)
}
}
$input = array_slice($input, $length);
if (! count($input)) {
$nextPos = array(-1, -1);
}
return new Success($s, $pos, $input, $nextPos);
});
}
Expand Down Expand Up @@ -282,6 +290,9 @@ public function regex($regex)
$input = array_slice($input, $length);
$nextPos = $pos;
$nextPos[1] += $length;
if (! count($input)) {
$nextPos = array(-1, -1);
}
return new Match($matches, $pos, $input, $nextPos);
});
}
Expand Down Expand Up @@ -309,6 +320,9 @@ public function group($i, Parser $p)
if (isset($offset)) {
$pos[1] += $offset;
}
if (! count($input)) {
$nextPos = array(-1, -1);
}
return new Success($group, $pos, $r->nextInput, $r->nextPos);
});
}
Expand Down
22 changes: 22 additions & 0 deletions src/Parco.php
@@ -0,0 +1,22 @@
<?php
// Parco
// Copyright (c) 2015 Niels Sonnich Poulsen (http://nielssp.dk)
// Licensed under the MIT license.
// See the LICENSE file or http://opensource.org/licenses/MIT for more information.
namespace Parco;

/**
* Contains the Parco version string.
*
* @since 1.1.0
*/
class Parco
{

/**
* Parco version string.
*
* @var string
*/
const VERSION = '1.1.0';
}
2 changes: 1 addition & 1 deletion src/Parser.php
Expand Up @@ -18,7 +18,7 @@ abstract class Parser
* Input sequence.
* @param int[] $pos
* Current position as a 2-element array consisting of a line
* number and a column number.
* number and a column number. See {@see Positional}.
* @return Result Parser result.
*/
abstract public function parse($input, array $pos);
Expand Down
62 changes: 60 additions & 2 deletions src/Position.php
Expand Up @@ -6,15 +6,16 @@
namespace Parco;

/**
* A trait that adds a mutable position (line and column numbers).
* A trait that adds a mutable position (line and column numbers). Implements
* the interface {@see Positional}.
*/
trait Position
{

/**
* @var int[]
*/
private $pos = array(1, 1);
private $pos = [0, 0];

/**
* Get the stored position.
Expand Down Expand Up @@ -42,6 +43,8 @@ public function setPosition(array $pos)
* Get the stored line number.
*
* @return int Line number (starting from 1).
* @deprecated Use {@see getPosition} or {@see getInputLine} depending on
* context.
*/
public function line()
{
Expand All @@ -52,12 +55,58 @@ public function line()
* Get the stored column number.
*
* @return int Column number (starting from 1).
* @deprecated Use {@see getPosition} or {@see getInputColumn} depending on
* context.
*/
public function column()
{
return $this->pos[1];
}

/**
* Get the line number for an array of lines.
* Returns `0` if the position is unknown, or the sequence of lines is
* empty.
* Returns the last line number if position is at the end of the input.
*
* @param string[] $lines
* List of lines (e.g. result of `explode("\n", $input)`).
* @return int The positive line number or 0 if unknown.
* @since 1.1.0
*/
public function getInputLine($lines)
{
if (! count($lines) or $this->pos[0] == 0) {
return 0;
}
if ($this->pos[0] < 0) {
return count($lines);
}
return $this->pos[0];
}

/**
* Get the column number for an array of lines.
* Returns `0` if the position is unknown, or the sequence of lines is
* empty.
* If the position is at the end of the input,
*
* @param string[] $lines
* List of lines (e.g. result of `explode("\n", $input)`).
* @return int The positive column number or 0 if unknown.
* @since 1.1.0
*/
public function getInputColumn($lines)
{
if (! count($lines) or $this->pos[0] == 0) {
return 0;
}
if ($this->pos[0] < 0) {
return strlen($lines[count($lines) - 1]) + 1;
}
return $this->pos[1];
}

/**
* Compare two positions.
*
Expand All @@ -71,6 +120,15 @@ public function column()
*/
public static function comparePositions(array $a, array $b)
{
if ($a[0] < 0) {
if ($b[0] < 0) {
return 0;
}
return 1;
}
if ($b[0] < 0) {
return -1;
}
if ($a[0] == $b[0]) {
return $a[1] - $b[1];
}
Expand Down
6 changes: 6 additions & 0 deletions src/Positional.php
Expand Up @@ -8,6 +8,12 @@
/**
* Interface for classes with stored positional information. Used in conjunction
* with {@see Position}.
*
* A position is an array of the form `array($line, $column)`, where `$line`
* and `$column` are integers. `array(1, 1)` is always the first character of the
* first line of an input stream.
* There are two special cases: `array(0, 0)` represents an unknown or
* undefined position and `array(-1, -1)` represents the end of the input.
*/
interface Positional
{
Expand Down
16 changes: 8 additions & 8 deletions tests/Combinator/ParsersTest.php
Expand Up @@ -63,7 +63,7 @@ public function testElem()
$this->assertTrue($result->successful);
$this->assertEquals("\n", $result->get());
$this->assertEquals(array(), $result->nextInput);
$this->assertEquals(array(2, 1), $result->nextPos);
$this->assertEquals(array(-1, -1), $result->nextPos);

$result = $this->apply($p1, array('1'));
$this->assertFalse($result->successful);
Expand Down Expand Up @@ -125,7 +125,7 @@ public function testPhrase()
$this->assertTrue($result->successful);
$this->assertEquals(1, $result->get());
$this->assertEquals(array(), $result->nextInput);
$this->assertEquals(array(1, 2), $result->nextPos);
$this->assertEquals(array(-1, -1), $result->nextPos);
}

public function testPositioned()
Expand Down Expand Up @@ -185,7 +185,7 @@ public function testRep()
$this->assertTrue($result->successful);
$this->assertEquals(array(1, 1, 1), $result->get());
$this->assertEquals(array(), $result->nextInput);
$this->assertEquals(array(1, 4), $result->nextPos);
$this->assertEquals(array(-1, -1), $result->nextPos);
}

public function testRepsep()
Expand All @@ -209,7 +209,7 @@ public function testRepsep()
$this->assertTrue($result->successful);
$this->assertEquals(array(1, 1, 1), $result->get());
$this->assertEquals(array(), $result->nextInput);
$this->assertEquals(array(1, 6), $result->nextPos);
$this->assertEquals(array(-1, -1), $result->nextPos);
}

public function testRep1()
Expand All @@ -225,7 +225,7 @@ public function testRep1()
$this->assertTrue($result->successful);
$this->assertEquals(array(1, 1, 1), $result->get());
$this->assertEquals(array(), $result->nextInput);
$this->assertEquals(array(1, 4), $result->nextPos);
$this->assertEquals(array(-1, -1), $result->nextPos);
}

public function testRep1sep()
Expand All @@ -246,7 +246,7 @@ public function testRep1sep()
$this->assertTrue($result->successful);
$this->assertEquals(array(1, 1, 1), $result->get());
$this->assertEquals(array(), $result->nextInput);
$this->assertEquals(array(1, 6), $result->nextPos);
$this->assertEquals(array(-1, -1), $result->nextPos);
}

public function testRepn()
Expand Down Expand Up @@ -354,7 +354,7 @@ public function testChainl()
$this->assertTrue($result->successful);
$this->assertEquals(-1, $result->get());
$this->assertEquals(array(), $result->nextInput);
$this->assertEquals(array(1, 6), $result->nextPos);
$this->assertEquals(array(-1, -1), $result->nextPos);
}

public function testChainr()
Expand All @@ -377,6 +377,6 @@ public function testChainr()
$this->assertTrue($result->successful);
$this->assertEquals(1, $result->get());
$this->assertEquals(array(), $result->nextInput);
$this->assertEquals(array(1, 6), $result->nextPos);
$this->assertEquals(array(-1, -1), $result->nextPos);
}
}

0 comments on commit b27a844

Please sign in to comment.