%{
/*
 * 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.
 *
 */

/* 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 "inet/common/INETDefs.h"

#if !defined(_WIN32) && !defined(__WIN32__) && !defined(WIN32) && !defined(__CYGWIN__) && !defined(_WIN64)
#include <arpa/inet.h>
#include <netinet/in.h>
#else
#include "winsock2.h"
#endif
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include "PacketDrillUtils.h"
#include "PacketDrill.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 int yywrap(void);
extern char *yytext;
extern int yylex(void);
extern int yyparse(void);

/* 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 use this object to look up configuration info needed during
 * parsing.
 */
static PacketDrillConfig *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 PacketDrillScript *out_script = NULL;


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

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


/* 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(PacketDrillConfig *config, PacketDrillScript *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 YYDEBUG
    yydebug = 1;
#endif

    /* Now parse the script from our buffer. */
    yyin = fopen(script->getScriptPath(), "r");
    if (!yyin)
        printf("fopen: parse error opening script buffer");
    current_script_path = config->getScriptPath();
    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))
        printf("fclose: error closing script buffer");

    /* Unlock parser. */

    return result ? -1 : 0;
}

void parse_and_finalize_config(struct invocation *invocation)
{
    invocation->config->parseScriptOptions(invocation->script->getOptionList());
}

/* 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);
}

static void semantic_error(const char* message)
{
    printf("%s\n", message);
    throw cTerminationException("Packetdrill error: Script error");
}

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


/* Create and initialize a new option. */
/*static struct option_list *new_option(char *name, char *value)
{
    return NULL;
}*/

%}

%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 {
    int64 integer;
    double floating;
    char *string;
    char *reserved;
    int64 time_usecs;
    enum direction_t direction;
    uint16 port;
    int32 window;
    uint32 sequence_number;
    struct {
        int protocol;    /* IPPROTO_TCP or IPPROTO_UDP */
        uint32 start_sequence;
        uint16 payload_bytes;
    } tcp_sequence_info;
    PacketDrillEvent *event;
    PacketDrillPacket *packet;
    struct syscall_spec *syscall;
    struct command_spec *command;
    PacketDrillStruct *sack_block;
    PacketDrillStruct *cause_item;
    PacketDrillExpression *expression;
    cQueue *expression_list;
    PacketDrillTcpOption *tcp_option;
    PacketDrillSctpParameter *sctp_parameter;
    PacketDrillOption *option;
    cQueue *tcp_options;
    struct errno_spec *errno_info;
    cQueue *sctp_chunk_list;
    cQueue *sctp_parameter_list;
    cQueue *address_types_list;
    cQueue *sack_block_list;
    cQueue *stream_list;
    cQueue *cause_list;
    PacketDrillBytes *byte_list;
    uint8 byte;
    PacketDrillSctpChunk *sctp_chunk;
}

/* 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> UDP _HTONS_ _HTONL_ BACK_QUOTED SA_FAMILY SIN_PORT SIN_ADDR
%token <reserved> ACK WIN WSCALE MSS NOP TIMESTAMP ECR EOL TCPSACK VAL SACKOK
%token <reserved> OPTION IPV4_TYPE IPV6_TYPE INET_ADDR
%token <reserved> SPP_ASSOC_ID SPP_ADDRESS SPP_HBINTERVAL SPP_PATHMAXRXT SPP_PATHMTU
%token <reserved> SPP_FLAGS SPP_IPV6_FLOWLABEL_ SPP_DSCP_
%token <reserved> SINFO_STREAM SINFO_SSN SINFO_FLAGS SINFO_PPID SINFO_CONTEXT SINFO_ASSOC_ID
%token <reserved> SINFO_TIMETOLIVE SINFO_TSN SINFO_CUMTSN SINFO_PR_VALUE
%token <reserved> CHUNK MYDATA MYINIT MYINIT_ACK MYHEARTBEAT MYHEARTBEAT_ACK MYABORT
%token <reserved> MYSHUTDOWN MYSHUTDOWN_ACK MYERROR MYCOOKIE_ECHO MYCOOKIE_ACK
%token <reserved> MYSHUTDOWN_COMPLETE PAD ERROR
%token <reserved> HEARTBEAT_INFORMATION CAUSE_INFO MYSACK STATE_COOKIE PARAMETER MYSCTP
%token <reserved> TYPE FLAGS LEN MYSUPPORTED_EXTENSIONS MYSUPPORTED_ADDRESS_TYPES
%token <reserved> TYPES CWR ECNE
%token <reserved> TAG A_RWND OS IS TSN MYSID SSN PPID CUM_TSN GAPS DUPS MID FSN
%token <reserved> SRTO_ASSOC_ID SRTO_INITIAL SRTO_MAX SRTO_MIN
%token <reserved> SINIT_NUM_OSTREAMS SINIT_MAX_INSTREAMS SINIT_MAX_ATTEMPTS
%token <reserved> SINIT_MAX_INIT_TIMEO MYSACK_DELAY SACK_FREQ
%token <reserved> ASSOC_VALUE ASSOC_ID SACK_ASSOC_ID RECONFIG
%token <reserved> OUTGOING_SSN_RESET REQ_SN RESP_SN LAST_TSN SIDS INCOMING_SSN_RESET
%token <reserved> RECONFIG_RESPONSE RESULT SENDER_NEXT_TSN RECEIVER_NEXT_TSN
%token <reserved> SSN_TSN_RESET ADD_INCOMING_STREAMS NUMBER_OF_NEW_STREAMS
%token <reserved> ADD_OUTGOING_STREAMS RECONFIG_REQUEST_GENERIC
%token <reserved> SRS_ASSOC_ID SRS_FLAGS SRS_NUMBER_STREAMS SRS_STREAM_LIST
%token <reserved> SSTAT_ASSOC_ID SSTAT_STATE SSTAT_RWND SSTAT_UNACKDATA SSTAT_PENDDATA
%token <reserved> SSTAT_INSTRMS SSTAT_OUTSTRMS SSTAT_FRAGMENTATION_POINT
%token <reserved> SSTAT_PRIMARY
%token <reserved> SASOC_ASOCMAXRXT SASOC_ASSOC_ID SASOC_NUMBER_PEER_DESTINATIONS SASOC_PEER_RWND
%token <reserved> SASOC_LOCAL_RWND SASOC_COOKIE_LIFE
%token <reserved> SAS_ASSOC_ID SAS_INSTRMS SAS_OUTSTRMS
%token <reserved> MYINVALID_STREAM_IDENTIFIER ISID
%token <floating> MYFLOAT
%token <integer> INTEGER HEX_INTEGER
%token <string> MYWORD MYSTRING
%type <direction> direction
%type <event> event events event_time action
%type <option> option options opt_options
%type <time_usecs> time opt_end_time
%type <packet> packet_spec tcp_packet_spec udp_packet_spec sctp_packet_spec
%type <packet> packet_prefix
%type <syscall> syscall_spec
%type <command> command_spec
%type <string> option_flag option_value flags
%type <tcp_sequence_info> seq
%type <tcp_options> opt_tcp_options tcp_option_list
%type <tcp_option> tcp_option
%type <string> opt_note note word_list
%type <sack_block> sack_block gap dup
%type <window> opt_window
%type <sequence_number> opt_ack
%type <string> script
%type <string> function_name
%type <sack_block_list> sack_block_list opt_gaps gap_list dup_list opt_dups
%type <expression_list> expression_list function_arguments
%type <expression_list> opt_parameter_list sctp_parameter_list
%type <expression> expression binary_expression array sockaddr
%type <expression> sctp_paddrparams spp_address spp_hbinterval spp_pathmtu spp_pathmaxrxt
%type <expression> spp_flags spp_ipv6_flowlabel spp_dscp
%type <expression> decimal_integer hex_integer sctp_assoc_id stream address_type
%type <expression> sctp_rtoinfo srto_initial srto_max srto_min
%type <expression> sctp_initmsg sinit_num_ostreams sinit_max_instreams sinit_max_attempts
%type <expression> sinit_max_init_timeo sctp_assoc_value sctp_sackinfo sack_delay sack_freq
%type <expression> sctp_sndrcvinfo sinfo_stream sinfo_ssn sinfo_flags sinfo_ppid sinfo_context
%type <expression> sinfo_timetolive sinfo_tsn sinfo_cumtsn
%type <expression> sasoc_asocmaxrxt sasoc_number_peer_destinations sasoc_peer_rwnd
%type <expression> sasoc_local_rwnd sasoc_cookie_life sctp_assocparams
%type <expression> sctp_reset_streams srs_flags
%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_add_streams
%type <errno_info> opt_errno
%type <integer> opt_flags opt_len opt_data_flags opt_abort_flags chunk_type
%type <integer> opt_shutdown_complete_flags opt_tag opt_a_rwnd opt_os opt_is
%type <integer> opt_tsn opt_sid opt_ssn opt_ppid opt_cum_tsn
%type <integer> opt_req_sn opt_resp_sn opt_last_tsn opt_result
%type <integer> opt_number_of_new_streams
%type <integer> opt_sender_next_tsn opt_receiver_next_tsn
%type <sctp_chunk_list> sctp_chunk_list
%type <sctp_chunk> sctp_chunk
%type <sctp_chunk> sctp_data_chunk_spec sctp_abort_chunk_spec sctp_reconfig_chunk_spec
%type <sctp_chunk> sctp_init_chunk_spec sctp_init_ack_chunk_spec
%type <sctp_chunk> sctp_sack_chunk_spec sctp_heartbeat_chunk_spec sctp_heartbeat_ack_chunk_spec
%type <sctp_chunk> sctp_shutdown_chunk_spec sctp_shutdown_ack_chunk_spec
%type <sctp_chunk> sctp_cookie_echo_chunk_spec sctp_cookie_ack_chunk_spec
%type <sctp_chunk> sctp_shutdown_complete_chunk_spec sctp_error_chunk_spec
%type <sctp_parameter> sctp_parameter sctp_heartbeat_information_parameter
%type <sctp_parameter> sctp_state_cookie_parameter sctp_supported_extensions_parameter
%type <sctp_parameter> outgoing_ssn_reset_request incoming_ssn_reset_request
%type <sctp_parameter> reconfig_response ssn_tsn_reset_request
%type <sctp_parameter> sctp_supported_address_types_parameter
%type <sctp_parameter> add_outgoing_streams_request add_incoming_streams_request
%type <byte_list> opt_val byte_list chunk_types_list
%type <byte> byte
%type <stream_list> stream_list address_types_list;
%type <cause_list> opt_cause_list sctp_cause_list
%type <cause_item> sctp_invalid_stream_identifier_cause_spec sctp_cause_spec

%%  /* The grammar follows. */

script
: opt_options 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->addOption($1);
    $$ = $1;    /* return the tail so we can append to it */
}
| options option {
    out_script->addOption($2);
    $$ = $2;    /* return the tail so we can append to it */
}
;

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

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

option_value
: INTEGER   { $$ = strdup(yytext); }
| MYWORD    { $$ = $1; }
| MYSTRING  { $$ = $1; }
;


events
: event {
    out_script->addEvent($1);    /* save pointer to event list as output of parser */
    $$ = $1;    /* return the tail so that we can append to it */
}
| events event {
    out_script->addEvent($2);
    $$ = $2;    /* return the tail so that we can append to it */
}
;

event
: event_time action {
    $$ = $2;
    $$->setLineNumber($1->getLineNumber());    /* use timestamp's line */
    $$->setEventTime($1->getEventTime());
    $$->setEventTimeEnd($1->getEventTimeEnd());
    $$->setTimeType($1->getTimeType());
    $1->getLineNumber(),
    $1->getEventTime().dbl(),
    $1->getEventTimeEnd().dbl(),
    $1->getTimeType();
    if ($$->getEventTimeEnd() != NO_TIME_RANGE) {
        if ($$->getEventTimeEnd() < $$->getEventTime())
            semantic_error("time range is backwards");
    }
    if ($$->getTimeType() == ANY_TIME &&  ($$->getType() != PACKET_EVENT ||
        ($$->getPacket())->getDirection() != DIRECTION_OUTBOUND)) {
        yylineno = $$->getLineNumber();
        semantic_error("event time <star> can only be used with outbound packets");
    } else if (($$->getTimeType() == ABSOLUTE_RANGE_TIME ||
        $$->getTimeType() == RELATIVE_RANGE_TIME) &&
        ($$->getType() != PACKET_EVENT ||
        ($$->getPacket())->getDirection() != DIRECTION_OUTBOUND)) {
        yylineno = $$->getLineNumber();
        semantic_error("event time range can only be used with outbound packets");
    }
    delete($1);
}
;

event_time
: '+' time {
    $$ = new PacketDrillEvent(INVALID_EVENT);
    $$->setLineNumber(@2.first_line);
    $$->setEventTime($2);
    $$->setTimeType(RELATIVE_TIME);
}
| time {
    $$ = new PacketDrillEvent(INVALID_EVENT);
    $$->setLineNumber(@1.first_line);
    $$->setEventTime($1);
    $$->setTimeType(ABSOLUTE_TIME);
}
| '*' {
    $$ = new PacketDrillEvent(INVALID_EVENT);
    $$->setLineNumber(@1.first_line);
    $$->setTimeType(ANY_TIME);
}
| time '~' time {
    $$ = new PacketDrillEvent(INVALID_EVENT);
    $$->setLineNumber(@1.first_line);
    $$->setTimeType(ABSOLUTE_RANGE_TIME);
    $$->setEventTime($1);
    $$->setEventTimeEnd($3);
}
| '+' time '~' '+' time {
    $$ = new PacketDrillEvent(INVALID_EVENT);
    $$->setLineNumber(@1.first_line);
    $$->setTimeType(RELATIVE_RANGE_TIME);
    $$->setEventTime($2);
    $$->setEventTimeEnd($5);
}
;

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

action
: packet_spec {
    if ($1) {
        $$ = new PacketDrillEvent(PACKET_EVENT);  $$->setPacket($1);
    } else {
        $$ = NULL;
    }
}
| syscall_spec {
    $$ = new PacketDrillEvent(SYSCALL_EVENT);
    $$->setSyscall($1);
}
| command_spec {
    $$ = new PacketDrillEvent(COMMAND_EVENT);
    $$->setCommand($1);
}
;

command_spec
: BACK_QUOTED       {
    $$ = (struct command_spec *)calloc(1, sizeof(struct command_spec));
    $$->command_line = $1;
}
;

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

tcp_packet_spec
: packet_prefix flags seq opt_ack opt_window opt_tcp_options {
    char *error = NULL;
    PacketDrillPacket *outer = $1, *inner = NULL;
    enum direction_t direction = outer->getDirection();

    if (($6 == NULL) && (direction != DIRECTION_OUTBOUND)) {
        yylineno = @6.first_line;
        printf("<...> for TCP options can only be used with outbound packets");
    }
    cPacket* pkt = PacketDrill::buildTCPPacket(in_config->getWireProtocol(), direction,
                                               $2,
                                               $3.start_sequence, $3.payload_bytes,
                                               $4, $5, $6, &error);

    free($2);

    inner = new PacketDrillPacket();
    inner->setInetPacket(pkt);

    inner->setDirection(direction);

    $$ = inner;
}
;

udp_packet_spec
: packet_prefix UDP '(' INTEGER ')' {
    char *error = NULL;
    PacketDrillPacket *outer = $1, *inner = NULL;

    enum direction_t direction = outer->getDirection();
    cPacket* pkt = PacketDrill::buildUDPPacket(in_config->getWireProtocol(), direction, $4, &error);
    if (direction == DIRECTION_INBOUND)
        pkt->setName("parserInbound");
    else
        pkt->setName("parserOutbound");
    inner = new PacketDrillPacket();
    inner->setInetPacket(pkt);
    inner->setDirection(direction);

    $$ = inner;
}
;

sctp_packet_spec
: packet_prefix MYSCTP ':' sctp_chunk_list {
    PacketDrillPacket *inner = NULL;
    enum direction_t direction = $1->getDirection();
    cPacket* pkt = PacketDrill::buildSCTPPacket(in_config->getWireProtocol(), direction, $4);
    if (pkt) {
        if (direction == DIRECTION_INBOUND)
            pkt->setName("parserInbound");
        else
            pkt->setName("parserOutbound");
        inner = new PacketDrillPacket();
        inner->setInetPacket(pkt);
        inner->setDirection(direction);
    } else {
        semantic_error("inbound packets must be fully specified");
    }
    $$ = inner;
}
;

sctp_chunk_list
: sctp_chunk                     { $$ = new cQueue("sctpChunkList");
                                   $$->insert((cObject*)$1); }
| sctp_chunk_list ';' sctp_chunk { $$ = $1;
                                   $$->insert($3); }
;


sctp_chunk
: 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_cookie_echo_chunk_spec       { $$ = $1; }
| sctp_cookie_ack_chunk_spec        { $$ = $1; }
| sctp_shutdown_complete_chunk_spec { $$ = $1; }
| sctp_reconfig_chunk_spec          { $$ = $1; }
| sctp_error_chunk_spec             { $$ = $1; }
;


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

byte_list
: byte               { $$ = new PacketDrillBytes($1); }
| byte_list ',' byte { $$ = $1;
                       $1->appendByte($3); }
;

chunk_types_list
: { $$ = new PacketDrillBytes();}
| chunk_type         { $$ = new PacketDrillBytes($1);}
| chunk_types_list ',' chunk_type { $$ = $1;
                       $1->appendByte($3); }
;

byte
: HEX_INTEGER {
    if (!is_valid_u8($1)) {
        semantic_error("byte value out of range");
    }
    $$ = $1;
}
| INTEGER {
    if (!is_valid_u8($1)) {
        semantic_error("byte value out of range");
    }
    $$ = $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;
}
| MYDATA {
    $$ = SCTP_DATA_CHUNK_TYPE;
}
| MYINIT {
    $$ = SCTP_INIT_CHUNK_TYPE;
}
| MYINIT_ACK {
    $$ = SCTP_INIT_ACK_CHUNK_TYPE;
}
| MYSACK {
    $$ = SCTP_SACK_CHUNK_TYPE;
}
| MYHEARTBEAT {
    $$ = SCTP_HEARTBEAT_CHUNK_TYPE;
}
| MYHEARTBEAT_ACK {
    $$ = SCTP_HEARTBEAT_ACK_CHUNK_TYPE;
}
| MYABORT {
    $$ = SCTP_ABORT_CHUNK_TYPE;
}
| MYSHUTDOWN {
    $$ = SCTP_SHUTDOWN_CHUNK_TYPE;
}
| MYSHUTDOWN_ACK {
    $$ = SCTP_SHUTDOWN_ACK_CHUNK_TYPE;
}
| MYERROR {
    $$ = SCTP_ERROR_CHUNK_TYPE;
}
| MYCOOKIE_ECHO {
    $$ = SCTP_COOKIE_ECHO_CHUNK_TYPE;
}
| MYCOOKIE_ACK {
    $$ = SCTP_COOKIE_ACK_CHUNK_TYPE;
}
| MYSHUTDOWN_COMPLETE{
    $$ = SCTP_SHUTDOWN_COMPLETE_CHUNK_TYPE;
}
| PAD {
    $$ = SCTP_PAD_CHUNK_TYPE;
}
| RECONFIG {
    $$ = SCTP_RECONFIG_CHUNK_TYPE;
}
;

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 '=' MYWORD        {
    uint64 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");
        }
    }
    $$ = 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 '=' MYWORD        {
    uint64 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");
        }
    }
    $$ = 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 '=' MYWORD        {
    uint64 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");
        }
    }
    $$ = 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
: MYSID '=' ELLIPSIS { $$ = -1; }
| MYSID '=' 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_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_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; }
;


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


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

sctp_init_chunk_spec
: MYINIT '[' opt_flags ',' opt_tag ',' opt_a_rwnd ',' opt_os ',' opt_is ',' opt_tsn opt_parameter_list ']' {
    $$ = PacketDrill::buildInitChunk($3, $5, $7, $9, $11, $13, $14);
}

sctp_init_ack_chunk_spec
: MYINIT_ACK '[' opt_flags ',' opt_tag ',' opt_a_rwnd ',' opt_os ',' opt_is ',' opt_tsn opt_parameter_list ']' {
    $$ = PacketDrill::buildInitAckChunk($3, $5, $7, $9, $11, $13, $14);
}

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

sctp_heartbeat_chunk_spec
: MYHEARTBEAT '[' opt_flags ',' sctp_heartbeat_information_parameter ']' {
    $$ = PacketDrill::buildHeartbeatChunk($3, $5);
}


sctp_heartbeat_ack_chunk_spec
: MYHEARTBEAT_ACK '[' opt_flags ',' sctp_heartbeat_information_parameter ']' {
    $$ = PacketDrill::buildHeartbeatAckChunk($3, $5);
}


sctp_abort_chunk_spec
: MYABORT '[' opt_abort_flags ']' {
    $$ = PacketDrill::buildAbortChunk($3);
}

sctp_shutdown_chunk_spec
: MYSHUTDOWN '[' opt_flags ',' opt_cum_tsn ']' {
    $$ = PacketDrill::buildShutdownChunk($3, $5);
}

sctp_shutdown_ack_chunk_spec
: MYSHUTDOWN_ACK '[' opt_flags ']' {
    $$ = PacketDrill::buildShutdownAckChunk($3);
}

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

sctp_cookie_ack_chunk_spec
: MYCOOKIE_ACK '[' opt_flags ']' {
    $$ = PacketDrill::buildCookieAckChunk($3);
}

opt_cause_list
: ',' ELLIPSIS             { $$ = NULL; }
|                          { $$ = new cQueue("empty"); }
| ',' sctp_cause_list { $$ = $2; }
;

sctp_cause_list
: sctp_cause_spec                          { $$ = new cQueue("cause list");
                                             $$->insert($1); }
| sctp_cause_list ',' sctp_cause_spec      { $$ = $1;
                                             $$->insert($3); }
;

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

sctp_cause_spec
: sctp_invalid_stream_identifier_cause_spec      { $$ = $1; }
;

sctp_error_chunk_spec
: MYERROR '[' opt_flags opt_cause_list ']' {
    $$ = PacketDrill::buildErrorChunk($3, $4);
}

sctp_shutdown_complete_chunk_spec
: MYSHUTDOWN_COMPLETE '[' opt_shutdown_complete_flags ']' {
    $$ = PacketDrill::buildShutdownCompleteChunk($3);
}


opt_req_sn
: REQ_SN '=' INTEGER {
    if (!is_valid_u32($3)) {
        semantic_error("req_sn out of range");
    }
    $$ = $3;
}
| REQ_SN '=' ELLIPSIS { $$ = -1; }
;

opt_resp_sn
: RESP_SN '=' INTEGER {
    if (!is_valid_u32($3)) {
        semantic_error("resp_sn out of range");
    }
    $$ = $3;
}
| RESP_SN '=' ELLIPSIS { $$ = -1; }
;

opt_last_tsn
: LAST_TSN '=' INTEGER {
    if (!is_valid_u32($3)) {
    semantic_error("last_tsn out of range");
    }
    $$ = $3;
}
| LAST_TSN '=' ELLIPSIS { $$ = -1; }
;

opt_result
: RESULT '=' INTEGER {
    if (!is_valid_u32($3)) {
    semantic_error("result out of range");
    }
    $$ = $3;
}
| RESULT '=' ELLIPSIS { $$ = -1; }
;

opt_sender_next_tsn
: SENDER_NEXT_TSN '=' INTEGER {
    if (!is_valid_u32($3)) {
    semantic_error("sender_next_tsn out of range");
    }
    $$ = $3;
}
| SENDER_NEXT_TSN '=' HEX_INTEGER {
    if (!is_valid_u32($3)) {
    semantic_error("sender_next_tsn out of range");
    }
    $$ = $3;
}
| SENDER_NEXT_TSN '=' ELLIPSIS { $$ = -1; }
;

opt_receiver_next_tsn
: RECEIVER_NEXT_TSN '=' INTEGER {
    if (!is_valid_u32($3)) {
    semantic_error("receiver_next_tsn out of range");
    }
    $$ = $3;
}
| RECEIVER_NEXT_TSN '=' HEX_INTEGER {
    if (!is_valid_u32($3)) {
        semantic_error("receiver_next_tsn out of range");
    }
    $$ = $3;
}
| RECEIVER_NEXT_TSN '=' ELLIPSIS { $$ = -1; }
;

opt_number_of_new_streams
: NUMBER_OF_NEW_STREAMS '=' INTEGER {
    if (!is_valid_u16($3)) {
        semantic_error("number_of_new_streams out of range");
    }
    $$ = $3;
}
| NUMBER_OF_NEW_STREAMS '=' ELLIPSIS { $$ = -1; }
;

stream_list
: stream {
    $$ = new cQueue("stream_list");
    $$->insert($1);
}
| stream_list ',' stream {
    $$ = $1; $$->insert($3);
}
;

stream
: {
    $$ = new_integer_expression(-1, "%d");
}
| INTEGER {
    if (!is_valid_u16($1)) {
        semantic_error("Stream number value out of range");
    }
    $$ = new_integer_expression($1, "%u");
}
;


outgoing_ssn_reset_request
: OUTGOING_SSN_RESET '[' opt_req_sn ',' opt_resp_sn ',' opt_last_tsn ']' {
    $$ = new PacketDrillSctpParameter(OUTGOING_RESET_REQUEST_PARAMETER, 16, new PacketDrillStruct($3, $5, $7, -2, NULL));
}
| OUTGOING_SSN_RESET '[' opt_req_sn ',' opt_resp_sn ',' opt_last_tsn ',' SIDS '=' '[' stream_list ']' ']' {
    $$ = new PacketDrillSctpParameter(OUTGOING_RESET_REQUEST_PARAMETER, 16, new PacketDrillStruct($3, $5, $7, -2, $12));
}
;

incoming_ssn_reset_request
: INCOMING_SSN_RESET '[' opt_req_sn ']' {
    $$ = new PacketDrillSctpParameter(INCOMING_RESET_REQUEST_PARAMETER, 8, new PacketDrillStruct($3, -2, -2, -2, NULL));
}
| INCOMING_SSN_RESET '[' opt_req_sn ',' SIDS '=' '[' stream_list ']' ']' {
    $$ = new PacketDrillSctpParameter(INCOMING_RESET_REQUEST_PARAMETER, 8, new PacketDrillStruct($3, -2, -2, -2, $8));
}
;

ssn_tsn_reset_request
: SSN_TSN_RESET '[' opt_req_sn ']' {
    $$ = new PacketDrillSctpParameter(SSN_TSN_RESET_REQUEST_PARAMETER, 8, new PacketDrillStruct($3, -2, -2, -2, NULL));
}
;

reconfig_response
: RECONFIG_RESPONSE '[' opt_resp_sn ',' opt_result ']' {
    $$ = new PacketDrillSctpParameter(STREAM_RESET_RESPONSE_PARAMETER, 8, new PacketDrillStruct($3, $5, -2, -2, NULL));
}
| RECONFIG_RESPONSE '[' opt_resp_sn ',' opt_result ',' opt_sender_next_tsn ',' opt_receiver_next_tsn']' {
    $$ = new PacketDrillSctpParameter(STREAM_RESET_RESPONSE_PARAMETER, 12, new PacketDrillStruct($3, $5, $7, $9, NULL));
}
;

add_outgoing_streams_request
: ADD_OUTGOING_STREAMS '[' opt_req_sn ',' opt_number_of_new_streams ']' {
    $$ = new PacketDrillSctpParameter(ADD_OUTGOING_STREAMS_REQUEST_PARAMETER, 12, new PacketDrillStruct($3, $5, -2, -2, NULL));
}
;

add_incoming_streams_request
: ADD_INCOMING_STREAMS '[' opt_req_sn ',' opt_number_of_new_streams ']' {
    $$ = new PacketDrillSctpParameter(ADD_INCOMING_STREAMS_REQUEST_PARAMETER, 12, new PacketDrillStruct($3, $5, -2, -2, NULL));
}
;

//generic_reconfig_request
//: RECONFIG_REQUEST_GENERIC '[' opt_parameter_type ',' opt_len ',' opt_req_sn ',' opt_val ']' {
//    $$ = sctp_generic_reconfig_request_parameter_new($3, $5, $7, $9);
//}
//;

sctp_reconfig_chunk_spec
: RECONFIG '[' opt_flags  opt_parameter_list ']' {
    $$ = PacketDrill::buildReconfigChunk($3, $4);
}
;

opt_parameter_list
: ',' ELLIPSIS                 { $$ = NULL; }
|                              { $$ = new cQueue("empty"); }
| ',' sctp_parameter_list { $$ = $2; }
;

sctp_parameter_list
: sctp_parameter {
    $$ = new cQueue("sctp_parameter_list");
    $$->insert($1);
}
| sctp_parameter_list ',' sctp_parameter {
    $$ = $1;
    $$->insert($3);
}
;


sctp_parameter
: sctp_heartbeat_information_parameter   { $$ = $1; }
| sctp_state_cookie_parameter            { $$ = $1; }
| sctp_supported_extensions_parameter    { $$ = $1; }
| sctp_supported_address_types_parameter { $$ = $1; }
| outgoing_ssn_reset_request             { $$ = $1; }
| incoming_ssn_reset_request             { $$ = $1; }
| ssn_tsn_reset_request                  { $$ = $1; }
| reconfig_response                      { $$ = $1; }
| add_outgoing_streams_request                { $$ = $1; }
| add_incoming_streams_request                { $$ = $1; }
;


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

sctp_supported_extensions_parameter
: MYSUPPORTED_EXTENSIONS '[' TYPES '=' ELLIPSIS ']' {
    $$ = new PacketDrillSctpParameter(SUPPORTED_EXTENSIONS, -1, NULL);
}
| MYSUPPORTED_EXTENSIONS '[' TYPES '=' '[' chunk_types_list ']' ']' {
    $$ = new PacketDrillSctpParameter(SUPPORTED_EXTENSIONS, $6->getListLength(), $6);
}

address_types_list
:                                     { $$ = new cQueue("empty_address_types_list");
}
| address_type                        { $$ = new cQueue("address_types_list");
                                        $$->insert($1);
}
| address_types_list ',' address_type { $$ = $1;
                                        $$->insert($3);
}
;

address_type
: INTEGER       { if (!is_valid_u16($1)) {
                  semantic_error("address type value out of range");
                  }
                  $$ = new_integer_expression($1, "%u"); }
| IPV4_TYPE     { $$ = new_integer_expression(SCTP_IPV4_ADDRESS_PARAMETER_TYPE, "%u"); }
| IPV6_TYPE     { $$ = new_integer_expression(SCTP_IPV6_ADDRESS_PARAMETER_TYPE, "%u"); }
;

sctp_supported_address_types_parameter
: MYSUPPORTED_ADDRESS_TYPES '[' TYPES '=' ELLIPSIS ']' {
    $$ = new PacketDrillSctpParameter(SUPPORTED_ADDRESS_TYPES, -1, NULL);
}
| MYSUPPORTED_ADDRESS_TYPES '[' TYPES '=' '[' address_types_list ']' ']' {
$6->setName("SupportedAddressTypes");
    $$ = new PacketDrillSctpParameter(SUPPORTED_ADDRESS_TYPES, $6->getLength(), $6);
}

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


packet_prefix
: direction {
    $$ = new PacketDrillPacket();
    $$->setDirection($1);
}
;


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

flags
: MYWORD {
    $$ = $1;
}
| '.' {
    $$ = strdup(".");
}
| MYWORD '.' {
    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;
    $$.protocol = IPPROTO_TCP;
}
;

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
: {
    $$ = new cQueue("opt_tcp_options");
}
| '<' tcp_option_list '>' {
    $$ = $2;
}
| '<' ELLIPSIS '>' {
    $$ = NULL; /* FLAG_OPTIONS_NOCHECK */
}
;


tcp_option_list
: tcp_option {
    $$ = new cQueue("tcp_option");
    $$->insert($1);
}
| tcp_option_list ',' tcp_option {
    $$ = $1;
    $$->insert($3);
}
;


tcp_option
: NOP {
    $$ = new PacketDrillTcpOption(TCPOPT_NOP, 1);
}
| EOL {
    $$ = new PacketDrillTcpOption(TCPOPT_EOL, 1);
}
| MSS INTEGER {
    $$ = new PacketDrillTcpOption(TCPOPT_MAXSEG, TCPOLEN_MAXSEG);
    if (!is_valid_u16($2)) {
        semantic_error("mss value out of range");
    }
    $$->setMss($2);
}
| WSCALE INTEGER {
    $$ = new PacketDrillTcpOption(TCPOPT_WINDOW, TCPOLEN_WINDOW);
    if (!is_valid_u8($2)) {
        semantic_error("window scale shift count out of range");
    }
    $$->setWindowScale($2);
}
| SACKOK {
    $$ = new PacketDrillTcpOption(TCPOPT_SACK_PERMITTED, TCPOLEN_SACK_PERMITTED);
}
| TCPSACK sack_block_list {
    $$ = new PacketDrillTcpOption(TCPOPT_SACK, 2+8*$2->getLength());
    $$->setBlockList($2);
}
| TIMESTAMP VAL INTEGER ECR INTEGER {
    uint32 val, ecr;
    $$ = new PacketDrillTcpOption(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;
    $$->setVal(val);
    $$->setEcr(ecr);
}
;

sack_block_list
: sack_block {
    $$ = new cQueue("sack_block_list");
    $$->insert($1);
}
| sack_block_list sack_block {
    $$ = $1; $$->insert($2);
}
;

gap_list
:            { $$ = new cQueue("gap_list");}
|  gap {
    $$ = new cQueue("gap_list");
    $$->insert($1);
}
| gap_list ',' gap {
    $$ = $1; $$->insert($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");
    }
    $$ = new PacketDrillStruct($1, $3);
}
;

dup_list
:            { $$ = new cQueue("dup_list");}
|  dup {
    $$ = new cQueue("dup_list");
    $$->insert($1);
}
| dup_list ',' dup {
    $$ = $1; $$->insert($3);
}
;

dup
: 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");
    }
    $$ = new PacketDrillStruct($1, $3);
}
;

sack_block
: INTEGER ':' INTEGER {
    if (!is_valid_u32($1)) {
        semantic_error("TCP SACK left sequence number out of range\n");
    }
    if (!is_valid_u32($3)) {
        semantic_error("TCP SACK right sequence number out of range");
    }
    PacketDrillStruct *block = new PacketDrillStruct($1, $3);
    $$ = block;
}
;

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

opt_end_time
: {
    $$ = -1;
}
| ELLIPSIS time {
    $$ = $2;
}
;

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

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

expression_list
: expression {
    $$ = new cQueue("new_expressionList");
    $$->insert((cObject*)$1);
}
| expression_list ',' expression {
    $$ = $1;
    $$->insert($3);
}
;

expression
: ELLIPSIS {
    $$ = new PacketDrillExpression(EXPR_ELLIPSIS);
}
| decimal_integer {
    $$ = $1; }
| hex_integer {
    $$ = $1;
}
| _HTONL_ '(' INTEGER ')' {
    if (!is_valid_u32($3)) {
        semantic_error("number out of range");
    }
    $$ = new_integer_expression($3, "%lu");
}
| _HTONL_ '(' HEX_INTEGER ')' {
    if (!is_valid_u32($3)) {
        semantic_error("number out of range");
    }
    $$ = new_integer_expression($3, "%lu");
}
| _HTONS_ '(' INTEGER ')' {
    if (!is_valid_u16($3)) {
        semantic_error("number out of range");
    }
    $$ = new_integer_expression($3, "%lu");
}
| MYWORD {
    $$ = new PacketDrillExpression(EXPR_WORD);
    $$->setString($1);
}
| MYSTRING {
    $$ = new PacketDrillExpression(EXPR_STRING);
    $$->setString($1);
    $$->setFormat("\"%s\"");
}
| MYSTRING ELLIPSIS {
    $$ = new PacketDrillExpression(EXPR_STRING);
    $$->setString($1);
    $$->setFormat("\"%s\"...");
}
| binary_expression {
    $$ = $1;
}
| sockaddr          {
    $$ = $1;
}
| array {
    $$ = $1;
}
| sctp_initmsg      {
    $$ = $1;
}
| sctp_assoc_value  {
    $$ = $1;
}
| sctp_rtoinfo      {
    $$ = $1;
}
| sctp_sackinfo     {
    $$ = $1;
}
| sctp_status       {
    $$ = $1;
}
| sctp_paddrparams  {
    $$ = $1;
}
| sctp_assocparams  {
    $$ = $1;
}
| sctp_sndrcvinfo   {
    $$ = $1;
}
| sctp_reset_streams{
    $$ = $1;
}
| sctp_add_streams  {
    $$ = $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 PacketDrillExpression(EXPR_BINARY);
    struct binary_expression *binary = (struct binary_expression *) malloc(sizeof(struct binary_expression));
    binary->op = strdup("|");
    binary->lhs = $1;
    binary->rhs = $3;
    $$->setBinary(binary);
}
;

array
: '[' ']' {
    $$ = new PacketDrillExpression(EXPR_LIST);
    $$->setList(NULL);
}
| '[' expression_list ']' {
    $$ = new PacketDrillExpression(EXPR_LIST);
    $$->setList($2);
}
;

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

srto_max
: SRTO_MAX '=' INTEGER {
    $$ = new_integer_expression($3, "%u");
}
| SRTO_MAX '=' ELLIPSIS { $$ = new PacketDrillExpression(EXPR_ELLIPSIS); }
;

srto_min
: SRTO_MIN '=' INTEGER {
    $$ = new_integer_expression($3, "%u");
}
| SRTO_MIN '=' ELLIPSIS { $$ = new PacketDrillExpression(EXPR_ELLIPSIS); }
;

sctp_assoc_id
: INTEGER {
    $$ = new_integer_expression($1, "%u");
}
| MYWORD {
    $$ = new PacketDrillExpression(EXPR_WORD);
    $$->setString($1);
}
| ELLIPSIS { $$ = new PacketDrillExpression(EXPR_ELLIPSIS); }
;

sctp_rtoinfo
: '{' SRTO_ASSOC_ID '=' sctp_assoc_id ',' srto_initial ',' srto_max ',' srto_min '}' {
    $$ = new PacketDrillExpression(EXPR_SCTP_RTOINFO);
    struct sctp_rtoinfo_expr *rtoinfo = (struct sctp_rtoinfo_expr *) malloc(sizeof(struct sctp_rtoinfo_expr));
    rtoinfo->srto_assoc_id = $4;
    rtoinfo->srto_initial = $6;
    rtoinfo->srto_max = $8;
    rtoinfo->srto_min = $10;
    $$->setRtoinfo(rtoinfo);
}
| '{' srto_initial ',' srto_max ',' srto_min '}' {
    $$ = new PacketDrillExpression(EXPR_SCTP_RTOINFO);
    struct sctp_rtoinfo_expr *rtoinfo = (struct sctp_rtoinfo_expr *) malloc(sizeof(struct sctp_rtoinfo_expr));
    rtoinfo->srto_assoc_id = new PacketDrillExpression(EXPR_ELLIPSIS);
    rtoinfo->srto_initial = $2;
    rtoinfo->srto_max = $4;
    rtoinfo->srto_min = $6;
    $$->setRtoinfo(rtoinfo);
}
;

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 PacketDrillExpression(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 PacketDrillExpression(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 PacketDrillExpression(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 PacketDrillExpression(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 PacketDrillExpression(EXPR_ELLIPSIS); }
;

sctp_assocparams
: '{' SASOC_ASSOC_ID '=' sctp_assoc_id ',' sasoc_asocmaxrxt ',' sasoc_number_peer_destinations ','
      sasoc_peer_rwnd ',' sasoc_local_rwnd ',' sasoc_cookie_life '}' {
    $$ = new PacketDrillExpression(EXPR_SCTP_ASSOCPARAMS);
    struct sctp_assocparams_expr *assocparams = (struct sctp_assocparams_expr *) malloc(sizeof(struct sctp_assocparams_expr));
    assocparams->sasoc_assoc_id = $4;
    assocparams->sasoc_asocmaxrxt = $6;
    assocparams->sasoc_number_peer_destinations = $8;
    assocparams->sasoc_peer_rwnd = $10;
    assocparams->sasoc_local_rwnd = $12;
    assocparams->sasoc_cookie_life = $14;
    $$->setAssocParams(assocparams);
}
| '{' sasoc_asocmaxrxt ',' sasoc_number_peer_destinations ','
      sasoc_peer_rwnd ',' sasoc_local_rwnd ',' sasoc_cookie_life '}' {
    $$ = new PacketDrillExpression(EXPR_SCTP_ASSOCPARAMS);
    struct sctp_assocparams_expr *assocparams = (struct sctp_assocparams_expr *) malloc(sizeof(struct sctp_assocparams_expr));
    assocparams->sasoc_assoc_id = new PacketDrillExpression(EXPR_ELLIPSIS);
    assocparams->sasoc_asocmaxrxt = $2;
    assocparams->sasoc_number_peer_destinations = $4;
    assocparams->sasoc_peer_rwnd = $6;
    assocparams->sasoc_local_rwnd = $8;
    assocparams->sasoc_cookie_life = $10;
    $$->setAssocParams(assocparams);
}
;


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 PacketDrillExpression(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 PacketDrillExpression(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 PacketDrillExpression(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 PacketDrillExpression(EXPR_ELLIPSIS); }
;

sctp_initmsg
: '{' sinit_num_ostreams ',' sinit_max_instreams ',' sinit_max_attempts ',' sinit_max_init_timeo '}'
{
    $$ = new PacketDrillExpression(EXPR_SCTP_INITMSG);
    struct sctp_initmsg_expr *initmsg = (struct sctp_initmsg_expr *) malloc(sizeof(struct sctp_initmsg_expr));
    initmsg->sinit_num_ostreams = $2;
    initmsg->sinit_max_instreams = $4;
    initmsg->sinit_max_attempts = $6;
    initmsg->sinit_max_init_timeo = $8;
    $$->setInitmsg(initmsg);
}
;

sockaddr
:   '{' SA_FAMILY '=' MYWORD ','
    SIN_PORT '=' _HTONS_ '(' INTEGER ')' ','
    SIN_ADDR '=' INET_ADDR '(' MYSTRING ')' '}' {
    if (strcmp($4, "AF_INET") == 0) {
        $$ = new PacketDrillExpression(EXPR_SOCKET_ADDRESS_IPV4);
        $$->setIp(new L3Address(IPv4Address()));
    } else if (strcmp($4, "AF_INET6") == 0) {
        $$ = new PacketDrillExpression(EXPR_SOCKET_ADDRESS_IPV6);
        $$->setIp(new L3Address(IPv6Address()));
    }
}
;

spp_address
: SPP_ADDRESS '=' ELLIPSIS { $$ = new PacketDrillExpression(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 PacketDrillExpression(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 PacketDrillExpression(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 PacketDrillExpression(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 PacketDrillExpression(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 PacketDrillExpression(EXPR_ELLIPSIS); }
;

sctp_paddrparams
: '{' SPP_ASSOC_ID '=' sctp_assoc_id ',' spp_address ',' spp_hbinterval ',' spp_pathmaxrxt ',' spp_pathmtu ','spp_flags ','
      spp_ipv6_flowlabel ',' spp_dscp'}' {
    $$ = new PacketDrillExpression(EXPR_SCTP_PEER_ADDR_PARAMS);
    struct sctp_paddrparams_expr *params = (struct sctp_paddrparams_expr *) malloc(sizeof(struct sctp_paddrparams_expr));
    params->spp_assoc_id = $4;
    params->spp_address = $6;
    params->spp_hbinterval = $8;
    params->spp_pathmaxrxt = $10;
    params->spp_pathmtu = $12;
    params->spp_flags = $14;
    params->spp_ipv6_flowlabel = $16;
    params->spp_dscp = $18;
    $$->setPaddrParams(params);
}
| '{' spp_address ',' spp_hbinterval ',' spp_pathmaxrxt ',' spp_pathmtu ','spp_flags ','
      spp_ipv6_flowlabel ',' spp_dscp'}' {
    $$ = new PacketDrillExpression(EXPR_SCTP_PEER_ADDR_PARAMS);
    struct sctp_paddrparams_expr *params = (struct sctp_paddrparams_expr *) malloc(sizeof(struct sctp_paddrparams_expr));
    params->spp_assoc_id = new PacketDrillExpression(EXPR_ELLIPSIS);
    params->spp_address = $2;
    params->spp_hbinterval = $4;
    params->spp_pathmaxrxt = $6;
    params->spp_pathmtu = $8;
    params->spp_flags = $10;
    params->spp_ipv6_flowlabel = $12;
    params->spp_dscp = $14;
    $$->setPaddrParams(params);
}
;

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 PacketDrillExpression(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 PacketDrillExpression(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 PacketDrillExpression(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 PacketDrillExpression(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 PacketDrillExpression(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 PacketDrillExpression(EXPR_ELLIPSIS); }
;

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

sctp_status
: '{' SSTAT_ASSOC_ID '=' sctp_assoc_id ',' sstat_state ',' sstat_rwnd ',' sstat_unackdata ',' sstat_penddata ',' sstat_instrms ',' sstat_outstrms ','
    sstat_fragmentation_point ',' sstat_primary '}' {
    $$ = new PacketDrillExpression(EXPR_SCTP_STATUS);
    struct sctp_status_expr *stat = (struct sctp_status_expr *) calloc(1, sizeof(struct sctp_status_expr));
    stat->sstat_assoc_id = $4;
    stat->sstat_state = $6;
    stat->sstat_rwnd = $8;
    stat->sstat_unackdata = $10;
    stat->sstat_penddata = $12;
    stat->sstat_instrms = $14;
    stat->sstat_outstrms = $16;
    stat->sstat_fragmentation_point = $18;
    stat->sstat_primary = $20;
    $$->setStatus(stat);
}
| '{' sstat_state ',' sstat_rwnd ',' sstat_unackdata ',' sstat_penddata ',' sstat_instrms ',' sstat_outstrms ','
    sstat_fragmentation_point ',' sstat_primary '}' {
    $$ = new PacketDrillExpression(EXPR_SCTP_STATUS);
    struct sctp_status_expr *stat = (struct sctp_status_expr *) calloc(1, sizeof(struct sctp_status_expr));
    stat->sstat_assoc_id = new PacketDrillExpression(EXPR_ELLIPSIS);
    stat->sstat_state = $2;
    stat->sstat_rwnd = $4;
    stat->sstat_unackdata = $6;
    stat->sstat_penddata = $8;
    stat->sstat_instrms = $10;
    stat->sstat_outstrms = $12;
    stat->sstat_fragmentation_point = $14;
    stat->sstat_primary = $16;
    $$->setStatus(stat);
}
;

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 PacketDrillExpression(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 PacketDrillExpression(EXPR_ELLIPSIS); }
;

sinfo_flags
: SINFO_FLAGS '=' expression { $$ = $3; }
;

sinfo_ppid
: SINFO_PPID '=' _HTONL_ '(' INTEGER ')' {
    if (!is_valid_u32($5)) {
        semantic_error("sinfo_ppid out of range");
    }
    $$ = new_integer_expression($5, "%u");
}
| SINFO_PPID '=' ELLIPSIS { $$ = new PacketDrillExpression(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 PacketDrillExpression(EXPR_ELLIPSIS); }
;

sinfo_timetolive
: SINFO_TIMETOLIVE '=' INTEGER {
    if (!is_valid_u32($3)) {
        semantic_error("sinfo_timetolive out of range");
    }
    $$ = new_integer_expression($3, "%u");
}
| SINFO_TIMETOLIVE '=' ELLIPSIS { $$ = new PacketDrillExpression(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 PacketDrillExpression(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 PacketDrillExpression(EXPR_ELLIPSIS); }
;


sctp_sndrcvinfo
: '{' sinfo_stream ',' sinfo_ssn ',' sinfo_flags ',' sinfo_ppid ',' sinfo_context ',' sinfo_timetolive ','
      sinfo_tsn ',' sinfo_cumtsn ',' SINFO_ASSOC_ID '=' sctp_assoc_id '}' {
    $$ = new PacketDrillExpression(EXPR_SCTP_SNDRCVINFO);
    struct sctp_sndrcvinfo_expr *info = (struct sctp_sndrcvinfo_expr *) calloc(1, sizeof(struct sctp_sndrcvinfo_expr));
    info->sinfo_stream = $2;
    info->sinfo_ssn = $4;
    info->sinfo_flags = $6;
    info->sinfo_ppid = $8;
    info->sinfo_context = $10;
    info->sinfo_timetolive = $12;
    info->sinfo_tsn = $14;
    info->sinfo_cumtsn = $16;
    info->sinfo_assoc_id = $20;
    $$->setSndRcvInfo(info);
}
| '{' sinfo_stream ',' sinfo_ssn ',' sinfo_flags ',' sinfo_ppid ',' sinfo_context ',' sinfo_timetolive ','
      sinfo_tsn ',' sinfo_cumtsn '}' {
    $$ = new PacketDrillExpression(EXPR_SCTP_SNDRCVINFO);
    struct sctp_sndrcvinfo_expr *info = (struct sctp_sndrcvinfo_expr *) malloc(sizeof(struct sctp_sndrcvinfo_expr));
    info->sinfo_stream = $2;
    info->sinfo_ssn = $4;
    info->sinfo_flags = $6;
    info->sinfo_ppid = $8;
    info->sinfo_context = $10;
    info->sinfo_timetolive = $12;
    info->sinfo_tsn = $14;
    info->sinfo_cumtsn = $16;
    info->sinfo_assoc_id = new PacketDrillExpression(EXPR_ELLIPSIS);
    $$->setSndRcvInfo(info);
};

srs_flags
: SRS_FLAGS '=' INTEGER {
printf("SRS_FLAGS = INTEGER\n");
    if (!is_valid_u16($3)) {
        semantic_error("srs_flags out of range");
    }
    $$ = new_integer_expression($3, "%hu");
}
| SRS_FLAGS '=' MYWORD {
printf("SRS_FLAGS = MYWORD\n");
    $$ = new PacketDrillExpression(EXPR_WORD);
    $$->setString($3);
}
| SRS_FLAGS '=' binary_expression {
    $$ = $3;
}
;

sctp_reset_streams
: '{' SRS_ASSOC_ID '=' sctp_assoc_id ',' srs_flags ',' SRS_NUMBER_STREAMS '=' INTEGER ',' SRS_STREAM_LIST '=' array '}' {
    $$ = new PacketDrillExpression(EXPR_SCTP_RESET_STREAMS);
    struct sctp_reset_streams_expr *rs = (struct sctp_reset_streams_expr *) malloc(sizeof(struct sctp_reset_streams_expr));
    rs->srs_assoc_id = $4;
    rs->srs_flags = $6;
    if (!is_valid_u16($10)) {
        semantic_error("srs_number_streams out of range");
    }
    rs->srs_number_streams = new_integer_expression($10, "%hu");
    rs->srs_stream_list = $14;
    $$->setResetStreams(rs);
}
| '{' srs_flags ',' SRS_NUMBER_STREAMS '=' INTEGER ',' SRS_STREAM_LIST '=' array '}' {
    $$ = new PacketDrillExpression(EXPR_SCTP_RESET_STREAMS);
    struct sctp_reset_streams_expr *rs = (struct sctp_reset_streams_expr *) malloc(sizeof(struct sctp_reset_streams_expr));
    rs->srs_assoc_id = new PacketDrillExpression(EXPR_ELLIPSIS);
    rs->srs_flags = $2;
    if (!is_valid_u16($6)) {
        semantic_error("srs_number_streams out of range");
    }
    rs->srs_number_streams = new_integer_expression($6, "%hu");
    rs->srs_stream_list = $10;
    $$->setResetStreams(rs);
}
;

sctp_add_streams
: '{' SAS_ASSOC_ID '=' sctp_assoc_id ',' SAS_INSTRMS '=' INTEGER ',' SAS_OUTSTRMS '=' INTEGER '}' {
    $$ = new PacketDrillExpression(EXPR_SCTP_ADD_STREAMS);
    struct sctp_add_streams_expr *rs = (struct sctp_add_streams_expr *) malloc(sizeof(struct sctp_add_streams_expr));
    rs->sas_assoc_id = $4;
    if (!is_valid_u16($8)) {
        semantic_error("sas_instrms out of range");
    }
    rs->sas_instrms = new_integer_expression($8, "%hu");
    if (!is_valid_u16($12)) {
        semantic_error("sas_outstrms out of range");
    }
    rs->sas_outstrms = new_integer_expression($12, "%hu");
    $$->setAddStreams(rs);
}
| '{' SAS_INSTRMS '=' INTEGER ',' SAS_OUTSTRMS '=' INTEGER '}' {
    $$ = new PacketDrillExpression(EXPR_SCTP_ADD_STREAMS);
    struct sctp_add_streams_expr *rs = (struct sctp_add_streams_expr *) malloc(sizeof(struct sctp_add_streams_expr));
    rs->sas_assoc_id = new PacketDrillExpression(EXPR_ELLIPSIS);
    if (!is_valid_u16($4)) {
        semantic_error("sas_instrms out of range");
    }
    rs->sas_instrms = new_integer_expression($4, "%hu");
    if (!is_valid_u16($8)) {
        semantic_error("sas_outstrms out of range");
    }
    rs->sas_outstrms = new_integer_expression($8, "%hu");
    $$->setAddStreams(rs);
}
;


sctp_assoc_value
: '{' ASSOC_ID '=' sctp_assoc_id ',' ASSOC_VALUE '=' expression '}' {
    $$ = new PacketDrillExpression(EXPR_SCTP_ASSOCVAL);
    struct sctp_assoc_value_expr *assocval = (struct sctp_assoc_value_expr *) malloc(sizeof(struct sctp_assoc_value_expr));
    assocval->assoc_id = $4;
    assocval->assoc_value = $8;
    $$->setAssocval(assocval);
}
| '{' ASSOC_VALUE '=' expression '}' {
    $$ = new PacketDrillExpression(EXPR_SCTP_ASSOCVAL);
    struct sctp_assoc_value_expr *assocval = (struct sctp_assoc_value_expr *) malloc(sizeof(struct sctp_assoc_value_expr));
    assocval->assoc_id = new PacketDrillExpression(EXPR_ELLIPSIS);
    assocval->assoc_value = $4;
    $$->setAssocval(assocval);
}
;

sack_delay
: MYSACK_DELAY '=' INTEGER {
    if (!is_valid_u32($3)) {
        semantic_error("sack_delay out of range");
    }
    $$ = new_integer_expression($3, "%u");
}
| MYSACK_DELAY '=' ELLIPSIS {
    $$ = new PacketDrillExpression(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 PacketDrillExpression(EXPR_ELLIPSIS); }

sctp_sackinfo
: '{' SACK_ASSOC_ID '=' sctp_assoc_id ',' sack_delay ',' sack_freq '}' {
    $$ = new PacketDrillExpression(EXPR_SCTP_SACKINFO);
    struct sctp_sack_info_expr *sackinfo = (struct sctp_sack_info_expr *) malloc(sizeof(struct sctp_sack_info_expr));
    sackinfo->sack_assoc_id = $4;
    sackinfo->sack_delay = $6;
    sackinfo->sack_freq = $8;
    $$->setSackinfo(sackinfo);
}
| '{' sack_delay ',' sack_freq '}' {
    $$ = new PacketDrillExpression(EXPR_SCTP_SACKINFO);
    struct sctp_sack_info_expr *sackinfo = (struct sctp_sack_info_expr *) malloc(sizeof(struct sctp_sack_info_expr));
    sackinfo->sack_assoc_id = new PacketDrillExpression(EXPR_ELLIPSIS);
    sackinfo->sack_delay = $2;
    sackinfo->sack_freq = $4;
    $$->setSackinfo(sackinfo);
}
;

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

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

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

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

