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
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "parser", "parser\parser.csproj", "{240E1F81-DB04-46CE-AB7B-60616B6D868C}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "parser", "parser\parser.csproj", "{240E1F81-DB04-46CE-AB7B-60616B6D868C}"
|
||||||
EndProject
|
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
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{240E1F81-DB04-46CE-AB7B-60616B6D868C}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
@ -179,13 +179,18 @@ Their type is an `u8` array with length of the string in bytes.
|
||||||
```
|
```
|
||||||
###### use
|
###### use
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
let a: [u8] = "foo";
|
let a: [u8] = "foo";
|
||||||
let b: u8 = a[1];
|
let b: u8 = a[1];
|
||||||
let d: [s32; 4] = []{1,2,3,4};
|
let d: [s32; 4] = []{1,2,3,4};
|
||||||
let e = [u8]{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];
|
type Arr = [u8];
|
||||||
let f = Arr[]{1,2,3};
|
let g = Arr[]{1,2,3};
|
||||||
|
|
||||||
```
|
```
|
||||||
##### struct
|
##### struct
|
||||||
|
@ -202,6 +207,7 @@ type Point = struct {
|
||||||
z: s32;
|
z: s32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type Options = struct {
|
type Options = struct {
|
||||||
api: struct {
|
api: struct {
|
||||||
int foo;
|
int foo;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using meowlang.parser;
|
using meowlang.parser;
|
||||||
|
using meowlang.typechecker;
|
||||||
|
|
||||||
namespace meowlang.compiler;
|
namespace meowlang.compiler;
|
||||||
|
|
||||||
|
@ -9,6 +10,8 @@ static class Compiler
|
||||||
{
|
{
|
||||||
var path = "example.mew";
|
var path = "example.mew";
|
||||||
var model = Parser.Parse(path);
|
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>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\parser\parser.csproj" />
|
<ProjectReference Include="..\parser\parser.csproj" />
|
||||||
|
<ProjectReference Include="..\typechecker\typechecker.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -94,9 +94,23 @@ type F<X> = Foo<Bar<X>>;
|
||||||
type X = [Y; 10];
|
type X = [Y; 10];
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
/*
|
||||||
|
tuple X<A> (A,A,A);
|
||||||
|
|
||||||
|
|
||||||
|
type X<T> = [T; 10];
|
||||||
|
type X<T1, T2> = (T1, T2);
|
||||||
|
|
||||||
fn foo() {
|
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};
|
meoooow let x-nyaaaa = []{1-nya, meow 2};
|
||||||
|
|
||||||
outer: loop let x = 1; x < 10; x += 1 {
|
outer: loop let x = 1; x < 10; x += 1 {
|
||||||
|
@ -110,3 +124,66 @@ fn foo() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
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;
|
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);
|
(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;
|
using meowlang.parser.antlr;
|
||||||
|
|
||||||
namespace meowlang.parser;
|
namespace meowlang.parser;
|
||||||
|
@ -33,7 +32,7 @@ public class DeclarationVisitorNya : MeowBaseVisitorNya<TopLevelConstructModel>
|
||||||
functionReturns, constraints, body);
|
functionReturns, constraints, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ConstraintTopLevelConstructModel VisitConstraintDeclaration(
|
public override ConstraintDeclarationModel VisitConstraintDeclaration(
|
||||||
MeowParser.ConstraintDeclarationContext context)
|
MeowParser.ConstraintDeclarationContext context)
|
||||||
{
|
{
|
||||||
var attributes = context.attribute().Select(x => new AttributeVisitorNya().Visit(x)).ToList();
|
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 name = context.name.Text;
|
||||||
var typeNames = context._types.Select(x => x.Text).ToList();
|
var typeNames = context._types.Select(x => x.Text).ToList();
|
||||||
var rules = context.constraintRule().Select(x => new ConstraintRuleVisitorNya().Visit(x)).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)
|
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() ??
|
var constraints = context.constraint()?.Select(x => new FunctionConstraintVisitorNya().Visit(x)).ToList() ??
|
||||||
new List<ConstraintModel>();
|
new List<ConstraintModel>();
|
||||||
var structModel = new TypeReferenceVisitorNya().Visit(context.structType()) as StructTypeModel;
|
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)
|
public override TopLevelConstructModel VisitEnumDeclaration(MeowParser.EnumDeclarationContext context)
|
||||||
|
@ -66,7 +65,7 @@ public class DeclarationVisitorNya : MeowBaseVisitorNya<TopLevelConstructModel>
|
||||||
new List<ConstraintModel>();
|
new List<ConstraintModel>();
|
||||||
|
|
||||||
var enumModel = new TypeReferenceVisitorNya().Visit(context.enumType()) as EnumTypeModel;
|
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)
|
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() ??
|
var constraints = context.constraint()?.Select(x => new FunctionConstraintVisitorNya().Visit(x)).ToList() ??
|
||||||
new List<ConstraintModel>();
|
new List<ConstraintModel>();
|
||||||
var tupleModel = new TypeReferenceVisitorNya().Visit(context.tupleType()) as TupleTypeModel;
|
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)
|
public override TopLevelConstructModel VisitTypeAlias(MeowParser.TypeAliasContext context)
|
||||||
|
@ -91,11 +90,3 @@ public class DeclarationVisitorNya : MeowBaseVisitorNya<TopLevelConstructModel>
|
||||||
return new TypeAliasModel(context.GetSpan(), attributes, pub, name, typeParameters, type);
|
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;
|
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 span = new Span(expression.Span.Filename, expression.Span.From, current.name.StopIndex);
|
||||||
var name = current.name.Text;
|
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, "");
|
throw new InvalidStructAccessorException(current.name, "");
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,4 +2,19 @@ namespace meowlang.parser;
|
||||||
|
|
||||||
public record IfExpressionModel(Span Span, ExpressionModel Condition, BlockExpressionModel Block,
|
public record IfExpressionModel(Span Span, ExpressionModel Condition, BlockExpressionModel Block,
|
||||||
List<(ExpressionModel Condition, BlockExpressionModel Block)> IfElses,
|
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*/;
|
NumericLiteral: DecimalNumberLiteral /*| HexadecimalNumberLiteral | OctalNumberLiteral | BinaryNumberLiteral*/;
|
||||||
|
|
||||||
fragment DecimalNumberLiteral: DecimalNumberPart ('.' DecimalNumberPart)? | '.' DecimalNumberPart;
|
fragment DecimalNumberLiteral: '-'? DecimalNumberPart ('.' DecimalNumberPart)? | '.' DecimalNumberPart;
|
||||||
fragment DecimalNumberPart: [0-9] [0-9_]*;
|
fragment DecimalNumberPart: [0-9] [0-9_]*;
|
||||||
BoolLiteral: 'true' | 'false' ;
|
BoolLiteral: 'true' | 'false' ;
|
||||||
CharacterLiteral: '\'' (CharacterEscape | ~['\\]) '\'';
|
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;
|
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;
|
namespace meowlang.parser;
|
||||||
|
|
||||||
public record SwitchExpressionModel(Span Span, ExpressionModel Expression,
|
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 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)
|
public override TypeReferenceModel VisitTupleType(MeowParser.TupleTypeContext context)
|
||||||
|
|
|
@ -40,6 +40,12 @@ public static class ObjectExtensions
|
||||||
return $"\"{val}\"";
|
return $"\"{val}\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type.IsAssignableTo(typeof(Guid)))
|
||||||
|
{
|
||||||
|
var val = o.ToString();
|
||||||
|
return $"\"{val}\"";
|
||||||
|
}
|
||||||
|
|
||||||
string json;
|
string json;
|
||||||
|
|
||||||
if (o is ITuple t)
|
if (o is ITuple t)
|
||||||
|
@ -48,7 +54,25 @@ public static class ObjectExtensions
|
||||||
return values.AutoToString();
|
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 enumerable = (o as IEnumerable)!.Cast<object>();
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
|
|
|
@ -10,7 +10,7 @@ public class VisitorNya : MeowBaseVisitorNya<Model>
|
||||||
public override Model VisitFile(MeowParser.FileContext context)
|
public override Model VisitFile(MeowParser.FileContext context)
|
||||||
{
|
{
|
||||||
var imports = context.importStatement().Select(x => new ImportVisitorNya().Visit(x)).ToList();
|
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);
|
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