Michael Ludwig avatar Michael Ludwig committed 6729070

Complete implementations for all expressions, structures, etc.

Comments (0)

Files changed (22)

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/AbstractExpression.java

 
 import com.ferox.resource.shader.Expression;
 import com.ferox.resource.shader.LValue;
+import com.ferox.resource.shader.ShaderAccumulator;
 import com.ferox.resource.shader.simple_grammar.BinaryExpression.BinaryOperator;
 import com.ferox.resource.shader.simple_grammar.UnaryExpression.UnaryOperator;
 
 public abstract class AbstractExpression implements Expression {
+    public static enum Precedence {
+        ASSIGNMENT_EXPRESSIONS,
+        LOGICAL_OR_EXPRESSIONS,
+        LOGICAL_XOR_EXPRESSIONS,
+        LOGICAL_AND_EXPRESSIONS,
+
+        // bitwise or (single |)
+        OR_EXPRESSIONS,
+
+        // bitwise xor (single ^)
+        XOR_EXPRESSIONS,
+
+        // bitwise and (single &)
+        AND_EXPRESSIONS,
+
+        // equal, not equal
+        EQUALITY_EXPERSSIONS,
+
+        // greater/less than (or equal)
+        RELATIONAL_EXPRESSIONS,
+
+        // left and right shifts
+        SHIFT_EXPRESSIONS,
+
+        // addition, subtraction
+        ADDITIVE_EXPRESSIONS,
+
+        // modulation, division, multiplication
+        MULTIPLICATIVE_EXPRESSIONS,
+
+        // bit inversions, logical and mathematical negation, prefix
+        // increment/decrement
+        UNARY_EXPRESSIONS,
+
+        // postfix decrement/increment, field selection, array access,
+        // and function calls
+        POSTFIX_EXPRESSIONS,
+
+        // variables, constants, nested expressions
+        PRIMARY_EXPRESSIONS
+    }
+
+    @Override
+    public LValue x() {
+        return field("x");
+    }
+
+    @Override
+    public LValue y() {
+        return field("y");
+    }
+
+    @Override
+    public LValue z() {
+        return field("z");
+    }
+
+    @Override
+    public LValue xy() {
+        return field("xy");
+    }
+
+    @Override
+    public LValue xyz() {
+        return field("xyz");
+    }
+
+    @Override
+    public LValue m(int col, int row) {
+        return array(new Constant(col)).array(new Constant(row));
+    }
 
     @Override
     public Expression mul(Expression right) {
 
     @Override
     public Expression negate() {
-        return new UnaryExpression(UnaryOperator.DASH, this);
+        return new UnaryExpression(UnaryOperator.NEGATE, this);
     }
 
     @Override
     public Expression not() {
-        return new UnaryExpression(UnaryOperator.BANG, this);
+        return new UnaryExpression(UnaryOperator.LOGICAL_NEGATE, this);
     }
 
     @Override
     public LValue array(Expression index) {
         return new ArrayAccess(this, index);
     }
+
+    @Override
+    public void emit(ShaderAccumulator shader) {
+        shader.addLine(emitExpression(shader) + ";");
+    }
+
+    @Override
+    public boolean containsDeclaration() {
+        return false;
+    }
 }

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/AbstractLValue.java

 
 import com.ferox.resource.shader.Expression;
 import com.ferox.resource.shader.LValue;
-import com.ferox.resource.shader.Statement;
 
 public abstract class AbstractLValue extends AbstractExpression implements LValue {
     @Override
-    public Statement setTo(Expression value) {
+    public Expression setTo(Expression value) {
         return new Assignment(this, value);
     }
 }

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/ArrayAccess.java

 import com.ferox.resource.shader.Environment;
 import com.ferox.resource.shader.Expression;
 import com.ferox.resource.shader.PrimitiveType;
+import com.ferox.resource.shader.ShaderAccumulator;
 import com.ferox.resource.shader.Type;
 
 public class ArrayAccess extends AbstractLValue {
     }
 
     @Override
-    public String emitExpression() {
+    public String emitExpression(ShaderAccumulator shader) {
         if (array.getPrecedence() < getPrecedence()) {
-            return "(" + array.emitExpression() + ")[" + index.emitExpression() + "]";
+            return "(" + array.emitExpression(shader) + ")[" + index.emitExpression(shader) + "]";
         } else {
-            return array.emitExpression() + "[" + index.emitExpression() + "]";
+            return array.emitExpression(shader) + "[" + index.emitExpression(shader) + "]";
         }
     }
 

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/Assignment.java

 import com.ferox.resource.shader.Environment;
 import com.ferox.resource.shader.Expression;
 import com.ferox.resource.shader.LValue;
+import com.ferox.resource.shader.ShaderAccumulator;
 import com.ferox.resource.shader.Type;
 
 public class Assignment extends AbstractExpression {
     }
 
     @Override
-    public String emitExpression() {
+    public String emitExpression(ShaderAccumulator shader) {
         // we do not need to contain either expression in parentheses because
         // the assignment expression has the lowest precedence
-        return lvalue.emitExpression() + " = " + rvalue.emitExpression();
+        return lvalue.emitExpression(shader) + " = " + rvalue.emitExpression(shader);
     }
 
     @Override

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/BinaryExpression.java

 import com.ferox.resource.shader.Environment;
 import com.ferox.resource.shader.Expression;
 import com.ferox.resource.shader.PrimitiveType;
+import com.ferox.resource.shader.ShaderAccumulator;
 import com.ferox.resource.shader.Type;
 
 public class BinaryExpression extends AbstractExpression {
         EQUAL("=="),
         NOT_EQUAL("!="),
         LOGICAL_AND("&&"),
-        LOGICAL_XOR("^"),
+        LOGICAL_XOR("^^"),
         LOGICAL_OR("||");
         // FIXME add bitwise AND, XOR, and OR
 
         if (get((PrimitiveType) leftType, operator, (PrimitiveType) rightType) == null) {
             throw new IllegalStateException("Binary operator not supported with left and right expressions");
         }
+        if (left.containsDeclaration() || right.containsDeclaration()) {
+            throw new IllegalStateException("Binary expressions cannot contain declarations");
+        }
 
         return environment;
     }
 
     @Override
-    public String emitExpression() {
+    public String emitExpression(ShaderAccumulator shader) {
         StringBuilder sb = new StringBuilder();
         if (left.getPrecedence() < getPrecedence()) {
             sb.append('(');
-            sb.append(left.emitExpression());
+            sb.append(left.emitExpression(shader));
             sb.append(')');
         } else {
-            sb.append(left.emitExpression());
+            sb.append(left.emitExpression(shader));
         }
 
         sb.append(' ');
 
         if (right.getPrecedence() < getPrecedence()) {
             sb.append('(');
-            sb.append(right.emitExpression());
+            sb.append(right.emitExpression(shader));
             sb.append(')');
+        } else {
+            sb.append(right.emitExpression(shader));
         }
 
         return sb.toString();
         }
     }
 
-    private static final Map<BinaryOperator, Map<PrimitiveType, Map<PrimitiveType, PrimitiveType>>> operatorMap = new HashMap<BinaryExpression.BinaryOperator, Map<PrimitiveType, Map<PrimitiveType, PrimitiveType>>>();
+    private static final Map<BinaryOperator, Map<PrimitiveType, Map<PrimitiveType, PrimitiveType>>> operatorMap = new HashMap<BinaryOperator, Map<PrimitiveType, Map<PrimitiveType, PrimitiveType>>>();
 
     private static void add(PrimitiveType leftType, BinaryOperator op,
                             PrimitiveType rightType, PrimitiveType resultType) {

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/Constant.java

 
 import com.ferox.resource.shader.Environment;
 import com.ferox.resource.shader.PrimitiveType;
+import com.ferox.resource.shader.ShaderAccumulator;
 import com.ferox.resource.shader.Type;
 
 public class Constant extends AbstractExpression {
     }
 
     @Override
-    public String emitExpression() {
+    public String emitExpression(ShaderAccumulator shader) {
         return value.toString();
     }
 

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/DoWhileLoop.java

         }
 
         // validate loop body
-        Environment scoped = environment.newScope();
+        Environment scoped = environment.newScope(true);
         for (int i = 0; i < body.length; i++) {
             scoped = body[i].validate(scoped);
         }
             body[i].emit(accumulator);
         }
         accumulator.popIndent();
-        accumulator.addLine("} while (" + condition.emitExpression() + ");");
+        accumulator.addLine("} while (" + condition.emitExpression(accumulator) + ");");
     }
 
     public static class Builder implements DoWhileBuilder {

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/FieldSelection.java

 import com.ferox.resource.shader.Environment;
 import com.ferox.resource.shader.Expression;
 import com.ferox.resource.shader.PrimitiveType;
+import com.ferox.resource.shader.ShaderAccumulator;
 import com.ferox.resource.shader.Struct;
 import com.ferox.resource.shader.Type;
 
     }
 
     @Override
-    public String emitExpression() {
+    public String emitExpression(ShaderAccumulator shader) {
         if (variable.getPrecedence() < getPrecedence()) {
-            return "(" + variable.emitExpression() + ")." + field;
+            return "(" + variable.emitExpression(shader) + ")." + field;
         } else {
-            return variable.emitExpression() + "." + field;
+            return variable.emitExpression(shader) + "." + field;
         }
     }
 

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/ForLoop.java

         }
 
         // validate loop body
-        Environment scoped = environment.newScope();
+        Environment scoped = environment.newScope(true);
         for (int i = 0; i < body.length; i++) {
             scoped = body[i].validate(scoped);
         }
 
     @Override
     public void emit(ShaderAccumulator accumulator) {
-        accumulator.addLine("for (" + initStatement.emitExpression() + "; " + condition.emitExpression() + "; " + increment.emitExpression() + ") {");
+        accumulator.addLine("for (" + initStatement.emitExpression(accumulator) + "; " + condition.emitExpression(accumulator) + "; " + increment.emitExpression(accumulator) + ") {");
         accumulator.pushIndent();
         for (int i = 0; i < body.length; i++) {
             body[i].emit(accumulator);

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/FunctionCall.java

 
     @Override
     public Environment validate(Environment environment) {
-        // TODO Auto-generated method stub
-        return null;
+        for (int i = 0; i < parameters.length; i++) {
+            environment = parameters[i].validate(environment);
+            if (!parameters[i].getType(environment)
+                              .equals(function.getParameterTypes()[i])) {
+                throw new IllegalStateException("Parameter is of illegal type");
+            }
+        }
+
+        function.validate(environment.getRoot());
+
+        return environment;
     }
 
     @Override
-    public void emit(ShaderAccumulator accumulator) {
-        // TODO Auto-generated method stub
+    public String emitExpression(ShaderAccumulator accumulator) {
+        // parameters do not need to be wrapped in parentheses because they
+        // are unambiguous given the outer parentheses and commas
+        StringBuilder sb = new StringBuilder();
+        sb.append(function.getName());
+        sb.append("(");
+        for (int i = 0; i < parameters.length; i++) {
+            if (i > 0) {
+                sb.append(", ");
+            }
 
+            sb.append(parameters[i].emitExpression(accumulator));
+        }
+        sb.append(")");
+
+        // also include the function definition
+        accumulator.accumulateFunction(function);
+        return sb.toString();
+    }
+
+    @Override
+    public int getPrecedence() {
+        return Precedence.POSTFIX_EXPRESSIONS.ordinal();
     }
 }

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/FunctionDefinition.java

 import java.util.List;
 import java.util.Map;
 
+import com.ferox.resource.shader.Environment;
 import com.ferox.resource.shader.Expression;
 import com.ferox.resource.shader.Function;
 import com.ferox.resource.shader.FunctionBuilder;
+import com.ferox.resource.shader.ShaderAccumulator;
 import com.ferox.resource.shader.Statement;
 import com.ferox.resource.shader.Type;
 import com.ferox.resource.shader.simple_grammar.Parameter.ParameterQualifier;
     }
 
     @Override
+    public Type[] getParameterTypes() {
+        Type[] paramTypes = new Type[params.length];
+        for (int i = 0; i < params.length; i++) {
+            paramTypes[i] = params[i].getType();
+        }
+        return paramTypes;
+    }
+
+    @Override
     public Type getReturnType() {
         return returnType;
     }
 
+    @Override
+    public Environment validate(Environment environment) {
+        Environment bodyEnv = environment.functionScope(returnType, paramMap);
+        for (int i = 0; i < body.length; i++) {
+            bodyEnv = body[i].validate(bodyEnv);
+        }
+
+        return bodyEnv;
+    }
+
+    @Override
+    public void emit(ShaderAccumulator accumulator) {
+        StringBuilder header = new StringBuilder();
+        header.append(returnType.getTypeIdentifier(accumulator, ""));
+        header.append(" ");
+        header.append(name);
+        header.append("(");
+
+        for (int i = 0; i < params.length; i++) {
+            if (i > 0) {
+                header.append(", ");
+            }
+            if (params[i].getQualifier() != ParameterQualifier.NONE) {
+                header.append(params[i].getQualifier().name().toLowerCase());
+                header.append(" ");
+            }
+            header.append(params[i].getType().getTypeIdentifier(accumulator,
+                                                                params[i].getName()));
+        }
+
+        header.append(") {");
+
+        accumulator.addLine(header.toString());
+        accumulator.pushIndent();
+
+        for (int i = 0; i < body.length; i++) {
+            body[i].emit(accumulator);
+        }
+
+        accumulator.popIndent();
+        accumulator.addLine("}");
+    }
+
     public static class Builder implements FunctionBuilder {
         private final List<Parameter> params;
         private final Type returnType;

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/IfThenElse.java

 import com.ferox.resource.shader.Environment;
 import com.ferox.resource.shader.Expression;
 import com.ferox.resource.shader.IfBuilder;
+import com.ferox.resource.shader.PrimitiveType;
 import com.ferox.resource.shader.ShaderAccumulator;
 import com.ferox.resource.shader.Statement;
 
 
     @Override
     public Environment validate(Environment environment) {
-        // TODO Auto-generated method stub
-        return null;
+        environment = condition.validate(environment);
+        if (condition.getType(environment).equals(PrimitiveType.BOOL)) {
+            throw new IllegalStateException("If condition expression must evaluate to a boolean");
+        }
+        if (condition.containsDeclaration()) {
+            throw new IllegalStateException("If condition cannot contain a declaration");
+        }
+
+        // validate true block
+        Environment trueScope = environment.newScope(false);
+        for (int i = 0; i < onTrue.length; i++) {
+            trueScope = onTrue[i].validate(trueScope);
+        }
+
+        // validate false block if it exists
+        if (onFalse != null) {
+            Environment falseScope = environment.newScope(false);
+            for (int i = 0; i < onFalse.length; i++) {
+                falseScope = onFalse[i].validate(falseScope);
+            }
+        }
+
+        return environment;
     }
 
     @Override
     public void emit(ShaderAccumulator accumulator) {
-        // TODO Auto-generated method stub
+        accumulator.addLine("if (" + condition.emitExpression(accumulator) + ") {");
+        accumulator.pushIndent();
+        for (int i = 0; i < onTrue.length; i++) {
+            onTrue[i].emit(accumulator);
+        }
+        accumulator.popIndent();
 
+        if (onFalse != null) {
+            accumulator.addLine("} else {");
+            accumulator.pushIndent();
+            for (int i = 0; i < onFalse.length; i++) {
+                onFalse[i].emit(accumulator);
+            }
+            accumulator.popIndent();
+        }
+
+        accumulator.addLine("}");
     }
 
     public static class Builder implements IfBuilder {

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/Jump.java

 
 import com.ferox.resource.shader.Environment;
 import com.ferox.resource.shader.Expression;
+import com.ferox.resource.shader.PrimitiveType;
 import com.ferox.resource.shader.ShaderAccumulator;
 import com.ferox.resource.shader.Statement;
 
 
     @Override
     public Environment validate(Environment environment) {
-        // TODO Auto-generated method stub
-        return null;
+        switch (type) {
+        case BREAK:
+        case CONTINUE:
+            if (!environment.inLoop()) {
+                throw new IllegalStateException("Continue and break can only be used in loops");
+            }
+            break;
+        case DISCARD:
+            if (!environment.inFragmentShader()) {
+                throw new IllegalStateException("Discard can only be used in a fragment shader");
+            }
+            break;
+        case RETURN:
+            if (returnExpression != null) {
+                if (!returnExpression.getType(environment)
+                                     .equals(environment.getRequiredReturnType())) {
+                    throw new IllegalStateException("Returned expression does not match required return type for function");
+                }
+            } else {
+                if (!environment.getRequiredReturnType().equals(PrimitiveType.VOID)) {
+                    throw new IllegalStateException("Return statement must return void");
+                }
+            }
+            break;
+        }
+
+        return environment;
     }
 
     @Override
     public void emit(ShaderAccumulator accumulator) {
-        // TODO Auto-generated method stub
-
+        if (returnExpression != null) {
+            // type is RETURN and not a void return
+            accumulator.addLine("return " + returnExpression.emitExpression(accumulator) + ";");
+        } else {
+            accumulator.addLine(type.name().toLowerCase() + ";");
+        }
     }
 }

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/LeftAssociative.java

-package com.ferox.resource.shader.simple_grammar;
-
-public interface LeftAssociative {
-
-}

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/LocalDeclaration.java

+package com.ferox.resource.shader.simple_grammar;
+
+import com.ferox.resource.shader.Environment;
+import com.ferox.resource.shader.ShaderAccumulator;
+import com.ferox.resource.shader.Type;
+
+public class LocalDeclaration extends AbstractLValue {
+    private final Type type;
+    private final String identifier;
+
+    public LocalDeclaration(Type type, String identifier) {
+        this.type = type;
+        this.identifier = identifier;
+    }
+
+    @Override
+    public Type getType(Environment env) {
+        return type;
+    }
+
+    @Override
+    public Environment validate(Environment environment) {
+        // declarations are always valid, we just need to create a new scope
+        return environment.declare(type, identifier);
+    }
+
+    @Override
+    public String emitExpression(ShaderAccumulator accumulator) {
+        return type.getTypeIdentifier(accumulator, identifier);
+    }
+
+    @Override
+    public int getPrecedence() {
+        return Precedence.ASSIGNMENT_EXPRESSIONS.ordinal();
+    }
+}

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/RightAssociative.java

-package com.ferox.resource.shader.simple_grammar;
-
-public interface RightAssociative {
-
-}

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/StructDeclaration.java

-package com.ferox.resource.shader.simple_grammar;
-
-public class StructDeclaration implements Declaration, Type {
-    private final String identifier;
-    private final Parameter[] parameters;
-
-    public StructDeclaration(String identifier, Parameter... parameters) {
-        this.identifier = identifier;
-        this.parameters = parameters;
-    }
-}

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/StructDefinition.java

+package com.ferox.resource.shader.simple_grammar;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.ferox.resource.shader.Environment;
+import com.ferox.resource.shader.ShaderAccumulator;
+import com.ferox.resource.shader.Struct;
+import com.ferox.resource.shader.StructBuilder;
+import com.ferox.resource.shader.Type;
+import com.ferox.resource.shader.simple_grammar.Parameter.ParameterQualifier;
+
+public class StructDefinition implements Struct {
+    private final String identifier;
+    private final Parameter[] parameters;
+    private final Map<String, Type> paramMap;
+
+    public StructDefinition(String identifier, Parameter... parameters) {
+        this.identifier = identifier;
+        this.parameters = parameters;
+
+        Map<String, Type> paramMap = new HashMap<String, Type>();
+        for (Parameter p : parameters) {
+            paramMap.put(p.getName(), p.getType());
+        }
+        this.paramMap = Collections.unmodifiableMap(paramMap);
+    }
+
+    @Override
+    public String getTypeIdentifier(ShaderAccumulator accumulator, String varIdentifier) {
+        accumulator.accumulateStruct(this);
+        return identifier + " " + varIdentifier;
+    }
+
+    @Override
+    public Map<String, Type> getFields() {
+        return paramMap;
+    }
+
+    @Override
+    public Environment validate(Environment environment) {
+        // no validation to be done
+        return environment;
+    }
+
+    @Override
+    public void emit(ShaderAccumulator accumulator) {
+        accumulator.addLine("struct " + identifier + "{");
+        accumulator.pushIndent();
+
+        for (int i = 0; i < parameters.length; i++) {
+            accumulator.addLine(parameters[i].getType()
+                                             .getTypeIdentifier(accumulator,
+                                                                parameters[i].getName()) + ";");
+        }
+
+        accumulator.popIndent();
+        accumulator.addLine("}");
+    }
+
+    @Override
+    public String[] getOrderedFields() {
+        String[] fields = new String[parameters.length];
+        for (int i = 0; i < fields.length; i++) {
+            fields[i] = parameters[i].getName();
+        }
+        return fields;
+    }
+
+    public static class Builder implements StructBuilder {
+        private final String identifier;
+        private final List<Parameter> parameters;
+
+        public Builder(String identifier) {
+            this.identifier = identifier;
+            parameters = new ArrayList<Parameter>();
+        }
+
+        @Override
+        public StructBuilder add(Type type, String name) {
+            parameters.add(new Parameter(ParameterQualifier.NONE, type, name));
+            return this;
+        }
+
+        @Override
+        public Struct build() {
+            return new StructDefinition(identifier,
+                                        parameters.toArray(new Parameter[parameters.size()]));
+        }
+    }
+}

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/UnaryExpression.java

 package com.ferox.resource.shader.simple_grammar;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import com.ferox.resource.shader.Environment;
 import com.ferox.resource.shader.Expression;
+import com.ferox.resource.shader.PrimitiveType;
 import com.ferox.resource.shader.ShaderAccumulator;
 import com.ferox.resource.shader.Type;
 
 public class UnaryExpression extends AbstractExpression {
     public static enum UnaryOperator {
-        // FIXME other expressions are between the postfix and prefix in terms of precedence
-        // is that awkward?
-        POSTFIX_INCREMENT,
-        POSTFIX_DECREMENT,
+        POSTFIX_INCREMENT("++"),
+        POSTFIX_DECREMENT("--"),
 
-        PREFIX_INCREMENT, // ++
-        PREFIX_DECREMENT, // --
-        // FIXME make these names nicer? after what they do?
-        PLUS, // +
-        DASH, // -
-        BANG, // !
-        TILDE /* reserved */
+        PREFIX_INCREMENT("++"), // ++
+        PREFIX_DECREMENT("--"), // --
+        NEGATE("-"),
+        LOGICAL_NEGATE("!"),
+        BIT_INVERT("~"); /* reserved */
+
+        private UnaryOperator(String symbol) {
+            this.symbol = symbol;
+        }
+
+        private String symbol;
     }
 
     private final UnaryOperator operator;
 
     @Override
     public Type getType(Environment env) {
-        // TODO Auto-generated method stub
-        return null;
+        return get((PrimitiveType) expression.getType(env), operator);
     }
 
     @Override
     public Environment validate(Environment environment) {
-        // TODO Auto-generated method stub
+        environment = expression.validate(environment);
+
+        Type base = expression.getType(environment);
+        if (!(base instanceof PrimitiveType)) {
+            throw new IllegalStateException("Unary operators only support primitive types");
+        }
+        if (get((PrimitiveType) base, operator) == null) {
+            throw new IllegalStateException("Unary operator not supported on type");
+        }
+        if (expression.containsDeclaration()) {
+            throw new IllegalStateException("Unary operator cannot operate on expressions containing declarations");
+        }
+        return environment;
+    }
+
+    @Override
+    public String emitExpression(ShaderAccumulator accumulator) {
+        String expr = (expression.getPrecedence() < getPrecedence() ? "(" + expression.emitExpression(accumulator) + ")" : expression.emitExpression(accumulator));
+        if (operator == UnaryOperator.POSTFIX_DECREMENT || operator == UnaryOperator.POSTFIX_INCREMENT) {
+            return expr + operator.symbol;
+        } else {
+            return operator.symbol + expr;
+        }
+    }
+
+    @Override
+    public int getPrecedence() {
+        switch (operator) {
+        case LOGICAL_NEGATE:
+        case NEGATE:
+        case PREFIX_DECREMENT:
+        case PREFIX_INCREMENT:
+        case BIT_INVERT:
+            return Precedence.UNARY_EXPRESSIONS.ordinal();
+        case POSTFIX_DECREMENT:
+        case POSTFIX_INCREMENT:
+            return Precedence.POSTFIX_EXPRESSIONS.ordinal();
+        default:
+            throw new UnsupportedOperationException("Unmapped operator, no associated precedence");
+        }
+    }
+
+    private static final Map<UnaryOperator, Map<PrimitiveType, PrimitiveType>> operatorMap = new HashMap<UnaryOperator, Map<PrimitiveType, PrimitiveType>>();
+
+    private static void add(PrimitiveType type, UnaryOperator op, PrimitiveType resultType) {
+        Map<PrimitiveType, PrimitiveType> typeMap = operatorMap.get(op);
+        if (typeMap == null) {
+            typeMap = new HashMap<PrimitiveType, PrimitiveType>();
+            operatorMap.put(op, typeMap);
+        }
+
+        typeMap.put(type, resultType);
+    }
+
+    private static PrimitiveType get(PrimitiveType type, UnaryOperator op) {
+        Map<PrimitiveType, PrimitiveType> typeMap = operatorMap.get(op);
+        if (typeMap != null) {
+            return typeMap.get(type);
+        }
+
         return null;
     }
 
-    @Override
-    public void emit(ShaderAccumulator accumulator) {
-        // TODO Auto-generated method stub
+    static {
+        // float types
+        add(PrimitiveType.FLOAT, UnaryOperator.NEGATE, PrimitiveType.FLOAT);
+        add(PrimitiveType.FLOAT, UnaryOperator.POSTFIX_DECREMENT, PrimitiveType.FLOAT);
+        add(PrimitiveType.FLOAT, UnaryOperator.POSTFIX_INCREMENT, PrimitiveType.FLOAT);
+        add(PrimitiveType.FLOAT, UnaryOperator.PREFIX_DECREMENT, PrimitiveType.FLOAT);
+        add(PrimitiveType.FLOAT, UnaryOperator.PREFIX_INCREMENT, PrimitiveType.FLOAT);
 
+        // int types
+        add(PrimitiveType.INT, UnaryOperator.NEGATE, PrimitiveType.INT);
+        add(PrimitiveType.INT, UnaryOperator.POSTFIX_DECREMENT, PrimitiveType.INT);
+        add(PrimitiveType.INT, UnaryOperator.POSTFIX_INCREMENT, PrimitiveType.INT);
+        add(PrimitiveType.INT, UnaryOperator.PREFIX_DECREMENT, PrimitiveType.INT);
+        add(PrimitiveType.INT, UnaryOperator.PREFIX_INCREMENT, PrimitiveType.INT);
+        add(PrimitiveType.INT, UnaryOperator.BIT_INVERT, PrimitiveType.INT);
+
+        // boolean types
+        add(PrimitiveType.BOOL, UnaryOperator.LOGICAL_NEGATE, PrimitiveType.BOOL);
+
+        // vec2 types
+        add(PrimitiveType.VEC2, UnaryOperator.NEGATE, PrimitiveType.VEC2);
+        add(PrimitiveType.VEC2, UnaryOperator.POSTFIX_DECREMENT, PrimitiveType.VEC2);
+        add(PrimitiveType.VEC2, UnaryOperator.POSTFIX_INCREMENT, PrimitiveType.VEC2);
+        add(PrimitiveType.VEC2, UnaryOperator.PREFIX_DECREMENT, PrimitiveType.VEC2);
+        add(PrimitiveType.VEC2, UnaryOperator.PREFIX_INCREMENT, PrimitiveType.VEC2);
+        add(PrimitiveType.IVEC2, UnaryOperator.NEGATE, PrimitiveType.IVEC2);
+        add(PrimitiveType.IVEC2, UnaryOperator.POSTFIX_DECREMENT, PrimitiveType.IVEC2);
+        add(PrimitiveType.IVEC2, UnaryOperator.POSTFIX_INCREMENT, PrimitiveType.IVEC2);
+        add(PrimitiveType.IVEC2, UnaryOperator.PREFIX_DECREMENT, PrimitiveType.IVEC2);
+        add(PrimitiveType.IVEC2, UnaryOperator.PREFIX_INCREMENT, PrimitiveType.IVEC2);
+        add(PrimitiveType.BVEC2, UnaryOperator.NEGATE, PrimitiveType.BVEC2);
+        add(PrimitiveType.BVEC2, UnaryOperator.POSTFIX_DECREMENT, PrimitiveType.BVEC2);
+        add(PrimitiveType.BVEC2, UnaryOperator.POSTFIX_INCREMENT, PrimitiveType.BVEC2);
+        add(PrimitiveType.BVEC2, UnaryOperator.PREFIX_DECREMENT, PrimitiveType.BVEC2);
+        add(PrimitiveType.BVEC2, UnaryOperator.PREFIX_INCREMENT, PrimitiveType.BVEC2);
+
+        // vec3 types
+        add(PrimitiveType.VEC3, UnaryOperator.NEGATE, PrimitiveType.VEC3);
+        add(PrimitiveType.VEC3, UnaryOperator.POSTFIX_DECREMENT, PrimitiveType.VEC3);
+        add(PrimitiveType.VEC3, UnaryOperator.POSTFIX_INCREMENT, PrimitiveType.VEC3);
+        add(PrimitiveType.VEC3, UnaryOperator.PREFIX_DECREMENT, PrimitiveType.VEC3);
+        add(PrimitiveType.VEC3, UnaryOperator.PREFIX_INCREMENT, PrimitiveType.VEC3);
+        add(PrimitiveType.IVEC3, UnaryOperator.NEGATE, PrimitiveType.IVEC3);
+        add(PrimitiveType.IVEC3, UnaryOperator.POSTFIX_DECREMENT, PrimitiveType.IVEC3);
+        add(PrimitiveType.IVEC3, UnaryOperator.POSTFIX_INCREMENT, PrimitiveType.IVEC3);
+        add(PrimitiveType.IVEC3, UnaryOperator.PREFIX_DECREMENT, PrimitiveType.IVEC3);
+        add(PrimitiveType.IVEC3, UnaryOperator.PREFIX_INCREMENT, PrimitiveType.IVEC3);
+        add(PrimitiveType.BVEC3, UnaryOperator.NEGATE, PrimitiveType.BVEC3);
+        add(PrimitiveType.BVEC3, UnaryOperator.POSTFIX_DECREMENT, PrimitiveType.BVEC3);
+        add(PrimitiveType.BVEC3, UnaryOperator.POSTFIX_INCREMENT, PrimitiveType.BVEC3);
+        add(PrimitiveType.BVEC3, UnaryOperator.PREFIX_DECREMENT, PrimitiveType.BVEC3);
+        add(PrimitiveType.BVEC3, UnaryOperator.PREFIX_INCREMENT, PrimitiveType.BVEC3);
+
+        // vec4 types
+        add(PrimitiveType.VEC4, UnaryOperator.NEGATE, PrimitiveType.VEC4);
+        add(PrimitiveType.VEC4, UnaryOperator.POSTFIX_DECREMENT, PrimitiveType.VEC4);
+        add(PrimitiveType.VEC4, UnaryOperator.POSTFIX_INCREMENT, PrimitiveType.VEC4);
+        add(PrimitiveType.VEC4, UnaryOperator.PREFIX_DECREMENT, PrimitiveType.VEC4);
+        add(PrimitiveType.VEC4, UnaryOperator.PREFIX_INCREMENT, PrimitiveType.VEC4);
+        add(PrimitiveType.IVEC4, UnaryOperator.NEGATE, PrimitiveType.IVEC4);
+        add(PrimitiveType.IVEC4, UnaryOperator.POSTFIX_DECREMENT, PrimitiveType.IVEC4);
+        add(PrimitiveType.IVEC4, UnaryOperator.POSTFIX_INCREMENT, PrimitiveType.IVEC4);
+        add(PrimitiveType.IVEC4, UnaryOperator.PREFIX_DECREMENT, PrimitiveType.IVEC4);
+        add(PrimitiveType.IVEC4, UnaryOperator.PREFIX_INCREMENT, PrimitiveType.IVEC4);
+        add(PrimitiveType.BVEC4, UnaryOperator.NEGATE, PrimitiveType.BVEC4);
+        add(PrimitiveType.BVEC4, UnaryOperator.POSTFIX_DECREMENT, PrimitiveType.BVEC4);
+        add(PrimitiveType.BVEC4, UnaryOperator.POSTFIX_INCREMENT, PrimitiveType.BVEC4);
+        add(PrimitiveType.BVEC4, UnaryOperator.PREFIX_DECREMENT, PrimitiveType.BVEC4);
+        add(PrimitiveType.BVEC4, UnaryOperator.PREFIX_INCREMENT, PrimitiveType.BVEC4);
+
+        // mat2 types
+        add(PrimitiveType.MAT2, UnaryOperator.NEGATE, PrimitiveType.MAT2);
+        add(PrimitiveType.MAT2, UnaryOperator.POSTFIX_DECREMENT, PrimitiveType.MAT2);
+        add(PrimitiveType.MAT2, UnaryOperator.POSTFIX_INCREMENT, PrimitiveType.MAT2);
+        add(PrimitiveType.MAT2, UnaryOperator.PREFIX_DECREMENT, PrimitiveType.MAT2);
+        add(PrimitiveType.MAT2, UnaryOperator.PREFIX_INCREMENT, PrimitiveType.MAT2);
+
+        // mat3 types
+        add(PrimitiveType.MAT3, UnaryOperator.NEGATE, PrimitiveType.MAT3);
+        add(PrimitiveType.MAT3, UnaryOperator.POSTFIX_DECREMENT, PrimitiveType.MAT3);
+        add(PrimitiveType.MAT3, UnaryOperator.POSTFIX_INCREMENT, PrimitiveType.MAT3);
+        add(PrimitiveType.MAT3, UnaryOperator.PREFIX_DECREMENT, PrimitiveType.MAT3);
+        add(PrimitiveType.MAT3, UnaryOperator.PREFIX_INCREMENT, PrimitiveType.MAT3);
     }
 }

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/Variable.java

     }
 
     @Override
-    public void emit(ShaderAccumulator accumulator) {
-        // TODO Auto-generated method stub
+    public String emitExpression(ShaderAccumulator accumulator) {
+        return identifier;
+    }
 
+    @Override
+    public int getPrecedence() {
+        return Precedence.PRIMARY_EXPRESSIONS.ordinal();
     }
 }

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/VariableDeclaration.java

-package com.ferox.resource.shader.simple_grammar;
-
-public class VariableDeclaration implements Declaration {
-    // FIXME type qualifier (does this get consumed by parameter qualifier?)
-    private final Type type;
-    private final String identifier;
-    private final Expression initializer; // nullable for no initializer
-
-    public VariableDeclaration(Type type, String identifier, Expression initializer) {
-        this.type = type;
-        this.identifier = identifier;
-        this.initializer = initializer;
-    }
-}

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/WhileLoop.java

 
 import com.ferox.resource.shader.Environment;
 import com.ferox.resource.shader.Expression;
+import com.ferox.resource.shader.PrimitiveType;
 import com.ferox.resource.shader.ShaderAccumulator;
 import com.ferox.resource.shader.Statement;
 import com.ferox.resource.shader.WhileBuilder;
 
     @Override
     public Environment validate(Environment environment) {
-        // TODO Auto-generated method stub
-        return null;
+        environment = condition.validate(environment);
+        if (!condition.getType(environment).equals(PrimitiveType.BOOL)) {
+            throw new IllegalStateException("Loop condition expression must evaluate to a boolean");
+        }
+
+        // validate loop body
+        Environment scoped = environment.newScope(true);
+        for (int i = 0; i < body.length; i++) {
+            scoped = body[i].validate(scoped);
+        }
+
+        return environment;
     }
 
     @Override
     public void emit(ShaderAccumulator accumulator) {
-        // TODO Auto-generated method stub
-
+        accumulator.addLine("while (" + condition.emitExpression(accumulator) + ") {");
+        accumulator.pushIndent();
+        for (int i = 0; i < body.length; i++) {
+            body[i].emit(accumulator);
+        }
+        accumulator.popIndent();
+        accumulator.addLine("}");
     }
 
     public static class Builder implements WhileBuilder {
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.