First Commit
This commit is contained in:
1481
externals/nihstro/src/assembler.cpp
vendored
Normal file
1481
externals/nihstro/src/assembler.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
316
externals/nihstro/src/disassembler.cpp
vendored
Normal file
316
externals/nihstro/src/disassembler.cpp
vendored
Normal file
@@ -0,0 +1,316 @@
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "nihstro/bit_field.h"
|
||||
#include "nihstro/shader_bytecode.h"
|
||||
#include "nihstro/parser_shbin.h"
|
||||
|
||||
using namespace nihstro;
|
||||
|
||||
struct float24 {
|
||||
static float24 FromFloat32(float val) {
|
||||
float24 ret;
|
||||
ret.value = val;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 16 bit mantissa, 7 bit exponent, 1 bit sign
|
||||
// TODO: No idea if this works as intended
|
||||
static float24 FromRawFloat24(uint32_t hex) {
|
||||
float24 ret;
|
||||
if ((hex & 0xFFFFFF) == 0) {
|
||||
ret.value = 0;
|
||||
} else {
|
||||
uint32_t mantissa = hex & 0xFFFF;
|
||||
uint32_t exponent = (hex >> 16) & 0x7F;
|
||||
uint32_t sign = hex >> 23;
|
||||
ret.value = std::pow(2.0f, (float)exponent-63.0f) * (1.0f + mantissa * std::pow(2.0f, -16.f));
|
||||
if (sign)
|
||||
ret.value = -ret.value;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Not recommended for anything but logging
|
||||
float ToFloat32() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
// Stored as a regular float, merely for convenience
|
||||
// TODO: Perform proper arithmetic on this!
|
||||
float value;
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// TODO: Make this check portable!
|
||||
if (argc < 2) {
|
||||
std::cout << "Error: No filename given" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ShbinParser parser;
|
||||
try {
|
||||
parser.ReadHeaders(argv[1]);
|
||||
|
||||
for (int i = 0; i < parser.GetDVLBHeader().num_programs; ++i) {
|
||||
auto& filename = parser.GetFilename(i);
|
||||
std::cout << "DVLE " << std::setw(3) << std::dec << i << ": "
|
||||
// << "offset: 0x" << std::hex << std::setfill('0') << std::setw(4) << offset << ", "
|
||||
<< std::hex << std::setfill('0')
|
||||
<< "byte offset to main: 0x" << std::hex << std::setw(8) << 4 * parser.GetDVLEHeader(i).main_offset_words << "\", "
|
||||
<< "original filename \"" << filename << "\""
|
||||
<< std::setfill(' ') << std::dec << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "Got " << parser.GetDVLBHeader().num_programs << " DVLE headers" << std::endl;
|
||||
|
||||
if (argc < 3) {
|
||||
std::cout << "Error: No DVLE index given" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t dvle_index = std::stoi(std::string(argv[2]));
|
||||
parser.ReadDVLE(dvle_index);
|
||||
|
||||
auto& dvle_header = parser.dvle_headers[dvle_index];
|
||||
for (int i = 0; i < parser.shader_info.constant_table.size(); ++i) {
|
||||
auto& info = parser.shader_info.constant_table[i];
|
||||
|
||||
switch (info.type) {
|
||||
case ConstantInfo::Float:
|
||||
std::cout << "Constant register info: " << GetRegisterName(RegisterType::FloatUniform) << info.regid.Value()
|
||||
<< " = (" << float24::FromRawFloat24(info.f.x).ToFloat32() << ", " << float24::FromRawFloat24(info.f.y).ToFloat32()
|
||||
<< ", " << float24::FromRawFloat24(info.f.z).ToFloat32() << ", " << float24::FromRawFloat24(info.f.w).ToFloat32() << ")"
|
||||
<< " (raw: 0x" << std::hex << std::setfill('0') << std::setw(8) << info.full_first_word
|
||||
<< " 0x" << std::setw(8) << info.f.x << " 0x" << std::setw(8) << info.f.y
|
||||
<< " 0x" << std::setw(8) << info.f.z << " 0x" << std::setw(8) << info.f.w << std::dec << std::setfill( ' ') << ")"
|
||||
<< std::endl;
|
||||
break;
|
||||
|
||||
case ConstantInfo::Int:
|
||||
std::cout << "Constant register info: " << GetRegisterName(RegisterType::IntUniform) << info.regid.Value()
|
||||
<< " = (" << (int)info.i.x << ", " << (int)info.i.y
|
||||
<< ", " << (int)info.i.z << ", " << (int)info.i.w << ")"
|
||||
<< " (raw: 0x" << std::hex << std::setfill('0') << std::setw(8) << info.full_first_word
|
||||
<< std::dec << std::setfill( ' ') << ")"
|
||||
<< std::endl;
|
||||
break;
|
||||
|
||||
case ConstantInfo::Bool:
|
||||
std::cout << "Constant register info: " << GetRegisterName(RegisterType::BoolUniform) << info.regid.Value()
|
||||
<< " = " << std::boolalpha << (bool)info.b
|
||||
<< " (raw: 0x" << std::hex << std::setfill('0') << std::setw(8) << info.full_first_word
|
||||
<< std::dec << std::setfill( ' ') << ")"
|
||||
<< std::endl;
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
std::stringstream str("Unknown constant type: ");
|
||||
str << std::hex << info.type.Value();
|
||||
throw str.str();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < parser.shader_info.label_table.size(); ++i) {
|
||||
const auto& label_info = parser.shader_info.label_table[i];
|
||||
std::cout << "Found label \"" << parser.shader_info.labels[label_info.program_offset]
|
||||
<< "\" at program offset 0x" << std::hex << 4 * label_info.program_offset
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
for (auto& info : parser.shader_info.output_register_info)
|
||||
std::cout << "Output register info: " << GetRegisterName(RegisterType::Output) << info.id.Value() << '.' << std::setw(4) << std::left << info.GetMask()
|
||||
<< " as " << std::setw(8) << info.GetSemanticName()
|
||||
<< " (" << std::hex << std::setw(16) << std::setfill('0') << (uint64_t)info.hex << std::setfill(' ') << ")" << std::endl;
|
||||
|
||||
if (!parser.shader_info.uniform_table.empty()) {
|
||||
size_t max_uniform_name_length = std::max_element(parser.shader_info.uniform_table.begin(), parser.shader_info.uniform_table.end(),
|
||||
[](const UniformInfo& i1, UniformInfo& i2) { return i1.name.length() < i2.name.length(); }
|
||||
)->name.length();
|
||||
for (auto& uniform_info : parser.shader_info.uniform_table) {
|
||||
bool is_range = (uniform_info.basic.reg_start != uniform_info.basic.reg_end);
|
||||
|
||||
std::cout << "Found uniform symbol \"" << std::setw(max_uniform_name_length) << uniform_info.name
|
||||
<< "\" for register" << (is_range ? "s " : " ") << std::dec
|
||||
<< GetRegisterName(uniform_info.basic.GetStartType()) << uniform_info.basic.GetStartIndex();
|
||||
if (is_range)
|
||||
std::cout << "-" << GetRegisterName(uniform_info.basic.GetEndType()) << uniform_info.basic.GetEndIndex();
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// std::cout << "Disassembling " << parser.GetDVLPHeader().binary_size_words << " bytes from offset "
|
||||
// << dvlp_offset << " + " << dvlp_header.binary_offset << " = " << main_offset << " (main at byte offset " << "0x" << std::hex << 4 * dvle_header.main_offset_words << ")" << std::endl;
|
||||
} catch (const std::string& err) {
|
||||
std::cout << "Exception while reading \"" << argv[1] << "\": " << err << std::endl;
|
||||
return 1;
|
||||
} catch (const std::ios_base::failure& except) {
|
||||
std::cout << "Exception while reading \"" << argv[1] << "\": ios_base::failure \"" << except.what() << "\" (invalid shbin?)" << std::endl;
|
||||
return 1;
|
||||
} catch (const std::bad_alloc&) {
|
||||
std::cout << "Exception while reading \"" << argv[1] << "\": bad_alloc (invalid shbin?)" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const ShaderInfo& shader_info = parser.shader_info;
|
||||
for (uint32_t word = 0; word < shader_info.code.size(); ++word) {
|
||||
std::cout.flags(std::ios::left | std::ios::hex);
|
||||
if (shader_info.HasLabel(word)) {
|
||||
std::cout << std::setw(8) << std::right << std::setfill('0') << 4*word
|
||||
<< " [--------] " << shader_info.GetLabel(word) << ":" << std::endl;
|
||||
}
|
||||
|
||||
Instruction instr = shader_info.code[word];
|
||||
OpCode opcode = instr.opcode.Value();
|
||||
|
||||
std::cout << std::setw(8) << std::right << std::setfill('0') << 4*word << " "
|
||||
<< "[" << std::setw(8) << std::right << std::setfill('0') << instr.hex << "] "
|
||||
<< std::setw(7) << std::left << std::setfill(' ') << opcode.GetInfo().name;
|
||||
|
||||
const SwizzlePattern& swizzle = shader_info.swizzle_info[instr.common.operand_desc_id].pattern;
|
||||
|
||||
// TODO: Not sure if name lookup works properly, yet!
|
||||
|
||||
if (opcode.GetInfo().type == OpCode::Type::Arithmetic) {
|
||||
bool src_reversed = 0 != (opcode.GetInfo().subtype & OpCode::Info::SrcInversed);
|
||||
auto src1 = instr.common.GetSrc1(src_reversed);
|
||||
auto src2 = instr.common.GetSrc2(src_reversed);
|
||||
auto dest = instr.common.dest.Value();
|
||||
|
||||
std::string src1_relative_address;
|
||||
if (!instr.common.AddressRegisterName().empty())
|
||||
src1_relative_address = "[" + instr.common.AddressRegisterName() + "]";
|
||||
|
||||
if (opcode.GetInfo().subtype & OpCode::Info::Dest) {
|
||||
std::cout << std::setw(4) << std::right << dest.GetName() << "." << swizzle.DestMaskToString() << " ";
|
||||
} else {
|
||||
std::cout << " ";
|
||||
}
|
||||
|
||||
if (opcode.GetInfo().subtype & OpCode::Info::Src1) {
|
||||
std::cout << std::setw(8) << std::right << ((swizzle.negate_src1 ? "-" : "") + src1.GetName()) + src1_relative_address << "." << swizzle.SelectorToString(false) << " ";
|
||||
} else {
|
||||
std::cout << " ";
|
||||
}
|
||||
|
||||
if (opcode.GetInfo().subtype & OpCode::Info::CompareOps) {
|
||||
std::cout << instr.common.compare_op.ToString(instr.common.compare_op.x) << " " << instr.common.compare_op.ToString(instr.common.compare_op.y) << " ";
|
||||
} else {
|
||||
}
|
||||
|
||||
if (opcode.GetInfo().subtype & OpCode::Info::Src2) {
|
||||
std::cout << std::setw(4) << std::right << (swizzle.negate_src2 ? "-" : "") + src2.GetName() << "." << swizzle.SelectorToString(true) << " ";
|
||||
} else {
|
||||
std::cout << " ";
|
||||
}
|
||||
|
||||
std::cout << std::setw(2) << instr.common.operand_desc_id.Value() << " addr:" << instr.common.address_register_index.Value()
|
||||
<< "; " << shader_info.LookupDestName(dest, swizzle) << " <- " << (swizzle.negate_src1 ? "-" : "") + shader_info.LookupSourceName(src1, instr.common.address_register_index);
|
||||
if (opcode.GetInfo().subtype & OpCode::Info::Src2)
|
||||
std::cout << ", " << (swizzle.negate_src2 ? "-" : "") + shader_info.LookupSourceName(src2, 0);
|
||||
|
||||
std::cout << std::endl;
|
||||
} else if (opcode.GetInfo().type == OpCode::Type::Conditional) {
|
||||
std::cout << "if ";
|
||||
|
||||
if (opcode.GetInfo().subtype & OpCode::Info::HasCondition) {
|
||||
const char* ops[] = {
|
||||
" || ", " && ", "", ""
|
||||
};
|
||||
if (instr.flow_control.op != instr.flow_control.JustY)
|
||||
std::cout << ((!instr.flow_control.refx) ? "!" : " ") << "cc.x";
|
||||
|
||||
std::cout << ops[instr.flow_control.op];
|
||||
|
||||
if (instr.flow_control.op != instr.flow_control.JustX)
|
||||
std::cout << ((!instr.flow_control.refy) ? "!" : " ") << "cc.y";
|
||||
|
||||
std::cout << " ";
|
||||
} else if (opcode.GetInfo().subtype & OpCode::Info::HasUniformIndex) {
|
||||
std::cout << "b" << instr.flow_control.bool_uniform_id << " ";
|
||||
}
|
||||
|
||||
uint32_t target_addr = instr.flow_control.dest_offset;
|
||||
uint32_t target_addr_else = instr.flow_control.dest_offset;
|
||||
|
||||
if (opcode.GetInfo().subtype & OpCode::Info::HasAlternative) {
|
||||
std::cout << "else jump to 0x" << std::setw(4) << std::right << std::setfill('0') << 4 * instr.flow_control.dest_offset
|
||||
<< " aka \"" << shader_info.GetLabel(instr.flow_control.dest_offset) << "\"";
|
||||
} else if (opcode.GetInfo().subtype & OpCode::Info::HasExplicitDest) {
|
||||
std::cout << "jump to 0x" << std::setw(4) << std::right << std::setfill('0') << 4 * instr.flow_control.dest_offset
|
||||
<< " aka \"" << shader_info.GetLabel(instr.flow_control.dest_offset) << "\"";
|
||||
} else {
|
||||
// TODO: Handle other cases
|
||||
}
|
||||
|
||||
if (opcode.GetInfo().subtype & OpCode::Info::HasFinishPoint) {
|
||||
std::cout << "(return on " << std::setw(4) << std::right << std::setfill('0') << 4 * instr.flow_control.dest_offset + 4 * instr.flow_control.num_instructions << "\")";
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
} else {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << std::endl << "Swizzle patterns:" << std::endl;
|
||||
|
||||
for (int i = 0; i < shader_info.swizzle_info.size(); ++i) {
|
||||
const auto& info = shader_info.swizzle_info[i];
|
||||
const auto& pattern = info.pattern;
|
||||
std::cout << "(" << std::setw(3) << std::right << std::hex << i << ") " << std::setw(8) << std::setfill('0') << pattern.hex << ": " << pattern.dest_mask.Value() << " " <<
|
||||
" " << (int)pattern.negate_src1 << " " <<
|
||||
" " << (int)pattern.src1_selector_3.Value() << " " << (int)pattern.src1_selector_2.Value() <<
|
||||
" " << (int)pattern.src1_selector_1.Value() << " " << (int)pattern.src1_selector_0.Value() << " " <<
|
||||
" " << (int)pattern.negate_src2 << " " <<
|
||||
" " << (int)pattern.src2_selector_3.Value() << " " << (int)pattern.src2_selector_2.Value() <<
|
||||
" " << (int)pattern.src2_selector_1.Value() << " " << (int)pattern.src2_selector_0.Value() << " " <<
|
||||
" " << (int)pattern.negate_src3 << " " <<
|
||||
" " << (int)pattern.src3_selector_3.Value() << " " << (int)pattern.src3_selector_2.Value() <<
|
||||
" " << (int)pattern.src3_selector_1.Value() << " " << (int)pattern.src3_selector_0.Value() << " " <<
|
||||
" " << std::setw(8) << std::setfill('0') << info.unknown << std::setfill(' ') << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
277
externals/nihstro/src/parser_assembly.cpp
vendored
Normal file
277
externals/nihstro/src/parser_assembly.cpp
vendored
Normal file
@@ -0,0 +1,277 @@
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
// Enable this for detailed XML overview of parser results
|
||||
// #define BOOST_SPIRIT_DEBUG
|
||||
|
||||
#include <boost/fusion/include/adapt_struct.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
|
||||
#include "nihstro/parser_assembly.h"
|
||||
#include "nihstro/parser_assembly_private.h"
|
||||
|
||||
#include "nihstro/shader_binary.h"
|
||||
#include "nihstro/shader_bytecode.h"
|
||||
|
||||
namespace spirit = boost::spirit;
|
||||
namespace qi = boost::spirit::qi;
|
||||
namespace ascii = boost::spirit::qi::ascii;
|
||||
namespace phoenix = boost::phoenix;
|
||||
|
||||
using spirit::_1;
|
||||
using spirit::_2;
|
||||
using spirit::_3;
|
||||
using spirit::_4;
|
||||
|
||||
using namespace nihstro;
|
||||
|
||||
// Adapt parser data structures for use with boost::spirit
|
||||
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
SetEmitInstruction::Flags,
|
||||
(boost::optional<bool>, primitive_flag)
|
||||
(boost::optional<bool>, invert_flag)
|
||||
)
|
||||
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
SetEmitInstruction,
|
||||
(OpCode, opcode)
|
||||
(unsigned, vertex_id)
|
||||
(SetEmitInstruction::Flags, flags)
|
||||
)
|
||||
|
||||
phoenix::function<ErrorHandler> error_handler;
|
||||
|
||||
template<typename Iterator, bool require_end_of_line>
|
||||
TrivialOpParser<Iterator, require_end_of_line>::TrivialOpParser(const ParserContext& context)
|
||||
: TrivialOpParser::base_type(trivial_instruction),
|
||||
common(context),
|
||||
opcodes_trivial(common.opcodes_trivial),
|
||||
opcodes_compare(common.opcodes_compare),
|
||||
opcodes_float(common.opcodes_float),
|
||||
opcodes_flowcontrol(common.opcodes_flowcontrol),
|
||||
end_of_statement(common.end_of_statement),
|
||||
diagnostics(common.diagnostics) {
|
||||
|
||||
// Setup rules
|
||||
if (require_end_of_line) {
|
||||
opcode = qi::no_case[qi::lexeme[opcodes_trivial >> &ascii::space]];
|
||||
trivial_instruction = opcode > end_of_statement;
|
||||
} else {
|
||||
opcode = qi::no_case[qi::lexeme[opcodes_trivial | opcodes_compare | opcodes_float[0]
|
||||
| opcodes_float[1] | opcodes_float[2] | opcodes_float[3]
|
||||
| opcodes_flowcontrol[0] | opcodes_flowcontrol[1] >> &ascii::space]];
|
||||
trivial_instruction = opcode;
|
||||
}
|
||||
|
||||
// Error handling
|
||||
BOOST_SPIRIT_DEBUG_NODE(opcode);
|
||||
BOOST_SPIRIT_DEBUG_NODE(trivial_instruction);
|
||||
|
||||
qi::on_error<qi::fail>(trivial_instruction, error_handler(phoenix::ref(diagnostics), _1, _2, _3, _4));
|
||||
}
|
||||
|
||||
template<typename Iterator>
|
||||
SetEmitParser<Iterator>::SetEmitParser(const ParserContext& context)
|
||||
: SetEmitParser::base_type(setemit_instruction),
|
||||
common(context),
|
||||
opcodes_setemit(common.opcodes_setemit),
|
||||
end_of_statement(common.end_of_statement),
|
||||
diagnostics(common.diagnostics) {
|
||||
|
||||
// Setup rules
|
||||
|
||||
auto comma_rule = qi::lit(',');
|
||||
|
||||
opcode = qi::lexeme[qi::no_case[opcodes_setemit] >> &ascii::space];
|
||||
|
||||
vertex_id = qi::uint_;
|
||||
prim_flag = qi::lit("prim") >> &(!ascii::alnum) >> qi::attr(true);
|
||||
inv_flag = qi::lit("inv") >> &(!ascii::alnum) >> qi::attr(true);
|
||||
flags = ((comma_rule >> prim_flag) ^ (comma_rule >> inv_flag));
|
||||
|
||||
setemit_instruction = ((opcode >> vertex_id) >> (flags | qi::attr(SetEmitInstruction::Flags{}))) > end_of_statement;
|
||||
|
||||
// Error handling
|
||||
BOOST_SPIRIT_DEBUG_NODE(opcode);
|
||||
BOOST_SPIRIT_DEBUG_NODE(vertex_id);
|
||||
BOOST_SPIRIT_DEBUG_NODE(prim_flag);
|
||||
BOOST_SPIRIT_DEBUG_NODE(inv_flag);
|
||||
BOOST_SPIRIT_DEBUG_NODE(flags);
|
||||
BOOST_SPIRIT_DEBUG_NODE(setemit_instruction);
|
||||
|
||||
qi::on_error<qi::fail>(setemit_instruction, error_handler(phoenix::ref(diagnostics), _1, _2, _3, _4));
|
||||
}
|
||||
|
||||
template<typename Iterator>
|
||||
LabelParser<Iterator>::LabelParser(const ParserContext& context)
|
||||
: LabelParser::base_type(label), common(context),
|
||||
end_of_statement(common.end_of_statement),
|
||||
identifier(common.identifier),
|
||||
diagnostics(common.diagnostics) {
|
||||
|
||||
label = identifier >> qi::lit(':') > end_of_statement;
|
||||
|
||||
BOOST_SPIRIT_DEBUG_NODE(label);
|
||||
|
||||
qi::on_error<qi::fail>(label, error_handler(phoenix::ref(diagnostics), _1, _2, _3, _4));
|
||||
}
|
||||
template struct LabelParser<ParserIterator>;
|
||||
|
||||
|
||||
struct Parser::ParserImpl {
|
||||
using Iterator = SourceTreeIterator;
|
||||
|
||||
ParserImpl(const ParserContext& context) : label(context), plain_instruction(context),
|
||||
simple_instruction(context), instruction(context),
|
||||
compare(context), flow_control(context),
|
||||
setemit(context), declaration(context) {
|
||||
}
|
||||
|
||||
unsigned Skip(Iterator& begin, Iterator end) {
|
||||
unsigned lines_skipped = 0;
|
||||
do {
|
||||
parse(begin, end, skipper);
|
||||
lines_skipped++;
|
||||
} while (boost::spirit::qi::parse(begin, end, boost::spirit::qi::eol));
|
||||
|
||||
return --lines_skipped;
|
||||
}
|
||||
|
||||
void SkipSingleLine(Iterator& begin, Iterator end) {
|
||||
qi::parse(begin, end, *(qi::char_ - (qi::eol | qi::eoi)) >> (qi::eol | qi::eoi));
|
||||
}
|
||||
|
||||
bool ParseLabel(Iterator& begin, Iterator end, StatementLabel* content) {
|
||||
assert(content != nullptr);
|
||||
|
||||
return phrase_parse(begin, end, label, skipper, *content);
|
||||
}
|
||||
|
||||
bool ParseOpCode(Iterator& begin, Iterator end, OpCode* content) {
|
||||
assert(content != nullptr);
|
||||
|
||||
return phrase_parse(begin, end, plain_instruction, skipper, *content);
|
||||
}
|
||||
|
||||
bool ParseSimpleInstruction(Iterator& begin, Iterator end, OpCode* content) {
|
||||
assert(content != nullptr);
|
||||
|
||||
return phrase_parse(begin, end, simple_instruction, skipper, *content);
|
||||
}
|
||||
|
||||
bool ParseFloatOp(Iterator& begin, Iterator end, FloatOpInstruction* content) {
|
||||
assert(content != nullptr);
|
||||
|
||||
return phrase_parse(begin, end, instruction, skipper, *content);
|
||||
}
|
||||
|
||||
bool ParseCompare(Iterator& begin, Iterator end, CompareInstruction* content) {
|
||||
assert(content != nullptr);
|
||||
|
||||
return phrase_parse(begin, end, compare, skipper, *content);
|
||||
}
|
||||
|
||||
bool ParseFlowControl(Iterator& begin, Iterator end, FlowControlInstruction* content) {
|
||||
assert(content != nullptr);
|
||||
|
||||
return phrase_parse(begin, end, flow_control, skipper, *content);
|
||||
}
|
||||
|
||||
bool ParseSetEmit(Iterator& begin, Iterator end, SetEmitInstruction* content) {
|
||||
assert(content != nullptr);
|
||||
|
||||
return phrase_parse(begin, end, setemit, skipper, *content);
|
||||
}
|
||||
|
||||
bool ParseDeclaration(Iterator& begin, Iterator end, StatementDeclaration* content) {
|
||||
assert(content != nullptr);
|
||||
|
||||
return phrase_parse(begin, end, declaration, skipper, *content);
|
||||
}
|
||||
|
||||
private:
|
||||
AssemblySkipper<Iterator> skipper;
|
||||
|
||||
LabelParser<Iterator> label;
|
||||
TrivialOpParser<Iterator, false> plain_instruction;
|
||||
TrivialOpParser<Iterator, true> simple_instruction;
|
||||
FloatOpParser<Iterator> instruction;
|
||||
CompareParser<Iterator> compare;
|
||||
FlowControlParser<Iterator> flow_control;
|
||||
SetEmitParser<Iterator> setemit;
|
||||
DeclarationParser<Iterator> declaration;
|
||||
};
|
||||
|
||||
|
||||
|
||||
Parser::Parser(const ParserContext& context) : impl(new ParserImpl(context)) {
|
||||
};
|
||||
|
||||
Parser::~Parser() {
|
||||
}
|
||||
|
||||
unsigned Parser::Skip(Iterator& begin, Iterator end) {
|
||||
return impl->Skip(begin, end);
|
||||
}
|
||||
|
||||
void Parser::SkipSingleLine(Iterator& begin, Iterator end) {
|
||||
impl->SkipSingleLine(begin, end);
|
||||
}
|
||||
|
||||
bool Parser::ParseLabel(Iterator& begin, Iterator end, StatementLabel* label) {
|
||||
return impl->ParseLabel(begin, end, label);
|
||||
}
|
||||
|
||||
bool Parser::ParseOpCode(Iterator& begin, Iterator end, OpCode* opcode) {
|
||||
return impl->ParseOpCode(begin, end, opcode);
|
||||
}
|
||||
|
||||
bool Parser::ParseSimpleInstruction(Iterator& begin, Iterator end, OpCode* opcode) {
|
||||
return impl->ParseSimpleInstruction(begin, end, opcode);
|
||||
}
|
||||
|
||||
bool Parser::ParseFloatOp(Iterator& begin, Iterator end, FloatOpInstruction* instruction) {
|
||||
return impl->ParseFloatOp(begin, end, instruction);
|
||||
}
|
||||
|
||||
bool Parser::ParseCompare(Iterator& begin, Iterator end, CompareInstruction* content) {
|
||||
return impl->ParseCompare(begin, end, content);
|
||||
}
|
||||
|
||||
bool Parser::ParseFlowControl(Iterator& begin, Iterator end, FlowControlInstruction* content) {
|
||||
return impl->ParseFlowControl(begin, end, content);
|
||||
}
|
||||
|
||||
bool Parser::ParseSetEmit(Iterator& begin, Iterator end, SetEmitInstruction* content) {
|
||||
return impl->ParseSetEmit(begin, end, content);
|
||||
}
|
||||
|
||||
bool Parser::ParseDeclaration(Iterator& begin, Iterator end, StatementDeclaration* declaration) {
|
||||
return impl->ParseDeclaration(begin, end, declaration);
|
||||
}
|
||||
185
externals/nihstro/src/parser_assembly/common.cpp
vendored
Normal file
185
externals/nihstro/src/parser_assembly/common.cpp
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
// Enable this for detailed XML overview of parser results
|
||||
// #define BOOST_SPIRIT_DEBUG
|
||||
|
||||
#include <boost/fusion/include/adapt_struct.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
|
||||
#include "nihstro/parser_assembly.h"
|
||||
#include "nihstro/parser_assembly_private.h"
|
||||
|
||||
#include "nihstro/shader_binary.h"
|
||||
#include "nihstro/shader_bytecode.h"
|
||||
|
||||
namespace spirit = boost::spirit;
|
||||
namespace qi = boost::spirit::qi;
|
||||
namespace ascii = boost::spirit::qi::ascii;
|
||||
namespace phoenix = boost::phoenix;
|
||||
|
||||
using spirit::_1;
|
||||
using spirit::_2;
|
||||
using spirit::_3;
|
||||
using spirit::_4;
|
||||
|
||||
using namespace nihstro;
|
||||
|
||||
// Adapt parser data structures for use with boost::spirit
|
||||
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
IntegerWithSign,
|
||||
(int, sign)
|
||||
(unsigned, value)
|
||||
)
|
||||
|
||||
/**
|
||||
* Implementation of transform_attribute from std::vector<InputSwizzlerMask::Component> to InputSwizzlerMask.
|
||||
* This eases swizzle mask parsing a lot.
|
||||
*/
|
||||
namespace boost { namespace spirit { namespace traits {
|
||||
template<>
|
||||
struct transform_attribute<InputSwizzlerMask, std::vector<InputSwizzlerMask::Component>, qi::domain>
|
||||
{
|
||||
using Exposed = InputSwizzlerMask;
|
||||
|
||||
using type = std::vector<InputSwizzlerMask::Component>;
|
||||
|
||||
static void post(Exposed& val, const type& attr) {
|
||||
val.num_components = attr.size();
|
||||
for (size_t i = 0; i < attr.size(); ++i)
|
||||
val.components[i] = attr[i];
|
||||
}
|
||||
|
||||
static type pre(Exposed& val) {
|
||||
type vec;
|
||||
for (int i = 0; i < val.num_components; ++i)
|
||||
vec.push_back(val.components[i]);
|
||||
return vec;
|
||||
}
|
||||
|
||||
static void fail(Exposed&) { }
|
||||
};
|
||||
}}} // namespaces
|
||||
|
||||
template<>
|
||||
CommonRules<ParserIterator>::CommonRules(const ParserContext& context) {
|
||||
// Setup symbol table
|
||||
opcodes_trivial.add
|
||||
( "nop", OpCode::Id::NOP )
|
||||
( "end", OpCode::Id::END )
|
||||
( "emit", OpCode::Id::EMIT )
|
||||
( "else", OpCode::Id::ELSE )
|
||||
( "endif", OpCode::Id::ENDIF )
|
||||
( "endloop", OpCode::Id::ENDLOOP );
|
||||
|
||||
opcodes_float[0].add
|
||||
( "mova", OpCode::Id::MOVA );
|
||||
|
||||
opcodes_float[1].add
|
||||
( "exp", OpCode::Id::EX2 )
|
||||
( "log", OpCode::Id::LG2 )
|
||||
( "lit", OpCode::Id::LIT )
|
||||
( "flr", OpCode::Id::FLR )
|
||||
( "rcp", OpCode::Id::RCP )
|
||||
( "rsq", OpCode::Id::RSQ )
|
||||
( "mov", OpCode::Id::MOV );
|
||||
opcodes_float[2].add
|
||||
( "add", OpCode::Id::ADD )
|
||||
( "dp3", OpCode::Id::DP3 )
|
||||
( "dp4", OpCode::Id::DP4 )
|
||||
( "dph", OpCode::Id::DPH )
|
||||
( "dst", OpCode::Id::DST )
|
||||
( "mul", OpCode::Id::MUL )
|
||||
( "sge", OpCode::Id::SGE )
|
||||
( "slt", OpCode::Id::SLT )
|
||||
( "max", OpCode::Id::MAX )
|
||||
( "min", OpCode::Id::MIN );
|
||||
opcodes_float[3].add
|
||||
( "mad", OpCode::Id::MAD );
|
||||
|
||||
opcodes_compare.add
|
||||
( "cmp", OpCode::Id::CMP );
|
||||
|
||||
opcodes_flowcontrol[0].add
|
||||
( "break", OpCode::Id::BREAK )
|
||||
( "breakc", OpCode::Id::BREAKC )
|
||||
( "if", OpCode::Id::GEN_IF )
|
||||
( "loop", OpCode::Id::LOOP );
|
||||
opcodes_flowcontrol[1].add
|
||||
( "jmp", OpCode::Id::GEN_JMP )
|
||||
( "call", OpCode::Id::GEN_CALL );
|
||||
|
||||
opcodes_setemit.add
|
||||
( "setemitraw", OpCode::Id::SETEMIT );
|
||||
|
||||
signs.add( "+", +1)
|
||||
( "-", -1);
|
||||
|
||||
// TODO: Add rgba/stq masks
|
||||
swizzlers.add
|
||||
( "x", InputSwizzlerMask::x )
|
||||
( "y", InputSwizzlerMask::y )
|
||||
( "z", InputSwizzlerMask::z )
|
||||
( "w", InputSwizzlerMask::w );
|
||||
|
||||
// TODO: Make sure this is followed by a space or *some* separator
|
||||
// TODO: Use qi::repeat(1,4)(swizzlers) instead of Kleene [failed to work when I tried, so make this work!]
|
||||
// TODO: Use qi::lexeme[swizzlers] [crashed when I tried, so make this work!]
|
||||
swizzle_mask = qi::attr_cast<InputSwizzlerMask, std::vector<InputSwizzlerMask::Component>>(*swizzlers);
|
||||
|
||||
identifier = qi::lexeme[qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z0-9_")];
|
||||
peek_identifier = &identifier;
|
||||
|
||||
uint_after_sign = qi::uint_; // TODO: NOT dot (or alphanum) after this to prevent floats..., TODO: overflows?
|
||||
sign_with_uint = signs > uint_after_sign;
|
||||
index_expression_first_term = (qi::attr(+1) >> qi::uint_) | (peek_identifier > identifier);
|
||||
index_expression_following_terms = (qi::lit('+') >> peek_identifier > identifier) | sign_with_uint;
|
||||
index_expression = (-index_expression_first_term) // the first element has an optional sign
|
||||
>> (*index_expression_following_terms); // following elements have a mandatory sign
|
||||
|
||||
expression = ((-signs) > peek_identifier > identifier) >> (-(qi::lit('[') > index_expression > qi::lit(']'))) >> *(qi::lit('.') > swizzle_mask);
|
||||
|
||||
end_of_statement = qi::omit[qi::eol | qi::eoi];
|
||||
|
||||
// Error handling
|
||||
BOOST_SPIRIT_DEBUG_NODE(identifier);
|
||||
BOOST_SPIRIT_DEBUG_NODE(uint_after_sign);
|
||||
BOOST_SPIRIT_DEBUG_NODE(index_expression);
|
||||
BOOST_SPIRIT_DEBUG_NODE(peek_identifier);
|
||||
BOOST_SPIRIT_DEBUG_NODE(expression);
|
||||
BOOST_SPIRIT_DEBUG_NODE(swizzle_mask);
|
||||
BOOST_SPIRIT_DEBUG_NODE(end_of_statement);
|
||||
|
||||
diagnostics.Add(swizzle_mask.name(), "Expected swizzle mask after period");
|
||||
diagnostics.Add(peek_identifier.name(), "Expected identifier");
|
||||
diagnostics.Add(uint_after_sign.name(), "Expected integer number after sign");
|
||||
diagnostics.Add(index_expression.name(), "Expected index expression between '[' and ']'");
|
||||
diagnostics.Add(expression.name(), "Expected expression of a known identifier");
|
||||
diagnostics.Add(end_of_statement.name(), "Expected end of statement");
|
||||
}
|
||||
106
externals/nihstro/src/parser_assembly/compare.cpp
vendored
Normal file
106
externals/nihstro/src/parser_assembly/compare.cpp
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
// Enable this for detailed XML overview of parser results
|
||||
// #define BOOST_SPIRIT_DEBUG
|
||||
|
||||
#include <boost/fusion/include/adapt_struct.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
|
||||
#include "nihstro/parser_assembly.h"
|
||||
#include "nihstro/parser_assembly_private.h"
|
||||
|
||||
#include "nihstro/shader_binary.h"
|
||||
#include "nihstro/shader_bytecode.h"
|
||||
|
||||
namespace spirit = boost::spirit;
|
||||
namespace qi = boost::spirit::qi;
|
||||
namespace ascii = boost::spirit::qi::ascii;
|
||||
namespace phoenix = boost::phoenix;
|
||||
|
||||
using spirit::_1;
|
||||
using spirit::_2;
|
||||
using spirit::_3;
|
||||
using spirit::_4;
|
||||
|
||||
using namespace nihstro;
|
||||
|
||||
// Adapt parser data structures for use with boost::spirit
|
||||
|
||||
/*BOOST_FUSION_ADAPT_STRUCT(
|
||||
IntegerWithSign,
|
||||
(int, sign)
|
||||
(unsigned, value)
|
||||
)
|
||||
*/
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
CompareInstruction,
|
||||
(OpCode, opcode)
|
||||
(std::vector<Expression>, arguments)
|
||||
(std::vector<Instruction::Common::CompareOpType::Op>, ops)
|
||||
)
|
||||
|
||||
template<>
|
||||
CompareParser<ParserIterator>::CompareParser(const ParserContext& context)
|
||||
: CompareParser::base_type(instruction),
|
||||
common(context),
|
||||
opcodes_compare(common.opcodes_compare),
|
||||
expression(common.expression),
|
||||
end_of_statement(common.end_of_statement),
|
||||
diagnostics(common.diagnostics) {
|
||||
|
||||
// TODO: Will this properly match >= ?
|
||||
compare_ops.add
|
||||
( "==", CompareOp::Equal )
|
||||
( "!=", CompareOp::NotEqual )
|
||||
( "<", CompareOp::LessThan )
|
||||
( "<=", CompareOp::LessEqual )
|
||||
( ">", CompareOp::GreaterThan )
|
||||
( ">=", CompareOp::GreaterEqual );
|
||||
|
||||
// Setup rules
|
||||
|
||||
auto comma_rule = qi::lit(',');
|
||||
|
||||
opcode = qi::no_case[qi::lexeme[opcodes_compare >> &ascii::space]];
|
||||
compare_op = qi::lexeme[compare_ops];
|
||||
|
||||
// cmp src1, src2, op1, op2
|
||||
// TODO: Also allow "cmp src1 op1 src2, src1 op2 src2"
|
||||
two_ops = compare_op > comma_rule > compare_op;
|
||||
two_expressions = expression > comma_rule > expression;
|
||||
instr[0] = opcode > two_expressions > comma_rule > two_ops;
|
||||
|
||||
instruction = instr[0] > end_of_statement;
|
||||
|
||||
// Error handling
|
||||
BOOST_SPIRIT_DEBUG_NODE(instr[0]);
|
||||
BOOST_SPIRIT_DEBUG_NODE(instruction);
|
||||
|
||||
qi::on_error<qi::fail>(instruction, error_handler(phoenix::ref(diagnostics), _1, _2, _3, _4));
|
||||
}
|
||||
132
externals/nihstro/src/parser_assembly/declaration.cpp
vendored
Normal file
132
externals/nihstro/src/parser_assembly/declaration.cpp
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
#include <boost/fusion/include/adapt_struct.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
|
||||
#include "nihstro/parser_assembly.h"
|
||||
#include "nihstro/parser_assembly_private.h"
|
||||
|
||||
#include "nihstro/shader_binary.h"
|
||||
#include "nihstro/shader_bytecode.h"
|
||||
|
||||
using spirit::_1;
|
||||
using spirit::_2;
|
||||
using spirit::_3;
|
||||
using spirit::_4;
|
||||
|
||||
using namespace nihstro;
|
||||
|
||||
// Adapt parser data structures for use with boost::spirit
|
||||
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
ConditionInput,
|
||||
(bool, invert)
|
||||
(Identifier, identifier)
|
||||
(boost::optional<InputSwizzlerMask>, swizzler_mask)
|
||||
)
|
||||
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
StatementDeclaration::Extra,
|
||||
(std::vector<float>, constant_value)
|
||||
(boost::optional<OutputRegisterInfo::Type>, output_semantic)
|
||||
)
|
||||
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
StatementDeclaration,
|
||||
(std::string, alias_name)
|
||||
(Identifier, identifier_start)
|
||||
(boost::optional<Identifier>, identifier_end)
|
||||
(boost::optional<InputSwizzlerMask>, swizzle_mask)
|
||||
(StatementDeclaration::Extra, extra)
|
||||
)
|
||||
|
||||
// Manually define a swap() overload for qi::hold to work.
|
||||
/*namespace boost {
|
||||
namespace spirit {
|
||||
void swap(nihstro::Condition& a, nihstro::Condition& b) {
|
||||
boost::fusion::swap(a, b);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
template<>
|
||||
DeclarationParser<ParserIterator>::DeclarationParser(const ParserContext& context)
|
||||
: DeclarationParser::base_type(declaration),
|
||||
common(context),
|
||||
identifier(common.identifier), swizzle_mask(common.swizzle_mask),
|
||||
end_of_statement(common.end_of_statement),
|
||||
diagnostics(common.diagnostics) {
|
||||
|
||||
// Setup symbol table
|
||||
output_semantics.add("position", OutputRegisterInfo::POSITION);
|
||||
output_semantics.add("quaternion", OutputRegisterInfo::QUATERNION);
|
||||
output_semantics.add("color", OutputRegisterInfo::COLOR);
|
||||
output_semantics.add("texcoord0", OutputRegisterInfo::TEXCOORD0);
|
||||
output_semantics.add("texcoord1", OutputRegisterInfo::TEXCOORD1);
|
||||
output_semantics.add("texcoord2", OutputRegisterInfo::TEXCOORD2);
|
||||
output_semantics.add("view", OutputRegisterInfo::VIEW);
|
||||
output_semantics_rule = qi::lexeme[output_semantics];
|
||||
|
||||
// Setup rules
|
||||
|
||||
alias_identifier = qi::omit[qi::lexeme["alias" >> ascii::blank]] > identifier;
|
||||
|
||||
// e.g. 5.4 or (1.1, 2, 3)
|
||||
constant = (qi::repeat(1)[qi::float_]
|
||||
| (qi::lit('(') > (qi::float_ % qi::lit(',')) > qi::lit(')')));
|
||||
|
||||
dummy_const = qi::attr(std::vector<float>());
|
||||
dummy_semantic = qi::attr(boost::optional<OutputRegisterInfo::Type>());
|
||||
|
||||
// match a constant or a semantic, and fill the respective other one with a dummy
|
||||
const_or_semantic = (dummy_const >> output_semantics_rule) | (constant >> dummy_semantic);
|
||||
|
||||
// TODO: Would like to use +ascii::blank instead, but somehow that fails to parse lines like ".alias name o2.xy texcoord0" correctly
|
||||
string_as = qi::omit[qi::no_skip[*/*+*/ascii::blank >> qi::lit("as") >> +ascii::blank]];
|
||||
|
||||
declaration = ((qi::lit('.') > alias_identifier) >> identifier >> -(qi::lit('-') > identifier) >> -(qi::lit('.') > swizzle_mask))
|
||||
>> (
|
||||
(string_as > const_or_semantic)
|
||||
| (dummy_const >> dummy_semantic)
|
||||
)
|
||||
> end_of_statement;
|
||||
|
||||
// Error handling
|
||||
output_semantics_rule.name("output semantic after \"as\"");
|
||||
alias_identifier.name("known preprocessor directive (i.e. alias).");
|
||||
const_or_semantic.name("constant or semantic after \"as\"");
|
||||
|
||||
BOOST_SPIRIT_DEBUG_NODE(output_semantics_rule);
|
||||
BOOST_SPIRIT_DEBUG_NODE(constant);
|
||||
BOOST_SPIRIT_DEBUG_NODE(alias_identifier);
|
||||
BOOST_SPIRIT_DEBUG_NODE(const_or_semantic);
|
||||
BOOST_SPIRIT_DEBUG_NODE(declaration);
|
||||
|
||||
qi::on_error<qi::fail>(declaration, error_handler(phoenix::ref(diagnostics), _1, _2, _3, _4));
|
||||
}
|
||||
116
externals/nihstro/src/parser_assembly/floatop.cpp
vendored
Normal file
116
externals/nihstro/src/parser_assembly/floatop.cpp
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
// Enable this for detailed XML overview of parser results
|
||||
// #define BOOST_SPIRIT_DEBUG
|
||||
|
||||
#include <boost/fusion/include/adapt_struct.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
|
||||
#include "nihstro/parser_assembly.h"
|
||||
#include "nihstro/parser_assembly_private.h"
|
||||
|
||||
#include "nihstro/shader_binary.h"
|
||||
#include "nihstro/shader_bytecode.h"
|
||||
|
||||
namespace spirit = boost::spirit;
|
||||
namespace qi = boost::spirit::qi;
|
||||
namespace ascii = boost::spirit::qi::ascii;
|
||||
namespace phoenix = boost::phoenix;
|
||||
|
||||
using spirit::_1;
|
||||
using spirit::_2;
|
||||
using spirit::_3;
|
||||
using spirit::_4;
|
||||
|
||||
using namespace nihstro;
|
||||
|
||||
// Adapt parser data structures for use with boost::spirit
|
||||
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
StatementInstruction,
|
||||
(OpCode, opcode)
|
||||
(std::vector<Expression>, expressions)
|
||||
)
|
||||
|
||||
template<>
|
||||
FloatOpParser<ParserIterator>::FloatOpParser(const ParserContext& context)
|
||||
: FloatOpParser::base_type(float_instruction),
|
||||
common(context),
|
||||
opcodes_float(common.opcodes_float),
|
||||
expression(common.expression),
|
||||
end_of_statement(common.end_of_statement),
|
||||
diagnostics(common.diagnostics) {
|
||||
|
||||
// Setup rules
|
||||
|
||||
auto comma_rule = qi::lit(',');
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
// Make sure that a mnemonic is always followed by a space (such that e.g. "addbla" fails to match)
|
||||
opcode[i] = qi::no_case[qi::lexeme[opcodes_float[i] >> &ascii::space]];
|
||||
}
|
||||
|
||||
// chain of arguments for each group of opcodes
|
||||
expression_chain[0] = expression;
|
||||
for (int i = 1; i < 4; ++i) {
|
||||
expression_chain[i] = expression_chain[i - 1] >> comma_rule > expression;
|
||||
}
|
||||
|
||||
// e.g. "add o1, t2, t5"
|
||||
float_instr[0] = opcode[0] > expression_chain[0];
|
||||
float_instr[1] = opcode[1] > expression_chain[1];
|
||||
float_instr[2] = opcode[2] > expression_chain[2];
|
||||
float_instr[3] = opcode[3] > expression_chain[3];
|
||||
|
||||
float_instruction %= (float_instr[0] | float_instr[1] | float_instr[2] | float_instr[3]) > end_of_statement;
|
||||
|
||||
// Error handling
|
||||
BOOST_SPIRIT_DEBUG_NODE(opcode[0]);
|
||||
BOOST_SPIRIT_DEBUG_NODE(opcode[1]);
|
||||
BOOST_SPIRIT_DEBUG_NODE(opcode[2]);
|
||||
BOOST_SPIRIT_DEBUG_NODE(opcode[3]);
|
||||
|
||||
BOOST_SPIRIT_DEBUG_NODE(expression_chain[0]);
|
||||
BOOST_SPIRIT_DEBUG_NODE(expression_chain[1]);
|
||||
BOOST_SPIRIT_DEBUG_NODE(expression_chain[2]);
|
||||
BOOST_SPIRIT_DEBUG_NODE(expression_chain[3]);
|
||||
|
||||
BOOST_SPIRIT_DEBUG_NODE(float_instr[0]);
|
||||
BOOST_SPIRIT_DEBUG_NODE(float_instr[1]);
|
||||
BOOST_SPIRIT_DEBUG_NODE(float_instr[2]);
|
||||
BOOST_SPIRIT_DEBUG_NODE(float_instr[3]);
|
||||
BOOST_SPIRIT_DEBUG_NODE(float_instruction);
|
||||
|
||||
diagnostics.Add(expression_chain[0].name(), "one argument");
|
||||
diagnostics.Add(expression_chain[1].name(), "two arguments");
|
||||
diagnostics.Add(expression_chain[2].name(), "three arguments");
|
||||
diagnostics.Add(expression_chain[3].name(), "four arguments");
|
||||
|
||||
qi::on_error<qi::fail>(float_instruction, error_handler(phoenix::ref(diagnostics), _1, _2, _3, _4));
|
||||
}
|
||||
148
externals/nihstro/src/parser_assembly/flowcontrol.cpp
vendored
Normal file
148
externals/nihstro/src/parser_assembly/flowcontrol.cpp
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
// Enable this for detailed XML overview of parser results
|
||||
// #define BOOST_SPIRIT_DEBUG
|
||||
|
||||
#include <boost/fusion/include/adapt_struct.hpp>
|
||||
#include <boost/fusion/include/swap.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
|
||||
#include "nihstro/parser_assembly.h"
|
||||
#include "nihstro/parser_assembly_private.h"
|
||||
|
||||
#include "nihstro/shader_binary.h"
|
||||
#include "nihstro/shader_bytecode.h"
|
||||
|
||||
namespace spirit = boost::spirit;
|
||||
namespace qi = boost::spirit::qi;
|
||||
namespace ascii = boost::spirit::qi::ascii;
|
||||
namespace phoenix = boost::phoenix;
|
||||
|
||||
using spirit::_1;
|
||||
using spirit::_2;
|
||||
using spirit::_3;
|
||||
using spirit::_4;
|
||||
|
||||
using namespace nihstro;
|
||||
|
||||
// Adapt parser data structures for use with boost::spirit
|
||||
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
ConditionInput,
|
||||
(bool, invert)
|
||||
(Identifier, identifier)
|
||||
(boost::optional<InputSwizzlerMask>, swizzler_mask)
|
||||
)
|
||||
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
Condition,
|
||||
(ConditionInput, input1)
|
||||
(Instruction::FlowControlType::Op, op)
|
||||
(ConditionInput, input2)
|
||||
)
|
||||
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
FlowControlInstruction,
|
||||
(OpCode, opcode)
|
||||
(std::string, target_label)
|
||||
(boost::optional<std::string>, return_label)
|
||||
(boost::optional<Condition>, condition)
|
||||
)
|
||||
|
||||
// Manually define a swap() overload for qi::hold to work.
|
||||
namespace boost {
|
||||
namespace spirit {
|
||||
void swap(nihstro::Condition& a, nihstro::Condition& b) {
|
||||
boost::fusion::swap(a, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
FlowControlParser<ParserIterator>::FlowControlParser(const ParserContext& context)
|
||||
: FlowControlParser::base_type(flow_control_instruction),
|
||||
common(context),
|
||||
opcodes_flowcontrol(common.opcodes_flowcontrol),
|
||||
expression(common.expression),
|
||||
identifier(common.identifier),
|
||||
swizzle_mask(common.swizzle_mask),
|
||||
end_of_statement(common.end_of_statement),
|
||||
diagnostics(common.diagnostics) {
|
||||
|
||||
condition_ops.add
|
||||
( "&&", ConditionOp::And )
|
||||
( "||", ConditionOp::Or );
|
||||
|
||||
// Setup rules
|
||||
|
||||
auto blank_rule = qi::omit[ascii::blank];
|
||||
auto label_rule = identifier.alias();
|
||||
|
||||
opcode[0] = qi::lexeme[qi::no_case[opcodes_flowcontrol[0]] >> &ascii::space];
|
||||
opcode[1] = qi::lexeme[qi::no_case[opcodes_flowcontrol[1]] >> &ascii::space];
|
||||
|
||||
condition_op = qi::lexeme[condition_ops];
|
||||
|
||||
negation = qi::matches[qi::lit("!")];
|
||||
|
||||
condition_input = negation >> identifier >> -(qi::lit('.') > swizzle_mask);
|
||||
|
||||
// May be a condition involving the conditional codes, or a reference to a uniform
|
||||
// TODO: Make sure we use qi::hold wherever necessary
|
||||
condition = qi::hold[condition_input >> condition_op >> condition_input]
|
||||
| (condition_input >> qi::attr(ConditionOp::JustX) >> qi::attr(ConditionInput{}));
|
||||
|
||||
// if condition
|
||||
instr[0] = opcode[0]
|
||||
>> qi::attr("__dummy") // Dummy label (set indirectly using else,endif, or endloop pseudo-instructions)
|
||||
>> qi::attr(boost::optional<std::string>()) // Dummy return label
|
||||
>> condition;
|
||||
|
||||
// call target_label until return_label if condition
|
||||
instr[1] = opcode[1]
|
||||
>> label_rule
|
||||
>> -(qi::no_skip[(blank_rule >> qi::lit("until")) > blank_rule] >> label_rule)
|
||||
>> -(qi::no_skip[(blank_rule >> qi::lit("if")) > blank_rule] >> condition);
|
||||
|
||||
flow_control_instruction %= (instr[0] | instr[1]) > end_of_statement;
|
||||
|
||||
// Error handling
|
||||
BOOST_SPIRIT_DEBUG_NODE(opcode[0]);
|
||||
BOOST_SPIRIT_DEBUG_NODE(opcode[1]);
|
||||
BOOST_SPIRIT_DEBUG_NODE(negation);
|
||||
BOOST_SPIRIT_DEBUG_NODE(condition_op);
|
||||
BOOST_SPIRIT_DEBUG_NODE(condition_input);
|
||||
BOOST_SPIRIT_DEBUG_NODE(condition);
|
||||
|
||||
BOOST_SPIRIT_DEBUG_NODE(instr[0]);
|
||||
BOOST_SPIRIT_DEBUG_NODE(instr[1]);
|
||||
BOOST_SPIRIT_DEBUG_NODE(flow_control_instruction);
|
||||
|
||||
qi::on_error<qi::fail>(flow_control_instruction, error_handler(phoenix::ref(diagnostics), _1, _2, _3, _4));
|
||||
}
|
||||
140
externals/nihstro/src/parser_shbin.cpp
vendored
Normal file
140
externals/nihstro/src/parser_shbin.cpp
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "nihstro/parser_shbin.h"
|
||||
|
||||
using namespace nihstro;
|
||||
|
||||
void ShbinParser::ReadHeaders(const std::string& filename) {
|
||||
file.exceptions(std::fstream::badbit | std::fstream::failbit | std::fstream::eofbit);
|
||||
file.open(filename, std::fstream::in | std::fstream::binary);
|
||||
|
||||
file.seekg(0);
|
||||
file.read((char*)&dvlb_header, sizeof(dvlb_header));
|
||||
if (dvlb_header.magic_word != DVLBHeader::MAGIC_WORD) {
|
||||
std::stringstream stream;
|
||||
stream << "Wrong DVLB magic word: Got 0x" << std::hex << dvlb_header.magic_word;
|
||||
throw stream.str();
|
||||
}
|
||||
|
||||
dvle_offsets.resize(dvlb_header.num_programs);
|
||||
dvle_headers.resize(dvlb_header.num_programs);
|
||||
for (auto& offset : dvle_offsets) {
|
||||
file.read((char*)&offset, sizeof(offset));
|
||||
}
|
||||
|
||||
// DVLP comes directly after the DVLE offset table
|
||||
dvlp_offset = file.tellg();
|
||||
file.seekg(dvlp_offset);
|
||||
file.read((char*)&dvlp_header, sizeof(dvlp_header));
|
||||
if (dvlp_header.magic_word != DVLPHeader::MAGIC_WORD) {
|
||||
std::stringstream stream;
|
||||
stream << "Wrong DVLP magic word at offset " << std::hex << dvlp_offset << ": Got " << std::hex << dvlp_header.magic_word;
|
||||
throw stream.str();
|
||||
}
|
||||
|
||||
for (int i = 0; i < dvlb_header.num_programs; ++i) {
|
||||
auto& dvle_header = dvle_headers[i];
|
||||
file.seekg(dvle_offsets[i]);
|
||||
file.read((char*)&dvle_header, sizeof(dvle_header));
|
||||
if (dvle_header.magic_word != DVLEHeader::MAGIC_WORD) {
|
||||
std::stringstream stream;
|
||||
stream << "Wrong DVLE header in DVLE #" << i << ": " << std::hex << dvle_header.magic_word;
|
||||
throw stream.str();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Is there indeed exactly one filename per DVLE?
|
||||
dvle_filenames.resize(dvlb_header.num_programs);
|
||||
uint32_t offset = dvlp_offset + dvlp_header.filename_symbol_offset;
|
||||
for (int i = 0; i < dvlb_header.num_programs; ++i) {
|
||||
auto& filename = dvle_filenames[i];
|
||||
filename = ReadSymbol(offset);
|
||||
offset += filename.length() + 1;
|
||||
}
|
||||
|
||||
// Read shader binary code
|
||||
shader_info.code.resize(dvlp_header.binary_size_words);
|
||||
file.seekg(dvlp_offset + dvlp_header.binary_offset);
|
||||
file.read((char*)shader_info.code.data(), dvlp_header.binary_size_words * sizeof(Instruction));
|
||||
|
||||
// Read operand descriptor table
|
||||
shader_info.swizzle_info.resize(dvlp_header.swizzle_info_num_entries);
|
||||
file.seekg(dvlp_offset + dvlp_header.swizzle_info_offset);
|
||||
file.read((char*)shader_info.swizzle_info.data(), dvlp_header.swizzle_info_num_entries * sizeof(SwizzleInfo));
|
||||
}
|
||||
|
||||
void ShbinParser::ReadDVLE(int dvle_index) {
|
||||
// TODO: Check if we have called ReadHeaders() before!
|
||||
|
||||
if (dvle_index >= dvlb_header.num_programs) {
|
||||
std::stringstream stream;
|
||||
stream << "Invalid DVLE index " << dvle_index << "given";
|
||||
throw stream.str();
|
||||
}
|
||||
|
||||
auto& dvle_header = dvle_headers[dvle_index];
|
||||
auto& dvle_offset = dvle_offsets[dvle_index];
|
||||
|
||||
uint32_t symbol_table_offset = dvle_offset + dvle_header.symbol_table_offset;
|
||||
|
||||
shader_info.constant_table.resize(dvle_header.constant_table_size);
|
||||
uint32_t constant_table_offset = dvle_offset + dvle_header.constant_table_offset;
|
||||
file.seekg(constant_table_offset);
|
||||
for (int i = 0; i < dvle_header.constant_table_size; ++i)
|
||||
file.read((char*)&shader_info.constant_table[i], sizeof(ConstantInfo));
|
||||
|
||||
shader_info.label_table.resize(dvle_header.label_table_size);
|
||||
uint32_t label_table_offset = dvle_offset + dvle_header.label_table_offset;
|
||||
file.seekg(label_table_offset);
|
||||
for (int i = 0; i < dvle_header.label_table_size; ++i)
|
||||
file.read((char*)&shader_info.label_table[i], sizeof(LabelInfo));
|
||||
for (const auto& label_info : shader_info.label_table)
|
||||
shader_info.labels.insert({label_info.program_offset, ReadSymbol(symbol_table_offset + label_info.name_offset)});
|
||||
|
||||
shader_info.output_register_info.resize(dvle_header.output_register_table_size);
|
||||
file.seekg(dvle_offset + dvle_header.output_register_table_offset);
|
||||
for (auto& info : shader_info.output_register_info)
|
||||
file.read((char*)&info, sizeof(OutputRegisterInfo));
|
||||
|
||||
shader_info.uniform_table.resize(dvle_header.uniform_table_size);
|
||||
uint32_t uniform_table_offset = dvle_offset + dvle_header.uniform_table_offset;
|
||||
file.seekg(uniform_table_offset);
|
||||
for (int i = 0; i < dvle_header.uniform_table_size; ++i)
|
||||
file.read((char*)&shader_info.uniform_table[i].basic, sizeof(shader_info.uniform_table[i].basic));
|
||||
for (auto& uniform_info : shader_info.uniform_table)
|
||||
uniform_info.name = ReadSymbol(symbol_table_offset + uniform_info.basic.symbol_offset);
|
||||
|
||||
main_offset = dvlp_offset + dvlp_header.binary_offset;
|
||||
}
|
||||
|
||||
std::string ShbinParser::ReadSymbol(uint32_t offset) {
|
||||
std::string name;
|
||||
file.seekg(offset);
|
||||
std::getline(file, name, '\0');
|
||||
return name;
|
||||
};
|
||||
100
externals/nihstro/src/preprocessor.cpp
vendored
Normal file
100
externals/nihstro/src/preprocessor.cpp
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright 2015 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <nihstro/parser_assembly_private.h>
|
||||
#include <nihstro/preprocessor.h>
|
||||
#include <nihstro/source_tree.h>
|
||||
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace nihstro {
|
||||
|
||||
template<typename Iterator>
|
||||
struct IncludeParser : qi::grammar<Iterator, std::string(), AssemblySkipper<Iterator>> {
|
||||
using Skipper = AssemblySkipper<Iterator>;
|
||||
|
||||
IncludeParser() : IncludeParser::base_type(include) {
|
||||
include = qi::lexeme[qi::lit(".include") >> &qi::ascii::space]
|
||||
> qi::lexeme[qi::lit("\"") > +qi::char_("a-zA-Z0-9./_\\-") > qi::lit("\"")]
|
||||
> qi::omit[qi::eol | qi::eoi];
|
||||
}
|
||||
|
||||
qi::rule<Iterator, std::string(), Skipper> include;
|
||||
};
|
||||
|
||||
|
||||
SourceTree PreprocessAssemblyFile(const std::string& filename) {
|
||||
SourceTree tree;
|
||||
tree.file_info.filename = filename;
|
||||
|
||||
std::ifstream input_file(filename);
|
||||
if (!input_file) {
|
||||
throw std::runtime_error("Could not open input file " + filename);
|
||||
}
|
||||
|
||||
std::string prefix;
|
||||
{
|
||||
auto last_slash = filename.find_last_of("/");
|
||||
if (last_slash != std::string::npos)
|
||||
prefix = filename.substr(0, last_slash + 1);
|
||||
}
|
||||
|
||||
input_file.seekg(0, std::ios::end);
|
||||
tree.code.resize(input_file.tellg());
|
||||
|
||||
input_file.seekg(0, std::ios::beg);
|
||||
input_file.read(&tree.code[0], tree.code.size());
|
||||
input_file.close();
|
||||
|
||||
auto cursor = tree.code.begin();
|
||||
|
||||
IncludeParser<decltype(cursor)> include_parser;
|
||||
AssemblySkipper<decltype(cursor)> skipper;
|
||||
|
||||
while (cursor != tree.code.end()) {
|
||||
std::string parsed_filename;
|
||||
auto cursor_prev = cursor;
|
||||
if (qi::phrase_parse(cursor, tree.code.end(), include_parser, skipper, parsed_filename)) {
|
||||
if (parsed_filename[0] == '/')
|
||||
throw std::runtime_error("Given filename must be relative to the path of the including file");
|
||||
|
||||
// TODO: Protect against circular inclusions
|
||||
auto newtree = PreprocessAssemblyFile(prefix + parsed_filename);
|
||||
tree.Attach(newtree, cursor_prev - tree.code.begin());
|
||||
cursor = tree.code.erase(cursor_prev, cursor);
|
||||
cursor = tree.code.insert(cursor, '\n');
|
||||
} else {
|
||||
// Skip this line
|
||||
qi::parse(cursor, tree.code.end(), *(qi::char_ - (qi::eol | qi::eoi)) >> (qi::eol | qi::eoi));
|
||||
}
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
393
externals/nihstro/src/tests/parser.cpp
vendored
Normal file
393
externals/nihstro/src/tests/parser.cpp
vendored
Normal file
@@ -0,0 +1,393 @@
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "nihstro/parser_assembly.h"
|
||||
#include "nihstro/parser_assembly_private.h"
|
||||
|
||||
#include <boost/optional/optional_io.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
|
||||
#define BOOST_TEST_MODULE Parser
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
// Implement some ostream<< operators for BOOST_CHECK*
|
||||
namespace std {
|
||||
|
||||
template<typename T>
|
||||
std::ostream& operator << (std::ostream& os, const std::vector<T>& vec) {
|
||||
auto it = vec.begin();
|
||||
os << "{";
|
||||
if (!vec.empty())
|
||||
os << " " << *it;
|
||||
while (it != vec.end() && ++it != vec.end()) {
|
||||
os << ", " << *it;
|
||||
}
|
||||
os << " }";
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator << (std::ostream& os, const nihstro::Expression& expr) {
|
||||
if (expr.signed_identifier.sign)
|
||||
os << *expr.signed_identifier.sign;
|
||||
os << expr.signed_identifier.identifier;
|
||||
if (expr.index)
|
||||
os << "[" << *expr.index << "]";
|
||||
for (auto& mask : expr.swizzle_masks)
|
||||
os << "." << mask;
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator << (std::ostream& os, const nihstro::IndexExpression& expr) {
|
||||
for (size_t i = 0; i < expr.GetCount(); ++i) {
|
||||
if (i != 0)
|
||||
os << ", ";
|
||||
|
||||
if (expr.IsRawIndex(i)) {
|
||||
os << expr.GetRawIndex(i);
|
||||
} else if (expr.IsAddressRegisterIdentifier(i)) {
|
||||
os << expr.GetAddressRegisterIdentifier(i);
|
||||
} else {
|
||||
os << "?";
|
||||
}
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator << (std::ostream& os, const nihstro::IntegerWithSign& num) {
|
||||
os << num.GetValue();
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator << (std::ostream& os, const nihstro::Instruction::FlowControlType::Op& op) {
|
||||
if (op == Instruction::FlowControlType::And)
|
||||
os << "&&";
|
||||
else if (op == Instruction::FlowControlType::Or)
|
||||
os << "||";
|
||||
else
|
||||
os << "??";
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator << (std::ostream& os, const nihstro::ConditionInput& inp) {
|
||||
if (inp.invert)
|
||||
os << "!";
|
||||
|
||||
os << inp.identifier;
|
||||
if (inp.swizzler_mask)
|
||||
os << "." << *inp.swizzler_mask;
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator << (std::ostream& os, const nihstro::Condition& cond) {
|
||||
os << "{ " << cond.input1;
|
||||
|
||||
if (cond.op != Instruction::FlowControlType::JustX)
|
||||
os << " " << cond.op << " " << cond.input2;
|
||||
|
||||
os << " }";
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace std
|
||||
|
||||
// Utility comparison operators
|
||||
namespace nihstro {
|
||||
bool operator == (const IntegerWithSign& a, const IntegerWithSign& b) {
|
||||
return a.sign == b.sign && a.value == b.value;
|
||||
}
|
||||
|
||||
bool operator == (const Expression& a, const Expression& b) {
|
||||
bool ret = true;
|
||||
ret &= a.signed_identifier.sign == b.signed_identifier.sign;
|
||||
ret &= a.signed_identifier.identifier == b.signed_identifier.identifier;
|
||||
ret &= a.index == b.index;
|
||||
ret &= a.swizzle_masks == b.swizzle_masks;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool operator == (const ConditionInput& a, const ConditionInput& b) {
|
||||
return a.invert == b.invert && a.identifier == b.identifier && a.swizzler_mask == b.swizzler_mask;
|
||||
}
|
||||
|
||||
bool operator == (const Condition& a, const Condition& b) {
|
||||
return a.input1 == b.input1 && a.input2 == b.input2 && a.op == b.op;
|
||||
}
|
||||
|
||||
} // namespace nihstro
|
||||
|
||||
// Utility function to parse the given input. Upon match, returns the parsed contents, and returns no value upon failure.
|
||||
template<typename Attr, typename Parser, typename Skipper>
|
||||
static boost::optional<Attr> parse(const std::string& input, const Parser& parser, const Skipper& skipper) {
|
||||
BOOST_TEST_MESSAGE(("Parsing \"" + input + "\"").c_str());
|
||||
Attr attr;
|
||||
SourceTree inp;
|
||||
inp.code = input;
|
||||
if (boost::spirit::qi::phrase_parse(inp.begin(), inp.end(), parser, skipper, attr))
|
||||
return attr;
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
// Utility function to check that the given vectors are equal.
|
||||
template<typename T>
|
||||
static void CheckVector(const std::vector<T>& vec, const std::vector<T>& exp) {
|
||||
// TODO: Could just check directly for equality, but need an ostream<< operator for that.
|
||||
BOOST_CHECK_EQUAL(vec, exp);
|
||||
}
|
||||
|
||||
// Utility function to convert a compile-time character to the corresponding swizzle mask component
|
||||
template<char a>
|
||||
static InputSwizzlerMask::Component MakeSwizzlerMaskComponent() {
|
||||
static_assert(a == 'x' || a == 'y' || a == 'z' || a == 'w', "Invalid component");
|
||||
if (a == 'x') return InputSwizzlerMask::x;
|
||||
else if (a == 'y') return InputSwizzlerMask::y;
|
||||
else if (a == 'z') return InputSwizzlerMask::z;
|
||||
else return InputSwizzlerMask::w;
|
||||
}
|
||||
|
||||
// Utility function to convert a series of up to four characters to the corresponding swizzle mask
|
||||
template<char a>
|
||||
static InputSwizzlerMask MakeInputSwizzlerMask() {
|
||||
return { 1, { MakeSwizzlerMaskComponent<a>() } };
|
||||
}
|
||||
template<char a, char b>
|
||||
static InputSwizzlerMask MakeInputSwizzlerMask() {
|
||||
return { 2, { MakeSwizzlerMaskComponent<a>(), MakeSwizzlerMaskComponent<b>() } };
|
||||
}
|
||||
template<char a, char b, char c>
|
||||
static InputSwizzlerMask MakeInputSwizzlerMask() {
|
||||
return { 3, { MakeSwizzlerMaskComponent<a>(), MakeSwizzlerMaskComponent<b>(), MakeSwizzlerMaskComponent<c>() } };
|
||||
}
|
||||
template<char a, char b, char c, char d>
|
||||
static InputSwizzlerMask MakeInputSwizzlerMask() {
|
||||
return { 4, { MakeSwizzlerMaskComponent<a>(), MakeSwizzlerMaskComponent<b>(), MakeSwizzlerMaskComponent<c>(), MakeSwizzlerMaskComponent<d>() } };
|
||||
}
|
||||
|
||||
// Utility function to check that parsing of the first declaration statement was successfull and that the result matches the second declaration statements.
|
||||
static void CheckDeclaration(const boost::optional<StatementDeclaration>& declaration, const StatementDeclaration& exp) {
|
||||
if (!declaration) {
|
||||
BOOST_CHECK(false);
|
||||
} else {
|
||||
BOOST_CHECK_EQUAL(declaration->alias_name, exp.alias_name);
|
||||
BOOST_CHECK_EQUAL(declaration->identifier_start, exp.identifier_start);
|
||||
BOOST_CHECK_EQUAL(declaration->identifier_end, exp.identifier_end);
|
||||
BOOST_CHECK_EQUAL(declaration->swizzle_mask, exp.swizzle_mask);
|
||||
CheckVector(declaration->extra.constant_value, exp.extra.constant_value);
|
||||
BOOST_CHECK_EQUAL(declaration->extra.output_semantic, exp.extra.output_semantic);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(declaration) {
|
||||
ParserContext context;
|
||||
DeclarationParser<ParserIterator> declaration_parser(context);
|
||||
AssemblySkipper<ParserIterator> skipper;
|
||||
|
||||
{
|
||||
// Plain alias
|
||||
auto declaration = parse<StatementDeclaration>(".alias my_alias r5.xy", declaration_parser, skipper);
|
||||
CheckDeclaration(declaration, { "my_alias", "r5", {}, MakeInputSwizzlerMask<'x', 'y'>() });
|
||||
}
|
||||
|
||||
{
|
||||
// Array alias
|
||||
auto declaration = parse<StatementDeclaration>(".alias my_alias r5-r10", declaration_parser, skipper);
|
||||
CheckDeclaration(declaration, { "my_alias", "r5", std::string("r10") });
|
||||
}
|
||||
|
||||
{
|
||||
// Output alias
|
||||
auto declaration = parse<StatementDeclaration>(".alias my_alias o5.xyz as texcoord0", declaration_parser, skipper);
|
||||
CheckDeclaration(declaration, { "my_alias", "o5", {}, MakeInputSwizzlerMask<'x', 'y', 'z'>(), { {}, OutputRegisterInfo::TEXCOORD0 } });
|
||||
}
|
||||
|
||||
{
|
||||
// Output alias without output semantic
|
||||
BOOST_CHECK_THROW(parse<StatementDeclaration>(".alias my_alias o5.xyz", declaration_parser, skipper),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
{
|
||||
// Constant alias
|
||||
auto declaration = parse<StatementDeclaration>(".alias my_alias c5.xy as (1.0, -2.4)", declaration_parser, skipper);
|
||||
CheckDeclaration(declaration, { "my_alias", "c5", {}, MakeInputSwizzlerMask<'x', 'y'>(), { { 1.0, -2.4 } } });
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(label) {
|
||||
ParserContext context;
|
||||
LabelParser<ParserIterator> label_parser(context);
|
||||
AssemblySkipper<ParserIterator> skipper;
|
||||
|
||||
{
|
||||
auto label = parse<StatementLabel>("my_label:", label_parser, skipper);
|
||||
BOOST_CHECK(label);
|
||||
BOOST_CHECK_EQUAL(*label, "my_label");
|
||||
}
|
||||
}
|
||||
|
||||
static void CheckParsedArithmeticInstruction(const boost::optional<FloatOpInstruction>& result, const FloatOpInstruction& exp) {
|
||||
if (!result) {
|
||||
BOOST_CHECK(false);
|
||||
} else {
|
||||
BOOST_CHECK_EQUAL(result->opcode, exp.opcode);
|
||||
CheckVector(result->expressions, exp.expressions);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(arithmetic) {
|
||||
ParserContext context;
|
||||
|
||||
FloatOpParser<ParserIterator> arithmetic_parser(context);
|
||||
AssemblySkipper<ParserIterator> skipper;
|
||||
|
||||
{
|
||||
// one-argument instruction
|
||||
auto result = parse<FloatOpInstruction>("mova r2.xy", arithmetic_parser, skipper);
|
||||
Expression r2_xy = { { {}, "r2" }, {}, { MakeInputSwizzlerMask<'x', 'y'>() } };
|
||||
CheckParsedArithmeticInstruction(result, { nihstro::OpCode::Id::MOVA, { r2_xy } });
|
||||
}
|
||||
|
||||
{
|
||||
// two-argument instruction
|
||||
auto result = parse<FloatOpInstruction>("mov o0.wz, r2.xy", arithmetic_parser, skipper);
|
||||
Expression o0_wz = { { {}, "o0" }, {}, { MakeInputSwizzlerMask<'w', 'z'>() } };
|
||||
Expression r2_xy = { { {}, "r2" }, {}, { MakeInputSwizzlerMask<'x', 'y'>() } };
|
||||
CheckParsedArithmeticInstruction(result, { nihstro::OpCode::Id::MOV, { o0_wz, r2_xy } });
|
||||
}
|
||||
|
||||
{
|
||||
// two-argument instruction with trivial register index
|
||||
auto result = parse<FloatOpInstruction>("mov o0.wz, r2[5].xy", arithmetic_parser, skipper);
|
||||
IndexExpression index_expr;
|
||||
index_expr.emplace_back(IntegerWithSign{ +1, 5 });
|
||||
Expression o0_wz = { { {}, "o0" }, {}, { MakeInputSwizzlerMask<'w', 'z'>() } };
|
||||
Expression r2_xy = { { {}, "r2" }, index_expr, { MakeInputSwizzlerMask<'x', 'y'>() } };
|
||||
CheckParsedArithmeticInstruction(result, { nihstro::OpCode::Id::MOV, { o0_wz, r2_xy } });
|
||||
}
|
||||
|
||||
{
|
||||
// two-argument instruction with nontrivial register index
|
||||
auto result = parse<FloatOpInstruction>("mov o0.wz, r2[5+a1-4].xy", arithmetic_parser, skipper);
|
||||
IndexExpression index_expr;
|
||||
index_expr.emplace_back(IntegerWithSign{ +1, 5 });
|
||||
index_expr.emplace_back("a1");
|
||||
index_expr.emplace_back(IntegerWithSign{ -1, 4 });
|
||||
Expression o0_wz = { { {}, "o0" }, {}, { MakeInputSwizzlerMask<'w', 'z'>() } };
|
||||
Expression r2_xy = { { {}, "r2" }, index_expr, { MakeInputSwizzlerMask<'x', 'y'>() } };
|
||||
CheckParsedArithmeticInstruction(result, { nihstro::OpCode::Id::MOV, { o0_wz, r2_xy } });
|
||||
}
|
||||
|
||||
{
|
||||
// three-argument instruction
|
||||
auto result = parse<FloatOpInstruction>("add o0.xy, r2.xy, r3.xy", arithmetic_parser, skipper);
|
||||
Expression o0_xy = { { {}, "o0" }, {}, { MakeInputSwizzlerMask<'x', 'y'>() } };
|
||||
Expression r2_xy = { { {}, "r2" }, {}, { MakeInputSwizzlerMask<'x', 'y'>() } };
|
||||
Expression r3_xy = { { {}, "r3" }, {}, { MakeInputSwizzlerMask<'x', 'y'>() } };
|
||||
CheckParsedArithmeticInstruction(result, { nihstro::OpCode::Id::ADD, { o0_xy, r2_xy, r3_xy } });
|
||||
}
|
||||
|
||||
{
|
||||
// four-argument instruction
|
||||
auto result = parse<FloatOpInstruction>("mad o0.xy, r2.xy, r3.xy, v4.xy", arithmetic_parser, skipper);
|
||||
Expression o0_xy = { { {}, "o0" }, {}, { MakeInputSwizzlerMask<'x', 'y'>() } };
|
||||
Expression r2_xy = { { {}, "r2" }, {}, { MakeInputSwizzlerMask<'x', 'y'>() } };
|
||||
Expression r3_xy = { { {}, "r3" }, {}, { MakeInputSwizzlerMask<'x', 'y'>() } };
|
||||
Expression v4_xy = { { {}, "v4" }, {}, { MakeInputSwizzlerMask<'x', 'y'>() } };
|
||||
CheckParsedArithmeticInstruction(result, { nihstro::OpCode::Id::MAD, { o0_xy, r2_xy, r3_xy, v4_xy } });
|
||||
}
|
||||
}
|
||||
|
||||
static void CheckParsedFlowControlInstruction(const boost::optional<FlowControlInstruction>& result, const FlowControlInstruction& exp) {
|
||||
if (!result) {
|
||||
BOOST_CHECK(false);
|
||||
} else {
|
||||
BOOST_CHECK_EQUAL(result->opcode, exp.opcode);
|
||||
BOOST_CHECK_EQUAL(result->target_label, exp.target_label);
|
||||
BOOST_CHECK_EQUAL(result->return_label, exp.return_label);
|
||||
BOOST_CHECK_EQUAL(result->condition, exp.condition);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(flowcontrol) {
|
||||
ParserContext context;
|
||||
FlowControlParser<ParserIterator> parser(context);
|
||||
AssemblySkipper<ParserIterator> skipper;
|
||||
|
||||
{
|
||||
// If-conditionals with two arguments
|
||||
auto result = parse<FlowControlInstruction>("if cc.x && !cc.y", parser, skipper);
|
||||
Condition cond = { { false, "cc", MakeInputSwizzlerMask<'x'>() }, Instruction::FlowControlType::And, { true, "cc", MakeInputSwizzlerMask<'y'>() } };
|
||||
CheckParsedFlowControlInstruction(result, { nihstro::OpCode::Id::GEN_IF, "__dummy", {}, cond } );
|
||||
}
|
||||
|
||||
{
|
||||
// If-conditionals with one argument with one component
|
||||
auto result = parse<FlowControlInstruction>("if cc.x", parser, skipper);
|
||||
Condition cond = { { false, "cc", MakeInputSwizzlerMask<'x'>() }, Instruction::FlowControlType::JustX };
|
||||
CheckParsedFlowControlInstruction(result, { nihstro::OpCode::Id::GEN_IF, "__dummy", {}, cond } );
|
||||
}
|
||||
|
||||
{
|
||||
// If-conditionals with one argument with two components
|
||||
auto result = parse<FlowControlInstruction>("if !cc.xy", parser, skipper);
|
||||
Condition cond = { { true, "cc", MakeInputSwizzlerMask<'x', 'y'>() }, Instruction::FlowControlType::JustX };
|
||||
CheckParsedFlowControlInstruction(result, { nihstro::OpCode::Id::GEN_IF, "__dummy", {}, cond });
|
||||
}
|
||||
|
||||
{
|
||||
// Loop instruction
|
||||
auto result = parse<FlowControlInstruction>("loop i3.y", parser, skipper);
|
||||
Condition cond = { { false, "i3", MakeInputSwizzlerMask<'y'>() }, Instruction::FlowControlType::JustX };
|
||||
CheckParsedFlowControlInstruction(result, { OpCode::Id::LOOP, "__dummy", {}, cond });
|
||||
}
|
||||
}
|
||||
|
||||
static void CheckParsedCompareInstruction(const boost::optional<CompareInstruction>& result, const CompareInstruction& exp) {
|
||||
if (!result) {
|
||||
BOOST_CHECK(false);
|
||||
} else {
|
||||
BOOST_CHECK_EQUAL(result->opcode, exp.opcode);
|
||||
CheckVector(result->arguments, exp.arguments);
|
||||
CheckVector(result->ops, exp.ops);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(compare) {
|
||||
ParserContext context;
|
||||
CompareParser<ParserIterator> parser(context);
|
||||
AssemblySkipper<ParserIterator> skipper;
|
||||
|
||||
{
|
||||
// Two separate comparisons
|
||||
auto result = parse<CompareInstruction>("cmp r5.xy, r2.zx, ==, <=", parser, skipper);
|
||||
Expression r5_xy = { { {}, "r5" }, {}, { MakeInputSwizzlerMask<'x', 'y'>() } };
|
||||
Expression r2_zx = { { {}, "r2" }, {}, { MakeInputSwizzlerMask<'z', 'x'>() } };
|
||||
CheckParsedCompareInstruction(result, { OpCode::Id::CMP, { r5_xy, r2_zx }, { Instruction::Common::CompareOpType::Equal, Instruction::Common::CompareOpType::LessEqual } });
|
||||
}
|
||||
}
|
||||
169
externals/nihstro/src/tests/source_tree_iterator.cpp
vendored
Normal file
169
externals/nihstro/src/tests/source_tree_iterator.cpp
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
// Copyright 2015 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <iostream>
|
||||
#include "nihstro/source_tree.h"
|
||||
|
||||
#define BOOST_TEST_MODULE SourceTreeIterator
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <iterator>
|
||||
|
||||
namespace std {
|
||||
|
||||
std::ostream& operator << (std::ostream& os, const nihstro::SourceTree& tree) {
|
||||
std::string::const_iterator it = tree.code.cbegin();
|
||||
for (auto& child : tree.children) {
|
||||
os << "\"";
|
||||
os << std::string(it, tree.code.cbegin() + child.offset_within_parent);
|
||||
os << "\"";
|
||||
os << " { ";
|
||||
os << child.tree;
|
||||
os << " } ";
|
||||
it = tree.code.cbegin() + child.offset_within_parent;
|
||||
}
|
||||
os << "\"" << std::string(it, tree.code.end()) << "\"";
|
||||
return os;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Utility function to manually flatten the given tree into a string
|
||||
static std::string FlattenTree(const nihstro::SourceTree& tree) {
|
||||
std::string ret;
|
||||
std::string::const_iterator it = tree.code.cbegin();
|
||||
for (auto& child : tree.children) {
|
||||
ret += std::string(it, tree.code.cbegin() + child.offset_within_parent);
|
||||
ret += FlattenTree(child.tree);
|
||||
it = tree.code.cbegin() + child.offset_within_parent;
|
||||
}
|
||||
ret += std::string(it, tree.code.end());
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Utility function to manually determine the size of the given tree
|
||||
static std::string::size_type TreeSize(const nihstro::SourceTree& tree) {
|
||||
std::string::size_type ret = 0;
|
||||
for (auto& child : tree.children) {
|
||||
ret += TreeSize(child.tree);
|
||||
}
|
||||
ret += tree.code.length();
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define CHECK_TREE(tree) do { \
|
||||
/* Check length */ \
|
||||
BOOST_CHECK_EQUAL(tree.end() - tree.begin(), TreeSize(tree)); \
|
||||
BOOST_CHECK_EQUAL(std::distance(tree.begin(), tree.end()), TreeSize(tree)); \
|
||||
/* Check forward iteration */ \
|
||||
std::string flattened_tree; \
|
||||
for (auto& val : tree) \
|
||||
flattened_tree += val; \
|
||||
auto reference_flattened_tree = FlattenTree(tree); \
|
||||
BOOST_CHECK_EQUAL(flattened_tree, reference_flattened_tree); \
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(flattened_tree.begin(), flattened_tree.end(), \
|
||||
reference_flattened_tree.begin(), reference_flattened_tree.end()); \
|
||||
\
|
||||
/* Check reverse iteration */ \
|
||||
flattened_tree.clear(); \
|
||||
for (auto it = tree.end() - 1;; it -= 1) { \
|
||||
flattened_tree += *it; \
|
||||
if (it == tree.begin()) \
|
||||
break; \
|
||||
} \
|
||||
std::reverse(reference_flattened_tree.begin(), reference_flattened_tree.end()); \
|
||||
BOOST_CHECK_EQUAL(flattened_tree, reference_flattened_tree); \
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(flattened_tree.begin(), flattened_tree.end(), \
|
||||
reference_flattened_tree.begin(), reference_flattened_tree.end()); \
|
||||
\
|
||||
} while (false)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(simple_tree) {
|
||||
nihstro::SourceTree tree;
|
||||
|
||||
tree.code = "a b c";
|
||||
|
||||
CHECK_TREE(tree);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(nested_tree) {
|
||||
nihstro::SourceTree tree;
|
||||
nihstro::SourceTree child1;
|
||||
nihstro::SourceTree child2;
|
||||
|
||||
tree.code = "aXbXc";
|
||||
child1.code = "child1";
|
||||
child2.code = "child2";
|
||||
tree.Attach(child1, 1).Attach(child2, 3);
|
||||
|
||||
CHECK_TREE(tree);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(deep_tree) {
|
||||
nihstro::SourceTree tree;
|
||||
nihstro::SourceTree child1;
|
||||
nihstro::SourceTree child1_child1;
|
||||
nihstro::SourceTree child1_child2;
|
||||
nihstro::SourceTree child1_child2_child1;
|
||||
nihstro::SourceTree child1_child3;
|
||||
nihstro::SourceTree child2;
|
||||
nihstro::SourceTree child3;
|
||||
nihstro::SourceTree child3_child1;
|
||||
nihstro::SourceTree child4;
|
||||
|
||||
tree.code = "aaaXaaaXaaaXaaaXaaa";
|
||||
child1.code = "FirstChild:bbbXbbbXbbbXbbb\n";
|
||||
child1_child1.code = "FirstSubchildOfChild1:ccc";
|
||||
child1_child2.code = "SecondSubchildOfChild1:dddXddd";
|
||||
child1_child2_child1.code = "FirstSubsubchildOfSubchild2OfChild1:eee";
|
||||
child1_child3.code = "ThirdSubchildOfChild1:fff";
|
||||
child2.code = "SecondChild:ggg\n";
|
||||
child3.code = "ThirdChild:hhhXhhh\n";
|
||||
child3_child1.code = "FirstSubchildOfChild3:iii";
|
||||
child4.code = "FourthChild:jjj\n";
|
||||
|
||||
child1_child2.Attach(child1_child2_child1, 26);
|
||||
child1.Attach(child1_child1, 14).Attach(child1_child2, 18).Attach(child1_child3, 22);
|
||||
child3.Attach(child3_child1, 14);
|
||||
tree.Attach(child1, 3).Attach(child2, 7).Attach(child3, 11).Attach(child4, 15);
|
||||
|
||||
CHECK_TREE(tree);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(subtree_at_begin_and_end) {
|
||||
nihstro::SourceTree tree;
|
||||
nihstro::SourceTree child1;
|
||||
tree.code = "aaa";
|
||||
child1.code = "bbb";
|
||||
|
||||
tree.Attach(child1, 0);
|
||||
CHECK_TREE(tree);
|
||||
|
||||
tree.children.clear();
|
||||
tree.Attach(child1, tree.code.length());
|
||||
CHECK_TREE(tree);
|
||||
}
|
||||
Reference in New Issue
Block a user