-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathbindings.go
697 lines (591 loc) · 18 KB
/
bindings.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
package sitter
//#include "bindings.h"
import "C"
import (
"fmt"
"reflect"
"runtime"
"unsafe"
)
// Parse is a shortcut for parsing bytes of source code, returns root node.
// This should not be used outside quick prototypes or tests as it prevents
// closing the created Parser and Tree, it relies strictly on the Finalizer to
// free those resources.
func Parse(content []byte, lang *Language) *Node {
p := NewParser()
p.SetLanguage(lang)
return p.Parse(content).RootNode()
}
// Parser produces concrete syntax tree based on source code using Language
type Parser struct{ c *C.TSParser }
// NewParser creates new Parser
func NewParser() *Parser {
p := &Parser{C.ts_parser_new()}
runtime.SetFinalizer(p, deleteParser)
return p
}
// Close releases the resources used by the parser. Once called,
// the parser shouldn't be used anymore. All trees generated by
// this Parser should be closed before closing the parser.
func (p *Parser) Close() {
runtime.SetFinalizer(p, nil)
deleteParser(p)
}
// SetLanguage assignes Language to a parser
func (p *Parser) SetLanguage(lang *Language) {
cLang := (*C.struct_TSLanguage)(lang.ptr)
C.ts_parser_set_language(p.c, cLang)
}
// Parse produces new Tree from content
func (p *Parser) Parse(content []byte) *Tree {
return p.ParseWithTree(content, nil)
}
// ParseWithTree produces new Tree from content using old tree
func (p *Parser) ParseWithTree(content []byte, t *Tree) *Tree {
var cTree *C.TSTree
if t != nil {
cTree = t.c
}
input := C.CBytes(content)
cTree = C.ts_parser_parse_string(p.c, cTree, (*C.char)(input), C.uint32_t(len(content)))
C.free(input)
return p.newTree(cTree)
}
// OperationLimit returns the duration in microseconds that parsing is allowed to take
func (p *Parser) OperationLimit() int {
return int(C.ts_parser_timeout_micros(p.c))
}
// SetOperationLimit limits the maximum duration in microseconds that parsing should be allowed to take before halting
func (p *Parser) SetOperationLimit(limit int) {
C.ts_parser_set_timeout_micros(p.c, C.uint64_t(limit))
}
// Reset causes the parser to parse from scratch on the next call to parse, instead of resuming
// so that it sees the changes to the beginning of the source code.
func (p *Parser) Reset() {
C.ts_parser_reset(p.c)
}
// SetIncludedRanges sets text ranges of a file
func (p *Parser) SetIncludedRanges(ranges []Range) {
cRanges := make([]C.TSRange, len(ranges))
for i, r := range ranges {
cRanges[i] = C.TSRange{
start_point: C.TSPoint{
row: C.uint32_t(r.StartPoint.Row),
column: C.uint32_t(r.StartPoint.Column),
},
end_point: C.TSPoint{
row: C.uint32_t(r.EndPoint.Row),
column: C.uint32_t(r.EndPoint.Column),
},
start_byte: C.uint32_t(r.StartByte),
end_byte: C.uint32_t(r.EndByte),
}
}
C.ts_parser_set_included_ranges(p.c, (*C.TSRange)(unsafe.Pointer(&cRanges[0])), C.uint(len(ranges)))
}
// Debug enables debug output to stderr
func (p *Parser) Debug() {
logger := C.stderr_logger_new(true)
C.ts_parser_set_logger(p.c, logger)
}
func deleteParser(p *Parser) {
C.ts_parser_delete(p.c)
p.c = nil
}
type Point struct {
Row uint32
Column uint32
}
type Range struct {
StartPoint Point
EndPoint Point
StartByte uint32
EndByte uint32
}
// newTree creates a new tree object from a C pointer. The function will set a finalizer for the object,
// thus no free is needed for it.
func (p *Parser) newTree(c *C.TSTree) *Tree {
newTree := &Tree{p: p, c: c}
runtime.SetFinalizer(newTree, deleteTree)
return newTree
}
// Tree represents the syntax tree of an entire source code file
// Note: Tree instances are not thread safe;
// you must copy a tree if you want to use it on multiple threads simultaneously.
type Tree struct {
// p is a pointer to a Parser that produced the Tree. Only used to keep Parser alive.
// Otherwise Parser may be GC'ed (and deleted by the finalizer) while some Tree objects are still in use.
p *Parser
c *C.TSTree
}
// Close releases the resources used by the tree. Once called,
// the tree shouldn't be used anymore.
func (t *Tree) Close() {
runtime.SetFinalizer(t, nil)
deleteTree(t)
}
// Copy returns a new copy of a tree
func (t *Tree) Copy() *Tree {
return t.p.newTree(C.ts_tree_copy(t.c))
}
// RootNode returns root node of a tree
func (t *Tree) RootNode() *Node {
ptr := C.ts_tree_root_node(t.c)
return t.createNode(ptr)
}
func (t *Tree) createNode(ptr C.TSNode) *Node {
if ptr.id == nil {
return nil
}
n := &Node{c: ptr, t: t}
n.batchFill()
return n
}
func deleteTree(t *Tree) {
t.p = nil
C.ts_tree_delete(t.c)
t.c = nil
}
type EditInput struct {
StartIndex uint32
OldEndIndex uint32
NewEndIndex uint32
StartPoint Point
OldEndPoint Point
NewEndPoint Point
}
func (i EditInput) c() *C.TSInputEdit {
return &C.TSInputEdit{
start_byte: C.uint32_t(i.StartIndex),
old_end_byte: C.uint32_t(i.OldEndIndex),
new_end_byte: C.uint32_t(i.NewEndIndex),
start_point: C.TSPoint{
row: C.uint32_t(i.StartPoint.Row),
column: C.uint32_t(i.StartPoint.Column),
},
old_end_point: C.TSPoint{
row: C.uint32_t(i.OldEndPoint.Row),
column: C.uint32_t(i.OldEndPoint.Column),
},
new_end_point: C.TSPoint{
row: C.uint32_t(i.OldEndPoint.Row),
column: C.uint32_t(i.OldEndPoint.Column),
},
}
}
// Edit the syntax tree to keep it in sync with source code that has been edited.
func (t *Tree) Edit(i EditInput) {
C.ts_tree_edit(t.c, i.c())
}
// Language defines how to parse a particular programming language
type Language struct {
ptr unsafe.Pointer
}
// NewLanguage creates new Language from c pointer
func NewLanguage(ptr unsafe.Pointer) *Language {
return &Language{ptr}
}
// SymbolName returns a node type string for the given Symbol.
func (l *Language) SymbolName(s Symbol) string {
return C.GoString(C.ts_language_symbol_name((*C.TSLanguage)(l.ptr), s))
}
// SymbolType returns named, anonymous, or a hidden type for a Symbol.
func (l *Language) SymbolType(s Symbol) SymbolType {
return SymbolType(C.ts_language_symbol_type((*C.TSLanguage)(l.ptr), s))
}
// SymbolCount returns the number of distinct field names in the language.
func (l *Language) SymbolCount() uint32 {
return uint32(C.ts_language_symbol_count((*C.TSLanguage)(l.ptr)))
}
// Node represents a single node in the syntax tree
// It tracks its start and end positions in the source code,
// as well as its relation to other nodes like its parent, siblings and children.
type Node struct {
c C.TSNode
t *Tree // keep pointer on tree because node is valid only as long as tree is
batchLoaded bool // true if the following fields have been filled
startPt, endPt Point
startB, endB uint32
childCount uint32
sym Symbol
typ string
}
func (n *Node) batchFill() {
var (
sp, ep C.TSPoint
sb, eb, cc C.uint32_t
sym C.TSSymbol
)
typ := C.GoString(C.ts_node_batch_get_info(n.c, &sp, &ep, &sb, &eb, &cc, &sym))
n.batchLoaded = true
n.startPt = Point{Row: uint32(sp.row), Column: uint32(sp.column)}
n.endPt = Point{Row: uint32(ep.row), Column: uint32(ep.column)}
n.startB = uint32(sb)
n.endB = uint32(eb)
n.childCount = uint32(cc)
n.sym = sym
n.typ = typ
}
type Symbol = C.TSSymbol
type SymbolType int
const (
SymbolTypeRegular SymbolType = iota
SymbolTypeAnonymous
SymbolTypeAuxiliary
)
var symbolTypeNames = []string{
"Regular",
"Anonymous",
"Auxiliary",
}
func (t SymbolType) String() string {
return symbolTypeNames[t]
}
// StartByte returns the node's start byte.
func (n *Node) StartByte() uint32 {
if n.batchLoaded {
return n.startB
}
return uint32(C.ts_node_start_byte(n.c))
}
// EndByte returns the node's end byte.
func (n *Node) EndByte() uint32 {
if n.batchLoaded {
return n.endB
}
return uint32(C.ts_node_end_byte(n.c))
}
// StartPoint returns the node's start position in terms of rows and columns.
func (n *Node) StartPoint() Point {
if n.batchLoaded {
return n.startPt
}
p := C.ts_node_start_point(n.c)
return Point{
Row: uint32(p.row),
Column: uint32(p.column),
}
}
// EndPoint returns the node's end position in terms of rows and columns.
func (n *Node) EndPoint() Point {
if n.batchLoaded {
return n.endPt
}
p := C.ts_node_end_point(n.c)
return Point{
Row: uint32(p.row),
Column: uint32(p.column),
}
}
// Symbol returns the node's type as a Symbol.
func (n *Node) Symbol() Symbol {
if n.batchLoaded {
return n.sym
}
return C.ts_node_symbol(n.c)
}
// Type returns the node's type as a string.
func (n *Node) Type() string {
if n.batchLoaded {
return n.typ
}
return C.GoString(C.ts_node_type(n.c))
}
// String returns an S-expression representing the node as a string.
func (n *Node) String() string {
ptr := C.ts_node_string(n.c)
defer C.free(unsafe.Pointer(ptr))
return C.GoString(ptr)
}
// Equal checks if two nodes are identical.
func (n *Node) Equal(other *Node) bool {
return bool(C.ts_node_eq(n.c, other.c))
}
// IsNull checks if the node is null.
func (n *Node) IsNull() bool {
return bool(C.ts_node_is_null(n.c))
}
// IsNamed checks if the node is *named*.
// Named nodes correspond to named rules in the grammar,
// whereas *anonymous* nodes correspond to string literals in the grammar.
func (n *Node) IsNamed() bool {
return bool(C.ts_node_is_named(n.c))
}
// IsMissing checks if the node is *missing*.
// Missing nodes are inserted by the parser in order to recover from certain kinds of syntax errors.
func (n *Node) IsMissing() bool {
return bool(C.ts_node_is_missing(n.c))
}
// HasChanges checks if a syntax node has been edited.
func (n *Node) HasChanges() bool {
return bool(C.ts_node_has_changes(n.c))
}
// HasError check if the node is a syntax error or contains any syntax errors.
func (n *Node) HasError() bool {
return bool(C.ts_node_has_error(n.c))
}
// Parent returns the node's immediate parent.
func (n *Node) Parent() *Node {
nn := C.ts_node_parent(n.c)
return n.t.createNode(nn)
}
// Child returns the node's child at the given index, where zero represents the first child.
func (n *Node) Child(idx int) *Node {
nn := C.ts_node_child(n.c, C.uint32_t(idx))
return n.t.createNode(nn)
}
// NamedChild returns the node's *named* child at the given index.
func (n *Node) NamedChild(idx int) *Node {
nn := C.ts_node_named_child(n.c, C.uint32_t(idx))
return n.t.createNode(nn)
}
// ChildCount returns the node's number of children.
func (n *Node) ChildCount() uint32 {
if n.batchLoaded {
return n.childCount
}
return uint32(C.ts_node_child_count(n.c))
}
// NamedChildCount returns the node's number of *named* children.
func (n *Node) NamedChildCount() uint32 {
return uint32(C.ts_node_named_child_count(n.c))
}
// ChildByFieldName returns the node's child with the given field name.
func (n *Node) ChildByFieldName(name string) *Node {
nn := C.ts_node_child_by_field_name(n.c, C.CString(name), C.uint32_t(len(name)))
return n.t.createNode(nn)
}
// NextSibling returns the node's next sibling.
func (n *Node) NextSibling() *Node {
nn := C.ts_node_next_sibling(n.c)
return n.t.createNode(nn)
}
// NextNamedSibling returns the node's next *named* sibling.
func (n *Node) NextNamedSibling() *Node {
nn := C.ts_node_next_named_sibling(n.c)
return n.t.createNode(nn)
}
// PrevSibling returns the node's previous sibling.
func (n *Node) PrevSibling() *Node {
nn := C.ts_node_prev_sibling(n.c)
return n.t.createNode(nn)
}
// PrevNamedSibling returns the node's previous *named* sibling.
func (n *Node) PrevNamedSibling() *Node {
nn := C.ts_node_prev_named_sibling(n.c)
return n.t.createNode(nn)
}
// Edit the node to keep it in-sync with source code that has been edited.
func (n *Node) Edit(i EditInput) {
C.ts_node_edit(&n.c, i.c())
}
// Content returns node's source code from input as a string
func (n *Node) Content(input []byte) string {
return string(input[n.StartByte():n.EndByte()])
}
// TreeCursor allows you to walk a syntax tree more efficiently than is
// possible using the `Node` functions. It is a mutable object that is always
// on a certain syntax node, and can be moved imperatively to different nodes.
type TreeCursor struct {
c *C.TSTreeCursor
t *Tree
}
// NewTreeCursor creates a new tree cursor starting from the given node.
func NewTreeCursor(n *Node) *TreeCursor {
cc := C.ts_tree_cursor_new(n.c)
c := &TreeCursor{
c: &cc,
t: n.t,
}
runtime.SetFinalizer(c, deleteTreeCursor)
return c
}
// Close releases the resources used by the tree cursor. Once called,
// the cursor shouldn't be used anymore.
func (c *TreeCursor) Close() {
runtime.SetFinalizer(c, nil)
deleteTreeCursor(c)
}
func deleteTreeCursor(c *TreeCursor) {
c.t = nil
C.ts_tree_cursor_delete(c.c)
c.c = nil
}
// Reset re-initializes a tree cursor to start at a different node.
func (c *TreeCursor) Reset(n *Node) {
c.t = n.t
C.ts_tree_cursor_reset(c.c, n.c)
}
// CurrentNode of the tree cursor.
func (c *TreeCursor) CurrentNode() *Node {
n := C.ts_tree_cursor_current_node(c.c)
return c.t.createNode(n)
}
// CurrentFieldName gets the field name of the tree cursor's current node.
//
// This returns empty string if the current node doesn't have a field.
func (c *TreeCursor) CurrentFieldName() string {
return C.GoString(C.ts_tree_cursor_current_field_name(c.c))
}
// GoToParent moves the cursor to the parent of its current node.
//
// This returns `true` if the cursor successfully moved, and returns `false`
// if there was no parent node (the cursor was already on the root node).
func (c *TreeCursor) GoToParent() bool {
return bool(C.ts_tree_cursor_goto_parent(c.c))
}
// GoToNextSibling moves the cursor to the next sibling of its current node.
//
// This returns `true` if the cursor successfully moved, and returns `false`
// if there was no next sibling node.
func (c *TreeCursor) GoToNextSibling() bool {
return bool(C.ts_tree_cursor_goto_next_sibling(c.c))
}
// GoToFirstChild moves the cursor to the first child of its current node.
//
// This returns `true` if the cursor successfully moved, and returns `false`
// if there were no children.
func (c *TreeCursor) GoToFirstChild() bool {
return bool(C.ts_tree_cursor_goto_first_child(c.c))
}
// GoToFirstChildForByte moves the cursor to the first child of its current node
// that extends beyond the given byte offset.
//
// This returns the index of the child node if one was found, and returns -1
// if no such child was found.
func (c *TreeCursor) GoToFirstChildForByte(b uint32) int64 {
return int64(C.ts_tree_cursor_goto_first_child_for_byte(c.c, C.uint32_t(b)))
}
// QueryErrorType - value that indicates the type of QueryError.
type QueryErrorType int
const (
QueryErrorNone QueryErrorType = iota
QueryErrorSyntax
QueryErrorNodeType
QueryErrorField
QueryErrorCapture
)
// QueryError - if there is an error in the query,
// then the Offset argument will be set to the byte offset of the error,
// and the Type argument will be set to a value that indicates the type of error.
type QueryError struct {
Offset uint32
Type QueryErrorType
}
func (qe *QueryError) Error() string {
switch qe.Type {
case QueryErrorNone:
return ""
case QueryErrorSyntax:
return fmt.Sprintf("syntax error (offset: %d)", qe.Offset)
case QueryErrorNodeType:
return fmt.Sprintf("node type error (offset: %d)", qe.Offset)
case QueryErrorField:
return fmt.Sprintf("field error (offset: %d)", qe.Offset)
case QueryErrorCapture:
return fmt.Sprintf("capture error (offset: %d)", qe.Offset)
default:
return fmt.Sprintf("unknown error (offset: %d)", qe.Offset)
}
}
// Query API
type Query struct{ c *C.TSQuery }
// NewQuery creates a query by specifying a string containing one or more patterns.
// In case of error returns QueryError.
func NewQuery(pattern []byte, lang *Language) (*Query, error) {
var (
erroff C.uint32_t
errtype C.TSQueryError
)
input := C.CBytes(pattern)
c := C.ts_query_new(
(*C.struct_TSLanguage)(lang.ptr),
(*C.char)(input),
C.uint32_t(len(pattern)),
&erroff,
&errtype,
)
C.free(input)
if errtype != C.TSQueryError(QueryErrorNone) {
return nil, &QueryError{Offset: uint32(erroff), Type: QueryErrorType(errtype)}
}
q := &Query{c}
runtime.SetFinalizer(q, deleteQuery)
return q, nil
}
// Close releases the resources used by the query. Once called,
// the query shouldn't be used anymore.
func (q *Query) Close() {
runtime.SetFinalizer(q, nil)
deleteQuery(q)
}
func deleteQuery(q *Query) {
C.ts_query_delete(q.c)
q.c = nil
}
// QueryCursor carries the state needed for processing the queries.
type QueryCursor struct {
c *C.TSQueryCursor
t *Tree
}
// NewQueryCursor creates a query cursor.
func NewQueryCursor() *QueryCursor {
qc := &QueryCursor{c: C.ts_query_cursor_new(), t: nil}
runtime.SetFinalizer(qc, deleteQueryCursor)
return qc
}
// Close releases the resources used by the query cursor. Once called,
// the cursor shouldn't be used anymore.
func (qc *QueryCursor) Close() {
runtime.SetFinalizer(qc, nil)
deleteQueryCursor(qc)
}
func deleteQueryCursor(qc *QueryCursor) {
qc.t = nil
C.ts_query_cursor_delete(qc.c)
qc.c = nil
}
// Exec executes the query on a given syntax node.
func (qc *QueryCursor) Exec(q *Query, n *Node) {
qc.t = n.t
C.ts_query_cursor_exec(qc.c, q.c, n.c)
}
// QueryCapture is a captured node by a query with an index
type QueryCapture struct {
Index uint32
Node *Node
}
// QueryMatch - you can then iterate over the matches.
type QueryMatch struct {
ID uint32
PatternIndex uint16
Captures []QueryCapture
}
// NextMatch iterates over matches.
// This function will return (nil, false) when there are no more matches.
// Otherwise, it will populate the QueryMatch with data
// about which pattern matched and which nodes were captured.
func (qc *QueryCursor) NextMatch() (*QueryMatch, bool) {
var (
cqm C.TSQueryMatch
cqc []C.TSQueryCapture
)
if ok := C.ts_query_cursor_next_match(qc.c, &cqm); !bool(ok) {
return nil, false
}
qm := &QueryMatch{
ID: uint32(cqm.id),
PatternIndex: uint16(cqm.pattern_index),
}
count := int(cqm.capture_count)
slice := (*reflect.SliceHeader)((unsafe.Pointer(&cqc)))
slice.Cap = count
slice.Len = count
slice.Data = uintptr(unsafe.Pointer(cqm.captures))
for _, c := range cqc {
idx := uint32(c.index)
node := qc.t.createNode(c.node)
qm.Captures = append(qm.Captures, QueryCapture{idx, node})
}
return qm, true
}