diff --git a/src/end-to-end.test.ts b/src/end-to-end.test.ts index 7ff9fe7..9a474b3 100644 --- a/src/end-to-end.test.ts +++ b/src/end-to-end.test.ts @@ -32,6 +32,7 @@ testCases(endToEnd, code => code)('end-to-end tests', [ ['{a:A {@lookup {a}}}', either.makeRight({ a: 'A', 0: 'A' })], ['{a:A :{a}}', either.makeRight({ a: 'A', 0: 'A' })], ['{ a: (a => :a)(A) }', either.makeRight({ a: 'A' })], + ['{ a: ( a => :a )( A ) }', either.makeRight({ a: 'A' })], ['(a => :a)(A)', either.makeRight('A')], [ '{ a: a => :a, b: :a(A) }', @@ -53,13 +54,13 @@ testCases(endToEnd, code => code)('end-to-end tests', [ }), ], ['{ a: ({ A }) }', either.makeRight({ a: { 0: 'A' } })], - ['{ a: (A) }', either.makeRight({ a: 'A' })], + ['{ a: ( A ) }', either.makeRight({ a: 'A' })], ['{ a: ("A A A") }', either.makeRight({ a: 'A A A' })], ['{ ("a"): A }', either.makeRight({ a: 'A' })], ['{ a: :(b), b: B }', either.makeRight({ a: 'B', b: 'B' })], ['{ a: :("b"), b: B }', either.makeRight({ a: 'B', b: 'B' })], ['{ (a: A) (b: B) }', either.makeRight({ a: 'A', b: 'B' })], - ['({ ((a): :(b)) ((b): B) })', either.makeRight({ a: 'B', b: 'B' })], + ['( { ((a): :(b)) ( ( b ): B ) } )', either.makeRight({ a: 'B', b: 'B' })], ['{ (a: :(")")), (")": (B)) }', either.makeRight({ a: 'B', ')': 'B' })], [':match({ a: A })({ tag: a, value: {} })', either.makeRight('A')], [':{string concatenate}(a)(b)', either.makeRight('ba')], diff --git a/src/language/parsing/molecule.ts b/src/language/parsing/molecule.ts index 9119a3c..e54d6f5 100644 --- a/src/language/parsing/molecule.ts +++ b/src/language/parsing/molecule.ts @@ -1,6 +1,7 @@ import { parser, type Parser } from '../../parsing.js' import { atomParser, type Atom } from './atom.js' import { optionallySurroundedByParentheses } from './parentheses.js' +import { whitespace } from './whitespace.js' export type Molecule = { readonly [key: Atom]: Molecule | Atom } @@ -48,7 +49,6 @@ const makeIncrementingIndexer = (): Indexer => { // Language-specific parsers follow. const propertyDelimiter = parser.regularExpression(/[\s,]+/) -const whitespace = parser.regularExpression(/\s+/) const sugaredLookup: Parser = optionallySurroundedByParentheses( diff --git a/src/language/parsing/parentheses.ts b/src/language/parsing/parentheses.ts index 4c0d870..eacec96 100644 --- a/src/language/parsing/parentheses.ts +++ b/src/language/parsing/parentheses.ts @@ -1,4 +1,5 @@ import { parser, type Parser } from '../../parsing.js' +import { whitespace } from './whitespace.js' const optionallySurroundedBy = ( parser1: Parser, @@ -16,4 +17,16 @@ const optionallySurroundedBy = ( export const optionallySurroundedByParentheses = ( theParser: Parser, ): Parser => - optionallySurroundedBy(parser.literal('('), theParser, parser.literal(')')) + parser.oneOf([ + // This allows `theParser` to greedily consume whitespace. + optionallySurroundedBy( + parser.literal('('), + theParser, + parser.sequence([parser.zeroOrMore(whitespace), parser.literal(')')]), + ), + optionallySurroundedBy( + parser.sequence([parser.literal('('), parser.zeroOrMore(whitespace)]), + theParser, + parser.sequence([parser.zeroOrMore(whitespace), parser.literal(')')]), + ), + ]) diff --git a/src/language/parsing/whitespace.ts b/src/language/parsing/whitespace.ts new file mode 100644 index 0000000..212bb43 --- /dev/null +++ b/src/language/parsing/whitespace.ts @@ -0,0 +1,3 @@ +import { parser } from '../../parsing.js' + +export const whitespace = parser.regularExpression(/\s+/)