fix marking, don’t use ragel’s variables

#include <string.h>

#include "parser.h"
#include "parser_internal.h"

#define BUFSIZE 0x10000

// TODO:
// - error handling, also when string methods fail

unsigned int parse_hex(const unsigned char *src, unsigned int len) {
    unsigned int i = 0;
    for (unsigned int j = 0; j < len; j++) {
        i *= 16;
        char c = src[j];
        if (c >= '0' && c <= '9') {
            i += c - '0';
        } else if ((c >= 'A' && c <= 'F')
                   || (c >= 'A' && c <= 'F'))
        {
            i += c - 'A' + 10;
        } else if (c >= 'a' && c <= 'f') {
            i += c - 'a' +  10;
        }
    }
    return i;
}

void emit(entee_parser *parser, entee_token_type type) {
    if (!string_end(parser->string)) {
        parser->finished = 1;
    } else {
        parser->handler(type, parser->string->chars);
    }
}

%%{
    machine ntriples;

    alphtype unsigned char;

    action mark { mark = p; }

    action unescape {
        unsigned int codepoint = parse_hex(mark, p - mark);
        mark = 0;
        if (!string_add_codepoint(parser->string, codepoint)) {
            parser->finished = 1;
        }
    }

    action endTriple {
        // TODO: call handler?
    }

    action endLine {
        parser->line++;
    }

    action putChar {
        if (!string_add_checked(parser->string, *p))
            parser->finished = 1;
    }

    action startString {
        if (parser->string != NULL) {
            string_free(parser->string);
        }
        parser->string = string_new();
        if (parser->string == NULL) {
            parser->finished = 1;
        }
    }

    action endSimpleLiteral {
        emit(parser, ENTEE_SIMPLE_LITERAL_VALUE);
    }

    action datatypeLiteral {
        emit(parser, ENTEE_DATATYPE_LITERAL_VALUE);
    }

    action langtagLiteral {
        emit(parser, ENTEE_LANGUAGE_TAGGED_LITERAL_VALUE);
    }

    action endLangtagLiteral {
        emit(parser, ENTEE_LANGUAGE_TAGGED_LITERAL_LANGUAGE);
    }

    action endDatatypeLiteral {
        emit(parser, ENTEE_DATATYPE_LITERAL_IRI);
    }

    action endIRI {
        emit(parser, ENTEE_IRI);
    }

    action endBlankNode {
        emit(parser, ENTEE_BLANK_NODE);
    }

    WS = ' ' | '\t';
    EOL = '\r' | '\n';
    HEX = '0' .. '9' | 'A' .. 'F' | 'a' .. 'f';
    PN_CHARS_BASE = ('A' .. 'Z' | 'a' .. 'z'
                     # The N-Triple spec specifies Unicode codepoints,
                     # but files are encoded (and parsed) as UTF-8:
                     # 0x00C0 .. 0x00D6
                     | 0xC3 0x80..0x96
                     # 0x00D8 .. 0x00F6
                     | 0xC3 0x98..0xB6
                     # 0x00F8 .. 0x02FF
                     | 0xC3 0xB8..0xFF
                     | 0xC4..0xCA 0x00..0xFF
                     | 0xCB 0x00..0xBF
                     # 0x0370 .. 0x037D
                     | 0xCD 0xB0..0xBD
                     # 0x037F .. 0x1FFF = 0x037F .. 0x07FF | 0x0800 .. 0x1FFF
                     | 0xCD 0xBF..0xFF
                     | 0xCE..0xDE 0x00..0xFF
                     | 0xDF 0x00..0xBF
                     | 0xE0 0xA0 0x80..0xFF
                     | 0xE0 0xA1..0xFF 0x00..0xFF
                     | 0xE1 0x00 0x00..0xFF
                     | 0xE1 0x01..0xBE 0x00..0xFF
                     | 0xE1 0xBF 0x00..0xBF
                     # 0x200C .. 0x200D
                     | 0xE2 0x80 0x8C..0x8D
                     # 0x2070 .. 0x218F
                     | 0xE2 0x81 0xB0..0xFF
                     | 0xE2 0x82..0x85 0x00..0xFF
                     | 0xE2 0x86 0x00..0x8F
                     # 0x2C00 .. 0x2FEF
                     | 0xE2 0xB0 0x80..0xFF
                     | 0xE2 0xB1..0xBE 0x00..0xFF
                     | 0xE2 0xBF 0x00..0xAF
                     # 0x3001 .. 0xD7FF
                     | 0xE3 0x80 0x81..0xFF
                     | 0xE3 0x81..0xFF 0x00..0xFF
                     | 0xE4..0xEC 0x00..0xFF 0x00..0xFF
                     | 0xED 0x00 0x00..0xFF
                     | 0xED 0x01..0x9E 0x00..0xFF
                     | 0xED 0x9F 0x00..0xBF
                     # 0xF900 .. 0xFDCF
                     | 0xEF 0xA4 0x80..0xFF
                     | 0xEF 0xA5..0xB6 0x00..0xFF
                     | 0xEF 0xB7 0x00..0x8F
                     # 0xFDF0 .. 0xFFFD
                     | 0xEF 0xB7 0xB0..0xFF
                     | 0xEF 0xB8..0xBE 0x00..0xFF
                     | 0xEF 0xBF 0x00..0xBD
                     # 0x10000 .. 0xEFFFF
                     | 0xF0 0x90 0x80 0x80..0xFF
                     | 0xF0 0x90 0x81..0xFF 0x00..0xFF
                     | 0xF0 0x91..0xFF 0x00..0xFF 0x00..0xFF
                     | 0xF1..0xF2 0x00..0xFF 0x00..0xFF 0x00..0xFF
                     | 0xF3 0x00 0x00 0x00..0xFF
                     | 0xF3 0x00 0x01..0xFF 0x00..0xFF
                     | 0xF3 0x01..0xAE 0x00..0xFF 0x00..0xFF
                     | 0xF3 0xAF 0x00 0x00..0xFF
                     | 0xF3 0xAF 0x01..0xBE 0x00..0xFF
                     | 0xF3 0xAF 0xBF 0x00..0xBF
                     ) $putChar;
    PN_CHARS_U = PN_CHARS_BASE | (('_' | ':') $putChar);
    PN_CHARS = PN_CHARS_U
               | (('-' | '0' .. '9' | 0x00B7
                   # 0x0300 .. 0x036F
                   | 0xCC 0x80..0xFF
                   | 0xCD 0x00..0xAF
                   # 0x203F .. 0x2040
                   | 0xE2 0x80 0xBF..0xFF
                   | 0xE2 0x81 0x00..0x80)
                  $putChar);
    ECHAR = '\\' .
            (  ('t' ${ string_add_checked(parser->string, '\t'); })
             | ('b' ${ string_add_checked(parser->string, '\b'); })
             | ('n' ${ string_add_checked(parser->string, '\n'); })
             | ('r' ${ string_add_checked(parser->string, '\r'); })
             | ('f' ${ string_add_checked(parser->string, '\f'); })
             | ('"' ${ string_add_checked(parser->string, '\"'); })
             | ('\'' ${ string_add_checked(parser->string, '\''); })
             | ('\\' ${ string_add_checked(parser->string, '\\'); })
             );
    UCHAR = '\\' .
            (  ('u' HEX{4} >mark %unescape)
             | ('U' HEX{8} >mark %unescape));
    BLANK_NODE_LABEL = '_:' >startString (PN_CHARS_U | '0' .. '9' $putChar)
                       ((PN_CHARS | '.' $putChar)* PN_CHARS?);
    STRING_VALUE = (^(0x22 | 0x5C | 0xA | 0xD) $putChar | ECHAR | UCHAR)* >startString;
    IRIREF_VALUE = (^(0x00 .. 0x20 | '<' | '>' | '"' | '{' | '}' | '|' | '^' | '`' | '\\') $putChar | UCHAR)*
                   >startString;
    IRIREF = '<' IRIREF_VALUE '>';
    LANGTAG_VALUE = (('a' .. 'z' | 'A' .. 'Z') $putChar)+ >startString
                    ('-' $putChar (('a' .. 'z' | 'A' .. 'Z' | '0' .. '9') $putChar)+)*;
    datatypeLiteral = '"' STRING_VALUE '"' '^^' >datatypeLiteral IRIREF %endDatatypeLiteral;
    langtagLiteral =  '"' STRING_VALUE '"' '@' >langtagLiteral LANGTAG_VALUE %endLangtagLiteral;
    simpleLiteral = '"' STRING_VALUE '"' %endSimpleLiteral;
    literal = datatypeLiteral | langtagLiteral | simpleLiteral;
    subject = IRIREF %endIRI
            | BLANK_NODE_LABEL %endBlankNode;
    predicate = IRIREF %endIRI;
    object = literal
           | IRIREF %endIRI
           | BLANK_NODE_LABEL %endBlankNode;
    comment = '#' (any - EOL)*;
    # TODO: ensure left-guarded concatenation is correctly preventing
    #       ambiguity of blank nodes with dots
    triple = subject WS* predicate WS* object WS* <: '.' WS* %endTriple;
    line = WS* ( comment | triple )? (EOL %endLine)+;
    gobble_line := (any -- EOL)* EOL @{ fcall main; };
    main := line* $err{
        // failed on line arser->line
        fhold; fgoto gobble_line;
    };

    write data;
}%%

entee_parser *entee_new_parser() {
    entee_parser *parser = malloc(sizeof(entee_parser));
    parser->string = NULL;
    parser->line = 1;
    parser->finished = 0;
    parser->reader = NULL;
    parser->reader_arg = NULL;
    parser->handler = NULL;
    parser->handler_arg = NULL;
    return parser;
}

void entee_free_parser(entee_parser *parser) {
    free(parser);
}

void entee_parser_set_reader(entee_parser *parser,
                             entee_reader reader,
                             void *reader_arg)
{
    parser->reader = reader;
    parser->reader_arg = reader_arg;
}

void entee_parser_set_handler(entee_parser *parser,
                              entee_handler handler,
                              void *handler_arg)
{
    parser->handler = handler;
    parser->handler_arg = handler_arg;
}

int entee_parser_parse(entee_parser *parser) {
    if (parser->reader == NULL
        || parser->handler == NULL)
    {
        return 0;
    }

    int res = 1;
    int have = 0;
    int cs;
    int top;
    int stack[32];
    unsigned char buf[BUFSIZE];

    unsigned char *mark = 0;

    %% write init;

    entee_reader reader = parser->reader;

    while (!parser->finished) {
        unsigned char *p = buf + have;
        unsigned char *pe;
        unsigned char *eof = 0;

        int space = BUFSIZE - have;
        if (space == 0) {
            // out of buffer space
            res = 0;
            break;
        }

        int len = reader(p, space, parser->reader_arg);
        pe = p + len;

        if (len < space) {
            eof = pe;
            parser->finished = 1;
        }

        %% write exec;

        if (cs == ntriples_error) {
            res = 0;
            break;
        }

        if (mark == 0) {
            have = 0;
        } else {
            have = pe - mark;
            memmove(buf, mark, have);
            mark = buf;
        }
    }

    return res;
}

#include <string.h>

#include "parser.h"
#include "parser_internal.h"

#define BUFSIZE 0x100000

// TODO:
// - error handling, also when string methods fail

unsigned int parse_hex(const unsigned char *src, unsigned int len) {
    unsigned int i = 0;
    for (unsigned int j = 0; j < len; j++) {
        i *= 16;
        char c = src[j];
        if (c >= '0' && c <= '9') {
            i += c - '0';
        } else if ((c >= 'A' && c <= 'F')
                   || (c >= 'A' && c <= 'F'))
        {
            i += c - 'A' + 10;
        } else if (c >= 'a' && c <= 'f') {
            i += c - 'a' +  10;
        }
    }
    return i;
}

void emit(entee_parser *parser, entee_token_type type) {
    if (!string_end(parser->string)) {
        parser->finished = 1;
    } else {
        parser->handler(type, parser->string->chars);
    }
}

%%{
    machine ntriples;

    alphtype unsigned char;

    action startMark { ts = p; }

    action endMark { te = p; }

    action unescape {
        unsigned int codepoint = parse_hex(ts, te - ts);
        if (!string_add_codepoint(parser->string, codepoint))
            parser->finished = 1;
    }

    action endTriple {
        // TODO: call handler?
    }

    action endLine {
        parser->line++;
    }

    action putChar {
        if (!string_add_checked(parser->string, *p))
            parser->finished = 1;
    }

    action startString {
        if (parser->string != NULL) {
            string_free(parser->string);
        }
        parser->string = string_new();
        if (parser->string == NULL) {
            parser->finished = 1;
        }
    }

    action endSimpleLiteral {
        emit(parser, ENTEE_SIMPLE_LITERAL_VALUE);
    }

    action datatypeLiteral {
        emit(parser, ENTEE_DATATYPE_LITERAL_VALUE);
    }

    action langtagLiteral {
        emit(parser, ENTEE_LANGUAGE_TAGGED_LITERAL_VALUE);
    }

    action endLangtagLiteral {
        emit(parser, ENTEE_LANGUAGE_TAGGED_LITERAL_LANGUAGE);
    }

    action endDatatypeLiteral {
        emit(parser, ENTEE_DATATYPE_LITERAL_IRI);
    }

    action endIRI {
        emit(parser, ENTEE_IRI);
    }

    action endBlankNode {
        emit(parser, ENTEE_BLANK_NODE);
    }

    WS = ' ' | '\t';
    EOL = '\r' | '\n';
    HEX = '0' .. '9' | 'A' .. 'F' | 'a' .. 'f';
    PN_CHARS_BASE = ('A' .. 'Z' | 'a' .. 'z'
                     # The N-Triple spec specifies Unicode codepoints,
                     # but files are encoded (and parsed) as UTF-8:
                     # 0x00C0 .. 0x00D6
                     | 0xC3 0x80..0x96
                     # 0x00D8 .. 0x00F6
                     | 0xC3 0x98..0xB6
                     # 0x00F8 .. 0x02FF
                     | 0xC3 0xB8..0xFF
                     | 0xC4..0xCA 0x00..0xFF
                     | 0xCB 0x00..0xBF
                     # 0x0370 .. 0x037D
                     | 0xCD 0xB0..0xBD
                     # 0x037F .. 0x1FFF = 0x037F .. 0x07FF | 0x0800 .. 0x1FFF
                     | 0xCD 0xBF..0xFF
                     | 0xCE..0xDE 0x00..0xFF
                     | 0xDF 0x00..0xBF
                     | 0xE0 0xA0 0x80..0xFF
                     | 0xE0 0xA1..0xFF 0x00..0xFF
                     | 0xE1 0x00 0x00..0xFF
                     | 0xE1 0x01..0xBE 0x00..0xFF
                     | 0xE1 0xBF 0x00..0xBF
                     # 0x200C .. 0x200D
                     | 0xE2 0x80 0x8C..0x8D
                     # 0x2070 .. 0x218F
                     | 0xE2 0x81 0xB0..0xFF
                     | 0xE2 0x82..0x85 0x00..0xFF
                     | 0xE2 0x86 0x00..0x8F
                     # 0x2C00 .. 0x2FEF
                     | 0xE2 0xB0 0x80..0xFF
                     | 0xE2 0xB1..0xBE 0x00..0xFF
                     | 0xE2 0xBF 0x00..0xAF
                     # 0x3001 .. 0xD7FF
                     | 0xE3 0x80 0x81..0xFF
                     | 0xE3 0x81..0xFF 0x00..0xFF
                     | 0xE4..0xEC 0x00..0xFF 0x00..0xFF
                     | 0xED 0x00 0x00..0xFF
                     | 0xED 0x01..0x9E 0x00..0xFF
                     | 0xED 0x9F 0x00..0xBF
                     # 0xF900 .. 0xFDCF
                     | 0xEF 0xA4 0x80..0xFF
                     | 0xEF 0xA5..0xB6 0x00..0xFF
                     | 0xEF 0xB7 0x00..0x8F
                     # 0xFDF0 .. 0xFFFD
                     | 0xEF 0xB7 0xB0..0xFF
                     | 0xEF 0xB8..0xBE 0x00..0xFF
                     | 0xEF 0xBF 0x00..0xBD
                     # 0x10000 .. 0xEFFFF
                     | 0xF0 0x90 0x80 0x80..0xFF
                     | 0xF0 0x90 0x81..0xFF 0x00..0xFF
                     | 0xF0 0x91..0xFF 0x00..0xFF 0x00..0xFF
                     | 0xF1..0xF2 0x00..0xFF 0x00..0xFF 0x00..0xFF
                     | 0xF3 0x00 0x00 0x00..0xFF
                     | 0xF3 0x00 0x01..0xFF 0x00..0xFF
                     | 0xF3 0x01..0xAE 0x00..0xFF 0x00..0xFF
                     | 0xF3 0xAF 0x00 0x00..0xFF
                     | 0xF3 0xAF 0x01..0xBE 0x00..0xFF
                     | 0xF3 0xAF 0xBF 0x00..0xBF
                     ) $putChar;
    PN_CHARS_U = PN_CHARS_BASE | (('_' | ':') $putChar);
    PN_CHARS = PN_CHARS_U
               | (('-' | '0' .. '9' | 0x00B7
                   # 0x0300 .. 0x036F
                   | 0xCC 0x80..0xFF
                   | 0xCD 0x00..0xAF
                   # 0x203F .. 0x2040
                   | 0xE2 0x80 0xBF..0xFF
                   | 0xE2 0x81 0x00..0x80)
                  $putChar);
    ECHAR = '\\' .
            (  ('t' ${ string_add_checked(parser->string, '\t'); })
             | ('b' ${ string_add_checked(parser->string, '\b'); })
             | ('n' ${ string_add_checked(parser->string, '\n'); })
             | ('r' ${ string_add_checked(parser->string, '\r'); })
             | ('f' ${ string_add_checked(parser->string, '\f'); })
             | ('"' ${ string_add_checked(parser->string, '\"'); })
             | ('\'' ${ string_add_checked(parser->string, '\''); })
             | ('\\' ${ string_add_checked(parser->string, '\\'); })
             );
    UCHAR = '\\' .
            (  ('u' HEX{4} >startMark %endMark %unescape)
             | ('U' HEX{8} >startMark %endMark %unescape));
    BLANK_NODE_LABEL = '_:' >startString (PN_CHARS_U | '0' .. '9' $putChar)
                       ((PN_CHARS | '.' $putChar)* PN_CHARS?);
    STRING_VALUE = (^(0x22 | 0x5C | 0xA | 0xD) $putChar | ECHAR | UCHAR)* >startString;
    IRIREF_VALUE = (^(0x00 .. 0x20 | '<' | '>' | '"' | '{' | '}' | '|' | '^' | '`' | '\\') $putChar | UCHAR)*
                   >startString;
    IRIREF = '<' IRIREF_VALUE '>';
    LANGTAG_VALUE = (('a' .. 'z' | 'A' .. 'Z') $putChar)+ >startString
                    ('-' $putChar (('a' .. 'z' | 'A' .. 'Z' | '0' .. '9') $putChar)+)*;
    datatypeLiteral = '"' STRING_VALUE '"' '^^' >datatypeLiteral IRIREF %endDatatypeLiteral;
    langtagLiteral =  '"' STRING_VALUE '"' '@' >langtagLiteral LANGTAG_VALUE %endLangtagLiteral;
    simpleLiteral = '"' STRING_VALUE '"' %endSimpleLiteral;
    literal = datatypeLiteral | langtagLiteral | simpleLiteral;
    subject = IRIREF %endIRI
            | BLANK_NODE_LABEL %endBlankNode;
    predicate = IRIREF %endIRI;
    object = literal
           | IRIREF %endIRI
           | BLANK_NODE_LABEL %endBlankNode;
    comment = '#' (any - EOL)*;
    # TODO: ensure left-guarded concatenation is correctly preventing
    #       ambiguity of blank nodes with dots
    triple = subject WS* predicate WS* object WS* <: '.' WS* %endTriple;
    line = WS* ( comment | triple )? (EOL %endLine)+;
    gobble_line := (any -- EOL)* EOL @{ fcall main; };
    main := line* $err{
        // failed on line arser->line
        fhold; fgoto gobble_line;
    };

    write data;
}%%

entee_parser *entee_new_parser() {
    entee_parser *parser = malloc(sizeof(entee_parser));
    parser->string = NULL;
    parser->line = 1;
    parser->finished = 0;
    parser->reader = NULL;
    parser->reader_arg = NULL;
    parser->handler = NULL;
    parser->handler_arg = NULL;
    return parser;
}

void entee_free_parser(entee_parser *parser) {
    free(parser);
}

void entee_parser_set_reader(entee_parser *parser,
                             entee_reader reader,
                             void *reader_arg)
{
    parser->reader = reader;
    parser->reader_arg = reader_arg;
}

void entee_parser_set_handler(entee_parser *parser,
                              entee_handler handler,
                              void *handler_arg)
{
    parser->handler = handler;
    parser->handler_arg = handler_arg;
}

int entee_parser_parse(entee_parser *parser) {
    if (parser->reader == NULL
        || parser->handler == NULL)
    {
        return 0;
    }

    int res = 1;
    int have = 0;
    int cs;
    int top;
    int stack[32];
    unsigned char *ts = 0;
    unsigned char *te = 0;
    unsigned char buf[BUFSIZE];

    %% write init;

    entee_reader reader = parser->reader;

    while (!parser->finished) {
        unsigned char *p = buf + have;
        unsigned char *pe;
        unsigned char *eof = 0;

        int space = BUFSIZE - have;
        if (space == 0) {
            // out of buffer space
            res = 0;
            break;
        }

        int len = reader(p, space, parser->reader_arg);
        pe = p + len;

        if (len < space) {
            eof = pe;
            parser->finished = 1;
        }

        %% write exec;

        if (cs == ntriples_error) {
            res = 0;
            break;
        }

        if (ts == 0)
            have = 0;
        else {
            have = pe - ts;
            memmove(buf, ts, have);
            te = buf + (te - ts);
            ts = buf;
        }

    }

    return res;
}


comments

comments

package dns

// Parse RRs
// With the thankful help of gdnsd and the Go examples for Ragel.

import (
    "os"
    "io"
    "net"
    "time"
    "strings"
    "strconv"
)

const _IOBUF = MaxMsgSize

// A Parser represents a DNS parser for a
// particular input stream. Each parsed RR will be returned
// on the channel RR.
type Parser struct {
        // nothing here yet
        buf    []byte
        RR     chan RR
        Error  chan *ParseError
}

type ParseError struct {
        Error string
        name  string
        line  int
}

func (e *ParseError) String() string {
        s := e.Error + ": \""  + e.name + "\" at line: " + strconv.Itoa(e.line)
        return s
}

// NewParser creates a new DNS file parser from r.
// Need sliding window stuff TODO.
func NewParser(r io.Reader) *Parser {
        buf := make([]byte, _IOBUF)
        n, err := r.Read(buf)
        if err != nil {
            return nil
        }
        if buf[n-1] != '\n' {
            buf[n] = '\n'
            n++
        }
        buf = buf[:n]
        p := new(Parser)
        p.buf = buf
        p.RR = make(chan RR)
        p.Error = make(chan ParseError)
        return p
}

// Translate the RRSIG's incep. and expir. times from
// string values ("20110403154150") to an integer.
// Taking into account serial arithmetic (RFC 1982)
func dateToTime(s string) (uint32, os.Error) {
    t, e := time.Parse("20060102150405", s)
    if e != nil {
        return 0, e
    }
    mod := t.Seconds() / Year68
    ti := uint32(t.Seconds() - (mod * Year68))
    return ti, nil
}

// Return the rdata fields as a string slice.
// All starting whitespace is deleted.
// If i is 0 no spaces are deleted from the final rdfs.
func fields(s string, i int) (rdf []string) {
    rdf = strings.Fields(s)
    for i, _ := range rdf {
        rdf[i] = strings.TrimSpace(rdf[i])
    }
    if i > 0 && len(rdf) > i {
        // The last rdf contained embedded spaces, glue it back together.
        for j := i; j < len(rdf); j++ {
            rdf[i-1] += rdf[j]
        }
    }
    return
}

%%{
        machine z;
        write data;
}%%

// First will return the first RR found when parsing.
func (zp *Parser) First() (RR, os.Error) {
    // defer close something
    go run(zp, quit)
    select {
    case r := <-zp.RR:
        return r, nil
    case e := <-zp.Error:
        return nil, e
    }
}

// Run starts the parsers and returns the parsed Rr on the RR channel.
// Errors are return on the Error channel. After an error the parsing stops.
func (zp *Parser) Run(quit chan bool) {
    go run(zp, quit)
}

// Run parses an DNS master zone file. It returns each parsed RR
// on the channel as soon as it has been parsed.
func run(zp *Parser, quit chan bool) (err os.Error) {
        data := string(zp.buf)
        cs, p, pe := 0, 0, len(data)
        eof := len(data)

        defer close(zp.Error)
        defer close(zp.RR)

//        brace := false
        l := 1  // or... 0?
        mark := 0
        var hdr RR_Header
        // Need to listen to the quit channel, do this in the mark action, or something?

        %%{

                action mark       { mark = p }
                action lineCount  { l++ }
                action setQname   { if ! IsDomainName(data[mark:p]) {
                                            zp.Error <- &ParseError{Error: "bad qname: " + data[mark:p], line: l}
                                            return
                                    }
                                    hdr.Name = data[mark:p]
                                  }
                action errQclass  { zp.Error <- &ParseError{Error: "bad qclass: " + data[mark:p], line: l}; return }
                action setQclass  { hdr.Class = str_class[data[mark:p]] }
                action defTtl     { /* ... */ }
                action errTtl     { /* ... */ }
                action setTtl     { i, _ := strconv.Atoui(data[mark:p]); hdr.Ttl = uint32(i) }
#                action openBrace  { if brace { println("Brace already open")} ; brace = true }
#                action closeBrace { if !brace { println("Brace already closed")}; brace = false }
#                action brace      { brace }

                include "types.rl";

                nl  = [\n]+ $lineCount;
                comment = ';' [^\n]*;
                ttl = digit+ >mark; #@err(errTtl)
#                bl  = ( [ \t]+
#                    | '(' $openBrace
#                    | ')' $closeBrace
#                    | (comment? nl)+ when brace
#                )+;
                bl = [ \t]+;

                rdata = [^\n]+ >mark;
                qname  = [a-zA-Z0-9.\-_*]+ >mark %setQname;
                qclass = ('IN'i|'CH'i|'HS'i) >mark %setQclass; # @err(errQclass);

                lhs = qname? bl %defTtl (
                      (ttl %setTtl bl (qclass bl)?)
                    | (qclass bl (ttl %setTtl bl)?)
                )?;

                rhs = (
                      ( 'A'i        bl rdata ) %setA
                    | ( 'PTR'i      bl rdata ) %setPTR
                    | ( 'TXT'i      bl rdata ) %setTXT
                    | ( 'SRV'i      bl rdata ) %setSRV
                    | ( 'CERT'i     bl rdata ) %setCERT
                    | ( 'NAPTR'i    bl rdata ) %setNAPTR
                    | ( 'AAAA'i     bl rdata ) %setAAAA
                    | ( 'SOA'i      bl rdata ) %setSOA
                    | ( 'CNAME'i    bl rdata ) %setCNAME
                    | ( 'DNAME'i    bl rdata ) %setDNAME
                    | ( 'NS'i       bl rdata ) %setNS
                    | ( 'MX'i       bl rdata ) %setMX
                    | ( 'DS'i       bl rdata ) %setDS
                    | ( 'DLV'i      bl rdata ) %setDLV
                    | ( 'TA'i       bl rdata ) %setTA
                    | ( 'DNSKEY'i   bl rdata ) %setDNSKEY
                    | ( 'RRSIG'i    bl rdata ) %setRRSIG
                    | ( 'NSEC'i     bl rdata ) %setNSEC
                    | ( 'NSEC3'i    bl rdata ) %setNSEC3
                    | ( 'SSHFP'i    bl rdata ) %setSSHFP
                    | ( 'NSEC3PARAM'i bl rdata ) %setNSEC3PARAM
                );

                rr = lhs rhs;
#                main := (rr? bl? ((comment? nl) when !brace))*;
                main := ((rr?|comment?) nl)*;

                write init;
                write exec;
        }%%

        if eof > -1 {
                if cs < z_first_final {
                        // No clue what I'm doing what so ever
                        if p == pe {
                                println("p", p, "pe", pe)
                                println("cs", cs, "z_first_final", z_first_final)
                                println("unexpected eof at line ", l)
                                return
                        } else {
                                println("error at position ", p, "\"",data[mark:p],"\" at line ", l)
                                return
                        }
                }
        }
        return
}

package dns

// Parse RRs
// With the thankful help of gdnsd and the Go examples for Ragel.

import (
    "os"
    "io"
    "net"
    "time"
    "strings"
    "strconv"
)

const _IOBUF = MaxMsgSize

// A Parser represents a DNS parser for a
// particular input stream. Each parsed RR will be returned
// on the channel RR.
type Parser struct {
        // nothing here yet
        buf    []byte
        RR     chan RR
        Error  chan *ParseError
}

type ParseError struct {
        Error string
        name  string
        line  int
}

func (e *ParseError) String() string {
        s := e.Error + ": \""  + e.name + "\" at line: " + strconv.Itoa(e.line)
        return s
}

// NewParser creates a new DNS file parser from r.
// Need sliding window stuff TODO.
func NewParser(r io.Reader) *Parser {
        buf := make([]byte, _IOBUF)
        n, err := r.Read(buf)
        if err != nil {
            return nil
        }
        if buf[n-1] != '\n' {
            buf[n] = '\n'
            n++
        }
        buf = buf[:n]
        p := new(Parser)
        p.buf = buf
        p.RR = make(chan RR)
        p.Error = make(chan ParseError)
        return p
}

// Translate the RRSIG's incep. and expir. times from
// string values ("20110403154150") to an integer.
// Taking into account serial arithmetic (RFC 1982)
func dateToTime(s string) (uint32, os.Error) {
    t, e := time.Parse("20060102150405", s)
    if e != nil {
        return 0, e
    }
    mod := t.Seconds() / Year68
    ti := uint32(t.Seconds() - (mod * Year68))
    return ti, nil
}

// Return the rdata fields as a string slice.
// All starting whitespace is deleted.
// If i is 0 no spaces are deleted from the final rdfs.
func fields(s string, i int) (rdf []string) {
    rdf = strings.Fields(s)
    for i, _ := range rdf {
        rdf[i] = strings.TrimSpace(rdf[i])
    }
    if i > 0 && len(rdf) > i {
        // The last rdf contained embedded spaces, glue it back together.
        for j := i; j < len(rdf); j++ {
            rdf[i-1] += rdf[j]
        }
    }
    return
}

%%{
        machine z;
        write data;
}%%

// First will return the first RR found when parsing.
func (zp *Parser) First() (RR, os.Error) {
    // defer close something
    go run(zp, quit)
    select {
    case r := <-zp.RR:
        return r, nil
    case e := <-zp.Error:
        return nil, e
    }
}

// Run starts the parsers and returns the parsed Rr on the RR channel.
// Errors are return on the Error channel. After an error the parsing stops.
func (zp *Parser) Run(quit chan bool) {
    go run(zp, quit)
}

// Run parses an DNS master zone file. It returns each parsed RR
// on the channel as soon as it has been parsed.
func run(zp *Parser, quit chan bool) (err os.Error) {
        data := string(zp.buf)
        cs, p, pe := 0, 0, len(data)
        eof := len(data)

        defer close(zp.Error)
        defer close(zp.RR)

//        brace := false
        l := 1  // or... 0?
        mark := 0
        var hdr RR_Header
        // Need to listen to the quit channel

        %%{

                action mark       { mark = p }
                action lineCount  { l++ }
                action setQname   { if ! IsDomainName(data[mark:p]) {
                                            zp.Error <- &ParseError{Error: "bad qname: " + data[mark:p], line: l}
                                            return
                                    }
                                    hdr.Name = data[mark:p]
                                  }
                action errQclass  { zp.Error <- &ParseError{Error: "bad qclass: " + data[mark:p], line: l}; return }
                action setQclass  { hdr.Class = str_class[data[mark:p]] }
                action defTtl     { /* ... */ }
                action errTtl     { /* ... */ }
                action setTtl     { i, _ := strconv.Atoui(data[mark:p]); hdr.Ttl = uint32(i) }
#                action openBrace  { if brace { println("Brace already open")} ; brace = true }
#                action closeBrace { if !brace { println("Brace already closed")}; brace = false }
#                action brace      { brace }

                include "types.rl";

                nl  = [\n]+ $lineCount;
                comment = ';' [^\n]*;
                ttl = digit+ >mark; #@err(errTtl)
#                bl  = ( [ \t]+
#                    | '(' $openBrace
#                    | ')' $closeBrace
#                    | (comment? nl)+ when brace
#                )+;
                bl = [ \t]+;

                rdata = [^\n]+ >mark;
                qname  = [a-zA-Z0-9.\-_*]+ >mark %setQname;
                qclass = ('IN'i|'CH'i|'HS'i) >mark %setQclass; # @err(errQclass);

                lhs = qname? bl %defTtl (
                      (ttl %setTtl bl (qclass bl)?)
                    | (qclass bl (ttl %setTtl bl)?)
                )?;

                rhs = (
                      ( 'A'i        bl rdata ) %setA
                    | ( 'PTR'i      bl rdata ) %setPTR
                    | ( 'TXT'i      bl rdata ) %setTXT
                    | ( 'SRV'i      bl rdata ) %setSRV
                    | ( 'CERT'i     bl rdata ) %setCERT
                    | ( 'NAPTR'i    bl rdata ) %setNAPTR
                    | ( 'AAAA'i     bl rdata ) %setAAAA
                    | ( 'SOA'i      bl rdata ) %setSOA
                    | ( 'CNAME'i    bl rdata ) %setCNAME
                    | ( 'DNAME'i    bl rdata ) %setDNAME
                    | ( 'NS'i       bl rdata ) %setNS
                    | ( 'MX'i       bl rdata ) %setMX
                    | ( 'DS'i       bl rdata ) %setDS
                    | ( 'DLV'i      bl rdata ) %setDLV
                    | ( 'TA'i       bl rdata ) %setTA
                    | ( 'DNSKEY'i   bl rdata ) %setDNSKEY
                    | ( 'RRSIG'i    bl rdata ) %setRRSIG
                    | ( 'NSEC'i     bl rdata ) %setNSEC
                    | ( 'NSEC3'i    bl rdata ) %setNSEC3
                    | ( 'SSHFP'i    bl rdata ) %setSSHFP
                    | ( 'NSEC3PARAM'i bl rdata ) %setNSEC3PARAM
                );

                rr = lhs rhs;
#                main := (rr? bl? ((comment? nl) when !brace))*;
                main := ((rr?|comment?) nl)*;

                write init;
                write exec;
        }%%

        if eof > -1 {
                if cs < z_first_final {
                        // No clue what I'm doing what so ever
                        if p == pe {
                                println("p", p, "pe", pe)
                                println("cs", cs, "z_first_final", z_first_final)
                                println("unexpected eof at line ", l)
                                return
                        } else {
                                println("error at position ", p, "\"",data[mark:p],"\" at line ", l)
                                return
                        }
                }
        }
        return
}


More operators

More operators

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

/* debug */
#define TOKENV(name, v, len) \
  memset(sbuf, 0, 1024); \
  strncpy(sbuf, v, len); \
  printf("<%s:%s> ", name, sbuf)
#define TOKEN(name) \
  printf("<%s> ", name)

#define MAX_INDENT     30
#define INDENT_PUSH(i) (assert(pind < MAX_INDENT-1), inds[++pind] = i)
#define INDENT_POP()   inds[--pind]

%%{
  machine min;

  newline     = "\r"? "\n" %{ printf(" (line %d)\n", curline); curline++; };
  whitespace  = " " | "\f" | "\t" | "\v";

  indent      = newline whitespace+ newline?;
  term        = (newline | ".");
  id          = [a-z]+;
  int         = [0-9]+;
  string      = '"' (any - '"')* '"' | "'" (any - "'")* "'";
  comment     = "#"+ (any - newline)* newline;

  main := |*

    # indentation magic
    indent      => {
      if (ts[0] == '\r') ts++;
      if (ts[0] == '\n') ts++;

      if (te-ts > ind) {
        ind = te-ts;
        INDENT_PUSH(ind);
        TOKEN("term");
        TOKEN("indent");
      } else if (te-ts < ind) {
        ind = te-ts;
        INDENT_POP();
        TOKEN("dedent");
        TOKEN("term");
      } else {
        TOKEN("term");
      }
    };
    newline     => {
      while(pind > 0) {
        INDENT_POP();
        TOKEN("dedent");
      }
      ind = 0;
      TOKEN("term");
    };

    whitespace;
    comment;

    # literals
    id          => { TOKENV("id", ts, te-ts); };
    int         => { TOKENV("int", ts, te-ts); };
    string      => { TOKENV("string", ts+1, te-ts-2); };
    term        => { TOKEN("term"); };

    # ponctuation
    ","         => { TOKEN("comma"); };
    ":"         => { TOKEN("colon"); };
    "("         => { TOKEN("o_par"); };
    ")"         => { TOKEN("c_par"); };
    "{"         => { TOKEN("o_bra"); };
    "}"         => { TOKEN("c_bra"); };
    "["         => { TOKEN("o_sqbra"); };
    "]"         => { TOKEN("c_sqbra"); };

    # assign
    "="         => { TOKEN("assign"); };
    "+="        => { TOKEN("assign_plus"); };
    "-="        => { TOKEN("assign_minus"); };

    # operators
    "=="        => { TOKEN("eq"); };
    "!="        => { TOKEN("neq"); };
    "!"         => { TOKEN("not"); };
    "||"        => { TOKEN("or"); };
    "&&"        => { TOKEN("and"); };
    "<"         => { TOKEN("lt"); };
    "<="        => { TOKEN("lte"); };
    ">"         => { TOKEN("gt"); };
    ">="        => { TOKEN("gte"); };
    "<<"        => { TOKEN("lsh"); };
    ">>"        => { TOKEN("rsh"); };
    "*"         => { TOKEN("mult"); };
    "**"        => { TOKEN("pow"); };
    "/"         => { TOKEN("div"); };
    "%"         => { TOKEN("mod"); };
    "+"         => { TOKEN("plus"); };
    "-"         => { TOKEN("minus"); };
    "@"         => { TOKEN("at"); };
    "@@"        => { TOKEN("atat"); };
    "?"         => { TOKEN("ques"); };
    ".."        => { TOKEN("to"); };
  *|;

  write data nofinal;
}%%

void min_parse(char *code) {
  int cs, act;
  char *p, *pe, *ts, *te, *eof = 0;
  int curline = 1;
  int ind = 0;
  int inds[MAX_INDENT]; /* max indent level */
  int pind = 0;
  char sbuf[1024]; /* debug */

  inds[0] = 0;
  p = code;
  pe = p + strlen(code) + 1;

  %% write init;
  %% write exec;

  /* close all open indent on stack */
  while(pind > 0) {
    INDENT_POP();
    TOKEN("dedent");
  }
}

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

/* debug */
#define TOKENV(name, v, len) \
  memset(sbuf, 0, 1024); \
  strncpy(sbuf, v, len); \
  printf("<%s:%s> ", name, sbuf)
#define TOKEN(name) \
  printf("<%s> ", name)

#define MAX_INDENT     30
#define INDENT_PUSH(i) (assert(pind < MAX_INDENT-1), inds[++pind] = i)
#define INDENT_POP()   inds[--pind]

%%{
  machine min;

  newline     = "\r"? "\n" %{ printf(" (line %d)\n", curline); curline++; };
  whitespace  = " " | "\f" | "\t" | "\v";

  indent      = newline whitespace+ newline?;
  term        = (newline | ".");
  id          = [a-z]+;
  int         = [0-9]+;
  string      = '"' (any - '"')* '"' | "'" (any - "'")* "'";
  comment     = "#"+ (any - newline)* newline;

  main := |*

    # indentation magic
    indent      => {
      if (ts[0] == '\r') ts++;
      if (ts[0] == '\n') ts++;

      if (te-ts > ind) {
        ind = te-ts;
        INDENT_PUSH(ind);
        TOKEN("term");
        TOKEN("indent");
      } else if (te-ts < ind) {
        ind = te-ts;
        INDENT_POP();
        TOKEN("dedent");
        TOKEN("term");
      } else {
        TOKEN("term");
      }
    };
    newline     => {
      while(pind > 0) {
        INDENT_POP();
        TOKEN("dedent");
      }
      ind = 0;
      TOKEN("term");
    };

    whitespace;
    comment;

    # literals
    id          => { TOKENV("id", ts, te-ts); };
    int         => { TOKENV("int", ts, te-ts); };
    string      => { TOKENV("string", ts+1, te-ts-2); };
    term        => { TOKEN("term"); };

    # ponctuation
    ","         => { TOKEN("comma"); };
    ":"         => { TOKEN("colon"); };
    "("         => { TOKEN("o_par"); };
    ")"         => { TOKEN("c_par"); };
    "{"         => { TOKEN("o_bra"); };
    "}"         => { TOKEN("c_bra"); };
    "["         => { TOKEN("o_sqbra"); };
    "]"         => { TOKEN("c_sqbra"); };

    # assign
    "="         => { TOKEN("assign"); };
    "+="        => { TOKEN("assign_plus"); };
    "-="        => { TOKEN("assign_minus"); };

    # operators
    "=="        => { TOKEN("eq"); };
    "!="        => { TOKEN("neq"); };
    "||"        => { TOKEN("or"); };
    "&&"        => { TOKEN("and"); };
    "<"         => { TOKEN("lt"); };
    "<="        => { TOKEN("lte"); };
    ">"         => { TOKEN("gt"); };
    ">="        => { TOKEN("gte"); };
    "<<"        => { TOKEN("lsh"); };
    ">>"        => { TOKEN("rsh"); };
    "*"         => { TOKEN("mult"); };
    "**"        => { TOKEN("pow"); };
    "/"         => { TOKEN("div"); };
    "%"         => { TOKEN("mod"); };
    "+"         => { TOKEN("plus"); };
    "-"         => { TOKEN("minus"); };
    "@"         => { TOKEN("at"); };
    "@@"         => { TOKEN("atat"); };
  *|;

  write data nofinal;
}%%

void min_parse(char *code) {
  int cs, act;
  char *p, *pe, *ts, *te, *eof = 0;
  int curline = 1;
  int ind = 0;
  int inds[MAX_INDENT]; /* max indent level */
  int pind = 0;
  char sbuf[1024]; /* debug */

  inds[0] = 0;
  p = code;
  pe = p + strlen(code) + 1;

  %% write init;
  %% write exec;

  /* close all open indent on stack */
  while(pind > 0) {
    INDENT_POP();
    TOKEN("dedent");
  }
}


add '->' (increases) abbreviation

add '->' (increases) abbreviation

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "bel-ast.h"
#include "bel-node-stack.h"

/*
 * Value size: The maximum number of characters to hold in an
 * accumulated value.
 */
#define BEL_VALUE_CHAR_LEN 512

/*
 * Term stack size: A value of 20 allows for a nesting of
 * a max of 20 terms.
 */
#define TERM_STACK_SIZE 20

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

bel_ast* bel_parse_statement(char* line) {
    int             cs;
    char            *p;
    char            *pe;
    int             top;
    int             *stack;
    bel_ast_node*   statement;
    bel_ast_node*   subject;
    bel_ast_node*   rel;
    bel_ast_node*   object;
    bel_ast_node*   term;
    bel_ast_node*   current_nv;
    bel_ast_node*   arg;
    bel_ast*        ast;
    bel_node_stack* term_stack;
    char            *function;
    char            *relationship;
    char            *value;
    int             fi;
    int             ri;
    int             vi;

    p            = line;
    pe           = line + strlen(line);
    top          = 0;
    stack        = malloc(sizeof(int) * TERM_STACK_SIZE);
    current_nv   = NULL;
    function     = malloc(sizeof(char) * BEL_VALUE_CHAR_LEN);
    relationship = malloc(sizeof(char) * BEL_VALUE_CHAR_LEN);
    value        = malloc(sizeof(char) * BEL_VALUE_CHAR_LEN);
    fi           = 0;
    ri           = 0;
    vi           = 0;

    term_stack   = stack_init(TERM_STACK_SIZE);
    statement    = bel_new_ast_node_token(BEL_TOKEN_STATEMENT);
    subject      = bel_new_ast_node_token(BEL_TOKEN_SUBJECT);
    rel          = bel_new_ast_node_token(BEL_TOKEN_REL);
    object       = bel_new_ast_node_token(BEL_TOKEN_OBJECT);
    term         = NULL;
    ast          = bel_new_ast();
    ast->root    = statement;

    memset(function, '\0', BEL_VALUE_CHAR_LEN);
    memset(relationship, '\0', BEL_VALUE_CHAR_LEN);
    memset(value, '\0', BEL_VALUE_CHAR_LEN);

    %%{
        action fxc {
            fi = 0;
        }

        action relc {
            ri = 0;
        }

        action valc {
            vi = 0;
        }

        action fxn {
            function[fi++] = fc;
        }

        action reln {
            relationship[ri++] = fc;
        }

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

        action FX {
            term               = stack_peek(term_stack);
            term->token->left  = bel_new_ast_node_value(BEL_VALUE_FX, function);
            term->token->right = bel_new_ast_node_token(BEL_TOKEN_ARG);

            memset(function, '\0', BEL_VALUE_CHAR_LEN);
            fi = 0;
        }

        action REL {
            rel->token->left = bel_new_ast_node_value(BEL_VALUE_REL, relationship);
            object->token->left = rel;

            memset(relationship, '\0', BEL_VALUE_CHAR_LEN);
            ri = 0;
        }

        action NESTED_FX {
            bel_ast_node* term_top = stack_peek(term_stack);

            // find ARG leaf
            arg = term_top->token->right;
            while(arg->token->right != NULL) {
                arg = arg->token->right;
            }

            // create new nested term
            term               = bel_new_ast_node_token(BEL_TOKEN_TERM);
            term->token->left  = bel_new_ast_node_value(BEL_VALUE_FX, function);
            term->token->right = bel_new_ast_node_token(BEL_TOKEN_ARG);
            // set head term, left: new nested term, right: next arg
            arg->token->left   = term;
            arg->token->right  = bel_new_ast_node_token(BEL_TOKEN_ARG);

            // push new nested term onto stack
            stack_push(term_stack, term);

            memset(function, '\0', BEL_VALUE_CHAR_LEN);
            memset(value, '\0', BEL_VALUE_CHAR_LEN);
            fi = 0;
        }

        action PFX {
            term = stack_peek(term_stack);

            // find ARG leaf
            arg = term->token->right;
            while(arg->token->right != NULL) {
                arg = arg->token->right;
            }

            current_nv               = bel_new_ast_node_token(BEL_TOKEN_NV);
            current_nv->token->left  = bel_new_ast_node_value(BEL_VALUE_PFX, value);
            current_nv->token->right = bel_new_ast_node_value(BEL_VALUE_VAL, NULL);
            arg->token->left         = current_nv;
            arg->token->right        = bel_new_ast_node_token(BEL_TOKEN_ARG);

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

        action VAL {
            if (!current_nv) {
                term = stack_peek(term_stack);

                // find ARG leaf
                arg = term->token->right;
                while(arg->token->right != NULL) {
                    arg = arg->token->right;
                }

                current_nv               = bel_new_ast_node_token(BEL_TOKEN_NV);
                current_nv->token->left  = bel_new_ast_node_value(BEL_VALUE_PFX, NULL);
                current_nv->token->right = bel_new_ast_node_value(BEL_VALUE_VAL, value);
                arg->token->left         = current_nv;
                arg->token->right        = bel_new_ast_node_token(BEL_TOKEN_ARG);
            } else {
                current_nv->token->right = bel_new_ast_node_value(BEL_VALUE_VAL, value);
            }

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

        action CALL_ARGUMENTS {
            fcall arguments;
        }

        action RET_ARGUMENTS {
            term = stack_pop(term_stack);
            fret;
        }

        action CALL_TERM {
            fhold;
            term = bel_new_ast_node_token(BEL_TOKEN_TERM);
            term_stack->top = -1;
            stack_push(term_stack, term);
            fcall term;
        }

        action RET_TERM {
            if (statement->token->left == NULL) {
                // statement left token (e.g. subject) is available
                subject->token->left = bel_copy_ast_node(term);
                statement->token->left = subject;
            } else {
                // assume right token (e.g. object) is available
                object->token->right = bel_copy_ast_node(term);
                statement->token->right = object;
            }

            fret;
        }

        SP           = ' ';
        NL           = '\n' | '\r' '\n'?;
        O_PAREN      = '(';
        C_PAREN      = ')';
        COLON        = ':';
        STRING       = '"' ('\\\"' | [^"])* '"';
        IDENT        = [a-zA-Z0-9_];
        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';
        RELATIONSHIP = 'increases'|'->'|'decreases'|'-|'|'directlyIncreases'|'=>'|'directlyDecreases'|'=|'|'causesNoChange'|'positiveCorrelation'|'negativeCorrelation'|'translatedTo'|'>>'|'transcribedTo'|':>'|'isA'|'subProcessOf'|'rateLimitingStepOf'|'biomarkerFor'|'prognosticBiomarkerFor'|'orthologous'|'analogous'|'association'|'--'|'hasMembers'|'hasComponents'|'hasMember'|'hasComponent';

        arguments :=
            (
                (IDENT+ >valc $valn ':')? @PFX (STRING|IDENT+) >valc $valn %VAL |
                FUNCTION >fxc $fxn %NESTED_FX O_PAREN @CALL_ARGUMENTS
            )
            (
                SP* ',' SP*
                (
                    (IDENT+ >valc $valn ':')? @PFX (STRING|IDENT+) >valc $valn %VAL |
                    FUNCTION >fxc $fxn %NESTED_FX O_PAREN @CALL_ARGUMENTS
                )
            )* C_PAREN @RET_ARGUMENTS;

        term :=
            FUNCTION >fxc $fxn %FX O_PAREN >CALL_ARGUMENTS (SP+|NL) @RET_TERM;

        statement :=
            IDENT @CALL_TERM RELATIONSHIP >relc $reln @REL SP+ IDENT @CALL_TERM;

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

    // free allocations
    bel_free_ast_node(term);
    if (term_stack) {
        stack_destroy(term_stack);
    }
    free(stack);
    free(function);
    free(relationship);
    free(value);

    return ast;
};
// vim: ft=c sw=4 ts=4 sts=4 expandtab

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "bel-ast.h"
#include "bel-node-stack.h"

/*
 * Value size: The maximum number of characters to hold in an
 * accumulated value.
 */
#define BEL_VALUE_CHAR_LEN 512

/*
 * Term stack size: A value of 20 allows for a nesting of
 * a max of 20 terms.
 */
#define TERM_STACK_SIZE 20

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

bel_ast* bel_parse_statement(char* line) {
    int             cs;
    char            *p;
    char            *pe;
    int             top;
    int             *stack;
    bel_ast_node*   statement;
    bel_ast_node*   subject;
    bel_ast_node*   rel;
    bel_ast_node*   object;
    bel_ast_node*   term;
    bel_ast_node*   current_nv;
    bel_ast_node*   arg;
    bel_ast*        ast;
    bel_node_stack* term_stack;
    char            *function;
    char            *relationship;
    char            *value;
    int             fi;
    int             ri;
    int             vi;

    p            = line;
    pe           = line + strlen(line);
    top          = 0;
    stack        = malloc(sizeof(int) * TERM_STACK_SIZE);
    current_nv   = NULL;
    function     = malloc(sizeof(char) * BEL_VALUE_CHAR_LEN);
    relationship = malloc(sizeof(char) * BEL_VALUE_CHAR_LEN);
    value        = malloc(sizeof(char) * BEL_VALUE_CHAR_LEN);
    fi           = 0;
    ri           = 0;
    vi           = 0;

    term_stack   = stack_init(TERM_STACK_SIZE);
    statement    = bel_new_ast_node_token(BEL_TOKEN_STATEMENT);
    subject      = bel_new_ast_node_token(BEL_TOKEN_SUBJECT);
    rel          = bel_new_ast_node_token(BEL_TOKEN_REL);
    object       = bel_new_ast_node_token(BEL_TOKEN_OBJECT);
    term         = NULL;
    ast          = bel_new_ast();
    ast->root    = statement;

    memset(function, '\0', BEL_VALUE_CHAR_LEN);
    memset(relationship, '\0', BEL_VALUE_CHAR_LEN);
    memset(value, '\0', BEL_VALUE_CHAR_LEN);

    %%{
        action fxc {
            fi = 0;
        }

        action relc {
            ri = 0;
        }

        action valc {
            vi = 0;
        }

        action fxn {
            function[fi++] = fc;
        }

        action reln {
            relationship[ri++] = fc;
        }

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

        action FX {
            term               = stack_peek(term_stack);
            term->token->left  = bel_new_ast_node_value(BEL_VALUE_FX, function);
            term->token->right = bel_new_ast_node_token(BEL_TOKEN_ARG);

            memset(function, '\0', BEL_VALUE_CHAR_LEN);
            fi = 0;
        }

        action REL {
            rel->token->left = bel_new_ast_node_value(BEL_VALUE_REL, relationship);
            object->token->left = rel;

            memset(relationship, '\0', BEL_VALUE_CHAR_LEN);
            ri = 0;
        }

        action NESTED_FX {
            bel_ast_node* term_top = stack_peek(term_stack);

            // find ARG leaf
            arg = term_top->token->right;
            while(arg->token->right != NULL) {
                arg = arg->token->right;
            }

            // create new nested term
            term               = bel_new_ast_node_token(BEL_TOKEN_TERM);
            term->token->left  = bel_new_ast_node_value(BEL_VALUE_FX, function);
            term->token->right = bel_new_ast_node_token(BEL_TOKEN_ARG);
            // set head term, left: new nested term, right: next arg
            arg->token->left   = term;
            arg->token->right  = bel_new_ast_node_token(BEL_TOKEN_ARG);

            // push new nested term onto stack
            stack_push(term_stack, term);

            memset(function, '\0', BEL_VALUE_CHAR_LEN);
            memset(value, '\0', BEL_VALUE_CHAR_LEN);
            fi = 0;
        }

        action PFX {
            term = stack_peek(term_stack);

            // find ARG leaf
            arg = term->token->right;
            while(arg->token->right != NULL) {
                arg = arg->token->right;
            }

            current_nv               = bel_new_ast_node_token(BEL_TOKEN_NV);
            current_nv->token->left  = bel_new_ast_node_value(BEL_VALUE_PFX, value);
            current_nv->token->right = bel_new_ast_node_value(BEL_VALUE_VAL, NULL);
            arg->token->left         = current_nv;
            arg->token->right        = bel_new_ast_node_token(BEL_TOKEN_ARG);

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

        action VAL {
            if (!current_nv) {
                term = stack_peek(term_stack);

                // find ARG leaf
                arg = term->token->right;
                while(arg->token->right != NULL) {
                    arg = arg->token->right;
                }

                current_nv               = bel_new_ast_node_token(BEL_TOKEN_NV);
                current_nv->token->left  = bel_new_ast_node_value(BEL_VALUE_PFX, NULL);
                current_nv->token->right = bel_new_ast_node_value(BEL_VALUE_VAL, value);
                arg->token->left         = current_nv;
                arg->token->right        = bel_new_ast_node_token(BEL_TOKEN_ARG);
            } else {
                current_nv->token->right = bel_new_ast_node_value(BEL_VALUE_VAL, value);
            }

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

        action CALL_ARGUMENTS {
            fcall arguments;
        }

        action RET_ARGUMENTS {
            term = stack_pop(term_stack);
            fret;
        }

        action CALL_TERM {
            fhold;
            term = bel_new_ast_node_token(BEL_TOKEN_TERM);
            term_stack->top = -1;
            stack_push(term_stack, term);
            fcall term;
        }

        action RET_TERM {
            if (statement->token->left == NULL) {
                // statement left token (e.g. subject) is available
                subject->token->left = bel_copy_ast_node(term);
                statement->token->left = subject;
            } else {
                // assume right token (e.g. object) is available
                object->token->right = bel_copy_ast_node(term);
                statement->token->right = object;
            }

            fret;
        }

        SP           = ' ';
        NL           = '\n' | '\r' '\n'?;
        O_PAREN      = '(';
        C_PAREN      = ')';
        COLON        = ':';
        STRING       = '"' ('\\\"' | [^"])* '"';
        IDENT        = [a-zA-Z0-9_];
        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';
        RELATIONSHIP = 'increases'|'decreases'|'-|'|'directlyIncreases'|'=>'|'directlyDecreases'|'=|'|'causesNoChange'|'positiveCorrelation'|'negativeCorrelation'|'translatedTo'|'>>'|'transcribedTo'|':>'|'isA'|'subProcessOf'|'rateLimitingStepOf'|'biomarkerFor'|'prognosticBiomarkerFor'|'orthologous'|'analogous'|'association'|'--'|'hasMembers'|'hasComponents'|'hasMember'|'hasComponent';

        arguments :=
            (
                (IDENT+ >valc $valn ':')? @PFX (STRING|IDENT+) >valc $valn %VAL |
                FUNCTION >fxc $fxn %NESTED_FX O_PAREN @CALL_ARGUMENTS
            )
            (
                SP* ',' SP*
                (
                    (IDENT+ >valc $valn ':')? @PFX (STRING|IDENT+) >valc $valn %VAL |
                    FUNCTION >fxc $fxn %NESTED_FX O_PAREN @CALL_ARGUMENTS
                )
            )* C_PAREN @RET_ARGUMENTS;

        term :=
            FUNCTION >fxc $fxn %FX O_PAREN >CALL_ARGUMENTS (SP+|NL) @RET_TERM;

        statement :=
            IDENT @CALL_TERM RELATIONSHIP >relc $reln @REL SP+ IDENT @CALL_TERM;

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

    // free allocations
    bel_free_ast_node(term);
    if (term_stack) {
        stack_destroy(term_stack);
    }
    free(stack);
    free(function);
    free(relationship);
    free(value);

    return ast;
};
// vim: ft=c sw=4 ts=4 sts=4 expandtab


Error handling

Error handling

# Parser for VCF-header info and format fields

module VcfHeader

  module RagelKeyValues

=begin
%%{

  machine simple_lexer;

  action mark { ts=p }
  action endquoted {
    emit.call(:value,data,ts,p)
  }

  squote = "'";
  dquote = '"';
  not_squote_or_escape = [^'\\];
  not_dquote_or_escape = [^"\\];
  escaped_something = /\\./;
  ss = space* squote ( not_squote_or_escape | escaped_something )* >mark %endquoted squote;
  dd = space* dquote ( not_dquote_or_escape | escaped_something )* >mark %endquoted dquote;

  integer     = ('+'|'-')?digit+;
  float       = ('+'|'-')?digit+'.'digit+;
  assignment  = '=';
  identifier  = (alpha alnum+);
  str         = (ss|dd)* ;
  boolean     = '.';
  key_word    = ( ('Number'|'Type'|'Description') >mark %{ emit.call(:key_word,data,ts,p) } );
  value       = ( (integer|float|boolean|identifier|str) >mark %{ emit.call(:value,data,ts,p) } );

  key_value = ( ('ID=' identifier) | (key_word '=' value) ) @!{ error_code="key-value" };

  main := ( '##' ('FILTER'|'FORMAT'|'INFO'|'ALT') '=') (('<'|',') key_value )* ;
}%%
=end

%% write data;
# %% this just fixes our syntax highlighting...

def self.run_lexer(buf, options = {})
  data = buf.unpack("c*") if(buf.is_a?(String))
  eof = data.length
  values = []
  stack = []

  emit = lambda { |type, data, ts, p|
    # Print the type and text of the last read token
    # p ts,p
    puts "#{type}: #{data[ts...p].pack('c*')}" if options[:debug]==true
    values << [type,data[ts...p].pack('c*')]
  }

  error_code = nil

  %% write init;
  %% write exec;

  raise "ERROR: "+error_code+" in "+buf if error_code

  res = {}
  values.each_slice(2) do | a,b |
    # p '*',a,b
    res[a[1]] = b[1]
    # p h[:value] if h[:name]==:identifier or h[:name]==:value or h[:name]==:string
  end
  p res
end

  end
end

if __FILE__ == $0

lines = <<LINES
##FORMAT=<ID=GT,Number=1,Type=String,Description="Genotype">
##FORMAT=<ID=DP,Number=1,Type=Integer,Description="Total read depth">
##FORMAT=<ID=DP4,Number=4,Type=Integer,Description="# high-quality ref-forward bases, ref-reverse, alt-forward and alt-reverse bases">
##INFO=<ID=PM,Number=0,Type=Flag,Description="Variant is Precious(Clinical,Pubmed Cited)">
##INFO=<ID=VP,Number=1,Type=String,Description="Variation Property.  Documentation is at ftp://ftp.ncbi.nlm.nih.gov/snp/specs/dbSNP_BitField_latest.pdf">
##INFO=<ID=GENEINFO,Number=1,Type=String,Description="Pairs each of gene symbol:gene id.  The gene symbol and id are delimited by a colon (:), and each pair is delimited by a vertical bar (|)">
##INFO=<ID=CLNHGVS,Number=.,Type=String,Description="Variant names from HGVS. The order of these variants corresponds to the order of the info in the other clinical  INFO tags.">
##INFO=<ID="CLNHGVS1",Number=.,Type=String,Description="Variant names from \\"HGVS\\". The order of these 'variants' corresponds to the order of the info in the other clinical  INFO tags.">
LINES

lines.strip.split("\n").each { |s|
  print s,"\n"
  VcfHeader::RagelKeyValues.run_lexer(s, debug: false)
}

end

# Parser for VCF-header info and format fields

module VcfHeader

  module RagelKeyValues

=begin
%%{

  machine simple_lexer;

  action mark { ts=p }
  action endquoted {
    emit.call(:value,data,ts,p)
  }

  squote = "'";
  dquote = '"';
  not_squote_or_escape = [^'\\];
  not_dquote_or_escape = [^"\\];
  escaped_something = /\\./;
  ss = space* squote ( not_squote_or_escape | escaped_something )* >mark %endquoted squote;
  dd = space* dquote ( not_dquote_or_escape | escaped_something )* >mark %endquoted dquote;

  integer     = ('+'|'-')?digit+;
  float       = ('+'|'-')?digit+'.'digit+;
  assignment  = '=';
  identifier  = (alpha alnum+);
  str         = (ss|dd)* ;
  boolean     = '.';
  key_word    = ( ('Number'|'Type'|'Description') >mark %{ emit.call(:key_word,data,ts,p) } );
  value       = ( (integer|float|boolean|identifier|str) >mark %{ emit.call(:value,data,ts,p) } );

  key_value = ( ('ID=' identifier) | (key_word '=' value) ) @!{ error_code="key-value" };

  main := ( '##' ('FORMAT'|'INFO'|'ALT') '=') (('<'|',') key_value )* ;
}%%
=end

%% write data;
# %% this just fixes our syntax highlighting...

def self.run_lexer(buf, options = {})
  data = buf.unpack("c*") if(buf.is_a?(String))
  eof = data.length
  values = []
  stack = []

  emit = lambda { |type, data, ts, p|
    # Print the type and text of the last read token
    # p ts,p
    puts "#{type}: #{data[ts...p].pack('c*')}" if options[:debug]==true
    values << [type,data[ts...p].pack('c*')]
  }

  error_code = nil

  %% write init;
  %% write exec;

  raise "ERROR: "+error_code+" in "+buf if error_code

  res = {}
  values.each_slice(2) do | a,b |
    # p '*',a,b
    res[a[1]] = b[1]
    # p h[:value] if h[:name]==:identifier or h[:name]==:value or h[:name]==:string
  end
  p res
end

  end
end

if __FILE__ == $0

lines = <<LINES
##FORMAT=<ID=GT,Number=1,Type=String,Description="Genotype">
##FORMAT=<ID=DP,Number=1,Type=Integer,Description="Total read depth">
##FORMAT=<ID=DP4,Number=4,Type=Integer,Description="# high-quality ref-forward bases, ref-reverse, alt-forward and alt-reverse bases">
##INFO=<ID=PM,Number=0,Type=Flag,Description="Variant is Precious(Clinical,Pubmed Cited)">
##INFO=<ID=VP,Number=1,Type=String,Description="Variation Property.  Documentation is at ftp://ftp.ncbi.nlm.nih.gov/snp/specs/dbSNP_BitField_latest.pdf">
##INFO=<ID=GENEINFO,Number=1,Type=String,Description="Pairs each of gene symbol:gene id.  The gene symbol and id are delimited by a colon (:), and each pair is delimited by a vertical bar (|)">
##INFO=<ID=CLNHGVS,Number=.,Type=String,Description="Variant names from HGVS. The order of these variants corresponds to the order of the info in the other clinical  INFO tags.">
##INFO=<ID="CLNHGVS1",Number=.,Type=String,Description="Variant names from \\"HGVS\\". The order of these 'variants' corresponds to the order of the info in the other clinical  INFO tags.">
LINES

lines.strip.split("\n").each { |s|
  print s,"\n"
  VcfHeader::RagelKeyValues.run_lexer(s, debug: false)
}

end


Removed void casts on Ragel generated variable declarations

/*
 * Copyright (c) 2019 Miroslav Foltýn.  All Rights Reserved.
 *
 * 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.
 */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>

#include "parsers.h"
#include "parser-ragel.h"
#include "parsers-utils.h"
#include "utils.h"

%%{
	machine statsd;
	write data;
}%%

/**
 * Ragel parser entry point
 * Parsers given buffer and populates datagram with parsed data if they are valid
 * @arg str - Buffer to be parsed
 * @arg datagram - Placeholder for parsed data
 * @return 1 on success, 0 on fail
 */
int
ragel_parser_parse(char* str, struct statsd_datagram** datagram) {
	*datagram = (struct statsd_datagram*) malloc(sizeof(struct statsd_datagram));
	*(*datagram) = (struct statsd_datagram) {0};
	ALLOC_CHECK("Not enough memory to save datagram");
	size_t length = strlen(str);
	char *p = str, *pe = (str + length + 1);
	char *eof = pe;
	int cs;
	size_t current_index = 0;
	size_t current_segment_start_index = 0;
	struct tag_collection* tags = NULL;
	char* tag_key = NULL;
	char* tag_value = NULL;
	int tag_key_allocated = 0;
	int tag_value_allocated = 0;
	int any_tags = 0;

	%%{

		action error_handler {
			goto error_clean_up;
		}

		action not_final_state_finish {
			goto error_clean_up;
		}

		action increment_character_counter {
			current_index++;
		}

		action name_parsing {
			current_segment_start_index = current_index;
		}

		action name_parsed {
			size_t current_segment_length = current_index - current_segment_start_index;
			(*datagram)->name = (char*) malloc(current_segment_length + 1);
			ALLOC_CHECK("Not enough memory to save metric name");
			memcpy(
				(*datagram)->name,
				&str[current_segment_start_index],
				current_segment_length
			);
			(*datagram)->name[current_segment_length] = '\0';
			current_segment_start_index = current_index + 1;
		}

		action tag_parsed {
			size_t key_len = strlen(tag_key) + 1;
			size_t value_len = strlen(tag_value) + 1;
			struct tag* t = (struct tag*) malloc(sizeof(struct tag));
			ALLOC_CHECK("Unable to allocate memory for tag.");
			t->key = (char*) malloc(key_len);
			ALLOC_CHECK("Unable to allocate memory for tag key.");
			memcpy(t->key, tag_key, key_len);
			t->value = (char*) malloc(value_len);
			ALLOC_CHECK("Unable to allocate memory for tag value.");
			memcpy(t->value, tag_value, value_len);
			if (!any_tags) {
				tags = (struct tag_collection*) malloc(sizeof(struct tag_collection));
				ALLOC_CHECK("Unable to allocate memory for tag collection.");
				tags->values = (struct tag**) malloc(sizeof(struct tag*));
				tags->values[0] = t;
				tags->length = 1;
				any_tags = 1;
			} else {
				struct tag** new_tags =
					(struct tag**) realloc(tags->values, sizeof(struct tag*) * (tags->length + 1));
				ALLOC_CHECK("Unable to allocate memory for tags");
				if (tags != NULL) {
					tags->values = new_tags;
					tags->values[tags->length] = t;
					tags->length++;
				}
			}
			free(tag_key);
			free(tag_value);
			tag_key = NULL;
			tag_value = NULL;
			tag_key_allocated = 0;
			tag_value_allocated = 0;
		}

		action value_parsed {
			char* startptr;
			char* endptr;
			if (str[current_segment_start_index] == '+') {
				(*datagram)->explicit_sign = SIGN_PLUS;
				startptr = &str[current_segment_start_index + 1];
			} else if (str[current_segment_start_index] == '-') {
				(*datagram)->explicit_sign = SIGN_MINUS;
				startptr = &str[current_segment_start_index + 1];
			} else {
				(*datagram)->explicit_sign = SIGN_NONE;
				startptr = &str[current_segment_start_index];
			}
			double value = strtod(startptr, &endptr);
			if (startptr == endptr || errno == ERANGE) {
				goto error_clean_up;
			}
			(*datagram)->value = value;
			current_segment_start_index = current_index + 1;
		}

		action type_parsed {
			if (str[current_segment_start_index] == 'c') {
				(*datagram)->type = METRIC_TYPE_COUNTER;
			} else if (str[current_segment_start_index] == 'g') {
				(*datagram)->type = METRIC_TYPE_GAUGE;
			} else {
				(*datagram)->type = METRIC_TYPE_DURATION;
			}
			current_segment_start_index = current_index + 1;
		}

		action tag_key_parsed {
			size_t current_segment_length = current_index - current_segment_start_index;
			tag_key = (char *) realloc(tag_key, current_segment_length + 1);
			ALLOC_CHECK("Not enough memory for tag key buffer.");
			tag_key_allocated = 1;
			memcpy(
				tag_key,
				&str[current_segment_start_index],
				current_segment_length
			);
			tag_key[current_segment_length] = '\0';
			current_segment_start_index = current_index + 1;
		}

		action tag_value_parsed {
			size_t current_segment_length = current_index - current_segment_start_index;
			tag_value = (char *) realloc(tag_value, current_segment_length + 1);
			ALLOC_CHECK("Not enough memory for tag key buffer.");
			tag_value_allocated = 1;
			memcpy(
				tag_value,
				&str[current_segment_start_index],
				current_segment_length
			);
			tag_value[current_segment_length] = '\0';
			current_segment_start_index = current_index + 1;
		}

		str_value = [a-z][a-zA-Z0-9_.]*;
		tag_string = [a-zA-Z0-9_.]{1,};
		name = str_value[:,]{1};
		value = ('+'|'-')?[0-9]+(('.')[0-9]+)?(('e'|'E')('+'|'-')?[0-9]+)?('|');
		type = ('c'|'g'|'ms')('@'|'\0'|'\n\0'|("|#"));
		tags = ((tag_string'=')@tag_key_parsed(tag_string(':'|',')@tag_value_parsed))+;
		tags_end = ((tag_string':')@tag_key_parsed(tag_string(','|'\0'|'\n\0')@tag_value_parsed))+;
		main := ((name >name_parsing @name_parsed . (tags @ tag_parsed)? . value @value_parsed . type @type_parsed . (tags_end @ tag_parsed)? ))
			  $~increment_character_counter @!error_handler @/not_final_state_finish;
		# */
		# Initialize and execute.
		write init;
		write exec;

	}%%

	if (any_tags) {
		char* json = tag_collection_to_json(tags);
		if (json != NULL) {
			(*datagram)->tags = json;
			(*datagram)->tags_pair_count = tags->length;
		}
		free_tag_collection(tags);
		if (tag_key_allocated) free(tag_key);
		if (tag_value_allocated) free(tag_value);
	}
	if (str[length - 1] == '\n')
        str[length - 1] = 0;
	VERBOSE_LOG(2, "Parsed: %s", str);
	return 1;

	error_clean_up:
	if (any_tags) {
		free_tag_collection(tags);
	}
	if (tag_key_allocated) free(tag_key);
	if (tag_value_allocated) free(tag_value);
	if (str[length - 1] == '\n')
        str[length - 1] = 0;
	free_datagram(*datagram);
	METRIC_PROCESSING_ERR_LOG("Throwing away datagram. REASON: unable to parse: %s", str);
	return 0;
};

/**
 * --------------------------------------
 * |                                    |
 * |     UNIT TEST FOR THIS FILE        |
 * |                                    |
 * --------------------------------------
 */
#if _TEST_TARGET == 2

int
main() {
    INIT_TEST("Running tests for ragel parser:", ragel_parser_parse);
    SUITE_HEADER("Unparsable values")
    CHECK_ERROR("", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
    CHECK_ERROR("wow", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
    CHECK_ERROR("wow:2", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
    CHECK_ERROR("wow|g", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
    CHECK_ERROR("2|g", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
    CHECK_ERROR("1:1|c", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
    CHECK_ERROR("e x-2 ple:20|c", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
    CHECK_ERROR("example,tags=dwq=qwddqwd=qwd:10|c", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
    SUITE_HEADER("Basic values");
    CHECK_ERROR("example:-1|c", "example", NULL, 1, METRIC_TYPE_COUNTER, SIGN_MINUS);
    CHECK_ERROR("example:+1|g", "example", NULL, 1, METRIC_TYPE_GAUGE, SIGN_PLUS);
    CHECK_ERROR("example:1|ms", "example", NULL, 1, METRIC_TYPE_DURATION, SIGN_NONE);
    SUITE_HEADER("Sanitizable metric name");
    SUITE_HEADER("Non integer values")
    CHECK_ERROR("example:1.2|c", "example", NULL, 1.2, METRIC_TYPE_COUNTER, SIGN_NONE);
    CHECK_ERROR("example:1.000000004|c", "example", NULL, 1.000000004, METRIC_TYPE_COUNTER, SIGN_NONE);
    CHECK_ERROR("example:1.00000000000000000000000000000004|c", "example", NULL, 1.00000000000000000000000000000004, METRIC_TYPE_COUNTER, SIGN_NONE);
    SUITE_HEADER("Instance descriptors")
    CHECK_ERROR("example,instance=20:20|c", "example", "{\"instance\":\"20\"}", 20, METRIC_TYPE_COUNTER, SIGN_NONE);
    SUITE_HEADER("Tag descriptors")
    CHECK_ERROR("example,tagY=20,tagX=10:10|c", "example", "{\"tagX\":\"10\",\"tagY\":\"20\"}", 10, METRIC_TYPE_COUNTER, SIGN_NONE);
	CHECK_ERROR("example,tagY=20:10|c|#tagY:20,tagW:W", "example", "{\"tagW\":\"W\",\"tagY\":\"20\"}", 10, METRIC_TYPE_COUNTER, SIGN_NONE);
    SUITE_HEADER("Malformed tags")
    SUITE_HEADER("Too long tag descriptor (gets thrown away)")
    CHECK_ERROR("example,tags=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:10|c", "example", NULL, 10, METRIC_TYPE_COUNTER, SIGN_NONE);
    END_TEST();
}

#endif

/*
 * Copyright (c) 2019 Miroslav Foltýn.  All Rights Reserved.
 *
 * 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.
 */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>

#include "parsers.h"
#include "parser-ragel.h"
#include "parsers-utils.h"
#include "utils.h"

%%{
	machine statsd;
	write data;
}%%

/**
 * Ragel parser entry point
 * Parsers given buffer and populates datagram with parsed data if they are valid
 * @arg str - Buffer to be parsed
 * @arg datagram - Placeholder for parsed data
 * @return 1 on success, 0 on fail
 */
int
ragel_parser_parse(char* str, struct statsd_datagram** datagram) {
	*datagram = (struct statsd_datagram*) malloc(sizeof(struct statsd_datagram));
	*(*datagram) = (struct statsd_datagram) {0};
	ALLOC_CHECK("Not enough memory to save datagram");
	size_t length = strlen(str);
	char *p = str, *pe = (str + length + 1);
	char *eof = pe;
	int cs;
	size_t current_index = 0;
	size_t current_segment_start_index = 0;
	struct tag_collection* tags = NULL;
	char* tag_key = NULL;
	char* tag_value = NULL;
	int tag_key_allocated = 0;
	int tag_value_allocated = 0;
	int any_tags = 0;

	%%{

		action error_handler {
			goto error_clean_up;
		}

		action not_final_state_finish {
			goto error_clean_up;
		}

		action increment_character_counter {
			current_index++;
		}

		action name_parsing {
			current_segment_start_index = current_index;
		}

		action name_parsed {
			size_t current_segment_length = current_index - current_segment_start_index;
			(*datagram)->name = (char*) malloc(current_segment_length + 1);
			ALLOC_CHECK("Not enough memory to save metric name");
			memcpy(
				(*datagram)->name,
				&str[current_segment_start_index],
				current_segment_length
			);
			(*datagram)->name[current_segment_length] = '\0';
			current_segment_start_index = current_index + 1;
		}

		action tag_parsed {
			size_t key_len = strlen(tag_key) + 1;
			size_t value_len = strlen(tag_value) + 1;
			struct tag* t = (struct tag*) malloc(sizeof(struct tag));
			ALLOC_CHECK("Unable to allocate memory for tag.");
			t->key = (char*) malloc(key_len);
			ALLOC_CHECK("Unable to allocate memory for tag key.");
			memcpy(t->key, tag_key, key_len);
			t->value = (char*) malloc(value_len);
			ALLOC_CHECK("Unable to allocate memory for tag value.");
			memcpy(t->value, tag_value, value_len);
			if (!any_tags) {
				tags = (struct tag_collection*) malloc(sizeof(struct tag_collection));
				ALLOC_CHECK("Unable to allocate memory for tag collection.");
				tags->values = (struct tag**) malloc(sizeof(struct tag*));
				tags->values[0] = t;
				tags->length = 1;
				any_tags = 1;
			} else {
				struct tag** new_tags =
					(struct tag**) realloc(tags->values, sizeof(struct tag*) * (tags->length + 1));
				ALLOC_CHECK("Unable to allocate memory for tags");
				if (tags != NULL) {
					tags->values = new_tags;
					tags->values[tags->length] = t;
					tags->length++;
				}
			}
			free(tag_key);
			free(tag_value);
			tag_key = NULL;
			tag_value = NULL;
			tag_key_allocated = 0;
			tag_value_allocated = 0;
		}

		action value_parsed {
			char* startptr;
			char* endptr;
			if (str[current_segment_start_index] == '+') {
				(*datagram)->explicit_sign = SIGN_PLUS;
				startptr = &str[current_segment_start_index + 1];
			} else if (str[current_segment_start_index] == '-') {
				(*datagram)->explicit_sign = SIGN_MINUS;
				startptr = &str[current_segment_start_index + 1];
			} else {
				(*datagram)->explicit_sign = SIGN_NONE;
				startptr = &str[current_segment_start_index];
			}
			double value = strtod(startptr, &endptr);
			if (startptr == endptr || errno == ERANGE) {
				goto error_clean_up;
			}
			(*datagram)->value = value;
			current_segment_start_index = current_index + 1;
		}

		action type_parsed {
			if (str[current_segment_start_index] == 'c') {
				(*datagram)->type = METRIC_TYPE_COUNTER;
			} else if (str[current_segment_start_index] == 'g') {
				(*datagram)->type = METRIC_TYPE_GAUGE;
			} else {
				(*datagram)->type = METRIC_TYPE_DURATION;
			}
			current_segment_start_index = current_index + 1;
		}

		action tag_key_parsed {
			size_t current_segment_length = current_index - current_segment_start_index;
			tag_key = (char *) realloc(tag_key, current_segment_length + 1);
			ALLOC_CHECK("Not enough memory for tag key buffer.");
			tag_key_allocated = 1;
			memcpy(
				tag_key,
				&str[current_segment_start_index],
				current_segment_length
			);
			tag_key[current_segment_length] = '\0';
			current_segment_start_index = current_index + 1;
		}

		action tag_value_parsed {
			size_t current_segment_length = current_index - current_segment_start_index;
			tag_value = (char *) realloc(tag_value, current_segment_length + 1);
			ALLOC_CHECK("Not enough memory for tag key buffer.");
			tag_value_allocated = 1;
			memcpy(
				tag_value,
				&str[current_segment_start_index],
				current_segment_length
			);
			tag_value[current_segment_length] = '\0';
			current_segment_start_index = current_index + 1;
		}

		str_value = [a-z][a-zA-Z0-9_.]*;
		tag_string = [a-zA-Z0-9_.]{1,};
		name = str_value[:,]{1};
		value = ('+'|'-')?[0-9]+(('.')[0-9]+)?(('e'|'E')('+'|'-')?[0-9]+)?('|');
		type = ('c'|'g'|'ms')('@'|'\0'|'\n\0'|("|#"));
		tags = ((tag_string'=')@tag_key_parsed(tag_string(':'|',')@tag_value_parsed))+;
		tags_end = ((tag_string':')@tag_key_parsed(tag_string(','|'\0'|'\n\0')@tag_value_parsed))+;
		main := ((name >name_parsing @name_parsed . (tags @ tag_parsed)? . value @value_parsed . type @type_parsed . (tags_end @ tag_parsed)? ))
			  $~increment_character_counter @!error_handler @/not_final_state_finish;
		# */
		# Initialize and execute.
		write init;
		write exec;

	}%%
	(void)statsd_en_main;
	(void)statsd_error;
	(void)statsd_first_final;
	(void)_statsd_eof_actions;

	if (any_tags) {
		char* json = tag_collection_to_json(tags);
		if (json != NULL) {
			(*datagram)->tags = json;
			(*datagram)->tags_pair_count = tags->length;
		}
		free_tag_collection(tags);
		if (tag_key_allocated) free(tag_key);
		if (tag_value_allocated) free(tag_value);
	}
	if (str[length - 1] == '\n')
        str[length - 1] = 0;
	VERBOSE_LOG(2, "Parsed: %s", str);
	return 1;

	error_clean_up:
	if (any_tags) {
		free_tag_collection(tags);
	}
	if (tag_key_allocated) free(tag_key);
	if (tag_value_allocated) free(tag_value);
	if (str[length - 1] == '\n')
        str[length - 1] = 0;
	free_datagram(*datagram);
	METRIC_PROCESSING_ERR_LOG("Throwing away datagram. REASON: unable to parse: %s", str);
	return 0;
};

/**
 * --------------------------------------
 * |                                    |
 * |     UNIT TEST FOR THIS FILE        |
 * |                                    |
 * --------------------------------------
 */
#if _TEST_TARGET == 2

int
main() {
    INIT_TEST("Running tests for ragel parser:", ragel_parser_parse);
    SUITE_HEADER("Unparsable values")
    CHECK_ERROR("", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
    CHECK_ERROR("wow", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
    CHECK_ERROR("wow:2", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
    CHECK_ERROR("wow|g", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
    CHECK_ERROR("2|g", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
    CHECK_ERROR("1:1|c", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
    CHECK_ERROR("e x-2 ple:20|c", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
    CHECK_ERROR("example,tags=dwq=qwddqwd=qwd:10|c", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
    SUITE_HEADER("Basic values");
    CHECK_ERROR("example:-1|c", "example", NULL, 1, METRIC_TYPE_COUNTER, SIGN_MINUS);
    CHECK_ERROR("example:+1|g", "example", NULL, 1, METRIC_TYPE_GAUGE, SIGN_PLUS);
    CHECK_ERROR("example:1|ms", "example", NULL, 1, METRIC_TYPE_DURATION, SIGN_NONE);
    SUITE_HEADER("Sanitizable metric name");
    SUITE_HEADER("Non integer values")
    CHECK_ERROR("example:1.2|c", "example", NULL, 1.2, METRIC_TYPE_COUNTER, SIGN_NONE);
    CHECK_ERROR("example:1.000000004|c", "example", NULL, 1.000000004, METRIC_TYPE_COUNTER, SIGN_NONE);
    CHECK_ERROR("example:1.00000000000000000000000000000004|c", "example", NULL, 1.00000000000000000000000000000004, METRIC_TYPE_COUNTER, SIGN_NONE);
    SUITE_HEADER("Instance descriptors")
    CHECK_ERROR("example,instance=20:20|c", "example", "{\"instance\":\"20\"}", 20, METRIC_TYPE_COUNTER, SIGN_NONE);
    SUITE_HEADER("Tag descriptors")
    CHECK_ERROR("example,tagY=20,tagX=10:10|c", "example", "{\"tagX\":\"10\",\"tagY\":\"20\"}", 10, METRIC_TYPE_COUNTER, SIGN_NONE);
	CHECK_ERROR("example,tagY=20:10|c|#tagY:20,tagW:W", "example", "{\"tagW\":\"W\",\"tagY\":\"20\"}", 10, METRIC_TYPE_COUNTER, SIGN_NONE);
    SUITE_HEADER("Malformed tags")
    SUITE_HEADER("Too long tag descriptor (gets thrown away)")
    CHECK_ERROR("example,tags=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:10|c", "example", NULL, 10, METRIC_TYPE_COUNTER, SIGN_NONE);
    END_TEST();
}

#endif