1 ! Copyright (C) 2009 Doug Coleman.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors assocs c.lexer combinators
4 combinators.short-circuit io io.directories io.encodings.utf8
5 io.files io.pathnames io.streams.string kernel make math
6 sequences sequences.parser splitting unicode ;
9 : initial-library-paths ( -- seq )
10 V{ "/usr/include" } clone ;
12 : initial-symbol-table ( -- hashtable )
19 TUPLE: preprocessor-state library-paths symbol-table
20 include-nesting include-nesting-max processing-disabled?
21 ifdef-nesting warnings errors
26 : <preprocessor-state> ( -- preprocessor-state )
27 preprocessor-state new
28 initial-library-paths >>library-paths
29 initial-symbol-table >>symbol-table
31 200 >>include-nesting-max
36 V{ } clone >>include-nexts
41 DEFER: preprocess-file
43 ERROR: unknown-c-preprocessor sequence-parser name ;
45 ERROR: bad-include-line line ;
47 ERROR: header-file-missing path ;
49 :: read-standard-include ( preprocessor-state path -- )
50 preprocessor-state dup library-paths>>
51 [ path append-path file-exists? ] find nip
58 ! path header-file-missing
62 : read-local-include ( preprocessor-state path -- )
63 dup file-exists? [ preprocess-file ] [ 2drop ] if ;
65 : skip-whitespace/comments ( sequence-parser -- sequence-parser )
68 { [ dup take-c-comment ] [ skip-whitespace/comments ] }
69 { [ dup take-c++-comment ] [ skip-whitespace/comments ] }
73 : handle-include ( preprocessor-state sequence-parser -- )
74 skip-whitespace/comments advance dup previous {
75 { CHAR: < [ CHAR: > take-until-object read-standard-include ] }
76 { CHAR: \" [ CHAR: \" take-until-object read-local-include ] }
81 readln "\\" ?tail [ , ] dip [ (readlns) ] when ;
83 : readlns ( -- string ) [ (readlns) ] { } make concat ;
85 : take-define-identifier ( sequence-parser -- string )
86 skip-whitespace/comments
87 [ current { [ blank? ] [ CHAR: ( = ] } 1|| ] take-until ;
89 :: handle-define ( preprocessor-state sequence-parser -- )
90 sequence-parser take-define-identifier :> ident
91 sequence-parser skip-whitespace/comments take-rest :> def
92 def "\\" ?tail [ readlns append ] when :> def
93 def ident preprocessor-state symbol-table>> set-at ;
95 : handle-undef ( preprocessor-state sequence-parser -- )
96 take-token swap symbol-table>> delete-at ;
98 : handle-ifdef ( preprocessor-state sequence-parser -- )
99 [ [ 1 + ] change-ifdef-nesting ] dip
100 take-token over symbol-table>> key?
101 [ drop ] [ t >>processing-disabled? drop ] if ;
103 : handle-ifndef ( preprocessor-state sequence-parser -- )
104 [ [ 1 + ] change-ifdef-nesting ] dip
105 take-token over symbol-table>> key?
106 [ t >>processing-disabled? drop ]
109 : handle-endif ( preprocessor-state sequence-parser -- )
110 drop [ 1 - ] change-ifdef-nesting drop ;
112 : handle-if ( preprocessor-state sequence-parser -- )
113 [ [ 1 + ] change-ifdef-nesting ] dip
114 skip-whitespace/comments take-rest swap ifs>> push ;
116 : handle-elif ( preprocessor-state sequence-parser -- )
117 skip-whitespace/comments take-rest swap elifs>> push ;
119 : handle-else ( preprocessor-state sequence-parser -- )
120 skip-whitespace/comments take-rest swap elses>> push ;
122 : handle-pragma ( preprocessor-state sequence-parser -- )
123 skip-whitespace/comments take-rest swap pragmas>> push ;
125 : handle-include-next ( preprocessor-state sequence-parser -- )
126 skip-whitespace/comments take-rest swap include-nexts>> push ;
128 : handle-error ( preprocessor-state sequence-parser -- )
129 skip-whitespace/comments take-rest swap errors>> push ;
130 ! nip take-rest throw ;
132 : handle-warning ( preprocessor-state sequence-parser -- )
133 skip-whitespace/comments
134 take-rest swap warnings>> push ;
136 : parse-directive ( preprocessor-state sequence-parser string -- )
138 { "warning" [ handle-warning ] }
139 { "error" [ handle-error ] }
140 { "include" [ handle-include ] }
141 { "define" [ handle-define ] }
142 { "undef" [ handle-undef ] }
143 { "ifdef" [ handle-ifdef ] }
144 { "ifndef" [ handle-ifndef ] }
145 { "endif" [ handle-endif ] }
146 { "if" [ handle-if ] }
147 { "elif" [ handle-elif ] }
148 { "else" [ handle-else ] }
149 { "pragma" [ handle-pragma ] }
150 { "include_next" [ handle-include-next ] }
151 [ unknown-c-preprocessor ]
154 : parse-directive-line ( preprocessor-state sequence-parser -- )
155 advance dup take-token
156 pick processing-disabled?>> [
158 drop f >>processing-disabled?
159 [ 1 - ] change-ifdef-nesting
166 : preprocess-line ( preprocessor-state sequence-parser -- )
167 skip-whitespace/comments dup current CHAR: # =
168 [ parse-directive-line ]
169 [ swap processing-disabled?>> [ drop ] [ write-full nl ] if ] if ;
171 : preprocess-lines ( preprocessor-state -- )
173 [ <sequence-parser> [ preprocess-line ] [ drop preprocess-lines ] 2bi ]
176 ERROR: include-nested-too-deeply ;
178 : check-nesting ( preprocessor-state -- preprocessor-state )
179 [ 1 + ] change-include-nesting
180 dup [ include-nesting>> ] [ include-nesting-max>> ] bi > [
181 include-nested-too-deeply
184 : preprocess-file ( preprocessor-state path -- )
185 [ check-nesting ] dip
186 [ utf8 [ preprocess-lines ] with-file-reader ]
187 [ drop [ 1 - ] change-include-nesting drop ] 2bi ;
189 : start-preprocess-file ( path -- preprocessor-state string )
190 dup parent-directory [
192 [ <preprocessor-state> dup ] dip preprocess-file