emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/parseclj 2588470302 116/185: Merge pull request #9 from la


From: ELPA Syncer
Subject: [nongnu] elpa/parseclj 2588470302 116/185: Merge pull request #9 from lambdaisland/docstrings-and-conventions
Date: Tue, 28 Dec 2021 14:05:27 -0500 (EST)

branch: elpa/parseclj
commit 2588470302645f09c8f7e4e88cd953a248d58133
Merge: 168027fed5 e1cb9e5514
Author: Arne Brasseur <arne.brasseur@gmail.com>
Commit: GitHub <noreply@github.com>

    Merge pull request #9 from lambdaisland/docstrings-and-conventions
    
    Add docstrings and follow up on some conventions
---
 .dir-locals.el             |  10 +-
 parseclj-ast.el            | 112 ++++++++--
 parseclj-lex.el            | 193 ++++++++++++-----
 parseclj-parser.el         | 230 ++++++++++++++++++++
 parseclj-unparse.el        |  53 -----
 parseclj.el                | 297 ++------------------------
 parseedn.el                |  55 ++++-
 test/parseclj-lex-test.el  |  52 ++---
 test/parseclj-test-data.el | 517 +++++++++++++++++++++++----------------------
 test/parseclj-test.el      |  54 ++---
 10 files changed, 859 insertions(+), 714 deletions(-)

diff --git a/.dir-locals.el b/.dir-locals.el
index e793bf010b..ab85ded9a1 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -1,2 +1,8 @@
-((nil .
-  ((buffer-save-without-query . t))))
+((emacs-lisp-mode
+  (buffer-save-without-query . t)
+  (eval . (flycheck-mode))
+  (eval . (checkdoc-minor-mode))
+  (sentence-end-double-space . t)
+  (emacs-lisp-docstring-fill-column . 75)
+  (checkdoc-package-keywords-flag)
+  (checkdoc-arguments-in-order-flag)))
diff --git a/parseclj-ast.el b/parseclj-ast.el
index 7326471af1..26adeced61 100644
--- a/parseclj-ast.el
+++ b/parseclj-ast.el
@@ -27,41 +27,74 @@
 
 ;;; Code:
 
+(require 'subr-x)
+(require 'parseclj-lex)
+(require 'parseedn)
+
 ;; AST helper functions
 
 (defun parseclj-ast-node (type position &rest attributes)
   "Create an AST node with given TYPE and POSITION.
-
-Other ATTRIBUTES can be given as a flat list of key-value pairs. "
+Other ATTRIBUTES can be given as a flat list of key-value pairs."
   (apply 'a-list :node-type type :position position attributes))
 
-(defun parseclj-ast-node? (node)
-  "Return `t' if the given NODE is a Clojure AST node."
+(defun parseclj-ast-node-p (node)
+  "Return t if the given NODE is a Clojure AST node."
   (and (consp node)
        (consp (car node))
        (eq :node-type (caar node))))
 
+(defun parseclj-ast-node-attr (node attr)
+  "Return NODE's ATTR, or nil."
+  (a-get node attr))
+
 (defun parseclj-ast-node-type (node)
   "Return the type of the AST node NODE."
   (a-get node :node-type))
 
-(defun parseclj-ast-leaf-node? (node)
-  "Return `t' if the given ast NODE is a leaf node."
-  (member (parseclj-ast-node-type node) parseclj--leaf-tokens))
+(defun parseclj-ast-children (node)
+  "Return children for the AST NODE."
+  (a-get node :children))
+
+(defun parseclj-ast-value (node)
+  "Return the value of NODE as another AST node."
+  (a-get node :value))
+
+(defun parseclj-ast-leaf-node-p (node)
+  "Return t if the given ast NODE is a leaf node."
+  (member (parseclj-ast-node-type node) parseclj-lex--leaf-tokens))
 
+(defun parseclj-ast-branch-node-p (node)
+  "Return t if the given AST NODE is a branch node."
+  (not (parseclj-ast-leaf-node-p node)))
+
+
 ;; Parse/reduce strategy functions
 
-(defun parseclj-ast--reduce-leaf (stack token options)
+(defun parseclj-ast--reduce-leaf (stack token &optional _options)
+  "Put into the STACK an AST leaf node based on TOKEN.
+Ignores white spaces and comments.
+
+OPTIONS is an association list.  See `parseclj-parse' for more information
+on available options."
   (if (member (parseclj-lex-token-type token) '(:whitespace :comment))
       stack
     (cons
      (parseclj-ast-node (parseclj-lex-token-type token)
                         (a-get token :pos)
                         :form (a-get token :form)
-                        :value (parseclj--leaf-token-value token))
+                        :value (parseclj-lex--leaf-token-value token))
      stack)))
 
 (defun parseclj-ast--reduce-leaf-with-lexical-preservation (stack token 
options)
+  "Put into STACK an AST leaf node based on TOKEN.
+This function is very similar to `parseclj-ast--reduce-leaf', but unlike
+it, takes into account tokens representing white space or comments and
+saves them into the STACK.  Nodes produced by this function have a
+`:lexical-preservation' key set to t.
+
+OPTIONS is an association list.  See `parseclj-parse' for more information
+on available options."
   (let ((token-type (parseclj-lex-token-type token))
         (top (car stack)))
     (if (member token-type '(:whitespace :comment))
@@ -75,7 +108,15 @@ Other ATTRIBUTES can be given as a flat list of key-value 
pairs. "
                 stack))
       (parseclj-ast--reduce-leaf stack token options))))
 
-(defun parseclj-ast--reduce-branch (stack opening-token children options)
+(defun parseclj-ast--reduce-branch (stack opening-token children _options)
+  "Reduce STACK with an AST branch node representing a collection of elements.
+Ignores discard tokens.
+
+OPENING-TOKEN is a lex token representing an opening paren, bracket or
+brace.
+CHILDREN is the collection of nodes to be reduced into the AST branch node.
+OPTIONS is an association list.  See `parseclj-parse' for more information
+on available options."
   (let* ((pos (a-get opening-token :pos))
          (type (parseclj-lex-token-type opening-token))
          (type (cl-case type
@@ -86,26 +127,71 @@ Other ATTRIBUTES can be given as a flat list of key-value 
pairs. "
     (cl-case type
       (:root (cons (parseclj-ast-node :root pos :children children) stack))
       (:discard stack)
-      (:tag (list (parseclj-ast-node :tag
+      (:tag (cons (parseclj-ast-node :tag
                                      pos
                                      :tag (intern (substring (a-get 
opening-token :form) 1))
-                                     :children children)))
+                                     :children children)
+                  stack))
       (t (cons
           (parseclj-ast-node type pos :children children)
           stack)))))
 
 (defun parseclj-ast--reduce-branch-with-lexical-preservation (stack 
opening-token children options)
+  "Reduce STACK with an AST branch node representing a collection of elements.
+Similar to `parseclj-ast--reduce-branch', but reduces discard tokens as
+well.  Nodes produced by this function have a `:lexical-preservation'
+key set to t.
+
+OPENING-TOKEN is a lex token representing an opening paren, bracket or
+brace.
+CHILDREN is the collection of tokens to be reduced into the AST branch
+node.
+OPTIONS is an association list.  See `parseclj-parse' for more information
+on available options."
   (if (eq :discard (parseclj-lex-token-type opening-token))
       (cons (parseclj-ast-node :discard (a-get opening-token :pos) :children 
children) stack)
     (let* ((stack (funcall #'parseclj-ast--reduce-branch stack opening-token 
children options))
            (top (car stack)))
-      (if (parseclj-ast-node? top)
+      (if (parseclj-ast-node-p top)
           (cons (cl-list* (car top) ;; make sure :node-type remains the first 
element in the list
                           '(:lexical-preservation . t)
                           (cdr top))
                 (cdr stack))
         stack))))
 
+
+
+;; Unparse functions
+
+(declare-function parseclj-unparse-clojure "parseclj")
+
+(defun parseclj-ast--unparse-collection (node)
+  "Insert a string representation of the given AST branch NODE into buffer."
+  (let* ((token-type (parseclj-ast-node-type node))
+         (delimiters (cl-case token-type
+                       (:root (cons "" ""))
+                       (:list (cons "(" ")"))
+                       (:vector (cons "[" "]"))
+                       (:set (cons "#{" "}"))
+                       (:map (cons "{" "}")))))
+    (insert (car delimiters))
+    (let ((nodes (alist-get ':children node)))
+      (when-let (node (car nodes))
+        (parseclj-unparse-clojure node))
+      (seq-doseq (child (cdr nodes))
+        (when (not (a-get node :lexical-preservation))
+          (insert " "))
+        (parseclj-unparse-clojure child)))
+    (insert (cdr delimiters))))
+
+(defun parseclj-ast--unparse-tag (node)
+  "Insert a string representation of the given AST tag NODE into buffer."
+  (progn
+    (insert "#")
+    (insert (symbol-name (a-get node :tag)))
+    (insert " ")
+    (parseclj-unparse-clojure (car (a-get node :children)))))
+
 (provide 'parseclj-ast)
 
 ;;; parseclj-ast.el ends here
diff --git a/parseclj-lex.el b/parseclj-lex.el
index df59881f67..d835457903 100644
--- a/parseclj-lex.el
+++ b/parseclj-lex.el
@@ -25,7 +25,26 @@
 
 ;; A reader for EDN data files and parser for Clojure source files.
 
-;;; Code
+;;; Code:
+
+(defvar parseclj-lex--leaf-tokens '(:whitespace
+                                    :comment
+                                    :number
+                                    :nil
+                                    :true
+                                    :false
+                                    :symbol
+                                    :keyword
+                                    :string
+                                    :character)
+  "Types of tokens that represent leaf nodes in the AST.")
+
+(defvar parseclj-lex--closing-tokens '(:rparen
+                                       :rbracket
+                                       :rbrace)
+  "Types of tokens that mark the end of a non-atomic form.")
+
+;; Token interface
 
 (defun parseclj-lex-token (type form pos &rest attributes)
   "Create a lexer token with the specified attributes.
@@ -38,10 +57,10 @@ Tokens at a mimimum have these attributes
 Other ATTRIBUTES can be given as a flat list of key-value pairs."
   (apply 'a-list :token-type type :form form :pos pos attributes))
 
-(defun parseclj-lex-token? (token)
+(defun parseclj-lex-token-p (token)
   "Is the given TOKEN a parseclj-lex TOKEN.
 
-A token is an association list with :token-type as its first key. "
+A token is an association list with :token-type as its first key."
   (and (consp token)
        (consp (car token))
        (eq :token-type (caar token))))
@@ -51,15 +70,68 @@ A token is an association list with :token-type as its 
first key. "
   (and (consp token)
        (cdr (assq :token-type token))))
 
-(defun parseclj-lex-leaf-token? (token)
-  "Return `t' if the given ast TOKEN is a leaf node."
-  (member (parseclj-lex-token-type token) parseclj--leaf-tokens))
-
-(defun parseclj-lex-closing-token? (token)
-  "Return `t' if the given ast TOKEN is a closing toking."
-  (member (parseclj-lex-token-type token) parseclj--closing-tokens))
-
-(defun parseclj-lex-at-whitespace? ()
+(defun parseclj-lex-leaf-token-p (token)
+  "Return t if the given AST TOKEN is a leaf node."
+  (member (parseclj-lex-token-type token) parseclj-lex--leaf-tokens))
+
+(defun parseclj-lex-closing-token-p (token)
+  "Return t if the given ast TOKEN is a closing token."
+  (member (parseclj-lex-token-type token) parseclj-lex--closing-tokens))
+
+
+;; Elisp values from tokens
+
+(defun parseclj-lex--string-value (s)
+  ""
+  (replace-regexp-in-string
+   "\\\\o[0-8]\\{3\\}"
+   (lambda (x)
+     (make-string 1 (string-to-number (substring x 2) 8) ))
+   (replace-regexp-in-string
+    "\\\\u[0-9a-fA-F]\\{4\\}"
+    (lambda (x)
+      (make-string 1 (string-to-number (substring x 2) 16)))
+    (replace-regexp-in-string "\\\\[tbnrf'\"\\]"
+                              (lambda (x)
+                                (cl-case (elt x 1)
+                                  (?t "\t")
+                                  (?f "\f")
+                                  (?\" "\"")
+                                  (?r "\r")
+                                  (?n "\n")
+                                  (?\\ "\\\\")
+                                  (t (substring x 1))))
+                              (substring s 1 -1)))))
+
+(defun parseclj-lex--character-value (c)
+  "Parse a EDN character C into an Emacs Lisp character."
+  (let ((first-char (elt c 1)))
+    (cond
+     ((equal c "\\newline") ?\n)
+     ((equal c "\\return") ?\r)
+     ((equal c "\\space") ?\ )
+     ((equal c "\\tab") ?\t)
+     ((eq first-char ?u) (string-to-number (substring c 2) 16))
+     ((eq first-char ?o) (string-to-number (substring c 2) 8))
+     (t first-char))))
+
+(defun parseclj-lex--leaf-token-value (token)
+  "Parse the given leaf TOKEN to an Emacs Lisp value."
+  (cl-case (parseclj-lex-token-type token)
+    (:number (string-to-number (alist-get :form token)))
+    (:nil nil)
+    (:true t)
+    (:false nil)
+    (:symbol (intern (alist-get :form token)))
+    (:keyword (intern (alist-get :form token)))
+    (:string (parseclj-lex--string-value (alist-get :form token)))
+    (:character (parseclj-lex--character-value (alist-get :form token)))))
+
+
+;; Stream tokenization
+
+(defun parseclj-lex-at-whitespace-p ()
+  "Return t if char at point is white space."
   (let ((char (char-after (point))))
     (or (equal char ?\ )
         (equal char ?\t)
@@ -67,24 +139,28 @@ A token is an association list with :token-type as its 
first key. "
         (equal char ?\r)
         (equal char ?,))))
 
-(defun parseclj-lex-at-eof? ()
+(defun parseclj-lex-at-eof-p ()
+  "Return t if point is at the end of file."
   (eq (point) (point-max)))
 
 (defun parseclj-lex-whitespace ()
+  "Consume all consecutive white space as possible and return an :whitespace 
token."
   (let ((pos (point)))
-    (while (parseclj-lex-at-whitespace?)
+    (while (parseclj-lex-at-whitespace-p)
       (right-char))
     (parseclj-lex-token :whitespace
-                   (buffer-substring-no-properties pos (point))
-                   pos)))
+                        (buffer-substring-no-properties pos (point))
+                        pos)))
 
 (defun parseclj-lex-skip-digits ()
+  "Skip all consecutive digits after point."
   (while (and (char-after (point))
               (<= ?0 (char-after (point)))
               (<= (char-after (point)) ?9))
     (right-char)))
 
 (defun parseclj-lex-skip-number ()
+  "Skip a number at point."
   ;; [\+\-]?\d+\.\d+
   (when (member (char-after (point)) '(?+ ?-))
     (right-char))
@@ -97,6 +173,7 @@ A token is an association list with :token-type as its first 
key. "
   (parseclj-lex-skip-digits))
 
 (defun parseclj-lex-number ()
+  "Consume a number and return a `:number' token representing it."
   (let ((pos (point)))
     (parseclj-lex-skip-number)
 
@@ -117,51 +194,59 @@ A token is an association list with :token-type as its 
first key. "
           (progn
             (right-char)
             (parseclj-lex-token :lex-error
-                           (buffer-substring-no-properties pos (point))
-                           pos
-                           :error-type :invalid-number-format))
+                                (buffer-substring-no-properties pos (point))
+                                pos
+                                :error-type :invalid-number-format))
 
         (parseclj-lex-token :number
-                       (buffer-substring-no-properties pos (point))
-                       pos)))))
+                            (buffer-substring-no-properties pos (point))
+                            pos)))))
 
 
-(defun parseclj-lex-digit? (char)
+(defun parseclj-lex-digit-p (char)
+  "Return t if CHAR is a digit."
   (and char (<= ?0 char) (<= char ?9)))
 
-(defun parseclj-lex-at-number? ()
+(defun parseclj-lex-at-number-p ()
+  "Return t if point is at a number."
   (let ((char (char-after (point))))
-    (or (parseclj-lex-digit? char)
+    (or (parseclj-lex-digit-p char)
         (and (member char '(?- ?+ ?.))
-             (parseclj-lex-digit? (char-after (1+ (point))))))))
+             (parseclj-lex-digit-p (char-after (1+ (point))))))))
 
-(defun parseclj-lex-symbol-start? (char &optional alpha-only)
-  "Symbols begin with a non-numeric character and can contain
-alphanumeric characters and . * + ! - _ ? $ % & = < >. If -, + or
-. are the first character, the second character (if any) must be
-non-numeric.
+(defun parseclj-lex-symbol-start-p (char &optional alpha-only)
+  "Return t if CHAR is a valid start for a symbol.
 
-In some cases, like in tagged elements, symbols are required to
-start with alphabetic characters only. ALPHA-ONLY ensures this
-behavior."
+Symbols begin with a non-numeric character and can contain alphanumeric
+characters and . * + ! - _ ? $ % & = < >.  If - + or . are the first
+character, the second character (if any) must be non-numeric.
+
+In some cases, like in tagged elements, symbols are required to start with
+alphabetic characters only.  ALPHA-ONLY ensures this behavior."
   (not (not (and char
                  (or (and (<= ?a char) (<= char ?z))
                      (and (<= ?A char) (<= char ?Z))
                      (and (not alpha-only) (member char '(?. ?* ?+ ?! ?- ?_ ?? 
?$ ?% ?& ?= ?< ?> ?/))))))))
 
-(defun parseclj-lex-symbol-rest? (char)
-  (or (parseclj-lex-symbol-start? char)
-      (parseclj-lex-digit? char)
+(defun parseclj-lex-symbol-rest-p (char)
+  "Return t if CHAR is a valid character in a symbol.
+For more information on what determines a valid symbol, see
+`parseclj-lex-symbol-start-p'"
+  (or (parseclj-lex-symbol-start-p char)
+      (parseclj-lex-digit-p char)
       (eq ?: char)
       (eq ?# char)))
 
 (defun parseclj-lex-get-symbol-at-point (pos)
-  "Return the symbol at point."
-  (while (parseclj-lex-symbol-rest? (char-after (point)))
+  "Return the symbol at POS as a string."
+  (while (parseclj-lex-symbol-rest-p (char-after (point)))
     (right-char))
   (buffer-substring-no-properties pos (point)))
 
 (defun parseclj-lex-symbol ()
+  "Return a lex token representing a symbol.
+Because of their special meaning, symbols \"nil\", \"true\", and \"false\"
+are returned as their own lex tokens."
   (let ((pos (point)))
     (right-char)
     (let ((sym (parseclj-lex-get-symbol-at-point pos)))
@@ -172,9 +257,12 @@ behavior."
        (t (parseclj-lex-token :symbol sym pos))))))
 
 (defun parseclj-lex-string ()
+  "Return a lex token representing a string.
+If EOF is reached without finding a closing double quote, a :lex-error
+token is returned."
   (let ((pos (point)))
     (right-char)
-    (while (not (or (equal (char-after (point)) ?\") (parseclj-lex-at-eof?)))
+    (while (not (or (equal (char-after (point)) ?\") (parseclj-lex-at-eof-p)))
       (if (equal (char-after (point)) ?\\)
           (right-char 2)
         (right-char)))
@@ -185,9 +273,11 @@ behavior."
       (parseclj-lex-token :lex-error (buffer-substring-no-properties pos 
(point)) pos))))
 
 (defun parseclj-lex-lookahead (n)
+  "Return a lookahead string of N characters after point."
   (buffer-substring-no-properties (point) (min (+ (point) n) (point-max))))
 
 (defun parseclj-lex-character ()
+  "Return a lex token representing a character."
   (let ((pos (point)))
     (right-char)
     (cond
@@ -220,6 +310,11 @@ behavior."
       (parseclj-lex-token :character (buffer-substring-no-properties pos 
(point)) pos)))))
 
 (defun parseclj-lex-keyword ()
+  "Return a lex token representing a keyword.
+Keywords follow the same rules as symbols, except they start with one or
+two colon characters.
+
+See `parseclj-lex-symbol', `parseclj-lex-symbol-start-p'."
   (let ((pos (point)))
     (right-char)
     (when (equal (char-after (point)) ?:) ;; same-namespace keyword
@@ -229,12 +324,13 @@ behavior."
           (right-char)
           (parseclj-lex-token :lex-error (buffer-substring-no-properties pos 
(point)) pos :error-type :invalid-keyword))
       (progn
-        (while (or (parseclj-lex-symbol-rest? (char-after (point)))
+        (while (or (parseclj-lex-symbol-rest-p (char-after (point)))
                    (equal (char-after (point)) ?#))
           (right-char))
         (parseclj-lex-token :keyword (buffer-substring-no-properties pos 
(point)) pos)))))
 
 (defun parseclj-lex-comment ()
+  "Return a lex token representing a comment."
   (let ((pos (point)))
     (goto-char (line-end-position))
     (when (equal (char-after (point)) ?\n)
@@ -242,12 +338,15 @@ behavior."
     (parseclj-lex-token :comment (buffer-substring-no-properties pos (point)) 
pos)))
 
 (defun parseclj-lex-next ()
-  (if (parseclj-lex-at-eof?)
+  "Consume characters at point and return the next lexical token.
+
+See `parseclj-lex-token'."
+  (if (parseclj-lex-at-eof-p)
       (parseclj-lex-token :eof nil (point))
     (let ((char (char-after (point)))
           (pos  (point)))
       (cond
-       ((parseclj-lex-at-whitespace?)
+       ((parseclj-lex-at-whitespace-p)
         (parseclj-lex-whitespace))
 
        ((equal char ?\()
@@ -274,10 +373,10 @@ behavior."
         (right-char)
         (parseclj-lex-token :rbrace "}" pos))
 
-       ((parseclj-lex-at-number?)
+       ((parseclj-lex-at-number-p)
         (parseclj-lex-number))
 
-       ((parseclj-lex-symbol-start? char)
+       ((parseclj-lex-symbol-start-p char)
         (parseclj-lex-symbol))
 
        ((equal char ?\")
@@ -302,12 +401,12 @@ behavior."
            ((equal char ?_)
             (right-char)
             (parseclj-lex-token :discard "#_" pos))
-           ((parseclj-lex-symbol-start? char t)
+           ((parseclj-lex-symbol-start-p char t)
             (right-char)
             (parseclj-lex-token :tag (concat "#" 
(parseclj-lex-get-symbol-at-point (1+ pos))) pos))
            (t
-            (while (not (or (parseclj-lex-at-whitespace?)
-                            (parseclj-lex-at-eof?)))
+            (while (not (or (parseclj-lex-at-whitespace-p)
+                            (parseclj-lex-at-eof-p)))
               (right-char))
             (parseclj-lex-token :lex-error (buffer-substring-no-properties pos 
(point)) pos :error-type :invalid-hashtag-dispatcher)))))
 
diff --git a/parseclj-parser.el b/parseclj-parser.el
new file mode 100644
index 0000000000..1e07d2d0a7
--- /dev/null
+++ b/parseclj-parser.el
@@ -0,0 +1,230 @@
+;;; parseclj-parser.el --- Clojure/EDN parser              -*- 
lexical-binding: t; -*-
+
+;; Copyright (C) 2017  Arne Brasseur
+
+;; Author: Arne Brasseur <arne@arnebrasseur.net>
+;; Keywords: lisp
+;; Package-Requires: ((emacs "25") (a "0.1.0alpha4"))
+;; Version: 0.1.0
+
+;; This file is not part of GNU Emacs.
+
+;; This file is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; This file is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to
+;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+
+;; A shift/reduce parser for Clojure source.
+
+;;; Code:
+
+(require 'cl-lib)
+(require 'subr-x)
+(require 'a)
+(require 'parseclj-lex)
+
+(define-error 'parseclj-parser-error "parseclj: Syntax error")
+
+(defun parseclj--error (format &rest args)
+  "Signal a parse error.
+Takes a FORMAT string and optional ARGS to be passed to
+`format-message'.  Signals a 'parseclj-parser-error signal, which
+can be handled with `condition-case'."
+  (signal 'parseclj-parser-error (list (apply #'format-message format args))))
+
+(defun parseclj--find-opening-token (stack closing-token)
+  "Scan STACK for an opening-token matching CLOSING-TOKEN."
+  (cl-case (parseclj-lex-token-type closing-token)
+    (:rparen :lparen)
+    (:rbracket :lbracket)
+    (:rbrace (parseclj-lex-token-type
+              (seq-find (lambda (token)
+                          (member (parseclj-lex-token-type token)
+                                  '(:lbrace :set)))
+                        stack)))))
+
+(defun parseclj--reduce-coll (stack closing-token reduce-branch options)
+  "Reduce collection based on the top of the STACK and a CLOSING-TOKEN.
+
+REDUCE-BRANCH is a function to be applied to the collection of tokens found
+from the top of the stack until an opening token that matches
+CLOSING-TOKEN.  This function should return an AST token representing such
+collection.
+
+OPTIONS is an association list.  This list is also passed down to the
+REDUCE-BRANCH function.  See `parseclj-parser' for more information on
+available options."
+  (let ((opening-token-type (parseclj--find-opening-token stack closing-token))
+        (fail-fast (a-get options :fail-fast t))
+        (collection nil))
+
+    ;; unwind the stack until opening-token-type is found, adding to collection
+    (while (and stack (not (eq (parseclj-lex-token-type (car stack)) 
opening-token-type)))
+      (push (pop stack) collection))
+
+    ;; did we find the right token?
+    (if (eq (parseclj-lex-token-type (car stack)) opening-token-type)
+        (progn
+          (when fail-fast
+            ;; any unreduced tokens left: bail early
+            (when-let ((token (seq-find #'parseclj-lex-token-p collection)))
+              (parseclj--error "At position %s, unmatched %S"
+                               (a-get token :pos)
+                               (parseclj-lex-token-type token))))
+
+          ;; all good, call the reducer so it can return an updated stack with 
a
+          ;; new node at the top.
+          (let ((opening-token (pop stack)))
+            (funcall reduce-branch stack opening-token collection options)))
+
+      ;; Unwound the stack without finding a matching paren: either bail early
+      ;; or return the original stack and continue parsing
+      (if fail-fast
+          (parseclj--error "At position %s, unmatched %S"
+                           (a-get closing-token :pos)
+                           (parseclj-lex-token-type closing-token))
+
+        (reverse collection)))))
+
+(defun parseclj--take-value (stack value-p)
+  "Scan STACK until a value is found.
+Return everything up to the value in reversed order (meaning the value
+comes first in the result).
+
+STACK is the current parse stack to scan.
+
+VALUE-P a predicate to distinguish reduced values from non-values (tokens
+and whitespace)."
+  (let ((result nil))
+    (cl-block nil
+      (while stack
+        (cond
+         ((parseclj-lex-token-p (car stack))
+          (cl-return nil))
+
+         ((funcall value-p (car stack))
+          (cl-return (cons (car stack) result)))
+
+         (t
+          (push (pop stack) result)))))))
+
+(defun parseclj--take-token (stack value-p token-types)
+  "Scan STACK until a token of a certain type is found.
+Returns nil if a value is encountered before a matching token is found.
+Return everything up to the token in reversed order (meaning the token
+comes first in the result).
+
+STACK is the current parse stack to scan.
+
+VALUE-P a predicate to distinguish reduced values from non-values (tokens
+and whitespace).
+
+TOKEN-TYPES are the token types to look for."
+  (let ((result nil))
+    (cl-block nil
+      (while stack
+        (cond
+         ((member (parseclj-lex-token-type (car stack)) token-types)
+          (cl-return (cons (car stack) result)))
+         ((funcall value-p (car stack))
+          (cl-return nil))
+         ((parseclj-lex-token-p (car stack))
+          (cl-return nil))
+         (t
+          (push (pop stack) result)))))))
+
+(defun parseclj-parser (reduce-leaf reduce-branch &optional options)
+  "Clojure/EDN stack-based shift-reduce parser.
+
+REDUCE-LEAF does reductions for leaf nodes.  It is a function that takes
+the current value of the stack and a token, and either returns an updated
+stack, with a new leaf node at the top (front), or returns the stack
+unmodified.
+
+REDUCE-BRANCH does reductions for branch nodes.  It is a function that
+takes the current value of the stack, the type of branch node to create,
+and a list of child nodes, and returns an updated stack, with the new node
+at the top (front).
+
+What \"node\" means in this case is up to the reducing functions, it could
+be AST nodes (as in the case of `parseclj-parser-clojure'), or plain
+values/sexps (as in the case of `parseedn-read'), or something else. The
+only requirement is that they should not put raw tokens back on the stack,
+as the parser relies on the presence or absence of these to detect parse
+errors.
+
+OPTIONS is an association list which is passed on to the reducing
+functions. Additionally the following options are recognized
+
+- `:fail-fast'
+  Raise an error when a parse error is encountered, rather than continuing
+  with a partial result.
+- `:value-p'
+  A predicate function to differentiate values from tokens and
+  whitespace. This is needed when scanning the stack to see if any
+  reductions can be performed. By default anything that isn't a token is
+  considered a value. This can be problematic when parsing with
+  `:lexical-preservation', and which case you should provide an
+  implementation that also returns falsy for :whitespace, :comment, and
+  :discard AST nodes.
+- `:tag-readers'
+  An association list that describes tag handler functions for any possible
+  tag.  This options in only available in `parseedn-read', for more
+  information, please refer to its documentation."
+  (let ((fail-fast (a-get options :fail-fast t))
+        (value-p (a-get options :value-p (lambda (e) (not 
(parseclj-lex-token-p e)))))
+        (stack nil)
+        (token (parseclj-lex-next)))
+
+    (while (not (eq (parseclj-lex-token-type token) :eof))
+      ;; (message "STACK: %S" stack)
+      ;; (message "TOKEN: %S\n" token)
+
+      ;; Reduce based on the top item on the stack (collections)
+      (cond
+       ((parseclj-lex-leaf-token-p token)
+        (setf stack (funcall reduce-leaf stack token options)))
+
+       ((parseclj-lex-closing-token-p token)
+        (setf stack (parseclj--reduce-coll stack token reduce-branch options)))
+
+       (t (push token stack)))
+
+      ;; Reduce based on top two items on the stack (special prefixed elements)
+      (let* ((top-value (parseclj--take-value stack value-p))
+             (opening-token (parseclj--take-token (nthcdr (length top-value) 
stack) value-p '(:discard :tag)))
+             (new-stack (nthcdr (+ (length top-value) (length opening-token)) 
stack)))
+        (when (and top-value opening-token)
+          ;; (message "Reducing...")
+          ;; (message "  - STACK %S" stack)
+          ;; (message "  - OPENING_TOKEN %S" opening-token)
+          ;; (message "  - TOP_VALUE %S\n" top-value)
+          (setq stack (funcall reduce-branch new-stack (car opening-token) 
(append (cdr opening-token) top-value) options))))
+
+      (setq token (parseclj-lex-next)))
+
+    ;; reduce root
+    (when fail-fast
+      (when-let ((token (seq-find #'parseclj-lex-token-p stack)))
+        (parseclj--error "At position %s, unmatched %S"
+                         (a-get token :pos)
+                         (parseclj-lex-token-type token))))
+
+    (car (funcall reduce-branch nil (parseclj-lex-token :root "" 1)
+                  (reverse stack)
+                  options))))
+
+(provide 'parseclj-parser)
+;;; parseclj-parser.el ends here
diff --git a/parseclj-unparse.el b/parseclj-unparse.el
deleted file mode 100644
index 8f2495c2b6..0000000000
--- a/parseclj-unparse.el
+++ /dev/null
@@ -1,53 +0,0 @@
-;;; parseclj-unparser.el --- Clojure unparser   -*- lexical-binding: t; -*-
-
-;; Copyright (C) 2017  Arne Brasseur
-
-;; Author: Arne Brasseur <arne@arnebrasseur.net>
-
-;; This file is not part of GNU Emacs.
-
-;; This file is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
-
-;; This file is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to
-;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
-
-;;; Commentary:
-
-;; Unparse an AST to Clojure code
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Unparser helpers
-
-(defun parseclj-unparse--collection (node ld rd)
-  (insert ld)
-  (let ((nodes (alist-get ':children node)))
-    (when-let (node (car nodes))
-      (parseclj-unparse-clojure node))
-    (seq-doseq (child (cdr nodes))
-      (when (not (a-get node :lexical-preservation))
-        (insert " "))
-      (parseclj-unparse-clojure child)))
-  (insert rd))
-
-(defun parseclj-unparse--tag (node)
-  (progn
-    (insert "#")
-    (insert (symbol-name (a-get node :tag)))
-    (insert " ")
-    (parseclj-unparse-clojure (car (a-get node :children)))))
-
-(provide 'parseclj-unparse)
-
-;;; parseclj-unparse.el ends here
diff --git a/parseclj.el b/parseclj.el
index c9e6abc489..515a5bbe80 100644
--- a/parseclj.el
+++ b/parseclj.el
@@ -26,302 +26,43 @@
 
 ;;; Commentary:
 
-;; A reader for EDN data files and parser for Clojure source files.
+;; Top level API for the Clojure parser.
 
 ;;; Code:
 
-(require 'cl-lib)
-(require 'a)
-
-(require 'parseclj-lex)
-(require 'parseedn)
+(require 'parseclj-parser)
 (require 'parseclj-ast)
-(require 'parseclj-unparse)
-
-(defvar parseclj--leaf-tokens '(:whitespace
-                                :comment
-                                :number
-                                :nil
-                                :true
-                                :false
-                                :symbol
-                                :keyword
-                                :string
-                                :character)
-  "Types of tokens that represent leaf nodes in the AST.")
-
-(defvar parseclj--closing-tokens '(:rparen
-                                   :rbracket
-                                   :rbrace)
-  "Types of tokens that mark the end of a non-atomic form.")
-
-;; The EDN spec is not clear about wether \u0123 and \o012 are supported in
-;; strings. They are described as character literals, but not as string escape
-;; codes. In practice all implementations support them (mostly with broken
-;; surrogate pair support), so we do the same. Sorry, emoji 🙁.
-;;
-;; Note that this is kind of broken, we don't correctly detect if \u or \o 
forms
-;; don't have the right forms.
-(defun parseclj--string (s)
-  (replace-regexp-in-string
-   "\\\\o[0-8]\\{3\\}"
-   (lambda (x)
-     (make-string 1 (string-to-number (substring x 2) 8) ))
-   (replace-regexp-in-string
-    "\\\\u[0-9a-fA-F]\\{4\\}"
-    (lambda (x)
-      (make-string 1 (string-to-number (substring x 2) 16)))
-    (replace-regexp-in-string "\\\\[tbnrf'\"\\]"
-                              (lambda (x)
-                                (cl-case (elt x 1)
-                                  (?t "\t")
-                                  (?f "\f")
-                                  (?\" "\"")
-                                  (?r "\r")
-                                  (?n "\n")
-                                  (?\\ "\\\\")
-                                  (t (substring x 1))))
-                              (substring s 1 -1)))))
-
-(defun parseclj--character (c)
-  (let ((first-char (elt c 1)))
-    (cond
-     ((equal c "\\newline") ?\n)
-     ((equal c "\\return") ?\r)
-     ((equal c "\\space") ?\ )
-     ((equal c "\\tab") ?\t)
-     ((eq first-char ?u) (string-to-number (substring c 2) 16))
-     ((eq first-char ?o) (string-to-number (substring c 2) 8))
-     (t first-char))))
-
-(defun parseclj--leaf-token-value (token)
-  (cl-case (parseclj-lex-token-type token)
-    (:number (string-to-number (alist-get :form token)))
-    (:nil nil)
-    (:true t)
-    (:false nil)
-    (:symbol (intern (alist-get :form token)))
-    (:keyword (intern (alist-get :form token)))
-    (:string (parseclj--string (alist-get :form token)))
-    (:character (parseclj--character (alist-get :form token)))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; Shift-Reduce Parser
-
-(define-error 'parseclj-parse-error "parseclj: Syntax error")
-
-(defun parseclj--error (format &rest args)
-  "Signal a parse error.
-Takes a FORMAT string and optional ARGS to be passed to
-`format-message'. Signals a 'parseclj-parse-error signal, which
-can be handled with `condition-case'."
-  (signal 'parseclj-parse-error (list (apply #'format-message format args))))
-
-(defun parseclj--find-opening-token (stack closing-token)
-  (cl-case (parseclj-lex-token-type closing-token)
-    (:rparen :lparen)
-    (:rbracket :lbracket)
-    (:rbrace (parseclj-lex-token-type
-              (seq-find (lambda (token)
-                          (member (parseclj-lex-token-type token)
-                                  '(:lbrace :set)))
-                        stack)))))
-
-(defun parseclj--reduce-coll (stack closing-token reduce-branch options)
-  "Reduce collection based on the top of the stack"
-  (let ((opening-token-type (parseclj--find-opening-token stack closing-token))
-        (fail-fast (a-get options :fail-fast t))
-        (collection nil))
-
-    ;; unwind the stack until opening-token-type is found, adding to collection
-    (while (and stack (not (eq (parseclj-lex-token-type (car stack)) 
opening-token-type)))
-      (push (pop stack) collection))
-
-    ;; did we find the right token?
-    (if (eq (parseclj-lex-token-type (car stack)) opening-token-type)
-        (progn
-          (when fail-fast
-            ;; any unreduced tokens left: bail early
-            (when-let ((token (seq-find #'parseclj-lex-token? collection)))
-              (parseclj--error "parseclj: Syntax Error at position %s, 
unmatched %S"
-                               (a-get token :pos)
-                               (parseclj-lex-token-type token))))
-
-          ;; all good, call the reducer so it can return an updated stack with 
a
-          ;; new node at the top.
-          (let ((opening-token (pop stack)))
-            (funcall reduce-branch stack opening-token collection options)))
-
-      ;; Unwound the stack without finding a matching paren: either bail early
-      ;; or return the original stack and continue parsing
-      (if fail-fast
-          (parseclj--error "parseclj: Syntax Error at position %s, unmatched 
%S"
-                           (a-get closing-token :pos)
-                           (parseclj-lex-token-type closing-token))
-
-        (reverse collection)))))
-
-(defun parseclj--take-value (stack value-p)
-  "Scan until a value is found.
-Return everything up to the value in reversed order (meaning the
-value comes first in the result).
-
-STACK is the current parse stack to scan.
-
-VALUE-P a predicate to distinguish reduced values from
-non-values (tokens and whitespace)."
-  (let ((result nil))
-    (cl-block nil
-      (while stack
-        (cond
-         ((parseclj-lex-token? (car stack))
-          (cl-return nil))
-
-         ((funcall value-p (car stack))
-          (cl-return (cons (car stack) result)))
-
-         (t
-          (push (pop stack) result)))))))
-
-(defun parseclj--take-token (stack value-p token-types)
-  "Scan until a token of a certain type is found.
-Returns nil if a value is encountered before a matching token is
-found. Return everything up to the token in reversed
-order (meaning the token comes first in the result).
-
-STACK is the current parse stack to scan.
-
-VALUE-P a predicate to distinguish reduced values from
-non-values (tokens and whitespace).
-
-TOKEN-TYPES are the token types to look for."
-  (let ((result nil))
-    (cl-block nil
-      (while stack
-        (cond
-         ((member (parseclj-lex-token-type (car stack)) token-types)
-          (cl-return (cons (car stack) result)))
-
-         ((funcall value-p (car stack))
-          (cl-return nil))
-
-         ((parseclj-lex-token? (car stack))
-          (cl-return nil))
-
-         (t
-          (push (pop stack) result)))))))
-
-(defun parseclj-parse (reduce-leaf reduce-branch &optional options)
-  "Clojure/EDN stack-based shift-reduce parser.
-
-REDUCE-LEAF does reductions for leaf nodes. It is a function that
-takes the current value of the stack and a token, and either
-returns an updated stack, with a new leaf node at the
-top (front), or returns the stack unmodified.
-
-REDUCE-BRANCH does reductions for branch nodes. It is a function
-that takes the current value of the stack, the type of branch
-node to create, and a list of child nodes, and returns an updated
-stack, with the new node at the top (front).
-
-What \"node\" means in this case is up to the reducing functions,
-it could be AST nodes (as in the case of
-`parseclj-parse-clojure'), or plain values/sexps (as in the case
-of `parseedn-read'), or something else. The only requirement is
-that they should not put raw tokens back on the stack, as the
-parser relies on the presence or absence of these to detect parse
-errors.
-
-OPTIONS is an association list which is passed on to the reducing
-functions. Additionally the following options are recognized
-
-- :fail-fast
-  Raise an error when a parse error is encountered, rather than
-  continuing with a partial result.
-- :value-p
-  A predicate function to differentiate values from tokens and
-  whitespace. This is needed when scanning the stack to see if
-  any reductions can be performed. By default anything that isn't
-  a token is considered a value. This can be problematic when
-  parsing with `:lexical-preservation', and which case you should
-  provide an implementation that also returns falsy for
-  :whitespace, :comment, and :discard AST nodes. "
-  (let ((fail-fast (a-get options :fail-fast t))
-        (value-p (a-get options :value-p (lambda (e) (not (parseclj-lex-token? 
e)))))
-        (stack nil)
-        (token (parseclj-lex-next)))
-
-    (while (not (eq (parseclj-lex-token-type token) :eof))
-      ;; (message "STACK: %S" stack)
-      ;; (message "TOKEN: %S\n" token)
-
-      ;; Reduce based on the top item on the stack (collections)
-      (cond
-       ((parseclj-lex-leaf-token? token)
-        (setf stack (funcall reduce-leaf stack token options)))
-
-       ((parseclj-lex-closing-token? token)
-        (setf stack (parseclj--reduce-coll stack token reduce-branch options)))
-
-       (t (push token stack)))
-
-      ;; Reduce based on top two items on the stack (special prefixed elements)
-      (let* ((top-value (parseclj--take-value stack value-p))
-             (opening-token (parseclj--take-token (nthcdr (length top-value) 
stack) value-p '(:discard :tag)))
-             (new-stack (nthcdr (+ (length top-value) (length opening-token)) 
stack)))
-        (when (and top-value opening-token)
-          ;; (message "Reducing...")
-          ;; (message "  - STACK %S" stack)
-          ;; (message "  - OPENING_TOKEN %S" opening-token)
-          ;; (message "  - TOP_VALUE %S\n" top-value)
-          (setq stack (funcall reduce-branch new-stack (car opening-token) 
(append (cdr opening-token) top-value) options))))
-
-      (setq token (parseclj-lex-next)))
-
-    ;; reduce root
-    (when fail-fast
-      (when-let ((token (seq-find #'parseclj-lex-token? stack)))
-        (parseclj--error "parseclj: Syntax Error at position %s, unmatched %S"
-                         (a-get token :pos)
-                         (parseclj-lex-token-type token))))
-
-    (car (funcall reduce-branch nil (parseclj-lex-token :root "" 1)
-                  (reverse stack)
-                  options))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Top level API
 
 (defun parseclj-parse-clojure (&rest string-and-options)
   "Parse Clojure source to AST.
 
 Reads either from the current buffer, starting from point, until
-point-max, or reads from the optional string argument.
+`point-max', or reads from the optional string argument.
 
 STRING-AND-OPTIONS can be an optional string, followed by
 key-value pairs to specify parsing options.
 
 - `:lexical-preservation' Retain whitespace, comments, and
-  discards. Defaults to false (`nil').
+  discards.  Defaults to nil.
 - `:fail-fast' Raise an error
-  when encountering invalid syntax. Defaults to true (`t'). "
+  when encountering invalid syntax.  Defaults to t."
   (if (stringp (car string-and-options))
       (with-temp-buffer
         (insert (car string-and-options))
         (goto-char 1)
         (apply 'parseclj-parse-clojure (cdr string-and-options)))
     (let* ((value-p (lambda (e)
-                      (and (parseclj-ast-node? e)
+                      (and (parseclj-ast-node-p e)
                            (not (member (parseclj-ast-node-type e) 
'(:whitespace :comment :discard))))))
            (options (apply 'a-list :value-p value-p string-and-options))
            (lexical? (a-get options :lexical-preservation)))
-      (parseclj-parse (if lexical?
-                          #'parseclj-ast--reduce-leaf-with-lexical-preservation
-                        #'parseclj-ast--reduce-leaf)
-                      (if lexical?
-                          
#'parseclj-ast--reduce-branch-with-lexical-preservation
-                        #'parseclj-ast--reduce-branch)
-                      options))))
+      (parseclj-parser (if lexical?
+                           
#'parseclj-ast--reduce-leaf-with-lexical-preservation
+                         #'parseclj-ast--reduce-leaf)
+                       (if lexical?
+                           
#'parseclj-ast--reduce-branch-with-lexical-preservation
+                         #'parseclj-ast--reduce-branch)
+                       options))))
 
 (defun parseclj-unparse-clojure (ast)
   "Parse Clojure AST to source code.
@@ -329,15 +70,11 @@ key-value pairs to specify parsing options.
 Given an abstract syntax tree AST (as returned by
 parseclj-parse-clojure), turn it back into source code, and
 insert it into the current buffer."
-  (if (parseclj-ast-leaf-node? ast)
+  (if (parseclj-ast-leaf-node-p ast)
       (insert (a-get ast :form))
-    (cl-case (parseclj-ast-node-type ast)
-      (:root (parseclj-unparse--collection ast "" ""))
-      (:list (parseclj-unparse--collection ast "(" ")"))
-      (:vector (parseclj-unparse--collection ast "[" "]"))
-      (:set (parseclj-unparse--collection ast "#{" "}"))
-      (:map (parseclj-unparse--collection ast "{" "}"))
-      (:tag (parseclj-unparse--tag ast)))))
+    (if (eql (parseclj-ast-node-type ast) :tag)
+        (parseclj-ast--unparse-tag ast)
+      (parseclj-ast--unparse-collection ast))))
 
 (defun parseclj-unparse-clojure-to-string (ast)
   "Parse Clojure AST to a source code string.
diff --git a/parseedn.el b/parseedn.el
index 9eaa9fd38e..b1ee4654f7 100644
--- a/parseedn.el
+++ b/parseedn.el
@@ -27,6 +27,17 @@
 
 ;;; Code:
 
+;; The EDN spec is not clear about wether \u0123 and \o012 are supported in
+;; strings. They are described as character literals, but not as string escape
+;; codes. In practice all implementations support them (mostly with broken
+;; surrogate pair support), so we do the same. Sorry, emoji 🙁.
+;;
+;; Note that this is kind of broken, we don't correctly detect if \u or \o 
forms
+;; don't have the right forms.
+
+(require 'a)
+(require 'parseclj-parser)
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Reader
 
@@ -36,17 +47,30 @@
           'uuid (lambda (s)
                   (list 'edn-uuid s)))
   "Default reader functions for handling tagged literals in EDN.
-These are the ones defined in the EDN spec, #inst and #uuid. It
+These are the ones defined in the EDN spec, #inst and #uuid.  It
 is not recommended you change this variable, as this globally
-changes the behavior of the EDN reader. Instead pass your own
+changes the behavior of the EDN reader.  Instead pass your own
 handlers as an optional argument to the reader functions.")
 
 (defun parseedn-reduce-leaf (stack token options)
+  "Put in the STACK an elisp value representing TOKEN.
+
+OPTIONS is an association list.  See `parseclj-parse' for more information
+on available options."
   (if (member (parseclj-lex-token-type token) (list :whitespace :comment))
       stack
-    (cons (parseclj--leaf-token-value token) stack)))
+    (cons (parseclj-lex--leaf-token-value token) stack)))
 
 (defun parseedn-reduce-branch (stack opening-token children options)
+  "Reduce STACK with an sequence containing a collection of other elisp values.
+Ignores discard tokens.
+
+OPENING-TOKEN is a lex token representing an opening paren, bracket or
+brace.
+CHILDREN is a collection elisp values to be reduced into an elisp
+sequence.
+OPTIONS is an association list.  See `parseclj-parse' for more information
+on available options."
   (let ((tag-readers (a-merge parseedn-default-tag-readers (a-get options 
:tag-readers)))
         (token-type (parseclj-lex-token-type opening-token)))
     (if (eq token-type :discard)
@@ -71,21 +95,33 @@ handlers as an optional argument to the reader functions.")
        stack))))
 
 (defun parseedn-read (&optional tag-readers)
-  (parseclj-parse #'parseedn-reduce-leaf
-                  #'parseedn-reduce-branch
-                  (a-list :tag-readers tag-readers)))
+  "Read content from current buffer and parse it as EDN source.
+Returns an Emacs Lisp value.
+
+TAG-READERS is an optional association list where keys are symbols
+identifying *tags*, and values are tag handler functions that receive one
+argument: *the tagged element*, and specify how to interpret it."
+  (parseclj-parser #'parseedn-reduce-leaf
+                   #'parseedn-reduce-branch
+                   (a-list :tag-readers tag-readers)))
 
 (defun parseedn-read-str (s &optional tag-readers)
+  "Parse string S as EDN.
+Returns an Emacs Lisp value.
+
+TAG-READERS is an optional association list.  For more information, see
+`parseedn-read'."
   (with-temp-buffer
     (insert s)
     (goto-char 1)
     (car (parseedn-read tag-readers))))
 
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Printer
 
-
 (defun parseedn-print-seq (coll)
+  "Insert sequence COLL as EDN into the current buffer."
   (parseedn-print (elt coll 0))
   (let ((next (seq-drop coll 1)))
     (when (not (seq-empty-p next))
@@ -93,6 +129,7 @@ handlers as an optional argument to the reader functions.")
       (parseedn-print-seq next))))
 
 (defun parseedn-print-kvs (map)
+  "Insert hash table MAP as an EDN map into the current buffer."
   (let ((keys (a-keys map)))
     (parseedn-print (car keys))
     (insert " ")
@@ -103,6 +140,8 @@ handlers as an optional argument to the reader functions.")
         (parseedn-print-kvs next)))))
 
 (defun parseedn-print (datum)
+  "Insert DATUM as EDN into the current buffer.
+DATUM can be any Emacs Lisp value."
   (cond
    ((or (null datum) (numberp datum))
     (prin1 datum (current-buffer)))
@@ -138,6 +177,8 @@ handlers as an optional argument to the reader functions.")
     (insert "{") (parseedn-print-kvs datum) (insert "}"))))
 
 (defun parseedn-print-str (datum)
+  "Return a string containing DATUM as EDN.
+DATUM can be any Emacs Lisp value."
   (with-temp-buffer
     (parseedn-print datum)
     (buffer-substring-no-properties (point-min) (point-max))))
diff --git a/test/parseclj-lex-test.el b/test/parseclj-lex-test.el
index 6e96522fbc..6589809c48 100644
--- a/test/parseclj-lex-test.el
+++ b/test/parseclj-lex-test.el
@@ -197,18 +197,18 @@
     (should (equal (parseclj-lex-next) (parseclj-lex-token :number "14" 21)))
     (should (equal (parseclj-lex-next) (parseclj-lex-token :rparen ")" 23)))))
 
-(ert-deftest parseclj-lex-test-at-number? ()
+(ert-deftest parseclj-lex-test-at-number-p ()
   (dolist (str '("123" ".9" "+1" "0" "-456"))
     (with-temp-buffer
       (insert str)
       (goto-char 1)
-      (should (equal (parseclj-lex-at-number?) t))))
+      (should (equal (parseclj-lex-at-number-p) t))))
 
   (dolist (str '("a123" "$.9" "+/1" "++0" "-"))
     (with-temp-buffer
       (insert str)
       (goto-char 1)
-      (should (equal (parseclj-lex-at-number?) nil)))))
+      (should (equal (parseclj-lex-at-number-p) nil)))))
 
 (ert-deftest parseclj-lex-test-token ()
   (should (equal (parseclj-lex-token :whitespace ",,," 10)
@@ -216,29 +216,29 @@
                    (:form . ",,,")
                    (:pos . 10)))))
 
-(ert-deftest parseclj-lex-test-digit? ()
-  (should (equal (parseclj-lex-digit? ?0) t))
-  (should (equal (parseclj-lex-digit? ?5) t))
-  (should (equal (parseclj-lex-digit? ?9) t))
-  (should (equal (parseclj-lex-digit? ?a) nil))
-  (should (equal (parseclj-lex-digit? ?-) nil)))
-
-(ert-deftest parseclj-lex-test-symbol-start? ()
-  (should (equal (parseclj-lex-symbol-start? ?0) nil))
-  (should (equal (parseclj-lex-symbol-start? ?a) t))
-  (should (equal (parseclj-lex-symbol-start? ?A) t))
-  (should (equal (parseclj-lex-symbol-start? ?.) t))
-  (should (equal (parseclj-lex-symbol-start? ?. t) nil))
-  (should (equal (parseclj-lex-symbol-start? ?~) nil))
-  (should (equal (parseclj-lex-symbol-start? ? ) nil)))
-
-(ert-deftest parseclj-lex-test-symbol-rest? ()
-  (should (equal (parseclj-lex-symbol-rest? ?0) t))
-  (should (equal (parseclj-lex-symbol-rest? ?a) t))
-  (should (equal (parseclj-lex-symbol-rest? ?A) t))
-  (should (equal (parseclj-lex-symbol-rest? ?.) t))
-  (should (equal (parseclj-lex-symbol-rest? ?~) nil))
-  (should (equal (parseclj-lex-symbol-rest? ? ) nil)))
+(ert-deftest parseclj-lex-test-digit-p ()
+  (should (equal (parseclj-lex-digit-p ?0) t))
+  (should (equal (parseclj-lex-digit-p ?5) t))
+  (should (equal (parseclj-lex-digit-p ?9) t))
+  (should (equal (parseclj-lex-digit-p ?a) nil))
+  (should (equal (parseclj-lex-digit-p ?-) nil)))
+
+(ert-deftest parseclj-lex-test-symbol-start-p ()
+  (should (equal (parseclj-lex-symbol-start-p ?0) nil))
+  (should (equal (parseclj-lex-symbol-start-p ?a) t))
+  (should (equal (parseclj-lex-symbol-start-p ?A) t))
+  (should (equal (parseclj-lex-symbol-start-p ?.) t))
+  (should (equal (parseclj-lex-symbol-start-p ?. t) nil))
+  (should (equal (parseclj-lex-symbol-start-p ?~) nil))
+  (should (equal (parseclj-lex-symbol-start-p ? ) nil)))
+
+(ert-deftest parseclj-lex-test-symbol-rest-p ()
+  (should (equal (parseclj-lex-symbol-rest-p ?0) t))
+  (should (equal (parseclj-lex-symbol-rest-p ?a) t))
+  (should (equal (parseclj-lex-symbol-rest-p ?A) t))
+  (should (equal (parseclj-lex-symbol-rest-p ?.) t))
+  (should (equal (parseclj-lex-symbol-rest-p ?~) nil))
+  (should (equal (parseclj-lex-symbol-rest-p ? ) nil)))
 
 (ert-deftest parseclj-lex-test-get-symbol-at-point ()
   (with-temp-buffer
diff --git a/test/parseclj-test-data.el b/test/parseclj-test-data.el
index 8ec8a000b6..28a03d91dc 100644
--- a/test/parseclj-test-data.el
+++ b/test/parseclj-test-data.el
@@ -28,271 +28,294 @@
 ;;; Code:
 
 (setq parseclj-test-data
-  (a-list
+      (a-list
 
-   "simple-list"
-   (a-list
-    :tags '(:edn-roundtrip)
-    :source "(1 2 3)"
-    :edn '((1 2 3))
-    :ast '((:node-type . :root)
-           (:position . 1)
-           (:children . (((:node-type . :list)
-                          (:position . 1)
-                          (:children . (((:node-type . :number)
-                                         (:position . 2)
-                                         (:form . "1")
-                                         (:value . 1))
-                                        ((:node-type . :number)
-                                         (:position . 4)
-                                         (:form . "2")
-                                         (:value . 2))
-                                        ((:node-type . :number)
-                                         (:position . 6)
-                                         (:form . "3")
-                                         (:value . 3)))))))))
+       "simple-list"
+       (a-list
+        :tags '(:edn-roundtrip)
+        :source "(1 2 3)"
+        :edn '((1 2 3))
+        :ast '((:node-type . :root)
+               (:position . 1)
+               (:children . (((:node-type . :list)
+                              (:position . 1)
+                              (:children . (((:node-type . :number)
+                                             (:position . 2)
+                                             (:form . "1")
+                                             (:value . 1))
+                                            ((:node-type . :number)
+                                             (:position . 4)
+                                             (:form . "2")
+                                             (:value . 2))
+                                            ((:node-type . :number)
+                                             (:position . 6)
+                                             (:form . "3")
+                                             (:value . 3)))))))))
 
 
-   "empty-list"
-   (a-list
-    :source "()"
-    :edn '(())
-    :ast '((:node-type . :root)
-           (:position . 1)
-           (:children . (((:node-type . :list)
-                          (:position . 1)
-                          (:children . nil))))))
+       "empty-list"
+       (a-list
+        :source "()"
+        :edn '(())
+        :ast '((:node-type . :root)
+               (:position . 1)
+               (:children . (((:node-type . :list)
+                              (:position . 1)
+                              (:children . nil))))))
 
-   "size-1"
-   (a-list
-    :tags '(:edn-roundtrip)
-    :source "(1)"
-    :edn '((1))
-    :ast '((:node-type . :root)
-           (:position . 1)
-           (:children . (((:node-type . :list)
-                          (:position . 1)
-                          (:children . (((:node-type . :number)
-                                         (:position . 2)
-                                         (:form . "1")
-                                         (:value . 1)))))))))
+       "size-1"
+       (a-list
+        :tags '(:edn-roundtrip)
+        :source "(1)"
+        :edn '((1))
+        :ast '((:node-type . :root)
+               (:position . 1)
+               (:children . (((:node-type . :list)
+                              (:position . 1)
+                              (:children . (((:node-type . :number)
+                                             (:position . 2)
+                                             (:form . "1")
+                                             (:value . 1)))))))))
 
-   "leafs"
-   (a-list
-    :source "(nil true false hello-world)"
-    :edn '((nil t nil hello-world))
-    :ast '((:node-type . :root)
-           (:position . 1)
-           (:children . (((:node-type . :list)
-                          (:position . 1)
-                          (:children . (((:node-type . :nil)
-                                         (:position . 2)
-                                         (:form . "nil")
-                                         (:value . nil))
-                                        ((:node-type . :true)
-                                         (:position . 6)
-                                         (:form . "true")
-                                         (:value . t))
-                                        ((:node-type . :false)
-                                         (:position . 11)
-                                         (:form . "false")
-                                         (:value . nil))
-                                        ((:node-type . :symbol)
-                                         (:position . 17)
-                                         (:form . "hello-world")
-                                         (:value . hello-world)))))))))
+       "leafs"
+       (a-list
+        :source "(nil true false hello-world)"
+        :edn '((nil t nil hello-world))
+        :ast '((:node-type . :root)
+               (:position . 1)
+               (:children . (((:node-type . :list)
+                              (:position . 1)
+                              (:children . (((:node-type . :nil)
+                                             (:position . 2)
+                                             (:form . "nil")
+                                             (:value . nil))
+                                            ((:node-type . :true)
+                                             (:position . 6)
+                                             (:form . "true")
+                                             (:value . t))
+                                            ((:node-type . :false)
+                                             (:position . 11)
+                                             (:form . "false")
+                                             (:value . nil))
+                                            ((:node-type . :symbol)
+                                             (:position . 17)
+                                             (:form . "hello-world")
+                                             (:value . hello-world)))))))))
 
-   "qualified-symbol"
-   (a-list
-    :tags '(:edn-roundtrip)
-    :source "clojure.string/join"
-    :edn '(clojure.string/join)
-    :ast '((:node-type . :root)
-           (:position . 1)
-           (:children . (((:node-type . :symbol)
-                          (:position . 1)
-                          (:form . "clojure.string/join")
-                          (:value . clojure.string/join))))))
+       "qualified-symbol"
+       (a-list
+        :tags '(:edn-roundtrip)
+        :source "clojure.string/join"
+        :edn '(clojure.string/join)
+        :ast '((:node-type . :root)
+               (:position . 1)
+               (:children . (((:node-type . :symbol)
+                              (:position . 1)
+                              (:form . "clojure.string/join")
+                              (:value . clojure.string/join))))))
 
-   "nested-lists"
-   (a-list
-    :source "((.9 abc (true) (hello)))"
-    :edn '(((0.9 abc (t) (hello))))
-    :ast '((:node-type . :root)
-           (:position . 1)
-           (:children . (((:node-type . :list)
-                          (:position . 1)
-                          (:children . (((:node-type . :list)
-                                         (:position . 2)
-                                         (:children ((:node-type . :number)
-                                                     (:position . 3)
-                                                     (:form . ".9")
-                                                     (:value . 0.9))
-                                                    ((:node-type . :symbol)
-                                                     (:position . 6)
-                                                     (:form . "abc")
-                                                     (:value . abc))
-                                                    ((:node-type . :list)
-                                                     (:position . 10)
-                                                     (:children ((:node-type . 
:true)
-                                                                 (:position . 
11)
-                                                                 (:form . 
"true")
-                                                                 (:value . 
t))))
-                                                    ((:node-type . :list)
-                                                     (:position . 17)
-                                                     (:children ((:node-type . 
:symbol)
-                                                                 (:position . 
18)
-                                                                 (:form . 
"hello")
-                                                                 (:value . 
hello)))))))))))))
+       "nested-lists"
+       (a-list
+        :source "((.9 abc (true) (hello)))"
+        :edn '(((0.9 abc (t) (hello))))
+        :ast '((:node-type . :root)
+               (:position . 1)
+               (:children . (((:node-type . :list)
+                              (:position . 1)
+                              (:children . (((:node-type . :list)
+                                             (:position . 2)
+                                             (:children ((:node-type . :number)
+                                                         (:position . 3)
+                                                         (:form . ".9")
+                                                         (:value . 0.9))
+                                                        ((:node-type . :symbol)
+                                                         (:position . 6)
+                                                         (:form . "abc")
+                                                         (:value . abc))
+                                                        ((:node-type . :list)
+                                                         (:position . 10)
+                                                         (:children 
((:node-type . :true)
+                                                                     
(:position . 11)
+                                                                     (:form . 
"true")
+                                                                     (:value . 
t))))
+                                                        ((:node-type . :list)
+                                                         (:position . 17)
+                                                         (:children 
((:node-type . :symbol)
+                                                                     
(:position . 18)
+                                                                     (:form . 
"hello")
+                                                                     (:value . 
hello)))))))))))))
 
-   "strings-1"
-   (a-list
-    :tags '(:edn-roundtrip)
-    :source "\"abc hello \\t\\\"x\""
-    :edn '("abc hello \t\"x")
-    :ast '((:node-type . :root)
-           (:position . 1)
-           (:children . (((:node-type . :string)
-                          (:position . 1)
-                          (:form . "\"abc hello \\t\\\"x\"")
-                          (:value . "abc hello \t\"x"))))))
+       "strings-1"
+       (a-list
+        :tags '(:edn-roundtrip)
+        :source "\"abc hello \\t\\\"x\""
+        :edn '("abc hello \t\"x")
+        :ast '((:node-type . :root)
+               (:position . 1)
+               (:children . (((:node-type . :string)
+                              (:position . 1)
+                              (:form . "\"abc hello \\t\\\"x\"")
+                              (:value . "abc hello \t\"x"))))))
 
-   "strings-2"
-   (a-list
-    :source "(\"---\\f---\\\"-'\\'-\\\\-\\r\\n\")"
-    :edn '(("---\f---\"-''-\\-\r\n"))
-    :ast '((:node-type . :root)
-           (:position . 1)
-           (:children . (((:node-type . :list)
-                          (:position . 1)
-                          (:children . (((:node-type . :string)
-                                         (:position . 2)
-                                         (:form . 
"\"---\\f---\\\"-'\\'-\\\\-\\r\\n\"")
-                                         (:value . 
"---\f---\"-''-\\-\r\n")))))))))
+       "strings-2"
+       (a-list
+        :source "(\"---\\f---\\\"-'\\'-\\\\-\\r\\n\")"
+        :edn '(("---\f---\"-''-\\-\r\n"))
+        :ast '((:node-type . :root)
+               (:position . 1)
+               (:children . (((:node-type . :list)
+                              (:position . 1)
+                              (:children . (((:node-type . :string)
+                                             (:position . 2)
+                                             (:form . 
"\"---\\f---\\\"-'\\'-\\\\-\\r\\n\"")
+                                             (:value . 
"---\f---\"-''-\\-\r\n")))))))))
 
-   "chars-1"
-   (a-list
-    :source "(\\newline \\return \\space \\tab \\a \\b \\c \\u0078 \\o171)"
-    :edn '((?\n ?\r ?\ ?\t ?a ?b ?c ?x ?y))
-    :ast '((:node-type . :root)
-           (:position . 1)
-           (:children . (((:node-type . :list)
-                          (:position . 1)
-                          (:children . (((:node-type . :character) (:position 
. 2) (:form . "\\newline") (:value . ?\n))
-                                        ((:node-type . :character) (:position 
. 11) (:form . "\\return") (:value . ?\r))
-                                        ((:node-type . :character) (:position 
. 19) (:form . "\\space") (:value . 32))
-                                        ((:node-type . :character) (:position 
. 26) (:form . "\\tab") (:value . ?\t))
-                                        ((:node-type . :character) (:position 
. 31) (:form . "\\a") (:value . ?a))
-                                        ((:node-type . :character) (:position 
. 34) (:form . "\\b") (:value . ?b))
-                                        ((:node-type . :character) (:position 
. 37) (:form . "\\c") (:value . ?c))
-                                        ((:node-type . :character) (:position 
. 40) (:form . "\\u0078") (:value . ?x))
-                                        ((:node-type . :character) (:position 
. 47) (:form . "\\o171") (:value . ?y)))))))))
+       "chars-1"
+       (a-list
+        :source "(\\newline \\return \\space \\tab \\a \\b \\c \\u0078 \\o171)"
+        :edn '((?\n ?\r ?\ ?\t ?a ?b ?c ?x ?y))
+        :ast '((:node-type . :root)
+               (:position . 1)
+               (:children . (((:node-type . :list)
+                              (:position . 1)
+                              (:children . (((:node-type . :character) 
(:position . 2) (:form . "\\newline") (:value . ?\n))
+                                            ((:node-type . :character) 
(:position . 11) (:form . "\\return") (:value . ?\r))
+                                            ((:node-type . :character) 
(:position . 19) (:form . "\\space") (:value . 32))
+                                            ((:node-type . :character) 
(:position . 26) (:form . "\\tab") (:value . ?\t))
+                                            ((:node-type . :character) 
(:position . 31) (:form . "\\a") (:value . ?a))
+                                            ((:node-type . :character) 
(:position . 34) (:form . "\\b") (:value . ?b))
+                                            ((:node-type . :character) 
(:position . 37) (:form . "\\c") (:value . ?c))
+                                            ((:node-type . :character) 
(:position . 40) (:form . "\\u0078") (:value . ?x))
+                                            ((:node-type . :character) 
(:position . 47) (:form . "\\o171") (:value . ?y)))))))))
 
-   "chars-2"
-   (a-list
-    :source "\"\\u0078 \\o171\""
-    :edn '("x y")
-    :ast '((:node-type . :root)
-           (:position . 1)
-           (:children . (((:node-type . :string)
-                          (:position . 1)
-                          (:form . "\"\\u0078 \\o171\"")
-                          (:value . "x y"))))))
+       "chars-2"
+       (a-list
+        :source "\"\\u0078 \\o171\""
+        :edn '("x y")
+        :ast '((:node-type . :root)
+               (:position . 1)
+               (:children . (((:node-type . :string)
+                              (:position . 1)
+                              (:form . "\"\\u0078 \\o171\"")
+                              (:value . "x y"))))))
 
-   "keywords"
-   (a-list
-    :tags '(:edn-roundtrip)
-    :source ":foo-bar"
-    :edn '(:foo-bar)
-    :ast '((:node-type . :root)
-           (:position . 1)
-           (:children . (((:node-type . :keyword)
-                          (:position . 1)
-                          (:form . ":foo-bar")
-                          (:value . :foo-bar))))))
+       "keywords"
+       (a-list
+        :tags '(:edn-roundtrip)
+        :source ":foo-bar"
+        :edn '(:foo-bar)
+        :ast '((:node-type . :root)
+               (:position . 1)
+               (:children . (((:node-type . :keyword)
+                              (:position . 1)
+                              (:form . ":foo-bar")
+                              (:value . :foo-bar))))))
 
-   "vector"
-   (a-list
-    :tags '(:edn-roundtrip)
-    :source "[123]"
-    :edn '([123])
-    :ast '((:node-type . :root)
-           (:position . 1)
-           (:children . (((:node-type . :vector)
-                          (:position . 1)
-                          (:children . (((:node-type . :number)
-                                         (:position . 2)
-                                         (:form . "123")
-                                         (:value . 123)))))))))
+       "vector"
+       (a-list
+        :tags '(:edn-roundtrip)
+        :source "[123]"
+        :edn '([123])
+        :ast '((:node-type . :root)
+               (:position . 1)
+               (:children . (((:node-type . :vector)
+                              (:position . 1)
+                              (:children . (((:node-type . :number)
+                                             (:position . 2)
+                                             (:form . "123")
+                                             (:value . 123)))))))))
 
-   "map"
-   (a-list
-    :tags '(:edn-roundtrip)
-    :source "{:count 123}"
-    :edn (list (a-hash-table :count 123))
-    :ast '((:node-type . :root)
-           (:position . 1)
-           (:children . (((:node-type . :map)
-                          (:position . 1)
-                          (:children . (((:node-type . :keyword)
-                                         (:position . 2)
-                                         (:form . ":count")
-                                         (:value . :count))
-                                        ((:node-type . :number)
-                                         (:position . 9)
-                                         (:form . "123")
-                                         (:value . 123)))))))))
+       "map"
+       (a-list
+        :tags '(:edn-roundtrip)
+        :source "{:count 123}"
+        :edn (list (a-hash-table :count 123))
+        :ast '((:node-type . :root)
+               (:position . 1)
+               (:children . (((:node-type . :map)
+                              (:position . 1)
+                              (:children . (((:node-type . :keyword)
+                                             (:position . 2)
+                                             (:form . ":count")
+                                             (:value . :count))
+                                            ((:node-type . :number)
+                                             (:position . 9)
+                                             (:form . "123")
+                                             (:value . 123)))))))))
 
-   "set"
-   (a-list
-    :tags '(:edn-roundtrip)
-    :source "#{:x}"
-    :edn '((edn-set (:x)))
-    :ast '((:node-type . :root)
-           (:position . 1)
-           (:children . (((:node-type . :set)
-                          (:position . 1)
-                          (:children . (((:node-type . :keyword)
-                                         (:position . 3)
-                                         (:form . ":x")
-                                         (:value . :x)))))))))
+       "set"
+       (a-list
+        :tags '(:edn-roundtrip)
+        :source "#{:x}"
+        :edn '((edn-set (:x)))
+        :ast '((:node-type . :root)
+               (:position . 1)
+               (:children . (((:node-type . :set)
+                              (:position . 1)
+                              (:children . (((:node-type . :keyword)
+                                             (:position . 3)
+                                             (:form . ":x")
+                                             (:value . :x)))))))))
 
-   "discard"
-   (a-list
-    :source "(10 #_11 12 #_#_ 13 14)"
-    :edn '((10 12))
-    :ast '((:node-type . :root)
-           (:position . 1)
-           (:children . (((:node-type . :list)
-                          (:position . 1)
-                          (:children . (((:node-type . :number)
-                                         (:position . 2)
-                                         (:form . "10")
-                                         (:value . 10))
-                                        ((:node-type . :number)
-                                         (:position . 10)
-                                         (:form . "12")
-                                         (:value . 12)))))))))
+       "discard"
+       (a-list
+        :source "(10 #_11 12 #_#_ 13 14)"
+        :edn '((10 12))
+        :ast '((:node-type . :root)
+               (:position . 1)
+               (:children . (((:node-type . :list)
+                              (:position . 1)
+                              (:children . (((:node-type . :number)
+                                             (:position . 2)
+                                             (:form . "10")
+                                             (:value . 10))
+                                            ((:node-type . :number)
+                                             (:position . 10)
+                                             (:form . "12")
+                                             (:value . 12)))))))))
 
 
-   "tag"
-   (a-list
-    :source "#foo/bar [1]"
-    :ast '((:node-type . :root)
-           (:position . 1)
-           (:children . (((:node-type . :tag)
-                          (:position . 1)
-                          (:tag . foo/bar)
-                          (:children . (((:node-type . :vector)
-                                         (:position . 10)
-                                         (:children . (((:node-type . :number)
-                                                        (:position . 11)
-                                                        (:form . "1")
-                                                        (:value . 1))))))))))))
+       "tag"
+       (a-list
+        :source "#foo/bar [1]"
+        :ast '((:node-type . :root)
+               (:position . 1)
+               (:children . (((:node-type . :tag)
+                              (:position . 1)
+                              (:tag . foo/bar)
+                              (:children . (((:node-type . :vector)
+                                             (:position . 10)
+                                             (:children . (((:node-type . 
:number)
+                                                            (:position . 11)
+                                                            (:form . "1")
+                                                            (:value . 
1))))))))))))
 
-   "booleans"
-   (a-list
-    :source "[nil true false]"
-    :edn '([nil t nil]))))
+       "nested-tag"
+       (a-list
+        :source "(fn #param :param-name 1)"
+        :ast '((:node-type . :root)
+               (:position . 1)
+               (:children . (((:node-type . :list)
+                              (:position . 1)
+                              (:children . (((:node-type . :symbol)
+                                             (:position . 2)
+                                             (:form . "fn")
+                                             (:value . fn))
+                                            ((:node-type . :tag)
+                                             (:position . 5)
+                                             (:tag . param)
+                                             (:children . (((:node-type . 
:keyword)
+                                                            (:position . 12)
+                                                            (:form . 
":param-name")
+                                                            (:value . 
:param-name)))))
+                                            ((:node-type . :number)
+                                             (:position . 24)
+                                             (:form . "1")
+                                             (:value . 1)))))))))
+
+       "booleans"
+       (a-list
+        :source "[nil true false]"
+        :edn '([nil t nil]))))
 
 ;;; parseclj-test-data.el ends here
diff --git a/test/parseclj-test.el b/test/parseclj-test.el
index 1809464141..1f77eb74b8 100644
--- a/test/parseclj-test.el
+++ b/test/parseclj-test.el
@@ -68,58 +68,34 @@
   (should (equal
            (condition-case errdata
                (parseclj-parse-clojure "foo]")
-             (parseclj-parse-error (cadr errdata)))
-           "parseclj: Syntax Error at position 4, unmatched :rbracket"))
+             (parseclj-parser-error (cadr errdata)))
+           "At position 4, unmatched :rbracket"))
 
   (should (equal
            (condition-case errdata
                (parseclj-parse-clojure "[foo")
-             (parseclj-parse-error (cadr errdata)))
-           "parseclj: Syntax Error at position 1, unmatched :lbracket"))
+             (parseclj-parser-error (cadr errdata)))
+           "At position 1, unmatched :lbracket"))
 
   (should (equal
            (condition-case errdata
                (parseclj-parse-clojure "(1 2 [ 4)")
-             (parseclj-parse-error (cadr errdata)))
-           "parseclj: Syntax Error at position 6, unmatched :lbracket"))
+             (parseclj-parser-error (cadr errdata)))
+           "At position 6, unmatched :lbracket"))
 
   (should (equal
            (condition-case errdata
                (parseclj-parse-clojure "1 2 #_")
-             (parseclj-parse-error (cadr errdata)))
-           "parseclj: Syntax Error at position 5, unmatched :discard"))
+             (parseclj-parser-error (cadr errdata)))
+           "At position 5, unmatched :discard"))
 
   (should (equal
            (condition-case errdata
                (parseclj-parse-clojure "(1 [2 {3 ( 4}])")
-             (parseclj-parse-error (cadr errdata)))
-           "parseclj: Syntax Error at position 10, unmatched :lparen")))
-
-(ert-deftest parseclj-parse-clojure-fail-fast-test ()
-  (should (equal
-           (condition-case errdata
-               (parseclj-parse-clojure "foo]")
-             (parseclj-parse-error (cadr errdata)))
-           "parseclj: Syntax Error at position 4, unmatched :rbracket"))
-
-  (should (equal
-           (condition-case errdata
-               (parseclj-parse-clojure "[foo")
-             (parseclj-parse-error (cadr errdata)))
-           "parseclj: Syntax Error at position 1, unmatched :lbracket"))
-
-  (should (equal
-           (condition-case errdata
-               (parseclj-parse-clojure "(1 2 [ 4)")
-             (parseclj-parse-error (cadr errdata)))
-           "parseclj: Syntax Error at position 6, unmatched :lbracket"))
-
-  (should (equal
-           (condition-case errdata
-               (parseclj-parse-clojure "1 2 #_")
-             (parseclj-parse-error (cadr errdata)))
-           "parseclj: Syntax Error at position 5, unmatched :discard"))
+             (parseclj-parser-error (cadr errdata)))
+           "At position 10, unmatched :lparen")))
 
+(ert-deftest parseclj-parse-clojure-not-fail-fast-test ()
   (should (equal (parseclj-parse-clojure "(1 [2 {3 ( 4}])" :fail-fast nil)
                  '((:node-type . :root)
                    (:position . 1)
@@ -217,7 +193,7 @@
                   (parseclj-lex-token :discard "#_" 30)
                   (parseclj-ast-node :comment 20))
             (lambda (e)
-              (and (parseclj-ast-node? e)
+              (and (parseclj-ast-node-p e)
                    (not (member (parseclj-ast-node-type e) '(:whitespace 
:comment :discard)))))
             '(:discard))
            '(((:token-type . :discard) (:form . "#_") (:pos . 30))
@@ -231,7 +207,7 @@
                   (parseclj-lex-token :discard "#_" 30)
                   (parseclj-ast-node :comment 20))
             (lambda (e)
-              (and (parseclj-ast-node? e)
+              (and (parseclj-ast-node-p e)
                    (not (member (parseclj-ast-node-type e) '(:whitespace 
:comment :discard)))))
             '(:discard))
            nil)))
@@ -240,7 +216,7 @@
   (let ((stack '(((:node-type . :number) (:position . 3) (:form . "4") (:value 
. 4))
                  ((:token-type . :discard) (:form . "#_") (:pos . 1))))
         (value-p (lambda (e)
-                   (and (parseclj-ast-node? e)
+                   (and (parseclj-ast-node-p e)
                         (not (member (parseclj-ast-node-type e) '(:whitespace 
:comment :discard)))))))
     (should (equal (parseclj--take-value stack value-p)
                    '(((:node-type . :number) (:position . 3) (:form . "4") 
(:value . 4)))))
@@ -256,7 +232,7 @@
   (let ((stack '(((:node-type . :whitespace) (:position . 3) (:form . " "))
                  ((:token-type . :discard) (:form . "#_") (:pos . 1))))
         (value-p (lambda (e)
-                   (and (parseclj-ast-node? e)
+                   (and (parseclj-ast-node-p e)
                         (not (member (parseclj-ast-node-type e) '(:whitespace 
:comment :discard)))))))
 
     (let* ((top-value (parseclj--take-value stack value-p))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]