#include <stdlib.h>#include <stdio.h>#include <stdarg.h>#include <inttypes.h>#include <ctype.h>#include <string.h>#include <assert.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <errno.h>#include <unistd.h> #include "util.h"#include "parse.h" #include "gram.h" #define End (-1) char *filename;Srcloc curloc;Tok *curtok; /* the file contents are stored globally */static int fidx;static int fbufsz;static char *fbuf; static intpeekn(int n){ if (fidx + n >= fbufsz) return End; else return fbuf[fidx + n];} static intpeek(void){ return peekn(0);} static intnext(void){ int c; c = peek(); fidx++; return c;} static voidunget(void){ fidx--; assert(fidx >= 0);} /* * Consumes the character iff * the character is equal to 'c'. * returns true if there was a match, * false otherwise. */static intmatch(char c){ if (peek() == c) { next(); return 1; } else { return 0; }} static Tok *mktok(int tt){ Tok *t; t = zalloc(sizeof(Tok)); t->type = tt; t->loc = curloc; return t;} static intidentchar(int c) { return isalnum(c) || c == '_' || c == '$';} static voideatcomment(void){ int depth; int startln; int c; depth = 0; startln = curloc.line; while (1) { c = next(); switch (c) { /* enter level of nesting */ case '/': if (match('*')) depth++; break; /* leave level of nesting */ case '*': if (match('/')) depth--; break; /* have to keep line numbers synced */ case '\n': curloc.line++; break; case End: lfatal(curloc, "File ended within comment starting at line %d", startln); break; } if (depth == 0) break; }} /* * Consumes all forms of whitespace, * including comments. If we are in a * state where we should ignore newlines, * we also consume '\n'. ';' is still * accepted as a line ending. */static voideatspace(void){ int c; int ignorenl; ignorenl = 0; while (1) { c = peek(); if (!ignorenl && c == '\n') { break; } else if (c == '\\') { ignorenl = 1; next(); } else if (ignorenl && c == '\n') { next(); curloc.line++; ignorenl = 0; } else if (isspace(c)) { next(); } else if (c == '/' && peekn(1) == '*') { eatcomment(); } else if (c == '/' && peekn(1) == '/') { while (peek() != End && peek() != '\n') next(); } else { break; } }} /* * Decides if an identifier is a * keyword or not. Returns the * token type to use for the * identifier. */static intkwd(char *s){ static const struct { char *kw; int tt; } kwmap[] = { {"$noret", Tattr}, {"_", Tgap}, {"auto", Tauto}, {"break", Tbreak}, {"const", Tconst}, {"continue", Tcontinue}, {"elif", Telif}, {"else", Telse}, {"extern", Tattr}, {"false", Tboollit}, {"for", Tfor}, {"generic", Tgeneric}, {"goto", Tgoto}, {"if", Tif}, {"impl", Timpl}, {"match", Tmatch}, {"pkg", Tpkg}, {"pkglocal", Tattr}, {"sizeof", Tsizeof}, {"struct", Tstruct}, {"trait", Ttrait}, {"true", Tboollit}, {"type", Ttype}, {"union", Tunion}, {"use", Tuse}, {"var", Tvar}, {"void", Tvoidlit}, {"while", Twhile}, }; size_t min, max, mid; int cmp; min = 0; max = sizeof(kwmap) / sizeof(kwmap[0]); while (max > min) { mid = (max + min) / 2; cmp = strcmp(s, kwmap[mid].kw); if (cmp == 0) return kwmap[mid].tt; else if (cmp > 0) min = mid + 1; else if (cmp < 0) max = mid; } return Tident;} static intidentstr(char *buf, size_t sz){ size_t i; char c; i = 0; for (c = peek(); i < sz && identchar(c); c = peek()) { next(); buf[i++] = c; } buf[i] = '\0'; return i;} static Tok *kwident(void){ char buf[1024]; Tok *t; if (!identstr(buf, sizeof buf)) return NULL; t = mktok(kwd(buf)); t->id = strdup(buf); return t;} static voidappend(char **buf, size_t *len, size_t *sz, int c){ if (!*sz) { *sz = 16; *buf = malloc(*sz); } if (*len == *sz - 1) { *sz = *sz * 2; *buf = realloc(*buf, *sz); } buf[0][*len] = c; (*len)++;} static voidencode(char *buf, size_t len, uint32_t c){ int mark; size_t i; assert(len > 0 && len < 5); if (len == 1) mark = 0; else mark = (((1 << (8 - len)) - 1) ^ 0xff); for (i = len - 1; i > 0; i--) { buf[i] = (c & 0x3f) | 0x80; c >>= 6; } buf[0] = (c | mark);} /* * Appends a unicode codepoint 'c' to a growable buffer 'buf', * resizing if needed. */static voidappendc(char **buf, size_t *len, size_t *sz, uint32_t c){ size_t i, charlen; char charbuf[5] = {0}; if (c < 0x80) charlen = 1; else if (c < 0x800) charlen = 2; else if (c < 0x10000) charlen = 3; else if (c < 0x200000) charlen = 4; else lfatal(curloc, "invalid utf character '\\u{%x}'", c); encode(charbuf, charlen, c); for (i = 0; i < charlen; i++) append(buf, len, sz, charbuf[i]);} static intishexval(char c){ if (c >= 'a' && c <= 'f') return 1; else if (c >= 'A' && c <= 'F') return 1; else if (c >= '0' && c <= '9') return 1; return 0;} /* * Converts a character to its hex value. */static inthexval(char c){ if (c >= 'a' && c <= 'f') return c - 'a' + 10; else if (c >= 'A' && c <= 'F') return c - 'A' + 10; else if (c >= '0' && c <= '9') return c - '0'; lfatal(curloc, "passed non-hex value '%c' to where hex was expected", c); return -1;} /* \u{abc} */static int32_tunichar(void){ uint32_t v; int c; /* we've already seen the \u */ if (next() != '{') lfatal(curloc, "\\u escape sequence without initial '{'"); v = 0; while (ishexval(peek())) { c = next(); v = 16 * v + hexval(c); if (v > 0x10FFFF) lfatal(curloc, "invalid codepoint for \\u escape sequence"); } if (next() != '}') lfatal(curloc, "\\u escape sequence without ending '}'"); return v;} /* * decodes an escape code. These are * shared between strings and characters. * Unknown escape codes are ignored. */static intdecode(char **buf, size_t *len, size_t *sz){ char c, c1, c2; int32_t v; c = next(); /* we've already seen the '\' */ switch (c) { case 'u': v = unichar(); appendc(buf, len, sz, v); return v; case 'x': /* arbitrary hex */ c1 = next(); if (!isxdigit(c1)) lfatal(curloc, "expected hex digit, got %c", c1); c2 = next(); if (!isxdigit(c2)) lfatal(curloc, "expected hex digit, got %c", c1); v = 16 * hexval(c1) + hexval(c2); break; case 'n': v = '\n'; break; case 'r': v = '\r'; break; case 't': v = '\t'; break; case 'b': v = '\b'; break; case '"': v = '\"'; break; case '\'': v = '\''; break; case 'v': v = '\v'; break; case '\\': v = '\\'; break; case '0': v = '\0'; break; default: lfatal(curloc, "unknown escape code \\%c", c); } append(buf, len, sz, v); return v;} static Tok *strlit(void){ Tok *t; int c; size_t len, sz; char *buf; assert(next() == '"'); buf = NULL; len = 0; sz = 0; while (1) { c = next(); /* we don't unescape here, but on output */ if (c == '"') break; else if (c == End) lfatal(curloc, "Unexpected EOF within string"); else if (c == '\n') lfatal(curloc, "Newlines not allowed in strings"); else if (c == '\\') decode(&buf, &len, &sz); else append(&buf, &len, &sz, c); }; t = mktok(Tstrlit); t->strval.len = len; /* null terminator should not count towards length */ append(&buf, &len, &sz, '\0'); t->strval.buf = buf; t->id = buf; return t;} static uint32_treadutf(char c, char **buf, size_t *buflen, size_t *sz){ size_t i, len; uint32_t val; if ((c & 0x80) == 0) len = 1; else if ((c & 0xe0) == 0xc0) len = 2; else if ((c & 0xf0) == 0xe0) len = 3; else if ((c & 0xf8) == 0xf0) len = 4; else lfatal(curloc, "Invalid utf8 encoded character constant"); val = c & ((1 << (8 - len)) - 1); append(buf, buflen, sz, c); for (i = 1; i < len; i++) { c = next(); if ((c & 0xc0) != 0x80) lfatal(curloc, "Invalid utf8 codepoint in character literal"); val = (val << 6) | (c & 0x3f); append(buf, buflen, sz, c); } return val;} static Tok *charlit(void){ Tok *t; int c; uint32_t val; size_t len, sz; char *buf; assert(next() == '\''); buf = NULL; len = 0; sz = 0; val = 0; c = next(); if (c == End) lfatal(curloc, "Unexpected EOF within char lit"); else if (c == '\n') lfatal(curloc, "Newlines not allowed in char lit"); else if (c == '\\') val = decode(&buf, &len, &sz); else val = readutf(c, &buf, &len, &sz); append(&buf, &len, &sz, '\0'); if (next() != '\'') lfatal(curloc, "Character constant with multiple characters"); Lines 1–500 of 912. Load more
#include <stdlib.h> #include <stdio.h> #include <stdarg.h> #include <inttypes.h> #include <ctype.h> #include <string.h> #include <assert.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <unistd.h> #include "util.h" #include "parse.h" #include "gram.h" #define End (-1) char *filename; Srcloc curloc; Tok *curtok; /* the file contents are stored globally */ static int fidx; static int fbufsz; static char *fbuf; static int peekn(int n) { if (fidx + n >= fbufsz) return End; else return fbuf[fidx + n]; } static int peek(void) { return peekn(0); } static int next(void) { int c; c = peek(); fidx++; return c; } static void unget(void) { fidx--; assert(fidx >= 0); } /* * Consumes the character iff * the character is equal to 'c'. * returns true if there was a match, * false otherwise. */ static int match(char c) { if (peek() == c) { next(); return 1; } else { return 0; } } static Tok * mktok(int tt) { Tok *t; t = zalloc(sizeof(Tok)); t->type = tt; t->loc = curloc; return t; } static int identchar(int c) { return isalnum(c) || c == '_' || c == '$'; } static void eatcomment(void) { int depth; int startln; int c; depth = 0; startln = curloc.line; while (1) { c = next(); switch (c) { /* enter level of nesting */ case '/': if (match('*')) depth++; break; /* leave level of nesting */ case '*': if (match('/')) depth--; break; /* have to keep line numbers synced */ case '\n': curloc.line++; break; case End: lfatal(curloc, "File ended within comment starting at line %d", startln); break; } if (depth == 0) break; } } /* * Consumes all forms of whitespace, * including comments. If we are in a * state where we should ignore newlines, * we also consume '\n'. ';' is still * accepted as a line ending. */ static void eatspace(void) { int c; int ignorenl; ignorenl = 0; while (1) { c = peek(); if (!ignorenl && c == '\n') { break; } else if (c == '\\') { ignorenl = 1; next(); } else if (ignorenl && c == '\n') { next(); curloc.line++; ignorenl = 0; } else if (isspace(c)) { next(); } else if (c == '/' && peekn(1) == '*') { eatcomment(); } else if (c == '/' && peekn(1) == '/') { while (peek() != End && peek() != '\n') next(); } else { break; } } } /* * Decides if an identifier is a * keyword or not. Returns the * token type to use for the * identifier. */ static int kwd(char *s) { static const struct { char *kw; int tt; } kwmap[] = { {"$noret", Tattr}, {"_", Tgap}, {"auto", Tauto}, {"break", Tbreak}, {"const", Tconst}, {"continue", Tcontinue}, {"elif", Telif}, {"else", Telse}, {"extern", Tattr}, {"false", Tboollit}, {"for", Tfor}, {"generic", Tgeneric}, {"goto", Tgoto}, {"if", Tif}, {"impl", Timpl}, {"match", Tmatch}, {"pkg", Tpkg}, {"pkglocal", Tattr}, {"sizeof", Tsizeof}, {"struct", Tstruct}, {"trait", Ttrait}, {"true", Tboollit}, {"type", Ttype}, {"union", Tunion}, {"use", Tuse}, {"var", Tvar}, {"void", Tvoidlit}, {"while", Twhile}, }; size_t min, max, mid; int cmp; min = 0; max = sizeof(kwmap) / sizeof(kwmap[0]); while (max > min) { mid = (max + min) / 2; cmp = strcmp(s, kwmap[mid].kw); if (cmp == 0) return kwmap[mid].tt; else if (cmp > 0) min = mid + 1; else if (cmp < 0) max = mid; } return Tident; } static int identstr(char *buf, size_t sz) { size_t i; char c; i = 0; for (c = peek(); i < sz && identchar(c); c = peek()) { next(); buf[i++] = c; } buf[i] = '\0'; return i; } static Tok * kwident(void) { char buf[1024]; Tok *t; if (!identstr(buf, sizeof buf)) return NULL; t = mktok(kwd(buf)); t->id = strdup(buf); return t; } static void append(char **buf, size_t *len, size_t *sz, int c) { if (!*sz) { *sz = 16; *buf = malloc(*sz); } if (*len == *sz - 1) { *sz = *sz * 2; *buf = realloc(*buf, *sz); } buf[0][*len] = c; (*len)++; } static void encode(char *buf, size_t len, uint32_t c) { int mark; size_t i; assert(len > 0 && len < 5); if (len == 1) mark = 0; else mark = (((1 << (8 - len)) - 1) ^ 0xff); for (i = len - 1; i > 0; i--) { buf[i] = (c & 0x3f) | 0x80; c >>= 6; } buf[0] = (c | mark); } /* * Appends a unicode codepoint 'c' to a growable buffer 'buf', * resizing if needed. */ static void appendc(char **buf, size_t *len, size_t *sz, uint32_t c) { size_t i, charlen; char charbuf[5] = {0}; if (c < 0x80) charlen = 1; else if (c < 0x800) charlen = 2; else if (c < 0x10000) charlen = 3; else if (c < 0x200000) charlen = 4; else lfatal(curloc, "invalid utf character '\\u{%x}'", c); encode(charbuf, charlen, c); for (i = 0; i < charlen; i++) append(buf, len, sz, charbuf[i]); } static int ishexval(char c) { if (c >= 'a' && c <= 'f') return 1; else if (c >= 'A' && c <= 'F') return 1; else if (c >= '0' && c <= '9') return 1; return 0; } /* * Converts a character to its hex value. */ static int hexval(char c) { if (c >= 'a' && c <= 'f') return c - 'a' + 10; else if (c >= 'A' && c <= 'F') return c - 'A' + 10; else if (c >= '0' && c <= '9') return c - '0'; lfatal(curloc, "passed non-hex value '%c' to where hex was expected", c); return -1; } /* \u{abc} */ static int32_t unichar(void) { uint32_t v; int c; /* we've already seen the \u */ if (next() != '{') lfatal(curloc, "\\u escape sequence without initial '{'"); v = 0; while (ishexval(peek())) { c = next(); v = 16 * v + hexval(c); if (v > 0x10FFFF) lfatal(curloc, "invalid codepoint for \\u escape sequence"); } if (next() != '}') lfatal(curloc, "\\u escape sequence without ending '}'"); return v; } /* * decodes an escape code. These are * shared between strings and characters. * Unknown escape codes are ignored. */ static int decode(char **buf, size_t *len, size_t *sz) { char c, c1, c2; int32_t v; c = next(); /* we've already seen the '\' */ switch (c) { case 'u': v = unichar(); appendc(buf, len, sz, v); return v; case 'x': /* arbitrary hex */ c1 = next(); if (!isxdigit(c1)) lfatal(curloc, "expected hex digit, got %c", c1); c2 = next(); if (!isxdigit(c2)) lfatal(curloc, "expected hex digit, got %c", c1); v = 16 * hexval(c1) + hexval(c2); break; case 'n': v = '\n'; break; case 'r': v = '\r'; break; case 't': v = '\t'; break; case 'b': v = '\b'; break; case '"': v = '\"'; break; case '\'': v = '\''; break; case 'v': v = '\v'; break; case '\\': v = '\\'; break; case '0': v = '\0'; break; default: lfatal(curloc, "unknown escape code \\%c", c); } append(buf, len, sz, v); return v; } static Tok * strlit(void) { Tok *t; int c; size_t len, sz; char *buf; assert(next() == '"'); buf = NULL; len = 0; sz = 0; while (1) { c = next(); /* we don't unescape here, but on output */ if (c == '"') break; else if (c == End) lfatal(curloc, "Unexpected EOF within string"); else if (c == '\n') lfatal(curloc, "Newlines not allowed in strings"); else if (c == '\\') decode(&buf, &len, &sz); else append(&buf, &len, &sz, c); }; t = mktok(Tstrlit); t->strval.len = len; /* null terminator should not count towards length */ append(&buf, &len, &sz, '\0'); t->strval.buf = buf; t->id = buf; return t; } static uint32_t readutf(char c, char **buf, size_t *buflen, size_t *sz) { size_t i, len; uint32_t val; if ((c & 0x80) == 0) len = 1; else if ((c & 0xe0) == 0xc0) len = 2; else if ((c & 0xf0) == 0xe0) len = 3; else if ((c & 0xf8) == 0xf0) len = 4; else lfatal(curloc, "Invalid utf8 encoded character constant"); val = c & ((1 << (8 - len)) - 1); append(buf, buflen, sz, c); for (i = 1; i < len; i++) { c = next(); if ((c & 0xc0) != 0x80) lfatal(curloc, "Invalid utf8 codepoint in character literal"); val = (val << 6) | (c & 0x3f); append(buf, buflen, sz, c); } return val; } static Tok * charlit(void) { Tok *t; int c; uint32_t val; size_t len, sz; char *buf; assert(next() == '\''); buf = NULL; len = 0; sz = 0; val = 0; c = next(); if (c == End) lfatal(curloc, "Unexpected EOF within char lit"); else if (c == '\n') lfatal(curloc, "Newlines not allowed in char lit"); else if (c == '\\') val = decode(&buf, &len, &sz); else val = readutf(c, &buf, &len, &sz); append(&buf, &len, &sz, '\0'); if (next() != '\'') lfatal(curloc, "Character constant with multiple characters");