Artifact Content
Not logged in

Artifact d36ee595e22be49c2dff94a0738cd57fa361998b


     1  -----------------------------------------------------------------------------
     2    Polemy 0.1.0
     3                                                   by k.inaba (www.kmonos.net)
     4                                                                 Nov 20, 2010
     5  -----------------------------------------------------------------------------
     6  
     7  
     8  
     9  <<How to Build>>
    10  
    11    - Install DMD
    12        http://www.digitalmars.com/d/2.0/changelog.html
    13      Version 2.050 is recommended. Older or newer version may not work.
    14  
    15    - Build
    16        (for Windows) Run build.bat
    17        (for Unix) Run build.sh
    18        or use your favorite build tools upon main.d and polemy/*.d.
    19  
    20    Then you will get the executable "polemy" in the "bin" directory.
    21  
    22  
    23  
    24  <<License>>
    25  
    26    d2stacktrace/*
    27  
    28      is written by Benjamin Thaut and licensed under 2-clause BSD License.
    29      See http://3d.benjamin-thaut.de/?p=15 for the detail.
    30  
    31      (this package is used only for enabling stack-traces during printing exceptions;
    32       it is not used for release builds.)
    33  
    34    polemy/*
    35    main.d
    36  
    37      All the other parts are written by Kazuhiro Inaba and
    38      licensed under NYSL 0.9982 ( http://www.kmonos.net/nysl/ ).
    39  
    40  
    41  
    42  <<How to Use>>
    43  
    44    > polemy
    45        starts REPL
    46  
    47    > polemy foo.pmy
    48        executes foo.pmy
    49  
    50    > polemy -l foo.pmy
    51        after executing foo.pmy, starts REPL
    52  
    53    > polemy -l foo.pmy -l bar.pmy buz.pmy
    54        executes foo.pmy, bar.bmy, and then buz.pmy
    55  
    56  
    57  
    58  <<Syntax>>
    59  
    60   Comment is "# ... \n"
    61  
    62   E ::=
    63     // declaration
    64       | ("var"|"let"|"def"|LAYER) ID "=" E (";"|"in") E
    65       | ("var"|"let"|"def"|LAYER) ID "(" PARAMS ")" "{" E "}" (";"|"in") E
    66       | ("var"|"let"|"def"|LAYER) ID "=" E
    67       | ("var"|"let"|"def"|LAYER) ID "(" PARAMS ")" "{" E "}"
    68     // literal
    69       | INTEGER
    70  	 | STRING
    71  	 | "{" ENTRYS "}"                   // table
    72       | "fun" "(" PARAMS ")" "{" E "}"   // anonymous function
    73     // function call
    74       | E "(" ARGS")"
    75  	     where ARGS ::= E "," ... "," E
    76  	         PARAMS ::= ID LAYER* "," ... "," ID LAYER*
    77  			 ENTRYS ::= ID ":" E  "," ... "," ID ":" E
    78                   ID ::= 'a-zA-Z0-9_...'+
    79                LAYER ::= "@" ID
    80     // operators
    81       | "(" E ")"
    82  	 | E "." ID           // table field access
    83  	 | E ".?" ID          // table field existence check
    84       | E "{" ENTRYS "}"   // table extend (pure functionally)
    85       | E BINOP E
    86       | "if" "(" E ")" "{" E "}"
    87       | "if" "(" E ")" "{" E "}" "else "{" E "}"
    88     // layered exec
    89       | LAYER "(" E ")"
    90  
    91  The following are actually rewritten to function calls:
    92  
    93    - if (E) then{E} else{E} ==> if( E, fun(){E}, fun(){E} )
    94    - E BINOP E              ==> BINOP(E, E)
    95    - E.ID                   ==> . (E, ID)
    96    - E.?ID                  ==> .?(E, ID)
    97    - {}                     ==> {}()
    98    - { ENTRIES }            ==> {}{ ENTRIES }
    99    - E {ID:E, ...}          ==> (.=(E, ID, E)) { ... }
   100  
   101  Several styles of variable declaration can be used:
   102  
   103    - fun(x){ fun(y){x} }               # K-combinator
   104    - fun(x){ let f = fun(y){x} in f }  # let-in style
   105    - fun(x){ var f = fun(y){x}; f }    # var-;  style
   106    - fun(x){ def f = fun(y){x} in f }  # you can use any combination of (let|var|def)-(;|in)
   107    - fun(x){ def f(y){x} in f } # syntax sugar for function declaration
   108    - fun(x){ let f(y){x}; f }   # this is also ok
   109    - fun(x){ var f(y){x} }      # omitting (;|in) returns the last declared object directly
   110    - fun(x,y){x} #< this is not equal to the above ones. functions are no curried.
   111  
   112  NOTE: Theres no "let rec" syntax, but still recursive definition works
   113      def f(x) { if(x==0){1}else{x*f(x-1)} } in f(10)  #=> 3628800
   114    yet still the code below also works
   115      def x=21 in def x=x+x in x  #=> 42.
   116    The internal scoping mechanism is a little tricky (this is for coping with
   117    the "layer" feature explained below), but I hope that it works as everyone
   118    expects in most cases, as long as you don't use the same-name-variables heavily :).
   119  
   120  (Experimental) pattern matching is also available. Here is an example.
   121  
   122    def adjSum(lst)
   123    {
   124      case( lst )
   125        when( {car:x, cdr:{car: y, cdr:z}} ) { {car: x+y, cdr: adjSum(z)} }
   126        when( {car:x, cdr:{}} ) { {car: x, cdr: {}} }
   127        when( {} ) { {} }
   128    };
   129  
   130  It is expanded to a sequence of if-then-elses prefering the first-match. 
   131  Note that {a: _} pattern matches all the tables that have the .a field.
   132  It also matches to {a: 123, b: 456} having extra .b field. So, changing the
   133  order of "when"s in the above code changes the behavior.
   134  
   135  
   136  
   137  
   138  <<Basic Features>>
   139  
   140    Polemy is an untyped functional programming language that has
   141     - integers:   0, 123, 456666666666666666666666666666666666666789, ...
   142     - strings:    "hello, world!\n", ...
   143     - tables:     {car: 1, cdr: {car: 2, cdr: {}}}
   144     - functions:  fun(x){x+1}
   145    as primitive datatypes. Functions capture lexical closures.
   146    It is almost 'pure' (except the primitve function "print" and some
   147    trick inside scoping mechanisms).
   148  
   149  
   150  <<Layers :: Overview>>
   151  
   152    Polemy's runtime environment has many "layer"s.
   153    Usual execution run in the @value layer.
   154  
   155      >> 1 + 2
   156      3
   157      >> @value( 1 + 2 )
   158      3
   159  
   160    Here you can see that @LayerName( Expression ) executes the inner Expression in
   161    the @LayerName layer. Other than @value, one other predefined layer exists: @macro.
   162  
   163      >> @macro( 1+2 )
   164      {pos@value:{lineno@value:3, column@value:9, filename@value:<REPL>},
   165        is@value:app,
   166      args@value:{car@value:{pos@value:{lineno@value:3, column@value:9, filename@value:<REPL>},
   167                              is@value:int,
   168                            data@value:1},
   169                  cdr@value:{
   170                    car@value:{pos@value:{lineno@value:3, column@value:11, filename@value:<REPL>},
   171                                is@value:int,
   172                              data@value:2},
   173                    cdr@value:{}}},
   174       fun@value:{pos@value:{lineno@value:3, column@value:10, filename@value:<REPL>},
   175                   is@value:var,
   176                 name@value:+}}
   177  
   178    (Sorry, this pretty printing is not available on the actual interpreter...)
   179    This evaluates the expression 1+2 in the @macro layer. In this layer, the meaning of
   180    the program is its abstract syntax tree.
   181  
   182    You can interleave layers.
   183    The root node of the abstract syntax tree is function "app"lication.
   184  
   185      >> @value(@macro( 1+2 ).is)
   186      app
   187  
   188  
   189  
   190  <<Layers :: Defining a new layer>>
   191  
   192    To define a new layer, you should first tell how to "lift" existing values two the new layer.
   193    Let us define the "@type" layer, where the meaning of programs is their static type.
   194  
   195      >> @@type = fun(x) {
   196      >>   if( _isint(x) ) { "int" } else {
   197      >>   if( _isfun(x) ) { x } else { "unknown" } }
   198      >> }
   199      (Note: polemy REPL may warn some exception here but please ignore)
   200  
   201    For simplicity, I here deal only with integers.
   202    _isint is a primitive function of Polemy that checks the dynamic type of a value.
   203    For function, leaving it untouched works well for almost all layers.
   204  
   205      >> @type( 1 )
   206      int
   207      >> @type( 2 )
   208      int
   209      >> @type( "foo" )
   210      unknown
   211  
   212    Fine! Let's try to type 1+2.
   213  
   214      >> @type( 1 + 2 )
   215      ...\value.d(119): [<REPL>:6:8] only @value layer can call native function
   216  
   217    Note that the behavior of this program is
   218      - run 1+2 in the @type layer
   219    and NOT
   220      - run 1+2 in @value and obtain 3 and run 3 in the @type.
   221    The problem is, the variable "+" is defined only in the @value layer.
   222    To carry out computation in the @type layer. We need to define it also
   223    in the @type layer.
   224  
   225    To define some variable in a specific layer, use @LayerName in place of
   226    (let|var|def)s.
   227  
   228      >> let x = 2
   229      >> @value x = 2
   230      >> @type x = "int"
   231      >> @hoge x = "fuga"
   232  
   233    For "+", do it like this.
   234  
   235      >> @type "+" = fun(x,y) {@value(
   236      >>   if( @type(x)=="int" && @type(y)=="int" ) { "int" } else { "typeerror" }
   237      >> )}
   238      polemy.value.native!(IntValue,IntValue,IntValue).native.__anonclass24
   239  
   240    It is just computing the return type from the input type.
   241    Not here that the intended "meaning" of if-then-else is the runtime-branching,
   242    and the meaning of "==" is the value-comparison. These are the @value layer
   243    behavior. So we have defined the function body inside @value layer.
   244    But when we refer the variables x and y, we need its @type layer meaning.
   245    Hence we use @type() there.
   246  
   247    Now we get it.
   248  
   249      >> @type( 1 + 2 )
   250      int
   251  
   252    Well, but do we have to define the @type layer meaning for every variables???
   253    No. After you defined @type "+", you'll automatically get the following:
   254  
   255      >> def double(x) { x + x }
   256      (function:17e4740:1789720)
   257  
   258      >> @type( double(123) )
   259      int
   260  
   261    Every user-defined functions are automatically "lift"ed to the appropriate layer.
   262    Only primitive functions like "+" requires @yourNewLayer annotation.
   263  
   264  
   265  
   266  <<Layers :: neutral-layer>>
   267  
   268    let|var|def is to define a variable in the "current" layer.
   269    Not necessary to the @value layer.
   270  
   271      >> @value( let x = 1 in @value(x) )
   272      1
   273  
   274      >> @macro( let x = 1 in @value(x) )
   275      polemy.failure.RuntimeException: [<REPL>:14:29] variable x not found
   276  
   277      >> @macro( let x = 1 in @macro(x) )
   278      {pos@value:{lineno@value:15, ...
   279  
   280  
   281  
   282  <<Layers :: Layered-Parameters>>
   283  
   284      >> def foo(x @macro @value) { {fst: x, snd: @macro(x)} }
   285      (function:1730360:1789720)
   286  
   287    If you annotate function parameters by @LayerNames, when you invoke the function...
   288  
   289      >> foo(1+2)
   290      {snd@value: {pos@value:{lineno@value:17, column@value:5, filename@value:<REPL>},
   291                    is@value:app, arg@value:{...
   292      /fst@value:3
   293      /}
   294  
   295    its corresponding arguments are evaluated in the layer and passed to it.
   296    If you specify multiple layers, the argument expression is run multiple times.
   297    If you do not specify any layer for a parameter, it works in the neutral layer.
   298  
   299  
   300  
   301  <<@macro layer>>
   302  
   303     When function is invoked, it first run in the @macro layer, and after that,
   304     it run in the neutral layer. Here is an example.
   305  
   306       >> @macro twice(x) { x; x }
   307       >> def f() { twice(print("Hello")); 999 }
   308       (function:173b6a0:1789720)
   309       >> f()
   310       Hello
   311       Hello
   312       999
   313  
   314     When the interpreter evaluates f(), it first executes
   315       "twice(print("Hello")); 999"
   316     in the @macro layer. Basically what it does is to just construct its syntax tree.
   317     But, since we have defined the "twice" function in the @macro layer, it is
   318     execute as a function. Resulting syntax tree is
   319       "print("Hello"); print("Hello"); 999"
   320     and this is executed on the neutral (in this example, @value) layer.
   321     This is the reason why you see two "Hello"s.
   322  
   323  
   324  
   325        [[limitations]]
   326  
   327     This @macro layer is a very primitive one, and not a perfect macro language.
   328     Two major limitations are seen in the following "it" example.
   329  
   330       >> @macro LetItBe(x, y) { let it = x in y };
   331  
   332     The variable name is not hygenic, and so without any effort, the syntax tree "y"
   333     can access the outer variable "it".
   334  
   335       >> def foo() { LetItBe( 1+2+3, it*it ) }
   336       >> foo()
   337       36
   338  
   339     Of course, this is not just a limitation; it can sometimes allow us to write
   340     many interesting macros.
   341  
   342     The other problem is that the macro expansion is only done at function startup.
   343     So 
   344  
   345       >> LetItBe( 1+2+3, it*it )
   346       ...\value.d(173): [<REPL>:24:1] variable LetItBe is not set in layer @value
   347  
   348     you cannot directly use the macro in the same scope as the definition.
   349     You need to wrap it up in a function (like the foo() in the above example).
   350  
   351  
   352  
   353        [[quote and unquote]]
   354  
   355     Here is more involved example of code genration.
   356     From "x", it generates "x*x*x*x*x*x*x*x*x*x".
   357  
   358       @macro pow10(x) {
   359         @value(
   360           def pow(x, n) {
   361             if( n == 1 ) { x }
   362             else {
   363               @macro( @value(x) * @value(pow(x,n-1)) )
   364             }
   365           }
   366           in
   367             pow(@macro(x),10)
   368         )
   369       };
   370  
   371     Here, x is a syntax tree but n is an actual integer. If you read carefully,
   372     you should get what is going on. Basically, @macro can be considered like
   373     quasiquoting and @value to be an escape from it.
   374  
   375  
   376  
   377  <<Primitives>>
   378  
   379    {}  0-ary  create-empty-table
   380    .   2-ary  table-get
   381    .?  2-ary  table-has?
   382    .=  3-ary  table-set
   383  
   384    if  3-ary  if-then-else
   385  
   386    + - * / % || &&    2-ary  integer-operations (no short-circuit!)
   387    < > <= >= == !=    2-ary  generic comparison
   388  
   389    print 1-ary print-to-stdout
   390  
   391    _isint _isstr _isfun _isundefined _istable  1-ary  dynamic-type-test
   392