From 0ceea038b45ab23deaa727caf7f0480e4f411fd4 Mon Sep 17 00:00:00 2001 From: Kefniark Date: Tue, 4 May 2021 23:24:55 +0900 Subject: [PATCH] Add loop continue + fix index --- package-lock.json | 6 ++-- package.json | 2 +- src/ako_grammar.txt | 28 +++++++++--------- src/core.ts | 1 + src/elements/conditional.ts | 2 ++ src/elements/loop.ts | 15 ++++++++-- src/interpreter.ts | 5 ++++ src/semantic.ts | 2 +- tests/loop.test.ts | 57 +++++++++++++++++++++++++++++++------ 9 files changed, 90 insertions(+), 28 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6aa6f39..64494cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2073,9 +2073,9 @@ "dev": true }, "esbuild": { - "version": "0.11.16", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.11.16.tgz", - "integrity": "sha512-34ZWjo4ouvM5cDe7uRoM9GFuMyEmpH9fnVYmomvS1cq9ED9d/0ZG1r+p4P2VbX7ihjv36zA2SWTmP8Zmt/EANA==", + "version": "0.11.18", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.11.18.tgz", + "integrity": "sha512-KD7v4N9b5B8bxPUNn/3GA9r0HWo4nJk3iwjZ+2zG1ffg+r8ig+wqj7sW6zgI6Sn4/B2FnbzqWxcAokAGGM5zwQ==", "dev": true }, "esbuild-register": { diff --git a/package.json b/package.json index 996aefe..8ab0710 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "@types/node": "^14.14.41", "@typescript-eslint/eslint-plugin": "^4.22.0", "@typescript-eslint/parser": "^4.22.0", - "esbuild": "^0.11.16", + "esbuild": "^0.11.18", "esbuild-register": "^2.5.0", "eslint": "^7.25.0", "mocha": "^8.3.2", diff --git a/src/ako_grammar.txt b/src/ako_grammar.txt index e5d8b11..3cf4f6c 100644 --- a/src/ako_grammar.txt +++ b/src/ako_grammar.txt @@ -3,7 +3,7 @@ Ako { // Statement Block = "{" Stmt* "}" - Stmt = (Metadata | Assign | AssignTask | TaskDef | Task | If | ForLoop | Expr) + Stmt = (Metadata | If | Assign | AssignTask | TaskDef | Task | ForLoop | Expr) Assign = AssignLeft | AssignAdd | AssignIncr | AssignDecr | AssignSub AssignTask = Var "=" Task @@ -12,19 +12,19 @@ Ako { AssignSub = Var "-=" Expr AssignIncr = Var "++" AssignDecr = Var "--" - Metadata = "##" Id Term + Metadata = "##" id Term - TaskDef = "task" Id (Array)? Block - Task = "@" (Id ".")? Id "(" NamedArguments ")" + TaskDef = "task" id (Array)? Block + Task = "@" (id ".")? id "(" NamedArguments ")" NamedArguments = ListOf<(KeyValue | Expr), ","> Arguments = ListOf // Expression Expr = LambdaInline | Fn | MathExpr | Term Term =Var | Number | String | bool | Array | Dictionary | Last - Fn = (Id ".")? Id "(" Arguments ")" - // Lambda = "(" ListOf ")" "=>" Block - LambdaInline = "(" ListOf ")" "=>" Expr + Fn = (id ".")? id "(" Arguments ")" + // Lambda = "(" ListOf ")" "=>" Block + LambdaInline = "(" ListOf ")" "=>" Expr // Math MathExpr = BinExpr @@ -64,25 +64,27 @@ Ako { // Loop ForLoop = Foreach | While | Infinite | Continue | Return - Foreach = "for" Id ("," Id)? ":=" Expr Block + Foreach = "for" id ("," id)? ":=" Expr Block While = "for" Expr Block Infinite = "for" Block Continue = "continue" - Return = "return" Expr + Return = "return" (Expr)? // Identifier / Variable Last = "$" Var = Var "[" Expr "]" -- subscript | Var "[" Expr? ":" Expr? "]" -- range - | Var "." Id -- select - | Id -- single - Id = ~(keyword) (letter alnum*) + | Var "." id -- select + | id -- single + + id = ~keyword char + char = letter (letter | digit | "_")* keyword = "$" | "task" | "for" | "false" | "true" | "or" | "and" | "not" | "if" | "elif" | "else" | "continue" | "return" // List Array = "[" ListOf "]" Dictionary = "{" ListOf "}" - KeyValue = Id "=" Expr + KeyValue = id "=" Expr // Boolean bool = "true" | "false" diff --git a/src/core.ts b/src/core.ts index ce28290..b167e3b 100644 --- a/src/core.ts +++ b/src/core.ts @@ -15,6 +15,7 @@ export interface Stack { child: string | undefined elements: any[] elementsData: any[] + continue?: boolean result?: any } diff --git a/src/elements/conditional.ts b/src/elements/conditional.ts index 1625bbe..e1ef3df 100644 --- a/src/elements/conditional.ts +++ b/src/elements/conditional.ts @@ -7,6 +7,7 @@ export const If = { if (ctx.vm.evaluate(ctx, entry.ifCond, true)) { const block = ctx.vm.createStack(entry.ifBlock.statements, ctx.stack.parent ? ctx.stack.parent : ctx.stack.uid) entryData.meta = {block: block.uid} + console.log('CREATE IFBLOCK', entryData.meta) } // else if @@ -35,6 +36,7 @@ export const If = { const stack = ctx.vm.stacks.get(entryData.meta.block) const res = ctx.vm.updateStack(stack, timeRemains, true) if (res.done && 'result' in stack) ctx.vm.callReturn(ctx, stack.result) + if (res.done && 'continue' in stack) ctx.vm.callContinue(ctx) return res } } diff --git a/src/elements/loop.ts b/src/elements/loop.ts index 4edf893..72cf32f 100644 --- a/src/elements/loop.ts +++ b/src/elements/loop.ts @@ -23,7 +23,7 @@ export const LoopWhile = { export const LoopFor = { create: (item, index, iterator, block) => { - return {type: 'LoopFor', item, index, iterator, block} as LoopForData + return {type: 'LoopFor', item, index: index.length > 0 ? index[0] : index, iterator, block} as LoopForData }, initialize: (ctx: Context, entry: LoopForData, entryData: any) => { const block = ctx.vm.createStack(entry.block.statements, ctx.stack.parent ? ctx.stack.parent : ctx.stack.uid) @@ -66,6 +66,13 @@ export const LoopFor = { const res = ctx.vm.updateStack(stack, timeRemains, true) timeRemains = res.timeRemains + if ('continue' in stack && stack.continue) { + entryData.meta.index++ + LoopFor.next(ctx, entry, entryData, timeRemains) + stack.continue = false + continue + } + if (res.done && 'result' in stack) { ctx.vm.callReturn(ctx, stack.result) return {timeRemains, done: true} @@ -95,6 +102,10 @@ export const Block = { export const Continue = { create: () => { return {type: 'Continue'} + }, + execute: (ctx: Context, entry, entryData, timeRemains: number) => { + ctx.vm.callContinue(ctx) + return {timeRemains, done: true} } } @@ -103,7 +114,7 @@ export const Return = { return {type: 'Return', expr} }, execute: (ctx: Context, entry, entryData, timeRemains: number) => { - ctx.vm.callReturn(ctx, ctx.vm.evaluate(ctx, entry.expr, true)) + ctx.vm.callReturn(ctx, ctx.vm.evaluate(ctx, entry.expr.length > 0 ? entry.expr[0] : entry.expr, true)) return {timeRemains, done: true} } } diff --git a/src/interpreter.ts b/src/interpreter.ts index b339cd4..fab08c9 100644 --- a/src/interpreter.ts +++ b/src/interpreter.ts @@ -69,6 +69,11 @@ export class Interpreter { this.tasks.set(name, method) } + callContinue(ctx: Context): void { + ctx.stack.continue = true + ctx.stack.index = ctx.stack.elements.length + } + callReturn(ctx: Context, val: any): void { ctx.stack.result = val ctx.stack.index = ctx.stack.elements.length diff --git a/src/semantic.ts b/src/semantic.ts index de592b9..798275f 100644 --- a/src/semantic.ts +++ b/src/semantic.ts @@ -83,7 +83,7 @@ export function getGrammar(akoGrammar: string) { Return: (a, b) => AkoElement.Return.create(b.toAST()), // Var - Id: (a, b) => AkoElement.String.create(a.sourceString + b.sourceString), + id: (a) => AkoElement.String.create(a.sourceString), Var_single: (a) => AkoElement.Symbol.create(a.toAST()), Var_select: (a, b, c) => AkoElement.SymbolSelect.create(a.toAST(), c.toAST()), Var_range: (a, b, c, d, e, f) => AkoElement.SymbolRange.create(a.toAST(), c.toAST(), e.toAST()), diff --git a/tests/loop.test.ts b/tests/loop.test.ts index 5ae3e69..6b82000 100644 --- a/tests/loop.test.ts +++ b/tests/loop.test.ts @@ -12,20 +12,61 @@ for a := [1,2,3,4,5,6,7,8] { assert.strictEqual((stack.data as any)['b'], 2) }) - it('Simple loop', () => { + it('Nested Loop', () => { const {stack} = runCode(` -b = 38 -for a := [1,2,3,4,5,6,7,8] { - if a > 3 { return 0 } - b -= a +counter = 0 +for a := [1,2] { + for b := [3,4] { + for c, index := [1,2,3,4] { + counter += c + } + counter += b + } + counter += a +} + `) + assert.strictEqual((stack.data as any)['counter'], 57) + }) + + it('Nested Loop with return', () => { + const {stack} = runCode(` +counter = 0 +for a := [1,2] { + for b := [3,4] { + for c, index := [1,2,3,4] { + counter += c + if counter >= 3 { + return + } + } + counter += b + } + counter += a +} + `) + assert.strictEqual((stack.data as any)['counter'], 3) + }) + + it('Nested Loop with continue', () => { + const {stack} = runCode(` +counter = 0 +for a := [1,2] { + for b := [3,4] { + for c, index := [1,2,3,4] { + if counter >= 3 { + continue + } + counter += c + } + counter += b + } + counter += a } `) - assert.strictEqual((stack.data as any)['b'], 32) + assert.strictEqual((stack.data as any)['counter'], 20) }) - // TODO: Implement continue // TODO: Implement Infinite loop - // TODO: Check Index /* it('Check Index', () => { const {stack} = runCode(`