catch parse failure; exit with error

catch parse failure; exit with error

/*
 * Parses BEL terms.
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include "belparser.h"

%%{
    machine set;
    write data;
}%%

bel_arg_stack* stack_init(int max) {
    bel_arg_stack* stack;
    bel_ast_node*  contents;

    stack = (bel_arg_stack *) malloc(sizeof(bel_arg_stack));
    contents = (bel_ast_node *) malloc(sizeof(bel_ast_node) * max);

    if (contents == NULL) {
        fprintf(stderr, "Insufficient memory to initialize stack.\n");
        exit(1);  /* Exit, returning error code. */
    }

    stack->top      = -1;
    stack->max      = max;
    stack->contents = contents;

    return stack;
};

void stack_destroy(bel_arg_stack* stack) {
    free(stack->contents);
    stack->top = -1;
    stack->max = 0;
    stack->contents = NULL;
};

bel_ast_node* stack_peek(bel_arg_stack* stack) {
    if (stack_is_full(stack)) {
        fprintf(stderr, "Can't push element on stack: stack is full.\n");
        exit(1);
    }

    bel_ast_node* top = &(stack->contents[stack->top]);
    return top;
};

void stack_push(bel_arg_stack* stack, bel_ast_node element) {
    if (stack_is_full(stack)) {
        fprintf(stderr, "Can't push element on stack: stack is full.\n");
        exit(1);
    }

    stack->contents[++stack->top] = element;
};

bel_ast_node stack_pop(bel_arg_stack* stack) {
    if (stack_is_empty(stack)) {
        fprintf(stderr, "Can't pop element from stack: stack is empty.\n");
        exit(1);
    }

    return stack->contents[stack->top--];
};

int stack_is_empty(bel_arg_stack* stack) {
    return stack->top < 0;
};

int stack_is_full(bel_arg_stack* stack) {
    return stack->top >= stack->max - 1;
};

bel_ast_node* bel_new_ast_node_token(bel_ast_token_type type) {
    bel_ast_node* node;
    node = malloc(sizeof(bel_ast_node));
    node->token = malloc(sizeof(bel_ast_node_token));
    node->token->type  = TOKEN;
    node->token->ttype = type;
    node->token->left  = NULL;
    node->token->right = NULL;
    return node;
};

bel_ast_node* bel_new_ast_node_value(bel_ast_value_type type, char value[]) {
    bel_ast_node* node;
    char*         copy_value;

    if (value) {
        copy_value = (char *) malloc(sizeof(value));
        strncpy(copy_value, value, sizeof(value));
    } else {
        copy_value = NULL;
    }

    node = malloc(sizeof(bel_ast_node));
    node->value = malloc(sizeof(bel_ast_node_value));
    node->value->type  = VALUE;
    node->value->vtype = type;
    node->value->value = copy_value;
    return node;
};

bel_ast* bel_new_ast() {
    bel_ast* ast;
    ast = malloc(sizeof(bel_ast));
    ast->root = NULL;
    return ast;
};

void bel_free_ast(bel_ast* ast) {
    if (!ast) {
        return;
    }
    bel_free_ast_node(ast->root);
    free(ast);
};

void bel_free_ast_node(bel_ast_node* node) {
    if (node->type_info->type == TOKEN && node->token->ttype != TOKEN_NIL) {
        fprintf(stdout, "free token: %u\n", node->type_info->ttype);
        bel_free_ast_node(node->token->left);
        bel_free_ast_node(node->token->right);
    }
    free(node);
};

void bel_print_ast_node(bel_ast_node* node) {
    if (!node) {
        return;
    }

    switch(node->type_info->type) {
    case TOKEN:
        switch(node->type_info->ttype) {
            case TOKEN_ARG:
                fprintf(stdout, "ARG\n");
                break;
            case TOKEN_NIL:
                fprintf(stdout, "NIL\n");
                break;
            case TOKEN_NV:
                fprintf(stdout, "NV\n");
                break;
            case TOKEN_TERM:
                fprintf(stdout, "TERM\n");
                break;
        }
        bel_print_ast_node(node->token->left);
        bel_print_ast_node(node->token->right);
        break;
    case VALUE:
        switch(node->type_info->vtype) {
            case VALUE_FX:
                fprintf(stdout, "fx(%s)\n", node->value->value);
                break;
            case VALUE_NIL:
                fprintf(stdout, "nil(%s)\n", node->value->value);
                break;
            case VALUE_PFX:
                fprintf(stdout, "pfx(%s)\n", node->value->value);
                break;
            case VALUE_VAL:
                fprintf(stdout, "val(%s)\n", node->value->value);
                break;
        }
        break;
    }
}

void bel_print_ast(bel_ast* ast) {
    if (!ast) {
        return;
    }
    bel_print_ast_node(ast->root);
    fprintf(stdout, "\n");
};

#define VALUE_SIZE     128
#define BUFSIZE        1024 * 32 // 32 kilobytes
#define ARG_STACK_SIZE 100

bel_ast* parse_term(char* line, char value[]) {
    int            cs;
    char           *p;
    char           *pe;
    bel_ast_node*  current_term;
    bel_ast_node*  current_nv;
    bel_ast_node*  arg;
    bel_ast_node*  next_arg;
    bel_ast*       ast;
    bel_arg_stack* arg_stack;
    int            vi;

    p            = line;
    pe           = line + strlen(line);
    current_term = NULL;
    current_nv   = NULL;
    arg          = NULL;
    next_arg     = NULL;
    ast          = bel_new_ast();
    arg_stack    = stack_init(ARG_STACK_SIZE);
    vi           = 0;

    %%{
        action vi {
            value[vi++] = fc;
        }

        action FX {
            current_term = bel_new_ast_node_token(TOKEN_TERM);
            current_term->token->left = bel_new_ast_node_value(VALUE_FX,  value);

            // Set term as AST root; on first term
            if (!ast->root) {
                ast->root = current_term;
            }

            // Create Nil argument as a placeholder; add to stack
            arg = bel_new_ast_node_token(TOKEN_ARG);
            arg->token->left  = bel_new_ast_node_token(TOKEN_NIL);
            arg->token->right = bel_new_ast_node_token(TOKEN_NIL);
            stack_push(arg_stack, *arg);

            // Set as term argument
            current_term->token->right = arg;

	        memset(value, '\0', VALUE_SIZE);
            vi = 0;
        }

        action PFX {
            bel_ast_node* top = stack_peek(arg_stack);
            current_nv = bel_new_ast_node_token(TOKEN_NV);
            current_nv->token->left  = bel_new_ast_node_value(VALUE_PFX, value);
            current_nv->token->right = bel_new_ast_node_value(VALUE_VAL, "");
            top->token->left = current_nv;

            next_arg = bel_new_ast_node_token(TOKEN_ARG);
            next_arg->token->left  = bel_new_ast_node_token(TOKEN_NIL);
            next_arg->token->right = bel_new_ast_node_token(TOKEN_NIL);
            top->token->right = next_arg;
            stack_push(arg_stack, *next_arg);

	        memset(value, '\0', VALUE_SIZE);
            vi = 0;
        }

        action VAL {
            bel_ast_node* top = stack_peek(arg_stack);
            if (!current_nv) {
                current_nv = bel_new_ast_node_token(TOKEN_NV);
                current_nv->token->left  = bel_new_ast_node_value(VALUE_PFX, "");
                current_nv->token->right = bel_new_ast_node_value(VALUE_VAL, value);
                top->token->left = current_nv;

                next_arg = bel_new_ast_node_token(TOKEN_ARG);
                next_arg->token->left  = bel_new_ast_node_token(TOKEN_NIL);
                next_arg->token->right = bel_new_ast_node_token(TOKEN_NIL);
                top->token->right = next_arg;
                stack_push(arg_stack, *next_arg);
            } else {
                current_nv->token->right = bel_new_ast_node_value(VALUE_VAL, value);
            }

            current_nv = 0;
	        memset(value, '\0', VALUE_SIZE);
            vi = 0;
        }

        SP           = ' ';
        O_PAREN      = '(';
        C_PAREN      = ')';
        COLON        = ':';
        IDENT        = [a-zA-Z0-9_]+;
        STRING       = ('"' ('\\\"' | [^"])* '"');
        FUNCTION     = ('proteinAbundance'|'p'|'rnaAbundance'|'r'|'abundance'|'a'|'microRNAAbundance'|'m'|'geneAbundance'|'g'|'biologicalProcess'|'bp'|'pathology'|'path'|'complexAbundance'|'complex'|'translocation'|'tloc'|'cellSecretion'|'sec'|'cellSurfaceExpression'|'surf'|'reaction'|'rxn'|'compositeAbundance'|'composite'|'fusion'|'fus'|'degradation'|'deg'|'molecularActivity'|'act'|'catalyticActivity'|'cat'|'kinaseActivity'|'kin'|'phosphataseActivity'|'phos'|'peptidaseActivity'|'pep'|'ribosylationActivity'|'ribo'|'transcriptionalActivity'|'tscript'|'transportActivity'|'tport'|'gtpBoundActivity'|'gtp'|'chaperoneActivity'|'chap'|'proteinModification'|'pmod'|'substitution'|'sub'|'truncation'|'trunc'|'reactants'|'products'|'list');
#      FUNCTION     = ('p'|'r'|'a'|'m'|'g'|'bp');

#        term =
#            FUNCTION $vi %FX <>lerr(FX) O_PAREN C_PAREN;
        term =
            FUNCTION $vi %FX O_PAREN (IDENT $vi ':')? @PFX (STRING $vi | IDENT $vi) %VAL C_PAREN;

        main :=
            term;

        # Initialize and execute.
        write init;
        write exec;
    }%%

    if (arg_stack) {
        stack_destroy(arg_stack);
    }

    return ast;
};

int main(int argc, char *argv[]) {
    FILE *input;
    int len;
    char line[BUFSIZE];
    char value[VALUE_SIZE];

    if (argc == 2) {
        input = fopen(argv[1], "rb");
    } else {
        input = stdin;
    }

    while (fgets(line, BUFSIZE, input) != NULL) {
        len = strlen(line);
        if (line[len - 1] == '\n') {
            line[len - 1] = '\0';
        }

	    memset(value, '\0', VALUE_SIZE);
	    fprintf(stdout, "parsing line -> %s\n", line);
        bel_ast* tree = parse_term(line, value);

        if (!tree->root) {
            fprintf(stderr, "parse failed\n");
            exit(1);
        }
        bel_print_ast(tree);
        bel_free_ast(tree);
    }
    fclose(input);
    return 0;
}

// vim: ft=c sw=4 ts=4 sts=4 expandtab

/*
 * Parses BEL terms.
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include "belparser.h"

%%{
    machine set;
    write data;
}%%

bel_arg_stack* stack_init(int max) {
    bel_arg_stack* stack;
    bel_ast_node*  contents;

    stack = (bel_arg_stack *) malloc(sizeof(bel_arg_stack));
    contents = (bel_ast_node *) malloc(sizeof(bel_ast_node) * max);

    if (contents == NULL) {
        fprintf(stderr, "Insufficient memory to initialize stack.\n");
        exit(1);  /* Exit, returning error code. */
    }

    stack->top      = -1;
    stack->max      = max;
    stack->contents = contents;

    return stack;
};

void stack_destroy(bel_arg_stack* stack) {
    free(stack->contents);
    stack->top = -1;
    stack->max = 0;
    stack->contents = NULL;
};

bel_ast_node* stack_peek(bel_arg_stack* stack) {
    if (stack_is_full(stack)) {
        fprintf(stderr, "Can't push element on stack: stack is full.\n");
        exit(1);
    }

    bel_ast_node* top = &(stack->contents[stack->top]);
    return top;
};

void stack_push(bel_arg_stack* stack, bel_ast_node element) {
    if (stack_is_full(stack)) {
        fprintf(stderr, "Can't push element on stack: stack is full.\n");
        exit(1);
    }

    stack->contents[++stack->top] = element;
};

bel_ast_node stack_pop(bel_arg_stack* stack) {
    if (stack_is_empty(stack)) {
        fprintf(stderr, "Can't pop element from stack: stack is empty.\n");
        exit(1);
    }

    return stack->contents[stack->top--];
};

int stack_is_empty(bel_arg_stack* stack) {
    return stack->top < 0;
};

int stack_is_full(bel_arg_stack* stack) {
    return stack->top >= stack->max - 1;
};

bel_ast_node* bel_new_ast_node_token(bel_ast_token_type type) {
    bel_ast_node* node;
    node = malloc(sizeof(bel_ast_node));
    node->token = malloc(sizeof(bel_ast_node_token));
    node->token->type  = TOKEN;
    node->token->ttype = type;
    node->token->left  = NULL;
    node->token->right = NULL;
    return node;
};

bel_ast_node* bel_new_ast_node_value(bel_ast_value_type type, char value[]) {
    bel_ast_node* node;
    char*         copy_value;

    if (value) {
        copy_value = (char *) malloc(sizeof(value));
        strncpy(copy_value, value, sizeof(value));
    } else {
        copy_value = NULL;
    }

    node = malloc(sizeof(bel_ast_node));
    node->value = malloc(sizeof(bel_ast_node_value));
    node->value->type  = VALUE;
    node->value->vtype = type;
    node->value->value = copy_value;
    return node;
};

bel_ast* bel_new_ast() {
    bel_ast* ast;
    ast = malloc(sizeof(bel_ast));
    ast->root = NULL;
    return ast;
};

void bel_free_ast(bel_ast* ast) {
    if (!ast) {
        return;
    }
    bel_free_ast_node(ast->root);
    free(ast);
};

void bel_free_ast_node(bel_ast_node* node) {
    if (node->type_info->type == TOKEN && node->token->ttype != TOKEN_NIL) {
        fprintf(stdout, "free token: %u\n", node->type_info->ttype);
        bel_free_ast_node(node->token->left);
        bel_free_ast_node(node->token->right);
    }
    free(node);
};

void bel_print_ast_node(bel_ast_node* node) {
    if (!node) {
        return;
    }

    switch(node->type_info->type) {
    case TOKEN:
        switch(node->type_info->ttype) {
            case TOKEN_ARG:
                fprintf(stdout, "ARG\n");
                break;
            case TOKEN_NIL:
                fprintf(stdout, "NIL\n");
                break;
            case TOKEN_NV:
                fprintf(stdout, "NV\n");
                break;
            case TOKEN_TERM:
                fprintf(stdout, "TERM\n");
                break;
        }
        bel_print_ast_node(node->token->left);
        bel_print_ast_node(node->token->right);
        break;
    case VALUE:
        switch(node->type_info->vtype) {
            case VALUE_FX:
                fprintf(stdout, "fx(%s)\n", node->value->value);
                break;
            case VALUE_NIL:
                fprintf(stdout, "nil(%s)\n", node->value->value);
                break;
            case VALUE_PFX:
                fprintf(stdout, "pfx(%s)\n", node->value->value);
                break;
            case VALUE_VAL:
                fprintf(stdout, "val(%s)\n", node->value->value);
                break;
        }
        break;
    }
}

void bel_print_ast(bel_ast* ast) {
    if (!ast) {
        return;
    }
    bel_print_ast_node(ast->root);
    fprintf(stdout, "\n");
};

#define VALUE_SIZE     128
#define BUFSIZE        1024 * 32 // 32 kilobytes
#define ARG_STACK_SIZE 100

bel_ast* parse_term(char* line, char value[]) {
    int            cs;
    char           *p;
    char           *pe;
    bel_ast_node*  current_term;
    bel_ast_node*  current_nv;
    bel_ast_node*  arg;
    bel_ast_node*  next_arg;
    bel_ast*       ast;
    bel_arg_stack* arg_stack;
    int            vi;

    p            = line;
    pe           = line + strlen(line);
    current_term = NULL;
    current_nv   = NULL;
    arg          = NULL;
    next_arg     = NULL;
    ast          = bel_new_ast();
    arg_stack    = stack_init(ARG_STACK_SIZE);
    vi           = 0;

    %%{
        action vi {
            value[vi++] = fc;
        }

        action FX {
            current_term = bel_new_ast_node_token(TOKEN_TERM);
            current_term->token->left = bel_new_ast_node_value(VALUE_FX,  value);

            // Set term as AST root; on first term
            if (!ast->root) {
                ast->root = current_term;
            }

            // Create Nil argument as a placeholder; add to stack
            arg = bel_new_ast_node_token(TOKEN_ARG);
            arg->token->left  = bel_new_ast_node_token(TOKEN_NIL);
            arg->token->right = bel_new_ast_node_token(TOKEN_NIL);
            stack_push(arg_stack, *arg);

            // Set as term argument
            current_term->token->right = arg;

	        memset(value, '\0', VALUE_SIZE);
            vi = 0;
        }

        action PFX {
            bel_ast_node* top = stack_peek(arg_stack);
            current_nv = bel_new_ast_node_token(TOKEN_NV);
            current_nv->token->left  = bel_new_ast_node_value(VALUE_PFX, value);
            current_nv->token->right = bel_new_ast_node_value(VALUE_VAL, "");
            top->token->left = current_nv;

            next_arg = bel_new_ast_node_token(TOKEN_ARG);
            next_arg->token->left  = bel_new_ast_node_token(TOKEN_NIL);
            next_arg->token->right = bel_new_ast_node_token(TOKEN_NIL);
            top->token->right = next_arg;
            stack_push(arg_stack, *next_arg);

	        memset(value, '\0', VALUE_SIZE);
            vi = 0;
        }

        action VAL {
            bel_ast_node* top = stack_peek(arg_stack);
            if (!current_nv) {
                current_nv = bel_new_ast_node_token(TOKEN_NV);
                current_nv->token->left  = bel_new_ast_node_value(VALUE_PFX, "");
                current_nv->token->right = bel_new_ast_node_value(VALUE_VAL, value);
                top->token->left = current_nv;

                next_arg = bel_new_ast_node_token(TOKEN_ARG);
                next_arg->token->left  = bel_new_ast_node_token(TOKEN_NIL);
                next_arg->token->right = bel_new_ast_node_token(TOKEN_NIL);
                top->token->right = next_arg;
                stack_push(arg_stack, *next_arg);
            } else {
                current_nv->token->right = bel_new_ast_node_value(VALUE_VAL, value);
            }

            current_nv = 0;
	        memset(value, '\0', VALUE_SIZE);
            vi = 0;
        }

        SP           = ' ';
        O_PAREN      = '(';
        C_PAREN      = ')';
        COLON        = ':';
        IDENT        = [a-zA-Z0-9_]+;
        STRING       = ('"' ('\\\"' | [^"])* '"');
        FUNCTION     = ('proteinAbundance'|'p'|'rnaAbundance'|'r'|'abundance'|'a'|'microRNAAbundance'|'m'|'geneAbundance'|'g'|'biologicalProcess'|'bp'|'pathology'|'path'|'complexAbundance'|'complex'|'translocation'|'tloc'|'cellSecretion'|'sec'|'cellSurfaceExpression'|'surf'|'reaction'|'rxn'|'compositeAbundance'|'composite'|'fusion'|'fus'|'degradation'|'deg'|'molecularActivity'|'act'|'catalyticActivity'|'cat'|'kinaseActivity'|'kin'|'phosphataseActivity'|'phos'|'peptidaseActivity'|'pep'|'ribosylationActivity'|'ribo'|'transcriptionalActivity'|'tscript'|'transportActivity'|'tport'|'gtpBoundActivity'|'gtp'|'chaperoneActivity'|'chap'|'proteinModification'|'pmod'|'substitution'|'sub'|'truncation'|'trunc'|'reactants'|'products'|'list');
#       FUNCTION     = ('p'|'r'|'a'|'m'|'g'|'bp');

#        term =
#            FUNCTION $vi %FX <>lerr(FX) O_PAREN C_PAREN;
        term =
            FUNCTION $vi %FX O_PAREN (IDENT $vi ':')? @PFX (STRING $vi | IDENT $vi) %VAL C_PAREN;

        main :=
            term;

        # Initialize and execute.
        write init;
        write exec;
    }%%

    if (arg_stack) {
        stack_destroy(arg_stack);
    }

    return ast;
};

int main(int argc, char *argv[]) {
    FILE *input;
    int len;
    char line[BUFSIZE];
    char value[VALUE_SIZE];

    if (argc == 2) {
        input = fopen(argv[1], "rb");
    } else {
        input = stdin;
    }

    while (fgets(line, BUFSIZE, input) != NULL) {
        len = strlen(line);
        if (line[len - 1] == '\n') {
            line[len - 1] = '\0';
        }

	    memset(value, '\0', VALUE_SIZE);
	    fprintf(stdout, "parsing line -> %s\n", line);
        bel_ast* tree = parse_term(line, value);

        bel_print_ast(tree);
        bel_free_ast(tree);
    }
    fclose(input);
    return 0;
}

// vim: ft=c sw=4 ts=4 sts=4 expandtab


Dup a pre-existing empty buffer, for SPEED (not...)

%%{
  machine message;

  # data, p, pe, eof, cs, top, stack, ts, te and act
  getkey data.getbyte(p); # code for retrieving current char

  ## Action state - needs resetting once consumed!
  action mark {
    buffer = EMPTY_BUFFER.dup
  }
  action buffer {
    buffer << fc
  }
  action mark_key {
    mk = buffer # needs reset
    buffer = nil
  }
  action mark_message {
    message = Stomp::Message.new(nil, nil)
    message_size = 0
  }
  action check_message_size {
    message_size += 1
    raise MessageSizeExceeded if message_size > max_message_size
  }

  ## Action commands - should reset used state!
  action write_command {
    message.write_command(buffer)
    buffer = nil
  }

  action write_header {
    message.write_header(mk, buffer)
    mk = buffer = nil
  }

  action write_body {
    message.write_body(buffer)
    buffer = nil
  }

  action finish_headers {
    if message.headers.has_key?("content-length")
      content_length = Integer(message.headers["content-length"])
    end
  }

  action consume_null {
    buffer.bytesize < content_length if content_length
  }

  action consume_octet {
    if content_length
      buffer.bytesize < content_length
    else
      true
    end
  }

  action finish_message {
    yield message
    message = nil
  }

  ## Stomp message grammar

  NULL = "\0";
  EOL = "\r"? . "\n";
  OCTET = any;

  client_command = "CONNECT" > mark;
  command = client_command $ buffer % write_command . EOL;

  HEADER_ESCAPE = "\\" . ("\\" | "n" | "r" | "c");
  HEADER_OCTET = HEADER_ESCAPE | (OCTET - "\r" - "\n" - "\\" - ":");
  header_key = HEADER_OCTET+ > mark $ buffer % mark_key;
  header_value = HEADER_OCTET* > mark $ buffer;
  header = header_key . ":" . header_value;
  headers = (header % write_header . EOL)* % finish_headers . EOL;

  body = ((NULL when consume_null | ^NULL when consume_octet)* $ buffer) >to(mark) % write_body <: NULL;

  message = ((command > mark_message) :> headers :> (body @ finish_message)) $ check_message_size;

  stream := (EOL | message)*;
}%%

module Stompede
  module Stomp
    # Provides an API for running parsers.
    #
    # It:
    # - provides a .parse method which remembers state between invocations
    # - buffering of data between chunks
    class Parser
      # this manipulates the singleton class of our context,
      # so we do not want to run this code very often or we
      # bust our ruby method caching
      %% write data noprefix;

      class << self
        # @attr [Integer] maximum size (in bytes) a message may become before raising MessageSizeExceeded.
        attr_accessor :max_message_size
      end

      EMPTY_BUFFER = "".force_encoding("BINARY").freeze
      self.max_message_size = 1024 * 100 # 100KB

      # Parse a chunk of Stomp-formatted data into a Message.
      #
      # @param [String] data
      # @param [Parser] state
      # @return [Stomp::Message, nil]
      def self.parse(data, state)
        pe = data.bytesize # end of chunk

        p = 0 # pointer to current character
        message = state.message # message currently being parsed, if any
        cs = state.current_state # current state
        mk = state.mark_key # key for header currently being read
        buffer = state.buffer # buffered data for marks
        message_size = state.message_size # how many bytes current message contains in total
        content_length = state.content_length # content length of current message

        %% write exec;

        if cs == Stomp::Parser.error
          # build error message context
          ctx = 7
          min = [0, p - ctx].max
          cur = p - min
          err = data.byteslice(min, ctx + 1 + ctx)
          chr = err[cur]
          err[cur] = " -->#{err[cur]}<-- "

          raise ParseError.new("unexpected #{chr.inspect} in data (#{err.inspect})")
        else
          state.message = message
          state.current_state = cs
          state.mark_key = mk
          state.buffer = buffer
          state.message_size = message_size
          state.content_length = content_length
        end

        nil
      end

      # Construct the parser.
      def initialize
        @error = nil
        @buffer = nil
        @message_size = nil
        @current_state = Stomp::Parser.start
        @message = nil
        @mark_key = nil
      end

      # @return [StandardError] error raised during parsing
      attr_accessor :error

      # @return [Integer] message size accumulated so far
      attr_accessor :message_size

      # @return [String] binary string of current parsing buffer
      attr_accessor :buffer

      # @return [Integer, nil]
      attr_accessor :content_length

      # @return [Integer] current parsing state
      attr_accessor :current_state

      # @return [Stomp::Message] stomp message currently being parsed
      attr_accessor :message

      # @return [String] header key currently being parsed
      attr_accessor :mark_key

      # Parse a chunk of data. Retains state beteween invocations.
      #
      # @param [String] data
      # @raise [ParseError]
      def parse(data)
        raise error if error

        begin
          Parser.parse(data, self) do |message|
            yield message
          end
        rescue => error
          self.error = error
          raise
        end
      end
    end
  end
end

%%{
  machine message;

  # data, p, pe, eof, cs, top, stack, ts, te and act
  getkey data.getbyte(p); # code for retrieving current char

  ## Action state - needs resetting once consumed!
  action mark {
    buffer = "".force_encoding("BINARY")
  }
  action buffer {
    buffer << fc
  }
  action mark_key {
    mk = buffer # needs reset
    buffer = nil
  }
  action mark_message {
    message = Stomp::Message.new(nil, nil)
    message_size = 0
  }
  action check_message_size {
    message_size += 1
    raise MessageSizeExceeded if message_size > max_message_size
  }

  ## Action commands - should reset used state!
  action write_command {
    message.write_command(buffer)
    buffer = nil
  }

  action write_header {
    message.write_header(mk, buffer)
    mk = buffer = nil
  }

  action write_body {
    message.write_body(buffer)
    buffer = nil
  }

  action finish_headers {
    if message.headers.has_key?("content-length")
      content_length = Integer(message.headers["content-length"])
    end
  }

  action consume_null {
    buffer.bytesize < content_length if content_length
  }

  action consume_octet {
    if content_length
      buffer.bytesize < content_length
    else
      true
    end
  }

  action finish_message {
    yield message
    message = nil
  }

  ## Stomp message grammar

  NULL = "\0";
  EOL = "\r"? . "\n";
  OCTET = any;

  client_command = "CONNECT" > mark;
  command = client_command $ buffer % write_command . EOL;

  HEADER_ESCAPE = "\\" . ("\\" | "n" | "r" | "c");
  HEADER_OCTET = HEADER_ESCAPE | (OCTET - "\r" - "\n" - "\\" - ":");
  header_key = HEADER_OCTET+ > mark $ buffer % mark_key;
  header_value = HEADER_OCTET* > mark $ buffer;
  header = header_key . ":" . header_value;
  headers = (header % write_header . EOL)* % finish_headers . EOL;

  body = ((NULL when consume_null | ^NULL when consume_octet)* $ buffer) >to(mark) % write_body <: NULL;

  message = ((command > mark_message) :> headers :> (body @ finish_message)) $ check_message_size;

  stream := (EOL | message)*;
}%%

module Stompede
  module Stomp
    # Provides an API for running parsers.
    #
    # It:
    # - provides a .parse method which remembers state between invocations
    # - buffering of data between chunks
    class Parser
      # this manipulates the singleton class of our context,
      # so we do not want to run this code very often or we
      # bust our ruby method caching
      %% write data noprefix;

      class << self
        # @attr [Integer] maximum size (in bytes) a message may become before raising MessageSizeExceeded.
        attr_accessor :max_message_size
      end

      self.max_message_size = 1024 * 100 # 100KB

      # Parse a chunk of Stomp-formatted data into a Message.
      #
      # @param [String] data
      # @param [Parser] state
      # @return [Stomp::Message, nil]
      def self.parse(data, state)
        pe = data.bytesize # end of chunk

        p = 0 # pointer to current character
        message = state.message # message currently being parsed, if any
        cs = state.current_state # current state
        mk = state.mark_key # key for header currently being read
        buffer = state.buffer # buffered data for marks
        message_size = state.message_size # how many bytes current message contains in total
        content_length = state.content_length # content length of current message

        %% write exec;

        if cs == Stomp::Parser.error
          # build error message context
          ctx = 7
          min = [0, p - ctx].max
          cur = p - min
          err = data.byteslice(min, ctx + 1 + ctx)
          chr = err[cur]
          err[cur] = " -->#{err[cur]}<-- "

          raise ParseError.new("unexpected #{chr.inspect} in data (#{err.inspect})")
        else
          state.message = message
          state.current_state = cs
          state.mark_key = mk
          state.buffer = buffer
          state.message_size = message_size
          state.content_length = content_length
        end

        nil
      end

      # Construct the parser.
      def initialize
        @error = nil
        @buffer = nil
        @message_size = nil
        @current_state = Stomp::Parser.start
        @message = nil
        @mark_key = nil
      end

      # @return [StandardError] error raised during parsing
      attr_accessor :error

      # @return [Integer] message size accumulated so far
      attr_accessor :message_size

      # @return [String] binary string of current parsing buffer
      attr_accessor :buffer

      # @return [Integer, nil]
      attr_accessor :content_length

      # @return [Integer] current parsing state
      attr_accessor :current_state

      # @return [Stomp::Message] stomp message currently being parsed
      attr_accessor :message

      # @return [String] header key currently being parsed
      attr_accessor :mark_key

      # Parse a chunk of data. Retains state beteween invocations.
      #
      # @param [String] data
      # @raise [ParseError]
      def parse(data)
        raise error if error

        begin
          Parser.parse(data, self) do |message|
            yield message
          end
        rescue => error
          self.error = error
          raise
        end
      end
    end
  end
end


* Changed prefix of character classes from 'w' to 'u'

/* scanner.rl -*-C-*- */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wchar.h>
#include <wctype.h>
#include "global.h"
#include "internal.h"

#define RET goto ret;

#define STRIP(c) do { \
    strip_char = c;   \
    goto ret;         \
} while(0)

%%{
    machine StdTok;
    alphtype unsigned int;
    include WChar "src/uchar-ucs4.rl";

    frt_alpha = ualpha;
    frt_alnum = ualnum;
    frt_digit = udigit;

    include StdTok "src/scanner.in";

    main := any @{ fhold; fcall frt_tokenizer; };
}%%

%% write data nofinal;

static const char *position_in_mb( const unsigned int *orig_wc,
                                   const char *orig_mb,
                                   const unsigned int *curr_wc )
{
    const char         *mb = orig_mb;
    const unsigned int *wc = orig_wc;

    while (wc < curr_wc)
    {
        char buf[MB_LEN_MAX];
        mb += wctomb(buf, *wc);
        ++wc;
    }

    return mb;
}

static int mb_next_char(unsigned int *wchr, const char *s, mbstate_t *state)
{
    int num_bytes;
    if ((num_bytes = (int)mbrtowc((wchar_t*)wchr, s, MB_CUR_MAX, state)) < 0) {
        const char *t = s;
        do {
            t++;
            ZEROSET(state, mbstate_t);
            num_bytes = (int)mbrtowc((wchar_t*)wchr, t, MB_CUR_MAX, state);
        } while ((num_bytes < 0) && (*t != 0));
        num_bytes = t - s;
        if (*t == 0) *wchr = 0;
    }
    return num_bytes;
}

static int wc_next_char(char *s, const unsigned int *wchr, mbstate_t *state)
{
    return (int)wcrtomb(s, *wchr, state);
}

/*
 * All input to Ragel must be in a buffer of 32bit unicode codepoints.
 * To that end, we require that the input to the scanner must be in a
 * codepage that mbtowc will convert to unicode codepoints.  The easy
 * way to do this is to supply the scanner with UTF8.  It will call
 * mbtowc on the buffer, pass it to the tokenizer which will extract
 * one token out.  This token will then be converted back with wctomb.
 *
 * frt_scan_mb takes in a pointer to the mb buffer +inmb+ that has max
 * size +in_size+.  The resulting token will be stored in +outmb+,
 * with at most +out_size+ bytes written.
 *
 * While tokenizing, part of the token may be stripped out. Eg,
 * 'foo!!' will become 'foo', and 'http://www.bar.com' will become
 * 'www.bar.com'.  So that the caller can track what exactly has been
 * tokenized in +inmb+, +startmb+ and +endmb+ are set to point to the
 * extremeties of the original unmodified token in +inmb+.
 *
 * The size of the token written out to +outmb+ is stored in
 * +token_size+.
 */

static void mb_to_wc(const char *in,
                     unsigned int *out, size_t out_size)
{
    mbstate_t state;
    const char *in_p     = in;
    unsigned int *out_p = out;
    ZEROSET(&state, mbstate_t);

    while (*in_p && out_p < (out + out_size/sizeof(*out)))
    {
        /* We can break out early here on, say, a space XXX */

        int n = mb_next_char(out_p, in_p, &state);
        if (n < 0)
        {
            ++in_p;
            continue;
        }

        in_p += n;
        ++out_p;
    }
}

static void wc_to_mb(char *out, size_t out_size, int *token_size,
                     const unsigned int *in_wc, size_t in_wc_size)
{
    mbstate_t state;
    char *out_p = out;
    const unsigned int *in_wc_p = in_wc;
    ZEROSET(&state, mbstate_t);
    *token_size = 0;

    while (out_p < (out + out_size) && in_wc_p < (in_wc + in_wc_size))
    {
        if (!*in_wc_p)
            break;

        int n = wc_next_char(out_p, in_wc_p, &state);
        if (n < 0)
        {
            ++in_wc_p;
            continue;
        }

        out_p += n;
        ++in_wc_p;
    }

    *token_size = out_p - out;
}

void frt_std_scan_mb(const char *in_mb,
                     char *out_mb, size_t out_mb_size,
                     const char **start_mb,
                     const char **end_mb,
                     int *token_size)
{
    int cs, act, top;
    int stack[32];
    unsigned int *ts = 0, *te = 0;

    %% write init;

    unsigned int in_wc[4096] = {0};
    mb_to_wc(in_mb, in_wc, sizeof(in_wc));

    unsigned int *p = in_wc, *pe = 0, *eof = pe;
    int skip = 0;
    int trunc = 0;
    unsigned int strip_char = 0;

    *end_mb = 0;
    *start_mb = 0;
    *token_size = 0;

    %% write exec;

    if ( cs == StdTok_error )
                   fwprintf(stderr, L"PARSE ERROR\n");
    else if ( ts ) fwprintf(stderr, L"STUFF LEFT: '%ls'\n", ts);
    return;

 ret:
    {
        unsigned int out_wc[4096] = {0};
        size_t __len = te - ts - skip - trunc;

        *start_mb = position_in_mb(in_wc, in_mb, ts);
        *end_mb   = position_in_mb(in_wc, in_mb, te);

        if (strip_char) {
            unsigned int *__p = ts + skip;
            unsigned int *__o = out_wc;
            for (; __p < (ts + skip + __len); ++__p) {
                if (*__p != strip_char)
                    *__o++ = *__p;
            }
        }
        else {
            memcpy(out_wc, ts + skip, __len*sizeof(unsigned int));
        }

        wc_to_mb(out_mb, out_mb_size, token_size, out_wc, sizeof(out_wc));
        out_mb[*token_size] = 0;
    }
}

/* scanner.rl -*-C-*- */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wchar.h>
#include <wctype.h>
#include "global.h"
#include "internal.h"

#define RET goto ret;

#define STRIP(c) do { \
    strip_char = c;   \
    goto ret;         \
} while(0)

%%{
    machine StdTok;
    alphtype unsigned int;
    include WChar "src/wchar.rl";

    frt_alpha = walpha;
    frt_alnum = walnum;
    frt_digit = wdigit;

    include StdTok "src/scanner.in";

    main := any @{ fhold; fcall frt_tokenizer; };
}%%

%% write data nofinal;

static const char *position_in_mb( const unsigned int *orig_wc,
                                   const char *orig_mb,
                                   const unsigned int *curr_wc )
{
    const char         *mb = orig_mb;
    const unsigned int *wc = orig_wc;

    while (wc < curr_wc)
    {
        char buf[MB_LEN_MAX];
        mb += wctomb(buf, *wc);
        ++wc;
    }

    return mb;
}

static int mb_next_char(unsigned int *wchr, const char *s, mbstate_t *state)
{
    int num_bytes;
    if ((num_bytes = (int)mbrtowc((wchar_t*)wchr, s, MB_CUR_MAX, state)) < 0) {
        const char *t = s;
        do {
            t++;
            ZEROSET(state, mbstate_t);
            num_bytes = (int)mbrtowc((wchar_t*)wchr, t, MB_CUR_MAX, state);
        } while ((num_bytes < 0) && (*t != 0));
        num_bytes = t - s;
        if (*t == 0) *wchr = 0;
    }
    return num_bytes;
}

static int wc_next_char(char *s, const unsigned int *wchr, mbstate_t *state)
{
    return (int)wcrtomb(s, *wchr, state);
}

/*
 * All input to Ragel must be in a buffer of 32bit unicode codepoints.
 * To that end, we require that the input to the scanner must be in a
 * codepage that mbtowc will convert to unicode codepoints.  The easy
 * way to do this is to supply the scanner with UTF8.  It will call
 * mbtowc on the buffer, pass it to the tokenizer which will extract
 * one token out.  This token will then be converted back with wctomb.
 *
 * frt_scan_mb takes in a pointer to the mb buffer +inmb+ that has max
 * size +in_size+.  The resulting token will be stored in +outmb+,
 * with at most +out_size+ bytes written.
 *
 * While tokenizing, part of the token may be stripped out. Eg,
 * 'foo!!' will become 'foo', and 'http://www.bar.com' will become
 * 'www.bar.com'.  So that the caller can track what exactly has been
 * tokenized in +inmb+, +startmb+ and +endmb+ are set to point to the
 * extremeties of the original unmodified token in +inmb+.
 *
 * The size of the token written out to +outmb+ is stored in
 * +token_size+.
 */

static void mb_to_wc(const char *in,
                     unsigned int *out, size_t out_size)
{
    mbstate_t state;
    const char *in_p     = in;
    unsigned int *out_p = out;
    ZEROSET(&state, mbstate_t);

    while (*in_p && out_p < (out + out_size/sizeof(*out)))
    {
        /* We can break out early here on, say, a space XXX */

        int n = mb_next_char(out_p, in_p, &state);
        if (n < 0)
        {
            ++in_p;
            continue;
        }

        in_p += n;
        ++out_p;
    }
}

static void wc_to_mb(char *out, size_t out_size, int *token_size,
                     const unsigned int *in_wc, size_t in_wc_size)
{
    mbstate_t state;
    char *out_p = out;
    const unsigned int *in_wc_p = in_wc;
    ZEROSET(&state, mbstate_t);
    *token_size = 0;

    while (out_p < (out + out_size) && in_wc_p < (in_wc + in_wc_size))
    {
        if (!*in_wc_p)
            break;

        int n = wc_next_char(out_p, in_wc_p, &state);
        if (n < 0)
        {
            ++in_wc_p;
            continue;
        }

        out_p += n;
        ++in_wc_p;
    }

    *token_size = out_p - out;
}

void frt_std_scan_mb(const char *in_mb,
                     char *out_mb, size_t out_mb_size,
                     const char **start_mb,
                     const char **end_mb,
                     int *token_size)
{
    int cs, act, top;
    int stack[32];
    unsigned int *ts = 0, *te = 0;

    %% write init;

    unsigned int in_wc[4096] = {0};
    mb_to_wc(in_mb, in_wc, sizeof(in_wc));

    unsigned int *p = in_wc, *pe = 0, *eof = pe;
    int skip = 0;
    int trunc = 0;
    unsigned int strip_char = 0;

    *end_mb = 0;
    *start_mb = 0;
    *token_size = 0;

    %% write exec;

    if ( cs == StdTok_error )
                   fwprintf(stderr, L"PARSE ERROR\n");
    else if ( ts ) fwprintf(stderr, L"STUFF LEFT: '%ls'\n", ts);
    return;

 ret:
    {
        unsigned int out_wc[4096] = {0};
        size_t __len = te - ts - skip - trunc;

        *start_mb = position_in_mb(in_wc, in_mb, ts);
        *end_mb   = position_in_mb(in_wc, in_mb, te);

        if (strip_char) {
            unsigned int *__p = ts + skip;
            unsigned int *__o = out_wc;
            for (; __p < (ts + skip + __len); ++__p) {
                if (*__p != strip_char)
                    *__o++ = *__p;
            }
        }
        else {
            memcpy(out_wc, ts + skip, __len*sizeof(unsigned int));
        }

        wc_to_mb(out_mb, out_mb_size, token_size, out_wc, sizeof(out_wc));
        out_mb[*token_size] = 0;
    }
}


* remove old code

* remove old code


Unquote quoted-strings.  Don't remove interior line breaks of generic headers (they may have been quoted, which the unfold function doesn't detect).

// Copyright (c) 2009 - Decho Corp.
/* To compile to .cpp:
   ragel parser.rl -G2 -o parser.cpp
*/

#include "parser.h"

// From uri.rl
std::string unescape(const std::string& str);

static
HTTP::Version
parseVersion(const char *str)
{
    HTTP::Version ver;
    ver.major = atoi(str + 5);
    ver.minor = atoi(strchr(str + 5, '.') + 1);
    return ver;
}

static
HTTP::Method
parseMethod(const char *str, const char *end)
{
	*(char*)end = '\0';
    for(size_t i = 0; i < 8; ++i) {
		if (stricmp(str, HTTP::methods[i]) == 0) {
			return (HTTP::Method)i;
		}
    }
    throw std::invalid_argument("Unrecognized method");
}

static
std::string
unfold(char *p, char *pe)
{
    char *start = p;
    char *pw = p;

    while (p < pe) {
        // Skip leading whitespace
        if (pw == start) {
            if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
                ++p; ++pw; ++start;
                continue;
            }
        }
        // Only copy if necessary
        if (pw != p) {
            *pw = *p;
        }
        ++p; ++pw;
    }
    // Remove trailing whitespace
    do {
        --pw;
    } while ((*pw == ' ' || *pw == '\t' || *pw == '\r' || *pw == '\n') && pw >= start);
    ++pw;
    return std::string(start, pw - start);
}

static
std::string
unquote(char *p, char *pe)
{
    if (pe == p || *p != '"')
        return std::string(p, pe - p);
    char *start = p;
    char *pw = p;

    assert(*p == '"');
    assert(*(pe - 1) == '"');
    ++p; ++pw; ++start;
    --pe;
    bool escaping = false;
    while (p < pe) {
        if (escaping) {
            escaping = false;
        } else if (*p == '\\') {
            escaping = true;
            ++p;
            continue;
        }
        // Only copy if necessary
        if (pw != p) {
            *pw = *p;
        }
        ++p; ++pw;
    }
    return std::string(start, pw - start);
}

%%{
    machine http_parser;

    action mark { mark = fpc; }
    action done { fbreak; }

    # basic character types
    OCTET = any;
    CHAR = ascii;
    UPALPHA = "A".."Z";
    LOALPHA = "a".."z";
    ALPHA = alpha;
    DIGIT = digit;
    CTL = cntrl | 127;
    CR = "\r";
    LF = "\n";
    SP = " ";
    HT = "\t";

    # almost-basic character types
    # note that we allow a single LF for a CR LF
    CRLF = CR LF | LF;
    LWS = CRLF? ( SP | HT )+;
    TEXT = LWS | (OCTET -- CTL);
    HEX = xdigit;

    # some basic tokens
    separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT;
    token = (CHAR -- (separators | CTL))+;
    quoted_pair = "\\" CHAR;
    ctext = TEXT -- ("(" | ")");
    base_comment = "(" ( ctext | quoted_pair )* ")";
    comment = "(" ( ctext | quoted_pair | base_comment )* ")";
    qdtext = TEXT -- "\"";
    quoted_string = "\"" ( qdtext | quoted_pair )* "\"";

    action parse_HTTP_Version {
        *m_ver = parseVersion(mark);
        mark = NULL;
    }

    HTTP_Version = ("HTTP/" DIGIT+ "." DIGIT+) >mark %parse_HTTP_Version;

    delta_seconds = DIGIT+;

    product_version = token;
    product = token ("/" product_version)?;

    qvalue = ("0" ("." DIGIT{0,3})) | ("1" ("." "0"{0,3}));

    subtag = ALPHA{1,8};
    primary_tag = ALPHA{1,8};
    language_tag = primary_tag ("-" subtag);

    weak = "W/";
    opaque_tag = quoted_string;
    entity_tag = (weak)? opaque_tag;

    bytes_unit = "bytes";
    other_range_unit = token;
    range_unit = bytes_unit | other_range_unit;

    action save_field_name {
        m_temp1 = std::string(mark, fpc - mark);
        mark = NULL;
    }
    action save_field_value {
        if (m_headerHandled) {
            m_headerHandled = false;
        } else {
            std::string fieldValue = unfold((char*)mark, (char*)fpc);

            StringMap::iterator it = m_entity->extension.find(m_temp1);
            if (it == m_entity->extension.end()) {
                m_entity->extension[m_temp1] = fieldValue;
            } else {
                it->second.append(", ");
                it->second.append(fieldValue);
            }
            mark = NULL;
        }
    }

    field_chars = OCTET -- (CTL | CR LF SP HT);
    field_name = token >mark %save_field_name;
    field_value = TEXT* >mark %save_field_value;
    message_header = field_name ":" field_value;

    action save_string {
        *m_string = std::string(mark, fpc - mark);
        mark = NULL;
    }
    action save_ulong {
        *m_ulong = _atoi64(mark);
        mark = NULL;
    }

    action save_element {
        m_list->insert(std::string(mark, fpc - mark));
        mark = NULL;
    }
    action save_element_eof {
		m_list->insert(std::string(mark, pe - mark));
        mark = NULL;
    }
    element = token >mark %save_element %/save_element_eof;
    list = LWS* element ( LWS* ',' LWS* element)* LWS*;

    action save_parameterized_list_element {
        ValueWithParameters vp;
        vp.value = std::string(mark, fpc - mark);
        m_parameterizedList->push_back(vp);
        m_parameters = &m_parameterizedList->back().parameters;
        mark = NULL;
    }

    action save_parameter_attribute {
        m_temp1 = std::string(mark, fpc - mark);
        mark = NULL;
    }

    action save_parameter_value {
        (*m_parameters)[m_temp1] = unquote((char*)mark, (char*)fpc);
        mark = NULL;
    }

    attribute = token >mark %save_parameter_attribute;
    value = token | quoted_string >mark %save_parameter_value;
    parameter = attribute '=' value;
    parameterizedListElement = token >mark %save_parameterized_list_element (';' parameter)*;
    parameterizedList = LWS* parameterizedListElement ( LWS* ',' LWS* parameterizedListElement)* LWS*;

    action set_connection {
        m_headerHandled = true;
        m_list = &m_general->connection;
    }

    action set_transfer_encoding {
        m_headerHandled = true;
        m_parameterizedList = &m_general->transferEncoding;
    }

    Connection = 'Connection:' @set_connection list;
    Transfer_Encoding = 'Transfer-Encoding:' @set_transfer_encoding parameterizedList;

    general_header = Connection | Transfer_Encoding;

    action set_content_length {
        m_headerHandled = true;
        m_ulong = &m_entity->contentLength;
    }

    Content_Length = 'Content-Length:' @set_content_length LWS* DIGIT+ >mark %save_ulong LWS*;

    action set_content_type
    {
		m_headerHandled = true;
		m_parameters = &m_entity->contentType.parameters;
    }
    action save_type
    {
		m_entity->contentType.type = std::string(mark, fpc - mark);
		mark = NULL;
    }
    action save_subtype
    {
		m_entity->contentType.subtype = std::string(mark, fpc - mark);
		mark = NULL;
    }

    type = token >mark %save_type;
    subtype = token >mark %save_subtype;
    media_type = type'/' subtype (';' parameter)*;
    Content_Type = 'Content-Type:' @set_content_type LWS* media_type LWS*;

    extension_header = message_header;

    entity_header = Content_Length | Content_Type | extension_header;

}%%

%%{
    machine http_request_parser;
    include http_parser;
    include uri_parser "../uri.rl";

    action parse_Method {
        m_request->requestLine.method = parseMethod(mark, fpc);
        mark = NULL;
    }

    action set_host {
        m_headerHandled = true;
        m_string = &m_request->request.host;
    }

    Host = 'Host:' @set_host LWS* host (':' port)? >mark %save_string LWS*;

    request_header = Host;

    Method = token >mark %parse_Method;
    Request_URI = ( "*" | absolute_URI | hier_part | authority);
    Request_Line = Method SP Request_URI SP HTTP_Version CRLF;
    Request = Request_Line ((general_header | request_header | entity_header) CRLF)* CRLF @done;

    main := Request;
    write data;
}%%

HTTP::RequestParser::RequestParser(Request& request)
: m_request(&request),
  m_ver(&request.requestLine.ver),
  m_uri(&request.requestLine.uri),
  m_path(&request.requestLine.uri.path),
  m_general(&request.general),
  m_entity(&request.entity)
{}

void
HTTP::RequestParser::init()
{
    RagelParser::init();
    %% write init;
}

bool
HTTP::RequestParser::complete() const
{
    return cs >= http_request_parser_first_final;
}

bool
HTTP::RequestParser::error() const
{
    return cs == http_request_parser_error;
}

void
HTTP::RequestParser::exec()
{
    %% write exec;
}

%%{
    machine http_response_parser;
    include http_parser;
    include uri_parser "../uri.rl";

    action parse_Status_Code {
        m_response->status.status = (HTTP::Status)atoi(mark);
        mark = NULL;
    }

    action parse_Reason_Phrase {
        m_response->status.reason = std::string(mark, fpc - mark);
        mark = NULL;
    }

    action set_location {
        m_headerHandled = true;
    }

    Location = 'Location:' @set_location LWS* absolute_URI LWS*;

    response_header = Location;

    Status_Code = DIGIT{3} > mark %parse_Status_Code;
    Reason_Phrase = (TEXT -- (CR | LF))* >mark %parse_Reason_Phrase;
    Status_Line = HTTP_Version SP Status_Code SP Reason_Phrase CRLF;
    Response = Status_Line ((general_header | response_header | entity_header) CRLF)* CRLF @done;

    main := Response;

    write data;
}%%

HTTP::ResponseParser::ResponseParser(Response& response)
: m_response(&response),
  m_ver(&response.status.ver),
  m_uri(&response.response.location),
  m_path(&response.response.location.path),
  m_general(&response.general),
  m_entity(&response.entity)
{}

void
HTTP::ResponseParser::init()
{
    RagelParser::init();
    %% write init;
}

bool
HTTP::ResponseParser::complete() const
{
    return cs >= http_response_parser_first_final;
}

bool
HTTP::ResponseParser::error() const
{
    return cs == http_response_parser_error;
}

void
HTTP::ResponseParser::exec()
{
    %% write exec;
}

%%{
    machine http_trailer_parser;
    include http_parser;

    trailer = (entity_header CRLF)*;

    main := trailer CRLF @done;

    write data;
}%%

HTTP::TrailerParser::TrailerParser(EntityHeaders& entity)
: m_entity(&entity)
{}

void
HTTP::TrailerParser::init()
{
    RagelParser::init();
    %% write init;
}

bool
HTTP::TrailerParser::complete() const
{
    return cs >= http_trailer_parser_first_final;
}

bool
HTTP::TrailerParser::error() const
{
    return cs == http_trailer_parser_error;
}

void
HTTP::TrailerParser::exec()
{
    %% write exec;
}

// Copyright (c) 2009 - Decho Corp.
/* To compile to .cpp:
   ragel parser.rl -G2 -o parser.cpp
*/

#include "parser.h"

// From uri.rl
std::string unescape(const std::string& str);

static
HTTP::Version
parseVersion(const char *str)
{
    HTTP::Version ver;
    ver.major = atoi(str + 5);
    ver.minor = atoi(strchr(str + 5, '.') + 1);
    return ver;
}

static
HTTP::Method
parseMethod(const char *str, const char *end)
{
	*(char*)end = '\0';
    for(size_t i = 0; i < 8; ++i) {
		if (stricmp(str, HTTP::methods[i]) == 0) {
			return (HTTP::Method)i;
		}
    }
    throw std::invalid_argument("Unrecognized method");
}

static
std::string
unfold(char *p, char *pe)
{
    char *start = p;
    char *pw = p;

    while (p < pe) {
        // Skip leading whitespace
        if (pw == start) {
            if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
                ++p; ++pw; ++start;
                continue;
            }
        }
        // Remove interior line breaks
        if (*p == '\r' || *p == '\n') {
            ++p;
            continue;
        }
        // Only copy if necessary
        if (pw != p) {
            *pw = *p;
        }
        ++p; ++pw;
    }
    // Remove trailing whitespace (\r and \n already removed)
    do {
        --pw;
    } while ((*pw == ' ' || *pw == '\t') && pw >= start);
    ++pw;
    return std::string(start, pw - start);
}

%%{
    machine http_parser;

    action mark { mark = fpc; }
    action done { fbreak; }

    # basic character types
    OCTET = any;
    CHAR = ascii;
    UPALPHA = "A".."Z";
    LOALPHA = "a".."z";
    ALPHA = alpha;
    DIGIT = digit;
    CTL = cntrl | 127;
    CR = "\r";
    LF = "\n";
    SP = " ";
    HT = "\t";

    # almost-basic character types
    # note that we allow a single LF for a CR LF
    CRLF = CR LF | LF;
    LWS = CRLF? ( SP | HT )+;
    TEXT = LWS | (OCTET -- CTL);
    HEX = xdigit;

    # some basic tokens
    separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT;
    token = (CHAR -- (separators | CTL))+;
    quoted_pair = "\\" CHAR;
    ctext = TEXT -- ("(" | ")");
    base_comment = "(" ( ctext | quoted_pair )* ")";
    comment = "(" ( ctext | quoted_pair | base_comment )* ")";
    qdtext = TEXT -- "\"";
    quoted_string = "\"" ( qdtext | quoted_pair )* "\"";

    action parse_HTTP_Version {
        *m_ver = parseVersion(mark);
        mark = NULL;
    }

    HTTP_Version = ("HTTP/" DIGIT+ "." DIGIT+) >mark %parse_HTTP_Version;

    delta_seconds = DIGIT+;

    product_version = token;
    product = token ("/" product_version)?;

    qvalue = ("0" ("." DIGIT{0,3})) | ("1" ("." "0"{0,3}));

    subtag = ALPHA{1,8};
    primary_tag = ALPHA{1,8};
    language_tag = primary_tag ("-" subtag);

    weak = "W/";
    opaque_tag = quoted_string;
    entity_tag = (weak)? opaque_tag;

    bytes_unit = "bytes";
    other_range_unit = token;
    range_unit = bytes_unit | other_range_unit;

    action save_field_name {
        m_temp1 = std::string(mark, fpc - mark);
        mark = NULL;
    }
    action save_field_value {
        if (m_headerHandled) {
            m_headerHandled = false;
        } else {
            std::string fieldValue = unfold((char*)mark, (char*)fpc);

            StringMap::iterator it = m_entity->extension.find(m_temp1);
            if (it == m_entity->extension.end()) {
                m_entity->extension[m_temp1] = fieldValue;
            } else {
                it->second.append(", ");
                it->second.append(fieldValue);
            }
            mark = NULL;
        }
    }

    field_chars = OCTET -- (CTL | CR LF SP HT);
    field_name = token >mark %save_field_name;
    field_value = TEXT* >mark %save_field_value;
    message_header = field_name ":" field_value;

    action save_string {
        *m_string = std::string(mark, fpc - mark);
        mark = NULL;
    }
    action save_ulong {
        *m_ulong = _atoi64(mark);
        mark = NULL;
    }

    action save_element {
        m_list->insert(std::string(mark, fpc - mark));
        mark = NULL;
    }
    action save_element_eof {
		m_list->insert(std::string(mark, pe - mark));
        mark = NULL;
    }
    element = token >mark %save_element %/save_element_eof;
    list = LWS* element ( LWS* ',' LWS* element)* LWS*;

    action save_parameterized_list_element {
        ValueWithParameters vp;
        vp.value = std::string(mark, fpc - mark);
        m_parameterizedList->push_back(vp);
        m_parameters = &m_parameterizedList->back().parameters;
        mark = NULL;
    }

    action save_parameter_attribute {
        m_temp1 = std::string(mark, fpc - mark);
        mark = NULL;
    }

    action save_parameter_value {
        (*m_parameters)[m_temp1] = std::string(mark, fpc - mark);
        mark = NULL;
    }

    attribute = token >mark %save_parameter_attribute;
    value = token | quoted_string >mark %save_parameter_value;
    parameter = attribute '=' value;
    parameterizedListElement = token >mark %save_parameterized_list_element (';' parameter)*;
    parameterizedList = LWS* parameterizedListElement ( LWS* ',' LWS* parameterizedListElement)* LWS*;

    action set_connection {
        m_headerHandled = true;
        m_list = &m_general->connection;
    }

    action set_transfer_encoding {
        m_headerHandled = true;
        m_parameterizedList = &m_general->transferEncoding;
    }

    Connection = 'Connection:' @set_connection list;
    Transfer_Encoding = 'Transfer-Encoding:' @set_transfer_encoding parameterizedList;

    general_header = Connection | Transfer_Encoding;

    action set_content_length {
        m_headerHandled = true;
        m_ulong = &m_entity->contentLength;
    }

    Content_Length = 'Content-Length:' @set_content_length LWS* DIGIT+ >mark %save_ulong LWS*;

    action set_content_type
    {
		m_headerHandled = true;
		m_parameters = &m_entity->contentType.parameters;
    }
    action save_type
    {
		m_entity->contentType.type = std::string(mark, fpc - mark);
		mark = NULL;
    }
    action save_subtype
    {
		m_entity->contentType.subtype = std::string(mark, fpc - mark);
		mark = NULL;
    }

    type = token >mark %save_type;
    subtype = token >mark %save_subtype;
    media_type = type'/' subtype (';' parameter)*;
    Content_Type = 'Content-Type:' @set_content_type LWS* media_type LWS*;

    extension_header = message_header;

    entity_header = Content_Length | Content_Type | extension_header;

}%%

%%{
    machine http_request_parser;
    include http_parser;
    include uri_parser "../uri.rl";

    action parse_Method {
        m_request->requestLine.method = parseMethod(mark, fpc);
        mark = NULL;
    }

    action set_host {
        m_headerHandled = true;
        m_string = &m_request->request.host;
    }

    Host = 'Host:' @set_host LWS* host (':' port)? >mark %save_string LWS*;

    request_header = Host;

    Method = token >mark %parse_Method;
    Request_URI = ( "*" | absolute_URI | hier_part | authority);
    Request_Line = Method SP Request_URI SP HTTP_Version CRLF;
    Request = Request_Line ((general_header | request_header | entity_header) CRLF)* CRLF @done;

    main := Request;
    write data;
}%%

HTTP::RequestParser::RequestParser(Request& request)
: m_request(&request),
  m_ver(&request.requestLine.ver),
  m_uri(&request.requestLine.uri),
  m_path(&request.requestLine.uri.path),
  m_general(&request.general),
  m_entity(&request.entity)
{}

void
HTTP::RequestParser::init()
{
    RagelParser::init();
    %% write init;
}

bool
HTTP::RequestParser::complete() const
{
    return cs >= http_request_parser_first_final;
}

bool
HTTP::RequestParser::error() const
{
    return cs == http_request_parser_error;
}

void
HTTP::RequestParser::exec()
{
    %% write exec;
}

%%{
    machine http_response_parser;
    include http_parser;
    include uri_parser "../uri.rl";

    action parse_Status_Code {
        m_response->status.status = (HTTP::Status)atoi(mark);
        mark = NULL;
    }

    action parse_Reason_Phrase {
        m_response->status.reason = std::string(mark, fpc - mark);
        mark = NULL;
    }

    action set_location {
        m_headerHandled = true;
    }

    Location = 'Location:' @set_location LWS* absolute_URI LWS*;

    response_header = Location;

    Status_Code = DIGIT{3} > mark %parse_Status_Code;
    Reason_Phrase = (TEXT -- (CR | LF))* >mark %parse_Reason_Phrase;
    Status_Line = HTTP_Version SP Status_Code SP Reason_Phrase CRLF;
    Response = Status_Line ((general_header | response_header | entity_header) CRLF)* CRLF @done;

    main := Response;

    write data;
}%%

HTTP::ResponseParser::ResponseParser(Response& response)
: m_response(&response),
  m_ver(&response.status.ver),
  m_uri(&response.response.location),
  m_path(&response.response.location.path),
  m_general(&response.general),
  m_entity(&response.entity)
{}

void
HTTP::ResponseParser::init()
{
    RagelParser::init();
    %% write init;
}

bool
HTTP::ResponseParser::complete() const
{
    return cs >= http_response_parser_first_final;
}

bool
HTTP::ResponseParser::error() const
{
    return cs == http_response_parser_error;
}

void
HTTP::ResponseParser::exec()
{
    %% write exec;
}

%%{
    machine http_trailer_parser;
    include http_parser;

    trailer = (entity_header CRLF)*;

    main := trailer CRLF @done;

    write data;
}%%

HTTP::TrailerParser::TrailerParser(EntityHeaders& entity)
: m_entity(&entity)
{}

void
HTTP::TrailerParser::init()
{
    RagelParser::init();
    %% write init;
}

bool
HTTP::TrailerParser::complete() const
{
    return cs >= http_trailer_parser_first_final;
}

bool
HTTP::TrailerParser::error() const
{
    return cs == http_trailer_parser_error;
}

void
HTTP::TrailerParser::exec()
{
    %% write exec;
}


Remove unnecessary states for newlines between messages

%%{
  machine message;

  # data, p, pe, eof, cs, top, stack, ts, te and act
  getkey data.getbyte(p); # code for retrieving current char

  ## Action state - needs resetting once consumed!
  action mark { m = p }
  action mark_key {
    mk = data.byteslice(m, p - m) # needs reset
    m = nil
  }
  action mark_message { message = Stomp::Message.new }

  ## Action commands - should reset used state!
  action write_command {
    message.write_command(data.byteslice(m, p - m))
    m = nil
  }

  action write_header {
    message.write_header(mk, data.byteslice(m, p - m))
    m = mk = nil
  }

  action write_body {
    message.write_body(data.byteslice(m, p - m))
    m = nil
  }

  action finish_headers {
  }

  action finish_message {
    yield message
    message = nil
  }

  ## Stomp message grammar

  NULL = "\0";
  EOL = "\r"? . "\n";
  OCTET = any;

  client_command = "CONNECT" > mark;
  command = client_command % write_command . EOL;

  HEADER_ESCAPE = "\\" . ("\\" | "n" | "r" | "c");
  HEADER_OCTET = HEADER_ESCAPE | (OCTET - "\r" - "\n" - "\\" - ":");
  header_key = HEADER_OCTET+ > mark % mark_key;
  header_value = HEADER_OCTET* > mark;
  header = header_key . ":" . header_value;
  headers = (header % write_header . EOL)* % finish_headers . EOL;

  dynamic_body = (OCTET* > mark) % write_body :> NULL;

  message = (command > mark_message) :> headers :> (dynamic_body @ finish_message);

  stream := (EOL | message)*;
}%%

module Stompede
  module Stomp
    # Provides an API for running parsers.
    #
    # It:
    # - provides a .parse method which remembers state between invocations
    # - buffering of data between chunks
    class Parser
      # this manipulates the singleton class of our context,
      # so we do not want to run this code very often or we
      # bust our ruby method caching
      %% write data noprefix;

      # Parse a chunk of Stomp-formatted data into a Message.
      #
      # @param [String] data
      # @param [Parser] state
      # @return [Stomp::Message, nil]
      def self.parse(data, state)
        pe = data.bytesize # end of chunk
        eof = :ignored # end of input

        p = 0 # pointer to current character
        message = state.message # message currently being parsed, if any
        cs = state.current_state # current state
        m = state.mark # pointer to marked character (for data buffering)
        mk = state.mark_key # key for header currently being read

        begin
          %% write exec;
        rescue => ex
          state.error = ex
        else
          state.cursor = p
          state.message = message
          state.current_state = cs
          state.mark = m
          state.mark_key = mk
        end

        nil
      end

      # Construct the parser.
      #
      # The buffer_size parameter determines how big the largest
      # chunk of data may become. This includes commands, header
      # keys, header values, and the body. If this size is reached,
      # the parser will throw a #{BufferLimitExceeded} error.
      #
      # The message_size parameter determines how big the largest
      # message may become. This includes all of the content in the
      # message being parsed. If this size is reached for an individual
      # message, the parser will throw a #{MessageSizeExceeded} error.
      #
      # @param [Integer] buffer_size (10K) maximum buffer size
      # @param [Integer] message_size (buffer_size) maximum message size
      def initialize(buffer_size = 1024 * 1024, message_size = buffer_size)
        @buffer_size = buffer_size
        @message_size = message_size
        @error = nil

        @cursor = 0
        @current_state = Stomp::Parser.start
        @message = nil
        @mark = nil
        @mark_key = nil
      end

      # @return [Integer] maximum buffer size for parsed values
      attr_reader :buffer_size

      # @return [Integer] maximum message size for parsed messages
      attr_reader :message_size

      # @return [StandardError] error raised during parsing
      attr_accessor :error

      # @return [Integer] index of parsing cursor in data
      attr_accessor :cursor

      # @return [Integer] current parsing state
      attr_accessor :current_state

      # @return [Stomp::Message] stomp message currently being parsed
      attr_accessor :message

      # @return [Integer] pointer to beginning of current buffer
      attr_accessor :mark

      # @return [String] header key currently being parsed
      attr_accessor :mark_key

      # Parse a chunk of data. Retains state beteween invocations.
      #
      # @param [String] data
      # @raise [ParseError]
      def parse(data)
        if error
          raise error
        else
          Parser.parse(data, self) do |message|
            yield message
          end
        end
      end
    end
  end
end

%%{
  machine message;

  # data, p, pe, eof, cs, top, stack, ts, te and act
  getkey data.getbyte(p); # code for retrieving current char

  ## Action state - needs resetting once consumed!
  action mark { m = p }
  action mark_key {
    mk = data.byteslice(m, p - m) # needs reset
    m = nil
  }
  action mark_message { message = Stomp::Message.new }

  ## Action commands - should reset used state!
  action write_command {
    message.write_command(data.byteslice(m, p - m))
    m = nil
  }

  action write_header {
    message.write_header(mk, data.byteslice(m, p - m))
    m = mk = nil
  }

  action write_body {
    message.write_body(data.byteslice(m, p - m))
    m = nil
  }

  action finish_headers {
  }

  action finish_message {
    yield message
    message = nil
  }

  ## Stomp message grammar

  NULL = "\0";
  EOL = "\r"? . "\n";
  OCTET = any;

  client_command = "CONNECT" > mark;
  command = client_command % write_command . EOL;

  HEADER_ESCAPE = "\\" . ("\\" | "n" | "r" | "c");
  HEADER_OCTET = HEADER_ESCAPE | (OCTET - "\r" - "\n" - "\\" - ":");
  header_key = HEADER_OCTET+ > mark % mark_key;
  header_value = HEADER_OCTET* > mark;
  header = header_key . ":" . header_value;
  headers = (header % write_header . EOL)* % finish_headers . EOL;

  dynamic_body = (OCTET* > mark) % write_body :> NULL;

  message = (command > mark_message) :> headers :> (dynamic_body @ finish_message);

  stream := (EOL* :> message :> EOL*)+;
}%%

module Stompede
  module Stomp
    # Provides an API for running parsers.
    #
    # It:
    # - provides a .parse method which remembers state between invocations
    # - buffering of data between chunks
    class Parser
      # this manipulates the singleton class of our context,
      # so we do not want to run this code very often or we
      # bust our ruby method caching
      %% write data noprefix;

      # Parse a chunk of Stomp-formatted data into a Message.
      #
      # @param [String] data
      # @param [Parser] state
      # @return [Stomp::Message, nil]
      def self.parse(data, state)
        pe = data.bytesize # end of chunk
        eof = :ignored # end of input

        p = 0 # pointer to current character
        message = state.message # message currently being parsed, if any
        cs = state.current_state # current state
        m = state.mark # pointer to marked character (for data buffering)
        mk = state.mark_key # key for header currently being read

        begin
          %% write exec;
        rescue => ex
          state.error = ex
        else
          state.cursor = p
          state.message = message
          state.current_state = cs
          state.mark = m
          state.mark_key = mk
        end

        nil
      end

      # Construct the parser.
      #
      # The buffer_size parameter determines how big the largest
      # chunk of data may become. This includes commands, header
      # keys, header values, and the body. If this size is reached,
      # the parser will throw a #{BufferLimitExceeded} error.
      #
      # The message_size parameter determines how big the largest
      # message may become. This includes all of the content in the
      # message being parsed. If this size is reached for an individual
      # message, the parser will throw a #{MessageSizeExceeded} error.
      #
      # @param [Integer] buffer_size (10K) maximum buffer size
      # @param [Integer] message_size (buffer_size) maximum message size
      def initialize(buffer_size = 1024 * 1024, message_size = buffer_size)
        @buffer_size = buffer_size
        @message_size = message_size
        @error = nil

        @cursor = 0
        @current_state = Stomp::Parser.start
        @message = nil
        @mark = nil
        @mark_key = nil
      end

      # @return [Integer] maximum buffer size for parsed values
      attr_reader :buffer_size

      # @return [Integer] maximum message size for parsed messages
      attr_reader :message_size

      # @return [StandardError] error raised during parsing
      attr_accessor :error

      # @return [Integer] index of parsing cursor in data
      attr_accessor :cursor

      # @return [Integer] current parsing state
      attr_accessor :current_state

      # @return [Stomp::Message] stomp message currently being parsed
      attr_accessor :message

      # @return [Integer] pointer to beginning of current buffer
      attr_accessor :mark

      # @return [String] header key currently being parsed
      attr_accessor :mark_key

      # Parse a chunk of data. Retains state beteween invocations.
      #
      # @param [String] data
      # @raise [ParseError]
      def parse(data)
        if error
          raise error
        else
          Parser.parse(data, self) do |message|
            yield message
          end
        end
      end
    end
  end
end