前置記法を採用したプログラミング言語

;;
;; 概要
;;

ポーランド記法/前置記法を採用したプログラミング言語。

+ 1 2  ; ポーランド記法
1 2 +  ; 逆ポーランド記法
1 + 2  ; 中置記法

;;
;; サンプル
;;

= str "hello"          ; var str = "hello"
= ary ["world" "end"]  ; var ary = ["world", "end"]

print str ary.0        ; print("hello", "world")

if == str "hello"
  print "yes"
  print "no"

= v + 1 2
print v    ; 3

sort [3 2 1] {[a b] > a b}  ; [1 2 3]
[3 2 1].sort {> $0 $1}      ; [1 2 3]
[3 2 1].sort (>)            ; [1 2 3]

func counter ()
  = c 0
  fn () = c (+ c 1)

= f counter
f  ; 1
f  ; 2

func fact n
  if == n 0
     1
     * n
       fact - n 1

fact 3  ; 6
fact 4  ; 24

times 3
  print str $        ; "hello0hello1hello2"
times (3 i) print i  ; "012"

1 + 2  ; 3  (1.+ 2)

;;
;; 設計方針
;;

- インタプリタ
- 型システム無し
- evalあり
- for文if文等の基本機能はライブラリ関数として実装
- コア言語がサポートする基本型は最小限にする
  -- Number型、String型、List型、Map型、Key型、Function型
- 基本型のメンバ関数はライブラリ側で拡張可能にする

;;
;; 仕様
;;
;; 言語仕様の全体像をサンプルコードと共に示す
;; 今後はこれらの仕様と改善案を元に、
;; 更なる言語仕様の軽量化を図っていくものとする
;;
;; Create: 2016-12-24
;; Update: 2016-12-24  公開(基本文法、言語仕様、コア機能、拡張機能)
;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 基本構文
;;

;; 代入・比較
;;
= i 3    ; var i = 3
== i 3   ; i == 3

;; 四則演算
;;
+ 1 2    ; 1 + 2
- 1 2    ; 1 - 2

+ 1 2 3       ; 1 + 2 + 3
+ 1, 2, 3     ; 1 + 2 + 3
(+ 1 2 3)     ; 1 + 2 + 3
+(1 2 3)      ; 1 + 2 + 3       ;; 要検討(糖衣構文)
+ (1 2 3) 4   ; [1, 2, 3] + 4   ;; 要検証

;; 二項演算優先の原則(保留)
; + 1 2 3       ; 1 + 2; 3;
; + + 1 2 3     ; (1 + 2) + 3
; * + 1 2 - 6 3 ; (1 + 2) * (6 - 3)
; if == nil v return 0 ; if (nil == v) return 0

;; リテラル
;;
99          ; Number
"abc"       ; String
[1 2 3]     ; List
(1 2 3)     ; List ;; 要検討
[:key 99]   ; Dict
[key: 99]   ; Dict ;; 要検討(糖衣構文)
{:key 99}   ; Dict ;; 要検討

;; 配列
;;
= ary [1 2 3]
ary 0  ; 1
ary[1] ; 2 ;; 糖衣構文
ary.2  ; 3 ;; メンバアクセス
ary(2) ; 3 ;; 糖衣構文

;; 辞書
;;
= dic [:key 99 :may 5]
dic "key"   ; 99
dic["key"]  ; 99 ;; 糖衣構文
dic.key     ; 99

;; 関数
;;
;; 関数定義式の採用は保留
;;   - 無名関数を変数へ代入することで代用
;;
= add fn (a b) + a b  ; func add(a, b) { a + b }
add 1 2   ; 3
add 1, 2  ; 3
add(1 2)  ; 3

= sub ~ (a b) - a b  ;; 要検討(糖衣構文または基本構文とする)
= mul ~~ * $0 $1     ;; 案2-1
= div ~~ / _0 _1     ;; 案2-2
= rem ^(a b) % a b   ;; 案3
= add {(a b) + a b}  ;; 案4-1
= add {+ _0 _1}      ;; 案4-2(仮引数省略時専用)
= add {[a b] + a b}  ;; 案4-3(案4-2との混在が可能)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; オブジェクト指向
;;

;; メソッド
;;
"s".len nil       ; "s".len()
"s".len()         ; 糖衣構文
"s" len           ; "s"(:len)
"R".sub "R", "r"  ; "R".sub("R", "r")

;; クラス
;;
class Number
  fn init(v) = self.intValue v
  fn is(v)   == v self.intValue
  fn pow(v)  Math.pow self.intValue, v
  fn +(v)    + self.intValue v
  fn get()   return self.intValue
  ;; 案2
  = is  fn (v) == self v
  = pow fn (v) Math.pow self v
  = pow {[v]
    Math.pow self v
  }
  ;; fn VS func
  ; 無名関数との区別のためにfuncの採用を検討中
  ; func name(a b) + a b ;; メソッド宣言式
  ; fn (a b) + a b       ;; ラムダ式

;; インスタンス化
;;
= v (Number 99)

;; メソッド呼び出し
;;
v.is 2     ; v.is(2)
+ v 2      ; v.+(2)
10.pow 3   ; 1000
v get      ; 9
v.get()    ; 9
v.get      ; func Number::get
v.get nil  ; 9
(9.get)    ; 9

;; 糖衣構文
;;
"".split(",") ; ("".split ",")
"".len()      ; ("".len void)
print("")     ; (print "")

;; オブジェクト
;;
;; あらゆる値はオブジェクトとして表現される
;; 各演算子の挙動は被演算子側のオブジェクトに依存する
;;
= (+) fn (a b) (a.+ b)
+ 1 2       ; 3
class String
  func (+) (s) (self.copy().append s)
+ "a" "b"   ; "ab"

;; Objectクラス
;; 全てのオブジェクトはObjectクラスに準拠する
;;
class Object
  func (==) (a) __UNIMPLEMENTED__
  func (!=) (a) self.==(a)
class String
  func (==) (a) !(__builtin__["string.h"].strcmp self a)

(!= "a" "b") ; true

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; コア機能
;;
;; 参考:
;;   [#用語の意味]
;;   [#予約語]
;;   [#備考]
;;

;; ブロックの解釈
;;
;; インデントで式の繋がりを表現する
;;  - 複文の実現が可能になる(関数の処理部やクラスのメソッド宣言部で使用)
;;  - 入れ子された式の延長用途にも使われる
;;
;; 深さ1:
;;    一つ前の段落の式に対応する要素式であることを意味する
;; 深さ2以上:
;;    前の行の最後式に続く要素式であることを意味する
;; 特性
;;    インデントされた要素式は単一の式として解釈される
;;

;;;
;;; 例(深さ1)
;;;    ※ インデント幅は空白2つと仮定

; + 1 2
+
  1
  2
; + 1 2
+ 1
  2
; + 1 (2 3)
+ 1
  2 3  ;; 3は+関数の第三引数にならない
; (+ 1 2 3)
+ 1
  2
  3
; + 1 (+ 2 3)
+ 1
  + 2 3
; + (+ 1 2) 3
+
  + 1 2
  3
; - (+ 1 2 3)
-
  + 1 2
    3
; = v (+ 1 2)
= v
  + 2 3

;;;
;;; 例(深さ2以上)
;;;

; + 1 (+ 2 3)
+ 1 + 2
    3
; + 1 (+ 2 3)
+ 1 + 2
      3
; = v (+ 1 (+ 2 3) 4)
= v
  + 1 + 2
        3
    4
; = add {(a b) (+ a b)}
= add fn (a b)
        + a b

if true
  then:
    print "true"
    print "yes"
  else:
    print "false"
    print "no"

;; 中置記法
;;
1 + 2  ; (1 + 2) --> 1(+, 2) --> 1.operator+(2)

;; 糖衣構文
;;
= bob :name "bob" :age 16  ; = bob {name:"bob", age:16}
= tom :name "tom"
      :age 32

;; 遅延評価
;;
;; 引数は全て式として渡される
;; 式は関数側で遅延評価される
;;
;; 式中に仮引数名が出現した場合は評価される
;; 式の評価は仮引数側で自動的に行われる
;; 仮引数名に前置`#`を付与することで式の評価を保留することが出来る
;;
= foo fn (a)   ; この段階でaが評価される
        a      ; aには評価結果が格納されている
foo + 1 2      ; 3
;; 仮引数の先頭が`#`で始まる場合は式の評価を保留する
= foo fn (#a)  ; 評価されない
        #a     ; aは式
foo + 1 2      ; fn () + 1 2
;; 本文中で#が省略された場合は式の展開・評価が行われる
= foo fn (#a)  ; 評価されない
        a      ; 評価される
foo + 1 2      ; 3
;; if文はこの仕組みを用いて実装することができる
func if (cond #then #else)
       ?: cond #then #else
if nil (print "a") (print "b")  ; "b"
;; この仕組みによって独自のキーワードを処理することも可能になる
= if fn (c #t #else #e)
    (?: c #t (?: (= #else else:) #else #e))
if nil 1 else: 2
;; この仕組みによって演算子の優先順位を実現することが可能になる
class Number
  func (+) (#expr)
    if && == "Expression" typeof #expr
          == "Number"     typeof #expr[0]
          == "Operator"   typeof #expr[1]
          || == #expr[1] (*)
             == #expr[1] (/)
      + self   expr ; (+ 1 (2.* 3))  ->  (+ 1 6)
      + self ##expr ; (+ 3 2 - 3)    ->  (+ 3 2).- 3
1 + 2 * 3  ; 7
3 + 2 - 1  ; 4

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 例外的な仕様と拡張機能
;;

;;
;; ブロックの解釈
;;
;; インデントの深さを関数名の幅基準にする拡張案
;;
add 1
    2

;; 影響1
add 1 + 2 3
    4
;; 対応案
add 1 + 2 3
        4
;; 影響2
printDictionary :name "mary"
                :age  16
;; 対応案
printDictionary : name "mary"
                  age  16

;;
;; 動的スコープ
;;
;; #コア機能  #重要度 100%  #影響度 50%
;;
;; classのメソッド宣言式を実現するために必要
;;   - func関数からclassのselfインスタンスにアクセス出来るようになる
;;
= func fn (name ...) (= $local.self[name] ...)
class X
  func get() 99
= i X()
i.get()  ; 99
;; varの実装が可能になる
= var fn (name val) = $local[name] val
var i 99   ; = i 99

;;
;; インライン・インデント
;;
;; #拡張機能  #重要度 70%  #影響度 50%
;;
;; インデント区切り(タブ区切りまたは2文字以上の空白)を、
;; ブロックの区切りと見なす拡張案
;;
*  + 1 2 3  + 3 4 5  ; * (+ 1 2 3) (+ 3 4 5)
+  1 2  3 4          ; + (1 2) (3 4)

;; 演算子の前置記法
;;
;; #コア機能  #重要度 75%  #影響度 0%
;;
+ -1 2   ; (+ (- 1) 2)
= *p &v  ; int *p = &v;
;; ラムダの糖衣構文`~`を実現することが可能になる
= not ~ (bit) ~bit

;; 前置呼び出し
;;
;; #コア機能  #拡張機能  #重要度 100%  #影響度 0%
;; #関連仕様 [#演算子の前置記法]
;;
+(1 2)     ; (+ 1 2)
-1         ; (- 1)
print(1 2) ; (print 1 2)
;; 影響
^(a b) + a b  ; (fn a b) (+ a b)

;; 前置呼び出し昇格
;;
;; #拡張機能  #重要度 9%  #影響度 100%
;; #前提仕様 [#前置呼び出し]
;; #代替仕様 [#式展開の原則]
;;
;; 前置呼び出しされた式をオペレータとして解釈させる拡張案
;; - 前置式はオペレータに昇格する
;; - 続く式はオペランドとして解釈される
;;
~~(a b) + a b ; (~~ (a b) + a b)
^(a b) ^ a b  ; (fn (a b) ^ a b)
;; 辞書リテラルの糖衣構文はこの仕様によって実現可能となる
:a 1 :b 2     ; (: a 1 (: b 2))
;; 影響
print -1 2     ; print (- 1 2)
print f() 2    ; print (f() 2)
== :key val ; = (: key val)
;; 対応案
print -1, 2   ; print -1 2
== key: val ; = key: key

;; 代替関数呼び出し
;;
;; #コア機能  #拡張機能  #重要度 90%  #影響度 25%
;;
;; 1. グローバル関数が存在しない場合はオブジェクトのメンバ関数を呼び出す
;; 2. オブジェクトにメンバ関数が存在しない場合はグローバル関数を呼び出す
;;   ※ 元の関数呼び出し形式に応じて、いずれか一方の探索を行うものとする
;;
len "a"   ; ("a" len)
1.+ 2     ; (+ 1 2)

;; プロパティ呼び出し
;;
;; #コア機能  #重要度 60%  #影響度 100%
;; #関連仕様 [#遅延評価 #式展開の原則]
;;
;; プロパティを取り入れるために関数呼び出しの仕組みを変更する案
;;
;; 案1 -- 関数呼び出しをデフォルトにする
"ab".len   ; 2
#"ab".len  ; func String::len
"ab".len#  ; 案2
"ab".len&  ; 案3
"ab"::len  ; 案4(静的メンバ採用時注意 -> Int::MAX vs 9::MAX)
;; 案2 -- 後置"."を関数呼び出しのトリガーにする
self.copy.append "a" ; self().copy().append "a"
;; 案3 -- 遅延評価の仕様に例外を設ける
func test (#f)
  ;; before
  f  ; func String::len
  ;; after
  f  ; 2
test "ab".len

;; 式展開の原則
;;
;; #コア機能  #拡張機能
;; #重要度 75%  #影響度 200%
;; #前提仕様 [#遅延評価]
;;
;; 遅延評価の仕様を拡張
;; 式の文脈に応じて展開後の値を渡せるようにする拡張機能
;;   - [#プロパティ呼び出し 案1]の実現に必要
;;
;; 1. 式がオペレータとして評価できる場合にのみ展開を行う
;; 2. 式がオペランドとして評価される場合には展開を行わない
;; 3. メンバアクセスについては常に展開を行う
;;
= say ~~ "hello"
say         ; "hello"
#say        ; ~~ "hello"
{#_0} say   ; "hello"
{#_0} #say  ; ???
"a".len        ; 3
{#_0} "a".len  ; 3
{#_0} #"a".len ; func String::len
;; [#前置呼び出し昇格]への影響を考慮する必要がある
^(a) print a  ; (fn (a) print a) ; (## fn (a) pritn a)

func while (#cond #stmt)
  = i    0
  = stop 0
  = break ~~ = stop 1
  if && cond
        !stop
    stmt i--
while true
  if > 9 $0
    print $0
    break

;; 引数リスト
;;
;;  仮引数の明示/省略を区別するための仕様案
;;  第一引数を仮引数リストとして解釈するラムダ式とそうでないラムダ式の、
;;  その両者の区別をなくすために必要となる
;;  引数リストは配列リテラルで表現する
;;
= add ~~ [a b] (+ a b)
= sub ~~ (+ _0 _1)
{[a] print a} 1 ; "1"
{print _} 2     ; "2"
;; 影響
;;   コア機能に影響
;;   ラムダ宣言関数`fn`, `~~`の内部で引数リスト`[]`の判定が必要になる
;;   配列リテラルとリストの内部表現を区別する仕組みが必要になる
;;
; before
typeof [1 2]  ; List
typeof (1 2)  ; List
; after
typeof [1 2]  ; Array
typeof (1 2)  ; List

;; 遅延評価の拡張
;;
;; 変数宣言時にも遅延評価の仕組みを利用可能にする
;;
=# f (+ 1 2)
f  ; (+ 1 2)
#f ; 3
=# f (+ 1 2) ; 案2(=#演算子側で参照バインド)
# f (+ 1 2)  ; 案3(専用の#演算子を定義)
= f #(+ 1 2) ; 案4(前置#演算子でラップ)
= f &(+ 1 2) ; 案5
=& f (+ 1 2) ; 案6
# f (+ 1 2)  ; 案7
*f ; 3

;; 演算子の結合規則
;;
;; 二項演算子は第二引数までを処理する
+ 1 2 3     ; (+ 1 2) 3
+ + 1 2 3   ; (+ (+ 1 2) 3)
+ + 1 2 3 4 ; (+ (+ 1 2) 3) 4
;; 単行演算子は第1引数までを処理する
! 1  ; nil
~ 0  ; -1

;; 可変長式
;;
;; 二項演算優先の原則に例外を設ける
;;
;; 1. 演算子に続く要素が値として評価できる場合は、
;;    たとえそれが三項目以降の要素であっても、
;;    演算子に対応する被演算子と見なすことができる
+ 1 2 3  ; (+ 1 2 3)
;; 2. 続く要素に演算子が現れた場合はその先に続く要素をその演算子の被演算子とする
+ 1 + 2 3 4  ; (+ 1 (+ 2 3 4))
= v + 1 2 3     ; = v (+ 1 2 3)
= v + 1 + 2 3 4 ; = v (+ 1 (+ 2 3 4))
= add  ~ (a b) + a b ;; = add (~ (a b) (+ a b))
= add fn (a b) + a b ;; ???: 予約語については要検討
;; 3. 演算子が連続する場合は内側の演算子を二項演算として処理する
+ + 1 2 3 4 ; (+ (+ 1 2) 3 4)
;; 4. ユーザ定義関数については標準でこの仕様が適用される
add 1 2 3 ; (add 1 2 3)
;; 4. 可変長式は可変長引数で処理する
func add (...) (+ ...)
add 1 2    ; (+ 1 2)
add 1 2 3  ; (+ 1 2 3)
;;
func switch (val ...cases)
    = i 0
    while true
      if = val cases[i][0]
        return cases[i][1]
        ++ i
switch argc
  1 print "1"
  2 print "2"
;; ヨーダ記法との相性
== "foobar" + foo bar
== (+ foo bar) "foobar" ; ヨーダ記法未使用時

;; 影響
if == obj nil print "nil"   ; (if (== obj nil print "nil"))
if (== obj nil) print "nil" ; (if (== obj nil) (print "nil"))

;; selfとthis
;;
;; クラスをコア機能に取り込む場合は以下の区別を行う
;;
;; this: 関数オブジェクトが自身を参照する際に使用
;; self: メソッドがクラスのインスタンスを参照する際に使用
;;
;; selfは動的スコープの仕様と組み合わせて実装できる
;;
= self fn () $local.this.$local.this
class X
  = v 99
  func f ()
    this         ; f関数への参照
    self         ; Xインスタンスへの参照
    $local.this  ; Xインスタンスへの参照
    self.v         ; 99
    $local.this.v  ; 99
;; superの定義案
= super fn () $local.this.$isa.this

;; スコープチェーン
;;
;; 変数の捜索はまずスコープ内で行われる
;; 次に引数リスト
;; 次にthisオブジェクト
;; 次に動的スコープ($local)から同じ手順で
;;   $local.スコープ内
;;   $local.引数リスト
;;   $local.thisオブジェクト
;;   $local.$local
;; へと捜索を繰り返す
;;
;; クロージャーはこの仕組みによって実現される
= v 99
(~~ () print v)() ; "99"
;; selfの省略記法もこの仕組みで実現される
class X
  = v 99
  func f ()
    self.v ; 99
    v      ; 99
;; 動的スコープとの関係
func foo (#f)
  = v 0
  f
= v 1
foo {echo v} ; 1

;; アンダースコア命名規則
;;
;; #コア機能  #重要度 75%  #影響度 75%
;;
;; アンダースコアで始まる変数は暗黙的にthisのメンバを指すものとする拡張案
;;
= add ~~ + _0 _1 ; = add (~~ (+ this["0"] this["1"]))
;; 影響
{echo _} 2 ; 2?  this[""]?

class X
  = v 1
  func f () _v
= x X
= (#x.f)._v 2
x.f   ; 2
x.v   ; 1?

;; 比較演算子の拡張
;;
;; 式中の代入演算子`=`を比較演算子`==`として解釈させる案
;;
if (= a nil) print a  ; if (== a nil) print a
;; 影響
func lazyView ()
  return = _view (View new)
while = next next.next  print next.v

;; 型の明示
;;
;; 空白を挟まない`要素:型名`記法が用いられていた場合、
;; インタプリタ側では`:型名`の部分を無視する
;;
func makeView (point:Point size:Size):View
  (View point size)
;; この記法を正式な型システムに取り入れる場合は、
;; 仮引数のインターフェースを厳格に定める必要がある
= func fn (name #params ...)
           = $local[name] (fn params ...)
           params
# params (func makeView (point:Point size:Size):View nil)
params[0].name ; point
params[0].type ; Point
params[1].name ; size
params[1].type ; Size
params.type    ; View
params[0]      ; ???

;; 名前付き引数
;;
(View point:(2 3) size:(600 450)) ; 案1
(View (2 3):point (600 450):size)  ; 案2

;; その他
;;
;; 関数をオブジェクトとして渡すための記法
call + 1 2   ; (call (+ 1 2))
call +, 1 2  ; (call + 1 2)
call (+) 1 2 ; 代替案2
call #+ 1 2  ; 代替案3

;; 予約語
;;
;; 今後の言語拡張を想定し以下の演算子・記号・識別子を予約語とする
;;
;; - 一般的な演算子 (+ - * / & % etc.)
;; - "_" で始まる全ての識別子
;; - (? ~ ~~ ` \ _)
;; - (fn func fun class)
;; - (ret return break)
;; - (it is as eq has then else end)
;; - (not or and)
;; - (self this that)
;; - (may be to do)
;; - (yes no T nil void)
;; - (typeof)
;;
;; 想定される予約語の利用用途
? "cond"
  echo "then"
  echo "else"
= f ~~ echo _
f 99   ; "99"
do {echo "a"} {echo "b"} ; "ab"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 用語の意味
;;
;; | 演算子   | オペレータに相当するもの
;; | 被演算子  | オペランドに相当するもの
;; | 関数    | 演算子に相当するもの
;;
;; | 値     | 単項式に相当するもの。数値や文字等のリテラルを含む
;; | 要素    | 値や変数または要素式
;; | 式     | 要素のまとまり
;; | 要素式   | 式の要素となる式
;;
;; | 式渡し   | 遅延評価の仕組みによって渡される式
;;
(= v (+ 1 2))  ; 式
 =             ; 関数・演算子
   v           ; 要素・被演算子
      +        ; 関数・演算子
     (+ 1 2)   ; 要素・要素式・式
        1      ; 要素・被演算子・値
+ v 1          ; 式
+              ; 関数・演算子
  v            ; 要素・被演算子・値・変数
    1          ; 要素・被演算子・値
(print "a")    ; 式
 print         ; 関数・演算子

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 備考
;;
;; 配列リテラル`[1 2 3]`の採用を検討中
;;    リストリテラル`(1 2 3)`の廃止が可能になる
;; 辞書リテラル`{:key val}`を採用しない方針を検討中
;;    代替記法: `[:key val]`
;;    `{}`をラムダ式用の糖衣構文として使いたい
;; 二項演算優先の規約に例外的な仕様を設ける
;;    `+ 1 2 3`は`(+ 1 2 3)`として解釈される
;; 中置記法の実現するための拡張案を検討中
;;   C言語・JavaScript風の記法を可能にする
;;   記法の定義と制御をライブラリ側で行えるようにする
;;   インタプリタインタプリタの実現が必要
;;   言語仕様の肥大化に繋がる