From 5b075b33748ae84320c09a0cbb500083879fd757 Mon Sep 17 00:00:00 2001 From: nella Date: Tue, 29 Sep 2026 07:23:41 +0200 Subject: [PATCH 1/3] Split preprocessor. --- src/compiler/lex/char_class.h | 44 +-- src/compiler/lex/lexer.cpp | 41 +-- src/compiler/lex/lexer.h | 1 + src/compiler/lex/preprocess.cpp | 499 ++++++++++++-------------- src/compiler/lex/preprocess_detail.h | 107 ++++-- src/compiler/lex/preprocess_eval.cpp | 55 ++- src/compiler/lex/preprocess_lex.cpp | 418 ++++++++++----------- src/compiler/lex/preprocess_macro.cpp | 307 ++++++++-------- src/compiler/lex/token_stream.cpp | 92 ++--- src/compiler/lex/token_stream.h | 7 + 10 files changed, 736 insertions(+), 835 deletions(-) diff --git a/src/compiler/lex/char_class.h b/src/compiler/lex/char_class.h index a713a344..e4d0e1b0 100644 --- a/src/compiler/lex/char_class.h +++ b/src/compiler/lex/char_class.h @@ -23,43 +23,15 @@ namespace rat::cc { return -1; } inline B32 simpleEscape(C8 e, U8& out) { - switch(e) { - case 'n': - out = '\n'; - return true; - case 't': - out = '\t'; - return true; - case 'r': - out = '\r'; - return true; - case '\\': - out = '\\'; - return true; - case '\'': - out = '\''; - return true; - case '"': - out = '"'; - return true; - case 'a': - out = '\a'; - return true; - case 'b': - out = '\b'; - return true; - case 'f': - out = '\f'; - return true; - case 'v': - out = '\v'; - return true; - case 'e': - out = 27; - return true; - default: - return false; + constexpr C8 kFrom[] = {'n', 't', 'r', '\\', '\'', '"', 'a', 'b', 'f', 'v', 'e'}; + constexpr U8 kTo[] = {'\n', '\t', '\r', '\\', '\'', '"', '\a', '\b', '\f', '\v', 27}; + for(U32 i = 0; i < sizeof kTo; ++i) { + if(kFrom[i] == e) { + out = kTo[i]; + return true; + } } + return false; } } // namespace rat::cc diff --git a/src/compiler/lex/lexer.cpp b/src/compiler/lex/lexer.cpp index 1a3f89e2..0af62464 100644 --- a/src/compiler/lex/lexer.cpp +++ b/src/compiler/lex/lexer.cpp @@ -27,7 +27,8 @@ namespace rat::cc { "kTokNames must cover every TokKind"); B32 validIntSuffix(const C8* s, U32 n) { - B32 haveU = false, haveL = false; + B32 haveU = false; + B32 haveL = false; U32 i = 0; while(i < n) { C8 c = s[i]; @@ -49,7 +50,8 @@ namespace rat::cc { } B32 validFloatSuffix(const C8* s, U32 n) { - B32 haveSize = false, imag = false; + B32 haveSize = false; + B32 imag = false; for(U32 i = 0; i < n; ++i) { C8 c = s[i]; if(c == 'f' || c == 'F' || c == 'l' || c == 'L') { @@ -89,6 +91,19 @@ namespace rat::cc { return true; } + B32 scanExponent(const C8* s, U32 n, U32& i, const C8* missing, String& err) { + ++i; + if(charAt(s, n, i) == '+' || charAt(s, n, i) == '-') + ++i; + if(!isDigit(charAt(s, n, i))) { + err = missing; + return false; + } + while(isDigit(charAt(s, n, i))) + ++i; + return true; + } + B32 scanHexNumber(const C8* s, U32 n, U32& i, B32& isFloat, String& err) { i += 2; // "0x" B32 anyDigits = false; @@ -116,16 +131,7 @@ namespace rat::cc { return false; } isFloat = true; - ++i; - if(charAt(s, n, i) == '+' || charAt(s, n, i) == '-') - ++i; - if(!isDigit(charAt(s, n, i))) { - err = "expected digits in binary exponent"; - return false; - } - while(isDigit(charAt(s, n, i))) - ++i; - return true; + return scanExponent(s, n, i, "expected digits in binary exponent", err); } B32 scanDecNumber(const C8* s, U32 n, U32& i, B32& isFloat, String& err) { @@ -141,16 +147,7 @@ namespace rat::cc { if(c != 'e' && c != 'E') return true; isFloat = true; - ++i; - if(charAt(s, n, i) == '+' || charAt(s, n, i) == '-') - ++i; - if(!isDigit(charAt(s, n, i))) { - err = "expected digits in exponent"; - return false; - } - while(isDigit(charAt(s, n, i))) - ++i; - return true; + return scanExponent(s, n, i, "expected digits in exponent", err); } B32 scanNumber(const C8* s, U32 n, U32& i, TokKind& kind, String& err) { diff --git a/src/compiler/lex/lexer.h b/src/compiler/lex/lexer.h index 1878294c..fa9eb2c0 100644 --- a/src/compiler/lex/lexer.h +++ b/src/compiler/lex/lexer.h @@ -126,6 +126,7 @@ namespace rat::cc { // scanners over one token spelling; advance i, false on error with err set B32 scanSuffix(const C8* s, U32 n, U32& i, B32 isFloat, TokKind& kind, String& err); + B32 scanExponent(const C8* s, U32 n, U32& i, const C8* missing, String& err); B32 scanHexNumber(const C8* s, U32 n, U32& i, B32& isFloat, String& err); B32 scanDecNumber(const C8* s, U32 n, U32& i, B32& isFloat, String& err); U32 encodingPrefix(const C8* s, U32 n); diff --git a/src/compiler/lex/preprocess.cpp b/src/compiler/lex/preprocess.cpp index fa723d71..8c9a2154 100644 --- a/src/compiler/lex/preprocess.cpp +++ b/src/compiler/lex/preprocess.cpp @@ -8,6 +8,52 @@ namespace rat::cc { namespace detail { + String withSlash(String dir) { + if(!dir.empty() && dir.back() != '/') + dir += '/'; + return dir; + } + + String joinSpelling(PpSpan toks) { + String s; + for(U64 k = 0; k < toks.size(); ++k) { + if(k && toks[k].spaceBefore) + s += ' '; + s += *toks[k].text; + } + return s; + } + + List stampDefs() { + String date = "\"??? ?? ????\""; + String time = "\"??:??:??\""; + time_t now = ::time(nullptr); + if(struct tm* lt = std::localtime(&now)) { + C8 buf[64]; + std::strftime(buf, sizeof buf, "%b %e %Y", lt); + date = String("\"") + buf + "\""; + std::strftime(buf, sizeof buf, "%H:%M:%S", lt); + time = String("\"") + buf + "\""; + } + return {"__DATE__ " + date, "__TIME__ " + time}; + } + + // clang-format off + const C8* const kBuiltinDefs[] = { + "__STDC__ 1", "__STDC_HOSTED__ 1", "__STDC_VERSION__ 199901L", + // GNU C extensions + "__attribute__(x)", "__attribute(x)", "__asm__ asm", "__asm asm", "__restrict", + "__restrict__ restrict", "__inline inline", "__inline__ inline", "__volatile__ volatile", + "__volatile volatile", "__extension__", "__alignof__ _Alignof", "__alignof _Alignof", + "__signed__ signed", "__signed signed", "__const const", "__thread", + // GCC extended floating types + "_Float32 float", "_Float32x double", "_Float64 double", "_Float64x double", + "_Float128 double", "_Float128x double", + // GCC 128-bit integers + "__int128 long long", + }; + // clang-format on + void Preprocessor::fail(const String& m) { if(ok) { ok = false; @@ -30,71 +76,42 @@ namespace rat::cc { return true; } - void Preprocessor::doInclude(PpSpan restIn, const String& curDir, B32 next) { - B32 angled = false; - String fname; - - auto reconstruct = [&](PpSpan toks) -> B32 { - if(toks.empty()) - return false; - if(toks[0].kind == Pk::Str) { - fname = unquote(*toks[0].text); - angled = false; - return true; - } - if(isPunct(toks[0], "<")) { - angled = true; - fname.clear(); - for(U64 k = 1; k < toks.size(); ++k) { - if(isPunct(toks[k], ">")) - return true; - if(k > 1 && toks[k].spaceBefore) - fname += ' '; - fname += *toks[k].text; - } - return false; // missing > - } + B32 Preprocessor::includeName(PpSpan toks, String& fname, B32& angled) { + if(toks.empty()) return false; - }; - - List expanded; - if(!reconstruct(restIn)) { - // try macro expansion of the operand - expanded = expand(restIn); - if(!reconstruct(PpSpan(expanded))) { - fail("#include expects \"file\" or "); - return; + if(toks[0].kind == Pk::Str) { + fname = unquote(*toks[0].text); + angled = false; + return true; + } + if(!isPunct(toks[0], "<")) + return false; + for(U64 k = 1; k < toks.size(); ++k) { + if(isPunct(toks[k], ">")) { + fname = joinSpelling(PpSpan(toks.b + 1, toks.b + k)); + angled = true; + return true; } } + return false; // missing > + } - List tries; - if(isAbsPath(fname)) { - tries.push_back(fname); - } else { - U64 startDir = 0, best = 0; - if(next) { - for(U64 k = 0; k < opts.includeDirs.size(); ++k) { - String d = opts.includeDirs[k]; - if(!d.empty() && d.back() != '/') - d += '/'; - if(d.size() > best && d.size() <= curDir.size() && - curDir.compare(0, d.size(), d) == 0) { - best = d.size(); - startDir = k + 1; - } - } - } else if(!angled) { - tries.push_back(curDir.empty() ? fname : curDir + fname); - } - for(U64 k = startDir; k < opts.includeDirs.size(); ++k) { - String base = opts.includeDirs[k]; - if(!base.empty() && base.back() != '/') - base += '/'; - tries.push_back(base + fname); + U64 Preprocessor::nextIncludeDir(const String& curDir) { + U64 startDir = 0; + U64 best = 0; + for(U64 k = 0; k < opts.includeDirs.size(); ++k) { + String d = withSlash(opts.includeDirs[k]); + if(d.size() > best && d.size() <= curDir.size() && curDir.compare(0, d.size(), d) == 0) { + best = d.size(); + startDir = k + 1; } } + return startDir; + } - String found, content; + void Preprocessor::enterInclude(const List& tries, const String& fname) { + String found; + String content; for(const String& p : tries) { if(readFile(p, content)) { found = p; @@ -116,6 +133,29 @@ namespace rat::cc { --includeDepth; } + void Preprocessor::doInclude(PpSpan rest, const String& path, B32 next) { + B32 angled = false; + String fname; + B32 named = includeName(rest, fname, angled); + if(!named) // try macro expansion of the operand + named = includeName(expand(rest), fname, angled); + if(!named) { + fail("#include expects \"file\" or "); + return; + } + String curDir = dirOf(path); + List tries; + if(isAbsPath(fname)) { + tries.push_back(fname); + } else { + if(!next && !angled) + tries.push_back(curDir + fname); + for(U64 k = next ? nextIncludeDir(curDir) : 0; k < opts.includeDirs.size(); ++k) + tries.push_back(withSlash(opts.includeDirs[k]) + fname); + } + enterInclude(tries, fname); + } + void Preprocessor::doLine(PpSpan restIn, U32 physicalNextLine) { List rest = expand(restIn); if(rest.empty() || rest[0].kind != Pk::Num) { @@ -128,40 +168,45 @@ namespace rat::cc { fileName = unquote(*rest[1].text); } - void Preprocessor::doPragma(PpSpan rest, const String& path) { - if(rest.size() == 1 && rest[0].kind == Pk::Id && *rest[0].text == "once") { - pragmaOnce.insert(path); - } else if(rest.size() >= 1 && rest[0].kind == Pk::Id && - (*rest[0].text == "push_macro" || *rest[0].text == "pop_macro")) { - String mname; - for(U64 k = 1; k < rest.size(); ++k) { - if(rest[k].kind == Pk::Str) { - mname = unquote(*rest[k].text); - break; - } - } - if(!mname.empty()) { - const String* key = intern(mname); - if(*rest[0].text == "push_macro") { - auto it = macros.find(key); - SavedMacro sv; - sv.defined = it != macros.end(); - if(sv.defined) - sv.macro = it->second; - macroStack[key].push_back(std::move(sv)); - } else { - auto it = macroStack.find(key); - if(it != macroStack.end() && !it->second.empty()) { - SavedMacro sv = std::move(it->second.back()); - it->second.pop_back(); - if(sv.defined) - macros[key] = std::move(sv.macro); - else - macros.erase(key); - } - } + void Preprocessor::pushPopMacro(PpSpan rest, B32 push) { + String mname; + for(U64 k = 1; k < rest.size(); ++k) { + if(rest[k].kind == Pk::Str) { + mname = unquote(*rest[k].text); + break; } } + if(mname.empty()) + return; + const String* key = intern(mname); + if(push) { + auto it = macros.find(key); + SavedMacro sv; + sv.defined = it != macros.end(); + if(sv.defined) + sv.macro = it->second; + macroStack[key].push_back(std::move(sv)); + return; + } + auto it = macroStack.find(key); + if(it == macroStack.end() || it->second.empty()) + return; + SavedMacro sv = std::move(it->second.back()); + it->second.pop_back(); + if(sv.defined) + macros[key] = std::move(sv.macro); + else + macros.erase(key); + } + + void Preprocessor::doPragma(PpSpan rest, const String& path) { + if(rest.empty() || rest[0].kind != Pk::Id) + return; + const String& what = *rest[0].text; + if(what == "once" && rest.size() == 1) + pragmaOnce.insert(path); + else if(what == "push_macro" || what == "pop_macro") + pushPopMacro(rest, what == "push_macro"); } String Preprocessor::destringize(const String& lit) { @@ -223,70 +268,85 @@ namespace rat::cc { return stack.empty() ? true : stack.back().active; } + void Preprocessor::pushCond(const String& name, PpSpan rest, List& stack) { + B32 parent = condActive(stack); + B32 cond = false; + if(parent && name == "if") + cond = evalExpr(rest); + else if(parent && (rest.empty() || rest[0].kind != Pk::Id)) + fail("#" + name + " expects an identifier"); + else if(parent) + cond = isDefined(rest[0].text) == (name == "ifdef"); + B32 active = parent && cond; + stack.push_back({parent, active, active}); + } + B32 Preprocessor::handleConditional(const String& name, PpSpan rest, List& stack) { if(name == "if" || name == "ifdef" || name == "ifndef") { - B32 parent = condActive(stack); - B32 cond = false; - if(parent) { - if(name == "if") { - cond = evalExpr(rest); - } else { - if(rest.empty() || rest[0].kind != Pk::Id) { - fail("#" + name + " expects an identifier"); - } else { - B32 def = isDefined(rest[0].text); - cond = (name == "ifdef") ? def : !def; - } - } - } - B32 active = parent && cond; - stack.push_back({parent, active, active}); + pushCond(name, rest, stack); return true; } - if(name == "elif") { - if(stack.empty()) { - fail("#elif without #if"); - return true; - } - Cond& c = stack.back(); - if(c.sawElse) { - fail("#elif after #else"); - return true; - } - if(c.parentActive && !c.taken) { - B32 cond = evalExpr(rest); - c.active = cond; - if(cond) - c.taken = true; - } else { - c.active = false; - } + if(name != "elif" && name != "else" && name != "endif") + return false; + if(stack.empty()) { + fail("#" + name + " without #if"); return true; } - if(name == "else") { - if(stack.empty()) { - fail("#else without #if"); - return true; - } - Cond& c = stack.back(); - if(c.sawElse) { - fail("#else after #else"); - return true; - } + Cond& c = stack.back(); + if(name == "endif") { + stack.pop_back(); + } else if(c.sawElse) { + fail("#" + name + " after #else"); + } else if(name == "elif") { + c.active = c.parentActive && !c.taken && evalExpr(rest); + c.taken |= c.active; + } else { c.sawElse = true; c.active = c.parentActive && !c.taken; c.taken = true; - return true; } - if(name == "endif") { - if(stack.empty()) { - fail("#endif without #if"); - return true; - } - stack.pop_back(); - return true; + return true; + } + + void Preprocessor::doDirective(PpSpan line, U32 phys, const String& path, List& stack) { + const PpToken& dir = line[0]; + if(dir.kind == Pk::Num) { + if(condActive(stack)) + doLine(line, phys); + return; } - return false; + if(dir.kind != Pk::Id) { + if(condActive(stack)) + fail("invalid preprocessing directive"); + return; + } + const String& name = *dir.text; + PpSpan rest(line.b + 1, line.e); + if(handleConditional(name, rest, stack) || !condActive(stack)) + return; + if(name == "define") + doDefine(rest); + else if(name == "undef") + doUndef(rest); + else if(name == "include" || name == "include_next") + doInclude(rest, path, name == "include_next"); + else if(name == "error") + fail(path + ": #error " + joinSpelling(rest)); + else if(name == "pragma") + doPragma(rest, path); + else if(name == "line") + doLine(rest, phys); + else + fail("invalid preprocessing directive #" + name); + } + + void Preprocessor::doUndef(PpSpan rest) { + if(rest.empty() || rest[0].kind != Pk::Id) + fail("#undef expects an identifier"); + else if(rest[0].text == idDefined) + fail("'defined' cannot be used as a macro name"); + else + macros.erase(rest[0].text); } void Preprocessor::runFile(const String& path, const String& source) { @@ -301,7 +361,6 @@ namespace rat::cc { String savedFile = fileName; lineDelta = 0; fileName = path; - String curDir = dirOf(path); List stack; List textBuf; @@ -315,73 +374,17 @@ namespace rat::cc { i = j; const PpToken& first = toks[start]; - B32 isDirective = isPunct(first, "#") && first.bol; - - if(!isDirective) { + if(!isPunct(first, "#") || !first.bol) { if(condActive(stack)) textBuf.insert(textBuf.end(), toks.begin() + start, toks.begin() + j); continue; } flush(textBuf); - - U64 d = start + 1; - if(d >= j) // null directive - continue; - - const PpToken& dir = toks[d]; - // physical line after the directive, for #line - U32 phys = j < n ? toks[j].line : dir.line + 1; - - if(dir.kind == Pk::Num) { - if(condActive(stack)) - doLine(PpSpan(toks.data() + d, toks.data() + j), phys); - continue; - } - - if(dir.kind != Pk::Id) { - if(condActive(stack)) - fail("invalid preprocessing directive"); - continue; - } - - const String& name = *dir.text; - PpSpan rest(toks.data() + d + 1, toks.data() + j); - - if(handleConditional(name, rest, stack)) - continue; - - if(!condActive(stack)) + if(start + 1 == j) // null directive continue; - - if(name == "define") { - doDefine(rest); - } else if(name == "undef") { - if(rest.empty() || rest[0].kind != Pk::Id) - fail("#undef expects an identifier"); - else if(rest[0].text == idDefined) - fail("'defined' cannot be used as a macro name"); - else - macros.erase(rest[0].text); - } else if(name == "include") { - doInclude(rest, curDir); - } else if(name == "include_next") { - doInclude(rest, curDir, true); - } else if(name == "error") { - String msg; - for(U64 k = 0; k < rest.size(); ++k) { - if(k && rest[k].spaceBefore) - msg += ' '; - msg += *rest[k].text; - } - fail(path + ": #error " + msg); - } else if(name == "pragma") { - doPragma(rest, path); - } else if(name == "line") { - doLine(rest, phys); - } else { - fail("invalid preprocessing directive #" + name); - } + U32 phys = j < n ? toks[j].line : toks[start + 1].line + 1; + doDirective(PpSpan(toks.data() + start + 1, toks.data() + j), phys, path, stack); } if(ok && !stack.empty()) @@ -393,58 +396,16 @@ namespace rat::cc { fileName = savedFile; } + void Preprocessor::defineFragment(const String& text, const C8* file) { + doDefine(lexFragment(text, intern(file))); + } + void Preprocessor::installBuiltins() { - struct Stamp { - String date = "\"??? ?? ????\""; - String time = "\"??:??:??\""; - }; - static const Stamp stamp = [] { - Stamp s; - time_t now = ::time(nullptr); - struct tm* lt = std::localtime(&now); - if(lt) { - C8 buf[64]; - std::strftime(buf, sizeof buf, "%b %e %Y", lt); - s.date = String("\"") + buf + "\""; - std::strftime(buf, sizeof buf, "%H:%M:%S", lt); - s.time = String("\"") + buf + "\""; - } - return s; - }(); - defineSimple("__DATE__", stamp.date); - defineSimple("__TIME__", stamp.time); - defineSimple("__STDC__", "1"); - defineSimple("__STDC_HOSTED__", "1"); - defineSimple("__STDC_VERSION__", "199901L"); - - // GNU C extensions - auto defineFrag = [&](const C8* text) { doDefine(lexFragment(text, intern(""))); }; - defineFrag("__attribute__(x)"); - defineFrag("__attribute(x)"); - defineFrag("__asm__ asm"); - defineFrag("__asm asm"); - defineFrag("__restrict"); - defineFrag("__restrict__ restrict"); - defineFrag("__inline inline"); - defineFrag("__inline__ inline"); - defineFrag("__volatile__ volatile"); - defineFrag("__volatile volatile"); - defineFrag("__extension__"); - defineFrag("__alignof__ _Alignof"); - defineFrag("__alignof _Alignof"); - defineFrag("__signed__ signed"); - defineFrag("__signed signed"); - defineFrag("__const const"); - defineFrag("__thread"); - // GCC extended floating types - defineFrag("_Float32 float"); - defineFrag("_Float32x double"); - defineFrag("_Float64 double"); - defineFrag("_Float64x double"); - defineFrag("_Float128 double"); - defineFrag("_Float128x double"); - // GCC 128-bit integers - defineFrag("__int128 long long"); + static const List stamps = stampDefs(); + for(const String& d : stamps) + defineFragment(d, ""); + for(const C8* d : kBuiltinDefs) + defineFragment(d, ""); } void Preprocessor::applyCommandLine() { @@ -461,6 +422,16 @@ namespace rat::cc { macros.erase(intern(u)); } + B32 Preprocessor::run(const String& path, const String& source, String& errOut) { + installBuiltins(); + applyCommandLine(); + if(ok) + runFile(path, source); + if(!ok) + errOut = err; + return ok; + } + String Preprocessor::serialize() { String s; U64 total = 0; @@ -468,18 +439,12 @@ namespace rat::cc { if(t.kind != Pk::Placemarker && t.kind != Pk::Eof) total += t.text->size() + 1; s.reserve(total + 2); - B32 first = true; for(const PpToken& t : out) { if(t.kind == Pk::Placemarker || t.kind == Pk::Eof) continue; - if(!first) { - if(t.bol) - s += '\n'; - else - s += ' '; - } + if(!s.empty()) + s += t.bol ? '\n' : ' '; s += *t.text; - first = false; } s += '\n'; return s; @@ -489,14 +454,8 @@ namespace rat::cc { B32 preprocess( const String& path, const String& source, const PpOptions& opts, String& out, String& err) { detail::Preprocessor pp(opts); - pp.installBuiltins(); - pp.applyCommandLine(); - if(pp.ok) - pp.runFile(path, source); - if(!pp.ok) { - err = pp.err; + if(!pp.run(path, source, err)) return false; - } out = pp.serialize(); return true; } diff --git a/src/compiler/lex/preprocess_detail.h b/src/compiler/lex/preprocess_detail.h index ca745b5f..4c4b5b79 100644 --- a/src/compiler/lex/preprocess_detail.h +++ b/src/compiler/lex/preprocess_detail.h @@ -72,6 +72,9 @@ namespace rat::cc { U32 line; }; + U64 decodeTrigraph(const String& src, U64 p, C8& c); + U64 newlineLen(const String& s, U64 i); + // trigraph + splice + newline norm in one copy; sparse LineMarks void splice(const String& src, String& out, List& marks); @@ -90,15 +93,42 @@ namespace rat::cc { const PpToken& operator[](U64 i) const { return b[i]; } }; + String withSlash(String dir); + String joinSpelling(PpSpan toks); + List stampDefs(); + struct LexResult { List toks; B32 ok = true; String err; }; + struct PpLexer { + const String& s; + const List& marks; + const String* file; + Interner& in; + LexResult r; + U64 mi = 0; + U32 line = 1; + B32 bolPending = true; + B32 spacePending = false; + + void advanceTo(U64 p); + void push(Pk kind, U64 start, U64 end); + U64 skipComment(U64 i); + U64 wordEnd(U64 j) const; + U64 quotedEnd(U64 i) const; + U64 numberEnd(U64 j) const; + U64 lexToken(U64 i); + LexResult run(); + }; + LexResult lexAll(const String& s, const List& marks, const String* file, Interner& in); + using ArgLists = List>; + // macros struct Macro { B32 isFunc = false; @@ -109,6 +139,8 @@ namespace rat::cc { List body; }; + I32 formalIndex(const Macro& m, const String* s); + // constexpr evaluator struct Val { U64 u = 0; @@ -116,6 +148,7 @@ namespace rat::cc { B32 truth() const { return u != 0; } }; + Val truthVal(B32 r); I64 parseCharConst(const String& txt); Val parseNumLit(const String& txt); @@ -161,21 +194,21 @@ namespace rat::cc { std::deque hideStore; Map> hidePool; // keyed by pointer-FNV, chained // pre-interned, compared by pointer on hot paths - const String* idLine; // "__LINE__" - const String* idFile; // "__FILE__" - const String* idDefined; // "defined" - const String* idVaArgs; // "__VA_ARGS__" - const String* idAttr; // "__attribute__" - const String* idAttr2; // "__attribute" - const String* idNoinline; // "noinline" - const String* idNoinline2; // "__noinline__" - const String* idNoinlineMark; // "__rat_noinline__" - const String* idAligned; // "aligned" - const String* idAligned2; // "__aligned__" - const String* idAlignas; // "_Alignas" - const String* idAlias; // "alias" - const String* idAlias2; // "__alias__" - const String* idAliasMark; // "__rat_alias__" + const String* idLine = interner.intern("__LINE__"); + const String* idFile = interner.intern("__FILE__"); + const String* idDefined = interner.intern("defined"); + const String* idVaArgs = interner.intern("__VA_ARGS__"); + const String* idAttr = interner.intern("__attribute__"); + const String* idAttr2 = interner.intern("__attribute"); + const String* idNoinline = interner.intern("noinline"); + const String* idNoinline2 = interner.intern("__noinline__"); + const String* idNoinlineMark = interner.intern("__rat_noinline__"); + const String* idAligned = interner.intern("aligned"); + const String* idAligned2 = interner.intern("__aligned__"); + const String* idAlignas = interner.intern("_Alignas"); + const String* idAlias = interner.intern("alias"); + const String* idAlias2 = interner.intern("__alias__"); + const String* idAliasMark = interner.intern("__rat_alias__"); String err; B32 ok = true; I64 lineDelta = 0; @@ -189,23 +222,7 @@ namespace rat::cc { }; explicit Preprocessor(const PpOptions& o) - : opts(o) { - idLine = interner.intern("__LINE__"); - idFile = interner.intern("__FILE__"); - idDefined = interner.intern("defined"); - idVaArgs = interner.intern("__VA_ARGS__"); - idAttr = interner.intern("__attribute__"); - idAttr2 = interner.intern("__attribute"); - idNoinline = interner.intern("noinline"); - idNoinline2 = interner.intern("__noinline__"); - idNoinlineMark = interner.intern("__rat_noinline__"); - idAligned = interner.intern("aligned"); - idAligned2 = interner.intern("__aligned__"); - idAlignas = interner.intern("_Alignas"); - idAlias = interner.intern("alias"); - idAlias2 = interner.intern("__alias__"); - idAliasMark = interner.intern("__rat_alias__"); - } + : opts(o) {} B32 isDefined(const String* name) { return macros.count(name) || name == idLine || name == idFile; @@ -227,12 +244,16 @@ namespace rat::cc { void pasteInto(PpToken& dst, const PpToken& r); PpToken stringize(const List& a, B32 spaceBefore); void appendList(List& os, List src, B32 firstSpace); - List substitute(const Macro& m, const List>& args, const HideSet* hs); + void pasteArg(const List& a, B32 commaVa, List& os); + List substitute(const Macro& m, const ArgLists& args, const HideSet* hs); + List applyHideSet(List& os, const HideSet* hs); static U32 matchParen(const List& arg, U32 open); // stack: next token is work.back() - B32 gatherArgs(List& work, List>& raw, PpToken& rparen); - B32 mapArgs(const Macro& m, const List>& raw, List>& actuals); - void emitAttrMarkers(const PpToken& at, const List>& raw, List& os); + B32 gatherArgs(List& work, ArgLists& raw, PpToken& rparen); + B32 mapArgs(const Macro& m, const ArgLists& raw, ArgLists& actuals); + void emitAttrMarkers(const PpToken& at, const ArgLists& raw, List& os); + B32 expandBuiltinName(const PpToken& t, List& os); + void requeueAlignas(const PpToken& at, const ArgLists& raw, List& work); void requeueExpansion(List& r, const PpToken& invoker, List& work); List expand(PpSpan in); @@ -241,20 +262,30 @@ namespace rat::cc { B32 evalExpr(PpSpan toks); // directives + B32 parseMacroParams(PpSpan toks, U64& i, Macro& m); + B32 checkMacroBody(const Macro& m); void doDefine(PpSpan toks); - void defineSimple(const String& name, const String& value); + void doUndef(PpSpan rest); + void defineFragment(const String& text, const C8* file); static String dirOf(const String& path); B32 readFile(const String& path, String& content); - void doInclude(PpSpan restIn, const String& curDir, B32 next = false); + B32 includeName(PpSpan toks, String& fname, B32& angled); + U64 nextIncludeDir(const String& curDir); + void enterInclude(const List& tries, const String& fname); + void doInclude(PpSpan rest, const String& path, B32 next); void doLine(PpSpan restIn, U32 physicalNextLine); + void pushPopMacro(PpSpan rest, B32 push); void doPragma(PpSpan rest, const String& path); String destringize(const String& lit); List applyPragmaOperators(List& toks, const String& path); void flush(List& textBuf); static B32 condActive(const List& stack); + void pushCond(const String& name, PpSpan rest, List& stack); B32 handleConditional(const String& name, PpSpan rest, List& stack); + void doDirective(PpSpan line, U32 phys, const String& path, List& stack); // driver + B32 run(const String& path, const String& source, String& errOut); void runFile(const String& path, const String& source); void installBuiltins(); void applyCommandLine(); diff --git a/src/compiler/lex/preprocess_eval.cpp b/src/compiler/lex/preprocess_eval.cpp index 273d80b1..fcf4afe4 100644 --- a/src/compiler/lex/preprocess_eval.cpp +++ b/src/compiler/lex/preprocess_eval.cpp @@ -51,7 +51,7 @@ namespace rat::cc { if(isOp("!")) { ++i; Val v = parseUnary(); - return Val{v.u ? 0u : 1u, false}; + return truthVal(!v.truth()); } if(isOp("~")) { ++i; @@ -63,32 +63,27 @@ namespace rat::cc { } I32 Eval::prec(const String& op) { - if(op == "*" || op == "/" || op == "%") - return 10; - if(op == "+" || op == "-") - return 9; - if(op == "<<" || op == ">>") - return 8; - if(op == "<" || op == "<=" || op == ">" || op == ">=") - return 7; - if(op == "==" || op == "!=") - return 6; - if(op == "&") - return 5; - if(op == "^") - return 4; - if(op == "|") - return 3; - if(op == "&&") - return 2; - if(op == "||") - return 1; + struct OpPrec { + const C8* op; + I32 prec; + }; + // clang-format off + static const OpPrec kPrec[] = { + {"*", 10}, {"/", 10}, {"%", 10}, {"+", 9}, {"-", 9}, {"<<", 8}, {">>", 8}, + {"<", 7}, {"<=", 7}, {">", 7}, {">=", 7}, {"==", 6}, {"!=", 6}, {"&", 5}, + {"^", 4}, {"|", 3}, {"&&", 2}, {"||", 1}, + }; + // clang-format on + for(const auto& [spelling, p] : kPrec) + if(op == spelling) + return p; return -1; } + Val truthVal(B32 r) { return Val{r ? 1u : 0u, false}; } + Val Eval::apply(const String& op, Val a, Val b) { B32 u = a.isU || b.isU; - auto cmp = [](B32 r) { return Val{r ? 1u : 0u, false}; }; if(op == "*") return Val{a.u * b.u, u}; if(op == "+") @@ -117,17 +112,17 @@ namespace rat::cc { return a.isU ? Val{a.u >> (b.u & kShiftMask), true} : Val{(U64)((I64)a.u >> (b.u & kShiftMask)), false}; if(op == "<") - return cmp(u ? a.u < b.u : (I64)a.u < (I64)b.u); + return truthVal(u ? a.u < b.u : (I64)a.u < (I64)b.u); if(op == "<=") - return cmp(u ? a.u <= b.u : (I64)a.u <= (I64)b.u); + return truthVal(u ? a.u <= b.u : (I64)a.u <= (I64)b.u); if(op == ">") - return cmp(u ? a.u > b.u : (I64)a.u > (I64)b.u); + return truthVal(u ? a.u > b.u : (I64)a.u > (I64)b.u); if(op == ">=") - return cmp(u ? a.u >= b.u : (I64)a.u >= (I64)b.u); + return truthVal(u ? a.u >= b.u : (I64)a.u >= (I64)b.u); if(op == "==") - return cmp(a.u == b.u); + return truthVal(a.u == b.u); if(op == "!=") - return cmp(a.u != b.u); + return truthVal(a.u != b.u); if(op == "&") return Val{a.u & b.u, u}; if(op == "^") @@ -135,9 +130,9 @@ namespace rat::cc { if(op == "|") return Val{a.u | b.u, u}; if(op == "&&") - return cmp(a.truth() && b.truth()); + return truthVal(a.truth() && b.truth()); if(op == "||") - return cmp(a.truth() || b.truth()); + return truthVal(a.truth() || b.truth()); return {}; } diff --git a/src/compiler/lex/preprocess_lex.cpp b/src/compiler/lex/preprocess_lex.cpp index 9ffea8ca..168e04a2 100644 --- a/src/compiler/lex/preprocess_lex.cpp +++ b/src/compiler/lex/preprocess_lex.cpp @@ -2,7 +2,7 @@ #include "lex/char_class.h" -#include +#include namespace rat::cc { namespace detail { @@ -51,70 +51,34 @@ namespace rat::cc { return Pk::Punct; } + U64 decodeTrigraph(const String& src, U64 p, C8& c) { + static const C8 kFrom[] = "=(/)'-"; + static const C8 kTo[] = "#[\\]^{|}~"; + c = src[p]; + if(c != '?' || p + 2 >= src.size() || src[p + 1] != '?') + return 1; + const C8* hit = (const C8*)std::memchr(kFrom, src[p + 2], sizeof kFrom - 1); + if(!hit) + return 1; + c = kTo[hit - kFrom]; + return 3; + } + + U64 newlineLen(const String& s, U64 i) { + if(i < s.size() && s[i] == '\n') + return 1; + return i + 1 < s.size() && s[i] == '\r' && s[i + 1] == '\n' ? 2 : 0; + } + // trigraph + splice + newline norm in one pass void splice(const String& src, String& out, List& marks) { U32 line = 1; - B32 pendingMark = false; U64 i = 0, n = src.size(); out.reserve(n + 1); - - // decode one logical char at p (trigraph-aware); returns length - auto decode = [&](U64 p, C8& c) -> U64 { - c = src[p]; - if(c == '?' && p + 2 < n && src[p + 1] == '?') { - C8 r = 0; - switch(src[p + 2]) { - case '=': - r = '#'; - break; - case '(': - r = '['; - break; - case '/': - r = '\\'; - break; - case ')': - r = ']'; - break; - case '\'': - r = '^'; - break; - case '<': - r = '{'; - break; - case '!': - r = '|'; - break; - case '>': - r = '}'; - break; - case '-': - r = '~'; - break; - default: - break; - } - if(r) { - c = r; - return 3; - } - } - return 1; - }; - - auto emit = [&](C8 c) { - if(pendingMark) { - marks.push_back({(U32)out.size(), line}); - pendingMark = false; - } - out.push_back(c); - }; - const C8* data = src.data(); while(i < n) { // fast path: bulk-copy a run with no splice/trigraph/CR triggers U64 start = i; - U32 startLine = line; // line of first C8 in run while(i < n) { C8 c = data[i]; if(c == '\\' || c == '\r' || c == '?') @@ -123,54 +87,34 @@ namespace rat::cc { ++line; ++i; } - if(i > start) { - if(pendingMark) { - marks.push_back({(U32)out.size(), startLine}); - pendingMark = false; - } + if(i > start) out.append(data + start, i - start); - } if(i >= n) break; C8 c; - U64 len = decode(i, c); - if(c == '\\') { - // backslash-newline splice (backslash may be a trigraph) - U64 j = i + len; - if(j < n) { - if(src[j] == '\n') { - i = j + 1; - ++line; - pendingMark = true; - continue; - } - if(src[j] == '\r' && j + 1 < n && src[j + 1] == '\n') { - i = j + 2; - ++line; - pendingMark = true; - continue; - } - } - emit(c); - i += len; + U64 len = decodeTrigraph(src, i, c); + // backslash-newline splice (backslash may be a trigraph) + U64 nl = c == '\\' ? newlineLen(src, i + len) : 0; + if(nl) { + i += len + nl; + ++line; + marks.push_back({(U32)out.size(), line}); continue; } if(c == '\r') { if(i + 1 < n && src[i + 1] == '\n') ++i; - emit('\n'); + out.push_back('\n'); ++line; ++i; continue; } - emit(c); - if(c == '\n') - ++line; + out.push_back(c); i += len; } if(out.empty() || out.back() != '\n') - emit('\n'); + out.push_back('\n'); } // longest-match punctuator length at s[i]; assumes s[i] starts one @@ -216,53 +160,152 @@ namespace rat::cc { } } - LexResult - lexAll(const String& s, const List& marks, const String* file, Interner& in) { - LexResult r; - U64 i = 0, n = s.size(); - r.toks.reserve(n / 3 + 8); - B32 bolPending = true; - B32 spacePending = false; - U32 line = 1; - U64 mi = 0, mn = marks.size(); + // apply splice line-corrections at offsets <= p + inline void PpLexer::advanceTo(U64 p) { + while(mi < marks.size() && marks[mi].off <= p) { + line = marks[mi].line; + ++mi; + } + } - // apply splice line-corrections at offsets <= p - auto advanceTo = [&](U64 p) { - while(mi < mn && marks[mi].off <= p) { - line = marks[mi].line; - ++mi; + inline void PpLexer::push(Pk kind, U64 start, U64 end) { + std::string_view sv(s.data() + start, end - start); + if(kind == Pk::Punct) { + // digraph canonicalization + if(sv == "<:") + sv = "["; + else if(sv == ":>") + sv = "]"; + else if(sv == "<%") + sv = "{"; + else if(sv == "%>") + sv = "}"; + else if(sv == "%:") + sv = "#"; + else if(sv == "%:%:") + sv = "##"; + } + PpToken t; + t.kind = kind; + t.text = in.intern(sv); + t.spaceBefore = spacePending; + t.bol = bolPending; + t.line = line; + t.file = file; + r.toks.push_back(t); + bolPending = false; + spacePending = false; + } + + U64 PpLexer::skipComment(U64 i) { + U64 n = s.size(); + if(s[i + 1] == '/') { + while(i < n && s[i] != '\n') + ++i; + return i; + } + i += 2; + while(i + 1 < n && !(s[i] == '*' && s[i + 1] == '/')) { + if(s[i] == '\n') { + bolPending = true; + advanceTo(i); + ++line; } - }; + ++i; + } + if(i + 1 >= n) { + r.ok = false; + r.err = "unterminated comment"; + return n; + } + return i + 2; + } - auto pushTok = [&](Pk kind, U64 start, U64 end) { - PpToken t; - t.kind = kind; - std::string_view sv(s.data() + start, end - start); - if(kind == Pk::Punct) { - // digraph canonicalization - if(sv == "<:") - sv = "["; - else if(sv == ":>") - sv = "]"; - else if(sv == "<%") - sv = "{"; - else if(sv == "%>") - sv = "}"; - else if(sv == "%:") - sv = "#"; - else if(sv == "%:%:") - sv = "##"; + U64 PpLexer::wordEnd(U64 j) const { + for(;;) { + if(U64 u = ucnLen(s, j)) + j += u; + else if(j < s.size() && isIdentCont(s[j])) + ++j; + else + return j; + } + } + + U64 PpLexer::quotedEnd(U64 i) const { + C8 quote = s[i]; + U64 j = i + 1, n = s.size(); + while(j < n && s[j] != quote) { + if(s[j] == '\\' && j + 1 < n) + j += 2; + else if(s[j] == '\n') + break; + else + ++j; + } + return j < n && s[j] == quote ? j + 1 : 0; + } + + U64 PpLexer::numberEnd(U64 j) const { + U64 n = s.size(); + for(++j; j < n;) { + C8 d = s[j]; + if((d == 'e' || d == 'E' || d == 'p' || d == 'P') && j + 1 < n && + (s[j + 1] == '+' || s[j + 1] == '-')) + j += 2; + else if(isIdentCont(d) || d == '.') + ++j; + else + break; + } + return j; + } + + U64 PpLexer::lexToken(U64 i) { + U64 n = s.size(); + C8 c = s[i]; + // string / char literal, with optional prefix + U64 pfx = i; + if(isIdentStart(c) || ucnLen(s, i)) { + U64 j = wordEnd(i); + std::string_view word(s.data() + i, j - i); + B32 isPrefix = (word == "L" || word == "u" || word == "U" || word == "u8"); + if(!isPrefix || j >= n || (s[j] != '"' && s[j] != '\'')) { + push(Pk::Id, pfx, j); + return j; } - t.text = in.intern(sv); - t.spaceBefore = spacePending; - t.bol = bolPending; - t.line = line; - t.file = file; - r.toks.push_back(t); - bolPending = false; - spacePending = false; - }; + // fall through into literal lexing starting at the quote + i = j; + c = s[i]; + } + if(c == '"' || c == '\'') { + U64 j = quotedEnd(i); + if(!j) { + r.ok = false; + r.err = "unterminated literal"; + return n; + } + push(c == '"' ? Pk::Str : Pk::Char, pfx, j); + return j; + } + + // pp-number + if(isDigit(c) || (c == '.' && i + 1 < n && isDigit(s[i + 1]))) { + U64 j = numberEnd(i); + push(Pk::Num, i, j); + return j; + } + + // punctuator + U64 plen = punctLen(s, i, n); + push(Pk::Punct, i, i + plen); + return i + plen; + } + + LexResult PpLexer::run() { + U64 i = 0, n = s.size(); + r.toks.reserve(n / 3 + 8); while(i < n) { advanceTo(i); C8 c = s[i]; @@ -278,105 +321,14 @@ namespace rat::cc { ++i; continue; } - if(c == '/' && i + 1 < n && s[i + 1] == '/') { - i += 2; - while(i < n && s[i] != '\n') - ++i; + if(c == '/' && i + 1 < n && (s[i + 1] == '/' || s[i + 1] == '*')) { + i = skipComment(i); spacePending = true; - continue; + } else { + i = lexToken(i); } - if(c == '/' && i + 1 < n && s[i + 1] == '*') { - i += 2; - while(i + 1 < n && !(s[i] == '*' && s[i + 1] == '/')) { - if(s[i] == '\n') { - bolPending = true; - advanceTo(i); - ++line; - } - ++i; - } - if(i + 1 >= n) { - r.ok = false; - r.err = "unterminated comment"; - return r; - } - i += 2; - spacePending = true; - continue; - } - - // string / char literal, with optional prefix - U64 pfx = i; - if(isIdentStart(c) || ucnLen(s, i)) { - U64 j = i; - for(;;) { - if(U64 u = ucnLen(s, j)) - j += u; - else if(j < n && isIdentCont(s[j])) - ++j; - else - break; - } - std::string_view word(s.data() + i, j - i); - B32 isPrefix = (word == "L" || word == "u" || word == "U" || word == "u8"); - if(isPrefix && j < n && (s[j] == '"' || s[j] == '\'')) { - // fall through into literal lexing starting at the quote - i = j; - c = s[i]; - } else { - pushTok(Pk::Id, pfx, j); - i = j; - continue; - } - } - - if(c == '"' || c == '\'') { - C8 quote = c; - U64 j = i + 1; - while(j < n && s[j] != quote) { - if(s[j] == '\\' && j + 1 < n) - j += 2; - else if(s[j] == '\n') - break; - else - ++j; - } - if(j >= n || s[j] != quote) { - r.ok = false; - r.err = "unterminated literal"; - return r; - } - ++j; // include closing quote - pushTok(quote == '"' ? Pk::Str : Pk::Char, pfx, j); - i = j; - continue; - } - - // pp-number - if(isDigit(c) || (c == '.' && i + 1 < n && isDigit(s[i + 1]))) { - U64 j = i + 1; - while(j < n) { - C8 d = s[j]; - if((d == 'e' || d == 'E' || d == 'p' || d == 'P') && j + 1 < n && - (s[j + 1] == '+' || s[j + 1] == '-')) { - j += 2; - continue; - } - if(isIdentCont(d) || d == '.') { - ++j; - continue; - } - break; - } - pushTok(Pk::Num, i, j); - i = j; - continue; - } - - // punctuator - U64 plen = punctLen(s, i, n); - pushTok(Pk::Punct, i, i + plen); - i += plen; + if(!r.ok) + return std::move(r); } PpToken eof; @@ -385,7 +337,13 @@ namespace rat::cc { eof.bol = true; eof.file = file; r.toks.push_back(eof); - return r; + return std::move(r); + } + + LexResult + lexAll(const String& s, const List& marks, const String* file, Interner& in) { + PpLexer lx{s, marks, file, in, {}}; + return lx.run(); } I64 parseCharConst(const String& txt) { @@ -438,11 +396,9 @@ namespace rat::cc { break; acc = acc * (U64)base + (U64)d; } - for(; p < txt.size(); ++p) { - C8 c = (C8)std::tolower(txt[p]); - if(c == 'u') + for(; p < txt.size(); ++p) + if(txt[p] == 'u' || txt[p] == 'U') v.isU = true; - } v.u = acc; return v; } diff --git a/src/compiler/lex/preprocess_macro.cpp b/src/compiler/lex/preprocess_macro.cpp index 99a6b77e..9bde7f8c 100644 --- a/src/compiler/lex/preprocess_macro.cpp +++ b/src/compiler/lex/preprocess_macro.cpp @@ -155,17 +155,31 @@ namespace rat::cc { } } + I32 formalIndex(const Macro& m, const String* s) { + for(U64 k = 0; k < m.formals.size(); ++k) + if(m.formals[k] == s) + return (I32)k; + return -1; + } + + void Preprocessor::pasteArg(const List& a, B32 commaVa, List& os) { + if(commaVa) { + if(a.empty()) + os.pop_back(); // drop the comma + else + appendList(os, a, true); // keep comma, no paste + return; + } + if(a.empty()) + return; // paste with empty operand -> previous unchanged + pasteInto(os.back(), a.front()); + os.insert(os.end(), a.begin() + 1, a.end()); + } + List - Preprocessor::substitute(const Macro& m, const List>& args, const HideSet* hs) { + Preprocessor::substitute(const Macro& m, const ArgLists& args, const HideSet* hs) { List os; const List& body = m.body; - auto idxOf = [&](const String* s) -> I32 { - for(U64 k = 0; k < m.formals.size(); ++k) - if(m.formals[k] == s) - return (I32)k; - return -1; - }; - U64 i = 0; while(i < body.size()) { const PpToken& T = body[i]; @@ -174,7 +188,7 @@ namespace rat::cc { // # param -> stringize if(m.isFunc && isHash && i + 1 < body.size()) { - I32 p = idxOf(body[i + 1].text); + I32 p = formalIndex(m, body[i + 1].text); if(p >= 0) { os.push_back(stringize(args[p], T.spaceBefore)); i += 2; @@ -185,23 +199,11 @@ namespace rat::cc { // ## token -> paste onto the previous token if(isPaste && !os.empty() && i + 1 < body.size()) { const PpToken& R = body[i + 1]; - I32 p = idxOf(R.text); + I32 p = formalIndex(m, R.text); if(p >= 0) { - const List& a = args[p]; // GNU comma elision B32 commaVa = m.variadic && R.text == m.vaName && isPunct(os.back(), ","); - if(commaVa) { - if(a.empty()) - os.pop_back(); // drop the comma - else - appendList(os, a, true); // keep comma, no paste - } else if(a.empty()) { - // paste with empty operand -> previous unchanged - } else { - pasteInto(os.back(), a.front()); - for(U64 k = 1; k < a.size(); ++k) - os.push_back(a[k]); - } + pasteArg(args[p], commaVa, os); } else { pasteInto(os.back(), R); } @@ -209,7 +211,7 @@ namespace rat::cc { continue; } - I32 p = idxOf(T.text); + I32 p = formalIndex(m, T.text); if(p >= 0) { B32 nextPaste = i + 1 < body.size() && isPunct(body[i + 1], "##"); if(nextPaste) { @@ -232,8 +234,12 @@ namespace rat::cc { i += 1; } - // drop placemarkers, union hide set into every token; memoize on the - // incoming set (nearly always repeated) + return applyHideSet(os, hs); + } + + // drop placemarkers, union hide set into every token; memoize on the + // incoming set (nearly always repeated) + List Preprocessor::applyHideSet(List& os, const HideSet* hs) { List res; res.reserve(os.size()); const HideSet* memoIn = (const HideSet*)&res; // impossible value @@ -262,7 +268,7 @@ namespace rat::cc { return (U32)arg.size(); } - B32 Preprocessor::gatherArgs(List& work, List>& raw, PpToken& rparen) { + B32 Preprocessor::gatherArgs(List& work, ArgLists& raw, PpToken& rparen) { I32 depth = 1; List cur; for(;;) { @@ -292,9 +298,7 @@ namespace rat::cc { } } - B32 Preprocessor::mapArgs(const Macro& m, - const List>& raw, - List>& actuals) { + B32 Preprocessor::mapArgs(const Macro& m, const ArgLists& raw, ArgLists& actuals) { U64 np = m.params.size(); if(!m.variadic) { if(np == 0 && raw.size() == 1 && raw[0].empty()) @@ -327,9 +331,7 @@ namespace rat::cc { return true; } - void Preprocessor::emitAttrMarkers(const PpToken& at, - const List>& raw, - List& os) { + void Preprocessor::emitAttrMarkers(const PpToken& at, const ArgLists& raw, List& os) { B32 noinl = false; const PpToken* aliasTarget = nullptr; for(const List& arg : raw) { @@ -370,6 +372,53 @@ namespace rat::cc { work.push_back(*rit); } + B32 Preprocessor::expandBuiltinName(const PpToken& t, List& os) { + PpToken n; + if(t.text == idLine) { + n = makeNum((U64)((I64)t.line + lineDelta)); + } else if(t.text == idFile) { + String file = fileName.empty() ? (t.file ? *t.file : String()) : fileName; + n.kind = Pk::Str; + n.text = intern("\"" + file + "\""); + } else { + return false; + } + n.spaceBefore = t.spaceBefore; + n.bol = t.bol; + os.push_back(n); + return true; + } + + void Preprocessor::requeueAlignas(const PpToken& at, const ArgLists& raw, List& work) { + const List* alignArg = nullptr; + U32 alignAt = 0; + U32 alignEnd = 0; + for(const List& arg : raw) { + for(U32 i = 0; i < arg.size(); ++i) { + const PpToken& w = arg[i]; + if(w.kind != Pk::Id || (w.text != idAligned && w.text != idAligned2)) + continue; + if(i + 1 >= arg.size() || !isPunct(arg[i + 1], "(")) + continue; + U32 e = matchParen(arg, i + 1); + if(e < arg.size()) { + alignArg = &arg; + alignAt = i + 1; + alignEnd = e; + } + } + } + if(!alignArg) + return; + List r; + PpToken n = at; + n.text = idAlignas; + r.push_back(n); + for(U32 i = alignAt; i <= alignEnd; ++i) + r.push_back((*alignArg)[i]); + requeueExpansion(r, at, work); + } + List Preprocessor::expand(PpSpan in) { List work; work.reserve(in.size()); @@ -385,24 +434,8 @@ namespace rat::cc { } auto it = macros.find(t.text); if(it == macros.end()) { - if(t.text == idLine) { - PpToken n = makeNum((U64)((I64)t.line + lineDelta)); - n.spaceBefore = t.spaceBefore; - n.bol = t.bol; - os.push_back(n); - continue; - } - if(t.text == idFile) { - PpToken n; - n.kind = Pk::Str; - n.text = - intern("\"" + (fileName.empty() ? (t.file ? *t.file : String()) : fileName) + "\""); - n.spaceBefore = t.spaceBefore; - n.bol = t.bol; - os.push_back(n); - continue; - } - os.push_back(t); + if(!expandBuiltinName(t, os)) + os.push_back(t); continue; } if(hideHas(t.hide, t.text)) { @@ -422,11 +455,11 @@ namespace rat::cc { continue; } work.pop_back(); // ( - List> raw; + ArgLists raw; PpToken rparen; if(!gatherArgs(work, raw, rparen)) return os; - List> actuals; + ArgLists actuals; if(!mapArgs(m, raw, actuals)) return os; // the builtin __attribute__ erases its list, but noinline and alias survive @@ -434,32 +467,7 @@ namespace rat::cc { // parser can honor all three if(t.text == idAttr || t.text == idAttr2) { emitAttrMarkers(t, raw, os); - const List* alignArg = nullptr; - U32 alignAt = 0, alignEnd = 0; - for(const List& arg : raw) { - for(U32 i = 0; i < arg.size(); ++i) { - const PpToken& w = arg[i]; - if(w.kind != Pk::Id || (w.text != idAligned && w.text != idAligned2)) - continue; - if(i + 1 >= arg.size() || !isPunct(arg[i + 1], "(")) - continue; - U32 e = matchParen(arg, i + 1); - if(e < arg.size()) { - alignArg = &arg; - alignAt = i + 1; - alignEnd = e; - } - } - } - if(alignArg) { - List r; - PpToken n = t; - n.text = idAlignas; - r.push_back(n); - for(U32 i = alignAt; i <= alignEnd; ++i) - r.push_back((*alignArg)[i]); - requeueExpansion(r, t, work); - } + requeueAlignas(t, raw, work); continue; } const HideSet* hs = hideInsert(hideIntersect(t.hide, rparen.hide), t.text); @@ -469,6 +477,70 @@ namespace rat::cc { return os; } + B32 Preprocessor::parseMacroParams(PpSpan toks, U64& i, Macro& m) { + m.isFunc = true; + ++i; // ( + B32 expectName = true; + while(i < toks.size() && !isPunct(toks[i], ")")) { + const PpToken& t = toks[i]; + if(isPunct(t, ",")) { + expectName = true; + ++i; + continue; + } + if(isPunct(t, "...")) { + m.variadic = true; + m.vaName = idVaArgs; + ++i; + break; + } + if(t.kind == Pk::Id && expectName) { + // GNU named variadic + if(i + 1 < toks.size() && isPunct(toks[i + 1], "...")) { + m.variadic = true; + m.vaName = t.text; + i += 2; + break; + } + m.params.push_back(t.text); + expectName = false; + ++i; + continue; + } + fail("malformed macro parameter list"); + return false; + } + if(i >= toks.size() || !isPunct(toks[i], ")")) { + fail("missing ')' in macro parameter list"); + return false; + } + ++i; // ) + m.formals = m.params; + if(m.variadic) + m.formals.push_back(m.vaName); + return true; + } + + B32 Preprocessor::checkMacroBody(const Macro& m) { + if(!m.body.empty() && (isPunct(m.body.front(), "##") || isPunct(m.body.back(), "##"))) { + fail("'##' cannot appear at either end of a macro expansion"); + return false; + } + if(!m.isFunc) + return true; + for(U64 k = 0; k < m.body.size(); ++k) { + if(!isPunct(m.body[k], "#")) + continue; + B32 okOperand = k + 1 < m.body.size() && m.body[k + 1].kind == Pk::Id && + formalIndex(m, m.body[k + 1].text) >= 0; + if(!okOperand) { + fail("'#' is not followed by a macro parameter"); + return false; + } + } + return true; + } + void Preprocessor::doDefine(PpSpan toks) { if(toks.empty() || toks[0].kind != Pk::Id) { fail("#define expects a macro name"); @@ -481,48 +553,9 @@ namespace rat::cc { return; } U64 i = 1; - if(i < toks.size() && isPunct(toks[i], "(") && !toks[i].spaceBefore) { - m.isFunc = true; - ++i; // ( - B32 expectName = true; - while(i < toks.size() && !isPunct(toks[i], ")")) { - const PpToken& t = toks[i]; - if(isPunct(t, ",")) { - expectName = true; - ++i; - continue; - } - if(isPunct(t, "...")) { - m.variadic = true; - m.vaName = idVaArgs; - ++i; - break; - } - if(t.kind == Pk::Id && expectName) { - // GNU named variadic - if(i + 1 < toks.size() && isPunct(toks[i + 1], "...")) { - m.variadic = true; - m.vaName = t.text; - i += 2; - break; - } - m.params.push_back(t.text); - expectName = false; - ++i; - continue; - } - fail("malformed macro parameter list"); - return; - } - if(i >= toks.size() || !isPunct(toks[i], ")")) { - fail("missing ')' in macro parameter list"); - return; - } - ++i; // ) - m.formals = m.params; - if(m.variadic) - m.formals.push_back(m.vaName); - } + B32 funcLike = i < toks.size() && isPunct(toks[i], "(") && !toks[i].spaceBefore; + if(funcLike && !parseMacroParams(toks, i, m)) + return; for(; i < toks.size(); ++i) { PpToken b = toks[i]; b.bol = false; @@ -530,36 +563,8 @@ namespace rat::cc { b.spaceBefore = false; m.body.push_back(b); } - if(!m.body.empty() && (isPunct(m.body.front(), "##") || isPunct(m.body.back(), "##"))) { - fail("'##' cannot appear at either end of a macro expansion"); - return; - } - if(m.isFunc) { - for(U64 k = 0; k < m.body.size(); ++k) { - if(!isPunct(m.body[k], "#")) - continue; - B32 okOperand = false; - if(k + 1 < m.body.size() && m.body[k + 1].kind == Pk::Id) { - const String* nm = m.body[k + 1].text; - okOperand = std::find(m.formals.begin(), m.formals.end(), nm) != m.formals.end(); - } - if(!okOperand) { - fail("'#' is not followed by a macro parameter"); - return; - } - } - } - macros[name] = std::move(m); - } - - void Preprocessor::defineSimple(const String& name, const String& value) { - Macro m; - m.body = lexFragment(value, intern("")); - for(PpToken& t : m.body) - t.bol = false; - if(!m.body.empty()) - m.body.front().spaceBefore = false; - macros[intern(name)] = std::move(m); + if(checkMacroBody(m)) + macros[name] = std::move(m); } } // namespace detail } // namespace rat::cc diff --git a/src/compiler/lex/token_stream.cpp b/src/compiler/lex/token_stream.cpp index 6b6e84d0..e4973ca9 100644 --- a/src/compiler/lex/token_stream.cpp +++ b/src/compiler/lex/token_stream.cpp @@ -7,20 +7,43 @@ namespace rat::cc { static_assert((U32)TokKind::KwAlignas + 1 == (U32)TokKind::LParen, "keywords and punctuators must be contiguous"); + namespace detail { + void pushToken(TokenStream& ts, TokKind kind, const String* text, U32 line) { + Token t; + t.kind = kind; + t.offset = (U32)ts.toks.size(); + t.line = line; + ts.toks.push_back(t); + ts.texts.push_back(text); + } + + TokKind tokKindOf(const PpToken& t, const Map& kw, TokenStream& ts) { + if(t.kind == Pk::Num || t.kind == Pk::Char || t.kind == Pk::Str) { + String lerr; + TokKind k = classifyLiteral(*t.text, lerr); + if(k == TokKind::Error && ts.errMsg.empty()) + ts.errMsg = lerr; + return k; + } + auto it = kw.find(t.text); + if(it != kw.end()) + return it->second; + if(t.kind == Pk::Id) + return TokKind::Identifier; + if(ts.errMsg.empty()) + ts.errMsg = "unexpected token '" + *t.text + "'"; + return TokKind::Error; + } + } // namespace detail + B32 preprocessToTokens(const String& path, const String& source, const PpOptions& opts, TokenStream& ts, String& err) { detail::Preprocessor pp(opts); - pp.installBuiltins(); - pp.applyCommandLine(); - if(pp.ok) - pp.runFile(path, source); - if(!pp.ok) { - err = pp.err; + if(!pp.run(path, source, err)) return false; - } // pointer-keyed keyword/punct maps over the pp interner Map kindOf; @@ -33,56 +56,11 @@ namespace rat::cc { ts.fileName = path; ts.toks.reserve(pp.out.size() + 1); ts.texts.reserve(pp.out.size() + 1); - - const String* emptyText = pp.interner.intern(std::string_view()); - B32 sawError = false; - - auto push = [&](TokKind kind, const String* text, U32 line) { - Token t; - t.kind = kind; - t.offset = (U32)ts.toks.size(); - t.line = line; - ts.toks.push_back(t); - ts.texts.push_back(text); - }; - - for(const detail::PpToken& t : pp.out) { - switch(t.kind) { - case detail::Pk::Id: { - auto it = kindOf.find(t.text); - push(it != kindOf.end() ? it->second : TokKind::Identifier, t.text, t.line); - break; - } - case detail::Pk::Punct: { - auto it = kindOf.find(t.text); - if(it != kindOf.end()) { - push(it->second, t.text, t.line); - } else { - if(!sawError) { - sawError = true; - ts.errMsg = "unexpected token '" + *t.text + "'"; - } - push(TokKind::Error, t.text, t.line); - } - break; - } - case detail::Pk::Num: - case detail::Pk::Char: - case detail::Pk::Str: { - String lerr; - TokKind k = detail::classifyLiteral(*t.text, lerr); - if(k == TokKind::Error && !sawError) { - sawError = true; - ts.errMsg = lerr; - } - push(k, t.text, t.line); - break; - } - default: - break; - } - } - push(TokKind::Eof, emptyText, ts.toks.empty() ? 1 : ts.toks.back().line); + for(const detail::PpToken& t : pp.out) + if(t.kind != detail::Pk::Eof && t.kind != detail::Pk::Placemarker) + detail::pushToken(ts, detail::tokKindOf(t, kindOf, ts), t.text, t.line); + U32 eofLine = ts.toks.empty() ? 1 : ts.toks.back().line; + detail::pushToken(ts, TokKind::Eof, pp.interner.intern(std::string_view()), eofLine); // tokens reference interned spellings; take ownership of the pool ts.ownedText = std::move(pp.interner.store); diff --git a/src/compiler/lex/token_stream.h b/src/compiler/lex/token_stream.h index 13ef71c8..b7ec5fae 100644 --- a/src/compiler/lex/token_stream.h +++ b/src/compiler/lex/token_stream.h @@ -32,6 +32,13 @@ namespace rat::cc { void reset() { pos = 0; } }; + namespace detail { + struct PpToken; + + void pushToken(TokenStream& ts, TokKind kind, const String* text, U32 line); + TokKind tokKindOf(const PpToken& t, const Map& kw, TokenStream& ts); + } // namespace detail + // preprocess source and convert straight to parser tokens B32 preprocessToTokens(const String& path, const String& source, From 9f015c4a87d7ca558aabe3fd40f3b3d23930f030 Mon Sep 17 00:00:00 2001 From: nella Date: Tue, 29 Sep 2026 08:52:16 +0200 Subject: [PATCH 2/3] Split parser. --- src/compiler/parse/ast.cpp | 210 ++++++--------- src/compiler/parse/ast.h | 4 + src/compiler/parse/parse_asm.cpp | 45 ++-- src/compiler/parse/parse_declarator.cpp | 9 +- src/compiler/parse/parse_expr.cpp | 64 ++--- src/compiler/parse/parse_init.cpp | 330 +++++++++++++----------- src/compiler/parse/parse_literal.cpp | 112 ++++---- src/compiler/parse/parse_primary.cpp | 305 ++++++++++++---------- src/compiler/parse/parse_stmt.cpp | 218 ++++++++-------- src/compiler/parse/parse_struct.cpp | 90 +++---- src/compiler/parse/parse_type.cpp | 147 +++++------ src/compiler/parse/parser.h | 35 ++- src/compiler/parse/parser_detail.h | 2 + 13 files changed, 788 insertions(+), 783 deletions(-) diff --git a/src/compiler/parse/ast.cpp b/src/compiler/parse/ast.cpp index e0da818c..1759639c 100644 --- a/src/compiler/parse/ast.cpp +++ b/src/compiler/parse/ast.cpp @@ -41,7 +41,9 @@ namespace rat::cc { return t; } - static U32 intPrec(CType t) { return t.bitPrec ? t.bitPrec : t.bits; } + namespace detail { + U32 intPrec(CType t) { return t.bitPrec ? t.bitPrec : t.bits; } + } // namespace detail CType defaultArgPromote(CType t) { if(t.isFloat() && t.ptr == 0 && t.bits < 64) { @@ -76,7 +78,7 @@ namespace rat::cc { } a = promote(a); b = promote(b); - U32 pa = intPrec(a), pb = intPrec(b); + U32 pa = detail::intPrec(a), pb = detail::intPrec(b); if(pa == pb && a.isUnsigned() == b.isUnsigned()) { // keep the higher integer rank (long long > long > int) U32 ra = a.isLongLong() ? 2u : (a.isLong() ? 1u : 0u); @@ -92,15 +94,29 @@ namespace rat::cc { return s; } - static void appendStars(String& s, U32 ptr) { - for(U32 i = 0; i < ptr; ++i) - s += "*"; - } + namespace detail { + const C8* floatName(U32 bits) { + if(bits == 32) + return "float"; + return bits == 128 ? "long double" : "double"; + } - String typeName(CType t) { - if(t.func) { + const C8* intName(U32 bits) { + switch(bits) { + case 8: + return "char"; + case 16: + return "short"; + case 32: + return "int"; + default: + return "long"; + } + } + + String funcTypeName(CType t) { String s = typeName(t.func->ret) + " ("; - appendStars(s, t.ptr); + s.append(t.ptr, '*'); s += ")("; for(U32 i = 0; i < t.func->params.size(); ++i) { if(i) @@ -114,51 +130,27 @@ namespace rat::cc { s += ")"; return s; } - if(t.array) { - String s = typeName(t.array->elem) + "[" + std::to_string(t.array->count) + "]"; - appendStars(s, t.ptr); - return s; - } - if(t.isComplex()) { - String s = (t.bits == 32 ? "float" : t.bits == 128 ? "long double" : "double"); - s += " _Complex"; - appendStars(s, t.ptr); - return s; - } - if(t.strukt) { - String s = (t.strukt->isUnion ? "union " : "struct ") + t.strukt->tag; - appendStars(s, t.ptr); - return s; - } - if(t.isVoid()) { - String s = "void"; - appendStars(s, t.ptr); - return s; - } - if(t.isFloat()) { - String s = t.bits == 32 ? "float" : t.bits == 128 ? "long double" : "double"; - appendStars(s, t.ptr); - return s; - } - String base; - switch(t.bits) { - case 1: + } // namespace detail + + String typeName(CType t) { + if(t.func) + return detail::funcTypeName(t); + String s; + if(t.array) + s = typeName(t.array->elem) + "[" + std::to_string(t.array->count) + "]"; + else if(t.isComplex()) + s = String(detail::floatName(t.bits)) + " _Complex"; + else if(t.strukt) + s = (t.strukt->isUnion ? "union " : "struct ") + t.strukt->tag; + else if(t.isVoid()) + s = "void"; + else if(t.isFloat()) + s = detail::floatName(t.bits); + else if(t.bits == 1) return "_Bool"; - case 8: - base = "char"; - break; - case 16: - base = "short"; - break; - case 32: - base = "int"; - break; - default: - base = "long"; - break; - } - String s = t.isUnsigned() ? "unsigned " + base : base; - appendStars(s, t.ptr); + else + s = String(t.isUnsigned() ? "unsigned " : "") + detail::intName(t.bits); + s.append(t.ptr, '*'); return s; } @@ -278,92 +270,44 @@ namespace rat::cc { } void dumpStmt(const Stmt* s, U32 depth, std::ostream& os) { + // clang-format off + static const C8* const kNames[] = { + "block", "decl", "if", "while", "do-while", "for", "switch", "case", "default", "break", + "continue", "return", "expr", "empty", "label", "goto", "asm", + }; + // clang-format on + static_assert(sizeof(kNames) / sizeof(kNames[0]) == (U32)StmtKind::Asm + 1, + "kNames must cover every StmtKind"); pad(os, depth); - switch(s->kind) { - case StmtKind::Compound: - os << "block\n"; - for(const Stmt* child : s->body) - dumpStmt(child, depth + 1, os); - return; - case StmtKind::Decl: - os << "decl\n"; - for(const Declarator& d : s->decls) { - pad(os, depth + 1); - os << "var " << typeName(d.type) << " " << *d.name << "\n"; - if(d.init) - dumpExpr(d.init, depth + 2, os); - } - return; - case StmtKind::If: - os << "if\n"; - dumpExpr(s->expr, depth + 1, os); - dumpStmt(s->thenBody, depth + 1, os); - if(s->elseBody) - dumpStmt(s->elseBody, depth + 1, os); - return; - case StmtKind::While: - os << "while\n"; - dumpExpr(s->expr, depth + 1, os); - dumpStmt(s->thenBody, depth + 1, os); - return; - case StmtKind::DoWhile: - os << "do-while\n"; - dumpStmt(s->thenBody, depth + 1, os); - dumpExpr(s->expr, depth + 1, os); - return; - case StmtKind::For: - os << "for\n"; - if(s->forInit) - dumpStmt(s->forInit, depth + 1, os); - if(s->expr) - dumpExpr(s->expr, depth + 1, os); - if(s->forPost) - dumpExpr(s->forPost, depth + 1, os); + os << kNames[(U32)s->kind]; + if(s->kind == StmtKind::Label || s->kind == StmtKind::Goto) + os << " " << *s->label; + if(s->kind == StmtKind::Asm) + os << " \"" << *s->asmBlock->text << "\""; + os << "\n"; + for(const Stmt* child : s->body) + dumpStmt(child, depth + 1, os); + for(const Declarator& d : s->decls) { + pad(os, depth + 1); + os << "var " << typeName(d.type) << " " << *d.name << "\n"; + if(d.init) + dumpExpr(d.init, depth + 2, os); + } + if(s->kind == StmtKind::DoWhile) { dumpStmt(s->thenBody, depth + 1, os); - return; - case StmtKind::Switch: - os << "switch\n"; dumpExpr(s->expr, depth + 1, os); - dumpStmt(s->thenBody, depth + 1, os); return; - case StmtKind::Case: - os << "case\n"; - dumpExpr(s->expr, depth + 1, os); - dumpStmt(s->thenBody, depth + 1, os); - return; - case StmtKind::Default: - os << "default\n"; - dumpStmt(s->thenBody, depth + 1, os); - return; - case StmtKind::Break: - os << "break\n"; - return; - case StmtKind::Continue: - os << "continue\n"; - return; - case StmtKind::Return: - os << "return\n"; - if(s->expr) - dumpExpr(s->expr, depth + 1, os); - return; - case StmtKind::Expr: - os << "expr\n"; + } + if(s->forInit) + dumpStmt(s->forInit, depth + 1, os); + if(s->expr) dumpExpr(s->expr, depth + 1, os); - return; - case StmtKind::Empty: - os << "empty\n"; - return; - case StmtKind::Label: - os << "label " << *s->label << "\n"; + if(s->forPost) + dumpExpr(s->forPost, depth + 1, os); + if(s->thenBody) dumpStmt(s->thenBody, depth + 1, os); - return; - case StmtKind::Goto: - os << "goto " << *s->label << "\n"; - return; - case StmtKind::Asm: - os << "asm \"" << *s->asmBlock->text << "\"\n"; - return; - } + if(s->elseBody) + dumpStmt(s->elseBody, depth + 1, os); } } // namespace detail diff --git a/src/compiler/parse/ast.h b/src/compiler/parse/ast.h index 3c007b2c..8f2f03bc 100644 --- a/src/compiler/parse/ast.h +++ b/src/compiler/parse/ast.h @@ -477,6 +477,10 @@ namespace rat::cc { } namespace detail { + U32 intPrec(CType t); + const C8* floatName(U32 bits); + const C8* intName(U32 bits); + String funcTypeName(CType t); void pad(std::ostream& os, U32 depth); void dumpStmt(const Stmt* s, U32 depth, std::ostream& os); void dumpExpr(const Expr* e, U32 depth, std::ostream& os); diff --git a/src/compiler/parse/parse_asm.cpp b/src/compiler/parse/parse_asm.cpp index 54a1e5fb..95790db9 100644 --- a/src/compiler/parse/parse_asm.cpp +++ b/src/compiler/parse/parse_asm.cpp @@ -3,17 +3,22 @@ namespace rat::cc { // string-literal... // adjacent literals concatenate - B32 Parser::parseAsmTemplate(const String*& out) { + B32 Parser::parseStrings(String& out, const C8* where) { if(!check(TokKind::StringLiteral)) { - fail(peek(), "expected a string literal in 'asm'"); + fail(peek(), String("expected a string literal in ") + where); return false; } - String bytes; - if(!parseStringLiteral(advance(), bytes)) - return false; while(check(TokKind::StringLiteral)) - if(!parseStringLiteral(advance(), bytes)) + if(!parseStringLiteral(advance(), out)) return false; + return true; + } + + // string-literal... + B32 Parser::parseAsmTemplate(const String*& out) { + String bytes; + if(!parseStrings(bytes, "'asm'")) + return false; out = arena.make(std::move(bytes)); return true; } @@ -65,8 +70,21 @@ namespace rat::cc { return true; } + // [ name [, name]... ] + B32 Parser::parseAsmLabels(List& out) { + while(!check(TokKind::RParen)) { + if(!check(TokKind::Identifier)) { + fail(peek(), "expected a label name in 'asm goto'"); + return false; + } + out.push_back(arena.make(lex.text(advance()))); + if(!accept(TokKind::Comma)) + break; + } + return true; + } + // asm [ volatile | inline | goto ]... ( template [: out [: in [: clobbers [: labels]]]] ) ; - // labels: [ name [, name]... ] // no colon at all means basic asm, which is always volatile Stmt* Parser::parseAsmStatement() { Token kw = advance(); // asm @@ -104,17 +122,8 @@ namespace rat::cc { return nullptr; break; case 3: - for(;;) { - if(check(TokKind::RParen)) - break; - if(!check(TokKind::Identifier)) { - fail(peek(), "expected a label name in 'asm goto'"); - return nullptr; - } - blk->labels.push_back(arena.make(lex.text(advance()))); - if(!accept(TokKind::Comma)) - break; - } + if(!parseAsmLabels(blk->labels)) + return nullptr; break; default: fail(peek(), "too many ':' sections in 'asm'"); diff --git a/src/compiler/parse/parse_declarator.cpp b/src/compiler/parse/parse_declarator.cpp index 25ea8aaa..f8caf147 100644 --- a/src/compiler/parse/parse_declarator.cpp +++ b/src/compiler/parse/parse_declarator.cpp @@ -21,12 +21,6 @@ namespace rat::cc { } } - // [ static | qualifier ]... inside an array bound - void Parser::skipArrayQualifiers() { - while(check(TokKind::KwStatic) || detail::isTypeQualifier(peek().kind)) - advance(); - } - // [cond-expr] ']' // a bound that is not a positive constant is kept as a VLA expr B32 Parser::parseArrayBound(DeclOp& op) { @@ -175,7 +169,8 @@ namespace rat::cc { List sfx; for(;;) { if(accept(TokKind::LBracket)) { - skipArrayQualifiers(); + while(check(TokKind::KwStatic) || detail::isTypeQualifier(peek().kind)) + advance(); DeclOp op; op.kind = DeclOp::Kind::Array; if(check(TokKind::Star) && peek2().kind == TokKind::RBracket) diff --git a/src/compiler/parse/parse_expr.cpp b/src/compiler/parse/parse_expr.cpp index 9ee4ef41..4bbda0fb 100644 --- a/src/compiler/parse/parse_expr.cpp +++ b/src/compiler/parse/parse_expr.cpp @@ -69,6 +69,8 @@ namespace rat::cc { {TokKind::Star, ExprOp::Deref}, {TokKind::KwReal, ExprOp::Real}, {TokKind::KwImag, ExprOp::Imag}, + {TokKind::PlusPlus, ExprOp::PreInc}, + {TokKind::MinusMinus, ExprOp::PreDec}, }; for(const Entry& e : kUnary) if(e.kind == kind) { @@ -117,38 +119,37 @@ namespace rat::cc { return e; } - // size-op ( type-name ) | size-op unary | cast-or-compound - // | unary-op unary | ++ unary | -- unary | postfix + // size-op ( type-name ) | size-op unary // size-op: sizeof | _Alignof + Expr* Parser::parseSizeOp() { + Token kw = advance(); // sizeof / _Alignof + ExprKind kind = kw.kind == TokKind::KwSizeof ? ExprKind::Sizeof : ExprKind::AlignOf; + Expr* e = makeExpr(kind, kw.offset); + if(check(TokKind::LParen) && startsType(peek2())) { + advance(); // ( + CType ty; + if(!parseTypeName(ty)) // sizeof(typename) + return nullptr; + if(!expect(TokKind::RParen, "')'")) + return nullptr; + e->sizeOf.type = ty; + e->sizeOf.operand = nullptr; + } else { + Expr* operand = parseUnary(); + if(!operand) + return nullptr; + e->sizeOf.operand = operand; + } + return e; + } + + // size-op-expr | cast-or-compound | unary-op unary | ++ unary | -- unary | postfix Expr* Parser::parseUnary() { DepthScope scope(*this); if(!enterDepth()) return nullptr; - if(check(TokKind::KwSizeof) || check(TokKind::KwAlignof)) { - Token kw = advance(); // sizeof / _Alignof - ExprKind kind; - if(kw.kind == TokKind::KwSizeof) - kind = ExprKind::Sizeof; - else - kind = ExprKind::AlignOf; - Expr* e = makeExpr(kind, kw.offset); - if(check(TokKind::LParen) && startsType(peek2())) { - advance(); // ( - CType ty; - if(!parseTypeName(ty)) // sizeof(typename) - return nullptr; - if(!expect(TokKind::RParen, "')'")) - return nullptr; - e->sizeOf.type = ty; - e->sizeOf.operand = nullptr; - } else { - Expr* operand = parseUnary(); - if(!operand) - return nullptr; - e->sizeOf.operand = operand; - } - return e; - } + if(check(TokKind::KwSizeof) || check(TokKind::KwAlignof)) + return parseSizeOp(); if(check(TokKind::LParen) && startsType(peek2())) return parseCastOrCompound(); ExprOp op; @@ -159,15 +160,6 @@ namespace rat::cc { return nullptr; return makeUnary(t.offset, op, operand); } - TokKind k = peek().kind; - if(k == TokKind::PlusPlus || k == TokKind::MinusMinus) { - Token t = advance(); - Expr* operand = parseUnary(); - if(!operand) - return nullptr; - ExprOp pre = k == TokKind::PlusPlus ? ExprOp::PreInc : ExprOp::PreDec; - return makeUnary(t.offset, pre, operand); - } return parsePostfix(); } diff --git a/src/compiler/parse/parse_init.cpp b/src/compiler/parse/parse_init.cpp index d8739157..b4ef7995 100644 --- a/src/compiler/parse/parse_init.cpp +++ b/src/compiler/parse/parse_init.cpp @@ -1,94 +1,99 @@ #include "parse/parser.h" namespace rat::cc { - // { [ [designation] initializer [, [designation] initializer]... [,] ] } | assignment - // designation: designator... = - // designator: . name | '[' const-expr [ '...' const-expr ] ']' + // . name | '[' const-expr [ '...' const-expr ] ']' // a '...' range is allowed on the first designator only - Expr* Parser::parseInitializer() { - if(check(TokKind::LBrace)) { - Token lb = advance(); // { - Expr* e = makeExpr(ExprKind::InitList, lb.offset); - if(!check(TokKind::RBrace)) { - for(;;) { - Designator des; - I64 rangeEnd = -1; // GNU range designator [a ... b] - if(check(TokKind::Dot) || check(TokKind::LBracket)) { - Designator* cur = &des; - for(B32 first = true;; first = false) { - if(accept(TokKind::Dot)) { - if(!check(TokKind::Identifier)) { - fail(peek(), "expected a field name after '.'"); - return nullptr; - } - Token id = advance(); - cur->isIndex = false; - cur->field = arena.make(lex.text(id)); - } else if(accept(TokKind::LBracket)) { - Expr* ix = parseAssignment(); - I64 v = 0; - if(!ix || !evalIntConst(ix, v) || v < 0) { - fail(peek(), - "array designator must be a non-negative " - "integer constant"); - return nullptr; - } - cur->isIndex = true; - cur->index = v; - if(first && accept(TokKind::Ellipsis)) { - Expr* hi = parseAssignment(); - I64 hv = 0; - if(!hi || !evalIntConst(hi, hv) || hv < v) { - fail(peek(), "invalid range designator"); - return nullptr; - } - rangeEnd = hv; - } - if(!expect(TokKind::RBracket, "']'")) - return nullptr; - } else { - break; - } - if(check(TokKind::Dot) || check(TokKind::LBracket)) { - Designator* nxt = arena.make(); - nxt->isSet = true; - cur->next = nxt; - cur = nxt; - continue; - } - break; - } - if(!expect(TokKind::Assign, "'=' after designator")) - return nullptr; - des.isSet = true; - } - Expr* el = parseInitializer(); - if(!el) - return nullptr; - if(rangeEnd >= des.index) { - for(I64 k = des.index; k <= rangeEnd; ++k) { - Designator d2; - d2.isSet = true; - d2.isIndex = true; - d2.index = k; - e->args.push_back(el); - e->designators.push_back(d2); - } - } else { - e->args.push_back(el); - e->designators.push_back(des); - } - if(!accept(TokKind::Comma)) - break; - if(check(TokKind::RBrace)) - break; - } + B32 Parser::parseDesignator(Designator& cur, B32 first, I64& rangeEnd) { + if(accept(TokKind::Dot)) { + if(!check(TokKind::Identifier)) { + fail(peek(), "expected a field name after '.'"); + return false; + } + Token id = advance(); + cur.isIndex = false; + cur.field = arena.make(lex.text(id)); + return true; + } + advance(); // '[' + Expr* ix = parseAssignment(); + I64 v = 0; + if(!ix || !evalIntConst(ix, v) || v < 0) { + fail(peek(), "array designator must be a non-negative integer constant"); + return false; + } + cur.isIndex = true; + cur.index = v; + if(first && accept(TokKind::Ellipsis)) { + Expr* hi = parseAssignment(); + I64 hv = 0; + if(!hi || !evalIntConst(hi, hv) || hv < v) { + fail(peek(), "invalid range designator"); + return false; } - if(!expect(TokKind::RBrace, "'}'")) + rangeEnd = hv; + } + return expect(TokKind::RBracket, "']'"); + } + + // designator... = + B32 Parser::parseDesignation(Designator& des, I64& rangeEnd) { + Designator* cur = &des; + for(B32 first = true;; first = false) { + if(!parseDesignator(*cur, first, rangeEnd)) + return false; + if(!check(TokKind::Dot) && !check(TokKind::LBracket)) + break; + Designator* nxt = arena.make(); + nxt->isSet = true; + cur->next = nxt; + cur = nxt; + } + if(!expect(TokKind::Assign, "'=' after designator")) + return false; + des.isSet = true; + return true; + } + + // [designation] initializer + B32 Parser::parseInitElement(Expr* list) { + Designator des; + I64 rangeEnd = -1; // GNU range designator [a ... b] + if((check(TokKind::Dot) || check(TokKind::LBracket)) && !parseDesignation(des, rangeEnd)) + return false; + Expr* el = parseInitializer(); + if(!el) + return false; + if(rangeEnd < des.index) { + list->args.push_back(el); + list->designators.push_back(des); + return true; + } + for(I64 k = des.index; k <= rangeEnd; ++k) { + Designator d2; + d2.isSet = true; + d2.isIndex = true; + d2.index = k; + list->args.push_back(el); + list->designators.push_back(d2); + } + return true; + } + + // { [ element [, element]... [,] ] } | assignment + Expr* Parser::parseInitializer() { + if(!check(TokKind::LBrace)) + return parseAssignment(); + Token lb = advance(); // { + Expr* e = makeExpr(ExprKind::InitList, lb.offset); + while(!check(TokKind::RBrace)) { + if(!parseInitElement(e)) return nullptr; - return e; + if(!accept(TokKind::Comma)) + break; } - return parseAssignment(); + if(!expect(TokKind::RBrace, "'}'")) + return nullptr; + return e; } // wraps v to the width and signedness of an integer type @@ -106,64 +111,96 @@ namespace rat::cc { return (I64)low; } - // folds an integer constant expression; anything else is a diagnostic - B32 Parser::evalIntConst(const Expr* e, I64& out) { - switch(e->kind) { - case ExprKind::IntLit: - out = e->intLit.value; - return true; - case ExprKind::Unary: { - I64 v; - if(!evalIntConst(e->unary.operand, v)) - return false; - switch(e->unary.op) { - // clang-format off - case ExprOp::Pos: out = v; return true; - case ExprOp::Neg: out = -v; return true; - case ExprOp::Not: out = !v; return true; - case ExprOp::BitNot: out = ~v; return true; - // clang-format on - default: - fail(peek(), "non-constant operator in constant expression"); + B32 Parser::evalUnaryConst(const Expr* e, I64& out) { + I64 v; + if(!evalIntConst(e->unary.operand, v)) + return false; + switch(e->unary.op) { + // clang-format off + case ExprOp::Pos: out = v; return true; + case ExprOp::Neg: out = -v; return true; + case ExprOp::Not: out = !v; return true; + case ExprOp::BitNot: out = ~v; return true; + // clang-format on + default: + fail(peek(), "non-constant operator in constant expression"); + return false; + } + } + + B32 Parser::evalBinaryConst(const Expr* e, I64& out) { + I64 l; + I64 r; + if(!evalIntConst(e->binary.lhs, l) || !evalIntConst(e->binary.rhs, r)) + return false; + switch(e->binary.op) { + // clang-format off + case ExprOp::Add: out = l + r; return true; + case ExprOp::Sub: out = l - r; return true; + case ExprOp::Mul: out = l * r; return true; + case ExprOp::Shl: out = l << r; return true; + case ExprOp::Shr: out = l >> r; return true; + case ExprOp::Lt: out = l < r; return true; + case ExprOp::Gt: out = l > r; return true; + case ExprOp::Le: out = l <= r; return true; + case ExprOp::Ge: out = l >= r; return true; + case ExprOp::Eq: out = l == r; return true; + case ExprOp::Ne: out = l != r; return true; + case ExprOp::BitAnd: out = l & r; return true; + case ExprOp::BitOr: out = l | r; return true; + case ExprOp::BitXor: out = l ^ r; return true; + case ExprOp::LogAnd: out = l && r; return true; + case ExprOp::LogOr: out = l || r; return true; + // clang-format on + case ExprOp::Div: + case ExprOp::Rem: + if(r == 0) { + fail(peek(), "division by zero in constant expression"); return false; } + out = (e->binary.op == ExprOp::Div) ? (l / r) : (l % r); + return true; + default: + fail(peek(), "non-constant operator in constant expression"); + return false; } - case ExprKind::Binary: { - I64 l, r; - if(!evalIntConst(e->binary.lhs, l) || !evalIntConst(e->binary.rhs, r)) - return false; - switch(e->binary.op) { - // clang-format off - case ExprOp::Add: out = l + r; return true; - case ExprOp::Sub: out = l - r; return true; - case ExprOp::Mul: out = l * r; return true; - case ExprOp::Shl: out = l << r; return true; - case ExprOp::Shr: out = l >> r; return true; - case ExprOp::Lt: out = l < r; return true; - case ExprOp::Gt: out = l > r; return true; - case ExprOp::Le: out = l <= r; return true; - case ExprOp::Ge: out = l >= r; return true; - case ExprOp::Eq: out = l == r; return true; - case ExprOp::Ne: out = l != r; return true; - case ExprOp::BitAnd: out = l & r; return true; - case ExprOp::BitOr: out = l | r; return true; - case ExprOp::BitXor: out = l ^ r; return true; - case ExprOp::LogAnd: out = l && r; return true; - case ExprOp::LogOr: out = l || r; return true; - // clang-format on - case ExprOp::Div: - case ExprOp::Rem: - if(r == 0) { - fail(peek(), "division by zero in constant expression"); - return false; - } - out = (e->binary.op == ExprOp::Div) ? (l / r) : (l % r); - return true; - default: - fail(peek(), "non-constant operator in constant expression"); + } + + B32 Parser::evalSizeofConst(const Expr* e, I64& out) { + if(e->kind == ExprKind::AlignOf) { + if(e->sizeOf.operand) { + fail(peek(), "_Alignof of an expression is not a constant expression here"); return false; } + out = (I64)typeAlignBytes(e->sizeOf.type); + return true; } + if(e->sizeOf.operand) { + fail(peek(), "sizeof expression not allowed in constant expression"); + return false; + } + if(isVlaType(e->sizeOf.type)) { + fail(peek(), "sizeof a variable-length array is not constant"); + return false; + } + if(isStruct(e->sizeOf.type) && !e->sizeOf.type.strukt->complete) { + fail(peek(), "sizeof applied to an incomplete type"); + return false; + } + out = (I64)typeSizeBytes(e->sizeOf.type); + return true; + } + + // folds an integer constant expression; anything else is a diagnostic + B32 Parser::evalIntConst(const Expr* e, I64& out) { + switch(e->kind) { + case ExprKind::IntLit: + out = e->intLit.value; + return true; + case ExprKind::Unary: + return evalUnaryConst(e, out); + case ExprKind::Binary: + return evalBinaryConst(e, out); case ExprKind::Ternary: { I64 c; if(!evalIntConst(e->ternary.cond, c)) @@ -176,27 +213,8 @@ namespace rat::cc { out = castConstValue(out, e->cast.type); return true; case ExprKind::Sizeof: - if(e->sizeOf.operand) { - fail(peek(), "sizeof expression not allowed in constant expression"); - return false; - } - if(isVlaType(e->sizeOf.type)) { - fail(peek(), "sizeof a variable-length array is not constant"); - return false; - } - if(isStruct(e->sizeOf.type) && !e->sizeOf.type.strukt->complete) { - fail(peek(), "sizeof applied to an incomplete type"); - return false; - } - out = (I64)typeSizeBytes(e->sizeOf.type); - return true; case ExprKind::AlignOf: - if(e->sizeOf.operand) { - fail(peek(), "_Alignof of an expression is not a constant expression here"); - return false; - } - out = (I64)typeAlignBytes(e->sizeOf.type); - return true; + return evalSizeofConst(e, out); default: fail(peek(), "expected a constant expression"); return false; diff --git a/src/compiler/parse/parse_literal.cpp b/src/compiler/parse/parse_literal.cpp index a362735c..142c32d4 100644 --- a/src/compiler/parse/parse_literal.cpp +++ b/src/compiler/parse/parse_literal.cpp @@ -25,7 +25,8 @@ namespace rat::cc { U32 utf8Decode(const String& bytes, U32& i, U32 n) { U8 b0 = (U8)bytes[i++]; - U32 cp = b0, extra = 0; + U32 cp = b0; + U32 extra = 0; if((b0 & 0xE0) == 0xC0) { cp = b0 & 0x1F; extra = 1; @@ -54,6 +55,18 @@ namespace rat::cc { } } + B32 ucnAt(const String& s, U32 i, U32 end) { return i < end && (s[i] == 'u' || s[i] == 'U'); } + + void quotedRange(const String& s, C8 quote, U32& begin, U32& end) { + begin = 0; // skip any encoding prefix + while(begin < s.size() && s[begin] != quote) + ++begin; + ++begin; + end = (U32)s.size(); + if(end > 0 && s[end - 1] == quote) + --end; + } + void appendCodeUnits(String& out, U32 cp, U32 unitBytes) { if(unitBytes == 2 && cp >= 0x10000) { // UTF-16 surrogate pair U32 v = cp - 0x10000; @@ -67,9 +80,8 @@ namespace rat::cc { } // namespace detail // [ 0x | 0X | 0 ] digits [ u | U | l | L ]... - // the type follows C11 6.4.4.1 B32 Parser::parseIntLiteral(const Token& tok, I64& value, U32& bits, U8& mods) { - String s = lex.text(tok); + const String& s = lex.text(tok); B32 isUnsigned = false; U32 lCount = 0; @@ -106,7 +118,13 @@ namespace rat::cc { v = v * (U64)base + (U64)d; } - B32 dec = (base == 10); + value = (I64)v; + mods = intLiteralMods(v, base == 10, isUnsigned, lCount, bits); + return true; + } + + // the type follows C11 6.4.4.1 + U8 Parser::intLiteralMods(U64 v, B32 dec, B32 isUnsigned, U32 lCount, U32& bits) const { B32 fitsI32 = v <= 0x7fffffffULL; B32 fitsU32 = v <= 0xffffffffULL; B32 fitsI64 = v <= 0x7fffffffffffffffULL; @@ -156,10 +174,8 @@ namespace rat::cc { } bits = (isLongLong || (isLong && lay.longBits >= 64)) ? 64 : (isLong ? lay.longBits : 32); - value = (I64)v; - mods = (U8)((isUnsigned ? CType::Unsigned : 0) | (isLong ? CType::Long : 0) | + return (U8)((isUnsigned ? CType::Unsigned : 0) | (isLong ? CType::Long : 0) | (isLongLong ? CType::LongLong : 0)); - return true; } // after \ : simple-escape | ? | x hex-digit... | octal-digit [octal-digit [octal-digit]] @@ -241,16 +257,12 @@ namespace rat::cc { // [ L | u | U ] ' c-char... ' // several chars pack big-endian into one int B32 Parser::parseCharLiteral(const Token& tok, I64& value) { - String s = lex.text(tok); + const String& s = lex.text(tok); U32 maxVal = detail::escapeMaxVal(s.size() ? s[0] : '\''); B32 prefixed = s.size() != 0 && s[0] != '\''; - U32 i = 0; // skip any encoding prefix - while(i < s.size() && s[i] != '\'') - ++i; - ++i; - U32 end = (U32)s.size(); - if(end > 0 && s[end - 1] == '\'') - --end; + U32 i = 0; + U32 end = 0; + detail::quotedRange(s, '\'', i, end); if(i >= end) { fail(tok, "empty character constant"); @@ -263,17 +275,11 @@ namespace rat::cc { I64 c; if(s[i] == '\\') { ++i; - if(i < end && (s[i] == 'u' || s[i] == 'U')) { - U32 cp; - if(!decodeUcn(s, i, end, tok, cp)) - return false; - c = (I64)cp; - } else { - U32 val; - if(!decodeEscape(s, i, end, tok, maxVal, val)) - return false; - c = maxVal <= 0xFFu ? (I64)(I8)(U8)val : (I64)(I32)val; - } + B32 wide = detail::ucnAt(s, i, end) || maxVal > 0xFFu; + U32 val; + if(!decodeBackslash(s, i, end, tok, maxVal, val)) + return false; + c = wide ? (I64)(I32)val : (I64)(I8)(U8)val; } else if(prefixed) { c = (I64)detail::utf8Decode(s, i, end); // L/u/U hold one code point } else { @@ -289,37 +295,35 @@ namespace rat::cc { return true; } + // after \ : ucn | escape + B32 Parser::decodeBackslash( + const String& s, U32& i, U32 end, const Token& tok, U32 maxVal, U32& out) { + if(detail::ucnAt(s, i, end)) + return decodeUcn(s, i, end, tok, out); + return decodeEscape(s, i, end, tok, maxVal, out); + } + // [ L | u | u8 | U ] " s-char... " // appended to out as utf-8 bytes B32 Parser::parseStringLiteral(const Token& tok, String& out) { - String s = lex.text(tok); + const String& s = lex.text(tok); U32 maxVal = (s.size() && s[0] == 'u' && s.size() > 1 && s[1] == '8') ? 0xFFu : detail::escapeMaxVal(s.size() ? s[0] : '"'); U32 i = 0; - while(i < s.size() && s[i] != '"') - ++i; - ++i; - U32 end = (U32)s.size(); - if(end > 0 && s[end - 1] == '"') - --end; + U32 end = 0; + detail::quotedRange(s, '"', i, end); while(i < end) { C8 c = s[i++]; if(c != '\\') { out.push_back(c); continue; } - if(i < end && (s[i] == 'u' || s[i] == 'U')) { - U32 cp; - if(!decodeUcn(s, i, end, tok, cp)) - return false; - detail::utf8Encode(out, cp); - continue; - } + B32 wide = detail::ucnAt(s, i, end) || maxVal > 0xFFu; U32 val; - if(!decodeEscape(s, i, end, tok, maxVal, val)) + if(!decodeBackslash(s, i, end, tok, maxVal, val)) return false; - if(maxVal > 0xFFu) + if(wide) detail::utf8Encode(out, val); else out.push_back((C8)(U8)val); @@ -330,29 +334,18 @@ namespace rat::cc { // [ L | u | U ] " s-char... " // appended to out as unitBytes-wide code units B32 Parser::parseWideStringLiteral(const Token& tok, U32 unitBytes, String& out) { - String s = lex.text(tok); + const String& s = lex.text(tok); U32 maxVal = detail::escapeMaxVal(s.size() ? s[0] : '"'); U32 i = 0; - while(i < s.size() && s[i] != '"') - ++i; - ++i; - U32 end = (U32)s.size(); - if(end > 0 && s[end - 1] == '"') - --end; + U32 end = 0; + detail::quotedRange(s, '"', i, end); while(i < end) { U32 cp; if(s[i] == '\\') { ++i; - if(i < end && (s[i] == 'u' || s[i] == 'U')) { - if(!decodeUcn(s, i, end, tok, cp)) - return false; - } else { - // a wide escape carries its full value, it is not cut to a byte - U32 value; - if(!decodeEscape(s, i, end, tok, maxVal, value)) - return false; - cp = value; - } + // a wide escape carries its full value, it is not cut to a byte + if(!decodeBackslash(s, i, end, tok, maxVal, cp)) + return false; } else { cp = detail::utf8Decode(s, i, end); } @@ -360,5 +353,4 @@ namespace rat::cc { } return true; } - } // namespace rat::cc diff --git a/src/compiler/parse/parse_primary.cpp b/src/compiler/parse/parse_primary.cpp index 0a2266c8..ac64fdd0 100644 --- a/src/compiler/parse/parse_primary.cpp +++ b/src/compiler/parse/parse_primary.cpp @@ -108,51 +108,125 @@ namespace rat::cc { return e; } + // string-literal... + Expr* Parser::parsePrimaryString() { + List parts; + while(check(TokKind::StringLiteral)) + parts.push_back(advance()); + U32 unitBytes = 1; + for(const Token& t : parts) { + const String& h = lex.text(t); + if(h.empty()) + continue; + if(h[0] == 'L') + unitBytes = lay.wcharBytes; + else if(h[0] == 'U') + unitBytes = 4; + else if(h[0] == 'u' && !(h.size() > 1 && h[1] == '8')) + unitBytes = 2; + } + String bytes; + for(const Token& t : parts) { + B32 read = unitBytes == 1 ? parseStringLiteral(t, bytes) + : parseWideStringLiteral(t, unitBytes, bytes); + if(!read) + return nullptr; + } + Expr* e = makeExpr(ExprKind::StrLit, parts[0].offset); + e->str.bytes = arena.make(std::move(bytes)); + e->str.isWide = unitBytes != 1; + e->str.charSize = unitBytes; + return e; + } + + // float-const + Expr* Parser::parsePrimaryFloat() { + Token lit = advance(); + String text = lex.text(lit); + B32 isFloat = false; + B32 isLongDouble = false; + B32 isImaginary = false; + while(!text.empty()) { + C8 c = text.back(); + if(c == 'f' || c == 'F') + isFloat = true; + else if(c == 'l' || c == 'L') + isLongDouble = true; + else if(c == 'i' || c == 'I' || c == 'j' || c == 'J') + isImaginary = true; + else + break; + text.pop_back(); + } + errno = 0; + C8* end = nullptr; + F80 value = std::strtold(text.c_str(), &end); + if(end == text.c_str() || *end != '\0') { + fail(lit, "invalid floating constant '" + text + "'"); + return nullptr; + } + Expr* e = makeExpr(ExprKind::FloatLit, lit.offset); + e->floatLit = {value, isFloat ? 32u : (isLongDouble ? 128u : 64u), isImaginary}; + return e; + } + + // name + // __func__, __builtin_offsetof and enum constants fold here + Expr* Parser::parsePrimaryName() { + Token id = advance(); + const String& name = lex.text(id); + if(name == "__func__") { + Expr* e = makeExpr(ExprKind::StrLit, id.offset); + e->str.bytes = arena.make(curFuncName); + e->str.isWide = false; + e->str.charSize = 1; + return e; + } + if(name == "__builtin_offsetof") + return parseBuiltinOffsetof(id); + if(const I64* ec = enumConstants.get(name)) + return makeInt(id, *ec, 32); + return makeIdent(id); + } + + // ( expr ) | ( compound ) + Expr* Parser::parsePrimaryParen() { + Token open = advance(); // ( + if(check(TokKind::LBrace)) { // GNU statement expression + Stmt* body = parseCompound(); + if(!body) + return nullptr; + if(!expect(TokKind::RParen, "')'")) + return nullptr; + Expr* e = makeExpr(ExprKind::StmtExpr, open.offset); + e->stmtExpr.body = body; + return e; + } + Expr* inner = parseExpression(); + if(!inner) + return nullptr; + if(!expect(TokKind::RParen, "')'")) + return nullptr; + return inner; + } + // generic | string-literal... | char-const | int-const | float-const | name // | ( expr ) | ( compound ) - // __func__, __builtin_offsetof and enum constants fold here Expr* Parser::parsePrimary() { const Token& tok = peek(); - if(tok.kind == TokKind::KwGeneric) + switch(tok.kind) { + case TokKind::KwGeneric: return parseGeneric(); - if(tok.kind == TokKind::StringLiteral) { - List parts; - parts.push_back(advance()); - while(check(TokKind::StringLiteral)) - parts.push_back(advance()); - U32 unitBytes = 1; - for(const Token& t : parts) { - String h = lex.text(t); - if(h.empty()) - continue; - if(h[0] == 'L') - unitBytes = lay.wcharBytes; - else if(h[0] == 'U') - unitBytes = 4; - else if(h[0] == 'u' && !(h.size() > 1 && h[1] == '8')) - unitBytes = 2; - } - String bytes; - for(const Token& t : parts) { - B32 read = unitBytes == 1 ? parseStringLiteral(t, bytes) - : parseWideStringLiteral(t, unitBytes, bytes); - if(!read) - return nullptr; - } - Expr* e = makeExpr(ExprKind::StrLit, parts[0].offset); - e->str.bytes = arena.make(std::move(bytes)); - e->str.isWide = unitBytes != 1; - e->str.charSize = unitBytes; - return e; - } - if(tok.kind == TokKind::CharConstant) { + case TokKind::StringLiteral: + return parsePrimaryString(); + case TokKind::CharConstant: { Token lit = advance(); I64 value; if(!parseCharLiteral(lit, value)) return nullptr; return makeInt(lit, value, 32); } - if(tok.kind == TokKind::IntConstant) { + case TokKind::IntConstant: { Token lit = advance(); I64 value; U32 bits; @@ -161,68 +235,16 @@ namespace rat::cc { return nullptr; return makeInt(lit, value, bits, mods); } - if(tok.kind == TokKind::FloatConstant) { - Token lit = advance(); - String text = lex.text(lit); - B32 isFloat = false, isLongDouble = false, isImaginary = false; - while(!text.empty()) { - C8 c = text.back(); - if(c == 'f' || c == 'F') - isFloat = true; - else if(c == 'l' || c == 'L') - isLongDouble = true; - else if(c == 'i' || c == 'I' || c == 'j' || c == 'J') - isImaginary = true; - else - break; - text.pop_back(); - } - errno = 0; - C8* end = nullptr; - F80 value = std::strtold(text.c_str(), &end); - if(end == text.c_str() || *end != '\0') { - fail(lit, "invalid floating constant '" + text + "'"); - return nullptr; - } - Expr* e = makeExpr(ExprKind::FloatLit, lit.offset); - e->floatLit = {value, isFloat ? 32u : (isLongDouble ? 128u : 64u), isImaginary}; - return e; - } - if(tok.kind == TokKind::Identifier) { - Token id = advance(); - if(lex.text(id) == "__func__") { - Expr* e = makeExpr(ExprKind::StrLit, id.offset); - e->str.bytes = arena.make(curFuncName); - e->str.isWide = false; - e->str.charSize = 1; - return e; - } - if(lex.text(id) == "__builtin_offsetof") - return parseBuiltinOffsetof(id); - if(const I64* ec = enumConstants.get(lex.text(id))) - return makeInt(id, *ec, 32); - return makeIdent(id); - } - if(accept(TokKind::LParen)) { - if(check(TokKind::LBrace)) { // GNU statement expression - Stmt* body = parseCompound(); - if(!body) - return nullptr; - if(!expect(TokKind::RParen, "')'")) - return nullptr; - Expr* e = makeExpr(ExprKind::StmtExpr, tok.offset); - e->stmtExpr.body = body; - return e; - } - Expr* inner = parseExpression(); - if(!inner) - return nullptr; - if(!expect(TokKind::RParen, "')'")) - return nullptr; - return inner; + case TokKind::FloatConstant: + return parsePrimaryFloat(); + case TokKind::Identifier: + return parsePrimaryName(); + case TokKind::LParen: + return parsePrimaryParen(); + default: + fail(tok, String("expected expression, found '") + tokKindName(tok.kind) + "'"); + return nullptr; } - fail(tok, String("expected expression, found '") + tokKindName(tok.kind) + "'"); - return nullptr; } // primary postfix-tail @@ -233,54 +255,63 @@ namespace rat::cc { return parsePostfixTail(e); } - // [ ( [ assignment [, assignment]... ] ) | '[' expr ']' | . name | -> name | ++ | -- ]... + // __builtin_va_arg ( assignment , type-name ) + Expr* Parser::parseVaArg() { + Token lp = advance(); // ( + Expr* ap = parseAssignment(); + if(!ap) + return nullptr; + if(!expect(TokKind::Comma, "','")) + return nullptr; + CType ty; + if(!parseTypeName(ty)) { + fail(peek(), "expected a type in __builtin_va_arg"); + return nullptr; + } + if(!expect(TokKind::RParen, "')'")) + return nullptr; + Expr* va = makeExpr(ExprKind::VaArg, lp.offset); + va->vaArg.ap = ap; + va->vaArg.type = ty; + return va; + } + + // ( [ assignment [, assignment]... ] ) + Expr* Parser::parseCall(Expr* e) { + Token lp = advance(); // ( + Expr* callE = makeExpr(ExprKind::Call, lp.offset); + if(e->kind == ExprKind::Ident) { // by-name call + callE->call.callee = e->ident.name; + callE->call.target = nullptr; + } else { // indirect call + callE->call.callee = nullptr; + callE->call.target = e; + } + if(!check(TokKind::RParen)) { + for(;;) { + Expr* arg = parseAssignment(); + if(!arg) + return nullptr; + callE->args.push_back(arg); + if(!accept(TokKind::Comma)) + break; + } + } + if(!expect(TokKind::RParen, "')'")) + return nullptr; + return callE; + } + + // [ call | '[' expr ']' | . name | -> name | ++ | -- ]... // __builtin_va_arg ( assignment , type-name ) is a call in form only Expr* Parser::parsePostfixTail(Expr* e) { for(;;) { TokKind k = peek().kind; if(k == TokKind::LParen) { - if(e->kind == ExprKind::Ident && *e->ident.name == "__builtin_va_arg") { - Token lp = advance(); // ( - Expr* ap = parseAssignment(); - if(!ap) - return nullptr; - if(!expect(TokKind::Comma, "','")) - return nullptr; - CType ty; - if(!parseTypeName(ty)) { - fail(peek(), "expected a type in __builtin_va_arg"); - return nullptr; - } - if(!expect(TokKind::RParen, "')'")) - return nullptr; - Expr* va = makeExpr(ExprKind::VaArg, lp.offset); - va->vaArg.ap = ap; - va->vaArg.type = ty; - e = va; - continue; - } - Token lp = advance(); // ( - Expr* callE = makeExpr(ExprKind::Call, lp.offset); - if(e->kind == ExprKind::Ident) { // by-name call - callE->call.callee = e->ident.name; - callE->call.target = nullptr; - } else { // indirect call - callE->call.callee = nullptr; - callE->call.target = e; - } - if(!check(TokKind::RParen)) { - for(;;) { - Expr* arg = parseAssignment(); - if(!arg) - return nullptr; - callE->args.push_back(arg); - if(!accept(TokKind::Comma)) - break; - } - } - if(!expect(TokKind::RParen, "')'")) + B32 isVaArg = e->kind == ExprKind::Ident && *e->ident.name == "__builtin_va_arg"; + e = isVaArg ? parseVaArg() : parseCall(e); + if(!e) return nullptr; - e = callE; } else if(k == TokKind::LBracket) { Token lb = advance(); Expr* idx = parseExpression(); diff --git a/src/compiler/parse/parse_stmt.cpp b/src/compiler/parse/parse_stmt.cpp index b3fe878f..b9a186e3 100644 --- a/src/compiler/parse/parse_stmt.cpp +++ b/src/compiler/parse/parse_stmt.cpp @@ -12,18 +12,8 @@ namespace rat::cc { if(!expect(TokKind::Comma, "','")) return false; String msg; - if(!check(TokKind::StringLiteral)) { - fail(peek(), "expected a string literal in _Static_assert"); + if(!parseStrings(msg, "_Static_assert")) return false; - } - if(!parseStringLiteral(advance(), msg)) - return false; - while(check(TokKind::StringLiteral)) { - String more; - if(!parseStringLiteral(advance(), more)) - return false; - msg += more; - } if(!expect(TokKind::RParen, "')'")) return false; if(!expect(TokKind::Semicolon, "';'")) @@ -206,116 +196,82 @@ namespace rat::cc { return parseStatement(); } - // compound | asm | name : stmt | goto name ; | declaration - // | if | while | do | for | switch | case const-expr : [stmt] | default : [stmt] - // | break ; | continue ; | return [expr] ; | ; | expr ; - Stmt* Parser::parseStatement() { - DepthScope scope(*this); - if(!enterDepth()) + // name : stmt + Stmt* Parser::parseLabel() { + Token nameTok = advance(); + advance(); // ':' + Stmt* sub = parseStatement(); + if(!sub) return nullptr; - const Token& tok = peek(); - if(tok.kind == TokKind::LBrace) - return parseCompound(); - - if(tok.kind == TokKind::KwAsm) - return parseAsmStatement(); - - if(tok.kind == TokKind::Identifier && peek2().kind == TokKind::Colon) { - Token nameTok = advance(); - advance(); // ':' - Stmt* sub = parseStatement(); - if(!sub) - return nullptr; - Stmt* s = makeStmt(StmtKind::Label, nameTok.offset); - s->label = arena.make(lex.text(nameTok)); - s->thenBody = sub; - return s; - } + Stmt* s = makeStmt(StmtKind::Label, nameTok.offset); + s->label = arena.make(lex.text(nameTok)); + s->thenBody = sub; + return s; + } - if(tok.kind == TokKind::KwGoto) { - Token kw = advance(); - if(!check(TokKind::Identifier)) { - fail(peek(), "expected a label name after 'goto'"); - return nullptr; - } - Token nameTok = advance(); - Stmt* s = makeStmt(StmtKind::Goto, kw.offset); - s->label = arena.make(lex.text(nameTok)); - if(!expect(TokKind::Semicolon, "';'")) - return nullptr; - return s; + // goto name ; + Stmt* Parser::parseGoto() { + Token kw = advance(); + if(!check(TokKind::Identifier)) { + fail(peek(), "expected a label name after 'goto'"); + return nullptr; } + Token nameTok = advance(); + Stmt* s = makeStmt(StmtKind::Goto, kw.offset); + s->label = arena.make(lex.text(nameTok)); + if(!expect(TokKind::Semicolon, "';'")) + return nullptr; + return s; + } - if(startsType(tok) || tok.kind == TokKind::KwTypedef || tok.kind == TokKind::KwStaticAssert) - return parseDeclaration(nullptr); - - if(tok.kind == TokKind::KwIf) - return parseIf(); - if(tok.kind == TokKind::KwWhile) - return parseWhile(); - if(tok.kind == TokKind::KwDo) - return parseDoWhile(); - if(tok.kind == TokKind::KwFor) - return parseFor(); - if(tok.kind == TokKind::KwSwitch) - return parseSwitch(); - - if(tok.kind == TokKind::KwCase) { - Token kw = advance(); - Expr* value = parseConditional(); + // case const-expr : [stmt] | default : [stmt] + Stmt* Parser::parseCaseLabel() { + Token kw = advance(); + B32 isCase = kw.kind == TokKind::KwCase; + Expr* value = nullptr; + if(isCase) { + value = parseConditional(); if(!value) return nullptr; - if(!expect(TokKind::Colon, "':'")) - return nullptr; - Stmt* sub = parseLabeledSub(); - if(!sub) - return nullptr; - Stmt* s = makeStmt(StmtKind::Case, kw.offset); - s->expr = value; - s->thenBody = sub; - return s; - } - - if(tok.kind == TokKind::KwDefault) { - Token kw = advance(); - if(!expect(TokKind::Colon, "':'")) - return nullptr; - Stmt* sub = parseLabeledSub(); - if(!sub) - return nullptr; - Stmt* s = makeStmt(StmtKind::Default, kw.offset); - s->thenBody = sub; - return s; } + if(!expect(TokKind::Colon, "':'")) + return nullptr; + Stmt* sub = parseLabeledSub(); + if(!sub) + return nullptr; + Stmt* s = makeStmt(isCase ? StmtKind::Case : StmtKind::Default, kw.offset); + s->expr = value; + s->thenBody = sub; + return s; + } - if(tok.kind == TokKind::KwBreak || tok.kind == TokKind::KwContinue) { - Token kw = advance(); - Stmt* s = - makeStmt(kw.kind == TokKind::KwBreak ? StmtKind::Break : StmtKind::Continue, kw.offset); - if(!expect(TokKind::Semicolon, "';'")) - return nullptr; - return s; - } + // break ; | continue ; + Stmt* Parser::parseBreak() { + Token kw = advance(); + Stmt* s = + makeStmt(kw.kind == TokKind::KwBreak ? StmtKind::Break : StmtKind::Continue, kw.offset); + if(!expect(TokKind::Semicolon, "';'")) + return nullptr; + return s; + } - if(tok.kind == TokKind::KwReturn) { - Token kw = advance(); - Stmt* s = makeStmt(StmtKind::Return, kw.offset); - if(!check(TokKind::Semicolon)) { - s->expr = parseExpression(); - if(!s->expr) - return nullptr; - } - if(!expect(TokKind::Semicolon, "';'")) + // return [expr] ; + Stmt* Parser::parseReturn() { + Token kw = advance(); + Stmt* s = makeStmt(StmtKind::Return, kw.offset); + if(!check(TokKind::Semicolon)) { + s->expr = parseExpression(); + if(!s->expr) return nullptr; - return s; - } - - if(tok.kind == TokKind::Semicolon) { - Token semi = advance(); - return makeStmt(StmtKind::Empty, semi.offset); } + if(!expect(TokKind::Semicolon, "';'")) + return nullptr; + return s; + } - Stmt* s = makeStmt(StmtKind::Expr, tok.offset); + // expr ; + Stmt* Parser::parseExprStatement() { + Stmt* s = makeStmt(StmtKind::Expr, peek().offset); s->expr = parseExpression(); if(!s->expr) return nullptr; @@ -324,6 +280,50 @@ namespace rat::cc { return s; } + // compound | asm | name : stmt | goto name ; | declaration + // | if | while | do | for | switch | case const-expr : [stmt] | default : [stmt] + // | break ; | continue ; | return [expr] ; | ; | expr ; + Stmt* Parser::parseStatement() { + DepthScope scope(*this); + if(!enterDepth()) + return nullptr; + const Token& tok = peek(); + if(tok.kind == TokKind::Identifier && peek2().kind == TokKind::Colon) + return parseLabel(); + if(startsType(tok) || tok.kind == TokKind::KwTypedef || tok.kind == TokKind::KwStaticAssert) + return parseDeclaration(nullptr); + switch(tok.kind) { + case TokKind::LBrace: + return parseCompound(); + case TokKind::KwAsm: + return parseAsmStatement(); + case TokKind::KwGoto: + return parseGoto(); + case TokKind::KwIf: + return parseIf(); + case TokKind::KwWhile: + return parseWhile(); + case TokKind::KwDo: + return parseDoWhile(); + case TokKind::KwFor: + return parseFor(); + case TokKind::KwSwitch: + return parseSwitch(); + case TokKind::KwCase: + case TokKind::KwDefault: + return parseCaseLabel(); + case TokKind::KwBreak: + case TokKind::KwContinue: + return parseBreak(); + case TokKind::KwReturn: + return parseReturn(); + case TokKind::Semicolon: + return makeStmt(StmtKind::Empty, advance().offset); + default: + return parseExprStatement(); + } + } + // { [stmt]... } Stmt* Parser::parseCompound() { Token open = peek(); diff --git a/src/compiler/parse/parse_struct.cpp b/src/compiler/parse/parse_struct.cpp index 36f25883..60a0fcc5 100644 --- a/src/compiler/parse/parse_struct.cpp +++ b/src/compiler/parse/parse_struct.cpp @@ -11,26 +11,25 @@ namespace rat::cc { return st; } - // appends f at the next slot that fits align; a union starts every member at 0 - void - Parser::placeField(StructType* st, Field f, U64 size, U32 align, B32 isUnion, StructLayout& l) { - f.offset = isUnion ? 0 : detail::alignUp(l.offset, align); - st->fields.push_back(f); + // the next slot that fits align; a union starts every member at 0 + U64 Parser::placeMember(U64 size, U32 align, B32 isUnion, StructLayout& l) { + U64 at = isUnion ? 0 : detail::alignUp(l.offset, align); if(isUnion) { if(size > l.offset) l.offset = size; } else { - l.offset = f.offset + size; + l.offset = at + size; } l.bitPos = l.offset * 8; if(align > l.align) l.align = align; + return at; } // the members of an unnamed struct or union member become members of st void Parser::spliceAnonMember(StructType* st, const StructType* inner, B32 isUnion, StructLayout& l) { - U64 mbase = isUnion ? 0 : detail::alignUp(l.offset, inner->align); + U64 mbase = placeMember(inner->size, inner->align, isUnion, l); B32 first = true; for(const Field& sub : inner->fields) { Field f = sub; @@ -41,15 +40,6 @@ namespace rat::cc { first = false; st->fields.push_back(f); } - if(isUnion) { - if(inner->size > l.offset) - l.offset = inner->size; - } else { - l.offset = mbase + inner->size; - } - if(inner->align > l.align) - l.align = inner->align; - l.bitPos = l.offset * 8; } // array members need a constant, non-negative bound on every level @@ -170,7 +160,8 @@ namespace rat::cc { U32 falign = typeAlignBytes(ft); if(r.align > falign) falign = r.align; - placeField(st, f, size, falign, isUnion, l); + f.offset = placeMember(size, falign, isUnion, l); + st->fields.push_back(f); return true; } @@ -277,8 +268,39 @@ namespace rat::cc { return expect(TokKind::RParen, "')'"); } - // enum [alignas]... [tag] [ { [ enumerator [, enumerator]... [,] ] } ] + // [ enumerator [, enumerator]... [,] ] } // enumerator: name [= const-expr] + B32 Parser::parseEnumerators(B32& anyNegative) { + I64 next = 0; + while(!check(TokKind::RBrace) && !check(TokKind::Eof)) { + if(!check(TokKind::Identifier)) { + fail(peek(), "expected enumerator name"); + return false; + } + Token name = advance(); + I64 value = next; + if(accept(TokKind::Assign)) { + Expr* init = parseConditional(); + if(!init) + return false; + if(!evalIntConst(init, value)) + return false; + } + if(value < detail::kIntMin || value > detail::kIntMax) { + fail(name, "enumerator value is not representable as int"); + return false; + } + if(value < 0) + anyNegative = true; + enumConstants.set(lex.text(name), value); + next = value + 1; + if(!accept(TokKind::Comma)) + break; + } + return expect(TokKind::RBrace, "'}'"); + } + + // enum [alignas]... [tag] [ { enumerators ] B32 Parser::parseEnumSpec(CType& out) { advance(); // enum String tag; @@ -290,36 +312,8 @@ namespace rat::cc { B32 anyNegative = false; B32 haveList = accept(TokKind::LBrace); - if(haveList) { - I64 next = 0; - while(!check(TokKind::RBrace) && !check(TokKind::Eof)) { - if(!check(TokKind::Identifier)) { - fail(peek(), "expected enumerator name"); - return false; - } - Token name = advance(); - I64 value = next; - if(accept(TokKind::Assign)) { - Expr* init = parseConditional(); - if(!init) - return false; - if(!evalIntConst(init, value)) - return false; - } - if(value < detail::kIntMin || value > detail::kIntMax) { - fail(name, "enumerator value is not representable as int"); - return false; - } - if(value < 0) - anyNegative = true; - enumConstants.set(lex.text(name), value); - next = value + 1; - if(!accept(TokKind::Comma)) - break; - } - if(!expect(TokKind::RBrace, "'}'")) - return false; - } + if(haveList && !parseEnumerators(anyNegative)) + return false; if(!tag.empty()) { if(haveList) enumSignedTags.set(tag, anyNegative); diff --git a/src/compiler/parse/parse_type.cpp b/src/compiler/parse/parse_type.cpp index 57e2b29d..cc920b58 100644 --- a/src/compiler/parse/parse_type.cpp +++ b/src/compiler/parse/parse_type.cpp @@ -179,13 +179,8 @@ namespace rat::cc { return true; } - // [ qualifier | storage | alignas ]... spec-body [ noinline | alignas ]... - // spec-body: typeof-spec | enum-spec | struct-spec | typedef-name - // | type-keyword [ type-keyword | qualifier | storage | alignas ]... - B32 Parser::parseTypeSpec(CType& out) { - DeclSpecs seen; - specs = DeclSpecs{}; - specAlign = 0; + // [ qualifier | storage | alignas ]... + B32 Parser::parseQualStorage(DeclSpecs& seen) { for(;;) { if(check(TokKind::KwAlignas)) { if(!parseAlignasSpec(specAlign)) @@ -193,93 +188,55 @@ namespace rat::cc { continue; } if(!detail::isQualOrStorage(peek().kind)) - break; + return true; applyQualStorage(seen, advance().kind); } - if(seen.storageCount > 1) { - fail(peek(), "more than one storage-class specifier"); - return false; - } - if(check(TokKind::KwTypeof)) - return parseTypeofSpec(out) && finishTypeSpec(seen, out); - if(check(TokKind::KwEnum)) - return parseEnumSpec(out) && finishTypeSpec(seen, out); - if(check(TokKind::KwStruct) || check(TokKind::KwUnion)) - return parseStructSpec(out) && finishTypeSpec(seen, out); - if(check(TokKind::Identifier)) { - if(const CType* td = typedefs.get(lex.text(peek()))) { - advance(); - out = *td; - return finishTypeSpec(seen, out); - } - } + } - B32 isVoid = false, isBool = false, isChar = false, isShort = false; - B32 isUnsigned = false, isSigned = false; - B32 isFloat = false, isDouble = false; - B32 isComplex = false; - I32 longCount = 0; - I32 count = 0; + // type-keyword [ type-keyword | qualifier | storage | alignas ]... + B32 Parser::parseTypeKeywords(DeclSpecs& seen, TypeWords& w) { + // clang-format off + static const TokKind kWords[] = { + TokKind::KwVoid, TokKind::KwBool, TokKind::KwChar, TokKind::KwShort, TokKind::KwInt, + TokKind::KwLong, TokKind::KwFloat, TokKind::KwDouble, TokKind::KwSigned, + TokKind::KwUnsigned, TokKind::KwComplex, TokKind::KwImaginary, + }; + // clang-format on for(;;) { + if(!parseQualStorage(seen)) + return false; TokKind k = peek().kind; - if(k == TokKind::KwVoid) - isVoid = true; - else if(k == TokKind::KwBool) - isBool = true; - else if(k == TokKind::KwChar) - isChar = true; - else if(k == TokKind::KwShort) - isShort = true; - else if(k == TokKind::KwFloat) - isFloat = true; - else if(k == TokKind::KwDouble) - isDouble = true; - else if(k == TokKind::KwComplex || k == TokKind::KwImaginary) - isComplex = true; - else if(k == TokKind::KwLong) - ++longCount; - else if(k == TokKind::KwUnsigned) - isUnsigned = true; - else if(k == TokKind::KwSigned) - isSigned = true; - else if(k == TokKind::KwInt) - ; // base int - else if(k == TokKind::KwAlignas) { - if(!parseAlignasSpec(specAlign)) - return false; - continue; - } else if(detail::isQualOrStorage(k)) { - applyQualStorage(seen, advance().kind); - continue; - } else - break; + if(std::find(std::begin(kWords), std::end(kWords), k) == std::end(kWords)) + return true; advance(); - ++count; - } - if(count == 0) - return false; - if(seen.storageCount > 1) { - fail(peek(), "more than one storage-class specifier"); - return false; + ++w.count[(U32)k]; + ++w.total; } + } + + CType Parser::basicType(const TypeWords& w) { + B32 isFloat = w.of(TokKind::KwFloat) != 0; + B32 isComplex = w.of(TokKind::KwComplex) || w.of(TokKind::KwImaginary); + B32 isUnsigned = w.of(TokKind::KwUnsigned) != 0; + U32 longCount = w.of(TokKind::KwLong); CType t; - if(isVoid) { + if(w.of(TokKind::KwVoid)) { t.base = CType::Base::Void; - } else if(isFloat || isDouble || isComplex) { + } else if(isFloat || w.of(TokKind::KwDouble) || isComplex) { t.base = CType::Base::Float; - t.bits = isFloat ? 32 : (isDouble && longCount >= 1 ? 128 : 64); + t.bits = isFloat ? 32 : (w.of(TokKind::KwDouble) && longCount >= 1 ? 128 : 64); t.set(CType::Complex, isComplex); if(isComplex) t.strukt = complexStruct(t); - } else if(isBool) { + } else if(w.of(TokKind::KwBool)) { t.bits = 1; t.set(CType::Unsigned); } else { t.set(CType::Unsigned, isUnsigned); - if(isChar) { + if(w.of(TokKind::KwChar)) { t.bits = 8; - t.set(CType::PlainChar, !isUnsigned && !isSigned); - } else if(isShort) + t.set(CType::PlainChar, !isUnsigned && !w.of(TokKind::KwSigned)); + } else if(w.of(TokKind::KwShort)) t.bits = 16; else if(longCount >= 2) { t.bits = 64; @@ -291,7 +248,43 @@ namespace rat::cc { } else t.bits = 32; } - out = t; + return t; + } + + // [ qualifier | storage | alignas ]... spec-body [ noinline | alignas ]... + // spec-body: typeof-spec | enum-spec | struct-spec | typedef-name + // | type-keyword [ type-keyword | qualifier | storage | alignas ]... + B32 Parser::parseTypeSpec(CType& out) { + DeclSpecs seen; + specs = DeclSpecs{}; + specAlign = 0; + if(!parseQualStorage(seen)) + return false; + if(seen.storageCount > 1) { + fail(peek(), "more than one storage-class specifier"); + return false; + } + if(check(TokKind::KwTypeof)) + return parseTypeofSpec(out) && finishTypeSpec(seen, out); + if(check(TokKind::KwEnum)) + return parseEnumSpec(out) && finishTypeSpec(seen, out); + if(check(TokKind::KwStruct) || check(TokKind::KwUnion)) + return parseStructSpec(out) && finishTypeSpec(seen, out); + if(check(TokKind::Identifier)) { + if(const CType* td = typedefs.get(lex.text(peek()))) { + advance(); + out = *td; + return finishTypeSpec(seen, out); + } + } + TypeWords w; + if(!parseTypeKeywords(seen, w) || w.total == 0) + return false; + if(seen.storageCount > 1) { + fail(peek(), "more than one storage-class specifier"); + return false; + } + out = basicType(w); return finishTypeSpec(seen, out); } } // namespace rat::cc diff --git a/src/compiler/parse/parser.h b/src/compiler/parse/parser.h index a27aee31..0c854147 100644 --- a/src/compiler/parse/parser.h +++ b/src/compiler/parse/parser.h @@ -88,13 +88,22 @@ namespace rat::cc { U32 storageCount = 0; }; B32 startsType(const Token& tok); + struct TypeWords { + U8 count[(U32)TokKind::KwAlignas + 1] = {}; + U32 total = 0; + U32 of(TokKind k) const { return count[(U32)k]; } + }; B32 parseTypeSpec(CType& out); + B32 parseQualStorage(DeclSpecs& seen); + B32 parseTypeKeywords(DeclSpecs& seen, TypeWords& w); + CType basicType(const TypeWords& w); void applyQualStorage(DeclSpecs& seen, TokKind kind); B32 finishTypeSpec(DeclSpecs seen, CType& out); B32 parseAlignasSpec(U32& align); B32 acceptTrailingAlignas(U32& align); B32 parseTypeofSpec(CType& out); B32 parseEnumSpec(CType& out); + B32 parseEnumerators(B32& anyNegative); B32 parseStructSpec(CType& out); struct StructLayout { U64 offset = 0; // bytes used so far; the union's widest member @@ -106,7 +115,7 @@ namespace rat::cc { B32 parseStructMember(StructType* st, CType base, U32 baseAlign, B32 isUnion, StructLayout& l); B32 arrayMemberCount(CType t, U64& count); B32 parseBitfield(StructType* st, Field f, U32 memberAlign, B32 isUnion, StructLayout& l); - void placeField(StructType* st, Field f, U64 size, U32 align, B32 isUnion, StructLayout& l); + U64 placeMember(U64 size, U32 align, B32 isUnion, StructLayout& l); StructType* complexStruct(CType realType); B32 parseTypedef(); B32 parseTypeName(CType& out); @@ -135,7 +144,6 @@ namespace rat::cc { B32 parseDirectDeclarator(List& ops, DeclResult& out); B32 parseDeclaratorSuffixes(List& ops, U32& align); B32 parseArrayBound(DeclOp& op); - void skipArrayQualifiers(); B32 parseParamTypeList(FuncType* ft); B32 parseParamNames(FuncType* ft); B32 looksLikeGroupingParen(); @@ -171,8 +179,16 @@ namespace rat::cc { Stmt* parseAsmStatement(); B32 parseAsmOperands(List& out); B32 parseAsmStrings(List& out); + B32 parseAsmLabels(List& out); + B32 parseStrings(String& out, const C8* where); B32 parseAsmTemplate(const String*& out); Stmt* parseLabeledSub(); + Stmt* parseLabel(); + Stmt* parseGoto(); + Stmt* parseCaseLabel(); + Stmt* parseBreak(); + Stmt* parseReturn(); + Stmt* parseExprStatement(); Expr* parseParenCond(); Stmt* parseIf(); Stmt* parseWhile(); @@ -183,14 +199,24 @@ namespace rat::cc { // expressions Expr* parseExpression(); Expr* parseInitializer(); + B32 parseInitElement(Expr* list); + B32 parseDesignation(Designator& des, I64& rangeEnd); + B32 parseDesignator(Designator& cur, B32 first, I64& rangeEnd); Expr* parseAssignment(); Expr* parseConditional(); Expr* parseBinary(I32 minPrec); Expr* parseUnary(); + Expr* parseSizeOp(); Expr* parseCastOrCompound(); Expr* parsePostfix(); Expr* parsePostfixTail(Expr* e); Expr* parsePrimary(); + Expr* parsePrimaryString(); + Expr* parsePrimaryFloat(); + Expr* parsePrimaryName(); + Expr* parsePrimaryParen(); + Expr* parseVaArg(); + Expr* parseCall(Expr* e); Expr* parseGeneric(); Expr* parseBuiltinOffsetof(const Token& kw); @@ -213,14 +239,19 @@ namespace rat::cc { // literals B32 parseIntLiteral(const Token& tok, I64& value, U32& bits, U8& mods); + U8 intLiteralMods(U64 v, B32 dec, B32 isUnsigned, U32 lCount, U32& bits) const; B32 parseCharLiteral(const Token& tok, I64& value); B32 parseStringLiteral(const Token& tok, String& out); B32 parseWideStringLiteral(const Token& tok, U32 unitBytes, String& out); B32 decodeEscape(const String& s, U32& i, U32 end, const Token& tok, U32 maxVal, U32& out); B32 decodeUcn(const String& s, U32& i, U32 end, const Token& tok, U32& cp); + B32 decodeBackslash(const String& s, U32& i, U32 end, const Token& tok, U32 maxVal, U32& out); // constant folding B32 evalIntConst(const Expr* e, I64& out); + B32 evalUnaryConst(const Expr* e, I64& out); + B32 evalBinaryConst(const Expr* e, I64& out); + B32 evalSizeofConst(const Expr* e, I64& out); B32 tryEvalIntConst(const Expr* e, I64& out); I64 castConstValue(I64 v, CType ty) const; U64 typeSizeBytes(CType t) const { return typeSize(t, lay.ptrBytes); } diff --git a/src/compiler/parse/parser_detail.h b/src/compiler/parse/parser_detail.h index 1b8f687c..5763ac08 100644 --- a/src/compiler/parse/parser_detail.h +++ b/src/compiler/parse/parser_detail.h @@ -20,6 +20,8 @@ namespace rat::cc::detail { U32 utf8Decode(const String& bytes, U32& i, U32 n); void appendCodeUnits(String& out, U32 cp, U32 unitBytes); U32 escapeMaxVal(C8 prefix); + B32 ucnAt(const String& s, U32 i, U32 end); + void quotedRange(const String& s, C8 quote, U32& begin, U32& end); B32 isTypeQualifier(TokKind kind); B32 isQualOrStorage(TokKind kind); From 6d8d0e6d0e6f1eb89d7eee64531eedbee3fcef62 Mon Sep 17 00:00:00 2001 From: nella Date: Tue, 29 Sep 2026 10:37:09 +0200 Subject: [PATCH 3/3] Tidy driver, drop renameMain. --- src/backend/README.md | 1 - src/backend/pass/opt/rename_symbol.cpp | 34 ----------------- src/backend/pass/opt/rename_symbol.h | 21 ---------- src/backend/rat.h | 2 - src/compiler/builtin_headers.cpp | 11 +++--- src/compiler/builtin_headers.h | 1 + src/compiler/compile.cpp | 3 -- src/compiler/compile.h | 1 - src/compiler/host.cpp | 26 +++++++------ src/compiler/host.h | 1 + src/compiler/main.cpp | 53 ++++++++++++++------------ 11 files changed, 51 insertions(+), 103 deletions(-) delete mode 100644 src/backend/pass/opt/rename_symbol.cpp delete mode 100644 src/backend/pass/opt/rename_symbol.h diff --git a/src/backend/README.md b/src/backend/README.md index e353e3a4..5cb4281f 100644 --- a/src/backend/README.md +++ b/src/backend/README.md @@ -94,7 +94,6 @@ Passes come in three kinds: module `Pass`, `FunctionPass` (run per function), an ## utility - [**verify:**](./pass/verify.h) Edge consistency + per-opcode structural invariants (see [ir](#ir)). -- [**rename-symbol:**](./pass/opt/rename_symbol.h) Rename a function or global and every reference to it. Visualization passes (`text-emitter`, `graph-emitter`) are covered in [x86-64 backend](#x86-64-backend). diff --git a/src/backend/pass/opt/rename_symbol.cpp b/src/backend/pass/opt/rename_symbol.cpp deleted file mode 100644 index 0728678d..00000000 --- a/src/backend/pass/opt/rename_symbol.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "pass/opt/rename_symbol.h" - -#include "ir/function.h" -#include "ir/module.h" -#include "ir/node.h" - -namespace rat { - RenameSymbolPass::RenameSymbolPass(String from, String to) - : from(std::move(from)), - to(std::move(to)) {} - - const C8* RenameSymbolPass::name() const { return "rename-symbol"; } - - B32 RenameSymbolPass::run(Module& module, const TargetInfo&) { - if(from.empty() || from == to) - return false; - - B32 changed = false; - for(Function* fn : module) { - if(fn->getName() == from) { - fn->setName(to); - changed = true; - } - for(Node* n : *fn) { - CallNode* c = dyn_cast(n); - if(c && !c->isIndirect() && c->getCallee() == from) { - c->setCallee(to); - changed = true; - } - } - } - return changed; - } -} // namespace rat diff --git a/src/backend/pass/opt/rename_symbol.h b/src/backend/pass/opt/rename_symbol.h deleted file mode 100644 index f62a994e..00000000 --- a/src/backend/pass/opt/rename_symbol.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef RAT_PASS_OPT_RENAMESYMBOL_H -#define RAT_PASS_OPT_RENAMESYMBOL_H - -#include "core.h" -#include "pass/pass.h" - -namespace rat { - struct Module; - - struct RenameSymbolPass : Pass { - RenameSymbolPass(String from, String to); - - const C8* name() const override; - B32 run(Module& module, const TargetInfo& target) override; - private: - String from; - String to; - }; -} // namespace rat - -#endif diff --git a/src/backend/rat.h b/src/backend/rat.h index c8301004..023e88e1 100644 --- a/src/backend/rat.h +++ b/src/backend/rat.h @@ -16,6 +16,4 @@ #include "pass/emit/x86/x86_lower.h" #include "pass/emit/x86/x86_peephole.h" -#include "pass/opt/rename_symbol.h" - #endif diff --git a/src/compiler/builtin_headers.cpp b/src/compiler/builtin_headers.cpp index 57fd6c2e..1800bb81 100644 --- a/src/compiler/builtin_headers.cpp +++ b/src/compiler/builtin_headers.cpp @@ -37,14 +37,15 @@ namespace rat::cc { return ec ? fs::path() : p; #endif } + + String includeDirOf(const fs::path& exe) { + fs::path h = exe.parent_path() / ".." / "src" / "compiler" / "headers"; + return h.lexically_normal().generic_string(); + } } // namespace detail const String& builtinIncludeDir() { - static const String dir = [] { - detail::fs::path exe = detail::selfExePath(); - detail::fs::path h = exe.parent_path() / ".." / "src" / "compiler" / "headers"; - return h.lexically_normal().generic_string(); - }(); + static const String dir = detail::includeDirOf(detail::selfExePath()); return dir; } } // namespace rat::cc diff --git a/src/compiler/builtin_headers.h b/src/compiler/builtin_headers.h index e26f9ce4..bbcacc63 100644 --- a/src/compiler/builtin_headers.h +++ b/src/compiler/builtin_headers.h @@ -9,6 +9,7 @@ namespace rat::cc { namespace detail { // absolute path of the running executable, cross-platform std::filesystem::path selfExePath(); + String includeDirOf(const std::filesystem::path& exe); } // namespace detail const String& builtinIncludeDir(); diff --git a/src/compiler/compile.cpp b/src/compiler/compile.cpp index 10c314d4..16efa974 100644 --- a/src/compiler/compile.cpp +++ b/src/compiler/compile.cpp @@ -4,9 +4,6 @@ namespace rat::cc { void composePipeline(PassManager& pm, CompileOptions& opt, std::ostream& out) { - if(!opt.renameMain.empty()) - pm.add("main", opt.renameMain); - Set seen; for(UniquePtr& p : opt.optPasses) { String name = p->name(); diff --git a/src/compiler/compile.h b/src/compiler/compile.h index bc8e94b9..ae6fa709 100644 --- a/src/compiler/compile.h +++ b/src/compiler/compile.h @@ -15,7 +15,6 @@ namespace rat::cc { struct CompileOptions { List> optPasses; List> machinePasses; // empty = default x86 pipeline - String renameMain; }; void composePipeline(PassManager& pm, CompileOptions& opt, std::ostream& out); diff --git a/src/compiler/host.cpp b/src/compiler/host.cpp index f30973bf..636f7d41 100644 --- a/src/compiler/host.cpp +++ b/src/compiler/host.cpp @@ -5,20 +5,22 @@ namespace rat::cc { namespace detail { - TargetTriple& hostTripleStorage() { - static TargetTriple triple = [] { - TargetTriple t; + TargetTriple defaultTriple() { + TargetTriple t; #if defined(_WIN32) - t.os = OS::Windows; // native builds default to the host platform + t.os = OS::Windows; // native builds default to the host platform #endif - const char* env = std::getenv("RATCC_TARGET"); - if(env && *env) { - String err; - if(!TargetTriple::parse(env, t, err)) - std::fprintf(stderr, "ratcc: ignoring RATCC_TARGET: %s\n", err.c_str()); - } - return t; - }(); + const C8* env = std::getenv("RATCC_TARGET"); + if(env && *env) { + String err; + if(!TargetTriple::parse(env, t, err)) + std::fprintf(stderr, "ratcc: ignoring RATCC_TARGET: %s\n", err.c_str()); + } + return t; + } + + TargetTriple& hostTripleStorage() { + static TargetTriple triple = defaultTriple(); return triple; } } // namespace detail diff --git a/src/compiler/host.h b/src/compiler/host.h index 7192d59f..92e6462a 100644 --- a/src/compiler/host.h +++ b/src/compiler/host.h @@ -7,6 +7,7 @@ namespace rat::cc { namespace detail { + TargetTriple defaultTriple(); TargetTriple& hostTripleStorage(); } // namespace detail diff --git a/src/compiler/main.cpp b/src/compiler/main.cpp index 10c87045..d54c8fae 100644 --- a/src/compiler/main.cpp +++ b/src/compiler/main.cpp @@ -34,8 +34,10 @@ namespace detail { String passSpec; // -fpasses=: exact opt pipeline String machineSpec; // -fmachine-passes=: exact x86 machine pipeline List extraPasses; // individual -f requests (in order) - B32 timePasses = false, preprocessOnly = false; - B32 noStdInc = false, noPredefs = false; // -nostdinc / -undef: skip builtin headers / predefs + B32 timePasses = false; + B32 preprocessOnly = false; + B32 noStdInc = false; // -nostdinc: skip builtin headers + B32 noPredefs = false; // -undef: skip predefs String targetSpec; PpOptions pp; }; @@ -106,45 +108,47 @@ namespace detail { " -list-passes list available passes and exit\n"; } + // -X and -X + String joinedValue(I32 argc, C8** argv, I32& i, U32 prefix) { + String arg = argv[i]; + if(arg.size() > prefix) + return arg.substr(prefix); + if(++i >= argc) + cli::die(kTool, arg + " expects an argument"); + return argv[i]; + } + + B32 setFlag(const String& arg, const C8* name, B32& out) { return arg == name && (out = true); } + Options parseArgs(I32 argc, C8** argv) { Options opt; for(I32 i = 1; i < argc; ++i) { String arg = argv[i]; - // -X and -X - auto rest = [&](U32 prefix) -> String { - if(arg.size() > prefix) - return arg.substr(prefix); - if(++i >= argc) - cli::die(kTool, arg + " expects an argument"); - return argv[i]; - }; - auto value = [&](const C8* name, String& out) -> B32 { - return cli::value(kTool, argc, argv, i, name, out); - }; - auto flag = [&](const C8* name, B32& out) -> B32 { return arg == name && (out = true); }; cli::stdFlags(kTool, arg, usage); if(arg == "-list-passes") listPasses(std::cout, true), std::exit(0); else if(arg == "-O0" || arg == "-O1" || arg == "-O") opt.optLevel = arg != "-O0"; - else if(flag("-E", opt.preprocessOnly) || flag("-nostdinc", opt.noStdInc) || - flag("-undef", opt.noPredefs) || flag("-ftime-passes", opt.timePasses)) + else if(setFlag(arg, "-E", opt.preprocessOnly) || setFlag(arg, "-nostdinc", opt.noStdInc) || + setFlag(arg, "-undef", opt.noPredefs) || + setFlag(arg, "-ftime-passes", opt.timePasses)) ; - else if(String spec; value("-emit", spec)) + else if(String spec; cli::value(kTool, argc, argv, i, "-emit", spec)) parseEmit(spec, opt.emits); - else if(value("-target", opt.targetSpec) || value("-fpasses", opt.passSpec) || - value("-fmachine-passes", opt.machineSpec)) + else if(cli::value(kTool, argc, argv, i, "-target", opt.targetSpec) || + cli::value(kTool, argc, argv, i, "-fpasses", opt.passSpec) || + cli::value(kTool, argc, argv, i, "-fmachine-passes", opt.machineSpec)) ; else if(arg.rfind("-f", 0) == 0 && createPass(arg.substr(2), std::cerr)) opt.extraPasses.push_back(arg.substr(2)); else if(arg.rfind("-o", 0) == 0) - opt.output = rest(2); + opt.output = joinedValue(argc, argv, i, 2); else if(arg.rfind("-I", 0) == 0) - opt.pp.includeDirs.push_back(rest(2)); + opt.pp.includeDirs.push_back(joinedValue(argc, argv, i, 2)); else if(arg.rfind("-D", 0) == 0) - opt.pp.defines.push_back(rest(2)); + opt.pp.defines.push_back(joinedValue(argc, argv, i, 2)); else if(arg.rfind("-U", 0) == 0) - opt.pp.undefs.push_back(rest(2)); + opt.pp.undefs.push_back(joinedValue(argc, argv, i, 2)); else if(arg.size() > 1 && arg[0] == '-') cli::die(kTool, "unknown option '" + arg + "'"); else if(opt.input.empty()) @@ -266,7 +270,8 @@ static I32 run(I32 argc, C8** argv) { // only -E needs serialized text; every -emit kind runs off the pp token stream B32 needText = opt.preprocessOnly, needToks = !opt.emits.empty(); - String pped, ppErr; + String pped; + String ppErr; TokenStream ts; ::detail::PhaseClock::time_point tPp = ::detail::PhaseClock::now(); B32 ppOk = (!needText || preprocess(path, source, opt.pp, pped, ppErr)) &&