Expressions and symbols

An abstract syntax tree (AST) is a tree representation of the abstract syntactic structure of source code written in a programming language. When Julia code is parsed by its LLVM JIT compiler, it is internally represented as an abstract syntax tree. The nodes of this tree are simple data structures of the type expression Expr. For more information on abstract syntax trees, refer to http://en.wikipedia.org/wiki/Abstract_syntax_tree.

An expression is simply an object that represents Julia code. For example, 2 + 3 is a piece of code, which is an expression of type Int64 (follow along with the code in Chapter 7expressions.jl). Its syntax tree can be visualized as follows:

To make Julia see this as an expression and block its evaluation, we have to quote it, that is, precede it by a colon (:) as in :(2 + 3). When you evaluate :(2 + 3) in the REPL, it just returns :(2 + 3), which is of type Expr: typeof(:(2 + 3)) returns Expr. In fact, the : operator (also called the quote operator) sets out to treat its argument as data, not as code.

If this code is more than one line, enclose the lines between the quote and end keywords to turn the code into an expression. For example, this expression just returns itself:

quote 
    a = 42 
    b = a^2 
    a - b 
end 

In fact, this is the same as :(a = 42; b = a^2; a - b). quote...end is just another way to convert blocks of code into expressions.

We can give an expression such as this a name, such as e1 = :(2 + 3). We can ask for the following information:

  • e1.head returns :call, indicating the kind of expression, which here is a function call
  • e1.args returns 3-element Array{Any,1}: :+ 2 3

Indeed the expression 2 + 3 is, in fact, a call of the + function with the argument 2, and 3: 2 + 3 == + (2, 3) returns true. The args argument consists of a symbol :+, and two literal values, 2 and 3. Expressions are made up of symbols and literals. More complicated expressions will consist of literal values, symbols, and sub- or nested expressions, which can, in turn, be reduced to symbols and literals.

For example, consider the expression e2 = :(2 + a * b - c), which can be visualized by the following syntax tree:

e2 consists of e2.args, which is a 3-element Array{Any,1} that contains :- and :c, which are symbols, and :(2 + a * b), which is also an expression. This last expression, in turn, is itself an expression with args:+, 2, and :(a * b); :(a * b) is an expression with arguments and symbols: :*, :a, and :b. We can see that this works recursively; we can simplify every subexpression in the same way until we end up with elementary symbols and literals.

In the context of an expression, symbols are used to indicate access to variables; they represent the variable in the tree structure for the code. In fact, the prevent evaluation character of the quote operator (:) is already at work with symbols: after x = 5x returns 5, but :x returns :x.

The dump function presents the abstract syntax tree for its argument in a nice way. For example, dump(:(2 + a * b - c)) returns the output, as shown in the following screenshot:

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.246.245