diff --git a/midori/src/main.cpp b/midori/src/main.cpp index fb432f9..a460890 100644 --- a/midori/src/main.cpp +++ b/midori/src/main.cpp @@ -42,13 +42,22 @@ int test_lang() { LangASTPrinter p; program->accept(&p); CodeGen cg; - std::unique_ptr proto(new LangASTPrototype("main", "Int", {})); + std::vector> v; + //v.push_back(std::unique_ptr(new LangASTDecl("foo", "Int"))); + std::unique_ptr proto(new LangASTPrototype("main", "Int", std::move(v))); LangASTFunction g(std::move(proto), std::move(program)); cg.process(&g); cg.dump("program.bc"); + cg.run(); return 0; } +extern "C" { + void putint(int x) { + std::cout << "int " << x << std::endl; + } +} + void foo(int& x) { x = 3; } diff --git a/midori/src/midori/CMakeLists.txt b/midori/src/midori/CMakeLists.txt index 551fc6a..1b016bb 100644 --- a/midori/src/midori/CMakeLists.txt +++ b/midori/src/midori/CMakeLists.txt @@ -18,9 +18,10 @@ find_package(LLVM REQUIRED CONFIG) include_directories(${LLVM_INCLUDE_DIRS}) add_definitions(${LLVM_DEFINITIONS}) -llvm_map_components_to_libnames(llvm_libs core orcjit native) +llvm_map_components_to_libnames(llvm_libs core interpreter mcjit orcjit native) add_library(midori SHARED ${SOURCES}) target_compile_options(midori PRIVATE -Wall -Wextra -Wpedantic -Werror -Wno-unknown-pragmas) +target_link_libraries(midori PUBLIC -rdynamic) target_link_libraries(midori PUBLIC ${llvm_libs}) #target_link_libraries(midori PUBLIC coverage) diff --git a/midori/src/midori/codegen.cpp b/midori/src/midori/codegen.cpp index dd40a42..310521e 100644 --- a/midori/src/midori/codegen.cpp +++ b/midori/src/midori/codegen.cpp @@ -8,8 +8,14 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Bitcode/BitcodeWriter.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/ExecutionEngine/GenericValue.h" +#include "llvm/ExecutionEngine/OrcMCJITReplacement.h" +#include "llvm/ExecutionEngine/MCJIT.h" +//#include "llvm/ExecutionEngine/Interpreter.h" +#include -CodeGen::CodeGen() : builder(this->context), module("midori", this->context), type_manager(&(this->context)) { +CodeGen::CodeGen() : builder(this->context), module(new llvm::Module("midori", this->context)), type_manager(&(this->context)) { llvm::BasicBlock::Create(this->context, "main"); } @@ -17,16 +23,22 @@ void CodeGen::process(LangAST* program) { TypeChecker tc(&(this->type_manager)); program->accept(&tc); program->accept(this); + llvm::verifyModule(*(this->module), &(llvm::errs())); } std::error_code CodeGen::dump(std::string path) { std::error_code ec; llvm::raw_fd_ostream o(path, ec, llvm::sys::fs::OpenFlags::F_None); - llvm::WriteBitcodeToFile(&(this->module), o); - o.close(); + llvm::WriteBitcodeToFile(this->module.get(), o); return ec; } +void CodeGen::run() { + llvm::Function* f = this->get_function("main"); + llvm::ExecutionEngine* ee = llvm::EngineBuilder(std::move(this->module)).create(); + ee->runFunction(f, {}); +} + void CodeGen::visit(LangASTBlock* v) { this->push_scope(); for (std::unique_ptr const& l : v->lines) { @@ -82,36 +94,78 @@ void CodeGen::visit(LangASTBinOp* v) { } v->left->accept(this); llvm::Value* lhs = this->ret; + llvm::Type* type = lhs->getType(); + this->ret = nullptr; switch (v->op) { case LangASTBinOp::Op::PLUS: - this->ret = this->builder.CreateAdd(lhs, rhs, "addtmp"); + if (type->isIntegerTy()) { + this->ret = this->builder.CreateAdd(lhs, rhs, "addtmp"); + } else if (type->isFloatingPointTy()) { + this->ret = this->builder.CreateFAdd(lhs, rhs, "addftmp"); + } break; case LangASTBinOp::Op::MINUS: - this->ret = this->builder.CreateSub(lhs, rhs, "subtmp"); + if (type->isIntegerTy()) { + this->ret = this->builder.CreateSub(lhs, rhs, "subtmp"); + } else if (type->isFloatingPointTy()) { + this->ret = this->builder.CreateFSub(lhs, rhs, "subftmp"); + } break; case LangASTBinOp::Op::STAR: - this->ret = this->builder.CreateMul(lhs, rhs, "multmp"); + if (type->isIntegerTy()) { + this->ret = this->builder.CreateMul(lhs, rhs, "multmp"); + } else if (type->isFloatingPointTy()) { + this->ret = this->builder.CreateFMul(lhs, rhs, "mulftmp"); + } break; case LangASTBinOp::Op::SLASH: - this->ret = this->builder.CreateSDiv(lhs, rhs, "divtmp"); + if (type->isIntegerTy()) { + this->ret = this->builder.CreateSDiv(lhs, rhs, "divtmp"); + } else if (type->isFloatingPointTy()) { + this->ret = this->builder.CreateFDiv(lhs, rhs, "divftmp"); + } break; case LangASTBinOp::Op::EQ: - this->ret = this->builder.CreateICmpEQ(lhs, rhs, "eqtmp"); + if (type->isIntegerTy()) { + this->ret = this->builder.CreateICmpEQ(lhs, rhs, "eqtmp"); + } else if (type->isFloatingPointTy()) { + this->ret = this->builder.CreateFCmpOEQ(lhs, rhs, "eqftmp"); + } break; case LangASTBinOp::Op::NE: - this->ret = this->builder.CreateICmpNE(lhs, rhs, "netmp"); + if (type->isIntegerTy()) { + this->ret = this->builder.CreateICmpNE(lhs, rhs, "netmp"); + } else if (type->isFloatingPointTy()) { + this->ret = this->builder.CreateFCmpONE(lhs, rhs, "neftmp"); + } break; case LangASTBinOp::Op::LT: - this->ret = this->builder.CreateICmpSLT(lhs, rhs, "lttmp"); + if (type->isIntegerTy()) { + this->ret = this->builder.CreateICmpSLT(lhs, rhs, "lttmp"); + } else if (type->isFloatingPointTy()) { + this->ret = this->builder.CreateFCmpOLT(lhs, rhs, "ltftmp"); + } break; case LangASTBinOp::Op::GT: - this->ret = this->builder.CreateICmpSGT(lhs, rhs, "gttmp"); + if (type->isIntegerTy()) { + this->ret = this->builder.CreateICmpSGT(lhs, rhs, "gttmp"); + } else if (type->isFloatingPointTy()) { + this->ret = this->builder.CreateFCmpOGT(lhs, rhs, "gtftmp"); + } break; case LangASTBinOp::Op::LE: - this->ret = this->builder.CreateICmpSLE(lhs, rhs, "letmp"); + if (type->isIntegerTy()) { + this->ret = this->builder.CreateICmpSGE(lhs, rhs, "getmp"); + } else if (type->isFloatingPointTy()) { + this->ret = this->builder.CreateFCmpOGE(lhs, rhs, "geftmp"); + } break; case LangASTBinOp::Op::GE: - this->ret = this->builder.CreateICmpSGE(lhs, rhs, "getmp"); + if (type->isIntegerTy()) { + this->ret = this->builder.CreateICmpSLE(lhs, rhs, "letmp"); + } else if (type->isFloatingPointTy()) { + this->ret = this->builder.CreateFCmpOLE(lhs, rhs, "leftmp"); + } break; default: this->ret = nullptr; @@ -139,13 +193,17 @@ void CodeGen::visit(LangASTIf* v) { llvm::BasicBlock* then_bb = llvm::BasicBlock::Create(this->context, "then", f); llvm::BasicBlock* else_bb = llvm::BasicBlock::Create(this->context, "else"); llvm::BasicBlock* merge_bb = llvm::BasicBlock::Create(this->context, "ifcont"); + int x = 0; this->builder.CreateCondBr(cond, then_bb, else_bb); this->builder.SetInsertPoint(then_bb); v->block_if->accept(this); - this->builder.CreateBr(merge_bb); + if (this->builder.GetInsertBlock()->getTerminator() == nullptr) { + this->builder.CreateBr(merge_bb); + x++; + } f->getBasicBlockList().push_back(else_bb); this->builder.SetInsertPoint(else_bb); @@ -154,10 +212,15 @@ void CodeGen::visit(LangASTIf* v) { v->block_else->accept(this); } - this->builder.CreateBr(merge_bb); + if (this->builder.GetInsertBlock()->getTerminator() == nullptr) { + this->builder.CreateBr(merge_bb); + x++; + } - f->getBasicBlockList().push_back(merge_bb); - this->builder.SetInsertPoint(merge_bb); + if (x > 0) { + f->getBasicBlockList().push_back(merge_bb); + this->builder.SetInsertPoint(merge_bb); + } this->pop_scope(); this->ret = nullptr; } @@ -176,7 +239,9 @@ void CodeGen::visit(LangASTWhile* v) { f->getBasicBlockList().push_back(loop_bb); this->builder.SetInsertPoint(loop_bb); v->block->accept(this); - this->builder.CreateBr(cond_bb); + if (this->builder.GetInsertBlock()->getTerminator() == nullptr) { + this->builder.CreateBr(cond_bb); + } f->getBasicBlockList().push_back(after_bb); this->builder.SetInsertPoint(after_bb); this->pop_scope(); @@ -189,7 +254,7 @@ void CodeGen::visit(LangASTPrototype* v) { arg_types.push_back(a->type->llvm_type); } llvm::FunctionType* ft = llvm::FunctionType::get(this->type_manager.get(v->return_type)->llvm_type, arg_types, false); - llvm::Function* f = llvm::Function::Create(ft, llvm::Function::ExternalLinkage, v->name, &(this->module)); + llvm::Function* f = llvm::Function::Create(ft, llvm::Function::ExternalLinkage, v->name, this->module.get()); Int i = 0; for (llvm::Argument& a : f->args()) { a.setName(v->args.at(i)->name); @@ -216,18 +281,29 @@ void CodeGen::visit(LangASTFunction* v) { i++; } v->body->accept(this); - llvm::Value* z = llvm::ConstantInt::get(this->type_manager.get("Int")->llvm_type, 0, true); - this->builder.CreateRet(z); - - llvm::verifyFunction(*f); + if (this->builder.GetInsertBlock()->getTerminator() == nullptr) { + if (v->proto->return_type == this->type_manager.void_type()->name) { + this->builder.CreateRetVoid(); + } + } f->print(llvm::errs()); + llvm::verifyFunction(*f, &(llvm::errs())); this->pop_scope(); this->builder.SetInsertPoint(old); this->ret = f; } +void CodeGen::visit(LangASTReturn* v) { + if (v->val == nullptr) { + this->ret = this->builder.CreateRetVoid(); + return; + } + v->val->accept(this); + this->ret = this->builder.CreateRet(this->ret); +} + void CodeGen::visit(LangASTCall* v) { llvm::Function* f = this->get_function(v->function); if (f->arg_size() != v->args.size()) { @@ -239,6 +315,10 @@ void CodeGen::visit(LangASTCall* v) { a->accept(this); args.push_back(this->ret); } + if (f->getReturnType() == this->type_manager.void_type()->llvm_type) { + this->ret = this->builder.CreateCall(f, args); + return; + } this->ret = this->builder.CreateCall(f, args, "calltmp"); } @@ -261,7 +341,7 @@ llvm::Value* CodeGen::named_value(std::string s) { } llvm::Function* CodeGen::get_function(std::string name) { - if (llvm::Function* f = this->module.getFunction(name)) { + if (llvm::Function* f = this->module->getFunction(name)) { return f; } return nullptr; diff --git a/midori/src/midori/codegen.h b/midori/src/midori/codegen.h index 8e311fd..3fc00d8 100644 --- a/midori/src/midori/codegen.h +++ b/midori/src/midori/codegen.h @@ -17,6 +17,7 @@ class CodeGen : public ILangASTVisitor { CodeGen(); void process(LangAST*); std::error_code dump(std::string); + void run(); virtual void visit(LangASTBlock*) override; virtual void visit(LangASTIdent*) override; virtual void visit(LangASTDecl*) override; @@ -28,12 +29,13 @@ class CodeGen : public ILangASTVisitor { virtual void visit(LangASTWhile*) override; virtual void visit(LangASTPrototype*) override; virtual void visit(LangASTFunction*) override; + virtual void visit(LangASTReturn*) override; virtual void visit(LangASTCall*) override; private: llvm::LLVMContext context; llvm::IRBuilder<> builder; - llvm::Module module; + std::unique_ptr module; std::list> frames; llvm::Value* ret; TypeManager type_manager; diff --git a/midori/src/midori/lang.cpp b/midori/src/midori/lang.cpp index f337fb0..fe1366a 100644 --- a/midori/src/midori/lang.cpp +++ b/midori/src/midori/lang.cpp @@ -65,6 +65,10 @@ void LangASTFunction::accept(ILangASTVisitor* v) { v->visit(this); } +void LangASTReturn::accept(ILangASTVisitor* v) { + v->visit(this); +} + void LangASTCall::accept(ILangASTVisitor* v) { v->visit(this); } @@ -189,6 +193,17 @@ void LangASTPrinter::visit(LangASTFunction* v) { std::cout << "} endfunction " << v->proto->name << std::endl; } +void LangASTPrinter::visit(LangASTReturn* v) { + f(); + std::cout << "return"; + if (v->val == nullptr) { + std::cout << std::endl; + } else { + std::cout << " "; + v->val->accept(this); + } +} + void LangASTPrinter::visit(LangASTCall* v) { f(); std::cout << "call " << v->function << " {" << std::endl; @@ -226,6 +241,7 @@ void Lang::generate() { p->add_token("ELSE", re.parse("else")); p->add_token("WHILE", re.parse("while")); p->add_token("BREAK", re.parse("break")); + p->add_token("RETURN", re.parse("return")); p->add_token("IDENTIFIER", re.parse("[a-z][a-zA-Z0-9_]*")); p->add_token("TYPE", re.parse("[A-Z][a-zA-Z0-9_]*")); p->add_token("EQUALS", re.parse("=")); @@ -244,6 +260,10 @@ void Lang::generate() { p->add_token("STAR", re.parse("\\*")); p->add_token("SLASH", re.parse("/")); p->add_token("ARROW", re.parse("->")); + p->add_token("EQ", re.parse("==")); + p->add_token("NE", re.parse("!=")); + p->add_token("LE", re.parse("<=")); + p->add_token("GE", re.parse(">=")); p->add_production("code", { "lines" }, [](MatchedNonterminal* m) -> std::unique_ptr { return std::move(m->nonterminal(0)->value); @@ -294,6 +314,9 @@ void Lang::generate() { p->add_production("expression_void", { "function_proto" }, [](MatchedNonterminal* m) -> std::unique_ptr { return std::move(m->nonterminal(0)->value); }); + p->add_production("expression_void", { "function_return" }, [](MatchedNonterminal* m) -> std::unique_ptr { + return std::move(m->nonterminal(0)->value); + }); p->add_production("var_declaration", { "VAR", "declaration" }, [](MatchedNonterminal* m) -> std::unique_ptr { return std::move(m->nonterminal(1)->value); @@ -334,6 +357,15 @@ void Lang::generate() { return std::unique_ptr(new ParserValueDeclList(std::move(args))); }); + p->add_production("function_return", { "RETURN" }, [](MatchedNonterminal* m) -> std::unique_ptr { + (void) m; + return std::unique_ptr(new ParserValueLang(std::unique_ptr(new LangASTReturn(nullptr)))); + }); + p->add_production("function_return", { "RETURN", "expression" }, [](MatchedNonterminal* m) -> std::unique_ptr { + std::unique_ptr v = std::move(m->nonterminal(1)->value->get>()); + return std::unique_ptr(new ParserValueLang(std::unique_ptr(new LangASTReturn(std::move(v))))); + }); + p->add_production("expression", { "identifier" }, [](MatchedNonterminal* m) -> std::unique_ptr { return std::move(m->nonterminal(0)->value); }); @@ -409,11 +441,11 @@ void Lang::generate() { (void) m; return std::unique_ptr(new ParserValue(LangASTBinOp::Op::SLASH)); }); - p->add_production("bin_op", { "EQUALS", "EQUALS" }, [](MatchedNonterminal* m) -> std::unique_ptr { + p->add_production("bin_op", { "EQ" }, [](MatchedNonterminal* m) -> std::unique_ptr { (void) m; return std::unique_ptr(new ParserValue(LangASTBinOp::Op::EQ)); }); - p->add_production("bin_op", { "NOT", "EQUALS" }, [](MatchedNonterminal* m) -> std::unique_ptr { + p->add_production("bin_op", { "NE" }, [](MatchedNonterminal* m) -> std::unique_ptr { (void) m; return std::unique_ptr(new ParserValue(LangASTBinOp::Op::NE)); }); @@ -425,11 +457,11 @@ void Lang::generate() { (void) m; return std::unique_ptr(new ParserValue(LangASTBinOp::Op::GT)); }); - p->add_production("bin_op", { "LANGLE", "EQUALS" }, [](MatchedNonterminal* m) -> std::unique_ptr { + p->add_production("bin_op", { "LE" }, [](MatchedNonterminal* m) -> std::unique_ptr { (void) m; return std::unique_ptr(new ParserValue(LangASTBinOp::Op::LE)); }); - p->add_production("bin_op", { "RANGLE", "EQUALS" }, [](MatchedNonterminal* m) -> std::unique_ptr { + p->add_production("bin_op", { "GE" }, [](MatchedNonterminal* m) -> std::unique_ptr { (void) m; return std::unique_ptr(new ParserValue(LangASTBinOp::Op::GE)); }); @@ -463,7 +495,7 @@ void Lang::generate() { p->add_production("if_block", { "IF", "expression", "LBRACE", "lines", "RBRACE", "ELSE", "LBRACE", "lines", "RBRACE" }, [](MatchedNonterminal* m) -> std::unique_ptr { std::unique_ptr p = std::move(m->nonterminal(1)->value->get>()); std::unique_ptr b1 = std::move(m->nonterminal(3)->value->get>()); - std::unique_ptr b2 = std::move(m->nonterminal(5)->value->get>()); + std::unique_ptr b2 = std::move(m->nonterminal(7)->value->get>()); std::unique_ptr i(new LangASTIf(std::move(p), std::move(b1), std::move(b2))); return std::unique_ptr(new ParserValueLang(std::move(i))); }); @@ -517,18 +549,26 @@ void Lang::generate() { p->set_precedence_class("assign", 10, Precedence::Associativity::LEFT); p->set_precedence("EQUALS", "assign"); - p->set_precedence_class("binop_left", 20, Precedence::Associativity::LEFT); - p->set_precedence("PLUS", "binop_left"); - p->set_precedence("MINUS", "binop_left"); - p->set_precedence("STAR", "binop_left"); - p->set_precedence("SLASH", "binop_left"); - p->set_precedence("LANGLE", "binop_left"); - p->set_precedence("RANGLE", "binop_left"); - p->set_precedence_class("right", 30, Precedence::Associativity::RIGHT); + p->set_precedence_class("binop_left_eq", 20, Precedence::Associativity::LEFT); + p->set_precedence("EQ", "binop_left_eq"); + p->set_precedence("NE", "binop_left_eq"); + p->set_precedence("LANGLE", "binop_left_eq"); + p->set_precedence("RANGLE", "binop_left_eq"); + p->set_precedence("LE", "binop_left_eq"); + p->set_precedence("GE", "binop_left_eq"); + p->set_precedence_class("binop_left_add", 30, Precedence::Associativity::LEFT); + p->set_precedence("PLUS", "binop_left_add"); + p->set_precedence("MINUS", "binop_left_add"); + p->set_precedence_class("binop_left_mul", 40, Precedence::Associativity::LEFT); + p->set_precedence("STAR", "binop_left_mul"); + p->set_precedence("SLASH", "binop_left_mul"); + p->set_precedence_class("right", 50, Precedence::Associativity::RIGHT); p->set_precedence("LPAREN", "right"); p->set_precedence("NOT", "right"); p->generate(Parser::Type::LALR1, "code"); + std::cout << "===== LANG" << std::endl; + p->debug(); std::cout << "===== CONFLICTS" << std::endl; for (GrammarConflict const& gc : p->conflicts()) { p->debug_grammar_conflict(gc); diff --git a/midori/src/midori/lang.h b/midori/src/midori/lang.h index 6a2c0de..02efe5d 100644 --- a/midori/src/midori/lang.h +++ b/midori/src/midori/lang.h @@ -163,6 +163,15 @@ class LangASTFunction : public LangASTVoid { virtual void accept(ILangASTVisitor*) override; }; +class LangASTReturn : public LangASTVoid { +public: + std::unique_ptr val; + LangASTReturn(std::unique_ptr v) : val(std::move(v)) { + return; + } + virtual void accept(ILangASTVisitor*) override; +}; + class LangASTCall : public LangASTExpression { public: std::string function; @@ -188,6 +197,7 @@ class ILangASTVisitor { virtual void visit(LangASTWhile*) = 0; virtual void visit(LangASTPrototype*) = 0; virtual void visit(LangASTFunction*) = 0; + virtual void visit(LangASTReturn*) = 0; virtual void visit(LangASTCall*) = 0; }; @@ -205,6 +215,7 @@ class LangASTPrinter : public ILangASTVisitor { virtual void visit(LangASTWhile*) override; virtual void visit(LangASTPrototype*) override; virtual void visit(LangASTFunction*) override; + virtual void visit(LangASTReturn*) override; virtual void visit(LangASTCall*) override; private: diff --git a/midori/src/midori/type_checker.cpp b/midori/src/midori/type_checker.cpp index f0e783e..8c5dddd 100644 --- a/midori/src/midori/type_checker.cpp +++ b/midori/src/midori/type_checker.cpp @@ -94,6 +94,12 @@ void TypeChecker::visit(LangASTFunction* v) { this->ret(this->type_manager->void_type(), nullptr); } +void TypeChecker::visit(LangASTReturn* v) { + if (v->val != nullptr) { + v->val->accept(this); + } +} + void TypeChecker::visit(LangASTCall* v) { LangASTPrototype* f = this->find_function(v->function); Int i = 0; diff --git a/midori/src/midori/type_checker.h b/midori/src/midori/type_checker.h index 82f97f2..c9ad1df 100644 --- a/midori/src/midori/type_checker.h +++ b/midori/src/midori/type_checker.h @@ -20,6 +20,7 @@ class TypeChecker : public ILangASTVisitor { virtual void visit(LangASTWhile*) override; virtual void visit(LangASTPrototype*) override; virtual void visit(LangASTFunction*) override; + virtual void visit(LangASTReturn*) override; virtual void visit(LangASTCall*) override; private: diff --git a/midori/src/midori/types.cpp b/midori/src/midori/types.cpp index b34b752..90c20c8 100644 --- a/midori/src/midori/types.cpp +++ b/midori/src/midori/types.cpp @@ -10,7 +10,7 @@ bool Type::is_primitive() { // TODO: why this works? isn't the types map uninitialized TypeManager::TypeManager(llvm::LLVMContext* c) { - this->_void_type = this->register_type("Void", nullptr); + this->_void_type = this->register_type("Void", llvm::Type::getVoidTy(*c)); llvm::IntegerType* llvm_int1 = llvm::Type::getInt1Ty(*c); llvm::IntegerType* llvm_int8 = llvm::Type::getInt8Ty(*c); llvm::IntegerType* llvm_int16 = llvm::Type::getInt16Ty(*c); @@ -47,8 +47,8 @@ Type* TypeManager::register_type(std::string name, llvm::Type* llvm_type) { } Type* TypeManager::get(std::string name) { - std::map::iterator it = this->primitives.find(name); - return (it == this->primitives.end()) ? nullptr : it->second; + std::map>::iterator it = this->types.find(name); + return (it == this->types.end()) ? nullptr : it->second.get(); } bool TypeManager::is_primitive(std::string name) { diff --git a/midori/src/program.txt b/midori/src/program.txt index 0ebddf9..6b0280d 100644 --- a/midori/src/program.txt +++ b/midori/src/program.txt @@ -1,11 +1,22 @@ def putchar(x: Int) -> Int; +def foobar(x: Int) -> Void; def put2(x: Int) -> Int { var y: Int = x + 1; putchar(x); putchar(y); + return y * x; +} +def put3(x: Int) -> Void { + putchar(x); + putchar(x + 2); + putchar(x + 2 * 2); + if x == 97 { + return; + } else { + } } var a: Int = 3; var b: Int = a; @@ -39,5 +50,15 @@ var y: Long = 10; } put2(97); putchar(10); +put3(97); +putchar(10); a = 0xa; -a; +def foo() -> Void { + var x: Int = 0; + while x < 10 { + return; + } + putchar(101); +} +putint(123); +return a;