[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master 68d6973 3/5: Fixed parsing multiple forms inside if/then/e
From: |
Ian Dunn |
Subject: |
[elpa] master 68d6973 3/5: Fixed parsing multiple forms inside if/then/else blocks |
Date: |
Wed, 7 Feb 2018 20:43:26 -0500 (EST) |
branch: master
commit 68d6973ce1509a00d26f86a3addff28b7c42222e
Author: Ian Dunn <address@hidden>
Commit: Ian Dunn <address@hidden>
Fixed parsing multiple forms inside if/then/else blocks
* org-edna.el (org-edna--normalize-forms): New defun.
(org-edna--normalize-sexp-form): Use it for if-statements
(org-edna--normalize-all-forms): Use it.
* org-edna-tests.el: Added new parsing tests.
---
org-edna-tests.el | 86 ++++++++++++++++++++++++++++++++++++++++++++++++-------
org-edna.el | 61 +++++++++++++++++++++++++--------------
org-edna.info | 13 ++++++---
org-edna.org | 5 ++++
4 files changed, 130 insertions(+), 35 deletions(-)
diff --git a/org-edna-tests.el b/org-edna-tests.el
index 01842ec..0a9ddca 100644
--- a/org-edna-tests.el
+++ b/org-edna-tests.el
@@ -175,10 +175,10 @@
(sexp (org-edna-string-form-to-sexp-form input-string 'action)))
(should (equal
sexp
- '(((if ((match "checklist")
- (done?))
- ((self)
- (todo! TODO))
+ '(((if (((match "checklist")
+ (done?)))
+ (((self)
+ (todo! TODO)))
nil)))))))
(ert-deftest org-edna-form-to-sexp-if-else ()
@@ -186,12 +186,78 @@
(sexp (org-edna-string-form-to-sexp-form input-string 'action)))
(should (equal
sexp
- '(((if ((match "checklist")
- (done?))
- ((self)
- (todo! TODO))
- ((siblings)
- (todo! DONE)))))))))
+ '(((if (((match "checklist")
+ (done?)))
+ (((self)
+ (todo! TODO)))
+ (((siblings)
+ (todo! DONE))))))))))
+
+(ert-deftest org-edna-form-to-sexp-if-multiple-thens ()
+ (let* ((input-string "if match(\"checklist\") done? then self next-sibling
todo!(TODO) self set-property!(\"COUNTER\" \"0\") endif")
+ (sexp (org-edna-string-form-to-sexp-form input-string 'action)))
+ (should (equal
+ sexp
+ '(((if (((match "checklist")
+ (done?)))
+ (((self)
+ (next-sibling)
+ (todo! TODO))
+ ((self)
+ (set-property! "COUNTER" "0")))
+ nil)))))))
+
+(ert-deftest org-edna-form-to-sexp-if-multiple-elses ()
+ (let* ((input-string "if match(\"checklist\") done? then self todo!(TODO)
else siblings todo!(DONE) self todo!(TODO) endif")
+ (sexp (org-edna-string-form-to-sexp-form input-string 'action)))
+ (should (equal
+ sexp
+ '(((if (((match "checklist")
+ (done?)))
+ (((self)
+ (todo! TODO)))
+ (((siblings)
+ (todo! DONE))
+ ((self)
+ (todo! TODO))))))))))
+
+(ert-deftest org-edna-form-to-sexp-failed-if ()
+ (pcase-let* ((input-string "if match(\"checklist\") done?")
+ (`(,error . ,data) (should-error
(org-edna-string-form-to-sexp-form
+ input-string 'action)
+ :type 'invalid-read-syntax)))
+ (should (eq error 'invalid-read-syntax))
+ (should (listp data))
+ (should (eq (length data) 6))
+ (should (string-equal (plist-get data :msg) "Malformed if-construct;
expected then terminator"))
+ ;; Error should point to the start of the if-statement
+ (should (eq (plist-get data :error-pos) 0))))
+
+(ert-deftest org-edna-form-to-sexp-failed-if-then ()
+ (pcase-let* ((input-string "if match(\"checklist\") done? then")
+ (`(,error . ,data) (should-error
(org-edna-string-form-to-sexp-form
+ input-string 'action)
+ :type 'invalid-read-syntax)))
+ (should (eq error 'invalid-read-syntax))
+ (should (listp data))
+ (should (eq (length data) 6))
+ (should (string-equal (plist-get data :msg)
+ "Malformed if-construct; expected else or endif
terminator"))
+ ;; Error should point to the start of the if-statement
+ (should (eq (plist-get data :error-pos) 28))))
+
+(ert-deftest org-edna-form-to-sexp-failed-if-then-else ()
+ (pcase-let* ((input-string "if match(\"checklist\") done? then todo!(TODO)
else todo!(TODO)")
+ (`(,error . ,data) (should-error
(org-edna-string-form-to-sexp-form
+ input-string 'action)
+ :type 'invalid-read-syntax)))
+ (should (eq error 'invalid-read-syntax))
+ (should (listp data))
+ (should (eq (length data) 6))
+ (should (string-equal (plist-get data :msg)
+ "Malformed if-construct; expected endif terminator"))
+ ;; Error should point to the start of the if-statement
+ (should (eq (plist-get data :error-pos) 45))))
(ert-deftest org-edna-expand-sexp-form ()
;; Override cl-gentemp so we have a repeatable test
diff --git a/org-edna.el b/org-edna.el
index a72377b..a4d9d3e 100644
--- a/org-edna.el
+++ b/org-edna.el
@@ -218,44 +218,50 @@ the remainder of FORM after the current scope was parsed."
;; ending. If it doesn't match, throw an error.
(let (cond-form then-form else-form have-else)
(pcase-let* ((`(,temp-form ,r-form)
- (org-edna--normalize-sexp-form
+ (org-edna--normalize-forms
remaining-form
;; Only allow conditions in cond forms
'condition
+ '((then))
from-string)))
- ;; Use car-safe to catch r-form = nil
- (unless (equal (car-safe r-form) '(then))
+ (unless r-form
(org-edna--syntax-error
"Malformed if-construct; expected then terminator"
- from-string (cdr (car-safe r-form))))
+ from-string error-pos))
+ ;; Skip the 'then' construct and move forward
(setq cond-form temp-form
+ error-pos (cdar r-form)
remaining-form (cdr r-form)))
+
(pcase-let* ((`(,temp-form ,r-form)
- (org-edna--normalize-sexp-form remaining-form
-
action-or-condition
- from-string)))
- (unless (member (car-safe r-form) '((else) (endif)))
+ (org-edna--normalize-forms remaining-form
+ action-or-condition
+ '((else) (endif))
+ from-string)))
+ (unless r-form
(org-edna--syntax-error
"Malformed if-construct; expected else or endif
terminator"
- from-string (cdr (car-safe r-form))))
- (setq have-else (equal (car r-form) '(else))
+ from-string error-pos))
+ (setq have-else (equal (caar r-form) '(else))
then-form temp-form
+ error-pos (cdar r-form)
remaining-form (cdr r-form)))
(when have-else
(pcase-let* ((`(,temp-form ,r-form)
- (org-edna--normalize-sexp-form remaining-form
-
action-or-condition
- from-string)))
- (unless (equal (car-safe r-form) '(endif))
+ (org-edna--normalize-forms remaining-form
+ action-or-condition
+ '((endif))
+ from-string)))
+ (unless r-form
(org-edna--syntax-error "Malformed if-construct;
expected endif terminator"
- from-string (cdr (car-safe
r-form))))
+ from-string error-pos))
(setq else-form temp-form
remaining-form (cdr r-form))))
(push `(if ,cond-form ,then-form ,else-form) final-form)))
((or 'then 'else 'endif)
(setq need-break t)
;; Push the object back on remaining-form so the if knows where we
are
- (setq remaining-form (cons current-form remaining-form)))
+ (setq remaining-form (cons (cons current-form error-pos)
remaining-form)))
(_
;; Determine the type of the form
;; If we need to change state, return from this scope
@@ -291,22 +297,35 @@ the remainder of FORM after the current scope was parsed."
(push '(!done?) final-form))
(list (nreverse final-form) remaining-form)))
-(defun org-edna--normalize-all-forms (form-list action-or-condition &optional
from-string)
- "Normalize all forms in flat form list FORM-LIST.
+(defun org-edna--normalize-forms (form-list action-or-condition end-forms
&optional from-string)
+ "Normalize forms in flat form list FORM-LIST until one of END-FORMS is found.
ACTION-OR-CONDITION is either 'action or 'condition, indicating
which of the two types is allowed in FORM.
FROM-STRING is used internally, and is non-nil if FORM was
-originally a string."
+originally a string.
+
+END-FORMS is a list of forms. When one of them is found, stop parsing."
(pcase-let* ((`(,final-form ,rem-form) (org-edna--normalize-sexp-form
form-list action-or-condition from-string)))
(setq final-form (list final-form))
- (while rem-form
+ ;; Use car-safe to catch r-form = nil
+ (while (and rem-form (not (member (car (car-safe rem-form)) end-forms)))
(pcase-let* ((`(,new-form ,r-form)
(org-edna--normalize-sexp-form rem-form
action-or-condition from-string)))
(setq final-form (append final-form (list new-form))
rem-form r-form)))
- final-form))
+ (list final-form rem-form)))
+
+(defun org-edna--normalize-all-forms (form-list action-or-condition &optional
from-string)
+ "Normalize all forms in flat form list FORM-LIST.
+
+ACTION-OR-CONDITION is either 'action or 'condition, indicating
+which of the two types is allowed in FORM.
+
+FROM-STRING is used internally, and is non-nil if FORM was
+originally a string."
+ (car-safe (org-edna--normalize-forms form-list action-or-condition nil
from-string)))
(defun org-edna-string-form-to-sexp-form (string-form action-or-condition)
"Parse string form STRING-FORM into an Edna sexp form.
diff --git a/org-edna.info b/org-edna.info
index 25a9421..33b6708 100644
--- a/org-edna.info
+++ b/org-edna.info
@@ -1523,9 +1523,14 @@ File: org-edna.info, Node: 10beta6, Next: 10beta5,
Up: Changelog
1.0beta6
========
+Lots of parsing fixes.
+
• Fixed error reporting
+
• Fixed parsing of negations in conditions
+ • Fixed parsing of multiple forms inside if/then/else blocks
+
File: org-edna.info, Node: 10beta5, Next: 10beta4, Prev: 10beta6, Up:
Changelog
@@ -1667,10 +1672,10 @@ Node: Development43742
Node: Documentation44895
Node: Changelog45340
Node: 10beta645548
-Node: 10beta545716
-Node: 10beta446103
-Node: 10beta346356
-Node: 10beta246795
+Node: 10beta545808
+Node: 10beta446195
+Node: 10beta346448
+Node: 10beta246887
End Tag Table
diff --git a/org-edna.org b/org-edna.org
index 0676a31..e3422de 100644
--- a/org-edna.org
+++ b/org-edna.org
@@ -1292,8 +1292,13 @@ making any changes:
:DESCRIPTION: List of changes by version
:END:
** 1.0beta6
+Lots of parsing fixes.
+
- Fixed error reporting
+
- Fixed parsing of negations in conditions
+
+- Fixed parsing of multiple forms inside if/then/else blocks
** 1.0beta5
Some new forms and a new build system.