Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug/issue 51 do not translate epsilon to 5e-324 #92

Merged
merged 3 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#region copyright
// --------------------------------------------------------------------------------------------------------------------
// Copyright (c) Stephen Reindl. All rights reserved.
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// --------------------------------------------------------------------------------------------------------------------
#endregion

using System.IO;
using Microsoft.CodeAnalysis.CSharp;
using Xunit;
using Xunit.Abstractions;

namespace Oberon0.Generator.MsilBin.Tests.Expressions;

public class ConstExpressionTests(ITestOutputHelper output)
{
[Fact]
public void TestTrueAndFalseAreNotInGeneratedCode()
{
const string source = """
MODULE TestTrueAndFalseAreNotInGeneratedCode;

BEGIN
WriteLn
END TestTrueAndFalseAreNotInGeneratedCode.
""";
CompileHelper.CompileOberon0Code(source, out string code, output);

Assert.NotEmpty(code);
Assert.DoesNotContain("private bool TRUE = true;", code);
Assert.DoesNotContain("private bool FALSE = false;", code);
}

[Fact]
public void TestCorrectEpsilon()
{
const string source = """
MODULE TestCorrectEpsilon;
VAR
res: BOOLEAN;
BEGIN
IF (EPSILON = 4.94065645841247E-324) THEN res := TRUE ELSE res := FALSE END;
WriteBool(res);
WriteLn
END TestCorrectEpsilon.
""";
var cg = CompileHelper.CompileOberon0Code(source, out string code, output);
Assert.NotEmpty(code);

var syntaxTree = CSharpSyntaxTree.ParseText(code);

// Assert.Contains("private double EPSILON = double.Epsilon;", code);

var assembly = syntaxTree.CompileAndLoadAssembly(cg, true);
Assert.NotNull(assembly);

using var output1 = new StringWriter();
Runner.Execute(assembly, output1);
Assert.Equal($"{true}\n", output1.ToString().NlFix());
}
}
4 changes: 2 additions & 2 deletions Oberon0.Generator.MsilBin.Tests/Types/RealTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ END Test.

var syntaxTree = CSharpSyntaxTree.ParseText(code);

var assembly = syntaxTree.CompileAndLoadAssembly(cg, true);
Assert.True(assembly != null);
byte[] assembly = syntaxTree.CompileAndLoadAssembly(cg, true);
Assert.NotNull(assembly);

using var output1 = new StringWriter();
Runner.Execute(assembly, output1);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#region copyright
// --------------------------------------------------------------------------------------------------------------------
// Copyright (c) Stephen Reindl. All rights reserved.
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// --------------------------------------------------------------------------------------------------------------------
#endregion

using System;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Oberon0.Compiler.Definitions;
using Oberon0.Compiler.Generator;

namespace Oberon0.Generator.MsilBin.GeneratorInfo;

/// <summary>
/// This generator info is applied to all const definitions and are applied during code generation.
/// </summary>
public class ConstDeclarationGeneratorInfo: IGeneratorInfo
{
/// <summary>
/// If true, this constant will not be generated to target
/// </summary>
public bool DropGeneration { get; set; } = false;

/// <summary>
/// If set, instead of a constant, the generated code will be applied to the target expression
/// </summary>
public Func<Declaration, ExpressionSyntax>? GeneratorFunc { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
using Oberon0.Compiler.Definitions;
using Oberon0.Compiler.Generator;

namespace Oberon0.Generator.MsilBin
namespace Oberon0.Generator.MsilBin.GeneratorInfo
{
internal class DeclarationGeneratorInfo : IGeneratorInfo
{
public ProcedureParameterDeclaration OriginalField { get; internal set; } = null!;
public Declaration ReplacedBy { get; internal set; } = null!;
public ProcedureParameterDeclaration? OriginalField { get; internal set; }
public Declaration? ReplacedBy { get; internal set; }
}
}
110 changes: 87 additions & 23 deletions Oberon0.Generator.MsilBin/MsilBinGenerator.Declarations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Oberon0.Compiler.Definitions;
using Oberon0.Compiler.Types;
using Oberon0.Generator.MsilBin.GeneratorInfo;

namespace Oberon0.Generator.MsilBin
{
Expand Down Expand Up @@ -125,7 +126,11 @@ private ClassDeclarationSyntax GenerateRecordType(RecordTypeDefinition recordTyp

foreach (var declaration in recordType.Elements)
{
record = record.AddMembers(GenerateFieldDeclaration(declaration, true));
var fieldDeclaration = GenerateFieldDeclaration(declaration, true);
if (fieldDeclaration != null)
{
record = record.AddMembers(fieldDeclaration);
}
}

return record;
Expand All @@ -134,8 +139,15 @@ record = record.AddMembers(GenerateFieldDeclaration(declaration, true));
private static VariableDeclaratorSyntax FieldConstDeclaration(ConstDeclaration constDeclaration,
VariableDeclaratorSyntax varDeclarator)
{
var assignment =
constDeclaration.Type.Type switch
EqualsValueClauseSyntax assignment;
if (constDeclaration.GeneratorInfo is ConstDeclarationGeneratorInfo { GeneratorFunc: not null } gi)
{
assignment = SyntaxFactory.EqualsValueClause(gi.GeneratorFunc(constDeclaration));
}
else
{
// ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault
assignment = constDeclaration.Type.Type switch
{
BaseTypes.Int => SyntaxFactory.EqualsValueClause(
SyntaxFactory.LiteralExpression(
Expand All @@ -154,12 +166,18 @@ private static VariableDeclaratorSyntax FieldConstDeclaration(ConstDeclaration c
"Cannot handle type " + Enum.GetName(typeof(BaseTypes), constDeclaration.Type.Type),
nameof(constDeclaration))
};
}
return varDeclarator.WithInitializer(assignment);
}

private static FieldDeclarationSyntax GenerateFieldDeclaration(Declaration declaration, bool makePublic)
private static FieldDeclarationSyntax? GenerateFieldDeclaration(Declaration declaration, bool makePublic)
{
var field = SyntaxFactory.FieldDeclaration(GenerateVariableDeclaration(declaration));
var varDeclaration = GenerateVariableDeclaration(declaration);
if (varDeclaration == null)
{
return null;
}
var field = SyntaxFactory.FieldDeclaration(varDeclaration);
if (makePublic || declaration.Exportable)
{
field = field.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword));
Expand All @@ -171,7 +189,7 @@ private static FieldDeclarationSyntax GenerateFieldDeclaration(Declaration decla
return field;
}

private static VariableDeclarationSyntax GenerateVariableDeclaration(Declaration declaration)
private static VariableDeclarationSyntax? GenerateVariableDeclaration(Declaration declaration)
{
if (declaration.Type.Type.HasFlag(BaseTypes.Simple))
{
Expand All @@ -183,8 +201,7 @@ private static VariableDeclarationSyntax GenerateVariableDeclaration(Declaration
{
BaseTypes.Array => GenerateArrayVariableDefinition(declaration),
BaseTypes.Record => GenerateRecordVariableDeclaration(declaration),
_ => throw new NotImplementedException()
};
_ => throw new InvalidDataException($"Provided type is not known: {declaration.Type.Type:G}") };
}

private static VariableDeclarationSyntax GenerateRecordVariableDeclaration(Declaration declaration)
Expand All @@ -202,7 +219,7 @@ private static VariableDeclarationSyntax GenerateRecordVariableDeclaration(Decla
.WithInitializer(
SyntaxFactory.EqualsValueClause(
SyntaxFactory.ObjectCreationExpression(
GetTypeName(declaration.Type))
GetSyntaxType(declaration.Type))
.WithArgumentList(
SyntaxFactory.ArgumentList()))));
} else
Expand All @@ -221,21 +238,25 @@ private static VariableDeclarationSyntax GenerateRecordVariableDeclaration(Decla
.WithTypeArgumentList(
SyntaxFactory.TypeArgumentList(
SyntaxFactory.SingletonSeparatedList(
GetTypeName(declaration.Type)))))))));
GetSyntaxType(declaration.Type)))))))));
}

var variable = SyntaxFactory.VariableDeclaration(GetTypeName(declaration.Type))
var variable = SyntaxFactory.VariableDeclaration(GetSyntaxType(declaration.Type))
.WithVariables(varDeclarator);
return variable;
}

private static VariableDeclarationSyntax GenerateSimpleVariableDeclaration([NotNull] Declaration declaration)
private static VariableDeclarationSyntax? GenerateSimpleVariableDeclaration(Declaration declaration)
{
var typeSyntax = GetTypeName(declaration.Type);
var typeSyntax = GetSyntaxType(declaration.Type);

var varDeclarator = SyntaxFactory.VariableDeclarator(MapReservedWordName(declaration.Name));
if (declaration is ConstDeclaration constDeclaration)
{
if (constDeclaration.GeneratorInfo is ConstDeclarationGeneratorInfo { DropGeneration: true })
{
return null;
}
varDeclarator = FieldConstDeclaration(constDeclaration, varDeclarator);
}

Expand All @@ -250,7 +271,7 @@ private static VariableDeclarationSyntax GenerateArrayVariableDefinition(Declara
Debug.Assert(arrayType != null, nameof(arrayType) + " != null");
var dgi = declaration.GeneratorInfo as DeclarationGeneratorInfo;

var typeSyntax = GetTypeName(arrayType.ArrayType);
var typeSyntax = GetSyntaxType(arrayType.ArrayType);

VariableDeclaratorSyntax varDeclarator;

Expand All @@ -259,7 +280,7 @@ private static VariableDeclarationSyntax GenerateArrayVariableDefinition(Declara
varDeclarator = SyntaxFactory.VariableDeclarator(MapReservedWordName(declaration.Name)).WithInitializer(
SyntaxFactory.EqualsValueClause(
SyntaxFactory.ArrayCreationExpression(SyntaxFactory.ArrayType(
GetSimpleTypeSyntaxName(
GetSimpleTypeSyntax(
arrayType.ArrayType))
.WithRankSpecifiers(
SyntaxFactory.SingletonList(
Expand Down Expand Up @@ -320,21 +341,29 @@ private static IReadOnlyList<StatementSyntax> GenerateLocalDefinitions(FunctionD
// declarations
foreach (var declaration in functionDeclaration.Block.Declarations)
{
if (declaration is ProcedureParameterDeclaration)
switch (declaration)
{
continue;
case ProcedureParameterDeclaration:
case ConstDeclaration constDeclaration when
((constDeclaration.GeneratorInfo as ConstDeclarationGeneratorInfo)!).DropGeneration:
continue;
default:
var localDeclaration = GenerateVariableDeclaration(declaration);
if (localDeclaration != null)
{
statements =
statements.Add(SyntaxFactory.LocalDeclarationStatement(localDeclaration));
}
break;
}

statements =
statements.Add(SyntaxFactory.LocalDeclarationStatement(GenerateVariableDeclaration(declaration)));
}

return statements;
}

private static TypeSyntax AddTypeSyntaxSpecification([NotNull] Declaration declaration)
private static TypeSyntax AddTypeSyntaxSpecification(Declaration declaration)
{
var type = GetTypeName(declaration.Type);
var type = GetSyntaxType(declaration.Type);
if (declaration.Type.Type.HasFlag(BaseTypes.Simple) || declaration.Type.Type == BaseTypes.Record)
{
return type;
Expand All @@ -353,5 +382,40 @@ private static TypeSyntax AddTypeSyntaxSpecification([NotNull] Declaration decla
throw new ArgumentException("Unknown type " + Enum.GetName(typeof(BaseTypes), declaration.Type.Type),
nameof(declaration));
}

private void PatchConstDeclarations()
{
PatchConstDeclarations(Module.Block);
}

private static void PatchConstDeclarations(Block block)
{
foreach (var constDeclaration in block.Declarations.OfType<ConstDeclaration>())
{
var gi = new ConstDeclarationGeneratorInfo();
if (block.Parent == null)
{
switch (constDeclaration.Name)
{
// top level
case "EPSILON":
gi.GeneratorFunc = _ => SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.DoubleKeyword)),
MapIdentifierName("Epsilon"));
break;
case "TRUE" or "FALSE":
gi.DropGeneration = true;
break;
}
}
constDeclaration.GeneratorInfo = gi;
}

foreach (var blockProcedure in block.Procedures.Where(b => !b.IsInternal))
{
PatchConstDeclarations(blockProcedure.Block);
}
}
}
}
5 changes: 3 additions & 2 deletions Oberon0.Generator.MsilBin/MsilBinGenerator.Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Oberon0.Compiler.Expressions;
using Oberon0.Compiler.Expressions.Constant;
using Oberon0.Compiler.Solver;
using Oberon0.Generator.MsilBin.GeneratorInfo;

namespace Oberon0.Generator.MsilBin;

Expand Down Expand Up @@ -65,8 +66,8 @@
private ExpressionSyntax GenerateVariableReference(Declaration declaration, VariableSelector? selector,
bool ignoreReplace = false)
{
var dgi = (DeclarationGeneratorInfo?) declaration.GeneratorInfo;
if (!ignoreReplace && dgi?.ReplacedBy != null)
;

Check warning on line 69 in Oberon0.Generator.MsilBin/MsilBinGenerator.Expressions.cs

View workflow job for this annotation

GitHub Actions / build

Remove this empty statement. (https://rules.sonarsource.com/csharp/RSPEC-1116)

Check warning on line 69 in Oberon0.Generator.MsilBin/MsilBinGenerator.Expressions.cs

View workflow job for this annotation

GitHub Actions / build

Remove this empty statement. (https://rules.sonarsource.com/csharp/RSPEC-1116)

Check warning on line 69 in Oberon0.Generator.MsilBin/MsilBinGenerator.Expressions.cs

View workflow job for this annotation

GitHub Actions / build

Remove this empty statement. (https://rules.sonarsource.com/csharp/RSPEC-1116)

Check warning on line 69 in Oberon0.Generator.MsilBin/MsilBinGenerator.Expressions.cs

View workflow job for this annotation

GitHub Actions / build

Remove this empty statement. (https://rules.sonarsource.com/csharp/RSPEC-1116)

Check warning on line 69 in Oberon0.Generator.MsilBin/MsilBinGenerator.Expressions.cs

View workflow job for this annotation

GitHub Actions / build

Remove this empty statement. (https://rules.sonarsource.com/csharp/RSPEC-1116)

Check warning on line 69 in Oberon0.Generator.MsilBin/MsilBinGenerator.Expressions.cs

View workflow job for this annotation

GitHub Actions / build

Remove this empty statement. (https://rules.sonarsource.com/csharp/RSPEC-1116)
if (!ignoreReplace && declaration.GeneratorInfo is DeclarationGeneratorInfo { ReplacedBy: not null } dgi)
{
declaration = dgi.ReplacedBy;
}
Expand Down
Loading
Loading