void IOBlock::preprocess() {
	// get rid of the beginning and end parens
	this->get_unprocessed()->erase(this->get_unprocessed()->begin());
	this->get_unprocessed()->erase(this->get_unprocessed()->end() - 1);
	if (this->action == IO_WRITE) {
		SymbolListPtr pre = SymbolListPtr(new SymbolList());
		for (auto i = this->get_unprocessed()->begin();
			 i != this->get_unprocessed()->end(); i++) {
			TokenPtr t = *i;
			if (t->get_token() != MP_COMMA) {
				SymbolPtr p = translate(t);
				pre->push_back(p);
			} else {
				// reset
				this->expressions->push_back(pre);
				pre = SymbolListPtr(new SymbolList());
			}
		}
		this->expressions->push_back(pre);
	} else if (this->action == IO_READ) {
		for (auto i = this->get_unprocessed()->begin();
			 i != this->get_unprocessed()->end(); i++) {
			TokenPtr t = *i;
			if (t->get_token() != MP_COMMA) {
				SymbolPtr p = translate(t);
				this->get_symbol_list()->push_back(p);
			}
		}
	}
}
void LoopBlock::generate_pre() {
	if (this->type == RPTUNTLLOOP) {
		write_raw(this->body_label + ":\n");
	} else if (this->type == WHILELOOP) {
		write_raw(this->cond_label + ":\n");
		VarType result = this->generate_expr(this->get_symbol_list());
		if (result != BOOLEAN) {
			report_error_lc("Semantic Error",
							"Conditional expression doesn't evaluate to boolean value.",
							(*this->get_symbol_list()->begin())->get_row(),
							(*this->get_symbol_list()->begin())->get_col());
		}
		write_raw("\nBRTS " + this->body_label);
		write_raw("BR " + this->exit_label);
		write_raw(this->body_label + ":\n");
	} else if (this->type == FORLOOP) {
		// parse the assignment
		// process the assignment
		AssignmentBlockPtr assignment = AssignmentBlockPtr(new AssignmentBlock(false));
		assignment->set_analyzer(this->get_analyzer());
		for (auto i = 0; i < 3; i++) {
			assignment->catch_token((*this->get_unprocessed())[i]);
		}
		// generate its code
		assignment->preprocess();
		assignment->generate_pre();
		assignment->generate_post();
		// generate the condition label
		write_raw(this->cond_label + ":\n");
		// process the ordinal expression
		AssignmentBlockPtr ordinal_expr = AssignmentBlockPtr(new AssignmentBlock(true));
		ordinal_expr->set_analyzer(this->get_analyzer());
		for (unsigned int i = 4; i < this->get_unprocessed()->size(); i++) {
			ordinal_expr->catch_token((*this->get_unprocessed())[i]);
		}
		// get the comparison components for the ordinal expr
		TokenPtr incrementer = (*this->get_unprocessed())[3];
		if (incrementer->get_token() == MP_TO) {
			ordinal_expr->catch_token(TokenPtr(new Token(MP_EQUALS, "=", -1, -1)));
		} else if (incrementer->get_token() == MP_DOWNTO) {
			ordinal_expr->catch_token(TokenPtr(new Token(MP_EQUALS, "=", -1, -1)));
		}
		ordinal_expr->catch_token((*this->get_unprocessed())[0]);
		if (incrementer->get_token() == MP_TO) {
			ordinal_expr->catch_token(TokenPtr(new Token (MP_MINUS, "-", -1, -1)));
			ordinal_expr->catch_token(TokenPtr(new Token (MP_INT_LITERAL, "1", -1, -1)));
		} else if (incrementer->get_token() == MP_DOWNTO) {
			ordinal_expr->catch_token(TokenPtr(new Token (MP_PLUS, "+", -1, -1)));
			ordinal_expr->catch_token(TokenPtr(new Token (MP_INT_LITERAL, "1", -1, -1)));
		}
		// generate its code
		ordinal_expr->preprocess();
		ordinal_expr->generate_pre();
		write_raw("\nBRFS " + this->body_label);
		write_raw("BR " + this->exit_label + "\n");
		write_raw(this->body_label + ":\n");
	}
}
void IOBlock::catch_token(TokenPtr token) {
	// no filtering, since the capture is for
	// comma separated expressions
	if (token->get_token() != MP_WRITE
		&& token->get_token() != MP_WRITELN
		&& token->get_token() != MP_READ
		&& token->get_token() != MP_READLN)
	this->get_unprocessed()->push_back(token);
}
// Conditionals
void ConditionalBlock::catch_token(TokenPtr symbol) {
	if (symbol->get_token() != MP_IF
		&& symbol->get_token() != MP_THEN
		&& symbol->get_token() != MP_ELSE
		&& symbol->get_token() != MP_BEGIN
		&& symbol->get_token() != MP_END
		&& symbol->get_token() != MP_SEMI_COLON) {
		this->get_unprocessed()->push_back(symbol);
	}
}
void LoopBlock::catch_token(TokenPtr symbol) {
	if (symbol->get_token() != MP_WHILE
		&& symbol->get_token() != MP_REPEAT
		&& symbol->get_token() != MP_UNTIL
		&& symbol->get_token() != MP_DO
		&& symbol->get_token() != MP_FOR
		&& symbol->get_token() != MP_BEGIN
		&& symbol->get_token() != MP_END
		&& symbol->get_token() != MP_SEMI_COLON) {
		this->get_unprocessed()->push_back(symbol);
	}
}
void LoopBlock::generate_post() {
	if (this->type == RPTUNTLLOOP) {
		write_raw(this->cond_label + ":\n");
		VarType result = this->generate_expr(this->get_symbol_list());
		if (result != BOOLEAN) {
			report_error_lc("Semantic Error",
							"Conditional expression doesn't evaluate to boolean value.",
							(*this->get_symbol_list()->begin())->get_row(),
							(*this->get_symbol_list()->begin())->get_col());
		}
		write_raw("\nBRFS " + this->body_label);
		write_raw("BR " + this->exit_label + "\n");
		write_raw(this->exit_label + ":\n");
	} else if (this->type == WHILELOOP) {
		write_raw("BR " + this->cond_label);
		write_raw(this->exit_label + ":\n");
	} else if (this->type == FORLOOP) {
		// get the incrementer token
		TokenPtr incrementer = (*this->get_unprocessed())[3];
		if (incrementer->get_token() == MP_TO) {
			// generate an incrementer
			AssignmentBlockPtr inc = AssignmentBlockPtr(new AssignmentBlock(false));
			inc->set_analyzer(this->get_analyzer());
			inc->catch_token((*this->get_unprocessed())[0]);
			inc->catch_token((*this->get_unprocessed())[0]);
			inc->catch_token(TokenPtr(new Token(MP_PLUS, "+", -1, -1)));
			inc->catch_token(TokenPtr(new Token(MP_INT_LITERAL, "1", -1, -1)));
			inc->preprocess();
			inc->generate_pre();
			inc->generate_post();
		} else if (incrementer->get_token() == MP_DOWNTO) {
			// generate a decrementer
			AssignmentBlockPtr dec = AssignmentBlockPtr(new AssignmentBlock(false));
			dec->set_analyzer(this->get_analyzer());
			dec->catch_token((*this->get_unprocessed())[0]);
			dec->catch_token((*this->get_unprocessed())[0]);
			dec->catch_token(TokenPtr(new Token(MP_MINUS, "-", -1, -1)));
			dec->catch_token(TokenPtr(new Token(MP_INT_LITERAL, "1", -1, -1)));
			dec->preprocess();
			dec->generate_pre();
			dec->generate_post();
		}
		write_raw("BR " + this->cond_label + "\n");
		write_raw(this->exit_label + ":\n");
	}
}
void ActivationBlock::catch_token(TokenPtr symbol) {
	if (this->activity == DEFINITION) {
		if (symbol->get_token() != MP_BEGIN
			&& symbol->get_token() != MP_END
			&& symbol->get_token() != MP_SEMI_COLON) {
			this->get_unprocessed()->push_back(symbol);
		}
	} else if (this->activity == CALL) {
		bool first_id = true;
		if (first_id == true && symbol->get_token() == MP_ID) {
			this->caller_name = symbol->get_lexeme();
			first_id = false;
		} else {
			if (symbol->get_token() == MP_ID
				|| symbol->get_token() == MP_INT_LITERAL
				|| symbol->get_token() == MP_STRING_LITERAL
				|| symbol->get_token() == MP_FLOAT_LITERAL) {
				this->get_unprocessed()->push_back(symbol);
			}
		}
	}
}
int scanner_test(string filename) {
	cout << "[ Scanner Test ]" << endl;
	shared_ptr<Input> test_input = Input::open_file(filename);
    if (test_input != nullptr) {
        shared_ptr<Scanner> scanner = shared_ptr<Scanner>(new Scanner(test_input));
        TokenPtr t;
        do {
            t = scanner->scan_one();
            if (t == nullptr) {
                report_error("Scan Error", "Token returned was invalid");
            }
        } while (t->get_token() != MP_EOF);
        scanner->display_tokens();
    } else {
        return -1;
    }
	cout << "[ End ]" << endl;
	return 0;
}
void AssignmentBlock::catch_token(TokenPtr symbol) {
	if (symbol->get_token() != MP_COMMENT) {
		this->get_unprocessed()->push_back(symbol);
	}
}
SymbolPtr CodeBlock::translate(TokenPtr token) {
	if (token->get_token() == MP_ID) {
		string search_lexeme = token->get_lexeme();
		unsigned int search_level = this->get_nesting_level();
		SymbolListPtr filtered_data = this->get_analyzer()->get_symtable()->data_in_scope_at(search_lexeme, search_level);
		if (this->check_filter_size(filtered_data)) {
			SymbolPtr found = *filtered_data->begin();
			found->set_col(token->get_column());
			found->set_row(token->get_line());
			return found;
		} else {
			SymbolListPtr global_data = this->get_analyzer()->get_symtable()->data_in_scope_at(search_lexeme, 0);
			if (this->check_filter_size(global_data)) {
				SymbolPtr found = *global_data->begin();
				found->set_col(token->get_column());
				found->set_row(token->get_line());
				return found;
			} else {
				if (global_data == nullptr) {
					report_error_lc("Semantic Error", "ID '" + search_lexeme + "' not found",
									token->get_line(), token->get_column());
				} else {
					report_error("Semantic Error", "ID '" + search_lexeme + "' redefined as...");
					for (auto it = global_data->begin(); it != global_data->end(); it++) {
						SymDataPtr dptr = static_pointer_cast<SymData>(*it);
						report_error_lc("Definition @", (*it)->get_symbol_name() + " as " + var_type_to_string(dptr->get_var_type()), (*it)->get_row(), (*it)->get_col());
					}
				}
				this->valid = false;
				return nullptr;
			}
		}
	} else if (token->get_token() == MP_INT_LITERAL) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), INTEGER_LITERAL));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_STRING_LITERAL) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), STRING_LITERAL));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_FLOAT_LITERAL) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), FLOATING_LITERAL));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_TRUE) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), BOOLEAN_LITERAL_T));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_FALSE) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), BOOLEAN_LITERAL_F));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_LEFT_PAREN) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), LPAREN));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_RIGHT_PAREN) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), RPAREN));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_PLUS) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), ADD));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_MINUS) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), SUB));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_MULT) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), MUL));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_DIV) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), DIV));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_DIV_KW) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), DIV));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_MOD_KW) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), MOD));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_AND) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), AND));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_OR) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), OR));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_NOT) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), NOT));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_LESSTHAN) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), ILT));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_EQUALS) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), IEQ));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_LESSTHAN_EQUALTO) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), ILE));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_GREATERTHAN) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), IGT));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_GREATERTHAN_EQUALTO) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), IGE));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else if (token->get_token() == MP_NOT_EQUAL) {
		SymbolPtr s = SymbolPtr(new SymConstant(token->get_lexeme(), INE));
		s->set_row(token->get_line());
		s->set_col(token->get_column());
		return s;
	} else {
		return nullptr;
	}
}