Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Add AST nodes of infix operations #741

Merged
merged 4 commits into from
Aug 3, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
110 changes: 109 additions & 1 deletion etc/aiscript.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@

// Warning: (ae-forgotten-export) The symbol "NodeBase" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
type Add = NodeBase & {
type: 'add';
left: Expression;
right: Expression;
operatorLoc: Loc;
};

// @public (undocumented)
type AddAssign = NodeBase & {
type: 'addAssign';
Expand Down Expand Up @@ -138,6 +146,18 @@ declare namespace Ast {
Assign,
Expression,
Not,
Pow,
Mul,
Div,
Rem,
Add,
Sub,
Lt,
Lteq,
Gt,
Gteq,
Eq,
Neq,
And,
Or,
If,
Expand Down Expand Up @@ -226,6 +246,14 @@ type Definition = NodeBase & {
attr: Attribute[];
};

// @public (undocumented)
type Div = NodeBase & {
type: 'div';
left: Expression;
right: Expression;
operatorLoc: Loc;
};

// @public (undocumented)
type Each = NodeBase & {
type: 'each';
Expand All @@ -234,6 +262,14 @@ type Each = NodeBase & {
for: Statement | Expression;
};

// @public (undocumented)
type Eq = NodeBase & {
type: 'eq';
left: Expression;
right: Expression;
operatorLoc: Loc;
};

// @public (undocumented)
function eq(a: Value, b: Value): boolean;

Expand Down Expand Up @@ -264,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 | And | Or | Identifier | Call | Index | Prop;
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;

// @public (undocumented)
const FALSE: {
Expand Down Expand Up @@ -311,6 +347,22 @@ type For = NodeBase & {
// @public (undocumented)
function getLangVersion(input: string): string | null;

// @public (undocumented)
type Gt = NodeBase & {
type: 'gt';
left: Expression;
right: Expression;
operatorLoc: Loc;
};

// @public (undocumented)
type Gteq = NodeBase & {
type: 'gteq';
left: Expression;
right: Expression;
operatorLoc: Loc;
};

// @public (undocumented)
type Identifier = NodeBase & {
type: 'identifier';
Expand Down Expand Up @@ -403,6 +455,22 @@ type Loop = NodeBase & {
statements: (Statement | Expression)[];
};

// @public (undocumented)
type Lt = NodeBase & {
type: 'lt';
left: Expression;
right: Expression;
operatorLoc: Loc;
};

// @public (undocumented)
type Lteq = NodeBase & {
type: 'lteq';
left: Expression;
right: Expression;
operatorLoc: Loc;
};

// @public (undocumented)
type Match = NodeBase & {
type: 'match';
Expand All @@ -421,6 +489,14 @@ type Meta = NodeBase & {
value: Expression;
};

// @public (undocumented)
type Mul = NodeBase & {
type: 'mul';
left: Expression;
right: Expression;
operatorLoc: Loc;
};

// @public (undocumented)
type NamedTypeSource = NodeBase & {
type: 'namedTypeSource';
Expand All @@ -435,6 +511,14 @@ type Namespace = NodeBase & {
members: (Definition | Namespace)[];
};

// @public (undocumented)
type Neq = NodeBase & {
type: 'neq';
left: Expression;
right: Expression;
operatorLoc: Loc;
};

// @public (undocumented)
type Node_2 = Namespace | Meta | Statement | Expression | TypeSource | Attribute;

Expand Down Expand Up @@ -510,13 +594,29 @@ type Pos = {
column: number;
};

// @public (undocumented)
type Pow = NodeBase & {
type: 'pow';
left: Expression;
right: Expression;
operatorLoc: Loc;
};

// @public (undocumented)
type Prop = NodeBase & {
type: 'prop';
target: Expression;
name: string;
};

// @public (undocumented)
type Rem = NodeBase & {
type: 'rem';
left: Expression;
right: Expression;
operatorLoc: Loc;
};

// @public (undocumented)
function reprValue(value: Value, literalLike?: boolean, processedObjects?: Set<object>): string;

Expand Down Expand Up @@ -566,6 +666,14 @@ type Str = NodeBase & {
value: string;
};

// @public (undocumented)
type Sub = NodeBase & {
type: 'sub';
left: Expression;
right: Expression;
operatorLoc: Loc;
};

// @public (undocumented)
type SubAssign = NodeBase & {
type: 'subAssign';
Expand Down
96 changes: 96 additions & 0 deletions src/interpreter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
in?(q: string): Promise<string>;
out?(value: Value): void;
err?(e: AiScriptError): void;
log?(type: string, params: Record<string, any>): void;

Check warning on line 32 in src/interpreter/index.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
maxStep?: number;
abortOnError?: boolean;
} = {},
Expand Down Expand Up @@ -105,10 +105,10 @@
}

@autobind
public static collectMetadata(script?: Ast.Node[]): Map<any, any> | undefined {

Check warning on line 108 in src/interpreter/index.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

Check warning on line 108 in src/interpreter/index.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
if (script == null || script.length === 0) return;

function nodeToJs(node: Ast.Node): any {

Check warning on line 111 in src/interpreter/index.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
switch (node.type) {
case 'arr': return node.value.map(item => nodeToJs(item));
case 'bool': return node.value;
Expand Down Expand Up @@ -279,7 +279,7 @@
if (cond.value) {
return this._eval(node.then, scope);
} else {
if (node.elseif && node.elseif.length > 0) {

Check warning on line 282 in src/interpreter/index.ts

View workflow job for this annotation

GitHub Actions / lint

Unnecessary conditional, value is always truthy
for (const elseif of node.elseif) {
const cond = await this._eval(elseif.cond, scope);
assertBoolean(cond);
Expand Down Expand Up @@ -313,7 +313,7 @@

case 'loop': {
// eslint-disable-next-line no-constant-condition
while (true) {

Check warning on line 316 in src/interpreter/index.ts

View workflow job for this annotation

GitHub Actions / lint

Unnecessary conditional, value is always truthy
const v = await this._run(node.statements, scope.createChildScope());
if (v.type === 'break') {
break;
Expand Down Expand Up @@ -546,6 +546,102 @@
return NULL; // nop
}

case 'pow': {
const callee = scope.get('Core:pow');
assertFunction(callee);
const left = await this._eval(node.left, scope);
const right = await this._eval(node.right, scope);
return this._fn(callee, [left, right]);
}

case 'mul': {
const callee = scope.get('Core:mul');
assertFunction(callee);
const left = await this._eval(node.left, scope);
const right = await this._eval(node.right, scope);
return this._fn(callee, [left, right]);
}

case 'div': {
const callee = scope.get('Core:div');
assertFunction(callee);
const left = await this._eval(node.left, scope);
const right = await this._eval(node.right, scope);
return this._fn(callee, [left, right]);
}

case 'rem': {
const callee = scope.get('Core:mod');
assertFunction(callee);
const left = await this._eval(node.left, scope);
const right = await this._eval(node.right, scope);
return this._fn(callee, [left, right]);
}

case 'add': {
const callee = scope.get('Core:add');
assertFunction(callee);
const left = await this._eval(node.left, scope);
const right = await this._eval(node.right, scope);
return this._fn(callee, [left, right]);
}

case 'sub': {
const callee = scope.get('Core:sub');
assertFunction(callee);
const left = await this._eval(node.left, scope);
const right = await this._eval(node.right, scope);
return this._fn(callee, [left, right]);
}

case 'lt': {
const callee = scope.get('Core:lt');
assertFunction(callee);
const left = await this._eval(node.left, scope);
const right = await this._eval(node.right, scope);
return this._fn(callee, [left, right]);
}

case 'lteq': {
const callee = scope.get('Core:lteq');
assertFunction(callee);
const left = await this._eval(node.left, scope);
const right = await this._eval(node.right, scope);
return this._fn(callee, [left, right]);
}

case 'gt': {
const callee = scope.get('Core:gt');
assertFunction(callee);
const left = await this._eval(node.left, scope);
const right = await this._eval(node.right, scope);
return this._fn(callee, [left, right]);
}

case 'gteq': {
const callee = scope.get('Core:gteq');
assertFunction(callee);
const left = await this._eval(node.left, scope);
const right = await this._eval(node.right, scope);
return this._fn(callee, [left, right]);
}

case 'eq': {
const callee = scope.get('Core:eq');
assertFunction(callee);
const left = await this._eval(node.left, scope);
const right = await this._eval(node.right, scope);
return this._fn(callee, [left, right]);
}

case 'neq': {
const callee = scope.get('Core:neq');
assertFunction(callee);
const left = await this._eval(node.left, scope);
const right = await this._eval(node.right, scope);
return this._fn(callee, [left, right]);
}

case 'and': {
const leftValue = await this._eval(node.left, scope);
assertBoolean(leftValue);
Expand Down Expand Up @@ -650,12 +746,12 @@
} else if (dest.type === 'arr') {
assertArray(value);
await Promise.all(dest.value.map(
(item, index) => this.assign(scope, item, value.value[index] ?? NULL)

Check warning on line 749 in src/interpreter/index.ts

View workflow job for this annotation

GitHub Actions / lint

Missing trailing comma
));
} else if (dest.type === 'obj') {
assertObject(value);
await Promise.all([...dest.value].map(
([key, item]) => this.assign(scope, item, value.value.get(key) ?? NULL)

Check warning on line 754 in src/interpreter/index.ts

View workflow job for this annotation

GitHub Actions / lint

Missing trailing comma
));
} else {
throw new AiScriptRuntimeError('The left-hand side of an assignment expression must be a variable or a property/index access.');
Expand Down
3 changes: 1 addition & 2 deletions src/interpreter/lib/std.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
import { AiScriptRuntimeError, AiScriptUserError } from '../../error.js';
import { AISCRIPT_VERSION } from '../../constants.js';
import { textDecoder } from '../../const.js';
import type { Value } from '../value.js';

export const std: Record<string, Value> = {
export const std = {
marihachi marked this conversation as resolved.
Show resolved Hide resolved
'help': STR('SEE: https://github.com/syuilo/aiscript/blob/master/docs/get-started.md'),

//#region Core
Expand Down Expand Up @@ -160,7 +159,7 @@
assertString(json);
try {
return jsToVal(JSON.parse(json.value));
} catch (e) {

Check warning on line 162 in src/interpreter/lib/std.ts

View workflow job for this annotation

GitHub Actions / lint

'e' is defined but never used. Allowed unused caught errors must match /^_/u
return ERROR('not_json');
}
}),
Expand Down
Loading
Loading