Skip to content

Commit f8d8cd8

Browse files
author
Минин Степан Александрович
committed
refactor(TopDownParser): Grammar
- Replaced null returns with `ParserException` for better error handling - Extracted `CurrentIsStatement` for cleaner parsing logic - Updated grammar to improve clarity on `MemberExpression` Closes #233
1 parent b8e4d6b commit f8d8cd8

5 files changed

Lines changed: 111 additions & 93 deletions

File tree

src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/TopDownParser.Declaration.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ private Declaration Declaration()
3030
return TypeDeclaration();
3131
}
3232

33-
return null!;
33+
throw new ParserException(nameof(Declaration), _tokens.Current);
3434
}
3535

3636
/// <summary>
@@ -186,7 +186,7 @@ private TypeValue TypeValue()
186186
return WithSuffix(new ObjectTypeValue(propertyTypes));
187187
}
188188

189-
return null!;
189+
throw new ParserException(nameof(TypeValue), _tokens.Current);
190190
}
191191

192192
/// <summary>

src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/TopDownParser.Expression.cs

Lines changed: 78 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System.Globalization;
22
using System.Text.RegularExpressions;
3-
using HydraScript.Domain.FrontEnd.Lexer;
43
using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions;
54
using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions.AccessExpressions;
65
using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions.ComplexLiterals;
@@ -12,7 +11,7 @@ public partial class TopDownParser
1211
{
1312
/// <summary>
1413
/// Expression -> CastExpression | AssignmentExpression
15-
/// AssignmentExpression -> MemberExpression "Operator"? '=' Expression
14+
/// AssignmentExpression -> MemberExpression '=' Expression
1615
/// </summary>
1716
private Expression Expression()
1817
{
@@ -28,7 +27,8 @@ private Expression Expression()
2827
}
2928

3029
/// <summary>
31-
/// CallExpression -> MemberExpression Arguments (Arguments | '[' Expression ']' | '.' 'Ident')*
30+
/// CallExpression -> MemberExpression Arguments
31+
/// Arguments -> '(' (Expression ',')* ')'
3232
/// </summary>
3333
private Expression CallExpression()
3434
{
@@ -46,55 +46,49 @@ private Expression CallExpression()
4646
}
4747

4848
var rp = Expect("RightParen");
49-
return new CallExpression((member as MemberExpression)!, expressions)
49+
return new CallExpression(member, expressions)
5050
{ Segment = member.Segment + rp.Segment };
5151
}
5252

53-
return member;
53+
return member.Empty() && !CurrentIs("Assign") ? member.Id : member;
5454
}
5555

5656
/// <summary>
57-
/// MemberExpression -> "Ident" ('[' Expression ']' | '.' 'Ident')*
57+
/// MemberExpression -> Var ('[' Expression ']' | '.' 'Ident')*
5858
/// </summary>
59-
private Expression MemberExpression()
59+
private MemberExpression MemberExpression()
6060
{
61-
var primary = PrimaryExpression();
62-
63-
if (!CurrentIs("LeftBracket") && !CurrentIs("Dot") &&
64-
!CurrentIs("Assign") && !CurrentIs("LeftParen"))
65-
return primary;
66-
67-
var identRef = (primary as IdentifierReference)!;
68-
var accessChain = new List<AccessExpression>();
61+
var memberRoot = Var();
62+
var accessChain = new LinkedList<AccessExpression>();
6963
while (CurrentIs("LeftBracket") || CurrentIs("Dot"))
7064
{
71-
Token access;
7265
if (CurrentIs("LeftBracket"))
7366
{
74-
access = Expect("LeftBracket");
75-
var lb = access.Segment;
67+
var lb = Expect("LeftBracket").Segment;
7668
var expr = Expression();
7769
var rb = Expect("RightBracket").Segment;
78-
accessChain.Add(
79-
new IndexAccess(expr, accessChain.LastOrDefault()) { Segment = lb + rb });
70+
accessChain.AddLast(
71+
new IndexAccess(expr, accessChain.Last?.Value)
72+
{ Segment = lb + rb });
8073
}
8174
else if (CurrentIs("Dot"))
8275
{
83-
access = Expect("Dot");
84-
var identToken = Expect("Ident");
85-
var idRef = new IdentifierReference(identToken.Value)
86-
{ Segment = identToken.Segment };
87-
accessChain.Add(
88-
new DotAccess(idRef, accessChain.LastOrDefault()) { Segment = access.Segment });
76+
var access = Expect("Dot");
77+
var propToken = Expect("Ident");
78+
var propIdent = new IdentifierReference(propToken.Value)
79+
{ Segment = propToken.Segment };
80+
accessChain.AddLast(
81+
new DotAccess(propIdent, accessChain.Last?.Value)
82+
{ Segment = access.Segment });
8983
}
9084
}
9185

9286
return new MemberExpression(
93-
identRef,
94-
accessChain.FirstOrDefault(),
95-
tail: accessChain.LastOrDefault())
87+
memberRoot,
88+
accessChain.First?.Value,
89+
tail: accessChain.Last?.Value)
9690
{
97-
Segment = identRef.Segment
91+
Segment = memberRoot.Segment
9892
};
9993
}
10094

@@ -285,18 +279,14 @@ private Expression UnaryExpression()
285279
}
286280

287281
/// <summary>
288-
/// LeftHandSideExpression -> MemberExpression | CallExpression
282+
/// LeftHandSideExpression -> PrimaryExpression
283+
/// ParenthesizedExpression
284+
/// ComplexLiteral
285+
/// MemberExpression
286+
/// CallExpression
287+
/// ParenthesizedExpression -> '(' Expression ')'
289288
/// </summary>
290289
private Expression LeftHandSideExpression()
291-
{
292-
return CallExpression();
293-
}
294-
295-
/// <summary>
296-
/// PrimaryExpression -> "Ident" | EnvVar | Literal | '(' Expression ')' | ObjectLiteral | ArrayLiteral
297-
/// EnvVar -> '$' "Ident"
298-
/// </summary>
299-
private Expression PrimaryExpression()
300290
{
301291
if (CurrentIs("LeftParen"))
302292
{
@@ -306,41 +296,48 @@ private Expression PrimaryExpression()
306296
return expr;
307297
}
308298

309-
if (CurrentIs("Ident"))
299+
if (CurrentIs("LeftCurl") || CurrentIs("LeftBracket"))
310300
{
311-
var ident = Expect("Ident");
312-
return new IdentifierReference(ident.Value)
313-
{
314-
Segment = ident.Segment
315-
};
301+
return ComplexLiteral();
316302
}
317303

318-
if (CurrentIsOperator("$"))
304+
if (CurrentIs("Ident") || CurrentIsOperator("$"))
319305
{
320-
var dollar = Expect("Operator");
321-
var ident = Expect("Ident");
322-
return new EnvVarReference(ident.Value)
323-
{
324-
Segment = dollar.Segment + ident.Segment
325-
};
306+
return CallExpression();
326307
}
327308

328-
if (CurrentIsLiteral())
329-
{
330-
return LiteralNode();
331-
}
309+
return PrimaryExpression();
310+
}
332311

333-
if (CurrentIs("LeftCurl"))
334-
{
335-
return ObjectLiteral();
336-
}
312+
/// <summary>
313+
/// PrimaryExpression -> Var | Literal
314+
/// </summary>
315+
private PrimaryExpression PrimaryExpression()
316+
{
317+
return LiteralNode();
318+
}
337319

338-
if (CurrentIs("LeftBracket"))
320+
/// <summary>
321+
/// Var -> "Ident" | EnvVar
322+
/// EnvVar -> '$' "Ident"
323+
/// </summary>
324+
private IdentifierReference Var()
325+
{
326+
if (CurrentIs("Ident"))
339327
{
340-
return ArrayLiteral();
328+
var ident = Expect("Ident");
329+
return new IdentifierReference(ident.Value)
330+
{
331+
Segment = ident.Segment
332+
};
341333
}
342334

343-
return null!;
335+
var dollar = Expect("Operator");
336+
var envIdent = Expect("Ident");
337+
return new EnvVarReference(envIdent.Value)
338+
{
339+
Segment = dollar.Segment + envIdent.Segment
340+
};
344341
}
345342

346343
/// <summary>
@@ -379,12 +376,27 @@ private Literal LiteralNode()
379376
CultureInfo.InvariantCulture),
380377
segment),
381378
"BooleanLiteral" => Literal.Boolean(value: bool.Parse(Expect("BooleanLiteral").Value), segment),
382-
_ => throw new ParserException("There are no more supported literals")
379+
_ => throw new ParserException("Literal", _tokens.Current)
383380
};
384381
}
385382

383+
/// <summary>
384+
/// ComplexLiteral -> ObjectLiteral | ArrayLiteral
385+
/// </summary>
386+
private ComplexLiteral ComplexLiteral()
387+
{
388+
if (CurrentIs("LeftCurl"))
389+
{
390+
return ObjectLiteral();
391+
}
392+
393+
return ArrayLiteral();
394+
}
395+
386396
/// <summary>
387397
/// ObjectLiteral -> '{' PropertyDefinitionList '}'
398+
/// PropertyDefinitionList -> (FieldProperty ';')*
399+
/// FieldProperty -> "Ident" ':' Expression
388400
/// </summary>
389401
private ObjectLiteral ObjectLiteral()
390402
{
@@ -409,6 +421,7 @@ private ObjectLiteral ObjectLiteral()
409421

410422
/// <summary>
411423
/// ArrayLiteral -> '[' ElementList ']'
424+
/// ElementList -> (Expression ',')*
412425
/// </summary>
413426
private ArrayLiteral ArrayLiteral()
414427
{

src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/TopDownParser.Statement.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ private Statement Statement()
5454
if (CurrentIs("Input"))
5555
return InputStatement();
5656

57-
return null!;
57+
throw new ParserException(nameof(Statement), _tokens.Current);
5858
}
5959

6060
/// <summary>

src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/TopDownParser.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,22 +62,24 @@ private bool CurrentIsExpression() =>
6262
CurrentIs("Ident") || CurrentIsLiteral() || CurrentIsUnaryOperator() ||
6363
CurrentIs("LeftParen") || CurrentIs("LeftCurl") || CurrentIs("LeftBracket");
6464

65+
private bool CurrentIsStatement() =>
66+
CurrentIsExpression() ||
67+
CurrentIs("Output") || CurrentIs("Input") ||
68+
CurrentIsKeyword("return") || CurrentIsKeyword("break") || CurrentIsKeyword("continue") ||
69+
CurrentIsKeyword("if") || CurrentIsKeyword("while");
70+
6571
/// <summary>
6672
/// Script -> StatementList
6773
/// </summary>
68-
private ScriptBody Script() =>
69-
new(StatementList());
74+
private ScriptBody Script() => new(StatementList());
7075

7176
/// <summary>
7277
/// StatementList -> StatementListItem*
7378
/// </summary>
7479
private List<StatementListItem> StatementList()
7580
{
7681
var statementList = new List<StatementListItem>();
77-
while (CurrentIsDeclaration() || CurrentIsExpression() ||
78-
CurrentIs("Output") || CurrentIs("Input") ||
79-
CurrentIsKeyword("return") || CurrentIsKeyword("break") || CurrentIsKeyword("continue") ||
80-
CurrentIsKeyword("if") || CurrentIsKeyword("while"))
82+
while (CurrentIsDeclaration() || CurrentIsStatement())
8183
{
8284
statementList.Add(StatementListItem());
8385
}
@@ -91,9 +93,7 @@ private List<StatementListItem> StatementList()
9193
private StatementListItem StatementListItem()
9294
{
9395
if (CurrentIsDeclaration())
94-
{
9596
return Declaration();
96-
}
9797

9898
return Statement();
9999
}

src/Domain/HydraScript.Domain.FrontEnd/Parser/grammar.txt

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
Script -> StatementList
22
StatementList -> StatementListItem*
3-
StatementListItem -> Statement
4-
Declaration
3+
StatementListItem -> Statement | Declaration
54

65
Statement -> BlockStatement
76
ExpressionStatement
@@ -20,18 +19,29 @@ Declaration -> LexicalDeclaration
2019
BlockStatement -> '{' StatementList '}'
2120

2221
ExpressionStatement -> Expression
23-
Expression -> CastExpression
24-
AssignmentExpression
25-
22+
Expression -> CastExpression | AssignmentExpression
2623
CastExpression -> WithExpression 'as' TypeValue
24+
AssignmentExpression -> MemberExpression '=' Expression
2725

28-
AssignmentExpression -> MemberExpression "Operator"? '=' Expression
29-
30-
LeftHandSideExpression -> MemberExpression
26+
LeftHandSideExpression -> PrimaryExpression
27+
ParenthesizedExpression
28+
ComplexLiteral
29+
MemberExpression
3130
CallExpression
32-
CallExpression -> MemberExpression Arguments (Arguments | '[' Expression ']' | '.' 'Ident')*
33-
MemberExpression -> "Ident" ('[' Expression ']' | '.' 'Ident')*
3431

32+
ParenthesizedExpression -> '(' Expression ')'
33+
34+
ComplexLiteral -> ObjectLiteral | ArrayLiteral
35+
36+
ObjectLiteral -> '{' PropertyDefinitionList '}'
37+
PropertyDefinitionList -> (FieldProperty ';')*
38+
FieldProperty -> "Ident" ':' Expression
39+
40+
ArrayLiteral -> '[' ElementList ']'
41+
ElementList -> (Expression ',')*
42+
43+
CallExpression -> MemberExpression Arguments
44+
MemberExpression -> Var ('[' Expression ']' | '.' 'Ident')*
3545
Arguments -> '(' (Expression ',')* ')'
3646

3747
WithExpression -> ConditionalExpression 'with' ObjectLiteral
@@ -44,19 +54,14 @@ AddExpression -> MulExpression (('+'|'-') MulExpression)*
4454
MulExpression -> UnaryExpression (('*'|'/'|'%'|'++'|'::') UnaryExpression)*
4555
UnaryExpression -> LeftHandSideExpression | ('-'|'!'|'~') UnaryExpression
4656

47-
PrimaryExpression -> "Ident" | EnvVar | Literal | '(' Expression ')' | ObjectLiteral | ArrayLiteral
57+
PrimaryExpression -> Var | Literal
58+
Var -> "Ident" | EnvVar
4859
EnvVar -> '$' "Ident"
4960
Literal -> "NullLiteral"
5061
"IntegerLiteral"
5162
"FloatLiteral"
5263
"StringLiteral"
5364
"BooleanLiteral"
54-
ObjectLiteral -> '{' PropertyDefinitionList '}'
55-
PropertyDefinitionList -> (FieldProperty ';')*
56-
FieldProperty -> "Ident" ':' Expression
57-
58-
ArrayLiteral -> '[' ElementList ']'
59-
ElementList -> (Expression ',')*
6065

6166
IfStatement -> 'if' '(' Expression ')' Statement ('else' Statement)?
6267

0 commit comments

Comments
 (0)