Crates.io | mago-type-syntax |
lib.rs | mago-type-syntax |
version | 1.0.0-beta.3 |
created_at | 2025-04-13 16:01:49.648665+00 |
updated_at | 2025-08-29 16:43:12.20048+00 |
description | Provides core utilities useful for building lexers and parsers within Mago. |
homepage | https://mago.carthage.software |
repository | https://github.com/carthage-software/mago |
max_upload_size | |
id | 1631926 |
size | 185,398 |
A fast, memory-efficient Rust crate for parsing PHP docblock type strings (e.g., from @var
, @param
, @return
tags) into a structured Abstract Syntax Tree (AST).
Originally developed as part of the Mago static analysis toolset, this crate provides the specialized lexer, parser, and AST definitions needed to work with PHP's docblock type syntax, including many Psalm and PHPStan extensions.
lexer::TypeLexer
) and recursive descent parser (parser::construct
internally, exposed via parse_str
) specifically designed for type strings.ast::Type
) representing the type's structure, moving beyond simple string manipulation.mago_span::Span
) information for all AST nodes, relative to the original source file (requires providing the correct initial Span
when parsing).error::ParseError
) with span information on failure.mago_syntax_core
for shared low-level lexing infrastructure like the Input
buffer and utility functions/macros.This parser covers a wide range of standard PHPDoc, PHPStan, and Psalm type syntaxes:
int
, string
, bool
, float
, mixed
, null
, void
, never
, object
, resource
, true
, false
, scalar
, numeric
, array-key
, list
, non-empty-list
, non-empty-string
, class-string
, iterable
, callable
, pure-callable
, pure-closure
, stringable-object
, lowercase-string
, positive-int
, negative-int
, resource
, closed-resource
, open-resource
, numeric-string
, truthy-string
, etc.'string-literal'
, "another one"
123
, -45
, 0x1A
, 0o77
, 0b10
, 123_456
1.23
, -0.5
, .5
, 1.2e3
, 7E-10
literal-int
, literal-string
, non-empty-literal-string
|
(Union), &
(Intersection), ?
(Nullable)(int|string)
?int
, ?array<string>
int|string|null
Countable&Traversable
MyClass::CONST
, MyClass::class
array<KeyType, ValueType>
, array<ValueType>
list<ValueType>
, non-empty-list<ValueType>
iterable<KeyType, ValueType>
, iterable<ValueType>
class-string<ClassName>
, interface-string<InterfaceName>
, etc.My\Collection<ItemType>
self
, static
, parent
(Parsed as Type::Reference
which can have generics)array{key: Type, 'other-key': Type}
list{Type, Type}
array{name: string, age?: int}
array{name: string, ...}
, list{int, ...<int|string>}
Type
as a key, per design choice)callable
, Closure
, pure-callable
, pure-Closure
callable(ParamType1, ParamType2): ReturnType
Closure(): void
callable(int=)
callable(string...)
$var
$var is string ? int : bool
T is not null ? T : mixed
key-of<T>
, value-of<T>
T[K]
int<0, 100>
, int<min, 0>
, int<1, max>
properties-of<T>
, public-properties-of<T>
, protected-properties-of<T>
, private-properties-of<T>
+
/-
Types: +1
, -2.0
(parsed as Type::Posited
, Type::Negated
)This crate does not yet support parsing the following syntax:
int-mask<T>
, int-mask-of<T>
Add Dependencies:
Add mago_type_syntax
to your Cargo.toml
. You will also likely need mago_span
and mago_database
to create the necessary inputs.
[dependencies]
mago_type_syntax = "..."
mago_span = "..."
mago_database = "..."
Parse a Type String:
Use the main entry point mago_type_syntax::parse_str
. You need the type string itself and the Span
indicating its position within the original source file.
use mago_type_syntax::{parse_str, ast::Type};
use mago_span::{Position, Span};
use mago_span::HasSpan;
use mago_database::file::FileId;
fn main() {
let type_string = "array<int, string>|null";
let file_id = FileId::zero(); // Use your actual source identifier
// Calculate the span of the type string within its original file
// Example: if it starts at byte 100 and ends at byte 124
let start_pos = Position::new(file_id, 100);
let end_pos = Position::new(file_id, 100 + type_string.len());
let type_span = Span::new(start_pos, end_pos);
// Parse the string
match parse_str(type_span, type_string) {
Ok(parsed_ast) => {
println!("Successfully parsed AST: {:#?}", parsed_ast);
// You can now traverse or analyze the parsed_ast (Type enum)
match parsed_ast {
Type::Union(union_type) => {
// ... process union ...
println!("Parsed a union type!");
}
Type::Array(array_type) => {
// This won't be hit for the example above
println!("Parsed an array type!");
}
// ... handle other Type variants ...
_ => { println!("Parsed other type variant"); }
}
}
Err(parse_error) => {
eprintln!("Failed to parse type string: {:?}", parse_error);
// Access span via parse_error.span() if needed from HasSpan trait
eprintln!("Error occurred at span: {:?}", parse_error.span());
}
}
}