140
|
1
|
;;; ffi/tree-sitter/api.lisp --- High-level API for Tree-sitter library |
|
2
|
|
|
3
|
;; High-level Tree-sitter API |
|
4
|
|
|
5
|
;;; Code: |
|
6
|
(in-package :tree-sitter) |
|
7
|
|
|
8
|
(defstruct (node (:type list)) |
|
9
|
type range children) |
|
10
|
|
|
11
|
(defun make-lisp-name (string) |
|
12
|
(intern (string-upcase (substitute #\- #\_ string)) |
|
13
|
(load-time-value (find-package "KEYWORD")))) |
|
14
|
|
|
15
|
(define-condition tree-sitter-error (error) |
|
16
|
()) |
|
17
|
|
|
18
|
(define-condition create-parser-error (tree-sitter-error) |
|
19
|
()) |
|
20
|
|
|
21
|
(define-condition set-language-error (tree-sitter-error) |
|
22
|
((language :initarg :language :reader tree-sitter-error-language))) |
|
23
|
|
|
24
|
(define-condition parse-string-error (tree-sitter-error) |
|
25
|
((string :initarg :string :reader tree-sitter-error-string) |
|
26
|
(string-start :initarg :string-start :reader tree-sitter-error-string-start) |
|
27
|
(string-end :initarg :string-end :reader tree-sitter-error-string-end) |
|
28
|
(language :initarg :language :reader tree-sitter-error-language))) |
|
29
|
|
|
30
|
(define-condition null-node-pointer (tree-sitter-error) |
|
31
|
()) |
|
32
|
|
|
33
|
(define-condition null-tree-cursor-pointer (tree-sitter-error) |
|
34
|
()) |
|
35
|
|
|
36
|
;; util |
141
|
37
|
(defmacro with-ts-node ((var node) &body forms) |
140
|
38
|
`(let ((,var ,node)) |
|
39
|
(when (sb-alien:null-alien ,var) |
|
40
|
(error 'null-node-pointer)) |
|
41
|
(unwind-protect |
|
42
|
(progn ,@forms) |
|
43
|
(sb-alien:free-alien ,var)))) |
|
44
|
|
141
|
45
|
(defmacro with-tree-cursor ((var tree) &body forms &aux (node (gensym))) |
|
46
|
`(with-ts-node (,node (ts-tree-root-node ,tree)) |
|
47
|
(let ((,var (ts-tree-cursor-new ,node))) |
140
|
48
|
(when (sb-alien:null-alien ,var) |
|
49
|
(error 'null-tree-cursor-pointer)) |
|
50
|
(unwind-protect |
|
51
|
(progn ,@forms) |
|
52
|
(ts-tree-cursor-delete ,var))))) |
|
53
|
|
|
54
|
(defun parse-string (language string &key (start 0) end produce-cst (name-generator #'make-lisp-name)) |
|
55
|
"Parse a STRING that represents LANGUAGE code using tree-sitter. START is |
|
56
|
where to start parsing STRING. END is where to stop parsing STRING. |
|
57
|
When PRODUCE-CST is set, the full concrete syntax tree will be produced as |
|
58
|
opposed to the abstract syntax tree. See 'Named vs Anonymous Nodes': |
|
59
|
http://tree-sitter.github.io/tree-sitter/using-parsers#named-vs-anonymous-nodes |
|
60
|
NAME-GENERATOR is a function which converts a string from tree-sitter into a |
|
61
|
desired name for use in lisp." |
|
62
|
(let ((parser (ts-parser-new))) |
|
63
|
(when (sb-alien:null-alien parser) |
|
64
|
(error 'cant-create-parser)) |
|
65
|
(unwind-protect (parse-string-with-language language string parser |
|
66
|
:start start |
|
67
|
:end end |
|
68
|
:produce-cst produce-cst |
|
69
|
:name-generator name-generator) |
|
70
|
(ts-parser-delete parser)))) |
|
71
|
|
|
72
|
(defun parse-string-with-language (language string parser |
|
73
|
&key (start 0) end produce-cst name-generator) |
|
74
|
(unless (ts-parser-set-language parser (language-module language)) |
|
75
|
(error 'cant-set-language :language language)) |
|
76
|
(let* ((string-start start) |
|
77
|
(string-end (or end (length string))) |
|
78
|
;; TODO: this might need to be +1 if it's actually a c-string for null |
|
79
|
(string-length (- string-end string-start)) |
|
80
|
(string-to-pass (if (plusp string-start) |
|
81
|
(subseq string string-start string-end) |
|
82
|
string)) |
231
|
83
|
(tree (ts-parser-parse-string parser string string-to-pass string-length))) |
140
|
84
|
(when (sb-alien:null-alien tree) |
|
85
|
(error 'cant-parse-string |
|
86
|
:string string |
|
87
|
:string-start start |
|
88
|
:string-end end |
|
89
|
:language language)) |
|
90
|
(unwind-protect (convert-foreign-tree-to-list tree :produce-cst produce-cst |
|
91
|
:name-generator name-generator) |
|
92
|
(ts-tree-delete tree)))) |
|
93
|
|
|
94
|
(defun convert-foreign-tree-to-list (tree &key produce-cst name-generator |
|
95
|
&aux did-visit-children parse-stack) |
141
|
96
|
(with-tree-cursor (cursor tree) |
140
|
97
|
;; Closely follows tree-sitter-cli parse |
|
98
|
;; implementation with a modification to |
|
99
|
;; allow for production of the full CST. |
|
100
|
(loop |
141
|
101
|
(with-ts-node (node (ts-tree-cursor-current-node cursor)) |
|
102
|
(let ((is-named (or produce-cst (ts-node-is-named node)))) |
140
|
103
|
(cond (did-visit-children |
|
104
|
(when (and is-named (second parse-stack)) |
|
105
|
(let ((item (pop parse-stack))) |
|
106
|
(setf (node-children item) |
|
107
|
(nreverse (node-children item))) |
|
108
|
(push item (node-children (first parse-stack))))) |
|
109
|
(cond ((ts-tree-cursor-goto-next-sibling cursor) |
|
110
|
(setf did-visit-children nil)) |
|
111
|
((ts-tree-cursor-goto-parent cursor) |
|
112
|
(setf did-visit-children t)) |
|
113
|
(t |
|
114
|
(let ((root (first parse-stack))) |
|
115
|
(setf (node-children root) |
|
116
|
(nreverse (node-children root))) |
|
117
|
(return root))))) |
|
118
|
(t |
|
119
|
(when is-named |
141
|
120
|
(let ((start-point (ts-node-start-point node)) |
|
121
|
(end-point (ts-node-end-point node)) |
|
122
|
(type (funcall name-generator (ts-node-type node))) |
140
|
123
|
(field-name-ptr (ts-tree-cursor-current-field-name cursor))) |
|
124
|
(unless (sb-alien:null-alien field-name-ptr) |
|
125
|
;; TODO |
|
126
|
(let ((field-name (deref field-name-ptr))) |
|
127
|
(setf type (list (funcall name-generator field-name) type)))) |
241
|
128
|
(push (make-node :type type :range (list start-point end-point)) |
140
|
129
|
parse-stack))) |
|
130
|
(setf did-visit-children |
|
131
|
(not (ts-tree-cursor-goto-first-child cursor)))))))))) |