mirror of https://github.com/AxioDL/metaforce.git
Major refactor of hecl parser using @hackyourlife architecture
This commit is contained in:
parent
3aae48d0bf
commit
ddf7c983da
|
@ -37,6 +37,11 @@ private:
|
|||
return hecl::Format("vec3(%g,%g,%g)", vec.vec[0], vec.vec[1], vec.vec[2]);
|
||||
}
|
||||
|
||||
std::string EmitVec3(const std::string& a, const std::string& b, const std::string& c) const
|
||||
{
|
||||
return hecl::Format("vec3(%s,%s,%s)", a.c_str(), b.c_str(), c.c_str());
|
||||
}
|
||||
|
||||
std::string EmitTexGenSource2(TexGenSrc src, int uvIdx) const;
|
||||
std::string EmitTexGenSource4(TexGenSrc src, int uvIdx) const;
|
||||
};
|
||||
|
|
|
@ -33,6 +33,11 @@ private:
|
|||
return hecl::Format("float3(%g,%g,%g)", vec.vec[0], vec.vec[1], vec.vec[2]);
|
||||
}
|
||||
|
||||
std::string EmitVec3(const std::string& a, const std::string& b, const std::string& c) const
|
||||
{
|
||||
return hecl::Format("float3(%s,%s,%s)", a.c_str(), b.c_str(), c.c_str());
|
||||
}
|
||||
|
||||
std::string EmitTexGenSource2(TexGenSrc src, int uvIdx) const;
|
||||
std::string EmitTexGenSource4(TexGenSrc src, int uvIdx) const;
|
||||
};
|
||||
|
|
|
@ -37,6 +37,11 @@ private:
|
|||
return hecl::Format("float3(%g,%g,%g)", vec.vec[0], vec.vec[1], vec.vec[2]);
|
||||
}
|
||||
|
||||
std::string EmitVec3(const std::string& a, const std::string& b, const std::string& c) const
|
||||
{
|
||||
return hecl::Format("float3(%s,%s,%s)", a.c_str(), b.c_str(), c.c_str());
|
||||
}
|
||||
|
||||
std::string EmitTexGenSource2(TexGenSrc src, int uvIdx) const;
|
||||
std::string EmitTexGenSource4(TexGenSrc src, int uvIdx) const;
|
||||
};
|
||||
|
|
|
@ -103,6 +103,7 @@ private:
|
|||
}
|
||||
|
||||
virtual std::string EmitVec3(const atVec4f& vec) const=0;
|
||||
virtual std::string EmitVec3(const std::string& a, const std::string& b, const std::string& c) const=0;
|
||||
|
||||
std::string EmitVal(float val) const
|
||||
{
|
||||
|
|
|
@ -9,11 +9,11 @@
|
|||
#include <hecl/hecl.hpp>
|
||||
#include <boo/graphicsdev/IGraphicsDataFactory.hpp>
|
||||
|
||||
namespace hecl
|
||||
{
|
||||
namespace Frontend
|
||||
namespace hecl::Frontend
|
||||
{
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
struct SourceLocation
|
||||
{
|
||||
int line = -1;
|
||||
|
@ -32,85 +32,250 @@ public:
|
|||
void reset(std::string_view name, std::string_view source) {m_name = name; m_source = source;}
|
||||
void reset(std::string_view name) {m_name = name; m_source.clear();}
|
||||
void setBackend(std::string_view backend) {m_backend = backend;}
|
||||
void reportScannerErr(const SourceLocation& l, const char* format, ...);
|
||||
void reportParserErr(const SourceLocation& l, const char* format, ...);
|
||||
void reportLexerErr(const SourceLocation& l, const char* format, ...);
|
||||
void reportCompileErr(const SourceLocation& l, const char* format, ...);
|
||||
void reportBackendErr(const SourceLocation& l, const char* format, ...);
|
||||
|
||||
std::string_view getName() const {return m_name;}
|
||||
std::string_view getSource() const {return m_source;}
|
||||
};
|
||||
|
||||
class Parser
|
||||
struct Token
|
||||
{
|
||||
public:
|
||||
enum class TokenType
|
||||
enum class Kind
|
||||
{
|
||||
None,
|
||||
SourceBegin,
|
||||
SourceEnd,
|
||||
NumLiteral,
|
||||
VectorSwizzle,
|
||||
EvalGroupStart,
|
||||
EvalGroupEnd,
|
||||
FunctionStart,
|
||||
FunctionEnd,
|
||||
FunctionArgDelim,
|
||||
ArithmeticOp,
|
||||
Eof,
|
||||
Lf,
|
||||
Plus,
|
||||
Minus,
|
||||
Times,
|
||||
Div,
|
||||
Lpar,
|
||||
Rpar,
|
||||
Comma,
|
||||
Period,
|
||||
Ident,
|
||||
Number
|
||||
};
|
||||
private:
|
||||
|
||||
static std::string_view KindToStr(Kind k)
|
||||
{
|
||||
switch (k)
|
||||
{
|
||||
case Kind::None: return "none"sv;
|
||||
case Kind::Eof: return "eof"sv;
|
||||
case Kind::Lf: return "lf"sv;
|
||||
case Kind::Plus: return "+"sv;
|
||||
case Kind::Minus: return "-"sv;
|
||||
case Kind::Times: return "*"sv;
|
||||
case Kind::Div: return "/"sv;
|
||||
case Kind::Lpar: return "("sv;
|
||||
case Kind::Rpar: return ")"sv;
|
||||
case Kind::Comma: return ","sv;
|
||||
case Kind::Period: return "."sv;
|
||||
case Kind::Ident: return "ident"sv;
|
||||
case Kind::Number: return "number"sv;
|
||||
}
|
||||
}
|
||||
|
||||
Kind kind = Kind::None;
|
||||
std::string str;
|
||||
SourceLocation loc;
|
||||
|
||||
Token() = default;
|
||||
Token(Kind kind, const SourceLocation& loc)
|
||||
: kind(kind), loc(loc) {}
|
||||
Token(Kind kind, std::string&& str, const SourceLocation& loc)
|
||||
: kind(kind), str(std::move(str)), loc(loc) {}
|
||||
|
||||
std::string toString() const
|
||||
{
|
||||
if (str.empty())
|
||||
return hecl::Format("%d:%d: %s", loc.line, loc.col, KindToStr(kind).data());
|
||||
else
|
||||
return hecl::Format("%d:%d: %s (%s)", loc.line, loc.col, KindToStr(kind).data(), str.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
class Scanner
|
||||
{
|
||||
friend class Parser;
|
||||
static constexpr char LF = '\n';
|
||||
static constexpr char COMMENT = '#';
|
||||
|
||||
Diagnostics& m_diag;
|
||||
std::string_view m_source;
|
||||
std::string_view::const_iterator m_sourceIt;
|
||||
std::vector<TokenType> m_parenStack;
|
||||
bool m_reset = false;
|
||||
void skipWhitespace(std::string_view::const_iterator& it);
|
||||
public:
|
||||
struct Token
|
||||
{
|
||||
TokenType m_type;
|
||||
SourceLocation m_location;
|
||||
std::string m_tokenString;
|
||||
int m_tokenInt = 0;
|
||||
float m_tokenFloat = 0.0;
|
||||
Token() : m_type(TokenType::None) {}
|
||||
Token(TokenType type, SourceLocation loc) : m_type(type), m_location(loc) {}
|
||||
const char* typeString() const
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case TokenType::None:
|
||||
return "None";
|
||||
case TokenType::SourceBegin:
|
||||
return "SourceBegin";
|
||||
case TokenType::SourceEnd:
|
||||
return "SourceEnd";
|
||||
case TokenType::NumLiteral:
|
||||
return "NumLiteral";
|
||||
case TokenType::VectorSwizzle:
|
||||
return "VectorSwizzle";
|
||||
case TokenType::EvalGroupStart:
|
||||
return "EvalGroupStart";
|
||||
case TokenType::EvalGroupEnd:
|
||||
return "EvalGroupEnd";
|
||||
case TokenType::FunctionStart:
|
||||
return "FunctionStart";
|
||||
case TokenType::FunctionEnd:
|
||||
return "FunctionEnd";
|
||||
case TokenType::FunctionArgDelim:
|
||||
return "FunctionArgDelim";
|
||||
case TokenType::ArithmeticOp:
|
||||
return "ArithmeticOp";
|
||||
default: break;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
void reset(std::string_view source);
|
||||
Token consumeToken();
|
||||
SourceLocation getLocation() const;
|
||||
char ch;
|
||||
SourceLocation loc;
|
||||
int lfcol;
|
||||
|
||||
Parser(Diagnostics& diag) : m_diag(diag) {}
|
||||
std::string lastLine;
|
||||
std::string currentLine;
|
||||
|
||||
Token::Kind CharToTokenKind(char ch)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case '(': return Token::Kind::Lpar;
|
||||
case ')': return Token::Kind::Rpar;
|
||||
case ',': return Token::Kind::Comma;
|
||||
case '.': return Token::Kind::Period;
|
||||
case '+': return Token::Kind::Plus;
|
||||
case '-': return Token::Kind::Minus;
|
||||
case '*': return Token::Kind::Times;
|
||||
case '/': return Token::Kind::Div;
|
||||
default: return Token::Kind::None;
|
||||
}
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
void error(const SourceLocation& loc, const char* s, Args&&... args)
|
||||
{
|
||||
m_diag.reportScannerErr(loc, s, args...);
|
||||
}
|
||||
|
||||
static bool isDigit(char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
static bool isStartIdent(char c)
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
|
||||
(c == '_');
|
||||
}
|
||||
|
||||
static bool isMidIdent(char c)
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
|
||||
(c == '_') || isDigit(c);
|
||||
}
|
||||
|
||||
int _read();
|
||||
bool read();
|
||||
|
||||
static char chr(char c) { return (c < 32 || c > 127) ? '.' : c; }
|
||||
|
||||
public:
|
||||
Scanner(Diagnostics& diag) : m_diag(diag) {}
|
||||
|
||||
void reset(std::string_view in)
|
||||
{
|
||||
m_source = in;
|
||||
m_sourceIt = in.cbegin();
|
||||
ch = 0;
|
||||
loc.line = 1;
|
||||
loc.col = 0;
|
||||
lfcol = 0;
|
||||
lastLine = std::string();
|
||||
currentLine = std::string();
|
||||
read();
|
||||
}
|
||||
|
||||
Token next();
|
||||
};
|
||||
|
||||
struct IRNode
|
||||
{
|
||||
friend struct IR;
|
||||
|
||||
enum class Op
|
||||
{
|
||||
Add, Sub, Mul, Div
|
||||
};
|
||||
enum class Kind
|
||||
{
|
||||
None, Call, Imm, Binop, Swizzle
|
||||
};
|
||||
Kind kind = Kind::None;
|
||||
std::string str;
|
||||
float val;
|
||||
Op op;
|
||||
std::unique_ptr<IRNode> left;
|
||||
std::unique_ptr<IRNode> right;
|
||||
std::list<IRNode> children;
|
||||
SourceLocation loc;
|
||||
|
||||
static std::string_view OpToStr(Op op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case Op::Add: return "+"sv;
|
||||
case Op::Sub: return "-"sv;
|
||||
case Op::Mul: return "*"sv;
|
||||
case Op::Div: return "/"sv;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string_view KindToStr(Kind k)
|
||||
{
|
||||
switch (k)
|
||||
{
|
||||
case Kind::None: return "none"sv;
|
||||
case Kind::Call: return "call"sv;
|
||||
case Kind::Imm: return "imm"sv;
|
||||
case Kind::Binop: return "binop"sv;
|
||||
case Kind::Swizzle: return "swizzle"sv;
|
||||
}
|
||||
}
|
||||
|
||||
IRNode() = default;
|
||||
IRNode(Kind kind, std::string&& str, const SourceLocation& loc)
|
||||
: kind(kind), str(std::move(str)), loc(loc) {}
|
||||
IRNode(Kind kind, float val, const SourceLocation& loc)
|
||||
: kind(kind), val(val), loc(loc) {}
|
||||
IRNode(Kind kind, std::string&& str, std::list<IRNode>&& children, const SourceLocation& loc)
|
||||
: kind(kind), str(std::move(str)), children(std::move(children)), loc(loc) {}
|
||||
IRNode(Op op, IRNode&& left, IRNode&& right, const SourceLocation& loc)
|
||||
: kind(Kind::Binop), op(op), left(new IRNode(std::move(left))), right(new IRNode(std::move(right))), loc(loc) {}
|
||||
IRNode(Kind kind, std::string&& str, IRNode&& node, const SourceLocation& loc)
|
||||
: kind(kind), str(std::move(str)), left(new IRNode(std::move(node))), loc(loc) {}
|
||||
|
||||
std::string toString() const { return fmt(0); }
|
||||
|
||||
private:
|
||||
static std::string rep(int n, std::string_view s);
|
||||
std::string fmt(int level) const;
|
||||
std::string describe() const;
|
||||
};
|
||||
|
||||
class Parser
|
||||
{
|
||||
Scanner m_scanner;
|
||||
|
||||
Token t;
|
||||
Token la;
|
||||
Token::Kind sym;
|
||||
|
||||
void scan()
|
||||
{
|
||||
t = la;
|
||||
la = m_scanner.next();
|
||||
sym = la.kind;
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
void error(const char* s, Args&&... args)
|
||||
{
|
||||
m_scanner.m_diag.reportParserErr(la.loc, s, args...);
|
||||
}
|
||||
|
||||
void check(Token::Kind expected);
|
||||
IRNode call();
|
||||
static bool imm(const IRNode& a, const IRNode& b);
|
||||
IRNode expr();
|
||||
IRNode sum();
|
||||
IRNode factor();
|
||||
IRNode value();
|
||||
|
||||
public:
|
||||
Parser(Diagnostics& diag)
|
||||
: m_scanner(diag) {}
|
||||
|
||||
void reset(std::string_view in) { la = Token(); m_scanner.reset(in); }
|
||||
std::list<IRNode> parse();
|
||||
};
|
||||
|
||||
using BigDNA = athena::io::DNA<athena::BigEndian>;
|
||||
|
@ -175,116 +340,14 @@ struct IR : BigDNA
|
|||
Value<atUint16> m_instIdx;
|
||||
} m_swizzle;
|
||||
|
||||
Instruction(OpType type, const SourceLocation& loc) : m_op(type), m_loc(loc) {}
|
||||
|
||||
int getChildCount() const
|
||||
{
|
||||
switch (m_op)
|
||||
{
|
||||
case OpType::Call:
|
||||
return m_call.m_argInstIdxs.size();
|
||||
case OpType::Arithmetic:
|
||||
return 2;
|
||||
case OpType::Swizzle:
|
||||
return 1;
|
||||
default:
|
||||
LogModule.report(logvisor::Fatal, "invalid op type");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
const IR::Instruction& getChildInst(const IR& ir, size_t idx) const
|
||||
{
|
||||
switch (m_op)
|
||||
{
|
||||
case OpType::Call:
|
||||
return ir.m_instructions.at(m_call.m_argInstIdxs.at(idx));
|
||||
case OpType::Arithmetic:
|
||||
if (idx > 1)
|
||||
LogModule.report(logvisor::Fatal, "arithmetic child idx must be 0 or 1");
|
||||
return ir.m_instructions.at(m_arithmetic.m_instIdxs[idx]);
|
||||
case OpType::Swizzle:
|
||||
if (idx > 0)
|
||||
LogModule.report(logvisor::Fatal, "swizzle child idx must be 0");
|
||||
return ir.m_instructions.at(m_swizzle.m_instIdx);
|
||||
default:
|
||||
LogModule.report(logvisor::Fatal, "invalid op type");
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
const atVec4f& getImmVec() const
|
||||
{
|
||||
if (m_op != OpType::LoadImm)
|
||||
LogModule.report(logvisor::Fatal, "invalid op type");
|
||||
return m_loadImm.m_immVec;
|
||||
}
|
||||
|
||||
void read(athena::io::IStreamReader& reader)
|
||||
{
|
||||
m_op = OpType(reader.readUByte());
|
||||
m_target = reader.readUint16Big();
|
||||
switch (m_op)
|
||||
{
|
||||
default: break;
|
||||
case OpType::Call:
|
||||
m_call.read(reader);
|
||||
break;
|
||||
case OpType::LoadImm:
|
||||
m_loadImm.read(reader);
|
||||
break;
|
||||
case OpType::Arithmetic:
|
||||
m_arithmetic.read(reader);
|
||||
break;
|
||||
case OpType::Swizzle:
|
||||
m_swizzle.read(reader);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void write(athena::io::IStreamWriter& writer) const
|
||||
{
|
||||
writer.writeUByte(m_op);
|
||||
writer.writeUint16Big(m_target);
|
||||
switch (m_op)
|
||||
{
|
||||
default: break;
|
||||
case OpType::Call:
|
||||
m_call.write(writer);
|
||||
break;
|
||||
case OpType::LoadImm:
|
||||
m_loadImm.write(writer);
|
||||
break;
|
||||
case OpType::Arithmetic:
|
||||
m_arithmetic.write(writer);
|
||||
break;
|
||||
case OpType::Swizzle:
|
||||
m_swizzle.write(writer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t binarySize(size_t sz) const
|
||||
{
|
||||
sz += 3;
|
||||
switch (m_op)
|
||||
{
|
||||
default: break;
|
||||
case OpType::Call:
|
||||
sz = m_call.binarySize(sz);
|
||||
break;
|
||||
case OpType::LoadImm:
|
||||
sz = m_loadImm.binarySize(sz);
|
||||
break;
|
||||
case OpType::Arithmetic:
|
||||
sz = m_arithmetic.binarySize(sz);
|
||||
break;
|
||||
case OpType::Swizzle:
|
||||
sz = m_swizzle.binarySize(sz);
|
||||
break;
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
Instruction(OpType type, RegID target, const SourceLocation& loc)
|
||||
: m_op(type), m_target(target), m_loc(loc) {}
|
||||
int getChildCount() const;
|
||||
const IR::Instruction& getChildInst(const IR& ir, size_t idx) const;
|
||||
const atVec4f& getImmVec() const;
|
||||
void read(athena::io::IStreamReader& reader);
|
||||
void write(athena::io::IStreamWriter& writer) const;
|
||||
size_t binarySize(size_t sz) const;
|
||||
|
||||
Instruction(athena::io::IStreamReader& reader) {read(reader);}
|
||||
};
|
||||
|
@ -297,122 +360,23 @@ struct IR : BigDNA
|
|||
boo::BlendFactor m_blendDst = boo::BlendFactor::Zero;
|
||||
bool m_doAlpha = false;
|
||||
|
||||
void read(athena::io::IStreamReader& reader)
|
||||
{
|
||||
m_hash = reader.readUint64Big();
|
||||
m_regCount = reader.readUint16Big();
|
||||
atUint16 instCount = reader.readUint16Big();
|
||||
m_instructions.clear();
|
||||
m_instructions.reserve(instCount);
|
||||
for (atUint16 i=0 ; i<instCount ; ++i)
|
||||
m_instructions.emplace_back(reader);
|
||||
|
||||
/* Pre-resolve blending mode */
|
||||
const IR::Instruction& rootCall = m_instructions.back();
|
||||
m_doAlpha = false;
|
||||
if (!rootCall.m_call.m_name.compare("HECLOpaque"))
|
||||
{
|
||||
m_blendSrc = boo::BlendFactor::One;
|
||||
m_blendDst = boo::BlendFactor::Zero;
|
||||
}
|
||||
else if (!rootCall.m_call.m_name.compare("HECLAlpha"))
|
||||
{
|
||||
m_blendSrc = boo::BlendFactor::SrcAlpha;
|
||||
m_blendDst = boo::BlendFactor::InvSrcAlpha;
|
||||
m_doAlpha = true;
|
||||
}
|
||||
else if (!rootCall.m_call.m_name.compare("HECLAdditive"))
|
||||
{
|
||||
m_blendSrc = boo::BlendFactor::SrcAlpha;
|
||||
m_blendDst = boo::BlendFactor::One;
|
||||
m_doAlpha = true;
|
||||
}
|
||||
}
|
||||
|
||||
void write(athena::io::IStreamWriter& writer) const
|
||||
{
|
||||
writer.writeUint64Big(m_hash);
|
||||
writer.writeUint16Big(m_regCount);
|
||||
writer.writeUint16Big(m_instructions.size());
|
||||
for (const Instruction& inst : m_instructions)
|
||||
inst.write(writer);
|
||||
}
|
||||
|
||||
size_t binarySize(size_t sz) const
|
||||
{
|
||||
sz += 12;
|
||||
for (const Instruction& inst : m_instructions)
|
||||
sz = inst.binarySize(sz);
|
||||
return sz;
|
||||
}
|
||||
};
|
||||
|
||||
class Lexer
|
||||
{
|
||||
friend class OperationNode;
|
||||
Diagnostics& m_diag;
|
||||
|
||||
/* Intermediate tree-node for organizing tokens into operations */
|
||||
struct OperationNode
|
||||
{
|
||||
Parser::Token m_tok;
|
||||
OperationNode* m_prev = nullptr;
|
||||
OperationNode* m_next = nullptr;
|
||||
OperationNode* m_sub = nullptr;
|
||||
|
||||
OperationNode() {}
|
||||
OperationNode(Parser::Token&& tok) : m_tok(std::move(tok)) {}
|
||||
};
|
||||
|
||||
/* Pool of nodes to keep ownership (forward_list so pointers aren't invalidated) */
|
||||
std::forward_list<OperationNode> m_pool;
|
||||
|
||||
/* Final lexed root function (IR comes from this) */
|
||||
OperationNode* m_root = nullptr;
|
||||
|
||||
/* Helper for relinking operator precedence */
|
||||
void ReconnectArithmetic(OperationNode* sn, OperationNode** lastSub, OperationNode** newSub) const;
|
||||
|
||||
/* Recursive IR compile funcs */
|
||||
void RecursiveFuncCompile(IR& ir, const Lexer::OperationNode* funcNode, IR::RegID target) const;
|
||||
void RecursiveGroupCompile(IR& ir, const Lexer::OperationNode* groupNode, IR::RegID target) const;
|
||||
void EmitVec3(IR& ir, const Lexer::OperationNode* funcNode, IR::RegID target) const;
|
||||
void EmitVec4(IR& ir, const Lexer::OperationNode* funcNode, IR::RegID target) const;
|
||||
void EmitArithmetic(IR& ir, const Lexer::OperationNode* arithNode, IR::RegID target) const;
|
||||
void EmitVectorSwizzle(IR& ir, const Lexer::OperationNode* swizNode, IR::RegID target) const;
|
||||
|
||||
static void PrintChain(const Lexer::OperationNode* begin, const Lexer::OperationNode* end);
|
||||
static void PrintTree(const Lexer::OperationNode* node, int indent=0);
|
||||
|
||||
public:
|
||||
void reset();
|
||||
void consumeAllTokens(Parser& parser);
|
||||
IR compileIR(atUint64 hash) const;
|
||||
|
||||
Lexer(Diagnostics& diag) : m_diag(diag) {}
|
||||
static atInt8 swizzleCompIdx(char aChar);
|
||||
int addInstruction(const IRNode& n, IR::RegID target);
|
||||
void read(athena::io::IStreamReader& reader);
|
||||
void write(athena::io::IStreamWriter& writer) const;
|
||||
size_t binarySize(size_t sz) const;
|
||||
};
|
||||
|
||||
class Frontend
|
||||
{
|
||||
Diagnostics m_diag;
|
||||
Parser m_parser;
|
||||
Lexer m_lexer;
|
||||
public:
|
||||
IR compileSource(std::string_view source, std::string_view diagName)
|
||||
{
|
||||
Hash hash(source);
|
||||
m_diag.reset(diagName, source);
|
||||
m_parser.reset(source);
|
||||
m_lexer.consumeAllTokens(m_parser);
|
||||
return m_lexer.compileIR(hash.val64());
|
||||
}
|
||||
|
||||
Diagnostics& getDiagnostics() {return m_diag;}
|
||||
|
||||
Frontend() : m_parser(m_diag), m_lexer(m_diag) {}
|
||||
IR compileSource(std::string_view source, std::string_view diagName);
|
||||
Diagnostics& getDiagnostics() { return m_diag; }
|
||||
Frontend() : m_parser(m_diag) {}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HECLFRONTEND_HPP
|
||||
|
|
|
@ -578,6 +578,8 @@ struct GLSLBackendFactory : IShaderBackendFactory
|
|||
std::string vertSource = r.readString();
|
||||
std::string fragSource = r.readString();
|
||||
|
||||
printf("%s\n%s\n", vertSource.c_str(), fragSource.c_str());
|
||||
|
||||
if (r.hasError())
|
||||
return false;
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ unsigned GX::RecursiveTraceTexGen(const IR& ir, Diagnostics& diag, const IR::Ins
|
|||
if (inst.getChildCount() < 1)
|
||||
diag.reportBackendErr(inst.m_loc, "TexCoordGen UV(layerIdx) requires one argument");
|
||||
const IR::Instruction& idxInst = inst.getChildInst(ir, 0);
|
||||
const atVec4f& idxImm = idxInst.getImmVec();
|
||||
auto& idxImm = idxInst.getImmVec();
|
||||
return addTexCoordGen(diag, inst.m_loc, TexGenSrc(TG_TEX0 + unsigned(idxImm.vec[0])), mtx, normalize, pmtx);
|
||||
}
|
||||
else if (!tcgName.compare("Normal"))
|
||||
|
@ -125,7 +125,7 @@ GX::TraceResult GX::RecursiveTraceColor(const IR& ir, Diagnostics& diag, const I
|
|||
diag.reportBackendErr(inst.m_loc, "Texture(map, texgen) requires 2 arguments");
|
||||
|
||||
const IR::Instruction& mapInst = inst.getChildInst(ir, 0);
|
||||
const atVec4f& mapImm = mapInst.getImmVec();
|
||||
auto& mapImm = mapInst.getImmVec();
|
||||
newStage.m_texMapIdx = unsigned(mapImm.vec[0]);
|
||||
newStage.m_color[0] = swizzleAlpha ? CC_TEXA : CC_TEXC;
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ unsigned ProgrammableCommon::RecursiveTraceTexGen(const IR& ir, Diagnostics& dia
|
|||
if (inst.getChildCount() < 1)
|
||||
diag.reportBackendErr(inst.m_loc, "TexCoordGen UV(layerIdx) requires one argument");
|
||||
const IR::Instruction& idxInst = inst.getChildInst(ir, 0);
|
||||
const atVec4f& idxImm = idxInst.getImmVec();
|
||||
auto& idxImm = idxInst.getImmVec();
|
||||
return addTexCoordGen(TexGenSrc::UV, idxImm.vec[0], mtx, normalize);
|
||||
}
|
||||
else if (!tcgName.compare("Normal"))
|
||||
|
@ -91,7 +91,7 @@ std::string ProgrammableCommon::RecursiveTraceColor(const IR& ir, Diagnostics& d
|
|||
diag.reportBackendErr(inst.m_loc, "Texture(map, texgen) requires 2 arguments");
|
||||
|
||||
const IR::Instruction& mapInst = inst.getChildInst(ir, 0);
|
||||
const atVec4f& mapImm = mapInst.getImmVec();
|
||||
auto& mapImm = mapInst.getImmVec();
|
||||
unsigned mapIdx = unsigned(mapImm.vec[0]);
|
||||
|
||||
const IR::Instruction& tcgInst = inst.getChildInst(ir, 1);
|
||||
|
@ -111,6 +111,17 @@ std::string ProgrammableCommon::RecursiveTraceColor(const IR& ir, Diagnostics& d
|
|||
m_lighting = true;
|
||||
return toSwizzle ? EmitLightingRaw() : EmitLightingRGB();
|
||||
}
|
||||
else if (!name.compare("vec3"))
|
||||
{
|
||||
if (inst.getChildCount() < 3)
|
||||
diag.reportBackendErr(inst.m_loc, "vec3(r,g,b) requires 3 arguments");
|
||||
const IR::Instruction& aInst = inst.getChildInst(ir, 0);
|
||||
const IR::Instruction& bInst = inst.getChildInst(ir, 1);
|
||||
const IR::Instruction& cInst = inst.getChildInst(ir, 2);
|
||||
return EmitVec3(RecursiveTraceAlpha(ir, diag, aInst, false),
|
||||
RecursiveTraceAlpha(ir, diag, bInst, false),
|
||||
RecursiveTraceAlpha(ir, diag, cInst, false));
|
||||
}
|
||||
else
|
||||
diag.reportBackendErr(inst.m_loc, "unable to interpret '%s'", name.c_str());
|
||||
break;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
set(FRONTEND_SOURCES
|
||||
Parser.cpp
|
||||
Lexer.cpp
|
||||
Diagnostics.cpp)
|
||||
Scanner.cpp
|
||||
Diagnostics.cpp
|
||||
Frontend.cpp)
|
||||
|
||||
hecl_add_list(Frontend FRONTEND_SOURCES)
|
||||
|
|
|
@ -12,9 +12,7 @@
|
|||
#define BOLD "\x1b[1m"
|
||||
#define NORMAL "\x1b[0m"
|
||||
|
||||
namespace hecl
|
||||
{
|
||||
namespace Frontend
|
||||
namespace hecl::Frontend
|
||||
{
|
||||
|
||||
std::string Diagnostics::sourceDiagString(const SourceLocation& l, bool ansi) const
|
||||
|
@ -43,6 +41,28 @@ std::string Diagnostics::sourceDiagString(const SourceLocation& l, bool ansi) co
|
|||
return retval;
|
||||
}
|
||||
|
||||
void Diagnostics::reportScannerErr(const SourceLocation& l, const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
char* result = nullptr;
|
||||
#ifdef _WIN32
|
||||
int length = _vscprintf(fmt, ap);
|
||||
result = (char*)malloc(length);
|
||||
vsnprintf(result, length, fmt, ap);
|
||||
#else
|
||||
vasprintf(&result, fmt, ap);
|
||||
#endif
|
||||
va_end(ap);
|
||||
if (logvisor::XtermColor)
|
||||
LogModule.report(logvisor::Fatal, CYAN "[Scanner]" NORMAL " %s " YELLOW "@%d:%d " NORMAL "\n%s\n%s",
|
||||
m_name.c_str(), l.line, l.col, result, sourceDiagString(l, true).c_str());
|
||||
else
|
||||
LogModule.report(logvisor::Fatal, "[Scanner] %s @%d:%d\n%s\n%s",
|
||||
m_name.c_str(), l.line, l.col, result, sourceDiagString(l, false).c_str());
|
||||
free(result);
|
||||
}
|
||||
|
||||
void Diagnostics::reportParserErr(const SourceLocation& l, const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
@ -65,50 +85,6 @@ void Diagnostics::reportParserErr(const SourceLocation& l, const char* fmt, ...)
|
|||
free(result);
|
||||
}
|
||||
|
||||
void Diagnostics::reportLexerErr(const SourceLocation& l, const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
char* result = nullptr;
|
||||
#ifdef _WIN32
|
||||
int length = _vscprintf(fmt, ap);
|
||||
result = (char*)malloc(length);
|
||||
vsnprintf(result, length, fmt, ap);
|
||||
#else
|
||||
vasprintf(&result, fmt, ap);
|
||||
#endif
|
||||
va_end(ap);
|
||||
if (logvisor::XtermColor)
|
||||
LogModule.report(logvisor::Fatal, CYAN "[Lexer]" NORMAL " %s " YELLOW "@%d:%d " NORMAL "\n%s\n%s",
|
||||
m_name.c_str(), l.line, l.col, result, sourceDiagString(l, true).c_str());
|
||||
else
|
||||
LogModule.report(logvisor::Fatal, "[Lexer] %s @%d:%d\n%s\n%s",
|
||||
m_name.c_str(), l.line, l.col, result, sourceDiagString(l, false).c_str());
|
||||
free(result);
|
||||
}
|
||||
|
||||
void Diagnostics::reportCompileErr(const SourceLocation& l, const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
char* result = nullptr;
|
||||
#ifdef _WIN32
|
||||
int length = _vscprintf(fmt, ap);
|
||||
result = (char*)malloc(length);
|
||||
vsnprintf(result, length, fmt, ap);
|
||||
#else
|
||||
vasprintf(&result, fmt, ap);
|
||||
#endif
|
||||
va_end(ap);
|
||||
if (logvisor::XtermColor)
|
||||
LogModule.report(logvisor::Fatal, CYAN "[Compiler]" NORMAL " %s " YELLOW "@%d:%d " NORMAL "\n%s\n%s",
|
||||
m_name.c_str(), l.line, l.col, result, sourceDiagString(l, true).c_str());
|
||||
else
|
||||
LogModule.report(logvisor::Fatal, "[Compiler] %s @%d:%d\n%s\n%s",
|
||||
m_name.c_str(), l.line, l.col, result, sourceDiagString(l, false).c_str());
|
||||
free(result);
|
||||
}
|
||||
|
||||
void Diagnostics::reportBackendErr(const SourceLocation& l, const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
@ -132,4 +108,3 @@ void Diagnostics::reportBackendErr(const SourceLocation& l, const char* fmt, ...
|
|||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,294 @@
|
|||
#include "hecl/Frontend.hpp"
|
||||
|
||||
namespace hecl::Frontend
|
||||
{
|
||||
|
||||
int IR::Instruction::getChildCount() const
|
||||
{
|
||||
switch (m_op)
|
||||
{
|
||||
case OpType::Call:
|
||||
return m_call.m_argInstIdxs.size();
|
||||
case OpType::Arithmetic:
|
||||
return 2;
|
||||
case OpType::Swizzle:
|
||||
return 1;
|
||||
default:
|
||||
LogModule.report(logvisor::Fatal, "invalid op type");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
const IR::Instruction& IR::Instruction::getChildInst(const IR& ir, size_t idx) const
|
||||
{
|
||||
switch (m_op)
|
||||
{
|
||||
case OpType::Call:
|
||||
return ir.m_instructions.at(m_call.m_argInstIdxs.at(idx));
|
||||
case OpType::Arithmetic:
|
||||
if (idx > 1)
|
||||
LogModule.report(logvisor::Fatal, "arithmetic child idx must be 0 or 1");
|
||||
return ir.m_instructions.at(m_arithmetic.m_instIdxs[idx]);
|
||||
case OpType::Swizzle:
|
||||
if (idx > 0)
|
||||
LogModule.report(logvisor::Fatal, "swizzle child idx must be 0");
|
||||
return ir.m_instructions.at(m_swizzle.m_instIdx);
|
||||
default:
|
||||
LogModule.report(logvisor::Fatal, "invalid op type");
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
const atVec4f& IR::Instruction::getImmVec() const
|
||||
{
|
||||
if (m_op != OpType::LoadImm)
|
||||
LogModule.report(logvisor::Fatal, "invalid op type");
|
||||
return m_loadImm.m_immVec;
|
||||
}
|
||||
|
||||
void IR::Instruction::read(athena::io::IStreamReader& reader)
|
||||
{
|
||||
m_op = OpType(reader.readUByte());
|
||||
m_target = reader.readUint16Big();
|
||||
switch (m_op)
|
||||
{
|
||||
default: break;
|
||||
case OpType::Call:
|
||||
m_call.read(reader);
|
||||
break;
|
||||
case OpType::LoadImm:
|
||||
m_loadImm.read(reader);
|
||||
break;
|
||||
case OpType::Arithmetic:
|
||||
m_arithmetic.read(reader);
|
||||
break;
|
||||
case OpType::Swizzle:
|
||||
m_swizzle.read(reader);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void IR::Instruction::write(athena::io::IStreamWriter& writer) const
|
||||
{
|
||||
writer.writeUByte(m_op);
|
||||
writer.writeUint16Big(m_target);
|
||||
switch (m_op)
|
||||
{
|
||||
default: break;
|
||||
case OpType::Call:
|
||||
m_call.write(writer);
|
||||
break;
|
||||
case OpType::LoadImm:
|
||||
m_loadImm.write(writer);
|
||||
break;
|
||||
case OpType::Arithmetic:
|
||||
m_arithmetic.write(writer);
|
||||
break;
|
||||
case OpType::Swizzle:
|
||||
m_swizzle.write(writer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t IR::Instruction::binarySize(size_t sz) const
|
||||
{
|
||||
sz += 3;
|
||||
switch (m_op)
|
||||
{
|
||||
default: break;
|
||||
case OpType::Call:
|
||||
sz = m_call.binarySize(sz);
|
||||
break;
|
||||
case OpType::LoadImm:
|
||||
sz = m_loadImm.binarySize(sz);
|
||||
break;
|
||||
case OpType::Arithmetic:
|
||||
sz = m_arithmetic.binarySize(sz);
|
||||
break;
|
||||
case OpType::Swizzle:
|
||||
sz = m_swizzle.binarySize(sz);
|
||||
break;
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
atInt8 IR::swizzleCompIdx(char aChar)
|
||||
{
|
||||
switch (aChar)
|
||||
{
|
||||
case 'x':
|
||||
case 'r':
|
||||
return 0;
|
||||
case 'y':
|
||||
case 'g':
|
||||
return 1;
|
||||
case 'z':
|
||||
case 'b':
|
||||
return 2;
|
||||
case 'w':
|
||||
case 'a':
|
||||
return 3;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int IR::addInstruction(const IRNode& n, IR::RegID target)
|
||||
{
|
||||
if (n.kind == IRNode::Kind::None)
|
||||
return -1;
|
||||
switch (n.kind)
|
||||
{
|
||||
case IRNode::Kind::Call:
|
||||
{
|
||||
if (!n.str.compare("vec3") && n.children.size() >= 3)
|
||||
{
|
||||
atVec4f vec = {};
|
||||
auto it = n.children.cbegin();
|
||||
int i;
|
||||
for (i=0 ; i<3 ; ++i, ++it)
|
||||
{
|
||||
if (it->kind != IRNode::Kind::Imm)
|
||||
break;
|
||||
vec.vec[i] = it->val;
|
||||
}
|
||||
if (i == 3)
|
||||
{
|
||||
m_instructions.emplace_back(OpType::LoadImm, target, n.loc);
|
||||
Instruction::LoadImm& inst = m_instructions.back().m_loadImm;
|
||||
inst.m_immVec = vec;
|
||||
return m_instructions.size() - 1;
|
||||
}
|
||||
}
|
||||
else if (!n.str.compare("vec4") && n.children.size() >= 4)
|
||||
{
|
||||
atVec4f vec = {};
|
||||
auto it = n.children.cbegin();
|
||||
int i;
|
||||
for (i=0 ; i<4 ; ++i, ++it)
|
||||
{
|
||||
if (it->kind != IRNode::Kind::Imm)
|
||||
break;
|
||||
vec.vec[i] = it->val;
|
||||
}
|
||||
if (i == 4)
|
||||
{
|
||||
m_instructions.emplace_back(OpType::LoadImm, target, n.loc);
|
||||
Instruction::LoadImm& inst = m_instructions.back().m_loadImm;
|
||||
inst.m_immVec = vec;
|
||||
return m_instructions.size() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<atUint16> argInstIdxs;
|
||||
argInstIdxs.reserve(n.children.size());
|
||||
IR::RegID tgt = target;
|
||||
for (auto& c : n.children)
|
||||
argInstIdxs.push_back(addInstruction(c, tgt++));
|
||||
m_instructions.emplace_back(OpType::Call, target, n.loc);
|
||||
Instruction::Call& inst = m_instructions.back().m_call;
|
||||
inst.m_name = n.str;
|
||||
inst.m_argInstCount = atUint16(argInstIdxs.size());
|
||||
inst.m_argInstIdxs = argInstIdxs;
|
||||
return m_instructions.size() - 1;
|
||||
}
|
||||
case IRNode::Kind::Imm:
|
||||
{
|
||||
m_instructions.emplace_back(OpType::LoadImm, target, n.loc);
|
||||
Instruction::LoadImm& inst = m_instructions.back().m_loadImm;
|
||||
inst.m_immVec.vec[0] = n.val;
|
||||
inst.m_immVec.vec[1] = n.val;
|
||||
inst.m_immVec.vec[2] = n.val;
|
||||
inst.m_immVec.vec[3] = n.val;
|
||||
return m_instructions.size() - 1;
|
||||
}
|
||||
case IRNode::Kind::Binop:
|
||||
{
|
||||
atUint16 left = addInstruction(*n.left, target);
|
||||
atUint16 right = addInstruction(*n.right, target + 1);
|
||||
m_instructions.emplace_back(OpType::Arithmetic, target, n.loc);
|
||||
Instruction::Arithmetic& inst = m_instructions.back().m_arithmetic;
|
||||
inst.m_op = Instruction::ArithmeticOpType(int(n.op) + 1);
|
||||
inst.m_instIdxs[0] = left;
|
||||
inst.m_instIdxs[1] = right;
|
||||
return m_instructions.size() - 1;
|
||||
}
|
||||
case IRNode::Kind::Swizzle:
|
||||
{
|
||||
atUint16 left = addInstruction(*n.left, target);
|
||||
m_instructions.emplace_back(OpType::Swizzle, target, n.loc);
|
||||
Instruction::Swizzle& inst = m_instructions.back().m_swizzle;
|
||||
for (int i=0 ; i<n.str.size() && i<4 ; ++i)
|
||||
inst.m_idxs[i] = swizzleCompIdx(n.str[i]);
|
||||
inst.m_instIdx = left;
|
||||
return m_instructions.size() - 1;
|
||||
}
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void IR::read(athena::io::IStreamReader& reader)
|
||||
{
|
||||
m_hash = reader.readUint64Big();
|
||||
m_regCount = reader.readUint16Big();
|
||||
atUint16 instCount = reader.readUint16Big();
|
||||
m_instructions.clear();
|
||||
m_instructions.reserve(instCount);
|
||||
for (atUint16 i=0 ; i<instCount ; ++i)
|
||||
m_instructions.emplace_back(reader);
|
||||
|
||||
/* Pre-resolve blending mode */
|
||||
const IR::Instruction& rootCall = m_instructions.back();
|
||||
m_doAlpha = false;
|
||||
if (!rootCall.m_call.m_name.compare("HECLOpaque"))
|
||||
{
|
||||
m_blendSrc = boo::BlendFactor::One;
|
||||
m_blendDst = boo::BlendFactor::Zero;
|
||||
}
|
||||
else if (!rootCall.m_call.m_name.compare("HECLAlpha"))
|
||||
{
|
||||
m_blendSrc = boo::BlendFactor::SrcAlpha;
|
||||
m_blendDst = boo::BlendFactor::InvSrcAlpha;
|
||||
m_doAlpha = true;
|
||||
}
|
||||
else if (!rootCall.m_call.m_name.compare("HECLAdditive"))
|
||||
{
|
||||
m_blendSrc = boo::BlendFactor::SrcAlpha;
|
||||
m_blendDst = boo::BlendFactor::One;
|
||||
m_doAlpha = true;
|
||||
}
|
||||
}
|
||||
|
||||
void IR::write(athena::io::IStreamWriter& writer) const
|
||||
{
|
||||
writer.writeUint64Big(m_hash);
|
||||
writer.writeUint16Big(m_regCount);
|
||||
writer.writeUint16Big(m_instructions.size());
|
||||
for (const Instruction& inst : m_instructions)
|
||||
inst.write(writer);
|
||||
}
|
||||
|
||||
size_t IR::binarySize(size_t sz) const
|
||||
{
|
||||
sz += 12;
|
||||
for (const Instruction& inst : m_instructions)
|
||||
sz = inst.binarySize(sz);
|
||||
return sz;
|
||||
}
|
||||
|
||||
IR Frontend::compileSource(std::string_view source, std::string_view diagName)
|
||||
{
|
||||
Hash hash(source);
|
||||
m_diag.reset(diagName, source);
|
||||
m_parser.reset(source);
|
||||
auto insts = m_parser.parse();
|
||||
IR ir;
|
||||
ir.m_hash = hash.val64();
|
||||
for (auto& inst : insts)
|
||||
ir.addInstruction(inst, 0);
|
||||
return ir;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,722 +0,0 @@
|
|||
#include "hecl/hecl.hpp"
|
||||
#include "hecl/Frontend.hpp"
|
||||
|
||||
/* Combined lexer and semantic analysis system */
|
||||
|
||||
namespace hecl
|
||||
{
|
||||
namespace Frontend
|
||||
{
|
||||
|
||||
static IR::Instruction::ArithmeticOpType ArithType(int aChar)
|
||||
{
|
||||
switch (aChar)
|
||||
{
|
||||
case '+':
|
||||
return IR::Instruction::ArithmeticOpType::Add;
|
||||
case '-':
|
||||
return IR::Instruction::ArithmeticOpType::Subtract;
|
||||
case '*':
|
||||
return IR::Instruction::ArithmeticOpType::Multiply;
|
||||
case '/':
|
||||
return IR::Instruction::ArithmeticOpType::Divide;
|
||||
default:
|
||||
return IR::Instruction::ArithmeticOpType::None;
|
||||
}
|
||||
}
|
||||
|
||||
void Lexer::ReconnectArithmetic(OperationNode* sn, OperationNode** lastSub, OperationNode** newSub) const
|
||||
{
|
||||
sn->m_sub = sn->m_prev;
|
||||
sn->m_prev = nullptr;
|
||||
sn->m_sub->m_prev = nullptr;
|
||||
|
||||
sn->m_sub->m_next = sn->m_next;
|
||||
sn->m_next = sn->m_next->m_next;
|
||||
sn->m_sub->m_next->m_prev = sn->m_sub;
|
||||
sn->m_sub->m_next->m_next = nullptr;
|
||||
|
||||
if (*lastSub)
|
||||
{
|
||||
(*lastSub)->m_next = sn;
|
||||
sn->m_prev = *lastSub;
|
||||
}
|
||||
*lastSub = sn;
|
||||
|
||||
if (!*newSub)
|
||||
*newSub = sn;
|
||||
}
|
||||
|
||||
void Lexer::PrintChain(const Lexer::OperationNode* begin, const Lexer::OperationNode* end)
|
||||
{
|
||||
for (const Lexer::OperationNode* n = begin ; n != end ; n = n->m_next)
|
||||
{
|
||||
printf("%3d %s %s\n", n->m_tok.m_location.col, n->m_tok.typeString(),
|
||||
n->m_tok.m_tokenString.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Lexer::PrintTree(const Lexer::OperationNode* node, int indent)
|
||||
{
|
||||
for (const Lexer::OperationNode* n = node ; n ; n = n->m_next)
|
||||
{
|
||||
for (int i=0 ; i<indent ; ++i)
|
||||
printf(" ");
|
||||
printf("%3d %s %s %c %g\n", n->m_tok.m_location.col, n->m_tok.typeString(),
|
||||
n->m_tok.m_tokenString.c_str(), n->m_tok.m_tokenInt, n->m_tok.m_tokenFloat);
|
||||
if (n->m_sub)
|
||||
PrintTree(n->m_sub, indent + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void Lexer::reset()
|
||||
{
|
||||
m_root = nullptr;
|
||||
m_pool.clear();
|
||||
}
|
||||
|
||||
void Lexer::consumeAllTokens(Parser& parser)
|
||||
{
|
||||
reset();
|
||||
Parser::Token firstTok = parser.consumeToken();
|
||||
if (firstTok.m_type != Parser::TokenType::SourceBegin)
|
||||
{
|
||||
m_diag.reportLexerErr(firstTok.m_location, "expected start token");
|
||||
return;
|
||||
}
|
||||
|
||||
m_pool.emplace_front(std::move(firstTok));
|
||||
Lexer::OperationNode* firstNode = &m_pool.front();
|
||||
Lexer::OperationNode* lastNode = firstNode;
|
||||
|
||||
/* Build linked-list of nodes parsed in-order */
|
||||
{
|
||||
std::vector<SourceLocation> funcStack;
|
||||
std::vector<SourceLocation> groupStack;
|
||||
while (lastNode->m_tok.m_type != Parser::TokenType::SourceEnd)
|
||||
{
|
||||
Parser::Token tok = parser.consumeToken();
|
||||
switch (tok.m_type)
|
||||
{
|
||||
case Parser::TokenType::EvalGroupStart:
|
||||
groupStack.push_back(tok.m_location);
|
||||
break;
|
||||
case Parser::TokenType::EvalGroupEnd:
|
||||
if (groupStack.empty())
|
||||
{
|
||||
m_diag.reportLexerErr(tok.m_location, "unbalanced group detected");
|
||||
return;
|
||||
}
|
||||
groupStack.pop_back();
|
||||
break;
|
||||
case Parser::TokenType::FunctionStart:
|
||||
funcStack.push_back(tok.m_location);
|
||||
break;
|
||||
case Parser::TokenType::FunctionEnd:
|
||||
if (funcStack.empty())
|
||||
{
|
||||
m_diag.reportLexerErr(tok.m_location, "unbalanced function detected");
|
||||
return;
|
||||
}
|
||||
funcStack.pop_back();
|
||||
break;
|
||||
case Parser::TokenType::SourceEnd:
|
||||
case Parser::TokenType::NumLiteral:
|
||||
case Parser::TokenType::VectorSwizzle:
|
||||
case Parser::TokenType::FunctionArgDelim:
|
||||
case Parser::TokenType::ArithmeticOp:
|
||||
break;
|
||||
default:
|
||||
m_diag.reportLexerErr(tok.m_location, "invalid token");
|
||||
return;
|
||||
}
|
||||
m_pool.emplace_front(std::move(tok));
|
||||
lastNode->m_next = &m_pool.front();
|
||||
m_pool.front().m_prev = lastNode;
|
||||
lastNode = &m_pool.front();
|
||||
}
|
||||
|
||||
/* Ensure functions and groups are balanced */
|
||||
if (funcStack.size())
|
||||
{
|
||||
m_diag.reportLexerErr(funcStack.back(), "unclosed function detected");
|
||||
return;
|
||||
}
|
||||
if (groupStack.size())
|
||||
{
|
||||
m_diag.reportLexerErr(groupStack.back(), "unclosed group detected");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure first non-start node is a function */
|
||||
if (firstNode->m_next->m_tok.m_type != Parser::TokenType::FunctionStart)
|
||||
{
|
||||
m_diag.reportLexerErr(firstNode->m_tok.m_location, "expected root function");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Organize marked function args into implicit groups */
|
||||
for (Lexer::OperationNode* n = firstNode ; n != lastNode ; n = n->m_next)
|
||||
{
|
||||
if (n->m_tok.m_type == Parser::TokenType::FunctionStart)
|
||||
{
|
||||
if (n->m_next->m_tok.m_type != Parser::TokenType::FunctionEnd)
|
||||
{
|
||||
if (n->m_next->m_tok.m_type == Parser::TokenType::FunctionArgDelim)
|
||||
{
|
||||
m_diag.reportLexerErr(n->m_next->m_tok.m_location, "empty function arg");
|
||||
return;
|
||||
}
|
||||
m_pool.emplace_front(
|
||||
Parser::Token(Parser::TokenType::EvalGroupStart, n->m_next->m_tok.m_location));
|
||||
Lexer::OperationNode* grp = &m_pool.front();
|
||||
grp->m_next = n->m_next;
|
||||
grp->m_prev = n;
|
||||
n->m_next->m_prev = grp;
|
||||
n->m_next = grp;
|
||||
}
|
||||
}
|
||||
else if (n->m_tok.m_type == Parser::TokenType::FunctionEnd)
|
||||
{
|
||||
if (n->m_prev->m_tok.m_type != Parser::TokenType::FunctionStart)
|
||||
{
|
||||
m_pool.emplace_front(
|
||||
Parser::Token(Parser::TokenType::EvalGroupEnd, n->m_tok.m_location));
|
||||
Lexer::OperationNode* grp = &m_pool.front();
|
||||
grp->m_next = n;
|
||||
grp->m_prev = n->m_prev;
|
||||
n->m_prev->m_next = grp;
|
||||
n->m_prev = grp;
|
||||
}
|
||||
}
|
||||
else if (n->m_tok.m_type == Parser::TokenType::FunctionArgDelim)
|
||||
{
|
||||
if (n->m_next->m_tok.m_type == Parser::TokenType::FunctionArgDelim ||
|
||||
n->m_next->m_tok.m_type == Parser::TokenType::FunctionEnd)
|
||||
{
|
||||
m_diag.reportLexerErr(n->m_next->m_tok.m_location, "empty function arg");
|
||||
return;
|
||||
}
|
||||
|
||||
m_pool.emplace_front(
|
||||
Parser::Token(Parser::TokenType::EvalGroupEnd, n->m_tok.m_location));
|
||||
Lexer::OperationNode* egrp = &m_pool.front();
|
||||
|
||||
m_pool.emplace_front(
|
||||
Parser::Token(Parser::TokenType::EvalGroupStart, n->m_next->m_tok.m_location));
|
||||
Lexer::OperationNode* sgrp = &m_pool.front();
|
||||
|
||||
egrp->m_next = sgrp;
|
||||
sgrp->m_prev = egrp;
|
||||
|
||||
sgrp->m_next = n->m_next;
|
||||
egrp->m_prev = n->m_prev;
|
||||
n->m_next->m_prev = sgrp;
|
||||
n->m_prev->m_next = egrp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Organize marked groups into tree-hierarchy */
|
||||
{
|
||||
std::vector<Lexer::OperationNode*> groupStack;
|
||||
for (Lexer::OperationNode* n = firstNode ; n != lastNode ; n = n->m_next)
|
||||
{
|
||||
if (n->m_tok.m_type == Parser::TokenType::EvalGroupStart)
|
||||
groupStack.push_back(n);
|
||||
else if (n->m_tok.m_type == Parser::TokenType::EvalGroupEnd)
|
||||
{
|
||||
Lexer::OperationNode* start = groupStack.back();
|
||||
groupStack.pop_back();
|
||||
if (n->m_prev == start)
|
||||
{
|
||||
m_diag.reportLexerErr(start->m_tok.m_location, "empty group");
|
||||
return;
|
||||
}
|
||||
start->m_sub = start->m_next;
|
||||
start->m_next = n->m_next;
|
||||
if (n->m_next)
|
||||
n->m_next->m_prev = start;
|
||||
n->m_prev->m_next = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Organize functions into tree-hierarchy */
|
||||
for (Lexer::OperationNode& n : m_pool)
|
||||
{
|
||||
if (n.m_tok.m_type == Parser::TokenType::FunctionStart)
|
||||
{
|
||||
for (Lexer::OperationNode* sn = n.m_next ; sn ; sn = sn->m_next)
|
||||
{
|
||||
if (sn->m_tok.m_type == Parser::TokenType::FunctionEnd)
|
||||
{
|
||||
n.m_sub = n.m_next;
|
||||
if (n.m_next == sn)
|
||||
n.m_sub = nullptr;
|
||||
n.m_next = sn->m_next;
|
||||
if (sn->m_next)
|
||||
sn->m_next->m_prev = &n;
|
||||
if (n.m_sub)
|
||||
n.m_sub->m_prev = nullptr;
|
||||
if (sn->m_prev)
|
||||
sn->m_prev->m_next = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Organize vector swizzles into tree-hierarchy */
|
||||
for (Lexer::OperationNode& n : m_pool)
|
||||
{
|
||||
if (n.m_tok.m_type == Parser::TokenType::VectorSwizzle)
|
||||
{
|
||||
if (n.m_prev->m_tok.m_type != Parser::TokenType::FunctionStart)
|
||||
{
|
||||
m_diag.reportLexerErr(n.m_tok.m_location,
|
||||
"vector swizzles may only follow functions");
|
||||
return;
|
||||
}
|
||||
Lexer::OperationNode* func = n.m_prev;
|
||||
n.m_sub = func;
|
||||
n.m_prev = func->m_prev;
|
||||
if (func->m_prev)
|
||||
{
|
||||
if (func->m_prev->m_sub == func)
|
||||
func->m_prev->m_sub = &n;
|
||||
else
|
||||
func->m_prev->m_next = &n;
|
||||
}
|
||||
func->m_next = nullptr;
|
||||
func->m_prev = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure evaluation groups have proper arithmetic usage */
|
||||
for (Lexer::OperationNode& n : m_pool)
|
||||
{
|
||||
if (n.m_tok.m_type == Parser::TokenType::EvalGroupStart)
|
||||
{
|
||||
int idx = 0;
|
||||
for (Lexer::OperationNode* sn = n.m_sub ; sn ; sn = sn->m_next, ++idx)
|
||||
{
|
||||
if ((sn->m_tok.m_type == Parser::TokenType::ArithmeticOp && !(idx & 1)) ||
|
||||
(sn->m_tok.m_type != Parser::TokenType::ArithmeticOp && (idx & 1)))
|
||||
{
|
||||
m_diag.reportLexerErr(sn->m_tok.m_location, "improper arithmetic expression");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Organize arithmetic usage into tree-hierarchy */
|
||||
for (Lexer::OperationNode& n : m_pool)
|
||||
{
|
||||
if (n.m_tok.m_type == Parser::TokenType::EvalGroupStart)
|
||||
{
|
||||
Lexer::OperationNode* newSub = nullptr;
|
||||
Lexer::OperationNode* lastSub = nullptr;
|
||||
for (Lexer::OperationNode* sn = n.m_sub ; sn ; sn = sn->m_next)
|
||||
{
|
||||
if (sn->m_tok.m_type == Parser::TokenType::ArithmeticOp)
|
||||
{
|
||||
IR::Instruction::ArithmeticOpType op = ArithType(sn->m_tok.m_tokenInt);
|
||||
if (op == IR::Instruction::ArithmeticOpType::Multiply ||
|
||||
op == IR::Instruction::ArithmeticOpType::Divide)
|
||||
ReconnectArithmetic(sn, &lastSub, &newSub);
|
||||
}
|
||||
}
|
||||
for (Lexer::OperationNode* sn = n.m_sub ; sn ; sn = sn->m_next)
|
||||
{
|
||||
if (sn->m_tok.m_type == Parser::TokenType::ArithmeticOp)
|
||||
{
|
||||
IR::Instruction::ArithmeticOpType op = ArithType(sn->m_tok.m_tokenInt);
|
||||
if (op == IR::Instruction::ArithmeticOpType::Add ||
|
||||
op == IR::Instruction::ArithmeticOpType::Subtract)
|
||||
ReconnectArithmetic(sn, &lastSub, &newSub);
|
||||
}
|
||||
}
|
||||
if (newSub)
|
||||
n.m_sub = newSub;
|
||||
}
|
||||
}
|
||||
|
||||
if (hecl::VerbosityLevel > 1)
|
||||
{
|
||||
printf("%s\n", m_diag.getSource().data());
|
||||
PrintTree(firstNode);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/* Done! */
|
||||
m_root = firstNode->m_next;
|
||||
}
|
||||
|
||||
void Lexer::EmitVec3(IR& ir, const Lexer::OperationNode* funcNode, IR::RegID target) const
|
||||
{
|
||||
/* Optimization case: if empty call, emit zero imm load */
|
||||
const Lexer::OperationNode* gn = funcNode->m_sub;
|
||||
if (!gn)
|
||||
{
|
||||
ir.m_instructions.emplace_back(IR::OpType::LoadImm, funcNode->m_tok.m_location);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Optimization case: if all numeric literals, emit vector imm load */
|
||||
bool opt = true;
|
||||
const Parser::Token* imms[3];
|
||||
for (int i=0 ; i<3 ; ++i)
|
||||
{
|
||||
if (!gn->m_sub || gn->m_sub->m_tok.m_type != Parser::TokenType::NumLiteral)
|
||||
{
|
||||
opt = false;
|
||||
break;
|
||||
}
|
||||
imms[i] = &gn->m_sub->m_tok;
|
||||
gn = gn->m_next;
|
||||
}
|
||||
if (opt)
|
||||
{
|
||||
ir.m_instructions.emplace_back(IR::OpType::LoadImm, funcNode->m_tok.m_location);
|
||||
atVec4f& vec = ir.m_instructions.back().m_loadImm.m_immVec;
|
||||
vec.vec[0] = imms[0]->m_tokenFloat;
|
||||
vec.vec[1] = imms[1]->m_tokenFloat;
|
||||
vec.vec[2] = imms[2]->m_tokenFloat;
|
||||
vec.vec[3] = 1.0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Otherwise treat as normal function */
|
||||
RecursiveFuncCompile(ir, funcNode, target);
|
||||
}
|
||||
|
||||
void Lexer::EmitVec4(IR& ir, const Lexer::OperationNode* funcNode, IR::RegID target) const
|
||||
{
|
||||
/* Optimization case: if empty call, emit zero imm load */
|
||||
const Lexer::OperationNode* gn = funcNode->m_sub;
|
||||
if (!gn)
|
||||
{
|
||||
ir.m_instructions.emplace_back(IR::OpType::LoadImm, funcNode->m_tok.m_location);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Optimization case: if all numeric literals, emit vector imm load */
|
||||
bool opt = true;
|
||||
const Parser::Token* imms[4];
|
||||
for (int i=0 ; i<4 ; ++i)
|
||||
{
|
||||
if (!gn || !gn->m_sub || gn->m_sub->m_tok.m_type != Parser::TokenType::NumLiteral)
|
||||
{
|
||||
opt = false;
|
||||
break;
|
||||
}
|
||||
imms[i] = &gn->m_sub->m_tok;
|
||||
gn = gn->m_next;
|
||||
}
|
||||
if (opt)
|
||||
{
|
||||
ir.m_instructions.emplace_back(IR::OpType::LoadImm, funcNode->m_tok.m_location);
|
||||
atVec4f& vec = ir.m_instructions.back().m_loadImm.m_immVec;
|
||||
vec.vec[0] = imms[0]->m_tokenFloat;
|
||||
vec.vec[1] = imms[1]->m_tokenFloat;
|
||||
vec.vec[2] = imms[2]->m_tokenFloat;
|
||||
vec.vec[3] = imms[3]->m_tokenFloat;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Otherwise treat as normal function */
|
||||
RecursiveFuncCompile(ir, funcNode, target);
|
||||
}
|
||||
|
||||
void Lexer::EmitArithmetic(IR& ir, const Lexer::OperationNode* arithNode, IR::RegID target) const
|
||||
{
|
||||
/* Evaluate operands */
|
||||
atVec4f* opt[2] = {nullptr};
|
||||
size_t instCount = ir.m_instructions.size();
|
||||
const Lexer::OperationNode* on = arithNode->m_sub;
|
||||
IR::RegID tgt = target;
|
||||
size_t argInsts[2];
|
||||
for (int i=0 ; i<2 ; ++i, ++tgt)
|
||||
{
|
||||
const Parser::Token& tok = on->m_tok;
|
||||
switch (tok.m_type)
|
||||
{
|
||||
case Parser::TokenType::FunctionStart:
|
||||
if (!tok.m_tokenString.compare("vec3"))
|
||||
EmitVec3(ir, on, tgt);
|
||||
else if (!tok.m_tokenString.compare("vec4"))
|
||||
EmitVec4(ir, on, tgt);
|
||||
else
|
||||
RecursiveFuncCompile(ir, on, tgt);
|
||||
break;
|
||||
case Parser::TokenType::EvalGroupStart:
|
||||
RecursiveGroupCompile(ir, on, tgt);
|
||||
break;
|
||||
case Parser::TokenType::NumLiteral:
|
||||
{
|
||||
ir.m_instructions.emplace_back(IR::OpType::LoadImm, arithNode->m_tok.m_location);
|
||||
IR::Instruction& inst = ir.m_instructions.back();
|
||||
inst.m_target = tgt;
|
||||
inst.m_loadImm.m_immVec.vec[0] = tok.m_tokenFloat;
|
||||
inst.m_loadImm.m_immVec.vec[1] = tok.m_tokenFloat;
|
||||
inst.m_loadImm.m_immVec.vec[2] = tok.m_tokenFloat;
|
||||
inst.m_loadImm.m_immVec.vec[3] = tok.m_tokenFloat;
|
||||
break;
|
||||
}
|
||||
case Parser::TokenType::VectorSwizzle:
|
||||
EmitVectorSwizzle(ir, on, tgt);
|
||||
break;
|
||||
default:
|
||||
m_diag.reportCompileErr(tok.m_location, "invalid lexer node for IR");
|
||||
break;
|
||||
};
|
||||
argInsts[i] = ir.m_instructions.size() - 1;
|
||||
if (ir.m_instructions.back().m_op == IR::OpType::LoadImm)
|
||||
opt[i] = &ir.m_instructions.back().m_loadImm.m_immVec;
|
||||
on = on->m_next;
|
||||
}
|
||||
|
||||
/* Optimization case: if both operands imm load, pre-evalulate */
|
||||
if (opt[0] && opt[1] && (ir.m_instructions.size() - instCount == 2))
|
||||
{
|
||||
atVec4f eval;
|
||||
switch (ArithType(arithNode->m_tok.m_tokenInt))
|
||||
{
|
||||
case IR::Instruction::ArithmeticOpType::Add:
|
||||
eval.vec[0] = opt[0]->vec[0] + opt[1]->vec[0];
|
||||
eval.vec[1] = opt[0]->vec[1] + opt[1]->vec[1];
|
||||
eval.vec[2] = opt[0]->vec[2] + opt[1]->vec[2];
|
||||
eval.vec[3] = opt[0]->vec[3] + opt[1]->vec[3];
|
||||
break;
|
||||
case IR::Instruction::ArithmeticOpType::Subtract:
|
||||
eval.vec[0] = opt[0]->vec[0] - opt[1]->vec[0];
|
||||
eval.vec[1] = opt[0]->vec[1] - opt[1]->vec[1];
|
||||
eval.vec[2] = opt[0]->vec[2] - opt[1]->vec[2];
|
||||
eval.vec[3] = opt[0]->vec[3] - opt[1]->vec[3];
|
||||
break;
|
||||
case IR::Instruction::ArithmeticOpType::Multiply:
|
||||
eval.vec[0] = opt[0]->vec[0] * opt[1]->vec[0];
|
||||
eval.vec[1] = opt[0]->vec[1] * opt[1]->vec[1];
|
||||
eval.vec[2] = opt[0]->vec[2] * opt[1]->vec[2];
|
||||
eval.vec[3] = opt[0]->vec[3] * opt[1]->vec[3];
|
||||
break;
|
||||
case IR::Instruction::ArithmeticOpType::Divide:
|
||||
eval.vec[0] = opt[0]->vec[0] / opt[1]->vec[0];
|
||||
eval.vec[1] = opt[0]->vec[1] / opt[1]->vec[1];
|
||||
eval.vec[2] = opt[0]->vec[2] / opt[1]->vec[2];
|
||||
eval.vec[3] = opt[0]->vec[3] / opt[1]->vec[3];
|
||||
break;
|
||||
default:
|
||||
m_diag.reportCompileErr(arithNode->m_tok.m_location, "invalid arithmetic type");
|
||||
break;
|
||||
}
|
||||
ir.m_instructions.pop_back();
|
||||
ir.m_instructions.pop_back();
|
||||
ir.m_instructions.emplace_back(IR::OpType::LoadImm, arithNode->m_tok.m_location);
|
||||
IR::Instruction& inst = ir.m_instructions.back();
|
||||
inst.m_target = target;
|
||||
inst.m_loadImm.m_immVec = eval;
|
||||
}
|
||||
else
|
||||
{
|
||||
ir.m_instructions.emplace_back(IR::OpType::Arithmetic, arithNode->m_tok.m_location);
|
||||
IR::Instruction& inst = ir.m_instructions.back();
|
||||
inst.m_target = target;
|
||||
inst.m_arithmetic.m_instIdxs[0] = argInsts[0];
|
||||
inst.m_arithmetic.m_instIdxs[1] = argInsts[1];
|
||||
inst.m_arithmetic.m_op = ArithType(arithNode->m_tok.m_tokenInt);
|
||||
if (tgt > ir.m_regCount)
|
||||
ir.m_regCount = tgt;
|
||||
}
|
||||
}
|
||||
|
||||
static int SwizzleCompIdx(char aChar, Diagnostics& diag, const SourceLocation& loc)
|
||||
{
|
||||
switch (aChar)
|
||||
{
|
||||
case 'x':
|
||||
case 'r':
|
||||
return 0;
|
||||
case 'y':
|
||||
case 'g':
|
||||
return 1;
|
||||
case 'z':
|
||||
case 'b':
|
||||
return 2;
|
||||
case 'w':
|
||||
case 'a':
|
||||
return 3;
|
||||
default:
|
||||
diag.reportCompileErr(loc, "invalid swizzle char %c", aChar);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Lexer::EmitVectorSwizzle(IR& ir, const Lexer::OperationNode* swizNode, IR::RegID target) const
|
||||
{
|
||||
const std::string& str = swizNode->m_tok.m_tokenString;
|
||||
if (str.size() != 1 && str.size() != 3 && str.size() != 4)
|
||||
m_diag.reportCompileErr(swizNode->m_tok.m_location, "%d component swizzles not supported", int(str.size()));
|
||||
|
||||
size_t instCount = ir.m_instructions.size();
|
||||
const Lexer::OperationNode* on = swizNode->m_sub;
|
||||
const Parser::Token& tok = on->m_tok;
|
||||
switch (tok.m_type)
|
||||
{
|
||||
case Parser::TokenType::FunctionStart:
|
||||
if (!tok.m_tokenString.compare("vec3"))
|
||||
EmitVec3(ir, on, target);
|
||||
else if (!tok.m_tokenString.compare("vec4"))
|
||||
EmitVec4(ir, on, target);
|
||||
else
|
||||
RecursiveFuncCompile(ir, on, target);
|
||||
break;
|
||||
case Parser::TokenType::EvalGroupStart:
|
||||
RecursiveGroupCompile(ir, on, target);
|
||||
break;
|
||||
case Parser::TokenType::NumLiteral:
|
||||
{
|
||||
ir.m_instructions.emplace_back(IR::OpType::LoadImm, swizNode->m_tok.m_location);
|
||||
IR::Instruction& inst = ir.m_instructions.back();
|
||||
inst.m_target = target;
|
||||
inst.m_loadImm.m_immVec.vec[0] = tok.m_tokenFloat;
|
||||
inst.m_loadImm.m_immVec.vec[1] = tok.m_tokenFloat;
|
||||
inst.m_loadImm.m_immVec.vec[2] = tok.m_tokenFloat;
|
||||
inst.m_loadImm.m_immVec.vec[3] = tok.m_tokenFloat;
|
||||
break;
|
||||
}
|
||||
case Parser::TokenType::VectorSwizzle:
|
||||
EmitVectorSwizzle(ir, on, target);
|
||||
break;
|
||||
default:
|
||||
m_diag.reportCompileErr(tok.m_location, "invalid lexer node for IR");
|
||||
break;
|
||||
};
|
||||
|
||||
/* Optimization case: if operand imm load, pre-evalulate */
|
||||
if (ir.m_instructions.back().m_op == IR::OpType::LoadImm && (ir.m_instructions.size() - instCount == 1))
|
||||
{
|
||||
atVec4f* opt = &ir.m_instructions.back().m_loadImm.m_immVec;
|
||||
const SourceLocation& loc = ir.m_instructions.back().m_loc;
|
||||
atVec4f eval = {};
|
||||
switch (str.size())
|
||||
{
|
||||
case 1:
|
||||
eval.vec[0] = opt->vec[SwizzleCompIdx(str[0], m_diag, loc)];
|
||||
eval.vec[1] = eval.vec[0];
|
||||
eval.vec[2] = eval.vec[0];
|
||||
eval.vec[3] = eval.vec[0];
|
||||
break;
|
||||
case 3:
|
||||
eval.vec[0] = opt->vec[SwizzleCompIdx(str[0], m_diag, loc)];
|
||||
eval.vec[1] = opt->vec[SwizzleCompIdx(str[1], m_diag, loc)];
|
||||
eval.vec[2] = opt->vec[SwizzleCompIdx(str[2], m_diag, loc)];
|
||||
eval.vec[3] = 1.0;
|
||||
break;
|
||||
case 4:
|
||||
eval.vec[0] = opt->vec[SwizzleCompIdx(str[0], m_diag, loc)];
|
||||
eval.vec[1] = opt->vec[SwizzleCompIdx(str[1], m_diag, loc)];
|
||||
eval.vec[2] = opt->vec[SwizzleCompIdx(str[2], m_diag, loc)];
|
||||
eval.vec[3] = opt->vec[SwizzleCompIdx(str[3], m_diag, loc)];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ir.m_instructions.pop_back();
|
||||
ir.m_instructions.emplace_back(IR::OpType::LoadImm, swizNode->m_tok.m_location);
|
||||
IR::Instruction& inst = ir.m_instructions.back();
|
||||
inst.m_target = target;
|
||||
inst.m_loadImm.m_immVec = eval;
|
||||
}
|
||||
else
|
||||
{
|
||||
ir.m_instructions.emplace_back(IR::OpType::Swizzle, swizNode->m_tok.m_location);
|
||||
IR::Instruction& inst = ir.m_instructions.back();
|
||||
inst.m_swizzle.m_instIdx = ir.m_instructions.size() - 2;
|
||||
inst.m_target = target;
|
||||
for (int i=0 ; i<str.size() ; ++i)
|
||||
inst.m_swizzle.m_idxs[i] = SwizzleCompIdx(str[i], m_diag, swizNode->m_tok.m_location);
|
||||
}
|
||||
}
|
||||
|
||||
void Lexer::RecursiveGroupCompile(IR& ir, const Lexer::OperationNode* groupNode, IR::RegID target) const
|
||||
{
|
||||
IR::RegID tgt = target;
|
||||
for (const Lexer::OperationNode* sn = groupNode->m_sub ; sn ; sn = sn->m_next, ++tgt)
|
||||
{
|
||||
const Parser::Token& tok = sn->m_tok;
|
||||
switch (tok.m_type)
|
||||
{
|
||||
case Parser::TokenType::FunctionStart:
|
||||
if (!tok.m_tokenString.compare("vec3"))
|
||||
EmitVec3(ir, sn, tgt);
|
||||
else if (!tok.m_tokenString.compare("vec4"))
|
||||
EmitVec4(ir, sn, tgt);
|
||||
else
|
||||
RecursiveFuncCompile(ir, sn, tgt);
|
||||
break;
|
||||
case Parser::TokenType::EvalGroupStart:
|
||||
RecursiveGroupCompile(ir, sn, tgt);
|
||||
break;
|
||||
case Parser::TokenType::NumLiteral:
|
||||
{
|
||||
ir.m_instructions.emplace_back(IR::OpType::LoadImm, tok.m_location);
|
||||
IR::Instruction& inst = ir.m_instructions.back();
|
||||
inst.m_target = tgt;
|
||||
inst.m_loadImm.m_immVec.vec[0] = tok.m_tokenFloat;
|
||||
inst.m_loadImm.m_immVec.vec[1] = tok.m_tokenFloat;
|
||||
inst.m_loadImm.m_immVec.vec[2] = tok.m_tokenFloat;
|
||||
inst.m_loadImm.m_immVec.vec[3] = tok.m_tokenFloat;
|
||||
break;
|
||||
}
|
||||
case Parser::TokenType::ArithmeticOp:
|
||||
EmitArithmetic(ir, sn, tgt);
|
||||
break;
|
||||
case Parser::TokenType::VectorSwizzle:
|
||||
EmitVectorSwizzle(ir, sn, tgt);
|
||||
break;
|
||||
default:
|
||||
m_diag.reportCompileErr(tok.m_location, "invalid lexer node for IR");
|
||||
break;
|
||||
};
|
||||
}
|
||||
if (tgt > ir.m_regCount)
|
||||
ir.m_regCount = tgt;
|
||||
}
|
||||
|
||||
void Lexer::RecursiveFuncCompile(IR& ir, const Lexer::OperationNode* funcNode, IR::RegID target) const
|
||||
{
|
||||
IR::RegID tgt = target;
|
||||
std::vector<atUint16> instIdxs;
|
||||
for (const Lexer::OperationNode* gn = funcNode->m_sub ; gn ; gn = gn->m_next, ++tgt)
|
||||
{
|
||||
RecursiveGroupCompile(ir, gn, tgt);
|
||||
instIdxs.push_back(ir.m_instructions.size() - 1);
|
||||
}
|
||||
ir.m_instructions.emplace_back(IR::OpType::Call, funcNode->m_tok.m_location);
|
||||
IR::Instruction& inst = ir.m_instructions.back();
|
||||
inst.m_call.m_name = funcNode->m_tok.m_tokenString;
|
||||
inst.m_call.m_argInstCount = instIdxs.size();
|
||||
inst.m_call.m_argInstIdxs = std::move(instIdxs);
|
||||
inst.m_target = target;
|
||||
if (tgt > ir.m_regCount)
|
||||
ir.m_regCount = tgt;
|
||||
}
|
||||
|
||||
IR Lexer::compileIR(atUint64 hash) const
|
||||
{
|
||||
if (!m_root)
|
||||
m_diag.reportCompileErr(SourceLocation(), "unable to compile HECL-IR for invalid source");
|
||||
|
||||
IR ir;
|
||||
ir.m_hash = hash;
|
||||
RecursiveFuncCompile(ir, m_root, 0);
|
||||
return ir;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -3,191 +3,277 @@
|
|||
|
||||
/* Syntatical token parsing system */
|
||||
|
||||
namespace hecl
|
||||
{
|
||||
namespace Frontend
|
||||
namespace hecl::Frontend
|
||||
{
|
||||
|
||||
void Parser::skipWhitespace(std::string_view::const_iterator& it)
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
while (it != m_source.cend())
|
||||
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) const
|
||||
{
|
||||
std::string buf;
|
||||
auto indent = rep(level, "\t"sv);
|
||||
switch (kind)
|
||||
{
|
||||
while (it != m_source.cend() && isspace(*it))
|
||||
++it;
|
||||
|
||||
/* Skip comment line */
|
||||
if (it != m_source.cend() && *it == '#')
|
||||
case Kind::Call:
|
||||
buf.append(indent);
|
||||
buf.append("Call "sv).append(str);
|
||||
if (!children.empty())
|
||||
{
|
||||
while (it != m_source.cend() && *it != '\n')
|
||||
++it;
|
||||
if (it != m_source.cend() && *it == '\n')
|
||||
++it;
|
||||
continue;
|
||||
buf.append(" {\n"sv);
|
||||
for (const IRNode& n : children)
|
||||
{
|
||||
buf.append(n.fmt(level + 1));
|
||||
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)).append("\n"sv);
|
||||
buf.append(right->fmt(level + 1)).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)).append("\n"sv);
|
||||
buf.append(indent).append("}"sv);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
void Parser::reset(std::string_view source)
|
||||
|
||||
std::string IRNode::describe() const
|
||||
{
|
||||
m_source = source;
|
||||
m_sourceIt = m_source.cbegin();
|
||||
m_parenStack.clear();
|
||||
m_reset = true;
|
||||
std::vector<std::string> 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;
|
||||
}
|
||||
|
||||
Parser::Token Parser::consumeToken()
|
||||
void Parser::check(Token::Kind expected)
|
||||
{
|
||||
if (m_source.empty())
|
||||
return Parser::Token(TokenType::None, SourceLocation());
|
||||
|
||||
/* If parser has just been reset, emit begin token */
|
||||
if (m_reset)
|
||||
{
|
||||
m_reset = false;
|
||||
return Parser::Token(TokenType::SourceBegin, getLocation());
|
||||
}
|
||||
|
||||
/* Skip whitespace */
|
||||
skipWhitespace(m_sourceIt);
|
||||
|
||||
/* Check for source end */
|
||||
if (m_sourceIt == m_source.cend())
|
||||
return Parser::Token(TokenType::SourceEnd, getLocation());
|
||||
|
||||
/* Check for numeric literal */
|
||||
{
|
||||
char* strEnd;
|
||||
float val = strtof(&*m_sourceIt, &strEnd);
|
||||
if (&*m_sourceIt != strEnd)
|
||||
{
|
||||
Parser::Token tok(TokenType::NumLiteral, getLocation());
|
||||
tok.m_tokenFloat = val;
|
||||
m_sourceIt += (strEnd - &*m_sourceIt);
|
||||
return tok;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for swizzle op */
|
||||
if (*m_sourceIt == '.')
|
||||
{
|
||||
int count = 0;
|
||||
std::string_view::const_iterator tmp = m_sourceIt + 1;
|
||||
if (tmp != m_source.cend())
|
||||
{
|
||||
for (int i=0 ; i<4 ; ++i)
|
||||
{
|
||||
std::string_view::const_iterator tmp2 = tmp + i;
|
||||
if (tmp2 == m_source.cend())
|
||||
break;
|
||||
char ch = tolower(*tmp2);
|
||||
if (ch >= 'w' && ch <= 'z')
|
||||
++count;
|
||||
else if (ch == 'r' || ch == 'g' || ch == 'b' || ch == 'a')
|
||||
++count;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (count)
|
||||
{
|
||||
Parser::Token tok(TokenType::VectorSwizzle, getLocation());
|
||||
for (int i=0 ; i<count ; ++i)
|
||||
{
|
||||
std::string_view::const_iterator tmp2 = tmp + i;
|
||||
tok.m_tokenString += tolower(*tmp2);
|
||||
}
|
||||
m_sourceIt = tmp + count;
|
||||
return tok;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for arithmetic op */
|
||||
if (*m_sourceIt == '+' || *m_sourceIt == '-' || *m_sourceIt == '*' || *m_sourceIt == '/')
|
||||
{
|
||||
Parser::Token tok(TokenType::ArithmeticOp, getLocation());
|
||||
tok.m_tokenInt = *m_sourceIt;
|
||||
++m_sourceIt;
|
||||
return tok;
|
||||
}
|
||||
|
||||
/* Check for parenthesis end (group or function call) */
|
||||
if (*m_sourceIt == ')')
|
||||
{
|
||||
if (m_parenStack.empty())
|
||||
{
|
||||
m_diag.reportParserErr(getLocation(), "unexpected ')' while parsing");
|
||||
return Parser::Token(TokenType::None, SourceLocation());
|
||||
}
|
||||
Parser::Token tok(m_parenStack.back(), getLocation());
|
||||
++m_sourceIt;
|
||||
m_parenStack.pop_back();
|
||||
return tok;
|
||||
}
|
||||
|
||||
/* Check for group start */
|
||||
if (*m_sourceIt == '(')
|
||||
{
|
||||
m_parenStack.push_back(TokenType::EvalGroupEnd);
|
||||
Parser::Token tok(TokenType::EvalGroupStart, getLocation());
|
||||
++m_sourceIt;
|
||||
return tok;
|
||||
}
|
||||
|
||||
/* Check for function start */
|
||||
if (isalpha(*m_sourceIt) || *m_sourceIt == '_')
|
||||
{
|
||||
std::string_view::const_iterator tmp = m_sourceIt + 1;
|
||||
while (tmp != m_source.cend() && (isalnum(*tmp) || *tmp == '_') && *tmp != '(')
|
||||
++tmp;
|
||||
std::string_view::const_iterator nameEnd = tmp;
|
||||
skipWhitespace(tmp);
|
||||
if (*tmp == '(')
|
||||
{
|
||||
Parser::Token tok(TokenType::FunctionStart, getLocation());
|
||||
tok.m_tokenString.assign(m_sourceIt, nameEnd);
|
||||
m_sourceIt = tmp + 1;
|
||||
m_parenStack.push_back(TokenType::FunctionEnd);
|
||||
return tok;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for function arg delimitation */
|
||||
if (*m_sourceIt == ',')
|
||||
{
|
||||
if (m_parenStack.empty() || m_parenStack.back() != TokenType::FunctionEnd)
|
||||
{
|
||||
m_diag.reportParserErr(getLocation(), "unexpected ',' while parsing");
|
||||
return Parser::Token(TokenType::None, SourceLocation());
|
||||
}
|
||||
Parser::Token tok(TokenType::FunctionArgDelim, getLocation());
|
||||
++m_sourceIt;
|
||||
return tok;
|
||||
}
|
||||
|
||||
/* Error condition if reached */
|
||||
m_diag.reportParserErr(getLocation(), "unexpected token while parsing");
|
||||
return Parser::Token(TokenType::None, SourceLocation());
|
||||
if (sym == expected)
|
||||
scan();
|
||||
else
|
||||
error("expected %s, was %s", Token::KindToStr(expected).data(), Token::KindToStr(sym).data());
|
||||
}
|
||||
|
||||
SourceLocation Parser::getLocation() const
|
||||
IRNode Parser::call()
|
||||
{
|
||||
if (m_source.empty())
|
||||
return SourceLocation();
|
||||
std::string_view::const_iterator it = m_source.cbegin();
|
||||
int line = 0;
|
||||
int col = 0;
|
||||
for (; it != m_sourceIt ; ++it)
|
||||
check(Token::Kind::Ident);
|
||||
std::string name = t.str;
|
||||
|
||||
std::list<IRNode> args;
|
||||
check(Token::Kind::Lpar);
|
||||
if (sym == Token::Kind::Lpar || sym == Token::Kind::Ident ||
|
||||
sym == Token::Kind::Number || sym == Token::Kind::Minus)
|
||||
{
|
||||
++col;
|
||||
if (*it == '\n')
|
||||
args.push_back(expr());
|
||||
|
||||
while (sym == Token::Kind::Comma)
|
||||
{
|
||||
++line;
|
||||
col = 0;
|
||||
scan();
|
||||
args.push_back(expr());
|
||||
}
|
||||
}
|
||||
return {line+1, col+1};
|
||||
|
||||
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<IRNode> Parser::parse()
|
||||
{
|
||||
std::list<IRNode> 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
#include "hecl/Frontend.hpp"
|
||||
|
||||
namespace hecl::Frontend
|
||||
{
|
||||
|
||||
int Scanner::_read()
|
||||
{
|
||||
if (m_sourceIt == m_source.end())
|
||||
return -1;
|
||||
return *m_sourceIt++;
|
||||
}
|
||||
|
||||
bool Scanner::read()
|
||||
{
|
||||
if (ch == EOF)
|
||||
return false;
|
||||
if (ch == LF)
|
||||
{
|
||||
lastLine = std::move(currentLine);
|
||||
currentLine = std::string();
|
||||
}
|
||||
int c = _read();
|
||||
ch = char(c);
|
||||
if (ch == LF)
|
||||
{
|
||||
loc.line++;
|
||||
lfcol = loc.col;
|
||||
loc.col = 0;
|
||||
}
|
||||
else if (c != EOF)
|
||||
{
|
||||
currentLine += ch;
|
||||
loc.col++;
|
||||
}
|
||||
return c != EOF;
|
||||
}
|
||||
|
||||
Token Scanner::next()
|
||||
{
|
||||
if (ch == EOF)
|
||||
return Token(Token::Kind::Eof, loc);
|
||||
|
||||
char c = ch;
|
||||
int tline = loc.line;
|
||||
int tcol = loc.col;
|
||||
int tlfcol = lfcol;
|
||||
read();
|
||||
|
||||
// skip comments and newlines
|
||||
while (c != EOF && (c == COMMENT || isspace(c)))
|
||||
{
|
||||
if (c == COMMENT)
|
||||
{
|
||||
while (c != LF && c != EOF)
|
||||
{
|
||||
tline = loc.line;
|
||||
tcol = loc.col;
|
||||
tlfcol = lfcol;
|
||||
c = ch;
|
||||
read();
|
||||
}
|
||||
}
|
||||
while (c != EOF && isspace(c))
|
||||
{
|
||||
if (c == LF)
|
||||
return Token(Token::Kind::Lf, {tline - 1, tlfcol + 1});
|
||||
tline = loc.line;
|
||||
tcol = loc.col;
|
||||
tlfcol = lfcol;
|
||||
c = ch;
|
||||
read();
|
||||
}
|
||||
}
|
||||
|
||||
Token::Kind kind = CharToTokenKind(c);
|
||||
if (kind != Token::Kind::None)
|
||||
return Token(kind, {tline, tcol});
|
||||
|
||||
if (ch == EOF)
|
||||
return Token(Token::Kind::Eof, {tline, tcol});
|
||||
|
||||
// ident or number
|
||||
if (isDigit(c))
|
||||
{
|
||||
std::string buf;
|
||||
buf += c;
|
||||
while (isDigit(ch))
|
||||
{
|
||||
buf += ch;
|
||||
read();
|
||||
}
|
||||
if (ch == '.')
|
||||
{ // float
|
||||
buf += ch;
|
||||
read();
|
||||
while (isDigit(ch))
|
||||
{
|
||||
buf += ch;
|
||||
read();
|
||||
}
|
||||
}
|
||||
return Token(Token::Kind::Number, std::move(buf), {tline, tcol});
|
||||
}
|
||||
|
||||
if (isStartIdent(c))
|
||||
{
|
||||
std::string buf;
|
||||
buf += c;
|
||||
while (isMidIdent(ch))
|
||||
{
|
||||
buf += ch;
|
||||
read();
|
||||
}
|
||||
return Token(Token::Kind::Ident, std::move(buf), {tline, tcol});
|
||||
}
|
||||
|
||||
error({tline, tcol}, "unexpected character '%c' (X'%02X')", chr(c), int(c));
|
||||
|
||||
return Token(Token::Kind::None, {tline, tcol});
|
||||
}
|
||||
|
||||
}
|
|
@ -544,9 +544,12 @@ ShaderCacheManager::buildExtendedShader(const ShaderTag& tag, const hecl::Fronte
|
|||
if (search != m_pipelineLookup.cend())
|
||||
return search->second;
|
||||
|
||||
if (tag.valSizeT() == 3543211830293577851)
|
||||
printf("");
|
||||
|
||||
std::shared_ptr<ShaderPipelines> ret = std::make_shared<ShaderPipelines>();
|
||||
ShaderCachedData foundData = lookupData(tag);
|
||||
if (foundData)
|
||||
if (false && foundData)
|
||||
{
|
||||
factory.commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
|
|
|
@ -42,6 +42,8 @@ struct HECLApplicationCallback : boo::IApplicationCallback
|
|||
|
||||
int appMain(boo::IApplication* app)
|
||||
{
|
||||
hecl::VerbosityLevel = 2;
|
||||
|
||||
/* Setup boo window */
|
||||
m_mainWindow = app->newWindow(_S("HECL Test"), 1);
|
||||
m_mainWindow->setCallback(&m_windowCb);
|
||||
|
@ -88,7 +90,7 @@ struct HECLApplicationCallback : boo::IApplicationCallback
|
|||
|
||||
/* Compile HECL shader */
|
||||
static std::string testShader = "HECLOpaque(Texture(0, UV(0)))";
|
||||
//static std::string testShader = "HECLOpaque(vec4(1.0,1.0,1.0,1.0))";
|
||||
//static std::string testShader = "HECLOpaque(vec3(1.0,1.0,1.0),1.0)";
|
||||
hecl::Runtime::ShaderTag testShaderTag(testShader, 0, 1, 0, 0, 0, boo::Primitive::TriStrips,
|
||||
hecl::Backend::ReflectionType::None, false, false, false);
|
||||
std::shared_ptr<hecl::Runtime::ShaderPipelines> testShaderObj =
|
||||
|
|
Loading…
Reference in New Issue