Skip to content

Commit 6bcf30e

Browse files
committed
Expand README with AST details, usage examples, and supported features
1 parent e5a0230 commit 6bcf30e

1 file changed

Lines changed: 102 additions & 36 deletions

File tree

README.md

Lines changed: 102 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,122 @@
11
# maml-php
22

3-
A modern, well‑tested implementation of the [MAML](https://maml.dev) data format for PHP.
3+
[MAML](https://maml.dev) parser for PHP. Includes a full AST with source positions, comment preservation, and pretty printing.
44

5-
- Spec‑accurate parser and pretty serializer
5+
- Spec-accurate parser and serializer
6+
- Full AST with source positions (offset, line, column) on every node
7+
- Comments preserved and attached to nearest nodes
8+
- `printAst()` reconstructs source from AST, including comments
9+
- `errorSnippet()` for user-friendly error messages pointing at source locations
610
- Zero dependencies
7-
- 100% test coverage (classes, methods, lines)
11+
- 100% test coverage
812

913
## Installation
1014

1115
```
1216
composer require maml/maml
1317
```
1418

15-
## Usage
19+
Requires PHP 8.2+ with `mbstring`.
20+
21+
## Quick Start
1622

1723
```php
1824
use Maml\Maml;
1925

20-
$data = Maml::parse('{
21-
project: "MAML"
22-
tags: [
23-
"minimal"
24-
"readable"
25-
]
26-
27-
# A simple nested object
28-
spec: {
29-
version: 1
30-
author: "Anton Medvedev"
31-
}
32-
33-
notes: """
34-
This is a raw multiline string.
35-
Keeps formatting as‑is.
36-
"""
37-
}');
38-
39-
echo $data['project']; // "MAML"
40-
41-
$text = Maml::stringify(['foo' => 'bar', 'list' => [1, 2, 3]]);
42-
/*
43-
{
44-
foo: "bar"
45-
list: [
46-
1
47-
2
48-
3
49-
]
50-
}
51-
*/
26+
// Parse to plain PHP values
27+
$data = Maml::parse('{name: "MAML", version: 1}');
28+
$data['name']; // "MAML"
29+
30+
// Serialize back to MAML
31+
Maml::stringify(['name' => 'MAML', 'version' => 1]);
32+
// {
33+
// name: "MAML"
34+
// version: 1
35+
// }
36+
```
37+
38+
## AST
39+
40+
```php
41+
$source = '{
42+
# Database config
43+
host: "localhost"
44+
port: 5432
45+
}';
46+
47+
$doc = Maml::parseAst($source);
48+
```
49+
50+
Every node has a `type` string and a `span` with start/end positions:
51+
52+
```php
53+
$doc->value->type; // "Object"
54+
$doc->value->span->start->line; // 1
55+
$doc->value->properties[0]->key->value; // "host"
56+
```
57+
58+
### Printing
59+
60+
`printAst()` reconstructs MAML source from an AST, preserving comments and blank lines:
61+
62+
```php
63+
Maml::printAst($doc);
64+
// {
65+
// # Database config
66+
// host: "localhost"
67+
// port: 5432
68+
// }
69+
```
70+
71+
### Converting to plain values
72+
73+
`toValue()` strips AST metadata and returns plain PHP values:
74+
75+
```php
76+
Maml::toValue($doc); // ["host" => "localhost", "port" => 5432]
77+
```
78+
79+
### Error snippets
80+
81+
Point at any AST node in source for user-friendly error messages:
82+
83+
```php
84+
$node = $doc->value->properties[1]->value;
85+
Maml::errorSnippet($source, $node->span->start, 'Port out of range');
86+
// Port out of range on line 4.
87+
//
88+
// port: 5432
89+
// ........^
5290
```
5391

92+
## Node Types
93+
94+
| Node | `type` | `value` | `raw` |
95+
|------------|----------------|----------|-------|
96+
| String | `"String"` | `string` | yes |
97+
| Raw String | `"RawString"` | `string` | yes |
98+
| Integer | `"Integer"` | `int` | yes |
99+
| Float | `"Float"` | `float` | yes |
100+
| Boolean | `"Boolean"` | `bool` | -- |
101+
| Null | `"Null"` | `null` | -- |
102+
| Object | `"Object"` | `properties: Property[]` | -- |
103+
| Array | `"Array"` | `elements: Element[]` | -- |
104+
105+
Object keys are either `IdentifierKey` for bare keys like `host`, or `StringNode` for quoted keys like `"host name"`.
106+
107+
### Comments
108+
109+
Comments are attached to the nearest node:
110+
111+
- **`Property.leadingComments`** / **`Element.leadingComments`** -- comments on lines before
112+
- **`Property.trailingComment`** / **`Element.trailingComment`** -- comment on the same line after
113+
- **`ObjectNode.danglingComments`** / **`ArrayNode.danglingComments`** -- comments inside empty containers or after the last entry
114+
- **`Document.leadingComments`** / **`Document.danglingComments`** -- comments before/after the root value
115+
116+
### Blank lines
117+
118+
`Property.emptyLineBefore` and `Element.emptyLineBefore` are `true` when there is a blank line separating from the previous entry.
119+
54120
## License
55121

56122
[MIT](LICENSE)

0 commit comments

Comments
 (0)