Asterisk AEL2

Business PBX Solutions
Provider Solution Details
Bicom VoIP Become an ITSP Now!
  • Become a serious competitor in VoIP Immediately
  • FULL Consultancy, Installation, Training & Support
  • Sell Hosted IP PBXs, Biz Lines, Call Centre
  • Turnkey Provisioning at your data center
Details
3CX Software PBX for Windows
  • Windows Software Solution
  • Easy to Install and Manage
  • Auto Configures Phones & Trunks
  • Android, iOS, Windows & Mac clients
Details

Asterisk Extension Language v.2

AEL v.2 is intended to provide an actual programming language that can be used to write an Asterisk dialplan. It
further extends AEL, and provides more flexible syntax, better error messages, and some missing functionality.
AEL v.2 is a new version of the AEL compiler, written by Steve Murphy. It was originally introduced as a large asterisk patch in the Asterisk bug database. See: Bug 6021.

AEL is really the merger of 4 different 'languages', or syntaxes:
  • The first and most obvious is the AEL v.2 syntax itself. 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/AEL2, and is not used directly in AEL/AEL2, is the Extension Language Syntax. The extension language is what you see in extensions.conf, and AEL2 compiles the higher level AEL2 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. There are references to Wiki pages covering these, down in the "The Power of AEL" section below.


AEL2 Announcements and News


NEW: For 1.2 users who'd like to have access to AEL2, I've got a better idea. The old way was to "svn diff http://svn.digium.com/svn/asterisk/branches/1.2 http://svn.digium.com/svn/asterisk/team/murf/AEL2-1.2 > AEL.patch", and then apply the patch to your current 1.2 based source with "patch -p0 < AEL.patch"; this is a bit of a hassle, as that branch is brought back into sync with 1.2 on a weekly or monthly basis. An easier approach would be to:

svn co http://svn.digium.com/svn/asterisk/team/murf/AEL2-1.2/patches

And then, in the 1.2 source directory, you can simply:

patch -p0 < patches/diffs.AEL2.patch

which will apply the changes.

Good news for all you AEL2 fans out there! A couple of weeks ago, I dropped a bug in Murf's ear about adding an option to dump the parsed AEL code into a file, basically a "compile" option. Well, he coded it, we've both tested it, and it seems solid, so it's now in the trunk! just use aelparse as usual and add the -w option if you want to dump the file extensions.conf.aeldump. Handy for coding in AEL2 but still being able to easily deliver to clients who aren't able or willing to use AEL2 in their Asterisk implementation! -Sherwood McGowan

AEL2 has replaced AEL in the trunk version of Asterisk. You now get it automatically when you svn checkout asterisk! It's upward compatible with the original AEL, but adds several features, and removes some limitations.

  • Please note that AEL (See Asterisk AEL ) is stepping out of the "Experimental" stage. Please help us to make it solid and useful by using it!!

The Asterisk Extension Language v.2

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 in the macro are executed until a return statement is executed. 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


In the following discussion, if you are using the "trunk" version of Asterisk, please note that AEL2 replaced the original AEL implementation.
So, anywhere in the following discussion where "ael2" or "AEL2" is mentioned, you would use "ael" or "AEL" instead. For instance, in the
discussion below, you could type in the command "ael2 reload" if you are using Asterisk version 1.2.x; but on "trunk" versions of asterisk, you would use "ael reload".

However, for those installing AEL2 into 1.2 versions of Asterisk, should note that 'ael2' and 'AEL2' should be used as-is.

The AEL2 parser (pbx_ael2.so) is completely separate from the module that parses extensions.conf (pbx_config.so). To use AEL2, the only thing that has to be done is the module pbx_ael2.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.ael2' in /etc/asterisk/. extensions.conf and extensions.ael and extensions.ael2 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.ael2

To reload extensions.ael2, the following command can be issued at the CLI.
  • CLI> ael2 reload

Debugging


Right at this moment, the following commands are available, but do nothing:

Enable AEL2 contexts debug
  • CLI> ael2 debug contexts

Enable AEL2 macros debug
  • CLI> ael2 debug macros

Enable AEL2 read debug
  • CLI> ael2 debug read

Enable AEL2 tokens debug
  • CLI> ael2 debug tokens

Disable AEL2 debug messages
  • CLI> ael2 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 "dialplan show" command in asterisk
  3. The standalone executable, "aelparse" built in the utils/ dir in the source.

You can also use the "aelparse" program to check your extensions.ael2 file before feeding it to asterisk. Wouldn't it be nice to eliminate most errors before giving the file to asterisk?


AELPARSE


You should never place code in /etc/extensions.ael without first testing it with aelparse!!

The aelparse program is built in the utils/ directory of the asterisk source. It is a standalone executable, and accepts only the options -d, -w, -q, and -n.

  • -d: Using this option says, instead of reading the file "extensions.ael2" (in the trunk version, that would be "extensions.ael") from /etc/asterisk (or wherever the config files are normally kept) aelparse will instead look in the current directory. This is very useful for testing your dialplan somewhere other than your production directory.
  • -w: Convert the input dialplan file (extensions.ael2) to extensions.conf format, and dump the file in the current directory under the file name "extensions.conf.aeldump". Why on earth would you ever want to do this? Well, you could take that file and use it on 1.2 or earlier asterisk systems! You don't have to program dialplans in extensions.conf format any more!
  • -n: Don't display the function calls to build the extensions in asterisk.
  • -q: (implies -n). Pretty much, only show warnings and errors.



General Notes about Syntax

Note that the syntax and style are 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 a good reason, and you should consider replacing the application call with an AEL2 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

Applications

The file /var/lib/asterisk/applist contains entries for over 140 applications, including arguments, the names of variables the application can or will set, the options, and the list of arguments, optional and required. If you use an application that is not in the list, you can simply add one, following the syntax of other entries.

Don't fall in love with the specs in the applist file. The format of the file, the entries themselves will change in time.

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.

NEW! You can use C-style comments that begin with '/*' and end with '*/' in AEL files. These comments can span lines, if so desired!
Example:
/* this is a
multiline comment that
makes little or no sense!
*/


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.


context default {
     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 AEL2 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.

NEW: You can add the callerID to the extension name for matching by adding a slash (/) followed by the CallerID to match after the extension name.



 context zoombo {
    819/7079953345 => { NoOp(hello, 3345); }
}


In the above, the 819/7079953345 extension will only be matched if the CallerID is 7079953345, and the dialed number is 819. Hopefully you have another 819 extension defined for all those who wish 819, that are not so lucky as to have 7079953345 as their CallerID!


Includes

Contexts can be included in other contexts. All included contexts are listed within a single block.



 context default {
     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.ael2"



An interesting property of the #include, is that you can use it almost anywhere in the .ael2 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).

NEW in 1.4 and trunk versions! You can use 'globbing' patterns (as in UNIX shell commands) to include multiple files with a single #include directive:


  1. include "t2/*.ael"
  2. include "t3/x[1-5]*"
  3. include "t4/abc?.ael"
  4. include "t5/{t1,t2,t3}.ael"


will include all the files that end with ".ael" in the t2 directory; all files in t3 that begin with x1, x2, x3, x4, or x5; and t4/abc.ael, and any other file in t4 that starts with abc, ends with ".ael", and has one other character in between; and t1.ael, t2.ael, and t3.ael in the t5 directory.

Dialplan Switches

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};
     }
 }



Ignorepat

ignorepat can be used to instruct channel drivers to not cancel dial tone upon receipt of a particular pattern. The most commonly used example is '9'.



 context outgoing {
     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. Global variables are set in their own block.



 globals {
     CONSOLE=Console/dsp;
     TRUNK=Zap/g2;
 }



Variables can be set within extensions as well.



 context foo {
     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)} !);
     }
 }



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;
          }
     }
 }



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.
NOTE: In for loops, both the init and increment expressions are embedded in a Set() call if an '=' is found anywhere in the expression. Otherwise, they are treated as app calls.

Conditionals

AEL2 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")
          {
               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 ~np~777[4-9]:~/np~
                     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: AEL2 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: AEL2 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: 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.
NOTE: NEW: if the expression in the switch(expr) is null, this will end up in the default case, if it is specified. If there is no matching case, in a switch statement, the dialplan will most likely terminate with a hangup.


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.

Examples:



 context loops {
     1 => {
          for (x=0; ${x} < 3; x=${x} + 1) {
               Verbose(x is ${x} !);
               if( ${x} == 2 && ${y} == 17)
                    break;
               if(${x} == 2 && ${y} == 16)
                    continue;  /*keep going */
          }
     }
     2 => {
          y=10;
          while (${y} >= 0) {
               Verbose(y is ${y} !);
                if (${z}<20)
                     break;
               y=${y}-1;
          }
     }
 }




NEW The semantics of the return keyword has changed as of 1 Sep in trunk. It used to simply generate a Goto() call to a label generated at the end of the current extension or Macro. Now, it simply generates a Return() application call. This will complement the fact that Macros will be implemented with Gosub() calls instead of Macro() calls from now on, to avoid restricted memory issues.

goto, jump, and labels

The goto statement is for directing execution to a label. The jump statement appears to be pretty much the same, but you can also specify only an extension and it will jump to the first prio in that extension, unlike goto which MUST have a label specified.

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 conflict 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 jumpexample {
     s => {
 begin:
          NoOp(Infinite Loop!  yay!);
          Wait(1);
          jump s;    // go to first priority in same extension
     }
     3 => {
             jump s,begin;   // go to the begin label in different extension
      }
      4 => {
             jump s,begin@jumpexample;  // overkill go to label in same context
      }
      5 => {
            jump 4@jumpexample; // jump to first prio in extension 4;
      }
 }

 context jumpexample2 {
      s =>  {
    end: 
            jump s@jumpexample;   // go to the s extension 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 AEL2 introduces the special label "1", which is the beginning context number for most extensions.
NOTE: NEW: As of 30 Mar2006, goto syntax now accepts commas in the place of vertical bars. You must either use vertical bars as separators, or commas. You cannot mix them. Thus, either "goto gotoexample|s|begin" or "goto gotoexample,s,begin", but NOT "goto gotoexample,s|begin"

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 referred to by that same name. A catch block can be specified to catch special extensions.

Alittle more about 'catch'. A macro is converted to a regular context, and all its code normally goes into the 's' extension.

Defining a catch allows you to define other extensions in the macro context. For example, you can define the 'h' extension
for hangups, and 'i' for invalid input, etc. — To 'catch' events that might happen during the execution of a macro.



 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 preceding 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");
     _6XXX => &std-exten(, "IAX2");
     _7XXX => &std-exten(${EXTEN},);
     _8XXX => &std-exten(,);
 }



NOTE: Until release 1.4 was forked, and in 1.4 and previous, AEL2 used the Macro() application to implement this construct in the dialplan. Macros have a serious limitation, in that one cannot recursively nest macro calls deeper than 7 levels, due to limitations on thread stack allocation sizes. To get around this limitation, from the time 1.4 is/was forked from trunk, the AEL2 compiler will implement macros with Gosub() application calls, which pose no such limits on call depth. The new methodology will be available on trunk and releases 1.6 and above.

Examples




 context demo {
     s => {
          Wait(1);
          Answer();
          TIMEOUT(digit)=5;
          TIMEOUT(response)=10;
 restart:
          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;
     }
     # => {
 hangup:
          Playback(demo-thanks);
          Hangup();
     }
     t => goto #|hangup;
     i => Playback(invalid);
 }



Semantic Checks


AEL2, after parsing, but before compiling, traverses the dialplan tree, and makes several checks:

  • (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...
  • Macro calls to non-existent macros.
  • Macro calls to contexts.
  • Macro calls with argument count not matching the definition.
  • application call to macro.
  • 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.
  • Calls to applications not in the "applist" database (installed in /var/lib/asterisk/applist" on most systems).
  • 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:
    • the time range has to have two times separated by a dash;
    • the times have to be in range of 0 to 24 hours.
    • The weekdays have to match the internal list, if they are provided;
    • the day of the month, if provided, must be in range of 1 to 31;
    • the month name or names have to match those in the internal list.
  • 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.
  • (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.
  • (New) Make sure that labels are unique within extensions/macros.
  • (New) Issue hard error if a break is not in a loop or switch case.
  • (New) Issue hard error if a continue is not in a loop.
  • Empty extensions will issue a warning.
  • In release 1.6, labels outside of extensions or Macros will generate an error.
  • In release 1.6, return statements will only be allowed in macro definitions.
  • In release 1.6, a warning is generated if a macro definition does not contain a return statement at the end of its contents. One is automatically inserted.
  • In release 1.6, a warning is generated if a goto is located in a macro, and the target is outside the macro.
  • In release 1.6, a warning is issued, if a context referenced in 'includes' can't be found.

Differences with the original version of AEL

  1. It reads in "/etc/asterisk/extensions.ael2", instead of extensions.ael (extensions.ael in the trunk version) (Only in the 1.2 patch)
  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_ael2.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.ael2 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. AEL2 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.
  21. 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, and C++ hackers feel at home here.

Hints and Bugs

''Note: I hope that this section will end up just being "hints" by the time it is included!

  • The safest way to check for a null strings is to say $[ "${x}" = "" ] The old way would do as shell scripts often do (but shouldn't, as that will yield the same problems as here), 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 to 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 AEL2 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.
  • Macro recursion depth. For large dialplans, or any other application that requires macros calling macros calling macros to 7 or more levels deep, keep in mind that there is a limit as to how deeply you can nest macro calls. That limit is 7 levels. This shortcoming in the Macro() app is not limited to usage by AEL. In general, the Macro() application has this limitation whether used by AEL, or just called straight from extensions.conf. The alternative is to use the Gosub application instead. One shortcoming of Gosub in releases 1.4 and previous, is that it did not accept any arguments. An enhancement to the Gosub() application will allow arguments to be passed and kept on a stack. This enhancement will be available in release 1.6, and is fundamental to AEL being able to use Gosub instead of Macro in generated dialplan code.
  • Spaces in Set(). You can get the most unexpected and interesting errors if you put spaces in your Set() arguments. "Set( a = 1)" is not equivalent to "Set(a=1)". The variable name in the first case is " a ", and includes the spaces before and after. You will have to insure that those spaces are present every time that variable is referenced. This is bad form!

The Full Power of AEL and AEL2

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. See Asterisk Functions
  • Expressions: An expression evaluation engine handles items wrapped inside $[...]. This includes some string manipulation facilities, arithmetic expressions, etc. See Asterisk Expressions.
  • 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. See Asterisk AGI.
  • 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. See Asterisk Variables.

Examples

Here you can find some examples on how to use AEL/AEL2 to create powerful dialplans: AEL Example Snippets.

AEL2 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.




 <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>
        | includes
        | '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' '{' '}'





AEL2 Bug Report History

(note: RFT means "Ready for Test" in the following:)
Bug ID Category Severity Status Last Updated Summary
0007721 PBX/pbx_ael minor closed (murf) 08-12-06 AEL2 can't parse hint with the and char (Zap/4&Zap5)
0007598 PBX/pbx_ael minor closed (murf) 08-07-06 labels for goto not working in macros
0007605 PBX/pbx_ael minor closed (murf) 08-04-06 AEL2 parser doesn't see macros in .conf language files
0007635 PBX/pbx_ael minor closed (murf) 08-01-06 For loop increment not working right
0007474 PBX/pbx_ael major closed (murf) 07-06-06 AEL2 not properly escaping commas
0007804 PBX/pbx_ael minorclosed (murf) 08-25-06 Various AEL crashes fixed
0008661 PBX/pbx_ael minor closed (murf) 12-29-06 delimiter
0007714 PBX/pbx_ael tweak closed (murf) 08-14-06 proposed alternative to switch-case-extensions
0007715 PBX/pbx-ael feature closed (murf) 08-10-06 support gosub & macros
0008004 PBX/pbx_ael minor closed (murf) 09-22-06 support AST_MODULE_LOAD_DECLINE
0008031 PBX/pbx_ael minor closed (murf) 09-26-06 AEL load on debian
0008090 PBX/pbx_ael crash closed (murf) 10-04-06 core dump loading AEL file
0008099 PBX/pbx_ael crash closed (murf) 10-05-06 core on empty extension
0008128 PBX/pbx_ael major closed (murf) 10-13-06 switch fallthru fails w/ patterns
0008836 PBX/pbx_ael minor closed (murf) 01-22-07 reload nukes subscription tables
0009184 PBX/pbx_ael major closed (murf) 03-02-07 comma/escape issues again.
0009228 PBX/pbx_ael major closed (murf) 03-21-07 macro vs. Gosub: what about macros in Dial?
0009316 PBX/pbx_ael minor closed (murf) 03-20-07 AEL security risk via switch-case-extensions
00009373 PBX/pbx_ael crash closed (murf) 2007-03-27 A segmentation fault occurs when two context have the same name
0009419 PBX/pbx_ael minor closed (murf) 2007-03-29 Global variables defined in extensions.ael not reloaded when doing a reload, other parts of dialplan are.
0009435 PBX/pbx_ael major closed (murf) 2007-04-02 (patch) "continue" doesn't perform the increment in "for" loops
0009452 PBX/pbx_ael minor closed (murf) 2007-06-21 AEL doesn't load default context at first time
0009508 PBX/pbx_ael minor closed (murf) 2007-07-03 ast_expr2.y fails on negative real numbers
0009623 PBX/pbx_ael minor closed (murf) 2007-05-03 AEL gives syntax error on @ in a switch
0009805 PBX/pbx_ael minor closed (murf) 2007-05-25 MACRO_EXTEN, MACRO_... is empty in AEL
0009881 PBX/pbx_ael minor closed (murf) 2007-06-05 Switch construct breaks the dialplan flow if it doesn't have a default case
0009883 PBX/pbx_ael minor closed (murf) 2007-06-05 AEL macros do not allow "includes" construct
0010322 PBX/pbx_ael minor closed (murf) 2007-07-27 (patch) Switch pbx_ael to generate code with comma delimiters
0010411 PBX/pbx_ael minor closed (murf) 2007-08-14 Variables in AEL2 context names
0010472 PBX/pbx_ael minor closed (murf) 2007-12-21 conf2ael improvements
0010498 PBX/pbx_ael minor closed (murf) 2007-08-27 pbx_ael crashs asterisk when unloading/loading
0010786 PBX/pbx_ael minor closed (murf) 2007-09-22 #include directive does not work
0010787 PBX/pbx_ael major closed (murf) 2007-11-01 Impossible to make optional macro arguments in 1.4
0010788 PBX/pbx_ael major closed (murf) 2007-09-22 for loops broken
0010789 PBX/pbx_ael major closed (murf) 2007-09-22 No way to set a variable without math
0010834 PBX/pbx_ael minor closed (murf) 2007-10-03 (patch) Empty vars in AEL2 switches lead to dropped calls
0010870 PBX/pbx_ael minor closed (murf) 2007-10-03 AEL & CUT
0011005 PBX/pbx_ael tweak closed (murf) 2007-10-24 glob #include uses base path different from non-glob #include
0011009 PBX/pbx_ael major closed (qwell) 2007-10-17 i extention not all times work
0011037 PBX/pbx_ael minor closed (murf) 2007-10-23 hint to monitor meetme using ael syntax
0011086 PBX/pbx_ael feature closed (murf) 2007-10-30 avoid nonexistent context warnings when loading AEL
0011287 PBX/pbx_ael minor closed (murf) 2007-12-20 Failed compile pbx_ael on solaris
0011329 PBX/pbx_ael minor closed (murf) 2008-01-11 AEL macro argument variables aren't properly quoted when set
0011903 PBX/pbx_ael minor closed (murf) 2008-03-18 Wrong error file shown if includes used
0012238 PBX/pbx_ael major closed (murf) 2008-03-18 include block not loaded on asterisk start
0012302 PBX/pbx_ael major closed (murf) 2008-03-27 ${EXTEN} variable is corrupted within switch()
0012303 PBX/pbx_ael minor closed (murf)2008-03-26 digit extension pattern matches also dialed string, that looks like pattern itself
0012467 PBX/pbx_ael minor closed (murf) 2008-04-21 Can't add 0 in front of function result within if() block of AEL
0013046 PBX/pbx_ael crash closed (murf) 2008-07-30 Asterisk segfault at startup/ael reload
0013051 PBX/pbx_ael minor closed (murf)2008-07-30 SIPPEER isn't working inside REGEX and IF
0013061 PBX/pbx_ael minor closed (mvanbaak) 2008-07-12 random appears to be broken
0013090 PBX/pbx_ael minor closed (murf) 2008-07-16 multiple "infinite loop" messages being output by AEL compiler.
0013197 PBX/pbx_ael minor closed (murf) 2008-07-30 STAT() inside IF() doesn't work
0013236 PBX/pbx_ael major closed (murf) 2008-08-07 doesn't set ~~EXTEN~~ on the rigth place when a switch statement has been found
0013305 PBX/pbx_ael crash closed (murf) 2008-08-20 AEL while loop - high cpu usage and crash (private)
0013339 PBX/pbx_ael feature closed (murf) 2008-09-08 AEL parser should trim RHS of an assignment
0013357 PBX/pbx_ael minor closed (murf) 2008-09-04 AEL doesn't like using dialplan function call as argument for 'jump' statement
0013407 PBX/pbx_ael minor closed 2008-09-02 AEL switches inside macros not working like extensions.conf's ones
0013416 PBX/pbx_ael minor closed (murf) 2008-09-04 Function CUT doesn't work if passed as parameter to macro in AEL
0013765 PBX/pbx_ael minor closed (murf) 2008-10-24 after return from child context ARG is cleared



AEL2 Enhancement Request History


1. (Originally filed as bug 7606) Generate extensions.conf format ouput from aelparse
Status: Finished, -w option added to aelparse
Description: Standalone AEL2 compiler
Requested by: Sherwood McGowan


2. (Originally filed as bug 7638) Reverse compiler from extensions.conf format to AEL format
Status: In Progress
Description: how hard would it be to write a reverse compiler for translating existing dialplan files into .ael/.ael2 files?
Requested by: Sherwood McGowan
Branch: http://svn.digium.com/svn/asterisk/team/murf/bug_7638
Discussion:
Murf:
Now, as to the reverse, converting extensions.conf to AEL: it is possible, but a lot of work. Think of it this
way: what you are asking for is a reverse compiler. Given, for example, an assembly file, produce the equivalent
C++ code that might have produced that assembly file. It's a bit easier (relatively) if you have some nice constraints,
say you know the assembly file was generated with g++, and only one particular version of it, at that. You could then
pattern match certain sequences in the assembler output back into C++ structures with reasonable confidence.
Others, you just synthesize something that would work.

But, incoming extensions.conf code does not follow such narrow limits.It would not be uncommon to see subtle tricks being
used to accomplish some goal, which would be difficult for a human to interpret, let alone a program. That being said, most
extensions code **is** pretty simple, and in most cases simple heuristics could cover a great majority of the code. Stuff that
it can't grok could simply be copied verbatim into the output for a human to complete. Most of the formatting and easy stuff
could be done by the translator and speed up the job of conversion tremendously.

I'm all for it. Such a program could help reduce the cost of converting to AEL, and aid the whole community greatly.
— 29 Dec 2006; I've got the basics lined up for experiment 1 of this code, using the existing asterisk config file reading code
to pull in the extensions.conf; Experiment 2 will be to use benjk's ini file parser-- but it looks like that would be a heck of a
lot more work to incorporate. So I'll go the easy route first. (And it wasn't that easy).
— 22 Mar 2007; Finally, I've refactored the code to get a standalone config file processor that yields the internal structs
used by asterisk. The reverse compiler generates contexts, extensions, priorities, with no real translation of gotos, loops,
etc. But it's a crude start.

3. Add a quiet option to aelparse-- it's too noisy!!!
Status: Finished: added -q option to aelparse
Description: I can't find the errors amid all the progress messages!
Requested by: Sherwood McGowan

4. (originally filed as bug 7715) Replace the Gosub command with an internal AEL2 command?
Status: Completed in Trunk (will appear in 1.6).
Description: Replace the Gosub command with an internal AEL2 command?
Requested by: Sherwood McGowan
Branch: team/murf/macrogosub
Discussion:
Sherwood McGowan:
I like the replacement goto statement, and jump is nice, but how hard would it be to create a replacement for Gosub?
I've been having issues with macros needing to call other macros (I use 'em like functions) but if there's more than one or two
deep, it won't work, and it's a pain. I'd love to see the ability to call a label or extension within another context with a return
address saved so that when the end of the called context is hit, we return to the calling context.....
Another thing that would be AWESOME would be to make the AEL2 version of subroutine handling allow supplying arguments
to the subroutine, kind of like a merge between the Asterisk macros and Gosub called contexts.... Basically, I'm dying for the
ability to define something like a function that when called, will save a return address AND won't have the problems that Macros
have of not always working when they're called from within another Macro/Function.....

murf:
Macro **is** the right idea for this sort of thing-- let's spend some time and see why it's not behaving right for you. We can open
a bug, and supply a test case?

murf:
I am modifying the code in pbx_ael.c to generate Gosub() calls instead of Macro() calls.
This is made possible by an upgrade provided by Corydon(Tilghman Lesher) to Gosub, so
that it can be called with arguments. The reason? The Application Macro() uses up a fair
amount of stack space to do its thing. If you go more than 7 levels deep in macros (via recursion
or subdivision of tasks) then safeguards will kick in and the calls will terminate early to keep
asterisk from crashing. Gosubs don't traverse the macro def "by hand" like Macro does, and
will allow a bit more "headroom". We are hoping about 20 levels of depth. The only problem
is that the coder has to explicitly include "return" statements in their macros, or you'll have
pretty big problems!!!

Otherwise, there is little difference with previous usage. Macros are still declared the same
in AEL, they are just compiled differently underneath now. Just make sure to put the return;
statement in the right spots! They will be converted to Return() application calls.


5. (originally filed as bug 7714) Alternative to naming new extensions when building a switch conditionals
Status: ON HOLD Not going to do this as yet; the current method seems better at this time.
Description: Don't break switch cases into separate extensions?
Requested by: Sherwood McGowan
Branch: not yet
Discussion:
Sherwood McGowan:
The existing method of using multiple extensions to separate cases within the switch statement is not the best. Have you thought about
just using n(labelname) directives? See the additional information box for my examples/comparisons.
the n(label) labels are GREAT, and you can reference them just like we do within the AEL code :)
AEL2's outputted code: (excess removed)

           [macro-endcall]
           exten => s,1,Goto(sw-10-${calltype}|1)
           exten => s,2,NoOp(Finish switch-endcall-10)
           exten => sw-10-in,1,Macro(setdispo|${callid})
           exten => sw-10-out,1,Macro(dbconn|cdr|open)

           A suggested way to do this:

           exten => s,1,Goto(${EXTEN}|${calltype})
           exten => s,2,NoOp(Finish switch-endcall)

           exten => s,n(in),Macro(setdispo|${callid})
           exten => s,n,Return

           exten => s,n(out),Macro(dbconn|cdr|open)
           exten => s,n,Return

Murf:
As to the switch thing, as I call it...

I have definitely thought much on this issue while I was coding up AEL2...
but first and foremost, that was the way (using separate
extensions) the original AEL implemented switches, and
I didn't want to rock the boat.

Next, it does have a certain appeal. Besides the normal case and default
statements you can put in a switch, there is also the "pattern"
statement, which basically is just like a case, except that it prepends an
underscore in front of the extension name, allowing a pattern extension,
which is something you just cannot get with a simple switch...

        switch(${NUMTODIAL})
        {
                case 911:
                        verbose("Hey, an emergency!");
                        break;
                case 411:
                        verbose("What, you need information?");
                        break;
                pattern 307XXXXXXX:
                        verbose("Why on earth would you want to call Wyoming?");
                        break;
                pattern 801XXXXXXX:
                        verbose("Why on earth would anyone call Utah?");
                        break;
                default:
                        verbose(Ah, a discriminating caller, indeed!);
                        break;
        }

I guess if labels could define patterns, you could get the same affect.
Until then, you have to admit, the pattern statement is a nice feature.

Sherwood McGowan:

Hey, you're right! I completely forgot about the pattern matching abilities.
Your way definitely makes much more sense :)

pkempgen (via the Mantis bug tracker, 9316, 03-19-07 15:29):

You never know for sure that ${EXTEN} is what you
think it is because it might happen to be inside
a different extension - and Dial(SIP/sw-87-user) is
not what I expected :)

6. Multiline Commenting
Status: finished
Description: Enhance the AEL2 parser to allow for a multiline comment keyword set.
Requested by: Sherwood McGowan
Branch: already committed to trunk
Discussion: C style comments are now supported: example: /* this is a comment */

7. New keyword: subroutine
Status: Will not be done, sort of... see above about Macro/Gosub
Description: Enhancement to the context keyword, if context is prefaced by subroutine force 'return' statements within that context to use the subroutine command Return.
Requested by: Sherwood McGowan
Branch: not yet
Discussion: Due to memory constraints built into asterisk, we will implement macros as gosubs instead. See above.

8. #Include wildcards
Status: Finished
Description: Allow #including wildcards like the rest of the Asterisk configs. This allows things like: #include "people/*.conf"
Requested by: Luke-Jr
Discussion/Notes:
Sherwood McGowan: I know that #include is already implemented, haven't personally tested wildcards because I wasn't aware you could wildcard includes at all. Will test or check with murph
murf: I finished this. Is it in just trunk, or did I add it to 1.4 also?

9. Optimize AEL generated extensions.conf code by leaving out the NoOp() markers
Status: Not Yet Begun
Description: The NoOps() that are output to mark the end of if, while, switch, etc statements are unnecc. — please remove them.
Requested by: yarns (bug 13422)
Discussion/Notes:

10. allow macros to have variable numbers of arguments.
Status: Not yet begun.
Description: Allow macros to have a variable number of arguments
Requested by: SEVERAL
Discussion/Notes: I've been discouraging users from using ${ARG1}, ${ARG2}, etc inside macros, in favor of using the name of the argument instead... eg. ${name}.
Since Gosub (in 1.6.x./trunk) is being used, users can use ${ARGx} to access such arguments, but the AEL parser will reject extra arguments if included,
and complain about missing arguments if you include too few. I've been thinking about including a new keyword, "...", which, if included as the last
argument in the macro definition, would turn off such checking for that and following arguments.


This page accessed 186397 times!

Asterisk Extension Language v.2

AEL v.2 is intended to provide an actual programming language that can be used to write an Asterisk dialplan. It
further extends AEL, and provides more flexible syntax, better error messages, and some missing functionality.
AEL v.2 is a new version of the AEL compiler, written by Steve Murphy. It was originally introduced as a large asterisk patch in the Asterisk bug database. See: Bug 6021.

AEL is really the merger of 4 different 'languages', or syntaxes:
  • The first and most obvious is the AEL v.2 syntax itself. 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/AEL2, and is not used directly in AEL/AEL2, is the Extension Language Syntax. The extension language is what you see in extensions.conf, and AEL2 compiles the higher level AEL2 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. There are references to Wiki pages covering these, down in the "The Power of AEL" section below.


AEL2 Announcements and News


NEW: For 1.2 users who'd like to have access to AEL2, I've got a better idea. The old way was to "svn diff http://svn.digium.com/svn/asterisk/branches/1.2 http://svn.digium.com/svn/asterisk/team/murf/AEL2-1.2 > AEL.patch", and then apply the patch to your current 1.2 based source with "patch -p0 < AEL.patch"; this is a bit of a hassle, as that branch is brought back into sync with 1.2 on a weekly or monthly basis. An easier approach would be to:

svn co http://svn.digium.com/svn/asterisk/team/murf/AEL2-1.2/patches

And then, in the 1.2 source directory, you can simply:

patch -p0 < patches/diffs.AEL2.patch

which will apply the changes.

Good news for all you AEL2 fans out there! A couple of weeks ago, I dropped a bug in Murf's ear about adding an option to dump the parsed AEL code into a file, basically a "compile" option. Well, he coded it, we've both tested it, and it seems solid, so it's now in the trunk! just use aelparse as usual and add the -w option if you want to dump the file extensions.conf.aeldump. Handy for coding in AEL2 but still being able to easily deliver to clients who aren't able or willing to use AEL2 in their Asterisk implementation! -Sherwood McGowan

AEL2 has replaced AEL in the trunk version of Asterisk. You now get it automatically when you svn checkout asterisk! It's upward compatible with the original AEL, but adds several features, and removes some limitations.

  • Please note that AEL (See Asterisk AEL ) is stepping out of the "Experimental" stage. Please help us to make it solid and useful by using it!!

The Asterisk Extension Language v.2

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 in the macro are executed until a return statement is executed. 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


In the following discussion, if you are using the "trunk" version of Asterisk, please note that AEL2 replaced the original AEL implementation.
So, anywhere in the following discussion where "ael2" or "AEL2" is mentioned, you would use "ael" or "AEL" instead. For instance, in the
discussion below, you could type in the command "ael2 reload" if you are using Asterisk version 1.2.x; but on "trunk" versions of asterisk, you would use "ael reload".

However, for those installing AEL2 into 1.2 versions of Asterisk, should note that 'ael2' and 'AEL2' should be used as-is.

The AEL2 parser (pbx_ael2.so) is completely separate from the module that parses extensions.conf (pbx_config.so). To use AEL2, the only thing that has to be done is the module pbx_ael2.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.ael2' in /etc/asterisk/. extensions.conf and extensions.ael and extensions.ael2 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.ael2

To reload extensions.ael2, the following command can be issued at the CLI.
  • CLI> ael2 reload

Debugging


Right at this moment, the following commands are available, but do nothing:

Enable AEL2 contexts debug
  • CLI> ael2 debug contexts

Enable AEL2 macros debug
  • CLI> ael2 debug macros

Enable AEL2 read debug
  • CLI> ael2 debug read

Enable AEL2 tokens debug
  • CLI> ael2 debug tokens

Disable AEL2 debug messages
  • CLI> ael2 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 "dialplan show" command in asterisk
  3. The standalone executable, "aelparse" built in the utils/ dir in the source.

You can also use the "aelparse" program to check your extensions.ael2 file before feeding it to asterisk. Wouldn't it be nice to eliminate most errors before giving the file to asterisk?


AELPARSE


You should never place code in /etc/extensions.ael without first testing it with aelparse!!

The aelparse program is built in the utils/ directory of the asterisk source. It is a standalone executable, and accepts only the options -d, -w, -q, and -n.

  • -d: Using this option says, instead of reading the file "extensions.ael2" (in the trunk version, that would be "extensions.ael") from /etc/asterisk (or wherever the config files are normally kept) aelparse will instead look in the current directory. This is very useful for testing your dialplan somewhere other than your production directory.
  • -w: Convert the input dialplan file (extensions.ael2) to extensions.conf format, and dump the file in the current directory under the file name "extensions.conf.aeldump". Why on earth would you ever want to do this? Well, you could take that file and use it on 1.2 or earlier asterisk systems! You don't have to program dialplans in extensions.conf format any more!
  • -n: Don't display the function calls to build the extensions in asterisk.
  • -q: (implies -n). Pretty much, only show warnings and errors.



General Notes about Syntax

Note that the syntax and style are 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 a good reason, and you should consider replacing the application call with an AEL2 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

Applications

The file /var/lib/asterisk/applist contains entries for over 140 applications, including arguments, the names of variables the application can or will set, the options, and the list of arguments, optional and required. If you use an application that is not in the list, you can simply add one, following the syntax of other entries.

Don't fall in love with the specs in the applist file. The format of the file, the entries themselves will change in time.

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.

NEW! You can use C-style comments that begin with '/*' and end with '*/' in AEL files. These comments can span lines, if so desired!
Example:
/* this is a
multiline comment that
makes little or no sense!
*/


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.


context default {
     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 AEL2 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.

NEW: You can add the callerID to the extension name for matching by adding a slash (/) followed by the CallerID to match after the extension name.



 context zoombo {
    819/7079953345 => { NoOp(hello, 3345); }
}


In the above, the 819/7079953345 extension will only be matched if the CallerID is 7079953345, and the dialed number is 819. Hopefully you have another 819 extension defined for all those who wish 819, that are not so lucky as to have 7079953345 as their CallerID!


Includes

Contexts can be included in other contexts. All included contexts are listed within a single block.



 context default {
     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.ael2"



An interesting property of the #include, is that you can use it almost anywhere in the .ael2 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).

NEW in 1.4 and trunk versions! You can use 'globbing' patterns (as in UNIX shell commands) to include multiple files with a single #include directive:


  1. include "t2/*.ael"
  2. include "t3/x[1-5]*"
  3. include "t4/abc?.ael"
  4. include "t5/{t1,t2,t3}.ael"


will include all the files that end with ".ael" in the t2 directory; all files in t3 that begin with x1, x2, x3, x4, or x5; and t4/abc.ael, and any other file in t4 that starts with abc, ends with ".ael", and has one other character in between; and t1.ael, t2.ael, and t3.ael in the t5 directory.

Dialplan Switches

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};
     }
 }



Ignorepat

ignorepat can be used to instruct channel drivers to not cancel dial tone upon receipt of a particular pattern. The most commonly used example is '9'.



 context outgoing {
     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. Global variables are set in their own block.



 globals {
     CONSOLE=Console/dsp;
     TRUNK=Zap/g2;
 }



Variables can be set within extensions as well.



 context foo {
     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)} !);
     }
 }



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;
          }
     }
 }



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.
NOTE: In for loops, both the init and increment expressions are embedded in a Set() call if an '=' is found anywhere in the expression. Otherwise, they are treated as app calls.

Conditionals

AEL2 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")
          {
               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 ~np~777[4-9]:~/np~
                     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: AEL2 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: AEL2 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: 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.
NOTE: NEW: if the expression in the switch(expr) is null, this will end up in the default case, if it is specified. If there is no matching case, in a switch statement, the dialplan will most likely terminate with a hangup.


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.

Examples:



 context loops {
     1 => {
          for (x=0; ${x} < 3; x=${x} + 1) {
               Verbose(x is ${x} !);
               if( ${x} == 2 && ${y} == 17)
                    break;
               if(${x} == 2 && ${y} == 16)
                    continue;  /*keep going */
          }
     }
     2 => {
          y=10;
          while (${y} >= 0) {
               Verbose(y is ${y} !);
                if (${z}<20)
                     break;
               y=${y}-1;
          }
     }
 }




NEW The semantics of the return keyword has changed as of 1 Sep in trunk. It used to simply generate a Goto() call to a label generated at the end of the current extension or Macro. Now, it simply generates a Return() application call. This will complement the fact that Macros will be implemented with Gosub() calls instead of Macro() calls from now on, to avoid restricted memory issues.

goto, jump, and labels

The goto statement is for directing execution to a label. The jump statement appears to be pretty much the same, but you can also specify only an extension and it will jump to the first prio in that extension, unlike goto which MUST have a label specified.

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 conflict 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 jumpexample {
     s => {
 begin:
          NoOp(Infinite Loop!  yay!);
          Wait(1);
          jump s;    // go to first priority in same extension
     }
     3 => {
             jump s,begin;   // go to the begin label in different extension
      }
      4 => {
             jump s,begin@jumpexample;  // overkill go to label in same context
      }
      5 => {
            jump 4@jumpexample; // jump to first prio in extension 4;
      }
 }

 context jumpexample2 {
      s =>  {
    end: 
            jump s@jumpexample;   // go to the s extension 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 AEL2 introduces the special label "1", which is the beginning context number for most extensions.
NOTE: NEW: As of 30 Mar2006, goto syntax now accepts commas in the place of vertical bars. You must either use vertical bars as separators, or commas. You cannot mix them. Thus, either "goto gotoexample|s|begin" or "goto gotoexample,s,begin", but NOT "goto gotoexample,s|begin"

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 referred to by that same name. A catch block can be specified to catch special extensions.

Alittle more about 'catch'. A macro is converted to a regular context, and all its code normally goes into the 's' extension.

Defining a catch allows you to define other extensions in the macro context. For example, you can define the 'h' extension
for hangups, and 'i' for invalid input, etc. — To 'catch' events that might happen during the execution of a macro.



 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 preceding 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");
     _6XXX => &std-exten(, "IAX2");
     _7XXX => &std-exten(${EXTEN},);
     _8XXX => &std-exten(,);
 }



NOTE: Until release 1.4 was forked, and in 1.4 and previous, AEL2 used the Macro() application to implement this construct in the dialplan. Macros have a serious limitation, in that one cannot recursively nest macro calls deeper than 7 levels, due to limitations on thread stack allocation sizes. To get around this limitation, from the time 1.4 is/was forked from trunk, the AEL2 compiler will implement macros with Gosub() application calls, which pose no such limits on call depth. The new methodology will be available on trunk and releases 1.6 and above.

Examples




 context demo {
     s => {
          Wait(1);
          Answer();
          TIMEOUT(digit)=5;
          TIMEOUT(response)=10;
 restart:
          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;
     }
     # => {
 hangup:
          Playback(demo-thanks);
          Hangup();
     }
     t => goto #|hangup;
     i => Playback(invalid);
 }



Semantic Checks


AEL2, after parsing, but before compiling, traverses the dialplan tree, and makes several checks:

  • (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...
  • Macro calls to non-existent macros.
  • Macro calls to contexts.
  • Macro calls with argument count not matching the definition.
  • application call to macro.
  • 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.
  • Calls to applications not in the "applist" database (installed in /var/lib/asterisk/applist" on most systems).
  • 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:
    • the time range has to have two times separated by a dash;
    • the times have to be in range of 0 to 24 hours.
    • The weekdays have to match the internal list, if they are provided;
    • the day of the month, if provided, must be in range of 1 to 31;
    • the month name or names have to match those in the internal list.
  • 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.
  • (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.
  • (New) Make sure that labels are unique within extensions/macros.
  • (New) Issue hard error if a break is not in a loop or switch case.
  • (New) Issue hard error if a continue is not in a loop.
  • Empty extensions will issue a warning.
  • In release 1.6, labels outside of extensions or Macros will generate an error.
  • In release 1.6, return statements will only be allowed in macro definitions.
  • In release 1.6, a warning is generated if a macro definition does not contain a return statement at the end of its contents. One is automatically inserted.
  • In release 1.6, a warning is generated if a goto is located in a macro, and the target is outside the macro.
  • In release 1.6, a warning is issued, if a context referenced in 'includes' can't be found.

Differences with the original version of AEL

  1. It reads in "/etc/asterisk/extensions.ael2", instead of extensions.ael (extensions.ael in the trunk version) (Only in the 1.2 patch)
  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_ael2.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.ael2 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. AEL2 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.
  21. 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, and C++ hackers feel at home here.

Hints and Bugs

''Note: I hope that this section will end up just being "hints" by the time it is included!

  • The safest way to check for a null strings is to say $[ "${x}" = "" ] The old way would do as shell scripts often do (but shouldn't, as that will yield the same problems as here), 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 to 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 AEL2 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.
  • Macro recursion depth. For large dialplans, or any other application that requires macros calling macros calling macros to 7 or more levels deep, keep in mind that there is a limit as to how deeply you can nest macro calls. That limit is 7 levels. This shortcoming in the Macro() app is not limited to usage by AEL. In general, the Macro() application has this limitation whether used by AEL, or just called straight from extensions.conf. The alternative is to use the Gosub application instead. One shortcoming of Gosub in releases 1.4 and previous, is that it did not accept any arguments. An enhancement to the Gosub() application will allow arguments to be passed and kept on a stack. This enhancement will be available in release 1.6, and is fundamental to AEL being able to use Gosub instead of Macro in generated dialplan code.
  • Spaces in Set(). You can get the most unexpected and interesting errors if you put spaces in your Set() arguments. "Set( a = 1)" is not equivalent to "Set(a=1)". The variable name in the first case is " a ", and includes the spaces before and after. You will have to insure that those spaces are present every time that variable is referenced. This is bad form!

The Full Power of AEL and AEL2

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. See Asterisk Functions
  • Expressions: An expression evaluation engine handles items wrapped inside $[...]. This includes some string manipulation facilities, arithmetic expressions, etc. See Asterisk Expressions.
  • 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. See Asterisk AGI.
  • 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. See Asterisk Variables.

Examples

Here you can find some examples on how to use AEL/AEL2 to create powerful dialplans: AEL Example Snippets.

AEL2 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.




 <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>
        | includes
        | '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' '{' '}'





AEL2 Bug Report History

(note: RFT means "Ready for Test" in the following:)
Bug ID Category Severity Status Last Updated Summary
0007721 PBX/pbx_ael minor closed (murf) 08-12-06 AEL2 can't parse hint with the and char (Zap/4&Zap5)
0007598 PBX/pbx_ael minor closed (murf) 08-07-06 labels for goto not working in macros
0007605 PBX/pbx_ael minor closed (murf) 08-04-06 AEL2 parser doesn't see macros in .conf language files
0007635 PBX/pbx_ael minor closed (murf) 08-01-06 For loop increment not working right
0007474 PBX/pbx_ael major closed (murf) 07-06-06 AEL2 not properly escaping commas
0007804 PBX/pbx_ael minorclosed (murf) 08-25-06 Various AEL crashes fixed
0008661 PBX/pbx_ael minor closed (murf) 12-29-06 delimiter
0007714 PBX/pbx_ael tweak closed (murf) 08-14-06 proposed alternative to switch-case-extensions
0007715 PBX/pbx-ael feature closed (murf) 08-10-06 support gosub & macros
0008004 PBX/pbx_ael minor closed (murf) 09-22-06 support AST_MODULE_LOAD_DECLINE
0008031 PBX/pbx_ael minor closed (murf) 09-26-06 AEL load on debian
0008090 PBX/pbx_ael crash closed (murf) 10-04-06 core dump loading AEL file
0008099 PBX/pbx_ael crash closed (murf) 10-05-06 core on empty extension
0008128 PBX/pbx_ael major closed (murf) 10-13-06 switch fallthru fails w/ patterns
0008836 PBX/pbx_ael minor closed (murf) 01-22-07 reload nukes subscription tables
0009184 PBX/pbx_ael major closed (murf) 03-02-07 comma/escape issues again.
0009228 PBX/pbx_ael major closed (murf) 03-21-07 macro vs. Gosub: what about macros in Dial?
0009316 PBX/pbx_ael minor closed (murf) 03-20-07 AEL security risk via switch-case-extensions
00009373 PBX/pbx_ael crash closed (murf) 2007-03-27 A segmentation fault occurs when two context have the same name
0009419 PBX/pbx_ael minor closed (murf) 2007-03-29 Global variables defined in extensions.ael not reloaded when doing a reload, other parts of dialplan are.
0009435 PBX/pbx_ael major closed (murf) 2007-04-02 (patch) "continue" doesn't perform the increment in "for" loops
0009452 PBX/pbx_ael minor closed (murf) 2007-06-21 AEL doesn't load default context at first time
0009508 PBX/pbx_ael minor closed (murf) 2007-07-03 ast_expr2.y fails on negative real numbers
0009623 PBX/pbx_ael minor closed (murf) 2007-05-03 AEL gives syntax error on @ in a switch
0009805 PBX/pbx_ael minor closed (murf) 2007-05-25 MACRO_EXTEN, MACRO_... is empty in AEL
0009881 PBX/pbx_ael minor closed (murf) 2007-06-05 Switch construct breaks the dialplan flow if it doesn't have a default case
0009883 PBX/pbx_ael minor closed (murf) 2007-06-05 AEL macros do not allow "includes" construct
0010322 PBX/pbx_ael minor closed (murf) 2007-07-27 (patch) Switch pbx_ael to generate code with comma delimiters
0010411 PBX/pbx_ael minor closed (murf) 2007-08-14 Variables in AEL2 context names
0010472 PBX/pbx_ael minor closed (murf) 2007-12-21 conf2ael improvements
0010498 PBX/pbx_ael minor closed (murf) 2007-08-27 pbx_ael crashs asterisk when unloading/loading
0010786 PBX/pbx_ael minor closed (murf) 2007-09-22 #include directive does not work
0010787 PBX/pbx_ael major closed (murf) 2007-11-01 Impossible to make optional macro arguments in 1.4
0010788 PBX/pbx_ael major closed (murf) 2007-09-22 for loops broken
0010789 PBX/pbx_ael major closed (murf) 2007-09-22 No way to set a variable without math
0010834 PBX/pbx_ael minor closed (murf) 2007-10-03 (patch) Empty vars in AEL2 switches lead to dropped calls
0010870 PBX/pbx_ael minor closed (murf) 2007-10-03 AEL & CUT
0011005 PBX/pbx_ael tweak closed (murf) 2007-10-24 glob #include uses base path different from non-glob #include
0011009 PBX/pbx_ael major closed (qwell) 2007-10-17 i extention not all times work
0011037 PBX/pbx_ael minor closed (murf) 2007-10-23 hint to monitor meetme using ael syntax
0011086 PBX/pbx_ael feature closed (murf) 2007-10-30 avoid nonexistent context warnings when loading AEL
0011287 PBX/pbx_ael minor closed (murf) 2007-12-20 Failed compile pbx_ael on solaris
0011329 PBX/pbx_ael minor closed (murf) 2008-01-11 AEL macro argument variables aren't properly quoted when set
0011903 PBX/pbx_ael minor closed (murf) 2008-03-18 Wrong error file shown if includes used
0012238 PBX/pbx_ael major closed (murf) 2008-03-18 include block not loaded on asterisk start
0012302 PBX/pbx_ael major closed (murf) 2008-03-27 ${EXTEN} variable is corrupted within switch()
0012303 PBX/pbx_ael minor closed (murf)2008-03-26 digit extension pattern matches also dialed string, that looks like pattern itself
0012467 PBX/pbx_ael minor closed (murf) 2008-04-21 Can't add 0 in front of function result within if() block of AEL
0013046 PBX/pbx_ael crash closed (murf) 2008-07-30 Asterisk segfault at startup/ael reload
0013051 PBX/pbx_ael minor closed (murf)2008-07-30 SIPPEER isn't working inside REGEX and IF
0013061 PBX/pbx_ael minor closed (mvanbaak) 2008-07-12 random appears to be broken
0013090 PBX/pbx_ael minor closed (murf) 2008-07-16 multiple "infinite loop" messages being output by AEL compiler.
0013197 PBX/pbx_ael minor closed (murf) 2008-07-30 STAT() inside IF() doesn't work
0013236 PBX/pbx_ael major closed (murf) 2008-08-07 doesn't set ~~EXTEN~~ on the rigth place when a switch statement has been found
0013305 PBX/pbx_ael crash closed (murf) 2008-08-20 AEL while loop - high cpu usage and crash (private)
0013339 PBX/pbx_ael feature closed (murf) 2008-09-08 AEL parser should trim RHS of an assignment
0013357 PBX/pbx_ael minor closed (murf) 2008-09-04 AEL doesn't like using dialplan function call as argument for 'jump' statement
0013407 PBX/pbx_ael minor closed 2008-09-02 AEL switches inside macros not working like extensions.conf's ones
0013416 PBX/pbx_ael minor closed (murf) 2008-09-04 Function CUT doesn't work if passed as parameter to macro in AEL
0013765 PBX/pbx_ael minor closed (murf) 2008-10-24 after return from child context ARG is cleared



AEL2 Enhancement Request History


1. (Originally filed as bug 7606) Generate extensions.conf format ouput from aelparse
Status: Finished, -w option added to aelparse
Description: Standalone AEL2 compiler
Requested by: Sherwood McGowan


2. (Originally filed as bug 7638) Reverse compiler from extensions.conf format to AEL format
Status: In Progress
Description: how hard would it be to write a reverse compiler for translating existing dialplan files into .ael/.ael2 files?
Requested by: Sherwood McGowan
Branch: http://svn.digium.com/svn/asterisk/team/murf/bug_7638
Discussion:
Murf:
Now, as to the reverse, converting extensions.conf to AEL: it is possible, but a lot of work. Think of it this
way: what you are asking for is a reverse compiler. Given, for example, an assembly file, produce the equivalent
C++ code that might have produced that assembly file. It's a bit easier (relatively) if you have some nice constraints,
say you know the assembly file was generated with g++, and only one particular version of it, at that. You could then
pattern match certain sequences in the assembler output back into C++ structures with reasonable confidence.
Others, you just synthesize something that would work.

But, incoming extensions.conf code does not follow such narrow limits.It would not be uncommon to see subtle tricks being
used to accomplish some goal, which would be difficult for a human to interpret, let alone a program. That being said, most
extensions code **is** pretty simple, and in most cases simple heuristics could cover a great majority of the code. Stuff that
it can't grok could simply be copied verbatim into the output for a human to complete. Most of the formatting and easy stuff
could be done by the translator and speed up the job of conversion tremendously.

I'm all for it. Such a program could help reduce the cost of converting to AEL, and aid the whole community greatly.
— 29 Dec 2006; I've got the basics lined up for experiment 1 of this code, using the existing asterisk config file reading code
to pull in the extensions.conf; Experiment 2 will be to use benjk's ini file parser-- but it looks like that would be a heck of a
lot more work to incorporate. So I'll go the easy route first. (And it wasn't that easy).
— 22 Mar 2007; Finally, I've refactored the code to get a standalone config file processor that yields the internal structs
used by asterisk. The reverse compiler generates contexts, extensions, priorities, with no real translation of gotos, loops,
etc. But it's a crude start.

3. Add a quiet option to aelparse-- it's too noisy!!!
Status: Finished: added -q option to aelparse
Description: I can't find the errors amid all the progress messages!
Requested by: Sherwood McGowan

4. (originally filed as bug 7715) Replace the Gosub command with an internal AEL2 command?
Status: Completed in Trunk (will appear in 1.6).
Description: Replace the Gosub command with an internal AEL2 command?
Requested by: Sherwood McGowan
Branch: team/murf/macrogosub
Discussion:
Sherwood McGowan:
I like the replacement goto statement, and jump is nice, but how hard would it be to create a replacement for Gosub?
I've been having issues with macros needing to call other macros (I use 'em like functions) but if there's more than one or two
deep, it won't work, and it's a pain. I'd love to see the ability to call a label or extension within another context with a return
address saved so that when the end of the called context is hit, we return to the calling context.....
Another thing that would be AWESOME would be to make the AEL2 version of subroutine handling allow supplying arguments
to the subroutine, kind of like a merge between the Asterisk macros and Gosub called contexts.... Basically, I'm dying for the
ability to define something like a function that when called, will save a return address AND won't have the problems that Macros
have of not always working when they're called from within another Macro/Function.....

murf:
Macro **is** the right idea for this sort of thing-- let's spend some time and see why it's not behaving right for you. We can open
a bug, and supply a test case?

murf:
I am modifying the code in pbx_ael.c to generate Gosub() calls instead of Macro() calls.
This is made possible by an upgrade provided by Corydon(Tilghman Lesher) to Gosub, so
that it can be called with arguments. The reason? The Application Macro() uses up a fair
amount of stack space to do its thing. If you go more than 7 levels deep in macros (via recursion
or subdivision of tasks) then safeguards will kick in and the calls will terminate early to keep
asterisk from crashing. Gosubs don't traverse the macro def "by hand" like Macro does, and
will allow a bit more "headroom". We are hoping about 20 levels of depth. The only problem
is that the coder has to explicitly include "return" statements in their macros, or you'll have
pretty big problems!!!

Otherwise, there is little difference with previous usage. Macros are still declared the same
in AEL, they are just compiled differently underneath now. Just make sure to put the return;
statement in the right spots! They will be converted to Return() application calls.


5. (originally filed as bug 7714) Alternative to naming new extensions when building a switch conditionals
Status: ON HOLD Not going to do this as yet; the current method seems better at this time.
Description: Don't break switch cases into separate extensions?
Requested by: Sherwood McGowan
Branch: not yet
Discussion:
Sherwood McGowan:
The existing method of using multiple extensions to separate cases within the switch statement is not the best. Have you thought about
just using n(labelname) directives? See the additional information box for my examples/comparisons.
the n(label) labels are GREAT, and you can reference them just like we do within the AEL code :)
AEL2's outputted code: (excess removed)

           [macro-endcall]
           exten => s,1,Goto(sw-10-${calltype}|1)
           exten => s,2,NoOp(Finish switch-endcall-10)
           exten => sw-10-in,1,Macro(setdispo|${callid})
           exten => sw-10-out,1,Macro(dbconn|cdr|open)

           A suggested way to do this:

           exten => s,1,Goto(${EXTEN}|${calltype})
           exten => s,2,NoOp(Finish switch-endcall)

           exten => s,n(in),Macro(setdispo|${callid})
           exten => s,n,Return

           exten => s,n(out),Macro(dbconn|cdr|open)
           exten => s,n,Return

Murf:
As to the switch thing, as I call it...

I have definitely thought much on this issue while I was coding up AEL2...
but first and foremost, that was the way (using separate
extensions) the original AEL implemented switches, and
I didn't want to rock the boat.

Next, it does have a certain appeal. Besides the normal case and default
statements you can put in a switch, there is also the "pattern"
statement, which basically is just like a case, except that it prepends an
underscore in front of the extension name, allowing a pattern extension,
which is something you just cannot get with a simple switch...

        switch(${NUMTODIAL})
        {
                case 911:
                        verbose("Hey, an emergency!");
                        break;
                case 411:
                        verbose("What, you need information?");
                        break;
                pattern 307XXXXXXX:
                        verbose("Why on earth would you want to call Wyoming?");
                        break;
                pattern 801XXXXXXX:
                        verbose("Why on earth would anyone call Utah?");
                        break;
                default:
                        verbose(Ah, a discriminating caller, indeed!);
                        break;
        }

I guess if labels could define patterns, you could get the same affect.
Until then, you have to admit, the pattern statement is a nice feature.

Sherwood McGowan:

Hey, you're right! I completely forgot about the pattern matching abilities.
Your way definitely makes much more sense :)

pkempgen (via the Mantis bug tracker, 9316, 03-19-07 15:29):

You never know for sure that ${EXTEN} is what you
think it is because it might happen to be inside
a different extension - and Dial(SIP/sw-87-user) is
not what I expected :)

6. Multiline Commenting
Status: finished
Description: Enhance the AEL2 parser to allow for a multiline comment keyword set.
Requested by: Sherwood McGowan
Branch: already committed to trunk
Discussion: C style comments are now supported: example: /* this is a comment */

7. New keyword: subroutine
Status: Will not be done, sort of... see above about Macro/Gosub
Description: Enhancement to the context keyword, if context is prefaced by subroutine force 'return' statements within that context to use the subroutine command Return.
Requested by: Sherwood McGowan
Branch: not yet
Discussion: Due to memory constraints built into asterisk, we will implement macros as gosubs instead. See above.

8. #Include wildcards
Status: Finished
Description: Allow #including wildcards like the rest of the Asterisk configs. This allows things like: #include "people/*.conf"
Requested by: Luke-Jr
Discussion/Notes:
Sherwood McGowan: I know that #include is already implemented, haven't personally tested wildcards because I wasn't aware you could wildcard includes at all. Will test or check with murph
murf: I finished this. Is it in just trunk, or did I add it to 1.4 also?

9. Optimize AEL generated extensions.conf code by leaving out the NoOp() markers
Status: Not Yet Begun
Description: The NoOps() that are output to mark the end of if, while, switch, etc statements are unnecc. — please remove them.
Requested by: yarns (bug 13422)
Discussion/Notes:

10. allow macros to have variable numbers of arguments.
Status: Not yet begun.
Description: Allow macros to have a variable number of arguments
Requested by: SEVERAL
Discussion/Notes: I've been discouraging users from using ${ARG1}, ${ARG2}, etc inside macros, in favor of using the name of the argument instead... eg. ${name}.
Since Gosub (in 1.6.x./trunk) is being used, users can use ${ARGx} to access such arguments, but the AEL parser will reject extra arguments if included,
and complain about missing arguments if you include too few. I've been thinking about including a new keyword, "...", which, if included as the last
argument in the macro definition, would turn off such checking for that and following arguments.


This page accessed 186397 times!

Created by: murf, Last modification: Thu 10 of Sep, 2015 (00:55 UTC) by rushowr
Please update this page with new information, just login and click on the "Edit" or "Discussion" tab. Get a free login here: Register Thanks! - Find us on Google+