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