Skip to content

Commit 105d3f6

Browse files
committed
Implemented stringify
1 parent 682ff59 commit 105d3f6

5 files changed

Lines changed: 313 additions & 8 deletions

File tree

Source/README.md

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11

22
# HJSON   [![Badge License]][License]
33

4-
*A parser / stringifier for **[HJSON]**.*
4+
A parser / stringifier for **[HJSON]**.
55

66
<br>
77

88
## Import
99

10-
```JavaScript
10+
```js
1111
import HJson from 'https://deno.land/x/hjson/mod.ts'
1212
```
1313

1414
<br>
1515
<br>
1616

17-
## Parsing
17+
## Parse
1818

19-
```JavaScript
19+
```js
2020
import { parse } from 'https://deno.land/x/hjson/mod.ts'
2121

2222
const { log } = console;
@@ -48,9 +48,38 @@ log(parse(hjson));
4848
<br>
4949
<br>
5050

51-
## Stringification
51+
## Stringify
5252

53-
**Work In Progress**
53+
```js
54+
import { stringify } from 'https://deno.land/x/hjson/mod.ts'
55+
56+
const { log } = console;
57+
58+
const object = {
59+
some : 3 ,
60+
variables : [ 'with' , 'a' , 'lot' ] ,
61+
inside : { them : 9 }
62+
}
63+
64+
log(stringify(hjson));
65+
66+
67+
/*
68+
* {
69+
* some : 3
70+
*
71+
* variables : [
72+
* with
73+
* a
74+
* lot
75+
* ]
76+
*
77+
* inside : {
78+
* them : 9
79+
* }
80+
* }
81+
*/
82+
```
5483

5584
<br>
5685

Source/Stringify/Encode.js

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
2+
3+
export default function stringify(object,options = {}){
4+
5+
options.style ??= 'modern';
6+
7+
if(options.style !== 'modern')
8+
throw 'Unsupported style';
9+
10+
const base = modern(object);
11+
12+
const pretty = base
13+
.replace(/\[\s+\{/g,'[{')
14+
.replace(/\}\s+\]/g,'}]')
15+
.replace(/\}\s*,\s*\{/g,'},{');
16+
// .replace(//g,`\n$1:\n\n'''`);
17+
// .replace(/\[\{\s*/g,'[{\n\n');
18+
19+
return [... indent(pretty) ].join('\n');
20+
}
21+
22+
function * indent(pretty){
23+
24+
const lines = pretty.split('\n');
25+
26+
let level = 0;
27+
let padding = ' ';
28+
let open = false;
29+
30+
31+
for(const line of lines){
32+
33+
const padded = () =>
34+
padding.repeat(Math.max(level,0)) + line;
35+
36+
const trimmed = line.trim();
37+
38+
if(trimmed.length < 1){
39+
yield padded();
40+
continue;
41+
}
42+
43+
if(trimmed.startsWith(`'''`)){
44+
45+
if(!open)
46+
level++
47+
48+
yield padded();
49+
50+
if(open)
51+
level--;
52+
53+
open = !open;
54+
continue;
55+
}
56+
57+
if(
58+
trimmed.startsWith('{') ||
59+
trimmed.endsWith('{') ||
60+
trimmed.endsWith('[')
61+
){
62+
yield padded();
63+
level++;
64+
continue;
65+
}
66+
67+
if(
68+
trimmed.startsWith('}') ||
69+
trimmed.endsWith('}') ||
70+
trimmed.endsWith(']')
71+
){
72+
level--;
73+
yield padded();
74+
continue;
75+
}
76+
77+
yield padded();
78+
}
79+
}
80+
81+
82+
function modern(object){
83+
84+
let lines = toObject(object);
85+
86+
return lines.join('\n');
87+
}
88+
89+
function toItem(item){
90+
91+
switch(typeof item){
92+
case 'number' :
93+
return [ `${ item }` ];
94+
case 'boolean' :
95+
return [ (item) ? 'true' : 'false' ];
96+
case 'string' :
97+
98+
if(item.includes('\n'))
99+
return [ `'''` , item , `'''` ];
100+
101+
if(
102+
item.startsWith('#') ||
103+
/[,:\[\]\{\}]/g.test(item)
104+
) return [ `'${ item }'`];
105+
106+
return [ `${ item }` ];
107+
case 'array' :
108+
return toArray(item);
109+
case 'object':
110+
return toObject(item);
111+
}
112+
}
113+
//
114+
// function indent(string){
115+
// return ' ' + string;
116+
// }
117+
118+
function toArray(array){
119+
120+
const lines = [ '[' ];
121+
122+
for(const item of array)
123+
lines.push(...toItem(item));//.map(indent)
124+
125+
lines.push(']');
126+
127+
return (lines.length === 2)
128+
? [ '[]' ]
129+
: lines ;
130+
}
131+
132+
function toObject(object){
133+
134+
const longestFirst = ([ a ],[ b ]) => b.length - a.length;
135+
136+
const
137+
entries = Object.entries(object) ,
138+
simple = entries
139+
.filter(([ _ , value ]) => [ 'string' , 'number' , 'boolean' ].includes(typeof value))
140+
.sort(longestFirst)
141+
.map(formString)
142+
.flat();
143+
// .map((string) => ' ' + string);
144+
145+
const complex = entries
146+
.filter(([ _ , value ]) => [ 'object' , 'array' ].includes(typeof value))
147+
.sort(longestFirst)
148+
.map(toComplex)
149+
.flat();
150+
// .map((string) => ' ' + string);
151+
152+
complex.shift();
153+
154+
const lines = [ '{' ];
155+
156+
if(simple.length && complex.length)
157+
lines.push('');
158+
159+
if(simple.length)
160+
lines.push(...simple);
161+
162+
if(simple.length && complex.length)
163+
lines.push('');
164+
165+
if(complex.length)
166+
lines.push(...complex);
167+
168+
lines.push('}');
169+
170+
return (lines.length === 2)
171+
? [ '{}' ]
172+
: lines ;
173+
}
174+
175+
function toComplex([ key , value ]){
176+
177+
if(Array.isArray(value)){
178+
179+
const [ first , ...rest ] = toArray(value);
180+
181+
return [
182+
'' ,
183+
`${ toString(key) } : ${ first }`,
184+
...rest
185+
];
186+
} else {
187+
const [ first , ...rest ] = toObject(value);
188+
189+
return [
190+
'' ,
191+
`${ toString(key) } : ${ first }`,
192+
...rest
193+
];
194+
}
195+
}
196+
197+
198+
function formString([ key , value ]){
199+
switch(typeof value){
200+
case 'number' :
201+
return [ `${ toString(key) } : ${ value }` ];
202+
case 'boolean' :
203+
return [ `${ toString(key) } : ${ (value)
204+
? ['true']
205+
: ['false'] }` ]
206+
case 'string' :
207+
208+
if(value.includes('\n'))
209+
return [ '' , `${ toString(key) }:` , '' , `'''` , value , `'''` ];
210+
211+
if(
212+
value.startsWith('#') ||
213+
/[,:\[\]\{\}]/g.test(value)
214+
) return [ `${ toString(key) } : '${ value }'`];
215+
216+
return [ `${ toString(key) } : ${ value }` ];
217+
}
218+
}
219+
220+
function toString(value){
221+
222+
if(
223+
value.startsWith('#') ||
224+
/[,:\[\]\{\}]/g.test(value)
225+
) return `'${ value }'`;
226+
227+
return value;
228+
}

Source/Stringify/mod.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
import encode from './Encode.js'
3+
4+
5+
export default function stringify ( value : object , options : object ) : string {
6+
return encode(value,options);
7+
}

Source/mod.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11

22
import decode from './Decode.js'
33

4-
54
export function parse ( text : string ) : object {
65
return decode(text);
76
}
87

9-
export default { parse }
8+
9+
import encode from './Stringify/mod.ts'
10+
11+
export function stringify ( value : object , options : object ) : string {
12+
return encode(value,options);
13+
}
14+
15+
16+
export default { parse , stringify }

Tests/Stringify/Basic.test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
2+
import { stringify } from '../../Source/mod.ts'
3+
4+
const { log } = console;
5+
6+
7+
const object = {
8+
difficulty : 3 ,
9+
name : 'lab' ,
10+
planet : 'pluto' ,
11+
sector : 3 ,
12+
captureWave : 10 ,
13+
14+
research : {
15+
16+
name : '\ntesting \'wow\'\n' ,
17+
18+
parent : 'nuclearFacility' ,
19+
20+
objectives: [{
21+
preset : 'nuclearFacility' ,
22+
type : 'SectorComplete'
23+
}]
24+
}
25+
}
26+
27+
28+
29+
Deno.test('Basic Stringification Test',() => {
30+
31+
const hjson = stringify(object);
32+
33+
log(hjson);
34+
});

0 commit comments

Comments
 (0)