Ara
Parser
A fault-tolerant, recursive-descent parser for Ara
Programming Language 🌲
Note: This project is a hard-fork of
php-rust-tools/parser
Special thanks to the original authors for their work.
Usage
Add ara_parser
to your Cargo.toml
, and you're good to go!
[]
= "0.2.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 -
Ara
does not allow opening, or closing tags, so you can't use<?php
,<?=
,<?
, or?>
syntax. -
No top-level statement -
Ara
does not allow top-level code, so you can't use code outside of a class, or function. -
No
switch
-Ara
does not allowswitch
statements, useif
ormatch
instead. -
No type casting -
Ara
does not allow type casting, so you can't use(int)
,(float)
,(string)
,(array)
,(object)
,(bool)
syntax. -
No dynamic identifiers -
Ara
does not allow dynamic identifiers, so you can't use$foo->{$a}();
syntax. -
No variable identifiers -
Ara
does not allow variable identifiers, so you can't use$foo->$bar();
syntax -
No dynamic variables -
Ara
does not allow dynamic variables, so you can't use${$a}
syntax. -
No variable variables -
Ara
does not allow variable-variables, so you can't use$$a
syntax. -
No heredoc -
Ara
does not allow heredoc, so you can't use<<<
syntax. -
No nowdoc -
Ara
does not allow nowdoc, so you can't use<<<'
syntax. -
No interpolation -
Ara
does not allow interpolation, so you can't use${}
syntax. -
No string interpolation -
Ara
does not allow string interpolation, so you can't use variables, or arbitrary expressions inside strings. -
No backticks -
Ara
does not allow backticks as an alias forshell_exec
, so you can't use$result = `command -arg`;
syntax. -
No
include
-Ara
does not allowinclude
, so you can't useinclude
orinclude_once
syntax. -
No
require
-Ara
does not allowrequire
, so you can't userequire
orrequire_once
syntax. -
No
eval
-Ara
does not alloweval
, so you can't useeval
syntax. -
No
die
-Ara
does not allowdie
, so you can't usedie
syntax. ( note:exit
is allowed ) -
No goto -
Ara
does not allowgoto
, so you can't usegoto
syntax. -
No declare(...) -
Ara
does not allowdeclare(...)
statements, so you can't usedeclare(ticks=1)
syntax. -
No
if(...): ... endif;
-Ara
does not allowif(...): ... endif;
syntax, useif(...) { ... }
instead. -
No
for(...): ... endfor;
-Ara
does not allowfor(...): ... endfor;
syntax, usefor(...) { ... }
instead. -
No
foreach(...): ... endforeach;
-Ara
does not allowforeach(...): ... endforeach;
syntax, useforeach(...) { ... }
instead. -
No
while(...): ... endwhile;
-Ara
does not allowwhile(...): ... endwhile;
syntax, usewhile(...) { ... }
instead. -
No traits -
Ara
does not havetrait
s.Note: Trait support is planned for the future, once the language is more stable.
-
No
__TRAIT__
-Ara
does not allow__TRAIT__
, so you can't use__TRAIT__
syntax. -
No
__halt_compiler
-Ara
does not allow__halt_compiler
, so you can't use__halt_compiler
syntax. -
No
__COMPILER_HALT_OFFSET__
-Ara
does not allow__COMPILER_HALT_OFFSET__
, so you can't use__COMPILER_HALT_OFFSET__
syntax. -
No
var
-Ara
does not allowvar
properties. -
No
global
-Ara
does not allowglobal
variables. -
No
static
variables -Ara
does not allowstatic
variables,static
class properties, and methods are allowed. -
No
empty
-Ara
does not allowempty
syntax for checking if a variable is empty, the type of a variable inAra
always known, so you can useif $foo { ... }
for boolean checks,if $foo != '' { ... }
for string checks,if $foo != 0 { ... }
for integer checks, ..etc. -
No
@
-Ara
does not allow@
syntax for suppressing errors. -
No
list(...)
-Ara
does not allowlist(...)
syntax for destructuring arrays. -
No
array(...)
-Ara
does not allowarray(...)
syntax for creating arrays. -
No
[...]
-Ara
does not allow[...]
syntax for creating arrays. -
No
isset(...)
-Ara
does not allowisset(...)
syntax for checking if a variable is set. -
No
unset(...)
-Ara
does not allowunset(...)
syntax for unsetting a variable. -
No single statement bodies -
Ara
does not allow single statement bodies, so you can't useif (...) $a = 1;
syntax, useif (...) { $a = 1; }
instead. -
No
callable
type -Ara
does not allowcallable
type, use(fn(T): R)
type instead. -
Required types -
Ara
requires 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
-Ara
does not allow groupeduse
statements, so you can't useuse Foo\{Bar, Baz};
syntax. -
No
echo
,print
-Ara
does not allowecho
, orprint
, usePsl\IO\write(..)
,Psl\IO\write_line(..)
,Psl\IO\write_error(..)
, orPsl\IO\write_error_line(..)
instead. -
Required parentheses for
new
-Ara
requires parentheses fornew
expression, so you can't usenew Foo
syntax, 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[]
-Ara
allowsvec[]
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[]
-Ara
allowsdict[]
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 -
Ara
allows 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
mixed
if 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 thatT
must 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 thatT
is 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
Collection
covariant, tellingAra
that 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 -Ara
has(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 -
Ara
allows 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
-Ara
allowsis
syntax for type checks, so you can use$foo is string
syntax.Examples:
function to_integer(mixed $value): int { if $value is int { return $value; } throw new InvalidArgumentException('Expected an integer.'); }
-
as
-Ara
allowsas
syntax for type assertions, so you can use$foo as string
syntax.Note:
as
would 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 }
as
assertion can also be used within aforeach
loop:function example(mixed $items): void { foreach $items as vec<int> as $index => $value { do_something($index, $value); } }
-
where
-Ara
adds support forwhere
type constraint on methods.Note:
where
type constraints are enforced at compile time, not at runtime. Note:where
can 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
where
type 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)
-Ara
allows(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
-Ara
allowsasync
syntax for running functions asynchronously.Note:
async
expressions 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
-Ara
allowsawait
syntax for awaiting asynchronous expressions.Note:
await
expressions are compiled toPsl\Async\await(...)
at runtime.Examples:
function something(): Awaitable<int> { // ... return $result; } function wait_for_something(): int { $result = await something(); $result }
-
concurrently
-Ara
allowsconcurrently
syntax for running asynchronous expressions concurrently.Note:
concurrently
expressions 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
-Ara
allows implicitreturn
syntax for functions, so you can usefunction foo(): int { 1 }
syntax.Note: implicit return expressions are compiled to
return
at runtime.Examples:
function foo(): int { 1 }
-
optional condition parentheses -
Ara
allows 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 { // ... }
-
nonnull
type -Ara
adds support fornonnull
type.Examples:
function example(nonnull $value): void { // ... }
-
resource
type -Ara
adds support forresource
type.Examples:
function example(resource $value): void { // ... }
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.