-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
parse-result: New module for generic client-defined parse results
This is a generalization of the concrete-tree-syntax module that uses client-defined parse result representations instead of CST instances. It also adds the ability to integrate representations of skipped input into the result tree. fixes #28
- Loading branch information
Showing
9 changed files
with
557 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
(cl:in-package #:eclector.parse-result) | ||
|
||
(defclass parse-result-client () | ||
()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
(cl:in-package #:eclector.parse-result) | ||
|
||
;;; Source location protocol | ||
|
||
(defgeneric source-position (client stream) | ||
(:method (client stream) | ||
(declare (ignore client)) | ||
(file-position stream))) | ||
|
||
(defgeneric make-source-range (client start end) | ||
(:method (client start end) | ||
(declare (ignore client)) | ||
(cons start end))) | ||
|
||
;;; Parse result protocol | ||
|
||
(defgeneric make-expression-result (client result children source)) | ||
|
||
(defgeneric make-skipped-input-result (client stream reason source) | ||
(:method (client stream reason source) | ||
(declare (ignore client stream reason source)) | ||
nil)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
(cl:defpackage #:eclector.parse-result | ||
(:use | ||
#:common-lisp | ||
#:alexandria) | ||
|
||
(:shadow | ||
#:read) | ||
|
||
;; Source location protocol | ||
(:export | ||
#:source-position | ||
#:make-source-range) | ||
|
||
;; Parse result protocol | ||
(:export | ||
#:make-expression-result | ||
#:make-skipped-input-result) | ||
|
||
;; Read protocol | ||
(:export | ||
#:read) | ||
|
||
;; Client protocol class (can be used as a superclass) | ||
(:export | ||
#:parse-result-client)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
(cl:in-package #:eclector.parse-result) | ||
|
||
;;; A list of sub-lists the form | ||
;;; | ||
;;; (CHILDREN-OF-CURRENT-NODE CHILDREN-OF-PARENT ...) | ||
;;; | ||
(defvar *stack*) | ||
|
||
(defvar *start*) | ||
|
||
(flet ((skip-whitespace (stream eof-error-p) | ||
(loop with readtable = eclector.reader:*readtable* | ||
for char = (eclector.reader:read-char stream eof-error-p) | ||
when (null char) | ||
do (return nil) | ||
while (eq (eclector.readtable:syntax-type readtable char) | ||
:whitespace) | ||
finally (progn | ||
(unread-char char stream) | ||
(return t))))) | ||
|
||
(defmethod eclector.reader:note-skipped-input | ||
((client parse-result-client) input-stream reason) | ||
(let* ((start *start*) | ||
(end (source-position client input-stream)) | ||
(range (make-source-range client start end)) | ||
(parse-result (make-skipped-input-result | ||
client input-stream reason range))) | ||
(when parse-result | ||
(push parse-result (second *stack*))) | ||
;; Try to advance to the next non-whitespace input character, | ||
;; then update *START*. This way, the source location for an | ||
;; object subsequently read INPUT-STREAM will not include the | ||
;; whitespace. | ||
(skip-whitespace input-stream nil) | ||
(setf *start* (source-position client input-stream)))) | ||
|
||
(defmethod eclector.reader:read-common :around | ||
((client parse-result-client) input-stream eof-error-p eof-value) | ||
(let ((*stack* (cons '() *stack*))) | ||
(unless (skip-whitespace input-stream eof-error-p) | ||
(return-from eclector.reader:read-common eof-value)) | ||
(let* (;; *START* is used and potentially modified in | ||
;; NOTE-SKIPPED-INPUT to reflect skipped input | ||
;; (comments, reader macros, *READ-SUPPRESS*) before | ||
;; actually reading something. | ||
(*start* (source-position client input-stream)) | ||
(result (call-next-method)) | ||
(children (reverse (first *stack*))) | ||
(end (source-position client input-stream)) | ||
(source (make-source-range client *start* end)) | ||
(parse-result (make-expression-result | ||
client result children source))) | ||
(push parse-result (second *stack*)) | ||
(values result parse-result))))) | ||
|
||
(defun read (&rest arguments) | ||
(when (null eclector.reader:*client*) | ||
(error "~S must be bound to a client instance." | ||
'eclector.reader:*client*)) | ||
|
||
(destructuring-bind (&optional eof-error-p eof-value) (rest arguments) | ||
(multiple-value-bind (result parse-result orphan-results) | ||
(let ((*stack* (list '()))) | ||
(multiple-value-call #'values | ||
(apply #'eclector.reader:read arguments) | ||
(reverse (rest (first *stack*))))) | ||
;; If we come here, that means that either the call to READ | ||
;; succeeded without encountering end-of-file, or that | ||
;; EOF-ERROR-P is false, end-of-file was encountered, and | ||
;; EOF-VALUE was returned. In the latter case, we want | ||
;; READ to return EOF-VALUE. | ||
(if (and (null eof-error-p) (eq eof-value result)) | ||
eof-value | ||
(values parse-result orphan-results))))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
(cl:defpackage #:eclector.parse-result.test | ||
(:use | ||
#:common-lisp | ||
#:fiveam) | ||
|
||
(:export | ||
#:run-tests)) | ||
|
||
(cl:in-package #:eclector.parse-result.test) | ||
|
||
(def-suite :eclector.parse-result | ||
:in :eclector) | ||
|
||
(defun run-tests () | ||
(run! :eclector.parse-result)) |
Oops, something went wrong.