yoob.js-erize the script and the page.
Chris Pressey
10 years ago
6 | 6 | |
7 | 7 | <h1>Matchbox</h1> |
8 | 8 | |
9 | <textarea id="prog1"> | |
10 | MOV M0, R0 | |
11 | INC R0 | |
12 | MOV R0, M0 | |
13 | </textarea> | |
14 | ||
15 | <textarea id="prog2"> | |
16 | MOV M0, R0 | |
17 | INC R0 | |
18 | INC R0 | |
19 | MOV R0, M0 | |
20 | </textarea> | |
21 | ||
22 | <button onclick="interleave()">Interleave</button> | |
23 | ||
24 | <pre id="output"> | |
25 | </pre> | |
9 | <div id="container"></div> | |
26 | 10 | |
27 | 11 | </body> |
28 | 12 | <script src="../src/matchbox.js"></script> |
29 | 13 | <script> |
30 | "use strict"; | |
31 | ||
32 | var output = document.getElementById('output'); | |
33 | ||
34 | var x = parseCode(document.getElementById('prog1').value); | |
35 | var y = parseCode(document.getElementById('prog2').value); | |
36 | ||
37 | var interleave = function() { | |
38 | output.innerHTML = ''; | |
39 | var interleavings = findAllInterleavings(x, y); | |
40 | for (var i = 0; i < interleavings.length; i++) { | |
41 | output.innerHTML += '[' + interleavings[i] + ']\n'; | |
42 | } | |
43 | }; | |
14 | launch('../src/yoob/', 'container', {}); | |
44 | 15 | </script> |
0 | 0 | "use strict"; |
1 | 1 | |
2 | /* | |
3 | * A lexical analyzer. | |
4 | * Create a new yoob.Scanner object, then call init, passing it an | |
5 | * array of two-element arrays; first element of each of these is the | |
6 | * type of token, the second element is a regular expression (in a | |
7 | * String) which matches that token at the start of the string. The | |
8 | * regular expression should have exactly one capturing group. | |
9 | * Then call reset, passing it the string to be scanned. | |
10 | * | |
11 | */ | |
12 | var Scanner = function() { | |
13 | this.text = undefined; | |
14 | this.token = undefined; | |
15 | this.type = undefined; | |
16 | this.error = undefined; | |
17 | this.table = undefined; | |
18 | this.whitespacePattern = "^[ \\t\\n\\r]*"; | |
19 | ||
20 | this.init = function(table) { | |
21 | this.table = table; | |
22 | return this; | |
23 | }; | |
24 | ||
25 | this.reset = function(text) { | |
26 | this.text = text; | |
27 | this.token = undefined; | |
28 | this.type = undefined; | |
29 | this.error = undefined; | |
30 | this.scan(); | |
31 | }; | |
32 | ||
33 | this.scanPattern = function(pattern, type) { | |
34 | var re = new RegExp(pattern); | |
35 | var match = re.exec(this.text); | |
36 | if (match === null) return false; | |
37 | this.type = type; | |
38 | this.token = match[1]; | |
39 | this.text = this.text.substr(match[0].length); | |
40 | //console.log(this.type, this.token); | |
41 | return true; | |
42 | }; | |
43 | ||
44 | this.scan = function() { | |
45 | this.scanPattern(this.whitespacePattern, "whitespace"); | |
46 | if (this.text.length === 0) { | |
47 | this.token = null; | |
48 | this.type = "EOF"; | |
49 | return; | |
50 | } | |
51 | for (var i = 0; i < this.table.length; i++) { | |
52 | var type = this.table[i][0]; | |
53 | var pattern = this.table[i][1]; | |
54 | if (this.scanPattern(pattern, type)) return; | |
55 | } | |
56 | if (this.scanPattern("^([\\s\\S])", "unknown character")) return; | |
57 | // should never get here | |
58 | }; | |
59 | ||
60 | this.expect = function(token) { | |
61 | if (this.token === token) { | |
62 | this.scan(); | |
63 | } else { | |
64 | this.error = "expected '" + token + "' but found '" + this.token + "'"; | |
65 | } | |
66 | }; | |
67 | ||
68 | this.on = function(token) { | |
69 | return this.token === token; | |
70 | }; | |
71 | ||
72 | this.onType = function(type) { | |
73 | return this.type === type; | |
74 | }; | |
75 | ||
76 | this.checkType = function(type) { | |
77 | if (this.type !== type) { | |
78 | this.error = "expected " + type + " but found " + this.type + " (" + this.token + ")" | |
79 | } | |
80 | }; | |
81 | ||
82 | this.expectType = function(type) { | |
83 | this.checkType(type); | |
84 | this.scan(); | |
85 | }; | |
86 | ||
87 | this.consume = function(token) { | |
88 | if (this.on(token)) { | |
89 | this.scan(); | |
90 | return true; | |
91 | } else { | |
92 | return false; | |
93 | } | |
94 | }; | |
95 | ||
96 | }; | |
97 | ||
98 | var matchboxScanner = (new Scanner()).init([ | |
99 | ['immediate', "^(\\d+)"], | |
100 | ['register', "^([rR]\\d+)"], | |
101 | ['memory', "^([mM]\\d+)"], | |
102 | ['opcode', "^([a-zA-Z]+)"], | |
103 | ['comma', "^(,)"] | |
104 | ]); | |
2 | function launch(prefix, container, config) { | |
3 | if (typeof container === 'string') { | |
4 | container = document.getElementById(container); | |
5 | } | |
6 | config = config || {}; | |
7 | var deps = [ | |
8 | "scanner.js", | |
9 | "element-factory.js" | |
10 | ]; | |
11 | var loaded = 0; | |
12 | for (var i = 0; i < deps.length; i++) { | |
13 | var elem = document.createElement('script'); | |
14 | elem.src = prefix + deps[i]; | |
15 | elem.onload = function() { | |
16 | if (++loaded < deps.length) return; | |
17 | ||
18 | var prog1ta = yoob.makeTextArea(container, 20, 10); | |
19 | var prog2ta = yoob.makeTextArea(container, 20, 10); | |
20 | ||
21 | prog1ta.value = "MOV M0, R0\nINC R0\nMOV R0, M0"; | |
22 | prog2ta.value = "MOV M0, R0\nINC R0\nMOV R0, M0"; | |
23 | ||
24 | var interleaveBtn = yoob.makeButton(container, "Interleave"); | |
25 | ||
26 | var output = yoob.makePre(container); | |
27 | ||
28 | initScanner(); | |
29 | ||
30 | interleaveBtn.onclick = function() { | |
31 | var x = parseCode(prog1ta.value); | |
32 | var y = parseCode(prog2ta.value); | |
33 | ||
34 | output.innerHTML = ''; | |
35 | var interleavings = findAllInterleavings(x, y); | |
36 | for (var i = 0; i < interleavings.length; i++) { | |
37 | output.innerHTML += '[' + interleavings[i] + ']\n'; | |
38 | } | |
39 | }; | |
40 | ||
41 | }; | |
42 | document.body.appendChild(elem); | |
43 | } | |
44 | } | |
45 | ||
46 | var matchboxScanner; | |
47 | ||
48 | function initScanner() { | |
49 | matchboxScanner = (new yoob.Scanner()); | |
50 | matchboxScanner.init([ | |
51 | ['immediate', "^(\\d+)"], | |
52 | ['register', "^([rR]\\d+)"], | |
53 | ['memory', "^([mM]\\d+)"], | |
54 | ['opcode', "^([a-zA-Z]+)"], | |
55 | ['comma', "^(,)"] | |
56 | ]); | |
57 | } | |
105 | 58 | |
106 | 59 | /* |
107 | 60 | * Each instruction is an object with some fields: |
0 | /* | |
1 | * This file is part of yoob.js version 0.8 | |
2 | * Available from https://github.com/catseye/yoob.js/ | |
3 | * This file is in the public domain. See http://unlicense.org/ for details. | |
4 | */ | |
5 | if (window.yoob === undefined) yoob = {}; | |
6 | ||
7 | /* | |
8 | * Functions for creating elements. | |
9 | */ | |
10 | ||
11 | yoob.makeCanvas = function(container, width, height) { | |
12 | var canvas = document.createElement('canvas'); | |
13 | if (width) { | |
14 | canvas.width = width; | |
15 | } | |
16 | if (height) { | |
17 | canvas.height = height; | |
18 | } | |
19 | container.appendChild(canvas); | |
20 | return canvas; | |
21 | }; | |
22 | ||
23 | yoob.makeButton = function(container, labelText, fun) { | |
24 | var button = document.createElement('button'); | |
25 | button.innerHTML = labelText; | |
26 | container.appendChild(button); | |
27 | if (fun) { | |
28 | button.onclick = fun; | |
29 | } | |
30 | return button; | |
31 | }; | |
32 | ||
33 | yoob.checkBoxNumber = 0; | |
34 | yoob.makeCheckbox = function(container, checked, labelText, fun) { | |
35 | var checkbox = document.createElement('input'); | |
36 | checkbox.type = "checkbox"; | |
37 | checkbox.id = 'cfzzzb_' + yoob.checkBoxNumber; | |
38 | checkbox.checked = checked; | |
39 | var label = document.createElement('label'); | |
40 | label.htmlFor = 'cfzzzb_' + yoob.checkBoxNumber; | |
41 | yoob.checkBoxNumber += 1; | |
42 | label.appendChild(document.createTextNode(labelText)); | |
43 | ||
44 | container.appendChild(checkbox); | |
45 | container.appendChild(label); | |
46 | ||
47 | if (fun) { | |
48 | checkbox.onchange = function(e) { | |
49 | fun(checkbox.checked); | |
50 | }; | |
51 | } | |
52 | return checkbox; | |
53 | }; | |
54 | ||
55 | yoob.makeTextInput = function(container, size, value) { | |
56 | var input = document.createElement('input'); | |
57 | input.size = "" + (size || 12); | |
58 | input.value = value || ""; | |
59 | container.appendChild(input); | |
60 | return input; | |
61 | }; | |
62 | ||
63 | yoob.makeSlider = function(container, min, max, value, fun) { | |
64 | var slider = document.createElement('input'); | |
65 | slider.type = "range"; | |
66 | slider.min = min; | |
67 | slider.max = max; | |
68 | slider.value = value || 0; | |
69 | if (fun) { | |
70 | slider.onchange = function(e) { | |
71 | fun(parseInt(slider.value, 10)); | |
72 | }; | |
73 | } | |
74 | container.appendChild(slider); | |
75 | return slider; | |
76 | }; | |
77 | ||
78 | yoob.makeParagraph = function(container, innerHTML) { | |
79 | var p = document.createElement('p'); | |
80 | p.innerHTML = innerHTML || ''; | |
81 | container.appendChild(p); | |
82 | return p; | |
83 | }; | |
84 | ||
85 | yoob.makeSpan = function(container, innerHTML) { | |
86 | var span = document.createElement('span'); | |
87 | span.innerHTML = innerHTML || ''; | |
88 | container.appendChild(span); | |
89 | return span; | |
90 | }; | |
91 | ||
92 | yoob.makeDiv = function(container, innerHTML) { | |
93 | var div = document.createElement('div'); | |
94 | div.innerHTML = innerHTML || ''; | |
95 | container.appendChild(div); | |
96 | return div; | |
97 | }; | |
98 | ||
99 | yoob.makePre = function(container, innerHTML) { | |
100 | var elem = document.createElement('pre'); | |
101 | elem.innerHTML = innerHTML || ''; | |
102 | container.appendChild(elem); | |
103 | return elem; | |
104 | }; | |
105 | ||
106 | yoob.makePanel = function(container, title, isOpen) { | |
107 | isOpen = !!isOpen; | |
108 | var panelContainer = document.createElement('div'); | |
109 | var button = document.createElement('button'); | |
110 | var innerContainer = document.createElement('div'); | |
111 | innerContainer.style.display = isOpen ? "block" : "none"; | |
112 | ||
113 | button.innerHTML = (isOpen ? "∇" : "⊳") + " " + title; | |
114 | button.onclick = function(e) { | |
115 | isOpen = !isOpen; | |
116 | button.innerHTML = (isOpen ? "∇" : "⊳") + " " + title; | |
117 | innerContainer.style.display = isOpen ? "block" : "none"; | |
118 | }; | |
119 | ||
120 | panelContainer.appendChild(button); | |
121 | panelContainer.appendChild(innerContainer); | |
122 | container.appendChild(panelContainer); | |
123 | return innerContainer; | |
124 | }; | |
125 | ||
126 | yoob.makeTextArea = function(container, cols, rows, initial) { | |
127 | var textarea = document.createElement('textarea'); | |
128 | textarea.rows = "" + rows; | |
129 | textarea.cols = "" + cols; | |
130 | if (initial) { | |
131 | container.value = initial; | |
132 | } | |
133 | container.appendChild(textarea); | |
134 | return textarea; | |
135 | }; | |
136 | ||
137 | yoob.makeLineBreak = function(container) { | |
138 | var br = document.createElement('br'); | |
139 | container.appendChild(br); | |
140 | return br; | |
141 | }; | |
142 | ||
143 | yoob.makeSelect = function(container, labelText, optionsArray) { | |
144 | var label = document.createElement('label'); | |
145 | label.innerHTML = labelText; | |
146 | container.appendChild(label); | |
147 | ||
148 | var select = document.createElement("select"); | |
149 | ||
150 | for (var i = 0; i < optionsArray.length; i++) { | |
151 | var op = document.createElement("option"); | |
152 | op.value = optionsArray[i][0]; | |
153 | op.text = optionsArray[i][1]; | |
154 | if (optionsArray[i].length > 2) { | |
155 | op.selected = optionsArray[i][2]; | |
156 | } else { | |
157 | op.selected = false; | |
158 | } | |
159 | select.options.add(op); | |
160 | } | |
161 | ||
162 | container.appendChild(select); | |
163 | return select; | |
164 | }; | |
165 | ||
166 | SliderPlusTextInput = function() { | |
167 | this.init = function(cfg) { | |
168 | this.slider = cfg.slider; | |
169 | this.textInput = cfg.textInput; | |
170 | this.callback = cfg.callback; | |
171 | return this; | |
172 | }; | |
173 | ||
174 | this.set = function(value) { | |
175 | this.slider.value = "" + value; | |
176 | this.textInput.value = "" + value; | |
177 | this.callback(value); | |
178 | }; | |
179 | }; | |
180 | ||
181 | yoob.makeSliderPlusTextInput = function(container, label, min_, max_, size, value, fun) { | |
182 | yoob.makeSpan(container, label); | |
183 | var slider = yoob.makeSlider(container, min_, max_, value); | |
184 | var s = "" + value; | |
185 | var textInput = yoob.makeTextInput(container, size, s); | |
186 | slider.onchange = function(e) { | |
187 | textInput.value = slider.value; | |
188 | fun(parseInt(slider.value, 10)); | |
189 | }; | |
190 | textInput.onchange = function(e) { | |
191 | var v = parseInt(textInput.value, 10); | |
192 | if (v !== NaN) { | |
193 | slider.value = "" + v; | |
194 | fun(v); | |
195 | } | |
196 | }; | |
197 | return new SliderPlusTextInput().init({ | |
198 | 'slider': slider, | |
199 | 'textInput': textInput, | |
200 | 'callback': fun | |
201 | }); | |
202 | }; |
0 | /* | |
1 | * This file is part of yoob.js version 0.3 | |
2 | * Available from https://github.com/catseye/yoob.js/ | |
3 | * This file is in the public domain. See http://unlicense.org/ for details. | |
4 | */ | |
5 | if (window.yoob === undefined) yoob = {}; | |
6 | ||
7 | /* | |
8 | * A lexical analyzer. | |
9 | * Create a new yoob.Scanner object, then call init, passing it an | |
10 | * array of two-element arrays; first element of each of these is the | |
11 | * type of token, the second element is a regular expression (in a | |
12 | * String) which matches that token at the start of the string. The | |
13 | * regular expression should have exactly one capturing group. | |
14 | * Then call reset, passing it the string to be scanned. | |
15 | * | |
16 | */ | |
17 | yoob.Scanner = function() { | |
18 | this.text = undefined; | |
19 | this.token = undefined; | |
20 | this.type = undefined; | |
21 | this.error = undefined; | |
22 | this.table = undefined; | |
23 | this.whitespacePattern = "^[ \\t\\n\\r]*"; | |
24 | ||
25 | this.init = function(table) { | |
26 | this.table = table; | |
27 | }; | |
28 | ||
29 | this.reset = function(text) { | |
30 | this.text = text; | |
31 | this.token = undefined; | |
32 | this.type = undefined; | |
33 | this.error = undefined; | |
34 | this.scan(); | |
35 | }; | |
36 | ||
37 | this.scanPattern = function(pattern, type) { | |
38 | var re = new RegExp(pattern); | |
39 | var match = re.exec(this.text); | |
40 | if (match === null) return false; | |
41 | this.type = type; | |
42 | this.token = match[1]; | |
43 | this.text = this.text.substr(match[0].length); | |
44 | return true; | |
45 | }; | |
46 | ||
47 | this.scan = function() { | |
48 | this.scanPattern(this.whitespacePattern, "whitespace"); | |
49 | if (this.text.length === 0) { | |
50 | this.token = null; | |
51 | this.type = "EOF"; | |
52 | return; | |
53 | } | |
54 | for (var i = 0; i < this.table.length; i++) { | |
55 | var type = this.table[i][0]; | |
56 | var pattern = this.table[i][1]; | |
57 | if (this.scanPattern(pattern, type)) return; | |
58 | } | |
59 | if (this.scanPattern("^([\\s\\S])", "unknown character")) return; | |
60 | // should never get here | |
61 | }; | |
62 | ||
63 | this.expect = function(token) { | |
64 | if (this.token === token) { | |
65 | this.scan(); | |
66 | } else { | |
67 | this.error = "expected '" + token + "' but found '" + this.token + "'"; | |
68 | } | |
69 | }; | |
70 | ||
71 | this.on = function(token) { | |
72 | return this.token === token; | |
73 | }; | |
74 | ||
75 | this.onType = function(type) { | |
76 | return this.type === type; | |
77 | }; | |
78 | ||
79 | this.checkType = function(type) { | |
80 | if (this.type !== type) { | |
81 | this.error = "expected " + type + " but found " + this.type + " (" + this.token + ")" | |
82 | } | |
83 | }; | |
84 | ||
85 | this.expectType = function(type) { | |
86 | this.checkType(type); | |
87 | this.scan(); | |
88 | }; | |
89 | ||
90 | this.consume = function(token) { | |
91 | if (this.on(token)) { | |
92 | this.scan(); | |
93 | return true; | |
94 | } else { | |
95 | return false; | |
96 | } | |
97 | }; | |
98 | ||
99 | }; |