Skip to content

Commit a3a9ec7

Browse files
committed
add initial implementation of conditional indexer operator
1 parent bc71d73 commit a3a9ec7

File tree

12 files changed

+109
-30
lines changed

12 files changed

+109
-30
lines changed

Core/EVIL.Grammar/AST/Expressions/IndexerExpression.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,19 @@ public sealed class IndexerExpression : Expression
1010
public Expression Indexable { get; }
1111
public Expression KeyExpression { get; }
1212

13+
public bool IsConditional { get; }
1314
public bool WillBeAssigned { get; }
1415

15-
public IndexerExpression(Expression indexable, Expression keyExpression, bool willBeAssigned)
16+
public IndexerExpression(
17+
Expression indexable,
18+
Expression keyExpression,
19+
bool isConditional,
20+
bool willBeAssigned)
1621
{
1722
Indexable = indexable;
1823
KeyExpression = keyExpression;
1924

25+
IsConditional = isConditional;
2026
WillBeAssigned = willBeAssigned;
2127

2228
Reparent(Indexable, KeyExpression);

Core/EVIL.Grammar/Parsing/Expressions/Parser.IndexerExpression.cs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,26 @@ private IndexerExpression IndexerExpression(Expression indexable)
1111
{
1212
int line, col;
1313
Expression indexer;
14+
var isConditional = false;
1415

15-
if (CurrentToken.Type == TokenType.Dot)
16+
if (CurrentToken.Type == TokenType.Dot || CurrentToken.Type == TokenType.Elvis)
1617
{
17-
(line, col) = Match(Token.Dot);
18+
if (CurrentToken.Type == TokenType.Dot)
19+
{
20+
(line, col) = Match(Token.Dot);
21+
}
22+
else if (CurrentToken.Type == TokenType.Elvis)
23+
{
24+
(line, col) = Match(Token.Elvis);
25+
isConditional = true;
26+
}
27+
else
28+
{
29+
throw new ParserException(
30+
$"Unexpected token '{CurrentToken}'.",
31+
(CurrentToken.Line, CurrentToken.Column)
32+
);
33+
}
1834

1935
var identifier = Identifier();
2036
indexer = new StringConstant(identifier.Name, false)
@@ -40,7 +56,11 @@ private IndexerExpression IndexerExpression(Expression indexable)
4056
Match(Token.RBracket);
4157
}
4258

43-
return new IndexerExpression(indexable, indexer, CurrentToken.Type == TokenType.Assign)
59+
return new IndexerExpression(
60+
indexable,
61+
indexer,
62+
isConditional,
63+
CurrentToken.Type == TokenType.Assign)
4464
{ Line = line, Column = col };
4565
}
4666
}

Core/EVIL.Grammar/Parsing/Expressions/Parser.PostfixExpression.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public partial class Parser
1212
TokenType.LParenthesis,
1313
TokenType.LBracket,
1414
TokenType.Dot,
15+
TokenType.Elvis,
1516
TokenType.DoubleColon,
1617
TokenType.Increment,
1718
TokenType.Decrement,
@@ -47,7 +48,7 @@ private Expression PostfixExpression()
4748
{
4849
node = InvocationExpression(node);
4950
}
50-
else if (token.Type == TokenType.LBracket || token.Type == TokenType.Dot)
51+
else if (token.Type == TokenType.LBracket || token.Type == TokenType.Dot || token.Type == TokenType.Elvis)
5152
{
5253
node = IndexerExpression(node);
5354
}

Core/EVIL.Grammar/Parsing/Statements/Parser.EnsureStatement.cs

Lines changed: 0 additions & 6 deletions
This file was deleted.

Core/EVIL.Lexical/Lexer.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ public void NextToken()
119119
Advance();
120120
State.CurrentToken = Token.DoubleQuestionMark with { Line = line, Column = col };
121121
break;
122+
case '?' when Peek() == '.':
123+
Advance();
124+
State.CurrentToken = Token.Elvis with { Line = line, Column = col };
125+
break;
122126
case '?':
123127
State.CurrentToken = Token.QuestionMark with { Line = line, Column = col };
124128
break;

Core/EVIL.Lexical/Token.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ public override int GetHashCode()
9494
public static readonly Token AsNumber = new(TokenType.AsNumber, TokenClass.Operator, "$");
9595
public static readonly Token AsString = new(TokenType.AsString, TokenClass.Operator, "@");
9696
public static readonly Token QuestionMark = new(TokenType.QuestionMark, TokenClass.Operator, "?");
97+
public static readonly Token Elvis = new(TokenType.Elvis, TokenClass.Operator, "?.");
9798
public static readonly Token DoubleQuestionMark = new(TokenType.DoubleQuestionMark, TokenClass.Operator, "??");
9899
public static readonly Token Colon = new(TokenType.Colon, TokenClass.Operator, ":");
99100
public static readonly Token DoubleColon = new(TokenType.DoubleColon, TokenClass.Operator, "::");

Core/EVIL.Lexical/TokenType.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public enum TokenType
5757

5858
Comma,
5959
Colon,
60+
Elvis,
6061
DoubleColon,
6162
Semicolon,
6263
LParenthesis,

VirtualMachine/EVIL.Ceres.Runtime/ScriptBuiltins/dofile.vil

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
val fail = core.fail;
2-
val get_text = fs.file.get_text;
3-
val file_exists = fs.file.exists;
4-
val dname = fs.path.get_dname;
5-
val fname = fs.path.get_fname;
6-
val strace = debug.strace;
7-
val path_cmb = fs.path.cmb;
8-
val evil_compile = evil.compile;
1+
val get_text = fs?.file?.get_text ?? throw("dofile: fs.file.get_text missing");
2+
val file_exists = fs?.file?.exists ?? throw("dofile: fs.file.exists missing");
3+
val dname = fs?.path?.get_dname ?? throw("dofile: fs.path.get_dname missing");
4+
val fname = fs?.path?.get_fname ?? throw("dofile: fs.path.get_fname missing");
5+
val strace = debug?.strace ?? throw("dofile: debug.strace missing");
6+
val path_cmb = fs?.path?.cmb ?? throw("dofile: fs.path.cmb missing");
7+
val evil_compile = evil?.compile ?? throw("dofile: evil.compile missing");
98

109
val attempted_imports = array { };
1110

VirtualMachine/EVIL.Ceres.Runtime/ScriptBuiltins/require.vil

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
val get_text = fs.file.get_text;
2-
val file_exists = fs.file.exists;
3-
val dir_exists = fs.dir.exists;
4-
val dname = fs.path.get_dname;
5-
val fname = fs.path.get_fname;
6-
val strace = debug.strace;
7-
val path_cmb = fs.path.cmb;
8-
val evil_compile = evil.compile;
1+
val get_text = fs?.file?.get_text ?? throw("require: fs.file.get_text missing");
2+
val file_exists = fs?.file?.exists ?? throw("require: fs.file.exists missing");
3+
val dir_exists = fs?.dir?.exists ?? throw("require: fs.sue.exists missing");
4+
val dname = fs?.path?.get_dname ?? throw("require: fs.path.get_dname missing");
5+
val fname = fs?.path?.get_fname ?? throw("require: fs.path.get_fname missing");
6+
val strace = debug?.strace ?? throw("require: debug.strace missing");
7+
val path_cmb = fs?.path?.cmb ?? throw("require: fs.path.cmb missing");
8+
val evil_compile = evil?.compile ?? throw("require: evil.compile missing");
99

1010
val attempted_imports = array { };
1111

VirtualMachine/EVIL.Ceres/TranslationEngine/Compiler.Expression.Indexer.cs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,28 @@ public partial class Compiler
77
{
88
public override void Visit(IndexerExpression indexerExpression)
99
{
10-
Visit(indexerExpression.Indexable);
11-
Visit(indexerExpression.KeyExpression);
12-
Chunk.CodeGenerator.Emit(OpCode.INDEX);
10+
if (indexerExpression.IsConditional)
11+
{
12+
var endIndexerLabel = Chunk.CreateLabel();
13+
var indexLabel = Chunk.CreateLabel();
14+
15+
Visit(indexerExpression.Indexable);
16+
Chunk.CodeGenerator.Emit(OpCode.DUP);
17+
Chunk.CodeGenerator.Emit(OpCode.LDNIL);
18+
Chunk.CodeGenerator.Emit(OpCode.CEQ);
19+
Chunk.CodeGenerator.Emit(OpCode.FJMP, indexLabel);
20+
Chunk.CodeGenerator.Emit(OpCode.LDNIL);
21+
Chunk.CodeGenerator.Emit(OpCode.JUMP, endIndexerLabel);
22+
Chunk.UpdateLabel(indexLabel, Chunk.CodeGenerator.IP);
23+
Visit(indexerExpression.KeyExpression);
24+
Chunk.CodeGenerator.Emit(OpCode.INDEX);
25+
Chunk.UpdateLabel(endIndexerLabel, Chunk.CodeGenerator.IP);
26+
}
27+
else
28+
{
29+
Visit(indexerExpression.Indexable);
30+
Visit(indexerExpression.KeyExpression);
31+
Chunk.CodeGenerator.Emit(OpCode.INDEX);
32+
}
1333
}
1434
}

0 commit comments

Comments
 (0)