diff --git a/.Net Core Templates/templatepack.csproj b/.Net Core Templates/templatepack.csproj index 9ecae706..4fb1b501 100644 --- a/.Net Core Templates/templatepack.csproj +++ b/.Net Core Templates/templatepack.csproj @@ -2,7 +2,7 @@ Template - 1.0.60 + 1.0.61 Backlang.Templates Backlang Templates furesoft diff --git a/.Net Core Templates/templates/console/Program.back b/.Net Core Templates/templates/console/Program.back index 74848074..765b79c1 100644 --- a/.Net Core Templates/templates/console/Program.back +++ b/.Net Core Templates/templates/console/Program.back @@ -1,6 +1,4 @@ -public static func main() -> i32 +func main() { print("Hello World"); - - return 0; } diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a03cb38a..2c6f4b0c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,9 +21,9 @@ jobs: - kind: macOS os: macos-latest target: osx-x64 - + runs-on: ${{ matrix.os }} # For a list of available runner types, refer to https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on - + steps: - name: Checkout uses: actions/checkout@v3 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1e17ff16..ed1139b4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,12 +2,12 @@ name: Tests on: push: - branches: + branches: - '*' # matches every branch that doesn't contain a '/' - '*/*' # matches every branch containing a single '/' - '**' # matches every branch pull_request: - branches: + branches: - '*' # matches every branch that doesn't contain a '/' - '*/*' # matches every branch containing a single '/' - '**' # matches every branch @@ -19,7 +19,7 @@ jobs: env: Solution_Name: Source/Backlang-Compiler.sln # Replace with your solution name, i.e. MyWpfApp.sln. Test_Project_Path: Source/TestProject1/TestProject1.csproj # Replace with the path to your test project, i.e. MyWpfApp.Tests\MyWpfApp.Tests.csproj. - + steps: - name: Checkout uses: actions/checkout@v3 @@ -31,7 +31,10 @@ jobs: with: dotnet-version: 7.0.x include-prerelease: true - + + - name: Install WASM + run: dotnet workload install wasm-tools + - name: Build run: dotnet build $Solution_Name --configuration Release diff --git a/.gitignore b/.gitignore index 824d32e3..f8aef24a 100644 --- a/.gitignore +++ b/.gitignore @@ -350,4 +350,4 @@ MigrationBackup/ .ionide/ *.vsix -StreamPlan.txt \ No newline at end of file +*.dll diff --git a/Source/.vscode/launch.json b/.vscode/launch.json similarity index 55% rename from Source/.vscode/launch.json rename to .vscode/launch.json index 594545c9..d5863821 100644 --- a/Source/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,16 +9,12 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceFolder}/Backlang-Compiler/bin/Debug/net7.0/Backlang-Compiler.dll", - "args": ["-i", "${workspaceFolder}/Backlang-Compiler/bin/Debug/net7.0/compilation.back", "-o", "compilation"], + "program": "${workspaceFolder}/Source/Backlang-Compiler/bin/Debug/net7.0/Backlang-Compiler.dll", + "args": ["-i", "${workspaceFolder}/Source/Backlang-Compiler/bin/Debug/net7.0/compilation.back", "-o", "compilation"], "cwd": "${workspaceFolder}", - "console": "internalConsole", - "stopAtEntry": false - }, - { - "name": ".NET Core Attach", - "type": "coreclr", - "request": "attach" + "stopAtEntry": false, + "console": "internalConsole" } + ] } \ No newline at end of file diff --git a/Source/.vscode/tasks.json b/.vscode/tasks.json similarity index 96% rename from Source/.vscode/tasks.json rename to .vscode/tasks.json index 31c32bd3..9ee9d4ad 100644 --- a/Source/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -9,6 +9,9 @@ "type": "shell", "args": [ "build", + + "Source/", + // Ask dotnet build to generate full paths for file names. "/property:GenerateFullPaths=true", // Do not generate summary otherwise it leads to duplicate errors in Problems panel diff --git a/Source/Backlang-Compiler.sln b/Source/Backlang-Compiler.sln index 6020586e..b387f7da 100644 --- a/Source/Backlang-Compiler.sln +++ b/Source/Backlang-Compiler.sln @@ -29,6 +29,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Backlang.Backends.Bs2k", "P EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Backlang.ResourcePreprocessor.Mif", "Plugins\Backlang.ResourcePreprocessor.Mif\Backlang.ResourcePreprocessor.Mif.csproj", "{F45724ED-51E7-4262-A94C-42C1F639E7AE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Backlang.WasmBridge", "Backlang.WasmBridge\Backlang.WasmBridge.csproj", "{DFA33B5E-1C8F-4930-9130-24C00D935919}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -71,6 +73,10 @@ Global {F45724ED-51E7-4262-A94C-42C1F639E7AE}.Debug|Any CPU.Build.0 = Debug|Any CPU {F45724ED-51E7-4262-A94C-42C1F639E7AE}.Release|Any CPU.ActiveCfg = Release|Any CPU {F45724ED-51E7-4262-A94C-42C1F639E7AE}.Release|Any CPU.Build.0 = Release|Any CPU + {DFA33B5E-1C8F-4930-9130-24C00D935919}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DFA33B5E-1C8F-4930-9130-24C00D935919}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DFA33B5E-1C8F-4930-9130-24C00D935919}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DFA33B5E-1C8F-4930-9130-24C00D935919}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Source/Backlang-Compiler/Backlang-Compiler.csproj b/Source/Backlang-Compiler/Backlang-Compiler.csproj index 90c73d84..0ae62399 100644 --- a/Source/Backlang-Compiler/Backlang-Compiler.csproj +++ b/Source/Backlang-Compiler/Backlang-Compiler.csproj @@ -13,10 +13,10 @@ - - - - + + + + @@ -29,9 +29,6 @@ Always - - Always - diff --git a/Source/Backlang-Compiler/Program.cs b/Source/Backlang-Compiler/Program.cs index 0352ed33..8498b5dc 100644 --- a/Source/Backlang-Compiler/Program.cs +++ b/Source/Backlang-Compiler/Program.cs @@ -8,8 +8,13 @@ public static class Program { public static void Main(string[] args) { - Parser.Default.ParseArguments(args) - .WithParsed(context => { + Parser.Default.ParseArguments(args) + .WithParsed(options => { + var context = new CompilerContext + { + Options = options + }; + CompilerDriver.Compile(context); }); } diff --git a/Source/Backlang-Compiler/Properties/launchSettings.json b/Source/Backlang-Compiler/Properties/launchSettings.json index 897e10f2..fbc368a8 100644 --- a/Source/Backlang-Compiler/Properties/launchSettings.json +++ b/Source/Backlang-Compiler/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Backlang-Compiler": { "commandName": "Project", - "commandLineArgs": "-i compilation.back -o compilation --target dotnet" + "commandLineArgs": "-i compilation.back -o compilation --target dotnet --framework net7.0" } } } \ No newline at end of file diff --git a/Source/Backlang-Compiler/compilation.back b/Source/Backlang-Compiler/compilation.back index 5309930f..ec2c5489 100644 --- a/Source/Backlang-Compiler/compilation.back +++ b/Source/Backlang-Compiler/compilation.back @@ -1,11 +1,11 @@ -import System; -public struct Point -{ - let callback: (i32, bool) -> none; - let tuple: (i32, char); - //let a: i32*; // this make a crashy crashy crash -} +func main() { + if true { + return 1; + } + else { + return 2; + } -Main { + print("after"); } \ No newline at end of file diff --git a/Source/Backlang-Compiler/test.back b/Source/Backlang-Compiler/test.back deleted file mode 100644 index c71e2968..00000000 --- a/Source/Backlang-Compiler/test.back +++ /dev/null @@ -1,103 +0,0 @@ -func main() { - let something : i32* = 12; - doSomething(); - return 0; -} - -func print(value : T) - T is i8-i32 { - -} - -class SomeModel : #ViewModel() { - #notify(Name : string); -} - - -type int = i32; -const bits = 32; - -global flags : Flags = #EAX; -global behavior : Flags = &250; - -func doSomething() { - print("Hello World"); - let arr = [12, 42, 3]; - - print(arr[0]); - - - /* - multi - line - comment - */ - - while true { - doSomething(2); - } - - for x : i32 in 1..12 { - print(x); - } - - for x in arr { - print(x); - } -} - -enum Colors { - Red = 0xFF0000, - Green = 0x00FF00, - Blue = 0x0000FF -} - -bitfield Flags { - DivideByZero = 0, - IsZero = 1, -} - -enum Instructions { - Invalid, - Move = 1 << 12, - Pop, -} - -enum StringEnum : string { - prefix = "Hello", - suffix = "World", -} - -func tests(something : i32) { //check if something is none, if not return none - - let kkk = sizeof; - let mm : i32 = default; - let mn = default; - - let a = 12; - let mut b : i32 = 42; - let c = &a; - - let d : list = []; - - let e = list> = []; - - a <-> b; - - a = b % 3; - - if a <= 12 and a >= 13 { - - } - else { - - } - - let result = match a with - 12 => 13, - > 13 => 15, - _ => 0, - [1, .. , 3]; // need to be implemented - - return none; -} \ No newline at end of file diff --git a/Source/Backlang.Codeanalysis/Backlang.Codeanalysis.csproj b/Source/Backlang.Codeanalysis/Backlang.Codeanalysis.csproj index 380ce683..8f5261fd 100644 --- a/Source/Backlang.Codeanalysis/Backlang.Codeanalysis.csproj +++ b/Source/Backlang.Codeanalysis/Backlang.Codeanalysis.csproj @@ -18,6 +18,7 @@ + True @@ -27,8 +28,24 @@ + + + + True + True + Resources.resx + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + diff --git a/Source/Backlang.Codeanalysis/Core/Attributes/BinaryOperatorInfoAttribute.cs b/Source/Backlang.Codeanalysis/Core/Attributes/BinaryOperatorInfoAttribute.cs index 82bd78e4..0e51f25e 100644 --- a/Source/Backlang.Codeanalysis/Core/Attributes/BinaryOperatorInfoAttribute.cs +++ b/Source/Backlang.Codeanalysis/Core/Attributes/BinaryOperatorInfoAttribute.cs @@ -1,5 +1,4 @@ -using Backlang.Codeanalysis.Core.Attributes; -using Backlang.Codeanalysis.Parsing.Precedences; +using Backlang.Codeanalysis.Parsing.Precedences; namespace Backlang.Codeanalysis.Core.Attributes; @@ -13,5 +12,4 @@ public BinaryOperatorInfoAttribute(int precedence) : base(precedence, isUnary: f public BinaryOperatorInfoAttribute(BinaryOpPrecedences precedence) : this((int)precedence) { } - -} +} \ No newline at end of file diff --git a/Source/Backlang.Codeanalysis/Core/BaseLexer.cs b/Source/Backlang.Codeanalysis/Core/BaseLexer.cs index c87e56e0..9348e31b 100644 --- a/Source/Backlang.Codeanalysis/Core/BaseLexer.cs +++ b/Source/Backlang.Codeanalysis/Core/BaseLexer.cs @@ -61,7 +61,7 @@ protected void ReportError() _column++; var range = SourceRange.New(_document, new IndexRange(_position, 1)); - Messages.Add(Message.Error($"Unknown Charakter '{Current()}'", range)); + Messages.Add(Message.Error(new LocalizableString(ErrorID.UnknownCharacter, Current().ToString()), range)); Advance(); } } \ No newline at end of file diff --git a/Source/Backlang.Codeanalysis/Core/ErrorID.cs b/Source/Backlang.Codeanalysis/Core/ErrorID.cs new file mode 100644 index 00000000..f38e9301 --- /dev/null +++ b/Source/Backlang.Codeanalysis/Core/ErrorID.cs @@ -0,0 +1,37 @@ +namespace Backlang.Codeanalysis.Core; + +public enum ErrorID : int +{ + UnexpecedType = 0001, + InvalidModifierCombination = 0002, + UnknownCharacter = 0003, + UnterminatedCharLiteral = 0004, + UnknownExpression = 0005, + UnknownLiteral = 0006, + ForbiddenTrailingComma = 0007, + BitfieldNotLiteral = 0008, + UnexpecedTypeMember = 0009, + ExpectedTypeLiteral = 0010, + UnknownSwitchOption = 0011, + NoCatchBlock = 0012, + EmptyFile = 0013, + ExpectedIdentifier = 0014, + UnterminatedStringLiteral = 0015, + NotClosedMultilineComment = 0016, + Expected = 0017, + DuplicateModifier = 0018, + NamespaceAlreadyImported = 0019, + RunnableTypeButNoEntrypoint = 0020, + CannotBeFoundDidYouMean = 0021, + CannotImplementTypeNotFound = 0022, + CannotImplementUnitType = 0023, + AlreadyDeclared = 0024, + TypeMismatch = 0025, + UnitTypeMismatch = 0026, + CannotBeResolved = 0027, + CannotFindFunction = 0028, + TargetNotFound = 0029, + NotDefined = 0030, + CannotDeduceType = 31, + DeducingTypeNotPossible = 32, +} \ No newline at end of file diff --git a/Source/Backlang.Codeanalysis/Core/LevensteinDistance.cs b/Source/Backlang.Codeanalysis/Core/LevensteinDistance.cs index 8923202e..6f766472 100644 --- a/Source/Backlang.Codeanalysis/Core/LevensteinDistance.cs +++ b/Source/Backlang.Codeanalysis/Core/LevensteinDistance.cs @@ -6,23 +6,27 @@ public static int Calculate(string source, string target) { if (string.IsNullOrEmpty(source)) { - if (string.IsNullOrEmpty(target)) return 0; - return target.Length; + return string.IsNullOrEmpty(target) ? 0 : target.Length; + } + + if (string.IsNullOrEmpty(target)) + { + return source.Length; } - if (string.IsNullOrEmpty(target)) return source.Length; if (source.Length > target.Length) { - var temp = target; - target = source; - source = temp; + (source, target) = (target, source); } var m = target.Length; var n = source.Length; var distance = new int[2, m + 1]; // Initialize the distance matrix - for (var j = 1; j <= m; j++) distance[0, j] = j; + for (var j = 1; j <= m; j++) + { + distance[0, j] = j; + } var currentRow = 0; for (var i = 1; i <= n; ++i) @@ -49,7 +53,10 @@ public static string Suggest(string source, IEnumerable possibilities) foreach (var str in possibilities) { - if (str == "Main") continue; + if (str == "Main") + { + continue; + } var distance = Calculate(source, str); if (distance <= lastDistance) diff --git a/Source/Backlang.Codeanalysis/Core/LocalizableString.cs b/Source/Backlang.Codeanalysis/Core/LocalizableString.cs new file mode 100644 index 00000000..23261e2e --- /dev/null +++ b/Source/Backlang.Codeanalysis/Core/LocalizableString.cs @@ -0,0 +1,53 @@ +using Backlang.Codeanalysis.Properties; +using System.Resources; + +namespace Backlang.Codeanalysis.Core; + +public readonly struct LocalizableString +{ + public readonly ErrorID ErrorID; + public readonly string[] Arguments; + public readonly string FallbackValue; + + private static readonly ResourceManager _resourceManager = new("Backlang.Codeanalysis.Properties.Resources", typeof(Resources).Assembly); + + public LocalizableString(ErrorID errorID, params string[] arguments) + { + ErrorID = errorID; + Arguments = arguments; + FallbackValue = null; + } + + public LocalizableString(ErrorID errorID) : this() + { + ErrorID = errorID; + Arguments = new[] { "NO_VALUE" }; + } + + public LocalizableString(string fallbackValue) : this() + { + FallbackValue = fallbackValue; + } + + public static implicit operator string(LocalizableString lstr) + { + if (!string.IsNullOrEmpty(lstr.FallbackValue)) + { + return lstr.FallbackValue; + } + + var resourceID = $"BL({(int)lstr.ErrorID:D4})"; + + return string.Format(resourceID + ": " + _resourceManager.GetString(resourceID), args: lstr.Arguments); + } + + public static implicit operator LocalizableString(ErrorID id) + { + return new(id); + } + + public static implicit operator LocalizableString(string message) + { + return new(message); + } +} \ No newline at end of file diff --git a/Source/Backlang.Codeanalysis/Core/ParsingHelpers.cs b/Source/Backlang.Codeanalysis/Core/ParsingHelpers.cs index 9a6071d1..fb106b1f 100644 --- a/Source/Backlang.Codeanalysis/Core/ParsingHelpers.cs +++ b/Source/Backlang.Codeanalysis/Core/ParsingHelpers.cs @@ -24,13 +24,15 @@ public static LNodeList ParseSeperated( if (parser.Iterator.IsMatch(seperator) && parser.Iterator.Peek(1).Type == terminator) { - parser.Iterator.Messages.Add(Message.Error("Trailing comma is forbidden")); + parser.AddError(ErrorID.ForbiddenTrailingComma); parser.Iterator.Match(seperator); } } while (parser.Iterator.ConsumeIfMatch(seperator)); if (consumeTerminator) + { parser.Iterator.Match(terminator); + } return list; } diff --git a/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/BitFieldMemberDeclaration.cs b/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/BitFieldMemberDeclaration.cs index 1ae438df..6a3e77b1 100644 --- a/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/BitFieldMemberDeclaration.cs +++ b/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/BitFieldMemberDeclaration.cs @@ -6,8 +6,7 @@ public sealed class BitFieldMemberDeclaration : IParsePoint { public static LNode Parse(TokenIterator iterator, Parser parser) { - Token nameToken = null; - + Token nameToken; if (iterator.Current.Type == TokenType.Identifier) { nameToken = iterator.Current; @@ -25,7 +24,7 @@ public static LNode Parse(TokenIterator iterator, Parser parser) if (!value[0].HasValue) { - iterator.Messages.Add(Message.Error("Bitfield member declaration only allows literals", value.Range)); + parser.AddError(Core.ErrorID.BitfieldNotLiteral, value.Range); } return SyntaxTree.Factory.Tuple(SyntaxTree.Factory.Id(nameToken.Text).WithRange(nameToken), value); diff --git a/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/EnumMemberDeclaration.cs b/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/EnumMemberDeclaration.cs index 630ded87..f93ddcbd 100644 --- a/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/EnumMemberDeclaration.cs +++ b/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/EnumMemberDeclaration.cs @@ -6,7 +6,7 @@ public class EnumMemberDeclaration : IParsePoint { public static LNode Parse(TokenIterator iterator, Parser parser) { - Annotation.TryParse(parser, out var annotations); + _ = Annotation.TryParse(parser, out var annotations); var memberNameToken = iterator.Match(TokenType.Identifier); LNode value = LNode.Missing; diff --git a/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/ImplementationDeclaration.cs b/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/ImplementationDeclaration.cs index 902a3d42..557f651c 100644 --- a/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/ImplementationDeclaration.cs +++ b/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/ImplementationDeclaration.cs @@ -7,8 +7,6 @@ public class ImplementationDeclaration : IParsePoint public static LNode Parse(TokenIterator iterator, Parser parser) { var keywordToken = iterator.Prev; - - LNode target = null; var targets = new LNodeList(); while (iterator.Current.Type != TokenType.OpenCurly && !parser.Iterator.IsMatch(TokenType.EOF)) @@ -28,6 +26,7 @@ public static LNode Parse(TokenIterator iterator, Parser parser) } } + LNode target; if (targets.Count == 1) { target = targets[0]; @@ -43,8 +42,8 @@ public static LNode Parse(TokenIterator iterator, Parser parser) LNodeList body = new(); while (!parser.Iterator.IsMatch(TokenType.EOF) && iterator.Current.Type != TokenType.CloseCurly) { - Annotation.TryParse(parser, out var annotations); - Modifier.TryParse(parser, out var modifiers); + _ = Annotation.TryParse(parser, out var annotations); + _ = Modifier.TryParse(parser, out var modifiers); body.Add(parser.InvokeParsePoint(parser.DeclarationParsePoints).PlusAttrs(annotations).PlusAttrs(modifiers)); } diff --git a/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/MacroBlockDeclaration.cs b/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/MacroBlockDeclaration.cs index ccef9306..a0c2d104 100644 --- a/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/MacroBlockDeclaration.cs +++ b/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/MacroBlockDeclaration.cs @@ -12,7 +12,10 @@ public static LNode Parse(TokenIterator iterator, Parser parser) foreach (var p in parser.StatementParsePoints) { - if (pp.ContainsKey(p.Key)) continue; + if (pp.ContainsKey(p.Key)) + { + continue; + } pp.Add(p.Key, p.Value); } diff --git a/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/ParameterDeclaration.cs b/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/ParameterDeclaration.cs index 48ceab47..4d55cd9f 100644 --- a/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/ParameterDeclaration.cs +++ b/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/ParameterDeclaration.cs @@ -7,11 +7,16 @@ public sealed class ParameterDeclaration : IParsePoint { public static LNode Parse(TokenIterator iterator, Parser parser) { - Annotation.TryParse(parser, out var annotations); + _ = Annotation.TryParse(parser, out var annotations); var keywordToken = iterator.Current; var name = iterator.Match(TokenType.Identifier); + var assertNotNull = false; + if(iterator.ConsumeIfMatch(TokenType.Exclamation)) { + assertNotNull = true; + } + iterator.Match(TokenType.Colon); var type = TypeLiteral.Parse(iterator, parser); @@ -25,6 +30,10 @@ public static LNode Parse(TokenIterator iterator, Parser parser) defaultValue = Expression.Parse(parser); } + if(assertNotNull) { + annotations = annotations.Add(LNode.Id(Symbols.AssertNonNull)); + } + return SyntaxTree.Factory.Var(type, name.Text, defaultValue).PlusAttrs(annotations) .WithRange(keywordToken, iterator.Prev); } diff --git a/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/TypeMemberDeclaration.cs b/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/TypeMemberDeclaration.cs index 13b074e6..e20d61f8 100644 --- a/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/TypeMemberDeclaration.cs +++ b/Source/Backlang.Codeanalysis/Parsing/AST/Declarations/TypeMemberDeclaration.cs @@ -7,8 +7,8 @@ public sealed class TypeMemberDeclaration : IParsePoint { public static LNode Parse(TokenIterator iterator, Parser parser) { - Annotation.TryParse(parser, out var annotations); - Modifier.TryParse(parser, out var modifiers); + _ = Annotation.TryParse(parser, out var annotations); + _ = Modifier.TryParse(parser, out var modifiers); LNode declaration = LNode.Missing; @@ -28,7 +28,7 @@ public static LNode Parse(TokenIterator iterator, Parser parser) { var range = new SourceRange(parser.Document, iterator.Current.Start, iterator.Current.Text.Length); - parser.Messages.Add(Message.Error($"Expected Function, Property or Field-declaration for Type, but got {iterator.Current}", range)); + parser.AddError(new(Core.ErrorID.UnexpecedTypeMember, iterator.Current.Text), range); iterator.NextToken(); } @@ -71,13 +71,12 @@ public static LNode ParseProperty(TokenIterator iterator, Parser parser) LNode setter = LNode.Missing; var needModifier = false; - LNodeList modifier; - Modifier.TryParse(parser, out modifier); + _ = Modifier.TryParse(parser, out var modifier); if (iterator.IsMatch(TokenType.Get)) { iterator.NextToken(); - LNodeList args = LNode.List(); + var args = LNode.List(); if (iterator.IsMatch(TokenType.Semicolon)) { iterator.NextToken(); @@ -90,11 +89,15 @@ public static LNode ParseProperty(TokenIterator iterator, Parser parser) needModifier = true; } - if (needModifier) Modifier.TryParse(parser, out modifier); + if (needModifier) + { + _ = Modifier.TryParse(parser, out modifier); + } + if (iterator.IsMatch(TokenType.Set)) { iterator.NextToken(); - LNodeList args = LNode.List(); + var args = LNode.List(); if (iterator.IsMatch(TokenType.Semicolon)) { iterator.NextToken(); @@ -108,7 +111,7 @@ public static LNode ParseProperty(TokenIterator iterator, Parser parser) else if (iterator.IsMatch(TokenType.Init)) { iterator.NextToken(); - LNodeList args = LNode.List(); + var args = LNode.List(); if (iterator.IsMatch(TokenType.Semicolon)) { iterator.NextToken(); diff --git a/Source/Backlang.Codeanalysis/Parsing/AST/DocComment.cs b/Source/Backlang.Codeanalysis/Parsing/AST/DocComment.cs new file mode 100644 index 00000000..3896ed9e --- /dev/null +++ b/Source/Backlang.Codeanalysis/Parsing/AST/DocComment.cs @@ -0,0 +1,26 @@ +using Loyc; +using Loyc.Syntax; +using System.Xml; + +namespace Backlang.Codeanalysis.Parsing.AST; + +public sealed class DocComment +{ + public static LNode Parse(Parser parser) + { + var comment = parser.Iterator.Match(TokenType.DocComment); + var xmlDocument = new XmlDocument(); + xmlDocument.LoadXml($"{comment.Text}"); + + return LNode.Trivia(Symbol.For("DocComment"), xmlDocument); + } + + public static bool TryParse(Parser parser, out LNode node) + { + var result = parser.Iterator.IsMatch(TokenType.DocComment); + + node = result ? Parse(parser) : LNode.Missing; + + return result; + } +} diff --git a/Source/Backlang.Codeanalysis/Parsing/AST/Modifier.cs b/Source/Backlang.Codeanalysis/Parsing/AST/Modifier.cs index 226868d7..57fd4c39 100644 --- a/Source/Backlang.Codeanalysis/Parsing/AST/Modifier.cs +++ b/Source/Backlang.Codeanalysis/Parsing/AST/Modifier.cs @@ -1,4 +1,5 @@ -using Loyc; +using Backlang.Codeanalysis.Core; +using Loyc; using Loyc.Syntax; using System.Collections.Immutable; @@ -6,7 +7,7 @@ namespace Backlang.Codeanalysis.Parsing.AST; public sealed class Modifier { - private static ImmutableDictionary possibleModifiers = new Dictionary() { + private static readonly ImmutableDictionary _possibleModifiers = new Dictionary() { { TokenType.Static, CodeSymbols.Static }, { TokenType.Public, CodeSymbols.Public }, { TokenType.Protected, CodeSymbols.Protected }, @@ -22,12 +23,12 @@ public static bool TryParse(Parser parser, out LNodeList node) { var modifiers = new LNodeList(); - while (possibleModifiers.ContainsKey(parser.Iterator.Current.Type)) + while (_possibleModifiers.ContainsKey(parser.Iterator.Current.Type)) { - var modifier = ParseSingle(parser.Iterator, parser); + var modifier = ParseSingle(parser.Iterator); if (modifiers.Contains(modifier)) { - parser.AddError($"Modifier '{modifier.Name.Name}' is already applied"); + parser.AddError(new(ErrorID.DuplicateModifier, modifier.Name.Name), modifier.Range); continue; } @@ -38,10 +39,10 @@ public static bool TryParse(Parser parser, out LNodeList node) return modifiers.Count > 0; } - private static LNode ParseSingle(TokenIterator iterator, Parser parser) + private static LNode ParseSingle(TokenIterator iterator) { var currentToken = iterator.Current; - var mod = SyntaxTree.Factory.Id(possibleModifiers[currentToken.Type]); + var mod = SyntaxTree.Factory.Id(_possibleModifiers[currentToken.Type]); iterator.NextToken(); return mod.WithRange(currentToken); diff --git a/Source/Backlang.Codeanalysis/Parsing/AST/Signature.cs b/Source/Backlang.Codeanalysis/Parsing/AST/Signature.cs index b19c0c1b..a020ff25 100644 --- a/Source/Backlang.Codeanalysis/Parsing/AST/Signature.cs +++ b/Source/Backlang.Codeanalysis/Parsing/AST/Signature.cs @@ -9,23 +9,19 @@ public static LNode Parse(Parser parser) { var iterator = parser.Iterator; - LNode name; - if (!TypeLiteral.TryParse(parser, out name)) + if (!TypeLiteral.TryParse(parser, out var name)) { - //error var range = new SourceRange(parser.Document, iterator.Current.Start, iterator.Current.Text.Length); - parser.Messages.Add(Message.Error( - $"Expected Identifier, got {iterator.Current.Text}", range)); - } - LNode returnType = SyntaxTree.Type("none", LNode.List()); - LNodeList generics = new(); + parser.AddError(new(Core.ErrorID.ExpectedTypeLiteral, iterator.Current.Text), range); + } + LNode returnType = LNode.Missing; iterator.Match(TokenType.OpenParen); var parameters = ParameterDeclaration.ParseList(parser); - + LNodeList generics = new(); while (iterator.IsMatch(TokenType.Where)) { iterator.NextToken(); @@ -34,7 +30,11 @@ public static LNode Parse(Parser parser) var bases = new LNodeList(); do { - if (iterator.IsMatch(TokenType.Comma)) iterator.NextToken(); + if (iterator.IsMatch(TokenType.Comma)) + { + iterator.NextToken(); + } + bases.Add(TypeLiteral.Parse(iterator, parser)); } while (iterator.IsMatch(TokenType.Comma)); diff --git a/Source/Backlang.Codeanalysis/Parsing/AST/Statements/ContinueStatement.cs b/Source/Backlang.Codeanalysis/Parsing/AST/Statements/Loops/ContinueStatement.cs similarity index 84% rename from Source/Backlang.Codeanalysis/Parsing/AST/Statements/ContinueStatement.cs rename to Source/Backlang.Codeanalysis/Parsing/AST/Statements/Loops/ContinueStatement.cs index 5fd7d1fd..02bff3ba 100644 --- a/Source/Backlang.Codeanalysis/Parsing/AST/Statements/ContinueStatement.cs +++ b/Source/Backlang.Codeanalysis/Parsing/AST/Statements/Loops/ContinueStatement.cs @@ -1,6 +1,6 @@ using Loyc.Syntax; -namespace Backlang.Codeanalysis.Parsing.AST.Statements; +namespace Backlang.Codeanalysis.Parsing.AST.Statements.Loops; public sealed class ContinueStatement : IParsePoint { diff --git a/Source/Backlang.Codeanalysis/Parsing/AST/Statements/Loops/DoWhileStatement.cs b/Source/Backlang.Codeanalysis/Parsing/AST/Statements/Loops/DoWhileStatement.cs new file mode 100644 index 00000000..394b9078 --- /dev/null +++ b/Source/Backlang.Codeanalysis/Parsing/AST/Statements/Loops/DoWhileStatement.cs @@ -0,0 +1,21 @@ +using Loyc.Syntax; + +namespace Backlang.Codeanalysis.Parsing.AST.Statements.Loops; + +public sealed class DoWhileStatement : IParsePoint +{ + public static LNode Parse(TokenIterator iterator, Parser parser) + { + var keywordToken = iterator.Prev; + + var body = Statement.ParseBlock(parser); + + iterator.Match(TokenType.While); + + var cond = Expression.Parse(parser); + + iterator.Match(TokenType.Semicolon); + + return SyntaxTree.DoWhile(body, cond).WithRange(keywordToken, iterator.Prev); + } +} \ No newline at end of file diff --git a/Source/Backlang.Codeanalysis/Parsing/AST/Statements/ForStatement.cs b/Source/Backlang.Codeanalysis/Parsing/AST/Statements/Loops/ForStatement.cs similarity index 92% rename from Source/Backlang.Codeanalysis/Parsing/AST/Statements/ForStatement.cs rename to Source/Backlang.Codeanalysis/Parsing/AST/Statements/Loops/ForStatement.cs index 80012272..5a246b2c 100644 --- a/Source/Backlang.Codeanalysis/Parsing/AST/Statements/ForStatement.cs +++ b/Source/Backlang.Codeanalysis/Parsing/AST/Statements/Loops/ForStatement.cs @@ -1,6 +1,6 @@ using Loyc.Syntax; -namespace Backlang.Codeanalysis.Parsing.AST.Statements; +namespace Backlang.Codeanalysis.Parsing.AST.Statements.Loops; public sealed class ForStatement : IParsePoint { diff --git a/Source/Backlang.Codeanalysis/Parsing/AST/Statements/WhileStatement.cs b/Source/Backlang.Codeanalysis/Parsing/AST/Statements/Loops/WhileStatement.cs similarity index 81% rename from Source/Backlang.Codeanalysis/Parsing/AST/Statements/WhileStatement.cs rename to Source/Backlang.Codeanalysis/Parsing/AST/Statements/Loops/WhileStatement.cs index 0a8f33ba..af52d52d 100644 --- a/Source/Backlang.Codeanalysis/Parsing/AST/Statements/WhileStatement.cs +++ b/Source/Backlang.Codeanalysis/Parsing/AST/Statements/Loops/WhileStatement.cs @@ -1,12 +1,11 @@ using Loyc.Syntax; -namespace Backlang.Codeanalysis.Parsing.AST.Statements; +namespace Backlang.Codeanalysis.Parsing.AST.Statements.Loops; public sealed class WhileStatement : IParsePoint { public static LNode Parse(TokenIterator iterator, Parser parser) { - // while true { 42; } var keywordToken = iterator.Prev; var cond = Expression.Parse(parser); diff --git a/Source/Backlang.Codeanalysis/Parsing/AST/Statements/SwitchStatement.cs b/Source/Backlang.Codeanalysis/Parsing/AST/Statements/SwitchStatement.cs index d64d6b8d..0fbd2751 100644 --- a/Source/Backlang.Codeanalysis/Parsing/AST/Statements/SwitchStatement.cs +++ b/Source/Backlang.Codeanalysis/Parsing/AST/Statements/SwitchStatement.cs @@ -28,23 +28,34 @@ public static LNode Parse(TokenIterator iterator, Parser parser) while (!parser.Iterator.IsMatch(TokenType.CloseCurly)) { - bool autoBreak = iterator.IsMatch(TokenType.Break); + var autoBreak = iterator.IsMatch(TokenType.Break); - if (autoBreak) iterator.Match(TokenType.Break); + if (autoBreak) + { + iterator.Match(TokenType.Break); + } if (iterator.IsMatch(TokenType.Case)) + { cases.Add(ParseCase(parser, autoBreak)); + } else if (iterator.IsMatch(TokenType.If)) + { cases.Add(ParseIf(parser, autoBreak)); + } else if (iterator.IsMatch(TokenType.When)) + { cases.Add(ParseWhen(parser, autoBreak)); + } else if (iterator.IsMatch(TokenType.Default)) + { cases.Add(ParseDefault(parser, autoBreak)); + } else { var range = new SourceRange(parser.Document, iterator.Current.Start, iterator.Current.Text.Length); - parser.Messages.Add(Message.Error("Switch Statement can only have case, if or default, but got " + iterator.Current.Text, range)); + parser.AddError(new(Core.ErrorID.UnknownSwitchOption), range); return LNode.Missing; } } @@ -65,7 +76,9 @@ private static LNode ParseCase(Parser parser, bool autoBreak) var body = Statement.ParseOneOrBlock(parser); if (autoBreak) + { body = body.PlusArg(LNode.Call(CodeSymbols.Break)); + } return SyntaxTree.Case(condition, body).WithRange(keywordToken, parser.Iterator.Prev); } @@ -79,7 +92,9 @@ private static LNode ParseDefault(Parser parser, bool autoBreak) var body = Statement.ParseOneOrBlock(parser); if (autoBreak) + { body = body.PlusArg(LNode.Call(CodeSymbols.Break)); + } return SyntaxTree.Case(LNode.Call(CodeSymbols.Default), body); } @@ -95,7 +110,9 @@ private static LNode ParseIf(Parser parser, bool autoBreak) var body = Statement.ParseOneOrBlock(parser); if (autoBreak) + { body = body.PlusArg(LNode.Call(CodeSymbols.Break)); + } return SyntaxTree.If(condition, body, SyntaxTree.Factory.Braces()); } @@ -105,8 +122,7 @@ private static LNode ParseWhen(Parser parser, bool autoBreak) parser.Iterator.Match(TokenType.When); LNode binOp = LNode.Missing; - LNode right = LNode.Missing; - + LNode right; if (Expression.GetBinaryOperatorPrecedence(parser.Iterator.Current.Type) > 0) { // with binary expression @@ -122,7 +138,8 @@ private static LNode ParseWhen(Parser parser, bool autoBreak) { var range = new SourceRange(parser.Document, parser.Iterator.Current.Start, parser.Iterator.Current.Text.Length); - parser.Messages.Add(Message.Error($"Expected {TokenType.Identifier} but got {parser.Iterator.Current.Type}", range)); + parser.AddError(new(Core.ErrorID.ExpectedIdentifier, parser.Iterator.Current.Text), range); + return LNode.Missing; } var name = LNode.Id(parser.Iterator.Current.Text); @@ -138,7 +155,9 @@ private static LNode ParseWhen(Parser parser, bool autoBreak) var body = Statement.ParseOneOrBlock(parser); if (autoBreak) + { body = body.PlusArg(LNode.Call(CodeSymbols.Break)); + } return SyntaxTree.When(binOp, right, body); } diff --git a/Source/Backlang.Codeanalysis/Parsing/AST/Statements/TryStatement.cs b/Source/Backlang.Codeanalysis/Parsing/AST/Statements/TryStatement.cs index e9c49fda..0081b8a3 100644 --- a/Source/Backlang.Codeanalysis/Parsing/AST/Statements/TryStatement.cs +++ b/Source/Backlang.Codeanalysis/Parsing/AST/Statements/TryStatement.cs @@ -6,7 +6,6 @@ public sealed class TryStatement : IParsePoint { public static LNode Parse(TokenIterator iterator, Parser parser) { - // try {} catch (Exception e) {} catch (Exception e) {} finally {} var keywordToken = iterator.Prev; var body = Statement.ParseOneOrBlock(parser); LNodeList catches = new(LNode.Missing); @@ -15,7 +14,7 @@ public static LNode Parse(TokenIterator iterator, Parser parser) { var range = new SourceRange(parser.Document, iterator.Current.Start, iterator.Current.Text.Length); - parser.Messages.Add(Message.Error("Expected at least one catch block at try statement", range)); + parser.AddError(new(Core.ErrorID.NoCatchBlock), range); } while (iterator.Current.Type == TokenType.Catch) diff --git a/Source/Backlang.Codeanalysis/Parsing/AST/Symbols.cs b/Source/Backlang.Codeanalysis/Parsing/AST/Symbols.cs index 2405e16c..d64a70b0 100644 --- a/Source/Backlang.Codeanalysis/Parsing/AST/Symbols.cs +++ b/Source/Backlang.Codeanalysis/Parsing/AST/Symbols.cs @@ -22,6 +22,7 @@ public static class Symbols public static readonly Symbol Mutable = GSymbol.Get("#mutable"); public static readonly Symbol PointerType = GSymbol.Get("#type*"); public static readonly Symbol RefType = GSymbol.Get("#type&"); + public static readonly Symbol NullableType = GSymbol.Get("#type?"); public static readonly Symbol Range = GSymbol.Get("'.."); public static readonly Symbol ToExpand = GSymbol.Get("'to_expand'"); public static readonly Symbol TypeLiteral = GSymbol.Get("#type"); @@ -30,4 +31,5 @@ public static class Symbols public static readonly Symbol Init = GSymbol.Get("#init"); public static readonly Symbol Unit = GSymbol.Get("#unit"); public static readonly Symbol UnitDecl = GSymbol.Get("#unitDecl"); + public static readonly Symbol AssertNonNull = GSymbol.Get("#notnull"); } \ No newline at end of file diff --git a/Source/Backlang.Codeanalysis/Parsing/AST/TypeLiteral.cs b/Source/Backlang.Codeanalysis/Parsing/AST/TypeLiteral.cs index eafa36e2..fa63d02d 100644 --- a/Source/Backlang.Codeanalysis/Parsing/AST/TypeLiteral.cs +++ b/Source/Backlang.Codeanalysis/Parsing/AST/TypeLiteral.cs @@ -1,4 +1,5 @@ -using Loyc.Syntax; +using Backlang.Codeanalysis.Core; +using Loyc.Syntax; namespace Backlang.Codeanalysis.Parsing.AST; @@ -28,84 +29,32 @@ public static LNode Parse(TokenIterator iterator, Parser parser) typeNode = SyntaxTree.RefType(typeNode).WithRange(typeToken, iterator.Prev); } - else if (iterator.IsMatch(TokenType.OpenSquare)) - { + else if(iterator.IsMatch(TokenType.Questionmark)) { iterator.NextToken(); - var dimensions = 1; - - while (iterator.IsMatch(TokenType.Comma)) - { - dimensions++; - - iterator.NextToken(); - } - - iterator.Match(TokenType.CloseSquare); - - typeNode = SyntaxTree.Array(typeNode, dimensions).WithRange(typeToken, iterator.Prev); + typeNode = SyntaxTree.NullableType(typeNode).WithRange(typeToken, iterator.Prev); + } + else if (iterator.IsMatch(TokenType.OpenSquare)) + { + typeNode = ParseArrayType(iterator, typeNode, typeToken); } else if (iterator.IsMatch(TokenType.LessThan)) { - iterator.NextToken(); - - while (!iterator.IsMatch(TokenType.GreaterThan)) - { - if (iterator.IsMatch(TokenType.Identifier)) - { - args.Add(Parse(iterator, parser)); - } - - if (!iterator.IsMatch(TokenType.GreaterThan)) - { - iterator.Match(TokenType.Comma); - } - } - - iterator.Match(TokenType.GreaterThan); - - typeNode = SyntaxTree.Type(typename, args).WithRange(typeToken, parser.Iterator.Prev); + typeNode = ParseGenericType(iterator, parser, typeToken, typename, args); } } else if (iterator.IsMatch(TokenType.None)) { - typeNode = SyntaxTree.Type("none", LNode.List()).WithRange(typeToken); // Missing is the normal type for none + typeNode = SyntaxTree.Type("none", LNode.List()).WithRange(typeToken); iterator.NextToken(); } else if (iterator.IsMatch(TokenType.OpenParen)) { - iterator.Match(TokenType.OpenParen); - - var parameters = new LNodeList(); - while (parser.Iterator.Current.Type != TokenType.CloseParen) - { - parameters.Add(TypeLiteral.Parse(iterator, parser)); - - if (parser.Iterator.Current.Type != TokenType.CloseParen) - { - parser.Iterator.Match(TokenType.Comma); - } - } - - parser.Iterator.Match(TokenType.CloseParen); - - if (iterator.Current.Type == TokenType.Arrow) - { - iterator.NextToken(); - - var returnType = TypeLiteral.Parse(iterator, parser); - - typeNode = SyntaxTree.Factory.Call(CodeSymbols.Fn, - LNode.List(returnType, LNode.Missing, LNode.Call(CodeSymbols.AltList, parameters))).WithRange(typeToken, iterator.Prev); - } - else - { - typeNode = SyntaxTree.Factory.Tuple(parameters).WithRange(typeToken, iterator.Prev); - } + typeNode = ParseFunctionOrTupleType(iterator, parser, typeToken); } else { - parser.AddError("Expected Identifier, TupleType or Function-Signature as TypeLiteral, but got " + iterator.Current.Type); + parser.AddError(new(ErrorID.UnexpecedType, TokenIterator.GetTokenRepresentation(iterator.Current.Type))); //ToDo: Add Range typeNode = LNode.Missing; iterator.NextToken(); @@ -127,4 +76,82 @@ public static bool TryParse(Parser parser, out LNode node) return true; } + + private static LNode ParseFunctionOrTupleType(TokenIterator iterator, Parser parser, Token typeToken) + { + LNode typeNode; + iterator.Match(TokenType.OpenParen); + + var parameters = new LNodeList(); + while (parser.Iterator.Current.Type != TokenType.CloseParen) + { + parameters.Add(TypeLiteral.Parse(iterator, parser)); + + if (parser.Iterator.Current.Type != TokenType.CloseParen) + { + parser.Iterator.Match(TokenType.Comma); + } + } + + parser.Iterator.Match(TokenType.CloseParen); + + if (iterator.Current.Type == TokenType.Arrow) + { + iterator.NextToken(); + + var returnType = Parse(iterator, parser); + + typeNode = SyntaxTree.Factory.Call(CodeSymbols.Fn, + LNode.List(returnType, LNode.Missing, LNode.Call(CodeSymbols.AltList, parameters))).WithRange(typeToken, iterator.Prev); + } + else + { + typeNode = SyntaxTree.Factory.Tuple(parameters).WithRange(typeToken, iterator.Prev); + } + + return typeNode; + } + + private static LNode ParseGenericType(TokenIterator iterator, Parser parser, Token typeToken, string typename, LNodeList args) + { + LNode typeNode; + iterator.NextToken(); + + while (!iterator.IsMatch(TokenType.GreaterThan)) + { + if (iterator.IsMatch(TokenType.Identifier)) + { + args.Add(Parse(iterator, parser)); + } + + if (!iterator.IsMatch(TokenType.GreaterThan)) + { + iterator.Match(TokenType.Comma); + } + } + + iterator.Match(TokenType.GreaterThan); + + typeNode = SyntaxTree.Type(typename, args).WithRange(typeToken, parser.Iterator.Prev); + return typeNode; + } + + private static LNode ParseArrayType(TokenIterator iterator, LNode typeNode, Token typeToken) + { + iterator.NextToken(); + + var dimensions = 1; + + while (iterator.IsMatch(TokenType.Comma)) + { + dimensions++; + + iterator.NextToken(); + } + + iterator.Match(TokenType.CloseSquare); + + typeNode = SyntaxTree.Array(typeNode, dimensions).WithRange(typeToken, iterator.Prev); + return typeNode; + } } \ No newline at end of file diff --git a/Source/Backlang.Codeanalysis/Parsing/Expression.cs b/Source/Backlang.Codeanalysis/Parsing/Expression.cs index f4d044e3..3203e117 100644 --- a/Source/Backlang.Codeanalysis/Parsing/Expression.cs +++ b/Source/Backlang.Codeanalysis/Parsing/Expression.cs @@ -48,26 +48,35 @@ static Expression() public static LNode Parse(Parser parser, ParsePoints parsePoints = null, int parentPrecedence = 0) { - LNode left; - var unaryOperatorPrecedence = GetPreUnaryOperatorPrecedence(parser.Iterator.Current.Type); + LNode left = null; + var preUnaryOperatorPrecedence = GetPreUnaryOperatorPrecedence(parser.Iterator.Current.Type); - if (unaryOperatorPrecedence != 0 && unaryOperatorPrecedence >= parentPrecedence) + if (preUnaryOperatorPrecedence != 0 && preUnaryOperatorPrecedence >= parentPrecedence) { - var operatorToken = parser.Iterator.NextToken(); + if (IsPreUnary(parser.Iterator.Current.Type)) + { + var operatorToken = parser.Iterator.NextToken(); - var operand = Parse(parser, parsePoints, unaryOperatorPrecedence + 1); + var operand = Parse(parser, parsePoints, preUnaryOperatorPrecedence + 1); - left = SyntaxTree.Unary(GSymbol.Get($"'{operatorToken.Text}"), operand).WithRange(operatorToken.Start, operand.Range.EndIndex).WithStyle(NodeStyle.PrefixNotation); + left = SyntaxTree.Unary(GSymbol.Get($"'{operatorToken.Text}"), operand).WithRange(operatorToken.Start, operand.Range.EndIndex).WithStyle(NodeStyle.PrefixNotation); + } } else { - left = parser.ParsePrimary(parsePoints); + left = parser.ParsePrimary(); - if (IsPostUnary(parser.Iterator.Current.Type)) + //parsing postunarys for: hello?; + var postUnaryOperatorPrecedence = GetPostUnaryOperatorPrecedence(parser.Iterator.Current.Type); + + if (postUnaryOperatorPrecedence != 0 && postUnaryOperatorPrecedence >= parentPrecedence) { - var operatorToken = parser.Iterator.NextToken(); + if (IsPostUnary(parser.Iterator.Current.Type)) + { + var unaryOperatorToken = parser.Iterator.NextToken(); - left = SyntaxTree.Unary(GSymbol.Get($"'{operatorToken.Text}"), left).WithRange(left.Range.StartIndex, operatorToken.End); + left = SyntaxTree.Unary(GSymbol.Get($"'suf{unaryOperatorToken.Text}"), left).WithRange(left.Range.StartIndex, unaryOperatorToken.End).WithStyle(NodeStyle.Operator); + } } } @@ -83,6 +92,19 @@ public static LNode Parse(Parser parser, ParsePoints parsePoints = null, int par var right = Parse(parser, parsePoints, precedence); left = SyntaxTree.Binary(GSymbol.Get($"'{operatorToken.Text}"), left, right).WithRange(left.Range.StartIndex, right.Range.StartIndex); + + // parsing postunary for: Hello::new()? = false; + var postUnaryOperatorPrecedence = GetPostUnaryOperatorPrecedence(parser.Iterator.Current.Type); + + if (postUnaryOperatorPrecedence != 0 && postUnaryOperatorPrecedence >= parentPrecedence) + { + if (IsPostUnary(parser.Iterator.Current.Type)) + { + var unaryOperatorToken = parser.Iterator.NextToken(); + + left = SyntaxTree.Unary(GSymbol.Get($"'suf{unaryOperatorToken.Text}"), left).WithRange(left.Range.StartIndex, unaryOperatorToken.End).WithStyle(NodeStyle.Operator); + } + } } return left; @@ -94,7 +116,9 @@ public static LNodeList ParseList(Parser parser, TokenType terminator, bool cons } private static int GetPreUnaryOperatorPrecedence(TokenType kind) => PreUnaryOperators.GetValueOrDefault(kind); + private static int GetPostUnaryOperatorPrecedence(TokenType kind) => PostUnaryOperators.GetValueOrDefault(kind); + private static bool IsPreUnary(TokenType kind) => PreUnaryOperators.ContainsKey(kind); private static bool IsPostUnary(TokenType kind) => PostUnaryOperators.ContainsKey(kind); private class ExpressionParser : IParsePoint diff --git a/Source/Backlang.Codeanalysis/Parsing/LNodeExtensions.cs b/Source/Backlang.Codeanalysis/Parsing/LNodeExtensions.cs index a511e8fa..cadc13a9 100644 --- a/Source/Backlang.Codeanalysis/Parsing/LNodeExtensions.cs +++ b/Source/Backlang.Codeanalysis/Parsing/LNodeExtensions.cs @@ -1,4 +1,5 @@ using Loyc.Syntax; +using Backlang.Codeanalysis.Parsing.AST; namespace Backlang.Codeanalysis.Parsing; @@ -23,4 +24,9 @@ public static LNode FromToken(this LNodeFactory factory, Token token) { return factory.Id(token.Text).WithRange(token); } + + public static bool IsNoneType(this LNode node) + { + return node.Name == Symbols.TypeLiteral && node.Args[0].Args[0].Name.Name == "none" && node.Args[0].Args[0].IsId; + } } \ No newline at end of file diff --git a/Source/Backlang.Codeanalysis/Parsing/Lexer.cs b/Source/Backlang.Codeanalysis/Parsing/Lexer.cs index 72e80ce4..9a3d68a2 100644 --- a/Source/Backlang.Codeanalysis/Parsing/Lexer.cs +++ b/Source/Backlang.Codeanalysis/Parsing/Lexer.cs @@ -1,7 +1,10 @@ using Backlang.Codeanalysis.Core; using Backlang.Codeanalysis.Core.Attributes; +using Loyc.Collections.Impl; using Loyc.Syntax; using System.Reflection; +using System.Text; +using System.Transactions; namespace Backlang.Codeanalysis.Parsing; @@ -32,6 +35,11 @@ static Lexer() protected override Token NextToken() { + if (IsMatch("///")) + { + return LexDocComment(); + } + SkipWhitespaces(); SkipComments(); @@ -87,6 +95,36 @@ protected override Token NextToken() return Token.Invalid; } + private Token LexDocComment() + { + var oldPos = _position; + var contentBuilder = new StringBuilder(); + + do + { + contentBuilder.AppendLine(ReadLine().TrimStart('/').Trim()); + } while (IsMatch("///")); + + return new Token(TokenType.DocComment, contentBuilder.ToString(), oldPos, _position, _line, _column); + } + + + private string ReadLine() + { + var sb = new StringBuilder(); + + while (Current() != '\n') + { + Advance(); + _column++; + } + Advance(); + _column = 0; + _line++; + + return sb.ToString(); + } + private static bool IsBinaryDigit(char c) { return c == '0' || c == '1'; @@ -94,7 +132,7 @@ private static bool IsBinaryDigit(char c) private static bool IsHex(char c) { - return c >= '0' && c <= '9' || c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F'; + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } private TokenType GetOperatorKind(Token identifier) @@ -165,7 +203,7 @@ private Token LexCharLiteral() if (Current() == '\n' || Current() == '\r') { var range = new SourceRange(_document, _column, 1); - Messages.Add(Message.Error($"Unterminated CharLiteral", range)); + Messages.Add(Message.Error(ErrorID.UnterminatedCharLiteral, range)); return Token.Invalid; } @@ -216,8 +254,10 @@ private Token LexDoubleQuoteString() { if (Current() == '\n' || Current() == '\r') { - var range = new SourceRange(_document, _column, 1); - Messages.Add(Message.Error($"Unterminated String", range)); + var range = new SourceRange(_document, _position, 1); + Messages.Add(Message.Error(ErrorID.UnterminatedStringLiteral, range)); + + return new Token(TokenType.StringLiteral, _document.Text.Slice(oldpos, _position - oldpos - 1).ToString(), oldpos - 1, _position, _line, oldColumn); } Advance(); @@ -301,7 +341,7 @@ private void SkipComments() } else if (IsMatch("/*")) { - int oldcol = _column; + int oldpos = _position; Advance(); Advance(); @@ -328,8 +368,9 @@ private void SkipComments() } else { - var range = new SourceRange(_document, _column, 1); - Messages.Add(Message.Error("Multiline comment is not closed.", range)); + var range = new SourceRange(_document, oldpos, _position); + Messages.Add(Message.Error(ErrorID.NotClosedMultilineComment, range)); + return; } diff --git a/Source/Backlang.Codeanalysis/Parsing/Message.cs b/Source/Backlang.Codeanalysis/Parsing/Message.cs index a8b47fee..4d6621da 100644 --- a/Source/Backlang.Codeanalysis/Parsing/Message.cs +++ b/Source/Backlang.Codeanalysis/Parsing/Message.cs @@ -1,4 +1,5 @@ -using Loyc.Syntax; +using Backlang.Codeanalysis.Core; +using Loyc.Syntax; namespace Backlang.Codeanalysis.Parsing; @@ -28,14 +29,14 @@ public Message(MessageSeverity severity, string text, SourceRange range) public MessageSeverity Severity { get; set; } public string Text { get; set; } - public static Message Error(string message, SourceRange range) + public static Message Error(LocalizableString message, SourceRange range) { return new Message(MessageSeverity.Error, message, range); } - public static Message Error(string message) + public static Message Error(LocalizableString message) { - return Error(message, SourceRange.Synthetic); + return new Message(MessageSeverity.Error, message, SourceRange.Synthetic); } public static Message Info(string message, SourceRange range) diff --git a/Source/Backlang.Codeanalysis/Parsing/Parser.Expressions.cs b/Source/Backlang.Codeanalysis/Parsing/Parser.Expressions.cs index 5398f47d..dba73acb 100644 --- a/Source/Backlang.Codeanalysis/Parsing/Parser.Expressions.cs +++ b/Source/Backlang.Codeanalysis/Parsing/Parser.Expressions.cs @@ -8,7 +8,7 @@ namespace Backlang.Codeanalysis.Parsing; public sealed partial class Parser { - private Dictionary _lits = new() { + private readonly Dictionary _lits = new() { {"ub", CodeSymbols.UInt8}, {"us", CodeSymbols.UInt16}, {"u", CodeSymbols.UInt32}, @@ -22,22 +22,19 @@ public sealed partial class Parser {"d", Symbols.Float64}, }; - public void AddError(string message, SourceRange range) + public void AddError(LocalizableString message, SourceRange range) { Messages.Add(Message.Error(message, range)); } - public void AddError(string message) + public void AddError(LocalizableString message) { Messages.Add(Message.Error(message, new SourceRange(Document, Iterator.Current.Start, Iterator.Current.Text.Length))); } internal LNode ParsePrimary(ParsePoints parsePoints = null) { - if (parsePoints == null) - { - parsePoints = ExpressionParsePoints; - } + parsePoints ??= ExpressionParsePoints; return Iterator.Current.Type switch { @@ -63,7 +60,7 @@ private LNode ParseArrayLiteral() return SyntaxTree.Factory.Call(CodeSymbols.Array, elements).WithRange(startToken, Iterator.Prev); } - private LNode Invalid(string message) + private LNode Invalid(LocalizableString message) { AddError(message); @@ -75,15 +72,19 @@ private LNode InvokeExpressionParsePoint(ParsePoints parsePoints) var token = Iterator.Current; var type = token.Type; - if (parsePoints.ContainsKey(type)) + if (parsePoints.TryGetValue(type, out var value)) { Iterator.NextToken(); - return parsePoints[type](Iterator, this).WithRange(token, Iterator.Prev); + return value(Iterator, this).WithRange(token, Iterator.Prev); + } + else if (type == Token.Invalid.Type) + { + return LNode.Missing; } else { - return Invalid($"Unexpected Expression '{Iterator.Current.Text}'"); + return Invalid(ErrorID.UnknownExpression); } } @@ -131,7 +132,7 @@ private LNode ParseHexNumber() } return SyntaxTree.Factory.Call(CodeSymbols.Int32, - LNode.List(SyntaxTree.Factory.Literal(int.Parse(valueToken.Text, NumberStyles.HexNumber)))) + LNode.List(SyntaxTree.Factory.Literal(result))) .WithRange(Iterator.Prev); } @@ -162,14 +163,14 @@ private LNode ParseNumber() if (Iterator.Current.Type == TokenType.Identifier) { - if (_lits.ContainsKey(Iterator.Current.Text.ToLower())) + if (_lits.TryGetValue(Iterator.Current.Text.ToLower(), out var value)) { - result = SyntaxTree.Factory.Call(_lits[Iterator.Current.Text.ToLower()], + result = SyntaxTree.Factory.Call(value, LNode.List(result)).WithRange(Iterator.Prev, Iterator.Current); } else { - AddError($"Unknown Literal {Iterator.Current.Text}"); + AddError(new(ErrorID.UnknownLiteral, Iterator.Current.Text)); result = LNode.Missing; } diff --git a/Source/Backlang.Codeanalysis/Parsing/Parser.ParsePoints.cs b/Source/Backlang.Codeanalysis/Parsing/Parser.ParsePoints.cs index 4868b885..3176612f 100644 --- a/Source/Backlang.Codeanalysis/Parsing/Parser.ParsePoints.cs +++ b/Source/Backlang.Codeanalysis/Parsing/Parser.ParsePoints.cs @@ -1,8 +1,10 @@ -using Backlang.Codeanalysis.Parsing.AST; +using Backlang.Codeanalysis.Core; +using Backlang.Codeanalysis.Parsing.AST; using Backlang.Codeanalysis.Parsing.AST.Declarations; using Backlang.Codeanalysis.Parsing.AST.Expressions; using Backlang.Codeanalysis.Parsing.AST.Expressions.Match; using Backlang.Codeanalysis.Parsing.AST.Statements; +using Backlang.Codeanalysis.Parsing.AST.Statements.Loops; using Loyc.Syntax; namespace Backlang.Codeanalysis.Parsing; @@ -50,6 +52,7 @@ public void InitParsePoints() AddStatementParsePoint(TokenType.Switch); AddStatementParsePoint(TokenType.If); AddStatementParsePoint(TokenType.While); + AddStatementParsePoint(TokenType.Do); AddStatementParsePoint(TokenType.Try); AddStatementParsePoint(TokenType.For); AddStatementParsePoint(TokenType.Identifier); @@ -80,10 +83,11 @@ public LNodeList InvokeDeclarationParsePoints(TokenType terminator = TokenType.E var body = new LNodeList(); while (Iterator.Current.Type != terminator) { + DocComment.TryParse(this, out var docComment); Annotation.TryParse(this, out var annotation); Modifier.TryParse(this, out var modifiers); - var item = InvokeParsePoint(parsePoints)?.PlusAttrs(annotation).PlusAttrs(modifiers); + var item = InvokeParsePoint(parsePoints)?.PlusAttrs(annotation).PlusAttrs(modifiers).PlusAttr(docComment); if (item != null) { @@ -107,7 +111,7 @@ public LNode InvokeParsePoint(ParsePoints parsePoints) var range = new SourceRange(Document, Iterator.Current.Start, Iterator.Current.Text.Length); - Messages.Add(Message.Error($"Unexpected '{Iterator.Current.Text}'", range)); + AddError(new(ErrorID.UnknownExpression, Iterator.Current.Text), range); Iterator.NextToken(); diff --git a/Source/Backlang.Codeanalysis/Parsing/Parser.cs b/Source/Backlang.Codeanalysis/Parsing/Parser.cs index a2016daa..e9bfbc5a 100644 --- a/Source/Backlang.Codeanalysis/Parsing/Parser.cs +++ b/Source/Backlang.Codeanalysis/Parsing/Parser.cs @@ -1,3 +1,4 @@ +using Backlang.Codeanalysis.Core; using Backlang.Codeanalysis.Parsing.AST; using Loyc.Syntax; @@ -29,7 +30,7 @@ public static CompilationUnit Parse(SourceDocument src) return new CompilationUnit { Body = LNode.List(LNode.Missing), - Messages = new() { Message.Error("Empty File", SourceRange.Synthetic) }, + Messages = new() { Message.Error(ErrorID.EmptyFile, SourceRange.Synthetic) }, Document = document }; } diff --git a/Source/Backlang.Codeanalysis/Parsing/Precedences/BinaryOpPrecedences.cs b/Source/Backlang.Codeanalysis/Parsing/Precedences/BinaryOpPrecedences.cs index d06485ad..65ecd4ce 100644 --- a/Source/Backlang.Codeanalysis/Parsing/Precedences/BinaryOpPrecedences.cs +++ b/Source/Backlang.Codeanalysis/Parsing/Precedences/BinaryOpPrecedences.cs @@ -1,4 +1,5 @@ namespace Backlang.Codeanalysis.Parsing.Precedences; + public enum BinaryOpPrecedences { Casting = 1, // as @@ -17,12 +18,12 @@ public enum BinaryOpPrecedences Or = Percent, Comparisons = Or, // < <= >= > SwapOperator = 2, - + FunctionCalls = 7, // . :: PipeOperator = 8, // |> - OperationShortcuts = 9, // += -= *= /= + Dot = 8, + OperationShortcuts = 2, // += -= *= /= Equals = OperationShortcuts, - -} +} \ No newline at end of file diff --git a/Source/Backlang.Codeanalysis/Parsing/SourceDocument.cs b/Source/Backlang.Codeanalysis/Parsing/SourceDocument.cs index 3fb74e6e..a1997247 100644 --- a/Source/Backlang.Codeanalysis/Parsing/SourceDocument.cs +++ b/Source/Backlang.Codeanalysis/Parsing/SourceDocument.cs @@ -5,7 +5,7 @@ namespace Backlang.Codeanalysis.Parsing; public sealed class SourceDocument { - private SourceFile _document; + private readonly SourceFile _document; public SourceDocument(string filename) { diff --git a/Source/Backlang.Codeanalysis/Parsing/SyntaxTree.cs b/Source/Backlang.Codeanalysis/Parsing/SyntaxTree.cs index 052cf158..3bdd80a6 100644 --- a/Source/Backlang.Codeanalysis/Parsing/SyntaxTree.cs +++ b/Source/Backlang.Codeanalysis/Parsing/SyntaxTree.cs @@ -168,6 +168,11 @@ public static LNode RefType(LNode type) return Factory.Call(Symbols.RefType, LNode.List(type)); } + public static LNode NullableType(LNode type) + { + return Factory.Call(Symbols.NullableType, LNode.List(type)); + } + public static LNode Signature(LNode name, LNode type, LNodeList args, LNodeList generics) { return Factory.Call(CodeSymbols.Fn, LNode.List( @@ -249,8 +254,15 @@ public static LNode UnitDeclaration(Token nameToken) return Factory.Call(Symbols.UnitDecl, LNode.List(Factory.FromToken(nameToken))); } - internal static LNode TypeOfExpression(LNode type) + public static LNode TypeOfExpression(LNode type) { return Factory.Call(CodeSymbols.Typeof, LNode.List(type)); } + + public static LNode DoWhile(LNode body, LNode cond) + { + return Factory.Call(CodeSymbols.DoWhile, LNode.List(body, cond)); + } + + } \ No newline at end of file diff --git a/Source/Backlang.Codeanalysis/Parsing/TokenIterator.cs b/Source/Backlang.Codeanalysis/Parsing/TokenIterator.cs index 835cf923..1d57db42 100644 --- a/Source/Backlang.Codeanalysis/Parsing/TokenIterator.cs +++ b/Source/Backlang.Codeanalysis/Parsing/TokenIterator.cs @@ -72,7 +72,7 @@ public Token Match(TokenType kind) return NextToken(); Messages.Add( - Message.Error($"Expected '{GetTokenRepresentation(kind)}' but got '{GetTokenRepresentation(Current.Type)}'", + Message.Error(new(Core.ErrorID.Expected, GetTokenRepresentation(kind), GetTokenRepresentation(Current.Type)), new SourceRange(_document, Current.Start, Current.Text.Length))); NextToken(); diff --git a/Source/Backlang.Codeanalysis/Parsing/TokenType.cs b/Source/Backlang.Codeanalysis/Parsing/TokenType.cs index f216ccd0..8cbc83db 100644 --- a/Source/Backlang.Codeanalysis/Parsing/TokenType.cs +++ b/Source/Backlang.Codeanalysis/Parsing/TokenType.cs @@ -15,7 +15,7 @@ public enum TokenType CharLiteral, [Lexeme(".")] - [BinaryOperatorInfo(BinaryOpPrecedences.FunctionCalls)] + [BinaryOperatorInfo(BinaryOpPrecedences.Dot)] Dot, [Lexeme("::")] @@ -62,6 +62,10 @@ public enum TokenType [Lexeme("*")] Star, + [PostUnaryOperatorInfo(UnaryOpPrecedences.Negate)] + [Lexeme(".*")] + DotAsterisk, // for namespace imports + [BinaryOperatorInfo(BinaryOpPrecedences.Hat)] [Lexeme("**")] StarStar, @@ -91,6 +95,7 @@ public enum TokenType [Lexeme("-=")] [Lexeme("|=")] [Lexeme("&=")] + [Lexeme("!!=")] [BinaryOperatorInfo(BinaryOpPrecedences.OperationShortcuts)] EqualsShortcutToken, @@ -98,9 +103,6 @@ public enum TokenType [BinaryOperatorInfo(BinaryOpPrecedences.Equals)] EqualsToken, - [Lexeme("#")] - Hash, - [Lexeme("<=")] [BinaryOperatorInfo(BinaryOpPrecedences.Comparisons)] LessThanEqual, @@ -149,6 +151,10 @@ public enum TokenType [PreUnaryOperatorInfo(UnaryOpPrecedences.Dollar)] Dollar, + [Lexeme("?")] + [PostUnaryOperatorInfo(UnaryOpPrecedences.Negate)] + Questionmark, + [Lexeme("==")] [BinaryOperatorInfo(BinaryOpPrecedences.EqualsEquals)] EqualsEquals, @@ -291,6 +297,9 @@ public enum TokenType [Keyword("while")] While, + [Keyword("do")] + Do, + [Keyword("in")] In, @@ -353,4 +362,5 @@ public enum TokenType [Keyword("unit")] Unit, + DocComment, } \ No newline at end of file diff --git a/Source/Backlang.Codeanalysis/Properties/Resources.Designer.cs b/Source/Backlang.Codeanalysis/Properties/Resources.Designer.cs new file mode 100644 index 00000000..f11e57ec --- /dev/null +++ b/Source/Backlang.Codeanalysis/Properties/Resources.Designer.cs @@ -0,0 +1,342 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace Backlang.Codeanalysis.Properties { + using System; + + + /// + /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// + // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert + // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Backlang.Codeanalysis.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Expected Identifier, TupleType or Function-Signature as TypeLiteral, but got {0} ähnelt. + /// + internal static string BL_0001_ { + get { + return ResourceManager.GetString("BL(0001)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Invalid Modifier Combination ähnelt. + /// + internal static string BL_0002_ { + get { + return ResourceManager.GetString("BL(0002)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Unknown Character '{0}' ähnelt. + /// + internal static string BL_0003_ { + get { + return ResourceManager.GetString("BL(0003)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Unterminated Char-Literal ähnelt. + /// + internal static string BL_0004_ { + get { + return ResourceManager.GetString("BL(0004)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Unexpected Expression {0} ähnelt. + /// + internal static string BL_0005_ { + get { + return ResourceManager.GetString("BL(0005)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Unknown Literal {0} ähnelt. + /// + internal static string BL_0006_ { + get { + return ResourceManager.GetString("BL(0006)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Trailing comma is forbidden ähnelt. + /// + internal static string BL_0007_ { + get { + return ResourceManager.GetString("BL(0007)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Bitfield member declaration only allows literals ähnelt. + /// + internal static string BL_0008_ { + get { + return ResourceManager.GetString("BL(0008)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Expected Function, Property or Field-declaration for Type, but got {0} ähnelt. + /// + internal static string BL_0009_ { + get { + return ResourceManager.GetString("BL(0009)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Expected Type but got {0} ähnelt. + /// + internal static string BL_0010_ { + get { + return ResourceManager.GetString("BL(0010)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Unexpected Switch Option ähnelt. + /// + internal static string BL_0011_ { + get { + return ResourceManager.GetString("BL(0011)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Expected at least one catch block ähnelt. + /// + internal static string BL_0012_ { + get { + return ResourceManager.GetString("BL(0012)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die SourceFile is empty ähnelt. + /// + internal static string BL_0013_ { + get { + return ResourceManager.GetString("BL(0013)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Expected Identifier but got {0} ähnelt. + /// + internal static string BL_0014_ { + get { + return ResourceManager.GetString("BL(0014)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Unterminated String-Literal ähnelt. + /// + internal static string BL_0015_ { + get { + return ResourceManager.GetString("BL(0015)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Multiline comment is not closed ähnelt. + /// + internal static string BL_0016_ { + get { + return ResourceManager.GetString("BL(0016)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Expected '{0}' but got '{1}' ähnelt. + /// + internal static string BL_0017_ { + get { + return ResourceManager.GetString("BL(0017)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Duplicate Modifier '{0}' ähnelt. + /// + internal static string BL_0018_ { + get { + return ResourceManager.GetString("BL(0018)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Namespace '{0}' already imported ähnelt. + /// + internal static string BL_0019_ { + get { + return ResourceManager.GetString("BL(0019)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Got OutputType 'Exe' but couldn't find entry point. ähnelt. + /// + internal static string BL_0020_ { + get { + return ResourceManager.GetString("BL(0020)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die {0} cannot be found. Did you mean '{1}'? ähnelt. + /// + internal static string BL_0021_ { + get { + return ResourceManager.GetString("BL(0021)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Cannot implement '{0}', type not found ähnelt. + /// + internal static string BL_0022_ { + get { + return ResourceManager.GetString("BL(0022)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Cannot implement unit type '{0}' ähnelt. + /// + internal static string BL_0023_ { + get { + return ResourceManager.GetString("BL(0023)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die '{0}' already declared ähnelt. + /// + internal static string BL_0024_ { + get { + return ResourceManager.GetString("BL(0024)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Type mismatch {0} {1} ähnelt. + /// + internal static string BL_0025_ { + get { + return ResourceManager.GetString("BL(0025)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Unit Type mismatch {0} {1} ähnelt. + /// + internal static string BL_0026_ { + get { + return ResourceManager.GetString("BL(0026)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die {0} cannot be resolved ähnelt. + /// + internal static string BL_0027_ { + get { + return ResourceManager.GetString("BL(0027)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Cannot find function '{0}' ähnelt. + /// + internal static string BL_0028_ { + get { + return ResourceManager.GetString("BL(0028)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Target '{0}' cannot be found ähnelt. + /// + internal static string BL_0029_ { + get { + return ResourceManager.GetString("BL(0029)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Identifier '{0}' is not defined ähnelt. + /// + internal static string BL_0030_ { + get { + return ResourceManager.GetString("BL(0030)", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Cannot set type ähnelt. + /// + internal static string BL_0031_ { + get { + return ResourceManager.GetString("BL(0031)", resourceCulture); + } + } + } +} diff --git a/Source/Backlang.Codeanalysis/Properties/Resources.resx b/Source/Backlang.Codeanalysis/Properties/Resources.resx new file mode 100644 index 00000000..01400d02 --- /dev/null +++ b/Source/Backlang.Codeanalysis/Properties/Resources.resx @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Expected Identifier, TupleType or Function-Signature as TypeLiteral, but got {0} + + + Invalid Modifier Combination + + + Unknown Character '{0}' + + + Unterminated Char-Literal + + + Unexpected Expression {0} + + + Unknown Literal {0} + + + Trailing comma is forbidden + + + Bitfield member declaration only allows literals + + + Expected Function, Property or Field-declaration for Type, but got {0} + + + Expected Type but got {0} + + + Unexpected Switch Option + + + Expected at least one catch block + + + SourceFile is empty + + + Expected Identifier but got {0} + + + Unterminated String-Literal + + + Multiline comment is not closed + + + Expected '{0}' but got '{1}' + + + Duplicate Modifier '{0}' + + + Namespace '{0}' already imported + + + Got OutputType 'Exe' but couldn't find entry point. + + + {0} cannot be found. Did you mean '{1}'? + + + Cannot implement '{0}', type not found + + + Cannot implement unit type '{0}' + + + '{0}' already declared + + + Type mismatch {0} {1} + + + Unit Type mismatch {0} {1} + + + {0} cannot be resolved + + + Cannot find function '{0}' + + + Target '{0}' cannot be found + + + Identifier '{0}' is not defined + + + Cannot set type + + + Cannot deduce Type. Declare it manually! + + \ No newline at end of file diff --git a/Source/Backlang.Contracts/Backlang.Contracts.csproj b/Source/Backlang.Contracts/Backlang.Contracts.csproj index 6db8d956..8730ee04 100644 --- a/Source/Backlang.Contracts/Backlang.Contracts.csproj +++ b/Source/Backlang.Contracts/Backlang.Contracts.csproj @@ -3,7 +3,7 @@ net7.0 enable - enable + disable preview True True @@ -16,11 +16,11 @@ - - + + - + diff --git a/Source/Backlang.Contracts/CompilerContext.cs b/Source/Backlang.Contracts/CompilerContext.cs index 2bf1e0b1..4782f995 100644 --- a/Source/Backlang.Contracts/CompilerContext.cs +++ b/Source/Backlang.Contracts/CompilerContext.cs @@ -1,65 +1,46 @@ -namespace Backlang.Contracts; +using Backlang.Codeanalysis.Core; + +namespace Backlang.Contracts; #nullable disable public sealed class CompilerContext { - public IEnumerable writeMethods; - public ICompilationTarget CompilationTarget; public PluginContainer Plugins; public DescribedAssembly Assembly { get; set; } + public PlaygroundData Playground { get; set; } = new(); + public CompilerCliOptions Options { get; set; } = new(); + + public Stream OutputStream { get; set; } + public TypeResolver Binder { get; set; } = new(); public Scope GlobalScope { get; } = new(null); - public Dictionary ImportetNamespaces { get; set; } = new(); + public FileScopeData FileScope { get; set; } = new(); public List BodyCompilations { get; set; } = new(); public TypeEnvironment Environment { get; set; } - [Option('i', "input", Required = true, HelpText = "Input files to be compiled.")] - public IEnumerable InputFiles { get; set; } - public string[] MacroReferences { get; set; } public List Messages { get; set; } = new(); - [Option('o', "output", Required = true, HelpText = "Output filename")] - public string OutputFilename { get; set; } - public string OutputPath { get; set; } - [Option('p', "print-tree", Required = false, HelpText = "Output files as tree")] - public bool OutputTree { get; set; } - - [Option('t', "type", Required = false, HelpText = "Outputtype")] - public string OutputType { get; set; } - public string ProjectFile { get; set; } - [Option('r', "reference", Required = false, HelpText = "References of the assembly")] - public IEnumerable References { get; set; } = Array.Empty(); - public string ResultingOutputPath { get; set; } - [Option("target", Required = false, HelpText = "For which platform to compile to")] - public string Target { get; set; } - public string TempOutputPath { get; set; } public List Trees { get; set; } = new(); - [Option('e', longName: "embedd", HelpText = "Embedd files into the assembly as resource")] - public IEnumerable EmbeddedResource { get; set; } - public string CorLib { get; set; } - [Option('v', longName: "version", HelpText = "Set the assembly version")] - public string Version { get; set; } - - public void AddError(LNode node, string msg) + public void AddError(LNode node, LocalizableString msg) { if (node.Range.Source is not SourceFile) return; diff --git a/Source/Backlang.Contracts/ConversionUtils.cs b/Source/Backlang.Contracts/ConversionUtils.cs index 2c134dbe..6967f16b 100644 --- a/Source/Backlang.Contracts/ConversionUtils.cs +++ b/Source/Backlang.Contracts/ConversionUtils.cs @@ -39,11 +39,38 @@ public static void SetAccessModifier(LNode node, DescribedMember type, AccessMod } } + public static QualifiedName QualifyNamespace(string @namespace) + { + var spl = @namespace.Split('.'); + + QualifiedName? name = null; + + foreach (var path in spl) + { + if (name == null) + { + name = new SimpleName(path).Qualify(); + continue; + } + + name = new SimpleName(path).Qualify(name.Value); + } + + return name.Value; + } + public static QualifiedName GetQualifiedName(LNode lNode) { bool isPointer = false; PointerKind pointerKind = PointerKind.Transient; + + if (lNode is ("'suf.*", var ns)) + { + var qualifiedNs = GetQualifiedName(ns); + + return new SimpleName("*").Qualify(qualifiedNs); + } if (lNode is ("#type*", var arg)) { isPointer = true; @@ -64,7 +91,7 @@ public static QualifiedName GetQualifiedName(LNode lNode) if (lNode.ArgCount == 3 && lNode is ("#fn", var retType, _, var args)) { - string typename = retType == LNode.Missing ? "Action`" + (args.ArgCount) : "Func`" + (args.ArgCount + 1); + string typename = retType.IsNoneType() ? "Action`" + (args.ArgCount) : "Func`" + (args.ArgCount + 1); return new SimpleName(typename).Qualify("System"); } diff --git a/Source/Backlang.Contracts/Datas/CompilerCliOptions.cs b/Source/Backlang.Contracts/Datas/CompilerCliOptions.cs new file mode 100644 index 00000000..1ce4245a --- /dev/null +++ b/Source/Backlang.Contracts/Datas/CompilerCliOptions.cs @@ -0,0 +1,40 @@ +namespace Backlang.Contracts; + +public class CompilerCliOptions +{ + public CompilerCliOptions() + { + References = Array.Empty(); + EmbeddedResource = Array.Empty(); + } + + [Option('i', "input", Required = true, HelpText = "Input files to be compiled.")] + public IEnumerable InputFiles { get; set; } + + [Option('o', "output", Required = true, HelpText = "Output filename")] + public string OutputFilename { get; set; } + + [Option('p', "print-tree", Required = false, HelpText = "Output files as tree")] + public bool OutputTree { get; set; } + + [Option('t', "type", Required = false, HelpText = "Outputtype")] + public string OutputType { get; set; } + + [Option('r', "reference", Required = false, HelpText = "References of the assembly")] + public IEnumerable References { get; set; } + + [Option("target", Required = false, HelpText = "For which platform to compile to")] + public string Target { get; set; } + + [Option('e', longName: "embedd", HelpText = "Embedd files into the assembly as resource")] + public IEnumerable EmbeddedResource { get; set; } + + [Option('v', longName: "version", HelpText = "Set the assembly version")] + public string Version { get; set; } + + [Option('f', longName: "framework", HelpText = "On which framework should the assembly be runned on")] + public string TargetFramework { get; set; } + + [Option("debug", HelpText = "Wait for debugger been attached for debugging plugins")] + public bool WaitForDebugger { get; set; } +} \ No newline at end of file diff --git a/Source/Backlang.Contracts/Datas/FileScopeData.cs b/Source/Backlang.Contracts/Datas/FileScopeData.cs new file mode 100644 index 00000000..b4fbdcbf --- /dev/null +++ b/Source/Backlang.Contracts/Datas/FileScopeData.cs @@ -0,0 +1,11 @@ +namespace Backlang.Contracts; + +public struct FileScopeData +{ + public FileScopeData() + { + ImportetNamespaces = new(); + } + + public Dictionary ImportetNamespaces { get; set; } +} diff --git a/Source/Backlang.Contracts/Datas/PlaygroundData.cs b/Source/Backlang.Contracts/Datas/PlaygroundData.cs new file mode 100644 index 00000000..76d7dd1a --- /dev/null +++ b/Source/Backlang.Contracts/Datas/PlaygroundData.cs @@ -0,0 +1,7 @@ +namespace Backlang.Contracts; + +public struct PlaygroundData +{ + public bool IsPlayground { get; set; } + public string Source { get; set; } +} \ No newline at end of file diff --git a/Source/Backlang.Contracts/Names.cs b/Source/Backlang.Contracts/Names.cs index 73cb9841..bae58fcd 100644 --- a/Source/Backlang.Contracts/Names.cs +++ b/Source/Backlang.Contracts/Names.cs @@ -5,4 +5,5 @@ public static class Names public static readonly string Extensions = "Extensions"; public static readonly string MainMethod = "Main"; public static readonly string FreeFunctions = "FreeFunctions"; + public static readonly string ArrayValues = "''"; } \ No newline at end of file diff --git a/Source/Backlang.Contracts/NamespaceImports.cs b/Source/Backlang.Contracts/NamespaceImports.cs index 5b692322..450019af 100644 --- a/Source/Backlang.Contracts/NamespaceImports.cs +++ b/Source/Backlang.Contracts/NamespaceImports.cs @@ -1,4 +1,6 @@ using Backlang.Driver; +using Backlang.Codeanalysis.Core; +using Backlang.Contracts.Scoping.Items; namespace Backlang.Contracts; @@ -14,13 +16,43 @@ public void ImportNamespace(LNode importStatement, CompilerContext context) if (ImportedNamespaces.Contains(qualifiedNs)) { - context.AddError(importStatement, $"Namespace '{qualifiedNs}' already imported"); + context.AddError(importStatement, new(ErrorID.NamespaceAlreadyImported, qualifiedNs.ToString())); return; } ImportedNamespaces.Add(qualifiedNs); + + ExpandNamespaceImports(context); } } + private void ExpandNamespaceImports(CompilerContext context) + { + for (var i = 0; i < ImportedNamespaces.Count; i++) + { + var import = ImportedNamespaces[i]; + var imp = import.ToString(); + + if (!imp.EndsWith(".*")) + { + continue; + } + + var withoutWildcard = imp[..^2]; + + context.Binder.TryResolveNamespace(ConversionUtils.QualifyNamespace(withoutWildcard), out var foundNamespaces); + if (foundNamespaces == null) + { + //ToDo: add error that namespace has no subnamespace(s) + return; + } + + ImportedNamespaces.Remove(import); + foreach (var foundNs in foundNamespaces.Namespaces) + { + ImportedNamespaces.Add(foundNs.Key.Qualify(withoutWildcard)); + } + } + } } diff --git a/Source/Backlang.Contracts/Scoping/Scope.cs b/Source/Backlang.Contracts/Scoping/Scope.cs index 055dc2f0..bb451ded 100644 --- a/Source/Backlang.Contracts/Scoping/Scope.cs +++ b/Source/Backlang.Contracts/Scoping/Scope.cs @@ -11,8 +11,10 @@ public class Scope public Scope(Scope parent) { Parent = parent; + TypeAliases = new(); } + public Dictionary TypeAliases { get; set; } public Scope Parent { get; set; } public bool Add(ScopeItem item) diff --git a/Source/Backlang.Contracts/Semantic/ModifierCheck.cs b/Source/Backlang.Contracts/Semantic/ModifierCheck.cs new file mode 100644 index 00000000..5f6a0fd1 --- /dev/null +++ b/Source/Backlang.Contracts/Semantic/ModifierCheck.cs @@ -0,0 +1,35 @@ +using Backlang.Codeanalysis.Core; + +namespace Backlang.Contracts.Semantic; + +internal class ModifierCheck : ISemanticCheck +{ + public void Check(CompilationUnit tree, CompilerContext context) + { + var nodesWithModifiers = tree.Body + .SelectMany(_ => _.DescendantsAndSelf()).Where(IsModifiableNode).ToArray(); + + foreach (var node in nodesWithModifiers) + { + CheckForInvalidModifierCombination(node, context); + } + } + + private void CheckForInvalidModifierCombination(LNode node, CompilerContext context) + { + var attrs = node.Attrs; + var condition = (attrs.Contains(LNode.Id(CodeSymbols.Public)) && attrs.Contains(LNode.Id(CodeSymbols.Private))) + || (attrs.Contains(LNode.Id(CodeSymbols.Public)) && attrs.Contains(LNode.Id(CodeSymbols.Internal))) + || (attrs.Contains(LNode.Id(CodeSymbols.Private)) && attrs.Contains(LNode.Id(CodeSymbols.Internal))); + + if (condition) + { + context.AddError(node, ErrorID.InvalidModifierCombination); + } + } + + private bool IsModifiableNode(LNode arg) + { + return arg.Calls(CodeSymbols.Class) || arg.Calls(CodeSymbols.Struct) || arg.Calls(CodeSymbols.Fn) || arg.Calls(Symbols.Bitfield); + } +} \ No newline at end of file diff --git a/Source/Backlang.Contracts/Semantic/VariableTypeCheck.cs b/Source/Backlang.Contracts/Semantic/VariableTypeCheck.cs new file mode 100644 index 00000000..2d9ac775 --- /dev/null +++ b/Source/Backlang.Contracts/Semantic/VariableTypeCheck.cs @@ -0,0 +1,23 @@ +using Backlang.Codeanalysis.Core; +using Backlang.Driver; + +namespace Backlang.Contracts.Semantic; + +internal class VariableTypeCheck : ISemanticCheck +{ + public void Check(CompilationUnit tree, CompilerContext context) + { + var letNodes = tree.Body.SelectMany(_ => _.Descendants()).Where(_ => _.Calls(CodeSymbols.Var)).ToArray(); + + foreach (var node in letNodes) + { + if (node is (_, (_, (_, var type)), (_, _, var value))) + { + if (type.Name.Name == "" && value.Calls(CodeSymbols.Void)) + { + context.AddError(node, ErrorID.CannotDeduceType); + } + } + } + } +} \ No newline at end of file diff --git a/Source/Backlang.Contracts/SemanticChecker.cs b/Source/Backlang.Contracts/SemanticChecker.cs index 7368263c..e78cce4b 100644 --- a/Source/Backlang.Contracts/SemanticChecker.cs +++ b/Source/Backlang.Contracts/SemanticChecker.cs @@ -9,6 +9,8 @@ public static class SemanticChecker new ModuleDefinitionCheck(), new ImportCheck(), new TypenameCheck(), + new VariableTypeCheck(), + new ModifierCheck(), new InterfaceNameCheck() }; diff --git a/Source/Backlang.Core/Macros/BuiltInMacros.cs b/Source/Backlang.Core/Macros/BuiltInMacros.cs index 213c2876..8245199f 100644 --- a/Source/Backlang.Core/Macros/BuiltInMacros.cs +++ b/Source/Backlang.Core/Macros/BuiltInMacros.cs @@ -26,6 +26,18 @@ public static LNode Todo(LNode node, IMacroContext context) return LNode.Missing; } + [LexicalMacro("notimplemented", "Semantic info for functionality that is not implemented", "notimplemented")] + public static LNode NotImplemented(LNode node, IMacroContext context) + { + return LNode.Missing; + } + + [LexicalMacro("refactor", "Semantic info for functionality that needs to be refactored", "refactor")] + public static LNode Refactor(LNode node, IMacroContext context) + { + return LNode.Missing; + } + [LexicalMacro(@"nameof(id_or_expr)", @"Converts the 'key' name component of an expression to a string (e.g. nameof(A.B(D)) == ""B"")", "nameof", Mode = MacroMode.MatchIdentifierOrCall)] public static LNode @Nameof(LNode nameof, IMacroContext context) diff --git a/Source/Backlang.Core/Macros/TestCustomMacros.cs b/Source/Backlang.Core/Macros/TestCustomMacros.cs deleted file mode 100644 index 8826a959..00000000 --- a/Source/Backlang.Core/Macros/TestCustomMacros.cs +++ /dev/null @@ -1,20 +0,0 @@ -using LeMP; -using Loyc.Syntax; - -namespace Backlang.Core.Macros; - -[ContainsMacros] -public static class TestCustomMacros -{ - [LexicalMacro("#matchCode(expr) {}", "match code", "matchCode", Mode = MacroMode.MatchIdentifierOrCall)] - public static LNode matchCode(LNode node, IMacroContext context) - { - return node.With(LNode.Id("macro executed"), LNode.List(node.Args[0])); - } - - [LexicalMacro("#someMacro {}", "some Macro", "someMacro", Mode = MacroMode.MatchIdentifierOrCall)] - public static LNode someMacro(LNode node, IMacroContext context) - { - return LNode.Id("macro executed"); - } -} \ No newline at end of file diff --git a/Source/Backlang.Core/Result.cs b/Source/Backlang.Core/Result.cs index e4714c10..3a1f07ec 100644 --- a/Source/Backlang.Core/Result.cs +++ b/Source/Backlang.Core/Result.cs @@ -1,4 +1,6 @@ -namespace Backlang.Core +using System.Runtime.CompilerServices; + +namespace Backlang.Core { public class Result { @@ -9,14 +11,16 @@ public Result(T value) _value = value; } - public static implicit operator bool(Result value) + public static implicit operator T(Result value) { - return value._value != null; + return value._value; } - public static implicit operator T(Result value) + //Unpacking operator + [SpecialName] + public static bool op_Unpacking(Result value) { - return value._value; + return value._value != null; } } } \ No newline at end of file diff --git a/Source/Backlang.Core/Sealed.cs b/Source/Backlang.Core/Sealed.cs new file mode 100644 index 00000000..8b88cdc3 --- /dev/null +++ b/Source/Backlang.Core/Sealed.cs @@ -0,0 +1,46 @@ +using System.Runtime.CompilerServices; + +namespace Backlang.Core; + +public struct Sealed +{ + private T _value; + + public bool IsFreezed { get; private set; } + + public static implicit operator Sealed(T value) + { + return new Sealed { _value = value, IsFreezed = true }; + } + + public static implicit operator T(Sealed @sealed) + { + return @sealed._value; + } + + public void Set(T value) + { + if (IsFreezed) + { + throw new InvalidOperationException("Object is freezed"); + } + _value = value; + } + + public void Freeze() + { + IsFreezed = true; + } + + public void Unfreeze() + { + IsFreezed = false; + } + + //Unpacking operator + [SpecialName] + public static T op_Unpacking(Sealed value) + { + return value._value; + } +} diff --git a/Source/Backlang.Driver/Backlang.Driver.csproj b/Source/Backlang.Driver/Backlang.Driver.csproj index 97ffb438..d96664f2 100644 --- a/Source/Backlang.Driver/Backlang.Driver.csproj +++ b/Source/Backlang.Driver/Backlang.Driver.csproj @@ -27,14 +27,15 @@ - - + + - + + - + diff --git a/Source/Backlang.Driver/BinderExtensions.cs b/Source/Backlang.Driver/BinderExtensions.cs new file mode 100644 index 00000000..2ff9545b --- /dev/null +++ b/Source/Backlang.Driver/BinderExtensions.cs @@ -0,0 +1,80 @@ +using System.Collections.Concurrent; + +namespace Backlang.Driver; + +public static class BinderExtensions +{ + private static readonly ConcurrentDictionary _functionCache = new(); + + /// + /// Finds a method based on a selector + /// + /// + /// System.StringBuilder::AppendLine(System.String) + /// + public static IMethod FindFunction(this TypeResolver binder, string selector) + { + if (_functionCache.ContainsKey(selector)) + { + return _functionCache[selector]; + } + + var convertedSelector = GetSelector(selector); + + var type = binder.ResolveTypes(convertedSelector.Typename)?.FirstOrDefault(); + + if(type == null) { + throw new Exception($"Type '{type.FullName}' not found"); + } + + var methods = type.Methods + .Where(_ => _.Name.ToString() == convertedSelector.FunctionName) + .Where(_ => _.Parameters.Count == convertedSelector.ParameterTypes.Length); + + foreach (var method in methods) + { + for (int i = 0; i < method.Parameters.Count; i++) + { + if (method.Parameters[i].Type.FullName.ToString() == convertedSelector.ParameterTypes[i]) + { + _functionCache.AddOrUpdate(selector, _ => method, (_, __) => __); + return method; + } + } + } + + var function = methods.FirstOrDefault(); + if (function != null) + { + _functionCache.AddOrUpdate(selector, _ => methods.FirstOrDefault(), (_, __) => __); + } + + return methods.FirstOrDefault(); + } + + private static FunctionSelector GetSelector(string selector) + { + var ms = new FunctionSelector(); + + var spl = selector.Split("::"); + + ms.Typename = ConversionUtils.QualifyNamespace(spl[0]); + + var methodPart = spl[1]; + ms.FunctionName = methodPart.Substring(0, methodPart.IndexOf("(")); + + var parameterPart = methodPart[ms.FunctionName.Length..].Trim('(', ')'); + ms.ParameterTypes = parameterPart + .Split(",", StringSplitOptions.RemoveEmptyEntries) + .ToArray(); + + return ms; + } + + private class FunctionSelector + { + public QualifiedName Typename { get; set; } + public string FunctionName { get; set; } + public string[] ParameterTypes { get; set; } + } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/CompilerDriver.cs b/Source/Backlang.Driver/CompilerDriver.cs index 524329ee..90526acc 100644 --- a/Source/Backlang.Driver/CompilerDriver.cs +++ b/Source/Backlang.Driver/CompilerDriver.cs @@ -11,10 +11,14 @@ public static async void Compile(CompilerContext context) var pipeline = Flo.Pipeline.Build( cfg => { + cfg.When(_ => _.Options.WaitForDebugger, _ => { + _.Add(); + }); + cfg.Add(); cfg.Add(); - cfg.When(_ => !hasError(_.Messages) && _.OutputTree, _ => { + cfg.When(_ => !hasError(_.Messages) && _.Options.OutputTree, _ => { _.Add(); }); diff --git a/Source/Backlang.Driver/Compiling/IRGenerator.cs b/Source/Backlang.Driver/Compiling/IRGenerator.cs index 9bbd13d2..84a8c530 100644 --- a/Source/Backlang.Driver/Compiling/IRGenerator.cs +++ b/Source/Backlang.Driver/Compiling/IRGenerator.cs @@ -3,11 +3,83 @@ using Furesoft.Core.CodeDom.Compiler.Flow; using Furesoft.Core.CodeDom.Compiler.Instructions; using Furesoft.Core.CodeDom.Compiler.TypeSystem; +using System.Numerics; namespace Backlang.Driver.Compiling; public static class IRGenerator { + private static readonly int[] _primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41}; + + public static void GenerateGetHashCode(CompilerContext context, DescribedType type) + { + var gethashcodeMethod = new DescribedBodyMethod(type, new SimpleName("GetHashCode"), + false, context.Environment.Int32); + gethashcodeMethod.IsPublic = true; + gethashcodeMethod.IsOverride = true; + + var graph = Utils.CreateGraphBuilder(); + + var block = graph.EntryPoint; + + block.AppendParameter(new BlockParameter(context.Environment.Int32, "hash")); + + var startPrime = SelectPrime(); + var constant = block.AppendInstruction( + Instruction.CreateLoad(context.Environment.Int32, block.AppendInstruction( + Instruction.CreateConstant(new IntegerConstant(startPrime), context.Environment.Int32)) + ) + ); + + block.AppendInstruction(Instruction.CreateAlloca(context.Environment.Int32)); + + foreach (var field in type.Fields) + { + var methods = field.FieldType.Methods.Where(_ => _.Name.ToString() == "GetHashCode"); + if (methods.Any()) + { + var method = methods.First(); + + // hash = hash * 23 + field.GetHashCode(); + var loadHash = Instruction.CreateLoadLocal(new Parameter(context.Environment.Int32, "hash")); + var hash = block.AppendInstruction(loadHash); + var c = block.AppendInstruction( + Instruction.CreateLoad(context.Environment.Int32, block.AppendInstruction( + Instruction.CreateConstant(new IntegerConstant(SelectPrime()), context.Environment.Int32)) + ) + ); + + var multiplication = block.AppendInstruction(Instruction.CreateBinaryArithmeticIntrinsic("*", false, context.Environment.Int32, hash, c)); + + block.AppendInstruction(Instruction.CreateLoadArg(new Parameter(type))); + var instruction = Instruction.CreateLoadField(field); + + if (field.FieldType.BaseTypes.Count() > 0 && field.FieldType.BaseTypes[0].FullName.ToString() == "System.ValueType") + { + instruction = Instruction.CreateGetFieldPointer(field, null); + } + + var ldField = block.AppendInstruction(instruction); + var call = block.AppendInstruction(Instruction.CreateCall(method, MethodLookup.Virtual, new List() { ldField })); + + var addition = block.AppendInstruction(Instruction.CreateBinaryArithmeticIntrinsic("+", false, context.Environment.Int32, ldField, multiplication)); + + block.AppendInstruction(Instruction.CreateStore(context.Environment.Int32, hash, addition)); + } + } + + block.AppendInstruction(Instruction.CreateLoadLocal(new Parameter(context.Environment.Int32, "hash"))); + block.Flow = new ReturnFlow(); + + gethashcodeMethod.Body = new MethodBody(new Parameter(), new Parameter(type), EmptyArray.Value, graph.ToImmutable()); + type.AddMethod(gethashcodeMethod); + } + + private static int SelectPrime() + { + return _primes[Random.Shared.Next(0, _primes.Length)]; + } + public static void GenerateToString(CompilerContext context, DescribedType type) { var toStringMethod = new DescribedBodyMethod(type, new SimpleName("ToString"), false, Utils.ResolveType(context.Binder, typeof(string))); @@ -23,7 +95,8 @@ public static void GenerateToString(CompilerContext context, DescribedType type) var p = block.AppendParameter(new BlockParameter(sbType, varname)); var ctor = sbType.Methods.First(_ => _.IsConstructor && _.Parameters.Count == 0); - var appendLineMethod = sbType.Methods.First(_ => _.Name.ToString() == "AppendLine" && _.Parameters.Count == 1 && _.Parameters[0].Type.Name.ToString() == "String"); + + var appendLineMethod = context.Binder.FindFunction("System.Text.StringBuilder::AppendLine(System.String)"); block.AppendInstruction(Instruction.CreateNewObject(ctor, new List())); block.AppendInstruction(Instruction.CreateAlloca(sbType)); @@ -34,23 +107,21 @@ public static void GenerateToString(CompilerContext context, DescribedType type) foreach (var field in type.Fields) { + //AppendThis(block, p.Type); + var loadSbf = block.AppendInstruction(Instruction.CreateLoadLocal(new Parameter(p.Type, p.Tag.Name))); AppendLine(context, block, appendLineMethod, loadSbf, field.Name + " = "); var value = AppendLoadField(block, field); - var callee = sbType.Methods.FirstOrDefault(_ => _.Name.ToString() == "Append" && _.Parameters.Count == 1 && _.Parameters[0].Type.Name.ToString() == field.FieldType.Name.ToString()); + var appendMethod = context.Binder.FindFunction($"System.Text.StringBuilder::Append({field.FieldType.FullName})"); - if (callee == null) - { - callee = sbType.Methods.First(_ => _.Parameters.Count == 1 && _.Parameters[0].Type.FullName.ToString() == "System.Object"); - } + appendMethod ??= context.Binder.FindFunction("System.Text.StringBuilder::Append(System.Object)"); - block.AppendInstruction(Instruction.CreateCall(callee, MethodLookup.Virtual, new List { loadSbf, value })); + block.AppendInstruction(Instruction.CreateCall(appendMethod, MethodLookup.Virtual, new List { loadSbf, value })); } - var tsM = sbType.Methods.First(_ => _.Name.ToString() == "ToString"); - + var tsM = context.Binder.FindFunction($"System.Text.StringBuilder::ToString()"); block.AppendInstruction(Instruction.CreateCall(tsM, MethodLookup.Virtual, new List { loadSb })); block.Flow = new ReturnFlow(); @@ -60,6 +131,26 @@ public static void GenerateToString(CompilerContext context, DescribedType type) type.AddMethod(toStringMethod); } + public static void GenerateEmptyCtor(CompilerContext context, DescribedType type) + { + var ctorMethod = new DescribedBodyMethod(type, new SimpleName(".ctor"), false, Utils.ResolveType(context.Binder, typeof(void))) + { + IsConstructor = true + }; + + ctorMethod.IsPublic = true; + + var graph = Utils.CreateGraphBuilder(); + + var block = graph.EntryPoint; + + block.Flow = new ReturnFlow(); + + ctorMethod.Body = new MethodBody(new Parameter(), Parameter.CreateThisParameter(type), EmptyArray.Value, graph.ToImmutable()); + + type.AddMethod(ctorMethod); + } + public static void GenerateDefaultCtor(CompilerContext context, DescribedType type) { var ctorMethod = new DescribedBodyMethod(type, new SimpleName(".ctor"), false, Utils.ResolveType(context.Binder, typeof(void))) diff --git a/Source/Backlang.Driver/Compiling/Stages/CompilationStages/CompileTargetStage.cs b/Source/Backlang.Driver/Compiling/Stages/CompilationStages/CompileTargetStage.cs index bf9c789b..245b0424 100644 --- a/Source/Backlang.Driver/Compiling/Stages/CompilationStages/CompileTargetStage.cs +++ b/Source/Backlang.Driver/Compiling/Stages/CompilationStages/CompileTargetStage.cs @@ -1,4 +1,5 @@ -using Backlang.Core.CompilerService; +using Backlang.Codeanalysis.Core; +using Backlang.Core.CompilerService; using Backlang.Driver.Compiling.Targets.Dotnet; using Flo; using Furesoft.Core.CodeDom.Compiler.Pipeline; @@ -14,21 +15,27 @@ public async Task HandleAsync(CompilerContext context, Func _expressions = new List() @@ -28,9 +35,11 @@ public partial class ImplementationStage new ArrayExpressionImplementor(), new DefaultExpressionImplementor(), new TypeOfExpressionImplementor(), + new AsExpressionImplementor(), new AddressExpressionImplementor(), new CtorExpressionImplementor(), new UnaryExpressionImplementor(), + new MemberExpressionImplementor(), new BinaryExpressionImplementor(), new IdentifierExpressionImplementor(), new PointerExpressionImplementor(), @@ -45,8 +54,16 @@ public static MethodBody CompileBody(LNode function, CompilerContext context, IM { var graph = Utils.CreateGraphBuilder(); var block = graph.EntryPoint; + var branchLabels = new BranchLabels(); - AppendBlock(function.Args[3], block, context, method, modulename, scope); + var afterBlock = AppendBlock(function.Args[3], block, context, method, modulename, scope, branchLabels); + + SetReturnType((DescribedBodyMethod)method, function, context, scope, modulename.Value); + + if (afterBlock.Flow is NothingFlow && method.ReturnParameter.Type.FullName.ToString() != "System.Void") + { + afterBlock.Flow = new ReturnFlow(); + } return new MethodBody( method.ReturnParameter, @@ -55,8 +72,10 @@ public static MethodBody CompileBody(LNode function, CompilerContext context, IM graph.ToImmutable()); } - public static BasicBlockBuilder AppendBlock(LNode blkNode, BasicBlockBuilder block, CompilerContext context, IMethod method, QualifiedName? modulename, Scope scope) + public static BasicBlockBuilder AppendBlock(LNode blkNode, BasicBlockBuilder block, CompilerContext context, IMethod method, QualifiedName? modulename, Scope scope, BranchLabels branchLabels) { + block.Flow = new NothingFlow(); + foreach (var node in blkNode.Args) { if (!node.IsCall) continue; @@ -65,67 +84,22 @@ public static BasicBlockBuilder AppendBlock(LNode blkNode, BasicBlockBuilder blo { if (node.ArgCount == 0) continue; - block = AppendBlock(node, block.Graph.AddBasicBlock(), context, method, modulename, scope.CreateChildScope()); + block = AppendBlock(node, block.Graph.AddBasicBlock(), context, method, modulename, scope.CreateChildScope(), branchLabels); continue; } if (_implementations.ContainsKey(node.Name)) { - block = _implementations[node.Name].Implement(context, method, block, node, modulename, scope); - - if (block == null) - return block; - } - else if (node.Calls("print")) - { - AppendCall(context, block, node, context.writeMethods, scope, modulename.Value, methodName: "Write"); - } - else if (node.Calls("println")) - { - AppendCall(context, block, node, context.writeMethods, scope, modulename.Value, methodName: "WriteLine"); + block = _implementations[node.Name].Implement(node, block, context, method, modulename, scope, branchLabels); } else { - //ToDo: continue implementing static function call in same type - var type = (DescribedType)method.ParentType; - var calleeName = node.Target; - var methods = type.Methods; - - if (!methods.Any(_ => _.Name.ToString() == calleeName.ToString())) - { - type = (DescribedType)context.Binder.ResolveTypes(new SimpleName(Names.FreeFunctions).Qualify(modulename.Value)).FirstOrDefault(); - - if (type == null) - { - context.AddError(node, $"Cannot find function '{calleeName}'"); - } - } - - if (scope.TryGet(calleeName.Name.Name, out var callee)) - { - if (type.IsStatic && !callee.IsStatic) - { - context.AddError(node, $"A non static function '{calleeName.Name.Name}' cannot be called in a static function."); - return block; - } - - //ToDo: add overload AppendCall with known callee - AppendCall(context, block, node, type.Methods, scope, modulename.Value); - } - else - { - var suggestion = LevensteinDistance.Suggest(calleeName.Name.Name, type.Methods.Select(_ => _.Name.ToString())); - - context.AddError(node, $"Cannot find function '{calleeName.Name.Name}'. Did you mean '{suggestion}'?"); - } + EmitFunctionCall(method, node, block, context, scope, modulename); } } //automatic dtor call - foreach (var v in block.Parameters) - { - AppendDtor(context, block, scope, modulename, v.Tag.Name); - } + AppendAllDtors(block, context, modulename, scope); return block; } @@ -165,6 +139,11 @@ public static NamedInstructionBuilder AppendDtor(CompilerContext context, BasicB { if (scope.TryGet(varname, out var scopeItem)) { + if (!scopeItem.Type.Methods.Any(_ => _.Name.ToString() == "Finalize")) + { + return null; + } + block.AppendInstruction(Instruction.CreateLoadLocal(scopeItem.Parameter)); return AppendCall(context, block, LNode.Missing, scopeItem.Type.Methods, scope, modulename, false, "Finalize"); @@ -230,7 +209,7 @@ public static List AppendCallArguments(CompilerContext context, BasicB { var suggestion = LevensteinDistance.Suggest(arg.Name.Name, scope.GetAllScopeNames()); - context.AddError(arg, $"{arg.Name.Name} cannot be found. Did you mean '{suggestion}'?"); + context.AddError(arg, new(ErrorID.CannotBeFoundDidYouMean, arg.Name.Name, suggestion)); } } } @@ -238,6 +217,69 @@ public static List AppendCallArguments(CompilerContext context, BasicB return callTags; } + public static void AppendAllDtors(BasicBlockBuilder block, CompilerContext context, QualifiedName? modulename, Scope scope) + { + foreach (var v in block.Parameters) + { + AppendDtor(context, block, scope, modulename, v.Tag.Name); + } + } + + private static void SetReturnType(DescribedBodyMethod method, LNode function, CompilerContext context, Scope scope, QualifiedName modulename) + { + var retType = function.Args[0]; + + if (retType.Name != LNode.Missing.Name) + { + var rtype = TypeInheritanceStage.ResolveTypeWithModule(retType, context, modulename); + + method.ReturnParameter = new Parameter(rtype); + } + else + { + var deducedReturnType = TypeDeducer.DeduceFunctionReturnType(function, context, scope, modulename); + + method.ReturnParameter = deducedReturnType != null ? new Parameter(deducedReturnType) : new Parameter(Utils.ResolveType(context.Binder, typeof(void))); + } + } + + private static BasicBlockBuilder EmitFunctionCall(IMethod method, LNode node, BasicBlockBuilder block, CompilerContext context, Scope scope, QualifiedName? moduleName) + { + //ToDo: continue implementing static function call in same type + var type = (DescribedType)method.ParentType; + var calleeName = node.Target; + var methods = type.Methods; + + if (!methods.Any(_ => _.Name.ToString() == calleeName.ToString())) + { + type = (DescribedType)context.Binder.ResolveTypes(new SimpleName(Names.FreeFunctions).Qualify(moduleName.Value)).FirstOrDefault(); + + if (type == null) + { + context.AddError(node, new(ErrorID.CannotFindFunction, calleeName.ToString())); + } + } + + if (scope.TryGet(calleeName.Name.Name, out var callee)) + { + if (type.IsStatic && !callee.IsStatic) + { + context.AddError(node, $"A non static function '{calleeName.Name.Name}' cannot be called in a static function."); + return block; + } + + AppendCall(context, block, node, type.Methods, scope, moduleName.Value); + } + else + { + var suggestion = LevensteinDistance.Suggest(calleeName.Name.Name, type.Methods.Select(_ => _.Name.ToString())); + + context.AddError(node, new(ErrorID.CannotBeFoundDidYouMean, calleeName.Name.Name, suggestion)); + } + + return block; + } + private static void ConvertMethodBodies(CompilerContext context) { foreach (var bodyCompilation in context.BodyCompilations) diff --git a/Source/Backlang.Driver/Compiling/Stages/CompilationStages/ImplementationStage.Helpers.cs b/Source/Backlang.Driver/Compiling/Stages/CompilationStages/ImplementationStage.Helpers.cs index dcf36b13..32bcd961 100644 --- a/Source/Backlang.Driver/Compiling/Stages/CompilationStages/ImplementationStage.Helpers.cs +++ b/Source/Backlang.Driver/Compiling/Stages/CompilationStages/ImplementationStage.Helpers.cs @@ -7,6 +7,7 @@ public sealed partial class ImplementationStage : IHandler LiteralTypeMap = new Dictionary { + [CodeSymbols.Object] = typeof(object), [CodeSymbols.Bool] = typeof(bool), [CodeSymbols.String] = typeof(string), @@ -32,6 +33,7 @@ public enum ConditionalJumpKind NotEquals, Equals, True, + False, } public static IType GetLiteralType(LNode value, CompilerContext context, Scope scope, QualifiedName? modulename) @@ -146,19 +148,24 @@ public static bool MatchesParameters(IMethod method, List argTypes) public static IMethod GetMatchingMethod(CompilerContext context, List argTypes, IEnumerable methods, string methodname, bool shouldAppendError = true) { + var candiates = new List(); foreach (var m in methods.Where(_ => _.Name.ToString() == methodname)) { if (m.Parameters.Count == argTypes.Count) { if (MatchesParameters(m, argTypes)) - return m; + candiates.Add(m); } } - if (shouldAppendError) + if (shouldAppendError && candiates.Count == 0) { context.Messages.Add(Message.Error($"Cannot find matching function '{methodname}({string.Join(", ", argTypes.Select(_ => _.FullName.ToString()))})'")); + return null; } - return null; + + //ToDo: refactor getting best candidate + var orderedCandidates = candiates.OrderByDescending(_ => _.Parameters.Select(__ => _.FullName.ToString()).Contains("System.Object")); + return orderedCandidates.FirstOrDefault(); } } \ No newline at end of file diff --git a/Source/Backlang.Driver/Compiling/Stages/CompilationStages/ImplementationStage.cs b/Source/Backlang.Driver/Compiling/Stages/CompilationStages/ImplementationStage.cs index 4214fd3b..077b8391 100644 --- a/Source/Backlang.Driver/Compiling/Stages/CompilationStages/ImplementationStage.cs +++ b/Source/Backlang.Driver/Compiling/Stages/CompilationStages/ImplementationStage.cs @@ -1,3 +1,4 @@ +using Backlang.Codeanalysis.Core; using Backlang.Contracts.Scoping.Items; using Flo; using Furesoft.Core.CodeDom.Compiler.TypeSystem; @@ -43,6 +44,13 @@ private static void ImplementDefaultsForStructs(CompilerContext context, LNode s { IRGenerator.GenerateDefaultCtor(context, type); } + + IRGenerator.GenerateEmptyCtor(context, type); + + if (!type.Methods.Any(_ => _.Name.ToString() == "GetHashCode" && _.Parameters.Count == 0)) + { + IRGenerator.GenerateGetHashCode(context, type); + } } private static void CollectImplementations(CompilerContext context, LNode st, QualifiedName modulename) @@ -65,13 +73,13 @@ private static void CollectImplementations(CompilerContext context, LNode st, Qu if (targetType == null) { - context.AddError(typenode, $"Cannot implement '{fullname.FullName}', type not found"); + context.AddError(typenode, new(ErrorID.CannotImplementTypeNotFound, fullname.FullName)); return; } if (Utils.IsUnitType(context, targetType)) { - context.AddError(typenode, $"Cannot implement unit type '{fullname.FullName}'"); + context.AddError(typenode, new(ErrorID.CannotImplementUnitType, fullname.FullName)); } typeScope = context.GlobalScope.CreateChildScope(); @@ -99,6 +107,9 @@ private static void CollectImplementations(CompilerContext context, LNode st, Qu IsStatic = true, IsPublic = true }; + + Utils.AddCompilerGeneratedAttribute(context.Binder, extensionType); + context.Assembly.AddType(extensionType); } diff --git a/Source/Backlang.Driver/Compiling/Stages/CompilationStages/IntermediateStage.cs b/Source/Backlang.Driver/Compiling/Stages/CompilationStages/IntermediateStage.cs index 2ba7c661..c20aedbd 100644 --- a/Source/Backlang.Driver/Compiling/Stages/CompilationStages/IntermediateStage.cs +++ b/Source/Backlang.Driver/Compiling/Stages/CompilationStages/IntermediateStage.cs @@ -9,7 +9,7 @@ public sealed class IntermediateStage : IHandler HandleAsync(CompilerContext context, Func> next) { - context.Assembly = new DescribedAssembly(new QualifiedName(context.OutputFilename.Replace(".dll", ""))); + context.Assembly = new DescribedAssembly(new QualifiedName(context.Options.OutputFilename.Replace(".dll", ""))); foreach (var tree in context.Trees) { @@ -22,11 +22,15 @@ public async Task HandleAsync(CompilerContext context, Func GetNamespaceImports(CompilationUnit cu, CompilerContext context) { for (var i = 0; i < cu.Body.Count; i++) @@ -124,10 +137,12 @@ private IEnumerable GetNamespaceImports(CompilationUnit cu, CompilerConte private void ConvertUnitDeclaration(CompilerContext context, LNode st, QualifiedName modulename, Scope globalScope) { - var unitType = new DescribedType(new SimpleName(st[0].Name.ToString()).Qualify(modulename), context.Assembly); - - unitType.IsStatic = true; - unitType.IsPublic = true; + var unitType = new DescribedType( + new SimpleName(st[0].Name.ToString()).Qualify(modulename), context.Assembly) + { + IsStatic = true, + IsPublic = true + }; unitType.AddAttribute(new DescribedAttribute(Utils.ResolveType(context.Binder, typeof(Backlang.Core.CompilerService.UnitTypeAttribute)))); @@ -179,7 +194,9 @@ private void ConvertDiscriminatedUnion(CompilerContext context, LNode discrim, Q discType.AddField(fieldType); } + IRGenerator.GenerateGetHashCode(context, discType); IRGenerator.GenerateDefaultCtor(context, discType); + IRGenerator.GenerateEmptyCtor(context, discType); IRGenerator.GenerateToString(context, discType); } } diff --git a/Source/Backlang.Driver/Compiling/Stages/CompilationStages/TypeInheritanceStage.ConvertFunction.cs b/Source/Backlang.Driver/Compiling/Stages/CompilationStages/TypeInheritanceStage.ConvertFunction.cs index 3ae13f54..f15fcfdf 100644 --- a/Source/Backlang.Driver/Compiling/Stages/CompilationStages/TypeInheritanceStage.ConvertFunction.cs +++ b/Source/Backlang.Driver/Compiling/Stages/CompilationStages/TypeInheritanceStage.ConvertFunction.cs @@ -43,7 +43,6 @@ public static DescribedBodyMethod ConvertFunction(CompilerContext context, Descr var scope = parentScope.CreateChildScope(); AddParameters(method, function, context, modulename, scope); - SetReturnType(method, function, context, modulename); if (methodName == ".ctor") { @@ -100,9 +99,11 @@ private static void ConvertFreeFunction(CompilerContext context, LNode node, Qua if (!context.Assembly.Types.Any(_ => _.FullName.ToString() == new SimpleName(Names.FreeFunctions).Qualify(modulename).ToString())) { - type = new DescribedType(new SimpleName(Names.FreeFunctions).Qualify(modulename), context.Assembly); - type.IsStatic = true; - type.IsPublic = true; + type = new DescribedType(new SimpleName(Names.FreeFunctions).Qualify(modulename), context.Assembly) + { + IsStatic = true, + IsPublic = true + }; context.Assembly.AddType(type); var tr = new TypeResolver(); @@ -146,13 +147,4 @@ private static Parameter ConvertParameter(LNode p, CompilerContext context, Qual return param; } - - private static void SetReturnType(DescribedBodyMethod method, LNode function, CompilerContext context, QualifiedName modulename) - { - var retType = function.Args[0]; - - var rtype = ResolveTypeWithModule(retType, context, modulename); - - method.ReturnParameter = new Parameter(rtype); - } } \ No newline at end of file diff --git a/Source/Backlang.Driver/Compiling/Stages/CompilationStages/TypeInheritanceStage.Resolving.cs b/Source/Backlang.Driver/Compiling/Stages/CompilationStages/TypeInheritanceStage.Resolving.cs index bdd8b50c..da63dc2a 100644 --- a/Source/Backlang.Driver/Compiling/Stages/CompilationStages/TypeInheritanceStage.Resolving.cs +++ b/Source/Backlang.Driver/Compiling/Stages/CompilationStages/TypeInheritanceStage.Resolving.cs @@ -1,5 +1,6 @@ using Backlang.Contracts.TypeSystem; using Flo; +using Loyc.Geometry; namespace Backlang.Driver.Compiling.Stages; @@ -50,53 +51,35 @@ public static IType ResolveTypeWithModule(LNode typeNode, CompilerContext contex } IType resolvedType; - if (TypenameTable.ContainsKey(fullName.ToString())) + + if (context.GlobalScope.TypeAliases.ContainsKey(fullName.ToString())) + { + resolvedType = context.GlobalScope.TypeAliases[fullName.ToString()]; + } + else if (TypenameTable.ContainsKey(fullName.ToString())) { resolvedType = Utils.ResolveType(context.Binder, TypenameTable[fullName.FullName]); if (typeNode is (_, (_, _, (_, var unit))) && unit != LNode.Missing) { - if (unit is (_, (_, var u))) - { - var resolvedUnit = ResolveTypeWithModule(u, context, modulename); - - if (!Utils.IsUnitType(context, resolvedUnit)) - { - context.AddError(u, $"{resolvedUnit} is not a unit type"); - } - - resolvedType = new UnitType(resolvedType, resolvedUnit); - } + ResolveUnitType(context, modulename, ref resolvedType, unit); } } + else if (typeNode is ("#type?", var nullableArg)) + { + resolvedType = ResolveNullableType(nullableArg, context, modulename); + } else if (fullName is ("System", var func) && (func.StartsWith("Action") || func.StartsWith("Func"))) { - var fnType = Utils.ResolveType(context.Binder, func, "System"); - - var funcArgs = new List(); - foreach (var garg in typeNode.Args[2].Args) - { - funcArgs.Add(ResolveTypeWithModule(garg, context, modulename)); - } - - resolvedType = fnType.MakeGenericType(funcArgs); + resolvedType = ResolveFunctionType(typeNode, context, modulename, func); } else if (typeNode.Calls(CodeSymbols.Tuple)) { - var tupleType = Utils.ResolveType(context.Binder, $"Tuple`{typeNode.ArgCount}", "System"); - - var tupleArgs = new List(); - foreach (var garg in typeNode.Args) - { - tupleArgs.Add(ResolveTypeWithModule(garg, context, modulename)); - } - - resolvedType = tupleType.MakeGenericType(tupleArgs); + resolvedType = ResolveTupleType(typeNode, context, modulename); } else if (typeNode.Calls(CodeSymbols.Array)) { - resolvedType = ResolveTypeWithModule(typeNode[0], context, modulename); - resolvedType = context.Environment.MakeArrayType(resolvedType, (int)typeNode[1].Value); + resolvedType = ResolveArrayType(typeNode, context, modulename); } else { @@ -109,23 +92,15 @@ public static IType ResolveTypeWithModule(LNode typeNode, CompilerContext contex if (resolvedType == null) { - if (context.ImportetNamespaces.ContainsKey(typeNode.Range.Source.FileName)) + if (context.FileScope.ImportetNamespaces.TryGetValue(typeNode.Range.Source.FileName, out var importedNamespaces)) { - var namespaceImport = context.ImportetNamespaces[typeNode.Range.Source.FileName]; - - foreach (var importedNs in namespaceImport.ImportedNamespaces) - { - var tmpName = fullName.Qualify(importedNs); - - resolvedType = context.Binder.ResolveTypes(tmpName).FirstOrDefault(); - - if (resolvedType != null) break; - } + ResolveImportedType(typeNode, context, ref fullName, ref resolvedType); } if (resolvedType == null && !string.IsNullOrEmpty(fullName.ToString())) { context.AddError(typeNode, $"Type {fullName} cannot be found"); + return null; } } } @@ -137,4 +112,85 @@ public static IType ResolveTypeWithModule(LNode typeNode, CompilerContext contex return resolvedType; } + + private static IType ResolveNullableType(LNode nullableArg, CompilerContext context, QualifiedName modulename) + { + var tupleType = Utils.ResolveType(context.Binder, $"Nullable`1", "System"); + + var innerType = ResolveTypeWithModule(nullableArg, context, modulename); + + return tupleType.MakeGenericType(new List() { innerType }); + } + + private static void ResolveImportedType(LNode typeNode, CompilerContext context, ref QualifiedName fullName, ref IType resolvedType) + { + var namespaceImport = context.FileScope.ImportetNamespaces[typeNode.Range.Source.FileName]; + + foreach (var importedNs in namespaceImport.ImportedNamespaces) + { + var tmpName = fullName.Qualify(importedNs); + + resolvedType = context.Binder.ResolveTypes(tmpName).FirstOrDefault(); + + if (resolvedType != null) break; + } + } + + private static IType ResolveArrayType(LNode typeNode, CompilerContext context, QualifiedName modulename) + { + IType resolvedType; + var arrType = ResolveTypeWithModule(typeNode[0], context, modulename); + resolvedType = context.Environment.MakeArrayType(arrType, (int)typeNode[1].Value); + return resolvedType; + } + + private static IType ResolveTupleType(LNode typeNode, CompilerContext context, QualifiedName modulename) + { + IType resolvedType; + var tupleType = Utils.ResolveType(context.Binder, $"Tuple`{typeNode.ArgCount}", "System"); + + var tupleArgs = new List(); + foreach (var garg in typeNode.Args) + { + tupleArgs.Add(ResolveTypeWithModule(garg, context, modulename)); + } + + resolvedType = tupleType.MakeGenericType(tupleArgs); + return resolvedType; + } + + private static IType ResolveFunctionType(LNode typeNode, CompilerContext context, QualifiedName modulename, string func) + { + IType resolvedType; + var fnType = Utils.ResolveType(context.Binder, func, "System"); + + var funcArgs = new List(); + foreach (var garg in typeNode.Args[2].Args) + { + funcArgs.Add(ResolveTypeWithModule(garg, context, modulename)); + } + + if (func.StartsWith("Func")) + { + funcArgs.Add(ResolveTypeWithModule(typeNode.Args[0], context, modulename)); + } + + resolvedType = fnType.MakeGenericType(funcArgs); + return resolvedType; + } + + private static void ResolveUnitType(CompilerContext context, QualifiedName modulename, ref IType resolvedType, LNode unit) + { + if (unit is (_, (_, var u))) + { + var resolvedUnit = ResolveTypeWithModule(u, context, modulename); + + if (!Utils.IsUnitType(context, resolvedUnit)) + { + context.AddError(u, $"{resolvedUnit} is not a unit type"); + } + + resolvedType = new UnitType(resolvedType, resolvedUnit); + } + } } \ No newline at end of file diff --git a/Source/Backlang.Driver/Compiling/Stages/EmitTreeStage.cs b/Source/Backlang.Driver/Compiling/Stages/EmitTreeStage.cs index ef56151a..f331e26d 100644 --- a/Source/Backlang.Driver/Compiling/Stages/EmitTreeStage.cs +++ b/Source/Backlang.Driver/Compiling/Stages/EmitTreeStage.cs @@ -6,24 +6,21 @@ public sealed class EmitTreeStage : IHandler { public async Task HandleAsync(CompilerContext context, Func> next) { - if (!context.Messages.Any()) - { - var sb = new StringBuilder(); - var tree = context.Trees.FirstOrDefault(); - - if (tree == null) - { - return context; - } + var sb = new StringBuilder(); + var tree = context.Trees.FirstOrDefault(); - foreach (var node in tree.Body) - { - sb.AppendLine(node.ToString()); - } + if (tree == null) + { + return context; + } - File.WriteAllText(Path.Combine(context.TempOutputPath, context.OutputFilename + ".txt"), sb.ToString()); + foreach (var node in tree.Body) + { + sb.AppendLine(node.ToString()); } + File.WriteAllText(Path.Combine(context.TempOutputPath, context.Options.OutputFilename + ".txt"), sb.ToString()); + return await next.Invoke(context); } } \ No newline at end of file diff --git a/Source/Backlang.Driver/Compiling/Stages/ExpandingStages/ExpandMacrosStage.cs b/Source/Backlang.Driver/Compiling/Stages/ExpandingStages/ExpandMacrosStage.cs index 5149a1c2..489fbd0a 100644 --- a/Source/Backlang.Driver/Compiling/Stages/ExpandingStages/ExpandMacrosStage.cs +++ b/Source/Backlang.Driver/Compiling/Stages/ExpandingStages/ExpandMacrosStage.cs @@ -46,7 +46,7 @@ public async Task HandleAsync(CompilerContext context, Func HandleAsync(CompilerContext context, Func> next) { - foreach (var resource in context.EmbeddedResource) + foreach (var resource in context.Options.EmbeddedResource) { Stream strm = File.OpenRead(resource); diff --git a/Source/Backlang.Driver/Compiling/Stages/InitStages/InitStage.TypeSystem.cs b/Source/Backlang.Driver/Compiling/Stages/InitStages/InitStage.TypeSystem.cs index ea82a778..224596d3 100644 --- a/Source/Backlang.Driver/Compiling/Stages/InitStages/InitStage.TypeSystem.cs +++ b/Source/Backlang.Driver/Compiling/Stages/InitStages/InitStage.TypeSystem.cs @@ -1,4 +1,5 @@ -using Backlang.Contracts.Scoping.Items; +using Backlang.Codeanalysis.Core; +using Backlang.Contracts.Scoping.Items; using Backlang.Driver.Compiling.Targets.Dotnet; using Flo; using System.Collections.Concurrent; @@ -21,19 +22,19 @@ public void InitTypeSystem(CompilerContext context) InitPluginTargets(context.Plugins); - if (string.IsNullOrEmpty(context.Target)) + if (string.IsNullOrEmpty(context.Options.Target)) { - context.Target = "dotnet"; + context.Options.Target = "dotnet"; } - if (context.OutputType == "dotnet") + if (context.Options.OutputType == "dotnet") { - context.OutputType = "Exe"; + context.Options.OutputType = "Exe"; } - if (_targets.ContainsKey(context.Target)) + if (_targets.ContainsKey(context.Options.Target)) { - var compilationTarget = _targets[context.Target]; + var compilationTarget = _targets[context.Options.Target]; context.CompilationTarget = compilationTarget; context.Environment = compilationTarget.Init(context); @@ -49,21 +50,14 @@ public void InitTypeSystem(CompilerContext context) } else { - context.Messages.Add(Message.Error($"Target '{context.Target}' cannot be found")); + context.Messages.Add(Message.Error(new(ErrorID.TargetNotFound, context.Options.Target))); return; } - - var consoleType = context.Binder.ResolveTypes(new SimpleName("Console").Qualify("System")).FirstOrDefault(); - - context.writeMethods = consoleType?.Methods.Where( - method => (method.Name.ToString() == "Write" || method.Name.ToString() == "WriteLine") - && method.IsStatic - && method.ReturnParameter.Type == context.Environment.Void); } private static void AddIntrinsicType(CompilerContext context, Type type) { - var qualifier = Utils.QualifyNamespace(type.Namespace); + var qualifier = ConversionUtils.QualifyNamespace(type.Namespace); var intrinsicAssembly = new DescribedAssembly(qualifier); var instrinsicsType = new DescribedType( diff --git a/Source/Backlang.Driver/Compiling/Stages/ParsingStage.cs b/Source/Backlang.Driver/Compiling/Stages/ParsingStage.cs index 99afb022..2c8da970 100644 --- a/Source/Backlang.Driver/Compiling/Stages/ParsingStage.cs +++ b/Source/Backlang.Driver/Compiling/Stages/ParsingStage.cs @@ -6,14 +6,28 @@ public sealed class ParsingStage : IHandler { public async Task HandleAsync(CompilerContext context, Func> next) { - Parallel.ForEachAsync(context.InputFiles, (filename, ct) => { + if (context.Playground.IsPlayground) + { + var tree = CompilationUnit.FromText(context.Playground.Source); + + ApplyTree(context, tree); + + return await next.Invoke(context); + } + + ParseSourceFiles(context); + + return await next.Invoke(context); + } + + private static void ParseSourceFiles(CompilerContext context) + { + Parallel.ForEachAsync(context.Options.InputFiles, (filename, ct) => { if (File.Exists(filename)) { var tree = CompilationUnit.FromFile(filename); - context.Trees.Add(tree); - - context.Messages.AddRange(tree.Messages); + ApplyTree(context, tree); } else { @@ -22,7 +36,12 @@ public async Task HandleAsync(CompilerContext context, Func +{ + public async Task HandleAsync(CompilerContext context, Func> next) + { + while (!Debugger.IsAttached) + { + Thread.Sleep(1); + } + + return await next.Invoke(context); + } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Compiling/Targets/Dotnet/ClrTypeEnvironmentBuilder.cs b/Source/Backlang.Driver/Compiling/Targets/Dotnet/ClrTypeEnvironmentBuilder.cs index 43b2d0c0..67aab8fa 100644 --- a/Source/Backlang.Driver/Compiling/Targets/Dotnet/ClrTypeEnvironmentBuilder.cs +++ b/Source/Backlang.Driver/Compiling/Targets/Dotnet/ClrTypeEnvironmentBuilder.cs @@ -19,7 +19,7 @@ public static IAssembly CollectTypes(Assembly ass) { if (!type.IsPublic) continue; - var ns = Utils.QualifyNamespace(type.Namespace); + var ns = ConversionUtils.QualifyNamespace(type.Namespace); var dt = new DescribedType(new SimpleName(type.Name).Qualify(ns), assembly); dt.IsSealed = type.IsSealed; diff --git a/Source/Backlang.Driver/Compiling/Targets/Dotnet/DotNetAssembly.cs b/Source/Backlang.Driver/Compiling/Targets/Dotnet/DotNetAssembly.cs index 1a121a69..f141b494 100644 --- a/Source/Backlang.Driver/Compiling/Targets/Dotnet/DotNetAssembly.cs +++ b/Source/Backlang.Driver/Compiling/Targets/Dotnet/DotNetAssembly.cs @@ -1,9 +1,11 @@ using Backlang.Core.CompilerService; +using Backlang.Driver.Compiling.Targets.Dotnet.RuntimeOptionsModels; using Furesoft.Core.CodeDom.Compiler.Pipeline; using Furesoft.Core.CodeDom.Compiler.TypeSystem; using Mono.Cecil; using Mono.Cecil.Cil; using System.Collections.Concurrent; +using System.ComponentModel; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -33,7 +35,7 @@ public DotNetAssembly(AssemblyContentDescription description) _description = description; - SetTargetFramework(); + SetTargetFramework("net7.0"); //ToDo: get framework moniker from options var console = typeof(Console).Assembly.GetName(); var core = typeof(UnitTypeAttribute).Assembly.GetName(); @@ -50,6 +52,8 @@ public void WriteTo(Stream output) var clrType = new TypeDefinition(type.FullName.Slice(0, type.FullName.PathLength - 1).FullName.ToString(), type.Name.ToString(), TypeAttributes.Class); + MakeStructReadonly(type, clrType); + _assemblyDefinition.MainModule.Types.Add(clrType); typeMap.AddOrUpdate(type, (_) => clrType, (_, __) => clrType); @@ -160,6 +164,22 @@ private static void ApplyStructLayout(TypeDefinition clrType, DescribedAttribute } } + private void MakeStructReadonly(DescribedType type, TypeDefinition clrType) + { + if (type.BaseTypes.Count > 0 && type.BaseTypes[0].FullName.ToString() == "System.ValueType") + { + clrType.IsSealed = true; + + var readonlyCtor = typeof(ReadOnlyAttribute).GetConstructors()[0]; + + var ca = new CustomAttribute(_assemblyDefinition.MainModule.ImportReference(readonlyCtor)); + + ca.ConstructorArguments.Add(new(_assemblyDefinition.MainModule.ImportReference(typeof(bool)), true)); + + clrType.CustomAttributes.Add(ca); + } + } + private FieldDefinition GeneratePropertyField(DescribedProperty property) { var clrField = new FieldDefinition(@$"<{property.Name}>k__BackingField", FieldAttributes.Private, Resolve(property.PropertyType.FullName)); @@ -258,17 +278,17 @@ private void ConvertProperties(DescribedType type, TypeDefinition clrType) private void AdjustBaseTypesAndInterfaces() { - foreach (var baseType in _needToAdjust) + foreach (var (definition, name) in _needToAdjust) { - var type = Resolve(baseType.name).Resolve(); + var type = Resolve(name).Resolve(); if (type.IsInterface) { - baseType.definition.Interfaces.Add(new InterfaceImplementation(type)); + definition.Interfaces.Add(new InterfaceImplementation(type)); } else { - baseType.definition.BaseType = type; + definition.BaseType = type; } } } @@ -328,8 +348,7 @@ private void CompileBodys() { foreach (var bodyCompilation in _methodBodyCompilations) { - var variables = - MethodBodyCompiler.Compile(bodyCompilation.DescribedMethod, bodyCompilation.ClrMethod, _assemblyDefinition, bodyCompilation.ClrType); + var variables = MethodBodyCompiler.Compile(bodyCompilation.DescribedMethod, bodyCompilation.ClrMethod, _assemblyDefinition, bodyCompilation.ClrType); bodyCompilation.ClrMethod.DebugInformation.Scope = new ScopeDebugInformation(bodyCompilation.ClrMethod.Body.Instructions[0], @@ -539,9 +558,6 @@ private MethodDefinition GetMethodDefinition(DescribedBodyMethod m, IType return private TypeReference Resolve(IType dtype) { - //ToDo: Only for debugging, remove if typecheck is done - if (dtype == null) throw new Exception($"Type not found"); - var resolvedType = Resolve(dtype.FullName); if (resolvedType.HasGenericParameters) { @@ -572,13 +588,14 @@ private TypeReference Resolve(QualifiedName name) return _assemblyDefinition.ImportType(name); } - private void SetTargetFramework() + private void SetTargetFramework(string moniker) { var tf = _assemblyDefinition.MainModule.ImportReference(typeof(TargetFrameworkAttribute).GetConstructors().First()); var item = new CustomAttribute(tf); item.ConstructorArguments.Add( - new CustomAttributeArgument(_assemblyDefinition.MainModule.ImportReference(typeof(string)), ".NETCoreApp,Version=v7.0")); + new CustomAttributeArgument(_assemblyDefinition.MainModule.ImportReference(typeof(string)), + $".NETCoreApp,Version=v{RuntimeConfig.GetVersion(moniker)}")); _assemblyDefinition.CustomAttributes.Add(item); } diff --git a/Source/Backlang.Driver/Compiling/Targets/Dotnet/DotNetTarget.cs b/Source/Backlang.Driver/Compiling/Targets/Dotnet/DotNetTarget.cs index 29001567..bfb0ed98 100644 --- a/Source/Backlang.Driver/Compiling/Targets/Dotnet/DotNetTarget.cs +++ b/Source/Backlang.Driver/Compiling/Targets/Dotnet/DotNetTarget.cs @@ -1,4 +1,5 @@ using Backlang.Core; +using Backlang.Driver.Compiling.Targets.Dotnet.RuntimeOptionsModels; using Furesoft.Core.CodeDom.Compiler.Pipeline; using LeMP; using System.Collections; @@ -18,18 +19,13 @@ public class DotNetTarget : ICompilationTarget public void AfterCompiling(CompilerContext context) { - var runtimeConfigStream = typeof(DotNetTarget).Assembly.GetManifestResourceStream("Backlang.Driver.compilation.runtimeconfig.json"); - var jsonStream = File.OpenWrite($"{Path.Combine(context.TempOutputPath, Path.GetFileNameWithoutExtension(context.OutputFilename))}.runtimeconfig.json"); - - runtimeConfigStream.CopyTo(jsonStream); - - jsonStream.Close(); - runtimeConfigStream.Close(); + RuntimeConfig.Save(context.TempOutputPath, + Path.GetFileNameWithoutExtension(context.Options.OutputFilename), context.Options); } public void BeforeCompiling(CompilerContext context) { - context.OutputFilename += ".dll"; + context.Options.OutputFilename += ".dll"; } public void BeforeExpandMacros(MacroProcessor processor) @@ -69,7 +65,7 @@ public TypeEnvironment Init(CompilerContext context) public void InitReferences(CompilerContext context) { - foreach (var r in context.References) + foreach (var r in context.Options.References) { AddFromAssembly(context, r); } diff --git a/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/ArithmetikEmitter.cs b/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/ArithmetikEmitter.cs new file mode 100644 index 00000000..8b1650c0 --- /dev/null +++ b/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/ArithmetikEmitter.cs @@ -0,0 +1,58 @@ +using Furesoft.Core.CodeDom.Compiler.Instructions; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Backlang.Driver.Compiling.Targets.Dotnet.Emitters; + +internal class ArithmetikEmitter : IEmitter +{ + private readonly ImmutableDictionary _stringOPMap = new Dictionary() + { + ["arith.+"] = OpCodes.Add, + ["arith.-"] = OpCodes.Sub, + ["arith.*"] = OpCodes.Mul, + ["arith./"] = OpCodes.Div, + + ["arith.%"] = OpCodes.Div, + + ["arith.&"] = OpCodes.And, + ["arith.|"] = OpCodes.Or, + ["arith.^"] = OpCodes.Xor, + + ["arith.<"] = OpCodes.Clt, + ["arith.>"] = OpCodes.Cgt, + ["arith.=="] = OpCodes.Ceq, + }.ToImmutableDictionary(); + + public void Emit(AssemblyDefinition assemblyDefinition, ILProcessor ilProcessor, Furesoft.Core.CodeDom.Compiler.Instruction instruction, BasicBlock block) + { + var arithProtype = (IntrinsicPrototype)instruction.Prototype; + + if (_stringOPMap.ContainsKey(arithProtype.Name)) + { + var op = _stringOPMap[arithProtype.Name]; + ilProcessor.Emit(op); return; + } + + switch (arithProtype.Name) + { + case "arith.!=": + ilProcessor.Emit(OpCodes.Ceq); + ilProcessor.Emit(OpCodes.Ldc_I4, 0); + ilProcessor.Emit(OpCodes.Ceq); + break; + + case "arith.<=": + ilProcessor.Emit(OpCodes.Cgt); + ilProcessor.Emit(OpCodes.Ldc_I4, 0); + ilProcessor.Emit(OpCodes.Ceq); + break; + + case "arith.>=": + ilProcessor.Emit(OpCodes.Clt); + ilProcessor.Emit(OpCodes.Ldc_I4, 0); + ilProcessor.Emit(OpCodes.Ceq); + break; + } + } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/CallEmitter.cs b/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/CallEmitter.cs new file mode 100644 index 00000000..65fe668e --- /dev/null +++ b/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/CallEmitter.cs @@ -0,0 +1,50 @@ +using Furesoft.Core.CodeDom.Compiler.Instructions; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Backlang.Driver.Compiling.Targets.Dotnet.Emitters; + +internal class CallEmitter : IEmitter +{ + public void Emit(AssemblyDefinition assemblyDefinition, ILProcessor ilProcessor, + Furesoft.Core.CodeDom.Compiler.Instruction instruction, BasicBlock block) + { + var callPrototype = (CallPrototype)instruction.Prototype; + + if (IntrinsicHelper.IsIntrinsicType(typeof(Intrinsics), callPrototype)) + { + IntrinsicHelper.InvokeIntrinsic(typeof(Intrinsics), callPrototype.Callee, instruction, block); + return; + } + + var method = MethodBodyCompiler.GetMethod(assemblyDefinition, callPrototype.Callee); + + for (var i = 0; i < method.Parameters.Count; i++) + { + var valueType = block.Graph.NamedInstructions + .Where(_ => instruction.Arguments[i] == _.Tag) + .Select(_ => _.ResultType).FirstOrDefault(); + + var arg = method.Parameters[i]; + + //ToDo: move to IR + if (arg.ParameterType.FullName.ToString() == "System.Object") + { + ilProcessor.Emit(OpCodes.Box, assemblyDefinition.ImportType(valueType)); + } + } + + var op = OpCodes.Call; + + if (!callPrototype.Callee.IsStatic) + { + op = OpCodes.Callvirt; + } + + ilProcessor.Emit(op, + assemblyDefinition.MainModule.ImportReference( + method + ) + ); + } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/CopyEmitter.cs b/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/CopyEmitter.cs new file mode 100644 index 00000000..ec849e59 --- /dev/null +++ b/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/CopyEmitter.cs @@ -0,0 +1,12 @@ +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Backlang.Driver.Compiling.Targets.Dotnet.Emitters; + +public class CopyEmitter : IEmitter +{ + public void Emit(AssemblyDefinition assemblyDefinition, ILProcessor ilProcessor, Furesoft.Core.CodeDom.Compiler.Instruction instruction, BasicBlock block) + { + ilProcessor.Emit(OpCodes.Dup); + } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/DynamicCastEmitter.cs b/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/DynamicCastEmitter.cs new file mode 100644 index 00000000..3402e1ae --- /dev/null +++ b/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/DynamicCastEmitter.cs @@ -0,0 +1,16 @@ +using Furesoft.Core.CodeDom.Compiler.Instructions; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Backlang.Driver.Compiling.Targets.Dotnet.Emitters; + +internal class DynamicCastEmitter : IEmitter +{ + public void Emit(AssemblyDefinition assemblyDefinition, ILProcessor ilProcessor, Furesoft.Core.CodeDom.Compiler.Instruction instruction, BasicBlock block) + { + var dcp = (DynamicCastPrototype)instruction.Prototype; + var checkType = assemblyDefinition.ImportType(dcp.TargetType.ElementType); + + ilProcessor.Emit(OpCodes.Isinst, checkType); + } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/LoadEmitter.cs b/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/LoadEmitter.cs new file mode 100644 index 00000000..41de7fe5 --- /dev/null +++ b/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/LoadEmitter.cs @@ -0,0 +1,15 @@ +using Furesoft.Core.CodeDom.Compiler.Instructions; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Backlang.Driver.Compiling.Targets.Dotnet.Emitters; + +internal class LoadEmitter : IEmitter +{ + public void Emit(AssemblyDefinition assemblyDefinition, ILProcessor ilProcessor, Furesoft.Core.CodeDom.Compiler.Instruction instruction, BasicBlock block) + { + var valueInstruction = block.Graph.GetInstruction(instruction.Arguments[0]); + + MethodBodyCompiler.EmitConstant(ilProcessor, (ConstantPrototype)valueInstruction.Prototype); + } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/LoadIndirectEmitter.cs b/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/LoadIndirectEmitter.cs new file mode 100644 index 00000000..ef631132 --- /dev/null +++ b/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/LoadIndirectEmitter.cs @@ -0,0 +1,12 @@ +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Backlang.Driver.Compiling.Targets.Dotnet.Emitters; + +internal class LoadIndirectEmitter : IEmitter +{ + public void Emit(AssemblyDefinition assemblyDefinition, ILProcessor ilProcessor, Furesoft.Core.CodeDom.Compiler.Instruction instruction, BasicBlock block) + { + ilProcessor.Emit(OpCodes.Ldind_I4); + } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/NewArrayEmitter.cs b/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/NewArrayEmitter.cs new file mode 100644 index 00000000..7a0e0e59 --- /dev/null +++ b/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/NewArrayEmitter.cs @@ -0,0 +1,15 @@ +using Furesoft.Core.CodeDom.Compiler.Instructions; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Backlang.Driver.Compiling.Targets.Dotnet.Emitters; + +internal class NewArrayEmitter : IEmitter +{ + public void Emit(AssemblyDefinition assemblyDefinition, ILProcessor ilProcessor, Furesoft.Core.CodeDom.Compiler.Instruction instruction, BasicBlock block) + { + var prototype = (AllocaArrayPrototype)instruction.Prototype; + + ilProcessor.Emit(OpCodes.Newarr, assemblyDefinition.ImportType(prototype.ElementType)); + } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/NewObjectEmitter.cs b/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/NewObjectEmitter.cs new file mode 100644 index 00000000..52b47279 --- /dev/null +++ b/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/NewObjectEmitter.cs @@ -0,0 +1,16 @@ +using Furesoft.Core.CodeDom.Compiler.Instructions; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Backlang.Driver.Compiling.Targets.Dotnet.Emitters; + +internal class NewObjectEmitter : IEmitter +{ + public void Emit(AssemblyDefinition assemblyDefinition, ILProcessor ilProcessor, Furesoft.Core.CodeDom.Compiler.Instruction instruction, BasicBlock block) + { + var newObjectPrototype = (NewObjectPrototype)instruction.Prototype; + var method = MethodBodyCompiler.GetMethod(assemblyDefinition, newObjectPrototype.Constructor); + + ilProcessor.Emit(OpCodes.Newobj, method); + } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/TypeofEmitter.cs b/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/TypeofEmitter.cs new file mode 100644 index 00000000..c220abda --- /dev/null +++ b/Source/Backlang.Driver/Compiling/Targets/Dotnet/Emitters/TypeofEmitter.cs @@ -0,0 +1,16 @@ +using Backlang.Driver.Core.Instructions; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Backlang.Driver.Compiling.Targets.Dotnet.Emitters; + +internal class TypeofEmitter : IEmitter +{ + public void Emit(AssemblyDefinition assemblyDefinition, ILProcessor ilProcessor, + Furesoft.Core.CodeDom.Compiler.Instruction instruction, BasicBlock block) + { + var toip = (TypeOfInstructionPrototype)instruction.Prototype; + + ilProcessor.Emit(OpCodes.Ldtoken, assemblyDefinition.ImportType(toip.Type)); + } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Compiling/Targets/Dotnet/IEmitter.cs b/Source/Backlang.Driver/Compiling/Targets/Dotnet/IEmitter.cs new file mode 100644 index 00000000..3759f324 --- /dev/null +++ b/Source/Backlang.Driver/Compiling/Targets/Dotnet/IEmitter.cs @@ -0,0 +1,11 @@ +using Mono.Cecil; +using Mono.Cecil.Cil; +using Instruction = Furesoft.Core.CodeDom.Compiler.Instruction; + +namespace Backlang.Driver.Compiling.Targets.Dotnet; + +internal interface IEmitter +{ + void Emit(AssemblyDefinition assemblyDefinition, ILProcessor ilProcessor, + Instruction instruction, BasicBlock block); +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Compiling/Targets/Dotnet/MethodBodyCompiler.cs b/Source/Backlang.Driver/Compiling/Targets/Dotnet/MethodBodyCompiler.cs index 61aa11cd..19d82dc1 100644 --- a/Source/Backlang.Driver/Compiling/Targets/Dotnet/MethodBodyCompiler.cs +++ b/Source/Backlang.Driver/Compiling/Targets/Dotnet/MethodBodyCompiler.cs @@ -1,39 +1,34 @@ -using Backlang.Driver.Core.Instructions; +using Backlang.Driver.Compiling.Targets.Dotnet.Emitters; +using Backlang.Driver.Core.Instructions; using Furesoft.Core.CodeDom.Compiler.Core.Constants; using Furesoft.Core.CodeDom.Compiler.Flow; using Furesoft.Core.CodeDom.Compiler.Instructions; using Furesoft.Core.CodeDom.Compiler.TypeSystem; using Mono.Cecil; using Mono.Cecil.Cil; + using static Backlang.Driver.Compiling.Stages.CompilationStages.ImplementationStage; using Instruction = Mono.Cecil.Cil.Instruction; +using LoadFieldPrototype = Furesoft.Core.CodeDom.Compiler.Instructions.LoadFieldPrototype; +using MethodDefinition = Mono.Cecil.MethodDefinition; namespace Backlang.Driver.Compiling.Targets.Dotnet; -internal class NothingFlow : BlockFlow +public static class MethodBodyCompiler { - public override IReadOnlyList Instructions => throw new NotImplementedException(); - - public override IReadOnlyList Branches => throw new NotImplementedException(); - - public override InstructionBuilder GetInstructionBuilder(BasicBlockBuilder block, int instructionIndex) - { - throw new NotImplementedException(); - } - - public override BlockFlow WithBranches(IReadOnlyList branches) - { - throw new NotImplementedException(); - } - - public override BlockFlow WithInstructions(IReadOnlyList instructions) + private static readonly Dictionary _emitters = new() { - throw new NotImplementedException(); - } -} + [typeof(CallPrototype)] = new CallEmitter(), + [typeof(TypeOfInstructionPrototype)] = new TypeofEmitter(), + [typeof(DynamicCastPrototype)] = new DynamicCastEmitter(), + [typeof(LoadIndirectPrototype)] = new LoadIndirectEmitter(), + [typeof(NewObjectPrototype)] = new NewObjectEmitter(), + [typeof(IntrinsicPrototype)] = new ArithmetikEmitter(), + [typeof(AllocaArrayPrototype)] = new NewArrayEmitter(), + [typeof(LoadPrototype)] = new LoadEmitter(), + [typeof(CopyPrototype)] = new CopyEmitter(), + }; -public static class MethodBodyCompiler -{ public static Dictionary Compile(DescribedBodyMethod m, MethodDefinition clrMethod, AssemblyDefinition assemblyDefinition, TypeDefinition parentType) { var ilProcessor = clrMethod.Body.GetILProcessor(); @@ -58,6 +53,68 @@ public static Dictionary Compile(DescribedBodyMethod return variables; } + public static MethodReference GetMethod(AssemblyDefinition assemblyDefinition, IMethod method) + { + var parentType = assemblyDefinition.ImportType(method.ParentType).Resolve(); + + foreach (var m in parentType.Methods + .Where(_ => _.Name == method.Name.ToString())) + { + var parameters = m.Parameters; + + if (parameters.Count == method.Parameters.Count) + { + if (method.GenericParameters.Any()) + { + if (method.IsConstructor) + { + var dts = (DirectTypeSpecialization)GenericTypeMap.Cache[(method.FullName, method)]; + var args = dts.GetRecursiveGenericArguments().Select(_ => assemblyDefinition.ImportType(_)).ToArray(); + var genericType = assemblyDefinition.ImportType(dts); + var gctor = genericType.Resolve().Methods.FirstOrDefault(_ => _.Name == method.Name.ToString()); + + var mm = m.MakeHostInstanceGeneric(args); + + return assemblyDefinition.MainModule.ImportReference(mm); + } + + return m; + } + + if (MatchesParameters(parameters, method)) + return assemblyDefinition.MainModule.ImportReference(m); + } + } + + return null; + } + + public static void EmitConstant(ILProcessor ilProcessor, ConstantPrototype consProto) + { + dynamic v = consProto.Value; + + if (v is StringConstant str) + { + ilProcessor.Emit(OpCodes.Ldstr, str.Value); + } + else if (v is Float32Constant f32) + { + ilProcessor.Emit(OpCodes.Ldc_R4, f32.Value); + } + else if (v is Float64Constant f64) + { + ilProcessor.Emit(OpCodes.Ldc_R8, f64.Value); + } + else if (v is NullConstant) + { + ilProcessor.Emit(OpCodes.Ldnull); + } + else if (v is IntegerConstant ic) + { + EmitIntegerConstant(ilProcessor, v, ic); + } + } + private static void FixJumps(ILProcessor ilProcessor, Dictionary labels, List<(int InstructionIndex, BasicBlockTag Target)> fixups) { foreach (var fixup in fixups) @@ -81,69 +138,100 @@ private static void CompileBlock(BasicBlock block, AssemblyDefinition assemblyDe { var instruction = item.Instruction; - if (instruction.Prototype is CallPrototype) + var prototypeType = instruction.Prototype.GetType(); + if (_emitters.ContainsKey(prototypeType)) { - EmitCall(assemblyDefinition, ilProcessor, instruction, block.Graph, block); - } - else if (instruction.Prototype is TypeOfInstructionPrototype toip) - { - ilProcessor.Emit(OpCodes.Ldtoken, assemblyDefinition.ImportType(toip.Type)); + _emitters[prototypeType].Emit(assemblyDefinition, ilProcessor, instruction, block); } else if (instruction.Prototype is LoadLocalAPrototype lda) { var definition = variables[lda.Parameter.Name.ToString()]; ilProcessor.Emit(OpCodes.Ldloca_S, definition); } - else if (instruction.Prototype is LoadIndirectPrototype ldi) - { - ilProcessor.Emit(OpCodes.Ldind_I4); - } - else if (instruction.Prototype is NewObjectPrototype newObjectPrototype) - { - EmitNewObject(assemblyDefinition, ilProcessor, newObjectPrototype); - } - else if (instruction.Prototype is LoadPrototype) - { - var valueInstruction = block.Graph.GetInstruction(instruction.Arguments[0]); - EmitConstant(assemblyDefinition, ilProcessor, (ConstantPrototype)valueInstruction.Prototype); - } else if (instruction.Prototype is AllocaPrototype allocA) { var variable = EmitVariableDeclaration(clrMethod, assemblyDefinition, ilProcessor, item, allocA); variables.Add(item.Block.Parameters[variables.Count].Tag.Name, variable); } - else if (instruction.Prototype is AllocaArrayPrototype allocArray) + else if (instruction.Prototype is LoadArgPrototype larg) { - ilProcessor.Emit(OpCodes.Newarr, assemblyDefinition.ImportType(allocArray.ElementType)); + if (item.NextInstructionOrNull?.Prototype is not StorePrototype) + { + EmitLoadArg(clrMethod, ilProcessor, parentType, larg); + } } - else if (instruction.Prototype is IntrinsicPrototype arith) + else if (instruction.Prototype is LoadLocalPrototype lloc) { - EmitArithmetic(ilProcessor, arith); + if (item.NextInstructionOrNull?.Prototype is not StorePrototype) + { + EmitLoadLocal(ilProcessor, lloc, variables); + } } - else if (instruction.Prototype is LoadArgPrototype larg) + else if (instruction.Prototype is LoadFieldPrototype fp) { - EmitLoadArg(clrMethod, ilProcessor, parentType, larg); + if (item.NextInstructionOrNull?.Prototype is not StorePrototype) + { + var type = assemblyDefinition.ImportType(fp.Field.ParentType).Resolve(); + EmitLoadField(type, ilProcessor, fp); + } } - else if (instruction.Prototype is LoadLocalPrototype lloc) + else if (instruction.Prototype is GetFieldPointerPrototype fpa) { - EmitLoadLocal(ilProcessor, lloc, variables); + if (item.NextInstructionOrNull?.Prototype is not StorePrototype) + { + EmitLoadFieldA(parentType, ilProcessor, fpa); + } } - else if (instruction.Prototype is LoadFieldPrototype fp) + else if (instruction.Prototype is StorePrototype sp) { - EmitLoadField(parentType, ilProcessor, fp); + var ptr = sp.GetPointer(instruction); + var prototype = block.Graph.GetInstruction(ptr).Prototype; + + if (prototype is LoadLocalPrototype lcp) + { + var definition = variables[lcp.Parameter.Name.ToString()]; + ilProcessor.Emit(OpCodes.Stloc, definition); + } + else if (prototype is LoadFieldPrototype lfp) + { + var fieldType = assemblyDefinition.ImportType(lfp.Field.ParentType).Resolve(); + var field = fieldType.Fields.FirstOrDefault(_ => _.Name == lfp.Field.Name.ToString()); + + ilProcessor.Emit(OpCodes.Stfld, field); + } + else if (prototype is GetFieldPointerPrototype lfap) + { + EmitLoadFieldA(parentType, ilProcessor, lfap); + } + else if (prototype is LoadArgPrototype largp) + { + var param = clrMethod.Parameters.FirstOrDefault(_ => _.Name == largp.Parameter.Name.ToString()); + + if (param != null) + { + var index = clrMethod.Parameters.IndexOf(param); + + ilProcessor.Emit(OpCodes.Starg, index); + } + } } - else if (instruction.Prototype is StoreFieldPointerPrototype sp) + else if (instruction.Prototype is StoreFieldPointerPrototype spa) { - EmitStoreField(parentType, ilProcessor, sp); + EmitStoreField(parentType, ilProcessor, spa); } } + EmitBlockFlow(block, ilProcessor, clrMethod, fixups); + } + + private static void EmitBlockFlow(BasicBlock block, ILProcessor ilProcessor, MethodDefinition clrMethod, List<(int InstructionIndex, BasicBlockTag Target)> fixups) + { if (block.Flow is ReturnFlow rf) { if (rf.HasReturnValue) { - EmitConstant(assemblyDefinition, ilProcessor, (ConstantPrototype)rf.ReturnValue.Prototype); + EmitConstant(ilProcessor, (ConstantPrototype)rf.ReturnValue.Prototype); } ilProcessor.Emit(OpCodes.Ret); @@ -156,21 +244,34 @@ private static void CompileBlock(BasicBlock block, AssemblyDefinition assemblyDe else if (block.Flow is JumpConditionalFlow n) { fixups.Add((ilProcessor.Body.Instructions.Count, n.Branch.Target)); + + var op = OpCodes.Br; + + var selector = (ConditionalJumpKind)n.ConditionSelector; + if (selector == ConditionalJumpKind.True) + { + op = OpCodes.Brtrue; + } + else if (selector == ConditionalJumpKind.False) + { + op = OpCodes.Brfalse; + } + else if (selector == ConditionalJumpKind.Equals) + { + op = OpCodes.Beq; + } + else if (selector == ConditionalJumpKind.NotEquals) + { + op = OpCodes.Bne_Un; + } + ilProcessor.Emit( - (ConditionalJumpKind)n.ConditionSelector == ConditionalJumpKind.True ? - OpCodes.Brtrue : OpCodes.Br, Instruction.Create(OpCodes.Nop) + op, Instruction.Create(OpCodes.Nop) ); } else if (block.Flow is UnreachableFlow) { - if (clrMethod.ReturnType.Name == "Void") - { - ilProcessor.Emit(OpCodes.Ret); - } - else - { - ilProcessor.Emit(OpCodes.Throw); - } + ilProcessor.Emit(OpCodes.Throw); } } @@ -194,6 +295,13 @@ private static void EmitLoadField(TypeDefinition parentType, ILProcessor ilProce ilProcessor.Emit(OpCodes.Ldfld, field); } + private static void EmitLoadFieldA(TypeDefinition parentType, ILProcessor ilProcessor, GetFieldPointerPrototype fp) + { + var field = parentType.Fields.FirstOrDefault(_ => _.Name == fp.Field.Name.ToString()); + + ilProcessor.Emit(OpCodes.Ldflda, field); + } + private static void EmitLoadArg(MethodDefinition clrMethod, ILProcessor ilProcessor, TypeReference parentType, LoadArgPrototype larg) { var param = clrMethod.Parameters.FirstOrDefault(_ => _.Name == larg.Parameter.Name.ToString()); @@ -208,175 +316,67 @@ private static void EmitLoadArg(MethodDefinition clrMethod, ILProcessor ilProces } else { - var thisPtr = larg.Parameter.Type.Name.ToString() == parentType.Name.ToString(); //ToDo: fix namespacing - - if (thisPtr) + if (larg.Parameter.Name.ToString() == "this") { ilProcessor.Emit(OpCodes.Ldarg_0); } } } - private static void EmitArithmetic(ILProcessor ilProcessor, IntrinsicPrototype arith) + private static void EmitIntegerConstant(ILProcessor ilProcessor, dynamic v, IntegerConstant ic) { - switch (arith.Name) + switch (ic.Spec.Size) { - case "arith.+": - ilProcessor.Emit(OpCodes.Add); break; - case "arith.-": - ilProcessor.Emit(OpCodes.Sub); break; - case "arith.*": - ilProcessor.Emit(OpCodes.Mul); break; - case "arith./": - ilProcessor.Emit(OpCodes.Div); break; - case "arith.%": - ilProcessor.Emit(OpCodes.Rem); break; - case "arith.&": - ilProcessor.Emit(OpCodes.And); break; - case "arith.|": - ilProcessor.Emit(OpCodes.Or); break; - case "arith.^": - ilProcessor.Emit(OpCodes.Xor); break; - case "arith.==": - ilProcessor.Emit(OpCodes.Ceq); break; - case "arith.!=": - ilProcessor.Emit(OpCodes.Ceq); - ilProcessor.Emit(OpCodes.Ldc_I4, 0); - ilProcessor.Emit(OpCodes.Ceq); + case 1: + ilProcessor.Emit(!v.IsZero ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); break; - case "arith.<": - ilProcessor.Emit(OpCodes.Clt); break; - case "arith.<=": - ilProcessor.Emit(OpCodes.Cgt); - ilProcessor.Emit(OpCodes.Ldc_I4, 0); - ilProcessor.Emit(OpCodes.Ceq); + case 8: + if (ic.Spec.IsSigned) + { + ilProcessor.Emit(OpCodes.Ldc_I4, ic.ToInt8()); + } + else + { + ilProcessor.Emit(OpCodes.Ldc_I4, ic.ToUInt8()); + } break; - case "arith.>": - ilProcessor.Emit(OpCodes.Cgt); break; - case "arith.>=": - ilProcessor.Emit(OpCodes.Clt); - ilProcessor.Emit(OpCodes.Ldc_I4, 0); - ilProcessor.Emit(OpCodes.Ceq); + case 16: + if (ic.Spec.IsSigned) + { + ilProcessor.Emit(OpCodes.Ldc_I4, ic.ToInt16()); + } + else + { + ilProcessor.Emit(OpCodes.Ldc_I4, ic.ToUInt16()); + } break; - } - } - - private static void EmitNewObject(AssemblyDefinition assemblyDefinition, ILProcessor ilProcessor, NewObjectPrototype newObjectPrototype) - { - var method = GetMethod(assemblyDefinition, newObjectPrototype.Constructor); - - ilProcessor.Emit(OpCodes.Newobj, method); - } - - private static void EmitCall(AssemblyDefinition assemblyDefinition, ILProcessor ilProcessor, Furesoft.Core.CodeDom.Compiler.Instruction instruction, FlowGraph implementation, BasicBlock block) - { - var callPrototype = (CallPrototype)instruction.Prototype; - - if (IntrinsicHelper.IsIntrinsicType(typeof(Intrinsics), callPrototype)) - { - IntrinsicHelper.InvokeIntrinsic(typeof(Intrinsics), callPrototype.Callee, instruction, block); - return; - } - - var method = GetMethod(assemblyDefinition, callPrototype.Callee); - - for (var i = 0; i < method.Parameters.Count; i++) - { - var valueType = implementation.NamedInstructions - .Where(_ => instruction.Arguments[i] == _.Tag) - .Select(_ => _.ResultType).FirstOrDefault(); - - var arg = method.Parameters[i]; - if (arg.ParameterType.FullName.ToString() == "System.Object") - { - ilProcessor.Emit(OpCodes.Box, assemblyDefinition.ImportType(valueType)); - } - } - - ilProcessor.Emit(OpCodes.Call, - assemblyDefinition.MainModule.ImportReference( - method - ) - ); - } - - private static void EmitConstant(AssemblyDefinition assemblyDefinition, ILProcessor ilProcessor, ConstantPrototype consProto) - { - dynamic v = consProto.Value; - - if (v is StringConstant str) - { - ilProcessor.Emit(OpCodes.Ldstr, str.Value); - } - else if (v is Float32Constant f32) - { - ilProcessor.Emit(OpCodes.Ldc_R4, f32.Value); - } - else if (v is Float64Constant f64) - { - ilProcessor.Emit(OpCodes.Ldc_R8, f64.Value); - } - else if (v is NullConstant) - { - ilProcessor.Emit(OpCodes.Ldnull); - } - else if (v is IntegerConstant ic) - { - switch (ic.Spec.Size) - { - case 1: - ilProcessor.Emit(!v.IsZero ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); - break; - - case 8: - if (ic.Spec.IsSigned) - { - ilProcessor.Emit(OpCodes.Ldc_I4, ic.ToInt8()); - } - else - { - ilProcessor.Emit(OpCodes.Ldc_I4, ic.ToUInt8()); - } - break; - - case 16: - if (ic.Spec.IsSigned) - { - ilProcessor.Emit(OpCodes.Ldc_I4, ic.ToInt16()); - } - else - { - ilProcessor.Emit(OpCodes.Ldc_I4, ic.ToUInt16()); - } - break; - case 32: - if (ic.Spec.IsSigned) - { - ilProcessor.Emit(OpCodes.Ldc_I4, ic.ToInt32()); - } - else - { - ilProcessor.Emit(OpCodes.Ldc_I4, ic.ToUInt32()); - } - break; + case 32: + if (ic.Spec.IsSigned) + { + ilProcessor.Emit(OpCodes.Ldc_I4, ic.ToInt32()); + } + else + { + ilProcessor.Emit(OpCodes.Ldc_I4, ic.ToUInt32()); + } + break; - case 64: - if (ic.Spec.IsSigned) - { - ilProcessor.Emit(OpCodes.Ldc_I8, ic.ToInt64()); - } - else - { - ilProcessor.Emit(OpCodes.Ldc_I4, ic.ToUInt64()); - } - break; + case 64: + if (ic.Spec.IsSigned) + { + ilProcessor.Emit(OpCodes.Ldc_I8, ic.ToInt64()); + } + else + { + ilProcessor.Emit(OpCodes.Ldc_I4, ic.ToUInt64()); + } + break; - default: - break; - } + default: + break; } } @@ -401,41 +401,6 @@ private static VariableDefinition EmitVariableDeclaration(MethodDefinition clrMe return variable; } - private static MethodReference GetMethod(AssemblyDefinition assemblyDefinition, IMethod method) - { - var parentType = assemblyDefinition.ImportType(method.ParentType).Resolve(); - - foreach (var m in parentType.Methods.Where(_ => _.Name == method.Name.ToString())) - { - var parameters = m.Parameters; - - if (parameters.Count == method.Parameters.Count) - { - if (method.GenericParameters.Any()) - { - if (method.IsConstructor) - { - var dts = (DirectTypeSpecialization)GenericTypeMap.Cache[(method.FullName, method)]; - var args = dts.GetRecursiveGenericArguments().Select(_ => assemblyDefinition.ImportType(_)).ToArray(); - var genericType = assemblyDefinition.ImportType(dts); - var gctor = genericType.Resolve().Methods.FirstOrDefault(_ => _.Name == method.Name.ToString()); - - var mm = m.MakeHostInstanceGeneric(args); - - return assemblyDefinition.MainModule.ImportReference(mm); - } - - return m; - } - - if (MatchesParameters(parameters, method)) - return assemblyDefinition.MainModule.ImportReference(m); - } - } - - return null; - } - private static bool MatchesParameters(Mono.Collections.Generic.Collection parameters, IMethod method) { //ToDo: refactor to improve code diff --git a/Source/Backlang.Driver/Compiling/Targets/Dotnet/RuntimeOptionsModels/FrameworkOptions.cs b/Source/Backlang.Driver/Compiling/Targets/Dotnet/RuntimeOptionsModels/FrameworkOptions.cs new file mode 100644 index 00000000..195fdb3e --- /dev/null +++ b/Source/Backlang.Driver/Compiling/Targets/Dotnet/RuntimeOptionsModels/FrameworkOptions.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Backlang.Driver.Compiling.Targets.Dotnet.RuntimeOptionsModels; + +public class FrameworkOptions +{ + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("version")] + public string Version { get; set; } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Compiling/Targets/Dotnet/RuntimeOptionsModels/RuntimeConfig.cs b/Source/Backlang.Driver/Compiling/Targets/Dotnet/RuntimeOptionsModels/RuntimeConfig.cs new file mode 100644 index 00000000..b67bf667 --- /dev/null +++ b/Source/Backlang.Driver/Compiling/Targets/Dotnet/RuntimeOptionsModels/RuntimeConfig.cs @@ -0,0 +1,28 @@ +using Newtonsoft.Json; + +namespace Backlang.Driver.Compiling.Targets.Dotnet.RuntimeOptionsModels; + +public class RuntimeConfig +{ + [JsonProperty("runtimeOptions")] + public RuntimeOptions RuntimeOptions { get; set; } = new(); + + public static void Save(string path, string name, CompilerCliOptions options) + { + path = Path.Combine(path, name + ".runtimeconfig.json"); + + var data = new RuntimeConfig(); + data.RuntimeOptions.Tfm = options.TargetFramework; + data.RuntimeOptions.Framework.Name = "Microsoft.NETCore.App"; + data.RuntimeOptions.Framework.Version = "7.0.0"; + + var json = JsonConvert.SerializeObject(data, Formatting.Indented); + + File.WriteAllText(path, json); + } + + public static string GetVersion(string frameworkMoniker) + { + return frameworkMoniker.Replace("net", ""); + } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Compiling/Targets/Dotnet/RuntimeOptionsModels/RuntimeOptions.cs b/Source/Backlang.Driver/Compiling/Targets/Dotnet/RuntimeOptionsModels/RuntimeOptions.cs new file mode 100644 index 00000000..f2478478 --- /dev/null +++ b/Source/Backlang.Driver/Compiling/Targets/Dotnet/RuntimeOptionsModels/RuntimeOptions.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Backlang.Driver.Compiling.Targets.Dotnet.RuntimeOptionsModels; + +public class RuntimeOptions +{ + [JsonProperty("tfm")] + public string Tfm { get; set; } + + [JsonProperty("framework")] + public FrameworkOptions Framework { get; set; } = new(); +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Core/Implementors/Branches.cs b/Source/Backlang.Driver/Core/Implementors/Branches.cs new file mode 100644 index 00000000..08af9339 --- /dev/null +++ b/Source/Backlang.Driver/Core/Implementors/Branches.cs @@ -0,0 +1,7 @@ +namespace Backlang.Driver.Core.Implementors; + +public class BranchLabels +{ + public BasicBlockTag continueBranch; + public BasicBlockTag breakBranch; +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Core/Implementors/Expressions/ArrayExpressionImplementor.cs b/Source/Backlang.Driver/Core/Implementors/Expressions/ArrayExpressionImplementor.cs index 78082b77..04d5956e 100644 --- a/Source/Backlang.Driver/Core/Implementors/Expressions/ArrayExpressionImplementor.cs +++ b/Source/Backlang.Driver/Core/Implementors/Expressions/ArrayExpressionImplementor.cs @@ -1,4 +1,6 @@ using Furesoft.Core.CodeDom.Compiler.Core.Constants; +using Furesoft.Core.CodeDom.Compiler.Instructions; +using System.Runtime.CompilerServices; namespace Backlang.Driver.Core.Implementors.Expressions; @@ -17,7 +19,41 @@ public NamedInstructionBuilder Handle(LNode node, BasicBlockBuilder block, IType elementType = context.Binder.ResolveTypes(gn.TypeArgumentNames[0]).FirstOrDefault(); } - //ToDo: add array initialisation - return block.AppendInstruction(Instruction.CreateAllocaArray(elementType, counter)); + var arrayValuesType = GetOrAddArrayValueType(context.Environment.MakeArrayType(elementType, 1), context, out var field); //Todo: replace rank + + field.InitialValue = new byte[] { 1,2,3 }; + + //Todo: only emit this if values are primitive values otherwise emit storeelementref + var args = new List { + block.AppendInstruction(Instruction.CreateAllocaArray(elementType, counter)), + block.AppendInstruction(Instruction.CreateCopy(arrayValuesType, null)) + }; + + var initArrayMethod = context.Binder.FindFunction("System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(System.Array, System.RuntimeFieldHandle)"); + + return block.AppendInstruction(Instruction.CreateCall(initArrayMethod, MethodLookup.Static, args)); + } + + private static DescribedType GetOrAddArrayValueType(IType elementType, CompilerContext context, out DescribedField field) + { + var arrayValuesType = (DescribedType)context.Binder.ResolveTypes(new SimpleName(Names.ArrayValues).Qualify("")).FirstOrDefault(); + + if (arrayValuesType == null) + { + arrayValuesType = new DescribedType(new SimpleName(Names.ArrayValues).Qualify(""), context.Assembly) + { + IsStatic = true + }; + + Utils.AddCompilerGeneratedAttribute(context.Binder, arrayValuesType); + + context.Assembly.AddType(arrayValuesType); + } + + var randomFieldName = Utils.GenerateIdentifier(); + field = new DescribedField(arrayValuesType, new SimpleName(randomFieldName), true, elementType); + + arrayValuesType.AddField(field); + return arrayValuesType; } } \ No newline at end of file diff --git a/Source/Backlang.Driver/Core/Implementors/Expressions/AsExpressionImplementor.cs b/Source/Backlang.Driver/Core/Implementors/Expressions/AsExpressionImplementor.cs new file mode 100644 index 00000000..f3c04a90 --- /dev/null +++ b/Source/Backlang.Driver/Core/Implementors/Expressions/AsExpressionImplementor.cs @@ -0,0 +1,21 @@ +using static Backlang.Driver.Compiling.Stages.CompilationStages.ImplementationStage; + +namespace Backlang.Driver.Core.Implementors.Expressions; + +public class AsExpressionImplementor : IExpressionImplementor +{ + public bool CanHandle(LNode node) => node.ArgCount == 2 && node.Calls(CodeSymbols.As); + + public NamedInstructionBuilder Handle(LNode node, BasicBlockBuilder block, + IType elementType, CompilerContext context, Scope scope, QualifiedName? modulename) + { + //ToDo: if type is obj and expr is valuetype -> box and vice versa unbox + var exprType = TypeDeducer.Deduce(node.Args[0], scope, context, modulename.Value); + + AppendExpression(block, node.Args[0], exprType, context, scope, modulename); + + var instr = Instruction.CreateDynamicCast(elementType.MakePointerType(PointerKind.Transient), null); + + return block.AppendInstruction(instr); + } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Core/Implementors/Expressions/BinaryExpressionImplementor.cs b/Source/Backlang.Driver/Core/Implementors/Expressions/BinaryExpressionImplementor.cs index a01aed5b..0547f90d 100644 --- a/Source/Backlang.Driver/Core/Implementors/Expressions/BinaryExpressionImplementor.cs +++ b/Source/Backlang.Driver/Core/Implementors/Expressions/BinaryExpressionImplementor.cs @@ -5,10 +5,13 @@ namespace Backlang.Driver.Core.Implementors.Expressions; public class BinaryExpressionImplementor : IExpressionImplementor { - public bool CanHandle(LNode node) => node.ArgCount == 2 - && !node.Calls(CodeSymbols.ColonColon) - && !node.Calls(CodeSymbols.Tuple) - && node.Name.Name.StartsWith("'"); + public bool CanHandle(LNode node) + { + return node.ArgCount == 2 + && !node.Calls(CodeSymbols.ColonColon) + && !node.Calls(CodeSymbols.Tuple) + && node.Name.Name.StartsWith("'"); + } public NamedInstructionBuilder Handle(LNode node, BasicBlockBuilder block, IType elementType, CompilerContext context, Scope scope, QualifiedName? modulename) @@ -21,10 +24,9 @@ public NamedInstructionBuilder Handle(LNode node, BasicBlockBuilder block, if (leftType.TryGetOperator(node.Name.Name, out var opMethod, leftType, rightType)) { - return block.AppendInstruction( - Instruction.CreateCall(opMethod, MethodLookup.Static, new ValueTag[] { lhs, rhs })); + return block.AppendInstruction(Instruction.CreateCall(opMethod, MethodLookup.Static, new ValueTag[] { lhs, rhs })); } - else if (leftType == context.Environment.String || rightType == context.Environment.String) + else if (node.Calls(CodeSymbols.Add) && (leftType == context.Environment.String || rightType == context.Environment.String)) { var concatMethods = context.Environment.String.Methods .Where(_ => _.Name.ToString() == "Concat" && _.Parameters.Count == 2); @@ -35,6 +37,7 @@ public NamedInstructionBuilder Handle(LNode node, BasicBlockBuilder block, return block.AppendInstruction(call); } + //ToDo: add check for unittype and no unitype. then unpack unit type return block.AppendInstruction(Instruction.CreateBinaryArithmeticIntrinsic(node.Name.Name.Substring(1), false, elementType, lhs, rhs)); } diff --git a/Source/Backlang.Driver/Core/Implementors/Expressions/CallExpressionEmitter.cs b/Source/Backlang.Driver/Core/Implementors/Expressions/CallExpressionEmitter.cs index 37a7e1d6..2691cc9c 100644 --- a/Source/Backlang.Driver/Core/Implementors/Expressions/CallExpressionEmitter.cs +++ b/Source/Backlang.Driver/Core/Implementors/Expressions/CallExpressionEmitter.cs @@ -9,17 +9,43 @@ public class CallExpressionEmitter : IExpressionImplementor public NamedInstructionBuilder Handle(LNode node, BasicBlockBuilder block, IType elementType, CompilerContext context, Scope scope, QualifiedName? modulename) { - if (node.Calls(CodeSymbols.New)) + if (scope.TryGet(node.Name.Name, out var fn)) { - //ToDo: append ctor + return ImplementationStage.AppendCall(context, block, node, fn.Overloads, scope, modulename, methodName: node.Name.Name); } - if (scope.TryGet(node.Name.Name, out var fn)) + if (TryGetFreeFunctionsFromNamespace(context, node.Range.Source.FileName, out var functions)) { - return ImplementationStage.AppendCall(context, block, node, fn.Overloads, scope, modulename, methodName: node.Name.Name); + return ImplementationStage.AppendCall(context, block, node, functions, scope, modulename, methodName: node.Name.Name); } context.AddError(node, $"function {node.Name.Name} not found"); return null; } + + private static bool TryGetFreeFunctionsFromNamespace(CompilerContext context, string filename, out IEnumerable functions) + { + var allFreeFunctions = new List(); + if (context.FileScope.ImportetNamespaces.TryGetValue(filename, out var importedNamespaces)) + { + foreach (var ns in importedNamespaces.ImportedNamespaces) + { + if (context.Binder.TryResolveNamespace(ns, out var resolvedNs)) + { + if (!resolvedNs.Types.Any(_ => _.Name.ToString() == Names.FreeFunctions)) + { + continue; + } + + allFreeFunctions.AddRange(resolvedNs.Types.First(_ => _.Name.ToString() == Names.FreeFunctions).Methods); + } + } + + functions = allFreeFunctions; + return true; + } + + functions = null; + return false; + } } \ No newline at end of file diff --git a/Source/Backlang.Driver/Core/Implementors/Expressions/IdentifierExpressionImplementor.cs b/Source/Backlang.Driver/Core/Implementors/Expressions/IdentifierExpressionImplementor.cs index c2ebf6f6..de97483e 100644 --- a/Source/Backlang.Driver/Core/Implementors/Expressions/IdentifierExpressionImplementor.cs +++ b/Source/Backlang.Driver/Core/Implementors/Expressions/IdentifierExpressionImplementor.cs @@ -9,14 +9,17 @@ public class IdentifierExpressionImplementor : IExpressionImplementor public NamedInstructionBuilder Handle(LNode node, BasicBlockBuilder block, IType elementType, CompilerContext context, Scope scope, QualifiedName? modulename) { - scope.TryGet(node.Name.Name, out var item); + if (!scope.TryGet(node.Name.Name, out var item)) + { + context.AddError(node, new(Codeanalysis.Core.ErrorID.NotDefined, node.Name.Name)); + return null; + } if (item is ParameterScopeItem psi) { return block.AppendInstruction(Instruction.CreateLoadArg(psi.Parameter)); } - - if (item is VariableScopeItem vsi) + else if (item is VariableScopeItem vsi) { return block.AppendInstruction(Instruction.CreateLoadLocal(vsi.Parameter)); } diff --git a/Source/Backlang.Driver/Core/Implementors/Expressions/MemberExpressionImplementor.cs b/Source/Backlang.Driver/Core/Implementors/Expressions/MemberExpressionImplementor.cs new file mode 100644 index 00000000..ed4116b4 --- /dev/null +++ b/Source/Backlang.Driver/Core/Implementors/Expressions/MemberExpressionImplementor.cs @@ -0,0 +1,42 @@ +using Backlang.Contracts.Scoping.Items; +using Furesoft.Core.CodeDom.Compiler.Instructions; + +namespace Backlang.Driver.Core.Implementors.Expressions; + +public class MemberExpressionImplementor : IExpressionImplementor +{ + public bool CanHandle(LNode node) => node.ArgCount == 2 + && !node.Calls(CodeSymbols.ColonColon) + && !node.Calls(CodeSymbols.Tuple) + && node.Name.Name.StartsWith("'."); + + public NamedInstructionBuilder Handle(LNode node, BasicBlockBuilder block, + IType elementType, CompilerContext context, Scope scope, QualifiedName? modulename) + { + scope.TryGet(node.Args[0].Name.Name, out var item); + + var type = TypeDeducer.Deduce(node.Args[0], scope, context, modulename.Value); + + var field = type.Fields.FirstOrDefault(_ => _.Name.ToString() == node.Args[1].Name.ToString()); + if (field != null) + { + block.AppendInstruction(Instruction.CreateLoadLocal(item.Parameter)); + return block.AppendInstruction(Instruction.CreateLoadField(field)); + } + + var method = type.Methods.FirstOrDefault(_ => _.Name.ToString() == node.Args[1].Name.ToString()); + if (method != null) + { + var instance = ImplementationStage.AppendExpression(block, node[0], type, context, scope, modulename); + + var callTags = ImplementationStage.AppendCallArguments(context, block, node[1], scope, modulename); + callTags.Insert(0, instance); + + var call = Instruction.CreateCall(method, MethodLookup.Virtual, callTags); + + return block.AppendInstruction(call); + } + + return null; + } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Core/Implementors/IStatementImplementor.cs b/Source/Backlang.Driver/Core/Implementors/IStatementImplementor.cs index 7445cb92..d1a4d747 100644 --- a/Source/Backlang.Driver/Core/Implementors/IStatementImplementor.cs +++ b/Source/Backlang.Driver/Core/Implementors/IStatementImplementor.cs @@ -2,6 +2,5 @@ public interface IStatementImplementor { - BasicBlockBuilder Implement(CompilerContext context, IMethod method, - BasicBlockBuilder block, LNode node, QualifiedName? modulename, Scope scope); + BasicBlockBuilder Implement(LNode node, BasicBlockBuilder block, CompilerContext context, IMethod method, QualifiedName? modulename, Scope scope, BranchLabels branchLabels = null); } \ No newline at end of file diff --git a/Source/Backlang.Driver/Core/Implementors/Statements/AssignmentImplementor.cs b/Source/Backlang.Driver/Core/Implementors/Statements/AssignmentImplementor.cs index 219fc758..071c2267 100644 --- a/Source/Backlang.Driver/Core/Implementors/Statements/AssignmentImplementor.cs +++ b/Source/Backlang.Driver/Core/Implementors/Statements/AssignmentImplementor.cs @@ -5,7 +5,7 @@ namespace Backlang.Driver.Core.Implementors.Statements; public class AssignmentImplementor : IStatementImplementor { - public BasicBlockBuilder Implement(CompilerContext context, IMethod method, BasicBlockBuilder block, LNode node, QualifiedName? modulename, Scope scope) + public BasicBlockBuilder Implement(LNode node, BasicBlockBuilder block, CompilerContext context, IMethod method, QualifiedName? modulename, Scope scope, BranchLabels branchLabels = null) { if (node is (_, var left, var right)) { @@ -20,9 +20,11 @@ public BasicBlockBuilder Implement(CompilerContext context, IMethod method, Basi context.AddError(node, $"Cannot assing immutable variable '{va.Name}'"); } - var value = AppendExpression(block, right, rt, context, scope, modulename.Value); + var value = AppendExpression(block, right, rt, context, + scope, modulename.Value); + var pointer = block.AppendInstruction(Instruction.CreateLoadLocal(va.Parameter)); - block.AppendInstruction(Instruction.CreateStore(lt, new ValueTag(va.Parameter.Name.ToString()), value)); + block.AppendInstruction(Instruction.CreateStore(lt, pointer, value)); } else if (left is ("'.", var t, var c)) { @@ -32,8 +34,11 @@ public BasicBlockBuilder Implement(CompilerContext context, IMethod method, Basi if (field != null) { + block.AppendInstruction(Instruction.CreateLoadLocalAdress(vsi.Parameter)); + var value = AppendExpression(block, right, field.FieldType, + context, scope, modulename.Value); + var pointer = block.AppendInstruction(Instruction.CreateLoadField(field)); - var value = AppendExpression(block, right, field.FieldType, context, scope, modulename.Value); block.AppendInstruction(Instruction.CreateStore(field.FieldType, pointer, value)); } diff --git a/Source/Backlang.Driver/Core/Implementors/Statements/BreakStatementImplementor.cs b/Source/Backlang.Driver/Core/Implementors/Statements/BreakStatementImplementor.cs new file mode 100644 index 00000000..a3a5459e --- /dev/null +++ b/Source/Backlang.Driver/Core/Implementors/Statements/BreakStatementImplementor.cs @@ -0,0 +1,13 @@ +using Furesoft.Core.CodeDom.Compiler.Flow; + +namespace Backlang.Driver.Core.Implementors.Statements; + +public class BreakStatementImplementor : IStatementImplementor +{ + public BasicBlockBuilder Implement(LNode node, BasicBlockBuilder block, CompilerContext context, IMethod method, QualifiedName? modulename, Scope scope, BranchLabels branchLabels = null) + { + block.Flow = new JumpFlow(branchLabels.breakBranch); + + return block; + } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Core/Implementors/Statements/CallImplementor.cs b/Source/Backlang.Driver/Core/Implementors/Statements/CallImplementor.cs index 0195f117..3a3b1865 100644 --- a/Source/Backlang.Driver/Core/Implementors/Statements/CallImplementor.cs +++ b/Source/Backlang.Driver/Core/Implementors/Statements/CallImplementor.cs @@ -1,12 +1,21 @@ using Backlang.Contracts.Scoping.Items; +using Backlang.Driver.Core.Instructions; using static Backlang.Driver.Compiling.Stages.CompilationStages.ImplementationStage; namespace Backlang.Driver.Core.Implementors.Statements; public class CallImplementor : IStatementImplementor { - public BasicBlockBuilder Implement(CompilerContext context, IMethod method, BasicBlockBuilder block, - LNode node, QualifiedName? modulename, Scope scope) + public static void AppendDiscardReturnValue(BasicBlockBuilder block, IType type) + { + if (type.FullName.ToString() != "System.Void") + { + //Discard value if its not been stored anywhere + block.AppendInstruction(new PopInstructionPrototype().Instantiate(new List())); + } + } + + public BasicBlockBuilder Implement(LNode node, BasicBlockBuilder block, CompilerContext context, IMethod method, QualifiedName? modulename, Scope scope, BranchLabels branchLabels = null) { if (node is ("'.", var target, var callee) && target is ("this", _)) { @@ -18,11 +27,14 @@ public BasicBlockBuilder Implement(CompilerContext context, IMethod method, Basi if (scope.TryGet(callee.Name.Name, out var fsi)) { - AppendCall(context, block, callee, fsi.Overloads, scope, modulename); + AppendCall(context, block, callee, fsi.Overloads, + scope, modulename); + + AppendDiscardReturnValue(block, fsi.Overloads[0].ReturnParameter.Type); } else { - context.AddError(node, $"Cannot find function '{callee.Name.Name}'"); + context.AddError(node, new(Codeanalysis.Core.ErrorID.CannotFindFunction, callee.Name.Name)); } } else diff --git a/Source/Backlang.Driver/Core/Implementors/Statements/ContinueStatementImplementor.cs b/Source/Backlang.Driver/Core/Implementors/Statements/ContinueStatementImplementor.cs new file mode 100644 index 00000000..2828335c --- /dev/null +++ b/Source/Backlang.Driver/Core/Implementors/Statements/ContinueStatementImplementor.cs @@ -0,0 +1,13 @@ +using Furesoft.Core.CodeDom.Compiler.Flow; + +namespace Backlang.Driver.Core.Implementors.Statements; + +public class ContinueStatementImplementor : IStatementImplementor +{ + public BasicBlockBuilder Implement(LNode node, BasicBlockBuilder block, CompilerContext context, IMethod method, QualifiedName? modulename, Scope scope, BranchLabels branchLabels = null) + { + block.Flow = new JumpFlow(branchLabels.continueBranch); + + return block; + } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Core/Implementors/Statements/DoWhileImplementor.cs b/Source/Backlang.Driver/Core/Implementors/Statements/DoWhileImplementor.cs new file mode 100644 index 00000000..a9ab0853 --- /dev/null +++ b/Source/Backlang.Driver/Core/Implementors/Statements/DoWhileImplementor.cs @@ -0,0 +1,37 @@ +using Furesoft.Core.CodeDom.Compiler.Flow; +using static Backlang.Driver.Compiling.Stages.CompilationStages.ImplementationStage; + +namespace Backlang.Driver.Core.Implementors.Statements; + +public class DoWhileImplementor : IStatementImplementor +{ + public BasicBlockBuilder Implement(LNode node, BasicBlockBuilder block, CompilerContext context, IMethod method, QualifiedName? modulename, Scope scope, BranchLabels branchLabels = null) + { + if (node is (_, var body, var condition)) + { + TypeDeducer.ExpectType(condition, scope, context, + modulename.Value, context.Environment.Boolean); + + var do_body = block.Graph.AddBasicBlock(LabelGenerator.NewLabel("do_start")); + var do_condition = block.Graph.AddBasicBlock(LabelGenerator.NewLabel("do_body")); + var do_after = block.Graph.AddBasicBlock(LabelGenerator.NewLabel("do_after")); + + do_after.Flow = new NothingFlow(); + do_body.Flow = new NothingFlow(); + + branchLabels.breakBranch = do_after; + branchLabels.continueBranch = do_body; + + AppendBlock(body, do_body, context, method, modulename, scope.CreateChildScope(), branchLabels); + + AppendExpression(do_condition, condition, context.Environment.Boolean, + context, scope.CreateChildScope(), modulename); + + do_condition.Flow = new JumpConditionalFlow(do_body, ConditionalJumpKind.True); + + return do_after; + } + + return block; + } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Core/Implementors/Statements/IfImplementor.cs b/Source/Backlang.Driver/Core/Implementors/Statements/IfImplementor.cs index e1997b15..a18785c5 100644 --- a/Source/Backlang.Driver/Core/Implementors/Statements/IfImplementor.cs +++ b/Source/Backlang.Driver/Core/Implementors/Statements/IfImplementor.cs @@ -5,41 +5,74 @@ namespace Backlang.Driver.Core.Implementors.Statements; public class IfImplementor : IStatementImplementor { - public BasicBlockBuilder Implement(CompilerContext context, IMethod method, BasicBlockBuilder block, - LNode node, QualifiedName? modulename, Scope scope) + public BasicBlockBuilder Implement(LNode node, BasicBlockBuilder block, CompilerContext context, IMethod method, QualifiedName? modulename, Scope scope, BranchLabels branchLabels = null) { - if (node is (_, (_, var condition, var body, var el))) + if (node is (_, (_, var condition, var body, var elseBody))) { - TypeDeducer.ExpectType(condition, scope, context, modulename.Value, context.Environment.Boolean); + TypeDeducer.ExpectType(condition, scope, context, modulename.Value, + context.Environment.Boolean); - var ifBlock = block.Graph.AddBasicBlock(LabelGenerator.NewLabel("if")); - AppendBlock(body, ifBlock, context, method, modulename, scope.CreateChildScope()); + var if_after = ImplementIf(block, context, method, modulename, scope, branchLabels, condition, body); - if (el != LNode.Missing) + if (elseBody.Name != LNode.Missing.Name) { - var elseBlock = block.Graph.AddBasicBlock(LabelGenerator.NewLabel("else")); - AppendBlock(el, elseBlock, context, method, modulename, scope.CreateChildScope()); + if_after = ImplementIf(if_after, context, method, modulename, scope, branchLabels, condition, elseBody, true); } - var if_condition = block.Graph.AddBasicBlock(LabelGenerator.NewLabel("if_condition")); - if (condition.Calls(CodeSymbols.Bool)) - { - if_condition.Flow = new JumpConditionalFlow(ifBlock, ConditionalJumpKind.True); - } - else - { - AppendExpression(if_condition, condition, context.Environment.Boolean, context, scope, modulename); - if_condition.Flow = new JumpConditionalFlow(ifBlock, ConditionalJumpKind.Equals); - } + return if_after; + } + + return null; + } + + private static BasicBlockBuilder ImplementIf(BasicBlockBuilder block, CompilerContext context, IMethod method, QualifiedName? modulename, + Scope scope, BranchLabels branchLabels, LNode condition, LNode body, bool negate = false) + { + var if_start = block.Graph.AddBasicBlock(LabelGenerator.NewLabel("if_start")); + var if_condition = block.Graph.AddBasicBlock(LabelGenerator.NewLabel("if_condition")); + var if_end = block.Graph.AddBasicBlock(LabelGenerator.NewLabel("if_end")); + + AppendBlock(body, if_start, context, method, modulename, scope.CreateChildScope(), branchLabels); + + AppendExpression(if_condition, condition, context.Environment.Boolean, context, scope, modulename); + + if_condition.Flow = new JumpConditionalFlow(if_start, negate ? ConditionalJumpKind.False : ConditionalJumpKind.True); - block.Flow = new JumpFlow(if_condition); + block.Flow = new JumpFlow(if_condition); - var after = block.Graph.AddBasicBlock(LabelGenerator.NewLabel("after")); - ifBlock.Flow = new JumpFlow(after); + if_end.Flow = new NothingFlow(); - return after; + var if_after = block.Graph.AddBasicBlock(LabelGenerator.NewLabel("if_after")); + if_after.Flow = new NothingFlow(); + + return if_after; + } + + private static BasicBlockBuilder ImplementIfElse(BasicBlockBuilder block, CompilerContext context, IMethod method, QualifiedName? modulename, + Scope scope, BranchLabels branchLabels, LNode condition, LNode body, LNode elseBody) + { + var if_condition = block.Graph.AddBasicBlock(LabelGenerator.NewLabel("if_condition")); + var if_body = block.Graph.AddBasicBlock(LabelGenerator.NewLabel("if_start")); + var else_end = block.Graph.AddBasicBlock(LabelGenerator.NewLabel("else_end")); + var else_body = block.Graph.AddBasicBlock(LabelGenerator.NewLabel("else_start")); + + else_end.Flow = new NothingFlow(); + + AppendExpression(if_condition, condition, context.Environment.Boolean, context, scope, modulename); + if_condition.Flow = new JumpConditionalFlow(else_body, ConditionalJumpKind.False); + + AppendBlock(body, if_body, context, method, modulename, scope.CreateChildScope(), branchLabels); + AppendBlock(elseBody, else_body, context, method, modulename, scope.CreateChildScope(), branchLabels); + + if (if_body.Flow is NothingFlow) + { + if_body.Flow = new JumpFlow(else_end); + } + if (else_body.Flow is NothingFlow) + { + else_body.Flow = new JumpFlow(else_end); } - return null; + return else_end; } } \ No newline at end of file diff --git a/Source/Backlang.Driver/Core/Implementors/Statements/PrintOrPrintlnImplementor.cs b/Source/Backlang.Driver/Core/Implementors/Statements/PrintOrPrintlnImplementor.cs new file mode 100644 index 00000000..b29c0f40 --- /dev/null +++ b/Source/Backlang.Driver/Core/Implementors/Statements/PrintOrPrintlnImplementor.cs @@ -0,0 +1,18 @@ +namespace Backlang.Driver.Core.Implementors.Statements; + +public class PrintOrPrintlnImplementor : IStatementImplementor +{ + public BasicBlockBuilder Implement(LNode node, BasicBlockBuilder block, CompilerContext context, IMethod method, QualifiedName? modulename, Scope scope, BranchLabels branchLabels = null) + { + var deducedArg = TypeDeducer.Deduce(node.Args[0], scope, + context, modulename.Value); + var functionName = node.Calls("println") ? "WriteLine" : "Write"; + + var printFunction = context.Binder.FindFunction($"System.Console::{functionName}({deducedArg})"); + + ImplementationStage.AppendCall(context, block, node, + printFunction, scope, modulename.Value); + + return block; + } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Core/Implementors/Statements/ReturnImplementor.cs b/Source/Backlang.Driver/Core/Implementors/Statements/ReturnImplementor.cs index 9d69e39f..79c4e940 100644 --- a/Source/Backlang.Driver/Core/Implementors/Statements/ReturnImplementor.cs +++ b/Source/Backlang.Driver/Core/Implementors/Statements/ReturnImplementor.cs @@ -5,15 +5,15 @@ namespace Backlang.Driver.Core.Implementors.Statements; public class ReturnImplementor : IStatementImplementor { - public BasicBlockBuilder Implement(CompilerContext context, IMethod method, BasicBlockBuilder block, - LNode node, QualifiedName? modulename, Scope scope) + public BasicBlockBuilder Implement(LNode node, BasicBlockBuilder block, CompilerContext context, IMethod method, QualifiedName? modulename, Scope scope, BranchLabels branchLabels = null) { if (node.ArgCount == 1) { var valueNode = node.Args[0]; AppendExpression(block, valueNode, - TypeDeducer.Deduce(valueNode, scope, context, modulename.Value), context, scope, modulename); + TypeDeducer.Deduce(valueNode, scope, context, modulename.Value), + context, scope, modulename); block.Flow = new ReturnFlow(); } @@ -21,6 +21,7 @@ public BasicBlockBuilder Implement(CompilerContext context, IMethod method, Basi { block.Flow = new ReturnFlow(); } + return block; } } \ No newline at end of file diff --git a/Source/Backlang.Driver/Core/Implementors/Statements/StaticCallImplementor.cs b/Source/Backlang.Driver/Core/Implementors/Statements/StaticCallImplementor.cs index 06594fb2..00de84f8 100644 --- a/Source/Backlang.Driver/Core/Implementors/Statements/StaticCallImplementor.cs +++ b/Source/Backlang.Driver/Core/Implementors/Statements/StaticCallImplementor.cs @@ -14,10 +14,11 @@ public NamedInstructionBuilder Handle(LNode node, BasicBlockBuilder block, return ImplementationStage.AppendCall(context, block, callee, type.Methods, scope, modulename, methodName: callee.Name.Name); } - public BasicBlockBuilder Implement(CompilerContext context, IMethod method, BasicBlockBuilder block, - LNode node, QualifiedName? modulename, Scope scope) + public BasicBlockBuilder Implement(LNode node, BasicBlockBuilder block, CompilerContext context, IMethod method, QualifiedName? modulename, Scope scope, BranchLabels branchLabels = null) { - Handle(node, block, TypeDeducer.Deduce(node, scope, context, modulename.Value), context, scope, modulename); + Handle(node, block, + TypeDeducer.Deduce(node, scope, context, modulename.Value), + context, scope, modulename); return block; } diff --git a/Source/Backlang.Driver/Core/Implementors/Statements/ThrowImplementor.cs b/Source/Backlang.Driver/Core/Implementors/Statements/ThrowImplementor.cs index 88eca0a1..4738513b 100644 --- a/Source/Backlang.Driver/Core/Implementors/Statements/ThrowImplementor.cs +++ b/Source/Backlang.Driver/Core/Implementors/Statements/ThrowImplementor.cs @@ -5,15 +5,16 @@ namespace Backlang.Driver.Core.Implementors.Statements; public class ThrowImplementor : IStatementImplementor { - public BasicBlockBuilder Implement(CompilerContext context, IMethod method, BasicBlockBuilder block, - LNode node, QualifiedName? modulename, Scope scope) + public BasicBlockBuilder Implement(LNode node, BasicBlockBuilder block, CompilerContext context, IMethod method, QualifiedName? modulename, Scope scope, BranchLabels branchLabels = null) { var valueNode = node.Args[0].Args[0]; var constant = block.AppendInstruction(ConvertConstant( - GetLiteralType(valueNode, context, scope, modulename.Value), valueNode.Value)); + GetLiteralType(valueNode, context, scope, + modulename.Value), valueNode.Value)); var msg = block.AppendInstruction( - Instruction.CreateLoad(GetLiteralType(valueNode, context, scope, modulename.Value), constant)); + Instruction.CreateLoad(GetLiteralType(valueNode, context, + scope, modulename.Value), constant)); if (node.Args[0].Name.Name == "#string") { diff --git a/Source/Backlang.Driver/Core/Implementors/Statements/VariableImplementor.cs b/Source/Backlang.Driver/Core/Implementors/Statements/VariableImplementor.cs index b97572a4..8df43f8c 100644 --- a/Source/Backlang.Driver/Core/Implementors/Statements/VariableImplementor.cs +++ b/Source/Backlang.Driver/Core/Implementors/Statements/VariableImplementor.cs @@ -1,20 +1,22 @@ -using Backlang.Contracts.Scoping.Items; +using Backlang.Codeanalysis.Core; +using Backlang.Contracts.Scoping.Items; using Backlang.Contracts.TypeSystem; namespace Backlang.Driver.Core.Implementors.Statements; public class VariableImplementor : IStatementImplementor { - public BasicBlockBuilder Implement(CompilerContext context, IMethod method, BasicBlockBuilder block, - LNode node, QualifiedName? modulename, Scope scope) + public BasicBlockBuilder Implement(LNode node, BasicBlockBuilder block, CompilerContext context, IMethod method, QualifiedName? modulename, Scope scope, BranchLabels branchLabels = null) { var decl = node.Args[1]; var typename = ConversionUtils.GetQualifiedName(node.Args[0]); - var elementType = TypeInheritanceStage.ResolveTypeWithModule(node.Args[0], context, modulename.Value, typename); + var elementType = TypeInheritanceStage.ResolveTypeWithModule(node.Args[0], + context, modulename.Value, typename); - var deducedValueType = TypeDeducer.Deduce(decl.Args[1], scope, context, modulename.Value); + var deducedValueType = TypeDeducer.Deduce(decl.Args[1], scope, + context, modulename.Value); if (elementType == null) { @@ -24,24 +26,28 @@ public BasicBlockBuilder Implement(CompilerContext context, IMethod method, Basi { if (node.Args[0] is (_, (_, var tp))) //ToDo: Implement Helper function To Get Typename { - context.AddError(node, $"{tp.Name} cannot be resolved"); + context.AddError(node, + new(ErrorID.CannotBeResolved, tp.Name.ToString())); } } } else { - if (deducedValueType != null && !elementType.IsAssignableTo(deducedValueType) && deducedValueType != context.Environment.Void) + if (deducedValueType != null && !elementType.IsAssignableTo(deducedValueType) && + deducedValueType != context.Environment.Void) { if (elementType is UnitType ut) { if (ut != deducedValueType) { - context.AddError(node, $"Unit Type mismatch {elementType} {deducedValueType}"); + context.AddError(node, + new(ErrorID.UnitTypeMismatch, elementType.ToString(), deducedValueType.ToString())); } return block; } - context.AddError(node, $"Type mismatch {elementType} {deducedValueType}"); + context.AddError(node, + new(ErrorID.TypeMismatch, elementType.ToString(), deducedValueType.ToString())); } } @@ -59,12 +65,13 @@ public BasicBlockBuilder Implement(CompilerContext context, IMethod method, Basi } else { - context.AddError(decl.Args[0], $"{varname} already declared"); + context.AddError(decl.Args[0], new(ErrorID.AlreadyDeclared, varname)); } if (deducedValueType == null) return block; - ImplementationStage.AppendExpression(block, decl.Args[1], elementType, context, scope, modulename); + ImplementationStage.AppendExpression(block, decl.Args[1], elementType, context, + scope, modulename); block.AppendInstruction(Instruction.CreateAlloca(elementType)); diff --git a/Source/Backlang.Driver/Core/Implementors/Statements/WhileImplementor.cs b/Source/Backlang.Driver/Core/Implementors/Statements/WhileImplementor.cs index 1dcb4620..61b51389 100644 --- a/Source/Backlang.Driver/Core/Implementors/Statements/WhileImplementor.cs +++ b/Source/Backlang.Driver/Core/Implementors/Statements/WhileImplementor.cs @@ -1,45 +1,40 @@ -using Backlang.Driver.Compiling.Targets.Dotnet; -using Furesoft.Core.CodeDom.Compiler.Flow; +using Furesoft.Core.CodeDom.Compiler.Flow; using static Backlang.Driver.Compiling.Stages.CompilationStages.ImplementationStage; namespace Backlang.Driver.Core.Implementors.Statements; public class WhileImplementor : IStatementImplementor { - public BasicBlockBuilder Implement(CompilerContext context, IMethod method, BasicBlockBuilder block, - LNode node, QualifiedName? modulename, Scope scope) + public BasicBlockBuilder Implement(LNode node, BasicBlockBuilder block, CompilerContext context, IMethod method, QualifiedName? modulename, Scope scope, BranchLabels branchLabels) { if (node is (_, var condition, var body)) { - TypeDeducer.ExpectType(condition, scope, context, modulename.Value, context.Environment.Boolean); + TypeDeducer.ExpectType(condition, scope, context, modulename.Value, + context.Environment.Boolean); var while_start = block.Graph.AddBasicBlock(LabelGenerator.NewLabel("while_start")); - AppendBlock(body, while_start, context, method, modulename, scope.CreateChildScope()); - var while_condition = block.Graph.AddBasicBlock(LabelGenerator.NewLabel("while_condition")); - AppendExpression(block, condition, context.Environment.Boolean, context, scope, modulename); - //while_condition.Flow = new JumpFlow(while_start); - while_condition.Flow = new NothingFlow(); - var while_end = block.Graph.AddBasicBlock(LabelGenerator.NewLabel("while_end")); - block.Flow = new JumpFlow(while_condition); - //block.Flow = new NothingFlow(); + var while_after = block.Graph.AddBasicBlock(LabelGenerator.NewLabel("while_after")); + while_after.Flow = new NothingFlow(); - while_start.Flow = new NothingFlow(); - - if (condition.Calls(CodeSymbols.Bool)) - { - while_end.Flow = new JumpConditionalFlow(while_start, ConditionalJumpKind.True); - } - else + branchLabels = new() { - //AppendExpression(block, condition, method.ParentType, context, scope, modulename); - while_end.Flow = new NothingFlow(); - } + breakBranch = while_after, + continueBranch = while_condition + }; + + AppendBlock(body, while_start, context, method, modulename, scope.CreateChildScope(), branchLabels); + + AppendExpression(while_condition, condition, context.Environment.Boolean, context, scope, modulename); + + while_condition.Flow = new JumpConditionalFlow(while_start, ConditionalJumpKind.True); + + block.Flow = new JumpFlow(while_condition); - // while_end.Flow = new NothingFlow(); + while_end.Flow = new NothingFlow(); - return block.Graph.AddBasicBlock(); + return while_after; } return null; diff --git a/Source/Backlang.Driver/Core/ImplicitTypeCastTable.cs b/Source/Backlang.Driver/Core/ImplicitTypeCastTable.cs index 891545fd..122986a8 100644 --- a/Source/Backlang.Driver/Core/ImplicitTypeCastTable.cs +++ b/Source/Backlang.Driver/Core/ImplicitTypeCastTable.cs @@ -12,9 +12,9 @@ public static void InitCastMap(TypeEnvironment environment) castMap.Add(environment.UInt16, new[] { environment.UInt32, environment.UInt64 }); castMap.Add(environment.UInt32, new[] { environment.UInt64 }); - castMap.Add(environment.Int8, new[] { environment.Int16, environment.Int32, environment.Int64 }); - castMap.Add(environment.Int16, new[] { environment.Int32, environment.Int64 }); - castMap.Add(environment.Int32, new[] { environment.Int64 }); + castMap.Add(environment.Int8, new[] { environment.Int16, environment.Int32, environment.Int64, environment.Float32 }); + castMap.Add(environment.Int16, new[] { environment.Int32, environment.Int64, environment.Float32, environment.Float64 }); + castMap.Add(environment.Int32, new[] { environment.Int64, environment.Float64 }); } public static bool IsAssignableTo(this IType type, IType toCast) diff --git a/Source/Backlang.Driver/Core/Instructions/PopInstructionPrototype.cs b/Source/Backlang.Driver/Core/Instructions/PopInstructionPrototype.cs new file mode 100644 index 00000000..2a9371cf --- /dev/null +++ b/Source/Backlang.Driver/Core/Instructions/PopInstructionPrototype.cs @@ -0,0 +1,18 @@ +namespace Backlang.Driver.Core.Instructions; + +internal class PopInstructionPrototype : InstructionPrototype +{ + public override IType ResultType => null; + + public override int ParameterCount => 0; + + public override IReadOnlyList CheckConformance(Instruction instance, MethodBody body) + { + return null; + } + + public override InstructionPrototype Map(MemberMapping mapping) + { + return null; + } +} \ No newline at end of file diff --git a/Source/Backlang.Driver/Core/Instructions/TypeOfInstructionPrototype.cs b/Source/Backlang.Driver/Core/Instructions/TypeOfInstructionPrototype.cs index 1ad8160e..8f18019e 100644 --- a/Source/Backlang.Driver/Core/Instructions/TypeOfInstructionPrototype.cs +++ b/Source/Backlang.Driver/Core/Instructions/TypeOfInstructionPrototype.cs @@ -21,4 +21,4 @@ public override InstructionPrototype Map(MemberMapping mapping) { return null; } -} \ No newline at end of file +} diff --git a/Source/Backlang.Driver/Core/OperatorOverloadingHelpers.cs b/Source/Backlang.Driver/Core/OperatorOverloadingHelpers.cs index afde8c54..182a864f 100644 --- a/Source/Backlang.Driver/Core/OperatorOverloadingHelpers.cs +++ b/Source/Backlang.Driver/Core/OperatorOverloadingHelpers.cs @@ -13,6 +13,8 @@ public static class OperatorOverloadingHelpers ["'&"] = "op_BitwiseAnd", ["'|"] = "op_BitwiseOr", ["'^"] = "op_ExclusiveOr", + ["'=="] = "op_Equality", + ["'!="] = "op_Inequality", }.ToImmutableDictionary(); private static readonly ImmutableDictionary unMap = new Dictionary() @@ -25,6 +27,10 @@ public static class OperatorOverloadingHelpers ["'&"] = "op_AddressOf", ["'%"] = "op_Percentage", + ["'suf?"] = "op_Unpacking", + + ["implicit"] = "op_Implicit", + ["explicit"] = "op_Explicit", }.ToImmutableDictionary(); public static bool TryGetOperator(this IType type, string op, out IMethod opMethod, params IType[] args) diff --git a/Source/Backlang.Driver/InternalMacros/IntrinsicsMacros.cs b/Source/Backlang.Driver/InternalMacros/IntrinsicsMacros.cs index 7710901b..299e8def 100644 --- a/Source/Backlang.Driver/InternalMacros/IntrinsicsMacros.cs +++ b/Source/Backlang.Driver/InternalMacros/IntrinsicsMacros.cs @@ -1,4 +1,5 @@ using LeMP; +using Loyc.Syntax; using System.Reflection; namespace Backlang.Driver.InternalMacros; @@ -37,10 +38,7 @@ public static LNode InlineBlock(LNode node, IMacroContext context) return LNode.Call((Symbol)"'{}"); } - if (intrinsicNames == null) - { - intrinsicNames = InitAvailableIntrinsicNames(compContext.CompilationTarget.IntrinsicType); - } + intrinsicNames ??= InitAvailableIntrinsicNames(compContext.CompilationTarget.IntrinsicType); var availableConstants = GetAvailableConstants(compContext.CompilationTarget.IntrinsicType); @@ -54,7 +52,7 @@ public static LNode InlineBlock(LNode node, IMacroContext context) continue; } - calls = ConvertCall(calls, compContext, availableConstants, compContext.CompilationTarget.IntrinsicType); + calls = ConvertCall(calls, compContext, availableConstants); var newCall = ConvertIntrinsic(calls, compContext.CompilationTarget.IntrinsicType); newBodyArgs = newBodyArgs.Add(newCall); @@ -63,7 +61,7 @@ public static LNode InlineBlock(LNode node, IMacroContext context) return LNode.Call((Symbol)"'{}", newBodyArgs).WithStyle(NodeStyle.Operator); } - private static LNode ConvertCall(LNode calls, CompilerContext context, Dictionary availableConstants, Type intrinsicType) + private static LNode ConvertCall(LNode calls, CompilerContext context, Dictionary availableConstants) { var newArgs = new LNodeList(); diff --git a/Source/Backlang.Driver/InternalMacros/SyntacticMacros.cs b/Source/Backlang.Driver/InternalMacros/SyntacticMacros.cs index f5e23337..88f95371 100644 --- a/Source/Backlang.Driver/InternalMacros/SyntacticMacros.cs +++ b/Source/Backlang.Driver/InternalMacros/SyntacticMacros.cs @@ -1,5 +1,4 @@ using LeMP; -using Loyc.Collections; using System.Text.RegularExpressions; namespace Backlang.Driver.InternalMacros; @@ -23,7 +22,11 @@ public static class SyntacticMacros ["bitwise_or"] = ("BitwiseOr", 2), ["exclusive_or"] = ("ExclusiveOr", 2), + ["equality"] = ("Equality", 2), + ["inequality"] = ("Inequality", 2), + ["bitwise_not"] = ("OnesComplement", 1), + ["unpacking"] = ("Unpacking", 1), ["deref"] = ("Deref", 1), ["addrof"] = ("AddressOf", 2), @@ -31,37 +34,28 @@ public static class SyntacticMacros ["percent"] = ("Percentage", 1), }; - private static LNodeFactory F = new LNodeFactory(EmptySourceFile.Synthetic); - - [LexicalMacro("#autofree(hat) {}", "Frees the handles after using them in the body", "autofree")] - public static LNode AutoFree(LNode @operator, IMacroContext context) - { - var body = @operator.Args.Last; - - var handles = @operator.Args.Take(@operator.Args.Count - 1); - - var freeCalls = LNode.List(); - - foreach (var handle in handles) - { - freeCalls.Add(LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(handle, LNode.Id((Symbol)"Free"))).SetStyle(NodeStyle.Operator))); - } - - return body.WithArgs(LNode.List().AddRange(body.Args).AddRange(freeCalls)); - } + private static readonly LNodeFactory F = new LNodeFactory(EmptySourceFile.Synthetic); [LexicalMacro("constructor()", "Convert constructor() to .ctor() function", "#constructor", Mode = MacroMode.MatchIdentifierOrCall)] - public static LNode Constructor(LNode @operator, IMacroContext context) + public static LNode Constructor(LNode node, IMacroContext context) { - return SyntaxTree.Signature(SyntaxTree.Type(".ctor", new()), SyntaxTree.Type("none", LNode.List()), @operator.Args[0].Args, new()).PlusArg(@operator.Args[1]).WithAttrs(@operator.Attrs); + var factory = new LNodeFactory(node.Source); + SyntaxTree.Factory = factory; + + return SyntaxTree.Signature(SyntaxTree.Type(".ctor", new()), + SyntaxTree.Type("none", LNode.List()), node.Args[0].Args, + new()).PlusArg(node.Args[1]).WithAttrs(node.Attrs).WithRange(node.Range); } [LexicalMacro("destructor()", "Convert destructor() to .dtor() function", "#destructor", Mode = MacroMode.MatchIdentifierOrCall)] - public static LNode Destructor(LNode @operator, IMacroContext context) + public static LNode Destructor(LNode node, IMacroContext context) { + var factory = new LNodeFactory(node.Source); + SyntaxTree.Factory = factory; + return SyntaxTree.Signature(SyntaxTree.Type(".dtor", new()), - SyntaxTree.Type("none", LNode.List()), @operator.Args[0].Args, new()) - .PlusArg(@operator.Args[1]).WithAttrs(@operator.Attrs); + SyntaxTree.Type("none", LNode.List()), node.Args[0].Args, new()) + .PlusArg(node.Args[1]).WithAttrs(node.Attrs).WithRange(node.Range); } [LexicalMacro(".dtor()", "Convert destructor() or .dtor() to Finalize", ".dtor", Mode = MacroMode.MatchIdentifierOrCall)] @@ -70,15 +64,56 @@ public static LNode DestructorNormalisation(LNode @operator, IMacroContext conte return @operator.WithTarget(LNode.Id("Finalize")); } - [LexicalMacro("left /= right;", "Convert to left = left / something", "'/=", Mode = MacroMode.MatchIdentifierOrCall)] + [LexicalMacro("'/=", "Convert to left = left / something", "'/=", Mode = MacroMode.MatchIdentifierOrCall)] public static LNode DivEquals(LNode @operator, IMacroContext context) { return ConvertToAssignment(@operator, CodeSymbols.Div); } - [LexicalMacro("operator", "Convert to public static op_", "#fn", - Mode = MacroMode.MatchIdentifierOrCall | MacroMode.PriorityOverride)] - public static LNode ExpandOperator(LNode @operator, IMacroContext context) + [LexicalMacro("#fn", "Transform function", "#fn", + Mode = MacroMode.MatchIdentifierOrCall | MacroMode.ProcessChildrenBefore)] + public static LNode TransformFunction(LNode node, IMacroContext context) + { + node = ExpandOperator(node, context); + + node = ExpandNotnullAssertionPostfix(node, context); + + return node; + } + + private static LNode ExpandNotnullAssertionPostfix(LNode node, IMacroContext context) + { + var newBody = new LNodeList(); + var newParameters = new LNodeList(); + + if(node.ArgCount == 0) return node; + + foreach (var parameter in node[2].Args) + { + var nonNullAttribute = LNode.Id(Symbols.AssertNonNull); + + if (parameter.Attrs.Contains(nonNullAttribute)) + { + var name = parameter[1][0]; + + var throwNode = LNode.Call(CodeSymbols.Throw, LNode.List(LNode.Call(CodeSymbols.String, LNode.List(LNode.Literal($"Parameter '{name.Name}' is none"))))); + var ifBody = LNode.Call(CodeSymbols.Braces, LNode.List(throwNode)); + + newBody = newBody.Add(SyntaxTree.If(LNode.Call(CodeSymbols.Eq, LNode.List(name, SyntaxTree.None())), ifBody, LNode.Missing)); + } + + newParameters.Add(parameter.WithoutAttrNamed(nonNullAttribute.Name)); + } + + newBody = newBody.AddRange(node[3].Args); + + node = node.WithArgChanged(2, node[3].WithArgs(newParameters)); + node = node.WithArgChanged(3, LNode.Call(CodeSymbols.Braces, newBody)); + + return node; + } + + private static LNode ExpandOperator(LNode @operator, IMacroContext context) { var operatorAttribute = SyntaxTree.Factory.Id((Symbol)"#operator"); if (@operator.Attrs.Contains(operatorAttribute)) @@ -150,7 +185,7 @@ public static LNode HandleOperator(LNode @operator, IMacroContext context) LNode.List(@operator.Args[0])); } - [LexicalMacro("Point::new()", "Convert ::New To CodeSymbols.New", "'::", Mode = MacroMode.MatchIdentifierOrCall)] + [LexicalMacro("'::'", "Convert ::New To CodeSymbols.New", "'::", Mode = MacroMode.MatchIdentifierOrCall)] public static LNode Instantiation(LNode node, IMacroContext context) { if (node.Args.IsEmpty) @@ -194,6 +229,17 @@ public static LNode MinusEquals(LNode @operator, IMacroContext context) return ConvertToAssignment(@operator, CodeSymbols.Sub); } + + //ToDo: move to other stage to enable typecheck. only allowed if a type is nullable + [LexicalMacro("left !!= right;", "None check shotcut operator", "'!!=", Mode = MacroMode.MatchIdentifierOrCall)] + public static LNode NoneCheckShortcut(LNode node, IMacroContext context) + { + var condition = LNode.Call(CodeSymbols.NotEq, LNode.List(node[0], SyntaxTree.None())); + var body = LNode.List(LNode.Call(CodeSymbols.Assign, LNode.List(node[0], node[1]))); + + return SyntaxTree.If(condition, LNode.Call(CodeSymbols.Braces, body), LNode.Missing); + } + [LexicalMacro("left *= right;", "Convert to left = left * something", "'*=", Mode = MacroMode.MatchIdentifierOrCall)] public static LNode MulEquals(LNode @operator, IMacroContext context) { @@ -239,24 +285,30 @@ public static LNode InterpolateString(LNode node, IMacroContext context) string formatString = valueNode.Value.ToString(); if (formatString.Contains('$')) { - var interpolateOptions = GetInterpoltedStringOptions(formatString); var formatArgs = new List(); int counter = 0; - foreach (var item in interpolateOptions) - { - if (formatString[item.start - 1] == '\\') + + formatString = Regex.Replace(formatString, "\\$(?\\w[0-9a-zA-Z_]*)(:(\\{(?[0-9a-zA-Z_]*)\\}))?", _ => { + var sb = new StringBuilder(); + sb.Append('{').Append(counter++); + + var options = _.Groups["options"].Value; + + if (!string.IsNullOrEmpty(options)) { - continue; + sb.Append(':').Append(options); } - formatString = formatString.Replace($"{item.name}", "{" + counter++ + "}"); + sb.Append('}'); var varRange = new SourceRange(valueNode.Range.Source, - item.start + node.Range.StartIndex + 1, item.length); + _.Index + node.Range.StartIndex + 1, _.Length); - formatArgs.Add(SyntaxTree.Factory.Id(item.name[1..]).WithRange(varRange)); - } + formatArgs.Add(SyntaxTree.Factory.Id(_.Groups["name"].Value).WithRange(varRange)); + + return sb.ToString(); + }); formatArgs.Insert(0, SyntaxTree.Factory.Call(CodeSymbols.String, LNode.List(SyntaxTree.Factory.Literal(formatString)))); @@ -267,25 +319,13 @@ public static LNode InterpolateString(LNode node, IMacroContext context) return node; } - private static List<(string name, int start, int length)> GetInterpoltedStringOptions(string value) + private static LNode ConvertToAssignment(LNode node, Symbol symbol) { - var result = new List<(string name, int start, int length)>(); - - var match = Regex.Matches(value, "\\$[a-zA-Z_][0-9a-zA-Z_]*"); + var arg1 = node.Args[0]; + var arg2 = node.Args[1]; - foreach (Match m in match) - { - result.Add((m.Value, m.Index, m.Length)); - } - - return result; - } - - private static LNode ConvertToAssignment(LNode @operator, Symbol symbol) - { - var arg1 = @operator.Args[0]; - var arg2 = @operator.Args[1]; + var factory = new LNodeFactory(node.Source); - return F.Call(CodeSymbols.Assign, arg1, F.Call(symbol, arg1, arg2)); + return factory.Call(CodeSymbols.Assign, arg1, factory.Call(symbol, arg1, arg2)); } } \ No newline at end of file diff --git a/Source/Backlang.Driver/TypeDeducer.cs b/Source/Backlang.Driver/TypeDeducer.cs index 6811f559..ca72c1e9 100644 --- a/Source/Backlang.Driver/TypeDeducer.cs +++ b/Source/Backlang.Driver/TypeDeducer.cs @@ -1,5 +1,6 @@ using Backlang.Codeanalysis.Core; using Backlang.Contracts.TypeSystem; +using System.Linq.Expressions; namespace Backlang.Driver; @@ -43,18 +44,15 @@ public static IType Deduce(LNode node, Scope scope, CompilerContext context, Qua } else if (node.Calls(CodeSymbols.Typeof)) { - return context.Binder.ResolveTypes(new SimpleName("Type").Qualify("System")).First(); + return Utils.ResolveType(context.Binder, typeof(Type)); } else if (node.Calls(Symbols.Unit) && node is (_, var value, var unit)) { - var resolvedUnit = TypeInheritanceStage.ResolveTypeWithModule(unit, context, modulename); - - if (!Utils.IsUnitType(context, resolvedUnit)) - { - context.AddError(unit, $"{resolvedUnit} is not a unit type"); - } - - return new UnitType(Deduce(value, scope, context, modulename), resolvedUnit); + return DeduceUnitType(scope, context, modulename, value, unit); + } + else if (node.Calls(CodeSymbols.As) && node is (_, var expr, var castType)) + { + return Deduce(castType, scope, context, modulename); } else if (node.ArgCount == 1 && node.Calls(CodeSymbols.Default)) { @@ -94,6 +92,13 @@ public static IType Deduce(LNode node, Scope scope, CompilerContext context, Qua } else { + var type = TypeInheritanceStage.ResolveTypeWithModule(node, context, modulename); + + if (type != null) + { + return type; + } + var suggestion = LevensteinDistance.Suggest(node.Name.Name, scope.GetAllScopeNames()); context.AddError(node, $"{node.Name} cannot be resolved. Did you mean '{suggestion}'?"); @@ -103,6 +108,87 @@ public static IType Deduce(LNode node, Scope scope, CompilerContext context, Qua return null; } + public static IType DeduceFunctionReturnType(LNode funcDefinition, CompilerContext context, Scope scope, QualifiedName modulename) + { + var returnNodes = funcDefinition.Descendants().Where(_ => _.Calls(CodeSymbols.Return)).ToArray(); + + if (!returnNodes.Any()) + { + return Utils.ResolveType(context.Binder, typeof(void)); + } + + if (returnNodes.Length == 1 && returnNodes[0].ArgCount == 1) + { + return Deduce(returnNodes[0][0], scope, context, modulename); + } + + var types = returnNodes.Where(_ => _.ArgCount > 0).Select(_ => Deduce(_[0], scope, context, modulename)); + + if (!types.Any()) + { + return null; + } + + var aggregatedCommonType = types.Aggregate(FindCommonType); + + if (aggregatedCommonType != null) + { + return aggregatedCommonType; + } + + context.AddError(funcDefinition, ErrorID.DeducingTypeNotPossible); + return context.Environment.Void; + } + + private static IType FindCommonType(IType first, IType second) + { + if (first == second) + { + return first; + } + + if (ImplicitTypeCastTable.IsAssignableTo(first, second)) + { + return first; + } + if (ImplicitTypeCastTable.IsAssignableTo(second, first)) + { + return second; + } + + if (second.BaseTypes.Count > 0 && second.BaseTypes[0] == first) + { + return first; + } + + if (first.BaseTypes.Count > 0 && first.BaseTypes[0] == second) + { + return second; + } + + if (first.BaseTypes.Count > 0 && second.BaseTypes.Count > 0) + { + if (first.BaseTypes[0].FullName.ToString() == "System.ValueType" || second.BaseTypes[0].FullName.ToString() == "System.ValueType") + { + return null; + } + + if (first.BaseTypes[0].FullName.ToString() == "System.Object" || second.BaseTypes[0].FullName.ToString() == "System.Object") + { + return null; + } + + if (first.BaseTypes[0] == second.BaseTypes[0]) + { + return first.BaseTypes[0]; + } + + return FindCommonType(first.BaseTypes[0], second.BaseTypes[0]); + } + + return null; + } + public static void ExpectType(LNode node, Scope scope, CompilerContext context, QualifiedName modulename, IType expectedType) { var deducedType = Deduce(node, scope, context, modulename); @@ -125,6 +211,18 @@ public static IType NotExpectType(LNode node, Scope scope, CompilerContext conte return deducedType; } + private static IType DeduceUnitType(Scope scope, CompilerContext context, QualifiedName modulename, LNode value, LNode unit) + { + var resolvedUnit = TypeInheritanceStage.ResolveTypeWithModule(unit, context, modulename); + + if (!Utils.IsUnitType(context, resolvedUnit)) + { + context.AddError(unit, $"{resolvedUnit} is not a unit type"); + } + + return new UnitType(Deduce(value, scope, context, modulename), resolvedUnit); + } + private static IType DeduceArray(LNode node, Scope scope, CompilerContext context, QualifiedName modulename) { //ToDo: Make deducing array type better @@ -172,20 +270,20 @@ private static IType DeduceTuple(LNode node, Scope scope, CompilerContext contex return tupleType.MakeGenericType(generics); } - private static IType DeduceBinary(LNode node, Scope scope, CompilerContext context, QualifiedName modulename) + private static IType DeduceBinary(LNode node, Scope scope, CompilerContext context, QualifiedName moduleName) { if (node.Calls(CodeSymbols.Add) || node.Calls(CodeSymbols.Mul) || node.Calls(CodeSymbols.Div) || node.Calls(CodeSymbols.Sub) || node.Calls(CodeSymbols.AndBits) - || node.Calls(CodeSymbols.OrBits) || node.Calls(CodeSymbols.Xor) || node.Calls(CodeSymbols.Mod)) + || node.Calls(CodeSymbols.OrBits) || node.Calls((Symbol)"'^") || node.Calls(CodeSymbols.Mod)) { - return DeduceBinaryHelper(node, scope, context, modulename); + return DeduceBinaryHelper(node, scope, context, moduleName); } else if (node.Calls(CodeSymbols.LT) || node.Calls(CodeSymbols.GT) || node.Calls(CodeSymbols.LE) || node.Calls(CodeSymbols.GE)) { - NotExpectType(node[0], scope, context, modulename, context.Environment.Boolean); - NotExpectType(node[1], scope, context, modulename, context.Environment.Boolean); + NotExpectType(node[0], scope, context, moduleName, context.Environment.Boolean); + NotExpectType(node[1], scope, context, moduleName, context.Environment.Boolean); return context.Environment.Boolean; } @@ -195,58 +293,88 @@ private static IType DeduceBinary(LNode node, Scope scope, CompilerContext conte } else if (node.Calls(CodeSymbols.As)) { - if (TypenameTable.ContainsKey(node.Args[1].Name.Name)) - { - var typName = LNode.Id(TypenameTable[node.Args[1].Name.Name]); - return Deduce(typName, scope, context, modulename); - } - - return Deduce(node.Args[1], scope, context, modulename); + return DeduceExplicitCast(node, scope, context, moduleName); } else if (node.Calls(CodeSymbols.Dot)) { - var qualified = ConversionUtils.GetQualifiedName(node); - var resolved = context.Binder.ResolveTypes(qualified).FirstOrDefault(); - - if (resolved == null) - { - var left = Deduce(node.Args[0], scope, context, modulename); //Todo: implement deducing for members - } - - return resolved; + return DeduceMember(node, scope, context, moduleName); } else if (node.Calls(CodeSymbols.ColonColon)) { - var type = Deduce(node.Args[0], scope, context, modulename); - var fnName = node.Args[1].Name; + return DeduceStaticMethod(node, scope, context, moduleName); + } - var methods = type.Methods.Where(_ => _.Name.ToString() == fnName.ToString()); - var deducedArgs = new List(); + return null; + } - foreach (var arg in node.Args[1].Args) - { - deducedArgs.Add(Deduce(arg, scope, context, modulename)); - } + private static IType DeduceStaticMethod(LNode node, Scope scope, CompilerContext context, QualifiedName moduleName) + { + var type = Deduce(node.Args[0], scope, context, moduleName); + var fnName = node.Args[1].Name; + + var methods = type.Methods.Where(_ => _.Name.ToString() == fnName.ToString()); + var deducedArgs = new List(); - methods = methods.Where(_ => _.Parameters.Count == deducedArgs.Count); + foreach (var arg in node.Args[1].Args) + { + deducedArgs.Add(Deduce(arg, scope, context, moduleName)); + } - if (methods.Any()) + methods = methods.Where(_ => _.Parameters.Count == deducedArgs.Count); + + if (methods.Any()) + { + foreach (var method in methods) { - foreach (var method in methods) + if (!ImplementationStage.MatchesParameters(method, deducedArgs)) { - if (ImplementationStage.MatchesParameters(method, deducedArgs)) - { - return method.ReturnParameter.Type; - } + continue; } + + return method.ReturnParameter.Type; } - else + } + else + { + context.AddError(node, $"Mismatching Parameter count: {type.FullName.ToString() + "::" + fnName}()"); + } + + return null; + } + + private static IType DeduceMember(LNode node, Scope scope, CompilerContext context, QualifiedName modulename) + { + var qualified = ConversionUtils.GetQualifiedName(node); + var resolved = context.Binder.ResolveTypes(qualified).FirstOrDefault(); + + if (resolved == null) + { + var left = Deduce(node.Args[0], scope, context, modulename); + var field = left.Fields.FirstOrDefault(_ => _.Name.ToString() == qualified.Name.ToString()); + + if (field != null) { - context.AddError(node, $"Mismatching Parameter count: {type.FullName.ToString() + "::" + fnName}()"); + return field.FieldType; } + + var funcArgs = node[1].Args.Select(_ => Deduce(_, scope, context, modulename)); + var func = context.Binder.FindFunction(left.ToString() + "::" + qualified.Name + "(" + string.Join(',', funcArgs) + ")"); + + return func.ReturnParameter.Type; } - return null; + return resolved; + } + + private static IType DeduceExplicitCast(LNode node, Scope scope, CompilerContext context, QualifiedName modulename) + { + if (TypenameTable.ContainsKey(node.Args[1].Name.Name)) + { + var typName = LNode.Id(TypenameTable[node.Args[1].Name.Name]); + return Deduce(typName, scope, context, modulename); + } + + return Deduce(node.Args[1], scope, context, modulename); } private static IType DeduceUnary(LNode node, Scope scope, CompilerContext context, QualifiedName modulename) @@ -301,6 +429,15 @@ private static IType DeduceBinaryHelper(LNode node, Scope scope, CompilerContext if (left != right) //ToDo: Add implicit casting check { + if (left is UnitType && right is not UnitType) + { + return left; + } + else if (right is UnitType && left is not UnitType) + { + return right; + } + if (left.IsPointerType()) { ExpectType(node.Args[1], scope, context, modulename, context.Environment.Int32); diff --git a/Source/Backlang.Driver/Utils.cs b/Source/Backlang.Driver/Utils.cs index db1aca04..7731185e 100644 --- a/Source/Backlang.Driver/Utils.cs +++ b/Source/Backlang.Driver/Utils.cs @@ -1,4 +1,6 @@ using Backlang.Core.CompilerService; +using Furesoft.Core.CodeDom.Compiler.TypeSystem; +using System.Runtime.CompilerServices; namespace Backlang.Driver; @@ -12,29 +14,9 @@ public static FlowGraphBuilder CreateGraphBuilder() return graph; } - public static QualifiedName QualifyNamespace(string @namespace) - { - var spl = @namespace.Split('.'); - - QualifiedName? name = null; - - foreach (var path in spl) - { - if (name == null) - { - name = new SimpleName(path).Qualify(); - continue; - } - - name = new SimpleName(path).Qualify(name.Value); - } - - return name.Value; - } - public static DescribedType ResolveType(TypeResolver resolver, Type type) { - var ns = QualifyNamespace(type.Namespace); + var ns = ConversionUtils.QualifyNamespace(type.Namespace); return (DescribedType)resolver.ResolveTypes(new SimpleName(type.Name).Qualify(ns))?.FirstOrDefault(); } @@ -65,4 +47,10 @@ public static bool IsUnitType(CompilerContext context, IType resolvedUnit) return attr.Select(_ => _.AttributeType).Contains(attrType); } + + public static void AddCompilerGeneratedAttribute(TypeResolver binder, DescribedType type) { + var attributeType = ResolveType(binder, typeof(CompilerGeneratedAttribute)); + + type.AddAttribute(new DescribedAttribute(attributeType)); + } } \ No newline at end of file diff --git a/Source/Backlang.NET.Sdk/Backlang.NET.Sdk.csproj b/Source/Backlang.NET.Sdk/Backlang.NET.Sdk.csproj index 250177a1..a978dbe2 100644 --- a/Source/Backlang.NET.Sdk/Backlang.NET.Sdk.csproj +++ b/Source/Backlang.NET.Sdk/Backlang.NET.Sdk.csproj @@ -51,8 +51,8 @@ - - + + diff --git a/Source/Backlang.NET.Sdk/BuildTask.cs b/Source/Backlang.NET.Sdk/BuildTask.cs index dd15754a..7d3c36b6 100644 --- a/Source/Backlang.NET.Sdk/BuildTask.cs +++ b/Source/Backlang.NET.Sdk/BuildTask.cs @@ -81,21 +81,23 @@ public override bool Execute() } var context = new CompilerContext(); - context.InputFiles = Compile; - context.OutputFilename = OutputName; - context.OutputType = OutputType; + context.Options.InputFiles = Compile; + context.Options.OutputFilename = OutputName; + context.Options.OutputType = OutputType; + context.Options.Version = Version; + context.Options.EmbeddedResource = Resources; + context.Options.TargetFramework = TargetFramework; + context.TempOutputPath = TempOutputPath; context.OutputPath = OutputPath; context.MacroReferences = MacroReferences; context.ResultingOutputPath = ResultingOutputPath; context.ProjectFile = ProjectFile; - context.EmbeddedResource = Resources; context.CorLib = CorLib; - context.Version = Version; if (!string.IsNullOrEmpty(OutputTree)) { - context.OutputTree = bool.Parse(OutputTree); + context.Options.OutputTree = bool.Parse(OutputTree); } CompilerDriver.Compile(context); diff --git a/Source/Backlang.NET.Sdk/build/Backlang.Version.props b/Source/Backlang.NET.Sdk/build/Backlang.Version.props index 595c7f6f..224618b2 100644 --- a/Source/Backlang.NET.Sdk/build/Backlang.Version.props +++ b/Source/Backlang.NET.Sdk/build/Backlang.Version.props @@ -1,5 +1,5 @@ -1.0.80 +1.0.83 diff --git a/Source/Backlang.WasmBridge/Backlang.WasmBridge.csproj b/Source/Backlang.WasmBridge/Backlang.WasmBridge.csproj new file mode 100644 index 00000000..c3559195 --- /dev/null +++ b/Source/Backlang.WasmBridge/Backlang.WasmBridge.csproj @@ -0,0 +1,16 @@ + + + net7.0 + browser-wasm + main.mjs + Exe + true + true + preview + True + + + + + + diff --git a/Source/Backlang.WasmBridge/Bridge.cs b/Source/Backlang.WasmBridge/Bridge.cs new file mode 100644 index 00000000..d04f0f46 --- /dev/null +++ b/Source/Backlang.WasmBridge/Bridge.cs @@ -0,0 +1,34 @@ +using Backlang.Contracts; +using Backlang.Driver; +using System; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices.JavaScript; +using System.Text; + +public partial class Bridge +{ + [JSExport] + public static string CompileAndRun(string src) + { + var context = new CompilerContext(); + context.Playground = new() { IsPlayground = true, Source = src }; + + var assemblyStream = new MemoryStream(); + + context.OutputStream = assemblyStream; + + var output = new MemoryStream(); + var sw = new StreamWriter(output); + Console.SetOut(sw); + + CompilerDriver.Compile(context); + + assemblyStream.Seek(0, SeekOrigin.Begin); + + //var assembly = Assembly.Load(assemblyStream.ToArray()); + //assembly.EntryPoint.Invoke(null, Array.Empty()); + + return Encoding.UTF8.GetString(assemblyStream.ToArray()); + } +} \ No newline at end of file diff --git a/Source/Backlang.WasmBridge/Program.cs b/Source/Backlang.WasmBridge/Program.cs new file mode 100644 index 00000000..bbf14fc1 --- /dev/null +++ b/Source/Backlang.WasmBridge/Program.cs @@ -0,0 +1,6 @@ +public static class Program +{ + public static void Main() + { + } +} \ No newline at end of file diff --git a/Source/Backlang.WasmBridge/Properties/launchSettings.json b/Source/Backlang.WasmBridge/Properties/launchSettings.json new file mode 100644 index 00000000..cd2fb77a --- /dev/null +++ b/Source/Backlang.WasmBridge/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "Backlang.WasmBridge": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:57539;http://localhost:57540" + } + } +} \ No newline at end of file diff --git a/Source/Backlang.WasmBridge/main.mjs b/Source/Backlang.WasmBridge/main.mjs new file mode 100644 index 00000000..409a9d64 --- /dev/null +++ b/Source/Backlang.WasmBridge/main.mjs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import { dotnet } from './dotnet.js' + +const { setModuleImports, getAssemblyExports, getConfig } = await dotnet + .withDiagnosticTracing(false) + .create(); + +setModuleImports('main.mjs', { + +}); + +const config = getConfig(); +const exports = await getAssemblyExports(config.mainAssemblyName); + +const result = exports.Bridge.CompileAndRun("func main() { print(\"Hello Wasm\"); }"); +console.log("result" + result); + +await dotnet.run(); diff --git a/Source/Backlang.WasmBridge/runtimeconfig.template.json b/Source/Backlang.WasmBridge/runtimeconfig.template.json new file mode 100644 index 00000000..4fac1499 --- /dev/null +++ b/Source/Backlang.WasmBridge/runtimeconfig.template.json @@ -0,0 +1,16 @@ +{ + "wasmHostProperties": { + "perHostConfig": [ + { + "name": "node", + "js-path": "main.mjs", + "Host": "nodejs" + }, + { + "name": "v8", + "js-path": "main.mjs", + "Host": "v8" + } + ] + } +} diff --git a/Source/Plugins/Backlang.Backends.Bs2k/BS2KTarget.cs b/Source/Plugins/Backlang.Backends.Bs2k/BS2KTarget.cs index dc68fdd8..8f31bcbb 100644 --- a/Source/Plugins/Backlang.Backends.Bs2k/BS2KTarget.cs +++ b/Source/Plugins/Backlang.Backends.Bs2k/BS2KTarget.cs @@ -18,7 +18,7 @@ public void AfterCompiling(CompilerContext context) public void BeforeCompiling(CompilerContext context) { - context.OutputFilename += ".bsm"; + context.Options.OutputFilename += ".bsm"; } public void BeforeExpandMacros(MacroProcessor processor) diff --git a/Source/Std/Keyboard.back b/Source/Std/Keyboard.back deleted file mode 100644 index ddbc2d44..00000000 --- a/Source/Std/Keyboard.back +++ /dev/null @@ -1,77 +0,0 @@ -module Keyboard; - -public static func isKeyDown(key : T) -> bool - where T : KeyCode, u8 { - - inline(b2k) { - mov(R0, key); - GetKeyState(R1, R0); - push(R1); - } -} - -public enum KeyCode { - Backspace = 8, - Space = 0x20, - Key0 = 48, - Key1, - Key2, - Key3, - Key4, - Key5, - Key6, - Key7, - Key8, - Key9, - A = 65, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, - M, - N, - O, - P, - Q, - R, - S, - T, - U, - V, - W, - X, - Y, - Z, - a = 97, - b, - c, - d, - f, - g, - h, - i, - j, - k, - l, - m, - n, - o, - p, - q, - r, - s, - t, - u, - v, - w, - x, - y, - z -} \ No newline at end of file diff --git a/Source/Std/Limits.back b/Source/Std/Limits.back deleted file mode 100644 index cadc64d2..00000000 --- a/Source/Std/Limits.back +++ /dev/null @@ -1,15 +0,0 @@ -implement u8 { - const Max : u8 = 0xFF; -} - -implement u16 { - const Max : u16 = 0xFF_FF; -} - -implement u32 { - const Max : u32 = 0xFF_FF_FF_FF; -} - -implement u8..u32 { - const Min : SELF = 0; -} \ No newline at end of file diff --git a/Source/Std/LinkedList.back b/Source/Std/LinkedList.back deleted file mode 100644 index 5213169e..00000000 --- a/Source/Std/LinkedList.back +++ /dev/null @@ -1,84 +0,0 @@ -struct LinkedListNode { - Next : LinkedListNode*; - Value : T; -} - -public struct LinkedList { - public let Root : LinkedListNode; - public let Count : u32; -} - -implement LinkedList -{ - public func add(value : T) { - if Root == none { - Root = LinkedListNode::new(value); - } - else { - let lastNode = getLastNode(); - - lastNode.Next = LinkedListNode::new(value); - } - - Count += 1; - } - - func getLastNode() { - let tmp = Root; - - while tmp.Next != none { - tmp = tmp.Next; - } - - return &tmp; - } - - func getNodeAtIndex(index : u32) { - assert(index < Count, "Index is out of bound"); - - let tmp = Root; - - for i in 0..(index - 1) { - tmp = tmp.Next; - } - - return &tmp; - } - - func remove(value : T) { - let tmp = Root; - - if tmp.Value == value { - Root = *tmp.Next; - } - - while tmp.Next != none { - if (*tmp.Next).Value == value { - let nextValidNode = (*tmp.Next).Next; - - LinkedListNode::Free(tmp.Next); - - tmp.Next = nextValidNode; - - return; - } - else { - tmp = tmp.Next; - } - } - - Count -= 1; - - error("No Value found to delete"); - } - - operator func index_get(index : u32) { - return (*getNodeAtIndex(index)).Value; - } - - operator func index_set(index : u32, value : T) { - let node = *getNodeAtIndex(index); - - node.Value = value; - } -} \ No newline at end of file diff --git a/Source/Std/Math.back b/Source/Std/Math.back deleted file mode 100644 index e95b1649..00000000 --- a/Source/Std/Math.back +++ /dev/null @@ -1,47 +0,0 @@ -module Math; - -const PI = 3.1415926535897931; -const E = 2.7182818284590451; - -public func pow(base : T, exponent : T) - where T : i8..i32, u8..u32 -{ - let mut result = 1; - - for i in 0..exponent { - result *= base; - } - - return result; -} - -public func min(x : T, y : T) - where T : u8..u32, i8..i32 -{ - if x < y { - return x; - } - - return y; -} - -public func max(x : T, y : T) - where T : u8..u32, i8..i32 -{ - if x > y { - return y; - } - - return x; -} - -public func abs(value : T) - where T : i8..i32 -{ - if value < 0 { - return value * -1; - } - - return value; -} - diff --git a/Source/Std/Memory.back b/Source/Std/Memory.back deleted file mode 100644 index 8a9bf66e..00000000 --- a/Source/Std/Memory.back +++ /dev/null @@ -1,32 +0,0 @@ -module Memory; - -public static func clear(start : u32*, end : u32*) { - Memory::set(start, end, 0); -} - -public static func set(start : u32*, end : u32*, value : u32) { - if end < start { - return error(nameof(end) + " must be greater than " + start); - } - - for i in 0..end) { - *start = value; - start += 1; - } -} - -public static func copy(src : u32*, dest : u32*) { - *dest = *src; -} - -public static func copy(src : T[], dst : T[]) - where T : u8..u32, i8, i32 -{ - if src.length != dst.length { - error(nameof(src) + " has to have same length as " + nameof(dst)); - } - - for i in 0..src.length { - dst[i] = src[i]; - } -} \ No newline at end of file diff --git a/Source/Std/Numbers.back b/Source/Std/Numbers.back deleted file mode 100644 index 59706122..00000000 --- a/Source/Std/Numbers.back +++ /dev/null @@ -1,32 +0,0 @@ -import Math; - -implement u8..u32 { - public static func parse(str : string) -> SELF { - let mut result = 0; - - for c in str.length..0 { - if c >= "0" and c <= "9" { - result += ( c - "0") * Math::pow(10, i); - } - } - - if result > Max { - error("Value is not in range"); - } - - return result; - } -} - -implement bool { - public static func parse(str : string) { - let condition = str == "true" or str == "false"; - - if condition { - return condition; - } - else { - error("invalid input for bool"); - } - } -} \ No newline at end of file diff --git a/Source/Std/Terminal.back b/Source/Std/Terminal.back deleted file mode 100644 index 3e870141..00000000 --- a/Source/Std/Terminal.back +++ /dev/null @@ -1,68 +0,0 @@ -import Memory; -import Limits; -import Keyboard; - -struct Terminal { - End = Width * Height; - Width = 80; - Height = 25; - - CursorX = 0; - CursorY = 0; -} - -implement Terminal { - public static func write(char : i8) { - let address : u32* = CursorY * Width + CursorX; - - *address = char; - - CursorX += 1; - - if CursorX = Width { - CursorX = 0; - CursorY++; - } - if CursorY == Height { - CursorY = 0; - CursorX = 0; - } - } - - public static func write(str : string) { - for c in str) { - write(c); - } - } - - public static func writeln(str : string) { - write(str); - - CursorY += 1; - CursorX = 0; - } - - public static func readChar() { - for i in 0..u8::Max { - if Keyboard::isKeyDown(i) { - return i; - } - } - } - - public static func clear() { - Memory::clear(0, End); - - CursorX = 0; - CursorY = 0; - } - - public static func setCursorPosition(top : u32, left : u32) { - CursorX = left; - CursorY = top; - } - - public static func getCursorPosition() { - return Tuple::new(CursorX, CursorY); - } -} \ No newline at end of file diff --git a/Source/Std/Tuple.back b/Source/Std/Tuple.back deleted file mode 100644 index bc42af11..00000000 --- a/Source/Std/Tuple.back +++ /dev/null @@ -1,11 +0,0 @@ -struct Tuple { - Item1 : T; - Item2 : U; -} - -implement Tuple { - public constructor(item1 : T, item2 : U) { - Item1 = item1; - Item2 = item2; - } -} \ No newline at end of file diff --git a/Source/Std/Video/Color.back b/Source/Std/Video/Color.back deleted file mode 100644 index 95ee4e7f..00000000 --- a/Source/Std/Video/Color.back +++ /dev/null @@ -1,11 +0,0 @@ -public struct Color { - public let Value : u32; -} - -implement Color { - - public constructor(r : u8, g : u8, b : u8, a : u8) { - - } - -} \ No newline at end of file diff --git a/Source/Std/Video/VideoMemory.back b/Source/Std/Video/VideoMemory.back deleted file mode 100644 index aef8acab..00000000 --- a/Source/Std/Video/VideoMemory.back +++ /dev/null @@ -1,32 +0,0 @@ -import Color; - -struct VideoMemory { - let FramebufferAddress : i8; - - const Width = 480; - const Height = Width / 4 * 3; -} - -implement VideoMemory { - - public constructor() { - inline(bs2k) { - InvFbAddr(R1); - mov(&FramebufferAddress, R1); - } - } - - public func swap() { - inline(bs2K) { - Swap(); - InvFbAddr(R1); - mov(&FramebufferAddress, R1); - } - } - - public func setPixel(x : u32, y : u32, color : Color) { - let address : u32* = y * VideoMemory::Width + x; - - *address = color.value; - } -} \ No newline at end of file diff --git a/Source/TestProject1/TestProject1.csproj b/Source/TestProject1/TestProject1.csproj index 6d65c34f..8c7f8bef 100644 --- a/Source/TestProject1/TestProject1.csproj +++ b/Source/TestProject1/TestProject1.csproj @@ -10,7 +10,7 @@ - + diff --git a/Source/Version.props b/Source/Version.props index 1f763704..ef52452b 100644 --- a/Source/Version.props +++ b/Source/Version.props @@ -1,5 +1,5 @@  - 1.0.80 + 1.0.83 \ No newline at end of file diff --git a/Source/Backlang.Driver/Resources/compilation.runtimeconfig.json b/compilation.runtimeconfig.json similarity index 59% rename from Source/Backlang.Driver/Resources/compilation.runtimeconfig.json rename to compilation.runtimeconfig.json index 46de2091..a01ee703 100644 --- a/Source/Backlang.Driver/Resources/compilation.runtimeconfig.json +++ b/compilation.runtimeconfig.json @@ -1,9 +1,9 @@ { "runtimeOptions": { - "tfm": "net7.0", + "tfm": null, "framework": { "name": "Microsoft.NETCore.App", - "version": "7.0.0-preview.2.22152.2" + "version": "7.0.0" } } } \ No newline at end of file