somewhat improved C++ lexing
authorD Herring <dherring@at.tentpost.dot.com>
Fri, 30 Mar 2012 06:21:26 +0000 (02:21 -0400)
committerD Herring <dherring@at.tentpost.dot.com>
Fri, 30 Mar 2012 06:21:26 +0000 (02:21 -0400)
examples/c++lex-phases.lisp
examples/extensible-parser-example.lisp

index 86f2a7c..5baf0f3 100644 (file)
@@ -9,10 +9,23 @@
 
 
 (defrule c++-lex-phase3
-  (repeat 0 nil (and (preprocessing-token) (repeat 0 nil (whitespace)))))
+  ;; need to detect "#" "include" and bind *enable-header* to t
+  ;; need to make sure whitespace exists between certain tokens?
+  (repeat 0 nil (or (preprocessing-token) (whitespace))))
 
 (c++-lex-phase3 "#define a
 //this is a 123456 test
 /* test */
 void f(int *x);
 ")
+
+(defun parse-file (filename)
+  (let (str
+        (*filename* filename))
+    (with-open-file (file filename)
+      (setf str (make-sequence 'string (file-length file)))
+      (read-sequence str file))
+    ;; eventually return the AST, the comment list, and the preproc list
+    ;; also return a list of newlines (so line/col can be quickly calculated)
+    ;; also return an indication if the parse didn't consume the whole file
+    (c++-lex-phase3 str)))
index 586260b..267a112 100644 (file)
@@ -3,7 +3,29 @@
 ;; developed with n3290.pdf
 ;; a `grep '2\.' $file` should show the overall structure and state of implementation
 
-#|;; translation of Boost's cpp.re|#
+(defstruct pp-token
+  type ;; :identifier, :whitespace, :literal, etc.
+  file ;; what file was it in
+  start ;; what was the first character index
+  end ;; what was the first index after the token
+  string ;; (subseq string start end)
+  value ;; an integer or other representation
+  )
+
+(defparameter *filename* nil
+  "filename to use when creating tokens")
+
+(defun create-token (type string start end &optional value)
+  (make-pp-token
+   :type type
+   :file *filename*
+   :start start
+   :end end
+   :string (subseq string start end)
+   :value value))
+
+
+#|;; for comparison, see Boost's cpp.re|#
 
 (defconstant slash-t #\Tab "C escape: \t; (code-char 9)")
 (defconstant slash-n #\Linefeed "C escape: \n; (code-char 10)")
 (defrule q-char-sequence
   (repeat 1 nil (q-char)))
 
+(defparameter *enable-header* nil
+  "Section 2.9, header name preprocessing shall only appear within #include...")
+
 (defrule header-name
-  (or (and #\< (h-char-sequence) #\>)
-      (and #\" (q-char-sequence) #\")))
+  (or
+   (:cl
+    (when *enable-header*
+      (match-filter (:context) (string start after end)
+          (and #\< (h-char-sequence) #\>)
+        (create-token :h-include string start after))))
+   (:cl
+    (when *enable-header*
+      (match-filter (:context) (string start after end)
+          (and #\" (q-char-sequence) #\")
+        (create-token :q-include string start after))))))
 
 
 ;; 2.10, lex.ppnumber
   (:cl
    (match-filter (:context) (string start after end)
        (and (identifier-nondigit) (repeat 0 nil (or (identifier-nondigit) (digit))))
-     (list :identifier (subseq string start after)))))
+     (create-token :identifier string start after))))
 ;; todo: identify tokens that are keywords or alternate names
+;; but this has to happen *after* the preprocessing (for ## splicing, etc.)
 
                                                 
 ;; 2.12, lex.key -- see below
   ;; sort in an order so longest matches first
   ;; recognize keywords like and_eq after normal tokenization
   ;; 57 other tokens remain
-  (or "{"
-      "}"
-      "<:"
-      ":>"
-      "["
-      "]"
-      "<%"
-      "%>"
-      "##"
-      "#"
-      "%:%:"
-      "%:"
-      #\(
-      #\)
-      #\;
-      #\:
-      "..."
-      "?"
-      "::"
-      ";"
-      ":"
-      ".*"
-      "."
-      "++"
-      "+="
-      "+"
-      "->*"
-      "->"
-      "*="
-      "*"
-      "/="
-      "/"
-      "--"
-      "-="
-      "-"
-      "<<="
-      "<<"
-      "<="
-      "<"
-      ">>="
-      ">>"
-      ">="
-      ">"
-      "|="
-      "||"
-      "|"
-      "&="
-      "&&"
-      "&"
-      "^="
-      "^"
-      "%="
-      "%"
-      "~"
-      "!="
-      "!"
-      "=="
-      "="
-      ","))
+  (exception
+   (or "{"
+       "}"
+       "<:"
+       ":>"
+       "["
+       "]"
+       "<%"
+       "%>"
+       "##"
+       "#"
+       "%:%:"
+       "%:"
+       #\(
+       #\)
+       #\;
+       #\:
+       "..."
+       "?"
+       "::"
+       ";"
+       ":"
+       ".*"
+       "."
+       "++"
+       "+="
+       "+"
+       "->*"
+       "->"
+       "*="
+       "*"
+       "/="
+       "/"
+       "--"
+       "-="
+       "-"
+       "<<="
+       "<<"
+       "<="
+       "<"
+       ">>="
+       ">>"
+       ">="
+       ">"
+       "|="
+       "||"
+       "|"
+       "&="
+       "&&"
+       "&"
+       "^="
+       "^"
+       "%="
+       "%"
+       "~"
+       "!="
+       "!"
+       "=="
+       "="
+       ",")
+   ;; avoid accidentally matching the start of a comment
+   (or "//" "/*")))
       
 
 ;; 2.14, lex.literal
 ;;      (and (optional (encoding-prefix)) #\R (raw-string))))
 
 (defrule string-literal
-  (and (optional (encoding-prefix)) #\" (optional (s-char-sequence)) #\"))
+  (and (optional (encoding-prefix))
+       ;;(and #\" (optional (s-char-sequence)) #\")))
+       (:cl
+        (match-filter (:context) (string start after end)
+            (and #\" (optional (s-char-sequence)) #\")
+          ;;(print (subseq string start after))
+          (create-token :string string start after)))))
+;;(subseq string (1+ start) (1- after))))))
 
 
 ;; 2.14.6, lex.bool -- TBD