144 lines
3.3 KiB
C++
144 lines
3.3 KiB
C++
|
|
#include "compile.hpp"
|
|
#include <filesystem>
|
|
#include <format>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <GL/glew.h>
|
|
|
|
static constexpr std::string KEYWORD = "#include";
|
|
|
|
enum include_mode_t {
|
|
WHITESPACE,
|
|
READING,
|
|
ENDING,
|
|
};
|
|
|
|
static bool parse(std::vector<char>& source, const std::filesystem::path& path) {
|
|
std::ifstream file(path);
|
|
std::vector<char> line;
|
|
|
|
int n_line = 1;
|
|
int n_col = 0;
|
|
int keyword_at = 0;
|
|
include_mode_t mode = include_mode_t::WHITESPACE;
|
|
|
|
if(!file.is_open()) {
|
|
return false;
|
|
}
|
|
|
|
for(auto it = std::istreambuf_iterator<char>(file); it != std::istreambuf_iterator<char>(); it++) {
|
|
char c = *it;
|
|
|
|
if(c == '\0') {
|
|
break;
|
|
}
|
|
if(c == '\n') {
|
|
n_line++;
|
|
n_col = 0;
|
|
} else {
|
|
n_col++;
|
|
}
|
|
|
|
if(keyword_at < KEYWORD.size()) {
|
|
if(keyword_at == n_col - 1 && KEYWORD[keyword_at] == c) {
|
|
keyword_at++;
|
|
continue;
|
|
}
|
|
else if(keyword_at > 0) {
|
|
source.insert(source.end(), &KEYWORD[0], &KEYWORD[keyword_at]);
|
|
keyword_at = 0;
|
|
}
|
|
source.push_back(c);
|
|
continue;
|
|
}
|
|
|
|
switch(mode) {
|
|
case include_mode_t::WHITESPACE: {
|
|
if(c != ' ' && c != '\t' && c != '\r' && c != '"') {
|
|
throw std::runtime_error(std::format("At {}:{}: Syntax error", n_line, n_col));
|
|
}
|
|
if(c == '"') {
|
|
mode = include_mode_t::READING;
|
|
}
|
|
break;
|
|
}
|
|
case include_mode_t::READING: {
|
|
if(c != '"') {
|
|
line.push_back(c);
|
|
break;
|
|
}
|
|
mode = include_mode_t::ENDING;
|
|
std::filesystem::path path_next = path.parent_path().append(line.begin(), line.end());
|
|
std::string comment = std::format("//'{}' {}\n", path_next.string(), '{');
|
|
source.insert(source.end(), comment.begin(), comment.end());
|
|
if(!parse(source, path_next)) {
|
|
throw std::runtime_error(std::format("At {}:{} in '{}': File '{}' not found", n_line, n_col, path.string(), path_next.string()));
|
|
}
|
|
comment = "//}\n";
|
|
source.insert(source.end(), comment.begin(), comment.end());
|
|
line.clear();
|
|
break;
|
|
}
|
|
case include_mode_t::ENDING: {
|
|
if(c == '\r') {
|
|
break;
|
|
}
|
|
if(c != '\n') {
|
|
throw std::runtime_error(std::format("At {}:{} in '{}': Syntax error", n_line, n_col, path.string()));
|
|
}
|
|
mode = include_mode_t::WHITESPACE;
|
|
keyword_at = 0;
|
|
break;
|
|
}}
|
|
}
|
|
|
|
if(keyword_at > 0) {
|
|
source.insert(source.end(), &KEYWORD[0], &KEYWORD[keyword_at]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Graphics::Shader::compile(unsigned int shader, const std::filesystem::path& path) {
|
|
std::vector<char> source;
|
|
|
|
if(!parse(source, path)) {
|
|
throw std::runtime_error(std::format("File '{}' not found", path.string()));
|
|
}
|
|
|
|
const char* data = source.data();
|
|
int data_len = source.size();
|
|
glShaderSource(shader, 1, &data, &data_len);
|
|
glCompileShader(shader);
|
|
|
|
int success;
|
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
|
|
|
if(!success) {
|
|
char info_log[1024];
|
|
glGetShaderInfoLog(shader, sizeof(info_log), nullptr, info_log);
|
|
|
|
std::ostringstream source_processed;
|
|
int n_line = 1;
|
|
|
|
source_processed << n_line << '\t';
|
|
|
|
for(char c : source) {
|
|
if(c == '\n') {
|
|
source_processed << '\n' << ++n_line << '\t';
|
|
} else {
|
|
source_processed << c;
|
|
}
|
|
}
|
|
|
|
throw std::runtime_error(std::format("OpenGL shader compilation error\n{}\nIn expanded source file '{}'\n{}", source_processed.str(), path.string(), info_log));
|
|
}
|
|
}
|
|
|