diff --git a/packages/uzu/test/uzu.test.mjs b/packages/uzu/test/uzu.test.mjs
index c58eb1a8..33451f0c 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 6a515781..e79488a5 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) {