diff --git a/src/ark/compiler/index.ts b/src/ark/compiler/index.ts index 6fc6687..082204a 100644 --- a/src/ark/compiler/index.ts +++ b/src/ark/compiler/index.ts @@ -172,10 +172,7 @@ export function flatToJs(insts: ArkInsts, file: string | null = null): CodeWithS } else if (inst instanceof ArkContinueInst) { return sourceNode('continue\n') } else if (inst instanceof ArkYieldInst) { - return sourceNode([ - `yield ${inst.argId.description}\n`, - letAssign(inst.id, inst.argId.description!), - ]) + return sourceNode(letAssign(inst.id, `yield ${inst.argId.description}`)) } else if (inst instanceof ArkReturnInst) { return sourceNode(`return ${inst.argId.description}\n`) } else if (inst instanceof ArkLetCopyInst) { diff --git a/src/ark/interpreter.ts b/src/ark/interpreter.ts index 0223cba..3faef9a 100644 --- a/src/ark/interpreter.ts +++ b/src/ark/interpreter.ts @@ -814,7 +814,7 @@ async function evalFlat(outerArk: ArkState): Promise { inst = ark.loopStack[0] } else if (inst instanceof ArkYieldInst) { const result = mem.get(inst.argId)! - ark.inst = inst.next + ark.inst = inst if (ark.outerState === undefined || ark.outerState.continuation === undefined) { throw new ArkRuntimeError(ark, 'yield outside a generator', inst.sourceLoc) } @@ -877,6 +877,11 @@ async function evalFlat(outerArk: ArkState): Promise { callable.state.outerState = ark ark = callable.state inst = ark.inst + // If we're resuming, 'inst' pointed to the ArkYieldInst so we can + // set its result, so we need to advance to the next instruction. + if (inst instanceof ArkYieldInst) { + inst = inst.next + } } } else if (callable instanceof NativeFn) { mem.set(inst.id, callable.body(...args)) diff --git a/src/grammar/ursa.ohm b/src/grammar/ursa.ohm index 1bcd860..16926ee 100644 --- a/src/grammar/ursa.ohm +++ b/src/grammar/ursa.ohm @@ -107,6 +107,7 @@ Ursa { | Loop | For | await Exp -- await + | yield Exp? -- yield | LogicExp Assignment @@ -120,7 +121,6 @@ Ursa { | continue -- continue | launch Exp -- launch | return Exp? -- return - | yield Exp? -- yield | Exp Lets = NonemptyListOf diff --git a/src/ursa/compiler.ts b/src/ursa/compiler.ts index 85711ef..f6b2014 100644 --- a/src/ursa/compiler.ts +++ b/src/ursa/compiler.ts @@ -525,6 +525,13 @@ semantics.addOperation('toExp(a)', { return addLoc(new ArkAwait(exp.toExp(this.args.a)), this) }, + Exp_yield(yield_, exp) { + if (!this.args.a.inGenerator) { + throw new UrsaCompilerError(yield_.source, 'yield may only be used in a generator') + } + return addLoc(new ArkYield(maybeVal(this.args.a, exp)), this) + }, + Statement_break(_break, exp) { if (!this.args.a.inLoop) { throw new UrsaCompilerError(_break.source, 'break used outside a loop') @@ -548,14 +555,6 @@ semantics.addOperation('toExp(a)', { } return addLoc(new ArkReturn(maybeVal(this.args.a, exp)), this) }, - Statement_yield(yield_, exp) { - if (!this.args.a.inGenerator) { - throw new UrsaCompilerError(yield_.source, 'yield may only be used in a generator') - } else if (this.args.a.inExp) { - throw new UrsaCompilerError(yield_.source, 'yield may not be used inside an expression') - } - return addLoc(new ArkYield(maybeVal(this.args.a, exp)), this) - }, Block(_open, seq, _close) { return seq.toExp(this.args.a) diff --git a/src/ursa/examples.test.ts b/src/ursa/examples.test.ts index ed3d283..ddf8167 100644 --- a/src/ursa/examples.test.ts +++ b/src/ursa/examples.test.ts @@ -63,7 +63,11 @@ ursaTest('yield', 'test/yield', [], `\ 3 4 null -null`) +null +0 +1 +5 +5`) ursaTest('generator', 'test/generator', [], `\ 0 2 diff --git a/src/ursa/fmt.ts b/src/ursa/fmt.ts index 946c69f..728cd8f 100644 --- a/src/ursa/fmt.ts +++ b/src/ursa/fmt.ts @@ -463,6 +463,14 @@ semantics.addOperation('fmt(a)', { ) }, + Exp_yield(_yield, exp) { + return tryFormats( + this.args.a, + (a) => hSpan(['yield', ...fmtOptional(a, exp)]), + [(a) => hSpan(['yield', ...fmtOptional(a, exp)])], + ) + }, + Statement_break(_break, exp) { return tryFormats( this.args.a, @@ -487,13 +495,6 @@ semantics.addOperation('fmt(a)', { [(a) => hSpan(['return', ...fmtOptional(a, exp)])], ) }, - Statement_yield(_yield, exp) { - return tryFormats( - this.args.a, - (a) => hSpan(['yield', ...fmtOptional(a, exp)]), - [(a) => hSpan(['yield', ...fmtOptional(a, exp)])], - ) - }, Lets(lets) { return tryFormats( diff --git a/test/yield.ursa b/test/yield.ursa index 439c4e6..08d6af7 100644 --- a/test/yield.ursa +++ b/test/yield.ursa @@ -17,3 +17,15 @@ print(h()) print(h()) print(h()) print(h()) + +let totalizer = gen() { + let i = 0 + loop { + i := i + (yield i) + } +} +let t = totalizer() +print(t(0)) +print(t(1)) +print(t(4)) +print(t(0))