Files
gtk/gsk/gskslexpression.c
Benjamin Otte 2bceaedd37 gsksl: Add an error enum
And specify the correct error value whenever an error is emitted.
2017-10-30 02:58:02 +01:00

2046 lines
68 KiB
C

/* GTK - The GIMP Toolkit
*
* Copyright © 2017 Benjamin Otte <otte@gnome.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gskslexpressionprivate.h"
#include "gskslpreprocessorprivate.h"
#include "gskslfunctionprivate.h"
#include "gskslnodeprivate.h"
#include "gskslpointertypeprivate.h"
#include "gskslscopeprivate.h"
#include "gsksltokenizerprivate.h"
#include "gsksltypeprivate.h"
#include "gskslvalueprivate.h"
#include "gskslvariableprivate.h"
#include "gskspvwriterprivate.h"
#include <string.h>
typedef struct _GskSlExpressionClass GskSlExpressionClass;
struct _GskSlExpression {
const GskSlExpressionClass *class;
guint ref_count;
};
struct _GskSlExpressionClass {
void (* free) (GskSlExpression *expression);
void (* print) (const GskSlExpression *expression,
GString *string);
GskSlType * (* get_return_type) (const GskSlExpression *expression);
GskSlValue * (* get_constant) (const GskSlExpression *expression);
guint32 (* write_spv) (const GskSlExpression *expression,
GskSpvWriter *writer);
};
static GskSlExpression *
gsk_sl_expression_alloc (const GskSlExpressionClass *klass,
gsize size)
{
GskSlExpression *expression;
expression = g_slice_alloc0 (size);
expression->class = klass;
expression->ref_count = 1;
return expression;
}
#define gsk_sl_expression_new(_name, _klass) ((_name *) gsk_sl_expression_alloc ((_klass), sizeof (_name)))
/* ASSIGNMENT */
typedef struct _GskSlExpressionAssignment GskSlExpressionAssignment;
struct _GskSlExpressionAssignment {
GskSlExpression parent;
GskSlTokenType op;
GskSlExpression *lvalue;
GskSlExpression *rvalue;
};
static void
gsk_sl_expression_assignment_free (GskSlExpression *expression)
{
GskSlExpressionAssignment *assignment = (GskSlExpressionAssignment *) expression;
gsk_sl_expression_unref (assignment->lvalue);
if (assignment->rvalue)
gsk_sl_expression_unref (assignment->rvalue);
g_slice_free (GskSlExpressionAssignment, assignment);
}
static void
gsk_sl_expression_assignment_print (const GskSlExpression *expression,
GString *string)
{
const GskSlExpressionAssignment *assignment = (const GskSlExpressionAssignment *) expression;
gsk_sl_expression_print (assignment->lvalue, string);
switch ((guint) assignment->op)
{
case GSK_SL_TOKEN_EQUAL:
g_string_append (string, " = ");
break;
case GSK_SL_TOKEN_MUL_ASSIGN:
g_string_append (string, " *= ");
break;
case GSK_SL_TOKEN_DIV_ASSIGN:
g_string_append (string, " /= ");
break;
case GSK_SL_TOKEN_MOD_ASSIGN:
g_string_append (string, " %= ");
break;
case GSK_SL_TOKEN_ADD_ASSIGN:
g_string_append (string, " += ");
break;
case GSK_SL_TOKEN_SUB_ASSIGN:
g_string_append (string, " -= ");
break;
case GSK_SL_TOKEN_LEFT_ASSIGN:
g_string_append (string, " <<= ");
break;
case GSK_SL_TOKEN_RIGHT_ASSIGN:
g_string_append (string, " >>= ");
break;
case GSK_SL_TOKEN_AND_ASSIGN:
g_string_append (string, " &= ");
break;
case GSK_SL_TOKEN_XOR_ASSIGN:
g_string_append (string, " ^= ");
break;
case GSK_SL_TOKEN_OR_ASSIGN:
g_string_append (string, " |= ");
break;
default:
g_assert_not_reached ();
break;
}
gsk_sl_expression_print (assignment->rvalue, string);
}
static GskSlType *
gsk_sl_expression_assignment_get_return_type (const GskSlExpression *expression)
{
const GskSlExpressionAssignment *assignment = (const GskSlExpressionAssignment *) expression;
return gsk_sl_expression_get_return_type (assignment->lvalue);
}
static GskSlValue *
gsk_sl_expression_assignment_get_constant (const GskSlExpression *expression)
{
return NULL;
}
static guint32
gsk_sl_expression_assignment_write_spv (const GskSlExpression *expression,
GskSpvWriter *writer)
{
g_assert_not_reached ();
return 0;
}
static const GskSlExpressionClass GSK_SL_EXPRESSION_ASSIGNMENT = {
gsk_sl_expression_assignment_free,
gsk_sl_expression_assignment_print,
gsk_sl_expression_assignment_get_return_type,
gsk_sl_expression_assignment_get_constant,
gsk_sl_expression_assignment_write_spv
};
/* BINARY */
typedef enum {
GSK_SL_OPERATION_MUL,
GSK_SL_OPERATION_DIV,
GSK_SL_OPERATION_MOD,
GSK_SL_OPERATION_ADD,
GSK_SL_OPERATION_SUB,
GSK_SL_OPERATION_LSHIFT,
GSK_SL_OPERATION_RSHIFT,
GSK_SL_OPERATION_LESS,
GSK_SL_OPERATION_GREATER,
GSK_SL_OPERATION_LESS_EQUAL,
GSK_SL_OPERATION_GREATER_EQUAL,
GSK_SL_OPERATION_EQUAL,
GSK_SL_OPERATION_NOT_EQUAL,
GSK_SL_OPERATION_AND,
GSK_SL_OPERATION_XOR,
GSK_SL_OPERATION_OR,
GSK_SL_OPERATION_LOGICAL_AND,
GSK_SL_OPERATION_LOGICAL_XOR,
GSK_SL_OPERATION_LOGICAL_OR
} GskSlOperation;
typedef struct _GskSlExpressionOperation GskSlExpressionOperation;
struct _GskSlExpressionOperation {
GskSlExpression parent;
GskSlOperation op;
GskSlExpression *left;
GskSlExpression *right;
};
static void
gsk_sl_expression_operation_free (GskSlExpression *expression)
{
GskSlExpressionOperation *operation = (GskSlExpressionOperation *) expression;
gsk_sl_expression_unref (operation->left);
if (operation->right)
gsk_sl_expression_unref (operation->right);
g_slice_free (GskSlExpressionOperation, operation);
}
static void
gsk_sl_expression_operation_print (const GskSlExpression *expression,
GString *string)
{
const char *op_str[] = {
[GSK_SL_OPERATION_MUL] = " * ",
[GSK_SL_OPERATION_DIV] = " / ",
[GSK_SL_OPERATION_MOD] = " % ",
[GSK_SL_OPERATION_ADD] = " + ",
[GSK_SL_OPERATION_SUB] = " - ",
[GSK_SL_OPERATION_LSHIFT] = " << ",
[GSK_SL_OPERATION_RSHIFT] = " >> ",
[GSK_SL_OPERATION_LESS] = " < ",
[GSK_SL_OPERATION_GREATER] = " > ",
[GSK_SL_OPERATION_LESS_EQUAL] = " <= ",
[GSK_SL_OPERATION_GREATER_EQUAL] = " >= ",
[GSK_SL_OPERATION_EQUAL] = " == ",
[GSK_SL_OPERATION_NOT_EQUAL] = " != ",
[GSK_SL_OPERATION_AND] = " & ",
[GSK_SL_OPERATION_XOR] = " ^ ",
[GSK_SL_OPERATION_OR] = " | ",
[GSK_SL_OPERATION_LOGICAL_AND] = " && ",
[GSK_SL_OPERATION_LOGICAL_XOR] = " ^^ ",
[GSK_SL_OPERATION_LOGICAL_OR] = " || "
};
GskSlExpressionOperation *operation = (GskSlExpressionOperation *) expression;
/* XXX: figure out the need for bracketing here */
gsk_sl_expression_print (operation->left, string);
g_string_append (string, op_str[operation->op]);
gsk_sl_expression_print (operation->right, string);
}
static GskSlType *
gsk_sl_expression_arithmetic_type_check (GskSlPreprocessor *stream,
gboolean multiply,
GskSlType *ltype,
GskSlType *rtype)
{
GskSlScalarType scalar;
if (gsk_sl_scalar_type_can_convert (gsk_sl_type_get_scalar_type (ltype),
gsk_sl_type_get_scalar_type (rtype)))
scalar = gsk_sl_type_get_scalar_type (ltype);
else if (gsk_sl_scalar_type_can_convert (gsk_sl_type_get_scalar_type (rtype),
gsk_sl_type_get_scalar_type (ltype)))
scalar = gsk_sl_type_get_scalar_type (rtype);
else
{
if (stream)
{
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH,
"Operand types %s and %s do not share compatible scalar types.",
gsk_sl_type_get_name (ltype), gsk_sl_type_get_name (rtype));
}
return NULL;
}
if (gsk_sl_type_is_matrix (ltype))
{
if (gsk_sl_type_is_matrix (rtype))
{
if (multiply)
{
if (gsk_sl_type_get_length (ltype) != gsk_sl_type_get_length (gsk_sl_type_get_index_type (rtype)))
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH,
"Matrices to multiplication have incompatible dimensions.");
return NULL;
}
return gsk_sl_type_get_matrix (scalar,
gsk_sl_type_get_length (gsk_sl_type_get_index_type (ltype)),
gsk_sl_type_get_length (rtype));
}
else
{
if (gsk_sl_type_can_convert (ltype, rtype))
{
return ltype;
}
else if (gsk_sl_type_can_convert (rtype, ltype))
{
return rtype;
}
else
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH,
"Matrix types %s and %s have different size.",
gsk_sl_type_get_name (ltype), gsk_sl_type_get_name (rtype));
return NULL;
}
}
}
else if (gsk_sl_type_is_vector (rtype))
{
if (multiply)
{
if (gsk_sl_type_get_length (ltype) != gsk_sl_type_get_length (rtype))
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH,
"Matrix column count doesn't match vector length.");
return NULL;
}
return gsk_sl_type_get_vector (scalar, gsk_sl_type_get_length (gsk_sl_type_get_index_type (ltype)));
}
else
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH,
"Cannot perform arithmetic operation between matrix and vector.");
return NULL;
}
}
else if (gsk_sl_type_is_scalar (rtype))
{
return gsk_sl_type_get_matrix (scalar,
gsk_sl_type_get_length (ltype),
gsk_sl_type_get_length (gsk_sl_type_get_index_type (ltype)));
}
else
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH,
"Right operand is incompatible type for arithemtic operation.");
return NULL;
}
}
else if (gsk_sl_type_is_vector (ltype))
{
if (gsk_sl_type_is_matrix (rtype))
{
if (multiply)
{
if (gsk_sl_type_get_length (ltype) != gsk_sl_type_get_length (gsk_sl_type_get_index_type (rtype)))
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH,
"Vector length for %s doesn't match row count for %s",
gsk_sl_type_get_name (ltype), gsk_sl_type_get_name (rtype));
return NULL;
}
return gsk_sl_type_get_vector (scalar, gsk_sl_type_get_length (rtype));
}
else
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, "Cannot perform arithmetic operation between vector and matrix.");
return NULL;
}
}
else if (gsk_sl_type_is_vector (rtype))
{
if (gsk_sl_type_get_length (ltype) != gsk_sl_type_get_length (rtype))
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH,
"Vector operands %s and %s to arithmetic operation have different length.",
gsk_sl_type_get_name (ltype), gsk_sl_type_get_name (rtype));
return NULL;
}
return gsk_sl_type_get_vector (scalar, gsk_sl_type_get_length (ltype));
}
else if (gsk_sl_type_is_scalar (rtype))
{
return gsk_sl_type_get_vector (scalar,
gsk_sl_type_get_length (ltype));
}
else
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH,
"Right operand is incompatible type for arithemtic operation.");
return NULL;
}
}
else if (gsk_sl_type_is_scalar (ltype))
{
if (gsk_sl_type_is_matrix (rtype))
{
return gsk_sl_type_get_matrix (scalar,
gsk_sl_type_get_length (rtype),
gsk_sl_type_get_length (gsk_sl_type_get_index_type (rtype)));
}
else if (gsk_sl_type_is_vector (rtype))
{
return gsk_sl_type_get_vector (scalar,
gsk_sl_type_get_length (rtype));
}
else if (gsk_sl_type_is_scalar (rtype))
{
return gsk_sl_type_get_scalar (scalar);
}
else
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, "Right operand is incompatible type for arithemtic operation.");
return NULL;
}
}
else
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, "Left operand is incompatible type for arithemtic operation.");
return NULL;
}
}
static GskSlType *
gsk_sl_expression_bitwise_type_check (GskSlPreprocessor *stream,
GskSlType *ltype,
GskSlType *rtype)
{
GskSlScalarType lscalar, rscalar;
lscalar = gsk_sl_type_get_scalar_type (ltype);
if (lscalar != GSK_SL_INT && lscalar != GSK_SL_UINT)
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, "Left operand %s is not an integer type.", gsk_sl_type_get_name (ltype));
return NULL;
}
rscalar = gsk_sl_type_get_scalar_type (ltype);
if (rscalar != GSK_SL_INT && rscalar != GSK_SL_UINT)
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, "Right operand %s is not an integer type.", gsk_sl_type_get_name (rtype));
return NULL;
}
if (!gsk_sl_type_is_scalar (ltype) && !gsk_sl_type_is_vector (ltype))
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, "Left operand %s is neither a scalar nor a vector.", gsk_sl_type_get_name (ltype));
return NULL;
}
if (!gsk_sl_type_is_scalar (rtype) && !gsk_sl_type_is_vector (rtype))
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, "Right operand %s is neither a scalar nor a vector.", gsk_sl_type_get_name (rtype));
return NULL;
}
if (gsk_sl_type_is_vector (ltype) && gsk_sl_type_is_vector (rtype) &&
gsk_sl_type_get_length (ltype) != gsk_sl_type_get_length (rtype))
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH,
"Vector operands %s and %s do not have the same length.",
gsk_sl_type_get_name (ltype), gsk_sl_type_get_name (rtype));
return NULL;
}
rscalar = lscalar == GSK_SL_UINT ? GSK_SL_UINT : rscalar;
if (gsk_sl_type_is_scalar (ltype) && gsk_sl_type_is_scalar (rtype))
return gsk_sl_type_get_scalar (rscalar);
else
return gsk_sl_type_get_vector (rscalar, gsk_sl_type_get_length (ltype));
}
static gboolean
gsk_sl_expression_shift_type_check (GskSlPreprocessor *stream,
GskSlType *ltype,
GskSlType *rtype)
{
GskSlScalarType lscalar, rscalar;
lscalar = gsk_sl_type_get_scalar_type (ltype);
if (lscalar != GSK_SL_INT && lscalar != GSK_SL_UINT)
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, "Left operand %s is not an integer type.", gsk_sl_type_get_name (ltype));
return FALSE;
}
rscalar = gsk_sl_type_get_scalar_type (ltype);
if (rscalar != GSK_SL_INT && rscalar != GSK_SL_UINT)
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, "Right operand %s is not an integer type.", gsk_sl_type_get_name (rtype));
return FALSE;
}
if (!gsk_sl_type_is_scalar (ltype) && !gsk_sl_type_is_vector (ltype))
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, "Left operand %s is neither a scalar nor a vector.", gsk_sl_type_get_name (ltype));
return FALSE;
}
if (!gsk_sl_type_is_scalar (rtype) && !gsk_sl_type_is_vector (rtype))
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, "Right operand %s is neither a scalar nor a vector.", gsk_sl_type_get_name (rtype));
return FALSE;
}
if (gsk_sl_type_is_scalar (ltype) && gsk_sl_type_is_vector (rtype))
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, "Right operand to shift cannot be a vector if left operand is a scalar.");
return FALSE;
}
if (gsk_sl_type_is_vector (ltype) && gsk_sl_type_is_vector (rtype) &&
gsk_sl_type_get_length (ltype) != gsk_sl_type_get_length (rtype))
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, "Vector operands do not have the same length.");
return FALSE;
}
return TRUE;
}
static gboolean
gsk_sl_expression_relational_type_check (GskSlPreprocessor *stream,
GskSlType *ltype,
GskSlType *rtype)
{
if (!gsk_sl_type_is_scalar (ltype))
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, "Left operand to relational operator is not a scalar.");
return FALSE;
}
if (gsk_sl_type_get_scalar_type (ltype) == GSK_SL_BOOL)
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, "Left operand to relational operator must not be bool.");
return FALSE;
}
if (!gsk_sl_type_is_scalar (rtype))
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, "Right operand to relational operator is not a scalar.");
return FALSE;
}
if (gsk_sl_type_get_scalar_type (rtype) == GSK_SL_BOOL)
{
if (stream)
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, "Right operand to relational operator must not be bool.");
return FALSE;
}
return TRUE;
}
static GskSlType *
gsk_sl_expression_operation_get_return_type (const GskSlExpression *expression)
{
GskSlExpressionOperation *operation = (GskSlExpressionOperation *) expression;
switch (operation->op)
{
case GSK_SL_OPERATION_MUL:
return gsk_sl_expression_arithmetic_type_check (NULL,
TRUE,
gsk_sl_expression_get_return_type (operation->left),
gsk_sl_expression_get_return_type (operation->right));
case GSK_SL_OPERATION_DIV:
case GSK_SL_OPERATION_ADD:
case GSK_SL_OPERATION_SUB:
return gsk_sl_expression_arithmetic_type_check (NULL,
FALSE,
gsk_sl_expression_get_return_type (operation->left),
gsk_sl_expression_get_return_type (operation->right));
case GSK_SL_OPERATION_LSHIFT:
case GSK_SL_OPERATION_RSHIFT:
return gsk_sl_expression_get_return_type (operation->left);
case GSK_SL_OPERATION_MOD:
case GSK_SL_OPERATION_AND:
case GSK_SL_OPERATION_XOR:
case GSK_SL_OPERATION_OR:
return gsk_sl_expression_bitwise_type_check (NULL,
gsk_sl_expression_get_return_type (operation->left),
gsk_sl_expression_get_return_type (operation->right));
case GSK_SL_OPERATION_LESS:
case GSK_SL_OPERATION_GREATER:
case GSK_SL_OPERATION_LESS_EQUAL:
case GSK_SL_OPERATION_GREATER_EQUAL:
case GSK_SL_OPERATION_EQUAL:
case GSK_SL_OPERATION_NOT_EQUAL:
case GSK_SL_OPERATION_LOGICAL_AND:
case GSK_SL_OPERATION_LOGICAL_XOR:
case GSK_SL_OPERATION_LOGICAL_OR:
return gsk_sl_type_get_scalar (GSK_SL_BOOL);
default:
g_assert_not_reached ();
return NULL;
}
}
static GskSlValue *
gsk_sl_expression_operation_get_constant (const GskSlExpression *expression)
{
//const GskSlExpressionOperation *operation = (const GskSlExpressionOperation *) expression;
/* FIXME: These need constant evaluations */
return NULL;
}
static guint32
gsk_sl_expression_operation_write_spv (const GskSlExpression *expression,
GskSpvWriter *writer)
{
g_assert_not_reached ();
return 0;
}
static const GskSlExpressionClass GSK_SL_EXPRESSION_OPERATION = {
gsk_sl_expression_operation_free,
gsk_sl_expression_operation_print,
gsk_sl_expression_operation_get_return_type,
gsk_sl_expression_operation_get_constant,
gsk_sl_expression_operation_write_spv
};
/* REFERENCE */
typedef struct _GskSlExpressionReference GskSlExpressionReference;
struct _GskSlExpressionReference {
GskSlExpression parent;
GskSlVariable *variable;
};
static void
gsk_sl_expression_reference_free (GskSlExpression *expression)
{
GskSlExpressionReference *reference = (GskSlExpressionReference *) expression;
gsk_sl_variable_unref (reference->variable);
g_slice_free (GskSlExpressionReference, reference);
}
static void
gsk_sl_expression_reference_print (const GskSlExpression *expression,
GString *string)
{
const GskSlExpressionReference *reference = (const GskSlExpressionReference *) expression;
g_string_append (string, gsk_sl_variable_get_name (reference->variable));
}
static GskSlType *
gsk_sl_expression_reference_get_return_type (const GskSlExpression *expression)
{
const GskSlExpressionReference *reference = (const GskSlExpressionReference *) expression;
return gsk_sl_pointer_type_get_type (gsk_sl_variable_get_type (reference->variable));
}
static GskSlValue *
gsk_sl_expression_reference_get_constant (const GskSlExpression *expression)
{
GskSlExpressionReference *reference = (GskSlExpressionReference *) expression;
const GskSlValue *initial_value;
if (!gsk_sl_variable_is_constant (reference->variable))
return NULL;
initial_value = gsk_sl_variable_get_initial_value (reference->variable);
if (initial_value == NULL)
return NULL;
return gsk_sl_value_copy (initial_value);
}
static guint32
gsk_sl_expression_reference_write_spv (const GskSlExpression *expression,
GskSpvWriter *writer)
{
GskSlExpressionReference *reference = (GskSlExpressionReference *) expression;
guint32 declaration_id, result_id, type_id;
type_id = gsk_spv_writer_get_id_for_type (writer, gsk_sl_pointer_type_get_type (gsk_sl_variable_get_type (reference->variable)));
declaration_id = gsk_spv_writer_get_id_for_variable (writer, reference->variable);
result_id = gsk_spv_writer_next_id (writer);
gsk_spv_writer_add (writer,
GSK_SPV_WRITER_SECTION_CODE,
4, GSK_SPV_OP_LOAD,
(guint32[3]) { type_id,
result_id,
declaration_id });
return result_id;
}
static const GskSlExpressionClass GSK_SL_EXPRESSION_REFERENCE = {
gsk_sl_expression_reference_free,
gsk_sl_expression_reference_print,
gsk_sl_expression_reference_get_return_type,
gsk_sl_expression_reference_get_constant,
gsk_sl_expression_reference_write_spv
};
/* FUNCTION_CALL */
typedef struct _GskSlExpressionFunctionCall GskSlExpressionFunctionCall;
struct _GskSlExpressionFunctionCall {
GskSlExpression parent;
GskSlFunction *function;
GskSlExpression **arguments;
guint n_arguments;
};
static void
gsk_sl_expression_function_call_free (GskSlExpression *expression)
{
GskSlExpressionFunctionCall *function_call = (GskSlExpressionFunctionCall *) expression;
guint i;
for (i = 0; i < function_call->n_arguments; i++)
{
gsk_sl_expression_unref (function_call->arguments[i]);
}
g_free (function_call->arguments);
gsk_sl_function_unref (function_call->function);
g_slice_free (GskSlExpressionFunctionCall, function_call);
}
static void
gsk_sl_expression_function_call_print (const GskSlExpression *expression,
GString *string)
{
const GskSlExpressionFunctionCall *function_call = (const GskSlExpressionFunctionCall *) expression;
guint i;
g_string_append (string, gsk_sl_function_get_name (function_call->function));
g_string_append (string, " (");
for (i = 0; i < function_call->n_arguments; i++)
{
if (i > 0)
g_string_append (string, ", ");
gsk_sl_expression_print (function_call->arguments[i], string);
}
g_string_append (string, ")");
}
static GskSlType *
gsk_sl_expression_function_call_get_return_type (const GskSlExpression *expression)
{
const GskSlExpressionFunctionCall *function_call = (const GskSlExpressionFunctionCall *) expression;
return gsk_sl_function_get_return_type (function_call->function);
}
static GskSlValue *
gsk_sl_expression_function_call_get_constant (const GskSlExpression *expression)
{
/* FIXME: some functions are constant */
return NULL;
}
static guint32
gsk_sl_expression_function_call_write_spv (const GskSlExpression *expression,
GskSpvWriter *writer)
{
g_assert_not_reached ();
return 0;
}
static const GskSlExpressionClass GSK_SL_EXPRESSION_FUNCTION_CALL = {
gsk_sl_expression_function_call_free,
gsk_sl_expression_function_call_print,
gsk_sl_expression_function_call_get_return_type,
gsk_sl_expression_function_call_get_constant,
gsk_sl_expression_function_call_write_spv
};
/* SWIZZLE */
typedef enum {
GSK_SL_SWIZZLE_POINT,
GSK_SL_SWIZZLE_COLOR,
GSK_SL_SWIZZLE_TEXCOORD
} GskSlSwizzleName;
static const char *swizzle_options[] = { "xyzw", "rgba", "stpq" };
typedef struct _GskSlExpressionSwizzle GskSlExpressionSwizzle;
struct _GskSlExpressionSwizzle {
GskSlExpression parent;
GskSlExpression *expr;
GskSlSwizzleName name;
guint length;
guint indexes[4];
};
static void
gsk_sl_expression_swizzle_free (GskSlExpression *expression)
{
GskSlExpressionSwizzle *swizzle = (GskSlExpressionSwizzle *) expression;
gsk_sl_expression_unref (swizzle->expr);
g_slice_free (GskSlExpressionSwizzle, swizzle);
}
static void
gsk_sl_expression_swizzle_print (const GskSlExpression *expression,
GString *string)
{
const GskSlExpressionSwizzle *swizzle = (const GskSlExpressionSwizzle *) expression;
guint i;
gsk_sl_expression_print (swizzle->expr, string);
g_string_append (string, ".");
for (i = 0; i < swizzle->length; i++)
{
g_string_append_c (string, swizzle_options[swizzle->name][swizzle->indexes[i]]);
}
}
static GskSlType *
gsk_sl_expression_swizzle_get_return_type (const GskSlExpression *expression)
{
const GskSlExpressionSwizzle *swizzle = (const GskSlExpressionSwizzle *) expression;
GskSlType *type;
type = gsk_sl_expression_get_return_type (swizzle->expr);
if (swizzle->length == 1)
return gsk_sl_type_get_scalar (gsk_sl_type_get_scalar_type (type));
else
return gsk_sl_type_get_vector (gsk_sl_type_get_scalar_type (type), swizzle->length);
}
static GskSlValue *
gsk_sl_expression_swizzle_get_constant (const GskSlExpression *expression)
{
const GskSlExpressionSwizzle *swizzle = (const GskSlExpressionSwizzle *) expression;
GskSlValue *result, *value;
guchar *sdata, *ddata;
gsize sstride, dstride;
GskSlScalarType scalar_type;
guint i;
value = gsk_sl_expression_get_constant (swizzle->expr);
if (value == NULL)
return NULL;
scalar_type = gsk_sl_type_get_scalar_type (gsk_sl_value_get_type (value));
sdata = gsk_sl_value_get_data (value);
sstride = gsk_sl_type_get_index_stride (gsk_sl_value_get_type (value));
result = gsk_sl_value_new (gsk_sl_expression_get_return_type (expression));
ddata = gsk_sl_value_get_data (result);
dstride = gsk_sl_type_get_index_stride (gsk_sl_value_get_type (result));
for (i = 0; i < swizzle->length; i++)
{
gsk_sl_scalar_type_convert_value (scalar_type,
ddata + dstride * i,
scalar_type,
sdata + sstride * swizzle->indexes[i]);
}
gsk_sl_value_free (value);
return result;
}
static guint32
gsk_sl_expression_swizzle_write_spv (const GskSlExpression *expression,
GskSpvWriter *writer)
{
const GskSlExpressionSwizzle *swizzle = (const GskSlExpressionSwizzle *) expression;
GskSlType *type;
guint32 expr_id, type_id, result_id;
type = gsk_sl_expression_get_return_type (swizzle->expr);
expr_id = gsk_sl_expression_write_spv (swizzle->expr, writer);
if (gsk_sl_type_is_scalar (type))
{
if (swizzle->length == 1)
return expr_id;
type_id = gsk_spv_writer_get_id_for_type (writer, gsk_sl_expression_get_return_type (expression));
result_id = gsk_spv_writer_next_id (writer);
gsk_spv_writer_add (writer,
GSK_SPV_WRITER_SECTION_CODE,
3 + swizzle->length, GSK_SPV_OP_COMPOSITE_CONSTRUCT,
(guint32[6]) { type_id,
result_id,
expr_id,
expr_id,
expr_id,
expr_id });
return result_id;
}
else if (gsk_sl_type_is_vector (type))
{
type_id = gsk_spv_writer_get_id_for_type (writer, gsk_sl_expression_get_return_type (expression));
result_id = gsk_spv_writer_next_id (writer);
if (swizzle->length == 1)
{
gsk_spv_writer_add (writer,
GSK_SPV_WRITER_SECTION_CODE,
4, GSK_SPV_OP_COMPOSITE_EXTRACT,
(guint32[6]) { type_id,
result_id,
swizzle->indexes[0] });
}
else
{
gsk_spv_writer_add (writer,
GSK_SPV_WRITER_SECTION_CODE,
5 + swizzle->length, GSK_SPV_OP_COMPOSITE_CONSTRUCT,
(guint32[8]) { type_id,
result_id,
expr_id,
expr_id,
swizzle->indexes[0],
swizzle->indexes[1],
swizzle->indexes[2],
swizzle->indexes[3] });
}
return result_id;
}
else
{
g_assert_not_reached ();
return 0;
}
}
static const GskSlExpressionClass GSK_SL_EXPRESSION_SWIZZLE = {
gsk_sl_expression_swizzle_free,
gsk_sl_expression_swizzle_print,
gsk_sl_expression_swizzle_get_return_type,
gsk_sl_expression_swizzle_get_constant,
gsk_sl_expression_swizzle_write_spv
};
/* CONSTANT */
typedef struct _GskSlExpressionConstant GskSlExpressionConstant;
struct _GskSlExpressionConstant {
GskSlExpression parent;
GskSlValue *value;
};
static void
gsk_sl_expression_constant_free (GskSlExpression *expression)
{
GskSlExpressionConstant *constant = (GskSlExpressionConstant *) expression;
gsk_sl_value_free (constant->value);
}
static void
gsk_sl_expression_constant_print (const GskSlExpression *expression,
GString *string)
{
const GskSlExpressionConstant *constant = (const GskSlExpressionConstant *) expression;
gsk_sl_value_print (constant->value, string);
}
static GskSlType *
gsk_sl_expression_constant_get_return_type (const GskSlExpression *expression)
{
const GskSlExpressionConstant *constant = (const GskSlExpressionConstant *) expression;
return gsk_sl_value_get_type (constant->value);
}
static GskSlValue *
gsk_sl_expression_constant_get_constant (const GskSlExpression *expression)
{
const GskSlExpressionConstant *constant = (const GskSlExpressionConstant *) expression;
return gsk_sl_value_copy (constant->value);
}
static guint32
gsk_sl_expression_constant_write_spv (const GskSlExpression *expression,
GskSpvWriter *writer)
{
const GskSlExpressionConstant *constant = (const GskSlExpressionConstant *) expression;
return gsk_spv_writer_get_id_for_value (writer, constant->value);
}
static const GskSlExpressionClass GSK_SL_EXPRESSION_CONSTANT = {
gsk_sl_expression_constant_free,
gsk_sl_expression_constant_print,
gsk_sl_expression_constant_get_return_type,
gsk_sl_expression_constant_get_constant,
gsk_sl_expression_constant_write_spv
};
GskSlExpression *
gsk_sl_expression_parse_constructor_call (GskSlScope *scope,
GskSlPreprocessor *stream,
GskSlType *type)
{
GskSlExpressionFunctionCall *call;
const GskSlToken *token;
GskSlType **types;
GError *err = NULL;
gboolean fail = FALSE;
guint i;
call = gsk_sl_expression_new (GskSlExpressionFunctionCall, &GSK_SL_EXPRESSION_FUNCTION_CALL);
call->function = gsk_sl_function_new_constructor (type);
g_assert (call->function);
token = gsk_sl_preprocessor_get (stream);
if (!gsk_sl_token_is (token, GSK_SL_TOKEN_LEFT_PAREN))
{
gsk_sl_preprocessor_error (stream, SYNTAX, "Expected opening \"(\" when calling constructor");
gsk_sl_expression_unref ((GskSlExpression *) call);
return NULL;
}
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) call);
token = gsk_sl_preprocessor_get (stream);
if (!gsk_sl_token_is (token, GSK_SL_TOKEN_RIGHT_PAREN))
{
GPtrArray *arguments;
arguments = g_ptr_array_new ();
while (TRUE)
{
GskSlExpression *expression = gsk_sl_expression_parse_assignment (scope, stream);
if (expression != NULL)
g_ptr_array_add (arguments, expression);
else
fail = TRUE;
token = gsk_sl_preprocessor_get (stream);
if (!gsk_sl_token_is (token, GSK_SL_TOKEN_COMMA))
break;
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) call);
}
call->n_arguments = arguments->len;
call->arguments = (GskSlExpression **) g_ptr_array_free (arguments, FALSE);
}
types = g_newa (GskSlType *, call->n_arguments);
for (i = 0; i < call->n_arguments; i++)
types[i] = gsk_sl_expression_get_return_type (call->arguments[i]);
if (!gsk_sl_function_matches (call->function, types, call->n_arguments, &err))
{
gsk_sl_preprocessor_emit_error (stream, TRUE, gsk_sl_preprocessor_get_location (stream), err);
g_clear_error (&err);
fail = TRUE;
}
if (!gsk_sl_token_is (token, GSK_SL_TOKEN_RIGHT_PAREN))
{
gsk_sl_preprocessor_error (stream, SYNTAX, "Expected closing \")\" after arguments.");
gsk_sl_expression_unref ((GskSlExpression *) call);
return NULL;
}
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) call);
if (fail)
{
gsk_sl_expression_unref ((GskSlExpression *) call);
return NULL;
}
return (GskSlExpression *) call;
}
static GskSlExpression *
gsk_sl_expression_parse_primary (GskSlScope *scope,
GskSlPreprocessor *stream)
{
GskSlExpressionConstant *constant;
const GskSlToken *token;
token = gsk_sl_preprocessor_get (stream);
switch ((guint) token->type)
{
case GSK_SL_TOKEN_IDENTIFIER:
{
GskSlExpressionReference *reference;
GskSlVariable *variable;
variable = gsk_sl_scope_lookup_variable (scope, token->str);
if (variable == NULL)
{
gsk_sl_preprocessor_error (stream, DECLARATION, "No variable named \"%s\".", token->str);
gsk_sl_preprocessor_consume (stream, NULL);
return NULL;
}
reference = gsk_sl_expression_new (GskSlExpressionReference, &GSK_SL_EXPRESSION_REFERENCE);
reference->variable = gsk_sl_variable_ref (variable);
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) reference);
return (GskSlExpression *) reference;
}
case GSK_SL_TOKEN_INTCONSTANT:
constant = gsk_sl_expression_new (GskSlExpressionConstant, &GSK_SL_EXPRESSION_CONSTANT);
constant->value = gsk_sl_value_new (gsk_sl_type_get_scalar (GSK_SL_INT));
*(gint32 *) gsk_sl_value_get_data (constant->value) = token->i32;
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) constant);
return (GskSlExpression *) constant;
case GSK_SL_TOKEN_UINTCONSTANT:
constant = gsk_sl_expression_new (GskSlExpressionConstant, &GSK_SL_EXPRESSION_CONSTANT);
constant->value = gsk_sl_value_new (gsk_sl_type_get_scalar (GSK_SL_UINT));
*(guint32 *) gsk_sl_value_get_data (constant->value) = token->u32;
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) constant);
return (GskSlExpression *) constant;
case GSK_SL_TOKEN_FLOATCONSTANT:
constant = gsk_sl_expression_new (GskSlExpressionConstant, &GSK_SL_EXPRESSION_CONSTANT);
constant->value = gsk_sl_value_new (gsk_sl_type_get_scalar (GSK_SL_FLOAT));
*(float *) gsk_sl_value_get_data (constant->value) = token->f;
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) constant);
return (GskSlExpression *) constant;
case GSK_SL_TOKEN_BOOLCONSTANT:
constant = gsk_sl_expression_new (GskSlExpressionConstant, &GSK_SL_EXPRESSION_CONSTANT);
constant->value = gsk_sl_value_new (gsk_sl_type_get_scalar (GSK_SL_BOOL));
*(guint32 *) gsk_sl_value_get_data (constant->value) = token->b;
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) constant);
return (GskSlExpression *) constant;
case GSK_SL_TOKEN_DOUBLECONSTANT:
constant = gsk_sl_expression_new (GskSlExpressionConstant, &GSK_SL_EXPRESSION_CONSTANT);
constant->value = gsk_sl_value_new (gsk_sl_type_get_scalar (GSK_SL_DOUBLE));
*(double *) gsk_sl_value_get_data (constant->value) = token->f;
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) constant);
return (GskSlExpression *) constant;
case GSK_SL_TOKEN_VOID:
case GSK_SL_TOKEN_FLOAT:
case GSK_SL_TOKEN_DOUBLE:
case GSK_SL_TOKEN_INT:
case GSK_SL_TOKEN_UINT:
case GSK_SL_TOKEN_BOOL:
case GSK_SL_TOKEN_BVEC2:
case GSK_SL_TOKEN_BVEC3:
case GSK_SL_TOKEN_BVEC4:
case GSK_SL_TOKEN_IVEC2:
case GSK_SL_TOKEN_IVEC3:
case GSK_SL_TOKEN_IVEC4:
case GSK_SL_TOKEN_UVEC2:
case GSK_SL_TOKEN_UVEC3:
case GSK_SL_TOKEN_UVEC4:
case GSK_SL_TOKEN_VEC2:
case GSK_SL_TOKEN_VEC3:
case GSK_SL_TOKEN_VEC4:
case GSK_SL_TOKEN_DVEC2:
case GSK_SL_TOKEN_DVEC3:
case GSK_SL_TOKEN_DVEC4:
case GSK_SL_TOKEN_MAT2:
case GSK_SL_TOKEN_MAT3:
case GSK_SL_TOKEN_MAT4:
case GSK_SL_TOKEN_DMAT2:
case GSK_SL_TOKEN_DMAT3:
case GSK_SL_TOKEN_DMAT4:
case GSK_SL_TOKEN_MAT2X2:
case GSK_SL_TOKEN_MAT2X3:
case GSK_SL_TOKEN_MAT2X4:
case GSK_SL_TOKEN_MAT3X2:
case GSK_SL_TOKEN_MAT3X3:
case GSK_SL_TOKEN_MAT3X4:
case GSK_SL_TOKEN_MAT4X2:
case GSK_SL_TOKEN_MAT4X3:
case GSK_SL_TOKEN_MAT4X4:
case GSK_SL_TOKEN_DMAT2X2:
case GSK_SL_TOKEN_DMAT2X3:
case GSK_SL_TOKEN_DMAT2X4:
case GSK_SL_TOKEN_DMAT3X2:
case GSK_SL_TOKEN_DMAT3X3:
case GSK_SL_TOKEN_DMAT3X4:
case GSK_SL_TOKEN_DMAT4X2:
case GSK_SL_TOKEN_DMAT4X3:
case GSK_SL_TOKEN_DMAT4X4:
{
GskSlType *type;
type = gsk_sl_type_new_parse (stream);
if (type == NULL)
return NULL;
return gsk_sl_expression_parse_constructor_call (scope, stream, type);
}
default:
gsk_sl_preprocessor_error (stream, SYNTAX, "Expected an expression.");
gsk_sl_preprocessor_consume (stream, NULL);
return NULL;
}
}
static GskSlExpression *
gsk_sl_expression_parse_field_selection (GskSlScope *scope,
GskSlPreprocessor *stream,
GskSlExpression *expr,
const char *name,
gboolean *success)
{
GskSlType *type;
if (g_str_equal (name, "length"))
{
gsk_sl_preprocessor_error (stream, UNSUPPORTED, ".length() is not implemented yet.");
*success = FALSE;
return expr;
}
type = gsk_sl_expression_get_return_type (expr);
if (gsk_sl_type_is_scalar (type) || gsk_sl_type_is_vector (type))
{
GskSlExpressionSwizzle *swizzle;
guint type_length = MAX (1, gsk_sl_type_get_length (type));
swizzle = gsk_sl_expression_new (GskSlExpressionSwizzle, &GSK_SL_EXPRESSION_SWIZZLE);
for (swizzle->name = 0; swizzle->name < G_N_ELEMENTS(swizzle_options); swizzle->name++)
{
const char *found = strchr (swizzle_options[swizzle->name], name[0]);
if (found)
break;
}
if (swizzle->name == G_N_ELEMENTS(swizzle_options))
{
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, "Type %s has no member named \"%s\".", gsk_sl_type_get_name (type), name);
gsk_sl_expression_unref ((GskSlExpression *) swizzle);
*success = FALSE;
return expr;
}
swizzle->expr = expr;
for (swizzle->length = 0; swizzle->length < 4 && name[swizzle->length]; swizzle->length++)
{
const char *found = strchr (swizzle_options[swizzle->name], name[swizzle->length]);
if (found == NULL)
{
gsk_sl_preprocessor_error (stream, SYNTAX,
"Character '%c' is not valid for swizzle. Must be one of \"%s\".",
name[swizzle->length], swizzle_options[swizzle->name]);
*success = FALSE;
return (GskSlExpression *) swizzle;
}
swizzle->indexes[swizzle->length] = found - swizzle_options[swizzle->name];
if (swizzle->indexes[swizzle->length] >= type_length)
{
gsk_sl_preprocessor_error (stream, SYNTAX,
"Swizzle index '%c' not allowed for type %s",
name[swizzle->length], gsk_sl_type_get_name (type));
*success = FALSE;
return (GskSlExpression *) swizzle;
}
}
if (name[swizzle->length])
{
gsk_sl_preprocessor_error (stream, SYNTAX, "Too many swizzle options. A maximum of 4 characters are allowed.");
*success = FALSE;
return (GskSlExpression *) swizzle;
}
return (GskSlExpression *) swizzle;
}
else
{
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, "Type %s has no fields to select.", gsk_sl_type_get_name (type));
*success = FALSE;
return expr;
}
}
static GskSlExpression *
gsk_sl_expression_parse_postfix (GskSlScope *scope,
GskSlPreprocessor *stream)
{
GskSlExpression *expr;
const GskSlToken *token;
gboolean success = TRUE;
expr = gsk_sl_expression_parse_primary (scope, stream);
while (TRUE)
{
token = gsk_sl_preprocessor_get (stream);
if (gsk_sl_token_is (token, GSK_SL_TOKEN_DOT))
{
gsk_sl_preprocessor_consume (stream, NULL);
token = gsk_sl_preprocessor_get (stream);
if (gsk_sl_token_is (token, GSK_SL_TOKEN_IDENTIFIER))
{
char *field = g_strdup (token->str);
gsk_sl_preprocessor_consume (stream, NULL);
expr = gsk_sl_expression_parse_field_selection (scope, stream, expr, field, &success);
g_free (field);
}
else
{
gsk_sl_preprocessor_error (stream, SYNTAX, "Expected an identifier to select a field.");
success = FALSE;
continue;
}
}
else
{
break;
}
}
if (!success)
{
gsk_sl_expression_unref (expr);
return NULL;
}
return expr;
}
static GskSlExpression *
gsk_sl_expression_parse_unary (GskSlScope *scope,
GskSlPreprocessor *stream)
{
return gsk_sl_expression_parse_postfix (scope, stream);
}
static GskSlExpression *
gsk_sl_expression_parse_multiplicative (GskSlScope *scope,
GskSlPreprocessor *stream)
{
const GskSlToken *token;
GskSlExpression *expression;
GskSlExpressionOperation *operation;
GskSlOperation op;
expression = gsk_sl_expression_parse_unary (scope, stream);
if (expression == NULL)
return NULL;
while (TRUE)
{
token = gsk_sl_preprocessor_get (stream);
if (gsk_sl_token_is (token, GSK_SL_TOKEN_STAR))
op = GSK_SL_OPERATION_MUL;
else if (gsk_sl_token_is (token, GSK_SL_TOKEN_SLASH))
op = GSK_SL_OPERATION_DIV;
else if (gsk_sl_token_is (token, GSK_SL_TOKEN_PERCENT))
op = GSK_SL_OPERATION_MOD;
else
return expression;
operation = gsk_sl_expression_new (GskSlExpressionOperation, &GSK_SL_EXPRESSION_OPERATION);
operation->left = expression;
operation->op = op;
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) operation);
operation->right = gsk_sl_expression_parse_unary (scope, stream);
if (operation->right == NULL)
{
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else if ((op == GSK_SL_OPERATION_MOD &&
!gsk_sl_expression_bitwise_type_check (stream,
gsk_sl_expression_get_return_type (operation->left),
gsk_sl_expression_get_return_type (operation->right))) ||
(op != GSK_SL_OPERATION_MOD &&
!gsk_sl_expression_arithmetic_type_check (stream,
FALSE,
gsk_sl_expression_get_return_type (operation->left),
gsk_sl_expression_get_return_type (operation->right))))
{
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else
{
expression = (GskSlExpression *) operation;
}
}
return expression;
}
static GskSlExpression *
gsk_sl_expression_parse_additive (GskSlScope *scope,
GskSlPreprocessor *stream)
{
const GskSlToken *token;
GskSlExpression *expression;
GskSlExpressionOperation *operation;
GskSlOperation op;
expression = gsk_sl_expression_parse_multiplicative (scope, stream);
if (expression == NULL)
return NULL;
while (TRUE)
{
token = gsk_sl_preprocessor_get (stream);
if (gsk_sl_token_is (token, GSK_SL_TOKEN_PLUS))
op = GSK_SL_OPERATION_ADD;
else if (gsk_sl_token_is (token, GSK_SL_TOKEN_DASH))
op = GSK_SL_OPERATION_SUB;
else
return expression;
operation = gsk_sl_expression_new (GskSlExpressionOperation, &GSK_SL_EXPRESSION_OPERATION);
operation->left = expression;
operation->op = op;
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) operation);
operation->right = gsk_sl_expression_parse_additive (scope, stream);
if (operation->right == NULL)
{
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else if (!gsk_sl_expression_arithmetic_type_check (stream,
FALSE,
gsk_sl_expression_get_return_type (operation->left),
gsk_sl_expression_get_return_type (operation->right)))
{
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else
{
expression = (GskSlExpression *) operation;
}
}
return expression;
}
static GskSlExpression *
gsk_sl_expression_parse_shift (GskSlScope *scope,
GskSlPreprocessor *stream)
{
const GskSlToken *token;
GskSlExpression *expression;
GskSlExpressionOperation *operation;
GskSlOperation op;
expression = gsk_sl_expression_parse_additive (scope, stream);
if (expression == NULL)
return NULL;
while (TRUE)
{
token = gsk_sl_preprocessor_get (stream);
if (gsk_sl_token_is (token, GSK_SL_TOKEN_LEFT_OP))
op = GSK_SL_OPERATION_LSHIFT;
else if (gsk_sl_token_is (token, GSK_SL_TOKEN_RIGHT_OP))
op = GSK_SL_OPERATION_RSHIFT;
else
return expression;
operation = gsk_sl_expression_new (GskSlExpressionOperation, &GSK_SL_EXPRESSION_OPERATION);
operation->left = expression;
operation->op = op;
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) operation);
operation->right = gsk_sl_expression_parse_additive (scope, stream);
if (operation->right == NULL)
{
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else if (!gsk_sl_expression_shift_type_check (stream,
gsk_sl_expression_get_return_type (operation->left),
gsk_sl_expression_get_return_type (operation->right)))
{
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else
{
expression = (GskSlExpression *) operation;
}
}
return expression;
}
static GskSlExpression *
gsk_sl_expression_parse_relational (GskSlScope *scope,
GskSlPreprocessor *stream)
{
const GskSlToken *token;
GskSlExpression *expression;
GskSlExpressionOperation *operation;
GskSlOperation op;
expression = gsk_sl_expression_parse_shift (scope, stream);
if (expression == NULL)
return NULL;
while (TRUE)
{
token = gsk_sl_preprocessor_get (stream);
if (gsk_sl_token_is (token, GSK_SL_TOKEN_LEFT_ANGLE))
op = GSK_SL_OPERATION_LESS;
else if (gsk_sl_token_is (token, GSK_SL_TOKEN_RIGHT_ANGLE))
op = GSK_SL_OPERATION_GREATER;
else if (gsk_sl_token_is (token, GSK_SL_TOKEN_LE_OP))
op = GSK_SL_OPERATION_LESS_EQUAL;
else if (gsk_sl_token_is (token, GSK_SL_TOKEN_GE_OP))
op = GSK_SL_OPERATION_GREATER_EQUAL;
else
return expression;
operation = gsk_sl_expression_new (GskSlExpressionOperation, &GSK_SL_EXPRESSION_OPERATION);
operation->left = expression;
operation->op = op;
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) operation);
operation->right = gsk_sl_expression_parse_shift (scope, stream);
if (operation->right == NULL)
{
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else if (!gsk_sl_expression_relational_type_check (stream,
gsk_sl_expression_get_return_type (operation->left),
gsk_sl_expression_get_return_type (operation->right)))
{
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else
{
expression = (GskSlExpression *) operation;
}
}
return expression;
}
static GskSlExpression *
gsk_sl_expression_parse_equality (GskSlScope *scope,
GskSlPreprocessor *stream)
{
const GskSlToken *token;
GskSlExpression *expression;
GskSlExpressionOperation *operation;
GskSlOperation op;
expression = gsk_sl_expression_parse_relational (scope, stream);
if (expression == NULL)
return NULL;
while (TRUE)
{
token = gsk_sl_preprocessor_get (stream);
if (gsk_sl_token_is (token, GSK_SL_TOKEN_EQ_OP))
op = GSK_SL_OPERATION_EQUAL;
else if (gsk_sl_token_is (token, GSK_SL_TOKEN_NE_OP))
op = GSK_SL_OPERATION_NOT_EQUAL;
else
return expression;
operation = gsk_sl_expression_new (GskSlExpressionOperation, &GSK_SL_EXPRESSION_OPERATION);
operation->left = expression;
operation->op = op;
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) operation);
operation->right = gsk_sl_expression_parse_relational (scope, stream);
if (operation->right == NULL)
{
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else
{
expression = (GskSlExpression *) operation;
}
}
return expression;
}
static GskSlExpression *
gsk_sl_expression_parse_and (GskSlScope *scope,
GskSlPreprocessor *stream)
{
const GskSlToken *token;
GskSlExpression *expression;
GskSlExpressionOperation *operation;
expression = gsk_sl_expression_parse_equality (scope, stream);
if (expression == NULL)
return NULL;
while (TRUE)
{
token = gsk_sl_preprocessor_get (stream);
if (!gsk_sl_token_is (token, GSK_SL_TOKEN_AMPERSAND))
return expression;
operation = gsk_sl_expression_new (GskSlExpressionOperation, &GSK_SL_EXPRESSION_OPERATION);
operation->left = expression;
operation->op = GSK_SL_OPERATION_AND;
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) operation);
operation->right = gsk_sl_expression_parse_equality (scope, stream);
if (operation->right == NULL)
{
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else if (!gsk_sl_expression_bitwise_type_check (stream,
gsk_sl_expression_get_return_type (operation->left),
gsk_sl_expression_get_return_type (operation->right)))
{
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else
{
expression = (GskSlExpression *) operation;
}
}
return expression;
}
static GskSlExpression *
gsk_sl_expression_parse_xor (GskSlScope *scope,
GskSlPreprocessor *stream)
{
const GskSlToken *token;
GskSlExpression *expression;
GskSlExpressionOperation *operation;
expression = gsk_sl_expression_parse_and (scope, stream);
if (expression == NULL)
return NULL;
while (TRUE)
{
token = gsk_sl_preprocessor_get (stream);
if (!gsk_sl_token_is (token, GSK_SL_TOKEN_CARET))
return expression;
operation = gsk_sl_expression_new (GskSlExpressionOperation, &GSK_SL_EXPRESSION_OPERATION);
operation->left = expression;
operation->op = GSK_SL_OPERATION_XOR;
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) operation);
operation->right = gsk_sl_expression_parse_and (scope, stream);
if (operation->right == NULL)
{
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else if (!gsk_sl_expression_bitwise_type_check (stream,
gsk_sl_expression_get_return_type (operation->left),
gsk_sl_expression_get_return_type (operation->right)))
{
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else
{
expression = (GskSlExpression *) operation;
}
}
return expression;
}
static GskSlExpression *
gsk_sl_expression_parse_or (GskSlScope *scope,
GskSlPreprocessor *stream)
{
const GskSlToken *token;
GskSlExpression *expression;
GskSlExpressionOperation *operation;
expression = gsk_sl_expression_parse_xor (scope, stream);
if (expression == NULL)
return NULL;
while (TRUE)
{
token = gsk_sl_preprocessor_get (stream);
if (!gsk_sl_token_is (token, GSK_SL_TOKEN_VERTICAL_BAR))
return expression;
operation = gsk_sl_expression_new (GskSlExpressionOperation, &GSK_SL_EXPRESSION_OPERATION);
operation->left = expression;
operation->op = GSK_SL_OPERATION_OR;
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) operation);
operation->right = gsk_sl_expression_parse_xor (scope, stream);
if (operation->right == NULL)
{
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else if (!gsk_sl_expression_bitwise_type_check (stream,
gsk_sl_expression_get_return_type (operation->left),
gsk_sl_expression_get_return_type (operation->right)))
{
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else
{
expression = (GskSlExpression *) operation;
}
}
return expression;
}
static GskSlExpression *
gsk_sl_expression_parse_logical_and (GskSlScope *scope,
GskSlPreprocessor *stream)
{
const GskSlToken *token;
GskSlExpression *expression;
GskSlExpressionOperation *operation;
expression = gsk_sl_expression_parse_or (scope, stream);
if (expression == NULL)
return NULL;
while (TRUE)
{
token = gsk_sl_preprocessor_get (stream);
if (!gsk_sl_token_is (token, GSK_SL_TOKEN_AND_OP))
return expression;
operation = gsk_sl_expression_new (GskSlExpressionOperation, &GSK_SL_EXPRESSION_OPERATION);
operation->left = expression;
operation->op = GSK_SL_OPERATION_LOGICAL_AND;
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) operation);
operation->right = gsk_sl_expression_parse_or (scope, stream);
if (operation->right == NULL)
{
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else if (!gsk_sl_type_can_convert (gsk_sl_type_get_scalar (GSK_SL_BOOL),
gsk_sl_expression_get_return_type (operation->right)))
{
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH,
"Right operand of && expression is not bool but %s",
gsk_sl_type_get_name (gsk_sl_expression_get_return_type (operation->right)));
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else if (!gsk_sl_type_can_convert (gsk_sl_type_get_scalar (GSK_SL_BOOL),
gsk_sl_expression_get_return_type (expression)))
{
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH,
"Left operand of && expression is not bool but %s",
gsk_sl_type_get_name (gsk_sl_expression_get_return_type (expression)));
expression = operation->right;
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else
{
expression = (GskSlExpression *) operation;
}
}
return expression;
}
static GskSlExpression *
gsk_sl_expression_parse_logical_xor (GskSlScope *scope,
GskSlPreprocessor *stream)
{
const GskSlToken *token;
GskSlExpression *expression;
GskSlExpressionOperation *operation;
expression = gsk_sl_expression_parse_logical_and (scope, stream);
if (expression == NULL)
return NULL;
while (TRUE)
{
token = gsk_sl_preprocessor_get (stream);
if (!gsk_sl_token_is (token, GSK_SL_TOKEN_XOR_OP))
return expression;
operation = gsk_sl_expression_new (GskSlExpressionOperation, &GSK_SL_EXPRESSION_OPERATION);
operation->left = expression;
operation->op = GSK_SL_OPERATION_LOGICAL_XOR;
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) operation);
operation->right = gsk_sl_expression_parse_logical_and (scope, stream);
if (operation->right == NULL)
{
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else if (!gsk_sl_type_can_convert (gsk_sl_type_get_scalar (GSK_SL_BOOL),
gsk_sl_expression_get_return_type (operation->right)))
{
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH,
"Right operand of ^^ expression is not bool but %s",
gsk_sl_type_get_name (gsk_sl_expression_get_return_type (operation->right)));
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else if (!gsk_sl_type_can_convert (gsk_sl_type_get_scalar (GSK_SL_BOOL),
gsk_sl_expression_get_return_type (expression)))
{
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH,
"Left operand of ^^ expression is not bool but %s",
gsk_sl_type_get_name (gsk_sl_expression_get_return_type (expression)));
expression = operation->right;
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else
{
expression = (GskSlExpression *) operation;
}
}
return expression;
}
static GskSlExpression *
gsk_sl_expression_parse_logical_or (GskSlScope *scope,
GskSlPreprocessor *stream)
{
const GskSlToken *token;
GskSlExpression *expression;
GskSlExpressionOperation *operation;
expression = gsk_sl_expression_parse_logical_xor (scope, stream);
if (expression == NULL)
return NULL;
while (TRUE)
{
token = gsk_sl_preprocessor_get (stream);
if (!gsk_sl_token_is (token, GSK_SL_TOKEN_OR_OP))
return expression;
operation = gsk_sl_expression_new (GskSlExpressionOperation, &GSK_SL_EXPRESSION_OPERATION);
operation->left = expression;
operation->op = GSK_SL_OPERATION_LOGICAL_OR;
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) operation);
operation->right = gsk_sl_expression_parse_logical_xor (scope, stream);
if (operation->right == NULL)
{
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else if (!gsk_sl_type_can_convert (gsk_sl_type_get_scalar (GSK_SL_BOOL),
gsk_sl_expression_get_return_type (operation->right)))
{
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH,
"Right operand of || expression is not bool but %s",
gsk_sl_type_get_name (gsk_sl_expression_get_return_type (operation->right)));
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else if (!gsk_sl_type_can_convert (gsk_sl_type_get_scalar (GSK_SL_BOOL),
gsk_sl_expression_get_return_type (expression)))
{
gsk_sl_preprocessor_error (stream, TYPE_MISMATCH,
"Left operand of || expression is not bool but %s",
gsk_sl_type_get_name (gsk_sl_expression_get_return_type (expression)));
expression = operation->right;
gsk_sl_expression_ref (expression);
gsk_sl_expression_unref ((GskSlExpression *) operation);
}
else
{
expression = (GskSlExpression *) operation;
}
}
return expression;
}
static GskSlExpression *
gsk_sl_expression_parse_conditional (GskSlScope *scope,
GskSlPreprocessor *stream)
{
/* XXX: support conditionals */
return gsk_sl_expression_parse_logical_or (scope, stream);
}
GskSlExpression *
gsk_sl_expression_parse_constant (GskSlScope *scope,
GskSlPreprocessor *stream)
{
return gsk_sl_expression_parse_conditional (scope, stream);
}
GskSlExpression *
gsk_sl_expression_parse_assignment (GskSlScope *scope,
GskSlPreprocessor *stream)
{
const GskSlToken *token;
GskSlExpression *lvalue;
GskSlExpressionAssignment *assign;
lvalue = gsk_sl_expression_parse_conditional (scope, stream);
if (lvalue == NULL)
return NULL;
token = gsk_sl_preprocessor_get (stream);
switch ((guint) token->type)
{
case GSK_SL_TOKEN_EQUAL:
case GSK_SL_TOKEN_MUL_ASSIGN:
case GSK_SL_TOKEN_DIV_ASSIGN:
case GSK_SL_TOKEN_MOD_ASSIGN:
case GSK_SL_TOKEN_ADD_ASSIGN:
case GSK_SL_TOKEN_SUB_ASSIGN:
case GSK_SL_TOKEN_LEFT_ASSIGN:
case GSK_SL_TOKEN_RIGHT_ASSIGN:
case GSK_SL_TOKEN_AND_ASSIGN:
case GSK_SL_TOKEN_XOR_ASSIGN:
case GSK_SL_TOKEN_OR_ASSIGN:
break;
default:
return lvalue;
}
#if 0
if (gsk_sl_expression_is_constant (lvalue))
{
gsk_sl_preprocessor_error (stream, "Cannot assign to a return lvalue.");
/* Continue parsing like normal here to get more errors */
gsk_sl_preprocessor_consume (stream, lvalue);
gsk_sl_expression_unref (lvalue);
return gsk_sl_expression_parse_assignment (scope, stream);
}
#endif
assign = gsk_sl_expression_new (GskSlExpressionAssignment, &GSK_SL_EXPRESSION_ASSIGNMENT);
assign->lvalue = lvalue;
assign->op = token->type;
gsk_sl_preprocessor_consume (stream, (GskSlExpression *) assign);
assign->rvalue = gsk_sl_expression_parse_assignment (scope, stream);
if (assign->rvalue == NULL)
{
gsk_sl_expression_unref ((GskSlExpression *) assign);
return lvalue;
}
return (GskSlExpression *) assign;
}
GskSlExpression *
gsk_sl_expression_parse (GskSlScope *scope,
GskSlPreprocessor *stream)
{
/* XXX: Allow comma here */
return gsk_sl_expression_parse_assignment (scope, stream);
}
GskSlExpression *
gsk_sl_expression_ref (GskSlExpression *expression)
{
g_return_val_if_fail (expression != NULL, NULL);
expression->ref_count += 1;
return expression;
}
void
gsk_sl_expression_unref (GskSlExpression *expression)
{
if (expression == NULL)
return;
expression->ref_count -= 1;
if (expression->ref_count > 0)
return;
expression->class->free (expression);
}
void
gsk_sl_expression_print (const GskSlExpression *expression,
GString *string)
{
expression->class->print (expression, string);
}
GskSlType *
gsk_sl_expression_get_return_type (const GskSlExpression *expression)
{
return expression->class->get_return_type (expression);
}
GskSlValue *
gsk_sl_expression_get_constant (const GskSlExpression *expression)
{
return expression->class->get_constant (expression);
}
guint32
gsk_sl_expression_write_spv (const GskSlExpression *expression,
GskSpvWriter *writer)
{
return expression->class->write_spv (expression, writer);
}