はじめに

KoumeはJSONで記述するプログラミング言語です。
Koumeは以下の特徴を持ちます。

  • 第1級の関数と無名関数

  • 第1級の継続

  • 末尾再帰の最適化

  • LISPライクなマクロ

  • メッセージパッシング機構

構文リファレンス

リテラル

JSONの数値、真偽値、nullはリテラルとなります。
文字列はリテラルにはなりません。クオートが必要になります。

変数参照

JSONの文字列は変数への参照になります。

関数の呼び出し

[ "add", 1, 2 ]
[ { "q": [1, 2, 3] }, 0 ]
[ { "q": { "obj1": 1 } }, { "q": "obj1" } ]
[ { "q": "abc" }, 0 ]

配列の先頭に関数名、その後に引数を指定することで関数を呼び出せます。
配列にインデックスを適用することで、そのインデックスの要素を取得できます。
オブジェクトにプロパティ名を適用することで、そのプロパティを取得することができます。
文字列にインデックスを適用することで、そのインデックスの文字(1文字の文字列)を取得することができます。

クオート

{
  "q": {
    "obj": 1
  }
}

オブジェクトを"q"で囲うことによりクオートします。

オブジェクトの組み立て

{
  "cons": {
    "obj1": [ "add", 1, 2 ],
    "obj2": 3
  }
}

オブジェクト名とオブジェクトの値の組を与えることでオブジェクトを組み立てられます。
オブジェクトの値は評価されます。
オブジェクトの値にはJSONオブジェクトのみを与えることができます。

タプルの組み立て

{
  "cons": {
    "obj1": {
      "function": {
        "args": [],
        "begin": [
          [ "add", 1, 2 ]
        ]
      }
    }
  }
}

consとほぼ同じですが、オブジェクトの値にはJSONオブジェクト以外も与えることができます。

ブロック

{
  "begin": [
    [ "add", 1, 2 ],
    [ "add", 2, 3 ]
  ]
}

構文をブロック化します。最後の値が返されます。

関数作成

{
  "function": {
    "args": [ "x", "y" ],
    "rest": "r",
    "begin": [
      [ "add", "x", "y", [ "r", 0 ] ]
    ]
  }
}

関数を作成します。argsに引数を、restに残余引数を設定します。
restの変数に変数番号を適用することでrestの値を参照できます。

条件分岐

{
  "if": {
    "cond": [ "eqv", "x", 0 ],
    "then": 1,
    "else": 2
  }
}

条件分岐です。condの値がfalseでないときはthenを、falseのときはelseを実行します。
elseは省略可能です。

多数の条件分岐

{
  "cond": [
    {
      "case": ["eqv", "x", 2],
      "then": 4
    },
    {
      "case": ["eqv", "x", 3],
      "then": 6
    },
    {
      "case": true,
      "then": 0
    }
  ]
}

多数の条件分岐です。上からcaseの条件を実行して、falseでなければthenを実行します。
condにtrueを指定することでelseを指定できます。

変数定義

{
  "define": {
  	"x": 1,
  	"y": 2
  }
}

変数を定義します。
オブジェクトのキーの変数名にオブジェクトの値を束縛します。

変数への値のセット

{
  "set": {
  	"x": 1,
  	"y": 2
  }
}

変数へ値をセットします。
オブジェクトのキーの変数名にオブジェクトの値をセットします。
変数はすでに値が束縛されている必要があります。

ローカル変数の定義

{
  "let": {
    "vars": {
      "x": 1,
      "y": 2
    },
    "begin": [
      [ "add", "x", "y" ]
    ]
  }
}

ローカル変数に値を束縛します。
varsのオブジェクトのキーへオブジェクトの値が束縛されます。

ループ(named let)

{
  "let": {
    "name": "sum",
    "vars": {
      "x": 10,
      "y": 0
    },
    "begin": [
      {
        "if": {
          "cond": ["eqv", "x", 0],
          "then": "y",
          "else": ["sum", ["sub", "x", 1], ["add", "x", "y"]]
        }
      }
    ]
  }
}

letにname属性をつけることでbeginの内容を関数にできます。
これにより、再帰によるループを実現できます。

ローカル関数の定義

{
  "letrec": {
    "vars": {
      "sum": {
        "function": {
          "args": ["x", "y"],
          "begin": [
            {
              "if": {
                "cond": ["eqv", "x", 0],
                "then": "y",
                "else": ["sum", ["sub", "x", 1], ["add", "x", "y"]]
              }
            }
          ]
        }
      }
    },
    "begin": [
      ["sum", 10, 0]
    ]
  }
}

letと同じですが、varsで定義できる関数内でvarsの値を参照できます。

準クオート

{
  "qq": {
    "obj1": "string",
    "obj2": {
      "obj3": { "uq": [ "add", 1, 2 ] }
    }
  }
}

クオートと同じですが、"uq"のみをキーとして持つオブジェクトが現れたときは"uq"の値を評価します。

タプルの準クオート

{
  "tq": {
    "obj1": "string",
    "obj2": {
      "obj3": { "uq": [ "add", 1, 2 ] }
    }
  }
}

qqとほぼ同じですが、タプルを生成します。

オブジェクトのパターンマッチ

{
  "match": {
    "target": "x",
    "patterns": [
  	  {
  	    "pattern": {
  	  	  "aaaa": "a",
  	  	  "bbbb": {
  	  	    "cccc": "c",
  	  	  },
  	  	  "iiii": ["d", "e"]
  	  	},
  	    "begin": [["list", "a", "c", "d", "e"]]
  	  },
  	  {
  	    "pattern": {
  	      "jjjj": "a"
  	    },
  	    "begin": ["a"]
  	  }
  	]
  }
}

オブジェクトがパターンにマッチするかを判定します。
オブジェクトの値に現れる文字列は同名の変数に束縛され、beginに渡されます。

文字列クオート

{ "sq": "Welcome to ${y}, $x.aaaa.cccc and $x.dddd production" }

文字列内の${変数名}を変数の文字列表現に置き換えます。
文字列の前後が空白のときは"$変数名"でも表現できます。
$x.propsの形でオブジェクト内のプロパティ名を指定できます。

マクロ

[
  {
    "defmacro": {
      "name": "aMacro",
      "patterns": [
        {
          "pattern": {
            "obj1": "a"
          },
          "begin": [
            {
              "qq": ["list", { "uq": "a" }]
            }
          ]
        }
      ]
    }
  },
  {
    "aMacro": {
      "obj1": 1
    }
  }
]

マクロのパターンにマッチした構文に対してbeginで評価された値で置き換えます。
置き換えられた後再度マクロの評価がされます。それにより再帰的なマクロも記述できます。
マクロの呼び出しは通常の構文と同様にマクロ名をオブジェクトのキーにして呼び出します。

and

{ "and": [1, 2, 3] }

andに与えられた配列が全てfalseでないときは最も右の値が戻ります。
1つでもfalseがあったときはfalseになります。
配列が空のときはtrueとなります。

or

{ "or": [false, 2, 3] }

orに与えられた配列に1つでもfalseでない値があったときはその値が戻ります。
全てfalseのときはfalseになります。
配列が空のときはfalseになります。

メッセージパッシング

{
  "message": {
    "extends": false,
    "messages": {
      "aaaa": 765,
      "bbbb": 346
    }
  }
}

文字列を与えてその結果がオブジェクトのキーに対応する値を返す関数を定義します。
メッセージが見当たらないときは、extendsの関数にそのメッセージを渡します。
extendsがfalseのときはエラーを返します。

messageを利用して簡易的なオブジェクト指向を実現することができます。

[
  {
    "define": {
      "class": {
        "function": {
          "args": ["x"],
          "begin": [
            {
              "message": {
                "extends": false,
                "messages": {
                  "add": {
                    "function": {
                      "args": ["y"],
                      "begin": [
                        ["add", "x", "y"]
                      ]
                    }
                  }
                }
              }
            }
          ]
        }
      }
    }
  },
  {
    "define": {
      "obj": ["class", 765]
    }
  },
  [["obj", { "q": "add" }], 346]
]

ライブラリリファレンス

四則演算

加算・乗算

[ "add", 1, 2, 3 ]
[ "+", 1, 2, 3 ]
[ "mul", 1, 2, 3 ]
[ "*", 1, 2, 3 ]

引数で与えられた値を全て足す、または掛けます。

減算・除算

[ "sub", 1, 2, 3 ]
[ "-", 1, 2, 3 ]
[ "div", 1, 2, 3 ]
[ "-", 1, 2, 3 ]

第1引数から第2引数以降を引く、または割ります。
第1引数のみが与えられた時は符号反転、または逆数を得ます。

整数の除算

[ "quotient", 13, 4 ]
[ "remainder", 13, 4 ]
[ "modulo", 13, 4 ]

quotientは整数で除算した商を返します。
remainderとmoduloは整数で除算した余りを返します。
remainderとmoduloの違いは以下の通りです。

Table 1. remainderとmoduloの違い
第1引数 第2引数 remainder modulo

13

4

1

1

-13

4

-1

3

13

-4

1

-3

-13

-4

-1

-1

比較演算

等値

[ "eqv", 1, 2 ]
[ "equal", { "q": { "obj1": 222 } }, { "q": { "obj1": 222 } } ]

eqvは値が等しいかを判定しますが、構造までは判定しません。 equalは値を構造まで含めて判定します。

数値

[ "=", 1, 1, 1, 1 ]
[ "!=", 1, 5, 3, 4 ]
[ "<", 1, 2, 3, 4 ]
[ "<=", 1, 2, 2, 3 ]
[ ">", 4, 3, 2, 1 ]
[ ">=", 3, 2, 2, 1 ]

引数が全て等しい、等しくない、単調増加、広義の単調増加、単調減少、広義の単調減少であるかを判定します。

文字列

[ "string=", "a", "a", "a" ]
[ "string!=", "a", "c", b" ]
[ "string<", "a", "aa", "b" ]
[ "string<=", "a", "a", "b" ]
[ "string>", "b", "aa", "a" ]
[ "string>=", "b", "a", "a" ]

文字列が辞書順にが全て等しい、等しくない、単調増加、広義の単調増加、単調減少、広義の単調減少であるかを判定します。

文字列(大文字小文字を区別しない)

[ "stringci=", "a", "A", "a" ]
[ "stringci!=", "a", "C", b" ]
[ "stringci<", "a", "Aa", "b" ]
[ "stringci<=", "a", "A", "b" ]
[ "stringci>", "b", "Aa", "a" ]
[ "stringci>=", "b", "A", "a" ]

文字列が辞書順にが全て等しい、等しくない、単調増加、広義の単調増加、単調減少、広義の単調減少であるかを判定します。
大文字小文字を区別しません。

論理演算

[ "not", false ]

引数がfalseでないときはfalse、falseのときはtrueを返します。

数学関数

三角関数

[ "sin", 0 ]
[ "cos", 0 ]
[ "tan", 0 ]

三角関数を返します。

逆三角関数

[ "asin", 0 ]
[ "acos", 0 ]
[ "atan", 1 ]

逆三角関数を返します。値域の外のときはNaNを返します。

指数・対数関数

[ "exp", 0 ]
[ "log", 1 ]

指数・対数関数を返します。値域の外のときはNaNを返します。

べき乗

[ "expt", 2, 3 ]

第1引数の第2引数乗を返します。

天井・床関数等

[ "floor", 2.3 ]
[ "ceiling", 2.3 ]
[ "truncate", 2.3 ]
[ "round", 2.3 ]

天井関数、床関数、小数点の切り捨て、小数点の四捨五入をします。

最大値・最小値

[ "max", 1, 3, 4, 2 ]
[ "min", 1, 3, 4, 2 ]

引数の中で最も大きい、または小さい値を取得します。

型述語

[ "numberp", 2.5 ]
[ "integerp", 2 ]
[ "booleanp", false ]
[ "nullp", null ]
[ "arrayp", { "q": [1] } ]
[ "objectp", { "q": { "obj": 1 } } ]
[ "functionp", { "function": { "args": [], "begin": [1] } } ]

第1引数が数、整数、論理値、null、配列、オブジェクト、関数であるときにtrueを返します。
配列もオブジェクトになります。
functionpは継続のときもtrueを得ます。

配列操作

配列作成

[ "list", 1, 2, 3 ]

引数を要素とする配列を生成します。

配列の先頭・2番目以降の取得

[ "first", { "q": [1, 2, 3] } ]
[ "rest", { "q": [1, 2, 3] } ]

配列の先頭の要素、2番目以降の配列を取得します。

プロパティ値の設定

[ "setprop", "obj1", { "q": { "obj1": 1 } }, 2 ]

プロパティの値を設定します。

連結

[ "concat", { "q": [1, 2, 3] }, { "q": [4, 5, 6] }, { "q": [7, 8, 9] } ]

引数で与えらえた配列を連結します。

配列のmap

[ "arraymap", "add", { "q": [1, 2, 3] }, { "q": [4, 5, 6] }, { "q": [7, 8] } ]

第2引数以降で与えられた配列を第1引数で与えられた関数に適用して、その値を要素とする配列を作成します。
上記の例では、[ "add", 1, 4, 7 ], [ "add", 2, 5, 8 ]が適用されて、[12, 15]が結果と仕返されます。

文字列操作

文字列の連結

[ "stringAppend", { "q": "abc" }, { "q": "def" }, { "q": "ghi" } ]

引数で与えられた文字列を連結します。

部分文字列の取得

[ "substring", { "q": "abcde" }, 1, 3 ]

第1引数で与えられた文字列の第2引数から(第3引数-1)番目までの文字列を取得します。

継続

[
  "callcc",
  {
    "function": {
      "args": ["k"],
      "begin": [
        ["k", 765]
      ]
    }
  }
]

現在の継続を取り出して、第1引数で与えられた関数の引数に渡します。
継続を使用することで、大域脱出や状態の復元を実現できます。

状態の復元の例を以下に示します。結果は765となります。

[
  {
    "define": {
      "s": null
    }
  },
  [
    "add",
    346,
    [
      "callcc",
      {
        "function": {
          "args": ["k"],
          "begin": [
            {
              "set": { "s": "k" }
            },
            961
          ]
        }
      }
    ]
  ],
  ["s", 765]
]

その他

関数への適用

[ "apply", "add", { "q": [1, 2, 3] } ]

第1引数で与えられた関数へ第2引数で与えられた配列の要素を引数として適用します。

オブジェクトのmap

[ "objectmap", "sub", { "q": { "obj1": 1, "obj2": 2 } } ]

第2引数で与えられたオブジェクトを第1引数で与えられた関数に適用して、その値を要素とする配列を作成します。

数値から文字列への変換

[ "numberToString", 100, 16 ]

第1引数で与えらえた数値を第2引数で与えられた基数で文字列に変換します。
基数は2〜36である必要があります。
基数が省略されたときは10とします。

文字列から整数への変換

[ "stringToInteger", "100", 8 ]

第1引数で与えらえた文字列を第2引数で与えられた基数で整数に変換します。
基数は2〜36である必要があります。
基数が省略されたときは10とします。

文字列から数値への変換

[ "stringToNumber", "100.3" ]

第1引数で与えらえた文字列を数値に変換します。

オブジェクトのキーの取得

[ "keys", { "q": { "obj1": 1, "obj2": 2 } } ]

オブジェクトからオブジェクトのキーを要素に持つ配列を取得します。

長さの取得

[ "length", { "q": [1, 2, 3] } ]
[ "length", { "q": "abc" } ]

配列または文字列の長さを取得します。

多値の生成

[ "value", 1, 2, { "function": { "args": [], "begin": [1] } } ]

多値を生成します。
配列にはJSONで表現可能な値しか要素になれませんでしたが、多値はJSONで表現不可能な値も要素になれます。

文字の出力

[ "p", { "q": "console output" } ]

コンソールに文字を出力します。

文字列表現の取得

[ "toString", { "q": [1, 2, 3] } ]

文字列表現を取得します。
JSONで表現可能な値のときはJSONでシリアライズされた値が取得できます。

エラーの出力

[ "error", { "q": "Error occurred" } ]

エラーを出力し、処理を終了させます。