Mago Type Syntax
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.
Features
- Dedicated Lexer & Parser: Includes a performant lexer (
lexer::TypeLexer) and recursive descent parser (parser::constructinternally, exposed viaparse_str) specifically designed for type strings. - Structured AST: Produces a detailed Abstract Syntax Tree (
ast::Type) representing the type's structure, moving beyond simple string manipulation. - Accurate Spans: Preserves accurate source location (
mago_span::Span) information for all AST nodes, relative to the original source file (requires providing the correct initialSpanwhen parsing). - Performance: Designed with performance and memory efficiency in mind.
- Error Reporting: Provides structured error types (
error::ParseError) with span information on failure. - Core Utilities: Relies on
mago_syntax_corefor shared low-level lexing infrastructure like theInputbuffer and utility functions/macros.
Supported Syntax (Examples)
This parser covers a wide range of standard PHPDoc, PHPStan, and Psalm type syntaxes:
- Keywords:
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. - Literals:
- Strings:
'string-literal',"another one" - Integers:
123,-45,0x1A,0o77,0b10,123_456 - Floats:
1.23,-0.5,.5,1.2e3,7E-10
- Strings:
- Unspecified Literals:
literal-int,literal-string,literal-float,non-empty-literal-string - Operators:
|(Union),&(Intersection),?(Nullable) - Structure:
- Parentheses:
(int|string) - Nullables:
?int,?array<string>
- Unions:
int|string|null - Intersections:
Countable&Traversable - Member References:
MyClass::CONST,MyClass::class
- Parentheses:
- Generics:
array<KeyType, ValueType>,array<ValueType>list<ValueType>,non-empty-list<ValueType>iterable<KeyType, ValueType>,iterable<ValueType>class-string<ClassName>,interface-string<InterfaceName>, etc.- User types:
My\Collection<ItemType> self,static,parent(Parsed asType::Referencewhich can have generics)
- Array Shapes:
array{key: Type, 'other-key': Type}list{Type, Type}- Optional keys:
array{name: string, age?: int} - Unsealed shapes:
array{name: string, ...},list{int, ...<int|string>} - (Note: Supports any parsed
Typeas a key, per design choice)
- Callables:
callable,Closure,pure-callable,pure-Closurecallable(ParamType1, ParamType2): ReturnTypeClosure(): void- Optional params:
callable(int=) - Variadic params:
callable(string...)
- Variables:
$var - Conditionals:
$var is string ? int : boolT is not null ? T : mixed
KeyOf/ValueOf:key-of<T>,value-of<T>- Indexed Access:
T[K] - Int Ranges:
int<0, 100>,int<min, 0>,int<1, max> - Int Masks:
int-mask<1, 2, 4>,int-mask-of<Permissions::*> - Properties Of:
properties-of<T>,public-properties-of<T>,protected-properties-of<T>,private-properties-of<T> - Unary
+/-Types:+1,-2.0(parsed asType::Posited,Type::Negated)
Usage
-
Add Dependencies:
Add
mago_type_syntaxto yourCargo.toml. You will also likely needmago_spanandmago_databaseto create the necessary inputs.[] = "..." = "..." = "..." -
Parse a Type String: Use the main entry point
mago_type_syntax::parse_str. You need the type string itself and theSpanindicating its position within the original source file.use ; use ; use HasSpan; use FileId;