changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / lisp/ffi/tree-sitter/api.lisp

changeset 698: 96958d3eb5b0
parent: 48e671eac752
author: Richard Westhaver <ellis@rwest.io>
date: Fri, 04 Oct 2024 22:04:59 -0400
permissions: -rw-r--r--
description: fixes
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
37 (defmacro with-ts-node ((var node) &body forms)
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 
45 (defmacro with-tree-cursor ((var tree) &body forms &aux (node (gensym)))
46  `(with-ts-node (,node (ts-tree-root-node-pointer ,tree))
47  (let ((,var (ts-tree-cursor-new-pointer ,node)))
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))
83  (tree (ts-parser-parse-string parser string string-to-pass string-length)))
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)
96  (with-tree-cursor (cursor tree)
97  ;; Closely follows tree-sitter-cli parse
98  ;; implementation with a modification to
99  ;; allow for production of the full CST.
100  (loop
101  (with-ts-node (node (ts-tree-cursor-current-node-pointer cursor))
102  (let ((is-named (or produce-cst (ts-node-is-named-pointer node))))
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
120  (let ((start-point (ts-node-start-point-pointer node))
121  (end-point (ts-node-end-point-pointer node))
122  (type (funcall name-generator (ts-node-type-pointer node)))
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))))
128  (push (make-node :type type :range (list start-point end-point))
129  parse-stack)))
130  (setf did-visit-children
131  (not (ts-tree-cursor-goto-first-child cursor))))))))))