├── source ├── WebIde │ ├── wwwroot │ │ ├── .nojekyll │ │ ├── css │ │ │ └── open-iconic │ │ │ │ ├── font │ │ │ │ └── fonts │ │ │ │ │ ├── open-iconic.eot │ │ │ │ │ ├── open-iconic.otf │ │ │ │ │ ├── open-iconic.ttf │ │ │ │ │ └── open-iconic.woff │ │ │ │ └── ICON-LICENSE │ │ ├── 404.html │ │ └── index.html │ ├── Backend │ │ ├── IRequiresAsyncLoad.cs │ │ ├── WasmMetadataReferenceProvider.cs │ │ └── EditorEngine.cs │ ├── LinkerConfig.xml │ ├── App.razor │ ├── _Imports.razor │ ├── Properties │ │ └── launchSettings.json │ ├── WebIde.csproj │ └── Program.cs ├── flc.Tests.Integration │ ├── Data │ │ └── FluentLang.Runtime.dll │ ├── flc.Tests.Integration.csproj │ └── FileAssemblyLoaderTests.cs ├── Compiler │ ├── IVTs.cs │ ├── Compilation │ │ ├── CompilationResultStatus.cs │ │ ├── IAssemblyCompiler.cs │ │ ├── CompilationResult.cs │ │ └── AssemblyCompiler.cs │ ├── Symbols │ │ ├── Interfaces │ │ │ ├── MethodBody │ │ │ │ ├── ILocal.cs │ │ │ │ ├── IParameterLocal.cs │ │ │ │ ├── IDeclaredLocal.cs │ │ │ │ ├── IObjectPatch.cs │ │ │ │ ├── IExpression.cs │ │ │ │ ├── IStatement.cs │ │ │ │ ├── IInvocationExpression.cs │ │ │ │ ├── Operator.cs │ │ │ │ ├── INewObjectExpression.cs │ │ │ │ ├── ILiteralExpression.cs │ │ │ │ ├── IMixinPatch.cs │ │ │ │ ├── IReturnStatement.cs │ │ │ │ ├── ILocalReferenceExpression.cs │ │ │ │ ├── IPrefixUnaryOperatorExpression.cs │ │ │ │ ├── IBinaryOperatorExpression.cs │ │ │ │ ├── IConditionalExpression.cs │ │ │ │ ├── IMethodPatch.cs │ │ │ │ ├── IMemberInvocationExpression.cs │ │ │ │ ├── IMatchExpression.cs │ │ │ │ ├── IMatchExpressionArm.cs │ │ │ │ ├── IDeclarationStatement.cs │ │ │ │ ├── IObjectPatchingExpression.cs │ │ │ │ └── IStaticInvocationExpression.cs │ │ │ ├── ISymbol.cs │ │ │ ├── IDocument.cs │ │ │ ├── IType.cs │ │ │ ├── IParameter.cs │ │ │ ├── ITypeParameter.cs │ │ │ ├── IInterfaceMethod.cs │ │ │ ├── IUnion.cs │ │ │ ├── IAssembly.cs │ │ │ ├── Version.cs │ │ │ └── IMethod.cs │ │ ├── ErrorSymbols │ │ │ ├── IErrorType.cs │ │ │ ├── ErrorParameter.cs │ │ │ ├── ErrorInterfaceMethod.cs │ │ │ ├── ErrorType.cs │ │ │ ├── ErrorInterface.cs │ │ │ └── ErrorMethod.cs │ │ ├── Visitor │ │ │ ├── IVisitableSymbol.cs │ │ │ ├── SymbolExtensions.cs │ │ │ └── ISymbolVisitor.cs │ │ ├── DocumentFactory.cs │ │ ├── Source │ │ │ ├── MethodBody │ │ │ │ ├── ParameterLocal.cs │ │ │ │ ├── DeclaredLocal.cs │ │ │ │ ├── NewObjectExpression.cs │ │ │ │ ├── EmptyInterface.cs │ │ │ │ ├── MethodBodySymbolContextExtensions.cs │ │ │ │ ├── ReturnStatement.cs │ │ │ │ ├── MethodBodySymbolContext.cs │ │ │ │ ├── MixinPatch.cs │ │ │ │ ├── MethodPatch.cs │ │ │ │ ├── LocalReferenceExpression.cs │ │ │ │ ├── CharParser.cs │ │ │ │ ├── StaticInvocationExpression.cs │ │ │ │ ├── StaticInvocationMethodBinder.cs │ │ │ │ ├── PrefixUnaryOperatorExpression.cs │ │ │ │ └── MatchExpressionArm.cs │ │ │ ├── SymbolBase.cs │ │ │ ├── SourceParameter.cs │ │ │ ├── SourceSymbolContext.cs │ │ │ ├── SourceDocument.cs │ │ │ ├── SourceUnion.cs │ │ │ ├── SourceTypeParameter.cs │ │ │ ├── SourceInterfaceMethod.cs │ │ │ └── SourceNamedInterface.cs │ │ ├── SymbolHelpers.cs │ │ ├── Substituted │ │ │ ├── SubstitutedParameter.cs │ │ │ ├── SubstitutedUnion.cs │ │ │ ├── ConstructedInterface.cs │ │ │ ├── SubstitutedInterfaceMethod.cs │ │ │ └── SubstitutedInterface.cs │ │ ├── Metadata │ │ │ ├── Utils.cs │ │ │ └── MetadataNamedInterface.cs │ │ ├── AssemblyFactory.cs │ │ ├── QualifiedName.cs │ │ └── Primitive.cs │ ├── Diagnostics │ │ ├── IDiagnosticFormatter.cs │ │ ├── Diagnostic.cs │ │ ├── DiagnosticFormatter.cs │ │ ├── ErrorCode.cs │ │ ├── DiagnosticBag.cs │ │ └── Location.cs │ ├── Emit │ │ ├── IMetadataReferenceProvider.cs │ │ ├── Utils.cs │ │ ├── CSharpNameEscaper.cs │ │ ├── DictionaryExtensions.cs │ │ ├── FluentlangToCSharpEmitter.MethodKeyOrParamName.cs │ │ └── LocalSystemDllsMetadataReferenceProvider.cs │ ├── GlobalSuppressions.cs │ ├── Parsing │ │ ├── DiagnosticErrorListener.cs │ │ └── ParserFactory.cs │ ├── Compiler.csproj │ └── Generated │ │ ├── FluentLangLexer.tokens │ │ └── FluentLangParser.tokens ├── nuget.config ├── Shared │ ├── SpanLinq.cs │ ├── ArrayExtensions.cs │ ├── StreamExtensions.cs │ ├── AsyncEnumerableExtensions.cs │ ├── Shared.csproj │ ├── ImmutableArrayExtensions.cs │ ├── Release.cs │ └── ImmutableArrayDictionary.cs ├── Runtime │ ├── Metadata │ │ ├── AssemblyNameAttribute.cs │ │ ├── DependencyAttribute.cs │ │ ├── MethodSignatureAttribute.cs │ │ └── InterfaceAttribute.cs │ ├── MethodKey.cs │ ├── Union.cs │ ├── Runtime.csproj │ └── FLObject.cs ├── flc │ ├── DependencyLoading │ │ ├── IAssemblyLoader.cs │ │ ├── AssemblyLoadResult.cs │ │ ├── IProjectLoader.cs │ │ ├── DependencyAttributeReader.cs │ │ ├── IDependencyLoader.cs │ │ ├── Dependency.cs │ │ ├── GlobalPackagesFolderProvider.cs │ │ └── FileAssemblyLoader.cs │ ├── DependencyInjection │ │ ├── FlcContainer.cs │ │ └── FlcModule.cs │ ├── ProjectSystem │ │ ├── Version.cs │ │ ├── SolutionInfo.cs │ │ ├── Reference.cs │ │ ├── ProjectInfo.cs │ │ └── SolutionFactory.cs │ ├── FlcException.cs │ ├── flc.csproj │ ├── Testing │ │ ├── TestRunner.cs │ │ └── TestResult.cs │ └── Program.cs ├── TestUtils │ ├── TestUtils.csproj │ ├── WorkItemAttribute.cs │ ├── TestInterface.cs │ ├── XunitLogger.cs │ ├── TextToken.cs │ └── TestBase.cs ├── Compiler.Tests.Unit │ ├── Parsing │ │ └── ParserErrorTests.cs │ ├── Compiler.Tests.Unit.csproj │ ├── Symbols │ │ ├── SourceSymbols │ │ │ ├── MethodBodyTests │ │ │ │ ├── NewObjectExpressionTests.cs │ │ │ │ ├── PipedStaticInvocationExpressionTests.cs │ │ │ │ └── ReturnStatementTests.cs │ │ │ └── SourceUnionTests.cs │ │ ├── Interfaces │ │ │ └── VersionTests.cs │ │ ├── Subtyping │ │ │ └── PrimitiveTests.cs │ │ └── Equivalence │ │ │ └── PrimitiveTests.cs │ └── Emit │ │ ├── MetadataTests.cs │ │ └── RegressionTests.cs └── flc.Tests.Unit │ ├── flc.Tests.Unit.csproj │ ├── DependencyInjectionTests.cs │ ├── DependencyLoading │ └── DependencyAttributeReaderTests.cs │ └── ProjectSystem │ └── ProjectDependencyOrganizerTests.cs ├── .gitignore └── LICENSE /source/WebIde/wwwroot/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/fluentlang/HEAD/.gitignore -------------------------------------------------------------------------------- /source/flc.Tests.Integration/Data/FluentLang.Runtime.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/fluentlang/HEAD/source/flc.Tests.Integration/Data/FluentLang.Runtime.dll -------------------------------------------------------------------------------- /source/WebIde/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/fluentlang/HEAD/source/WebIde/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /source/WebIde/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/fluentlang/HEAD/source/WebIde/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /source/WebIde/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/fluentlang/HEAD/source/WebIde/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /source/WebIde/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/fluentlang/HEAD/source/WebIde/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /source/Compiler/IVTs.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("Compiler.Tests.Unit")] 4 | [assembly: InternalsVisibleTo("flc.Tests.Unit")] 5 | [assembly: InternalsVisibleTo("TestUtils")] -------------------------------------------------------------------------------- /source/WebIde/Backend/IRequiresAsyncLoad.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace FluentLang.WebIde.Backend 4 | { 5 | public interface IRequiresAsyncInitialize 6 | { 7 | public ValueTask InitializeAsync(); 8 | } 9 | } -------------------------------------------------------------------------------- /source/Compiler/Compilation/CompilationResultStatus.cs: -------------------------------------------------------------------------------- 1 | namespace FluentLang.Compiler.Compilation 2 | { 3 | public enum CompilationResultStatus 4 | { 5 | CodeErrors, 6 | InternalErrors, 7 | Succeeded, 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/ILocal.cs: -------------------------------------------------------------------------------- 1 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 2 | { 3 | public interface ILocal 4 | { 5 | public string Identifier { get; } 6 | public IType Type { get; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/IParameterLocal.cs: -------------------------------------------------------------------------------- 1 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 2 | { 3 | public interface IParameterLocal : ILocal 4 | { 5 | public IParameter Parameter { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/IDeclaredLocal.cs: -------------------------------------------------------------------------------- 1 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 2 | { 3 | public interface IDeclaredLocal : ILocal 4 | { 5 | public IDeclarationStatement Declaration { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/IObjectPatch.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Visitor; 2 | 3 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 4 | { 5 | public interface IObjectPatch : IVisitableSymbol 6 | { 7 | 8 | } 9 | } -------------------------------------------------------------------------------- /source/nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /source/Compiler/Diagnostics/IDiagnosticFormatter.cs: -------------------------------------------------------------------------------- 1 | namespace FluentLang.Compiler.Diagnostics 2 | { 3 | public interface IDiagnosticFormatter 4 | { 5 | string CreateDiagnosticMessage(Diagnostic diagnostic); 6 | string CreateLocationMessage(Diagnostic diagnostic); 7 | } 8 | } -------------------------------------------------------------------------------- /source/Compiler/Symbols/ErrorSymbols/IErrorType.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Interfaces; 2 | using System.Collections.Generic; 3 | 4 | namespace FluentLang.Compiler.Symbols.ErrorSymbols 5 | { 6 | public interface IErrorSymbol : ISymbol 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/IExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Visitor; 2 | 3 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 4 | { 5 | public interface IExpression : IVisitableSymbol 6 | { 7 | public IType Type { get; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/IStatement.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Visitor; 2 | 3 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 4 | { 5 | public interface IStatement : IVisitableSymbol 6 | { 7 | int OrdinalPositionInMethod { get; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /source/WebIde/LinkerConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /source/Compiler/Emit/IMetadataReferenceProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using System.Collections.Immutable; 3 | 4 | namespace FluentLang.Compiler.Emit 5 | { 6 | public interface IMetadataReferenceProvider 7 | { 8 | public ImmutableArray MetadataReferences { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/IInvocationExpression.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | 3 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 4 | { 5 | public interface IInvocationExpression : IExpression 6 | { 7 | public ImmutableArray Arguments { get; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /source/Compiler/Emit/Utils.cs: -------------------------------------------------------------------------------- 1 | namespace FluentLang.Compiler.Emit 2 | { 3 | internal static class Utils 4 | { 5 | private const string ASSEMBLY_LEVEL_METHODS = "_AssemblyLevelMethods"; 6 | public static string GetAssemblyLevelMethodsClassName(string assemblyName) 7 | => assemblyName + ASSEMBLY_LEVEL_METHODS; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /source/Shared/SpanLinq.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentLang.Shared 4 | { 5 | public static class SpanLinq 6 | { 7 | public static bool Any(this ReadOnlySpan span, Func func) 8 | { 9 | foreach (var t in span) 10 | { 11 | if (func(t)) 12 | return true; 13 | } 14 | return false; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Visitor/IVisitableSymbol.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Interfaces; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace FluentLang.Compiler.Symbols.Visitor 7 | { 8 | public interface IVisitableSymbol : ISymbol 9 | { 10 | public T Visit(ISymbolVisitor visitor); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/DocumentFactory.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Interfaces; 2 | using FluentLang.Compiler.Symbols.Source; 3 | 4 | namespace FluentLang.Compiler.Symbols 5 | { 6 | public static class DocumentFactory 7 | { 8 | public static IDocument FromString(string source) 9 | { 10 | return new SourceDocument(source); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/ISymbol.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using System.Collections.Immutable; 3 | 4 | namespace FluentLang.Compiler.Symbols.Interfaces 5 | { 6 | public interface ISymbol 7 | { 8 | internal void EnsureAllLocalDiagnosticsCollected(); 9 | 10 | public ImmutableArray AllDiagnostics { get; } 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /source/Shared/ArrayExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace FluentLang.Shared 5 | { 6 | public static class ArrayExtensions 7 | { 8 | public static ImmutableArray UnsafeAsImmutableArray(this T[] array) 9 | { 10 | return Unsafe.As>(ref array); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/Operator.cs: -------------------------------------------------------------------------------- 1 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 2 | { 3 | public enum Operator 4 | { 5 | Plus, 6 | Minus, 7 | Multiply, 8 | Divide, 9 | Remainder, 10 | LessThan, 11 | GreaterThan, 12 | Equal, 13 | NotEqual, 14 | LessThanOrEqualTo, 15 | GreaterThanOrEqualTo, 16 | And, 17 | Or, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /source/Runtime/Metadata/AssemblyNameAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentLang.Runtime.Metadata 4 | { 5 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] 6 | public class AssemblyNameAttribute : Attribute 7 | { 8 | public AssemblyNameAttribute(string name) 9 | { 10 | Name = name; 11 | } 12 | 13 | public string Name { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /source/WebIde/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Sorry, there's nothing at this address.

8 |
9 |
10 |
-------------------------------------------------------------------------------- /source/WebIde/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 7 | @using Microsoft.JSInterop 8 | @using WebIde 9 | @using WebIde.Shared 10 | @using WebIde.Backend 11 | -------------------------------------------------------------------------------- /source/Shared/StreamExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | using System.IO; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace FluentLang.Shared 6 | { 7 | public static class StreamExtensions 8 | { 9 | public static ImmutableArray ToImmutableArray(this MemoryStream stream) 10 | { 11 | return stream.ToArray().UnsafeAsImmutableArray(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/INewObjectExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Visitor; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 5 | { 6 | public interface INewObjectExpression : IExpression 7 | { 8 | [return: MaybeNull] 9 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 10 | => visitor.Visit(this); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/ILiteralExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Visitor; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 5 | { 6 | public interface ILiteralExpression : IExpression 7 | { 8 | [return: MaybeNull] 9 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 10 | => visitor.Visit(this); 11 | object? Value { get; } 12 | } 13 | } -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/IMixinPatch.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Visitor; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 5 | { 6 | public interface IMixinPatch : IObjectPatch 7 | { 8 | [return: MaybeNull] 9 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 10 | => visitor.Visit(this); 11 | 12 | IExpression Expression { get; } 13 | } 14 | } -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/IReturnStatement.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Visitor; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 5 | { 6 | public interface IReturnStatement : IStatement 7 | { 8 | [return: MaybeNull] 9 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 10 | => visitor.Visit(this); 11 | IExpression Expression { get; } 12 | } 13 | } -------------------------------------------------------------------------------- /source/flc/DependencyLoading/IAssemblyLoader.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.Loader; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace FluentLang.flc.DependencyLoading 7 | { 8 | public interface IAssemblyLoader 9 | { 10 | ValueTask TryLoadAssemblyAsync(AssemblyLoadContext assemblyLoadContext, Dependency dependency, CancellationToken cancellationToken = default); 11 | } 12 | } -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/IDocument.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using System.Collections.Immutable; 3 | using static FluentLang.Compiler.Generated.FluentLangParser; 4 | 5 | namespace FluentLang.Compiler.Symbols.Interfaces 6 | { 7 | public interface IDocument 8 | { 9 | public string FullName { get; } 10 | public Compilation_unitContext SyntaxTree { get; } 11 | public ImmutableArray Diagnostics { get; } 12 | } 13 | } -------------------------------------------------------------------------------- /source/Runtime/Metadata/DependencyAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentLang.Runtime.Metadata 4 | { 5 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 6 | public class DependencyAttribute : Attribute 7 | { 8 | public DependencyAttribute(string name, string version) 9 | { 10 | Name = name; 11 | Version = version; 12 | } 13 | 14 | public string Name { get; } 15 | public string Version { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /source/flc/DependencyInjection/FlcContainer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using StrongInject; 3 | 4 | namespace FluentLang.flc.DependencyInjection 5 | { 6 | [RegisterModule(typeof(FlcModule))] 7 | public partial class FlcContainer : IAsyncContainer 8 | { 9 | [Instance] private readonly LogLevel _logLevel; 10 | 11 | public FlcContainer(LogLevel logLevel) 12 | { 13 | _logLevel = logLevel; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /source/flc/ProjectSystem/Version.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace FluentLang.flc.ProjectSystem 5 | { 6 | public class Version 7 | { 8 | [JsonConstructor] 9 | public Version(int major, int minor, string? suffix = null) 10 | { 11 | Major = major; 12 | Minor = minor; 13 | Suffix = suffix; 14 | } 15 | 16 | public int Major { get; } 17 | public int Minor { get; } 18 | public string? Suffix { get; } 19 | } 20 | } -------------------------------------------------------------------------------- /source/Compiler/Emit/CSharpNameEscaper.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols; 2 | 3 | namespace FluentLang.Compiler.Emit 4 | { 5 | public static class CSharpNameEscaper 6 | { 7 | public static string Escape(QualifiedName name) => 8 | name.ToString().Replace("_", "__").Replace(".", "_dot_"); 9 | public static QualifiedName Unescape(string name) => 10 | QualifiedName.Parse(name.Replace("__", "$").Replace("_dot_", ".").Replace("$", "_")); 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /source/flc/DependencyLoading/AssemblyLoadResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | using System.Reflection; 3 | 4 | namespace FluentLang.flc.DependencyLoading 5 | { 6 | public class AssemblyLoadResult 7 | { 8 | public AssemblyLoadResult(Assembly assembly, ImmutableArray bytes) 9 | { 10 | Assembly = assembly; 11 | Bytes = bytes; 12 | } 13 | 14 | public Assembly Assembly { get; } 15 | 16 | public ImmutableArray Bytes { get; } 17 | } 18 | } -------------------------------------------------------------------------------- /source/Compiler/Compilation/IAssemblyCompiler.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Interfaces; 2 | using System.IO; 3 | using System.Threading; 4 | 5 | namespace FluentLang.Compiler.Compilation 6 | { 7 | public interface IAssemblyCompiler 8 | { 9 | CompilationResult CompileAssembly( 10 | IAssembly assembly, 11 | Stream outputStream, 12 | Stream? csharpOutputStream = null, 13 | Stream? pdbStream = null, 14 | CancellationToken cancellationToken = default); 15 | } 16 | } -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/ILocalReferenceExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Visitor; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 5 | { 6 | public interface ILocalReferenceExpression : IExpression 7 | { 8 | [return: MaybeNull] 9 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 10 | => visitor.Visit(this); 11 | string Identifier { get; } 12 | ILocal Local { get; } 13 | } 14 | } -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/IPrefixUnaryOperatorExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Visitor; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 5 | { 6 | public interface IPrefixUnaryOperatorExpression : IExpression 7 | { 8 | [return: MaybeNull] 9 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 10 | => visitor.Visit(this); 11 | Operator Operator { get; } 12 | IExpression Expression { get; } 13 | } 14 | } -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/IBinaryOperatorExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Visitor; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 5 | { 6 | public interface IBinaryOperatorExpression : IExpression 7 | { 8 | [return: MaybeNull] 9 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 10 | => visitor.Visit(this); 11 | 12 | IExpression Left { get; } 13 | Operator Operator { get; } 14 | IExpression Right { get; } 15 | } 16 | } -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/IConditionalExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Visitor; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 5 | { 6 | public interface IConditionalExpression : IExpression 7 | { 8 | [return: MaybeNull] 9 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 10 | => visitor.Visit(this); 11 | IExpression Condition { get; } 12 | IExpression IfFalse { get; } 13 | IExpression IfTrue { get; } 14 | } 15 | } -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/IMethodPatch.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Visitor; 2 | using System.Collections.Immutable; 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 6 | { 7 | public interface IMethodPatch : IObjectPatch 8 | { 9 | [return: MaybeNull] 10 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 11 | => visitor.Visit(this); 12 | 13 | IMethod Method { get; } 14 | ImmutableArray TypeArguments { get; } 15 | } 16 | } -------------------------------------------------------------------------------- /source/flc/FlcException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace FluentLang.flc 5 | { 6 | [Serializable] 7 | public class FlcException : Exception 8 | { 9 | public FlcException(string? message) : base(message) 10 | { 11 | } 12 | 13 | public FlcException(string? message, Exception? innerException) : base(message, innerException) 14 | { 15 | } 16 | 17 | protected FlcException(SerializationInfo info, StreamingContext context) : base(info, context) 18 | { 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/IMemberInvocationExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Visitor; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 5 | { 6 | public interface IMemberInvocationExpression : IInvocationExpression 7 | { 8 | [return: MaybeNull] 9 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 10 | => visitor.Visit(this); 11 | IExpression Expression { get; } 12 | IInterfaceMethod Method { get; } 13 | string MemberName { get; } 14 | } 15 | } -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/IMatchExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Visitor; 2 | using System.Collections.Immutable; 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 6 | { 7 | public interface IMatchExpression : IExpression 8 | { 9 | [return: MaybeNull] 10 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 11 | => visitor.Visit(this); 12 | 13 | IExpression Expression { get; } 14 | 15 | ImmutableArray Arms { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/IMatchExpressionArm.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Visitor; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 5 | { 6 | public interface IMatchExpressionArm : IVisitableSymbol 7 | { 8 | [return: MaybeNull] 9 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 10 | => visitor.Visit(this); 11 | IType Type { get; } 12 | string? IdentifierName { get; } 13 | ILocal? Local { get; } 14 | IExpression Expression { get; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/MethodBody/ParameterLocal.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Interfaces; 2 | using FluentLang.Compiler.Symbols.Interfaces.MethodBody; 3 | 4 | namespace FluentLang.Compiler.Symbols.Source.MethodBody 5 | { 6 | public class ParameterLocal : IParameterLocal 7 | { 8 | public ParameterLocal(IParameter parameter) 9 | { 10 | Parameter = parameter; 11 | } 12 | 13 | public string Identifier => Parameter.Name; 14 | 15 | public IType Type => Parameter.Type; 16 | 17 | public IParameter Parameter { get; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /source/Compiler/Emit/DictionaryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace FluentLang.Compiler.Emit 5 | { 6 | internal static class DictionaryExtensions 7 | { 8 | public static TValue GetOrAdd( 9 | this Dictionary dict, 10 | TKey key, 11 | Func valueGenerator) 12 | { 13 | if (dict.TryGetValue(key, out var value)) 14 | { 15 | return value; 16 | } 17 | var result = valueGenerator(key); 18 | dict.Add(key, result); 19 | return result; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /source/Runtime/Metadata/MethodSignatureAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentLang.Runtime.Metadata 4 | { 5 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] 6 | public class MethodSignatureAttribute : Attribute 7 | { 8 | public MethodSignatureAttribute(string signature, string[] requireMethodKeySignatures) 9 | { 10 | Signature = signature; 11 | RequireMethodKeySignatures = requireMethodKeySignatures; 12 | } 13 | 14 | public string Signature { get; } 15 | public string[] RequireMethodKeySignatures { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/SymbolHelpers.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Interfaces; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace FluentLang.Compiler.Symbols 5 | { 6 | public static class SymbolHelpers 7 | { 8 | public static bool TryFindBestType(IType a, IType b, [NotNullWhen(true)] out IType? type) 9 | { 10 | if (a.IsSubtypeOf(b)) 11 | { 12 | type = b; 13 | return true; 14 | } 15 | if (b.IsSubtypeOf(a)) 16 | { 17 | type = a; 18 | return true; 19 | } 20 | type = null; 21 | return false; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /source/Shared/AsyncEnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.Immutable; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace FluentLang.Shared 8 | { 9 | public static class AsyncEnumerableExtensions 10 | { 11 | public static async ValueTask> ToImmutableArrayAsync(this IAsyncEnumerable @this, CancellationToken cancellationToken = default) 12 | { 13 | return (await @this.ToArrayAsync(cancellationToken)).UnsafeAsImmutableArray(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /source/flc/DependencyLoading/IProjectLoader.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Interfaces; 2 | using FluentLang.flc.ProjectSystem; 3 | using System.Collections.Generic; 4 | using System.Runtime.Loader; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace FluentLang.flc.DependencyLoading 9 | { 10 | public interface IProjectLoader 11 | { 12 | ValueTask LoadProjectAsync(ProjectInfo projectInfo, AssemblyLoadContext assemblyLoadContext, IEnumerable alreadyLoadedProjects, CancellationToken cancellationToken = default); 13 | } 14 | } -------------------------------------------------------------------------------- /source/TestUtils/TestUtils.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | FluentLang.TestUtils 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /source/flc/DependencyLoading/DependencyAttributeReader.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Metadata; 2 | using FluentLang.Runtime.Metadata; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | 7 | namespace FluentLang.flc.DependencyLoading 8 | { 9 | public class DependencyAttributeReader 10 | { 11 | public IEnumerable ReadDependencies(Assembly assembly) 12 | { 13 | return 14 | assembly 15 | .GetAttributes() 16 | .Select(x => new Dependency(x.Name, x.Version)); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /source/Shared/Shared.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | FluentLang.Shared 6 | latest 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/IDeclarationStatement.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Visitor; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 5 | { 6 | public interface IDeclarationStatement : IStatement 7 | { 8 | [return: MaybeNull] 9 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 10 | => visitor.Visit(this); 11 | IExpression Expression { get; } 12 | string? IdentifierName { get; } 13 | IType? DeclaredType { get; } 14 | IType Type { get; } 15 | IDeclaredLocal? Local { get; } 16 | } 17 | } -------------------------------------------------------------------------------- /source/TestUtils/WorkItemAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentLang.TestUtils 4 | { 5 | /// 6 | /// Used to tag test methods or types which are created for a given WorkItem 7 | /// 8 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] 9 | public sealed class WorkItemAttribute : Attribute 10 | { 11 | /// The URI where the work item can be viewed. 12 | public WorkItemAttribute(string issueUri) 13 | { 14 | IssueUri = issueUri; 15 | } 16 | 17 | public string IssueUri { get; } 18 | } 19 | } -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/IObjectPatchingExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Source.MethodBody; 2 | using FluentLang.Compiler.Symbols.Visitor; 3 | using System.Collections.Immutable; 4 | using System.Diagnostics.CodeAnalysis; 5 | 6 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 7 | { 8 | public interface IObjectPatchingExpression : IExpression 9 | { 10 | [return: MaybeNull] 11 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 12 | => visitor.Visit(this); 13 | IExpression Expression { get; } 14 | ImmutableArray Patches { get; } 15 | } 16 | } -------------------------------------------------------------------------------- /source/Compiler.Tests.Unit/Parsing/ParserErrorTests.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.TestUtils; 3 | using Xunit; 4 | using Xunit.Abstractions; 5 | 6 | namespace FluentLang.Compiler.Tests.Unit.Parsing 7 | { 8 | public class ParserErrorTests : TestBase 9 | { 10 | public ParserErrorTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) 11 | { 12 | } 13 | 14 | [Fact] 15 | public void ReportInvalidToken() 16 | { 17 | CreateAssembly("#!~@`") 18 | .VerifyDiagnostics( 19 | new Diagnostic(new Location(new TextToken(@"#")), ErrorCode.SyntaxError)); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /source/Compiler/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | [assembly: SuppressMessage("Style", "IDE0045:Convert to conditional expression", Justification = "", Scope = "member", Target = "~M:FluentLang.Compiler.Emit.FluentlangToCSharpEmitter.Impl.EmitRequiredMethodKeyArguments(FluentLang.Compiler.Symbols.Interfaces.IMethod,System.IO.TextWriter)")] 9 | -------------------------------------------------------------------------------- /source/flc/DependencyLoading/IDependencyLoader.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Interfaces; 2 | using FluentLang.flc.ProjectSystem; 3 | using System.Collections.Generic; 4 | using System.Collections.Immutable; 5 | using System.Runtime.Loader; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace FluentLang.flc.DependencyLoading 10 | { 11 | public interface IDependencyLoader 12 | { 13 | ValueTask> LoadDependenciesAsync(ProjectInfo project, AssemblyLoadContext assemblyLoadContext, IEnumerable alreadyLoadedProjects, CancellationToken cancellationToken = default); 14 | } 15 | } -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/IType.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Visitor; 2 | using FluentLang.Shared; 3 | using System.Collections.Generic; 4 | 5 | namespace FluentLang.Compiler.Symbols.Interfaces 6 | { 7 | public interface IType : IVisitableSymbol 8 | { 9 | public sealed bool IsEquivalentTo(IType other) => IsEquivalentTo(other, null); 10 | 11 | internal bool IsEquivalentTo(IType other, Stack<(IType, IType)>? dependantEqualities); 12 | 13 | public bool IsSubtypeOf(IType other); 14 | 15 | internal IType Substitute(ImmutableArrayDictionary substitutions, Dictionary substituted); 16 | } 17 | } -------------------------------------------------------------------------------- /source/Runtime/Metadata/InterfaceAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentLang.Runtime.Metadata 4 | { 5 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 6 | public class InterfaceAttribute : Attribute 7 | { 8 | public InterfaceAttribute(string fullyQualifiedName, string anonymousInterfaceDeclaration, string[] typeParameters) 9 | { 10 | FullyQualifiedName = fullyQualifiedName; 11 | AnonymousInterfaceDeclaration = anonymousInterfaceDeclaration; 12 | TypeParameters = typeParameters; 13 | } 14 | 15 | public string FullyQualifiedName { get; } 16 | public string AnonymousInterfaceDeclaration { get; } 17 | public string[] TypeParameters { get; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /source/Compiler/Emit/FluentlangToCSharpEmitter.MethodKeyOrParamName.cs: -------------------------------------------------------------------------------- 1 | namespace FluentLang.Compiler.Emit 2 | { 3 | public partial class FluentlangToCSharpEmitter 4 | { 5 | private abstract class MethodKeyOrParamName 6 | { 7 | private MethodKeyOrParamName() { } 8 | 9 | public sealed class MethodKey : MethodKeyOrParamName 10 | { 11 | public MethodKey(string value) 12 | { 13 | Value = value; 14 | } 15 | 16 | public string Value { get; } 17 | } 18 | 19 | public sealed class ParamName : MethodKeyOrParamName 20 | { 21 | public ParamName(string value) 22 | { 23 | Value = value; 24 | } 25 | 26 | public string Value { get; } 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /source/Shared/ImmutableArrayExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | using System.IO; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace FluentLang.Shared 6 | { 7 | public static class ImmutableArrayExtensions 8 | { 9 | public static T[] UnsafeAsArray(this ImmutableArray array) 10 | { 11 | return Unsafe.As, T[]>(ref array); 12 | } 13 | 14 | public static Stream ToStream(this ImmutableArray bytes) 15 | { 16 | return new MemoryStream(bytes.UnsafeAsArray(), writable: false); 17 | } 18 | 19 | public static ImmutableArray EmptyIfDefault(this ImmutableArray array) 20 | { 21 | return array.IsDefault ? ImmutableArray.Empty : array; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/MethodBody/IStaticInvocationExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Visitor; 2 | using System.Collections.Immutable; 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | namespace FluentLang.Compiler.Symbols.Interfaces.MethodBody 6 | { 7 | public interface IStaticInvocationExpression : IInvocationExpression 8 | { 9 | [return: MaybeNull] 10 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 11 | => visitor.Visit(this); 12 | IMethod Method { get; } 13 | QualifiedName MethodName { get; } 14 | ImmutableArray TypeArguments { get; } 15 | } 16 | 17 | public interface IPipedStaticInvocationExpression : IStaticInvocationExpression 18 | { 19 | new string MethodName { get; } 20 | } 21 | } -------------------------------------------------------------------------------- /source/Compiler/Diagnostics/Diagnostic.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Immutable; 4 | using System.Text; 5 | 6 | namespace FluentLang.Compiler.Diagnostics 7 | { 8 | public class Diagnostic 9 | { 10 | public Diagnostic(Location location, ErrorCode errorCode, ImmutableArray additionalContext = default) 11 | { 12 | Location = location; 13 | ErrorCode = errorCode; 14 | if (additionalContext.IsDefault) 15 | additionalContext = ImmutableArray.Empty; 16 | AdditionalContext = additionalContext; 17 | } 18 | 19 | public Location Location { get; } 20 | public ErrorCode ErrorCode { get; } 21 | public ImmutableArray AdditionalContext { get; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/ErrorSymbols/ErrorParameter.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using System.Collections.Immutable; 4 | 5 | namespace FluentLang.Compiler.Symbols.ErrorSymbols 6 | { 7 | public class ErrorParameter : IParameter, IErrorSymbol 8 | { 9 | public static ErrorParameter Instance = new ErrorParameter(); 10 | 11 | private ErrorParameter() { } 12 | 13 | public string Name => ""; 14 | 15 | public IType Type => ErrorType.Instance; 16 | 17 | public ImmutableArray AllDiagnostics => ImmutableArray.Empty; 18 | 19 | void ISymbol.EnsureAllLocalDiagnosticsCollected() 20 | { 21 | throw new System.NotImplementedException(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/MethodBody/DeclaredLocal.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Interfaces; 2 | using FluentLang.Compiler.Symbols.Interfaces.MethodBody; 3 | using System; 4 | 5 | namespace FluentLang.Compiler.Symbols.Source.MethodBody 6 | { 7 | internal class DeclaredLocal : IDeclaredLocal 8 | { 9 | public DeclaredLocal(IDeclarationStatement declaration) 10 | { 11 | Declaration = declaration; 12 | if (Declaration.IdentifierName is null) 13 | throw new ArgumentNullException(nameof(Declaration) + "." + nameof(Declaration.IdentifierName)); 14 | } 15 | 16 | public IDeclarationStatement Declaration { get; } 17 | 18 | public string Identifier => Declaration.IdentifierName!; 19 | 20 | public IType Type => Declaration.Type; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/MethodBody/NewObjectExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.Compiler.Symbols.Interfaces.MethodBody; 4 | using System.Collections.Immutable; 5 | 6 | namespace FluentLang.Compiler.Symbols.Source.MethodBody 7 | { 8 | internal sealed class NewObjectExpression : INewObjectExpression 9 | { 10 | private NewObjectExpression() { } 11 | public static NewObjectExpression Instance { get; } = new NewObjectExpression(); 12 | 13 | public IType Type => EmptyInterface.Instance; 14 | 15 | ImmutableArray ISymbol.AllDiagnostics => ImmutableArray.Empty; 16 | 17 | void ISymbol.EnsureAllLocalDiagnosticsCollected() 18 | { 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/flc/DependencyLoading/Dependency.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentLang.flc.DependencyLoading 4 | { 5 | public class Dependency : IEquatable 6 | { 7 | public Dependency(string name, string version) 8 | { 9 | Name = name; 10 | Version = version; 11 | } 12 | 13 | public string Name { get; } 14 | public string Version { get; } 15 | 16 | public override bool Equals(object? obj) 17 | { 18 | return Equals(obj as Dependency); 19 | } 20 | 21 | public bool Equals(Dependency? other) 22 | { 23 | return other != null && 24 | Name == other.Name && 25 | Version == other.Version; 26 | } 27 | 28 | public override int GetHashCode() 29 | { 30 | return HashCode.Combine(Name, Version); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /source/flc/DependencyLoading/GlobalPackagesFolderProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | 7 | namespace FluentLang.flc.DependencyLoading 8 | { 9 | public static class GlobalPackagesFolderProvider 10 | { 11 | public static string GlobalPackagesFolder => 12 | Environment.GetEnvironmentVariable("NUGET_PACKAGES", EnvironmentVariableTarget.Process) 13 | ?? Environment.GetEnvironmentVariable("NUGET_PACKAGES", EnvironmentVariableTarget.User) 14 | ?? Environment.GetEnvironmentVariable("NUGET_PACKAGES", EnvironmentVariableTarget.Machine) 15 | ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget/packages"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/IParameter.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Substituted; 2 | using FluentLang.Compiler.Symbols.Visitor; 3 | using FluentLang.Shared; 4 | using System.Collections.Generic; 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | namespace FluentLang.Compiler.Symbols.Interfaces 8 | { 9 | public interface IParameter : IVisitableSymbol 10 | { 11 | [return: MaybeNull] 12 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 13 | => visitor.Visit(this); 14 | public string Name { get; } 15 | public IType Type { get; } 16 | 17 | internal IParameter Substitute(ImmutableArrayDictionary substitutions, Dictionary substituted) 18 | => new SubstitutedParameter(this, substitutions, substituted); 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /source/Compiler/Compilation/CompilationResult.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using System.Collections.Immutable; 3 | 4 | namespace FluentLang.Compiler.Compilation 5 | { 6 | public class CompilationResult 7 | { 8 | public CompilationResult( 9 | CompilationResultStatus status, ImmutableArray assemblyDiagnostics, 10 | ImmutableArray roslynDiagnostics) 11 | { 12 | Status = status; 13 | AssemblyDiagnostics = assemblyDiagnostics; 14 | RoslynDiagnostics = roslynDiagnostics; 15 | } 16 | 17 | public CompilationResultStatus Status { get; } 18 | 19 | public ImmutableArray AssemblyDiagnostics { get; } 20 | 21 | public ImmutableArray RoslynDiagnostics { get; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /source/Compiler/Parsing/DiagnosticErrorListener.cs: -------------------------------------------------------------------------------- 1 | using Antlr4.Runtime; 2 | using FluentLang.Compiler.Diagnostics; 3 | using System.Collections.Immutable; 4 | using System.IO; 5 | 6 | namespace FluentLang.Compiler.Parsing 7 | { 8 | public class DiagnosticErrorListener : BaseErrorListener 9 | { 10 | private readonly DiagnosticBag _diagnostics; 11 | 12 | public DiagnosticErrorListener(DiagnosticBag diagnostics) 13 | { 14 | _diagnostics = diagnostics; 15 | } 16 | 17 | public override void SyntaxError(TextWriter output, IRecognizer recognizer, IToken offendingSymbol, int line, int charPositionInLine, string msg, RecognitionException e) 18 | { 19 | _diagnostics.Add(new Diagnostic(new Location(offendingSymbol), ErrorCode.SyntaxError, ImmutableArray.Create(msg))); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /source/Compiler/Parsing/ParserFactory.cs: -------------------------------------------------------------------------------- 1 | using Antlr4.Runtime; 2 | using FluentLang.Compiler.Diagnostics; 3 | using FluentLang.Compiler.Generated; 4 | using System.IO; 5 | 6 | namespace FluentLang.Compiler.Parsing 7 | { 8 | public static class ParserFactory 9 | { 10 | public static FluentLangParser Create(string source, DiagnosticBag diagnostics) 11 | { 12 | using var reader = new StringReader(source); 13 | 14 | var input = new AntlrInputStream(reader); 15 | var lexer = new FluentLangLexer(input); 16 | var tokenStream = new CommonTokenStream(lexer); 17 | var parser = new FluentLangParser(tokenStream); 18 | 19 | var errorListener = new DiagnosticErrorListener(diagnostics); 20 | parser.RemoveErrorListeners(); 21 | parser.AddErrorListener(errorListener); 22 | 23 | return parser; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /source/Runtime/MethodKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentLang.Runtime 4 | { 5 | public struct MethodKey : IEquatable 6 | { 7 | private readonly string _key; 8 | 9 | public MethodKey(string key) 10 | { 11 | _key = key; 12 | } 13 | 14 | public bool Equals(MethodKey other) 15 | { 16 | return other._key?.Equals(_key) ?? 17 | throw new InvalidOperationException($"{nameof(_key)} is null. Default MethodKeys are invalid."); 18 | } 19 | 20 | public override bool Equals(object? obj) 21 | { 22 | if (obj is MethodKey methodKey) 23 | return Equals(methodKey); 24 | 25 | return false; 26 | } 27 | 28 | public override int GetHashCode() 29 | { 30 | return _key.GetHashCode(); 31 | } 32 | 33 | public override string ToString() 34 | { 35 | return _key; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /source/flc/ProjectSystem/SolutionInfo.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Collections.Immutable; 3 | 4 | namespace FluentLang.flc.ProjectSystem 5 | { 6 | public class SolutionInfo 7 | { 8 | [JsonConstructor] 9 | public SolutionInfo(ImmutableArray projects, ImmutableArray libDirectories, ImmutableArray nugetFeeds) 10 | { 11 | Projects = projects.IsDefaultOrEmpty ? ImmutableArray.Empty : projects; 12 | LibDirectories = libDirectories.IsDefault ? ImmutableArray.Empty : libDirectories; 13 | NugetFeeds = nugetFeeds.IsDefault ? ImmutableArray.Empty : nugetFeeds; 14 | } 15 | 16 | public ImmutableArray Projects { get; } 17 | 18 | public ImmutableArray LibDirectories { get; } 19 | 20 | public ImmutableArray NugetFeeds { get; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /source/Compiler.Tests.Unit/Compiler.Tests.Unit.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | FluentLang.Compiler.Tests.Unit 6 | enable 7 | xUnit1019 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /source/TestUtils/TestInterface.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols; 3 | using FluentLang.Compiler.Symbols.Interfaces; 4 | using System.Collections.Immutable; 5 | 6 | namespace FluentLang.TestUtils 7 | { 8 | public class TestInterface : IInterface 9 | { 10 | public QualifiedName? FullyQualifiedName { get; set; } 11 | 12 | public bool IsExported => false; 13 | 14 | public ImmutableArray TypeParameters => ImmutableArray.Empty; 15 | 16 | public ImmutableArray TypeArguments => ImmutableArray.Empty; 17 | 18 | public ImmutableArray Methods { get; set; } = ImmutableArray.Empty; 19 | 20 | public ImmutableArray AllDiagnostics { get; set; } = ImmutableArray.Empty; 21 | 22 | void ISymbol.EnsureAllLocalDiagnosticsCollected() { } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/ErrorSymbols/ErrorInterfaceMethod.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using System.Collections.Immutable; 4 | using System.Linq; 5 | 6 | namespace FluentLang.Compiler.Symbols.ErrorSymbols 7 | { 8 | public class ErrorInterfaceMethod : IInterfaceMethod, IErrorSymbol 9 | { 10 | public ErrorInterfaceMethod(string name, int numParameters) 11 | { 12 | Name = name; 13 | Parameters = Enumerable.Repeat(ErrorParameter.Instance, numParameters).ToImmutableArray(); 14 | } 15 | 16 | public string Name { get; } 17 | 18 | public IType ReturnType => ErrorType.Instance; 19 | 20 | public ImmutableArray Parameters { get; } 21 | 22 | public ImmutableArray AllDiagnostics => ImmutableArray.Empty; 23 | 24 | void ISymbol.EnsureAllLocalDiagnosticsCollected(){ } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /source/TestUtils/XunitLogger.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using Xunit.Abstractions; 4 | 5 | namespace FluentLang.TestUtils 6 | { 7 | public class XunitLogger : ILogger, IDisposable 8 | { 9 | private readonly ITestOutputHelper _output; 10 | 11 | public XunitLogger(ITestOutputHelper output) 12 | { 13 | _output = output; 14 | } 15 | public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) where TState : notnull 16 | { 17 | _output.WriteLine($"{logLevel.ToString().ToUpper()}: {formatter(state, exception)}"); 18 | } 19 | 20 | public bool IsEnabled(LogLevel logLevel) 21 | { 22 | return true; 23 | } 24 | 25 | public IDisposable BeginScope(TState state) 26 | { 27 | return this; 28 | } 29 | 30 | public void Dispose() 31 | { 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /source/Runtime/Union.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentLang.Runtime 4 | { 5 | public class Union 6 | { 7 | public Union(object inner, ulong matchingOptions) 8 | { 9 | Inner = inner; 10 | MatchingOptions = matchingOptions; 11 | } 12 | 13 | public Union(Union union, Span matchingTargetOptionsPerOption) 14 | : this(union.Inner, union.GetUpcastToUnionMatchingOptions(matchingTargetOptionsPerOption)) 15 | { 16 | } 17 | 18 | public object Inner { get; } 19 | public ulong MatchingOptions { get; } 20 | 21 | private ulong GetUpcastToUnionMatchingOptions(Span matchingTargetOptionsPerOption) 22 | { 23 | ulong bits = 0; 24 | for (var i = 0; i < matchingTargetOptionsPerOption.Length; i++) 25 | { 26 | if ((MatchingOptions & ((ulong)1 << i)) != 0) 27 | { 28 | bits |= matchingTargetOptionsPerOption[i]; 29 | } 30 | } 31 | 32 | return bits; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /source/Shared/Release.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace FluentLang.Shared 5 | { 6 | public static class Release 7 | { 8 | public static void Assert([DoesNotReturnIf(false)] bool condition) 9 | { 10 | Assert(condition, string.Empty); 11 | } 12 | 13 | public static void Assert([DoesNotReturnIf(false)] bool condition, string? message) 14 | { 15 | if (!condition) 16 | Fail(message); 17 | } 18 | 19 | /// 20 | /// Throws an 21 | /// 22 | /// Returns an exception so that for flow control analysis you can write `throw Release.Fail()` 23 | /// However this will never actually occur. 24 | /// 25 | /// 26 | /// 27 | [DoesNotReturn] 28 | public static Exception Fail(string? message) 29 | { 30 | throw new InvalidOperationException(message); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /source/TestUtils/TextToken.cs: -------------------------------------------------------------------------------- 1 | using Antlr4.Runtime; 2 | 3 | namespace FluentLang.TestUtils 4 | { 5 | public class TextToken : IToken 6 | { 7 | public TextToken(string text) 8 | { 9 | Text = text; 10 | } 11 | 12 | public string Text { get; } 13 | 14 | public int Type => throw new System.NotImplementedException(); 15 | 16 | public int Line => throw new System.NotImplementedException(); 17 | 18 | public int Column => throw new System.NotImplementedException(); 19 | 20 | public int Channel => throw new System.NotImplementedException(); 21 | 22 | public int TokenIndex => throw new System.NotImplementedException(); 23 | 24 | public int StartIndex => throw new System.NotImplementedException(); 25 | 26 | public int StopIndex => throw new System.NotImplementedException(); 27 | 28 | public ITokenSource TokenSource => throw new System.NotImplementedException(); 29 | 30 | public ICharStream InputStream => throw new System.NotImplementedException(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /source/flc.Tests.Unit/flc.Tests.Unit.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | FluentLang.Compiler.Tests.Unit 6 | enable 7 | xUnit1019 8 | 9.0 9 | 10 | 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /source/Compiler.Tests.Unit/Symbols/SourceSymbols/MethodBodyTests/NewObjectExpressionTests.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Interfaces.MethodBody; 2 | using FluentLang.Compiler.Symbols.Source.MethodBody; 3 | using FluentLang.TestUtils; 4 | using System.Linq; 5 | using Xunit; 6 | using Xunit.Abstractions; 7 | 8 | namespace FluentLang.Compiler.Tests.Unit.Symbols.SourceSymbols.MethodBodyTests 9 | { 10 | public class NewObjectExpressionTests : TestBase 11 | { 12 | public NewObjectExpressionTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) 13 | { 14 | } 15 | 16 | [Fact] 17 | public void CanParseNewObjectExpression() 18 | { 19 | var assembly = CreateAssembly(@" 20 | M() : {} { 21 | return {}; 22 | }").VerifyDiagnostics().VerifyEmit(); 23 | var m = AssertGetMethod(assembly, "M"); 24 | var returnStatement = Assert.IsAssignableFrom(m.Statements.Single()); 25 | Assert.IsAssignableFrom(returnStatement.Expression); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /source/Compiler/Emit/LocalSystemDllsMetadataReferenceProvider.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Runtime; 2 | using Microsoft.CodeAnalysis; 3 | using System.Collections.Immutable; 4 | using System.IO; 5 | using System.Linq; 6 | 7 | namespace FluentLang.Compiler.Emit 8 | { 9 | public class LocalSystemDllsMetadataReferenceProvider : IMetadataReferenceProvider 10 | { 11 | private LocalSystemDllsMetadataReferenceProvider() 12 | { 13 | 14 | } 15 | 16 | public static LocalSystemDllsMetadataReferenceProvider Instance { get; } = new LocalSystemDllsMetadataReferenceProvider(); 17 | 18 | public ImmutableArray MetadataReferences { get; } = 19 | Directory.GetFiles(Path.GetDirectoryName(typeof(object).Assembly.Location), "System.*.dll") 20 | .Concat(Directory.GetFiles(Path.GetDirectoryName(typeof(object).Assembly.Location), "netstandard.dll")) 21 | .Append(typeof(FLObject).Assembly.Location) 22 | .Select(x => MetadataReference.CreateFromFile(x)) 23 | .ToImmutableArray(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/MethodBody/EmptyInterface.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using System.Collections.Immutable; 4 | 5 | namespace FluentLang.Compiler.Symbols.Source.MethodBody 6 | { 7 | internal sealed class EmptyInterface : IInterface 8 | { 9 | private EmptyInterface() { } 10 | 11 | public static EmptyInterface Instance { get; } = new EmptyInterface(); 12 | 13 | public QualifiedName? FullyQualifiedName => null; 14 | 15 | public bool IsExported => false; 16 | 17 | public ImmutableArray TypeParameters => ImmutableArray.Empty; 18 | 19 | public ImmutableArray TypeArguments => ImmutableArray.Empty; 20 | 21 | public ImmutableArray Methods => ImmutableArray.Empty; 22 | 23 | public ImmutableArray AllDiagnostics => ImmutableArray.Empty; 24 | 25 | void ISymbol.EnsureAllLocalDiagnosticsCollected() 26 | { 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /source/Compiler/Diagnostics/DiagnosticFormatter.cs: -------------------------------------------------------------------------------- 1 | using Antlr4.Runtime; 2 | using System.Linq; 3 | 4 | namespace FluentLang.Compiler.Diagnostics 5 | { 6 | public class DiagnosticFormatter : IDiagnosticFormatter 7 | { 8 | public string CreateDiagnosticMessage(Diagnostic diagnostic) 9 | { 10 | return diagnostic.ErrorCode switch 11 | { 12 | //TODO: explicitly handle more errors. 13 | ErrorCode.InvalidMetadataAssembly => 14 | diagnostic.AdditionalContext.FirstOrDefault()?.ToString() ?? "", 15 | var errorCode => 16 | $"code: {errorCode}, text: {diagnostic.Location.GetText().ToString()}, aditionalContext: {string.Join(", ", diagnostic.AdditionalContext)}", 17 | }; 18 | } 19 | 20 | public string CreateLocationMessage(Diagnostic diagnostic) 21 | { 22 | //TODO: store file number. 23 | var location = diagnostic.Location.TextRange; 24 | return $"Start: [Ln {location.Start.Line}, Col {location.Start.Column}] End: [Ln {location.End.Line}, Col {location.End.Column}]"; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /source/WebIde/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:54390", 7 | "sslPort": 44368 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | } 18 | }, 19 | "WebIde": { 20 | "commandName": "Project", 21 | "launchBrowser": true, 22 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 23 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Substituted/SubstitutedParameter.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.Shared; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Collections.Immutable; 7 | 8 | namespace FluentLang.Compiler.Symbols.Substituted 9 | { 10 | internal class SubstitutedParameter : IParameter 11 | { 12 | private readonly IParameter _original; 13 | private readonly Lazy _type; 14 | 15 | public SubstitutedParameter(IParameter original, ImmutableArrayDictionary substitutions, Dictionary substituted) 16 | { 17 | _original = original; 18 | _type = new Lazy(() => _original.Type.Substitute(substitutions, substituted)); 19 | } 20 | 21 | public string Name => _original.Name; 22 | 23 | public IType Type => _type.Value; 24 | 25 | public ImmutableArray AllDiagnostics => ImmutableArray.Empty; 26 | 27 | void ISymbol.EnsureAllLocalDiagnosticsCollected() { } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/SymbolBase.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using System; 4 | using System.Collections.Immutable; 5 | 6 | namespace FluentLang.Compiler.Symbols.Source 7 | { 8 | public abstract class SymbolBase : ISymbol 9 | { 10 | protected readonly DiagnosticBag _diagnostics; 11 | private readonly Lazy> _allDiagnostics; 12 | 13 | public SymbolBase(DiagnosticBag diagnostics) 14 | { 15 | _diagnostics = diagnostics.CreateChildBag(this); 16 | _allDiagnostics = new Lazy>(() => 17 | { 18 | _diagnostics.EnsureAllDiagnosticsCollectedForSymbol(); 19 | return _diagnostics.ToImmutableArray(); 20 | }); 21 | } 22 | 23 | public ImmutableArray AllDiagnostics => _allDiagnostics.Value; 24 | 25 | void ISymbol.EnsureAllLocalDiagnosticsCollected() 26 | { 27 | EnsureAllLocalDiagnosticsCollected(); 28 | } 29 | 30 | protected abstract void EnsureAllLocalDiagnosticsCollected(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /source/Compiler.Tests.Unit/Symbols/SourceSymbols/SourceUnionTests.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.Compiler.Symbols.Source.MethodBody; 4 | using FluentLang.TestUtils; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using Xunit; 10 | using Xunit.Abstractions; 11 | 12 | namespace FluentLang.Compiler.Tests.Unit.Symbols.SourceSymbols 13 | { 14 | public class SourceUnionTests : TestBase 15 | { 16 | public SourceUnionTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) 17 | { 18 | } 19 | 20 | [Fact] 21 | public void CanParseUnion() 22 | { 23 | var assembly = CreateAssembly("M() : int | {} { return 42; }").VerifyDiagnostics().VerifyEmit(); 24 | var m = AssertGetMethod(assembly, "M"); 25 | var union = Assert.IsAssignableFrom(m.ReturnType); 26 | Assert.Equal(Primitive.Int, union.Options[0]); 27 | Assert.True(union.Options[1].IsEquivalentTo(EmptyInterface.Instance)); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /source/Compiler/Compiler.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | FluentLang.Compiler 6 | enable 7 | true 8 | CS3021,CS8717 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Substituted/SubstitutedUnion.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.Shared; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Collections.Immutable; 7 | using System.Linq; 8 | 9 | namespace FluentLang.Compiler.Symbols.Substituted 10 | { 11 | internal class SubstitutedUnion : IUnion 12 | { 13 | private readonly Lazy> _options; 14 | 15 | public SubstitutedUnion(IUnion original, ImmutableArrayDictionary substitutions, Dictionary substituted) 16 | { 17 | substituted.Add(original, this); 18 | substituted.Add(this, this); 19 | _options = new Lazy>( 20 | () => original.Options.Select(x => x.Substitute(substitutions, substituted)).ToImmutableArray()); 21 | } 22 | 23 | public ImmutableArray Options => _options.Value; 24 | 25 | public ImmutableArray AllDiagnostics => ImmutableArray.Empty; 26 | 27 | void ISymbol.EnsureAllLocalDiagnosticsCollected() { } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /source/flc.Tests.Integration/flc.Tests.Integration.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | FluentLang.Compiler.Tests.Integration 6 | enable 7 | xUnit1019 8 | preview 9 | 10 | 11 | 12 | 13 | PreserveNewest 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | all 22 | runtime; build; native; contentfiles; analyzers; buildtransitive 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /source/flc/ProjectSystem/Reference.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.ComponentModel; 4 | 5 | namespace FluentLang.flc.ProjectSystem 6 | { 7 | public sealed class Reference 8 | { 9 | [JsonConstructor] 10 | public Reference( 11 | ReferenceType type, 12 | string name, 13 | string? version = null) 14 | { 15 | if (type == ReferenceType.Project && version is { }) 16 | throw new ArgumentException("Project reference cannot specify a version", nameof(version)); 17 | 18 | if (type == ReferenceType.Assembly && version is null) 19 | throw new ArgumentException("Assembly reference must specify a version", nameof(version)); 20 | 21 | if (type != ReferenceType.Assembly && type != ReferenceType.Project) 22 | throw new InvalidEnumArgumentException(nameof(type), (int)type, typeof(ReferenceType)); 23 | 24 | Type = type; 25 | Name = name; 26 | Version = version; 27 | } 28 | 29 | public ReferenceType Type { get; } 30 | public string Name { get; } 31 | public string? Version { get; } 32 | 33 | public enum ReferenceType 34 | { 35 | Assembly, 36 | Project, 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /source/Runtime/Runtime.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | FluentLang.Runtime 6 | latest 7 | enable 8 | true 9 | CS3021,CS8717 10 | true 11 | snupkg 12 | true 13 | FluentLang.Runtime 14 | 1.0.0 15 | FluentLang 16 | FluentLang 17 | FluentLang 18 | .Net based Runtime for the FluentLang Language. 19 | 20 | MIT 21 | https://github.com/YairHalberstadt/fluentlang/ 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Yair Halberstadt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Metadata/Utils.cs: -------------------------------------------------------------------------------- 1 | using Antlr4.Runtime; 2 | using FluentLang.Compiler.Diagnostics; 3 | using FluentLang.Compiler.Generated; 4 | using FluentLang.Compiler.Parsing; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Diagnostics.CodeAnalysis; 8 | using System.IO; 9 | using System.Reflection; 10 | using System.Text; 11 | using DiagnosticErrorListener = FluentLang.Compiler.Parsing.DiagnosticErrorListener; 12 | 13 | namespace FluentLang.Compiler.Symbols.Metadata 14 | { 15 | public static class Utils 16 | { 17 | public static T? GetAttribute(this MethodInfo method) where T : Attribute 18 | { 19 | return Attribute.GetCustomAttribute(method, typeof(T)) as T; 20 | } 21 | 22 | public static T[] GetAttributes(this Assembly assembly) where T : Attribute 23 | { 24 | return (T[])Attribute.GetCustomAttributes(assembly, typeof(T)); 25 | } 26 | 27 | public static T Parse(string source, Func getT, DiagnosticBag diagnostics) where T : ParserRuleContext 28 | { 29 | var parser = ParserFactory.Create(source, diagnostics); 30 | 31 | var t = getT(parser); 32 | return t; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /source/WebIde/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /source/flc.Tests.Unit/DependencyInjectionTests.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.flc.DependencyInjection; 2 | using FluentLang.flc.DependencyLoading; 3 | using FluentLang.flc.ProjectSystem; 4 | using Microsoft.Extensions.Logging; 5 | using StrongInject; 6 | using System; 7 | using System.Threading.Tasks; 8 | using Xunit; 9 | 10 | namespace FluentLang.Compiler.Tests.Unit 11 | { 12 | public partial class DependencyInjectionTests 13 | { 14 | [Fact] 15 | public async Task CanResolveCompiler() 16 | { 17 | await using var container = new FlcContainer(LogLevel.Information); 18 | await container.RunAsync(compiler => Assert.NotNull(compiler)); 19 | } 20 | 21 | [RegisterModule(typeof(FlcModule))] 22 | public partial class ProjectLoaderContainer : IContainer> 23 | { 24 | [Instance] private readonly LogLevel _logLevel; 25 | 26 | public ProjectLoaderContainer(LogLevel logLevel) 27 | { 28 | _logLevel = logLevel; 29 | } 30 | } 31 | 32 | [Fact] 33 | public void CanResolveProjectLoader() 34 | { 35 | using var container = new ProjectLoaderContainer(LogLevel.Information); 36 | container.Run(func => Assert.NotNull(func(new SolutionInfo(default, default, default)))); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/AssemblyFactory.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Compilation; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.Compiler.Symbols.Metadata; 4 | using FluentLang.Compiler.Symbols.Source; 5 | using System.Collections.Immutable; 6 | using System.Reflection; 7 | 8 | namespace FluentLang.Compiler.Symbols 9 | { 10 | public class AssemblyFactory 11 | { 12 | private readonly IAssemblyCompiler _assemblyCompiler; 13 | 14 | public AssemblyFactory(IAssemblyCompiler assemblyCompiler) 15 | { 16 | _assemblyCompiler = assemblyCompiler; 17 | } 18 | 19 | public IAssembly FromSource( 20 | QualifiedName name, 21 | (int major, int minor, string? suffix) version, 22 | ImmutableArray directlyReferencedAssemblies, 23 | ImmutableArray documents) => new SourceAssembly( 24 | name, 25 | new Version(version.major, version.minor, version.suffix ?? ""), 26 | directlyReferencedAssemblies, 27 | documents, 28 | _assemblyCompiler); 29 | 30 | public IAssembly FromMetadata( 31 | Assembly assembly, 32 | ImmutableArray assemblyBytes, 33 | ImmutableArray dependencies) => 34 | new MetadataAssembly(assembly, assemblyBytes, dependencies); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/ITypeParameter.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Visitor; 2 | using FluentLang.Shared; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | namespace FluentLang.Compiler.Symbols.Interfaces 8 | { 9 | public interface ITypeParameter : IType, IEquatable 10 | { 11 | [return: MaybeNull] 12 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 13 | => visitor.Visit(this); 14 | 15 | public string Name { get; } 16 | public IType? ConstrainedTo { get; } 17 | 18 | bool IType.IsEquivalentTo(IType other, Stack<(IType, IType)>? dependantEqualities) 19 | { 20 | return ReferenceEquals(this, other); 21 | } 22 | 23 | bool IType.IsSubtypeOf(IType other) 24 | { 25 | return IsEquivalentTo(other) || (ConstrainedTo?.IsSubtypeOf(other) ?? false); 26 | } 27 | 28 | IType IType.Substitute(ImmutableArrayDictionary substitutions, Dictionary substituted) 29 | { 30 | return substitutions.TryGetValue(this, out var substitution) 31 | ? substitution 32 | : this; 33 | } 34 | 35 | bool IEquatable.Equals(ITypeParameter other) => ReferenceEquals(this, other); 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/SourceParameter.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using System; 4 | using System.Collections.Immutable; 5 | using static FluentLang.Compiler.Generated.FluentLangParser; 6 | 7 | namespace FluentLang.Compiler.Symbols.Source 8 | { 9 | internal class SourceParameter : SymbolBase, IParameter 10 | { 11 | private readonly ParameterContext _context; 12 | private readonly SourceSymbolContext _sourceSymbolContext; 13 | private readonly Lazy _type; 14 | 15 | public SourceParameter( 16 | ParameterContext context, 17 | SourceSymbolContext sourceSymbolContext, 18 | bool isExported, 19 | DiagnosticBag diagnostics) : base(diagnostics) 20 | { 21 | _context = context; 22 | _sourceSymbolContext = sourceSymbolContext; 23 | Name = _context.LOWERCASE_IDENTIFIER().Symbol.Text; 24 | _type = new Lazy(() => _context.type_declaration().type().BindType(_sourceSymbolContext, isExported, _diagnostics)); 25 | } 26 | public string Name { get; } 27 | 28 | public IType Type => _type.Value; 29 | 30 | protected override void EnsureAllLocalDiagnosticsCollected() 31 | { 32 | // Touch all lazy fields to force binding; 33 | 34 | _ = _type.Value; 35 | } 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /source/flc/flc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net5.0 6 | FluentLang.flc 7 | enable 8 | true 9 | CS3021,CS8717 10 | 9.0 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /source/WebIde/WebIde.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 3.0 6 | FluentLang.WebIde 7 | latest 8 | enable 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/MethodBody/MethodBodySymbolContextExtensions.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.Shared; 4 | using System.Collections.Immutable; 5 | using static FluentLang.Compiler.Generated.FluentLangParser; 6 | 7 | namespace FluentLang.Compiler.Symbols.Source.MethodBody 8 | { 9 | internal static class MethodBodySymbolContextExtensions 10 | { 11 | public static void WarnIfUseOfMethodWhichCapturesUnassignedLocals(this MethodBodySymbolContext methodBodySymbolContext, IMethod target, DiagnosticBag diagnostics, Method_referenceContext syntax) 12 | { 13 | var currentMethod = methodBodySymbolContext.SourceSymbolContext.Scope; 14 | Release.Assert(currentMethod != null); 15 | if (target.DeclaringMethod == currentMethod) 16 | { 17 | if (target.InScopeAfter is { } declarationStatement) 18 | { 19 | var currentStatement = methodBodySymbolContext.CurrentStatement; 20 | Release.Assert(currentStatement != null); 21 | if (declarationStatement.OrdinalPositionInMethod >= currentStatement.OrdinalPositionInMethod) 22 | { 23 | diagnostics.Add(new Diagnostic( 24 | new Location(syntax), 25 | ErrorCode.UseOfMethodWhichCapturesUnassignedLocals, 26 | ImmutableArray.Create(target))); 27 | } 28 | } 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /source/Compiler/Symbols/Substituted/ConstructedInterface.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.Compiler.Symbols.Source; 4 | using System.Collections.Generic; 5 | using System.Collections.Immutable; 6 | 7 | namespace FluentLang.Compiler.Symbols.Substituted 8 | { 9 | internal class ConstructedInterface : IInterface 10 | { 11 | private readonly SubstitutedInterface _substituted; 12 | 13 | public ConstructedInterface(IInterface original, ImmutableArray typeArguments) 14 | { 15 | _substituted = new SubstitutedInterface( 16 | original, 17 | SourceSymbolContextExtensions.CreateTypeMap(typeArguments, original.TypeParameters), 18 | new Dictionary()); 19 | 20 | TypeArguments = typeArguments; 21 | } 22 | 23 | public bool IsExported => _substituted.IsExported; 24 | 25 | public QualifiedName? FullyQualifiedName => _substituted.FullyQualifiedName; 26 | 27 | public ImmutableArray Methods => _substituted.Methods; 28 | 29 | public ImmutableArray TypeParameters => ImmutableArray.Empty; 30 | 31 | public ImmutableArray AllDiagnostics => ImmutableArray.Empty; 32 | 33 | public ImmutableArray TypeArguments { get; } 34 | 35 | void ISymbol.EnsureAllLocalDiagnosticsCollected() 36 | { 37 | 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /source/Compiler/Generated/FluentLangLexer.tokens: -------------------------------------------------------------------------------- 1 | SINGLE_LINE_COMMENT=1 2 | WHITESPACES=2 3 | PLUS=3 4 | MINUS=4 5 | STAR=5 6 | DIV=6 7 | PERCENT=7 8 | LT=8 9 | GT=9 10 | OP_EQ=10 11 | OP_NE=11 12 | OP_LE=12 13 | OP_GE=13 14 | OP_AND=14 15 | OP_OR=15 16 | LOGICAL_OR=16 17 | ASSIGNMENT=17 18 | OPEN_BRACE=18 19 | CLOSE_BRACE=19 20 | OPEN_PARENS=20 21 | CLOSE_PARENS=21 22 | DOT=22 23 | COMMA=23 24 | COLON=24 25 | SEMICOLON=25 26 | DISCARD=26 27 | RIGHT_ARROW=27 28 | DOT_DOT=28 29 | BOOL=29 30 | INT=30 31 | DOUBLE=31 32 | CHAR=32 33 | STRING=33 34 | LITERAL_TRUE=34 35 | LITERAL_FALSE=35 36 | INTEGER_LITERAL=36 37 | REAL_LITERAL=37 38 | CHARACTER_LITERAL=38 39 | REGULAR_STRING=39 40 | NAMESPACE=40 41 | INTERFACE=41 42 | RETURN=42 43 | IF=43 44 | ELSE=44 45 | MIXIN=45 46 | EXPORT=46 47 | OPEN=47 48 | LET=48 49 | MATCH=49 50 | UPPERCASE_IDENTIFIER=50 51 | LOWERCASE_IDENTIFIER=51 52 | ErrorChar=52 53 | '+'=3 54 | '-'=4 55 | '*'=5 56 | '/'=6 57 | '%'=7 58 | '<'=8 59 | '>'=9 60 | '=='=10 61 | '!='=11 62 | '<='=12 63 | '>='=13 64 | '&&'=14 65 | '||'=15 66 | '|'=16 67 | '='=17 68 | '{'=18 69 | '}'=19 70 | '('=20 71 | ')'=21 72 | '.'=22 73 | ','=23 74 | ':'=24 75 | ';'=25 76 | '_'=26 77 | '=>'=27 78 | '..'=28 79 | 'bool'=29 80 | 'int'=30 81 | 'double'=31 82 | 'char'=32 83 | 'string'=33 84 | 'true'=34 85 | 'false'=35 86 | 'namespace'=40 87 | 'interface'=41 88 | 'return'=42 89 | 'if'=43 90 | 'else'=44 91 | 'mixin'=45 92 | 'export'=46 93 | 'open'=47 94 | 'let'=48 95 | 'match'=49 96 | -------------------------------------------------------------------------------- /source/Compiler/Generated/FluentLangParser.tokens: -------------------------------------------------------------------------------- 1 | SINGLE_LINE_COMMENT=1 2 | WHITESPACES=2 3 | PLUS=3 4 | MINUS=4 5 | STAR=5 6 | DIV=6 7 | PERCENT=7 8 | LT=8 9 | GT=9 10 | OP_EQ=10 11 | OP_NE=11 12 | OP_LE=12 13 | OP_GE=13 14 | OP_AND=14 15 | OP_OR=15 16 | LOGICAL_OR=16 17 | ASSIGNMENT=17 18 | OPEN_BRACE=18 19 | CLOSE_BRACE=19 20 | OPEN_PARENS=20 21 | CLOSE_PARENS=21 22 | DOT=22 23 | COMMA=23 24 | COLON=24 25 | SEMICOLON=25 26 | DISCARD=26 27 | RIGHT_ARROW=27 28 | DOT_DOT=28 29 | BOOL=29 30 | INT=30 31 | DOUBLE=31 32 | CHAR=32 33 | STRING=33 34 | LITERAL_TRUE=34 35 | LITERAL_FALSE=35 36 | INTEGER_LITERAL=36 37 | REAL_LITERAL=37 38 | CHARACTER_LITERAL=38 39 | REGULAR_STRING=39 40 | NAMESPACE=40 41 | INTERFACE=41 42 | RETURN=42 43 | IF=43 44 | ELSE=44 45 | MIXIN=45 46 | EXPORT=46 47 | OPEN=47 48 | LET=48 49 | MATCH=49 50 | UPPERCASE_IDENTIFIER=50 51 | LOWERCASE_IDENTIFIER=51 52 | ErrorChar=52 53 | '+'=3 54 | '-'=4 55 | '*'=5 56 | '/'=6 57 | '%'=7 58 | '<'=8 59 | '>'=9 60 | '=='=10 61 | '!='=11 62 | '<='=12 63 | '>='=13 64 | '&&'=14 65 | '||'=15 66 | '|'=16 67 | '='=17 68 | '{'=18 69 | '}'=19 70 | '('=20 71 | ')'=21 72 | '.'=22 73 | ','=23 74 | ':'=24 75 | ';'=25 76 | '_'=26 77 | '=>'=27 78 | '..'=28 79 | 'bool'=29 80 | 'int'=30 81 | 'double'=31 82 | 'char'=32 83 | 'string'=33 84 | 'true'=34 85 | 'false'=35 86 | 'namespace'=40 87 | 'interface'=41 88 | 'return'=42 89 | 'if'=43 90 | 'else'=44 91 | 'mixin'=45 92 | 'export'=46 93 | 'open'=47 94 | 'let'=48 95 | 'match'=49 96 | -------------------------------------------------------------------------------- /source/flc/ProjectSystem/ProjectInfo.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Immutable; 4 | 5 | namespace FluentLang.flc.ProjectSystem 6 | { 7 | public class ProjectInfo 8 | { 9 | [JsonConstructor] 10 | public ProjectInfo( 11 | string name, 12 | Version version, 13 | ImmutableArray includedFilesAndFolders, 14 | ImmutableArray excludedFilesAndFolders = default, 15 | ImmutableArray references = default, 16 | bool isTest = false) 17 | { 18 | if (string.IsNullOrEmpty(name)) 19 | throw new ArgumentException("is null or empty", nameof(name)); 20 | Name = name; 21 | Version = version ?? throw new ArgumentNullException(nameof(version)); 22 | if (includedFilesAndFolders.IsDefaultOrEmpty) 23 | throw new ArgumentException("must include at least one file", nameof(includedFilesAndFolders)); 24 | IncludedFilesAndFolders = includedFilesAndFolders; 25 | IsTest = isTest; 26 | ExcludedFilesAndFolders = excludedFilesAndFolders.IsDefault ? ImmutableArray.Empty : excludedFilesAndFolders; 27 | References = references.IsDefault ? ImmutableArray.Empty: references; 28 | } 29 | 30 | public ImmutableArray IncludedFilesAndFolders { get; } 31 | public bool IsTest { get; } 32 | public ImmutableArray ExcludedFilesAndFolders { get; } 33 | public string Name { get; } 34 | public ImmutableArray References { get; } 35 | public Version Version { get; } 36 | } 37 | } -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/MethodBody/ReturnStatement.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces.MethodBody; 3 | using System; 4 | using static FluentLang.Compiler.Generated.FluentLangParser; 5 | 6 | namespace FluentLang.Compiler.Symbols.Source.MethodBody 7 | { 8 | internal sealed class ReturnStatement : SymbolBase, IReturnStatement 9 | { 10 | private readonly Return_statementContext _context; 11 | private readonly MethodBodySymbolContext _methodBodySymbolContext; 12 | private readonly Lazy _expression; 13 | 14 | public ReturnStatement( 15 | Return_statementContext context, 16 | int ordinalPositionInMethod, 17 | MethodBodySymbolContext methodBodySymbolContext, 18 | DiagnosticBag diagnostics) : base(diagnostics) 19 | { 20 | _context = context; 21 | OrdinalPositionInMethod = ordinalPositionInMethod; 22 | _methodBodySymbolContext = methodBodySymbolContext.WithStatement(this); 23 | _expression = new Lazy(BindExpression); 24 | } 25 | 26 | private IExpression BindExpression() 27 | { 28 | return _context.expression().BindExpression(_methodBodySymbolContext, _diagnostics); 29 | } 30 | 31 | public IExpression Expression => _expression.Value; 32 | 33 | public int OrdinalPositionInMethod { get; } 34 | 35 | protected override void EnsureAllLocalDiagnosticsCollected() 36 | { 37 | // Touch all lazy fields to force binding; 38 | 39 | _ = _expression.Value; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/SourceSymbolContext.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Interfaces; 2 | using FluentLang.Shared; 3 | using System; 4 | using System.Collections.Immutable; 5 | 6 | namespace FluentLang.Compiler.Symbols.Source 7 | { 8 | internal class SourceSymbolContext 9 | { 10 | public SourceSymbolContext( 11 | IMethod? scope, 12 | IAssembly assembly, 13 | ImmutableArray imports, 14 | QualifiedName? nameSpace, 15 | Func> currentLevelTypeParameters) 16 | { 17 | Scope = scope; 18 | Assembly = assembly; 19 | Imports = imports.IsDefault ? throw Release.Fail("imports cannot be default") : imports; 20 | NameSpace = nameSpace; 21 | CurrentLevelTypeParameters = currentLevelTypeParameters; 22 | } 23 | 24 | public IMethod? Scope { get; } 25 | public Func> CurrentLevelTypeParameters { get; } 26 | public IAssembly Assembly { get; } 27 | public ImmutableArray Imports { get; } 28 | public QualifiedName? NameSpace { get; } 29 | public SourceSymbolContext WithScope(IMethod? scope) 30 | { 31 | return new SourceSymbolContext(scope, Assembly, Imports, NameSpace, () => ImmutableArray.Empty); 32 | } 33 | public SourceSymbolContext WithTypeParameters(Func> currentLevelTypeParameters) 34 | { 35 | return new SourceSymbolContext(Scope, Assembly, Imports, NameSpace, currentLevelTypeParameters); 36 | } 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/IInterfaceMethod.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Substituted; 2 | using FluentLang.Compiler.Symbols.Visitor; 3 | using FluentLang.Shared; 4 | using System.Collections.Generic; 5 | using System.Collections.Immutable; 6 | using System.Diagnostics.CodeAnalysis; 7 | using System.Linq; 8 | 9 | namespace FluentLang.Compiler.Symbols.Interfaces 10 | { 11 | public interface IInterfaceMethod : IVisitableSymbol 12 | { 13 | [return: MaybeNull] 14 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 15 | => visitor.Visit(this); 16 | 17 | public string Name { get; } 18 | public IType ReturnType { get; } 19 | public ImmutableArray Parameters { get; } 20 | public IInterfaceMethod OriginalDefinition => this; 21 | 22 | internal bool IsEquivalentTo(IInterfaceMethod otherMethod, Stack<(IType, IType)>? dependantEqualities) 23 | { 24 | if (Name != otherMethod.Name) 25 | return false; 26 | 27 | if (Parameters.Length != otherMethod.Parameters.Length) 28 | return false; 29 | 30 | if (!ReturnType.IsEquivalentTo(otherMethod.ReturnType, dependantEqualities)) 31 | return false; 32 | 33 | return Parameters.SequenceEqual(otherMethod.Parameters, (x, y) => x.Type.IsEquivalentTo(y.Type, dependantEqualities)); 34 | } 35 | 36 | internal IInterfaceMethod Substitute(ImmutableArrayDictionary substitutions, Dictionary substituted) 37 | => new SubstitutedInterfaceMethod(this, substitutions, substituted); 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Visitor/SymbolExtensions.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Interfaces; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics.CodeAnalysis; 5 | 6 | namespace FluentLang.Compiler.Symbols.Visitor 7 | { 8 | public static class SymbolExtensions 9 | { 10 | public static IEnumerable DescendantNodes(this IVisitableSymbol symbol, Func? visitChildren = null, Func? includeSymbol = null) 11 | { 12 | var visitor = new CollectAllSymbolsVisitor( 13 | visitChildren ?? (_ => true), 14 | includeSymbol ?? (_ => true)); 15 | symbol.Visit(visitor); 16 | return visitor.Symbols; 17 | } 18 | 19 | private sealed class CollectAllSymbolsVisitor : BaseSymbolVisitor 20 | { 21 | public readonly List Symbols = new List(); 22 | 23 | private readonly Func _visitChildren; 24 | private readonly Func _includeSymbol; 25 | 26 | public CollectAllSymbolsVisitor(Func visitChildren, Func includeSymbol) 27 | { 28 | _visitChildren = visitChildren; 29 | _includeSymbol = includeSymbol; 30 | } 31 | 32 | [return: MaybeNull] 33 | protected override object DefaultVisit(IVisitableSymbol symbol) 34 | { 35 | if (_includeSymbol(symbol)) 36 | { 37 | Symbols.Add(symbol); 38 | } 39 | if (_visitChildren(symbol)) 40 | { 41 | return symbol.Visit(this); 42 | } 43 | return default; 44 | } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/IUnion.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Substituted; 2 | using FluentLang.Compiler.Symbols.Visitor; 3 | using FluentLang.Shared; 4 | using System.Collections.Generic; 5 | using System.Collections.Immutable; 6 | using System.Diagnostics.CodeAnalysis; 7 | using System.Linq; 8 | 9 | namespace FluentLang.Compiler.Symbols.Interfaces 10 | { 11 | public interface IUnion : IType 12 | { 13 | [return: MaybeNull] 14 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 15 | => visitor.Visit(this); 16 | 17 | ImmutableArray Options { get; } 18 | 19 | bool IType.IsEquivalentTo(IType other, Stack<(IType, IType)>? dependantEqualities) 20 | { 21 | if (ReferenceEquals(this, other)) 22 | { 23 | return true; 24 | } 25 | 26 | if (other is IUnion union && Options.Length == union.Options.Length) 27 | { 28 | return 29 | Options 30 | .Zip(union.Options, (a, b) => a.IsEquivalentTo(b, dependantEqualities)) 31 | .All(x => x); 32 | } 33 | 34 | return false; 35 | } 36 | 37 | bool IType.IsSubtypeOf(IType other) 38 | { 39 | if (ReferenceEquals(this, other)) 40 | { 41 | return true; 42 | } 43 | 44 | return Options.All(x => x.IsSubtypeOf(other)); 45 | } 46 | 47 | IType IType.Substitute(ImmutableArrayDictionary substitutions, Dictionary substituted) 48 | => substituted.TryGetValue(this, out var substitution) ? substitution : new SubstitutedUnion(this, substitutions, substituted); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/SourceDocument.cs: -------------------------------------------------------------------------------- 1 | using Antlr4.Runtime; 2 | using FluentLang.Compiler.Diagnostics; 3 | using FluentLang.Compiler.Generated; 4 | using FluentLang.Compiler.Parsing; 5 | using FluentLang.Compiler.Symbols.Interfaces; 6 | using System; 7 | using System.Collections.Immutable; 8 | using System.IO; 9 | using static FluentLang.Compiler.Generated.FluentLangParser; 10 | using DiagnosticErrorListener = FluentLang.Compiler.Parsing.DiagnosticErrorListener; 11 | 12 | namespace FluentLang.Compiler.Symbols.Source 13 | { 14 | internal sealed class SourceDocument : IDocument 15 | { 16 | private readonly string _source; 17 | private readonly Lazy _syntaxTree; 18 | private ImmutableArray _diagnostics; 19 | 20 | public SourceDocument(string source) 21 | { 22 | _source = source; 23 | _syntaxTree = new Lazy(GetSyntaxTree); 24 | } 25 | 26 | public string FullName => "test.cs"; 27 | 28 | public Compilation_unitContext SyntaxTree => _syntaxTree.Value; 29 | 30 | public ImmutableArray Diagnostics 31 | { 32 | get 33 | { 34 | _ = _syntaxTree.Value; 35 | return _diagnostics; 36 | } 37 | } 38 | 39 | public Compilation_unitContext GetSyntaxTree() 40 | { 41 | var diagnostics = new DiagnosticBag(null!); 42 | var parser = ParserFactory.Create(_source, diagnostics); 43 | 44 | var compilationUnit = parser.compilation_unit(); 45 | _diagnostics = diagnostics.ToImmutableArray(); 46 | return compilationUnit; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/MethodBody/MethodBodySymbolContext.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Interfaces; 2 | using FluentLang.Compiler.Symbols.Interfaces.MethodBody; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Collections.Immutable; 6 | using System.Text; 7 | 8 | namespace FluentLang.Compiler.Symbols.Source.MethodBody 9 | { 10 | internal class MethodBodySymbolContext 11 | { 12 | public MethodBodySymbolContext(SourceSymbolContext sourceSymbolContext) 13 | { 14 | SourceSymbolContext = sourceSymbolContext; 15 | Locals = ImmutableList.Empty; 16 | } 17 | 18 | private MethodBodySymbolContext(SourceSymbolContext sourceSymbolContext, ImmutableList locals, IStatement? currentStatement) 19 | { 20 | SourceSymbolContext = sourceSymbolContext; 21 | Locals = locals; 22 | CurrentStatement = currentStatement; 23 | } 24 | 25 | public SourceSymbolContext SourceSymbolContext { get; } 26 | public ImmutableList Locals { get; } 27 | public IStatement? CurrentStatement { get; } 28 | 29 | public MethodBodySymbolContext WithLocal(ILocal local) 30 | => new MethodBodySymbolContext(SourceSymbolContext, Locals.Add(local), CurrentStatement); 31 | public MethodBodySymbolContext WithScope(IMethod method) 32 | => new MethodBodySymbolContext(SourceSymbolContext.WithScope(method), Locals.AddRange(method.ParameterLocals), CurrentStatement); 33 | public MethodBodySymbolContext WithStatement(IStatement statement) 34 | => new MethodBodySymbolContext(SourceSymbolContext, Locals, statement); 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /source/flc/Testing/TestRunner.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Shared; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | 7 | namespace FluentLang.flc.Testing 8 | { 9 | public class TestRunner 10 | { 11 | public static TestResult RunTests(Assembly compiledAssembly) 12 | { 13 | var methods = 14 | compiledAssembly 15 | .ExportedTypes 16 | .SelectMany(x => x.GetMethods(BindingFlags.Static | BindingFlags.Public)) 17 | .Where(x => 18 | x.GetParameters().Length == 0 19 | && (x.ReturnType == typeof(string) || x.ReturnType == typeof(bool))); 20 | 21 | var successes = new List(); 22 | var failures = new List<(string name, string message)>(); 23 | foreach (var method in methods) 24 | { 25 | object? result; 26 | try 27 | { 28 | result = method.Invoke(null, null); 29 | } 30 | catch (Exception e) 31 | { 32 | result = e.ToString(); 33 | } 34 | 35 | switch (result) 36 | { 37 | case true: 38 | case "": 39 | case null: 40 | successes.Add(method.Name); 41 | break; 42 | case false: 43 | failures.Add((method.Name, "failed")); 44 | break; 45 | case string message: 46 | failures.Add((method.Name, message)); 47 | break; 48 | default: 49 | Release.Fail("This location is thought to be unreachable"); 50 | break; 51 | } 52 | } 53 | 54 | return failures.Any() 55 | ? (TestResult)new TestResult.Failure(successes, failures) 56 | : new TestResult.Success(successes); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Substituted/SubstitutedInterfaceMethod.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.Shared; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Collections.Immutable; 7 | using System.Diagnostics; 8 | using System.Linq; 9 | 10 | namespace FluentLang.Compiler.Symbols.Substituted 11 | { 12 | [DebuggerDisplay("{Name}")] 13 | internal class SubstitutedInterfaceMethod : IInterfaceMethod 14 | { 15 | private readonly IInterfaceMethod _original; 16 | private readonly Lazy _returnType; 17 | private readonly Lazy> _parameters; 18 | 19 | public SubstitutedInterfaceMethod(IInterfaceMethod original, ImmutableArrayDictionary substitutions, Dictionary substituted) 20 | { 21 | _original = original; 22 | _returnType = new Lazy(_original.ReturnType.Substitute(substitutions, substituted)); 23 | _parameters = new Lazy>( 24 | () => _original.Parameters.Select(x => x.Substitute(substitutions, substituted)).ToImmutableArray()); 25 | } 26 | 27 | public string Name => _original.Name; 28 | 29 | public IType ReturnType => _returnType.Value; 30 | 31 | public ImmutableArray Parameters => _parameters.Value; 32 | 33 | public ImmutableArray AllDiagnostics => ImmutableArray.Empty; 34 | 35 | public IInterfaceMethod OriginalDefinition => _original.OriginalDefinition; 36 | 37 | void ISymbol.EnsureAllLocalDiagnosticsCollected() { } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /source/Runtime/FLObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Immutable; 3 | 4 | namespace FluentLang.Runtime 5 | { 6 | public class FLObject 7 | { 8 | public static FLObject Empty { get; } = new FLObject(); 9 | private FLObject() => _methods = ImmutableDictionary.Empty; 10 | 11 | private FLObject(ImmutableDictionary methods) 12 | { 13 | _methods = methods; 14 | } 15 | 16 | private readonly ImmutableDictionary _methods; 17 | 18 | public T GetMethod(MethodKey methodKey) where T : Delegate 19 | { 20 | if (!_methods.TryGetValue(methodKey, out var method)) 21 | { 22 | throw new InvalidOperationException( 23 | $@"Method with MethodKey {methodKey.ToString()} does not exist on this. 24 | Make sure this is running with the same versions of dependencies as it was compiled against."); 25 | } 26 | if (!(method is T t)) 27 | { 28 | throw new InvalidOperationException( 29 | $@"Method with MethodKey {methodKey.ToString()} is of type {method.GetType()} but was expected to be of type {typeof(T)}. 30 | Make sure this is running with the same versions of dependencies as it was compiled against."); 31 | } 32 | return t; 33 | } 34 | 35 | public FLObject With(MethodKey methodKey, Delegate method) 36 | { 37 | return new FLObject(_methods.SetItem(methodKey, method)); 38 | } 39 | } 40 | 41 | public static class FLObjectExtensions 42 | { 43 | public static FLObject ToTemp(this FLObject fLObject, out FLObject temp) 44 | { 45 | temp = fLObject; 46 | return fLObject; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /source/Compiler/Diagnostics/ErrorCode.cs: -------------------------------------------------------------------------------- 1 | namespace FluentLang.Compiler.Diagnostics 2 | { 3 | public enum ErrorCode 4 | { 5 | SyntaxError, 6 | DuplicateInterfaceDeclaration, 7 | DuplicateMethodDeclaration, 8 | TypeNotFound, 9 | CannotReferenceSelfAsAdditiveInterface, 10 | InvalidParseTree, 11 | MethodNotFound, 12 | AmbigiousInterfaceReference, 13 | AmbigiousMethodReference, 14 | MismatchedTypes, 15 | CanOnlyPatchInterface, 16 | CannotMixInNonInterface, 17 | CannotPatchInMethodWithoutParameters, 18 | ResultantTypeOfObjectPatchingExpressionIsNotSubtypeOfFirstParameterOfPatchedInMethod, 19 | InvalidArgument, 20 | InvalidIntegerLiteral, 21 | IntegerLiteralOutOfRange, 22 | InvalidCharLiteral, 23 | InvalidRealLiteral, 24 | NonBooleanCondition, 25 | NoBestType, 26 | InvalidLocalReference, 27 | MemberNotFound, 28 | LastStatementMustBeReturnStatement, 29 | OnlyLastStatementCanBeReturnStatement, 30 | ReturnTypeDoesNotMatch, 31 | MethodMustContainAtLeastOneStatement, 32 | HidesLocal, 33 | InvalidEscapeSequence, 34 | UseOfMethodWhichCapturesUnassignedLocals, 35 | ParametersShareNames, 36 | InvalidMetadataAssembly, 37 | CannotUseUnexportedInterfaceFromExportedMember, 38 | MultipleVersionsOfSameAssembly, 39 | TooManyOptionsInUnion, 40 | CannotMatchOnNonUnion, 41 | MatchNotExhaustive, 42 | CanOnlyCombineInterfaces, 43 | TypeParametersShareNames, 44 | CannotConstrainToPrimitive, 45 | WrongNumberOfTypeArguments, 46 | TypeArgumentDoesntMatchConstraints, 47 | RecursiveFunctionCallWithDifferentTypeArguments, 48 | PipedStaticInvocationExpressionCannotHaveQualifiedName, 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /source/Compiler.Tests.Unit/Symbols/Interfaces/VersionTests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using Version = FluentLang.Compiler.Symbols.Interfaces.Version; 3 | 4 | namespace FluentLang.Compiler.Tests.Unit.Symbols.Interfaces 5 | { 6 | public class VersionTests 7 | { 8 | [Fact] 9 | public void CanParseVersion() 10 | { 11 | Assert.True(Version.TryParse("1.2.3", out var version)); 12 | Assert.Equal(new Version(1, 2, 3), version); 13 | } 14 | 15 | [Fact] 16 | public void CanParseVersionWithLongBuild() 17 | { 18 | Assert.True(Version.TryParse($"1.2.{long.MaxValue}", out var version)); 19 | Assert.Equal(new Version(1, 2, long.MaxValue), version); 20 | } 21 | 22 | [Fact] 23 | public void CanParsePrerealeaseVersion() 24 | { 25 | Assert.True(Version.TryParse("1.2.0-alpha.3", out var version)); 26 | Assert.Equal(new Version(1, 2, 3, "alpha"), version); 27 | } 28 | 29 | [Fact] 30 | public void CanParsePrerealeaseVersionCOntaningExtraDashesDotsAndNumbers() 31 | { 32 | Assert.True(Version.TryParse("1.2.0-alpha-1.17.3", out var version)); 33 | Assert.Equal(new Version(1, 2, 3, "alpha-1.17"), version); 34 | } 35 | 36 | [Theory] 37 | [InlineData("1")] 38 | [InlineData("1,2,3")] 39 | [InlineData("1a.2.3")] 40 | [InlineData("1..3")] 41 | [InlineData("1-alpha.1")] 42 | [InlineData("1,2,3-alpha.1")] 43 | [InlineData("1a.2.3-alpha.1")] 44 | [InlineData("1..3-alpha.1")] 45 | [InlineData("1.2.1-alpha.1")] 46 | [InlineData("1.2.0-alpha")] 47 | [InlineData("1.2.0-alpha1")] 48 | public void DoesNotParseInvalidVersions(string str) 49 | { 50 | Assert.False(Version.TryParse(str, out _)); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/ErrorSymbols/ErrorType.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.Compiler.Symbols.Visitor; 4 | using FluentLang.Shared; 5 | using System.Collections.Generic; 6 | using System.Collections.Immutable; 7 | using System.Diagnostics.CodeAnalysis; 8 | 9 | namespace FluentLang.Compiler.Symbols.ErrorSymbols 10 | { 11 | internal class ErrorType : IType, IErrorSymbol 12 | { 13 | private ErrorType() { } 14 | 15 | public static ErrorType Instance { get; } = new ErrorType(); 16 | 17 | public ImmutableArray AllDiagnostics => ImmutableArray.Empty; 18 | 19 | bool IType.IsEquivalentTo(IType other, Stack<(IType, IType)>? dependantEqualities) 20 | { 21 | // We use ReferenceEquals since it ensures that AreEquavalent maintains an Equivalence Relation 22 | // If we want to treat ErrorTypes differently, we should explicitly check for IErrorSymbol 23 | return ReferenceEquals(this, other); 24 | } 25 | 26 | bool IType.IsSubtypeOf(IType other) 27 | { 28 | // We use ReferenceEquals since it ensures that IsSubtypeOf maintains a partial ordering 29 | // If we want to treat ErrorTypes differently, we should explicitly check for IErrorSymbol 30 | return ReferenceEquals(this, other); 31 | } 32 | 33 | void ISymbol.EnsureAllLocalDiagnosticsCollected() 34 | { 35 | } 36 | 37 | IType IType.Substitute(ImmutableArrayDictionary substitutions, Dictionary substituted) 38 | { 39 | return this; 40 | } 41 | 42 | [return: MaybeNull] 43 | public T Visit(ISymbolVisitor visitor) => default; 44 | } 45 | } -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/MethodBody/MixinPatch.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.Compiler.Symbols.Interfaces.MethodBody; 4 | using System; 5 | using System.Collections.Immutable; 6 | using static FluentLang.Compiler.Generated.FluentLangParser; 7 | 8 | namespace FluentLang.Compiler.Symbols.Source.MethodBody 9 | { 10 | internal sealed class MixinPatch : SymbolBase, IMixinPatch 11 | { 12 | private readonly ExpressionContext _context; 13 | private readonly MethodBodySymbolContext _methodBodySymbolContext; 14 | private readonly Lazy _expression; 15 | 16 | public MixinPatch( 17 | ExpressionContext context, 18 | MethodBodySymbolContext methodBodySymbolContext, 19 | DiagnosticBag diagnostics) : base(diagnostics) 20 | { 21 | _context = context; 22 | _methodBodySymbolContext = methodBodySymbolContext; 23 | _expression = new Lazy(BindExpression); 24 | } 25 | 26 | private IExpression BindExpression() 27 | { 28 | var expression = _context.BindExpression(_methodBodySymbolContext, _diagnostics); 29 | if (!(expression.Type is IInterface)) //TODO: do we want to make this check lazy? 30 | { 31 | _diagnostics.Add(new Diagnostic(new Location(_context), ErrorCode.CannotMixInNonInterface, ImmutableArray.Create(expression.Type))); 32 | } 33 | return expression; 34 | } 35 | 36 | public IExpression Expression => _expression.Value; 37 | 38 | protected override void EnsureAllLocalDiagnosticsCollected() 39 | { 40 | // Touch all lazy fields to force binding; 41 | 42 | _ = _expression.Value; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /source/Compiler.Tests.Unit/Symbols/SourceSymbols/MethodBodyTests/PipedStaticInvocationExpressionTests.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.TestUtils; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using Xunit; 7 | using Xunit.Abstractions; 8 | 9 | namespace FluentLang.Compiler.Tests.Unit.Symbols.SourceSymbols.MethodBodyTests 10 | { 11 | public class PipedStaticInvocationExpressionTests : TestBase 12 | { 13 | public PipedStaticInvocationExpressionTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) 14 | { 15 | } 16 | 17 | [Fact] 18 | public void CanBindPipedStaticInvocationExpression() 19 | { 20 | CreateAssembly(@" 21 | Main() : int { 22 | return 5..M(37); 23 | } 24 | M(a : int, b : int) : int { 25 | return a + b; 26 | }") 27 | .VerifyDiagnostics().VerifyEmit(expectedResult: 42); 28 | } 29 | 30 | [Fact] 31 | public void ErrorIfMethodNotFound() 32 | { 33 | CreateAssembly(@" 34 | Main() : int { 35 | return 5..M(37); 36 | }") 37 | .VerifyDiagnostics( 38 | new Diagnostic(new Location(new TextToken(@"return5..M(37);")), ErrorCode.ReturnTypeDoesNotMatch), 39 | new Diagnostic(new Location(new TextToken(@"M")), ErrorCode.MethodNotFound)); 40 | } 41 | 42 | [Fact] 43 | public void ErrorIfQualifiedNameUsed() 44 | { 45 | CreateAssembly(@" 46 | Main() : int { 47 | return 5..N.M(37); 48 | } 49 | namespace N { 50 | M(a : int, b : int) : int { 51 | return a + b; 52 | } 53 | }") 54 | .VerifyDiagnostics( 55 | new Diagnostic(new Location(new TextToken(@"N.M")), ErrorCode.PipedStaticInvocationExpressionCannotHaveQualifiedName)); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/ErrorSymbols/ErrorInterface.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using System.Collections.Generic; 4 | using System.Collections.Immutable; 5 | 6 | namespace FluentLang.Compiler.Symbols.ErrorSymbols 7 | { 8 | internal class ErrorInterface : IInterface, IErrorSymbol 9 | { 10 | private ErrorInterface() { } 11 | 12 | public static ErrorInterface Instance { get; } = new ErrorInterface(); 13 | 14 | public QualifiedName? FullyQualifiedName => new QualifiedName("", null); 15 | 16 | public bool IsExported => false; 17 | 18 | public ImmutableArray TypeParameters => ImmutableArray.Empty; 19 | 20 | public ImmutableArray TypeArguments => ImmutableArray.Empty; 21 | 22 | public ImmutableArray Methods => ImmutableArray.Empty; 23 | 24 | public ImmutableArray AllDiagnostics => ImmutableArray.Empty; 25 | 26 | bool IType.IsEquivalentTo(IType other, Stack<(IType, IType)>? dependantEqualities) 27 | { 28 | // We use ReferenceEquals since it ensures that AreEquavalent maintains an Equivalence Relation 29 | // If we want to treat ErrorTypes differently, we should explicitly check for IErrorSymbol 30 | return ReferenceEquals(this, other); 31 | } 32 | 33 | bool IType.IsSubtypeOf(IType other) 34 | { 35 | // We use ReferenceEquals since it ensures that IsSubtypeOf maintains a partial ordering 36 | // If we want to treat ErrorTypes differently, we should explicitly check for IErrorSymbol 37 | return ReferenceEquals(this, other); 38 | } 39 | 40 | void ISymbol.EnsureAllLocalDiagnosticsCollected() 41 | { 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/SourceUnion.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using System; 4 | using System.Collections.Immutable; 5 | using System.Linq; 6 | using static FluentLang.Compiler.Generated.FluentLangParser; 7 | 8 | namespace FluentLang.Compiler.Symbols.Source 9 | { 10 | internal sealed class SourceUnion : SymbolBase, IUnion 11 | { 12 | private readonly UnionContext _context; 13 | private readonly SourceSymbolContext _sourceSymbolContext; 14 | private readonly bool _isExported; 15 | private readonly Lazy> _options; 16 | 17 | public SourceUnion( 18 | UnionContext context, 19 | SourceSymbolContext sourceSymbolContext, 20 | bool isExported, 21 | DiagnosticBag diagnostics) : base(diagnostics) 22 | { 23 | _context = context; 24 | _sourceSymbolContext = sourceSymbolContext; 25 | _isExported = isExported; 26 | _options = new Lazy>(BindOptions); 27 | } 28 | 29 | public ImmutableArray Options => _options.Value; 30 | 31 | private ImmutableArray BindOptions() 32 | { 33 | var options = 34 | _context 35 | .union_part_type() 36 | .Select(x => x.BindUnionPartType(_sourceSymbolContext, _isExported, _diagnostics)) 37 | .ToImmutableArray(); 38 | 39 | if (options.Length > 64) 40 | { 41 | _diagnostics.Add(new Diagnostic( 42 | new Location(_context), 43 | ErrorCode.TooManyOptionsInUnion, 44 | ImmutableArray.Create(this))); 45 | } 46 | 47 | return options; 48 | } 49 | 50 | protected override void EnsureAllLocalDiagnosticsCollected() 51 | { 52 | // Touch all lazy fields to force binding; 53 | 54 | _ = _options.Value; 55 | } 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/SourceTypeParameter.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using System; 4 | using System.Collections.Immutable; 5 | using static FluentLang.Compiler.Generated.FluentLangParser; 6 | 7 | namespace FluentLang.Compiler.Symbols.Source 8 | { 9 | internal class SourceTypeParameter : SymbolBase, ITypeParameter 10 | { 11 | private readonly Type_parameterContext _context; 12 | private readonly SourceSymbolContext _sourceSymbolContext; 13 | private readonly bool _isExported; 14 | private readonly Lazy _constrainedTo; 15 | 16 | public SourceTypeParameter( 17 | Type_parameterContext context, 18 | SourceSymbolContext sourceSymbolContext, 19 | bool isExported, 20 | DiagnosticBag diagnostics) : base(diagnostics) 21 | { 22 | _context = context; 23 | _sourceSymbolContext = sourceSymbolContext; 24 | _isExported = isExported; 25 | Name = _context.UPPERCASE_IDENTIFIER().Symbol.Text; 26 | _constrainedTo = new Lazy(BindConstrainedTo); 27 | } 28 | 29 | private IType? BindConstrainedTo() 30 | { 31 | var type = _context.type_declaration()?.type().BindType(_sourceSymbolContext, _isExported, _diagnostics); 32 | if (type is Primitive) 33 | { 34 | _diagnostics.Add(new Diagnostic( 35 | new Location(_context.type_declaration()), 36 | ErrorCode.CannotConstrainToPrimitive, 37 | ImmutableArray.Create(this, type))); 38 | } 39 | return type; 40 | } 41 | 42 | public string Name { get; } 43 | 44 | public IType? ConstrainedTo => _constrainedTo.Value; 45 | 46 | protected override void EnsureAllLocalDiagnosticsCollected() 47 | { 48 | // Touch all lazy fields to force binding; 49 | 50 | _ = _constrainedTo.Value; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Visitor/ISymbolVisitor.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Interfaces; 2 | using FluentLang.Compiler.Symbols.Interfaces.MethodBody; 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | namespace FluentLang.Compiler.Symbols.Visitor 6 | { 7 | public interface ISymbolVisitor 8 | { 9 | [return: MaybeNull] T Visit(IBinaryOperatorExpression binaryOperatorExpression); 10 | [return: MaybeNull] T Visit(IMixinPatch mixinPatch); 11 | [return: MaybeNull] T Visit(IMethodPatch methodPatch); 12 | [return: MaybeNull] T Visit(IStaticInvocationExpression staticInvocationExpression); 13 | [return: MaybeNull] T Visit(IParameter parameter); 14 | [return: MaybeNull] T Visit(ITypeParameter typeParameter); 15 | [return: MaybeNull] T Visit(IInterfaceMethod interfaceMethod); 16 | [return: MaybeNull] T Visit(IUnion union); 17 | [return: MaybeNull] T Visit(IInterface @interface); 18 | [return: MaybeNull] T Visit(IMatchExpression matchExpression); 19 | [return: MaybeNull] T Visit(Primitive primitive); 20 | [return: MaybeNull] T Visit(IMethod method); 21 | [return: MaybeNull] T Visit(IReturnStatement returnStatement); 22 | [return: MaybeNull] T Visit(IPrefixUnaryOperatorExpression prefixUnaryOperatorExpression); 23 | [return: MaybeNull] T Visit(IMatchExpressionArm matchExpressionArm); 24 | [return: MaybeNull] T Visit(INewObjectExpression newObjectExpression); 25 | [return: MaybeNull] T Visit(IObjectPatchingExpression objectPatchingExpression); 26 | [return: MaybeNull] T Visit(IMemberInvocationExpression memberInvocationExpression); 27 | [return: MaybeNull] T Visit(ILocalReferenceExpression localReferenceExpression); 28 | [return: MaybeNull] T Visit(ILiteralExpression literalExpression); 29 | [return: MaybeNull] T Visit(IDeclarationStatement declarationStatement); 30 | [return: MaybeNull] T Visit(IConditionalExpression conditionalExpression); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Substituted/SubstitutedInterface.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.Shared; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Collections.Immutable; 7 | using System.Diagnostics; 8 | using System.Linq; 9 | 10 | namespace FluentLang.Compiler.Symbols.Substituted 11 | { 12 | [DebuggerDisplay("{FullyQualifiedName}")] 13 | internal class SubstitutedInterface : IInterface 14 | { 15 | private readonly IInterface _original; 16 | 17 | private readonly Lazy> _methods; 18 | private readonly Lazy> _typeArguments; 19 | 20 | public SubstitutedInterface(IInterface original, ImmutableArrayDictionary substitutions, Dictionary substituted) 21 | { 22 | substituted.Add(original, this); 23 | substituted.Add(this, this); 24 | _original = original; 25 | _methods = new Lazy>( 26 | () => original.Methods.Select(x => x.Substitute(substitutions, substituted)).ToImmutableArray()); 27 | _typeArguments = new Lazy>( 28 | () => original.TypeArguments.Select(x => x.Substitute(substitutions, substituted)).ToImmutableArray()); 29 | } 30 | 31 | public bool IsExported => _original.IsExported; 32 | 33 | public QualifiedName? FullyQualifiedName => _original.FullyQualifiedName; 34 | 35 | public ImmutableArray Methods => _methods.Value; 36 | 37 | public ImmutableArray TypeParameters => ImmutableArray.Empty; 38 | 39 | public ImmutableArray TypeArguments => _typeArguments.Value; 40 | 41 | public ImmutableArray AllDiagnostics => ImmutableArray.Empty; 42 | 43 | void ISymbol.EnsureAllLocalDiagnosticsCollected() 44 | { 45 | 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /source/WebIde/Backend/WasmMetadataReferenceProvider.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Emit; 2 | using FluentLang.Runtime; 3 | using FluentLang.Shared; 4 | using Microsoft.AspNetCore.Components; 5 | using Microsoft.CodeAnalysis; 6 | using System; 7 | using System.Collections.Immutable; 8 | using System.Linq; 9 | using System.Net.Http; 10 | using System.Reflection; 11 | using System.Threading.Tasks; 12 | 13 | namespace FluentLang.WebIde.Backend 14 | { 15 | public class WasmMetadataReferenceProvider : IMetadataReferenceProvider, IRequiresAsyncInitialize 16 | { 17 | private readonly HttpClient _httpClient; 18 | private readonly NavigationManager _navigationManager; 19 | 20 | public WasmMetadataReferenceProvider(HttpClient httpClient, NavigationManager navigationManager) 21 | { 22 | _httpClient = httpClient; 23 | _navigationManager = navigationManager; 24 | } 25 | 26 | public ImmutableArray MetadataReferences { get; private set; } 27 | 28 | public async ValueTask InitializeAsync() 29 | { 30 | var baseAddress = new Uri(_navigationManager.BaseUri); 31 | 32 | var references = ImmutableArray.CreateBuilder(); 33 | 34 | MetadataReferences = await _availableAssemblies.Distinct().ToAsyncEnumerable().SelectAwait(async x => 35 | { 36 | var stream = await _httpClient.GetStreamAsync(new Uri(baseAddress, $"_framework/_bin/{x}")); 37 | return MetadataReference.CreateFromStream(stream); 38 | }).ToImmutableArrayAsync(); 39 | } 40 | 41 | private static readonly string[] _availableAssemblies = 42 | { 43 | typeof(FLObject).Assembly.Location, 44 | typeof(ImmutableArray).Assembly.Location, 45 | typeof(AssemblyFileVersionAttribute).Assembly.Location, 46 | typeof(object).Assembly.Location, 47 | typeof(Span<>).Assembly.Location, 48 | typeof(Func<>).Assembly.Location, 49 | typeof(Attribute).Assembly.Location, 50 | "netstandard.dll", 51 | }; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /source/flc.Tests.Unit/DependencyLoading/DependencyAttributeReaderTests.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Compilation; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.Compiler.Symbols.Source; 4 | using FluentLang.flc.DependencyLoading; 5 | using FluentLang.TestUtils; 6 | using System.Collections.Immutable; 7 | using System.IO; 8 | using Xunit; 9 | using Xunit.Abstractions; 10 | 11 | namespace FluentLang.Compiler.Tests.Unit.DependencyLoading 12 | { 13 | public class DependencyAttributeReaderTests : TestBase 14 | { 15 | public DependencyAttributeReaderTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) 16 | { 17 | } 18 | 19 | [Fact] 20 | public void CanReadDependencies() 21 | { 22 | var dep1Name = "a1"; 23 | var dep2Name = "a2"; 24 | var dep1Version = new Version(1, 2, 3); 25 | var dep2Version = new Version(4, 5, 6, "alpha1"); 26 | var dep1 = new SourceAssembly( 27 | QualifiedName(dep1Name), 28 | dep1Version, 29 | ImmutableArray.Empty, 30 | ImmutableArray.Empty, 31 | _assemblyCompiler); 32 | var dep2 = new SourceAssembly( 33 | QualifiedName(dep2Name), 34 | dep2Version, 35 | ImmutableArray.Empty, 36 | ImmutableArray.Empty, 37 | _assemblyCompiler); 38 | var assembly = new SourceAssembly( 39 | QualifiedName("a"), 40 | new Version(0, 0, 0), 41 | ImmutableArray.Create(dep1, dep2), 42 | ImmutableArray.Empty, 43 | _assemblyCompiler); 44 | assembly.VerifyDiagnostics().VerifyEmit( 45 | testEmittedAssembly: (_, a, _1) => 46 | Assert.Equal(new[] 47 | { 48 | new Dependency(dep1Name, dep1Version.ToString()), 49 | new Dependency(dep2Name, dep2Version.ToString()) 50 | }, 51 | new DependencyAttributeReader().ReadDependencies(a))); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/SourceInterfaceMethod.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using System; 4 | using System.Collections.Immutable; 5 | using System.Diagnostics; 6 | using static FluentLang.Compiler.Generated.FluentLangParser; 7 | 8 | namespace FluentLang.Compiler.Symbols.Source 9 | { 10 | [DebuggerDisplay("{Name}")] 11 | internal sealed class SourceInterfaceMethod : SymbolBase, IInterfaceMethod 12 | { 13 | private readonly Method_signatureContext _context; 14 | private readonly SourceSymbolContext _sourceSymbolContext; 15 | private readonly bool _isExported; 16 | private readonly Lazy _returnType; 17 | private readonly Lazy> _parameters; 18 | 19 | public SourceInterfaceMethod( 20 | Method_signatureContext context, 21 | SourceSymbolContext sourceSymbolContext, 22 | bool isExported, 23 | DiagnosticBag diagnostics) : base(diagnostics) 24 | { 25 | _context = context; 26 | _sourceSymbolContext = sourceSymbolContext; 27 | _isExported = isExported; 28 | Name = context.UPPERCASE_IDENTIFIER().Symbol.Text; 29 | 30 | _returnType = new Lazy(BindReturnType); 31 | _parameters = new Lazy>(BindParameters); 32 | } 33 | 34 | private IType BindReturnType() 35 | { 36 | return _context.BindReturnType(_sourceSymbolContext, _isExported, _diagnostics); 37 | } 38 | 39 | private ImmutableArray BindParameters() 40 | { 41 | return 42 | _context 43 | .parameters() 44 | .BindParameters(_sourceSymbolContext, _isExported, _diagnostics); 45 | } 46 | 47 | public string Name { get; } 48 | 49 | public IType ReturnType => _returnType.Value; 50 | 51 | public ImmutableArray Parameters => _parameters.Value; 52 | 53 | protected override void EnsureAllLocalDiagnosticsCollected() 54 | { 55 | // Touch all lazy fields to force binding; 56 | 57 | _ = _returnType.Value; 58 | _ = _parameters.Value; 59 | } 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /source/WebIde/wwwroot/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Single Page Apps for GitHub Pages 6 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/IAssembly.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Compilation; 2 | using FluentLang.Compiler.Diagnostics; 3 | using System.Collections.Generic; 4 | using System.Collections.Immutable; 5 | using System.Diagnostics.CodeAnalysis; 6 | using System.Linq; 7 | using static MoreLinq.Extensions.DistinctByExtension; 8 | 9 | namespace FluentLang.Compiler.Symbols.Interfaces 10 | { 11 | public interface IAssembly : ISymbol 12 | { 13 | public QualifiedName Name { get; } 14 | public Version Version { get; } 15 | public ImmutableArray ReferencedAssemblies { get; } 16 | public ImmutableArray ReferencedAssembliesAndSelf { get; } 17 | public ImmutableArray Interfaces { get; } 18 | public ImmutableArray Methods { get; } 19 | 20 | internal sealed IEnumerable CalculateReferencedAssemblies(ImmutableArray directlyReferencedAssemblies, DiagnosticBag diagnostics) 21 | { 22 | { 23 | var allByName = 24 | directlyReferencedAssemblies 25 | .SelectMany(x => x.ReferencedAssembliesAndSelf) 26 | .Concat(directlyReferencedAssemblies) 27 | .GroupBy(x => x.Name) 28 | .Select(x => x.DistinctBy(x => x.Version)).ToList(); 29 | 30 | foreach (var multipleVersions in allByName.Where(x => x.Count() > 1)) 31 | { 32 | diagnostics.Add(new Diagnostic( 33 | new Location(), 34 | ErrorCode.MultipleVersionsOfSameAssembly, 35 | multipleVersions.ToImmutableArray())); 36 | } 37 | 38 | return 39 | allByName 40 | .Select(x => x.First()); 41 | } 42 | } 43 | 44 | public bool TryGetInterface(QualifiedName fullyQualifiedName, [NotNullWhen(true)] out IInterface? @interface); 45 | public bool TryGetMethod(QualifiedName fullyQualifiedName, [NotNullWhen(true)] out IMethod? method); 46 | public CompilationResult CompileAssembly( 47 | out ImmutableArray assemblyBytes, 48 | out ImmutableArray csharpBytes, 49 | out ImmutableArray pdbBytes); 50 | 51 | public bool TryGetAssemblyBytes(out ImmutableArray bytes); 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /source/flc/ProjectSystem/SolutionFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Newtonsoft.Json; 3 | using System; 4 | using System.IO.Abstractions; 5 | using System.Linq; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace FluentLang.flc.ProjectSystem 10 | { 11 | public class SolutionFactory 12 | { 13 | private readonly ILogger _logger; 14 | private readonly IFileSystem _fileSystem; 15 | 16 | public SolutionFactory(ILogger logger, IFileSystem fileSystem) 17 | { 18 | _logger = logger; 19 | _fileSystem = fileSystem; 20 | } 21 | 22 | public SolutionInfo Parse(string text) 23 | { 24 | _logger.LogDebug("Found Solution File:\n{0}", text); 25 | SolutionInfo solution; 26 | try 27 | { 28 | solution = JsonConvert.DeserializeObject(text); 29 | } 30 | catch (Exception e) 31 | { 32 | throw new FlcException("Invalid Solution File", e); 33 | } 34 | ValidateSolution(solution); 35 | return solution; 36 | } 37 | 38 | public async ValueTask ParseFromFileAsync( 39 | string filePath, 40 | CancellationToken cancellationToken = default) 41 | { 42 | return Parse(await _fileSystem.File.ReadAllTextAsync(filePath, cancellationToken).ConfigureAwait(false)); 43 | } 44 | 45 | private void ValidateSolution(SolutionInfo solutionInfo) 46 | { 47 | var projectNames = solutionInfo.Projects.Select(x => x.Name).ToHashSet(); 48 | 49 | foreach(var project in solutionInfo.Projects) 50 | { 51 | var references = 52 | project 53 | .References 54 | .Where(x => x.Type == Reference.ReferenceType.Project) 55 | .Select(x => x.Name); 56 | 57 | foreach (var reference in references) 58 | { 59 | if (project.Name == reference) 60 | throw new FlcException( 61 | $"{project.Name} cannot reference itself"); 62 | if (!projectNames.Contains(reference!)) 63 | throw new FlcException( 64 | $"{project.Name} cannot reference {reference} as no project exists with that name in this solution"); 65 | } 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /source/Shared/ImmutableArrayDictionary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Collections.Immutable; 5 | using System.Diagnostics.CodeAnalysis; 6 | using System.Linq; 7 | 8 | namespace FluentLang.Shared 9 | { 10 | public class ImmutableArrayDictionary : IReadOnlyDictionary 11 | { 12 | private ImmutableArray> _entries; 13 | 14 | public ImmutableArrayDictionary(ImmutableArray> entries) 15 | { 16 | _entries = entries; 17 | } 18 | 19 | public TValue this[TKey key] 20 | { 21 | get 22 | { 23 | foreach (var (k, v) in _entries) 24 | { 25 | if (EqualityComparer.Default.Equals(k, key)) 26 | { 27 | return v; 28 | } 29 | } 30 | throw new ArgumentOutOfRangeException(nameof(key)); 31 | } 32 | } 33 | 34 | public IEnumerable Keys => _entries.Select(x => x.Key); 35 | 36 | public IEnumerable Values => _entries.Select(x => x.Value); 37 | 38 | public int Count => _entries.Length; 39 | 40 | public bool ContainsKey(TKey key) 41 | { 42 | throw new NotImplementedException(); 43 | } 44 | 45 | public IEnumerator> GetEnumerator() 46 | { 47 | IEnumerable> enumerable = _entries; 48 | return enumerable.GetEnumerator(); 49 | } 50 | 51 | #pragma warning disable CS8767 // Nullability of reference types in type of parameter doesn't match implicitly implemented member because of nullability attributes. 52 | public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) 53 | #pragma warning restore CS8767 // Nullability of reference types in type of parameter doesn't match implicitly implemented member because of nullability attributes. 54 | { 55 | foreach (var (k, v) in _entries) 56 | { 57 | if (EqualityComparer.Default.Equals(k, key)) 58 | { 59 | value = v; 60 | return true; 61 | } 62 | } 63 | value = default!; 64 | return false; 65 | } 66 | 67 | IEnumerator IEnumerable.GetEnumerator() 68 | { 69 | return GetEnumerator(); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/MethodBody/MethodPatch.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.Compiler.Symbols.Interfaces.MethodBody; 4 | using System; 5 | using System.Collections.Immutable; 6 | using static FluentLang.Compiler.Generated.FluentLangParser; 7 | 8 | namespace FluentLang.Compiler.Symbols.Source.MethodBody 9 | { 10 | internal sealed class MethodPatch : SymbolBase, IMethodPatch 11 | { 12 | private readonly Method_referenceContext _context; 13 | private readonly MethodBodySymbolContext _methodBodySymbolContext; 14 | private readonly Lazy _method; 15 | private readonly Lazy> _typeArguments; 16 | 17 | public MethodPatch( 18 | Method_referenceContext context, 19 | MethodBodySymbolContext methodBodySymbolContext, 20 | DiagnosticBag diagnostics) : base(diagnostics) 21 | { 22 | _context = context; 23 | _methodBodySymbolContext = methodBodySymbolContext; 24 | _method = new Lazy(BindMethod); 25 | _typeArguments = new Lazy>(BindTypeArguments); 26 | } 27 | 28 | private ImmutableArray BindTypeArguments() 29 | { 30 | return 31 | _context 32 | .type_argument_list() 33 | .BindTypeArgumentList( 34 | _methodBodySymbolContext.SourceSymbolContext, 35 | _diagnostics); 36 | } 37 | 38 | private IMethod BindMethod() 39 | { 40 | var method = _methodBodySymbolContext.SourceSymbolContext.GetMethodOrError( 41 | _context.qualified_name().GetQualifiedName(), 42 | TypeArguments, 43 | out var diagnostic); 44 | if (diagnostic != null) 45 | _diagnostics.Add(diagnostic(new Location(_context))); 46 | 47 | _methodBodySymbolContext.WarnIfUseOfMethodWhichCapturesUnassignedLocals(method, _diagnostics, _context); 48 | 49 | return method; 50 | } 51 | 52 | public IMethod Method => _method.Value; 53 | public ImmutableArray TypeArguments => _typeArguments.Value; 54 | 55 | protected override void EnsureAllLocalDiagnosticsCollected() 56 | { 57 | // Touch all lazy fields to force binding; 58 | 59 | _ = _method.Value; 60 | _ = _typeArguments.Value; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /source/WebIde/Program.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Compilation; 2 | using FluentLang.Compiler.Diagnostics; 3 | using FluentLang.Compiler.Emit; 4 | using FluentLang.Compiler.Symbols; 5 | using FluentLang.WebIde.Backend; 6 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using System; 9 | using System.Net.Http; 10 | using System.Threading.Tasks; 11 | 12 | namespace FluentLang.WebIde 13 | { 14 | public class Program 15 | { 16 | public static async Task Main(string[] args) 17 | { 18 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 19 | builder.RootComponents.Add("app"); 20 | 21 | builder.Services.AddTransient(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 22 | builder.Services.AddLogging(); 23 | 24 | builder.Services.AddSingletonWhichRequiresAsyncInitialization(); 25 | builder.Services.AddSingleton(); 26 | builder.Services.AddSingleton(); 27 | builder.Services.AddSingleton(); 28 | builder.Services.AddSingleton(); 29 | builder.Services.AddSingleton(); 30 | builder.Services.AddSingleton(); 31 | var container = builder.Build(); 32 | foreach (var requiresAsyncLoad in container.Services.GetServices()) 33 | { 34 | await requiresAsyncLoad.InitializeAsync(); 35 | } 36 | await container.RunAsync(); 37 | } 38 | } 39 | 40 | internal static class ServiceCollectionExtensions 41 | { 42 | public static IServiceCollection AddSingletonWhichRequiresAsyncInitialization(this IServiceCollection services) 43 | where TService : class where TImplementation : class, IRequiresAsyncInitialize, TService 44 | { 45 | services.AddSingleton(); 46 | services.AddSingleton(x => x.GetRequiredService()); 47 | services.AddSingleton(x => x.GetRequiredService()); 48 | return services; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/QualifiedName.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace FluentLang.Compiler.Symbols 5 | { 6 | public sealed class QualifiedName : IEquatable 7 | { 8 | public QualifiedName(string name, QualifiedName? parent = null) 9 | { 10 | Name = name; 11 | Parent = parent; 12 | } 13 | 14 | public string Name { get; } 15 | 16 | public QualifiedName? Parent { get; } 17 | 18 | public QualifiedName Append(QualifiedName? qualifiedName) 19 | { 20 | if (qualifiedName is null) 21 | return this; 22 | 23 | var name = qualifiedName.Name; 24 | if (qualifiedName.Parent is null) 25 | return new QualifiedName(name, this); 26 | return new QualifiedName(name, Append(qualifiedName.Parent)); 27 | } 28 | 29 | public QualifiedName Prepend(QualifiedName? qualifiedName) 30 | { 31 | if (qualifiedName is null) 32 | return this; 33 | 34 | return qualifiedName.Append(this); 35 | } 36 | 37 | public bool Equals(QualifiedName? other) 38 | { 39 | if (other is null) 40 | return false; 41 | 42 | if (!Name.Equals(other.Name, StringComparison.Ordinal)) 43 | return false; 44 | 45 | if (Parent is null) 46 | return other.Parent is null; 47 | 48 | return Parent.Equals(other.Parent); 49 | } 50 | 51 | public override int GetHashCode() 52 | { 53 | return (Parent?.GetHashCode() ?? 0) * 17 + Name.GetHashCode(); 54 | } 55 | 56 | public override bool Equals(object? obj) 57 | { 58 | if (obj is QualifiedName other) 59 | { 60 | return Equals(other); 61 | } 62 | return false; 63 | } 64 | 65 | public override string ToString() 66 | { 67 | if (Parent is null) 68 | return Name; 69 | return Parent.ToString() + "." + Name; 70 | } 71 | 72 | public static bool operator ==(QualifiedName? a, QualifiedName? b) 73 | { 74 | if (a is null) 75 | return b is null; 76 | return a.Equals(b); 77 | } 78 | 79 | public static bool operator !=(QualifiedName? a, QualifiedName? b) 80 | { 81 | return !(a == b); 82 | } 83 | 84 | public static QualifiedName Parse(string name) => 85 | name.Split('.').Aggregate((QualifiedName?)null, (l, r) => new QualifiedName(r, l))!; 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /source/Compiler/Diagnostics/DiagnosticBag.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Interfaces; 2 | using System.Collections; 3 | using System.Collections.Concurrent; 4 | using System.Collections.Generic; 5 | 6 | namespace FluentLang.Compiler.Diagnostics 7 | { 8 | public class DiagnosticBag : IEnumerable 9 | { 10 | private readonly ConcurrentBag _topLevelDiagnostics = new ConcurrentBag(); 11 | private readonly ConcurrentBag _childDiagnosticBags = new ConcurrentBag(); 12 | private readonly ISymbol _forSymbol; 13 | 14 | public DiagnosticBag(ISymbol forSymbol) 15 | { 16 | _forSymbol = forSymbol; 17 | } 18 | 19 | public void Add(Diagnostic diagnostic) 20 | { 21 | _topLevelDiagnostics.Add(diagnostic); 22 | } 23 | 24 | public void AddRange(IEnumerable diagnostics) 25 | { 26 | foreach (var diagnostic in diagnostics) 27 | { 28 | _topLevelDiagnostics.Add(diagnostic); 29 | } 30 | } 31 | 32 | public DiagnosticBag CreateChildBag(ISymbol forSymbol) 33 | { 34 | var child = new DiagnosticBag(forSymbol); 35 | _childDiagnosticBags.Add(child); 36 | return child; 37 | } 38 | 39 | public IEnumerator GetEnumerator() 40 | { 41 | // https://stackoverflow.com/a/20335369/7607408 42 | // this will be more efficient for large trees. 43 | 44 | var stack = new Stack(); 45 | stack.Push(this); 46 | while (stack.Count > 0) 47 | { 48 | var current = stack.Pop(); 49 | foreach (var diagnostic in current._topLevelDiagnostics) 50 | yield return diagnostic; 51 | foreach (var child in current._childDiagnosticBags) 52 | stack.Push(child); 53 | } 54 | } 55 | 56 | IEnumerator IEnumerable.GetEnumerator() 57 | { 58 | return GetEnumerator(); 59 | } 60 | 61 | private bool _allCollected; 62 | 63 | public void EnsureAllDiagnosticsCollectedForSymbol() 64 | { 65 | if (_allCollected) 66 | return; 67 | 68 | _forSymbol.EnsureAllLocalDiagnosticsCollected(); 69 | 70 | foreach (var childBag in _childDiagnosticBags) 71 | { 72 | childBag.EnsureAllDiagnosticsCollectedForSymbol(); 73 | } 74 | 75 | _allCollected = true; 76 | } 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/SourceNamedInterface.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using System; 4 | using System.Collections.Immutable; 5 | using static FluentLang.Compiler.Generated.FluentLangParser; 6 | 7 | namespace FluentLang.Compiler.Symbols.Source 8 | { 9 | internal sealed class SourceNamedInterface : SymbolBase, IInterface 10 | { 11 | private readonly Interface_declarationContext _context; 12 | private readonly SourceSymbolContext _sourceSymbolContext; 13 | private readonly SourceAnonymousInterface _anonymousSourceInterface; 14 | private readonly Lazy> _typeParameters; 15 | 16 | public SourceNamedInterface( 17 | Interface_declarationContext context, 18 | SourceSymbolContext sourceSymbolContext, 19 | DiagnosticBag diagnostics) : base(diagnostics) 20 | { 21 | _anonymousSourceInterface = new SourceAnonymousInterface( 22 | context.anonymous_interface_declaration(), 23 | sourceSymbolContext.WithTypeParameters(() => TypeParameters), 24 | context.EXPORT() is { }, 25 | _diagnostics); 26 | _context = context; 27 | _sourceSymbolContext = sourceSymbolContext; 28 | FullyQualifiedName = new QualifiedName( 29 | context.UPPERCASE_IDENTIFIER().Symbol.Text, 30 | sourceSymbolContext.Scope is null 31 | ? sourceSymbolContext.NameSpace 32 | : null); 33 | _typeParameters = new Lazy>(GenerateTypeParameters); 34 | } 35 | 36 | public bool IsExported => _anonymousSourceInterface.IsExported; 37 | public QualifiedName? FullyQualifiedName { get; } 38 | public ImmutableArray TypeParameters => _typeParameters.Value; 39 | public ImmutableArray TypeArguments => ImmutableArray.Empty; 40 | public ImmutableArray Methods => _anonymousSourceInterface.Methods; 41 | 42 | private ImmutableArray GenerateTypeParameters() 43 | { 44 | return _context.type_parameter_list().BindTypeParameters(_sourceSymbolContext, IsExported, _diagnostics); 45 | } 46 | 47 | protected override void EnsureAllLocalDiagnosticsCollected() 48 | { 49 | // Touch all lazy fields to force binding; 50 | 51 | _ = _typeParameters.Value; 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /source/flc/Testing/TestResult.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Shared; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace FluentLang.flc.Testing 6 | { 7 | public abstract class TestResult 8 | { 9 | private TestResult() { } 10 | public class Success : TestResult 11 | { 12 | public Success(IEnumerable succeeded) 13 | { 14 | Succeeded = succeeded; 15 | } 16 | 17 | public IEnumerable Succeeded { get; } 18 | 19 | public override string GetMessage() 20 | { 21 | return $@"Test Run Succeeded: 22 | 23 | {Succeeded.Count()} Successes: 24 | {string.Join('\n', Succeeded)}"; 25 | } 26 | 27 | public override TestResult Merge(TestResult other) 28 | { 29 | if (other is Success success) 30 | return new Success(Succeeded.Concat(success.Succeeded)); 31 | else if (other is Failure failure) 32 | { 33 | return new Failure(Succeeded.Concat(failure.Succeeded), failure.Failed); 34 | } 35 | throw Release.Fail("This location is thought to be unreachable"); 36 | } 37 | } 38 | 39 | public class Failure: TestResult 40 | { 41 | public Failure(IEnumerable succeeded, IEnumerable<(string testName, string message)> failed) 42 | { 43 | Succeeded = succeeded; 44 | Failed = failed; 45 | } 46 | 47 | public IEnumerable Succeeded { get; } 48 | public IEnumerable<(string testName, string message)> Failed { get; } 49 | 50 | public override TestResult Merge(TestResult other) 51 | { 52 | if (other is Success success) 53 | return new Failure(Succeeded.Concat(success.Succeeded), Failed); 54 | else if (other is Failure failure) 55 | { 56 | return new Failure(Succeeded.Concat(failure.Succeeded), Failed.Concat(failure.Failed)); 57 | } 58 | throw Release.Fail("This location is thought to be unreachable"); 59 | } 60 | 61 | public override string GetMessage() 62 | { 63 | return $@"Test Run Failed: 64 | 65 | {Failed.Count()} Failures: 66 | {string.Join('\n', Failed.Select(x => $"{x.testName}: {x.message}"))} 67 | 68 | {Succeeded.Count()} Successes: 69 | {string.Join('\n', Succeeded)}"; 70 | } 71 | } 72 | 73 | public abstract TestResult Merge(TestResult other); 74 | 75 | public abstract string GetMessage(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /source/Compiler.Tests.Unit/Emit/MetadataTests.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.TestUtils; 2 | using Xunit; 3 | using Xunit.Abstractions; 4 | 5 | namespace FluentLang.Compiler.Tests.Unit.Emit 6 | { 7 | public class MetadataTests : TestBase 8 | { 9 | public MetadataTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) 10 | { 11 | } 12 | 13 | [Fact] 14 | public void CorrectMetadataEmittedForMethodReturningPrimitive() 15 | { 16 | CreateAssembly("export M() : int { return 5; }") 17 | .VerifyDiagnostics() 18 | .VerifyEmit(); 19 | } 20 | 21 | [Fact] 22 | public void CorrectMetadataEmittedForMethodAcceptingAndReturningInterface() 23 | { 24 | CreateAssembly("export M(a: { M(a : {}) : int; }) : { M(a : {}) : int; } { return a; }") 25 | .VerifyDiagnostics() 26 | .VerifyEmit(); 27 | } 28 | 29 | [Fact] 30 | public void CorrectMetadataEmittedForMethodWithMultipleParameters() 31 | { 32 | CreateAssembly("export M(a: int, b: bool) : string { return \"\"; }") 33 | .VerifyDiagnostics() 34 | .VerifyEmit(); 35 | } 36 | 37 | [Fact] 38 | public void CorrectMetadataEmittedForInterface() 39 | { 40 | CreateAssembly("export interface I { M() : int; }") 41 | .VerifyDiagnostics() 42 | .VerifyEmit(); 43 | } 44 | 45 | [Fact] 46 | public void CorrectMetadataEmittedForInterfaceReferencingExportedInterface() 47 | { 48 | CreateAssembly("export interface I { M() : I1; } export interface I1 {}") 49 | .VerifyDiagnostics() 50 | .VerifyEmit(); 51 | } 52 | 53 | [Fact] 54 | public void CorrectMetadataEmittedForInterfaceReferencingItself() 55 | { 56 | CreateAssembly("export interface I { M() : I; }") 57 | .VerifyDiagnostics() 58 | .VerifyEmit(); 59 | } 60 | 61 | [Fact] 62 | public void CorrectMetadataEmittedForMethodReferencingExportedInterfaceInParameter() 63 | { 64 | CreateAssembly(@" 65 | export interface I { } 66 | export M(a : I, b : { M() : I; }) : int { return 42; }") 67 | .VerifyDiagnostics() 68 | .VerifyEmit(); 69 | } 70 | 71 | [Fact] 72 | public void CorrectMetadataEmittedForMethodReferencingExportedInterfaceInReturnType() 73 | { 74 | CreateAssembly(@" 75 | export interface I { } 76 | export M() : I { return {}; }") 77 | .VerifyDiagnostics() 78 | .VerifyEmit(); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/MethodBody/LocalReferenceExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.ErrorSymbols; 3 | using FluentLang.Compiler.Symbols.Interfaces; 4 | using FluentLang.Compiler.Symbols.Interfaces.MethodBody; 5 | using System; 6 | using System.Collections.Immutable; 7 | using System.Linq; 8 | using static FluentLang.Compiler.Generated.FluentLangParser; 9 | 10 | namespace FluentLang.Compiler.Symbols.Source.MethodBody 11 | { 12 | 13 | internal sealed class LocalReferenceExpression : SymbolBase, ILocalReferenceExpression 14 | { 15 | private readonly Local_reference_expressionContext _context; 16 | private readonly MethodBodySymbolContext _methodBodySymbolContext; 17 | private readonly Lazy _local; 18 | 19 | public LocalReferenceExpression( 20 | Local_reference_expressionContext context, 21 | MethodBodySymbolContext methodBodySymbolContext, 22 | DiagnosticBag diagnostics) : base(diagnostics) 23 | { 24 | _context = context; 25 | _methodBodySymbolContext = methodBodySymbolContext; 26 | 27 | Identifier = context.LOWERCASE_IDENTIFIER().Symbol.Text; 28 | _local = new Lazy(BindLocal); 29 | } 30 | 31 | private ILocal BindLocal() 32 | { 33 | var local = _methodBodySymbolContext.Locals.FirstOrDefault(x => x.Identifier == Identifier); 34 | if (local != null) 35 | return local; 36 | 37 | _diagnostics.Add(new Diagnostic( 38 | new Location(_context.LOWERCASE_IDENTIFIER()), 39 | ErrorCode.InvalidLocalReference, 40 | ImmutableArray.Create(Identifier))); 41 | 42 | return new ErrorLocal(Identifier); 43 | } 44 | 45 | public string Identifier { get; } 46 | public ILocal Local => _local.Value; 47 | public IType Type => Local.Type; 48 | 49 | protected override void EnsureAllLocalDiagnosticsCollected() 50 | { 51 | _ = _local.Value; 52 | } 53 | 54 | class ErrorLocal : ILocal, IErrorSymbol 55 | { 56 | public ErrorLocal(string identifier) 57 | { 58 | Identifier = identifier; 59 | } 60 | 61 | public string Identifier { get; } 62 | 63 | public IType Type => ErrorType.Instance; 64 | 65 | public ImmutableArray AllDiagnostics => ImmutableArray.Empty; 66 | 67 | void ISymbol.EnsureAllLocalDiagnosticsCollected() { } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /source/flc/Program.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.flc.DependencyInjection; 2 | using Microsoft.Extensions.Logging; 3 | using StrongInject; 4 | using System.CommandLine; 5 | using System.CommandLine.Builder; 6 | using System.CommandLine.Invocation; 7 | using System.IO; 8 | using System.Threading.Tasks; 9 | 10 | namespace FluentLang.flc 11 | { 12 | internal class Program 13 | { 14 | private static async Task Main(string[] args) 15 | { 16 | var buildCommand = new Command("build") 17 | { 18 | new Argument("solution-file").ExistingOnly(), 19 | new Argument("output-directory"), 20 | new Option("--output-csharp") 21 | { 22 | Argument = new Argument(defaultValue: () => false) 23 | }, 24 | new Option("--verbosity") 25 | { 26 | Argument = new Argument(defaultValue: () => LogLevel.Information) 27 | }, 28 | new Option("--test") 29 | { 30 | Argument = new Argument(defaultValue: () => true) 31 | }, 32 | }; 33 | 34 | var runCommand = new Command("run") 35 | { 36 | new Argument("solution-file").ExistingOnly(), 37 | new Argument("project-name"), 38 | new Option("--verbosity") 39 | { 40 | Argument = new Argument(defaultValue: () => LogLevel.Information) 41 | }, 42 | }; 43 | 44 | var rootCommand = new RootCommand 45 | { 46 | buildCommand, 47 | runCommand, 48 | }; 49 | 50 | buildCommand.Handler = CommandHandler.Create( 51 | async (FileInfo solutionFile, FileInfo outputDirectory, bool outputCSharp, LogLevel verbosity, bool test) => 52 | { 53 | await using var container = new FlcContainer(verbosity); 54 | var success = await container.RunAsync(compiler => compiler.Build( 55 | solutionFile.FullName, 56 | outputDirectory.FullName, 57 | outputCSharp, 58 | test, 59 | cancellationToken: default)); 60 | return success ? 0 : 1; 61 | }); 62 | 63 | runCommand.Handler = CommandHandler.Create( 64 | async (FileInfo solutionFile, string projectName, LogLevel verbosity) => 65 | { 66 | await using var container = new FlcContainer(verbosity); 67 | await container.RunAsync(compiler => compiler.Run(solutionFile.FullName, projectName, default)); 68 | }); 69 | 70 | await rootCommand.InvokeAsync(args); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /source/Compiler/Compilation/AssemblyCompiler.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Emit; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using System.Collections.Immutable; 4 | using System.IO; 5 | using System.Threading; 6 | using Diagnostic = FluentLang.Compiler.Diagnostics.Diagnostic; 7 | 8 | namespace FluentLang.Compiler.Compilation 9 | { 10 | public class AssemblyCompiler : IAssemblyCompiler 11 | { 12 | private readonly FluentlangToCSharpEmitter _fluentlangToCSharpEmitter; 13 | private readonly CSharpToAssemblyCompiler _cSharpToAssemblyCompiler; 14 | 15 | public AssemblyCompiler( 16 | FluentlangToCSharpEmitter fluentlangToCSharpEmitter, 17 | CSharpToAssemblyCompiler cSharpToAssemblyCompiler) 18 | { 19 | _fluentlangToCSharpEmitter = fluentlangToCSharpEmitter; 20 | _cSharpToAssemblyCompiler = cSharpToAssemblyCompiler; 21 | } 22 | 23 | public CompilationResult CompileAssembly( 24 | IAssembly assembly, 25 | Stream outputStream, 26 | Stream? csharpOutputStream = null, 27 | Stream? pdbStream = null, 28 | CancellationToken cancellationToken = default) 29 | { 30 | cancellationToken.ThrowIfCancellationRequested(); 31 | var diagnostics = assembly.AllDiagnostics; 32 | if (diagnostics.Length > 0) 33 | return new CompilationResult( 34 | CompilationResultStatus.CodeErrors, 35 | diagnostics, 36 | ImmutableArray.Empty); 37 | 38 | cancellationToken.ThrowIfCancellationRequested(); 39 | csharpOutputStream ??= new MemoryStream(); 40 | var writer = new StreamWriter(csharpOutputStream); 41 | var reader = new StreamReader(csharpOutputStream); 42 | 43 | _fluentlangToCSharpEmitter.Emit(assembly, writer); 44 | 45 | cancellationToken.ThrowIfCancellationRequested(); 46 | 47 | csharpOutputStream.Position = 0; 48 | 49 | var emitResult = _cSharpToAssemblyCompiler.Compile( 50 | reader, 51 | assembly, 52 | outputStream, 53 | pdbStream, 54 | cancellationToken); 55 | 56 | if (!emitResult.Success) 57 | { 58 | return new CompilationResult( 59 | CompilationResultStatus.InternalErrors, 60 | ImmutableArray.Empty, 61 | emitResult.Diagnostics); 62 | } 63 | 64 | return new CompilationResult( 65 | CompilationResultStatus.Succeeded, 66 | ImmutableArray.Empty, 67 | emitResult.Diagnostics); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /source/flc/DependencyLoading/FileAssemblyLoader.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Shared; 2 | using Microsoft.Extensions.Logging; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Collections.Immutable; 6 | using System.Diagnostics; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Reflection; 10 | using System.Runtime.Loader; 11 | using System.Threading; 12 | using System.Threading.Tasks; 13 | 14 | namespace FluentLang.flc.DependencyLoading 15 | { 16 | public class FileAssemblyLoader : IAssemblyLoader 17 | { 18 | private readonly ILogger _logger; 19 | private readonly ImmutableArray _directoriesToCheck; 20 | 21 | public FileAssemblyLoader( 22 | ILogger logger, 23 | IEnumerable directoriesToCheck) 24 | { 25 | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 26 | _directoriesToCheck = directoriesToCheck?.ToImmutableArray() ?? throw new ArgumentNullException(nameof(directoriesToCheck)); 27 | } 28 | public async ValueTask TryLoadAssemblyAsync(AssemblyLoadContext assemblyLoadContext, Dependency dependency, CancellationToken cancellationToken = default) 29 | { 30 | var possiblePaths = 31 | _directoriesToCheck 32 | .Select(x => Path.Combine(x, dependency.Name + ".dll")); 33 | 34 | var assemblyLoadResult = await possiblePaths.ToAsyncEnumerable().SelectAwait(async x => 35 | { 36 | // Check if file exists to reduce nr of exceptions 37 | // no point injecting IFileSystem since it doesn't have 38 | // an abstraction for FileVersionInfo anyway 39 | if (File.Exists(x)) 40 | { 41 | try 42 | { 43 | if (FileVersionInfo.GetVersionInfo(x).FileVersion == dependency.Version) 44 | { 45 | var bytes = (await File.ReadAllBytesAsync(x)).UnsafeAsImmutableArray(); 46 | var assembly = assemblyLoadContext.LoadFromStream(bytes.ToStream()); 47 | return new AssemblyLoadResult(assembly, bytes); 48 | } 49 | } 50 | catch (FileNotFoundException) 51 | { 52 | } 53 | } 54 | return null; 55 | }).FirstOrDefaultAsync(x => x != null); 56 | 57 | if (assemblyLoadResult is null) 58 | { 59 | _logger.LogInformation("Could not find assembly {0} {1} in any library directory", dependency.Name, dependency.Version); 60 | } 61 | 62 | return assemblyLoadResult; 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /source/Compiler/Symbols/Primitive.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.Compiler.Symbols.Visitor; 4 | using FluentLang.Shared; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Collections.Immutable; 8 | using System.Diagnostics.CodeAnalysis; 9 | using System.Linq; 10 | 11 | namespace FluentLang.Compiler.Symbols 12 | { 13 | public sealed class Primitive : IType, IEquatable 14 | { 15 | [return: MaybeNull] 16 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 17 | => visitor.Visit(this); 18 | 19 | public static Primitive Bool { get; } = new Primitive(new QualifiedName("bool")); 20 | public static Primitive Int { get; } = new Primitive(new QualifiedName("int")); 21 | public static Primitive Double { get; } = new Primitive(new QualifiedName("double")); 22 | public static Primitive Char { get; } = new Primitive(new QualifiedName("char")); 23 | public static Primitive String { get; } = new Primitive(new QualifiedName("string")); 24 | 25 | private Primitive(QualifiedName fullyQualifiedName) 26 | { 27 | FullyQualifiedName = fullyQualifiedName; 28 | } 29 | 30 | public QualifiedName FullyQualifiedName { get; } 31 | 32 | public bool Equals(Primitive? other) 33 | { 34 | return ReferenceEquals(this, other); 35 | } 36 | 37 | public override bool Equals(object? obj) 38 | { 39 | return ReferenceEquals(this, obj); 40 | } 41 | 42 | public override int GetHashCode() 43 | { 44 | return base.GetHashCode(); 45 | } 46 | 47 | bool IType.IsEquivalentTo(IType other, Stack<(IType, IType)>? dependantEqualities) 48 | { 49 | return ReferenceEquals(this, other); 50 | } 51 | 52 | public bool IsSubtypeOf(IType other) 53 | { 54 | if (other is IUnion union) 55 | return union.Options.Any(x => IsSubtypeOf(x)); 56 | 57 | return ReferenceEquals(this, other); 58 | } 59 | 60 | public override string ToString() 61 | { 62 | return FullyQualifiedName.ToString(); 63 | } 64 | 65 | ImmutableArray ISymbol.AllDiagnostics => ImmutableArray.Empty; 66 | 67 | void ISymbol.EnsureAllLocalDiagnosticsCollected() 68 | { 69 | } 70 | 71 | IType IType.Substitute(ImmutableArrayDictionary substitutions, Dictionary substituted) 72 | { 73 | return this; 74 | } 75 | } 76 | } 77 | 78 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/ErrorSymbols/ErrorMethod.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.Compiler.Symbols.Interfaces.MethodBody; 4 | using FluentLang.Shared; 5 | using System.Collections.Immutable; 6 | using System.Diagnostics; 7 | using System.Linq; 8 | 9 | namespace FluentLang.Compiler.Symbols.ErrorSymbols 10 | { 11 | [DebuggerDisplay("{FullyQualifiedName}")] 12 | internal class ErrorMethod : IMethod, IErrorSymbol 13 | { 14 | public static ErrorMethod Instance { get; } = new ErrorMethod(new QualifiedName(""), 0); 15 | 16 | public ErrorMethod(QualifiedName name, int numParameters) 17 | { 18 | FullyQualifiedName = name; 19 | Parameters = Enumerable.Repeat(ErrorParameter.Instance, numParameters).ToImmutableArray(); 20 | } 21 | 22 | public QualifiedName FullyQualifiedName { get; } 23 | 24 | public ImmutableArray TypeParameters => ImmutableArray.Empty; 25 | 26 | public IType ReturnType => ErrorType.Instance; 27 | 28 | public ImmutableArray Parameters { get; } 29 | 30 | public ImmutableArray LocalInterfaces => ImmutableArray.Empty; 31 | 32 | public ImmutableArray LocalMethods => ImmutableArray.Empty; 33 | 34 | public IMethod? DeclaringMethod => null; 35 | 36 | public ImmutableArray AllDiagnostics => ImmutableArray.Empty; 37 | 38 | public ImmutableArray Statements => ImmutableArray.Empty; 39 | 40 | public ImmutableArray ParameterLocals => ImmutableArray.Empty; 41 | 42 | public IDeclarationStatement? InScopeAfter => null; 43 | 44 | ImmutableArray IMethod.DirectlyCapturedDeclaredLocals => throw Release.Fail("unreachable"); 45 | 46 | ImmutableArray IMethod.UsedLocalMethods => throw Release.Fail("unreachable"); 47 | 48 | public bool IsExported => false; 49 | 50 | public IAssembly DeclaringAssembly => throw Release.Fail("unreachable"); 51 | 52 | ImmutableArray IMethod.RequiredMethodKeys => 53 | ImmutableArray.Empty; 54 | 55 | ImmutableArray IMethod.DirectlyRequiredMethodKeys => 56 | ImmutableArray.Empty; 57 | 58 | void ISymbol.EnsureAllLocalDiagnosticsCollected() 59 | { 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /source/flc.Tests.Integration/FileAssemblyLoaderTests.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.flc.DependencyLoading; 2 | using FluentLang.Runtime; 3 | using Microsoft.Extensions.Logging.Abstractions; 4 | using System.Collections.Immutable; 5 | using System.Runtime.Loader; 6 | using System.Threading.Tasks; 7 | using Xunit; 8 | 9 | namespace FluentLang.Compiler.Tests.Integration 10 | { 11 | public class FileAssemblyLoaderTests 12 | { 13 | [Fact] 14 | public async Task CanLoadAssemblyFromFile() 15 | { 16 | var loader = new FileAssemblyLoader(NullLogger.Instance, ImmutableArray.Create("Data")); 17 | var assemblyLoadContext = new AssemblyLoadContext(null, true); 18 | var assembly = await loader.TryLoadAssemblyAsync( 19 | assemblyLoadContext, 20 | new Dependency("FluentLang.Runtime", "1.0.0.0")); 21 | Assert.NotNull(assembly?.Assembly.GetType(typeof(FLObject).FullName!)); 22 | assemblyLoadContext.Unload(); 23 | } 24 | 25 | [Fact] 26 | public async Task ReturnsNullWhenNameNotFound() 27 | { 28 | var loader = new FileAssemblyLoader(NullLogger.Instance, ImmutableArray.Create("Data")); 29 | var assemblyLoadContext = new AssemblyLoadContext(null, true); 30 | var assembly = await loader.TryLoadAssemblyAsync( 31 | assemblyLoadContext, 32 | new Dependency("FluentLang.abcdefg", "1.0.0.0")); 33 | Assert.Null(assembly); 34 | assemblyLoadContext.Unload(); 35 | } 36 | 37 | [Fact] 38 | public async Task ReturnsNullWhenVersionNotFound() 39 | { 40 | var loader = new FileAssemblyLoader(NullLogger.Instance, ImmutableArray.Create("Data")); 41 | var assemblyLoadContext = new AssemblyLoadContext(null, true); 42 | var assembly = await loader.TryLoadAssemblyAsync( 43 | assemblyLoadContext, 44 | new Dependency("FluentLang.Runtime", "1.2.3.4")); 45 | Assert.Null(assembly); 46 | assemblyLoadContext.Unload(); 47 | } 48 | 49 | [Fact] 50 | public async Task IfNotFoundInFirstPathLooksInSecond() 51 | { 52 | var loader = new FileAssemblyLoader(NullLogger.Instance, ImmutableArray.Create("notADirectory", "Data")); 53 | var assemblyLoadContext = new AssemblyLoadContext(null, true); 54 | var assembly = await loader.TryLoadAssemblyAsync( 55 | assemblyLoadContext, 56 | new Dependency("FluentLang.Runtime", "1.0.0.0")); 57 | Assert.NotNull(assembly?.Assembly.GetType(typeof(FLObject).FullName!)); 58 | assemblyLoadContext.Unload(); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/MethodBody/CharParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentLang.Shared; 3 | 4 | namespace FluentLang.Compiler.Symbols.Source.MethodBody 5 | { 6 | internal static class CharParser 7 | { 8 | public enum ParseCharResult 9 | { 10 | Succeeded, 11 | CharsEmpty, 12 | InvalidEscapeSequence, 13 | } 14 | 15 | public static ParseCharResult TryParseNextChar( 16 | ReadOnlySpan chars, 17 | out int charsConsumed, 18 | out char parsedChar) 19 | { 20 | if (chars.IsEmpty) 21 | { 22 | charsConsumed = 0; 23 | parsedChar = default; 24 | return ParseCharResult.CharsEmpty; 25 | } 26 | 27 | if (chars[0] != '\\') 28 | { 29 | parsedChar = chars[0]; 30 | charsConsumed = 1; 31 | return ParseCharResult.Succeeded; 32 | } 33 | 34 | if (chars.Length == 1) 35 | { 36 | parsedChar = chars[0]; 37 | charsConsumed = 1; 38 | return ParseCharResult.InvalidEscapeSequence; 39 | } 40 | 41 | switch (chars[1]) 42 | { 43 | // escaped characters that translate to themselves 44 | case '"': 45 | case '\\': 46 | case '\'': 47 | parsedChar = chars[1]; 48 | goto onParsed; 49 | case '0': 50 | parsedChar = '\u0000'; 51 | goto onParsed; 52 | case 'a': 53 | parsedChar = '\u0007'; 54 | goto onParsed; 55 | case 'b': 56 | parsedChar = '\u0008'; 57 | goto onParsed; 58 | case 'f': 59 | parsedChar = '\u000c'; 60 | goto onParsed; 61 | case 'n': 62 | parsedChar = '\u000a'; 63 | goto onParsed; 64 | case 'r': 65 | parsedChar = '\u000d'; 66 | goto onParsed; 67 | case 't': 68 | parsedChar = '\u0009'; 69 | goto onParsed; 70 | case 'v': 71 | parsedChar = '\u000b'; 72 | onParsed: 73 | charsConsumed = 2; 74 | return ParseCharResult.Succeeded; 75 | case 'u': 76 | if (chars.Length < 6 77 | || chars[2..4].Any(x => (x < '0' || x > '9') && (x < 'a' || x > 'f'))) 78 | { 79 | parsedChar = default; 80 | charsConsumed = Math.Min(6, chars.Length); 81 | return ParseCharResult.InvalidEscapeSequence; 82 | } 83 | parsedChar = (char)int.Parse( 84 | chars[2..6], 85 | System.Globalization.NumberStyles.HexNumber); 86 | charsConsumed = 6; 87 | return ParseCharResult.Succeeded; 88 | default: 89 | parsedChar = default; 90 | charsConsumed = 2; 91 | return ParseCharResult.InvalidEscapeSequence; 92 | } 93 | 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/Version.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Text.RegularExpressions; 4 | 5 | namespace FluentLang.Compiler.Symbols.Interfaces 6 | { 7 | public class Version : IEquatable 8 | { 9 | public Version(int major, int minor, string suffix = ""): this (major, minor, DateTimeOffset.UtcNow.ToUnixTimeSeconds(), suffix) 10 | { } 11 | 12 | 13 | internal Version(int major, int minor, long build, string suffix = "") 14 | { 15 | Major = major; 16 | Minor = minor; 17 | Build = build; 18 | Suffix = suffix; 19 | } 20 | 21 | public int Major { get; } 22 | public int Minor { get; } 23 | public long Build { get; } 24 | public string Suffix { get; } 25 | 26 | public DateTime Timestamp => DateTimeOffset.FromUnixTimeSeconds(Build).UtcDateTime; 27 | 28 | public override string ToString() 29 | { 30 | return Suffix is "" ? $"{Major}.{Minor}.{Build}" : $"{Major}.{Minor}.0-{Suffix}.{Build}"; 31 | } 32 | 33 | public override bool Equals(object? obj) 34 | { 35 | return obj is Version version && Equals(version); 36 | } 37 | 38 | public bool Equals(Version? other) 39 | { 40 | if (other is null) 41 | return false; 42 | return 43 | ReferenceEquals(this, other) 44 | || Major == other.Major 45 | && Minor == other.Minor 46 | && Build == other.Build 47 | && Suffix == other.Suffix; 48 | 49 | } 50 | 51 | public override int GetHashCode() 52 | { 53 | return HashCode.Combine(Major, Minor, Build, Suffix); 54 | } 55 | 56 | private static readonly Regex _versionRegex = new Regex("^(\\d+)\\.(\\d+)\\.(\\d+)$"); 57 | private static readonly Regex _preReleaseVersionRegex = new Regex("^(\\d+)\\.(\\d+)\\.0-(.*)\\.(\\d+)$"); 58 | public static bool TryParse(string str, [NotNullWhen(true)] out Version? version) 59 | { 60 | var match = _versionRegex.Match(str); 61 | if (match.Success) 62 | { 63 | version = new Version( 64 | int.Parse(match.Groups[1].Value), 65 | int.Parse(match.Groups[2].Value), 66 | long.Parse(match.Groups[3].Value)); 67 | return true; 68 | } 69 | match = _preReleaseVersionRegex.Match(str); 70 | if (match.Success) 71 | { 72 | version = new Version( 73 | int.Parse(match.Groups[1].Value), 74 | int.Parse(match.Groups[2].Value), 75 | long.Parse(match.Groups[4].Value), 76 | match.Groups[3].Value); 77 | return true; 78 | } 79 | version = null; 80 | return false; 81 | } 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Metadata/MetadataNamedInterface.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.Compiler.Symbols.Source; 4 | using FluentLang.Runtime.Metadata; 5 | using System; 6 | using System.Collections.Immutable; 7 | using System.Linq; 8 | 9 | namespace FluentLang.Compiler.Symbols.Metadata 10 | { 11 | internal sealed class MetadataNamedInterface : SymbolBase, IInterface 12 | { 13 | private readonly InterfaceAttribute _attribute; 14 | private readonly SourceSymbolContext _sourceSymbolContext; 15 | private readonly SourceAnonymousInterface _anonymousSourceInterface; 16 | private readonly Lazy> _typeParameters; 17 | 18 | public MetadataNamedInterface( 19 | InterfaceAttribute attribute, 20 | SourceSymbolContext sourceSymbolContext, 21 | DiagnosticBag diagnostics) : base(diagnostics) 22 | { 23 | _anonymousSourceInterface = new SourceAnonymousInterface( 24 | Utils.Parse( 25 | attribute.AnonymousInterfaceDeclaration, 26 | p => p.anonymous_interface_declaration_metadata().anonymous_interface_declaration(), 27 | _diagnostics), 28 | sourceSymbolContext.WithTypeParameters(() => TypeParameters), 29 | true, 30 | _diagnostics); 31 | _attribute = attribute; 32 | _sourceSymbolContext = sourceSymbolContext; 33 | FullyQualifiedName = QualifiedName.Parse(attribute.FullyQualifiedName); 34 | _typeParameters = new Lazy>(GenerateTypeParameters); 35 | } 36 | 37 | public bool IsExported => _anonymousSourceInterface.IsExported; 38 | public QualifiedName? FullyQualifiedName { get; } 39 | public ImmutableArray TypeParameters => _typeParameters.Value; 40 | public ImmutableArray TypeArguments => ImmutableArray.Empty; 41 | public ImmutableArray Methods => _anonymousSourceInterface.Methods; 42 | 43 | private ImmutableArray GenerateTypeParameters() 44 | { 45 | return 46 | _attribute 47 | .TypeParameters 48 | .Select(x => 49 | new SourceTypeParameter( 50 | Utils.Parse(x, p => p.type_parameter_metadata(), _diagnostics).type_parameter(), 51 | _sourceSymbolContext, 52 | isExported: true, 53 | _diagnostics)) 54 | .ToImmutableArray(); 55 | } 56 | 57 | protected override void EnsureAllLocalDiagnosticsCollected() 58 | { 59 | // Touch all lazy fields to force binding; 60 | 61 | _ = _typeParameters.Value; 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /source/Compiler.Tests.Unit/Symbols/Subtyping/PrimitiveTests.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.TestUtils; 4 | using Xunit; 5 | using Xunit.Abstractions; 6 | 7 | namespace FluentLang.Compiler.Tests.Unit.Symbols.Subtyping 8 | { 9 | public class PrimitiveTests : TestBase 10 | { 11 | public static Primitive[][] Primitives = 12 | { 13 | new [] { Primitive.Bool }, 14 | new [] { Primitive.Int }, 15 | new [] { Primitive.Double }, 16 | new [] { Primitive.Char }, 17 | new [] { Primitive.String }, 18 | }; 19 | 20 | public static Primitive[][] PrimitivePairs = 21 | { 22 | new [] { Primitive.Bool, Primitive.Int }, 23 | new [] { Primitive.Bool, Primitive.Double }, 24 | new [] { Primitive.Bool, Primitive.Char }, 25 | new [] { Primitive.Bool, Primitive.String }, 26 | new [] { Primitive.Int, Primitive.Bool }, 27 | new [] { Primitive.Int, Primitive.Double }, 28 | new [] { Primitive.Int, Primitive.Char }, 29 | new [] { Primitive.Int, Primitive.String }, 30 | new [] { Primitive.Double, Primitive.Bool }, 31 | new [] { Primitive.Double, Primitive.Int }, 32 | new [] { Primitive.Double, Primitive.Char }, 33 | new [] { Primitive.Double, Primitive.String }, 34 | new [] { Primitive.Char, Primitive.Bool }, 35 | new [] { Primitive.Char, Primitive.Int }, 36 | new [] { Primitive.Char, Primitive.Double }, 37 | new [] { Primitive.Char, Primitive.String }, 38 | new [] { Primitive.String, Primitive.Bool }, 39 | new [] { Primitive.String, Primitive.Int }, 40 | new [] { Primitive.String, Primitive.Double }, 41 | new [] { Primitive.String, Primitive.Char }, 42 | }; 43 | 44 | public PrimitiveTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) 45 | { 46 | } 47 | 48 | [Theory] 49 | [MemberData(nameof(Primitives))] 50 | public void PrimitivesAreSubtypesOfThemselves(Primitive primitive) 51 | { 52 | Assert.True(primitive.IsSubtypeOf(primitive)); 53 | } 54 | 55 | [Theory] 56 | [MemberData(nameof(PrimitivePairs))] 57 | public void PrimitivesAreNotSubtypesOfOtherPrimitives(Primitive a, Primitive b) 58 | { 59 | Assert.False(a.IsSubtypeOf(b)); 60 | } 61 | 62 | [Theory] 63 | [MemberData(nameof(Primitives))] 64 | public void PrimitivesAreNotSubtypesOfInterface_EvenWithSameName(Primitive primitive) 65 | { 66 | var @interface = (IInterface)new TestInterface { FullyQualifiedName = primitive.FullyQualifiedName }; 67 | Assert.False(primitive.IsSubtypeOf(@interface)); 68 | Assert.False(@interface.IsSubtypeOf(primitive)); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /source/Compiler.Tests.Unit/Emit/RegressionTests.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.TestUtils; 2 | using Xunit; 3 | using Xunit.Abstractions; 4 | 5 | namespace FluentLang.Compiler.Tests.Unit.Emit 6 | { 7 | public class RegressionTests : TestBase 8 | { 9 | public RegressionTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) 10 | { 11 | } 12 | 13 | [Fact, WorkItem("https://github.com/YairHalberstadt/fluentlang/issues/13")] 14 | public void StackOverflow1() 15 | { 16 | CreateAssembly(@" 17 | export interface Func { 18 | Invoke(param: TParam) : TResult; 19 | } 20 | 21 | namespace Sequences { 22 | export interface Sequence { 23 | Decompose() : { Head() : T; Tail() : Sequence; } | {}; 24 | } 25 | 26 | export Empty() : Sequence { 27 | return {} + Decompose; 28 | Decompose(a : {}) : { Head() : T; Tail() : Sequence; } | {} { 29 | return {}; 30 | } 31 | } 32 | 33 | export Map(source : Sequence, func : Func) : Sequence { 34 | let decomposed = source.Decompose(); 35 | return decomposed match { 36 | {} => Empty(); 37 | cons : { Head() : TSource; Tail() : Sequence; } => MapAndCombine(cons); 38 | }; 39 | MapAndCombine(cons : { Head() : TSource; Tail() : Sequence; }) : Sequence { 40 | return {} + Decompose; 41 | Decompose(a : {}) : { Head() : TResult; Tail() : Sequence; } | {} { 42 | return {} + Head + Tail; 43 | Head(b: {}) : TResult { return func.Invoke(cons.Head()); } 44 | Tail(b : {}) : Sequence { return Map(cons.Tail(), func); } 45 | } 46 | } 47 | } 48 | }") 49 | .VerifyDiagnostics() 50 | .VerifyEmit(); 51 | } 52 | 53 | [Fact, WorkItem("https://github.com/YairHalberstadt/fluentlang/issues/20")] 54 | public void InternalErrors1() 55 | { 56 | CreateAssembly(@"interface Counter 57 | { 58 | Increment() : Counter; 59 | Value() : int; 60 | } 61 | 62 | CreateCounter() : Counter 63 | { 64 | return {} + Increment, Value; 65 | Increment(counter : Counter) : Counter 66 | { 67 | let value = counter.Value(); 68 | return counter + Value; 69 | Value(this : {}) : int 70 | { 71 | return value + 1; 72 | } 73 | } 74 | 75 | Value(counter : {}) : int 76 | { 77 | return 0; 78 | } 79 | }") 80 | .VerifyDiagnostics() 81 | .VerifyEmit(); 82 | 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /source/TestUtils/TestBase.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Compilation; 2 | using FluentLang.Compiler.Emit; 3 | using FluentLang.Compiler.Symbols; 4 | using FluentLang.Compiler.Symbols.Interfaces; 5 | using FluentLang.Compiler.Symbols.Source; 6 | using Microsoft.Extensions.Logging; 7 | using System.Collections.Generic; 8 | using System.Collections.Immutable; 9 | using System.Linq; 10 | using Xunit; 11 | using Xunit.Abstractions; 12 | 13 | namespace FluentLang.TestUtils 14 | { 15 | public abstract class TestBase 16 | { 17 | protected readonly ITestOutputHelper _testOutputHelper; 18 | protected readonly IAssemblyCompiler _assemblyCompiler; 19 | protected readonly AssemblyFactory _assemblyFactory; 20 | 21 | protected TestBase(ITestOutputHelper testOutputHelper) 22 | { 23 | _testOutputHelper = testOutputHelper; 24 | _assemblyCompiler = new AssemblyCompiler( 25 | new FluentlangToCSharpEmitter(), 26 | new CSharpToAssemblyCompiler(LocalSystemDllsMetadataReferenceProvider.Instance, GetLogger())); 27 | _assemblyFactory = new AssemblyFactory(_assemblyCompiler); 28 | } 29 | 30 | protected IAssembly CreateAssembly(string source, string? name = null, Version? version = null, params IAssembly[] references) 31 | { 32 | var document = DocumentFactory.FromString(source); 33 | return new SourceAssembly( 34 | QualifiedName(name ?? "Test"), 35 | version: version ?? new Version(1, 0, 0), 36 | references.ToImmutableArray(), 37 | ImmutableArray.Create(document), 38 | _assemblyCompiler); 39 | } 40 | 41 | protected IAssembly CreateAssembly(IEnumerable sources) 42 | { 43 | return new SourceAssembly( 44 | QualifiedName("Test"), 45 | version: new Version(1, 0, 0), 46 | ImmutableArray.Empty, 47 | sources.Select(x => DocumentFactory.FromString(x)).ToImmutableArray(), 48 | _assemblyCompiler); 49 | } 50 | 51 | protected static QualifiedName QualifiedName(string qualifiedName) 52 | { 53 | return Compiler.Symbols.QualifiedName.Parse(qualifiedName); 54 | } 55 | 56 | protected static IMethod AssertGetMethod(IAssembly assembly, string qualifiedName) 57 | { 58 | Assert.True(assembly.TryGetMethod(QualifiedName(qualifiedName), out var method)); 59 | return method!; 60 | } 61 | 62 | protected static IInterface AssertGetInterface(IAssembly assembly, string qualifiedName) 63 | { 64 | Assert.True(assembly.TryGetInterface(QualifiedName(qualifiedName), out var @interface)); 65 | return @interface!; 66 | } 67 | 68 | public ILogger GetLogger() => new XunitLogger(_testOutputHelper); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /source/Compiler.Tests.Unit/Symbols/SourceSymbols/MethodBodyTests/ReturnStatementTests.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces.MethodBody; 3 | using FluentLang.TestUtils; 4 | using System.Linq; 5 | using Xunit; 6 | using Xunit.Abstractions; 7 | 8 | namespace FluentLang.Compiler.Tests.Unit.Symbols.SourceSymbols.MethodBodyTests 9 | { 10 | public class ReturnStatementTests : TestBase 11 | { 12 | public ReturnStatementTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) 13 | { 14 | } 15 | 16 | [Fact] 17 | public void CanReturnSameTypeAsMethod() 18 | { 19 | var assembly = CreateAssembly("M() : {} { return {}; }") 20 | .VerifyDiagnostics().VerifyEmit(); 21 | var m = AssertGetMethod(assembly, "M"); 22 | var returnStatement = Assert.IsAssignableFrom(m.Statements.Single()); 23 | Assert.True(returnStatement.Expression.Type.IsEquivalentTo(m.ReturnType)); 24 | } 25 | 26 | [Fact] 27 | public void CanReturnSubTypeOfMethod() 28 | { 29 | var assembly = CreateAssembly("M(param : { M() : bool; }) : {} { return param; }") 30 | .VerifyDiagnostics().VerifyEmit(); 31 | var m = AssertGetMethod(assembly, "M"); 32 | var returnStatement = Assert.IsAssignableFrom(m.Statements.Single()); 33 | Assert.True(returnStatement.Expression.Type.IsSubtypeOf(m.ReturnType)); 34 | } 35 | 36 | [Fact] 37 | public void CantReturnNonSubTypeOfMethod() 38 | { 39 | CreateAssembly("M() : {} { return 5; }") 40 | .VerifyDiagnostics( 41 | new Diagnostic(new Location(new TextToken(@"return5;")), ErrorCode.ReturnTypeDoesNotMatch)); 42 | } 43 | 44 | [Fact] 45 | public void ReturnStatementMustBeLastStatement() 46 | { 47 | CreateAssembly("M() : int { return 5; _ = 5; }") 48 | .VerifyDiagnostics( 49 | new Diagnostic(new Location(new TextToken(@"return5;")), ErrorCode.OnlyLastStatementCanBeReturnStatement), 50 | new Diagnostic(new Location(new TextToken(@"_=5;")), ErrorCode.LastStatementMustBeReturnStatement)); 51 | } 52 | 53 | [Fact] 54 | public void MustHaveReturnStatement() 55 | { 56 | CreateAssembly("M() : int { _ = 5; }") 57 | .VerifyDiagnostics( 58 | new Diagnostic(new Location(new TextToken(@"_=5;")), ErrorCode.LastStatementMustBeReturnStatement)); 59 | } 60 | 61 | [Fact] 62 | public void CannotHaveMultipleReturnStatements() 63 | { 64 | CreateAssembly("M() : int { return 5; return 5; }") 65 | .VerifyDiagnostics( 66 | new Diagnostic(new Location(new TextToken(@"return5;")), ErrorCode.OnlyLastStatementCanBeReturnStatement)); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /source/Compiler.Tests.Unit/Symbols/Equivalence/PrimitiveTests.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.TestUtils; 4 | using Xunit; 5 | using Xunit.Abstractions; 6 | 7 | namespace FluentLang.Compiler.Tests.Unit.Model.Equivalence 8 | { 9 | public class PrimitiveTests : TestBase 10 | { 11 | public static Primitive[][] Primitives = 12 | { 13 | new [] { Primitive.Bool }, 14 | new [] { Primitive.Int }, 15 | new [] { Primitive.Double }, 16 | new [] { Primitive.Char }, 17 | new [] { Primitive.String }, 18 | }; 19 | 20 | public static Primitive[][] PrimitivePairs = 21 | { 22 | new [] { Primitive.Bool, Primitive.Int }, 23 | new [] { Primitive.Bool, Primitive.Double }, 24 | new [] { Primitive.Bool, Primitive.Char }, 25 | new [] { Primitive.Bool, Primitive.String }, 26 | new [] { Primitive.Int, Primitive.Bool }, 27 | new [] { Primitive.Int, Primitive.Double }, 28 | new [] { Primitive.Int, Primitive.Char }, 29 | new [] { Primitive.Int, Primitive.String }, 30 | new [] { Primitive.Double, Primitive.Bool }, 31 | new [] { Primitive.Double, Primitive.Int }, 32 | new [] { Primitive.Double, Primitive.Char }, 33 | new [] { Primitive.Double, Primitive.String }, 34 | new [] { Primitive.Char, Primitive.Bool }, 35 | new [] { Primitive.Char, Primitive.Int }, 36 | new [] { Primitive.Char, Primitive.Double }, 37 | new [] { Primitive.Char, Primitive.String }, 38 | new [] { Primitive.String, Primitive.Bool }, 39 | new [] { Primitive.String, Primitive.Int }, 40 | new [] { Primitive.String, Primitive.Double }, 41 | new [] { Primitive.String, Primitive.Char }, 42 | }; 43 | 44 | public PrimitiveTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) 45 | { 46 | } 47 | 48 | [Theory] 49 | [MemberData(nameof(Primitives))] 50 | public void PrimitivesAreEqualToThemselves(Primitive primitive) 51 | { 52 | Assert.True(((IType)primitive).IsEquivalentTo(primitive)); 53 | } 54 | 55 | [Theory] 56 | [MemberData(nameof(PrimitivePairs))] 57 | public void PrimitivesAreNotEqualToOtherPrimitives(Primitive a, Primitive b) 58 | { 59 | Assert.False(((IType)a).IsEquivalentTo(b)); 60 | } 61 | 62 | [Theory] 63 | [MemberData(nameof(Primitives))] 64 | public void PrimitivesAreNotEquivalentToInterface_EvenWithSameName(Primitive primitive) 65 | { 66 | var @interface = (IInterface)new TestInterface { FullyQualifiedName = primitive.FullyQualifiedName }; 67 | Assert.False(((IType)primitive).IsEquivalentTo(@interface)); 68 | Assert.False(@interface.IsEquivalentTo(primitive)); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /source/flc.Tests.Unit/ProjectSystem/ProjectDependencyOrganizerTests.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.flc; 2 | using FluentLang.flc.ProjectSystem; 3 | using System.Collections.Immutable; 4 | using System.Linq; 5 | using Xunit; 6 | 7 | namespace FluentLang.Compiler.Tests.Unit.ProjectSystem 8 | { 9 | public class ProjectDependencyOrganizerTests 10 | { 11 | private ProjectInfo Project(string name, params string[] references) 12 | { 13 | return new ProjectInfo( 14 | name, 15 | new Version(0, 0), 16 | ImmutableArray.Create(""), 17 | references: references 18 | .Select(x => new Reference(Reference.ReferenceType.Project, name: x)) 19 | .ToImmutableArray()); 20 | } 21 | 22 | [Fact] 23 | public void ShouldDetectSimpleCycles() 24 | { 25 | var projects = new[] 26 | { 27 | Project("p1", "p2"), 28 | Project("p2", "p1"), 29 | }; 30 | Assert.Throws(() => ProjectDependencyOrganizer.CreateBuildOrder(projects).ToList()); 31 | } 32 | 33 | 34 | [Fact] 35 | public void ShouldDetectLongCycles() 36 | { 37 | var projects = new[] 38 | { 39 | Project("p1", "p3"), 40 | Project("p2", "p1"), 41 | Project("p3", "p2"), 42 | }; 43 | Assert.Throws(() => ProjectDependencyOrganizer.CreateBuildOrder(projects).ToList()); 44 | } 45 | 46 | [Fact] 47 | public void ShouldOrderDependenciesInOnlyOrderWhenOnlyOneAvailable() 48 | { 49 | var projects = new[] 50 | { 51 | Project("p4", "p2", "p3"), 52 | Project("p3", "p2"), 53 | Project("p2", "p1"), 54 | Project("p1"), 55 | }; 56 | 57 | Assert.Equal( 58 | new[] { projects[3], projects[2], projects[1], projects[0] }, 59 | ProjectDependencyOrganizer.CreateBuildOrder(projects)); 60 | } 61 | 62 | [Fact] 63 | public void ShouldOrderDependenciesInPassedInOrderWhenMultipleAvailable() 64 | { 65 | var projects = new[] 66 | { 67 | Project("p1"), 68 | Project("p4", "p2", "p3"), 69 | Project("p2", "p1"), 70 | Project("p3", "p1"), 71 | }; 72 | 73 | Assert.Equal( 74 | new[] { projects[0], projects[2], projects[3], projects[1] }, 75 | ProjectDependencyOrganizer.CreateBuildOrder(projects)); 76 | } 77 | 78 | [Fact] 79 | public void ShouldIgnoreNonProjectReferences() 80 | { 81 | var projects = new[] 82 | { 83 | new ProjectInfo( 84 | "p1", 85 | new Version(0, 0), 86 | ImmutableArray.Create(""), 87 | references: ImmutableArray.Create(new Reference(Reference.ReferenceType.Assembly, "", ""))), 88 | }; 89 | 90 | Assert.Equal( 91 | projects, 92 | ProjectDependencyOrganizer.CreateBuildOrder(projects)); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/MethodBody/StaticInvocationExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Immutable; 3 | using FluentLang.Compiler.Diagnostics; 4 | using FluentLang.Compiler.Symbols.Interfaces; 5 | using FluentLang.Compiler.Symbols.Interfaces.MethodBody; 6 | using static FluentLang.Compiler.Generated.FluentLangParser; 7 | 8 | namespace FluentLang.Compiler.Symbols.Source.MethodBody 9 | { 10 | internal sealed class StaticInvocationExpression : SymbolBase, IStaticInvocationExpression 11 | { 12 | private readonly Static_invocation_expressionContext _context; 13 | private readonly MethodBodySymbolContext _methodBodySymbolContext; 14 | private readonly Lazy> _arguments; 15 | private readonly Lazy _method; 16 | private readonly Lazy> _typeArguments; 17 | 18 | public StaticInvocationExpression( 19 | Static_invocation_expressionContext context, 20 | MethodBodySymbolContext methodBodySymbolContext, 21 | DiagnosticBag diagnostics) : base(diagnostics) 22 | { 23 | _context = context; 24 | _methodBodySymbolContext = methodBodySymbolContext; 25 | 26 | MethodName = _context.method_reference().qualified_name().GetQualifiedName(); 27 | _arguments = new Lazy>(BindArguments); 28 | _method = new Lazy(BindMethod); 29 | _typeArguments = new Lazy>(BindTypeArguments); 30 | } 31 | 32 | private ImmutableArray BindArguments() 33 | { 34 | return 35 | _context 36 | .invocation() 37 | .BindArguments(_methodBodySymbolContext, _diagnostics); 38 | } 39 | 40 | private ImmutableArray BindTypeArguments() 41 | { 42 | return _context.method_reference().type_argument_list().BindTypeArgumentList( 43 | _methodBodySymbolContext.SourceSymbolContext, 44 | _diagnostics); 45 | } 46 | 47 | private IMethod BindMethod() 48 | { 49 | return StaticInvocationMethodBinder.BindMethod( 50 | MethodName, 51 | TypeArguments, 52 | Arguments, 53 | _context.method_reference(), 54 | _methodBodySymbolContext, 55 | _diagnostics); 56 | } 57 | 58 | public QualifiedName MethodName { get; } 59 | 60 | public ImmutableArray Arguments => _arguments.Value; 61 | 62 | public IMethod Method => _method.Value; 63 | 64 | public IType Type => Method.ReturnType; 65 | 66 | public ImmutableArray TypeArguments => _typeArguments.Value; 67 | 68 | protected override void EnsureAllLocalDiagnosticsCollected() 69 | { 70 | _ = _arguments.Value; 71 | _ = _typeArguments.Value; 72 | _ = _method.Value; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /source/WebIde/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WebIde 8 | 9 | 10 | 11 | 13 | 14 | 16 | 17 | 18 | 19 | 27 | 40 | 46 | 47 | 48 | 49 | Loading... 50 | 51 |
52 | An unhandled error has occurred. 53 | Reload 54 | 🗙 55 |
56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /source/flc/DependencyInjection/FlcModule.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Compilation; 2 | using FluentLang.Compiler.Diagnostics; 3 | using FluentLang.Compiler.Emit; 4 | using FluentLang.Compiler.Symbols; 5 | using FluentLang.flc.DependencyLoading; 6 | using FluentLang.flc.ProjectSystem; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Logging; 9 | using StrongInject; 10 | using StrongInject.Modules; 11 | using System; 12 | using System.IO.Abstractions; 13 | 14 | namespace FluentLang.flc.DependencyInjection 15 | { 16 | [Register(typeof(ServiceCollection), Scope.SingleInstance, typeof(IServiceCollection))] 17 | [Register(typeof(FluentLangCompiler), Scope.SingleInstance)] 18 | [Register(typeof(FileSystem), Scope.SingleInstance, typeof(IFileSystem))] 19 | [Register(typeof(SolutionFactory), Scope.SingleInstance)] 20 | [Register(typeof(DependencyAttributeReader), Scope.SingleInstance)] 21 | [Register(typeof(DependencyLoader), typeof(IDependencyLoader))] 22 | [Register(typeof(ProjectLoader), typeof(IProjectLoader))] 23 | [Register(typeof(FluentlangToCSharpEmitter), Scope.SingleInstance)] 24 | [Register(typeof(CSharpToAssemblyCompiler), Scope.SingleInstance)] 25 | [Register(typeof(AssemblyCompiler), Scope.SingleInstance, typeof(IAssemblyCompiler))] 26 | [Register(typeof(DiagnosticFormatter), Scope.SingleInstance, typeof(IDiagnosticFormatter))] 27 | [Register(typeof(AssemblyFactory), Scope.SingleInstance)] 28 | [RegisterModule(typeof(CollectionsModule))] 29 | public class FlcModule 30 | { 31 | [DecoratorFactory] 32 | public static IServiceCollection ConfigureServices(IServiceCollection services, LogLevel logLevel) 33 | { 34 | services.AddLogging(builder => builder 35 | .AddConsole() 36 | .AddFilter(level => level >= logLevel)); 37 | 38 | return services; 39 | } 40 | 41 | [Factory(Scope.SingleInstance)] 42 | public static IServiceProvider GetServiceProvider(IServiceCollection services) => services.BuildServiceProvider(); 43 | 44 | [Factory(Scope.SingleInstance)] 45 | public static ILogger GetLogger(IServiceProvider services) => services.GetRequiredService>(); 46 | 47 | [Instance] 48 | public static IMetadataReferenceProvider MetadataReferenceProvider => LocalSystemDllsMetadataReferenceProvider.Instance; 49 | 50 | [Factory] 51 | public static IAssemblyLoader FileAssemblyLoader(ILogger logger, SolutionInfo solutionInfo) 52 | { 53 | return new FileAssemblyLoader(logger, solutionInfo.LibDirectories); 54 | } 55 | 56 | [Factory] 57 | public static IAssemblyLoader NugetAssemblyLoader(ILogger logger, SolutionInfo solutionInfo) 58 | { 59 | return new NugetAssemblyLoader(logger, GlobalPackagesFolderProvider.GlobalPackagesFolder, solutionInfo.LibDirectories); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /source/Compiler/Diagnostics/Location.cs: -------------------------------------------------------------------------------- 1 | using Antlr4.Runtime; 2 | using Antlr4.Runtime.Tree; 3 | using System; 4 | 5 | namespace FluentLang.Compiler.Diagnostics 6 | { 7 | public struct Location 8 | { 9 | private readonly IToken? _token; 10 | 11 | private readonly ParserRuleContext? _parseTree; 12 | 13 | private readonly Range _tokenRange; 14 | 15 | public Location(IToken token, Range tokenRange = default) 16 | { 17 | _token = token; 18 | _tokenRange = tokenRange.Equals(default) ? .. : tokenRange; 19 | _parseTree = null; 20 | } 21 | 22 | public Location(ParserRuleContext parseTree) 23 | { 24 | _token = null; 25 | _tokenRange = default; 26 | _parseTree = parseTree; 27 | } 28 | 29 | public Location(ITerminalNode terminalNode) : this(terminalNode.Symbol) 30 | { 31 | } 32 | 33 | public ReadOnlySpan GetText() 34 | { 35 | if (_parseTree is { } parseTree) 36 | return parseTree.GetText().AsSpan(); 37 | if (_token is { } token) 38 | return token.Text.AsSpan()[_tokenRange]; 39 | return ""; 40 | } 41 | 42 | public TextRange TextRange { 43 | get 44 | { 45 | if (_parseTree is { } parseTree) 46 | { 47 | var startLine = parseTree.Start.Line; 48 | var startColumn = parseTree.Start.Column; 49 | var stopLine = parseTree.Stop.Line; 50 | var stopColumn = parseTree.Stop.Column + parseTree.Stop.Text.Length; 51 | 52 | return new TextRange(new TextPoint(startLine, startColumn), new TextPoint(stopLine, stopColumn)); 53 | } 54 | if (_token is { } token) 55 | { 56 | var line = token.Line; 57 | var column = token.Column; 58 | var tokenLength = token.Text.Length; 59 | var (offset, length) = _tokenRange.GetOffsetAndLength(tokenLength); 60 | return new TextRange(new TextPoint(line, column + offset), new TextPoint(line, column + offset + length)); 61 | } 62 | return default; 63 | } 64 | } 65 | 66 | } 67 | 68 | public struct TextRange 69 | { 70 | public TextRange(TextPoint start, TextPoint end) 71 | { 72 | Start = start; 73 | End = end; 74 | } 75 | 76 | public TextPoint Start { get; } 77 | public TextPoint End { get; } 78 | 79 | public void Deconstruct(out TextPoint start, out TextPoint end) => (start, end) = (Start, End); 80 | public void Deconstruct(out int startLine, out int startColumn, out int endLine, out int endColumn) => 81 | (startLine, startColumn, endLine, endColumn) = (Start.Line, Start.Column, End.Line, End.Column); 82 | } 83 | 84 | public struct TextPoint 85 | { 86 | public TextPoint(int line, int column) 87 | { 88 | Line = line; 89 | Column = column; 90 | } 91 | 92 | public void Deconstruct(out int line, out int column) => (line, column) = (Line, Column); 93 | 94 | public int Line { get; } 95 | public int Column { get; } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/MethodBody/StaticInvocationMethodBinder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.Immutable; 3 | using System.Linq; 4 | using FluentLang.Compiler.Diagnostics; 5 | using FluentLang.Compiler.Symbols.ErrorSymbols; 6 | using FluentLang.Compiler.Symbols.Interfaces; 7 | using FluentLang.Compiler.Symbols.Interfaces.MethodBody; 8 | using static FluentLang.Compiler.Generated.FluentLangParser; 9 | 10 | namespace FluentLang.Compiler.Symbols.Source.MethodBody 11 | { 12 | internal static class StaticInvocationMethodBinder 13 | { 14 | public static IMethod BindMethod( 15 | QualifiedName MethodName, 16 | ImmutableArray TypeArguments, 17 | ImmutableArray Arguments, 18 | Method_referenceContext method_reference, 19 | MethodBodySymbolContext _methodBodySymbolContext, 20 | DiagnosticBag _diagnostics) 21 | { 22 | var methods = _methodBodySymbolContext.SourceSymbolContext.GetPossibleMethods(MethodName, TypeArguments); 23 | 24 | var matching = 25 | methods 26 | .Where(x => x.Parameters.Length == Arguments.Length) 27 | .Select(x => 28 | { 29 | Diagnostic? diagnostic = null; 30 | if (TypeArguments.Length > 0) 31 | { 32 | var typeParameters = x.TypeParameters; 33 | if (!SourceSymbolContextExtensions.HasValidTypeArguments( 34 | TypeArguments, 35 | typeParameters, 36 | out var diagnosticFunc)) 37 | { 38 | diagnostic = diagnosticFunc(new Location(method_reference)); 39 | } 40 | var substituted = x.Substitute( 41 | SourceSymbolContextExtensions.CreateTypeMap(TypeArguments, typeParameters), 42 | new Dictionary()); 43 | 44 | return (method: substituted, diagnostic); 45 | } 46 | return (method: x, diagnostic: null); 47 | }) 48 | .Where(x => x.method.Parameters.Zip(Arguments, (p, a) => a.Type.IsSubtypeOf(p.Type)).All(x => x)) 49 | .ToList(); 50 | 51 | if (matching.Count == 1) 52 | { 53 | var (target, diagnostic) = matching[0]; 54 | 55 | if (diagnostic != null) 56 | { 57 | _diagnostics.Add(diagnostic); 58 | } 59 | 60 | _methodBodySymbolContext.WarnIfUseOfMethodWhichCapturesUnassignedLocals(target, _diagnostics, method_reference); 61 | return target; 62 | } 63 | 64 | 65 | if (matching.Count == 0) 66 | { 67 | _diagnostics.Add(new Diagnostic( 68 | new Location(method_reference), 69 | ErrorCode.MethodNotFound, 70 | ImmutableArray.Create(MethodName, Arguments))); 71 | } 72 | else 73 | { 74 | _diagnostics.Add(new Diagnostic( 75 | new Location(method_reference), 76 | ErrorCode.AmbigiousMethodReference, 77 | ImmutableArray.Create(matching))); 78 | } 79 | 80 | return new ErrorMethod(MethodName, Arguments.Length); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /source/WebIde/Backend/EditorEngine.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Compilation; 2 | using FluentLang.Compiler.Symbols; 3 | using FluentLang.Compiler.Symbols.Interfaces; 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.CSharp; 6 | using Microsoft.CodeAnalysis.Text; 7 | using System; 8 | using System.Collections.Immutable; 9 | using System.IO; 10 | using System.Linq; 11 | using System.Reflection; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | using Diagnostic = FluentLang.Compiler.Diagnostics.Diagnostic; 15 | 16 | namespace FluentLang.WebIde.Backend 17 | { 18 | public class EditorEngine 19 | { 20 | private readonly AssemblyFactory _assemblyFactory; 21 | 22 | private readonly IAssemblyCompiler _assemblyCompiler; 23 | 24 | public EditorEngine(AssemblyFactory assemblyFactory, IAssemblyCompiler assemblyCompiler) 25 | { 26 | _assemblyFactory = assemblyFactory; 27 | _assemblyCompiler = assemblyCompiler; 28 | } 29 | 30 | public async Task CompileAndRun(string source, CancellationToken token) 31 | { 32 | var result = new Result(); 33 | 34 | using var assemblyStream = new MemoryStream(); 35 | using var csharpStream = new MemoryStream(); 36 | var compilationResult = _assemblyCompiler.CompileAssembly( 37 | _assemblyFactory.FromSource( 38 | QualifiedName.Parse("WebIde"), 39 | (0, 0, null), 40 | ImmutableArray.Empty, 41 | ImmutableArray.Create(DocumentFactory.FromString(source))), 42 | assemblyStream, 43 | csharpStream, 44 | cancellationToken: token); 45 | 46 | await Task.Yield(); 47 | token.ThrowIfCancellationRequested(); 48 | 49 | if (compilationResult.AssemblyDiagnostics.Any()) 50 | { 51 | result.Diagnostics = compilationResult 52 | .AssemblyDiagnostics; 53 | } 54 | else 55 | { 56 | csharpStream.Position = 0; 57 | result.EmittedCSharp = CSharpSyntaxTree.ParseText(SourceText.From(csharpStream)).GetRoot().NormalizeWhitespace().ToFullString(); 58 | 59 | await Task.Yield(); 60 | token.ThrowIfCancellationRequested(); 61 | 62 | try 63 | { 64 | var assembly = Assembly.Load(assemblyStream.ToArray()); 65 | 66 | await Task.Yield(); 67 | token.ThrowIfCancellationRequested(); 68 | 69 | var entryPoint = assembly.EntryPoint; 70 | if (entryPoint != null) 71 | { 72 | result.RunResult = entryPoint.Invoke(null, null)?.ToString() ?? ""; 73 | } 74 | } 75 | catch (Exception e) 76 | { 77 | result.RuntimeError = e.ToString(); 78 | } 79 | } 80 | 81 | return result; 82 | } 83 | 84 | public class Result 85 | { 86 | public string? EmittedCSharp { get; set; } 87 | public string? RunResult { get; set; } 88 | public string? RuntimeError { get; set; } 89 | public ImmutableArray Diagnostics { get; set; } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/MethodBody/PrefixUnaryOperatorExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.Compiler.Symbols.Interfaces.MethodBody; 4 | using FluentLang.Shared; 5 | using System; 6 | using System.Collections.Immutable; 7 | using static FluentLang.Compiler.Generated.FluentLangParser; 8 | 9 | namespace FluentLang.Compiler.Symbols.Source.MethodBody 10 | { 11 | internal sealed class PrefixUnaryOperatorExpression : SymbolBase, IExpression, IPrefixUnaryOperatorExpression 12 | { 13 | private readonly Lazy _expression; 14 | private readonly Lazy _operator; 15 | private readonly Lazy _type; 16 | private readonly Prefix_unary_operator_expressionContext _context; 17 | private readonly MethodBodySymbolContext _methodBodySymbolContext; 18 | 19 | public PrefixUnaryOperatorExpression( 20 | Prefix_unary_operator_expressionContext context, 21 | MethodBodySymbolContext methodBodySymbolContext, 22 | DiagnosticBag diagnostics) : base(diagnostics) 23 | { 24 | _context = context; 25 | _methodBodySymbolContext = methodBodySymbolContext; 26 | 27 | _expression = new Lazy(BindExpression); 28 | _operator = new Lazy(BindOperator); 29 | _type = new Lazy(BindType); 30 | } 31 | 32 | private IExpression BindExpression() 33 | { 34 | return _context.expression().BindExpression(_methodBodySymbolContext, _diagnostics); 35 | } 36 | 37 | private Operator BindOperator() 38 | { 39 | return _context.prefix_unary_operator().Start.Type switch 40 | { 41 | MINUS => Operator.Minus, 42 | var type => throw new InvalidOperationException($"Unexpected operator: {type}") 43 | }; 44 | } 45 | 46 | private static bool OperatorIsDefinedOnType(Operator op, Primitive primitive) 47 | { 48 | if (op.Equals(Operator.Minus)) 49 | return primitive.Equals(Primitive.Double) 50 | || primitive.Equals(Primitive.Int); 51 | 52 | throw Release.Fail($"unexpected operator: {op}"); 53 | } 54 | 55 | private IType BindType() 56 | { 57 | if (!(Expression.Type is Primitive expType) 58 | || !OperatorIsDefinedOnType(Operator, expType)) 59 | { 60 | _diagnostics.Add(new Diagnostic( 61 | new Location(_context.expression()), 62 | ErrorCode.InvalidArgument, 63 | ImmutableArray.Create(Expression.Type, Operator))); 64 | } 65 | 66 | return Expression.Type; 67 | } 68 | 69 | public IExpression Expression => _expression.Value; 70 | public Operator Operator => _operator.Value; 71 | public IType Type => _type.Value; 72 | 73 | protected override void EnsureAllLocalDiagnosticsCollected() 74 | { 75 | // Touch all lazy fields to force binding; 76 | 77 | _ = _expression.Value; 78 | _ = _operator.Value; 79 | _ = _type.Value; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /source/Compiler/Symbols/Interfaces/IMethod.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Symbols.Interfaces.MethodBody; 2 | using FluentLang.Compiler.Symbols.Substituted; 3 | using FluentLang.Compiler.Symbols.Visitor; 4 | using FluentLang.Shared; 5 | using System.Collections.Generic; 6 | using System.Collections.Immutable; 7 | using System.Diagnostics.CodeAnalysis; 8 | using System.Linq; 9 | using static MoreLinq.Extensions.MaxByExtension; 10 | 11 | namespace FluentLang.Compiler.Symbols.Interfaces 12 | { 13 | public interface IMethod : IVisitableSymbol 14 | { 15 | [return: MaybeNull] 16 | T IVisitableSymbol.Visit(ISymbolVisitor visitor) 17 | => visitor.Visit(this); 18 | public bool IsExported { get; } 19 | public QualifiedName FullyQualifiedName { get; } 20 | public string Name => FullyQualifiedName.Name; 21 | public QualifiedName? Namespace => FullyQualifiedName.Parent; 22 | public IType ReturnType { get; } 23 | public ImmutableArray TypeParameters { get; } 24 | public ImmutableArray Parameters { get; } 25 | public ImmutableArray ParameterLocals { get; } 26 | public ImmutableArray LocalInterfaces { get; } 27 | public ImmutableArray LocalMethods { get; } 28 | public IMethod? DeclaringMethod { get; } 29 | public IAssembly DeclaringAssembly { get; } 30 | public ImmutableArray Statements { get; } 31 | public IDeclarationStatement? InScopeAfter { get; } 32 | public IMethod OriginalDefinition => this; 33 | internal ImmutableArray DirectlyCapturedDeclaredLocals { get; } 34 | internal ImmutableArray UsedLocalMethods { get; } 35 | private IEnumerable CalculateTransitivelyCapturedDeclaredLocals(HashSet visitedMethods) 36 | { 37 | visitedMethods.Add(this); 38 | return 39 | DirectlyCapturedDeclaredLocals 40 | .Concat(UsedLocalMethods 41 | .Where(x => !visitedMethods.Contains(x)) 42 | .SelectMany(x => x.CalculateTransitivelyCapturedDeclaredLocals(visitedMethods))); 43 | } 44 | 45 | //TODO: once base calls are sorted make this protected. 46 | internal sealed IDeclarationStatement? CalculateInScopeAfter() 47 | { 48 | return 49 | DeclaringMethod is null 50 | ? null 51 | : CalculateTransitivelyCapturedDeclaredLocals(new HashSet()) 52 | .Select(x => x.Declaration) 53 | .Intersect(DeclaringMethod.Statements.OfType()) 54 | .MaxBy(x => x.OrdinalPositionInMethod) 55 | .FirstOrDefault(); 56 | } 57 | 58 | internal IMethod Substitute(ImmutableArrayDictionary substitutions, Dictionary substituted) 59 | => new SubstitutedMethod(this, substitutions, substituted); 60 | 61 | internal ImmutableArray RequiredMethodKeys { get; } 62 | internal ImmutableArray DirectlyRequiredMethodKeys { get; } 63 | } 64 | } -------------------------------------------------------------------------------- /source/Compiler/Symbols/Source/MethodBody/MatchExpressionArm.cs: -------------------------------------------------------------------------------- 1 | using FluentLang.Compiler.Diagnostics; 2 | using FluentLang.Compiler.Symbols.Interfaces; 3 | using FluentLang.Compiler.Symbols.Interfaces.MethodBody; 4 | using FluentLang.Shared; 5 | using System; 6 | using System.Collections.Immutable; 7 | using System.Linq; 8 | using static FluentLang.Compiler.Generated.FluentLangParser; 9 | 10 | namespace FluentLang.Compiler.Symbols.Source.MethodBody 11 | { 12 | internal sealed class MatchExpressionArm : SymbolBase, IMatchExpressionArm 13 | { 14 | private readonly Match_expression_armContext _context; 15 | private readonly MethodBodySymbolContext _methodBodySymbolContext; 16 | private readonly Lazy _type; 17 | private readonly Lazy _expression; 18 | 19 | public MatchExpressionArm( 20 | Match_expression_armContext context, 21 | MethodBodySymbolContext methodBodySymbolContext, 22 | DiagnosticBag diagnostics) : base(diagnostics) 23 | { 24 | _context = context; 25 | _methodBodySymbolContext = methodBodySymbolContext; 26 | 27 | IdentifierName = _context.LOWERCASE_IDENTIFIER()?.Symbol.Text; 28 | Local = IdentifierName is null ? null : new MatchExpressionArmLocal(this); 29 | _type = new Lazy(BindType); 30 | _expression = new Lazy(BindExpression); 31 | } 32 | 33 | private IType BindType() 34 | { 35 | return _context.type().BindType(_methodBodySymbolContext.SourceSymbolContext, false, _diagnostics); 36 | } 37 | 38 | private IExpression BindExpression() 39 | { 40 | if (IdentifierName != null && _methodBodySymbolContext.Locals.Any(x => x.Identifier == IdentifierName)) 41 | { 42 | _diagnostics.Add(new Diagnostic( 43 | new Location(_context.LOWERCASE_IDENTIFIER()), 44 | ErrorCode.HidesLocal, 45 | ImmutableArray.Create(Local, _methodBodySymbolContext.Locals.First(x => x.Identifier == IdentifierName)))); 46 | } 47 | 48 | return _context.expression().BindExpression( 49 | Local is { } local ? _methodBodySymbolContext.WithLocal(local) : _methodBodySymbolContext, 50 | _diagnostics); 51 | } 52 | 53 | public IType Type => _type.Value; 54 | 55 | public string? IdentifierName { get; } 56 | 57 | public ILocal? Local { get; } 58 | 59 | public IExpression Expression => _expression.Value; 60 | 61 | protected override void EnsureAllLocalDiagnosticsCollected() 62 | { 63 | _ = _type.Value; 64 | _ = _expression.Value; 65 | } 66 | 67 | private class MatchExpressionArmLocal : ILocal 68 | { 69 | private readonly MatchExpressionArm _matchExpressionArm; 70 | 71 | public MatchExpressionArmLocal(MatchExpressionArm matchExpressionArm) 72 | { 73 | _matchExpressionArm = matchExpressionArm; 74 | } 75 | 76 | public string Identifier => _matchExpressionArm.IdentifierName ?? throw Release.Fail("Unreachable"); 77 | 78 | public IType Type => _matchExpressionArm.Type; 79 | } 80 | } 81 | } 82 | --------------------------------------------------------------------------------