close enough for a parse fix

This commit is contained in:
kree 2026-08-06 12:37:53 -04:00
parent 10003a1167
commit 435103a304
2 changed files with 169 additions and 42 deletions

View file

@ -1,17 +1,3 @@
const example =
`Dear {{.Name}},
{{if .Attended}}
It was a pleasure to see you at the wedding.
{{- else}}
It is a shame you couldn't make it to the wedding.
{{- end}}
{{with .Gift -}}
Thank you for the lovely {{.}}.
{{end}}
Best wishes,
Josie`;
type Node = { type Node = {
type: 'Text' | 'Data' | 'Conditional' | 'Marker' | 'Container' | 'Empty' type: 'Text' | 'Data' | 'Conditional' | 'Marker' | 'Container' | 'Empty'
@ -39,13 +25,11 @@ type Node = {
parent?: Node; parent?: Node;
}; };
function parse(text: string): Node { type Template = {
let root: Node = { name: string;
type: 'Container', root: Node;
placeholders: [] };
};
return _parse(text, root);
}
function _parse(text: string, root: Node): Node { function _parse(text: string, root: Node): Node {
if (!text || text.length < 1) { if (!text || text.length < 1) {
@ -130,7 +114,7 @@ function _parse(text: string, root: Node): Node {
if (textNode.startIndex! < textNode.endIndex!) { if (textNode.startIndex! < textNode.endIndex!) {
textNode.parent = scope; textNode.parent = scope;
textNode.text = text.substring(textNode.startIndex!, textNode.endIndex!) textNode.text = text.substring(textNode.startIndex!, textNode.endIndex! + 1)
scope.placeholders!.push(textNode); scope.placeholders!.push(textNode);
} }
@ -223,35 +207,97 @@ function parseDataText(text: string): Node {
}; };
} }
function printNode(node: Node, offset='') { function _execute(node: Node, data: Record<string, any> | string, build: string): string {
if (!node) {
return build;
}
switch (node.type) { switch (node.type) {
case 'Conditional':
console.log(`${offset}${node.conditionType}`);
console.log(`${offset}Condition:\n`)
printNode(node.condition!, offset + '\t')
console.log(`${offset}Then:\n`)
printNode(node.consequent!, offset + '\t')
console.log(offset + 'Else:\n')
printNode(node.alternative!, offset + '\t')
break;
case 'Container': case 'Container':
console.log(offset + node.type) return build + node.placeholders!.map(p => _execute(p, data, '')).join('');
for (let p of node.placeholders ?? []) { case 'Conditional':
printNode(p, offset + '|__'); const condEval = _execute(node.condition!, data, '');
let value = condEval;
try {
value = JSON.parse(value);
} catch (e) {}
if (node.conditionType == 'if') {
// BREH JS BS (without the parenthesis it includes build in its ternary condition -.-)
return build + (!!value ?
_execute(node.consequent!, data, '') :
_execute(node.alternative!, data, ''))
} else {
// BREH JS BS
return build + (!!value ?
_execute(node.consequent!, value, '') :
_execute(node.alternative!, value, ''))
} }
break;
case 'Data': case 'Data':
console.log(`${offset}${node.type} [key=${node.key}]`); if (node.key == ".") {
break; if (typeof data === 'string') {
return build + data
} else {
return build + JSON.stringify(data);
}
} else {
if (typeof data === 'object') {
const inner = data[node.key!.substring(1)]
if (typeof inner === 'string') {
return build + inner
}
return build + JSON.stringify(inner);
}
throw new Error('huh?')
}
case 'Text': case 'Text':
console.log(`${offset}${node.type} [text=${JSON.stringify(node.text)}]`); return node.text!;
break;
default: default:
console.log(`${offset}${node.type}`); return build + '[NOT_IMPLEMENTED]'
} }
} }
printNode(parse(example)); export namespace Gin {
export function parse(text: string): Node {
let root: Node = {
type: 'Container',
placeholders: []
};
return _parse(text, root);
};
export function printNode(node: Node, offset='') {
switch (node.type) {
case 'Conditional':
console.log(`${offset}${node.conditionType}`);
console.log(`${offset}Condition:\n`)
printNode(node.condition!, offset + '\t')
console.log(`${offset}Then:\n`)
printNode(node.consequent!, offset + '\t')
console.log(offset + 'Else:\n')
printNode(node.alternative!, offset + '\t')
break;
case 'Container':
console.log(offset + node.type)
for (let p of node.placeholders ?? []) {
printNode(p, offset + '|__');
}
break;
case 'Data':
console.log(`${offset}${node.type} [key=${node.key}]`);
break;
case 'Text':
console.log(`${offset}${node.type} [text=${JSON.stringify(node.text)}]`);
break;
default:
console.log(`${offset}${node.type}`);
}
}
export function execute(template: Template, data: Record<string, any>) {
return _execute(template.root, data, '');
}
};
/** /**
* Stopgap :( * Stopgap :(
*/ */

81
test/util/gin.test.ts Normal file
View file

@ -0,0 +1,81 @@
import { describe, it, expect } from 'bun:test'
import { Gin } from '../../src/util/gin'
describe('Gin template porting test', () => {
it('parse', async () => {
const example =
`Dear {{.Name}},
{{if .Attended}}
It was a pleasure to see you at the wedding.
{{- else}}
It is a shame you couldn't make it to the wedding.
{{- end}}
{{with .Gift -}}
Thank you for the lovely {{.}}.
{{end}}
Best wishes,
Josie`;
const template = {
name: 'example',
root: Gin.parse(example)
};
const data = [
{
Name: 'Aunt Mildred',
Gift: 'bone china tea set',
Attended: true,
},
{
Name: 'Uncle John',
Gift: 'moleskin pants',
Attended: false,
},
{
Name: 'Cousin Rodney',
Gift: '',
Attended: false,
},
]
const expected =
`Dear Aunt Mildred,
It was a pleasure to see you at the wedding.
Thank you for the lovely bone china tea set.
Best wishes,
Josie
Dear Uncle John,
It is a shame you couldn't make it to the wedding.
Thank you for the lovely moleskin pants.
Best wishes,
Josie
Dear Cousin Rodney,
It is a shame you couldn't make it to the wedding.
Best wishes,
Josie`
const actual = data.map(r => Gin.execute(template, r)).join('\n');
console.log(actual)
expect(actual).toEqual(expected);
})
// it('parseGlob', async () => {
// const gin = new Gin();
// const paths = await gin.parseGlob('public/**/*html');
// })
// it('parseHTML', async () => {
// const gin = new Gin();
// const text = await gin.parseHTML('public/pages/movie.html');
// console.log(text);
// })
})