src/lexer-{proto,impl}.lisp, src/pset-parse.lisp: Other C operators.
authorMark Wooding <mdw@distorted.org.uk>
Tue, 20 Aug 2019 01:34:14 +0000 (02:34 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Tue, 20 Aug 2019 11:29:52 +0000 (12:29 +0100)
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
src/lexer-impl.lisp
src/lexer-proto.lisp
src/pset-parse.lisp

index fc34e4e..c50608c 100644 (file)
@@ -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>
index de76371..ccaca5c 100644 (file)
 
          ;; 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))
index a811298..a237a92 100644 (file)
                              (: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)
index cfd2b4a..e3c0d4b 100644 (file)
               ;; 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 ()
                        (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 #\)))