private string GetFunctionSource(List <ZScriptToken> shaderSource) { List <string> functionNames = new List <string>(); GetReferencedFunctions(shaderSource, functionNames); string preOutput = ""; string output = ""; foreach (string functionName in functionNames) { ShaderFunction func = GetFunction(functionName); if (func == null) { func = Group.GetFunction(functionName); } if (func == null) { continue; } string funcOutput = string.Format("#line {0}\n", func.Line) + func.ReturnValue + " " + func.Name + " (" + GetTokenListSource(func.Arguments) + ")"; preOutput += funcOutput + ";\n"; funcOutput += " {\n" + string.Format("#line {0}\n", func.CodeLine) + GetTokenListSource(func.Code) + "}\n"; output += funcOutput; } return(preOutput + "\n" + output); }
private void GetReferencedFunctions(List <ZScriptToken> source, List <string> functions) { for (int i = 0; i < source.Count; i++) { ZScriptToken token = source[i]; if (token.Type != ZScriptTokenType.Identifier) { continue; } if (functions.Contains(token.Value)) { continue; } // check token to the left - needs to not be identifier. // check token to the right - needs to be open parenthesis // --- // the idea is that we can differentiate pixel = desaturate(...) from vec4 desaturate(1,1,1,1) // ZScriptTokenType leftToken = ZScriptTokenType.Invalid; ZScriptTokenType rightToken = ZScriptTokenType.Invalid; for (int j = i - 1; j >= 0; j--) { ZScriptTokenType tt = source[j].Type; if (!IsWhitespace(tt)) { leftToken = tt; break; } } for (int j = i + 1; j < source.Count; j++) { ZScriptTokenType tt = source[j].Type; if (!IsWhitespace(tt)) { rightToken = tt; break; } } if (leftToken != ZScriptTokenType.Identifier && rightToken == ZScriptTokenType.OpenParen) { // find function functions.Add(token.Value); // if function was found, recurse and find functions it may depend on. ShaderFunction func = GetFunction(token.Value); if (func == null) { func = Group.GetFunction(token.Value); } if (func != null) { GetReferencedFunctions(func.Code, functions); } } } }
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 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); } }