Repository: syuilo/aiscript
Pull request #357 (2023-09-17 03:01:15)

エラーのクラス名を変更
AiScriptErrorのサブクラスにAiScript-の接頭辞をつけます。
## why
#351 

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/syuilo/aiscript/pull/357?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo) Report
Patch coverage: **`61.66%`** and project coverage change: **`+0.03%`** :tada:
> Comparison is base [(`36fab97`)](https://app.codecov.io/gh/syuilo/aiscript/commit/36fab971b20a6086f2493fb4f168907abad96fe2?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo) 88.51% compared to head [(`9ec8a75`)](https://app.codecov.io/gh/syuilo/aiscript/pull/357?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo) 88.55%.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #357      +/-   ##
==========================================
+ Coverage   88.51%   88.55%   +0.03%     
==========================================
  Files          21       21              
  Lines        3432     3442      +10     
  Branches      561      561              
==========================================
+ Hits         3038     3048      +10     
  Misses        383      383              
  Partials       11       11              
```


| [Files Changed](https://app.codecov.io/gh/syuilo/aiscript/pull/357?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo) | Coverage Δ | |
|---|---|---|
| [src/interpreter/util.ts](https://app.codecov.io/gh/syuilo/aiscript/pull/357?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo#diff-c3JjL2ludGVycHJldGVyL3V0aWwudHM=) | `67.69% <21.42%> (ø)` | |
| [src/interpreter/index.ts](https://app.codecov.io/gh/syuilo/aiscript/pull/357?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo#diff-c3JjL2ludGVycHJldGVyL2luZGV4LnRz) | `93.14% <33.33%> (ø)` | |
| [src/parser/plugins/set-attribute.ts](https://app.codecov.io/gh/syuilo/aiscript/pull/357?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo#diff-c3JjL3BhcnNlci9wbHVnaW5zL3NldC1hdHRyaWJ1dGUudHM=) | `81.25% <33.33%> (ø)` | |
| [src/parser/plugins/infix-to-fncall.ts](https://app.codecov.io/gh/syuilo/aiscript/pull/357?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo#diff-c3JjL3BhcnNlci9wbHVnaW5zL2luZml4LXRvLWZuY2FsbC50cw==) | `98.63% <50.00%> (ø)` | |
| [src/type.ts](https://app.codecov.io/gh/syuilo/aiscript/pull/357?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo#diff-c3JjL3R5cGUudHM=) | `51.57% <50.00%> (ø)` | |
| [src/interpreter/scope.ts](https://app.codecov.io/gh/syuilo/aiscript/pull/357?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo#diff-c3JjL2ludGVycHJldGVyL3Njb3BlLnRz) | `93.47% <60.00%> (ø)` | |
| [src/interpreter/primitive-props.ts](https://app.codecov.io/gh/syuilo/aiscript/pull/357?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo#diff-c3JjL2ludGVycHJldGVyL3ByaW1pdGl2ZS1wcm9wcy50cw==) | `97.11% <66.66%> (ø)` | |
| [src/parser/index.ts](https://app.codecov.io/gh/syuilo/aiscript/pull/357?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo#diff-c3JjL3BhcnNlci9pbmRleC50cw==) | `82.95% <66.66%> (ø)` | |
| [src/error.ts](https://app.codecov.io/gh/syuilo/aiscript/pull/357?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo#diff-c3JjL2Vycm9yLnRz) | `96.49% <100.00%> (+0.74%)` | :arrow_up: |
| [src/interpreter/lib/std.ts](https://app.codecov.io/gh/syuilo/aiscript/pull/357?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo#diff-c3JjL2ludGVycHJldGVyL2xpYi9zdGQudHM=) | `75.17% <100.00%> (ø)` | |
| ... and [1 more](https://app.codecov.io/gh/syuilo/aiscript/pull/357?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo) | |


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/syuilo/aiscript/pull/357?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo).


Patch:
diff --git a/etc/aiscript.api.md b/etc/aiscript.api.md
index f6b0326..52f97e9 100644
--- a/etc/aiscript.api.md
+++ b/etc/aiscript.api.md
@@ -29,6 +29,26 @@ abstract class AiScriptError extends Error {
     info?: any;
 }
 
+// @public
+class AiScriptIndexOutOfRangeError extends AiScriptRuntimeError {
+    constructor(message: string, info?: any);
+}
+
+// @public
+class AiScriptRuntimeError extends AiScriptError {
+    constructor(message: string, info?: any);
+}
+
+// @public
+class AiScriptSyntaxError extends AiScriptError {
+    constructor(message: string, info?: any);
+}
+
+// @public
+class AiScriptTypeError extends AiScriptError {
+    constructor(message: string, info?: any);
+}
+
 // @public (undocumented)
 type And = NodeBase & {
     type: 'and';
@@ -345,10 +365,10 @@ declare namespace errors {
     export {
         AiScriptError,
         NonAiScriptError,
-        SyntaxError_2 as SyntaxError,
-        TypeError_2 as TypeError,
-        RuntimeError,
-        IndexOutOfRangeError
+        AiScriptSyntaxError,
+        AiScriptTypeError,
+        AiScriptRuntimeError,
+        AiScriptIndexOutOfRangeError
     }
 }
 export { errors }
@@ -512,11 +532,6 @@ type IndexChain = NodeBase_2 & {
     index: Expression_2;
 };
 
-// @public (undocumented)
-class IndexOutOfRangeError extends RuntimeError {
-    constructor(message: string, info?: any);
-}
-
 // @public (undocumented)
 type Infix = NodeBase_2 & {
     type: 'infix';
@@ -812,11 +827,6 @@ type Return_2 = NodeBase_2 & {
     expr: Expression_2;
 };
 
-// @public (undocumented)
-class RuntimeError extends AiScriptError {
-    constructor(message: string, info?: any);
-}
-
 // @public (undocumented)
 export class Scope {
     constructor(layerdStates?: Scope['layerdStates'], parent?: Scope, name?: Scope['name']);
@@ -874,11 +884,6 @@ type SubAssign_2 = NodeBase_2 & {
     expr: Expression_2;
 };
 
-// @public (undocumented)
-class SyntaxError_2 extends AiScriptError {
-    constructor(message: string, info?: any);
-}
-
 // @public (undocumented)
 type Tmpl = NodeBase & {
     type: 'tmpl';
@@ -897,11 +902,6 @@ const TRUE: {
     value: boolean;
 };
 
-// @public (undocumented)
-class TypeError_2 extends AiScriptError {
-    constructor(message: string, info?: any);
-}
-
 // @public (undocumented)
 type TypeSource = NamedTypeSource | FnTypeSource;
 
diff --git a/src/error.ts b/src/error.ts
index 8c6be6d..921119e 100644
--- a/src/error.ts
+++ b/src/error.ts
@@ -22,25 +22,35 @@ export class NonAiScriptError extends AiScriptError {
 	}
 }
 
-export class SyntaxError extends AiScriptError {
+/**
+ * Parse-time errors.
+ */
+export class AiScriptSyntaxError extends AiScriptError {
 	constructor(message: string, info?: any) {
 		super(message, info);
 	}
 }
-
-export class TypeError extends AiScriptError {
+/**
+ * Type validation(parser/plugins/validate-type) errors.
+ */ 
+export class AiScriptTypeError extends AiScriptError {
 	constructor(message: string, info?: any) {
 		super(message, info);
 	}
 }
 
-export class RuntimeError extends AiScriptError {
+/**
+ * Interpret-time errors.
+ */
+export class AiScriptRuntimeError extends AiScriptError {
 	constructor(message: string, info?: any) {
 		super(message, info);
 	}
 }
-
-export class IndexOutOfRangeError extends RuntimeError {
+/**
+ * RuntimeError for illegal access to arrays.
+ */
+export class AiScriptIndexOutOfRangeError extends AiScriptRuntimeError {
 	constructor(message: string, info?: any) {
 		super(message, info);
 	}
diff --git a/src/interpreter/index.ts b/src/interpreter/index.ts
index 89e2e5b..a6b018d 100644
--- a/src/interpreter/index.ts
+++ b/src/interpreter/index.ts
@@ -3,7 +3,7 @@
  */
 
 import { autobind } from '../utils/mini-autobind.js';
-import { AiScriptError, NonAiScriptError, IndexOutOfRangeError, RuntimeError } from '../error.js';
+import { AiScriptError, NonAiScriptError, AiScriptIndexOutOfRangeError, AiScriptRuntimeError } from '../error.js';
 import { Scope } from './scope.js';
 import { std } from './lib/std.js';
 import { assertNumber, assertString, assertFunction, assertBoolean, assertObject, assertArray, eq, isObject, isArray, expectAny, reprValue } from './util.js';
@@ -248,7 +248,7 @@ export class Interpreter {
 		if (this.stepCount % IRQ_RATE === IRQ_AT) await new Promise(resolve => setTimeout(resolve, 5));
 		this.stepCount++;
 		if (this.opts.maxStep && this.stepCount > this.opts.maxStep) {
-			throw new RuntimeError('max step exceeded');
+			throw new AiScriptRuntimeError('max step exceeded');
 		}
 
 		switch (node.type) {
@@ -452,7 +452,7 @@ export class Interpreter {
 					assertNumber(i);
 					const item = target.value[i.value];
 					if (item === undefined) {
-						throw new IndexOutOfRangeError(`Index out of range. index: ${i.value} max: ${target.value.length - 1}`);
+						throw new AiScriptIndexOutOfRangeError(`Index out of range. index: ${i.value} max: ${target.value.length - 1}`);
 					}
 					return item;
 				} else if (isObject(target)) {
@@ -463,7 +463,7 @@ export class Interpreter {
 						return NULL;
 					}
 				} else {
-					throw new RuntimeError(`Cannot read prop (${reprValue(i)}) of ${target.type}.`);
+					throw new AiScriptRuntimeError(`Cannot read prop (${reprValue(i)}) of ${target.type}.`);
 				}
 			}
 
@@ -613,7 +613,7 @@ export class Interpreter {
 				assertString(i);
 				assignee.value.set(i.value, value);
 			} else {
-				throw new RuntimeError(`Cannot read prop (${reprValue(i)}) of ${assignee.type}.`);
+				throw new AiScriptRuntimeError(`Cannot read prop (${reprValue(i)}) of ${assignee.type}.`);
 			}
 		} else if (dest.type === 'prop') {
 			const assignee = await this._eval(dest.target, scope);
@@ -621,7 +621,7 @@ export class Interpreter {
 
 			assignee.value.set(dest.name, value);
 		} else {
-			throw new RuntimeError('The left-hand side of an assignment expression must be a variable or a property/index access.');
+			throw new AiScriptRuntimeError('The left-hand side of an assignment expression must be a variable or a property/index access.');
 		}
 	}
 }
diff --git a/src/interpreter/lib/std.ts b/src/interpreter/lib/std.ts
index 91b69ae..481f4ae 100644
--- a/src/interpreter/lib/std.ts
+++ b/src/interpreter/lib/std.ts
@@ -3,7 +3,7 @@ import { v4 as uuid } from 'uuid';
 import seedrandom from 'seedrandom';
 import { NUM, STR, FN_NATIVE, FALSE, TRUE, ARR, NULL, BOOL, OBJ, ERROR } from '../value.js';
 import { assertNumber, assertString, assertBoolean, valToJs, jsToVal, assertFunction, assertObject, eq, expectAny, assertArray, reprValue } from '../util.js';
-import { RuntimeError } from '../../error.js';
+import { AiScriptRuntimeError } from '../../error.js';
 import type { Value } from '../value.js';
 
 export const std: Record<string, Value> = {
@@ -67,7 +67,7 @@ export const std: Record<string, Value> = {
 		assertNumber(a);
 		assertNumber(b);
 		const res = a.value ** b.value;
-		if (isNaN(res)) throw new RuntimeError('Invalid operation.'); // ex. √-1
+		if (isNaN(res)) throw new AiScriptRuntimeError('Invalid operation.'); // ex. √-1
 		return NUM(res);
 	}),
 
@@ -75,7 +75,7 @@ export const std: Record<string, Value> = {
 		assertNumber(a);
 		assertNumber(b);
 		const res = a.value / b.value;
-		if (isNaN(res)) throw new RuntimeError('Invalid operation.');
+		if (isNaN(res)) throw new AiScriptRuntimeError('Invalid operation.');
 		return NUM(res);
 	}),
 
@@ -386,7 +386,7 @@ export const std: Record<string, Value> = {
 	'Math:sqrt': FN_NATIVE(([v]) => {
 		assertNumber(v);
 		const res = Math.sqrt(v.value);
-		if (isNaN(res)) throw new RuntimeError('Invalid operation.');
+		if (isNaN(res)) throw new AiScriptRuntimeError('Invalid operation.');
 		return NUM(res);
 	}),
 
diff --git a/src/interpreter/primitive-props.ts b/src/interpreter/primitive-props.ts
index 3784f8d..fb048e1 100644
--- a/src/interpreter/primitive-props.ts
+++ b/src/interpreter/primitive-props.ts
@@ -1,5 +1,5 @@
 import { substring, length, indexOf, toArray } from 'stringz';
-import { RuntimeError } from '../error.js';
+import { AiScriptRuntimeError } from '../error.js';
 import { assertArray, assertBoolean, assertFunction, assertNumber, assertString, expectAny } from './util.js';
 import { ARR, FALSE, FN_NATIVE, NULL, NUM, STR, TRUE } from './value.js';
 import type { Value, VArr, VFn, VNum, VStr, VError } from './value.js';
@@ -235,9 +235,9 @@ export function getPrimProp(target: Value, name: string): Value {
 		if (Object.hasOwn(props, name)) {
 			return props[name]!(target);
 		} else {
-			throw new RuntimeError(`No such prop (${name}) in ${target.type}.`);
+			throw new AiScriptRuntimeError(`No such prop (${name}) in ${target.type}.`);
 		}
 	} else {
-		throw new RuntimeError(`Cannot read prop of ${target.type}. (reading ${name})`);
+		throw new AiScriptRuntimeError(`Cannot read prop of ${target.type}. (reading ${name})`);
 	}
 }
diff --git a/src/interpreter/scope.ts b/src/interpreter/scope.ts
index 9938252..498d11e 100644
--- a/src/interpreter/scope.ts
+++ b/src/interpreter/scope.ts
@@ -1,5 +1,5 @@
 import { autobind } from '../utils/mini-autobind.js';
-import { RuntimeError } from '../error.js';
+import { AiScriptRuntimeError } from '../error.js';
 import type { Value } from './value.js';
 import type { Variable } from './variable.js';
 
@@ -56,7 +56,7 @@ export class Scope {
 			}
 		}
 
-		throw new RuntimeError(
+		throw new AiScriptRuntimeError(
 			`No such variable '${name}' in scope '${this.name}'`,
 			{ scope: this.layerdStates });
 	}
@@ -99,7 +99,7 @@ export class Scope {
 		this.log('add', { var: name, val: variable });
 		const states = this.layerdStates[0]!;
 		if (states.has(name)) {
-			throw new RuntimeError(
+			throw new AiScriptRuntimeError(
 				`Variable '${name}' is alerady exists in scope '${this.name}'`,
 				{ scope: this.layerdStates });
 		}
@@ -119,7 +119,7 @@ export class Scope {
 			if (layer.has(name)) {
 				const variable = layer.get(name)!;
 				if (!variable.isMutable) {
-					throw new RuntimeError(`Cannot assign to an immutable variable ${name}.`);
+					throw new AiScriptRuntimeError(`Cannot assign to an immutable variable ${name}.`);
 				}
 
 				variable.value = val;
@@ -131,7 +131,7 @@ export class Scope {
 			i++;
 		}
 
-		throw new RuntimeError(
+		throw new AiScriptRuntimeError(
 			`No such variable '${name}' in scope '${this.name}'`,
 			{ scope: this.layerdStates });
 	}
diff --git a/src/interpreter/util.ts b/src/interpreter/util.ts
index 348aaa6..1d60c0f 100644
--- a/src/interpreter/util.ts
+++ b/src/interpreter/util.ts
@@ -1,64 +1,64 @@
-import { RuntimeError } from '../error.js';
+import { AiScriptRuntimeError } from '../error.js';
 import { STR, NUM, ARR, OBJ, NULL, BOOL } from './value.js';
 import type { Value, VStr, VNum, VBool, VFn, VObj, VArr } from './value.js';
 
 export function expectAny(val: Value | null | undefined): asserts val is Value {
 	if (val == null) {
-		throw new RuntimeError('Expect anything, but got nothing.');
+		throw new AiScriptRuntimeError('Expect anything, but got nothing.');
 	}
 }
 
 export function assertBoolean(val: Value | null | undefined): asserts val is VBool {
 	if (val == null) {
-		throw new RuntimeError('Expect boolean, but got nothing.');
+		throw new AiScriptRuntimeError('Expect boolean, but got nothing.');
 	}
 	if (val.type !== 'bool') {
-		throw new RuntimeError(`Expect boolean, but got ${val.type}.`);
+		throw new AiScriptRuntimeError(`Expect boolean, but got ${val.type}.`);
 	}
 }
 
 export function assertFunction(val: Value | null | undefined): asserts val is VFn {
 	if (val == null) {
-		throw new RuntimeError('Expect function, but got nothing.');
+		throw new AiScriptRuntimeError('Expect function, but got nothing.');
 	}
 	if (val.type !== 'fn') {
-		throw new RuntimeError(`Expect function, but got ${val.type}.`);
+		throw new AiScriptRuntimeError(`Expect function, but got ${val.type}.`);
 	}
 }
 
 export function assertString(val: Value | null | undefined): asserts val is VStr {
 	if (val == null) {
-		throw new RuntimeError('Expect string, but got nothing.');
+		throw new AiScriptRuntimeError('Expect string, but got nothing.');
 	}
 	if (val.type !== 'str') {
-		throw new RuntimeError(`Expect string, but got ${val.type}.`);
+		throw new AiScriptRuntimeError(`Expect string, but got ${val.type}.`);
 	}
 }
 
 export function assertNumber(val: Value | null | undefined): asserts val is VNum {
 	if (val == null) {
-		throw new RuntimeError('Expect number, but got nothing.');
+		throw new AiScriptRuntimeError('Expect number, but got nothing.');
 	}
 	if (val.type !== 'num') {
-		throw new RuntimeError(`Expect number, but got ${val.type}.`);
+		throw new AiScriptRuntimeError(`Expect number, but got ${val.type}.`);
 	}
 }
 
 export function assertObject(val: Value | null | undefined): asserts val is VObj {
 	if (val == null) {
-		throw new RuntimeError('Expect object, but got nothing.');
+		throw new AiScriptRuntimeError('Expect object, but got nothing.');
 	}
 	if (val.type !== 'obj') {
-		throw new RuntimeError(`Expect object, but got ${val.type}.`);
+		throw new AiScriptRuntimeError(`Expect object, but got ${val.type}.`);
 	}
 }
 
 export function assertArray(val: Value | null | undefined): asserts val is VArr {
 	if (val == null) {
-		throw new RuntimeError('Expect array, but got nothing.');
+		throw new AiScriptRuntimeError('Expect array, but got nothing.');
 	}
 	if (val.type !== 'arr') {
-		throw new RuntimeError(`Expect array, but got ${val.type}.`);
+		throw new AiScriptRuntimeError(`Expect array, but got ${val.type}.`);
 	}
 }
 
diff --git a/src/parser/index.ts b/src/parser/index.ts
index ef0094a..0c34f64 100644
--- a/src/parser/index.ts
+++ b/src/parser/index.ts
@@ -1,4 +1,4 @@
-import { SyntaxError } from '../error.js';
+import { AiScriptSyntaxError } from '../error.js';
 import * as parser from './parser.js';
 
 import { validateKeyword } from './plugins/validate-keyword.js';
@@ -65,9 +65,9 @@ export class Parser {
 		} catch (e) {
 			if (e.location) {
 				if (e.expected) {
-					throw new SyntaxError(`Parsing error. (Line ${e.location.start.line}:${e.location.start.column})`, e);
+					throw new AiScriptSyntaxError(`Parsing error. (Line ${e.location.start.line}:${e.location.start.column})`, e);
 				} else {
-					throw new SyntaxError(`${e.message} (Line ${e.location.start.line}:${e.location.start.column})`, e);
+					throw new AiScriptSyntaxError(`${e.message} (Line ${e.location.start.line}:${e.location.start.column})`, e);
 				}
 			}
 			throw e;
diff --git a/src/parser/plugins/infix-to-fncall.ts b/src/parser/plugins/infix-to-fncall.ts
index c4862f0..7d55132 100644
--- a/src/parser/plugins/infix-to-fncall.ts
+++ b/src/parser/plugins/infix-to-fncall.ts
@@ -1,5 +1,5 @@
 import { visitNode } from '../visit.js';
-import { SyntaxError } from '../../error.js';
+import { AiScriptSyntaxError } from '../../error.js';
 import type * as Cst from '../node.js';
 
 /**
@@ -122,7 +122,7 @@ function transform(node: Cst.Infix): Cst.Node {
 	const infos = node.operators.map(op => {
 		const info = infoTable[op];
 		if (info == null) {
-			throw new SyntaxError(`No such operator: ${op}.`);
+			throw new AiScriptSyntaxError(`No such operator: ${op}.`);
 		}
 		return info;
 	});
diff --git a/src/parser/plugins/set-attribute.ts b/src/parser/plugins/set-attribute.ts
index bc20bfc..a19e754 100644
--- a/src/parser/plugins/set-attribute.ts
+++ b/src/parser/plugins/set-attribute.ts
@@ -1,4 +1,4 @@
-import { SyntaxError } from '../../error.js';
+import { AiScriptSyntaxError } from '../../error.js';
 import type * as Cst from '../node.js';
 
 export function setAttribute(node: Cst.Expression[]): Cst.Expression[]
@@ -25,7 +25,7 @@ export function setAttribute(nodes: Cst.Node[]): Cst.Node[] {
 			result.push(node);
 		} else {
 			if (stockedAttrs.length > 0) {
-				throw new SyntaxError('invalid attribute.');
+				throw new AiScriptSyntaxError('invalid attribute.');
 			}
 			switch (node.type) {
 				case 'fn': {
@@ -41,7 +41,7 @@ export function setAttribute(nodes: Cst.Node[]): Cst.Node[] {
 		}
 	}
 	if (stockedAttrs.length > 0) {
-		throw new SyntaxError('invalid attribute.');
+		throw new AiScriptSyntaxError('invalid attribute.');
 	}
 
 	return result;
diff --git a/src/parser/plugins/validate-keyword.ts b/src/parser/plugins/validate-keyword.ts
index 61c7815..3e9af58 100644
--- a/src/parser/plugins/validate-keyword.ts
+++ b/src/parser/plugins/validate-keyword.ts
@@ -1,4 +1,4 @@
-import { SyntaxError } from '../../error.js';
+import { AiScriptSyntaxError } from '../../error.js';
 import { visitNode } from '../visit.js';
 import type * as Cst from '../node.js';
 
@@ -43,7 +43,7 @@ const reservedWord = [
 ];
 
 function throwReservedWordError(name: string): void {
-	throw new SyntaxError(`Reserved word "${name}" cannot be used as variable name.`);
+	throw new AiScriptSyntaxError(`Reserved word "${name}" cannot be used as variable name.`);
 }
 
 function validateNode(node: Cst.Node): Cst.Node {
diff --git a/src/type.ts b/src/type.ts
index 42ba6da..5e93acf 100644
--- a/src/type.ts
+++ b/src/type.ts
@@ -1,4 +1,4 @@
-import { SyntaxError } from './error.js';
+import { AiScriptSyntaxError } from './error.js';
 import type * as Ast from './node.js';
 
 // Type (Semantic analyzed)
@@ -151,7 +151,7 @@ export function getTypeBySource(typeSource: Ast.TypeSource): Type {
 				return T_GENERIC(typeSource.name, [innerType]);
 			}
 		}
-		throw new SyntaxError(`Unknown type: '${getTypeNameBySource(typeSource)}'`);
+		throw new AiScriptSyntaxError(`Unknown type: '${getTypeNameBySource(typeSource)}'`);
 	} else {
 		const argTypes = typeSource.args.map(arg => getTypeBySource(arg));
 		return T_FN(argTypes, getTypeBySource(typeSource.result));


Test patch:
diff --git a/test/index.ts b/test/index.ts
index cd7db13..6e2d3dd 100644
--- a/test/index.ts
+++ b/test/index.ts
@@ -6,7 +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 { RuntimeError } from '../src/error';
+let { AiScriptRuntimeError, AiScriptIndexOutOfRangeError } = errors;
 
 const exe = (program: string): Promise<any> => new Promise((ok, err) => {
 	const aiscript = new Interpreter({}, {
@@ -126,7 +126,7 @@ describe('ops', () => {
 		try {
 			await exe('<: (true && null)');
 		} catch (e) {
-			if (!(e instanceof RuntimeError)) assert.fail();
+			assert.ok(e instanceof AiScriptRuntimeError);
 			return;
 		}
 
@@ -174,7 +174,7 @@ describe('ops', () => {
 		try {
 			await exe('<: (false || null)');
 		} catch (e) {
-			if (!(e instanceof RuntimeError)) assert.fail();
+			assert.ok(e instanceof AiScriptRuntimeError);
 			return;
 		}
 
@@ -911,7 +911,7 @@ describe('Array', () => {
 					<: null
 				`)
 		} catch (e) {
-			eq(e instanceof errors.IndexOutOfRangeError, false);
+			eq(e instanceof AiScriptIndexOutOfRangeError, false);
 		}
 
 		try {
@@ -923,7 +923,7 @@ describe('Array', () => {
 					<: null
 				`)
 		} catch (e) {
-			eq(e instanceof errors.IndexOutOfRangeError, true);
+			eq(e instanceof AiScriptIndexOutOfRangeError, true);
 		}
 	});
 
@@ -933,7 +933,7 @@ describe('Array', () => {
 			<: [42][1]
 			`);
 		} catch (e) {
-			assert.equal(e instanceof errors.IndexOutOfRangeError, true);
+			assert.equal(e instanceof AiScriptIndexOutOfRangeError, true);
 			return;
 		}
 		assert.fail();
@@ -1189,7 +1189,7 @@ describe('Function call', () => {
 			<: Core:eq(1)
 			`);
 		} catch (e) {
-			assert.equal(e instanceof errors.RuntimeError, true);
+			assert.ok(e instanceof AiScriptRuntimeError);
 			return;
 		}
 		assert.fail();
@@ -2292,7 +2292,7 @@ describe('Variable declaration', () => {
 			hoge = 4
 		`).then(() => undefined).catch(err => err);
 
-		assert.ok(err instanceof RuntimeError);
+		assert.ok(err instanceof AiScriptRuntimeError);
 	});
 })
 
@@ -2878,7 +2878,7 @@ describe('Security', () => {
 			`);
 			assert.fail();
 		} catch (e) {
-			assert.equal(e instanceof errors.RuntimeError, true);
+			assert.ok(e instanceof AiScriptRuntimeError);
 		}
 
 		try {
@@ -2887,7 +2887,7 @@ describe('Security', () => {
 			`);
 			assert.fail();
 		} catch (e) {
-			assert.equal(e instanceof errors.RuntimeError, true);
+			assert.ok(e instanceof AiScriptRuntimeError);
 		}
 
 		try {
@@ -2896,7 +2896,7 @@ describe('Security', () => {
 			`);
 			assert.fail();
 		} catch (e) {
-			assert.equal(e instanceof errors.RuntimeError, true);
+			assert.ok(e instanceof AiScriptRuntimeError);
 		}
 	});
 
@@ -2930,7 +2930,7 @@ describe('Security', () => {
 			`);
 			assert.fail();
 		} catch (e) {
-			assert.equal(e instanceof errors.RuntimeError, true);
+			assert.ok(e instanceof AiScriptRuntimeError);
 		}
 
 		try {
@@ -2939,7 +2939,7 @@ describe('Security', () => {
 			`);
 			assert.fail();
 		} catch (e) {
-			assert.equal(e instanceof errors.RuntimeError, true);
+			assert.ok(e instanceof AiScriptRuntimeError);
 		}
 
 		try {
@@ -2948,7 +2948,7 @@ describe('Security', () => {
 			`);
 			assert.fail();
 		} catch (e) {
-			assert.equal(e instanceof errors.RuntimeError, true);
+			assert.equal(e instanceof AiScriptRuntimeError, true);
 		}
 	});
 });