]>
git.proxmox.com Git - mirror_qemu.git/blob - scripts/minikconf.py
4 # Copyright (c) 2015 Red Hat Inc.
7 # Paolo Bonzini <pbonzini@redhat.com>
9 # This work is licensed under the terms of the GNU GPL, version 2
10 # or, at your option, any later version. See the COPYING file in
11 # the top-level directory.
13 from __future__
import print_function
17 __all__
= [ 'KconfigParserError', 'KconfigData', 'KconfigParser' ]
19 def debug_print(*args
):
20 #print('# ' + (' '.join(str(x) for x in args)))
23 # -------------------------------------------
24 # KconfigData implements the Kconfig semantics. For now it can only
25 # detect undefined symbols, i.e. symbols that were referenced in
26 # assignments or dependencies but were not declared with "config FOO".
28 # Semantic actions are represented by methods called do_*. The do_var
29 # method return the semantic value of a variable (which right now is
31 # -------------------------------------------
35 self
.previously_included
= []
37 self
.defined_vars
= set()
38 self
.referenced_vars
= set()
40 # semantic analysis -------------
42 def check_undefined(self
):
44 for i
in self
.referenced_vars
:
45 if not (i
in self
.defined_vars
):
46 print("undefined symbol %s" % (i
), file=sys
.stderr
)
50 # semantic actions -------------
52 def do_declaration(self
, var
):
53 if (var
in self
.defined_vars
):
54 raise Exception('variable "' + var
+ '" defined twice')
56 self
.defined_vars
.add(var
)
58 # var is a string with the variable's name.
60 # For now this just returns the variable's name itself.
61 def do_var(self
, var
):
62 self
.referenced_vars
.add(var
)
65 def do_assignment(self
, var
, val
):
68 def do_default(self
, var
, val
, cond
=None):
71 def do_depends_on(self
, var
, expr
):
74 def do_select(self
, var
, symbol
, cond
=None):
77 def do_imply(self
, var
, symbol
, cond
=None):
80 # -------------------------------------------
81 # KconfigParser implements a recursive descent parser for (simplified)
83 # -------------------------------------------
88 TOK_LPAREN
= 0; TOKENS
[TOK_LPAREN
] = '"("';
89 TOK_RPAREN
= 1; TOKENS
[TOK_RPAREN
] = '")"';
90 TOK_EQUAL
= 2; TOKENS
[TOK_EQUAL
] = '"="';
91 TOK_AND
= 3; TOKENS
[TOK_AND
] = '"&&"';
92 TOK_OR
= 4; TOKENS
[TOK_OR
] = '"||"';
93 TOK_NOT
= 5; TOKENS
[TOK_NOT
] = '"!"';
94 TOK_DEPENDS
= 6; TOKENS
[TOK_DEPENDS
] = '"depends"';
95 TOK_ON
= 7; TOKENS
[TOK_ON
] = '"on"';
96 TOK_SELECT
= 8; TOKENS
[TOK_SELECT
] = '"select"';
97 TOK_IMPLY
= 9; TOKENS
[TOK_IMPLY
] = '"imply"';
98 TOK_CONFIG
= 10; TOKENS
[TOK_CONFIG
] = '"config"';
99 TOK_DEFAULT
= 11; TOKENS
[TOK_DEFAULT
] = '"default"';
100 TOK_Y
= 12; TOKENS
[TOK_Y
] = '"y"';
101 TOK_N
= 13; TOKENS
[TOK_N
] = '"n"';
102 TOK_SOURCE
= 14; TOKENS
[TOK_SOURCE
] = '"source"';
103 TOK_BOOL
= 15; TOKENS
[TOK_BOOL
] = '"bool"';
104 TOK_IF
= 16; TOKENS
[TOK_IF
] = '"if"';
105 TOK_ID
= 17; TOKENS
[TOK_ID
] = 'identifier';
106 TOK_EOF
= 18; TOKENS
[TOK_EOF
] = 'end of file';
108 class KconfigParserError(Exception):
109 def __init__(self
, parser
, msg
, tok
=None):
110 self
.loc
= parser
.location()
111 tok
= tok
or parser
.tok
113 location
= TOKENS
.get(tok
, None) or ('"%s"' % tok
)
114 msg
= '%s before %s' % (msg
, location
)
118 return "%s: %s" % (self
.loc
, self
.msg
)
124 parser
= KconfigParser(data
)
125 parser
.parse_file(fp
)
126 if data
.check_undefined():
127 raise KconfigParserError(parser
, "there were undefined symbols")
131 def __init__(self
, data
):
134 def parse_file(self
, fp
):
135 self
.abs_fname
= os
.path
.abspath(fp
.name
)
137 self
.data
.previously_included
.append(self
.abs_fname
)
139 if self
.src
== '' or self
.src
[-1] != '\n':
147 # file management -----
149 def error_path(self
):
150 inf
= self
.data
.incl_info
153 res
= ("In file included from %s:%d:\n" % (inf
['file'],
160 for ch
in self
.src
[self
.line_pos
:self
.pos
]:
162 col
+= 8 - ((col
- 1) % 8)
165 return '%s%s:%d:%d' %(self
.error_path(), self
.fname
, self
.line
, col
)
167 def do_include(self
, include
):
168 incl_abs_fname
= os
.path
.join(os
.path
.dirname(self
.abs_fname
),
170 # catch inclusion cycle
171 inf
= self
.data
.incl_info
173 if incl_abs_fname
== os
.path
.abspath(inf
['file']):
174 raise KconfigParserError(self
, "Inclusion loop for %s"
178 # skip multiple include of the same file
179 if incl_abs_fname
in self
.data
.previously_included
:
182 fp
= open(incl_abs_fname
, 'r')
184 raise KconfigParserError(self
,
185 '%s: %s' % (e
.strerror
, include
))
187 inf
= self
.data
.incl_info
188 self
.data
.incl_info
= { 'file': self
.fname
, 'line': self
.line
,
190 KconfigParser(self
.data
).parse_file(fp
)
191 self
.data
.incl_info
= inf
193 # recursive descent parser -----
196 def parse_y_or_n(self
):
197 if self
.tok
== TOK_Y
:
200 if self
.tok
== TOK_N
:
203 raise KconfigParserError(self
, 'Expected "y" or "n"')
207 if self
.tok
== TOK_ID
:
210 return self
.data
.do_var(val
)
212 raise KconfigParserError(self
, 'Expected identifier')
214 # assignment_var: ID (starting with "CONFIG_")
215 def parse_assignment_var(self
):
216 if self
.tok
== TOK_ID
:
218 if not val
.startswith("CONFIG_"):
219 raise KconfigParserError(self
,
220 'Expected identifier starting with "CONFIG_"', TOK_NONE
)
222 return self
.data
.do_var(val
[7:])
224 raise KconfigParserError(self
, 'Expected identifier')
226 # assignment: var EQUAL y_or_n
227 def parse_assignment(self
):
228 var
= self
.parse_assignment_var()
229 if self
.tok
!= TOK_EQUAL
:
230 raise KconfigParserError(self
, 'Expected "="')
232 self
.data
.do_assignment(var
, self
.parse_y_or_n())
234 # primary: NOT primary
235 # | LPAREN expr RPAREN
237 def parse_primary(self
):
238 if self
.tok
== TOK_NOT
:
241 elif self
.tok
== TOK_LPAREN
:
244 if self
.tok
!= TOK_RPAREN
:
245 raise KconfigParserError(self
, 'Expected ")"')
247 elif self
.tok
== TOK_ID
:
250 raise KconfigParserError(self
, 'Expected "!" or "(" or identifier')
252 # disj: primary (OR primary)*
253 def parse_disj(self
):
255 while self
.tok
== TOK_OR
:
259 # expr: disj (AND disj)*
260 def parse_expr(self
):
262 while self
.tok
== TOK_AND
:
268 def parse_condition(self
):
269 if self
.tok
== TOK_IF
:
271 return self
.parse_expr()
275 # property: DEFAULT y_or_n condition
277 # | SELECT var condition
279 def parse_property(self
, var
):
280 if self
.tok
== TOK_DEFAULT
:
282 val
= self
.parse_y_or_n()
283 cond
= self
.parse_condition()
284 self
.data
.do_default(var
, val
, cond
)
285 elif self
.tok
== TOK_DEPENDS
:
287 if self
.tok
!= TOK_ON
:
288 raise KconfigParserError(self
, 'Expected "on"')
290 self
.data
.do_depends_on(var
, self
.parse_expr())
291 elif self
.tok
== TOK_SELECT
:
293 symbol
= self
.parse_var()
294 cond
= self
.parse_condition()
295 self
.data
.do_select(var
, symbol
, cond
)
296 elif self
.tok
== TOK_IMPLY
:
298 symbol
= self
.parse_var()
299 cond
= self
.parse_condition()
300 self
.data
.do_imply(var
, symbol
, cond
)
301 elif self
.tok
== TOK_BOOL
:
304 raise KconfigParserError(self
, 'Error in recursive descent?')
306 # properties: properties property
308 def parse_properties(self
, var
):
310 while self
.tok
== TOK_DEFAULT
or self
.tok
== TOK_DEPENDS
or \
311 self
.tok
== TOK_SELECT
or self
.tok
== TOK_BOOL
or \
312 self
.tok
== TOK_IMPLY
:
313 self
.parse_property(var
)
314 self
.data
.do_default(var
, False)
316 # for nicer error message
317 if self
.tok
!= TOK_SOURCE
and self
.tok
!= TOK_CONFIG
and \
318 self
.tok
!= TOK_ID
and self
.tok
!= TOK_EOF
:
319 raise KconfigParserError(self
, 'expected "source", "config", identifier, '
320 + '"default", "depends on", "imply" or "select"')
322 # declaration: config var properties
323 def parse_declaration(self
):
324 if self
.tok
== TOK_CONFIG
:
326 var
= self
.parse_var()
327 self
.data
.do_declaration(var
)
328 self
.parse_properties(var
)
330 raise KconfigParserError(self
, 'Error in recursive descent?')
335 def parse_clause(self
):
336 if self
.tok
== TOK_SOURCE
:
340 elif self
.tok
== TOK_CONFIG
:
341 self
.parse_declaration()
342 elif self
.tok
== TOK_ID
:
343 self
.parse_assignment()
345 raise KconfigParserError(self
, 'expected "source", "config" or identifier')
347 # config: clause+ EOF
348 def parse_config(self
):
349 while self
.tok
!= TOK_EOF
:
357 self
.tok
= self
.src
[self
.cursor
]
358 self
.pos
= self
.cursor
362 self
.tok
= self
.scan_token()
363 if self
.tok
is not None:
366 def check_keyword(self
, rest
):
367 if not self
.src
.startswith(rest
, self
.cursor
):
370 if self
.src
[self
.cursor
+ length
].isalnum() or self
.src
[self
.cursor
+ length
] == '|':
372 self
.cursor
+= length
375 def scan_token(self
):
377 self
.cursor
= self
.src
.find('\n', self
.cursor
)
379 elif self
.tok
== '=':
381 elif self
.tok
== '(':
383 elif self
.tok
== ')':
385 elif self
.tok
== '&' and self
.src
[self
.pos
+1] == '&':
388 elif self
.tok
== '|' and self
.src
[self
.pos
+1] == '|':
391 elif self
.tok
== '!':
393 elif self
.tok
== 'd' and self
.check_keyword("epends"):
395 elif self
.tok
== 'o' and self
.check_keyword("n"):
397 elif self
.tok
== 's' and self
.check_keyword("elect"):
399 elif self
.tok
== 'i' and self
.check_keyword("mply"):
401 elif self
.tok
== 'c' and self
.check_keyword("onfig"):
403 elif self
.tok
== 'd' and self
.check_keyword("efault"):
405 elif self
.tok
== 'b' and self
.check_keyword("ool"):
407 elif self
.tok
== 'i' and self
.check_keyword("f"):
409 elif self
.tok
== 'y' and self
.check_keyword(""):
411 elif self
.tok
== 'n' and self
.check_keyword(""):
413 elif (self
.tok
== 's' and self
.check_keyword("ource")) or \
414 self
.tok
== 'i' and self
.check_keyword("nclude"):
417 while self
.src
[self
.cursor
].isspace():
420 self
.cursor
= self
.src
.find('\n', self
.cursor
)
421 self
.val
= self
.src
[start
:self
.cursor
]
423 elif self
.tok
.isalpha():
425 while self
.src
[self
.cursor
].isalnum() or self
.src
[self
.cursor
] == '_':
427 self
.val
= self
.src
[self
.pos
:self
.cursor
]
429 elif self
.tok
== '\n':
430 if self
.cursor
== len(self
.src
):
433 self
.line_pos
= self
.cursor
434 elif not self
.tok
.isspace():
435 raise KconfigParserError(self
, 'invalid input')
439 if __name__
== '__main__':
440 fname
= len(sys
.argv
) > 1 and sys
.argv
[1] or 'Kconfig.test'
441 KconfigParser
.parse(open(fname
, 'r'))