←THE LAST ACTION LANGUAGE→

From Esolang
Jump to navigation Jump to search


←THE_LAST_ACTION_LANGUAGE→

overview

  • natural-language agnostic (all keywords are 1 special character) so you can program in Toki Pona
  • prefix notation
  • variadic functions are not allowed
  • tacit programming
  • variables are mutable
  • their types are immutable
  • functions are values
  • events


types

there are 8 types

   void            [;]
   type            [~]
   error           [`]
   boolean         [?]
   number          [0]
   block           [_]
   function        [#]
   event handler   [:]


basics

the syntax is all prefix notation, so to assign (2+5)*3 to a variable Foo, you would

   $ [Foo] * + 2 5 3

all functions have constant arity, so you always know which argument goes to which parameter. in the example above, we use the function $ which is the assignment. assignment needs 2 arguments, the first is the name of a variable, and the second is a value. let's go step by step.

                   ; [ this is a comment ]
   $               ; [ this is our assignment function call ]
       [Foo]       ; [ this is the first argument of $ ]
       * + 2 5 3   ; [ this is the second argument of $ ]

again, slower.

   $               ; [ this is our assignment function call ]
       [Foo]       ; [ this is the first argument of $ ]
       *           ; [ this is the second argument of $ ]
           +       ; [ this is the first argument of * ]
               2   ; [ this is the first argument of + ]
               5   ; [ this is the second argument of + ]
           3       ; [ this is the second argument of * ]

notice how Foo is between square brackets. this is because assignment is not a special form, so Foo alone would have been evaluated and 21 would have been assigned not to Foo but to Foo's current value.

btw, using a variable that has not been assigned a value yet, raises an error. you need to initialize them before you use them.


immutable types

once a variable has a value, its type becomes immutable.

   $ [Foo] 5
   $ [Foo] [bar baz]

this raises an error because 5 is a number while [bar baz] is a block. this would change the type of Foo, which is forbidden.


section

the source code is made of sections. here is a section named Init.

   § [Init]

the content of a section is accessible by using its name like a variable name. you can read it, and you can also assign a new value to it, which changes directly the source code of the running program.

   $ [Init] [ § [Init] some code here...]


conditional

if-then-else goes like:

   ?                   ; [ if ]
       < n 0           ; [ the condition, directly ]
       [ $ [n] 0 ]     ; [ then-part code, enclosed in a quoting block ]
       [ ]             ; [ else-part is mandatory, even though it's empty ]

then-part and else-part must be enclosed in blocks to prevent their direct execution.


loooop

who needs loops anyway? ok, let's calculate

   $ [n] p $ [r] 1
   €
       < 0 n                           ; [ condition ]
       [ $ [n] - n 1 $ [r] * 2 r ]     ; [ loop body ]


index

say you have a list.

   $ [list] [ One A Two B Three C 0 D 1 E 2 F ]

then after

   $ [x] ° list 0
   $ [y] ° list 1
   $ [z] ° list [Two]

x is One, y is A, and z is B.

if the index is a number, then the list is treated as an array. if the index is a block, the list is treated as a key-value object. if the index is something else, an error is raised.


template

inside of a block, everything between curlies will be evaluated, and the curlies will be replaced by the result of the evaluation. no matter how deep curlies are.

   [ this is a [ test where one plus one equals { + 1 1 } ] ]

will become

   [ this is a [ test where one plus one equals 2 ] ]


function assembler

the function assembler is not a special form. it is a function which needs 3 arguments: an arity, a return type, and a function code body.

   $ [Double] # 1 [0] [ ^ * 2 @ 1 ]
   $ [Addten] # 1 [0] [ ^ + @ 1 10 ]

step by step

   $                           ; [ we asssign ]
       [Double]                ; [ to the variable named "Double" ]
       #                       ; [ a function ]
           1                   ; [ which takes 1 argument ]
           [0]                 ; [ and which returns a number ]
           [
               ^               ; [ return ]
                   *
                       2       ; [ twice ]
                       @ 1     ; [ the first argument ]
           ]


function disassembler

the function disassembler does the opposite.

   $ [Src] ' [Double]

now Src is a block which contains:

   # 1 [0] [ ^ * 2 @ 1 ]


evaluation & composition

parens ( ) evaluate their content. say you have

   $ [Doubten] [Double Addten]

then if you do

   $ [Answer] (Doubten) 11

Answer will be 42.


exception

you can do classical try/catch blocks like this.

   "
       [ + 1 [meh] ]   ; [ try block, executed first ]
       [ oooops... ]   ; [ catch block, executed if the try block failed ]

you can also raise an error manually. since I don't like backticks much, the raise function is `.

   ` [ my own error ]


events, timing & I/O

you can attach an event handler, which returns an handle. with this handle, you can detach the event handler.

events are how you communicate with anything outside of the interpreter. output is done by emitting events, input is received as events.

they also have a timing functionality: when you emit events, you can choose to delay their emission.

   $ [keypressedHandle] : [ keypressed ] [

       $ [pressedKey] @ 1
       handleInput pressedKey
   ]

                   ; [ now we have an active event handler ]

   . keypressedHandle

                   ; [ now then handler is deactivated ]

   >               ; [ emit ]
       0           ; [ after 0 msec ]
       [print]     ; [ an event named "print" ]
       [be bop]    ; [ with an argument "be bop" ]


shadowing

when you're inside of a closure, if you read a variable, you're reading locally first, then in the callers.

But when you set a variable's value, you're always working on a local variable, even when a variable with the same name exists in the callers.


misc.

to get a list of all variable names:

   _

and to get the type of something:

   ~ [datum]

you must put your datum inside of a block.


keywords

section

syntax:

   § name:block


escape

syntax:

   \


conditional

syntax:

   ? condition:boolean then:block else:block


while

syntax:

   € condition:boolean body:block


index

syntax:

   ° data:block index:any


block

syntax:

   [ ]


evaluate

syntax:

   ( )


template

syntax:

   [ { } ]


set variable value

syntax:

   $ varName:block value:any


assemble function

syntax:

   # arity:number type:type body:block


return

syntax:

   ^ value:any


argument

syntax:

   @ index:number


disassemble function

syntax:

   ' funcExpr:block


exception

syntax:

   " try:block catch:block


raise

syntax:

   ` error:block


event on

syntax:

   : event:block body:block


event off

syntax:

   . handle:event-handler


emit

syntax:

   > delay:number event:block argument:any


vocabulary

syntax:

   _


no op

syntax:

   ; comment:any


type of

syntax:

   ~ datumExpr:block


math

syntax:

   + a:number b:number         ; [ add ]
   - a:number b:number         ; [ subtract ]
   * a:number b:number         ; [ multiply ]
   / a:number b:number         ; [ divide ]
   % a:number b:number         ; [ modulo ]
   , a:number                  ; [ trunc ]
   = a:number b:number         ; [ equal ]
   < a:number b:number         ; [ less than ]
   ! a:boolean                 ; [ not ]
   & a:boolean b:boolean       ; [ and ]
   | a:boolean b:boolean       ; [ or ]


factorial example

   $ [factorial] # 1 [0] [

       ^ ? < @ 1 2
           1
           *
               @ 1
               factorial - @ 1 1
   ]


length example

   $ [length] # 1 [0] [

       $ [len] 0
       € ! = ° @ 1 len [] [ $ [len] + len 1 ]
       ^ len
   ]