From 49f4258f01eb8ab69f63730f381ba7116a90aeee Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Sat, 11 Jul 2015 12:03:52 -0500 Subject: [PATCH 01/16] Add check for long forms --- resources/longform.lisp | 118 ++++++++++++++++++++++++++++++++++++++++ src/main/checker.lisp | 26 +++++++-- 2 files changed, 139 insertions(+), 5 deletions(-) create mode 100644 resources/longform.lisp diff --git a/resources/longform.lisp b/resources/longform.lisp new file mode 100644 index 0000000..e21062c --- /dev/null +++ b/resources/longform.lisp @@ -0,0 +1,118 @@ +(in-package #:nothing) + +(progn + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%"))))) diff --git a/src/main/checker.lisp b/src/main/checker.lisp index 2ccf7f4..b369a1b 100644 --- a/src/main/checker.lisp +++ b/src/main/checker.lisp @@ -2,7 +2,7 @@ ; Rules ; - Elements in each form must be indented the same amount -; - No form longer than 50 lines +; * No form longer than 50 lines ; - Top level multiline forms must be separated by exactly one space ; * No line longer than 120 characters ; - No use of unexported symbols in other packages @@ -25,11 +25,13 @@ (defvar *line-no* nil) (defvar *col-no* nil) (defvar *evaluators* nil) +(defvar *form-stack* nil) -(defparameter *possible-states* - '(:begin ; start of file - :normal ; normal processing - )) +(eval-when (:compile-toplevel :load-toplevel :execute) + (defparameter *possible-states* + '(:begin ; start of file + :normal ; normal processing + ))) (defun set-state (state) @@ -109,5 +111,19 @@ (incf *line-no*) (setf *col-no* 0) nil)) + (defevaluator :normal "\\(" + (lambda () + (push + (list *line-no* *col-no*) + *form-stack*) + nil)) + (defevaluator :normal "\\)" + (lambda () + (let + ((form (pop *form-stack*))) + (when + (< 50 (- *line-no* (car form))) + "Forms can't be over 50 lines long")))) + (defevaluator :normal "." (constantly nil)) ) -- 2.25.1 From c03906deb55ebe1c1cd2518df4acdafe2d116501 Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Sat, 11 Jul 2015 14:25:08 -0500 Subject: [PATCH 02/16] Only one in-package per file --- resources/twoinpackage.lisp | 17 +++++++++++++++++ src/main/checker.lisp | 12 +++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 resources/twoinpackage.lisp diff --git a/resources/twoinpackage.lisp b/resources/twoinpackage.lisp new file mode 100644 index 0000000..fb7fbfc --- /dev/null +++ b/resources/twoinpackage.lisp @@ -0,0 +1,17 @@ +(in-package #:nothing) + +(defun hello-world (a b c) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%"))))) + +(in-package #:something) + +(defun hello-world (a b c) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%"))))) diff --git a/src/main/checker.lisp b/src/main/checker.lisp index b369a1b..a9c4082 100644 --- a/src/main/checker.lisp +++ b/src/main/checker.lisp @@ -11,6 +11,7 @@ ; * in-package must be first line in file unless file is package.lisp ; - No whitespace only lines ; - No empty lines at end of file +; * Only one in-package per file ; ; Some thoughts ; - form starting reader macros will have to be hand added to this code @@ -104,8 +105,9 @@ (lambda () (set-state :normal) nil)) (defevaluator :begin ".*" - (lambda () - "Must begin with in-package form")) + (constantly "Must begin with in-package form")) + (defevaluator :normal "\\( *in-package " + (constantly "Only one in-package per file")) (defevaluator :normal "\\n" (lambda () (incf *line-no*) @@ -121,9 +123,9 @@ (lambda () (let ((form (pop *form-stack*))) - (when - (< 50 (- *line-no* (car form))) - "Forms can't be over 50 lines long")))) + (cond + ((not form) "Unmatched ending paren") + ((< 50 (- *line-no* (car form))) "Forms can't be over 50 lines long"))))) (defevaluator :normal "." (constantly nil)) ) -- 2.25.1 From 091a882409debf24cf717ad53e1388e2bada6832 Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Sat, 11 Jul 2015 19:05:38 -0500 Subject: [PATCH 03/16] Check whitespace at end of lines --- resources/whitespaceendline.lisp | 8 ++++++++ resources/whitespacelines.lisp | 8 ++++++++ src/main/checker.lisp | 26 +++++++++++++++++++++++--- 3 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 resources/whitespaceendline.lisp create mode 100644 resources/whitespacelines.lisp diff --git a/resources/whitespaceendline.lisp b/resources/whitespaceendline.lisp new file mode 100644 index 0000000..068ff19 --- /dev/null +++ b/resources/whitespaceendline.lisp @@ -0,0 +1,8 @@ +(in-package #:nothing) + +(defun hello-world (a b c) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%"))))) diff --git a/resources/whitespacelines.lisp b/resources/whitespacelines.lisp new file mode 100644 index 0000000..3724206 --- /dev/null +++ b/resources/whitespacelines.lisp @@ -0,0 +1,8 @@ +(in-package #:nothing) + +(defun hello-world (a b c) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%"))))) diff --git a/src/main/checker.lisp b/src/main/checker.lisp index a9c4082..8bae64f 100644 --- a/src/main/checker.lisp +++ b/src/main/checker.lisp @@ -9,7 +9,8 @@ ; - No tabs ; - Only one space between elements in a form on a single line ; * in-package must be first line in file unless file is package.lisp -; - No whitespace only lines +; * No whitespace at end of line +; * No lines that are only whitespace ; - No empty lines at end of file ; * Only one in-package per file ; @@ -32,6 +33,8 @@ (defparameter *possible-states* '(:begin ; start of file :normal ; normal processing + :beginning-of-line + :beginning-of-symbols ))) @@ -57,7 +60,7 @@ (and ,scanner (stringp text) (multiple-value-bind (start end) (cl-ppcre:scan ,scanner text) - (and start end (= 0 start) (/= 0 end))))))) + (and start end (= 0 start))))))) (lambda (text) (second (multiple-value-list (cl-ppcre:scan ,scanner text)))) ,func) *evaluators*)))) @@ -110,8 +113,22 @@ (constantly "Only one in-package per file")) (defevaluator :normal "\\n" (lambda () + (set-state :beginning-of-line) (incf *line-no*) - (setf *col-no* 0) + (setf *col-no* -1) + nil)) + (defevaluator :normal " +\\n" + (lambda () + "No whitespace at end of line")) + (defevaluator :beginning-of-line " *" + (lambda () + (set-state :beginning-of-symbols) + nil)) + (defevaluator :beginning-of-symbols "\\n" + (lambda () (when (< 0 *col-no*) "No whitespace only lines"))) + (defevaluator :beginning-of-symbols "" + (lambda () + (set-state :normal) nil)) (defevaluator :normal "\\(" (lambda () @@ -127,5 +144,8 @@ ((not form) "Unmatched ending paren") ((< 50 (- *line-no* (car form))) "Forms can't be over 50 lines long"))))) +(cl-ppcre:scan (cl-ppcre:create-scanner " *") " +asdf asdf") + (defevaluator :normal "." (constantly nil)) ) -- 2.25.1 From 7b5c585eae313b0c9ae0731a75c1a6f6d4fefde7 Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Sun, 12 Jul 2015 07:40:12 -0500 Subject: [PATCH 04/16] No tabs --- resources/tabs.lisp | 8 ++++++++ src/main/checker.lisp | 7 +++++-- 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 resources/tabs.lisp diff --git a/resources/tabs.lisp b/resources/tabs.lisp new file mode 100644 index 0000000..e986574 --- /dev/null +++ b/resources/tabs.lisp @@ -0,0 +1,8 @@ +(in-package #:nothing) + +(defun hello-world (a b c) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%"))))) diff --git a/src/main/checker.lisp b/src/main/checker.lisp index 8bae64f..2c42714 100644 --- a/src/main/checker.lisp +++ b/src/main/checker.lisp @@ -6,7 +6,7 @@ ; - Top level multiline forms must be separated by exactly one space ; * No line longer than 120 characters ; - No use of unexported symbols in other packages -; - No tabs +; * No tabs ; - Only one space between elements in a form on a single line ; * in-package must be first line in file unless file is package.lisp ; * No whitespace at end of line @@ -35,6 +35,7 @@ :normal ; normal processing :beginning-of-line :beginning-of-symbols + :all ; matches everything ))) @@ -54,7 +55,7 @@ (list (lambda (state text) (and - (eql ,state state) + (or (eql :all ,state) (eql ,state state)) (or (and (symbolp text) (eql text ,match)) (and ,scanner @@ -104,6 +105,8 @@ ; These are in reverse order (progn (setf *evaluators* nil) + (defevaluator :all "\\t" + (constantly "Must not use tabs")) (defevaluator :begin "\\(in-package[^\\)]*\\)" (lambda () (set-state :normal) nil)) -- 2.25.1 From 1437fe10eb4bcfca53c8c4746fdf68ab37aba0c1 Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Sun, 12 Jul 2015 08:06:53 -0500 Subject: [PATCH 05/16] Check for duplicated empty lines, empty at end of file --- resources/emptylineatend.lisp | 9 +++++++++ resources/twoemptylines.lisp | 9 +++++++++ src/main/checker.lisp | 18 ++++++++++++++++-- 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 resources/emptylineatend.lisp create mode 100644 resources/twoemptylines.lisp diff --git a/resources/emptylineatend.lisp b/resources/emptylineatend.lisp new file mode 100644 index 0000000..ead2dbb --- /dev/null +++ b/resources/emptylineatend.lisp @@ -0,0 +1,9 @@ +(in-package #:nothing) + +(defun hello-world (a b c) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%"))))) + diff --git a/resources/twoemptylines.lisp b/resources/twoemptylines.lisp new file mode 100644 index 0000000..8bba7ed --- /dev/null +++ b/resources/twoemptylines.lisp @@ -0,0 +1,9 @@ +(in-package #:nothing) + + +(defun hello-world (a b c) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%"))))) diff --git a/src/main/checker.lisp b/src/main/checker.lisp index 2c42714..f20b6a8 100644 --- a/src/main/checker.lisp +++ b/src/main/checker.lisp @@ -11,7 +11,8 @@ ; * in-package must be first line in file unless file is package.lisp ; * No whitespace at end of line ; * No lines that are only whitespace -; - No empty lines at end of file +; * No empty lines at end of file +; * Never have two empty lines in a row ; * Only one in-package per file ; ; Some thoughts @@ -34,6 +35,7 @@ '(:begin ; start of file :normal ; normal processing :beginning-of-line + :beginning-of-separators-with-space ; empty space in there :beginning-of-symbols :all ; matches everything ))) @@ -110,6 +112,10 @@ (defevaluator :begin "\\(in-package[^\\)]*\\)" (lambda () (set-state :normal) nil)) + (defevaluator :beginning-of-separators-with-space :eof + (constantly "Must not end with empty line")) + (defevaluator :beginning-of-separators-with-space "\\n" + (constantly "Must not have two empty lines in a row")) (defevaluator :begin ".*" (constantly "Must begin with in-package form")) (defevaluator :normal "\\( *in-package " @@ -127,8 +133,16 @@ (lambda () (set-state :beginning-of-symbols) nil)) + (defevaluator :beginning-of-separators-with-space " *" + (lambda () + (set-state :beginning-of-symbols) + nil)) (defevaluator :beginning-of-symbols "\\n" - (lambda () (when (< 0 *col-no*) "No whitespace only lines"))) + (lambda () + (if + (< 0 *col-no*) + "No whitespace only lines" + (set-state :beginning-of-separators-with-space)))) (defevaluator :beginning-of-symbols "" (lambda () (set-state :normal) -- 2.25.1 From 1f818db9cb54bc53b62e5b7c2268e6db7d62a3fa Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Sun, 12 Jul 2015 08:13:07 -0500 Subject: [PATCH 06/16] Check for internal symbols --- resources/internalsymbols.lisp | 8 ++++++++ src/main/checker.lisp | 7 +++---- 2 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 resources/internalsymbols.lisp diff --git a/resources/internalsymbols.lisp b/resources/internalsymbols.lisp new file mode 100644 index 0000000..ba20295 --- /dev/null +++ b/resources/internalsymbols.lisp @@ -0,0 +1,8 @@ +(in-package #:nothing) + +(defun hello-world (a b c) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line nothing::str) + (format t "This file had some things in int, yay!~%"))))) diff --git a/src/main/checker.lisp b/src/main/checker.lisp index f20b6a8..dcbe9ec 100644 --- a/src/main/checker.lisp +++ b/src/main/checker.lisp @@ -5,7 +5,7 @@ ; * No form longer than 50 lines ; - Top level multiline forms must be separated by exactly one space ; * No line longer than 120 characters -; - No use of unexported symbols in other packages +; * No use of unexported symbols in other packages ; * No tabs ; - Only one space between elements in a form on a single line ; * in-package must be first line in file unless file is package.lisp @@ -160,9 +160,8 @@ (cond ((not form) "Unmatched ending paren") ((< 50 (- *line-no* (car form))) "Forms can't be over 50 lines long"))))) - -(cl-ppcre:scan (cl-ppcre:create-scanner " *") " -asdf asdf") + (defevaluator :normal "::" + (constantly "No internal symbols from other packages")) (defevaluator :normal "." (constantly nil)) ) -- 2.25.1 From 659997d6add304dee5b24b2f496412f2e8e57c4d Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Sun, 12 Jul 2015 09:01:32 -0500 Subject: [PATCH 07/16] Check that toplevel multiline forms must be separated by a space --- resources/good.lisp | 3 +++ resources/unspacedforms.lisp | 13 ++++++++++ src/main/checker.lisp | 47 +++++++++++++++++++----------------- 3 files changed, 41 insertions(+), 22 deletions(-) create mode 100644 resources/unspacedforms.lisp diff --git a/resources/good.lisp b/resources/good.lisp index 3328e2e..bca3e85 100644 --- a/resources/good.lisp +++ b/resources/good.lisp @@ -1,5 +1,8 @@ (in-package #:nothing) +(defun small-guy (a b) (+ a b)) +(defun small-guy-2 (a b) (+ a b)) + (defun hello-world (a b c) (progn (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) diff --git a/resources/unspacedforms.lisp b/resources/unspacedforms.lisp new file mode 100644 index 0000000..5b86285 --- /dev/null +++ b/resources/unspacedforms.lisp @@ -0,0 +1,13 @@ +(in-package #:nothing) + +(defun small-guy (a b) + (+ a b)) +(defun small-guy-2 (a b) + (+ a b)) + +(defun hello-world (a b c) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%"))))) diff --git a/src/main/checker.lisp b/src/main/checker.lisp index dcbe9ec..ae6c1fb 100644 --- a/src/main/checker.lisp +++ b/src/main/checker.lisp @@ -3,7 +3,7 @@ ; Rules ; - Elements in each form must be indented the same amount ; * No form longer than 50 lines -; - Top level multiline forms must be separated by exactly one space +; * Top level multiline forms must be separated by exactly one space ; * No line longer than 120 characters ; * No use of unexported symbols in other packages ; * No tabs @@ -14,6 +14,7 @@ ; * No empty lines at end of file ; * Never have two empty lines in a row ; * Only one in-package per file +; - No hanging close parens ; ; Some thoughts ; - form starting reader macros will have to be hand added to this code @@ -29,14 +30,16 @@ (defvar *col-no* nil) (defvar *evaluators* nil) (defvar *form-stack* nil) +(defvar *form-ended-on-same-line* nil) (eval-when (:compile-toplevel :load-toplevel :execute) (defparameter *possible-states* '(:begin ; start of file :normal ; normal processing :beginning-of-line - :beginning-of-separators-with-space ; empty space in there + :beginning-of-line-with-separator ; empty space in there :beginning-of-symbols + :beginning-of-symbols-with-separator :all ; matches everything ))) @@ -94,6 +97,8 @@ (set-state :begin) (setf *line-no* 0) (setf *col-no* 0) + (setf *form-stack* nil) + (setf *form-ended-on-same-line* nil) (format t "~%File: ~A~%" file) (handler-case (progn (evaluate (slurp-file file)) t) @@ -110,11 +115,10 @@ (defevaluator :all "\\t" (constantly "Must not use tabs")) (defevaluator :begin "\\(in-package[^\\)]*\\)" - (lambda () - (set-state :normal) nil)) - (defevaluator :beginning-of-separators-with-space :eof + (lambda () (set-state :normal))) + (defevaluator :beginning-of-line-with-separator :eof (constantly "Must not end with empty line")) - (defevaluator :beginning-of-separators-with-space "\\n" + (defevaluator :beginning-of-line-with-separator "\\n" (constantly "Must not have two empty lines in a row")) (defevaluator :begin ".*" (constantly "Must begin with in-package form")) @@ -127,26 +131,26 @@ (setf *col-no* -1) nil)) (defevaluator :normal " +\\n" - (lambda () - "No whitespace at end of line")) + (constantly "No whitespace at end of line")) (defevaluator :beginning-of-line " *" - (lambda () - (set-state :beginning-of-symbols) - nil)) - (defevaluator :beginning-of-separators-with-space " *" - (lambda () - (set-state :beginning-of-symbols) - nil)) + (lambda () (set-state :beginning-of-symbols))) + (defevaluator :beginning-of-line-with-separator " *" + (lambda () (set-state :beginning-of-symbols-with-separator))) (defevaluator :beginning-of-symbols "\\n" (lambda () (if (< 0 *col-no*) "No whitespace only lines" - (set-state :beginning-of-separators-with-space)))) + (set-state :beginning-of-line-with-separator)))) (defevaluator :beginning-of-symbols "" (lambda () - (set-state :normal) - nil)) + (if + (and (not *form-stack*) (not *form-ended-on-same-line*)) + "Multiline top level forms must be separated by a space" + (set-state :normal)))) + (defevaluator :beginning-of-symbols-with-separator "" + (lambda () + (set-state :normal))) (defevaluator :normal "\\(" (lambda () (push @@ -159,9 +163,8 @@ ((form (pop *form-stack*))) (cond ((not form) "Unmatched ending paren") - ((< 50 (- *line-no* (car form))) "Forms can't be over 50 lines long"))))) + ((< 50 (- *line-no* (car form))) "Forms can't be over 50 lines long") + (t (setf *form-ended-on-same-line* (= *line-no* (car form))) nil))))) (defevaluator :normal "::" (constantly "No internal symbols from other packages")) - - (defevaluator :normal "." (constantly nil)) - ) + (defevaluator :normal "." (constantly nil))) -- 2.25.1 From 7b1e850e86136da3a24923fe457f76c835620d6d Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Sun, 12 Jul 2015 09:05:08 -0500 Subject: [PATCH 08/16] No hanging close parens --- resources/hangingcloseparens1.lisp | 12 ++++++++++++ resources/hangingcloseparens2.lisp | 13 +++++++++++++ src/main/checker.lisp | 6 +++++- 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 resources/hangingcloseparens1.lisp create mode 100644 resources/hangingcloseparens2.lisp diff --git a/resources/hangingcloseparens1.lisp b/resources/hangingcloseparens1.lisp new file mode 100644 index 0000000..1f08a71 --- /dev/null +++ b/resources/hangingcloseparens1.lisp @@ -0,0 +1,12 @@ +(in-package #:nothing) + +(defun small-guy (a b) (+ a b)) +(defun small-guy-2 (a b) (+ a b)) + +(defun hello-world (a b c) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) +) diff --git a/resources/hangingcloseparens2.lisp b/resources/hangingcloseparens2.lisp new file mode 100644 index 0000000..17cd142 --- /dev/null +++ b/resources/hangingcloseparens2.lisp @@ -0,0 +1,13 @@ +(in-package #:nothing) + +(defun small-guy (a b) (+ a b)) +(defun small-guy-2 (a b) (+ a b)) + +(defun hello-world (a b c) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%")))) + + ) diff --git a/src/main/checker.lisp b/src/main/checker.lisp index ae6c1fb..3db05e2 100644 --- a/src/main/checker.lisp +++ b/src/main/checker.lisp @@ -14,7 +14,7 @@ ; * No empty lines at end of file ; * Never have two empty lines in a row ; * Only one in-package per file -; - No hanging close parens +; * No hanging close parens ; ; Some thoughts ; - form starting reader macros will have to be hand added to this code @@ -142,6 +142,10 @@ (< 0 *col-no*) "No whitespace only lines" (set-state :beginning-of-line-with-separator)))) + (defevaluator :beginning-of-symbols "\\)" + (constantly "No hanging close parens")) + (defevaluator :beginning-of-symbols-with-separator "\\)" + (constantly "No hanging close parens")) (defevaluator :beginning-of-symbols "" (lambda () (if -- 2.25.1 From 52971da82a4de888ce44eedbc942ee480bc0306f Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Sun, 12 Jul 2015 10:15:55 -0500 Subject: [PATCH 09/16] Check for spaces/newlines after opening a form --- resources/good.lisp | 11 ++++--- resources/newlineafteropenparens.lisp | 15 +++++++++ resources/spaceafteropenparens.lisp | 14 +++++++++ src/main/checker.lisp | 45 +++++++++++---------------- 4 files changed, 55 insertions(+), 30 deletions(-) create mode 100644 resources/newlineafteropenparens.lisp create mode 100644 resources/spaceafteropenparens.lisp diff --git a/resources/good.lisp b/resources/good.lisp index bca3e85..adb5e37 100644 --- a/resources/good.lisp +++ b/resources/good.lisp @@ -5,7 +5,10 @@ (defun hello-world (a b c) (progn - (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) - (when - (read-line str) - (format t "This file had some things in int, yay!~%"))))) + (let + ((x y) + (z 9)) + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%"))))) diff --git a/resources/newlineafteropenparens.lisp b/resources/newlineafteropenparens.lisp new file mode 100644 index 0000000..749ffba --- /dev/null +++ b/resources/newlineafteropenparens.lisp @@ -0,0 +1,15 @@ +(in-package #:nothing) + +(defun small-guy (a b) (+ a b)) +(defun small-guy-2 (a b) (+ a b)) + +(defun hello-world (a b c) + (progn + (let + ((x y) + (z 9)) + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + ( + when + (read-line str) + (format t "This file had some things in int, yay!~%"))))) diff --git a/resources/spaceafteropenparens.lisp b/resources/spaceafteropenparens.lisp new file mode 100644 index 0000000..4cab605 --- /dev/null +++ b/resources/spaceafteropenparens.lisp @@ -0,0 +1,14 @@ +(in-package #:nothing) + +(defun small-guy (a b) (+ a b)) +(defun small-guy-2 (a b) (+ a b)) + +(defun hello-world (a b c) + (progn + (let + ((x y) + (z 9)) + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + ( when + (read-line str) + (format t "This file had some things in int, yay!~%"))))) diff --git a/src/main/checker.lisp b/src/main/checker.lisp index 3db05e2..02a3d2d 100644 --- a/src/main/checker.lisp +++ b/src/main/checker.lisp @@ -1,7 +1,8 @@ (in-package #:style-checker) ; Rules -; - Elements in each form must be indented the same amount +; - Elements on new line in each form must be indented the same amount +; * No space/newline after open parens ; * No form longer than 50 lines ; * Top level multiline forms must be separated by exactly one space ; * No line longer than 120 characters @@ -40,6 +41,7 @@ :beginning-of-line-with-separator ; empty space in there :beginning-of-symbols :beginning-of-symbols-with-separator + :first-symbol-of-form ; After an open parens :all ; matches everything ))) @@ -112,40 +114,29 @@ ; These are in reverse order (progn (setf *evaluators* nil) - (defevaluator :all "\\t" - (constantly "Must not use tabs")) - (defevaluator :begin "\\(in-package[^\\)]*\\)" - (lambda () (set-state :normal))) - (defevaluator :beginning-of-line-with-separator :eof - (constantly "Must not end with empty line")) - (defevaluator :beginning-of-line-with-separator "\\n" - (constantly "Must not have two empty lines in a row")) - (defevaluator :begin ".*" - (constantly "Must begin with in-package form")) - (defevaluator :normal "\\( *in-package " - (constantly "Only one in-package per file")) + (defevaluator :all "\\t" (constantly "Must not use tabs")) + (defevaluator :begin "\\(in-package[^\\)]*\\)" (lambda () (set-state :normal))) + (defevaluator :beginning-of-line-with-separator :eof (constantly "Must not end with empty line")) + (defevaluator :beginning-of-line-with-separator "\\n" (constantly "Must not have two empty lines in a row")) + (defevaluator :begin ".*" (constantly "Must begin with in-package form")) + (defevaluator :normal "\\( *in-package " (constantly "Only one in-package per file")) (defevaluator :normal "\\n" (lambda () (set-state :beginning-of-line) (incf *line-no*) (setf *col-no* -1) nil)) - (defevaluator :normal " +\\n" - (constantly "No whitespace at end of line")) - (defevaluator :beginning-of-line " *" - (lambda () (set-state :beginning-of-symbols))) - (defevaluator :beginning-of-line-with-separator " *" - (lambda () (set-state :beginning-of-symbols-with-separator))) + (defevaluator :normal " +\\n" (constantly "No whitespace at end of line")) + (defevaluator :beginning-of-line " *" (lambda () (set-state :beginning-of-symbols))) + (defevaluator :beginning-of-line-with-separator " *" (lambda () (set-state :beginning-of-symbols-with-separator))) (defevaluator :beginning-of-symbols "\\n" (lambda () (if (< 0 *col-no*) "No whitespace only lines" (set-state :beginning-of-line-with-separator)))) - (defevaluator :beginning-of-symbols "\\)" - (constantly "No hanging close parens")) - (defevaluator :beginning-of-symbols-with-separator "\\)" - (constantly "No hanging close parens")) + (defevaluator :beginning-of-symbols "\\)" (constantly "No hanging close parens")) + (defevaluator :beginning-of-symbols-with-separator "\\)" (constantly "No hanging close parens")) (defevaluator :beginning-of-symbols "" (lambda () (if @@ -160,7 +151,7 @@ (push (list *line-no* *col-no*) *form-stack*) - nil)) + (set-state :first-symbol-of-form))) (defevaluator :normal "\\)" (lambda () (let @@ -169,6 +160,8 @@ ((not form) "Unmatched ending paren") ((< 50 (- *line-no* (car form))) "Forms can't be over 50 lines long") (t (setf *form-ended-on-same-line* (= *line-no* (car form))) nil))))) - (defevaluator :normal "::" - (constantly "No internal symbols from other packages")) + (defevaluator :normal "::" (constantly "No internal symbols from other packages")) + (defevaluator :first-symbol-of-form "\\n" (constantly "No new line after opening form")) + (defevaluator :first-symbol-of-form " " (constantly "No space after opening parens")) + (defevaluator :first-symbol-of-form "" (lambda () (set-state :normal))) (defevaluator :normal "." (constantly nil))) -- 2.25.1 From 4ce7dfc301ff2317fe5591a9c23f17e8ac7b7367 Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Sun, 12 Jul 2015 11:57:45 -0500 Subject: [PATCH 10/16] Check for indent level in forms --- resources/invalidindent1.lisp | 14 +++++++++ resources/invalidindent2.lisp | 16 ++++++++++ resources/toplevelindented.lisp | 14 +++++++++ src/main/checker.lisp | 56 ++++++++++++++++++++++----------- 4 files changed, 82 insertions(+), 18 deletions(-) create mode 100644 resources/invalidindent1.lisp create mode 100644 resources/invalidindent2.lisp create mode 100644 resources/toplevelindented.lisp diff --git a/resources/invalidindent1.lisp b/resources/invalidindent1.lisp new file mode 100644 index 0000000..2c2a8f2 --- /dev/null +++ b/resources/invalidindent1.lisp @@ -0,0 +1,14 @@ +(in-package #:nothing) + +(defun small-guy (a b) (+ a b)) +(defun small-guy-2 (a b) (+ a b)) + +(defun hello-world (a b c) + (progn + (let + ((x y) + (z 9)) + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%"))))) diff --git a/resources/invalidindent2.lisp b/resources/invalidindent2.lisp new file mode 100644 index 0000000..83d92dc --- /dev/null +++ b/resources/invalidindent2.lisp @@ -0,0 +1,16 @@ +(in-package #:nothing) + +(defun small-guy (a b) (+ a b)) +(defun small-guy-2 (a b) (+ a b)) + +(defun hello-world (a b c) + (progn + (let + ((x + y) + (z + 9)) + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%"))))) diff --git a/resources/toplevelindented.lisp b/resources/toplevelindented.lisp new file mode 100644 index 0000000..b7aefa4 --- /dev/null +++ b/resources/toplevelindented.lisp @@ -0,0 +1,14 @@ +(in-package #:nothing) + +(defun small-guy (a b) (+ a b)) + (defun small-guy-2 (a b) (+ a b)) + +(defun hello-world (a b c) + (progn + (let + ((x y) + (z 9)) + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%"))))) diff --git a/src/main/checker.lisp b/src/main/checker.lisp index 02a3d2d..2513812 100644 --- a/src/main/checker.lisp +++ b/src/main/checker.lisp @@ -1,7 +1,7 @@ (in-package #:style-checker) ; Rules -; - Elements on new line in each form must be indented the same amount +; * Elements on new line in each form must be indented the same amount ; * No space/newline after open parens ; * No form longer than 50 lines ; * Top level multiline forms must be separated by exactly one space @@ -17,6 +17,10 @@ ; * Only one in-package per file ; * No hanging close parens ; +; Exceptions +; - comments +; - multiline strings + ; Some thoughts ; - form starting reader macros will have to be hand added to this code ; - exceptions will eventually arise, and the rule file will have to be changed @@ -41,7 +45,7 @@ :beginning-of-line-with-separator ; empty space in there :beginning-of-symbols :beginning-of-symbols-with-separator - :first-symbol-of-form ; After an open parens + :first-symbol ; first symbol of form/line :all ; matches everything ))) @@ -84,7 +88,8 @@ (when (not evaluator) (error (make-condition 'check-failure :msg (format nil "Can't check in state ~S: ~S..." *state* (subseq text 0 (min (length text) 10))) :line-no *line-no* :col-no *col-no*))) (let ((problem (funcall (third evaluator)))) - (when problem (error (make-condition 'check-failure :msg problem :line-no *line-no* :col-no *col-no*))) + (when problem + (error (make-condition 'check-failure :msg problem :line-no *line-no* :col-no *col-no*))) (let ((length-of-match (funcall (cadr evaluator) text))) (incf *col-no* length-of-match) @@ -114,18 +119,30 @@ ; These are in reverse order (progn (setf *evaluators* nil) + (defevaluator :normal "\\(" + (lambda () + (push (list *line-no* *col-no*) *form-stack*) + (set-state :first-symbol))) + (defevaluator :first-symbol "\\(" + (lambda () + (cond + ((and (not *form-stack*) (not (zerop *col-no*))) "Top level forms must begin on first column") + ((and *form-stack* (/= (1+ (cadr (car *form-stack*))) *col-no*)) + "All form elements must be indented equally") + (t + (push (list *line-no* *col-no*) *form-stack*) + (set-state :first-symbol))))) (defevaluator :all "\\t" (constantly "Must not use tabs")) (defevaluator :begin "\\(in-package[^\\)]*\\)" (lambda () (set-state :normal))) (defevaluator :beginning-of-line-with-separator :eof (constantly "Must not end with empty line")) (defevaluator :beginning-of-line-with-separator "\\n" (constantly "Must not have two empty lines in a row")) (defevaluator :begin ".*" (constantly "Must begin with in-package form")) - (defevaluator :normal "\\( *in-package " (constantly "Only one in-package per file")) + (defevaluator :all "\\( *in-package " (constantly "Only one in-package per file")) (defevaluator :normal "\\n" (lambda () - (set-state :beginning-of-line) (incf *line-no*) (setf *col-no* -1) - nil)) + (set-state :beginning-of-line))) (defevaluator :normal " +\\n" (constantly "No whitespace at end of line")) (defevaluator :beginning-of-line " *" (lambda () (set-state :beginning-of-symbols))) (defevaluator :beginning-of-line-with-separator " *" (lambda () (set-state :beginning-of-symbols-with-separator))) @@ -134,7 +151,10 @@ (if (< 0 *col-no*) "No whitespace only lines" - (set-state :beginning-of-line-with-separator)))) + (progn + (incf *line-no*) + (setf *col-no* -1) + (set-state :beginning-of-line-with-separator))))) (defevaluator :beginning-of-symbols "\\)" (constantly "No hanging close parens")) (defevaluator :beginning-of-symbols-with-separator "\\)" (constantly "No hanging close parens")) (defevaluator :beginning-of-symbols "" @@ -142,16 +162,10 @@ (if (and (not *form-stack*) (not *form-ended-on-same-line*)) "Multiline top level forms must be separated by a space" - (set-state :normal)))) + (set-state :first-symbol)))) (defevaluator :beginning-of-symbols-with-separator "" (lambda () - (set-state :normal))) - (defevaluator :normal "\\(" - (lambda () - (push - (list *line-no* *col-no*) - *form-stack*) - (set-state :first-symbol-of-form))) + (set-state :first-symbol))) (defevaluator :normal "\\)" (lambda () (let @@ -161,7 +175,13 @@ ((< 50 (- *line-no* (car form))) "Forms can't be over 50 lines long") (t (setf *form-ended-on-same-line* (= *line-no* (car form))) nil))))) (defevaluator :normal "::" (constantly "No internal symbols from other packages")) - (defevaluator :first-symbol-of-form "\\n" (constantly "No new line after opening form")) - (defevaluator :first-symbol-of-form " " (constantly "No space after opening parens")) - (defevaluator :first-symbol-of-form "" (lambda () (set-state :normal))) + (defevaluator :first-symbol "\\n" (constantly "No new line after opening form")) + (defevaluator :first-symbol " " (constantly "No space after opening parens")) + (defevaluator :first-symbol "" + (lambda () + ;(format t "HMMM: ~A ~A ~A~%" *form-stack* *line-no* *col-no*) + (cond + ((and *form-stack* (/= (1+ (cadr (car *form-stack*))) *col-no*)) + "All form elements must be indented equally") + (t (set-state :normal))))) (defevaluator :normal "." (constantly nil))) -- 2.25.1 From 243043b049ee29fb5fbe17286f40f3392b57cbd0 Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Sun, 12 Jul 2015 12:53:57 -0500 Subject: [PATCH 11/16] Check for multiple spaces in form --- resources/twospaces.lisp | 14 ++++++++++++++ src/main/checker.lisp | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 resources/twospaces.lisp diff --git a/resources/twospaces.lisp b/resources/twospaces.lisp new file mode 100644 index 0000000..c5183b3 --- /dev/null +++ b/resources/twospaces.lisp @@ -0,0 +1,14 @@ +(in-package #:nothing) + +(defun small-guy (a b) (+ a b)) +(defun small-guy-2 (a b) (+ a b)) + +(defun hello-world (a b c) + (progn + (let + ((x y) + (z 9)) + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%"))))) diff --git a/src/main/checker.lisp b/src/main/checker.lisp index 2513812..bb057fb 100644 --- a/src/main/checker.lisp +++ b/src/main/checker.lisp @@ -8,7 +8,7 @@ ; * No line longer than 120 characters ; * No use of unexported symbols in other packages ; * No tabs -; - Only one space between elements in a form on a single line +; * Only one space between elements in a form on a single line ; * in-package must be first line in file unless file is package.lisp ; * No whitespace at end of line ; * No lines that are only whitespace @@ -49,7 +49,6 @@ :all ; matches everything ))) - (defun set-state (state) (when (not (find state *possible-states*)) (error "Can't set state to ~A" state)) @@ -184,4 +183,5 @@ ((and *form-stack* (/= (1+ (cadr (car *form-stack*))) *col-no*)) "All form elements must be indented equally") (t (set-state :normal))))) + (defevaluator :normal " " (constantly "Only one space between items of a form")) (defevaluator :normal "." (constantly nil))) -- 2.25.1 From c9508457e34796fd7fcd8029ecd3209582f4c945 Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Sun, 12 Jul 2015 13:28:50 -0500 Subject: [PATCH 12/16] Exceptions for comments --- resources/commentneedsspace.lisp | 8 ++++++ resources/goodcomment.lisp | 36 +++++++++++++++++++++++++ resources/twoemptylineswithcomment.lisp | 15 +++++++++++ src/main/checker.lisp | 23 +++++++++++++++- 4 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 resources/commentneedsspace.lisp create mode 100644 resources/goodcomment.lisp create mode 100644 resources/twoemptylineswithcomment.lisp diff --git a/resources/commentneedsspace.lisp b/resources/commentneedsspace.lisp new file mode 100644 index 0000000..8bd5ae1 --- /dev/null +++ b/resources/commentneedsspace.lisp @@ -0,0 +1,8 @@ +(in-package #:nothing) + +; header comments +(defun multiline-1 (a b c) + (+ a b)) +; still need a space between these forms! +(defun multiline-1 (a b c) + (+ a b)) diff --git a/resources/goodcomment.lisp b/resources/goodcomment.lisp new file mode 100644 index 0000000..9865926 --- /dev/null +++ b/resources/goodcomment.lisp @@ -0,0 +1,36 @@ +(in-package #:nothing) + +(defun small-guy (a b) (+ a b)) + ; great comment here +(defun small-guy-2 (a b) (+ a b)) + +; this is a function definition +; comment about things +; yo +(defun hello-world (a b c) ; This comment is a test + (progn ; of things + (let ; ( and how + ((x y) ; comments can break + (z 9)) ; the evaluator and everything is ok + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when ; because we don't care + (read-line str) + (format t "This file had some things in int, yay!~%")))))) +; what kinds of comments +; do to a file +; + +; header comments +(defun multiline-1 (a b c) + (+ a b)) + +; header comments +(defun multiline-1 (a b c) + (+ a b)) + +; then a comment for +; informational purpose + +(x y z) + +; Then a final comment in the file diff --git a/resources/twoemptylineswithcomment.lisp b/resources/twoemptylineswithcomment.lisp new file mode 100644 index 0000000..080740b --- /dev/null +++ b/resources/twoemptylineswithcomment.lisp @@ -0,0 +1,15 @@ +(in-package #:nothing) + +; comment resets line counter + +; this one does two + +; but we'll get an error soon! + + +(defun hello-world (a b c) + (progn + (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) + (when + (read-line str) + (format t "This file had some things in int, yay!~%"))))) diff --git a/src/main/checker.lisp b/src/main/checker.lisp index bb057fb..0e3dd7a 100644 --- a/src/main/checker.lisp +++ b/src/main/checker.lisp @@ -18,7 +18,7 @@ ; * No hanging close parens ; ; Exceptions -; - comments +; * comments ; - multiline strings ; Some thoughts @@ -45,6 +45,8 @@ :beginning-of-line-with-separator ; empty space in there :beginning-of-symbols :beginning-of-symbols-with-separator + :comment-with-separator ; weird edge case for pre-function comments + :beginning-of-line-with-comment-and-separator ; weird edge case part 2 :first-symbol ; first symbol of form/line :all ; matches everything ))) @@ -118,6 +120,12 @@ ; These are in reverse order (progn (setf *evaluators* nil) + (defevaluator :beginning-of-symbols " *;[^\\n]*" + (lambda () (set-state :normal))) + (defevaluator :beginning-of-symbols-with-separator " *;[^\\n]*" + (lambda () (set-state :comment-with-separator))) + (defevaluator :normal " *;[^\\n]*" + (lambda () (set-state :normal))) (defevaluator :normal "\\(" (lambda () (push (list *line-no* *col-no*) *form-stack*) @@ -142,9 +150,22 @@ (incf *line-no*) (setf *col-no* -1) (set-state :beginning-of-line))) + (defevaluator :comment-with-separator "\\n" + (lambda () + (incf *line-no*) + (setf *col-no* -1) + (set-state :beginning-of-line-with-comment-and-separator) + nil)) (defevaluator :normal " +\\n" (constantly "No whitespace at end of line")) (defevaluator :beginning-of-line " *" (lambda () (set-state :beginning-of-symbols))) (defevaluator :beginning-of-line-with-separator " *" (lambda () (set-state :beginning-of-symbols-with-separator))) + (defevaluator :beginning-of-line-with-comment-and-separator "\\n" + (lambda () + (progn + (incf *line-no*) + (setf *col-no* -1) + (set-state :beginning-of-line-with-separator)))) + (defevaluator :beginning-of-line-with-comment-and-separator " *" (lambda () (set-state :beginning-of-symbols-with-separator))) (defevaluator :beginning-of-symbols "\\n" (lambda () (if -- 2.25.1 From 02e970926c489005f408d69d363d017fb0abfe53 Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Sun, 12 Jul 2015 13:45:26 -0500 Subject: [PATCH 13/16] Add exception for strings --- resources/good.lisp | 13 ++++++++++--- src/main/checker.lisp | 12 +++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/resources/good.lisp b/resources/good.lisp index adb5e37..10a6091 100644 --- a/resources/good.lisp +++ b/resources/good.lisp @@ -3,12 +3,19 @@ (defun small-guy (a b) (+ a b)) (defun small-guy-2 (a b) (+ a b)) +; This comment is awesome (defun hello-world (a b c) (progn (let - ((x y) - (z 9)) + ((x y) ; Ok, this comment is also great + (z 9)) ; so is this one! (with-open-file (str "increasinglylongfilenamesfailme.dat" :direction :input :if-does-not-exist :create) (when (read-line str) - (format t "This file had some things in int, yay!~%"))))) + (format t "This file had some things in int, yay!~%")))))) + +(defvar *x* "hello world + this is a multiline string + with \" some escaped things + and some (_) and whatnot + ") diff --git a/src/main/checker.lisp b/src/main/checker.lisp index 0e3dd7a..3a78d88 100644 --- a/src/main/checker.lisp +++ b/src/main/checker.lisp @@ -19,7 +19,7 @@ ; ; Exceptions ; * comments -; - multiline strings +; * multiline strings ; Some thoughts ; - form starting reader macros will have to be hand added to this code @@ -49,6 +49,7 @@ :beginning-of-line-with-comment-and-separator ; weird edge case part 2 :first-symbol ; first symbol of form/line :all ; matches everything + :in-string ))) (defun set-state (state) @@ -195,6 +196,15 @@ ((< 50 (- *line-no* (car form))) "Forms can't be over 50 lines long") (t (setf *form-ended-on-same-line* (= *line-no* (car form))) nil))))) (defevaluator :normal "::" (constantly "No internal symbols from other packages")) + (defevaluator :in-string "\\\\\"" (constantly nil)) + (defevaluator :normal "\"" (lambda () (set-state :in-string))) + (defevaluator :in-string "\"" (lambda () (set-state :normal))) + (defevaluator :in-string "\\n" + (lambda () + (incf *line-no*) + (setf *col-no* -1) + nil)) + (defevaluator :in-string "." (constantly nil)) (defevaluator :first-symbol "\\n" (constantly "No new line after opening form")) (defevaluator :first-symbol " " (constantly "No space after opening parens")) (defevaluator :first-symbol "" -- 2.25.1 From 3c0f6bbe9b0c88e21965b2d7d2d9378fd660f1b7 Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Sun, 12 Jul 2015 22:52:06 -0500 Subject: [PATCH 14/16] Add pretty printing --- src/main/checker.lisp | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/main/checker.lisp b/src/main/checker.lisp index 3a78d88..f811ee3 100644 --- a/src/main/checker.lisp +++ b/src/main/checker.lisp @@ -108,15 +108,35 @@ (setf *col-no* 0) (setf *form-stack* nil) (setf *form-ended-on-same-line* nil) - (format t "~%File: ~A~%" file) (handler-case - (progn (evaluate (slurp-file file)) t) + (progn + (evaluate (slurp-file file)) + (list :success file)) (check-failure (cf) - (format t " - Had an error: ~S at ~A:~A~%" (check-failure-msg cf) (check-failure-line-no cf) (check-failure-col-no cf)) - nil))) + (list :failure file (check-failure-msg cf) (check-failure-line-no cf) (check-failure-col-no cf))))) (defun check-directory (dir) - (every #'identity (mapcar #'check-file (directory (format nil "~A/**/*.lisp" dir))))) + (mapcar #'check-file (directory (format nil "~A/**/*.lisp" dir)))) + +(defun any-failures (checks) + (find :failure checks :key #'car)) + +(defun print-failure (failure) + (format nil + "Style error in ~A at ~A:~A: ~A~%- ~A~%~VT^" + (second failure) + (1+ (fourth failure)) + (1+ (fifth failure)) + (third failure) + (with-open-file (str (second failure)) (loop :repeat (fourth failure) :do (read-line str)) (read-line str)) + (+ (fifth failure) 2))) + +(defun pretty-print-check-directory (dir) + (let + ((checks (check-directory dir))) + (format t "In ~A: Checked ~A files with ~A failures~%~%" dir (length checks) (length (remove :success checks :key #'car))) + (format t "~{~A~%~}" (mapcar #'print-failure (remove :success checks :key #'car))) + (sb-ext:exit :code (if (any-failures checks) 1 0)))) ; These are in reverse order (progn @@ -142,7 +162,10 @@ (set-state :first-symbol))))) (defevaluator :all "\\t" (constantly "Must not use tabs")) (defevaluator :begin "\\(in-package[^\\)]*\\)" (lambda () (set-state :normal))) - (defevaluator :beginning-of-line-with-separator :eof (constantly "Must not end with empty line")) + (defevaluator :beginning-of-line-with-separator :eof + (lambda () + (incf *line-no* -1) + "Must not end with empty line")) (defevaluator :beginning-of-line-with-separator "\\n" (constantly "Must not have two empty lines in a row")) (defevaluator :begin ".*" (constantly "Must begin with in-package form")) (defevaluator :all "\\( *in-package " (constantly "Only one in-package per file")) @@ -209,7 +232,6 @@ (defevaluator :first-symbol " " (constantly "No space after opening parens")) (defevaluator :first-symbol "" (lambda () - ;(format t "HMMM: ~A ~A ~A~%" *form-stack* *line-no* *col-no*) (cond ((and *form-stack* (/= (1+ (cadr (car *form-stack*))) *col-no*)) "All form elements must be indented equally") -- 2.25.1 From 07a408587371ca312441a3e030810f4fd0632131 Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Sun, 12 Jul 2015 23:01:32 -0500 Subject: [PATCH 15/16] Add exception for package.lisp --- resources/package.lisp | 6 ++++++ src/main/checker.lisp | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 resources/package.lisp diff --git a/resources/package.lisp b/resources/package.lisp new file mode 100644 index 0000000..3a72323 --- /dev/null +++ b/resources/package.lisp @@ -0,0 +1,6 @@ +(defpackage #:something (:use :common-lisp) + (:export :a :b :c)) + +(defpackage #:nothing + (:use :common-lisp) + (:export :l)) diff --git a/src/main/checker.lisp b/src/main/checker.lisp index f811ee3..857d2a7 100644 --- a/src/main/checker.lisp +++ b/src/main/checker.lisp @@ -20,6 +20,7 @@ ; Exceptions ; * comments ; * multiline strings +; * exclude in-package check from package.lisp ; Some thoughts ; - form starting reader macros will have to be hand added to this code @@ -103,7 +104,9 @@ (let ((seq (make-sequence sequence-type (file-length str)))) (read-sequence seq str) seq))) (defun check-file (file) - (set-state :begin) + (if (string= "package" (pathname-name file)) + (set-state :normal) + (set-state :begin)) (setf *line-no* 0) (setf *col-no* 0) (setf *form-stack* nil) -- 2.25.1 From 5dd1203401bbef1e3657fe5cc89ab2e577dd260b Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Mon, 13 Jul 2015 00:49:06 -0500 Subject: [PATCH 16/16] Preparing for release --- bin/buildRelease.sh | 13 ++ bin/test.sh | 4 + src/main/checker.asd | 3 - src/main/checker.lisp | 243 ------------------------------ src/main/package.lisp | 4 +- src/main/style-checker.asd | 8 + src/main/syntax-checker.lisp | 282 +++++++++++++++++++++++++++++++++++ 7 files changed, 309 insertions(+), 248 deletions(-) create mode 100755 bin/buildRelease.sh create mode 100755 bin/test.sh delete mode 100644 src/main/checker.asd delete mode 100644 src/main/checker.lisp create mode 100644 src/main/style-checker.asd create mode 100644 src/main/syntax-checker.lisp diff --git a/bin/buildRelease.sh b/bin/buildRelease.sh new file mode 100755 index 0000000..d5c1019 --- /dev/null +++ b/bin/buildRelease.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +version=$(sbcl --noinform --disable-ldb --lose-on-corruption --end-runtime-options --eval '(format t "~A" (asdf:component-version (asdf:find-system :style-checker)))' --eval "(quit)") + +echo -n "Building version $version, hit enter to continue" +read + +mkdir style-checker_$version +cp -ap src/main/* style-checker_$version/ +tar zcf style-checker_${version}.tar.gz style-checker_$version/ +rm -rf style-checker_$version + +echo "All done, it's in style-checker_${version}.tar.gz, you should tag it and push it up to github" diff --git a/bin/test.sh b/bin/test.sh new file mode 100755 index 0000000..e4cb97d --- /dev/null +++ b/bin/test.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +sbcl --eval "(asdf:load-system :style-checker)" --eval '(syntax-checker:pretty-print-check-directory "src")' +sbcl --eval "(asdf:load-system :style-checker)" --eval '(syntax-checker:pretty-print-check-directory "resources")' diff --git a/src/main/checker.asd b/src/main/checker.asd deleted file mode 100644 index b714aa3..0000000 --- a/src/main/checker.asd +++ /dev/null @@ -1,3 +0,0 @@ -(asdf:defsystem checker :serial t - :components ((:file "package") (:file "checker")) - :depends-on (:cl-ppcre)) diff --git a/src/main/checker.lisp b/src/main/checker.lisp deleted file mode 100644 index 857d2a7..0000000 --- a/src/main/checker.lisp +++ /dev/null @@ -1,243 +0,0 @@ -(in-package #:style-checker) - -; Rules -; * Elements on new line in each form must be indented the same amount -; * No space/newline after open parens -; * No form longer than 50 lines -; * Top level multiline forms must be separated by exactly one space -; * No line longer than 120 characters -; * No use of unexported symbols in other packages -; * No tabs -; * Only one space between elements in a form on a single line -; * in-package must be first line in file unless file is package.lisp -; * No whitespace at end of line -; * No lines that are only whitespace -; * No empty lines at end of file -; * Never have two empty lines in a row -; * Only one in-package per file -; * No hanging close parens -; -; Exceptions -; * comments -; * multiline strings -; * exclude in-package check from package.lisp - -; Some thoughts -; - form starting reader macros will have to be hand added to this code -; - exceptions will eventually arise, and the rule file will have to be changed -; - the proper formatting of "loop" is weird - -(define-condition check-failure nil ((msg :initarg :msg :reader check-failure-msg) - (line-no :initarg :line-no :reader check-failure-line-no) - (col-no :initarg :col-no :reader check-failure-col-no))) - -(defvar *state* nil) -(defvar *line-no* nil) -(defvar *col-no* nil) -(defvar *evaluators* nil) -(defvar *form-stack* nil) -(defvar *form-ended-on-same-line* nil) - -(eval-when (:compile-toplevel :load-toplevel :execute) - (defparameter *possible-states* - '(:begin ; start of file - :normal ; normal processing - :beginning-of-line - :beginning-of-line-with-separator ; empty space in there - :beginning-of-symbols - :beginning-of-symbols-with-separator - :comment-with-separator ; weird edge case for pre-function comments - :beginning-of-line-with-comment-and-separator ; weird edge case part 2 - :first-symbol ; first symbol of form/line - :all ; matches everything - :in-string - ))) - -(defun set-state (state) - (when (not (find state *possible-states*)) - (error "Can't set state to ~A" state)) - (setf *state* state) - nil) - -(defmacro defevaluator (state match func) - (when (not (find state *possible-states*)) (error "~A is an invalid state" state)) - (let - ((scanner (gensym))) - `(let - ((,scanner (when (stringp ,match) (cl-ppcre:create-scanner ,match)))) - (pushnew - (list - (lambda (state text) - (and - (or (eql :all ,state) (eql ,state state)) - (or - (and (symbolp text) (eql text ,match)) - (and ,scanner - (stringp text) - (multiple-value-bind (start end) (cl-ppcre:scan ,scanner text) - (and start end (= 0 start))))))) - (lambda (text) (second (multiple-value-list (cl-ppcre:scan ,scanner text)))) - ,func) - *evaluators*)))) - -(defun evaluate (text) - (if (string= "" text) - (let* - ((evaluator (find-if (lambda (f) (funcall f *state* :eof)) *evaluators* :from-end t :key #'car)) - (problem (when evaluator (funcall (third evaluator))))) - (when problem (error (make-condition 'check-failure :msg problem :line-no *line-no* :col-no *col-no*)))) - (let - ((evaluator (find-if (lambda (f) (funcall f *state* text)) *evaluators* :from-end t :key #'car))) - (when (not evaluator) (error (make-condition 'check-failure :msg (format nil "Can't check in state ~S: ~S..." *state* (subseq text 0 (min (length text) 10))) :line-no *line-no* :col-no *col-no*))) - (let - ((problem (funcall (third evaluator)))) - (when problem - (error (make-condition 'check-failure :msg problem :line-no *line-no* :col-no *col-no*))) - (let - ((length-of-match (funcall (cadr evaluator) text))) - (incf *col-no* length-of-match) - (when (< 120 *col-no*) (error (make-condition 'check-failure :msg "Line longer than 120 characters" :line-no *line-no* :col-no 0))) - (evaluate (subseq text length-of-match))))))) - -(defun slurp-file (filename &key (element-type 'character) (sequence-type 'string)) - (with-open-file (str filename :element-type element-type) - (let ((seq (make-sequence sequence-type (file-length str)))) (read-sequence seq str) seq))) - -(defun check-file (file) - (if (string= "package" (pathname-name file)) - (set-state :normal) - (set-state :begin)) - (setf *line-no* 0) - (setf *col-no* 0) - (setf *form-stack* nil) - (setf *form-ended-on-same-line* nil) - (handler-case - (progn - (evaluate (slurp-file file)) - (list :success file)) - (check-failure (cf) - (list :failure file (check-failure-msg cf) (check-failure-line-no cf) (check-failure-col-no cf))))) - -(defun check-directory (dir) - (mapcar #'check-file (directory (format nil "~A/**/*.lisp" dir)))) - -(defun any-failures (checks) - (find :failure checks :key #'car)) - -(defun print-failure (failure) - (format nil - "Style error in ~A at ~A:~A: ~A~%- ~A~%~VT^" - (second failure) - (1+ (fourth failure)) - (1+ (fifth failure)) - (third failure) - (with-open-file (str (second failure)) (loop :repeat (fourth failure) :do (read-line str)) (read-line str)) - (+ (fifth failure) 2))) - -(defun pretty-print-check-directory (dir) - (let - ((checks (check-directory dir))) - (format t "In ~A: Checked ~A files with ~A failures~%~%" dir (length checks) (length (remove :success checks :key #'car))) - (format t "~{~A~%~}" (mapcar #'print-failure (remove :success checks :key #'car))) - (sb-ext:exit :code (if (any-failures checks) 1 0)))) - -; These are in reverse order -(progn - (setf *evaluators* nil) - (defevaluator :beginning-of-symbols " *;[^\\n]*" - (lambda () (set-state :normal))) - (defevaluator :beginning-of-symbols-with-separator " *;[^\\n]*" - (lambda () (set-state :comment-with-separator))) - (defevaluator :normal " *;[^\\n]*" - (lambda () (set-state :normal))) - (defevaluator :normal "\\(" - (lambda () - (push (list *line-no* *col-no*) *form-stack*) - (set-state :first-symbol))) - (defevaluator :first-symbol "\\(" - (lambda () - (cond - ((and (not *form-stack*) (not (zerop *col-no*))) "Top level forms must begin on first column") - ((and *form-stack* (/= (1+ (cadr (car *form-stack*))) *col-no*)) - "All form elements must be indented equally") - (t - (push (list *line-no* *col-no*) *form-stack*) - (set-state :first-symbol))))) - (defevaluator :all "\\t" (constantly "Must not use tabs")) - (defevaluator :begin "\\(in-package[^\\)]*\\)" (lambda () (set-state :normal))) - (defevaluator :beginning-of-line-with-separator :eof - (lambda () - (incf *line-no* -1) - "Must not end with empty line")) - (defevaluator :beginning-of-line-with-separator "\\n" (constantly "Must not have two empty lines in a row")) - (defevaluator :begin ".*" (constantly "Must begin with in-package form")) - (defevaluator :all "\\( *in-package " (constantly "Only one in-package per file")) - (defevaluator :normal "\\n" - (lambda () - (incf *line-no*) - (setf *col-no* -1) - (set-state :beginning-of-line))) - (defevaluator :comment-with-separator "\\n" - (lambda () - (incf *line-no*) - (setf *col-no* -1) - (set-state :beginning-of-line-with-comment-and-separator) - nil)) - (defevaluator :normal " +\\n" (constantly "No whitespace at end of line")) - (defevaluator :beginning-of-line " *" (lambda () (set-state :beginning-of-symbols))) - (defevaluator :beginning-of-line-with-separator " *" (lambda () (set-state :beginning-of-symbols-with-separator))) - (defevaluator :beginning-of-line-with-comment-and-separator "\\n" - (lambda () - (progn - (incf *line-no*) - (setf *col-no* -1) - (set-state :beginning-of-line-with-separator)))) - (defevaluator :beginning-of-line-with-comment-and-separator " *" (lambda () (set-state :beginning-of-symbols-with-separator))) - (defevaluator :beginning-of-symbols "\\n" - (lambda () - (if - (< 0 *col-no*) - "No whitespace only lines" - (progn - (incf *line-no*) - (setf *col-no* -1) - (set-state :beginning-of-line-with-separator))))) - (defevaluator :beginning-of-symbols "\\)" (constantly "No hanging close parens")) - (defevaluator :beginning-of-symbols-with-separator "\\)" (constantly "No hanging close parens")) - (defevaluator :beginning-of-symbols "" - (lambda () - (if - (and (not *form-stack*) (not *form-ended-on-same-line*)) - "Multiline top level forms must be separated by a space" - (set-state :first-symbol)))) - (defevaluator :beginning-of-symbols-with-separator "" - (lambda () - (set-state :first-symbol))) - (defevaluator :normal "\\)" - (lambda () - (let - ((form (pop *form-stack*))) - (cond - ((not form) "Unmatched ending paren") - ((< 50 (- *line-no* (car form))) "Forms can't be over 50 lines long") - (t (setf *form-ended-on-same-line* (= *line-no* (car form))) nil))))) - (defevaluator :normal "::" (constantly "No internal symbols from other packages")) - (defevaluator :in-string "\\\\\"" (constantly nil)) - (defevaluator :normal "\"" (lambda () (set-state :in-string))) - (defevaluator :in-string "\"" (lambda () (set-state :normal))) - (defevaluator :in-string "\\n" - (lambda () - (incf *line-no*) - (setf *col-no* -1) - nil)) - (defevaluator :in-string "." (constantly nil)) - (defevaluator :first-symbol "\\n" (constantly "No new line after opening form")) - (defevaluator :first-symbol " " (constantly "No space after opening parens")) - (defevaluator :first-symbol "" - (lambda () - (cond - ((and *form-stack* (/= (1+ (cadr (car *form-stack*))) *col-no*)) - "All form elements must be indented equally") - (t (set-state :normal))))) - (defevaluator :normal " " (constantly "Only one space between items of a form")) - (defevaluator :normal "." (constantly nil))) diff --git a/src/main/package.lisp b/src/main/package.lisp index bb4da60..31eb3e3 100644 --- a/src/main/package.lisp +++ b/src/main/package.lisp @@ -1,2 +1,2 @@ -(defpackage #:style-checker (:use :cl)) - +(defpackage #:syntax-checker (:use :cl) + (:export #:check-file #:check-directory #:pretty-print-check-directory)) diff --git a/src/main/style-checker.asd b/src/main/style-checker.asd new file mode 100644 index 0000000..3ed785e --- /dev/null +++ b/src/main/style-checker.asd @@ -0,0 +1,8 @@ +(asdf:defsystem style-checker + :name "Style Checker" + :version "0.1" + :maintainer "Frank Duncan (frank@kank.com)" + :author "Frank Duncan (frank@kank.com)" + :serial t + :components ((:file "package") (:file "syntax-checker")) + :depends-on (:cl-ppcre)) diff --git a/src/main/syntax-checker.lisp b/src/main/syntax-checker.lisp new file mode 100644 index 0000000..9a0716d --- /dev/null +++ b/src/main/syntax-checker.lisp @@ -0,0 +1,282 @@ +(in-package #:syntax-checker) + +; Rules +; * Elements on new line in each form must be indented the same amount +; * No space/newline after open parens +; * No form longer than 50 lines +; * Top level multiline forms must be separated by exactly one space +; * No line longer than 120 characters +; * No use of unexported symbols in other packages +; * No tabs +; * Only one space between elements in a form on a single line +; * in-package must be first line in file unless file is package.lisp +; * No whitespace at end of line +; * No lines that are only whitespace +; * No empty lines at end of file +; * Never have two empty lines in a row +; * Only one in-package per file +; * No hanging close parens +; +; Exceptions +; * comments +; * multiline strings +; * exclude in-package check from package.lisp + +; Some thoughts +; - form starting reader macros will have to be hand added to this code +; - exceptions will eventually arise, and the rule file will have to be changed +; - the proper formatting of "loop" is weird + +(define-condition check-failure nil ((msg :initarg :msg :reader check-failure-msg) + (line-no :initarg :line-no :reader check-failure-line-no) + (col-no :initarg :col-no :reader check-failure-col-no))) + +(defvar *state* nil) +(defvar *line-no* nil) +(defvar *col-no* nil) +(defvar *evaluators* nil) +(defvar *form-stack* nil) +(defvar *form-ended-on-same-line* nil) + +(eval-when (:compile-toplevel :load-toplevel :execute) + (defparameter *possible-states* + '(:begin ; start of file + :normal ; normal processing + :beginning-of-line + :beginning-of-line-with-separator ; empty space in there + :beginning-of-symbols + :beginning-of-symbols-with-separator + :comment-with-separator ; weird edge case for pre-function comments + :beginning-of-line-with-comment-and-separator ; weird edge case part 2 + :first-symbol ; first symbol of form/line + :all ; matches everything + :in-string))) + +(defun set-state (state) + (when (not (find state *possible-states*)) + (error "Can't set state to ~A" state)) + (setf *state* state) + nil) + +(defmacro defevaluator (state match func) + (when (not (find state *possible-states*)) (error "~A is an invalid state" state)) + (let + ((scanner (gensym))) + `(let + ((,scanner (when (stringp ,match) (cl-ppcre:create-scanner ,match)))) + (pushnew + (list + (lambda (state text) + (and + (or (eql :all ,state) (eql ,state state)) + (or + (and (symbolp text) (eql text ,match)) + (and + ,scanner + (stringp text) + (multiple-value-bind (start end) (cl-ppcre:scan ,scanner text) + (and start end (= 0 start))))))) + (lambda (text) (second (multiple-value-list (cl-ppcre:scan ,scanner text)))) + ,func) + *evaluators*)))) + +(defun evaluate (text) + (if (string= "" text) + (let* + ((evaluator (find-if (lambda (f) (funcall f *state* :eof)) *evaluators* :from-end t :key #'car)) + (problem (when evaluator (funcall (third evaluator))))) + (when problem (error (make-condition 'check-failure :msg problem :line-no *line-no* :col-no *col-no*)))) + (let + ((evaluator (find-if (lambda (f) (funcall f *state* text)) *evaluators* :from-end t :key #'car))) + (when (not evaluator) + (error + (make-condition 'check-failure + :msg (format nil "Can't check in state ~S: ~S..." + *state* (subseq text 0 (min (length text) 10))) :line-no *line-no* :col-no *col-no*))) + (let + ((problem (funcall (third evaluator)))) + (when problem + (error (make-condition 'check-failure :msg problem :line-no *line-no* :col-no *col-no*))) + (let + ((length-of-match (funcall (cadr evaluator) text))) + (incf *col-no* length-of-match) + (when (< 120 *col-no*) + (error (make-condition 'check-failure :msg "Line longer than 120 characters" :line-no *line-no* :col-no 0))) + (evaluate (subseq text length-of-match))))))) + +(defun slurp-file (filename &key (element-type 'character) (sequence-type 'string)) + (with-open-file (str filename :element-type element-type) + (let ((seq (make-sequence sequence-type (file-length str)))) (read-sequence seq str) seq))) + +(defun check-file (file) + (if (string= "package" (pathname-name file)) + (set-state :normal) + (set-state :begin)) + (setf *line-no* 0) + (setf *col-no* 0) + (setf *form-stack* nil) + (setf *form-ended-on-same-line* nil) + (handler-case + (progn + (evaluate (slurp-file file)) + (list :success file)) + (check-failure (cf) + (list :failure file (check-failure-msg cf) (check-failure-line-no cf) (check-failure-col-no cf))))) + +(defun check-directory (dir) + (mapcar #'check-file (directory (format nil "~A/**/*.lisp" dir)))) + +(defun any-failures (checks) + (find :failure checks :key #'car)) + +(defun print-failure (failure) + (format nil + "Style error in ~A at ~A:~A: ~A~%- ~A~%~VT^" + (second failure) + (1+ (fourth failure)) + (1+ (fifth failure)) + (third failure) + (with-open-file (str (second failure)) (loop :repeat (fourth failure) :do (read-line str)) (read-line str)) + (+ (fifth failure) 2))) + +(defun pretty-print-check-directory (dir) + (let + ((checks (check-directory dir))) + (format t "In ~A: Checked ~A files with ~A failures~%~%" + dir (length checks) (length (remove :success checks :key #'car))) + (format t "~{~A~%~}" (mapcar #'print-failure (remove :success checks :key #'car))) + (not (any-failures checks)))) + +; These are in reverse order +(defevaluator :beginning-of-symbols " *;[^\\n]*" + (lambda () (set-state :normal))) + +(defevaluator :beginning-of-symbols-with-separator " *;[^\\n]*" + (lambda () (set-state :comment-with-separator))) + +(defevaluator :normal " *;[^\\n]*" + (lambda () (set-state :normal))) + +(defevaluator :normal "\\(" + (lambda () + (push (list *line-no* *col-no*) *form-stack*) + (set-state :first-symbol))) + +(defevaluator :first-symbol "\\(" + (lambda () + (cond + ((and (not *form-stack*) (not (zerop *col-no*))) "Top level forms must begin on first column") + ((and *form-stack* (/= (1+ (cadr (car *form-stack*))) *col-no*)) + "All form elements must be indented equally") + (t + (push (list *line-no* *col-no*) *form-stack*) + (set-state :first-symbol))))) + +(defevaluator :all "\\t" (constantly "Must not use tabs")) + +(defevaluator :begin "\\(in-package[^\\)]*\\)" (lambda () (set-state :normal))) + +(defevaluator :beginning-of-line-with-separator :eof + (lambda () + (incf *line-no* -1) + "Must not end with empty line")) + +(defevaluator :beginning-of-line-with-separator "\\n" (constantly "Must not have two empty lines in a row")) + +(defevaluator :begin ".*" (constantly "Must begin with in-package form")) + +(defevaluator :all "\\( *in-package " (constantly "Only one in-package per file")) + +(defevaluator :normal "\\n" + (lambda () + (incf *line-no*) + (setf *col-no* -1) + (set-state :beginning-of-line))) + +(defevaluator :comment-with-separator "\\n" + (lambda () + (incf *line-no*) + (setf *col-no* -1) + (set-state :beginning-of-line-with-comment-and-separator) + nil)) + +(defevaluator :normal " +\\n" (constantly "No whitespace at end of line")) + +(defevaluator :beginning-of-line " *" (lambda () (set-state :beginning-of-symbols))) + +(defevaluator :beginning-of-line-with-separator " *" (lambda () (set-state :beginning-of-symbols-with-separator))) + +(defevaluator :beginning-of-line-with-comment-and-separator "\\n" + (lambda () + (progn + (incf *line-no*) + (setf *col-no* -1) + (set-state :beginning-of-line-with-separator)))) + +(defevaluator :beginning-of-line-with-comment-and-separator " *" + (lambda () (set-state :beginning-of-symbols-with-separator))) + +(defevaluator :beginning-of-symbols "\\n" + (lambda () + (if + (< 0 *col-no*) + "No whitespace only lines" + (progn + (incf *line-no*) + (setf *col-no* -1) + (set-state :beginning-of-line-with-separator))))) + +(defevaluator :beginning-of-symbols "\\)" (constantly "No hanging close parens")) + +(defevaluator :beginning-of-symbols-with-separator "\\)" (constantly "No hanging close parens")) + +(defevaluator :beginning-of-symbols "" + (lambda () + (if + (and (not *form-stack*) (not *form-ended-on-same-line*)) + "Multiline top level forms must be separated by a space" + (set-state :first-symbol)))) + +(defevaluator :beginning-of-symbols-with-separator "" + (lambda () + (set-state :first-symbol))) + +(defevaluator :normal "\\)" + (lambda () + (let + ((form (pop *form-stack*))) + (cond + ((not form) "Unmatched ending paren") + ((< 50 (- *line-no* (car form))) "Forms can't be over 50 lines long") + (t (setf *form-ended-on-same-line* (= *line-no* (car form))) nil))))) + +(defevaluator :normal "::" (constantly "No internal symbols from other packages")) + +(defevaluator :in-string "\\\\\"" (constantly nil)) + +(defevaluator :normal "\"" (lambda () (set-state :in-string))) + +(defevaluator :in-string "\"" (lambda () (set-state :normal))) + +(defevaluator :in-string "\\n" + (lambda () + (incf *line-no*) + (setf *col-no* -1) + nil)) + +(defevaluator :in-string "." (constantly nil)) + +(defevaluator :first-symbol "\\n" (constantly "No new line after opening form")) + +(defevaluator :first-symbol " " (constantly "No space after opening parens")) + +(defevaluator :first-symbol "" + (lambda () + (cond + ((and *form-stack* (/= (1+ (cadr (car *form-stack*))) *col-no*)) + "All form elements must be indented equally") + (t (set-state :normal))))) + +(defevaluator :normal " " (constantly "Only one space between items of a form")) + +(defevaluator :normal "." (constantly nil)) -- 2.25.1