From be9323414ed21afb1a8f78786207b5a909d10e74 Mon Sep 17 00:00:00 2001 From: takejohn Date: Wed, 13 Nov 2024 12:55:22 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=E5=BC=8F=E3=81=AB=E8=B2=A0=E5=8F=B7?= =?UTF-8?q?=E3=82=92=E4=BD=BF=E3=81=88=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- etc/aiscript.api.md | 16 +++++++++++++++- src/interpreter/index.ts | 25 +++++++++++++++++++++++++ src/node.ts | 12 ++++++++++++ src/parser/syntaxes/expressions.ts | 12 ++---------- src/parser/visit.ts | 10 +++++++++- test/syntax.ts | 20 ++++++++++++++++++++ 6 files changed, 83 insertions(+), 12 deletions(-) diff --git a/etc/aiscript.api.md b/etc/aiscript.api.md index 95be0bb1..da923ce9 100644 --- a/etc/aiscript.api.md +++ b/etc/aiscript.api.md @@ -150,6 +150,8 @@ declare namespace Ast { SubAssign, Assign, Expression, + Plus, + Minus, Not, Pow, Mul, @@ -298,7 +300,7 @@ type Exists = NodeBase & { function expectAny(val: Value | null | undefined): asserts val is Value; // @public (undocumented) -type Expression = If | Fn | Match | Block | Exists | Tmpl | Str | Num | Bool | Null | Obj | Arr | Not | Pow | Mul | Div | Rem | Add | Sub | Lt | Lteq | Gt | Gteq | Eq | Neq | And | Or | Identifier | Call | Index | Prop; +type Expression = If | Fn | Match | Block | Exists | Tmpl | Str | Num | Bool | Null | Obj | Arr | Plus | Minus | Not | Pow | Mul | Div | Rem | Add | Sub | Lt | Lteq | Gt | Gteq | Eq | Neq | And | Or | Identifier | Call | Index | Prop; // @public (undocumented) const FALSE: { @@ -502,6 +504,12 @@ type Meta = NodeBase & { value: Expression; }; +// @public (undocumented) +type Minus = NodeBase & { + type: 'minus'; + expr: Expression; +}; + // @public (undocumented) type Mul = NodeBase & { type: 'mul'; @@ -598,6 +606,12 @@ export type ParserPlugin = (nodes: Ast.Node[]) => Ast.Node[]; // @public (undocumented) export type PluginType = 'validate' | 'transform'; +// @public (undocumented) +type Plus = NodeBase & { + type: 'plus'; + expr: Expression; +}; + // @public type Pos = { line: number; diff --git a/src/interpreter/index.ts b/src/interpreter/index.ts index c314fd99..2d89264f 100644 --- a/src/interpreter/index.ts +++ b/src/interpreter/index.ts @@ -642,6 +642,24 @@ export class Interpreter { } } + case 'plus': { + const v = await this._eval(node.expr, scope, callStack); + if (isControl(v)) { + return v; + } + assertNumber(v); + return v; + } + + case 'minus': { + const v = await this._eval(node.expr, scope, callStack); + if (isControl(v)) { + return v; + } + assertNumber(v); + return NUM(-v.value); + } + case 'not': { const v = await this._eval(node.expr, scope, callStack); if (isControl(v)) { @@ -813,7 +831,14 @@ export class Interpreter { } } + case 'namedTypeSource': + case 'fnTypeSource': + case 'attr': { + throw new Error('invalid node type'); + } + default: { + node satisfies never; throw new Error('invalid node type'); } } diff --git a/src/node.ts b/src/node.ts index d8e94768..19e94e97 100644 --- a/src/node.ts +++ b/src/node.ts @@ -133,6 +133,8 @@ export type Expression = Null | Obj | Arr | + Plus | + Minus | Not | Pow | Mul | @@ -162,6 +164,16 @@ export function isExpression(x: Node): x is Expression { return expressionTypes.includes(x.type); } +export type Plus = NodeBase & { + type: 'plus'; // 正号 + expr: Expression; // 式 +}; + +export type Minus = NodeBase & { + type: 'minus'; // 負号 + expr: Expression; // 式 +}; + export type Not = NodeBase & { type: 'not'; // 否定 expr: Expression; // 式 diff --git a/src/parser/syntaxes/expressions.ts b/src/parser/syntaxes/expressions.ts index 80887680..ab22a95a 100644 --- a/src/parser/syntaxes/expressions.ts +++ b/src/parser/syntaxes/expressions.ts @@ -69,24 +69,16 @@ function parsePrefix(s: ITokenStream, minBp: number): Ast.Expression { switch (op) { case TokenKind.Plus: { - // 数値リテラル以外は非サポート if (expr.type === 'num') { return NODE('num', { value: expr.value }, startPos, endPos); - } else { - throw new AiScriptSyntaxError('currently, sign is only supported for number literal.', startPos); } - // TODO: 将来的にサポートされる式を拡張 - // return NODE('plus', { expr }, startPos, endPos); + return NODE('plus', { expr }, startPos, endPos); } case TokenKind.Minus: { - // 数値リテラル以外は非サポート if (expr.type === 'num') { return NODE('num', { value: -1 * expr.value }, startPos, endPos); - } else { - throw new AiScriptSyntaxError('currently, sign is only supported for number literal.', startPos); } - // TODO: 将来的にサポートされる式を拡張 - // return NODE('minus', { expr }, startPos, endPos); + return NODE('minus', { expr }, startPos, endPos); } case TokenKind.Not: { return NODE('not', { expr }, startPos, endPos); diff --git a/src/parser/visit.ts b/src/parser/visit.ts index 52bbf48f..36fcf3a0 100644 --- a/src/parser/visit.ts +++ b/src/parser/visit.ts @@ -49,8 +49,16 @@ function visitNodeInner(node: Ast.Node, fn: (node: Ast.Node, ancestors: Ast.Node result.dest = visitNodeInner(result.dest, fn, ancestors) as Ast.Assign['dest']; break; } + case 'plus': { + result.expr = visitNodeInner(result.expr, fn, ancestors) as Ast.Plus['expr']; + break; + } + case 'minus': { + result.expr = visitNodeInner(result.expr, fn, ancestors) as Ast.Minus['expr']; + break; + } case 'not': { - result.expr = visitNodeInner(result.expr, fn, ancestors) as Ast.Return['expr']; + result.expr = visitNodeInner(result.expr, fn, ancestors) as Ast.Not['expr']; break; } case 'if': { diff --git a/test/syntax.ts b/test/syntax.ts index 40ec4f64..e6b3b938 100644 --- a/test/syntax.ts +++ b/test/syntax.ts @@ -1314,6 +1314,26 @@ describe('operators', () => { }); +describe('plus', () => { + test.concurrent('Basic', async () => { + const res = await exe(` + let a = 1 + <: +a + `); + eq(res, NUM(1)); + }) +}) + +describe('minus', () => { + test.concurrent('Basic', async () => { + const res = await exe(` + let a = 1 + <: -a + `); + eq(res, NUM(-1)); + }) +}) + describe('not', () => { test.concurrent('Basic', async () => { const res = await exe(` From dc4230d3bd46b7c92d286b4e9f57819fe6088cf5 Mon Sep 17 00:00:00 2001 From: takejohn Date: Wed, 13 Nov 2024 15:44:30 +0900 Subject: [PATCH 2/2] CHANGELOG --- unreleased/unary-sign-operators.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 unreleased/unary-sign-operators.md diff --git a/unreleased/unary-sign-operators.md b/unreleased/unary-sign-operators.md new file mode 100644 index 00000000..e064b955 --- /dev/null +++ b/unreleased/unary-sign-operators.md @@ -0,0 +1 @@ +- 単項演算子の正号 `+`・負号 `-`が数値リテラル以外の式にも使用できるようになりました。