separate crates for compiler-lib, compiler-cli and vm
This commit is contained in:
parent
72122bf4fc
commit
4e9a4d1204
17 changed files with 337 additions and 191 deletions
17
Cargo.toml
17
Cargo.toml
|
@ -1,11 +1,6 @@
|
||||||
[package]
|
[workspace]
|
||||||
name = "ltir"
|
members = [
|
||||||
version = "0.1.0"
|
"lib-ltcore",
|
||||||
edition = "2021"
|
"ltcc",
|
||||||
|
"ltvm"
|
||||||
[dependencies]
|
]
|
||||||
laddertypes = { path = "../lib-laddertypes" }
|
|
||||||
tisc = { path = "../lib-tisc" }
|
|
||||||
iterate-text = "0.0.1"
|
|
||||||
tiny-ansi = "0.1.0"
|
|
||||||
|
|
||||||
|
|
10
lib-ltcore/Cargo.toml
Normal file
10
lib-ltcore/Cargo.toml
Normal file
|
@ -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"] }
|
||||||
|
|
8
lib-ltcore/src/lib.rs
Normal file
8
lib-ltcore/src/lib.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
pub mod expr;
|
||||||
|
pub mod lexer;
|
||||||
|
pub mod parser;
|
||||||
|
pub mod procedure_compiler;
|
||||||
|
pub mod runtime;
|
||||||
|
pub mod symbols;
|
||||||
|
|
13
ltcc/Cargo.toml
Normal file
13
ltcc/Cargo.toml
Normal file
|
@ -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"
|
||||||
|
|
8
ltcc/hello.lt
Normal file
8
ltcc/hello.lt
Normal file
|
@ -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';
|
||||||
|
};
|
||||||
|
}
|
70
ltcc/src/diagnostic.rs
Normal file
70
ltcc/src/diagnostic.rs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
125
ltcc/src/main.rs
Normal file
125
ltcc/src/main.rs
Normal file
|
@ -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 );
|
||||||
|
}
|
30
ltcc/src/oldmain.rs
Normal file
30
ltcc/src/oldmain.rs
Normal file
|
@ -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);
|
||||||
|
|
||||||
|
}
|
12
ltvm/Cargo.toml
Normal file
12
ltvm/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "ltvm"
|
||||||
|
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"
|
||||||
|
bincode = "1.3.3"
|
||||||
|
|
55
ltvm/src/main.rs
Normal file
55
ltvm/src/main.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
use {
|
||||||
|
std::io::Read,
|
||||||
|
clap::Parser,
|
||||||
|
tiny_ansi::TinyAnsi,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
struct Args {
|
||||||
|
/// source files
|
||||||
|
sources: Vec< String >,
|
||||||
|
|
||||||
|
/// 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::<String, tisc::LinkAddr>::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
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
180
src/main.rs
180
src/main.rs
|
@ -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<String> = 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
|
|
||||||
);
|
|
||||||
}
|
|
Loading…
Reference in a new issue