2
0
Эх сурвалжийг харах

Added ternary expr and switch stmt codegen

Vicious 4 жил өмнө
parent
commit
471355a274

+ 1 - 0
src/compiler/gravity_ast.h

@@ -89,6 +89,7 @@ typedef struct {
     gnode_t             base;               // CASE or DEFAULT
     gnode_t             base;               // CASE or DEFAULT
     gnode_t             *expr;              // expression in case of CASE
     gnode_t             *expr;              // expression in case of CASE
     gnode_t             *stmt;              // common statement
     gnode_t             *stmt;              // common statement
+    uint32_t            label_case;         // for switch to jump
 } gnode_label_stmt_t;
 } gnode_label_stmt_t;
 
 
 typedef struct {
 typedef struct {

+ 91 - 3
src/compiler/gravity_codegen.c

@@ -390,12 +390,13 @@ static void visit_compound_stmt (gvisitor_t *self, gnode_compound_stmt_t *node)
 
 
 static void visit_label_stmt (gvisitor_t *self, gnode_label_stmt_t *node) {
 static void visit_label_stmt (gvisitor_t *self, gnode_label_stmt_t *node) {
     DEBUG_CODEGEN("visit_label_stmt");
     DEBUG_CODEGEN("visit_label_stmt");
+    DECLARE_CODE();
 
 
     gtoken_t type = NODE_TOKEN_TYPE(node);
     gtoken_t type = NODE_TOKEN_TYPE(node);
     assert((type == TOK_KEY_DEFAULT) || (type == TOK_KEY_CASE));
     assert((type == TOK_KEY_DEFAULT) || (type == TOK_KEY_CASE));
 
 
-    if (type == TOK_KEY_DEFAULT) {visit(node->stmt);}
-    else if (type == TOK_KEY_CASE) {visit(node->expr); visit(node->stmt);}
+    ircode_marklabel(code, node->label_case, LINE_NUMBER(node));
+    visit(node->stmt);
 }
 }
 
 
 static void visit_flow_if_stmt (gvisitor_t *self, gnode_flow_stmt_t *node) {
 static void visit_flow_if_stmt (gvisitor_t *self, gnode_flow_stmt_t *node) {
@@ -442,10 +443,97 @@ static void visit_flow_if_stmt (gvisitor_t *self, gnode_flow_stmt_t *node) {
 
 
 static void visit_flow_switch_stmt (gvisitor_t *self, gnode_flow_stmt_t *node) {
 static void visit_flow_switch_stmt (gvisitor_t *self, gnode_flow_stmt_t *node) {
     DEBUG_CODEGEN("visit_flow_switch_stmt");
     DEBUG_CODEGEN("visit_flow_switch_stmt");
+    DECLARE_CODE();
+
+    /*
+         <condition>
+         <case jumps>
+         if-equal case_expr1: goto $case1
+         if-equal case_expr2: goto $case2
+         ...
+         if-equal case_exprN: goto $caseN
+         if-exist default:    goto $default
+         goto $end
+         <cases>
+         $case1:
+         $case2:
+         ...
+         $caseN:
+         [$default:]
+         $end:
+     */
+
+    uint32_t cond_reg, reg;
+    uint32_t label_final = ircode_newlabel(code);
+
+    ircode_setlabel_false(code, label_final);
+
+    visit(node->cond);
+    cond_reg = ircode_register_last(code);
+    if (cond_reg == REGISTER_ERROR) report_error(self, (gnode_t *)node, "Invalid switch condition expression.");
+    if (!NODE_ISA(node->stmt, NODE_COMPOUND_STAT)) {
+        report_error(self, (gnode_t *)node, "Invalid switch stmt expression.");
+    }
+    gnode_compound_stmt_t *cases = (gnode_compound_stmt_t *)node->stmt;
+    gnode_label_stmt_t *default_stmt = NULL;
+    gnode_array_each(cases->stmts, {
+        if (NODE_ISA(val, NODE_LABEL_STAT)) {
+            gtoken_t type = val->token.type;
+            if (TOK_KEY_CASE == type) {
+                gnode_label_stmt_t *case_stmt = (gnode_label_stmt_t *)val;
+                visit(case_stmt->expr);
+                reg = ircode_register_pop(code);
+                if (reg == REGISTER_ERROR) report_error(self, (gnode_t *)node, "Invalid switch case expression.");
+
+                case_stmt->label_case = ircode_newlabel(code);
+                ircode_add(code, NEQ, reg, cond_reg, reg, LINE_NUMBER(case_stmt));
+                ircode_add(code, JUMPF, reg, case_stmt->label_case, 0, LINE_NUMBER(case_stmt));
+            } else if (TOK_KEY_DEFAULT == type) {
+                default_stmt = (gnode_label_stmt_t *)val;
+                default_stmt->label_case = ircode_newlabel(code);
+            }
+        }
+    });
+    if (NULL != default_stmt) {
+        ircode_add(code, JUMP, default_stmt->label_case, 0, 0, LINE_NUMBER(default_stmt));
+    } else {
+        ircode_add(code, JUMP, label_final, 0, 0, LINE_NUMBER(node));
+    }
+    // pop cond_reg
+    ircode_register_pop(code);
+
+    visit(node->stmt);
+    ircode_marklabel(code, label_final, LINE_NUMBER(node));
+    ircode_unsetlabel_false(code);
+
     return;
     return;
+}
+
+static void visit_flow_ternary_stmt (gvisitor_t *self, gnode_flow_stmt_t *node) {
+    DEBUG_CODEGEN("visit_flow_ternary_stmt");
+    DECLARE_CODE();
+
+    uint32_t reg;
+    uint32_t label_false = ircode_newlabel(code);
+    uint32_t label_final = ircode_newlabel(code);
 
 
     visit(node->cond);
     visit(node->cond);
+    reg = ircode_register_pop(code);
+    if (reg == REGISTER_ERROR) report_error(self, (gnode_t *)node, "Invalid ternary condition expression.");
+    ircode_add(code, JUMPF, reg, label_false, 0, LINE_NUMBER(node));
+
     visit(node->stmt);
     visit(node->stmt);
+    reg = ircode_register_pop(code);
+    if (reg == REGISTER_ERROR) report_error(self, (gnode_t *)node, "Invalid ternary left stmt expression.");
+    ircode_add(code, JUMP, label_final, 0, 0, LINE_NUMBER(node));
+
+    ircode_marklabel(code, label_false, LINE_NUMBER(node));
+    visit(node->elsestmt);
+    reg = ircode_register_last(code);
+    if (reg == REGISTER_ERROR) report_error(self, (gnode_t *)node, "Invalid ternary right stmt expression.");
+    ircode_marklabel(code, label_final, LINE_NUMBER(node));
+
+    return;
 }
 }
 
 
 static void visit_flow_stmt (gvisitor_t *self, gnode_flow_stmt_t *node) {
 static void visit_flow_stmt (gvisitor_t *self, gnode_flow_stmt_t *node) {
@@ -459,7 +547,7 @@ static void visit_flow_stmt (gvisitor_t *self, gnode_flow_stmt_t *node) {
     } else if (type == TOK_KEY_SWITCH) {
     } else if (type == TOK_KEY_SWITCH) {
         visit_flow_switch_stmt(self, node);
         visit_flow_switch_stmt(self, node);
     } else if (type == TOK_OP_TERNARY) {
     } else if (type == TOK_OP_TERNARY) {
-        report_error(self, (gnode_t *)node, "Ternary operator not yet supported.");
+        visit_flow_ternary_stmt(self, node);
     }
     }
 }
 }
 
 

+ 7 - 1
src/compiler/gravity_semacheck2.c

@@ -294,7 +294,7 @@ static gnode_t *lookup_symtable_id (gvisitor_t *self, gnode_identifier_expr_t *i
 
 
 static bool is_expression (gnode_t *node) {
 static bool is_expression (gnode_t *node) {
     gnode_n tag = NODE_TAG(node);
     gnode_n tag = NODE_TAG(node);
-    return ((tag >= NODE_BINARY_EXPR) && (tag <= NODE_ACCESS_EXPR));
+    return ((tag >= NODE_BINARY_EXPR) && (tag <= NODE_ACCESS_EXPR)) || (tag == NODE_FLOW_STAT);
 }
 }
 
 
 static bool is_expression_assignment (gnode_t *node) {
 static bool is_expression_assignment (gnode_t *node) {
@@ -365,6 +365,12 @@ static bool is_expression_valid (gnode_t *node) {
             return is_expression_valid(expr->right);
             return is_expression_valid(expr->right);
         }
         }
 
 
+        case NODE_FLOW_STAT: {
+            gnode_flow_stmt_t *flow_stmt = (gnode_flow_stmt_t *)node;
+            if (TOK_OP_TERNARY != NODE_TOKEN_TYPE(flow_stmt)) return false;
+            return is_expression_valid(flow_stmt->cond) && is_expression_valid(flow_stmt->stmt) && is_expression_valid(flow_stmt->elsestmt);
+        }
+
         case NODE_IDENTIFIER_EXPR: {
         case NODE_IDENTIFIER_EXPR: {
             return true;
             return true;
         }
         }

+ 40 - 0
test/unittest/flow_switch.gravity

@@ -0,0 +1,40 @@
+#unittest {
+    name: "Switch flow control.";
+    error: NONE;
+    result: "^1244??STR2";
+}
+
+func test(x) {
+    var value = "^";
+    switch (x) {
+        case 0:
+            break;
+        case 1:
+            return "1";
+        case 2:
+            value = "2";
+            break;
+        case 3:
+            value = "3";
+        case 4:
+            value = "4";
+            break;
+        case "str2":
+            value = "STR2"
+            break;
+        default:
+            return "?";
+    }
+    return value;
+}
+
+func main() {
+    var result = "";
+    for (var i in 0...5) {
+        result += test(i);
+    }
+    result += test("str1");
+    result += test("str2");
+    return result;
+}
+

+ 17 - 0
test/unittest/ternary_operator.gravity

@@ -0,0 +1,17 @@
+#unittest {
+    name: "Ternary operator.";
+    error: NONE;
+    result: "le1le1eq2eq3ge4ge4";
+}
+
+func test(x) {
+    return x <= 2 ? x == 2 ? "eq2" : "le1" : x >= 4 ? "ge4" : "eq3";
+}
+func main() {
+    var result = "";
+    for(var i in 0...5) {
+        result += test(i);
+    }
+    return result;
+}
+