private static List <ZScriptToken> CompileShaderSource(ZScriptTokenizer t) { // syntax: // fragment { ... code ... } // or // vertex { ... code ... } t.SkipWhitespace(); ZScriptToken token = t.ExpectToken(ZScriptTokenType.OpenCurly); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected shader source block, got {0}", token?.ToString() ?? "<EOF>"); } List <ZScriptToken> tokens = ReadEverythingUntil(t, ZScriptTokenType.CloseCurly, false, false); token = t.ExpectToken(ZScriptTokenType.CloseCurly); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected end of shader source block, got {0}", token?.ToString() ?? "<EOF>"); } return(tokens); }
private static void CompileShaderFunction(ShaderFunction func, ZScriptTokenizer t) { ZScriptToken token; t.SkipWhitespace(); token = t.ExpectToken(ZScriptTokenType.Identifier); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected function name, got {0}", token?.ToString() ?? "<EOF>"); } func.Name = token.Value; t.SkipWhitespace(); token = t.ExpectToken(ZScriptTokenType.OpenParen); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected function argument list, got {0}", token?.ToString() ?? "<EOF>"); } func.Arguments = ReadEverythingUntil(t, ZScriptTokenType.CloseParen, false, false); token = t.ExpectToken(ZScriptTokenType.CloseParen); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected end of function arguments, got {0}", token?.ToString() ?? "<EOF>"); } t.SkipWhitespace(); token = t.ExpectToken(ZScriptTokenType.OpenCurly); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected function code block, got {0}", token?.ToString() ?? "<EOF>"); } func.CodeLine = t.PositionToLine(token.Position); func.Code = ReadEverythingUntil(t, ZScriptTokenType.CloseCurly, false, false); token = t.ExpectToken(ZScriptTokenType.CloseCurly); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected end of function code block, got {0}", token?.ToString() ?? "<EOF>"); } }
private static List <ShaderField> CompileShaderDataBlock(ZScriptTokenizer t) { List <ShaderField> fields = new List <ShaderField>(); t.SkipWhitespace(); ZScriptToken token = t.ExpectToken(ZScriptTokenType.OpenCurly); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected data block, got {0}", token?.ToString() ?? "<EOF>"); } while (true) { t.SkipWhitespace(); token = t.ExpectToken(ZScriptTokenType.Identifier, ZScriptTokenType.CloseCurly); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected data field or end of block, got {0}", token?.ToString() ?? "<EOF>"); } if (token.Type == ZScriptTokenType.CloseCurly) { break; } ShaderField field = new ShaderField(); field.Line = t.PositionToLine(token.Position); field.TypeName = token.Value; CompileShaderField(field, t); fields.Add(field); } return(fields); }
private static void CompileUniforms(ShaderGroup output, ZScriptTokenizer t) { // so a type of a variable is normally identifier+array dimensions // array dimensions may also exist on the variable itself (oh god this shitty C syntax) t.SkipWhitespace(); ZScriptToken token; token = t.ExpectToken(ZScriptTokenType.OpenCurly); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected uniforms block, got {0}", token?.ToString() ?? "<EOF>"); } while (true) { t.SkipWhitespace(); token = t.ExpectToken(ZScriptTokenType.Identifier, ZScriptTokenType.CloseCurly); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected uniform or end of block, got {0}", token?.ToString() ?? "<EOF>"); } if (token.Type == ZScriptTokenType.CloseCurly) { break; // done reading uniforms } // first goes the name, then array dimensions, then the variable name, then array dimensions, then initializer ShaderField field = new ShaderField(); field.Line = t.PositionToLine(token.Position); field.TypeName = token.Value; CompileShaderField(field, t); // done reading field, add it output.Uniforms.Add(field); } }
private static void CompileShader(ShaderGroup output, ZScriptTokenizer t) { t.SkipWhitespace(); ZScriptToken token = t.ExpectToken(ZScriptTokenType.Identifier); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected shader identifier, got {0}", token?.ToString() ?? "<EOF>"); } t.SkipWhitespace(); Shader s = new Shader(output); s.Name = token.Value; output.Shaders.Add(s); token = t.ExpectToken(ZScriptTokenType.Identifier, ZScriptTokenType.OpenCurly); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected parent identifier or shader block, got {0}", token?.ToString() ?? "<EOF>"); } // has parent shader id if (token.Type == ZScriptTokenType.Identifier) { if (token.Value != "extends") { throw new ShaderCompileException("Expected 'extends', got {0}", token.ToString()); } t.SkipWhitespace(); token = t.ExpectToken(ZScriptTokenType.Identifier); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected parent identifier, got {0}", token?.ToString() ?? "<EOF>"); } s.ParentName = token.Value; t.SkipWhitespace(); token = t.ExpectToken(ZScriptTokenType.OpenCurly); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected shader block, got {0}", token?.ToString() ?? "<EOF>"); } } s.CodeLine = t.PositionToLine(token.Position); while (true) { t.SkipWhitespace(); token = t.ExpectToken(ZScriptTokenType.Identifier, ZScriptTokenType.CloseCurly); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected shader sub-block or end of block, got {0}", token?.ToString() ?? "<EOF>"); } if (token.Type == ZScriptTokenType.CloseCurly) { break; } switch (token.Value) { case "in": s.In = CompileShaderDataBlock(t); break; case "out": s.Out = CompileShaderDataBlock(t); break; case "v2f": s.V2F = CompileShaderDataBlock(t); break; case "functions": CompileShaderFunctions(s, t); break; case "vertex": s.SourceVertex = CompileShaderSource(t); if (s.SourceVertex != null && s.SourceVertex.Count > 0) { s.SourceVertexLine = t.PositionToLine(s.SourceVertex[0].Position); } break; case "fragment": s.SourceFragment = CompileShaderSource(t); if (s.SourceFragment != null && s.SourceFragment.Count > 0) { s.SourceFragmentLine = t.PositionToLine(s.SourceFragment[0].Position); } break; default: throw new ShaderCompileException("Expected shader sub-block, got {0}", token.ToString()); } } }
private static void CompileShaderFunctions(Shader output, ZScriptTokenizer t) { t.SkipWhitespace(); ZScriptToken token; token = t.ExpectToken(ZScriptTokenType.OpenCurly); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected functions block, got {0}", token?.ToString() ?? "<EOF>"); } while (true) { t.SkipWhitespace(); token = t.ExpectToken(ZScriptTokenType.Identifier, ZScriptTokenType.CloseCurly); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected function or end of block, got {0}", token?.ToString() ?? "<EOF>"); } if (token.Type == ZScriptTokenType.CloseCurly) { break; // done reading functions } bool isoverride = false; if (token.Value == "override") { isoverride = true; t.SkipWhitespace(); token = t.ExpectToken(ZScriptTokenType.Identifier); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected function return type, got {0}", token?.ToString() ?? "<EOF>"); } } // <return value> <name> (<arguments>) { <code> } ShaderFunction func = new ShaderFunction(); func.Line = t.PositionToLine(token.Position); func.ReturnValue = token.Value; func.Override = isoverride; CompileShaderFunction(func, t); // check if function with such name already exists in the shader // delete it. // overloading is not supported for now if (!isoverride) { ShaderFunction existingFunc = output.Group.GetFunction(func.Name); if (existingFunc != null) { throw new ShaderCompileException("Function {0} is double-defined without 'override' keyword! (previous declaration at line {1})", func.Name, existingFunc.Line); } } for (int i = 0; i < output.Functions.Count; i++) { if (output.Functions[i].Name == func.Name) { if (!isoverride) { throw new ShaderCompileException("Function {0} is double-defined without 'override' keyword! (previous declaration at line {1})", func.Name, output.Functions[i].Line); } output.Functions.RemoveAt(i); i--; continue; } } output.Functions.Add(func); } }
private static void CompileShaderField(ShaderField field, ZScriptTokenizer t) { ZScriptToken token; // read name and array dimensions while (true) { t.SkipWhitespace(); token = t.ExpectToken(ZScriptTokenType.OpenSquare, ZScriptTokenType.Identifier); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected array dimensions or field name, got {0}", token?.ToString() ?? "<EOF>"); } // array finished if (token.Type == ZScriptTokenType.Identifier) { field.Name = token.Value; break; } // read array List <ZScriptToken> arrayDimTokens = ReadEverythingUntil(t, ZScriptTokenType.CloseSquare, false, false); if (field.ArrayDimensions == null) { field.ArrayDimensions = new List <List <ZScriptToken> >(); } field.ArrayDimensions.Add(arrayDimTokens); token = t.ExpectToken(ZScriptTokenType.CloseSquare); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected closing square brace, got {0}", token?.ToString() ?? "<EOF>"); } } // read additional array dimensions if present, and initializer. or end parsing while (true) { t.SkipWhitespace(); token = t.ExpectToken(ZScriptTokenType.OpenSquare, ZScriptTokenType.OpAssign, ZScriptTokenType.Semicolon); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected array dimensions, initializer or semicolon, got {0}", token?.ToString() ?? "<EOF>"); } // field is done if (token.Type == ZScriptTokenType.Semicolon) { break; } // has initializer if (token.Type == ZScriptTokenType.OpAssign) { field.Initializer = ReadEverythingUntil(t, ZScriptTokenType.Semicolon, false, false); token = t.ExpectToken(ZScriptTokenType.Semicolon); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected semicolon, got {0}", token?.ToString() ?? "<EOF>"); } break; } // read array List <ZScriptToken> arrayDimTokens = ReadEverythingUntil(t, ZScriptTokenType.CloseSquare, false, false); if (field.ArrayDimensions == null) { field.ArrayDimensions = new List <List <ZScriptToken> >(); } field.ArrayDimensions.Add(arrayDimTokens); token = t.ExpectToken(ZScriptTokenType.CloseSquare); if (!(token?.IsValid ?? true)) { throw new ShaderCompileException("Expected closing square brace, got {0}", token?.ToString() ?? "<EOF>"); } } }
public static ShaderGroup Compile(string src) { ShaderGroup output = new ShaderGroup(); using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(src))) using (BinaryReader br = new BinaryReader(ms)) { ZScriptTokenizer t = new ZScriptTokenizer(br); // main cycle // in the root scope, we allow three blocks: // - uniforms{} // - functions{} // - shader <name> {} // everything else is a syntax error. while (true) { t.SkipWhitespace(); ZScriptToken token = t.ExpectToken(ZScriptTokenType.Identifier); if (token == null) { break; } if (!token.IsValid) { throw new ShaderCompileException("Expected 'uniforms', 'functions', or 'shader'; got {0}", token.ToString()); } switch (token.Value) { case "uniforms": CompileUniforms(output, t); break; case "functions": CompileFunctions(output, t); break; case "shader": CompileShader(output, t); break; default: throw new ShaderCompileException("Expected 'uniforms', 'functions', or 'shader'; got {0}", token.ToString()); } } // done parsing, postprocess - apply parents foreach (Shader s in output.Shaders) { List <string> parents = new List <string>(); parents.Add(s.Name); Shader p = s; while (p.ParentName != null && p.ParentName != "") { string parentName = p.ParentName; if (parents.Contains(parentName)) { throw new ShaderCompileException("Recursive parent shader {0} found", parentName); } parents.Add(parentName); p = output.GetShader(parentName); if (p == null) { throw new ShaderCompileException("Parent shader {0} not found", parentName); } if (s.In == null) { s.In = p.In; } if (s.Out == null) { s.Out = p.Out; } if (s.V2F == null) { s.V2F = p.V2F; } if (s.SourceFragment == null) { s.SourceFragment = p.SourceFragment; s.SourceFragmentLine = p.SourceFragmentLine; } if (s.SourceVertex == null) { s.SourceVertex = p.SourceVertex; s.SourceVertexLine = p.SourceVertexLine; } // add functions from parent foreach (ShaderFunction func in p.Functions) { if (s.GetFunction(func.Name) == null) { s.Functions.Add(func); } } } } return(output); } }