From b71b1354c3c8dcdfe6b8c79d1a170c9d1ae3759e Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 16 Mar 2025 00:51:59 +0100 Subject: [PATCH] uzu: stacks now work within () + write more tests --- packages/uzu/test/uzu.test.mjs | 23 +++++++++++++++++++++-- packages/uzu/uzu.mjs | 31 ++++++++++++++++++++----------- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/packages/uzu/test/uzu.test.mjs b/packages/uzu/test/uzu.test.mjs index c58eb1a86..33451f0cf 100644 --- a/packages/uzu/test/uzu.test.mjs +++ b/packages/uzu/test/uzu.test.mjs @@ -58,13 +58,32 @@ let desguar = (a) => { describe('uzu sugar', () => { it('should desugar []', () => expect(desguar('[a b c]')).toEqual('(seq a b c)')); - it('should desugar [] nested', () => expect(desguar('[a [b c]]')).toEqual('(seq a (seq b c))')); + it('should desugar [] nested', () => expect(desguar('[a [b c] d]')).toEqual('(seq a (seq b c) d)')); it('should desugar <>', () => expect(desguar('')).toEqual('(cat a b c)')); - it('should desugar <> nested', () => expect(desguar('>')).toEqual('(cat a (cat b c))')); + it('should desugar <> nested', () => expect(desguar(' d>')).toEqual('(cat a (cat b c) d)')); it('should desugar mixed [] <>', () => expect(desguar('[a ]')).toEqual('(seq a (cat b c))')); it('should desugar mixed <> []', () => expect(desguar('')).toEqual('(cat a (seq b c))')); + it('should desugar .', () => expect(desguar('s jazz . fast 2')).toEqual('(fast (s jazz) 2)')); + it('should desugar . seq', () => expect(desguar('[bd cp . fast 2]')).toEqual('(fast (seq bd cp) 2)')); it('should desugar . twice', () => expect(desguar('s jazz . fast 2 . slow 2')).toEqual('(slow (fast (s jazz) 2) 2)')); + it('should desugar . nested', () => expect(desguar('(s cp . fast 2)')).toEqual('(fast (s cp) 2)')); + it('should desugar . within []', () => expect(desguar('[bd cp . fast 2]')).toEqual('(fast (seq bd cp) 2)')); + it('should desugar . within , within []', () => + expect(desguar('[bd cp . fast 2, x]')).toEqual('(stack (fast (seq bd cp) 2) x)')); + + it('should desugar . ()', () => expect(desguar('[jazz hh.(fast 2)]')).toEqual('(seq jazz (fast hh 2))')); + + it('should desugar , seq', () => expect(desguar('[bd, hh]')).toEqual('(stack bd hh)')); + it('should desugar , seq 2', () => expect(desguar('[bd, hh oh]')).toEqual('(stack bd (seq hh oh))')); + it('should desugar , seq 3', () => expect(desguar('[bd cp, hh oh]')).toEqual('(stack (seq bd cp) (seq hh oh))')); + it('should desugar , cat', () => expect(desguar('')).toEqual('(stack bd hh)')); + it('should desugar , cat 2', () => expect(desguar('')).toEqual('(stack bd (cat hh oh))')); + it('should desugar , cat 3', () => expect(desguar('')).toEqual('(stack (cat bd cp) (cat hh oh))')); + it('should desugar , ()', () => expect(desguar('(s bd, s cp)')).toEqual('(stack (s bd) (s cp))')); + it('should desugar * /', () => expect(desguar('[a b*2 c d/3 e]')).toEqual('(seq a (* b 2) c (/ d 3) e)')); + it('should desugar []*x', () => expect(desguar('[a [b c]*3]')).toEqual('(seq a (* (seq b c) 3))')); + it('should desugar README example', () => expect(desguar('s [bd hh*2 cp.(crush 4) ] . speed .8')).toEqual( '(speed (s (seq bd (* hh 2) (crush cp 4) (cat mt ht lt))) .8)', diff --git a/packages/uzu/uzu.mjs b/packages/uzu/uzu.mjs index 6a5157811..e79488a59 100644 --- a/packages/uzu/uzu.mjs +++ b/packages/uzu/uzu.mjs @@ -86,10 +86,14 @@ export class UzuParser { return children; } // Token[] => Token[][] . returns empty list if type not found - split_children(children, type) { + split_children(children, split_type, sequence_type) { + if (sequence_type) { + // if given, the first child is ignored + children = children.slice(1); + } const chunks = []; while (true) { - let commaIndex = children.findIndex((child) => child.type === type); + let commaIndex = children.findIndex((child) => child.type === split_type); if (commaIndex === -1) break; const chunk = children.slice(0, commaIndex); chunks.push(chunk); @@ -101,13 +105,11 @@ export class UzuParser { chunks.push(children); return chunks; } - desugar_stack(children) { - let [type, ...rest] = children; + desugar_stack(children, sequence_type) { // children is expected to contain seq or cat as first item - const chunks = this.split_children(rest, 'stack'); + const chunks = this.split_children(children, 'stack', sequence_type); if (!chunks.length) { - // no stack - return children; + return this.desugar_children(children); } // collect args of stack function const args = chunks.map((chunk) => { @@ -115,8 +117,14 @@ export class UzuParser { // chunks of one element can be added to the stack as is return chunk[0]; } else { - // chunks of multiple args are added to a subsequence of type - return { type: 'list', children: [type, ...chunk] }; + // chunks of multiple args + if (sequence_type) { + // if given, each chunk needs to be prefixed + // [a b, c d] => (stack (seq a b) (seq c d)) + chunk = [{ type: 'plain', value: sequence_type }, ...chunk]; + } + chunk = this.desugar_children(chunk); + return { type: 'list', children: chunk }; } }); return [{ type: 'plain', value: 'stack' }, ...args]; @@ -203,21 +211,22 @@ export class UzuParser { } parse_list() { let children = this.parse_pair('open_list', 'close_list'); + children = this.desugar_stack(children); children = this.desugar_children(children); return { type: 'list', children }; } parse_cat() { let children = this.parse_pair('open_cat', 'close_cat'); children = [{ type: 'plain', value: 'cat' }, ...children]; - children = this.desugar_children(children); children = this.desugar_stack(children, 'cat'); + children = this.desugar_children(children); return { type: 'list', children }; } parse_seq() { let children = this.parse_pair('open_seq', 'close_seq'); children = [{ type: 'plain', value: 'seq' }, ...children]; - children = this.desugar_children(children); children = this.desugar_stack(children, 'seq'); + children = this.desugar_children(children); return { type: 'list', children }; } consume(type) {