summaryrefslogtreecommitdiff
path: root/switch.c
diff options
context:
space:
mode:
authorSøren Sandmann <sandmann@redhat.com>2007-09-06 02:43:36 -0400
committerSøren Sandmann <sandmann@redhat.com>2007-09-06 02:43:36 -0400
commita78f3c2495a5703dbf2b1021f7c3d55017f12e81 (patch)
tree94b1ed0140a810fe05d6c13fa3d96224198d107a /switch.c
parent559dd1585a01b74dfdd41dfb2c35eaf52b88a8aa (diff)
Add switch.c ; Checking constant expressions
Diffstat (limited to 'switch.c')
-rw-r--r--switch.c167
1 files changed, 167 insertions, 0 deletions
diff --git a/switch.c b/switch.c
new file mode 100644
index 0000000..0bb4638
--- /dev/null
+++ b/switch.c
@@ -0,0 +1,167 @@
+#include "ast.h"
+#include <stdlib.h>
+
+/* Check switch statements */
+
+static int
+compare_cases (const void *a,
+ const void *b)
+{
+ const ast_case_t *case_a = *(ast_case_t **)a;
+ const ast_case_t *case_b = *(ast_case_t **)b;
+
+ if (case_a->common.type == AST_DEFAULT_CASE &&
+ case_b->common.type == AST_DEFAULT_CASE)
+ {
+ return 0;
+ }
+
+ if (case_a->common.type == AST_DEFAULT_CASE)
+ return 1;
+
+ if (case_b->common.type == AST_DEFAULT_CASE)
+ return -1;
+
+ g_assert (case_a->common.type == AST_EXPRESSION_CASE);
+ g_assert (case_b->common.type == AST_EXPRESSION_CASE);
+
+ return case_a->int_.value - case_b->int_.value;
+}
+
+static gboolean
+constant_expression (ast_t *ast)
+{
+ GList *list;
+
+ if (ast->common.type != AST_EXPRESSION)
+ return FALSE;
+
+ switch (ast->expression.common.type)
+ {
+ case AST_INT_LITERAL_EXPRESSION:
+ case AST_BOOL_LITERAL_EXPRESSION:
+ case AST_NULL_EXPRESSION:
+ return TRUE;
+
+ case AST_IDENTIFIER_EXPRESSION:
+ /* FIXME: should check if it's a constant */
+ return FALSE;
+
+ case AST_LAMBDA_EXPRESSION:
+ case AST_CALL_EXPRESSION:
+ case AST_STATEMENT_EXPRESSION:
+ case AST_NEW_EXPRESSION:
+ case AST_DOT_EXPRESSION:
+ case AST_THIS_EXPRESSION:
+ case AST_VOID_EXPRESSION:
+ return FALSE;
+
+ case AST_UNARY_EXPRESSION:
+ case AST_BINARY_EXPRESSION:
+ case AST_TERNARY_EXPRESSION:
+ for (list = ast->common.children->head; list; list = list->next)
+ {
+ if (!constant_expression (list->data))
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+check_constant_case_expressions (ast_t *ast)
+{
+ GList *list;
+
+ for (list = ast->common.children->head; list; list = list->next)
+ {
+ if (!check_constant_case_expressions (list->data))
+ return FALSE;
+ }
+
+ if (ast_is (ast, AST_CASE, AST_EXPRESSION_CASE))
+ {
+ /* FIXME: check that we are dealing with a constant expression,
+ * and store the value of that expression in the case clause
+ */
+#if 0
+ if (!constant_expression (ast->case_.expression, &value))
+ {
+ g_print ("Expression in case clause must be constant\n");
+
+ return FALSE;
+ }
+#endif
+
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_unique_cases (ast_t *ast)
+{
+ GList *list;
+
+ for (list = ast->common.children->head; list; list = list->next)
+ {
+ if (!check_unique_cases (list->data))
+ return FALSE;
+ }
+
+ if (ast_is (ast, AST_STATEMENT, AST_SWITCH_STATEMENT))
+ {
+ int n_cases = array_length ((gpointer *)ast->statement.switch_.cases);
+ ast_case_t **cases = ast->statement.switch_.cases;
+ int i;
+
+ if (n_cases <= 1)
+ {
+ /* Only one case; no need to check uniqueness */
+ return TRUE;
+ }
+
+ /* Sort cases according to value, with default(s) last */
+ qsort (cases, n_cases, sizeof (ast_case_t *), compare_cases);
+
+ for (i = 0; cases[i + 1] != NULL; ++i)
+ {
+ ast_case_t *case_a = cases[i];
+ ast_case_t *case_b = cases[i + 1];
+
+ if (case_a->common.type == AST_DEFAULT_CASE &&
+ case_b->common.type == AST_DEFAULT_CASE)
+ {
+ g_print ("More than default case in switch statement\n");
+ return FALSE;
+ }
+
+ if (case_a->common.type == AST_EXPRESSION_CASE &&
+ case_b->common.type == AST_EXPRESSION_CASE &&
+ case_a->int_.value == case_b->int_.value)
+ {
+ g_print ("More than one case with value %d\n",
+ case_a->int_.value);
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+gboolean
+switch_check (ast_program_t *program)
+{
+ if (!check_constant_case_expressions ((ast_t *)program))
+ return FALSE;
+
+ if (!check_unique_cases ((ast_t *)program))
+ return FALSE;
+
+ return TRUE;
+}