Also add a new pragma to specify source names.
{
tokenizer.signal_preprocess.connect(sigc::mem_fun(&preprocessor, &Preprocessor::preprocess));
preprocessor.signal_version.connect(sigc::mem_fun(this, &Parser::set_required_version));
+ preprocessor.signal_source.connect(sigc::mem_fun(this, &Parser::source_reference));
preprocessor.signal_stage_change.connect(sigc::mem_fun(this, &Parser::stage_change));
+ preprocessor.signal_line.connect(sigc::mem_fun(this, &Parser::line_change));
}
Parser::~Parser()
delete module;
module = new Module;
cur_stage = &module->shared;
+ base_index = index;
source_index = index;
- module->source_map.set_name(source_index, name);
+ source_reference(1, name);
tokenizer.begin(name, source);
while(RefPtr<Statement> statement = parse_global_declaration())
cur_stage->content.body.push_back(statement);
cur_stage->required_version = ver;
}
+void Parser::source_reference(unsigned index, const string &name)
+{
+ if(index<1)
+ throw invalid_shader_source(tokenizer.get_location(), "Invalid source reference");
+
+ module->source_map.set_name(base_index+index-1, name);
+}
+
void Parser::stage_change(Stage::Type stage)
{
if(!allow_stage_change)
cur_stage = &module->stages.back();
}
+void Parser::line_change(int index, unsigned line)
+{
+ if(index>0)
+ source_index = base_index+index-1;
+ else if(index==0)
+ source_index = 0;
+ else
+ index = source_index;
+
+ string name = module->source_map.get_name(index);
+ if(name.empty())
+ name = format("<%d>", index);
+ tokenizer.set_location(Location(name, line));
+}
+
string Parser::expect_type()
{
string token = tokenizer.parse_token();
{
private:
std::string source;
+ unsigned base_index;
unsigned source_index;
Tokenizer tokenizer;
Preprocessor preprocessor;
private:
void parse_source(const std::string &, unsigned);
void set_required_version(const Version &);
+ void source_reference(unsigned, const std::string &);
void stage_change(Stage::Type);
+ void line_change(int, unsigned);
std::string expect_type();
std::string expect_identifier();
+#include <msp/strings/utils.h>
#include "glsl_error.h"
#include "preprocessor.h"
#include "tokenizer.h"
preprocess_pragma();
else if(token=="version")
preprocess_version();
+ else if(token=="line")
+ preprocess_line();
else if(token=="define" || token=="undef" || token=="if" || token=="ifdef" || token=="ifndef" || token=="else" ||
- token=="elif" || token=="endif" || token=="error" || token=="extension" || token=="line")
+ token=="elif" || token=="endif" || token=="error" || token=="extension")
throw invalid_shader_source(tokenizer.get_location(), "Unsupported preprocessor directive '%s'", token);
else if(!token.empty())
throw parse_error(tokenizer.get_location(), token, "a preprocessor directive");
throw parse_error(tokenizer.get_location(), token, "end of line");
}
+void Preprocessor::preprocess_line()
+{
+ tokenizer.expect("line");
+ unsigned line_number = lexical_cast<unsigned>(tokenizer.parse_token());
+
+ int source_index = -1;
+ string token = tokenizer.parse_token();
+ if(!token.empty())
+ source_index = lexical_cast<unsigned>(token);
+
+ token = tokenizer.parse_token();
+ if(!token.empty())
+ throw parse_error(tokenizer.get_location(), token, "end of line");
+
+ signal_line.emit(source_index, line_number);
+}
+
void Preprocessor::preprocess_pragma()
{
tokenizer.expect("pragma");
string token = tokenizer.peek_token();
if(token=="stage")
preprocess_stage();
+ else if(token=="source")
+ preprocess_source();
else
throw invalid_shader_source(tokenizer.get_location(), "Unrecognized MSP pragma '%s'", token);
signal_stage_change.emit(stage);
}
+void Preprocessor::preprocess_source()
+{
+ tokenizer.expect("source");
+ tokenizer.expect("(");
+ unsigned source_index = lexical_cast<unsigned>(tokenizer.parse_token());
+ tokenizer.expect(",");
+ string filename = tokenizer.parse_token();
+ if(filename[0]!='"')
+ throw parse_error(tokenizer.get_location(), filename, "a string literal");
+ filename = c_unescape(filename.substr(1, filename.size()-2));
+ tokenizer.expect(")");
+
+ signal_source.emit(source_index, filename);
+}
+
} // namespace SL
} // namespace GL
} // namespace Msp
{
public:
sigc::signal<void, const Version &> signal_version;
+ sigc::signal<void, unsigned, const std::string &> signal_source;
sigc::signal<void, Stage::Type> signal_stage_change;
+ sigc::signal<void, int, unsigned> signal_line;
private:
Tokenizer &tokenizer;
void preprocess();
private:
void preprocess_version();
+ void preprocess_line();
void preprocess_pragma();
void preprocess_pragma_msp();
void preprocess_stage();
+ void preprocess_source();
};
} // namespace SL
source_names[i] = n;
}
+const string &SourceMap::get_name(unsigned i) const
+{
+ i -= base_index;
+ if(i<source_names.size())
+ return source_names[i];
+
+ static string empty;
+ return empty;
+}
+
void SourceMap::merge_from(const SourceMap &other)
{
if(other.base_index<base_index)
void set_name(unsigned, const std::string &);
unsigned get_count() const { return base_index+source_names.size(); }
+ const std::string &get_name(unsigned) const;
void merge_from(const SourceMap &);
std::string translate_errors(const std::string &) const;
};
namespace SL {
Tokenizer::Tokenizer():
- allow_preprocess(true)
+ allow_preprocess(true),
+ suppress_line_advance(false)
{
static string empty;
iter = empty.begin();
source_end = empty.end();
- location.line = 0;
}
void Tokenizer::begin(const string &name, const string &src)
throw parse_error(location, parsed, format("'%s'", token));
}
+void Tokenizer::set_location(const Location &loc)
+{
+ location = loc;
+}
+
string Tokenizer::parse_token_()
{
while(1)
return parse_identifier();
else if(isdigit(*iter))
return parse_number();
- else if(*iter=='"' || *iter=='#' || *iter=='$' || *iter=='\'' || *iter=='@' || *iter=='\\' || *iter=='`')
+ else if(*iter=='"')
+ return parse_string();
+ else if(*iter=='#' || *iter=='$' || *iter=='\'' || *iter=='@' || *iter=='\\' || *iter=='`')
throw syntax_error(location, string(1, *iter), "Invalid character in source");
else
return parse_other();
return number;
}
+string Tokenizer::parse_string()
+{
+ string str(1, *iter++);
+ bool escape = false;
+ while(iter!=source_end)
+ {
+ char c = *iter++;
+ str += c;
+ if(c=='\\')
+ escape = true;
+ else if(c=='"' && !escape)
+ break;
+ else
+ escape = false;
+ }
+
+ return str;
+}
+
string Tokenizer::parse_other()
{
if(iter==source_end)
if(*iter=='\n')
{
- ++location.line;
+ if(!suppress_line_advance)
+ ++location.line;
allow_preprocess = (comment<3);
}
++iter;
}
+
+ suppress_line_advance = false;
}
} // namespace SL
{
std::string name;
unsigned line;
+
+ Location(): line(0) { }
+ Location(const std::string &n, unsigned l): name(n), line(l) { }
};
class Tokenizer
std::string::const_iterator source_end;
Location location;
bool allow_preprocess;
+ bool suppress_line_advance;
std::string last_token;
std::deque<std::string> next_tokens;
const std::string &peek_token(unsigned = 0);
const std::string &parse_token();
void expect(const std::string &);
+ void set_location(const Location &);
const Location &get_location() const { return location; }
private:
std::string parse_token_();
void preprocess();
std::string parse_identifier();
std::string parse_number();
+ std::string parse_string();
std::string parse_other();
void skip_comment_and_whitespace();
};