Index: .poseidon ================================================================== --- .poseidon +++ .poseidon @@ -9,15 +9,15 @@ 0 main.d - -D -Dddoc -g -unittest + -g -unittest - doc\candydoc\candy.ddoc doc\candydoc\modules.ddoc + 0 0 0 0 @@ -50,15 +50,16 @@ build.bat build.sh - doc\candydoc\candy.ddoc + builddoc.bat doc\candydoc\modules.ddoc + index.dd readme.txt ADDED builddoc.bat Index: builddoc.bat ================================================================== --- builddoc.bat +++ builddoc.bat @@ -0,0 +1,6 @@ +@setlocal ENABLEDELAYEDEXPANSION +@set ARGS= +@for %%I in (main.d polemy\*.d tricks\*.d) do @set ARGS=!ARGS! %%I +@if not exist bin mkdir bin +@echo dmd -o- -D -Dddoc index.dd doc\candydoc\candy.ddoc doc\candydoc\modules.ddoc %ARGS% +@dmd -o- -D -Dddoc index.dd doc\candydoc\candy.ddoc doc\candydoc\modules.ddoc %ARGS% Index: doc/candydoc/modules.ddoc ================================================================== --- doc/candydoc/modules.ddoc +++ doc/candydoc/modules.ddoc @@ -1,6 +1,7 @@ MODULES = + $(MODULE index) $(MODULE main) $(MODULE tricks.tricks) $(MODULE tricks.test) $(MODULE polemy._common) $(MODULE polemy.failure) ADDED index.dd Index: index.dd ================================================================== --- index.dd +++ index.dd @@ -0,0 +1,514 @@ +Ddoc +$(DDOC_AUTHORS k.inaba) +$(DDOC_LICENSE NYSL 0.9982 (http://www.kmonos.net/nysl/)) + +

+このファイルは、言語仕様などの簡単な説明です。 +

+

+あとついでに、左のサイドバーの "Package" タブをクリックすると実装のソースのドキュメントが読めます。 +

+ +$(DDOC_MEMBERS + +$(SECTION Syntax, $(SECBODY + +

+文法について。 +字句解析がわりと適当なので、 +変数宣言の変数名のところに、数字を変数名として使えて参照できない変数が作れたり、 +予約語は予約語として解釈され得ないところでは普通に変数名として使えちゃったりして、 +偶にとんでもない見かけのソースが構文解析通りますが、気にしないで適当に使って下さい。 +

+ +$(DDOC_MEMBERS + +$(SECTION 文字コード, $(SECBODY +

+UTF-8 のみ対応です。 +

+)) + +$(SECTION コメント, $(SECBODY +

+行コメントは # から改行までです。 +

+

+ブロックコメントはありません。 +

+)) + +$(SECTION BNF, $(SECBODY +
+ ID    ::= 適当に識別子っぽい文字列
+ LAYER ::= "@" ID
+
+ E ::=
+   $(D_COMMENT # 変数宣言)
+     | ("var"|"let"|"def"|LAYER) ID "=" E (";"|"in") E
+     | ("var"|"let"|"def"|LAYER) ID "(" PARAMS ")" "{" E "}" (";"|"in") E
+     | ("var"|"let"|"def"|LAYER) ID "=" E
+     | ("var"|"let"|"def"|LAYER) ID "(" PARAMS ")" "{" E "}"
+
+   $(D_COMMENT # リテラル)
+     | INTEGER                        $(D_COMMENT # 非負整数)
+     | STRING                         $(D_COMMENT # "" でくくった文字列。\" と \\ は使える)
+     | "{" ENTRYS "}"                 $(D_COMMENT # テーブル)
+     | "fun" "(" PARAMS ")" "{" E "}" $(D_COMMENT # 無名関数)
+     |  "λ" "(" PARAMS ")" "{" E "}" $(D_COMMENT # 無名関数)
+
+   $(D_COMMENT # 関数呼び出し)
+     | E "(" ARGS")"
+
+         where    ARGS ::= E "," ... "," E
+                PARAMS ::= (ID|LAYER)+ "," ... "," (ID|LAYER)+
+                ENTRYS ::= ID ":" E    "," ... "," ID ":" E
+
+   $(D_COMMENT # 演算子など)
+     | "(" E ")"                 $(D_COMMENT # ただの括弧)
+     | E BINOP E                 $(D_COMMENT # 二項演算子いろいろ)
+     | E "."  ID                 $(D_COMMENT # テーブルのフィールドアクセス)
+     | E ".?" ID                 $(D_COMMENT # テーブルにフィールドがあるか否か)
+     | E "{" ENTRYS "}"          $(D_COMMENT # テーブル拡張)
+     | "if" "(" E ")" "{" E "}"
+     | "if" "(" E ")" "{" E "}" "else "{" E "}"
+
+   $(D_COMMENT # パターンマッチ)
+     | "case" "(" E ")" ("when" "(" PATTERN ")" "{" E "}")* 
+
+         where PATTERN ::= 式がだいたいなんでも書ける気がする
+
+   $(D_COMMENT # レイヤ指定実行)
+     | LAYER "(" E ")"
+
+)) + +$(SECTION 糖衣構文, $(SECBODY +

+演算子というものはありません。内部的には全て関数呼び出し構文に書き換えられています。if もです。 +
+パターンマッチも全部 if==&& と +..? を使った関数呼び出し式に書き換えられていますが、 +規則の詳細を説明するのが面倒なので適当に想像して下さい。 +他の書き換えはこんな感じです。 +

+
+    if (E) then{E}         ⇒ if( E, fun(){E}, fun(){} )
+    if (E) then{E} else{E} ⇒ if( E, fun(){E}, fun(){E} )
+    E BINOP E              ⇒ BINOP(E, E)
+    { ENTRIES }            ⇒ {}{ ENTRIES }
+    {}                     ⇒ {}()
+    E {ID:E, ...}          ⇒ .=(E, ID, E) { ... }
+
+

+変数宣言に色々ありますが、letvardef は同じ扱いで、 +in; は同じ扱いです。つまり +

+
+   let x = E in E
+   var x = E in E
+   def x = E in E
+   let x = E ; E
+   var x = E ; E
+   def x = E ; E
+
+

+以上のどれも同じ意味なので、なんとなく関数型っぽく書きたい気分の日は let in を、 +手続き型っぽく書きたい気分の日は var ; を使うとよいでしょう。 +

+

+関数を宣言するときは、funλ を省略できます。 +以下の書き換えが行われます。 +

+
+   def f( ARGS ) { E }; E   ⇒   def f = fun(ARGS){E}; E
+
+

+他に、もっと手続き型っぽくための書き換え色々 +

+
+   fun () { E; E; E      }   ⇒   fun () { let _ = E in let _ = E in E }
+   fun () { var x = 100  }   ⇒   fun () { var x = 100; x }
+   fun () { var x = 100; }   ⇒   fun () { var x = 100; x }
+   fun () { }                ⇒   fun () { "(empty function body)" }
+
+

+中身が空の関数に何を返させるかは適当です。今はとりあえず適当に文字列返してます。 +

+)) + +$(SECTION 変数のスコープ規則, $(SECBODY +

+NOTE: Theres no "let rec" syntax, but still recursive definition works + def f(x) { if(x==0){1}else{x*f(x-1)} } in f(10) #=> 3628800 + yet still the code below also works + def x=21 in def x=x+x in x #=> 42. + The internal scoping mechanism is a little tricky (this is for coping with + the "layer" feature explained below), but I hope that it works as everyone + expects in most cases, as long as you don't use the same-name-variables heavily :). +

+)) + +)) + + + + +$(SECTION Basic Features, $(SECBODY +

+特に特徴的でもない部分を簡単にまとめ。 +

+ +

+静的型システムがないのは意図的ですが、破壊的代入がないのは、単に実装がめんどかっただけなので、 +今後何か増えるかもしれません。増えないかもしれません。 +

+$(DDOC_MEMBERS +$(SECTION データ型, $(SECBODY +

+以下のデータ型があります。 +

+ +

+関数はいわゆる「クロージャ」です。静的スコープで外側の環境にアクセスできます。 +テーブルはいわゆるプロトタイプチェーンを持っていて、 +自分にないフィールドの場合は親に問い合わせが行く感じになっていますが、 +フィールドの書き換えがないので、これは特に意味ないかもしれない…。 +

+)) +$(SECTION パターンマッチ, $(SECBODY +pattern matching is also available. Here is an example. + + def adjSum(lst) + { + case( lst ) + when( {car:x, cdr:{car: y, cdr:z}} ) { {car: x+y, cdr: adjSum(z)} } + when( {car:x, cdr:{}} ) { {car: x, cdr: {}} } + when( {} ) { {} } + }; + +It is expanded to a sequence of if-then-elses prefering the first-match. +Note that {a: _} pattern matches all the tables that have the .a field. +It also matches to {a: 123, b: 456} having extra .b field. So, changing the +order of "when"s in the above code changes the behavior. +)) +) +)) + + + + + +$(SECTION Layers, $(SECBODY +
+[Layers :: Overview]
+
+  Polemy's runtime environment has many "layer"s.
+  Usual execution run in the @value layer.
+
+    >> 1 + 2
+    3
+    >> @value( 1 + 2 )
+    3
+
+  Here you can see that @LayerName( Expression ) executes the inner Expression in
+  the @LayerName layer. Other than @value, one other predefined layer exists: @macro.
+
+    >> @macro( 1+2 )
+    {pos@value:{lineno@value:3, column@value:9, filename@value:},
+      is@value:app,
+    args@value:{car@value:{pos@value:{lineno@value:3, column@value:9, filename@value:},
+                            is@value:int,
+                          data@value:1},
+                cdr@value:{
+                  car@value:{pos@value:{lineno@value:3, column@value:11, filename@value:},
+                              is@value:int,
+                            data@value:2},
+                  cdr@value:{}}},
+     fun@value:{pos@value:{lineno@value:3, column@value:10, filename@value:},
+                 is@value:var,
+               name@value:+}}
+
+  (Sorry, this pretty printing is not available on the actual interpreter...)
+  This evaluates the expression 1+2 in the @macro layer. In this layer, the meaning of
+  the program is its abstract syntax tree.
+
+  You can interleave layers.
+  The root node of the abstract syntax tree is function "app"lication.
+
+    >> @value(@macro( 1+2 ).is)
+    app
+
+
+
+[Layers :: Defining a new layer]
+
+  To define a new layer, you should first tell how to "lift" existing values two the new layer.
+  Let us define the "@type" layer, where the meaning of programs is their static type.
+
+    >> @@type = fun(x) {
+    >>   if( _isint(x) ) { "int" } else {
+    >>   if( _isfun(x) ) { x } else { "unknown" } }
+    >> }
+    (Note: polemy REPL may warn some exception here but please ignore)
+
+  For simplicity, I here deal only with integers.
+  _isint is a primitive function of Polemy that checks the dynamic type of a value.
+  For function, leaving it untouched works well for almost all layers.
+
+    >> @type( 1 )
+    int
+    >> @type( 2 )
+    int
+    >> @type( "foo" )
+    unknown
+
+  Fine! Let's try to type 1+2.
+
+    >> @type( 1 + 2 )
+    ...\value.d(119): [:6:8] only @value layer can call native function
+
+  Note that the behavior of this program is
+    - run 1+2 in the @type layer
+  and NOT
+    - run 1+2 in @value and obtain 3 and run 3 in the @type.
+  The problem is, the variable "+" is defined only in the @value layer.
+  To carry out computation in the @type layer. We need to define it also
+  in the @type layer.
+
+  To define some variable in a specific layer, use @LayerName in place of
+  (let|var|def)s.
+
+    >> let x = 2
+    >> @value x = 2
+    >> @type x = "int"
+    >> @hoge x = "fuga"
+
+  For "+", do it like this.
+
+    >> @type "+" = fun(x,y) {@value(
+    >>   if( @type(x)=="int" && @type(y)=="int" ) { "int" } else { "typeerror" }
+    >> )}
+    polemy.value.native!(IntValue,IntValue,IntValue).native.__anonclass24
+
+  It is just computing the return type from the input type.
+  Not here that the intended "meaning" of if-then-else is the runtime-branching,
+  and the meaning of "==" is the value-comparison. These are the @value layer
+  behavior. So we have defined the function body inside @value layer.
+  But when we refer the variables x and y, we need its @type layer meaning.
+  Hence we use @type() there.
+
+  Now we get it.
+
+    >> @type( 1 + 2 )
+    int
+
+  Well, but do we have to define the @type layer meaning for every variables???
+  No. After you defined @type "+", you'll automatically get the following:
+
+    >> def double(x) { x + x }
+    (function:17e4740:1789720)
+
+    >> @type( double(123) )
+    int
+
+  Every user-defined functions are automatically "lift"ed to the appropriate layer.
+  Only primitive functions like "+" requires @yourNewLayer annotation.
+
+
+
+[Layers :: neutral-layer]
+
+  let|var|def is to define a variable in the "current" layer.
+  Not necessary to the @value layer.
+
+    >> @value( let x = 1 in @value(x) )
+    1
+
+    >> @macro( let x = 1 in @value(x) )
+    polemy.failure.RuntimeException: [:14:29] variable x not found
+
+    >> @macro( let x = 1 in @macro(x) )
+    {pos@value:{lineno@value:15, ...
+
+
+
+[Layers :: Layered-Parameters]
+
+    >> def foo(x @macro @value) { {fst: x, snd: @macro(x)} }
+    (function:1730360:1789720)
+
+  If you annotate function parameters by @LayerNames, when you invoke the function...
+
+    >> foo(1+2)
+    {snd@value: {pos@value:{lineno@value:17, column@value:5, filename@value:},
+                  is@value:app, arg@value:{...
+    /fst@value:3
+    /}
+
+  its corresponding arguments are evaluated in the layer and passed to it.
+  If you specify multiple layers, the argument expression is run multiple times.
+  If you do not specify any layer for a parameter, it works in the neutral layer.
+
+
+
+[@macro layer]
+
+   When function is invoked, it first run in the @macro layer, and after that,
+   it run in the neutral layer. Here is an example.
+
+     >> @macro twice(x) { x; x }
+     >> def f() { twice(print("Hello")); 999 }
+     (function:173b6a0:1789720)
+     >> f()
+     Hello
+     Hello
+     999
+
+   When the interpreter evaluates f(), it first executes
+     "twice(print("Hello")); 999"
+   in the @macro layer. Basically what it does is to just construct its syntax tree.
+   But, since we have defined the "twice" function in the @macro layer, it is
+   execute as a function. Resulting syntax tree is
+     "print("Hello"); print("Hello"); 999"
+   and this is executed on the neutral (in this example, @value) layer.
+   This is the reason why you see two "Hello"s.
+
+
+
+      [[limitations]]
+
+   This @macro layer is a very primitive one, and not a perfect macro language.
+   Two major limitations are seen in the following "it" example.
+
+     >> @macro LetItBe(x, y) { let it = x in y };
+
+   The variable name is not hygenic, and so without any effort, the syntax tree "y"
+   can access the outer variable "it".
+
+     >> def foo() { LetItBe( 1+2+3, it*it ) }
+     >> foo()
+     36
+
+   Of course, this is not just a limitation; it can sometimes allow us to write
+   many interesting macros.
+
+   The other problem is that the macro expansion is only done at function startup.
+   So 
+
+     >> LetItBe( 1+2+3, it*it )
+     ...\value.d(173): [:24:1] variable LetItBe is not set in layer @value
+
+   you cannot directly use the macro in the same scope as the definition.
+   You need to wrap it up in a function (like the foo() in the above example).
+
+
+
+      [[quote and unquote]]
+
+   Here is more involved example of code genration.
+   From "x", it generates "x*x*x*x*x*x*x*x*x*x".
+
+     @macro pow10(x) {
+       @value(
+         def pow(x, n) {
+           if( n == 1 ) { x }
+           else {
+             @macro( @value(x) * @value(pow(x,n-1)) )
+           }
+         }
+         in
+           pow(@macro(x),10)
+       )
+     };
+
+   Here, x is a syntax tree but n is an actual integer. If you read carefully,
+   you should get what is going on. Basically, @macro can be considered like
+   quasiquoting and @value to be an escape from it.
+
+)) + + +$(SECTION Built-in Primitives, $(SECBODY +

+組み込み関数・変数の一覧。 +

+$(DDOC_MEMBERS + +$(SECTION テーブル操作, $(SECBODY + $(TABLE + $(TR $(TH {}) $(TD ()) $(TD 空のテーブルを作る)) + $(TR $(TH .) $(TD (t, s)) $(TD テーブル t の名前 s のフィールドの値を取得。なければ undefined)) + $(TR $(TH .?) $(TD (t, s)) $(TD テーブル t に名前 s のフィールドがあれば 1、なければ 0)) + $(TR $(TH .=) $(TD (t, s, v)) $(TD テーブル t を親に持ち、名前 s のフィールドに v が入ったテーブルを作る)) + ) +)) +
+ +$(SECTION 制御フロー, $(SECBODY + $(TABLE + $(TR $(TH if) $(TD (n, ft, fe)) $(TD n が非 0 なら ft()、0 なら fe() を実行)) + ) +)) +
+ +$(SECTION 演算, $(SECBODY + $(TABLE + $(TR $(TH +) $(TD (n, m)) $(TD 整数 n と整数 m を足して返す)) + $(TR $(TH -) $(TD (n, m)) $(TD 整数の引き算)) + $(TR $(TH *) $(TD (n, m)) $(TD 整数の掛け算)) + $(TR $(TH /) $(TD (n, m)) $(TD 整数の割り算)) + $(TR $(TH %) $(TD (n, m)) $(TD 整数の剰余)) + $(TR $(TH &&) $(TD (n, m)) $(TD 整数 n と m が両方非 0 なら 1、それ以外では 0)) + $(TR $(TH ||) $(TD (n, m)) $(TD 整数 n と m がどちらか非 0 なら 1、それ以外では 0)) + $(TR $(TH ~) $(TD (a, b)) $(TD a と b を文字列化して結合)) + $(TR $(TH <) $(TD (a, b)) $(TD a と b を比較)) + $(TR $(TH <=) $(TD (a, b)) $(TD a と b を比較)) + $(TR $(TH >) $(TD (a, b)) $(TD a と b を比較)) + $(TR $(TH >=) $(TD (a, b)) $(TD a と b を比較)) + $(TR $(TH ==) $(TD (a, b)) $(TD a と b を比較)) + $(TR $(TH !=) $(TD (a, b)) $(TD a と b を比較)) + ) +

+注意点として、作者の趣味の問題で、&&|| は short-circuit 評価をしません。 +整数演算の種類が少ないのは、D 言語の std.bigint がビット演算などをサポートしてないためです。 +文字列が結合しかできないのは、単に手抜きです。 +

+)) + +$(SECTION 外部とのやりとり, $(SECBODY + $(TABLE + $(TR $(TH print) $(TD (a)) $(TD a を文字列化標準出力に改行付きで表示)) + $(TR $(TH argv) $(TD ) $(TD スクリプトに渡された引数文字列のconsリスト)) + ) +)) +
+ +$(SECTION データ型判定, $(SECBODY + $(TABLE + $(TR $(TH _isint) $(TD (a)) $(TD a が整数なら 1、でなければ 0)) + $(TR $(TH _isstr) $(TD (a)) $(TD a が文字列なら 1、でなければ 0)) + $(TR $(TH _isfun) $(TD (a)) $(TD a が関数なら 1、でなければ 0)) + $(TR $(TH _istable) $(TD (a)) $(TD a がテーブルなら 1、でなければ 0)) + $(TR $(TH _isundefined) $(TD (a)) $(TD a が未定義値なら 1、でなければ 0)) + ) +)) +) +)) + +) +Macros: + TITLE=Polemy Reference Manual + DOCFILENAME=index.html + SECTION=$(DDOC_DECL $(DDOC_PSYMBOL $1)) $(DDOC_DECL_DD $2) + SECBODY=$0 Index: polemy/eval.d ================================================================== --- polemy/eval.d +++ polemy/eval.d @@ -247,38 +247,29 @@ Value createNewFunction(Fun e, Table ctx) { class UserDefinedFunValue : FunValue { - Fun ast; - Table defCtx; + Fun ast; + Table defCtx; override const(Parameter[]) params() { return ast.params; } - override Table definitionContext() { return defCtx; } + override Table definitionContext() { return defCtx; } this(Fun ast, Table defCtx) { this.ast=ast; this.defCtx=defCtx; } override string toString() const { return sprintf!"(function:%x:%x)"(cast(void*)ast, cast(void*)defCtx); } - override bool opEquals(Object rhs_) const /// member-by-member equality - { - if( auto rhs = cast(typeof(this))rhs_ ) - return this.ast==rhs.ast && this.defCtx==rhs.defCtx; - assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_))); - } - override hash_t toHash() const /// member-by-member hash - { - return typeid(this.ast).getHash(&this.ast) + typeid(this.defCtx).getHash(&this.defCtx); + override int opCmp(Object rhs) { + if(auto r = cast(UserDefinedFunValue)rhs) { + if(auto i = this.ast.opCmp(r.ast)) + return i; + return this.defCtx.opCmp(r.defCtx); + } + if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r)); + throw genex!RuntimeException("comparison with value and something other"); } - override int opCmp(Object rhs_) /// member-by-member compare - { - if( auto rhs = cast(typeof(this))rhs_ ) - { - if(auto i = this.ast.opCmp(rhs.ast)) - return i; - return this.defCtx.opCmp(rhs.defCtx); - } - assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_))); - } + mixin SimpleToHash; + AST afterMacroAST; override Value invoke(Layer lay, Table ctx, LexPosition pos) { if( isASTLayer(lay) ) return eval(ast.funbody, lay, ctx); if( afterMacroAST is null ) @@ -288,12 +279,10 @@ return va[0]; } else return eval(afterMacroAST, lay, ctx); } - - AST afterMacroAST; } return new UserDefinedFunValue(e,ctx); } public: @@ -307,11 +296,11 @@ override string toString() { return sprintf!"(native:%x)"(dg.funcptr); } override int opCmp(Object rhs) { if(auto r = cast(NativeFunValue)rhs) return typeid(typeof(dg)).compare(&dg,&r.dg); if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r)); - throw genex!RuntimeException(LexPosition.dummy, "comparison with value and somithing other"); + throw genex!RuntimeException(LexPosition.dummy, "comparison with value and something other"); } mixin SimpleToHash; R delegate(T) dg; Parameter[] params_data; Index: polemy/failure.d ================================================================== --- polemy/failure.d +++ polemy/failure.d @@ -1,100 +1,100 @@ -/** - * Authors: k.inaba - * License: NYSL 0.9982 http://www.kmonos.net/nysl/ - * - * Error Information for Polemy Programming Language - */ -module polemy.failure; -import polemy._common; - -/// Represents a position in source codes - -alias immutable(LexPosition_t) LexPosition; - -/// Represents a position in source codes - -class LexPosition_t -{ - immutable string filename; /// name of the source file - immutable int lineno; /// 1-origin - immutable int column; /// 1-origin - - mixin SimpleClass; - override string toString() const - { return sprintf!("%s:%d:%d")(filename, lineno, column); } - static LexPosition dummy; - static this(){ dummy = new LexPosition("",0,0); } -} - -unittest -{ - auto p = new LexPosition("hello.cpp", 123, 45); - - assert_eq( p.filename, "hello.cpp" ); - assert_eq( p.lineno, 123 ); - assert_eq( p.column, 45 ); - assert_eq( text(p), "hello.cpp:123:45" ); - - assert( !__traits(compiles, new LexPosition) ); - assert( !__traits(compiles, p.filename="foo") ); - assert( !__traits(compiles, p.lineno =789) ); - assert( !__traits(compiles, p.column =222) ); - - auto q = new LexPosition("hello.cpp", 123, 46); - assert_lt( p, q ); - assert_ne( p, q ); -} - -/*mixin*/ -template ExceptionWithPosition() -{ - LexPosition pos; - this( LexPosition pos, string msg, string file=null, size_t line=0, Throwable next=null ) - { - string fullmsg = pos is null ? sprintf!("\n[??] %s")(msg) - : sprintf!("\n[%s] %s")(pos, msg); - for(int i=0; i",0,0); } +} + +unittest +{ + auto p = new LexPosition("hello.cpp", 123, 45); + + assert_eq( p.filename, "hello.cpp" ); + assert_eq( p.lineno, 123 ); + assert_eq( p.column, 45 ); + assert_eq( text(p), "hello.cpp:123:45" ); + + assert( !__traits(compiles, new LexPosition) ); + assert( !__traits(compiles, p.filename="foo") ); + assert( !__traits(compiles, p.lineno =789) ); + assert( !__traits(compiles, p.column =222) ); + + auto q = new LexPosition("hello.cpp", 123, 46); + assert_lt( p, q ); + assert_ne( p, q ); +} + +/*mixin*/ +template ExceptionWithPosition() +{ + LexPosition pos; + this( LexPosition pos, string msg, string file=null, size_t line=0, Throwable next=null ) + { + string fullmsg = pos is null ? sprintf!("\n[??] %s")(msg) + : sprintf!("\n[%s] %s")(pos, msg); + for(int i=0; i"; } override int opCmp(Object rhs) { if(auto r = cast(StrValue)rhs) return 0; if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r)); - throw genex!RuntimeException(LexPosition.dummy, "comparison with value and somithing other"); + throw genex!RuntimeException("comparison with value and somithing other"); } mixin SimpleToHash; } /// Index: readme.txt ================================================================== --- readme.txt +++ readme.txt @@ -1,14 +1,12 @@ ----------------------------------------------------------------------------- Polemy 0.1.0 by k.inaba (www.kmonos.net) - Nov 20, 2010 + Nov 24, 2010 ----------------------------------------------------------------------------- - - -<> +[How to Build] - Install DMD http://www.digitalmars.com/d/2.0/changelog.html Version 2.050 is recommended. Older or newer version may not work. @@ -19,11 +17,11 @@ Then you will get the executable "polemy" in the "bin" directory. -<> +[License] d2stacktrace/* is written by Benjamin Thaut and licensed under 2-clause BSD License. See http://3d.benjamin-thaut.de/?p=15 for the detail. @@ -37,11 +35,11 @@ All the other parts are written by Kazuhiro Inaba and licensed under NYSL 0.9982 ( http://www.kmonos.net/nysl/ ). -<> +[How to Use] > polemy starts REPL > polemy foo.pmy @@ -53,341 +51,9 @@ > polemy -l foo.pmy -l bar.pmy buz.pmy executes foo.pmy, bar.bmy, and then buz.pmy -<> - - Comment is "# ... \n" - - E ::= - // declaration - | ("var"|"let"|"def"|LAYER) ID "=" E (";"|"in") E - | ("var"|"let"|"def"|LAYER) ID "(" PARAMS ")" "{" E "}" (";"|"in") E - | ("var"|"let"|"def"|LAYER) ID "=" E - | ("var"|"let"|"def"|LAYER) ID "(" PARAMS ")" "{" E "}" - // literal - | INTEGER - | STRING - | "{" ENTRYS "}" // table - | "fun" "(" PARAMS ")" "{" E "}" // anonymous function - // function call - | E "(" ARGS")" - where ARGS ::= E "," ... "," E - PARAMS ::= ID LAYER* "," ... "," ID LAYER* - ENTRYS ::= ID ":" E "," ... "," ID ":" E - ID ::= 'a-zA-Z0-9_...'+ - LAYER ::= "@" ID - // operators - | "(" E ")" - | E "." ID // table field access - | E ".?" ID // table field existence check - | E "{" ENTRYS "}" // table extend (pure functionally) - | E BINOP E - | "if" "(" E ")" "{" E "}" - | "if" "(" E ")" "{" E "}" "else "{" E "}" - // layered exec - | LAYER "(" E ")" +[Language Reference] -The following are actually rewritten to function calls: - - - if (E) then{E} else{E} ==> if( E, fun(){E}, fun(){E} ) - - E BINOP E ==> BINOP(E, E) - - E.ID ==> . (E, ID) - - E.?ID ==> .?(E, ID) - - {} ==> {}() - - { ENTRIES } ==> {}{ ENTRIES } - - E {ID:E, ...} ==> (.=(E, ID, E)) { ... } - -Several styles of variable declaration can be used: - - - fun(x){ fun(y){x} } # K-combinator - - fun(x){ let f = fun(y){x} in f } # let-in style - - fun(x){ var f = fun(y){x}; f } # var-; style - - fun(x){ def f = fun(y){x} in f } # you can use any combination of (let|var|def)-(;|in) - - fun(x){ def f(y){x} in f } # syntax sugar for function declaration - - fun(x){ let f(y){x}; f } # this is also ok - - fun(x){ var f(y){x} } # omitting (;|in) returns the last declared object directly - - fun(x,y){x} #< this is not equal to the above ones. functions are no curried. - -NOTE: Theres no "let rec" syntax, but still recursive definition works - def f(x) { if(x==0){1}else{x*f(x-1)} } in f(10) #=> 3628800 - yet still the code below also works - def x=21 in def x=x+x in x #=> 42. - The internal scoping mechanism is a little tricky (this is for coping with - the "layer" feature explained below), but I hope that it works as everyone - expects in most cases, as long as you don't use the same-name-variables heavily :). - -(Experimental) pattern matching is also available. Here is an example. - - def adjSum(lst) - { - case( lst ) - when( {car:x, cdr:{car: y, cdr:z}} ) { {car: x+y, cdr: adjSum(z)} } - when( {car:x, cdr:{}} ) { {car: x, cdr: {}} } - when( {} ) { {} } - }; - -It is expanded to a sequence of if-then-elses prefering the first-match. -Note that {a: _} pattern matches all the tables that have the .a field. -It also matches to {a: 123, b: 456} having extra .b field. So, changing the -order of "when"s in the above code changes the behavior. - - - - -<> - - Polemy is an untyped functional programming language that has - - integers: 0, 123, 456666666666666666666666666666666666666789, ... - - strings: "hello, world!\n", ... - - tables: {car: 1, cdr: {car: 2, cdr: {}}} - - functions: fun(x){x+1} - as primitive datatypes. Functions capture lexical closures. - It is almost 'pure' (except the primitve function "print" and some - trick inside scoping mechanisms). - - -<> - - Polemy's runtime environment has many "layer"s. - Usual execution run in the @value layer. - - >> 1 + 2 - 3 - >> @value( 1 + 2 ) - 3 - - Here you can see that @LayerName( Expression ) executes the inner Expression in - the @LayerName layer. Other than @value, one other predefined layer exists: @macro. - - >> @macro( 1+2 ) - {pos@value:{lineno@value:3, column@value:9, filename@value:}, - is@value:app, - args@value:{car@value:{pos@value:{lineno@value:3, column@value:9, filename@value:}, - is@value:int, - data@value:1}, - cdr@value:{ - car@value:{pos@value:{lineno@value:3, column@value:11, filename@value:}, - is@value:int, - data@value:2}, - cdr@value:{}}}, - fun@value:{pos@value:{lineno@value:3, column@value:10, filename@value:}, - is@value:var, - name@value:+}} - - (Sorry, this pretty printing is not available on the actual interpreter...) - This evaluates the expression 1+2 in the @macro layer. In this layer, the meaning of - the program is its abstract syntax tree. - - You can interleave layers. - The root node of the abstract syntax tree is function "app"lication. - - >> @value(@macro( 1+2 ).is) - app - - - -<> - - To define a new layer, you should first tell how to "lift" existing values two the new layer. - Let us define the "@type" layer, where the meaning of programs is their static type. - - >> @@type = fun(x) { - >> if( _isint(x) ) { "int" } else { - >> if( _isfun(x) ) { x } else { "unknown" } } - >> } - (Note: polemy REPL may warn some exception here but please ignore) - - For simplicity, I here deal only with integers. - _isint is a primitive function of Polemy that checks the dynamic type of a value. - For function, leaving it untouched works well for almost all layers. - - >> @type( 1 ) - int - >> @type( 2 ) - int - >> @type( "foo" ) - unknown - - Fine! Let's try to type 1+2. - - >> @type( 1 + 2 ) - ...\value.d(119): [:6:8] only @value layer can call native function - - Note that the behavior of this program is - - run 1+2 in the @type layer - and NOT - - run 1+2 in @value and obtain 3 and run 3 in the @type. - The problem is, the variable "+" is defined only in the @value layer. - To carry out computation in the @type layer. We need to define it also - in the @type layer. - - To define some variable in a specific layer, use @LayerName in place of - (let|var|def)s. - - >> let x = 2 - >> @value x = 2 - >> @type x = "int" - >> @hoge x = "fuga" - - For "+", do it like this. - - >> @type "+" = fun(x,y) {@value( - >> if( @type(x)=="int" && @type(y)=="int" ) { "int" } else { "typeerror" } - >> )} - polemy.value.native!(IntValue,IntValue,IntValue).native.__anonclass24 - - It is just computing the return type from the input type. - Not here that the intended "meaning" of if-then-else is the runtime-branching, - and the meaning of "==" is the value-comparison. These are the @value layer - behavior. So we have defined the function body inside @value layer. - But when we refer the variables x and y, we need its @type layer meaning. - Hence we use @type() there. - - Now we get it. - - >> @type( 1 + 2 ) - int - - Well, but do we have to define the @type layer meaning for every variables??? - No. After you defined @type "+", you'll automatically get the following: - - >> def double(x) { x + x } - (function:17e4740:1789720) - - >> @type( double(123) ) - int - - Every user-defined functions are automatically "lift"ed to the appropriate layer. - Only primitive functions like "+" requires @yourNewLayer annotation. - - - -<> - - let|var|def is to define a variable in the "current" layer. - Not necessary to the @value layer. - - >> @value( let x = 1 in @value(x) ) - 1 - - >> @macro( let x = 1 in @value(x) ) - polemy.failure.RuntimeException: [:14:29] variable x not found - - >> @macro( let x = 1 in @macro(x) ) - {pos@value:{lineno@value:15, ... - - - -<> - - >> def foo(x @macro @value) { {fst: x, snd: @macro(x)} } - (function:1730360:1789720) - - If you annotate function parameters by @LayerNames, when you invoke the function... - - >> foo(1+2) - {snd@value: {pos@value:{lineno@value:17, column@value:5, filename@value:}, - is@value:app, arg@value:{... - /fst@value:3 - /} - - its corresponding arguments are evaluated in the layer and passed to it. - If you specify multiple layers, the argument expression is run multiple times. - If you do not specify any layer for a parameter, it works in the neutral layer. - - - -<<@macro layer>> - - When function is invoked, it first run in the @macro layer, and after that, - it run in the neutral layer. Here is an example. - - >> @macro twice(x) { x; x } - >> def f() { twice(print("Hello")); 999 } - (function:173b6a0:1789720) - >> f() - Hello - Hello - 999 - - When the interpreter evaluates f(), it first executes - "twice(print("Hello")); 999" - in the @macro layer. Basically what it does is to just construct its syntax tree. - But, since we have defined the "twice" function in the @macro layer, it is - execute as a function. Resulting syntax tree is - "print("Hello"); print("Hello"); 999" - and this is executed on the neutral (in this example, @value) layer. - This is the reason why you see two "Hello"s. - - - - [[limitations]] - - This @macro layer is a very primitive one, and not a perfect macro language. - Two major limitations are seen in the following "it" example. - - >> @macro LetItBe(x, y) { let it = x in y }; - - The variable name is not hygenic, and so without any effort, the syntax tree "y" - can access the outer variable "it". - - >> def foo() { LetItBe( 1+2+3, it*it ) } - >> foo() - 36 - - Of course, this is not just a limitation; it can sometimes allow us to write - many interesting macros. - - The other problem is that the macro expansion is only done at function startup. - So - - >> LetItBe( 1+2+3, it*it ) - ...\value.d(173): [:24:1] variable LetItBe is not set in layer @value - - you cannot directly use the macro in the same scope as the definition. - You need to wrap it up in a function (like the foo() in the above example). - - - - [[quote and unquote]] - - Here is more involved example of code genration. - From "x", it generates "x*x*x*x*x*x*x*x*x*x". - - @macro pow10(x) { - @value( - def pow(x, n) { - if( n == 1 ) { x } - else { - @macro( @value(x) * @value(pow(x,n-1)) ) - } - } - in - pow(@macro(x),10) - ) - }; - - Here, x is a syntax tree but n is an actual integer. If you read carefully, - you should get what is going on. Basically, @macro can be considered like - quasiquoting and @value to be an escape from it. - - - -<> - - {} 0-ary create-empty-table - . 2-ary table-get - .? 2-ary table-has? - .= 3-ary table-set - - if 3-ary if-then-else - - + - * / % || && 2-ary integer-operations (NOTE! no short-circuit for && and ||.) - < > <= >= == != 2-ary generic comparison - ~ 2-ary string concatenation (works also for non-string objects) - - print 1-ary print-to-stdout - - _isint _isstr _isfun _isundefined _istable 1-ary dynamic-type-test + See doc/index.html (in Japanese)