From 11ab6f823bb609822ce02659e995453770ad15dd Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Sat, 30 Sep 2017 01:41:01 +0200 Subject: [PATCH] gsksl: Add support for overloaded functions This changes GskSlScope to allow multiple functions of the same name. It also introduces GskSlFunctionMatcher, which does overload matching in a way that can be used while parsing function calls. --- gsk/gskslcompiler.h | 1 + gsk/gskslexpression.c | 104 ++++++++++++----- gsk/gskslexpressionprivate.h | 1 + gsk/gskslfunction.c | 212 +++++++++++++++++++++++++++++------ gsk/gskslfunctionprivate.h | 25 ++++- gsk/gskslnode.c | 12 +- gsk/gskslprogram.c | 9 +- gsk/gskslscope.c | 38 +++++-- gsk/gskslscopeprivate.h | 3 +- gsk/gsksltype.c | 17 ++- gsk/gsksltypesprivate.h | 1 + 11 files changed, 338 insertions(+), 85 deletions(-) diff --git a/gsk/gskslcompiler.h b/gsk/gskslcompiler.h index a0422604a4..c5103f292b 100644 --- a/gsk/gskslcompiler.h +++ b/gsk/gskslcompiler.h @@ -36,6 +36,7 @@ typedef enum { GSK_SL_COMPILER_ERROR_SYNTAX, GSK_SL_COMPILER_ERROR_TOKENIZER, GSK_SL_COMPILER_ERROR_TYPE_MISMATCH, + GSK_SL_COMPILER_ERROR_UNIQUENESS, GSK_SL_COMPILER_ERROR_UNSUPPORTED, } GskSlCompilerError; diff --git a/gsk/gskslexpression.c b/gsk/gskslexpression.c index 973328afe7..7264884709 100644 --- a/gsk/gskslexpression.c +++ b/gsk/gskslexpression.c @@ -1279,16 +1279,14 @@ gsk_sl_constructor_get_args_by_type (const GskSlType *type) } GskSlExpression * -gsk_sl_expression_parse_function_call (GskSlScope *scope, - GskSlPreprocessor *stream, - GskSlFunction *function) +gsk_sl_expression_parse_function_call (GskSlScope *scope, + GskSlPreprocessor *stream, + GskSlFunctionMatcher *matcher, + GskSlFunction *function) { GskSlExpressionFunctionCall *call; const GskSlToken *token; - GskSlType **types; - GError *error = NULL; gssize missing_args; /* only used for builtin constructors */ - guint i; call = gsk_sl_expression_new (GskSlExpressionFunctionCall, &GSK_SL_EXPRESSION_FUNCTION_CALL); @@ -1316,13 +1314,33 @@ gsk_sl_expression_parse_function_call (GskSlScope *scope, { GskSlExpression *expression = gsk_sl_expression_parse_assignment (scope, stream); - g_ptr_array_add (arguments, expression); - - if (function == NULL) + if (function == NULL && matcher == NULL) { /* no checking necessary */ } - if (missing_args == 0) + else if (matcher) + { + GskSlType *type = gsk_sl_expression_get_return_type (expression); + + gsk_sl_function_matcher_match_argument (matcher, arguments->len, type); + if (!gsk_sl_function_matcher_has_matches (matcher)) + { + if (function) + gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, + "Cannot convert argument %u from %s to %s.", + arguments->len + 1, + gsk_sl_type_get_name (type), + gsk_sl_type_get_name (gsk_sl_function_get_argument_type (function, arguments->len))); + else + gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, + "No overloaded function available that matches the first %u arguments", + arguments->len + 1); + + matcher = NULL; + function = NULL; + } + } + else if (missing_args == 0) { gsk_sl_preprocessor_error (stream, ARGUMENT_COUNT, "Too many arguments given to builtin constructor, only the first %u are necessary.", @@ -1347,6 +1365,8 @@ gsk_sl_expression_parse_function_call (GskSlScope *scope, } } + g_ptr_array_add (arguments, expression); + token = gsk_sl_preprocessor_get (stream); if (!gsk_sl_token_is (token, GSK_SL_TOKEN_COMMA)) break; @@ -1365,14 +1385,29 @@ gsk_sl_expression_parse_function_call (GskSlScope *scope, 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 (function && missing_args < 0 && !gsk_sl_function_matches (function, types, call->n_arguments, &error)) + if (matcher) { - gsk_sl_preprocessor_emit_error (stream, TRUE, gsk_sl_preprocessor_get_location (stream), error); - g_clear_error (&error); - function = NULL; + gsk_sl_function_matcher_match_n_arguments (matcher, call->n_arguments); + if (!gsk_sl_function_matcher_has_matches (matcher)) + { + if (function) + gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, + "Function %s needs %"G_GSIZE_FORMAT" arguments, but %u given.", + gsk_sl_function_get_name (function), + gsk_sl_function_get_n_arguments (function), + call->n_arguments); + else + gsk_sl_preprocessor_error (stream, TYPE_MISMATCH, + "No overloaded function available that matches this call"); + function = NULL; + } + else + { + function = gsk_sl_function_matcher_get_match (matcher); + if (function == NULL) + gsk_sl_preprocessor_error (stream, UNIQUENESS, + "Cannot find unique match for overloaded function."); + } } if (!gsk_sl_token_is (token, GSK_SL_TOKEN_RIGHT_PAREN)) @@ -1387,8 +1422,7 @@ gsk_sl_expression_parse_function_call (GskSlScope *scope, gsk_sl_expression_unref ((GskSlExpression *) call); return gsk_sl_expression_error_new (); } - else - + call->function = gsk_sl_function_ref (function); return (GskSlExpression *) call; } @@ -1407,10 +1441,23 @@ gsk_sl_expression_parse_primary (GskSlScope *scope, { GskSlExpression *expr; GskSlVariable *variable; + GskSlType *type; char *name; - if (gsk_sl_scope_lookup_type (scope, token->str)) - goto its_a_type; + type = gsk_sl_scope_lookup_type (scope, token->str); + if (type) + { + GskSlFunctionMatcher matcher; + GskSlFunction *constructor; + + constructor = gsk_sl_function_new_constructor (type); + gsk_sl_function_matcher_init (&matcher, g_list_prepend (NULL, constructor)); + expr = gsk_sl_expression_parse_function_call (scope, stream, &matcher, constructor); + gsk_sl_function_matcher_finish (&matcher); + gsk_sl_function_unref (constructor); + + return expr; + } name = g_strdup (token->str); gsk_sl_preprocessor_consume (stream, NULL); @@ -1418,12 +1465,18 @@ gsk_sl_expression_parse_primary (GskSlScope *scope, token = gsk_sl_preprocessor_get (stream); if (gsk_sl_token_is (token, GSK_SL_TOKEN_LEFT_PAREN)) { - GskSlFunction *function = gsk_sl_scope_lookup_function (scope, name); + GskSlFunctionMatcher matcher; - if (function == NULL) + gsk_sl_scope_match_function (scope, &matcher, name); + + if (!gsk_sl_function_matcher_has_matches (&matcher)) gsk_sl_preprocessor_error (stream, DECLARATION, "No function named \"%s\".", name); - expr = gsk_sl_expression_parse_function_call (scope, stream, function); + expr = gsk_sl_expression_parse_function_call (scope, stream, + &matcher, + gsk_sl_function_matcher_get_match (&matcher)); + + gsk_sl_function_matcher_finish (&matcher); } else { @@ -1549,10 +1602,9 @@ gsk_sl_expression_parse_primary (GskSlScope *scope, GskSlExpression *expression; GskSlType *type; -its_a_type: type = gsk_sl_type_new_parse (scope, stream); constructor = gsk_sl_function_new_constructor (type); - expression = gsk_sl_expression_parse_function_call (scope, stream, constructor); + expression = gsk_sl_expression_parse_function_call (scope, stream, NULL, constructor); gsk_sl_function_unref (constructor); gsk_sl_type_unref (type); diff --git a/gsk/gskslexpressionprivate.h b/gsk/gskslexpressionprivate.h index 3fb1811ed3..02be57c28c 100644 --- a/gsk/gskslexpressionprivate.h +++ b/gsk/gskslexpressionprivate.h @@ -31,6 +31,7 @@ GskSlExpression * gsk_sl_expression_parse_constant (GskSlScope GskSlPreprocessor *stream); GskSlExpression * gsk_sl_expression_parse_function_call (GskSlScope *scope, GskSlPreprocessor *stream, + GskSlFunctionMatcher *matcher, GskSlFunction *function); GskSlExpression * gsk_sl_expression_ref (GskSlExpression *expression); diff --git a/gsk/gskslfunction.c b/gsk/gskslfunction.c index 75d4f1bf6e..09c2a06efc 100644 --- a/gsk/gskslfunction.c +++ b/gsk/gskslfunction.c @@ -568,42 +568,6 @@ gsk_sl_function_print (const GskSlFunction *function, function->class->print (function, string); } -gboolean -gsk_sl_function_matches (const GskSlFunction *function, - GskSlType **arguments, - gsize n_arguments, - GError **error) -{ - guint i; - - if (n_arguments != gsk_sl_function_get_n_arguments (function)) - { - g_set_error (error, - GSK_SL_COMPILER_ERROR, GSK_SL_COMPILER_ERROR_TYPE_MISMATCH, - "Function %s needs %"G_GSIZE_FORMAT" arguments, but %"G_GSIZE_FORMAT" given.", - gsk_sl_function_get_name (function), - gsk_sl_function_get_n_arguments (function), - n_arguments); - return FALSE; - } - - for (i = 0; i < n_arguments; i++) - { - if (!gsk_sl_type_can_convert (gsk_sl_function_get_argument_type (function, i), arguments[i])) - { - g_set_error (error, - GSK_SL_COMPILER_ERROR, GSK_SL_COMPILER_ERROR_TYPE_MISMATCH, - "Cannot convert argument %u from %s to %s.", - i + 1, - gsk_sl_type_get_name (arguments[i]), - gsk_sl_type_get_name (gsk_sl_function_get_argument_type (function, i))); - return FALSE; - } - } - - return TRUE; -} - guint32 gsk_sl_function_write_spv (const GskSlFunction *function, GskSpvWriter *writer) @@ -611,3 +575,179 @@ gsk_sl_function_write_spv (const GskSlFunction *function, return function->class->write_spv (function, writer); } +void +gsk_sl_function_matcher_init (GskSlFunctionMatcher *matcher, + GList *list) +{ + matcher->best_matches = list; + matcher->matches = NULL; +} + +void +gsk_sl_function_matcher_finish (GskSlFunctionMatcher *matcher) +{ + g_list_free (matcher->best_matches); + g_list_free (matcher->matches); +} + +gboolean +gsk_sl_function_matcher_has_matches (GskSlFunctionMatcher *matcher) +{ + return matcher->best_matches || matcher->matches; +} + +GskSlFunction * +gsk_sl_function_matcher_get_match (GskSlFunctionMatcher *matcher) +{ + if (matcher->best_matches == NULL) + return NULL; + + if (matcher->best_matches->next != NULL) + return NULL; + + return matcher->best_matches->data; +} + +void +gsk_sl_function_matcher_match_n_arguments (GskSlFunctionMatcher *matcher, + gsize n_arguments) +{ + GList *l; + + for (l = matcher->best_matches; l; l = l->next) + { + if (gsk_sl_function_get_n_arguments (l->data) != n_arguments) + matcher->best_matches = g_list_delete_link (matcher->best_matches, l); + } + for (l = matcher->matches; l; l = l->next) + { + if (gsk_sl_function_get_n_arguments (l->data) != n_arguments) + matcher->matches = g_list_delete_link (matcher->matches, l); + } +} + +typedef enum { + MATCH_NONE, + MATCH_CONVERT_TO_DOUBLE, + MATCH_CONVERT, + MATCH_EXACT +} GskSlFunctionMatch; + +static GskSlFunctionMatch +gsk_sl_function_matcher_match_types (const GskSlType *function_type, + const GskSlType *argument_type) +{ + if (!gsk_sl_type_can_convert (function_type, argument_type)) + return MATCH_NONE; + + if (gsk_sl_type_equal (function_type, argument_type)) + return MATCH_EXACT; + + if (gsk_sl_type_get_scalar_type (function_type)) + return MATCH_CONVERT_TO_DOUBLE; + + return MATCH_CONVERT; +} + +void +gsk_sl_function_matcher_match_argument (GskSlFunctionMatcher *matcher, + gsize n, + const GskSlType *argument_type) +{ + GList *best_matches = NULL, *matches = NULL, *l; + GskSlFunctionMatch best = MATCH_NONE, function_match; + + for (l = matcher->best_matches; l; l = l->next) + { + GskSlType *function_type; + GskSlFunctionMatch function_match; + + if (gsk_sl_function_get_n_arguments (l->data) <= n) + continue; + + function_type = gsk_sl_function_get_argument_type (l->data, n); + function_match = gsk_sl_function_matcher_match_types (function_type, argument_type); + if (function_match == MATCH_NONE) + continue; + + if (function_match == best) + { + best_matches = g_list_prepend (best_matches, l->data); + best = function_match; + } + else if (function_match > best) + { + matches = g_list_concat (matches, best_matches); + best_matches = g_list_prepend (NULL, l->data); + best = function_match; + } + else + { + matches = g_list_prepend (matches, l->data); + } + } + + for (l = matcher->matches; l; l = l->next) + { + GskSlType *function_type; + + if (gsk_sl_function_get_n_arguments (l->data) <= n) + continue; + + function_type = gsk_sl_function_get_argument_type (l->data, n); + function_match = gsk_sl_function_matcher_match_types (function_type, argument_type); + if (function_match == MATCH_NONE) + continue; + + if (function_match > best) + { + matches = g_list_concat (matches, best_matches); + best_matches = NULL; + best = function_match; + } + matches = g_list_prepend (matches, l->data); + } + + g_list_free (matcher->best_matches); + g_list_free (matcher->matches); + matcher->best_matches = best_matches; + matcher->matches = matches; +} + +void +gsk_sl_function_matcher_match_function (GskSlFunctionMatcher *matcher, + const GskSlFunction *function) +{ + GList *l; + gsize i, n; + + n = gsk_sl_function_get_n_arguments (function); + + for (l = matcher->best_matches; l; l = l->next) + { + GskSlFunction *f = l->data; + + if (gsk_sl_function_get_n_arguments (f) != n) + continue; + + for (i = 0; i < n; i++) + { + if (!gsk_sl_type_equal (gsk_sl_function_get_argument_type (f, i), + gsk_sl_function_get_argument_type (function, i))) + break; + } + if (i == n) + { + g_list_free (matcher->best_matches); + g_list_free (matcher->matches); + matcher->best_matches = g_list_prepend (NULL, f); + matcher->matches = NULL; + return; + } + } + + g_list_free (matcher->best_matches); + g_list_free (matcher->matches); + matcher->best_matches = NULL; + matcher->matches = NULL; +} diff --git a/gsk/gskslfunctionprivate.h b/gsk/gskslfunctionprivate.h index ce38b8ebe0..92d11f72ad 100644 --- a/gsk/gskslfunctionprivate.h +++ b/gsk/gskslfunctionprivate.h @@ -67,13 +67,30 @@ GskSlType * gsk_sl_function_get_return_type (const GskSlFunc gsize gsk_sl_function_get_n_arguments (const GskSlFunction *function); GskSlType * gsk_sl_function_get_argument_type (const GskSlFunction *function, gsize i); -gboolean gsk_sl_function_matches (const GskSlFunction *function, - GskSlType **arguments, - gsize n_arguments, - GError **error); guint32 gsk_sl_function_write_spv (const GskSlFunction *function, GskSpvWriter *writer); +struct _GskSlFunctionMatcher +{ + GList *best_matches; + GList *matches; +}; + +void gsk_sl_function_matcher_init (GskSlFunctionMatcher *matcher, + GList *list); +void gsk_sl_function_matcher_finish (GskSlFunctionMatcher *matcher); + +gboolean gsk_sl_function_matcher_has_matches (GskSlFunctionMatcher *matcher); +GskSlFunction * gsk_sl_function_matcher_get_match (GskSlFunctionMatcher *matcher); + +void gsk_sl_function_matcher_match_n_arguments (GskSlFunctionMatcher *matcher, + gsize n_arguments); +void gsk_sl_function_matcher_match_argument (GskSlFunctionMatcher *matcher, + gsize n, + const GskSlType *argument_type); +void gsk_sl_function_matcher_match_function (GskSlFunctionMatcher *matcher, + const GskSlFunction *function); + G_END_DECLS #endif /* __GSK_SL_FUNCTION_PRIVATE_H__ */ diff --git a/gsk/gskslnode.c b/gsk/gskslnode.c index 2f33f854fe..e8c9b2a307 100644 --- a/gsk/gskslnode.c +++ b/gsk/gskslnode.c @@ -402,7 +402,17 @@ its_a_type: constructor = gsk_sl_function_new_constructor (type); node_expression = gsk_sl_node_new (GskSlNodeExpression, &GSK_SL_NODE_EXPRESSION); - node_expression->expression = gsk_sl_expression_parse_function_call (scope, preproc, constructor); + if (gsk_sl_function_is_builtin_constructor (constructor)) + { + node_expression->expression = gsk_sl_expression_parse_function_call (scope, preproc, NULL, constructor); + } + else + { + GskSlFunctionMatcher matcher; + gsk_sl_function_matcher_init (&matcher, g_list_prepend (NULL, constructor)); + node_expression->expression = gsk_sl_expression_parse_function_call (scope, preproc, &matcher, constructor); + gsk_sl_function_matcher_finish (&matcher); + } node = (GskSlNode *) node_expression; gsk_sl_function_unref (constructor); } diff --git a/gsk/gskslprogram.c b/gsk/gskslprogram.c index 3d47dd6f14..d9a78c95f3 100644 --- a/gsk/gskslprogram.c +++ b/gsk/gskslprogram.c @@ -175,13 +175,20 @@ gsk_sl_program_parse_declaration (GskSlProgram *program, if (gsk_sl_token_is (token, GSK_SL_TOKEN_LEFT_PAREN)) { + GskSlFunctionMatcher matcher; GskSlFunction *function; function = gsk_sl_function_new_parse (scope, preproc, type, name); - gsk_sl_scope_add_function (scope, function); + gsk_sl_scope_match_function (scope, &matcher, gsk_sl_function_get_name (function)); + gsk_sl_function_matcher_match_function (&matcher, function); + if (gsk_sl_function_matcher_has_matches (&matcher)) + gsk_sl_preprocessor_error (preproc, DECLARATION, "A function with the same prototype has already been defined."); + else + gsk_sl_scope_add_function (scope, function); + gsk_sl_function_matcher_finish (&matcher); program->functions = g_slist_append (program->functions, function); } else diff --git a/gsk/gskslscope.c b/gsk/gskslscope.c index 03fc9a4bc0..2e1c17a7ee 100644 --- a/gsk/gskslscope.c +++ b/gsk/gskslscope.c @@ -20,8 +20,8 @@ #include "gskslscopeprivate.h" -#include "gsksltypeprivate.h" #include "gskslfunctionprivate.h" +#include "gsksltypeprivate.h" #include "gskslvariableprivate.h" #include @@ -39,6 +39,12 @@ struct _GskSlScope GHashTable *types; }; +static void +free_function_list (gpointer data) +{ + g_list_free_full (data, (GDestroyNotify) gsk_sl_function_unref); +} + GskSlScope * gsk_sl_scope_new (GskSlScope *parent, GskSlType *return_type) @@ -56,7 +62,7 @@ gsk_sl_scope_new (GskSlScope *parent, if (return_type) scope->return_type = gsk_sl_type_ref (return_type); scope->variables = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) gsk_sl_variable_unref); - scope->functions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) gsk_sl_function_unref); + scope->functions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) free_function_list); scope->types = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) gsk_sl_type_unref); return scope; @@ -140,25 +146,35 @@ void gsk_sl_scope_add_function (GskSlScope *scope, GskSlFunction *function) { - g_hash_table_replace (scope->functions, (gpointer) gsk_sl_function_get_name (function), gsk_sl_function_ref (function)); + GList *functions; + const char *name; + + name = gsk_sl_function_get_name (function); + + functions = g_hash_table_lookup (scope->functions, name); + gsk_sl_function_ref (function); + if (functions) + functions = g_list_append (functions, function); + else + g_hash_table_insert (scope->functions, (gpointer) name, g_list_prepend (NULL, function)); } -GskSlFunction * -gsk_sl_scope_lookup_function (GskSlScope *scope, - const char *name) +void +gsk_sl_scope_match_function (GskSlScope *scope, + GskSlFunctionMatcher *matcher, + const char *name) { - GskSlFunction *result; + GList *result = NULL, *lookup; for (; scope != NULL; scope = scope->parent) { - result = g_hash_table_lookup (scope->functions, name); - if (result) - return result; + lookup = g_hash_table_lookup (scope->functions, name); + result = g_list_concat (result, g_list_copy (lookup)); } - return NULL; + gsk_sl_function_matcher_init (matcher, result); } void diff --git a/gsk/gskslscopeprivate.h b/gsk/gskslscopeprivate.h index 50e6120c7e..6c6e865d1a 100644 --- a/gsk/gskslscopeprivate.h +++ b/gsk/gskslscopeprivate.h @@ -40,7 +40,8 @@ GskSlVariable * gsk_sl_scope_lookup_variable (GskSlScope const char *name); void gsk_sl_scope_add_function (GskSlScope *scope, GskSlFunction *function); -GskSlFunction * gsk_sl_scope_lookup_function (GskSlScope *scope, +void gsk_sl_scope_match_function (GskSlScope *scope, + GskSlFunctionMatcher *matcher, const char *name); void gsk_sl_scope_add_type (GskSlScope *scope, GskSlType *type); diff --git a/gsk/gsksltype.c b/gsk/gsksltype.c index be47436a06..c49f31cfb2 100644 --- a/gsk/gsksltype.c +++ b/gsk/gsksltype.c @@ -20,10 +20,10 @@ #include "gsksltypeprivate.h" -#include "gsksltokenizerprivate.h" -#include "gskslpreprocessorprivate.h" +#include "gskslfunctionprivate.h" #include "gskslpreprocessorprivate.h" #include "gskslscopeprivate.h" +#include "gsksltokenizerprivate.h" #include "gskslvalueprivate.h" #include "gskspvwriterprivate.h" @@ -1183,10 +1183,17 @@ out: { if (gsk_sl_scope_lookup_type (scope, gsk_sl_type_get_name (type))) gsk_sl_preprocessor_error (preproc, DECLARATION, "Redefinition of struct \"%s\".", gsk_sl_type_get_name (type)); - else if (gsk_sl_scope_lookup_function (scope, gsk_sl_type_get_name (type))) - gsk_sl_preprocessor_error (preproc, DECLARATION, "Constructor name \"%s\" would override function of same name.", gsk_sl_type_get_name (type)); else - gsk_sl_scope_add_type (scope, type); + { + GskSlFunctionMatcher matcher; + + gsk_sl_scope_match_function (scope, &matcher, gsk_sl_type_get_name (type)); + if (gsk_sl_function_matcher_has_matches (&matcher)) + gsk_sl_preprocessor_error (preproc, DECLARATION, "Constructor name \"%s\" would override function of same name.", gsk_sl_type_get_name (type)); + else + gsk_sl_scope_add_type (scope, type); + gsk_sl_function_matcher_finish (&matcher); + } } return type; } diff --git a/gsk/gsksltypesprivate.h b/gsk/gsksltypesprivate.h index cd15ae16e2..3a7125f720 100644 --- a/gsk/gsksltypesprivate.h +++ b/gsk/gsksltypesprivate.h @@ -23,6 +23,7 @@ typedef struct _GskSlExpression GskSlExpression; typedef struct _GskSlFunction GskSlFunction; +typedef struct _GskSlFunctionMatcher GskSlFunctionMatcher; typedef struct _GskSlNode GskSlNode; typedef struct _GskSlPreprocessor GskSlPreprocessor; typedef struct _GskSlPointerType GskSlPointerType;