AST API
- Expression Annotations: 'bind'
- Code Block: 'scope'
- Code Block Annotations: 'bind_scope'
- Code Block Bindings: 'ast_scope_binding'
- Code Block Parents: 'cfa_parent'
- Function: 'function'
- Function's Annotations: 'bind_func'
- Function's Specialization: 'cfa_function_specializations'
- Function's Entry: 'cfa_parent'
- Function's Result: 'dfa_fnret'
- Operations. Invocation: 'cfa_call', 'dfa_callfn', 'dfa_callargs'
- Operations. Loops
In order to reason about program, code model is translated into form suited for processing by Transcend. Translation details are described below.
Expression Annotations: 'bind'
- symbol-ref reference to the expression or identifier
- annotation expression's annotation
Declares expression's annotations.
Example:
test = function:: int { x = 5:: int; expected(even_number). x }
gets translated into:
bind(s(1,-2,0),expected(even_number))
Code Block: 'scope'
- scope-ref reference to the code block
Declares code block under its unique reference identifier.
Example:
test = function:: float { x = 3:: float. y = 8:: float. x + y }
Translation result: scope(0)
Code Block Annotations: 'bind_scope'
- scope-ref child block's reference
- annotation code block's annotation
Declares code block's annotations called context. There are two forms for different context' type:
- strong context known at compile time
- weak possible context, can't be decided for sure at compile time
Example:
test = function:: float { context:: arithmetic(fast). x = 3:: float. y = 8:: float. x + y }
Translation's result:
bind_scope(0,arithmetic(fast),strong)
Code Block Bindings: 'ast_scope_binding'
- scope-ref code block's reference
- binding-id number of code block's binding
- binding-alias name of a binding
Code blocks have zero or more bindings, i.e. Identifiers that have special meaning within the block. Predicate declares such code block's bindings. Bindings organized into ordered list. Order is conveyed by specifying binding-id for each binding-alias.
Example:
test = function:: int { loop ( 0 -> acc ):: int { if(acc > 10):: int {acc:: int; final} else {acc + 1} } }
Translation result: ast_scope_binding(1,0,"acc")
Code Block Parents: 'cfa_parent'
- scope-ref child block's reference
- scope-parent-ref parent block's reference
Represents nested code blocks structure in terms of child-parent relation.
Example:
test = function:: int { x = 19:: int. if (x>5):: int {x + 1} else {x - 1} }
Translation's result:
cfa_parent(1, scope(0)). cfa_parent(2, scope(0)).
Function: 'function'
- fn-name function name
Declares function identified by its name.
Example:
test = function:: int {0}
Translation's result: function("test")
Function's Annotations: 'bind_func'
- fn-name function's name
Declares function's annotations.
Example:
test = function:: int; status(obsolete) {0}
Translation's result: bind_func("test",status(obsolete))
Function's Specialization: 'cfa_function_specializations'
- fn-name name of function
- guard specialization guard
There is a possibility to have several functions with the same name called specializations. Each specialization is uniquely determined by annotation of special kind called guard.
Example:
guard:: arch(amd64) { test = function:: i64 {0} }
Translation's result: cfa_function_specializations("test",arch(amd64))
Function's Entry: 'cfa_parent'
- scope-entry-ref function's entry code block reference
- fn-name function's name
Each function has a single entry code block and is declared in terms of child-parent relation between entry block(which is top-level in blocks hierarchy of the given function) and the function itself.
Example:
test = function:: int {0}
Translation's result: cfa_parent(0,function("test"))
Function's Result: 'dfa_fnret'
- symbol-ret-ref reference to a function's return expression
Specifies which expression is used to compute function's return value.
Example:
test = function:: int {0}
Translation's result: dfa_fnret("test",s(0,-2,0))
Operations. Invocation: 'cfa_call', 'dfa_callfn', 'dfa_callargs'
- scope-caller-ref caller's code block's reference
- fn-callee-name callee function name
- symbol-ref invocation operation reference
- arg-formal-name function's formal argument
- arg-actual-ref actual argument reference
Each function invocation is transformed into several transcend facts as explained below:
(1) cfa_call | Specifies caller's code block and callee function name |
(2) dfa_callfn | Declares unique reference to a particular invocation site.The reference used by other facts to supply additional information |
(3) dfa_callargs | Declares assignment relations between actual arguments and formal arguments |
(4) weak(dfa_callargs) | The same as form (3). Weak relation used in cases when there is no enough information at compile time. One such case is when several function specializations exist and it's impossible to say which exactly specialization is called |
Example:
inc = function (x::int):: int { x + 1 } main = function:: int { arg = 10:: int. inc(arg) }
After translation following transcend facts are present:
cfa_call(1,"inc") dfa_callfn(s(0,-2,1),"inc") weak(dfa_callargs(s(0,-2,1),s(1,-2,0),s(1,-2,1))) dfa_callargs(s(0,-2,1),s(1,-2,0),s(1,-2,1))
Operations. Loops
- symbol-source-ref input list reference
- symbol-acc-ref accumulator reference
- symbol-result-ref result list reference
- loop-body-ref refers to a loop body expression
Facts declare loop operations.
Example:
test = function:: int; entry { singles = {1, 2, 3}:: [int]. doubles = loop map(singles->element:: int)::[int] {2 * element}. doubles[0] }
produces fact: ast_op_map(s(2,-2,0),s(1,-2,0),s(0,-2,1))