11import {
2- as ,
32 lazy ,
43 literal ,
54 map ,
@@ -29,21 +28,9 @@ export const moleculeParser: Parser<Molecule> = oneOf([
2928 lazy ( ( ) => sugaredFunction ) ,
3029] )
3130
32- // During parsing molecules and properties are represented as nested arrays (of key/value pairs).
33- // The following utilities make it easier to work with such a structure.
34-
35- const flat = < Output > ( theParser : Parser < readonly Output [ ] > ) =>
36- map ( theParser , output => output . flat ( ) )
37-
38- const omit = ( theParser : Parser < unknown > ) => as ( theParser , [ ] )
39-
4031const optional = < Output > (
41- theParser : Parser < readonly Output [ ] > ,
42- ) : Parser < readonly Output [ ] > => oneOf ( [ theParser , omit ( nothing ) ] )
43-
44- const withoutOmittedOutputs = < Output > (
45- theParser : Parser < readonly ( readonly Output [ ] ) [ ] > ,
46- ) => map ( theParser , output => output . filter ( output => output . length > 0 ) )
32+ parser : Parser < NonNullable < Output > > ,
33+ ) : Parser < Output | undefined > => oneOf ( [ parser , nothing ] )
4734
4835// Keyless properties are automatically assigned numeric indexes, which uses some mutable state.
4936type Indexer = ( ) => string
@@ -57,59 +44,50 @@ const makeIncrementingIndexer = (): Indexer => {
5744 }
5845}
5946
60- // Language-specific parsers follow.
61-
6247const propertyDelimiter = oneOf ( [
63- sequence ( [ optional ( omit ( trivia ) ) , literal ( ',' ) , optional ( omit ( trivia ) ) ] ) ,
48+ sequence ( [ optional ( trivia ) , literal ( ',' ) , optional ( trivia ) ] ) ,
6449 trivia ,
6550] )
6651
67- const sugaredLookup : Parser < PartialMolecule > =
68- optionallySurroundedByParentheses (
69- map (
70- sequence ( [ literal ( ':' ) , oneOf ( [ atomParser , moleculeParser ] ) ] ) ,
71- ( [ _colon , query ] ) => ( { 0 : '@lookup' , query } ) ,
72- ) ,
73- )
52+ const sugaredLookup : Parser < Molecule > = optionallySurroundedByParentheses (
53+ map (
54+ sequence ( [ literal ( ':' ) , oneOf ( [ atomParser , moleculeParser ] ) ] ) ,
55+ ( [ _colon , query ] ) => ( { 0 : '@lookup' , query } ) ,
56+ ) ,
57+ )
7458
75- const sugaredFunction : Parser < PartialMolecule > =
76- optionallySurroundedByParentheses (
77- map (
78- flat (
79- sequence ( [
80- map ( atomParser , output => [ output ] ) ,
81- omit ( trivia ) ,
82- omit ( literal ( '=>' ) ) ,
83- omit ( trivia ) ,
84- map (
85- lazy ( ( ) => propertyValue ) ,
86- output => [ output ] ,
87- ) ,
88- ] ) ,
89- ) ,
90- ( [ parameter , body ] ) => ( {
91- 0 : '@function' ,
92- parameter,
93- body,
94- } ) ,
95- ) ,
96- )
59+ const sugaredFunction : Parser < Molecule > = optionallySurroundedByParentheses (
60+ map (
61+ sequence ( [
62+ atomParser ,
63+ trivia ,
64+ literal ( '=>' ) ,
65+ trivia ,
66+ lazy ( ( ) => propertyValue ) ,
67+ ] ) ,
68+ ( [ parameter , _trivia1 , _arrow , _trivia2 , body ] ) => ( {
69+ 0 : '@function' ,
70+ parameter,
71+ body,
72+ } ) ,
73+ ) ,
74+ )
9775
98- const sugaredApply : Parser < PartialMolecule > = map (
76+ const sugaredApply : Parser < Molecule > = map (
9977 sequence ( [
10078 oneOf ( [ sugaredLookup , lazy ( ( ) => sugaredFunction ) ] ) ,
10179 oneOrMore (
10280 sequence ( [
10381 literal ( '(' ) ,
104- optional ( omit ( trivia ) ) ,
82+ optional ( trivia ) ,
10583 lazy ( ( ) => propertyValue ) ,
106- optional ( omit ( trivia ) ) ,
84+ optional ( trivia ) ,
10785 literal ( ')' ) ,
10886 ] ) ,
10987 ) ,
11088 ] ) ,
11189 ( [ f , multipleArguments ] ) =>
112- multipleArguments . reduce < PartialMolecule > (
90+ multipleArguments . reduce < Molecule > (
11391 ( expression , [ _1 , _2 , argument , _3 , _4 ] ) => ( {
11492 0 : '@apply' ,
11593 function : expression ,
@@ -127,43 +105,44 @@ const propertyValue = oneOf([
127105 sugaredLookup ,
128106] )
129107
130- const namedProperty = flat (
131- sequence ( [
132- propertyKey ,
133- omit ( literal ( ':' ) ) ,
134- optional ( omit ( trivia ) ) ,
135- propertyValue ,
136- ] ) ,
108+ const namedProperty = map (
109+ sequence ( [ propertyKey , literal ( ':' ) , optional ( trivia ) , propertyValue ] ) ,
110+ ( [ key , _colon , _trivia , value ] ) => [ key , value ] as const ,
137111)
138112
139113const numberedProperty = ( index : Indexer ) =>
140- map ( propertyValue , value => [ index ( ) , value ] )
114+ map ( propertyValue , value => [ index ( ) , value ] as const )
141115
142116const property = ( index : Indexer ) =>
143117 optionallySurroundedByParentheses (
144118 oneOf ( [ namedProperty , numberedProperty ( index ) ] ) ,
145119 )
146120
147- const moleculeAsEntries = ( index : Indexer ) =>
148- withoutOmittedOutputs (
149- flat (
150- sequence ( [
151- omit ( literal ( '{' ) ) ,
152- // Allow initial property not preceded by a delimiter (e.g. `{a b}`).
153- map ( optional ( property ( index ) ) , property => [ property ] ) ,
154- zeroOrMore (
155- flat (
156- sequence ( [ omit ( propertyDelimiter ) , lazy ( ( ) => property ( index ) ) ] ) ,
157- ) ,
121+ const moleculeAsEntries = (
122+ index : Indexer ,
123+ ) : Parser < readonly ( readonly [ string , string | Molecule ] ) [ ] > =>
124+ map (
125+ sequence ( [
126+ literal ( '{' ) ,
127+ // Allow initial property not preceded by a delimiter (e.g. `{a b}`).
128+ optional ( property ( index ) ) ,
129+ zeroOrMore (
130+ map (
131+ sequence ( [ propertyDelimiter , property ( index ) ] ) ,
132+ ( [ _delimiter , property ] ) => property ,
158133 ) ,
159- optional ( omit ( propertyDelimiter ) ) ,
160- omit ( literal ( '}' ) ) ,
161- ] ) ,
162- ) ,
134+ ) ,
135+ optional ( propertyDelimiter ) ,
136+ literal ( '}' ) ,
137+ ] ) ,
138+ ( [
139+ _openingBrace ,
140+ optionalInitialProperty ,
141+ remainingProperties ,
142+ _delimiter ,
143+ _closingBrace ,
144+ ] ) =>
145+ optionalInitialProperty === undefined
146+ ? remainingProperties
147+ : [ optionalInitialProperty , ...remainingProperties ] ,
163148 )
164-
165- // This is a lazy workaround for `sequence` returning an array rather than a tuple with
166- // definitely-present elements.
167- type PartialMolecule = {
168- readonly [ key : Atom ] : PartialMolecule | Atom | undefined
169- }
0 commit comments