#include "hecl/hecl.hpp" #include "hecl/Frontend.hpp" /* Syntatical token parsing system */ namespace hecl::Frontend { /* * hecl = { lf } call { lf { lf } call } { lf } . * call = ident "(" [ expr { "," expr } ] ")" . * expr = sum { ("+" | "-") sum } . * sum = factor { ("*" | "/") factor } . * factor = value | "(" expr ")" . * value = ( call [ "." ident ] ) * | [ "-" ] number * . */ std::string IRNode::rep(int n, std::string_view s) { std::string buf; buf.reserve(n * s.size()); for (int i = 0; i < n; i++) buf.append(s); return buf; } std::string IRNode::fmt(int level, bool stripUVAnims) const { std::string buf; auto indent = rep(level, "\t"sv); switch (kind) { case Kind::Call: if (stripUVAnims && (str == "Texture" || str == "TextureN") && children.size() >= 2) { auto it = children.begin(); IRNode& uvNode = const_cast(*++it); if (uvNode.str != "UV" && uvNode.str != "Normal" && uvNode.str != "View") { std::string replacementName(str); if (uvNode.str.back() == 'N' && replacementName.back() != 'N') replacementName += 'N'; IRNode replacementNode(Kind::Call, std::move(replacementName), std::move(uvNode.children), loc); auto ret = replacementNode.fmt(level, false); uvNode.children = std::move(replacementNode.children); return ret; } } buf.append(indent); buf.append("Call "sv).append(str); if (!children.empty()) { buf.append(" {\n"sv); for (const IRNode& n : children) { buf.append(n.fmt(level + 1, stripUVAnims)); buf.append("\n"sv); } buf.append(indent); buf.append("}"sv); } break; case Kind::Imm: buf.append(indent); buf.append("Imm "sv).append(hecl::Format("%f", val)); break; case Kind::Binop: buf.append(indent); buf.append("Binop "sv).append(OpToStr(op)).append(" {\n"sv); buf.append(left->fmt(level + 1, stripUVAnims)).append("\n"sv); buf.append(right->fmt(level + 1, stripUVAnims)).append("\n"sv); buf.append(indent).append("}"sv); break; case Kind::Swizzle: buf.append(indent); buf.append("Swizzle \""sv).append(str); buf.append("\" {\n"sv); buf.append(left->fmt(level + 1, stripUVAnims)).append("\n"sv); buf.append(indent).append("}"sv); break; default: break; } return buf; } std::string IRNode::describe() const { std::vector l; l.push_back("kind="s + KindToStr(kind).data()); if (!str.empty()) l.push_back("str="s + str); if (kind == Kind::Binop) { l.push_back("op="s + OpToStr(op).data()); l.push_back("left="s + left->toString()); l.push_back("right="s + right->toString()); } if (kind == Kind::Swizzle) l.push_back("node="s + left->toString()); if (kind == Kind::Call) { std::string str = "children=["s; for (auto it = children.begin(); it != children.end(); ++it) { str += it->toString(); if (&*it != &children.back()) str += ';'; } str += ']'; l.push_back(str); } std::string str = "IRNode["s; for (auto it = l.begin(); it != l.end(); ++it) { str += *it; if (&*it != &l.back()) str += ';'; } str += ']'; return str; } void Parser::check(Token::Kind expected) { if (sym == expected) scan(); else error("expected %s, was %s", Token::KindToStr(expected).data(), Token::KindToStr(sym).data()); } IRNode Parser::call() { check(Token::Kind::Ident); std::string name = t.str; std::list args; check(Token::Kind::Lpar); if (sym == Token::Kind::Lpar || sym == Token::Kind::Ident || sym == Token::Kind::Number || sym == Token::Kind::Minus) { args.push_back(expr()); while (sym == Token::Kind::Comma) { scan(); args.push_back(expr()); } } if (sym != Token::Kind::Rpar) error("expected expr|rpar, was %s", Token::KindToStr(sym).data()); else scan(); return IRNode(IRNode::Kind::Call, std::move(name), std::move(args), t.loc); } bool Parser::imm(const IRNode& a, const IRNode& b) { return a.kind == IRNode::Kind::Imm && b.kind == IRNode::Kind::Imm; } IRNode Parser::expr() { IRNode node = sum(); while (sym == Token::Kind::Plus || sym == Token::Kind::Minus) { scan(); Token::Kind op = t.kind; IRNode right = sum(); switch (op) { case Token::Kind::Plus: if (imm(node, right)) // constant folding return IRNode(IRNode::Kind::Imm, node.val + right.val, t.loc); else node = IRNode(IRNode::Op::Add, std::move(node), std::move(right), t.loc); break; case Token::Kind::Minus: if (imm(node, right)) // constant folding node = IRNode(IRNode::Kind::Imm, node.val - right.val, t.loc); else node = IRNode(IRNode::Op::Sub, std::move(node), std::move(right), t.loc); break; default: break; } } return node; } IRNode Parser::sum() { IRNode node = factor(); while (sym == Token::Kind::Times || sym == Token::Kind::Div) { scan(); Token::Kind op = t.kind; IRNode right = factor(); switch (op) { case Token::Kind::Times: if (imm(node, right)) // constant folding node = IRNode(IRNode::Kind::Imm, node.val * right.val, t.loc); else node = IRNode(IRNode::Op::Mul, std::move(node), std::move(right), t.loc); break; case Token::Kind::Div: if (imm(node, right)) // constant folding node = IRNode(IRNode::Kind::Imm, node.val / right.val, t.loc); else node = IRNode(IRNode::Op::Div, std::move(node), std::move(right), t.loc); break; default: break; } } return node; } IRNode Parser::factor() { if (sym == Token::Kind::Lpar) { scan(); IRNode node = expr(); check(Token::Kind::Rpar); return node; } else return value(); } IRNode Parser::value() { if (sym == Token::Kind::Number || sym == Token::Kind::Minus) { scan(); bool neg = false; if (t.kind == Token::Kind::Minus) { neg = true; check(Token::Kind::Number); } float val = strtof(((neg ? "-"s : ""s) + t.str).c_str(), nullptr); return IRNode(IRNode::Kind::Imm, val, t.loc); } else if (sym == Token::Kind::Ident) { IRNode call = Parser::call(); if (sym == Token::Kind::Period) { scan(); check(Token::Kind::Ident); return IRNode(IRNode::Kind::Swizzle, std::string(t.str), std::move(call), t.loc); } return call; } else { error("expected number|call, was %s", Token::KindToStr(sym).data()); return IRNode(); } } std::list Parser::parse() { std::list result; scan(); while (sym == Token::Kind::Lf) scan(); result.push_back(call()); while (sym == Token::Kind::Lf) { while (sym == Token::Kind::Lf) scan(); if (sym != Token::Kind::Eof) result.push_back(call()); } while (sym == Token::Kind::Lf) scan(); check(Token::Kind::Eof); if (hecl::VerbosityLevel > 1) for (auto& res : result) printf("%s\n", res.toString().c_str()); return result; } }