-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathdatalogRenderer.js
138 lines (125 loc) · 4.35 KB
/
datalogRenderer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
meh.declareModule('datalogRenderer', function(datalog, customElement, browser) {
// Helper methods
function makeSpan(/* child1, child2, ... */) {
var span = document.createElement('span')
for (var idx = 0; idx < arguments.length; idx++) {
var firstChar = arguments[idx].innerHTML[0].toLowerCase()
if (firstChar === '[' ||
firstChar === ']' ||
'a' <= firstChar && firstChar <= 'z' ||
'0' <= firstChar && firstChar <= '9')
span.appendChild(document.createTextNode(' '))
span.appendChild(arguments[idx])
}
return span
}
function delimWith(arr, cons) {
var ans = []
for (var idx = 0; idx < arr.length; idx++) {
if (idx > 0)
ans.push(cons())
ans.push(arr[idx])
}
return ans
}
// Elements for displaying tokens
customElement.newType('clauseName')
customElement.newType('var')
customElement.newType('value')
customElement.newType('aggregate').withInitializer(function(name) {
this.appendChild(customElement.create('dot', '.'))
this.appendChild(document.createTextNode(name))
})
customElement.newType('dot')
customElement.newType('beginList').withInitializer(function() {
this.appendChild(document.createTextNode('['))
})
customElement.newType('comma').withInitializer(function() {
this.appendChild(document.createTextNode(', '))
})
customElement.newType('endList').withInitializer(function() {
this.appendChild(document.createTextNode(']'))
})
customElement.newType('not').withInitializer(function() {
this.appendChild(document.createTextNode('~'))
})
customElement.newType('spaces')
customElement.newType('other')
customElement.newType('if').withInitializer(function() {
this.appendChild(document.createTextNode('if'))
})
customElement.newType('and').withInitializer(function() {
this.appendChild(document.createTextNode('and'))
})
// Helpers for rendering
function renderRule(rule) {
var parts = [renderClause(rule.head)]
for (var idx = 0; idx < rule.conditions.length; idx++) {
if (idx == 0)
parts.push(customElement.create('if'))
else
parts.push(customElement.create('and'))
parts.push(renderClause(rule.conditions[idx]))
}
var line = document.createElement('line')
line.appendChild(makeSpan.apply(this, parts))
return line
}
function renderClause(clause) {
if (clause instanceof datalog.Not) {
var not = clause
return makeSpan(customElement.create('not'), renderClause(not.clause))
} else {
var parts = []
var nameIdx = 0
var argIdx = 0
while (nameIdx < clause.name.length) {
if (clause.name[nameIdx] == '@') {
nameIdx++
parts.push(renderExpr(clause.args[argIdx++]))
} else {
var namePart = ''
while (nameIdx < clause.name.length && clause.name[nameIdx] != '@')
namePart += clause.name[nameIdx++]
parts.push(customElement.create('clauseName', namePart))
}
}
return makeSpan.apply(this, parts)
}
}
function renderExpr(expr) {
if (expr instanceof datalog.Variable)
return customElement.create('var', expr.name)
else if (expr instanceof datalog.Wildcard)
return customElement.create('var', '_')
else if (expr instanceof datalog.Aggregate)
return makeSpan(renderExpr(expr.term), customElement.create('aggregate', expr.name))
else if (expr instanceof datalog.Value)
return renderValue(expr)
else
browser.error('invalid expression', expr)
}
function renderValue(expr) {
var value = expr.value
if (value instanceof Array) {
var renderedElements = delimWith(
value.map(function(x) { return renderExpr(new datalog.Value(x)) }),
function() { return customElement.create('comma') }
)
renderedElements = makeSpan.apply(this, renderedElements)
return makeSpan(customElement.create('beginList'), renderedElements, customElement.create('endList'))
} else
return customElement.create('value', '' + value)
}
// Public methods
this.render = function(db) {
var ans = document.createElement('section')
for (var idx = 0; idx < db.rules.length; idx++) {
var rule = db.rules[idx]
if (idx > 0)
ans.appendChild(document.createElement('sep'))
ans.appendChild(renderRule(rule))
}
return ans
}
})