While I like Asterisk, and I’ve been very impressed with its abilities, it has a few things that need improvement. One of those is its configuration language. To get a feel for it, here’s a snippet out of my current configuration:

[macro-outsideline]
  exten => s,1,AbsoluteTimeout(7200)
  exten => s,2,SetCDRUserField(Outside (${CHANNEL}))
  exten => s,3,Macro(cidfix)
  exten => s,4,Macro(dbcallforward)
  exten => s,5,Macro(setalertinfo)
  exten => s,6,LookupBlacklist
  exten => s,7,Dial(${PHONES},13,Ttmr)
  exten => s,8,Answer
  exten => s,9,Goto(outside-ivr,s,1)
  exten => s,107,Goto(206)
  exten => s,108,Goto(7)
  exten => s,207,Wait(3600)
  exten => s,208,Hangup

The [something] block starts a new context; any context that starts with macro- can be called as a subroutine from other contexts. Inside of each context, you can have multiple extensions. In this case, all 13 lines refer to the s extension, which acts as a sort of default. The next field after the extension identifier is the priority field, but it’s really just a line number. If you want to insert a new line at the beginning, then you need to manually renumber all of the other lines–look at line 107, and you’ll see a reference to line 206, which doesn’t exist–at some point I manually renumbered this macro but missed that line.

Even worse, error handling is just nasty–look at line 7: it dials some phones. If it succeeds, then it never really returns–when the conversation is over, the user hangs up which terminates the call. If the Dial command times out (no answer), then it goes to line 8. If there’s an error, then it implicitly jumps ahead 101 priorities, to line 108. Now look at line 6: the LookupBlacklist works more or less the same way–if the caller matches the blacklist, then it jumps ahead 101 lines as well. What happens when you put LookupBlacklist and Dial right next to each other? Look at lines 107 and 108–you only have a single priority number to use for handling the LookupBlacklist match, because Dial errors will show up on the very next line.

Needless to say, there’s a lot of desire to improve Asterisk’s configuration language. Watching the mailing list, there are a lot of proposals. I’ve seen a couple requests for TCL and a couple serious proposals for XML. While I don’t have anything against XML for storing structured data, it’s a horrid format for programming languages, and that’s really what we’re talking about here.

Anyway, no matter where Asterisk’s configuration language goes in the future, there are already a couple small improvements in the post-1.0 CVS tree–you can now use a priority of n to mean ‘the next number’, s to mean ‘the same number’, plus you can add labels and numeric offsets. Here’s the configuration from above using the new changes:

[macro-outsideline]
  exten => s,1,AbsoluteTimeout(7200)
  exten => s,n,SetCDRUserField(Outside (${CHANNEL}))
  exten => s,n,Macro(cidfix)
  exten => s,n,Macro(dbcallforward)
  exten => s,n,Macro(setalertinfo)
  exten => s,n(blacklist),LookupBlacklist
  exten => s,n(dial),Dial(${PHONES},13,Ttmr)
  exten => s,n(ivr),Answer
  exten => s,n,Goto(outside-ivr,s,1)
  exten => s,blacklist+101,Goto(wait)
  exten => s,dial+101,Goto(ivr)
  exten => s,blacklist+201(wait),Wait(3600)
  exten => s,n,Hangup

It still doesn’t fix the blacklist/dial collision, but it gets rid of all of the other numbering problems. If I wanted to add another line at the top, I wouldn’t have to renumber anything–everything would Just Work. On the other hand, the line numbers are still there, they’re just not quite as obvious. So I suppose we can call this a pragmatic improvement, even if it doesn’t help the aesthetics of the language much.

On the other hand, this leaves us halfway to a decent configuration language–suppose the above syntax changed just a bit:

[macro-outsideline]
  exten(s):
    AbsoluteTimeout(7200)
    SetCDRUserField(Outside (${CHANNEL}))
    Macro(cidfix)
    Macro(dbcallforward)
    Macro(setalertinfo)
   blacklist: LookupBlacklist
   dial: Dial(${PHONES},13,Ttmr)
   ivr: Answer
    Goto(outside-ivr,s,1)
   dial-error: Goto(ivr)
   blacklist-error: Wait(3600)
    Hangup

Personally, I’d find this to be good enough. There’s still a bit of magic with labels and error handling, and there’s probably a better approach out there, but this isn’t too ugly, and it’s only a small step from where we are today.