├── examples ├── x64 │ ├── .gitignore │ ├── empty_main.go │ ├── exit.go │ ├── intlit.go │ ├── four_arith.go │ ├── unary_minus.go │ ├── unary_plus.go │ ├── global_const.go │ ├── declare_autovar.go │ ├── varinit.go │ ├── unsigned_int.go │ ├── pointer.go │ ├── without_argument.go │ ├── hello_world.go │ ├── declare_twovar.go │ ├── type_alias.go │ ├── boolean_1.go │ ├── boolean_2.go │ ├── with_argument.go │ ├── if_expression.go │ ├── countup.go │ ├── pointer2.go │ ├── simple_struct.go │ ├── six_times_deref.go │ ├── enum.go │ └── six_pointer.go └── aarch64 │ ├── .gitignore │ ├── intlit.go │ ├── unary_minus.go │ ├── four_arith.go │ ├── declare_autovar.go │ └── declare_twovar.go ├── src ├── arch.rs ├── bundler.rs ├── arch │ ├── x64 │ │ ├── pass.rs │ │ ├── ir.rs │ │ ├── ir │ │ │ ├── module.rs │ │ │ ├── basic_block.rs │ │ │ ├── function.rs │ │ │ ├── inst_kind.rs │ │ │ ├── operand.rs │ │ │ └── instruction.rs │ │ └── build.rs │ ├── aarch64 │ │ ├── pass.rs │ │ ├── ir.rs │ │ ├── ir │ │ │ ├── module.rs │ │ │ ├── basic_block.rs │ │ │ ├── function.rs │ │ │ ├── inst_kind.rs │ │ │ ├── operand.rs │ │ │ └── instruction.rs │ │ ├── build.rs │ │ └── pass │ │ │ └── codegen.rs │ ├── x64.rs │ └── aarch64.rs ├── common │ ├── pass │ │ ├── tokenizer.rs │ │ ├── build_cfg.rs │ │ ├── tld_collector.rs │ │ ├── translator.rs │ │ ├── parser.rs │ │ ├── analyzer.rs │ │ ├── parser │ │ │ ├── context.rs │ │ │ ├── parser_util.rs │ │ │ ├── statement.rs │ │ │ └── main.rs │ │ ├── backend.rs │ │ ├── tld_collector │ │ │ └── main.rs │ │ ├── analyzer │ │ │ ├── alloc_frame.rs │ │ │ └── constant_folding.rs │ │ ├── build_cfg │ │ │ └── construct.rs │ │ └── frontend.rs │ ├── analyze_resource │ │ ├── cfg.rs │ │ ├── tld.rs │ │ ├── token.rs │ │ ├── ast.rs │ │ ├── frame_object.rs │ │ ├── tld │ │ │ ├── tld_kind.rs │ │ │ └── _tld.rs │ │ ├── ast │ │ │ ├── statement.rs │ │ │ ├── statement_kind.rs │ │ │ ├── expression_kind.rs │ │ │ ├── function.rs │ │ │ ├── ast_root.rs │ │ │ └── expression.rs │ │ ├── cfg │ │ │ └── local_graph.rs │ │ ├── token │ │ │ ├── _token.rs │ │ │ └── tokenkind.rs │ │ └── peachili_type.rs │ ├── module.rs │ ├── analyze_resource.rs │ ├── error.rs │ ├── pass.rs │ ├── three_address_code.rs │ ├── three_address_code │ │ ├── code.rs │ │ ├── module.rs │ │ ├── value_kind.rs │ │ ├── function.rs │ │ ├── value.rs │ │ └── code_kind.rs │ ├── file_util.rs │ ├── module │ │ ├── module_test.rs │ │ └── _module.rs │ ├── position.rs │ ├── error │ │ ├── compile_error │ │ │ └── tokenize_error.rs │ │ ├── bundle_error.rs │ │ ├── compile_error.rs │ │ └── type_error.rs │ └── option.rs ├── debug.rs ├── lib.rs ├── common.rs ├── debug │ ├── dump_ir.rs │ └── dump_local_cfg.rs ├── main.rs ├── setup.rs └── bundler │ └── resolve.rs ├── failures ├── not_found_main.go ├── return_in_noreturn_func.go ├── invalid_type_main.go ├── minus_to_unsigned.go ├── invalid_arg_number.go ├── member_with_not_struct.go ├── undefined_function_call.go ├── invalid_assignment.go ├── return_local_pointer.go ├── invalid_arg_types.go ├── add_int64_and_boolean.go ├── if_expr_stmt1.go ├── if_expr_stmt2.go ├── assignment_to_constant.go ├── invalid_member.go ├── if_expr2.go ├── if_expr1.go ├── uint_and_sint.go └── invalid_integer_literal.go ├── docs ├── main.md ├── internals │ ├── index.md │ ├── tokenizer.md │ └── parser.md └── syntax.md ├── .gitignore ├── lib ├── startup_aarch64.go ├── startup_x64.go ├── aarch64.go └── x64.go ├── .github └── workflows │ └── clippy.yaml ├── Cargo.toml ├── README.md ├── aarch64_test.sh ├── LICENSE ├── x64_test.sh ├── error_test.sh └── Cargo.lock /examples/x64/.gitignore: -------------------------------------------------------------------------------- 1 | *.s 2 | -------------------------------------------------------------------------------- /examples/aarch64/.gitignore: -------------------------------------------------------------------------------- 1 | *.s 2 | -------------------------------------------------------------------------------- /src/arch.rs: -------------------------------------------------------------------------------- 1 | pub mod aarch64; 2 | pub mod x64; 3 | -------------------------------------------------------------------------------- /examples/x64/empty_main.go: -------------------------------------------------------------------------------- 1 | func main() Noreturn { 2 | } -------------------------------------------------------------------------------- /failures/not_found_main.go: -------------------------------------------------------------------------------- 1 | func not_main() noreturn { 2 | } -------------------------------------------------------------------------------- /src/bundler.rs: -------------------------------------------------------------------------------- 1 | mod resolve; 2 | 3 | pub use resolve::*; 4 | -------------------------------------------------------------------------------- /src/arch/x64/pass.rs: -------------------------------------------------------------------------------- 1 | mod codegen; 2 | 3 | pub use codegen::*; 4 | -------------------------------------------------------------------------------- /src/common/pass/tokenizer.rs: -------------------------------------------------------------------------------- 1 | mod main; 2 | 3 | pub use main::*; 4 | -------------------------------------------------------------------------------- /src/arch/aarch64/pass.rs: -------------------------------------------------------------------------------- 1 | mod codegen; 2 | 3 | pub use codegen::*; 4 | -------------------------------------------------------------------------------- /src/common/pass/build_cfg.rs: -------------------------------------------------------------------------------- 1 | pub use construct::*; 2 | mod construct; 3 | -------------------------------------------------------------------------------- /src/common/pass/tld_collector.rs: -------------------------------------------------------------------------------- 1 | mod main; 2 | 3 | pub use main::*; 4 | -------------------------------------------------------------------------------- /src/common/pass/translator.rs: -------------------------------------------------------------------------------- 1 | mod translate; 2 | pub use translate::*; 3 | -------------------------------------------------------------------------------- /failures/return_in_noreturn_func.go: -------------------------------------------------------------------------------- 1 | func main() noreturn { 2 | return 30; 3 | } -------------------------------------------------------------------------------- /src/arch/x64.rs: -------------------------------------------------------------------------------- 1 | mod build; 2 | mod ir; 3 | mod pass; 4 | 5 | pub use build::*; 6 | -------------------------------------------------------------------------------- /src/arch/aarch64.rs: -------------------------------------------------------------------------------- 1 | mod build; 2 | mod ir; 3 | mod pass; 4 | 5 | pub use build::*; 6 | -------------------------------------------------------------------------------- /src/common/analyze_resource/cfg.rs: -------------------------------------------------------------------------------- 1 | mod local_graph; 2 | 3 | pub use local_graph::*; 4 | -------------------------------------------------------------------------------- /src/common/module.rs: -------------------------------------------------------------------------------- 1 | pub use _module::*; 2 | 3 | mod _module; 4 | mod module_test; 5 | -------------------------------------------------------------------------------- /failures/invalid_type_main.go: -------------------------------------------------------------------------------- 1 | func main(v1 Int64) Int64 { 2 | return v1 + 1; 3 | } 4 | -------------------------------------------------------------------------------- /examples/x64/exit.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | func main() Noreturn { 4 | x64::exit_with(3); 5 | } 6 | -------------------------------------------------------------------------------- /examples/x64/intlit.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | func main() Noreturn { 4 | x64::exit_with(0); 5 | } 6 | -------------------------------------------------------------------------------- /examples/x64/four_arith.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | func main() Noreturn { 4 | x64::exit_with(1 + 2 * 4); 5 | } 6 | -------------------------------------------------------------------------------- /examples/x64/unary_minus.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | func main() Noreturn { 4 | x64::exit_with(-3 * -3); 5 | } 6 | -------------------------------------------------------------------------------- /examples/aarch64/intlit.go: -------------------------------------------------------------------------------- 1 | import aarch64; 2 | 3 | func main() Noreturn { 4 | aarch64::exit_with(42); 5 | } 6 | -------------------------------------------------------------------------------- /examples/x64/unary_plus.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | func main() Noreturn { 4 | x64::exit_with(0 + -3 + +3); 5 | } 6 | -------------------------------------------------------------------------------- /src/common/analyze_resource/tld.rs: -------------------------------------------------------------------------------- 1 | mod _tld; 2 | pub use _tld::*; 3 | 4 | mod tld_kind; 5 | pub use tld_kind::*; 6 | -------------------------------------------------------------------------------- /src/debug.rs: -------------------------------------------------------------------------------- 1 | mod dump_ir; 2 | 3 | pub use dump_ir::*; 4 | 5 | mod dump_local_cfg; 6 | pub use dump_local_cfg::*; 7 | -------------------------------------------------------------------------------- /examples/aarch64/unary_minus.go: -------------------------------------------------------------------------------- 1 | import aarch64; 2 | 3 | func main() Noreturn { 4 | aarch64::exit_with(-3 * -3); 5 | } 6 | -------------------------------------------------------------------------------- /failures/minus_to_unsigned.go: -------------------------------------------------------------------------------- 1 | func main() Noreturn { 2 | varinit x Uint64 = 1; 3 | varinit y Uint64 = -x; 4 | } 5 | -------------------------------------------------------------------------------- /examples/aarch64/four_arith.go: -------------------------------------------------------------------------------- 1 | import aarch64; 2 | 3 | func main() Noreturn { 4 | aarch64::exit_with(1 + 2 * 4); 5 | } 6 | -------------------------------------------------------------------------------- /src/common/analyze_resource/token.rs: -------------------------------------------------------------------------------- 1 | mod _token; 2 | mod tokenkind; 3 | 4 | pub use _token::*; 5 | pub use tokenkind::*; 6 | -------------------------------------------------------------------------------- /docs/main.md: -------------------------------------------------------------------------------- 1 | # The Peachili Language Documents 2 | 3 | ## [Syntax](./syntax.md) 4 | 5 | ## [Compiler Internals](./internals/index.md) -------------------------------------------------------------------------------- /examples/x64/global_const.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | pubconst X : Int64 = 30; 4 | 5 | func main() Noreturn { 6 | x64::exit_with(X); 7 | } 8 | -------------------------------------------------------------------------------- /src/common/pass/parser.rs: -------------------------------------------------------------------------------- 1 | mod context; 2 | mod expression; 3 | mod main; 4 | mod parser_util; 5 | mod statement; 6 | 7 | pub use main::*; 8 | -------------------------------------------------------------------------------- /examples/x64/declare_autovar.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | func main() Noreturn { 4 | declare x Int64; 5 | x = 30; 6 | x64::exit_with(x); 7 | } 8 | -------------------------------------------------------------------------------- /examples/x64/varinit.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | func main() Noreturn { 4 | varinit x Int64 = 10 + 10 + 10; 5 | x64::exit_with(x); 6 | } 7 | -------------------------------------------------------------------------------- /examples/x64/unsigned_int.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | func main() Noreturn { 4 | declare x Uint64; 5 | x = 100u; 6 | x64::exit_with(1); 7 | } 8 | -------------------------------------------------------------------------------- /failures/invalid_arg_number.go: -------------------------------------------------------------------------------- 1 | func callee(v1 Int64, v2 Uint64, v3 ConstStr) Noreturn { 2 | } 3 | 4 | func main() Noreturn { 5 | callee(); 6 | } 7 | -------------------------------------------------------------------------------- /failures/member_with_not_struct.go: -------------------------------------------------------------------------------- 1 | require ( 2 | "std" 3 | ) 4 | 5 | func main() Noreturn { 6 | declare x Boolean; 7 | x.foo = true; 8 | } 9 | -------------------------------------------------------------------------------- /src/common/analyze_resource.rs: -------------------------------------------------------------------------------- 1 | pub mod ast; 2 | pub mod cfg; 3 | pub mod frame_object; 4 | pub mod peachili_type; 5 | pub mod tld; 6 | pub mod token; 7 | -------------------------------------------------------------------------------- /examples/aarch64/declare_autovar.go: -------------------------------------------------------------------------------- 1 | import aarch64; 2 | 3 | func main() Noreturn { 4 | declare x Int64; 5 | x = 30; 6 | aarch64::exit_with(x); 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.idea 3 | /.idea/* 4 | /cmake-build-debug 5 | /cmake-build-debug/* 6 | *hir_dump* 7 | *.dot* 8 | *.png* 9 | *.gdb_history* 10 | -------------------------------------------------------------------------------- /examples/x64/pointer.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | func main() Noreturn { 4 | varinit x Int64 = 4; 5 | varinit y *Int64 = &x; 6 | x64::exit_with(*y); 7 | } 8 | -------------------------------------------------------------------------------- /examples/x64/without_argument.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | func foo() Int64 { 4 | return 30; 5 | } 6 | func main() Noreturn { 7 | x64::exit_with(foo()); 8 | } 9 | -------------------------------------------------------------------------------- /failures/undefined_function_call.go: -------------------------------------------------------------------------------- 1 | require ( 2 | "std" 3 | ) 4 | func main() noreturn { 5 | // exit_with はあるけど exit はない 6 | std::os::exit(0); 7 | } 8 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod arch; 2 | pub mod bundler; 3 | pub mod common; 4 | pub mod debug; 5 | pub mod setup; 6 | 7 | #[macro_use] 8 | extern crate lazy_static; 9 | -------------------------------------------------------------------------------- /examples/x64/hello_world.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | func main() Noreturn { 4 | x64::write(x64::STDOUT, "Hello, world!\n", 14); 5 | x64::exit_with(0); 6 | } 7 | -------------------------------------------------------------------------------- /failures/invalid_assignment.go: -------------------------------------------------------------------------------- 1 | require ( 2 | "std" 3 | ) 4 | func main() Noreturn { 5 | declare x Boolean; 6 | x = 24; 7 | std::os::exit_with(0); 8 | } 9 | -------------------------------------------------------------------------------- /examples/x64/declare_twovar.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | func main() Noreturn { 4 | declare x Int64; 5 | declare y Int64; 6 | x = y = 3; 7 | x64::exit_with(x * y); 8 | } 9 | -------------------------------------------------------------------------------- /failures/return_local_pointer.go: -------------------------------------------------------------------------------- 1 | func main() Noreturn { 2 | callee(); 3 | } 4 | 5 | func callee() *Int64 { 6 | varinit x Int64 = 0; 7 | return &x; 8 | } 9 | -------------------------------------------------------------------------------- /failures/invalid_arg_types.go: -------------------------------------------------------------------------------- 1 | func callee(v1 Int64, v2 Uint64, v3 ConstStr) Noreturn { 2 | } 3 | 4 | func main() Noreturn { 5 | callee(100u, "Hello, world!", 0); 6 | } 7 | -------------------------------------------------------------------------------- /src/common/error.rs: -------------------------------------------------------------------------------- 1 | mod bundle_error; 2 | mod compile_error; 3 | mod type_error; 4 | 5 | pub use bundle_error::*; 6 | pub use compile_error::*; 7 | pub use type_error::*; 8 | -------------------------------------------------------------------------------- /examples/x64/type_alias.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | pubtype Another = Int64; 4 | 5 | func main() Noreturn { 6 | declare x Another; 7 | x = 30; 8 | x64::exit_with(x); 9 | } 10 | -------------------------------------------------------------------------------- /failures/add_int64_and_boolean.go: -------------------------------------------------------------------------------- 1 | require ( 2 | "std" 3 | ) 4 | 5 | func main() Noreturn { 6 | declare x Int64; 7 | x = 30 + true; 8 | std::os::exit_with(2); 9 | } 10 | -------------------------------------------------------------------------------- /examples/x64/boolean_1.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | func main() Noreturn { 4 | if (true) { 5 | x64::exit_with(15); 6 | } else { 7 | x64::exit_with(30); 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /examples/x64/boolean_2.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | func main() Noreturn { 4 | if (false) { 5 | x64::exit_with(15); 6 | } else { 7 | x64::exit_with(30); 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /examples/x64/with_argument.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | func foo(x Int64, y Int64) Int64 { 4 | return x + y; 5 | } 6 | 7 | func main() Noreturn { 8 | x64::exit_with(foo(10, 20)); 9 | } 10 | -------------------------------------------------------------------------------- /examples/aarch64/declare_twovar.go: -------------------------------------------------------------------------------- 1 | import aarch64; 2 | 3 | func main() Noreturn { 4 | declare x Int64; 5 | declare y Int64; 6 | x = 3; 7 | y = 3; 8 | aarch64::exit_with(x * y); 9 | } 10 | -------------------------------------------------------------------------------- /examples/x64/if_expression.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | func main() Noreturn { 4 | declare x Int64; 5 | x = if(true) { 6 | ifret 1; 7 | } else { 8 | ifret 2; 9 | }; 10 | x64::exit_with(x); 11 | } 12 | -------------------------------------------------------------------------------- /failures/if_expr_stmt1.go: -------------------------------------------------------------------------------- 1 | require ( 2 | "std" 3 | ) 4 | func main() Noreturn { 5 | 6 | // if式内の条件式はBoolean型のみ 7 | if (1) { 8 | std::os::exit_with(1); 9 | }; 10 | std::os::exit_with(0); 11 | } 12 | -------------------------------------------------------------------------------- /failures/if_expr_stmt2.go: -------------------------------------------------------------------------------- 1 | require ( 2 | "std" 3 | ) 4 | func main() Noreturn { 5 | 6 | // if式内の条件式はboolean型のみ 7 | if (0) { 8 | std::os::exit_with(1); 9 | }; 10 | std::os::exit_with(0); 11 | } 12 | -------------------------------------------------------------------------------- /examples/x64/countup.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | func main() Noreturn { 3 | declare res Int64; 4 | 5 | // 左閉右開区間の数え上げ 6 | countup x Int64 from 0 to 10 { 7 | res = x; 8 | }; 9 | x64::exit_with(res); 10 | } 11 | -------------------------------------------------------------------------------- /docs/internals/index.md: -------------------------------------------------------------------------------- 1 | # The Peachili Compiler Internals 2 | 3 | コンパイラの実装について簡単にドキュメントを残す. 4 | 細かいコードの解説というよりは,処理の大まかな流れの紹介という感じ. 5 | Rust風疑似コードを用いて紹介. 6 | 7 | ## [Tokenizer](./tokenizer.md) 8 | 9 | ## [Parser](./parser.md) -------------------------------------------------------------------------------- /failures/assignment_to_constant.go: -------------------------------------------------------------------------------- 1 | require ( 2 | "std" 3 | ) 4 | 5 | func main() Noreturn { 6 | varinit x Int64 = 30; 7 | const y Int64 = x; 8 | y = y + 2; 9 | 10 | std::os::exit_with(0); 11 | } 12 | -------------------------------------------------------------------------------- /failures/invalid_member.go: -------------------------------------------------------------------------------- 1 | require ( 2 | "std" 3 | ) 4 | 5 | struct X { 6 | exist Boolean 7 | } 8 | 9 | func main() Noreturn { 10 | declare x X; 11 | x.foo = true; 12 | std::os::exit_with(x.foo); 13 | } 14 | -------------------------------------------------------------------------------- /examples/x64/pointer2.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | pubtype PointerToI64 = *Int64; 4 | 5 | func main() Noreturn { 6 | varinit x Int64 = 0; 7 | varinit y PointerToI64 = &x; 8 | *y = 4; 9 | 10 | x64::exit_with(x); 11 | } 12 | -------------------------------------------------------------------------------- /lib/startup_aarch64.go: -------------------------------------------------------------------------------- 1 | func initialize() noreturn { 2 | asm { 3 | "bl main", 4 | "mov x0, x1", // main関数の返り値(通常は0)をプロセス全体の返り値に 5 | "mov x8, #93", // 64bit linuxにおけるexitシステムコール 6 | "svc #0" 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /src/common.rs: -------------------------------------------------------------------------------- 1 | pub mod analyze_resource; 2 | pub mod error; 3 | pub mod file_util; 4 | pub mod module; 5 | pub mod option; 6 | pub mod pass; 7 | pub mod position; 8 | pub mod three_address_code; 9 | 10 | pub use analyze_resource::*; 11 | -------------------------------------------------------------------------------- /lib/startup_x64.go: -------------------------------------------------------------------------------- 1 | func initialize() Noreturn { 2 | asm { 3 | "call main"; 4 | "movq %rax, %rdi"; // main関数の返り値(通常は0)をプロセス全体の返り値に 5 | "movq $60, %rax"; // 64bit linuxにおけるexitシステムコール 6 | "syscall"; 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /failures/if_expr2.go: -------------------------------------------------------------------------------- 1 | require ( 2 | "std" 3 | ) 4 | func main() Noreturn { 5 | declare x Int64; 6 | 7 | // if式内の条件式はBoolean型のみ 8 | x = if (0) { 9 | ifret 1; 10 | } else { 11 | ifret 0; 12 | }; 13 | std::os::exit_with(x); 14 | } 15 | -------------------------------------------------------------------------------- /failures/if_expr1.go: -------------------------------------------------------------------------------- 1 | require ( 2 | "std" 3 | ) 4 | func main() Noreturn { 5 | declare x Int64; 6 | 7 | // if式内の条件式はBoolean型のみ 8 | x = if (1) { 9 | ifret 1; 10 | } else { 11 | ifret 0; 12 | }; 13 | 14 | std::os::exit_with(x); 15 | } 16 | -------------------------------------------------------------------------------- /src/common/pass/analyzer.rs: -------------------------------------------------------------------------------- 1 | mod type_resolve; 2 | 3 | pub use type_resolve::*; 4 | 5 | mod type_check; 6 | 7 | pub use type_check::*; 8 | 9 | mod alloc_frame; 10 | pub use alloc_frame::*; 11 | 12 | mod constant_folding; 13 | pub use constant_folding::*; 14 | -------------------------------------------------------------------------------- /failures/uint_and_sint.go: -------------------------------------------------------------------------------- 1 | require ( 2 | "std" 3 | ) 4 | 5 | func main() Noreturn { 6 | declare x Uint64; 7 | declare y Int64; 8 | x = 100; 9 | y = 50u; 10 | 11 | declare z Int64; 12 | z = x + y; 13 | std::os::exit_with(z); 14 | } 15 | -------------------------------------------------------------------------------- /examples/x64/simple_struct.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | struct A { 4 | foo Int64 5 | bar Int64 6 | } 7 | 8 | func main() Noreturn { 9 | declare a A; 10 | a.foo = 15; 11 | a.bar = 30; 12 | 13 | varinit res Int64 = a.foo + a.bar; 14 | x64::exit_with(res); 15 | } 16 | -------------------------------------------------------------------------------- /src/common/pass.rs: -------------------------------------------------------------------------------- 1 | mod analyzer; 2 | mod backend; 3 | mod build_cfg; 4 | mod frontend; 5 | mod parser; 6 | mod tld_collector; 7 | mod tokenizer; 8 | mod translator; 9 | 10 | pub use backend::*; 11 | pub use build_cfg::*; 12 | pub use frontend::*; 13 | pub use translator::*; 14 | -------------------------------------------------------------------------------- /examples/x64/six_times_deref.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | func main() Noreturn { 4 | varinit a Int64 = 4; 5 | varinit b *Int64 = &a; 6 | varinit c **Int64 = &b; 7 | varinit d ***Int64 = &c; 8 | varinit e ****Int64 = &d; 9 | varinit f *****Int64 = &e; 10 | x64::exit_with(*****f); 11 | } 12 | -------------------------------------------------------------------------------- /failures/invalid_integer_literal.go: -------------------------------------------------------------------------------- 1 | require ( 2 | "std" 3 | ) 4 | 5 | func main() Noreturn { 6 | declare status int64; 7 | status = 12343278421789312748923173892147983217938762189473129807329107329180743920817490237189203798321074890312; 8 | std::os::exit_with(status); 9 | } 10 | -------------------------------------------------------------------------------- /.github/workflows/clippy.yaml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v1 11 | - uses: icepuma/rust-action@master 12 | with: 13 | args: cargo fmt && cargo clippy -- -Dwarnings && cargo test 14 | -------------------------------------------------------------------------------- /src/arch/x64/ir.rs: -------------------------------------------------------------------------------- 1 | mod module; 2 | 3 | pub use module::*; 4 | mod function; 5 | 6 | pub use function::*; 7 | mod instruction; 8 | 9 | pub use instruction::*; 10 | mod basic_block; 11 | 12 | pub use basic_block::*; 13 | 14 | mod inst_kind; 15 | pub use inst_kind::*; 16 | 17 | mod operand; 18 | pub use operand::*; 19 | -------------------------------------------------------------------------------- /src/arch/aarch64/ir.rs: -------------------------------------------------------------------------------- 1 | mod module; 2 | 3 | pub use module::*; 4 | mod function; 5 | 6 | pub use function::*; 7 | mod instruction; 8 | 9 | pub use instruction::*; 10 | mod basic_block; 11 | 12 | pub use basic_block::*; 13 | 14 | mod inst_kind; 15 | pub use inst_kind::*; 16 | 17 | mod operand; 18 | pub use operand::*; 19 | -------------------------------------------------------------------------------- /src/common/three_address_code.rs: -------------------------------------------------------------------------------- 1 | mod code; 2 | 3 | pub use code::*; 4 | 5 | mod code_kind; 6 | 7 | pub use code_kind::*; 8 | 9 | mod function; 10 | 11 | pub use function::*; 12 | 13 | mod module; 14 | 15 | pub use module::*; 16 | 17 | mod value; 18 | pub use value::*; 19 | 20 | mod value_kind; 21 | pub use value_kind::*; 22 | -------------------------------------------------------------------------------- /examples/x64/enum.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | pubenum A { 4 | B, 5 | C, 6 | } 7 | 8 | func main() Noreturn { 9 | varinit x A = A::B; 10 | 11 | match x { 12 | A::B -> { 13 | x64::exit_with(2); 14 | }, 15 | A::C -> { 16 | x64::exit_with(4); 17 | }, 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/common/analyze_resource/ast.rs: -------------------------------------------------------------------------------- 1 | mod statement; 2 | pub use statement::*; 3 | 4 | mod statement_kind; 5 | pub use statement_kind::*; 6 | 7 | mod expression; 8 | pub use expression::*; 9 | 10 | mod expression_kind; 11 | pub use expression_kind::*; 12 | 13 | mod ast_root; 14 | pub use ast_root::*; 15 | 16 | mod function; 17 | pub use function::*; 18 | -------------------------------------------------------------------------------- /src/common/analyze_resource/frame_object.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | pub type StackFrame = BTreeMap>; 4 | 5 | /// 型 6 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] 7 | pub struct FrameObject { 8 | /// 変数のオフセット 9 | /// 関数の場合は関数が割り当てるスタックフレームのサイズが入っている 10 | pub offset: usize, 11 | } 12 | -------------------------------------------------------------------------------- /examples/x64/six_pointer.go: -------------------------------------------------------------------------------- 1 | import x64; 2 | 3 | 4 | func main() Noreturn { 5 | varinit a Int64 = 0; 6 | varinit b *Int64 = &a; 7 | varinit c **Int64 = &b; 8 | varinit d ***Int64 = &c; 9 | varinit e ****Int64 = &d; 10 | varinit f *****Int64 = &e; 11 | varinit g ******Int64 = &f; 12 | ******g = 4; 13 | 14 | x64::exit_with(a); 15 | } 16 | -------------------------------------------------------------------------------- /lib/aarch64.go: -------------------------------------------------------------------------------- 1 | func exit_with(status Int64) Noreturn { 2 | asm { 3 | "mov x8, #93"; // 64bit linuxにおけるexitシステムコール 4 | "svc #0"; 5 | }; 6 | } 7 | 8 | func write(fd FileDescriptor, buf ConstStr, count Int64) Noreturn { 9 | asm { 10 | "mov x8, #64"; // 64bit linuxにおけるwriteシステムコール 11 | "svc #0"; 12 | }; 13 | } 14 | 15 | pubtype FileDescriptor = Uint64; 16 | -------------------------------------------------------------------------------- /lib/x64.go: -------------------------------------------------------------------------------- 1 | func exit_with(status Int64) Noreturn { 2 | asm { 3 | "movq $60, %rax"; 4 | "syscall"; 5 | }; 6 | } 7 | 8 | func write(fd FileDescriptor, buf ConstStr, count Int64) Noreturn { 9 | asm { 10 | "movq $1, %rax"; 11 | "syscall"; 12 | }; 13 | } 14 | 15 | pubtype FileDescriptor = Uint64; 16 | 17 | pubconst STDIN : FileDescriptor = 0u; 18 | pubconst STDOUT : FileDescriptor = 1u; 19 | pubconst STDERR : FileDescriptor = 2u; 20 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "peachili" 3 | version = "0.1.0" 4 | authors = ["Drumato "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | clap = { git = "https://github.com/clap-rs/clap/", features = ["yaml"] } 11 | yaml-rust = "0.4" 12 | elf-utilities = "0.1.73" 13 | indexmap = "1.3.2" 14 | colored = "1.9.3" 15 | asmpeach = "0.1.46" 16 | pld = "0.1.45" 17 | lazy_static = "1.4.0" 18 | 19 | # llvm-scratch = "0.1.15" 20 | id-arena = "2.2.1" 21 | -------------------------------------------------------------------------------- /src/common/three_address_code/code.rs: -------------------------------------------------------------------------------- 1 | use crate::common::three_address_code::code_kind; 2 | use crate::common::three_address_code::function::ValueArena; 3 | use id_arena::Id; 4 | 5 | pub type CodeId = Id; 6 | 7 | /// IRの最小単位 8 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] 9 | pub struct Code { 10 | /// 種類 11 | pub kind: code_kind::CodeKind, 12 | // ベーシックブロック分割時に利用 13 | // pub label: Option, 14 | } 15 | 16 | impl Code { 17 | pub fn dump(&self, value_arena: ValueArena) -> String { 18 | self.kind.dump(value_arena) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/arch/x64/ir/module.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::x64::ir; 2 | 3 | pub struct Module { 4 | funcs: Vec, 5 | } 6 | 7 | impl Default for Module { 8 | fn default() -> Self { 9 | Self { funcs: Vec::new() } 10 | } 11 | } 12 | impl Module { 13 | pub fn push_function(&mut self, f: ir::Function) { 14 | self.funcs.push(f); 15 | } 16 | 17 | pub fn to_atandt(&self) -> String { 18 | let mut module_code = String::new(); 19 | 20 | for ir_fn in self.funcs.iter() { 21 | module_code += &ir_fn.to_atandt(); 22 | } 23 | 24 | module_code 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/common/file_util.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | use std::io::Write; 3 | 4 | /// pathから内容を読み込み,Stringを返す 5 | /// ファイルが存在しなかったとき,None 6 | pub fn read_program_from_file(path: &str) -> Option { 7 | let result_contents = fs::read_to_string(path); 8 | 9 | if result_contents.is_err() { 10 | return None; 11 | } 12 | 13 | Some(result_contents.unwrap()) 14 | } 15 | 16 | /// path で新規にファイルを作成し,programを書き込む 17 | pub fn write_program_into(path: &str, program: String) { 18 | let mut file = fs::File::create(path).unwrap(); 19 | file.write_all(program.as_bytes()).unwrap(); 20 | file.flush().unwrap(); 21 | } 22 | -------------------------------------------------------------------------------- /src/arch/aarch64/ir/module.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::aarch64::ir; 2 | 3 | pub struct Module { 4 | funcs: Vec, 5 | } 6 | 7 | impl Default for Module { 8 | fn default() -> Self { 9 | Self { funcs: Vec::new() } 10 | } 11 | } 12 | impl Module { 13 | pub fn push_function(&mut self, f: ir::Function) { 14 | self.funcs.push(f); 15 | } 16 | 17 | pub fn to_assembly(&self) -> String { 18 | let mut module_code = String::new(); 19 | 20 | for ir_fn in self.funcs.iter() { 21 | module_code += &ir_fn.to_assembly(); 22 | } 23 | 24 | module_code 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/common/three_address_code/module.rs: -------------------------------------------------------------------------------- 1 | use crate::common::three_address_code::*; 2 | use id_arena::Arena; 3 | 4 | #[derive(Debug, Clone)] 5 | pub struct IRModule { 6 | pub funcs: Vec, 7 | pub fn_allocator: Arena, 8 | } 9 | 10 | impl Default for IRModule { 11 | fn default() -> Self { 12 | Self { 13 | funcs: Vec::new(), 14 | fn_allocator: Arena::new(), 15 | } 16 | } 17 | } 18 | 19 | impl IRModule { 20 | pub fn get_fn(&self, fn_id: &function::IRFunctionId) -> &function::IRFunction { 21 | self.fn_allocator.get(*fn_id).unwrap() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/common/module/module_test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod module_tests { 3 | use crate::common::module::*; 4 | use id_arena::Arena; 5 | 6 | #[test] 7 | fn module_refs_increment_test() { 8 | let parent = Module::new_primary(String::new(), String::new()); 9 | let mut arena: Arena = Arena::new(); 10 | 11 | assert_eq!(0, parent.ref_count()); 12 | 13 | // 適当にrefを増やす 14 | for i in 0..3 { 15 | let e_m = arena.alloc(Module::new_external(String::new(), i.to_string())); 16 | parent.refs.lock().unwrap().push(e_m); 17 | } 18 | 19 | assert_eq!(3, parent.ref_count()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/common/analyze_resource/tld/tld_kind.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | type ArgType = String; 4 | type ArgName = String; 5 | type MemberType = String; 6 | type MemberName = String; 7 | 8 | #[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Clone)] 9 | pub enum TLDKind { 10 | CONST { 11 | type_name: String, 12 | expr: String, 13 | }, 14 | FN { 15 | return_type: String, 16 | args: Vec<(ArgName, ArgType)>, 17 | }, 18 | ALIAS { 19 | src_type: String, 20 | }, 21 | STRUCT { 22 | members: BTreeMap, 23 | }, 24 | ENUM { 25 | variants: BTreeMap, 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /src/arch/x64/ir/basic_block.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::x64::ir; 2 | 3 | pub struct BasicBlock { 4 | name: String, 5 | insts: Vec, 6 | } 7 | 8 | impl BasicBlock { 9 | pub fn new(name: &str) -> Self { 10 | Self { 11 | name: name.to_string(), 12 | insts: Vec::new(), 13 | } 14 | } 15 | 16 | pub fn push_inst(&mut self, inst: ir::Instruction) { 17 | self.insts.push(inst); 18 | } 19 | 20 | pub fn to_atandt(&self) -> String { 21 | let mut bb_str = format!("\"{}\":\n", self.name); 22 | 23 | for inst in self.insts.iter() { 24 | bb_str += &format!(" {}\n", inst.to_atandt()); 25 | } 26 | 27 | bb_str 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/arch/aarch64/ir/basic_block.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::aarch64::ir; 2 | 3 | pub struct BasicBlock { 4 | name: String, 5 | insts: Vec, 6 | } 7 | 8 | impl BasicBlock { 9 | pub fn new(name: &str) -> Self { 10 | Self { 11 | name: name.to_string(), 12 | insts: Vec::new(), 13 | } 14 | } 15 | 16 | pub fn push_inst(&mut self, inst: ir::Instruction) { 17 | self.insts.push(inst); 18 | } 19 | 20 | pub fn to_assembly(&self) -> String { 21 | let mut bb_str = format!("\"{}\":\n", self.name); 22 | 23 | for inst in self.insts.iter() { 24 | bb_str += &format!(" {}\n", inst.to_assembly()); 25 | } 26 | 27 | bb_str 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Peachili 2 | 3 | This programming language is inspired Golang strongly. 4 | 5 | ## usage 6 | 7 | ``` 8 | $ peachili compile # generate an assembly-file for X86_64 9 | $ peachili compile # generate an assembly-file for aarch64 10 | ``` 11 | 12 | ## [Documents](https://github.com/Drumato/peachili/blob/master/docs/main.md) 13 | 14 | ## Run all tests 15 | 16 | ### Unit tests 17 | 18 | ``` 19 | cargo test 20 | ``` 21 | 22 | ### Integration tests on x86_64 23 | 24 | ``` 25 | cargo build 26 | ./x64_test.sh 27 | ``` 28 | 29 | ### Integration tests on Arm64 30 | 31 | ``` 32 | cargo build 33 | ./aarch64_test.sh 34 | ``` 35 | 36 | ## Debug 37 | 38 | ``` 39 | # x64 40 | 41 | # aarch64 42 | qemu-aarch64-static -g a.out 43 | gdb-multiarch a.out 44 | target remote : 45 | ``` 46 | -------------------------------------------------------------------------------- /src/common/pass/parser/context.rs: -------------------------------------------------------------------------------- 1 | use crate::common::analyze_resource::ast; 2 | 3 | use std::collections::HashSet; 4 | use std::sync::{Arc, Mutex}; 5 | 6 | /// パース処理に必要な情報を集約する構造体 7 | pub struct Context { 8 | pub fn_arena: ast::FnArena, 9 | pub called_functions: HashSet, 10 | pub module_name: String, 11 | pub stmt_arena: ast::StmtArena, 12 | pub expr_arena: ast::ExprArena, 13 | } 14 | 15 | impl Default for Context { 16 | fn default() -> Self { 17 | Self { 18 | fn_arena: Arc::new(Mutex::new(Default::default())), 19 | called_functions: HashSet::new(), 20 | module_name: String::new(), 21 | stmt_arena: Arc::new(Mutex::new(Default::default())), 22 | expr_arena: Arc::new(Mutex::new(Default::default())), 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/common/position.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Display, Formatter, Result as FR}; 2 | 3 | /// ソースコード上の位置を保持する構造体 4 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone)] 5 | pub struct Position { 6 | // 行情報 7 | row: usize, 8 | // 列情報 9 | column: usize, 10 | } 11 | 12 | impl Default for Position { 13 | fn default() -> Self { 14 | Self { row: 0, column: 0 } 15 | } 16 | } 17 | 18 | impl Position { 19 | pub fn new(row: usize, column: usize) -> Self { 20 | Self { row, column } 21 | } 22 | /// 内部情報の取得 23 | pub fn get_info(&self) -> (usize, usize) { 24 | (self.row, self.column) 25 | } 26 | } 27 | 28 | impl Display for Position { 29 | fn fmt(&self, f: &mut Formatter<'_>) -> FR { 30 | let (r, c) = self.get_info(); 31 | write!(f, "({}, {})", r, c) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/common/analyze_resource/ast/statement.rs: -------------------------------------------------------------------------------- 1 | use crate::common::{ast::StatementNodeKind, position}; 2 | 3 | use id_arena::Id; 4 | 5 | pub type StNodeId = Id; 6 | 7 | /// 文ノード 8 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] 9 | pub struct StatementNode { 10 | k: StatementNodeKind, 11 | p: position::Position, 12 | } 13 | 14 | #[allow(dead_code)] 15 | impl StatementNode { 16 | pub fn new(k: StatementNodeKind, p: position::Position) -> Self { 17 | Self { k, p } 18 | } 19 | pub fn get_position(&self) -> position::Position { 20 | self.p 21 | } 22 | pub fn get_kind(&self) -> &StatementNodeKind { 23 | &self.k 24 | } 25 | pub fn is_ifret(&self) -> bool { 26 | match self.k { 27 | StatementNodeKind::IFRET { expr: _ } => true, 28 | _ => false, 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/common/three_address_code/value_kind.rs: -------------------------------------------------------------------------------- 1 | /// Valueの種類 2 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] 3 | pub enum ValueKind { 4 | INTLITERAL { value: i64 }, 5 | UINTLITERAL { value: u64 }, 6 | TEMP { number: usize }, 7 | ID { name: String }, 8 | BOOLEANLITERAL { truth: bool }, 9 | STRINGLITERAL { contents: String }, 10 | } 11 | 12 | impl ValueKind { 13 | pub fn dump(&self) -> String { 14 | match self { 15 | ValueKind::INTLITERAL { value } => value.to_string(), 16 | ValueKind::UINTLITERAL { value } => value.to_string(), 17 | ValueKind::BOOLEANLITERAL { truth } => truth.to_string(), 18 | ValueKind::STRINGLITERAL { contents } => contents.to_string(), 19 | 20 | ValueKind::TEMP { number } => format!("temp{}", number), 21 | ValueKind::ID { name } => name.to_string(), 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /aarch64_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | build_peachili_executable() { 3 | input="$1" 4 | ../../target/debug/peachili compile "$input" --target aarch64 5 | rustc_actual="$?" 6 | if [ $rustc_actual -ne 0 ]; then 7 | echo -e "\e[31mbuilding an executable binary failed!\e[m" 8 | exit 1 9 | fi 10 | } 11 | 12 | try() { 13 | expected="$1" 14 | input="$2" 15 | 16 | # テストファイルのコンパイル 17 | build_peachili_executable $input 18 | 19 | clang-10 asm.s --target=aarch64-linux-gnu -static 20 | ./a.out 21 | actual="$?" 22 | rm a.out 23 | 24 | if [ "$actual" = "$expected" ]; then 25 | echo -e "$input => \e[32m$actual\e[m" 26 | else 27 | echo -e "$input => \e[32m$expected\e[m expected, but got \e[31m$actual\e[m" 28 | exit 1 29 | fi 30 | } 31 | 32 | echo -e "start to test normal program...\n\n" 33 | 34 | cd examples/aarch64 35 | 36 | try 42 "intlit.go" 37 | try 9 "four_arith.go" 38 | try 9 "unary_minus.go" 39 | try 30 "declare_autovar.go" 40 | 41 | echo -e "\n\nOK" 42 | -------------------------------------------------------------------------------- /src/common/error/compile_error/tokenize_error.rs: -------------------------------------------------------------------------------- 1 | use fmt::Formatter; 2 | use std::fmt; 3 | 4 | use crate::common::error::CompileErrorKind; 5 | 6 | /// Tokenizerが発行するエラーの種類を列挙 7 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)] 8 | pub enum TokenizeErrorKind { 9 | /// 整数トークンが許容範囲外であった 10 | INTEGERLITERALOUTOFRANGE(String), 11 | 12 | /// これ以上トークナイズできない 13 | SOURCEISEMPTY, 14 | } 15 | 16 | impl CompileErrorKind for TokenizeErrorKind { 17 | fn category(&self) -> &'static str { 18 | "TokenizeError" 19 | } 20 | } 21 | 22 | impl fmt::Display for TokenizeErrorKind { 23 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 24 | let s = match self { 25 | TokenizeErrorKind::INTEGERLITERALOUTOFRANGE(number) => { 26 | format!("an int-literal `{}` out of range 64bit", number) 27 | } 28 | TokenizeErrorKind::SOURCEISEMPTY => "source is empty".to_string(), 29 | }; 30 | 31 | write!(f, "{}", s) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/common/error/bundle_error.rs: -------------------------------------------------------------------------------- 1 | use crate::common::error::CompileErrorKind; 2 | use fmt::Formatter; 3 | use std::fmt; 4 | 5 | /// Bundlerが発行するエラーを格納 6 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)] 7 | pub struct BundleError { 8 | /// エラーの種類 9 | kind: BundleErrorKind, 10 | } 11 | 12 | /// Bundlerが発行するエラーの種類を列挙 13 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)] 14 | pub enum BundleErrorKind { 15 | /// import しているファイルが存在しない 16 | NOTFOUNDSUCHAFILE { file_name: String }, 17 | } 18 | 19 | impl CompileErrorKind for BundleErrorKind { 20 | fn category(&self) -> &'static str { 21 | "BundleError" 22 | } 23 | } 24 | 25 | impl fmt::Display for BundleErrorKind { 26 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 27 | let s = match self { 28 | BundleErrorKind::NOTFOUNDSUCHAFILE { file_name } => { 29 | format!("not found such a file -> `{}`", file_name) 30 | } 31 | }; 32 | 33 | write!(f, "{}", s) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/common/error/compile_error.rs: -------------------------------------------------------------------------------- 1 | mod tokenize_error; 2 | 3 | pub use tokenize_error::*; 4 | 5 | use colored::*; 6 | 7 | use std::fmt; 8 | 9 | use crate::common::position::Position; 10 | 11 | /// Compilerが発行するエラーを格納 12 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)] 13 | pub struct CompileError { 14 | /// エラーの種類 15 | k: K, 16 | 17 | /// エラーの箇所 18 | p: Position, 19 | } 20 | 21 | impl CompileError { 22 | pub fn new(kind: K, position: Position) -> Self { 23 | Self { 24 | k: kind, 25 | p: position, 26 | } 27 | } 28 | /// 標準エラー出力にエラーを出力する 29 | pub fn output(&self) { 30 | eprintln!("{}{} : {}", self.k.category().red().bold(), self.p, self.k); 31 | } 32 | 33 | /// エラーの種類のを取得する 34 | pub fn get_kind(&self) -> &K { 35 | &self.k 36 | } 37 | } 38 | 39 | /// Compilerが発行するエラーの種類 40 | pub trait CompileErrorKind: fmt::Display { 41 | /// 具体的なエラーの分類 42 | fn category(&self) -> &'static str; 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Drumato 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/arch/aarch64/ir/function.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::aarch64::ir; 2 | 3 | pub struct Function { 4 | name: String, 5 | blocks: Vec, 6 | } 7 | 8 | impl Function { 9 | pub fn new(name: &str) -> Self { 10 | Self { 11 | name: name.to_string(), 12 | blocks: Vec::new(), 13 | } 14 | } 15 | 16 | pub fn get_name(&self) -> &String { 17 | &self.name 18 | } 19 | 20 | pub fn push_block(&mut self, name: &str) { 21 | self.blocks 22 | .push(ir::BasicBlock::new(&format!("{}_{}", self.name, name))); 23 | } 24 | 25 | pub fn add_inst_to_last_bb(&mut self, inst: ir::Instruction) { 26 | let last_bb = self.blocks.len() - 1; 27 | 28 | self.blocks[last_bb].push_inst(inst); 29 | } 30 | 31 | pub fn to_assembly(&self) -> String { 32 | let mut func_code = format!(".global \"{}\"\n", self.name); 33 | func_code += &format!("\"{}\":\n", self.name); 34 | 35 | for bb in self.blocks.iter() { 36 | func_code += &format!(" {}\n", bb.to_assembly()); 37 | } 38 | 39 | func_code 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/common/pass/backend.rs: -------------------------------------------------------------------------------- 1 | use crate::common::{analyze_resource as ar, option, pass, three_address_code as tac}; 2 | use crate::debug; 3 | use colored::*; 4 | 5 | use std::collections::BTreeMap; 6 | 7 | // 共通のバックエンドプロセス 8 | pub fn backend( 9 | fn_arena: ar::ast::FnArena, 10 | ast_root: ar::ast::ASTRoot, 11 | type_env: &BTreeMap>, 12 | target: option::Target, 13 | verbose_ir: bool, 14 | entry_point: String, 15 | ) -> ( 16 | tac::IRModule, 17 | BTreeMap, 18 | ) { 19 | let ir_module = pass::translate_ir(fn_arena, ast_root, type_env, target, entry_point); 20 | 21 | if verbose_ir { 22 | eprintln!("{}", "dump HIR to 'hir_dump'...".bold().blue()); 23 | debug::dump_hir(&ir_module); 24 | eprintln!("{}", "done!".bold().blue()); 25 | } 26 | 27 | // BasicBlockのない,ローカルなグラフを作成する 28 | let local_cfg = pass::build_local_cfg(&ir_module); 29 | 30 | if verbose_ir { 31 | eprintln!("{}", "dump CFG to 'local_cfg.dot' ...".bold().blue()); 32 | debug::dump_local_cfg("local_cfg.dot", &ir_module, &local_cfg); 33 | eprintln!("{}", "done!".bold().blue()); 34 | } 35 | 36 | (ir_module, local_cfg) 37 | } 38 | -------------------------------------------------------------------------------- /src/common/analyze_resource/cfg/local_graph.rs: -------------------------------------------------------------------------------- 1 | use crate::common::three_address_code as tac; 2 | use std::collections::{BTreeMap, BTreeSet}; 3 | 4 | /// BasicBlockを考慮しない,関数内のflat-graph 5 | pub struct LocalControlFlowGraph { 6 | /// あるノードから伸びる先行節 7 | pub predecessors: BTreeMap>, 8 | /// あるノードから伸びる後続節 9 | pub successors: BTreeMap>, 10 | } 11 | 12 | impl LocalControlFlowGraph { 13 | pub fn add_predecessor(&mut self, src: tac::CodeId, dst: tac::CodeId) { 14 | self.predecessors.entry(src).or_insert_with(BTreeSet::new); 15 | self.predecessors.get_mut(&src).unwrap().insert(dst); 16 | } 17 | 18 | pub fn add_successor(&mut self, src: tac::CodeId, dst: tac::CodeId) { 19 | self.successors.entry(src).or_insert_with(BTreeSet::new); 20 | self.successors.get_mut(&src).unwrap().insert(dst); 21 | } 22 | 23 | pub fn get_successors(&self, code_id: &tac::CodeId) -> &BTreeSet { 24 | self.predecessors.get(code_id).unwrap() 25 | } 26 | } 27 | 28 | impl Default for LocalControlFlowGraph { 29 | fn default() -> Self { 30 | Self { 31 | predecessors: BTreeMap::new(), 32 | successors: BTreeMap::new(), 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/common/three_address_code/function.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use id_arena::{Arena, Id}; 4 | 5 | use crate::common::peachili_type; 6 | use crate::common::three_address_code::*; 7 | 8 | pub type IRFunctionId = Id; 9 | pub type CodeArena = Arc>>; 10 | pub type ValueArena = Arc>>; 11 | 12 | /// 三番地コードにおける関数表現 13 | #[derive(Debug, Clone)] 14 | pub struct IRFunction { 15 | pub name: String, 16 | pub fn_ty: peachili_type::Type, 17 | pub codes: Vec, 18 | 19 | pub value_allocator: ValueArena, 20 | pub code_allocator: CodeArena, 21 | pub args: Vec, 22 | } 23 | 24 | #[allow(dead_code)] 25 | impl IRFunction { 26 | pub fn get_value(&self, v_id: ValueId) -> Value { 27 | self.value_allocator 28 | .lock() 29 | .unwrap() 30 | .get(v_id) 31 | .unwrap() 32 | .clone() 33 | } 34 | 35 | pub fn get_code(&self, c_id: CodeId) -> Code { 36 | self.code_allocator 37 | .lock() 38 | .unwrap() 39 | .get(c_id) 40 | .unwrap() 41 | .clone() 42 | } 43 | 44 | pub fn get_called_name(&self, v_id: ValueId) -> String { 45 | self.get_value(v_id).copy_contents() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/debug/dump_ir.rs: -------------------------------------------------------------------------------- 1 | use crate::common::file_util; 2 | use crate::common::three_address_code as tac; 3 | 4 | /// 三番地コードの関係をDOT言語で記述し,ファイルに書き出す 5 | pub fn dump_hir(ir_module: &tac::IRModule) { 6 | let mut dumper = IRDumper { 7 | output: Default::default(), 8 | filename: "hir_dump".to_string(), 9 | }; 10 | 11 | dumper.construct_hir_program(ir_module); 12 | 13 | dumper.write_hir_program(); 14 | } 15 | 16 | struct IRDumper { 17 | output: String, 18 | filename: String, 19 | } 20 | 21 | impl IRDumper { 22 | fn construct_hir_program(&mut self, ir_module: &tac::IRModule) { 23 | self.output += "Three Address Code Dump:\n"; 24 | 25 | for fn_id in ir_module.funcs.iter() { 26 | let func = ir_module.fn_allocator.get(*fn_id).unwrap(); 27 | self.dump_function(func); 28 | } 29 | } 30 | 31 | fn dump_function(&mut self, func: &tac::IRFunction) { 32 | self.output += &format!(" {}:\n", func.name); 33 | 34 | for code_id in func.codes.iter() { 35 | let code = func.get_code(*code_id); 36 | self.output += &format!(" {}\n", code.dump(func.value_allocator.clone())); 37 | } 38 | } 39 | 40 | fn write_hir_program(self) { 41 | file_util::write_program_into(&self.filename, self.output) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/common/analyze_resource/ast/statement_kind.rs: -------------------------------------------------------------------------------- 1 | use crate::common::ast::{ExNodeId, StNodeId}; 2 | use std::collections::BTreeMap; 3 | 4 | /// 文ノードの種類 5 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] 6 | pub enum StatementNodeKind { 7 | /// "return" expression `;` 8 | RETURN { expr: ExNodeId }, 9 | /// expression `;` 10 | EXPR { expr: ExNodeId }, 11 | /// "ifret" expression `;` 12 | IFRET { expr: ExNodeId }, 13 | /// "declare" identifier type `;` 14 | DECLARE { 15 | ident_name: String, 16 | type_name: String, 17 | }, 18 | /// "countup" identifier "begin" expression "exclude" expression block `;` 19 | COUNTUP { 20 | ident_name: String, 21 | begin_ex: ExNodeId, 22 | endpoint_ex: ExNodeId, 23 | body: Vec, 24 | }, 25 | /// "asm" block `; 26 | ASM { stmts: Vec }, 27 | /// "varinit" identifier type `=` expression `;` 28 | VARINIT { 29 | ident_name: String, 30 | type_name: String, 31 | expr: ExNodeId, 32 | }, 33 | /// "const" identifier type `=` expression `;` 34 | CONST { 35 | ident_name: String, 36 | type_name: String, 37 | expr: ExNodeId, 38 | }, 39 | MATCH { 40 | expr: ExNodeId, 41 | arms: BTreeMap>, 42 | }, 43 | } 44 | -------------------------------------------------------------------------------- /src/common/option.rs: -------------------------------------------------------------------------------- 1 | use clap::ArgMatches; 2 | 3 | #[derive(Clone)] 4 | pub struct BuildOption { 5 | pub matches: ArgMatches, 6 | pub target: Target, 7 | } 8 | 9 | impl BuildOption { 10 | pub fn new(matches: ArgMatches) -> Self { 11 | Self { 12 | matches, 13 | target: Target::X86_64, 14 | } 15 | } 16 | 17 | pub fn get_source(&self) -> String { 18 | match self.matches.subcommand() { 19 | ("build", Some(build_m)) => match build_m.value_of("source") { 20 | Some(s) => s.to_string(), 21 | None => panic!("source file must be specified"), 22 | }, 23 | ("compile", Some(compile_m)) => match compile_m.value_of("source") { 24 | Some(s) => s.to_string(), 25 | None => panic!("source file must be specified"), 26 | }, 27 | _ => panic!("source file must be specified"), 28 | } 29 | } 30 | } 31 | 32 | #[derive(Copy, Clone)] 33 | pub enum Target { 34 | X86_64, 35 | AARCH64, 36 | } 37 | 38 | impl Target { 39 | pub fn new(target_str: &str) -> Self { 40 | match target_str { 41 | "x86_64" => Target::X86_64, 42 | "aarch64" => Target::AARCH64, 43 | _ => panic!("unsupported target -> {}", target_str), 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/common/analyze_resource/ast/expression_kind.rs: -------------------------------------------------------------------------------- 1 | use crate::common::ast::{ExNodeId, StNodeId}; 2 | 3 | /// 式ノードの種類 4 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] 5 | pub enum ExpressionNodeKind { 6 | /// IF式ノード 7 | IF { 8 | cond_ex: ExNodeId, 9 | body: Vec, 10 | alter: Option>, 11 | }, 12 | 13 | /// 加算ノード 14 | ADD { lhs: ExNodeId, rhs: ExNodeId }, 15 | /// 減算ノード 16 | SUB { lhs: ExNodeId, rhs: ExNodeId }, 17 | /// 乗算ノード 18 | MUL { lhs: ExNodeId, rhs: ExNodeId }, 19 | /// 除算ノード 20 | DIV { lhs: ExNodeId, rhs: ExNodeId }, 21 | /// 代入ノード 22 | ASSIGN { lhs: ExNodeId, rhs: ExNodeId }, 23 | 24 | /// 符号反転 25 | NEG { value: ExNodeId }, 26 | /// アドレッシング 27 | ADDRESSOF { value: ExNodeId }, 28 | /// デリファレンス 29 | DEREFERENCE { value: ExNodeId }, 30 | /// メンバアクセス 31 | MEMBER { id: ExNodeId, member: String }, 32 | 33 | /// 整数ノード 34 | INTEGER { value: i64 }, 35 | /// 非符号付き整数ノード 36 | UINTEGER { value: u64 }, 37 | /// 真偽値リテラル 38 | BOOLEAN { truth: bool }, 39 | /// 文字列リテラル 40 | STRING { contents: String }, 41 | /// 識別子ノード 42 | /// std::os::exit_with() みたいなのを["std", "os", "exit_with"] で保持 43 | IDENTIFIER { names: Vec }, 44 | /// 呼び出し式ノード 45 | CALL { 46 | names: Vec, 47 | args: Vec, 48 | }, 49 | } 50 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate asmpeach; 2 | extern crate clap; 3 | extern crate id_arena; 4 | extern crate pld; 5 | extern crate yaml_rust; 6 | 7 | use arch::{aarch64, x64}; 8 | use common::option; 9 | use id_arena::Arena; 10 | use std::sync::{Arc, Mutex}; 11 | 12 | mod arch; 13 | mod bundler; 14 | mod common; 15 | mod debug; 16 | mod setup; 17 | 18 | #[macro_use] 19 | extern crate lazy_static; 20 | 21 | fn main() -> Result<(), Box> { 22 | match setup::BUILD_OPTION.matches.subcommand() { 23 | ("build", Some(_build_m)) => {} 24 | ("compile", Some(_compile_m)) => {} 25 | _ => { 26 | eprintln!("please specify a subcommand. see --help."); 27 | std::process::exit(1); 28 | } 29 | } 30 | 31 | let module_arena: common::module::ModuleArena = Arc::new(Mutex::new(Arena::new())); 32 | let source = setup::BUILD_OPTION.get_source(); 33 | let main_module = bundler::resolve_main(module_arena.clone(), source); 34 | 35 | // ****************** 36 | // * Compiler * 37 | // ****************** 38 | 39 | match setup::BUILD_OPTION.target { 40 | option::Target::X86_64 => { 41 | x64::main(module_arena, main_module, &setup::BUILD_OPTION.matches)? 42 | } 43 | option::Target::AARCH64 => { 44 | aarch64::main(module_arena, main_module, &setup::BUILD_OPTION.matches)? 45 | } 46 | } 47 | 48 | Ok(()) 49 | } 50 | -------------------------------------------------------------------------------- /docs/internals/tokenizer.md: -------------------------------------------------------------------------------- 1 | # Tokenizer 2 | 3 | - 入力: ソースプログラム `String` 4 | - 出力: トークン列 `Vec` 5 | 6 | ## 概要 7 | 8 | ```rust 9 | fn tokenize(source: String) -> Vec { 10 | // トークン列の初期化 11 | let tokens : Vec = Default::default(); 12 | 13 | // トークンやエラーの位置用に定義 14 | // 各トークンはこの情報を自身に保有する 15 | let mut row: usize = 1; 16 | let mut column: usize = 1; 17 | 18 | loop { 19 | // 一つのトークンを読み込む 20 | let te : Result = scan(&mut source, &mut row, &mut column); 21 | 22 | match te { 23 | Ok(tok) => { 24 | // 空白やカンマなど,構文解析アルゴリズム上で意味を持たないトークンは追加しない 25 | if t.should_ignore() { 26 | continue; 27 | } 28 | tokens.push(t); 29 | }, 30 | Err(e) => { 31 | match e.kind { 32 | // これ以上トークナイズできない -> EOFを挿入し終了 33 | TokenizeError::EMPTY => { 34 | tokens.push( 35 | Token { 36 | kind: EOF, 37 | pos: Default::default() 38 | } 39 | ); 40 | break; 41 | } 42 | // 他のエラーはコンパイルエラーとし,出力してプロセスを落とす 43 | err => err.output_and_panic(), 44 | } 45 | } 46 | } 47 | } 48 | 49 | tokens 50 | } 51 | ``` -------------------------------------------------------------------------------- /src/common/analyze_resource/tld/_tld.rs: -------------------------------------------------------------------------------- 1 | use crate::common::ast; 2 | use crate::common::tld::tld_kind; 3 | 4 | /// 宣言 5 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] 6 | pub struct TopLevelDecl { 7 | pub kind: tld_kind::TLDKind, 8 | } 9 | 10 | impl TopLevelDecl { 11 | pub fn new(k: tld_kind::TLDKind) -> Self { 12 | Self { kind: k } 13 | } 14 | pub fn new_alias(src_type: &str) -> Self { 15 | Self::new(tld_kind::TLDKind::ALIAS { 16 | src_type: src_type.to_string(), 17 | }) 18 | } 19 | pub fn new_const(type_name: &str, expr: String) -> Self { 20 | Self::new(tld_kind::TLDKind::CONST { 21 | type_name: type_name.to_string(), 22 | expr, 23 | }) 24 | } 25 | 26 | pub fn new_function_from_ast(fn_ty: ast::FunctionTypeDef) -> Self { 27 | Self::new(tld_kind::TLDKind::FN { 28 | return_type: fn_ty.return_type, 29 | args: fn_ty.args, 30 | }) 31 | } 32 | 33 | pub fn new_struct_from_ast(st_ty: ast::StructDef) -> Self { 34 | Self::new(tld_kind::TLDKind::STRUCT { 35 | members: st_ty.members, 36 | }) 37 | } 38 | 39 | pub fn new_enum(en_ty: ast::EnumDef) -> Self { 40 | Self::new(tld_kind::TLDKind::ENUM { 41 | variants: en_ty 42 | .variants 43 | .iter() 44 | .map(|(name, variant)| (name.to_string(), variant.tag)) 45 | .collect(), 46 | }) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /x64_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | build_peachili_executable() { 3 | input="$1" 4 | ../../target/debug/peachili compile "$input" 5 | rustc_actual="$?" 6 | if [ $rustc_actual -ne 0 ]; then 7 | echo -e "\e[31mbuilding an executable binary failed!\e[m" 8 | exit 1 9 | fi 10 | } 11 | 12 | try() { 13 | expected="$1" 14 | input="$2" 15 | extra_args="$3" 16 | 17 | # テストファイルのコンパイル 18 | build_peachili_executable $input 19 | gcc asm.s $extra_args 20 | ./a.out 21 | actual="$?" 22 | rm asm.s a.out 23 | 24 | if [ "$actual" = "$expected" ]; then 25 | echo -e "$input => \e[32m$actual\e[m" 26 | else 27 | echo -e "$input => \e[32m$expected\e[m expected, but got \e[31m$actual\e[m" 28 | exit 1 29 | fi 30 | } 31 | 32 | echo -e "start to test normal program...\n\n" 33 | 34 | cd examples/x64 35 | 36 | # try 0 "empty_main.go" 37 | try 0 "intlit.go" 38 | try 9 "four_arith.go" 39 | try 9 "unary_minus.go" 40 | try 0 "unary_plus.go" 41 | try 30 "declare_autovar.go" 42 | try 9 "declare_twovar.go" 43 | # try 9 "countup.go" 44 | try 30 "with_argument.go" 45 | try 30 "without_argument.go" 46 | try 3 "exit.go" 47 | try 15 "boolean_1.go" 48 | try 30 "boolean_2.go" 49 | try 30 "type_alias.go" 50 | try 1 "unsigned_int.go" 51 | try 30 "varinit.go" 52 | try 4 "pointer.go" 53 | try 4 "six_times_deref.go" 54 | try 4 "pointer2.go" 55 | try 4 "six_pointer.go" 56 | try 45 "simple_struct.go" 57 | try 1 "if_expression.go" 58 | try 0 "hello_world.go" "-static" 59 | try 1 "if_expression.go" 60 | try 30 "global_const.go" 61 | 62 | echo -e "\n\nOK" 63 | -------------------------------------------------------------------------------- /error_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | build_and_test_peachili_executable() { 3 | input="$1" 4 | ../target/debug/peachili "$input" -L 5 | rustc_actual="$?" 6 | if [ $rustc_actual -ne 1 ]; then 7 | 8 | echo -e "$input => \e[31mcompiler must detect with one or more errors, but not.\e[m" 9 | exit 1 10 | else 11 | echo -e "$input => \e[32mpassed\e[m" 12 | fi 13 | } 14 | 15 | echo -e "start to test invalid program...\n\n" 16 | 17 | cd failures 18 | 19 | build_and_test_peachili_executable "invalid_integer_literal.go" 20 | build_and_test_peachili_executable "if_expr1.go" 21 | build_and_test_peachili_executable "if_expr2.go" 22 | build_and_test_peachili_executable "if_expr_stmt1.go" 23 | build_and_test_peachili_executable "if_expr_stmt2.go" 24 | build_and_test_peachili_executable "invalid_assignment.go" 25 | build_and_test_peachili_executable "add_int64_and_boolean.go" 26 | build_and_test_peachili_executable "uint_and_sint.go" 27 | build_and_test_peachili_executable "assignment_to_constant.go" 28 | build_and_test_peachili_executable "invalid_arg_types.go" 29 | build_and_test_peachili_executable "invalid_arg_number.go" 30 | build_and_test_peachili_executable "minus_to_unsigned.go" 31 | build_and_test_peachili_executable "return_in_noreturn_func.go" 32 | build_and_test_peachili_executable "not_found_main.go" 33 | build_and_test_peachili_executable "invalid_type_main.go" 34 | build_and_test_peachili_executable "return_local_pointer.go" 35 | build_and_test_peachili_executable "member_with_not_struct.go" 36 | build_and_test_peachili_executable "invalid_member.go" 37 | 38 | echo -e "\n\nOK" -------------------------------------------------------------------------------- /src/common/analyze_resource/ast/function.rs: -------------------------------------------------------------------------------- 1 | use crate::common::{ 2 | ast::{ExpressionNode, StNodeId, StatementNode}, 3 | position, 4 | }; 5 | use id_arena::{Arena, Id}; 6 | use std::sync::{Arc, Mutex}; 7 | 8 | pub type FnId = Id; 9 | pub type StmtArena = Arc>>; 10 | pub type ExprArena = Arc>>; 11 | pub type FnArena = Arc>>; 12 | 13 | /// 関数 14 | #[derive(Debug, Clone)] 15 | pub struct Function { 16 | pub name: String, 17 | pub stmts: Vec, 18 | 19 | pub pos: position::Position, 20 | 21 | pub module_name: String, 22 | 23 | pub fn_type: FunctionTypeDef, 24 | 25 | // アロケータ 26 | pub stmt_arena: StmtArena, 27 | pub expr_arena: ExprArena, 28 | } 29 | 30 | impl Function { 31 | pub fn full_path(&self) -> String { 32 | if self.module_name.is_empty() { 33 | return self.name.to_string(); 34 | } 35 | 36 | format!("{}::{}", self.module_name, self.name) 37 | } 38 | 39 | pub fn copy_return_type(&self) -> String { 40 | self.fn_type.return_type.clone() 41 | } 42 | 43 | pub fn get_parameters(&self) -> &Vec<(String, String)> { 44 | &self.fn_type.args 45 | } 46 | } 47 | 48 | #[derive(Debug, Clone)] 49 | pub struct FunctionTypeDef { 50 | /// return type of the function 51 | pub return_type: String, 52 | 53 | /// arg_name -> arg_type 54 | pub args: Vec<(String, String)>, 55 | } 56 | 57 | impl FunctionTypeDef { 58 | pub fn new(return_type: String, args: Vec<(String, String)>) -> Self { 59 | Self { return_type, args } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/arch/aarch64/build.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::aarch64; 2 | use crate::common; 3 | use crate::setup; 4 | 5 | /// aarch64アーキテクチャ向けのビルドルーチン 6 | pub fn main( 7 | module_arena: common::module::ModuleArena, 8 | main_module_id: common::module::ModuleId, 9 | matches: &clap::ArgMatches, 10 | ) -> Result<(), Box> { 11 | match matches.subcommand() { 12 | ("compile", Some(compile_m)) => { 13 | let aarch64_module = aarch64::compile_main( 14 | module_arena, 15 | main_module_id, 16 | compile_m.is_present("verbose-hir"), 17 | compile_m.is_present("debug"), 18 | String::new(), 19 | ); 20 | 21 | common::file_util::write_program_into("asm.s", aarch64_module.to_assembly()); 22 | } 23 | _ => eprintln!("please specify a subcommand. see --help."), 24 | } 25 | Ok(()) 26 | } 27 | 28 | /// aarch64用コンパイラのメインルーチン 29 | /// 機械独立なパスを呼び出した後aarch64依存のパスを処理する. 30 | pub fn compile_main( 31 | module_arena: common::module::ModuleArena, 32 | main_module_id: common::module::ModuleId, 33 | verbose_ir: bool, 34 | debug: bool, 35 | startup: String, 36 | ) -> aarch64::ir::Module { 37 | let (fn_arena, ast_root, type_env, stack_frame) = 38 | common::pass::frontend(module_arena, main_module_id, debug); 39 | let (ir_module, _local_cfg) = common::pass::backend( 40 | fn_arena, 41 | ast_root, 42 | &type_env, 43 | setup::BUILD_OPTION.target, 44 | verbose_ir, 45 | startup, 46 | ); 47 | 48 | aarch64::pass::codegen_main(ir_module, stack_frame) 49 | } 50 | -------------------------------------------------------------------------------- /src/common/pass/tld_collector/main.rs: -------------------------------------------------------------------------------- 1 | use crate::common::ast; 2 | use crate::common::tld; 3 | use std::collections::BTreeMap; 4 | 5 | /// TLD解析のトップレベルルーチン 6 | pub fn main( 7 | fn_arena: ast::FnArena, 8 | full_ast: &ast::ASTRoot, 9 | ) -> BTreeMap { 10 | let mut tld_map: BTreeMap = BTreeMap::new(); 11 | 12 | for (alias_name, alias_type) in full_ast.alias.iter() { 13 | tld_map.insert( 14 | alias_name.to_string(), 15 | tld::TopLevelDecl::new_alias(alias_type), 16 | ); 17 | } 18 | 19 | for (type_name, struct_def) in full_ast.typedefs.iter() { 20 | tld_map.insert( 21 | type_name.to_string(), 22 | tld::TopLevelDecl::new_struct_from_ast(struct_def.clone()), 23 | ); 24 | } 25 | 26 | for (const_name, (const_type_name, const_expr)) in full_ast.constants.iter() { 27 | tld_map.insert( 28 | const_name.to_string(), 29 | tld::TopLevelDecl::new_const(const_type_name, const_expr.to_string()), 30 | ); 31 | } 32 | 33 | for (enum_name, enum_decl) in full_ast.enum_decls.iter() { 34 | tld_map.insert( 35 | enum_name.to_string(), 36 | tld::TopLevelDecl::new_enum(enum_decl.clone()), 37 | ); 38 | } 39 | 40 | for fn_id in full_ast.funcs.iter() { 41 | let ast_function = fn_arena.lock().unwrap().get(*fn_id).unwrap().clone(); 42 | 43 | tld_map.insert( 44 | ast_function.name.to_string(), 45 | tld::TopLevelDecl::new_function_from_ast(ast_function.fn_type), 46 | ); 47 | } 48 | 49 | tld_map 50 | } 51 | -------------------------------------------------------------------------------- /src/common/pass/analyzer/alloc_frame.rs: -------------------------------------------------------------------------------- 1 | use crate::common::{frame_object, peachili_type, tld}; 2 | use std::collections::BTreeMap; 3 | 4 | pub fn allocate_stack_frame( 5 | _tld_map: &BTreeMap, 6 | type_env: &BTreeMap>, 7 | ) -> frame_object::StackFrame { 8 | let mut stack_frame = BTreeMap::new(); 9 | 10 | for (scope_name, func_env) in type_env { 11 | let mut frame_in_func = BTreeMap::new(); 12 | 13 | let mut total_offset_in_func = 0; 14 | // 先に関数以外をすべて回したあと,関数を回す 15 | // すべてのローカル変数のサイズを合計しないといけないため 16 | for (entry_name, entry) in func_env { 17 | if entry.is_function() { 18 | continue; 19 | } 20 | total_offset_in_func += entry.size; 21 | frame_in_func.insert( 22 | entry_name.to_string(), 23 | frame_object::FrameObject { 24 | offset: total_offset_in_func, 25 | }, 26 | ); 27 | } 28 | 29 | // 先に関数以外をすべて回したあと,関数を回す 30 | // すべてのローカル変数のサイズを合計しないといけないため 31 | for (entry_name, entry) in func_env { 32 | if !entry.is_function() { 33 | continue; 34 | } 35 | total_offset_in_func += entry.size; 36 | frame_in_func.insert( 37 | entry_name.to_string(), 38 | frame_object::FrameObject { 39 | offset: total_offset_in_func, 40 | }, 41 | ); 42 | } 43 | 44 | stack_frame.insert(scope_name.clone(), frame_in_func); 45 | } 46 | 47 | stack_frame 48 | } 49 | -------------------------------------------------------------------------------- /src/arch/x64/ir/function.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::arch::x64::ir; 4 | 5 | type StrHash = u64; 6 | 7 | pub struct Function { 8 | name: String, 9 | blocks: Vec, 10 | strings: HashMap, 11 | } 12 | 13 | impl Function { 14 | pub fn new(name: &str) -> Self { 15 | Self { 16 | name: name.to_string(), 17 | blocks: Vec::new(), 18 | strings: HashMap::new(), 19 | } 20 | } 21 | 22 | pub fn get_name(&self) -> &String { 23 | &self.name 24 | } 25 | 26 | pub fn push_block(&mut self, name: &str) { 27 | self.blocks 28 | .push(ir::BasicBlock::new(&format!(".L{}_{}", self.name, name))); 29 | } 30 | 31 | pub fn add_inst_to_last_bb(&mut self, inst: ir::Instruction) { 32 | let last_bb = self.blocks.len() - 1; 33 | 34 | self.blocks[last_bb].push_inst(inst); 35 | } 36 | pub fn push_string(&mut self, contents: String, hash: StrHash) { 37 | self.strings.entry(contents).or_insert(hash); 38 | } 39 | 40 | pub fn to_atandt(&self) -> String { 41 | let mut func_code = format!(".global \"{}\"\n", self.name); 42 | func_code += &format!("\"{}\":\n", self.name); 43 | 44 | for bb in self.blocks.iter() { 45 | func_code += &format!(" {}\n", bb.to_atandt()); 46 | } 47 | 48 | func_code += " .section .rodata\n"; 49 | 50 | for (contents, hash) in self.strings.iter() { 51 | func_code += &format!(".LS{}:\n", hash); 52 | func_code += &format!(" .string \"{}\"\n", contents); 53 | } 54 | func_code += " .text\n"; 55 | 56 | func_code 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/arch/x64/ir/inst_kind.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::x64::ir; 2 | 3 | pub enum InstKind { 4 | /// add[d/l/q] src, dst 5 | ADD { 6 | operand_size: ir::OperandSize, 7 | src: ir::Operand, 8 | dst: ir::Operand, 9 | }, 10 | /// sub[d/l/q] src, dst 11 | SUB { 12 | operand_size: ir::OperandSize, 13 | src: ir::Operand, 14 | dst: ir::Operand, 15 | }, 16 | /// imul[d/l/q] src, dst 17 | IMUL { 18 | operand_size: ir::OperandSize, 19 | src: ir::Operand, 20 | dst: ir::Operand, 21 | }, 22 | /// idiv[d/l/q] src, dst 23 | IDIV { 24 | operand_size: ir::OperandSize, 25 | value: ir::Operand, 26 | }, 27 | /// mov[d/l/q] src, dst 28 | MOV { 29 | operand_size: ir::OperandSize, 30 | src: ir::Operand, 31 | dst: ir::Operand, 32 | }, 33 | /// cmp[d/l/q] src, dst 34 | CMP { 35 | operand_size: ir::OperandSize, 36 | src: ir::Operand, 37 | dst: ir::Operand, 38 | }, 39 | /// lea[d/l/q] src, dst 40 | LEA { 41 | operand_size: ir::OperandSize, 42 | src: ir::Operand, 43 | dst: ir::Operand, 44 | }, 45 | 46 | INLINEASM { 47 | contents: String, 48 | }, 49 | CALL { 50 | name: String, 51 | }, 52 | NEG { 53 | operand_size: ir::OperandSize, 54 | value: ir::Operand, 55 | }, 56 | PUSH { 57 | operand_size: ir::OperandSize, 58 | value: ir::Operand, 59 | }, 60 | POP { 61 | operand_size: ir::OperandSize, 62 | value: ir::Operand, 63 | }, 64 | RET, 65 | CLTD, 66 | JMP { 67 | label: String, 68 | }, 69 | JE { 70 | label: String, 71 | }, 72 | } 73 | -------------------------------------------------------------------------------- /docs/internals/parser.md: -------------------------------------------------------------------------------- 1 | # Parser 2 | 3 | - 入力: トークン列 `Vec` 4 | - 出力: モジュールレベルの構造体 `ASTRoot` 5 | 6 | ```rust 7 | pub struct ASTRoot { 8 | /// 関数列 9 | pub funcs: Vec, 10 | /// 構造体定義の集合 11 | pub typedefs: BTreeMap, 12 | /// 型エイリアスの集合 13 | pub alias: BTreeMap, 14 | } 15 | ``` 16 | 17 | ## 概要 18 | 19 | ほぼすべての関数が 「トークンを受け取ってパース, パース後の **残りのトークンを返す**」 という構造になっている. 20 | これはTopLevelDeclarationに限らない. 21 | 構文については [syntax.md](../syntax.md) を参照. 22 | 23 | ```rust 24 | pub fn main( 25 | tokens: Vec, 26 | module_name: String 27 | ) -> ASTRoot { 28 | // ASTRoot構造体の定義 29 | let mut ast_root: ASTRoot = Default::default(); 30 | 31 | // program -> toplevel* 32 | loop { 33 | let t = parser_util::head(&tokens); 34 | 35 | match t.get_kind() { 36 | // 関数定義のパース 37 | TokenKind::FUNC => { 38 | let (fn_id, rest_tokens) = func_def(module_name, tokens); 39 | tokens = rest_tokens; 40 | ast_root.funcs.push(fn_id); 41 | } 42 | // 構造体定義のパース 43 | TokenKind::STRUCT => { 44 | let (type_name, struct_def, rest_tokens) = struct_def(tokens); 45 | tokens = rest_tokens; 46 | ast_root.typedefs.insert(format!("{}::{}",module_name, type_name), struct_def); 47 | } 48 | // 型エイリアスのパース 49 | TokenKind::PUBTYPE => { 50 | let (alias_name, src_name, rest_tokens) = type_alias(tokens); 51 | tokens = rest_tokens; 52 | ast_root.alias.insert(format!("{}::{}",module_name, alias_name), src_name); 53 | } 54 | _ => break, 55 | } 56 | } 57 | 58 | ast_root 59 | } 60 | ``` -------------------------------------------------------------------------------- /src/arch/aarch64/ir/inst_kind.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::aarch64::ir; 2 | 3 | #[allow(dead_code)] 4 | pub enum InstKind { 5 | /// Add 6 | ADD { 7 | operand_size: ir::OperandSize, 8 | dst: ir::Operand, 9 | lop: ir::Operand, 10 | rop: ir::Operand, 11 | }, 12 | /// Sub 13 | SUB { 14 | operand_size: ir::OperandSize, 15 | dst: ir::Operand, 16 | lop: ir::Operand, 17 | rop: ir::Operand, 18 | }, 19 | /// Mul 20 | MUL { 21 | operand_size: ir::OperandSize, 22 | dst: ir::Operand, 23 | lop: ir::Operand, 24 | rop: ir::Operand, 25 | }, 26 | /// NEG 27 | NEG { 28 | operand_size: ir::OperandSize, 29 | dst: ir::Operand, 30 | value: ir::Operand, 31 | }, 32 | /// Move 33 | MOV { 34 | operand_size: ir::OperandSize, 35 | dst: ir::Operand, 36 | src: ir::Operand, 37 | }, 38 | /// Store 39 | STR { 40 | operand_size: ir::OperandSize, 41 | dst: ir::Operand, 42 | src: ir::Operand, 43 | }, 44 | /// Store Register Pair 45 | STP { 46 | operand_size: ir::OperandSize, 47 | reg1: ir::Register, 48 | reg2: ir::Register, 49 | dst: ir::Operand, 50 | }, 51 | /// Load To Register 52 | LDR { 53 | operand_size: ir::OperandSize, 54 | dst: ir::Operand, 55 | src: ir::Operand, 56 | }, 57 | /// Load Register Pair 58 | LDP { 59 | operand_size: ir::OperandSize, 60 | reg1: ir::Register, 61 | reg2: ir::Register, 62 | src: ir::Operand, 63 | }, 64 | 65 | /// Branch with Link. 66 | BL { name: String }, 67 | 68 | /// Inline Assembly 69 | INLINEASM { contents: String }, 70 | 71 | /// Return Value 72 | RET, 73 | } 74 | -------------------------------------------------------------------------------- /docs/syntax.md: -------------------------------------------------------------------------------- 1 | # Syntax Specification 2 | 3 | ## EBNF 4 | 5 | ``` 6 | Start Symbol: program 7 | Terminal Symbol: string_literal/integer_literal/identifier/uint-literal 8 | 9 | program -> toplevel* 10 | 11 | toplevel -> func_def | struct_def | type_def 12 | 13 | func_def -> "func" identifier arg_list type block 14 | struct_def -> "struct" identifier member_block 15 | type_def -> "pubtype" identifier `=` type `;` 16 | member_block -> `{` (identifier type)* `}` 17 | 18 | // Statement Rewrite Rule 19 | statement -> return_st | ifret_st| declare_st | countup_st | asm_st | varinit_st| const_st 20 | return_st -> "return" expression `;` 21 | ifret_st -> "ifret" expression `;` 22 | declare -> "declare" identifier type `;` 23 | countup_st -> "countup" identifier "begin" expression "exclude" expression block `;` 24 | asm_st -> "asm" block `;` 25 | varinit_st -> "varinit" identifier type `=` expression `;` 26 | const_st -> "const" identifier type `=` expression `;` 27 | 28 | 29 | // Expression Rewrite Rule 30 | expr -> if_expr | addition 31 | if_expf -> "if" paren_expr block ("else" block)? 32 | addition -> multiplication (addition_op multiplication)* 33 | multiplication -> prefix (multiplication_op prefix)* 34 | prefix -> prefix_op* postfix 35 | postfix -> primary (postfix_op postfix)* 36 | primary -> "true" | "false" | integer_literal | string_literal | identifier-path | uint-literal | paren_expr 37 | paren_expr -> `(` expression `)` 38 | 39 | // Operators 40 | addition_op -> `+` | `-` 41 | multiplication_op -> `*` | `/` 42 | prefix_op -> `+` | `-` | `&` | `*` 43 | postfix_op -> `.` 44 | 45 | // etc 46 | type -> "Int64" 47 | | "Uint64" 48 | | "Boolean" 49 | | "Noreturn" 50 | | "ConstStr" 51 | | identifier_path 52 | identifier_path -> identifier (`::` identifier)* 53 | block -> `{` statement* `}` 54 | ``` 55 | -------------------------------------------------------------------------------- /src/common/analyze_resource/ast/ast_root.rs: -------------------------------------------------------------------------------- 1 | use crate::common::ast::function; 2 | use std::collections::{BTreeMap, HashSet}; 3 | 4 | /// Root 5 | #[derive(Debug, Clone)] 6 | pub struct ASTRoot { 7 | pub funcs: Vec, 8 | pub typedefs: BTreeMap, 9 | pub alias: BTreeMap, 10 | pub called_functions: HashSet, 11 | 12 | /// 定数名 => (型名, 代入されている式) 13 | pub constants: BTreeMap, 14 | pub enum_decls: BTreeMap, 15 | } 16 | 17 | impl Default for ASTRoot { 18 | fn default() -> Self { 19 | Self { 20 | funcs: Vec::new(), 21 | alias: BTreeMap::new(), 22 | typedefs: BTreeMap::new(), 23 | called_functions: HashSet::new(), 24 | constants: BTreeMap::new(), 25 | enum_decls: BTreeMap::new(), 26 | } 27 | } 28 | } 29 | 30 | impl ASTRoot { 31 | /// 別モジュールのASTRootを吸収する 32 | pub fn absorb(&mut self, mut target: Self) { 33 | let dst_func_number = self.funcs.len(); 34 | let src_func_number = target.funcs.len(); 35 | self.funcs.append(&mut target.funcs); 36 | assert_eq!(dst_func_number + src_func_number, self.funcs.len()); 37 | 38 | self.typedefs.append(&mut target.typedefs); 39 | self.constants.append(&mut target.constants); 40 | self.enum_decls.append(&mut target.enum_decls); 41 | self.alias.append(&mut target.alias); 42 | self.called_functions = &self.called_functions | &target.called_functions; 43 | } 44 | } 45 | 46 | #[derive(Debug, Clone)] 47 | pub struct StructDef { 48 | pub members: BTreeMap, 49 | } 50 | 51 | impl Default for StructDef { 52 | fn default() -> Self { 53 | Self { 54 | members: BTreeMap::new(), 55 | } 56 | } 57 | } 58 | 59 | #[derive(Debug, Clone)] 60 | pub struct EnumDef { 61 | pub variants: BTreeMap, 62 | } 63 | 64 | #[derive(Debug, Clone)] 65 | pub struct VariantDef { 66 | pub tag: usize, 67 | } 68 | -------------------------------------------------------------------------------- /src/arch/x64/build.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::x64; 2 | use crate::common; 3 | use crate::setup; 4 | 5 | /// x64アーキテクチャ向けのビルドルーチン 6 | pub fn main( 7 | module_arena: common::module::ModuleArena, 8 | main_module_id: common::module::ModuleId, 9 | matches: &clap::ArgMatches, 10 | ) -> Result<(), Box> { 11 | match matches.subcommand() { 12 | ("build", Some(build_m)) => { 13 | let link_option = pld::LinkOption { 14 | entry_point: "startup::initialize".to_string(), 15 | }; 16 | let x64_module = compile_main( 17 | module_arena, 18 | main_module_id, 19 | build_m.is_present("verbose-hir"), 20 | build_m.is_present("debug"), 21 | link_option.entry_point.to_string(), 22 | ); 23 | 24 | let obj_file_dumper = 25 | asmpeach::assemble_code(x64_module.to_atandt(), asmpeach::Syntax::ATANDT)?; 26 | 27 | let exec_file_dumper = pld::static_link_with(obj_file_dumper.file, link_option); 28 | exec_file_dumper.generate_elf_file("a.out", 0o755)?; 29 | } 30 | ("compile", Some(compile_m)) => { 31 | let x64_module = compile_main( 32 | module_arena, 33 | main_module_id, 34 | compile_m.is_present("verbose-hir"), 35 | compile_m.is_present("debug"), 36 | String::new(), 37 | ); 38 | 39 | common::file_util::write_program_into("asm.s", x64_module.to_atandt()); 40 | } 41 | _ => eprintln!("please specify a subcommand. see --help."), 42 | } 43 | Ok(()) 44 | } 45 | 46 | /// x64用コンパイラのメインルーチン 47 | /// 機械独立なパスを呼び出した後x64依存のパスを処理する. 48 | pub fn compile_main( 49 | module_arena: common::module::ModuleArena, 50 | main_module_id: common::module::ModuleId, 51 | verbose_ir: bool, 52 | debug: bool, 53 | entry_point: String, 54 | ) -> x64::ir::Module { 55 | let (fn_arena, ast_root, type_env, stack_frame) = 56 | common::pass::frontend(module_arena, main_module_id, debug); 57 | let (ir_module, _local_cfg) = common::pass::backend( 58 | fn_arena, 59 | ast_root, 60 | &type_env, 61 | setup::BUILD_OPTION.target, 62 | verbose_ir, 63 | entry_point, 64 | ); 65 | 66 | x64::pass::codegen_main(ir_module, stack_frame) 67 | } 68 | -------------------------------------------------------------------------------- /src/common/three_address_code/value.rs: -------------------------------------------------------------------------------- 1 | use crate::common::option::Target; 2 | use crate::common::peachili_type::Type; 3 | use crate::common::three_address_code::value_kind; 4 | use id_arena::Id; 5 | 6 | pub type ValueId = Id; 7 | 8 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] 9 | pub struct Value { 10 | pub kind: value_kind::ValueKind, 11 | pub ty: Type, 12 | } 13 | 14 | #[allow(dead_code)] 15 | impl Value { 16 | pub fn dump(&self) -> String { 17 | format!("<{} {}>", self.ty.dump(), self.kind.dump()) 18 | } 19 | 20 | pub fn new(k: value_kind::ValueKind, ty: Type) -> Self { 21 | Self { kind: k, ty } 22 | } 23 | 24 | pub fn new_int64(value: i64, target: Target) -> Self { 25 | Self::new( 26 | value_kind::ValueKind::INTLITERAL { value }, 27 | Type::new_int64(target), 28 | ) 29 | } 30 | pub fn new_uint64(value: u64, target: Target) -> Self { 31 | Self::new( 32 | value_kind::ValueKind::UINTLITERAL { value }, 33 | Type::new_uint64(target), 34 | ) 35 | } 36 | pub fn new_boolean(truth: bool, target: Target) -> Self { 37 | Self::new( 38 | value_kind::ValueKind::BOOLEANLITERAL { truth }, 39 | Type::new_boolean(target), 40 | ) 41 | } 42 | pub fn new_string_literal(contents: String, target: Target) -> Self { 43 | Self::new( 44 | value_kind::ValueKind::STRINGLITERAL { contents }, 45 | Type::new_const_str(target), 46 | ) 47 | } 48 | pub fn new_temp(number: usize, ty: Type) -> Self { 49 | Self::new(value_kind::ValueKind::TEMP { number }, ty) 50 | } 51 | 52 | pub fn is_temp(&self) -> bool { 53 | match &self.kind { 54 | value_kind::ValueKind::TEMP { number: _ } => true, 55 | _ => false, 56 | } 57 | } 58 | pub fn is_id(&self) -> bool { 59 | match &self.kind { 60 | value_kind::ValueKind::ID { name: _ } => true, 61 | _ => false, 62 | } 63 | } 64 | 65 | pub fn copy_contents(&self) -> String { 66 | match &self.kind { 67 | value_kind::ValueKind::STRINGLITERAL { contents } => contents.clone(), 68 | value_kind::ValueKind::ID { name } => name.clone(), 69 | _ => String::new(), 70 | } 71 | } 72 | 73 | pub fn get_virt_number(&self) -> usize { 74 | match &self.kind { 75 | value_kind::ValueKind::TEMP { number } => *number, 76 | _ => 0, 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/arch/aarch64/ir/operand.rs: -------------------------------------------------------------------------------- 1 | pub enum OperandSize { 2 | /// 64bit size 3 | DWORD, 4 | } 5 | 6 | #[derive(Debug, Clone, Copy)] 7 | pub struct Operand { 8 | kind: OperandKind, 9 | } 10 | 11 | #[allow(dead_code)] 12 | impl Operand { 13 | pub fn to_dword(&self) -> String { 14 | match &self.kind { 15 | OperandKind::REGISTER { reg } => reg.to_dword(), 16 | OperandKind::IMMEDIATE { value } => format!("#{}", value), 17 | OperandKind::MEMORY { base, offset } => { 18 | if *offset == 0 { 19 | format!("[{}]", base.to_dword()) 20 | } else { 21 | format!("[{}, #{}]", base.to_dword(), *offset) 22 | } 23 | } 24 | } 25 | } 26 | 27 | pub fn new(kind: OperandKind) -> Self { 28 | Self { kind } 29 | } 30 | 31 | pub fn new_register(reg: Register) -> Self { 32 | Self::new(OperandKind::REGISTER { reg }) 33 | } 34 | pub fn new_immediate(v: i64) -> Self { 35 | Self::new(OperandKind::IMMEDIATE { value: v }) 36 | } 37 | pub fn new_memory(base: Register, offset: isize) -> Self { 38 | Self::new(OperandKind::MEMORY { base, offset }) 39 | } 40 | 41 | pub fn get_kind(&self) -> &OperandKind { 42 | &self.kind 43 | } 44 | pub fn get_base_reg(&self) -> Register { 45 | match self.kind { 46 | OperandKind::MEMORY { base, offset: _ } => base, 47 | _ => unreachable!(), 48 | } 49 | } 50 | pub fn get_offset(&self) -> isize { 51 | match self.kind { 52 | OperandKind::MEMORY { base: _, offset } => offset, 53 | _ => unreachable!(), 54 | } 55 | } 56 | } 57 | 58 | #[derive(Debug, Clone, Copy)] 59 | pub enum OperandKind { 60 | IMMEDIATE { value: i64 }, 61 | REGISTER { reg: Register }, 62 | MEMORY { base: Register, offset: isize }, 63 | } 64 | 65 | #[derive(Debug, Clone, Copy)] 66 | #[allow(dead_code)] 67 | pub enum Register { 68 | GPR { number: usize }, 69 | 70 | // Stack Pointer 71 | SP, 72 | // Frame Pointer 73 | FP, 74 | // Dword Link Register 75 | LINK, 76 | } 77 | 78 | impl Register { 79 | /// X8 ~ X18 80 | pub const AVAILABLES: usize = 10; 81 | 82 | pub fn to_dword(&self) -> String { 83 | match self { 84 | Register::SP => "sp".to_string(), 85 | Register::FP => "x29".to_string(), 86 | Register::LINK => "x30".to_string(), 87 | Register::GPR { number } => format!("x{}", number), 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/common/module/_module.rs: -------------------------------------------------------------------------------- 1 | use id_arena::{Arena, Id}; 2 | use std::sync::{Arc, Mutex}; 3 | 4 | /// 各ファイル(パッケージ)を表す構造体 5 | /// 依存グラフの各ノードとしても動作する 6 | #[derive(Clone)] 7 | pub struct Module { 8 | /// モジュールの種類 9 | pub kind: ModuleKind, 10 | /// 参照するモジュール 11 | pub refs: Arc>>, 12 | /// ディレクトリにぶら下がっているモジュール 13 | pub children: Arc>>, 14 | /// モジュールが存在するパス 15 | file_path: String, 16 | /// モジュール名 17 | name: String, 18 | } 19 | 20 | pub type ModuleArena = Arc>>; 21 | pub type ModuleId = Id; 22 | 23 | #[allow(dead_code)] 24 | impl Module { 25 | fn new(kind: ModuleKind, file_path: String, name: String) -> Self { 26 | Self { 27 | kind, 28 | file_path, 29 | name, 30 | refs: Arc::new(Mutex::new(Vec::new())), 31 | children: Arc::new(Mutex::new(Vec::new())), 32 | } 33 | } 34 | 35 | /// モジュールの依存ノードを追加する 36 | pub fn add_reference_module(&self, ref_module: ModuleId) { 37 | self.refs.lock().unwrap().push(ref_module); 38 | } 39 | 40 | /// モジュールの下位ノードを追加する 41 | pub fn add_child_module(&self, child_module: ModuleId) { 42 | self.children.lock().unwrap().push(child_module); 43 | } 44 | 45 | /// ファイルパスの参照 46 | pub fn get_path(&self) -> &String { 47 | &self.file_path 48 | } 49 | 50 | /// ファイルパスのコピー 51 | pub fn copy_path(&self) -> String { 52 | self.file_path.to_string() 53 | } 54 | 55 | /// モジュール名のコピー 56 | pub fn copy_name(&self) -> String { 57 | self.name.to_string() 58 | } 59 | 60 | /// mainパッケージを割り当てる 61 | pub fn new_primary(file_path: String, name: String) -> Self { 62 | Self::new(ModuleKind::PRIMARY, file_path, name) 63 | } 64 | 65 | /// 外部パッケージを割り当てる 66 | pub fn new_external(file_path: String, name: String) -> Self { 67 | Self::new(ModuleKind::EXTERNAL, file_path, name) 68 | } 69 | 70 | /// ファイルパスの設定 71 | pub fn set_file_path(&mut self, fp: String) { 72 | self.file_path = fp; 73 | } 74 | 75 | /// 依存モジュール数の取得 76 | pub fn ref_count(&self) -> usize { 77 | self.refs.lock().unwrap().len() 78 | } 79 | 80 | /// 下位モジュール数の取得 81 | pub fn child_count(&self) -> usize { 82 | self.children.lock().unwrap().len() 83 | } 84 | } 85 | 86 | #[derive(Clone, Copy)] 87 | #[allow(dead_code)] 88 | pub enum ModuleKind { 89 | /// func main() Noreturn を持つファイルのみが該当 90 | /// このパッケージが他のパッケージから参照されることはない 91 | PRIMARY, 92 | 93 | /// 何らかのパッケージから参照されているパッケージ 94 | EXTERNAL, 95 | } 96 | -------------------------------------------------------------------------------- /src/common/error/type_error.rs: -------------------------------------------------------------------------------- 1 | use crate::common::error::CompileErrorKind; 2 | use fmt::Formatter; 3 | use std::fmt; 4 | 5 | use crate::common::ast; 6 | 7 | /// Analyzerが発行するエラーを格納 8 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)] 9 | pub struct TypeError { 10 | /// エラーの種類 11 | kind: TypeErrorKind, 12 | } 13 | 14 | /// Analyzerが発行するエラーの種類を列挙 15 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)] 16 | pub enum TypeErrorKind { 17 | /// 型を導出しきれなかった 18 | CannotResolve { type_name: String }, 19 | 20 | /// メイン関数が見つからなかった 21 | NotFoundMainFunction, 22 | 23 | /// メイン関数に何らかの引数が定義されてしまっている 24 | MAINFUNCMUSTNOTHAVEANYARGUMENTS, 25 | 26 | /// メイン関数はいかなる値も返さない 27 | MainFunctionMustNotReturnAnyValues, 28 | 29 | /// 型名の場所で関数名が使用された 30 | GotFunctionNameAsType { func_name: String }, 31 | /// 型名の場所で定数名が使用された 32 | GotConstantNameAsType { const_name: String }, 33 | 34 | /// 変数以外へメンバアクセスしようとした. 35 | CannotAccessMemberWithNotAnIdentifier { struct_node: ast::ExpressionNode }, 36 | 37 | /// 構造体型以外にメンバアクセスした 38 | CannotAccessMemberWIthNotAStruct { struct_node: ast::ExpressionNode }, 39 | 40 | /// 該当するメンバが存在しなかった 41 | UndefinedSuchAMember { member: String }, 42 | } 43 | 44 | impl CompileErrorKind for TypeErrorKind { 45 | fn category(&self) -> &'static str { 46 | "TypeError" 47 | } 48 | } 49 | 50 | impl fmt::Display for TypeErrorKind { 51 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 52 | let s = match self { 53 | TypeErrorKind::CannotResolve { type_name } => { 54 | format!("cannot resolve a type -> `{}`", type_name) 55 | } 56 | TypeErrorKind::GotFunctionNameAsType { func_name } => { 57 | format!("a function `{}` used as a type-name", func_name) 58 | } 59 | TypeErrorKind::GotConstantNameAsType { const_name } => { 60 | format!("a constant `{}` used as a type-name", const_name) 61 | } 62 | TypeErrorKind::CannotAccessMemberWithNotAnIdentifier { struct_node } => format!( 63 | "cannot access member of `{:?}`, its not an identifier", 64 | struct_node 65 | ), 66 | TypeErrorKind::CannotAccessMemberWIthNotAStruct { struct_node } => format!( 67 | "cannot access member of `{:?}`, its not a struct", 68 | struct_node 69 | ), 70 | TypeErrorKind::UndefinedSuchAMember { member } => { 71 | format!("undefined such a member -> `{}`", member) 72 | } 73 | TypeErrorKind::NotFoundMainFunction => "entry point `main` not found".to_string(), 74 | TypeErrorKind::MAINFUNCMUSTNOTHAVEANYARGUMENTS => { 75 | "entry point `main` mustn't have any arguments".to_string() 76 | } 77 | TypeErrorKind::MainFunctionMustNotReturnAnyValues => { 78 | "entry point `main` mustn't return any values".to_string() 79 | } 80 | }; 81 | 82 | write!(f, "{}", s) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/setup.rs: -------------------------------------------------------------------------------- 1 | use crate::common; 2 | use clap::{App, Arg, ArgMatches}; 3 | 4 | pub const PEACHILI_VERSION: &str = "1.0"; 5 | 6 | lazy_static! { 7 | pub static ref BUILD_OPTION: common::option::BuildOption = { 8 | let matches = create_arg_matches(); 9 | 10 | // default_valueがあるので,unwrap()してよい 11 | let target = match matches.subcommand() { 12 | ("build", Some(build_m)) => Some(build_m.value_of("target").unwrap()), 13 | ("compile", Some(compile_m)) => Some(compile_m.value_of("target").unwrap()), 14 | _ => None, 15 | }; 16 | 17 | let mut build_option = common::option::BuildOption::new(matches.clone()); 18 | 19 | if let Some(target) = target { 20 | build_option.target = common::option::Target::new(target); 21 | } 22 | 23 | build_option 24 | }; 25 | } 26 | 27 | /// clap::ArgMatches 28 | fn create_arg_matches() -> ArgMatches { 29 | App::new("Peachili - The Peachili Programming Language Driver") 30 | .version(PEACHILI_VERSION) 31 | .author("Drumato ") 32 | .subcommand( 33 | App::new("compile") 34 | .version(PEACHILI_VERSION) 35 | .author("Drumato ") 36 | .args(&[ 37 | // コンパイル対象のファイル 38 | Arg::with_name("source") 39 | .required(true) 40 | .index(1) 41 | .help("Sets the input file to use"), 42 | // 生成するコードの対象 43 | Arg::with_name("target") 44 | .default_value("x86_64") 45 | .short('t') 46 | .long("target") 47 | .help("x86_64/aarch64"), 48 | // IRのダンプ 49 | Arg::with_name("verbose-hir") 50 | .long("verbose-hir") 51 | .help("dump IR-Module to hir.dot"), 52 | // debugオプション 53 | Arg::with_name("debug").long("debug").help("debug"), 54 | ]), 55 | ) 56 | .subcommand( 57 | App::new("build") 58 | .version(PEACHILI_VERSION) 59 | .author("Drumato ") 60 | .args(&[ 61 | // コンパイル対象のファイル 62 | Arg::with_name("source") 63 | .required(true) 64 | .index(1) 65 | .help("Sets the input file to use"), 66 | // 生成するコードの対象 67 | Arg::with_name("target") 68 | .default_value("x86_64") 69 | .short('t') 70 | .long("target") 71 | .help("x86_64/aarch64"), 72 | // IRのダンプ 73 | Arg::with_name("verbose-hir") 74 | .long("verbose-hir") 75 | .help("dump IR-Module to hir.dot"), 76 | // debugオプション 77 | Arg::with_name("debug").long("debug").help("debug"), 78 | ]), 79 | ) 80 | .get_matches() 81 | } 82 | -------------------------------------------------------------------------------- /src/common/analyze_resource/token/_token.rs: -------------------------------------------------------------------------------- 1 | use crate::common::{position::Position, token::TokenKind}; 2 | use std::fmt::{Display, Formatter, Result as FR}; 3 | 4 | /// トークナイザが返すリストの各要素 5 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] 6 | pub struct Token { 7 | /// トークンの種類 8 | k: TokenKind, 9 | /// ファイル上の位置 10 | p: Position, 11 | } 12 | 13 | #[allow(dead_code)] 14 | impl Token { 15 | pub fn new(kind: TokenKind, position: Position) -> Self { 16 | Self { 17 | k: kind, 18 | p: position, 19 | } 20 | } 21 | 22 | pub fn try_new_keyword(s: &str, p: Position) -> Option { 23 | let tk = TokenKind::try_new_keyword(s); 24 | tk.as_ref()?; 25 | 26 | Some(Token::new(tk.unwrap(), p)) 27 | } 28 | 29 | pub fn get_position(&self) -> Position { 30 | self.p 31 | } 32 | 33 | /// 識別子 34 | pub fn new_identifier(name: String, position: Position) -> Self { 35 | Self::new(TokenKind::IDENTIFIER { name }, position) 36 | } 37 | 38 | /// 文字列トークンの定義 39 | pub fn new_string_literal(contents: String, position: Position) -> Self { 40 | Self::new(TokenKind::STRLIT { contents }, position) 41 | } 42 | 43 | /// 整数トークンの定義 44 | pub fn new_int_literal(v: i64, position: Position) -> Self { 45 | Self::new(TokenKind::Integer { value: v }, position) 46 | } 47 | 48 | /// 非符号付き整数トークンの定義 49 | pub fn new_uint_literal(v: u64, position: Position) -> Self { 50 | Self::new(TokenKind::UNSIGNEDINTEGER { value: v }, position) 51 | } 52 | 53 | /// 空白の定義 54 | pub fn new_blank(position: Position) -> Self { 55 | Self::new(TokenKind::BLANK, position) 56 | } 57 | 58 | /// 整数トークンだと仮定して,数値を受け取る 59 | pub fn int_value(&self) -> i64 { 60 | match &self.k { 61 | TokenKind::Integer { value } => *value, 62 | _ => unimplemented!(), 63 | } 64 | } 65 | 66 | /// 非符号付き整数トークンだと仮定して,数値を受け取る 67 | pub fn uint_value(&self) -> u64 { 68 | match &self.k { 69 | TokenKind::UNSIGNEDINTEGER { value } => *value, 70 | _ => unimplemented!(), 71 | } 72 | } 73 | 74 | /// 文字列トークンだと仮定して,文字列を受け取る 75 | pub fn copy_contents(&self) -> String { 76 | match &self.k { 77 | TokenKind::STRLIT { contents } => contents.to_string(), 78 | _ => unimplemented!(), 79 | } 80 | } 81 | /// 識別子トークンだと仮定して,名前を受け取る 82 | pub fn copy_name(&self) -> String { 83 | match &self.k { 84 | TokenKind::IDENTIFIER { name } => name.to_string(), 85 | _ => unimplemented!(), 86 | } 87 | } 88 | 89 | pub fn get_kind(&self) -> &TokenKind { 90 | &self.k 91 | } 92 | 93 | /// Tokenizerが無視するトークンの種類 94 | pub fn should_ignore(&self) -> bool { 95 | match &self.k { 96 | TokenKind::BLANK | TokenKind::NEWLINE | TokenKind::COMMENT { contents: _ } => true, 97 | _ => false, 98 | } 99 | } 100 | } 101 | 102 | impl Display for Token { 103 | fn fmt(&self, f: &mut Formatter<'_>) -> FR { 104 | write!(f, "{}: {}", self.p, self.k) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/arch/x64/ir/operand.rs: -------------------------------------------------------------------------------- 1 | pub enum OperandSize { 2 | QWORD, 3 | } 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct Operand { 7 | kind: OperandKind, 8 | } 9 | 10 | impl Operand { 11 | pub fn to_atandt(&self) -> String { 12 | match &self.kind { 13 | OperandKind::REGISTER { reg } => reg.to_atandt(), 14 | OperandKind::IMMEDIATE { value } => format!("${}", value), 15 | OperandKind::MEMORY { base, offset } => { 16 | if *offset == 0 { 17 | format!("({})", base.to_atandt()) 18 | } else { 19 | format!("-{}({})", offset, base.to_atandt()) 20 | } 21 | } 22 | OperandKind::LABEL { name } => name.to_string(), 23 | } 24 | } 25 | pub fn add_offset(&mut self, appendix: usize) { 26 | if let OperandKind::MEMORY { base: _, offset } = &mut self.kind { 27 | *offset += appendix; 28 | } 29 | } 30 | pub fn get_reg(&self) -> Register { 31 | match &self.kind { 32 | OperandKind::REGISTER { reg } => *reg, 33 | _ => unreachable!(), 34 | } 35 | } 36 | 37 | pub fn new(kind: OperandKind) -> Self { 38 | Self { kind } 39 | } 40 | 41 | pub fn get_kind(&self) -> &OperandKind { 42 | &self.kind 43 | } 44 | } 45 | 46 | #[derive(Debug, Clone)] 47 | pub enum OperandKind { 48 | IMMEDIATE { value: i64 }, 49 | REGISTER { reg: Register }, 50 | MEMORY { base: Register, offset: usize }, 51 | LABEL { name: String }, 52 | } 53 | 54 | #[derive(Debug, Clone, Copy)] 55 | #[allow(dead_code)] 56 | pub enum Register { 57 | // 64bit general-purpose registers 58 | /// Accumulator Register 59 | RAX, 60 | 61 | /// (Stack) Base Pointer Register 62 | RBP, 63 | /// Stack Pointer Register 64 | RSP, 65 | /// Destination Index Register 66 | RDI, 67 | /// Source Index Register 68 | RSI, 69 | /// Data Register 70 | RDX, 71 | /// Counter Register 72 | RCX, 73 | /// Base Register 74 | RBX, 75 | 76 | // x64 appended registers 77 | R8, 78 | R9, 79 | R10, 80 | R11, 81 | R12, 82 | R13, 83 | R14, 84 | R15, 85 | } 86 | 87 | impl Register { 88 | /// R10 ~ R15 89 | pub const AVAILABLES: usize = 6; 90 | 91 | pub fn to_str(&self) -> &'static str { 92 | match self { 93 | // 64bit general-purpose registers 94 | Register::RAX => "rax", 95 | Register::RCX => "rcx", 96 | Register::RDX => "rdx", 97 | Register::RBX => "rbx", 98 | Register::RSP => "rsp", 99 | Register::RBP => "rbp", 100 | Register::RSI => "rsi", 101 | Register::RDI => "rdi", 102 | Register::R8 => "r8", 103 | Register::R9 => "r9", 104 | Register::R10 => "r10", 105 | Register::R11 => "r11", 106 | Register::R12 => "r12", 107 | Register::R13 => "r13", 108 | Register::R14 => "r14", 109 | Register::R15 => "r15", 110 | } 111 | } 112 | 113 | pub fn to_atandt(&self) -> String { 114 | format!("%{}", self.to_str()) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/arch/x64/ir/instruction.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::x64::ir; 2 | 3 | pub struct Instruction { 4 | kind: ir::InstKind, 5 | } 6 | 7 | impl Instruction { 8 | pub fn to_atandt(&self) -> String { 9 | match &self.kind { 10 | ir::InstKind::ADD { 11 | operand_size, 12 | src, 13 | dst, 14 | } => match operand_size { 15 | ir::OperandSize::QWORD => format!("addq {}, {}", src.to_atandt(), dst.to_atandt()), 16 | }, 17 | ir::InstKind::SUB { 18 | operand_size, 19 | src, 20 | dst, 21 | } => match operand_size { 22 | ir::OperandSize::QWORD => format!("subq {}, {}", src.to_atandt(), dst.to_atandt()), 23 | }, 24 | ir::InstKind::IMUL { 25 | operand_size, 26 | src, 27 | dst, 28 | } => match operand_size { 29 | ir::OperandSize::QWORD => format!("imulq {}, {}", src.to_atandt(), dst.to_atandt()), 30 | }, 31 | ir::InstKind::IDIV { 32 | operand_size, 33 | value, 34 | } => match operand_size { 35 | ir::OperandSize::QWORD => format!("idivq {}", value.to_atandt()), 36 | }, 37 | ir::InstKind::MOV { 38 | operand_size, 39 | src, 40 | dst, 41 | } => match operand_size { 42 | ir::OperandSize::QWORD => format!("movq {}, {}", src.to_atandt(), dst.to_atandt()), 43 | }, 44 | ir::InstKind::CMP { 45 | operand_size, 46 | src, 47 | dst, 48 | } => match operand_size { 49 | ir::OperandSize::QWORD => format!("cmpq {}, {}", src.to_atandt(), dst.to_atandt()), 50 | }, 51 | ir::InstKind::LEA { 52 | operand_size, 53 | src, 54 | dst, 55 | } => match operand_size { 56 | ir::OperandSize::QWORD => format!("leaq {}, {}", src.to_atandt(), dst.to_atandt()), 57 | }, 58 | ir::InstKind::INLINEASM { contents } => contents.to_string(), 59 | ir::InstKind::CALL { name } => format!("call \"{}\"", name), 60 | ir::InstKind::PUSH { 61 | operand_size, 62 | value, 63 | } => match operand_size { 64 | ir::OperandSize::QWORD => format!("pushq {}", value.to_atandt()), 65 | }, 66 | ir::InstKind::POP { 67 | operand_size, 68 | value, 69 | } => match operand_size { 70 | ir::OperandSize::QWORD => format!("popq {}", value.to_atandt()), 71 | }, 72 | ir::InstKind::NEG { 73 | operand_size, 74 | value, 75 | } => match operand_size { 76 | ir::OperandSize::QWORD => format!("negq {}", value.to_atandt()), 77 | }, 78 | ir::InstKind::CLTD => "cltd".to_string(), 79 | ir::InstKind::RET => "ret".to_string(), 80 | ir::InstKind::JMP { label } => format!("jmp .L{}", label), 81 | ir::InstKind::JE { label } => format!("je .L{}", label), 82 | } 83 | } 84 | 85 | pub fn new(k: ir::InstKind) -> Self { 86 | Self { kind: k } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/debug/dump_local_cfg.rs: -------------------------------------------------------------------------------- 1 | use crate::common::cfg::LocalControlFlowGraph; 2 | use crate::common::file_util; 3 | use crate::common::three_address_code as tac; 4 | use std::collections::BTreeMap; 5 | 6 | struct CFGDumper { 7 | output: String, 8 | file_path: String, 9 | } 10 | 11 | /// 通常のCFGダンプ 12 | pub fn dump_local_cfg( 13 | file_path: &str, 14 | ir_module: &tac::IRModule, 15 | local_cfg: &BTreeMap, 16 | ) { 17 | let mut cfg_dumper = CFGDumper { 18 | output: String::new(), 19 | file_path: file_path.to_string(), 20 | }; 21 | cfg_dumper.output += "digraph G { \n"; 22 | 23 | for fn_id in ir_module.funcs.iter() { 24 | cfg_dumper.append_function_cluster(ir_module, fn_id, local_cfg); 25 | } 26 | 27 | cfg_dumper.output += "} \n"; 28 | 29 | cfg_dumper.to_file(); 30 | } 31 | 32 | impl CFGDumper { 33 | /// サブグラフの定義 34 | fn append_function_cluster( 35 | &mut self, 36 | ir_module: &tac::IRModule, 37 | ir_fn_id: &tac::IRFunctionId, 38 | local_cfg: &BTreeMap, 39 | ) { 40 | let ir_fn = ir_module.fn_allocator.get(*ir_fn_id).unwrap(); 41 | 42 | self.append_cluster_attributes(&ir_fn.name); 43 | 44 | for code_id in ir_fn.codes.iter() { 45 | self.append_ir_node(*code_id, ir_fn); 46 | 47 | // エッジ定義 48 | self.append_succ_edge(local_cfg, ir_fn_id, code_id, &ir_fn.name); 49 | } 50 | 51 | self.output += " }\n"; 52 | } 53 | 54 | /// グラフのノード定義 55 | fn append_ir_node(&mut self, code_id: tac::CodeId, ir_fn: &tac::IRFunction) { 56 | let (code, shape) = self.get_shape(ir_fn, &code_id); 57 | self.output += &format!( 58 | " \"{}{:?}\"[label=\"{}\", shape=\"{}\"]\n", 59 | ir_fn.name, 60 | code_id, 61 | code.dump(ir_fn.value_allocator.clone()), 62 | shape 63 | ); 64 | } 65 | 66 | /// ノードの形をCodeKindによって決める 67 | fn get_shape(&self, ir_fn: &tac::IRFunction, code_id: &tac::CodeId) -> (tac::Code, String) { 68 | let code = ir_fn.get_code(*code_id); 69 | // ノード定義 70 | let shape = match code.kind { 71 | tac::CodeKind::LABEL { name: _ } => "ellipse", 72 | _ => "box", 73 | }; 74 | (code, shape.to_string()) 75 | } 76 | 77 | /// 後続節の定義 78 | fn append_succ_edge( 79 | &mut self, 80 | local_cfg: &BTreeMap, 81 | ir_fn_id: &tac::IRFunctionId, 82 | code_id: &tac::CodeId, 83 | cluster_name: &str, 84 | ) { 85 | let graph = local_cfg.get(ir_fn_id).unwrap().get_successors(code_id); 86 | for succ_edge in graph.iter() { 87 | self.output += &format!( 88 | " \"{}{:?}\" -> \"{}{:?}\"\n", 89 | cluster_name, code_id, cluster_name, succ_edge 90 | ); 91 | } 92 | } 93 | 94 | /// サブグラフの情報 95 | fn append_cluster_attributes(&mut self, cluster_name: &str) { 96 | self.output += &format!(" subgraph cluster_{} {{\n", cluster_name); 97 | self.output += &format!(" label = \"{}\";\n", cluster_name); 98 | self.output += " labelloc = \"t\"\n"; 99 | self.output += " labeljust = \"l\"\n"; 100 | self.output += " fillcolor = \"#ababab\";\n"; 101 | } 102 | 103 | fn to_file(&self) { 104 | file_util::write_program_into(&self.file_path, self.output.to_string()); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/arch/aarch64/ir/instruction.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::aarch64::ir; 2 | 3 | pub struct Instruction { 4 | kind: ir::InstKind, 5 | } 6 | 7 | impl Instruction { 8 | pub fn to_assembly(&self) -> String { 9 | match &self.kind { 10 | ir::InstKind::ADD { 11 | operand_size, 12 | dst, 13 | lop, 14 | rop, 15 | } => match operand_size { 16 | ir::OperandSize::DWORD => format!( 17 | "add {}, {}, {}", 18 | dst.to_dword(), 19 | lop.to_dword(), 20 | rop.to_dword() 21 | ), 22 | }, 23 | ir::InstKind::SUB { 24 | operand_size, 25 | dst, 26 | lop, 27 | rop, 28 | } => match operand_size { 29 | ir::OperandSize::DWORD => format!( 30 | "sub {}, {}, {}", 31 | dst.to_dword(), 32 | lop.to_dword(), 33 | rop.to_dword() 34 | ), 35 | }, 36 | ir::InstKind::MUL { 37 | operand_size, 38 | dst, 39 | lop, 40 | rop, 41 | } => match operand_size { 42 | ir::OperandSize::DWORD => format!( 43 | "mul {}, {}, {}", 44 | dst.to_dword(), 45 | lop.to_dword(), 46 | rop.to_dword() 47 | ), 48 | }, 49 | ir::InstKind::NEG { 50 | operand_size, 51 | dst, 52 | value, 53 | } => match operand_size { 54 | ir::OperandSize::DWORD => format!("neg {}, {}", dst.to_dword(), value.to_dword(),), 55 | }, 56 | ir::InstKind::MOV { 57 | operand_size, 58 | dst, 59 | src, 60 | } => match operand_size { 61 | ir::OperandSize::DWORD => format!("mov {}, {}", dst.to_dword(), src.to_dword()), 62 | }, 63 | ir::InstKind::STR { 64 | operand_size, 65 | dst, 66 | src, 67 | } => match operand_size { 68 | ir::OperandSize::DWORD => format!("str {}, {}", src.to_dword(), dst.to_dword()), 69 | }, 70 | ir::InstKind::STP { 71 | operand_size, 72 | reg1, 73 | reg2, 74 | dst, 75 | } => match operand_size { 76 | ir::OperandSize::DWORD => format!( 77 | "stp {}, {}, {}", 78 | reg1.to_dword(), 79 | reg2.to_dword(), 80 | dst.to_dword() 81 | ), 82 | }, 83 | ir::InstKind::LDR { 84 | operand_size, 85 | dst, 86 | src, 87 | } => match operand_size { 88 | ir::OperandSize::DWORD => format!("ldr {}, {}", dst.to_dword(), src.to_dword()), 89 | }, 90 | ir::InstKind::LDP { 91 | operand_size, 92 | reg1, 93 | reg2, 94 | src, 95 | } => match operand_size { 96 | ir::OperandSize::DWORD => format!( 97 | "ldp {}, {}, {}", 98 | reg1.to_dword(), 99 | reg2.to_dword(), 100 | src.to_dword() 101 | ), 102 | }, 103 | ir::InstKind::BL { name } => format!("bl \"{}\"", name), 104 | ir::InstKind::INLINEASM { contents } => contents.to_string(), 105 | ir::InstKind::RET => "ret".to_string(), 106 | } 107 | } 108 | 109 | pub fn new(k: ir::InstKind) -> Self { 110 | Self { kind: k } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/common/pass/build_cfg/construct.rs: -------------------------------------------------------------------------------- 1 | use crate::common::cfg::LocalControlFlowGraph; 2 | use crate::common::three_address_code as tac; 3 | use std::collections::{BTreeMap, BTreeSet}; 4 | use tac::CodeKind; 5 | 6 | pub fn build_local_cfg( 7 | ir_module: &tac::IRModule, 8 | ) -> BTreeMap { 9 | let mut local_cfg = BTreeMap::new(); 10 | 11 | for fn_id in ir_module.funcs.iter() { 12 | let ir_fn = ir_module.fn_allocator.get(*fn_id).unwrap(); 13 | 14 | let cfg_in_func = build_graph_in_func(ir_fn); 15 | local_cfg.insert(*fn_id, cfg_in_func); 16 | } 17 | 18 | local_cfg 19 | } 20 | 21 | fn build_graph_in_func(ir_fn: &tac::IRFunction) -> LocalControlFlowGraph { 22 | let mut graph: LocalControlFlowGraph = Default::default(); 23 | 24 | let mut label_to_jmp: BTreeMap = BTreeMap::new(); 25 | // 複数の可能性がある 26 | let mut jmp_to_label: BTreeMap> = BTreeMap::new(); 27 | 28 | for (idx, code_id) in ir_fn.codes.iter().enumerate() { 29 | graph.successors.insert(*code_id, BTreeSet::new()); 30 | graph.predecessors.insert(*code_id, BTreeSet::new()); 31 | 32 | if let Ok(arena) = ir_fn.code_allocator.lock() { 33 | let code = arena.get(*code_id).unwrap(); 34 | 35 | match &code.kind { 36 | CodeKind::LABEL { name } => { 37 | label_to_jmp.insert(name.clone(), *code_id); 38 | 39 | if let Some(jump_codes) = jmp_to_label.get(name) { 40 | for jump_code in jump_codes.iter() { 41 | add_pred_edge(&mut graph, *code_id, *jump_code); 42 | add_succ_edge(&mut graph, *jump_code, *code_id); 43 | } 44 | } 45 | 46 | add_pred_edge_from(&mut graph, *code_id, &ir_fn.codes, idx); 47 | add_succ_edge_from(&mut graph, *code_id, &ir_fn.codes, idx); 48 | } 49 | CodeKind::JUMPIFFALSE { 50 | label, 51 | cond_result: _, 52 | } => { 53 | if !jmp_to_label.contains_key(label) { 54 | jmp_to_label.insert(label.clone(), BTreeSet::new()); 55 | } 56 | jmp_to_label.get_mut(label).unwrap().insert(*code_id); 57 | 58 | if let Some(label_code) = label_to_jmp.get(label) { 59 | add_succ_edge(&mut graph, *code_id, *label_code); 60 | add_pred_edge(&mut graph, *label_code, *code_id); 61 | } 62 | 63 | add_succ_edge_from(&mut graph, *code_id, &ir_fn.codes, idx); 64 | add_pred_edge_from(&mut graph, *code_id, &ir_fn.codes, idx); 65 | } 66 | CodeKind::JUMP { label } => { 67 | if !jmp_to_label.contains_key(label) { 68 | jmp_to_label.insert(label.clone(), BTreeSet::new()); 69 | } 70 | jmp_to_label.get_mut(label).unwrap().insert(*code_id); 71 | 72 | if let Some(label_code) = label_to_jmp.get(label) { 73 | add_succ_edge(&mut graph, *code_id, *label_code); 74 | add_pred_edge(&mut graph, *label_code, *code_id); 75 | } 76 | add_pred_edge_from(&mut graph, *code_id, &ir_fn.codes, idx); 77 | } 78 | _ => { 79 | add_pred_edge_from(&mut graph, *code_id, &ir_fn.codes, idx); 80 | add_succ_edge_from(&mut graph, *code_id, &ir_fn.codes, idx); 81 | } 82 | } 83 | } 84 | } 85 | 86 | graph 87 | } 88 | 89 | fn add_pred_edge_from( 90 | graph: &mut LocalControlFlowGraph, 91 | src: tac::CodeId, 92 | codes: &[tac::CodeId], 93 | idx: usize, 94 | ) { 95 | if idx > 0 { 96 | graph.add_predecessor(src, codes[idx - 1]); 97 | } 98 | } 99 | 100 | fn add_succ_edge_from( 101 | graph: &mut LocalControlFlowGraph, 102 | src: tac::CodeId, 103 | codes: &[tac::CodeId], 104 | idx: usize, 105 | ) { 106 | if idx < codes.len() - 1 { 107 | graph.add_successor(src, codes[idx + 1]); 108 | } 109 | } 110 | 111 | fn add_pred_edge(graph: &mut LocalControlFlowGraph, src: tac::CodeId, dst: tac::CodeId) { 112 | graph.add_predecessor(src, dst); 113 | } 114 | 115 | fn add_succ_edge(graph: &mut LocalControlFlowGraph, src: tac::CodeId, dst: tac::CodeId) { 116 | graph.add_successor(src, dst) 117 | } 118 | -------------------------------------------------------------------------------- /src/common/analyze_resource/ast/expression.rs: -------------------------------------------------------------------------------- 1 | use crate::common::ast::{ExpressionNodeKind, StNodeId}; 2 | use crate::common::position; 3 | use crate::common::token::TokenKind; 4 | 5 | use id_arena::Id; 6 | 7 | pub type ExNodeId = Id; 8 | 9 | /// 式ノード 10 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] 11 | pub struct ExpressionNode { 12 | k: ExpressionNodeKind, 13 | p: position::Position, 14 | } 15 | 16 | #[allow(dead_code)] 17 | impl ExpressionNode { 18 | fn new(k: ExpressionNodeKind, p: position::Position) -> Self { 19 | Self { k, p } 20 | } 21 | pub fn get_pos(&self) -> position::Position { 22 | self.p 23 | } 24 | pub fn copy_names(&self) -> Vec { 25 | match self.get_kind() { 26 | ExpressionNodeKind::IDENTIFIER { names } => names.clone(), 27 | _ => panic!("cannot copy_names with not identifier"), 28 | } 29 | } 30 | pub fn new_integer(int_value: i64, pos: position::Position) -> Self { 31 | Self::new(ExpressionNodeKind::INTEGER { value: int_value }, pos) 32 | } 33 | pub fn new_identifier(names: Vec, pos: position::Position) -> Self { 34 | Self::new(ExpressionNodeKind::IDENTIFIER { names }, pos) 35 | } 36 | pub fn new_uinteger(uint_value: u64, pos: position::Position) -> Self { 37 | Self::new(ExpressionNodeKind::UINTEGER { value: uint_value }, pos) 38 | } 39 | pub fn new_string_literal(contents: String, pos: position::Position) -> Self { 40 | Self::new(ExpressionNodeKind::STRING { contents }, pos) 41 | } 42 | pub fn new_boolean(truth: bool, pos: position::Position) -> Self { 43 | Self::new(ExpressionNodeKind::BOOLEAN { truth }, pos) 44 | } 45 | pub fn new_call(names: Vec, args: Vec, pos: position::Position) -> Self { 46 | Self::new(ExpressionNodeKind::CALL { names, args }, pos) 47 | } 48 | pub fn new_if( 49 | cond_id: ExNodeId, 50 | body: Vec, 51 | alter: Option>, 52 | pos: position::Position, 53 | ) -> Self { 54 | Self::new( 55 | ExpressionNodeKind::IF { 56 | cond_ex: cond_id, 57 | body, 58 | alter, 59 | }, 60 | pos, 61 | ) 62 | } 63 | pub fn new_prefix_op(operator: &TokenKind, value: ExNodeId, pos: position::Position) -> Self { 64 | let nk = match operator { 65 | TokenKind::MINUS => ExpressionNodeKind::NEG { value }, 66 | TokenKind::AMPERSAND => ExpressionNodeKind::ADDRESSOF { value }, 67 | TokenKind::ASTERISK => ExpressionNodeKind::DEREFERENCE { value }, 68 | _ => panic!("cannot create prefix-operation from {}", operator), 69 | }; 70 | Self::new(nk, pos) 71 | } 72 | pub fn new_postfix_op( 73 | operator: &TokenKind, 74 | id: ExNodeId, 75 | member: String, 76 | pos: position::Position, 77 | ) -> Self { 78 | let nk = match operator { 79 | TokenKind::DOT => ExpressionNodeKind::MEMBER { id, member }, 80 | _ => panic!("cannot create postfix-operation from {}", operator), 81 | }; 82 | Self::new(nk, pos) 83 | } 84 | 85 | pub fn new_binop( 86 | tk: &TokenKind, 87 | lhs: ExNodeId, 88 | rhs: ExNodeId, 89 | pos: position::Position, 90 | ) -> Self { 91 | let nk = match tk { 92 | TokenKind::PLUS => ExpressionNodeKind::ADD { lhs, rhs }, 93 | TokenKind::MINUS => ExpressionNodeKind::SUB { lhs, rhs }, 94 | TokenKind::ASTERISK => ExpressionNodeKind::MUL { lhs, rhs }, 95 | TokenKind::SLASH => ExpressionNodeKind::DIV { lhs, rhs }, 96 | TokenKind::ASSIGN => ExpressionNodeKind::ASSIGN { lhs, rhs }, 97 | _ => panic!("cannot create binary-operation from {}", tk), 98 | }; 99 | 100 | Self::new(nk, pos) 101 | } 102 | 103 | pub fn get_kind(&self) -> &ExpressionNodeKind { 104 | &self.k 105 | } 106 | 107 | pub fn is_identifier(&self) -> bool { 108 | match self.get_kind() { 109 | ExpressionNodeKind::IDENTIFIER { names: _ } => true, 110 | _ => false, 111 | } 112 | } 113 | pub fn is_integer_literal(&self) -> bool { 114 | match self.get_kind() { 115 | ExpressionNodeKind::INTEGER { value: _ } => true, 116 | _ => false, 117 | } 118 | } 119 | pub fn get_integer_value(&self) -> i64 { 120 | match self.get_kind() { 121 | ExpressionNodeKind::INTEGER { value } => *value, 122 | _ => panic!("cannot get value from {:?}", self), 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/common/pass/frontend.rs: -------------------------------------------------------------------------------- 1 | use crate::common::pass::{analyzer, parser, tld_collector, tokenizer}; 2 | use crate::common::{ast, file_util, frame_object, module, peachili_type}; 3 | use crate::setup; 4 | use id_arena::Arena; 5 | use std::collections::BTreeMap; 6 | use std::sync::{Arc, Mutex}; 7 | 8 | /// フロントエンド資源をまとめる構造体 9 | struct FrontendManager { 10 | module_arena: module::ModuleArena, 11 | fn_arena: ast::FnArena, 12 | full_ast: ast::ASTRoot, 13 | } 14 | 15 | /// 字句解析,パース,意味解析等を行う. 16 | pub fn frontend( 17 | module_arena: module::ModuleArena, 18 | main_module_id: module::ModuleId, 19 | debug: bool, 20 | ) -> ( 21 | ast::FnArena, 22 | ast::ASTRoot, 23 | BTreeMap>, 24 | frame_object::StackFrame, 25 | ) { 26 | let mut manager = FrontendManager { 27 | module_arena, 28 | fn_arena: Arc::new(Mutex::new(Arena::new())), 29 | full_ast: Default::default(), 30 | }; 31 | 32 | let source = manager.read_module_contents(main_module_id); 33 | 34 | // 初期値として空のStringを渡しておく 35 | manager.parse_file(source, String::new()); 36 | 37 | // メインモジュールが参照する各モジュールも同様にパース 38 | manager.parse_requires(main_module_id, String::new()); 39 | 40 | // ASTレベルのconstant-folding 41 | analyzer::constant_folding(manager.fn_arena.clone(), &manager.full_ast); 42 | 43 | // TLD解析 44 | let tld_env = tld_collector::main(manager.fn_arena.clone(), &manager.full_ast); 45 | 46 | // 意味解析 47 | // 先に型環境を構築してから,型検査を行う 48 | let type_env = analyzer::type_resolve_main( 49 | manager.fn_arena.clone(), 50 | &tld_env, 51 | &manager.full_ast, 52 | setup::BUILD_OPTION.target, 53 | ); 54 | 55 | if debug { 56 | analyzer::type_check_main( 57 | manager.fn_arena.clone(), 58 | &tld_env, 59 | &type_env, 60 | &manager.full_ast, 61 | setup::BUILD_OPTION.target, 62 | ); 63 | } 64 | 65 | // スタック割付 66 | // 通常はローカル変数をすべてスタックに. 67 | // 最適化を有効化にしたらレジスタ割付したい 68 | let func_frame = analyzer::allocate_stack_frame(&tld_env, &type_env); 69 | 70 | (manager.fn_arena, manager.full_ast, type_env, func_frame) 71 | } 72 | 73 | impl FrontendManager { 74 | /// モジュールの内容(Peachiliコード)を読み出す 75 | fn read_module_contents(&self, module_id: module::ModuleId) -> String { 76 | if let Ok(arena) = self.module_arena.lock() { 77 | let m = arena.get(module_id).unwrap(); 78 | let source = file_util::read_program_from_file(m.get_path()); 79 | 80 | // Bundlerがファイルの存在はチェックしているはず 81 | assert!(source.is_some()); 82 | 83 | return source.unwrap(); 84 | } 85 | 86 | unreachable!() 87 | } 88 | 89 | /// 字句解析, 構文解析をして返す 90 | fn parse_file(&mut self, file_contents: String, module_name: String) { 91 | let tokens = tokenizer::main(file_contents); 92 | 93 | self.full_ast 94 | .absorb(parser::main(self.fn_arena.clone(), tokens, module_name)); 95 | } 96 | 97 | /// mod_idのモジュールが参照するすべてのモジュールをパースし,結合 98 | fn parse_requires(&mut self, mod_id: module::ModuleId, module_name: String) { 99 | // 参照ノードをすべて取得 100 | let requires = self 101 | .module_arena 102 | .lock() 103 | .unwrap() 104 | .get(mod_id) 105 | .unwrap() 106 | .refs 107 | .clone(); 108 | for req_id in requires.lock().unwrap().iter() { 109 | self.parse_ext_module(*req_id, module_name.clone()); 110 | } 111 | } 112 | 113 | /// 再帰呼出しされる,外部モジュールの組み立て関数 114 | /// 本体 -> 参照 -> 子の順にパースし,すべてを結合して返す 115 | fn parse_ext_module(&mut self, ext_id: module::ModuleId, mut module_name: String) { 116 | let is_dir_module = self 117 | .module_arena 118 | .lock() 119 | .unwrap() 120 | .get(ext_id) 121 | .unwrap() 122 | .child_count() 123 | != 0; 124 | // モジュール名を構築. 125 | let this_module_name = self 126 | .module_arena 127 | .lock() 128 | .unwrap() 129 | .get(ext_id) 130 | .unwrap() 131 | .copy_name(); 132 | construct_full_path(&mut module_name, this_module_name); 133 | 134 | if !is_dir_module { 135 | let source = self.read_module_contents(ext_id); 136 | self.parse_file(source, module_name.clone()); 137 | } 138 | 139 | // 参照・子ノードたちのパース,結合 140 | self.parse_requires(ext_id, module_name.clone()); 141 | self.parse_children(ext_id, module_name); 142 | } 143 | 144 | /// mod_idのモジュール以下のすべてのモジュールをパースし,結合 145 | fn parse_children(&mut self, mod_id: module::ModuleId, module_name: String) { 146 | // 参照ノードをすべて取得 147 | let children = self 148 | .module_arena 149 | .lock() 150 | .unwrap() 151 | .get(mod_id) 152 | .unwrap() 153 | .children 154 | .clone(); 155 | for child_id in children.lock().unwrap().iter() { 156 | self.parse_ext_module(*child_id, module_name.clone()); 157 | } 158 | } 159 | } 160 | 161 | // トップのモジュールなら `std` のように 162 | // それ以降なら `std::os` のようにつなげる 163 | fn construct_full_path(full_path: &mut String, module_name: String) { 164 | *full_path = if full_path.is_empty() { 165 | module_name 166 | } else { 167 | format!("{}::{}", full_path, module_name) 168 | }; 169 | } 170 | 171 | #[cfg(test)] 172 | mod frontend_tests { 173 | use super::*; 174 | 175 | #[test] 176 | fn construct_full_path_test() { 177 | let mut s1 = String::new(); 178 | construct_full_path(&mut s1, "std".to_string()); 179 | assert_eq!("std", s1); 180 | 181 | let mut s2 = String::from("std"); 182 | construct_full_path(&mut s2, "os".to_string()); 183 | assert_eq!("std::os", s2); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/common/pass/analyzer/constant_folding.rs: -------------------------------------------------------------------------------- 1 | use crate::common::analyze_resource::ast; 2 | 3 | /// ASTに対する定数畳み込みのメインルーチン 4 | pub fn constant_folding(fn_arena: ast::FnArena, full_ast: &ast::ASTRoot) { 5 | for fn_id in full_ast.funcs.iter() { 6 | if let Ok(ref mut arena) = fn_arena.lock() { 7 | if let Some(ast_fn) = arena.get_mut(*fn_id) { 8 | folding_fn(ast_fn); 9 | } 10 | } 11 | } 12 | } 13 | 14 | fn folding_fn(ast_fn: &mut ast::Function) { 15 | if let Ok(ref mut stmt_arena) = ast_fn.stmt_arena.lock() { 16 | for stmt_id in ast_fn.stmts.iter() { 17 | if let Some(stmt) = stmt_arena.get_mut(*stmt_id) { 18 | *stmt = folding_stmt(ast_fn.expr_arena.clone(), stmt); 19 | } 20 | } 21 | } 22 | } 23 | 24 | fn folding_stmt(expr_arena: ast::ExprArena, ast_stmt: &ast::StatementNode) -> ast::StatementNode { 25 | match ast_stmt.get_kind() { 26 | ast::StatementNodeKind::VARINIT { 27 | ident_name, 28 | type_name, 29 | expr: expr_id, 30 | } => { 31 | let initialize_expr = folding_expr(expr_arena.clone(), *expr_id); 32 | ast::StatementNode::new( 33 | ast::StatementNodeKind::VARINIT { 34 | ident_name: ident_name.clone(), 35 | type_name: type_name.clone(), 36 | expr: expr_arena.lock().unwrap().alloc(initialize_expr), 37 | }, 38 | ast_stmt.get_position(), 39 | ) 40 | } 41 | ast::StatementNodeKind::RETURN { expr: expr_id } => { 42 | let return_expr = folding_expr(expr_arena.clone(), *expr_id); 43 | 44 | ast::StatementNode::new( 45 | ast::StatementNodeKind::RETURN { 46 | expr: expr_arena.lock().unwrap().alloc(return_expr), 47 | }, 48 | ast_stmt.get_position(), 49 | ) 50 | } 51 | ast::StatementNodeKind::EXPR { expr: expr_id } => { 52 | let expr = folding_expr(expr_arena.clone(), *expr_id); 53 | 54 | ast::StatementNode::new( 55 | ast::StatementNodeKind::EXPR { 56 | expr: expr_arena.lock().unwrap().alloc(expr), 57 | }, 58 | ast_stmt.get_position(), 59 | ) 60 | } 61 | _ => ast_stmt.clone(), 62 | } 63 | } 64 | 65 | fn folding_expr(expr_arena: ast::ExprArena, ast_expr_id: ast::ExNodeId) -> ast::ExpressionNode { 66 | let ast_expr = expr_arena.lock().unwrap().get(ast_expr_id).unwrap().clone(); 67 | match ast_expr.get_kind() { 68 | ast::ExpressionNodeKind::ADD { 69 | lhs: lhs_id, 70 | rhs: rhs_id, 71 | } => { 72 | let lhs = folding_expr(expr_arena.clone(), *lhs_id); 73 | let rhs = folding_expr(expr_arena, *rhs_id); 74 | 75 | if lhs.is_integer_literal() && rhs.is_integer_literal() { 76 | return ast::ExpressionNode::new_integer( 77 | lhs.get_integer_value() + rhs.get_integer_value(), 78 | lhs.get_pos(), 79 | ); 80 | } 81 | 82 | ast_expr 83 | } 84 | ast::ExpressionNodeKind::SUB { 85 | lhs: lhs_id, 86 | rhs: rhs_id, 87 | } => { 88 | let lhs = folding_expr(expr_arena.clone(), *lhs_id); 89 | let rhs = folding_expr(expr_arena, *rhs_id); 90 | 91 | if lhs.is_integer_literal() && rhs.is_integer_literal() { 92 | return ast::ExpressionNode::new_integer( 93 | lhs.get_integer_value() - rhs.get_integer_value(), 94 | lhs.get_pos(), 95 | ); 96 | } 97 | 98 | ast_expr 99 | } 100 | ast::ExpressionNodeKind::MUL { 101 | lhs: lhs_id, 102 | rhs: rhs_id, 103 | } => { 104 | let lhs = folding_expr(expr_arena.clone(), *lhs_id); 105 | let rhs = folding_expr(expr_arena, *rhs_id); 106 | 107 | if lhs.is_integer_literal() && rhs.is_integer_literal() { 108 | return ast::ExpressionNode::new_integer( 109 | lhs.get_integer_value() * rhs.get_integer_value(), 110 | lhs.get_pos(), 111 | ); 112 | } 113 | 114 | ast_expr 115 | } 116 | ast::ExpressionNodeKind::DIV { 117 | lhs: lhs_id, 118 | rhs: rhs_id, 119 | } => { 120 | let lhs = folding_expr(expr_arena.clone(), *lhs_id); 121 | let rhs = folding_expr(expr_arena, *rhs_id); 122 | 123 | if lhs.is_integer_literal() && rhs.is_integer_literal() { 124 | return ast::ExpressionNode::new_integer( 125 | lhs.get_integer_value() / rhs.get_integer_value(), 126 | lhs.get_pos(), 127 | ); 128 | } 129 | 130 | ast_expr 131 | } 132 | ast::ExpressionNodeKind::NEG { value: value_id } => { 133 | let value = folding_expr(expr_arena, *value_id); 134 | 135 | if value.is_integer_literal() { 136 | return ast::ExpressionNode::new_integer( 137 | -value.get_integer_value(), 138 | value.get_pos(), 139 | ); 140 | } 141 | 142 | ast_expr 143 | } 144 | ast::ExpressionNodeKind::CALL { names, args } => { 145 | let mut optimized_args: Vec = Vec::new(); 146 | 147 | for arg_id in args.iter() { 148 | let optimized_arg = folding_expr(expr_arena.clone(), *arg_id); 149 | optimized_args.push(expr_arena.lock().unwrap().alloc(optimized_arg)); 150 | } 151 | 152 | ast::ExpressionNode::new_call(names.clone(), optimized_args, ast_expr.get_pos()) 153 | } 154 | _ => ast_expr, 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/common/pass/parser/parser_util.rs: -------------------------------------------------------------------------------- 1 | use crate::common::ast::{ExNodeId, ExpressionNode, StNodeId}; 2 | use crate::common::position::Position; 3 | use crate::common::token::{Token, TokenKind}; 4 | use std::sync::MutexGuard; 5 | 6 | use crate::common::pass::parser::context::Context; 7 | use id_arena::Arena; 8 | 9 | type ChildParser = fn(&mut Context, Vec) -> (ExNodeId, Vec); 10 | type OperatorParser = fn(&mut Context, Vec) -> (Option, Vec); 11 | 12 | impl Context { 13 | /// type -> "Int64" | "Uint64" | "ConstStr" | "Noreturn" | "Boolean" |`*` type | identifier-path 14 | pub fn expect_type(&self, mut tokens: Vec) -> (String, Vec) { 15 | let type_t = head(&tokens); 16 | 17 | match type_t.get_kind() { 18 | TokenKind::INT64 => { 19 | eat_token(&mut tokens); 20 | ("Int64".to_string(), tokens) 21 | } 22 | TokenKind::UINT64 => { 23 | eat_token(&mut tokens); 24 | ("Uint64".to_string(), tokens) 25 | } 26 | TokenKind::CONSTSTR => { 27 | eat_token(&mut tokens); 28 | ("ConstStr".to_string(), tokens) 29 | } 30 | TokenKind::NORETURN => { 31 | eat_token(&mut tokens); 32 | ("Noreturn".to_string(), tokens) 33 | } 34 | TokenKind::BOOLEAN => { 35 | eat_token(&mut tokens); 36 | ("Boolean".to_string(), tokens) 37 | } 38 | 39 | TokenKind::ASTERISK => { 40 | eat_token(&mut tokens); 41 | let (inner_type, rest_tokens) = self.expect_type(tokens); 42 | (format!("*{}", inner_type), rest_tokens) 43 | } 44 | TokenKind::IDENTIFIER { name: _ } => { 45 | let (names, rest_tokens) = expect_identifier(tokens); 46 | ( 47 | format!("{}::{}", self.module_name, names.join("::")), 48 | rest_tokens, 49 | ) 50 | } 51 | _ => panic!("TODO we must compile error when got difference token in expect_type()"), 52 | } 53 | } 54 | 55 | /// block -> `{` statement* `}` 56 | pub fn expect_block(&mut self, mut tokens: Vec) -> (Vec, Vec) { 57 | eat_token(&mut tokens); 58 | 59 | let mut stmts: Vec = Vec::new(); 60 | 61 | loop { 62 | let h = head(&tokens); 63 | 64 | if h.get_kind() == &TokenKind::RBRACE { 65 | eat_token(&mut tokens); 66 | break; 67 | } 68 | 69 | let (st_id, rt) = self.statement(tokens); 70 | stmts.push(st_id); 71 | tokens = rt; 72 | } 73 | 74 | (stmts, tokens) 75 | } 76 | } 77 | 78 | pub fn eat_token(tokens: &mut Vec) { 79 | if tokens.is_empty() { 80 | panic!("cannot remove top of tokens because Vec is empty"); 81 | } 82 | tokens.remove(0); 83 | } 84 | 85 | pub fn head(tokens: &[Token]) -> Token { 86 | if tokens.is_empty() { 87 | return Token::new(TokenKind::EOF, Default::default()); 88 | } 89 | tokens[0].clone() 90 | } 91 | 92 | pub fn current_position(tokens: &[Token]) -> Position { 93 | if tokens.is_empty() { 94 | return Default::default(); 95 | } 96 | tokens[0].get_position() 97 | } 98 | 99 | pub fn expect(expected: TokenKind, tokens: &mut Vec) { 100 | let h = head(tokens); 101 | if h.get_kind() != &expected { 102 | panic!("expected => {:?}, got => {:?}", expected, h.get_kind()); 103 | } 104 | eat_token(tokens); 105 | } 106 | 107 | pub fn consume(expected: TokenKind, tokens: &mut Vec) -> bool { 108 | let h = head(tokens); 109 | if h.get_kind() != &expected { 110 | return false; 111 | } 112 | eat_token(tokens); 113 | 114 | true 115 | } 116 | 117 | pub fn operator_parser( 118 | operators: Vec, 119 | mut tokens: Vec, 120 | ) -> (Option, Vec) { 121 | let head = head(&tokens); 122 | 123 | for tk in operators.iter() { 124 | if tk == head.get_kind() { 125 | eat_token(&mut tokens); 126 | 127 | return (Some(tk.clone()), tokens); 128 | } 129 | } 130 | 131 | (None, tokens) 132 | } 133 | 134 | pub fn binary_operation_parser( 135 | operator_parser: OperatorParser, 136 | child_parser: ChildParser, 137 | ctxt: &mut Context, 138 | tokens: Vec, 139 | ) -> (ExNodeId, Vec) { 140 | let (mut lhs_id, mut rest_tokens) = child_parser(ctxt, tokens); 141 | 142 | loop { 143 | let op_pos = current_position(&rest_tokens); 144 | let (op, rk) = operator_parser(ctxt, rest_tokens); 145 | rest_tokens = rk; 146 | match op { 147 | Some(op) => { 148 | let (rhs_id, rk) = child_parser(ctxt, rest_tokens.clone()); 149 | rest_tokens = rk; 150 | lhs_id = 151 | alloc_binop_node(ctxt.expr_arena.lock().unwrap(), &op, lhs_id, rhs_id, op_pos); 152 | } 153 | None => break, 154 | } 155 | } 156 | 157 | (lhs_id, rest_tokens) 158 | } 159 | 160 | /// 二項演算ノードのアロケート 161 | pub fn alloc_binop_node( 162 | mut arena: MutexGuard>, 163 | k: &TokenKind, 164 | lhs: ExNodeId, 165 | rhs: ExNodeId, 166 | pos: Position, 167 | ) -> ExNodeId { 168 | arena.alloc(ExpressionNode::new_binop(k, lhs, rhs, pos)) 169 | } 170 | 171 | /// identifier_path -> identifier (`::` identifier)* 172 | pub fn expect_identifier(mut tokens: Vec) -> (Vec, Vec) { 173 | let base = head(&tokens); 174 | // primary() で識別子であることはチェック済みなのでcopy_name()を読んで良い 175 | let mut names = vec![base.copy_name()]; 176 | 177 | eat_token(&mut tokens); 178 | loop { 179 | let next = head(&tokens); 180 | match next.get_kind() { 181 | TokenKind::DOUBLECOLON => { 182 | eat_token(&mut tokens); 183 | let ident = head(&tokens); 184 | names.push(ident.copy_name()); 185 | eat_token(&mut tokens); 186 | } 187 | _ => break, 188 | } 189 | } 190 | 191 | (names, tokens) 192 | } 193 | 194 | #[cfg(test)] 195 | mod parser_util_tests { 196 | use super::*; 197 | 198 | #[test] 199 | fn expect_identifier_test() { 200 | let tokens = vec![ 201 | Token::new_identifier("std".to_string(), Default::default()), 202 | Token::new(TokenKind::DOUBLECOLON, Default::default()), 203 | Token::new_identifier("os".to_string(), Default::default()), 204 | Token::new(TokenKind::DOUBLECOLON, Default::default()), 205 | Token::new_identifier("FileDescriptor".to_string(), Default::default()), 206 | Token::new(TokenKind::EOF, Default::default()), 207 | ]; 208 | 209 | let (names, rest_tokens) = expect_identifier(tokens); 210 | assert_eq!(1, rest_tokens.len()); 211 | assert_eq!( 212 | vec![ 213 | "std".to_string(), 214 | "os".to_string(), 215 | "FileDescriptor".to_string() 216 | ], 217 | names 218 | ); 219 | } 220 | 221 | #[test] 222 | fn expect_type_test() {} 223 | 224 | #[test] 225 | fn expect_block_test() {} 226 | } 227 | -------------------------------------------------------------------------------- /src/common/analyze_resource/token/tokenkind.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Display, Formatter, Result as FR}; 2 | 3 | /// トークンの種類 4 | #[allow(dead_code)] 5 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] 6 | pub enum TokenKind { 7 | /// 整数リテラル( `100` ) 8 | Integer { value: i64 }, 9 | 10 | /// 非符号付き整数リテラル( `100u` ) 11 | UNSIGNEDINTEGER { value: u64 }, 12 | 13 | // TODO: あとでIDにするかも 14 | /// 文字列リテラル( `"Drumato"` ) 15 | STRLIT { contents: String }, 16 | 17 | /// 識別子( `drumato` ) 18 | IDENTIFIER { name: String }, 19 | 20 | // 記号 21 | /// `+` 22 | PLUS, 23 | /// `-` 24 | MINUS, 25 | /// `*` 26 | ASTERISK, 27 | /// `/` 28 | SLASH, 29 | /// `//` 30 | DOUBLESLASH, 31 | /// `&` 32 | AMPERSAND, 33 | /// `(` 34 | LPAREN, 35 | /// `)` 36 | RPAREN, 37 | /// `{` 38 | LBRACE, 39 | /// `}` 40 | RBRACE, 41 | /// `:` 42 | COLON, 43 | /// `::` 44 | DOUBLECOLON, 45 | /// `->` 46 | ARROW, 47 | /// `;` 48 | SEMICOLON, 49 | /// `=` 50 | ASSIGN, 51 | /// ` ` 52 | BLANK, 53 | /// `\n` 54 | NEWLINE, 55 | /// `,` 56 | COMMA, 57 | /// `.` 58 | DOT, 59 | /// `(EOF)` 60 | EOF, 61 | /// `(COMMENT)` 62 | COMMENT { contents: String }, 63 | 64 | // 予約語 65 | /// `asm` 66 | ASM, 67 | /// `begin` 68 | BEGIN, 69 | /// `Boolean` 70 | BOOLEAN, 71 | /// `const` 72 | CONST, 73 | /// `ConstStr` 74 | CONSTSTR, 75 | /// `countup` 76 | COUNTUP, 77 | /// `declare` 78 | DECLARE, 79 | /// `else` 80 | ELSE, 81 | /// `exclude` 82 | EXCLUDE, 83 | /// `false` 84 | FALSE, 85 | /// `func` 86 | FUNC, 87 | /// `if` 88 | IF, 89 | /// `ifret` 90 | IFRET, 91 | /// `Int64` 92 | INT64, 93 | /// `import` 94 | IMPORT, 95 | /// `match` 96 | MATCH, 97 | /// `Noreturm` 98 | NORETURN, 99 | /// `pubenum` 100 | PUBENUM, 101 | /// `pubtype` 102 | PUBTYPE, 103 | /// `pubconst` 104 | PUBCONST, 105 | /// `return` 106 | RETURN, 107 | /// `asm` 108 | STRUCT, 109 | /// `true` 110 | TRUE, 111 | /// `Uint64` 112 | UINT64, 113 | /// `varinit` 114 | VARINIT, 115 | } 116 | 117 | impl Display for TokenKind { 118 | fn fmt(&self, f: &mut Formatter<'_>) -> FR { 119 | let s = match self { 120 | TokenKind::Integer { value } => value.to_string(), 121 | 122 | TokenKind::UNSIGNEDINTEGER { value } => value.to_string(), 123 | 124 | TokenKind::STRLIT { contents } => format!("\"{}\"", contents), 125 | 126 | TokenKind::IDENTIFIER { name } => name.to_string(), 127 | 128 | // 記号 129 | TokenKind::PLUS => "+".to_string(), 130 | TokenKind::MINUS => "-".to_string(), 131 | TokenKind::ASTERISK => "*".to_string(), 132 | TokenKind::SLASH => "/".to_string(), 133 | TokenKind::DOUBLESLASH => "//".to_string(), 134 | TokenKind::AMPERSAND => "&".to_string(), 135 | TokenKind::LPAREN => "(".to_string(), 136 | TokenKind::RPAREN => ")".to_string(), 137 | TokenKind::LBRACE => "{".to_string(), 138 | TokenKind::RBRACE => "}".to_string(), 139 | TokenKind::COLON => ":".to_string(), 140 | TokenKind::DOUBLECOLON => "::".to_string(), 141 | TokenKind::ARROW => "->".to_string(), 142 | TokenKind::SEMICOLON => ";".to_string(), 143 | TokenKind::ASSIGN => "=".to_string(), 144 | TokenKind::BLANK => "(BLANK)".to_string(), 145 | TokenKind::NEWLINE => "(NEWLINE)".to_string(), 146 | TokenKind::COMMA => ",".to_string(), 147 | TokenKind::DOT => ".".to_string(), 148 | TokenKind::EOF => "(EOF)".to_string(), 149 | TokenKind::COMMENT { contents: _ } => "(COMMENT)".to_string(), 150 | 151 | // 予約語 152 | TokenKind::ASM => "asm".to_string(), 153 | TokenKind::BEGIN => "begin".to_string(), 154 | TokenKind::BOOLEAN => "Boolean".to_string(), 155 | TokenKind::CONST => "const".to_string(), 156 | TokenKind::CONSTSTR => "ConstStr".to_string(), 157 | TokenKind::COUNTUP => "countup".to_string(), 158 | TokenKind::DECLARE => "declare".to_string(), 159 | TokenKind::ELSE => "else".to_string(), 160 | TokenKind::EXCLUDE => "exclude".to_string(), 161 | TokenKind::FALSE => "false".to_string(), 162 | TokenKind::FUNC => "func".to_string(), 163 | TokenKind::IF => "if".to_string(), 164 | TokenKind::IFRET => "ifret".to_string(), 165 | TokenKind::IMPORT => "import".to_string(), 166 | TokenKind::INT64 => "Int64".to_string(), 167 | TokenKind::MATCH => "match".to_string(), 168 | TokenKind::NORETURN => "Noreturn".to_string(), 169 | TokenKind::PUBENUM => "pubenum".to_string(), 170 | TokenKind::PUBTYPE => "pubtype".to_string(), 171 | TokenKind::PUBCONST => "pubconst".to_string(), 172 | TokenKind::RETURN => "return".to_string(), 173 | TokenKind::STRUCT => "struct".to_string(), 174 | TokenKind::TRUE => "true".to_string(), 175 | TokenKind::UINT64 => "Uint64".to_string(), 176 | TokenKind::VARINIT => "varinit".to_string(), 177 | }; 178 | 179 | write!(f, "{}", s) 180 | } 181 | } 182 | 183 | impl TokenKind { 184 | pub fn new_symbol_from_str(s: &str) -> Self { 185 | match s { 186 | "+" => TokenKind::PLUS, 187 | "-" => TokenKind::MINUS, 188 | "*" => TokenKind::ASTERISK, 189 | "/" => TokenKind::SLASH, 190 | "//" => TokenKind::DOUBLESLASH, 191 | "(" => TokenKind::LPAREN, 192 | ")" => TokenKind::RPAREN, 193 | "{" => TokenKind::LBRACE, 194 | "}" => TokenKind::RBRACE, 195 | ":" => TokenKind::COLON, 196 | "::" => TokenKind::DOUBLECOLON, 197 | "->" => TokenKind::ARROW, 198 | "=" => TokenKind::ASSIGN, 199 | "," => TokenKind::COMMA, 200 | ";" => TokenKind::SEMICOLON, 201 | "&" => TokenKind::AMPERSAND, 202 | "." => TokenKind::DOT, 203 | _ => panic!("invalid tokenkind from {}", s), 204 | } 205 | } 206 | pub fn try_new_keyword(s: &str) -> Option { 207 | match s { 208 | "asm" => Some(TokenKind::ASM), 209 | "begin" => Some(TokenKind::BEGIN), 210 | "Boolean" => Some(TokenKind::BOOLEAN), 211 | "const" => Some(TokenKind::CONST), 212 | "ConstStr" => Some(TokenKind::CONSTSTR), 213 | "countup" => Some(TokenKind::COUNTUP), 214 | "declare" => Some(TokenKind::DECLARE), 215 | "else" => Some(TokenKind::ELSE), 216 | "exclude" => Some(TokenKind::EXCLUDE), 217 | "false" => Some(TokenKind::FALSE), 218 | "func" => Some(TokenKind::FUNC), 219 | "if" => Some(TokenKind::IF), 220 | "ifret" => Some(TokenKind::IFRET), 221 | "import" => Some(TokenKind::IMPORT), 222 | "Int64" => Some(TokenKind::INT64), 223 | "match" => Some(TokenKind::MATCH), 224 | "Noreturn" => Some(TokenKind::NORETURN), 225 | "pubenum" => Some(TokenKind::PUBENUM), 226 | "pubtype" => Some(TokenKind::PUBTYPE), 227 | "return" => Some(TokenKind::RETURN), 228 | "struct" => Some(TokenKind::STRUCT), 229 | "true" => Some(TokenKind::TRUE), 230 | "Uint64" => Some(TokenKind::UINT64), 231 | "varinit" => Some(TokenKind::VARINIT), 232 | "pubconst" => Some(TokenKind::PUBCONST), 233 | _ => None, 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /src/common/three_address_code/code_kind.rs: -------------------------------------------------------------------------------- 1 | use crate::common::three_address_code::function::ValueArena; 2 | use crate::common::three_address_code::ValueId; 3 | 4 | /// Codeの種類 5 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] 6 | pub enum CodeKind { 7 | ADD { 8 | lop: ValueId, 9 | rop: ValueId, 10 | result: ValueId, 11 | }, 12 | SUB { 13 | lop: ValueId, 14 | rop: ValueId, 15 | result: ValueId, 16 | }, 17 | MUL { 18 | lop: ValueId, 19 | rop: ValueId, 20 | result: ValueId, 21 | }, 22 | DIV { 23 | lop: ValueId, 24 | rop: ValueId, 25 | result: ValueId, 26 | }, 27 | ASSIGN { 28 | value: ValueId, 29 | result: ValueId, 30 | }, 31 | STORE { 32 | value: ValueId, 33 | result: ValueId, 34 | }, 35 | NEG { 36 | value: ValueId, 37 | result: ValueId, 38 | }, 39 | ADDRESSOF { 40 | value: ValueId, 41 | result: ValueId, 42 | }, 43 | DEREFERENCE { 44 | value: ValueId, 45 | result: ValueId, 46 | }, 47 | MEMBER { 48 | id: ValueId, 49 | member: String, 50 | result: ValueId, 51 | }, 52 | RETURN { 53 | value: ValueId, 54 | }, 55 | PARAM { 56 | value: ValueId, 57 | }, 58 | CALL { 59 | name: ValueId, 60 | result: ValueId, 61 | }, 62 | ALLOC { 63 | temp: ValueId, 64 | }, 65 | LABEL { 66 | name: String, 67 | }, 68 | JUMPIFFALSE { 69 | label: String, 70 | cond_result: ValueId, 71 | }, 72 | JUMP { 73 | label: String, 74 | }, 75 | ASM { 76 | value: ValueId, 77 | }, 78 | } 79 | 80 | impl CodeKind { 81 | fn unop(operator: &str, result: &ValueId, value: &ValueId, value_arena: ValueArena) -> String { 82 | let res = value_arena 83 | .lock() 84 | .unwrap() 85 | .get(*result) 86 | .unwrap() 87 | .clone() 88 | .dump(); 89 | let value = value_arena 90 | .lock() 91 | .unwrap() 92 | .get(*value) 93 | .unwrap() 94 | .clone() 95 | .dump(); 96 | 97 | format!("{} <- {} {}", res, operator, value) 98 | } 99 | fn binop( 100 | operator: &str, 101 | result: &ValueId, 102 | lop: &ValueId, 103 | rop: &ValueId, 104 | value_arena: ValueArena, 105 | ) -> String { 106 | let res = value_arena 107 | .lock() 108 | .unwrap() 109 | .get(*result) 110 | .unwrap() 111 | .clone() 112 | .dump(); 113 | let lop = value_arena 114 | .lock() 115 | .unwrap() 116 | .get(*lop) 117 | .unwrap() 118 | .clone() 119 | .dump(); 120 | let rop = value_arena 121 | .lock() 122 | .unwrap() 123 | .get(*rop) 124 | .unwrap() 125 | .clone() 126 | .dump(); 127 | 128 | format!("{} <- {} {} {}", res, lop, operator, rop) 129 | } 130 | pub fn dump(&self, value_arena: ValueArena) -> String { 131 | match self { 132 | CodeKind::ADD { lop, rop, result } => Self::binop("+", result, lop, rop, value_arena), 133 | CodeKind::SUB { lop, rop, result } => Self::binop("-", result, lop, rop, value_arena), 134 | CodeKind::MUL { lop, rop, result } => Self::binop("*", result, lop, rop, value_arena), 135 | CodeKind::DIV { lop, rop, result } => Self::binop("/", result, lop, rop, value_arena), 136 | CodeKind::ASSIGN { value, result } => Self::unop("", result, value, value_arena), 137 | CodeKind::STORE { value, result } => { 138 | let result = value_arena 139 | .lock() 140 | .unwrap() 141 | .get(*result) 142 | .unwrap() 143 | .clone() 144 | .dump(); 145 | let value = value_arena 146 | .lock() 147 | .unwrap() 148 | .get(*value) 149 | .unwrap() 150 | .clone() 151 | .dump(); 152 | format!("store {} into {}", value, result) 153 | } 154 | CodeKind::NEG { value, result } => Self::unop("-", result, value, value_arena), 155 | CodeKind::ADDRESSOF { value, result } => Self::unop("&", result, value, value_arena), 156 | CodeKind::DEREFERENCE { value, result } => Self::unop("*", result, value, value_arena), 157 | CodeKind::MEMBER { id, member, result } => { 158 | let res = value_arena 159 | .lock() 160 | .unwrap() 161 | .get(*result) 162 | .unwrap() 163 | .clone() 164 | .dump(); 165 | let id = value_arena.lock().unwrap().get(*id).unwrap().clone().dump(); 166 | 167 | format!("{} <- {}.{}", res, id, member) 168 | } 169 | CodeKind::RETURN { value } => { 170 | let ret_value = value_arena 171 | .lock() 172 | .unwrap() 173 | .get(*value) 174 | .unwrap() 175 | .clone() 176 | .dump(); 177 | format!("return {}", ret_value) 178 | } 179 | CodeKind::PARAM { value } => { 180 | let arg_value = value_arena 181 | .lock() 182 | .unwrap() 183 | .get(*value) 184 | .unwrap() 185 | .clone() 186 | .dump(); 187 | format!("param {}", arg_value) 188 | } 189 | CodeKind::CALL { name, result } => { 190 | let result = value_arena 191 | .lock() 192 | .unwrap() 193 | .get(*result) 194 | .unwrap() 195 | .clone() 196 | .dump(); 197 | let name = value_arena 198 | .lock() 199 | .unwrap() 200 | .get(*name) 201 | .unwrap() 202 | .clone() 203 | .dump(); 204 | format!("{} <- call {}", result, name) 205 | } 206 | CodeKind::ALLOC { temp } => { 207 | let allocated = value_arena 208 | .lock() 209 | .unwrap() 210 | .get(*temp) 211 | .unwrap() 212 | .clone() 213 | .dump(); 214 | format!("alloc {}", allocated,) 215 | } 216 | CodeKind::LABEL { name } => format!("label {}", name,), 217 | CodeKind::JUMPIFFALSE { label, cond_result } => { 218 | let cond = value_arena 219 | .lock() 220 | .unwrap() 221 | .get(*cond_result) 222 | .unwrap() 223 | .clone() 224 | .dump(); 225 | format!("jump {} if not {}", label, cond,) 226 | } 227 | CodeKind::JUMP { label } => format!("jump {}", label,), 228 | CodeKind::ASM { value } => { 229 | let v = value_arena 230 | .lock() 231 | .unwrap() 232 | .get(*value) 233 | .unwrap() 234 | .clone() 235 | .dump(); 236 | format!("asm {}", v) 237 | } 238 | } 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /src/common/analyze_resource/peachili_type.rs: -------------------------------------------------------------------------------- 1 | use crate::common::option::Target; 2 | use std::collections::BTreeMap; 3 | 4 | /// 型 5 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] 6 | pub struct Type { 7 | /// 型の種類 8 | pub kind: TypeKind, 9 | /// 型のサイズ 10 | pub size: usize, 11 | } 12 | 13 | impl Type { 14 | pub fn dump(&self) -> String { 15 | match &self.kind { 16 | TypeKind::BOOLEAN => "Boolean".to_string(), 17 | TypeKind::CONSTSTR => "ConstStr".to_string(), 18 | TypeKind::INT64 => "Int64".to_string(), 19 | TypeKind::UINT64 => "Uint64".to_string(), 20 | TypeKind::NORETURN => "Noreturn".to_string(), 21 | TypeKind::FUNCTION { return_type } => format!("func() {}", return_type.dump()), 22 | TypeKind::POINTER { to } => format!("*{}", to.dump()), 23 | TypeKind::STRUCT { members } => { 24 | let mut type_strs = Vec::new(); 25 | 26 | for (member_name, (member_type, _offset)) in members.iter() { 27 | type_strs.push(format!("{}: {}", member_name, member_type.dump())); 28 | } 29 | 30 | format!("{{ {} }}", type_strs.join(", ")) 31 | } 32 | TypeKind::CONST { 33 | const_type, 34 | value: _, 35 | } => const_type.dump(), 36 | TypeKind::ENUM => "enum".to_string(), 37 | } 38 | } 39 | /// 関数型サイズ 40 | pub fn new_function(ret_ty: Type) -> Self { 41 | Self { 42 | kind: TypeKind::FUNCTION { 43 | return_type: Box::new(ret_ty), 44 | }, 45 | size: 0, 46 | } 47 | } 48 | 49 | pub fn can_be_constant(&self) -> bool { 50 | match self.kind { 51 | TypeKind::BOOLEAN | TypeKind::INT64 | TypeKind::UINT64 => true, 52 | _ => false, 53 | } 54 | } 55 | 56 | pub fn size(&self, target: Target) -> usize { 57 | match self.kind { 58 | TypeKind::BOOLEAN => Self::boolean_size(target), 59 | TypeKind::CONSTSTR => Self::conststr_size(target), 60 | TypeKind::INT64 => Self::int64_size(target), 61 | TypeKind::UINT64 => Self::uint64_size(target), 62 | TypeKind::ENUM => 8, 63 | _ => unreachable!(), 64 | } 65 | } 66 | 67 | /// Int64型サイズ 68 | pub fn int64_size(target: Target) -> usize { 69 | match target { 70 | Target::X86_64 => 8, 71 | Target::AARCH64 => 8, 72 | } 73 | } 74 | 75 | /// Uint64型サイズ 76 | pub fn uint64_size(target: Target) -> usize { 77 | match target { 78 | Target::X86_64 => 8, 79 | Target::AARCH64 => 8, 80 | } 81 | } 82 | 83 | /// ポインタ型サイズ 84 | pub fn pointer_size(target: Target) -> usize { 85 | match target { 86 | Target::X86_64 => 8, 87 | Target::AARCH64 => 8, 88 | } 89 | } 90 | /// Boolean型サイズ 91 | pub fn boolean_size(target: Target) -> usize { 92 | match target { 93 | Target::X86_64 => 8, 94 | Target::AARCH64 => 8, 95 | } 96 | } 97 | /// ConstStr 98 | pub fn conststr_size(target: Target) -> usize { 99 | match target { 100 | Target::X86_64 => 8, 101 | Target::AARCH64 => 8, 102 | } 103 | } 104 | 105 | /// Int64型を新たに割り当てる 106 | pub fn new_int64(target: Target) -> Self { 107 | Self { 108 | kind: TypeKind::INT64, 109 | size: Self::int64_size(target), 110 | } 111 | } 112 | 113 | /// Uint64型を新たに割り当てる 114 | pub fn new_uint64(target: Target) -> Self { 115 | Self { 116 | kind: TypeKind::UINT64, 117 | size: Self::uint64_size(target), 118 | } 119 | } 120 | /// Boolean型を新たに割り当てる 121 | pub fn new_boolean(target: Target) -> Self { 122 | Self { 123 | kind: TypeKind::BOOLEAN, 124 | size: Self::boolean_size(target), 125 | } 126 | } 127 | /// Noreturn型 128 | pub fn new_noreturn() -> Self { 129 | Self { 130 | kind: TypeKind::NORETURN, 131 | size: 0, 132 | } 133 | } 134 | /// ConstStr 135 | pub fn new_const_str(target: Target) -> Self { 136 | Self { 137 | kind: TypeKind::CONSTSTR, 138 | size: Self::conststr_size(target), 139 | } 140 | } 141 | pub fn new_const(const_type: Type, expr: String, target: Target) -> Self { 142 | let size = const_type.size(target); 143 | Self { 144 | kind: TypeKind::CONST { 145 | const_type: Box::new(const_type), 146 | value: expr, 147 | }, 148 | size, 149 | } 150 | } 151 | 152 | /// ポインタ型を新たに割り当てる 153 | pub fn new_pointer(to: Self, target: Target) -> Self { 154 | Self { 155 | kind: TypeKind::POINTER { to: Box::new(to) }, 156 | size: Self::pointer_size(target), 157 | } 158 | } 159 | 160 | /// 構造体型型を新たに割り当てる 161 | pub fn new_struct(members: BTreeMap, usize)>, total_size: usize) -> Self { 162 | Self { 163 | kind: TypeKind::STRUCT { members }, 164 | size: total_size, 165 | } 166 | } 167 | pub fn new_enum(size: usize) -> Self { 168 | Self { 169 | kind: TypeKind::ENUM, 170 | size, 171 | } 172 | } 173 | 174 | /// 構造体型であるか 175 | pub fn is_struct(&self) -> bool { 176 | match self.kind { 177 | TypeKind::STRUCT { members: _ } => true, 178 | _ => false, 179 | } 180 | } 181 | /// stantであるか 182 | pub fn is_constant(&self) -> bool { 183 | match self.kind { 184 | TypeKind::CONST { 185 | const_type: _, 186 | value: _, 187 | } => true, 188 | _ => false, 189 | } 190 | } 191 | /// 関数型であるか 192 | pub fn is_function(&self) -> bool { 193 | match &self.kind { 194 | TypeKind::FUNCTION { return_type: _ } => true, 195 | _ => false, 196 | } 197 | } 198 | /// ポインタ型であると解釈し, 指す型を取り出す 199 | pub fn pointer_to(&self) -> &Type { 200 | match &self.kind { 201 | TypeKind::POINTER { to } => to, 202 | _ => panic!("cannot call pointer_to() with not a pointer"), 203 | } 204 | } 205 | 206 | /// 定数であると解釈し,式文字列を取得する 207 | pub fn get_const_value(&self) -> String { 208 | match &self.kind { 209 | TypeKind::CONST { 210 | const_type: _, 211 | value, 212 | } => value.to_string(), 213 | _ => panic!("cannot call get_const_value() with not a constant"), 214 | } 215 | } 216 | pub fn get_const_type(&self) -> &Type { 217 | match &self.kind { 218 | TypeKind::CONST { 219 | const_type, 220 | value: _, 221 | } => const_type, 222 | _ => panic!("cannot call get_const_type() with not a constant"), 223 | } 224 | } 225 | 226 | /// 構造体型であると解釈し, メンバを取り出す 227 | pub fn get_members(&self) -> &BTreeMap, usize)> { 228 | match &self.kind { 229 | TypeKind::STRUCT { members } => members, 230 | _ => panic!("cannot call get_members() with not a struct"), 231 | } 232 | } 233 | } 234 | 235 | /// 型の種類 236 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] 237 | pub enum TypeKind { 238 | /// 64bit整数 239 | INT64, 240 | /// 64bit非符号付き整数 241 | UINT64, 242 | /// ポインタ 243 | POINTER { 244 | to: Box, 245 | }, 246 | /// ConstStr 247 | CONSTSTR, 248 | /// Boolean 249 | BOOLEAN, 250 | /// Noreturn 251 | NORETURN, 252 | /// 構造体型 253 | STRUCT { 254 | /// member_name -> (member_type, member_offset) 255 | members: BTreeMap, usize)>, 256 | }, 257 | /// 関数型 258 | FUNCTION { 259 | return_type: Box, 260 | // args 261 | }, 262 | /// 定数 263 | CONST { 264 | const_type: Box, 265 | value: String, 266 | }, 267 | ENUM, 268 | } 269 | -------------------------------------------------------------------------------- /src/common/pass/parser/statement.rs: -------------------------------------------------------------------------------- 1 | use crate::common::pass::parser::context::Context; 2 | use crate::common::pass::parser::parser_util; 3 | use crate::common::{ 4 | ast::{ExNodeId, StNodeId, StatementNode, StatementNodeKind}, 5 | token::{Token, TokenKind}, 6 | }; 7 | use std::collections::BTreeMap; 8 | 9 | impl Context { 10 | /// statement -> return_st | ifret_st | declare_st | countup_st| block_st | asm_st 11 | pub fn statement(&mut self, tokens: Vec) -> (StNodeId, Vec) { 12 | let head = parser_util::head(&tokens); 13 | 14 | match head.get_kind() { 15 | TokenKind::MATCH => self.match_statement(tokens), 16 | TokenKind::RETURN => self.return_statement(tokens), 17 | TokenKind::IFRET => self.ifret_statement(tokens), 18 | TokenKind::DECLARE => self.declare_statement(tokens), 19 | TokenKind::COUNTUP => self.countup_statement(tokens), 20 | TokenKind::ASM => self.asm_statement(tokens), 21 | TokenKind::VARINIT => self.varinit_statement(tokens), 22 | TokenKind::CONST => self.const_statement(tokens), 23 | _ => self.expression_statement(tokens), 24 | } 25 | } 26 | 27 | /// match_statement -> "match" expression `{` pattern* `}` 28 | fn match_statement(&mut self, mut tokens: Vec) -> (StNodeId, Vec) { 29 | let stmt_pos = parser_util::current_position(&tokens); 30 | parser_util::eat_token(&mut tokens); 31 | 32 | let (ex_id, mut rest_tokens) = self.expression(tokens); 33 | parser_util::expect(TokenKind::LBRACE, &mut rest_tokens); 34 | 35 | let mut arms = BTreeMap::new(); 36 | 37 | loop { 38 | if parser_util::consume(TokenKind::RBRACE, &mut rest_tokens) { 39 | break; 40 | } 41 | 42 | let (pattern_name, r) = parser_util::expect_identifier(rest_tokens); 43 | rest_tokens = r; 44 | 45 | parser_util::expect(TokenKind::ARROW, &mut rest_tokens); 46 | 47 | let (stmts, r) = self.expect_block(rest_tokens); 48 | rest_tokens = r; 49 | 50 | arms.insert(pattern_name.join("::"), stmts); 51 | 52 | parser_util::expect(TokenKind::COMMA, &mut rest_tokens); 53 | } 54 | 55 | parser_util::expect(TokenKind::SEMICOLON, &mut rest_tokens); 56 | ( 57 | self.stmt_arena.lock().unwrap().alloc(StatementNode::new( 58 | StatementNodeKind::MATCH { expr: ex_id, arms }, 59 | stmt_pos, 60 | )), 61 | rest_tokens, 62 | ) 63 | } 64 | 65 | /// return_statement -> "return" expression `;` 66 | fn return_statement(&mut self, mut tokens: Vec) -> (StNodeId, Vec) { 67 | let stmt_pos = parser_util::current_position(&tokens); 68 | parser_util::eat_token(&mut tokens); 69 | 70 | let (ex_id, mut rest_tokens) = self.expression(tokens); 71 | parser_util::expect(TokenKind::SEMICOLON, &mut rest_tokens); 72 | 73 | ( 74 | self.stmt_arena.lock().unwrap().alloc(StatementNode::new( 75 | StatementNodeKind::RETURN { expr: ex_id }, 76 | stmt_pos, 77 | )), 78 | rest_tokens, 79 | ) 80 | } 81 | 82 | /// ifret_statement -> "ifret" expression `;` 83 | fn ifret_statement(&mut self, mut tokens: Vec) -> (StNodeId, Vec) { 84 | let stmt_pos = parser_util::current_position(&tokens); 85 | parser_util::eat_token(&mut tokens); 86 | 87 | let (ex_id, mut rest_tokens) = self.expression(tokens); 88 | parser_util::expect(TokenKind::SEMICOLON, &mut rest_tokens); 89 | 90 | ( 91 | self.stmt_arena.lock().unwrap().alloc(StatementNode::new( 92 | StatementNodeKind::IFRET { expr: ex_id }, 93 | stmt_pos, 94 | )), 95 | rest_tokens, 96 | ) 97 | } 98 | 99 | /// declare_statement -> "declare" identifier identifier `;` 100 | fn declare_statement(&mut self, mut tokens: Vec) -> (StNodeId, Vec) { 101 | let stmt_pos = parser_util::current_position(&tokens); 102 | parser_util::eat_token(&mut tokens); 103 | 104 | let (declared_names, mut rest_tokens) = parser_util::expect_identifier(tokens); 105 | let (type_name, rt) = self.expect_type(rest_tokens); 106 | rest_tokens = rt; 107 | parser_util::expect(TokenKind::SEMICOLON, &mut rest_tokens); 108 | 109 | ( 110 | self.stmt_arena.lock().unwrap().alloc(StatementNode::new( 111 | StatementNodeKind::DECLARE { 112 | ident_name: declared_names[0].clone(), 113 | type_name, 114 | }, 115 | stmt_pos, 116 | )), 117 | rest_tokens, 118 | ) 119 | } 120 | 121 | /// countup_statement -> "countup" identifier "begin" expression "exclude" expression block_statement `;` 122 | fn countup_statement(&mut self, mut tokens: Vec) -> (StNodeId, Vec) { 123 | let stmt_pos = parser_util::current_position(&tokens); 124 | parser_util::eat_token(&mut tokens); 125 | 126 | let (ident_names, mut rest_tokens) = parser_util::expect_identifier(tokens); 127 | let ident_name = ident_names[0].clone(); 128 | parser_util::expect(TokenKind::BEGIN, &mut rest_tokens); 129 | 130 | let (e1_id, mut rest_tokens) = self.expression(rest_tokens); 131 | 132 | parser_util::expect(TokenKind::EXCLUDE, &mut rest_tokens); 133 | let (e2_id, rest_tokens) = self.expression(rest_tokens); 134 | 135 | let (stmts, mut rest_tokens) = self.expect_block(rest_tokens); 136 | parser_util::expect(TokenKind::SEMICOLON, &mut rest_tokens); 137 | 138 | ( 139 | self.stmt_arena.lock().unwrap().alloc(StatementNode::new( 140 | StatementNodeKind::COUNTUP { 141 | ident_name, 142 | begin_ex: e1_id, 143 | endpoint_ex: e2_id, 144 | body: stmts, 145 | }, 146 | stmt_pos, 147 | )), 148 | rest_tokens, 149 | ) 150 | } 151 | 152 | /// expression_statement -> expression `;` 153 | fn expression_statement(&mut self, tokens: Vec) -> (StNodeId, Vec) { 154 | let stmt_pos = parser_util::current_position(&tokens); 155 | let (ex_id, mut rest_tokens) = self.expression(tokens); 156 | parser_util::expect(TokenKind::SEMICOLON, &mut rest_tokens); 157 | 158 | ( 159 | self.stmt_arena.lock().unwrap().alloc(StatementNode::new( 160 | StatementNodeKind::EXPR { expr: ex_id }, 161 | stmt_pos, 162 | )), 163 | rest_tokens, 164 | ) 165 | } 166 | 167 | /// asm_st -> "asm" block `;` 168 | fn asm_statement(&mut self, mut tokens: Vec) -> (StNodeId, Vec) { 169 | let stmt_pos = parser_util::current_position(&tokens); 170 | parser_util::eat_token(&mut tokens); 171 | 172 | let (stmts, mut rest_tokens) = self.expect_block(tokens); 173 | parser_util::expect(TokenKind::SEMICOLON, &mut rest_tokens); 174 | 175 | ( 176 | self.stmt_arena.lock().unwrap().alloc(StatementNode::new( 177 | StatementNodeKind::ASM { stmts }, 178 | stmt_pos, 179 | )), 180 | rest_tokens, 181 | ) 182 | } 183 | 184 | /// varinit -> "varinit" identifier type `=` expression `;` 185 | fn varinit_statement(&mut self, tokens: Vec) -> (StNodeId, Vec) { 186 | let stmt_pos = parser_util::current_position(&tokens); 187 | let (ident, type_name, ex_id, rest_tokens) = self.initialize_statement(tokens); 188 | 189 | ( 190 | self.stmt_arena.lock().unwrap().alloc(StatementNode::new( 191 | StatementNodeKind::VARINIT { 192 | ident_name: ident, 193 | type_name, 194 | expr: ex_id, 195 | }, 196 | stmt_pos, 197 | )), 198 | rest_tokens, 199 | ) 200 | } 201 | 202 | /// const -> "const" identifier type `=` expression `;` 203 | fn const_statement(&mut self, tokens: Vec) -> (StNodeId, Vec) { 204 | let stmt_pos = parser_util::current_position(&tokens); 205 | let (ident, type_name, ex_id, rest_tokens) = self.initialize_statement(tokens); 206 | 207 | ( 208 | self.stmt_arena.lock().unwrap().alloc(StatementNode::new( 209 | StatementNodeKind::CONST { 210 | ident_name: ident, 211 | type_name, 212 | expr: ex_id, 213 | }, 214 | stmt_pos, 215 | )), 216 | rest_tokens, 217 | ) 218 | } 219 | 220 | fn initialize_statement( 221 | &mut self, 222 | mut tokens: Vec, 223 | ) -> (String, String, ExNodeId, Vec) { 224 | parser_util::eat_token(&mut tokens); 225 | 226 | let (declared_names, mut rest_tokens) = parser_util::expect_identifier(tokens); 227 | let (type_name, rt) = self.expect_type(rest_tokens); 228 | rest_tokens = rt; 229 | 230 | parser_util::expect(TokenKind::ASSIGN, &mut rest_tokens); 231 | 232 | let (ex_id, mut rest_tokens) = self.expression(rest_tokens); 233 | parser_util::expect(TokenKind::SEMICOLON, &mut rest_tokens); 234 | 235 | (declared_names[0].clone(), type_name, ex_id, rest_tokens) 236 | } 237 | } 238 | 239 | #[cfg(test)] 240 | mod statement_tests { 241 | #[test] 242 | fn return_statement_test() {} 243 | 244 | #[test] 245 | fn expr_statement_test() {} 246 | 247 | #[test] 248 | fn countup_statement_test() {} 249 | 250 | #[test] 251 | fn asm_statement_test() {} 252 | 253 | #[test] 254 | fn const_statement_test() {} 255 | } 256 | -------------------------------------------------------------------------------- /src/bundler/resolve.rs: -------------------------------------------------------------------------------- 1 | use crate::common::{ 2 | error::{BundleErrorKind as BEK, CompileError as CE}, 3 | file_util as fu, module as m, option as opt, 4 | }; 5 | use crate::setup; 6 | use id_arena::Arena; 7 | use std::sync::{Arc, Mutex, MutexGuard}; 8 | 9 | use std::fs; 10 | 11 | pub fn resolve_main(arena: Arc>>, source_name: String) -> m::ModuleId { 12 | let file_contents = try_to_get_file_contents(&source_name); 13 | let main_mod = alloc_main_module(arena.lock().unwrap(), source_name); 14 | 15 | // スタートアップ・ライブラリの追加 16 | let startup_module_path = setup_startup_routine(); 17 | let startup_module = 18 | process_ext_module(arena.clone(), startup_module_path, "startup".to_string()); 19 | arena 20 | .lock() 21 | .unwrap() 22 | .get_mut(main_mod) 23 | .unwrap() 24 | .add_reference_module(startup_module); 25 | 26 | // mainが参照するモジュールに対しそれぞれprocess_ext_moduleする 27 | let main_requires = collect_import_modules_from_program(file_contents); 28 | 29 | add_dependencies_to(arena, main_mod, main_requires, false); 30 | 31 | main_mod 32 | } 33 | 34 | /// (間接的にではあるが)再帰的に呼び出される 35 | fn process_ext_module( 36 | arena: Arc>>, 37 | ext_fp: String, 38 | ext_name: String, 39 | ) -> m::ModuleId { 40 | // 相対パス部分と拡張子を削る 41 | let ext_name = if ext_name.contains('/') { 42 | let ext_name = ext_name.split('/').collect::>().pop().unwrap(); 43 | if ext_name.contains('.') { 44 | ext_name.split('.').collect::>()[0].to_string() 45 | } else { 46 | ext_name.to_string() 47 | } 48 | } else { 49 | ext_name 50 | }; 51 | let parent_mod = alloc_ext_module(arena.lock().unwrap(), ext_fp.to_string(), ext_name); 52 | 53 | // TODO: エラー出したほうがいいかも 54 | let ext_module_is_dir = fs::metadata(&ext_fp).unwrap().is_dir(); 55 | 56 | if ext_module_is_dir { 57 | // parent_modのsubsにモジュールをぶら下げる 58 | process_submodules(arena, &parent_mod); 59 | } else { 60 | // 普通のファイルと同じように処理する 61 | let file_contents = try_to_get_file_contents(&ext_fp); 62 | let requires = collect_import_modules_from_program(file_contents); 63 | 64 | add_dependencies_to(arena, parent_mod, requires, false); 65 | } 66 | 67 | parent_mod 68 | } 69 | 70 | /// ディレクトリ内の各ファイルに対して,resolveを実行する 71 | fn process_submodules(arena: Arc>>, dir_module_id: &m::ModuleId) { 72 | let parent_module_path = arena 73 | .lock() 74 | .unwrap() 75 | .get_mut(*dir_module_id) 76 | .unwrap() 77 | .copy_path(); 78 | 79 | for entry in fs::read_dir(&parent_module_path).unwrap() { 80 | let file_in_dir = entry.unwrap(); 81 | let child_module_name = file_in_dir.path().to_str().unwrap().to_string(); 82 | let resolved_path = resolve_path_from_name(child_module_name.to_string()); 83 | 84 | let child_module = process_ext_module(arena.clone(), resolved_path, child_module_name); 85 | arena 86 | .lock() 87 | .unwrap() 88 | .get_mut(*dir_module_id) 89 | .unwrap() 90 | .add_child_module(child_module); 91 | } 92 | } 93 | 94 | /// 依存ノードを追加する 95 | fn add_dependencies_to( 96 | arena: Arc>>, 97 | src_mod_id: m::ModuleId, 98 | dependencies: Vec, 99 | is_dir: bool, 100 | ) { 101 | for req in dependencies { 102 | let req_path = resolve_path_from_name(req.to_string()); 103 | let ext_mod = process_ext_module(arena.clone(), req_path, req); 104 | 105 | if is_dir { 106 | arena 107 | .lock() 108 | .unwrap() 109 | .get_mut(src_mod_id) 110 | .unwrap() 111 | .add_child_module(ext_mod); 112 | } else { 113 | arena 114 | .lock() 115 | .unwrap() 116 | .get_mut(src_mod_id) 117 | .unwrap() 118 | .add_reference_module(ext_mod); 119 | } 120 | } 121 | } 122 | 123 | /// モジュール名から,モジュールが存在する絶対パスを取得する. 124 | /// 相対パスに存在するか,PEACHILI_LIB_PATH/lib/に存在すればOK 125 | /// そうでなければとりあえずpanic!する 126 | fn resolve_path_from_name(module_name: String) -> String { 127 | // 普通に相対パスで検索 128 | let resolved_path = search_module(module_name.to_string()); 129 | if let Some(relative_path) = resolved_path { 130 | return relative_path; 131 | } 132 | 133 | // PEACHILI_LIB_PATH/lib をつけて検索 134 | let resolved_path = search_module(format!("{}{}", get_lib_path(), module_name)); 135 | if let Some(lib_path) = resolved_path { 136 | return lib_path; 137 | } 138 | 139 | panic!("not found such a module -> `{}`", module_name) 140 | } 141 | 142 | /// モジュールが存在するかチェック. 143 | /// DIR_MODULEの可能性を考えて,`.go`無しとありの2パターンで検索する 144 | fn search_module(module_name: String) -> Option { 145 | let resolved_dir = search_directory(module_name.to_string()); 146 | 147 | if let Some(dir_path) = resolved_dir { 148 | return Some(dir_path); 149 | } 150 | 151 | // ディレクトリがなかったので,拡張子をつけて再度チェック 152 | let resolved_file = search_peachili_program(format!("{}.go", module_name)); 153 | if let Some(file_path) = resolved_file { 154 | return Some(file_path); 155 | } 156 | 157 | None 158 | } 159 | 160 | /// 引数に渡したディレクトリが存在するかチェック 161 | fn search_peachili_program(file_name: String) -> Option { 162 | let metadata = fs::metadata(file_name.to_string()); 163 | 164 | // そもそもファイルが存在しなかった 165 | if metadata.is_err() { 166 | return None; 167 | } 168 | 169 | // 拡張子をつけてファイルを見つけられた -> ソースファイルを発見した 170 | Some(file_name) 171 | } 172 | 173 | /// 引数に渡したPeachiliファイルが存在するかチェック 174 | fn search_directory(dir_name: String) -> Option { 175 | let metadata = fs::metadata(dir_name.to_string()); 176 | 177 | // そもそもファイルが存在しなかった 178 | if metadata.is_err() { 179 | return None; 180 | } 181 | 182 | // 何も拡張子をつけないでファイルを見つけられた -> ディレクトリを発見した 183 | Some(dir_name) 184 | } 185 | 186 | /// PRIMARYなモジュールをアロケートして返す 187 | fn alloc_main_module(mut arena: MutexGuard>, main_fp: String) -> m::ModuleId { 188 | arena.alloc(m::Module::new_primary(main_fp, "main".to_string())) 189 | } 190 | 191 | fn alloc_ext_module( 192 | mut arena: MutexGuard>, 193 | ext_fp: String, 194 | ext_name: String, 195 | ) -> m::ModuleId { 196 | arena.alloc(m::Module::new_external(ext_fp, ext_name)) 197 | } 198 | 199 | /// ファイル先頭にある任意数の `import ;` を解読して返す 200 | fn collect_import_modules_from_program(file_contents: String) -> Vec { 201 | let mut requires = Vec::new(); 202 | let lines_iter = file_contents.lines(); 203 | 204 | for l in lines_iter { 205 | // とりあえずTopLevelDeclがくるまでループしておく 206 | if l.contains("func") || l.contains("pubtype") || l.contains("struct") { 207 | return requires; 208 | } 209 | 210 | // importがなければ空行 211 | if !l.contains("import") { 212 | continue; 213 | } 214 | 215 | // ["import", ";"] 216 | let req_name = parse_import(l.to_string()); 217 | requires.push(req_name); 218 | } 219 | 220 | requires 221 | } 222 | 223 | /// import ; をパースして,モジュール名を切り出す 224 | fn parse_import(l: String) -> String { 225 | let mut iter = l.split_ascii_whitespace(); 226 | let _ = iter.next(); // import の読み飛ばし 227 | let import_string = iter.next().unwrap(); 228 | import_string.to_string().trim_end_matches(';').to_string() 229 | } 230 | 231 | /// コマンドライン引数に渡されたファイルから内容を読み取ろうとする 232 | /// エラーを発行する可能性もある 233 | fn try_to_get_file_contents(source_name: &str) -> String { 234 | match fu::read_program_from_file(source_name) { 235 | Some(contents) => contents, 236 | None => { 237 | CE::new( 238 | BEK::NOTFOUNDSUCHAFILE { 239 | file_name: source_name.to_string(), 240 | }, 241 | Default::default(), 242 | ) 243 | .output(); 244 | std::process::exit(1); 245 | } 246 | } 247 | } 248 | 249 | fn setup_startup_routine() -> String { 250 | match setup::BUILD_OPTION.target { 251 | opt::Target::X86_64 => format!("{}startup_x64.go", get_lib_path()), 252 | opt::Target::AARCH64 => format!("{}startup_aarch64.go", get_lib_path()), 253 | } 254 | } 255 | 256 | /// コンパイラのディレクトリに存在するlib/を返す 257 | fn get_lib_path() -> String { 258 | let lib_path = std::env::var("PEACHILI_LIB_PATH"); 259 | 260 | if lib_path.is_err() { 261 | panic!("`PEACHILI_LIB_PATH` was not found."); 262 | } 263 | 264 | let lib_path = lib_path.unwrap(); 265 | let ends_with_slash = lib_path.ends_with('/'); 266 | 267 | if ends_with_slash { 268 | lib_path 269 | } else { 270 | lib_path + "/" 271 | } 272 | } 273 | 274 | #[cfg(test)] 275 | mod resolve_tests { 276 | use super::*; 277 | 278 | #[test] 279 | fn parse_import_test() { 280 | let actual = parse_import("import A;".to_string()); 281 | assert_eq!("A", actual); 282 | } 283 | 284 | #[test] 285 | fn collect_import_modules_from_program_test() { 286 | // 空行あり 287 | let s1 = "import A;\nimport B;\nimport C;\n\nstruct A{}\n".to_string(); 288 | 289 | let actual = collect_import_modules_from_program(s1); 290 | 291 | assert_eq!(3, actual.len()); 292 | 293 | // importなし 294 | let s2 = "\n\n\n\nstruct A{}\n".to_string(); 295 | 296 | let actual = collect_import_modules_from_program(s2); 297 | 298 | assert_eq!(0, actual.len()); 299 | } 300 | 301 | #[test] 302 | fn search_directory_test() { 303 | // テスト実行時の相対パスで取っている 304 | let dir = search_directory("examples".to_string()); 305 | assert!(dir.is_some()); 306 | } 307 | 308 | #[test] 309 | fn search_peachili_program_test() { 310 | let file = search_peachili_program("examples/x64/empty_main.go".to_string()); 311 | assert!(file.is_some()); 312 | } 313 | 314 | #[test] 315 | fn search_module_test() { 316 | let dir = search_module("examples".to_string()); 317 | assert!(dir.is_some()); 318 | 319 | let file = search_module("examples/x64/empty_main.go".to_string()); 320 | assert!(file.is_some()); 321 | 322 | let invalid = search_module("invalid".to_string()); 323 | assert!(invalid.is_none()); 324 | } 325 | 326 | #[test] 327 | #[ignore] 328 | fn resolve_test() { 329 | // lib/ のディレクトリ 330 | let p = resolve_path_from_name("std".to_string()); 331 | assert_eq!(format!("{}std", get_lib_path()), p); 332 | 333 | // lib/ のファイル 334 | let p = resolve_path_from_name("std/os".to_string()); 335 | assert_eq!(format!("{}std/os.go", get_lib_path()), p); 336 | 337 | // 相対パスのディレクトリ 338 | let p = resolve_path_from_name("examples".to_string()); 339 | assert_eq!("examples", p); 340 | 341 | // 相対パスのファイル 342 | let p = resolve_path_from_name("examples/boolean_1.go".to_string()); 343 | assert_eq!("examples/boolean_1.go", p); 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "ansi_term" 5 | version = "0.12.1" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 8 | dependencies = [ 9 | "winapi", 10 | ] 11 | 12 | [[package]] 13 | name = "asmpeach" 14 | version = "0.1.46" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "02e73fbe8f015a327a44d027577cc0125420b8328ad2d19b66ec54496f3d0792" 17 | dependencies = [ 18 | "elf-utilities", 19 | "indexmap", 20 | ] 21 | 22 | [[package]] 23 | name = "atty" 24 | version = "0.2.14" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 27 | dependencies = [ 28 | "hermit-abi", 29 | "libc", 30 | "winapi", 31 | ] 32 | 33 | [[package]] 34 | name = "autocfg" 35 | version = "1.0.0" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 38 | 39 | [[package]] 40 | name = "bincode" 41 | version = "1.3.1" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d" 44 | dependencies = [ 45 | "byteorder", 46 | "serde", 47 | ] 48 | 49 | [[package]] 50 | name = "bitflags" 51 | version = "1.2.1" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 54 | 55 | [[package]] 56 | name = "byteorder" 57 | version = "1.3.4" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 60 | 61 | [[package]] 62 | name = "clap" 63 | version = "3.0.0-beta.1" 64 | source = "git+https://github.com/clap-rs/clap/#b63c4ea03f8bd5c23cc05e957f22f47daafcce61" 65 | dependencies = [ 66 | "ansi_term", 67 | "atty", 68 | "bitflags", 69 | "clap_derive", 70 | "indexmap", 71 | "lazy_static", 72 | "strsim", 73 | "textwrap", 74 | "unicode-width", 75 | "vec_map", 76 | "yaml-rust", 77 | ] 78 | 79 | [[package]] 80 | name = "clap_derive" 81 | version = "3.0.0-beta.1" 82 | source = "git+https://github.com/clap-rs/clap/#b63c4ea03f8bd5c23cc05e957f22f47daafcce61" 83 | dependencies = [ 84 | "heck", 85 | "proc-macro-error", 86 | "proc-macro2", 87 | "quote", 88 | "syn", 89 | ] 90 | 91 | [[package]] 92 | name = "colored" 93 | version = "1.9.3" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" 96 | dependencies = [ 97 | "atty", 98 | "lazy_static", 99 | "winapi", 100 | ] 101 | 102 | [[package]] 103 | name = "elf-utilities" 104 | version = "0.1.73" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "a4380ccfcb360a5fe4b5abc9456126c2aeb812beed41b1e29ce58af2cfe0fbcf" 107 | dependencies = [ 108 | "bincode", 109 | "serde", 110 | "thiserror", 111 | ] 112 | 113 | [[package]] 114 | name = "hashbrown" 115 | version = "0.9.0" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7" 118 | 119 | [[package]] 120 | name = "heck" 121 | version = "0.3.1" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" 124 | dependencies = [ 125 | "unicode-segmentation", 126 | ] 127 | 128 | [[package]] 129 | name = "hermit-abi" 130 | version = "0.1.10" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" 133 | dependencies = [ 134 | "libc", 135 | ] 136 | 137 | [[package]] 138 | name = "id-arena" 139 | version = "2.2.1" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" 142 | 143 | [[package]] 144 | name = "indexmap" 145 | version = "1.6.0" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" 148 | dependencies = [ 149 | "autocfg", 150 | "hashbrown", 151 | ] 152 | 153 | [[package]] 154 | name = "lazy_static" 155 | version = "1.4.0" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 158 | 159 | [[package]] 160 | name = "libc" 161 | version = "0.2.68" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" 164 | 165 | [[package]] 166 | name = "linked-hash-map" 167 | version = "0.5.2" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" 170 | 171 | [[package]] 172 | name = "peachili" 173 | version = "0.1.0" 174 | dependencies = [ 175 | "asmpeach", 176 | "clap", 177 | "colored", 178 | "elf-utilities", 179 | "id-arena", 180 | "indexmap", 181 | "lazy_static", 182 | "pld", 183 | "yaml-rust", 184 | ] 185 | 186 | [[package]] 187 | name = "pld" 188 | version = "0.1.45" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "b38310d4395fa1f9594aa14b735b3e39d7a92929d5bb56aae46a8e64ebd8721c" 191 | dependencies = [ 192 | "elf-utilities", 193 | ] 194 | 195 | [[package]] 196 | name = "proc-macro-error" 197 | version = "0.4.12" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7" 200 | dependencies = [ 201 | "proc-macro-error-attr", 202 | "proc-macro2", 203 | "quote", 204 | "syn", 205 | "version_check", 206 | ] 207 | 208 | [[package]] 209 | name = "proc-macro-error-attr" 210 | version = "0.4.12" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de" 213 | dependencies = [ 214 | "proc-macro2", 215 | "quote", 216 | "syn", 217 | "syn-mid", 218 | "version_check", 219 | ] 220 | 221 | [[package]] 222 | name = "proc-macro2" 223 | version = "1.0.21" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c" 226 | dependencies = [ 227 | "unicode-xid", 228 | ] 229 | 230 | [[package]] 231 | name = "quote" 232 | version = "1.0.3" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" 235 | dependencies = [ 236 | "proc-macro2", 237 | ] 238 | 239 | [[package]] 240 | name = "serde" 241 | version = "1.0.116" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5" 244 | dependencies = [ 245 | "serde_derive", 246 | ] 247 | 248 | [[package]] 249 | name = "serde_derive" 250 | version = "1.0.116" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8" 253 | dependencies = [ 254 | "proc-macro2", 255 | "quote", 256 | "syn", 257 | ] 258 | 259 | [[package]] 260 | name = "strsim" 261 | version = "0.9.3" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" 264 | 265 | [[package]] 266 | name = "syn" 267 | version = "1.0.40" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350" 270 | dependencies = [ 271 | "proc-macro2", 272 | "quote", 273 | "unicode-xid", 274 | ] 275 | 276 | [[package]] 277 | name = "syn-mid" 278 | version = "0.5.0" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" 281 | dependencies = [ 282 | "proc-macro2", 283 | "quote", 284 | "syn", 285 | ] 286 | 287 | [[package]] 288 | name = "textwrap" 289 | version = "0.11.0" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 292 | dependencies = [ 293 | "unicode-width", 294 | ] 295 | 296 | [[package]] 297 | name = "thiserror" 298 | version = "1.0.21" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42" 301 | dependencies = [ 302 | "thiserror-impl", 303 | ] 304 | 305 | [[package]] 306 | name = "thiserror-impl" 307 | version = "1.0.21" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab" 310 | dependencies = [ 311 | "proc-macro2", 312 | "quote", 313 | "syn", 314 | ] 315 | 316 | [[package]] 317 | name = "unicode-segmentation" 318 | version = "1.6.0" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" 321 | 322 | [[package]] 323 | name = "unicode-width" 324 | version = "0.1.7" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" 327 | 328 | [[package]] 329 | name = "unicode-xid" 330 | version = "0.2.0" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 333 | 334 | [[package]] 335 | name = "vec_map" 336 | version = "0.8.1" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" 339 | 340 | [[package]] 341 | name = "version_check" 342 | version = "0.9.1" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" 345 | 346 | [[package]] 347 | name = "winapi" 348 | version = "0.3.8" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" 351 | dependencies = [ 352 | "winapi-i686-pc-windows-gnu", 353 | "winapi-x86_64-pc-windows-gnu", 354 | ] 355 | 356 | [[package]] 357 | name = "winapi-i686-pc-windows-gnu" 358 | version = "0.4.0" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 361 | 362 | [[package]] 363 | name = "winapi-x86_64-pc-windows-gnu" 364 | version = "0.4.0" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 367 | 368 | [[package]] 369 | name = "yaml-rust" 370 | version = "0.4.3" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" 373 | dependencies = [ 374 | "linked-hash-map", 375 | ] 376 | -------------------------------------------------------------------------------- /src/arch/aarch64/pass/codegen.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::aarch64::ir as lir; 2 | use crate::common::analyze_resource::frame_object::StackFrame; 3 | use crate::common::three_address_code as tac; 4 | 5 | pub fn codegen_main(ir_module: tac::IRModule, stack_frame: StackFrame) -> lir::Module { 6 | let mut aarch64_module: lir::Module = Default::default(); 7 | 8 | for tac_fn_id in ir_module.funcs.iter() { 9 | let tac_fn = ir_module.get_fn(tac_fn_id); 10 | let aarch64_fn = gen_aarch64_fn(tac_fn, &stack_frame); 11 | aarch64_module.push_function(aarch64_fn); 12 | } 13 | 14 | aarch64_module 15 | } 16 | 17 | fn gen_aarch64_fn(tac_fn: &tac::IRFunction, stack_frame: &StackFrame) -> lir::Function { 18 | let mut aarch64_fn = lir::Function::new(&tac_fn.name); 19 | aarch64_fn.push_block("entry"); 20 | 21 | let mut generator = FunctionGenerator::new(aarch64_fn, stack_frame); 22 | 23 | // prologue 24 | generator.gen_function_prologue(); 25 | 26 | // 引数定義があったらその分storeする 27 | // generator.gen_arguments_to_stack(tac_fn); 28 | 29 | for code_id in tac_fn.codes.iter() { 30 | let code = tac_fn.get_code(*code_id); 31 | generator.gen_aarch64_inst(tac_fn, code); 32 | } 33 | 34 | generator.gen_function_epilogue(); 35 | 36 | generator.f 37 | } 38 | 39 | struct FunctionGenerator<'a> { 40 | f: lir::Function, 41 | param_count: usize, 42 | virtual_number: usize, 43 | frame: &'a StackFrame, 44 | } 45 | 46 | impl<'a> FunctionGenerator<'a> { 47 | /// IRタイプごとに命令を生成する 48 | fn gen_aarch64_inst(&mut self, tac_fn: &tac::IRFunction, code: tac::Code) { 49 | match code.kind { 50 | tac::CodeKind::ADDRESSOF { value, result } => { 51 | self.gen_address_inst(tac_fn, value, result) 52 | } 53 | tac::CodeKind::STORE { value, result } => self.gen_store_inst(tac_fn, value, result), 54 | tac::CodeKind::PARAM { value } => self.gen_param_inst(tac_fn, value), 55 | tac::CodeKind::CALL { name, result } => self.gen_call_inst(tac_fn, name, result), 56 | tac::CodeKind::ASM { value } => { 57 | let asm_literal = tac_fn.get_value(value); 58 | self.gen_inst_to_last_bb(lir::InstKind::INLINEASM { 59 | contents: asm_literal.copy_contents(), 60 | }); 61 | } 62 | _ => eprintln!("unimplemented {:?} inst", code.kind), 63 | } 64 | } 65 | 66 | fn gen_store_inst( 67 | &mut self, 68 | tac_fn: &tac::IRFunction, 69 | src: tac::ValueId, 70 | result: tac::ValueId, 71 | ) { 72 | let src_value = tac_fn.get_value(src); 73 | let src_op = self.operand_from_value(src_value); 74 | 75 | self.virtual_number -= 2; 76 | let result_value = tac_fn.get_value(result); 77 | let result_op = self.operand_from_value(result_value); 78 | 79 | match result_op.get_kind() { 80 | lir::OperandKind::REGISTER { reg } => { 81 | self.gen_inst_to_last_bb(lir::InstKind::STR { 82 | operand_size: lir::OperandSize::DWORD, 83 | dst: lir::Operand::new_memory(*reg, 0), 84 | src: src_op, 85 | }); 86 | } 87 | _ => { 88 | self.gen_inst_to_last_bb(lir::InstKind::STR { 89 | operand_size: lir::OperandSize::DWORD, 90 | dst: result_op, 91 | src: src_op, 92 | }); 93 | } 94 | } 95 | } 96 | 97 | fn gen_address_inst( 98 | &mut self, 99 | tac_fn: &tac::IRFunction, 100 | src: tac::ValueId, 101 | result: tac::ValueId, 102 | ) { 103 | let src_value = tac_fn.get_value(src); 104 | let src_op = self.operand_from_value(src_value); 105 | let result_value = tac_fn.get_value(result); 106 | let result_op = self.operand_from_value(result_value); 107 | 108 | self.gen_inst_to_last_bb(lir::InstKind::ADD { 109 | operand_size: lir::OperandSize::DWORD, 110 | dst: result_op, 111 | lop: lir::Operand::new_register(src_op.get_base_reg()), 112 | rop: lir::Operand::new_immediate(src_op.get_offset() as i64), 113 | }); 114 | } 115 | 116 | fn gen_call_inst( 117 | &mut self, 118 | tac_fn: &tac::IRFunction, 119 | callee: tac::ValueId, 120 | result: tac::ValueId, 121 | ) { 122 | let called_name = tac_fn.get_called_name(callee); 123 | let result_value = tac_fn.get_value(result); 124 | 125 | self.gen_inst_to_last_bb(lir::InstKind::BL { name: called_name }); 126 | let result_op = self.operand_from_value(result_value); 127 | 128 | self.gen_inst_to_last_bb(lir::InstKind::MOV { 129 | operand_size: lir::OperandSize::DWORD, 130 | dst: result_op, 131 | src: lir::Operand::new_register(lir::Register::GPR { number: 0 }), 132 | }); 133 | 134 | self.param_count = 0; 135 | } 136 | 137 | /// 引数のpushを実装する 138 | fn gen_param_inst(&mut self, tac_fn: &tac::IRFunction, value_id: tac::ValueId) { 139 | let value = tac_fn.get_value(value_id); 140 | let param_value = self.operand_from_value(value); 141 | let param_reg = self.get_param_register(); 142 | 143 | match param_value.get_kind() { 144 | lir::OperandKind::MEMORY { base: _, offset: _ } => { 145 | self.gen_inst_to_last_bb(lir::InstKind::LDR { 146 | operand_size: lir::OperandSize::DWORD, 147 | dst: param_reg, 148 | src: param_value, 149 | }); 150 | } 151 | _ => { 152 | self.gen_inst_to_last_bb(lir::InstKind::MOV { 153 | operand_size: lir::OperandSize::DWORD, 154 | dst: param_reg, 155 | src: param_value, 156 | }); 157 | } 158 | } 159 | 160 | self.param_count += 1; 161 | } 162 | 163 | /// 関数プロローグを生成する. 164 | fn gen_function_prologue(&mut self) { 165 | let stack_size = self.get_stack_size_from_current_function(); 166 | 167 | // 関数フレームの割付 168 | self.gen_inst_to_last_bb(lir::InstKind::SUB { 169 | operand_size: lir::OperandSize::DWORD, 170 | dst: lir::Operand::new_register(lir::Register::SP), 171 | lop: lir::Operand::new_register(lir::Register::SP), 172 | rop: lir::Operand::new_immediate(stack_size as i64), 173 | }); 174 | 175 | // fp, lr の保存 176 | self.gen_inst_to_last_bb(lir::InstKind::STP { 177 | operand_size: lir::OperandSize::DWORD, 178 | reg1: lir::Register::FP, 179 | reg2: lir::Register::LINK, 180 | dst: lir::Operand::new_memory(lir::Register::SP, stack_size as isize - 16), 181 | }); 182 | 183 | // フレームポインタの更新 184 | // これは,fp/lrの保存に用いた領域を無視する為に挿入される 185 | self.gen_inst_to_last_bb(lir::InstKind::ADD { 186 | operand_size: lir::OperandSize::DWORD, 187 | dst: lir::Operand::new_register(lir::Register::FP), 188 | lop: lir::Operand::new_register(lir::Register::FP), 189 | rop: lir::Operand::new_immediate(16), 190 | }); 191 | } 192 | 193 | /// 関数エピローグの生成 194 | fn gen_function_epilogue(&mut self) { 195 | let stack_size = self.get_stack_size_from_current_function(); 196 | // フレーム/リンクレジスタの復帰,スタックポインタの復帰 197 | self.gen_inst_to_last_bb(lir::InstKind::LDP { 198 | operand_size: lir::OperandSize::DWORD, 199 | reg1: lir::Register::FP, 200 | reg2: lir::Register::LINK, 201 | src: lir::Operand::new_memory(lir::Register::SP, stack_size as isize - 16), 202 | }); 203 | 204 | self.gen_inst_to_last_bb(lir::InstKind::ADD { 205 | operand_size: lir::OperandSize::DWORD, 206 | dst: lir::Operand::new_register(lir::Register::SP), 207 | lop: lir::Operand::new_register(lir::Register::SP), 208 | rop: lir::Operand::new_immediate(stack_size as i64), 209 | }); 210 | } 211 | 212 | /// 三番地コードをaarch64の命令オペランドに変換する 213 | fn operand_from_value(&mut self, v: tac::Value) -> lir::Operand { 214 | match v.kind { 215 | tac::ValueKind::TEMP { number: _ } => self.gen_physical_reg(), 216 | 217 | tac::ValueKind::ID { name } => { 218 | let id_offset = self 219 | .frame 220 | .get(self.f.get_name()) 221 | .unwrap() 222 | .get(&name) 223 | .unwrap() 224 | .offset; 225 | lir::Operand::new_memory(lir::Register::FP, id_offset as isize) 226 | } 227 | // 多少冗長だけど,レジスタにロードしておく 228 | tac::ValueKind::INTLITERAL { value: int_value } => { 229 | let dst_reg = self.gen_physical_reg(); 230 | 231 | self.gen_inst_to_last_bb(lir::InstKind::MOV { 232 | operand_size: lir::OperandSize::DWORD, 233 | dst: dst_reg, 234 | src: lir::Operand::new_immediate(int_value), 235 | }); 236 | 237 | dst_reg 238 | } 239 | tac::ValueKind::BOOLEANLITERAL { truth } => { 240 | let int_value = if truth { 241 | lir::Operand::new_immediate(1) 242 | } else { 243 | lir::Operand::new_immediate(0) 244 | }; 245 | 246 | let dst_reg = self.gen_physical_reg(); 247 | 248 | self.gen_inst_to_last_bb(lir::InstKind::MOV { 249 | operand_size: lir::OperandSize::DWORD, 250 | dst: dst_reg, 251 | src: int_value, 252 | }); 253 | 254 | dst_reg 255 | } 256 | _ => panic!("cannot generate from {:?}", v.kind), 257 | } 258 | } 259 | 260 | /// 引数渡すする際に用いるレジスタを取得する 261 | /// w0/x0 は返り値として用いるため + 1 262 | fn get_param_register(&mut self) -> lir::Operand { 263 | if 6 < self.param_count + 1 { 264 | panic!("callee register exhausted"); 265 | } 266 | 267 | lir::Operand::new_register(lir::Register::GPR { 268 | number: self.param_count, 269 | }) 270 | } 271 | 272 | /// aarch64のために re-numbering しつつレジスタを生成する 273 | fn gen_physical_reg(&mut self) -> lir::Operand { 274 | let reg = match self.virtual_number % lir::Register::AVAILABLES { 275 | reg_number @ 0..=lir::Register::AVAILABLES => lir::Register::GPR { 276 | number: reg_number + 10, 277 | }, 278 | _ => panic!("phys register exhausted"), 279 | }; 280 | 281 | self.virtual_number += 1; 282 | lir::Operand::new_register(reg) 283 | } 284 | 285 | /// 関数フレームのサイズ取得 286 | /// スタックサイズは,fp/lrの保存のために増しておく 287 | fn get_stack_size_from_current_function(&self) -> usize { 288 | let fn_name = self.f.get_name(); 289 | self.frame 290 | .get(fn_name) 291 | .unwrap() 292 | .get(fn_name) 293 | .unwrap() 294 | .offset 295 | + 16 296 | } 297 | fn gen_inst_to_last_bb(&mut self, ik: lir::InstKind) { 298 | self.f.add_inst_to_last_bb(lir::Instruction::new(ik)); 299 | } 300 | fn new(aarch64_fn: lir::Function, stack_frame: &'a StackFrame) -> Self { 301 | Self { 302 | f: aarch64_fn, 303 | param_count: 0, 304 | virtual_number: 0, 305 | frame: stack_frame, 306 | } 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /src/common/pass/parser/main.rs: -------------------------------------------------------------------------------- 1 | use crate::common::ast::{ 2 | ASTRoot, EnumDef, FnArena, FnId, Function, FunctionTypeDef, StructDef, VariantDef, 3 | }; 4 | use crate::common::token::{Token, TokenKind}; 5 | 6 | use crate::common::pass::parser::context::Context; 7 | use crate::common::pass::parser::*; 8 | use id_arena::Arena; 9 | use std::collections::BTreeMap; 10 | use std::sync::{Arc, Mutex}; 11 | 12 | pub fn main(fn_arena: FnArena, mut tokens: Vec, module_name: String) -> ASTRoot { 13 | let mut ast_root: ASTRoot = Default::default(); 14 | let mut ctxt: Context = Default::default(); 15 | ctxt.fn_arena = fn_arena; 16 | ctxt.module_name = module_name; 17 | 18 | // program -> toplevel* 19 | loop { 20 | let t = parser_util::head(&tokens); 21 | 22 | match t.get_kind() { 23 | TokenKind::IMPORT => skip_import_directive(&mut tokens), 24 | TokenKind::FUNC => { 25 | let (fn_id, rest_tokens) = ctxt.func_def(tokens); 26 | tokens = rest_tokens; 27 | ast_root.funcs.push(fn_id); 28 | } 29 | TokenKind::STRUCT => { 30 | let (type_name, struct_def, rest_tokens) = ctxt.struct_def(tokens); 31 | tokens = rest_tokens; 32 | 33 | ast_root 34 | .typedefs 35 | .insert(format!("{}::{}", ctxt.module_name, type_name), struct_def); 36 | } 37 | TokenKind::PUBENUM => { 38 | let (enum_name, variants, rest_tokens) = ctxt.enum_declaration(tokens); 39 | tokens = rest_tokens; 40 | 41 | ast_root 42 | .enum_decls 43 | .insert(format!("{}::{}", ctxt.module_name, enum_name), variants); 44 | } 45 | TokenKind::PUBCONST => { 46 | let (const_name, type_name, expr, rest_tokens) = ctxt.const_declaration(tokens); 47 | tokens = rest_tokens; 48 | 49 | ast_root.constants.insert( 50 | format!("{}::{}", ctxt.module_name, const_name), 51 | (type_name, expr), 52 | ); 53 | } 54 | TokenKind::PUBTYPE => { 55 | let (alias_name, src_name, rest_tokens) = ctxt.type_alias(tokens); 56 | tokens = rest_tokens; 57 | 58 | ast_root 59 | .alias 60 | .insert(format!("{}::{}", ctxt.module_name, alias_name), src_name); 61 | } 62 | _ => break, 63 | } 64 | } 65 | 66 | ast_root.called_functions = ctxt.called_functions; 67 | ast_root 68 | } 69 | 70 | impl Context { 71 | /// 関数定義をパースする関数 72 | fn func_def(&mut self, mut tokens: Vec) -> (FnId, Vec) { 73 | // 関数ごとにStmt/ExprArenaは初期化する 74 | self.expr_arena = Arc::new(Mutex::new(Arena::new())); 75 | self.stmt_arena = Arc::new(Mutex::new(Arena::new())); 76 | 77 | let func_pos = parser_util::current_position(&tokens); 78 | parser_util::eat_token(&mut tokens); 79 | 80 | let (func_names, rest_tokens) = parser_util::expect_identifier(tokens); 81 | let func_name = func_names[0].clone(); 82 | 83 | let (arg_map, rest_tokens) = self.arg_list(rest_tokens); 84 | 85 | let (return_type, rest_tokens) = self.expect_type(rest_tokens); 86 | 87 | let (stmts, rest_tokens) = self.expect_block(rest_tokens); 88 | 89 | ( 90 | self.fn_arena.lock().unwrap().alloc(Function { 91 | name: func_name, 92 | fn_type: FunctionTypeDef::new(return_type, arg_map), 93 | stmts, 94 | pos: func_pos, 95 | module_name: self.module_name.clone(), 96 | stmt_arena: self.stmt_arena.clone(), 97 | expr_arena: self.expr_arena.clone(), 98 | }), 99 | rest_tokens, 100 | ) 101 | } 102 | 103 | /// 引数定義リストをパースする関数 104 | fn arg_list(&mut self, mut tokens: Vec) -> (Vec<(String, String)>, Vec) { 105 | parser_util::expect(TokenKind::LPAREN, &mut tokens); 106 | 107 | let mut args = Vec::new(); 108 | 109 | loop { 110 | let t = parser_util::head(&tokens); 111 | 112 | if t.get_kind() == &TokenKind::RPAREN { 113 | parser_util::expect(TokenKind::RPAREN, &mut tokens); 114 | break; 115 | } 116 | 117 | let (arg_names, rest_tokens) = parser_util::expect_identifier(tokens); 118 | let arg_name = arg_names[0].clone(); 119 | tokens = rest_tokens; 120 | 121 | let (type_name, rest_tokens) = self.expect_type(tokens); 122 | tokens = rest_tokens; 123 | 124 | parser_util::consume(TokenKind::COMMA, &mut tokens); 125 | 126 | args.push((arg_name, type_name)); 127 | } 128 | 129 | (args, tokens) 130 | } 131 | 132 | /// 構造体型の定義をパースする. 133 | fn struct_def(&mut self, mut tokens: Vec) -> (String, StructDef, Vec) { 134 | parser_util::eat_token(&mut tokens); 135 | 136 | let (type_names, rest_tokens) = parser_util::expect_identifier(tokens); 137 | let type_name = type_names[0].clone(); 138 | 139 | let (members, rest_tokens) = self.member_block(rest_tokens); 140 | (type_name, StructDef { members }, rest_tokens) 141 | } 142 | 143 | /// Enum型をパースする. 144 | fn enum_declaration(&mut self, mut tokens: Vec) -> (String, EnumDef, Vec) { 145 | parser_util::eat_token(&mut tokens); 146 | 147 | let (enum_name, mut rest_tokens) = parser_util::expect_identifier(tokens); 148 | parser_util::expect(TokenKind::LBRACE, &mut rest_tokens); 149 | 150 | let mut variants = BTreeMap::new(); 151 | let mut variant_tag = 0; 152 | 153 | loop { 154 | if parser_util::consume(TokenKind::RBRACE, &mut rest_tokens) { 155 | break; 156 | } 157 | 158 | let (variant_name, r) = parser_util::expect_identifier(rest_tokens); 159 | rest_tokens = r; 160 | 161 | variants.insert(variant_name[0].clone(), VariantDef { tag: variant_tag }); 162 | variant_tag += 1; 163 | 164 | parser_util::consume(TokenKind::COMMA, &mut rest_tokens); 165 | } 166 | 167 | (enum_name[0].clone(), EnumDef { variants }, rest_tokens) 168 | } 169 | 170 | /// 構造体型内のメンバ定義列をパースする. 171 | /// 引数のように,リスト構造をパースするメタ関数を作ってもいいかも. 172 | fn member_block(&mut self, mut tokens: Vec) -> (BTreeMap, Vec) { 173 | let mut members = BTreeMap::new(); 174 | parser_util::expect(TokenKind::LBRACE, &mut tokens); 175 | 176 | loop { 177 | let t = parser_util::head(&tokens); 178 | if t.get_kind() == &TokenKind::RBRACE { 179 | parser_util::expect(TokenKind::RBRACE, &mut tokens); 180 | break; 181 | } 182 | 183 | let (member_names, rest_tokens) = parser_util::expect_identifier(tokens); 184 | tokens = rest_tokens; 185 | let member_name = member_names[0].clone(); 186 | 187 | let (member_type, rest_tokens) = self.expect_type(tokens); 188 | tokens = rest_tokens; 189 | 190 | members.insert(member_name, member_type); 191 | } 192 | 193 | (members, tokens) 194 | } 195 | 196 | /// 型エイリアスをパースする関数 197 | fn type_alias(&mut self, mut tokens: Vec) -> (String, String, Vec) { 198 | parser_util::eat_token(&mut tokens); 199 | 200 | let (alias_names, mut rest_tokens) = parser_util::expect_identifier(tokens); 201 | let alias_name = alias_names[0].clone(); 202 | 203 | parser_util::expect(TokenKind::ASSIGN, &mut rest_tokens); 204 | 205 | let (src_name, mut rest_tokens) = self.expect_type(rest_tokens); 206 | parser_util::expect(TokenKind::SEMICOLON, &mut rest_tokens); 207 | 208 | (alias_name, src_name, rest_tokens) 209 | } 210 | 211 | /// 定数宣言をパースする関数 212 | fn const_declaration( 213 | &mut self, 214 | mut tokens: Vec, 215 | ) -> (String, String, String, Vec) { 216 | parser_util::eat_token(&mut tokens); 217 | 218 | let (const_name, mut rest_tokens) = parser_util::expect_identifier(tokens); 219 | let const_name = const_name[0].clone(); 220 | 221 | parser_util::expect(TokenKind::COLON, &mut rest_tokens); 222 | let (type_name, mut rest_tokens) = self.expect_type(rest_tokens); 223 | parser_util::expect(TokenKind::ASSIGN, &mut rest_tokens); 224 | 225 | let expr = rest_tokens[0].get_kind().to_string(); 226 | parser_util::eat_token(&mut rest_tokens); 227 | parser_util::expect(TokenKind::SEMICOLON, &mut rest_tokens); 228 | 229 | (const_name, type_name, expr, rest_tokens) 230 | } 231 | } 232 | 233 | /// コンパイラ内部では用いないのでスキップする. 234 | /// ASTRootに情報を含めることで,ルートがインポートしていないパッケージはバイナリに含めない,みたいなことができるかも. 235 | fn skip_import_directive(tokens: &mut Vec) { 236 | parser_util::eat_token(tokens); 237 | parser_util::eat_token(tokens); 238 | parser_util::eat_token(tokens); 239 | } 240 | 241 | #[cfg(test)] 242 | mod toplevel_tests { 243 | use super::*; 244 | 245 | use id_arena::Arena; 246 | use std::sync::{Arc, Mutex}; 247 | 248 | #[test] 249 | fn type_alias_test() {} 250 | 251 | #[test] 252 | fn struct_def_test() {} 253 | 254 | #[test] 255 | fn member_block_test() {} 256 | 257 | #[test] 258 | fn arg_list_test() {} 259 | 260 | #[test] 261 | fn func_def_test() {} 262 | 263 | #[test] 264 | fn main_test() { 265 | let tokens = vec![ 266 | Token::new(TokenKind::FUNC, Default::default()), 267 | Token::new_identifier("main".to_string(), Default::default()), 268 | Token::new(TokenKind::LPAREN, Default::default()), 269 | Token::new_identifier("foo".to_string(), Default::default()), 270 | Token::new(TokenKind::INT64, Default::default()), 271 | Token::new(TokenKind::COMMA, Default::default()), 272 | Token::new_identifier("bar".to_string(), Default::default()), 273 | Token::new(TokenKind::UINT64, Default::default()), 274 | Token::new(TokenKind::COMMA, Default::default()), 275 | Token::new_identifier("fizz".to_string(), Default::default()), 276 | Token::new(TokenKind::INT64, Default::default()), 277 | Token::new(TokenKind::RPAREN, Default::default()), 278 | Token::new(TokenKind::NORETURN, Default::default()), 279 | Token::new(TokenKind::LBRACE, Default::default()), 280 | Token::new(TokenKind::RBRACE, Default::default()), 281 | Token::new(TokenKind::FUNC, Default::default()), 282 | Token::new_identifier("sub1".to_string(), Default::default()), 283 | Token::new(TokenKind::LPAREN, Default::default()), 284 | Token::new_identifier("foo".to_string(), Default::default()), 285 | Token::new(TokenKind::INT64, Default::default()), 286 | Token::new(TokenKind::COMMA, Default::default()), 287 | Token::new_identifier("bar".to_string(), Default::default()), 288 | Token::new(TokenKind::UINT64, Default::default()), 289 | Token::new(TokenKind::COMMA, Default::default()), 290 | Token::new_identifier("fizz".to_string(), Default::default()), 291 | Token::new(TokenKind::INT64, Default::default()), 292 | Token::new(TokenKind::RPAREN, Default::default()), 293 | Token::new(TokenKind::NORETURN, Default::default()), 294 | Token::new(TokenKind::LBRACE, Default::default()), 295 | Token::new(TokenKind::RBRACE, Default::default()), 296 | Token::new(TokenKind::EOF, Default::default()), 297 | ]; 298 | let fn_arena = new_allocators(); 299 | 300 | let root = main(fn_arena, tokens, "sample".to_string()); 301 | 302 | assert_eq!(2, root.funcs.len()); 303 | } 304 | 305 | fn new_allocators() -> FnArena { 306 | Arc::new(Mutex::new(Arena::new())) 307 | } 308 | } 309 | --------------------------------------------------------------------------------