Nicolas Goaziou пре 7 година
родитељ
комит
cecdb2bd67
3 измењених фајлова са 100 додато и 46 уклоњено
  1. 83 44
      lisp/ob-core.el
  2. 3 0
      lisp/org-compat.el
  3. 14 2
      testing/lisp/test-ob.el

+ 83 - 44
lisp/ob-core.el

@@ -1440,36 +1440,79 @@ specified in the properties of the current outline entry."
 
 (defun org-babel-balanced-split (string alts)
   "Split STRING on instances of ALTS.
-ALTS is a cons of two character options where each option may be
-either the numeric code of a single character or a list of
-character alternatives.  For example to split on balanced
-instances of \"[ \t]:\" set ALTS to ((32 9) . 58)."
-  (let* ((matches (lambda (ch spec) (if (listp spec) (member ch spec) (equal spec ch))))
-	 (matched (lambda (ch last)
-		    (if (consp alts)
-			(and (funcall matches ch (cdr alts))
-			     (funcall matches last (car alts)))
-		      (funcall matches ch alts))))
-	 (balance 0) (last 0)
-	 quote partial lst)
-    (mapc (lambda (ch)  ; split on [], (), "" balanced instances of [ \t]:
-	    (setq balance (+ balance
-			     (cond ((or (equal 91 ch) (equal 40 ch)) 1)
-				   ((or (equal 93 ch) (equal 41 ch)) -1)
-				   (t 0))))
-	    (when (and (equal 34 ch) (not (equal 92 last)))
-	      (setq quote (not quote)))
-	    (setq partial (cons ch partial))
-	    (when (and (= balance 0) (not quote) (funcall matched ch last))
-	      (setq lst (cons (apply #'string (nreverse
-					       (if (consp alts)
-						   (cddr partial)
-						 (cdr partial))))
-			      lst))
-	      (setq partial nil))
-	    (setq last ch))
-	  (string-to-list string))
-    (nreverse (cons (apply #'string (nreverse partial)) lst))))
+ALTS is a character, or cons of two character options where each
+option may be either the numeric code of a single character or
+a list of character alternatives.  For example, to split on
+balanced instances of \"[ \t]:\", set ALTS to ((32 9) . 58)."
+  (with-temp-buffer
+    (insert string)
+    (goto-char (point-min))
+    (let ((splitp (lambda (past next)
+		    ;; Non-nil when there should be a split after NEXT
+		    ;; character. PAST is the character before NEXT.
+		    (pcase alts
+		      (`(,(and first (pred consp)) . ,(and second (pred consp)))
+		       (and (memq past first) (memq next second)))
+		      (`(,first . ,(and second (pred consp)))
+		       (and (eq past first) (memq next second)))
+		      (`(,(and first (pred consp)) . ,second)
+		       (and (memq past first) (eq next second)))
+		      (`(,first . ,second)
+		       (and (eq past first) (eq next second)))
+		      ((pred (eq next)) t)
+		      (_ nil))))
+	  (partial nil)
+	  (result nil))
+      (while (not (eobp))
+        (cond
+	 ((funcall splitp (char-before) (char-after))
+	  ;; There is a split after point.  If ALTS is two-folds,
+	  ;; remove last parsed character as it belongs to ALTS.
+	  (when (consp alts) (pop partial))
+	  ;; Include elements parsed so far in RESULTS and flush
+	  ;; partial parsing.
+	  (when partial
+	    (push (apply #'string (nreverse partial)) result)
+	    (setq partial nil))
+	  (forward-char))
+	 ((memq (char-after) '(?\( ?\[))
+	  ;; Include everything between balanced brackets.
+	  (let* ((origin (point))
+		 (after (char-after))
+		 (openings (list after)))
+	    (forward-char)
+	    (while (and openings (re-search-forward "[]()]" nil t))
+	      (pcase (char-before)
+		((and match (or ?\[ ?\()) (push match openings))
+		(?\] (when (eq ?\[ (car openings)) (pop openings)))
+		(_ (when (eq ?\( (car openings)) (pop openings)))))
+	    (if (null openings)
+		(setq partial
+		      (nconc (nreverse (string-to-list
+					(buffer-substring origin (point))))
+			     partial))
+	      ;; Un-balanced bracket.  Backtrack.
+	      (push after partial)
+	      (goto-char (1+ origin)))))
+	 ((and (eq ?\" (char-after)) (not (eq ?\\ (char-before))))
+	  ;; Include everything between non-escaped double quotes.
+	  (push ?\" partial)
+	  (let ((origin (point)))
+	    (condition-case nil
+		;; Use `read' since it is fast and takes care of
+		;; escaped quotes already.
+		(setq partial
+		      (nconc (cons ?\"
+				   (nreverse (string-to-list
+					      (read (current-buffer)))))
+			     partial))
+	      ;; No closing double quote found.  Backtrack.
+	      (end-of-file (goto-char (1+ origin))))))
+	 (t (push (char-after) partial)
+	    (forward-char))))
+      ;; Add pending parsing and return result.
+      (when partial (push (apply #'string (nreverse partial)) result))
+      (nreverse result))))
 
 (defun org-babel-join-splits-near-ch (ch list)
   "Join splits where \"=\" is on either end of the split."
@@ -2854,24 +2897,20 @@ Otherwise if CELL looks like lisp (meaning it starts with a
 lisp, otherwise return it unmodified as a string.  Optional
 argument INHIBIT-LISP-EVAL inhibits lisp evaluation for
 situations in which is it not appropriate."
-  (if (and (stringp cell) (not (equal cell "")))
-      (or (org-babel--string-to-number cell)
-          (if (and (not inhibit-lisp-eval)
-		   (or (member (substring cell 0 1) '("(" "'" "`" "["))
-		       (string= cell "*this*")))
-              (eval (read cell) t)
-            (if (string= (substring cell 0 1) "\"")
-		(read cell)
-	      (progn (set-text-properties 0 (length cell) nil cell) cell))))
-    cell))
+  (cond ((not (org-string-nw-p cell)) cell)
+	((org-babel--string-to-number cell))
+	((and (not inhibit-lisp-eval)
+	      (or (memq (string-to-char cell) '(?\( ?' ?` ?\[))
+		  (string= cell "*this*")))
+	 (eval (read cell) t))
+	((eq (string-to-char cell) ?\") (read cell))
+	(t (org-no-properties cell))))
 
 (defun org-babel--string-to-number (string)
   "If STRING represents a number return its value.
-
 Otherwise return nil."
-  (when (string-match "\\`-?[0-9]*\\.?[0-9]*\\'" string)
-    (string-to-number string)))
-(define-obsolete-function-alias 'org-babel-number-p 'org-babel--string-to-number "Org 9.0")
+  (and (string-match-p "\\`-?[0-9]*\\.?[0-9]*\\'" string)
+       (string-to-number string)))
 
 (defun org-babel-import-elisp-from-file (file-name &optional separator)
   "Read the results located at FILE-NAME into an elisp table.

+ 3 - 0
lisp/org-compat.el

@@ -362,6 +362,9 @@ use of this function is for the stuck project list."
 (make-obsolete-variable 'org-time-clocksum-use-effort-durations
   "set `org-duration-units' instead." "Org 9.1")
 
+(define-obsolete-function-alias 'org-babel-number-p
+  'org-babel--string-to-number "Org 9.0")
+
 
 ;;;; Obsolete link types
 

+ 14 - 2
testing/lisp/test-ob.el

@@ -701,11 +701,23 @@ x
     (should (= 2 (length (org-babel-ref-split-args
 			  "a=\"this, no work\", b=1"))))))
 
-(ert-deftest test-ob/org-babel-balanced-split ()
+(ert-deftest test-ob/balanced-split ()
+  "Test `org-babel-balanced-split' specifications."
   (should (equal
 	   '(":a 1" "b [2 3]" "c (4 :d (5 6))")
 	   (org-babel-balanced-split ":a 1 :b [2 3] :c (4 :d (5 6))"
-				     '((32 9) . 58)))))
+				     '((32 9) . 58))))
+  ;; Handle un-balanced parens.
+  (should
+   (equal '(":foo ((6)" "bar 1")
+	  (org-babel-balanced-split ":foo ((6) :bar 1" '((32 9) . 58))))
+  (should
+   (equal '(":foo \"(foo\"" "bar 2")
+	  (org-babel-balanced-split ":foo \"(foo\" :bar 2" '((32 9) . 58))))
+  ;; Handle un-balanced quotes.
+  (should
+   (equal '(":foo \"1" "bar 3")
+	  (org-babel-balanced-split ":foo \"1 :bar 3" '((32 9) . 58)))))
 
 (ert-deftest test-ob/commented-last-block-line-no-var ()
   (org-test-with-temp-text-in-file "