/*
* Copyright 2013-2016 consulo.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package consulo.unity3d.shaderlab.lang.parser.roles;
import static consulo.unity3d.shaderlab.lang.parser.ShaderLabParser.doneError;
import static consulo.unity3d.shaderlab.lang.parser.ShaderLabParser.expectWithError;
import static consulo.unity3d.shaderlab.lang.parser.ShaderLabParser.parseBracketReference;
import static consulo.unity3d.shaderlab.lang.parser.ShaderLabParser.parseElementsInBraces;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.intellij.lang.PsiBuilder;
import com.intellij.lang.PsiBuilderUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.ThreeState;
import consulo.unity3d.shaderlab.lang.ShaderLabPropertyType;
import consulo.unity3d.shaderlab.lang.parser.ShaderLabParser;
import consulo.unity3d.shaderlab.lang.parser.ShaderLabParserBuilder;
import consulo.unity3d.shaderlab.lang.psi.ShaderLabElements;
import consulo.unity3d.shaderlab.lang.psi.ShaderLabTokens;
/**
* @author VISTALL
* @since 09.05.2015
*/
public abstract class ShaderLabRole
{
public static final ShaderLabRole Properties = new ShaderLabRole()
{
@Override
public PsiBuilder.Marker parseAndDone(ShaderLabParserBuilder builder, @NotNull PsiBuilder.Marker mark)
{
if(expectWithError(builder, ShaderLabTokens.LBRACE, "'{' expected"))
{
int balanceCount = 0;
loop:
while(!builder.eof())
{
ThreeState threeState = parseProperty(builder);
switch(threeState)
{
case YES:
if(balanceCount == 0)
{
break loop;
}
balanceCount--;
doneError(builder, "Unexpected token");
break;
case UNSURE:
if(builder.getTokenType() == ShaderLabTokens.LBRACE)
{
balanceCount++;
}
doneError(builder, "Unexpected token");
break;
}
}
expectWithError(builder, ShaderLabTokens.RBRACE, "'}' expected");
}
mark.done(ShaderLabElements.PROPERTY_LIST);
return mark;
}
@NotNull
private ThreeState parseProperty(PsiBuilder builder)
{
IElementType tokenType = builder.getTokenType();
PsiBuilder.Marker propertyMark = null;
if(tokenType == ShaderLabTokens.LBRACKET)
{
propertyMark = builder.mark();
builder.advanceLexer();
if(!ShaderLabParser.parseReference(builder))
{
builder.error("Expected identifier");
}
parseElementsInBraces(builder, ShaderLabTokens.LPAR, ShaderLabTokens.RPAR, null);
expectWithError(builder, ShaderLabTokens.RBRACKET, "']' expected");
propertyMark.done(ShaderLabElements.PROPERTY_ATTRIBUTE);
propertyMark = propertyMark.precede();
tokenType = builder.getTokenType();
}
if(tokenType == ShaderLabTokens.IDENTIFIER)
{
PsiBuilder.Marker mark = propertyMark == null ? builder.mark() : propertyMark;
builder.advanceLexer();
ShaderLabPropertyType shaderLabPropertyType = null;
if(expectWithError(builder, ShaderLabTokens.LPAR, "'(' expected"))
{
expectWithError(builder, ShaderLabTokens.STRING_LITERAL, "Name expected");
expectWithError(builder, ShaderLabTokens.COMMA, "Comma expected");
shaderLabPropertyType = parsePropertyType(builder);
expectWithError(builder, ShaderLabTokens.RPAR, "')' expected");
}
if(expectWithError(builder, ShaderLabTokens.EQ, "'=' expected"))
{
if(shaderLabPropertyType != null)
{
PsiBuilder.Marker valueMark = builder.mark();
switch(shaderLabPropertyType)
{
case Float:
case Int:
case Range:
expectWithError(builder, ShaderLabTokens.INTEGER_LITERAL, "Value expected");
break;
case Color:
case Vector:
parseElementsInBraces(builder, ShaderLabTokens.LPAR, ShaderLabTokens.RPAR, ShaderLabTokens.INTEGER_LITERAL);
break;
case Any:
case Cube:
case _2D:
case _3D:
expectWithError(builder, ShaderLabTokens.STRING_LITERAL, "Name expected");
if(expectWithError(builder, ShaderLabTokens.LBRACE, "'{' expected"))
{
while(!builder.eof())
{
if(builder.getTokenType() == ShaderLabTokens.IDENTIFIER)
{
PsiBuilder.Marker optionMarker = builder.mark();
builder.advanceLexer();
expectWithError(builder, ShaderLabTokens.IDENTIFIER, "Expected value");
optionMarker.done(ShaderLabElements.PROPERTY_OPTION);
}
else
{
break;
}
}
expectWithError(builder, ShaderLabTokens.RBRACE, "'}' expected");
}
break;
}
valueMark.done(ShaderLabElements.PROPERTY_VALUE);
}
else
{
builder.error("Wrong property type");
}
}
mark.done(ShaderLabElements.PROPERTY);
return ThreeState.NO;
}
else if(tokenType == ShaderLabTokens.RBRACE)
{
return ThreeState.YES;
}
if(propertyMark != null)
{
propertyMark.error("Expected identifier");
}
return ThreeState.UNSURE;
}
@Nullable
public ShaderLabPropertyType parsePropertyType(PsiBuilder builder)
{
IElementType tokenType = builder.getTokenType();
if(tokenType == ShaderLabTokens.IDENTIFIER)
{
PsiBuilder.Marker mark = builder.mark();
String tokenText = builder.getTokenText();
assert tokenText != null;
ShaderLabPropertyType shaderLabPropertyType = ShaderLabPropertyType.find(tokenText);
builder.advanceLexer();
parseElementsInBraces(builder, ShaderLabTokens.LPAR, ShaderLabTokens.RPAR, ShaderLabTokens.INTEGER_LITERAL);
mark.done(ShaderLabElements.PROPERTY_TYPE);
return shaderLabPropertyType;
}
else if(builder.getTokenType() == ShaderLabTokens.RPAR || builder.getTokenType() == ShaderLabTokens.EQ)
{
return null;
}
else
{
doneError(builder, "Type expected");
return null;
}
}
};
public static final ShaderLabRole Tags = new ShaderLabRole()
{
@Override
public PsiBuilder.Marker parseAndDone(ShaderLabParserBuilder builder, @NotNull PsiBuilder.Marker mark)
{
if(expectWithError(builder, ShaderLabTokens.LBRACE, "'{' expected"))
{
while(!builder.eof())
{
if(builder.getTokenType() == ShaderLabTokens.STRING_LITERAL)
{
PsiBuilder.Marker optionMarker = builder.mark();
builder.advanceLexer();
if(expectWithError(builder, ShaderLabTokens.EQ, "'=' expected"))
{
expectWithError(builder, ShaderLabTokens.STRING_LITERAL, "Expected value");
}
optionMarker.done(ShaderLabElements.TAG);
}
else
{
break;
}
}
expectWithError(builder, ShaderLabTokens.RBRACE, "'}' expected");
}
mark.done(ShaderLabElements.TAG_LIST);
return mark;
}
};
public static final ShaderLabRole Cull = new ShaderLabSimpleRole("Off", "Back", "Front");
public static final ShaderLabRole ZWrite = new ShaderLabSimpleRole("Off", "On");
public static final ShaderLabRole Lighting = new ShaderLabSimpleRole("Off", "On");
public static final ShaderLabRole SeparateSpecular = new ShaderLabSimpleRole("Off", "On");
public static final ShaderLabRole ColorMaterial = new ShaderLabSimpleRole("Emission", "AmbientAndDiffuse");
public static final ShaderLabRole Mode = new ShaderLabSimpleRole("Off", "Global", "Linear", "Exp", "Exp2");
public static final ShaderLabRole ZTest = new ShaderLabSimpleRole("Always", "Less", "Greater", "LEqual", "GEqual", "Equal", "NotEqual");
public static final ShaderLabRole Color = new ShaderLabColorRole();
public static final ShaderLabRole Fallback = new ShaderLabRole()
{
@Override
public PsiBuilder.Marker parseAndDone(ShaderLabParserBuilder builder, @NotNull PsiBuilder.Marker mark)
{
IElementType valueTokenType = builder.getTokenType();
if(valueTokenType == ShaderLabTokens.IDENTIFIER)
{
ShaderLabParser.validateIdentifier(builder, "Off");
}
else if(valueTokenType == ShaderLabTokens.STRING_LITERAL)
{
PsiBuilder.Marker refMarker = builder.mark();
builder.advanceLexer();
refMarker.done(ShaderLabElements.REFERENCE);
}
mark.done(ShaderLabElements.SIMPLE_VALUE);
return mark;
}
@Nullable
@Override
public String getDefaultInsertValue()
{
return "Off";
}
};
public static final ShaderLabRole UsePass = new ShaderLabRole()
{
@Override
public PsiBuilder.Marker parseAndDone(ShaderLabParserBuilder builder, @NotNull PsiBuilder.Marker mark)
{
IElementType valueTokenType = builder.getTokenType();
if(valueTokenType == ShaderLabTokens.STRING_LITERAL)
{
PsiBuilder.Marker refMarker = builder.mark();
builder.advanceLexer();
refMarker.done(ShaderLabElements.REFERENCE);
}
else
{
doneWithErrorSafe(builder, "Wrong value");
}
mark.done(ShaderLabElements.SIMPLE_VALUE);
return mark;
}
};
public static final ShaderLabRole ConstantColor = new ShaderLabColorRole();
public static final ShaderLabRole Matrix = new ShaderLabRole()
{
@Override
public PsiBuilder.Marker parseAndDone(ShaderLabParserBuilder builder, @NotNull PsiBuilder.Marker mark)
{
if(builder.getTokenType() == ShaderLabTokens.LBRACKET)
{
parseBracketReference(builder);
}
else
{
builder.error("Expected value");
}
mark.done(ShaderLabElements.SIMPLE_VALUE);
return mark;
}
};
public static final ShaderLabRole SetTexture = new ShaderLabCompositeRole(ShaderLabElements.SET_TEXTURE, Matrix, ConstantColor)
{
@Override
public void parseBefore(ShaderLabParserBuilder builder)
{
if(!ShaderLabParser.parseBracketReference(builder))
{
builder.error("Expected reference");
}
}
};
public static final ShaderLabRole Diffuse = new ShaderLabColorRole();
public static final ShaderLabRole Ambient = new ShaderLabColorRole();
public static final ShaderLabRole Shininess = new ShaderLabColorRole();
public static final ShaderLabRole Specular = new ShaderLabColorRole();
public static final ShaderLabRole Emission = new ShaderLabColorRole();
public static final ShaderLabRole Material = new ShaderLabCompositeRole(ShaderLabElements.MATERIAL, Diffuse, Ambient, Shininess, Specular, Emission);
public static final ShaderLabRole LOD = new ShaderLabTokenRole(ShaderLabTokens.INTEGER_LITERAL);
public static final ShaderLabRole Offset = new ShaderLabCommaPairRole(new ShaderLabTokenRole(ShaderLabTokens.INTEGER_LITERAL), new ShaderLabTokenRole(ShaderLabTokens.INTEGER_LITERAL));
public static final ShaderLabRole AlphaTest = new ShaderLabOrRole(new ShaderLabSimpleRole("Off"), new ShaderLabPairRole(new ShaderLabSimpleRole("Always", "Less", "Greater", "LEqual", "GEqual",
"Equal", "NotEqual", "Never"), new ShaderLabOrRole(new ShaderLabTokenRole(ShaderLabTokens.INTEGER_LITERAL), ShaderLabReferenceRole.INSTANCE)))
{
@Nullable
@Override
public String getDefaultInsertValue()
{
return "Off";
}
};
public static final ShaderLabRole Fog = new ShaderLabCompositeRole(ShaderLabElements.FOG, Color, Mode);
public static final ShaderLabRole Pass = new ShaderLabCompositeRole(ShaderLabElements.PASS, Color, SetTexture, Lighting, ZWrite, Cull, Fog, ZTest, SeparateSpecular, Material, AlphaTest, Offset);
public static final ShaderLabRole SubShader = new ShaderLabCompositeRole(ShaderLabElements.SUB_SHADER, Pass, Tags, Lighting, ZWrite, Cull, Fog, UsePass, Material, LOD);
public static final ShaderLabRole Shader = new ShaderLabCompositeRole(ShaderLabElements.SHADER_DEF, Properties, Fallback, SubShader)
{
@Override
public void parseBefore(ShaderLabParserBuilder builder)
{
if(!PsiBuilderUtil.expect(builder, ShaderLabTokens.STRING_LITERAL))
{
builder.error("Expected name");
}
}
};
private String myName;
public ShaderLabRole()
{
}
@NotNull
public String getName()
{
return myName;
}
@Nullable
public String getDefaultInsertValue()
{
return null;
}
public boolean tryParse(ShaderLabParserBuilder builder)
{
if(builder.is(this))
{
parseImpl(builder);
return true;
}
return false;
}
protected void doneWithErrorSafe(@NotNull ShaderLabParserBuilder builder, @NotNull String error)
{
IElementType tokenType = builder.getTokenType();
if(tokenType == ShaderLabTokens.LBRACE || tokenType == ShaderLabTokens.RBRACE)
{
builder.error("Expected value");
}
else
{
doneError(builder, error);
}
}
public PsiBuilder.Marker parseImpl(ShaderLabParserBuilder builder)
{
PsiBuilder.Marker mark = builder.mark();
builder.advanceLexer();
if(parseAndDone(builder, mark) == null)
{
builder.error("Expected value");
mark.done(ShaderLabElements.SIMPLE_VALUE);
}
return mark;
}
public abstract PsiBuilder.Marker parseAndDone(ShaderLabParserBuilder builder, PsiBuilder.Marker mark);
private static Map<String, ShaderLabRole> ourRoles = new HashMap<String, ShaderLabRole>();
@Nullable
public static ShaderLabRole findRole(String name)
{
name = name.toLowerCase();
return ourRoles.get(name);
}
static
{
Field[] declaredFields = ShaderLabRole.class.getFields();
for(Field declaredField : declaredFields)
{
if(Modifier.isStatic(declaredField.getModifiers()))
{
try
{
ShaderLabRole value = (ShaderLabRole) declaredField.get(null);
value.myName = declaredField.getName();
ourRoles.put(declaredField.getName().toLowerCase(), value);
}
catch(IllegalAccessException e)
{
throw new Error(e);
}
}
}
}
}