Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions etc/aiscript.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,18 @@ class AiScriptRuntimeError extends AiScriptError {

// @public
class AiScriptSyntaxError extends AiScriptError {
constructor(message: string, info?: any);
constructor(message: string, loc: Loc, info?: any);
// (undocumented)
loc: Loc;
// (undocumented)
name: string;
}

// @public
class AiScriptTypeError extends AiScriptError {
constructor(message: string, info?: any);
constructor(message: string, loc: Loc, info?: any);
// (undocumented)
loc: Loc;
// (undocumented)
name: string;
}
Expand Down
10 changes: 6 additions & 4 deletions src/error.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { Loc } from './node.js';

export abstract class AiScriptError extends Error {
// name is read by Error.prototype.toString
public name = 'AiScript';
Expand Down Expand Up @@ -30,17 +32,17 @@ export class NonAiScriptError extends AiScriptError {
*/
export class AiScriptSyntaxError extends AiScriptError {
public name = 'Syntax';
constructor(message: string, info?: any) {
super(message, info);
constructor(message: string, public loc: Loc, info?: any) {
super(`${message} (Line ${loc.line}, Column ${loc.column})`, info);
}
}
/**
* Type validation(parser/plugins/validate-type) errors.
*/
export class AiScriptTypeError extends AiScriptError {
public name = 'Type';
constructor(message: string, info?: any) {
super(message, info);
constructor(message: string, public loc: Loc, info?: any) {
super(`${message} (Line ${loc.line}, Column ${loc.column})`, info);
}
}

Expand Down
10 changes: 5 additions & 5 deletions src/parser/plugins/validate-keyword.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ const reservedWord = [
// 'out',
];

function throwReservedWordError(name: string): void {
throw new AiScriptSyntaxError(`Reserved word "${name}" cannot be used as variable name.`);
function throwReservedWordError(name: string, loc: Ast.Loc): void {
throw new AiScriptSyntaxError(`Reserved word "${name}" cannot be used as variable name.`, loc);
}

function validateNode(node: Ast.Node): Ast.Node {
Expand All @@ -53,20 +53,20 @@ function validateNode(node: Ast.Node): Ast.Node {
case 'ns':
case 'identifier': {
if (reservedWord.includes(node.name)) {
throwReservedWordError(node.name);
throwReservedWordError(node.name, node.loc);
}
break;
}
case 'meta': {
if (node.name != null && reservedWord.includes(node.name)) {
throwReservedWordError(node.name);
throwReservedWordError(node.name, node.loc);
}
break;
}
case 'fn': {
for (const arg of node.args) {
if (reservedWord.includes(arg.name)) {
throwReservedWordError(arg.name);
throwReservedWordError(arg.name, node.loc);
}
}
break;
Expand Down
18 changes: 9 additions & 9 deletions src/parser/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class Scanner implements ITokenStream {
*/
public expect(kind: TokenKind): void {
if (this.kind !== kind) {
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[this.kind]}`);
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[this.kind]}`, this.token.loc);
}
}

Expand Down Expand Up @@ -140,7 +140,7 @@ export class Scanner implements ITokenStream {
this.stream.next();
token = TOKEN(TokenKind.OpenSharpBracket, loc, { hasLeftSpacing });
} else {
throw new AiScriptSyntaxError('invalid character: "#"');
throw new AiScriptSyntaxError('invalid character: "#"', loc);
}
break;
}
Expand Down Expand Up @@ -327,7 +327,7 @@ export class Scanner implements ITokenStream {
token = wordToken;
break;
}
throw new AiScriptSyntaxError(`invalid character: "${this.stream.char}"`);
throw new AiScriptSyntaxError(`invalid character: "${this.stream.char}"`, loc);
}
break;
}
Expand Down Expand Up @@ -432,7 +432,7 @@ export class Scanner implements ITokenStream {
this.stream.next();
}
if (fractional.length === 0) {
throw new AiScriptSyntaxError('digit expected');
throw new AiScriptSyntaxError('digit expected', loc);
}
}
let value;
Expand All @@ -456,7 +456,7 @@ export class Scanner implements ITokenStream {
switch (state) {
case 'string': {
if (this.stream.eof) {
throw new AiScriptSyntaxError('unexpected EOF');
throw new AiScriptSyntaxError('unexpected EOF', loc);
}
if (this.stream.char === '\\') {
this.stream.next();
Expand All @@ -474,7 +474,7 @@ export class Scanner implements ITokenStream {
}
case 'escape': {
if (this.stream.eof) {
throw new AiScriptSyntaxError('unexpected EOF');
throw new AiScriptSyntaxError('unexpected EOF', loc);
}
value += this.stream.char;
this.stream.next();
Expand All @@ -501,7 +501,7 @@ export class Scanner implements ITokenStream {
case 'string': {
// テンプレートの終了が無いままEOFに達した
if (this.stream.eof) {
throw new AiScriptSyntaxError('unexpected EOF');
throw new AiScriptSyntaxError('unexpected EOF', loc);
}
// エスケープ
if (this.stream.char === '\\') {
Expand Down Expand Up @@ -537,7 +537,7 @@ export class Scanner implements ITokenStream {
case 'escape': {
// エスケープ対象の文字が無いままEOFに達した
if (this.stream.eof) {
throw new AiScriptSyntaxError('unexpected EOF');
throw new AiScriptSyntaxError('unexpected EOF', loc);
}
// 普通の文字として取り込み
buf += this.stream.char;
Expand All @@ -549,7 +549,7 @@ export class Scanner implements ITokenStream {
case 'expr': {
// 埋め込み式の終端記号が無いままEOFに達した
if (this.stream.eof) {
throw new AiScriptSyntaxError('unexpected EOF');
throw new AiScriptSyntaxError('unexpected EOF', loc);
}
// skip spasing
if (spaceChars.includes(this.stream.char)) {
Expand Down
2 changes: 1 addition & 1 deletion src/parser/streams/token-stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export class TokenStream implements ITokenStream {
*/
public expect(kind: TokenKind): void {
if (this.kind !== kind) {
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[this.kind]}`);
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[this.kind]}`, this.token.loc);
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/parser/syntaxes/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function parseParams(s: ITokenStream): { name: string, argType?: Ast.Node
if (s.kind === TokenKind.Comma) {
s.next();
} else if (!s.token.hasLeftSpacing) {
throw new AiScriptSyntaxError('separator expected');
throw new AiScriptSyntaxError('separator expected', s.token.loc);
}
}

Expand Down Expand Up @@ -61,7 +61,7 @@ export function parseBlock(s: ITokenStream): Ast.Node[] {
steps.push(parseStatement(s));

if ((s.kind as TokenKind) !== TokenKind.NewLine && (s.kind as TokenKind) !== TokenKind.CloseBrace) {
throw new AiScriptSyntaxError('Multiple statements cannot be placed on a single line.');
throw new AiScriptSyntaxError('Multiple statements cannot be placed on a single line.', s.token.loc);
}
while ((s.kind as TokenKind) === TokenKind.NewLine) {
s.next();
Expand Down Expand Up @@ -101,7 +101,7 @@ function parseFnType(s: ITokenStream): Ast.Node {
if (s.kind === TokenKind.Comma) {
s.next();
} else if (!s.token.hasLeftSpacing) {
throw new AiScriptSyntaxError('separator expected');
throw new AiScriptSyntaxError('separator expected', s.token.loc);
}
}
const type = parseType(s);
Expand Down
26 changes: 13 additions & 13 deletions src/parser/syntaxes/expressions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function parsePrefix(s: ITokenStream, minBp: number): Ast.Node {
if (expr.type === 'num') {
return NODE('num', { value: expr.value }, loc);
} else {
throw new AiScriptSyntaxError('currently, sign is only supported for number literal.');
throw new AiScriptSyntaxError('currently, sign is only supported for number literal.', loc);
}
// TODO: 将来的にサポートされる式を拡張
// return NODE('plus', { expr }, loc);
Expand All @@ -80,7 +80,7 @@ function parsePrefix(s: ITokenStream, minBp: number): Ast.Node {
if (expr.type === 'num') {
return NODE('num', { value: -1 * expr.value }, loc);
} else {
throw new AiScriptSyntaxError('currently, sign is only supported for number literal.');
throw new AiScriptSyntaxError('currently, sign is only supported for number literal.', loc);
}
// TODO: 将来的にサポートされる式を拡張
// return NODE('minus', { expr }, loc);
Expand All @@ -89,7 +89,7 @@ function parsePrefix(s: ITokenStream, minBp: number): Ast.Node {
return NODE('not', { expr }, loc);
}
default: {
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[op]}`);
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[op]}`, loc);
}
}
}
Expand Down Expand Up @@ -161,7 +161,7 @@ function parseInfix(s: ITokenStream, left: Ast.Node, minBp: number): Ast.Node {
return NODE('or', { left, right }, loc);
}
default: {
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[op]}`);
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[op]}`, loc);
}
}
}
Expand All @@ -186,7 +186,7 @@ function parsePostfix(s: ITokenStream, expr: Ast.Node): Ast.Node {
}, loc);
}
default: {
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[op]}`);
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[op]}`, loc);
}
}
}
Expand Down Expand Up @@ -231,13 +231,13 @@ function parseAtom(s: ITokenStream, isStatic: boolean): Ast.Node {
const exprStream = new TokenStream(element.children!);
const expr = parseExpr(exprStream, false);
if (exprStream.kind !== TokenKind.EOF) {
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[exprStream.token.kind]}`);
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[exprStream.token.kind]}`, exprStream.token.loc);
}
values.push(expr);
break;
}
default: {
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[element.kind]}`);
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[element.kind]}`, element.loc);
}
}
}
Expand Down Expand Up @@ -283,7 +283,7 @@ function parseAtom(s: ITokenStream, isStatic: boolean): Ast.Node {
return expr;
}
}
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[s.kind]}`);
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[s.kind]}`, loc);
}

/**
Expand All @@ -301,7 +301,7 @@ function parseCall(s: ITokenStream, target: Ast.Node): Ast.Node {
if (s.kind === TokenKind.Comma) {
s.next();
} else if (!s.token.hasLeftSpacing) {
throw new AiScriptSyntaxError('separator expected');
throw new AiScriptSyntaxError('separator expected', s.token.loc);
}
}

Expand Down Expand Up @@ -451,11 +451,11 @@ function parseReference(s: ITokenStream): Ast.Node {
if (segs.length > 0) {
if (s.kind === TokenKind.Colon) {
if (s.token.hasLeftSpacing) {
throw new AiScriptSyntaxError('Cannot use spaces in a reference.');
throw new AiScriptSyntaxError('Cannot use spaces in a reference.', s.token.loc);
}
s.next();
if (s.token.hasLeftSpacing) {
throw new AiScriptSyntaxError('Cannot use spaces in a reference.');
throw new AiScriptSyntaxError('Cannot use spaces in a reference.', s.token.loc);
}
} else {
break;
Expand Down Expand Up @@ -505,7 +505,7 @@ function parseObject(s: ITokenStream, isStatic: boolean): Ast.Node {
// noop
} else {
if (!s.token.hasLeftSpacing) {
throw new AiScriptSyntaxError('separator expected');
throw new AiScriptSyntaxError('separator expected', s.token.loc);
}
}

Expand Down Expand Up @@ -546,7 +546,7 @@ function parseArray(s: ITokenStream, isStatic: boolean): Ast.Node {
// noop
} else {
if (!s.token.hasLeftSpacing) {
throw new AiScriptSyntaxError('separator expected');
throw new AiScriptSyntaxError('separator expected', s.token.loc);
}
}

Expand Down
10 changes: 5 additions & 5 deletions src/parser/syntaxes/statements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export function parseDefStatement(s: ITokenStream): Ast.Node {
return parseFnDef(s);
}
default: {
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[s.kind]}`);
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[s.kind]}`, s.token.loc);
}
}
}
Expand Down Expand Up @@ -112,7 +112,7 @@ function parseVarDef(s: ITokenStream): Ast.Node {
break;
}
default: {
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[s.kind]}`);
throw new AiScriptSyntaxError(`unexpected token: ${TokenKind[s.kind]}`, s.token.loc);
}
}
s.next();
Expand Down Expand Up @@ -213,7 +213,7 @@ function parseEach(s: ITokenStream): Ast.Node {
if (s.kind === TokenKind.Comma) {
s.next();
} else if (!s.token.hasLeftSpacing) {
throw new AiScriptSyntaxError('separator expected');
throw new AiScriptSyntaxError('separator expected', s.token.loc);
}

const items = parseExpr(s, false);
Expand Down Expand Up @@ -263,7 +263,7 @@ function parseFor(s: ITokenStream): Ast.Node {
if ((s.kind as TokenKind) === TokenKind.Comma) {
s.next();
} else if (!s.token.hasLeftSpacing) {
throw new AiScriptSyntaxError('separator expected');
throw new AiScriptSyntaxError('separator expected', s.token.loc);
}

const to = parseExpr(s, false);
Expand Down Expand Up @@ -326,7 +326,7 @@ function parseStatementWithAttr(s: ITokenStream): Ast.Node {
const statement = parseStatement(s);

if (statement.type !== 'def') {
throw new AiScriptSyntaxError('invalid attribute.');
throw new AiScriptSyntaxError('invalid attribute.', statement.loc);
}
if (statement.attr != null) {
statement.attr.push(...attrs);
Expand Down
4 changes: 2 additions & 2 deletions src/parser/syntaxes/toplevel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function parseTopLevel(s: ITokenStream): Ast.Node[] {
}

if ((s.kind as TokenKind) !== TokenKind.NewLine && (s.kind as TokenKind) !== TokenKind.EOF) {
throw new AiScriptSyntaxError('Multiple statements cannot be placed on a single line.');
throw new AiScriptSyntaxError('Multiple statements cannot be placed on a single line.', s.token.loc);
}
while ((s.kind as TokenKind) === TokenKind.NewLine) {
s.next();
Expand Down Expand Up @@ -82,7 +82,7 @@ export function parseNamespace(s: ITokenStream): Ast.Node {
}

if ((s.kind as TokenKind) !== TokenKind.NewLine && (s.kind as TokenKind) !== TokenKind.CloseBrace) {
throw new AiScriptSyntaxError('Multiple statements cannot be placed on a single line.');
throw new AiScriptSyntaxError('Multiple statements cannot be placed on a single line.', s.token.loc);
}
while ((s.kind as TokenKind) === TokenKind.NewLine) {
s.next();
Expand Down
2 changes: 1 addition & 1 deletion src/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export function getTypeBySource(typeSource: Ast.TypeSource): Type {
return T_GENERIC(typeSource.name, [innerType]);
}
}
throw new AiScriptSyntaxError(`Unknown type: '${getTypeNameBySource(typeSource)}'`);
throw new AiScriptSyntaxError(`Unknown type: '${getTypeNameBySource(typeSource)}'`, typeSource.loc);
} else {
const argTypes = typeSource.args.map(arg => getTypeBySource(arg));
return T_FN(argTypes, getTypeBySource(typeSource.result));
Expand Down