#!/usr/bin/env attl # # Welcome to ATTL! This is an executable tutorial for the ATTL language. # You will need to get the ATTL interpreter and install it for your platform # to run this script. # What is the line on top of the script? It is there to allow the ATTL script # to be executed directly on Unix like OS. # # Attl works in two phases: parse and evaluate. # # In the parse phase ATTL reads in the script and parses it, # which means it breaks it down into a format it can interpret. If an error # occurs in this phase, ATTL will report that error and the script will # do nothing at all. # # In the evaluate phase, ATTL then interprets or evaluates the parsed script. # If an error occurs the script will still partially be executed, # and such errors may be caught and handled. # # Now let's start exploring the ATTL language itself. # In front of this line and those above, you can see there is a # mark. # This is to indicate that the line is a comment. # ATTL parses comments but they do nothing during evaluation. # This is useful to explain what an ATTL script does like in this tutorial. # # Comments run from the # mark until the end of the line. # Comments may also be indented with spaces. # For example: this is also comment # # To make ATTL actually do something, we need to write a command in the script. # Let's start with the traditional "Hello World!" below: print "Hello World!\n" # The command above consists of an order print, and it's parameter # "Hello world\n" which is a literal text, with a new line character in it at # the end. # This commands orders the ATTL interpreter to print the text "Hello World" # followed by a new line to the interpreter's standard output. # # Commands in ATTL consist of an order followed optionally by parameters # Every parameter has to have whitespace in front of it, so ATTL can find it. # That's why it's not allowed to have whitespace at the end of the line. # Attl will give a parse error since it expects a parameter to follow the space. # An Attl command must be terminated by a newline or a semicolon. print "Hello " ; print "World!\n" # Some arguments take multiple parameters. # For example, print can also take multiple parameters. print "Hello $1\n" "World!" # # The $1 in the string above is an example of string interpolation. # This is explained later, below in this tutorial. For now, note that # print will replace $1, $2, ... with the text representation of it's 1st, # 2nd, and so forth parameter. # # All commands in ATTL have this same form. Unlike many other programming # languages, there are no special constructs. The evaluation of an Attl script # is fully determined by the available commands. # In this tutorial we look at how ATTL works with the builtin commands it # provides, but when ATTL is used as an embedded language, it might work # differently if the commands are different or not available. # # The effect of ATTL commands also depend on the Environment in which they # are executed. ATTL command can write to the ATTL environment's output, # read from the ATTL's environment input, and store data in the environment # using variables. Variabes are described later. # # Let's continue with some examples of commands. # For example this is how do do 1 + 2 in ATTL: iadd 1 2 # As you can see attl has no operators, just commands, so we use the command # iadd, short for integer add to add 1 and 2 together. # You will notice that when running this script, this iadd seems to do nothing # This is because we don't print the value anywhere. # Let's see how to do this. print "1+2=$1\n" [iadd 1 2] # The command above prints 1+2=3. But, what are the square brackets [] for? # Let's see what happens when we remove them: print "1+2=$1\n" iadd 1 2 # Then the command prints 1+3=iadd # The brackets [] are to indicate to ATTL that it should not take what is # between them as parameters of the first command in the line, but that it # starts a new command of which the value should be taken and used as # a parameter. Such a command between [] is called an Evaluation. # # Evaluations are eagerly evaluated when they are in an argument list. # This means they are evaluated before the command receives them as arguments. # # Evaluations may be nested as much as you like. For instance: print "((5+4)*3)-1)/2=$1\n" [idiv [isub [imul [iadd 5 4] 3] 1] 2] # .. prints ((5+4)*3)-1)/2=13 # # While this seems a bit cumbersome compared to operators, it keeps ATTL simple. # I do not recommend using ATTL for intensive maths. # # From this we can see that attl commands not only have effects such as writing # to the output, they also produce a result. If the ATTL command is successful # it returns a Value. If it not successful, it fails, which causes execution to # end, unless a rescue command is used (see later). # All commands either return a value or fail. Even print returns the amount of # bytes written, as follows: # print "Written $1 bytes\n" [print "Hello World\n"] # # The basic data which ATTL operates on, and which commands return # is called a Value. Unlike languages like TCL, ATTL Values are typed. # To see the type of a value use the typeof command print "type of 1: $1\n" [typeof 1] print "type of \"hi\": $1\n" [typeof "hi"] # # ATTL supports the following types by default: # * String: for literal text, in UTF-8 format. write "String µ出来る\n" # The above should write String µ出来るif your terminal supports it. # Normal strings support the following escape sequences: # \a: bell character # \b: backspace character # \e: escape character # \f: form feed character # \n: new line character # \r: carriage return character # \t: tab character # \\: a \ character # \": a " character # For instance, on a terminal which suports ANSI color the following prints # foo bar\ in red: print "escapes: \nX\b\e[31mfoo\tbar\\\e[0m\n\r" # Strings can run over multiple lines # A string can also run over multiple lines if it is a raw string between `` # Raw strings accept no escape characters and always terminate on a `. # There is no way to put a `in a raw string, but a raw string ans a normal # string can be appended to each other to allow this if the sadd # command is available. print `A "raw" string \\ no escapes \" ` # * Int: which is for integer numbers. print "Int: $1\n" 123 # * Word: which is for names. # Words consist have any non-symbol, non whitespace character, _ and / print "Word: $1\n" iAmA_Word/7 # * Bool: for boolean values. $true and $false are default boolean variables. # Words consist have any non-symbol, non whitespace character, _ and / print "Bool: $1 $2\n" $true $false # * List: for lists of ATTL values. # lists are created wth the list command print "List: $1\n" [list 1 2 3] # * Map: for a lookup map by string to value. # maps are created wth the map command. The key of maps must be strings. print "Map: $1\n" [map "a" 1 "b" 2] # * Block: for blocks of ATTL commands, between {}. # More about blocks later. print "Block: $1\n" {print "block ";print "world"} # * Type: for types. # Types can be defined with the command type. they are useful for defining # overloads. More on overloads later. print "Type: $1\n" [typeof [type "Type"]] # # When embedding ATTL, the embedding program can define other types. # # We can store values the ATTL environment as variables. # In ATTL, a variable must first be declared and initialized with let # before using it. # print "Hello $1 variable\n" $variable # This prints Hello !nil because the variable has not been defined yet. # To define a variabe with a value, use let let variable "World" # To get the value of the variable use get in an evaluation: print "Hello Variable: $1\n" [get variable] # [get variablename] is a common operation so we can abbreviate it using # a dollar sign: $variablename print "Hello Variable: $1\n" $variable # It's possible to repeat the dollar sign for indirect variable expansion let name variable print "Hello indirect variable: $1\n" $$name # String interpolation. # print as a command, performs string interpolation. That means it looks for # any dolar signs in the text and replaces them with the value of the # following variable in the ATTL environment. print "Hello Variable: $variable\n" # The variable for interpolation can also be indicated by a ${variable} # This is useful to if the variable must befollowed by a non-space: print "Hello Variable: ${variable}Record\n" # To obtain a $ in an interpolated value, just double it. print "Hello dollar: $$${1}\n" 35 # ATTL does not do string interpolation automatically, it is up # to the commands that take strings to do this if needed. # To interpolate strings ourselves, we can use the expand command set variable "expanded" let expanded [expand "Hello $variable\n"] print $expanded # Blocks are lists of commands. Block are not evaluated when passed as # a parameter to a command, but passed as such, and can be used to # implement flow control using commands, or to define new commands. # Blocks can run over multiple lines, the newlines in a block do NOT # terminate the top level command in front of the block. # The value of executing a block is set by the last command in the block, # or the return command if present. # Blocks also have anonymous parameters that are evaluated upon execution # of the block. let block { # Blocks receive their anyonymous parameters in $1..$argc, and # as a list in $argv let p1 $1 print "hello $p1 $argc $argv" print " block\n" return 77 } # Blocks stored in variables can be executed as if they were commands, # and can take parameters block "param1" # And blocks evaluate to their last command or return statement. print "block value: $1\n" [block] # Variables are dynamically and block-scoped. # When using set, and get, the innermost variable defined with let # will be used to set or get the value. # XXX bug here it seems let foo 1 #{ # let foo 2 # { # let foo 3 # print "$foo\n" # } # print "$foo\n" #} print "$foo\n" # compare the above with the below: let bar 1;{set bar 2;{set bar 3;print "$bar\n"};print "$bar\n"};print "$bar\n" # You can define commands yourself with the builtin "to" command. # This allows you to use named parameters in the block. # Parameters are passed by value, but using word parameters, you can implement # pass by name parameters as well. to set_print name value { print "Setting $name from $1 to $value\n" $$name set $name $value return 0 } let quux 123 set_print quux 321 print "quux: $quux\n" # Flow control can be done with the built in if command. if [igt $quux 200] { print "OK\n" } else { print "HUH?\n" } # ...or with the switch comand. The default word is used to mark the default # block. Other values are compared by string equality. switch $quux default { print "NOK?\n" } 123 { print "ALSO NOT OK\n" } 321 { print "OK\n" } # The overload command allows you to use the same command for different types # by redirecting them to a type-specific command overload add iadd Int Int overload add sadd String String print [add 10 20] print [add "10" "20"] # When an error occurs, it can be caught by setting up a rescue block with # the rescue command. The rescue block itself has block scope, that is # it applies to the block in which the rescue command is called. # To cause an error, use the fail command. to rescue_fail { rescue { let e $1 print "Error rescued: $e\n" } fail "Synthetic error" } rescue_fail # There are many other builtin commands available. See their description # by using the help or help all command. # help all help help