loooots of stuff
the typechecker project can collect all the top level types from a file, which is pretty cool I think (except for pointers, those aren't implemented yet...)
This commit is contained in:
parent
3884422afb
commit
d6bdd08002
12
MeowLang.sln
12
MeowLang.sln
|
@ -4,6 +4,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "compiler", "compiler\compil
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "parser", "parser\parser.csproj", "{240E1F81-DB04-46CE-AB7B-60616B6D868C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "typechecker", "typechecker\typechecker.csproj", "{C8E93E36-E566-4610-A5A9-58EAA3A0A286}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "visitorgenerator", "visitorgenerator\visitorgenerator.csproj", "{21040DD5-B347-4A5C-B40C-1C470BA9A67A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -18,5 +22,13 @@ Global
|
|||
{240E1F81-DB04-46CE-AB7B-60616B6D868C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{240E1F81-DB04-46CE-AB7B-60616B6D868C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{240E1F81-DB04-46CE-AB7B-60616B6D868C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C8E93E36-E566-4610-A5A9-58EAA3A0A286}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C8E93E36-E566-4610-A5A9-58EAA3A0A286}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C8E93E36-E566-4610-A5A9-58EAA3A0A286}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C8E93E36-E566-4610-A5A9-58EAA3A0A286}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{21040DD5-B347-4A5C-B40C-1C470BA9A67A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{21040DD5-B347-4A5C-B40C-1C470BA9A67A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{21040DD5-B347-4A5C-B40C-1C470BA9A67A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{21040DD5-B347-4A5C-B40C-1C470BA9A67A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -179,13 +179,18 @@ Their type is an `u8` array with length of the string in bytes.
|
|||
```
|
||||
###### use
|
||||
```
|
||||
|
||||
|
||||
let a: [u8] = "foo";
|
||||
let b: u8 = a[1];
|
||||
let d: [s32; 4] = []{1,2,3,4};
|
||||
let e = [u8]{1,2,3,4};
|
||||
|
||||
let f = d[1:3]; // f has type [s32] (slice) now
|
||||
|
||||
|
||||
type Arr = [u8];
|
||||
let f = Arr[]{1,2,3};
|
||||
let g = Arr[]{1,2,3};
|
||||
|
||||
```
|
||||
##### struct
|
||||
|
@ -202,6 +207,7 @@ type Point = struct {
|
|||
z: s32;
|
||||
}
|
||||
|
||||
|
||||
type Options = struct {
|
||||
api: struct {
|
||||
int foo;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using meowlang.parser;
|
||||
using meowlang.typechecker;
|
||||
|
||||
namespace meowlang.compiler;
|
||||
|
||||
|
@ -9,6 +10,8 @@ static class Compiler
|
|||
{
|
||||
var path = "example.mew";
|
||||
var model = Parser.Parse(path);
|
||||
Console.WriteLine(model.AutoToString());
|
||||
Console.Write(model.AutoToString());
|
||||
if (model == null) return;
|
||||
TypeChecker.CheckAndInferTypes(model);
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\parser\parser.csproj" />
|
||||
<ProjectReference Include="..\typechecker\typechecker.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -94,9 +94,23 @@ type F<X> = Foo<Bar<X>>;
|
|||
type X = [Y; 10];
|
||||
|
||||
*/
|
||||
/*
|
||||
tuple X<A> (A,A,A);
|
||||
|
||||
|
||||
type X<T> = [T; 10];
|
||||
type X<T1, T2> = (T1, T2);
|
||||
|
||||
fn foo() {
|
||||
|
||||
let foo = X<int> {a: 1, b: 2};
|
||||
let bar = X<string> {a: "foo" , b: "bar"};
|
||||
|
||||
let test = Imported:X<int-nya>@bar []{1,2,3};
|
||||
|
||||
foo.a + 1;
|
||||
bar.a + 1;
|
||||
|
||||
meoooow let x-nyaaaa = []{1-nya, meow 2};
|
||||
|
||||
outer: loop let x = 1; x < 10; x += 1 {
|
||||
|
@ -109,4 +123,67 @@ fn foo() {
|
|||
default: bar(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct Bar {
|
||||
y: Foo;
|
||||
}
|
||||
|
||||
|
||||
struct Foo {
|
||||
x: enum {Foo, Bar};
|
||||
}
|
||||
|
||||
struct A<T> {
|
||||
x: B<T>;
|
||||
y: s32;
|
||||
}
|
||||
|
||||
struct B<T> {
|
||||
x: T;
|
||||
}
|
||||
|
||||
struct C {
|
||||
x: B<u32>;
|
||||
}
|
||||
|
||||
enum E {
|
||||
A,
|
||||
B,
|
||||
C
|
||||
}
|
||||
|
||||
enum F {
|
||||
A(u8),
|
||||
B(B<u16>),
|
||||
C(C)
|
||||
}
|
||||
|
||||
fn foo(x: u32) {}
|
||||
fn foo(x: s32) {}
|
||||
fn bar(x: f32) {}
|
||||
fn bar(x: f64) {}
|
||||
|
||||
fn test() {
|
||||
let x = 1; // no idea what the type is
|
||||
foo(x); // could be u32 or s32
|
||||
bar(x); // f32 and f64 conflict with u32 and s32
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
type A = int*;
|
||||
|
||||
struct B {
|
||||
x: (int, int);
|
||||
}*/
|
||||
|
||||
type A<T> = [(T,int); 10];
|
||||
|
||||
struct A<T> {
|
||||
x: B<T>;
|
||||
y: s32;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
namespace meowlang.parser;
|
||||
|
||||
public record ArrayTypeModel(Span Span, TypeReferenceModel Type, string Length) : TypeReferenceModel(Span);
|
||||
public record ArrayTypeModel(Span Span, TypeReferenceModel Type, uint Size) : TypeReferenceModel(Span);
|
|
@ -1,4 +1,4 @@
|
|||
namespace meowlang.parser;
|
||||
|
||||
public record ConstraintTopLevelConstructModel
|
||||
public record ConstraintDeclarationModel
|
||||
(Span Span, List<AttributeModel> Attributes, bool Pub, string Name, List<string> TypeNames, List<ConstraintRuleModel> Rules) : TopLevelConstructModel(Span);
|
|
@ -1,4 +1,3 @@
|
|||
using Antlr4.Runtime.Tree;
|
||||
using meowlang.parser.antlr;
|
||||
|
||||
namespace meowlang.parser;
|
||||
|
@ -33,7 +32,7 @@ public class DeclarationVisitorNya : MeowBaseVisitorNya<TopLevelConstructModel>
|
|||
functionReturns, constraints, body);
|
||||
}
|
||||
|
||||
public override ConstraintTopLevelConstructModel VisitConstraintDeclaration(
|
||||
public override ConstraintDeclarationModel VisitConstraintDeclaration(
|
||||
MeowParser.ConstraintDeclarationContext context)
|
||||
{
|
||||
var attributes = context.attribute().Select(x => new AttributeVisitorNya().Visit(x)).ToList();
|
||||
|
@ -41,7 +40,7 @@ public class DeclarationVisitorNya : MeowBaseVisitorNya<TopLevelConstructModel>
|
|||
var name = context.name.Text;
|
||||
var typeNames = context._types.Select(x => x.Text).ToList();
|
||||
var rules = context.constraintRule().Select(x => new ConstraintRuleVisitorNya().Visit(x)).ToList();
|
||||
return new ConstraintTopLevelConstructModel(context.GetSpan(), attributes, pub, name, typeNames, rules);
|
||||
return new ConstraintDeclarationModel(context.GetSpan(), attributes, pub, name, typeNames, rules);
|
||||
}
|
||||
|
||||
public override TopLevelConstructModel VisitStructDeclaration(MeowParser.StructDeclarationContext context)
|
||||
|
@ -53,7 +52,7 @@ public class DeclarationVisitorNya : MeowBaseVisitorNya<TopLevelConstructModel>
|
|||
var constraints = context.constraint()?.Select(x => new FunctionConstraintVisitorNya().Visit(x)).ToList() ??
|
||||
new List<ConstraintModel>();
|
||||
var structModel = new TypeReferenceVisitorNya().Visit(context.structType()) as StructTypeModel;
|
||||
return new StructTopLevelConstructModel(context.GetSpan(), attributes, pub, name, typeParameters, constraints, structModel!);
|
||||
return new StructDeclarationModel(context.GetSpan(), attributes, pub, name, typeParameters, constraints, structModel!);
|
||||
}
|
||||
|
||||
public override TopLevelConstructModel VisitEnumDeclaration(MeowParser.EnumDeclarationContext context)
|
||||
|
@ -66,7 +65,7 @@ public class DeclarationVisitorNya : MeowBaseVisitorNya<TopLevelConstructModel>
|
|||
new List<ConstraintModel>();
|
||||
|
||||
var enumModel = new TypeReferenceVisitorNya().Visit(context.enumType()) as EnumTypeModel;
|
||||
return new EnumTopLevelConstructModel(context.GetSpan(), attributes, pub, name, typeParameters, constraints, enumModel!);
|
||||
return new EnumDeclarationModel(context.GetSpan(), attributes, pub, name, typeParameters, constraints, enumModel!);
|
||||
}
|
||||
|
||||
public override TopLevelConstructModel VisitTupleDeclaration(MeowParser.TupleDeclarationContext context)
|
||||
|
@ -78,7 +77,7 @@ public class DeclarationVisitorNya : MeowBaseVisitorNya<TopLevelConstructModel>
|
|||
var constraints = context.constraint()?.Select(x => new FunctionConstraintVisitorNya().Visit(x)).ToList() ??
|
||||
new List<ConstraintModel>();
|
||||
var tupleModel = new TypeReferenceVisitorNya().Visit(context.tupleType()) as TupleTypeModel;
|
||||
return new TupleTopLevelConstructModel(context.GetSpan(), attributes, pub, name, typeParameters, constraints, tupleModel!);
|
||||
return new TupleDeclarationModel(context.GetSpan(), attributes, pub, name, typeParameters, constraints, tupleModel!);
|
||||
}
|
||||
|
||||
public override TopLevelConstructModel VisitTypeAlias(MeowParser.TypeAliasContext context)
|
||||
|
@ -90,12 +89,4 @@ public class DeclarationVisitorNya : MeowBaseVisitorNya<TopLevelConstructModel>
|
|||
var type = new TypeReferenceVisitorNya().Visit(context.typeReference());
|
||||
return new TypeAliasModel(context.GetSpan(), attributes, pub, name, typeParameters, type);
|
||||
}
|
||||
}
|
||||
|
||||
public class MeowBaseVisitorNya<T> : MeowBaseVisitor<T>
|
||||
{
|
||||
public T? TryVisit(IParseTree? tree)
|
||||
{
|
||||
return tree == null ? default : Visit(tree);
|
||||
}
|
||||
}
|
4
parser/EnumDeclarationModel.cs
Normal file
4
parser/EnumDeclarationModel.cs
Normal file
|
@ -0,0 +1,4 @@
|
|||
namespace meowlang.parser;
|
||||
|
||||
public record EnumDeclarationModel(Span Span, List<AttributeModel> Attributes, bool Pub, string Name, List<string> TypeParameters,
|
||||
List<ConstraintModel> Constraints, EnumTypeModel Enum) : TopLevelConstructModel(Span);
|
|
@ -1,4 +0,0 @@
|
|||
namespace meowlang.parser;
|
||||
|
||||
public record EnumTopLevelConstructModel(Span Span, List<AttributeModel> Attributes, bool Pub, string Name, List<string> TypeParameters,
|
||||
List<ConstraintModel> Constraints, EnumTypeModel Enum) : TopLevelConstructModel(Span);
|
|
@ -1,3 +1,5 @@
|
|||
namespace meowlang.parser;
|
||||
|
||||
public abstract record ExpressionModel(Span Span) : ModelBase(Span);
|
||||
public abstract record ExpressionModel(Span Span) : ModelBase(Span)
|
||||
{
|
||||
}
|
|
@ -160,7 +160,7 @@ public class ExpressionVisitorNya : MeowBaseVisitorNya<ExpressionModel>
|
|||
{
|
||||
var span = new Span(expression.Span.Filename, expression.Span.From, current.name.StopIndex);
|
||||
var name = current.name.Text;
|
||||
if (name.Contains('.') || !Char.IsDigit(name.TrimStart('0')[0]))
|
||||
if (name.Contains('.') || (char.IsDigit(name[0]) && !char.IsDigit(name.TrimStart('0')[0])))
|
||||
{
|
||||
throw new InvalidStructAccessorException(current.name, "");
|
||||
}
|
||||
|
|
|
@ -2,4 +2,19 @@ namespace meowlang.parser;
|
|||
|
||||
public record IfExpressionModel(Span Span, ExpressionModel Condition, BlockExpressionModel Block,
|
||||
List<(ExpressionModel Condition, BlockExpressionModel Block)> IfElses,
|
||||
BlockExpressionModel? Else) : ExpressionModel(Span);
|
||||
BlockExpressionModel? Else) : ExpressionModel(Span)
|
||||
{
|
||||
public override IEnumerable<ModelBase> GetChildren()
|
||||
{
|
||||
foreach (var modelBase in base.GetChildren())
|
||||
{
|
||||
yield return modelBase;
|
||||
}
|
||||
|
||||
foreach (var (condition, block) in IfElses)
|
||||
{
|
||||
yield return condition;
|
||||
yield return block;
|
||||
}
|
||||
}
|
||||
}
|
10
parser/InvalidArrayLengthException.cs
Normal file
10
parser/InvalidArrayLengthException.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using Antlr4.Runtime;
|
||||
|
||||
namespace meowlang.parser;
|
||||
|
||||
public class InvalidArrayLengthException : MeowVisitorException
|
||||
{
|
||||
public InvalidArrayLengthException(IToken token) : base(token, $"invalid array size `{token.Text}`")
|
||||
{
|
||||
}
|
||||
}
|
|
@ -399,7 +399,7 @@ Identifier: [a-zA-Z_][a-zA-Z_0-9]*;
|
|||
|
||||
NumericLiteral: DecimalNumberLiteral /*| HexadecimalNumberLiteral | OctalNumberLiteral | BinaryNumberLiteral*/;
|
||||
|
||||
fragment DecimalNumberLiteral: DecimalNumberPart ('.' DecimalNumberPart)? | '.' DecimalNumberPart;
|
||||
fragment DecimalNumberLiteral: '-'? DecimalNumberPart ('.' DecimalNumberPart)? | '.' DecimalNumberPart;
|
||||
fragment DecimalNumberPart: [0-9] [0-9_]*;
|
||||
BoolLiteral: 'true' | 'false' ;
|
||||
CharacterLiteral: '\'' (CharacterEscape | ~['\\]) '\'';
|
||||
|
|
12
parser/MeowBaseVisitorNya.cs
Normal file
12
parser/MeowBaseVisitorNya.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using Antlr4.Runtime.Tree;
|
||||
using meowlang.parser.antlr;
|
||||
|
||||
namespace meowlang.parser;
|
||||
|
||||
public class MeowBaseVisitorNya<T> : MeowBaseVisitor<T>
|
||||
{
|
||||
public T? TryVisit(IParseTree? tree)
|
||||
{
|
||||
return tree == null ? default : Visit(tree);
|
||||
}
|
||||
}
|
1303
parser/MeowModelVisitor.cs
Normal file
1303
parser/MeowModelVisitor.cs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,3 +1,30 @@
|
|||
using System.Collections;
|
||||
|
||||
namespace meowlang.parser;
|
||||
|
||||
public record ModelBase([property:Ignore] Span Span);
|
||||
public abstract record ModelBase([property: Ignore] Span Span)
|
||||
{
|
||||
public Dictionary<Guid, object> Metadata { get; } = new();
|
||||
|
||||
public virtual IEnumerable<ModelBase> GetChildren()
|
||||
{
|
||||
var properties = GetType().GetProperties();
|
||||
foreach (var propertyInfo in properties)
|
||||
{
|
||||
if (propertyInfo.PropertyType.IsAssignableTo(typeof(ModelBase)))
|
||||
{
|
||||
if (propertyInfo.GetValue(this) is ModelBase value) yield return value;
|
||||
}
|
||||
else if (propertyInfo.PropertyType.IsAssignableTo(typeof(IEnumerable)))
|
||||
{
|
||||
if (propertyInfo.GetValue(this) is IEnumerable<ModelBase> values)
|
||||
{
|
||||
foreach (var value in values)
|
||||
{
|
||||
yield return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
4
parser/StructDeclarationModel.cs
Normal file
4
parser/StructDeclarationModel.cs
Normal file
|
@ -0,0 +1,4 @@
|
|||
namespace meowlang.parser;
|
||||
|
||||
public record StructDeclarationModel(Span Span, List<AttributeModel> Attributes, bool Pub, string Name, List<string> TypeParameters,
|
||||
List<ConstraintModel> Constraints, StructTypeModel Struct) : TopLevelConstructModel(Span);
|
|
@ -1,4 +0,0 @@
|
|||
namespace meowlang.parser;
|
||||
|
||||
public record StructTopLevelConstructModel(Span Span, List<AttributeModel> Attributes, bool Pub, string Name, List<string> TypeParameters,
|
||||
List<ConstraintModel> Constraints, StructTypeModel Struct) : TopLevelConstructModel(Span);
|
|
@ -1,4 +1,19 @@
|
|||
namespace meowlang.parser;
|
||||
|
||||
public record SwitchExpressionModel(Span Span, ExpressionModel Expression,
|
||||
List<(LiteralModel Value, ExpressionModel? Body)> Cases, ExpressionModel? Default) : ExpressionModel(Span);
|
||||
List<(LiteralModel Value, ExpressionModel? Body)> Cases, ExpressionModel? Default) : ExpressionModel(Span)
|
||||
{
|
||||
public override IEnumerable<ModelBase> GetChildren()
|
||||
{
|
||||
foreach (var modelBase in base.GetChildren())
|
||||
{
|
||||
yield return modelBase;
|
||||
}
|
||||
|
||||
foreach (var (condition, block) in Cases)
|
||||
{
|
||||
yield return condition;
|
||||
if (block != null) yield return block;
|
||||
}
|
||||
}
|
||||
}
|
4
parser/TupleDeclarationModel.cs
Normal file
4
parser/TupleDeclarationModel.cs
Normal file
|
@ -0,0 +1,4 @@
|
|||
namespace meowlang.parser;
|
||||
|
||||
public record TupleDeclarationModel(Span Span, List<AttributeModel> Attributes, bool Pub, string Name, List<string> TypeParameters,
|
||||
List<ConstraintModel> Constraints, TupleTypeModel Tuple) : TopLevelConstructModel(Span);
|
|
@ -1,4 +0,0 @@
|
|||
namespace meowlang.parser;
|
||||
|
||||
public record TupleTopLevelConstructModel(Span Span, List<AttributeModel> Attributes, bool Pub, string Name, List<string> TypeParameters,
|
||||
List<ConstraintModel> Constraints, TupleTypeModel Tuple) : TopLevelConstructModel(Span);
|
|
@ -31,7 +31,14 @@ public class TypeReferenceVisitorNya : MeowBaseVisitorNya<TypeReferenceModel>
|
|||
return new SliceTypeModel(context.GetSpan(), type);
|
||||
}
|
||||
|
||||
return new ArrayTypeModel(context.GetSpan(), type, context.length.Text);
|
||||
if (context.length.Text.Contains('.') || context.length.Text.Contains('-'))
|
||||
{
|
||||
throw new InvalidArrayLengthException(context.length);
|
||||
}
|
||||
|
||||
var size = uint.Parse(context.length.Text.Replace("_", ""));
|
||||
|
||||
return new ArrayTypeModel(context.GetSpan(), type, size);
|
||||
}
|
||||
|
||||
public override TypeReferenceModel VisitTupleType(MeowParser.TupleTypeContext context)
|
||||
|
|
|
@ -40,6 +40,12 @@ public static class ObjectExtensions
|
|||
return $"\"{val}\"";
|
||||
}
|
||||
|
||||
if (type.IsAssignableTo(typeof(Guid)))
|
||||
{
|
||||
var val = o.ToString();
|
||||
return $"\"{val}\"";
|
||||
}
|
||||
|
||||
string json;
|
||||
|
||||
if (o is ITuple t)
|
||||
|
@ -48,7 +54,25 @@ public static class ObjectExtensions
|
|||
return values.AutoToString();
|
||||
}
|
||||
|
||||
if (type.IsAssignableTo(typeof(IEnumerable)))
|
||||
if (type.IsAssignableTo(typeof(IDictionary)))
|
||||
{
|
||||
var dictionary = (o as IDictionary);
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.Append('{');
|
||||
|
||||
var first = true;
|
||||
foreach (DictionaryEntry entry in dictionary!)
|
||||
{
|
||||
if (!first) sb.Append(',');
|
||||
first = false;
|
||||
sb.Append($"\"{entry.Key.ToString()}\": {entry.Value.AutoToString()}");
|
||||
}
|
||||
|
||||
sb.Append('}');
|
||||
json = sb.ToString();
|
||||
}
|
||||
else if (type.IsAssignableTo(typeof(IEnumerable)))
|
||||
{
|
||||
var enumerable = (o as IEnumerable)!.Cast<object>();
|
||||
var sb = new StringBuilder();
|
||||
|
|
|
@ -10,7 +10,7 @@ public class VisitorNya : MeowBaseVisitorNya<Model>
|
|||
public override Model VisitFile(MeowParser.FileContext context)
|
||||
{
|
||||
var imports = context.importStatement().Select(x => new ImportVisitorNya().Visit(x)).ToList();
|
||||
var declarations = context.topLevelDeclaration().Select(x => new DeclarationVisitorNya().Visit(x)).ToList();
|
||||
var declarations = context.topLevelDeclaration().Select(x => new DeclarationVisitorNya().Visit(x.unterminatedTopLevelDeclaration())).ToList();
|
||||
|
||||
return new Model(context.GetSpan(), imports, declarations);
|
||||
}
|
||||
|
|
3
typechecker/GenericTypeId.cs
Normal file
3
typechecker/GenericTypeId.cs
Normal file
|
@ -0,0 +1,3 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public record GenericTypeId(PossiblyGenericTypeRef Key, List<GenericTypeId> GenericParams);
|
20
typechecker/ModelExtensions.cs
Normal file
20
typechecker/ModelExtensions.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using meowlang.parser;
|
||||
|
||||
namespace meowlang.typechecker;
|
||||
|
||||
public static class ModelExtensions
|
||||
{
|
||||
private static readonly Guid ExpressionTypeKey = new Guid("6560485F-A4B0-4E24-A03D-D87776BBF9F4");
|
||||
|
||||
public static void SetType(this ExpressionModel m, TypeId t)
|
||||
{
|
||||
m.Metadata[ExpressionTypeKey] = t;
|
||||
}
|
||||
|
||||
public static TypeId? GetType(this ExpressionModel m)
|
||||
{
|
||||
m.Metadata.TryGetValue(ExpressionTypeKey, out var value);
|
||||
return value as TypeId;
|
||||
}
|
||||
|
||||
}
|
19
typechecker/PossiblyGenericTypeRef.cs
Normal file
19
typechecker/PossiblyGenericTypeRef.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public record PossiblyGenericTypeRef
|
||||
{
|
||||
public Guid? TypeKey { get; }
|
||||
public string? GenericParameterName { get; }
|
||||
|
||||
public bool IsGeneric => GenericParameterName != null;
|
||||
|
||||
public PossiblyGenericTypeRef(string genericParameterName)
|
||||
{
|
||||
GenericParameterName = genericParameterName;
|
||||
}
|
||||
|
||||
public PossiblyGenericTypeRef(Guid typeKey)
|
||||
{
|
||||
TypeKey = typeKey;
|
||||
}
|
||||
};
|
125
typechecker/Scope.cs
Normal file
125
typechecker/Scope.cs
Normal file
|
@ -0,0 +1,125 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public class Scope
|
||||
{
|
||||
private readonly Dictionary<string, TypeId> _variableScope = new();
|
||||
private readonly Dictionary<(string, int), Guid> _typeScope = new();
|
||||
|
||||
private readonly TypeRegistry _typeRegistry;
|
||||
|
||||
private readonly Scope? _parent;
|
||||
|
||||
|
||||
public Scope? DebugGetParent => _parent;
|
||||
public Dictionary<string, TypeId> DebugGetVariableScope => _variableScope;
|
||||
public Dictionary<(string, int), Guid> DebugGetTypeScope => _typeScope;
|
||||
|
||||
|
||||
public Scope(TypeRegistry typeRegistry, Scope? parent = null)
|
||||
{
|
||||
_typeRegistry = typeRegistry;
|
||||
_parent = parent;
|
||||
}
|
||||
|
||||
public void AddVariable(string name, TypeId type)
|
||||
{
|
||||
if (_variableScope.ContainsKey(name))
|
||||
{
|
||||
throw new DuplicateVariableNameException(name);
|
||||
}
|
||||
|
||||
_variableScope[name] = type;
|
||||
}
|
||||
|
||||
public bool HasVariable(string name)
|
||||
{
|
||||
if (_variableScope.ContainsKey(name)) return true;
|
||||
return _parent?.HasVariable(name) ?? false;
|
||||
}
|
||||
|
||||
public TypeId GetVariableType(string name)
|
||||
{
|
||||
return _variableScope.TryGetValue(name, out var value) ? value : throw new UnknownVariableNameException(name);
|
||||
}
|
||||
|
||||
public Guid AddAnonymousType(TypeDescription type)
|
||||
{
|
||||
var guid = Guid.NewGuid();
|
||||
_typeRegistry.Add(guid, type);
|
||||
return guid;
|
||||
}
|
||||
|
||||
public Guid AddNamedType(string name, int arity, TypeDescription type)
|
||||
{
|
||||
Guid guid;
|
||||
if (_typeScope.TryGetValue((name, arity), out guid))
|
||||
{
|
||||
if (_typeRegistry.Has(guid))
|
||||
{
|
||||
throw new DuplicateTypeNameException(name, arity);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (HasType(name, arity))
|
||||
{
|
||||
throw new DuplicateTypeNameException(name, arity);
|
||||
}
|
||||
guid = Guid.NewGuid();
|
||||
_typeScope[(name, arity)] = guid;
|
||||
}
|
||||
_typeRegistry.Add(guid, type);
|
||||
return guid;
|
||||
}
|
||||
|
||||
public bool HasType(string name, int arity)
|
||||
{
|
||||
if (_typeScope.ContainsKey((name, arity))) return true;
|
||||
return _parent?.HasType(name, arity) ?? false;
|
||||
}
|
||||
|
||||
public TypeDescription GetType(Guid guid)
|
||||
{
|
||||
return _typeRegistry.Get(guid);
|
||||
}
|
||||
|
||||
private Guid? GetTypeGuidWithoutCreate(string name, int arity)
|
||||
{
|
||||
if (_typeScope.ContainsKey((name, arity))) return _typeScope[(name, arity)];
|
||||
return _parent?.GetTypeGuidWithoutCreate(name, arity);
|
||||
}
|
||||
|
||||
public Guid GetTypeGuid(string name, int arity)
|
||||
{
|
||||
var key = GetTypeGuidWithoutCreate(name, arity);
|
||||
if (key == null)
|
||||
{
|
||||
key = Guid.NewGuid();
|
||||
_typeScope[(name, arity)] = key.Value;
|
||||
}
|
||||
|
||||
return key.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public class UnknownTypeException : Exception
|
||||
{
|
||||
private readonly string _name;
|
||||
private readonly int _arity;
|
||||
|
||||
public UnknownTypeException(string name, int arity): base($"unknown type `{name}` with arity {arity}")
|
||||
{
|
||||
_name = name;
|
||||
_arity = arity;
|
||||
}
|
||||
}
|
||||
|
||||
public class UnknownVariableNameException : Exception
|
||||
{
|
||||
public string Name { get; }
|
||||
|
||||
public UnknownVariableNameException(string name) : base($"unknown variable `{name}`")
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
41
typechecker/TypeChecker.cs
Normal file
41
typechecker/TypeChecker.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using meowlang.parser;
|
||||
|
||||
namespace meowlang.typechecker;
|
||||
|
||||
public static class TypeChecker
|
||||
{
|
||||
public static void CheckAndInferTypes(Model model)
|
||||
{
|
||||
var typeRegistry = new TypeRegistry();
|
||||
var topLevelScope = new Scope(typeRegistry);
|
||||
InitialiseTopLevelScope(topLevelScope);
|
||||
|
||||
new CollectTypesVisitor(topLevelScope).Visit(model);
|
||||
|
||||
Console.Write(topLevelScope.AutoToString());
|
||||
Console.Write(typeRegistry.AutoToString());
|
||||
return;
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static void InitialiseTopLevelScope(Scope scope)
|
||||
{
|
||||
scope.AddNamedType("s8", 0, new PrimitiveTypeDescription());
|
||||
scope.AddNamedType("s16", 0, new PrimitiveTypeDescription());
|
||||
scope.AddNamedType("s32", 0, new PrimitiveTypeDescription());
|
||||
scope.AddNamedType("s64", 0, new PrimitiveTypeDescription());
|
||||
scope.AddNamedType("s128", 0, new PrimitiveTypeDescription());
|
||||
|
||||
scope.AddNamedType("u8", 0, new PrimitiveTypeDescription());
|
||||
scope.AddNamedType("u16", 0, new PrimitiveTypeDescription());
|
||||
scope.AddNamedType("u32", 0, new PrimitiveTypeDescription());
|
||||
scope.AddNamedType("u64", 0, new PrimitiveTypeDescription());
|
||||
scope.AddNamedType("u128", 0, new PrimitiveTypeDescription());
|
||||
|
||||
scope.AddNamedType("f32", 0, new PrimitiveTypeDescription());
|
||||
scope.AddNamedType("f64", 0, new PrimitiveTypeDescription());
|
||||
|
||||
scope.AddNamedType("bool", 0, new PrimitiveTypeDescription());
|
||||
}
|
||||
}
|
3
typechecker/TypeId.cs
Normal file
3
typechecker/TypeId.cs
Normal file
|
@ -0,0 +1,3 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public record TypeId(Guid Key, List<TypeId> GenericParams);
|
25
typechecker/TypeRegistry.cs
Normal file
25
typechecker/TypeRegistry.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public class TypeRegistry
|
||||
{
|
||||
private readonly Dictionary<Guid, TypeDescription> _types = new ();
|
||||
|
||||
public Dictionary<Guid, TypeDescription> DebugGetTypes => _types;
|
||||
|
||||
public void Add(Guid guid, TypeDescription description)
|
||||
{
|
||||
_types[guid] = description;
|
||||
}
|
||||
|
||||
public TypeDescription Get(Guid guid)
|
||||
{
|
||||
return _types[guid];
|
||||
}
|
||||
|
||||
public bool Has(Guid guid)
|
||||
{
|
||||
return _types.ContainsKey(guid);
|
||||
}
|
||||
|
||||
|
||||
}
|
13
typechecker/exceptions/DuplicateTypeNameException.cs
Normal file
13
typechecker/exceptions/DuplicateTypeNameException.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public class DuplicateTypeNameException : Exception
|
||||
{
|
||||
public string Name { get; }
|
||||
public int Arity { get; }
|
||||
|
||||
public DuplicateTypeNameException(string name, int arity) : base($"duplicate type name `{name}` with arity {arity}")
|
||||
{
|
||||
Name = name;
|
||||
Arity = arity;
|
||||
}
|
||||
}
|
11
typechecker/exceptions/DuplicateVariableNameException.cs
Normal file
11
typechecker/exceptions/DuplicateVariableNameException.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public class DuplicateVariableNameException : Exception
|
||||
{
|
||||
public string Name { get; }
|
||||
|
||||
public DuplicateVariableNameException(string name) : base($"duplicate variable name `{name}` in scope")
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public class UndeclaredGenericParameterException : Exception
|
||||
{
|
||||
public string Name { get; }
|
||||
|
||||
public UndeclaredGenericParameterException(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
11
typechecker/exceptions/UnknownEnumMemberException.cs
Normal file
11
typechecker/exceptions/UnknownEnumMemberException.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public class UnknownEnumMemberException : Exception
|
||||
{
|
||||
public string Name { get; }
|
||||
|
||||
public UnknownEnumMemberException(string name) : base($"unknown enum member `{name}`")
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
11
typechecker/exceptions/UnknownStructMemberException.cs
Normal file
11
typechecker/exceptions/UnknownStructMemberException.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public class UnknownStructMemberException : Exception
|
||||
{
|
||||
public string Name { get; }
|
||||
|
||||
public UnknownStructMemberException(string name) : base($"unknown struct member `{name}`")
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
11
typechecker/exceptions/UnknownTupleMemberException.cs
Normal file
11
typechecker/exceptions/UnknownTupleMemberException.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public class UnknownTupleMemberException : Exception
|
||||
{
|
||||
public ushort Index { get; }
|
||||
|
||||
public UnknownTupleMemberException(ushort index) : base($"unknown tuple member `{index}`")
|
||||
{
|
||||
Index = index;
|
||||
}
|
||||
}
|
14
typechecker/typechecker.csproj
Normal file
14
typechecker/typechecker.csproj
Normal file
|
@ -0,0 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>meowlang.typechecker</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\parser\parser.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
4
typechecker/typechecker.csproj.DotSettings
Normal file
4
typechecker/typechecker.csproj.DotSettings
Normal file
|
@ -0,0 +1,4 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=exceptions/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=types/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=visitor/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
25
typechecker/types/ArrayTypeDescription.cs
Normal file
25
typechecker/types/ArrayTypeDescription.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public record ArrayTypeDescription : TypeDescription
|
||||
{
|
||||
private readonly TypeId _elementType;
|
||||
private readonly uint _size;
|
||||
|
||||
public ArrayTypeDescription(TypeId elementType, uint size)
|
||||
{
|
||||
_elementType = elementType;
|
||||
_size = size;
|
||||
}
|
||||
|
||||
public TypeId ElementType => _elementType;
|
||||
|
||||
public uint Size => _size;
|
||||
}
|
||||
public record PrimitiveTypeDescription : TypeDescription
|
||||
{
|
||||
|
||||
public PrimitiveTypeDescription()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
24
typechecker/types/EnumTypeDescription.cs
Normal file
24
typechecker/types/EnumTypeDescription.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public record EnumTypeDescription : TypeDescription
|
||||
{
|
||||
private readonly List<(string Name, TypeId? Type)> _members;
|
||||
private readonly Dictionary<string, TypeId?> _memberMap;
|
||||
|
||||
public Dictionary<string, TypeId?> DebugGetMembers => _memberMap;
|
||||
|
||||
public EnumTypeDescription(List<(string Name, TypeId? Type)> members)
|
||||
{
|
||||
_members = members;
|
||||
_memberMap = members.ToDictionary(x => x.Name, x => x.Type);
|
||||
}
|
||||
|
||||
public IReadOnlyList<string> MemberNames => _members.Select(x => x.Name).ToList();
|
||||
|
||||
public TypeId? GetTypeForMember(string name)
|
||||
{
|
||||
return _memberMap.TryGetValue(name, out var value) ? value : throw new UnknownEnumMemberException(name);
|
||||
}
|
||||
|
||||
public bool HasMember(string name) => _memberMap.ContainsKey(name);
|
||||
};
|
19
typechecker/types/FunctionTypeDescription.cs
Normal file
19
typechecker/types/FunctionTypeDescription.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public record FunctionTypeDescription : TypeDescription
|
||||
{
|
||||
private readonly List<TypeId> _parameters;
|
||||
private readonly List<TypeId> _returns;
|
||||
|
||||
public FunctionTypeDescription(List<TypeId> parameters, List<TypeId> returns)
|
||||
{
|
||||
_parameters = parameters;
|
||||
_returns = returns;
|
||||
}
|
||||
|
||||
public IReadOnlyList<TypeId> Parameters => _parameters;
|
||||
public IReadOnlyList<TypeId> Returns => _returns;
|
||||
|
||||
public int ParameterArity => _parameters.Count;
|
||||
public int ReturnArity => _returns.Count;
|
||||
}
|
18
typechecker/types/GenericArrayTypeDescription.cs
Normal file
18
typechecker/types/GenericArrayTypeDescription.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public record GenericArrayTypeDescription : GenericTypeDescription
|
||||
{
|
||||
private readonly GenericTypeId _elementType;
|
||||
private readonly uint _size;
|
||||
|
||||
public GenericArrayTypeDescription(List<string> genericNames, GenericTypeId elementType, uint size) : base(genericNames)
|
||||
{
|
||||
_elementType = elementType;
|
||||
_size = size;
|
||||
}
|
||||
|
||||
public override ArrayTypeDescription Concretize(List<Guid> typeParams)
|
||||
{
|
||||
return new ArrayTypeDescription(ConcretizeGenericType(_elementType, typeParams), _size);
|
||||
}
|
||||
}
|
21
typechecker/types/GenericEnumTypeDescription.cs
Normal file
21
typechecker/types/GenericEnumTypeDescription.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public record GenericEnumTypeDescription : GenericTypeDescription
|
||||
{
|
||||
private readonly List<(string Name, GenericTypeId? Type)> _members;
|
||||
|
||||
public GenericEnumTypeDescription(List<string> genericNames, List<(string Name, GenericTypeId? Type)> members) :
|
||||
base(genericNames)
|
||||
{
|
||||
_members = members;
|
||||
CheckGenericNames(members.Where(x => x.Type != null).Select(x => x.Type!).ToList());
|
||||
}
|
||||
|
||||
|
||||
public override EnumTypeDescription Concretize(List<Guid> typeParams)
|
||||
{
|
||||
var members = _members.Select(x =>
|
||||
x.Type == null ? (x.Name, null) : (x.Name, ConcretizeGenericType(x.Type, typeParams))).ToList();
|
||||
return new EnumTypeDescription(members);
|
||||
}
|
||||
}
|
21
typechecker/types/GenericFunctionTypeDescription.cs
Normal file
21
typechecker/types/GenericFunctionTypeDescription.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public record GenericFunctionTypeDescription : GenericTypeDescription
|
||||
{
|
||||
private readonly List<GenericTypeId> _parameters;
|
||||
private readonly List<GenericTypeId> _returns;
|
||||
|
||||
public GenericFunctionTypeDescription(List<string> genericNames, List<GenericTypeId> parameters, List<GenericTypeId> returns) : base(genericNames)
|
||||
{
|
||||
_parameters = parameters;
|
||||
_returns = returns;
|
||||
}
|
||||
|
||||
|
||||
public override FunctionTypeDescription Concretize(List<Guid> typeParams)
|
||||
{
|
||||
var parameters = _parameters.Select(x => ConcretizeGenericType(x, typeParams)).ToList();
|
||||
var returns = _returns.Select(x => ConcretizeGenericType(x, typeParams)).ToList();
|
||||
return new FunctionTypeDescription(parameters, returns);
|
||||
}
|
||||
};
|
16
typechecker/types/GenericSliceTypeDescription.cs
Normal file
16
typechecker/types/GenericSliceTypeDescription.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public record GenericSliceTypeDescription : GenericTypeDescription
|
||||
{
|
||||
private readonly GenericTypeId _elementType;
|
||||
|
||||
public GenericSliceTypeDescription(List<string> genericNames, GenericTypeId elementType) : base(genericNames)
|
||||
{
|
||||
_elementType = elementType;
|
||||
}
|
||||
|
||||
public override SliceTypeDescription Concretize(List<Guid> typeParams)
|
||||
{
|
||||
return new SliceTypeDescription(ConcretizeGenericType(_elementType, typeParams));
|
||||
}
|
||||
}
|
21
typechecker/types/GenericStructTypeDescription.cs
Normal file
21
typechecker/types/GenericStructTypeDescription.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public record GenericStructTypeDescription : GenericTypeDescription
|
||||
{
|
||||
private readonly List<(string Name, GenericTypeId Type)> _members;
|
||||
|
||||
public List<(string Name, GenericTypeId Type)> DebugGetMembers => _members;
|
||||
|
||||
public GenericStructTypeDescription(List<string> genericNames, List<(string Name, GenericTypeId Type)> members) : base(genericNames)
|
||||
{
|
||||
_members = members;
|
||||
CheckGenericNames(members.Select(x => x.Type).ToList());
|
||||
}
|
||||
|
||||
public override StructTypeDescription Concretize(List<Guid> typeParams)
|
||||
{
|
||||
var members = _members.Select(x => (x.Name, ConcretizeGenericType(x.Type, typeParams))).ToList();
|
||||
|
||||
return new StructTypeDescription(members);
|
||||
}
|
||||
}
|
20
typechecker/types/GenericTupleTypeDescription.cs
Normal file
20
typechecker/types/GenericTupleTypeDescription.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public record GenericTupleTypeDescription : GenericTypeDescription
|
||||
{
|
||||
private readonly List<GenericTypeId> _members;
|
||||
|
||||
public GenericTupleTypeDescription(List<string> genericNames, List<GenericTypeId> members) : base(genericNames)
|
||||
{
|
||||
_members = members;
|
||||
CheckGenericNames(members);
|
||||
}
|
||||
|
||||
|
||||
public override TupleTypeDescription Concretize(List<Guid> typeParams)
|
||||
{
|
||||
var members = _members.Select(x => ConcretizeGenericType(x, typeParams)).ToList();
|
||||
return new TupleTypeDescription(members);
|
||||
}
|
||||
|
||||
}
|
45
typechecker/types/GenericTypeDescription.cs
Normal file
45
typechecker/types/GenericTypeDescription.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public abstract record GenericTypeDescription : TypeDescription
|
||||
{
|
||||
protected readonly List<string> GenericNames;
|
||||
|
||||
public int Arity => GenericNames.Count;
|
||||
|
||||
public GenericTypeDescription(List<string> genericNames)
|
||||
{
|
||||
GenericNames = genericNames;
|
||||
}
|
||||
|
||||
protected void CheckGenericNames(List<GenericTypeId> genericTypes)
|
||||
{
|
||||
foreach (var typeId in genericTypes)
|
||||
{
|
||||
if (typeId.Key.IsGeneric)
|
||||
{
|
||||
if (GenericNames.IndexOf(typeId.Key.GenericParameterName!) < 0)
|
||||
{
|
||||
throw new UndeclaredGenericParameterException(typeId.Key.GenericParameterName!);
|
||||
}
|
||||
}
|
||||
|
||||
CheckGenericNames(typeId.GenericParams);
|
||||
}
|
||||
}
|
||||
public abstract TypeDescription Concretize(List<Guid> typeParams);
|
||||
|
||||
|
||||
|
||||
protected TypeId ConcretizeGenericType(GenericTypeId genericType, List<Guid> concreteTypes)
|
||||
{
|
||||
return new TypeId(ConcretizeGenericTypeRef(genericType.Key, concreteTypes),
|
||||
genericType.GenericParams.Select(x => ConcretizeGenericType(x, concreteTypes)).ToList());
|
||||
}
|
||||
|
||||
private Guid ConcretizeGenericTypeRef(PossiblyGenericTypeRef typeRef, List<Guid> concreteTypes)
|
||||
{
|
||||
if (!typeRef.IsGeneric) return typeRef.TypeKey!.Value;
|
||||
var typeIndex = GenericNames.IndexOf(typeRef.GenericParameterName!);
|
||||
return concreteTypes[typeIndex];
|
||||
}
|
||||
}
|
13
typechecker/types/SliceTypeDescription.cs
Normal file
13
typechecker/types/SliceTypeDescription.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public record SliceTypeDescription : TypeDescription
|
||||
{
|
||||
private readonly TypeId _elementType;
|
||||
|
||||
public SliceTypeDescription(TypeId elementType)
|
||||
{
|
||||
_elementType = elementType;
|
||||
}
|
||||
|
||||
public TypeId ElementType => _elementType;
|
||||
}
|
25
typechecker/types/StructTypeDescription.cs
Normal file
25
typechecker/types/StructTypeDescription.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public record StructTypeDescription : TypeDescription
|
||||
{
|
||||
private readonly List<(string Name, TypeId Type)> _members;
|
||||
private readonly Dictionary<string, TypeId> _memberMap;
|
||||
|
||||
public Dictionary<string, TypeId> DebugGetMembers => _memberMap;
|
||||
|
||||
public StructTypeDescription(List<(string Name, TypeId Type)> members)
|
||||
{
|
||||
_members = members;
|
||||
_memberMap = members.ToDictionary(x => x.Name, x => x.Type);
|
||||
}
|
||||
|
||||
public TypeId GetTypeForMember(string name)
|
||||
{
|
||||
return _memberMap.TryGetValue(name, out var value) ? value : throw new UnknownStructMemberException(name);
|
||||
}
|
||||
|
||||
public IReadOnlyList<string> MemberNames => _members.Select(x => x.Name).ToList();
|
||||
|
||||
|
||||
public bool HasMember(string name) => _memberMap.ContainsKey(name);
|
||||
};
|
24
typechecker/types/TupleTypeDescription.cs
Normal file
24
typechecker/types/TupleTypeDescription.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public record TupleTypeDescription : TypeDescription
|
||||
{
|
||||
private readonly List<TypeId> _members;
|
||||
|
||||
public List<TypeId> DebugGetMembers => _members;
|
||||
|
||||
public TupleTypeDescription(List<TypeId> members)
|
||||
{
|
||||
_members = members;
|
||||
}
|
||||
|
||||
public int Length => _members.Count;
|
||||
|
||||
public TypeId GetTypeForMember(ushort index)
|
||||
{
|
||||
if (index > Length)
|
||||
{
|
||||
throw new UnknownTupleMemberException(index);
|
||||
}
|
||||
return _members[index];
|
||||
}
|
||||
}
|
3
typechecker/types/TypeDescription.cs
Normal file
3
typechecker/types/TypeDescription.cs
Normal file
|
@ -0,0 +1,3 @@
|
|||
namespace meowlang.typechecker;
|
||||
|
||||
public abstract record TypeDescription();
|
77
typechecker/visitor/CollectTypesVisitor.cs
Normal file
77
typechecker/visitor/CollectTypesVisitor.cs
Normal file
|
@ -0,0 +1,77 @@
|
|||
using meowlang.parser;
|
||||
|
||||
namespace meowlang.typechecker;
|
||||
|
||||
public class CollectTypesVisitor : MeowModelVisitor
|
||||
{
|
||||
private readonly Scope _scope;
|
||||
|
||||
public CollectTypesVisitor(Scope scope)
|
||||
{
|
||||
_scope = scope;
|
||||
}
|
||||
|
||||
public override void VisitStructDeclaration(StructDeclarationModel m)
|
||||
{
|
||||
TypeDescription structDescription;
|
||||
var arity = m.TypeParameters.Count;
|
||||
if (arity > 0)
|
||||
{
|
||||
structDescription = new GenericTypeDescriptionVisitor(_scope, m.TypeParameters, m.Name).Visit(m);
|
||||
}
|
||||
else
|
||||
{
|
||||
structDescription = new TypeDescriptionVisitor(_scope, m.Name).Visit(m);
|
||||
}
|
||||
|
||||
_scope.AddNamedType(m.Name, arity, structDescription);
|
||||
}
|
||||
|
||||
public override void VisitEnumDeclaration(EnumDeclarationModel m)
|
||||
{
|
||||
TypeDescription enumDescription;
|
||||
var arity = m.TypeParameters.Count;
|
||||
if (arity > 0)
|
||||
{
|
||||
enumDescription = new GenericTypeDescriptionVisitor(_scope, m.TypeParameters, m.Name).Visit(m);
|
||||
}
|
||||
else
|
||||
{
|
||||
enumDescription = new TypeDescriptionVisitor(_scope, m.Name).Visit(m);
|
||||
}
|
||||
|
||||
_scope.AddNamedType(m.Name, arity, enumDescription);
|
||||
}
|
||||
|
||||
public override void VisitTupleDeclaration(TupleDeclarationModel m)
|
||||
{
|
||||
TypeDescription tupleDescription;
|
||||
var arity = m.TypeParameters.Count;
|
||||
if (arity > 0)
|
||||
{
|
||||
tupleDescription = new GenericTypeDescriptionVisitor(_scope, m.TypeParameters, m.Name).Visit(m);
|
||||
}
|
||||
else
|
||||
{
|
||||
tupleDescription = new TypeDescriptionVisitor(_scope, m.Name).Visit(m);
|
||||
}
|
||||
|
||||
_scope.AddNamedType(m.Name, arity, tupleDescription);
|
||||
}
|
||||
|
||||
public override void VisitTypeAlias(TypeAliasModel m)
|
||||
{
|
||||
TypeDescription typeDescription;
|
||||
var arity = m.TypeParameters.Count;
|
||||
if (arity > 0)
|
||||
{
|
||||
typeDescription = new GenericTypeDescriptionVisitor(_scope, m.TypeParameters, m.Name).Visit(m);
|
||||
}
|
||||
else
|
||||
{
|
||||
typeDescription = new TypeDescriptionVisitor(_scope, m.Name).Visit(m);
|
||||
}
|
||||
|
||||
_scope.AddNamedType(m.Name, arity, typeDescription);
|
||||
}
|
||||
}
|
59
typechecker/visitor/GenericTypeDescriptionVisitor.cs
Normal file
59
typechecker/visitor/GenericTypeDescriptionVisitor.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using meowlang.parser;
|
||||
|
||||
namespace meowlang.typechecker;
|
||||
|
||||
class GenericTypeDescriptionVisitor : MeowModelVisitor<GenericTypeDescription>
|
||||
{
|
||||
private readonly Scope _scope;
|
||||
private readonly List<string> _typeParams;
|
||||
private readonly string? _name;
|
||||
|
||||
public GenericTypeDescriptionVisitor(Scope scope, List<string> typeParams, string? name)
|
||||
{
|
||||
_scope = scope;
|
||||
_typeParams = typeParams;
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public override GenericTypeDescription VisitStructType(StructTypeModel m)
|
||||
{
|
||||
var members = new List<(string, GenericTypeId)>();
|
||||
foreach (var (_, typeReferenceModel, name) in m.Members)
|
||||
{
|
||||
var fullName = _name == null ? null : $"{_name}@{name}";
|
||||
members.Add((name, new GenericTypeIdVisitor(_scope, _typeParams, fullName).Visit(typeReferenceModel)));
|
||||
}
|
||||
|
||||
return new GenericStructTypeDescription(_typeParams, members);
|
||||
}
|
||||
|
||||
public override GenericTypeDescription VisitEnumType(EnumTypeModel m)
|
||||
{
|
||||
var members = new List<(string, GenericTypeId?)>();
|
||||
foreach (var (_, name, typeReferenceModel) in m.Members)
|
||||
{
|
||||
var fullName = _name == null ? null : $"{_name}@{name}";
|
||||
members.Add((name, new GenericTypeIdVisitor(_scope, _typeParams, fullName).TryVisit(typeReferenceModel)));
|
||||
}
|
||||
|
||||
return new GenericEnumTypeDescription(_typeParams, members);
|
||||
}
|
||||
|
||||
public override GenericTypeDescription VisitTupleType(TupleTypeModel m)
|
||||
{
|
||||
var members = m.Types.Select((t, i) => new GenericTypeIdVisitor(_scope, _typeParams, $"{_name}@{i}").Visit(t)).ToList();
|
||||
return new GenericTupleTypeDescription(_typeParams, members);
|
||||
}
|
||||
|
||||
public override GenericTypeDescription VisitArrayType(ArrayTypeModel m)
|
||||
{
|
||||
var elementType = new GenericTypeIdVisitor(_scope, _typeParams, $"{_name}@inner").Visit(m.Type);
|
||||
return new GenericArrayTypeDescription(_typeParams, elementType, m.Size);
|
||||
}
|
||||
|
||||
public override GenericTypeDescription VisitSliceType(SliceTypeModel m)
|
||||
{
|
||||
var elementType = new GenericTypeIdVisitor(_scope, _typeParams, $"{_name}@inner").Visit(m.Type);
|
||||
return new GenericSliceTypeDescription(_typeParams, elementType);
|
||||
}
|
||||
}
|
54
typechecker/visitor/GenericTypeIdVisitor.cs
Normal file
54
typechecker/visitor/GenericTypeIdVisitor.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
using meowlang.parser;
|
||||
|
||||
namespace meowlang.typechecker;
|
||||
|
||||
public class GenericTypeIdVisitor : MeowModelVisitor<GenericTypeId>
|
||||
{
|
||||
private readonly Scope _scope;
|
||||
private readonly List<string> _genericParameters;
|
||||
private readonly string? _name;
|
||||
|
||||
public GenericTypeIdVisitor(Scope scope, List<string> genericParameters, string? name)
|
||||
{
|
||||
_scope = scope;
|
||||
_genericParameters = genericParameters;
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public override GenericTypeId VisitTypeName(TypeNameModel m)
|
||||
{
|
||||
var typeParams = m.TypeParameters
|
||||
.Select(t => new GenericTypeIdVisitor(_scope, _genericParameters, null).Visit(t)).ToList();
|
||||
|
||||
var name = m.Name.Name;
|
||||
if (m.Name.ImportName != null)
|
||||
{
|
||||
name = $"{m.Name.ImportName}:{name}";
|
||||
}
|
||||
|
||||
foreach (var nested in m.Nested)
|
||||
{
|
||||
name = $"{name}@{nested}";
|
||||
}
|
||||
|
||||
PossiblyGenericTypeRef genericKey;
|
||||
|
||||
if (_genericParameters.Contains(name))
|
||||
{
|
||||
genericKey = new PossiblyGenericTypeRef(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
genericKey = new PossiblyGenericTypeRef(_scope.GetTypeGuid(name, typeParams.Count));
|
||||
}
|
||||
|
||||
return new GenericTypeId(genericKey, typeParams);
|
||||
}
|
||||
|
||||
public override GenericTypeId VisitStructType(StructTypeModel m)
|
||||
{
|
||||
var type = new GenericTypeDescriptionVisitor(_scope, _genericParameters, _name).Visit(m);
|
||||
var guid = _scope.AddAnonymousType(type);
|
||||
return new GenericTypeId(new PossiblyGenericTypeRef(guid), new List<GenericTypeId>());
|
||||
}
|
||||
}
|
60
typechecker/visitor/TypeDescriptionVisitor.cs
Normal file
60
typechecker/visitor/TypeDescriptionVisitor.cs
Normal file
|
@ -0,0 +1,60 @@
|
|||
using meowlang.parser;
|
||||
|
||||
namespace meowlang.typechecker;
|
||||
|
||||
class TypeDescriptionVisitor : MeowModelVisitor<TypeDescription>
|
||||
{
|
||||
private readonly Scope _scope;
|
||||
private readonly string? _name;
|
||||
|
||||
public TypeDescriptionVisitor(Scope scope)
|
||||
{
|
||||
_scope = scope;
|
||||
}
|
||||
public TypeDescriptionVisitor(Scope scope, string? name)
|
||||
{
|
||||
_scope = scope;
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public override TypeDescription VisitStructType(StructTypeModel m)
|
||||
{
|
||||
var members = new List<(string, TypeId)>();
|
||||
foreach (var (_, typeReferenceModel, name) in m.Members)
|
||||
{
|
||||
var fullName = _name == null ? null : $"{_name}@{name}";
|
||||
members.Add((name, new TypeIdVisitor(_scope, fullName).Visit(typeReferenceModel)));
|
||||
}
|
||||
|
||||
return new StructTypeDescription(members);
|
||||
}
|
||||
|
||||
public override TypeDescription VisitEnumType(EnumTypeModel m)
|
||||
{
|
||||
var members = new List<(string, TypeId?)>();
|
||||
foreach (var (_, name, typeReferenceModel) in m.Members)
|
||||
{
|
||||
var fullName = _name == null ? null : $"{_name}@{name}";
|
||||
members.Add((name, new TypeIdVisitor(_scope, fullName).TryVisit(typeReferenceModel)));
|
||||
}
|
||||
|
||||
return new EnumTypeDescription(members);
|
||||
}
|
||||
|
||||
public override TypeDescription VisitTupleType(TupleTypeModel m)
|
||||
{
|
||||
var members = m.Types.Select((t, i) => new TypeIdVisitor(_scope, $"{_name}@{i}").Visit(t)).ToList();
|
||||
return new TupleTypeDescription(members);
|
||||
}
|
||||
|
||||
public override TypeDescription VisitArrayType(ArrayTypeModel m)
|
||||
{
|
||||
var elementType = new TypeIdVisitor(_scope, $"{_name}@inner").Visit(m.Type);
|
||||
return new ArrayTypeDescription(elementType, m.Size);
|
||||
}
|
||||
public override TypeDescription VisitSliceType(SliceTypeModel m)
|
||||
{
|
||||
var elementType = new TypeIdVisitor(_scope, $"{_name}@inner").Visit(m.Type);
|
||||
return new SliceTypeDescription(elementType);
|
||||
}
|
||||
}
|
69
typechecker/visitor/TypeIdVisitor.cs
Normal file
69
typechecker/visitor/TypeIdVisitor.cs
Normal file
|
@ -0,0 +1,69 @@
|
|||
using meowlang.parser;
|
||||
|
||||
namespace meowlang.typechecker;
|
||||
|
||||
public class TypeIdVisitor : MeowModelVisitor<TypeId>
|
||||
{
|
||||
private readonly Scope _scope;
|
||||
private readonly string? _name;
|
||||
public TypeIdVisitor(Scope scope, string? name)
|
||||
{
|
||||
_scope = scope;
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public override TypeId VisitTypeName(TypeNameModel m)
|
||||
{
|
||||
var typeParams = m.TypeParameters.Select(t => new TypeIdVisitor(_scope, null).Visit(t)).ToList();
|
||||
|
||||
var name = m.Name.Name;
|
||||
if (m.Name.ImportName != null)
|
||||
{
|
||||
name = $"{m.Name.ImportName}:{name}";
|
||||
}
|
||||
|
||||
foreach (var nested in m.Nested)
|
||||
{
|
||||
name = $"{name}@{nested}";
|
||||
}
|
||||
|
||||
var guid = _scope.GetTypeGuid(name, typeParams.Count);
|
||||
|
||||
return new TypeId(guid, typeParams);
|
||||
}
|
||||
|
||||
private TypeId RegisterAnonymousType(ModelBase m)
|
||||
{
|
||||
var type = new TypeDescriptionVisitor(_scope, _name).Visit(m);
|
||||
Guid guid;
|
||||
if (_name == null)
|
||||
{
|
||||
guid = _scope.AddAnonymousType(type);
|
||||
}
|
||||
else
|
||||
{
|
||||
guid = _scope.AddNamedType(_name, 0, type);
|
||||
}
|
||||
return new TypeId(guid, new List<TypeId>());
|
||||
}
|
||||
public override TypeId VisitStructType(StructTypeModel m)
|
||||
{
|
||||
return RegisterAnonymousType(m);
|
||||
}
|
||||
public override TypeId VisitEnumType(EnumTypeModel m)
|
||||
{
|
||||
return RegisterAnonymousType(m);
|
||||
}
|
||||
public override TypeId VisitTupleType(TupleTypeModel m)
|
||||
{
|
||||
return RegisterAnonymousType(m);
|
||||
}
|
||||
public override TypeId VisitArrayType(ArrayTypeModel m)
|
||||
{
|
||||
return RegisterAnonymousType(m);
|
||||
}
|
||||
public override TypeId VisitSliceType(SliceTypeModel m)
|
||||
{
|
||||
return RegisterAnonymousType(m);
|
||||
}
|
||||
}
|
124
visitorgenerator/Program.cs
Normal file
124
visitorgenerator/Program.cs
Normal file
|
@ -0,0 +1,124 @@
|
|||
using System.Reflection;
|
||||
using meowlang.parser;
|
||||
|
||||
|
||||
var className = args[0];
|
||||
var destinatinDir = args[1] ?? ".";
|
||||
var destinationPath = Path.Join(destinatinDir, $"{className}.cs");
|
||||
|
||||
|
||||
var parserType = typeof(Parser);
|
||||
var nameSpace = parserType.Namespace;
|
||||
var modelTypes = parserType.Assembly.DefinedTypes.Where(t => t.IsAssignableTo(typeof(ModelBase))).Where(t => !t.IsAbstract);
|
||||
|
||||
string UnModelifyName(string name)
|
||||
{
|
||||
var nameWithoutModel = name;
|
||||
var modelIndex = nameWithoutModel.LastIndexOf("Model", StringComparison.Ordinal);
|
||||
if (modelIndex > -1)
|
||||
{
|
||||
nameWithoutModel = nameWithoutModel.Remove(modelIndex);
|
||||
}
|
||||
|
||||
return nameWithoutModel;
|
||||
}
|
||||
|
||||
string MakeVisitorMethod(TypeInfo model, bool isVoid)
|
||||
{
|
||||
if (model == null) throw new ArgumentNullException(nameof(model));
|
||||
var typeName = model.Name;
|
||||
var visitorName = $"Visit{UnModelifyName(typeName)}";
|
||||
return isVoid ? @$"
|
||||
public virtual void {visitorName}({typeName} m)
|
||||
{{
|
||||
foreach (var modelBase in m.GetChildren())
|
||||
{{
|
||||
Visit(modelBase);
|
||||
}}
|
||||
}}
|
||||
" : @$"
|
||||
public virtual T {visitorName}({typeName} m)
|
||||
{{
|
||||
T result = default;
|
||||
foreach (var modelBase in m.GetChildren())
|
||||
{{
|
||||
result = Visit(modelBase);
|
||||
}}
|
||||
|
||||
return result;
|
||||
}}
|
||||
";
|
||||
}
|
||||
|
||||
|
||||
string MakeSwitchArm(TypeInfo model, bool isVoid)
|
||||
{
|
||||
if (model == null) throw new ArgumentNullException(nameof(model));
|
||||
var typeName = model.Name;
|
||||
var visitorName = $"Visit{UnModelifyName(typeName)}";
|
||||
return isVoid ? $@"
|
||||
case {typeName} x: {visitorName}(x); break;" : $@"
|
||||
case {typeName} x: return {visitorName}(x);";
|
||||
}
|
||||
|
||||
var arms = new List<string>();
|
||||
var voidArms = new List<string>();
|
||||
var methods = new List<string>();
|
||||
var voidMethods = new List<string>();
|
||||
|
||||
foreach (var modelType in modelTypes)
|
||||
{
|
||||
arms.Add(MakeSwitchArm(modelType, false));
|
||||
voidArms.Add(MakeSwitchArm(modelType, true));
|
||||
methods.Add(MakeVisitorMethod(modelType, false));
|
||||
voidMethods.Add(MakeVisitorMethod(modelType, true));
|
||||
}
|
||||
|
||||
|
||||
var sourceCode = $@"// automatically generated
|
||||
|
||||
// ReSharper disable MemberCanBeProtected.Global
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
#pragma warning disable CS8603
|
||||
#pragma warning disable CS8600
|
||||
#pragma warning disable CS8509
|
||||
|
||||
using System.CodeDom.Compiler;
|
||||
namespace {nameSpace};
|
||||
|
||||
[GeneratedCode(""meow-visitorgenerator"", ""1"")]
|
||||
public class {className}<T>
|
||||
{{
|
||||
|
||||
public T? TryVisit(ModelBase? m)
|
||||
{{
|
||||
return m == null ? default : Visit(m);
|
||||
}}
|
||||
|
||||
public T Visit(ModelBase m)
|
||||
{{
|
||||
switch(m)
|
||||
{{
|
||||
{string.Join("", arms)}
|
||||
}}
|
||||
return default;
|
||||
}}
|
||||
{string.Join("", methods)}
|
||||
}}
|
||||
|
||||
[GeneratedCode(""meow-visitorgenerator"", ""1"")]
|
||||
public class {className}
|
||||
{{
|
||||
public void Visit(ModelBase m)
|
||||
{{
|
||||
switch(m)
|
||||
{{
|
||||
{string.Join("", voidArms)}
|
||||
}}
|
||||
}}
|
||||
{string.Join("", voidMethods)}
|
||||
}}
|
||||
";
|
||||
|
||||
File.WriteAllText(destinationPath, sourceCode);
|
||||
Console.WriteLine($"output written to {destinationPath}");
|
14
visitorgenerator/visitorgenerator.csproj
Normal file
14
visitorgenerator/visitorgenerator.csproj
Normal file
|
@ -0,0 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\parser\parser.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Loading…
Reference in a new issue