]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - lexer.h
Stuff
[xonotic/gmqcc.git] / lexer.h
diff --git a/lexer.h b/lexer.h
index b8e2e01f8449c1654ae298ffcff3181f6d58d5cb..f75dff6232d4b7ea72aaac132284731006884fa9 100644 (file)
--- a/lexer.h
+++ b/lexer.h
-#ifndef GMQCC_LEXER_HDR_
-#define GMQCC_LEXER_HDR_
+#ifndef GMQCC_LEXER_HDR
+#define GMQCC_LEXER_HDR
+
+#include "gmqcc.h"
+
+#define TOKENS(X) \
+    /* Other tokens which we can return: */ \
+    X(NONE, =0) \
+    X(CR, = '\r') \
+    X(LF, = '\n') \
+    X(WS, = ' ') \
+    X(BACKSLASH, = '\\') \
+    X(HASH, = '#') \
+    X(DOLLAR, = '$') \
+    X(DOT, = '.') \
+    X(COMMA, = ',') \
+    X(COLON, = ':') \
+    X(SEMICOLON, = ';') \
+    X(AND, = '&') \
+    X(OR, = '|') \
+    X(XOR, = '^') \
+    X(BITNOT, = '~') \
+    X(NOT, = '!') \
+    X(LT, = '<') \
+    X(GT, = '>') \
+    X(EQ, = '=') \
+    X(MUL, = '*') \
+    X(DIV, = '/') \
+    X(MOD, = '%') \
+    X(ADD, = '+') \
+    X(SUB, = '-') \
+    X(QUOT_SINGLE, = '\'') \
+    X(QUOT_DOUBLE, = '"') \
+    X(QUESTION, = '?') \
+    X(BRACE_OPEN, = '{') \
+    X(BRACE_CLOSE, = '}') \
+    X(BRACKET_OPEN, = '[') \
+    X(BRACKET_CLOSE, = ']') \
+    X(PAREN_OPEN, = '(') \
+    X(PAREN_CLOSE, = ')') \
+    X(START, = 128) \
+    X(IDENT, ) \
+    X(TYPENAME, ) \
+    X(OPERATOR, ) \
+    X(OP_AND, ) \
+    X(OP_CROSS, ) \
+    X(OP_LE, ) \
+    X(OP_GE, ) \
+    X(OP_LSH, ) \
+    X(OP_RSH, ) \
+    /* loop */ \
+    X(KEYWORD, ) \
+    /* 3 dots, ... */ \
+    X(DOTS, ) \
+    /* [[ */ \
+    X(ATTRIBUTE_OPEN, ) \
+    /* ]] */ \
+    X(ATTRIBUTE_CLOSE, ) \
+    /* for the ftepp only */ \
+    X(VA_ARGS, ) \
+    /* for the ftepp only */ \
+    X(VA_ARGS_ARRAY, ) \
+    /* to get the count of vaargs */ \
+    X(VA_COUNT, ) \
+    /* not the typename but an actual "string" */ \
+    X(STRINGCONST, ) \
+    X(CHARCONST, ) \
+    X(VECTORCONST, ) \
+    X(INTCONST, ) \
+    X(FLOATCONST, ) \
+    X(WHITE, ) \
+    X(EOL, ) \
+    /* if we add additional tokens before this, the exposed API \
+     * should not be broken anyway, but EOF/ERROR/... should \
+     * still be at the bottom \
+     */ \
+    X(END, = 1024) \
+    /* We use '< ERROR', so FATAL must come after it and any \
+     * other error related tokens as well \
+     */ \
+    X(ERROR, ) \
+    /* internal error, eg out of memory */ \
+    X(FATAL, ) \
+    /**/
+
+enum Token : int { // todo: enum class
+#define X(id, val) id val,
+    TOKENS(X)
+#undef X
+};
 
-typedef struct token_s token;
+inline const char *TokenName(Token t) {
+    switch (t) {
+        default:
+            return "UNKNOWN";
+#define X(id, val) case Token::id: return #id;
+        TOKENS(X)
+#undef X
+    }
+}
 
-#include "ast.h"
+struct cvec {
+    std::string value;
 
-struct token_s {
-       int ttype;
+    explicit cvec() = default;
 
-       char *value;
+    char *mut() {
+        return &value[0];
+    }
 
-       union {
-               vector v;
-               int    i;
-               double f;
-               int    t; /* type */
-       } constval;
+    const char *c_str() {
+        return value.c_str();
+    }
 
-#if 0
-       struct token_s *next;
-       struct token_s *prev;
-#endif
+    void shrinkto(size_t i) {
+        value.resize(i);
+    }
 
-       lex_ctx ctx;
-};
+    void shrinkby(size_t i) {
+        value.resize(value.size() - i);
+    }
 
-#if 0
-token* token_new();
-void   token_delete(token*);
-token* token_copy(const token *cp);
-void   token_delete_all(token *t);
-token* token_copy_all(const token *cp);
-#endif
-
-/* Lexer
- *
- */
-enum {
-    /* Other tokens which we can return: */
-    TOKEN_NONE = 0,
-    TOKEN_START = 128,
+    void push(char it) {
+        value.push_back(it);
+    }
+};
 
-    TOKEN_IDENT,
+struct token {
+    Token ttype;
+    cvec value;
+    union {
+        vec3_t v;
+        int i;
+        qcfloat_t f;
+        qc_type t; /* type */
+    } constval;
+    lex_ctx_t ctx;
+};
 
-    TOKEN_TYPENAME,
+struct frame_macro {
+    std::string name;
+    int value;
+};
 
-    TOKEN_OPERATOR,
+struct lex_file {
+    FILE *file;
+    const char *open_string;
+    size_t open_string_length;
+    size_t open_string_pos;
 
-    TOKEN_KEYWORD, /* loop */
+    char *name;
+    size_t line;
+    size_t sline; /* line at the start of a token */
+    size_t column;
 
-    TOKEN_DOTS, /* 3 dots, ... */
+    std::array<Token, 256> peek;
+    size_t peekpos;
 
-    TOKEN_STRINGCONST, /* not the typename but an actual "string" */
-    TOKEN_CHARCONST,
-    TOKEN_VECTORCONST,
-    TOKEN_INTCONST,
-    TOKEN_FLOATCONST,
+    bool eof;
 
-    TOKEN_WHITE,
-    TOKEN_EOL,
+    token tok; /* not a pointer anymore */
 
-    TOKEN_EOF,
+    struct {
+        bool noops:1;
+        bool nodigraphs:1; /* used when lexing string constants */
+        bool preprocessing:1; /* whitespace and EOLs become actual tokens */
+        bool mergelines:1; /* backslash at the end of a line escapes the newline */
+    } flags; /* sizeof == 1 */
 
-    /* We use '< TOKEN_ERROR', so TOKEN_FATAL must come after it and any
-     * other error related tokens as well
-     */
-    TOKEN_ERROR,
-    TOKEN_FATAL /* internal error, eg out of memory */
-};
+    int framevalue;
+    frame_macro *frames;
+    std::string modelname;
 
-static const char *_tokennames[] = {
-    "TOKEN_START",
-    "TOKEN_IDENT",
-    "TOKEN_TYPENAME",
-    "TOKEN_OPERATOR",
-    "TOKEN_KEYWORD",
-    "TOKEN_DOTS",
-    "TOKEN_STRINGCONST",
-    "TOKEN_CHARCONST",
-    "TOKEN_VECTORCONST",
-    "TOKEN_INTCONST",
-    "TOKEN_FLOATCONST",
-    "TOKEN_WHITE",
-    "TOKEN_EOL",
-    "TOKEN_EOF",
-    "TOKEN_ERROR",
-    "TOKEN_FATAL",
+    size_t push_line;
 };
-typedef int
-_all_tokennames_added_[
-       ((TOKEN_FATAL - TOKEN_START + 1) ==
-        (sizeof(_tokennames)/sizeof(_tokennames[0])))
-       ? 1 : -1];
-
-typedef struct {
-    char *name;
-    int   value;
-} frame_macro;
-
-typedef struct {
-       FILE   *file;
-       const char *open_string;
-       size_t      open_string_length;
-       size_t      open_string_pos;
 
-       char   *name;
-       size_t  line;
-       size_t  sline; /* line at the start of a token */
+lex_file *lex_open(const char *file);
 
-       char    peek[256];
-       size_t  peekpos;
+lex_file *lex_open_string(const char *str, size_t len, const char *name);
 
-       bool    eof;
+void lex_close(lex_file *lex);
 
-       token   tok; /* not a pointer anymore */
-
-       struct {
-           bool noops;
-           bool nodigraphs; /* used when lexing string constants */
-           bool preprocessing; /* whitespace and EOLs become actual tokens */
-           bool mergelines; /* backslash at the end of a line escapes the newline */
-       } flags;
-
-    int framevalue;
-       frame_macro *frames;
-       char *modelname;
-} lex_file;
+Token lex_do(lex_file *lex);
 
-lex_file* lex_open (const char *file);
-lex_file* lex_open_string(const char *str, size_t len, const char *name);
-void      lex_close(lex_file   *lex);
-int       lex_do   (lex_file   *lex);
-void      lex_cleanup(void);
+void lex_cleanup();
 
 /* Parser
  *
@@ -145,127 +196,188 @@ enum {
 #define OP_SUFFIX 1
 #define OP_PREFIX 2
 
-typedef struct {
-    const char   *op;
+struct oper_info {
+    const char *op;
     unsigned int operands;
     unsigned int id;
     unsigned int assoc;
-    unsigned int prec;
+    signed int prec;
     unsigned int flags;
-} oper_info;
+    bool folds;
+};
 
-#define opid1(a) (a)
-#define opid2(a,b) ((a<<8)|b)
-#define opid3(a,b,c) ((a<<16)|(b<<8)|c)
+/*
+ * Explicit uint8_t casts since the left operand of shift operator cannot
+ * be negative, even though it won't happen, this supresses the future
+ * possibility.
+ */
+#define opid1(a)     ((uint8_t)a)
+#define opid2(a, b)   (((uint8_t)a<<8) |(uint8_t)b)
+#define opid3(a, b, c) (((uint8_t)a<<16)|((uint8_t)b<<8)|(uint8_t)c)
 
 static const oper_info c_operators[] = {
-    { "(",   0, opid1('('),         ASSOC_LEFT,  99, OP_PREFIX}, /* paren expression - non function call */
+        {"(",       0, opid1('('),           ASSOC_LEFT,  99, OP_PREFIX, false}, /* paren expression - non function call */
+        {"_length", 1, opid3('l', 'e', 'n'), ASSOC_RIGHT, 98, OP_PREFIX, true},
+
+        {"++",      1, opid3('S', '+', '+'), ASSOC_LEFT,  17, OP_SUFFIX, false},
+        {"--",      1, opid3('S', '-', '-'), ASSOC_LEFT,  17, OP_SUFFIX, false},
+        {".",       2, opid1('.'),           ASSOC_LEFT,  17, 0,         false},
+        {"(",       0, opid1('('),           ASSOC_LEFT,  17, 0,         false}, /* function call */
+        {"[",       2, opid1('['),           ASSOC_LEFT,  17, 0,         false}, /* array subscript */
 
-    { "++",  1, opid3('S','+','+'), ASSOC_LEFT,  16, OP_SUFFIX},
-    { "--",  1, opid3('S','-','-'), ASSOC_LEFT,  16, OP_SUFFIX},
+        {"++",      1, opid3('+', '+', 'P'), ASSOC_RIGHT, 16, OP_PREFIX, false},
+        {"--",      1, opid3('-', '-', 'P'), ASSOC_RIGHT, 16, OP_PREFIX, false},
 
-    { ".",   2, opid1('.'),         ASSOC_LEFT,  15, 0 },
-    { "(",   0, opid1('('),         ASSOC_LEFT,  15, 0 }, /* function call */
-    { "[",   2, opid1('['),         ASSOC_LEFT,  15, 0 }, /* array subscript */
+        {"**",      2, opid2('*', '*'),      ASSOC_RIGHT, 14, 0,         true},
+        {"!",       1, opid2('!', 'P'),      ASSOC_RIGHT, 14, OP_PREFIX, true},
+        {"~",       1, opid2('~', 'P'),      ASSOC_RIGHT, 14, OP_PREFIX, true},
+        {"+",       1, opid2('+', 'P'),      ASSOC_RIGHT, 14, OP_PREFIX, false},
+        {"-",       1, opid2('-', 'P'),      ASSOC_RIGHT, 14, OP_PREFIX, true},
+/*  { "&",      1, opid2('&','P'),     ASSOC_RIGHT, 14, OP_PREFIX, false}, */
 
-    { "!",   1, opid2('!', 'P'),    ASSOC_RIGHT, 14, OP_PREFIX },
-    { "~",   1, opid2('~', 'P'),    ASSOC_RIGHT, 14, OP_PREFIX },
-    { "+",   1, opid2('+','P'),     ASSOC_RIGHT, 14, OP_PREFIX },
-    { "-",   1, opid2('-','P'),     ASSOC_RIGHT, 14, OP_PREFIX },
-    { "++",  1, opid3('+','+','P'), ASSOC_RIGHT, 14, OP_PREFIX },
-    { "--",  1, opid3('-','-','P'), ASSOC_RIGHT, 14, OP_PREFIX },
-/*  { "&",   1, opid2('&','P'),     ASSOC_RIGHT, 14, OP_PREFIX }, */
+        {"*",       2, opid1('*'),           ASSOC_LEFT,  13, 0,         true},
+        {"/",       2, opid1('/'),           ASSOC_LEFT,  13, 0,         true},
+        {"%",       2, opid1('%'),           ASSOC_LEFT,  13, 0,         true},
+        {"><",      2, opid2('>', '<'),      ASSOC_LEFT,  13, 0,         true},
 
-    { "*",   2, opid1('*'),         ASSOC_LEFT,  13, 0 },
-    { "/",   2, opid1('/'),         ASSOC_LEFT,  13, 0 },
-    { "%",   2, opid1('%'),         ASSOC_LEFT,  13, 0 },
+        {"+",       2, opid1('+'),           ASSOC_LEFT,  12, 0,         true},
+        {"-",       2, opid1('-'),           ASSOC_LEFT,  12, 0,         true},
 
-    { "+",   2, opid1('+'),         ASSOC_LEFT,  12, 0 },
-    { "-",   2, opid1('-'),         ASSOC_LEFT,  12, 0 },
+        {"<<",      2, opid2('<', '<'),      ASSOC_LEFT,  11, 0,         true},
+        {">>",      2, opid2('>', '>'),      ASSOC_LEFT,  11, 0,         true},
 
-    { "<<",  2, opid2('<','<'),     ASSOC_LEFT,  11, 0 },
-    { ">>",  2, opid2('>','>'),     ASSOC_LEFT,  11, 0 },
+        {"<",       2, opid1('<'),           ASSOC_LEFT,  10, 0,         false},
+        {">",       2, opid1('>'),           ASSOC_LEFT,  10, 0,         false},
+        {"<=>",     2, opid3('<', '=', '>'), ASSOC_LEFT,  10, 0,         true},
+        {"<=",      2, opid2('<', '='),      ASSOC_LEFT,  10, 0,         false},
+        {">=",      2, opid2('>', '='),      ASSOC_LEFT,  10, 0,         false},
 
-    { "<",   2, opid1('<'),         ASSOC_LEFT,  10, 0 },
-    { ">",   2, opid1('>'),         ASSOC_LEFT,  10, 0 },
-    { "<=",  2, opid2('<','='),     ASSOC_LEFT,  10, 0 },
-    { ">=",  2, opid2('>','='),     ASSOC_LEFT,  10, 0 },
+        {"==",      2, opid2('=', '='),      ASSOC_LEFT,  9,  0,         true},
+        {"!=",      2, opid2('!', '='),      ASSOC_LEFT,  9,  0,         true},
 
-    { "==",  2, opid2('=','='),     ASSOC_LEFT,  9,  0 },
-    { "!=",  2, opid2('!','='),     ASSOC_LEFT,  9,  0 },
+        {"&",       2, opid1('&'),           ASSOC_LEFT,  8,  0,         true},
 
-    { "&",   2, opid1('&'),         ASSOC_LEFT,  8,  0 },
+        {"^",       2, opid1('^'),           ASSOC_LEFT,  7,  0,         true},
 
-    { "^",   2, opid1('^'),         ASSOC_LEFT,  7,  0 },
+        {"|",       2, opid1('|'),           ASSOC_LEFT,  6,  0,         true},
 
-    { "|",   2, opid1('|'),         ASSOC_LEFT,  6,  0 },
+        {"&&",      2, opid2('&', '&'),      ASSOC_LEFT,  5,  0,         true},
 
-    { "&&",  2, opid2('&','&'),     ASSOC_LEFT,  5,  0 },
+        {"||",      2, opid2('|', '|'),      ASSOC_LEFT,  4,  0,         true},
 
-    { "||",  2, opid2('|','|'),     ASSOC_LEFT,  4,  0 },
+        {"?",       3, opid2('?', ':'),      ASSOC_RIGHT, 3,  0,         true},
 
-    { "?",   3, opid2('?',':'),     ASSOC_RIGHT, 3,  0 },
+        {"=",       2, opid1('='),           ASSOC_RIGHT, 2,  0,         false},
+        {"+=",      2, opid2('+', '='),      ASSOC_RIGHT, 2,  0,         false},
+        {"-=",      2, opid2('-', '='),      ASSOC_RIGHT, 2,  0,         false},
+        {"*=",      2, opid2('*', '='),      ASSOC_RIGHT, 2,  0,         false},
+        {"/=",      2, opid2('/', '='),      ASSOC_RIGHT, 2,  0,         false},
+        {"%=",      2, opid2('%', '='),      ASSOC_RIGHT, 2,  0,         false},
+        {">>=",     2, opid3('>', '>', '='), ASSOC_RIGHT, 2,  0,         false},
+        {"<<=",     2, opid3('<', '<', '='), ASSOC_RIGHT, 2,  0,         false},
+        {"&=",      2, opid2('&', '='),      ASSOC_RIGHT, 2,  0,         false},
+        {"^=",      2, opid2('^', '='),      ASSOC_RIGHT, 2,  0,         false},
+        {"|=",      2, opid2('|', '='),      ASSOC_RIGHT, 2,  0,         false},
 
-    { "=",   2, opid1('='),         ASSOC_RIGHT, 2,  0 },
-    { "+=",  2, opid2('+','='),     ASSOC_RIGHT, 2,  0 },
-    { "-=",  2, opid2('-','='),     ASSOC_RIGHT, 2,  0 },
-    { "*=",  2, opid2('*','='),     ASSOC_RIGHT, 2,  0 },
-    { "/=",  2, opid2('/','='),     ASSOC_RIGHT, 2,  0 },
-    { "%=",  2, opid2('%','='),     ASSOC_RIGHT, 2,  0 },
-    { ">>=", 2, opid3('>','>','='), ASSOC_RIGHT, 2,  0 },
-    { "<<=", 2, opid3('<','<','='), ASSOC_RIGHT, 2,  0 },
-    { "&=",  2, opid2('&','='),     ASSOC_RIGHT, 2,  0 },
-    { "^=",  2, opid2('^','='),     ASSOC_RIGHT, 2,  0 },
-    { "|=",  2, opid2('|','='),     ASSOC_RIGHT, 2,  0 },
+        {":",       0, opid2(':', '?'),      ASSOC_RIGHT, 1,  0,         false},
 
-    { ",",   2, opid1(','),         ASSOC_LEFT,  1,  0 }
+        {",",       2, opid1(','),           ASSOC_LEFT,  0,  0,         false}
 };
-static const size_t c_operator_count = (sizeof(c_operators) / sizeof(c_operators[0]));
 
-static const oper_info qcc_operators[] = {
-    { "(",   0, opid1('('),         ASSOC_LEFT,  99, OP_PREFIX}, /* paren expression - non function call */
-
-    { ".",   2, opid1('.'),         ASSOC_LEFT,  15, 0 },
-    { "(",   0, opid1('('),         ASSOC_LEFT,  15, 0 }, /* function call */
-    { "[",   2, opid1('['),         ASSOC_LEFT,  15, 0 }, /* array subscript */
-
-    { "!",   1, opid2('!', 'P'),    ASSOC_RIGHT, 14, OP_PREFIX },
-    { "+",   1, opid2('+','P'),     ASSOC_RIGHT, 14, OP_PREFIX },
-    { "-",   1, opid2('-','P'),     ASSOC_RIGHT, 14, OP_PREFIX },
-
-    { "*",   2, opid1('*'),         ASSOC_LEFT,  13, 0 },
-    { "/",   2, opid1('/'),         ASSOC_LEFT,  13, 0 },
-    { "&",   2, opid1('&'),         ASSOC_LEFT,  13, 0 },
-    { "|",   2, opid1('|'),         ASSOC_LEFT,  13, 0 },
-
-    { "+",   2, opid1('+'),         ASSOC_LEFT,  12, 0 },
-    { "-",   2, opid1('-'),         ASSOC_LEFT,  12, 0 },
-
-    { "<",   2, opid1('<'),         ASSOC_LEFT,  10, 0 },
-    { ">",   2, opid1('>'),         ASSOC_LEFT,  10, 0 },
-    { "<=",  2, opid2('<','='),     ASSOC_LEFT,  10, 0 },
-    { ">=",  2, opid2('>','='),     ASSOC_LEFT,  10, 0 },
-    { "==",  2, opid2('=','='),     ASSOC_LEFT,  10,  0 },
-    { "!=",  2, opid2('!','='),     ASSOC_LEFT,  10,  0 },
-
-    { "=",   2, opid1('='),         ASSOC_RIGHT, 8,  0 },
-    { "+=",  2, opid2('+','='),     ASSOC_RIGHT, 8,  0 },
-    { "-=",  2, opid2('-','='),     ASSOC_RIGHT, 8,  0 },
-    { "*=",  2, opid2('*','='),     ASSOC_RIGHT, 8,  0 },
-    { "/=",  2, opid2('/','='),     ASSOC_RIGHT, 8,  0 },
-    { "%=",  2, opid2('%','='),     ASSOC_RIGHT, 8,  0 },
-    { "&=",  2, opid2('&','='),     ASSOC_RIGHT, 8,  0 },
-    { "|=",  2, opid2('|','='),     ASSOC_RIGHT, 8,  0 },
-
-    { "&&",  2, opid2('&','&'),     ASSOC_LEFT,  5,  0 },
-    { "||",  2, opid2('|','|'),     ASSOC_LEFT,  5,  0 },
-
-    { ",",   2, opid1(','),         ASSOC_LEFT,  1,  0 }
+static const oper_info fte_operators[] = {
+        {"(",   0, opid1('('),           ASSOC_LEFT,  99, OP_PREFIX, false}, /* paren expression - non function call */
+
+        {"++",  1, opid3('S', '+', '+'), ASSOC_LEFT,  15, OP_SUFFIX, false},
+        {"--",  1, opid3('S', '-', '-'), ASSOC_LEFT,  15, OP_SUFFIX, false},
+        {".",   2, opid1('.'),           ASSOC_LEFT,  15, 0,         false},
+        {"(",   0, opid1('('),           ASSOC_LEFT,  15, 0,         false}, /* function call */
+        {"[",   2, opid1('['),           ASSOC_LEFT,  15, 0,         false}, /* array subscript */
+
+        {"!",   1, opid2('!', 'P'),      ASSOC_RIGHT, 14, OP_PREFIX, true},
+        {"+",   1, opid2('+', 'P'),      ASSOC_RIGHT, 14, OP_PREFIX, false},
+        {"-",   1, opid2('-', 'P'),      ASSOC_RIGHT, 14, OP_PREFIX, true},
+        {"++",  1, opid3('+', '+', 'P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
+        {"--",  1, opid3('-', '-', 'P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
+
+        {"*",   2, opid1('*'),           ASSOC_LEFT,  13, 0,         true},
+        {"/",   2, opid1('/'),           ASSOC_LEFT,  13, 0,         true},
+        {"&",   2, opid1('&'),           ASSOC_LEFT,  13, 0,         true},
+        {"|",   2, opid1('|'),           ASSOC_LEFT,  13, 0,         true},
+
+        {"+",   2, opid1('+'),           ASSOC_LEFT,  12, 0,         true},
+        {"-",   2, opid1('-'),           ASSOC_LEFT,  12, 0,         true},
+
+        {"<<",  2, opid2('<', '<'),      ASSOC_LEFT,  11, 0,         true},
+        {">>",  2, opid2('>', '>'),      ASSOC_LEFT,  11, 0,         true},
+
+        {"<",   2, opid1('<'),           ASSOC_LEFT,  10, 0,         false},
+        {">",   2, opid1('>'),           ASSOC_LEFT,  10, 0,         false},
+        {"<=",  2, opid2('<', '='),      ASSOC_LEFT,  10, 0,         false},
+        {">=",  2, opid2('>', '='),      ASSOC_LEFT,  10, 0,         false},
+        {"==",  2, opid2('=', '='),      ASSOC_LEFT,  10, 0,         true},
+        {"!=",  2, opid2('!', '='),      ASSOC_LEFT,  10, 0,         true},
+
+        {"?",   3, opid2('?', ':'),      ASSOC_RIGHT, 9,  0,         true},
+
+        {"=",   2, opid1('='),           ASSOC_RIGHT, 8,  0,         false},
+        {"+=",  2, opid2('+', '='),      ASSOC_RIGHT, 8,  0,         false},
+        {"-=",  2, opid2('-', '='),      ASSOC_RIGHT, 8,  0,         false},
+        {"*=",  2, opid2('*', '='),      ASSOC_RIGHT, 8,  0,         false},
+        {"/=",  2, opid2('/', '='),      ASSOC_RIGHT, 8,  0,         false},
+        {"%=",  2, opid2('%', '='),      ASSOC_RIGHT, 8,  0,         false},
+        {"&=",  2, opid2('&', '='),      ASSOC_RIGHT, 8,  0,         false},
+        {"|=",  2, opid2('|', '='),      ASSOC_RIGHT, 8,  0,         false},
+        {"&~=", 2, opid3('&', '~', '='), ASSOC_RIGHT, 8,  0,         false},
+
+        {"&&",  2, opid2('&', '&'),      ASSOC_LEFT,  5,  0,         true},
+        {"||",  2, opid2('|', '|'),      ASSOC_LEFT,  5,  0,         true},
+
+        /* Leave precedence 3 for : with -fcorrect-ternary */
+        {",",   2, opid1(','),           ASSOC_LEFT,  2,  0,         false},
+        {":",   0, opid2(':', '?'),      ASSOC_RIGHT, 1,  0,         false}
 };
-static const size_t qcc_operator_count = (sizeof(qcc_operators) / sizeof(qcc_operators[0]));
 
+static const oper_info qcc_operators[] = {
+        {"(",  0, opid1('('),      ASSOC_LEFT,  99, OP_PREFIX, false}, /* paren expression - non function call */
+
+        {".",  2, opid1('.'),      ASSOC_LEFT,  15, 0,         false},
+        {"(",  0, opid1('('),      ASSOC_LEFT,  15, 0,         false}, /* function call */
+        {"[",  2, opid1('['),      ASSOC_LEFT,  15, 0,         false}, /* array subscript */
+
+        {"!",  1, opid2('!', 'P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
+        {"+",  1, opid2('+', 'P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
+        {"-",  1, opid2('-', 'P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
+
+        {"*",  2, opid1('*'),      ASSOC_LEFT,  13, 0,         true},
+        {"/",  2, opid1('/'),      ASSOC_LEFT,  13, 0,         true},
+        {"&",  2, opid1('&'),      ASSOC_LEFT,  13, 0,         true},
+        {"|",  2, opid1('|'),      ASSOC_LEFT,  13, 0,         true},
+
+        {"+",  2, opid1('+'),      ASSOC_LEFT,  12, 0,         true},
+        {"-",  2, opid1('-'),      ASSOC_LEFT,  12, 0,         true},
+
+        {"<",  2, opid1('<'),      ASSOC_LEFT,  10, 0,         false},
+        {">",  2, opid1('>'),      ASSOC_LEFT,  10, 0,         false},
+        {"<=", 2, opid2('<', '='), ASSOC_LEFT,  10, 0,         false},
+        {">=", 2, opid2('>', '='), ASSOC_LEFT,  10, 0,         false},
+        {"==", 2, opid2('=', '='), ASSOC_LEFT,  10, 0,         true},
+        {"!=", 2, opid2('!', '='), ASSOC_LEFT,  10, 0,         true},
+
+        {"=",  2, opid1('='),      ASSOC_RIGHT, 8,  0,         false},
+        {"+=", 2, opid2('+', '='), ASSOC_RIGHT, 8,  0,         false},
+        {"-=", 2, opid2('-', '='), ASSOC_RIGHT, 8,  0,         false},
+        {"*=", 2, opid2('*', '='), ASSOC_RIGHT, 8,  0,         false},
+        {"/=", 2, opid2('/', '='), ASSOC_RIGHT, 8,  0,         false},
+        {"%=", 2, opid2('%', '='), ASSOC_RIGHT, 8,  0,         false},
+        {"&=", 2, opid2('&', '='), ASSOC_RIGHT, 8,  0,         false},
+        {"|=", 2, opid2('|', '='), ASSOC_RIGHT, 8,  0,         false},
+
+        {"&&", 2, opid2('&', '&'), ASSOC_LEFT,  5,  0,         true},
+        {"||", 2, opid2('|', '|'), ASSOC_LEFT,  5,  0,         true},
+
+        {",",  2, opid1(','),      ASSOC_LEFT,  2,  0,         false},
+};
 extern const oper_info *operators;
-extern size_t           operator_count;
-void lexerror(lex_file*, const char *fmt, ...);
+extern size_t operator_count;
 
 #endif