#include <cassert>
#include <cctype>
#include <cmath>
#include <iostream>
#include <optional>
#include <sstream>
#include <variant>
#include "lexer.h"
using namespace std::string_view_literals;
namespace wasm::WATParser {
Name srcAnnotationKind("src");
namespace {
struct LexResult {
std::string_view span;
};
struct LexCtx {
private:
std::string_view input;
size_t lexedSize = 0;
public:
explicit LexCtx(std::string_view in) : input(in) {}
std::optional<LexResult> lexed() const {
if (lexedSize > 0) {
return {LexResult{input.substr(0, lexedSize)}};
}
return {};
}
std::string_view next() const { return input.substr(lexedSize); }
uint8_t peek() const { return next()[0]; }
size_t size() const { return input.size() - lexedSize; }
bool empty() const { return size() == 0; }
bool canFinish() const;
size_t startsWith(std::string_view sv) const {
return next().substr(0, sv.size()) == sv;
}
void take(size_t n) { lexedSize += n; }
void take(const LexResult& res) { lexedSize += res.span.size(); }
bool takePrefix(std::string_view sv) {
if (startsWith(sv)) {
take(sv.size());
return true;
}
return false;
}
void takeAll() { lexedSize = input.size(); }
};
enum OverflowBehavior { DisallowOverflow, IgnoreOverflow };
std::optional<int> getDigit(char c) {
if ('0' <= c && c <= '9') {
return c - '0';
}
return {};
}
std::optional<int> getHexDigit(char c) {
if ('0' <= c && c <= '9') {
return c - '0';
}
if ('A' <= c && c <= 'F') {
return 10 + c - 'A';
}
if ('a' <= c && c <= 'f') {
return 10 + c - 'a';
}
return {};
}
struct LexIntResult : LexResult {
uint64_t n;
Sign sign;
};
struct LexIntCtx : LexCtx {
using LexCtx::take;
private:
uint64_t n = 0;
Sign sign = NoSign;
bool overflow = false;
public:
explicit LexIntCtx(std::string_view in) : LexCtx(in) {}
std::optional<LexIntResult> lexedRaw() {
if (auto basic = LexCtx::lexed()) {
return LexIntResult{*basic, 0, NoSign};
}
return {};
}
std::optional<LexIntResult> lexed() {
if (overflow) {
return {};
}
if (auto basic = LexCtx::lexed()) {
return LexIntResult{*basic, sign == Neg ? -n : n, sign};
}
return {};
}
void takeSign() {
if (takePrefix("+"sv)) {
sign = Pos;
} else if (takePrefix("-"sv)) {
sign = Neg;
} else {
sign = NoSign;
}
}
bool takeDigit() {
if (!empty()) {
if (auto d = getDigit(peek())) {
take(1);
uint64_t newN = n * 10 + *d;
if (newN < n) {
overflow = true;
}
n = newN;
return true;
}
}
return false;
}
bool takeHexdigit() {
if (!empty()) {
if (auto h = getHexDigit(peek())) {
take(1);
uint64_t newN = n * 16 + *h;
if (newN < n) {
overflow = true;
}
n = newN;
return true;
}
}
return false;
}
void take(const LexIntResult& res) {
LexCtx::take(res);
n = res.n;
}
};
struct LexFloatResult : LexResult {
std::optional<uint64_t> nanPayload;
double d;
};
struct LexFloatCtx : LexCtx {
std::optional<uint64_t> nanPayload;
LexFloatCtx(std::string_view in) : LexCtx(in) {}
std::optional<LexFloatResult> lexed() {
const double posNan = std::copysign(NAN, 1.0);
const double negNan = std::copysign(NAN, -1.0);
assert(!std::signbit(posNan) && "expected positive NaN to be positive");
assert(std::signbit(negNan) && "expected negative NaN to be negative");
auto basic = LexCtx::lexed();
if (!basic) {
return {};
}
if (basic->span.substr(0, 3) == "nan"sv ||
basic->span.substr(0, 4) == "+nan"sv) {
return LexFloatResult{*basic, nanPayload, posNan};
}
if (basic->span.substr(0, 4) == "-nan"sv) {
return LexFloatResult{*basic, nanPayload, negNan};
}
std::stringstream ss;
for (const char *curr = basic->span.data(),
*end = curr + basic->span.size();
curr != end;
++curr) {
if (*curr != '_') {
ss << *curr;
}
}
std::string str = ss.str();
char* last;
double d = std::strtod(str.data(), &last);
assert(last == str.data() + str.size() && "could not parse float");
return LexFloatResult{*basic, {}, d};
}
};
struct LexStrResult : LexResult {
std::optional<std::string> str;
};
struct LexStrCtx : LexCtx {
private:
std::optional<std::stringstream> escapeBuilder;
public:
LexStrCtx(std::string_view in) : LexCtx(in) {}
std::optional<LexStrResult> lexed() {
if (auto basic = LexCtx::lexed()) {
if (escapeBuilder) {
return LexStrResult{*basic, {escapeBuilder->str()}};
} else {
return LexStrResult{*basic, {}};
}
}
return {};
}
void takeChar() {
if (escapeBuilder) {
*escapeBuilder << peek();
}
LexCtx::take(1);
}
void ensureBuildingEscaped() {
if (escapeBuilder) {
return;
}
escapeBuilder = std::stringstream{};
*escapeBuilder << LexCtx::lexed()->span.substr(1);
}
void appendEscaped(char c) { *escapeBuilder << c; }
bool appendUnicode(uint64_t u) {
if ((0xd800 <= u && u < 0xe000) || 0x110000 <= u) {
return false;
}
if (u < 0x80) {
*escapeBuilder << uint8_t(u);
} else if (u < 0x800) {
*escapeBuilder << uint8_t(0b11000000 | ((u >> 6) & 0b00011111));
*escapeBuilder << uint8_t(0b10000000 | ((u >> 0) & 0b00111111));
} else if (u < 0x10000) {
*escapeBuilder << uint8_t(0b11100000 | ((u >> 12) & 0b00001111));
*escapeBuilder << uint8_t(0b10000000 | ((u >> 6) & 0b00111111));
*escapeBuilder << uint8_t(0b10000000 | ((u >> 0) & 0b00111111));
} else {
*escapeBuilder << uint8_t(0b11110000 | ((u >> 18) & 0b00000111));
*escapeBuilder << uint8_t(0b10000000 | ((u >> 12) & 0b00111111));
*escapeBuilder << uint8_t(0b10000000 | ((u >> 6) & 0b00111111));
*escapeBuilder << uint8_t(0b10000000 | ((u >> 0) & 0b00111111));
}
return true;
}
};
struct LexIdResult : LexResult {
bool isStr = false;
std::optional<std::string> str;
};
struct LexIdCtx : LexCtx {
bool isStr = false;
std::optional<std::string> str;
LexIdCtx(std::string_view in) : LexCtx(in) {}
std::optional<LexIdResult> lexed() {
if (auto basic = LexCtx::lexed()) {
return LexIdResult{*basic, isStr, str};
}
return {};
}
};
struct LexAnnotationResult : LexResult {
Annotation annotation;
};
struct LexAnnotationCtx : LexCtx {
std::string_view kind;
size_t kindSize = 0;
std::string_view contents;
size_t contentsSize = 0;
explicit LexAnnotationCtx(std::string_view in) : LexCtx(in) {}
void startKind() { kind = next(); }
void takeKind(size_t size) {
kindSize += size;
take(size);
}
void setKind(std::string_view kind) {
this->kind = kind;
kindSize = kind.size();
}
void startContents() { contents = next(); }
void takeContents(size_t size) {
contentsSize += size;
take(size);
}
std::optional<LexAnnotationResult> lexed() {
if (auto basic = LexCtx::lexed()) {
return LexAnnotationResult{
*basic,
{Name(kind.substr(0, kindSize)), contents.substr(0, contentsSize)}};
}
return std::nullopt;
}
};
std::optional<LexResult> lparen(std::string_view in) {
LexCtx ctx(in);
ctx.takePrefix("("sv);
return ctx.lexed();
}
std::optional<LexResult> rparen(std::string_view in) {
LexCtx ctx(in);
ctx.takePrefix(")"sv);
return ctx.lexed();
}
std::optional<LexResult> idchar(std::string_view);
std::optional<LexResult> space(std::string_view);
std::optional<LexResult> keyword(std::string_view);
std::optional<LexIntResult> integer(std::string_view);
std::optional<LexFloatResult> float_(std::string_view);
std::optional<LexStrResult> str(std::string_view);
std::optional<LexIdResult> ident(std::string_view);
std::optional<LexAnnotationResult> annotation(std::string_view in) {
LexAnnotationCtx ctx(in);
if (ctx.takePrefix(";;@"sv)) {
ctx.setKind(srcAnnotationKind.str);
ctx.startContents();
if (auto size = ctx.next().find('\n'); size != ""sv.npos) {
ctx.takeContents(size);
} else {
ctx.takeContents(ctx.next().size());
}
} else if (ctx.takePrefix("(@"sv)) {
ctx.startKind();
bool hasIdchar = false;
while (auto lexed = idchar(ctx.next())) {
ctx.takeKind(1);
hasIdchar = true;
}
if (!hasIdchar) {
return std::nullopt;
}
ctx.startContents();
size_t depth = 1;
while (true) {
if (ctx.empty()) {
return std::nullopt;
}
if (auto lexed = space(ctx.next())) {
ctx.takeContents(lexed->span.size());
continue;
}
if (auto lexed = keyword(ctx.next())) {
ctx.takeContents(lexed->span.size());
continue;
}
if (auto lexed = integer(ctx.next())) {
ctx.takeContents(lexed->span.size());
continue;
}
if (auto lexed = float_(ctx.next())) {
ctx.takeContents(lexed->span.size());
continue;
}
if (auto lexed = str(ctx.next())) {
ctx.takeContents(lexed->span.size());
continue;
}
if (auto lexed = ident(ctx.next())) {
ctx.takeContents(lexed->span.size());
continue;
}
if (ctx.startsWith("(@"sv)) {
ctx.takeContents(2);
bool hasIdchar = false;
while (auto lexed = idchar(ctx.next())) {
ctx.takeContents(1);
hasIdchar = true;
}
if (!hasIdchar) {
return std::nullopt;
}
++depth;
continue;
}
if (ctx.startsWith("("sv)) {
ctx.takeContents(1);
++depth;
continue;
}
if (ctx.startsWith(")"sv)) {
--depth;
if (depth == 0) {
ctx.take(1);
break;
}
ctx.takeContents(1);
continue;
}
return std::nullopt;
}
}
return ctx.lexed();
}
std::optional<LexResult> comment(std::string_view in) {
LexCtx ctx(in);
if (ctx.size() < 2) {
return {};
}
if (!ctx.startsWith(";;@"sv) && ctx.takePrefix(";;"sv)) {
if (auto size = ctx.next().find('\n'); size != ""sv.npos) {
ctx.take(size);
} else {
ctx.takeAll();
}
return ctx.lexed();
}
if (ctx.takePrefix("(;"sv)) {
size_t depth = 1;
while (depth > 0 && ctx.size() >= 2) {
if (ctx.takePrefix("(;"sv)) {
++depth;
} else if (ctx.takePrefix(";)"sv)) {
--depth;
} else {
ctx.take(1);
}
}
if (depth > 0) {
return {};
}
return ctx.lexed();
}
return {};
}
std::optional<LexResult> spacechar(std::string_view in) {
LexCtx ctx(in);
ctx.takePrefix(" "sv) || ctx.takePrefix("\n"sv) || ctx.takePrefix("\r"sv) ||
ctx.takePrefix("\t"sv);
return ctx.lexed();
}
std::optional<LexResult> space(std::string_view in) {
LexCtx ctx(in);
while (ctx.size()) {
if (auto lexed = spacechar(ctx.next())) {
ctx.take(*lexed);
} else if (auto lexed = comment(ctx.next())) {
ctx.take(*lexed);
} else {
break;
}
}
return ctx.lexed();
}
bool LexCtx::canFinish() const {
return empty() || lparen(next()) || rparen(next()) || spacechar(next()) ||
startsWith(";;"sv);
}
std::optional<LexIntResult> num(std::string_view in,
OverflowBehavior overflow = DisallowOverflow) {
LexIntCtx ctx(in);
if (ctx.empty()) {
return {};
}
if (!ctx.takeDigit()) {
return {};
}
while (true) {
bool under = ctx.takePrefix("_"sv);
if (!ctx.takeDigit()) {
if (!under) {
return overflow == DisallowOverflow ? ctx.lexed() : ctx.lexedRaw();
}
return {};
}
}
}
std::optional<LexIntResult>
hexnum(std::string_view in, OverflowBehavior overflow = DisallowOverflow) {
LexIntCtx ctx(in);
if (!ctx.takeHexdigit()) {
return {};
}
while (true) {
bool under = ctx.takePrefix("_"sv);
if (!ctx.takeHexdigit()) {
if (!under) {
return overflow == DisallowOverflow ? ctx.lexed() : ctx.lexedRaw();
}
return {};
}
}
}
std::optional<LexIntResult> integer(std::string_view in) {
LexIntCtx ctx(in);
ctx.takeSign();
if (ctx.takePrefix("0x"sv)) {
if (auto lexed = hexnum(ctx.next())) {
ctx.take(*lexed);
if (ctx.canFinish()) {
return ctx.lexed();
}
}
return {};
}
if (auto lexed = num(ctx.next())) {
ctx.take(*lexed);
if (ctx.canFinish()) {
return ctx.lexed();
}
}
return {};
}
std::optional<LexResult> decfloat(std::string_view in) {
LexCtx ctx(in);
if (auto lexed = num(ctx.next(), IgnoreOverflow)) {
ctx.take(*lexed);
} else {
return {};
}
if (ctx.takePrefix("."sv)) {
if (auto lexed = num(ctx.next(), IgnoreOverflow)) {
ctx.take(*lexed);
}
}
if (ctx.takePrefix("E"sv) || ctx.takePrefix("e"sv)) {
ctx.takePrefix("+"sv) || ctx.takePrefix("-"sv);
if (auto lexed = num(ctx.next(), IgnoreOverflow)) {
ctx.take(*lexed);
} else {
return {};
}
}
return ctx.lexed();
}
std::optional<LexResult> hexfloat(std::string_view in) {
LexCtx ctx(in);
if (!ctx.takePrefix("0x"sv)) {
return {};
}
if (auto lexed = hexnum(ctx.next(), IgnoreOverflow)) {
ctx.take(*lexed);
} else {
return {};
}
if (ctx.takePrefix("."sv)) {
if (auto lexed = hexnum(ctx.next(), IgnoreOverflow)) {
ctx.take(*lexed);
}
}
if (ctx.takePrefix("P"sv) || ctx.takePrefix("p"sv)) {
ctx.takePrefix("+"sv) || ctx.takePrefix("-"sv);
if (auto lexed = num(ctx.next(), IgnoreOverflow)) {
ctx.take(*lexed);
} else {
return {};
}
}
return ctx.lexed();
}
std::optional<LexFloatResult> float_(std::string_view in) {
LexFloatCtx ctx(in);
ctx.takePrefix("+"sv) || ctx.takePrefix("-"sv);
if (auto lexed = hexfloat(ctx.next())) {
ctx.take(*lexed);
} else if (auto lexed = decfloat(ctx.next())) {
ctx.take(*lexed);
} else if (ctx.takePrefix("inf"sv)) {
} else if (ctx.takePrefix("nan"sv)) {
if (ctx.takePrefix(":0x"sv)) {
if (auto lexed = hexnum(ctx.next())) {
ctx.take(*lexed);
ctx.nanPayload = lexed->n;
} else {
return {};
}
} else {
}
} else {
return {};
}
if (ctx.canFinish()) {
return ctx.lexed();
}
return {};
}
std::optional<LexResult> idchar(std::string_view in) {
LexCtx ctx(in);
if (ctx.empty()) {
return {};
}
uint8_t c = ctx.peek();
if (('0' <= c && c <= '9') || ('A' <= c && c <= 'Z') ||
('a' <= c && c <= 'z')) {
ctx.take(1);
} else {
switch (c) {
case '!':
case '#':
case '$':
case '%':
case '&':
case '\'':
case '*':
case '+':
case '-':
case '.':
case '/':
case ':':
case '<':
case '=':
case '>':
case '?':
case '@':
case '\\':
case '^':
case '_':
case '`':
case '|':
case '~':
ctx.take(1);
}
}
return ctx.lexed();
}
std::optional<LexStrResult> str(std::string_view in) {
LexStrCtx ctx(in);
if (!ctx.takePrefix("\""sv)) {
return {};
}
while (!ctx.takePrefix("\""sv)) {
if (ctx.empty()) {
return {};
}
if (ctx.startsWith("\\"sv)) {
ctx.ensureBuildingEscaped();
ctx.take(1);
if (ctx.takePrefix("t"sv)) {
ctx.appendEscaped('\t');
} else if (ctx.takePrefix("n"sv)) {
ctx.appendEscaped('\n');
} else if (ctx.takePrefix("r"sv)) {
ctx.appendEscaped('\r');
} else if (ctx.takePrefix("\\"sv)) {
ctx.appendEscaped('\\');
} else if (ctx.takePrefix("\""sv)) {
ctx.appendEscaped('"');
} else if (ctx.takePrefix("'"sv)) {
ctx.appendEscaped('\'');
} else if (ctx.takePrefix("u{"sv)) {
auto lexed = hexnum(ctx.next());
if (!lexed) {
return {};
}
ctx.take(*lexed);
if (!ctx.takePrefix("}"sv)) {
return {};
}
if (!ctx.appendUnicode(lexed->n)) {
return {};
}
} else {
LexIntCtx ictx(ctx.next());
if (!ictx.takeHexdigit() || !ictx.takeHexdigit()) {
return {};
}
auto lexed = *ictx.lexed();
ctx.take(lexed);
ctx.appendEscaped(char(lexed.n));
}
} else {
if (uint8_t c = ctx.peek(); c >= 0x20 && c != 0x7F) {
ctx.takeChar();
} else {
return {};
}
}
}
return ctx.lexed();
}
std::optional<LexIdResult> ident(std::string_view in) {
LexIdCtx ctx(in);
if (!ctx.takePrefix("$"sv)) {
return {};
}
if (auto s = str(ctx.next())) {
ctx.isStr = true;
ctx.str = s->str;
ctx.take(*s);
} else if (auto lexed = idchar(ctx.next())) {
ctx.take(*lexed);
while (auto lexed = idchar(ctx.next())) {
ctx.take(*lexed);
}
} else {
return {};
}
if (ctx.canFinish()) {
return ctx.lexed();
}
return {};
}
std::optional<LexResult> keyword(std::string_view in) {
LexCtx ctx(in);
if (ctx.empty()) {
return {};
}
uint8_t start = ctx.peek();
if ('a' <= start && start <= 'z') {
ctx.take(1);
} else {
return {};
}
while (auto lexed = idchar(ctx.next())) {
ctx.take(*lexed);
}
return ctx.lexed();
}
}
template<typename T> std::optional<T> Token::getU() const {
static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>);
if (auto* tok = std::get_if<IntTok>(&data)) {
if (tok->sign == NoSign && tok->n <= std::numeric_limits<T>::max()) {
return T(tok->n);
}
}
return {};
}
template<typename T> std::optional<T> Token::getS() const {
static_assert(std::is_integral_v<T> && std::is_signed_v<T>);
if (auto* tok = std::get_if<IntTok>(&data)) {
if (tok->sign == Neg) {
if (uint64_t(std::numeric_limits<T>::min()) <= tok->n || tok->n == 0) {
return T(tok->n);
}
} else {
if (tok->n <= uint64_t(std::numeric_limits<T>::max())) {
return T(tok->n);
}
}
}
return {};
}
template<typename T> std::optional<T> Token::getI() const {
static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>);
if (auto n = getU<T>()) {
return *n;
}
if (auto n = getS<std::make_signed_t<T>>()) {
return T(*n);
}
return {};
}
template std::optional<uint64_t> Token::getU<uint64_t>() const;
template std::optional<int64_t> Token::getS<int64_t>() const;
template std::optional<uint64_t> Token::getI<uint64_t>() const;
template std::optional<uint32_t> Token::getU<uint32_t>() const;
template std::optional<int32_t> Token::getS<int32_t>() const;
template std::optional<uint32_t> Token::getI<uint32_t>() const;
template std::optional<uint16_t> Token::getU<uint16_t>() const;
template std::optional<int16_t> Token::getS<int16_t>() const;
template std::optional<uint16_t> Token::getI<uint16_t>() const;
template std::optional<uint8_t> Token::getU<uint8_t>() const;
template std::optional<int8_t> Token::getS<int8_t>() const;
template std::optional<uint8_t> Token::getI<uint8_t>() const;
std::optional<double> Token::getF64() const {
constexpr int signif = 52;
constexpr uint64_t payloadMask = (1ull << signif) - 1;
constexpr uint64_t nanDefault = 1ull << (signif - 1);
if (auto* tok = std::get_if<FloatTok>(&data)) {
double d = tok->d;
if (std::isnan(d)) {
uint64_t payload = tok->nanPayload ? *tok->nanPayload : nanDefault;
if (payload == 0 || payload > payloadMask) {
return {};
}
uint64_t bits;
static_assert(sizeof(bits) == sizeof(d));
memcpy(&bits, &d, sizeof(bits));
bits = (bits & ~payloadMask) | payload;
memcpy(&d, &bits, sizeof(bits));
}
return d;
}
if (auto* tok = std::get_if<IntTok>(&data)) {
if (tok->sign == Neg) {
if (tok->n == 0) {
return -0.0;
}
return double(int64_t(tok->n));
}
return double(tok->n);
}
return {};
}
std::optional<float> Token::getF32() const {
constexpr int signif = 23;
constexpr uint32_t payloadMask = (1u << signif) - 1;
constexpr uint64_t nanDefault = 1ull << (signif - 1);
if (auto* tok = std::get_if<FloatTok>(&data)) {
float f = tok->d;
if (std::isnan(f)) {
uint64_t payload = tok->nanPayload ? *tok->nanPayload : nanDefault;
if (payload == 0 || payload > payloadMask) {
return {};
}
uint32_t bits;
static_assert(sizeof(bits) == sizeof(f));
memcpy(&bits, &f, sizeof(bits));
bits = (bits & ~payloadMask) | payload;
memcpy(&f, &bits, sizeof(bits));
}
return f;
}
if (auto* tok = std::get_if<IntTok>(&data)) {
if (tok->sign == Neg) {
if (tok->n == 0) {
return -0.0f;
}
return float(int64_t(tok->n));
}
return float(tok->n);
}
return {};
}
std::optional<std::string_view> Token::getString() const {
if (auto* tok = std::get_if<StringTok>(&data)) {
if (tok->str) {
return std::string_view(*tok->str);
}
return span.substr(1, span.size() - 2);
}
return {};
}
std::optional<std::string_view> Token::getID() const {
if (auto* tok = std::get_if<IdTok>(&data)) {
if (tok->str) {
return std::string_view(*tok->str);
}
if (tok->isStr) {
return span.substr(2, span.size() - 3);
}
return span.substr(1);
}
return {};
}
void Lexer::skipSpace() {
while (true) {
if (auto ctx = annotation(next())) {
index += ctx->span.size();
annotations.push_back(ctx->annotation);
continue;
}
if (auto ctx = space(next())) {
index += ctx->span.size();
continue;
}
break;
}
}
void Lexer::lexToken() {
Token tok;
if (auto t = lparen(next())) {
tok = Token{t->span, LParenTok{}};
} else if (auto t = rparen(next())) {
tok = Token{t->span, RParenTok{}};
} else if (auto t = ident(next())) {
tok = Token{t->span, IdTok{t->isStr, t->str}};
} else if (auto t = integer(next())) {
tok = Token{t->span, IntTok{t->n, t->sign}};
} else if (auto t = float_(next())) {
tok = Token{t->span, FloatTok{t->nanPayload, t->d}};
} else if (auto t = str(next())) {
tok = Token{t->span, StringTok{t->str}};
} else if (auto t = keyword(next())) {
tok = Token{t->span, KeywordTok{}};
} else {
curr = std::nullopt;
return;
}
index += tok.span.size();
curr = {tok};
}
TextPos Lexer::position(const char* c) const {
assert(size_t(c - buffer.data()) <= buffer.size());
TextPos pos{1, 0};
for (const char* p = buffer.data(); p != c; ++p) {
if (*p == '\n') {
pos.line++;
pos.col = 0;
} else {
pos.col++;
}
}
return pos;
}
bool TextPos::operator==(const TextPos& other) const {
return line == other.line && col == other.col;
}
bool IntTok::operator==(const IntTok& other) const {
return n == other.n && sign == other.sign;
}
bool FloatTok::operator==(const FloatTok& other) const {
return std::signbit(d) == std::signbit(other.d) &&
(d == other.d || (std::isnan(d) && std::isnan(other.d) &&
nanPayload == other.nanPayload));
}
bool Token::operator==(const Token& other) const {
return span == other.span &&
std::visit(
[](auto& t1, auto& t2) {
if constexpr (std::is_same_v<decltype(t1), decltype(t2)>) {
return t1 == t2;
} else {
return false;
}
},
data,
other.data);
}
std::ostream& operator<<(std::ostream& os, const TextPos& pos) {
return os << pos.line << ":" << pos.col;
}
std::ostream& operator<<(std::ostream& os, const LParenTok&) {
return os << "'('";
}
std::ostream& operator<<(std::ostream& os, const RParenTok&) {
return os << "')'";
}
std::ostream& operator<<(std::ostream& os, const IdTok&) { return os << "id"; }
std::ostream& operator<<(std::ostream& os, const IntTok& tok) {
return os << (tok.sign == Pos ? "+" : tok.sign == Neg ? "-" : "") << tok.n;
}
std::ostream& operator<<(std::ostream& os, const FloatTok& tok) {
if (std::isnan(tok.d)) {
os << (std::signbit(tok.d) ? "+" : "-");
if (tok.nanPayload) {
return os << "nan:0x" << std::hex << *tok.nanPayload << std::dec;
}
return os << "nan";
}
return os << tok.d;
}
std::ostream& operator<<(std::ostream& os, const StringTok& tok) {
if (tok.str) {
os << '"' << *tok.str << '"';
} else {
os << "(raw string)";
}
return os;
}
std::ostream& operator<<(std::ostream& os, const KeywordTok&) {
return os << "keyword";
}
std::ostream& operator<<(std::ostream& os, const Token& tok) {
std::visit([&](const auto& t) { os << t; }, tok.data);
return os << " \"" << tok.span << "\"";
}
}