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

連想配列の追加(再) #738

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
12 changes: 9 additions & 3 deletions console.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const getInterpreter = () => new Interpreter({}, {
},
err(e) {
console.log(chalk.red(`${e}`));
interpreter = getInterpreter();
},
log(type, params) {
switch (type) {
Expand All @@ -36,17 +37,22 @@ const getInterpreter = () => new Interpreter({}, {
}
});

let interpreter;
let interpreter = getInterpreter();
async function main(){
let a = await i.question('> ');
interpreter?.abort();
if (a === 'exit') return false;
if (a === 'reset') {
interpreter.abort();
interpreter = getInterpreter();
return true;
}
try {
let ast = Parser.parse(a);
interpreter = getInterpreter();
await interpreter.exec(ast);
} catch(e) {
console.log(chalk.red(`${e}`));
interpreter.abort();
interpreter = getInterpreter();
}
return true;
};
Expand Down
42 changes: 41 additions & 1 deletion docs/get-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ this is a comment
<tr><td>真理値</td><td><code>bool</code></td><td><code>true</code>/<code>false</code></td></tr>
<tr><td>配列</td><td><code>arr</code></td><td><code>["ai" "chan" "cute"]</code></td></tr>
<tr><td>オブジェクト</td><td><code>obj</code></td><td><code>{ foo: "bar"; a: 42; }</code></td></tr>
<tr><td>連想配列</td><td><code>dic</code></td><td><code>dic { [true]: "apple"; [[1, 2]]: 42; }</code></td></tr>
<tr><td>null</td><td><code>null</code></td><td><code>null</code></td></tr>
<tr><td>関数</td><td><code>fn</code></td><td><code>@(x) { x }</code></td></tr>
<tr><td>エラー</td><td><code>error</code></td><td><code>(TODO)</code></td></tr>
Expand Down Expand Up @@ -84,7 +85,7 @@ print(message)
```

## 配列
`[]`の中に式をスペースで区切って列挙します
`[]`の中に式をコンマ(または改行)で区切って列挙します
```
["ai", "chan", "kawaii"]
```
Expand Down Expand Up @@ -121,6 +122,45 @@ let obj = {foo: "bar", answer: 42}
<: obj["answer"] // 42
```

## 連想配列
オブジェクトと似た文法ですが、`{`の前にキーワード`dic`を置く必要があります。
また、キーにはプロパティ名の代わりに任意の式を利用します。
そして、全てのキーを`[]`で囲う必要があります。
アクセスの方法は`yourdic[<expr>]`です。(ドット記法は使えません)

AiScriptにおいてオブジェクトは文字列のみをキーとしますが、連想配列は全ての値をキーとします。
```js
var mydic = dic {
[null]: 42
[true]: 'foo'
[57]: false
['bar']: null
}
<: mydic['bar'] // null
mydic['bar'] = 12
<: mydic['bar'] // 12
```

キーを配列、オブジェクト、または連想配列にした場合はdeep-equalで検索が行われます。
```js
var mydic = dic {
[[1, 2, 3]]: Math:Infinity
}
<: mydic[[1, 2, 3]] // Infinity
mydic[[1, 2, 3]] = -0.083
<: mydic[[1, 2, 3]] // -0.083
```
関数は参照比較です。
```js
let key = @(){}
var mydic = dic {
[@(){}]: 1
[key]: 2
}
<: mydic[@(){}] // null (not found)
<: mydic[key] // 2
```

## 演算
演算は、
```
Expand Down
4 changes: 2 additions & 2 deletions docs/keywords.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ let match=null // エラー
## 一覧
以下の単語が予約語として登録されています。
### 使用中の語
`null`, `true`, `false`, `each`, `for`, `loop`, `break`, `continue`, `match`, `case`, `default`, `if`, `elif`, `else`, `return`, `eval`, `var`, `let`, `exists`
`null`, `true`, `false`, `each`, `for`, `loop`, `break`, `continue`, `match`, `case`, `default`, `if`, `elif`, `else`, `return`, `eval`, `var`, `let`, `exists`, `dic`

### 使用予定の語
`as`, `async`, `attr`, `attribute`, `await`, `catch`, `class`, `component`, `constructor`, `dictionary`, `do`, `enum`, `export`, `finally`, `fn`, `hash`, `in`, `interface`, `out`, `private`, `public`, `ref`, `static`, `struct`, `table`, `this`, `throw`, `trait`, `try`, `undefined`, `use`, `using`, `when`, `while`, `yield`, `import`, `is`, `meta`, `module`, `namespace`, `new`
`as`, `async`, `attr`, `attribute`, `await`, `catch`, `class`, `component`, `constructor`, `do`, `enum`, `export`, `finally`, `fn`, `hash`, `in`, `interface`, `out`, `private`, `public`, `ref`, `static`, `struct`, `table`, `this`, `throw`, `trait`, `try`, `undefined`, `use`, `using`, `when`, `while`, `yield`, `import`, `is`, `meta`, `module`, `namespace`, `new`
13 changes: 13 additions & 0 deletions docs/literals.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,19 @@ Previous statement is { !true }.`
{a: 12; b: 'hoge'} // Syntax Error
```

### 連想配列
```js
dic {} // 空の連想配列
dic {
[null]: 'foo'
[1]: true
['bar']: [1, 2, 3]
[[4, 5, 6]]: { a: 1, b: 2 }
[dic { [{}]: 42 }]: 57
}
dic{['ai']:'chan',['kawa']:'ii'} // ワンライナー
```

### 関数
関数のリテラルは「無名関数」と呼ばれており、[関数の宣言](./syntax.md#%E9%96%A2%E6%95%B0)とよく似た形をしていますが、関数名がありません。(そして、リテラルなので当然ながら、文ではなく式です)
```js
Expand Down
63 changes: 59 additions & 4 deletions etc/aiscript.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ function assertObject(val: Value | null | undefined): asserts val is VObj;
// @public (undocumented)
function assertString(val: Value | null | undefined): asserts val is VStr;

// @public (undocumented)
function assertValue<TLabel extends Value['type']>(val: Value | null | undefined, label: TLabel): asserts val is Value & {
type: TLabel;
};

// @public (undocumented)
type Assign = NodeBase & {
type: 'assign';
Expand Down Expand Up @@ -170,6 +175,7 @@ declare namespace Ast {
Null,
Obj,
Arr,
Dic,
Identifier,
Call,
Index,
Expand Down Expand Up @@ -244,6 +250,39 @@ type Definition = NodeBase & {
attr: Attribute[];
};

// @public (undocumented)
const DIC: {
fromNode: (dic: DicNode) => VDic;
fromEntries: (kvs?: [Value, Value][] | undefined) => VDic;
};

// @public (undocumented)
type Dic = NodeBase & {
type: 'dic';
value: [Expression, Expression][];
};

// @public (undocumented)
class DicNode {
constructor(kvs?: [Value, Value][]);
// (undocumented)
get(key: Value): Value;
// Warning: (ae-forgotten-export) The symbol "SeriExpToken" needs to be exported by the entry point index.d.ts
//
// (undocumented)
getRaw(keyGen: Generator<SeriExpToken, void, undefined>): Value | undefined;
// (undocumented)
has(key: Value): boolean;
// (undocumented)
kvs(): Generator<[Value, Value], void, undefined>;
// (undocumented)
serializedKvs(keyPrefix?: SeriExpToken[]): Generator<[SeriExpToken[], Value], void, undefined>;
// (undocumented)
set(key: Value, val: Value): void;
// (undocumented)
setRaw(keyGen: Generator<SeriExpToken, void, undefined>, val: Value): void;
}

// @public (undocumented)
type Div = NodeBase & {
type: 'div';
Expand Down Expand Up @@ -296,7 +335,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 | Dic | 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 @@ -434,6 +473,11 @@ function isStatement(x: Node_2): x is Statement;
// @public (undocumented)
function isString(val: Value): val is VStr;

// @public (undocumented)
function isValue<TLabel extends Value['type']>(val: Value, label: TLabel): val is Value & {
type: TLabel;
};

// @public (undocumented)
function jsToVal(val: unknown): Value;

Expand Down Expand Up @@ -699,12 +743,14 @@ declare namespace utils {
assertNumber,
assertObject,
assertArray,
assertValue,
isBoolean,
isFunction,
isString,
isNumber,
isObject,
isArray,
isValue,
eq,
valToString,
valToJs,
Expand All @@ -723,16 +769,18 @@ function valToJs(val: Value): JsValue;
function valToString(val: Value, simple?: boolean): string;

// @public (undocumented)
type Value = (VNull | VBool | VNum | VStr | VArr | VObj | VFn | VReturn | VBreak | VContinue | VError) & Attr_2;
type Value = (VNull | VBool | VNum | VStr | VArr | VObj | VDic | VFn | VReturn | VBreak | VContinue | VError) & Attr_2;

declare namespace values {
export {
DicNode,
VNull,
VBool,
VNum,
VStr,
VArr,
VObj,
VDic,
VFn,
VUserFn,
VFnArg,
Expand All @@ -751,6 +799,7 @@ declare namespace values {
BOOL,
OBJ,
ARR,
DIC,
FN,
FN_NATIVE,
RETURN,
Expand Down Expand Up @@ -786,6 +835,12 @@ type VContinue = {
value: null;
};

// @public (undocumented)
type VDic = {
type: 'dic';
value: DicNode;
};

// @public (undocumented)
type VError = {
type: 'error';
Expand Down Expand Up @@ -854,8 +909,8 @@ type VUserFn = VFnBase & {

// Warnings were encountered during analysis:
//
// src/interpreter/index.ts:39:4 - (ae-forgotten-export) The symbol "LogObject" needs to be exported by the entry point index.d.ts
// src/interpreter/value.ts:46:2 - (ae-forgotten-export) The symbol "Type" needs to be exported by the entry point index.d.ts
// src/interpreter/index.ts:40:4 - (ae-forgotten-export) The symbol "LogObject" needs to be exported by the entry point index.d.ts
// src/interpreter/value.ts:53:2 - (ae-forgotten-export) The symbol "Type" needs to be exported by the entry point index.d.ts

// (No @packageDocumentation comment for this package)

Expand Down
58 changes: 58 additions & 0 deletions src/interpreter/dic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* このコードではJavaScriptの反復処理プロトコル及びジェネレーター関数を利用しています。
* 詳細は https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/function*
* を参照して下さい。
*/

import { serialize, deserialize } from './serial-expression.js';
import { NULL } from './value.js';
import type { SeriExpToken } from './serial-expression.js';
import type { Value } from './value.js';

// TODO: 同時書き込みが発生した場合の衝突の解決
export class DicNode {
private data?: Value;
private children = new Map<SeriExpToken, DicNode>();

constructor(kvs?: [Value, Value][]) {
if (!kvs) return;
for (const [key, val] of kvs) this.set(key, val);
}

get(key: Value): Value {
return this.getRaw(serialize(key)) ?? NULL; // キーが見つからなかった場合の挙動を変えやすいように設計しています
}
has(key: Value): boolean {
return this.getRaw(serialize(key)) ? true : false;
}
getRaw(keyGen: Generator<SeriExpToken, void, undefined>): Value | undefined {
const { value: key, done } = keyGen.next();
if (done) return this.data;
else return this.children.get(key)?.getRaw(keyGen);
}

set(key: Value, val: Value): void {
this.setRaw(serialize(key), val);
}
setRaw(keyGen: Generator<SeriExpToken, void, undefined>, val: Value): void {
const { value: key, done } = keyGen.next();
if (done) this.data = val;
else {
if (!this.children.has(key)) this.children.set(key, new DicNode());
this.children.get(key)!.setRaw(keyGen, val);
}
}

*kvs(): Generator<[Value, Value], void, undefined> {
for (const [seriExp, val] of this.serializedKvs()) {
yield [deserialize(seriExp), val];
}
}
*serializedKvs(keyPrefix?: SeriExpToken[]): Generator<[SeriExpToken[], Value], void, undefined> {
const kp = keyPrefix ?? [];
if (this.data) yield [kp, this.data];
for (const [key, childNode] of this.children) {
yield* childNode.serializedKvs([...kp, key]);
}
}
}
Loading
Loading