Arcsecond Article
const { | |
between, | |
many, | |
choice, | |
sequenceOf, | |
char, | |
whitespace, | |
anythingExcept, | |
possibly, | |
regex, | |
digits, | |
anyOfString, | |
str, | |
recursiveParser, | |
sepBy | |
} = require('arcsecond'); | |
/*********************************** | |
Type Containers | |
***********************************/ | |
const makeBasicType = typeName => value => ({ | |
type: typeName, | |
value, | |
toString: () => `${typeName}(${value})` | |
}); | |
const makeMultiType = typeName => values => ({ | |
type: typeName, | |
value: values, | |
toString: () => `${typeName}(${values.map(v => v.toString()).join(', ')})` | |
}); | |
const stringType = makeBasicType('String'); | |
const numberType = makeBasicType('Number'); | |
const booleanType = makeBasicType('Boolean'); | |
const arrayType = makeMultiType('Array'); | |
const objectType = makeMultiType('Object'); | |
const keyValuePair = (key, value) => ({ | |
type: 'KeyValuePair', | |
value: [key, value], | |
toString: () => `KeyValuePair(${key.toString()}, ${value.toString()})` | |
}); | |
const nullType = () => ({ | |
type: 'Null', | |
value: null, | |
toString: () => 'Null' | |
}); | |
/*********************************** | |
Utility Parsers | |
***********************************/ | |
const orEmptyString = parser => possibly (parser) .map(x => (x) ? x : ''); | |
const joinedSequence = parsers => sequenceOf (parsers) .map(x => x.join('')); | |
const joinedMany = parser => many (parser) .map(x => x.join('')); | |
const whitespaceSurrounded = between (whitespace) (whitespace); | |
const betweenQuotes = between (char ('"')) (char ('"')); | |
const betweenSquareBrackets = between (char ('[')) (char (']')); | |
const betweenCurlyBrackets = between (whitespaceSurrounded (char ('{'))) (whitespaceSurrounded(char ('}'))); | |
const commaSeparated = sepBy (whitespaceSurrounded (char (','))); | |
/*********************************** | |
Main Recursive Parser | |
***********************************/ | |
const jsonParser = recursiveParser(() => choice ([ | |
nullParser, | |
trueParser, | |
falseParser, | |
numberParser, | |
stringParser, | |
arrayParser, | |
objectParser | |
])); | |
/*********************************** | |
Strings | |
***********************************/ | |
const stringParser = betweenQuotes (joinedMany (choice ([ | |
joinedSequence ([ | |
char ('\\'), | |
char ('"') | |
]), | |
anythingExcept (char ('"')) | |
]))) .map(stringType); | |
/*********************************** | |
Numbers | |
***********************************/ | |
const numberParser = joinedSequence ([ | |
orEmptyString (char ('-')), | |
choice ([ | |
char ('0'), | |
regex (/^[1-9][0-9]*/) | |
]), | |
orEmptyString (joinedSequence ([ | |
char ('.'), | |
digits | |
])), | |
orEmptyString (joinedSequence([ | |
anyOfString ('eE'), | |
orEmptyString (anyOfString ('-+')), | |
digits | |
])) | |
]) .map (x => numberType(Number(x))); | |
/*********************************** | |
Nulls | |
***********************************/ | |
const nullParser = str ('null') .map(nullType); | |
/*********************************** | |
Booleans | |
***********************************/ | |
const trueParser = str ('true') .map(booleanType); | |
const falseParser = str ('false') .map(booleanType); | |
/*********************************** | |
Arrays | |
***********************************/ | |
const arrayParser = betweenSquareBrackets (commaSeparated (jsonParser)) .map(arrayType); | |
const keyValuePairParser = sequenceOf ([ | |
stringParser, | |
whitespaceSurrounded (char (':')), | |
jsonParser | |
]) .map(([key, _, value]) => keyValuePair(key, value)); | |
/*********************************** | |
Objects | |
***********************************/ | |
const objectParser = betweenCurlyBrackets (commaSeparated (keyValuePairParser)) .map(objectType); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
Why does
betweenSquareBrackets
not allow whitespace around the square brackets?