From 8b43bf9eaf3b205b3ae1b9200f54ca0f737a6d3d Mon Sep 17 00:00:00 2001 From: arakur Date: Tue, 24 Oct 2023 23:52:44 +0900 Subject: [PATCH 1/5] Fix parser bug on property access of a parenthesized expression. --- src/parser/parser.peggy | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/parser/parser.peggy b/src/parser/parser.peggy index 2c55e520..4aacdbeb 100644 --- a/src/parser/parser.peggy +++ b/src/parser/parser.peggy @@ -308,7 +308,13 @@ Not Chain = e:Expr3 chain:(CallChain / IndexChain / PropChain)+ -{ return { ...e, chain }; } +{ + if (e.chain) { + return { ...e, chain: [...e.chain, ...chain] }; + } else { + return { ...e, chain }; + } +} CallChain = "(" _* args:CallArgs? _* ")" From 04086cb1acefb2531dab23a16bfff4b2126aa212 Mon Sep 17 00:00:00 2001 From: arakur Date: Wed, 25 Oct 2023 00:30:18 +0900 Subject: [PATCH 2/5] Added tests. --- test/index.ts | 1653 ++++++++++++++++++++++++++++--------------------- 1 file changed, 942 insertions(+), 711 deletions(-) diff --git a/test/index.ts b/test/index.ts index 369d3f24..ecff35ce 100644 --- a/test/index.ts +++ b/test/index.ts @@ -3,23 +3,39 @@ * Tests! */ -import * as assert from 'assert'; -import { Parser, Interpreter, utils, errors, Ast } from '../src'; -import { NUM, STR, NULL, ARR, OBJ, BOOL, TRUE, FALSE, ERROR ,FN_NATIVE } from '../src/interpreter/value'; +import * as assert from "assert"; +import { Parser, Interpreter, utils, errors, Ast } from "../src"; +import { + NUM, + STR, + NULL, + ARR, + OBJ, + BOOL, + TRUE, + FALSE, + ERROR, + FN_NATIVE, +} from "../src/interpreter/value"; +import { PropertyAccessChain, PropertyAccessExpression } from "typescript"; let { AiScriptRuntimeError, AiScriptIndexOutOfRangeError } = errors; -const exe = (program: string): Promise => new Promise((ok, err) => { - const aiscript = new Interpreter({}, { - out(value) { - ok(value); - }, - maxStep: 9999, - }); +const exe = (program: string): Promise => + new Promise((ok, err) => { + const aiscript = new Interpreter( + {}, + { + out(value) { + ok(value); + }, + maxStep: 9999, + } + ); - const parser = new Parser(); - const ast = parser.parse(program); - aiscript.exec(ast).catch(err); -}); + const parser = new Parser(); + const ast = parser.parse(program); + aiscript.exec(ast).catch(err); + }); const getMeta = (program: string) => { const parser = new Parser(); @@ -35,22 +51,23 @@ const eq = (a, b) => { assert.deepEqual(a.value, b.value); }; -test.concurrent('Hello, world!', async () => { +test.concurrent("Hello, world!", async () => { const res = await exe('<: "Hello, world!"'); - eq(res, STR('Hello, world!')); + eq(res, STR("Hello, world!")); }); -test.concurrent('empty script', async () => { +test.concurrent("empty script", async () => { const parser = new Parser(); - const ast = parser.parse(''); + const ast = parser.parse(""); assert.deepEqual(ast, []); }); -describe('Interpreter', () => { - describe('Scope', () => { - test.concurrent('getAll', async () => { +describe("Interpreter", () => { + describe("Scope", () => { + test.concurrent("getAll", async () => { const aiscript = new Interpreter({}); - await aiscript.exec(Parser.parse(` + await aiscript.exec( + Parser.parse(` let a = 1 @b() { let x = a + 1 @@ -60,73 +77,88 @@ describe('Interpreter', () => { var y = 2 } var c = true - `)); + `) + ); const vars = aiscript.scope.getAll(); - assert.ok(vars.get('a') != null); - assert.ok(vars.get('b') != null); - assert.ok(vars.get('c') != null); - assert.ok(vars.get('x') == null); - assert.ok(vars.get('y') == null); + assert.ok(vars.get("a") != null); + assert.ok(vars.get("b") != null); + assert.ok(vars.get("c") != null); + assert.ok(vars.get("x") == null); + assert.ok(vars.get("y") == null); }); }); }); -describe('error handler', () => { - test.concurrent('error from outside caller', async () => { +describe("error handler", () => { + test.concurrent("error from outside caller", async () => { let outsideCaller: () => Promise = async () => {}; let errCount: number = 0; - const aiscript = new Interpreter({ - emitError: FN_NATIVE((_args, _opts) => { - throw Error('emitError'); - }), - genOutsideCaller: FN_NATIVE(([fn], opts) => { - utils.assertFunction(fn); - outsideCaller = async () => { - opts.topCall(fn, []); - }; - }), - }, { - err(e) { errCount++ }, - }); - await aiscript.exec(Parser.parse(` + const aiscript = new Interpreter( + { + emitError: FN_NATIVE((_args, _opts) => { + throw Error("emitError"); + }), + genOutsideCaller: FN_NATIVE(([fn], opts) => { + utils.assertFunction(fn); + outsideCaller = async () => { + opts.topCall(fn, []); + }; + }), + }, + { + err(e) { + errCount++; + }, + } + ); + await aiscript.exec( + Parser.parse(` genOutsideCaller(emitError) - `)); + `) + ); assert.strictEqual(errCount, 0); await outsideCaller(); assert.strictEqual(errCount, 1); }); - test.concurrent('array.map calls the handler just once', async () => { + test.concurrent("array.map calls the handler just once", async () => { let errCount: number = 0; - const aiscript = new Interpreter({}, { - err(e) { errCount++ }, - }); - await aiscript.exec(Parser.parse(` + const aiscript = new Interpreter( + {}, + { + err(e) { + errCount++; + }, + } + ); + await aiscript.exec( + Parser.parse(` Core:range(1,5).map(@(){ hoge }) - `)); + `) + ); assert.strictEqual(errCount, 1); }); }); -describe('ops', () => { - test.concurrent('==', async () => { - eq(await exe('<: (1 == 1)'), BOOL(true)); - eq(await exe('<: (1 == 2)'), BOOL(false)); +describe("ops", () => { + test.concurrent("==", async () => { + eq(await exe("<: (1 == 1)"), BOOL(true)); + eq(await exe("<: (1 == 2)"), BOOL(false)); }); - test.concurrent('!=', async () => { - eq(await exe('<: (1 != 2)'), BOOL(true)); - eq(await exe('<: (1 != 1)'), BOOL(false)); + test.concurrent("!=", async () => { + eq(await exe("<: (1 != 2)"), BOOL(true)); + eq(await exe("<: (1 != 1)"), BOOL(false)); }); - test.concurrent('&&', async () => { - eq(await exe('<: (true && true)'), BOOL(true)); - eq(await exe('<: (true && false)'), BOOL(false)); - eq(await exe('<: (false && true)'), BOOL(false)); - eq(await exe('<: (false && false)'), BOOL(false)); - eq(await exe('<: (false && null)'), BOOL(false)); + test.concurrent("&&", async () => { + eq(await exe("<: (true && true)"), BOOL(true)); + eq(await exe("<: (true && false)"), BOOL(false)); + eq(await exe("<: (false && true)"), BOOL(false)); + eq(await exe("<: (false && false)"), BOOL(false)); + eq(await exe("<: (false && null)"), BOOL(false)); try { - await exe('<: (true && null)'); + await exe("<: (true && null)"); } catch (e) { assert.ok(e instanceof AiScriptRuntimeError); return; @@ -146,7 +178,7 @@ describe('ops', () => { <: tmp `), NULL - ) + ); eq( await exe(` @@ -162,19 +194,19 @@ describe('ops', () => { <: tmp `), BOOL(true) - ) + ); assert.fail(); }); - test.concurrent('||', async () => { - eq(await exe('<: (true || true)'), BOOL(true)); - eq(await exe('<: (true || false)'), BOOL(true)); - eq(await exe('<: (false || true)'), BOOL(true)); - eq(await exe('<: (false || false)'), BOOL(false)); - eq(await exe('<: (true || null)'), BOOL(true)); + test.concurrent("||", async () => { + eq(await exe("<: (true || true)"), BOOL(true)); + eq(await exe("<: (true || false)"), BOOL(true)); + eq(await exe("<: (false || true)"), BOOL(true)); + eq(await exe("<: (false || false)"), BOOL(false)); + eq(await exe("<: (true || null)"), BOOL(true)); try { - await exe('<: (false || null)'); + await exe("<: (false || null)"); } catch (e) { assert.ok(e instanceof AiScriptRuntimeError); return; @@ -194,7 +226,7 @@ describe('ops', () => { <: tmp `), NULL - ) + ); eq( await exe(` @@ -210,107 +242,106 @@ describe('ops', () => { <: tmp `), BOOL(true) - ) + ); assert.fail(); }); - test.concurrent('+', async () => { - eq(await exe('<: (1 + 1)'), NUM(2)); + test.concurrent("+", async () => { + eq(await exe("<: (1 + 1)"), NUM(2)); }); - test.concurrent('-', async () => { - eq(await exe('<: (1 - 1)'), NUM(0)); + test.concurrent("-", async () => { + eq(await exe("<: (1 - 1)"), NUM(0)); }); - test.concurrent('*', async () => { - eq(await exe('<: (1 * 1)'), NUM(1)); + test.concurrent("*", async () => { + eq(await exe("<: (1 * 1)"), NUM(1)); }); - test.concurrent('^', async () => { - eq(await exe('<: (1 ^ 0)'), NUM(1)); + test.concurrent("^", async () => { + eq(await exe("<: (1 ^ 0)"), NUM(1)); }); - test.concurrent('/', async () => { - eq(await exe('<: (1 / 1)'), NUM(1)); + test.concurrent("/", async () => { + eq(await exe("<: (1 / 1)"), NUM(1)); }); - test.concurrent('%', async () => { - eq(await exe('<: (1 % 1)'), NUM(0)); + test.concurrent("%", async () => { + eq(await exe("<: (1 % 1)"), NUM(0)); }); - test.concurrent('>', async () => { - eq(await exe('<: (2 > 1)'), BOOL(true)); - eq(await exe('<: (1 > 1)'), BOOL(false)); - eq(await exe('<: (0 > 1)'), BOOL(false)); + test.concurrent(">", async () => { + eq(await exe("<: (2 > 1)"), BOOL(true)); + eq(await exe("<: (1 > 1)"), BOOL(false)); + eq(await exe("<: (0 > 1)"), BOOL(false)); }); - test.concurrent('<', async () => { - eq(await exe('<: (2 < 1)'), BOOL(false)); - eq(await exe('<: (1 < 1)'), BOOL(false)); - eq(await exe('<: (0 < 1)'), BOOL(true)); + test.concurrent("<", async () => { + eq(await exe("<: (2 < 1)"), BOOL(false)); + eq(await exe("<: (1 < 1)"), BOOL(false)); + eq(await exe("<: (0 < 1)"), BOOL(true)); }); - test.concurrent('>=', async () => { - eq(await exe('<: (2 >= 1)'), BOOL(true)); - eq(await exe('<: (1 >= 1)'), BOOL(true)); - eq(await exe('<: (0 >= 1)'), BOOL(false)); + test.concurrent(">=", async () => { + eq(await exe("<: (2 >= 1)"), BOOL(true)); + eq(await exe("<: (1 >= 1)"), BOOL(true)); + eq(await exe("<: (0 >= 1)"), BOOL(false)); }); - test.concurrent('<=', async () => { - eq(await exe('<: (2 <= 1)'), BOOL(false)); - eq(await exe('<: (1 <= 1)'), BOOL(true)); - eq(await exe('<: (0 <= 1)'), BOOL(true)); + test.concurrent("<=", async () => { + eq(await exe("<: (2 <= 1)"), BOOL(false)); + eq(await exe("<: (1 <= 1)"), BOOL(true)); + eq(await exe("<: (0 <= 1)"), BOOL(true)); }); - test.concurrent('precedence', async () => { - eq(await exe('<: 1 + 2 * 3 + 4'), NUM(11)); - eq(await exe('<: 1 + 4 / 4 + 1'), NUM(3)); - eq(await exe('<: 1 + 1 == 2 && 2 * 2 == 4'), BOOL(true)); - eq(await exe('<: (1 + 1) * 2'), NUM(4)); + test.concurrent("precedence", async () => { + eq(await exe("<: 1 + 2 * 3 + 4"), NUM(11)); + eq(await exe("<: 1 + 4 / 4 + 1"), NUM(3)); + eq(await exe("<: 1 + 1 == 2 && 2 * 2 == 4"), BOOL(true)); + eq(await exe("<: (1 + 1) * 2"), NUM(4)); }); - test.concurrent('negative numbers', async () => { - eq(await exe('<: 1+-1'), NUM(0)); - eq(await exe('<: 1--1'), NUM(2));//反直観的、禁止される可能性がある? - eq(await exe('<: -1*-1'), NUM(1)); - eq(await exe('<: -1==-1'), BOOL(true)); - eq(await exe('<: 1>-1'), BOOL(true)); - eq(await exe('<: -1<1'), BOOL(true)); + test.concurrent("negative numbers", async () => { + eq(await exe("<: 1+-1"), NUM(0)); + eq(await exe("<: 1--1"), NUM(2)); //反直観的、禁止される可能性がある? + eq(await exe("<: -1*-1"), NUM(1)); + eq(await exe("<: -1==-1"), BOOL(true)); + eq(await exe("<: 1>-1"), BOOL(true)); + eq(await exe("<: -1<1"), BOOL(true)); }); - }); -describe('Infix expression', () => { - test.concurrent('simple infix expression', async () => { - eq(await exe('<: 0 < 1'), BOOL(true)); - eq(await exe('<: 1 + 1'), NUM(2)); +describe("Infix expression", () => { + test.concurrent("simple infix expression", async () => { + eq(await exe("<: 0 < 1"), BOOL(true)); + eq(await exe("<: 1 + 1"), NUM(2)); }); - test.concurrent('combination', async () => { - eq(await exe('<: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10'), NUM(55)); - eq(await exe('<: Core:add(1, 3) * Core:mul(2, 5)'), NUM(40)); + test.concurrent("combination", async () => { + eq(await exe("<: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10"), NUM(55)); + eq(await exe("<: Core:add(1, 3) * Core:mul(2, 5)"), NUM(40)); }); - test.concurrent('use parentheses to distinguish expr', async () => { - eq(await exe('<: (1 + 10) * (2 + 5)'), NUM(77)); + test.concurrent("use parentheses to distinguish expr", async () => { + eq(await exe("<: (1 + 10) * (2 + 5)"), NUM(77)); }); - test.concurrent('syntax symbols vs infix operators', async () => { + test.concurrent("syntax symbols vs infix operators", async () => { const res = await exe(` <: match true { 1 == 1 => "true" 1 < 1 => "false" } `); - eq(res, STR('true')); + eq(res, STR("true")); }); - test.concurrent('number + if expression', async () => { - eq(await exe('<: 1 + if true 1 else 2'), NUM(2)); + test.concurrent("number + if expression", async () => { + eq(await exe("<: 1 + if true 1 else 2"), NUM(2)); }); - test.concurrent('number + match expression', async () => { + test.concurrent("number + match expression", async () => { const res = await exe(` <: 1 + match 2 == 2 { true => 3 @@ -320,11 +351,11 @@ describe('Infix expression', () => { eq(res, NUM(4)); }); - test.concurrent('eval + eval', async () => { - eq(await exe('<: eval { 1 } + eval { 1 }'), NUM(2)); + test.concurrent("eval + eval", async () => { + eq(await exe("<: eval { 1 } + eval { 1 }"), NUM(2)); }); - test.concurrent('disallow line break', async () => { + test.concurrent("disallow line break", async () => { try { await exe(` <: 1 + @@ -337,14 +368,17 @@ describe('Infix expression', () => { assert.fail(); }); - test.concurrent('escaped line break', async () => { - eq(await exe(` + test.concurrent("escaped line break", async () => { + eq( + await exe(` <: 1 + \\ 1 + 1 - `), NUM(3)); + `), + NUM(3) + ); }); - test.concurrent('infix-to-fncall on namespace', async () => { + test.concurrent("infix-to-fncall on namespace", async () => { eq( await exe(` :: Hoge { @@ -359,8 +393,8 @@ describe('Infix expression', () => { }); }); -describe('Comment', () => { - test.concurrent('single line comment', async () => { +describe("Comment", () => { + test.concurrent("single line comment", async () => { const res = await exe(` // let a = ... let a = 42 @@ -369,7 +403,7 @@ describe('Comment', () => { eq(res, NUM(42)); }); - test.concurrent('multi line comment', async () => { + test.concurrent("multi line comment", async () => { const res = await exe(` /* variable declaration here... let a = ... @@ -380,7 +414,7 @@ describe('Comment', () => { eq(res, NUM(42)); }); - test.concurrent('multi line comment 2', async () => { + test.concurrent("multi line comment 2", async () => { const res = await exe(` /* variable declaration here... let a = ... @@ -394,13 +428,13 @@ describe('Comment', () => { eq(res, NUM(42)); }); - test.concurrent('// as string', async () => { + test.concurrent("// as string", async () => { const res = await exe('<: "//"'); - eq(res, STR('//')); + eq(res, STR("//")); }); }); -test.concurrent('式にコロンがあってもオブジェクトと判定されない', async () => { +test.concurrent("式にコロンがあってもオブジェクトと判定されない", async () => { const res = await exe(` <: eval { Core:eq("ai", "ai") @@ -409,7 +443,7 @@ test.concurrent('式にコロンがあってもオブジェクトと判定され eq(res, BOOL(true)); }); -test.concurrent('inc', async () => { +test.concurrent("inc", async () => { const res = await exe(` var a = 0 a += 1 @@ -420,7 +454,7 @@ test.concurrent('inc', async () => { eq(res, NUM(6)); }); -test.concurrent('dec', async () => { +test.concurrent("dec", async () => { const res = await exe(` var a = 0 a -= 1 @@ -431,7 +465,7 @@ test.concurrent('dec', async () => { eq(res, NUM(-6)); }); -test.concurrent('var', async () => { +test.concurrent("var", async () => { const res = await exe(` let a = 42 <: a @@ -439,7 +473,7 @@ test.concurrent('var', async () => { eq(res, NUM(42)); }); -test.concurrent('参照が繋がらない', async () => { +test.concurrent("参照が繋がらない", async () => { const res = await exe(` var f = @() { "a" } var g = f @@ -447,11 +481,11 @@ test.concurrent('参照が繋がらない', async () => { <: g() `); - eq(res, STR('a')); + eq(res, STR("a")); }); -describe('Cannot put multiple statements in a line', () => { - test.concurrent('var def', async () => { +describe("Cannot put multiple statements in a line", () => { + test.concurrent("var def", async () => { try { await exe(` let a = 42 let b = 11 @@ -463,7 +497,7 @@ describe('Cannot put multiple statements in a line', () => { assert.fail(); }); - test.concurrent('var def (op)', async () => { + test.concurrent("var def (op)", async () => { try { await exe(` let a = 13 + 75 let b = 24 + 146 @@ -476,7 +510,7 @@ describe('Cannot put multiple statements in a line', () => { }); }); -test.concurrent('empty function', async () => { +test.concurrent("empty function", async () => { const res = await exe(` @hoge() { } <: hoge() @@ -484,7 +518,7 @@ test.concurrent('empty function', async () => { eq(res, NULL); }); -test.concurrent('empty lambda', async () => { +test.concurrent("empty lambda", async () => { const res = await exe(` let hoge = @() { } <: hoge() @@ -492,7 +526,7 @@ test.concurrent('empty lambda', async () => { eq(res, NULL); }); -test.concurrent('lambda that returns an object', async () => { +test.concurrent("lambda that returns an object", async () => { const res = await exe(` let hoge = @() {{}} <: hoge() @@ -500,7 +534,7 @@ test.concurrent('lambda that returns an object', async () => { eq(res, OBJ(new Map())); }); -test.concurrent('Closure', async () => { +test.concurrent("Closure", async () => { const res = await exe(` @store(v) { let state = v @@ -511,10 +545,10 @@ test.concurrent('Closure', async () => { let s = store("ai") <: s() `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); -test.concurrent('Closure (counter)', async () => { +test.concurrent("Closure (counter)", async () => { const res = await exe(` @create_counter() { var count = 0 @@ -537,7 +571,7 @@ test.concurrent('Closure (counter)', async () => { eq(res, NUM(3)); }); -test.concurrent('Recursion', async () => { +test.concurrent("Recursion", async () => { const res = await exe(` @fact(n) { if (n == 0) { 1 } else { (fact((n - 1)) * n) } @@ -548,8 +582,8 @@ test.concurrent('Recursion', async () => { eq(res, NUM(120)); }); -describe('Var name starts with reserved word', () => { - test.concurrent('let', async () => { +describe("Var name starts with reserved word", () => { + test.concurrent("let", async () => { const res = await exe(` @f() { let letcat = "ai" @@ -557,10 +591,10 @@ describe('Var name starts with reserved word', () => { } <: f() `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); - test.concurrent('var', async () => { + test.concurrent("var", async () => { const res = await exe(` @f() { let varcat = "ai" @@ -568,10 +602,10 @@ describe('Var name starts with reserved word', () => { } <: f() `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); - test.concurrent('return', async () => { + test.concurrent("return", async () => { const res = await exe(` @f() { let returncat = "ai" @@ -579,10 +613,10 @@ describe('Var name starts with reserved word', () => { } <: f() `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); - test.concurrent('each', async () => { + test.concurrent("each", async () => { const res = await exe(` @f() { let eachcat = "ai" @@ -590,10 +624,10 @@ describe('Var name starts with reserved word', () => { } <: f() `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); - test.concurrent('for', async () => { + test.concurrent("for", async () => { const res = await exe(` @f() { let forcat = "ai" @@ -601,10 +635,10 @@ describe('Var name starts with reserved word', () => { } <: f() `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); - test.concurrent('loop', async () => { + test.concurrent("loop", async () => { const res = await exe(` @f() { let loopcat = "ai" @@ -612,10 +646,10 @@ describe('Var name starts with reserved word', () => { } <: f() `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); - test.concurrent('break', async () => { + test.concurrent("break", async () => { const res = await exe(` @f() { let breakcat = "ai" @@ -623,10 +657,10 @@ describe('Var name starts with reserved word', () => { } <: f() `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); - test.concurrent('continue', async () => { + test.concurrent("continue", async () => { const res = await exe(` @f() { let continuecat = "ai" @@ -634,10 +668,10 @@ describe('Var name starts with reserved word', () => { } <: f() `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); - test.concurrent('if', async () => { + test.concurrent("if", async () => { const res = await exe(` @f() { let ifcat = "ai" @@ -645,10 +679,10 @@ describe('Var name starts with reserved word', () => { } <: f() `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); - test.concurrent('match', async () => { + test.concurrent("match", async () => { const res = await exe(` @f() { let matchcat = "ai" @@ -656,10 +690,10 @@ describe('Var name starts with reserved word', () => { } <: f() `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); - test.concurrent('true', async () => { + test.concurrent("true", async () => { const res = await exe(` @f() { let truecat = "ai" @@ -667,10 +701,10 @@ describe('Var name starts with reserved word', () => { } <: f() `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); - test.concurrent('false', async () => { + test.concurrent("false", async () => { const res = await exe(` @f() { let falsecat = "ai" @@ -678,10 +712,10 @@ describe('Var name starts with reserved word', () => { } <: f() `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); - test.concurrent('null', async () => { + test.concurrent("null", async () => { const res = await exe(` @f() { let nullcat = "ai" @@ -689,12 +723,12 @@ describe('Var name starts with reserved word', () => { } <: f() `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); }); -describe('name validation of reserved word', () => { - test.concurrent('def', async () => { +describe("name validation of reserved word", () => { + test.concurrent("def", async () => { try { await exe(` let let = 1 @@ -706,7 +740,7 @@ describe('name validation of reserved word', () => { assert.fail(); }); - test.concurrent('attr', async () => { + test.concurrent("attr", async () => { try { await exe(` #[let 1] @@ -719,7 +753,7 @@ describe('name validation of reserved word', () => { assert.fail(); }); - test.concurrent('ns', async () => { + test.concurrent("ns", async () => { try { await exe(` :: let { @@ -733,7 +767,7 @@ describe('name validation of reserved word', () => { assert.fail(); }); - test.concurrent('var', async () => { + test.concurrent("var", async () => { try { await exe(` let @@ -745,7 +779,7 @@ describe('name validation of reserved word', () => { assert.fail(); }); - test.concurrent('prop', async () => { + test.concurrent("prop", async () => { try { await exe(` let x = { let: 1 } @@ -758,7 +792,7 @@ describe('name validation of reserved word', () => { assert.fail(); }); - test.concurrent('meta', async () => { + test.concurrent("meta", async () => { try { await exe(` ### let 1 @@ -770,7 +804,7 @@ describe('name validation of reserved word', () => { assert.fail(); }); - test.concurrent('fn', async () => { + test.concurrent("fn", async () => { try { await exe(` @let() { 1 } @@ -783,8 +817,8 @@ describe('name validation of reserved word', () => { }); }); -describe('Object', () => { - test.concurrent('property access', async () => { +describe("Object", () => { + test.concurrent("property access", async () => { const res = await exe(` let obj = { a: { @@ -799,7 +833,7 @@ describe('Object', () => { eq(res, NUM(42)); }); - test.concurrent('property access (fn call)', async () => { + test.concurrent("property access (fn call)", async () => { const res = await exe(` @f() { 42 } @@ -816,7 +850,7 @@ describe('Object', () => { eq(res, NUM(42)); }); - test.concurrent('property assign', async () => { + test.concurrent("property assign", async () => { const res = await exe(` let obj = { a: 1 @@ -833,15 +867,23 @@ describe('Object', () => { <: obj `); - eq(res, OBJ(new Map([ - ['a', NUM(24)], - ['b', OBJ(new Map([ - ['c', NUM(2)], - ['d', OBJ(new Map([ - ['e', NUM(42)], - ]))], - ]))], - ]))); + eq( + res, + OBJ( + new Map([ + ["a", NUM(24)], + [ + "b", + OBJ( + new Map([ + ["c", NUM(2)], + ["d", OBJ(new Map([["e", NUM(42)]]))], + ]) + ), + ], + ]) + ) + ); }); /* 未実装 @@ -882,17 +924,17 @@ describe('Object', () => { */ }); -describe('Array', () => { - test.concurrent('Array item access', async () => { +describe("Array", () => { + test.concurrent("Array item access", async () => { const res = await exe(` let arr = ["ai", "chan", "kawaii"] <: arr[1] `); - eq(res, STR('chan')); + eq(res, STR("chan")); }); - test.concurrent('Array item assign', async () => { + test.concurrent("Array item assign", async () => { const res = await exe(` let arr = ["ai", "chan", "kawaii"] @@ -900,10 +942,10 @@ describe('Array', () => { <: arr `); - eq(res, ARR([STR('ai'), STR('taso'), STR('kawaii')])); + eq(res, ARR([STR("ai"), STR("taso"), STR("kawaii")])); }); - test.concurrent('Assign array item to out of range', async () => { + test.concurrent("Assign array item to out of range", async () => { try { await exe(` let arr = [1, 2, 3] @@ -911,7 +953,7 @@ describe('Array', () => { arr[3] = 4 <: null - `) + `); } catch (e) { eq(e instanceof AiScriptIndexOutOfRangeError, false); } @@ -923,13 +965,13 @@ describe('Array', () => { arr[9] = 10 <: null - `) + `); } catch (e) { eq(e instanceof AiScriptIndexOutOfRangeError, true); } }); - test.concurrent('index out of range error', async () => { + test.concurrent("index out of range error", async () => { try { await exe(` <: [42][1] @@ -942,8 +984,8 @@ describe('Array', () => { }); }); -describe('chain', () => { - test.concurrent('chain access (prop + index + call)', async () => { +describe("chain", () => { + test.concurrent("chain access (prop + index + call)", async () => { const res = await exe(` let obj = { a: { @@ -953,10 +995,10 @@ describe('chain', () => { <: obj.a.b[0]("ai") `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); - test.concurrent('chained assign left side (prop + index)', async () => { + test.concurrent("chained assign left side (prop + index)", async () => { const res = await exe(` let obj = { a: { @@ -968,15 +1010,23 @@ describe('chain', () => { <: obj `); - eq(res, OBJ(new Map([ - ['a', OBJ(new Map([ - ['b', ARR([STR('ai'), STR('taso'), STR('kawaii')])] - ]))] - ]))); + eq( + res, + OBJ( + new Map([ + [ + "a", + OBJ(new Map([["b", ARR([STR("ai"), STR("taso"), STR("kawaii")])]])), + ], + ]) + ) + ); }); - test.concurrent('chained assign right side (prop + index + call)', async () => { - const res = await exe(` + test.concurrent( + "chained assign right side (prop + index + call)", + async () => { + const res = await exe(` let obj = { a: { b: ["ai", "chan", "kawaii"]; @@ -988,10 +1038,11 @@ describe('chain', () => { <: x `); - eq(res, STR('chan')); - }); + eq(res, STR("chan")); + } + ); - test.concurrent('chained inc/dec left side (index + prop)', async () => { + test.concurrent("chained inc/dec left side (index + prop)", async () => { const res = await exe(` let arr = [ { @@ -1005,15 +1056,20 @@ describe('chain', () => { <: arr `); - eq(res, ARR([ - OBJ(new Map([ - ['a', NUM(2)], - ['b', NUM(1)] - ])) - ])); + eq( + res, + ARR([ + OBJ( + new Map([ + ["a", NUM(2)], + ["b", NUM(1)], + ]) + ), + ]) + ); }); - test.concurrent('chained inc/dec left side (prop + index)', async () => { + test.concurrent("chained inc/dec left side (prop + index)", async () => { const res = await exe(` let obj = { a: { @@ -1026,14 +1082,15 @@ describe('chain', () => { <: obj `); - eq(res, OBJ(new Map([ - ['a', OBJ(new Map([ - ['b', ARR([NUM(1), NUM(3), NUM(2)])] - ]))] - ]))); + eq( + res, + OBJ( + new Map([["a", OBJ(new Map([["b", ARR([NUM(1), NUM(3), NUM(2)])]]))]]) + ) + ); }); - test.concurrent('prop in def', async () => { + test.concurrent("prop in def", async () => { const res = await exe(` let x = @() { let obj = { @@ -1047,7 +1104,7 @@ describe('chain', () => { eq(res, NUM(1)); }); - test.concurrent('prop in return', async () => { + test.concurrent("prop in return", async () => { const res = await exe(` let x = @() { let obj = { @@ -1062,7 +1119,7 @@ describe('chain', () => { eq(res, NUM(1)); }); - test.concurrent('prop in each', async () => { + test.concurrent("prop in each", async () => { const res = await exe(` let msgs = [] let x = { a: ["ai", "chan", "kawaii"] } @@ -1072,10 +1129,10 @@ describe('chain', () => { } <: msgs `); - eq(res, ARR([STR('ai!'), STR('chan!'), STR('kawaii!')])); + eq(res, ARR([STR("ai!"), STR("chan!"), STR("kawaii!")])); }); - test.concurrent('prop in for', async () => { + test.concurrent("prop in for", async () => { const res = await exe(` let x = { times: 10, count: 0 } for (let i, x.times) { @@ -1086,33 +1143,33 @@ describe('chain', () => { eq(res, NUM(45)); }); - test.concurrent('object with index', async () => { + test.concurrent("object with index", async () => { const res = await exe(` let ai = {a: {}}['a'] ai['chan'] = 'kawaii' <: ai[{a: 'chan'}['a']] `); - eq(res, STR('kawaii')); + eq(res, STR("kawaii")); }); }); -describe('Template syntax', () => { - test.concurrent('Basic', async () => { +describe("Template syntax", () => { + test.concurrent("Basic", async () => { const res = await exe(` let str = "kawaii" <: \`Ai is {str}!\` `); - eq(res, STR('Ai is kawaii!')); + eq(res, STR("Ai is kawaii!")); }); - test.concurrent('convert to str', async () => { + test.concurrent("convert to str", async () => { const res = await exe(` <: \`1 + 1 = {(1 + 1)}\` `); - eq(res, STR('1 + 1 = 2')); + eq(res, STR("1 + 1 = 2")); }); - test.concurrent('invalid', async () => { + test.concurrent("invalid", async () => { try { await exe(` <: \`{hoge}\` @@ -1124,16 +1181,16 @@ describe('Template syntax', () => { assert.fail(); }); - test.concurrent('Escape', async () => { + test.concurrent("Escape", async () => { const res = await exe(` let message = "Hello" <: \`\\\`a\\{b\\}c\\\`\` `); - eq(res, STR('`a{b}c`')); + eq(res, STR("`a{b}c`")); }); }); -test.concurrent('Throws error when divided by zero', async () => { +test.concurrent("Throws error when divided by zero", async () => { try { await exe(` <: (0 / 0) @@ -1145,8 +1202,8 @@ test.concurrent('Throws error when divided by zero', async () => { assert.fail(); }); -describe('Function call', () => { - test.concurrent('without args', async () => { +describe("Function call", () => { + test.concurrent("without args", async () => { const res = await exe(` @f() { 42 @@ -1156,7 +1213,7 @@ describe('Function call', () => { eq(res, NUM(42)); }); - test.concurrent('with args', async () => { + test.concurrent("with args", async () => { const res = await exe(` @f(x) { x @@ -1166,7 +1223,7 @@ describe('Function call', () => { eq(res, NUM(42)); }); - test.concurrent('with args (separated by comma)', async () => { + test.concurrent("with args (separated by comma)", async () => { const res = await exe(` @f(x, y) { (x + y) @@ -1176,7 +1233,7 @@ describe('Function call', () => { eq(res, NUM(2)); }); - test.concurrent('with args (separated by space)', async () => { + test.concurrent("with args (separated by space)", async () => { const res = await exe(` @f(x y) { (x + y) @@ -1186,21 +1243,24 @@ describe('Function call', () => { eq(res, NUM(2)); }); - test.concurrent('std: throw AiScript error when required arg missing', async () => { - try { - await exe(` + test.concurrent( + "std: throw AiScript error when required arg missing", + async () => { + try { + await exe(` <: Core:eq(1) `); - } catch (e) { - assert.ok(e instanceof AiScriptRuntimeError); - return; + } catch (e) { + assert.ok(e instanceof AiScriptRuntimeError); + return; + } + assert.fail(); } - assert.fail(); - }); + ); }); -describe('Return', () => { - test.concurrent('Early return', async () => { +describe("Return", () => { + test.concurrent("Early return", async () => { const res = await exe(` @f() { if true { @@ -1211,10 +1271,10 @@ describe('Return', () => { } <: f() `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); - test.concurrent('Early return (nested)', async () => { + test.concurrent("Early return (nested)", async () => { const res = await exe(` @f() { if true { @@ -1227,10 +1287,10 @@ describe('Return', () => { } <: f() `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); - test.concurrent('Early return (nested) 2', async () => { + test.concurrent("Early return (nested) 2", async () => { const res = await exe(` @f() { if true { @@ -1250,10 +1310,10 @@ describe('Return', () => { <: g() `); - eq(res, STR('kawaii')); + eq(res, STR("kawaii")); }); - test.concurrent('Early return without block', async () => { + test.concurrent("Early return without block", async () => { const res = await exe(` @f() { if true return "ai" @@ -1262,10 +1322,10 @@ describe('Return', () => { } <: f() `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); - test.concurrent('return inside for', async () => { + test.concurrent("return inside for", async () => { const res = await exe(` @f() { var count = 0 @@ -1281,7 +1341,7 @@ describe('Return', () => { eq(res, NUM(43)); }); - test.concurrent('return inside for 2', async () => { + test.concurrent("return inside for 2", async () => { const res = await exe(` @f() { for (let i, 10) { @@ -1294,7 +1354,7 @@ describe('Return', () => { eq(res, NUM(1)); }); - test.concurrent('return inside loop', async () => { + test.concurrent("return inside loop", async () => { const res = await exe(` @f() { var count = 0 @@ -1310,7 +1370,7 @@ describe('Return', () => { eq(res, NUM(42)); }); - test.concurrent('return inside loop 2', async () => { + test.concurrent("return inside loop 2", async () => { const res = await exe(` @f() { loop { @@ -1323,8 +1383,7 @@ describe('Return', () => { eq(res, NUM(1)); }); - test.concurrent('return inside each', async () => - { + test.concurrent("return inside each", async () => { const res = await exe(` @f() { var count = 0 @@ -1340,8 +1399,7 @@ describe('Return', () => { eq(res, NUM(2)); }); - test.concurrent('return inside each 2', async () => - { + test.concurrent("return inside each 2", async () => { const res = await exe(` @f() { each (let item, ["ai", "chan", "kawaii"]) { @@ -1355,8 +1413,8 @@ describe('Return', () => { }); }); -describe('Eval', () => { - test.concurrent('returns value', async () => { +describe("Eval", () => { + test.concurrent("returns value", async () => { const res = await exe(` let foo = eval { let a = 1 @@ -1370,8 +1428,8 @@ describe('Eval', () => { }); }); -describe('exists', () => { - test.concurrent('Basic', async () => { +describe("exists", () => { + test.concurrent("Basic", async () => { const res = await exe(` let foo = null <: [(exists foo) (exists bar)] @@ -1380,8 +1438,8 @@ describe('exists', () => { }); }); -describe('if', () => { - test.concurrent('if', async () => { +describe("if", () => { + test.concurrent("if", async () => { const res1 = await exe(` var msg = "ai" if true { @@ -1389,7 +1447,7 @@ describe('if', () => { } <: msg `); - eq(res1, STR('kawaii')); + eq(res1, STR("kawaii")); const res2 = await exe(` var msg = "ai" @@ -1398,10 +1456,10 @@ describe('if', () => { } <: msg `); - eq(res2, STR('ai')); + eq(res2, STR("ai")); }); - test.concurrent('else', async () => { + test.concurrent("else", async () => { const res1 = await exe(` var msg = null if true { @@ -1411,7 +1469,7 @@ describe('if', () => { } <: msg `); - eq(res1, STR('ai')); + eq(res1, STR("ai")); const res2 = await exe(` var msg = null @@ -1422,10 +1480,10 @@ describe('if', () => { } <: msg `); - eq(res2, STR('kawaii')); + eq(res2, STR("kawaii")); }); - test.concurrent('elif', async () => { + test.concurrent("elif", async () => { const res1 = await exe(` var msg = "bebeyo" if false { @@ -1435,7 +1493,7 @@ describe('if', () => { } <: msg `); - eq(res1, STR('kawaii')); + eq(res1, STR("kawaii")); const res2 = await exe(` var msg = "bebeyo" @@ -1446,10 +1504,10 @@ describe('if', () => { } <: msg `); - eq(res2, STR('bebeyo')); + eq(res2, STR("bebeyo")); }); - test.concurrent('if ~ elif ~ else', async () => { + test.concurrent("if ~ elif ~ else", async () => { const res1 = await exe(` var msg = null if false { @@ -1461,7 +1519,7 @@ describe('if', () => { } <: msg `); - eq(res1, STR('chan')); + eq(res1, STR("chan")); const res2 = await exe(` var msg = null @@ -1474,24 +1532,24 @@ describe('if', () => { } <: msg `); - eq(res2, STR('kawaii')); + eq(res2, STR("kawaii")); }); - test.concurrent('expr', async () => { + test.concurrent("expr", async () => { const res1 = await exe(` <: if true "ai" else "kawaii" `); - eq(res1, STR('ai')); + eq(res1, STR("ai")); const res2 = await exe(` <: if false "ai" else "kawaii" `); - eq(res2, STR('kawaii')); + eq(res2, STR("kawaii")); }); }); -describe('match', () => { - test.concurrent('Basic', async () => { +describe("match", () => { + test.concurrent("Basic", async () => { const res = await exe(` <: match 2 { 1 => "a" @@ -1499,10 +1557,10 @@ describe('match', () => { 3 => "c" } `); - eq(res, STR('b')); + eq(res, STR("b")); }); - test.concurrent('When default not provided, returns null', async () => { + test.concurrent("When default not provided, returns null", async () => { const res = await exe(` <: match 42 { 1 => "a" @@ -1513,7 +1571,7 @@ describe('match', () => { eq(res, NULL); }); - test.concurrent('With default', async () => { + test.concurrent("With default", async () => { const res = await exe(` <: match 42 { 1 => "a" @@ -1522,10 +1580,10 @@ describe('match', () => { * => "d" } `); - eq(res, STR('d')); + eq(res, STR("d")); }); - test.concurrent('With block', async () => { + test.concurrent("With block", async () => { const res = await exe(` <: match 2 { 1 => 1 @@ -1540,7 +1598,7 @@ describe('match', () => { eq(res, NUM(3)); }); - test.concurrent('With return', async () => { + test.concurrent("With return", async () => { const res = await exe(` @f(x) { match x { @@ -1552,12 +1610,12 @@ describe('match', () => { } <: f(1) `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); }); -describe('loop', () => { - test.concurrent('Basic', async () => { +describe("loop", () => { + test.concurrent("Basic", async () => { const res = await exe(` var count = 0 loop { @@ -1569,7 +1627,7 @@ describe('loop', () => { eq(res, NUM(10)); }); - test.concurrent('with continue', async () => { + test.concurrent("with continue", async () => { const res = await exe(` var a = ["ai" "chan" "kawaii" "yo" "!"] var b = [] @@ -1581,12 +1639,12 @@ describe('loop', () => { } <: b `); - eq(res, ARR([STR('ai'), STR('kawaii')])); + eq(res, ARR([STR("ai"), STR("kawaii")])); }); }); -describe('for', () => { - test.concurrent('Basic', async () => { +describe("for", () => { + test.concurrent("Basic", async () => { const res = await exe(` var count = 0 for (let i, 10) { @@ -1597,7 +1655,7 @@ describe('for', () => { eq(res, NUM(55)); }); - test.concurrent('initial value', async () => { + test.concurrent("initial value", async () => { const res = await exe(` var count = 0 for (let i = 2, 10) { @@ -1608,7 +1666,7 @@ describe('for', () => { eq(res, NUM(65)); }); - test.concurrent('wuthout iterator', async () => { + test.concurrent("wuthout iterator", async () => { const res = await exe(` var count = 0 for (10) { @@ -1619,7 +1677,7 @@ describe('for', () => { eq(res, NUM(10)); }); - test.concurrent('without brackets', async () => { + test.concurrent("without brackets", async () => { const res = await exe(` var count = 0 for let i, 10 { @@ -1630,7 +1688,7 @@ describe('for', () => { eq(res, NUM(45)); }); - test.concurrent('Break', async () => { + test.concurrent("Break", async () => { const res = await exe(` var count = 0 for (let i, 20) { @@ -1642,7 +1700,7 @@ describe('for', () => { eq(res, NUM(55)); }); - test.concurrent('continue', async () => { + test.concurrent("continue", async () => { const res = await exe(` var count = 0 for (let i, 10) { @@ -1654,7 +1712,7 @@ describe('for', () => { eq(res, NUM(9)); }); - test.concurrent('single statement', async () => { + test.concurrent("single statement", async () => { const res = await exe(` var count = 0 for 10 count += 1 @@ -1663,7 +1721,7 @@ describe('for', () => { eq(res, NUM(10)); }); - test.concurrent('var name without space', async () => { + test.concurrent("var name without space", async () => { try { await exe(` for (leti, 10) { @@ -1678,8 +1736,8 @@ describe('for', () => { }); }); -describe('for of', () => { - test.concurrent('standard', async () => { +describe("for of", () => { + test.concurrent("standard", async () => { const res = await exe(` let msgs = [] each let item, ["ai", "chan", "kawaii"] { @@ -1687,10 +1745,10 @@ describe('for of', () => { } <: msgs `); - eq(res, ARR([STR('ai!'), STR('chan!'), STR('kawaii!')])); + eq(res, ARR([STR("ai!"), STR("chan!"), STR("kawaii!")])); }); - test.concurrent('Break', async () => { + test.concurrent("Break", async () => { const res = await exe(` let msgs = [] each let item, ["ai", "chan", "kawaii" "yo"] { @@ -1699,19 +1757,19 @@ describe('for of', () => { } <: msgs `); - eq(res, ARR([STR('ai!'), STR('chan!')])); + eq(res, ARR([STR("ai!"), STR("chan!")])); }); - test.concurrent('single statement', async () => { + test.concurrent("single statement", async () => { const res = await exe(` let msgs = [] each let item, ["ai", "chan", "kawaii"] msgs.push([item, "!"].join()) <: msgs `); - eq(res, ARR([STR('ai!'), STR('chan!'), STR('kawaii!')])); + eq(res, ARR([STR("ai!"), STR("chan!"), STR("kawaii!")])); }); - test.concurrent('var name without space', async () => { + test.concurrent("var name without space", async () => { try { await exe(` each letitem, ["ai", "chan", "kawaii"] { @@ -1726,8 +1784,8 @@ describe('for of', () => { }); }); -describe('not', () => { - test.concurrent('Basic', async () => { +describe("not", () => { + test.concurrent("Basic", async () => { const res = await exe(` <: !true `); @@ -1735,8 +1793,8 @@ describe('not', () => { }); }); -describe('namespace', () => { - test.concurrent('standard', async () => { +describe("namespace", () => { + test.concurrent("standard", async () => { const res = await exe(` <: Foo:bar() @@ -1744,10 +1802,10 @@ describe('namespace', () => { @bar() { "ai" } } `); - eq(res, STR('ai')); + eq(res, STR("ai")); }); - test.concurrent('self ref', async () => { + test.concurrent("self ref", async () => { const res = await exe(` <: Foo:bar() @@ -1756,10 +1814,10 @@ describe('namespace', () => { @bar() { ai } } `); - eq(res, STR('kawaii')); + eq(res, STR("kawaii")); }); - test.concurrent('cannot declare mutable variable', async () => { + test.concurrent("cannot declare mutable variable", async () => { try { await exe(` :: Foo { @@ -1774,74 +1832,77 @@ describe('namespace', () => { }); }); -describe('literal', () => { - test.concurrent('string (single quote)', async () => { +describe("literal", () => { + test.concurrent("string (single quote)", async () => { const res = await exe(` <: 'foo' `); - eq(res, STR('foo')); + eq(res, STR("foo")); }); - test.concurrent('string (double quote)', async () => { + test.concurrent("string (double quote)", async () => { const res = await exe(` <: "foo" `); - eq(res, STR('foo')); + eq(res, STR("foo")); }); - test.concurrent('Escaped double quote', async () => { + test.concurrent("Escaped double quote", async () => { const res = await exe('<: "ai saw a note \\"bebeyo\\"."'); eq(res, STR('ai saw a note "bebeyo".')); }); - test.concurrent('Escaped single quote', async () => { - const res = await exe('<: \'ai saw a note \\\'bebeyo\\\'.\''); - eq(res, STR('ai saw a note \'bebeyo\'.')); + test.concurrent("Escaped single quote", async () => { + const res = await exe("<: 'ai saw a note \\'bebeyo\\'.'"); + eq(res, STR("ai saw a note 'bebeyo'.")); }); - test.concurrent('bool (true)', async () => { + test.concurrent("bool (true)", async () => { const res = await exe(` <: true `); eq(res, BOOL(true)); }); - test.concurrent('bool (false)', async () => { + test.concurrent("bool (false)", async () => { const res = await exe(` <: false `); eq(res, BOOL(false)); }); - test.concurrent('number (Int)', async () => { + test.concurrent("number (Int)", async () => { const res = await exe(` <: 10 `); eq(res, NUM(10)); }); - test.concurrent('number (Float)', async () => { + test.concurrent("number (Float)", async () => { const res = await exe(` <: 0.5 `); eq(res, NUM(0.5)); }); - test.concurrent('arr (separated by comma)', async () => { + test.concurrent("arr (separated by comma)", async () => { const res = await exe(` <: [1, 2, 3] `); eq(res, ARR([NUM(1), NUM(2), NUM(3)])); }); - test.concurrent('arr (separated by comma) (with trailing comma)', async () => { - const res = await exe(` + test.concurrent( + "arr (separated by comma) (with trailing comma)", + async () => { + const res = await exe(` <: [1, 2, 3,] `); - eq(res, ARR([NUM(1), NUM(2), NUM(3)])); - }); + eq(res, ARR([NUM(1), NUM(2), NUM(3)])); + } + ); - test.concurrent('arr (separated by line break)', async () => { + test.concurrent("arr (separated by line break)", async () => { const res = await exe(` <: [ 1 @@ -1852,7 +1913,7 @@ describe('literal', () => { eq(res, ARR([NUM(1), NUM(2), NUM(3)])); }); - test.concurrent('arr (separated by line break and comma)', async () => { + test.concurrent("arr (separated by line break and comma)", async () => { const res = await exe(` <: [ 1, @@ -1863,46 +1924,91 @@ describe('literal', () => { eq(res, ARR([NUM(1), NUM(2), NUM(3)])); }); - test.concurrent('arr (separated by line break and comma) (with trailing comma)', async () => { - const res = await exe(` + test.concurrent( + "arr (separated by line break and comma) (with trailing comma)", + async () => { + const res = await exe(` <: [ 1, 2, 3, ] `); - eq(res, ARR([NUM(1), NUM(2), NUM(3)])); - }); + eq(res, ARR([NUM(1), NUM(2), NUM(3)])); + } + ); - test.concurrent('obj (separated by comma)', async () => { + test.concurrent("obj (separated by comma)", async () => { const res = await exe(` <: { a: 1, b: 2, c: 3 } `); - eq(res, OBJ(new Map([['a', NUM(1)], ['b', NUM(2)], ['c', NUM(3)]]))); + eq( + res, + OBJ( + new Map([ + ["a", NUM(1)], + ["b", NUM(2)], + ["c", NUM(3)], + ]) + ) + ); }); - test.concurrent('obj (separated by comma) (with trailing comma)', async () => { - const res = await exe(` + test.concurrent( + "obj (separated by comma) (with trailing comma)", + async () => { + const res = await exe(` <: { a: 1, b: 2, c: 3, } `); - eq(res, OBJ(new Map([['a', NUM(1)], ['b', NUM(2)], ['c', NUM(3)]]))); - }); + eq( + res, + OBJ( + new Map([ + ["a", NUM(1)], + ["b", NUM(2)], + ["c", NUM(3)], + ]) + ) + ); + } + ); - test.concurrent('obj (separated by semicolon)', async () => { + test.concurrent("obj (separated by semicolon)", async () => { const res = await exe(` <: { a: 1; b: 2; c: 3 } `); - eq(res, OBJ(new Map([['a', NUM(1)], ['b', NUM(2)], ['c', NUM(3)]]))); + eq( + res, + OBJ( + new Map([ + ["a", NUM(1)], + ["b", NUM(2)], + ["c", NUM(3)], + ]) + ) + ); }); - test.concurrent('obj (separated by semicolon) (with trailing semicolon)', async () => { - const res = await exe(` + test.concurrent( + "obj (separated by semicolon) (with trailing semicolon)", + async () => { + const res = await exe(` <: { a: 1; b: 2; c: 3; } `); - eq(res, OBJ(new Map([['a', NUM(1)], ['b', NUM(2)], ['c', NUM(3)]]))); - }); + eq( + res, + OBJ( + new Map([ + ["a", NUM(1)], + ["b", NUM(2)], + ["c", NUM(3)], + ]) + ) + ); + } + ); - test.concurrent('obj (separated by line break)', async () => { + test.concurrent("obj (separated by line break)", async () => { const res = await exe(` <: { a: 1 @@ -1910,10 +2016,19 @@ describe('literal', () => { c: 3 } `); - eq(res, OBJ(new Map([['a', NUM(1)], ['b', NUM(2)], ['c', NUM(3)]]))); + eq( + res, + OBJ( + new Map([ + ["a", NUM(1)], + ["b", NUM(2)], + ["c", NUM(3)], + ]) + ) + ); }); - test.concurrent('obj (separated by line break and semicolon)', async () => { + test.concurrent("obj (separated by line break and semicolon)", async () => { const res = await exe(` <: { a: 1; @@ -1921,21 +2036,42 @@ describe('literal', () => { c: 3 } `); - eq(res, OBJ(new Map([['a', NUM(1)], ['b', NUM(2)], ['c', NUM(3)]]))); + eq( + res, + OBJ( + new Map([ + ["a", NUM(1)], + ["b", NUM(2)], + ["c", NUM(3)], + ]) + ) + ); }); - test.concurrent('obj (separated by line break and semicolon) (with trailing semicolon)', async () => { - const res = await exe(` + test.concurrent( + "obj (separated by line break and semicolon) (with trailing semicolon)", + async () => { + const res = await exe(` <: { a: 1; b: 2; c: 3; } `); - eq(res, OBJ(new Map([['a', NUM(1)], ['b', NUM(2)], ['c', NUM(3)]]))); - }); + eq( + res, + OBJ( + new Map([ + ["a", NUM(1)], + ["b", NUM(2)], + ["c", NUM(3)], + ]) + ) + ); + } + ); - test.concurrent('obj and arr (separated by line break)', async () => { + test.concurrent("obj and arr (separated by line break)", async () => { const res = await exe(` <: { a: 1 @@ -1947,25 +2083,30 @@ describe('literal', () => { c: 3 } `); - eq(res, OBJ(new Map([ - ['a', NUM(1)], - ['b', ARR([NUM(1), NUM(2), NUM(3)])], - ['c', NUM(3)] - ]))); + eq( + res, + OBJ( + new Map([ + ["a", NUM(1)], + ["b", ARR([NUM(1), NUM(2), NUM(3)])], + ["c", NUM(3)], + ]) + ) + ); }); }); -describe('type declaration', () => { - test.concurrent('def', async () => { +describe("type declaration", () => { + test.concurrent("def", async () => { const res = await exe(` let abc: num = 1 var xyz: str = "abc" <: [abc xyz] `); - eq(res, ARR([NUM(1), STR('abc')])); + eq(res, ARR([NUM(1), STR("abc")])); }); - test.concurrent('fn def', async () => { + test.concurrent("fn def", async () => { const res = await exe(` @f(x: arr, y: str, z: @(num) => bool): arr { x[3] = 0 @@ -1981,18 +2122,24 @@ describe('type declaration', () => { }); }); -describe('meta', () => { - test.concurrent('default meta', async () => { +describe("meta", () => { + test.concurrent("default meta", async () => { const res = getMeta(` ### { a: 1; b: 2; c: 3; } `); - eq(res, new Map([ - [null, { - a: 1, - b: 2, - c: 3, - }] - ])); + eq( + res, + new Map([ + [ + null, + { + a: 1, + b: 2, + c: 3, + }, + ], + ]) + ); eq(res!.get(null), { a: 1, b: 2, @@ -2000,61 +2147,51 @@ describe('meta', () => { }); }); - describe('String', () => { - test.concurrent('valid', async () => { + describe("String", () => { + test.concurrent("valid", async () => { const res = getMeta(` ### x "hoge" `); - eq(res, new Map([ - ['x', 'hoge'] - ])); + eq(res, new Map([["x", "hoge"]])); }); }); - describe('Number', () => { - test.concurrent('valid', async () => { + describe("Number", () => { + test.concurrent("valid", async () => { const res = getMeta(` ### x 42 `); - eq(res, new Map([ - ['x', 42] - ])); + eq(res, new Map([["x", 42]])); }); }); - describe('Boolean', () => { - test.concurrent('valid', async () => { + describe("Boolean", () => { + test.concurrent("valid", async () => { const res = getMeta(` ### x true `); - eq(res, new Map([ - ['x', true] - ])); + eq(res, new Map([["x", true]])); }); }); - describe('Null', () => { - test.concurrent('valid', async () => { + describe("Null", () => { + test.concurrent("valid", async () => { const res = getMeta(` ### x null `); - eq(res, new Map([ - ['x', null] - ])); + eq(res, new Map([["x", null]])); }); }); - describe('Array', () => { - test.concurrent('valid', async () => { + describe("Array", () => { + test.concurrent("valid", async () => { const res = getMeta(` ### x [1 2 3] `); - eq(res, new Map([ - ['x', [1, 2, 3]] - ])); + eq(res, new Map([["x", [1, 2, 3]]])); }); - test.concurrent('invalid', async () => { + test.concurrent("invalid", async () => { try { getMeta(` ### x [1 (2 + 2) 3] @@ -2067,21 +2204,27 @@ describe('meta', () => { }); }); - describe('Object', () => { - test.concurrent('valid', async () => { + describe("Object", () => { + test.concurrent("valid", async () => { const res = getMeta(` ### x { a: 1; b: 2; c: 3; } `); - eq(res, new Map([ - ['x', { - a: 1, - b: 2, - c: 3, - }] - ])); + eq( + res, + new Map([ + [ + "x", + { + a: 1, + b: 2, + c: 3, + }, + ], + ]) + ); }); - test.concurrent('invalid', async () => { + test.concurrent("invalid", async () => { try { getMeta(` ### x { a: 1; b: (2 + 2); c: 3; } @@ -2094,8 +2237,8 @@ describe('meta', () => { }); }); - describe('Template', () => { - test.concurrent('invalid', async () => { + describe("Template", () => { + test.concurrent("invalid", async () => { try { getMeta(` ### x \`foo {bar} baz\` @@ -2108,8 +2251,8 @@ describe('meta', () => { }); }); - describe('Expression', () => { - test.concurrent('invalid', async () => { + describe("Expression", () => { + test.concurrent("invalid", async () => { try { getMeta(` ### x (1 + 1) @@ -2123,18 +2266,18 @@ describe('meta', () => { }); }); -describe('lang version', () => { - test.concurrent('number', async () => { +describe("lang version", () => { + test.concurrent("number", async () => { const res = utils.getLangVersion(` /// @2021 @f(x) { x } `); - assert.strictEqual(res, '2021'); + assert.strictEqual(res, "2021"); }); - test.concurrent('chars', async () => { + test.concurrent("chars", async () => { const res = utils.getLangVersion(` /// @ canary const a = 1 @@ -2143,20 +2286,20 @@ describe('lang version', () => { } f(a) `); - assert.strictEqual(res, 'canary'); + assert.strictEqual(res, "canary"); }); - test.concurrent('complex', async () => { + test.concurrent("complex", async () => { const res = utils.getLangVersion(` /// @ 2.0-Alpha @f(x) { x } `); - assert.strictEqual(res, '2.0-Alpha'); + assert.strictEqual(res, "2.0-Alpha"); }); - test.concurrent('no specified', async () => { + test.concurrent("no specified", async () => { const res = utils.getLangVersion(` @f(x) { x @@ -2166,8 +2309,8 @@ describe('lang version', () => { }); }); -describe('Attribute', () => { - test.concurrent('single attribute with function (str)', async () => { +describe("Attribute", () => { + test.concurrent("single attribute with function (str)", async () => { let node: Ast.Node; let attr: Ast.Attribute; const parser = new Parser(); @@ -2179,22 +2322,24 @@ describe('Attribute', () => { `); assert.equal(nodes.length, 1); node = nodes[0]; - if (node.type !== 'def') assert.fail(); - assert.equal(node.name, 'onRecieved'); + if (node.type !== "def") assert.fail(); + assert.equal(node.name, "onRecieved"); assert.equal(node.attr.length, 1); // attribute 1 attr = node.attr[0]; - if (attr.type !== 'attr') assert.fail(); - assert.equal(attr.name, 'Event'); - if (attr.value.type !== 'str') assert.fail(); - assert.equal(attr.value.value, 'Recieved'); - }); - - test.concurrent('multiple attributes with function (obj, str, bool)', async () => { - let node: Ast.Node; - let attr: Ast.Attribute; - const parser = new Parser(); - const nodes = parser.parse(` + if (attr.type !== "attr") assert.fail(); + assert.equal(attr.name, "Event"); + if (attr.value.type !== "str") assert.fail(); + assert.equal(attr.value.value, "Recieved"); + }); + + test.concurrent( + "multiple attributes with function (obj, str, bool)", + async () => { + let node: Ast.Node; + let attr: Ast.Attribute; + const parser = new Parser(); + const nodes = parser.parse(` #[Endpoint { path: "/notes/create"; }] #[Desc "Create a note."] #[Cat true] @@ -2202,44 +2347,44 @@ describe('Attribute', () => { <: text } `); - assert.equal(nodes.length, 1); - node = nodes[0]; - if (node.type !== 'def') assert.fail(); - assert.equal(node.name, 'createNote'); - assert.equal(node.attr.length, 3); - // attribute 1 - attr = node.attr[0]; - if (attr.type !== 'attr') assert.fail(); - assert.equal(attr.name, 'Endpoint'); - if (attr.value.type !== 'obj') assert.fail(); - assert.equal(attr.value.value.size, 1); - for (const [k, v] of attr.value.value) { - if (k === 'path') { - if (v.type !== 'str') assert.fail(); - assert.equal(v.value, '/notes/create'); - } - else { - assert.fail(); + assert.equal(nodes.length, 1); + node = nodes[0]; + if (node.type !== "def") assert.fail(); + assert.equal(node.name, "createNote"); + assert.equal(node.attr.length, 3); + // attribute 1 + attr = node.attr[0]; + if (attr.type !== "attr") assert.fail(); + assert.equal(attr.name, "Endpoint"); + if (attr.value.type !== "obj") assert.fail(); + assert.equal(attr.value.value.size, 1); + for (const [k, v] of attr.value.value) { + if (k === "path") { + if (v.type !== "str") assert.fail(); + assert.equal(v.value, "/notes/create"); + } else { + assert.fail(); + } } - } - // attribute 2 - attr = node.attr[1]; - if (attr.type !== 'attr') assert.fail(); - assert.equal(attr.name, 'Desc'); - if (attr.value.type !== 'str') assert.fail(); - assert.equal(attr.value.value, 'Create a note.'); - // attribute 3 - attr = node.attr[2]; - if (attr.type !== 'attr') assert.fail(); - assert.equal(attr.name, 'Cat'); - if (attr.value.type !== 'bool') assert.fail(); - assert.equal(attr.value.value, true); - }); + // attribute 2 + attr = node.attr[1]; + if (attr.type !== "attr") assert.fail(); + assert.equal(attr.name, "Desc"); + if (attr.value.type !== "str") assert.fail(); + assert.equal(attr.value.value, "Create a note."); + // attribute 3 + attr = node.attr[2]; + if (attr.type !== "attr") assert.fail(); + assert.equal(attr.name, "Cat"); + if (attr.value.type !== "bool") assert.fail(); + assert.equal(attr.value.value, true); + } + ); // TODO: attributed function in block // TODO: attribute target does not exist - test.concurrent('single attribute (no value)', async () => { + test.concurrent("single attribute (no value)", async () => { let node: Ast.Node; let attr: Ast.Attribute; const parser = new Parser(); @@ -2249,20 +2394,20 @@ describe('Attribute', () => { `); assert.equal(nodes.length, 1); node = nodes[0]; - if (node.type !== 'def') assert.fail(); - assert.equal(node.name, 'data'); + if (node.type !== "def") assert.fail(); + assert.equal(node.name, "data"); assert.equal(node.attr.length, 1); // attribute 1 attr = node.attr[0]; - assert.ok(attr.type === 'attr'); - assert.equal(attr.name, 'serializable'); - if (attr.value.type !== 'bool') assert.fail(); + assert.ok(attr.type === "attr"); + assert.equal(attr.name, "serializable"); + if (attr.value.type !== "bool") assert.fail(); assert.equal(attr.value.value, true); }); }); -describe('Location', () => { - test.concurrent('function', async () => { +describe("Location", () => { + test.concurrent("function", async () => { let node: Ast.Node; const parser = new Parser(); const nodes = parser.parse(` @@ -2275,30 +2420,32 @@ describe('Location', () => { }); }); -describe('Variable declaration', () => { - test.concurrent('Do not assign to let (issue #328)', async () => { +describe("Variable declaration", () => { + test.concurrent("Do not assign to let (issue #328)", async () => { const err = await exe(` let hoge = 33 hoge = 4 - `).then(() => undefined).catch(err => err); + `) + .then(() => undefined) + .catch((err) => err); assert.ok(err instanceof AiScriptRuntimeError); }); -}) +}); -describe('primitive props', () => { - describe('num', () => { - test.concurrent('to_str', async () => { +describe("primitive props", () => { + describe("num", () => { + test.concurrent("to_str", async () => { const res = await exe(` let num = 123 <: num.to_str() `); - eq(res, STR('123')); + eq(res, STR("123")); }); }); - describe('str', () => { - test.concurrent('len', async () => { + describe("str", () => { + test.concurrent("len", async () => { const res = await exe(` let str = "hello" <: str.len @@ -2306,7 +2453,7 @@ describe('primitive props', () => { eq(res, NUM(5)); }); - test.concurrent('to_num', async () => { + test.concurrent("to_num", async () => { const res = await exe(` let str = "123" <: str.to_num() @@ -2314,39 +2461,39 @@ describe('primitive props', () => { eq(res, NUM(123)); }); - test.concurrent('upper', async () => { + test.concurrent("upper", async () => { const res = await exe(` let str = "hello" <: str.upper() `); - eq(res, STR('HELLO')); + eq(res, STR("HELLO")); }); - test.concurrent('lower', async () => { + test.concurrent("lower", async () => { const res = await exe(` let str = "HELLO" <: str.lower() `); - eq(res, STR('hello')); + eq(res, STR("hello")); }); - test.concurrent('trim', async () => { + test.concurrent("trim", async () => { const res = await exe(` let str = " hello " <: str.trim() `); - eq(res, STR('hello')); + eq(res, STR("hello")); }); - test.concurrent('replace', async () => { + test.concurrent("replace", async () => { const res = await exe(` let str = "hello" <: str.replace("l", "x") `); - eq(res, STR('hexxo')); + eq(res, STR("hexxo")); }); - test.concurrent('index_of', async () => { + test.concurrent("index_of", async () => { const res = await exe(` let str = "hello" <: str.index_of("l") @@ -2354,7 +2501,7 @@ describe('primitive props', () => { eq(res, NUM(2)); }); - test.concurrent('incl', async () => { + test.concurrent("incl", async () => { const res = await exe(` let str = "hello" <: [str.incl("ll"), str.incl("x")] @@ -2362,33 +2509,33 @@ describe('primitive props', () => { eq(res, ARR([TRUE, FALSE])); }); - test.concurrent('split', async () => { + test.concurrent("split", async () => { const res = await exe(` let str = "a,b,c" <: str.split(",") `); - eq(res, ARR([STR('a'), STR('b'), STR('c')])); + eq(res, ARR([STR("a"), STR("b"), STR("c")])); }); - test.concurrent('pick', async () => { + test.concurrent("pick", async () => { const res = await exe(` let str = "hello" <: str.pick(1) `); - eq(res, STR('e')); + eq(res, STR("e")); }); - test.concurrent('slice', async () => { + test.concurrent("slice", async () => { const res = await exe(` let str = "hello" <: str.slice(1, 3) `); - eq(res, STR('el')); + eq(res, STR("el")); }); }); - describe('arr', () => { - test.concurrent('len', async () => { + describe("arr", () => { + test.concurrent("len", async () => { const res = await exe(` let arr = [1, 2, 3] <: arr.len @@ -2396,7 +2543,7 @@ describe('primitive props', () => { eq(res, NUM(3)); }); - test.concurrent('push', async () => { + test.concurrent("push", async () => { const res = await exe(` let arr = [1, 2, 3] arr.push(4) @@ -2405,7 +2552,7 @@ describe('primitive props', () => { eq(res, ARR([NUM(1), NUM(2), NUM(3), NUM(4)])); }); - test.concurrent('unshift', async () => { + test.concurrent("unshift", async () => { const res = await exe(` let arr = [1, 2, 3] arr.unshift(4) @@ -2414,7 +2561,7 @@ describe('primitive props', () => { eq(res, ARR([NUM(4), NUM(1), NUM(2), NUM(3)])); }); - test.concurrent('pop', async () => { + test.concurrent("pop", async () => { const res = await exe(` let arr = [1, 2, 3] let popped = arr.pop() @@ -2423,7 +2570,7 @@ describe('primitive props', () => { eq(res, ARR([NUM(3), ARR([NUM(1), NUM(2)])])); }); - test.concurrent('shift', async () => { + test.concurrent("shift", async () => { const res = await exe(` let arr = [1, 2, 3] let shifted = arr.shift() @@ -2432,39 +2579,51 @@ describe('primitive props', () => { eq(res, ARR([NUM(1), ARR([NUM(2), NUM(3)])])); }); - test.concurrent('concat', async () => { + test.concurrent("concat", async () => { const res = await exe(` let arr = [1, 2, 3] let concated = arr.concat([4, 5]) <: [concated, arr] `); - eq(res, ARR([ - ARR([NUM(1), NUM(2), NUM(3), NUM(4), NUM(5)]), - ARR([NUM(1), NUM(2), NUM(3)]) - ])); + eq( + res, + ARR([ + ARR([NUM(1), NUM(2), NUM(3), NUM(4), NUM(5)]), + ARR([NUM(1), NUM(2), NUM(3)]), + ]) + ); }); - test.concurrent('slice', async () => { + test.concurrent("slice", async () => { const res = await exe(` let arr = ["ant", "bison", "camel", "duck", "elephant"] let sliced = arr.slice(2, 4) <: [sliced, arr] `); - eq(res, ARR([ - ARR([STR('camel'), STR('duck')]), - ARR([STR('ant'), STR('bison'), STR('camel'), STR('duck'), STR('elephant')]) - ])); + eq( + res, + ARR([ + ARR([STR("camel"), STR("duck")]), + ARR([ + STR("ant"), + STR("bison"), + STR("camel"), + STR("duck"), + STR("elephant"), + ]), + ]) + ); }); - test.concurrent('join', async () => { + test.concurrent("join", async () => { const res = await exe(` let arr = ["a", "b", "c"] <: arr.join("-") `); - eq(res, STR('a-b-c')); + eq(res, STR("a-b-c")); }); - test.concurrent('map', async () => { + test.concurrent("map", async () => { const res = await exe(` let arr = [1, 2, 3] <: arr.map(@(item) { item * 2 }) @@ -2472,7 +2631,7 @@ describe('primitive props', () => { eq(res, ARR([NUM(2), NUM(4), NUM(6)])); }); - test.concurrent('map with index', async () => { + test.concurrent("map with index", async () => { const res = await exe(` let arr = [1, 2, 3] <: arr.map(@(item, index) { item * index }) @@ -2480,7 +2639,7 @@ describe('primitive props', () => { eq(res, ARR([NUM(0), NUM(2), NUM(6)])); }); - test.concurrent('filter', async () => { + test.concurrent("filter", async () => { const res = await exe(` let arr = [1, 2, 3] <: arr.filter(@(item) { item != 2 }) @@ -2488,7 +2647,7 @@ describe('primitive props', () => { eq(res, ARR([NUM(1), NUM(3)])); }); - test.concurrent('filter with index', async () => { + test.concurrent("filter with index", async () => { const res = await exe(` let arr = [1, 2, 3, 4] <: arr.filter(@(item, index) { item != 2 && index != 3 }) @@ -2496,7 +2655,7 @@ describe('primitive props', () => { eq(res, ARR([NUM(1), NUM(3)])); }); - test.concurrent('reduce', async () => { + test.concurrent("reduce", async () => { const res = await exe(` let arr = [1, 2, 3, 4] <: arr.reduce(@(accumulator, currentValue) { (accumulator + currentValue) }) @@ -2504,7 +2663,7 @@ describe('primitive props', () => { eq(res, NUM(10)); }); - test.concurrent('reduce with index', async () => { + test.concurrent("reduce with index", async () => { const res = await exe(` let arr = [1, 2, 3, 4] <: arr.reduce(@(accumulator, currentValue, index) { (accumulator + (currentValue * index)) } 0) @@ -2512,23 +2671,23 @@ describe('primitive props', () => { eq(res, NUM(20)); }); - test.concurrent('find', async () => { + test.concurrent("find", async () => { const res = await exe(` let arr = ["abc", "def", "ghi"] <: arr.find(@(item) { item.incl("e") }) `); - eq(res, STR('def')); + eq(res, STR("def")); }); - test.concurrent('find with index', async () => { + test.concurrent("find with index", async () => { const res = await exe(` let arr = ["abc1", "def1", "ghi1", "abc2", "def2", "ghi2"] <: arr.find(@(item, index) { item.incl("e") && index > 1 }) `); - eq(res, STR('def2')); + eq(res, STR("def2")); }); - test.concurrent('incl', async () => { + test.concurrent("incl", async () => { const res = await exe(` let arr = ["abc", "def", "ghi"] <: [arr.incl("def"), arr.incl("jkl")] @@ -2536,7 +2695,7 @@ describe('primitive props', () => { eq(res, ARR([TRUE, FALSE])); }); - test.concurrent('reverse', async () => { + test.concurrent("reverse", async () => { const res = await exe(` let arr = [1, 2, 3] arr.reverse() @@ -2545,20 +2704,20 @@ describe('primitive props', () => { eq(res, ARR([NUM(3), NUM(2), NUM(1)])); }); - test.concurrent('copy', async () => { + test.concurrent("copy", async () => { const res = await exe(` let arr = [1, 2, 3] let copied = arr.copy() copied.reverse() <: [copied, arr] `); - eq(res, ARR([ - ARR([NUM(3), NUM(2), NUM(1)]), - ARR([NUM(1), NUM(2), NUM(3)]) - ])); + eq( + res, + ARR([ARR([NUM(3), NUM(2), NUM(1)]), ARR([NUM(1), NUM(2), NUM(3)])]) + ); }); - test.concurrent('sort num array', async () => { + test.concurrent("sort num array", async () => { const res = await exe(` var arr = [2, 10, 3] let comp = @(a, b) { a - b } @@ -2568,25 +2727,25 @@ describe('primitive props', () => { eq(res, ARR([NUM(2), NUM(3), NUM(10)])); }); - test.concurrent('sort string array (with Str:lt)', async () => { + test.concurrent("sort string array (with Str:lt)", async () => { const res = await exe(` var arr = ["hoge", "huga", "piyo", "hoge"] arr.sort(Str:lt) <: arr `); - eq(res, ARR([STR('hoge'), STR('hoge'), STR('huga'), STR('piyo')])); + eq(res, ARR([STR("hoge"), STR("hoge"), STR("huga"), STR("piyo")])); }); - test.concurrent('sort string array (with Str:gt)', async () => { + test.concurrent("sort string array (with Str:gt)", async () => { const res = await exe(` var arr = ["hoge", "huga", "piyo", "hoge"] arr.sort(Str:gt) <: arr `); - eq(res, ARR([ STR('piyo'), STR('huga'), STR('hoge'), STR('hoge')])); + eq(res, ARR([STR("piyo"), STR("huga"), STR("hoge"), STR("hoge")])); }); - test.concurrent('sort object array', async () => { + test.concurrent("sort object array", async () => { const res = await exe(` var arr = [{x: 2}, {x: 10}, {x: 3}] let comp = @(a, b) { a.x - b.x } @@ -2594,83 +2753,121 @@ describe('primitive props', () => { arr.sort(comp) <: arr `); - eq(res, ARR([OBJ(new Map([['x', NUM(2)]])), OBJ(new Map([['x', NUM(3)]])), OBJ(new Map([['x', NUM(10)]]))])); + eq( + res, + ARR([ + OBJ(new Map([["x", NUM(2)]])), + OBJ(new Map([["x", NUM(3)]])), + OBJ(new Map([["x", NUM(10)]])), + ]) + ); }); }); }); -describe('std', () => { - describe('Core', () => { - test.concurrent('range', async () => { - eq(await exe('<: Core:range(1, 10)'), ARR([NUM(1), NUM(2), NUM(3), NUM(4), NUM(5), NUM(6), NUM(7), NUM(8), NUM(9), NUM(10)])); - eq(await exe('<: Core:range(1, 1)'), ARR([NUM(1)])); - eq(await exe('<: Core:range(9, 7)'), ARR([NUM(9), NUM(8), NUM(7)])); - }); - - test.concurrent('to_str', async () => { - eq(await exe('<: Core:to_str("abc")'), STR('abc')); - eq(await exe('<: Core:to_str(123)'), STR('123')); - eq(await exe('<: Core:to_str(true)'), STR('true')); - eq(await exe('<: Core:to_str(false)'), STR('false')); - eq(await exe('<: Core:to_str(null)'), STR('null')); - eq(await exe('<: Core:to_str({ a: "abc", b: 1234 })'), STR('{ a: "abc", b: 1234 }')); - eq(await exe('<: Core:to_str([ true, 123, null ])'), STR('[ true, 123, null ]')); - eq(await exe('<: Core:to_str(@( a, b, c ) {})'), STR('@( a, b, c ) { ... }')); - eq(await exe(` +describe("std", () => { + describe("Core", () => { + test.concurrent("range", async () => { + eq( + await exe("<: Core:range(1, 10)"), + ARR([ + NUM(1), + NUM(2), + NUM(3), + NUM(4), + NUM(5), + NUM(6), + NUM(7), + NUM(8), + NUM(9), + NUM(10), + ]) + ); + eq(await exe("<: Core:range(1, 1)"), ARR([NUM(1)])); + eq(await exe("<: Core:range(9, 7)"), ARR([NUM(9), NUM(8), NUM(7)])); + }); + + test.concurrent("to_str", async () => { + eq(await exe('<: Core:to_str("abc")'), STR("abc")); + eq(await exe("<: Core:to_str(123)"), STR("123")); + eq(await exe("<: Core:to_str(true)"), STR("true")); + eq(await exe("<: Core:to_str(false)"), STR("false")); + eq(await exe("<: Core:to_str(null)"), STR("null")); + eq( + await exe('<: Core:to_str({ a: "abc", b: 1234 })'), + STR('{ a: "abc", b: 1234 }') + ); + eq( + await exe("<: Core:to_str([ true, 123, null ])"), + STR("[ true, 123, null ]") + ); + eq( + await exe("<: Core:to_str(@( a, b, c ) {})"), + STR("@( a, b, c ) { ... }") + ); + eq( + await exe(` let arr = [] arr.push(arr) <: Core:to_str(arr) - `), STR('[ ... ]')); - eq(await exe(` + `), + STR("[ ... ]") + ); + eq( + await exe(` let arr = [] arr.push({ value: arr }) <: Core:to_str(arr) - `), STR('[ { value: ... } ]')); + `), + STR("[ { value: ... } ]") + ); }); }); - describe('Arr', () => { - }); - - describe('Math', () => { - test.concurrent('trig', async () => { + describe("Arr", () => {}); + + describe("Math", () => { + test.concurrent("trig", async () => { eq(await exe("<: Math:sin(Math:PI / 2)"), NUM(1)); eq(await exe("<: Math:sin(0 - (Math:PI / 2))"), NUM(-1)); - eq(await exe("<: Math:sin(Math:PI / 4) * Math:cos(Math:PI / 4)"), NUM(0.5)); + eq( + await exe("<: Math:sin(Math:PI / 4) * Math:cos(Math:PI / 4)"), + NUM(0.5) + ); }); - test.concurrent('abs', async () => { + test.concurrent("abs", async () => { eq(await exe("<: Math:abs(1 - 6)"), NUM(5)); }); - test.concurrent('pow and sqrt', async () => { + test.concurrent("pow and sqrt", async () => { eq(await exe("<: Math:sqrt(3^2 + 4^2)"), NUM(5)); }); - test.concurrent('round', async () => { + test.concurrent("round", async () => { eq(await exe("<: Math:round(3.14)"), NUM(3)); eq(await exe("<: Math:round(-1.414213)"), NUM(-1)); }); - test.concurrent('ceil', async () => { + test.concurrent("ceil", async () => { eq(await exe("<: Math:ceil(2.71828)"), NUM(3)); eq(await exe("<: Math:ceil(0 - Math:PI)"), NUM(-3)); eq(await exe("<: Math:ceil(1 / Math:Infinity)"), NUM(0)); }); - test.concurrent('floor', async () => { + test.concurrent("floor", async () => { eq(await exe("<: Math:floor(23.14069)"), NUM(23)); eq(await exe("<: Math:floor(Math:Infinity / 0)"), NUM(Infinity)); }); - test.concurrent('min', async () => { + test.concurrent("min", async () => { eq(await exe("<: Math:min(2, 3)"), NUM(2)); }); - test.concurrent('max', async () => { + test.concurrent("max", async () => { eq(await exe("<: Math:max(-2, -3)"), NUM(-2)); }); - + /* flaky test.concurrent('rnd', async () => { const steps = 512; @@ -2707,11 +2904,11 @@ describe('std', () => { }); */ - test.concurrent('rnd with arg', async () => { + test.concurrent("rnd with arg", async () => { eq(await exe("<: Math:rnd(1, 1.5)"), NUM(1)); }); - test.concurrent('gen_rng', async () => { + test.concurrent("gen_rng", async () => { const res = await exe(` @test(seed) { let random = Math:gen_rng(seed) @@ -2722,74 +2919,77 @@ describe('std', () => { let test1 = if (test(seed1) == test(seed1)) {true} else {false} let test2 = if (test(seed1) == test(seed2)) {true} else {false} <: [test1 test2] - `) + `); eq(res, ARR([BOOL(true), BOOL(false)])); }); }); - describe('Obj', () => { - test.concurrent('keys', async () => { + describe("Obj", () => { + test.concurrent("keys", async () => { const res = await exe(` let o = { a: 1; b: 2; c: 3; } <: Obj:keys(o) `); - eq(res, ARR([STR('a'), STR('b'), STR('c')])); + eq(res, ARR([STR("a"), STR("b"), STR("c")])); }); - test.concurrent('vals', async () => { + test.concurrent("vals", async () => { const res = await exe(` let o = { _nul: null; _num: 24; _str: 'hoge'; _arr: []; _obj: {}; } <: Obj:vals(o) `); - eq(res, ARR([NULL, NUM(24), STR('hoge'), ARR([]), OBJ(new Map([]))])); + eq(res, ARR([NULL, NUM(24), STR("hoge"), ARR([]), OBJ(new Map([]))])); }); - test.concurrent('kvs', async () => { + test.concurrent("kvs", async () => { const res = await exe(` let o = { a: 1; b: 2; c: 3; } <: Obj:kvs(o) `); - eq(res, ARR([ - ARR([STR('a'), NUM(1)]), - ARR([STR('b'), NUM(2)]), - ARR([STR('c'), NUM(3)]) - ])); + eq( + res, + ARR([ + ARR([STR("a"), NUM(1)]), + ARR([STR("b"), NUM(2)]), + ARR([STR("c"), NUM(3)]), + ]) + ); }); - test.concurrent('merge', async () => { + test.concurrent("merge", async () => { const res = await exe(` let o1 = { a: 1; b: 2; } let o2 = { b: 3; c: 4; } <: Obj:merge(o1, o2) `); - eq(res, utils.jsToVal({ a: 1, b: 3, c: 4})); + eq(res, utils.jsToVal({ a: 1, b: 3, c: 4 })); }); }); - describe('Str', () => { - test.concurrent('lf', async () => { + describe("Str", () => { + test.concurrent("lf", async () => { const res = await exe(` <: Str:lf `); - eq(res, STR('\n')); + eq(res, STR("\n")); }); - test.concurrent('from_codepoint', async () => { + test.concurrent("from_codepoint", async () => { const res = await exe(` <: Str:from_codepoint(65) `); - eq(res, STR('A')); + eq(res, STR("A")); }); - test.concurrent('codepoint_at', async () => { + test.concurrent("codepoint_at", async () => { let res = await exe(` <: "aiscript".split().map(@(x, _) { x.codepoint_at(0) }) `); - eq(res, ARR([97, 105, 115, 99, 114, 105, 112, 116].map(x => NUM(x)))); + eq(res, ARR([97, 105, 115, 99, 114, 105, 112, 116].map((x) => NUM(x)))); res = await exe(` <: "".codepoint_at(0) @@ -2798,32 +2998,27 @@ describe('std', () => { }); }); - describe('Error', () => { - test.concurrent('create', async () => { + describe("Error", () => { + test.concurrent("create", async () => { eq( await exe(` <: Error:create('ai', {chan: 'kawaii'}) `), - ERROR('ai', OBJ(new Map([['chan', STR('kawaii')]]))) + ERROR("ai", OBJ(new Map([["chan", STR("kawaii")]]))) ); }); }); - describe('Json', () => { - test.concurrent('stringify: fn', async () => { + describe("Json", () => { + test.concurrent("stringify: fn", async () => { const res = await exe(` <: Json:stringify(@(){}) `); eq(res, STR('""')); }); - test.concurrent('parsable', async () => { - [ - 'null', - '"hoge"', - '[]', - '{}', - ].forEach(async (str) => { + test.concurrent("parsable", async () => { + ["null", '"hoge"', "[]", "{}"].forEach(async (str) => { const res = await exe(` <: [ Json:parsable('${str}') @@ -2833,56 +3028,52 @@ describe('std', () => { eq(res, ARR([TRUE, STR(str)])); }); }); - test.concurrent('unparsable', async () => { - [ - '', - 'hoge', - '[', - ].forEach(async (str) => { + test.concurrent("unparsable", async () => { + ["", "hoge", "["].forEach(async (str) => { const res = await exe(` <: [ Json:parsable('${str}') Json:parse('${str}') ] `); - eq(res, ARR([FALSE, ERROR('not_json')])); + eq(res, ARR([FALSE, ERROR("not_json")])); }); }); }); }); -describe('Unicode', () => { - test.concurrent('len', async () => { +describe("Unicode", () => { + test.concurrent("len", async () => { const res = await exe(` <: "👍🏽🍆🌮".len `); eq(res, NUM(3)); }); - test.concurrent('pick', async () => { + test.concurrent("pick", async () => { const res = await exe(` <: "👍🏽🍆🌮".pick(1) `); - eq(res, STR('🍆')); + eq(res, STR("🍆")); }); - test.concurrent('slice', async () => { + test.concurrent("slice", async () => { const res = await exe(` <: "Emojis 👍🏽 are 🍆 poison. 🌮s are bad.".slice(7, 14) `); - eq(res, STR('👍🏽 are 🍆')); + eq(res, STR("👍🏽 are 🍆")); }); - test.concurrent('split', async () => { + test.concurrent("split", async () => { const res = await exe(` <: "👍🏽🍆🌮".split() `); - eq(res, ARR([STR('👍🏽'), STR('🍆'), STR('🌮')])); + eq(res, ARR([STR("👍🏽"), STR("🍆"), STR("🌮")])); }); }); -describe('Security', () => { - test.concurrent('Cannot access js native property via var', async () => { +describe("Security", () => { + test.concurrent("Cannot access js native property via var", async () => { try { await exe(` <: constructor @@ -2911,7 +3102,7 @@ describe('Security', () => { } }); - test.concurrent('Cannot access js native property via object', async () => { + test.concurrent("Cannot access js native property via object", async () => { const res1 = await exe(` let obj = {} @@ -2934,38 +3125,75 @@ describe('Security', () => { eq(res3, NULL); }); - test.concurrent('Cannot access js native property via primitive prop', async () => { - try { - await exe(` + test.concurrent( + "Cannot access js native property via primitive prop", + async () => { + try { + await exe(` <: "".constructor `); - assert.fail(); - } catch (e) { - assert.ok(e instanceof AiScriptRuntimeError); - } + assert.fail(); + } catch (e) { + assert.ok(e instanceof AiScriptRuntimeError); + } - try { - await exe(` + try { + await exe(` <: "".prototype `); - assert.fail(); - } catch (e) { - assert.ok(e instanceof AiScriptRuntimeError); - } + assert.fail(); + } catch (e) { + assert.ok(e instanceof AiScriptRuntimeError); + } - try { - await exe(` + try { + await exe(` <: "".__proto__ `); - assert.fail(); - } catch (e) { - assert.equal(e instanceof AiScriptRuntimeError, true); + assert.fail(); + } catch (e) { + assert.equal(e instanceof AiScriptRuntimeError, true); + } } + ); +}); + +describe("property access on parenthesized expression", () => { + test.concurrent("property chain with parenthesis", async () => { + let ast = Parser.parse(` + (a.b).c + `); + const line = ast[0]; + if ( + line.type !== "prop" || + line.target.type !== "prop" || + line.target.target.type !== "identifier" + ) + assert.fail(); + assert.equal(line.target.target.name, "a"); + assert.equal(line.target.name, "b"); + assert.equal(line.name, "c"); + }); + test.concurrent("index chain with parenthesis", async () => { + let ast = Parser.parse(` + (a[42]).b + `); + const line = ast[0]; + if ( + line.type !== "prop" || + line.target.type !== "index" || + line.target.target.type !== "identifier" || + line.target.index.type !== "num" + ) + assert.fail(); + assert.equal(line.target.target.name, "a"); + assert.equal(line.target.index.value, 42); + assert.equal(line.name, "b"); }); }); -describe('extra', () => { - test.concurrent('Fizz Buzz', async () => { +describe("extra", () => { + test.concurrent("Fizz Buzz", async () => { const res = await exe(` let res = [] for (let i = 1, 15) { @@ -2978,26 +3206,29 @@ describe('extra', () => { } <: res `); - eq(res, ARR([ - NUM(1), - NUM(2), - STR('Fizz'), - NUM(4), - STR('Buzz'), - STR('Fizz'), - NUM(7), - NUM(8), - STR('Fizz'), - STR('Buzz'), - NUM(11), - STR('Fizz'), - NUM(13), - NUM(14), - STR('FizzBuzz'), - ])); - }); - - test.concurrent('SKI', async () => { + eq( + res, + ARR([ + NUM(1), + NUM(2), + STR("Fizz"), + NUM(4), + STR("Buzz"), + STR("Fizz"), + NUM(7), + NUM(8), + STR("Fizz"), + STR("Buzz"), + NUM(11), + STR("Fizz"), + NUM(13), + NUM(14), + STR("FizzBuzz"), + ]) + ); + }); + + test.concurrent("SKI", async () => { const res = await exe(` let s = @(x) { @(y) { @(z) { //let f = x(z) f(@(a){ let g = y(z) g(a) }) @@ -3027,6 +3258,6 @@ describe('extra', () => { let sksik = [s, [k, [s, i]], k] c([sksik, "foo", print]) `); - eq(res, STR('foo')); + eq(res, STR("foo")); }); }); From bc073518096bc4aee919b62bf09381ab205f4537 Mon Sep 17 00:00:00 2001 From: arakur Date: Wed, 25 Oct 2023 00:42:21 +0900 Subject: [PATCH 3/5] Added test cases and moved in `chain` closure. --- test/index.ts | 107 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 73 insertions(+), 34 deletions(-) diff --git a/test/index.ts b/test/index.ts index ecff35ce..74fc5de5 100644 --- a/test/index.ts +++ b/test/index.ts @@ -1151,6 +1151,79 @@ describe("chain", () => { `); eq(res, STR("kawaii")); }); + + test.concurrent("property chain with parenthesis", async () => { + let ast = Parser.parse(` + (a.b).c + `); + const line = ast[0]; + if ( + line.type !== "prop" || + line.target.type !== "prop" || + line.target.target.type !== "identifier" + ) + assert.fail(); + assert.equal(line.target.target.name, "a"); + assert.equal(line.target.name, "b"); + assert.equal(line.name, "c"); + }); + + test.concurrent("index chain with parenthesis", async () => { + let ast = Parser.parse(` + (a[42]).b + `); + const line = ast[0]; + if ( + line.type !== "prop" || + line.target.type !== "index" || + line.target.target.type !== "identifier" || + line.target.index.type !== "num" + ) + assert.fail(); + assert.equal(line.target.target.name, "a"); + assert.equal(line.target.index.value, 42); + assert.equal(line.name, "b"); + }); + + test.concurrent("call chain with parenthesis", async () => { + let ast = Parser.parse(` + (foo(42, 57)).bar + `); + const line = ast[0]; + if ( + line.type !== "prop" || + line.target.type !== "call" || + line.target.target.type !== "identifier" || + line.target.args.length !== 2 || + line.target.args[0].type !== "num" || + line.target.args[1].type !== "num" + ) + assert.fail(); + assert.equal(line.target.target.name, "foo"); + assert.equal(line.target.args[0].value, 42); + assert.equal(line.target.args[1].value, 57); + assert.equal(line.name, "bar"); + }); + + test.concurrent("longer chain with parenthesis", async () => { + let ast = Parser.parse(` + (a.b.c).d.e + `); + const line = ast[0]; + if ( + line.type !== "prop" || + line.target.type !== "prop" || + line.target.target.type !== "prop" || + line.target.target.target.type !== "prop" || + line.target.target.target.target.type !== "identifier" + ) + assert.fail(); + assert.equal(line.target.target.target.target.name, "a"); + assert.equal(line.target.target.target.name, "b"); + assert.equal(line.target.target.name, "c"); + assert.equal(line.target.name, "d"); + assert.equal(line.name, "e"); + }); }); describe("Template syntax", () => { @@ -3158,40 +3231,6 @@ describe("Security", () => { ); }); -describe("property access on parenthesized expression", () => { - test.concurrent("property chain with parenthesis", async () => { - let ast = Parser.parse(` - (a.b).c - `); - const line = ast[0]; - if ( - line.type !== "prop" || - line.target.type !== "prop" || - line.target.target.type !== "identifier" - ) - assert.fail(); - assert.equal(line.target.target.name, "a"); - assert.equal(line.target.name, "b"); - assert.equal(line.name, "c"); - }); - test.concurrent("index chain with parenthesis", async () => { - let ast = Parser.parse(` - (a[42]).b - `); - const line = ast[0]; - if ( - line.type !== "prop" || - line.target.type !== "index" || - line.target.target.type !== "identifier" || - line.target.index.type !== "num" - ) - assert.fail(); - assert.equal(line.target.target.name, "a"); - assert.equal(line.target.index.value, 42); - assert.equal(line.name, "b"); - }); -}); - describe("extra", () => { test.concurrent("Fizz Buzz", async () => { const res = await exe(` From 7cadd941b9f9bf4a5ff24c4da2e4f03d4fbf1b2e Mon Sep 17 00:00:00 2001 From: arakur Date: Wed, 25 Oct 2023 00:49:23 +0900 Subject: [PATCH 4/5] Removed diff in test/index.ts. --- test/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/index.ts b/test/index.ts index 74fc5de5..83b42d14 100644 --- a/test/index.ts +++ b/test/index.ts @@ -17,7 +17,6 @@ import { ERROR, FN_NATIVE, } from "../src/interpreter/value"; -import { PropertyAccessChain, PropertyAccessExpression } from "typescript"; let { AiScriptRuntimeError, AiScriptIndexOutOfRangeError } = errors; const exe = (program: string): Promise => From 7bebe9ee41793d6075cbee665f25c8e05d55dc9a Mon Sep 17 00:00:00 2001 From: arakur Date: Wed, 25 Oct 2023 00:55:54 +0900 Subject: [PATCH 5/5] Removed diff. --- test/index.ts | 1686 ++++++++++++++++++++++--------------------------- 1 file changed, 745 insertions(+), 941 deletions(-) diff --git a/test/index.ts b/test/index.ts index 83b42d14..d733e4f4 100644 --- a/test/index.ts +++ b/test/index.ts @@ -3,39 +3,24 @@ * Tests! */ -import * as assert from "assert"; -import { Parser, Interpreter, utils, errors, Ast } from "../src"; -import { - NUM, - STR, - NULL, - ARR, - OBJ, - BOOL, - TRUE, - FALSE, - ERROR, - FN_NATIVE, -} from "../src/interpreter/value"; +import * as assert from 'assert'; +import { Parser, Interpreter, utils, errors, Ast } from '../src'; +import { NUM, STR, NULL, ARR, OBJ, BOOL, TRUE, FALSE, ERROR ,FN_NATIVE } from '../src/interpreter/value'; let { AiScriptRuntimeError, AiScriptIndexOutOfRangeError } = errors; -const exe = (program: string): Promise => - new Promise((ok, err) => { - const aiscript = new Interpreter( - {}, - { - out(value) { - ok(value); - }, - maxStep: 9999, - } - ); - - const parser = new Parser(); - const ast = parser.parse(program); - aiscript.exec(ast).catch(err); +const exe = (program: string): Promise => new Promise((ok, err) => { + const aiscript = new Interpreter({}, { + out(value) { + ok(value); + }, + maxStep: 9999, }); + const parser = new Parser(); + const ast = parser.parse(program); + aiscript.exec(ast).catch(err); +}); + const getMeta = (program: string) => { const parser = new Parser(); const ast = parser.parse(program); @@ -50,23 +35,22 @@ const eq = (a, b) => { assert.deepEqual(a.value, b.value); }; -test.concurrent("Hello, world!", async () => { +test.concurrent('Hello, world!', async () => { const res = await exe('<: "Hello, world!"'); - eq(res, STR("Hello, world!")); + eq(res, STR('Hello, world!')); }); -test.concurrent("empty script", async () => { +test.concurrent('empty script', async () => { const parser = new Parser(); - const ast = parser.parse(""); + const ast = parser.parse(''); assert.deepEqual(ast, []); }); -describe("Interpreter", () => { - describe("Scope", () => { - test.concurrent("getAll", async () => { +describe('Interpreter', () => { + describe('Scope', () => { + test.concurrent('getAll', async () => { const aiscript = new Interpreter({}); - await aiscript.exec( - Parser.parse(` + await aiscript.exec(Parser.parse(` let a = 1 @b() { let x = a + 1 @@ -76,88 +60,73 @@ describe("Interpreter", () => { var y = 2 } var c = true - `) - ); + `)); const vars = aiscript.scope.getAll(); - assert.ok(vars.get("a") != null); - assert.ok(vars.get("b") != null); - assert.ok(vars.get("c") != null); - assert.ok(vars.get("x") == null); - assert.ok(vars.get("y") == null); + assert.ok(vars.get('a') != null); + assert.ok(vars.get('b') != null); + assert.ok(vars.get('c') != null); + assert.ok(vars.get('x') == null); + assert.ok(vars.get('y') == null); }); }); }); -describe("error handler", () => { - test.concurrent("error from outside caller", async () => { +describe('error handler', () => { + test.concurrent('error from outside caller', async () => { let outsideCaller: () => Promise = async () => {}; let errCount: number = 0; - const aiscript = new Interpreter( - { - emitError: FN_NATIVE((_args, _opts) => { - throw Error("emitError"); - }), - genOutsideCaller: FN_NATIVE(([fn], opts) => { - utils.assertFunction(fn); - outsideCaller = async () => { - opts.topCall(fn, []); - }; - }), - }, - { - err(e) { - errCount++; - }, - } - ); - await aiscript.exec( - Parser.parse(` + const aiscript = new Interpreter({ + emitError: FN_NATIVE((_args, _opts) => { + throw Error('emitError'); + }), + genOutsideCaller: FN_NATIVE(([fn], opts) => { + utils.assertFunction(fn); + outsideCaller = async () => { + opts.topCall(fn, []); + }; + }), + }, { + err(e) { errCount++ }, + }); + await aiscript.exec(Parser.parse(` genOutsideCaller(emitError) - `) - ); + `)); assert.strictEqual(errCount, 0); await outsideCaller(); assert.strictEqual(errCount, 1); }); - test.concurrent("array.map calls the handler just once", async () => { + test.concurrent('array.map calls the handler just once', async () => { let errCount: number = 0; - const aiscript = new Interpreter( - {}, - { - err(e) { - errCount++; - }, - } - ); - await aiscript.exec( - Parser.parse(` + const aiscript = new Interpreter({}, { + err(e) { errCount++ }, + }); + await aiscript.exec(Parser.parse(` Core:range(1,5).map(@(){ hoge }) - `) - ); + `)); assert.strictEqual(errCount, 1); }); }); -describe("ops", () => { - test.concurrent("==", async () => { - eq(await exe("<: (1 == 1)"), BOOL(true)); - eq(await exe("<: (1 == 2)"), BOOL(false)); +describe('ops', () => { + test.concurrent('==', async () => { + eq(await exe('<: (1 == 1)'), BOOL(true)); + eq(await exe('<: (1 == 2)'), BOOL(false)); }); - test.concurrent("!=", async () => { - eq(await exe("<: (1 != 2)"), BOOL(true)); - eq(await exe("<: (1 != 1)"), BOOL(false)); + test.concurrent('!=', async () => { + eq(await exe('<: (1 != 2)'), BOOL(true)); + eq(await exe('<: (1 != 1)'), BOOL(false)); }); - test.concurrent("&&", async () => { - eq(await exe("<: (true && true)"), BOOL(true)); - eq(await exe("<: (true && false)"), BOOL(false)); - eq(await exe("<: (false && true)"), BOOL(false)); - eq(await exe("<: (false && false)"), BOOL(false)); - eq(await exe("<: (false && null)"), BOOL(false)); + test.concurrent('&&', async () => { + eq(await exe('<: (true && true)'), BOOL(true)); + eq(await exe('<: (true && false)'), BOOL(false)); + eq(await exe('<: (false && true)'), BOOL(false)); + eq(await exe('<: (false && false)'), BOOL(false)); + eq(await exe('<: (false && null)'), BOOL(false)); try { - await exe("<: (true && null)"); + await exe('<: (true && null)'); } catch (e) { assert.ok(e instanceof AiScriptRuntimeError); return; @@ -177,7 +146,7 @@ describe("ops", () => { <: tmp `), NULL - ); + ) eq( await exe(` @@ -193,19 +162,19 @@ describe("ops", () => { <: tmp `), BOOL(true) - ); + ) assert.fail(); }); - test.concurrent("||", async () => { - eq(await exe("<: (true || true)"), BOOL(true)); - eq(await exe("<: (true || false)"), BOOL(true)); - eq(await exe("<: (false || true)"), BOOL(true)); - eq(await exe("<: (false || false)"), BOOL(false)); - eq(await exe("<: (true || null)"), BOOL(true)); + test.concurrent('||', async () => { + eq(await exe('<: (true || true)'), BOOL(true)); + eq(await exe('<: (true || false)'), BOOL(true)); + eq(await exe('<: (false || true)'), BOOL(true)); + eq(await exe('<: (false || false)'), BOOL(false)); + eq(await exe('<: (true || null)'), BOOL(true)); try { - await exe("<: (false || null)"); + await exe('<: (false || null)'); } catch (e) { assert.ok(e instanceof AiScriptRuntimeError); return; @@ -225,7 +194,7 @@ describe("ops", () => { <: tmp `), NULL - ); + ) eq( await exe(` @@ -241,106 +210,107 @@ describe("ops", () => { <: tmp `), BOOL(true) - ); + ) assert.fail(); }); - test.concurrent("+", async () => { - eq(await exe("<: (1 + 1)"), NUM(2)); + test.concurrent('+', async () => { + eq(await exe('<: (1 + 1)'), NUM(2)); }); - test.concurrent("-", async () => { - eq(await exe("<: (1 - 1)"), NUM(0)); + test.concurrent('-', async () => { + eq(await exe('<: (1 - 1)'), NUM(0)); }); - test.concurrent("*", async () => { - eq(await exe("<: (1 * 1)"), NUM(1)); + test.concurrent('*', async () => { + eq(await exe('<: (1 * 1)'), NUM(1)); }); - test.concurrent("^", async () => { - eq(await exe("<: (1 ^ 0)"), NUM(1)); + test.concurrent('^', async () => { + eq(await exe('<: (1 ^ 0)'), NUM(1)); }); - test.concurrent("/", async () => { - eq(await exe("<: (1 / 1)"), NUM(1)); + test.concurrent('/', async () => { + eq(await exe('<: (1 / 1)'), NUM(1)); }); - test.concurrent("%", async () => { - eq(await exe("<: (1 % 1)"), NUM(0)); + test.concurrent('%', async () => { + eq(await exe('<: (1 % 1)'), NUM(0)); }); - test.concurrent(">", async () => { - eq(await exe("<: (2 > 1)"), BOOL(true)); - eq(await exe("<: (1 > 1)"), BOOL(false)); - eq(await exe("<: (0 > 1)"), BOOL(false)); + test.concurrent('>', async () => { + eq(await exe('<: (2 > 1)'), BOOL(true)); + eq(await exe('<: (1 > 1)'), BOOL(false)); + eq(await exe('<: (0 > 1)'), BOOL(false)); }); - test.concurrent("<", async () => { - eq(await exe("<: (2 < 1)"), BOOL(false)); - eq(await exe("<: (1 < 1)"), BOOL(false)); - eq(await exe("<: (0 < 1)"), BOOL(true)); + test.concurrent('<', async () => { + eq(await exe('<: (2 < 1)'), BOOL(false)); + eq(await exe('<: (1 < 1)'), BOOL(false)); + eq(await exe('<: (0 < 1)'), BOOL(true)); }); - test.concurrent(">=", async () => { - eq(await exe("<: (2 >= 1)"), BOOL(true)); - eq(await exe("<: (1 >= 1)"), BOOL(true)); - eq(await exe("<: (0 >= 1)"), BOOL(false)); + test.concurrent('>=', async () => { + eq(await exe('<: (2 >= 1)'), BOOL(true)); + eq(await exe('<: (1 >= 1)'), BOOL(true)); + eq(await exe('<: (0 >= 1)'), BOOL(false)); }); - test.concurrent("<=", async () => { - eq(await exe("<: (2 <= 1)"), BOOL(false)); - eq(await exe("<: (1 <= 1)"), BOOL(true)); - eq(await exe("<: (0 <= 1)"), BOOL(true)); + test.concurrent('<=', async () => { + eq(await exe('<: (2 <= 1)'), BOOL(false)); + eq(await exe('<: (1 <= 1)'), BOOL(true)); + eq(await exe('<: (0 <= 1)'), BOOL(true)); }); - test.concurrent("precedence", async () => { - eq(await exe("<: 1 + 2 * 3 + 4"), NUM(11)); - eq(await exe("<: 1 + 4 / 4 + 1"), NUM(3)); - eq(await exe("<: 1 + 1 == 2 && 2 * 2 == 4"), BOOL(true)); - eq(await exe("<: (1 + 1) * 2"), NUM(4)); + test.concurrent('precedence', async () => { + eq(await exe('<: 1 + 2 * 3 + 4'), NUM(11)); + eq(await exe('<: 1 + 4 / 4 + 1'), NUM(3)); + eq(await exe('<: 1 + 1 == 2 && 2 * 2 == 4'), BOOL(true)); + eq(await exe('<: (1 + 1) * 2'), NUM(4)); }); - test.concurrent("negative numbers", async () => { - eq(await exe("<: 1+-1"), NUM(0)); - eq(await exe("<: 1--1"), NUM(2)); //反直観的、禁止される可能性がある? - eq(await exe("<: -1*-1"), NUM(1)); - eq(await exe("<: -1==-1"), BOOL(true)); - eq(await exe("<: 1>-1"), BOOL(true)); - eq(await exe("<: -1<1"), BOOL(true)); + test.concurrent('negative numbers', async () => { + eq(await exe('<: 1+-1'), NUM(0)); + eq(await exe('<: 1--1'), NUM(2));//反直観的、禁止される可能性がある? + eq(await exe('<: -1*-1'), NUM(1)); + eq(await exe('<: -1==-1'), BOOL(true)); + eq(await exe('<: 1>-1'), BOOL(true)); + eq(await exe('<: -1<1'), BOOL(true)); }); + }); -describe("Infix expression", () => { - test.concurrent("simple infix expression", async () => { - eq(await exe("<: 0 < 1"), BOOL(true)); - eq(await exe("<: 1 + 1"), NUM(2)); +describe('Infix expression', () => { + test.concurrent('simple infix expression', async () => { + eq(await exe('<: 0 < 1'), BOOL(true)); + eq(await exe('<: 1 + 1'), NUM(2)); }); - test.concurrent("combination", async () => { - eq(await exe("<: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10"), NUM(55)); - eq(await exe("<: Core:add(1, 3) * Core:mul(2, 5)"), NUM(40)); + test.concurrent('combination', async () => { + eq(await exe('<: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10'), NUM(55)); + eq(await exe('<: Core:add(1, 3) * Core:mul(2, 5)'), NUM(40)); }); - test.concurrent("use parentheses to distinguish expr", async () => { - eq(await exe("<: (1 + 10) * (2 + 5)"), NUM(77)); + test.concurrent('use parentheses to distinguish expr', async () => { + eq(await exe('<: (1 + 10) * (2 + 5)'), NUM(77)); }); - test.concurrent("syntax symbols vs infix operators", async () => { + test.concurrent('syntax symbols vs infix operators', async () => { const res = await exe(` <: match true { 1 == 1 => "true" 1 < 1 => "false" } `); - eq(res, STR("true")); + eq(res, STR('true')); }); - test.concurrent("number + if expression", async () => { - eq(await exe("<: 1 + if true 1 else 2"), NUM(2)); + test.concurrent('number + if expression', async () => { + eq(await exe('<: 1 + if true 1 else 2'), NUM(2)); }); - test.concurrent("number + match expression", async () => { + test.concurrent('number + match expression', async () => { const res = await exe(` <: 1 + match 2 == 2 { true => 3 @@ -350,11 +320,11 @@ describe("Infix expression", () => { eq(res, NUM(4)); }); - test.concurrent("eval + eval", async () => { - eq(await exe("<: eval { 1 } + eval { 1 }"), NUM(2)); + test.concurrent('eval + eval', async () => { + eq(await exe('<: eval { 1 } + eval { 1 }'), NUM(2)); }); - test.concurrent("disallow line break", async () => { + test.concurrent('disallow line break', async () => { try { await exe(` <: 1 + @@ -367,17 +337,14 @@ describe("Infix expression", () => { assert.fail(); }); - test.concurrent("escaped line break", async () => { - eq( - await exe(` + test.concurrent('escaped line break', async () => { + eq(await exe(` <: 1 + \\ 1 + 1 - `), - NUM(3) - ); + `), NUM(3)); }); - test.concurrent("infix-to-fncall on namespace", async () => { + test.concurrent('infix-to-fncall on namespace', async () => { eq( await exe(` :: Hoge { @@ -392,8 +359,8 @@ describe("Infix expression", () => { }); }); -describe("Comment", () => { - test.concurrent("single line comment", async () => { +describe('Comment', () => { + test.concurrent('single line comment', async () => { const res = await exe(` // let a = ... let a = 42 @@ -402,7 +369,7 @@ describe("Comment", () => { eq(res, NUM(42)); }); - test.concurrent("multi line comment", async () => { + test.concurrent('multi line comment', async () => { const res = await exe(` /* variable declaration here... let a = ... @@ -413,7 +380,7 @@ describe("Comment", () => { eq(res, NUM(42)); }); - test.concurrent("multi line comment 2", async () => { + test.concurrent('multi line comment 2', async () => { const res = await exe(` /* variable declaration here... let a = ... @@ -427,13 +394,13 @@ describe("Comment", () => { eq(res, NUM(42)); }); - test.concurrent("// as string", async () => { + test.concurrent('// as string', async () => { const res = await exe('<: "//"'); - eq(res, STR("//")); + eq(res, STR('//')); }); }); -test.concurrent("式にコロンがあってもオブジェクトと判定されない", async () => { +test.concurrent('式にコロンがあってもオブジェクトと判定されない', async () => { const res = await exe(` <: eval { Core:eq("ai", "ai") @@ -442,7 +409,7 @@ test.concurrent("式にコロンがあってもオブジェクトと判定され eq(res, BOOL(true)); }); -test.concurrent("inc", async () => { +test.concurrent('inc', async () => { const res = await exe(` var a = 0 a += 1 @@ -453,7 +420,7 @@ test.concurrent("inc", async () => { eq(res, NUM(6)); }); -test.concurrent("dec", async () => { +test.concurrent('dec', async () => { const res = await exe(` var a = 0 a -= 1 @@ -464,7 +431,7 @@ test.concurrent("dec", async () => { eq(res, NUM(-6)); }); -test.concurrent("var", async () => { +test.concurrent('var', async () => { const res = await exe(` let a = 42 <: a @@ -472,7 +439,7 @@ test.concurrent("var", async () => { eq(res, NUM(42)); }); -test.concurrent("参照が繋がらない", async () => { +test.concurrent('参照が繋がらない', async () => { const res = await exe(` var f = @() { "a" } var g = f @@ -480,11 +447,11 @@ test.concurrent("参照が繋がらない", async () => { <: g() `); - eq(res, STR("a")); + eq(res, STR('a')); }); -describe("Cannot put multiple statements in a line", () => { - test.concurrent("var def", async () => { +describe('Cannot put multiple statements in a line', () => { + test.concurrent('var def', async () => { try { await exe(` let a = 42 let b = 11 @@ -496,7 +463,7 @@ describe("Cannot put multiple statements in a line", () => { assert.fail(); }); - test.concurrent("var def (op)", async () => { + test.concurrent('var def (op)', async () => { try { await exe(` let a = 13 + 75 let b = 24 + 146 @@ -509,7 +476,7 @@ describe("Cannot put multiple statements in a line", () => { }); }); -test.concurrent("empty function", async () => { +test.concurrent('empty function', async () => { const res = await exe(` @hoge() { } <: hoge() @@ -517,7 +484,7 @@ test.concurrent("empty function", async () => { eq(res, NULL); }); -test.concurrent("empty lambda", async () => { +test.concurrent('empty lambda', async () => { const res = await exe(` let hoge = @() { } <: hoge() @@ -525,7 +492,7 @@ test.concurrent("empty lambda", async () => { eq(res, NULL); }); -test.concurrent("lambda that returns an object", async () => { +test.concurrent('lambda that returns an object', async () => { const res = await exe(` let hoge = @() {{}} <: hoge() @@ -533,7 +500,7 @@ test.concurrent("lambda that returns an object", async () => { eq(res, OBJ(new Map())); }); -test.concurrent("Closure", async () => { +test.concurrent('Closure', async () => { const res = await exe(` @store(v) { let state = v @@ -544,10 +511,10 @@ test.concurrent("Closure", async () => { let s = store("ai") <: s() `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); -test.concurrent("Closure (counter)", async () => { +test.concurrent('Closure (counter)', async () => { const res = await exe(` @create_counter() { var count = 0 @@ -570,7 +537,7 @@ test.concurrent("Closure (counter)", async () => { eq(res, NUM(3)); }); -test.concurrent("Recursion", async () => { +test.concurrent('Recursion', async () => { const res = await exe(` @fact(n) { if (n == 0) { 1 } else { (fact((n - 1)) * n) } @@ -581,8 +548,8 @@ test.concurrent("Recursion", async () => { eq(res, NUM(120)); }); -describe("Var name starts with reserved word", () => { - test.concurrent("let", async () => { +describe('Var name starts with reserved word', () => { + test.concurrent('let', async () => { const res = await exe(` @f() { let letcat = "ai" @@ -590,10 +557,10 @@ describe("Var name starts with reserved word", () => { } <: f() `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); - test.concurrent("var", async () => { + test.concurrent('var', async () => { const res = await exe(` @f() { let varcat = "ai" @@ -601,10 +568,10 @@ describe("Var name starts with reserved word", () => { } <: f() `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); - test.concurrent("return", async () => { + test.concurrent('return', async () => { const res = await exe(` @f() { let returncat = "ai" @@ -612,10 +579,10 @@ describe("Var name starts with reserved word", () => { } <: f() `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); - test.concurrent("each", async () => { + test.concurrent('each', async () => { const res = await exe(` @f() { let eachcat = "ai" @@ -623,10 +590,10 @@ describe("Var name starts with reserved word", () => { } <: f() `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); - test.concurrent("for", async () => { + test.concurrent('for', async () => { const res = await exe(` @f() { let forcat = "ai" @@ -634,10 +601,10 @@ describe("Var name starts with reserved word", () => { } <: f() `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); - test.concurrent("loop", async () => { + test.concurrent('loop', async () => { const res = await exe(` @f() { let loopcat = "ai" @@ -645,10 +612,10 @@ describe("Var name starts with reserved word", () => { } <: f() `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); - test.concurrent("break", async () => { + test.concurrent('break', async () => { const res = await exe(` @f() { let breakcat = "ai" @@ -656,10 +623,10 @@ describe("Var name starts with reserved word", () => { } <: f() `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); - test.concurrent("continue", async () => { + test.concurrent('continue', async () => { const res = await exe(` @f() { let continuecat = "ai" @@ -667,10 +634,10 @@ describe("Var name starts with reserved word", () => { } <: f() `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); - test.concurrent("if", async () => { + test.concurrent('if', async () => { const res = await exe(` @f() { let ifcat = "ai" @@ -678,10 +645,10 @@ describe("Var name starts with reserved word", () => { } <: f() `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); - test.concurrent("match", async () => { + test.concurrent('match', async () => { const res = await exe(` @f() { let matchcat = "ai" @@ -689,10 +656,10 @@ describe("Var name starts with reserved word", () => { } <: f() `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); - test.concurrent("true", async () => { + test.concurrent('true', async () => { const res = await exe(` @f() { let truecat = "ai" @@ -700,10 +667,10 @@ describe("Var name starts with reserved word", () => { } <: f() `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); - test.concurrent("false", async () => { + test.concurrent('false', async () => { const res = await exe(` @f() { let falsecat = "ai" @@ -711,10 +678,10 @@ describe("Var name starts with reserved word", () => { } <: f() `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); - test.concurrent("null", async () => { + test.concurrent('null', async () => { const res = await exe(` @f() { let nullcat = "ai" @@ -722,12 +689,12 @@ describe("Var name starts with reserved word", () => { } <: f() `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); }); -describe("name validation of reserved word", () => { - test.concurrent("def", async () => { +describe('name validation of reserved word', () => { + test.concurrent('def', async () => { try { await exe(` let let = 1 @@ -739,7 +706,7 @@ describe("name validation of reserved word", () => { assert.fail(); }); - test.concurrent("attr", async () => { + test.concurrent('attr', async () => { try { await exe(` #[let 1] @@ -752,7 +719,7 @@ describe("name validation of reserved word", () => { assert.fail(); }); - test.concurrent("ns", async () => { + test.concurrent('ns', async () => { try { await exe(` :: let { @@ -766,7 +733,7 @@ describe("name validation of reserved word", () => { assert.fail(); }); - test.concurrent("var", async () => { + test.concurrent('var', async () => { try { await exe(` let @@ -778,7 +745,7 @@ describe("name validation of reserved word", () => { assert.fail(); }); - test.concurrent("prop", async () => { + test.concurrent('prop', async () => { try { await exe(` let x = { let: 1 } @@ -791,7 +758,7 @@ describe("name validation of reserved word", () => { assert.fail(); }); - test.concurrent("meta", async () => { + test.concurrent('meta', async () => { try { await exe(` ### let 1 @@ -803,7 +770,7 @@ describe("name validation of reserved word", () => { assert.fail(); }); - test.concurrent("fn", async () => { + test.concurrent('fn', async () => { try { await exe(` @let() { 1 } @@ -816,8 +783,8 @@ describe("name validation of reserved word", () => { }); }); -describe("Object", () => { - test.concurrent("property access", async () => { +describe('Object', () => { + test.concurrent('property access', async () => { const res = await exe(` let obj = { a: { @@ -832,7 +799,7 @@ describe("Object", () => { eq(res, NUM(42)); }); - test.concurrent("property access (fn call)", async () => { + test.concurrent('property access (fn call)', async () => { const res = await exe(` @f() { 42 } @@ -849,7 +816,7 @@ describe("Object", () => { eq(res, NUM(42)); }); - test.concurrent("property assign", async () => { + test.concurrent('property assign', async () => { const res = await exe(` let obj = { a: 1 @@ -866,23 +833,15 @@ describe("Object", () => { <: obj `); - eq( - res, - OBJ( - new Map([ - ["a", NUM(24)], - [ - "b", - OBJ( - new Map([ - ["c", NUM(2)], - ["d", OBJ(new Map([["e", NUM(42)]]))], - ]) - ), - ], - ]) - ) - ); + eq(res, OBJ(new Map([ + ['a', NUM(24)], + ['b', OBJ(new Map([ + ['c', NUM(2)], + ['d', OBJ(new Map([ + ['e', NUM(42)], + ]))], + ]))], + ]))); }); /* 未実装 @@ -923,17 +882,17 @@ describe("Object", () => { */ }); -describe("Array", () => { - test.concurrent("Array item access", async () => { +describe('Array', () => { + test.concurrent('Array item access', async () => { const res = await exe(` let arr = ["ai", "chan", "kawaii"] <: arr[1] `); - eq(res, STR("chan")); + eq(res, STR('chan')); }); - test.concurrent("Array item assign", async () => { + test.concurrent('Array item assign', async () => { const res = await exe(` let arr = ["ai", "chan", "kawaii"] @@ -941,10 +900,10 @@ describe("Array", () => { <: arr `); - eq(res, ARR([STR("ai"), STR("taso"), STR("kawaii")])); + eq(res, ARR([STR('ai'), STR('taso'), STR('kawaii')])); }); - test.concurrent("Assign array item to out of range", async () => { + test.concurrent('Assign array item to out of range', async () => { try { await exe(` let arr = [1, 2, 3] @@ -952,7 +911,7 @@ describe("Array", () => { arr[3] = 4 <: null - `); + `) } catch (e) { eq(e instanceof AiScriptIndexOutOfRangeError, false); } @@ -964,13 +923,13 @@ describe("Array", () => { arr[9] = 10 <: null - `); + `) } catch (e) { eq(e instanceof AiScriptIndexOutOfRangeError, true); } }); - test.concurrent("index out of range error", async () => { + test.concurrent('index out of range error', async () => { try { await exe(` <: [42][1] @@ -983,8 +942,8 @@ describe("Array", () => { }); }); -describe("chain", () => { - test.concurrent("chain access (prop + index + call)", async () => { +describe('chain', () => { + test.concurrent('chain access (prop + index + call)', async () => { const res = await exe(` let obj = { a: { @@ -994,10 +953,10 @@ describe("chain", () => { <: obj.a.b[0]("ai") `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); - test.concurrent("chained assign left side (prop + index)", async () => { + test.concurrent('chained assign left side (prop + index)', async () => { const res = await exe(` let obj = { a: { @@ -1009,23 +968,15 @@ describe("chain", () => { <: obj `); - eq( - res, - OBJ( - new Map([ - [ - "a", - OBJ(new Map([["b", ARR([STR("ai"), STR("taso"), STR("kawaii")])]])), - ], - ]) - ) - ); + eq(res, OBJ(new Map([ + ['a', OBJ(new Map([ + ['b', ARR([STR('ai'), STR('taso'), STR('kawaii')])] + ]))] + ]))); }); - test.concurrent( - "chained assign right side (prop + index + call)", - async () => { - const res = await exe(` + test.concurrent('chained assign right side (prop + index + call)', async () => { + const res = await exe(` let obj = { a: { b: ["ai", "chan", "kawaii"]; @@ -1037,11 +988,10 @@ describe("chain", () => { <: x `); - eq(res, STR("chan")); - } - ); + eq(res, STR('chan')); + }); - test.concurrent("chained inc/dec left side (index + prop)", async () => { + test.concurrent('chained inc/dec left side (index + prop)', async () => { const res = await exe(` let arr = [ { @@ -1055,20 +1005,15 @@ describe("chain", () => { <: arr `); - eq( - res, - ARR([ - OBJ( - new Map([ - ["a", NUM(2)], - ["b", NUM(1)], - ]) - ), - ]) - ); + eq(res, ARR([ + OBJ(new Map([ + ['a', NUM(2)], + ['b', NUM(1)] + ])) + ])); }); - test.concurrent("chained inc/dec left side (prop + index)", async () => { + test.concurrent('chained inc/dec left side (prop + index)', async () => { const res = await exe(` let obj = { a: { @@ -1081,15 +1026,14 @@ describe("chain", () => { <: obj `); - eq( - res, - OBJ( - new Map([["a", OBJ(new Map([["b", ARR([NUM(1), NUM(3), NUM(2)])]]))]]) - ) - ); + eq(res, OBJ(new Map([ + ['a', OBJ(new Map([ + ['b', ARR([NUM(1), NUM(3), NUM(2)])] + ]))] + ]))); }); - test.concurrent("prop in def", async () => { + test.concurrent('prop in def', async () => { const res = await exe(` let x = @() { let obj = { @@ -1103,7 +1047,7 @@ describe("chain", () => { eq(res, NUM(1)); }); - test.concurrent("prop in return", async () => { + test.concurrent('prop in return', async () => { const res = await exe(` let x = @() { let obj = { @@ -1118,7 +1062,7 @@ describe("chain", () => { eq(res, NUM(1)); }); - test.concurrent("prop in each", async () => { + test.concurrent('prop in each', async () => { const res = await exe(` let msgs = [] let x = { a: ["ai", "chan", "kawaii"] } @@ -1128,10 +1072,10 @@ describe("chain", () => { } <: msgs `); - eq(res, ARR([STR("ai!"), STR("chan!"), STR("kawaii!")])); + eq(res, ARR([STR('ai!'), STR('chan!'), STR('kawaii!')])); }); - test.concurrent("prop in for", async () => { + test.concurrent('prop in for', async () => { const res = await exe(` let x = { times: 10, count: 0 } for (let i, x.times) { @@ -1142,106 +1086,106 @@ describe("chain", () => { eq(res, NUM(45)); }); - test.concurrent("object with index", async () => { + test.concurrent('object with index', async () => { const res = await exe(` let ai = {a: {}}['a'] ai['chan'] = 'kawaii' <: ai[{a: 'chan'}['a']] `); - eq(res, STR("kawaii")); + eq(res, STR('kawaii')); }); - test.concurrent("property chain with parenthesis", async () => { + test.concurrent('property chain with parenthesis', async () => { let ast = Parser.parse(` (a.b).c `); const line = ast[0]; if ( - line.type !== "prop" || - line.target.type !== "prop" || - line.target.target.type !== "identifier" + line.type !== 'prop' || + line.target.type !== 'prop' || + line.target.target.type !== 'identifier' ) assert.fail(); - assert.equal(line.target.target.name, "a"); - assert.equal(line.target.name, "b"); - assert.equal(line.name, "c"); + assert.equal(line.target.target.name, 'a'); + assert.equal(line.target.name, 'b'); + assert.equal(line.name, 'c'); }); - test.concurrent("index chain with parenthesis", async () => { + test.concurrent('index chain with parenthesis', async () => { let ast = Parser.parse(` (a[42]).b `); const line = ast[0]; if ( - line.type !== "prop" || - line.target.type !== "index" || - line.target.target.type !== "identifier" || - line.target.index.type !== "num" + line.type !== 'prop' || + line.target.type !== 'index' || + line.target.target.type !== 'identifier' || + line.target.index.type !== 'num' ) assert.fail(); - assert.equal(line.target.target.name, "a"); + assert.equal(line.target.target.name, 'a'); assert.equal(line.target.index.value, 42); - assert.equal(line.name, "b"); + assert.equal(line.name, 'b'); }); - test.concurrent("call chain with parenthesis", async () => { + test.concurrent('call chain with parenthesis', async () => { let ast = Parser.parse(` (foo(42, 57)).bar `); const line = ast[0]; if ( - line.type !== "prop" || - line.target.type !== "call" || - line.target.target.type !== "identifier" || + line.type !== 'prop' || + line.target.type !== 'call' || + line.target.target.type !== 'identifier' || line.target.args.length !== 2 || - line.target.args[0].type !== "num" || - line.target.args[1].type !== "num" + line.target.args[0].type !== 'num' || + line.target.args[1].type !== 'num' ) assert.fail(); - assert.equal(line.target.target.name, "foo"); + assert.equal(line.target.target.name, 'foo'); assert.equal(line.target.args[0].value, 42); assert.equal(line.target.args[1].value, 57); - assert.equal(line.name, "bar"); + assert.equal(line.name, 'bar'); }); - test.concurrent("longer chain with parenthesis", async () => { + test.concurrent('longer chain with parenthesis', async () => { let ast = Parser.parse(` (a.b.c).d.e `); const line = ast[0]; if ( - line.type !== "prop" || - line.target.type !== "prop" || - line.target.target.type !== "prop" || - line.target.target.target.type !== "prop" || - line.target.target.target.target.type !== "identifier" + line.type !== 'prop' || + line.target.type !== 'prop' || + line.target.target.type !== 'prop' || + line.target.target.target.type !== 'prop' || + line.target.target.target.target.type !== 'identifier' ) assert.fail(); - assert.equal(line.target.target.target.target.name, "a"); - assert.equal(line.target.target.target.name, "b"); - assert.equal(line.target.target.name, "c"); - assert.equal(line.target.name, "d"); - assert.equal(line.name, "e"); + assert.equal(line.target.target.target.target.name, 'a'); + assert.equal(line.target.target.target.name, 'b'); + assert.equal(line.target.target.name, 'c'); + assert.equal(line.target.name, 'd'); + assert.equal(line.name, 'e'); }); }); -describe("Template syntax", () => { - test.concurrent("Basic", async () => { +describe('Template syntax', () => { + test.concurrent('Basic', async () => { const res = await exe(` let str = "kawaii" <: \`Ai is {str}!\` `); - eq(res, STR("Ai is kawaii!")); + eq(res, STR('Ai is kawaii!')); }); - test.concurrent("convert to str", async () => { + test.concurrent('convert to str', async () => { const res = await exe(` <: \`1 + 1 = {(1 + 1)}\` `); - eq(res, STR("1 + 1 = 2")); + eq(res, STR('1 + 1 = 2')); }); - test.concurrent("invalid", async () => { + test.concurrent('invalid', async () => { try { await exe(` <: \`{hoge}\` @@ -1253,16 +1197,16 @@ describe("Template syntax", () => { assert.fail(); }); - test.concurrent("Escape", async () => { + test.concurrent('Escape', async () => { const res = await exe(` let message = "Hello" <: \`\\\`a\\{b\\}c\\\`\` `); - eq(res, STR("`a{b}c`")); + eq(res, STR('`a{b}c`')); }); }); -test.concurrent("Throws error when divided by zero", async () => { +test.concurrent('Throws error when divided by zero', async () => { try { await exe(` <: (0 / 0) @@ -1274,8 +1218,8 @@ test.concurrent("Throws error when divided by zero", async () => { assert.fail(); }); -describe("Function call", () => { - test.concurrent("without args", async () => { +describe('Function call', () => { + test.concurrent('without args', async () => { const res = await exe(` @f() { 42 @@ -1285,7 +1229,7 @@ describe("Function call", () => { eq(res, NUM(42)); }); - test.concurrent("with args", async () => { + test.concurrent('with args', async () => { const res = await exe(` @f(x) { x @@ -1295,7 +1239,7 @@ describe("Function call", () => { eq(res, NUM(42)); }); - test.concurrent("with args (separated by comma)", async () => { + test.concurrent('with args (separated by comma)', async () => { const res = await exe(` @f(x, y) { (x + y) @@ -1305,7 +1249,7 @@ describe("Function call", () => { eq(res, NUM(2)); }); - test.concurrent("with args (separated by space)", async () => { + test.concurrent('with args (separated by space)', async () => { const res = await exe(` @f(x y) { (x + y) @@ -1315,24 +1259,21 @@ describe("Function call", () => { eq(res, NUM(2)); }); - test.concurrent( - "std: throw AiScript error when required arg missing", - async () => { - try { - await exe(` + test.concurrent('std: throw AiScript error when required arg missing', async () => { + try { + await exe(` <: Core:eq(1) `); - } catch (e) { - assert.ok(e instanceof AiScriptRuntimeError); - return; - } - assert.fail(); + } catch (e) { + assert.ok(e instanceof AiScriptRuntimeError); + return; } - ); + assert.fail(); + }); }); -describe("Return", () => { - test.concurrent("Early return", async () => { +describe('Return', () => { + test.concurrent('Early return', async () => { const res = await exe(` @f() { if true { @@ -1343,10 +1284,10 @@ describe("Return", () => { } <: f() `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); - test.concurrent("Early return (nested)", async () => { + test.concurrent('Early return (nested)', async () => { const res = await exe(` @f() { if true { @@ -1359,10 +1300,10 @@ describe("Return", () => { } <: f() `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); - test.concurrent("Early return (nested) 2", async () => { + test.concurrent('Early return (nested) 2', async () => { const res = await exe(` @f() { if true { @@ -1382,10 +1323,10 @@ describe("Return", () => { <: g() `); - eq(res, STR("kawaii")); + eq(res, STR('kawaii')); }); - test.concurrent("Early return without block", async () => { + test.concurrent('Early return without block', async () => { const res = await exe(` @f() { if true return "ai" @@ -1394,10 +1335,10 @@ describe("Return", () => { } <: f() `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); - test.concurrent("return inside for", async () => { + test.concurrent('return inside for', async () => { const res = await exe(` @f() { var count = 0 @@ -1413,7 +1354,7 @@ describe("Return", () => { eq(res, NUM(43)); }); - test.concurrent("return inside for 2", async () => { + test.concurrent('return inside for 2', async () => { const res = await exe(` @f() { for (let i, 10) { @@ -1426,7 +1367,7 @@ describe("Return", () => { eq(res, NUM(1)); }); - test.concurrent("return inside loop", async () => { + test.concurrent('return inside loop', async () => { const res = await exe(` @f() { var count = 0 @@ -1442,7 +1383,7 @@ describe("Return", () => { eq(res, NUM(42)); }); - test.concurrent("return inside loop 2", async () => { + test.concurrent('return inside loop 2', async () => { const res = await exe(` @f() { loop { @@ -1455,7 +1396,8 @@ describe("Return", () => { eq(res, NUM(1)); }); - test.concurrent("return inside each", async () => { + test.concurrent('return inside each', async () => + { const res = await exe(` @f() { var count = 0 @@ -1471,7 +1413,8 @@ describe("Return", () => { eq(res, NUM(2)); }); - test.concurrent("return inside each 2", async () => { + test.concurrent('return inside each 2', async () => + { const res = await exe(` @f() { each (let item, ["ai", "chan", "kawaii"]) { @@ -1485,8 +1428,8 @@ describe("Return", () => { }); }); -describe("Eval", () => { - test.concurrent("returns value", async () => { +describe('Eval', () => { + test.concurrent('returns value', async () => { const res = await exe(` let foo = eval { let a = 1 @@ -1500,8 +1443,8 @@ describe("Eval", () => { }); }); -describe("exists", () => { - test.concurrent("Basic", async () => { +describe('exists', () => { + test.concurrent('Basic', async () => { const res = await exe(` let foo = null <: [(exists foo) (exists bar)] @@ -1510,8 +1453,8 @@ describe("exists", () => { }); }); -describe("if", () => { - test.concurrent("if", async () => { +describe('if', () => { + test.concurrent('if', async () => { const res1 = await exe(` var msg = "ai" if true { @@ -1519,7 +1462,7 @@ describe("if", () => { } <: msg `); - eq(res1, STR("kawaii")); + eq(res1, STR('kawaii')); const res2 = await exe(` var msg = "ai" @@ -1528,10 +1471,10 @@ describe("if", () => { } <: msg `); - eq(res2, STR("ai")); + eq(res2, STR('ai')); }); - test.concurrent("else", async () => { + test.concurrent('else', async () => { const res1 = await exe(` var msg = null if true { @@ -1541,7 +1484,7 @@ describe("if", () => { } <: msg `); - eq(res1, STR("ai")); + eq(res1, STR('ai')); const res2 = await exe(` var msg = null @@ -1552,10 +1495,10 @@ describe("if", () => { } <: msg `); - eq(res2, STR("kawaii")); + eq(res2, STR('kawaii')); }); - test.concurrent("elif", async () => { + test.concurrent('elif', async () => { const res1 = await exe(` var msg = "bebeyo" if false { @@ -1565,7 +1508,7 @@ describe("if", () => { } <: msg `); - eq(res1, STR("kawaii")); + eq(res1, STR('kawaii')); const res2 = await exe(` var msg = "bebeyo" @@ -1576,10 +1519,10 @@ describe("if", () => { } <: msg `); - eq(res2, STR("bebeyo")); + eq(res2, STR('bebeyo')); }); - test.concurrent("if ~ elif ~ else", async () => { + test.concurrent('if ~ elif ~ else', async () => { const res1 = await exe(` var msg = null if false { @@ -1591,7 +1534,7 @@ describe("if", () => { } <: msg `); - eq(res1, STR("chan")); + eq(res1, STR('chan')); const res2 = await exe(` var msg = null @@ -1604,24 +1547,24 @@ describe("if", () => { } <: msg `); - eq(res2, STR("kawaii")); + eq(res2, STR('kawaii')); }); - test.concurrent("expr", async () => { + test.concurrent('expr', async () => { const res1 = await exe(` <: if true "ai" else "kawaii" `); - eq(res1, STR("ai")); + eq(res1, STR('ai')); const res2 = await exe(` <: if false "ai" else "kawaii" `); - eq(res2, STR("kawaii")); + eq(res2, STR('kawaii')); }); }); -describe("match", () => { - test.concurrent("Basic", async () => { +describe('match', () => { + test.concurrent('Basic', async () => { const res = await exe(` <: match 2 { 1 => "a" @@ -1629,10 +1572,10 @@ describe("match", () => { 3 => "c" } `); - eq(res, STR("b")); + eq(res, STR('b')); }); - test.concurrent("When default not provided, returns null", async () => { + test.concurrent('When default not provided, returns null', async () => { const res = await exe(` <: match 42 { 1 => "a" @@ -1643,7 +1586,7 @@ describe("match", () => { eq(res, NULL); }); - test.concurrent("With default", async () => { + test.concurrent('With default', async () => { const res = await exe(` <: match 42 { 1 => "a" @@ -1652,10 +1595,10 @@ describe("match", () => { * => "d" } `); - eq(res, STR("d")); + eq(res, STR('d')); }); - test.concurrent("With block", async () => { + test.concurrent('With block', async () => { const res = await exe(` <: match 2 { 1 => 1 @@ -1670,7 +1613,7 @@ describe("match", () => { eq(res, NUM(3)); }); - test.concurrent("With return", async () => { + test.concurrent('With return', async () => { const res = await exe(` @f(x) { match x { @@ -1682,12 +1625,12 @@ describe("match", () => { } <: f(1) `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); }); -describe("loop", () => { - test.concurrent("Basic", async () => { +describe('loop', () => { + test.concurrent('Basic', async () => { const res = await exe(` var count = 0 loop { @@ -1699,7 +1642,7 @@ describe("loop", () => { eq(res, NUM(10)); }); - test.concurrent("with continue", async () => { + test.concurrent('with continue', async () => { const res = await exe(` var a = ["ai" "chan" "kawaii" "yo" "!"] var b = [] @@ -1711,12 +1654,12 @@ describe("loop", () => { } <: b `); - eq(res, ARR([STR("ai"), STR("kawaii")])); + eq(res, ARR([STR('ai'), STR('kawaii')])); }); }); -describe("for", () => { - test.concurrent("Basic", async () => { +describe('for', () => { + test.concurrent('Basic', async () => { const res = await exe(` var count = 0 for (let i, 10) { @@ -1727,7 +1670,7 @@ describe("for", () => { eq(res, NUM(55)); }); - test.concurrent("initial value", async () => { + test.concurrent('initial value', async () => { const res = await exe(` var count = 0 for (let i = 2, 10) { @@ -1738,7 +1681,7 @@ describe("for", () => { eq(res, NUM(65)); }); - test.concurrent("wuthout iterator", async () => { + test.concurrent('wuthout iterator', async () => { const res = await exe(` var count = 0 for (10) { @@ -1749,7 +1692,7 @@ describe("for", () => { eq(res, NUM(10)); }); - test.concurrent("without brackets", async () => { + test.concurrent('without brackets', async () => { const res = await exe(` var count = 0 for let i, 10 { @@ -1760,7 +1703,7 @@ describe("for", () => { eq(res, NUM(45)); }); - test.concurrent("Break", async () => { + test.concurrent('Break', async () => { const res = await exe(` var count = 0 for (let i, 20) { @@ -1772,7 +1715,7 @@ describe("for", () => { eq(res, NUM(55)); }); - test.concurrent("continue", async () => { + test.concurrent('continue', async () => { const res = await exe(` var count = 0 for (let i, 10) { @@ -1784,7 +1727,7 @@ describe("for", () => { eq(res, NUM(9)); }); - test.concurrent("single statement", async () => { + test.concurrent('single statement', async () => { const res = await exe(` var count = 0 for 10 count += 1 @@ -1793,7 +1736,7 @@ describe("for", () => { eq(res, NUM(10)); }); - test.concurrent("var name without space", async () => { + test.concurrent('var name without space', async () => { try { await exe(` for (leti, 10) { @@ -1808,8 +1751,8 @@ describe("for", () => { }); }); -describe("for of", () => { - test.concurrent("standard", async () => { +describe('for of', () => { + test.concurrent('standard', async () => { const res = await exe(` let msgs = [] each let item, ["ai", "chan", "kawaii"] { @@ -1817,10 +1760,10 @@ describe("for of", () => { } <: msgs `); - eq(res, ARR([STR("ai!"), STR("chan!"), STR("kawaii!")])); + eq(res, ARR([STR('ai!'), STR('chan!'), STR('kawaii!')])); }); - test.concurrent("Break", async () => { + test.concurrent('Break', async () => { const res = await exe(` let msgs = [] each let item, ["ai", "chan", "kawaii" "yo"] { @@ -1829,19 +1772,19 @@ describe("for of", () => { } <: msgs `); - eq(res, ARR([STR("ai!"), STR("chan!")])); + eq(res, ARR([STR('ai!'), STR('chan!')])); }); - test.concurrent("single statement", async () => { + test.concurrent('single statement', async () => { const res = await exe(` let msgs = [] each let item, ["ai", "chan", "kawaii"] msgs.push([item, "!"].join()) <: msgs `); - eq(res, ARR([STR("ai!"), STR("chan!"), STR("kawaii!")])); + eq(res, ARR([STR('ai!'), STR('chan!'), STR('kawaii!')])); }); - test.concurrent("var name without space", async () => { + test.concurrent('var name without space', async () => { try { await exe(` each letitem, ["ai", "chan", "kawaii"] { @@ -1856,8 +1799,8 @@ describe("for of", () => { }); }); -describe("not", () => { - test.concurrent("Basic", async () => { +describe('not', () => { + test.concurrent('Basic', async () => { const res = await exe(` <: !true `); @@ -1865,8 +1808,8 @@ describe("not", () => { }); }); -describe("namespace", () => { - test.concurrent("standard", async () => { +describe('namespace', () => { + test.concurrent('standard', async () => { const res = await exe(` <: Foo:bar() @@ -1874,10 +1817,10 @@ describe("namespace", () => { @bar() { "ai" } } `); - eq(res, STR("ai")); + eq(res, STR('ai')); }); - test.concurrent("self ref", async () => { + test.concurrent('self ref', async () => { const res = await exe(` <: Foo:bar() @@ -1886,10 +1829,10 @@ describe("namespace", () => { @bar() { ai } } `); - eq(res, STR("kawaii")); + eq(res, STR('kawaii')); }); - test.concurrent("cannot declare mutable variable", async () => { + test.concurrent('cannot declare mutable variable', async () => { try { await exe(` :: Foo { @@ -1904,77 +1847,74 @@ describe("namespace", () => { }); }); -describe("literal", () => { - test.concurrent("string (single quote)", async () => { +describe('literal', () => { + test.concurrent('string (single quote)', async () => { const res = await exe(` <: 'foo' `); - eq(res, STR("foo")); + eq(res, STR('foo')); }); - test.concurrent("string (double quote)", async () => { + test.concurrent('string (double quote)', async () => { const res = await exe(` <: "foo" `); - eq(res, STR("foo")); + eq(res, STR('foo')); }); - test.concurrent("Escaped double quote", async () => { + test.concurrent('Escaped double quote', async () => { const res = await exe('<: "ai saw a note \\"bebeyo\\"."'); eq(res, STR('ai saw a note "bebeyo".')); }); - test.concurrent("Escaped single quote", async () => { - const res = await exe("<: 'ai saw a note \\'bebeyo\\'.'"); - eq(res, STR("ai saw a note 'bebeyo'.")); + test.concurrent('Escaped single quote', async () => { + const res = await exe('<: \'ai saw a note \\\'bebeyo\\\'.\''); + eq(res, STR('ai saw a note \'bebeyo\'.')); }); - test.concurrent("bool (true)", async () => { + test.concurrent('bool (true)', async () => { const res = await exe(` <: true `); eq(res, BOOL(true)); }); - test.concurrent("bool (false)", async () => { + test.concurrent('bool (false)', async () => { const res = await exe(` <: false `); eq(res, BOOL(false)); }); - test.concurrent("number (Int)", async () => { + test.concurrent('number (Int)', async () => { const res = await exe(` <: 10 `); eq(res, NUM(10)); }); - test.concurrent("number (Float)", async () => { + test.concurrent('number (Float)', async () => { const res = await exe(` <: 0.5 `); eq(res, NUM(0.5)); }); - test.concurrent("arr (separated by comma)", async () => { + test.concurrent('arr (separated by comma)', async () => { const res = await exe(` <: [1, 2, 3] `); eq(res, ARR([NUM(1), NUM(2), NUM(3)])); }); - test.concurrent( - "arr (separated by comma) (with trailing comma)", - async () => { - const res = await exe(` + test.concurrent('arr (separated by comma) (with trailing comma)', async () => { + const res = await exe(` <: [1, 2, 3,] `); - eq(res, ARR([NUM(1), NUM(2), NUM(3)])); - } - ); + eq(res, ARR([NUM(1), NUM(2), NUM(3)])); + }); - test.concurrent("arr (separated by line break)", async () => { + test.concurrent('arr (separated by line break)', async () => { const res = await exe(` <: [ 1 @@ -1985,7 +1925,7 @@ describe("literal", () => { eq(res, ARR([NUM(1), NUM(2), NUM(3)])); }); - test.concurrent("arr (separated by line break and comma)", async () => { + test.concurrent('arr (separated by line break and comma)', async () => { const res = await exe(` <: [ 1, @@ -1996,91 +1936,46 @@ describe("literal", () => { eq(res, ARR([NUM(1), NUM(2), NUM(3)])); }); - test.concurrent( - "arr (separated by line break and comma) (with trailing comma)", - async () => { - const res = await exe(` + test.concurrent('arr (separated by line break and comma) (with trailing comma)', async () => { + const res = await exe(` <: [ 1, 2, 3, ] `); - eq(res, ARR([NUM(1), NUM(2), NUM(3)])); - } - ); + eq(res, ARR([NUM(1), NUM(2), NUM(3)])); + }); - test.concurrent("obj (separated by comma)", async () => { + test.concurrent('obj (separated by comma)', async () => { const res = await exe(` <: { a: 1, b: 2, c: 3 } `); - eq( - res, - OBJ( - new Map([ - ["a", NUM(1)], - ["b", NUM(2)], - ["c", NUM(3)], - ]) - ) - ); + eq(res, OBJ(new Map([['a', NUM(1)], ['b', NUM(2)], ['c', NUM(3)]]))); }); - test.concurrent( - "obj (separated by comma) (with trailing comma)", - async () => { - const res = await exe(` + test.concurrent('obj (separated by comma) (with trailing comma)', async () => { + const res = await exe(` <: { a: 1, b: 2, c: 3, } `); - eq( - res, - OBJ( - new Map([ - ["a", NUM(1)], - ["b", NUM(2)], - ["c", NUM(3)], - ]) - ) - ); - } - ); + eq(res, OBJ(new Map([['a', NUM(1)], ['b', NUM(2)], ['c', NUM(3)]]))); + }); - test.concurrent("obj (separated by semicolon)", async () => { + test.concurrent('obj (separated by semicolon)', async () => { const res = await exe(` <: { a: 1; b: 2; c: 3 } `); - eq( - res, - OBJ( - new Map([ - ["a", NUM(1)], - ["b", NUM(2)], - ["c", NUM(3)], - ]) - ) - ); + eq(res, OBJ(new Map([['a', NUM(1)], ['b', NUM(2)], ['c', NUM(3)]]))); }); - test.concurrent( - "obj (separated by semicolon) (with trailing semicolon)", - async () => { - const res = await exe(` + test.concurrent('obj (separated by semicolon) (with trailing semicolon)', async () => { + const res = await exe(` <: { a: 1; b: 2; c: 3; } `); - eq( - res, - OBJ( - new Map([ - ["a", NUM(1)], - ["b", NUM(2)], - ["c", NUM(3)], - ]) - ) - ); - } - ); + eq(res, OBJ(new Map([['a', NUM(1)], ['b', NUM(2)], ['c', NUM(3)]]))); + }); - test.concurrent("obj (separated by line break)", async () => { + test.concurrent('obj (separated by line break)', async () => { const res = await exe(` <: { a: 1 @@ -2088,19 +1983,10 @@ describe("literal", () => { c: 3 } `); - eq( - res, - OBJ( - new Map([ - ["a", NUM(1)], - ["b", NUM(2)], - ["c", NUM(3)], - ]) - ) - ); + eq(res, OBJ(new Map([['a', NUM(1)], ['b', NUM(2)], ['c', NUM(3)]]))); }); - test.concurrent("obj (separated by line break and semicolon)", async () => { + test.concurrent('obj (separated by line break and semicolon)', async () => { const res = await exe(` <: { a: 1; @@ -2108,42 +1994,21 @@ describe("literal", () => { c: 3 } `); - eq( - res, - OBJ( - new Map([ - ["a", NUM(1)], - ["b", NUM(2)], - ["c", NUM(3)], - ]) - ) - ); + eq(res, OBJ(new Map([['a', NUM(1)], ['b', NUM(2)], ['c', NUM(3)]]))); }); - test.concurrent( - "obj (separated by line break and semicolon) (with trailing semicolon)", - async () => { - const res = await exe(` + test.concurrent('obj (separated by line break and semicolon) (with trailing semicolon)', async () => { + const res = await exe(` <: { a: 1; b: 2; c: 3; } `); - eq( - res, - OBJ( - new Map([ - ["a", NUM(1)], - ["b", NUM(2)], - ["c", NUM(3)], - ]) - ) - ); - } - ); + eq(res, OBJ(new Map([['a', NUM(1)], ['b', NUM(2)], ['c', NUM(3)]]))); + }); - test.concurrent("obj and arr (separated by line break)", async () => { + test.concurrent('obj and arr (separated by line break)', async () => { const res = await exe(` <: { a: 1 @@ -2155,30 +2020,25 @@ describe("literal", () => { c: 3 } `); - eq( - res, - OBJ( - new Map([ - ["a", NUM(1)], - ["b", ARR([NUM(1), NUM(2), NUM(3)])], - ["c", NUM(3)], - ]) - ) - ); + eq(res, OBJ(new Map([ + ['a', NUM(1)], + ['b', ARR([NUM(1), NUM(2), NUM(3)])], + ['c', NUM(3)] + ]))); }); }); -describe("type declaration", () => { - test.concurrent("def", async () => { +describe('type declaration', () => { + test.concurrent('def', async () => { const res = await exe(` let abc: num = 1 var xyz: str = "abc" <: [abc xyz] `); - eq(res, ARR([NUM(1), STR("abc")])); + eq(res, ARR([NUM(1), STR('abc')])); }); - test.concurrent("fn def", async () => { + test.concurrent('fn def', async () => { const res = await exe(` @f(x: arr, y: str, z: @(num) => bool): arr { x[3] = 0 @@ -2194,24 +2054,18 @@ describe("type declaration", () => { }); }); -describe("meta", () => { - test.concurrent("default meta", async () => { +describe('meta', () => { + test.concurrent('default meta', async () => { const res = getMeta(` ### { a: 1; b: 2; c: 3; } `); - eq( - res, - new Map([ - [ - null, - { - a: 1, - b: 2, - c: 3, - }, - ], - ]) - ); + eq(res, new Map([ + [null, { + a: 1, + b: 2, + c: 3, + }] + ])); eq(res!.get(null), { a: 1, b: 2, @@ -2219,51 +2073,61 @@ describe("meta", () => { }); }); - describe("String", () => { - test.concurrent("valid", async () => { + describe('String', () => { + test.concurrent('valid', async () => { const res = getMeta(` ### x "hoge" `); - eq(res, new Map([["x", "hoge"]])); + eq(res, new Map([ + ['x', 'hoge'] + ])); }); }); - describe("Number", () => { - test.concurrent("valid", async () => { + describe('Number', () => { + test.concurrent('valid', async () => { const res = getMeta(` ### x 42 `); - eq(res, new Map([["x", 42]])); + eq(res, new Map([ + ['x', 42] + ])); }); }); - describe("Boolean", () => { - test.concurrent("valid", async () => { + describe('Boolean', () => { + test.concurrent('valid', async () => { const res = getMeta(` ### x true `); - eq(res, new Map([["x", true]])); + eq(res, new Map([ + ['x', true] + ])); }); }); - describe("Null", () => { - test.concurrent("valid", async () => { + describe('Null', () => { + test.concurrent('valid', async () => { const res = getMeta(` ### x null `); - eq(res, new Map([["x", null]])); + eq(res, new Map([ + ['x', null] + ])); }); }); - describe("Array", () => { - test.concurrent("valid", async () => { + describe('Array', () => { + test.concurrent('valid', async () => { const res = getMeta(` ### x [1 2 3] `); - eq(res, new Map([["x", [1, 2, 3]]])); + eq(res, new Map([ + ['x', [1, 2, 3]] + ])); }); - test.concurrent("invalid", async () => { + test.concurrent('invalid', async () => { try { getMeta(` ### x [1 (2 + 2) 3] @@ -2276,27 +2140,21 @@ describe("meta", () => { }); }); - describe("Object", () => { - test.concurrent("valid", async () => { + describe('Object', () => { + test.concurrent('valid', async () => { const res = getMeta(` ### x { a: 1; b: 2; c: 3; } `); - eq( - res, - new Map([ - [ - "x", - { - a: 1, - b: 2, - c: 3, - }, - ], - ]) - ); + eq(res, new Map([ + ['x', { + a: 1, + b: 2, + c: 3, + }] + ])); }); - test.concurrent("invalid", async () => { + test.concurrent('invalid', async () => { try { getMeta(` ### x { a: 1; b: (2 + 2); c: 3; } @@ -2309,8 +2167,8 @@ describe("meta", () => { }); }); - describe("Template", () => { - test.concurrent("invalid", async () => { + describe('Template', () => { + test.concurrent('invalid', async () => { try { getMeta(` ### x \`foo {bar} baz\` @@ -2323,8 +2181,8 @@ describe("meta", () => { }); }); - describe("Expression", () => { - test.concurrent("invalid", async () => { + describe('Expression', () => { + test.concurrent('invalid', async () => { try { getMeta(` ### x (1 + 1) @@ -2338,18 +2196,18 @@ describe("meta", () => { }); }); -describe("lang version", () => { - test.concurrent("number", async () => { +describe('lang version', () => { + test.concurrent('number', async () => { const res = utils.getLangVersion(` /// @2021 @f(x) { x } `); - assert.strictEqual(res, "2021"); + assert.strictEqual(res, '2021'); }); - test.concurrent("chars", async () => { + test.concurrent('chars', async () => { const res = utils.getLangVersion(` /// @ canary const a = 1 @@ -2358,20 +2216,20 @@ describe("lang version", () => { } f(a) `); - assert.strictEqual(res, "canary"); + assert.strictEqual(res, 'canary'); }); - test.concurrent("complex", async () => { + test.concurrent('complex', async () => { const res = utils.getLangVersion(` /// @ 2.0-Alpha @f(x) { x } `); - assert.strictEqual(res, "2.0-Alpha"); + assert.strictEqual(res, '2.0-Alpha'); }); - test.concurrent("no specified", async () => { + test.concurrent('no specified', async () => { const res = utils.getLangVersion(` @f(x) { x @@ -2381,8 +2239,8 @@ describe("lang version", () => { }); }); -describe("Attribute", () => { - test.concurrent("single attribute with function (str)", async () => { +describe('Attribute', () => { + test.concurrent('single attribute with function (str)', async () => { let node: Ast.Node; let attr: Ast.Attribute; const parser = new Parser(); @@ -2394,24 +2252,22 @@ describe("Attribute", () => { `); assert.equal(nodes.length, 1); node = nodes[0]; - if (node.type !== "def") assert.fail(); - assert.equal(node.name, "onRecieved"); + if (node.type !== 'def') assert.fail(); + assert.equal(node.name, 'onRecieved'); assert.equal(node.attr.length, 1); // attribute 1 attr = node.attr[0]; - if (attr.type !== "attr") assert.fail(); - assert.equal(attr.name, "Event"); - if (attr.value.type !== "str") assert.fail(); - assert.equal(attr.value.value, "Recieved"); - }); - - test.concurrent( - "multiple attributes with function (obj, str, bool)", - async () => { - let node: Ast.Node; - let attr: Ast.Attribute; - const parser = new Parser(); - const nodes = parser.parse(` + if (attr.type !== 'attr') assert.fail(); + assert.equal(attr.name, 'Event'); + if (attr.value.type !== 'str') assert.fail(); + assert.equal(attr.value.value, 'Recieved'); + }); + + test.concurrent('multiple attributes with function (obj, str, bool)', async () => { + let node: Ast.Node; + let attr: Ast.Attribute; + const parser = new Parser(); + const nodes = parser.parse(` #[Endpoint { path: "/notes/create"; }] #[Desc "Create a note."] #[Cat true] @@ -2419,44 +2275,44 @@ describe("Attribute", () => { <: text } `); - assert.equal(nodes.length, 1); - node = nodes[0]; - if (node.type !== "def") assert.fail(); - assert.equal(node.name, "createNote"); - assert.equal(node.attr.length, 3); - // attribute 1 - attr = node.attr[0]; - if (attr.type !== "attr") assert.fail(); - assert.equal(attr.name, "Endpoint"); - if (attr.value.type !== "obj") assert.fail(); - assert.equal(attr.value.value.size, 1); - for (const [k, v] of attr.value.value) { - if (k === "path") { - if (v.type !== "str") assert.fail(); - assert.equal(v.value, "/notes/create"); - } else { - assert.fail(); - } + assert.equal(nodes.length, 1); + node = nodes[0]; + if (node.type !== 'def') assert.fail(); + assert.equal(node.name, 'createNote'); + assert.equal(node.attr.length, 3); + // attribute 1 + attr = node.attr[0]; + if (attr.type !== 'attr') assert.fail(); + assert.equal(attr.name, 'Endpoint'); + if (attr.value.type !== 'obj') assert.fail(); + assert.equal(attr.value.value.size, 1); + for (const [k, v] of attr.value.value) { + if (k === 'path') { + if (v.type !== 'str') assert.fail(); + assert.equal(v.value, '/notes/create'); + } + else { + assert.fail(); } - // attribute 2 - attr = node.attr[1]; - if (attr.type !== "attr") assert.fail(); - assert.equal(attr.name, "Desc"); - if (attr.value.type !== "str") assert.fail(); - assert.equal(attr.value.value, "Create a note."); - // attribute 3 - attr = node.attr[2]; - if (attr.type !== "attr") assert.fail(); - assert.equal(attr.name, "Cat"); - if (attr.value.type !== "bool") assert.fail(); - assert.equal(attr.value.value, true); - } - ); + } + // attribute 2 + attr = node.attr[1]; + if (attr.type !== 'attr') assert.fail(); + assert.equal(attr.name, 'Desc'); + if (attr.value.type !== 'str') assert.fail(); + assert.equal(attr.value.value, 'Create a note.'); + // attribute 3 + attr = node.attr[2]; + if (attr.type !== 'attr') assert.fail(); + assert.equal(attr.name, 'Cat'); + if (attr.value.type !== 'bool') assert.fail(); + assert.equal(attr.value.value, true); + }); // TODO: attributed function in block // TODO: attribute target does not exist - test.concurrent("single attribute (no value)", async () => { + test.concurrent('single attribute (no value)', async () => { let node: Ast.Node; let attr: Ast.Attribute; const parser = new Parser(); @@ -2466,20 +2322,20 @@ describe("Attribute", () => { `); assert.equal(nodes.length, 1); node = nodes[0]; - if (node.type !== "def") assert.fail(); - assert.equal(node.name, "data"); + if (node.type !== 'def') assert.fail(); + assert.equal(node.name, 'data'); assert.equal(node.attr.length, 1); // attribute 1 attr = node.attr[0]; - assert.ok(attr.type === "attr"); - assert.equal(attr.name, "serializable"); - if (attr.value.type !== "bool") assert.fail(); + assert.ok(attr.type === 'attr'); + assert.equal(attr.name, 'serializable'); + if (attr.value.type !== 'bool') assert.fail(); assert.equal(attr.value.value, true); }); }); -describe("Location", () => { - test.concurrent("function", async () => { +describe('Location', () => { + test.concurrent('function', async () => { let node: Ast.Node; const parser = new Parser(); const nodes = parser.parse(` @@ -2492,32 +2348,30 @@ describe("Location", () => { }); }); -describe("Variable declaration", () => { - test.concurrent("Do not assign to let (issue #328)", async () => { +describe('Variable declaration', () => { + test.concurrent('Do not assign to let (issue #328)', async () => { const err = await exe(` let hoge = 33 hoge = 4 - `) - .then(() => undefined) - .catch((err) => err); + `).then(() => undefined).catch(err => err); assert.ok(err instanceof AiScriptRuntimeError); }); -}); +}) -describe("primitive props", () => { - describe("num", () => { - test.concurrent("to_str", async () => { +describe('primitive props', () => { + describe('num', () => { + test.concurrent('to_str', async () => { const res = await exe(` let num = 123 <: num.to_str() `); - eq(res, STR("123")); + eq(res, STR('123')); }); }); - describe("str", () => { - test.concurrent("len", async () => { + describe('str', () => { + test.concurrent('len', async () => { const res = await exe(` let str = "hello" <: str.len @@ -2525,7 +2379,7 @@ describe("primitive props", () => { eq(res, NUM(5)); }); - test.concurrent("to_num", async () => { + test.concurrent('to_num', async () => { const res = await exe(` let str = "123" <: str.to_num() @@ -2533,39 +2387,39 @@ describe("primitive props", () => { eq(res, NUM(123)); }); - test.concurrent("upper", async () => { + test.concurrent('upper', async () => { const res = await exe(` let str = "hello" <: str.upper() `); - eq(res, STR("HELLO")); + eq(res, STR('HELLO')); }); - test.concurrent("lower", async () => { + test.concurrent('lower', async () => { const res = await exe(` let str = "HELLO" <: str.lower() `); - eq(res, STR("hello")); + eq(res, STR('hello')); }); - test.concurrent("trim", async () => { + test.concurrent('trim', async () => { const res = await exe(` let str = " hello " <: str.trim() `); - eq(res, STR("hello")); + eq(res, STR('hello')); }); - test.concurrent("replace", async () => { + test.concurrent('replace', async () => { const res = await exe(` let str = "hello" <: str.replace("l", "x") `); - eq(res, STR("hexxo")); + eq(res, STR('hexxo')); }); - test.concurrent("index_of", async () => { + test.concurrent('index_of', async () => { const res = await exe(` let str = "hello" <: str.index_of("l") @@ -2573,7 +2427,7 @@ describe("primitive props", () => { eq(res, NUM(2)); }); - test.concurrent("incl", async () => { + test.concurrent('incl', async () => { const res = await exe(` let str = "hello" <: [str.incl("ll"), str.incl("x")] @@ -2581,33 +2435,33 @@ describe("primitive props", () => { eq(res, ARR([TRUE, FALSE])); }); - test.concurrent("split", async () => { + test.concurrent('split', async () => { const res = await exe(` let str = "a,b,c" <: str.split(",") `); - eq(res, ARR([STR("a"), STR("b"), STR("c")])); + eq(res, ARR([STR('a'), STR('b'), STR('c')])); }); - test.concurrent("pick", async () => { + test.concurrent('pick', async () => { const res = await exe(` let str = "hello" <: str.pick(1) `); - eq(res, STR("e")); + eq(res, STR('e')); }); - test.concurrent("slice", async () => { + test.concurrent('slice', async () => { const res = await exe(` let str = "hello" <: str.slice(1, 3) `); - eq(res, STR("el")); + eq(res, STR('el')); }); }); - describe("arr", () => { - test.concurrent("len", async () => { + describe('arr', () => { + test.concurrent('len', async () => { const res = await exe(` let arr = [1, 2, 3] <: arr.len @@ -2615,7 +2469,7 @@ describe("primitive props", () => { eq(res, NUM(3)); }); - test.concurrent("push", async () => { + test.concurrent('push', async () => { const res = await exe(` let arr = [1, 2, 3] arr.push(4) @@ -2624,7 +2478,7 @@ describe("primitive props", () => { eq(res, ARR([NUM(1), NUM(2), NUM(3), NUM(4)])); }); - test.concurrent("unshift", async () => { + test.concurrent('unshift', async () => { const res = await exe(` let arr = [1, 2, 3] arr.unshift(4) @@ -2633,7 +2487,7 @@ describe("primitive props", () => { eq(res, ARR([NUM(4), NUM(1), NUM(2), NUM(3)])); }); - test.concurrent("pop", async () => { + test.concurrent('pop', async () => { const res = await exe(` let arr = [1, 2, 3] let popped = arr.pop() @@ -2642,7 +2496,7 @@ describe("primitive props", () => { eq(res, ARR([NUM(3), ARR([NUM(1), NUM(2)])])); }); - test.concurrent("shift", async () => { + test.concurrent('shift', async () => { const res = await exe(` let arr = [1, 2, 3] let shifted = arr.shift() @@ -2651,51 +2505,39 @@ describe("primitive props", () => { eq(res, ARR([NUM(1), ARR([NUM(2), NUM(3)])])); }); - test.concurrent("concat", async () => { + test.concurrent('concat', async () => { const res = await exe(` let arr = [1, 2, 3] let concated = arr.concat([4, 5]) <: [concated, arr] `); - eq( - res, - ARR([ - ARR([NUM(1), NUM(2), NUM(3), NUM(4), NUM(5)]), - ARR([NUM(1), NUM(2), NUM(3)]), - ]) - ); + eq(res, ARR([ + ARR([NUM(1), NUM(2), NUM(3), NUM(4), NUM(5)]), + ARR([NUM(1), NUM(2), NUM(3)]) + ])); }); - test.concurrent("slice", async () => { + test.concurrent('slice', async () => { const res = await exe(` let arr = ["ant", "bison", "camel", "duck", "elephant"] let sliced = arr.slice(2, 4) <: [sliced, arr] `); - eq( - res, - ARR([ - ARR([STR("camel"), STR("duck")]), - ARR([ - STR("ant"), - STR("bison"), - STR("camel"), - STR("duck"), - STR("elephant"), - ]), - ]) - ); + eq(res, ARR([ + ARR([STR('camel'), STR('duck')]), + ARR([STR('ant'), STR('bison'), STR('camel'), STR('duck'), STR('elephant')]) + ])); }); - test.concurrent("join", async () => { + test.concurrent('join', async () => { const res = await exe(` let arr = ["a", "b", "c"] <: arr.join("-") `); - eq(res, STR("a-b-c")); + eq(res, STR('a-b-c')); }); - test.concurrent("map", async () => { + test.concurrent('map', async () => { const res = await exe(` let arr = [1, 2, 3] <: arr.map(@(item) { item * 2 }) @@ -2703,7 +2545,7 @@ describe("primitive props", () => { eq(res, ARR([NUM(2), NUM(4), NUM(6)])); }); - test.concurrent("map with index", async () => { + test.concurrent('map with index', async () => { const res = await exe(` let arr = [1, 2, 3] <: arr.map(@(item, index) { item * index }) @@ -2711,7 +2553,7 @@ describe("primitive props", () => { eq(res, ARR([NUM(0), NUM(2), NUM(6)])); }); - test.concurrent("filter", async () => { + test.concurrent('filter', async () => { const res = await exe(` let arr = [1, 2, 3] <: arr.filter(@(item) { item != 2 }) @@ -2719,7 +2561,7 @@ describe("primitive props", () => { eq(res, ARR([NUM(1), NUM(3)])); }); - test.concurrent("filter with index", async () => { + test.concurrent('filter with index', async () => { const res = await exe(` let arr = [1, 2, 3, 4] <: arr.filter(@(item, index) { item != 2 && index != 3 }) @@ -2727,7 +2569,7 @@ describe("primitive props", () => { eq(res, ARR([NUM(1), NUM(3)])); }); - test.concurrent("reduce", async () => { + test.concurrent('reduce', async () => { const res = await exe(` let arr = [1, 2, 3, 4] <: arr.reduce(@(accumulator, currentValue) { (accumulator + currentValue) }) @@ -2735,7 +2577,7 @@ describe("primitive props", () => { eq(res, NUM(10)); }); - test.concurrent("reduce with index", async () => { + test.concurrent('reduce with index', async () => { const res = await exe(` let arr = [1, 2, 3, 4] <: arr.reduce(@(accumulator, currentValue, index) { (accumulator + (currentValue * index)) } 0) @@ -2743,23 +2585,23 @@ describe("primitive props", () => { eq(res, NUM(20)); }); - test.concurrent("find", async () => { + test.concurrent('find', async () => { const res = await exe(` let arr = ["abc", "def", "ghi"] <: arr.find(@(item) { item.incl("e") }) `); - eq(res, STR("def")); + eq(res, STR('def')); }); - test.concurrent("find with index", async () => { + test.concurrent('find with index', async () => { const res = await exe(` let arr = ["abc1", "def1", "ghi1", "abc2", "def2", "ghi2"] <: arr.find(@(item, index) { item.incl("e") && index > 1 }) `); - eq(res, STR("def2")); + eq(res, STR('def2')); }); - test.concurrent("incl", async () => { + test.concurrent('incl', async () => { const res = await exe(` let arr = ["abc", "def", "ghi"] <: [arr.incl("def"), arr.incl("jkl")] @@ -2767,7 +2609,7 @@ describe("primitive props", () => { eq(res, ARR([TRUE, FALSE])); }); - test.concurrent("reverse", async () => { + test.concurrent('reverse', async () => { const res = await exe(` let arr = [1, 2, 3] arr.reverse() @@ -2776,20 +2618,20 @@ describe("primitive props", () => { eq(res, ARR([NUM(3), NUM(2), NUM(1)])); }); - test.concurrent("copy", async () => { + test.concurrent('copy', async () => { const res = await exe(` let arr = [1, 2, 3] let copied = arr.copy() copied.reverse() <: [copied, arr] `); - eq( - res, - ARR([ARR([NUM(3), NUM(2), NUM(1)]), ARR([NUM(1), NUM(2), NUM(3)])]) - ); + eq(res, ARR([ + ARR([NUM(3), NUM(2), NUM(1)]), + ARR([NUM(1), NUM(2), NUM(3)]) + ])); }); - test.concurrent("sort num array", async () => { + test.concurrent('sort num array', async () => { const res = await exe(` var arr = [2, 10, 3] let comp = @(a, b) { a - b } @@ -2799,25 +2641,25 @@ describe("primitive props", () => { eq(res, ARR([NUM(2), NUM(3), NUM(10)])); }); - test.concurrent("sort string array (with Str:lt)", async () => { + test.concurrent('sort string array (with Str:lt)', async () => { const res = await exe(` var arr = ["hoge", "huga", "piyo", "hoge"] arr.sort(Str:lt) <: arr `); - eq(res, ARR([STR("hoge"), STR("hoge"), STR("huga"), STR("piyo")])); + eq(res, ARR([STR('hoge'), STR('hoge'), STR('huga'), STR('piyo')])); }); - test.concurrent("sort string array (with Str:gt)", async () => { + test.concurrent('sort string array (with Str:gt)', async () => { const res = await exe(` var arr = ["hoge", "huga", "piyo", "hoge"] arr.sort(Str:gt) <: arr `); - eq(res, ARR([STR("piyo"), STR("huga"), STR("hoge"), STR("hoge")])); + eq(res, ARR([ STR('piyo'), STR('huga'), STR('hoge'), STR('hoge')])); }); - test.concurrent("sort object array", async () => { + test.concurrent('sort object array', async () => { const res = await exe(` var arr = [{x: 2}, {x: 10}, {x: 3}] let comp = @(a, b) { a.x - b.x } @@ -2825,121 +2667,83 @@ describe("primitive props", () => { arr.sort(comp) <: arr `); - eq( - res, - ARR([ - OBJ(new Map([["x", NUM(2)]])), - OBJ(new Map([["x", NUM(3)]])), - OBJ(new Map([["x", NUM(10)]])), - ]) - ); + eq(res, ARR([OBJ(new Map([['x', NUM(2)]])), OBJ(new Map([['x', NUM(3)]])), OBJ(new Map([['x', NUM(10)]]))])); }); }); }); -describe("std", () => { - describe("Core", () => { - test.concurrent("range", async () => { - eq( - await exe("<: Core:range(1, 10)"), - ARR([ - NUM(1), - NUM(2), - NUM(3), - NUM(4), - NUM(5), - NUM(6), - NUM(7), - NUM(8), - NUM(9), - NUM(10), - ]) - ); - eq(await exe("<: Core:range(1, 1)"), ARR([NUM(1)])); - eq(await exe("<: Core:range(9, 7)"), ARR([NUM(9), NUM(8), NUM(7)])); - }); - - test.concurrent("to_str", async () => { - eq(await exe('<: Core:to_str("abc")'), STR("abc")); - eq(await exe("<: Core:to_str(123)"), STR("123")); - eq(await exe("<: Core:to_str(true)"), STR("true")); - eq(await exe("<: Core:to_str(false)"), STR("false")); - eq(await exe("<: Core:to_str(null)"), STR("null")); - eq( - await exe('<: Core:to_str({ a: "abc", b: 1234 })'), - STR('{ a: "abc", b: 1234 }') - ); - eq( - await exe("<: Core:to_str([ true, 123, null ])"), - STR("[ true, 123, null ]") - ); - eq( - await exe("<: Core:to_str(@( a, b, c ) {})"), - STR("@( a, b, c ) { ... }") - ); - eq( - await exe(` +describe('std', () => { + describe('Core', () => { + test.concurrent('range', async () => { + eq(await exe('<: Core:range(1, 10)'), ARR([NUM(1), NUM(2), NUM(3), NUM(4), NUM(5), NUM(6), NUM(7), NUM(8), NUM(9), NUM(10)])); + eq(await exe('<: Core:range(1, 1)'), ARR([NUM(1)])); + eq(await exe('<: Core:range(9, 7)'), ARR([NUM(9), NUM(8), NUM(7)])); + }); + + test.concurrent('to_str', async () => { + eq(await exe('<: Core:to_str("abc")'), STR('abc')); + eq(await exe('<: Core:to_str(123)'), STR('123')); + eq(await exe('<: Core:to_str(true)'), STR('true')); + eq(await exe('<: Core:to_str(false)'), STR('false')); + eq(await exe('<: Core:to_str(null)'), STR('null')); + eq(await exe('<: Core:to_str({ a: "abc", b: 1234 })'), STR('{ a: "abc", b: 1234 }')); + eq(await exe('<: Core:to_str([ true, 123, null ])'), STR('[ true, 123, null ]')); + eq(await exe('<: Core:to_str(@( a, b, c ) {})'), STR('@( a, b, c ) { ... }')); + eq(await exe(` let arr = [] arr.push(arr) <: Core:to_str(arr) - `), - STR("[ ... ]") - ); - eq( - await exe(` + `), STR('[ ... ]')); + eq(await exe(` let arr = [] arr.push({ value: arr }) <: Core:to_str(arr) - `), - STR("[ { value: ... } ]") - ); + `), STR('[ { value: ... } ]')); }); }); - describe("Arr", () => {}); - - describe("Math", () => { - test.concurrent("trig", async () => { + describe('Arr', () => { + }); + + describe('Math', () => { + test.concurrent('trig', async () => { eq(await exe("<: Math:sin(Math:PI / 2)"), NUM(1)); eq(await exe("<: Math:sin(0 - (Math:PI / 2))"), NUM(-1)); - eq( - await exe("<: Math:sin(Math:PI / 4) * Math:cos(Math:PI / 4)"), - NUM(0.5) - ); + eq(await exe("<: Math:sin(Math:PI / 4) * Math:cos(Math:PI / 4)"), NUM(0.5)); }); - test.concurrent("abs", async () => { + test.concurrent('abs', async () => { eq(await exe("<: Math:abs(1 - 6)"), NUM(5)); }); - test.concurrent("pow and sqrt", async () => { + test.concurrent('pow and sqrt', async () => { eq(await exe("<: Math:sqrt(3^2 + 4^2)"), NUM(5)); }); - test.concurrent("round", async () => { + test.concurrent('round', async () => { eq(await exe("<: Math:round(3.14)"), NUM(3)); eq(await exe("<: Math:round(-1.414213)"), NUM(-1)); }); - test.concurrent("ceil", async () => { + test.concurrent('ceil', async () => { eq(await exe("<: Math:ceil(2.71828)"), NUM(3)); eq(await exe("<: Math:ceil(0 - Math:PI)"), NUM(-3)); eq(await exe("<: Math:ceil(1 / Math:Infinity)"), NUM(0)); }); - test.concurrent("floor", async () => { + test.concurrent('floor', async () => { eq(await exe("<: Math:floor(23.14069)"), NUM(23)); eq(await exe("<: Math:floor(Math:Infinity / 0)"), NUM(Infinity)); }); - test.concurrent("min", async () => { + test.concurrent('min', async () => { eq(await exe("<: Math:min(2, 3)"), NUM(2)); }); - test.concurrent("max", async () => { + test.concurrent('max', async () => { eq(await exe("<: Math:max(-2, -3)"), NUM(-2)); }); - + /* flaky test.concurrent('rnd', async () => { const steps = 512; @@ -2976,11 +2780,11 @@ describe("std", () => { }); */ - test.concurrent("rnd with arg", async () => { + test.concurrent('rnd with arg', async () => { eq(await exe("<: Math:rnd(1, 1.5)"), NUM(1)); }); - test.concurrent("gen_rng", async () => { + test.concurrent('gen_rng', async () => { const res = await exe(` @test(seed) { let random = Math:gen_rng(seed) @@ -2991,77 +2795,74 @@ describe("std", () => { let test1 = if (test(seed1) == test(seed1)) {true} else {false} let test2 = if (test(seed1) == test(seed2)) {true} else {false} <: [test1 test2] - `); + `) eq(res, ARR([BOOL(true), BOOL(false)])); }); }); - describe("Obj", () => { - test.concurrent("keys", async () => { + describe('Obj', () => { + test.concurrent('keys', async () => { const res = await exe(` let o = { a: 1; b: 2; c: 3; } <: Obj:keys(o) `); - eq(res, ARR([STR("a"), STR("b"), STR("c")])); + eq(res, ARR([STR('a'), STR('b'), STR('c')])); }); - test.concurrent("vals", async () => { + test.concurrent('vals', async () => { const res = await exe(` let o = { _nul: null; _num: 24; _str: 'hoge'; _arr: []; _obj: {}; } <: Obj:vals(o) `); - eq(res, ARR([NULL, NUM(24), STR("hoge"), ARR([]), OBJ(new Map([]))])); + eq(res, ARR([NULL, NUM(24), STR('hoge'), ARR([]), OBJ(new Map([]))])); }); - test.concurrent("kvs", async () => { + test.concurrent('kvs', async () => { const res = await exe(` let o = { a: 1; b: 2; c: 3; } <: Obj:kvs(o) `); - eq( - res, - ARR([ - ARR([STR("a"), NUM(1)]), - ARR([STR("b"), NUM(2)]), - ARR([STR("c"), NUM(3)]), - ]) - ); + eq(res, ARR([ + ARR([STR('a'), NUM(1)]), + ARR([STR('b'), NUM(2)]), + ARR([STR('c'), NUM(3)]) + ])); }); - test.concurrent("merge", async () => { + test.concurrent('merge', async () => { const res = await exe(` let o1 = { a: 1; b: 2; } let o2 = { b: 3; c: 4; } <: Obj:merge(o1, o2) `); - eq(res, utils.jsToVal({ a: 1, b: 3, c: 4 })); + eq(res, utils.jsToVal({ a: 1, b: 3, c: 4})); }); }); - describe("Str", () => { - test.concurrent("lf", async () => { + describe('Str', () => { + test.concurrent('lf', async () => { const res = await exe(` <: Str:lf `); - eq(res, STR("\n")); + eq(res, STR('\n')); }); - test.concurrent("from_codepoint", async () => { + test.concurrent('from_codepoint', async () => { const res = await exe(` <: Str:from_codepoint(65) `); - eq(res, STR("A")); + eq(res, STR('A')); }); - test.concurrent("codepoint_at", async () => { + test.concurrent('codepoint_at', async () => { let res = await exe(` <: "aiscript".split().map(@(x, _) { x.codepoint_at(0) }) `); - eq(res, ARR([97, 105, 115, 99, 114, 105, 112, 116].map((x) => NUM(x)))); + eq(res, ARR([97, 105, 115, 99, 114, 105, 112, 116].map(x => NUM(x)))); res = await exe(` <: "".codepoint_at(0) @@ -3070,27 +2871,32 @@ describe("std", () => { }); }); - describe("Error", () => { - test.concurrent("create", async () => { + describe('Error', () => { + test.concurrent('create', async () => { eq( await exe(` <: Error:create('ai', {chan: 'kawaii'}) `), - ERROR("ai", OBJ(new Map([["chan", STR("kawaii")]]))) + ERROR('ai', OBJ(new Map([['chan', STR('kawaii')]]))) ); }); }); - describe("Json", () => { - test.concurrent("stringify: fn", async () => { + describe('Json', () => { + test.concurrent('stringify: fn', async () => { const res = await exe(` <: Json:stringify(@(){}) `); eq(res, STR('""')); }); - test.concurrent("parsable", async () => { - ["null", '"hoge"', "[]", "{}"].forEach(async (str) => { + test.concurrent('parsable', async () => { + [ + 'null', + '"hoge"', + '[]', + '{}', + ].forEach(async (str) => { const res = await exe(` <: [ Json:parsable('${str}') @@ -3100,52 +2906,56 @@ describe("std", () => { eq(res, ARR([TRUE, STR(str)])); }); }); - test.concurrent("unparsable", async () => { - ["", "hoge", "["].forEach(async (str) => { + test.concurrent('unparsable', async () => { + [ + '', + 'hoge', + '[', + ].forEach(async (str) => { const res = await exe(` <: [ Json:parsable('${str}') Json:parse('${str}') ] `); - eq(res, ARR([FALSE, ERROR("not_json")])); + eq(res, ARR([FALSE, ERROR('not_json')])); }); }); }); }); -describe("Unicode", () => { - test.concurrent("len", async () => { +describe('Unicode', () => { + test.concurrent('len', async () => { const res = await exe(` <: "👍🏽🍆🌮".len `); eq(res, NUM(3)); }); - test.concurrent("pick", async () => { + test.concurrent('pick', async () => { const res = await exe(` <: "👍🏽🍆🌮".pick(1) `); - eq(res, STR("🍆")); + eq(res, STR('🍆')); }); - test.concurrent("slice", async () => { + test.concurrent('slice', async () => { const res = await exe(` <: "Emojis 👍🏽 are 🍆 poison. 🌮s are bad.".slice(7, 14) `); - eq(res, STR("👍🏽 are 🍆")); + eq(res, STR('👍🏽 are 🍆')); }); - test.concurrent("split", async () => { + test.concurrent('split', async () => { const res = await exe(` <: "👍🏽🍆🌮".split() `); - eq(res, ARR([STR("👍🏽"), STR("🍆"), STR("🌮")])); + eq(res, ARR([STR('👍🏽'), STR('🍆'), STR('🌮')])); }); }); -describe("Security", () => { - test.concurrent("Cannot access js native property via var", async () => { +describe('Security', () => { + test.concurrent('Cannot access js native property via var', async () => { try { await exe(` <: constructor @@ -3174,7 +2984,7 @@ describe("Security", () => { } }); - test.concurrent("Cannot access js native property via object", async () => { + test.concurrent('Cannot access js native property via object', async () => { const res1 = await exe(` let obj = {} @@ -3197,41 +3007,38 @@ describe("Security", () => { eq(res3, NULL); }); - test.concurrent( - "Cannot access js native property via primitive prop", - async () => { - try { - await exe(` + test.concurrent('Cannot access js native property via primitive prop', async () => { + try { + await exe(` <: "".constructor `); - assert.fail(); - } catch (e) { - assert.ok(e instanceof AiScriptRuntimeError); - } + assert.fail(); + } catch (e) { + assert.ok(e instanceof AiScriptRuntimeError); + } - try { - await exe(` + try { + await exe(` <: "".prototype `); - assert.fail(); - } catch (e) { - assert.ok(e instanceof AiScriptRuntimeError); - } + assert.fail(); + } catch (e) { + assert.ok(e instanceof AiScriptRuntimeError); + } - try { - await exe(` + try { + await exe(` <: "".__proto__ `); - assert.fail(); - } catch (e) { - assert.equal(e instanceof AiScriptRuntimeError, true); - } + assert.fail(); + } catch (e) { + assert.equal(e instanceof AiScriptRuntimeError, true); } - ); + }); }); -describe("extra", () => { - test.concurrent("Fizz Buzz", async () => { +describe('extra', () => { + test.concurrent('Fizz Buzz', async () => { const res = await exe(` let res = [] for (let i = 1, 15) { @@ -3244,29 +3051,26 @@ describe("extra", () => { } <: res `); - eq( - res, - ARR([ - NUM(1), - NUM(2), - STR("Fizz"), - NUM(4), - STR("Buzz"), - STR("Fizz"), - NUM(7), - NUM(8), - STR("Fizz"), - STR("Buzz"), - NUM(11), - STR("Fizz"), - NUM(13), - NUM(14), - STR("FizzBuzz"), - ]) - ); - }); - - test.concurrent("SKI", async () => { + eq(res, ARR([ + NUM(1), + NUM(2), + STR('Fizz'), + NUM(4), + STR('Buzz'), + STR('Fizz'), + NUM(7), + NUM(8), + STR('Fizz'), + STR('Buzz'), + NUM(11), + STR('Fizz'), + NUM(13), + NUM(14), + STR('FizzBuzz'), + ])); + }); + + test.concurrent('SKI', async () => { const res = await exe(` let s = @(x) { @(y) { @(z) { //let f = x(z) f(@(a){ let g = y(z) g(a) }) @@ -3296,6 +3100,6 @@ describe("extra", () => { let sksik = [s, [k, [s, i]], k] c([sksik, "foo", print]) `); - eq(res, STR("foo")); + eq(res, STR('foo')); }); });