Artifact Content
Not logged in

Artifact d36ee595e22be49c2dff94a0738cd57fa361998b


-----------------------------------------------------------------------------
  Polemy 0.1.0
                                                 by k.inaba (www.kmonos.net)
                                                               Nov 20, 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.

  - Build
      (for Windows) Run build.bat
      (for Unix) Run build.sh
      or use your favorite build tools upon main.d and polemy/*.d.

  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.

    (this package is used only for enabling stack-traces during printing exceptions;
     it is not used for release builds.)

  polemy/*
  main.d

    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
      executes foo.pmy

  > polemy -l foo.pmy
      after executing foo.pmy, starts REPL

  > polemy -l foo.pmy -l bar.pmy buz.pmy
      executes foo.pmy, bar.bmy, and then buz.pmy



<<Syntax>>

 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 ")"

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.




<<Basic Features>>

  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).


<<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:<REPL>},
      is@value:app,
    args@value:{car@value:{pos@value:{lineno@value:3, column@value:9, filename@value:<REPL>},
                            is@value:int,
                          data@value:1},
                cdr@value:{
                  car@value:{pos@value:{lineno@value:3, column@value:11, filename@value:<REPL>},
                              is@value:int,
                            data@value:2},
                  cdr@value:{}}},
     fun@value:{pos@value:{lineno@value:3, column@value:10, filename@value:<REPL>},
                 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): [<REPL>: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: [<REPL>: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:<REPL>},
                  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): [<REPL>: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.



<<Primitives>>

  {}  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 (no short-circuit!)
  < > <= >= == !=    2-ary  generic comparison

  print 1-ary print-to-stdout

  _isint _isstr _isfun _isundefined _istable  1-ary  dynamic-type-test