%{
/*
 * Copyright 2013 Google Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */
/*
 * Author: Author: ncardwell@google.com (Neal Cardwell)
 *
 * This is the parser for the packetdrill script language. It is
 * processed by the bison parser generator.
 *
 * For full documentation see: http://www.gnu.org/software/bison/manual/
 *
 * Here is a quick and dirty tutorial on bison:
 *
 * A bison parser specification is basically a BNF grammar for the
 * language you are parsing. Each rule specifies a nonterminal symbol
 * on the left-hand side and a sequence of terminal symbols (lexical
 * tokens) and or nonterminal symbols on the right-hand side that can
 * "reduce" to the symbol on the left hand side. When the parser sees
 * the sequence of symbols on the right where it "wants" to see a
 * nonterminal on the left, the rule fires, executing the semantic
 * action code in curly {} braces as it reduces the right hand side to
 * the left hand side.
 *
 * The semantic action code for a rule produces an output, which it
 * can reference using the $$ token. The set of possible types
 * returned in output expressions is given in the %union section of
 * the .y file. The specific type of the output for a terminal or
 * nonterminal symbol (corresponding to a field in the %union) is
 * given by the %type directive in the .y file. The action code can
 * access the outputs of the symbols on the right hand side by using
 * the notation $1 for the first symbol, $2 for the second symbol, and
 * so on.
 *
 * The lexer (generated by flex from lexer.l) feeds a stream of
 * terminal symbols up to this parser. Parser semantic actions can
 * access the lexer output for a terminal symbol with the same
 * notation they use for nonterminals.
 *
 * Here's an example rule with its semantic action in {} braces:
 *
 *     tcp_option
 *     ...
 *      | MSS INTEGER   {
 *        $$ = tcp_option_new(...);
 *        ...
 *        $$->data.mss.bytes = htons($2);
 *      }
 *
 * This rule basically says:
 *
 *   When the parser wants to see a tcp_option, if it sees an MSS from
 *   the lexer followed by an INTEGER from the lexer then run the
 *   action code that (a) stores in the output $$ a pointer to a
 *   struct tcp_option object, and then (b) stores in that object the
 *   value of the INTEGER token (accessed with $2).
 *
 */

/* The first part of the .y file consists of C code that bison copies
 * directly into the top of the .c file it generates.
 */

#include "types.h"

#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "gre_packet.h"
#include "ip.h"
#include "ip_packet.h"
#include "icmp_packet.h"
#include "logging.h"
#include "mpls.h"
#include "mpls_packet.h"
#include "sctp_packet.h"
#include "tcp_packet.h"
#include "udp_packet.h"
#include "udplite_packet.h"
#include "parse.h"
#include "script.h"
#include "tcp.h"
#include "tcp_options.h"

/* This include of the bison-generated .h file must go last so that we
 * can first include all of the declarations on which it depends.
 */
#include "parser.h"

/* Change this YYDEBUG to 1 to get verbose debug output for parsing: */
#define YYDEBUG 0
#if YYDEBUG
extern int yydebug;
#endif

extern FILE *yyin;
extern int yylineno;
extern char *yytext;
extern int yylex(void);
extern int yyparse(void);
extern int yywrap(void);

/* This mutex guards all parser global variables declared in this file. */
pthread_mutex_t parser_mutex = PTHREAD_MUTEX_INITIALIZER;

/* The input to the parser: the path name of the script file to parse. */
static const char* current_script_path = NULL;

/* The starting line number of the input script statement that we're
 * currently parsing. This may be different than yylineno if bison had
 * to look ahead and lexically scan a token on the following line to
 * decide that the current statement is done.
 */
static int current_script_line = -1;

/*
 * We uses this object to look up configuration info needed during
 * parsing (such as whether packets are IPv4 or IPv6).
 */
static const struct config *in_config = NULL;

/* The output of the parser: an output script containing
 * 1) a linked list of options
 * 2) a linked list of events
 */
static struct script *out_script = NULL;

/* The test invocation to pass back to parse_and_finalize_config(). */
struct invocation *invocation;

/* Copy the script contents into our single linear buffer. */
void copy_script(const char *script_buffer, struct script *script)
{
	DEBUGP("copy_script\n");

	free(script->buffer);
	script->length = strlen(script_buffer);
	script->buffer = strdup(script_buffer);
	assert(script->buffer != NULL);

	DEBUGP("copy_script: %d bytes\n", script->length);
}

/* Read the script file into a single linear buffer. */
void read_script(const char *script_path, struct script *script)
{
	int size = 0;

	DEBUGP("read_script(%s)\n", script_path);

	while (script->buffer == NULL) {
		struct stat script_info;
		int fd = -1;

		/* Allocate a buffer big enough for the whole file. */
		if (stat(script_path, &script_info) != 0)
			die("parse error: stat() of script file '%s': %s\n",
			    script_path, strerror(errno));

		/* Pick a buffer size larger than the file, so we'll
		 * know if the file grew.
		 */
		size = max((int)script_info.st_size, size) + 1;

		script->buffer = malloc(size);
		assert(script->buffer != NULL);

		/* Read the file into our buffer. */
		fd = open(script_path, O_RDONLY);
		if (fd < 0)
			die("parse error opening script file '%s': %s\n",
			    script_path, strerror(errno));

		script->length = read(fd, script->buffer, size);
		if (script->length < 0)
			die("parse error reading script file '%s': %s\n",
			    script_path, strerror(errno));

		/* If we filled the buffer, then probably another
		 * process wrote more to the file since our stat call,
		 * so we should try again.
		 */
		if (script->length == size) {
			free(script->buffer);
			script->buffer = NULL;
			script->length = 0;
		}

		if (close(fd))
			die_perror("close");
	}
	DEBUGP("read_script: %d bytes\n", script->length);
}


/* The public entry point for the script parser. Parses the
 * text script file with the given path name and fills in the script
 * object with the parsed representation.
 */
int parse_script(const struct config *config,
			 struct script *script,
			 struct invocation *callback_invocation)
{
	/* This bison-generated parser is not multi-thread safe, so we
	 * have a lock to prevent more than one thread using the
	 * parser at the same time. This is useful in the wire server
	 * context, where in general we may have more than one test
	 * thread running at the same time.
	 */
	if (pthread_mutex_lock(&parser_mutex) != 0)
		die_perror("pthread_mutex_lock");

#if YYDEBUG
	yydebug = 1;
#endif

	/* Now parse the script from our buffer. */
	yyin = fmemopen(script->buffer, script->length, "r");
	if (yyin == NULL)
		die_perror("fmemopen: parse error opening script buffer");

	current_script_path = config->script_path;
	in_config = config;
	out_script = script;
	invocation = callback_invocation;

	/* We have to reset the line number here since the wire server
	 * can do more than one yyparse().
	 */
	yylineno = 1;

	int result = yyparse();		/* invoke bison-generated parser */
	current_script_path = NULL;

	if (fclose(yyin))
		die_perror("fclose: error closing script buffer");

	/* Unlock parser. */
	if (pthread_mutex_unlock(&parser_mutex) != 0)
		die_perror("pthread_mutex_unlock");

	return result ? STATUS_ERR : STATUS_OK;
}

/* Bison emits code to call this method when there's a parse-time error.
 * We print the line number and the error message.
 */
static void yyerror(const char *message)
{
	fprintf(stderr, "%s:%d: parse error at '%s': %s\n",
		current_script_path, yylineno, yytext, message);
}

/* After we finish parsing each line of a script, we analyze the
 * semantics of the line. If we encounter an error then we print the
 * error message to stderr and exit with an error.
 */
static void semantic_error(const char* message)
{
	assert(current_script_line >= 0);
	die("%s:%d: semantic error: %s\n",
	    current_script_path, current_script_line, message);
}

/* This standard callback is invoked by flex when it encounters
 * the end of a file. We return 1 to tell flex to return EOF.
 */
int yywrap(void)
{
	return 1;
}

/* Create and initalize a new expression. */
static struct expression *new_expression(enum expression_t type)
{
	struct expression *expression = calloc(1, sizeof(struct expression));
	expression->type = type;
	return expression;
}

/* Create and initalize a new integer expression with the given
 * literal value and format string.
 */
static struct expression *new_integer_expression(s64 num, const char *format)
{
	struct expression *expression = new_expression(EXPR_INTEGER);
	expression->value.num = num;
	expression->format = format;
	return expression;
}

/* Create and initalize a new one-element expression_list. */
static struct expression_list *new_expression_list(
	struct expression *expression)
{
	struct expression_list *list;
	list = calloc(1, sizeof(struct expression_list));
	list->expression = expression;
	list->next = NULL;
	return list;
}

/* Add the expression to the end of the list. */
static void expression_list_append(struct expression_list *list,
				   struct expression *expression)
{
	while (list->next != NULL) {
		list = list->next;
	}
	list->next = new_expression_list(expression);
}

/* Create and initialize a new option. */
static struct option_list *new_option(char *name, char *value)
{
	struct option_list *opt = calloc(1, sizeof(struct option_list));
	opt->name = name;
	opt->value = value;
	return opt;
}

/* Create and initialize a new event. */
static struct event *new_event(enum event_t type)
{
	struct event *e = calloc(1, sizeof(struct event));
	e->type = type;
	e->time_usecs_end = NO_TIME_RANGE;
	e->offset_usecs = NO_TIME_RANGE;
	return e;
}

static int parse_hex_byte(const char *hex, u8 *byte)
{
	if (!isxdigit((int)hex[0]) || !isxdigit((int)hex[1])) {
		return STATUS_ERR;	/* need two hex digits per byte */
	}
	char buf[] = { hex[0], hex[1], '\0' };
	char* buf_end = NULL;
	u32 byte_value = strtoul(buf, &buf_end, 16);
	assert(byte_value <= 0xff);
	assert(buf_end == buf + 2);
	*byte = byte_value;
	return STATUS_OK;
}

/* Converts a hex string in 'hex' into bytes and stores them in a
 * buffer 'buf' of length 'buf_len' bytes; returns number of bytes in
 * out_len. Works for hex strings of arbitrary size, such as very long
 * TCP Fast Open cookies.
 */
static int parse_hex_string(const char *hex, u8 *buf, int buf_len,
			    int *out_len)
{
	u8 *out = buf;
	u8 *buf_end = buf + buf_len;
	while (hex[0] != '\0') {
		if (out >= buf_end) {
			return STATUS_ERR;	/* ran out of output space */
		}
		if (parse_hex_byte(hex, out))
			return STATUS_ERR;	/* bad character */
		hex += 2;
		out += 1;
	}
	*out_len = out - buf;
	assert(*out_len <= buf_len);
	return STATUS_OK;
}

static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
						    char **error)
{
	int cookie_string_len = strlen(cookie_string);
	if (cookie_string_len & 1) {
		asprintf(error,
			 "TCP fast open cookie has an odd number of digits");
		return NULL;
	}
	int cookie_bytes = cookie_string_len / 2;  /* 2 hex chars per byte */
	if (cookie_bytes > MAX_TCP_FAST_OPEN_COOKIE_BYTES) {
		asprintf(error, "TCP fast open cookie too long");
		asprintf(error, "TCP fast open cookie of %d bytes "
			 "exceeds maximum cookie length of %d bytes",
			 cookie_bytes, MAX_TCP_FAST_OPEN_COOKIE_BYTES);
		return NULL;
	}
	u8 option_bytes = TCPOLEN_EXP_FASTOPEN_BASE + cookie_bytes;
	struct tcp_option *option;
	option = tcp_option_new(TCPOPT_EXP, option_bytes);
	option->data.fast_open.magic = htons(TCPOPT_FASTOPEN_MAGIC);
	int parsed_bytes = 0;
	/* Parse cookie. This should be an ASCII hex string
	 * representing an even number of bytes (4-16 bytes). But we
	 * do not enforce this, since we want to allow test cases that
	 * supply invalid cookies.
	 */
	if (parse_hex_string(cookie_string, option->data.fast_open.cookie,
			     sizeof(option->data.fast_open.cookie),
			     &parsed_bytes)) {
		free(option);
		asprintf(error,
			 "TCP fast open cookie is not a valid hex string");
		return NULL;
	}
	assert(parsed_bytes == cookie_bytes);
	return option;
}

%}

%locations
%expect 1  /* we expect a shift/reduce conflict for the | binary expression */
/* The %union section specifies the set of possible types for values
 * for all nonterminal and terminal symbols in the grammar.
 */
%union {
	s64 integer;
	double floating;
	char *string;
	char *reserved;
	s64 time_usecs;
	enum direction_t direction;
	enum ip_ecn_t ip_ecn;
	struct mpls_stack *mpls_stack;
	struct mpls mpls_stack_entry;
	u16 port;
	s32 window;
	u32 sequence_number;
	struct {
		u32 start_sequence;
		u16 payload_bytes;
	} tcp_sequence_info;
	struct {
		int protocol;
		u16 payload_bytes;
		u32 verification_tag;	/* used for SCTP */
		u32 start_sequence;	/* used for TCP */
		u16 checksum_coverage;	/* used for UDPLite */
	} transport_info;
	struct option_list *option;
	struct event *event;
	struct packet *packet;
	struct sctp_chunk_list_item *chunk_list_item;
	struct sctp_chunk_list *chunk_list;
	struct sctp_byte_list_item *byte_list_item;
	struct sctp_byte_list *byte_list;
	struct sctp_sack_block_list_item *sack_block_list_item;
	struct sctp_sack_block_list *sack_block_list;
	struct sctp_address_type_list_item *address_type_list_item;
	struct sctp_address_type_list *address_type_list;
	struct sctp_parameter_type_list_item *parameter_type_list_item;
	struct sctp_parameter_type_list *parameter_type_list;
	struct sctp_parameter_list_item *parameter_list_item;
	struct sctp_parameter_list *parameter_list;
	struct sctp_cause_list_item *cause_list_item;
	struct sctp_cause_list *cause_list;
	struct syscall_spec *syscall;
	struct command_spec *command;
	struct code_spec *code;
	struct tcp_option *tcp_option;
	struct tcp_options *tcp_options;
	struct expression *expression;
	struct expression_list *expression_list;
	struct errno_spec *errno_info;
}

/* The specific type of the output for a symbol is given by the %type
 * directive. By convention terminal symbols returned from the lexer
 * have ALL_CAPS names, and nonterminal symbols have lower_case names.
 */
%token ELLIPSIS
%token <reserved> SA_FAMILY SIN_PORT SIN_ADDR _HTONS_ _HTONL_ INET_ADDR
%token <reserved> MSG_NAME MSG_IOV MSG_FLAGS
%token <reserved> FD EVENTS REVENTS ONOFF LINGER
%token <reserved> ACK ECR EOL MSS NOP SACK SACKOK TIMESTAMP VAL WIN WSCALE PRO
%token <reserved> FAST_OPEN
%token <reserved> IOV_BASE IOV_LEN
%token <reserved> ECT0 ECT1 CE ECT01 NO_ECN
%token <reserved> IPV4 IPV6 ICMP SCTP UDP UDPLITE GRE MTU
%token <reserved> MPLS LABEL TC TTL
%token <reserved> OPTION
%token <reserved> SRTO_INITIAL SRTO_MAX SRTO_MIN
%token <reserved> SINIT_NUM_OSTREAMS SINIT_MAX_INSTREAMS SINIT_MAX_ATTEMPTS
%token <reserved> SINIT_MAX_INIT_TIMEO
%token <reserved> ASSOC_VALUE
%token <reserved> STREAM_ID STREAM_VALUE
%token <reserved> SACK_DELAY SACK_FREQ
%token <reserved> SSTAT_STATE SSTAT_RWND SSTAT_UNACKDATA SSTAT_PENDDATA
%token <reserved> SSTAT_INSTRMS SSTAT_OUTSTRMS SSTAT_FRAGMENTATION_POINT
%token <reserved> SSTAT_PRIMARY
%token <reserved> SPINFO_ADDRESS SPINFO_STATE SPINFO_CWND SPINFO_SRTT SPINFO_RTO
%token <reserved> SPINFO_MTU
%token <reserved> CHUNK DATA INIT INIT_ACK HEARTBEAT HEARTBEAT_ACK ABORT
%token <reserved> SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECNE CWR
%token <reserved> SHUTDOWN_COMPLETE I_DATA PAD
%token <reserved> TYPE FLAGS LEN
%token <reserved> TAG A_RWND OS IS TSN SID SSN MID PPID FSN CUM_TSN GAPS DUPS
%token <reserved> PARAMETER HEARTBEAT_INFORMATION IPV4_ADDRESS IPV6_ADDRESS
%token <reserved> STATE_COOKIE UNRECOGNIZED_PARAMETER COOKIE_PRESERVATIVE
%token <reserved> HOSTNAME_ADDRESS SUPPORTED_ADDRESS_TYPES ECN_CAPABLE
%token <reserved> SUPPORTED_EXTENSIONS ADAPTATION_CODE_POINT ADAPTATION_INDICATION
%token <reserved> ADDR INCR TYPES PARAMS
%token <reserved> IPV4_TYPE IPV6_TYPE HOSTNAME_TYPE
%token <reserved> CAUSE
%token <reserved> CAUSE_CODE CAUSE_INFO
%token <reserved> INVALID_STREAM_IDENTIFIER MISSING_MANDATORY_PARAMETER
%token <reserved> STALE_COOKIE_ERROR OUT_OF_RESOURCE
%token <reserved> UNRESOLVABLE_ADDRESS UNRECOGNIZED_CHUNK_TYPE
%token <reserved> INVALID_MANDATORY_PARAMETER NO_USER_DATA
%token <reserved> COOKIE_RECEIVED_WHILE_SHUTDOWN RESTART_WITH_NEW_ADDRESSES
%token <reserved> USER_INITIATED_ABORT PROTOCOL_VIOLATION
%token <reserved> STALENESS CHK PARAM UNRECOGNIZED_PARAMETERS
%token <reserved> SPP_ADDRESS SPP_HBINTERVAL SPP_PATHMAXRXT SPP_PATHMTU
%token <reserved> SPP_FLAGS SPP_IPV6_FLOWLABEL_ SPP_DSCP_
%token <reserved> SASOC_ASOCMAXRXT SASOC_NUMBER_PEER_DESTINATIONS SASOC_PEER_RWND
%token <reserved> SASOC_LOCAL_RWND SASOC_COOKIE_LIFE SE_TYPE SE_ON
%token <reserved> SND_SID SND_FLAGS SND_PPID SND_CONTEXT SSB_ADAPTATION_IND
%token <reserved> BAD_CRC32C NULL_
%token <reserved> SINFO_STREAM SINFO_SSN SINFO_FLAGS SINFO_PPID SINFO_CONTEXT
%token <reserved> SINFO_TIMETOLIVE SINFO_TSN SINFO_CUMTSN
%token <reserved> PR_POLICY PR_VALUE AUTH_KEYNUMBER SENDV_FLAGS SENDV_SNDINFO
%token <reserved> SENDV_PRINFO SENDV_AUTHINFO
%token <reserved> RCV_SID RCV_SSN RCV_FLAGS RCV_PPID RCV_TSN RCV_CUMTSN RCV_CONTEXT
%token <reserved> NXT_SID NXT_FLAGS NXT_PPID NXT_LENGTH
%token <reserved> RECVV_RCVINFO RECVV_NXTINFO
%token <reserved> SSE_TYPE SSE_FLAGS SSE_LENGTH
%token <reserved> SENDER_DRY_TYPE SENDER_DRY_FLAGS SENDER_DRY_LENGTH SENDER_DRY_ASSOC_ID
%token <reserved> _SCTP_DATA_IO_EVENT_ _SCTP_ASSOCIATION_EVENT_ _SCTP_ADDRESS_EVENT_
%token <reserved> _SCTP_SEND_FAILURE_EVENT_ _SCTP_PEER_ERROR_EVENT_ _SCTP_SHUTDOWN_EVENT_
%token <reserved> _SCTP_PARTIAL_DELIVERY_EVENT_ _SCTP_ADAPTATION_LAYER_EVENT_
%token <reserved> _SCTP_AUTHENTICATION_EVENT_ _SCTP_SENDER_DRY_EVENT_
%token <reserved> SAC_TYPE SAC_FLAGS SAC_LENGTH SAC_STATE SAC_ERROR SAC_OUTBOUND_STREAMS
%token <reserved> SAC_INBOUND_STREAMS SAC_ASSOC_ID SAC_INFO
%token <reserved> SSFE_TYPE SSFE_FLAGS SSFE_LENGTH SSFE_ERROR SSFE_INFO SSFE_ASSOC_ID SSFE_DATA
%token <reserved> AUTH_TYPE AUTH_FLAGS AUTH_LENGTH AUTH_INDICATION AUTH_ASSOC_ID
%token <reserved> SRE_TYPE SRE_FLAGS SRE_LENGTH SRE_ERROR SRE_ASSOC_ID SRE_DATA PDAPI_ASSOC_ID
%token <reserved> PDAPI_TYPE PDAPI_FLAGS PDAPI_LENGTH PDAPI_INDICATION PDAPI_STREAM PDAPI_SEQ
%token <reserved> SPC_TYPE SPC_FLAGS SPC_LENGTH SPC_AADDR SPC_STATE SPC_ERROR SPC_ASSOC_ID
%token <reserved> SSF_TYPE SSF_LENGTH SSF_FLAGS SSF_ERROR SSF_INFO SSF_ASSOC_ID SSF_DATA
%token <reserved> SAI_TYPE SAI_FLAGS SAI_LENGTH SAI_ADAPTATION_IND SAI_ASSOC_ID
%token <reserved> SN_TYPE SN_FLAGS SN_LENGTH
%token <floating> FLOAT
%token <integer> INTEGER HEX_INTEGER
%token <string> WORD STRING BACK_QUOTED CODE IPV4_ADDR IPV6_ADDR
%type <direction> direction
%type <ip_ecn> opt_ip_info
%type <ip_ecn> ip_ecn
%type <option> option options opt_options
%type <event> event events event_time action
%type <time_usecs> time opt_end_time
%type <packet> packet_spec
%type <packet> sctp_packet_spec tcp_packet_spec
%type <packet> udp_packet_spec udplite_packet_spec
%type <packet> icmp_packet_spec
%type <packet> packet_prefix
%type <syscall> syscall_spec
%type <command> command_spec
%type <code> code_spec
%type <mpls_stack> mpls_stack
%type <mpls_stack_entry> mpls_stack_entry
%type <integer> opt_mpls_stack_bottom
%type <integer> opt_icmp_mtu
%type <string> icmp_type opt_icmp_code flags
%type <string> opt_tcp_fast_open_cookie tcp_fast_open_cookie
%type <string> opt_note note word_list
%type <string> option_flag option_value script
%type <window> opt_window
%type <sequence_number> opt_ack
%type <tcp_sequence_info> seq
%type <transport_info> opt_icmp_echoed
%type <tcp_options> opt_tcp_options tcp_option_list
%type <tcp_option> tcp_option sack_block_list sack_block
%type <string> function_name
%type <expression_list> expression_list function_arguments
%type <expression> expression binary_expression array
%type <expression> decimal_integer hex_integer data
%type <expression> inaddr sockaddr msghdr iovec pollfd opt_revents
%type <expression> linger l_onoff l_linger
%type <expression> sctp_status sstat_state sstat_rwnd sstat_unackdata sstat_penddata
%type <expression> sstat_instrms sstat_outstrms sstat_fragmentation_point sstat_primary
%type <expression> sctp_initmsg sinit_num_ostreams sinit_max_instreams sinit_max_attempts
%type <expression> sinit_max_init_timeo sctp_assoc_value sctp_stream_value
%type <expression> sctp_sackinfo sack_delay sack_freq
%type <expression> sctp_rtoinfo srto_initial srto_max srto_min sctp_paddrinfo
%type <expression> sctp_paddrparams spp_address spp_hbinterval spp_pathmtu spp_pathmaxrxt
%type <expression> spp_flags spp_ipv6_flowlabel spp_dscp
%type <expression> spinfo_address spinfo_state spinfo_cwnd spinfo_srtt spinfo_rto spinfo_mtu
%type <expression> sasoc_asocmaxrxt sasoc_number_peer_destinations sasoc_peer_rwnd
%type <expression> sasoc_local_rwnd sasoc_cookie_life sctp_assocparams
%type <expression> sctp_sndinfo snd_sid snd_flags snd_ppid snd_context
%type <expression> sctp_event se_type se_on sctp_setadaptation null
%type <expression> sctp_sndrcvinfo sinfo_stream sinfo_ssn sinfo_flags sinfo_ppid sinfo_context
%type <expression> sinfo_timetolive sinfo_tsn sinfo_cumtsn
%type <expression> sctp_prinfo sctp_authinfo pr_policy sctp_sendv_spa
%type <expression> sctp_rcvinfo rcv_sid rcv_ssn rcv_flags rcv_ppid rcv_tsn rcv_cumtsn rcv_context
%type <expression> sctp_nxtinfo nxt_sid nxt_flags nxt_ppid nxt_length sctp_recvv_rn
%type <expression> sctp_shutdown_event sse_type sse_flags sse_length
%type <expression> sctp_sender_dry_event sender_dry_type sender_dry_flags sender_dry_length sender_dry_assoc_id
%type <expression> sctp_event_subscribe
%type <expression> sctp_assoc_change sac_type sac_flags sac_length sac_state sac_error sac_outbound_streams
%type <expression> sac_inbound_streams sac_assoc_id sac_info
%type <expression> sctp_send_failed_event ssfe_type ssfe_flags ssfe_length ssfe_error ssfe_assoc_id ssfe_data
%type <expression> sctp_authkey_event auth_type auth_flags auth_length auth_keynumber auth_indication auth_assoc_id
%type <expression> sctp_remote_error sre_type sre_flags sre_length sre_error sre_assoc_id sre_data
%type <expression> sctp_pdapi_event pdapi_type pdapi_flags pdapi_length pdapi_indication pdapi_stream pdapi_seq pdapi_assoc_id
%type <expression> sctp_paddr_change spc_type spc_flags spc_length spc_aaddr spc_error spc_state spc_assoc_id
%type <expression> sctp_send_failed ssf_type ssf_length ssf_flags ssf_error ssf_info ssf_assoc_id ssf_data
%type <expression> sctp_adaptation_event sai_type sai_flags sai_length sai_adaptation_ind sai_assoc_id
%type <expression> sctp_tlv sn_type sn_flags sn_length
%type <errno_info> opt_errno
%type <chunk_list> sctp_chunk_list_spec
%type <chunk_list_item> sctp_chunk_spec
%type <chunk_list_item> sctp_generic_chunk_spec
%type <chunk_list_item> sctp_data_chunk_spec
%type <chunk_list_item> sctp_init_chunk_spec sctp_init_ack_chunk_spec
%type <chunk_list_item> sctp_sack_chunk_spec
%type <chunk_list_item> sctp_heartbeat_chunk_spec sctp_heartbeat_ack_chunk_spec
%type <chunk_list_item> sctp_abort_chunk_spec
%type <chunk_list_item> sctp_shutdown_chunk_spec
%type <chunk_list_item> sctp_shutdown_ack_chunk_spec
%type <chunk_list_item> sctp_error_chunk_spec
%type <chunk_list_item> sctp_cookie_echo_chunk_spec sctp_cookie_ack_chunk_spec
%type <chunk_list_item> sctp_ecne_chunk_spec sctp_cwr_chunk_spec
%type <chunk_list_item> sctp_shutdown_complete_chunk_spec
%type <chunk_list_item> sctp_i_data_chunk_spec
%type <chunk_list_item> sctp_pad_chunk_spec
%type <parameter_list> opt_parameter_list_spec sctp_parameter_list_spec
%type <parameter_list_item> sctp_parameter_spec
%type <parameter_list_item> sctp_generic_parameter_spec
%type <parameter_list_item> sctp_heartbeat_information_parameter_spec
%type <parameter_list_item> sctp_ipv4_address_parameter_spec
%type <parameter_list_item> sctp_ipv6_address_parameter_spec
%type <parameter_list_item> sctp_state_cookie_parameter_spec
%type <parameter_list_item> sctp_unrecognized_parameter_parameter_spec
%type <parameter_list_item> sctp_cookie_preservative_parameter_spec
%type <parameter_list_item> sctp_hostname_address_parameter_spec
%type <parameter_list_item> sctp_supported_address_types_parameter_spec
%type <parameter_list_item> sctp_ecn_capable_parameter_spec
%type <parameter_list_item> sctp_supported_extensions_parameter_spec
%type <parameter_list_item> sctp_adaptation_indication_parameter_spec
%type <parameter_list_item> sctp_pad_parameter_spec
%type <cause_list> opt_cause_list_spec sctp_cause_list_spec
%type <cause_list_item> sctp_cause_spec
%type <cause_list_item> sctp_generic_cause_spec
%type <cause_list_item> sctp_invalid_stream_identifier_cause_spec
%type <cause_list_item> sctp_missing_mandatory_parameter_cause_spec
%type <cause_list_item> sctp_stale_cookie_error_cause_spec
%type <cause_list_item> sctp_out_of_resources_cause_spec
%type <cause_list_item> sctp_unresolvable_address_cause_spec
%type <cause_list_item> sctp_unrecognized_chunk_type_cause_spec
%type <cause_list_item> sctp_invalid_mandatory_parameter_cause_spec
%type <cause_list_item> sctp_unrecognized_parameters_cause_spec
%type <cause_list_item> sctp_no_user_data_cause_spec
%type <cause_list_item> sctp_cookie_received_while_shutdown_cause_spec
%type <cause_list_item> sctp_restart_with_new_addresses_cause_spec
%type <cause_list_item> sctp_user_initiated_abort_cause_spec
%type <cause_list_item> sctp_protocol_violation_cause_spec
%type <integer> chunk_type opt_chunk_type opt_parameter_type opt_cause_code
%type <integer> opt_flags opt_data_flags opt_abort_flags
%type <integer> opt_shutdown_complete_flags opt_i_data_flags opt_len
%type <integer> opt_tag opt_a_rwnd opt_os opt_is opt_tsn opt_sid opt_ssn
%type <integer> opt_mid opt_fsn
%type <integer> opt_cum_tsn opt_ppid
%type <byte_list> opt_val opt_info byte_list chunk_types_list
%type <byte_list_item> byte
%type <sack_block_list> opt_gaps gap_list opt_dups dup_list
%type <sack_block_list_item> gap dup
%type <address_type_list> address_types_list
%type <address_type_list_item> address_type
%type <parameter_type_list> parameter_types_list
%type <parameter_type_list_item> parameter_type

%%  /* The grammar follows. */

script
: opt_options opt_init_command events {
	$$ = NULL;		/* The parser output is in out_script */
}
;

opt_options
:		{
	$$ = NULL;
	parse_and_finalize_config(invocation);
}
| options	{
	$$ = $1;
	parse_and_finalize_config(invocation);
}
;

options
: option		{
	out_script->option_list = $1;
	$$ = $1;		/* return the tail so we can append to it */
}
| options option	{
	$1->next = $2;
	$$ = $2;		/* return the tail so we can append to it */
}
;

option
: option_flag '=' option_value {
	$$ = new_option($1, $3);
}

option_flag
: OPTION	{ $$ = $1; }
;

option_value
: INTEGER	{ $$ = strdup(yytext); }
| WORD		{ $$ = $1; }
| STRING	{ $$ = $1; }
| IPV4_ADDR	{ $$ = $1; }
| IPV6_ADDR	{ $$ = $1; }
| IPV4		{ $$ = strdup("ipv4"); }
| IPV6		{ $$ = strdup("ipv6"); }
;

opt_init_command
:               { }
| init_command  { }
;

init_command
: command_spec  { out_script->init_command = $1; }
;

events
: event        {
	out_script->event_list = $1;  /* save pointer to event list as output
				       * of parser */
	$$ = $1;          /* return the tail so that we can append to it */
}
| events event {
	$1->next = $2;    /* link new event to the end of the existing list */
	$$ = $2;          /* return the tail so that we can append to it */
}
;

event
: event_time action  {
	$$ = $2;
	$$->line_number = $1->line_number;   /* use timestamp's line */
	$$->time_usecs  = $1->time_usecs;
	$$->time_usecs_end  = $1->time_usecs_end;
	$$->time_type = $1->time_type;

	if ($$->time_usecs_end != NO_TIME_RANGE) {
		if ($$->time_usecs_end < $$->time_usecs)
			semantic_error("time range is backwards");
	}
	if ($$->time_type == ANY_TIME &&  ($$->type != PACKET_EVENT ||
	    packet_direction($$->event.packet) != DIRECTION_OUTBOUND)) {
		yylineno = $$->line_number;
		semantic_error("event time <star> can only be used with "
			       "outbound packets");
	} else if (($$->time_type == ABSOLUTE_RANGE_TIME ||
		    $$->time_type == RELATIVE_RANGE_TIME) &&
	           ($$->type != PACKET_EVENT ||
		    packet_direction($$->event.packet) != DIRECTION_OUTBOUND)) {
		yylineno = $$->line_number;
		semantic_error("event time range can only be used with "
			       "outbound packets");
	}
	free($1);
}
;

event_time
: '+' time	{
	$$ = new_event(INVALID_EVENT);
	$$->line_number = @2.first_line;
	$$->time_usecs = $2;
	$$->time_type = RELATIVE_TIME;
}
| time         {
	$$ = new_event(INVALID_EVENT);
	$$->line_number = @1.first_line;
	$$->time_usecs = $1;
	$$->time_type = ABSOLUTE_TIME;
}
| '*'		{
	$$ = new_event(INVALID_EVENT);
	$$->line_number = @1.first_line;
	$$->time_type = ANY_TIME;
}
| time '~' time	{
	$$ = new_event(INVALID_EVENT);
	$$->line_number = @1.first_line;
	$$->time_type = ABSOLUTE_RANGE_TIME;
	$$->time_usecs = $1;
	$$->time_usecs_end = $3;
}
| '+' time '~' '+' time {
	$$ = new_event(INVALID_EVENT);
	$$->line_number = @1.first_line;
	$$->time_type = RELATIVE_RANGE_TIME;
	$$->time_usecs = $2;
	$$->time_usecs_end = $5;
}
;

time
: FLOAT        {
	if ($1 < 0) {
		semantic_error("negative time");
	}
	$$ = (s64)($1 * 1.0e6); /* convert float secs to s64 microseconds */
}
| INTEGER	{
	if ($1 < 0) {
		semantic_error("negative time");
	}
	$$ = (s64)($1 * 1000000); /* convert int secs to s64 microseconds */
}
;

action
: packet_spec  { $$ = new_event(PACKET_EVENT);  $$->event.packet  = $1; }
| syscall_spec { $$ = new_event(SYSCALL_EVENT); $$->event.syscall = $1; }
| command_spec { $$ = new_event(COMMAND_EVENT); $$->event.command = $1; }
| code_spec    { $$ = new_event(CODE_EVENT);    $$->event.code    = $1; }
;

packet_spec
: sctp_packet_spec    { $$ = $1; }
| tcp_packet_spec     { $$ = $1; }
| udp_packet_spec     { $$ = $1; }
| udplite_packet_spec { $$ = $1; }
| icmp_packet_spec    { $$ = $1; }
;

sctp_packet_spec
: packet_prefix opt_ip_info SCTP ':' sctp_chunk_list_spec {
	char *error = NULL;
	struct packet *outer = $1, *inner = NULL;
	enum direction_t direction = outer->direction;

	inner = new_sctp_packet(in_config->wire_protocol, direction, $2,
	                        -1, false, $5, &error);
	if (inner == NULL) {
		assert(error != NULL);
		semantic_error(error);
		free(error);
	}

	$$ = packet_encapsulate_and_free(outer, inner);
}
| packet_prefix opt_ip_info SCTP '(' BAD_CRC32C ')' ':' sctp_chunk_list_spec {
	char *error = NULL;
	struct packet *outer = $1, *inner = NULL;
	enum direction_t direction = outer->direction;

	inner = new_sctp_packet(in_config->wire_protocol, direction, $2,
	                        -1, true, $8, &error);
	if (inner == NULL) {
		assert(error != NULL);
		semantic_error(error);
		free(error);
	}

	$$ = packet_encapsulate_and_free(outer, inner);
}
| packet_prefix opt_ip_info SCTP '(' TAG '=' INTEGER ')' ':' sctp_chunk_list_spec {
	char *error = NULL;
	struct packet *outer = $1, *inner = NULL;
	enum direction_t direction = outer->direction;

	if (!is_valid_u32($7)) {
		semantic_error("tag value out of range");
	}
	inner = new_sctp_packet(in_config->wire_protocol, direction, $2,
	                        $7, false, $10, &error);
	if (inner == NULL) {
		assert(error != NULL);
		semantic_error(error);
		free(error);
	}

	$$ = packet_encapsulate_and_free(outer, inner);
}
| packet_prefix opt_ip_info SCTP '(' BAD_CRC32C ',' TAG '=' INTEGER ')' ':' sctp_chunk_list_spec {
	char *error = NULL;
	struct packet *outer = $1, *inner = NULL;
	enum direction_t direction = outer->direction;

	if (!is_valid_u32($9)) {
		semantic_error("tag value out of range");
	}
	inner = new_sctp_packet(in_config->wire_protocol, direction, $2,
	                        $9, true, $12, &error);
	if (inner == NULL) {
		assert(error != NULL);
		semantic_error(error);
		free(error);
	}

	$$ = packet_encapsulate_and_free(outer, inner);
}
;

sctp_chunk_list_spec
:                                          { $$ = sctp_chunk_list_new(); }
| sctp_chunk_spec                          { $$ = sctp_chunk_list_new();
                                             sctp_chunk_list_append($$, $1); }
| sctp_chunk_list_spec ';' sctp_chunk_spec { $$ = $1;
                                             sctp_chunk_list_append($1, $3); }
;

sctp_chunk_spec
: sctp_generic_chunk_spec           { $$ = $1; }
| sctp_data_chunk_spec              { $$ = $1; }
| sctp_init_chunk_spec              { $$ = $1; }
| sctp_init_ack_chunk_spec          { $$ = $1; }
| sctp_sack_chunk_spec              { $$ = $1; }
| sctp_heartbeat_chunk_spec         { $$ = $1; }
| sctp_heartbeat_ack_chunk_spec     { $$ = $1; }
| sctp_abort_chunk_spec             { $$ = $1; }
| sctp_shutdown_chunk_spec          { $$ = $1; }
| sctp_shutdown_ack_chunk_spec      { $$ = $1; }
| sctp_error_chunk_spec             { $$ = $1; }
| sctp_cookie_echo_chunk_spec       { $$ = $1; }
| sctp_cookie_ack_chunk_spec        { $$ = $1; }
| sctp_ecne_chunk_spec              { $$ = $1; }
| sctp_cwr_chunk_spec               { $$ = $1; }
| sctp_shutdown_complete_chunk_spec { $$ = $1; }
| sctp_i_data_chunk_spec            { $$ = $1; }
| sctp_pad_chunk_spec               { $$ = $1; }
;

chunk_type
: HEX_INTEGER {
	if (!is_valid_u8($1)) {
		semantic_error("type value out of range");
	}
	$$ = $1;
}
| INTEGER {
	if (!is_valid_u8($1)) {
		semantic_error("type value out of range");
	}
	$$ = $1;
}
| DATA {
	$$ = SCTP_DATA_CHUNK_TYPE;
}
| INIT {
	$$ = SCTP_INIT_CHUNK_TYPE;
}
| INIT_ACK {
	$$ = SCTP_INIT_ACK_CHUNK_TYPE;
}
| SACK {
	$$ = SCTP_SACK_CHUNK_TYPE;
}
| HEARTBEAT {
	$$ = SCTP_HEARTBEAT_CHUNK_TYPE;
}
| HEARTBEAT_ACK {
	$$ = SCTP_HEARTBEAT_ACK_CHUNK_TYPE;
}
| ABORT {
	$$ = SCTP_ABORT_CHUNK_TYPE;
}
| SHUTDOWN {
	$$ = SCTP_SHUTDOWN_CHUNK_TYPE;
}
| SHUTDOWN_ACK {
	$$ = SCTP_SHUTDOWN_ACK_CHUNK_TYPE;
}
| ERROR {
	$$ = SCTP_ERROR_CHUNK_TYPE;
}
| COOKIE_ECHO {
	$$ = SCTP_COOKIE_ECHO_CHUNK_TYPE;
}
| COOKIE_ACK {
	$$ = SCTP_COOKIE_ACK_CHUNK_TYPE;
}
| ECNE {
	$$ = SCTP_ECNE_CHUNK_TYPE;
}
| CWR {
	$$ = SCTP_CWR_CHUNK_TYPE;
}
| SHUTDOWN_COMPLETE{
	$$ = SCTP_SHUTDOWN_COMPLETE_CHUNK_TYPE;
}
| I_DATA {
	$$ = SCTP_I_DATA_CHUNK_TYPE;
}
| PAD {
	$$ = SCTP_PAD_CHUNK_TYPE;
}
;

opt_chunk_type
: TYPE '=' ELLIPSIS    { $$ = -1; }
| TYPE '=' chunk_type {
	$$ = $3;
}
;

opt_flags
: FLAGS '=' ELLIPSIS    { $$ = -1; }
| FLAGS '=' HEX_INTEGER {
	if (!is_valid_u8($3)) {
		semantic_error("flags value out of range");
	}
	$$ = $3;
}
| FLAGS '=' INTEGER     {
	if (!is_valid_u8($3)) {
		semantic_error("flags value out of range");
	}
	$$ = $3;
}
;

opt_len
: LEN '=' ELLIPSIS { $$ = -1; }
| LEN '=' INTEGER  {
	if (!is_valid_u16($3)) {
		semantic_error("length value out of range");
	}
	$$ = $3;
}
;

opt_val
: VAL '=' ELLIPSIS          { $$ = NULL; }
| VAL '=' '[' ELLIPSIS ']'  { $$ = NULL; }
| VAL '=' '[' byte_list ']' { $$ = $4; }
;

opt_info
: CAUSE_INFO '=' ELLIPSIS          { $$ = NULL; }
| CAUSE_INFO '=' '[' ELLIPSIS ']'  { $$ = NULL; }
| CAUSE_INFO '=' '[' byte_list ']' { $$ = $4; }
;

byte_list
:                    { $$ = sctp_byte_list_new(); }
| byte               { $$ = sctp_byte_list_new();
                       sctp_byte_list_append($$, $1); }
| byte_list ',' byte { $$ = $1;
                       sctp_byte_list_append($1, $3); }
;

byte
: HEX_INTEGER {
	if (!is_valid_u8($1)) {
		semantic_error("byte value out of range");
	}
	$$ = sctp_byte_list_item_new($1);
}
| INTEGER {
	if (!is_valid_u8($1)) {
		semantic_error("byte value out of range");
	}
	$$ = sctp_byte_list_item_new($1);
}
;

opt_data_flags
: FLAGS '=' ELLIPSIS    { $$ = -1; }
| FLAGS '=' HEX_INTEGER {
	if (!is_valid_u8($3)) {
		semantic_error("flags value out of range");
	}
	$$ = $3;
}
| FLAGS '=' INTEGER     {
	if (!is_valid_u8($3)) {
		semantic_error("flags value out of range");
	}
	$$ = $3;
}
| FLAGS '=' WORD        {
	u64 flags;
	char *c;

	flags = 0;
	for (c = $3; *c != '\0'; c++) {
		switch (*c) {
		case 'I':
			if (flags & SCTP_DATA_CHUNK_I_BIT) {
				semantic_error("I-bit specified multiple times");
			} else {
				flags |= SCTP_DATA_CHUNK_I_BIT;
			}
			break;
		case 'U':
			if (flags & SCTP_DATA_CHUNK_U_BIT) {
				semantic_error("U-bit specified multiple times");
			} else {
				flags |= SCTP_DATA_CHUNK_U_BIT;
			}
			break;
		case 'B':
			if (flags & SCTP_DATA_CHUNK_B_BIT) {
				semantic_error("B-bit specified multiple times");
			} else {
				flags |= SCTP_DATA_CHUNK_B_BIT;
			}
			break;
		case 'E':
			if (flags & SCTP_DATA_CHUNK_E_BIT) {
				semantic_error("E-bit specified multiple times");
			} else {
				flags |= SCTP_DATA_CHUNK_E_BIT;
			}
			break;
		default:
			semantic_error("Only expecting IUBE as flags");
			break;
		}
	}
	$$ = flags;
}
;

opt_abort_flags
: FLAGS '=' ELLIPSIS    { $$ = -1; }
| FLAGS '=' HEX_INTEGER {
	if (!is_valid_u8($3)) {
		semantic_error("flags value out of range");
	}
	$$ = $3;
}
| FLAGS '=' INTEGER     {
	if (!is_valid_u8($3)) {
		semantic_error("flags value out of range");
	}
	$$ = $3;
}
| FLAGS '=' WORD        {
	u64 flags;
	char *c;

	flags = 0;
	for (c = $3; *c != '\0'; c++) {
		switch (*c) {
		case 'T':
			if (flags & SCTP_ABORT_CHUNK_T_BIT) {
				semantic_error("T-bit specified multiple times");
			} else {
				flags |= SCTP_ABORT_CHUNK_T_BIT;
			}
			break;
		default:
			semantic_error("Only expecting T as flags");
			break;
		}
	}
	$$ = flags;
}
;

opt_shutdown_complete_flags
: FLAGS '=' ELLIPSIS    { $$ = -1; }
| FLAGS '=' HEX_INTEGER {
	if (!is_valid_u8($3)) {
		semantic_error("flags value out of range");
	}
	$$ = $3;
}
| FLAGS '=' INTEGER     {
	if (!is_valid_u8($3)) {
		semantic_error("flags value out of range");
	}
	$$ = $3;
}
| FLAGS '=' WORD        {
	u64 flags;
	char *c;

	flags = 0;
	for (c = $3; *c != '\0'; c++) {
		switch (*c) {
		case 'T':
			if (flags & SCTP_SHUTDOWN_COMPLETE_CHUNK_T_BIT) {
				semantic_error("T-bit specified multiple times");
			} else {
				flags |= SCTP_SHUTDOWN_COMPLETE_CHUNK_T_BIT;
			}
			break;
		default:
			semantic_error("Only expecting T as flags");
			break;
		}
	}
	$$ = flags;
}
;

opt_i_data_flags
: FLAGS '=' ELLIPSIS    { $$ = -1; }
| FLAGS '=' HEX_INTEGER {
	if (!is_valid_u8($3)) {
		semantic_error("flags value out of range");
	}
	$$ = $3;
}
| FLAGS '=' INTEGER     {
	if (!is_valid_u8($3)) {
		semantic_error("flags value out of range");
	}
	$$ = $3;
}
| FLAGS '=' WORD        {
	u64 flags;
	char *c;

	flags = 0;
	for (c = $3; *c != '\0'; c++) {
		switch (*c) {
		case 'I':
			if (flags & SCTP_I_DATA_CHUNK_I_BIT) {
				semantic_error("I-bit specified multiple times");
			} else {
				flags |= SCTP_I_DATA_CHUNK_I_BIT;
			}
			break;
		case 'U':
			if (flags & SCTP_I_DATA_CHUNK_U_BIT) {
				semantic_error("U-bit specified multiple times");
			} else {
				flags |= SCTP_I_DATA_CHUNK_U_BIT;
			}
			break;
		case 'B':
			if (flags & SCTP_I_DATA_CHUNK_B_BIT) {
				semantic_error("B-bit specified multiple times");
			} else {
				flags |= SCTP_I_DATA_CHUNK_B_BIT;
			}
			break;
		case 'E':
			if (flags & SCTP_I_DATA_CHUNK_E_BIT) {
				semantic_error("E-bit specified multiple times");
			} else {
				flags |= SCTP_I_DATA_CHUNK_E_BIT;
			}
			break;
		default:
			semantic_error("Only expecting IUBE as flags");
			break;
		}
	}
	$$ = flags;
}
;

opt_tag
: TAG '=' ELLIPSIS { $$ = -1; }
| TAG '=' INTEGER  {
	if (!is_valid_u32($3)) {
		semantic_error("tag value out of range");
	}
	$$ = $3;
}
;

opt_a_rwnd
: A_RWND '=' ELLIPSIS   { $$ = -1; }
| A_RWND '=' INTEGER    {
	if (!is_valid_u32($3)) {
		semantic_error("a_rwnd value out of range");
	}
	$$ = $3;
}
;

opt_os
: OS '=' ELLIPSIS { $$ = -1; }
| OS '=' INTEGER  {
	if (!is_valid_u16($3)) {
		semantic_error("os value out of range");
	}
	$$ = $3;
}
;

opt_is
: IS '=' ELLIPSIS { $$ = -1; }
| IS '=' INTEGER  {
	if (!is_valid_u16($3)) {
		semantic_error("is value out of range");
	}
	$$ = $3;
}
;

opt_tsn
: TSN '=' ELLIPSIS { $$ = -1; }
| TSN '=' INTEGER  {
	if (!is_valid_u32($3)) {
		semantic_error("tsn value out of range");
	}
	$$ = $3;
}
;

opt_sid
: SID '=' ELLIPSIS { $$ = -1; }
| SID '=' INTEGER  {
	if (!is_valid_u16($3)) {
		semantic_error("sid value out of range");
	}
	$$ = $3;
}
;

opt_ssn
: SSN '=' ELLIPSIS { $$ = -1; }
| SSN '=' INTEGER  {
	if (!is_valid_u16($3)) {
		semantic_error("ssn value out of range");
	}
	$$ = $3;
}
;

opt_mid
: MID '=' ELLIPSIS { $$ = -1; }
| MID '=' INTEGER  {
	if (!is_valid_u32($3)) {
		semantic_error("mid value out of range");
	}
	$$ = $3;
}
;

opt_ppid
: PPID '=' ELLIPSIS { $$ = -1; }
| PPID '=' INTEGER  {
	if (!is_valid_u32($3)) {
		semantic_error("ppid value out of range");
	}
	$$ = $3;
}
| PPID '=' HEX_INTEGER  {
	if (!is_valid_u32($3)) {
		semantic_error("ppid value out of range");
	}
	$$ = $3;
}
;

opt_fsn
: FSN '=' ELLIPSIS { $$ = -1; }
| FSN '=' INTEGER  {
	if (!is_valid_u32($3)) {
		semantic_error("fsn value out of range");
	}
	$$ = $3;
}
;

opt_cum_tsn
: CUM_TSN '=' ELLIPSIS { $$ = -1; }
| CUM_TSN '=' INTEGER  {
	if (!is_valid_u32($3)) {
		semantic_error("cum_tsn value out of range");
	}
	$$ = $3;
}
;

opt_gaps
: GAPS '=' ELLIPSIS         { $$ = NULL; }
| GAPS '=' '[' ELLIPSIS ']' { $$ = NULL; }
| GAPS '=' '[' gap_list ']' { $$ = $4; }
;

gap_list
:                  { $$ = sctp_sack_block_list_new(); }
| gap              { $$ = sctp_sack_block_list_new();
                     sctp_sack_block_list_append($$, $1); }
| gap_list ',' gap { $$ = $1;
                     sctp_sack_block_list_append($1, $3); }
;

gap
: INTEGER ':' INTEGER {
	if (!is_valid_u16($1)) {
		semantic_error("start value out of range");
	}
	if (!is_valid_u16($3)) {
		semantic_error("end value out of range");
	}
	$$ = sctp_sack_block_list_item_gap_new($1, $3);
}
;

opt_dups
: DUPS '=' ELLIPSIS         { $$ = NULL; }
| DUPS '=' '[' ELLIPSIS ']' { $$ = NULL; }
| DUPS '=' '[' dup_list ']' { $$ = $4; }
;

dup_list
:                  { $$ = sctp_sack_block_list_new(); }
| dup              { $$ = sctp_sack_block_list_new();
                     sctp_sack_block_list_append($$, $1); }
| dup_list ',' dup { $$ = $1;
                     sctp_sack_block_list_append($1, $3); }
;

dup
: INTEGER {
	if (!is_valid_u32($1)) {
		semantic_error("tsn value out of range");
	}
	$$ = sctp_sack_block_list_item_dup_new($1);
}
;

sctp_generic_chunk_spec
: CHUNK '[' opt_chunk_type ',' opt_flags ',' opt_len ',' opt_val ']' {
	if (($7 != -1) &&
	    (!is_valid_u16($7) || ($7 < sizeof(struct sctp_chunk)))) {
		semantic_error("length value out of range");
	}
	if (($7 != -1) && ($9 != NULL) &&
	    ($7 != sizeof(struct sctp_chunk) + $9->nr_entries)) {
		semantic_error("length value incompatible with val");
	}
	if (($7 == -1) && ($9 != NULL)) {
		semantic_error("length needs to be specified");
	}
	$$ = sctp_generic_chunk_new($3, $5, $7, $9);
}

sctp_data_chunk_spec
: DATA '[' opt_data_flags ',' opt_len ',' opt_tsn ',' opt_sid ',' opt_ssn ',' opt_ppid ']' {
	if (($5 != -1) &&
	    (!is_valid_u16($5) || ($5 < sizeof(struct sctp_data_chunk)))) {
		semantic_error("length value out of range");
	}
	$$ = sctp_data_chunk_new($3, $5, $7, $9, $11, $13);
}

sctp_init_chunk_spec
: INIT '[' opt_flags ',' opt_tag ',' opt_a_rwnd ',' opt_os ',' opt_is ',' opt_tsn opt_parameter_list_spec ']' {
	$$ = sctp_init_chunk_new($3, $5, $7, $9, $11, $13, $14);
}

sctp_init_ack_chunk_spec
: INIT_ACK '[' opt_flags ',' opt_tag ',' opt_a_rwnd ',' opt_os ',' opt_is ',' opt_tsn opt_parameter_list_spec ']' {
	$$ = sctp_init_ack_chunk_new($3, $5, $7, $9, $11, $13, $14);
}

sctp_sack_chunk_spec
: SACK '[' opt_flags ',' opt_cum_tsn ',' opt_a_rwnd ',' opt_gaps ',' opt_dups']' {
	$$ = sctp_sack_chunk_new($3, $5, $7, $9, $11);
}

sctp_heartbeat_chunk_spec
: HEARTBEAT '[' opt_flags ',' sctp_heartbeat_information_parameter_spec ']' {
	$$ = sctp_heartbeat_chunk_new($3, $5);
}

sctp_heartbeat_ack_chunk_spec
: HEARTBEAT_ACK '[' opt_flags ',' sctp_heartbeat_information_parameter_spec ']' {
	$$ = sctp_heartbeat_ack_chunk_new($3, $5);
}

sctp_abort_chunk_spec
: ABORT '[' opt_abort_flags opt_cause_list_spec ']' {
	$$ = sctp_abort_chunk_new($3, $4);
}

sctp_shutdown_chunk_spec
: SHUTDOWN '[' opt_flags ',' opt_cum_tsn ']' {
	$$ = sctp_shutdown_chunk_new($3, $5);
}

sctp_shutdown_ack_chunk_spec
: SHUTDOWN_ACK '[' opt_flags ']' {
	$$ = sctp_shutdown_ack_chunk_new($3);
}

sctp_error_chunk_spec
: ERROR '[' opt_flags opt_cause_list_spec ']' {
	$$ = sctp_error_chunk_new($3, $4);
}

sctp_cookie_echo_chunk_spec
: COOKIE_ECHO '[' opt_flags ',' opt_len ',' opt_val ']' {
	if (($5 != -1) &&
	    (!is_valid_u16($5) || ($5 < sizeof(struct sctp_cookie_echo_chunk)))) {
		semantic_error("length value out of range");
	}
	if (($5 != -1) && ($7 != NULL) &&
	    ($5 != sizeof(struct sctp_cookie_echo_chunk) + $7->nr_entries)) {
		semantic_error("length value incompatible with val");
	}
	if (($5 == -1) && ($7 != NULL)) {
		semantic_error("length needs to be specified");
	}
	$$ = sctp_cookie_echo_chunk_new($3, $5, $7);
}

sctp_cookie_ack_chunk_spec
: COOKIE_ACK '[' opt_flags ']' {
	$$ = sctp_cookie_ack_chunk_new($3);
}

sctp_ecne_chunk_spec
: ECNE '[' opt_flags ',' opt_tsn ']' {
	$$ = sctp_ecne_chunk_new($3, $5);
}

sctp_cwr_chunk_spec
: CWR '[' opt_flags ',' opt_tsn ']' {
	$$ = sctp_cwr_chunk_new($3, $5);
}

sctp_shutdown_complete_chunk_spec
: SHUTDOWN_COMPLETE '[' opt_shutdown_complete_flags ']' {
	$$ = sctp_shutdown_complete_chunk_new($3);
}

sctp_i_data_chunk_spec
: I_DATA '[' opt_i_data_flags ',' opt_len ',' opt_tsn ',' opt_sid ',' opt_mid ',' opt_ppid ']' {
	if (($5 != -1) &&
	    (!is_valid_u16($5) || ($5 < sizeof(struct sctp_i_data_chunk)))) {
		semantic_error("length value out of range");
	}
	$$ = sctp_i_data_chunk_new($3, $5, $7, $9, 0, $11, $13, -1);
}
| I_DATA '[' opt_i_data_flags ',' opt_len ',' opt_tsn ',' opt_sid ',' opt_mid ',' opt_fsn ']' {
	if (($5 != -1) &&
	    (!is_valid_u16($5) || ($5 < sizeof(struct sctp_i_data_chunk)))) {
		semantic_error("length value out of range");
	}
	$$ = sctp_i_data_chunk_new($3, $5, $7, $9, 0, $11, -1, $13);
}

sctp_pad_chunk_spec
: PAD '[' opt_flags ',' opt_len ',' VAL '=' ELLIPSIS ']' {
	if (($5 != -1) &&
	    (!is_valid_u16($5) || ($5 < sizeof(struct sctp_pad_chunk)))) {
		semantic_error("length value out of range");
	}
	$$ = sctp_pad_chunk_new($3, $5, NULL);
}

opt_parameter_list_spec
: ',' ELLIPSIS                 { $$ = NULL; }
|                              { $$ = sctp_parameter_list_new(); }
| ',' sctp_parameter_list_spec { $$ = $2; }
;

sctp_parameter_list_spec
: sctp_parameter_spec                              { $$ = sctp_parameter_list_new();
                                                     sctp_parameter_list_append($$, $1); }
| sctp_parameter_list_spec ',' sctp_parameter_spec { $$ = $1;
                                                     sctp_parameter_list_append($1, $3); }
;

sctp_parameter_spec
: sctp_generic_parameter_spec                 { $$ = $1; }
| sctp_heartbeat_information_parameter_spec   { $$ = $1; }
| sctp_ipv4_address_parameter_spec            { $$ = $1; }
| sctp_ipv6_address_parameter_spec            { $$ = $1; }
| sctp_state_cookie_parameter_spec            { $$ = $1; }
| sctp_unrecognized_parameter_parameter_spec  { $$ = $1; }
| sctp_cookie_preservative_parameter_spec     { $$ = $1; }
| sctp_hostname_address_parameter_spec        { $$ = $1; }
| sctp_supported_address_types_parameter_spec { $$ = $1; }
| sctp_ecn_capable_parameter_spec             { $$ = $1; }
| sctp_supported_extensions_parameter_spec    { $$ = $1; }
| sctp_adaptation_indication_parameter_spec   { $$ = $1; }
| sctp_pad_parameter_spec                     { $$ = $1; }
;

opt_parameter_type
: TYPE '=' ELLIPSIS    { $$ = -1; }
| TYPE '=' HEX_INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("type value out of range");
	}
	$$ = $3;
}
| TYPE '=' INTEGER     {
	if (!is_valid_u16($3)) {
		semantic_error("type value out of range");
	}
	$$ = $3;
}
;

sctp_generic_parameter_spec
: PARAMETER '[' opt_parameter_type ',' opt_len ',' opt_val ']' {
	if (($5 != -1) &&
	    (!is_valid_u16($5) || ($5 < sizeof(struct sctp_parameter)))) {
		semantic_error("length value out of range");
	}
	if (($5 != -1) && ($7 != NULL) &&
	    ($5 != sizeof(struct sctp_parameter) + $7->nr_entries)) {
		semantic_error("length value incompatible with val");
	}
	if (($5 == -1) && ($7 != NULL)) {
		semantic_error("length needs to be specified");
	}
	$$ = sctp_generic_parameter_new($3, $5, $7);
}

sctp_heartbeat_information_parameter_spec
: HEARTBEAT_INFORMATION '[' ELLIPSIS ']' {
	$$ = sctp_heartbeat_information_parameter_new(-1, NULL);
}
| HEARTBEAT_INFORMATION '[' opt_len ',' opt_val ']' {
	if (($3 != -1) &&
	    (!is_valid_u16($3) || ($3 < sizeof(struct sctp_heartbeat_information_parameter)))) {
		semantic_error("length value out of range");
	}
	if (($3 != -1) && ($5 != NULL) &&
	    ($3 != sizeof(struct sctp_heartbeat_information_parameter) + $5->nr_entries)) {
		semantic_error("length value incompatible with val");
	}
	if (($3 == -1) && ($5 != NULL)) {
		semantic_error("length needs to be specified");
	}
	$$ = sctp_heartbeat_information_parameter_new($3, $5);
}

sctp_ipv4_address_parameter_spec
: IPV4_ADDRESS '[' ADDR '=' IPV4_ADDR ']' {
	struct in_addr addr;

	if (inet_pton(AF_INET, $5, &addr) != 1) {
		semantic_error("Invalid address");
	}
	$$ = sctp_ipv4_address_parameter_new(&addr);
}
| IPV4_ADDRESS '[' ADDR '=' ELLIPSIS ']' {
	$$ = sctp_ipv4_address_parameter_new(NULL);
}

sctp_ipv6_address_parameter_spec
: IPV6_ADDRESS '[' ADDR '=' IPV6_ADDR ']' {
	struct in6_addr addr;

	if (inet_pton(AF_INET6, $5, &addr) != 1) {
		semantic_error("Invalid address");
	}
	$$ = sctp_ipv6_address_parameter_new(&addr);
}
| IPV6_ADDRESS '[' ADDR '=' ELLIPSIS ']' {
	$$ = sctp_ipv6_address_parameter_new(NULL);
}

sctp_state_cookie_parameter_spec
: STATE_COOKIE '[' ELLIPSIS ']' {
	$$ = sctp_state_cookie_parameter_new(-1, NULL);
}
| STATE_COOKIE '[' LEN '=' ELLIPSIS ',' VAL '=' ELLIPSIS ']' {
	$$ = sctp_state_cookie_parameter_new(-1, NULL);
}
| STATE_COOKIE '[' LEN '=' INTEGER ',' VAL '=' ELLIPSIS ']' {
	if (($5 < sizeof(struct sctp_state_cookie_parameter)) ||
	    !is_valid_u32($5)) {
		semantic_error("len value out of range");
	}
	$$ = sctp_state_cookie_parameter_new($5, NULL);
}

sctp_unrecognized_parameter_parameter_spec
: UNRECOGNIZED_PARAMETER '[' PARAMS '=' ELLIPSIS ']' {
	$$ = sctp_unrecognized_parameters_parameter_new(NULL);
}
| UNRECOGNIZED_PARAMETER '[' PARAMS '=' '[' sctp_parameter_list_spec ']' ']' {
	$$ = sctp_unrecognized_parameters_parameter_new($6);
}

sctp_cookie_preservative_parameter_spec
: COOKIE_PRESERVATIVE '[' INCR '=' INTEGER ']' {
	if (!is_valid_u32($5)) {
		semantic_error("increment value out of range");
	}
	$$ = sctp_cookie_preservative_parameter_new($5);
}
| COOKIE_PRESERVATIVE '[' INCR '=' ELLIPSIS ']' {
	$$ = sctp_cookie_preservative_parameter_new(-1);
}

sctp_hostname_address_parameter_spec
: HOSTNAME_ADDRESS '[' ADDR '=' STRING ']' {
	$$ = sctp_hostname_address_parameter_new($5);
}
| HOSTNAME_ADDRESS '[' ADDR '=' ELLIPSIS ']' {
	$$ = sctp_hostname_address_parameter_new(NULL);
}

address_types_list
:                                     { $$ = sctp_address_type_list_new(); }
| address_type                        { $$ = sctp_address_type_list_new();
                                        sctp_address_type_list_append($$, $1); }
| address_types_list ',' address_type { $$ = $1;
                                        sctp_address_type_list_append($1, $3); }
;

address_type
: INTEGER       { if (!is_valid_u16($1)) {
                  semantic_error("address type value out of range");
                  }
                  $$ = sctp_address_type_list_item_new($1); }
| IPV4_TYPE     { $$ = sctp_address_type_list_item_new(SCTP_IPV4_ADDRESS_PARAMETER_TYPE); }
| IPV6_TYPE     { $$ = sctp_address_type_list_item_new(SCTP_IPV6_ADDRESS_PARAMETER_TYPE); }
| HOSTNAME_TYPE { $$ = sctp_address_type_list_item_new(SCTP_HOSTNAME_ADDRESS_PARAMETER_TYPE); }
;

sctp_supported_address_types_parameter_spec
: SUPPORTED_ADDRESS_TYPES '[' TYPES '=' ELLIPSIS ']' {
	$$ = sctp_supported_address_types_parameter_new(NULL);
}
| SUPPORTED_ADDRESS_TYPES '[' TYPES '=' '[' address_types_list ']' ']' {
	$$ = sctp_supported_address_types_parameter_new($6);
}


sctp_ecn_capable_parameter_spec
: ECN_CAPABLE '[' ']' {
	$$ = sctp_ecn_capable_parameter_new();
}

chunk_types_list
: {
	$$ = sctp_byte_list_new();
}
| chunk_type {
	$$ = sctp_byte_list_new();
	sctp_byte_list_append($$, sctp_byte_list_item_new($1));
}
| chunk_types_list ',' chunk_type {
	$$ = $1;
	sctp_byte_list_append($1, sctp_byte_list_item_new($3));
}
;

sctp_adaptation_indication_parameter_spec
: ADAPTATION_INDICATION '[' ADAPTATION_CODE_POINT '=' INTEGER ']' {
	if (!is_valid_u32($5))
		semantic_error("adaptation_indication_code ot of range");
	$$ = sctp_adaptation_indication_parameter_new($5);
};

sctp_supported_extensions_parameter_spec
: SUPPORTED_EXTENSIONS '[' TYPES '=' ELLIPSIS ']' {
	$$ = sctp_supported_extensions_parameter_new(NULL);
}
| SUPPORTED_EXTENSIONS '[' TYPES '=' '[' chunk_types_list ']' ']' {
	$$ = sctp_supported_extensions_parameter_new($6);
}

sctp_pad_parameter_spec
: PAD '[' ELLIPSIS ']' {
	$$ = sctp_pad_parameter_new(-1, NULL);
}
| PAD '[' LEN '=' ELLIPSIS ',' VAL '=' ELLIPSIS ']' {
	$$ = sctp_pad_parameter_new(-1, NULL);
}
| PAD '[' LEN '=' INTEGER ',' VAL '=' ELLIPSIS ']' {
	if (($5 < sizeof(struct sctp_pad_parameter)) ||
	    !is_valid_u32($5)) {
		semantic_error("len value out of range");
	}
	$$ = sctp_pad_parameter_new($5, NULL);
}

opt_cause_list_spec
: ',' ELLIPSIS             { $$ = NULL; }
|                          { $$ = sctp_cause_list_new(); }
| ',' sctp_cause_list_spec { $$ = $2; }
;

sctp_cause_list_spec
: sctp_cause_spec                          { $$ = sctp_cause_list_new();
                                             sctp_cause_list_append($$, $1); }
| sctp_cause_list_spec ',' sctp_cause_spec { $$ = $1;
                                             sctp_cause_list_append($1, $3); }
;

sctp_cause_spec
: sctp_generic_cause_spec                        { $$ = $1; }
| sctp_invalid_stream_identifier_cause_spec      { $$ = $1; }
| sctp_missing_mandatory_parameter_cause_spec    { $$ = $1; }
| sctp_stale_cookie_error_cause_spec             { $$ = $1; }
| sctp_out_of_resources_cause_spec               { $$ = $1; }
| sctp_unresolvable_address_cause_spec           { $$ = $1; }
| sctp_unrecognized_chunk_type_cause_spec        { $$ = $1; }
| sctp_invalid_mandatory_parameter_cause_spec    { $$ = $1; }
| sctp_unrecognized_parameters_cause_spec        { $$ = $1; }
| sctp_no_user_data_cause_spec                   { $$ = $1; }
| sctp_cookie_received_while_shutdown_cause_spec { $$ = $1; }
| sctp_restart_with_new_addresses_cause_spec     { $$ = $1; }
| sctp_user_initiated_abort_cause_spec           { $$ = $1; }
| sctp_protocol_violation_cause_spec             { $$ = $1; }
;

opt_cause_code
: CAUSE_CODE '=' ELLIPSIS    { $$ = -1; }
| CAUSE_CODE '=' HEX_INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("cause value out of range");
	}
	$$ = $3;
}
| CAUSE_CODE '=' INTEGER     {
	if (!is_valid_u16($3)) {
		semantic_error("cause value out of range");
	}
	$$ = $3;
}
;

sctp_generic_cause_spec
: CAUSE '[' opt_cause_code ',' opt_len ',' opt_info ']' {
	if (($5 != -1) &&
	    (!is_valid_u16($5) || ($5 < sizeof(struct sctp_cause)))) {
		semantic_error("length value out of range");
	}
	if (($5 != -1) && ($7 != NULL) &&
	    ($5 != sizeof(struct sctp_cause) + $7->nr_entries)) {
		semantic_error("length value incompatible with val");
	}
	if (($5 == -1) && ($7 != NULL)) {
		semantic_error("length needs to be specified");
	}
	$$ = sctp_generic_cause_new($3, $5, $7);
}

sctp_invalid_stream_identifier_cause_spec
: INVALID_STREAM_IDENTIFIER '[' SID '=' INTEGER ']' {
	if (!is_valid_u16($5)) {
		semantic_error("stream identifier out of range");
	}
	$$ = sctp_invalid_stream_identifier_cause_new($5);
}
| INVALID_STREAM_IDENTIFIER '[' SID '=' ELLIPSIS ']' {
	$$ = sctp_invalid_stream_identifier_cause_new(-1);
}

parameter_types_list
:                                         { $$ = sctp_parameter_type_list_new(); }
| parameter_type                          { $$ = sctp_parameter_type_list_new();
                                            sctp_parameter_type_list_append($$, $1); }
| parameter_types_list ',' parameter_type { $$ = $1;
                                            sctp_parameter_type_list_append($1, $3); }
;

parameter_type
: INTEGER       { if (!is_valid_u16($1)) {
                  semantic_error("parameter type value out of range");
                  }
                  $$ = sctp_parameter_type_list_item_new($1); }
;

sctp_missing_mandatory_parameter_cause_spec
: MISSING_MANDATORY_PARAMETER '[' TYPES '=' ELLIPSIS ']' {
	$$ = sctp_missing_mandatory_parameter_cause_new(NULL);
}
| MISSING_MANDATORY_PARAMETER '[' TYPES '=' '[' parameter_types_list ']' ']' {
	$$ = sctp_missing_mandatory_parameter_cause_new($6);
}

sctp_stale_cookie_error_cause_spec
: STALE_COOKIE_ERROR '[' STALENESS '=' INTEGER ']' {
	if (!is_valid_u32($5)) {
		semantic_error("staleness out of range");
	}
	$$ = sctp_stale_cookie_error_cause_new($5);
}
| STALE_COOKIE_ERROR '[' STALENESS '=' ELLIPSIS ']' {
	$$ = sctp_stale_cookie_error_cause_new(-1);
}

sctp_out_of_resources_cause_spec
: OUT_OF_RESOURCE '[' ']' {
	$$ = sctp_out_of_resources_cause_new();
}

sctp_unresolvable_address_cause_spec
: UNRESOLVABLE_ADDRESS '[' PARAM '=' sctp_parameter_spec ']' {
	$$ = sctp_unresolvable_address_cause_new($5);
}
| UNRESOLVABLE_ADDRESS '[' PARAM '=' ELLIPSIS ']' {
	$$ = sctp_unresolvable_address_cause_new(NULL);
}

sctp_unrecognized_chunk_type_cause_spec
: UNRECOGNIZED_CHUNK_TYPE '[' CHK '=' sctp_chunk_spec ']' {
	$$ = sctp_unrecognized_chunk_type_cause_new($5);
}
| UNRECOGNIZED_CHUNK_TYPE '[' CHK '=' ELLIPSIS ']' {
	$$ = sctp_unrecognized_chunk_type_cause_new(NULL);
}

sctp_invalid_mandatory_parameter_cause_spec
: INVALID_MANDATORY_PARAMETER '[' ']' {
	$$ = sctp_invalid_mandatory_parameter_cause_new();
}

sctp_unrecognized_parameters_cause_spec
: UNRECOGNIZED_PARAMETERS '[' PARAMS '=' '[' sctp_parameter_list_spec ']' ']' {
	$$ = sctp_unrecognized_parameters_cause_new($6);
}
| UNRECOGNIZED_PARAMETERS '[' PARAMS '=' ELLIPSIS ']' {
	$$ = sctp_unrecognized_parameters_cause_new(NULL);
}

sctp_no_user_data_cause_spec
: NO_USER_DATA '[' TSN '=' INTEGER ']' {
	if (!is_valid_u32($5)) {
		semantic_error("tsn out of range");
	}
	$$ = sctp_no_user_data_cause_new($5);
}
| NO_USER_DATA '[' TSN '=' ELLIPSIS ']' {
	$$ = sctp_no_user_data_cause_new(-1);
}

sctp_cookie_received_while_shutdown_cause_spec
: COOKIE_RECEIVED_WHILE_SHUTDOWN '[' ']' {
	$$ = sctp_cookie_received_while_shutdown_cause_new();
}

sctp_restart_with_new_addresses_cause_spec
: RESTART_WITH_NEW_ADDRESSES '[' PARAMS '=' '[' sctp_parameter_list_spec ']' ']' {
	$$ = sctp_restart_with_new_addresses_cause_new($6);
}
| RESTART_WITH_NEW_ADDRESSES '[' PARAMS '=' ELLIPSIS ']' {
	$$ = sctp_restart_with_new_addresses_cause_new(NULL);
}

sctp_user_initiated_abort_cause_spec
: USER_INITIATED_ABORT '[' CAUSE_INFO '=' STRING ']' {
	$$ = sctp_user_initiated_abort_cause_new($5);
}
| USER_INITIATED_ABORT '[' CAUSE_INFO '=' ELLIPSIS ']' {
	$$ = sctp_user_initiated_abort_cause_new(NULL);
}

sctp_protocol_violation_cause_spec
: PROTOCOL_VIOLATION '[' CAUSE_INFO '=' STRING ']' {
	$$ = sctp_protocol_violation_cause_new($5);
}
| PROTOCOL_VIOLATION '[' CAUSE_INFO '=' ELLIPSIS ']' {
	$$ = sctp_protocol_violation_cause_new(NULL);
}

tcp_packet_spec
: packet_prefix opt_ip_info flags seq opt_ack opt_window opt_tcp_options {
	char *error = NULL;
	struct packet *outer = $1, *inner = NULL;
	enum direction_t direction = outer->direction;

	if (($7 == NULL) && (direction != DIRECTION_OUTBOUND)) {
		yylineno = @7.first_line;
		semantic_error("<...> for TCP options can only be used with "
			       "outbound packets");
	}

	inner = new_tcp_packet(in_config->wire_protocol,
			       direction, $2, $3,
			       $4.start_sequence, $4.payload_bytes,
			       $5, $6, $7, &error);
	free($3);
	free($7);
	if (inner == NULL) {
		assert(error != NULL);
		semantic_error(error);
		free(error);
	}

	$$ = packet_encapsulate_and_free(outer, inner);
}
;

udp_packet_spec
: packet_prefix UDP '(' INTEGER ')' {
	char *error = NULL;
	struct packet *outer = $1, *inner = NULL;
	enum direction_t direction = outer->direction;

	if (!is_valid_u16($4)) {
		semantic_error("UDP payload size out of range");
	}

	inner = new_udp_packet(in_config->wire_protocol, direction, $4, &error);
	if (inner == NULL) {
		assert(error != NULL);
		semantic_error(error);
		free(error);
	}

	$$ = packet_encapsulate_and_free(outer, inner);
}
;

udplite_packet_spec
: packet_prefix UDPLITE '(' INTEGER ',' INTEGER ')' {
	char *error = NULL;
	struct packet *outer = $1, *inner = NULL;
	enum direction_t direction = outer->direction;

	if (!is_valid_u16($4)) {
		semantic_error("UDPLite payload size out of range");
	}
	if (!is_valid_u16($6)) {
		semantic_error("UDPLite checksum coverage out of range");
	}

	inner = new_udplite_packet(in_config->wire_protocol, direction, $4, $6,
				   &error);
	if (inner == NULL) {
		assert(error != NULL);
		semantic_error(error);
		free(error);
	}

	$$ = packet_encapsulate_and_free(outer, inner);
}
;

icmp_packet_spec
: packet_prefix opt_icmp_echoed ICMP icmp_type opt_icmp_code opt_icmp_mtu {
	char *error = NULL;
	struct packet *outer = $1, *inner = NULL;
	enum direction_t direction = outer->direction;

	inner = new_icmp_packet(in_config->wire_protocol, direction, $4, $5,
				$2.protocol, $2.payload_bytes,
				$2.start_sequence, $2.checksum_coverage,
				$2.verification_tag, $6, &error);
	free($4);
	free($5);
	if (inner == NULL) {
		semantic_error(error);
		free(error);
	}

	$$ = packet_encapsulate_and_free(outer, inner);
}
;


packet_prefix
: direction {
	$$ = packet_new(PACKET_MAX_HEADER_BYTES);
	$$->direction = $1;
}
| packet_prefix IPV4 IPV4_ADDR '>' IPV4_ADDR ':' {
	char *error = NULL;
	struct packet *packet = $1;
	char *ip_src = $3;
	char *ip_dst = $5;
	if (ipv4_header_append(packet, ip_src, ip_dst, &error))
		semantic_error(error);
	free(ip_src);
	free(ip_dst);
	$$ = packet;
}
| packet_prefix IPV6 IPV6_ADDR '>' IPV6_ADDR ':' {
	char *error = NULL;
	struct packet *packet = $1;
	char *ip_src = $3;
	char *ip_dst = $5;
	if (ipv6_header_append(packet, ip_src, ip_dst, &error))
		semantic_error(error);
	free(ip_src);
	free(ip_dst);
	$$ = packet;
}
| packet_prefix GRE ':' {
	char *error = NULL;
	struct packet *packet = $1;
	if (gre_header_append(packet, &error))
		semantic_error(error);
	$$ = packet;
}
| packet_prefix MPLS mpls_stack ':' {
	char *error = NULL;
	struct packet *packet = $1;
	struct mpls_stack *mpls_stack = $3;

	if (mpls_header_append(packet, mpls_stack, &error))
		semantic_error(error);
	free(mpls_stack);
	$$ = packet;
}
;

mpls_stack
:				{
	$$ = mpls_stack_new();
}
| mpls_stack mpls_stack_entry	{
	if (mpls_stack_append($1, $2))
		semantic_error("too many MPLS labels");
	$$ = $1;
}
;

mpls_stack_entry
:
'(' LABEL INTEGER ',' TC INTEGER ',' opt_mpls_stack_bottom TTL INTEGER ')' {
	char *error = NULL;
	s64 label = $3;
	s64 traffic_class = $6;
	bool is_stack_bottom = $8;
	s64 ttl = $10;
	struct mpls mpls;

	if (new_mpls_stack_entry(label, traffic_class, is_stack_bottom, ttl,
				 &mpls, &error))
		semantic_error(error);
	$$ = mpls;
}
;

opt_mpls_stack_bottom
:			{ $$ = 0; }
| '[' WORD ']' ','	{
	if (strcmp($2, "S") != 0)
		semantic_error("expected [S] for MPLS label stack bottom");
	free($2);
	$$ = 1;
}
;

icmp_type
: WORD		{ $$ = $1; }
;

opt_icmp_code
:		{ $$ = NULL; }
| WORD		{ $$ = $1; }
;

/* This specifies the relevant details about the packet echoed by ICMP. */
opt_icmp_echoed
:			{
	$$.protocol		= IPPROTO_TCP;
	$$.payload_bytes	= 0;
	$$.start_sequence	= 0;
}
| '[' SCTP '(' INTEGER ')' ']'	{
	$$.protocol		= IPPROTO_SCTP;
	$$.payload_bytes	= 0;
	$$.verification_tag	= $4;
}
| '[' UDP '(' INTEGER ')' ']'	{
	$$.protocol		= IPPROTO_UDP;
	$$.payload_bytes	= $4;
	$$.start_sequence	= 0;
}
| '[' UDPLITE '(' INTEGER ',' INTEGER ')' ']'	{
	$$.protocol		= IPPROTO_UDPLITE;
	$$.payload_bytes	= $4;
	$$.checksum_coverage	= $6;
}
| '[' seq ']'		{
	$$.protocol		= IPPROTO_TCP;
	$$.payload_bytes	= $2.payload_bytes;
	$$.start_sequence	= $2.start_sequence;
}
;

opt_icmp_mtu
:		{ $$ = -1; }
| MTU INTEGER	{ $$ = $2; }
;

direction
: '<'          { $$ = DIRECTION_INBOUND;  current_script_line = yylineno; }
| '>'          { $$ = DIRECTION_OUTBOUND; current_script_line = yylineno; }
;

opt_ip_info
:			{ $$ = ECN_NOCHECK; }
| '[' ip_ecn ']'	{ $$ = $2; }
;

ip_ecn
: NO_ECN		{ $$ = ECN_NONE; }
| ECT0		{ $$ = ECN_ECT0; }
| ECT1		{ $$ = ECN_ECT1; }
| ECT01		{ $$ = ECN_ECT01; }
| CE		{ $$ = ECN_CE; }
;

flags
: WORD         { $$ = $1; }
| '.'          { $$ = strdup("."); }
| WORD '.'     { asprintf(&($$), "%s.", $1); free($1); }
| '-'          { $$ = strdup(""); }  /* no TCP flags set in segment */
;

seq
: INTEGER ':' INTEGER '(' INTEGER ')' {
	if (!is_valid_u32($1)) {
		semantic_error("TCP start sequence number out of range");
	}
	if (!is_valid_u32($3)) {
		semantic_error("TCP end sequence number out of range");
	}
	if (!is_valid_u16($5)) {
		semantic_error("TCP payload size out of range");
	}
	if ($3 != ($1 +$5)) {
		semantic_error("inconsistent TCP sequence numbers and "
			       "payload size");
	}
	$$.start_sequence = $1;
	$$.payload_bytes = $5;
}
;

opt_ack
:              { $$ = 0; }
| ACK INTEGER  {
	if (!is_valid_u32($2)) {
		semantic_error("TCP ack sequence number out of range");
	}
	$$ = $2;
}
;

opt_window
:		{ $$ = -1; }
| WIN INTEGER	{
	if (!is_valid_u16($2)) {
		semantic_error("TCP window value out of range");
	}
	$$ = $2;
}
;

opt_tcp_options
:                             { $$ = tcp_options_new(); }
| '<' tcp_option_list '>'     { $$ = $2; }
| '<' ELLIPSIS '>'            { $$ = NULL; /* FLAG_OPTIONS_NOCHECK */ }
;

tcp_option_list
: tcp_option                       {
	$$ = tcp_options_new();
	if (tcp_options_append($$, $1)) {
		semantic_error("TCP option list too long");
	}
}
| tcp_option_list ',' tcp_option   {
	$$ = $1;
	if (tcp_options_append($$, $3)) {
		semantic_error("TCP option list too long");
	}
}
;

opt_tcp_fast_open_cookie
:			{ $$ = strdup(""); }
| tcp_fast_open_cookie	{ $$ = $1; }
;

tcp_fast_open_cookie
: WORD    { $$ = strdup(yytext); }
| INTEGER { $$ = strdup(yytext); }
;

tcp_option
: NOP              { $$ = tcp_option_new(TCPOPT_NOP, 1); }
| EOL              { $$ = tcp_option_new(TCPOPT_EOL, 1); }
| MSS INTEGER      {
	$$ = tcp_option_new(TCPOPT_MAXSEG, TCPOLEN_MAXSEG);
	if (!is_valid_u16($2)) {
		semantic_error("mss value out of range");
	}
	$$->data.mss.bytes = htons($2);
}
| WSCALE INTEGER   {
	$$ = tcp_option_new(TCPOPT_WINDOW, TCPOLEN_WINDOW);
	if (!is_valid_u8($2)) {
		semantic_error("window scale shift count out of range");
	}
	$$->data.window_scale.shift_count = $2;
}
| SACKOK           {
	$$ = tcp_option_new(TCPOPT_SACK_PERMITTED,
				    TCPOLEN_SACK_PERMITTED);
}
| SACK sack_block_list {
	$$ = $2;
}
| TIMESTAMP VAL INTEGER ECR INTEGER  {
	u32 val, ecr;
	$$ = tcp_option_new(TCPOPT_TIMESTAMP, TCPOLEN_TIMESTAMP);
	if (!is_valid_u32($3)) {
		semantic_error("ts val out of range");
	}
	if (!is_valid_u32($5)) {
		semantic_error("ecr val out of range");
	}
	val = $3;
	ecr = $5;
	$$->data.time_stamp.val = htonl(val);
	$$->data.time_stamp.ecr = htonl(ecr);
}
| FAST_OPEN opt_tcp_fast_open_cookie  {
	char *error = NULL;
	$$ = new_tcp_fast_open_option($2, &error);
	free($2);
	if ($$ == NULL) {
		assert(error != NULL);
		semantic_error(error);
		free(error);
	}
}
;

sack_block_list
: sack_block                 { $$ = $1; }
| sack_block_list sack_block {
	const int list_block_bytes =  $1->length - 2;
	assert(list_block_bytes > 0);
	assert((list_block_bytes % sizeof(struct sack_block)) == 0);
	const int num_blocks = list_block_bytes / sizeof(struct sack_block);
	/* Append this SACK block to the end of the array of blocks. */
	memcpy($1->data.sack.block + num_blocks, $2->data.sack.block,
		sizeof(struct sack_block));
	$1->length += sizeof(struct sack_block);
	free($2);
	$$ = $1;
}
;

sack_block
: INTEGER ':' INTEGER {
	$$ = tcp_option_new(TCPOPT_SACK, 2 + sizeof(struct sack_block));
	if (!is_valid_u32($1)) {
		semantic_error("TCP SACK left sequence number out of range");
	}
	if (!is_valid_u32($3)) {
		semantic_error("TCP SACK right sequence number out of range");
	}
	$$->data.sack.block[0].left = htonl($1);
	$$->data.sack.block[0].right = htonl($3);
}
;

syscall_spec
: opt_end_time function_name function_arguments '='
  expression opt_errno opt_note  {
	$$ = calloc(1, sizeof(struct syscall_spec));
	$$->end_usecs	= $1;
	$$->name	= $2;
	$$->arguments	= $3;
	$$->result	= $5;
	$$->error	= $6;
	$$->note	= $7;
}
;

opt_end_time
:                         { $$ = SYSCALL_NON_BLOCKING; }
| ELLIPSIS time           { $$ = $2; }
;

function_name
: WORD                    { $$ = $1; current_script_line = yylineno; }
;

function_arguments
: '(' ')'                 { $$ = NULL; }
| '(' expression_list ')' { $$ = $2; }
;

expression_list
: expression                     { $$ = new_expression_list($1); }
| expression_list ',' expression { $$ = $1; expression_list_append($1, $3); }
;

expression
: ELLIPSIS          {
	$$ = new_expression(EXPR_ELLIPSIS);
}
| decimal_integer   { $$ = $1; }
| hex_integer       { $$ = $1; }
| _HTONL_ '(' INTEGER ')' {
	if (!is_valid_u32($3)) {
		semantic_error("number out of range");
	}
	$$ = new_integer_expression(htonl((u32)$3), "%lu");
}
| _HTONL_ '(' HEX_INTEGER ')' {
	if (!is_valid_u32($3)) {
		semantic_error("number out of range");
	}
	$$ = new_integer_expression(htonl((u32)$3), "%#lx");
}
| WORD              {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $1;
}
| STRING            {
	$$ = new_expression(EXPR_STRING);
	$$->value.string = $1;
	$$->format = "\"%s\"";
}
| STRING ELLIPSIS   {
	$$ = new_expression(EXPR_STRING);
	$$->value.string = $1;
	$$->format = "\"%s\"...";
}
| binary_expression {
	$$ = $1;
}
| array             {
	$$ = $1;
}
| inaddr          {
	$$ = $1;
}
| sockaddr          {
	$$ = $1;
}
| msghdr            {
	$$ = $1;
}
| iovec             {
	$$ = $1;
}
| pollfd            {
	$$ = $1;
}
| linger            {
	$$ = $1;
}
| sctp_rtoinfo      {
	$$ = $1;
}
| sctp_initmsg      {
	$$ = $1;
}
| sctp_assoc_value  {
	$$ = $1;
}
| sctp_stream_value  {
	$$ = $1;
}
| sctp_sackinfo     {
	$$ = $1;
}
| sctp_status       {
	$$ = $1;
}
| sctp_paddrinfo    {
	$$ = $1;
}
| sctp_paddrparams  {
	$$ = $1;
}
| sctp_assocparams  {
	$$ = $1;
}
| sctp_event        {
	$$ = $1;
}
| sctp_event_subscribe {
	$$ = $1;
}
| sctp_sndinfo      {
	$$ = $1;
}
| sctp_setadaptation{
	$$ = $1;
}
| sctp_sndrcvinfo   {
	$$ = $1;
}
| sctp_prinfo       {
	$$ = $1;
}
| sctp_authinfo     {
	$$ = $1;
}
| sctp_sendv_spa    {
	$$ = $1;
}
| sctp_rcvinfo      {
	$$ = $1;
}
| sctp_nxtinfo      {
	$$ = $1;
}
| sctp_recvv_rn     {
	$$ = $1;
}
| null              {
	$$ = $1;
}
;

decimal_integer
: INTEGER           {
	$$ = new_integer_expression($1, "%ld");
}
;

hex_integer
: HEX_INTEGER       {
	$$ = new_integer_expression($1, "%#lx");
}
;

binary_expression
: expression '|' expression {       /* bitwise OR */
	$$ = new_expression(EXPR_BINARY);
	struct binary_expression *binary =
			  malloc(sizeof(struct binary_expression));
	binary->op = strdup("|");
	binary->lhs = $1;
	binary->rhs = $3;
	$$->value.binary = binary;
}
;

array
: '[' ']'                 {
	$$ = new_expression(EXPR_LIST);
	$$->value.list = NULL;
}
| '[' expression_list ']' {
	$$ = new_expression(EXPR_LIST);
	$$->value.list = $2;
}
;

inaddr
: INET_ADDR '(' STRING ')' {
	__be32 ip_address = inet_addr($3);
	$$ = new_integer_expression(ip_address, "%#lx");
}
;

sockaddr
:   '{' SA_FAMILY '=' WORD ','
	SIN_PORT '=' _HTONS_ '(' INTEGER ')' ','
	SIN_ADDR '=' INET_ADDR '(' STRING ')' '}' {
	if (strcmp($4, "AF_INET") == 0) {
		struct sockaddr_in *ipv4 = malloc(sizeof(struct sockaddr_in));
		memset(ipv4, 0, sizeof(*ipv4));
		ipv4->sin_family = AF_INET;
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
		ipv4->sin_len = sizeof(*ipv4);
#endif
		ipv4->sin_port = htons($10);
		if (inet_pton(AF_INET, $17, &ipv4->sin_addr) == 1) {
			$$ = new_expression(EXPR_SOCKET_ADDRESS_IPV4);
			$$->value.socket_address_ipv4 = ipv4;
		} else {
			free(ipv4);
			semantic_error("invalid IPv4 address");
		}
	} else if (strcmp($4, "AF_INET6") == 0) {
		struct sockaddr_in6 *ipv6 = malloc(sizeof(struct sockaddr_in6));
		memset(ipv6, 0, sizeof(*ipv6));
		ipv6->sin6_family = AF_INET6;
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
		ipv6->sin6_len = sizeof(*ipv6);
#endif
		ipv6->sin6_port = htons($10);
		if (inet_pton(AF_INET6, $17, &ipv6->sin6_addr) == 1) {
			$$ = new_expression(EXPR_SOCKET_ADDRESS_IPV6);
			$$->value.socket_address_ipv6 = ipv6;
		} else {
			free(ipv6);
			semantic_error("invalid IPv6 address");
		}
	}
}
;

data
: ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
| sctp_assoc_change         { $$ = $1; }
| sctp_paddr_change         { $$ = $1; }
| sctp_remote_error         { $$ = $1; }
| sctp_send_failed          { $$ = $1; }
| sctp_shutdown_event       { $$ = $1; }
| sctp_adaptation_event     { $$ = $1; }
| sctp_pdapi_event          { $$ = $1; }
| sctp_sender_dry_event     { $$ = $1; }
| sctp_send_failed_event    { $$ = $1; }
| sctp_authkey_event        { $$ = $1; }
| sctp_tlv                  { $$ = $1; }
;

msghdr
: '{' MSG_NAME '(' ELLIPSIS ')' '=' ELLIPSIS ','
      MSG_IOV '(' decimal_integer ')' '=' array ','
      MSG_FLAGS '=' expression '}' {
	struct msghdr_expr *msg_expr = calloc(1, sizeof(struct msghdr_expr));
	$$ = new_expression(EXPR_MSGHDR);
	$$->value.msghdr = msg_expr;
	msg_expr->msg_name	= new_expression(EXPR_ELLIPSIS);
	msg_expr->msg_namelen	= new_expression(EXPR_ELLIPSIS);
	msg_expr->msg_iov	= $14;
	msg_expr->msg_iovlen	= $11;
	msg_expr->msg_flags	= $18;
	/* TODO(ncardwell): msg_control, msg_controllen */
}
;

iovec
: '{' data ',' decimal_integer '}' {
	struct iovec_expr *iov_expr = calloc(1, sizeof(struct iovec_expr));
	$$ = new_expression(EXPR_IOVEC);
	$$->value.iovec = iov_expr;
	iov_expr->iov_base = $2;
	iov_expr->iov_len = $4;
}
| '{' IOV_BASE '=' data ',' IOV_LEN '=' decimal_integer '}' {
	struct iovec_expr *iov_expr = calloc(1, sizeof(struct iovec_expr));
	$$ = new_expression(EXPR_IOVEC);
	$$->value.iovec = iov_expr;
	iov_expr->iov_base = $4;
	iov_expr->iov_len = $8;
}
;

pollfd
: '{' FD '=' expression ',' EVENTS '=' expression opt_revents '}' {
	struct pollfd_expr *pollfd_expr = calloc(1, sizeof(struct pollfd_expr));
	$$ = new_expression(EXPR_POLLFD);
	$$->value.pollfd = pollfd_expr;
	pollfd_expr->fd = $4;
	pollfd_expr->events = $8;
	pollfd_expr->revents = $9;
}
;

opt_revents
:                                { $$ = new_integer_expression(0, "%ld"); }
| ',' REVENTS '=' expression     { $$ = $4; }
;

l_onoff
: ONOFF '=' INTEGER {
	if (!is_valid_s32($3)) {
		semantic_error("linger onoff out of range");
	}
	$$ = new_integer_expression($3, "%ld");
}
| ONOFF '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

l_linger
: LINGER '=' INTEGER {
	if (!is_valid_s32($3)) {
		semantic_error("linger out of range");
	}
	$$ = new_integer_expression($3, "%ld");
}
| LINGER '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

linger
: '{' l_onoff ',' l_linger '}' {
	$$ = new_expression(EXPR_LINGER);
	$$->value.linger = calloc(1, sizeof(struct linger_expr));
	$$->value.linger->l_onoff  = $2;
	$$->value.linger->l_linger = $4;
}
;

srto_initial
: SRTO_INITIAL '=' INTEGER {
	if (!is_valid_u32($3)){
		semantic_error("srto_initial out of range");
	}
        $$ = new_integer_expression($3, "%u");
}
| SRTO_INITIAL '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

srto_max
: SRTO_MAX '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("srto_max out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SRTO_MAX '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

srto_min
: SRTO_MIN '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("srto_min out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SRTO_MIN '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sctp_rtoinfo
: '{' srto_initial ',' srto_max ',' srto_min '}' {
	$$ = new_expression(EXPR_SCTP_RTOINFO);
	$$->value.sctp_rtoinfo = calloc(1, sizeof(struct sctp_rtoinfo_expr));
	$$->value.sctp_rtoinfo->srto_initial = $2;
	$$->value.sctp_rtoinfo->srto_max = $4;
	$$->value.sctp_rtoinfo->srto_min = $6;
}
;

sinit_num_ostreams
: SINIT_NUM_OSTREAMS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sinit_num_ostreams out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SINIT_NUM_OSTREAMS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sinit_max_instreams
: SINIT_MAX_INSTREAMS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sinit_max_instreams out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SINIT_MAX_INSTREAMS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sinit_max_attempts
: SINIT_MAX_ATTEMPTS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sinit_max_attempts out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SINIT_MAX_ATTEMPTS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sinit_max_init_timeo
: SINIT_MAX_INIT_TIMEO '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sinit_max_init_timeo out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SINIT_MAX_INIT_TIMEO '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sctp_initmsg
: '{' sinit_num_ostreams ',' sinit_max_instreams ',' sinit_max_attempts ',' sinit_max_init_timeo '}'
{
	$$ = new_expression(EXPR_SCTP_INITMSG);
	$$->value.sctp_paddrinfo = calloc(1, sizeof(struct sctp_initmsg_expr));
	$$->value.sctp_initmsg->sinit_num_ostreams = $2;
	$$->value.sctp_initmsg->sinit_max_instreams = $4;
	$$->value.sctp_initmsg->sinit_max_attempts = $6;
	$$->value.sctp_initmsg->sinit_max_init_timeo = $8;
}
;

sctp_stream_value
: '{' STREAM_ID '=' expression ',' STREAM_VALUE '=' expression '}' {
	$$ = new_expression(EXPR_SCTP_STREAM_VALUE);
	$$->value.sctp_stream_value = calloc(1, sizeof(struct sctp_stream_value_expr));
	$$->value.sctp_stream_value->stream_id = $4;
	$$->value.sctp_stream_value->stream_value = $8;
}
;

sctp_assoc_value
: '{' ASSOC_VALUE '=' expression '}' {
	$$ = new_expression(EXPR_SCTP_ASSOC_VALUE);
	$$->value.sctp_assoc_value = calloc(1, sizeof(struct sctp_assoc_value_expr));
	$$->value.sctp_assoc_value->assoc_value = $4;
}
;

sack_delay
: SACK_DELAY '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sack_delay out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SACK_DELAY '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }

sack_freq
: SACK_FREQ '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sack_freq out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SACK_FREQ '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }

sctp_sackinfo
: '{' sack_delay ',' sack_freq '}' {
	$$ = new_expression(EXPR_SCTP_SACKINFO);
	$$->value.sctp_sack_info = calloc(1, sizeof(struct sctp_sack_info_expr));
	$$->value.sctp_sack_info->sack_delay = $2;
	$$->value.sctp_sack_info->sack_freq = $4;
}
;

sstat_state
: SSTAT_STATE '=' expression { $$ = $3; }
;

sstat_rwnd
: SSTAT_RWND '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sstat_rwnd out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SSTAT_RWND '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sstat_unackdata
: SSTAT_UNACKDATA '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sstat_unackdata out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SSTAT_UNACKDATA '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sstat_penddata
: SSTAT_PENDDATA '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sstat_penddata out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SSTAT_PENDDATA '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sstat_instrms
: SSTAT_INSTRMS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sstat_instrms out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SSTAT_INSTRMS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sstat_outstrms
: SSTAT_OUTSTRMS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sstat_outstrms out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SSTAT_OUTSTRMS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sstat_fragmentation_point
: SSTAT_FRAGMENTATION_POINT '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sstat_fragmentation_point out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SSTAT_FRAGMENTATION_POINT '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sstat_primary
: SSTAT_PRIMARY '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
| SSTAT_PRIMARY '=' sctp_paddrinfo  { $$ = $3; }
;

spinfo_address
: SPINFO_ADDRESS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
| SPINFO_ADDRESS '=' sockaddr { $$ = $3; }
;

spinfo_state
: SPINFO_STATE '=' INTEGER {
	if (!is_valid_s32($3)) {
		semantic_error("spinfo_state out of range");
	}
	$$ = new_integer_expression($3, "%d");
}
| SPINFO_STATE '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| SPINFO_STATE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

spinfo_cwnd
: SPINFO_CWND '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("spinfo_cwnd out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SPINFO_CWND '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

spinfo_srtt
: SPINFO_SRTT '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("spinfo_srtt out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SPINFO_SRTT '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

spinfo_rto
: SPINFO_RTO '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("spinfo_rto out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SPINFO_RTO '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

spinfo_mtu
: SPINFO_MTU '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("spinfo_mtu out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SPINFO_MTU '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sctp_paddrinfo
: '{' spinfo_address ',' spinfo_state ',' spinfo_cwnd ',' spinfo_srtt ',' spinfo_rto ',' spinfo_mtu '}' {
	$$ = new_expression(EXPR_SCTP_PADDRINFO);
	$$->value.sctp_paddrinfo = calloc(1, sizeof(struct sctp_paddrinfo_expr));
	$$->value.sctp_paddrinfo->spinfo_address = $2;
	$$->value.sctp_paddrinfo->spinfo_state = $4;
	$$->value.sctp_paddrinfo->spinfo_cwnd = $6;
	$$->value.sctp_paddrinfo->spinfo_srtt = $8;
	$$->value.sctp_paddrinfo->spinfo_rto = $10;
	$$->value.sctp_paddrinfo->spinfo_mtu = $12;
}
;

sctp_status
: '{' sstat_state ',' sstat_rwnd ',' sstat_unackdata ',' sstat_penddata ',' sstat_instrms ',' sstat_outstrms ','
	sstat_fragmentation_point ',' sstat_primary  '}' {
	$$ = new_expression(EXPR_SCTP_STATUS);
	$$->value.sctp_status = calloc(1, sizeof(struct sctp_status_expr));
	$$->value.sctp_status->sstat_state = $2;
	$$->value.sctp_status->sstat_rwnd = $4;
	$$->value.sctp_status->sstat_unackdata = $6;
	$$->value.sctp_status->sstat_penddata = $8;
	$$->value.sctp_status->sstat_instrms = $10;
	$$->value.sctp_status->sstat_outstrms = $12;
	$$->value.sctp_status->sstat_fragmentation_point = $14;
	$$->value.sctp_status->sstat_primary = $16;
}
;

spp_address
: SPP_ADDRESS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
| SPP_ADDRESS '=' sockaddr { $$ = $3; }
;

spp_hbinterval
: SPP_HBINTERVAL '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("spp_hbinterval out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SPP_HBINTERVAL '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

spp_pathmtu
: SPP_PATHMTU '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("spp_pathmtu out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SPP_PATHMTU '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

spp_pathmaxrxt
: SPP_PATHMAXRXT '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("spp_pathmaxrxt out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SPP_PATHMAXRXT '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

spp_flags
: SPP_FLAGS '=' expression { $$ = $3; }
;

spp_ipv6_flowlabel
: SPP_IPV6_FLOWLABEL_ '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("spp_ipv6_flowlabel out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SPP_IPV6_FLOWLABEL_ '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

spp_dscp
: SPP_DSCP_ '=' INTEGER {
	if (!is_valid_u8($3)) {
		semantic_error("spp_dscp out of range");
	}
	$$ = new_integer_expression($3, "%hhu");
}
| SPP_DSCP_ '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sctp_paddrparams
: '{' spp_address ',' spp_hbinterval ',' spp_pathmaxrxt ',' spp_pathmtu ','spp_flags ',' spp_ipv6_flowlabel ',' spp_dscp'}' {
	$$ = new_expression(EXPR_SCTP_PEER_ADDR_PARAMS);
	$$->value.sctp_paddrparams = calloc(1, sizeof(struct sctp_paddrparams_expr));
	$$->value.sctp_paddrparams->spp_address = $2;
	$$->value.sctp_paddrparams->spp_hbinterval = $4;
	$$->value.sctp_paddrparams->spp_pathmaxrxt = $6;
	$$->value.sctp_paddrparams->spp_pathmtu = $8;
	$$->value.sctp_paddrparams->spp_flags = $10;
	$$->value.sctp_paddrparams->spp_ipv6_flowlabel = $12;
	$$->value.sctp_paddrparams->spp_dscp = $14;
}
;

sasoc_asocmaxrxt
: SASOC_ASOCMAXRXT '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sasoc_asocmaxrxt out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SASOC_ASOCMAXRXT '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sasoc_number_peer_destinations
: SASOC_NUMBER_PEER_DESTINATIONS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sasoc_number_peer_destinations out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SASOC_NUMBER_PEER_DESTINATIONS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sasoc_peer_rwnd
: SASOC_PEER_RWND '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sasoc_peer_rwnd out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SASOC_PEER_RWND '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sasoc_local_rwnd
: SASOC_LOCAL_RWND '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sasoc_local_rwnd out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SASOC_LOCAL_RWND '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sasoc_cookie_life
: SASOC_COOKIE_LIFE '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sasoc_cookie_life out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SASOC_COOKIE_LIFE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sctp_assocparams
: '{' sasoc_asocmaxrxt ',' sasoc_number_peer_destinations ',' sasoc_peer_rwnd ',' sasoc_local_rwnd ',' sasoc_cookie_life '}' {
	$$ = new_expression(EXPR_SCTP_ASSOCPARAMS);
        $$->value.sctp_assocparams = calloc(1, sizeof(struct sctp_assocparams_expr));
        $$->value.sctp_assocparams->sasoc_asocmaxrxt = $2;
        $$->value.sctp_assocparams->sasoc_number_peer_destinations = $4;
        $$->value.sctp_assocparams->sasoc_peer_rwnd = $6;
        $$->value.sctp_assocparams->sasoc_local_rwnd = $8;
        $$->value.sctp_assocparams->sasoc_cookie_life = $10;
}
;

se_type
: SE_TYPE '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("se_type out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SE_TYPE '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
;

se_on
: SE_ON '=' INTEGER {
	if (!is_valid_u8($3)) {
		semantic_error("se_on out of range");
	}
	$$ = new_integer_expression($3, "%hhu");
}
| SE_ON '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sctp_event
: '{' se_type ',' se_on '}' {
	$$ = new_expression(EXPR_SCTP_EVENT);
	$$->value.sctp_event = calloc(1, sizeof(struct sctp_event_expr));
	$$->value.sctp_event->se_type = $2;
	$$->value.sctp_event->se_on = $4;
}
;

sctp_event_subscribe
: '{' _SCTP_DATA_IO_EVENT_          '=' INTEGER ',' _SCTP_ASSOCIATION_EVENT_      '=' INTEGER ','
      _SCTP_ADDRESS_EVENT_          '=' INTEGER ',' _SCTP_SEND_FAILURE_EVENT_     '=' INTEGER ','
      _SCTP_PEER_ERROR_EVENT_       '=' INTEGER ',' _SCTP_SHUTDOWN_EVENT_         '=' INTEGER ','
      _SCTP_PARTIAL_DELIVERY_EVENT_ '=' INTEGER ',' _SCTP_ADAPTATION_LAYER_EVENT_ '=' INTEGER ','
      _SCTP_AUTHENTICATION_EVENT_   '=' INTEGER ',' _SCTP_SENDER_DRY_EVENT_       '=' INTEGER '}' {
	$$ = new_expression(EXPR_SCTP_EVENT_SUBSCRIBE);
	$$->value.sctp_event_subscribe = calloc(1, sizeof(struct sctp_event_subscribe_expr));
	if (!is_valid_u8($4)) {
		semantic_error("sctp_data_io_event out of range");
	}
	$$->value.sctp_event_subscribe->sctp_data_io_event =  new_integer_expression($4, "%hhu");
	if (!is_valid_u8($8)) {
		semantic_error("sctp_association_event out of range");
	}
	$$->value.sctp_event_subscribe->sctp_association_event =  new_integer_expression($8, "%hhu");
	if (!is_valid_u8($12)) {
		semantic_error("sctp_address_event out of range");
	}
	$$->value.sctp_event_subscribe->sctp_address_event =  new_integer_expression($12, "%hhu");
	if (!is_valid_u8($16)) {
		semantic_error("sctp_send_failure_event out of range");
	}
	$$->value.sctp_event_subscribe->sctp_send_failure_event =  new_integer_expression($16, "%hhu");
	if (!is_valid_u8($20)) {
		semantic_error("sctp_peer_error_event out of range");
	}
	$$->value.sctp_event_subscribe->sctp_peer_error_event =  new_integer_expression($20, "%hhu");
	if (!is_valid_u8($24)) {
		semantic_error("sctp_shutdown_event out of range");
	}
	$$->value.sctp_event_subscribe->sctp_shutdown_event =  new_integer_expression($24, "%hhu");
	if (!is_valid_u8($28)) {
		semantic_error("sctp_partial_delivery_event out of range");
	}
	$$->value.sctp_event_subscribe->sctp_partial_delivery_event =  new_integer_expression($28, "%hhu");
	if (!is_valid_u8($32)) {
		semantic_error("sctp_adaptation_layer_event out of range");
	}
	$$->value.sctp_event_subscribe->sctp_adaptation_layer_event =  new_integer_expression($32, "%hhu");
	if (!is_valid_u8($36)) {
		semantic_error("sctp_authentication_event out of range");
	}
	$$->value.sctp_event_subscribe->sctp_authentication_event =  new_integer_expression($36, "%hhu");
	if (!is_valid_u8($40)) {
		semantic_error("sctp_sender_dry_event out of range");
	}
	$$->value.sctp_event_subscribe->sctp_sender_dry_event =  new_integer_expression($40, "%hhu");
}
;

snd_sid
: SND_SID '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("snd_sid out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SND_SID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

snd_flags
: SND_FLAGS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("snd_flags out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SND_FLAGS '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| SND_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

snd_ppid
: SND_PPID '=' _HTONL_ '(' INTEGER ')'{
	if (!is_valid_u32($5)) {
		semantic_error("snd_ppid out of range");
	}
	$$ = new_integer_expression(htonl((u32)$5), "%u");
}
| SND_PPID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

snd_context
: SND_CONTEXT '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("snd_context out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SND_CONTEXT '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sctp_sndinfo
: '{' snd_sid ',' snd_flags ',' snd_ppid ',' snd_context '}' {
	$$ = new_expression(EXPR_SCTP_SNDINFO);
	$$->value.sctp_sndinfo = calloc(1, sizeof(struct sctp_sndinfo_expr));
	$$->value.sctp_sndinfo->snd_sid = $2;
	$$->value.sctp_sndinfo->snd_flags = $4;
	$$->value.sctp_sndinfo->snd_ppid = $6;
	$$->value.sctp_sndinfo->snd_context = $8;
}
;

sctp_setadaptation
: '{' SSB_ADAPTATION_IND '=' INTEGER '}' {
	$$ = new_expression(EXPR_SCTP_SETADAPTATION);
	$$->value.sctp_setadaptation = calloc(1, sizeof(struct sctp_setadaptation_expr));
	if (!is_valid_u32($4)) {
		semantic_error("ssb_adaptation_ind out of range");
	}
	$$->value.sctp_setadaptation->ssb_adaptation_ind = new_integer_expression($4, "%u");
}
;

sinfo_stream
: SINFO_STREAM '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sinfo_stream out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SINFO_STREAM '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sinfo_ssn
: SINFO_SSN '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sinfo_ssn out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SINFO_SSN '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sinfo_flags
: SINFO_FLAGS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sinfo_flags out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SINFO_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sinfo_ppid
: SINFO_PPID '=' _HTONL_ '(' INTEGER ')' {
	if (!is_valid_u32($5)) {
		semantic_error("sinfo_ppid out of range");
	}
	$$ = new_integer_expression(htonl((u32)$5), "%u");
}
| SINFO_PPID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sinfo_context
: SINFO_CONTEXT '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sinfo_context out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SINFO_CONTEXT '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sinfo_timetolive
: SINFO_TIMETOLIVE '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("snd_timetolive out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SINFO_TIMETOLIVE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sinfo_tsn
: SINFO_TSN '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sinfo_tsn out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SINFO_TSN '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sinfo_cumtsn
: SINFO_CUMTSN '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sinfo_cumtsn out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SINFO_CUMTSN '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sctp_sndrcvinfo
: '{' sinfo_stream ',' sinfo_ssn ',' sinfo_flags ',' sinfo_ppid ',' sinfo_context ',' sinfo_timetolive ',' sinfo_tsn ',' sinfo_cumtsn '}' {
	$$ = new_expression(EXPR_SCTP_SNDRCVINFO);
	$$->value.sctp_sndrcvinfo = calloc(1, sizeof(struct sctp_sndrcvinfo_expr));
	$$->value.sctp_sndrcvinfo->sinfo_stream = $2;
	$$->value.sctp_sndrcvinfo->sinfo_ssn = $4;
	$$->value.sctp_sndrcvinfo->sinfo_flags = $6;
	$$->value.sctp_sndrcvinfo->sinfo_ppid = $8;
	$$->value.sctp_sndrcvinfo->sinfo_context = $10;
	$$->value.sctp_sndrcvinfo->sinfo_timetolive = $12;
	$$->value.sctp_sndrcvinfo->sinfo_tsn = $14;
	$$->value.sctp_sndrcvinfo->sinfo_cumtsn = $16;
};

rcv_sid
: RCV_SID '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("rcv_sid out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| RCV_SID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

rcv_ssn
: RCV_SSN '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("rcv_ssn out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| RCV_SSN '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

rcv_flags
: RCV_FLAGS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("rcv_flags out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| RCV_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

rcv_ppid
: RCV_PPID '=' _HTONL_ '(' INTEGER ')' {
	if (!is_valid_u32($5)) {
		semantic_error("rcv_ppid out of range");
	}
	$$ = new_integer_expression(htonl($5), "%u");
}
| RCV_PPID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

rcv_tsn
: RCV_TSN '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("rcv_tsn out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| RCV_TSN '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

rcv_cumtsn
: RCV_CUMTSN '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("rcv_cumtsn out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| RCV_CUMTSN '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

rcv_context
: RCV_CONTEXT '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("rcv_context out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| RCV_CONTEXT '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;


sctp_rcvinfo
: '{' rcv_sid ',' rcv_ssn ',' rcv_flags ',' rcv_ppid ',' rcv_tsn ',' rcv_cumtsn ',' rcv_context '}' {
	$$ = new_expression(EXPR_SCTP_RCVINFO);
	$$->value.sctp_rcvinfo = calloc(1, sizeof(struct sctp_rcvinfo_expr));
	$$->value.sctp_rcvinfo->rcv_sid = $2;
	$$->value.sctp_rcvinfo->rcv_ssn = $4;
	$$->value.sctp_rcvinfo->rcv_flags = $6;
	$$->value.sctp_rcvinfo->rcv_ppid = $8;
	$$->value.sctp_rcvinfo->rcv_tsn = $10;
	$$->value.sctp_rcvinfo->rcv_cumtsn = $12;
	$$->value.sctp_rcvinfo->rcv_context = $14;
}
;

pr_policy
: PR_POLICY '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| PR_POLICY '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("pr_policy out of range");
	}
	$$ = new_integer_expression($3, "%hu");
};

sctp_prinfo
: '{' pr_policy ',' PR_VALUE '=' INTEGER'}' {
	$$ = new_expression(EXPR_SCTP_PRINFO);
	$$->value.sctp_prinfo = calloc(1, sizeof(struct sctp_prinfo_expr));
	$$->value.sctp_prinfo->pr_policy = $2;
	if (!is_valid_u32($6)) {
		semantic_error("pr_value out of range");
	}
	$$->value.sctp_prinfo->pr_value = new_integer_expression($6, "%u");
}
;

sctp_authinfo
: '{' AUTH_KEYNUMBER '=' INTEGER '}' {
	$$ = new_expression(EXPR_SCTP_AUTHINFO);
	$$->value.sctp_authinfo = calloc(1, sizeof(struct sctp_authinfo_expr));
	if (!is_valid_u16($4)) {
		semantic_error("auth_keynumber out of range");
	}
	$$->value.sctp_authinfo->auth_keynumber = new_integer_expression($4, "%hu");
}
;

sctp_sendv_spa
: '{' SENDV_FLAGS '=' expression ',' SENDV_SNDINFO '=' expression ',' SENDV_PRINFO '=' expression ',' SENDV_AUTHINFO '=' expression '}' {
	$$ = new_expression(EXPR_SCTP_SENDV_SPA);
	$$->value.sctp_sendv_spa = calloc(1, sizeof(struct sctp_sendv_spa_expr));
	$$->value.sctp_sendv_spa->sendv_flags = $4;
	$$->value.sctp_sendv_spa->sendv_sndinfo = $8;
	$$->value.sctp_sendv_spa->sendv_prinfo = $12;
	$$->value.sctp_sendv_spa->sendv_authinfo = $16;
}
;

nxt_sid
: NXT_SID '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("nxt_sid out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| NXT_SID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

nxt_flags
: NXT_FLAGS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("nxt_flags out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| NXT_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

nxt_ppid
: NXT_PPID '=' _HTONL_ '(' INTEGER ')' {
	if (!is_valid_u32($5)) {
		semantic_error("nxt_ppid out of range");
	}
	$$ = new_integer_expression(htonl((u32)$5), "%u");
}
| NXT_PPID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

nxt_length 
: NXT_LENGTH '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("nxt_length out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| NXT_LENGTH '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sctp_nxtinfo
: '{' nxt_sid ',' nxt_flags ',' nxt_ppid ',' nxt_length '}' {
	$$ = new_expression(EXPR_SCTP_NXTINFO);
	$$->value.sctp_sendv_spa = calloc(1, sizeof(struct sctp_nxtinfo_expr));
	$$->value.sctp_nxtinfo->nxt_sid = $2;
	$$->value.sctp_nxtinfo->nxt_flags = $4;
	$$->value.sctp_nxtinfo->nxt_ppid = $6;
	$$->value.sctp_nxtinfo->nxt_length = $8;
}
;

sctp_recvv_rn
: '{' RECVV_RCVINFO '=' expression ',' RECVV_NXTINFO '=' expression '}' {
	$$ = new_expression(EXPR_SCTP_RECVV_RN);
	$$->value.sctp_sendv_spa = calloc(1, sizeof(struct sctp_recvv_rn_expr));
	$$->value.sctp_recvv_rn->recvv_rcvinfo = $4;
	$$->value.sctp_recvv_rn->recvv_nxtinfo = $8;
}
;

sse_type 
: SSE_TYPE '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sse_type out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SSE_TYPE '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| SSE_TYPE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sse_flags 
: SSE_FLAGS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sse_flags out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SSE_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sse_length 
: SSE_LENGTH '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sse_length out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SSE_LENGTH '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sctp_shutdown_event
: '{' sse_type ',' sse_flags ',' sse_length '}' {
	$$ = new_expression(EXPR_SCTP_SHUTDOWN_EVENT);
	$$->value.sctp_shutdown_event = calloc(1, sizeof(struct sctp_shutdown_event_expr));
	$$->value.sctp_shutdown_event->sse_type = $2;
	$$->value.sctp_shutdown_event->sse_flags = $4;
	$$->value.sctp_shutdown_event->sse_length = $6;
};

pdapi_type
: PDAPI_TYPE '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("pdapi_type out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| PDAPI_TYPE '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| PDAPI_TYPE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;
pdapi_flags
: PDAPI_FLAGS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("pdapi_flags out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| PDAPI_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

pdapi_length
: PDAPI_LENGTH '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("pdapi_length out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| PDAPI_LENGTH '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

pdapi_indication
: PDAPI_INDICATION '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("pdapi_indication out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| PDAPI_INDICATION '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| PDAPI_INDICATION '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

pdapi_stream
: PDAPI_STREAM '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("pdapi_stream out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| PDAPI_STREAM '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

pdapi_seq
: PDAPI_SEQ '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("pdapi_seq out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| PDAPI_SEQ '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

pdapi_assoc_id
: PDAPI_ASSOC_ID '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("pdapi_assoc_id out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| PDAPI_ASSOC_ID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sctp_pdapi_event
: '{' pdapi_type',' pdapi_flags ',' pdapi_length ',' pdapi_indication ',' pdapi_stream ',' pdapi_seq ',' pdapi_assoc_id '}' {
	$$ = new_expression(EXPR_SCTP_PDAPI_EVENT);
	$$->value.sctp_pdapi_event = calloc(1, sizeof(struct sctp_pdapi_event_expr));
	$$->value.sctp_pdapi_event->pdapi_type = $2;
	$$->value.sctp_pdapi_event->pdapi_flags = $4;
	$$->value.sctp_pdapi_event->pdapi_length = $6;
	$$->value.sctp_pdapi_event->pdapi_indication = $8;
	$$->value.sctp_pdapi_event->pdapi_stream = $10;
	$$->value.sctp_pdapi_event->pdapi_seq = $12;
	$$->value.sctp_pdapi_event->pdapi_assoc_id = $14;
}
;

auth_type
: AUTH_TYPE '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("auth_type out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| AUTH_TYPE '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| AUTH_TYPE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

auth_flags
: AUTH_FLAGS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("auth_flags out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| AUTH_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

auth_length
: AUTH_LENGTH '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("auth_length out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| AUTH_LENGTH '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

auth_keynumber
: AUTH_KEYNUMBER '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("auth_keynumber out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| AUTH_KEYNUMBER '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

auth_indication
: AUTH_INDICATION '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("auth_indication out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| AUTH_INDICATION '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| AUTH_INDICATION '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

auth_assoc_id
: AUTH_ASSOC_ID '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("auth_assoc_id out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| AUTH_ASSOC_ID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sctp_authkey_event
: '{' auth_type ',' auth_flags ',' auth_length ',' auth_keynumber ',' auth_indication ',' auth_assoc_id '}' {
	$$ = new_expression(EXPR_SCTP_AUTHKEY_EVENT);
	$$->value.sctp_authkey_event = calloc(1, sizeof(struct sctp_authkey_event_expr));
	$$->value.sctp_authkey_event->auth_type = $2;
	$$->value.sctp_authkey_event->auth_flags = $4;
	$$->value.sctp_authkey_event->auth_length = $6;
	$$->value.sctp_authkey_event->auth_keynumber = $8;
	$$->value.sctp_authkey_event->auth_indication = $10;
	$$->value.sctp_authkey_event->auth_assoc_id = $12;
}
;

sender_dry_type
: SENDER_DRY_TYPE '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sender_dry_type out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SENDER_DRY_TYPE '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| SENDER_DRY_TYPE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sender_dry_flags
: SENDER_DRY_FLAGS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sender_dry_flags out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SENDER_DRY_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sender_dry_length
: SENDER_DRY_LENGTH '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sender_dry_length out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SENDER_DRY_LENGTH '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sender_dry_assoc_id
: SENDER_DRY_ASSOC_ID '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sender_dry_assoc_id out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SENDER_DRY_ASSOC_ID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sctp_sender_dry_event
: '{'sender_dry_type ',' sender_dry_flags ',' sender_dry_length ',' sender_dry_assoc_id '}' {
	$$ = new_expression(EXPR_SCTP_SENDER_DRY_EVENT);
	$$->value.sctp_sender_dry_event = calloc(1, sizeof(struct sctp_sender_dry_event_expr));
	$$->value.sctp_sender_dry_event->sender_dry_type = $2;
	$$->value.sctp_sender_dry_event->sender_dry_flags = $4;
	$$->value.sctp_sender_dry_event->sender_dry_length = $6;
	$$->value.sctp_sender_dry_event->sender_dry_assoc_id = $8;
}
;
ssfe_type
: SSFE_TYPE '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("ssfe_type out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SSFE_TYPE '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| SSFE_TYPE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

ssfe_flags
: SSFE_FLAGS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("ssfe_flags out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SSFE_FLAGS '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| SSFE_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

ssfe_length
: SSFE_LENGTH '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("ssfe_length out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SSFE_LENGTH '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

ssfe_error
: SSFE_ERROR '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("ssfe_error out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SSFE_ERROR '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

ssfe_assoc_id
: SSFE_ASSOC_ID '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("ssfe_assoc_id out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SSFE_ASSOC_ID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

ssfe_data
: SSFE_DATA '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
| SSFE_DATA '=' array    { $$ = $3; }
;

sctp_send_failed_event
: '{' ssfe_type ',' ssfe_flags ',' ssfe_length ',' ssfe_error ',' SSFE_INFO '=' sctp_sndinfo ',' ssfe_assoc_id ',' ssfe_data '}' {
	$$ = new_expression(EXPR_SCTP_SEND_FAILED_EVENT);
	$$->value.sctp_send_failed_event = calloc(1, sizeof(struct sctp_send_failed_event_expr));
	$$->value.sctp_send_failed_event->ssfe_type = $2;
	$$->value.sctp_send_failed_event->ssfe_flags = $4;
	$$->value.sctp_send_failed_event->ssfe_length = $6;
	$$->value.sctp_send_failed_event->ssfe_error = $8;
	$$->value.sctp_send_failed_event->ssfe_info = $12;
	$$->value.sctp_send_failed_event->ssfe_assoc_id = $14;
	$$->value.sctp_send_failed_event->ssfe_data = $16;
};

sac_type
: SAC_TYPE '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sac_type out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SAC_TYPE '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| SAC_TYPE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sac_flags
: SAC_FLAGS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sac_flags out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SAC_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sac_length
: SAC_LENGTH '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sac_length out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SAC_LENGTH '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sac_state
: SAC_STATE '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sac_state out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SAC_STATE '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| SAC_STATE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sac_error
: SAC_ERROR '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sac_error out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SAC_ERROR '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sac_outbound_streams
: SAC_OUTBOUND_STREAMS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sac_outbound_streams out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SAC_OUTBOUND_STREAMS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sac_inbound_streams
: SAC_INBOUND_STREAMS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sac_inbound_streams out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SAC_INBOUND_STREAMS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sac_assoc_id
: SAC_ASSOC_ID '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sac_assoc_id out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SAC_ASSOC_ID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sac_info
: SAC_INFO '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
| SAC_INFO '=' array { $$ = $3; }
;

sctp_assoc_change
: '{' sac_type ',' sac_flags ',' sac_length ',' sac_state ',' sac_error ',' sac_outbound_streams ','
sac_inbound_streams ',' sac_assoc_id ',' sac_info '}' {
	$$ = new_expression(EXPR_SCTP_ASSOC_CHANGE);
	$$->value.sctp_assoc_change = calloc(1, sizeof(struct sctp_assoc_change_expr));
	$$->value.sctp_assoc_change->sac_type = $2;
	$$->value.sctp_assoc_change->sac_flags = $4;
	$$->value.sctp_assoc_change->sac_length = $6;
	$$->value.sctp_assoc_change->sac_state = $8;
	$$->value.sctp_assoc_change->sac_error = $10;
	$$->value.sctp_assoc_change->sac_outbound_streams = $12;
	$$->value.sctp_assoc_change->sac_inbound_streams = $14;
	$$->value.sctp_assoc_change->sac_assoc_id = $16;
	$$->value.sctp_assoc_change->sac_info = $18;
}
;

sre_type
: SRE_TYPE '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sre_type out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SRE_TYPE '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| SRE_TYPE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sre_flags
: SRE_FLAGS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sre_flags out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SRE_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sre_length
: SRE_LENGTH '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sre_length out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SRE_LENGTH '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sre_error
: SRE_ERROR '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sre_error out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SRE_ERROR '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sre_assoc_id
: SRE_ASSOC_ID '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sre_assoc_id out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SRE_ASSOC_ID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sre_data
: SRE_DATA '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
| SRE_DATA '=' array { $$ = $3; }
;

sctp_remote_error
: '{' sre_type ',' sre_flags ',' sre_length ',' sre_error ',' sre_assoc_id ',' sre_data '}' {
	$$ = new_expression(EXPR_SCTP_REMOTE_ERROR);
	$$->value.sctp_remote_error = calloc(1, sizeof(struct sctp_remote_error_expr));
	$$->value.sctp_remote_error->sre_type = $2;
	$$->value.sctp_remote_error->sre_flags = $4;
	$$->value.sctp_remote_error->sre_length = $6;
	$$->value.sctp_remote_error->sre_error = $8;
	$$->value.sctp_remote_error->sre_assoc_id = $10;
	$$->value.sctp_remote_error->sre_data = $12;
}
;

spc_type
: SPC_TYPE '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("spc_type out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SPC_TYPE '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| SPC_TYPE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

spc_flags
: SPC_FLAGS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("spc_flags out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SPC_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

spc_length
: SPC_LENGTH '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("spc_length out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SPC_LENGTH '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

spc_aaddr
: SPC_AADDR '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
| SPC_AADDR '=' sockaddr { $$ = $3; }
;

spc_state
: SPC_STATE '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("spc_state out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SPC_STATE '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| SPC_STATE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

spc_error
: SPC_ERROR '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("spc_error out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SPC_ERROR '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| SPC_ERROR '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

spc_assoc_id
: SPC_ASSOC_ID '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("spc_assoc_id out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SPC_ASSOC_ID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sctp_paddr_change
: '{' spc_type ',' spc_flags ',' spc_length ',' spc_aaddr ',' spc_state ',' spc_error ',' spc_assoc_id '}' {
	$$ = new_expression(EXPR_SCTP_PADDR_CHANGE);
	$$->value.sctp_paddr_change = calloc(1, sizeof(struct sctp_paddr_change_expr));
	$$->value.sctp_paddr_change->spc_type = $2;
	$$->value.sctp_paddr_change->spc_flags = $4;
	$$->value.sctp_paddr_change->spc_length = $6;
	$$->value.sctp_paddr_change->spc_aaddr = $8;
	$$->value.sctp_paddr_change->spc_state = $10;
	$$->value.sctp_paddr_change->spc_error = $12;
	$$->value.sctp_paddr_change->spc_assoc_id = $14;
}
;
ssf_type
: SSF_TYPE '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("ssf_type out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SSF_TYPE '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| SSF_TYPE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

ssf_length
: SSF_LENGTH '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("ssf_length out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SSF_LENGTH '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

ssf_flags
: SSF_FLAGS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("ssf_flags out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SSF_FLAGS '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| SSF_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

ssf_error
: SSF_ERROR '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("ssf_error out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SSF_ERROR '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| SSF_ERROR '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

ssf_info
: SSF_INFO '=' sctp_sndrcvinfo { $$ = $3; }
| SSF_INFO '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

ssf_assoc_id
: SSF_ASSOC_ID '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("ssf_assoc_id out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SSF_ASSOC_ID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

ssf_data
: SSF_DATA '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
| SSF_DATA '=' array { $$ = $3; }
;

sctp_send_failed
: '{' ssf_type ',' ssf_flags ',' ssf_length ',' ssf_error ',' ssf_info ',' ssf_assoc_id ',' ssf_data '}' {
	$$ = new_expression(EXPR_SCTP_SEND_FAILED);
	$$->value.sctp_send_failed = calloc(1, sizeof(struct sctp_send_failed_expr));
	$$->value.sctp_send_failed->ssf_type = $2;
	$$->value.sctp_send_failed->ssf_flags = $4;
	$$->value.sctp_send_failed->ssf_length = $6;
	$$->value.sctp_send_failed->ssf_error = $8;
	$$->value.sctp_send_failed->ssf_info = $10;
	$$->value.sctp_send_failed->ssf_assoc_id = $12;
	$$->value.sctp_send_failed->ssf_data = $14;
}
;

sai_type
: SAI_TYPE '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sai_type out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SAI_TYPE '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| SAI_TYPE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sai_flags
: SAI_FLAGS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sai_flags out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SAI_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sai_length
: SAI_LENGTH '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sai_length out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SAI_LENGTH '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sai_adaptation_ind
: SAI_ADAPTATION_IND '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sai_adaptation_ind out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SAI_ADAPTATION_IND '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sai_assoc_id
: SAI_ASSOC_ID '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sai_assoc_id out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SAI_ASSOC_ID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sctp_adaptation_event
: '{' sai_type ',' sai_flags ',' sai_length ',' sai_adaptation_ind ',' sai_assoc_id '}' {
	$$ = new_expression(EXPR_SCTP_ADAPTATION_EVENT);
	$$->value.sctp_adaptation_event = calloc(1, sizeof(struct sctp_adaptation_event_expr));
	$$->value.sctp_adaptation_event->sai_type = $2;
	$$->value.sctp_adaptation_event->sai_flags = $4;
	$$->value.sctp_adaptation_event->sai_length = $6;
	$$->value.sctp_adaptation_event->sai_adaptation_ind = $8;
	$$->value.sctp_adaptation_event->sai_assoc_id = $10;
}
;

sn_type
: SN_TYPE '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sn_type out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SN_TYPE '=' WORD {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $3;
}
| SN_TYPE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sn_flags
: SN_FLAGS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sn_flags out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SN_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sn_length
: SN_LENGTH '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sn_length out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SN_LENGTH '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sctp_tlv
: '{' sn_type ',' sn_flags ',' sn_length '}' {
	$$ = new_expression(EXPR_SCTP_TLV);
	$$->value.sctp_tlv = calloc(1, sizeof(struct sctp_tlv_expr));
	$$->value.sctp_tlv->sn_type = $2;
	$$->value.sctp_tlv->sn_flags = $4;
	$$->value.sctp_tlv->sn_length = $6;
}
;

opt_errno
:                   { $$ = NULL; }
| WORD note         {
	$$ = malloc(sizeof(struct errno_spec));
	$$->errno_macro = $1;
	$$->strerror    = $2;
}
;

opt_note
:                   { $$ = NULL; }
| note              { $$ = $1; }
;

note
: '(' word_list ')' { $$ = $2; }
;

word_list
: WORD              { $$ = $1; }
| word_list WORD    { asprintf(&($$), "%s %s", $1, $2); free($1); free($2); }
;

command_spec
: BACK_QUOTED       {
	$$ = malloc(sizeof(struct command_spec));
	$$->command_line = $1;
	current_script_line = yylineno;
}
;

code_spec
: CODE              {
	$$ = calloc(1, sizeof(struct code_spec));
	$$->text = $1;
	current_script_line = yylineno;
}
;

null
: NULL_ {
	$$ = new_expression(EXPR_NULL);
}
;