mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-29 08:26:37 +01:00
GL32 shaders and errors
- Update shaders to glsl 150 - Objectfy errors
This commit is contained in:
parent
5ce960c5d5
commit
0663218b67
27 changed files with 404 additions and 257 deletions
|
@ -1,8 +1,12 @@
|
||||||
package com.jozufozu.flywheel.backend.gl;
|
package com.jozufozu.flywheel.backend.gl;
|
||||||
|
|
||||||
public enum GLSLVersion {
|
public enum GLSLVersion {
|
||||||
|
@Deprecated
|
||||||
V110(110),
|
V110(110),
|
||||||
|
@Deprecated
|
||||||
V120(120),
|
V120(120),
|
||||||
|
V150(150),
|
||||||
|
V330(330),
|
||||||
;
|
;
|
||||||
|
|
||||||
public final int version;
|
public final int version;
|
||||||
|
|
|
@ -29,14 +29,14 @@ public class InstancingProgramMetaData {
|
||||||
Optional<ShaderFunction> vertexFunc = file.findFunction("vertex");
|
Optional<ShaderFunction> vertexFunc = file.findFunction("vertex");
|
||||||
Optional<ShaderFunction> fragmentFunc = file.findFunction("fragment");
|
Optional<ShaderFunction> fragmentFunc = file.findFunction("fragment");
|
||||||
|
|
||||||
if (!fragmentFunc.isPresent()) {
|
if (fragmentFunc.isEmpty()) {
|
||||||
ErrorReporter.generateMissingFunction(file, "fragment", "\"fragment\" function not defined");
|
ErrorReporter.generateMissingFunction(file, "fragment", "\"fragment\" function not defined");
|
||||||
}
|
}
|
||||||
if (!vertexFunc.isPresent()) {
|
if (vertexFunc.isEmpty()) {
|
||||||
ErrorReporter.generateFileError(file, "could not find \"vertex\" function");
|
ErrorReporter.generateFileError(file, "could not find \"vertex\" function");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fragmentFunc.isPresent() || !vertexFunc.isPresent()) {
|
if (fragmentFunc.isEmpty() || vertexFunc.isEmpty()) {
|
||||||
throw new ShaderLoadingException();
|
throw new ShaderLoadingException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,19 +64,19 @@ public class InstancingProgramMetaData {
|
||||||
Optional<ShaderStruct> maybeVertex = file.findStruct(vertexName);
|
Optional<ShaderStruct> maybeVertex = file.findStruct(vertexName);
|
||||||
Optional<ShaderStruct> maybeInstance = file.findStruct(instanceName);
|
Optional<ShaderStruct> maybeInstance = file.findStruct(instanceName);
|
||||||
|
|
||||||
if (!maybeVertex.isPresent()) {
|
if (maybeVertex.isEmpty()) {
|
||||||
ErrorReporter.generateMissingStruct(file, vertexName, "struct not defined");
|
ErrorReporter.generateMissingStruct(file, vertexName, "struct not defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!maybeInterpolant.isPresent()) {
|
if (maybeInterpolant.isEmpty()) {
|
||||||
ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined");
|
ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!maybeInstance.isPresent()) {
|
if (maybeInstance.isEmpty()) {
|
||||||
ErrorReporter.generateMissingStruct(file, instanceName, "struct not defined");
|
ErrorReporter.generateMissingStruct(file, instanceName, "struct not defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!maybeVertex.isPresent() || !maybeInterpolant.isPresent() || !maybeInstance.isPresent()) {
|
if (maybeVertex.isEmpty() || maybeInterpolant.isEmpty() || maybeInstance.isEmpty()) {
|
||||||
throw new ShaderLoadingException();
|
throw new ShaderLoadingException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,9 +37,9 @@ public class InstancingTemplate extends Template<InstancingProgramMetaData> {
|
||||||
public void vertexFooter(StringBuilder template, SourceFile file) {
|
public void vertexFooter(StringBuilder template, SourceFile file) {
|
||||||
InstancingProgramMetaData data = getMetadata(file);
|
InstancingProgramMetaData data = getMetadata(file);
|
||||||
|
|
||||||
Template.prefixFields(template, data.vertex, "attribute", "a_v_");
|
Template.prefixFields(template, data.vertex, "in", "a_v_");
|
||||||
Template.prefixFields(template, data.instance, "attribute", "a_i_");
|
Template.prefixFields(template, data.instance, "in", "a_i_");
|
||||||
Template.prefixFields(template, data.interpolant, "varying", "v2f_");
|
Template.prefixFields(template, data.interpolant, "out", "v2f_");
|
||||||
|
|
||||||
template.append("void main() {\n");
|
template.append("void main() {\n");
|
||||||
template.append(data.vertexName)
|
template.append(data.vertexName)
|
||||||
|
@ -63,7 +63,7 @@ public class InstancingTemplate extends Template<InstancingProgramMetaData> {
|
||||||
public void fragmentFooter(StringBuilder template, SourceFile file) {
|
public void fragmentFooter(StringBuilder template, SourceFile file) {
|
||||||
InstancingProgramMetaData data = getMetadata(file);
|
InstancingProgramMetaData data = getMetadata(file);
|
||||||
|
|
||||||
Template.prefixFields(template, data.interpolant, "varying", "v2f_");
|
Template.prefixFields(template, data.interpolant, "in", "v2f_");
|
||||||
|
|
||||||
template.append("void main() {\n");
|
template.append("void main() {\n");
|
||||||
template.append(data.interpolantName)
|
template.append(data.interpolantName)
|
||||||
|
|
|
@ -26,15 +26,15 @@ public class OneShotProgramMetaData {
|
||||||
Optional<ShaderFunction> maybeVertexMain = file.findFunction("vertex");
|
Optional<ShaderFunction> maybeVertexMain = file.findFunction("vertex");
|
||||||
Optional<ShaderFunction> maybeFragmentMain = file.findFunction("fragment");
|
Optional<ShaderFunction> maybeFragmentMain = file.findFunction("fragment");
|
||||||
|
|
||||||
if (!maybeVertexMain.isPresent()) {
|
if (maybeVertexMain.isEmpty()) {
|
||||||
ErrorReporter.generateFileError(file, "could not find \"vertex\" function");
|
ErrorReporter.generateFileError(file, "could not find \"vertex\" function");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!maybeFragmentMain.isPresent()) {
|
if (maybeFragmentMain.isEmpty()) {
|
||||||
ErrorReporter.generateMissingFunction(file, "fragment", "\"fragment\" function not defined");
|
ErrorReporter.generateMissingFunction(file, "fragment", "\"fragment\" function not defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!maybeVertexMain.isPresent() || !maybeFragmentMain.isPresent()) {
|
if (maybeVertexMain.isEmpty() || maybeFragmentMain.isEmpty()) {
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,13 +62,13 @@ public class OneShotProgramMetaData {
|
||||||
Optional<ShaderStruct> maybeInterpolant = file.findStruct(interpolantName);
|
Optional<ShaderStruct> maybeInterpolant = file.findStruct(interpolantName);
|
||||||
Optional<ShaderStruct> maybeVertex = file.findStruct(vertexName);
|
Optional<ShaderStruct> maybeVertex = file.findStruct(vertexName);
|
||||||
|
|
||||||
if (!maybeVertex.isPresent())
|
if (maybeVertex.isEmpty())
|
||||||
ErrorReporter.generateMissingStruct(file, vertexName, "struct not defined");
|
ErrorReporter.generateMissingStruct(file, vertexName, "struct not defined");
|
||||||
|
|
||||||
if (!maybeInterpolant.isPresent())
|
if (maybeInterpolant.isEmpty())
|
||||||
ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined");
|
ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined");
|
||||||
|
|
||||||
if (!maybeVertex.isPresent() || !maybeInterpolant.isPresent()) {
|
if (maybeVertex.isEmpty() || maybeInterpolant.isEmpty()) {
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,8 +32,8 @@ public class OneShotTemplate extends Template<OneShotProgramMetaData> {
|
||||||
public void vertexFooter(StringBuilder template, SourceFile file) {
|
public void vertexFooter(StringBuilder template, SourceFile file) {
|
||||||
OneShotProgramMetaData data = getMetadata(file);
|
OneShotProgramMetaData data = getMetadata(file);
|
||||||
|
|
||||||
Template.prefixFields(template, data.vertex, "attribute", "a_v_");
|
Template.prefixFields(template, data.vertex, "in", "a_v_");
|
||||||
Template.prefixFields(template, data.interpolant, "varying", "v2f_");
|
Template.prefixFields(template, data.interpolant, "out", "v2f_");
|
||||||
|
|
||||||
template.append("void main() {\n");
|
template.append("void main() {\n");
|
||||||
template.append(data.vertexName)
|
template.append(data.vertexName)
|
||||||
|
@ -53,7 +53,7 @@ public class OneShotTemplate extends Template<OneShotProgramMetaData> {
|
||||||
public void fragmentFooter(StringBuilder template, SourceFile file) {
|
public void fragmentFooter(StringBuilder template, SourceFile file) {
|
||||||
OneShotProgramMetaData data = getMetadata(file);
|
OneShotProgramMetaData data = getMetadata(file);
|
||||||
|
|
||||||
Template.prefixFields(template, data.interpolant, "varying", "v2f_");
|
Template.prefixFields(template, data.interpolant, "in", "v2f_");
|
||||||
|
|
||||||
template.append("void main() {\n");
|
template.append("void main() {\n");
|
||||||
template.append(data.interpolant.name)
|
template.append(data.interpolant.name)
|
||||||
|
|
|
@ -25,10 +25,10 @@ public abstract class Template<D> {
|
||||||
|
|
||||||
private final Map<SourceFile, D> metadata = new HashMap<>();
|
private final Map<SourceFile, D> metadata = new HashMap<>();
|
||||||
|
|
||||||
private final Function<SourceFile, D> parser;
|
private final Function<SourceFile, D> reader;
|
||||||
|
|
||||||
protected Template(Function<SourceFile, D> parser) {
|
protected Template(Function<SourceFile, D> reader) {
|
||||||
this.parser = parser;
|
this.reader = reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,11 +46,12 @@ public abstract class Template<D> {
|
||||||
public abstract Collection<ShaderInput> getShaderInputs(SourceFile file);
|
public abstract Collection<ShaderInput> getShaderInputs(SourceFile file);
|
||||||
|
|
||||||
public D getMetadata(SourceFile file) {
|
public D getMetadata(SourceFile file) {
|
||||||
return metadata.computeIfAbsent(file, parser);
|
// lazily read files, cache results
|
||||||
|
return metadata.computeIfAbsent(file, reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GLSLVersion getVersion() {
|
public GLSLVersion getVersion() {
|
||||||
return GLSLVersion.V120;
|
return GLSLVersion.V150;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void prefixFields(StringBuilder builder, ShaderStruct struct, String qualifier, String prefix) {
|
public static void prefixFields(StringBuilder builder, ShaderStruct struct, String qualifier, String prefix) {
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class WorldShader {
|
||||||
.append(template.getVersion())
|
.append(template.getVersion())
|
||||||
.append('\n')
|
.append('\n')
|
||||||
.append("#define ")
|
.append("#define ")
|
||||||
.append(type.define)
|
.append(type.define) // special case shader type declaration
|
||||||
.append('\n')
|
.append('\n')
|
||||||
.append(defines != null ? defines : "")
|
.append(defines != null ? defines : "")
|
||||||
.append(header)
|
.append(header)
|
||||||
|
|
|
@ -67,11 +67,10 @@ public class FileResolution {
|
||||||
try {
|
try {
|
||||||
file = sources.findSource(fileLoc);
|
file = sources.findSource(fileLoc);
|
||||||
} catch (RuntimeException error) {
|
} catch (RuntimeException error) {
|
||||||
ErrorBuilder builder = new ErrorBuilder();
|
ErrorBuilder builder = ErrorBuilder.error(String.format("could not find source for file %s", fileLoc));
|
||||||
builder.error(String.format("could not find source for file %s", fileLoc));
|
|
||||||
// print the location of all places where this file was referenced
|
// print the location of all places where this file was referenced
|
||||||
for (Span span : foundSpans) {
|
for (Span span : foundSpans) {
|
||||||
builder.in(span.getSourceFile())
|
builder.pointAtFile(span.getSourceFile())
|
||||||
.pointAt(span, 2);
|
.pointAt(span, 2);
|
||||||
}
|
}
|
||||||
Backend.log.error(builder.build());
|
Backend.log.error(builder.build());
|
||||||
|
|
|
@ -27,8 +27,8 @@ public class Index {
|
||||||
Collection<SourceFile> files = sources.values();
|
Collection<SourceFile> files = sources.values();
|
||||||
|
|
||||||
for (SourceFile file : files) {
|
for (SourceFile file : files) {
|
||||||
file.getStructs().forEach(knownStructs::put);
|
file.structs.forEach(knownStructs::put);
|
||||||
file.getFunctions().forEach(knownFunctions::put);
|
file.functions.forEach(knownFunctions::put);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,80 +13,65 @@ import com.google.common.collect.ImmutableMap;
|
||||||
import com.jozufozu.flywheel.backend.source.parse.Import;
|
import com.jozufozu.flywheel.backend.source.parse.Import;
|
||||||
import com.jozufozu.flywheel.backend.source.parse.ShaderFunction;
|
import com.jozufozu.flywheel.backend.source.parse.ShaderFunction;
|
||||||
import com.jozufozu.flywheel.backend.source.parse.ShaderStruct;
|
import com.jozufozu.flywheel.backend.source.parse.ShaderStruct;
|
||||||
import com.jozufozu.flywheel.backend.source.span.CharPos;
|
|
||||||
import com.jozufozu.flywheel.backend.source.span.ErrorSpan;
|
import com.jozufozu.flywheel.backend.source.span.ErrorSpan;
|
||||||
import com.jozufozu.flywheel.backend.source.span.Span;
|
import com.jozufozu.flywheel.backend.source.span.Span;
|
||||||
import com.jozufozu.flywheel.backend.source.span.StringSpan;
|
import com.jozufozu.flywheel.backend.source.span.StringSpan;
|
||||||
import com.jozufozu.flywheel.util.StringUtil;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Immutable class representing a shader file.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This class parses shader files and generates what is effectively a high level AST of the source.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
public class SourceFile {
|
public class SourceFile {
|
||||||
private static final Pattern includePattern = Pattern.compile("#use \"(.*)\"");
|
private static final Pattern includePattern = Pattern.compile("#use \"(.*)\"");
|
||||||
|
|
||||||
private static final Pattern newLine = Pattern.compile("(\\r\\n|\\r|\\n)");
|
|
||||||
|
|
||||||
// https://regexr.com/60n3d
|
// https://regexr.com/60n3d
|
||||||
public static final Pattern functionDeclaration = Pattern.compile("(\\w+)\\s+(\\w+)\\s*\\(([\\w,\\s]*)\\)\\s*\\{");
|
public static final Pattern functionDeclaration = Pattern.compile("(\\w+)\\s+(\\w+)\\s*\\(([\\w,\\s]*)\\)\\s*\\{");
|
||||||
|
|
||||||
public final ResourceLocation name;
|
public final ResourceLocation name;
|
||||||
|
|
||||||
public final ShaderSources parent;
|
public final ShaderSources parent;
|
||||||
private final String source;
|
public final String source;
|
||||||
private final CharSequence elided;
|
/**
|
||||||
private final ImmutableList<String> lines;
|
* Sections of the source that must be trimmed for compilation. Currently, it only contains the spans of imports.
|
||||||
|
*/
|
||||||
|
public final String elided;
|
||||||
|
|
||||||
private final IntList lineStarts;
|
public final SourceLines lines;
|
||||||
|
|
||||||
// Function name -> Function object
|
/**
|
||||||
private final ImmutableMap<String, ShaderFunction> functions;
|
* Function lookup by name.
|
||||||
private final ImmutableMap<String, ShaderStruct> structs;
|
*/
|
||||||
|
public final ImmutableMap<String, ShaderFunction> functions;
|
||||||
|
|
||||||
// Includes ordered as defined in the source
|
/**
|
||||||
private final ImmutableList<Import> imports;
|
* Struct lookup by name.
|
||||||
|
*/
|
||||||
|
public final ImmutableMap<String, ShaderStruct> structs;
|
||||||
|
|
||||||
// Sections of the source that must be trimmed for compilation.
|
/**
|
||||||
private final List<Span> elisions = new ArrayList<>();
|
* Includes ordered as defined in the source.
|
||||||
|
*/
|
||||||
|
public final ImmutableList<Import> imports;
|
||||||
|
|
||||||
public SourceFile(ShaderSources parent, ResourceLocation name, String source) {
|
public SourceFile(ShaderSources parent, ResourceLocation name, String source) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.source = source;
|
this.source = source;
|
||||||
|
|
||||||
this.lineStarts = createLineStarts();
|
this.lines = new SourceLines(source);
|
||||||
this.lines = createLineList(lineStarts);
|
|
||||||
|
|
||||||
this.imports = parseImports();
|
List<Span> elisions = new ArrayList<>();
|
||||||
|
|
||||||
|
this.imports = parseImports(elisions);
|
||||||
this.functions = parseFunctions();
|
this.functions = parseFunctions();
|
||||||
this.structs = parseStructs();
|
this.structs = parseStructs();
|
||||||
|
|
||||||
this.elided = createElidedSource();
|
this.elided = elideSource(source, elisions).toString();
|
||||||
}
|
|
||||||
|
|
||||||
public String getSource() {
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CharSequence getElidedSource() {
|
|
||||||
return elided;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ShaderSources getParent() {
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImmutableMap<String, ShaderFunction> getFunctions() {
|
|
||||||
return functions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImmutableMap<String, ShaderStruct> getStructs() {
|
|
||||||
return structs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImmutableList<Import> getIncludes() {
|
|
||||||
return imports;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,11 +81,11 @@ public class SourceFile {
|
||||||
* @return null if no definition matches the name.
|
* @return null if no definition matches the name.
|
||||||
*/
|
*/
|
||||||
public Optional<ShaderStruct> findStruct(CharSequence name) {
|
public Optional<ShaderStruct> findStruct(CharSequence name) {
|
||||||
ShaderStruct struct = getStructs().get(name.toString());
|
ShaderStruct struct = structs.get(name.toString());
|
||||||
|
|
||||||
if (struct != null) return Optional.of(struct);
|
if (struct != null) return Optional.of(struct);
|
||||||
|
|
||||||
for (Import include : getIncludes()) {
|
for (Import include : imports) {
|
||||||
Optional<ShaderStruct> externalStruct = include.getOptional()
|
Optional<ShaderStruct> externalStruct = include.getOptional()
|
||||||
.flatMap(file -> file.findStruct(name));
|
.flatMap(file -> file.findStruct(name));
|
||||||
|
|
||||||
|
@ -117,11 +102,11 @@ public class SourceFile {
|
||||||
* @return Optional#empty() if no definition matches the name.
|
* @return Optional#empty() if no definition matches the name.
|
||||||
*/
|
*/
|
||||||
public Optional<ShaderFunction> findFunction(CharSequence name) {
|
public Optional<ShaderFunction> findFunction(CharSequence name) {
|
||||||
ShaderFunction local = getFunctions().get(name.toString());
|
ShaderFunction local = functions.get(name.toString());
|
||||||
|
|
||||||
if (local != null) return Optional.of(local);
|
if (local != null) return Optional.of(local);
|
||||||
|
|
||||||
for (Import include : getIncludes()) {
|
for (Import include : imports) {
|
||||||
Optional<ShaderFunction> external = include.getOptional()
|
Optional<ShaderFunction> external = include.getOptional()
|
||||||
.flatMap(file -> file.findFunction(name));
|
.flatMap(file -> file.findFunction(name));
|
||||||
|
|
||||||
|
@ -142,29 +127,12 @@ public class SourceFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateFinalSource(StringBuilder source) {
|
public void generateFinalSource(StringBuilder source) {
|
||||||
for (Import include : getIncludes()) {
|
for (Import include : imports) {
|
||||||
SourceFile file = include.getFile();
|
SourceFile file = include.getFile();
|
||||||
|
|
||||||
if (file != null) file.generateFinalSource(source);
|
if (file != null) file.generateFinalSource(source);
|
||||||
}
|
}
|
||||||
source.append(getElidedSource());
|
source.append(elided);
|
||||||
}
|
|
||||||
|
|
||||||
public CharPos getCharPos(int charPos) {
|
|
||||||
int lineNo = 0;
|
|
||||||
for (; lineNo < lineStarts.size(); lineNo++) {
|
|
||||||
int ls = lineStarts.getInt(lineNo);
|
|
||||||
|
|
||||||
if (charPos < ls) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lineNo -= 1;
|
|
||||||
|
|
||||||
int lineStart = lineStarts.getInt(lineNo);
|
|
||||||
|
|
||||||
return new CharPos(charPos, lineNo, charPos - lineStart);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String printSource() {
|
public String printSource() {
|
||||||
|
@ -172,18 +140,13 @@ public class SourceFile {
|
||||||
|
|
||||||
builder.append("Source for shader '")
|
builder.append("Source for shader '")
|
||||||
.append(name)
|
.append(name)
|
||||||
.append("':\n");
|
.append("':\n")
|
||||||
int i = 1;
|
.append(lines.printLinesWithNumbers());
|
||||||
for (String s : lines) {
|
|
||||||
builder.append(String.format("%1$4s: ", i++))
|
|
||||||
.append(s)
|
|
||||||
.append('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private CharSequence createElidedSource() {
|
private static CharSequence elideSource(String source, List<Span> elisions) {
|
||||||
StringBuilder out = new StringBuilder();
|
StringBuilder out = new StringBuilder();
|
||||||
|
|
||||||
int lastEnd = 0;
|
int lastEnd = 0;
|
||||||
|
@ -199,34 +162,6 @@ public class SourceFile {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Scan the source for line breaks, recording the position of the first character of each line.
|
|
||||||
*/
|
|
||||||
private IntList createLineStarts() {
|
|
||||||
IntList l = new IntArrayList();
|
|
||||||
l.add(0); // first line is always at position 0
|
|
||||||
|
|
||||||
Matcher matcher = newLine.matcher(source);
|
|
||||||
|
|
||||||
while (matcher.find()) {
|
|
||||||
l.add(matcher.end());
|
|
||||||
}
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ImmutableList<String> createLineList(IntList lines) {
|
|
||||||
ImmutableList.Builder<String> builder = ImmutableList.builder();
|
|
||||||
|
|
||||||
for (int i = 1; i < lines.size(); i++) {
|
|
||||||
int start = lines.getInt(i - 1);
|
|
||||||
int end = lines.getInt(i);
|
|
||||||
|
|
||||||
builder.add(StringUtil.trimEnd(source.substring(start, end)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scan the source for function definitions and "parse" them into objects that contain properties of the function.
|
* Scan the source for function definitions and "parse" them into objects that contain properties of the function.
|
||||||
*/
|
*/
|
||||||
|
@ -284,8 +219,9 @@ public class SourceFile {
|
||||||
/**
|
/**
|
||||||
* Scan the source for <code>#use "..."</code> directives.
|
* Scan the source for <code>#use "..."</code> directives.
|
||||||
* Records the contents of the directive into an {@link Import} object, and marks the directive for elision.
|
* Records the contents of the directive into an {@link Import} object, and marks the directive for elision.
|
||||||
|
* @param elisions
|
||||||
*/
|
*/
|
||||||
private ImmutableList<Import> parseImports() {
|
private ImmutableList<Import> parseImports(List<Span> elisions) {
|
||||||
Matcher uses = includePattern.matcher(source);
|
Matcher uses = includePattern.matcher(source);
|
||||||
|
|
||||||
List<Import> imports = new ArrayList<>();
|
List<Import> imports = new ArrayList<>();
|
||||||
|
@ -321,11 +257,4 @@ public class SourceFile {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLineCount() {
|
|
||||||
return lines.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CharSequence getLine(int lineNo) {
|
|
||||||
return lines.get(lineNo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
package com.jozufozu.flywheel.backend.source;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.jozufozu.flywheel.backend.source.span.CharPos;
|
||||||
|
import com.jozufozu.flywheel.util.StringUtil;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
|
|
||||||
|
public class SourceLines {
|
||||||
|
|
||||||
|
private static final Pattern newLine = Pattern.compile("(\\r\\n|\\r|\\n)");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 0-indexed line to char pos mapping.
|
||||||
|
*/
|
||||||
|
private final IntList lineStarts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 0-indexed line lookup
|
||||||
|
*/
|
||||||
|
private final ImmutableList<String> lines;
|
||||||
|
|
||||||
|
public SourceLines(String source) {
|
||||||
|
this.lineStarts = createLineLookup(source);
|
||||||
|
this.lines = getLines(source, lineStarts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scan the source for line breaks, recording the position of the first character of each line.
|
||||||
|
* @param source
|
||||||
|
*/
|
||||||
|
private static IntList createLineLookup(String source) {
|
||||||
|
IntList l = new IntArrayList();
|
||||||
|
l.add(0); // first line is always at position 0
|
||||||
|
|
||||||
|
Matcher matcher = newLine.matcher(source);
|
||||||
|
|
||||||
|
while (matcher.find()) {
|
||||||
|
l.add(matcher.end());
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ImmutableList<String> getLines(String source, IntList lines) {
|
||||||
|
ImmutableList.Builder<String> builder = ImmutableList.builder();
|
||||||
|
|
||||||
|
for (int i = 1; i < lines.size(); i++) {
|
||||||
|
int start = lines.getInt(i - 1);
|
||||||
|
int end = lines.getInt(i);
|
||||||
|
|
||||||
|
builder.add(StringUtil.trimEnd(source.substring(start, end)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CharPos getCharPos(int charPos) {
|
||||||
|
int lineNo = 0;
|
||||||
|
for (; lineNo < lineStarts.size(); lineNo++) {
|
||||||
|
int ls = lineStarts.getInt(lineNo);
|
||||||
|
|
||||||
|
if (charPos < ls) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lineNo -= 1;
|
||||||
|
|
||||||
|
int lineStart = lineStarts.getInt(lineNo);
|
||||||
|
|
||||||
|
return new CharPos(charPos, lineNo, charPos - lineStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLineCount() {
|
||||||
|
return lines.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLine(int lineNo) {
|
||||||
|
return lines.get(lineNo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String printLinesWithNumbers() {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
for (int i = 0, linesSize = lines.size(); i < linesSize; i++) {
|
||||||
|
builder.append(String.format("%1$4s: ", i + 1))
|
||||||
|
.append(lines.get(i))
|
||||||
|
.append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,95 +1,79 @@
|
||||||
package com.jozufozu.flywheel.backend.source.error;
|
package com.jozufozu.flywheel.backend.source.error;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.source.SourceFile;
|
import com.jozufozu.flywheel.backend.source.SourceFile;
|
||||||
|
import com.jozufozu.flywheel.backend.source.SourceLines;
|
||||||
|
import com.jozufozu.flywheel.backend.source.error.lines.ErrorLine;
|
||||||
|
import com.jozufozu.flywheel.backend.source.error.lines.FileLine;
|
||||||
|
import com.jozufozu.flywheel.backend.source.error.lines.HeaderLine;
|
||||||
|
import com.jozufozu.flywheel.backend.source.error.lines.SourceLine;
|
||||||
|
import com.jozufozu.flywheel.backend.source.error.lines.SpanHighlightLine;
|
||||||
import com.jozufozu.flywheel.backend.source.span.Span;
|
import com.jozufozu.flywheel.backend.source.span.Span;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
import com.jozufozu.flywheel.util.FlwUtil;
|
||||||
|
|
||||||
public class ErrorBuilder {
|
public class ErrorBuilder {
|
||||||
|
|
||||||
private final StringBuilder internal = new StringBuilder();
|
private final List<ErrorLine> lines = new ArrayList<>();
|
||||||
|
|
||||||
public ErrorBuilder error(CharSequence msg) {
|
private final Level level;
|
||||||
internal.append("error: ")
|
|
||||||
.append(msg);
|
public ErrorBuilder(Level level, CharSequence msg) {
|
||||||
return endLine();
|
this.level = level;
|
||||||
|
|
||||||
|
lines.add(new HeaderLine(level.toString(), msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ErrorBuilder note(CharSequence msg) {
|
public static ErrorBuilder error(CharSequence msg) {
|
||||||
internal.append("note: ")
|
return new ErrorBuilder(Level.ERROR, msg);
|
||||||
.append(msg);
|
|
||||||
return endLine();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ErrorBuilder hint(CharSequence msg) {
|
public static ErrorBuilder warn(CharSequence msg) {
|
||||||
internal.append("hint: ")
|
return new ErrorBuilder(Level.WARN, msg);
|
||||||
.append(msg);
|
|
||||||
return endLine();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ErrorBuilder in(SourceFile file) {
|
public ErrorBuilder pointAtFile(SourceFile file) {
|
||||||
internal.append("--> ")
|
lines.add(new FileLine(file.name.toString()));
|
||||||
.append(file.name);
|
|
||||||
return endLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ErrorBuilder numberedLine(int no, CharSequence content) {
|
|
||||||
return line(String.valueOf(no), content);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ErrorBuilder numberedLine(int no, int width, CharSequence content) {
|
|
||||||
String fmt = "%1$" + width + 's';
|
|
||||||
return line(String.format(fmt, no), content);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ErrorBuilder line(CharSequence leftColumn, CharSequence rightColumn) {
|
|
||||||
|
|
||||||
internal.append(leftColumn)
|
|
||||||
.append(" | ")
|
|
||||||
.append(rightColumn);
|
|
||||||
|
|
||||||
return endLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ErrorBuilder endLine() {
|
|
||||||
internal.append('\n');
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ErrorBuilder hintIncludeFor(@Nullable Span span, CharSequence msg) {
|
public ErrorBuilder hintIncludeFor(@Nullable Span span, CharSequence msg) {
|
||||||
if (span == null) return this;
|
if (span == null) return this;
|
||||||
|
|
||||||
hint("add " + span.getSourceFile().importStatement() + " " + msg)
|
String builder = "add " + span.getSourceFile()
|
||||||
.in(span.getSourceFile())
|
.importStatement() + ' ' + msg;
|
||||||
.pointAt(span, 1);
|
|
||||||
|
|
||||||
return this;
|
lines.add(new HeaderLine("hint", builder));
|
||||||
|
|
||||||
|
return this.pointAtFile(span.getSourceFile())
|
||||||
|
.pointAt(span, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ErrorBuilder pointAt(Span span, int ctxLines) {
|
public ErrorBuilder pointAt(Span span, int ctxLines) {
|
||||||
|
|
||||||
SourceFile file = span.getSourceFile();
|
|
||||||
|
|
||||||
if (span.lines() == 1) {
|
if (span.lines() == 1) {
|
||||||
|
SourceLines lines = span.getSourceFile().lines;
|
||||||
|
|
||||||
int spanLine = span.firstLine();
|
int spanLine = span.firstLine();
|
||||||
|
|
||||||
int firstLine = Math.max(0, spanLine - ctxLines);
|
int firstLine = Math.max(0, spanLine - ctxLines);
|
||||||
int lastLine = Math.min(file.getLineCount(), spanLine + ctxLines);
|
int lastLine = Math.min(lines.getLineCount(), spanLine + ctxLines);
|
||||||
|
|
||||||
int digits = FlwUtil.numDigits(lastLine);
|
int firstCol = span.getStart()
|
||||||
|
.col();
|
||||||
int firstCol = span.getStart().getCol();
|
int lastCol = span.getEnd()
|
||||||
int lastCol = span.getEnd().getCol();
|
.col();
|
||||||
|
|
||||||
for (int i = firstLine; i <= lastLine; i++) {
|
for (int i = firstLine; i <= lastLine; i++) {
|
||||||
CharSequence line = file.getLine(i);
|
CharSequence line = lines.getLine(i);
|
||||||
|
|
||||||
numberedLine(i + 1, digits, line);
|
this.lines.add(SourceLine.numbered(i + 1, line.toString()));
|
||||||
|
|
||||||
if (i == spanLine) {
|
if (i == spanLine) {
|
||||||
line(FlwUtil.repeatChar(' ', digits), generateUnderline(firstCol, lastCol));
|
this.lines.add(new SpanHighlightLine(firstCol, lastCol));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,19 +82,23 @@ public class ErrorBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
public CharSequence build() {
|
public CharSequence build() {
|
||||||
return internal;
|
|
||||||
|
int maxLength = -1;
|
||||||
|
for (ErrorLine line : lines) {
|
||||||
|
int length = line.neededMargin();
|
||||||
|
|
||||||
|
if (length > maxLength) maxLength = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CharSequence generateUnderline(int firstCol, int lastCol) {
|
StringBuilder builder = new StringBuilder();
|
||||||
StringBuilder line = new StringBuilder(lastCol);
|
for (ErrorLine line : lines) {
|
||||||
for (int i = 0; i < firstCol; i++) {
|
int length = line.neededMargin();
|
||||||
line.append(' ');
|
|
||||||
|
builder.append(FlwUtil.repeatChar(' ', maxLength - length))
|
||||||
|
.append(line.build())
|
||||||
|
.append('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = firstCol; i < lastCol; i++) {
|
return builder;
|
||||||
line.append('^');
|
|
||||||
}
|
|
||||||
|
|
||||||
return line;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,8 @@ public class ErrorReporter {
|
||||||
public static void generateSpanError(Span span, String message) {
|
public static void generateSpanError(Span span, String message) {
|
||||||
SourceFile file = span.getSourceFile();
|
SourceFile file = span.getSourceFile();
|
||||||
|
|
||||||
ErrorBuilder builder = new ErrorBuilder();
|
CharSequence error = ErrorBuilder.error(message)
|
||||||
|
.pointAtFile(file)
|
||||||
CharSequence error = builder.error(message)
|
|
||||||
.in(file)
|
|
||||||
.pointAt(span, 2)
|
.pointAt(span, 2)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@ -25,10 +23,8 @@ public class ErrorReporter {
|
||||||
|
|
||||||
public static void generateFileError(SourceFile file, String message) {
|
public static void generateFileError(SourceFile file, String message) {
|
||||||
|
|
||||||
ErrorBuilder builder = new ErrorBuilder();
|
CharSequence error = ErrorBuilder.error(message)
|
||||||
|
.pointAtFile(file)
|
||||||
CharSequence error = builder.error(message)
|
|
||||||
.in(file)
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Backend.log.error(error);
|
Backend.log.error(error);
|
||||||
|
@ -43,10 +39,9 @@ public class ErrorReporter {
|
||||||
.stream()
|
.stream()
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.map(ShaderStruct::getName);
|
.map(ShaderStruct::getName);
|
||||||
ErrorBuilder builder = new ErrorBuilder();
|
|
||||||
|
|
||||||
ErrorBuilder error = builder.error(msg)
|
ErrorBuilder error = ErrorBuilder.error(msg)
|
||||||
.in(file)
|
.pointAtFile(file)
|
||||||
.pointAt(vertexName, 2)
|
.pointAt(vertexName, 2)
|
||||||
.hintIncludeFor(span.orElse(null), hint);
|
.hintIncludeFor(span.orElse(null), hint);
|
||||||
|
|
||||||
|
@ -62,10 +57,9 @@ public class ErrorReporter {
|
||||||
.stream()
|
.stream()
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.map(ShaderFunction::getName);
|
.map(ShaderFunction::getName);
|
||||||
ErrorBuilder builder = new ErrorBuilder();
|
|
||||||
|
|
||||||
ErrorBuilder error = builder.error(msg)
|
ErrorBuilder error = ErrorBuilder.error(msg)
|
||||||
.in(file)
|
.pointAtFile(file)
|
||||||
.hintIncludeFor(span.orElse(null), hint);
|
.hintIncludeFor(span.orElse(null), hint);
|
||||||
|
|
||||||
Backend.log.error(error.build());
|
Backend.log.error(error.build());
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.jozufozu.flywheel.backend.source.error;
|
||||||
|
|
||||||
|
public enum Level {
|
||||||
|
WARN("warn"),
|
||||||
|
ERROR("error"),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final String error;
|
||||||
|
|
||||||
|
Level(String error) {
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.jozufozu.flywheel.backend.source.error.lines;
|
||||||
|
|
||||||
|
public enum Divider {
|
||||||
|
BAR(" | "),
|
||||||
|
ARROW("-->"),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final String s;
|
||||||
|
|
||||||
|
Divider(String s) {
|
||||||
|
this.s = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.jozufozu.flywheel.backend.source.error.lines;
|
||||||
|
|
||||||
|
public interface ErrorLine {
|
||||||
|
|
||||||
|
default int neededMargin() {
|
||||||
|
return left().length();
|
||||||
|
}
|
||||||
|
|
||||||
|
default Divider divider() {
|
||||||
|
return Divider.BAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
default String build() {
|
||||||
|
return left() + divider() + right();
|
||||||
|
}
|
||||||
|
|
||||||
|
String left();
|
||||||
|
String right();
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.jozufozu.flywheel.backend.source.error.lines;
|
||||||
|
|
||||||
|
public record FileLine(String fileName) implements ErrorLine {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String left() {
|
||||||
|
return "--";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Divider divider() {
|
||||||
|
return Divider.ARROW;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String right() {
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.jozufozu.flywheel.backend.source.error.lines;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.backend.source.error.Level;
|
||||||
|
|
||||||
|
public record HeaderLine(String level, CharSequence message) implements ErrorLine {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int neededMargin() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String build() {
|
||||||
|
return level + ": " + message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String left() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String right() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.jozufozu.flywheel.backend.source.error.lines;
|
||||||
|
|
||||||
|
public record SourceLine(String number, String line) implements ErrorLine {
|
||||||
|
|
||||||
|
public static SourceLine numbered(int number, String line) {
|
||||||
|
return new SourceLine(Integer.toString(number), line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String left() {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String right() {
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.jozufozu.flywheel.backend.source.error.lines;
|
||||||
|
|
||||||
|
public class SpanHighlightLine implements ErrorLine {
|
||||||
|
private final String line;
|
||||||
|
|
||||||
|
public SpanHighlightLine(int firstCol, int lastCol) {
|
||||||
|
line = generateUnderline(firstCol, lastCol);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String left() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String right() {
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String generateUnderline(int firstCol, int lastCol) {
|
||||||
|
return " ".repeat(Math.max(0, firstCol)) + "^".repeat(Math.max(0, lastCol - firstCol));
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,27 +3,5 @@ package com.jozufozu.flywheel.backend.source.span;
|
||||||
/**
|
/**
|
||||||
* A position in a file.
|
* A position in a file.
|
||||||
*/
|
*/
|
||||||
public class CharPos {
|
public record CharPos(int pos, int line, int col) {
|
||||||
|
|
||||||
private final int idx;
|
|
||||||
private final int line;
|
|
||||||
private final int col;
|
|
||||||
|
|
||||||
public CharPos(int idx, int line, int col) {
|
|
||||||
this.idx = idx;
|
|
||||||
this.line = line;
|
|
||||||
this.col = col;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPos() {
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getLine() {
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCol() {
|
|
||||||
return col;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,11 @@ import java.util.regex.Matcher;
|
||||||
import com.jozufozu.flywheel.backend.source.SourceFile;
|
import com.jozufozu.flywheel.backend.source.SourceFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A span of code in a {@link SourceFile}.
|
* A segment of code in a {@link SourceFile}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Spans are used for pretty-printing errors.
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
public abstract class Span implements CharSequence {
|
public abstract class Span implements CharSequence {
|
||||||
|
|
||||||
|
@ -14,7 +18,7 @@ public abstract class Span implements CharSequence {
|
||||||
protected final CharPos end;
|
protected final CharPos end;
|
||||||
|
|
||||||
public Span(SourceFile in, int start, int end) {
|
public Span(SourceFile in, int start, int end) {
|
||||||
this(in, in.getCharPos(start), in.getCharPos(end));
|
this(in, in.lines.getCharPos(start), in.lines.getCharPos(end));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Span(SourceFile in, CharPos start, CharPos end) {
|
public Span(SourceFile in, CharPos start, CharPos end) {
|
||||||
|
@ -42,14 +46,14 @@ public abstract class Span implements CharSequence {
|
||||||
* @return the string index at the (inclusive) beginning of this code segment.
|
* @return the string index at the (inclusive) beginning of this code segment.
|
||||||
*/
|
*/
|
||||||
public int getStartPos() {
|
public int getStartPos() {
|
||||||
return start.getPos();
|
return start.pos();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the string index at the (exclusive) end of this code segment.
|
* @return the string index at the (exclusive) end of this code segment.
|
||||||
*/
|
*/
|
||||||
public int getEndPos() {
|
public int getEndPos() {
|
||||||
return end.getPos();
|
return end.pos();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,11 +67,11 @@ public abstract class Span implements CharSequence {
|
||||||
* @return how many lines this span spans.
|
* @return how many lines this span spans.
|
||||||
*/
|
*/
|
||||||
public int lines() {
|
public int lines() {
|
||||||
return end.getLine() - start.getLine() + 1;
|
return end.line() - start.line() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int firstLine() {
|
public int firstLine() {
|
||||||
return start.getLine();
|
return start.line();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -89,7 +93,7 @@ public abstract class Span implements CharSequence {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public char charAt(int index) {
|
public char charAt(int index) {
|
||||||
return in.getSource().charAt(start.getPos() + index);
|
return in.source.charAt(start.pos() + index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -14,13 +14,13 @@ public class StringSpan extends Span {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Span subSpan(int from, int to) {
|
public Span subSpan(int from, int to) {
|
||||||
return new StringSpan(in, start.getPos() + from, start.getPos() + to);
|
return new StringSpan(in, start.pos() + from, start.pos() + to);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String get() {
|
public String get() {
|
||||||
return in.getSource()
|
return in.source
|
||||||
.substring(start.getPos(), end.getPos());
|
.substring(start.pos(), end.pos());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -26,6 +26,8 @@ void FLWFinalizeWorldPos(inout vec4 worldPos) {
|
||||||
|
|
||||||
#elif defined(FRAGMENT_SHADER)
|
#elif defined(FRAGMENT_SHADER)
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
vec4 FLWBlockTexture(vec2 texCoords) {
|
vec4 FLWBlockTexture(vec2 texCoords) {
|
||||||
vec4 cr = texture2D(uCrumbling, texCoords * uTextureScale);
|
vec4 cr = texture2D(uCrumbling, texCoords * uTextureScale);
|
||||||
float diffuseAlpha = texture2D(uBlockAtlas, texCoords).a;
|
float diffuseAlpha = texture2D(uBlockAtlas, texCoords).a;
|
||||||
|
@ -42,7 +44,11 @@ void FLWFinalizeColor(vec4 color) {
|
||||||
color.a = a;
|
color.a = a;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
gl_FragColor = color;
|
if (color.a < 0.1) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragColor = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec4 FLWLight(vec2 lightCoords) {
|
vec4 FLWLight(vec2 lightCoords) {
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
varying float FragDistance;
|
#if defined(VERTEX_SHADER)
|
||||||
|
out float FragDistance;
|
||||||
|
#elif defined(FRAGMENT_SHADER)
|
||||||
|
in float FragDistance;
|
||||||
|
#endif
|
||||||
uniform vec4 uFogColor;
|
uniform vec4 uFogColor;
|
||||||
uniform vec2 uFogRange;
|
uniform vec2 uFogRange;
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,8 @@ void FLWFinalizeWorldPos(inout vec4 worldPos) {
|
||||||
//#endif
|
//#endif
|
||||||
//#endif
|
//#endif
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
vec4 FLWBlockTexture(vec2 texCoords) {
|
vec4 FLWBlockTexture(vec2 texCoords) {
|
||||||
return texture2D(uBlockAtlas, texCoords);
|
return texture2D(uBlockAtlas, texCoords);
|
||||||
}
|
}
|
||||||
|
@ -53,7 +55,7 @@ void FLWFinalizeColor(vec4 color) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
gl_FragColor = color;
|
fragColor = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec4 FLWLight(vec2 lightCoords) {
|
vec4 FLWLight(vec2 lightCoords) {
|
||||||
|
|
Loading…
Reference in a new issue