Skip to content

Commit e8a7e40

Browse files
author
Anton Kotenko
committed
initial state
0 parents  commit e8a7e40

File tree

4 files changed

+417
-0
lines changed

4 files changed

+417
-0
lines changed

data

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"firstName": "Yurii",
3+
"lastName": "Gorbylov",
4+
"contactInfo": {
5+
"email": "yuriygorbylov@gmail.com",
6+
"phoneNumber": "+38(063)195-51-31"
7+
},
8+
"skills": [
9+
"java8",
10+
"scala"
11+
]
12+
}

fakejq.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
var MYJSON = require('./json.js');
2+
var data = Buffer(0);
3+
4+
process.stdin.on('data', function (chunk) {
5+
data = Buffer.concat([data, chunk]);
6+
});
7+
process.stdin.on('end', function () {
8+
var obj;
9+
try {
10+
obj = MYJSON.parse(data.toString('utf8'));
11+
} catch (e) {
12+
console.error("bad input", e);
13+
process.exit();
14+
}
15+
var path = (process.argv[2] || '').replace(/\.+/g, '.').replace(/^\./, '');
16+
if (path) {
17+
console.log(path.split('.').reduce(function (value, key) {
18+
if (value[key] === undefined) {
19+
console.error("there are no such key" );
20+
process.exit();
21+
}
22+
return value[key];
23+
}, obj));
24+
} else {
25+
console.log(obj);
26+
}
27+
});
28+
process.stdin.resume();

json.js

+298
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
var trace = function (parser, msg) {
2+
console.log(msg, parser._string, parser._position);
3+
};
4+
var attachTraser = function (cls) {
5+
var tracedCls = function () {
6+
return cls.apply(this, arguments);
7+
};
8+
9+
tracedCls.prototype = Object.keys(cls.prototype).reduce(function (newPrototype, propName) {
10+
if (cls.prototype[propName] instanceof Function) {
11+
newPrototype[propName] = function () {
12+
var result;
13+
trace(this, propName);
14+
result = cls.prototype[propName].apply(this, arguments);
15+
trace(this, propName + ' exit');
16+
return result;
17+
}
18+
} else {
19+
newPrototype[propName] = cls.prototype[propName];
20+
}
21+
22+
return newPrototype;
23+
}, {});
24+
return tracedCls;
25+
};
26+
27+
var Util = require('util');
28+
var ParseError = function (parser, expectation) {
29+
var message = "ParseError at " + parser._position + " expectations " + expectation;
30+
Error.call(this, message);
31+
};
32+
Util.inherits(ParseError, Error);
33+
34+
var SymbolTraits = {
35+
_NUMBERS_INDEX: function () {
36+
var index = {};
37+
for (var i = 0; i < 10; i++) {
38+
index[i] = true;
39+
}
40+
return index;
41+
}(),
42+
43+
_WHITESPACE_INDEX: {
44+
" ": true,
45+
"\t": true,
46+
"\n" : true
47+
},
48+
49+
isNumber: function (symbol) {
50+
return this._NUMBERS_INDEX[symbol]
51+
},
52+
53+
isWhiteSpace: function (symbol) {
54+
return this._WHITESPACE_INDEX[symbol];
55+
}
56+
57+
};
58+
59+
var JSONParser = function (string) {
60+
this._string = string;
61+
this._position = 0;
62+
this._length = string.length;
63+
};
64+
65+
JSONParser.prototype = {
66+
_TOKENS: {
67+
OBJECT_OPEN: '{',
68+
OBJECT_CLOSE: '}',
69+
ARRAY_OPEN: '[',
70+
ARRAY_CLOSE: ']',
71+
QUOTE: '"',
72+
NULL: 'null',
73+
FALSE: 'false',
74+
TRUE: 'true',
75+
COLUMN: ':',
76+
COMA: ',',
77+
BACKSLASH: '\\',
78+
MINUS: '-',
79+
DOT: '.'
80+
},
81+
82+
parse: function () {
83+
var value = this._parse();
84+
//it's allowed to skip whitespaces at the end of the json
85+
this._skipWhiteSpace();
86+
if (this._position !== this._length) {
87+
throw new ParseError(this, "end of stirng to parse");
88+
}
89+
return value;
90+
},
91+
92+
_parse: function () {
93+
while(this._position <= this._length) {
94+
if (this._lookAheadForToken(this._TOKENS.OBJECT_OPEN)) {
95+
return this._consumeObject();
96+
} else if (this._lookAheadForToken(this._TOKENS.ARRAY_OPEN)) {
97+
return this._consumeArray();
98+
} else if (this._lookAheadForToken(this._TOKENS.QUOTE)) {
99+
return this._consumeString();
100+
} else if (this._lookAheadForToken(this._TOKENS.NULL)) {
101+
this._consumeToken(this._TOKENS.NULL);
102+
return null;
103+
} else if (this._lookAheadForToken(this._TOKENS.TRUE)) {
104+
this._consumeToken(this._TOKENS.TRUE);
105+
return true;
106+
} else if (this._lookAheadForToken(this._TOKENS.FALSE)) {
107+
this._consumeToken(this._TOKENS.FALSE);
108+
return false;
109+
} else if (this._lookAheadForNumber()) {
110+
return this._consumeNumber();
111+
} else {
112+
throw new ParseError(this, "null/true/false/number");
113+
}
114+
}
115+
},
116+
117+
_consumeObject: function () {
118+
var key,
119+
result = {};
120+
121+
this._consumeToken(this._TOKENS.OBJECT_OPEN);
122+
123+
do {
124+
this._skipWhiteSpace();
125+
if (this._lookAheadForToken(this._TOKENS.OBJECT_CLOSE)) {
126+
this._consumeToken(this._TOKENS.OBJECT_CLOSE);
127+
break;
128+
}
129+
130+
key = this._requireObjectKey();
131+
this._requireColumn();
132+
133+
this._skipWhiteSpace();
134+
result[key] = this._parse();
135+
136+
this._skipWhiteSpace();
137+
if (this._lookAheadForToken(this._TOKENS.COMA)) {
138+
this._consumeToken(this._TOKENS.COMA);
139+
}
140+
} while (true);
141+
142+
return result;
143+
},
144+
145+
_consumeArray: function () {
146+
var result = [];
147+
148+
this._consumeToken(this._TOKENS.ARRAY_OPEN);
149+
150+
do {
151+
this._skipWhiteSpace();
152+
if (this._lookAheadForToken(this._TOKENS.ARRAY_CLOSE)) {
153+
this._consumeToken(this._TOKENS.ARRAY_CLOSE);
154+
break;
155+
}
156+
157+
this._skipWhiteSpace();
158+
result.push(this._parse());
159+
160+
this._skipWhiteSpace();
161+
if (this._lookAheadForToken(this._TOKENS.COMA)) {
162+
this._consumeToken(this._TOKENS.COMA);
163+
}
164+
} while (true);
165+
166+
return result;
167+
},
168+
169+
_requireObjectKey: function () {
170+
this._skipWhiteSpace();
171+
if (this._lookAheadForToken(this._TOKENS.QUOTE)) {
172+
return this._consumeString();
173+
}
174+
throw new ParseError(this, 'string as object key expected');
175+
},
176+
177+
_requireColumn: function () {
178+
this._skipWhiteSpace();
179+
if (this._lookAheadForToken(this._TOKENS.COLUMN)) {
180+
this._consumeToken(this._TOKENS.COLUMN);
181+
} else {
182+
throw new ParseError(this, 'column expected');
183+
}
184+
},
185+
186+
_consumeString: function () {
187+
var stringEscapeFlag = false,
188+
parsedString = [];
189+
190+
this._consumeToken(this._TOKENS.QUOTE);
191+
192+
do {
193+
if (stringEscapeFlag) {
194+
parsedString.push(this._consumeCurrentSymbol());
195+
stringEscapeFlag = false;
196+
} else if (this._lookAheadForToken(this._TOKENS.QUOTE)) {
197+
this._consumeToken(this._TOKENS.QUOTE);
198+
break;
199+
} else if (this._lookAheadForToken(this._TOKENS.BACKSLASH)) {
200+
this._consumeToken(this._TOKENS.BACKSLASH);
201+
stringEscapeFlag = true;
202+
} else {
203+
parsedString.push(this._consumeCurrentSymbol());
204+
}
205+
} while (true);
206+
207+
return parsedString.join("");
208+
},
209+
210+
_consumeNumber: function () {
211+
var symbol,
212+
haveMinus = false,
213+
haveDot = false,
214+
initialPosition = this._position,
215+
symbols = [];
216+
217+
while (this._position < this._length) {
218+
symbol = this._getCurrentSymbol();
219+
if (SymbolTraits.isNumber(symbol)) {
220+
symbols.push(this._consumeCurrentSymbol());
221+
} else if (this._lookAheadForToken(this._TOKENS.MINUS)) {
222+
if (haveMinus) {
223+
throw new ParseError(this, "unexpected minus");
224+
}
225+
symbols.push(this._consumeCurrentSymbol());
226+
haveMinus = true;
227+
} else if (this._lookAheadForToken(this._TOKENS.DOT)) {
228+
if (haveDot) {
229+
throw new ParseError(this, "unexpected dot");
230+
}
231+
symbols.push(this._consumeCurrentSymbol());
232+
haveDot = true;
233+
} else {
234+
break;
235+
}
236+
}
237+
return Number(symbols.join(''));
238+
},
239+
240+
_lookAheadForToken: function (pattern) {
241+
return [].every.call(pattern, function (patternLetter, i) {
242+
var offset = this._position + i;
243+
244+
if (offset >= this.length) {
245+
return false;
246+
}
247+
return this._string[offset] === patternLetter;
248+
}, this);
249+
},
250+
251+
_consumeToken: function (token) {
252+
this._position += token.length;
253+
},
254+
255+
_lookAheadForNumber: function () {
256+
var currentSymbol = this._getCurrentSymbol();
257+
258+
return SymbolTraits.isNumber(currentSymbol) || currentSymbol === this._TOKENS.MINUS;
259+
},
260+
261+
_skipWhiteSpace: function () {
262+
while (this._position < this._length) {
263+
if (!SymbolTraits.isWhiteSpace([this._string[this._position]])) {
264+
return;
265+
}
266+
this._position++;
267+
}
268+
},
269+
270+
_advancePosition: function () {
271+
if (this._position >= this._length) {
272+
throw new ParseError(this, "unexpected eos");
273+
}
274+
this._position++;
275+
},
276+
277+
_getCurrentSymbol: function () {
278+
if (this._position >= this._length) {
279+
throw new ParseError(this, "unexpected eos");
280+
}
281+
return this._string[this._position];
282+
},
283+
284+
_consumeCurrentSymbol: function () {
285+
var symbol = this._getCurrentSymbol();
286+
this._advancePosition();
287+
return symbol;
288+
}
289+
290+
};
291+
292+
//JSONParser = attachTraser(JSONParser);
293+
294+
module.exports = {
295+
parse: function (string) {
296+
return (new JSONParser(string)).parse();
297+
}
298+
};

0 commit comments

Comments
 (0)