Skip to content

Commit 92f8d12

Browse files
committed
added CLAUDE.md
1 parent 8045f6c commit 92f8d12

File tree

1 file changed

+360
-0
lines changed

1 file changed

+360
-0
lines changed

CLAUDE.md

Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
**Nette NEON** is a parser/encoder library for the NEON format (Nette Object Notation), a human-readable structured data format similar to YAML but with unique features like entities. It's used primarily for configuration files in the Nette Framework.
8+
9+
- **Repository**: https://github.com/nette/neon
10+
- **Homepage**: https://neon.nette.org
11+
- **PHP Requirements**: 8.2 - 8.5
12+
- **Current Version**: 3.5-dev
13+
14+
## Essential Commands
15+
16+
### Testing
17+
```bash
18+
# Run all tests
19+
composer run tester
20+
21+
# Run specific test file
22+
vendor/bin/tester tests/Neon/Decoder.phpt -s
23+
24+
# Run tests in specific directory
25+
vendor/bin/tester tests/Neon/ -s
26+
```
27+
28+
### Static Analysis
29+
```bash
30+
# Run PHPStan (level 6)
31+
composer run phpstan
32+
```
33+
34+
### Validation
35+
```bash
36+
# Lint NEON files
37+
vendor/bin/neon-lint <path>
38+
```
39+
40+
## Architecture
41+
42+
### Parser Pipeline
43+
44+
The library follows a classic lexer → parser → AST → value pipeline:
45+
46+
1. **Lexer** (`src/Neon/Lexer.php`): Tokenizes NEON input using regex patterns
47+
- Produces tokens with type, text, and position information
48+
- Token types: `String`, `Literal`, `Char`, `Comment`, `Newline`, `Whitespace`, `End`
49+
50+
2. **TokenStream** (`src/Neon/TokenStream.php`): Manages token buffer
51+
- Handles whitespace/comment skipping
52+
- Provides token consumption and seeking
53+
54+
3. **Parser** (`src/Neon/Parser.php`): Builds Abstract Syntax Tree
55+
- Converts token stream to Node tree
56+
- Handles block (indentation-based) and inline (bracket-based) notation
57+
- Detects mixed tabs/spaces errors
58+
59+
4. **Node Hierarchy** (`src/Neon/Node/`): AST representation
60+
- All nodes extend abstract `Node` class with `toValue()` and `toString()` methods
61+
- Node types:
62+
- `LiteralNode` - numbers, booleans, null, dates
63+
- `StringNode` - quoted and unquoted strings
64+
- `EntityNode` - NEON entities like `Column(type: int)`
65+
- `EntityChainNode` - chained entities
66+
- `BlockArrayNode` / `InlineArrayNode` - arrays in different notations
67+
- `ArrayItemNode` - individual array items with optional key
68+
- All nodes track position (`start`, `end` as `Position` objects)
69+
70+
5. **Decoder** (`src/Neon/Decoder.php`): Public API for parsing
71+
- `parseToNode()` - returns AST
72+
- `decode()` - returns PHP value (calls `toValue()` on root node)
73+
74+
6. **Encoder** (`src/Neon/Encoder.php`): Converts PHP values to NEON
75+
- `valueToNode()` - creates Node tree from PHP value
76+
- `encode()` - returns NEON string (calls `toString()` on nodes)
77+
- Supports block and inline rendering modes
78+
79+
7. **Traverser** (`src/Neon/Traverser.php`): AST manipulation utility
80+
- Visitor pattern for node processing
81+
- Supports `enter` and `leave` callbacks
82+
- Return constants: `RemoveNode`, `StopTraversal`, `DontTraverseChildren`
83+
84+
### Key Design Patterns
85+
86+
- **Position Tracking**: All tokens and nodes include `Position` (line, column, offset) for precise error reporting
87+
- **Readonly Classes**: `Token` and `Position` are readonly for immutability
88+
- **Final by Default**: All concrete classes are `final` unless designed for extension
89+
- **Entity Pattern**: `Entity` class extends `stdClass` with `value` and `attributes` properties
90+
- **Type Safety**: All parameters, properties, and return types are strictly typed
91+
92+
## Code Style
93+
94+
### PHP Standards
95+
- Every file must start with `declare(strict_types=1)`
96+
- Use tabs for indentation
97+
- Use single quotes for strings (except when escaping needed)
98+
- PHP 8.2+ features are standard (readonly, union types, named parameters)
99+
100+
### Naming Conventions
101+
- Classes: PascalCase (e.g., `BlockArrayNode`)
102+
- Methods: camelCase (e.g., `toValue()`, `toString()`)
103+
- Constants: PascalCase (e.g., `Token::String`, `Traverser::RemoveNode`)
104+
- Namespace: `Nette\Neon`
105+
106+
### Documentation
107+
- Minimalist phpDoc - only document non-obvious information
108+
- Don't duplicate type information already in signatures
109+
- Use `@internal` for classes not meant for public use
110+
- Focus on explaining "why" and edge cases, not "what" (which is in the signature)
111+
112+
### Return Type Formatting
113+
```php
114+
public function example(
115+
string $param,
116+
array $options,
117+
): ReturnType
118+
{
119+
// method body on new line after opening brace
120+
}
121+
```
122+
123+
## Testing Patterns
124+
125+
Tests use **Nette Tester** with `.phpt` extension:
126+
127+
```php
128+
<?php
129+
130+
declare(strict_types=1);
131+
132+
use Tester\Assert;
133+
use Nette\Neon\Neon;
134+
135+
require __DIR__ . '/../bootstrap.php';
136+
137+
test('description of what is being tested', function () {
138+
$result = Neon::decode('hello: world');
139+
Assert::same(['hello' => 'world'], $result);
140+
});
141+
142+
test('handles edge case properly', function () {
143+
Assert::exception(
144+
fn() => Neon::decode('invalid [[['),
145+
Nette\Neon\Exception::class,
146+
'Unexpected %a%', // %a% = any text wildcard
147+
);
148+
});
149+
```
150+
151+
**Key Points**:
152+
- Use `test()` function for each test case (provided by bootstrap)
153+
- First parameter is clear description (no comments needed)
154+
- Use `Assert::same()` for exact comparisons
155+
- Use `Assert::exception()` for exception testing
156+
- Message patterns support `%a%` (any text) placeholders
157+
158+
## NEON Syntax Rules
159+
160+
Understanding these rules is critical when working with the parser:
161+
162+
### Block vs Inline Notation
163+
- **Block notation**: Items on separate lines with consistent indentation
164+
- **Inline notation**: Enclosed in brackets `[]` or braces `{}`, indentation irrelevant
165+
- **Critical limitation**: Block notation CANNOT be used inside inline notation
166+
167+
```neon
168+
# This works
169+
pets: [Cat, Dog]
170+
171+
# This does NOT work
172+
item: [
173+
pets:
174+
- Cat # INVALID!
175+
- Dog
176+
]
177+
```
178+
179+
### Indentation Rules
180+
- Both tabs and spaces allowed for indentation
181+
- **Cannot mix** tabs and spaces (parser will report error)
182+
- Indentation level determines structure hierarchy
183+
- Space after `:` in key-value pairs is **required**
184+
185+
### Mappings (Associative Arrays)
186+
- Format: `key: value` (space after `:` required)
187+
- Alternative: `key=value` (both block and inline)
188+
- Inline: `{key: value, key2: value2}` or multiline with `{}`
189+
190+
### Sequences (Indexed Arrays)
191+
- Block: Lines starting with `- ` (hyphen + space)
192+
- Inline: `[item1, item2]`
193+
- Hyphens cannot be used in inline notation
194+
195+
### String Quoting
196+
Must quote strings containing: `# " ' , : = - [ ] { } ( )`
197+
- Single quotes: Escape quotes by doubling `''`
198+
- Double quotes: JSON escape sequences + `\_` for non-breaking space
199+
- Unquoted: Simple strings without special characters
200+
201+
### Multiline Strings
202+
```neon
203+
'''
204+
content here
205+
'''
206+
```
207+
- Triple quotes on separate lines
208+
- Indentation of first line ignored for all lines
209+
- Use `"""` for escape sequences
210+
211+
### Comments
212+
- Start with `#`
213+
- All characters to the right are ignored
214+
```neon
215+
# Full line comment
216+
key: value # Inline comment
217+
```
218+
219+
### Mixed Arrays
220+
PHP uses same structure for mappings and sequences, so they can be merged:
221+
```neon
222+
- Cat # indexed item 0
223+
street: 742 Evergreen Terrace # key 'street'
224+
- Goldfish # indexed item 1
225+
```
226+
227+
Becomes:
228+
```php
229+
['Cat', 'street' => '742 Evergreen Terrace', 'Goldfish']
230+
```
231+
232+
## Common Patterns
233+
234+
### Parsing NEON
235+
```php
236+
// To PHP value
237+
$value = Nette\Neon\Neon::decode($neonString);
238+
239+
// From file (removes BOM automatically)
240+
$value = Nette\Neon\Neon::decodeFile('config.neon');
241+
242+
// To AST (for manipulation)
243+
$decoder = new Nette\Neon\Decoder;
244+
$node = $decoder->parseToNode($neonString);
245+
```
246+
247+
### Error Handling
248+
All methods throw `Nette\Neon\Exception` on error with position information:
249+
250+
```php
251+
try {
252+
$value = Neon::decode($neonString);
253+
} catch (Nette\Neon\Exception $e) {
254+
// Exception includes line/column position
255+
echo $e->getMessage();
256+
}
257+
```
258+
259+
### Encoding to NEON
260+
```php
261+
// Inline mode (single line)
262+
$neon = Nette\Neon\Neon::encode($value);
263+
264+
// Block mode (multiline)
265+
$neon = Nette\Neon\Neon::encode($value, blockMode: true);
266+
267+
// Custom indentation (default is tab)
268+
$neon = Nette\Neon\Neon::encode($value, blockMode: true, indentation: ' ');
269+
```
270+
271+
### AST Traversal
272+
```php
273+
$traverser = new Nette\Neon\Traverser;
274+
$newNode = $traverser->traverse($node,
275+
enter: function ($node) {
276+
// Process node before children
277+
if ($node instanceof StringNode) {
278+
return new StringNode($node->value . ' modified', $node->start, $node->end);
279+
}
280+
},
281+
leave: function ($node) {
282+
// Process node after children
283+
}
284+
);
285+
```
286+
287+
## Special Features
288+
289+
### JSON Compatibility
290+
**JSON is a subset of NEON** - any valid JSON can be parsed as NEON:
291+
```php
292+
$value = Neon::decode('{"key": "value"}'); // Valid NEON
293+
```
294+
295+
### Entities
296+
NEON entities are function-like structures parsed into `Entity` objects:
297+
298+
```neon
299+
Column(type: int, nulls: yes)
300+
```
301+
302+
Becomes:
303+
```php
304+
new Nette\Neon\Entity('Column', ['type' => 'int', 'nulls' => true])
305+
```
306+
307+
Chained entities use `Entity::Chain`:
308+
```neon
309+
Column(type: int) Field(id: 1)
310+
```
311+
312+
Inside entity parentheses, inline notation rules apply (multiline allowed, commas optional):
313+
```neon
314+
Column(
315+
type: int
316+
nulls: yes
317+
)
318+
```
319+
320+
### Literals (Booleans and Nulls)
321+
322+
**Booleans** - case insensitive variants:
323+
- `true` / `TRUE` / `True`
324+
- `false` / `FALSE` / `False`
325+
- `yes` / `YES` / `Yes`
326+
- `no` / `NO` / `No`
327+
328+
**Nulls** - case insensitive variants:
329+
- `null` / `NULL` / `Null`
330+
- Empty value (just `key:` without value)
331+
332+
### Date Parsing
333+
NEON automatically converts date strings to `DateTimeImmutable`:
334+
```neon
335+
- 2016-06-03 # date
336+
- 2016-06-03 19:00:00 # date & time
337+
- 2016-06-03 19:00:00.1234 # date & microtime
338+
- 2016-06-03 19:00:00 +0200 # date & time & timezone
339+
- 2016-06-03 19:00:00 +02:00 # date & time & timezone
340+
```
341+
342+
### Number Formats
343+
Supports binary, octal, hexadecimal, and scientific notation:
344+
```neon
345+
- 12 # integer
346+
- 12.3 # float
347+
- +1.2e-34 # scientific notation
348+
- 0b11010 # binary
349+
- 0o666 # octal
350+
- 0x7A # hexadecimal
351+
```
352+
353+
## Important Notes
354+
355+
- **This is a parser library** - focus on correct format handling, not business logic
356+
- **Position tracking is critical** - all parsing includes line/column info for error messages
357+
- **Minimal dependencies** - only requires `ext-json`
358+
- **Strict types everywhere** - proper type declarations are essential
359+
- **Final by default** - classes are `final` unless designed for extension
360+
- **Format preservation** - upcoming feature; be careful with changes that affect formatting

0 commit comments

Comments
 (0)