Currently the only thing validated is multiple definitions.
#include "debug.h"
#include "error.h"
#include "generate.h"
+#include "glsl_error.h"
#include "optimize.h"
#include "output.h"
#include "resources.h"
+#include "validate.h"
#undef interface
{
for(list<Stage>::iterator i=module->stages.begin(); i!=module->stages.end(); ++i)
generate(*i, mode);
+
+ bool valid = true;
+ for(list<Stage>::iterator i=module->stages.begin(); i!=module->stages.end(); ++i)
+ if(!validate(*i))
+ valid = false;
+ if(!valid)
+ throw invalid_shader_source(get_diagnostics());
+
unsigned n = 0;
for(list<Stage>::iterator i=module->stages.begin(); (i!=module->stages.end() && n<10000); ++n)
{
throw key_error(Stage::get_stage_name(stage_type));
}
+string Compiler::get_diagnostics() const
+{
+ string combined;
+ for(list<Stage>::const_iterator i=module->stages.begin(); i!=module->stages.end(); ++i)
+ for(vector<Diagnostic>::const_iterator j=i->diagnostics.begin(); j!=i->diagnostics.end(); ++j)
+ combined += format("%s:%d: %s\n", module->source_map.get_name(j->source), j->line, j->message);
+ return combined;
+}
+
void Compiler::append_module(Module &mod, DataFile::Collection *res)
{
module->source_map.merge_from(mod.source_map);
LegacyConverter().apply(stage, features);
}
+bool Compiler::validate(Stage &stage)
+{
+ DeclarationValidator().apply(stage);
+
+ for(vector<Diagnostic>::const_iterator i=stage.diagnostics.begin(); i!=stage.diagnostics.end(); ++i)
+ if(i->severity==Diagnostic::ERR)
+ return false;
+
+ return true;
+}
+
Compiler::OptimizeResult Compiler::optimize(Stage &stage)
{
ConstantConditionEliminator().apply(stage);
Intended for debugging purposes. */
std::string get_stage_debug(Stage::Type) const;
+ /** Returns diagnostics from compilation. The output is intended to be
+ viewed by humans. */
+ std::string get_diagnostics() const;
+
private:
/** Appends a module to the target, processing any imports found in it. */
void append_module(Module &, DataFile::Collection *);
variables. */
void generate(Stage &, Mode);
+ bool validate(Stage &);
+
/** Applies optimizations to a stage. The return value indicates which
stage should be optimized next. */
OptimizeResult optimize(Stage &);
virtual ~unsupported_shader() throw() { }
};
+struct Diagnostic
+{
+ enum Severity
+ {
+ INFO,
+ WARN,
+ ERR
+ };
+
+ Severity severity;
+ int source;
+ unsigned line;
+ std::string message;
+
+ Diagnostic(): severity(INFO), source(-2), line(0) { }
+};
+
} // namespace SL
} // namespace GL
} // namespace Msp
#include <vector>
#include <msp/core/refptr.h>
#include "features.h"
+#include "glsl_error.h"
#include "sourcemap.h"
#pragma push_macro("interface")
std::map<std::string, FunctionDeclaration *> functions;
std::map<std::string, unsigned> locations;
Features required_features;
+ std::vector<Diagnostic> diagnostics;
Stage(Type);
--- /dev/null
+#include <msp/core/raii.h>
+#include <msp/strings/format.h>
+#include "validate.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+namespace SL {
+
+Validator::Validator():
+ stage(0)
+{ }
+
+void Validator::diagnose(Statement *statement, Diagnostic::Severity severity, const string &message)
+{
+ Diagnostic diag;
+ diag.severity = severity;
+ if(statement)
+ {
+ diag.source = statement->source;
+ diag.line = statement->line;
+ }
+ diag.message = message;
+ stage->diagnostics.push_back(diag);
+}
+
+
+DeclarationValidator::DeclarationValidator():
+ anonymous_block(false)
+{ }
+
+Statement *DeclarationValidator::find_definition(const string &name)
+{
+ BlockDeclarationMap *decls = &declarations[current_block];
+ BlockDeclarationMap::const_iterator i = decls->find(name);
+ if(i==decls->end() && anonymous_block)
+ {
+ decls = &declarations[current_block->parent];
+ i = decls->find(name);
+ }
+ return (i!=decls->end() ? i->second : 0);
+}
+
+void DeclarationValidator::check_definition(const string &name, Statement &statement)
+{
+ if(Statement *previous = find_definition(name))
+ {
+ error(&statement, format("Multiple definition of '%s'", name));
+ diagnose(previous, Diagnostic::INFO, "Previous definition is here");
+ return;
+ }
+
+ declarations[current_block][name] = &statement;
+ if(anonymous_block)
+ declarations[current_block->parent][name] = &statement;
+}
+
+void DeclarationValidator::visit(VariableDeclaration &var)
+{
+ check_definition(var.name, var);
+ TraversingVisitor::visit(var);
+}
+
+void DeclarationValidator::visit(InterfaceBlock &iface)
+{
+ check_definition(iface.name, iface);
+ if(!iface.instance_name.empty())
+ check_definition(iface.instance_name, iface);
+ SetFlag set_anon(anonymous_block, iface.instance_name.empty());
+ TraversingVisitor::visit(iface);
+}
+
+void DeclarationValidator::visit(FunctionDeclaration &func)
+{
+ if(func.definition==&func)
+ check_definition(func.name, func);
+ TraversingVisitor::visit(func);
+}
+
+} // namespace SL
+} // namespace GL
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GL_SL_VALIDATE_H_
+#define MSP_GL_SL_VALIDATE_H_
+
+#include <string>
+#include <vector>
+#include "glsl_error.h"
+#include "visitor.h"
+
+namespace Msp {
+namespace GL {
+namespace SL {
+
+class Validator: protected TraversingVisitor
+{
+protected:
+ Stage *stage;
+
+ Validator();
+
+ void diagnose(Statement *, Diagnostic::Severity, const std::string &);
+ void error(Statement *s, const std::string &m) { diagnose(s, Diagnostic::ERR, m); }
+};
+
+class DeclarationValidator: private Validator
+{
+private:
+ typedef std::map<std::string, Statement *> BlockDeclarationMap;
+
+ std::map<Block *, BlockDeclarationMap> declarations;
+ bool anonymous_block;
+
+public:
+ DeclarationValidator();
+
+ void apply(Stage &s) { stage = &s; s.content.visit(*this); }
+
+private:
+ Statement *find_definition(const std::string &);
+ void check_definition(const std::string &, Statement &);
+
+ virtual void visit(VariableDeclaration &);
+ virtual void visit(InterfaceBlock &);
+ virtual void visit(FunctionDeclaration &);
+};
+
+} // namespace SL
+} // namespace GL
+} // namespace Msp
+
+#endif