diff --git a/src/parser/plugins/validate-keyword.ts b/src/parser/plugins/validate-keyword.ts index 6c1fdac1..a179aba7 100644 --- a/src/parser/plugins/validate-keyword.ts +++ b/src/parser/plugins/validate-keyword.ts @@ -2,44 +2,56 @@ import { AiScriptSyntaxError } from '../../error.js'; import { visitNode } from '../visit.js'; import type * as Ast from '../../node.js'; -const reservedWord = [ - 'null', - 'true', - 'false', - 'each', - 'for', - 'loop', - 'break', - 'continue', - 'match', - 'if', - 'elif', - 'else', - 'return', - 'eval', - 'var', - 'let', - 'exists', +// 予約語となっている識別子があるかを確認する。 +// - キーワードは字句解析の段階でそれぞれのKeywordトークンとなるため除外 +// - 文脈キーワードは識別子に利用できるため除外 - // future - 'fn', - 'namespace', - 'meta', +const reservedWord = [ + 'as', + 'async', 'attr', 'attribute', - 'static', + 'await', + 'catch', 'class', - 'struct', - 'module', - 'while', - 'import', - 'export', // 'const', + 'component', + 'constructor', // 'def', + 'dictionary', + 'do', + 'enum', + 'export', + 'finally', + 'fn', // 'func', // 'function', - // 'ref', - // 'out', + 'hash', + 'in', + 'interface', + 'out', + 'private', + 'public', + 'ref', + 'static', + 'struct', + 'table', + 'this', + 'throw', + 'trait', + 'try', + 'undefined', + 'use', + 'using', + 'when', + 'while', + 'yield', + 'import', + 'is', + 'meta', + 'module', + 'namespace', + 'new', ]; function throwReservedWordError(name: string, loc: Ast.Loc): void { @@ -48,10 +60,11 @@ function throwReservedWordError(name: string, loc: Ast.Loc): void { function validateNode(node: Ast.Node): Ast.Node { switch (node.type) { + case 'ns': case 'def': case 'attr': - case 'ns': - case 'identifier': { + case 'identifier': + case 'prop': { if (reservedWord.includes(node.name)) { throwReservedWordError(node.name, node.loc); } @@ -63,6 +76,18 @@ function validateNode(node: Ast.Node): Ast.Node { } break; } + case 'each': { + if (reservedWord.includes(node.var)) { + throwReservedWordError(node.var, node.loc); + } + break; + } + case 'for': { + if (node.var != null && reservedWord.includes(node.var)) { + throwReservedWordError(node.var, node.loc); + } + break; + } case 'fn': { for (const arg of node.args) { if (reservedWord.includes(arg.name)) { @@ -71,6 +96,14 @@ function validateNode(node: Ast.Node): Ast.Node { } break; } + case 'obj': { + for (const name of node.value.keys()) { + if (reservedWord.includes(name)) { + throwReservedWordError(name, node.loc); + } + } + break; + } } return node; diff --git a/test/index.ts b/test/index.ts index 48cb8a6b..4fab2a0b 100644 --- a/test/index.ts +++ b/test/index.ts @@ -6,6 +6,7 @@ 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 { AiScriptSyntaxError } from '../src/error'; let { AiScriptRuntimeError, AiScriptIndexOutOfRangeError } = errors; const exe = (program: string): Promise => new Promise((ok, err) => { @@ -2886,7 +2887,7 @@ describe('Security', () => { `); assert.fail(); } catch (e) { - assert.ok(e instanceof AiScriptRuntimeError); + assert.ok(e instanceof AiScriptSyntaxError); } try { @@ -2909,13 +2910,6 @@ describe('Security', () => { }); test.concurrent('Cannot access js native property via object', async () => { - const res1 = await exe(` - let obj = {} - - <: obj.constructor - `); - eq(res1, NULL); - const res2 = await exe(` let obj = {} @@ -2938,7 +2932,7 @@ describe('Security', () => { `); assert.fail(); } catch (e) { - assert.ok(e instanceof AiScriptRuntimeError); + assert.ok(e instanceof AiScriptSyntaxError); } try {