Skip to content

Commit 0b00652

Browse files
authored
Merge pull request #62 from mkantor/fix-infix-parsing
Fix parsing of infix expressions beginning with an object literal
2 parents 01d4a7c + ee37280 commit 0b00652

File tree

2 files changed

+84
-178
lines changed

2 files changed

+84
-178
lines changed

src/end-to-end.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,11 @@ testCases(endToEnd, code => code)('end-to-end tests', [
320320
[`b atom.append c atom.prepend a`, either.makeRight('abc')],
321321
[`(b atom.append c) atom.prepend a`, either.makeRight('abc')],
322322
[`a atom.append (c atom.prepend b)`, either.makeRight('abc')],
323+
[
324+
`{ a: "it works!" } object.lookup a`,
325+
either.makeRight({ tag: 'some', value: 'it works!' }),
326+
],
327+
[`{ a: :identity }.a(1) + 1`, either.makeRight('2')],
323328
[
324329
`1
325330
+ 2

src/language/parsing/expression.ts

Lines changed: 79 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -253,16 +253,13 @@ const infixOperator = sequence([
253253
])
254254

255255
const compactExpression: Parser<Molecule | Atom> = oneOf([
256-
// (a => :b).c(d)
256+
// (a)
257257
// (1 + 1)
258+
// (a => :b)(c)
259+
// ({ a: 1 } |> :identity).a
258260
map(
259261
sequence([
260-
surroundedByParentheses(
261-
oneOf([
262-
lazy(() => precededByAtomThenTrivia),
263-
lazy(() => precededByColonThenAtom),
264-
]),
265-
),
262+
surroundedByParentheses(lazy(() => expression)),
266263
compactTrailingIndexesAndArguments,
267264
]),
268265
([expression, trailingIndexesAndArguments]) =>
@@ -275,18 +272,7 @@ const compactExpression: Parser<Molecule | Atom> = oneOf([
275272
// :a.b(1).c
276273
// :f(x)
277274
// :a.b(1)(2)
278-
map(
279-
sequence([
280-
colon,
281-
atomRequiringDotQuotation,
282-
compactTrailingIndexesAndArguments,
283-
]),
284-
([_colon, key, trailingIndexesAndArguments]) =>
285-
trailingIndexesAndArgumentsToExpression(
286-
{ 0: '@lookup', key },
287-
trailingIndexesAndArguments,
288-
),
289-
),
275+
lazy(() => precededByColonThenAtom),
290276
// {}
291277
lazy(() => precededByOpeningBrace),
292278
// 1
@@ -326,95 +312,50 @@ const trailingInfixTokens = oneOrMore(
326312
),
327313
)
328314

329-
type TrailingInfixToken = readonly [
330-
operator: readonly [Atom, readonly TrailingIndexOrArgument[]],
331-
operand: Molecule | Atom,
332-
]
333-
type TrailingFunctionBodyOrInfixTokens =
334-
| {
335-
readonly kind: 'functionBody'
336-
readonly additionalParameters: readonly Atom[]
337-
readonly body: Molecule | Atom
338-
}
339-
| {
340-
readonly kind: 'infixTokens'
341-
readonly tokens: readonly [
342-
TrailingInfixToken,
343-
...(readonly TrailingInfixToken[]),
344-
]
345-
}
346-
347-
const precededByAtomThenTrivia = map(
315+
const precededByAtomThenArrow = map(
348316
sequence([
349317
atom,
350-
oneOf([
351-
// a => :b
352-
// a => {}
353-
// a => (b => c => :d)
354-
// a => b => c => d
355-
// a => 1 + 1
356-
map(
357-
sequence([
358-
trivia,
359-
arrow,
360-
trivia,
361-
zeroOrMore(
362-
map(
363-
sequence([atom, trivia, arrow, trivia]),
364-
([parameter, _trivia1, _arrow, _trivia2]) => parameter,
365-
),
366-
),
367-
lazy(() => expression),
368-
]),
369-
([
370-
_trivia1,
371-
_arrow,
372-
_trivia2,
373-
additionalParameters,
374-
body,
375-
]): TrailingFunctionBodyOrInfixTokens => ({
376-
kind: 'functionBody',
377-
additionalParameters,
378-
body,
379-
}),
380-
),
381-
// 1 + 2 + 3 + 4
382-
// 1 + (2 + 3 + 4)
318+
// a => :b
319+
// a => {}
320+
// a => (b => c => :d)
321+
// a => b => c => d
322+
// a => 1 + 1
323+
trivia,
324+
arrow,
325+
trivia,
326+
zeroOrMore(
383327
map(
384-
trailingInfixTokens,
385-
(tokens): TrailingFunctionBodyOrInfixTokens => ({
386-
kind: 'infixTokens',
387-
tokens,
388-
}),
328+
sequence([atom, trivia, arrow, trivia]),
329+
([parameter, _trivia1, _arrow, _trivia2]) => parameter,
389330
),
390-
]),
331+
),
332+
lazy(() => expression),
391333
]),
392-
([initialAtom, trailingFunctionBodyOrInfixTokens]) => {
393-
switch (trailingFunctionBodyOrInfixTokens.kind) {
394-
case 'functionBody':
395-
const [lastParameter, ...additionalParameters] = [
396-
...trailingFunctionBodyOrInfixTokens.additionalParameters.toReversed(),
397-
initialAtom,
398-
]
399-
const initialFunction = {
400-
0: '@function',
401-
parameter: lastParameter,
402-
body: trailingFunctionBodyOrInfixTokens.body,
403-
}
404-
return additionalParameters.reduce(
405-
(expression, additionalParameter) => ({
406-
0: '@function',
407-
parameter: additionalParameter,
408-
body: expression,
409-
}),
410-
initialFunction,
411-
)
412-
case 'infixTokens':
413-
return infixTokensToExpression([
414-
initialAtom,
415-
...trailingFunctionBodyOrInfixTokens.tokens.flat(),
416-
])
334+
([
335+
initialParameter,
336+
_trivia1,
337+
_arrow,
338+
_trivia2,
339+
trailingParameters,
340+
body,
341+
]) => {
342+
const [lastParameter, ...additionalParameters] = [
343+
...trailingParameters.toReversed(),
344+
initialParameter,
345+
]
346+
const initialFunction = {
347+
0: '@function',
348+
parameter: lastParameter,
349+
body: body,
417350
}
351+
return additionalParameters.reduce(
352+
(expression, additionalParameter) => ({
353+
0: '@function',
354+
parameter: additionalParameter,
355+
body: expression,
356+
}),
357+
initialFunction,
358+
)
418359
},
419360
)
420361

@@ -423,53 +364,13 @@ const precededByAtomThenTrivia = map(
423364
// :a.b(1).c
424365
// :f(x)
425366
// :a.b(1)(2)
426-
// :a b.c :z
427-
// :a b.c z
428-
// :f(g) + b
429-
// :a + :b + :c + :d
430367
const precededByColonThenAtom = map(
431-
sequence([
432-
colon,
433-
atomRequiringDotQuotation,
434-
trailingIndexesAndArguments,
435-
zeroOrMore(
436-
map(
437-
// See note in `trailingInfixTokens` about newlines.
438-
oneOf([
439-
sequence([
440-
trivia,
441-
infixOperator,
442-
triviaExceptNewlines,
443-
compactExpression,
444-
]),
445-
sequence([
446-
triviaExceptNewlines,
447-
infixOperator,
448-
trivia,
449-
compactExpression,
450-
]),
451-
]),
452-
([_trivia1, operator, _trivia2, operand]) =>
453-
[operator, operand] as const,
454-
),
455-
),
456-
]),
457-
([_colon, key, trailingIndexesAndArguments, infixOperationTokens]) => {
458-
const initialExpression = trailingIndexesAndArgumentsToExpression(
368+
sequence([colon, atomRequiringDotQuotation, trailingIndexesAndArguments]),
369+
([_colon, key, trailingIndexesAndArguments]) =>
370+
trailingIndexesAndArgumentsToExpression(
459371
{ 0: '@lookup', key },
460372
trailingIndexesAndArguments,
461-
)
462-
const [firstToken, ...additionalTokens] = infixOperationTokens
463-
if (firstToken === undefined) {
464-
return initialExpression
465-
} else {
466-
return infixTokensToExpression([
467-
initialExpression,
468-
...firstToken,
469-
...additionalTokens.flat(),
470-
])
471-
}
472-
},
373+
),
473374
)
474375

475376
// (1 + 1)
@@ -479,36 +380,21 @@ const precededByColonThenAtom = map(
479380
// (1 + 1).b
480381
// (:x => x)(1)
481382
// (:f >> :g)(1)
482-
// (1 + 1) - (1 + 1)
483-
const precededByOpeningParenthesis = oneOf([
484-
map(
485-
sequence([
486-
surroundedByParentheses(lazy(() => expression)),
487-
trailingInfixTokens,
488-
]),
489-
([initialExpression, trailingInfixTokens]) =>
490-
infixTokensToExpression([
491-
initialExpression,
492-
...trailingInfixTokens.flat(),
493-
]),
494-
),
495-
map(
496-
sequence([
497-
surroundedByParentheses(lazy(() => expression)),
383+
const precededByOpeningParenthesis = map(
384+
sequence([
385+
surroundedByParentheses(lazy(() => expression)),
386+
trailingIndexesAndArguments,
387+
]),
388+
([expression, trailingIndexesAndArguments]) =>
389+
trailingIndexesAndArgumentsToExpression(
390+
expression,
498391
trailingIndexesAndArguments,
499-
]),
500-
([expression, trailingIndexesAndArguments]) =>
501-
trailingIndexesAndArgumentsToExpression(
502-
expression,
503-
trailingIndexesAndArguments,
504-
),
505-
),
506-
])
392+
),
393+
)
507394

508395
// {}
509396
// { a: b }
510397
// { 1, 2, 3 }
511-
// {a::f}.a(1) + 1
512398
const precededByOpeningBrace = map(
513399
sequence([sugarFreeMolecule, trailingIndexesAndArguments]),
514400
([expression, trailingIndexesAndArguments]) =>
@@ -518,10 +404,25 @@ const precededByOpeningBrace = map(
518404
),
519405
)
520406

521-
export const expression: Parser<Atom | Molecule> = oneOf([
522-
precededByOpeningParenthesis,
523-
precededByOpeningBrace,
524-
precededByColonThenAtom,
525-
precededByAtomThenTrivia,
526-
atom,
527-
])
407+
export const expression: Parser<Atom | Molecule> = map(
408+
sequence([
409+
oneOf([
410+
precededByOpeningParenthesis,
411+
precededByOpeningBrace,
412+
precededByColonThenAtom,
413+
precededByAtomThenArrow,
414+
atom,
415+
]),
416+
optional(trailingInfixTokens),
417+
]),
418+
([initialExpression, trailingInfixTokens]) => {
419+
if (trailingInfixTokens === undefined) {
420+
return initialExpression
421+
} else {
422+
return infixTokensToExpression([
423+
initialExpression,
424+
...trailingInfixTokens.flat(),
425+
])
426+
}
427+
},
428+
)

0 commit comments

Comments
 (0)