From fe471148e34552002cfb25d235f62fd557865dda Mon Sep 17 00:00:00 2001
From: Mark Wooding <mdw@distorted.org.uk>
Date: Tue, 20 Aug 2019 02:34:14 +0100
Subject: [PATCH] src/lexer-{proto,impl}.lisp, src/pset-parse.lisp: Other C
 operators.

Add almost all of the familiar C operators.  Some of these need their
own tokens added.  There's a lexical gotcha now, since `x << y' and
`x < < y ...' mean different things: the former parses as `x shift-
left-by y', while the latter parses as `x is-less-than some-type-
beginning-with-y'.
---
 doc/syntax.tex       | 34 +++++++++++++++++++++++++++++--
 src/lexer-impl.lisp  |  8 ++++++++
 src/lexer-proto.lisp |  8 ++++++++
 src/pset-parse.lisp  | 56 +++++++++++++++++++++++++++++++++++++++++++++++++---
 4 files changed, 101 insertions(+), 5 deletions(-)

diff --git a/doc/syntax.tex b/doc/syntax.tex
index fc34e4e..c50608c 100644
--- a/doc/syntax.tex
+++ b/doc/syntax.tex
@@ -130,7 +130,8 @@ binary.  However, length and signedness indicators are not permitted.
 \subsection{Punctuation} \label{sec:syntax.lex.punct}
 
 \begin{grammar}
-<punctuation> ::= "\dots"
+<punctuation> ::= "<<" | ">>" | "||" | "&&"
+  | "<=" | ">=" | "==" | "!=" | "\dots"
 \alt any nonalphanumeric character other than "_", "\"", or "'"
 \end{grammar}
 
@@ -367,7 +368,36 @@ keyword arguments.
 
 <property> ::= <identifier> "=" <expression>
 
-<expression> ::= <additive>
+<expression> ::= <logical-or>
+
+<logical-or> ::= <logical-and>
+  | <logical-or> "||" <logical-and>
+
+<logical-and> ::= <bitwise-or>
+  | <logical-and> "&&" <bitwise-or>
+
+<bitwise-or> ::= <bitwise-xor>
+  | <bitwise-or> "|" <bitwise-xor>
+
+<bitwise-xor> ::= <bitwise-and>
+  | <bitwise-xor> "^" <bitwise-and>
+
+<bitwise-and> ::= <equality>
+  | <bitwise-and> "&" <equality>
+
+<equality> ::= <ordering>
+  | <equality> "==" <ordering>
+  | <equality> "!=" <ordering>
+
+<ordering> ::= <shift>
+  | <ordering> "<" <shift>
+  | <ordering> "<=" <shift>
+  | <ordering> ">=" <shift>
+  | <ordering> ">" <shift>
+
+<shift> ::= <additive>
+  | <shift> "<<" <additive>
+  | <shift> ">>" <additive>
 
 <additive> ::= <term>
   | <additive> "+" <term>
diff --git a/src/lexer-impl.lisp b/src/lexer-impl.lisp
index de76371..ccaca5c 100644
--- a/src/lexer-impl.lisp
+++ b/src/lexer-impl.lisp
@@ -188,6 +188,14 @@
 
 	  ;; Some special punctuation sequences are single tokens.
 	  ("..." (values :ellipsis nil))
+	  ("==" (values :eq))
+	  ("!=" (values :ne))
+	  ("<=" (values :le))
+	  (">=" (values :ge))
+	  ("&&" (values :and))
+	  ("||" (values :or))
+	  ("<<" (values :shl))
+	  (">>" (values :shr))
 
 	  ;; Any other character is punctuation.
 	  (:any (values it nil))
diff --git a/src/lexer-proto.lisp b/src/lexer-proto.lisp
index a811298..a237a92 100644
--- a/src/lexer-proto.lisp
+++ b/src/lexer-proto.lisp
@@ -65,6 +65,14 @@
 			      (:char "<character-literal>")
 			      (:eof "<end-of-file>")
 			      (:ellipsis "`...'")
+			      (:shl "`<<'")
+			      (:shr "`>>'")
+			      (:eq "`=='")
+			      (:ne "`!='")
+			      (:le "`<='")
+			      (:ge "`>='")
+			      (:and "`&&'")
+			      (:or "`||'")
 			      (t (format nil "<? ~S~@[ ~S~]>" type value)))))
 		      (show-expected (thing)
 			(acond ((gethash thing *indicator-map*) it)
diff --git a/src/pset-parse.lisp b/src/pset-parse.lisp
index cfd2b4a..e3c0d4b 100644
--- a/src/pset-parse.lisp
+++ b/src/pset-parse.lisp
@@ -77,7 +77,25 @@
 	       ;; Evaluate X and Y to integers, and apply INTOP to the
 	       ;; results, giving another integer.
 	       (oplambda
-		 (values :int (funcall intop (want :int x) (want :int y))))))
+		 (values :int (funcall intop (want :int x) (want :int y)))))
+
+	     (compareop (intop strop x y)
+	       ;; Evaluate X and Y.  If they're integers, then apply INTOP to
+	       ;; them; if they're strings, apply STROP.  The result is a
+	       ;; boolean.
+	       (oplambda
+		 (multiple-value-bind (xty xval) (funcall x)
+		   (case xty
+		     (:int
+		      (values :boolean
+			      (funcall intop xval (want :int y))))
+		     ((:id :string :symbol)
+		      (values :boolean
+			      (funcall strop
+				       (coerce-property-value xval xty :id)
+				       (want :id y))))
+		     (t
+		      (error "Can't compare objects of type ~(~A~)" xty)))))))
 
       (with-parser-context (token-scanner-context :scanner scanner)
 	(when-parse ()
@@ -145,10 +163,42 @@
 			(when (zerop y) (error "Division by zero"))
 			(values :int (floor x y)))))
 	       (:op #\+ binop "+" (x y 60) (int-binop #'+ x y))
-	       (:op #\- binop "-" (x y 60) (int-binop #'- x y)))
+	       (:op #\- binop "-" (x y 60) (int-binop #'- x y))
+	       (:op :shl binop "<<" (x y 50) (int-binop #'ash x y))
+	       (:op :shr binop ">>" (x y 50)
+		    (int-binop (lambda (x y) (ash x (- y))) x y))
+	       (:op #\< binop "<" (x y 45)
+		    (compareop #'< #'string< x y))
+	       (:op :le binop "<=" (x y 45)
+		    (compareop #'<= #'string<= x y))
+	       (:op :ge binop ">=" (x y 45)
+		    (compareop #'>= #'string>= x y))
+	       (:op #\> binop ">" (x y 45)
+		    (compareop #'> #'string> x y))
+	       (:op :eq binop "==" (x y 40)
+		    (compareop #'= #'string= x y))
+	       (:op :ne binop "!=" (x y 40)
+		    (compareop #'/= #'string/= x y))
+	       (:op #\& binop "&" (x y 37) (int-binop #'logand x y))
+	       (:op #\^ binop "^" (x y 35) (int-binop #'logxor x y))
+	       (:op #\| binop "|" (x y 32) (int-binop #'logior x y))
+	       (:op :and binop "&&" (x y 27)
+		    (oplambda (if (want :boolean x) (funcall y)
+				  (values :boolean nil))))
+	       (:op :or binop "||" (x y 22)
+		    (oplambda
+		      (multiple-value-bind (xty xval) (funcall x)
+			(if (coerce-property-value xval xty :boolean)
+			    (values xty xval)
+			    (funcall y))))))
 
 	      ;; Prefix operators.
-	      ((:op #\+ preop "+" (x 90) (int-unop #'identity x))
+	      ((:op #\~ preop "~" (x 90) (int-unop #'lognot x))
+	       (:op #\! preop "!" (x 90)
+		    (oplambda
+		      (values :boolean
+			      (not (want :boolean (funcall x))))))
+	       (:op #\+ preop "+" (x 90) (int-unop #'identity x))
 	       (:op #\- preop "-" (x 90) (int-unop #'- x))
 	       (:op #\( lparen #\)))
 
-- 
2.11.0

