[gtk+/wip/otte/shader: 146/156] gskslstatement: Implement for loops



commit e4bc35ec046a75d9b2696b5a48ae5f76335e10ca
Author: Benjamin Otte <otte redhat com>
Date:   Tue Oct 17 20:37:28 2017 +0200

    gskslstatement: Implement for loops

 gsk/gskslscope.c            |   29 +++++
 gsk/gskslscopeprivate.h     |    6 +
 gsk/gskslstatement.c        |  239 ++++++++++++++++++++++++++++++++++++++++++-
 gsk/gskslstatementprivate.h |    3 +-
 4 files changed, 271 insertions(+), 6 deletions(-)
---
diff --git a/gsk/gskslscope.c b/gsk/gskslscope.c
index 1d06b01..c37dc7b 100644
--- a/gsk/gskslscope.c
+++ b/gsk/gskslscope.c
@@ -36,6 +36,9 @@ struct _GskSlScope
   GHashTable *variables;
   GHashTable *functions;
   GHashTable *types;
+
+  guint can_break :1;
+  guint can_continue :1;
 };
 
 static void
@@ -48,6 +51,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);
@@ -57,6 +72,8 @@ gsk_sl_scope_new (GskSlScope *parent,
     scope->parent = gsk_sl_scope_ref (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);
@@ -103,6 +120,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 6c6e865..cb4bac8 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 df6affa..d60e760 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 c550a50..8dc9995 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);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]