OpenCores
URL https://opencores.org/ocsvn/c0or1k/c0or1k/trunk

Subversion Repositories c0or1k

[/] [c0or1k/] [trunk/] [tools/] [cml2-tools/] [cml.py] - Blame information for rev 2

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 drasko
"""
2
cml.py -- types for communication between CML2 compiler and configurators.
3
"""
4
import sys, os, time
5
 
6
version="2.3.0"
7
 
8
class trit:
9
    "A boolean or trit value"
10
    type = "trit"
11
    def __init__(self, value):
12
        if isinstance(value, trit):
13
            value = value.value
14
        self.value = value
15
    def __repr__(self):
16
        return "nmy"[self.value]
17
    def __nonzero__(self):
18
        return self.value
19
    def __hash__(self):
20
        return self.value       # This magic needed to make trits valid dictionary keys
21
    def __long__(self):
22
        return self.value != 0
23
    def __cmp__(self, other):
24
        if not isinstance(other, trit):
25
            if other is None:
26
                return 1               # any trit > None
27
            else:                       # Standard no-__cmp__ behavior=20
28
                if id(self) < id(other):
29
                    return -1
30
                elif id(self) > id(other):
31
                    return 1
32
                else:
33
                    return 0
34
        else:
35
            diff = self.value - other.value
36
            if diff == 0:
37
                return 0
38
            else:
39
                return diff / abs(diff)
40
    def __and__(self, other):
41
        return trit(min(self.value, other.value))
42
    def __or__(self, other):
43
        return trit(max(self.value, other.value))
44
    def eval(self):
45
        return self
46
 
47
# Trit value constants
48
y = trit(2)
49
m = trit(1)
50
n = trit(0)
51
 
52
# This describes a configuration symbol...
53
 
54
class ConfigSymbol:
55
    "Compiled information about a menu or configuration symbol"
56
    def __init__(self, name, type=None, default=None, prompt=None, file=None, lineno=None):
57
        # Name, location, type, default.
58
        self.name = name
59
        self.file = file        # Definition location source file
60
        self.lineno = lineno    # Definition location source line
61
        self.type = type        # Type of symbol
62
        self.range = None       # Range tuple
63
        self.enum = None
64
        self.discrete = None
65
        self.helptext = None    # Help reference
66
        self.default = default  # Value to use if none has been set.
67
        # Hierarchy location
68
        self.ancestors = []     # Ancestors of symbol (as set up by {})
69
        self.dependents = []    # Dependents of symbol (as set up by {})
70
        self.choicegroup = []   # Other symbols in a choicegroup.
71
        self.menu = None        # Unique parent menu of this symbol
72
        self.depth = 0           # Nesting depth in its subtree
73
        # Auxiliary information
74
        self.prompt = prompt    # Associated question string
75
        self.properties = {}    # Associated properties
76
        self.warnings = []      # Attached warndepend conditions
77
        self.visibility = None  # Visibility predicate for symbol 
78
        self.saveability = None # Saveability predicate for symbol
79
        self.items = []         # Menus only -- associated symbols
80
        # Compiler never touches these
81
        self.visits = 0          # Number of visits so far
82
        self.setcount = 0       # Should this symbol be written?
83
        self.included = 0       # Seen in an inclusion?
84
        self.inspected = 0      # Track menu inspections
85
        self.iced = 0            # Is this frozen?
86
 
87
    # Compute the value of a symbol 
88
    def eval(self, debug=0):
89
        "Value of symbol; passes back None if the symbol is unset."
90
        if self.default is not None:
91
            result = evaluate(self.default, debug)
92
            # Handle casting.  This can matter in derivations
93
            if self.type == "bool":
94
                if isinstance(result, trit):
95
                    result = trit(y.value * (result != n))
96
                elif type(result) == type(0):
97
                    result = trit(y.value * (result != 0))
98
            elif self.type in ("decimal", "hexadecimal"):
99
                if isinstance(result, trit):
100
                    result = (result != n)
101
            if debug > 3:
102
                sys.stderr.write("...eval(%s)->%s (through default %s)\n" % \
103
                                 (`self`, result, self.default))
104
            return result
105
        else:
106
            if debug > 2:
107
                sys.stderr.write("...eval(%s)->None (default empty)\n" % \
108
                                 (`self`))
109
            return None
110
 
111
    # Access to help.
112
    #
113
    # This is the only place in the front end that knows about the CML1
114
    # helpfile conventions.
115
    def help(self):
116
        "Is there help for the given symbol?"
117
        if self.helptext:
118
            return self.helptext
119
        # Next five lines implement the CML1 convention for choices help;
120
        # attach it to the first alternative.  But they check for help
121
        # attached to the symbol itself first.
122
        if self.menu and self.menu.type == "choices":
123
            self = self.menu
124
        if self.type == "choices" and not self.helptext:
125
            self = self.items[0]
126
        return self.helptext
127
 
128
    def ancestor_of(self, entry):
129
        "Test transitive completion of dependency."
130
        # We don't also check visibility, because visibility guards can have
131
        # disjunctions and it would be wrong to propagate up both branches.
132
        if entry.menu:
133
            searchpath = entry.ancestors + [entry.menu]
134
        else:
135
            searchpath = entry.ancestors
136
        if self in searchpath:
137
            return 1
138
        for x in searchpath:
139
            if self.ancestor_of(x):
140
                return 1
141
        return 0
142
 
143
    # Predicates
144
    def is_derived(self):
145
        "Is this a derived symbol?"
146
        return self.prompt is None
147
    def is_logical(self):
148
        "Is this a logical symbol?"
149
        return self.type in ("bool", "trit")
150
    def is_numeric(self):
151
        "Is this a numeric symbol?"
152
        return self.type in ("decimal", "hexadecimal")
153
    def is_symbol(self):
154
        "Is this a real symbol? (not a menu, not a choices, not a message)"
155
        return self.type in ("bool","trit", "decimal","hexadecimal", "string")
156
 
157
    # Property functions
158
    def hasprop(self, prop):
159
        return self.properties.has_key(prop)
160
    def setprop(self, prop, val=1):
161
        self.properties[prop] = val
162
    def delprop(self, prop):
163
        del self.properties[prop]
164
    def showprops(self,):
165
        return ", ".join(self.properties.keys())
166
 
167
    def __repr__(self):
168
        # So the right thing happens when we print symbols in expressions
169
        return self.name
170
    def dump(self):
171
        if self.prompt:
172
            res = "'%s'" % self.prompt
173
        else:
174
            res = "derived"
175
        res += ", type %s," % self.type
176
        if self.range:
177
            res = res + " range %s," % (self.range,)
178
        if self.menu:
179
            res = res + " in %s," % (self.menu.name,)
180
        if self.ancestors:
181
            res = res + " under %s," % (self.ancestors,)
182
        if self.dependents:
183
            res = res + " over %s," % (self.dependents,)
184
        if self.choicegroup:
185
            res = res + " choicegroup %s," % (self.choicegroup,)
186
        if self.visibility is not None:
187
            res = res + " visibility %s," % (display_expression(self.visibility),)
188
        if self.saveability is not None:
189
            res = res + " saveability %s," % (display_expression(self.saveability),)
190
        if self.default is not None:
191
            res = res + " default %s," % (`self.default`,)
192
        if self.items:
193
            res = res + " items %s," % (self.items,)
194
        if self.properties:
195
            res = res + " props=%s," % (self.showprops(),)
196
        if self.file and self.lineno is not None:
197
            res = res + " where=%s:%d," % (self.file, self.lineno)
198
        return res
199
    def __str__(self):
200
        # Note that requirements are not shown
201
        res = "%s={" % (self.name)
202
        res = res + self.dump()
203
        return res[:-1] + "}"
204
 
205
class Requirement:
206
    "A requirement, together with a message to be shown if it's violated."
207
    def __init__(self, wff, message, file, line):
208
        self.predicate = wff
209
        self.message = message
210
        self.file = file
211
        self.line = line
212
 
213
    def str(self):
214
        return display_expression(self.predicate)[1:-1]
215
 
216
    def __repr__(self):
217
        bindings = ""
218
        for sym in flatten_expr(self.predicate):
219
            bindings += "%s=%s, " % (sym.name, evaluate(sym))
220
        bindings = bindings[:-2]
221
        leader = '"%s", line %d: ' % (self.file, self.line)
222
        if self.message:
223
            return leader + self.message + " (" + bindings + ")"
224
        else:
225
            return leader + display_expression(self.predicate) + " (" + bindings + ")"
226
 
227
# This describes an entire configuration.
228
 
229
class CMLRulebase:
230
    "A dictionary of ConfigSymbols and a set of constraints."
231
    def __init__(self):
232
        self.version = version
233
        self.start = None               # Start menu name               
234
        self.dictionary = {}            # Configuration symbols
235
        self.prefix = ""                # Prepend this to all symbols
236
        self.banner = ""                # ID the configuration domain
237
        self.constraints = []           # All requirements
238
        self.icon = None                # Icon for this rulebase
239
        self.trit_tie = None            # Are trits enabled?
240
        self.help_tie = None            # Help required for visibility?
241
        self.expert_tie = None          # Expert flag for UI control
242
        self.reduced = []
243
    def __repr__(self):
244
        res = "Start menu = %s\n" % (self.start,)
245
        for k in self.dictionary.keys():
246
            res = res + str(self.dictionary[k]) + "\n"
247
        if self.prefix:
248
            res = res + "Prefix:" + `self.prefix`
249
        if self.banner:
250
            res = res + "Banner:" + `self.banner`
251
        return res
252
    def optimize_constraint_access(self):
253
        "Assign constraints to their associated symbols."
254
        for entry in self.dictionary.values():
255
            entry.constraints = []
256
        for requirement in self.reduced:
257
            for symbol in flatten_expr(requirement):
258
                if not requirement in symbol.constraints:
259
                    symbol.constraints.append(requirement)
260
 
261
# These functions are used by both interpreter and compiler
262
 
263
def evaluate(exp, debug=0):
264
    "Compute current value of an expression."
265
    def tritify(x):
266
        if x:
267
            return y
268
        else:
269
            return n
270
    if debug > 2:
271
        sys.stderr.write("evaluate(%s) begins...\n" % (`exp`,))
272
    if type(exp) is type(()):
273
        # Ternary operator
274
        if exp[0] == '?':
275
            guard = evaluate(exp[1], debug)
276
            if guard:
277
                return evaluate(exp[2], debug)
278
            else:
279
                return evaluate(exp[3], debug)
280
        # Logical operations -- always trit-valued
281
        elif exp[0] == 'not':
282
            return tritify(not evaluate(exp[1], debug))
283
        elif exp[0] == 'or':
284
            return tritify(evaluate(exp[1], debug) or evaluate(exp[2], debug))
285
        elif exp[0] == 'and':
286
            return tritify(evaluate(exp[1], debug) and evaluate(exp[2], debug))
287
        elif exp[0] == 'implies':
288
            return tritify(not ((evaluate(exp[1], debug) and not evaluate(exp[2], debug))))
289
        elif exp[0] == '==':
290
            return tritify(evaluate(exp[1], debug) == evaluate(exp[2], debug))
291
        elif exp[0] == '!=':
292
            return tritify(evaluate(exp[1], debug) != evaluate(exp[2], debug))
293
        elif exp[0] == '<=':
294
            return tritify(evaluate(exp[1], debug) <= evaluate(exp[2], debug))
295
        elif exp[0] == '>=':
296
            return tritify(evaluate(exp[1], debug) >= evaluate(exp[2], debug))
297
        elif exp[0] == '<':
298
            return tritify(evaluate(exp[1], debug) < evaluate(exp[2], debug))
299
        elif exp[0] == '>':
300
            return tritify(evaluate(exp[1], debug) > evaluate(exp[2], debug))
301
        # Arithmetic operations -- sometimes trit-valued
302
        elif exp[0] == '|':
303
            return evaluate(exp[1], debug) | evaluate(exp[2], debug)
304
        elif exp[0] == '&':
305
            return evaluate(exp[1], debug) & evaluate(exp[2], debug)
306
        elif exp[0] == '$':
307
            left = evaluate(exp[1])
308
            right = evaluate(exp[2])
309
            if left != right:
310
                return n
311
            else:
312
                return left
313
        elif exp[0] == '+':
314
            return long(evaluate(exp[1],debug)) + long(evaluate(exp[2],debug))
315
        elif exp[0] == '-':
316
            return long(evaluate(exp[1],debug)) - long(evaluate(exp[2],debug))
317
        elif exp[0] == '*':
318
            return long(evaluate(exp[1],debug)) * long(evaluate(exp[2],debug))
319
        else:
320
            raise SyntaxError, "Unknown operation %s in expression" % (exp[0],)
321
    elif isinstance(exp, trit) or type(exp) in (type(""), type(0), type(0L)):
322
        if debug > 2:
323
            sys.stderr.write("...evaluate(%s) returns itself\n" % (`exp`,))
324
        return exp
325
    elif isinstance(exp, ConfigSymbol):
326
        result = exp.eval(debug)
327
        if result:
328
            return result
329
        else:
330
            return n
331
    else:
332
        raise ValueError,"unknown object %s %s in expression" % (exp,type(exp))
333
 
334
def flatten_expr(node):
335
    "Flatten an expression -- skips the operators"
336
    if type(node) is type(()) or type(node) is type([]):
337
       sublists = map(flatten_expr, node)
338
       flattened = []
339
       for item in sublists:
340
           flattened = flattened + item
341
       return flattened
342
    elif isinstance(node, ConfigSymbol):
343
        if node.is_derived():
344
            return flatten_expr(node.default)
345
        else:
346
            return [node]
347
    else:
348
        return []
349
 
350
def display_expression(exp):
351
    "Display an expression in canonicalized infix form."
352
    if type(exp) is type(()):
353
        if exp[0] == "not":
354
            return "not " + display_expression(exp[1])
355
        elif exp[0] == '?':
356
            return "(%s ? %s : %s)" % (display_expression(exp[1]), display_expression(exp[2]), display_expression(exp[3]))
357
        else:
358
            return "(%s %s %s)" % (display_expression(exp[1]), exp[0], display_expression(exp[2]))
359
    elif isinstance(exp, ConfigSymbol):
360
        return exp.name
361
    else:
362
        return `exp`
363
 
364
class Baton:
365
    "Ship progress indication to stdout."
366
    def __init__(self, prompt, endmsg=None):
367
        if os.isatty(1):
368
            self.stream = sys.stdout
369
        elif os.isatty(2):
370
            self.stream = sys.stderr
371
        else:
372
            self.stream = None
373
        if self.stream:
374
            self.stream.write(prompt + "... \010")
375
            self.stream.flush()
376
        self.count = 0
377
        self.endmsg = endmsg
378
        self.time = time.time()
379
        return
380
 
381
    def twirl(self, ch=None):
382
        if self.stream is None:
383
            return
384
        if ch:
385
            self.stream.write(ch)
386
        else:
387
            self.stream.write("-/|\\"[self.count % 4])
388
            self.stream.write("\010")
389
        self.count = self.count + 1
390
        self.stream.flush()
391
        return
392
 
393
    def end(self, msg=None):
394
        if msg == None:
395
            msg = self.endmsg
396
        if self.stream:
397
            self.stream.write("...(%2.2f sec) %s.\n" % (time.time() - self.time, msg))
398
        return
399
 
400
if __name__ == "__main__":
401
    # Two classes without __cmp__
402
    class A:
403
        pass
404
 
405
    class B:
406
        pass
407
 
408
    a = A()
409
    b = B()
410
 
411
    t0 = trit(0)
412
    t1 = trit(1)
413
    t2 = trit(2)
414
 
415
    if not (t0 < t1 < t2 and t2 > t1 > t0) or t0 == t1 or t0 == t2 or t1 == t2:
416
        print "trit compare failed"
417
 
418
    if t0 < None:
419
        print "a trit is less than None?  Comparison failed"
420
 
421
    if None > t0:
422
        print "None is greater than a trit?  Comparison failed"
423
 
424
    if id(a) > id(b):
425
        if a < b > a:
426
            print "a/b comparison failed"
427
    elif b < a > b:
428
        print "a/b comparison failed"
429
 
430
 
431
    # Simulate standard no-cmp() behavior for non-trits
432
    if id(a) > id(t0):
433
        if a < t0:
434
            print "a/t0 comparison failed (id(a) greater)"
435
    elif t0 < a:
436
        print "a/t0 comparison failed"
437
 
438
# cml.py ends here.

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.