Skip to content

Commit d087b15

Browse files
authored
#16 - with выражение +semver:feature (#195)
* #16 - add xmldoc grammar rule summaries to TopDownParser * #16 - add 'with' keyword to TokenTypes * #16 - update grammar to include 'with' expressions * #16 - implement 'with' expression parsing and AST node * #16 - add IsSubsetOf method to ObjectType with comprehensive tests * #16 - update static analysis in SemanticChecker to support 'with' expression and refine visitor return types * #16 - add CalculateDifference method to ObjectType and integrate with SemanticChecker; update unit tests to include CalculateDifference and refactor IsSubsetOf tests * #16 - refactor ComplexLiteral hierarchy: make Id property abstract and implement in derived classes * #16 - add codegen visitor implementation for 'with' expressions and remove dead code in DotRead * #16 - add integration test samples for 'with' expressions * #16 - add contents:write perm to release workflow * #16 - add issues perm to release
1 parent bf1d03b commit d087b15

File tree

17 files changed

+596
-16
lines changed

17 files changed

+596
-16
lines changed

.github/workflows/release.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ on:
55
branches:
66
- 'release'
77

8+
permissions:
9+
contents: write
10+
issues: write
11+
812
jobs:
913
create-release:
1014
name: Create release

src/Application/HydraScript.Application.CodeGeneration/Visitors/ExpressionInstructionProvider.cs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ internal class ExpressionInstructionProvider : VisitorBase<IAbstractSyntaxTreeNo
2424
IVisitor<UnaryExpression, AddressedInstructions>,
2525
IVisitor<BinaryExpression, AddressedInstructions>,
2626
IVisitor<CastAsExpression, AddressedInstructions>,
27+
IVisitor<WithExpression, AddressedInstructions>,
2728
IVisitor<ConditionalExpression, AddressedInstructions>,
2829
IVisitor<AssignmentExpression, AddressedInstructions>,
2930
IVisitor<MemberExpression, AddressedInstructions>,
@@ -36,6 +37,8 @@ internal class ExpressionInstructionProvider : VisitorBase<IAbstractSyntaxTreeNo
3637
public ExpressionInstructionProvider(IValueDtoConverter valueDtoConverter) =>
3738
_valueDtoConverter = valueDtoConverter;
3839

40+
public override AddressedInstructions Visit(IAbstractSyntaxTreeNode visitable) => [];
41+
3942
public AddressedInstructions Visit(PrimaryExpression visitable) =>
4043
[new Simple(_valueDtoConverter.Convert(visitable.ToValueDto()))];
4144

@@ -76,9 +79,8 @@ public AddressedInstructions Visit(ObjectLiteral visitable)
7679

7780
var result = new AddressedInstructions { createObject };
7881

79-
var propInstructions =
80-
visitable.Properties.AsValueEnumerable()
81-
.SelectMany(property => property.Accept(This));
82+
var propInstructions = visitable.AsValueEnumerable()
83+
.SelectMany(property => property.Accept(This));
8284
foreach (var propInstruction in propInstructions)
8385
result.Add(propInstruction);
8486

@@ -158,6 +160,42 @@ public AddressedInstructions Visit(CastAsExpression visitable)
158160
return result;
159161
}
160162

163+
public AddressedInstructions Visit(WithExpression visitable)
164+
{
165+
if (visitable is { Expression: PrimaryExpression, ComputedCopiedProperties.Count: 0 })
166+
return [];
167+
168+
var objectId = visitable.ObjectLiteral.Id;
169+
var createObject = new CreateObject(objectId);
170+
171+
var result = new AddressedInstructions { createObject };
172+
173+
var propInstructions = visitable.ObjectLiteral
174+
.AsValueEnumerable()
175+
.SelectMany(property => property.Accept(This));
176+
foreach (var propInstruction in propInstructions)
177+
result.Add(propInstruction);
178+
179+
if (visitable.ComputedCopiedProperties.Count is 0)
180+
return result;
181+
182+
result.AddRange(visitable.Expression is PrimaryExpression ? [] : visitable.Expression.Accept(This));
183+
184+
var copyFrom = visitable.Expression is PrimaryExpression primary
185+
? (Name)_valueDtoConverter.Convert(primary.ToValueDto())
186+
: new Name(result.OfType<Simple>().Last().Left!);
187+
188+
for (var i = 0; i < visitable.ComputedCopiedProperties.Count; i++)
189+
{
190+
var property = new Constant(visitable.ComputedCopiedProperties[i]);
191+
result.Add(new DotRead(copyFrom, property));
192+
var read = result[result.End].Address.Name;
193+
result.Add(new DotAssignment(objectId, property, new Name(read)));
194+
}
195+
196+
return result;
197+
}
198+
161199
public AddressedInstructions Visit(ConditionalExpression visitable)
162200
{
163201
var blockId = $"cond_{visitable.GetHashCode()}";

src/Application/HydraScript.Application.StaticAnalysis/Visitors/SemanticChecker.cs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ internal class SemanticChecker : VisitorBase<IAbstractSyntaxTreeNode, Type>,
2626
IVisitor<IdentifierReference, Type>,
2727
IVisitor<Literal, Type>,
2828
IVisitor<ImplicitLiteral, Type>,
29-
IVisitor<ArrayLiteral, Type>,
30-
IVisitor<ObjectLiteral, Type>,
29+
IVisitor<ArrayLiteral, ArrayType>,
30+
IVisitor<ObjectLiteral, ObjectType>,
3131
IVisitor<ConditionalExpression, Type>,
3232
IVisitor<BinaryExpression, Type>,
3333
IVisitor<UnaryExpression, Type>,
@@ -37,6 +37,7 @@ internal class SemanticChecker : VisitorBase<IAbstractSyntaxTreeNode, Type>,
3737
IVisitor<IndexAccess, Type>,
3838
IVisitor<DotAccess, Type>,
3939
IVisitor<CastAsExpression, Type>,
40+
IVisitor<WithExpression, ObjectType>,
4041
IVisitor<CallExpression, Type>,
4142
IVisitor<FunctionDeclaration, Type>,
4243
IVisitor<BlockStatement, Type>,
@@ -163,7 +164,7 @@ public Type Visit(ImplicitLiteral visitable)
163164
return type;
164165
}
165166

166-
public Type Visit(ArrayLiteral visitable)
167+
public ArrayType Visit(ArrayLiteral visitable)
167168
{
168169
if (visitable.Expressions.Count == 0)
169170
return new ArrayType(new Any());
@@ -175,7 +176,7 @@ public Type Visit(ArrayLiteral visitable)
175176
throw new WrongArrayLiteralDeclaration(visitable.Segment, type);
176177
}
177178

178-
public Type Visit(ObjectLiteral visitable)
179+
public ObjectType Visit(ObjectLiteral visitable)
179180
{
180181
var properties = visitable.Properties.AsValueEnumerable().Select(prop =>
181182
{
@@ -398,6 +399,27 @@ public Type Visit(DotAccess visitable)
398399
return visitable.HasNext() ? visitable.Next?.Accept(This) ?? "undefined" : fieldType;
399400
}
400401

402+
public ObjectType Visit(WithExpression visitable)
403+
{
404+
var exprType = visitable.Expression.Accept(This);
405+
406+
if (exprType is not ObjectType supersetObjectType)
407+
throw new UnsupportedOperation(visitable.Segment, exprType, "with");
408+
409+
IVisitor<ObjectLiteral, ObjectType> objectLiteralVisitor = this;
410+
var subsetObjectType = visitable.ObjectLiteral.Accept(objectLiteralVisitor);
411+
412+
if (!supersetObjectType.IsSubsetOf(subsetObjectType))
413+
throw new IncompatibleTypesOfOperands(
414+
visitable.Segment,
415+
left: supersetObjectType,
416+
right: subsetObjectType);
417+
418+
visitable.ComputedCopiedProperties = supersetObjectType.CalculateDifference(subsetObjectType);
419+
420+
return supersetObjectType;
421+
}
422+
401423
public Type Visit(CastAsExpression visitable)
402424
{
403425
Type undefined = "undefined";

src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Read/DotRead.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ public class DotRead(Name @object, IValue property) : Simple(
1010
{
1111
private readonly IValue _property = property;
1212

13-
public string Property => (string)_property.Get(frame: null)!;
14-
1513
public Simple ToAssignment(IValue value) =>
1614
new DotAssignment(@object.ToString(), _property, value);
1715

src/Domain/HydraScript.Domain.Constants/TokenTypes.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public static IEnumerable<Dto> Stream
5959
yield return new(
6060
Tag: "Keyword",
6161
Pattern:
62-
"(?<![a-zA-Z0-9])(let|const|function|if|else|while|break|continue|return|as|type)(?![a-zA-Z0-9])",
62+
"(?<![a-zA-Z0-9])(let|const|function|if|else|while|break|continue|return|as|type|with)(?![a-zA-Z0-9])",
6363
Priority: 11);
6464

6565
yield return new(

src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/Ast/Nodes/Expressions/ComplexLiterals/ArrayLiteral.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ public partial class ArrayLiteral : ComplexLiteral
1010

1111
public IReadOnlyList<Expression> Expressions => _expressions;
1212

13+
public override string Id => Parent is AssignmentExpression assignment
14+
? assignment.Destination.Id
15+
: NullId;
16+
1317
public ArrayLiteral(List<Expression> expressions)
1418
{
1519
_expressions = expressions;

src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/Ast/Nodes/Expressions/ComplexLiterals/ComplexLiteral.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,16 @@ namespace HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions.ComplexL
22

33
public abstract class ComplexLiteral : Expression
44
{
5-
public string Id => Parent is AssignmentExpression assignment
6-
? assignment.Destination.Id
7-
: $"_t{GetHashCode()}";
5+
private string? _nullId;
6+
7+
protected string NullId
8+
{
9+
get
10+
{
11+
_nullId ??= $"{GetHashCode()}";
12+
return _nullId;
13+
}
14+
}
15+
16+
public abstract string Id { get; }
817
}

src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/Ast/Nodes/Expressions/ComplexLiterals/ObjectLiteral.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,20 @@ public partial class ObjectLiteral : ComplexLiteral
1010

1111
public IReadOnlyList<Property> Properties => _properties;
1212

13+
public override string Id
14+
{
15+
get
16+
{
17+
if (Parent is AssignmentExpression assignment)
18+
return assignment.Destination.Id;
19+
20+
if (Parent is WithExpression{Parent:AssignmentExpression withAssignment})
21+
return withAssignment.Destination.Id;
22+
23+
return NullId;
24+
}
25+
}
26+
1327
public ObjectLiteral(List<Property> properties)
1428
{
1529
_properties = properties;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions.ComplexLiterals;
2+
3+
namespace HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions;
4+
5+
[AutoVisitable<IAbstractSyntaxTreeNode>]
6+
public partial class WithExpression : Expression
7+
{
8+
protected override IReadOnlyList<IAbstractSyntaxTreeNode> Children { get; }
9+
10+
public Expression Expression { get; }
11+
public ObjectLiteral ObjectLiteral { get; }
12+
13+
public IReadOnlyList<string> ComputedCopiedProperties { get; set; } = [];
14+
15+
public WithExpression(Expression expression, ObjectLiteral objectLiteral)
16+
{
17+
Expression = expression;
18+
Expression.Parent = this;
19+
20+
ObjectLiteral = objectLiteral;
21+
ObjectLiteral.Parent = this;
22+
23+
Children = [Expression, ObjectLiteral];
24+
}
25+
26+
protected override string NodeRepresentation() => "with";
27+
}

0 commit comments

Comments
 (0)