Diferenças entre uma AST e uma ASR

Tomemos um simples código Fortran:

integer function f(a, b) result(r)
integer, intent(in) :: a, b
integer :: c, d
c = a + b - d
r = c * a
end function

e veja como são a AST e a ASR.

AST

[1]:
%%showast
integer function f(a, b) result(r)
integer, intent(in) :: a, b
integer :: c, d
c = a + b - d
r = c * a
end function
(TranslationUnit [(Function f [(a) (b)] [(AttrType TypeInteger [] () None)] r () () [] [] [] [(Declaration (AttrType TypeInteger [] () None) [(AttrIntent In)] [(a [] [] () None ()) (b [] [] () None ())] ()) (Declaration (AttrType TypeInteger [] () None) [] [(c [] [] () None ()) (d [] [] () None ())] ())] [(= 0 c (- (+ a b) d) ()) (= 0 r (* c a) ())] [])])

A AST não possui nenhuma informação semântica, mas sim «nós» que representam declarações do tipo integer, intent(in) :: a. Variáveis como a são representadas pelo nó Name e ainda não estão conectadas às suas declarações.

ASR

[2]:
%%showasr
integer function f(a, b) result(r)
integer, intent(in) :: a, b
integer :: c, d
c = a + b - d
r = c * a
end function
(TranslationUnit (SymbolTable 1 {f: (Function (SymbolTable 2 {a: (Variable 2 a [] In () () Default (Integer 4 []) Source Public Required .false.), b: (Variable 2 b [] In () () Default (Integer 4 []) Source Public Required .false.), c: (Variable 2 c [] Local () () Default (Integer 4 []) Source Public Required .false.), d: (Variable 2 d [] Local () () Default (Integer 4 []) Source Public Required .false.), r: (Variable 2 r [] ReturnVar () () Default (Integer 4 []) Source Public Required .false.)}) f [] [(Var 2 a) (Var 2 b)] [(= (Var 2 c) (IntegerBinOp (IntegerBinOp (Var 2 a) Add (Var 2 b) (Integer 4 []) ()) Sub (Var 2 d) (Integer 4 []) ()) ()) (= (Var 2 r) (IntegerBinOp (Var 2 c) Mul (Var 2 a) (Integer 4 []) ()) ())] (Var 2 r) Source Public Implementation () .false. .false. .false. .false. .false. [] [] .false.)}) [])

Uma ASR possui toda a informação semântica (tipos, etc.), nós do tipo Function possuem uma tabela de símbolos e não tem nenhuma nó de declaração. Variáveis são simplesmente ponteiros para a tabela de símbolos.

Discussão

O exemplo acima foi simples. As coisas ficam mais evidentes para exemplos mais complicados, como:

integer function f2b(a) result(r)
use gfort_interop, only: c_desc1_int32
integer, intent(in) :: a(:)
interface
    integer function f2b_c_wrapper(a) bind(c, name="__mod1_MOD_f2b")
    use gfort_interop, only: c_desc1_t
    type(c_desc1_t), intent(in) :: a
    end function
end interface
r = f2b_c_wrapper(c_desc1_int32(a))
end function

As AST devem representar todos os comandos use e blocos de interface, bem como manter as coisas semanticamente consistentes.

As ASR, por outro lado, devem manter os registros de c_desc1_int32, c_desc1_t e f2b_c_wrapper na tabela de símbolos e sabe que eles foram definidos no módulo gfort_interop e portanto a ASR não possui nenhuma desses nós de declaração.

Ao converter uma ASR em uma AST, o LFortran criará todos os nós de declaração da AST apropriados de forma automática e correta.