Skip to content

Commit 91266fc

Browse files
committed
small improvement to "test-file" a.k.a. ERTS files
1 parent 30a1c4d commit 91266fc

File tree

2 files changed

+146
-23
lines changed

2 files changed

+146
-23
lines changed

scratch-files/notes/strutural-editing.lisp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Point-Char: $
55
;; with paredit, you can't use paredit-splice-sexp-killing-backward
66
;; (M-up) without losing the comment
77
Name: splice backward keeps comments
8-
Command: breeze:splice-sexp-backwards
8+
Code: breeze:splice-sexp-backwards
99
;; TODO automatically skip if the "command" doesn't exist
1010
Skip: t
1111

@@ -24,7 +24,7 @@ $(+ 2 2)
2424

2525

2626
Name: contextual split sexp
27-
Command: breeze:split-sexp
27+
Code: breeze:split-sexp
2828

2929
=-=
3030
(:export
@@ -42,7 +42,7 @@ Command: breeze:split-sexp
4242
=-=-=
4343

4444
Name: fill-paragraph in multiline-comments
45-
Command: breeze:fill-paragraph
45+
Code: breeze:fill-paragraph
4646

4747
=-=
4848
#| this is a long line this is a long line this is a long line this is a long line |#
@@ -79,7 +79,7 @@ a long line |#
7979

8080

8181
Name: delete-char should let you delete a character to fix syntax errors
82-
Command: breeze:delete-char
82+
Code: breeze:delete-char
8383

8484
=-=
8585
(if $; test)
@@ -88,7 +88,7 @@ Command: breeze:delete-char
8888
=-=-=
8989

9090
Name: delete-forward-char should let you delete a character to fix syntax errors
91-
Command: breeze:delete-forward-char
91+
Code: breeze:delete-forward-char
9292

9393
=-=
9494
(if ;$ test)
@@ -98,7 +98,7 @@ Command: breeze:delete-forward-char
9898

9999

100100
Name: forward-slurp-sexp should not ignore comments
101-
Command: breeze:forward-slurp-sexp
101+
Code: breeze:forward-slurp-sexp
102102

103103
=-=
104104
($) ;; asdf
@@ -305,7 +305,7 @@ Name: transpose-chars
305305
(|)
306306
=-=
307307
(|)
308-
=-=
308+
=-=-=
309309

310310

311311
;; TODO transpose loop-clause

src/test-file.lisp

Lines changed: 139 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
;; TODO ironically... there's no tests for this code...
12

23
(cl:in-package :cl-user)
34

@@ -18,6 +19,65 @@
1819

1920
(in-package #:breeze.test-file)
2021

22+
23+
;;; Classes
24+
25+
(defclass test-spec ()
26+
((name
27+
:initform nil
28+
:initarg :name
29+
:accessor name
30+
:documentation "The name of the test.")
31+
(code
32+
:initform nil
33+
:initarg :code
34+
:accessor code
35+
:documentation "A string that specifies what to run for this test.")
36+
(point-char
37+
:initform nil
38+
:initarg :point-char
39+
:accessor point-char
40+
:documentation "The character that represents the point in the parts.")
41+
(before
42+
:initform nil
43+
:initarg :before
44+
:accessor before
45+
:documentation "A string representing the input of the test.")
46+
(after
47+
:initform nil
48+
:initarg :after
49+
:accessor after
50+
:documentation "A string representing the expected output of the test.")
51+
(skipp
52+
:initform nil
53+
:initarg :skipp
54+
:accessor skipp
55+
:documentation "Whether this test should be skipped.")
56+
(source-pathname
57+
:initform nil
58+
:initarg :source-pathname
59+
:accessor source-pathname
60+
:documentation "The pathname of the file from which this specification was read from.")
61+
(source-position
62+
:initform nil
63+
:initarg :source-position
64+
:accessor source-position
65+
:documentation "A cons (START . END) that corresponds to the file-position of this
66+
specification in the file it was read from.")
67+
;; TODO perhaps "source": which file it was read from, which position in the file?
68+
)
69+
(:documentation "A test that was specified in an \"erts\" file."))
70+
71+
(defmethod print-object ((test-spec test-spec) stream)
72+
(print-unreadable-object
73+
(test-spec stream :type t :identity nil)
74+
(destructuring-bind (start . end)
75+
(source-position test-spec)
76+
(format stream "~s ~s-~s" (name test-spec) start end))))
77+
78+
79+
;;; Parsing utils
80+
2181
(defun string-bool (string)
2282
"If string is a representation of T or NIL, then coerce it."
2383
(cond
@@ -34,19 +94,19 @@
3494
(string= (trim-whitespace string) "=-=-=")))
3595

3696

97+
;;; Low-level ERTS file parsing
3798

38-
(defun read-spec-file (pathname)
99+
(defun %read-spec-file (pathname)
39100
(with
40101
((open-file (stream pathname))
41102
(collectors (tests parts))
42103
(let ((attributes (make-hash-table))
43-
(eof (gensym "eof"))))
44-
(macrolet
45-
((push-char () `(write-char c out))))
104+
(eof '#.(gensym "eof"))
105+
(start 0)))
46106
(labels
47107
((peek (&optional (peek-type t))
48108
(peek-char peek-type stream nil eof))
49-
(get-char () (read-char stream))
109+
(get-char () (read-char stream))
50110
(eofp (x) (eq eof x))
51111
(clean-attributes () (remhash :skip attributes))
52112
(trim-last-newline (string)
@@ -58,21 +118,28 @@
58118
(when (char= #\; c)
59119
(read-line stream nil t)))
60120
(read-string (string)
121+
;; TODO this doesn't actually check anything...
61122
(loop :for c :across string
62123
:do (char= c (get-char))))
63124
(read-test (c)
64125
(when (char= #\= c)
65126
(with-output-to-string (out)
66-
(read-string #. (format nil "=-=~%"))
127+
(read-string #.(format nil "=-=~%"))
67128
(loop :for line = (read-line stream)
68129
:do (cond
69130
((part-delimiter-p line)
70-
(push-parts (trim-last-newline (get-output-stream-string out))))
131+
(push-parts (trim-last-newline
132+
(get-output-stream-string out))))
71133
((end-delimiter-p line)
72-
(push-parts (trim-last-newline (get-output-stream-string out)))
73-
(push-tests `(,@(alexandria:hash-table-plist attributes)
74-
:parts ,(drain-parts)))
134+
(push-parts (trim-last-newline
135+
(get-output-stream-string out)))
136+
(push-tests `(,@(alexandria:hash-table-alist attributes)
137+
(:parts ,@(drain-parts))
138+
(:pathname ,@pathname)
139+
(:position ,@(cons start
140+
(file-position stream)))))
75141
(peek) ;; skip whitespaces
142+
(setf start (file-position stream))
76143
(clean-attributes)
77144
(return-from read-test t))
78145
((string= "\\=-=" line)
@@ -108,7 +175,7 @@
108175
(setf (gethash name attributes) value))))))
109176
(loop
110177
:for c = (peek)
111-
:repeat 250 ;; guard
178+
;; :repeat 250 ;; guard
112179
:until (eofp c)
113180
:for part = (or
114181
(whitespacep c)
@@ -120,17 +187,73 @@
120187
;; (format t "~&Final: ~% ~{~s~%~}" (tests))
121188
(tests)))
122189

190+
191+
;;; ERTS file parsing and validation
192+
193+
;; TODO test-spec-condition instead of generic errors
194+
(defun read-spec-file (pathname)
195+
;; N.B. the validatoin would almost never fail, because the
196+
;; attributes are carried over, if a test doesn't specify a required
197+
;; attribute, it'll just "inherit" the attribute's value from the
198+
;; preceding test. I'm keeping this check in case there's a bug in
199+
;; the parser.
200+
(let ((specs (%read-spec-file pathname)))
201+
(flet ((get-attribute (spec key)
202+
(cdr (assoc key spec))))
203+
(loop :for test :in specs
204+
:for pathname = (get-attribute test :pathname)
205+
:for position = (get-attribute test :position)
206+
:for (start . end) = position
207+
:for name = (get-attribute test :name)
208+
:unless name
209+
:do (error "A test is missing a name (~s between char ~d and ~d)"
210+
pathname start end)
211+
:collect
212+
(let ((code (get-attribute test :code))
213+
(point-char (get-attribute test :point-char))
214+
(parts (get-attribute test :parts))
215+
(skipp (get-attribute test :skip))
216+
before after)
217+
(unless code
218+
(error "The test ~s is missing the \"Code:\" attribute." name))
219+
(unless point-char
220+
(error "The test ~s is missing the \"Point-Char:\" attribute." name))
221+
(unless parts
222+
(error "The test ~s is missing the actual test input(s)." name))
223+
(let ((number-of-parts (length parts)))
224+
(case number-of-parts
225+
(1
226+
(setf before (first parts)
227+
after (first parts)))
228+
(2 (setf before (first parts)
229+
after (second parts)))
230+
(t
231+
(unless (<= 1 number-of-parts 2)
232+
(error "The test ~s (defined in ~s between the chars ~d and ~d) has ~d parts, a test should have only 1 or 2."
233+
name pathname start end number-of-parts)))))
234+
;; TODO Don't discard the "unknown" attributes The way
235+
;; the parser is written, there's no limitation on the
236+
;; valid attributes. Let's say that these here are the
237+
;; "well-known" attributes.
238+
(make-instance 'test-spec
239+
:name name
240+
:source-pathname pathname
241+
:source-position position
242+
:code code
243+
:point-char point-char
244+
:before before
245+
:after after
246+
:skipp skipp))))))
247+
123248
#++
124249
(defparameter *structural-editing-tests*
125250
(read-spec-file
126251
(asdf:system-relative-pathname
127252
"breeze" "scratch-files/notes/strutural-editing.lisp")))
128253

254+
;; parachute:define-test
255+
;; parachute::ensure-test
129256

130257
#++
131258
(loop :for test :in *structural-editing-tests*
132-
:do (format t "~&~a: ~a parts"
133-
(getf test :name)
134-
(length (getf test :parts)))
135-
;; :do (print )
136-
)
259+
:do )

0 commit comments

Comments
 (0)