From 3841937dc781f54171c1c608878bc712529a738c Mon Sep 17 00:00:00 2001 From: Summpot Date: Sun, 5 Nov 2023 23:38:30 +0800 Subject: [PATCH] Update --- src/TreeSitterSharp.C/CLanguageProvider.cs | 4 +- src/TreeSitterSharp.C/CParser.cs | 13 +- src/TreeSitterSharp.C/CSyntaxNode.cs | 6 +- src/TreeSitterSharp.C/CSyntaxTree.cs | 7 +- .../JsonLanguageProvider.cs | 3 +- src/TreeSitterSharp.Json/JsonParser.cs | 19 ++- src/TreeSitterSharp.Json/JsonSyntaxNode.cs | 16 ++ src/TreeSitterSharp.Json/JsonSyntaxTree.cs | 25 +++ .../Extensions.cs | 22 +++ .../NodeTypesGenerator.cs | 161 ++++++++++++++++-- ...INativeObject.cs => INativeConvertible.cs} | 2 +- src/TreeSitterSharp/IParser.cs | 7 +- src/TreeSitterSharp/ISyntaxNode.cs | 16 ++ src/TreeSitterSharp/ISyntaxNodeCreation.cs | 14 ++ src/TreeSitterSharp/ISyntaxTree.cs | 15 ++ src/TreeSitterSharp/ISyntaxTreeCreation.cs | 14 ++ src/TreeSitterSharp/Language.cs | 2 +- src/TreeSitterSharp/Parser.cs | 24 +-- src/TreeSitterSharp/Point.cs | 6 +- src/TreeSitterSharp/Query.cs | 27 --- src/TreeSitterSharp/QueryCursor.cs | 43 ----- src/TreeSitterSharp/QueryMatch.cs | 19 --- src/TreeSitterSharp/SyntaxNode.cs | 58 ++++--- src/TreeSitterSharp/SyntaxTree.cs | 8 +- src/TreeSitterSharp/SyntaxTreeCursor.cs | 51 ------ tests/TreeSitterSharp.C.Tests/CParserTests.cs | 28 --- .../JsonParserTests.cs | 14 +- 27 files changed, 373 insertions(+), 251 deletions(-) create mode 100644 src/TreeSitterSharp.Json/JsonSyntaxNode.cs create mode 100644 src/TreeSitterSharp.Json/JsonSyntaxTree.cs rename src/TreeSitterSharp/{INativeObject.cs => INativeConvertible.cs} (76%) create mode 100644 src/TreeSitterSharp/ISyntaxNode.cs create mode 100644 src/TreeSitterSharp/ISyntaxNodeCreation.cs create mode 100644 src/TreeSitterSharp/ISyntaxTree.cs create mode 100644 src/TreeSitterSharp/ISyntaxTreeCreation.cs delete mode 100644 src/TreeSitterSharp/Query.cs delete mode 100644 src/TreeSitterSharp/QueryCursor.cs delete mode 100644 src/TreeSitterSharp/QueryMatch.cs delete mode 100644 src/TreeSitterSharp/SyntaxTreeCursor.cs diff --git a/src/TreeSitterSharp.C/CLanguageProvider.cs b/src/TreeSitterSharp.C/CLanguageProvider.cs index 3f7513c..21fa09d 100644 --- a/src/TreeSitterSharp.C/CLanguageProvider.cs +++ b/src/TreeSitterSharp.C/CLanguageProvider.cs @@ -1,12 +1,14 @@ using System.Runtime.InteropServices; using TreeSitterSharp.Native; +using TreeSitterSharp; namespace TreeSitterSharp.C; + +[LanguageName("C")] internal class CLanguageProvider : ILanguageProvider { [DllImport("tree-sitter-c", EntryPoint = "tree_sitter_c", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] private static extern unsafe TsLanguage* tree_sitter_c(); - public static unsafe Language GetLanguage() { return new Language(tree_sitter_c()); diff --git a/src/TreeSitterSharp.C/CParser.cs b/src/TreeSitterSharp.C/CParser.cs index 6d57bc7..6a87aae 100644 --- a/src/TreeSitterSharp.C/CParser.cs +++ b/src/TreeSitterSharp.C/CParser.cs @@ -3,21 +3,12 @@ namespace TreeSitterSharp.C; -public unsafe class CParser : Parser +public unsafe class CParser : Parser { public CParser() : base(CLanguageProvider.GetLanguage()) { } - public override CSyntaxTree Parse(string code) - { - return new CSyntaxTree(Ts.parser_parse_string(_parser, null, code, (uint)code.Length)); - } - - public override CSyntaxTree Parse(Span code, Encoding encoding) - { - byte[] bytes = Encoding.UTF8.GetBytes(encoding.GetString(code)); - return new CSyntaxTree(Ts.parser_parse_string_encoding(_parser, null, bytes, (uint)bytes.Length, TsInputEncoding.TSInputEncodingUTF8)); - } + public override CSyntaxTree Parse(Span code) => new(ParseCore(code)); } diff --git a/src/TreeSitterSharp.C/CSyntaxNode.cs b/src/TreeSitterSharp.C/CSyntaxNode.cs index 17aff2b..cec585e 100644 --- a/src/TreeSitterSharp.C/CSyntaxNode.cs +++ b/src/TreeSitterSharp.C/CSyntaxNode.cs @@ -6,11 +6,13 @@ using TreeSitterSharp.Native; namespace TreeSitterSharp.C; -public class CSyntaxNode : SyntaxNode + +public partial class CSyntaxNode : SyntaxNode, ISyntaxNodeCreation { protected internal CSyntaxNode(TsNode node) : base(node) { } - + + public static partial CSyntaxNode Create(TsNode node); } diff --git a/src/TreeSitterSharp.C/CSyntaxTree.cs b/src/TreeSitterSharp.C/CSyntaxTree.cs index 6b48210..66b501b 100644 --- a/src/TreeSitterSharp.C/CSyntaxTree.cs +++ b/src/TreeSitterSharp.C/CSyntaxTree.cs @@ -4,14 +4,17 @@ using System.Text; using System.Threading.Tasks; using System.Xml.Linq; +using Microsoft.CodeAnalysis; using TreeSitterSharp.Native; namespace TreeSitterSharp.C; -public unsafe class CSyntaxTree : SyntaxTree +public unsafe class CSyntaxTree : SyntaxTree, ISyntaxTreeCreation { protected internal CSyntaxTree(TsTree* tree) : base(tree) { } public TranslationUnit TranslationUnit => new(Ts.tree_root_node(_tree)); - public override SyntaxNode Root => TranslationUnit; + public override CSyntaxNode Root => TranslationUnit; + public override CSyntaxTree Copy() => new(Ts.tree_copy(_tree)); + public static CSyntaxTree Create(TsTree* tree) => new(tree); } diff --git a/src/TreeSitterSharp.Json/JsonLanguageProvider.cs b/src/TreeSitterSharp.Json/JsonLanguageProvider.cs index 2ba930a..6464b22 100644 --- a/src/TreeSitterSharp.Json/JsonLanguageProvider.cs +++ b/src/TreeSitterSharp.Json/JsonLanguageProvider.cs @@ -1,5 +1,6 @@ using System.Runtime.InteropServices; using TreeSitterSharp.Native; +using TreeSitterSharp; namespace TreeSitterSharp.Json { @@ -7,7 +8,7 @@ internal unsafe class JsonLanguageProvider : ILanguageProvider { [DllImport("tree-sitter-json", EntryPoint = "tree_sitter_json", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] private static extern TsLanguage* tree_sitter_json(); - + public static Language GetLanguage() => new(tree_sitter_json()); } } diff --git a/src/TreeSitterSharp.Json/JsonParser.cs b/src/TreeSitterSharp.Json/JsonParser.cs index fb55248..3f61d54 100644 --- a/src/TreeSitterSharp.Json/JsonParser.cs +++ b/src/TreeSitterSharp.Json/JsonParser.cs @@ -1,3 +1,20 @@  +using System.Text; + namespace TreeSitterSharp.Json; -public class JsonParser() : Parser(JsonLanguageProvider.GetLanguage()); + +public class JsonParser : Parser +{ + public JsonParser() : base(JsonLanguageProvider.GetLanguage()) + { + + } + + public override JsonSyntaxTree Parse(Span code) + { + unsafe + { + return new(ParseCore(code)); + } + } +} diff --git a/src/TreeSitterSharp.Json/JsonSyntaxNode.cs b/src/TreeSitterSharp.Json/JsonSyntaxNode.cs new file mode 100644 index 0000000..b7f3238 --- /dev/null +++ b/src/TreeSitterSharp.Json/JsonSyntaxNode.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TreeSitterSharp.Native; + +namespace TreeSitterSharp.Json; +public class JsonSyntaxNode : SyntaxNode, ISyntaxNodeCreation +{ + protected internal JsonSyntaxNode(TsNode node) : base(node) + { + } + + public static JsonSyntaxNode Create(TsNode node) => new(node); +} diff --git a/src/TreeSitterSharp.Json/JsonSyntaxTree.cs b/src/TreeSitterSharp.Json/JsonSyntaxTree.cs new file mode 100644 index 0000000..2d7418a --- /dev/null +++ b/src/TreeSitterSharp.Json/JsonSyntaxTree.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TreeSitterSharp.Native; + +namespace TreeSitterSharp.Json; +public class JsonSyntaxTree : SyntaxTree, ISyntaxTreeCreation +{ + internal unsafe JsonSyntaxTree(TsTree* tree) : base(tree) + { + } + + public override JsonSyntaxTree Copy() + { + unsafe + { + return new(Ts.tree_copy(_tree)); + } + } + + public override JsonSyntaxNode Root { get; } + public static unsafe JsonSyntaxTree Create(TsTree* tree) => new(tree); +} diff --git a/src/TreeSitterSharp.NodeTypesSourceGenerators/Extensions.cs b/src/TreeSitterSharp.NodeTypesSourceGenerators/Extensions.cs index 7657787..6341a83 100644 --- a/src/TreeSitterSharp.NodeTypesSourceGenerators/Extensions.cs +++ b/src/TreeSitterSharp.NodeTypesSourceGenerators/Extensions.cs @@ -6,7 +6,29 @@ namespace TreeSitterSharp.NodeTypesSourceGenerators; internal static class Extensions { + public static T? GetConstructorArgument(this AttributeData attribute, int index) + { + if (TryGetConstructorArgument(attribute, index, out var result)) + { + return result; + } + return default; + } + public static bool TryGetConstructorArgument(this AttributeData attribute, int index, out T? result) + { + if (index < attribute.ConstructorArguments.Length) + { + if (attribute.ConstructorArguments.ElementAtOrDefault(index).Value is T o) + { + result = o; + return true; + } + } + result = default; + return false; + + } public static T If(this T syntaxNode, bool condition, Func func) where T : SyntaxNode { return condition ? func(syntaxNode) : syntaxNode; diff --git a/src/TreeSitterSharp.NodeTypesSourceGenerators/NodeTypesGenerator.cs b/src/TreeSitterSharp.NodeTypesSourceGenerators/NodeTypesGenerator.cs index 72b99a5..3af4986 100644 --- a/src/TreeSitterSharp.NodeTypesSourceGenerators/NodeTypesGenerator.cs +++ b/src/TreeSitterSharp.NodeTypesSourceGenerators/NodeTypesGenerator.cs @@ -3,6 +3,7 @@ using Humanizer; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace TreeSitterSharp.NodeTypesSourceGenerators; @@ -12,24 +13,61 @@ internal class NodeTypesGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) { + + var languageProvider = context.SyntaxProvider.ForAttributeWithMetadataName( + "TreeSitterSharp.LanguageNameAttribute", (node, _) => node is ClassDeclarationSyntax, + (syntaxContext, _) => syntaxContext).Collect(); + var source = context.AdditionalTextsProvider.Combine(context.AnalyzerConfigOptionsProvider).Where(t => t.Right.GetOptions(t.Left).TryGetValue("build_metadata.AdditionalFiles.SourceItemGroup", out string? sourceItemGroup) && sourceItemGroup == "NodeTypesSchema" && t.Left.Path.EndsWith(".json")) .Select((t, _) => t.Left).Collect(); - context.RegisterSourceOutput(source, (spc, texts) => + context.RegisterPostInitializationOutput((initializationContext => { - var compilationUnit = CompilationUnit(); - foreach (AdditionalText nodeTypesText in texts) + string attributeSourceText = """ + namespace TreeSitterSharp; + + [AttributeUsage(AttributeTargets.Class)] + public class LanguageNameAttribute : System.Attribute { + public LanguageNameAttribute(string name) => Name = name; + + public string Name { get; set; } + } + + """; + initializationContext.AddSource("LanguageNameAttribute.cs", attributeSourceText); + })); + + context.RegisterSourceOutput(source.Combine(languageProvider), (spc, input) => + { + var compilationUnit = CompilationUnit(); - var nodeTypesInfo = JsonSerializer.Deserialize(nodeTypesText.GetText().ToString()); + if (input.Right.IsDefaultOrEmpty) + { + return; + } + var languageNameAttributeSyntaxContext = input.Right.SingleOrDefault(); + string? languageName = languageNameAttributeSyntaxContext.Attributes.SingleOrDefault()?.GetConstructorArgument(0); + compilationUnit = + compilationUnit + .AddUsings(UsingDirective(IdentifierName("System.Linq")) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), + UsingDirective(IdentifierName("System.Collections.Immutable")) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))) + .AddMembers(FileScopedNamespaceDeclaration(IdentifierName($"TreeSitterSharp.{languageName}"))); + foreach (AdditionalText nodeTypesText in input.Left) + { + var nodeTypesInfo = JsonSerializer.Deserialize(nodeTypesText.GetText()!.ToString()); if (nodeTypesInfo is null) { continue; } + var subtypeLookup = new Dictionary>(); + var grammarLookup = new Dictionary(); foreach (NodeTypesInfo.NodeTypeInfo nodeTypeInfo in nodeTypesInfo) { if (nodeTypeInfo.Subtypes is { } subtypes) @@ -41,18 +79,22 @@ public void Initialize(IncrementalGeneratorInitializationContext context) stack.Push(nodeTypeInfo.Type); continue; } + subtypeLookup.Add(subtype.Type, new Stack(new[] { nodeTypeInfo.Type })); } } + if (nodeTypeInfo.Named) { string className = nodeTypeInfo.Type.Pascalize(); string grammarType = nodeTypeInfo.Type; + grammarLookup.Add(grammarType, className); var classDeclaration = ClassDeclaration(Identifier(className)) .AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.UnsafeKeyword), Token(SyntaxKind.PartialKeyword)) - .If(nodeTypeInfo.Type.StartsWith("_") || !subtypeLookup.ContainsKey(nodeTypeInfo.Type), _ => _.AddBaseListTypes( - SimpleBaseType(IdentifierName("global::TreeSitterSharp.C.CSyntaxNode")))) + .If(nodeTypeInfo.Type.StartsWith("_") || !subtypeLookup.ContainsKey(nodeTypeInfo.Type), _ => + _.AddBaseListTypes( + SimpleBaseType(IdentifierName("global::TreeSitterSharp.C.CSyntaxNode")))) .If(subtypeLookup.TryGetValue(nodeTypeInfo.Type, out var stack) && stack.Count > 0, _ => _.AddBaseListTypes(SimpleBaseType(IdentifierName(stack!.Pop().Pascalize())))) .AddMembers(ConstructorDeclaration(className) @@ -90,18 +132,115 @@ public void Initialize(IncrementalGeneratorInitializationContext context) ArgumentList().AddArguments(Argument(InvocationExpression( MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName("global::TreeSitterSharp.Native.Ts"), - IdentifierName("node_child_by_field_name")), ArgumentList().AddArguments(Argument(IdentifierName("_node")), - Argument(LiteralExpression(SyntaxKind.StringLiteralExpression, - Literal(fieldName))), Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, - Literal(fieldName.Length))))))), null))) + IdentifierName("node_child_by_field_name")), ArgumentList() + .AddArguments(Argument(IdentifierName("_node")), + Argument(LiteralExpression(SyntaxKind.StringLiteralExpression, + Literal(fieldName))), Argument(LiteralExpression( + SyntaxKind.NumericLiteralExpression, + Literal(fieldName.Length))))))), null))) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))); } } + + if (nodeTypeInfo.Children is { } children) + { + if (children.Types is { } types) + { + if (children.Multiple) + { + foreach (NodeTypesInfo.SubtypeElement type in types) + { + if (!type.Named) + { + continue; + } + string? typeName = type.Type.Pascalize(); + classDeclaration = classDeclaration.AddMembers( + PropertyDeclaration( + GenericName("global::System.Collections.Immutable.ImmutableArray") + .AddTypeArgumentListArguments( + IdentifierName(typeName)), + type.Type.Pascalize().Pluralize()) + .AddModifiers(Token(SyntaxKind.PublicKeyword)) + .WithExpressionBody(ArrowExpressionClause( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("Children"), + GenericName(Identifier("OfType")) + .AddTypeArgumentListArguments( + IdentifierName(typeName)))), + IdentifierName("ToImmutableArray"))))) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))); + } + } + else + { + // We can't be sure if Types always has only one element when Multiple is false. + foreach (NodeTypesInfo.SubtypeElement type in types) + { + if (!type.Named) + { + continue; + } + string? typeName = type.Type.Pascalize(); + classDeclaration = classDeclaration.AddMembers( + PropertyDeclaration(IdentifierName(typeName), + type.Type.Pascalize()) + .AddModifiers(Token(SyntaxKind.PublicKeyword)) + .WithExpressionBody(ArrowExpressionClause( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("Children"), + GenericName(Identifier("OfType")) + .AddTypeArgumentListArguments( + IdentifierName(typeName)))), + IdentifierName("SingleOrDefault"))))) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))); + } + } + } + } compilationUnit = compilationUnit.AddMembers(classDeclaration); + + } } + + var switchStatement = SwitchStatement(InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("global::TreeSitterSharp.Native.Ts"), IdentifierName("node_grammar_type")), + ArgumentList().AddArguments(Argument(IdentifierName("node"))))); + foreach (KeyValuePair kvp in grammarLookup) + { + switchStatement = switchStatement.AddSections(SwitchSection() + .AddLabels(CaseSwitchLabel(LiteralExpression(SyntaxKind.StringLiteralExpression, + Literal(kvp.Key)))) + .AddStatements(ReturnStatement(ObjectCreationExpression(Token(SyntaxKind.NewKeyword), + IdentifierName(kvp.Key.Pascalize()), + ArgumentList().AddArguments(Argument(IdentifierName("node"))), null)))); + } + + switchStatement = switchStatement.AddSections(SwitchSection().AddLabels(DefaultSwitchLabel()) + .AddStatements(ReturnStatement(ObjectCreationExpression(Token(SyntaxKind.NewKeyword), + IdentifierName($"{languageName}SyntaxNode"), + ArgumentList().AddArguments(Argument(IdentifierName("node"))), null)))); + compilationUnit = compilationUnit.AddMembers(ClassDeclaration($"{languageName}SyntaxNode") + .AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.PartialKeyword)) + .AddMembers(MethodDeclaration(IdentifierName($"{languageName}SyntaxNode"), "Create") + .AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword), Token(SyntaxKind.PartialKeyword)) + .AddParameterListParameters(Parameter(Identifier("node")).WithType(IdentifierName("global::TreeSitterSharp.Native.TsNode"))) + .AddBodyStatements(switchStatement))); + + spc.AddSource("NodeTypes.g.cs", compilationUnit.NormalizeWhitespace().GetText(Encoding.UTF8)); } - spc.AddSource("NodeTypes.g.cs", compilationUnit.NormalizeWhitespace().GetText(Encoding.UTF8)); }); } } diff --git a/src/TreeSitterSharp/INativeObject.cs b/src/TreeSitterSharp/INativeConvertible.cs similarity index 76% rename from src/TreeSitterSharp/INativeObject.cs rename to src/TreeSitterSharp/INativeConvertible.cs index 18e98fa..5822b3c 100644 --- a/src/TreeSitterSharp/INativeObject.cs +++ b/src/TreeSitterSharp/INativeConvertible.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; namespace TreeSitterSharp; -internal interface INativeObject where T : struct +public interface INativeConvertible where T : struct { unsafe T* ToUnmanaged(); } diff --git a/src/TreeSitterSharp/IParser.cs b/src/TreeSitterSharp/IParser.cs index debf0eb..eb3e47d 100644 --- a/src/TreeSitterSharp/IParser.cs +++ b/src/TreeSitterSharp/IParser.cs @@ -5,7 +5,10 @@ using System.Threading.Tasks; namespace TreeSitterSharp; -internal interface IParser where TSyntaxTree : SyntaxTree +public interface IParser + where TSelf : IParser + where TSyntaxTree : ISyntaxTree + where TSyntaxNode : ISyntaxNode { - + TSyntaxTree Parse(Span code); } diff --git a/src/TreeSitterSharp/ISyntaxNode.cs b/src/TreeSitterSharp/ISyntaxNode.cs new file mode 100644 index 0000000..c5caafe --- /dev/null +++ b/src/TreeSitterSharp/ISyntaxNode.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TreeSitterSharp.Native; + +namespace TreeSitterSharp; +public interface ISyntaxNode + where TSelf : ISyntaxNode + where TSyntaxTree : ISyntaxTree +{ + TSyntaxTree Tree { get; } + + +} diff --git a/src/TreeSitterSharp/ISyntaxNodeCreation.cs b/src/TreeSitterSharp/ISyntaxNodeCreation.cs new file mode 100644 index 0000000..6c28bca --- /dev/null +++ b/src/TreeSitterSharp/ISyntaxNodeCreation.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TreeSitterSharp.Native; + +namespace TreeSitterSharp; +public interface ISyntaxNodeCreation + where TSelf : ISyntaxNode + where TSyntaxTree : ISyntaxTree +{ + static abstract TSelf Create(TsNode node); +} diff --git a/src/TreeSitterSharp/ISyntaxTree.cs b/src/TreeSitterSharp/ISyntaxTree.cs new file mode 100644 index 0000000..ab871c6 --- /dev/null +++ b/src/TreeSitterSharp/ISyntaxTree.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using TreeSitterSharp.Native; + +namespace TreeSitterSharp; +public interface ISyntaxTree + where TSelf : ISyntaxTree + where TSyntaxNode : ISyntaxNode +{ + TSelf Copy(); +} diff --git a/src/TreeSitterSharp/ISyntaxTreeCreation.cs b/src/TreeSitterSharp/ISyntaxTreeCreation.cs new file mode 100644 index 0000000..a679166 --- /dev/null +++ b/src/TreeSitterSharp/ISyntaxTreeCreation.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TreeSitterSharp.Native; + +namespace TreeSitterSharp; +public interface ISyntaxTreeCreation + where TSelf : ISyntaxTree + where TSyntaxNode : ISyntaxNode +{ + static abstract unsafe TSelf Create(TsTree* tree); +} diff --git a/src/TreeSitterSharp/Language.cs b/src/TreeSitterSharp/Language.cs index 58274ec..3279bc7 100644 --- a/src/TreeSitterSharp/Language.cs +++ b/src/TreeSitterSharp/Language.cs @@ -1,7 +1,7 @@ using TreeSitterSharp.Native; namespace TreeSitterSharp; -public unsafe class Language:INativeObject +public unsafe class Language:INativeConvertible { private TsLanguage* _language; diff --git a/src/TreeSitterSharp/Parser.cs b/src/TreeSitterSharp/Parser.cs index 91e174e..77570b3 100644 --- a/src/TreeSitterSharp/Parser.cs +++ b/src/TreeSitterSharp/Parser.cs @@ -3,12 +3,17 @@ using TreeSitterSharp.Native; namespace TreeSitterSharp; -public unsafe class Parser: INativeObject + +public abstract unsafe class Parser : INativeConvertible, + IParser + where TSelf : Parser + where TSyntaxNode : ISyntaxNode + where TSyntaxTree : SyntaxTree { protected TsParser* _parser; private Language? _language; - public Parser(Language language) + protected Parser(Language language) { static void* NewMalloc(nuint byteCount) => NativeMemory.Alloc(byteCount); static void* NewCalloc(nuint count, nuint size) => NativeMemory.AllocZeroed(count * size); @@ -39,14 +44,11 @@ public Language? Language } } - public virtual SyntaxTree Parse(string code) - { - return new SyntaxTree(Ts.parser_parse_string(_parser, null, code, (uint)code.Length)); - } + public TSyntaxTree Parse(string code) => Parse(Encoding.UTF8.GetBytes(code)); - public virtual SyntaxTree Parse(Span code, Encoding encoding) - { - byte[] bytes = Encoding.UTF8.GetBytes(encoding.GetString(code)); - return new SyntaxTree(Ts.parser_parse_string_encoding(_parser, null, bytes, (uint)bytes.Length, TsInputEncoding.TSInputEncodingUTF8)); - } + public abstract TSyntaxTree Parse(Span code); + + protected TsTree* ParseCore(Span code) => + Ts.parser_parse_string_encoding(_parser, null, code.ToArray(), (uint)code.Length, + TsInputEncoding.TSInputEncodingUTF8); } diff --git a/src/TreeSitterSharp/Point.cs b/src/TreeSitterSharp/Point.cs index 11b2157..887d59c 100644 --- a/src/TreeSitterSharp/Point.cs +++ b/src/TreeSitterSharp/Point.cs @@ -1,4 +1,8 @@ -namespace TreeSitterSharp; +using System.Diagnostics; + +namespace TreeSitterSharp; + +[DebuggerDisplay("({Row},{Column})")] public struct Point { public uint Row; diff --git a/src/TreeSitterSharp/Query.cs b/src/TreeSitterSharp/Query.cs deleted file mode 100644 index 93b4b56..0000000 --- a/src/TreeSitterSharp/Query.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Runtime.CompilerServices; -using TreeSitterSharp.Native; - -namespace TreeSitterSharp; -public unsafe class Query : INativeObject -{ - private TsQuery* _query; - - - private Query() - { - - } - - TsQuery* INativeObject.ToUnmanaged() => _query; - - public static Query Create(Language language, string source, out uint errorOffset, out TsQueryError error) - { - errorOffset = default; - error = default; - return new Query - { - _query = Ts.query_new(language.ToUnmanaged(), source, (uint)source.Length, (uint*)Unsafe.AsPointer(ref errorOffset), - (TsQueryError*)Unsafe.AsPointer(ref error)) - }; - } -} diff --git a/src/TreeSitterSharp/QueryCursor.cs b/src/TreeSitterSharp/QueryCursor.cs deleted file mode 100644 index 7c11fd5..0000000 --- a/src/TreeSitterSharp/QueryCursor.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; -using TreeSitterSharp.Native; - -namespace TreeSitterSharp; - -public unsafe class QueryCursor -{ - private TsQueryCursor* _queryCursor; - - public QueryCursor() - { - _queryCursor = Ts.query_cursor_new(); - } - - public void Execute(Query query, SyntaxNode node) - { - Ts.query_cursor_exec(_queryCursor, (query as INativeObject).ToUnmanaged(), node.ToUnmanaged()); - } - - public bool NextMatch([MaybeNullWhen(false)] out QueryMatch queryMatch) - { - TsQueryMatch match; - bool succeed = Ts.query_cursor_next_match(_queryCursor, &match); - queryMatch = succeed ? new QueryMatch(&match) : null; - return succeed; - } - - public bool NextCapture([MaybeNullWhen(false)] out QueryMatch queryMatch, out uint captureIndex) - { - TsQueryMatch match; - uint index; - bool succeed = Ts.query_cursor_next_capture(_queryCursor, &match, &index); - queryMatch = succeed ? new QueryMatch(&match) : null; - captureIndex = index; - return succeed; - } -} diff --git a/src/TreeSitterSharp/QueryMatch.cs b/src/TreeSitterSharp/QueryMatch.cs deleted file mode 100644 index a2b3f61..0000000 --- a/src/TreeSitterSharp/QueryMatch.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using TreeSitterSharp.Native; - -namespace TreeSitterSharp; -public unsafe class QueryMatch : INativeObject -{ - private readonly TsQueryMatch* _queryMatch; - - public QueryMatch(TsQueryMatch* queryMatch) => _queryMatch = queryMatch; - - public ImmutableArray Captures => new Span(_queryMatch->Captures, _queryMatch->CaptureCount).ToImmutableArray(); - - public TsQueryMatch* ToUnmanaged() => _queryMatch; -} diff --git a/src/TreeSitterSharp/SyntaxNode.cs b/src/TreeSitterSharp/SyntaxNode.cs index d5f8b9e..f4c3dcd 100644 --- a/src/TreeSitterSharp/SyntaxNode.cs +++ b/src/TreeSitterSharp/SyntaxNode.cs @@ -2,23 +2,25 @@ using TreeSitterSharp.Native; namespace TreeSitterSharp; -public unsafe class SyntaxNode +public abstract unsafe class SyntaxNode : ISyntaxNode + where TSelf : SyntaxNode, ISyntaxNodeCreation + where TSyntaxTree : SyntaxTree, ISyntaxTreeCreation { protected readonly TsNode _node; protected internal SyntaxNode(TsNode node) { _node = node; - Tree = new SyntaxTree(_node.tree); + Tree = TSyntaxTree.Create(_node.tree); } - public SyntaxTree Tree { get; } + public TSyntaxTree Tree { get; } public nint Id => (nint)_node.id; - public string Type => Ts.node_type(_node); + public string NodeType => Ts.node_type(_node); - public ushort Symbol => Ts.node_symbol(_node); + public ushort NodeSymbol => Ts.node_symbol(_node); public Language Language => new(Ts.node_language(_node)); public virtual string GrammarType => Ts.node_grammar_type(_node); @@ -29,48 +31,48 @@ protected internal SyntaxNode(TsNode node) public Point EndPoint => Ts.node_end_point(_node); public uint ChildCount => Ts.node_child_count(_node); public uint NamedChildCount => Ts.node_named_child_count(_node); - public SyntaxNode? PreviousSibling + public TSelf? PreviousSibling { get { var node = Ts.node_prev_sibling(_node); - return Ts.node_is_null(node) ? null : new(node); + return Ts.node_is_null(node) ? null : TSelf.Create(node); } } - public SyntaxNode? NextSibling + public TSelf? NextSibling { get { var node = Ts.node_next_sibling(_node); - return Ts.node_is_null(node) ? null : new(node); + return Ts.node_is_null(node) ? null : TSelf.Create(node); } } - public SyntaxNode? PreviousNamedSibling + public TSelf? PreviousNamedSibling { get { var node = Ts.node_prev_named_sibling(_node); - return Ts.node_is_null(node) ? null : new(node); + return Ts.node_is_null(node) ? null : TSelf.Create(node); } } - public SyntaxNode? NextNamedSibling + public TSelf? NextNamedSibling { get { var node = Ts.node_next_named_sibling(_node); - return Ts.node_is_null(node) ? null : new(node); + return Ts.node_is_null(node) ? null : TSelf.Create(node); } } - public SyntaxNode? Parent + public TSelf? Parent { get { var node = Ts.node_parent(_node); - return Ts.node_is_null(node) ? null : new(node); + return Ts.node_is_null(node) ? null : TSelf.Create(node); } } @@ -79,29 +81,29 @@ public SyntaxNode? Parent public bool IsExtra => Ts.node_is_extra(_node); public bool IsNull => Ts.node_is_null(_node); - public ImmutableArray Children => GetChildren().ToImmutableArray(); - public ImmutableArray NamedChildren => GetNamedChildren().ToImmutableArray(); + public ImmutableArray Children => GetChildren().ToImmutableArray(); + public ImmutableArray NamedChildren => GetNamedChildren().ToImmutableArray(); - public SyntaxNode GetChildByFieldName(string fieldName) + public TSelf GetChildByFieldName(string fieldName) { - return new(Ts.node_child_by_field_name(_node, fieldName, (uint)fieldName.Length)); + return TSelf.Create(Ts.node_child_by_field_name(_node, fieldName, (uint)fieldName.Length)); } - public SyntaxNode GetChildByFieldId(ushort fieldId) => new(Ts.node_child_by_field_id(_node, fieldId)); + public TSelf GetChildByFieldId(ushort fieldId) => TSelf.Create(Ts.node_child_by_field_id(_node, fieldId)); public string GetFieldNameForChild(uint childIndex) => Ts.node_field_name_for_child(_node, childIndex); - public SyntaxNode GetNamedChild(uint index) + public TSelf GetNamedChild(uint index) { - return new(Ts.node_named_child(_node, index)); + return TSelf.Create(Ts.node_named_child(_node, index)); } - public SyntaxNode GetChild(uint index) + public TSelf GetChild(uint index) { - return new(Ts.node_child(_node, index)); + return TSelf.Create(Ts.node_child(_node, index)); } - private IEnumerable GetNamedChildren() + private IEnumerable GetNamedChildren() { for (uint i = 0; i < NamedChildCount; i++) { @@ -109,7 +111,7 @@ private IEnumerable GetNamedChildren() } } - private IEnumerable GetChildren() + private IEnumerable GetChildren() { for (uint i = 0; i < ChildCount; i++) { @@ -127,7 +129,7 @@ public string GetSExpression() return Ts.node_string(_node); } - protected bool Equals(SyntaxNode other) => Ts.node_eq(_node, other._node); + protected bool Equals(TSelf other) => Ts.node_eq(_node, other._node); public override bool Equals(object? obj) { @@ -146,7 +148,7 @@ public override bool Equals(object? obj) return false; } - return Equals((SyntaxNode)obj); + return Equals((TSelf)obj); } public override int GetHashCode() => _node.GetHashCode(); diff --git a/src/TreeSitterSharp/SyntaxTree.cs b/src/TreeSitterSharp/SyntaxTree.cs index cc4e456..f4b4f80 100644 --- a/src/TreeSitterSharp/SyntaxTree.cs +++ b/src/TreeSitterSharp/SyntaxTree.cs @@ -2,7 +2,9 @@ using TreeSitterSharp.Native; namespace TreeSitterSharp; -public unsafe class SyntaxTree +public abstract unsafe class SyntaxTree : ISyntaxTree + where TSelf : SyntaxTree + where TSyntaxNode : ISyntaxNode { protected readonly TsTree* _tree; @@ -16,9 +18,9 @@ protected internal SyntaxTree(TsTree* tree) Ts.tree_delete(_tree); } - public SyntaxTree Copy() => new(Ts.tree_copy(_tree)); + public abstract TSelf Copy(); public Language Language => new(Ts.tree_language(_tree)); - public virtual SyntaxNode Root => new(Ts.tree_root_node(_tree)); + public abstract TSyntaxNode Root { get; } } diff --git a/src/TreeSitterSharp/SyntaxTreeCursor.cs b/src/TreeSitterSharp/SyntaxTreeCursor.cs deleted file mode 100644 index d8d6b41..0000000 --- a/src/TreeSitterSharp/SyntaxTreeCursor.cs +++ /dev/null @@ -1,51 +0,0 @@ -using TreeSitterSharp.Native; - -namespace TreeSitterSharp; -public class SyntaxTreeCursor -{ - private readonly TsTreeCursor _treeCursor; - private readonly TsNode _node; - - public SyntaxTreeCursor(SyntaxNode node) - { - _node = node.ToUnmanaged(); - _treeCursor = Ts.tree_cursor_new(_node); - } - - private SyntaxTreeCursor(TsTreeCursor treeCursor) => _treeCursor = treeCursor; - - ~SyntaxTreeCursor() - { - Ts.tree_cursor_delete(_treeCursor); - } - - public uint CurrentDepth => Ts.tree_cursor_current_depth(in _treeCursor); - public SyntaxNode CurrentNode => new(Ts.tree_cursor_current_node(in _treeCursor)); - public string CurrentFieldName => Ts.tree_cursor_current_field_name(in _treeCursor); - public ushort CurrentFieldId => Ts.tree_cursor_current_field_id(in _treeCursor); - public uint CurrentDescendantIndex => Ts.tree_cursor_current_descendant_index(in _treeCursor); - - public bool GotoParent() => Ts.tree_cursor_goto_parent(in _treeCursor); - public bool GotoFirstChild() => Ts.tree_cursor_goto_first_child(in _treeCursor); - public long GotoFirstChildForByte(uint goalByte) => Ts.tree_cursor_goto_first_child_for_byte(in _treeCursor, goalByte); - public long GotoFirstChildForPoint(Point point) => Ts.tree_cursor_goto_first_child_for_point(in _treeCursor, point); - public bool GotoLastChild() => Ts.tree_cursor_goto_last_child(in _treeCursor); - public bool GotoNextSibling() => Ts.tree_cursor_goto_next_sibling(in _treeCursor); - public bool GotoPreviousSibling() => Ts.tree_cursor_goto_previous_sibling(in _treeCursor); - public void GotoDescendant(uint descendantIndex) => Ts.tree_cursor_goto_descendant(in _treeCursor, descendantIndex); - - public void Reset() - { - Ts.tree_cursor_reset(_treeCursor, _node); - } - - public void ResetTo(SyntaxTreeCursor dst) - { - Ts.tree_cursor_reset_to(dst._treeCursor, _treeCursor); - } - - public SyntaxTreeCursor Copy() - { - return new SyntaxTreeCursor(Ts.tree_cursor_copy(_treeCursor)); - } -} diff --git a/tests/TreeSitterSharp.C.Tests/CParserTests.cs b/tests/TreeSitterSharp.C.Tests/CParserTests.cs index 5ec68f4..8db2dae 100644 --- a/tests/TreeSitterSharp.C.Tests/CParserTests.cs +++ b/tests/TreeSitterSharp.C.Tests/CParserTests.cs @@ -29,32 +29,4 @@ int fibonacci(int n) { Assert.Equal(expected, tree.Root.GetSExpression()); } - - [Fact] - public void QueryTest1() - { - var parser = new CParser(); - string code = """ - #include - - int fibonacci(int n) { - if (n <= 0) { - return 0; - } else if (n == 1) { - return 1; - } else { - return fibonacci(n - 1) + fibonacci(n - 2); - } - } - """; - - var tree = parser.Parse(code); - - var cursor = new QueryCursor(); - - cursor.Execute(Query.Create(tree.Language, "(preproc_include)", out var errorOffset, out var error), tree.Root); - - bool result = cursor.NextMatch(out var match); - Assert.True(result); - } } diff --git a/tests/TreeSitterSharp.Json.Tests/JsonParserTests.cs b/tests/TreeSitterSharp.Json.Tests/JsonParserTests.cs index 747173f..1b5c632 100644 --- a/tests/TreeSitterSharp.Json.Tests/JsonParserTests.cs +++ b/tests/TreeSitterSharp.Json.Tests/JsonParserTests.cs @@ -8,13 +8,13 @@ public void BasicParsing() var parser = new JsonParser(); string code = "[1, null]"; var tree = parser.Parse(code); - SyntaxNode rootNode = tree.Root; - SyntaxNode arrayNode = rootNode.GetNamedChild(0); - SyntaxNode numberNode = arrayNode.GetNamedChild(0); + JsonSyntaxNode rootNode = tree.Root; + JsonSyntaxNode arrayNode = rootNode.GetNamedChild(0); + JsonSyntaxNode numberNode = arrayNode.GetNamedChild(0); - Assert.Equal("document", rootNode.Type); - Assert.Equal("array", arrayNode.Type); - Assert.Equal("number", numberNode.Type); + Assert.Equal("document", rootNode.NodeType); + Assert.Equal("array", arrayNode.NodeType); + Assert.Equal("number", numberNode.NodeType); Assert.True(rootNode.ChildCount == 1); Assert.True(arrayNode.ChildCount == 5); @@ -28,7 +28,7 @@ public void PrintTree() var parser = new JsonParser(); string code = "[1, null]"; var tree = parser.Parse(code); - SyntaxNode rootNode = tree.Root; + JsonSyntaxNode rootNode = tree.Root; string expected = """ (document (array (number) (null)))