/// <summary> /// Process the "while" statement at "intermediate_code[index]". /// </summary> void process_while_statement(ref int index) { int start_while = statements.Count; int end_while_label = LabelAddresses.allocate_label(); int start_index = index + 1; // "start_index" is pointing to the first "while" statement, which marks // the start of the while loop. The "while conditional:" line is further down. // Search for the "while conditional:" line. int conditional_index = start_index; while (conditional_index < intermediate_code.Length) { if (intermediate_code[conditional_index][0].type == TokenType.While) { break; } conditional_index++; } // Generate statement for the code from "start_index" to // "conditional_index"-1, if necessary if (start_index < conditional_index) { construct_statements(start_index, conditional_index - 1, -1 * end_while_label, start_while); } // Generate statement for the "while conditional:" line var conditional_line = intermediate_code[conditional_index]; int true_addr = statements.Count + 1; statements.Add(new ConditionalStatement(conditional_line, true_addr, -1 * end_while_label)); // Find the end of the "while" body int end_index = find_next_line_with_equal_or_less_indentation(conditional_index); // Check for empty "while" body block if (end_index - conditional_index <= 1) { throw new Exception("The while statement body is empty."); } // Generate statement for the "while" body construct_statements(conditional_index + 1, end_index - 1, break_addr: -1 * end_while_label, continue_addr: start_while); // Generate statement for the end of the "while" body int indentation = conditional_line.indentation + 4; int line_number = intermediate_code[end_index - 1].line_number; statements.Add(new JumpStatement(start_while, indentation, line_number)); index = end_index; LabelAddresses.set_label_address(end_while_label, statements.Count); }
/// <summary> /// Process the "for" statement at "intermediate_code[index]". /// </summary> void process_for_statement(ref int index) { var for_statement = intermediate_code[index]; // extract the iterator and iteration variable var iteration_var = for_statement[1]; var iterator = for_statement[3]; // add statement to reset the iterator statements.Add(new FunctionCallStatement(iterator.ToString(), "reset", null, for_statement.indentation, for_statement.line_number)); // mark the start of the for statement int start_for = statements.Count; // call iterator.has_next_element() statements.Add(new FunctionCallStatement(iterator.ToString(), "has_next_element", null, for_statement.indentation, for_statement.line_number)); // add conditional for the "for" decision int end_for_label = LabelAddresses.allocate_label(); var for_condition = new TokenList(for_statement.line_number, for_statement.indentation); for_condition.add_token(Token.Constants.If); for_condition.add_token(Token.create_system_var("$return")); for_condition.add_token(Token.Constants.Colon); int true_addr = statements.Count + 1; statements.Add(new ConditionalStatement(for_condition, true_addr, -1 * end_for_label)); // call iterator.next_element() statements.Add(new FunctionCallStatement(iterator.ToString(), "next_element", null, for_statement.indentation, for_statement.line_number)); // iteration_var = iterator var assign_iteration_var = new TokenList(for_statement.line_number, for_statement.indentation); assign_iteration_var.add_token(iteration_var); assign_iteration_var.add_token(Token.Constants.Assign); assign_iteration_var.add_token(Token.create_system_var("$return")); statements.Add(new AssignStatement(assign_iteration_var)); // identify the for loop body int end_index = find_next_line_with_equal_or_less_indentation(index); // Check for empty "for" body block if (end_index - index <= 1) { throw new Exception("The for statement body is empty."); } // generate code for "for" loop body construct_statements(index + 1, end_index - 1, break_addr: -1 * end_for_label, continue_addr: start_for); // generate Jump at the end of the "for" loop body int indentation = for_statement.indentation + 4; int line_number = intermediate_code[end_index - 1].line_number; statements.Add(new JumpStatement(start_for, indentation, line_number)); index = end_index; LabelAddresses.set_label_address(end_for_label, statements.Count); }
/// <summary> /// Process the "if" statement at "intermediate_code[index]". /// </summary> void process_if_statement(ref int index, int?break_addr, int?continue_addr) { int end_if_final_label = LabelAddresses.allocate_label(); bool final_if_block = false; while (final_if_block == false) { // "index" points to the current "if" line // Find the end of the current if block int end_index = find_next_line_with_equal_or_less_indentation(index); var current_if_line = intermediate_code[index]; TokenList next_block_line = null; if (end_index < intermediate_code.Length) { next_block_line = intermediate_code[end_index]; } // Determine whether "next_block_line" is a continuation of the "if" statement. if (next_block_line == null) { final_if_block = true; // end of the program } else if (next_block_line[0].type != TokenType.Elif && next_block_line[0].type != TokenType.Else) { final_if_block = true; // something other than "elif" and "else" } else if (next_block_line.indentation < current_if_line.indentation) { final_if_block = true; // end of if block due to indentation } // end_if_final --- label ID for the final "if" block's end address // end_if --- label ID for the current "if" block's end address int end_if_label = end_if_final_label; if (final_if_block == false) { end_if_label = LabelAddresses.allocate_label(); } // Generate statement for the current if line if (current_if_line[0].type == TokenType.If || current_if_line[0].type == TokenType.Elif) { int true_addr = statements.Count + 1; statements.Add(new ConditionalStatement(current_if_line, true_addr, -1 * end_if_label)); } else if (current_if_line[0].type == TokenType.Else) { // do nothing } // Check for empty "if" body block if (end_index - index <= 1) { if (current_if_line[0].type == TokenType.If) { throw new Exception("The if statement body is empty."); } else if (current_if_line[0].type == TokenType.Elif) { throw new Exception("The elif statement body is empty."); } else { throw new Exception("The else statement body is empty."); } } // Generate statements for the "if" block body construct_statements(index + 1, end_index - 1, break_addr, continue_addr); // Generate statement for the end of the "if" block if (final_if_block == false) { int indentation = intermediate_code[end_index - 1].indentation; int line_number = intermediate_code[end_index - 1].line_number; statements.Add(new JumpStatement(-1 * end_if_final_label, indentation, line_number)); } // Record the current address as the "end_if" address LabelAddresses.set_label_address(end_if_label, statements.Count); // Set "index" for the future index = end_index; } LabelAddresses.set_label_address(end_if_final_label, statements.Count); }
/// <summary> /// Process the "for" statement at "intermediate_code[index]". /// </summary> void process_def_statement(ref int index) { var def_statement = intermediate_code[index]; // A jump statement that goes to the end of the function int end_func_label = LabelAddresses.allocate_label(); statements.Add(new JumpStatement(-1 * end_func_label, def_statement.indentation, def_statement.line_number)); // Syntax and assumption check if (def_statement[1].type != TokenType.Identifier) { throw new Exception("Function definition syntax error. \"" + def_statement[1].ToString() + "\" not an identifier."); } if (def_statement[2].type != TokenType.Left_Paren) { throw new Exception("Function definition syntax error. \"" + def_statement[2].ToString() + "\" is expected to be '('."); } if (def_statement[-1].type != TokenType.Colon) { throw new Exception("Function definition syntax error. \"" + "The function definition does not end with ':'."); } if (def_statement[-2].type != TokenType.Right_Paren) { throw new Exception("Function definition syntax error. \"" + "The second last character is expected to be ')'."); } // Process function name string function_name = (string)def_statement[1].value; if (user_def_function_locations.ContainsKey(function_name)) { throw new Exception("The function name \"" + function_name + "\" is already in use."); } user_def_function_locations.Add(function_name, statements.Count); // Create a function argument assignment statement for each argument int arg_index = 3; // def f ( arg0 -- start at arg0, token #3 int position_number = 0; while (arg_index < def_statement.Count - 2) { // check that arg_index is an identifier token if (def_statement[arg_index].type != TokenType.Identifier) { throw new Exception("Syntax error in function arguments. Expecting \"" + def_statement[arg_index].ToString() + "\" to be a variable name."); } // simple argument case - without default argument, it's either [arg0 ,] or [arg0 )] if (def_statement[arg_index + 1].type == TokenType.Comma || def_statement[arg_index + 1].type == TokenType.Right_Paren) { statements.Add(new FunctionArgAssignStatement(position_number, def_statement, arg_index, arg_index)); arg_index += 2; position_number++; } // simple argument plus type hint: [arg0 : str] else if (def_statement[arg_index + 1].type == TokenType.Colon && def_statement[arg_index + 2].type == TokenType.Identifier && (def_statement[arg_index + 3].type == TokenType.Comma || def_statement[arg_index + 3].type == TokenType.Right_Paren)) { statements.Add(new FunctionArgAssignStatement(position_number, def_statement, arg_index, arg_index)); arg_index += 4; position_number++; } // default argument case else if (def_statement[arg_index + 1].type == TokenType.Assign) { // The end of the default argument could be a ',' or a ')'. // In the case of ')', need to track '(' and ')' balance - since // "x=(1+2)*3" is allowed in the default argument. int arg_end_index = arg_index + 2; int paren_balance = 0; // +1 for every '(' seen while (arg_end_index < def_statement.Count - 1) { // t = current token var t = def_statement[arg_end_index]; if (t.type == TokenType.Comma) { break; } else if (t.type == TokenType.Left_Paren) { paren_balance++; } else if (t.type == TokenType.Right_Paren) { paren_balance--; if (paren_balance < 0) { break; } } arg_end_index++; } // Look for errors with "arg_end_index" // Check for parenthesis imbalance if (def_statement[arg_end_index].type == TokenType.Comma && paren_balance != 0) { throw new Exception("The default argument has parenthesis imbalance."); } if (def_statement[arg_end_index].type == TokenType.Right_Paren && paren_balance != -1) { throw new Exception("The default argument has parenthesis imbalance."); } // Check empty default statement if (arg_end_index == arg_index + 2) { throw new Exception("The default argument statement is empty."); } // Add argument assignment statement statements.Add(new FunctionArgAssignStatement(position_number, def_statement, arg_index, arg_end_index - 1)); arg_index = arg_end_index + 1; position_number++; } else { throw new Exception("Syntax error in function arguments."); } } // Look for the end of the function block int end_index = find_next_line_with_equal_or_less_indentation(index); // Check for empty "def" body block if (end_index - index <= 1) { throw new Exception("The " + def_statement.ToString().Trim() + " statement body is empty."); } // Generate statements for the function body construct_statements(index + 1, end_index - 1, null, null); // If there is no return statement, add one var last_func_statement = statements[statements.Count - 1]; if (last_func_statement.Type != StatementType.Return) { statements.Add(new ReturnStatement(last_func_statement.Indentation, last_func_statement.LineNumber)); } index = end_index; LabelAddresses.set_label_address(end_func_label, statements.Count); }