-
Notifications
You must be signed in to change notification settings - Fork 17
/
syntaxtree.js
121 lines (95 loc) · 2.84 KB
/
syntaxtree.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
// jsSyntaxTree - A syntax tree graph generator
// (c)2019 Andre Eisenbach <andre@ironcreek.net>
'use strict';
const VERSION = 'v1.2';
import Tree from './tree.js';
import rotateTip from './tip.js';
import * as Parser from './parser.js';
import * as Tokenizer from './tokenizer.js';
const tree = new Tree();
window.onload = () => {
registerServiceWorker();
e('version').innerHTML = VERSION;
tree.setCanvas(e('canvas'));
registerCallbacks();
const query = decodeURI(window.location.search).replace('?', '');
if (query != null && query.length > 2) e('code').value = query;
update();
rotateTip();
setInterval(rotateTip, 30 * 1000);
};
function e(id) {
return document.getElementById(id);
}
function registerServiceWorker() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('syntaxtree_worker.js').then(
(registration) => { console.info('Service worker registered.'); },
(error) => { console.warn('Unable to register service worker.'); }
);
} else {
console.info('Service workers not supported.');
}
}
function registerCallbacks() {
e('code').oninput = update;
e('font').onchange = () => {
tree.setFont(e('font').value);
update();
};
e('fontsize').onchange = () => {
tree.setFontsize(e('fontsize').value);
update();
};
e('triangles').onchange = () => {
tree.setTriangles(e('triangles').checked);
update();
};
e('nodecolor').onchange = () => {
tree.setColor(e('nodecolor').checked);
update();
};
e('autosub').onchange = () => {
tree.setSubscript(e('autosub').checked);
update();
};
e('align').onchange = () => {
tree.setAlignment(parseInt(e('align').value, 10));
update();
};
e('spacing').oninput = () => {
tree.setSpacing(parseFloat(e('spacing').value / 100));
update();
};
e('canvas').onclick = () => tree.download();
}
function update() {
const phrase = e('code').value;
e('parse-error').innerHTML = '';
try {
const tokens = Tokenizer.tokenize(phrase);
validateTokens(tokens);
const syntax_tree = Parser.parse(tokens);
tree.draw(syntax_tree);
} catch (err) {
e('parse-error').innerHTML = err;
}
}
function validateTokens(tokens) {
if (tokens.length < 3) throw 'Phrase too short';
if (tokens[0].type != Tokenizer.TokenType.BRACKET_OPEN ||
tokens[tokens.length - 1].type != Tokenizer.TokenType.BRACKET_CLOSE)
throw 'Phrase must start with [ and end with ]';
const brackets = countOpenBrackets(tokens);
if (brackets > 0) throw brackets + ' bracket(s) open [';
if (brackets < 0) throw Math.abs(brackets) + ' too many closed bracket(s) ]';
return null;
}
function countOpenBrackets(tokens) {
let o = 0;
for (const token of tokens) {
if (token.type == Tokenizer.TokenType.BRACKET_OPEN) ++o;
if (token.type == Tokenizer.TokenType.BRACKET_CLOSE) --o;
}
return o;
}