Skip to content

feature: ネストされた名前空間下の変数を参照できるように (#629) #630

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

Merged
merged 2 commits into from
Apr 22, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- `arr.fill`, `arr.repeat`, `Arr:create`を追加
- JavaScriptのように分割代入ができるように(現段階では機能は最小限)
- スコープおよび名前が同一である変数が宣言された際のエラーメッセージを修正
- ネストされた名前空間下の変数を参照できるように

# 0.17.0
- `package.json`を修正
Expand Down
6 changes: 5 additions & 1 deletion etc/aiscript.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -850,9 +850,11 @@ type Return_2 = NodeBase_2 & {

// @public (undocumented)
export class Scope {
constructor(layerdStates?: Scope['layerdStates'], parent?: Scope, name?: Scope['name']);
constructor(layerdStates?: Scope['layerdStates'], parent?: Scope, name?: Scope['name'], nsName?: string);
add(name: string, variable: Variable): void;
assign(name: string, val: Value): void;
// (undocumented)
createChildNamespaceScope(nsName: string, states?: Map<string, Variable>, name?: Scope['name']): Scope;
// Warning: (ae-forgotten-export) The symbol "Variable" needs to be exported by the entry point index.d.ts
//
// (undocumented)
Expand All @@ -863,6 +865,8 @@ export class Scope {
// (undocumented)
name: string;
// (undocumented)
nsName?: string;
// (undocumented)
opts: {
log?(type: string, params: Record<string, any>): void;
onUpdated?(name: string, value: Value): void;
Expand Down
17 changes: 9 additions & 8 deletions src/interpreter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,11 @@ export class Interpreter {
}

@autobind
private async collectNs(script: Ast.Node[]): Promise<void> {
private async collectNs(script: Ast.Node[], scope = this.scope): Promise<void> {
for (const node of script) {
switch (node.type) {
case 'ns': {
await this.collectNsMember(node);
await this.collectNsMember(node, scope);
break;
}

Expand All @@ -191,8 +191,10 @@ export class Interpreter {
}

@autobind
private async collectNsMember(ns: Ast.Namespace): Promise<void> {
const scope = this.scope.createChildScope();
private async collectNsMember(ns: Ast.Namespace, scope = this.scope): Promise<void> {
const nsScope = scope.createChildNamespaceScope(ns.name);

await this.collectNs(ns.members, nsScope);

for (const node of ns.members) {
switch (node.type) {
Expand All @@ -203,16 +205,15 @@ export class Interpreter {

const variable: Variable = {
isMutable: node.mut,
value: await this._eval(node.expr, scope),
value: await this._eval(node.expr, nsScope),
};
scope.add(node.name, variable);
nsScope.add(node.name, variable);

this.scope.add(ns.name + ':' + node.name, variable);
break;
}

case 'ns': {
break; // TODO
break; // nop
}

default: {
Expand Down
13 changes: 11 additions & 2 deletions src/interpreter/scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ export class Scope {
log?(type: string, params: Record<string, any>): void;
onUpdated?(name: string, value: Value): void;
} = {};
public nsName?: string;

constructor(layerdStates: Scope['layerdStates'] = [], parent?: Scope, name?: Scope['name']) {
constructor(layerdStates: Scope['layerdStates'] = [], parent?: Scope, name?: Scope['name'], nsName?: string) {
this.layerdStates = layerdStates;
this.parent = parent;
this.name = name || (layerdStates.length === 1 ? '<root>' : '<anonymous>');
this.nsName = nsName;
}

@autobind
Expand All @@ -42,6 +44,12 @@ export class Scope {
return new Scope(layer, this, name);
}

@autobind
public createChildNamespaceScope(nsName: string, states: Map<string, Variable> = new Map(), name?: Scope['name']): Scope {
const layer = [states, ...this.layerdStates];
return new Scope(layer, this, name, nsName);
}

/**
* 指定した名前の変数を取得します
* @param name - 変数名
Expand Down Expand Up @@ -90,7 +98,7 @@ export class Scope {
}

/**
* 指定した名前の変数を現在のスコープに追加します
* 指定した名前の変数を現在のスコープに追加します。名前空間である場合は接頭辞を付して親のスコープにも追加します
* @param name - 変数名
* @param val - 初期値
*/
Expand All @@ -105,6 +113,7 @@ export class Scope {
}
states.set(name, variable);
if (this.parent == null) this.onUpdated(name, variable.value);
else if (this.nsName != null) this.parent.add(this.nsName + ':' + name, variable);
}

/**
Expand Down
27 changes: 27 additions & 0 deletions test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1871,6 +1871,33 @@ describe('namespace', () => {
}
assert.fail();
});

test.concurrent('nested', async () => {
const res = await exe(`
<: Foo:Bar:baz()

:: Foo {
:: Bar {
@baz() { "ai" }
}
}
`);
eq(res, STR('ai'));
});

test.concurrent('nested ref', async () => {
const res = await exe(`
<: Foo:baz

:: Foo {
let baz = Bar:ai
:: Bar {
let ai = "kawaii"
}
}
`);
eq(res, STR('kawaii'));
});
});

describe('literal', () => {
Expand Down