1
2 /*
3
4 This is the grammar for Hootscript. This script also makes some transformations
5 to the grammar so that it contains predictions (instead of a separate prediction
6 table as you might expect).
7
8 This grammar is used by the Hoot parser to transform scripts into games.
9
10 */
11
12
13 function getGrammar(){
14
15 // the grammar
16 // =====================================================================
17
18
19 var grammar = {
20 "script": { rules: "+func", keep:true },
21 "func": { rules: "name colon +expr", keep:true },
22 "expr": { rules: "print | assign | ifseq | runme | incr | decr" },
23 "print": { rules: "qopen *qinnards qclose", keep:true },
24 "assign": { rules: "set name to num", keep:true },
25 "qinnards": { rules: "strname | upname | break | link | string" },
26 "ifseq": { rules: "if value test value then +expr *elseseq end", keep:true },
27 "elseseq": { rules: "else +expr", keep:true },
28 "test": { rules: "eq | lt | gt | de" },
29 "value": { rules: "name | num" },
30 "cond": { rules: "if value test value then +expr", keep:true },
31 "runme": { rules: "run name", keep:true },
32 "incr": { rules: "increase name", keep:true },
33 "decr": { rules: "decrease name", keep:true },
34 "colon": { lookahead: /^:/, extract: /^(:)\s*/ },
35 "name": { lookahead: /^\[/, extract: /^\[([^[_]+?)\]\s*/, keep:true },
36 "num": { lookahead: /^\d/, extract: /^(\d+)\s*/, keep:true },
37 "eq": { lookahead: /^e/, extract: /^(equals)\s*/, keep:true },
38 "lt": { lookahead: /^l/, extract: /^(less than)\s*/, keep:true },
39 "gt": { lookahead: /^g/, extract: /^(greater than)\s*/, keep:true },
40 "de": { lookahead: /^d/, extract: /^(doesn't equal)\s*/, keep:true },
41 "set": { lookahead: /^s/, extract: /^(set)\s*/ },
42 "to": { lookahead: /^t/, extract: /^(to)\s*/ },
43 "if": { lookahead: /^if/, extract: /^(if)\s*/ },
44 "then": { lookahead: /^t/, extract: /^(then)\s*/ },
45 "else": { lookahead: /^el/, extract: /^(else)\s*/ },
46 "end": { lookahead: /^en/, extract: /^(end)\s*/ },
47 "run": { lookahead: /^r/, extract: /^(run)\s*/ },
48 "increase": { lookahead: /^in/, extract: /^(increase)\s*/ },
49 "decrease": { lookahead: /^de/, extract: /^(decrease)\s*/ },
50 "qopen": { lookahead: /^\//, extract: /^(\/)/ },
51 "qclose": { lookahead: /^\//, extract: /^(\/)\s*/ },
52 "string": { lookahead: /^[^\/^[_]/, extract: /^((?:\\.|[^\/[_^])+)/, keep:true },
53 "link": { lookahead: /^_/, extract: /^_([^_]+?)_/, keep:true },
54 "break": { lookahead: /^__/, extract: /^(__)/, keep:true },
55 "strname": { lookahead: /^\[/, extract: /^\[([^[]+?)\]/, keep:true },
56 "upname": { lookahead: /^\^\[/, extract: /^\^\[(.+?)\]/, keep:true }
57 };
58
59
60
61
62 // massage the grammar
63 // =====================================================================
64
65 for(var g in grammar){
66 atom = grammar[g];
67 atom.name = g; // store its name inside itself
68 atom.operator = ""; // default
69
70 if(atom.rules){
71 // split alternative rules
72 atom.rules = atom.rules.split(" | ");
73 for(r in atom.rules){
74 // split rule into atoms
75 atom.rules[r] = atom.rules[r].split(" ");
76 for(x in atom.rules[r]){
77 // look for + and * operators on atoms
78 ratom = atom.rules[r][x];
79 firstchar = ratom[0];
80 if(firstchar == "+" || firstchar == "*"){
81 ratom = ratom.slice(1);
82 }
83 else{
84 firstchar = "";
85 }
86 atom.rules[r][x] = {
87 "name": ratom,
88 "operator":firstchar
89 }
90 }
91 }
92 }
93 }
94
95
96 // now go through again and make predictions
97 for(var g in grammar){
98 atom = grammar[g];
99
100 if(atom.rules){
101 atom.predictions = [];
102 for(r in atom.rules){
103 atom.predictions.push(getprediction(atom.rules[r][0].name));
104 }
105 }
106 }
107
108 function getprediction(atom){
109 if(!grammar[atom]){ alert("Bad Grammar! Couldn't find '"+atom+"'."); }
110 if(grammar[atom].rules){
111 return getprediction(grammar[atom].rules[0][0].name);
112 }
113 return grammar[atom].lookahead;
114 }
115
116
117
118
119 return grammar;
120 }