Merge Steve Murphy's (murf) complete re-implementation of AEL, which is now no longer...
[asterisk/asterisk.git] / doc / ael.txt
index e2014a4..54d68c6 100644 (file)
-The Asterisk Extension Language
-===================================
+The Asterisk Extension Language - v 2
+=====================================
 
-Over time, people have been pushing to add features to extensions.conf to make
-it more like a programming language.  AEL is intended to provide an actual
-programming language that can be used to write an Asterisk dialplan.
+AEL is a specialized language intended purely for 
+describing Asterisk dial plans.
 
-Getting Started
+The current version was written by Steve Murphy, and is a rewrite of 
+the original version.
+
+AEL is considered an EXPERIMENTAL Version. (yet is being
+used in the field with success)
+
+This new version further extends AEL, and
+provides more flexible syntax, better error messages, and some missing
+functionality.
+
+AEL is really the merger of 4 different 'languages', or syntaxes:
+
+    * The first and most obvious is the AEL syntax itselft. A BNF is
+      provided near the end of this document.
+
+    * The second syntax is the Expression Syntax, which is normally
+     handled by Asterisk extension engine, as expressions enclosed in
+     $[...]. The right hand side of assignments are wrapped in $[ ... ] 
+     by AEL, and so are the if and while expressions, among others.
+
+    * The third syntax is the Variable Reference Syntax, the stuff
+      enclosed in ${..} curly braces. It's a bit more involved than just
+      putting a variable name in there. You can include one of dozens of
+      'functions', and their arguments, and there are even some string
+      manipulation notation in there.
+
+    * The last syntax that underlies AEL, and is not used
+      directly in AEL, is the Extension Language Syntax. The
+      extension language is what you see in extensions.conf, and AEL
+      compiles the higher level AEL language into extensions and
+      priorities, and passes them via function calls into
+      Asterisk. Embedded in this language is the Application/AGI
+      commands, of which one application call per step, or priority
+      can be made. You can think of this as a "macro assembler"
+      language, that AEL2 will compile into.
+
+
+Any programmer of AEL should be familiar with it's syntax, of course,
+as well as the Expression syntax, and the Variable syntax.
+
+**************************
+* Asterisk in a Nutshell *
+**************************
+
+Asterisk acts as a server. Devices involved in telephony, like Zapata
+cards, or Voip phones, all indicate some context that should be
+activated in their behalf. See the config file formats for IAX, SIP,
+zapata.conf, etc. They all help describe a device, and they all
+specify a context to activate when somebody picks up a phone, or a
+call comes in from the phone company, or a voip phone, etc.
+
+Contexts
+--------
+
+Contexts are a grouping of extensions.
+
+Contexts can also include other contexts. Think of it as a sort of
+merge operation at runtime, whereby the included context's extensions
+are added to the contexts making the inclusion.
+
+Extensions and priorities
 -------------------------
+
+A Context contains zero or more Extensions. There are several
+predefined extensions. The "s" extension is the "start" extension, and
+when a device activates a context the "s" extension is the one that is
+going to be run. Other extensions are the timeout "t" extension, the
+invalid response, or "i" extension, and there's a "fax" extension. For
+instance, a normal call will activate the "s" extension, but an
+incoming FAX call will come into the "fax" extension, if it
+exists. (BTW, asterisk can tell it's a fax call by the little "beep"
+that the calling fax machine emits every so many seconds.).
+
+Extensions contain several priorities, which are individual
+instructions to perform. Some are as simple as setting a variable to a
+value. Others are as complex as initiating the Voicemail application,
+for instance. Priorities are executed in order.
+
+When the 's" extension completes, asterisk waits until the timeout for
+a response. If the response matches an extension's pattern in the
+context, then control is transferred to that extension. Usually the
+responses are tones emitted when a user presses a button on their
+phone. For instance, a context associated with a desk phone might not
+have any "s" extension. It just plays a dialtone until someone starts
+hitting numbers on the keypad, gather the number, find a matching
+extension, and begin executing it. That extension might Dial out over
+a connected telephone line for the user, and then connect the two
+lines together.
+
+The extensions can also contain "goto" or "jump" commands to skip to
+extensions in other contexts. Conditionals provide the ability to
+react to different stimiuli, and there you have it.
+
+Macros
+------
+
+Think of a macro as a combination of a context with one nameless
+extension, and a subroutine. It has arguments like a subroutine
+might. A macro call can be made within an extension, and the
+individual statements there are executed until it ends. At this point,
+execution returns to the next statement after the macro call. Macros
+can call other macros. And they work just like function calls.
+
+Applications
+------------
+
+Application calls, like "Dial()", or "Hangup()", or "Answer()", are
+available for users to use to accomplish the work of the
+dialplan. There are over 145 of them at the moment this was written,
+and the list grows as new needs and wants are uncovered. Some
+applications do fairly simple things, some provide amazingly complex
+services.
+
+Hopefully, the above objects will allow you do anything you need to in
+the Asterisk environment!
+
+
+*******************
+* Getting Started *
+*******************
+
 The AEL parser (pbx_ael.so) is completely separate from the module
-that parses extensions.conf (pbx_config.so).  To use AEL, the only thing that
-has to be done is the module pbx_ael.so must be loaded by Asterisk.  This will
-be done automatically if using 'autoload=yes' in /etc/asterisk/modules.conf.
-When the module is loaded, it will look for 'extensions.ael' in /etc/asterisk/.
-Both extensions.conf and extensions.ael can be used in conjunction with each 
-other if that is what is desired.  Some users may want to keep extensions.conf
-for the features that are configured in the 'general' section of 
-extensions.conf.
+that parses extensions.conf (pbx_config.so). To use AEL, the only
+thing that has to be done is the module pbx_ael.so must be loaded by
+Asterisk. This will be done automatically if using 'autoload=yes' in
+/etc/asterisk/modules.conf. When the module is loaded, it will look
+for 'extensions.ael' in /etc/asterisk/. extensions.conf and
+extensions.ael can be used in conjunction with
+each other if that is what is desired. Some users may want to keep
+extensions.conf for the features that are configured in the 'general'
+section of extensions.conf.
 
+------------------------------
+- Reloading extensions.ael  -
+------------------------------
 
-Reloading extensions.ael
--------------------------
-To reload extensions.ael, the following command can be issued at the CLI.
+To reload extensions.ael, the following command can be issued at the
+CLI:
 
-       *CLI> reload pbx_ael.so
+    *CLI> ael reload
 
 
-Contexts
+
+*************
+* Debugging *
+*************
+
+Right at this moment, the following commands are available, but do
+nothing:
+
+Enable AEL contexts debug
+   *CLI> ael debug contexts 
+
+Enable AEL macros debug
+   *CLI> ael debug macros 
+
+Enable AEL read debug
+   *CLI> ael debug read
+
+Enable AEL tokens debug
+   *CLI> ael debug tokens 
+
+Disable AEL debug messages
+   *CLI> ael no debug
+
+If things are going wrong in your dialplan, you can use the following
+facilities to debug your file:
+
+1. The messages log in /var/log/asterisk. (from the checks done at load time).
+2. the "show dialplan" command in asterisk
+3. the standalone executable, "aelparse" built in the utils/ dir in the source.
+
+
+*****************************
+* About "aelparse"          *
+*****************************
+
+You can also use the "aelparse" program to check your extensions.ael
+file before feeding it to asterisk. Wouldn't it be nice to eliminate
+most errors before giving the file to asterisk?
+
+aelparse is compiled in the utils directory of the asterisk release.
+It isn't installed anywhere (yet). You can copy it to your favorite
+spot in your PATH.
+
+aelparse has two optional arguments:
+
+-d   - Override the normal location of the config file dir, (usually
+       /etc/asterisk), and use the current directory instead as the
+       config file dir. Aelparse will then expect to find the file
+       "./extensions.ael" in the current directory, and any included
+       files in the current directory as well.
+
+-n   - don't show all the function calls to set priorities and contexts
+       within asterisk. It will just show the errors and warnings from
+       the parsing and semantic checking phases.
+
+
+******************************
+* General Notes about Syntax *
+******************************
+
+Note that the syntax and style are now a little more free-form. The
+opening '{' (curly-braces) do not have to be on the same line as the
+keyword that precedes them. Statements can be split across lines, as
+long as tokens are not broken by doing so. More than one statement can
+be included on a single line. Whatever you think is best!
+
+You can just as easily say,
+
+if(${x}=1) { NoOp(hello!); goto s|3; } else { NoOp(Goodbye!); goto s|12; }
+
+as you can say:
+
+if(${x}=1)
+{
+       NoOp(hello!);
+   goto s|3;
+}
+else
+{
+       NoOp(Goodbye!);
+       goto s|12;
+}
+
+or:
+
+if(${x}=1) {
+       NoOp(hello!);
+   goto s|3;
+} else {
+       NoOp(Goodbye!);
+       goto s|12;
+}
+
+or:
+
+if (${x}=1) {
+       NoOp(hello!); goto s|3;
+} else {
+       NoOp(Goodbye!); goto s|12;
+}
+
+or even:
+
+if
+(${x}=1)
+{
+NoOp(hello!);
+goto s|3;
+}
+else
+{
+NoOp(Goodbye!);
+goto s|12;
+}
+
+
+************
+* Keywords *
+************
+
+The AEL keywords are case-sensitive. If an application name and a
+keyword overlap, there is probably good reason, and you should
+consider replacing the application call with an AEL statement. If you
+do not wish to do so, you can still use the application, by using a
+capitalized letter somewhere in its name. In the Asterisk extension
+language, application names are NOT case-sensitive.
+
+The following are keywords in the AEL2 language:
+
+    * abstract
+    * context
+    * macro
+    * globals
+    * ignorepat
+    * switch
+    * if
+    * ifTime
+    * else
+    * random
+    * goto
+    * jump
+    * return
+    * break
+    * continue
+    * regexten
+    * hint
+    * for
+    * while
+    * case
+    * pattern
+    * default   NOTE: the "default" keyword can be used as a context name, 
+                      for those who would like to do so.
+    * catch
+    * switches
+    * eswitches
+    * includes 
+
+
+
+
+
+Procedural Interface and Internals
+==================================
+
+AEL first parses the extensions.ael file into a memory structure representing the file.
+The entire file is represented by a tree of "pval" structures linked together.
+
+This tree is then handed to the semantic check routine. 
+
+Then the tree is handed to the compiler. 
+
+After that, it is freed from memory.
+
+A program could be written that could build a tree of pval structures, and
+a pretty printing function is provided, that would dump the data to a file,
+or the tree could be handed to the compiler to merge the data into the 
+asterisk dialplan. The modularity of the design offers several opportunities
+for developers to simplify apps to generate dialplan data.
+
+
+
+=========================
+        AEL version 2 BNF
+=========================
+
+
+
+(hopefully, something close to bnf).
+
+First, some basic objects
+
+------------------------
+
+<word>    a lexical token consisting of characters matching this pattern: [-a-zA-Z0-9"_/.\<\>\*\+!$#\[\]][-a-zA-Z0-9"_/.!\*\+\<\>\{\}$#\[\]]*
+
+<word3-list>  a concatenation of up to 3 <word>s.
+
+<collected-word>  all characters encountered until the character that follows the <collected-word> in the grammar.
+
 -------------------------
-Contexts in AEL represent a set of extensions in the same way that they do
-in extensions.conf.
+
+<file> :== <objects>
+
+<objects> :== <object>
+           | <objects> <object>
+
+
+<object> :==  <context>
+         | <macro>
+         | <globals>
+         | ';'
+
+
+<context> :==  'context' <word> '{' <elements> '}'
+            | 'context' <word> '{' '}'
+            | 'context' 'default' '{' <elements> '}'
+            | 'context' 'default' '{' '}'
+            | 'abstract'  'context' <word> '{' <elements> '}'
+            | 'abstract'  'context' <word> '{' '}'
+            | 'abstract'  'context' 'default' '{' <elements> '}'
+            | 'abstract'  'context' 'default' '{' '}'
+
+
+<macro> :== 'macro' <word> '(' <arglist> ')' '{' <macro_statements> '}'
+       | 'macro' <word> '(' <arglist> ')' '{'  '}'
+       | 'macro' <word> '(' ')' '{' <macro_statements> '}'
+       | 'macro' <word> '(' ')' '{'  '}'
+
+
+<globals> :== 'globals' '{' <global_statements> '}'
+         | 'globals' '{' '}'
+
+
+<global_statements> :== <global_statement>
+                   | <global_statements> <global_statement>
+
+
+<global_statement> :== <word> '=' <collected-word> ';'
+
+
+<arglist> :== <word>
+         | <arglist> ',' <word>
+
+
+<elements> :==  <element>
+             | <elements> <element>
+
+
+<element> :== <extension>
+         | <includes>
+         | <switches>
+         | <eswitches>
+         | <ignorepat>
+         | <word> '='  <collected-word> ';'
+         | ';'
+
+
+<ignorepat> :== 'ignorepat' '=>' <word> ';'
+
+
+<extension> :== <word> '=>' <statement>
+           | 'regexten' <word> '=>' <statement>
+           | 'hint' '(' <word3-list> ')' <word> '=>' <statement>
+           | 'regexten' 'hint' '(' <word3-list> ')' <word> '=>' <statement>
+
+
+<statements> :== <statement>
+            | <statements> <statement>
+
+<if_head> :== 'if' '('  <collected-word> ')'
+
+<random_head> :== 'random' '(' <collected-word> ')'
+
+<ifTime_head> :== 'ifTime' '(' <word3-list> ':' <word3-list> ':' <word3-list> '|' <word3-list> '|' <word3-list> '|' <word3-list> ')'
+                       | 'ifTime' '(' <word> '|' <word3-list> '|' <word3-list> '|' <word3-list> ')'
+
+
+<word3-list> :== <word>
+       | <word> <word>
+       | <word> <word> <word>
+
+<switch_head> :== 'switch' '(' <collected-word> ')'  '{'
+
+
+<statement> :== '{' <statements> '}'
+       | <word> '='  <collected-word> ';'
+       | 'goto' <target> ';'
+       | 'jump' <jumptarget> ';'
+       | <word> ':'
+       | 'for' '('  <collected-word> ';'  <collected-word> ';' <collected-word> ')' <statement>
+       | 'while' '('  <collected-word> ')' <statement>
+       | <switch_head> '}'
+       | <switch_head> <case_statements> '}'
+       | '&' macro_call ';'
+       | <application_call> ';'
+       | <application_call> '='  <collected-word> ';'
+       | 'break' ';'
+       | 'return' ';'
+       | 'continue' ';'
+       | <random_head> <statement>
+       | <random_head> <statement> 'else' <statement>
+       | <if_head> <statement>
+       | <if_head> <statement> 'else' <statement>
+       | <ifTime_head> <statement>
+       | <ifTime_head> <statement> 'else' <statement>
+       | ';'
+
+<target> :== <word>
+       | <word> '|' <word>
+       | <word> '|' <word> '|' <word>
+       | 'default' '|' <word> '|' <word>
+       | <word> ',' <word>
+       | <word> ',' <word> ',' <word>
+       | 'default' ',' <word> ',' <word>
+
+<jumptarget> :== <word>
+               | <word> ',' <word>
+               | <word> ',' <word> '@' <word>
+               | <word> '@' <word>
+               | <word> ',' <word> '@' 'default'
+               | <word> '@' 'default'
+
+<macro_call> :== <word> '(' <eval_arglist> ')'
+       | <word> '(' ')'
+
+<application_call_head> :== <word>  '('
+
+<application_call> :== <application_call_head> <eval_arglist> ')'
+       | <application_call_head> ')'
+
+<eval_arglist> :==  <collected-word>
+       | <eval_arglist> ','  <collected-word>
+       |  /* nothing */
+       | <eval_arglist> ','  /* nothing */
+
+<case_statements> :== <case_statement>
+       | <case_statements> <case_statement>
+
+
+<case_statement> :== 'case' <word> ':' <statements>
+       | 'default' ':' <statements>
+       | 'pattern' <word> ':' <statements>
+       | 'case' <word> ':'
+       | 'default' ':'
+       | 'pattern' <word> ':'
+
+<macro_statements> :== <macro_statement>
+       | <macro_statements> <macro_statement>
+
+<macro_statement> :== <statement>
+       | 'catch' <word> '{' <statements> '}'
+
+<switches> :== 'switches' '{' <switchlist> '}'
+       | 'switches' '{' '}'
+
+<eswitches> :== 'eswitches' '{' <switchlist> '}'
+       | 'eswitches' '{'  '}'
+
+<switchlist> :== <word> ';'
+       | <switchlist> <word> ';'
+
+<includeslist> :== <includedname> ';'
+       | <includedname> '|' <word3-list> ':' <word3-list> ':' <word3-list> '|' <word3-list> '|' <word3-list> '|' <word3-list> ';'
+       | <includedname> '|' <word> '|' <word3-list> '|' <word3-list> '|' <word3-list> ';'
+       | <includeslist> <includedname> ';'
+       | <includeslist> <includedname> '|' <word3-list> ':' <word3-list> ':' <word3-list> '|' <word3-list> '|' <word3-list> '|' <word3-list> ';'
+       | <includeslist> <includedname> '|' <word> '|' <word3-list> '|' <word3-list> '|' <word3-list> ';'
+
+<includedname> :== <word>
+        | 'default'
+
+<includes> :== 'includes' '{' <includeslist> '}'
+       | 'includes' '{' '}'
+
+
+**************************
+* AEL Example USAGE *****
+**************************
+
+Comments
+========
+
+Comments begin with // and end with the end of the line.
+
+Comments are removed by the lexical scanner, and will not be
+recognized in places where it is busy gathering expressions to wrap in
+$[] , or inside application call argument lists. The safest place to put
+comments is after terminating semicolons, or on otherwise empty lines.
+
+
+Context
+=======
+
+Contexts in AEL represent a set of extensions in the same way that
+they do in extensions.conf.
+
 
 context default {
 
-};
+}
+
+
+A context can be declared to be "abstract", in which case, this
+declaration expresses the intent of the writer, that this context will
+only be included by another context, and not "stand on its own". The
+current effect of this keyword is to prevent "goto " statements from
+being checked.
+
+
+abstract context longdist {
+            _1NXXNXXXXXX => NoOp(generic long distance dialing actions in the US);
+}
+
 
 
 Extensions
--------------------------
-To specify an extension in a context, the following syntax is used.  If more
-than one application is be called in an extension, they can be listed in order
-inside of a block.
+==========
+
+To specify an extension in a context, the following syntax is used. If
+more than one application is be called in an extension, they can be
+listed in order inside of a block.
+
 
 context default {
-       1234 => Playback(tt-monkeys);
-       8000 => {
-               NoOp(one);
-               NoOp(two);
-               NoOp(three);
-       };
-       _5XXX => NoOp(it's a pattern!);
-};
+    1234 => Playback(tt-monkeys);
+    8000 => {
+         NoOp(one);
+         NoOp(two);
+         NoOp(three);
+    };
+    _5XXX => NoOp(it's a pattern!);
+}
+
+
+Two optional items have been added to the AEL syntax, that allow the
+specification of hints, and a keyword, regexten, that will force the
+numbering of priorities to start at 2.
+
+
+context default {
+
+    regexten _5XXX => NoOp(it's a pattern!);
+}
+
+
+
+context default {
+
+    hint(Sip/1) _5XXX => NoOp(it's a pattern!);
+}
+
+
+
+context default {
+
+    regexten hint(Sip/1) _5XXX => NoOp(it's a pattern!);
+}
+
+
+The regexten must come before the hint if they are both present.
+
 
 
 Includes
--------------------------
-Contexts can be included in other contexts.  All included contexts are listed
-within a single block.
+========
+
+Contexts can be included in other contexts. All included contexts are
+listed within a single block.
+
 
 context default {
-       includes {
-               local;
-               longdistance;
-               international;
-       };
-};
+    includes {
+         local;
+         longdistance;
+         international;
+    }
+}
+
+
+Time-limited inclusions can be specified, as in extensions.conf
+format, with the fields described in the wiki page Asterisk cmd
+GotoIfTime.
+
+
+context default {
+    includes {
+         local;
+         longdistance|16:00-23:59|mon-fri|*|*;
+         international;
+    }
+}
+
+
+#include
+========
+
+You can include other files with the #include "filepath" construct.
+
+
+   #include "/etc/asterisk/testfor.ael"
+
+
+An interesting property of the #include, is that you can use it almost
+anywhere in the .ael file. It is possible to include the contents of
+a file in a macro, context, or even extension.  The #include does not
+have to occur at the beginning of a line. Included files can include
+other files, up to 50 levels deep. If the path provided in quotes is a
+relative path, the parser looks in the config file directory for the
+file (usually /etc/asterisk).
+
 
 
 Dialplan Switches
--------------------------
-Switches are listed in their own block within a context.
+=================
+
+Switches are listed in their own block within a context. For clues as
+to what these are used for, see Asterisk - dual servers, and Asterisk
+config extensions.conf.
+
 
 context default {
-       switches {
-               DUNDi/e164;
-               IAX2/box5;
-       };
-       eswitches {
-               IAX2/context@${CURSERVER};
-       };
-};
+    switches {
+         DUNDi/e164;
+         IAX2/box5;
+    };
+    eswitches {
+         IAX2/context@${CURSERVER};
+    }
+}
+
 
 
 Ignorepat
--------------------------
-ignorepat can be used to instruct channel drivers to not cancel dialtone upon
-receipt of a particular pattern.  The most commonly used example is '9'.
+=========
+
+ignorepat can be used to instruct channel drivers to not cancel
+dialtone upon receipt of a particular pattern. The most commonly used
+example is '9'.
+
 
 context outgoing {
-       ignorepat => 9;
-};
+    ignorepat => 9;
+}
+
+
 
 
 Variables
--------------------------
-Variables in Asterisk do not have a type, so to define a variable, it just has
-to be specified with a value.
+=========
+
+Variables in Asterisk do not have a type, so to define a variable, it
+just has to be specified with a value.
 
 Global variables are set in their own block.
 
+
 globals {
-       CONSOLE=Console/dsp;
-       TRUNK=Zap/g2;
-};
+    CONSOLE=Console/dsp;
+    TRUNK=Zap/g2;
+}
+
+
 
 Variables can be set within extensions as well.
 
+
 context foo {
-       555 => {
-               x=5;
-               y=blah;
-               NoOp(x is ${x} and y is ${y} !);
-       };
-};
+    555 => {
+         x=5;
+         y=blah;
+         divexample=10/2
+         NoOp(x is ${x} and y is ${y} !);
+    }
+}
+
+
+NOTE: AEL wraps the right hand side of an assignment with $[ ] to allow 
+expressions to be used If this is unwanted, you can protect the right hand 
+side from being wrapped by using the Set() application. 
+Read the README.variables about the requirements and behavior 
+of $[ ] expressions.
+
+NOTE: These things are wrapped up in a $[ ] expression: The while() test; 
+the if() test; the middle expression in the for( x; y; z) statement 
+(the y expression); Assignments - the right hand side, so a = b -> Set(a=$[b])
 
 Writing to a dialplan function is treated the same as writing to a variable.
 
+
 context blah {
-       s => {
-               CALLERID(name)=ChickenMan;
-               NoOp(My name is ${CALLERID(name)} !);
-       };
-}; 
+    s => {
+         CALLERID(name)=ChickenMan;
+         NoOp(My name is ${CALLERID(name)} !);
+    }
+} 
+
 
 
 Loops
--------------------------
+=====
+
 AEL has implementations of 'for' and 'while' loops.
 
+
 context loops {
-       1 => {
-               for (x=0; ${x} < 3; x=${x} + 1) {
-                       Verbose(x is ${x} !);
-               };
-       };
-       2 => {
-               y=10;
-               while (${y} >= 0) {
-                       Verbose(y is ${y} !);
-                       y=${y}-1;
-               };
-       };
-};
+    1 => {
+         for (x=0; ${x} < 3; x=${x} + 1) {
+              Verbose(x is ${x} !);
+         }
+    }
+    2 => {
+         y=10;
+         while (${y} >= 0) {
+              Verbose(y is ${y} !);
+              y=${y}-1;
+         }
+    }
+}
+
+
+NOTE: The conditional expression (the "${y} >= 0" above) is wrapped in
+      $[ ] so it can be evaluated.  NOTE: The for loop test expression
+      (the "${x} < 3" above) is wrapped in $[ ] so it can be evaluated.
+
 
 
 Conditionals
--------------------------
-AEL supports if and switch statements.  Note that if you have an else 
-clause, you MUST place braces around the non-else portion of the if 
-statement.
+============
+
+AEL supports if and switch statements, like AEL, but adds ifTime, and
+random. Unlike the original AEL, though, you do NOT need to put curly
+braces around a single statement in the "true" branch of an if(), the
+random(), or an ifTime() statement. The if(), ifTime(), and random()
+statements allow optional else clause.
+
 
 context conditional {
-       _8XXX => {
-               Dial(SIP/${EXTEN});
-               if (${DIALSTATUS} = "BUSY") {
-                       Voicemail(${EXTEN}|b);
-               } else
-                       Voicemail(${EXTEN}|u);
-       };
-       _777X => {
-               switch (${EXTEN}) {
-                       case 7771:
-                               NoOp(You called 7771!);
-                               break;
-                       case 7772:
-                               NoOp(You called 7772!);
-                               break;
-                       case 7773:
-                               NoOp(You called 7773!);
-                               // fall through
-                       default:
-                               NoOp(In the default clause!);
-               };
-       };
-};
-
-
-goto and labels
--------------------------
+    _8XXX => {
+         Dial(SIP/${EXTEN});
+         if ("${DIALSTATUS}" = "BUSY")
+         {
+              NoOp(yessir);
+              Voicemail(${EXTEN}|b);
+         }
+         else
+              Voicemail(${EXTEN}|u);
+         ifTime (14:00-25:00|sat-sun|*|*) 
+              Voicemail(${EXTEN}|b);
+         else
+         {
+              Voicemail(${EXTEN}|u);
+              NoOp(hi, there!);
+         }
+         random(51) NoOp(This should appear 51% of the time);
+
+         random( 60 )
+         {
+                       NoOp( This should appear 60% of the time );
+         }
+         else
+         {
+                       random(75)
+                       {
+                               NoOp( This should appear 30% of the time! );
+                       }
+                       else
+                       {
+                               NoOp( This should appear 10% of the time! );
+                       }
+          }
+    }
+    _777X => {
+         switch (${EXTEN}) {
+              case 7771:
+                   NoOp(You called 7771!);
+                   break;
+              case 7772:
+                   NoOp(You called 7772!);
+                   break;
+              case 7773:
+                   NoOp(You called 7773!);
+                   // fall thru-
+              pattern 777[4-9]:
+                    NoOp(You called 777 something!);
+              default:
+                   NoOp(In the default clause!);
+         }
+    }
+}
+
+
+NOTE: The conditional expression in if() statements (the
+      "${DIALSTATUS}" = "BUSY" above) is wrapped by the compiler in 
+      $[] for evaluation.
+
+NOTE: Neither the switch nor case values are wrapped in $[ ]; they can
+      be constants, or ${var} type references only.
+
+NOTE: AEL generates each case as a separate extension. case clauses
+      with no terminating 'break', or 'goto', have a goto inserted, to
+      the next clause, which creates a 'fall thru' effect.
+
+NOTE: AEL introduces the ifTime keyword/statement, which works just
+      like the if() statement, but the expression is a time value,
+      exactly like that used by the application GotoIfTime(). See
+      Asterisk cmd GotoIfTime
+
+NOTE: The pattern statement makes sure the new extension that is
+      created has an '_' preceding it to make sure asterisk recognizes
+      the extension name as a pattern.
+
+NOTE: Every character enclosed by the switch expression's parenthesis
+      are included verbatim in the labels generated. So watch out for
+      spaces!
+
+NOTE: NEW: Previous to version 0.13, the random statement used the
+      "Random()" application, which has been deprecated. It now uses
+      the RAND() function instead, in the GotoIf application.
+
+
+Break, Continue, and Return
+===========================
+
+
+Three keywords, break, continue, and return, are included in the
+syntax to provide flow of control to loops, and switches.
+
+The break can be used in switches and loops, to jump to the end of the
+loop or switch.
+
+The continue can be used in loops (while and for) to immediately jump
+to the end of the loop. In the case of a for loop, the increment and
+test will then be performed. In the case of the while loop, the
+continue will jump to the test at the top of the loop.
+
+The return keyword will cause an immediate jump to the end of the
+context, or macro, and can be used anywhere.
+
+
+
+goto, jump, and labels
+======================
+
 This is an example of how to do a goto in AEL.
 
+
+context gotoexample {
+    s => {
+begin:
+         NoOp(Infinite Loop!  yay!);
+         Wait(1);
+         goto begin;    // go to label in same extension
+    }
+    3 => {
+            goto s|begin;   // go to label in different extension
+     }
+     4 => {
+            goto gotoexample|s|begin;  // overkill go to label in same context
+     }
+}
+
+context gotoexample2 {
+     s =>  {
+   end: 
+           goto gotoexample|s|begin;   // go to label in different context
+     }
+}
+
+You can use the special label of "1" in the goto and jump
+statements. It means the "first" statement in the extension. I would
+not advise trying to use numeric labels other than "1" in goto's or
+jumps, nor would I advise declaring a "1" label anywhere! As a matter
+of fact, it would be bad form to declare a numeric label, and it might
+confllict with the priority numbers used internally by asterisk.
+
+The syntax of the jump statement is: jump
+extension[,priority][@context] If priority is absent, it defaults to
+"1". If context is not present, it is assumed to be the same as that
+which contains the "jump".
+
+
 context gotoexample {
-       s => {
+    s => {
 begin:
-               NoOp(Infinite Loop!  yay!);
-               Wait(1);
-               goto begin;
-       };
-};
+         NoOp(Infinite Loop!  yay!);
+         Wait(1);
+         jump s;    // go to first extension in same extension
+    }
+    3 => {
+            jump s,begin;   // go to label in different extension
+     }
+     4 => {
+            jump s,begin@gotoexample;  // overkill go to label in same context
+     }
+}
+
+context gotoexample2 {
+     s =>  {
+   end: 
+           jump s@gotoexample;   // go to label in different context
+     }
+}
+
+NOTE: goto labels follow the same requirements as the Goto()
+      application, except the last value has to be a label. If the
+      label does not exist, you will have run-time errors. If the
+      label exists, but in a different extension, you have to specify
+      both the extension name and label in the goto, as in: goto s|z;
+      if the label is in a different context, you specify
+      context|extension|label. There is a note about using goto's in a
+      switch statement below...
+
+NOTE  AEL introduces the special label "1", which is the beginning
+      context number for most extensions.
+
+NOTE: A NEW addition to AEL: you can now use ',' instead of '|' to 
+      separate the items in the target address. You can't have a mix,
+      though, of '|' and ',' in the target. It's either one, or the other.
+
+
 
 
 Macros
--------------------------
-A macro is defined in its own block like this.  The arguments to the macro are
-specified with the name of the macro.  They are then reffered to by that same
-name.  A catch block can be specified to catch special extensions.
+======
+
+A macro is defined in its own block like this. The arguments to the
+macro are specified with the name of the macro. They are then referred
+to by that same name. A catch block can be specified to catch special
+extensions.
+
 
 macro std-exten( ext , dev ) {
-        Dial(${dev}/${ext},20);
-        switch(${DIALSTATUS) {
-        case BUSY:
-                Voicemail(b${ext});
-                break;
-        default:
-                Voicemail(u${ext});
-        };
-        catch a {
-                VoiceMailMain(${ext});
-                return;
-        };
-};
-
-A macro is then called by preceeding the macro name with an ampersand.
+       Dial(${dev}/${ext},20);
+       switch(${DIALSTATUS) {
+       case BUSY:
+               Voicemail(b${ext});
+               break;
+       default:
+               Voicemail(u${ext});
+
+       }
+       catch a {
+               VoiceMailMain(${ext});
+               return;
+       }
+}
+
+
+A macro is then called by preceeding the macro name with an
+ampersand. Empty arguments can be passed simply with nothing between
+comments(0.11).
+
 
 context example {
-       _5XXX => &std-exten(${EXTEN}, "IAX2");
-};
+    _5XXX => &std-exten(${EXTEN}, "IAX2");
+    _6XXX => &std-exten(, "IAX2");
+    _7XXX => &std-exten(${EXTEN},);
+    _8XXX => &std-exten(,);
+}
+
 
 
 Examples
-------------------------
+========
+
 
 context demo {
-       s => {
-               Wait(1);
-               Answer();
-               TIMEOUT(digit)=5;
-               TIMEOUT(response)=10;
+    s => {
+         Wait(1);
+         Answer();
+         TIMEOUT(digit)=5;
+         TIMEOUT(response)=10;
 restart:
-               Background(demo-congrats);
+         Background(demo-congrats);
 instructions:
-               for (x=0; ${x} < 3; x=${x} + 1) {
-                       Background(demo-instruct);
-                       WaitExten();
-               };
-       };
-       2 => {
-               Background(demo-moreinfo);
-               goto s|instructions;
-       };
-       3 => {
-               LANGUAGE()=fr;
-               goto s|restart;
-       };
-       500 => {
-               Playback(demo-abouttotry);
-               Dial(IAX2/guest@misery.digium.com);
-               Playback(demo-nogo);
-               goto s|instructions;
-       };
-       600 => {
-               Playback(demo-echotest);
-               Echo();
-               Playback(demo-echodone);
-               goto s|instructions;
-       };
-       # => {
+         for (x=0; ${x} < 3; x=${x} + 1) {
+              Background(demo-instruct);
+              WaitExten();
+         }
+    }
+    2 => {
+         Background(demo-moreinfo);
+         goto s|instructions;
+    }
+    3 => {
+         LANGUAGE()=fr;
+         goto s|restart;
+    }
+
+    500 => {
+         Playback(demo-abouttotry);
+         Dial(IAX2/guest@misery.digium.com);
+         Playback(demo-nogo);
+         goto s|instructions;
+    }
+    600 => {
+         Playback(demo-echotest);
+         Echo();
+         Playback(demo-echodone);
+         goto s|instructions;
+    }
+    # => {
 hangup:
-               Playback(demo-thanks);
-               Hangup();
-       };
-       t => goto #|hangup;
-       i => Playback(invalid);
-};
+         Playback(demo-thanks);
+         Hangup();
+    }
+    t => goto #|hangup;
+    i => Playback(invalid);
+}
 
 
-Syntax Note
-------------------------
-Please note that all opening {'s are on the same line as the keyword.  For
-the time being, that syntax is mandatory.  We are looking at ways to allow
-other syntax in the future for flexibility, but for now, that is the way
-you must write AEL clauses.
+Semantic Checks
+===============
+
+
+AEL, after parsing, but before compiling, traverses the dialplan
+tree, and makes several checks:
+
+    * Macro calls to non-existent macros.
+    * Macro calls to contexts.
+    * Macro calls with argument count not matching the definition.
+    * application call to macro. (missing the '&')
+    * application calls to "GotoIf", "GotoIfTime", "while",
+      "endwhile", "Random", and "execIf", will generate a message to
+      consider converting the call to AEL goto, while, etc. constructs.
+    * goto a label in an empty extension.
+    * goto a non-existent label, either a within-extension,
+      within-context, or in a different context, or in any included
+      contexts. Will even check "sister" context references.
+    * All the checks done on the time values in the dial plan, are
+      done on the time values in the ifTime() and includes times:
+          o the time range has to have two times separated by a dash;
+          o the times have to be in range of 0 to 24 hours.
+          o The weekdays have to match the internal list, if they are provided;
+          o the day of the month, if provided, must be in range of 1 to 31;
+          o the month name or names have to match those in the internal list. 
+    * (0.5) If an expression is wrapped in $[ ... ], and the compiler
+      will wrap it again, a warning is issued.
+    * (0.5) If an expression had operators (you know,
+      +,-,*,/,%,!,etc), but no ${ } variables, a warning is
+      issued. Maybe someone forgot to wrap a variable name?
+    * (0.12) check for duplicate context names.
+    * (0.12) check for abstract contexts that are not included by any context.
+    * (0.13) Issue a warning if a label is a numeric value. 
+
+There are a subset of checks that have been removed until the proposed
+AAL (Asterisk Argument Language) is developed and incorporated into Asterisk.
+These checks will be:
+
+    * (if the application argument analyzer is working: the presence
+      of the 'j' option is reported as error.
+    * if options are specified, that are not available in an
+      application.
+    * if you specify too many arguments to an application.
+    * a required argument is not present in an application call.
+    * Switch-case using "known" variables that applications set, that
+      does not cover all the possible values. (a "default" case will
+      solve this problem. Each "unhandled" value is listed.
+    * a Switch construct is used, which is uses a known variable, and
+      the application that would set that variable is not called in
+      the same extension. This is a warning only...
+    * Calls to applications not in the "applist" database (installed
+      in /var/lib/asterisk/applist" on most systems).
+    * In an assignment statement, if the assignment is to a function,
+      the function name used is checked to see if it one of the
+      currently known functions. A warning is issued if it is not.
+
+
+
+Differences with the original version of AEL
+============================================
+
+   1. The $[...] expressions have been enhanced to inlcude the ==, ||,
+      and && operators. These operators are exactly equivalent to the
+      =, |, and & operators, respectively. Why? So the C, Java, C++
+      hackers feel at home here.
+   2. It is more free-form. The newline character means very little,
+      and is pulled out of the white-space only for line numbers in
+      error messages.
+   3. It generates more error messages -- by this I mean that any
+      difference between the input and the grammar are reported, by
+      file, line number, and column.
+   4. It checks the contents of $[ ] expressions (or what will end up
+      being $[ ] expressions!) for syntax errors. It also does
+      matching paren/bracket counts.
+   5. It runs several semantic checks after the parsing is over, but
+      before the compiling begins, see the list above.
+   6. It handles #include "filepath" directives. -- ALMOST
+      anywhere, in fact. You could easily include a file in a context,
+      in an extension, or at the root level. Files can be included in
+      files that are included in files, down to 50 levels of hierarchy...
+   7. Local Goto's inside Switch statements automatically have the
+      extension of the location of the switch statement appended to them.
+   8. A pretty printer function is available within pbx_ael.so.
+   9. In the utils directory, two standalone programs are supplied for
+      debugging AEL files. One is called "aelparse", and it reads in
+      the /etc/asterisk/extensions.ael file, and shows the results of
+      syntax and semantic checking on stdout, and also shows the
+      results of compilation to stdout. The other is "aelparse1",
+      which uses the original ael compiler to do the same work,
+      reading in "/etc/asterisk/extensions.ael", using the original
+      'pbx_ael.so' instead.
+  10. AEL supports the "jump" statement, and the "pattern" statement
+      in switch constructs. Hopefully these will be documented in the
+      AEL README.
+  11. Added the "return" keyword, which will jump to the end of an
+      extension/Macro.
+  12. Added the ifTime (<time range>|<days of week>|<days of
+      month>|<months> ) {} [else {}] construct, which executes much
+      like an if () statement, but the decision is based on the
+      current time, and the time spec provided in the ifTime. See the
+      example above. (Note: all the other time-dependent Applications
+      can be used via ifTime)
+  13. Added the optional time spec to the contexts in the includes
+      construct. See examples above.
+  14. You don't have to wrap a single "true" statement in curly
+      braces, as in the orignal AEL. An "else" is attached to the
+      closest if. As usual, be careful about nested if statements!
+      When in doubt, use curlies!
+  15. Added the syntax [regexten] [hint(channel)] to preceed an
+      extension declaration. See examples above, under
+      "Extension". The regexten keyword will cause the priorities in
+      the extension to begin with 2 instead of 1. The hint keyword
+      will cause its arguments to be inserted in the extension under
+      the hint priority. They are both optional, of course, but the
+      order is fixed at the moment-- the regexten must come before the
+      hint, if they are both present.
+  16. Empty case/default/pattern statements will "fall thru" as
+      expected. (0.6)
+  17. A trailing label in an extension, will automatically have a
+      NoOp() added, to make sure the label exists in the extension on
+      Asterisk. (0.6)
+  18. (0.9) the semicolon is no longer required after a closing brace!
+      (i.e. "];" ===> "}". You can have them there if you like, but
+      they are not necessary. Someday they may be rejected as a syntax
+      error, maybe.
+  19. (0.9) the // comments are not recognized and removed in the
+      spots where expressions are gathered, nor in application call
+      arguments. You may have to move a comment if you get errors in
+      existing files.
+  20. (0.10) the random statement has been added. Syntax: random (
+      <expr> ) <lucky-statement> [ else <unlucky-statement> ]. The
+      probability of the lucky-statement getting executed is <expr>,
+      which should evaluate to an integer between 0 and 100. If the
+      <lucky-statement> isn't so lucky this time around, then the
+      <unlucky-statement> gets executed, if it is present.
+
+
+
+Hints and Bugs
+==============
+
+    * The safest way to check for a null strings is to say $[ "${x}" =
+     "" ] The old way would do as shell scripts often do, and append
+     something on both sides, like this: $[ ${x}foo = foo ]. The
+     trouble with the old way, is that, if x contains any spaces, then
+     problems occur, usually syntax errors. It is better practice and
+     safer wrap all such tests with double quotes! Also, there are now
+     some functions that can be used in a variable referenece,
+     ISNULL(), and LEN(), that can be used to test for an empty string:
+     ${ISNULL(${x})} or $[ ${LEN(${x}) = 0 ].
+
+    * Assignment vs. Set(). Keep in mind that setting a variable to
+      value can be done two different ways. If you choose say 'x=y;',
+      keep in mind that AEL will wrap the right-hand-side with
+      $[]. So, when compiled into extension language format, the end
+      result will be 'Set(x=$[y])'. If you don't want this effect,
+      then say "Set(x=y);" instead.
+
+
+The Full Power of AEL
+==============================
+
+A newcomer to Asterisk will look at the above constructs and
+descriptions, and ask, "Where's the string manipulation functions?",
+"Where's all the cool operators that other languages have to offer?",
+etc.
+
+The answer is that the rich capabilities of Asterisk are made
+available through AEL, via:
+
+    * Applications: See Asterisk - documentation of application
+      commands
+
+    * Functions: Functions were implemented inside ${ .. } variable
+      references, and supply many useful capabilities. 
+
+    * Expressions: An expression evaluation engine handles items
+      wrapped inside $[...]. This includes some string manipulation
+      facilities, arithmetic expressions, etc. 
+
+    * Application Gateway Interface: Asterisk can fork external
+      processes that communicate via pipe. AGI applications can be
+      written in any language. Very powerful applications can be added
+      this way. 
+
+    * Variables: Channels of communication have variables associated
+      with them, and asterisk provides some global variables. These can be
+      manipulated and/or consulted by the above mechanisms.