+%{
+
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "asn1parser.h"
+#include "asn1p_y.h"
+
+int asn1p_lex(void);
+void asn1p_lexer_hack_push_opaque_state(void); /* Used in .y */
+void asn1p_lexer_hack_enable_with_syntax(void); /* Used in .y */
+void asn1p_lexer_hack_push_encoding_control(void); /* Used in .y */
+
+#define YY_FATAL_ERROR(msg) do { \
+ fprintf(stderr, \
+ "lexer error at line %d, " \
+ "text \"%s\"\n", \
+ yylineno, yytext); \
+ exit(1); \
+ } while(0)
+
+int asn1p_lexer_pedantic_1990 = 0;
+int asn1p_lexer_types_year = 0;
+int asn1p_lexer_constructs_year = 0;
+int asn1p_lexer_extended_values = 0;
+
+int asn1p_as_pointer;
+
+static asn1c_integer_t _lex_atoi(const char *ptr);
+static double _lex_atod(const char *ptr);
+
+/*
+ * Check that the type is defined in the year of the standard choosen.
+ */
+#define TYPE_LIFETIME(fyr, lyr) \
+ (!asn1p_lexer_types_year \
+ || (fyr && fyr <= asn1p_lexer_types_year) \
+ || (lyr && lyr > asn1p_lexer_types_year))
+
+/*
+ * Check the the construction (or concept, i.e. CLASS) is defined in
+ * a given year.
+ */
+#define CONSTRUCT_LIFETIME(fyr, lyr) \
+ (!asn1p_lexer_constructs_year \
+ || (fyr && fyr <= asn1p_lexer_constructs_year) \
+ || (lyr && lyr > asn1p_lexer_constructs_year))
+
+/*
+ * Append quoted string.
+ */
+#define QAPPEND(text, tlen) do { \
+ char *prev_text = asn1p_lval.tv_opaque.buf; \
+ int prev_len = asn1p_lval.tv_opaque.len; \
+ char *p; \
+ \
+ p = malloc((tlen) + prev_len + 1); \
+ if(p == NULL) return -1; \
+ \
+ if(prev_text) memcpy(p, prev_text, prev_len); \
+ memcpy(p + prev_len, text, tlen); \
+ p[prev_len + (tlen)] = '\0'; \
+ \
+ free(asn1p_lval.tv_opaque.buf); \
+ asn1p_lval.tv_opaque.buf = p; \
+ asn1p_lval.tv_opaque.len = (tlen) + prev_len; \
+ } while(0)
+
+%}
+
+%option never-interactive
+%option noinput
+%option noyywrap stack
+/* Performance penalty is OK */
+%option yylineno
+/* Controlled from within application */
+%option debug
+
+%pointer
+
+%x dash_comment
+%x idash_comment
+%x cpp_comment
+%x quoted
+%x opaque
+%x encoding_control
+%x with_syntax
+%x extended_values
+
+/* Newline */
+NL [\r\v\f\n]
+/* White-space */
+WSP [\t\r\v\f\n ]
+
+%%
+
+<INITIAL>"\xef\xbb\xbf" return UTF8_BOM;
+
+-{3,}/[\r\n] /* Immediately terminated long comment */
+-{3,}/[^-\r\n] yy_push_state(idash_comment); /* Incorrect, but acceptable */
+<idash_comment>{
+ -{3,} yy_pop_state(); /* Acceptable end of comment */
+}
+
+--<[ \t]*ASN1C.RepresentAsPointer[ \t]*>-- asn1p_as_pointer = 1;
+
+<extended_values>{
+ "#BIT STRING" {
+ yy_pop_state();
+ return TOK_ExtValue_BIT_STRING;
+ }
+}
+
+<INITIAL,with_syntax>-- yy_push_state(dash_comment);
+<dash_comment,idash_comment>{
+
+ {NL} yy_pop_state();
+
+ -- yy_pop_state(); /* End of comment */
+ - /* Eat single dash */
+ [^\r\v\f\n-]+ /* Eat */
+}
+
+<INITIAL,cpp_comment,with_syntax>"/*" yy_push_state(cpp_comment);
+<cpp_comment>{
+ [^*/<] /* Eat */
+ "*/" yy_pop_state();
+ . /* Eat */
+}
+
+
+ /*
+ * This is state is being set from corresponding .y module when
+ * higher-level data is necessary to make proper parsing of the
+ * underlying data. Thus, we enter the <opaque> state and save
+ * everything for later processing.
+ */
+<opaque>{
+
+ "{" {
+ yy_push_state(opaque);
+ asn1p_lval.tv_opaque.buf = strdup(yytext);
+ asn1p_lval.tv_opaque.len = yyleng;
+ return TOK_opaque;
+ }
+
+ "}" {
+ yy_pop_state();
+ asn1p_lval.tv_opaque.buf = strdup(yytext);
+ asn1p_lval.tv_opaque.len = yyleng;
+ return TOK_opaque;
+ }
+
+ [^{}:=]+ {
+ asn1p_lval.tv_opaque.buf = strdup(yytext);
+ asn1p_lval.tv_opaque.len = yyleng;
+ return TOK_opaque;
+ }
+
+ "::=" {
+ fprintf(stderr,
+ "ASN.1 Parser synchronization failure: "
+ "\"%s\" at line %d must not appear "
+ "inside value definition\n",
+ yytext, yylineno);
+ return -1;
+ }
+
+ [:=] {
+ asn1p_lval.tv_opaque.buf = strdup(yytext);
+ asn1p_lval.tv_opaque.len = yyleng;
+ return TOK_opaque;
+ }
+
+ }
+
+\"[^\"]* {
+ asn1p_lval.tv_opaque.buf = 0;
+ asn1p_lval.tv_opaque.len = 0;
+ QAPPEND(yytext+1, yyleng-1);
+ yy_push_state(quoted);
+ }
+<quoted>{
+
+ \"\" { QAPPEND(yytext, yyleng-1); } /* Add a single quote */
+ [^\"]+ { QAPPEND(yytext, yyleng); }
+
+ \" {
+ yy_pop_state();
+ /* Do not append last quote:
+ // QAPPEND(yytext, yyleng); */
+
+ if(asn1p_lexer_pedantic_1990
+ && strchr(yytext, '\n')) {
+ fprintf(stderr, "%s: "
+ "Newlines are prohibited by ASN.1:1990\n",
+ asn1p_lval.tv_opaque.buf);
+ return -1;
+ }
+
+ return TOK_cstring;
+ }
+
+ }
+
+<encoding_control>{
+ ENCODING-CONTROL {
+ const char *s = "ENCODING-CONTROL";
+ const char *p = s + sizeof("ENCODING-CONTROL") - 2;
+ for(; p >= s; p--) unput(*p);
+ yy_pop_state();
+ }
+ END unput('D'); unput('N'); unput('E'); yy_pop_state();
+ [^{} \t\r\v\f\n]+
+ [[:alnum:]]+
+ . /* Eat everything else */
+ "\n"
+ }
+
+'[0-9A-F \t\r\v\f\n]+'H {
+ /* " \t\r\n" weren't allowed in ASN.1:1990. */
+ asn1p_lval.tv_str = strdup(yytext);
+ return TOK_hstring;
+ }
+
+'[01 \t\r\v\f\n]+'B {
+ /* " \t\r\n" weren't allowed in ASN.1:1990. */
+ asn1p_lval.tv_str = strdup(yytext);
+ return TOK_bstring;
+ }
+
+
+-[1-9][0-9]* {
+ asn1p_lval.a_int = _lex_atoi(yytext);
+ if(errno == ERANGE)
+ return -1;
+ return TOK_number_negative;
+ }
+
+[1-9][0-9]* {
+ asn1p_lval.a_int = _lex_atoi(yytext);
+ if(errno == ERANGE)
+ return -1;
+ return TOK_number;
+ }
+
+"0" {
+ asn1p_lval.a_int = _lex_atoi(yytext);
+ if(errno == ERANGE)
+ return -1;
+ return TOK_number;
+ }
+
+[-+]?[0-9]+[.]?([eE][-+]?)?[0-9]+ {
+ asn1p_lval.a_dbl = _lex_atod(yytext);
+ if(errno == ERANGE)
+ return -1;
+ return TOK_realnumber;
+ }
+
+ABSENT return TOK_ABSENT;
+ALL return TOK_ALL;
+ANY {
+ /* Appeared in 1990, removed in 1997 */
+ if(TYPE_LIFETIME(1990, 1997))
+ return TOK_ANY;
+ fprintf(stderr, "Keyword \"%s\" at line %d "
+ "is obsolete\n", yytext, yylineno);
+ REJECT;
+ }
+APPLICATION return TOK_APPLICATION;
+AUTOMATIC return TOK_AUTOMATIC;
+BEGIN {
+ if(asn1p_lexer_extended_values) {
+ yy_push_state(extended_values);
+ }
+ return TOK_BEGIN;
+ }
+BIT return TOK_BIT;
+BMPString {
+ if(TYPE_LIFETIME(1994, 0))
+ return TOK_BMPString;
+ REJECT;
+ }
+BOOLEAN return TOK_BOOLEAN;
+BY return TOK_BY;
+CHARACTER return TOK_CHARACTER;
+CHOICE return TOK_CHOICE;
+CLASS return TOK_CLASS;
+COMPONENT return TOK_COMPONENT;
+COMPONENTS return TOK_COMPONENTS;
+CONSTRAINED return TOK_CONSTRAINED;
+CONTAINING return TOK_CONTAINING;
+DEFAULT return TOK_DEFAULT;
+DEFINED {
+ /* Appeared in 1990, removed in 1997 */
+ if(TYPE_LIFETIME(1990, 1997))
+ return TOK_DEFINED;
+ fprintf(stderr, "Keyword \"%s\" at line %d "
+ "is obsolete\n", yytext, yylineno);
+ /* Deprecated since */
+ REJECT;
+ }
+DEFINITIONS return TOK_DEFINITIONS;
+EMBEDDED return TOK_EMBEDDED;
+ENCODED return TOK_ENCODED;
+ENCODING-CONTROL return TOK_ENCODING_CONTROL;
+END {
+ if(YYSTATE == extended_values) {
+ yy_pop_state();
+ }
+ return TOK_END;
+ }
+ENUMERATED return TOK_ENUMERATED;
+EXCEPT return TOK_EXCEPT;
+EXPLICIT return TOK_EXPLICIT;
+EXPORTS return TOK_EXPORTS;
+EXTENSIBILITY return TOK_EXTENSIBILITY;
+EXTERNAL return TOK_EXTERNAL;
+FALSE return TOK_FALSE;
+FROM return TOK_FROM;
+GeneralizedTime return TOK_GeneralizedTime;
+GeneralString return TOK_GeneralString;
+GraphicString return TOK_GraphicString;
+IA5String return TOK_IA5String;
+IDENTIFIER return TOK_IDENTIFIER;
+IMPLICIT return TOK_IMPLICIT;
+IMPLIED return TOK_IMPLIED;
+IMPORTS return TOK_IMPORTS;
+INCLUDES return TOK_INCLUDES;
+INSTANCE return TOK_INSTANCE;
+INSTRUCTIONS return TOK_INSTRUCTIONS;
+INTEGER return TOK_INTEGER;
+INTERSECTION return TOK_INTERSECTION;
+ISO646String return TOK_ISO646String;
+MAX return TOK_MAX;
+MIN return TOK_MIN;
+MINUS-INFINITY return TOK_MINUS_INFINITY;
+NULL return TOK_NULL;
+NumericString return TOK_NumericString;
+OBJECT return TOK_OBJECT;
+ObjectDescriptor return TOK_ObjectDescriptor;
+OCTET return TOK_OCTET;
+OF return TOK_OF;
+OPTIONAL return TOK_OPTIONAL;
+PATTERN return TOK_PATTERN;
+PDV return TOK_PDV;
+PLUS-INFINITY return TOK_PLUS_INFINITY;
+PRESENT return TOK_PRESENT;
+PrintableString return TOK_PrintableString;
+PRIVATE return TOK_PRIVATE;
+REAL return TOK_REAL;
+RELATIVE-OID return TOK_RELATIVE_OID;
+SEQUENCE return TOK_SEQUENCE;
+SET return TOK_SET;
+SIZE return TOK_SIZE;
+STRING return TOK_STRING;
+SYNTAX return TOK_SYNTAX;
+T61String return TOK_T61String;
+TAGS return TOK_TAGS;
+TeletexString return TOK_TeletexString;
+TRUE return TOK_TRUE;
+UNION return TOK_UNION;
+UNIQUE return TOK_UNIQUE;
+UNIVERSAL return TOK_UNIVERSAL;
+UniversalString {
+ if(TYPE_LIFETIME(1994, 0))
+ return TOK_UniversalString;
+ REJECT;
+ }
+UTCTime return TOK_UTCTime;
+UTF8String {
+ if(TYPE_LIFETIME(1994, 0))
+ return TOK_UTF8String;
+ REJECT;
+ }
+VideotexString return TOK_VideotexString;
+VisibleString return TOK_VisibleString;
+WITH return TOK_WITH;
+
+
+<INITIAL,with_syntax>&[A-Z][A-Za-z0-9]*([-][A-Za-z0-9]+)* {
+ asn1p_lval.tv_str = strdup(yytext);
+ return TOK_typefieldreference;
+ }
+
+<INITIAL,with_syntax>&[a-z][a-zA-Z0-9]*([-][a-zA-Z0-9]+)* {
+ asn1p_lval.tv_str = strdup(yytext);
+ return TOK_valuefieldreference;
+ }
+
+
+[a-z][a-zA-Z0-9]*([-][a-zA-Z0-9]+)* {
+ asn1p_lval.tv_str = strdup(yytext);
+ return TOK_identifier;
+ }
+
+ /*
+ * objectclassreference
+ */
+<INITIAL,extended_values>[A-Z][A-Z0-9]*([-][A-Z0-9]+)* {
+ asn1p_lval.tv_str = strdup(yytext);
+ return TOK_capitalreference;
+ }
+
+ /*
+ * typereference, modulereference
+ * NOTE: TOK_objectclassreference must be combined
+ * with this token to produce true typereference.
+ */
+[A-Z][A-Za-z0-9]*([-][A-Za-z0-9]+)* {
+ asn1p_lval.tv_str = strdup(yytext);
+ return TOK_typereference;
+ }
+
+<INITIAL,extended_values>"::=" return TOK_PPEQ;
+
+"..." return TOK_ThreeDots;
+".." return TOK_TwoDots;
+
+<with_syntax>{
+
+ [A-Z][A-Za-z0-9]*([-][A-Za-z0-9]+)* {
+ asn1p_lval.tv_str = strdup(yytext);
+ return TOK_Literal;
+ }
+
+ "," {
+ asn1p_lval.tv_str = strdup(yytext);
+ return TOK_Literal;
+ }
+
+ "{" {
+ yy_push_state(with_syntax);
+ asn1p_lval.tv_str = strdup(yytext);
+ return TOK_Literal;
+ }
+
+ "[" return '[';
+ "]" return ']';
+
+ {WSP}+ {
+ asn1p_lval.tv_opaque.buf = strdup(yytext);
+ asn1p_lval.tv_opaque.len = yyleng;
+ return TOK_whitespace;
+ }
+
+ "}" {
+ yy_pop_state();
+ if(YYSTATE == with_syntax) {
+ asn1p_lval.tv_str = strdup(yytext);
+ return TOK_Literal;
+ } else {
+ return '}';
+ }
+ }
+
+}
+
+
+<INITIAL,extended_values>{WSP}+ /* Ignore whitespace */
+
+
+[{][\t\r\v\f\n ]*[0-7][,][\t\r\v\f\n ]*[0-9]+[\t\r\v\f\n ]*[}] {
+ asn1c_integer_t v1 = -1, v2 = -1;
+ char *p;
+ for(p = yytext; *p; p++)
+ if(*p >= '0' && *p <= '9')
+ { v1 = _lex_atoi(p); break; }
+ while(*p >= '0' && *p <= '9') p++; /* Skip digits */
+ for(; *p; p++) if(*p >= '0' && *p <= '9')
+ { v2 = _lex_atoi(p); break; }
+ if(v1 < 0 || v1 > 7) {
+ fprintf(stderr, "%s at line %d: X.680:2003, #37.14 "
+ "mandates 0..7 range for Tuple's TableColumn\n",
+ yytext, yylineno);
+ return -1;
+ }
+ if(v2 < 0 || v2 > 15) {
+ fprintf(stderr, "%s at line %d: X.680:2003, #37.14 "
+ "mandates 0..15 range for Tuple's TableRow\n",
+ yytext, yylineno);
+ return -1;
+ }
+ asn1p_lval.a_int = (v1 << 4) + v2;
+ return TOK_tuple;
+ }
+
+[{][\t\r\v\f\n ]*[0-9]+[,][\t\r\v\f\n ]*[0-9]+[,][\t\r\v\f\n ]*[0-9]+[,][\t\r\v\f\n ]*[0-9]+[\t\r\v\f\n ]*[}] {
+ asn1c_integer_t v1 = -1, v2 = -1, v3 = -1, v4 = -1;
+ char *p;
+ for(p = yytext; *p; p++)
+ if(*p >= '0' && *p <= '9')
+ { v1 = _lex_atoi(p); break; }
+ while(*p >= '0' && *p <= '9') p++; /* Skip digits */
+ for(; *p; p++) if(*p >= '0' && *p <= '9')
+ { v2 = _lex_atoi(p); break; }
+ while(*p >= '0' && *p <= '9') p++;
+ for(; *p; p++) if(*p >= '0' && *p <= '9')
+ { v3 = _lex_atoi(p); break; }
+ while(*p >= '0' && *p <= '9') p++;
+ for(; *p; p++) if(*p >= '0' && *p <= '9')
+ { v4 = _lex_atoi(p); break; }
+ if(v1 < 0 || v1 > 127) {
+ fprintf(stderr, "%s at line %d: X.680:2003, #37.12 "
+ "mandates 0..127 range for Quadruple's Group\n",
+ yytext, yylineno);
+ return -1;
+ }
+ if(v2 < 0 || v2 > 255) {
+ fprintf(stderr, "%s at line %d: X.680:2003, #37.12 "
+ "mandates 0..255 range for Quadruple's Plane\n",
+ yytext, yylineno);
+ return -1;
+ }
+ if(v3 < 0 || v3 > 255) {
+ fprintf(stderr, "%s at line %d: X.680:2003, #37.12 "
+ "mandates 0..255 range for Quadruple's Row\n",
+ yytext, yylineno);
+ return -1;
+ }
+ if(v4 < 0 || v4 > 255) {
+ fprintf(stderr, "%s at line %d: X.680:2003, #37.12 "
+ "mandates 0..255 range for Quadruple's Cell\n",
+ yytext, yylineno);
+ return -1;
+ }
+ asn1p_lval.a_int = (v1 << 24) | (v2 << 16) | (v3 << 8) | v4;
+ return TOK_quadruple;
+ }
+
+
+"[[" return TOK_VBracketLeft;
+"]]" return TOK_VBracketRight;
+
+[(){},;:|!.&@\[\]^] return yytext[0];
+
+[^A-Za-z0-9:=,{}<.@()[]'\"|&^*;!-] {
+ if(TYPE_LIFETIME(1994, 0))
+ fprintf(stderr, "ERROR: ");
+ fprintf(stderr,
+ "Symbol '%c' at line %d is prohibited "
+ "by ASN.1:1994 and ASN.1:1997\n",
+ yytext[0], yylineno);
+ if(TYPE_LIFETIME(1994, 0))
+ return -1;
+ }
+
+<*>. {
+ fprintf(stderr,
+ "Unexpected token at line %d: \"%s\"\n",
+ yylineno, yytext);
+ while(YYSTATE != INITIAL)
+ yy_pop_state();
+ if(0) {
+ yy_top_state(); /* Just to use this function. */
+ yy_fatal_error("Parse error");
+ }
+ return -1;
+}
+
+<*><<EOF>> {
+ while(YYSTATE != INITIAL)
+ yy_pop_state();
+ yyterminate();
+ }
+
+
+%%
+
+/*
+ * Very dirty but wonderful hack allowing to rule states from within .y file.
+ */
+void asn1p_lexer_hack_push_opaque_state() { yy_push_state(opaque); }
+
+/*
+ * Another hack which disables recognizing some tokens when inside WITH SYNTAX.
+ */
+void asn1p_lexer_hack_enable_with_syntax() { yy_push_state(with_syntax); }
+
+/* Yet another */
+void asn1p_lexer_hack_push_encoding_control() {
+ yy_push_state(encoding_control);
+}
+
+static asn1c_integer_t
+_lex_atoi(const char *ptr) {
+ asn1c_integer_t value;
+ if(asn1p_atoi(ptr, &value)) {
+ fprintf(stderr,
+ "Value \"%s\" at line %d is too large "
+ "for this compiler! Please contact the asn1c author.\n",
+ ptr, yylineno);
+ errno = ERANGE;
+ }
+ return value;
+}
+
+static double
+_lex_atod(const char *ptr) {
+ double value;
+ errno = 0;
+ value = strtod(ptr, 0);
+ if(errno) {
+ fprintf(stderr,
+ "Value \"%s\" at line %d is outside of `double` range "
+ "in this compiler! Please contact the asn1c author.\n",
+ ptr, yylineno);
+ errno = ERANGE;
+ }
+ return value;
+}
+