add export block and ability to load multiple files

This commit is contained in:
Michael Sippel 2024-05-18 18:01:41 +02:00
parent f06bf14b52
commit 8d19767c98
Signed by: senvas
GPG key ID: F96CF119C34B64A6
13 changed files with 439 additions and 245 deletions

47
lt-stdlib/color.lt Normal file
View file

@ -0,0 +1,47 @@
export {
let morph-rgb-to-hsv = λ{
{
red: _0,1 ~ _256 ~ machine.UInt64;
green: _0,1 ~ _256 ~ machine.UInt64;
blue: _0,1 ~ _256 ~ machine.UInt64;
} : <Color sRGB>
~ RGB
~ <Vec3 _0,1 ~ _256 ~ machine.UInt64>;
/*
::> Color
~ HSV
~ {
hue: Angle
~ Degrees
~ _0,360
~ _360
~ machine.UInt64 ;
saturation: _0,1 ~ _256 ~ machine.UInt64 ;
value: _0,1 ~ _256 ~ machine.UInt64 ;
}
~ <Vec3 machine.UInt64>
*/
} ↦ {
let channel_max = int-max (int-max red green) blue;
let channel_min = int-min (int-min red green) blue;
/* value */
channel_max;
/* saturation */
i/ (i* 255 (i- channel_max channel_min)) channel_max;
/* hue */
i% (i/ (i* 60
if( int-eq channel_max red ) { i+ (i* 0 255) (i- green blue); }
else {
if( int-eq channel_max green ) { i+ (i* 2 255) (i- blue red); }
else { i+ (i* 4 255) (i- red green); };
}
)
(i- channel_max channel_min)
)
360;
};
}

28
lt-stdlib/euclidean.lt Normal file
View file

@ -0,0 +1,28 @@
/* Integer Math
*/
export {
/* Euclidean Algorithm to calculate greatest common divisor
*/
let gcd = λ{
a : ~ machine.Int64;
b : ~ machine.Int64;
} ↦ {
while( b ) {
let tmp = i% a b;
! a b;
! b tmp;
}
a;
};
/* least common multiple
*/
let lcm = λ{
a : ~ machine.Int64;
b : ~ machine.Int64;
} ↦ i* (int-abs a) (i/ (int-abs b) (gcd a b));
};

18
lt-stdlib/int.lt Normal file
View file

@ -0,0 +1,18 @@
/* Two's complement Signed Integers
*/
export {
let int-sign = λx:~machine.Int64 ↦ bit-and (bit-shr x 63) 1;
let int-neg = λx:~machine.Int64 ↦ i+ (bit-neg x) 1;
let int-abs = λx:~machine.Int64 ↦ if( int-sign x ) { int-neg x; } else { x; };
let int-lt = λ{ a:~machine.Int64; b:~machine.Int64; } ↦ int-sign (i- a b);
let int-gt = λ{ a:~machine.Int64; b:~machine.Int64; } ↦ int-sign (i- b a);
let int-eq = λ{ a:~machine.Int64; b:~machine.Int64; } ↦ if (bit-xor a b) { 0; } else { 1; };
let int-lte = λ{ a:~machine.Int64; b:~machine.Int64; } ↦ bit-or (int-lt a b) (int-eq a b);
let int-gte = λ{ a:~machine.Int64; b:~machine.Int64; } ↦ bit-or (int-gt a b) (int-eq a b);
let int-min = λ{ a:~machine.Int64; b:~machine.Int64; } ↦ if( int-lt a b ) { a; } else { b; };
let int-max = λ{ a:~machine.Int64; b:~machine.Int64; } ↦ if( int-gt a b ) { a; } else { b; };
};

63
lt-stdlib/posint.lt Normal file
View file

@ -0,0 +1,63 @@
/* Positional Integer
*/
export {
let fmt-uint-radix = λ{
radix : ~ _2^64 ~ machine.UInt64;
x : ~ _2^64 ~ machine.UInt64;
} ↦ {
if( x ) {
while( x ) {
let digit = (i% x radix);
if( int-lt digit 10 ) {
i+ '0' digit;
} else {
i+ (i- 'a' 10) digit;
};
! x (i/ x radix);
}
} else {
'0';
};
};
let uint-machine-to-posint =
λ{
radix: ~ _2^64 ~ machine.UInt64;
value: ~ _2^64 ~ machine.UInt64;
}
/*
::>
~ <PosInt radix BigEndian>
~ <Seq <Digit radix>
~ _radix
~ _2^64
~ machine.UInt64>
~ <LengthPrefixedArray machine.UInt64>
*/
↦ {
let len = 0;
while( value ) {
/* push digit to sequence on stack */
i% value radix;
! value (i/ value radix);
! len (i+ len 1);
}
/* push length of sequence to stack */
len;
};
let fmt-int-radix = λ{
radix: ~ _2^64 ~ machine.UInt64;
x : ~ machine.Int64;
} ↦ {
fmt-uint-radix radix (int-abs x);
if( int-sign x ) { '-'; };
};
let fmt-uint = λx: ↦ fmt-uint-radix 10 x;
let fmt-int = λx: ↦ fmt-int-radix 10 x;
}

45
lt-stdlib/ratio.lt Normal file
View file

@ -0,0 +1,45 @@
export {
/* Implementation of Rational Numbers
*/
let ratio-scale = λ{
{p:; q:;} : ~ <Ratio ~machine.UInt64> ;
n : ~ machine.UInt64 ;
} ↦ {
i* q n;
i* p n;
};
let ratio-normalize = λ{
p: ~machine.Int64;
q: ~machine.Int64;
} : ~ <Ratio ~machine.Int64>
↦ {
let s = gcd p q;
i/ q s;
i/ p s;
};
let ratio-add = λ{
{ap:; aq:;}: ~ <Ratio ~ _2^64 ~ machine.UInt64> ;
{bp:; bq:;}: ~ <Ratio ~ _2^64 ~ machine.UInt64> ;
} ↦ {
let l = lcm aq bq;
let as = i/ l aq;
let bs = i/ l bq;
i* aq as;
i+ (i* ap as) (i* bp bs);
};
let ratio-mul = λ{
{ap:; aq:;}: ~ <Ratio ~ _2^64 ~ machine.Int64> ;
{bp:; bq:;}: ~ <Ratio ~ _2^64 ~ machine.Int64> ;
} ↦ ratio-normalize (i* ap bp) (i* aq bq);
let fmt-ratio = λ{ p:; q:; }: ~<Ratio > ↦ {
fmt-int q;':';fmt-int p;
};
}

12
lt-stdlib/stdio.lt Normal file
View file

@ -0,0 +1,12 @@
export {
/* output nullterminated string directly from datastack
*/
let print-nullterm =
λ{} : < Seq Char~Ascii~machine.Word >
~ < NullTerminatedArray machine.Word >
↦ {
while(dup) { emit; }
drop;
};
}

23
lt-stdlib/vec3i.lt Normal file
View file

@ -0,0 +1,23 @@
export {
/* Vec3i
*/
let vec3i-add = λ{
{ ax:_2^64; ay:_2^64; az:_2^64; } : <Vec3 _2^64~machine.Int64>;
{ bx:_2^64; by:_2^64; bz:_2^64; } : <Vec3 _2^64~machine.Int64>;
} ↦ {
i+ az bz;
i+ ay by;
i+ ax bx;
};
let fmt-vec3i =
λ{ x:_2^64; y:_2^64; z:_2^64; } : <Vec3 _2^64~machine.Int64>
↦ {
'}';
fmt-int z; '='; 'z'; ' '; ';';
fmt-int y; '='; 'y'; ' '; ';';
fmt-int x; '='; 'x'; '{';
};
}

170
main.lt
View file

@ -1,146 +1,5 @@
{
/*
* Integer Operations
*/
let int-sign = λx:~machine.Int64 ↦ bit-and (bit-shr x 63) 1;
let int-neg = λx:~machine.Int64 ↦ i+ (bit-neg x) 1;
let int-abs = λx:~machine.Int64 ↦ if( int-sign x ) { int-neg x; } else { x; };
let int-lt = λ{ a:~machine.Int64; b:~machine.Int64; } ↦ int-sign (i- a b);
let int-gt = λ{ a:~machine.Int64; b:~machine.Int64; } ↦ int-sign (i- b a);
let int-eq = λ{ a:~machine.Int64; b:~machine.Int64; } ↦ if (i- a b) { 0; } else { 1; };
let int-lte = λ{ a:~machine.Int64; b:~machine.Int64; } ↦ bit-or (int-lt a b) (int-eq a b);
let int-gte = λ{ a:~machine.Int64; b:~machine.Int64; } ↦ bit-or (int-gt a b) (int-eq a b);
let int-min = λ{ a:~machine.Int64; b:~machine.Int64; } ↦ if( int-lt a b ) { a; } else { b; };
let int-max = λ{ a:~machine.Int64; b:~machine.Int64; } ↦ if( int-gt a b ) { a; } else { b; };
/* Euclidean Algorithm to calculate greatest common divisor
*/
let gcd = λ{
a : ~ machine.Int64;
b : ~ machine.Int64;
} ↦ {
while( b ) {
let tmp = i% a b;
! a b;
! b tmp;
}
a;
};
/* least common multiple
*/
let lcm = λ{
a : ~ machine.Int64;
b : ~ machine.Int64;
} ↦ i* (int-abs a) (i/ (int-abs b) (gcd a b));
/* Implementation of Rational Numbers
*/
let ratio-scale = λ{
{p:; q:;} : ~ <Ratio ~machine.UInt64> ;
n : ~ machine.UInt64 ;
} ↦ {
i* q n;
i* p n;
};
let ratio-normalize = λ{
p: ~machine.Int64;
q: ~machine.Int64;
} : ~ <Ratio ~machine.Int64>
↦ {
let s = gcd p q;
i/ q s;
i/ p s;
};
let ratio-add = λ{
{ap:; aq:;}: ~ <Ratio ~ _2^64 ~ machine.UInt64> ;
{bp:; bq:;}: ~ <Ratio ~ _2^64 ~ machine.UInt64> ;
} ↦ {
let l = lcm aq bq;
let as = i/ l aq;
let bs = i/ l bq;
i* aq as;
i+ (i* ap as) (i* bp bs);
};
let ratio-mul = λ{
{ap:; aq:;}: ~ <Ratio ~ _2^64 ~ machine.Int64> ;
{bp:; bq:;}: ~ <Ratio ~ _2^64 ~ machine.Int64> ;
} ↦ ratio-normalize (i* ap bp) (i* aq bq);
let morph-int-to-float =
λx: ~ machine.Int64 ~ machine.Word
↦ {
/* todo */
0;
};
let morph-ratio-to-float =
λ{
p : ~machine.Int64;
q : ~machine.Int64;
} : ~<Ratio ~machine.Int64>
↦ f/ (morph-int-to-float p) (morph-int-to-float q);
/* string output
*/
let print-nullterm =
λ{} : < Seq Char ~Ascii ~ machine.Word >
~ < NullTerminatedArray machine.Word >
↦ {
while(dup) { emit; }
drop;
};
print-nullterm 'H' 'a' 'l' 'l' 'o' ' ' 'W' 'e' 'l' 't' '!' '\n' '\0';
/* integer formatting
*/
let fmt-uint-radix = λ{
radix : ~ _2^64 ~ machine.UInt64;
x : ~ _2^64 ~ machine.UInt64;
} ↦ {
if( x ) {
while( x ) {
let digit = (i% x radix);
if( int-lt digit 10 ) {
i+ '0' digit;
} else {
i+ (i- 'a' 10) digit;
};
! x (i/ x radix);
}
} else {
'0';
};
};
let fmt-int-radix = λ{
radix: ~ _2^64 ~ machine.UInt64;
x : ~ machine.Int64;
} ↦ {
fmt-uint-radix radix (int-abs x);
if( int-sign x ) { '-'; };
};
let fmt-uint = λx: ↦ fmt-uint-radix 10 x;
let fmt-int = λx: ↦ fmt-int-radix 10 x;
/* ratio formatting
*/
let fmt-ratio = λ{ p:; q:; } : ~<Ratio ~machine.Int64> ↦ {
fmt-int q;':';fmt-int p;
};
print-nullterm 'H''e''l''l''o'' ''W''o''r''l''d''!''\n''\0';
/* test ratio
*/
@ -152,29 +11,6 @@
(fmt-ratio (ratio-mul { 4; int-neg 3; } { 7; 4; }))
'\n''\0';
/* Vec3i
*/
let vec3i-add = λ{
{ ax:_2^64; ay:_2^64; az:_2^64; } : <Vec3 _2^64~machine.Int64>;
{ bx:_2^64; by:_2^64; bz:_2^64; } : <Vec3 _2^64~machine.Int64>;
} ↦ {
i+ az bz;
i+ ay by;
i+ ax bx;
};
let fmt-vec3i =
λ{ x:_2^64; y:_2^64; z:_2^64; } : <Vec3 _2^64~machine.Int64>
↦ {
'}';
fmt-int z; '='; 'z'; ' '; ';';
fmt-int y; '='; 'y'; ' '; ';';
fmt-int x; '='; 'x'; '{';
};
/* Colors
*/
let red-u8rgb
@ -185,6 +21,8 @@
let blue-u8rgb = λ{} ↦ { 255; 0; 0; };
let yellow-u8rgb = λ{} ↦ { 0; 220; 220; };
print-nullterm (fmt-vec3i (morph-rgb-to-hsv yellow-u8rgb)) '\n''\0';
print-nullterm
(fmt-vec3i green-u8rgb)
' ''+'' '
@ -192,5 +30,7 @@
' ''='' '
(fmt-vec3i (vec3i-add green-u8rgb blue-u8rgb))
'\n''\0';
uint-machine-to-posint 16 256;
}

View file

@ -60,6 +60,9 @@ pub enum LTExpr {
Block {
statements: Vec<Statement>,
},
ExportBlock {
statements: Vec<Statement>,
}
}
impl LTExpr {

View file

@ -76,8 +76,10 @@ fn print_diagnostic(
}
/* TODO:
* - export / import , load multiple files
* - Compiler error reporting
* - parse float literals
* - return type annotation
* - write to address resulting from expression
* - sized objects
* - Typecheck for LTExpr::Application
@ -96,8 +98,17 @@ fn main() {
/* open source file
*/
let args: Vec<String> = std::env::args().collect();
let path = &args[1];
let iter_chars = iterate_text::file::characters::IterateFileCharacters::new(path);
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
*/
@ -110,38 +121,52 @@ fn main() {
match parser::parse_expr( &typectx, &mut program_tokens ) {
Ok( ast ) => {
let bytecode = ProcedureCompiler::new(&main_scope)
let (exports, bytecode) = ProcedureCompiler::new(&main_scope)
.compile(&ast)
.into_asm(&"main".into());
.into_asm(&path);
eprintln!("{}", "Compiled successfully.\n======================\n".green());
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("main", bytecode);
/* load & run compiled bytecode
*/
let main_addr = linker
.get_link_addr(&"main".into())
.expect("'main' not linked");
vm.load(linker.link_total().expect("could not link"));
vm.execute(main_addr);
eprintln!(
"\n====\nVM execution finished\ndatastack = {:?}\n====",
vm.data_stack
);
linker.add_procedure(path.as_str(), bytecode);
}
Err( (region, parse_error) ) => {
print_diagnostic(
path,
path.as_str(),
region,
format!("{:?}", parse_error)
);
eprintln!("=======\nerror: Parse Error\n");
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
);
}

View file

@ -364,7 +364,14 @@ where It: Iterator<Item = (InputRegionTag, Result<LTIRToken, LexError>)>
if_expr: Box::new(if_expr),
else_expr: Box::new(else_expr),
});
}
},
"export" => {
tokens.next();
let block = parse_statement_block(typectx, tokens)?;
children.push(LTExpr::ExportBlock {
statements: block
});
},
name => {
children.push(parse_atom(tokens)?);
}

View file

@ -11,7 +11,7 @@ use {
};
pub struct ProcedureCompiler {
symbols: Arc<RwLock<Scope>>,
pub symbols: Arc<RwLock<Scope>>,
asm: tisc::Assembler,
linker: tisc::Linker,
result_size: usize,
@ -27,8 +27,17 @@ impl ProcedureCompiler {
}
}
pub fn into_asm(mut self, proc_symbol: &String) -> Vec<tisc::assembler::AssemblyWord> {
let data_frame_size = self.symbols.read().unwrap().get_frame_size() as i64;
pub fn into_asm(mut self, proc_symbol: &String) -> (Vec<(String, SymbolDef)>, Vec<tisc::assembler::AssemblyWord>) {
let mut symbols =
Arc::try_unwrap(self.symbols).ok().unwrap()
.into_inner().unwrap();
symbols.update_link_addresses(
proc_symbol,
&self.linker
);
let data_frame_size = symbols.get_frame_size() as i64;
let body = self.asm.build();
self.linker.add_procedure("__procedure_body__", body);
@ -36,6 +45,7 @@ impl ProcedureCompiler {
.linker
.get_link_addr(&"__procedure_body__".into())
.unwrap();
let subroutines = self
.linker
.link_relative(&"__subroutines__".into())
@ -58,21 +68,37 @@ impl ProcedureCompiler {
superlink.add_procedure("", entry.build());
superlink.add_procedure("__subroutines__", subroutines);
let bytecode = superlink.link_relative(proc_symbol).expect("link error");
/*
eprintln!("\n\n{}:", proc_symbol);
for (i,w) in tisc::assembler::disassemble(&bytecode).iter().enumerate() {
eprintln!("{}:\t\t{}", i, w);
symbols.update_link_addresses(
&proc_symbol,
&superlink
);
let mut symbol_exports = symbols.export();
let subroutines_addr = superlink.get_link_addr(&"__subroutines__".into()).unwrap();
for (name, def) in symbol_exports.iter_mut() {
match def {
SymbolDef::Procedure{ in_types:_, out_types:_, link_addr, export:_ } => {
match link_addr {
LinkAddr::Relative{ symbol, offset } => {
*offset += subroutines_addr;
}
*/
bytecode
LinkAddr::Absolute(addr) => {
*addr += subroutines_addr;
}
}
}
_ => {}
}
}
let bytecode = superlink.link_relative(proc_symbol).expect("link error");
(symbol_exports, bytecode)
}
pub fn verify(&self) {
// todo
}
pub fn compile_statement(mut self, statement: &Statement) -> Self {
pub fn compile_statement(mut self, statement: &Statement, enable_export: bool) -> Self {
match statement {
Statement::Assignment { var_id, val_expr } => {
self = self.compile(val_expr);
@ -91,6 +117,7 @@ impl ProcedureCompiler {
in_types,
out_types,
link_addr,
export
}) => {
self.asm = self
.asm
@ -111,12 +138,23 @@ impl ProcedureCompiler {
self.symbols
.write()
.unwrap()
.declare_proc(var_id.clone(), vec![], vec![]);
let lambda_procedure = ProcedureCompiler::new(&self.symbols)
.declare_proc(var_id.clone(), vec![], vec![], enable_export);
let (exports, lambda_procedure) = ProcedureCompiler::new(&self.symbols)
.compile(val_expr)
.into_asm(var_id);
self.linker.add_procedure(var_id, lambda_procedure);
let offset = self.linker.get_link_addr(var_id).unwrap();
if enable_export {
for (name, def) in exports.iter() {
eprintln!("Procedure compiler: export {}", name);
}
self.symbols.write().unwrap().import( exports );
}
}
_ => {
self.symbols
@ -126,7 +164,7 @@ impl ProcedureCompiler {
self = self.compile_statement(&Statement::Assignment {
var_id: var_id.clone(),
val_expr: val_expr.clone(),
});
}, false);
}
},
Statement::WhileLoop { condition, body } => {
@ -138,7 +176,7 @@ impl ProcedureCompiler {
self.asm = tisc::Assembler::new();
for statement in body.into_iter() {
self = self.compile_statement(statement);
self = self.compile_statement(statement, false);
}
let body_asm = self.asm;
@ -168,8 +206,9 @@ impl ProcedureCompiler {
in_types,
out_types,
link_addr,
export
}) => {
self.asm = self.asm.call(symbol.as_str());
self.asm = self.asm.call_symbol(link_addr);
}
None => {
eprintln!("undefined symbol '{}'!", symbol);
@ -218,7 +257,12 @@ impl ProcedureCompiler {
}
LTExpr::Block { statements } => {
for s in statements.iter() {
self = self.compile_statement(s);
self = self.compile_statement(s, false);
}
}
LTExpr::ExportBlock{ statements } => {
for s in statements.iter() {
self = self.compile_statement(s, true);
}
}
}

View file

@ -4,6 +4,7 @@ use {
collections::HashMap,
sync::{Arc, RwLock},
},
tisc::linker::LinkAddr,
};
#[derive(Clone, Debug)]
@ -19,7 +20,8 @@ pub enum SymbolDef {
Procedure {
in_types: Vec<laddertypes::TypeTerm>,
out_types: Vec<laddertypes::TypeTerm>,
link_addr: Option<tisc::VM_Word>,
link_addr: LinkAddr,
export: bool
},
}
@ -35,6 +37,7 @@ impl SymbolDef {
in_types,
out_types,
link_addr: _,
export: _,
} => laddertypes::TypeTerm::App(vec![
typectx
.write()
@ -94,6 +97,24 @@ impl Scope {
Arc::new(RwLock::new(s))
}
pub fn export(self) -> Vec<(String, SymbolDef)> {
self.symbols
.into_iter()
.filter(|(name, def)|
match def {
SymbolDef::Procedure { in_types:_, out_types:_, link_addr:_, export } => *export,
_ => false
}
)
.collect()
}
pub fn import(&mut self, symbol_imports: Vec<(String, SymbolDef)>) {
for (name, def) in symbol_imports {
self.symbols.insert( name, def );
}
}
pub fn get_frame_size(&self) -> usize {
self.frame_size
}
@ -116,20 +137,35 @@ impl Scope {
}
}
}
/*
pub fn get_link_addr(&self, name: &str) -> Option<tisc::VM_Word> {
match self.get(name) {
Some(SymbolDef::Procedure{ in_types:_, out_types:_, link_addr }) => {
link_addr
}
Some(SymbolDef::StaticRef { typ:_, link_addr }) => {
link_addr
}
Some(SymbolDef::FrameRef { typ:_, stack_ref:_ }) => None,
None => None
/// takes the link-addresses from a Linker
/// and updates the symbol table to relative addresses
/// based on the next super-label
pub fn update_link_addresses(
&mut self,
base_symbol: &String,
linker: &tisc::Linker
) {
for (name, def) in self.symbols.iter_mut() {
if let Some(offset) = linker.get_link_addr( name ) {
match def {
SymbolDef::Procedure {
in_types:_,out_types:_,
link_addr,
export:_
} => {
*link_addr = LinkAddr::Relative{
symbol: base_symbol.clone(),
offset
};
},
_ => {}
}
}
*/
}
}
//<><><><><><>
pub fn declare_proc_parse(
@ -165,6 +201,7 @@ impl Scope {
.expect("parse typeterm")
})
.collect(),
false
);
}
@ -173,13 +210,15 @@ impl Scope {
name: String,
in_types: Vec<laddertypes::TypeTerm>,
out_types: Vec<laddertypes::TypeTerm>,
export: bool
) {
self.symbols.insert(
name,
name.clone(),
SymbolDef::Procedure {
link_addr: None, //LinkAddr::Relative{ name, offset: 0 },
link_addr: LinkAddr::Relative{ symbol: name, offset: 0 },
in_types,
out_types,
export
},
);
}