Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V2.7.0 #14

Merged
merged 7 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ $variables = [
'show_container' => false,
];

// Absolute file path or file content as a string
// Absolute file path to a text file
$file_path = __DIR__ . '/hello.html';

$parser = new Parser($file_path, $variables);
Expand Down Expand Up @@ -110,7 +110,8 @@ Parsed HTML to a PHP string
</body>
</html>
```
## Same example but for WordPress shortcode

### Same example but for WordPress shortcode

```php
use Serhii\GoodbyeHtml\Parser;
Expand All @@ -127,6 +128,21 @@ function shortcode_callback() {
}
```

## Options

The instance of `Parser` class takes the third argument as a `ParserOption` enum. You can pass it to the constructor of the `Parser` class as a third argument. For now, it has only a single options:

#### `ParserOption::PARSE_TEXT`
If you pass this option, the parser, instead of getting the content of the provided file path, will parse the provided string. This option is useful when you want to parse a string instead of a file.

```php
$parser = new Parser('<div>{{ $title }}</div>', [
'title' => 'Hello world'
], ParserOption::PARSE_TEXT);

// output: <div>Hello world</div>
```

## Supported types

Types that you can pass to the parser to include them in the `html/text` file. Note that not all PHP types are supported for know. More types will be added in next releases.
Expand Down Expand Up @@ -294,7 +310,16 @@ Loop takes 2 integer arguments. The first argument is from what number start loo
</div>
```

#### Assigning statements

You can assign values to variables inside your text files using curly braces. For example if you want to assign value 5 to variable `$a`, you can do it like this `{{ $a = 5 }}`. You can also use prefix operators to change the value of the variable. For example if you want to assign value false to variable `$is_smoking`, you can do it like this `{{ $is_smoking = !true }}`.

```html
<div>{{ $age = 33 }}</div>
```

## Getting started

```bash
$ composer require serhii/goodbye-html
```
9 changes: 9 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

----

## v2.7.0 (2023-11-22)

- Added more tests to make sure that everything works as expected
- Added more info to the `README.md` file
- Added added assign statement to the BNF grammar
- Added a third parameter to a `Parser.php` which excepts `ParserOption` ENUM

----

## v2.6.0 (2023-11-21)

- Added `elseif` statements to a BNF grammar
Expand Down
4 changes: 3 additions & 1 deletion docs/goodbye-html.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
| <expression-statement>
| <if-statement>
| <loop-statement>
| <block-statement>
| <assign-statement>

<digit> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"

Expand All @@ -29,6 +29,8 @@

<html-statement> ::= <string-literal>

<assign-statement> ::= <variable-expression> "=" <expression>

<expression-statement> ::= <expression>

<ternary-expression> ::= <expression> "?" <expression> ":" <expression>
Expand Down
6 changes: 6 additions & 0 deletions src/Ast/Expressions/PrefixExpression.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

readonly class PrefixExpression implements Expression
{
/**
* @param non-empty-string $operator
*/
public function __construct(
public Token $token,
public string $operator,
Expand All @@ -20,6 +23,9 @@ public function tokenLiteral(): string
return $this->token->literal;
}

/**
* @return non-empty-string
*/
public function string(): string
{
return sprintf('(%s%s)', $this->operator, $this->right->string());
Expand Down
2 changes: 1 addition & 1 deletion src/Ast/Literals/NullLiteral.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ public function tokenLiteral(): string

public function string(): string
{
return '';
return 'null';
}
}
4 changes: 4 additions & 0 deletions src/CoreParser/CoreParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,10 @@ private function parsePrefixExpression(): Expression

$right = $this->parseExpression(Precedence::PREFIX);

if ($operator === '') {
throw new CoreParserException(ParserError::prefixOperatorNotFound());
}

return new PrefixExpression($token, $operator, $right);
}

Expand Down
17 changes: 17 additions & 0 deletions src/CoreParser/ParserError.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ abstract class ParserError
{
private const PREFIX = '[PARSER_ERROR]:';

/**
* @return non-empty-string
*/
public static function noPrefixParseFunction(Token $token): string
{
return sprintf(
Expand All @@ -20,6 +23,9 @@ public static function noPrefixParseFunction(Token $token): string
);
}

/**
* @return non-empty-string
*/
public static function expectNextTokenToBeDifferent(TokenType $token, Token $peek): string
{
return sprintf(
Expand All @@ -30,11 +36,22 @@ public static function expectNextTokenToBeDifferent(TokenType $token, Token $pee
);
}

/**
* @return non-empty-string
*/
public static function elseIfBlockWrongPlace(): string
{
return sprintf(
'%s Wrong placement of the "elseif" block! "elseif" block must be placed after "if" or "elseif" block',
self::PREFIX,
);
}

/**
* @return non-empty-string
*/
public static function prefixOperatorNotFound(): string
{
return sprintf('%s prefix operator not found', self::PREFIX);
}
}
7 changes: 7 additions & 0 deletions src/Exceptions/CoreParserException.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,11 @@

class CoreParserException extends Exception
{
/**
* @param non-empty-string $message
*/
public function __construct(string $message)
{
parent::__construct($message);
}
}
7 changes: 7 additions & 0 deletions src/Exceptions/EvaluatorException.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,11 @@

class EvaluatorException extends Exception
{
/**
* @param non-empty-string $message
*/
public function __construct(string $message)
{
parent::__construct($message);
}
}
7 changes: 7 additions & 0 deletions src/Exceptions/ParserException.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,11 @@

class ParserException extends Exception
{
/**
* @param non-empty-string $message
*/
public function __construct(string $message)
{
parent::__construct($message);
}
}
36 changes: 34 additions & 2 deletions src/Lexer/Lexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,30 @@ class Lexer
{
private const LAST_CHAR = 'ø';

private readonly string $input;
/**
* The position of the current character in the input (points to current char)
*
* @var int<0, max>
*/
private int $position = 0;

/**
* The position of the next character in the input (points to next char)
*
* @var int<0, max>
*/
private int $nextPosition = 0;

private readonly string $input;

/**
* The current character under examination
*/
private string $char = '';

/**
* Tells us whether we are in HTML or in embedded code
*/
private bool $isHtml = true;

public function __construct(string $input)
Expand Down Expand Up @@ -74,7 +94,7 @@ private function readEmbeddedCodeToken(): Token
}

if ($this->isVariableStart()) {
$this->advanceChar();
$this->advanceChar(); // skip "$"
return new Token(TokenType::VAR, $this->readIdentifier());
}

Expand Down Expand Up @@ -147,12 +167,16 @@ private function advanceChar(): void
++$this->nextPosition;
}

/**
* @return non-empty-string
*/
private function peekChar(): string
{
if ($this->nextPosition >= strlen($this->input)) {
return self::LAST_CHAR;
}

/** @var non-empty-string */
return $this->input[$this->nextPosition];
}

Expand Down Expand Up @@ -184,6 +208,9 @@ private function isNumber(string $number): bool
return preg_match('/[0-9.]/', $number) === 1;
}

/**
* @return non-empty-string
*/
private function readIdentifier(): string
{
$position = $this->position;
Expand All @@ -192,6 +219,7 @@ private function readIdentifier(): string
$this->advanceChar();
}

/** @var non-empty-string $result */
$result = substr($this->input, $position, $this->position - $position);

// Handle case when identifier is "else if"
Expand All @@ -206,6 +234,9 @@ private function readIdentifier(): string
return $result;
}

/**
* @return non-empty-string
*/
private function readNumber(): string
{
$position = $this->position;
Expand All @@ -214,6 +245,7 @@ private function readNumber(): string
$this->advanceChar();
}

/** @var non-empty-string */
return substr($this->input, $position, $this->position - $position);
}

Expand Down
6 changes: 6 additions & 0 deletions src/Obj/ErrorObj.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

readonly class ErrorObj extends Obj
{
/**
* @param non-empty-string $message
*/
public function __construct(public string $message)
{
}
Expand All @@ -15,6 +18,9 @@ public function type(): ObjType
return ObjType::ERROR_OBJ;
}

/**
* @return non-empty-string
*/
public function value(): string
{
return $this->message;
Expand Down
12 changes: 9 additions & 3 deletions src/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ class Parser
private string $html_content;

/**
* @param string $file_path Absolute file path or the file content itself
* @param array<string, mixed>|null $variables Associative array ['var_name' => 'will be inserted']
* @param string $file_path Absolute file path
* @param array<non-empty-string, int|string|bool|float|null>|null $variables
* @param ParserOption|null $options
*/
public function __construct(
private readonly string $file_path,
private readonly array|null $variables = null,
private readonly ParserOption|null $options = null,
) {
$this->html_content = '';
}
Expand All @@ -45,6 +47,10 @@ public function __construct(
*/
public function parseHtml(): string
{
if ($this->file_path === '') {
return '';
}

$this->setHtmlContent();

if (!$this->hasVariables()) {
Expand All @@ -69,7 +75,7 @@ public function parseHtml(): string
*/
private function setHtmlContent(): void
{
if (!str_starts_with($this->file_path, '/')) {
if ($this->options && $this->options === ParserOption::PARSE_TEXT) {
$this->html_content = $this->file_path;
return;
}
Expand Down
10 changes: 10 additions & 0 deletions src/ParserOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Serhii\GoodbyeHtml;

enum ParserOption
{
case PARSE_TEXT;
}
Loading
Loading