From 0a7f0bc095465f5812341bad4c172b7a7d3055f5 Mon Sep 17 00:00:00 2001 From: Lumi Pakkanen Date: Tue, 18 Jun 2024 09:18:55 +0300 Subject: [PATCH] Make return statements legal in block expressions --- documentation/advanced-dsl.md | 11 +++++++++++ src/parser/__tests__/expression.spec.ts | 12 ++++++++++++ src/parser/expression.ts | 4 +++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/documentation/advanced-dsl.md b/documentation/advanced-dsl.md index cf76db4c..74744676 100644 --- a/documentation/advanced-dsl.md +++ b/documentation/advanced-dsl.md @@ -50,6 +50,17 @@ Blocks are valid expressions and evaluate to arrays. They have the lowest preced (* $ = [10, 20, 30] *) ``` +#### Block expression return value +Use a `return` statement inside a block expression to evaluate to the returned value. +```ocaml +const foo = { + const bar = 2 + const baz = 3 + return bar + baz +} +(* const foo = 5 *) +``` + ### Parent scale The current scale of the parent block can be accessed using `$$`. diff --git a/src/parser/__tests__/expression.spec.ts b/src/parser/__tests__/expression.spec.ts index 03d5ad38..79349cba 100644 --- a/src/parser/__tests__/expression.spec.ts +++ b/src/parser/__tests__/expression.spec.ts @@ -2460,6 +2460,18 @@ describe('SonicWeave expression evaluator', () => { expect(interval.value).toBeInstanceOf(TimeReal); expect(interval.valueOf()).toBeCloseTo(1.000000745, 10); }); + + it('allows return from block expressions', () => { + const foo = evaluate(` + const foo = ({ + const ba = "ba" + return ba "r" + throw "not executed" + }) + foo + `); + expect(foo).toBe('bar'); + }); }); describe('Poor grammar / Fun with "<"', () => { diff --git a/src/parser/expression.ts b/src/parser/expression.ts index efa5d503..04184c45 100644 --- a/src/parser/expression.ts +++ b/src/parser/expression.ts @@ -429,7 +429,9 @@ export class ExpressionVisitor { const scale = this.currentScale; subVisitor.mutables.set('$$', scale); const interrupt = subVisitor.executeStatements(node.body); - if (interrupt) { + if (interrupt?.type === 'ReturnStatement') { + return interrupt.value; + } else if (interrupt) { throw new Error('Illegal interupt.'); } return subVisitor.currentScale;