Skip to content

Commit

Permalink
(compiler) Add first steps towards experimental compiler
Browse files Browse the repository at this point in the history
  • Loading branch information
perlun committed Nov 1, 2023
1 parent fec87fb commit 08e9d9d
Show file tree
Hide file tree
Showing 75 changed files with 2,517 additions and 725 deletions.
13 changes: 11 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ jobs:
- windows-2022
- ubuntu-22.04
- macos-11
include:
# Run in interpreted mode on all runners, but additionally also in compiled mode on Ubuntu
- runner: ubuntu-22.04
experimental_compilation: 'true'
- runner: ubuntu-22.04
experimental_compilation: 'false'

steps:
- uses: actions/checkout@v1
Expand All @@ -24,10 +30,13 @@ jobs:
- name: Re-generate auto-generated files
run: make auto-generated

# Needed to run tests, when they are running in compilation mode
# Needed to run tests, when they are running in experimental compilation mode
# (PERLANG_EXPERIMENTAL_COMPILATION=true)
- name: Rebuild stdlib
run: make stdlib

- name: Run test suite
run: dotnet test --configuration Release --verbosity normal
run: dotnet test --configuration Release --verbosity minimal
env:
PERLANG_EXPERIMENTAL_COMPILATION: ${{ matrix.experimental_compilation }}
PERLANG_ROOT: .
7 changes: 7 additions & 0 deletions Perlang.sln
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perlang.Tests.Architecture", "src\Perlang.Tests.Architecture\Perlang.Tests.Architecture.csproj", "{E9D632CB-E520-4868-9E64-A4A70ACD713C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perlang.Compiler", "src\Perlang.Compiler\Perlang.Compiler.csproj", "{7E7CF7BF-F792-49C9-BB16-67FE856D8D51}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -65,6 +67,10 @@ Global
{E9D632CB-E520-4868-9E64-A4A70ACD713C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9D632CB-E520-4868-9E64-A4A70ACD713C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9D632CB-E520-4868-9E64-A4A70ACD713C}.Release|Any CPU.Build.0 = Release|Any CPU
{7E7CF7BF-F792-49C9-BB16-67FE856D8D51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7E7CF7BF-F792-49C9-BB16-67FE856D8D51}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7E7CF7BF-F792-49C9-BB16-67FE856D8D51}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7E7CF7BF-F792-49C9-BB16-67FE856D8D51}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{B338D29B-A85D-4650-9BFF-FBFE55258C58} = {B924E83B-6AFE-4ABF-B79D-444F92582892}
Expand All @@ -75,5 +81,6 @@ Global
{CD78DA11-CB31-4892-8F96-3B7FD7015B83} = {F13F8E4E-5231-4321-9722-1BFCC08E5BB5}
{1874E267-5D6F-4EA4-8938-F2A0D19F27B1} = {F13F8E4E-5231-4321-9722-1BFCC08E5BB5}
{E9D632CB-E520-4868-9E64-A4A70ACD713C} = {F13F8E4E-5231-4321-9722-1BFCC08E5BB5}
{7E7CF7BF-F792-49C9-BB16-67FE856D8D51} = {B924E83B-6AFE-4ABF-B79D-444F92582892}
EndGlobalSection
EndGlobal
1 change: 1 addition & 0 deletions global.ruleset
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

<Rules AnalyzerId="SonarAnalyzer.CSharp" RuleNamespace="SonarAnalyzer.CSharp">
<Rule Id="S125" Action="None"/>
<Rule Id="S927" Action="None"/> <!-- All declarations of an object or function should use the same names and type qualifiers -->
<Rule Id="S1134" Action="None"/>
<Rule Id="S1135" Action="None"/>
<Rule Id="S3260" Action="None"/> <!-- Private classes or records which are not derived in the current assembly should be marked as 'sealed' -->
Expand Down
2 changes: 2 additions & 0 deletions release-notes/v0.4.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
### Added
#### Experimental compilation
- Add C++-based stdlib project [[#407][407]]
- Add first steps towards experimental compiler [[#409][409]]
- Preserve exact floating point representation [[#412][412]]

### Changed
Expand Down Expand Up @@ -39,6 +40,7 @@
[386]: https://github.com/perlang-org/perlang/pull/386
[389]: https://github.com/perlang-org/perlang/pull/389
[407]: https://github.com/perlang-org/perlang/pull/407
[409]: https://github.com/perlang-org/perlang/pull/409
[410]: https://github.com/perlang-org/perlang/pull/410
[412]: https://github.com/perlang-org/perlang/pull/412
[413]: https://github.com/perlang-org/perlang/pull/413
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Perlang.Compiler;

/// <summary>
/// Exception thrown for valid Perlang code, which is currently "not yet supported" in compiled mode.
///
/// This exception is thrown to make it possible for integration tests to skip tests for code which is known to not
/// yet work.
/// </summary>
public class NotImplementedInCompiledModeException : PerlangCompilerException
{
public NotImplementedInCompiledModeException(string message)
: base(message)
{
}
}
11 changes: 11 additions & 0 deletions src/Perlang.Common/Compiler/PerlangCompilerException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace Perlang.Compiler;

public class PerlangCompilerException : Exception
{
public PerlangCompilerException(string message)
: base(message)
{
}
}
44 changes: 44 additions & 0 deletions src/Perlang.Common/ITypeReference.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
#nullable enable

using System;
using System.Numerics;

namespace Perlang
{
public interface ITypeReference
{
Token? TypeSpecifier { get; }

/// <summary>
/// Gets or sets the CLR/.NET type that this <see cref="ITypeReference"/> refers to.
/// </summary>
// TODO: Remove setter to make this interface and class be fully immutable, for debuggability.
Type? ClrType { get; set; }

/// <summary>
/// Gets the C++ type that this <see cref="ITypeReference"/> refers to.
/// </summary>
string CppType { get; }

/// <summary>
/// Gets a cast to the C++ type that this <see cref="ITypeReference"/> refers to. Note that no validation if the
/// cast will be possible or not is performed here; it is the responsibility of the caller.
/// </summary>
string CppTypeCast => $"({CppType})";

/// <summary>
/// Gets a value indicating whether the type reference contains an explicit type specifier or not. If this is
/// false, the user is perhaps intending for the type to be inferred from the program context.
Expand All @@ -27,5 +42,34 @@ public interface ITypeReference
/// Gets a value indicating whether this type reference refers to a `null` value.
/// </summary>
public bool IsNullObject => ClrType == typeof(NullObject);

bool IsValidNumberType =>
ClrType switch
{
null => false,
var t when t == typeof(SByte) => true,
var t when t == typeof(Int16) => true,
var t when t == typeof(Int32) => true,
var t when t == typeof(Int64) => true,
var t when t == typeof(Byte) => true,
var t when t == typeof(UInt16) => true,
var t when t == typeof(UInt32) => true,
var t when t == typeof(UInt64) => true,
var t when t == typeof(Single) => true, // i.e. float
var t when t == typeof(Double) => true,
var t when t == typeof(BigInteger) => true,
_ => false
};

bool IsStringType() =>
ClrType switch
{
null => false,

// Cannot use typeof(AsciiString) since Perlang.Common cannot depend on Perlang.Stdlib
var t when t.FullName == "Perlang.Lang.AsciiString" => true,

_ => false
};
}
}
25 changes: 25 additions & 0 deletions src/Perlang.Common/PerlangMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.IO;

namespace Perlang;

public static class PerlangMode
{
public static bool ExperimentalCompilation
{
get
{
string environmentVariable = Environment.GetEnvironmentVariable("PERLANG_EXPERIMENTAL_COMPILATION");

// The environment variable takes precedence, if set
if (Boolean.TryParse(environmentVariable, out bool flag))
{
return flag;
}

string homeDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);

return File.Exists(Path.Combine(homeDirectory, ".perlang_experimental_compilation"));
}
}
}
24 changes: 24 additions & 0 deletions src/Perlang.Common/TypeReference.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#nullable enable
using System;
using System.Numerics;
using Perlang.Compiler;

namespace Perlang
{
Expand Down Expand Up @@ -44,6 +46,28 @@ public Type? ClrType
}
}

public string CppType =>
clrType switch
{
// TODO: Add the other Perlang-supported types as well
var t when t == typeof(Int32) => "int32_t",
var t when t == typeof(UInt32) => "uint32_t",
var t when t == typeof(Int64) => "int64_t",
var t when t == typeof(UInt64) => "uint64_t",
var t when t == typeof(Single) => "float",
var t when t == typeof(Double) => "double",
var t when t == typeof(bool) => "bool",
var t when t == typeof(void) => "void",
var t when t == typeof(BigInteger) => throw new NotImplementedInCompiledModeException("BigInteger is not yet supported in compiled mode"),
null => throw new InvalidOperationException($"Internal error: ClrType was unexpectedly null"),

// TODO: Differentiate from these on the C++ level as well
var t when t.FullName == "Perlang.Lang.AsciiString" => "const char *",
var t when t.FullName == "Perlang.Lang.String" => "const char *",

_ => throw new NotImplementedInCompiledModeException($"Internal error: C++ type for {clrType} not defined")
};

/// <summary>
/// Initializes a new instance of the <see cref="TypeReference"/> class, for a given type specifier. The type
/// specifier can be null, in which case type inference will be attempted.
Expand Down
24 changes: 24 additions & 0 deletions src/Perlang.Compiler/Perlang.Compiler.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Nullable>enable</Nullable>
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DocumentationFile>bin\Debug\net7.0\Perlang.Compiler.xml</DocumentationFile>
<NoWarn>1591</NoWarn>
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DocumentationFile>bin\Release\net7.0\Perlang.Compiler.xml</DocumentationFile>
<NoWarn>1591</NoWarn>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Perlang.Common\Perlang.Common.csproj" />
<ProjectReference Include="..\Perlang.Parser\Perlang.Parser.csproj" />
</ItemGroup>

</Project>
5 changes: 5 additions & 0 deletions src/Perlang.Compiler/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using System.Reflection;
using Perlang;

[assembly: AssemblyVersion(CommonConstants.Version)]
[assembly: AssemblyInformationalVersion(CommonConstants.InformationalVersion)]
1 change: 1 addition & 0 deletions src/Perlang.ConsoleApp/Perlang.ConsoleApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Perlang.Compiler\Perlang.Compiler.csproj" />
<ProjectReference Include="..\Perlang.Interpreter\Perlang.Interpreter.csproj" />
</ItemGroup>

Expand Down
37 changes: 33 additions & 4 deletions src/Perlang.ConsoleApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Mono.Terminal;
using Perlang.Internal;
using Perlang.Interpreter;
using Perlang.Interpreter.Compiler;
using Perlang.Interpreter.NameResolution;
using Perlang.Parser;
using ParseError = Perlang.Parser.ParseError;
Expand Down Expand Up @@ -64,6 +65,7 @@ internal enum ExitCodes
}

private readonly PerlangInterpreter interpreter;
private readonly PerlangCompiler compiler;

/// <summary>
/// Writes a Perlang native string to the standard output stream.
Expand All @@ -87,6 +89,11 @@ internal enum ExitCodes

private readonly HashSet<WarningType> disabledWarningsAsErrors;

/// <summary>
/// A flag which determines if (highly experimental) compilation to machine code is enabled or not.
/// </summary>
private readonly bool experimentalCompilation;

private bool hadError;
private bool hadRuntimeError;

Expand Down Expand Up @@ -234,7 +241,8 @@ public static int MainWithCustomConsole(string[] args, IPerlangConsole console)
var program = new Program(
replMode: false,
standardOutputHandler: console.WriteStdoutLine,
disabledWarningsAsErrors: disabledWarningsAsErrorsList
disabledWarningsAsErrors: disabledWarningsAsErrorsList,
experimentalCompilation: PerlangMode.ExperimentalCompilation
);

result = program.RunFile(scriptName);
Expand All @@ -247,7 +255,8 @@ public static int MainWithCustomConsole(string[] args, IPerlangConsole console)
replMode: false,
arguments: remainingArguments,
standardOutputHandler: console.WriteStdoutLine,
disabledWarningsAsErrors: disabledWarningsAsErrorsList
disabledWarningsAsErrors: disabledWarningsAsErrorsList,
experimentalCompilation: PerlangMode.ExperimentalCompilation
);

result = program.RunFile(scriptName);
Expand Down Expand Up @@ -290,13 +299,15 @@ internal Program(
Action<Lang.String> standardOutputHandler,
IEnumerable<string>? arguments = null,
IEnumerable<WarningType>? disabledWarningsAsErrors = null,
Action<RuntimeError>? runtimeErrorHandler = null)
Action<RuntimeError>? runtimeErrorHandler = null,
bool experimentalCompilation = false)
{
// TODO: Make these be separate handlers at some point, so the caller can separate between these types of
// TODO: output.
this.standardOutputHandler = standardOutputHandler;
this.standardErrorHandler = standardOutputHandler;
this.disabledWarningsAsErrors = (disabledWarningsAsErrors ?? Enumerable.Empty<WarningType>()).ToHashSet();
this.experimentalCompilation = experimentalCompilation;

// Convenience fields while we are migrating away from CLR strings to Perlang strings.
this.standardOutputHandlerFromClrString = s => this.standardOutputHandler(Lang.String.from(s));
Expand All @@ -309,6 +320,11 @@ internal Program(
arguments ?? new List<string>(),
replMode: replMode
);

compiler = new PerlangCompiler(
runtimeErrorHandler ?? RuntimeError,
this.standardOutputHandler
);
}

private int RunFile(string path)
Expand All @@ -320,8 +336,16 @@ private int RunFile(string path)
}

var bytes = File.ReadAllBytes(path);
string source = Encoding.UTF8.GetString(bytes);

Run(Encoding.UTF8.GetString(bytes), CompilerWarning);
if (experimentalCompilation)
{
CompileAndRun(source, path, CompilerWarning);
}
else
{
Run(source, CompilerWarning);
}

// Indicate an error in the exit code.
if (hadError)
Expand Down Expand Up @@ -426,6 +450,11 @@ internal int Run(string source, CompilerWarningHandler compilerWarningHandler)
return (int)ExitCodes.SUCCESS;
}

private void CompileAndRun(string source, string path, CompilerWarningHandler compilerWarningHandler)
{
compiler.CompileAndRun(source, path, ScanError, ParseError, NameResolutionError, ValidationError, ValidationError, compilerWarningHandler);
}

private void ParseAndPrint(string source)
{
string? result = interpreter.Parse(source, ScanError, ParseError);
Expand Down
Loading

0 comments on commit 08e9d9d

Please sign in to comment.