|
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' |
5 | 2 | import type { Atom, Molecule } from '../parsing.js' |
6 | | -import { unquotedAtomParser } from '../parsing/atom.js' |
7 | 3 | 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) { |
142 | 15 | return either.makeRight(openBrace + closeBrace) |
143 | 16 | } 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), |
172 | 25 | ) |
173 | 26 | } |
174 | 27 | } |
175 | 28 |
|
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) |
182 | 31 |
|
183 | | -const atomOrMolecule = (value: Atom | Molecule) => |
184 | | - typeof value === 'string' ? atom(value) : molecule(value) |
| 32 | +const unparseMolecule = moleculeUnparser( |
| 33 | + unparseAtomOrMolecule, |
| 34 | + unparseSugarFreeMolecule, |
| 35 | +) |
185 | 36 |
|
186 | | -export const inlinePlz: Notation = { atom, molecule } |
| 37 | +export const inlinePlz: Notation = { |
| 38 | + atom: unparseAtom, |
| 39 | + molecule: unparseMolecule, |
| 40 | +} |
0 commit comments