diff options
author | Benjamin Otte <otte@redhat.com> | 2017-10-17 20:37:28 +0200 |
---|---|---|
committer | Benjamin Otte <otte@redhat.com> | 2017-10-30 02:58:03 +0100 |
commit | 031a1812b8f96d716f35ff27c71caf9a0202d711 (patch) | |
tree | 1b6ee1b2d5979b85e6ff61d0cf467a548d682d44 | |
parent | d59333da9ecb6ca5a1e7c065fd59054dd32ec864 (diff) | |
download | gtk+-031a1812b8f96d716f35ff27c71caf9a0202d711.tar.gz |
gskslstatement: Implement for loops
-rw-r--r-- | gsk/gskslscope.c | 29 | ||||
-rw-r--r-- | gsk/gskslscopeprivate.h | 6 | ||||
-rw-r--r-- | gsk/gskslstatement.c | 239 | ||||
-rw-r--r-- | gsk/gskslstatementprivate.h | 3 |
4 files changed, 271 insertions, 6 deletions
diff --git a/gsk/gskslscope.c b/gsk/gskslscope.c index 2e1c17a7ee..07fddf7b9d 100644 --- a/gsk/gskslscope.c +++ b/gsk/gskslscope.c @@ -37,6 +37,9 @@ struct _GskSlScope GHashTable *variables; GHashTable *functions; GHashTable *types; + + guint can_break :1; + guint can_continue :1; }; static void @@ -49,6 +52,18 @@ GskSlScope * gsk_sl_scope_new (GskSlScope *parent, GskSlType *return_type) { + if (parent) + return gsk_sl_scope_new_full (parent, return_type, parent->can_break, parent->can_continue); + else + return gsk_sl_scope_new_full (parent, return_type, FALSE, FALSE); +} + +GskSlScope * +gsk_sl_scope_new_full (GskSlScope *parent, + GskSlType *return_type, + gboolean can_break, + gboolean can_continue) +{ GskSlScope *scope; scope = g_slice_new0 (GskSlScope); @@ -61,6 +76,8 @@ gsk_sl_scope_new (GskSlScope *parent, } if (return_type) scope->return_type = gsk_sl_type_ref (return_type); + scope->can_break = can_break; + scope->can_continue = can_continue; 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) free_function_list); scope->types = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) gsk_sl_type_unref); @@ -112,6 +129,18 @@ gsk_sl_scope_get_return_type (const GskSlScope *scope) } gboolean +gsk_sl_scope_can_break (const GskSlScope *scope) +{ + return scope->can_break; +} + +gboolean +gsk_sl_scope_can_continue (const GskSlScope *scope) +{ + return scope->can_continue; +} + +gboolean gsk_sl_scope_is_global (const GskSlScope *scope) { return scope->parent == NULL; diff --git a/gsk/gskslscopeprivate.h b/gsk/gskslscopeprivate.h index 6c6e865d1a..cb4bac81df 100644 --- a/gsk/gskslscopeprivate.h +++ b/gsk/gskslscopeprivate.h @@ -27,11 +27,17 @@ G_BEGIN_DECLS GskSlScope * gsk_sl_scope_new (GskSlScope *parent, GskSlType *return_type); +GskSlScope * gsk_sl_scope_new_full (GskSlScope *parent, + GskSlType *return_type, + gboolean can_break, + gboolean can_continue); GskSlScope * gsk_sl_scope_ref (GskSlScope *scope); void gsk_sl_scope_unref (GskSlScope *scope); GskSlType * gsk_sl_scope_get_return_type (const GskSlScope *scope); +gboolean gsk_sl_scope_can_break (const GskSlScope *scope); +gboolean gsk_sl_scope_can_continue (const GskSlScope *scope); gboolean gsk_sl_scope_is_global (const GskSlScope *scope); void gsk_sl_scope_add_variable (GskSlScope *scope, diff --git a/gsk/gskslstatement.c b/gsk/gskslstatement.c index 56fc2ac988..4fdb31788b 100644 --- a/gsk/gskslstatement.c +++ b/gsk/gskslstatement.c @@ -443,6 +443,125 @@ static const GskSlStatementClass GSK_SL_STATEMENT_IF = { gsk_sl_statement_if_write_spv }; +/* FOR */ + +typedef struct _GskSlStatementFor GskSlStatementFor; + +struct _GskSlStatementFor { + GskSlStatement parent; + + GskSlScope *scope; + + GskSlStatement *init; + GskSlExpression *condition; + GskSlExpression *loop; + GskSlStatement *body; +}; + +static void +gsk_sl_statement_for_free (GskSlStatement *statement) +{ + GskSlStatementFor *for_stmt = (GskSlStatementFor *) statement; + + gsk_sl_scope_unref (for_stmt->scope); + + gsk_sl_statement_unref (for_stmt->init); + if (for_stmt->condition) + gsk_sl_expression_unref (for_stmt->condition); + if (for_stmt->loop) + gsk_sl_expression_unref (for_stmt->loop); + gsk_sl_statement_unref (for_stmt->body); + + g_slice_free (GskSlStatementFor, for_stmt); +} + +static void +gsk_sl_statement_for_print (const GskSlStatement *statement, + GskSlPrinter *printer) +{ + GskSlStatementFor *for_stmt = (GskSlStatementFor *) statement; + + gsk_sl_printer_append (printer, "for ("); + gsk_sl_statement_print (for_stmt->init, printer); + if (for_stmt->condition) + gsk_sl_expression_print (for_stmt->condition, printer); + gsk_sl_printer_append (printer, "; "); + if (for_stmt->loop) + gsk_sl_expression_print (for_stmt->loop, printer); + gsk_sl_printer_append (printer, ")"); + gsk_sl_printer_push_indentation (printer); + gsk_sl_printer_newline (printer); + gsk_sl_statement_print (for_stmt->body, printer); + gsk_sl_printer_pop_indentation (printer); +} + +static GskSlJump +gsk_sl_statement_for_get_jump (const GskSlStatement *statement) +{ + /* If the condition is FALSE before entering the body, it + * doesn't matter what the body does */ + return GSK_SL_JUMP_NONE; +} + +static gboolean +gsk_sl_statement_for_write_spv (const GskSlStatement *statement, + GskSpvWriter *writer) +{ + GskSlStatementFor *for_stmt = (GskSlStatementFor *) statement; + guint32 loop_id, continue_id, after_id, condition_id, body_id; + guint32 old_break_id, old_continue_id; + + if (gsk_sl_statement_write_spv (for_stmt->init, writer)) + g_assert_not_reached (); + + loop_id = gsk_spv_writer_make_id (writer); + body_id = gsk_spv_writer_make_id (writer); + after_id = gsk_spv_writer_make_id (writer); + continue_id = gsk_spv_writer_make_id (writer); + if (for_stmt->condition) + condition_id = gsk_spv_writer_make_id (writer); + old_break_id = gsk_spv_writer_get_break_id (writer); + old_continue_id = gsk_spv_writer_get_continue_id (writer); + + gsk_spv_writer_branch (writer, loop_id); + + gsk_spv_writer_label (writer, GSK_SPV_WRITER_SECTION_CODE, loop_id); + gsk_spv_writer_loop_merge (writer, after_id, continue_id, 0); + + if (for_stmt->condition) + { + guint32 test_id; + + gsk_spv_writer_branch (writer, condition_id); + gsk_spv_writer_start_code_block (writer, condition_id, continue_id, after_id); + gsk_spv_writer_label (writer, GSK_SPV_WRITER_SECTION_CODE, condition_id); + test_id = gsk_sl_expression_write_spv (for_stmt->condition, writer); + gsk_spv_writer_branch_conditional (writer, test_id, body_id, after_id, NULL, 0); + } + + gsk_spv_writer_start_code_block (writer, body_id, continue_id, after_id); + gsk_spv_writer_label (writer, GSK_SPV_WRITER_SECTION_CODE, body_id); + if (!gsk_sl_statement_write_spv (for_stmt->body, writer)) + gsk_spv_writer_branch (writer, continue_id); + + gsk_spv_writer_label (writer, GSK_SPV_WRITER_SECTION_CODE, continue_id); + if (for_stmt->loop) + gsk_sl_expression_write_spv (for_stmt->loop, writer); + gsk_spv_writer_branch (writer, loop_id); + + gsk_spv_writer_start_code_block (writer, after_id, old_continue_id, old_break_id); + gsk_spv_writer_label (writer, GSK_SPV_WRITER_SECTION_CODE, after_id); + + return FALSE; +} + +static const GskSlStatementClass GSK_SL_STATEMENT_FOR = { + gsk_sl_statement_for_free, + gsk_sl_statement_for_print, + gsk_sl_statement_for_get_jump, + gsk_sl_statement_for_write_spv +}; + /* EXPRESSION */ typedef struct _GskSlStatementExpression GskSlStatementExpression; @@ -500,6 +619,12 @@ static const GskSlStatementClass GSK_SL_STATEMENT_EXPRESSION = { /* API */ static GskSlStatement * +gsk_sl_statement_new_error (void) +{ + return (GskSlStatement *) gsk_sl_statement_new (GskSlStatementEmpty, &GSK_SL_STATEMENT_EMPTY); +} + +static GskSlStatement * gsk_sl_statement_parse_declaration (GskSlScope *scope, GskSlPreprocessor *stream, const GskSlQualifier *qualifier, @@ -607,19 +732,105 @@ gsk_sl_statement_parse_if (GskSlScope *scope, gsk_sl_preprocessor_consume (preproc, if_stmt); if_stmt->if_scope = gsk_sl_scope_new (scope, gsk_sl_scope_get_return_type (scope)); - if_stmt->if_part = gsk_sl_statement_parse (if_stmt->if_scope, preproc); + if_stmt->if_part = gsk_sl_statement_parse (if_stmt->if_scope, preproc, TRUE); token = gsk_sl_preprocessor_get (preproc); if (gsk_sl_token_is (token, GSK_SL_TOKEN_ELSE)) { gsk_sl_preprocessor_consume (preproc, if_stmt); if_stmt->else_scope = gsk_sl_scope_new (scope, gsk_sl_scope_get_return_type (scope)); - if_stmt->else_part = gsk_sl_statement_parse (if_stmt->else_scope, preproc); + if_stmt->else_part = gsk_sl_statement_parse (if_stmt->else_scope, preproc, TRUE); } return (GskSlStatement *) if_stmt; } +static GskSlExpression * +gsk_sl_statement_parse_condition_expression (GskSlScope *scope, + GskSlPreprocessor *preproc) +{ + GskSlExpression *expression; + GskSlValue *value; + + /* XXX: implement */ + expression = gsk_sl_expression_parse (scope, preproc); + + if (!gsk_sl_type_equal (gsk_sl_expression_get_return_type (expression), gsk_sl_type_get_scalar (GSK_SL_BOOL))) + { + gsk_sl_preprocessor_error (preproc, SYNTAX, + "Condition in for statment returns %s, not a bool", + gsk_sl_type_get_name (gsk_sl_expression_get_return_type (expression))); + } + value = gsk_sl_expression_get_constant (expression); + if (value) + { + gsk_sl_preprocessor_warn (preproc, CONSTANT, + "Condition is always %s", + *(guint32 *) gsk_sl_value_get_data (value) ? "true" : "false"); + gsk_sl_value_free (value); + } + + return expression; +} + +static GskSlStatement * +gsk_sl_statement_parse_for (GskSlScope *scope, + GskSlPreprocessor *preproc) +{ + GskSlStatementFor *for_stmt; + const GskSlToken *token; + + for_stmt = gsk_sl_statement_new (GskSlStatementFor, &GSK_SL_STATEMENT_FOR); + + for_stmt->scope = gsk_sl_scope_new_full (scope, + gsk_sl_scope_get_return_type (scope), + TRUE, TRUE); + + /* GSK_SL_TOKEN_FOR */ + gsk_sl_preprocessor_consume (preproc, for_stmt); + + token = gsk_sl_preprocessor_get (preproc); + if (!gsk_sl_token_is (token, GSK_SL_TOKEN_LEFT_PAREN)) + { + gsk_sl_preprocessor_error (preproc, SYNTAX, "Expected an opening \"(\""); + gsk_sl_statement_unref ((GskSlStatement *) for_stmt); + return gsk_sl_statement_new_error (); + } + gsk_sl_preprocessor_consume (preproc, for_stmt); + + for_stmt->init = gsk_sl_statement_parse (for_stmt->scope, preproc, FALSE); + token = gsk_sl_preprocessor_get (preproc); + if (gsk_sl_token_is (token, GSK_SL_TOKEN_SEMICOLON)) + { + gsk_sl_preprocessor_consume (preproc, for_stmt); + } + else + { + for_stmt->condition = gsk_sl_statement_parse_condition_expression (for_stmt->scope, preproc); + token = gsk_sl_preprocessor_get (preproc); + if (!gsk_sl_token_is (token, GSK_SL_TOKEN_SEMICOLON)) + gsk_sl_preprocessor_error (preproc, SYNTAX, "Expected \";\" after condition"); + else + gsk_sl_preprocessor_consume (preproc, for_stmt); + } + + token = gsk_sl_preprocessor_get (preproc); + if (!gsk_sl_token_is (token, GSK_SL_TOKEN_RIGHT_PAREN)) + { + for_stmt->loop = gsk_sl_expression_parse (for_stmt->scope, preproc); + token = gsk_sl_preprocessor_get (preproc); + } + + if (!gsk_sl_token_is (token, GSK_SL_TOKEN_RIGHT_PAREN)) + gsk_sl_preprocessor_error (preproc, SYNTAX, "Expected a closing \")\" at end of for statement"); + else + gsk_sl_preprocessor_consume (preproc, for_stmt); + + for_stmt->body = gsk_sl_statement_parse (for_stmt->scope, preproc, TRUE); + + return (GskSlStatement *) for_stmt; +} + GskSlStatement * gsk_sl_statement_parse_compound (GskSlScope *scope, GskSlPreprocessor *preproc, @@ -653,7 +864,7 @@ gsk_sl_statement_parse_compound (GskSlScope *scope, if (jump != GSK_SL_JUMP_NONE) gsk_sl_preprocessor_warn (preproc, DEAD_CODE, "Statement cannot be reached."); - statement = gsk_sl_statement_parse (scope, preproc); + statement = gsk_sl_statement_parse (scope, preproc, TRUE); compound->statements = g_slist_prepend (compound->statements, statement); jump = gsk_sl_statement_get_jump (statement); } @@ -671,7 +882,8 @@ gsk_sl_statement_parse_compound (GskSlScope *scope, GskSlStatement * gsk_sl_statement_parse (GskSlScope *scope, - GskSlPreprocessor *preproc) + GskSlPreprocessor *preproc, + gboolean parse_everything) { const GskSlToken *token; GskSlStatement *statement; @@ -686,14 +898,23 @@ gsk_sl_statement_parse (GskSlScope *scope, case GSK_SL_TOKEN_EOF: gsk_sl_preprocessor_error (preproc, SYNTAX, "Unexpected end of document"); - return (GskSlStatement *) gsk_sl_statement_new (GskSlStatementEmpty, &GSK_SL_STATEMENT_EMPTY); + return gsk_sl_statement_new_error (); case GSK_SL_TOKEN_LEFT_BRACE: + if (!parse_everything) + goto only_expression_and_declaration; return gsk_sl_statement_parse_compound (scope, preproc, TRUE); case GSK_SL_TOKEN_IF: + if (!parse_everything) + goto only_expression_and_declaration; return gsk_sl_statement_parse_if (scope, preproc); + case GSK_SL_TOKEN_FOR: + if (!parse_everything) + goto only_expression_and_declaration; + return gsk_sl_statement_parse_for (scope, preproc); + case GSK_SL_TOKEN_CONST: case GSK_SL_TOKEN_IN: case GSK_SL_TOKEN_OUT: @@ -800,6 +1021,9 @@ its_a_type: GskSlStatementReturn *return_statement; GskSlType *return_type; + if (!parse_everything) + goto only_expression_and_declaration; + return_statement = gsk_sl_statement_new (GskSlStatementReturn, &GSK_SL_STATEMENT_RETURN); gsk_sl_preprocessor_consume (preproc, (GskSlStatement *) return_statement); token = gsk_sl_preprocessor_get (preproc); @@ -864,6 +1088,11 @@ its_a_type: gsk_sl_preprocessor_consume (preproc, (GskSlStatement *) statement); return statement; + +only_expression_and_declaration: + gsk_sl_preprocessor_error (preproc, SYNTAX, "No semicolon at end of statement."); + gsk_sl_preprocessor_sync (preproc, GSK_SL_TOKEN_SEMICOLON); + return gsk_sl_statement_new_error (); } GskSlStatement * diff --git a/gsk/gskslstatementprivate.h b/gsk/gskslstatementprivate.h index c550a50366..8dc9995d7b 100644 --- a/gsk/gskslstatementprivate.h +++ b/gsk/gskslstatementprivate.h @@ -34,7 +34,8 @@ typedef enum { } GskSlJump; GskSlStatement * gsk_sl_statement_parse (GskSlScope *scope, - GskSlPreprocessor *preproc); + GskSlPreprocessor *preproc, + gboolean parse_everything); GskSlStatement * gsk_sl_statement_parse_compound (GskSlScope *scope, GskSlPreprocessor *preproc, gboolean new_scope); |