[SR-Dev] git:andrei/switch: script parsing: C style switch() & case support

Daniel-Constantin Mierla miconda at gmail.com
Wed Feb 4 22:18:40 CET 2009


On 02/04/2009 10:58 PM, Andrei Pelinescu-Onciul wrote:
> On Feb 04, 2009 at 22:29, Daniel-Constantin Mierla <miconda at gmail.com> wrote:
>   
>> Hi Andrei,
>>
>> does the switch support only integers?
>>
>> Apart of return code from functions, most of the switches I have seen 
>> are for strings (e.g., matching dialed number/address, user IDs, etc...).
>>     
>
> Yes, it's only for integers.
>
> For strings, ifs should be used, since the switch wouldn't bring any
> advantage.
>   
There are couple of advantages:
1)  config clarity - instead of a long
if (a==... || a==...)
 you have

switch(a) {
 case ...:
 case ...:
}

2) it can shorten the script and save some ifs:

switch(a) {
 case v1:
       something;
 case v2:
       something else;
break;
}

This is equivalent of:
if(a==v1) {
something;
}
if(a==v1 || a==v2){
something;
something else;
}

or

if(a==v1) {
something;
something else;
}
if(a==v2){
something else;
}

3) although not implemented yet in kamailio/openser (but planned), 
inside 'break' can make code cleaner
switch(a) {
case v1:
      ...
      if(...) {
          break;
      }
      ...
break;
}

4) traditionally, as I could learn, pstn switching/old telco school guys 
feel more confortable with line oriented routing rules, where they had 
something like:

extension match "1234": call emergency;

It could be a sswitch (although I would prefer single name to avoid 
confusions) if breaks logic for integer optimizations.

Cheers,
Daniel
> Andrei
>
>
>   
>> On 02/04/2009 09:50 PM, Andrei Pelinescu-Onciul wrote:
>>     
>>> Module: sip-router
>>> Branch: andrei/switch
>>> Commit: a29a8b6ddba24b8e71faaf78e7426aa7f85c19b5
>>> URL:    
>>> http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=a29a8b6ddba24b8e71faaf78e7426aa7f85c19b5
>>>
>>> Author: Andrei Pelinescu-Onciul <andrei at iptel.org>
>>> Committer: Andrei Pelinescu-Onciul <andrei at iptel.org>
>>> Date:   Wed Feb  4 20:44:26 2009 +0100
>>>
>>> script parsing: C style switch() & case support
>>>
>>> - support for parsing C style switch() & case, e.g.:
>>>  switch($var){
>>> 	case 1:
>>> 	        log(1, "1\n");
>>> 	        break;
>>> 	case 2:
>>> 	case 3:
>>> 	default:
>>> 	         log(1, "default\n");
>>> 	}
>>>  (note: this is different from kamailio/openser switch())
>>>
>>> ---
>>>
>>> cfg.lex        |    7 +++
>>> cfg.y          |  122 
>>> +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>>> route_struct.h |    8 +++-
>>> switch.h       |   60 +++++++++++++++++++++++++++
>>> 4 files changed, 194 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/cfg.lex b/cfg.lex
>>> index fc9eecb..13767b1 100644
>>> --- a/cfg.lex
>>> +++ b/cfg.lex
>>> @@ -187,6 +187,9 @@ ELSE			"else"
>>> SET_ADV_ADDRESS	"set_advertised_address"
>>> SET_ADV_PORT	"set_advertised_port"
>>> FORCE_SEND_SOCKET	"force_send_socket"
>>> +SWITCH			"switch"
>>> +CASE			"case"
>>> +DEFAULT			"default"
>>>
>>> /*ACTION LVALUES*/
>>> URIHOST			"uri:host"
>>> @@ -495,6 +498,10 @@ EAT_ABLE	[\ \t\b\r]
>>> 										return SET_ADV_PORT; }
>>> <INITIAL>{FORCE_SEND_SOCKET}	{	count(); yylval.strval=yytext;
>>> 									return FORCE_SEND_SOCKET; }
>>> +<INITIAL>{SWITCH}	{ count(); yylval.strval=yytext; return SWITCH; }
>>> +<INITIAL>{CASE}	{ count(); yylval.strval=yytext; return CASE; }
>>> +<INITIAL>{DEFAULT}	{ count(); yylval.strval=yytext; return DEFAULT; }
>>> +
>>>
>>> <INITIAL>{URIHOST}	{ count(); yylval.strval=yytext; return URIHOST; }
>>> <INITIAL>{URIPORT}	{ count(); yylval.strval=yytext; return URIPORT; }
>>> diff --git a/cfg.y b/cfg.y
>>> index 63c9eb4..431054d 100644
>>> --- a/cfg.y
>>> +++ b/cfg.y
>>> @@ -93,6 +93,7 @@
>>>  *               lval=rval_expr, where lval=avp|pvar  (andrei)
>>>  * 2007-12-06  expression are now evaluated in terms of rvalues;
>>>  *             NUMBER is now always positive; cleanup (andrei)
>>> + * 2009-01-26  case/switch() support (andrei)
>>> */
>>>
>>> %{
>>> @@ -109,6 +110,7 @@
>>> #include "route_struct.h"
>>> #include "globals.h"
>>> #include "route.h"
>>> +#include "switch.h"
>>> #include "dprint.h"
>>> #include "sr_module.h"
>>> #include "modparam.h"
>>> @@ -211,6 +213,8 @@ static struct socket_id* mk_listen_id2(struct 
>>> name_lst*, int, int);
>>> static void free_name_lst(struct name_lst* lst);
>>> static void free_socket_id_lst(struct socket_id* i);
>>>
>>> +static struct case_stms* mk_case_stm(struct rval_expr* ct, struct action* 
>>> a);
>>> +
>>> %}
>>>
>>> %union {
>>> @@ -219,6 +223,7 @@ static void free_socket_id_lst(struct socket_id* i);
>>> 	char* strval;
>>> 	struct expr* expr;
>>> 	struct action* action;
>>> +	struct case_stms* case_stms;
>>> 	struct net* ipnet;
>>> 	struct ip_addr* ipaddr;
>>> 	struct socket_id* sockid;
>>> @@ -272,6 +277,9 @@ static void free_socket_id_lst(struct socket_id* i);
>>> %token SET_ADV_ADDRESS
>>> %token SET_ADV_PORT
>>> %token FORCE_SEND_SOCKET
>>> +%token SWITCH
>>> +%token CASE
>>> +%token DEFAULT
>>> %token URIHOST
>>> %token URIPORT
>>> %token MAX_LEN
>>> @@ -493,6 +501,8 @@ static void free_socket_id_lst(struct socket_id* i);
>>> %type <intval> intno eint_op eint_op_onsend
>>> %type <intval> eip_op eip_op_onsend
>>> %type <action> action actions cmd fcmd if_cmd stm /*exp_stm*/ 
>>> assign_action
>>> +%type <action> switch_cmd
>>> +%type <case_stms> single_case case_stms
>>> %type <ipaddr> ipv4 ipv6 ipv6addr ip
>>> %type <ipnet> ipnet
>>> %type <strval> host
>>> @@ -514,7 +524,7 @@ static void free_socket_id_lst(struct socket_id* i);
>>> %type <attr> attr_id_any_str
>>> %type <pvar> pvar
>>> %type <lval> lval
>>> -%type <rv_expr> rval rval_expr 
>>> +%type <rv_expr> rval rval_expr ct_rval
>>> %type <lval> avp_pvar
>>> /* %type <intval> class_id */
>>> %type <intval> assign_op
>>> @@ -1762,6 +1772,7 @@ actions:
>>> action:
>>> 	fcmd SEMICOLON {$$=$1;}
>>> 	| if_cmd {$$=$1;}
>>> +	| switch_cmd {$$=$1;}
>>> 	| assign_action SEMICOLON {$$=$1;}
>>> 	| SEMICOLON /* null action */ {$$=0;}
>>> 	| fcmd error { $$=0; yyerror("bad command: missing ';'?"); }
>>> @@ -1770,6 +1781,98 @@ if_cmd:
>>> 	IF exp stm		{ $$=mk_action( IF_T, 3, EXPR_ST, $2, 
>>> 	ACTIONS_ST, $3, NOSUBTYPE, 0); }
>>> 	| IF exp stm ELSE stm	{ $$=mk_action( IF_T, 3, EXPR_ST, $2, 
>>> 	ACTIONS_ST, $3, ACTIONS_ST, $5); }
>>> 	;
>>> +
>>> +ct_rval: rval_expr {
>>> +			$$=0;
>>> +			if (!rve_is_constant($1)){
>>> +				yyerror("constant expected");
>>> +			}else if (!rve_check_type((enum rval_type*)&i_tmp, 
>>> $1, 0, 0 ,0)){
>>> +				yyerror("invalid expression (bad type)");
>>> +			}else if (i_tmp!=RV_INT){
>>> +				yyerror("invalid expression type, int 
>>> expected\n");
>>> +			}else
>>> +				$$=$1;
>>> +		}
>>> +;
>>> +single_case:
>>> +	CASE ct_rval COLON actions {
>>> +		$$=0;
>>> +		if ($2==0) yyerror ("bad case label");
>>> +		else if (($$=mk_case_stm($2, $4))==0){
>>> +				yyerror("internal error: memory allocation 
>>> failure");
>>> +				YYABORT;
>>> +		}
>>> +	}
>>> +	| CASE ct_rval COLON {
>>> +		$$=0;
>>> +		if ($2==0) yyerror ("bad case label");
>>> +		else if (($$=mk_case_stm($2, 0))==0){
>>> +				yyerror("internal error: memory allocation 
>>> failure");
>>> +				YYABORT;
>>> +		}
>>> +	}
>>> +	| DEFAULT COLON actions {
>>> +		if (($$=mk_case_stm(0, $3))==0){
>>> +				yyerror("internal error: memory allocation 
>>> failure");
>>> +				YYABORT;
>>> +		}
>>> +	}
>>> +	| DEFAULT COLON {
>>> +		if (($$=mk_case_stm(0, 0))==0){
>>> +				yyerror("internal error: memory allocation 
>>> failure");
>>> +				YYABORT;
>>> +		}
>>> +	}
>>> +	| CASE error { $$=0; yyerror("bad case label"); }
>>> +	| CASE ct_rval COLON error { $$=0; yyerror ("bad case body"); }
>>> +;
>>> +case_stms:
>>> +	case_stms single_case {
>>> +		$$=$1;
>>> +		if ($2==0) yyerror ("bad case");
>>> +		if ($$){
>>> +			*($$->append)=$2;
>>> +			if (*($$->append)!=0)
>>> +				$$->append=&((*($$->append))->next);
>>> +		}
>>> +	}
>>> +	| single_case {
>>> +		$$=$1;
>>> +		if ($1==0) yyerror ("bad case");
>>> +		else $$->append=&($$->next);
>>> +	}
>>> +;
>>> +switch_cmd:
>>> +	  SWITCH rval_expr LBRACE case_stms RBRACE { 
>>> +		$$=0;
>>> +		if ($2==0) yyerror("bad expression in switch(...)");
>>> +		else if ($4==0) yyerror ("bad switch body");
>>> +		else{
>>> +			$$=mk_action(SWITCH_T, 2, RVE_ST, $2, CASE_ST, $4);
>>> +			if ($$==0) {
>>> +				yyerror("internal error");
>>> +				YYABORT;
>>> +			}
>>> +		}
>>> +	}
>>> +	| SWITCH rval_expr LBRACE RBRACE {
>>> +		$$=0;
>>> +		warn("empty switch()");
>>> +		if ($2==0) yyerror("bad expression in switch(...)");
>>> +		else{
>>> +			/* it might have sideffects, so leave it for the 
>>> optimizer */
>>> +			$$=mk_action(SWITCH_T, 2, RVE_ST, $2, CASE_ST, 0);
>>> +			if ($$==0) {
>>> +				yyerror("internal error");
>>> +				YYABORT;
>>> +			}
>>> +		}
>>> +	}
>>> +	| SWITCH error { $$=0; yyerror ("bad expression in switch(...)"); }
>>> +	| SWITCH rval_expr LBRACE error RBRACE 
>>> +		{$$=0; yyerror ("bad switch body"); }
>>> +;
>>> +
>>> /* class_id:
>>> 	LBRACK ATTR_USER RBRACK { $$ = AVP_CLASS_USER; }
>>> 	| LBRACK ATTR_DOMAIN RBRACK { $$ = AVP_CLASS_DOMAIN; }
>>> @@ -2763,6 +2866,23 @@ static void free_socket_id_lst(struct socket_id* 
>>> lst)
>>> 	}
>>> }
>>>
>>> +
>>> +static struct case_stms* mk_case_stm(struct rval_expr* ct, struct action* 
>>> a)
>>> +{
>>> +	struct case_stms* s;
>>> +	s=pkg_malloc(sizeof(*s));
>>> +	if (s==0) {
>>> +		LOG(L_CRIT,"ERROR: cfg. parser: out of memory.\n");
>>> +	} else {
>>> +		memset(s, 0, sizeof(*s));
>>> +		s->ct_rve=ct;
>>> +		s->actions=a;
>>> +		s->next=0;
>>> +		s->append=0;
>>> +	}
>>> +	return s;
>>> +}
>>> +
>>> /*
>>> int main(int argc, char ** argv)
>>> {
>>> diff --git a/route_struct.h b/route_struct.h
>>> index 76aac25..e87b2d0 100644
>>> --- a/route_struct.h
>>> +++ b/route_struct.h
>>> @@ -70,7 +70,9 @@ enum { METHOD_O=1, URI_O, FROM_URI_O, TO_URI_O, SRCIP_O, 
>>> SRCPORT_O,
>>> enum { FORWARD_T=1, SEND_T, DROP_T, LOG_T, ERROR_T, ROUTE_T, EXEC_T,
>>> 		SET_HOST_T, SET_HOSTPORT_T, SET_USER_T, SET_USERPASS_T,
>>> 		SET_PORT_T, SET_URI_T, SET_HOSTPORTTRANS_T,
>>> -		IF_T, MODULE_T, MODULE3_T, MODULE4_T, MODULE5_T, MODULE6_T, 
>>> MODULEX_T,
>>> +		IF_T, SWITCH_T /* only until fixup*/,
>>> +		BLOCK_T, EVAL_T, SWITCH_JT_T, SWITCH_COND_T,
>>> +		MODULE_T, MODULE3_T, MODULE4_T, MODULE5_T, MODULE6_T, 
>>> MODULEX_T,
>>> 		SETFLAG_T, RESETFLAG_T, ISFLAGSET_T ,
>>> 		AVPFLAG_OPER_T,
>>> 		LEN_GT_T, PREFIX_T, STRIP_T,STRIP_TAIL_T,
>>> @@ -96,7 +98,9 @@ enum { NOSUBTYPE=0, STRING_ST, NET_ST, NUMBER_ST, IP_ST, 
>>> RE_ST, PROXY_ST,
>>> 		MYSELF_ST, STR_ST, SOCKID_ST, SOCKETINFO_ST, ACTION_ST, 
>>> 		AVP_ST,
>>> 		SELECT_ST, PVAR_ST,
>>> 		LVAL_ST,  RVE_ST,
>>> -		RETCODE_ST};
>>> +		RETCODE_ST, CASE_ST,
>>> +		BLOCK_ST, JUMPTABLE_ST, CONDTABLE_ST
>>> +};
>>>
>>> /* run flags */
>>> #define EXIT_R_F   1
>>> diff --git a/switch.h b/switch.h
>>> new file mode 100644
>>> index 0000000..4c9f78b
>>> --- /dev/null
>>> +++ b/switch.h
>>> @@ -0,0 +1,60 @@
>>> +/* 
>>> + * $Id$
>>> + * 
>>> + * Copyright (C) 2009 iptelorg GmbH
>>> + *
>>> + * Permission to use, copy, modify, and distribute this software for any
>>> + * purpose with or without fee is hereby granted, provided that the above
>>> + * copyright notice and this permission notice appear in all copies.
>>> + *
>>> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 
>>> WARRANTIES
>>> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
>>> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
>>> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
>>> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
>>> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
>>> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
>>> + */
>>> +/*
>>> + * /home/andrei/sr.git/switch.h
>>> + */
>>> +/*
>>> + * History:
>>> + * --------
>>> + *  2009-02-02  initial version (andrei)
>>> +*/
>>> +
>>> +#ifndef __switch_h
>>> +#define __switch_h
>>> +
>>> +#include "route_struct.h"
>>> +
>>> +struct case_stms{
>>> +	struct rval_expr* ct_rve;
>>> +	struct action* actions;
>>> +	struct case_stms* next;
>>> +	struct case_stms** append;
>>> +	int int_label;
>>> +	int is_default;
>>> +};
>>> +
>>> +
>>> +struct switch_cond_table{
>>> +	int n;                  /**< size */
>>> +	int* cond;              /**< int labels array */
>>> +	struct action** jump;   /**< jump points array */
>>> +	struct action* def; /**< default jump  */
>>> +};
>>> +
>>> +
>>> +struct switch_jmp_table{
>>> +	int first;              /**< first int label in the jump table */
>>> +	int last;               /**< last int label in the jump table */
>>> +	struct action** tbl;    /**< jmp table [v-first] iff first<=v<=last 
>>> */
>>> +	struct switch_cond_table rest; /**< normal cond. table for the rest 
>>> */
>>> +};
>>> +
>>> +
>>> +#endif /*__switch_h*/
>>> +
>>> +/* vi: set ts=4 sw=4 tw=79:ai:cindent: */
>>>
>>>
>>> _______________________________________________
>>> sr-dev mailing list
>>> sr-dev at lists.sip-router.org
>>> http://lists.sip-router.org/cgi-bin/mailman/listinfo/sr-dev
>>>  
>>>       
>> -- 
>> Daniel-Constantin Mierla
>> http://www.asipto.com
>>     

-- 
Daniel-Constantin Mierla
http://www.asipto.com




More information about the sr-dev mailing list