MANOOL Logo
MANOOL
MANOOL is Not an Object-Oriented Language!”

Compiler Dispatcher — Specification

Updated:

Metanotation

Two metalanguages are used in this section:

All MANOOL expressions in the metacircular specification are to be considered in the binding environment of the MANOOL standard library.

In Plain English

Run-time code, which is the output of the semantic analysis and code generation translation phase, is represented by a compiled entity, which internally (in its turn) may contain other compiled entities. Incidentally, compiled entities are also what identifiers are bound to in a binding environment.

A kind of object-oriented approach is used in this specification — compiled entities are considered to be active agents (represented by a MANOOL value/object for the purposes of the metacircular description) capable of providing services whenever the abstract machine asks them to.

The abstract machine has a (very) simple and compact semantic analysis and code generation core dispatcher. To compile an expression, the dispatcher first determines what kind of AST represents it:

  1. If it is a symbol (explicitly) bound to a compiled entity in the active binding environment, that compiled entity becomes the result of compilation.

  2. Otherwise, if it is an integer, a string, or a symbol, and the symbol starts with a character other than an ASCII lowercase letter (a thru z), the abstract machine constructs a compiled entity that represents a literal value.

  3. Otherwise, if it is a compound expression, the dispatcher compiles (recursively) the head subexpression and then asks the resulting compiled entity to compile the whole expression.

  4. Otherwise, if it is a special MANOOL value that encapsulates a compiled entity, which is produced by a #-expression (and for which the predicate IsCode returns True), then the encapsulated compiled entity becomes the result of compilation.

  5. As a fallback, the dispatcher reports an error as appropriate.

Note that checking the presence and placement of keywords (such as then, else, do, etc.) is performed, if needed, by compiled entities rather than directly by the dispatcher. Also, a compiled entity, when asked to compile an expression, may in turn call the dispatcher for subexpressions1 and possibly specify a different active binding environment to compile in.

Metacircular Specification

For the purposes of illustration, let's assume that a compiled entity recognizes the following polymorphic operations (beyond the standard ones):

The core dispatcher algorithm described above may be specified more formally using the following metacircular description (in MANOOL):2

{ let rec
  { Compile =
    { proc { Form; SymTab } as
    : if IsSym[Form] & SymTab.Exists[Form] then SymTab[Form] else -- bound identifier
    : if IsInt[Form] | IsStr[Form] | IsSym[Form] &
      { do True after
      : if (Str[Form] <> "") & (Str[Form][0] >= "a"[0]$) & (Str[Form][0] <= "z"[0]$) then
      : signal CompileError with "unbound keyword (nested in this context)"
      }
      then -- literal value
      { let { _Form } in
      : object { _Form = Form } with
        IsRvalue = {proc {_} as True}
        IsLvalue = {proc {_} as False}
        Compile' = CompileApply
        Execute  = {proc {Self; _} as Self[_Form]@}
      }
      else
    : if IsList[Form] & (Size[Form] <> 0) then Compile[Form[0]; SymTab].(Compile')[Form; SymTab] else -- compound expression
    : if IsCode[Form] then Form[_Entity]@ else -- a result of e# (used for metaprogramming)
    : signal CompileError with "invalid form"
    }
  }
  in
: export Compile
}

As a matter of illustration, a compiled entity bound to the if keyword could be constructed by evaluating the expression

{ object {} with
  -- Classification
  IsRvalue = {proc {_} as False}
  IsLvalue = {proc {_} as False}
  -- Compilation
  Compile' =
  { proc { _; Form; SymTab } as
  : if (Size[Form] >= 6) & (Form[2] == then') & (Form[4] = else') then
    { let { _Cond; _Body1; _Body2 } in
    : object
      { _Cond  = CompileRvalue[Form[1]; SymTab]
        _Body1 = CompileRvalue[Form[3]; SymTab]
        _Body2 = CompileRvalues[Form.Elems[Range[5; Size[Form]]]; SymTab]
      }
      with
      -- Classification
      IsRvalue = {proc {_} as True}
      IsLvalue = {proc {Self} as Self[_Body1]@.IsLvalue[] & Self[_Body2]@.IsLvalue[]}
      -- Execution
      Execute = {proc {Self; Ctx} as Execute[Self[{if Execute[Self[_Cond]@; Ctx] then _Body1 else _Body2}]@; Ctx]}
      ExecIn  = {proc {Self; Val; Ctx} as ExecIn[Self[{if Execute[Self[_Cond]@; Ctx] then _Body1 else _Body2}]@; Val; Ctx]}
      ExecOut = {proc {Self; Ctx} as ExecOut[Self[{if Execute[Self[_Cond]@; Ctx] then _Body1 else _Body2}]@; Ctx]}
      -- Compilation
      Compile' = CompileApply
    }
    else
  : if (Size[Form] >= 4) & (Form[2] == then') then
    { let { _Cond; _Body } in
    : object
      { _Cond = CompileRvalue[Form[1]; SymTab]
        _Body = CompileRvalues[Form.Elems[Range[3; Size[Form]]]; SymTab]
      }
      with
      -- Classification
      IsRvalue = {proc {_} as True}
      IsLvalue = {proc {_} as False}
      -- Execution
      Execute = {proc {Self; Ctx} as: if Execute[Self[_Cond]@; Ctx] then Execute[Self[_Body]@; Ctx]}
      -- Compilation
      Compile' = CompileApply
    }
    else
  : signal CompileError with "invalid form"
  }
}
Updated:
  1. .. and occasionally, even for expressions constructed artificially on-the-fly …

  2. Understanding a metacircular specification for a language L requires prior knowledge of the language L, which raises a question about its utility. However, normally only partial knowledge is required, and this roughly corresponds to how children learn communicative systems (i.e., natural languages) from zero.