diff --git a/common-lisp/matching-brackets/matching-brackets-test.lisp b/common-lisp/matching-brackets/matching-brackets-test.lisp index 124d1b36..dfad4099 100644 --- a/common-lisp/matching-brackets/matching-brackets-test.lisp +++ b/common-lisp/matching-brackets/matching-brackets-test.lisp @@ -1,7 +1,7 @@ ;; Ensures that matching-brackets.lisp and the testing library are always loaded (eval-when (:compile-toplevel :load-toplevel :execute) - (load "matching-brackets") - (quicklisp-client:quickload :fiveam)) + (load "matching-brackets") + (quicklisp-client:quickload :fiveam)) ;; Defines the testing package with symbols from matching-brackets and FiveAM in scope ;; The `run-tests` function is exported for use by both the user and test-runner @@ -17,83 +17,83 @@ (test paired-square-brackets (let ((value "[]")) - (is-true (matching-brackets:pairedp value)))) + (is-true (matching-brackets:pairedp value)))) (test empty-string (let ((value "")) - (is-true (matching-brackets:pairedp value)))) + (is-true (matching-brackets:pairedp value)))) (test unpaired-brackets (let ((value "[[")) - (is-false (matching-brackets:pairedp value)))) + (is-false (matching-brackets:pairedp value)))) (test wrong-ordered-brackets (let ((value "}{")) - (is-false (matching-brackets:pairedp value)))) + (is-false (matching-brackets:pairedp value)))) (test wrong-closing-bracket (let ((value "{]")) - (is-false (matching-brackets:pairedp value)))) + (is-false (matching-brackets:pairedp value)))) (test paired-with-whitespace (let ((value "{ }")) - (is-true (matching-brackets:pairedp value)))) + (is-true (matching-brackets:pairedp value)))) (test partially-paired-brackets (let ((value "{[])")) - (is-false (matching-brackets:pairedp value)))) + (is-false (matching-brackets:pairedp value)))) (test simple-nested-brackets (let ((value "{[]}")) - (is-true (matching-brackets:pairedp value)))) + (is-true (matching-brackets:pairedp value)))) (test several-paired-brackets (let ((value "{}[]")) - (is-true (matching-brackets:pairedp value)))) + (is-true (matching-brackets:pairedp value)))) (test paired-and-nested-brackets (let ((value "([{}({}[])])")) - (is-true (matching-brackets:pairedp value)))) + (is-true (matching-brackets:pairedp value)))) (test paired-and-wrong-nested-brackets-but-innermost-are-correct (let ((value "[({}])")) - (is-false (matching-brackets:pairedp value)))) + (is-false (matching-brackets:pairedp value)))) (test unopened-closing-brackets (let ((value "{[)][]}")) - (is-false (matching-brackets:pairedp value)))) + (is-false (matching-brackets:pairedp value)))) (test unpaired-and-nested-brackets (let ((value "([{])")) - (is-false (matching-brackets:pairedp value)))) + (is-false (matching-brackets:pairedp value)))) (test paired-and-wrong-nested-brackets (let ((value "[({]})")) - (is-false (matching-brackets:pairedp value)))) + (is-false (matching-brackets:pairedp value)))) (test paired-and-incomplete-brackets (let ((value "{}[")) - (is-false (matching-brackets:pairedp value)))) + (is-false (matching-brackets:pairedp value)))) (test too-many-closing-brackets (let ((value "[]]")) - (is-false (matching-brackets:pairedp value)))) + (is-false (matching-brackets:pairedp value)))) (test early-unexpected-brackets (let ((value ")()")) - (is-false (matching-brackets:pairedp value)))) + (is-false (matching-brackets:pairedp value)))) (test early-mismatched-brackets (let ((value "{)()")) - (is-false (matching-brackets:pairedp value)))) + (is-false (matching-brackets:pairedp value)))) (test math-expression (let ((value "(((185 + 223.85) * 15) - 543)/2")) - (is-true (matching-brackets:pairedp value)))) + (is-true (matching-brackets:pairedp value)))) (test complex-latex-expression (let ((value "\left(\begin{array}{cc} \frac{1}{3} & x\\ \mathrm{e}^{x} &... x^2 \end{array}\right)")) - (is-true (matching-brackets:pairedp value)))) + (is-true (matching-brackets:pairedp value)))) (defun run-tests (&optional (test-or-suite 'matching-brackets-suite)) "Provides human readable results of test run. Default to entire suite." diff --git a/common-lisp/matching-brackets/matching-brackets.lisp b/common-lisp/matching-brackets/matching-brackets.lisp index ada88ce7..eaf64903 100644 --- a/common-lisp/matching-brackets/matching-brackets.lisp +++ b/common-lisp/matching-brackets/matching-brackets.lisp @@ -9,14 +9,17 @@ ;;;; push open brackets into list ;;;; when closing bracket found, see if it matches last open bracket +;;; set to t to enable debugging; to nil to disable debugging +(defparameter debug-mode nil) + ;;; test to see if closing bracket matches open bracket (defun bracket-match (open-bracket close-bracket) (cond - ((and (char= #\[ open-bracket) (char= #\] close-bracket)) t) - ((and (char= #\( open-bracket) (char= #\) close-bracket)) t) - ((and (char= #\{ open-bracket) (char= #\} close-bracket)) t) - (t nil) - ) + ((and (char= #\[ open-bracket) (char= #\] close-bracket)) t) + ((and (char= #\( open-bracket) (char= #\) close-bracket)) t) + ((and (char= #\{ open-bracket) (char= #\} close-bracket)) t) + (t nil) + ) ) ; => t or nil ;;; test to see if character is an opening bracket @@ -45,37 +48,74 @@ ;;; pops the last value on the open bracket stack and also return the new stack (defun pop-last (a) - (let ((result nil)) - (values (setq result (last a)) (setq a (butlast a))) + (let ((popped nil)) + (values (setq popped (last a)) (setq a (butlast a))) ) ) ; => (G) ; (A B C D E F) +;;; remove the last value on the open bracket stack and also return the new stack +(defun remove-last (a) + (setq a (butlast a)) + ) ; => (A B C D E F) + ;;; let's you peek at the tail of the open bracket stack to see if it's a match ;;; for the closing bracket you just found (defun peek (a) (first (last a)) ) ; => G or nil +;;; only print debug messages when degugging +(defun debug-format (message) + (if debug-mode + (format t message) + ) + ) ; nil + ;;; look for matching bracket pairs (defun pairedp (value) + ;; fast return on corner cases + (when (= 0 (length value)) + (return-from pairedp t) + ) + ;; using a list as a stack - (let ((bracket-stack ())) + (let ((bracket-stack ()) (result t)) (loop for c across value do (when (bracket-either c) + (debug-format (format nil "~A" c)) (if (bracket-open c) - (setq bracket-stack (push-last c bracket-stack)) + (setq bracket-stack (push-last c bracket-stack)) (if (and (bracket-close c) (= 0 (length bracket-stack))) - (return nil) - (if (bracket-match (peek bracket-stack) c) - (multiple-value-bind - (last-open bracket-stack) - (pop-last bracket-stack) - ) ; multiple-value-bind - (return nil) - ) ; if closing matches open; else unmatched + (progn + (setq result nil) + (debug-format (format nil "return from close '~A' before open '~A'" c bracket-stack)) + (return-from pairedp result) + ) + (if (bracket-match (peek bracket-stack) c) + (progn + (setq bracket-stack (remove-last bracket-stack)) + (debug-format (format nil "matched, removing last from stack: '~A'~%" bracket-stack)) + ) + (progn + (setq result nil) + (debug-format (format nil "return from close '~A' doesn't match open '~A' stack:'~A'~%" c (peek bracket-stack) bracket-stack)) + (return-from pairedp result) + ) + ) ; if closing matches open; else unmatched ) ; if open and stack empty; else open and stack not empty ) ; if open; else close ) ; when either - ) ; loop + ) ; loop + (debug-format (format nil "~%")) + (if (= 0 (length bracket-stack)) + (progn + (debug-format (format nil "return from bracket-stack is empty~%")) + (return-from pairedp t) + ) + (progn + (debug-format (format nil "return from bracket-stack '~A' is not empty~%" bracket-stack)) + (return-from pairedp nil) + ) + ) ) ; let ) ; => t or nil diff --git a/common-lisp/matching-brackets/run-tests-lisp.txt b/common-lisp/matching-brackets/run-tests-lisp.txt index 408e2e2e..8c53c24f 100644 --- a/common-lisp/matching-brackets/run-tests-lisp.txt +++ b/common-lisp/matching-brackets/run-tests-lisp.txt @@ -11,8 +11,8 @@ To load "fiveam": Running test suite MATCHING-BRACKETS-SUITE - Running test COMPLEX-LATEX-EXPRESSION f - Running test MATH-EXPRESSION f + Running test COMPLEX-LATEX-EXPRESSION . + Running test MATH-EXPRESSION . Running test EARLY-MISMATCHED-BRACKETS . Running test EARLY-UNEXPECTED-BRACKETS . Running test TOO-MANY-CLOSING-BRACKETS . @@ -21,69 +21,34 @@ Running test suite MATCHING-BRACKETS-SUITE Running test UNPAIRED-AND-NESTED-BRACKETS . Running test UNOPENED-CLOSING-BRACKETS . Running test PAIRED-AND-WRONG-NESTED-BRACKETS-BUT-INNERMOST-ARE-CORRECT . - Running test PAIRED-AND-NESTED-BRACKETS f - Running test SEVERAL-PAIRED-BRACKETS f - Running test SIMPLE-NESTED-BRACKETS f + Running test PAIRED-AND-NESTED-BRACKETS . + Running test SEVERAL-PAIRED-BRACKETS . + Running test SIMPLE-NESTED-BRACKETS . Running test PARTIALLY-PAIRED-BRACKETS . - Running test PAIRED-WITH-WHITESPACE f + Running test PAIRED-WITH-WHITESPACE . Running test WRONG-CLOSING-BRACKET . Running test WRONG-ORDERED-BRACKETS . Running test UNPAIRED-BRACKETS . - Running test EMPTY-STRING f - Running test PAIRED-SQUARE-BRACKETS f + Running test EMPTY-STRING . + Running test PAIRED-SQUARE-BRACKETS . Did 20 checks. - Pass: 12 (60%) + Pass: 20 (100%) Skip: 0 ( 0%) - Fail: 8 (40%) - - Failure Details: - -------------------------------- - PAIRED-SQUARE-BRACKETS in MATCHING-BRACKETS-SUITE []: - (MATCHING-BRACKETS:PAIREDP VALUE) did not return a true value - -------------------------------- - -------------------------------- - EMPTY-STRING in MATCHING-BRACKETS-SUITE []: - (MATCHING-BRACKETS:PAIREDP VALUE) did not return a true value - -------------------------------- - -------------------------------- - PAIRED-WITH-WHITESPACE in MATCHING-BRACKETS-SUITE []: - (MATCHING-BRACKETS:PAIREDP VALUE) did not return a true value - -------------------------------- - -------------------------------- - SIMPLE-NESTED-BRACKETS in MATCHING-BRACKETS-SUITE []: - (MATCHING-BRACKETS:PAIREDP VALUE) did not return a true value - -------------------------------- - -------------------------------- - SEVERAL-PAIRED-BRACKETS in MATCHING-BRACKETS-SUITE []: - (MATCHING-BRACKETS:PAIREDP VALUE) did not return a true value - -------------------------------- - -------------------------------- - PAIRED-AND-NESTED-BRACKETS in MATCHING-BRACKETS-SUITE []: - (MATCHING-BRACKETS:PAIREDP VALUE) did not return a true value - -------------------------------- - -------------------------------- - MATH-EXPRESSION in MATCHING-BRACKETS-SUITE []: - (MATCHING-BRACKETS:PAIREDP VALUE) did not return a true value - -------------------------------- - -------------------------------- - COMPLEX-LATEX-EXPRESSION in MATCHING-BRACKETS-SUITE []: - (MATCHING-BRACKETS:PAIREDP VALUE) did not return a true value - -------------------------------- - - -real 0m2.054s -user 0m1.807s -sys 0m0.245s + Fail: 0 ( 0%) + + +real 0m2.087s +user 0m1.847s +sys 0m0.237s =============================================================================== sblint -v ./matching-brackets.lisp ./matching-brackets-test.lisp ./run-tests.lisp [INFO] Lint file matching-brackets.lisp -matching-brackets.lisp:7:0: style-warning: The variable VALUE is defined but never used. -real 0m0.521s -user 0m0.452s -sys 0m0.069s +real 0m0.461s +user 0m0.403s +sys 0m0.057s =============================================================================== @@ -95,17 +60,17 @@ Indenting region...done Indenting region... Indenting region...done -real 0m0.094s -user 0m0.066s -sys 0m0.029s +real 0m0.096s +user 0m0.068s +sys 0m0.028s =============================================================================== Running: misspell . -real 0m0.027s -user 0m0.026s -sys 0m0.013s +real 0m0.024s +user 0m0.030s +sys 0m0.010s ===============================================================================