@@ -8,10 +8,13 @@ import {
88 isSemanticGraph ,
99 readApplyExpression ,
1010 readFunctionExpression ,
11+ readIndexExpression ,
1112 readLookupExpression ,
1213 serialize ,
1314 type ApplyExpression ,
1415 type FunctionExpression ,
16+ type IndexExpression ,
17+ type KeyPath ,
1518 type LookupExpression ,
1619 type SemanticGraph ,
1720} from '../semantics.js'
@@ -35,52 +38,40 @@ export const moleculeUnparser =
3538 ) => Either < UnserializableValueError , string > ,
3639 ) =>
3740 ( value : Molecule ) : Either < UnserializableValueError , string > => {
38- const functionExpressionResult = readFunctionExpression ( value )
39- if ( ! either . isLeft ( functionExpressionResult ) ) {
40- return unparseSugaredFunction (
41- functionExpressionResult . value ,
42- unparseAtomOrMolecule ,
43- )
44- } else {
45- const applyExpressionResult = readApplyExpression ( value )
46- if ( ! either . isLeft ( applyExpressionResult ) ) {
47- return unparseSugaredApply (
48- applyExpressionResult . value ,
49- unparseAtomOrMolecule ,
50- )
51- } else {
52- const lookupExpressionResult = readLookupExpression ( value )
53- if ( ! either . isLeft ( lookupExpressionResult ) ) {
54- return unparseSugaredLookup (
55- lookupExpressionResult . value ,
56- unparseAtomOrMolecule ,
57- )
58- } else {
59- return unparseSugarFreeMolecule ( value , unparseAtomOrMolecule )
60- }
61- }
41+ switch ( value [ '0' ] ) {
42+ case '@apply' :
43+ return either . match ( readApplyExpression ( value ) , {
44+ left : _ => unparseSugarFreeMolecule ( value , unparseAtomOrMolecule ) ,
45+ right : applyExpression =>
46+ unparseSugaredApply ( applyExpression , unparseAtomOrMolecule ) ,
47+ } )
48+ case '@function' :
49+ return either . match ( readFunctionExpression ( value ) , {
50+ left : _ => unparseSugarFreeMolecule ( value , unparseAtomOrMolecule ) ,
51+ right : functionExpression =>
52+ unparseSugaredFunction ( functionExpression , unparseAtomOrMolecule ) ,
53+ } )
54+ case '@index' :
55+ return either . match ( readIndexExpression ( value ) , {
56+ left : _ => unparseSugarFreeMolecule ( value , unparseAtomOrMolecule ) ,
57+ right : indexExpression =>
58+ unparseSugaredIndex ( indexExpression , unparseAtomOrMolecule ) ,
59+ } )
60+ case '@lookup' :
61+ return either . match ( readLookupExpression ( value ) , {
62+ left : _ => unparseSugarFreeMolecule ( value , unparseAtomOrMolecule ) ,
63+ right : lookupExpression =>
64+ unparseSugaredLookup ( lookupExpression , unparseAtomOrMolecule ) ,
65+ } )
66+ default :
67+ return unparseSugarFreeMolecule ( value , unparseAtomOrMolecule )
6268 }
6369 }
6470
65- export const quoteIfNecessary = ( value : string ) : string => {
66- const unquotedAtomResult = parsing . parse ( unquotedAtomParser , value )
67- if ( either . isLeft ( unquotedAtomResult ) ) {
68- return quote . concat ( escapeStringContents ( value ) ) . concat ( quote )
69- } else {
70- return value
71- }
72- }
73-
74- export const serializeIfNeeded = (
75- nodeOrMolecule : SemanticGraph | Molecule ,
76- ) : Either < UnserializableValueError , Atom | Molecule > =>
77- isSemanticGraph ( nodeOrMolecule )
78- ? serialize ( nodeOrMolecule )
79- : either . makeRight ( nodeOrMolecule )
80-
81- export const sugarFreeMoleculeAsKeyValuePairStrings = (
71+ export const moleculeAsKeyValuePairStrings = (
8272 value : Molecule ,
8373 unparseAtomOrMolecule : UnparseAtomOrMolecule ,
74+ options : { readonly ordinalKeys : 'omit' | 'preserve' } ,
8475) : Either < UnserializableValueError , readonly string [ ] > => {
8576 const entries = Object . entries ( value )
8677
@@ -93,13 +84,16 @@ export const sugarFreeMoleculeAsKeyValuePairStrings = (
9384 }
9485
9586 // Omit ordinal property keys:
96- if ( propertyKey === String ( ordinalPropertyKeyCounter ) ) {
87+ if (
88+ propertyKey === String ( ordinalPropertyKeyCounter ) &&
89+ options . ordinalKeys === 'omit'
90+ ) {
9791 keyValuePairsAsStrings . push ( valueAsStringResult . value )
9892 ordinalPropertyKeyCounter += 1n
9993 } else {
10094 keyValuePairsAsStrings . push (
10195 kleur
102- . cyan ( quoteIfNecessary ( propertyKey ) . concat ( colon ) )
96+ . cyan ( quoteAtomIfNecessary ( propertyKey ) . concat ( colon ) )
10397 . concat ( ' ' )
10498 . concat ( valueAsStringResult . value ) ,
10599 )
@@ -111,10 +105,35 @@ export const sugarFreeMoleculeAsKeyValuePairStrings = (
111105export const unparseAtom = ( atom : string ) : Right < string > =>
112106 either . makeRight (
113107 / ^ @ [ ^ @ ] / . test ( atom )
114- ? kleur . bold ( kleur . underline ( quoteIfNecessary ( atom ) ) )
115- : quoteIfNecessary ( atom ) ,
108+ ? kleur . bold ( kleur . underline ( quoteAtomIfNecessary ( atom ) ) )
109+ : quoteAtomIfNecessary ( atom ) ,
116110 )
117111
112+ const quoteAtomIfNecessary = ( value : string ) : string => {
113+ const unquotedAtomResult = parsing . parse ( unquotedAtomParser , value )
114+ if ( either . isLeft ( unquotedAtomResult ) ) {
115+ return quote . concat ( escapeStringContents ( value ) ) . concat ( quote )
116+ } else {
117+ return value
118+ }
119+ }
120+
121+ const quoteKeyPathComponentIfNecessary = ( value : string ) : string => {
122+ const unquotedAtomResult = parsing . parse ( unquotedAtomParser , value )
123+ if ( either . isLeft ( unquotedAtomResult ) || value . includes ( '.' ) ) {
124+ return quote . concat ( escapeStringContents ( value ) ) . concat ( quote )
125+ } else {
126+ return value
127+ }
128+ }
129+
130+ const serializeIfNeeded = (
131+ nodeOrMolecule : SemanticGraph | Molecule ,
132+ ) : Either < UnserializableValueError , Atom | Molecule > =>
133+ isSemanticGraph ( nodeOrMolecule )
134+ ? serialize ( nodeOrMolecule )
135+ : either . makeRight ( nodeOrMolecule )
136+
118137type UnparseAtomOrMolecule = (
119138 value : Atom | Molecule ,
120139) => Either < UnserializableValueError , string >
@@ -167,10 +186,54 @@ const unparseSugaredFunction = (
167186 ) ,
168187 )
169188
189+ const unparseSugaredIndex = (
190+ expression : IndexExpression ,
191+ unparseAtomOrMolecule : UnparseAtomOrMolecule ,
192+ ) => {
193+ const objectUnparseResult = either . flatMap (
194+ serializeIfNeeded ( expression . object ) ,
195+ unparseAtomOrMolecule ,
196+ )
197+ if ( either . isLeft ( objectUnparseResult ) ) {
198+ return objectUnparseResult
199+ }
200+
201+ const keyPath = Object . entries ( expression . query ) . reduce (
202+ ( accumulator : KeyPath | 'invalid' , [ key , value ] ) => {
203+ if ( accumulator === 'invalid' ) {
204+ return accumulator
205+ } else {
206+ if ( key === String ( accumulator . length ) && typeof value === 'string' ) {
207+ return [ ...accumulator , value ]
208+ } else {
209+ return 'invalid'
210+ }
211+ }
212+ } ,
213+ [ ] ,
214+ )
215+
216+ if (
217+ keyPath === 'invalid' ||
218+ Object . keys ( expression . query ) . length !== keyPath . length
219+ ) {
220+ return either . makeLeft ( {
221+ kind : 'unserializableValue' ,
222+ message : 'invalid key path' ,
223+ } )
224+ }
225+
226+ return either . makeRight (
227+ objectUnparseResult . value
228+ . concat ( dot )
229+ . concat ( keyPath . map ( quoteKeyPathComponentIfNecessary ) . join ( dot ) ) ,
230+ )
231+ }
232+
170233const unparseSugaredLookup = (
171234 expression : LookupExpression ,
172- unparseAtomOrMolecule : UnparseAtomOrMolecule ,
235+ _unparseAtomOrMolecule : UnparseAtomOrMolecule ,
173236) =>
174- either . map ( unparseAtomOrMolecule ( expression . key ) , key =>
175- kleur . cyan ( colon . concat ( key ) ) ,
237+ either . makeRight (
238+ kleur . cyan ( colon . concat ( quoteKeyPathComponentIfNecessary ( expression . key ) ) ) ,
176239 )
0 commit comments