-
Notifications
You must be signed in to change notification settings - Fork 70
Analyzer
Analyzer takes read wisp code form and performs both syntax & macro expansions over it, producing AST nodes that are hash maps that implement following interface:
{:type :keyword
:form '(read data)}
The :type
key is mapped to a keyword representing the AST variant type.
All the AST nodes produced by analyzer comply to some AST type interface
described by this document.
The :form
key is mapped to an actual form that was analyzed producing
AST node containing it. Note that raw forms mapped to the key will also
typically have a metadata containing source location information of the
node.
Location information contains start (the position of the first character of the parsed source region) & and an end position (the position of the first character after the parsed source region):
Both :start
and :end
positions contain (0-indexed) :line
number and
a (0-indexed) :column
number that can be obtained as illustrated in the
examples below:
;; Start line position
(:line (:start (meta (:form node))))
;; Start column position
(:column (:start (meta (:form node))))
;; End line position
(:line (:end (meta (:form node))))
;; End columnt position
(:column (:end (meta (:form node))))
Literals are represented by an AST nodes of with :op :constant
:
{:op :constant
:form nil}
{:op :constant
:form "foo"}
{:op :constant
:form true}
{:op :constant
:form 5}
{:op :constant
:form #"foo"}
{:op :constant
:form ':bar}
{:op :constant
:form 'baz}
Reader spits out actual primitive on literals (except for quoted symbols), for literal nodes don't carry any metadata with them, but that should change in a future.
Identifier nodes typically represent either referces to a specific bindings
or an identifiers of the declared bindings. Declaration identifier nodes
are the ones which have :info
field to nil
.
{:op :var
:form 'foo
:info null}
Reference identifiers on the other hand have :info
field mapped to a hash
that representing binding it's reference of:
{:op :var
:form 'bar
:info {:name 'bar
:shadow nil}}
Sometimes bindings may shadow bindings in an outer scope. Such nodes will have
:shadow
field mapped to true
and :depth
field mapped to the integer
representing level of shadowing depth (this information is used by writer to
rename binding definitions based on shadowing depth).
{:op :var
:form 'foo
:info {:name 'foo
:shadow true
:depth 1}
Parameter nodes represent parameters of the function expressions or catch expressions. Parameter nodes also carry shadowing similar to identifiers:
{:op :parameter
:id IdentifierNode
:form 'foo
:depth 1
:shadow true}
Binding nodes represent bindings defined via let and loop forms
{:op :binding
:id {:op :var
:form 'foo
:info null
:depth 1
:shadow true}
:init {:op :constant
:form 1}
:form '(foo 1)}
{:op :def
:doc nil
:id IdentifierNode
:init Node
:export false
:form '(def foo 1)}
If nodes are nodes representing if expressions:
{:op :if
:test Node
:consequent Node
:alternate Node
:form '(if (empty? chars)
:eof
(recur (rest chars)
(conj result (first chars))))}
Throw nodes represent throw expressions. :throw
field is mapped to
arbitrary node that is a second form in the throw
.
{:op :throw
:throw {:op :var
:form 'error
:info nil}
:form '(throw error)}
Assignment nodes are produced by set! expressions. :target
field is mapped to an identifier node and :value
field is mapped
to expression being assigned to that identifier:
{:op :set!
:target {:op :var
:form 'foo
:info nil}
:value {:op :constant
:form "bar"}
:form '(set! foo "bar")}
New node are produced by new special forms. It's :constructor
field is mapped to a node (typically identifier but could be any
expression) representing a constructor. It's :params
field is
mapped to a vector of (any) nodes passed to a constructor:
{:type :new
:constructor {:op :var
:form 'Error
:info nil}
:params [{:op :constant
:form "Boom!"}]
:form '(new Error "Boom!)}