Skip to content

Commit 7e6034e

Browse files
committed
fix/refactor: fix filters operator precedence & remove bit operators
This haven't been noticed by anybody yet but operator precedence for the filter operator was different from what Jinja provides. These cases make the behavior clear: {{ -5 | abs }}: the above example outputs 5, since filters should be applied *after* evaluating unary operators. e.g. `(abs (- 5))` {{ 2 * 3 | plus1 | plus1 }} the above example will output 10 as the above expression gets compiled to something like `(* 2 (plus1 (plus1 0)))`.
1 parent 8ce5146 commit 7e6034e

File tree

2 files changed

+21
-83
lines changed

2 files changed

+21
-83
lines changed

templatel-tests.el

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@
9090
;; --- Operator Precedence ---
9191

9292
(ert-deftest operator-precedence ()
93+
;; unary operators have higher precedence than filter operators
94+
(should (equal "5" (templatel-render-string "{{ -5 | abs }}" '())))
95+
;; pipes (for filters) have higher precedence then other binary ops
96+
(should (equal "10" (templatel-render-string "{{ 2 * 3 | plus1 | plus1 }}" '())))
97+
;; Arithmetic
9398
(should (equal "10" (templatel-render-string "{{ 2 * 3 + 4 }}" '())))
9499
(should (equal "10" (templatel-render-string "{{ 80 / 2 ** 3 }}" '())))
95100
(should (equal "5" (templatel-render-string "{{ 13 % 9 + 1 }}" '())))
@@ -449,12 +454,6 @@ base-end")))))
449454
(should (equal (templatel-render-string "{{ -a }}" '(("a" . -10))) "10"))
450455
(should (equal (templatel-render-string "{{ -a }}" '(("a" . 10))) "-10")))
451456

452-
(ert-deftest render-expr-all-bitlogic ()
453-
(should (equal (templatel-render-string "{{ a & 0b11 }}" '(("a" . 10))) "2"))
454-
(should (equal (templatel-render-string "{{ a || 0b1 }}" '(("a" . 10))) "11"))
455-
(should (equal (templatel-render-string "{{ a ^ 0b11 }}" '(("a" . 10))) "9"))
456-
(should (equal (templatel-render-string "{{ ~a }}" '(("a" . 10))) "-11")))
457-
458457
(ert-deftest render-expr-all-cmp ()
459458
(should (equal (templatel-render-string "{{ a == 10 }}" '(("a" . 10))) "t"))
460459
(should (equal (templatel-render-string "{{ a == 11 }}" '(("a" . 10))) ""))

templatel.el

Lines changed: 16 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -379,12 +379,6 @@
379379
(templatel--parser-_ scanner)
380380
(templatel--join-chars m)))
381381

382-
(defun templatel--token-|| (scanner)
383-
"Read '||' off SCANNER's input."
384-
(let ((m (templatel--scanner-matchs scanner "||")))
385-
(templatel--parser-_ scanner)
386-
(templatel--join-chars m)))
387-
388382
(defun templatel--token-+ (scanner)
389383
"Read '+' off SCANNER's input."
390384
(let ((m (templatel--scanner-matchs scanner "+")))
@@ -463,30 +457,6 @@
463457
(templatel--parser-_ scanner)
464458
(templatel--join-chars m)))
465459

466-
(defun templatel--token-<< (scanner)
467-
"Read '<<' off SCANNER's input."
468-
(let ((m (templatel--scanner-matchs scanner "<<")))
469-
(templatel--parser-_ scanner)
470-
(templatel--join-chars m)))
471-
472-
(defun templatel--token->> (scanner)
473-
"Read '>>' off SCANNER's input."
474-
(let ((m (templatel--scanner-matchs scanner ">>")))
475-
(templatel--parser-_ scanner)
476-
(templatel--join-chars m)))
477-
478-
(defun templatel--token-& (scanner)
479-
"Read '&' off SCANNER's input."
480-
(let ((m (templatel--scanner-matchs scanner "&")))
481-
(templatel--parser-_ scanner)
482-
(templatel--join-chars m)))
483-
484-
(defun templatel--token-~ (scanner)
485-
"Read '~' off SCANNER's input."
486-
(let ((m (templatel--scanner-matchs scanner "~")))
487-
(templatel--parser-_ scanner)
488-
(templatel--join-chars m)))
489-
490460
(defun templatel--token-% (scanner)
491461
"Read '%' off SCANNER's input."
492462
;; This is needed or allowing a cutting point to be introduced right
@@ -730,12 +700,12 @@
730700
"Unclosed bracket")
731701
(cons "Expression" (list expr))))
732702

733-
;; GR: Expr <- Filter
703+
;; GR: Expr <- Logical
734704
(defun templatel--parser-expr (scanner)
735705
"Read an expression from SCANNER."
736706
(cons
737707
"Expr"
738-
(list (templatel--parser-filter scanner))))
708+
(list (templatel--parser-logical scanner))))
739709

740710
(defun templatel--parser-cut (scanner fn msg)
741711
"Try to parse FN off SCANNER or error with MSG.
@@ -784,47 +754,27 @@ operator (RATORFN)."
784754
(lambda() (funcall randfn scanner))
785755
"Missing operand after binary operator"))))))
786756

787-
;; GR: Filter <- Logical (_PIPE Logical)*
788-
(defun templatel--parser-filter (scanner)
789-
"Read Filter from SCANNER."
790-
(templatel--parser-binary scanner "Filter" #'templatel--parser-logical #'templatel--token-|))
791-
792-
;; GR: Logical <- BitLogical ((AND / OR) BitLogical)*
757+
;; GR: Logical <- Comparison ((AND / OR) Comparison)*
793758
(defun templatel--parser-logical (scanner)
794759
"Read Logical from SCANNER."
795760
(templatel--parser-binary
796761
scanner
797762
nil ; "Logical"
798-
#'templatel--parser-bit-logical
763+
#'templatel--parser-comparison
799764
(lambda(s)
800765
(templatel--scanner-or
801766
s
802767
(list
803768
(lambda() (templatel--token-and s))
804769
(lambda() (templatel--token-or s)))))))
805770

806-
;; GR: BitLogical <- Comparison ((BAND / BXOR / BOR) Comparison)*
807-
(defun templatel--parser-bit-logical (scanner)
808-
"Read BitLogical from SCANNER."
809-
(templatel--parser-binary
810-
scanner
811-
nil ; "BitLogical"
812-
#'templatel--parser-comparison
813-
(lambda(s)
814-
(templatel--scanner-or
815-
s
816-
(list
817-
(lambda() (templatel--token-& s))
818-
(lambda() (templatel--token-^ s))
819-
(lambda() (templatel--token-|| s)))))))
820-
821-
;; GR: Comparison <- BitShifting ((EQ / NEQ / LTE / GTE / LT / GT / IN) BitShifting)*
771+
;; GR: Comparison <- Term ((EQ / NEQ / LTE / GTE / LT / GT / IN) Term)*
822772
(defun templatel--parser-comparison (scanner)
823773
"Read a Comparison from SCANNER."
824774
(templatel--parser-binary
825775
scanner
826776
nil ; "Comparison"
827-
#'templatel--parser-bit-shifting
777+
#'templatel--parser-term
828778
(lambda(s)
829779
(templatel--scanner-or
830780
s
@@ -837,20 +787,6 @@ operator (RATORFN)."
837787
(lambda() (templatel--token-> s))
838788
(lambda() (templatel--token-in s)))))))
839789

840-
;; GR: BitShifting <- Term ((RSHIFT / LSHIFT) Term)*
841-
(defun templatel--parser-bit-shifting (scanner)
842-
"Read a BitShifting from SCANNER."
843-
(templatel--parser-binary
844-
scanner
845-
nil ; "BitShifting"
846-
#'templatel--parser-term
847-
(lambda(s)
848-
(templatel--scanner-or
849-
s
850-
(list
851-
(lambda() (templatel--token->> s))
852-
(lambda() (templatel--token-<< s)))))))
853-
854790
;; GR: Term <- Factor ((PLUS / MINUS) Factor)*
855791
(defun templatel--parser-term (scanner)
856792
"Read Term from SCANNER."
@@ -880,20 +816,28 @@ operator (RATORFN)."
880816
(lambda() (templatel--token-slash s))
881817
(lambda() (templatel--token-dslash s)))))))
882818

883-
;; GR: Power <- Unary ((POWER / MOD) Unary)*
819+
;; GR: Power <- Filter ((POWER / MOD) Filter)*
884820
(defun templatel--parser-power (scanner)
885821
"Read Power from SCANNER."
886822
(templatel--parser-binary
887823
scanner
888824
nil ; "Power"
889-
#'templatel--parser-unary
825+
#'templatel--parser-filter
890826
(lambda(s)
891827
(templatel--scanner-or
892828
s
893829
(list
894830
(lambda() (templatel--token-** s))
895831
(lambda() (templatel--token-% s)))))))
896832

833+
;; GR: Filter <- Unary (_PIPE Unary)*
834+
(defun templatel--parser-filter (scanner)
835+
"Read Filter from SCANNER."
836+
(templatel--parser-binary
837+
scanner
838+
"Filter"
839+
#'templatel--parser-unary #'templatel--token-|))
840+
897841
;; GR: UnaryOp <- PLUS / MINUS / NOT / BNOT
898842
(defun templatel--parser-unary-op (scanner)
899843
"Read an Unary operator from SCANNER."
@@ -902,7 +846,6 @@ operator (RATORFN)."
902846
(list
903847
(lambda() (templatel--token-+ scanner))
904848
(lambda() (templatel--token-- scanner))
905-
(lambda() (templatel--token-~ scanner))
906849
(lambda() (templatel--token-not scanner)))))
907850

908851
;; GR: Unary <- UnaryOp Unary / UnaryOp Primary / Primary
@@ -1660,10 +1603,6 @@ Otherwise its HTML entities are escaped."
16601603
;; Logic
16611604
("and" and)
16621605
("or" or)
1663-
;; Bit Logic
1664-
("&" logand)
1665-
("||" logior)
1666-
("^" logxor)
16671606
;; Comparison
16681607
("<" <)
16691608
(">" >)

0 commit comments

Comments
 (0)