From 184c8f3d50be4b1b35b61a77ef510a707ac13bb7 Mon Sep 17 00:00:00 2001 From: Michael Sippel Date: Wed, 14 Aug 2024 00:26:18 +0200 Subject: [PATCH] separate crates for compiler-lib, compiler-cli and vm --- Cargo.toml | 17 +- lib-ltcore/Cargo.toml | 10 + {src => lib-ltcore/src}/expr.rs | 0 {src => lib-ltcore/src}/lexer.rs | 0 lib-ltcore/src/lib.rs | 8 + {src => lib-ltcore/src}/parser.rs | 0 {src => lib-ltcore/src}/procedure_compiler.rs | 0 {src => lib-ltcore/src}/runtime.rs | 0 {src => lib-ltcore/src}/symbols.rs | 0 ltcc/Cargo.toml | 13 ++ ltcc/hello.lt | 8 + ltcc/hello.lt.o | Bin 0 -> 1826 bytes ltcc/src/diagnostic.rs | 70 +++++++ ltcc/src/main.rs | 125 ++++++++++++ ltcc/src/oldmain.rs | 30 +++ ltcc/stdio.lt.o | Bin 0 -> 1214 bytes ltvm/Cargo.toml | 12 ++ ltvm/src/main.rs | 55 ++++++ src/main.rs | 180 ------------------ 19 files changed, 337 insertions(+), 191 deletions(-) create mode 100644 lib-ltcore/Cargo.toml rename {src => lib-ltcore/src}/expr.rs (100%) rename {src => lib-ltcore/src}/lexer.rs (100%) create mode 100644 lib-ltcore/src/lib.rs rename {src => lib-ltcore/src}/parser.rs (100%) rename {src => lib-ltcore/src}/procedure_compiler.rs (100%) rename {src => lib-ltcore/src}/runtime.rs (100%) rename {src => lib-ltcore/src}/symbols.rs (100%) create mode 100644 ltcc/Cargo.toml create mode 100644 ltcc/hello.lt create mode 100644 ltcc/hello.lt.o create mode 100644 ltcc/src/diagnostic.rs create mode 100644 ltcc/src/main.rs create mode 100644 ltcc/src/oldmain.rs create mode 100644 ltcc/stdio.lt.o create mode 100644 ltvm/Cargo.toml create mode 100644 ltvm/src/main.rs delete mode 100644 src/main.rs diff --git a/Cargo.toml b/Cargo.toml index f203688..0558ecd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,6 @@ -[package] -name = "ltir" -version = "0.1.0" -edition = "2021" - -[dependencies] -laddertypes = { path = "../lib-laddertypes" } -tisc = { path = "../lib-tisc" } -iterate-text = "0.0.1" -tiny-ansi = "0.1.0" - +[workspace] +members = [ + "lib-ltcore", + "ltcc", + "ltvm" +] diff --git a/lib-ltcore/Cargo.toml b/lib-ltcore/Cargo.toml new file mode 100644 index 0000000..97f2c39 --- /dev/null +++ b/lib-ltcore/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "ltcore" +version = "0.1.0" +edition = "2021" + +[dependencies] +laddertypes = { path = "../../lib-laddertypes" } +tisc = { path = "../../lib-tisc" } +serde = { version = "1.0", features = ["derive"] } + diff --git a/src/expr.rs b/lib-ltcore/src/expr.rs similarity index 100% rename from src/expr.rs rename to lib-ltcore/src/expr.rs diff --git a/src/lexer.rs b/lib-ltcore/src/lexer.rs similarity index 100% rename from src/lexer.rs rename to lib-ltcore/src/lexer.rs diff --git a/lib-ltcore/src/lib.rs b/lib-ltcore/src/lib.rs new file mode 100644 index 0000000..bbe3719 --- /dev/null +++ b/lib-ltcore/src/lib.rs @@ -0,0 +1,8 @@ + +pub mod expr; +pub mod lexer; +pub mod parser; +pub mod procedure_compiler; +pub mod runtime; +pub mod symbols; + diff --git a/src/parser.rs b/lib-ltcore/src/parser.rs similarity index 100% rename from src/parser.rs rename to lib-ltcore/src/parser.rs diff --git a/src/procedure_compiler.rs b/lib-ltcore/src/procedure_compiler.rs similarity index 100% rename from src/procedure_compiler.rs rename to lib-ltcore/src/procedure_compiler.rs diff --git a/src/runtime.rs b/lib-ltcore/src/runtime.rs similarity index 100% rename from src/runtime.rs rename to lib-ltcore/src/runtime.rs diff --git a/src/symbols.rs b/lib-ltcore/src/symbols.rs similarity index 100% rename from src/symbols.rs rename to lib-ltcore/src/symbols.rs diff --git a/ltcc/Cargo.toml b/ltcc/Cargo.toml new file mode 100644 index 0000000..a4e93b2 --- /dev/null +++ b/ltcc/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "ltcc" +version = "0.1.0" +edition = "2021" + +[dependencies] +ltcore = { path = "../lib-ltcore" } +tisc = { path = "../../lib-tisc" } +clap = { version = "4.5.15", features = ["derive"] } +tiny-ansi = "0.1.0" +iterate-text = "0.0.1" +bincode = "1.3.3" + diff --git a/ltcc/hello.lt b/ltcc/hello.lt new file mode 100644 index 0000000..e22d951 --- /dev/null +++ b/ltcc/hello.lt @@ -0,0 +1,8 @@ +export { + let star = λ{} + ↦ emit 42; + + let main = λ{} ↦ { + print-nullterm 'H''e''l''l''o'' ''W''o''r''l''d''!''\n''\0'; + }; +} diff --git a/ltcc/hello.lt.o b/ltcc/hello.lt.o new file mode 100644 index 0000000000000000000000000000000000000000..28b340b8c5c5c0ca2f7ce2c7ff925c989892ab9c GIT binary patch literal 1826 zcmbuA%}&Bl5QU3{M2VoHAmaa)z%YCEo6))_8cdD${feo&)3G@Hu@!xOoeepL|rLXs!!gt`lc->1$oyr0^^RQ$M z_LVwW62HFpOL9MtJv0Cmz3vq}P=|d~vfB^j szuiBX>k&Lrza%^+JR|&#@Co4$gfkyD592H1{{-CcyZxW}dthhq191u-qyPW_ literal 0 HcmV?d00001 diff --git a/ltcc/src/diagnostic.rs b/ltcc/src/diagnostic.rs new file mode 100644 index 0000000..7764509 --- /dev/null +++ b/ltcc/src/diagnostic.rs @@ -0,0 +1,70 @@ +use { + std::collections::HashMap, + std::sync::{Arc, RwLock}, + std::{boxed::Box, ops::Deref}, + tiny_ansi::TinyAnsi, + ltcore::{ + lexer::InputRegionTag, + expr::{LTExpr, Statement}, + procedure_compiler::ProcedureCompiler, + symbols::Scope, + } +}; + +pub fn print_diagnostic( + path: &str, + region: InputRegionTag, + message: String +) { + let lines = iterate_text::file::lines::IterateFileLines::new(path); + + let mut line_region = InputRegionTag::default(); + + let n_before = 3; + let n_after = 3; + + let mut last_lines = Vec::new(); + let mut next_lines = 0; + + println!("\n{}:", path.green()); + for (i, l) in lines.enumerate() { + + line_region.end += l.chars().count(); + + last_lines.push((i+1, l.clone())); + if last_lines.len() > n_before { + last_lines.remove(0); + } + + if region.begin >= line_region.begin && + region.begin < line_region.end { + + next_lines = n_after; + + let column_begin = region.begin - line_region.begin; + let column_end = region.end - line_region.begin; + + // display the source line + for (j,ll) in last_lines.iter() { + print!("{}\t{}{}", + format!("{}",j).to_string().bright_black(), + "|".bright_black().bold(), + ll.bright_white()); + } + + print!("\t{}", "|".bright_magenta()); + for _ in 0..column_begin { print!("{}", ".".magenta().bold()); } + for _ in column_begin..column_end { print!("{}", "^".magenta().bold()); } + print!("\n"); + + print!("{} [{}-{}]: {}\n", "error".bright_red(), column_begin, column_end, message.white()); + } + else if next_lines > 0 { + next_lines -= 1; + print!("{}\t{}{}", format!("{}", i+1).to_string().bright_black(), "|".bright_black().bold(), l.bright_white()); + } + + line_region.begin = line_region.end; + } +} + diff --git a/ltcc/src/main.rs b/ltcc/src/main.rs new file mode 100644 index 0000000..26bdcae --- /dev/null +++ b/ltcc/src/main.rs @@ -0,0 +1,125 @@ +use clap::Parser; + +use { + std::collections::HashMap, + std::sync::{Arc, RwLock}, + std::{boxed::Box, ops::Deref}, + std::io::Write, + tiny_ansi::TinyAnsi, + ltcore::{ + lexer::InputRegionTag, + expr::{LTExpr, Statement}, + procedure_compiler::ProcedureCompiler, + symbols::Scope, + } +}; + +mod diagnostic; + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct Args { + /// source files + sources: Vec< String >, + + /// path to the target bytecode file + #[arg(short, long)] + output: String +} + +fn main() { + let args = Args::parse(); + + let mut linker = tisc::Linker::new(); + let root_scope = ltcore::runtime::init_runtime(&mut linker); + let main_scope = Scope::with_parent(&root_scope); + let typectx = main_scope.read().unwrap().typectx.clone(); + + for path in args.sources { + let iter_chars = iterate_text::file::characters::IterateFileCharacters::new(path.clone()); + + /* compile source file + */ + let mut lexer = ltcore::lexer::LTIRLexer::from( iter_chars.peekable() ); + let mut program_tokens = + lexer + .filter(|tok| match tok { + (_, Ok(ltcore::lexer::LTIRToken::Comment(_))) => false, + _ => true + }) + .peekable(); + + match ltcore::parser::parse_expr( &typectx, &mut program_tokens ) { + Ok( ast ) => { + let (exports, diagnostics, proc_code) = ProcedureCompiler::new(&main_scope) + .compile(&ast) + .into_asm(&path); + + for (region, message) in diagnostics { + crate::diagnostic::print_diagnostic( + path.as_str(), + region, + format!("{}", message) + ); + } + + eprintln!("{} {}", "Compiled".green(), path.bold()); + for (name, def) in exports.iter() { + eprintln!("export {}: {:?}", name.yellow().bold(), def); + } + + main_scope.write().unwrap().import( + exports + ); + + /* link assembly-program to symbols + */ + linker.add_procedure(path.as_str(), proc_code); + } + Err( (region, parse_error) ) => { + crate::diagnostic::print_diagnostic( + path.as_str(), + region, + format!("{:?}", parse_error) + ); + + eprintln!("=======\nParse Error: Abort\n"); + } + } + } + + eprintln!("write output file {}", args.output); + let obj_file = tisc::linker::ObjectFile { + symbols: Arc::into_inner(main_scope).unwrap().into_inner().unwrap() + .export() + .into_iter() + .filter_map(|(symbol, def)| match def { + ltcore::symbols::SymbolDef::Procedure { in_types:_, out_types:_, link_addr, export } => { + if export { + match link_addr { + tisc::LinkAddr::Absolute(w) => { + eprintln!("add symbol {} -> {}", symbol, w); + Some(( symbol, w )) + } + tisc::LinkAddr::Relative{ symbol: b, offset } => { + let addr = linker.get_link_addr(&b).unwrap_or(-1); + eprintln!("relative symbol {} -> {}({})+{}", symbol, b, addr, offset); + Some((symbol, addr + offset )) + } + } + } else { + None + } + } + _ => None + }) + .collect(), + + code: linker.link_partial().expect("Link error:") + }; + + let mut output = std::io::BufWriter::new( + std::fs::File::create(args.output).expect("Failed to open file") + ); + bincode::serialize_into( output, &obj_file ); +} diff --git a/ltcc/src/oldmain.rs b/ltcc/src/oldmain.rs new file mode 100644 index 0000000..5a321aa --- /dev/null +++ b/ltcc/src/oldmain.rs @@ -0,0 +1,30 @@ +use { + std::collections::HashMap, + std::sync::{Arc, RwLock}, + std::{boxed::Box, ops::Deref}, + tiny_ansi::TinyAnsi +}; + +use crate::{ + lexer::InputRegionTag, + expr::{LTExpr, Statement}, + procedure_compiler::ProcedureCompiler, + symbols::Scope, +}; + +/* TODO: + * - import function symbols + * - Compiler error reporting + * - parse float literals + * - return type annotation + * - write to address resulting from expression + * - sized objects + * - Typecheck for LTExpr::Application + * - typecheck & inference for rest + */ + +fn main() { + // create virtual machine with 4096 words of memory + let mut vm = tisc::VM::new(0x1000); + +} diff --git a/ltcc/stdio.lt.o b/ltcc/stdio.lt.o new file mode 100644 index 0000000000000000000000000000000000000000..04945b42478ac30ee30e2998c6e645a9d13c0d60 GIT binary patch literal 1214 zcmbu8%L>9k3`M6tK*e``KYu`fMR0GIE`(a7)PHxUh9+S;B;a$ReggafB?6Iji<_=JUy0~MeTSW_d;6D|rRkOj>cVPZs8vmZb u?~8Y=N?%{^0_VW*;^&, + + /// entry symbol + #[arg(short, long, default_value_t = String::from("main"))] + entry: String, + + /// memory size + #[arg(short, long, default_value_t = 0x1000)] + memsize: usize +} + +fn main() { + let args = Args::parse(); + + let mut vm = tisc::VM::new( args.memsize ); + let mut linker = tisc::Linker::new(); + + let mut symbols = std::collections::HashMap::::new(); + + for source_path in args.sources.iter() { + let mut input = std::io::BufReader::new( + std::fs::File::open(source_path).expect("Failed to open file") + ); + + linker.import( source_path, bincode::deserialize_from( input ).expect("") ); + } +/* + let entry_addr = symbols.get(&args.entry) + .expect(&format!("cant find entry symbol '{}'", args.entry)); +*/ + let entry_addr = linker.get_link_addr(&args.entry).unwrap(); + let bytecode = linker.link_total().expect("Link error:"); + + eprintln!("{} ({} bytes)", "Loaded bytecode.".green(), bytecode.len()); + eprintln!("================\n"); + + vm.load(bytecode); + vm.execute(entry_addr); + + eprintln!( + "\n================\nVM execution finished\ndatastack = {:?}\n====", + vm.data_stack + ); + +} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index cb15d3e..0000000 --- a/src/main.rs +++ /dev/null @@ -1,180 +0,0 @@ -use { - std::collections::HashMap, - std::sync::{Arc, RwLock}, - std::{boxed::Box, ops::Deref}, - tiny_ansi::TinyAnsi -}; - -mod expr; -mod lexer; -mod parser; -mod procedure_compiler; -mod runtime; -mod symbols; - -use crate::{ - lexer::InputRegionTag, - expr::{LTExpr, Statement}, - procedure_compiler::ProcedureCompiler, - symbols::Scope, -}; - -fn print_diagnostic( - path: &str, - region: InputRegionTag, - message: String -) { - let lines = iterate_text::file::lines::IterateFileLines::new(path); - - let mut line_region = InputRegionTag::default(); - - let n_before = 3; - let n_after = 3; - - let mut last_lines = Vec::new(); - let mut next_lines = 0; - - println!("\n{}:", path.green()); - for (i, l) in lines.enumerate() { - line_region.end += l.chars().count(); - - last_lines.push((i+1, l.clone())); - if last_lines.len() > n_before { - last_lines.remove(0); - } - - if region.begin >= line_region.begin && - region.begin < line_region.end { - - next_lines = n_after; - - let column_begin = region.begin - line_region.begin; - let column_end = region.end - line_region.begin; - - // display the source line - for (j,ll) in last_lines.iter() { - print!("{}\t{}{}", - format!("{}",j).to_string().bright_black(), - "|".bright_black().bold(), - ll.bright_white()); - } - - print!("\t{}", "|".bright_magenta()); - for _ in 0..column_begin { print!("{}", ".".magenta().bold()); } - for _ in column_begin..column_end { print!("{}", "^".magenta().bold()); } - print!("\n"); - - print!("{} [{}-{}]: {}\n", "error".bright_red(), column_begin, column_end, message.white()); - } - else if next_lines > 0 { - next_lines -= 1; - print!("{}\t{}{}", format!("{}", i+1).to_string().bright_black(), "|".bright_black().bold(), l.bright_white()); - } - - line_region.begin = line_region.end; - } -} - -/* TODO: - * - import function symbols - * - Compiler error reporting - * - parse float literals - * - return type annotation - * - write to address resulting from expression - * - sized objects - * - Typecheck for LTExpr::Application - * - typecheck & inference for rest - */ - -fn main() { - // create virtual machine with 4096 words of memory - let mut vm = tisc::VM::new(0x1000); - - let mut linker = tisc::Linker::new(); - let root_scope = crate::runtime::init_runtime(&mut linker); - let main_scope = Scope::with_parent(&root_scope); - let typectx = main_scope.read().unwrap().typectx.clone(); - - /* open source file - */ - let args: Vec = std::env::args().collect(); - - if args.len() < 2 { - eprintln!("{}", "No source files specified.".red()); - return; - } - - let mut args_iter = args.into_iter(); - args_iter.next(); - - for path in args_iter { - let iter_chars = iterate_text::file::characters::IterateFileCharacters::new(path.clone()); - - /* compile source file - */ - let mut lexer = lexer::LTIRLexer::from( iter_chars.peekable() ); - let mut program_tokens = lexer.filter(|tok| match tok { - (_, Ok(lexer::LTIRToken::Comment(_))) => false, - _ => true - }) - .peekable(); - - match parser::parse_expr( &typectx, &mut program_tokens ) { - Ok( ast ) => { - let (exports, diagnostics, bytecode) = ProcedureCompiler::new(&main_scope) - .compile(&ast) - .into_asm(&path); - - for (region, message) in diagnostics { - print_diagnostic( - path.as_str(), - region, - format!("{}", message) - ); - } - - eprintln!("{} {}", "Compiled".green(), path.bold()); - for (name, def) in exports.iter() { - eprintln!("export {}: {:?}", name.yellow().bold(), def); - } - - main_scope.write().unwrap().import( - exports - ); - - /* link assembly-program to symbols - */ - linker.add_procedure(path.as_str(), bytecode); - } - Err( (region, parse_error) ) => { - print_diagnostic( - path.as_str(), - region, - format!("{:?}", parse_error) - ); - - eprintln!("=======\nParse Error: Abort\n"); - } - } - - } - - /* load & run compiled bytecode - */ - let main_addr = linker - .get_link_addr(&"main.lt".into()) - .expect("'main.lt' not found"); - - let bytecode = linker.link_total().expect("Link error:"); - - eprintln!("{} ({} bytes)", "Linked bytecode.".green(), bytecode.len()); - eprintln!("================\n"); - - vm.load(bytecode); - vm.execute(main_addr); - - eprintln!( - "\n================\nVM execution finished\ndatastack = {:?}\n====", - vm.data_stack - ); -}