Ara Parser
A fault-tolerant, recursive-descent parser for Ara Programming Language 🌲
Note: This project is a hard-fork of
php-rust-tools/parserSpecial thanks to the original authors for their work.
Usage
Add ara_parser to your Cargo.toml, and you're good to go!
[]
= "0.1.0"
Example
use parser;
use Charset;
use ColorChoice;
use ReportBuilder;
use Error;
use load_directories;
Syntax
type num = int|float;
final readonly class Collection<T> {
public function __construct(
private vec<T> $items = vec[],
) {}
public function filter((fn(T): bool) $func): Collection<T> {
$result = vec[];
foreach $this->items as $item {
if $func($item) {
$result[] = $item;
}
}
return new Collection::<T>($result);
}
public function map<Tout>((fn(T): Tout) $func): Collection<Tout> {
$result = vec[];
foreach $this->items as $item {
$result[] = $func($item);
}
return new Collection::<Tout>($result);
}
public function sum(): int where T is num {
$result = 0;
foreach $this->items as $item {
$result += $item;
}
$result
}
}
function example(): int {
$collection = new Collection::<num>(vec[
1, 2, 3.0, 4.0, 5, 6, 7.0, 9.0, 10
]);
$collection
->filter(fn(num $n): bool => $n < 8)
->map::<float>(
fn(num $n): float => $n is float ? $n : $n + 0.0
)
->sum()
}
Diffrerences from PHP
Ara syntax is similar to PHP, with a handful of notable differences, the following is a list of the features that Ara does not support from PHP:
-
No opening, or closing tags -
Aradoes not allow opening, or closing tags, so you can't use<?php,<?=,<?, or?>syntax. -
No top-level statement -
Aradoes not allow top-level code, so you can't use code outside of a class, or function. -
No
switch-Aradoes not allowswitchstatements, useiformatchinstead. -
No type casting -
Aradoes not allow type casting, so you can't use(int),(float),(string),(array),(object),(bool)syntax. -
No dynamic identifiers -
Aradoes not allow dynamic identifiers, so you can't use$foo->{$a}();syntax. -
No variable identifiers -
Aradoes not allow variable identifiers, so you can't use$foo->$bar();syntax -
No dynamic variables -
Aradoes not allow dynamic variables, so you can't use${$a}syntax. -
No variable variables -
Aradoes not allow variable-variables, so you can't use$$asyntax. -
No heredoc -
Aradoes not allow heredoc, so you can't use<<<syntax. -
No nowdoc -
Aradoes not allow nowdoc, so you can't use<<<'syntax. -
No interpolation -
Aradoes not allow interpolation, so you can't use${}syntax. -
No string interpolation -
Aradoes not allow string interpolation, so you can't use variables, or arbitrary expressions inside strings. -
No backticks -
Aradoes not allow backticks as an alias forshell_exec, so you can't use$result = `command -arg`;syntax. -
No
include-Aradoes not allowinclude, so you can't useincludeorinclude_oncesyntax. -
No
require-Aradoes not allowrequire, so you can't userequireorrequire_oncesyntax. -
No
eval-Aradoes not alloweval, so you can't useevalsyntax. -
No
die-Aradoes not allowdie, so you can't usediesyntax. ( note:exitis allowed ) -
No goto -
Aradoes not allowgoto, so you can't usegotosyntax. -
No declare(...) -
Aradoes not allowdeclare(...)statements, so you can't usedeclare(ticks=1)syntax. -
No
if(...): ... endif;-Aradoes not allowif(...): ... endif;syntax, useif(...) { ... }instead. -
No
for(...): ... endfor;-Aradoes not allowfor(...): ... endfor;syntax, usefor(...) { ... }instead. -
No
foreach(...): ... endforeach;-Aradoes not allowforeach(...): ... endforeach;syntax, useforeach(...) { ... }instead. -
No
while(...): ... endwhile;-Aradoes not allowwhile(...): ... endwhile;syntax, usewhile(...) { ... }instead. -
No traits -
Aradoes not havetraits.Note: Trait support is planned for the future, once the language is more stable.
-
No
__TRAIT__-Aradoes not allow__TRAIT__, so you can't use__TRAIT__syntax. -
No
__halt_compiler-Aradoes not allow__halt_compiler, so you can't use__halt_compilersyntax. -
No
__COMPILER_HALT_OFFSET__-Aradoes not allow__COMPILER_HALT_OFFSET__, so you can't use__COMPILER_HALT_OFFSET__syntax. -
No
var-Aradoes not allowvarproperties. -
No
global-Aradoes not allowglobalvariables. -
No
staticvariables -Aradoes not allowstaticvariables,staticclass properties, and methods are allowed. -
No
empty-Aradoes not allowemptysyntax for checking if a variable is empty, the type of a variable inAraalways known, so you can useif $foo { ... }for boolean checks,if $foo != '' { ... }for string checks,if $foo != 0 { ... }for integer checks, ..etc. -
No
@-Aradoes not allow@syntax for suppressing errors. -
No
list(...)-Aradoes not allowlist(...)syntax for destructuring arrays. -
No
array(...)-Aradoes not allowarray(...)syntax for creating arrays. -
No
[...]-Aradoes not allow[...]syntax for creating arrays. -
No
isset(...)-Aradoes not allowisset(...)syntax for checking if a variable is set. -
No
unset(...)-Aradoes not allowunset(...)syntax for unsetting a variable. -
No single statement bodies -
Aradoes not allow single statement bodies, so you can't useif (...) $a = 1;syntax, useif (...) { $a = 1; }instead. -
No
callabletype -Aradoes not allowcallabletype, use(fn(T): R)type instead. -
Required types -
Ararequires types for all properties, parameters, and return types, so you can't usefunction foo($a) { ... }syntax, usefunction foo(int $a): void { ... }instead. -
No grouped
use-Aradoes not allow groupedusestatements, so you can't useuse Foo\{Bar, Baz};syntax. -
No
echo,print-Aradoes not allowecho, orprint, usePsl\IO\write(..),Psl\IO\write_line(..),Psl\IO\write_error(..), orPsl\IO\write_error_line(..)instead. -
Required parentheses for
new-Ararequires parentheses fornewexpression, so you can't usenew Foosyntax, usenew Foo()instead.Note: this also applies to
new static,new self, andnew parent, andnew class { ... }usenew static(),new self(),new parent(), andnew class() { ... }instead.
Features
Ara introduces the following features:
-
vec[]-Araallowsvec[]syntax for creating vectors.Note:
vec[]is a type-safe alternative to[], it is compiled to[]at runtime.Examples:
$vec = vec[1, 2, 3]; -
dict[]-Araallowsdict[]syntax for creating dictionaries.Note:
dict[]is a type-safe alternative to[], it is compiled to[]at runtime.Examples:
$dict = dict['foo' => 1, 'bar' => 2]; -
generics -
Araallows generics, so you can usefunction foo<T>(T $value): T { ... }syntax.Note: generics are erased at runtime, and are only used for type checking, and type inference, parameters and return types are compiled to the type they are constrained to, or
mixedif no constraint is provided.Examples:
function foo<T>(T $value): T { $value } function bar(): int { foo::<int>(1) }Generics can also be used with classes, and interfaces as well.
Examples:
interface ContainerInterface<T> { public function get(): T; } final readonly class Container<T> implements ContainerInterface<T> { public function __construct(private T $value) {} public function get(): T { $this->value } }Template constraints can be used to constrain the type of a generic template.
T as sometype, meaning thatTmust be a subtype ofsometype
Examples:
function add<T as int|float>(T $first, T $second): T { $first + $second } function example(): void { $a = add::<int>(1, 2); // OK - int is a subtype of int|float $b = add::<float>(1.0, 2.0); // OK - float is a subtype of int|float $c = add::<int|float>(1, 2.0); // OK - int|float is a subtype of int|float $d = add::<int|float>(1.0, 2); // OK - int|float is a subtype of int|float $e = add::<string>('1', '2'); // ERROR - string is not a subtype of int|float }Template covariance
+T, meaning thatTis covariant
If no variance annotation is provided, the type parameter is invariant.
For example, consider the following:
interface Animal { public function talk(): string; } final class Dog implements Animal { public function talk(): string { 'woof' } } final class Cat implements Animal { public function talk(): string { 'miaow' } } interface Collection<T> extends Iterator<int, T> {} function take_animals(Collection<Animal> $animals): void { foreach $animals as $animal { echo $animal->talk(); } } function take_dogs(Collection<Dog> $dogs): void { take_animals($dogs); // ERROR - expected Collection<Animal>, got Collection<Dog> }This results in an error, because
Collection<Dog>is not a subtype ofCollection<Animal>.We can fix this by making
Collectioncovariant, tellingArathat is it safe to pass subtypes for the templated param T.interface Animal { public function talk(): string; } final class Dog implements Animal { public function talk(): string { 'woof' } } final class Cat implements Animal { public function talk(): string { 'miaow' } } interface Collection<+T> extends Iterator<int, T> {} function take_animals(Collection<Animal> $animals): void { foreach $animals as $animal { echo $animal->talk(); } } function take_dogs(Collection<Dog> $dogs): void { take_animals($dogs); // OK - Collection<Dog> is a subtype of Collection<Animal> } -
(fn(T1, T2, ...): R)type -Arahas(fn(T1, T2, ...): R)type as a type-safe alternative tocallable.Examples:
function map<T, R>(vec<T> $items, (fn(T): R) $fn): vec<R> { $result = vec[]; foreach $items as $item { $result[] = $fn($item); } $result } function example(): void { $items = vec[1, 2, 3]; $result1 = map($items, fn(int $item): int => $item * 2); // OK $result2 = map($items, fn(string $item): string => $item); // Type error } -
type aliases -
Araallows type aliases, so you can usetype scalar = int | float | string | bool;syntax.Examples:
type scalar = int | float | string | bool; function to_scalar(mixed $value): scalar { $value as scalar } -
is-Araallowsissyntax for type checks, so you can use$foo is stringsyntax.Examples:
function to_integer(mixed $value): int { if $value is int { return $value; } throw new InvalidArgumentException('Expected an integer.'); } -
as-Araallowsassyntax for type assertions, so you can use$foo as stringsyntax.Note:
aswould result in a runtime error if the type assertion fails, so it's not recommended to use it in performance critical code.Examples:
function to_integer(mixed $value): int { $value as int }asassertion can also be used within aforeachloop:function example(mixed $items): void { foreach $items as vec<int> as $index => $value { do_something($index, $value); } } -
where-Araadds support forwheretype constraint on methods.Note:
wheretype constraints are enforced at compile time, not at runtime. Note:wherecan only be used on methods, not on functions.Examples:
use Psl\Str; final class Box<T> { public function __construct(public T $value) { } public function length(): int where T is string { Str\length($this->value) } } function example(): void { $box = new Box::<string>('hello world'); $box->length(); // OK $box = new Box::<int>(123); $box->length(); // Type error, method `Box<T = int>::length` requires `T` to be `string`, but `int` was provided. }A method can also have more than one
wheretype constraint.use Psl\Str; final class Foo<T, U> { public function __construct(public T $value, public U $other) { } public function bar(): int where T is string, U is int, { Str\length($this->value) + $this->other } } function example(): void { $foo = new Foo::<string, int>('hello world', 123); $foo->bar(); // OK $foo = new Foo::<int, string>(123, 'hello world'); $foo->bar(); // Type error, method `Foo::<T = int, U = string>` requires `T` to be `string`, but `int` was provided. } -
(1, 2, 3)-Araallows(1, 2, 3)syntax for creating tuples.Note:
(1, 2, 3)is a type-safe alternative to[], it is compiled to[]at runtime.Examples:
$tuple = (1, 2, 3); -
async-Araallowsasyncsyntax for running functions asynchronously.Note:
asyncexpressions are compiled toPsl\Async\run(...)at runtime.Examples:
function do_something(): int { // ... return 1; } function do_something_async(): Awaitable<int> { $result = async do_something(); $result } -
await-Araallowsawaitsyntax for awaiting asynchronous expressions.Note:
awaitexpressions are compiled toPsl\Async\await(...)at runtime.Examples:
function something(): Awaitable<int> { // ... return $result; } function wait_for_something(): int { $result = await something(); $result } -
concurrently-Araallowsconcurrentlysyntax for running asynchronous expressions concurrently.Note:
concurrentlyexpressions are compiled toPsl\Async\concurrently(...)at runtime.Examples:
function something(): Awaitable<int> { // ... return $result; } function something_else(): Awaitable<int> { // ... return $result; } function everything(): (int, int) { ($a, $b) = concurrently { await something(), await something_else(), }; ($a, $b) } -
implict
return-Araallows implicitreturnsyntax for functions, so you can usefunction foo(): int { 1 }syntax.Note: implicit return expressions are compiled to
returnat runtime.Examples:
function foo(): int { 1 } -
optional condition parentheses -
Araallows optional parentheses around conditions, so you can useif $foo { ... }syntax.Note: optional parentheses are compiled to
(...)at runtime.Examples:
if $foo { // ... } else if $bar { // ... } else { // ... } while $foo { // ... } do { // ... } while $foo; for $foo = 1; $foo < 10; $foo++ { // ... } foreach $foo as $bar { // ... } foreach $foo as $bar => $baz { // ... }
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.