nyacc: Move (ice-9 syncase) into cond-expand.
[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 (nyacc parse)
29   #:use-module (nyacc lex)
30   #:use-module (nyacc lang util)
31   #:use-module (rnrs arithmetic bitwise)
32   )
33
34 (cond-expand
35  (guile-2)
36  (guile
37   (use-modules (ice-9 syncase)))
38  (mes))
39
40 #|
41   #define  #undef  #include  #if  #ifdef  #ifndef  #else  #endif  #elif
42   #line  defined  #-operator  ##-operator  #pragma  #error
43
44 strategy:
45   don't expand macro calls -- treat like function calls, but provide dict
46 todo:
47   pragma
48   #-op ##-op
49   provide dict of #defines
50   provide util to expand defines
51 |#
52
53 ;;.@deffn skip-ws ch
54 ;; Helper for 
55 (define (skip-ws ch)
56   (if (eof-object? ch) ch
57       (if (char-set-contains? c:ws ch)
58           (skip-ws (read-char))
59           ch)))
60 ;; @deffn cpp-define => #f|???
61 (define (cpp-define)
62   ;; The (weak?) parse architecture is "unread la argument if no match"
63   (letrec
64       ((p-cppd ;; parse all
65         (lambda ()
66           (let* ((iden (read-c-ident (skip-ws (read-char))))
67                  ;;(args (or (p-args (skip-ws (read-char))) '()))
68                  ;; "define ABC(ARG)" not the same as "define ABC (ARG)"
69                  (args (or (p-args (read-char)) '()))
70                  (rest (or (p-rest (skip-ws (read-char))) " ")))
71             (if (pair? args)
72                 `(define (name ,iden) ,(cons 'args args) (repl ,rest))
73                 `(define (name ,iden) (repl ,rest))))))
74        (p-args ;; parse args
75         (lambda (la) ;; unread la if no match :(
76           (if (eq? la #\()
77               (let iter ((args '()) (la (skip-ws (read-char))))
78                 (cond
79                  ((eq? la #\)) (reverse args))
80                  ((read-c-ident la) =>
81                   (lambda (arg) (iter (cons arg args) (skip-ws (read-char)))))
82                  ((eq? la #\,)
83                   (iter args (skip-ws (read-char))))))
84               (begin (if (char? la) (unread-char la)) #f)))) ;; CLEANUP
85        (p-rest ;; parse rest
86         (lambda (la)
87           (cond ((char? la) (unread-char la) (drain-input (current-input-port)))
88                 (else #f)))))
89     (p-cppd)))
90
91 ;; @deffn cpp-include
92 ;; Parse CPP include statement.
93 (define (cpp-include)
94   (let* ((beg-ch (skip-ws (read-char)))
95          (end-ch (if (eq? beg-ch #\<) #\> #\"))
96          (path (let iter ((cl (list beg-ch)) (ch (read-char)))
97                  (if (eq? ch end-ch) (list->string (reverse (cons ch cl)))
98                      (iter (cons ch cl) (read-char))))))
99     `(include ,path)))
100
101 ;; @deffn read-cpp-stmt line defs => (stmt-type text)
102 ;; Parse a line from a CPP statement and return a parse tree.
103 ;; @example
104 ;; (parse-cpp-stmt "define X 123") => (define "X" "123")
105 ;; (parse-cpp-stmt "if defined(A) && defined(B) && defined(C)"
106 ;; => (if "defined(A) && defined(B) && defined(C)")
107 ;; @end example
108 ;; To evaluate the @code{if} statements use @code{parse-cpp-expr} and
109 ;; @code{eval-cpp-expr}.
110 (define (read-cpp-stmt line)
111   (define (rd-ident) (read-c-ident (skip-ws (read-char))))
112   (define (rd-num) (and=> (read-c-num (skip-ws (read-char))) cdr))
113   (define (rd-rest) (let ((ch (skip-ws (read-char))))
114                       (if (not (eof-object? ch)) (unread-char ch))
115                       (drain-input (current-input-port))))
116   (with-input-from-string line
117     (lambda ()
118       (let ((cmd (string->symbol (read-c-ident (skip-ws (read-char))))))
119          (case cmd
120            ((include) (cpp-include))
121            ((define) (cpp-define))
122            ((undef) `(undef ,(rd-ident)))
123            ((ifdef)
124             `(if ,(string-append "defined(" (rd-ident) ")" (rd-rest))))
125            ((ifndef)
126             `(if ,(string-append "!defined(" (rd-ident) ")" (rd-rest))))
127            ((if elif else endif line error pragma) (list cmd (rd-rest)))
128            (else '(unknown "")))))))
129     
130 (include-from-path "nyacc/lang/c99/mach.d/cpptab.scm")
131 (include-from-path "nyacc/lang/c99/mach.d/cppact.scm")
132
133 (define raw-parser
134   (make-lalr-parser
135    (list (cons 'len-v len-v) (cons 'pat-v pat-v) (cons 'rto-v rto-v)
136          (cons 'mtab mtab) (cons 'act-v act-v))))
137
138 ;; The included file "cppbody.scm" provides:
139 ;; gen-cpp-lexer
140 ;; parse-cpp-expr
141 ;; eval-cpp-expr
142 (include-from-path "nyacc/lang/c99/cppbody.scm")
143  
144 ;; --- last line ---