Skip to content

Commit 455d999

Browse files
committed
Implement optional types
1 parent 9532b2a commit 455d999

17 files changed

Lines changed: 294 additions & 42 deletions

File tree

src/Parser/Ast/TypeReferenceNode.php

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ final class TypeReferenceNode implements \JsonSerializable
3232
{
3333
private function __construct(
3434
public readonly string $name,
35-
public readonly bool $isArray
35+
public readonly bool $isArray,
36+
public readonly bool $isOptional
3637
) {
3738
}
3839

@@ -52,6 +53,14 @@ public static function fromString(string $typeReferenceAsString): self
5253
public static function fromTokens(\Iterator $tokens): self
5354
{
5455
Scanner::skipSpaceAndComments($tokens);
56+
57+
if (Scanner::type($tokens) === TokenType::QUESTIONMARK) {
58+
$isOptional = true;
59+
Scanner::skipOne($tokens);
60+
} else {
61+
$isOptional = false;
62+
}
63+
5564
Scanner::assertType($tokens, TokenType::STRING);
5665

5766
$name = Scanner::value($tokens);
@@ -68,7 +77,8 @@ public static function fromTokens(\Iterator $tokens): self
6877

6978
return new self(
7079
name: $name,
71-
isArray: $isArray
80+
isArray: $isArray,
81+
isOptional: $isOptional
7282
);
7383
}
7484

@@ -78,7 +88,8 @@ public function jsonSerialize(): mixed
7888
'type' => 'TypeReferenceNode',
7989
'payload' => [
8090
'name' => $this->name,
81-
'isArray' => $this->isArray
91+
'isArray' => $this->isArray,
92+
'isOptional' => $this->isOptional
8293
]
8394
];
8495
}

src/Target/Php/Transpiler/TypeReference/TypeReferenceTranspiler.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ public function __construct(
4343
public function transpile(TypeReferenceNode $typeReferenceNode): string
4444
{
4545
$type = $this->scope->resolveTypeReference($typeReferenceNode);
46-
47-
return match ($type::class) {
46+
$phpTypeReference = match ($type::class) {
4847
NumberType::class => 'int|float',
4948
StringType::class => 'string',
5049
BooleanType::class => 'bool',
@@ -54,5 +53,12 @@ public function transpile(TypeReferenceNode $typeReferenceNode): string
5453
StructType::class => $this->strategy->getPhpTypeReferenceForStructType($type, $typeReferenceNode),
5554
default => $this->strategy->getPhpTypeReferenceForCustomType($type, $typeReferenceNode)
5655
};
56+
57+
return $typeReferenceNode->isOptional
58+
? match ($phpTypeReference) {
59+
'int|float' => 'null|int|float',
60+
default => '?' . $phpTypeReference
61+
}
62+
: $phpTypeReference;
5763
}
5864
}

test/Integration/Examples/Component/Component.ast.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"type": "TypeReferenceNode",
1515
"payload": {
1616
"name": "string",
17-
"isArray": false
17+
"isArray": false,
18+
"isOptional": false
1819
}
1920
}
2021
},
@@ -24,7 +25,8 @@
2425
"type": "TypeReferenceNode",
2526
"payload": {
2627
"name": "string",
27-
"isArray": false
28+
"isArray": false,
29+
"isOptional": false
2830
}
2931
}
3032
},
@@ -34,7 +36,8 @@
3436
"type": "TypeReferenceNode",
3537
"payload": {
3638
"name": "string",
37-
"isArray": false
39+
"isArray": false,
40+
"isOptional": false
3841
}
3942
}
4043
}

test/Integration/Examples/ComponentWithNesting/ComponentWithNesting.ast.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"type": "TypeReferenceNode",
1515
"payload": {
1616
"name": "string",
17-
"isArray": false
17+
"isArray": false,
18+
"isOptional": false
1819
}
1920
}
2021
},
@@ -24,7 +25,8 @@
2425
"type": "TypeReferenceNode",
2526
"payload": {
2627
"name": "string",
27-
"isArray": false
28+
"isArray": false,
29+
"isOptional": false
2830
}
2931
}
3032
},
@@ -34,7 +36,8 @@
3436
"type": "TypeReferenceNode",
3537
"payload": {
3638
"name": "string",
37-
"isArray": false
39+
"isArray": false,
40+
"isOptional": false
3841
}
3942
}
4043
},
@@ -44,7 +47,8 @@
4447
"type": "TypeReferenceNode",
4548
"payload": {
4649
"name": "string",
47-
"isArray": false
50+
"isArray": false,
51+
"isOptional": false
4852
}
4953
}
5054
}

test/Integration/Examples/Expression/Expression.ast.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"type": "TypeReferenceNode",
1515
"payload": {
1616
"name": "number",
17-
"isArray": false
17+
"isArray": false,
18+
"isOptional": false
1819
}
1920
}
2021
},
@@ -24,7 +25,8 @@
2425
"type": "TypeReferenceNode",
2526
"payload": {
2627
"name": "number",
27-
"isArray": false
28+
"isArray": false,
29+
"isOptional": false
2830
}
2931
}
3032
}

test/Integration/Examples/ImportExport/ImportExport.ast.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"type": "TypeReferenceNode",
3030
"payload": {
3131
"name": "string",
32-
"isArray": false
32+
"isArray": false,
33+
"isOptional": false
3334
}
3435
}
3536
},
@@ -39,15 +40,16 @@
3940
"type": "TypeReferenceNode",
4041
"payload": {
4142
"name": "Link",
42-
"isArray": false
43+
"isArray": false,
44+
"isOptional": false
4345
}
4446
}
4547
},
4648
{
4749
"name": "button",
4850
"type": {
4951
"type": "TypeReferenceNode",
50-
"payload": { "name": "Button", "isArray": false }
52+
"payload": { "name": "Button", "isArray": false, "isOptional": false }
5153
}
5254
}
5355
],

test/Integration/Examples/Match/Match.ast.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"type": "TypeReferenceNode",
1515
"payload": {
1616
"name": "ButtonType",
17-
"isArray": false
17+
"isArray": false,
18+
"isOptional": false
1819
}
1920
}
2021
},
@@ -24,7 +25,8 @@
2425
"type": "TypeReferenceNode",
2526
"payload": {
2627
"name": "slot",
27-
"isArray": false
28+
"isArray": false,
29+
"isOptional": false
2830
}
2931
}
3032
}

test/Integration/Examples/Struct/Struct.ast.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
"name": "href",
1313
"type": {
1414
"type": "TypeReferenceNode",
15-
"payload": { "name": "string", "isArray": false }
15+
"payload": { "name": "string", "isArray": false, "isOptional": false }
1616
}
1717
},
1818
{
1919
"name": "target",
2020
"type": {
2121
"type": "TypeReferenceNode",
22-
"payload": { "name": "string", "isArray": false }
22+
"payload": { "name": "string", "isArray": false, "isOptional": false }
2323
}
2424
}
2525
]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export struct Image {
2+
src: string
3+
alt: string
4+
title: ?string
5+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"type": "ModuleNode",
3+
"payload": {
4+
"imports": {},
5+
"exports": [
6+
{
7+
"type": "Struct",
8+
"payload": {
9+
"structName": "Image",
10+
"propertyDeclarations": [
11+
{
12+
"name": "src",
13+
"type": {
14+
"type": "TypeReferenceNode",
15+
"payload": { "name": "string", "isArray": false, "isOptional": false }
16+
}
17+
},
18+
{
19+
"name": "alt",
20+
"type": {
21+
"type": "TypeReferenceNode",
22+
"payload": { "name": "string", "isArray": false, "isOptional": false }
23+
}
24+
},
25+
{
26+
"name": "title",
27+
"type": {
28+
"type": "TypeReferenceNode",
29+
"payload": { "name": "string", "isArray": false, "isOptional": true }
30+
}
31+
}
32+
]
33+
}
34+
}
35+
]
36+
}
37+
}

0 commit comments

Comments
 (0)