From df287205d6eb67f3581554aeee9d1a5ce4082d8a Mon Sep 17 00:00:00 2001 From: Alexander Lisachenko Date: Wed, 25 Mar 2026 16:46:33 +0200 Subject: [PATCH 1/2] Fix PHP 8.4/8.5 deprecations and update CI to target PHP 8.4+ - Add explicit nullable types (?int, ?string, ?array) to fix implicit nullable parameter deprecations in Util, SimpleLexer, StatefulLexer, StringWriter, and Parser - Update GitHub Actions workflow to test on PHP 8.4 and 8.5 - Add --display-all-issues flag to PHPUnit in CI - Add CLAUDE.md with project guidance Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/php-tests.yml | 4 +- CLAUDE.md | 64 ++++++++++++++++++++++++ composer.json | 10 ++-- src/Lexer/SimpleLexer.php | 2 +- src/Lexer/StatefulLexer.php | 2 +- src/Parser/LALR1/Dumper/StringWriter.php | 2 +- src/Parser/LALR1/Parser.php | 2 +- src/Util/Util.php | 2 +- 8 files changed, 76 insertions(+), 12 deletions(-) create mode 100644 CLAUDE.md diff --git a/.github/workflows/php-tests.yml b/.github/workflows/php-tests.yml index b53d88f..37b2166 100644 --- a/.github/workflows/php-tests.yml +++ b/.github/workflows/php-tests.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: operating-system: [ ubuntu-latest ] - php-version: ['8.2', '8.3'] + php-version: ['8.4', '8.5'] name: PHP ${{ matrix.php-version }} test on ${{ matrix.operating-system }} @@ -45,7 +45,7 @@ jobs: run: composer install --prefer-dist --no-progress - name: PHPUnit Tests - run: vendor/bin/phpunit --coverage-clover ./tests/coverage.xml + run: vendor/bin/phpunit --display-all-issues --coverage-clover ./tests/coverage.xml - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v3 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..b09b8b5 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,64 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Commands + +```bash +# Run tests +vendor/bin/phpunit + +# Run tests with coverage +composer run-script test-coverage + +# Apply Rector refactoring rules +vendor/bin/rector process + +# CLI tool +php bin/dissect.php +``` + +To run a single test file: `vendor/bin/phpunit tests/Path/To/SomeTest.php` + +## Architecture + +Dissect is a pure PHP library for lexical and syntactical analysis (building custom lexers and LALR(1) parsers). It is used by the GoAOP framework to parse pointcut DSL expressions. + +### Data Flow + +``` +Input String → Lexer → TokenStream → Parser → AST / semantic result + ↑ + Grammar (rules + callbacks) +``` + +### Core Modules + +**`src/Lexer/`** — Converts strings into token streams. +- `AbstractLexer` — Base class handling line tracking, token filtering, EOF insertion. Subclasses implement `extractToken()` and `shouldSkipToken()`. +- `SimpleLexer` / `StatefulLexer` — Fluent builders (`token()`, `regex()`, `skip()`, `state()`). `StatefulLexer` supports context-dependent tokenization via explicit state transitions. +- `RegexLexer` — Abstract base for custom regex-based lexers. +- Recognizers: `SimpleRecognizer` (string match) and `RegexRecognizer` (regex match) are tried in sequence until one succeeds. + +**`src/Parser/`** — Converts token streams into results using LALR(1) parsing. +- `Grammar` — Defines rules with a fluent API: `$grammar('NonTerminal')->is('A', 'B')->call(fn(...) => ...)`. Also handles operator precedence and associativity. +- `Rule` — A single grammar production with optional semantic action callback. +- `src/Parser/LALR1/` — Core LALR(1) engine: + - `Analyzer` — Computes FIRST/FOLLOW sets and builds the parse table (shift/reduce/accept actions) from a `Grammar`. + - `Automaton` / `State` / `Item` — LR(0) items and states forming the state machine. + - `Parser` — Drives the parse loop using the generated table; executes rule callbacks on reduction. + - Dumpers — Debug utilities to print parse tables and automaton graphs. + +**`src/Node/`** — AST representation. +- `Node` interface — Countable and IteratorAggregate for tree traversal. +- `CommonNode` — Default implementation with parent/child relationships and arbitrary attribute storage. + +**`src/Console/`** — CLI via Symfony Console (`bin/dissect`). + +**`src/Util/`** — Unicode-aware string utilities. + +### Key Patterns + +- Grammar rules are defined as callbacks: reductions transform matched symbols into AST nodes or computed values. +- The `Analyzer` is run once per grammar to build the parse table; the resulting `Automaton` is passed to `Parser` for repeated use. +- Conflict resolution (shift/reduce, reduce/reduce) is handled during grammar analysis using operator precedence declarations. diff --git a/composer.json b/composer.json index 7020aaf..949c2c8 100644 --- a/composer.json +++ b/composer.json @@ -32,12 +32,12 @@ "test-coverage": "phpunit --coverage-html tests/coverage" }, "require": { - "php": "^8.2.0" + "php": "^8.4.0" }, "require-dev": { - "phpunit/phpunit": "^11.0.3", - "rector/rector": "^1.0", - "symfony/console": ">=6.0" + "phpunit/phpunit": "^13.0", + "rector/rector": "^2.0", + "symfony/console": "^7.4 || ^8.0" }, "suggest": { "symfony/console": "for the command-line tool" @@ -59,7 +59,7 @@ "minimum-stability": "stable", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "4.0-dev" } }, "config": { diff --git a/src/Lexer/SimpleLexer.php b/src/Lexer/SimpleLexer.php index 854ff6c..f993365 100644 --- a/src/Lexer/SimpleLexer.php +++ b/src/Lexer/SimpleLexer.php @@ -32,7 +32,7 @@ class SimpleLexer extends AbstractLexer * @param string $type The token type. * @param string|null $value The value to be recognized. */ - public function token(string $type, string $value = null): self + public function token(string $type, ?string $value = null): self { if ($value) { $this->recognizers[$type] = new SimpleRecognizer($value); diff --git a/src/Lexer/StatefulLexer.php b/src/Lexer/StatefulLexer.php index 6a4ac5c..1190c35 100644 --- a/src/Lexer/StatefulLexer.php +++ b/src/Lexer/StatefulLexer.php @@ -42,7 +42,7 @@ class StatefulLexer extends AbstractLexer * @param string $type The token type. * @param string|null $value The value to be recognized. */ - public function token(string $type, string $value = null): StatefulLexer + public function token(string $type, ?string $value = null): StatefulLexer { if ($this->stateBeingBuilt === null) { throw new LogicException("Define a lexer state first."); diff --git a/src/Parser/LALR1/Dumper/StringWriter.php b/src/Parser/LALR1/Dumper/StringWriter.php index 2ae8d7a..26b0efd 100644 --- a/src/Parser/LALR1/Dumper/StringWriter.php +++ b/src/Parser/LALR1/Dumper/StringWriter.php @@ -61,7 +61,7 @@ public function outdent(): void * * @param string|null $string The string to write. */ - public function writeLine(string $string = null): void + public function writeLine(?string $string = null): void { if ($string) { $this->write(sprintf( diff --git a/src/Parser/LALR1/Parser.php b/src/Parser/LALR1/Parser.php index 2d5f71b..ca4ccb6 100644 --- a/src/Parser/LALR1/Parser.php +++ b/src/Parser/LALR1/Parser.php @@ -28,7 +28,7 @@ class Parser implements P\Parser * @param Grammar $grammar The grammar. * @param array|null $parseTable If given, the parser doesn't have to analyze the grammar. */ - public function __construct(Grammar $grammar, array $parseTable = null) + public function __construct(Grammar $grammar, ?array $parseTable = null) { $this->grammar = $grammar; diff --git a/src/Util/Util.php b/src/Util/Util.php index 0be9b01..4b4ee1c 100644 --- a/src/Util/Util.php +++ b/src/Util/Util.php @@ -57,7 +57,7 @@ public static function stringLength(string $str): int * * @return string The substring. */ - public static function substring(string $str, int $position, int $length = null): string + public static function substring(string $str, int $position, ?int $length = null): string { static $lengthFunc = null; From 31fc1950dbe11884cb1ae46dad513141b1cef5b2 Mon Sep 17 00:00:00 2001 From: Alexander Lisachenko Date: Wed, 25 Mar 2026 16:49:58 +0200 Subject: [PATCH 2/2] Limit CI triggers to master pushes and PR updates only Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/php-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/php-tests.yml b/.github/workflows/php-tests.yml index 37b2166..c3d3761 100644 --- a/.github/workflows/php-tests.yml +++ b/.github/workflows/php-tests.yml @@ -2,7 +2,10 @@ name: PHP Tests on: pull_request: + types: [opened, synchronize, reopened] push: + branches: + - master permissions: contents: read