f232e9028759ad8bd27140d0605683260a6af96d
[mes.git] / module / nyacc / lang / c99 / cpp.scm
1 ;;; lang/c/cpp.scm
2 ;;;
3 ;;; Copyright (C) 2015 Matthew R. Wette
4 ;;;
5 ;;; This program is free software: you can redistribute it and/or modify
6 ;;; it under the terms of the GNU General Public License as published by 
7 ;;; the Free Software Foundation, either version 3 of the License, or 
8 ;;; (at your option) any later version.
9 ;;;
10 ;;; This program is distributed in the hope that it will be useful,
11 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of 
12 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 ;;; GNU General Public License for more details.
14 ;;;
15 ;;; You should have received a copy of the GNU General Public License
16 ;;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 ;; C preprocessor.  This is not complete.
19
20 (define-module (nyacc lang c99 cpp)
21   #:export (parse-cpp-stmt
22             read-cpp-stmt
23             parse-cpp-expr
24             eval-cpp-expr
25             cpp-expand-text
26             expand-cpp-mref
27             )
28   #:use-module (ice-9 syncase)
29   #:use-module (nyacc parse)
30   #:use-module (nyacc lex)
31   #:use-module (nyacc lang util)
32   #:use-module (rnrs arithmetic bitwise)
33   )
34
35 #|
36   #define  #undef  #include  #if  #ifdef  #ifndef  #else  #endif  #elif
37   #line  defined  #-operator  ##-operator  #pragma  #error
38
39 strategy:
40   don't expand macro calls -- treat like function calls, but provide dict
41 todo:
42   pragma
43   #-op ##-op
44   provide dict of #defines
45   provide util to expand defines
46 |#
47
48 ;;.@deffn skip-ws ch
49 ;; Helper for 
50 (define (skip-ws ch)
51   (if (eof-object? ch) ch
52       (if (char-set-contains? c:ws ch)
53           (skip-ws (read-char))
54           ch)))
55 ;; @deffn cpp-define => #f|???
56 (define (cpp-define)
57   ;; The (weak?) parse architecture is "unread la argument if no match"
58   (letrec
59       ((p-cppd ;; parse all
60         (lambda ()
61           (let* ((iden (read-c-ident (skip-ws (read-char))))
62                  ;;(args (or (p-args (skip-ws (read-char))) '()))
63                  ;; "define ABC(ARG)" not the same as "define ABC (ARG)"
64                  (args (or (p-args (read-char)) '()))
65                  (rest (or (p-rest (skip-ws (read-char))) " ")))
66             (if (pair? args)
67                 `(define (name ,iden) ,(cons 'args args) (repl ,rest))
68                 `(define (name ,iden) (repl ,rest))))))
69        (p-args ;; parse args
70         (lambda (la) ;; unread la if no match :(
71           (if (eq? la #\()
72               (let iter ((args '()) (la (skip-ws (read-char))))
73                 (cond
74                  ((eq? la #\)) (reverse args))
75                  ((read-c-ident la) =>
76                   (lambda (arg) (iter (cons arg args) (skip-ws (read-char)))))
77                  ((eq? la #\,)
78                   (iter args (skip-ws (read-char))))))
79               (begin (if (char? la) (unread-char la)) #f)))) ;; CLEANUP
80        (p-rest ;; parse rest
81         (lambda (la)
82           (cond ((char? la) (unread-char la) (drain-input (current-input-port)))
83                 (else #f)))))
84     (p-cppd)))
85
86 ;; @deffn cpp-include
87 ;; Parse CPP include statement.
88 (define (cpp-include)
89   (let* ((beg-ch (skip-ws (read-char)))
90          (end-ch (if (eq? beg-ch #\<) #\> #\"))
91          (path (let iter ((cl (list beg-ch)) (ch (read-char)))
92                  (if (eq? ch end-ch) (list->string (reverse (cons ch cl)))
93                      (iter (cons ch cl) (read-char))))))
94     `(include ,path)))
95
96 ;; @deffn read-cpp-stmt line defs => (stmt-type text)
97 ;; Parse a line from a CPP statement and return a parse tree.
98 ;; @example
99 ;; (parse-cpp-stmt "define X 123") => (define "X" "123")
100 ;; (parse-cpp-stmt "if defined(A) && defined(B) && defined(C)"
101 ;; => (if "defined(A) && defined(B) && defined(C)")
102 ;; @end example
103 ;; To evaluate the @code{if} statements use @code{parse-cpp-expr} and
104 ;; @code{eval-cpp-expr}.
105 (define (read-cpp-stmt line)
106   (define (rd-ident) (read-c-ident (skip-ws (read-char))))
107   (define (rd-num) (and=> (read-c-num (skip-ws (read-char))) cdr))
108   (define (rd-rest) (let ((ch (skip-ws (read-char))))
109                       (if (not (eof-object? ch)) (unread-char ch))
110                       (drain-input (current-input-port))))
111   (with-input-from-string line
112     (lambda ()
113       (let ((cmd (string->symbol (read-c-ident (skip-ws (read-char))))))
114          (case cmd
115            ((include) (cpp-include))
116            ((define) (cpp-define))
117            ((undef) `(undef ,(rd-ident)))
118            ((ifdef)
119             `(if ,(string-append "defined(" (rd-ident) ")" (rd-rest))))
120            ((ifndef)
121             `(if ,(string-append "!defined(" (rd-ident) ")" (rd-rest))))
122            ((if elif else endif line error pragma) (list cmd (rd-rest)))
123            (else '(unknown "")))))))
124     
125 (include-from-path "nyacc/lang/c99/mach.d/cpptab.scm")
126 (include-from-path "nyacc/lang/c99/mach.d/cppact.scm")
127
128 (define raw-parser
129   (make-lalr-parser
130    (list (cons 'len-v len-v) (cons 'pat-v pat-v) (cons 'rto-v rto-v)
131          (cons 'mtab mtab) (cons 'act-v act-v))))
132
133 ;; The included file "cppbody.scm" provides:
134 ;; gen-cpp-lexer
135 ;; parse-cpp-expr
136 ;; eval-cpp-expr
137 (include-from-path "nyacc/lang/c99/cppbody.scm")
138  
139 ;; --- last line ---