Difference between an AST and ASR

Let us take a simple Fortran code:

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

and look at how the AST and ASR looks like.

AST

%%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 [] ())] r () [] [] [] [(Declaration (AttrType TypeInteger [] ()) [(AttrIntent In)] [(a [] [] () None ()) (b [] [] () None ())]) (Declaration (AttrType TypeInteger [] ()) [] [(c [] [] () None ()) (d [] [] () None ())])] [(= 0 c (- (+ a b) d)) (= 0 r (* c a))] [])])

The AST does not have any semantic information, but has nodes to represent declarations such as integer, intent(in) :: a. Variables such as a are represented by a Name node, and are not connected to their declarations yet.

ASR

%%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), b: (Variable 2 b In () Default (Integer 4 []) Source Public Required), c: (Variable 2 c Local () Default (Integer 4 []) Source Public Required), d: (Variable 2 d Local () Default (Integer 4 []) Source Public Required), r: (Variable 2 r ReturnVar () Default (Integer 4 []) Source Public Required)}) f [(Var 2 a) (Var 2 b)] [(= (Var 2 c) (BinOp (BinOp (Var 2 a) Add (Var 2 b) (Integer 4 [])) Sub (Var 2 d) (Integer 4 []))) (= (Var 2 r) (BinOp (Var 2 c) Mul (Var 2 a) (Integer 4 [])))] (Var 2 r) Source Public Implementation)}) [])

The ASR has all the semantic information (types, etc.), nodes like Function have a symbol table and do not have any declaration nodes. Variables are simply pointers to the symbol table.

Discussion

The above was a simple example. Things get more apparent for more complicated examples, such as:

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

AST must represent all the use statements and the interface block, and keep things semantically consistent.

ASR, on the other hand, keeps track of the c_desc1_int32, c_desc1_t and f2b_c_wrapper in the symbol table and it knows they are defined in the gfort_interop module, and so ASR does not have any of these declaration nodes.

When converting from ASR to AST, LFortran will create all the appropriate AST declaration nodes automatically and correctly.