mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-28 16:06:28 +01:00
Better errors still
- Actually can print something that underlines a span - Resolve imports and use the resolutions during building - Doesn't actually make sense to have #checkErrors - Simplify some regexes - Parse structs
This commit is contained in:
parent
279d3a2de7
commit
95dabd900e
13 changed files with 197 additions and 62 deletions
|
@ -89,6 +89,8 @@ public class ShaderSources implements ISelectiveResourceReloadListener {
|
|||
loadProgramSpecs(manager);
|
||||
loadShaderSources(manager);
|
||||
|
||||
shaderSources.values().forEach(SourceFile::resolveIncludes);
|
||||
|
||||
WorldShaderPipeline<WorldProgram> pl = new WorldShaderPipeline<>(this);
|
||||
|
||||
SourceFile source = source(new ResourceLocation(Flywheel.ID, "model.glsl"));
|
||||
|
|
|
@ -15,28 +15,25 @@ import net.minecraft.util.ResourceLocation;
|
|||
public class Includer {
|
||||
|
||||
public static List<SourceFile> recurseIncludes(SourceFile from) {
|
||||
ShaderSources sources = from.getParent();
|
||||
|
||||
Set<ResourceLocation> seen = new HashSet<>();
|
||||
|
||||
seen.add(from.name);
|
||||
|
||||
List<SourceFile> out = new ArrayList<>();
|
||||
|
||||
process(sources, seen, out, from);
|
||||
process(seen, out, from);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private static void process(ShaderSources sources, Set<ResourceLocation> seen, List<SourceFile> out, SourceFile source) {
|
||||
private static void process(Set<ResourceLocation> seen, List<SourceFile> out, SourceFile source) {
|
||||
ImmutableList<Include> includes = source.getIncludes();
|
||||
|
||||
for (Include include : includes) {
|
||||
|
||||
if (seen.add(include.getFile())) {
|
||||
SourceFile file = sources.source(include.getFile());
|
||||
|
||||
process(sources, seen, out, file);
|
||||
SourceFile file = include.getTarget();
|
||||
if (file != null && seen.add(file.name)) {
|
||||
process(seen, out, file);
|
||||
|
||||
out.add(file);
|
||||
}
|
||||
|
|
|
@ -10,20 +10,23 @@ import java.util.regex.Pattern;
|
|||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.jozufozu.flywheel.backend.ShaderSources;
|
||||
import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.AbstractShaderElement;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.Include;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderFunction;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct;
|
||||
import com.jozufozu.flywheel.backend.pipeline.span.CharPos;
|
||||
import com.jozufozu.flywheel.backend.pipeline.span.ErrorSpan;
|
||||
import com.jozufozu.flywheel.backend.pipeline.span.Span;
|
||||
import com.jozufozu.flywheel.backend.pipeline.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.util.ResourceLocation;
|
||||
|
||||
public class SourceFile {
|
||||
// #use "valid_namespace:valid/path_to_file.glsl"
|
||||
private static final Pattern includePattern = Pattern.compile("#use \"(\\w+:[\\w./]+)\"");
|
||||
private static final Pattern includePattern = Pattern.compile("#use \"(.*)\"");
|
||||
|
||||
private static final Pattern newLine = Pattern.compile("(\\r\\n|\\r|\\n)");
|
||||
|
||||
|
@ -34,11 +37,13 @@ public class SourceFile {
|
|||
|
||||
private final ShaderSources parent;
|
||||
private final String source;
|
||||
private final ImmutableList<String> lines;
|
||||
|
||||
private final IntList lineStarts;
|
||||
|
||||
// Function name -> Function object
|
||||
private final ImmutableMap<String, ShaderFunction> functions;
|
||||
private final ImmutableMap<String, ShaderStruct> structs;
|
||||
|
||||
// Includes ordered as defined in the source
|
||||
private final ImmutableList<Include> includes;
|
||||
|
@ -51,10 +56,12 @@ public class SourceFile {
|
|||
this.name = name;
|
||||
this.source = source;
|
||||
|
||||
this.lineStarts = getLinePositions();
|
||||
this.lineStarts = createLineStarts();
|
||||
this.lines = createLineList(lineStarts);
|
||||
|
||||
this.functions = parseFunctions();
|
||||
this.includes = parseIncludes();
|
||||
this.functions = parseFunctions();
|
||||
this.structs = parseStructs();
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
|
@ -73,6 +80,12 @@ public class SourceFile {
|
|||
return includes;
|
||||
}
|
||||
|
||||
public void resolveIncludes() {
|
||||
for (Include include : includes) {
|
||||
include.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
public CharPos getCharPos(int charPos) {
|
||||
int lineNo = 0;
|
||||
for (; lineNo < lineStarts.size(); lineNo++) {
|
||||
|
@ -83,7 +96,9 @@ public class SourceFile {
|
|||
}
|
||||
}
|
||||
|
||||
int lineStart = lineStarts.getInt(lineNo - 1);
|
||||
lineNo -= 1;
|
||||
|
||||
int lineStart = lineStarts.getInt(lineNo);
|
||||
|
||||
return new CharPos(charPos, lineNo, charPos - lineStart);
|
||||
}
|
||||
|
@ -95,7 +110,7 @@ public class SourceFile {
|
|||
.append(name)
|
||||
.append("':\n");
|
||||
int i = 1;
|
||||
for (String s : source.split("\n")) {
|
||||
for (String s : lines) {
|
||||
builder.append(String.format("%1$4s: ", i++))
|
||||
.append(s)
|
||||
.append('\n');
|
||||
|
@ -129,7 +144,7 @@ public class SourceFile {
|
|||
/**
|
||||
* Scan the source for line breaks, recording the position of the first character of each line.
|
||||
*/
|
||||
private IntList getLinePositions() {
|
||||
private IntList createLineStarts() {
|
||||
IntList l = new IntArrayList();
|
||||
l.add(0); // first line is always at position 0
|
||||
|
||||
|
@ -141,6 +156,19 @@ public class SourceFile {
|
|||
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.
|
||||
*/
|
||||
|
@ -175,6 +203,26 @@ public class SourceFile {
|
|||
return ImmutableMap.copyOf(functions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the source for function definitions and "parse" them into objects that contain properties of the function.
|
||||
*/
|
||||
private ImmutableMap<String, ShaderStruct> parseStructs() {
|
||||
Matcher matcher = ShaderStruct.struct.matcher(source);
|
||||
|
||||
ImmutableMap.Builder<String, ShaderStruct> functions = ImmutableMap.builder();
|
||||
while (matcher.find()) {
|
||||
Span self = Span.fromMatcher(this, matcher);
|
||||
Span name = Span.fromMatcher(this, matcher, 1);
|
||||
Span body = Span.fromMatcher(this, matcher, 2);
|
||||
|
||||
ShaderStruct shaderStruct = new ShaderStruct(self, name, body);
|
||||
|
||||
functions.put(body.get(), shaderStruct);
|
||||
}
|
||||
|
||||
return functions.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the source for <code>#use "..."</code> directives.
|
||||
* Records the contents of the directive into an {@link Include} object, and marks the directive for elision.
|
||||
|
@ -214,4 +262,12 @@ public class SourceFile {
|
|||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getLineCount() {
|
||||
return lines.size();
|
||||
}
|
||||
|
||||
public CharSequence getLine(int lineNo) {
|
||||
return lines.get(lineNo);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline.error;
|
||||
|
||||
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
|
||||
import com.jozufozu.flywheel.backend.pipeline.span.Span;
|
||||
|
||||
public class ErrorBuilder {
|
||||
|
||||
private StringBuilder internal;
|
||||
private final StringBuilder internal = new StringBuilder();
|
||||
|
||||
public ErrorBuilder header(CharSequence msg) {
|
||||
internal.append("error: ")
|
||||
|
@ -18,7 +19,15 @@ public class ErrorBuilder {
|
|||
return endLine();
|
||||
}
|
||||
|
||||
public ErrorBuilder line(int no, CharSequence content) {
|
||||
public ErrorBuilder numberedLine(int no, CharSequence content) {
|
||||
return line(String.valueOf(no), content);
|
||||
}
|
||||
|
||||
public ErrorBuilder line(CharSequence leftColumn, CharSequence rightColumn) {
|
||||
|
||||
internal.append(leftColumn)
|
||||
.append(" | ")
|
||||
.append(rightColumn);
|
||||
|
||||
return endLine();
|
||||
}
|
||||
|
@ -27,4 +36,49 @@ public class ErrorBuilder {
|
|||
internal.append('\n');
|
||||
return this;
|
||||
}
|
||||
|
||||
public ErrorBuilder pointAt(Span span, int ctxLines) {
|
||||
SourceFile file = span.getSourceFile();
|
||||
|
||||
if (span.lines() == 1) {
|
||||
|
||||
int spanLine = span.firstLine();
|
||||
|
||||
int firstLine = Math.max(0, spanLine - ctxLines);
|
||||
int lastLine = Math.min(file.getLineCount(), spanLine + ctxLines);
|
||||
|
||||
|
||||
int firstCol = span.getStart().getCol();
|
||||
int lastCol = span.getEnd().getCol();
|
||||
|
||||
for (int i = firstLine; i <= lastLine; i++) {
|
||||
CharSequence line = file.getLine(i);
|
||||
|
||||
numberedLine(i + 1, line);
|
||||
|
||||
if (i == spanLine) {
|
||||
line(" ", generateUnderline(firstCol, lastCol));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public CharSequence build() {
|
||||
return internal;
|
||||
}
|
||||
|
||||
public CharSequence generateUnderline(int firstCol, int lastCol) {
|
||||
StringBuilder line = new StringBuilder(lastCol);
|
||||
for (int i = 0; i < firstCol; i++) {
|
||||
line.append(' ');
|
||||
}
|
||||
|
||||
for (int i = firstCol; i < lastCol; i++) {
|
||||
line.append('^');
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline.error;
|
||||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
|
||||
import com.jozufozu.flywheel.backend.pipeline.span.Span;
|
||||
|
||||
public class ErrorReporter {
|
||||
|
||||
|
||||
public String generateSpanError(Span span, String message) {
|
||||
public static void generateSpanError(Span span, String message) {
|
||||
SourceFile file = span.getSourceFile();
|
||||
|
||||
ErrorBuilder builder = new ErrorBuilder();
|
||||
|
||||
CharSequence error = builder.header(message)
|
||||
.errorIn(file)
|
||||
.pointAt(span, 2)
|
||||
.build();
|
||||
|
||||
return "";
|
||||
Backend.log.info(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline.parse;
|
||||
|
||||
import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter;
|
||||
import com.jozufozu.flywheel.backend.pipeline.span.Span;
|
||||
|
||||
public abstract class AbstractShaderElement {
|
||||
|
@ -11,5 +10,4 @@ public abstract class AbstractShaderElement {
|
|||
this.self = self;
|
||||
}
|
||||
|
||||
public abstract void checkErrors(ErrorReporter e);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package com.jozufozu.flywheel.backend.pipeline.parse;
|
|||
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.backend.ShaderSources;
|
||||
import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter;
|
||||
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
|
||||
|
@ -14,37 +16,43 @@ public class Include extends AbstractShaderElement {
|
|||
private final ShaderSources sources;
|
||||
private Span file;
|
||||
|
||||
private ResourceLocation fileLoc;
|
||||
private SourceFile resolution;
|
||||
|
||||
|
||||
public Include(ShaderSources sources, Span self, Span file) {
|
||||
super(self);
|
||||
this.sources = sources;
|
||||
this.file = file;
|
||||
|
||||
try {
|
||||
fileLoc = new ResourceLocation(file.get());
|
||||
} catch (RuntimeException error) {
|
||||
ErrorReporter.generateSpanError(file, "malformed source name");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isResolved() {
|
||||
return resolution != null;
|
||||
}
|
||||
|
||||
public Optional<SourceFile> getTarget() {
|
||||
return Optional.ofNullable(resolution);
|
||||
@Nullable
|
||||
public SourceFile getTarget() {
|
||||
return resolution;
|
||||
}
|
||||
|
||||
public ResourceLocation getFile() {
|
||||
return new ResourceLocation(file.get());
|
||||
return fileLoc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkErrors(ErrorReporter e) {
|
||||
public void resolve() {
|
||||
|
||||
String name = file.get();
|
||||
if (fileLoc == null) return;
|
||||
|
||||
try {
|
||||
ResourceLocation loc = new ResourceLocation(name);
|
||||
resolution = sources.source(loc);
|
||||
resolution = sources.source(fileLoc);
|
||||
} catch (RuntimeException error) {
|
||||
|
||||
ErrorReporter.generateSpanError(file, "could not find source");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,9 +61,4 @@ public class ShaderFunction extends AbstractShaderElement {
|
|||
|
||||
return type + " " + name + "(" + p + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkErrors(ErrorReporter e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline.parse;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -12,8 +13,8 @@ import com.jozufozu.flywheel.backend.pipeline.span.Span;
|
|||
|
||||
public class ShaderStruct extends AbstractShaderElement {
|
||||
|
||||
// https://regexr.com/5t207
|
||||
public static final Pattern struct = Pattern.compile("struct\\s+([\\w\\d]*)\\s*\\{([\\w\\d \\t#\\[\\](),;\\n]*)}\\s*;");
|
||||
// https://regexr.com/61rpe
|
||||
public static final Pattern struct = Pattern.compile("struct\\s+([\\w\\d]*)\\s*\\{([\\w\\d\\s,;]*)}\\s*;\\s");
|
||||
|
||||
public final Span name;
|
||||
public final Span body;
|
||||
|
@ -26,18 +27,16 @@ public class ShaderStruct extends AbstractShaderElement {
|
|||
this.name = name;
|
||||
this.body = body;
|
||||
this.fields = parseFields();
|
||||
this.fields2Types = createTypeLookup();
|
||||
}
|
||||
|
||||
private ImmutableMap<String, Span> createTypeLookup() {
|
||||
ImmutableMap.Builder<String, Span> lookup = ImmutableMap.builder();
|
||||
for (StructField field : fields) {
|
||||
lookup.put(field.name.get(), field.type);
|
||||
}
|
||||
|
||||
this.fields2Types = lookup.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkErrors(ErrorReporter e) {
|
||||
|
||||
return lookup.build();
|
||||
}
|
||||
|
||||
private ImmutableList<StructField> parseFields() {
|
||||
|
|
|
@ -8,30 +8,25 @@ import com.jozufozu.flywheel.backend.pipeline.span.Span;
|
|||
public class StructField extends AbstractShaderElement {
|
||||
public static final Pattern fieldPattern = Pattern.compile("(\\S+)\\s*(\\S+);");
|
||||
|
||||
public Span name;
|
||||
public Span type;
|
||||
public Span name;
|
||||
|
||||
public StructField(Span self, Span name, Span type) {
|
||||
public StructField(Span self, Span type, Span name) {
|
||||
super(self);
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Span getName() {
|
||||
return name;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Span getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Span getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TaggedField{" + "name='" + name + '\'' + ", type='" + type + '\'' + '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkErrors(ErrorReporter e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,9 +21,4 @@ public class Variable extends AbstractShaderElement {
|
|||
public Span getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkErrors(ErrorReporter e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,14 @@ public abstract class Span implements CharSequence {
|
|||
return in;
|
||||
}
|
||||
|
||||
public CharPos getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public CharPos getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the string index at the (inclusive) beginning of this code segment.
|
||||
*/
|
||||
|
@ -51,6 +59,17 @@ public abstract class Span implements CharSequence {
|
|||
return start == end;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return how many lines this span spans.
|
||||
*/
|
||||
public int lines() {
|
||||
return end.getLine() - start.getLine() + 1;
|
||||
}
|
||||
|
||||
public int firstLine() {
|
||||
return start.getLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a span referring to a code segment inside this code segment.
|
||||
*/
|
||||
|
|
12
src/main/java/com/jozufozu/flywheel/util/StringUtil.java
Normal file
12
src/main/java/com/jozufozu/flywheel/util/StringUtil.java
Normal file
|
@ -0,0 +1,12 @@
|
|||
package com.jozufozu.flywheel.util;
|
||||
|
||||
public class StringUtil {
|
||||
public static String trimEnd(String value) {
|
||||
int len = value.length();
|
||||
int st = 0;
|
||||
while ((st < len) && Character.isWhitespace(value.charAt(len - 1))) {
|
||||
len--;
|
||||
}
|
||||
return value.substring(0, len);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue