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:
Gwendolyn 2022-02-13 02:41:16 +01:00
parent 3884422afb
commit d6bdd08002
62 changed files with 2619 additions and 40 deletions

View file

@ -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

View file

@ -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;

View file

@ -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);
}
}

View file

@ -14,6 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\parser\parser.csproj" />
<ProjectReference Include="..\typechecker\typechecker.csproj" />
</ItemGroup>
<ItemGroup>

View file

@ -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;
}

View file

@ -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);

View file

@ -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);

View file

@ -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);
}
}

View 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);

View file

@ -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);

View file

@ -1,3 +1,5 @@
namespace meowlang.parser;
public abstract record ExpressionModel(Span Span) : ModelBase(Span);
public abstract record ExpressionModel(Span Span) : ModelBase(Span)
{
}

View file

@ -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, "");
}

View file

@ -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;
}
}
}

View 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}`")
{
}
}

View file

@ -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 | ~['\\]) '\'';

View 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

File diff suppressed because it is too large Load diff

View file

@ -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;
}
}
}
}
}
}

View 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);

View file

@ -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);

View file

@ -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;
}
}
}

View 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);

View file

@ -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);

View file

@ -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)

View file

@ -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();

View file

@ -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);
}

View file

@ -0,0 +1,3 @@
namespace meowlang.typechecker;
public record GenericTypeId(PossiblyGenericTypeRef Key, List<GenericTypeId> GenericParams);

View 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;
}
}

View 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
View 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;
}
}

View 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
View file

@ -0,0 +1,3 @@
namespace meowlang.typechecker;
public record TypeId(Guid Key, List<TypeId> GenericParams);

View 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);
}
}

View 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;
}
}

View 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;
}
}

View file

@ -0,0 +1,11 @@
namespace meowlang.typechecker;
public class UndeclaredGenericParameterException : Exception
{
public string Name { get; }
public UndeclaredGenericParameterException(string name)
{
Name = name;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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>

View 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>

View 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()
{
}
}

View 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);
};

View 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;
}

View 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);
}
}

View 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);
}
}

View 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);
}
};

View 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));
}
}

View 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);
}
}

View 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);
}
}

View 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];
}
}

View 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;
}

View 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);
};

View 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];
}
}

View file

@ -0,0 +1,3 @@
namespace meowlang.typechecker;
public abstract record TypeDescription();

View 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);
}
}

View 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);
}
}

View 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>());
}
}

View 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);
}
}

View 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
View 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}");

View 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>