@@ -5,13 +5,15 @@ import type { UnserializableValueError } from '../errors.js'
55import type { Atom , Molecule } from '../parsing.js'
66import { unquotedAtomParser } from '../parsing/atom.js'
77import {
8+ isExpression ,
89 isSemanticGraph ,
910 readApplyExpression ,
1011 readFunctionExpression ,
1112 readIndexExpression ,
1213 readLookupExpression ,
1314 serialize ,
1415 type ApplyExpression ,
16+ type Expression ,
1517 type FunctionExpression ,
1618 type IndexExpression ,
1719 type KeyPath ,
@@ -55,7 +57,19 @@ export const moleculeUnparser =
5557 unparseSugaredLookup ( lookupExpression , unparseAtomOrMolecule ) ,
5658 } )
5759 default :
58- return unparseSugarFreeMolecule ( value , unparseAtomOrMolecule )
60+ if ( isExpression ( value ) ) {
61+ const result = unparseSugaredGeneralizedKeywordExpression (
62+ value ,
63+ unparseAtomOrMolecule ,
64+ )
65+ if ( either . isLeft ( result ) ) {
66+ return unparseSugarFreeMolecule ( value , unparseAtomOrMolecule )
67+ } else {
68+ return result
69+ }
70+ } else {
71+ return unparseSugarFreeMolecule ( value , unparseAtomOrMolecule )
72+ }
5973 }
6074 }
6175
@@ -101,10 +115,12 @@ export const unparseAtom = (atom: string): Right<string> =>
101115 : quoteAtomIfNecessary ( atom ) ,
102116 )
103117
118+ const requiresQuotation = ( atom : string ) : boolean =>
119+ either . isLeft ( parsing . parse ( unquotedAtomParser , atom ) )
120+
104121const quoteAtomIfNecessary = ( value : string ) : string => {
105122 const { quote } = punctuation ( kleur )
106- const unquotedAtomResult = parsing . parse ( unquotedAtomParser , value )
107- if ( either . isLeft ( unquotedAtomResult ) ) {
123+ if ( requiresQuotation ( value ) ) {
108124 return quote . concat ( escapeStringContents ( value ) ) . concat ( quote )
109125 } else {
110126 return value
@@ -252,3 +268,38 @@ const unparseSugaredLookup = (
252268 ) ,
253269 ) ,
254270 )
271+
272+ const unparseSugaredGeneralizedKeywordExpression = (
273+ expression : Expression ,
274+ unparseAtomOrMolecule : UnparseAtomOrMolecule ,
275+ ) => {
276+ if (
277+ // Not every valid keyword expression can be expressed with the
278+ // generalized sugar, e.g. if there are any additional properties
279+ // besides the keyword and its argument, or if the keyword requires
280+ // quotation (which won't be the case for any built-in keywords, but
281+ // maybe eventually users will be able to create custom keywords).
282+ requiresQuotation ( expression [ '0' ] . substring ( 1 ) ) ||
283+ Object . keys ( expression ) . some ( key => key !== '0' && key !== '1' )
284+ ) {
285+ return either . makeLeft ( {
286+ kind : 'unserializableValue' ,
287+ message :
288+ 'expression cannot be faithfully represented using generalized keyword expression sugar' ,
289+ } )
290+ } else {
291+ const unparsedKeyword = kleur . bold ( kleur . underline ( expression [ '0' ] ) )
292+ if ( '1' in expression ) {
293+ return either . map (
294+ either . flatMap (
295+ serializeIfNeeded ( expression [ '1' ] ) ,
296+ unparseAtomOrMolecule ,
297+ ) ,
298+ unparsedArgument =>
299+ unparsedKeyword . concat ( ' ' ) . concat ( unparsedArgument ) ,
300+ )
301+ } else {
302+ return either . makeRight ( unparsedKeyword )
303+ }
304+ }
305+ }
0 commit comments