Created
February 10, 2014 05:29
-
-
Save bmeck/8910816 to your computer and use it in GitHub Desktop.
Example of how to use yield* to combine generators
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Our incremental JSON parser (simplified only to use strings and arrays) | |
// while iterator result != done keep using .next(moreSrc) or .next(null) if you are done providing data | |
// while the iterator is not done it will return the state of the iterator | |
// the iterator is seeking more data to get a valid value | |
// call .next(str) to provide it more data | |
var NEED_MORE_DATA = 0; | |
// the iterator has a valid result and will return a result if you provide .next(null) | |
// this is not automatic, due to numbers and potentially invalid trailing characters after an expected end | |
var HAVE_RESULT = 1; | |
function* parseJSON(src) { | |
return (yield* parseJSONValue(src, false)).result; | |
} | |
function removeStartingSpaces(src) { | |
return src.replace(/^\s+/, ''); | |
} | |
function checkForUnexpectedEnd(src) { | |
if (src == null) { | |
throw new Error('unexpected end of source'); | |
} | |
return src; | |
} | |
function needToCheckTrailingSource(src) { | |
if (src == null) { | |
return false; | |
} | |
var invalidCharacter = /\S/.exec(src); | |
if (invalidCharacter) { | |
throw new Error('Found an invalid character after string ' + invalidCharacter[0]); | |
} | |
} | |
// our "internal" method, used to grab all of the types of data | |
function* parseJSONValue(src, stop_on_result) { | |
while (src != null) { | |
src = removeStartingSpaces(src); | |
if (src.length) switch (src[0]) { | |
case '"': | |
var value = yield* parseJSONString(src, stop_on_result); | |
return value; | |
case '[': | |
var value = yield* parseJSONArray(src, stop_on_result); | |
return value; | |
default: | |
throw new Error('Unexpected character while expecting value'); | |
} | |
src = yield checkForUnexpectedEnd(src); | |
} | |
checkForUnexpectedEnd(src); | |
} | |
function* parseJSONString(src, stop_on_result) { | |
// skip starting spaces | |
while (!src.length) { | |
src = removeStartingSpaces(checkForUnexpectedEnd(yield NEED_MORE_DATA)); | |
} | |
if (src[0] !== '"') { | |
throw new Error('Unexpected character while expecting string ' + src[i]); | |
} | |
var result = ''; | |
var escaped = false; | |
src = src.slice(1); | |
parse_loop: | |
while (true) { | |
for (var i = 0; i < src.length; i++) { | |
if (escaped) { | |
// ideally we would add some checks here, unicode, but this is just an example | |
result += src[i]; | |
} | |
else if (src[i] == '\\') { | |
escaped = true; | |
} | |
else if (src[i] == '"') { | |
src = src.slice(i+1); | |
break parse_loop; | |
} | |
else { | |
result += src[i]; | |
} | |
} | |
src = checkForUnexpectedEnd(yield NEED_MORE_DATA); | |
} | |
if (!stop_on_result) { | |
while (needToCheckTrailingSource(yield HAVE_RESULT)); | |
} | |
return { result: result, unused: src }; | |
} | |
function* parseJSONArray(src, stop_on_result) { | |
// skip starting spaces | |
while (!src.length) { | |
src = removeStartingSpaces(checkForUnexpectedEnd(yield NEED_MORE_DATA)); | |
} | |
if (src[0] !== '[') { | |
throw new Error('Unexpected character while expecting array ' + src[i]); | |
} | |
var result = []; | |
// remove opening bracket and spaces | |
src = removeStartingSpaces(src.slice(1)); | |
// skip spaces | |
while (!src.length) { | |
src = removeStartingSpaces(checkForUnexpectedEnd(yield NEED_MORE_DATA)); | |
} | |
if (src[0] != ']') while (true) { | |
// we want to grab a value from our iteration, but halt on getting a value | |
var values = yield* parseJSONValue(src, true); | |
result.push(values.result); | |
// keep processing data if we have stuff left over | |
src = values.unused; | |
// request more data, just skip starting spaces | |
while (!src.length) { | |
src = removeStartingSpaces(checkForUnexpectedEnd(yield NEED_MORE_DATA).replace(/^\s+/, '')); | |
} | |
if (src[0] == ']') { | |
break; | |
} | |
if (src[0] != ',') throw new Error('Found an invalid character while expecting end of array or comma'); | |
// remove comma | |
src = src.slice(1); | |
} | |
// remove trailing backet | |
src = src.slice(1); | |
if (!stop_on_result) { | |
while (needToCheckTrailingSource(yield HAVE_RESULT)); | |
} | |
return { result: result, unused: src }; | |
} | |
g=parseJSON('[[""],"test"]'); | |
g.next(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment