Skip to content

Commit 290d02d

Browse files
authored
Feature/explicit cast (#217)
* #202 - refactoring AsString.cs * #202 - new explicit cast backend instructions * #202 - static analysis * #202 - fix * #202 - type use iequatable * #202 - codegen * #202 - improve print codegen * #202 - fix 2 * #202 - new test
1 parent 91bd366 commit 290d02d

File tree

26 files changed

+204
-44
lines changed

26 files changed

+204
-44
lines changed

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment.ComplexData.Create;
66
using HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment.ComplexData.Read;
77
using HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment.ComplexData.Write;
8+
using HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment.ExplicitCast;
89
using HydraScript.Domain.BackEnd.Impl.Instructions.WithJump;
910
using HydraScript.Domain.BackEnd.Impl.Values;
1011
using HydraScript.Domain.FrontEnd.Parser;
@@ -150,13 +151,22 @@ public AddressedInstructions Visit(BinaryExpression visitable)
150151

151152
public AddressedInstructions Visit(CastAsExpression visitable)
152153
{
154+
Func<IValue, IExecutableInstruction> asFactory = visitable.ToType switch
155+
{
156+
CastAsExpression.DestinationType.Undefined => throw new NotSupportedException(),
157+
CastAsExpression.DestinationType.String => value => new AsString(value),
158+
CastAsExpression.DestinationType.Number => value => new AsNumber(value),
159+
CastAsExpression.DestinationType.Boolean => value => new AsBool(value),
160+
_ => throw new ArgumentOutOfRangeException(nameof(visitable.ToType))
161+
};
162+
153163
if (visitable.Expression is PrimaryExpression primary)
154-
return [new AsString(_valueFactory.Create(primary.ToValueDto()))];
155-
164+
return [asFactory(_valueFactory.Create(primary.ToValueDto()))];
165+
156166
var result = visitable.Expression.Accept(This);
157167
var last = result.OfType<Simple>().Last().Left!;
158-
result.Add(new AsString(last));
159-
168+
result.Add(asFactory(last));
169+
160170
return result;
161171
}
162172

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

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using HydraScript.Domain.BackEnd.Impl.Addresses;
33
using HydraScript.Domain.BackEnd.Impl.Instructions;
44
using HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment;
5+
using HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment.ExplicitCast;
56
using HydraScript.Domain.BackEnd.Impl.Instructions.WithJump;
67
using HydraScript.Domain.Constants;
78
using HydraScript.Domain.FrontEnd.Parser;
@@ -231,18 +232,26 @@ public AddressedInstructions Visit(IfStatement visitable)
231232

232233
public AddressedInstructions Visit(PrintStatement visitable)
233234
{
234-
AddressedInstructions result = [];
235-
236235
if (visitable.Expression is PrimaryExpression prim)
237-
result.Add(new AsString(_valueFactory.Create(prim.ToValueDto())));
238-
else
239236
{
240-
result.AddRange(visitable.Expression.Accept(_expressionVisitor));
241-
var name = result.OfType<Simple>().Last().Left!;
242-
result.Add(new AsString(name));
237+
var valueDto = prim.ToValueDto();
238+
var printedValue = _valueFactory.Create(valueDto);
239+
IExecutableInstruction instruction = valueDto is { Type: ValueDtoType.Env } or { Type: ValueDtoType.Constant, Value: string }
240+
? new Print(printedValue)
241+
: new AsString(printedValue);
242+
AddressedInstructions shortResult = [instruction];
243+
if (instruction is AsString asString)
244+
shortResult.Add(new Print(asString.Left!));
245+
return shortResult;
243246
}
244247

245-
result.Add(new Print((result[result.End] as AsString)!.Left!));
248+
AddressedInstructions result = [];
249+
250+
result.AddRange(visitable.Expression.Accept(_expressionVisitor));
251+
var name = result.OfType<Simple>().Last().Left!;
252+
var nameAsString = new AsString(name);
253+
result.Add(nameAsString);
254+
result.Add(new Print(nameAsString.Left!));
246255

247256
return result;
248257
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions;
3+
4+
namespace HydraScript.Application.StaticAnalysis.Exceptions;
5+
6+
[ExcludeFromCodeCoverage]
7+
public class ExplicitCastNotSupported(CastAsExpression cast, Type from, Type to) :
8+
SemanticException(cast.Segment, $"Cast from {from} to {to} is not supported");
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace HydraScript.Application.StaticAnalysis;
2+
3+
public interface IExplicitCastValidator
4+
{
5+
bool IsAllowed(Type from, Type to);
6+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using HydraScript.Domain.IR.Types;
2+
3+
namespace HydraScript.Application.StaticAnalysis.Impl;
4+
5+
internal sealed class ExplicitCastValidator : IExplicitCastValidator
6+
{
7+
private static readonly Type Boolean = "boolean";
8+
private static readonly Type Number = "number";
9+
private static readonly Type String = "string";
10+
private static readonly Any Any = new();
11+
12+
private readonly Dictionary<Type, List<Type>> _allowedConversions = new()
13+
{
14+
{ String, [Any] },
15+
{ Number, [String, Boolean] },
16+
{ Boolean, [String, Number] },
17+
};
18+
19+
public bool IsAllowed(Type from, Type to)
20+
{
21+
var typeEqualityComparer = default(CommutativeTypeEqualityComparer);
22+
23+
if (typeEqualityComparer.Equals(from, to))
24+
return true;
25+
26+
if (!_allowedConversions.TryGetValue(to, out var allowedFrom))
27+
return false;
28+
29+
for (var i = 0; i < allowedFrom.Count; i++)
30+
{
31+
if (typeEqualityComparer.Equals(allowedFrom[i], from))
32+
return true;
33+
}
34+
35+
return false;
36+
}
37+
}

src/Application/HydraScript.Application.StaticAnalysis/ServiceCollectionExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public static IServiceCollection AddStaticAnalysis(this IServiceCollection servi
2121
services.AddSingleton<IStandardLibraryProvider, StandardLibraryProvider>();
2222
services.AddSingleton<IJavaScriptTypesProvider, JavaScriptTypesProvider>();
2323
services.AddSingleton<IDefaultValueForTypeCalculator, DefaultValueForTypeCalculator>();
24+
services.AddSingleton<IExplicitCastValidator, ExplicitCastValidator>();
2425

2526
services.AddSingleton<IAmbiguousInvocationStorage, AmbiguousInvocationStorage>();
2627

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

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ internal class SemanticChecker : VisitorBase<IAbstractSyntaxTreeNode, Type>,
5050
private readonly ISymbolTableStorage _symbolTables;
5151
private readonly IComputedTypesStorage _computedTypes;
5252
private readonly IAmbiguousInvocationStorage _ambiguousInvocations;
53+
private readonly IExplicitCastValidator _explicitCastValidator;
5354
private readonly IVisitor<TypeValue, Type> _typeBuilder;
5455

5556
public SemanticChecker(
@@ -59,6 +60,7 @@ public SemanticChecker(
5960
ISymbolTableStorage symbolTables,
6061
IComputedTypesStorage computedTypes,
6162
IAmbiguousInvocationStorage ambiguousInvocations,
63+
IExplicitCastValidator explicitCastValidator,
6264
IVisitor<TypeValue, Type> typeBuilder)
6365
{
6466
_calculator = calculator;
@@ -67,6 +69,7 @@ public SemanticChecker(
6769
_symbolTables = symbolTables;
6870
_computedTypes = computedTypes;
6971
_ambiguousInvocations = ambiguousInvocations;
72+
_explicitCastValidator = explicitCastValidator;
7073
_typeBuilder = typeBuilder;
7174
}
7275

@@ -84,7 +87,7 @@ public Type Visit(ScriptBody visitable)
8487
_symbolTables.Clear();
8588
_computedTypes.Clear();
8689
_ambiguousInvocations.Clear();
87-
90+
8891
return "undefined";
8992
}
9093

@@ -433,14 +436,23 @@ public ObjectType Visit(WithExpression visitable)
433436
public Type Visit(CastAsExpression visitable)
434437
{
435438
Type undefined = "undefined";
436-
var exprType = visitable.Expression.Accept(This);
439+
var from = visitable.Expression.Accept(This);
437440

438-
if (exprType.Equals(undefined))
441+
if (from.Equals(undefined))
439442
throw new CannotDefineType(visitable.Expression.Segment);
440443

441-
return visitable.Cast.Accept(_typeBuilder) == "string"
442-
? "string"
443-
: throw new NotSupportedException("Other types but 'string' have not been supported for casting yet");
444+
var to = visitable.Cast.Accept(_typeBuilder);
445+
visitable.ToType = to switch
446+
{
447+
_ when to.Equals("string") => CastAsExpression.DestinationType.String,
448+
_ when to.Equals("number") => CastAsExpression.DestinationType.Number,
449+
_ when to.Equals("boolean") => CastAsExpression.DestinationType.Boolean,
450+
_ => CastAsExpression.DestinationType.Undefined
451+
};
452+
453+
return _explicitCastValidator.IsAllowed(from, to)
454+
? to
455+
: throw new ExplicitCastNotSupported(visitable, from, to);
444456
}
445457

446458
public Type Visit(CallExpression visitable)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment.ExplicitCast;
2+
3+
public class AsBool(IValue value) : AsInstruction<bool>(value)
4+
{
5+
protected override bool Convert(object? value) =>
6+
System.Convert.ToBoolean(value);
7+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment.ExplicitCast;
2+
3+
public abstract class AsInstruction<T>(IValue value) : Simple(value)
4+
{
5+
protected override void Assign()
6+
{
7+
var value = Right.right!.Get();
8+
var valueAsType = value is T ? value : Convert(value);
9+
Left?.Set(valueAsType);
10+
}
11+
12+
protected abstract T Convert(object? value);
13+
14+
protected override string ToStringInternal() =>
15+
$"{Left} = {Right.right} as {typeof(T).Name}";
16+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment.ExplicitCast;
2+
3+
public class AsNumber(IValue value) : AsInstruction<double>(value)
4+
{
5+
protected override double Convert(object? value) =>
6+
System.Convert.ToDouble(value);
7+
}

0 commit comments

Comments
 (0)