Skip to content

Commit 242df96

Browse files
committed
Share common utilities across plz unparsers
1 parent 2f84c7e commit 242df96

File tree

4 files changed

+261
-355
lines changed

4 files changed

+261
-355
lines changed
Lines changed: 30 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -1,186 +1,40 @@
1-
import type { Right } from '@matt.kantor/either'
2-
import either, { type Either } from '@matt.kantor/either'
3-
import kleur from 'kleur'
4-
import type { UnserializableValueError } from '../errors.js'
1+
import either from '@matt.kantor/either'
52
import type { Atom, Molecule } from '../parsing.js'
6-
import { unquotedAtomParser } from '../parsing/atom.js'
73
import {
8-
isSemanticGraph,
9-
readApplyExpression,
10-
readFunctionExpression,
11-
readLookupExpression,
12-
serialize,
13-
type KeyPath,
14-
type SemanticGraph,
15-
} from '../semantics.js'
16-
import { type Notation } from './unparsing-utilities.js'
17-
18-
// TODO: Share implementation details with pretty plz notation.
19-
20-
const dot = kleur.dim('.')
21-
const quote = kleur.dim('"')
22-
const colon = kleur.dim(':')
23-
const comma = kleur.dim(',')
24-
const openBrace = kleur.dim('{')
25-
const closeBrace = kleur.dim('}')
26-
const openParenthesis = kleur.dim('(')
27-
const closeParenthesis = kleur.dim(')')
28-
const arrow = kleur.dim('=>')
29-
30-
const escapeStringContents = (value: string) =>
31-
value.replace('\\', '\\\\').replace('"', '\\"')
32-
33-
const quoteIfNecessary = (value: string) => {
34-
const unquotedAtomResult = unquotedAtomParser(value)
35-
if (
36-
either.isLeft(unquotedAtomResult) ||
37-
unquotedAtomResult.value.remainingInput.length !== 0
38-
) {
39-
return quote.concat(escapeStringContents(value)).concat(quote)
40-
} else {
41-
return value
42-
}
43-
}
44-
45-
const atom = (node: string): Right<string> =>
46-
either.makeRight(
47-
quoteIfNecessary(
48-
/^@[^@]/.test(node) ? kleur.bold(kleur.underline(node)) : node,
49-
),
50-
)
51-
52-
const molecule = (
53-
value: Molecule,
54-
): Either<UnserializableValueError, string> => {
55-
const functionExpressionResult = readFunctionExpression(value)
56-
if (!either.isLeft(functionExpressionResult)) {
57-
return sugaredFunction(
58-
functionExpressionResult.value.parameter,
59-
functionExpressionResult.value.body,
60-
)
61-
} else {
62-
const applyExpressionResult = readApplyExpression(value)
63-
if (!either.isLeft(applyExpressionResult)) {
64-
return sugaredApply(
65-
applyExpressionResult.value.argument,
66-
applyExpressionResult.value.function,
67-
)
68-
} else {
69-
const lookupExpressionResult = readLookupExpression(value)
70-
if (!either.isLeft(lookupExpressionResult)) {
71-
return sugaredLookup(lookupExpressionResult.value.query)
72-
} else {
73-
return sugarFreeMolecule(value)
74-
}
75-
}
76-
}
77-
}
78-
79-
const sugaredLookup = (keyPathAsNode: Molecule | SemanticGraph) => {
80-
const keyPath = Object.entries(keyPathAsNode).reduce(
81-
(accumulator: KeyPath | 'invalid', [key, value]) => {
82-
if (accumulator === 'invalid') {
83-
return accumulator
84-
} else {
85-
if (key === String(accumulator.length) && typeof value === 'string') {
86-
return [...accumulator, value]
87-
} else {
88-
return 'invalid'
89-
}
90-
}
91-
},
92-
[],
93-
)
94-
95-
if (
96-
keyPath !== 'invalid' &&
97-
Object.keys(keyPathAsNode).length === keyPath.length &&
98-
keyPath.every(key => !either.isLeft(unquotedAtomParser(key)))
99-
) {
100-
return either.makeRight(kleur.cyan(colon.concat(keyPath.join(dot))))
101-
} else {
102-
return either.flatMap(serializeIfNeeded(keyPathAsNode), serializedKeyPath =>
103-
either.map(atomOrMolecule(serializedKeyPath), keyPathAsString =>
104-
kleur.cyan(colon.concat(keyPathAsString)),
105-
),
106-
)
107-
}
108-
}
109-
110-
const sugaredFunction = (
111-
parameterName: string,
112-
body: Molecule | SemanticGraph,
113-
) =>
114-
either.flatMap(serializeIfNeeded(body), serializedBody =>
115-
either.map(atomOrMolecule(serializedBody), bodyAsString =>
116-
[kleur.cyan(parameterName), arrow, bodyAsString].join(' '),
117-
),
118-
)
119-
120-
const sugaredApply = (
121-
argument: Molecule | SemanticGraph,
122-
functionToApply: Molecule | SemanticGraph,
123-
) =>
124-
either.flatMap(serializeIfNeeded(functionToApply), serializedFunction =>
125-
either.flatMap(
126-
atomOrMolecule(serializedFunction),
127-
functionToApplyAsString =>
128-
either.flatMap(serializeIfNeeded(argument), serializedArgument =>
129-
either.map(atomOrMolecule(serializedArgument), argumentAsString =>
130-
functionToApplyAsString
131-
.concat(openParenthesis)
132-
.concat(argumentAsString)
133-
.concat(closeParenthesis),
134-
),
135-
),
136-
),
137-
)
138-
139-
const sugarFreeMolecule = (value: Molecule) => {
140-
const entries = Object.entries(value)
141-
if (entries.length === 0) {
4+
closeBrace,
5+
comma,
6+
moleculeUnparser,
7+
openBrace,
8+
sugarFreeMoleculeAsKeyValuePairStrings,
9+
unparseAtom,
10+
} from './plz-utilities.js'
11+
import type { Notation } from './unparsing-utilities.js'
12+
13+
const unparseSugarFreeMolecule = (value: Molecule) => {
14+
if (Object.keys(value).length === 0) {
14215
return either.makeRight(openBrace + closeBrace)
14316
} else {
144-
const keyValuePairsAsStrings: string[] = []
145-
let ordinalPropertyKeyCounter = 0n
146-
for (const [propertyKey, propertyValue] of entries) {
147-
const valueAsStringResult = atomOrMolecule(propertyValue)
148-
if (either.isLeft(valueAsStringResult)) {
149-
return valueAsStringResult
150-
}
151-
152-
// Omit ordinal property keys:
153-
if (propertyKey === String(ordinalPropertyKeyCounter)) {
154-
keyValuePairsAsStrings.push(valueAsStringResult.value)
155-
ordinalPropertyKeyCounter += 1n
156-
} else {
157-
keyValuePairsAsStrings.push(
158-
kleur
159-
.cyan(quoteIfNecessary(propertyKey).concat(colon))
160-
.concat(' ')
161-
.concat(valueAsStringResult.value),
162-
)
163-
}
164-
}
165-
166-
return either.makeRight(
167-
openBrace
168-
.concat(' ')
169-
.concat(keyValuePairsAsStrings.join(comma.concat(' ')))
170-
.concat(' ')
171-
.concat(closeBrace),
17+
return either.map(
18+
sugarFreeMoleculeAsKeyValuePairStrings(value, unparseAtomOrMolecule),
19+
keyValuePairsAsStrings =>
20+
openBrace
21+
.concat(' ')
22+
.concat(keyValuePairsAsStrings.join(comma.concat(' ')))
23+
.concat(' ')
24+
.concat(closeBrace),
17225
)
17326
}
17427
}
17528

176-
const serializeIfNeeded = (
177-
nodeOrMolecule: SemanticGraph | Molecule,
178-
): Either<UnserializableValueError, Atom | Molecule> =>
179-
isSemanticGraph(nodeOrMolecule)
180-
? serialize(nodeOrMolecule)
181-
: either.makeRight(nodeOrMolecule)
29+
const unparseAtomOrMolecule = (value: Atom | Molecule) =>
30+
typeof value === 'string' ? unparseAtom(value) : unparseMolecule(value)
18231

183-
const atomOrMolecule = (value: Atom | Molecule) =>
184-
typeof value === 'string' ? atom(value) : molecule(value)
32+
const unparseMolecule = moleculeUnparser(
33+
unparseAtomOrMolecule,
34+
unparseSugarFreeMolecule,
35+
)
18536

186-
export const inlinePlz: Notation = { atom, molecule }
37+
export const inlinePlz: Notation = {
38+
atom: unparseAtom,
39+
molecule: unparseMolecule,
40+
}

0 commit comments

Comments
 (0)