commit 78fe931f97da235e7e574a213126d22090b7eea0 Author: Gwendolyn Date: Sat Feb 12 02:29:25 2022 +0100 first commit wtf am I doing diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..111f5a3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ +.idea +gen/ \ No newline at end of file diff --git a/MeowLang.sln b/MeowLang.sln new file mode 100644 index 0000000..1f402ef --- /dev/null +++ b/MeowLang.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "compiler", "compiler\compiler.csproj", "{1ABDD383-ABC6-4B3F-80FC-348879BF8AFE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "parser", "parser\parser.csproj", "{240E1F81-DB04-46CE-AB7B-60616B6D868C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1ABDD383-ABC6-4B3F-80FC-348879BF8AFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1ABDD383-ABC6-4B3F-80FC-348879BF8AFE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1ABDD383-ABC6-4B3F-80FC-348879BF8AFE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1ABDD383-ABC6-4B3F-80FC-348879BF8AFE}.Release|Any CPU.Build.0 = Release|Any CPU + {240E1F81-DB04-46CE-AB7B-60616B6D868C}.Debug|Any CPU.ActiveCfg = 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.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/README.md b/README.md new file mode 100644 index 0000000..77ab5e1 --- /dev/null +++ b/README.md @@ -0,0 +1,316 @@ +# meow + +most awesome language, mainly because it's not object-oriented and it's called meow. + +Multiple dispatch! + +Weird syntax! + +Implicit interfaces, except we call them constraints! whyyyy did I do this? + + + + + +``` + + + +``` + + + + + +### imports + +``` +import "std:strings" as str // standard library +import "std:strings/ext" as strext // nested +import "foo:bar" as fb // file bar from project foo +import ":foo/bar" // relative to project root +import "foo" // relative to current file +import "./foo" as whatever // relative to current file +import "../foobar" // relative to current file +``` + +access public thingies from imports with `:` + +``` +import foo; + +foo:somefunction(); +foo:SomeType +foo:SomeConstraint +``` + + +### functions +Functions are by default private to the current file, functions with `pub` are accessible from other files/projects. +``` +fn x() {} +pub fn y() {} +``` + +Functions can take the `[c_export]` attribute to make them callable when compiled as a static library, and to generate C headers for them. +``` +[c_export] pub fn foo(int a, Foo b, Bar c) -> int { + +} + +[c_export("renamed_foo2")] pub fn foo2(int a, Foo b, Bar c) -> int, int { + +} +``` + +Functions can be overloaded on parameter and return types +``` +fn a() -> s32 { + return -1; +} +fn a() -> u32 { + return 1; +} + +fn a(i32 x, i32 y) -> (i32, i32) { + return y, x; +} + +fn a(u32 x) -> u32 { + return x + 1; +} +``` + +### constraints +constraints are kind of weird because they + - can have multiple types, like, a constraint can constrain multiple types together + - can be used both as a constraint on generic types and as a type themselves + - but only can be used as a type when they constrain only one type, and then they're kinda like an interface? + - fuck this is weird + +``` +// this one can be used only as a constraint for generic type parameters +constraint Foo on A B { + some_func(A, B); + some_other_func(A) -> B; +} + +// this one can be used both as a constraint for generic type parameters or as an interface type +constraint Bar on A { + f1(A) -> A; + f2(A) -> i32; +} +``` + +``` +// usage as type parameter constraints +fn foo(T1 x) + where Foo T1 T2 +{ + T2 y = some_other_func(x); + some_func(x, y); +} + +fn bar(T a) -> i32 + where Bar T +{ + T b = f1(a); + return f2(b); +} + +// usage as interface types + +Bar x = getSomeTypeThatIsBar(); +Bar y = f1(x); +``` + +constraints can be embedded in other constraints like this: +``` +constraint Arithmetic on A { + operator+ (A, A) -> A; + operator- (A, A) -> A; + operator* (A, A) -> A; + operator/ (A, A) -> A; +} + +constraint Foo on A { + Arithmetic A; + some_func(A) -> A; +} + +``` + + +``` +int a = 1; +mut int b = 2; + +let a = 1; +mut let b = 2; + +``` + +### literals +#### numeric +Numeric literals are untyped but literals with a decimal point can not be used as integers. +They can contain underscores between digits which are ignored, e.g. `1_000` or `1_0_0_0`. +Unprefixed numeric literals are decimal, prefixes exist for hexadecimal (`0x`), octal (`0o`) and binary (`0b`). + +#### character +The character literal is a single unicode character enclosed in single quotes, its value is the character's unicode code point. + +#### bool +`true` and `false` + +#### string +String literals are enclosed in double quotes (e.g. `"hello world"`). +Their type is an `u8` array with length of the string in bytes. + + +### types +#### basic types + - bool: `bool` + - signed integer types: `s8`, `s16`, `s32`, `s64`, `s128` + - unsigned integer types: `u8`, `u16`, `u32`, `u64`, `u128` + - floating point types: `f32`, `f64` (possibly more?) + - decimal type? `decimal` + - complex types? `c64`, `c128` (does this refer to the total size or the size of each component?) + + +#### compound types +##### array & slice +###### declaration +``` +// array +[u8; 10] +// slice +[u8] +``` +###### 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}; +``` +##### struct +###### declaration +``` +struct Point { + s32 x; + s32 y; + s32 z; +} +type Point = struct { + s32 x; + s32 y; + s32 z; +} + +type Options = struct { + struct { + int foo; + } api; +} +``` +##### use +``` +let p1 = Point{ + x: 1, + y: 2, + z: 3, +}; +let p2: Point = { + x: 2, + y: 3, + z: 4, +}; + +let x = p1.x; + +p1.x = 2; + +let opt = Options{ + api: { + foo: 1 + } +}; + +let a: Options@api = opt.api; // yeah this is weird, try to avoid it + +``` + +##### enum +###### declaration +``` +enum A { + Foo, + Bar(u8) +} + +type A = enum { + Foo, + Bar(u8) +} + +type B = enum { + X, + Y(enum { // this is terrible but you can do it if you want for consistency reasons + A, + B + }) +} + +``` +###### use +``` +let x = A'Foo; +let y = A'Bar(10); +let z = B'Y(B@Y'A); // don't do this why would you do this +``` + +##### tuple +###### declaration +``` +type X = (s8, s8); +tuple X(s8, s8); +type Y = (struct {int a; int b}, enum {A, B}); // why would you do this aaaaa +``` +###### use +``` +let a: X = (1,2); +let b = a.0; +let c = a.1; +let d = Y({a: 1, b: 2}, Y@1'A); // oh no +let e: Y@1 = d.1; +``` + + + + + + +#### pointers +Pointers are taken with the `&` operator and dereferenced with the `*` operator. They are mostly similar to C I think. +Pointer types have a `*` appended to the type they point to. + +``` +let a: int = 1; +let b: int* = &a; +let c = *b; +``` + +A special `void*` type exists???? does it really? maybe? kinda yes? aaaaaaa + + +Pointer arithmetic works the same as in C, except that arithmetic on void pointers is allowed? + +``` +let a: [u8; 2] = {0,1}; +let b: u8* = &a[0]; +let c: u8* = b + 1; +let d: u8 = *b; // 0 +let e: u8 = *c; // 1 +*(&a[0]+1) = 2; // a is now {0,2} +``` + diff --git a/compiler/Compiler.cs b/compiler/Compiler.cs new file mode 100644 index 0000000..2905f6a --- /dev/null +++ b/compiler/Compiler.cs @@ -0,0 +1,14 @@ +using System; +using meowlang.parser; + +namespace meowlang.compiler; + +static class Compiler +{ + private static void Main(string[] args) + { + var path = "example.mew"; + var model = Parser.Parse(path); + Console.WriteLine(model.AutoToString()); + } +} \ No newline at end of file diff --git a/compiler/compiler.csproj b/compiler/compiler.csproj new file mode 100644 index 0000000..56d2268 --- /dev/null +++ b/compiler/compiler.csproj @@ -0,0 +1,26 @@ + + + + Exe + net6.0 + meow + 10 + enable + false + + meow compiler + meowlang.compiler + + + + + + + + + + PreserveNewest + + + + diff --git a/compiler/example.mew b/compiler/example.mew new file mode 100644 index 0000000..74705fb --- /dev/null +++ b/compiler/example.mew @@ -0,0 +1,91 @@ +import "std:strings"; +import "foo" as bar; +import "./foobar"; + +/* +fn foo() {} + +pub fn bar(int x) -> int {} + +pub fn multiret() -> int, int {} + +[c_export] pub fn add(int a, int b) -> int {} + +[c_export("add_floats")] pub fn add(float a, float b) -> float {} + +fn sub(T a, T b) -> T + where SUBTRACT T +{ + // do something +} + + + +fn ret_arr() -> [int; 10] {} +fn ret_slice() -> [int] {} +fn ret_multislice() -> [[int]] {} +fn ret_ptr() -> int* {} +fn ret_ptrslice() -> [int*] {} +fn ret_arrayptr() -> [int; 5]* {} +fn ret_tuple() -> (int, int) {} + +fn ret_imported() -> strings:StringBuilder {} + +fn ret_nested() -> foo@bar {} + +fn ret_imported_nested() -> foo:bar@some@nested@shit {} + + +constraint Foo A { + whatever(A); +} + +constraint Bar B { + constraint Foo B*; + constraint Foo [B]; + constraint Foo (int, B); + constraint Foo SomeType; +} + +*/ + +struct Foo { + int something; + struct { + int foo; + } bar; +} + + +Foo f; +f.something + +enum X { + A, + B, + C +} + +enum X { + A(T1), + B(T2), + C(T3), +} + +enum X where Constraint T1 T2 where Constraint T2 T3 { + A(T1), + B(T2), + C(T3), +} + +struct X { + T a; +} + +tuple Point(int,int,int) + +tuple Point where Arithmetic T (T,T,T) + +type A = Foo; + +type F = Foo>; diff --git a/parser/Meow.g4 b/parser/Meow.g4 new file mode 100644 index 0000000..57f43a9 --- /dev/null +++ b/parser/Meow.g4 @@ -0,0 +1,277 @@ +grammar Meow; + +file + : importStatement* declaration* EOF + ; + +importStatement + : ImportKeyword importpath=StringLiteral (AsKeyword importname=Identifier)? ';' + ; + +declaration + : function ';'? + | constraintDeclaration ';'? + | structDeclaration ';'? + | enumDeclaration ';'? + | tupleDeclaration ';'? + | typeAlias ';'? + ; + +structDeclaration + : StructKeyword name=Identifier (genericParameters constraint*)? structType + ; + +enumDeclaration + : EnumKeyword name=Identifier (genericParameters constraint*)? enumType + ; + +tupleDeclaration + : TupleKeyword name=Identifier (genericParameters constraint*)? tupleType + ; + +typeAlias + : TypeKeyword name=Identifier genericParameters? '=' typeReference + ; + + +constraintDeclaration + : ConstraintKeyword name=Identifier types+=Identifier+ '{' constraintRule* '}' + ; + +constraintRule + : embedConstraintRule + | functionConstraintRule + ; + +embedConstraintRule + : ConstraintKeyword name=Identifier types+=typeReference+ ';' + ; + +functionConstraintRule + : name=Identifier constraintFunctionParameters functionReturn? ';' + ; + +constraintFunctionParameters + : '(' (typeReference (',' typeReference)*)? ')' + ; + +function + : attribute* pub=PubKeyword? FnKeyword name=Identifier functionParameters functionReturn? functionBody + | attribute* pub=PubKeyword? FnKeyword name=Identifier genericParameters functionParameters functionReturn? constraint* functionBody + ; + + +attribute + : '[' name=Identifier ('(' (attributeValue (',' attributeValue)*)? ')')? ']' + ; + +attributeValue + : literal + | id=Identifier + ; + +literal + : stringLiteral + | numericLiteral + | boolLiteral + | characterLiteral + ; +stringLiteral: val=StringLiteral; +numericLiteral: val=NumericLiteral; +boolLiteral: val=BoolLiteral; +characterLiteral: val=CharacterLiteral; + +genericParameters + : '<' (name+=Identifier (',' name+=Identifier)*) '>' + ; + + +functionParameters + : '(' (functionParameter (',' functionParameter)*)? ')' + ; + +functionParameter + : typeReference name=Identifier + ; + +functionReturn + : '->' (typeReference (',' typeReference)*) + ; + +constraint + : WhereKeyword name=Identifier typenames+=Identifier+ + ; + +functionBody + : '{' statement* expression? '}' + ; + + +typeReference + : nonPointerTypeReference pointer='*'? + ; + +nonPointerTypeReference + : typeName + | arrayType + | tupleType + | StructKeyword structType + | EnumKeyword enumType + ; + +typeName + : (importName=Identifier ':')? name+=Identifier ('<' genericType+=typeName (',' genericType+=typeName)* '>')? ('@' name+=Identifier)* + ; + +arrayType + : '[' typeReference (';' length=NumericLiteral)? ']' + ; + +tupleType + : '(' typeReference (',' typeReference)* ')' + ; + +structType + : '{' structMember* '}' + ; + +structMember + : typeReference name=Identifier ';' + ; + +enumType + : '{' enumMember (',' enumMember)* ','? '}' + ; + +enumMember + : name=Identifier ('(' typeReference ')')? + ; + + +statement + : breakStatement + | continueStatement + | returnStatement + | loopStatement + | expressionStatement + ; + +breakStatement + : BreakKeyword label=Identifier? ';' + ; +continueStatement + : ContinueKeyword label=Identifier? ';' + ; +returnStatement + : ReturnKeyword expression? ';' + ; +loopStatement + : LoopKeyword '{' statement* '}' + | LoopKeyword condition=expression '{' statement* '}' + | LoopKeyword init=expression? ';' condition=expression? ';' increment=expression? '{' statement* '}' + ; + +expressionStatement + : expression ';' + ; + + +/* +todo: use C operator precedence: https://en.cppreference.com/w/c/language/operator_precedence + + binary operators: + == != > >= < <= + || + && + + - + * / % + & + | + ^ + << + >> + + unary operators: + + + - + ~ + ! + + + assignment: + = + += + -= + /= + *= + %= + &= + |= + ^= + <<= + >>= + + + */ +expression + : addExpression + ; + +addExpression + : multExpression ((op='+'|op='-') multExpression)* + ; + +multExpression + : terminalExpression ((op='*'|op='/'|op='%') terminalExpression)* + ; + +terminalExpression + : literal + | structConstructor + | arrayConstructor + | enumConstructor + | functionCall + | ifExpression + | switchExpression + | blockExpression + | parenthesisExpression + ; + +/////////////////////////// + +ImportKeyword: 'import'; +AsKeyword: 'as'; +PubKeyword: 'pub'; +FnKeyword: 'fn'; +WhereKeyword: 'where'; +ConstraintKeyword: 'constraint'; +StructKeyword: 'struct'; +EnumKeyword: 'enum'; +TupleKeyword: 'tuple'; +TypeKeyword: 'type'; +BreakKeyword: 'break'; +ContinueKeyword: 'continue'; +ReturnKeyword: 'return'; +LoopKeyword: 'loop'; + + + +// start with letters (upper or lower case) followed by any alphanumeric symbol +Identifier: [a-zA-Z_][a-zA-Z_0-9]*; + + + +NumericLiteral: DecimalNumberLiteral /*| HexadecimalNumberLiteral | OctalNumberLiteral | BinaryNumberLiteral*/; + +fragment DecimalNumberLiteral: DecimalNumberPart ('.' DecimalNumberPart)? | '.' DecimalNumberPart; +fragment DecimalNumberPart: [0-9] [0-9_]*; +BoolLiteral: 'true' | 'false' ; +CharacterLiteral: '\'' (CharacterEscape | ~['\\]) '\''; +fragment CharacterEscape: '\\\'' | '\\\\'; +StringLiteral: '"' (StringEscape | ~[\\"])*? '"'; +fragment StringEscape: '\\"' | '\\\\'; + +// comments and white space -> ignored +BLOCK_COMMENT: '/*' .*? '*/' -> channel(HIDDEN); +LINE_COMMENT: '//' ~[\n]* -> channel(HIDDEN); +WS: [ \t\r\n\f]+ -> channel(HIDDEN); \ No newline at end of file diff --git a/parser/ModelBase.cs b/parser/ModelBase.cs new file mode 100644 index 0000000..59aee58 --- /dev/null +++ b/parser/ModelBase.cs @@ -0,0 +1,3 @@ +namespace meowlang.parser; + +public record ModelBase([property:Ignore] Span span); \ No newline at end of file diff --git a/parser/Parser.cs b/parser/Parser.cs new file mode 100644 index 0000000..92fe72b --- /dev/null +++ b/parser/Parser.cs @@ -0,0 +1,20 @@ +using Antlr4.Runtime; +using meowlang.parser.antlr; + +namespace meowlang.parser; + +public class Parser +{ + public static Model? Parse(string path) + { + var lexer = new MeowLexer(new AntlrFileStream(path)); + var parser = new MeowParser(new CommonTokenStream(lexer)); + var visitor = new Visitor(); + var file = parser.file(); + if (parser.NumberOfSyntaxErrors > 0) + { + return null; + } + return visitor.Visit(file); + } +} \ No newline at end of file diff --git a/parser/Span.cs b/parser/Span.cs new file mode 100644 index 0000000..cce3422 --- /dev/null +++ b/parser/Span.cs @@ -0,0 +1,3 @@ +namespace meowlang.parser; + +public record Span(string Filename, int From, int To); \ No newline at end of file diff --git a/parser/Utils.cs b/parser/Utils.cs new file mode 100644 index 0000000..c70e241 --- /dev/null +++ b/parser/Utils.cs @@ -0,0 +1,139 @@ +using System.Collections; +using System.Reflection; +using System.Text; +using Antlr4.Runtime; +using Newtonsoft.Json; + +namespace meowlang.parser; + +[AttributeUsage(AttributeTargets.Property)] +public class IgnoreAttribute : Attribute +{ +} + +public static class ObjectExtensions +{ + public static string AutoToString(this object? o, bool prettyPrint = true, bool encapsulate = true) + { + if (o == null) return "null"; + var type = o.GetType(); + if (o is string s) + { + var escaped = s.Replace("\\", "\\\\").Replace("\"", "\\\""); + return $"\"{escaped}\""; + } + + if (o is bool b) + { + return b ? "true" : "false"; + } + + if (type.IsPrimitive) + { + return o.ToString()!; + } + + if (type.IsEnum) + { + var val = Enum.GetName(type, o); + return $"\"{val}\""; + } + + string json; + + if (type.IsAssignableTo(typeof(IEnumerable))) + { + var enumerable = (o as IEnumerable)!.Cast(); + var sb = new StringBuilder(); + sb.Append('['); + sb.AppendJoin(", ", enumerable.Select(x => x.AutoToString(false))); + sb.Append(']'); + json = sb.ToString(); + } + else + { + var properties = type.GetProperties(); + var sb = new StringBuilder(); + + sb.Append('{'); + sb.Append($"\"@type\": \"{type.Name}\""); + + + for (var index = 0; index < properties.Length; index++) + { + var propertyInfo = properties[index]; + if (propertyInfo.GetCustomAttribute() != null) continue; + sb.Append(','); + sb.Append($"\"{propertyInfo.Name}\": "); + var propertyValue = propertyInfo.GetValue(o); + sb.Append(propertyValue.AutoToString()); + } + + sb.Append('}'); + json = sb.ToString(); + } + + if (prettyPrint) + { + var temp = JsonConvert.DeserializeObject(json); + json = JsonConvert.SerializeObject(temp, Formatting.Indented); + } + + return json; + } +} + +internal static class ParserRuleContextExtensions +{ + public static Span GetSpan(this ParserRuleContext context) + { + return new Span(context.Start.TokenSource.SourceName, context.Start.StartIndex, context.Stop.StopIndex); + } +} + +internal static class TokenExtensions +{ + public static Span GetSpan(this IToken token) + { + return new Span(token.TokenSource.SourceName, token.StartIndex, token.StopIndex); + } +} + +internal static class StringExtensions +{ + public static string Unescape(this string str) + { + StringBuilder builder = new(); + var startPos = 0; + int backslashPos; + while ((backslashPos = str.IndexOf('\\', startPos)) > -1) + { + builder.Append(str[startPos..backslashPos]); + int length; + string ch; + switch (str[backslashPos + 1]) + { + case '\\': + ch = "\\"; + length = 1; + break; + case '\'': + ch = "'"; + length = 1; + break; + case '"': + ch = "\""; + length = 1; + break; + default: + throw new InvalidEscapeSequenceException($"\\{str[backslashPos + 1]}"); + } + + builder.Append(ch); + startPos = backslashPos + length + 1; + } + + builder.Append(str[startPos..]); + return builder.ToString(); + } +} \ No newline at end of file diff --git a/parser/Visitor.cs b/parser/Visitor.cs new file mode 100644 index 0000000..6a15bb9 --- /dev/null +++ b/parser/Visitor.cs @@ -0,0 +1,412 @@ +using System.Reflection.Metadata; +using System.Text; +using System.Text.RegularExpressions; +using Antlr4.Runtime; +using meowlang.parser.antlr; + +namespace meowlang.parser; + +public class InvalidEscapeSequenceException : Exception +{ + public InvalidEscapeSequenceException(string escapeSequence) : base($"invalid escape sequence `{escapeSequence}`") + { + } +} + +public class MeowVisitorException : Exception +{ + public IToken Token { get; } + + public MeowVisitorException(IToken token, string message) : base(message) + { + Token = token; + } +} + +public class InvalidImportPathException : MeowVisitorException +{ + public InvalidImportPathException(IToken context, string message) : base(context, message) + { + } +} + +public class Visitor : MeowBaseVisitor +{ + public override Model VisitFile(MeowParser.FileContext context) + { + var imports = context.importStatement().Select(x => new ImportVisitor().Visit(x)).ToList(); + var declarations = context.declaration().Select(x => new DeclarationVisitor().Visit(x)).ToList(); + + return new Model(context.GetSpan(), imports, declarations); + } +} + +public class ImportVisitor : MeowBaseVisitor +{ + private static readonly string PathSegmentPattern = "[a-z][a-z0-9_]*"; + + private static readonly Regex ProjectRegex = new Regex("^[a-z][a-z0-9_]*$"); + + private static readonly Regex PathRegexNoProject = + new Regex($"^({PathSegmentPattern}|\\.|\\.\\.)(/{PathSegmentPattern})*$"); + + private static readonly Regex PathRegex = new Regex($"^{PathSegmentPattern}(/{PathSegmentPattern})*$"); + + public override ImportModel VisitImportStatement(MeowParser.ImportStatementContext context) + { + var path = context.importpath.Text[1..^1].Unescape(); + + string? project = null; + + if (path.Contains(':')) + { + var parts = path.Split(':'); + project = parts[0]; + if (!ProjectRegex.IsMatch(project)) + { + throw new InvalidImportPathException(context.importpath, "malformed project name"); + } + + path = parts[1]; + if (!PathRegex.IsMatch(path)) + { + throw new InvalidImportPathException(context.importpath, "malformed path"); + } + } + else + { + if (!PathRegexNoProject.IsMatch(path)) + { + throw new InvalidImportPathException(context.importpath, "malformed path"); + } + } + + var alias = context.importname?.Text; + + return new ImportModel(context.GetSpan(), project, path, alias); + } +} + +public class DeclarationVisitor : MeowBaseVisitor +{ + public override DeclarationModel VisitFunction(MeowParser.FunctionContext context) + { + var attributes = context.attribute().Select(x => new AttributeVisitor().Visit(x)).ToList(); + var pub = context.pub != null; + var name = context.name.Text; + + var typeParameters = + context.genericParameters()?._name.Select(x => new TypeParameterModel(x.GetSpan(), x.Text)).ToList() ?? + new List(); + var functionParameters = context.functionParameters().functionParameter() + .Select(x => new FunctionParameterVisitor().Visit(x)).ToList(); + var functionReturns = context.functionReturn()?.typeReference().Select(x => new TypeReferenceVisitor().Visit(x)) + .ToList() ?? new List(); + var constraints = context.constraint()?.Select(x => new FunctionConstraintVisitor().Visit(x)).ToList() ?? + new List(); + + return new FunctionModel(context.GetSpan(), attributes, pub, name, typeParameters, functionParameters, + functionReturns, constraints, + new FunctionBodyModel(context.functionBody().GetSpan())); // TODO: visit function body + } + + public override ConstraintDeclarationModel VisitConstraintDeclaration( + MeowParser.ConstraintDeclarationContext context) + { + var name = context.name.Text; + var typeNames = context._types.Select(x => x.Text).ToList(); + var rules = context.constraintRule().Select(x => new ConstraintRuleVisitor().Visit(x)).ToList(); + return new ConstraintDeclarationModel(context.GetSpan(), name, typeNames, rules); + } + + public override DeclarationModel VisitStructDeclaration(MeowParser.StructDeclarationContext context) + { + var name = context.name.Text; + var typeParameters = context.genericParameters()?._name.Select(x => x.Text).ToList() ?? new List(); + var constraints = context.constraint()?.Select(x => new FunctionConstraintVisitor().Visit(x)).ToList() ?? + new List(); + var structModel = new TypeReferenceVisitor().Visit(context.structType()) as StructTypeModel; + return new StructDeclarationModel(context.GetSpan(), name, typeParameters, constraints, structModel!); + } + + public override DeclarationModel VisitEnumDeclaration(MeowParser.EnumDeclarationContext context) + { + var name = context.name.Text; + var typeParameters = context.genericParameters()?._name.Select(x => x.Text).ToList() ?? new List(); + var constraints = context.constraint()?.Select(x => new FunctionConstraintVisitor().Visit(x)).ToList() ?? + new List(); + + var enumModel = new TypeReferenceVisitor().Visit(context.enumType()) as EnumTypeModel; + return new EnumDeclarationModel(context.GetSpan(), name, typeParameters, constraints, enumModel!); + } + + public override DeclarationModel VisitTupleDeclaration(MeowParser.TupleDeclarationContext context) + { + var name = context.name.Text; + var typeParameters = context.genericParameters()?._name.Select(x => x.Text).ToList() ?? new List(); + var constraints = context.constraint()?.Select(x => new FunctionConstraintVisitor().Visit(x)).ToList() ?? + new List(); + var tupleModel = new TypeReferenceVisitor().Visit(context.tupleType()) as TupleTypeModel; + return new TupleDeclarationModel(context.GetSpan(), name, typeParameters, constraints, tupleModel!); + } + + public override DeclarationModel VisitTypeAlias(MeowParser.TypeAliasContext context) + { + var name = context.name.Text; + var typeParameters = context.genericParameters()?._name.Select(x => x.Text).ToList() ?? new List(); + var type = new TypeReferenceVisitor().Visit(context.typeReference()); + return new TypeAliasModel(context.GetSpan(), name, typeParameters, type); + } +} + +public class AttributeVisitor : MeowBaseVisitor +{ + public override AttributeModel VisitAttribute(MeowParser.AttributeContext context) + { + var name = context.name.Text; + var values = context.attributeValue().Select(x => new AttributeValueVisitor().Visit(x)).ToList(); + return new AttributeModel(context.GetSpan(), name, values); + } +} + +public class AttributeValueVisitor : MeowBaseVisitor +{ + public override AttributeValueModel VisitAttributeValue(MeowParser.AttributeValueContext context) + { + if (context.id != null) + { + return new IdentifierAttributeValueModel(context.GetSpan(), context.id.Text); + } + + var literal = new LiteralVisitor().Visit(context.literal()); + return new LiteralAttributeValueModel(context.GetSpan(), literal); + } +} + +public class FunctionParameterVisitor : MeowBaseVisitor +{ + public override FunctionParameterModel VisitFunctionParameter(MeowParser.FunctionParameterContext context) + { + var type = new TypeReferenceVisitor().Visit(context.typeReference()); + var name = context.name.Text; + return new FunctionParameterModel(context.GetSpan(), type, name); + } +} + +public class TypeReferenceVisitor : MeowBaseVisitor +{ + public override TypeReferenceModel VisitTypeReference(MeowParser.TypeReferenceContext context) + { + var type = Visit(context.nonPointerTypeReference()); + if (context.pointer != null) + { + return new PointerTypeReferenceModel(context.GetSpan(), type); + } + + return type; + } + + public override TypeReferenceModel VisitTypeName(MeowParser.TypeNameContext context) + { + var import = context.importName?.Text; + var nestedName = context._name.Select(x => x.Text).ToList(); + + var typeParameters = context._genericType.Select(Visit).ToList(); + + return new TypeNameModel(context.GetSpan(), import, nestedName[0], typeParameters, nestedName.Skip(1).ToList()); + } + + public override TypeReferenceModel VisitArrayType(MeowParser.ArrayTypeContext context) + { + var type = Visit(context.typeReference()); + if (context.length == null) + { + return new SliceTypeModel(context.GetSpan(), type); + } + + return new ArrayTypeModel(context.GetSpan(), type, context.length.Text); + } + + public override TypeReferenceModel VisitTupleType(MeowParser.TupleTypeContext context) + { + var types = context.typeReference().Select(Visit).ToList(); + return new TupleTypeModel(context.GetSpan(), types); + } + + public override TypeReferenceModel VisitStructType(MeowParser.StructTypeContext context) + { + var members = context.structMember().Select(x => new StructMemberVisitor().Visit(x)).ToList(); + return new StructTypeModel(context.GetSpan(), members); + } + + public override TypeReferenceModel VisitEnumType(MeowParser.EnumTypeContext context) + { + var members = context.enumMember().Select(x => new EnumMemberVisitor().Visit(x)).ToList(); + return new EnumTypeModel(context.GetSpan(), members); + } +} + +public class StructMemberVisitor : MeowBaseVisitor +{ + public override StructMemberModel VisitStructMember(MeowParser.StructMemberContext context) + { + var type = new TypeReferenceVisitor().Visit(context.typeReference()); + var name = context.name.Text; + return new StructMemberModel(context.GetSpan(), type, name); + } +} + +public class EnumMemberVisitor : MeowBaseVisitor +{ + public override EnumMemberModel VisitEnumMember(MeowParser.EnumMemberContext context) + { + var name = context.name.Text; + + TypeReferenceModel? type = null; + var typeRef = context.typeReference(); + if (typeRef != null) + { + type = new TypeReferenceVisitor().Visit(typeRef); + } + + return new EnumMemberModel(context.GetSpan(), name, type); + } +} + +public class FunctionConstraintVisitor : MeowBaseVisitor +{ + public override ConstraintModel VisitConstraint(MeowParser.ConstraintContext context) + { + var name = context.name.Text; + var typeNames = context._typenames.Select(x => x.Text).ToList(); + return new ConstraintModel(context.GetSpan(), name, typeNames); + } +} + +public class LiteralVisitor : MeowBaseVisitor +{ + public override LiteralModel VisitStringLiteral(MeowParser.StringLiteralContext context) + { + var value = context.val.Text[1..^1].Unescape(); + return new StringLiteralModel(context.GetSpan(), value); + } + + public override LiteralModel VisitBoolLiteral(MeowParser.BoolLiteralContext context) + { + var text = context.val.Text; + var value = text == "true"; + return new BoolLiteralModel(context.GetSpan(), value); + } + + public override LiteralModel VisitNumericLiteral(MeowParser.NumericLiteralContext context) + { + return new NumericLiteralModel(context.GetSpan(), context.val.Text); + } + + public override LiteralModel VisitCharacterLiteral(MeowParser.CharacterLiteralContext context) + { + var value = context.val.Text[1..^1].Unescape(); + return new CharacterLiteralModel(context.GetSpan(), value); + } +} + +public class ConstraintRuleVisitor : MeowBaseVisitor +{ + public override ConstraintRuleModel VisitEmbedConstraintRule(MeowParser.EmbedConstraintRuleContext context) + { + var name = context.name.Text; + var types = context._types.Select(x => new TypeReferenceVisitor().Visit(x)).ToList(); + return new EmbedConstraintRuleModel(context.GetSpan(), name, types); + } + + public override ConstraintRuleModel VisitFunctionConstraintRule(MeowParser.FunctionConstraintRuleContext context) + { + var name = context.name.Text; + var parameters = context.constraintFunctionParameters().typeReference() + .Select(x => new TypeReferenceVisitor().Visit(x)).ToList(); + var returns = + context.functionReturn()?.typeReference().Select(x => new TypeReferenceVisitor().Visit(x)).ToList() ?? + new List(); + return new FunctionConstraintRuleModel(context.GetSpan(), name, parameters, returns); + } +} + +public record Model(Span span, List Imports, List Declarations) : ModelBase(span); + +public record ImportModel(Span span, string? Project, string Path, string? Alias) : ModelBase(span); + +public abstract record DeclarationModel(Span span) : ModelBase(span); + +public record FunctionModel(Span span, List Attributes, bool Pub, string Name, + List TypeParameters, List Parameters, List Returns, + List Constraints, FunctionBodyModel Body) : DeclarationModel(span); + +public record AttributeModel(Span span, string Name, List Values) : ModelBase(span); + +public abstract record AttributeValueModel(Span span) : ModelBase(span); + +public record IdentifierAttributeValueModel(Span span, string Name) : AttributeValueModel(span); + +public record LiteralAttributeValueModel(Span span, LiteralModel Literal) : AttributeValueModel(span); + +public record TypeParameterModel(Span span, string Name) : ModelBase(span); + +public record FunctionParameterModel(Span span, TypeReferenceModel Type, string Name) : ModelBase(span); + +public abstract record TypeReferenceModel(Span span) : ModelBase(span); + +public record PointerTypeReferenceModel(Span span, TypeReferenceModel Type) : TypeReferenceModel(span); + +public record TypeNameModel(Span span, string? ImportName, string Name, List TypeParameters, + List Nested) : TypeReferenceModel(span); + +public record ArrayTypeModel(Span span, TypeReferenceModel Type, string Length) : TypeReferenceModel(span); + +public record SliceTypeModel(Span span, TypeReferenceModel Type) : TypeReferenceModel(span); + +public record TupleTypeModel(Span span, List Types) : TypeReferenceModel(span); + +public record StructTypeModel(Span span, List Members) : TypeReferenceModel(span); + +public record StructMemberModel(Span span, TypeReferenceModel Type, string Name) : ModelBase(span); + +public record EnumTypeModel(Span span, List Members) : TypeReferenceModel(span); + +public record EnumMemberModel(Span span, string Name, TypeReferenceModel? Type) : ModelBase(span); + +public record ConstraintModel(Span span, string Name, List TypeNames) : ModelBase(span); + +public record FunctionBodyModel(Span span) : ModelBase(span); + +public abstract record LiteralModel(Span span) : ModelBase(span); + +public record StringLiteralModel(Span span, string Value) : LiteralModel(span); + +public record CharacterLiteralModel(Span span, string Value) : LiteralModel(span); + +public record NumericLiteralModel(Span span, string StringValue) : LiteralModel(span); + +public record BoolLiteralModel(Span span, bool Value) : LiteralModel(span); + +public record ConstraintDeclarationModel + (Span span, string Name, List TypeNames, List Rules) : DeclarationModel(span); + +public abstract record ConstraintRuleModel(Span span) : ModelBase(span); + +public record EmbedConstraintRuleModel + (Span span, string Name, List Types) : ConstraintRuleModel(span); + +public record FunctionConstraintRuleModel(Span span, string Name, List Parameters, + List Returns) : ConstraintRuleModel(span); + +public record StructDeclarationModel(Span span, string Name, List TypeParameters, + List Constraints, StructTypeModel Struct) : DeclarationModel(span); + +public record EnumDeclarationModel(Span span, string Name, List TypeParameters, + List Constraints, EnumTypeModel Enum) : DeclarationModel(span); + +public record TupleDeclarationModel(Span span, string Name, List TypeParameters, + List Constraints, TupleTypeModel Tuple) : DeclarationModel(span); + +public record TypeAliasModel + (Span span, string Name, List TypeParameters, TypeReferenceModel Type) : DeclarationModel(span); \ No newline at end of file diff --git a/parser/parser.csproj b/parser/parser.csproj new file mode 100644 index 0000000..dfc30f6 --- /dev/null +++ b/parser/parser.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + enable + enable + meowlang.parser + + + + + + + +