-
Notifications
You must be signed in to change notification settings - Fork 3
/
reducer.html
316 lines (279 loc) · 9.69 KB
/
reducer.html
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Shift Reducer</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="favicon.ico">
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/bootstrap-responsive.min.css" rel="stylesheet">
<link href="css/ribbon.css" rel="stylesheet">
<link href="css/css.css" rel="stylesheet">
<link href="css/demo.css" rel="stylesheet">
<link href="//fonts.googleapis.com/css?family=Source+Code+Pro:400,700" rel="stylesheet" type="text/css"/>
<!-- highlight.js -->
<link href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.7/styles/magula.min.css" rel="stylesheet"/>
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.7/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<!-- ui-treeview web component -->
<script src="vendor/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>
<script type="module" src="vendor/ui-treeview/dist/index.js"></script>
<link href="css/ui-treeview.css" rel="stylesheet">
<style>
#demo0, #demo1 {
resize: vertical;
}
#demo0 {
height: 500px;
margin-top: 10px;
}
#reducing-container {
width: 50%;
}
#reducer-container {
width: 100%;
}
#reducing {
width: 100%;
}
#reducer {
width: 100%;
}
#output {
width: 100%;
background-color: #F8F8F8;
}
#demo1 .output-container {
width: 50%;
}
.thunked-radio-label {
font-size: medium;
}
.program-radio-label {
font-size: medium;
}
#program-radio {
margin-bottom: 0px;
}
#thunked-radio {
margin-bottom: -10px;
}
.error-message {
padding-left: 4.3em;
position: relative;
float: top;
}
.editor.error {
position: relative;
}
.editor.error .editor {
position: absolute;
bottom: 0;
height: 93%;
}
.editor.error .error-message {
display: block;
}
.hidden {
display: none;
width: 0%;
}
#save-button {
display: block;
margin-left: auto;
margin-right: auto;
}
.reducer-title {
margin: 10px 0px auto;
text-align: center;
font-size: 1.4em;
}
.double-title {
margin: 0 auto;
}
.reducing-title {
display: inline-block;
margin-bottom: 0px;
position: relative;
left: 20%;
transform: translate(-100%, 0%);
font-size: 1.4em;
}
.reduced-title {
display: inline-block;
margin-bottom: 0px;
position: relative;
left: 80%;
transform: translate(-100%, 0%);
font-size: 1.4em;
}
</style>
<script src="vendor/ace-builds/src-min/ace.js"></script>
<script src="js/shift-ast.js"></script>
<script src="js/shift-codegen.js"></script>
<script src="js/shift-parser.js"></script>
<script src="js/shift-reducer.js"></script>
<script>var MonoidalReducer = reducer.MonoidalReducer;</script>
</head>
<body>
<div class="ribbon-container right">
<div class="ribbon">
<a href="https://github.com/shapesecurity/shift-reducer-js" target="_blank">Fork me on GitHub</a>
</div>
</div>
<div class="container">
<div class="content">
<div class="masthead">
<h3><a href="./index.html" class="muted hub-link">part of the Shift suite</a></h3>
</div>
<div class="jumbotron">
<h1><span class="shift">Shift</span> Reducer</h1>
<p class="lead">
a framework for defining a bottom-up summarisation of a Shift AST
</p>
</div>
<hr />
<section id="introduction">
<h3>Introduction</h3>
<p>
The Shift Reducer (pun completely unintentional) is a framework for
definining instructions for summarising a Shift AST and executing those
instructions. This is similar to how
<code class="javascript">Array.prototype.reduce</code> summarises an
array using a function as its instructions. This very powerful
abstraction is used to create projects as diverse as the Shift Scope
Analyser, the Shift Validator, the Shift Early Error Checker (included
in the Shift Parser), the Shift Code Generator, and the simple demo
right on this page.
</p>
</section>
<section id="demo">
<h3>Demo</h3>
<p>
In this demo, a reducer is given as the default export of the program
on the left. It can be run against itself, after which the final
reduction state will be displayed as JSON on the right. ECMAScript
2015 syntax may be used.
</p>
<p>
The initially loaded reducer will collect all identifiers in
expression position into a list. Try changing
<code class="javascript">reduceIdentifierExpression</code> to
<code class="javascript">reduceBindingIdentifier</code>, or replace
<code class="javascript">node.name</code> with
<code class="javascript">node</code>.
</p>
<p class="reducer-title">
Reducer
</p>
<div class="demo" id="demo0">
<div id="reducer-container" class="editor">
<div spellcheck="false" class="editor" id="reducer">(function() {
let EMPTY;
class Collector {
constructor(x) { this.value = x; }
static empty() { return EMPTY; }
concat(a) { return new Collector(this.value.concat(a.value)); }
extract() { return this.value; }
}
EMPTY = new Collector([]);
return class IdentifierCollector extends MonoidalReducer {
constructor() {
super(Collector);
}
reduceModule(node, state) {
return super.reduceModule(node, state).extract();
}
reduceBindingIdentifier(node, state) {
return new Collector(['binding: ' + node.name]);
}
reduceIdentifierExpression(node, state) {
return new Collector(['reference: ' + node.name]);
}
}
})();
</div>
<div class="error-message"></div>
</div>
</div>
<div class="double-title">
<div class="reducing-title">
Input Program
<div id="program-radio" class="text-center">
<label class="radio inline program-radio-label">
<input type="radio" name="program-radio" id="script-radio" value="script"> Script
</label>
<label class="radio inline program-radio-label">
<input type="radio" name="program-radio" id="module-radio" value="module" checked> Module
</label>
</div>
</div>
<div class="reduced-title">Reducer Output</div>
</div>
<div class="demo" id="demo1">
<div id="reducing-container" class="editor">
<div spellcheck="false" class="editor" id="reducing">"use strict";
let [x,y,z] = [1,2,3].map(_ => _ * 2);
class Person {
constructor(name) {
this.name = name;
}
}
</div>
<div class="error-message"></div>
</div>
<div class="output-container">
<div class="editor hidden" spellcheck="false" id="output"></div>
<ui-treeview expanded class="output hidden"></ui-treeview>
</div>
<script src="js/reducer/main.js"></script>
</div>
</section>
<section id="usage">
<h3>Usage</h3>
<p>
A reducer needs to define a reduction method for each and every Shift
AST node type. The reduction methods tell the director how to combine
the state objects generated by the children of nodes of that type,
or, for leaf nodes, the value to use as the initial state.
</p>
<p>
Most of the time, we won't want to define a reduction method for
every Shift AST node type. Not only is that a lot of work, but in
these cases it would be very repetitive. When we can define an
associative combining operation and a special state object which can
be combined with any other state object to produce the other state
object, we can take advantage of the
<code class="javascript">MonoidalReducer</code>. This allows us to
define only the reduction methods that we care about.
</p>
<p>
Once we have a reducer, we simply pass an instance of it to the
director, the Shift Reducer default export, along with the Shift AST
on which we would like to run the reduction.
</p>
<pre><code class="javascript">import reduceUsing, {MonoidalReducer} from "shift-reducer";
import parse from "shift-parser";
class MyState {
static empty() { /* TODO: return the identity instance */ }
concat(otherState) { /* TODO: combine this and otherState */ }
}
class MyReducer extends MonoidalReducer {
constructor() {
super(MyState);
}
/* TODO: override any reduction methods we care about */
}
let program = parse("function hello(){ return world; }");
let finalState = reduceUsing(new MyReducer, program);</code></pre>
</section>
</div>
<hr />
<div class="footer">
<p>© Shape Security, Inc.</p>
</div>
</div>
</body>
</html>