-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathajon.max.js
316 lines (299 loc) · 10.1 KB
/
ajon.max.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
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
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
"use strict"
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement, fromIndex) {
var k;
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
var O = Object(this);
var len = O.length >>> 0;
if (len === 0) {
return -1;
}
var n = +fromIndex || 0;
if (Math.abs(n) === Infinity) {
n = 0;
}
if (n >= len) {
return -1;
}
k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
while (k < len) {
var kValue;
if (k in O && O[k] === searchElement) {
return k;
}
k++;
}
return -1;
};
}
var ajon = {
//Detailed type examination function...
_classof : function(ob) {
if (ob === undefined) return 'undefined';
if (ob === null) return 'null';
var ty = typeof ob;
if (ty === 'string' || ty === 'number' || ty === 'boolean') return ty;
function isArray(ob) { //Thx: Douglas Crockford
return typeof ob.length === 'number' &&
typeof ob.splice === 'function' &&
!(ob.propertyIsEnumerable('length'));
}
if (ty === 'object')
return isArray(ob) ? 'array' : 'object';
return 'bad';
},
//Per-type serialisation...
_stringifiers : {
'null' : function (ob,ex) { return "~"; },
'undefined' : function (ob,ex) { return "_"; },
'number' : function (ob,ex) { return "("+ob.toString()+")"; },
'boolean' : function (ob,ex) { return ob?"@":"!"; },
'string' : function (ob,ex) { return '<'+ajon._quote('>', ob)+'>'; },
'object' : function (ob,ex) { return "{"+ajon._serialise(ob,ex)+"}" },
'array' : function (ob,ex) { return "[("+ob.length+"):"+ajon._serialise(ob,ex)+"]"; },
'bad' : function (ob,ex) { return ""; },
},
stringify : function (ob, ex) { //ex is array of property names to be excluded
return ajon._stringifiers[ajon._classof(ob)](ob, ex);
},
//Escape terminal character and backslash...
_quote : function(char, str) {
//return str.replace(new RegExp('(['+char+'\\\\])', 'g'),function (a) { return '\\'+a; });
return str.replace(new RegExp('(['+char+'\\\\])', 'g'),'\\$1');
},
_unquote : function(char, str) {
return str.replace(new RegExp('\\\\(.)', 'g'),'$1');
},
//Recurse into objects and arrays...
_serialise : function(ob, ex) {
//The reason for this serialiser is to treat arrays just like other objects here.
var rets=[];
for (var i in ob)
if (ob.hasOwnProperty(i))
if (!ex || ex.indexOf(i)===-1)
rets.push( '<' + ajon._quote('>', i.toString()) + '>=' + ajon.stringify(ob[i], ex) + ';' );
rets.sort();
var ret = "";
for (var r in rets) if (rets.hasOwnProperty(r))
ret += rets[r];
return ret;
},
//That was stringifying, now for parsing...
//We do a top-down algorithm, which means:
// There are a bunch of getter functions, each of which:
// EITHER: Finds nothing relevant, makes no changes to the input stream and returns false,
// OR: Finds what it was written for, eats it, assigns its value to a passed-in reference and returns true.
// These rules must be followed faithfully otherwise things get very chaotic.
// More rules:
// There's a parsing context ("machine") telling us how far we got through the input stream ("cur") and
// what token we're looking at ("yytok").
// Input characters are examined one by one.
// cur always points to the next *unexamined* character, yytok classifies the previousy examined character
// and yyval provides details. We call chomp to consume the current token and load up the next one.
// In this very simple grammar, yyval is always equal to the last character read and yytok is the same
// in the case of special characters, or 'a' in the case of ordinary ones.
// Getter functions usually just examine yytok and return false if it's not for them.
// We have to start the whole parser by calling chomp.
// The point of returning boolean is that the getters can be combined as boolean expressions to express the grammar.
//For each string to be parsed, we'll make an object with this as its prototype...
machineProto : {
tokens : '~_!@<>/=()[]{};:\\',
//Naive chomp...
chomp_no_escape : function() {
this.yyval = this.input.substr(this.cur,1);
if (this.tokens.indexOf(this.yyval) !== -1) {
this.yytok = this.yyval
} else {
this.yytok = 'a';
}
this.cur++;
},
//With backslash-style escapes...
chomp : function() {
this.chomp_no_escape();
if (this.yytok!=='\\')
return;
this.chomp_no_escape();
this.yytok = 'a';
},
//If the current token equals the parameter, eat it...
getThisToken : function(tok) {
if (this.yytok !== tok)
return false;
this.chomp();
return true;
},
//Tried factoring this repetitive stuff but it caused more trouble than it was worth...
getUndefined : function(box) {
if (!this.getThisToken('_'))
return false;
box.val = undefined;
return true;
},
getNull : function(box) {
if (!this.getThisToken('~'))
return false;
box.val = null;
return true;
},
getTrue : function(box) {
if (!this.getThisToken('@'))
return false;
box.val = true;
return true;
},
getFalse : function(box) {
if (!this.getThisToken('!'))
return false;
box.val = false;
return true;
},
getBoolean : function(box) {
return this.getTrue(box) || this.getFalse(box);
},
//Get a string in customisable brackets, allowing the close-bracket to be escaped inside the string...
getInside : function(toko, tokc, box) {
//This could have been written more robustly in terms of chomp; this hack is faster though...
if (this.yytok !== toko)
return false;
if (this.input.length - this.cur < 1)
return false;
var found;
if (this.input.substr(this.cur, 1)===tokc)
found = 0;
else {
found = 1 + this.input.substr(this.cur).search(new RegExp("[^\\\\]\\"+tokc));
if (found === 0)
return false;
}
box.val = this.input.substr(this.cur, found);
this.cur += found+1;
this.chomp();
return true;
},
getNumber : function(box) {
var num = {};
if (!this.getInside('(', ')', num))
return false;
box.val = parseFloat(num.val);
return true;
},
getString : function(box) {
var s = {};
if (!this.getInside('<', '>', s))
return false;
box.val = ajon._unquote('>', s.val);
return true;
},
getObjectDelimiter : function(opening, box) {
if (!this.getThisToken(opening?'{':'}'))
return false;
if (opening)
box.val = {};
return true;
},
getArrayDelimiter : function(opening, box) {
if (!this.getThisToken(opening?'[':']'))
return false;
if (opening) {
var lenbox = {};
this.getNumber(lenbox);
box.val = new Array(lenbox.val); //OMG it actually came in useful!
this.getThisToken(':');
}
return true;
},
getBranchDelimiter : function(opening, box) {
return this.getObjectDelimiter(opening, box) || this.getArrayDelimiter(opening, box);
},
getBranch : function(box) {
var savecur = this.cur, savetok = this.tok;
if (!this.getBranchDelimiter(true, box))
return false;
var n = {}, v={};
while (this.getString(n) && this.getThisToken('=', {}) && this.getValue(v) && this.getThisToken(';', {}) )
box.val[n.val]=v.val;
if (!this.getBranchDelimiter(false, {})) {
//Without this precaution, we'd have broken the EITHER/OR rule above...
this.tok = savetok; this.cur = savecur;
return false;
}
return true;
},
getValue : function(box) {
return this.getUndefined(box) ||
this.getNull(box) ||
this.getNumber(box) ||
this.getString(box) ||
this.getBoolean(box) ||
this.getBranch(box) ;
}
},
init : function(str) {
var machine = Object.create(this.machineProto);
machine.input = str;
machine.cur = 0;
machine.chomp();
return machine;
},
parse : function (str) {
var res= {};
ajon.init(str).getValue(res);
return res.val;
}
}
module.exports = ajon;
//The real tests of ajon are those of the rhaboo library,
// that being what I wrote it for.
/*
var tortured = [0,1,2,3,4]
tortured[1] = undefined;
tortured[2] = null;
delete tortured[3];
tortured[8] = "foo";
tortured["bar"] = 123;
var tests = [
{ a: "b"},
{ 1 : "man", went: [2,"mow"] },
["the", ["quick", "brown"], { fox: "jumps", over: [2, "lazy", "dogs"] }],
123 ,
-321,
123.45,
-543.21,
"A String" ,
'A <very> \\ "complex" String',
false,
true,
undefined,
null,
tortured
]
function show(col) {
for (var i in col) if (col.hasOwnProperty(i))
console.log(col[i]);
console.log("========================");
}
var lins = [], reborns = [];
for (var t in tests) if (tests.hasOwnProperty(t)) {
var test = tests[t];
var lin = ajon.stringify(test);
var reborn = ajon.parse(lin);
lins.push(lin);
reborns.push(reborn);
}
show(tests);
show(lins);
show(reborns);
console.log(ajon.stringify( {
_rhaboo : "dont look at me",
foo: "bar"
}, ["_rhaboo"]));
*/
},{}],2:[function(require,module,exports){
(function (global){
global.Ajon = require('./ajon');
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./ajon":1}]},{},[2]);