You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

2950 lines
111 KiB

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
namespace UnityEditor.Rendering.HighDefinition
{
internal class HlslParser
{
internal struct TypeInfo
{
// If a non-struct, native type describes it. Otherwise, identifier for lookup. Struct for identifier
// must be at global scope (no structs defined in structs).
internal HlslNativeType nativeType;
internal string identifier;
internal int arrayDims;
internal ApdAllowedState allowedState;
static internal TypeInfo MakeNativeType(HlslNativeType nativeType, int dims, ApdAllowedState allowedState)
{
TypeInfo ret = new TypeInfo();
ret.nativeType = nativeType;
ret.identifier = "";
ret.arrayDims = dims;
ret.allowedState = allowedState;
return ret;
}
static internal TypeInfo MakeStruct(string structName, int dims)
{
TypeInfo ret = new TypeInfo();
ret.nativeType = HlslNativeType._struct;
ret.identifier = structName;
ret.arrayDims = dims;
ret.allowedState = ApdAllowedState.OnlyFpd;
return ret;
}
internal string DebugString()
{
string arrayStr = (arrayDims == 0) ? "" : "[" + arrayDims.ToString() + "]";
if (nativeType == HlslNativeType._struct)
{
return identifier + arrayStr;
}
else
{
return nativeType.ToString() + arrayStr;
}
}
}
internal struct TokenRunner
{
internal int currToken;
internal bool isValid;
internal string lastErr;
internal int tokenErr;
internal HlslParser parser;
internal bool allowPreprocessor;
internal string debugPrev;
internal string debugCurr; // curr = top
static internal TokenRunner MakeRunner(HlslParser srcParser, int srcToken)
{
TokenRunner ret = new TokenRunner();
ret.currToken = srcToken;
ret.isValid = true;
ret.lastErr = "";
ret.parser = srcParser;
ret.tokenErr = -1;
ret.allowPreprocessor = true;
ret.debugPrev = "";
ret.debugCurr = ""; // curr = top
return ret;
}
// Not technically needed because this is a struct, but this makes it explicit that it's a copy,
// and if in the future we have any copyable data (like an array) then we can adjust MakeCopy()
// and the functions that call it will "just work".
internal TokenRunner MakeCopy()
{
TokenRunner runner = this;
return runner;
}
internal bool IsEof()
{
return TopToken() == HlslToken._eof;
}
internal HlslToken TopToken()
{
if (currToken < parser.tokenizer.foundTokens.Count)
{
return parser.tokenizer.foundTokens[currToken].tokenType;
}
return HlslToken._eof;
}
internal HlslToken LookAheadToken(int numAhead)
{
TokenRunner lookAhead = MakeCopy();
for (int i = 0; i < numAhead; i++)
{
lookAhead.AdvanceToken();
}
return lookAhead.TopToken();
}
internal string TopData()
{
if (currToken < parser.tokenizer.foundTokens.Count)
{
return parser.tokenizer.foundTokens[currToken].data;
}
return "<invalid>";
}
internal HlslTokenizer.CodeSection TopCodeSection()
{
if (currToken < parser.tokenizer.foundTokens.Count)
{
return parser.tokenizer.foundTokens[currToken].codeSection;
}
return HlslTokenizer.CodeSection.Unknown;
}
internal string LookAheadData(int numAhead)
{
TokenRunner lookAhead = MakeCopy();
for (int i = 0; i < numAhead; i++)
{
lookAhead.AdvanceToken();
}
return lookAhead.TopData();
}
// get the next non-whitespace, non-define, non-comment token
internal void AdvanceToken()
{
// only go to the next token if we haven't gone past the edge of the list yet
if (currToken < parser.tokenizer.foundTokens.Count)
{
currToken++;
while (currToken < parser.tokenizer.foundTokens.Count &&
!HlslTokenizer.IsTokenReserved(TopToken()) &&
!HlslTokenizer.IsTokenOperator(TopToken()) &&
!HlslTokenizer.IsTokenLiteral(TopToken()) &&
!HlslTokenizer.IsTokenIdentifier(TopToken()))
{
// If preprocessor is not being passed through, error out if we hit one. In general,
// preprocessor macros are allowed in global scope, but not inside functions.
if (!allowPreprocessor)
{
if (TopToken() == HlslToken._preprocessor)
{
string debugData = TopData();
Error("Invalid place for preprocessor token.");
}
}
currToken++;
}
debugPrev = debugCurr;
debugCurr = (currToken < parser.tokenizer.foundTokens.Count) ? parser.tokenizer.foundTokens[currToken].data : "<eof>";
}
}
// Accept: If the condition is expected, then fetch and advance. Otherwise, return false.
// Check: See if the condition is expected, but do not advance.
// Expect: Assume that the condition is true. Otherwise, it's an error.
internal bool Check(HlslToken token)
{
HlslToken actual = TopToken();
return actual == token;
}
internal bool CheckLiteralOrBool()
{
HlslToken actual = TopToken();
return HlslTokenizer.IsTokenLiteralOrBool(actual);
}
internal void Error(string err)
{
// only apply the error if we haven't already applied one
if (isValid)
{
isValid = false;
lastErr = err;
tokenErr = currToken;
currToken = parser.tokenizer.foundTokens.Count;
parser.tree.ApplyError(err, tokenErr);
}
}
// Not really an assert, but unsure of the best word. If condition is true, all good.
// If condition is false, apply the error, and set the next token to EOF. This allows
// us to abandon parsing without having "if(condition) return err;" all over the code.
internal void ParseAssert(bool condition, string err)
{
if (!condition)
{
Error(err);
}
}
internal int GetStructDefinitionFromString(string name)
{
int ret = -1;
bool isUnityStruct = parser.unityReserved.IsIdentifierUnityStruct(name);
if (isUnityStruct)
{
HlslTree.NodeUnityStruct node = new HlslTree.NodeUnityStruct();
node.unityName = name;
ret = parser.tree.AddNode(node, parser, isValid);
}
else
{
int stackIndex = parser.tree.structLookupStack.Count - 1;
while (ret < 0 && stackIndex >= 0)
{
if (parser.tree.structLookupStack[stackIndex].ContainsKey(name))
{
ret = parser.tree.structLookupStack[stackIndex][name];
}
stackIndex--;
}
}
return ret;
}
internal int GetTokenStructType()
{
int foundNodeId = -1;
if (TopToken() == HlslToken._identifier)
{
string name = parser.tokenizer.GetTokenData(currToken);
foundNodeId = GetStructDefinitionFromString(name);
}
return foundNodeId;
}
internal bool Expect(HlslToken token)
{
if (TopToken() == token)
{
AdvanceToken();
return true;
}
Error("Expected token: " + token.ToString() + " vs. Actual: " + TopToken().ToString());
return false;
}
internal bool CheckParamModifier()
{
if (TopToken() == HlslToken._in ||
TopToken() == HlslToken._out ||
TopToken() == HlslToken._inout)
{
return true;
}
return false;
}
internal bool Accept(HlslToken token)
{
if (TopToken() == token)
{
AdvanceToken();
return true;
}
return false;
}
internal bool CheckType()
{
if (HlslTokenizer.IsTokenNativeType(TopToken()) ||
HlslTokenizer.IsTokenIdentifier(TopToken()) ||
TopToken() == HlslToken._void)
{
return true;
}
return false;
}
internal bool AcceptType()
{
bool ret = CheckType();
if (ret)
{
AdvanceToken();
}
return ret;
}
}
// https://en.cppreference.com/w/cpp/language/operator_precedence
internal int GetOperatorPrcedence(HlslOp op)
{
int ret = -1;
switch (op)
{
case HlslOp.ScopeReslution: // ::
ret = 1;
break;
case HlslOp.PostIncrement: // ++
case HlslOp.PostDecrement: // --
case HlslOp.FunctionalCast: // func()
case HlslOp.FunctionalCall: // func()
case HlslOp.Subscript: // []
case HlslOp.MemberAccess: // .
ret = 2;
break;
case HlslOp.PreIncrement: // ++
case HlslOp.PreDecrement: // --
case HlslOp.UnaryPlus: // +
case HlslOp.UnaryMinus: // -
case HlslOp.LogicalNot: // !
case HlslOp.BitwiseNot: // ~
case HlslOp.CStyleCast: // (int)
case HlslOp.Dereference: // * - not legal in HLSL?
case HlslOp.AddressOf: // & - not legal in HLSL?
case HlslOp.Sizeof: // sizeof
ret = 3;
break;
case HlslOp.PointerToMemberDot: // .* - not legal in HLSL
case HlslOp.PointerToMemorArrow: // ->* - not legal in HLSL
ret = 4;
break;
case HlslOp.Mul: // *
case HlslOp.Div: // /
case HlslOp.Mod: // %
ret = 5;
break;
case HlslOp.Add: // +
case HlslOp.Sub: // -
ret = 6;
break;
case HlslOp.ShiftL: // <<
case HlslOp.ShiftR: // >>
ret = 7;
break;
case HlslOp.ThreeWayCompare: // <=> - never used this
ret = 8;
break;
case HlslOp.LessThan: // <
case HlslOp.GreaterThan: // >
case HlslOp.LessEqual: // <=
case HlslOp.GreaterEqual: // >=
ret = 9;
break;
case HlslOp.CompareEqual: // ==
case HlslOp.NotEqual: // !=
ret = 10;
break;
case HlslOp.BitwiseAnd: // &
ret = 11;
break;
case HlslOp.BitwiseXor: // ^
ret = 12;
break;
case HlslOp.BitwiseOr: // |
ret = 13;
break;
case HlslOp.LogicalAnd: // &&
ret = 14;
break;
case HlslOp.LogicalOr: // ||
ret = 15;
break;
case HlslOp.TernaryQuestion: // ?
case HlslOp.TernaryColon: // :
case HlslOp.Assignment: // =
case HlslOp.AddEquals: // +=
case HlslOp.SubEquals: // -=
case HlslOp.MulEquals: // *=
case HlslOp.DivEquals: // /=
case HlslOp.ModEquals: // %=
case HlslOp.ShiftLEquals: // <<=
case HlslOp.ShiftREquals: // >>=
case HlslOp.AndEquals: // &=
case HlslOp.XorEquals: // ^=
case HlslOp.OrEquals: // |=
ret = 16;
break;
case HlslOp.Comma: // ,
ret = 17;
break;
case HlslOp.Invalid:
// When parsing, we generally only want to parse operators with priority lower than the
// existing priority, so by making 18 we default to not parsing an invalid operator in
// most cases to gracefully error when parsing fails.
ret = 18;
break;
default:
HlslUtil.ParserAssert(false);
break;
}
return ret;
}
internal int GetWeakestPrcedence()
{
return 17;
}
internal bool GetOperatorIsLeftToRight(HlslOp op)
{
bool ret = true;
switch (op)
{
case HlslOp.ScopeReslution: // ::
case HlslOp.PostIncrement: // ++
case HlslOp.PostDecrement: // --
case HlslOp.FunctionalCast: // func()
case HlslOp.FunctionalCall: // func()
case HlslOp.Subscript: // []
case HlslOp.MemberAccess: // .
ret = true;
break;
case HlslOp.PreIncrement: // ++
case HlslOp.PreDecrement: // --
case HlslOp.UnaryPlus: // +
case HlslOp.UnaryMinus: // -
case HlslOp.LogicalNot: // !
case HlslOp.BitwiseNot: // ~
case HlslOp.CStyleCast: // (int)
case HlslOp.Dereference: // * - not legal in HLSL?
case HlslOp.AddressOf: // & - not legal in HLSL?
case HlslOp.Sizeof: // sizeof
ret = false;
break;
case HlslOp.PointerToMemberDot: // .* - not legal in HLSL
case HlslOp.PointerToMemorArrow: // ->* - not legal in HLSL
case HlslOp.Mul: // *
case HlslOp.Div: // /
case HlslOp.Mod: // %
case HlslOp.Add: // +
case HlslOp.Sub: // -
case HlslOp.ShiftL: // <<
case HlslOp.ShiftR: // >>
case HlslOp.ThreeWayCompare: // <=> - never used this
case HlslOp.LessThan: // <
case HlslOp.GreaterThan: // >
case HlslOp.LessEqual: // <=
case HlslOp.GreaterEqual: // >=
case HlslOp.CompareEqual: // ==
case HlslOp.NotEqual: // !=
case HlslOp.BitwiseAnd: // &
case HlslOp.BitwiseXor: // ^
case HlslOp.BitwiseOr: // |
case HlslOp.LogicalAnd: // &&
case HlslOp.LogicalOr: // ||
ret = true;
break;
case HlslOp.TernaryQuestion: // ?
case HlslOp.TernaryColon: // :
case HlslOp.Assignment: // =
case HlslOp.AddEquals: // +=
case HlslOp.SubEquals: // -=
case HlslOp.MulEquals: // *=
case HlslOp.DivEquals: // /=
case HlslOp.ModEquals: // %=
case HlslOp.ShiftLEquals: // <<=
case HlslOp.ShiftREquals: // >>=
case HlslOp.AndEquals: // &=
case HlslOp.XorEquals: // ^=
case HlslOp.OrEquals: // |=
ret = false;
break;
case HlslOp.Comma: // ,
ret = true;
break;
default:
HlslUtil.ParserAssert(false);
break;
}
return ret;
}
static internal bool GetOperatorIsBinary(HlslOp op)
{
bool ret = true;
switch (op)
{
case HlslOp.ScopeReslution: // ::
ret = true;
break;
case HlslOp.PostIncrement: // ++
case HlslOp.PostDecrement: // --
case HlslOp.FunctionalCast: // func()
case HlslOp.FunctionalCall: // func()
case HlslOp.Subscript: // []
case HlslOp.MemberAccess: // .
ret = false;
break;
case HlslOp.PreIncrement: // ++
case HlslOp.PreDecrement: // --
case HlslOp.UnaryPlus: // +
case HlslOp.UnaryMinus: // -
ret = false;
break;
case HlslOp.LogicalNot: // !
case HlslOp.BitwiseNot: // ~
ret = false;
break;
case HlslOp.CStyleCast: // (int)
case HlslOp.Dereference: // * - not legal in HLSL?
case HlslOp.AddressOf: // & - not legal in HLSL?
case HlslOp.Sizeof: // sizeof
ret = false;
break;
case HlslOp.PointerToMemberDot: // .* - not legal in HLSL
case HlslOp.PointerToMemorArrow: // ->* - not legal in HLSL
ret = false;
break;
case HlslOp.Mul: // *
case HlslOp.Div: // /
case HlslOp.Mod: // %
case HlslOp.Add: // +
case HlslOp.Sub: // -
case HlslOp.ShiftL: // <<
case HlslOp.ShiftR: // >>
ret = true;
break;
case HlslOp.ThreeWayCompare: // <=> - never used this
ret = false; // ??
break;
case HlslOp.LessThan: // <
case HlslOp.GreaterThan: // >
case HlslOp.LessEqual: // <=
case HlslOp.GreaterEqual: // >=
case HlslOp.CompareEqual: // ==
case HlslOp.NotEqual: // !=
case HlslOp.BitwiseAnd: // &
case HlslOp.BitwiseXor: // ^
case HlslOp.BitwiseOr: // |
case HlslOp.LogicalAnd: // &&
case HlslOp.LogicalOr: // ||
ret = true;
break;
case HlslOp.TernaryQuestion: // ?
case HlslOp.TernaryColon: // :
ret = false;
break;
case HlslOp.Assignment: // =
case HlslOp.AddEquals: // +=
case HlslOp.SubEquals: // -=
case HlslOp.MulEquals: // *=
case HlslOp.DivEquals: // /=
case HlslOp.ModEquals: // %=
case HlslOp.ShiftLEquals: // <<=
case HlslOp.ShiftREquals: // >>=
case HlslOp.AndEquals: // &=
case HlslOp.XorEquals: // ^=
case HlslOp.OrEquals: // |=
ret = true;
break;
case HlslOp.Comma: // ,
ret = true;
break;
default:
HlslUtil.ParserAssert(false);
break;
}
return ret;
}
internal static Dictionary<HlslToken, HlslNativeType> GenerateTokenToNativeTable()
{
Dictionary<HlslToken, HlslNativeType> table = new Dictionary<HlslToken, HlslNativeType>();
Dictionary<string, HlslNativeType> nativeStringToType = new Dictionary<string, HlslNativeType>();
for (HlslNativeType i = HlslNativeType._unknown; i <= HlslNativeType._struct; i++)
{
string key = i.ToString();
nativeStringToType.Add(key, i);
}
for (HlslToken i = HlslToken._float; i < HlslToken._struct; i++)
{
HlslToken key = i;
string value = i.ToString();
if (nativeStringToType.ContainsKey(value))
{
HlslNativeType native = nativeStringToType[value];
table.Add(key, native);
}
}
return table;
}
internal HlslParser(HlslTokenizer srcTokenizer, HlslUnityReserved srcUnityReserved, List<string> srcIgnoredFuncs)
{
tokenizer = srcTokenizer;
unityReserved = srcUnityReserved;
tokenToNativeTable = GenerateTokenToNativeTable();
ignoredFuncs = srcIgnoredFuncs;
}
internal void ParseTokens(HlslTree srcTree)
{
TokenRunner runner = TokenRunner.MakeRunner(this, 0);
tree = srcTree;
int topLevel = ParseTopLevel(runner);
tree.topLevelNode = topLevel;
}
internal int CreateNodeForTopToken(ref TokenRunner runner)
{
HlslTree.NodeToken node = new HlslTree.NodeToken();
node.srcTokenId = runner.currToken;
int nodeId = runner.parser.tree.AddNode(node, this, runner.isValid);
return nodeId;
}
internal int CreateNodeForTokenId(ref TokenRunner runner, int tokenId)
{
HlslTree.NodeToken node = new HlslTree.NodeToken();
node.srcTokenId = tokenId;
int nodeId = runner.parser.tree.AddNode(node, this, runner.isValid);
return nodeId;
}
internal int CreateNodeForNativeType(ref TokenRunner runner, HlslNativeType type)
{
HlslTree.NodeNativeType node = new HlslTree.NodeNativeType();
node.nativeType = type;
int nodeId = runner.parser.tree.AddNode(node, this, runner.isValid);
return nodeId;
}
internal HlslTree.VariableInfo GetVariableInfoFromDecl(HlslTree.NodeDeclaration nodeDecl, int declNodeId)
{
HlslTree.VariableInfo variableInfo = new HlslTree.VariableInfo();
variableInfo.typeInfo.nativeType = nodeDecl.nativeType;
if (nodeDecl.nativeType == HlslNativeType._struct)
{
HlslTree.Node baseNode = tree.allNodes[nodeDecl.structId];
if (baseNode is HlslTree.NodeToken tokenNode)
{
variableInfo.typeInfo.identifier = tokenizer.GetTokenData(tokenNode.srcTokenId);
}
else if (baseNode is HlslTree.NodeStruct structNode)
{
variableInfo.typeInfo.identifier = tokenizer.GetTokenData(structNode.nameTokenId);
}
else if (baseNode is HlslTree.NodeUnityStruct unityNode)
{
variableInfo.typeInfo.identifier = unityNode.unityName;
}
else
{
variableInfo.typeInfo.nativeType = HlslNativeType._unknown;
variableInfo.typeInfo.identifier = "<error>";
}
variableInfo.structId = nodeDecl.structId;
}
else
{
variableInfo.typeInfo.identifier = HlslUtil.GetNativeTypeString(nodeDecl.nativeType);
variableInfo.structId = -1;
}
variableInfo.variableName = nodeDecl.variableName;
variableInfo.declId = declNodeId;
variableInfo.typeInfo.arrayDims = nodeDecl.arrayDims.Length;
return variableInfo;
}
internal int ParseDeclaration(out HlslTree.VariableInfo variableInfo, ref TokenRunner runner, bool trailingSemicolon, bool allowModifer)
{
variableInfo = new HlslTree.VariableInfo();
HlslTree.NodeDeclaration nodeDecl = new HlslTree.NodeDeclaration();
// if we allow modifers, see if we have one
if (runner.TopToken() == HlslToken._in ||
runner.TopToken() == HlslToken._out ||
runner.TopToken() == HlslToken._inout)
{
nodeDecl.modifierTokenId = runner.currToken;
runner.AdvanceToken();
}
ParseDeclTypeAndIdentifier(ref nodeDecl, ref runner, false);
if (trailingSemicolon)
{
// for structs, we need a semicolon after each field
runner.Expect(HlslToken._semicolon);
}
int ret = tree.AddNode(nodeDecl, this, runner.isValid);
variableInfo = GetVariableInfoFromDecl(nodeDecl, ret);
if (!variableInfo.IsValid())
{
runner.Error("Node declaration type invalid.");
}
return ret;
}
internal int ParseStruct(ref TokenRunner runner)
{
{
bool ok = runner.Accept(HlslToken._struct);
runner.ParseAssert(ok, "Expected struct.");
}
HlslTree.NodeStruct nodeStruct = new HlslTree.NodeStruct();
{
bool ok = runner.Check(HlslToken._identifier);
runner.ParseAssert(ok, "Expected identifier");
nodeStruct.nameTokenId = runner.currToken;
runner.AdvanceToken();
}
runner.Expect(HlslToken._brace_l);
List<int> declarations = new List<int>();
List<HlslTree.VariableInfo> variables = new List<HlslTree.VariableInfo>();
while (runner.CheckType())
{
HlslTree.VariableInfo variableInfo;
int declId = ParseDeclaration(out variableInfo, ref runner, true, false);
declarations.Add(declId);
variables.Add(variableInfo);
}
runner.Expect(HlslToken._brace_r);
nodeStruct.declarations = declarations.ToArray();
string structName = runner.parser.tokenizer.GetTokenData(nodeStruct.nameTokenId);
// For now, we won't allow to actually declare member functions in structs. They are
// allowed in unity reserved structures, but not the ones defined in the surface
// shader.
HlslUtil.StructInfo structInfo = new HlslUtil.StructInfo();
structInfo.identifier = tokenizer.GetTokenData(nodeStruct.nameTokenId);
structInfo.fields = new HlslUtil.FieldInfo[variables.Count];
structInfo.prototypes = new HlslUtil.PrototypeInfo[0];
for (int i = 0; i < variables.Count; i++)
{
HlslTree.VariableInfo variableInfo = variables[i];
HlslUtil.FieldInfo fieldInfo = new HlslUtil.FieldInfo();
fieldInfo.identifier = variableInfo.variableName;
fieldInfo.typeInfo = variableInfo.typeInfo;
//fieldInfo.typeInfo.arrayDims = variableInfo.typeInfo.dims;
fieldInfo.semantics = new string[0];
structInfo.fields[i] = fieldInfo;
}
nodeStruct.structInfoId = tree.AddStructInfo(structInfo);
int structNodeId = tree.AddNode(nodeStruct, this, runner.isValid);
tree.AddStructIdentifier(structName, structNodeId);
return structNodeId;
}
internal int ParseFunctionPrototype(ref TokenRunner runner, out List<HlslTree.VariableInfo> foundVariables)
{
foundVariables = new List<HlslTree.VariableInfo>();
HlslTree.NodeFunctionPrototype proto = new HlslTree.NodeFunctionPrototype();
// first should be an identifier for return type
bool valid = runner.CheckType();
if (!valid)
{
runner.Error("Expected type at function.");
return -1;
}
proto.returnTokenId = runner.currToken;
runner.AdvanceToken();
// next should be an identifier for the function name
if (runner.Check(HlslToken._identifier))
{
proto.nameTokenId = runner.currToken;
runner.AdvanceToken();
// next '('
runner.Expect(HlslToken._parense_l);
bool firstIteration = true;
List<int> declarations = new List<int>();
// keep parsing declarations until we find a ')'
while (runner.isValid && !runner.Check(HlslToken._parense_r))
{
// if this is not the first iteration, then we whould expect a comma
if (!firstIteration)
{
runner.Expect(HlslToken._comma);
}
HlslTree.VariableInfo variableInfo;
int foundDecl = ParseDeclaration(out variableInfo, ref runner, false, true);
if (foundDecl < 0)
{
// if we failed to find a declaration, than the runner should no longer be valid, which is
// important so that we exit the while loop
HlslUtil.ParserAssert(!runner.isValid);
}
foundVariables.Add(variableInfo);
declarations.Add(foundDecl);
// no longer first iteration, so following iterations will need a comma separator
firstIteration = false;
}
runner.Expect(HlslToken._parense_r);
proto.declarations = declarations.ToArray();
}
int protoId = -1;
if (runner.isValid)
{
protoId = tree.AddNode(proto, this, runner.isValid);
}
return protoId;
}
internal bool TryCStyleCast(ref TokenRunner runner, out int foundTokenId, out HlslToken foundTokenType, out int foundStructId, out string dstCastName)
{
dstCastName = "";
foundTokenId = -1;
foundTokenType = HlslToken._invalid;
foundStructId = -1;
// a big hacky, we really should be pushing/popping these errors
List<HlslTree.ErrInfo> errs = new List<HlslTree.ErrInfo>(tree.errList);
runner.Expect(HlslToken._parense_l);
HlslToken token = runner.TopToken();
bool isNativeType = HlslTokenizer.IsTokenNativeType(token);
bool isIdentifier = HlslTokenizer.IsTokenIdentifier(token);
if (isNativeType && token != HlslToken._struct)
{
foundTokenId = runner.currToken;
foundTokenType = token;
dstCastName = HlslTokenizer.AsReservedString(token);
}
else if (isIdentifier)
{
string name = runner.TopData();
int structId = runner.GetStructDefinitionFromString(name);
if (structId >= 0)
{
foundTokenId = runner.currToken;
foundTokenType = HlslToken._identifier;
dstCastName = name;
foundStructId = structId;
runner.AdvanceToken();
}
else
{
runner.isValid = false;
}
}
else
{
runner.isValid = false;
}
runner.Expect(HlslToken._parense_r);
if (!runner.isValid)
{
dstCastName = "";
foundTokenId = -1;
foundTokenType = HlslToken._invalid;
}
// revert any errors if we added them
tree.errList = errs;
return runner.isValid;
}
// try to parse the next token as an operator, but only legal if the op is <= precedence
internal bool TryParseNextOp(ref TokenRunner parentRunner, out HlslOp dstOp, out string dstCastName, out int dstTokenId, out int dstCastStructId, int precedence, bool hasPreceedingVariable)
{
TokenRunner runner = parentRunner.MakeCopy();
dstOp = HlslOp.Invalid;
dstCastName = "";
dstTokenId = -1;
dstCastStructId = -1;
HlslToken foundToken = runner.TopToken();
HlslOp foundOp = HlslOp.Invalid;
int foundTokenId = runner.currToken;
string foundCastName = "";
int foundStructId = -1;
switch (foundToken)
{
case HlslToken._colon_colon: // ::
foundOp = HlslOp.ScopeReslution;
break;
case HlslToken._plus_plus: // ++
foundOp = hasPreceedingVariable ? HlslOp.PostIncrement : HlslOp.PreIncrement;
break;
case HlslToken._minus_minus: // --
foundOp = hasPreceedingVariable ? HlslOp.PostDecrement : HlslOp.PreDecrement;
break;
case HlslToken._parense_l: // (
{
int cstyleTokenId;
HlslToken cstyleToken;
string cstyleCastName;
int cstyleStructId;
// check for c-style cast
bool foundCast = TryCStyleCast(ref runner, out cstyleTokenId, out cstyleToken, out cstyleStructId, out cstyleCastName);
if (foundCast)
{
foundTokenId = cstyleTokenId;
foundToken = cstyleToken;
foundCastName = cstyleCastName;
foundStructId = cstyleStructId;
foundOp = HlslOp.CStyleCast;
}
else
{
foundOp = HlslOp.Invalid;
}
}
break;
case HlslToken._parense_r: // )
foundOp = HlslOp.Invalid; // nope
break;
case HlslToken._bracket_l: // [
foundOp = HlslOp.Subscript;
break;
case HlslToken._bracket_r: // ]
foundOp = HlslOp.Invalid; // nope
break;
case HlslToken._period: // .
foundOp = HlslOp.MemberAccess;
break;
case HlslToken._plus: // +
foundOp = hasPreceedingVariable ? HlslOp.Add : HlslOp.UnaryPlus;
break;
case HlslToken._minus: // -
foundOp = hasPreceedingVariable ? HlslOp.Sub : HlslOp.UnaryMinus;
break;
case HlslToken._logical_not: // !
foundOp = HlslOp.LogicalNot;
break;
case HlslToken._bitwise_not: // ~
foundOp = HlslOp.BitwiseNot;
break;
case HlslToken._mul: // *
foundOp = HlslOp.Mul;
break;
case HlslToken._div: // /
foundOp = HlslOp.Div;
break;
case HlslToken._modulo: // %
foundOp = HlslOp.Mod;
break;
case HlslToken._shift_l: // <<
foundOp = HlslOp.ShiftL;
break;
case HlslToken._shift_r: // >>
foundOp = HlslOp.ShiftR;
break;
case HlslToken._less_than: // <
foundOp = HlslOp.LessThan;
break;
case HlslToken._greater_than: // >
foundOp = HlslOp.GreaterThan;
break;
case HlslToken._less_equal: // <=
foundOp = HlslOp.LessEqual;
break;
case HlslToken._greater_equal: // >=
foundOp = HlslOp.GreaterEqual;
break;
case HlslToken._equivalent: // ==
foundOp = HlslOp.CompareEqual;
break;
case HlslToken._not_equal: // !=
foundOp = HlslOp.NotEqual;
break;
case HlslToken._bitwise_and: // &
foundOp = HlslOp.Invalid;
break;
case HlslToken._bitwise_xor: // ^
foundOp = HlslOp.BitwiseXor;
break;
case HlslToken._bitwise_or: // |
foundOp = HlslOp.BitwiseOr;
break;
case HlslToken._logical_and: // &&
foundOp = HlslOp.LogicalAnd;
break;
case HlslToken._logical_or: // ||
foundOp = HlslOp.LogicalOr;
break;
case HlslToken._question: // ?
foundOp = HlslOp.TernaryQuestion;
break;
case HlslToken._colon: // :
// actually, at this part of the code, we actually want to reject
// the : operator so that it can be handled by the operator higher
// in the tree
//foundOp = HlslOp.TernaryColon;
foundOp = HlslOp.Invalid;
break;
case HlslToken._assignment: // =
foundOp = HlslOp.Assignment;
break;
case HlslToken._add_equals: // +=
foundOp = HlslOp.AddEquals;
break;
case HlslToken._sub_equals: // -=
foundOp = HlslOp.SubEquals;
break;
case HlslToken._mul_equals: // *=
foundOp = HlslOp.MulEquals;
break;
case HlslToken._div_equals: // /=
foundOp = HlslOp.DivEquals;
break;
case HlslToken._mod_equals: // %=
foundOp = HlslOp.ModEquals;
break;
case HlslToken._shift_l_equals: // <<=
foundOp = HlslOp.ShiftLEquals;
break;
case HlslToken._shift_r_equals: // >>=
foundOp = HlslOp.ShiftREquals;
break;
case HlslToken._and_equals: // &=
foundOp = HlslOp.AndEquals;
break;
case HlslToken._or_equals: // |=
foundOp = HlslOp.OrEquals;
break;
case HlslToken._xor_equals: // ^=
foundOp = HlslOp.XorEquals;
break;
case HlslToken._comma: // ,
foundOp = HlslOp.Comma;
break;
case HlslToken._brace_l: // {
foundOp = HlslOp.Invalid; // nope
break;
case HlslToken._brace_r: // }
foundOp = HlslOp.Invalid; // nope
break;
case HlslToken._semicolon: // ;
foundOp = HlslOp.Invalid; // nope
break;
default:
// not found, is already valid
break;
}
bool doesOpRequirePrecedingVariable = true;
switch (foundOp)
{
case HlslOp.PreDecrement:
case HlslOp.PreIncrement:
case HlslOp.UnaryMinus:
case HlslOp.UnaryPlus:
case HlslOp.BitwiseNot:
case HlslOp.LogicalNot:
case HlslOp.CStyleCast:
doesOpRequirePrecedingVariable = false;
break;
default:
doesOpRequirePrecedingVariable = true;
break;
}
if (foundOp != HlslOp.Invalid && doesOpRequirePrecedingVariable == hasPreceedingVariable)
{
// cstyle cast advances the full tokens, but all other types do not
if (foundOp != HlslOp.CStyleCast)
{
runner.AdvanceToken();
}
int foundPrecedence = GetOperatorPrcedence(foundOp);
bool isLeftToRight = GetOperatorIsLeftToRight(foundOp);
// by default, we parse by making a child node to the rhs of the our chain,
// so if we are leftToRight we want to fail if precedence is equal, but succeed
// if we are rightToLeft.
bool validPrecedence = isLeftToRight ? (foundPrecedence < precedence) : (foundPrecedence <= precedence);
if (validPrecedence)
{
dstOp = foundOp;
dstCastName = foundCastName;
dstTokenId = foundTokenId;
dstCastStructId = foundStructId;
parentRunner = runner;
}
}
return dstOp != HlslOp.Invalid;
}
internal int TryParseSingleOpExpression(ref TokenRunner runner, int precedence)
{
HlslOp dstOp;
string dstOpCastName;
int dstOpTokenId;
int foundStructId;
bool foundOp = TryParseNextOp(ref runner, out dstOp, out dstOpCastName, out dstOpTokenId, out foundStructId, precedence, false);
if (foundOp)
{
bool isBinary = GetOperatorIsBinary(dstOp);
if (isBinary)
{
runner.Error("Should not find binary operator at leftmost side of sub-expression.");
foundOp = false;
}
}
int foundExp = -1;
if (foundOp)
{
int opPrecedence = GetOperatorPrcedence(dstOp);
int lhsExp = ParseOperatorExpression(ref runner, opPrecedence);
if (lhsExp >= 0)
{
HlslTree.NodeExpression dstExp = new HlslTree.NodeExpression();
dstExp.lhsNodeId = lhsExp;
dstExp.rhsNodeId = -1;
dstExp.op = dstOp;
dstExp.opTokenId = dstOpTokenId;
dstExp.cstyleCastStructId = foundStructId;
foundExp = runner.parser.tree.AddNode(dstExp, this, runner.isValid);
}
}
return foundExp;
}
// find either a token, or unary expressions and then a followup expression. it can't be a
// binary expression because a subexpression needs to be to the left of it
internal int ParseSingleExpression(ref TokenRunner runner, int precedence)
{
// copy for debugging
TokenRunner debugCopy = runner.MakeCopy();
TokenRunner singleOpRunner = runner.MakeCopy();
int foundExpression = TryParseSingleOpExpression(ref singleOpRunner, precedence);
if (foundExpression >= 0)
{
runner = singleOpRunner;
}
else
{
// is it native? if so, then it's a function cast or a constructor
bool isTopNative = HlslTokenizer.IsTokenNativeType(runner.TopToken());
// is it a struct? if so, then it's a function cast
int topTokenStruct = runner.GetTokenStructType();
if (isTopNative)
{
HlslTree.NodeNativeConstructor node = new HlslTree.NodeNativeConstructor();
node.nativeType = tokenToNativeTable[runner.TopToken()];
node.typeTokenId = runner.currToken;
string typeName = runner.TopData();
runner.AdvanceToken();
runner.Expect(HlslToken._parense_l);
List<int> parsedParams = ParseParamList(ref runner);
runner.Expect(HlslToken._parense_r);
node.paramNodeIds = parsedParams.ToArray();
int numCols = HlslUtil.GetNumCols(node.nativeType);
foundExpression = runner.parser.tree.AddNode(node, this, runner.isValid);
}
else if (topTokenStruct >= 0)
{
// function cast to struct
runner.Error("Unimplemented");
}
else if (runner.Check(HlslToken._identifier))
{
int currTokenId = runner.currToken;
// it's an identifier, so it could either be a variable or a function call
int nameTokenId = runner.currToken;
string name = tokenizer.GetTokenData(nameTokenId);
int variableId;
HlslTree.VariableInfo variableInfo;
bool variableFound = tree.FindVariableInfo(out variableInfo, out variableId, name);
if (!variableFound)
{
// if we didn't find this variable name, then this might actually a function that we don't have
// a definition for.
runner.AdvanceToken();
int parenseTokenId = runner.currToken;
if (runner.Accept(HlslToken._parense_l))
{
HlslTree.NodeExpression node = new HlslTree.NodeExpression();
// if it's actually a function, then parse it as an external function that we treat as passthrough
List<int> parsedParams = ParseParamList(ref runner);
runner.Expect(HlslToken._parense_r);
node.lhsNodeId = CreateNodeForTokenId(ref runner, nameTokenId);
node.opTokenId = parenseTokenId;
node.op = HlslOp.FunctionalCall;
node.paramIds = parsedParams.ToArray();
foundExpression = runner.parser.tree.AddNode(node, this, runner.isValid);
}
else
{
runner.Error("Failed to find variable: " + name);
}
}
else
{
HlslTree.NodeVariable node = new HlslTree.NodeVariable();
node.nameTokenId = nameTokenId;
node.nameVariableId = variableId;
runner.AdvanceToken();
foundExpression = runner.parser.tree.AddNode(node, this, runner.isValid);
}
}
else if (runner.CheckLiteralOrBool())
{
HlslTree.NodeLiteralOrBool node = new HlslTree.NodeLiteralOrBool();
node.nameTokenId = runner.currToken;
runner.AdvanceToken();
foundExpression = runner.parser.tree.AddNode(node, this, runner.isValid);
}
else if (runner.Check(HlslToken._parense_l))
{
// accept '(', parse again with reset precedence, and accept ')'
runner.Accept(HlslToken._parense_l);
int childExpression = ParseExpression(ref runner);
HlslTree.NodeParenthesisGroup nodeExp = new HlslTree.NodeParenthesisGroup();
nodeExp.childNodeId = childExpression;
foundExpression = runner.parser.tree.AddNode(nodeExp, this, runner.isValid);
runner.Accept(HlslToken._parense_r);
}
}
return foundExpression;
}
internal bool IsIdentifierFunctionCall(string identifier)
{
bool isTreeFunc = tree.IsIdentifierFunction(identifier);
bool isUnityFunc = unityReserved.IsIdentifierUnityFunction(identifier);
bool ret = (isTreeFunc || isUnityFunc);
return ret;
}
internal bool IsFunctionCall(TokenRunner runner)
{
bool ret = false;
TokenRunner lookAhead = runner.MakeCopy();
if (lookAhead.Check(HlslToken._identifier))
{
string identifier = lookAhead.TopData();
lookAhead.AdvanceToken();
bool isFunctionCall = IsIdentifierFunctionCall(identifier);
if (isFunctionCall)
{
if (lookAhead.Expect(HlslToken._parense_l))
{
ret = true;
}
}
}
return ret;
}
internal HlslParser.TypeInfo[] GetTypeInfoFromNodeArray(int[] nodes)
{
HlslParser.TypeInfo[] ret = new HlslParser.TypeInfo[nodes.Length];
for (int i = 0; i < nodes.Length; i++)
{
int nodeId = nodes[i];
if (nodeId >= 0)
{
HlslTree.FullTypeInfo fti = tree.fullTypeInfo[nodeId];
ret[i].allowedState = ApdAllowedState.Any; // need to fix this?
ret[i].arrayDims = fti.arrayDims;
ret[i].nativeType = fti.nativeType;
ret[i].identifier = fti.identifier;
}
}
return ret;
}
static int GetVectorLengthConversionCost(int wantedLen, int actualLen)
{
int ret = -1;
if (wantedLen == actualLen)
{
// if they are exact, that's the best match
ret = 0;
}
else if (wantedLen > 1 && actualLen == 1)
{
// we can promote from a scalar to a vector
ret = 1;
}
else if (actualLen > wantedLen)
{
ret = 2;
}
else if (actualLen < wantedLen)
{
ret = 3;
}
else
{
HlslUtil.ParserAssert(false);
}
return ret;
}
static int GetBaseTypeConversionCost(int wantedType, int actualType)
{
// base type order:
// -1: other
// 0: bool
// 1: int
// 2: uint
// 3: half
// 4: float
// in general, we would prefer to be exact, but otherwise going from high to low (i.e. float to half)
// is better than converting from low to high (i.e. half to float)
int ret = 0;
if (wantedType == actualType)
{
// if they are the same, that's the best
ret = 0;
}
else if (actualType > wantedType)
{
ret = actualType - wantedType;
}
else
{
ret = 5 + (wantedType - actualType);
}
return ret;
}
internal bool IsParamListBetter(HlslParser.TypeInfo[] actualParams, HlslParser.TypeInfo[] bestProto, HlslParser.TypeInfo[] currProto)
{
bool isCurrBetter = false;
for (int paramIter = 0; paramIter < actualParams.Length; paramIter++)
{
HlslParser.TypeInfo actual = actualParams[paramIter];
HlslParser.TypeInfo best = bestProto[paramIter];
HlslParser.TypeInfo curr = currProto[paramIter];
HlslNativeType actualBaseType = HlslUtil.GetNativeBaseType(actual.nativeType);
HlslNativeType bestBaseType = HlslUtil.GetNativeBaseType(best.nativeType);
HlslNativeType currBaseType = HlslUtil.GetNativeBaseType(curr.nativeType);
int actualCols = HlslUtil.GetNumCols(actual.nativeType);
int actualRows = HlslUtil.GetNumRows(actual.nativeType);
int bestCols = HlslUtil.GetNumCols(best.nativeType);
int bestRows = HlslUtil.GetNumRows(best.nativeType);
int currCols = HlslUtil.GetNumCols(curr.nativeType);
int currRows = HlslUtil.GetNumRows(curr.nativeType);
bool isActualScalar = HlslUtil.IsNativeTypeScalar(actualBaseType);
bool isBestScalar = HlslUtil.IsNativeTypeScalar(bestBaseType);
bool isCurrScalar = HlslUtil.IsNativeTypeScalar(currBaseType);
// 1. If one type is a scalar and the other is not, bias towards that.
if (isBestScalar != isCurrScalar)
{
isCurrBetter = (isCurrScalar == isActualScalar);
break;
}
bool isBestExact = (best.nativeType == actual.nativeType);
bool isCurrExact = (curr.nativeType == actual.nativeType);
// 2. If one is exact and the other is not, then we have an easy choice
if (isBestExact && !isCurrExact)
{
isCurrBetter = false;
break;
}
if (!isBestExact && isCurrExact)
{
isCurrBetter = true;
break;
}
if (!isActualScalar)
{
// If the param type is not a scalar not much we can
// do unless one of them is exact and the other is not.
// Best we can do is move onto the next param.
}
else
{
int bestCostRows = GetVectorLengthConversionCost(actualRows, bestRows);
int bestCostCols = GetVectorLengthConversionCost(actualRows, bestCols);
int currCostRows = GetVectorLengthConversionCost(actualRows, currRows);
int currCostCols = GetVectorLengthConversionCost(actualRows, currCols);
if (bestCostRows != currCostRows)
{
isCurrBetter = currCostRows < bestCostRows;
break;
}
if (bestCostCols != currCostCols)
{
isCurrBetter = currCostCols < bestCostCols;
break;
}
int actualIndexHelper = HlslUtil.GetIndexHelperForBaseType(actualBaseType);
int bestIndexHelper = HlslUtil.GetIndexHelperForBaseType(bestBaseType);
int currIndexHelper = HlslUtil.GetIndexHelperForBaseType(currBaseType);
int bestCostType = GetBaseTypeConversionCost(actualIndexHelper, bestIndexHelper);
int currCostType = GetBaseTypeConversionCost(actualIndexHelper, currIndexHelper);
if (bestCostType != currCostType)
{
isCurrBetter = currCostType < bestCostType;
break;
}
}
}
return isCurrBetter;
}
internal bool IsValidFunctionParamList(HlslParser.TypeInfo[] actualParams, HlslParser.TypeInfo[] protoParams)
{
// todo, for now just be extremely permissive
bool isValid = (protoParams.Length == actualParams.Length);
for (int paramIter = 0; paramIter < protoParams.Length; paramIter++)
{
// We're going to be extremely permissive here because we allow unknown functions that could return anything.
// The only rule is that if both the prototype and actual args are numeric types (float, int2, half3x3, etc),
// then the actualParam can be promototed to larger type, but can not be demoted down. I.e. float->float4 is
// legal as in implicit conversion but float4->float is not.
HlslParser.TypeInfo protoType = protoParams[paramIter];
HlslParser.TypeInfo actualType = actualParams[paramIter];
int protoRows = HlslUtil.GetNumRows(protoType.nativeType);
int protoCols = HlslUtil.GetNumCols(protoType.nativeType);
// non-numeric types have rows/cols set to 0.
if (protoRows >= 1 && protoCols >= 1)
{
int actualRows = HlslUtil.GetNumRows(actualType.nativeType);
int actualCols = HlslUtil.GetNumCols(actualType.nativeType);
if (protoRows < actualRows || protoCols < actualCols)
{
isValid = false;
break;
}
}
}
return isValid;
}
internal int FindBestFunctionCallForPrototype(string identifier, int[] parsedParams, bool isValid)
{
int ret = -1;
bool isTreeFunc = tree.IsIdentifierFunction(identifier);
bool isUnityFunc = unityReserved.IsIdentifierUnityFunction(identifier);
if (isTreeFunc)
{
HlslParser.TypeInfo[] parsedTypeInfo = GetTypeInfoFromNodeArray(parsedParams);
// find the node id
int[] prototypeIds = tree.GetNodePrototypeIdsForFunction(identifier);
int bestMatch = -1;
HlslParser.TypeInfo[] bestParamInfo = new HlslParser.TypeInfo[0];
for (int protoIter = 0; protoIter < prototypeIds.Length; protoIter++)
{
int protoFlatId = prototypeIds[protoIter];
int protoNodeId = tree.parsedFuncStructData.allPrototypes[protoFlatId].uniqueId;
HlslTree.Node baseNode = tree.allNodes[protoNodeId];
if (baseNode is HlslTree.NodeFunctionPrototype)
{
HlslTree.NodeFunctionPrototype protoNode = (HlslTree.NodeFunctionPrototype)baseNode;
// no default params, so size must exactly match
if (protoNode.declarations.Length == parsedParams.Length)
{
// get the parameter types
HlslParser.TypeInfo[] protoDeclInfo = GetTypeInfoFromNodeArray(protoNode.declarations);
bool isMatch = IsValidFunctionParamList(parsedTypeInfo, protoDeclInfo);
if (isMatch)
{
bool isBetter = false;
if (bestMatch < 0)
{
isBetter = true;
}
else
{
isBetter = IsParamListBetter(parsedTypeInfo, bestParamInfo, protoDeclInfo);
}
if (isBetter)
{
bestMatch = protoNodeId;
bestParamInfo = protoDeclInfo;
}
}
}
}
else
{
HlslUtil.ParserAssert(false);
}
}
ret = bestMatch;
}
else if (isUnityFunc)
{
HlslParser.TypeInfo[] parsedTypeInfo = GetTypeInfoFromNodeArray(parsedParams);
// find the node id
int[] prototypeIds = unityReserved.GetPrototypeIdsForFunction(identifier);
int bestMatchProtoId = -1;
HlslUtil.PrototypeInfo bestProtoInfo = new HlslUtil.PrototypeInfo();
HlslParser.TypeInfo[] bestParamInfo = new HlslParser.TypeInfo[0];
for (int protoIter = 0; protoIter < prototypeIds.Length; protoIter++)
{
int protoId = prototypeIds[protoIter];
HlslUtil.PrototypeInfo protoInfo = unityReserved.parsedFuncStructData.allPrototypes[protoId];
HlslParser.TypeInfo[] protoDeclInfo = protoInfo.paramInfoVec;
bool isMatch = IsValidFunctionParamList(parsedTypeInfo, protoDeclInfo);
if (isMatch)
{
bool isBetter = false;
if (bestMatchProtoId < 0)
{
isBetter = true;
}
else
{
isBetter = IsParamListBetter(parsedTypeInfo, bestParamInfo, protoDeclInfo);
}
if (isBetter)
{
bestMatchProtoId = protoId;
bestParamInfo = protoDeclInfo;
bestProtoInfo = protoInfo;
}
}
}
if (bestMatchProtoId >= 0)
{
// create a node for this prototype
HlslTree.NodeFunctionPrototypeExternal proto = new HlslTree.NodeFunctionPrototypeExternal();
proto.protoInfo = bestProtoInfo;
ret = tree.AddNode(proto, this, isValid);
}
}
else
{
// no op? maybe we should put an error here?
}
return ret;
}
internal List<int> ParseParamList(ref TokenRunner runner)
{
List<int> parsedParams = new List<int>();
bool isFirst = true;
while (runner.isValid &&
!runner.Check(HlslToken._eof) &&
!runner.Check(HlslToken._parense_r))
{
if (isFirst)
{
isFirst = false;
}
else
{
runner.Expect(HlslToken._comma);
}
int foundExp = ParseExpression(ref runner);
parsedParams.Add(foundExp);
}
return parsedParams;
}
internal int ParseFunctionCall(ref TokenRunner runner)
{
string identifier = runner.TopData();
runner.AdvanceToken();
bool isFunctionCall = IsIdentifierFunctionCall(identifier);
if (!isFunctionCall)
{
runner.Error("Expected function call.");
}
runner.Expect(HlslToken._parense_l);
bool startRunnerValid = runner.isValid;
TokenRunner copyRunner = runner.MakeCopy();
List<int> parsedParams = ParseParamList(ref runner);
runner.Expect(HlslToken._parense_r);
if (startRunnerValid && !runner.isValid)
{
runner = copyRunner;
parsedParams = ParseParamList(ref runner);
runner.Expect(HlslToken._parense_r);
}
int dstId = -1;
if (runner.isValid)
{
HlslTree.NodeExpression node = new HlslTree.NodeExpression();
node.lhsNodeId = FindBestFunctionCallForPrototype(identifier, parsedParams.ToArray(), runner.isValid);
node.op = HlslOp.FunctionalCall;
node.paramIds = parsedParams.ToArray();
dstId = tree.AddNode(node, this, runner.isValid);
}
return dstId;
}
static internal HlslOp GetTokenLeftSideOperator(HlslToken token)
{
HlslOp op = HlslOp.Invalid;
switch (token)
{
case HlslToken._plus_plus: // ++
op = HlslOp.PreIncrement;
break;
case HlslToken._minus_minus: // --
op = HlslOp.PreDecrement;
break;
case HlslToken._plus: // +
op = HlslOp.UnaryPlus;
break;
case HlslToken._minus: // -
op = HlslOp.UnaryMinus;
break;
case HlslToken._logical_not: // !
op = HlslOp.LogicalNot;
break;
case HlslToken._bitwise_not: // ~
op = HlslOp.BitwiseNot;
break;
default:
op = HlslOp.Invalid;
break;
}
return op;
}
internal int ParseMemberAccessRhs(ref TokenRunner runner, int lhsExp)
{
int ret = -1;
// should be an identifier
if (runner.Check(HlslToken._identifier))
{
// get the type of the parent expression
HlslTree.Node lhsNode = runner.parser.tree.allNodes[lhsExp];
HlslTree.FullTypeInfo fti = runner.parser.tree.fullTypeInfo[lhsExp];
string rhsName = runner.TopData();
if (fti.nativeType == HlslNativeType._struct)
{
int structNodeId = runner.GetStructDefinitionFromString(fti.identifier);
// is this a struct member or function
int fieldIndex = -1;
int protoIndex = -1;
tree.DoesStructHaveDeclaration(out fieldIndex, out protoIndex, structNodeId, rhsName, unityReserved);
// we might find one or the other, but we should never find both
HlslUtil.ParserAssert(fieldIndex < 0 || protoIndex < 0);
HlslUtil.StructInfo structInfo = tree.GetStructInfoFromNodeId(structNodeId, unityReserved);
if (fieldIndex >= 0)
{
HlslTree.NodeMemberVariable nodeMemberVariable = new HlslTree.NodeMemberVariable();
nodeMemberVariable.structNodeId = structNodeId;
nodeMemberVariable.fieldIndex = fieldIndex;
int dstNodeId = tree.AddNode(nodeMemberVariable, this, runner.isValid);
ret = dstNodeId;
runner.AdvanceToken();
}
else if (protoIndex >= 0)
{
HlslTree.NodeMemberFunction nodeMemberFunc = new HlslTree.NodeMemberFunction();
nodeMemberFunc.structNodeId = structNodeId;
nodeMemberFunc.funcIndex = protoIndex;
runner.AdvanceToken();
// parse params
//dstExp.rhsNodeId = dstNodeId;
runner.Expect(HlslToken._parense_l);
List<int> parsedParams = ParseParamList(ref runner);
runner.Expect(HlslToken._parense_r);
nodeMemberFunc.funcParamNodeIds = parsedParams.ToArray();
ret = tree.AddNode(nodeMemberFunc, this, runner.isValid);
}
else
{
HlslUtil.StructInfo debugInfo = tree.GetStructInfoFromNodeId(structNodeId, unityReserved);
runner.Error("Failed to identify right side of member access operator.");
}
}
else
{
bool isValidSwizzleType = HlslUtil.IsValidSwizzleType(fti.nativeType);
if (isValidSwizzleType)
{
int swizzleLength = HlslUtil.GetSwizzleLength(rhsName, fti.nativeType);
if (swizzleLength >= 1)
{
HlslTree.NodeSwizzle nodeSwizzle = new HlslTree.NodeSwizzle();
nodeSwizzle.swizzle = rhsName;
int dstNodeId = tree.AddNode(nodeSwizzle, this, runner.isValid);
ret = dstNodeId;
runner.AdvanceToken();
}
else
{
runner.Error("Invalid swizzle.");
}
}
}
}
else
{
runner.Error("Invalid token for member access.");
}
return ret;
}
internal int ParseOperatorExpression(ref TokenRunner runner, int precedence)
{
// Making a copy for debugging. We only keep track of the current top token, but
// store the token when entering the function to make it easier to debug callstacks.
TokenRunner debugCopy = runner.MakeCopy();
// First, check if this is a function call, which we can do by checking if the
// the starting token is an identifier and it's a valid function identifier. Note
// that we would have to modify this logic if we were to allow the scope resolution
// operator as in:
// y = foo::bar(x);
// In that case, we would need a scope resolution node that merges foo::bar.
bool isFunctionCall = IsFunctionCall(runner);
int fullExp = -1;
if (isFunctionCall)
{
fullExp = ParseFunctionCall(ref runner);
}
else
{
// if it's not a function call, try to find the left side
fullExp = ParseSingleExpression(ref runner, precedence);
if (runner.Check(HlslToken._parense_l))
{
// could be either a function or a function cast
// skip the '('
runner.Expect(HlslToken._parense_l);
// is it native? if so, then it's a function cast
bool isTopNative = HlslTokenizer.IsTokenNativeType(runner.TopToken());
// is it a struct? if os, then it's a function cast
int topTokenStruct = runner.GetTokenStructType();
// otherwise is an identifier? if so, then it's a function.
bool isTopIdentifier = runner.Check(HlslToken._identifier);
int opTokenId = runner.currToken;
if (isTopNative || (topTokenStruct >= 0))
{
runner.Error("Unimplemented");
}
else if (isTopIdentifier)
{
string identifier = runner.TopData();
bool isLegalFunctionCall = IsIdentifierFunctionCall(identifier);
runner.Error("Unimplemented");
}
else
{
// failed to parse
runner.Error("Failed to parse either c-style cast or function call.");
}
runner.Expect(HlslToken._parense_r);
}
}
// try to parse any operators to the right of this expression
{
bool done = false;
while (!done)
{
HlslOp dstOp;
string dstOpCastName;
int dstOpTokenId;
int foundStructId;
TokenRunner rollbackRunner = runner.MakeCopy();
bool foundOp = TryParseNextOp(ref runner, out dstOp, out dstOpCastName, out dstOpTokenId, out foundStructId, precedence, true);
if (foundOp)
{
bool isBinary = GetOperatorIsBinary(dstOp);
if (dstOp == HlslOp.TernaryQuestion)
{
int lhsExp = fullExp;
int opPrecedence = GetOperatorPrcedence(dstOp);
int rhsExp = ParseOperatorExpression(ref runner, opPrecedence);
if (rhsExp < 0)
{
runner.Error("Could not parse middle of ternary expression.");
}
runner.Expect(HlslToken._colon);
int rhsRhsExp = ParseOperatorExpression(ref runner, opPrecedence);
if (rhsRhsExp < 0)
{
runner.Error("Could not parse right of ternary expression.");
}
HlslTree.NodeExpression dstExp = new HlslTree.NodeExpression();
// if it's a single operator then apply it
dstExp.lhsNodeId = lhsExp;
dstExp.rhsNodeId = rhsExp;
dstExp.rhsRhsNodeId = rhsRhsExp;
dstExp.op = dstOp;
dstExp.opTokenId = dstOpTokenId;
fullExp = runner.parser.tree.AddNode(dstExp, this, runner.isValid);
}
else if (dstOp == HlslOp.Subscript)
{
int lhsExp = fullExp;
// parse with reset precedence
int rhsExp = ParseExpression(ref runner);
runner.Expect(HlslToken._bracket_r);
HlslTree.NodeExpression dstExp = new HlslTree.NodeExpression();
dstExp.lhsNodeId = lhsExp;
dstExp.rhsNodeId = rhsExp;
dstExp.op = dstOp;
dstExp.opTokenId = dstOpTokenId;
fullExp = runner.parser.tree.AddNode(dstExp, this, runner.isValid);
}
else if (isBinary)
{
int opPrecedence = GetOperatorPrcedence(dstOp);
int rhsExp = ParseOperatorExpression(ref runner, opPrecedence);
if (rhsExp < 0)
{
runner.Error("Could not find right side expression.");
}
else
{
// if it's a single operator then apply it
int lhsExp = fullExp;
HlslTree.NodeExpression dstExp = new HlslTree.NodeExpression();
dstExp.lhsNodeId = lhsExp;
dstExp.rhsNodeId = rhsExp;
dstExp.op = dstOp;
dstExp.opTokenId = dstOpTokenId;
fullExp = runner.parser.tree.AddNode(dstExp, this, runner.isValid);
}
}
else
{
int lhsExp = fullExp;
// if it's a single operator then apply it
HlslTree.NodeExpression dstExp = new HlslTree.NodeExpression();
dstExp.lhsNodeId = lhsExp;
dstExp.op = dstOp;
dstExp.opTokenId = dstOpTokenId;
if (dstOp == HlslOp.MemberAccess)
{
HlslToken topToken = runner.TopToken();
TokenRunner copy = runner.MakeCopy();
dstExp.rhsNodeId = ParseMemberAccessRhs(ref runner, lhsExp);
if (runner.isValid && dstExp.rhsNodeId < 0)
{
runner = copy;
dstExp.rhsNodeId = ParseMemberAccessRhs(ref runner, lhsExp);
HlslUtil.ParserAssert(false);
}
}
else
{
}
fullExp = runner.parser.tree.AddNode(dstExp, this, runner.isValid);
}
}
else
{
// if we didn't find a parens, and we didn't find an op, then we are done
done = true;
}
}
}
return fullExp;
}
internal int ParseExpression(ref TokenRunner runner)
{
int ret = ParseOperatorExpression(ref runner, GetWeakestPrcedence());
return ret;
}
// For a declaration, parse only the type and identifer. Ignore the trailing comma/semicolon for function params/variables
// and any initializers. If skipAddNode is true, simply parse but don't actually create any nodes.
internal void ParseDeclTypeAndIdentifier(ref HlslTree.NodeDeclaration nodeDecl, ref TokenRunner runner, bool skipAddNode)
{
string topTokenText = runner.TopData();
bool isMacroTypeDecl = unityReserved.IsIdentifierMacroDecl(topTokenText);
if (isMacroTypeDecl)
{
ParseMacroDeclaration(out nodeDecl, ref runner);
}
else
{
int topTokenStruct = runner.GetTokenStructType();
if (skipAddNode)
{
nodeDecl.typeNodeId = -1;
}
else
{
nodeDecl.typeNodeId = CreateNodeForTopToken(ref runner);
}
if (HlslTokenizer.IsTokenNativeType(runner.TopToken()))
{
nodeDecl.nativeType = tokenToNativeTable[runner.TopToken()];
}
else
{
nodeDecl.structId = topTokenStruct;
nodeDecl.nativeType = HlslNativeType._struct;
}
nodeDecl.debugType = runner.TopData();
runner.AdvanceToken();
if (runner.Accept(HlslToken._less_than))
{
if (skipAddNode)
{
// this should be a native type or identifier for struct
nodeDecl.subTypeNodeId = CreateNodeForTopToken(ref runner);
}
else
{
nodeDecl.subTypeNodeId = -1;
}
runner.Expect(HlslToken._greater_than);
}
// next token should be the name
runner.Check(HlslToken._identifier);
nodeDecl.nameTokenId = runner.currToken;
nodeDecl.variableName = runner.TopData();
runner.AdvanceToken();
List<int> dims = new List<int>();
// do we have a bracket? If so, it's an array.
while (runner.Accept(HlslToken._bracket_l))
{
// a ']' means implicit size based on the initialzier, otherwise
// we have an expression here
int currExp = -1;
if (!runner.Check(HlslToken._bracket_r))
{
currExp = ParseExpression(ref runner);
}
dims.Add(currExp);
// we should expect a ']' on the end
runner.Expect(HlslToken._bracket_r);
// if we have another '[' then the while loop will continue, otherwise we are done.
}
nodeDecl.arrayDims = dims.ToArray();
}
}
internal int ParseBlockOrStatement(ref TokenRunner runner)
{
int ret = -1;
if (runner.Check(HlslToken._brace_l))
{
List<HlslTree.VariableInfo> ignoredVariables = new List<HlslTree.VariableInfo>();
ret = ParseBlock(ref runner, ignoredVariables);
}
else
{
ret = ParseStatement(ref runner);
}
return ret;
}
// Parse the initializer, such as:
// float3 val = { 0, foo*4, zelp.x };
// These can also be nested. For simplicity, we are going to parse the types blind and
// force them to FPD.
internal int ParseBlockInitializer(ref TokenRunner runner)
{
int ret = -1;
runner.Expect(HlslToken._brace_l);
List<int> initList = new List<int>();
bool done = false;
while (!done)
{
if (initList.Count > 0)
{
runner.Expect(HlslToken._comma);
}
int currChild = -1;
if (runner.Check(HlslToken._brace_l))
{
// parse child block
currChild = ParseBlockInitializer(ref runner);
}
else
{
// parse an expression
currChild = ParseExpression(ref runner);
}
if (currChild < 0)
{
done = true;
}
if (runner.IsEof() || runner.Check(HlslToken._brace_r))
{
done = true;
}
initList.Add(currChild);
}
runner.Expect(HlslToken._brace_r);
HlslTree.NodeBlockInitializer blockInit = new HlslTree.NodeBlockInitializer();
blockInit.initNodeIds = initList.ToArray();
ret = runner.parser.tree.AddNode(blockInit, this, runner.isValid);
return ret;
}
internal int ParseStatement(ref TokenRunner runner)
{
int foundExp = -1;
int topTokenStruct = runner.GetTokenStructType();
if (topTokenStruct >= 0 || HlslTokenizer.IsTokenNativeType(runner.TopToken()))
{
HlslTree.NodeDeclaration nodeDecl = new HlslTree.NodeDeclaration();
ParseDeclTypeAndIdentifier(ref nodeDecl, ref runner, false);
// check if we have an initializer
if (runner.Accept(HlslToken._assignment))
{
int rhsExp = -1;
if (runner.Check(HlslToken._brace_l))
{
rhsExp = ParseBlockInitializer(ref runner);
}
else
{
rhsExp = ParseExpression(ref runner);
}
nodeDecl.initializerId = rhsExp;
}
runner.Expect(HlslToken._semicolon);
foundExp = runner.parser.tree.AddNode(nodeDecl, this, runner.isValid);
HlslTree.VariableInfo variableInfo = GetVariableInfoFromDecl(nodeDecl, foundExp);
if (!variableInfo.IsValid())
{
runner.Error("Invalid variable.");
}
else
{
// add type for scope
tree.AddVariableIdentifier(variableInfo);
}
}
else if (runner.Check(HlslToken._return))
{
// skip the return
runner.Expect(HlslToken._return);
int expression = ParseExpression(ref runner);
runner.Expect(HlslToken._semicolon);
HlslTree.NodeStatement node = new HlslTree.NodeStatement();
node.expression = expression;
node.type = HlslStatementType.Return;
foundExp = runner.parser.tree.AddNode(node, this, runner.isValid);
}
else if (runner.Check(HlslToken._while))
{
// skip the while
runner.Expect(HlslToken._while);
// parse the expression in the while
runner.Expect(HlslToken._parense_l);
int whileExp = ParseExpression(ref runner);
runner.Expect(HlslToken._parense_r);
int childNode = ParseBlockOrStatement(ref runner);
HlslTree.NodeStatement node = new HlslTree.NodeStatement();
node.expression = whileExp;
node.childBlockOrStatement = childNode;
node.type = HlslStatementType.While;
foundExp = runner.parser.tree.AddNode(node, this, runner.isValid);
}
else if (runner.Check(HlslToken._do))
{
// skip the do
runner.Expect(HlslToken._do);
int childNode = ParseBlockOrStatement(ref runner);
runner.Expect(HlslToken._while);
// parse the expression in the while
runner.Expect(HlslToken._parense_l);
int whileExp = ParseExpression(ref runner);
runner.Expect(HlslToken._parense_r);
HlslTree.NodeStatement node = new HlslTree.NodeStatement();
node.expression = whileExp;
node.childBlockOrStatement = childNode;
node.type = HlslStatementType.Do;
foundExp = runner.parser.tree.AddNode(node, this, runner.isValid);
}
else if (runner.Check(HlslToken._for))
{
// skip the do
runner.Expect(HlslToken._for);
runner.Expect(HlslToken._parense_l);
int[] forExps = new int[3] { -1, -1, -1 };
forExps[0] = ParseExpression(ref runner);
runner.Expect(HlslToken._semicolon);
forExps[1] = ParseExpression(ref runner);
runner.Expect(HlslToken._semicolon);
forExps[2] = ParseExpression(ref runner);
runner.Expect(HlslToken._parense_r);
// Quick note on scope. The declarations in forExps[0] will be put in the outer block scope
// instead of just inside the for loop's block scope. Of course, that is wrong by c++
// standard but correct (for better or for worse) for hlsl.
int childNode = ParseBlockOrStatement(ref runner);
HlslTree.NodeStatement node = new HlslTree.NodeStatement();
node.expression = -1;
node.forExpressions = forExps;
node.childBlockOrStatement = childNode;
node.type = HlslStatementType.For;
foundExp = runner.parser.tree.AddNode(node, this, runner.isValid);
}
else if (runner.Check(HlslToken._if))
{
runner.Expect(HlslToken._if);
runner.Expect(HlslToken._parense_l);
int expression = ParseExpression(ref runner);
runner.Expect(HlslToken._parense_r);
int childNode = ParseBlockOrStatement(ref runner);
int childElse = -1;
if (runner.Accept(HlslToken._else))
{
childElse = ParseBlockOrStatement(ref runner);
}
HlslTree.NodeStatement node = new HlslTree.NodeStatement();
node.expression = expression;
node.elseBlockOrStatement = childElse;
node.childBlockOrStatement = childNode;
node.type = HlslStatementType.If;
foundExp = runner.parser.tree.AddNode(node, this, runner.isValid);
}
else if (runner.Check(HlslToken._continue))
{
runner.Expect(HlslToken._continue);
runner.Expect(HlslToken._semicolon);
HlslTree.NodeStatement node = new HlslTree.NodeStatement();
node.type = HlslStatementType.Continue;
foundExp = runner.parser.tree.AddNode(node, this, runner.isValid);
}
else if (runner.Check(HlslToken._switch))
{
runner.Expect(HlslToken._switch);
runner.Expect(HlslToken._parense_l);
int expression = ParseExpression(ref runner);
runner.Expect(HlslToken._parense_r);
List<HlslTree.VariableInfo> ignoredParams = new List<HlslTree.VariableInfo>();
int childBlock = ParseBlock(ref runner, ignoredParams);
HlslTree.NodeStatement node = new HlslTree.NodeStatement();
node.type = HlslStatementType.Switch;
node.expression = expression;
node.childBlockOrStatement = childBlock;
foundExp = runner.parser.tree.AddNode(node, this, runner.isValid);
}
else if (runner.Check(HlslToken._break))
{
runner.Expect(HlslToken._break);
runner.Expect(HlslToken._semicolon);
HlslTree.NodeStatement node = new HlslTree.NodeStatement();
node.type = HlslStatementType.Break;
foundExp = runner.parser.tree.AddNode(node, this, runner.isValid);
}
else if (runner.Check(HlslToken._default))
{
runner.Expect(HlslToken._default);
runner.Expect(HlslToken._colon);
HlslTree.NodeStatement node = new HlslTree.NodeStatement();
node.type = HlslStatementType.Default;
foundExp = runner.parser.tree.AddNode(node, this, runner.isValid);
}
else if (runner.Check(HlslToken._goto))
{
runner.Expect(HlslToken._goto);
int labelExpression = CreateNodeForTopToken(ref runner);
runner.Expect(HlslToken._identifier);
runner.Expect(HlslToken._semicolon);
HlslTree.NodeStatement node = new HlslTree.NodeStatement();
node.type = HlslStatementType.Goto;
node.expression = labelExpression;
foundExp = runner.parser.tree.AddNode(node, this, runner.isValid);
}
else
{
// We are either an expression or a label. Check 1 token ahead to see.
HlslToken currToken = runner.TopToken();
HlslToken nextToken = runner.LookAheadToken(1);
if (currToken == HlslToken._identifier && nextToken == HlslToken._colon)
{
int labelExpression = CreateNodeForTopToken(ref runner);
runner.Expect(HlslToken._identifier);
runner.Expect(HlslToken._colon);
HlslTree.NodeStatement node = new HlslTree.NodeStatement();
node.type = HlslStatementType.Label;
node.expression = labelExpression;
foundExp = runner.parser.tree.AddNode(node, this, runner.isValid);
}
else
{
int expression = ParseExpression(ref runner);
runner.Expect(HlslToken._semicolon);
HlslTree.NodeStatement node = new HlslTree.NodeStatement();
node.expression = expression;
node.type = HlslStatementType.Expression;
foundExp = runner.parser.tree.AddNode(node, this, runner.isValid);
}
}
return foundExp;
}
internal int ParseBlock(ref TokenRunner runner, List<HlslTree.VariableInfo> protoVariables)
{
bool preprocStatus = runner.allowPreprocessor;
runner.allowPreprocessor = false;
HlslTree.NodeBlock block = new HlslTree.NodeBlock();
runner.Expect(HlslToken._brace_l);
if (runner.isValid)
{
runner.parser.tree.PushScope();
// add the prototype variables to the scope
for (int i = 0; i < protoVariables.Count; i++)
{
tree.AddVariableIdentifier(protoVariables[i]);
}
List<int> statements = new List<int>();
while (runner.isValid && !runner.Check(HlslToken._brace_r))
{
//int statement = ParseStatement(ref runner);
int statement = ParseBlockOrStatement(ref runner);
if (statement >= 0)
{
statements.Add(statement);
}
else
{
runner.Error("Failed to parse statement.");
}
}
block.statements = statements.ToArray();
runner.parser.tree.PopScope();
}
// the status gets checked for the following token, so revert status before parsing the }
runner.allowPreprocessor = preprocStatus;
runner.Expect(HlslToken._brace_r);
int addedBlock = -1;
if (runner.isValid)
{
addedBlock = tree.AddNode(block, this, runner.isValid);
}
return addedBlock;
}
internal int ParseFunction(ref TokenRunner runner)
{
int foundId = -1;
List<HlslTree.VariableInfo> foundVariables;
int prototypeId = ParseFunctionPrototype(ref runner, out foundVariables);
if (prototypeId >= 0)
{
HlslUtil.ParserAssert(tree.allNodes[prototypeId] is HlslTree.NodeFunctionPrototype);
// is this one of our ignored funcs?
bool isIgnored = false;
string funcName;
{
HlslTree.NodeFunctionPrototype nodeProto = (HlslTree.NodeFunctionPrototype)tree.allNodes[prototypeId];
funcName = tokenizer.GetTokenData(nodeProto.nameTokenId);
if (ignoredFuncs.Contains(funcName))
{
// trigger an error, which will cause the parsing of this function to faile which will
// cause it to instead match braces.
runner.Error("Ignoring function: " + funcName);
isIgnored = true;
}
}
if (!isIgnored)
{
//bool preprocessorStatus = runner.allowPreprocessor;
//runner.allowPreprocessor = false; // preprocessor commands are not allowed inside functions
int blockId = ParseBlock(ref runner, foundVariables);
if (blockId >= 0)
{
HlslTree.NodeFunction node = new HlslTree.NodeFunction();
node.prototypeId = prototypeId;
node.blockId = blockId;
foundId = runner.parser.tree.AddNode(node, this, runner.isValid);
}
//runner.allowPreprocessor = preprocessorStatus; // revert to previous state
}
}
return foundId;
}
internal int ParseFunctionMatchOnly(ref TokenRunner runner, string parseErr)
{
int foundId = -1;
List<HlslTree.VariableInfo> foundVariables;
int prototypeId = ParseFunctionPrototype(ref runner, out foundVariables);
if (prototypeId >= 0)
{
int firstToken = runner.currToken;
runner.Expect(HlslToken._brace_l);
int stackSize = 1;
while (runner.isValid && runner.TopToken() != HlslToken._eof && stackSize > 0)
{
HlslToken currType = runner.TopToken();
if (currType == HlslToken._brace_l)
{
stackSize++;
}
if (currType == HlslToken._brace_r)
{
stackSize--;
}
runner.AdvanceToken();
}
if (stackSize > 0)
{
runner.Error("Failed to find closing brace");
}
int lastToken = runner.currToken;
// Note that we need to include everything in the range [firstToken,lastToken). The while loop
// skips whitespace tokens but we need to include them here.
int[] foundTokens = new int[lastToken - firstToken];
for (int i = 0; i < foundTokens.Length; i++)
{
foundTokens[i] = firstToken + i;
}
HlslTree.NodePassthrough nodePassthrough = new HlslTree.NodePassthrough();
nodePassthrough.tokenIds = foundTokens;
nodePassthrough.writeDirect = true;
int blockId = tree.AddNode(nodePassthrough, this, runner.isValid);
{
HlslTree.NodeFunction node = new HlslTree.NodeFunction();
node.prototypeId = prototypeId;
node.blockId = blockId;
node.parseErr = parseErr;
foundId = runner.parser.tree.AddNode(node, this, runner.isValid);
}
}
return foundId;
}
internal int ParseFunctionWithFallback(ref TokenRunner outerRunner)
{
TokenRunner currRunner = outerRunner.MakeCopy();
int funcId = ParseFunction(ref currRunner);
if (currRunner.isValid)
{
outerRunner = currRunner;
}
else
{
string err = currRunner.lastErr;
TokenRunner matchRunner = outerRunner.MakeCopy();
funcId = ParseFunctionMatchOnly(ref matchRunner, err);
outerRunner = matchRunner;
// remove errors
tree.ResetErrors();
}
return funcId;
}
internal bool IsTopTokenCustomGlobalMacro(TokenRunner runner)
{
// Is the current token a valid top-level macro in a shader, such as CBUFFER_START? These
// have special parsing rules.
string topTokenText = runner.TopData();
bool ret = false;
if (string.Equals(topTokenText, "CBUFFER_START") ||
string.Equals(topTokenText, "CBUFFER_END"))
{
ret = true;
}
bool isMacroTypeDecl = unityReserved.IsIdentifierMacroDecl(topTokenText);
if (isMacroTypeDecl)
{
ret = true;
}
return ret;
}
internal int CreatePassThroughForRunnerRange(TokenRunner startRunner, TokenRunner endRunner)
{
int firstToken = startRunner.currToken;
int endToken = endRunner.currToken;
List<int> tokenIds = new List<int>();
for (int i = startRunner.currToken; i < endRunner.currToken; i++)
{
tokenIds.Add(i);
}
HlslTree.NodePassthrough passthrough = new HlslTree.NodePassthrough();
passthrough.tokenIds = tokenIds.ToArray();
int passthroughId = tree.AddNode(passthrough, this, endRunner.isValid);
return passthroughId;
}
internal void ParseMacroDeclaration(out HlslTree.NodeDeclaration nodeDecl, ref TokenRunner runner)
{
nodeDecl = new HlslTree.NodeDeclaration();
string topTokenText = runner.TopData();
HlslParser.TypeInfo baseType;
HlslParser.TypeInfo subType;
unityReserved.GetMacroDeclBaseAndSubType(out baseType, out subType, topTokenText);
// these are all native base types, so don't worry about handling structs
HlslUtil.ParserAssert(baseType.nativeType != HlslNativeType._struct);
{
runner.AdvanceToken();
runner.Expect(HlslToken._parense_l);
// get the name
nodeDecl.nameTokenId = runner.currToken; //CreateNodeForTopToken(ref runner);
runner.Expect(HlslToken._identifier);
// If subType is non-invalid, then the subtype is predefined by the macro. Otherwise, it's a user paramater
// and we need a comma before the subType name. If the subtype is unknown, then we explicitly do not have
// have a subtype
if (subType.nativeType == HlslNativeType._invalid)
{
runner.Expect(HlslToken._comma);
if (HlslTokenizer.IsTokenNativeType(runner.TopToken()))
{
subType.nativeType = tokenToNativeTable[runner.TopToken()];
}
else
{
runner.Error("Invalid subType.");
}
runner.AdvanceToken();
}
nodeDecl.nativeType = baseType.nativeType;
nodeDecl.typeNodeId = CreateNodeForNativeType(ref runner, baseType.nativeType);
nodeDecl.subTypeNodeId = -1;
if (subType.nativeType != HlslNativeType._unknown)
{
nodeDecl.subTypeNodeId = CreateNodeForNativeType(ref runner, subType.nativeType);
}
nodeDecl.variableName = tokenizer.GetTokenData(nodeDecl.nameTokenId);
nodeDecl.isMacroDecl = true;
nodeDecl.macroDeclString = topTokenText;
{
List<int> dims = new List<int>();
// do we have a bracket? If so, it's an array.
while (runner.Accept(HlslToken._bracket_l))
{
// a ']' means implicit size based on the initialzier, otherwise
// we have an expression here
int currExp = -1;
if (!runner.Check(HlslToken._bracket_r))
{
currExp = ParseExpression(ref runner);
}
dims.Add(currExp);
// we should expect a ']' on the end
runner.Expect(HlslToken._bracket_r);
// if we have another '[' then the while loop will continue, otherwise we are done.
}
nodeDecl.arrayDims = dims.ToArray();
}
runner.Expect(HlslToken._parense_r);
}
}
internal int ParseToplevelCustomGlobalMacro(ref TokenRunner runner)
{
string topTokenText = runner.TopData();
int ret = -1;
if (string.Equals(topTokenText, "CBUFFER_START"))
{
TokenRunner startRunner = runner.MakeCopy();
runner.AdvanceToken();
runner.Expect(HlslToken._parense_l);
runner.Expect(HlslToken._identifier);
runner.Expect(HlslToken._parense_r);
int passthroughId = CreatePassThroughForRunnerRange(startRunner, runner);
ret = passthroughId;
}
else if (string.Equals(topTokenText, "CBUFFER_END"))
{
TokenRunner startRunner = runner.MakeCopy();
runner.AdvanceToken();
int passthroughId = CreatePassThroughForRunnerRange(startRunner, runner);
ret = passthroughId;
}
else
{
bool isMacroTypeDecl = unityReserved.IsIdentifierMacroDecl(topTokenText);
if (isMacroTypeDecl)
{
TokenRunner startRunner = runner.MakeCopy();
HlslTree.NodeDeclaration nodeDecl;
ParseMacroDeclaration(out nodeDecl, ref runner);
ret = tree.AddNode(nodeDecl, this, runner.isValid);
HlslTree.VariableInfo variableInfo = GetVariableInfoFromDecl(nodeDecl, ret);
tree.AddVariableIdentifier(variableInfo);
}
else
{
runner.Error("Invalid top level declaration.");
}
}
return ret;
}
internal int ParseTopLevel(TokenRunner runner)
{
HlslTree.NodeTopLevel topLevel = new HlslTree.NodeTopLevel();
List<int> statements = new List<int>();
List<HlslTokenizer.CodeSection> codeSections = new List<HlslTokenizer.CodeSection>();
while (!runner.IsEof())
{
HlslTokenizer.CodeSection currCodeSection = runner.TopCodeSection();
bool isMacroDecl = IsTopTokenCustomGlobalMacro(runner);
if (isMacroDecl)
{
int parsedMacroDecl = ParseToplevelCustomGlobalMacro(ref runner);
statements.Add(parsedMacroDecl);
codeSections.Add(currCodeSection);
}
else if (runner.Check(HlslToken._struct))
{
// struct
int parsedStruct = ParseStruct(ref runner);
statements.Add(parsedStruct);
codeSections.Add(currCodeSection);
}
else if (runner.CheckType())
{
// If we parse as:
// <type> <identifier> <'('>
// then we are a function. But if we instead have
// <type> <identifier> <anything other than a '('>
// then we are a variable declaration. So look ahead
bool isFunction = false;
{
HlslTree.NodeDeclaration nodeIgnored = new HlslTree.NodeDeclaration();
TokenRunner lookAheadRunner = runner.MakeCopy();
ParseDeclTypeAndIdentifier(ref nodeIgnored, ref lookAheadRunner, true);
bool isNextParens = lookAheadRunner.Check(HlslToken._parense_l);
if (lookAheadRunner.isValid && isNextParens)
{
isFunction = true;
}
}
if (isFunction)
{
int parsedFunc = ParseFunctionWithFallback(ref runner);
statements.Add(parsedFunc);
codeSections.Add(currCodeSection);
}
else
{
HlslTree.VariableInfo variableInfo;
int parsedDecl = ParseDeclaration(out variableInfo, ref runner, true, false);
statements.Add(parsedDecl);
codeSections.Add(currCodeSection);
// add this variable to the list for this scope
tree.AddVariableIdentifier(variableInfo);
}
}
else
{
runner.AdvanceToken();
}
}
HlslUtil.ParserAssert(statements.Count == codeSections.Count);
topLevel.statements = statements.ToArray();
topLevel.codeSections = codeSections.ToArray();
int topLevelId = tree.AddNode(topLevel, this, runner.isValid);
return topLevelId;
}
internal Dictionary<HlslToken, HlslNativeType> tokenToNativeTable;
internal HlslTokenizer tokenizer;
internal HlslUnityReserved unityReserved;
internal HlslTree tree;
List<string> ignoredFuncs;
}
}