SMOP sm0p Language


sm0p

Basic Structure

sm0p has a simple structure of a finite set of nodes. Each node is represented by a statement, and no inner blocks are allowed. It is delimited by the q:sm0p { ... } construct, and must be used as the rvalue of a C assignement like: "continuation = q:sm0p { ... }".
Every symbol is replaced by its C counterpart, which means that they are resolved at "definition time". On the other hand, the defined operations will only be executed later by the interpreter at "runtime". As sm0p is really a macro language, there's no much sense in defining a "compile time"

Statement

Each statement is composed by three parts:

  1. Optional Label
  2. Invocant / Responder
  3. Identifier
  4. Capture

And this three parts are always resolved at definition time, with the following rules:

  1. If they have a $ sigil, they should be resolved as local C variables.
  2. Barewords are treated as constant identifiers, use q:identifier[infix:+] for non alphanumerics
  3. SMOP__SLIME__Capturize.new(x,(a,b,c),(q,w,e),z) is translated directly to its lowlevel equivalent and usually appears as the argument to the move_capturize call
  4. When there are mixed named and positional arguments, the named must be before (for parsing reasons)
  5. Labels are defined to with label: in front of the node and refered to as `label (the translate to number representing the relative position of the labeled node

The messages are composed by:

  1. taking the responder interface of the invocant as the responder for the message.
  2. translating the capture of the call to a native capture creation, using the invocant unless other invocant is defined.
  3. taking the identifier as is.
  4. When the capture have the prefix:<|> operator, the given object is set as the capture instead of composing a capture with the object

Example

The default implementation of the interpreter in SMOP now is pretty lame. The sm0p language is a macro language that will translate from a higher-level meaningfull language to a set of repetitive C calls that noone wants to write by hand. The exemple seems to be the better way to illustrate that.

// c code before here
continuation = q:sm0p {
   $current;
   $interpreter;
   $obj.DESTROYALL(|$obj);
   $SMOP__SLIME__CurrentFrame.move_capturize(
       SMOP__SLIME__Capturize.new(2,(3),(),3));
   $SMOP__SLIME__CurrentFrame.forget();
   $SMOP__SLIME__CurrentFrame.free(|$obj);
   $interpreter.goto()
};
// c code after here

This embedded code, that means:

continuation = Frame.new(
  Node.new(
    result => $current
  ),
  Node.new(
    result => $interpreter
  ),
  Node.new(
    responder => ___RI___($obj),
    identifier => DESTROYALL,
    capture => $obj,
  ),
  Node.new(
    responder => ___RI___(SMOP__STACK__CurrentFrame),
    identifier => move_capturize,
    capture => \( SMOP__STACK__OPCAPTURE_Move_Capturize.new(2,(3),(),3) ),
  ),
  Node.new(
    responder => ___RI___(SMOP__STACK__CurrentFrame),
    identifier => forget,
  ),
  Node.new(
    responder => ___RI___(SMOP__STACK__CurrentFrame),
    identifier => free,
    capture => $obj,
  ),
  Node.new(
    responder => ___RI___($interpreter),
    identifier => goto,
    capture => \( $interpreter: )
  )
);

Would be translated by a pre-processor to:

continuation = SMOP_DISPATCH
(NULL, SMOP__SLIME__Frame, SMOP__ID__new, 
 SMOP__NATIVE__capture_create
 (SMOP__SLIME__Frame,
  (SMOP__Object*[]){
    // $current;
    SMOP_DISPATCH
    (NULL, SMOP__SLIME__Node, SMOP__ID__new,
     SMOP__NATIVE__capture_create
     (SMOP__SLIME__Node, NULL,
      (SMOP__Object*[]){
        SMOP__ID__result,
        current
      })),
    // $interpreter;
    SMOP_DISPATCH
    (NULL, SMOP__SLIME__Node, SMOP__ID__new,
     SMOP__NATIVE__capture_create
     (SMOP__SLIME__Node, NULL,
      (SMOP__Object*[]){
        SMOP__ID__result,
        interpreter
      })),
    // $obj.DESTROYALL(|$obj);
    SMOP_DISPATCH
    (NULL, SMOP__SLIME__Node, SMOP__ID__new,
     SMOP__NATIVE__capture_create
     (SMOP__SLIME__Node, NULL,
      (SMOP__Object*[]){
        SMOP__ID__responder,
        SMOP_RI(obj),
        SMOP__ID__identifier,
        SMOP__ID__DESTROYALL,
        SMOP__ID__capture,
        obj
      })),
    // SMOP__SLIME__CurrentFrame.move_capturize(
    //    SMOP__SLIME__Capturize.new(2,(3),(),3));
    SMOP_DISPATCH
    (NULL, SMOP__SLIME__Node, SMOP__ID__new,
     SMOP__NATIVE__capture_create
     (SMOP__SLIME__Node, NULL,
      (SMOP__Object*[]){
        SMOP__ID__responder,
        SMOP_RI(SMOP__SLIME__CurrentFrame),
        SMOP__ID__identifier,
        SMOP__ID__move_capturize,
        SMOP__ID__capture,
        SMOP__NATIVE__capture_create(SMOP__SLIME__CurrentFrame, SMOP__SLIME__Capturize_create(2,(int[]){3,0}, NULL, 3));
      })),
    // SMOP__SLIME__CurrentFrame.forget();
    SMOP_DISPATCH
    (NULL, SMOP__SLIME__Node, SMOP__ID__new,
     SMOP__NATIVE__capture_create
     (SMOP__SLIME__Node, NULL,
      (SMOP__Object*[]){
        SMOP__ID__responder,
        SMOP_RI(SMOP__SLIME__CurrentFrame),
        SMOP__ID__identifier,
        SMOP__ID__forget
      })),
    // SMOP__SLIME__Operators.free(|$obj);
    SMOP_DISPATCH
    (NULL, SMOP__SLIME__Node, SMOP__ID__new,
     SMOP__NATIVE__capture_create
     (SMOP__SLIME__Node, NULL,
      (SMOP__Object*[]){
        SMOP__ID__responder,
        SMOP_RI(SMOP__SLIME__Operators),
        SMOP__ID__identifier,
        SMOP__ID__free,
        SMOP__ID__capture,
        SMOP__NATIVE__capture_create(obj, NULL, NULL)
      })),
    // ___INTERPRETER___.goto()
    SMOP_DISPATCH
    (NULL, SMOP__SLIME__Node, SMOP__ID__new,
     SMOP__NATIVE__capture_create
     (SMOP__SLIME__Node, NULL,
      (SMOP__Object*[]){
        SMOP__ID__responder,
        SMOP_RI(___INTERPRETER___),
        SMOP__ID__identifier,
        SMOP__ID__goto
      })),
  }));