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