fluorite12

範囲系演算子

閉区間 start .. end

閉区間演算子は、左辺から右辺までの整数の範囲のストリームを生成します。

end自身はストリームに含まれます。

$ flc '1 .. 3'
# 1
# 2
# 3

左辺が右辺よりも大きい場合、カウントダウンを行います。

$ flc '3 .. 1'
# 3
# 2
# 1

半開区間 start ~ end

半開区間演算子は、左辺から右辺の1つ手前までの整数の範囲のストリームを生成します。

end自身はストリームに含まれません。

$ flc '1 ~ 3'
# 1
# 2

半開区間演算子は、閉区間演算子とは異なり、右辺が左辺よりも大きい場合、空のストリームを生成します。

$ flc '[3 ~ 1]'
# []

ストリーム結合系演算子

ストリームの結合 items, ...

演算子 , は、左右の要素またはストリームを結合したストリームを生成します。

fluorite12では、ラムダ演算子の左辺のような特殊な場所でない限り、 , は引数や配列要素等の区切りではなくストリーム結合演算子として解釈されます。

$ flc '1, 2 .. 4, 5'
# 1
# 2
# 3
# 4
# 5

ストリーム結合演算子は余計に多く書いても無視されます。

$ flc ', , 1, , , , 2, , '
# 1
# 2

ストリーム結合演算子のみを記述することができ、その場合は空ストリームを生成します。

$ flc '[,]'
# []

flcコマンドはデフォルトの挙動で与えられたソースコードの戻り値を出力しますが、空のストリームに対しては何も出力しないため、flcコマンドの出力を抑制するのに使われることもあります。

$ flc '"何らかの副作用を伴う処理"; ,'

ストリーム系演算子

ストリーム系演算子は、ストリームの加工や代入などを行う演算子です。

ストリーム系演算子の簡単な紹介

結合優先度についての解説のため、ストリーム系に属する演算子を軽く紹介します。


パイプ stream | argument => formula は、 stream の各要素について formula を適用したストリームを得ます。

formula 内では、 argument でその要素を参照できます。

$ flc '1, 2, 3 | x => x * 10'
# 10
# 20
# 30

実行パイプ value >> function は、 functionvalue を渡して実行します。

$ flc '1, 2, 3 >> REVERSE'
# 3
# 2
# 1

変数宣言 variable := value は、変数 variable を宣言しつつ、その値を value で初期化します。

$ flc '
  x := 123
  x
'
# 123

代入 variable = value は、変数 variablevalue を代入します。

$ flc '
  x := 123
  x = 456
  x
'
# 456

結合優先度について

ストリーム系演算子は、実用上の理由から、以下の文法で表される複雑な結合規則を持っています。

ストリームノード :=
    ストリーム結合ノード  代入系演算子  ストリームノード
  / ストリーム結合ノード  ストリーム後方付加部*

ストリーム後方付加部 :=
    パイプ演算子  パイプ右辺
  / 実行パイプ演算子  実行パイプ右辺

実行パイプ右辺 :=
    ストリーム結合ノード  代入系演算子  ストリームノード
  / ストリーム結合ノード

パイプ右辺 :=
    ストリーム結合ノード  パイプ系演算子  パイプ右辺
  / ストリーム結合ノード  代入系演算子  ストリームノード
  / ストリーム結合ノード

以下では、ストリーム系演算子の文法を例を用いて解説します。


パイプ系演算子は、原則として右優先結合です。

このため、前段の変数を後段から参照することができます。

$ flc '10, 20 | x => 3, 4 | y => x + y'
# 13
# 14
# 23
# 24

$ flc '10, 20 | x => (3, 4 | y => x + y)'
# 13
# 14
# 23
# 24

実行パイプ系演算子は、左側にあるパイプ系・実行パイプ系演算子をまとめて取ります。

これにより、様々に加工したストリームの全体を関数に入力することができます。

$ flc '10, 20 | x => 3, 4 | y => x + y >> JOIN["-"]'
# 13-14-23-24

$ flc '(10, 20 | x => 3, 4 | y => x + y) >> JOIN["-"]'
# 13-14-23-24

実行パイプ系演算子は他の実行パイプ系演算子も左辺にまとめて取ります。

また、実行パイプ系演算子による結果を、さらに別のパイプ系演算子で加工出来ます。

$ flc '1 .. 3 | _ * 10 >> REVERSE | _ + 5 >> JOIN["-"]'
# 35-25-15

$ flc '((1 .. 3 | _ * 10) >> REVERSE | _ + 5) >> JOIN["-"]'
# 35-25-15

代入系演算子は、右辺を左辺から分離します。

代入系演算子の右辺にある実行パイプ系演算子は、代入系演算子の左辺には影響を及ぼしません。

$ flc '
  pow2_joiner := stream -> stream | x => x * x >> JOIN["-"]
  pow2_joiner(1, 2, 3)
'
# 1-4-9

以下では、関数 setter を呼び出すと、与えた数値に36を足して平方根を取った値を変数 variable に代入します。

SQRT の左にある >> は、その左の = の手前までを左辺に取ります。

$ flc '
  variable := NULL
  setter := x -> x + 36 | x2 => variable = x2 >> SQRT
  setter(64)
  variable
'
# 10.0

配列の要素への代入(代入系) array(index) = value

左辺が配列の要素の参照であった場合、その要素に右辺の値を代入します。

$ flc -q '
  array := [1, 2, 3]
  OUT << array
  array(1) = 4
  OUT << array
'
# [1;2;3]
# [1;4;3]

エントリー演算子(代入系) key: value

エントリー演算子は、両辺を要素とする2要素の配列を生成する演算子です。

$ flc 'a: 1'
# [a;1]

左辺が識別子の場合、同名の変数があっても、変数を参照するのではなく文字列として扱います。

$ flc '
  a := "b"
  a: 1
'
# [a;1]

左辺で変数を参照したい場合は、括弧で囲むことで文字列として扱われることを回避できます。

$ flc '
  a := "b"
  (a): 1
'
# [b;1]

エントリー演算子は配列リテラルとは異なり、ストリームを展開せず、常に2要素の配列を生成します。

$ flc '["key"; 1 .. 3]'
# [key;1;2;3]

$ flc 'key: 1 .. 3'
# [key;123]

この演算子はオブジェクトを生成する際に有用です。

$ flc '
  {
    a: 1
    b: 2
  }
'
# {a:1;b:2}

左実行パイプ(代入系) function << value

左実行パイプは、右辺の値を左辺の関数の第1引数に指定して呼び出します。

右実行パイプの左右が逆のバージョンですが、結合優先度が代入系扱いです。

使い方によっては可読性に貢献する可能性を秘めています。

$ flc -q '
  OUT << "Hello, World"
'
# Hello, World

パイプ(パイプ系) stream | formula

パイプ演算子 | は、左辺のストリームの各値に対して右辺を評価し、そのフラットなストリームを返します。

右辺では、変数 _ によって左辺の各要素の値を得ることができます。

$ flc '1 .. 3 | _, _ * 10'
# 1
# 10
# 2
# 20
# 3
# 30

左辺がストリームでない場合、右辺の返却値はストリームで改めてラッピングされることなく、そのままの型で返されます。

$ flc '(5 | _ * 10) + 7'
# 57

右辺に渡される変数は => によって変更できます。

$ flc '5 | x => x * 10'
# 50

パイプ演算子をループ構文のように使うこともできます。

$ flc '
  x := 0
  1 .. 10 | (
    x = x + _
  )
  x
'
# 55

右実行パイプ(実行パイプ系) value >> function

右実行パイプは、左辺の値を右辺の関数の第1引数に指定して呼び出します。

$ flc '1 .. 3 >> JOIN["-"]'
# 1-2-3

この演算子はストリームを扱う関数の実行に便利です。

$ flc '"1+2+3" >> SPLIT["+"] | +_ * 2 >> JOIN["-"]'
# 2-4-6

オブジェクトの継承 parent{entry; ...}

オブジェクトに { } を後置すると、そのオブジェクトを親とする子オブジェクトを生成します。

オブジェクトの継承は主にメソッドの検索に使われ、エントリーの継承は行われません。

オブジェクトの生成方法はオブジェクトリテラルと共通です。

$ flc '{a: 1; m: this -> 3}{b: 2}'
# {b:2}

$ flc '{a: 1; m: this -> 3}{b: 2}.a'
# NULL

$ flc '{a: 1; m: this -> 3}{b: 2}::m()'
# 3

オブジェクトの要素アクセス object.key

. 演算子でオブジェクトの要素にアクセスできます。

$ flc '{x: 123}.x'
# 123

オブジェクトが親オブジェクトを持つ場合でも、親オブジェクトの要素は継承されません。

$ flc '{x: 123}{}.x'
# NULL

. の右辺に括弧を置くことで、任意の式によって参照できます。

$ flc '
  obj := {item1: 123; item2: 456}
  index := 2
  obj.("item$index")
'
# 456

. は、右辺が識別子の場合、それを変数ではなくキーとして解釈する性質があります。

したがって、括弧の有無によって参照するエントリーに違いが現れます。

$ flc '
  key := "item1"
  obj := {key: 123; item1: 456}
  [obj.key; obj.(key)]
'
# [123;456]

キーは自動的に文字列化されます。

$ flc '{1: 123}.1'
# 123

Null安全要素アクセス object?.key

?. 演算子は左辺が NULL の場合に、要素の取得を試みる代わりに NULL を返します。

$ flc '{x: 1}, NULL, {x: 3} | _?.x'
# 1
# NULL
# 3