Skip to content
disnet edited this page Apr 30, 2013 · 1 revision

Sweet.js

Core Structures

The main data types are Syntax and TermTree:

Syntax :: {
    token   :: Token,
    context :: Context,
    mark    :: (Int?) -> Syntax
    rename  :: (Syntax or [Syntax], Str) -> Syntax
    push_dummy_rename :: (Str) -> Syntax
    swap_dummy_rename :: (Syntax, Str, Str) -> Syntax 
}

The Syntax object holds the token (a holdover data structure from esprima which knows about the token's value and line numbers) and its context (a structure of marks and renames used for hygiene) along with a couple of methods to help with hygiene.

Each Syntax object is in one-to-one correlation with an individual token.

In contrast, the TermTree object holds a collection of of Syntax objects in a closer and closer approximation of the final AST.

TermTree :: {
    destruct :: () -> [Syntax]
}

It's basically an AST, just not as complete as the final AST generated by esprima. Productions in the grammar then are represented by individual term trees:

BinOp extends TermTree :: {
    left    :: TermTree
    op      :: TermTree
    right   :: TermTree
}

expand

The main entry point into the expander is, appropriately enough, expand:

expand :: ([Syntax], Map, Map) -> [TermTree]
function expand(toks, env, ctx) { ... }

The expand function is primarily responsible for handling hygiene. Most of its work happens when dealing with functions (renaming all the parameters and whatnot). The env param is a mapping from identifiers to macro definitions and ctx is a mapping of names to names (used for hygiene).

The expand function delegates to two sub expand like functions:

expandToTermTree :: ([Syntax], Map) -> {terms: [TermTree], env: Map}

Responsible for converting the syntax to TermTrees and loading any macro definitions it finds into the new env map.

expandTermTreeToFinal :: (TermTree, Map, Map) -> TermTree

Responsible for packing each term tree into its final state. This means recursively expanding sub trees and handling various hygiene tasks.

These are roughly analogous to parse1 and parse2 in the honu paper.

enforest

The enforest function deals with transforming Syntax objects to a TermTree:

enforest :: ([Syntax], Map) -> {
    result  :: TermTree
    rest    :: [Syntax] 
}

Honu

The main contracts for honu:

parse    :: ([Syntax], Env)     -> AST
parse1   :: ([Syntax], Env)     -> ([TermTree], Env)
parse2   :: ([TermTree], Env)   -> AST

enforest :: ([Syntax], Env)     -> (TermTree, [Syntax])

data TermTree = Macro Str [Syntax]
              | Expr [TermTree]
              | Bop TermTree Str TermTree
              | Fun Str [TermTree] [Syntax]
              | Block [Syntax]
              | Id Syntax
              | Lit Syntax
              | Call [TermTree] [TermTree]

Some partial pattern matching definitions in a haskelly notation:

parse :: ([Syntax], Env) -> AST
parse stx env = let (terms, env') = parse1 stx env 
                in parse2 terms env'

parse1 :: [Syntax] -> Env -> ([TermTree], Env)
parse1 [] env = ([], env)
parse1 in env = 
    case (enforest in env) of
        (Macro n b, rest)  -> parse1 rest (update n (loadm b))
        (Expr e, rest)     -> (Expr e) : (parse1 rest env)
        -- case when var declaration (somewhat complicated)

parse2 :: [TermTree] -> Env -> AST
parse2 forms env = map parse2' forms 

parse2' (Fun n vs b) env = FunAST n vs (parse b (aug env vs))
parse2' (Block b) env    = BlockAST (parse b env)
parse2' (Id i) env       = IdAST i
parse2' (Lit l) env      = LitAST l
parse2' (Call l r) env   = CallAST (parse2 l env) (parse2 r env) 
parse2' (Bop l o r) env  = BopAST (parse2 l env) o (parse2 r env) 

And the enforest function itself:

data Syntax = LitR Int
            | IdR Str
            | BinopR Str
            | ParenR [Syntax]
            | ...

enforest :: [Syntax] -> Env -> (TermTree, [Syntax])

enforest (LitR n:rest) env = enforest (Lit n:rest) env
enforest (IdR i:rest) env 
    | lookup i env == Var id  = enforest (Id id:rest) env
    | lookup i env == Macro t = enforest (t rest) env
enforest (ltree:(IdR i):rest) env
    | lookup i env == BopR op = case enforest rest env of
        (rtree, rest) -> enforest (Bop ltree op rtree:rest) env
-- similar for uop
enforest (ltree:ParenR args:rest) 
    | -- each arg must enforest to have no `rest`
    = enforest (Call ltree argtrees:rest) env 
Clone this wiki locally