Fix structure

This commit is contained in:
Felipe g 2022-09-19 13:06:12 -03:00
parent bdd438b8f7
commit 1a02e2d1e3
14 changed files with 1423 additions and 14 deletions

2
Cargo.lock generated
View File

@ -430,7 +430,7 @@ dependencies = [
[[package]]
name = "kind2"
version = "0.2.76"
version = "0.2.72"
dependencies = [
"clap",
"highlight_error",

View File

@ -14,3 +14,4 @@ hvm = "0.1.81"
#hvm = { path = "../hvm" }
highlight_error = "0.1.1"
clap = { version = "3.1.8", features = ["derive"] }
rand = "0.8.5"

View File

@ -11,7 +11,7 @@ pub fn to_checker_oper(oper: &Operator) -> String {
Operator::Div => "Kind.Operator.div".to_string(),
Operator::Mod => "Kind.Operator.mod".to_string(),
Operator::And => "Kind.Operator.and".to_string(),
Operator::Or => "Kind.Operator.or".to_string(),
Operator::Or => "Kind.Operator.or".to_string(),
Operator::Xor => "Kind.Operator.xor".to_string(),
Operator::Shl => "Kind.Operator.shl".to_string(),
Operator::Shr => "Kind.Operator.shr".to_string(),

3
src/codegen.rs Normal file
View File

@ -0,0 +1,3 @@
pub mod kdl;
pub mod hvm;

263
src/codegen/kdl.rs Normal file
View File

@ -0,0 +1,263 @@
pub mod book;
use crate::book::name::Ident;
use crate::book::Book;
use crate::codegen::kdl::book::{CompBook, CompEntry, CompRule, CompTerm};
use rand::Rng;
use std::collections::HashMap;
pub const KDL_NAME_LEN: usize = 12;
pub fn to_kdl_term(kdl_names: &HashMap<String, String>, term: &CompTerm) -> Result<String, String> {
let term = match term {
CompTerm::Var { name } => {
format!("{}", name)
}
CompTerm::Lam { name, body } => {
let body = to_kdl_term(kdl_names, body)?;
format!("@{} {}", name, body)
}
CompTerm::App { func, argm } => {
let func = to_kdl_term(kdl_names, func)?;
let argm = to_kdl_term(kdl_names, argm)?;
format!("({} {})", func, argm)
}
CompTerm::Dup {
nam0,
nam1,
expr,
body,
} => {
let expr = to_kdl_term(kdl_names, expr)?;
let body = to_kdl_term(kdl_names, body)?;
format!("dup {} {} = {}; {}", nam0, nam1, expr, body)
}
CompTerm::Let { name, expr, body } => {
let expr = to_kdl_term(kdl_names, expr)?;
let body = to_kdl_term(kdl_names, body)?;
format!("let {} = {}; {}", name, expr, body)
}
CompTerm::Ctr { name, args } => {
let kdl_name = kdl_names.get(name).expect(&format!("{}", name));
let args = args
.iter()
.map(|x| to_kdl_term(kdl_names, x))
.collect::<Result<Vec<String>, String>>()?;
let args = args.iter().map(|x| format!(" {}", x)).collect::<String>();
format!("{{{}{}}}", kdl_name, args)
}
CompTerm::Fun { name, args } => {
let kdl_name = kdl_names.get(name).expect(&format!("{}", name));
let args = args
.iter()
.map(|x| to_kdl_term(kdl_names, x))
.collect::<Result<Vec<String>, String>>()?;
let args = args.iter().map(|x| format!(" {}", x)).collect::<String>();
format!("({}{})", kdl_name, args)
}
CompTerm::Num { numb } => {
format!("#{}", numb)
}
CompTerm::Op2 { oper, val0, val1 } => {
let val0 = to_kdl_term(kdl_names, val0)?;
let val1 = to_kdl_term(kdl_names, val1)?;
format!("({} {} {})", oper, val0, val1)
}
CompTerm::Nil => {
return Err("Found nil term in compiled term while converting to kindelia".to_string());
}
};
Ok(term)
}
pub fn to_kdl_rule(
_book: &Book,
kdl_names: &HashMap<String, String>,
rule: &CompRule,
) -> Result<String, String> {
let name = &rule.name;
let kdl_name = kdl_names.get(name).unwrap();
let mut pats = vec![]; // stringified pattern args
for pat in rule.pats.iter() {
let pat = to_kdl_term(kdl_names, &pat)?;
pats.push(" ".to_string());
pats.push(pat);
}
let body = to_kdl_term(kdl_names, &rule.body)?;
let rule = format!("({}{}) = {}", kdl_name, pats.join(""), body);
Ok(rule)
}
pub fn to_kdl_entry(
book: &Book,
kdl_names: &HashMap<String, String>,
entry: &CompEntry,
) -> Result<String, String> {
let entry = match entry.name.as_str() {
// Main is compiled to a run block
// TODO: Maybe we should have run blocks come from a specific type of function instead
// TODO: run statements should always come last in the block
"Main" => format!(
"run {{\n {}\n}}\n\n",
to_kdl_term(kdl_names, &*entry.rules[0].body)?
),
_ => {
let kdl_name = kdl_names.get(&entry.name).unwrap();
let args_names = entry
.args
.iter()
.map(|arg| format!(" {}", arg))
.collect::<String>();
// If this entry existed in the original kind code, add some annotations as comments
let kind_entry = book.entrs.get(&Ident(entry.name.clone()));
let is_knd_ent = matches!(kind_entry, Some(_));
let cmnt = if is_knd_ent {
let kind_entry = kind_entry.unwrap();
let args_typed = kind_entry
.args
.iter()
.map(|arg| {
format!(
" {}({}: {})",
if arg.eras { "-" } else { "" },
arg.name,
&arg.tipo
)
})
.collect::<String>();
let kind_name = format!("{} #{}", entry.name, kdl_name);
format!("// {}{} : {}\n", kind_name, args_typed, &kind_entry.tipo)
} else {
String::new()
};
// Entries with no rules become constructors
// Entries with rules become functions
let fun = if entry.rules.is_empty() {
format!("ctr {{{}{}}}\n\n", kdl_name, args_names)
} else {
let mut rules = vec![];
for rule in &entry.rules {
rules.push(format!("\n {}", to_kdl_rule(book, kdl_names, rule)?));
}
format!(
"fun ({}{}) {{{}\n}}\n\n",
kdl_name,
args_names,
rules.join("")
)
};
cmnt + &fun
}
};
Ok(entry)
}
pub fn to_kdl_book(
book: &Book,
kdl_names: &HashMap<String, String>,
comp_book: &CompBook,
) -> Result<String, String> {
let mut lines = vec![];
for name in &comp_book.names {
let entry = comp_book.entrs.get(name).unwrap();
lines.push(to_kdl_entry(book, kdl_names, entry)?);
}
Ok(lines.join(""))
}
// Utils
// -----
// Returns a map of kind names to kindelia names
// Returns an err if any of the names can't be converted
pub fn get_kdl_names(book: &CompBook) -> Result<HashMap<String, String>, String> {
// Fits a name to the max size allowed by kindelia.
// If the name is too large, truncates and replaces the last characters by random chars.
// Fails if the namespace is too large.
fn rand_shorten(name: &String) -> Result<String, String> {
let (ns, fun) = name.rsplit_once('.').unwrap_or(("", name));
let ns = if !ns.is_empty() {
format!("{}.", ns)
} else {
ns.to_string()
};
if ns.len() > KDL_NAME_LEN - 1 {
let err = format!(
"Namespace for \"{}\" has more than {} characters.",
name,
KDL_NAME_LEN - 1
);
return Err(err);
}
let max_fn_name = KDL_NAME_LEN - ns.len();
// If the name doesn't fit, truncate and insert some random characters at the end
let fun = if fun.len() > max_fn_name {
let n_rnd_chrs = usize::min(3, max_fn_name);
let fun_cut = fun[..max_fn_name - n_rnd_chrs].to_string();
let mut rng = rand::thread_rng();
let rnd_chrs = (0..n_rnd_chrs)
.map(|_| rng.gen_range(0..63))
.map(|n| encode_base64(n))
.collect::<String>();
format!("{}{}", fun_cut, rnd_chrs)
} else {
fun.to_string()
};
Ok(format!("{}{}", ns, fun))
}
fn get_kdl_name(entry: &CompEntry) -> Result<String, String> {
let kind_name = &entry.name;
let kdln = match &entry.kdln {
Some(kdln) => {
// If the entry uses a kindelia name, use it
if !kdln.chars().next().unwrap().is_uppercase() {
let err = format!(
"Kindelia name \"{}\" doesn't start with an uppercase letter.",
kdln
);
return Err(err);
}
if entry.orig {
if kdln.len() > KDL_NAME_LEN {
let err = format!(
"Kindelia name \"{}\" for \"{}\" has more than {} characters.",
kdln,
kind_name,
KDL_NAME_LEN - 1
);
return Err(err);
}
kdln.clone()
} else {
// For entries created by the flattener, we shorten even the kindelia name
// TODO: Since these rules can come first,
// if the kdln is too large the err will happen in the generated function,
// potentially confusing the user.
rand_shorten(kdln)?
}
}
// Otherwise, try to fit the normal kind name
None => rand_shorten(kind_name)?,
};
Ok(kdln)
}
fn encode_base64(num: u8) -> char {
match num {
0..=9 => (num + b'0') as char,
10..=35 => (num - 10 + b'A') as char,
36..=61 => (num - 36 + b'a') as char,
62.. => '_',
}
}
let mut kdl_names = HashMap::new();
for name in &book.names {
let kdln = get_kdl_name(book.entrs.get(name).unwrap())?;
kdl_names.insert(name.clone(), kdln);
}
Ok(kdl_names)
}

1131
src/codegen/kdl/book.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
pub mod hvm;

View File

@ -10,7 +10,7 @@ use crate::driver::loader::File;
use crate::codegen;
use crate::parser::new_type;
const CHECKER_HVM: &str = include_str!("../checker.hvm");
const CHECKER_HVM: &str = include_str!("checker.hvm");
pub struct RunResult {
output: String,
@ -195,3 +195,12 @@ pub fn cmd_run_main(path: &str) -> Result<(), String> {
Err("Main not found.".to_string())
}
}
pub fn cmd_to_kdl(path: &str) -> Result<(), String> {
let loaded = load(path)?;
let comp_book = codegen::kdl::book::compile_book(&loaded.book)?;
let kdl_names = codegen::kdl::get_kdl_names(&comp_book)?;
let result = codegen::kdl::to_kdl_book(&loaded.book, &kdl_names, &comp_book)?;
print!("{}", result);
Ok(())
}

View File

@ -82,7 +82,6 @@ pub fn load_entry(name: &str, load: &mut Load) -> Result<(), String> {
}
pub fn load(name: &str) -> Result<Load, String> {
let mut load = Load::new_empty();
if !std::path::Path::new(name).is_file() {

View File

@ -62,13 +62,16 @@ pub trait Adjust {
where
Self: Sized,
{
self.adjust(false, &mut AdjustState {
book,
eras: 0,
holes: 0,
vars: Vec::new(),
types: HashMap::new(),
})
self.adjust(
false,
&mut AdjustState {
book,
eras: 0,
holes: 0,
vars: Vec::new(),
types: HashMap::new(),
},
)
}
}

View File

@ -1,10 +1,11 @@
pub mod book;
pub mod checker;
pub mod codegen;
pub mod driver;
pub mod lowering;
pub mod parser;
pub mod codegen;
use crate::driver::*;
use clap::{Parser, Subcommand};
@ -62,7 +63,7 @@ fn run_cli() -> Result<(), String> {
Command::Derive { file: path } => cmd_derive(&path),
Command::GenChecker { file: path } => cmd_gen_checker(&path),
Command::Show { file: path } => cmd_show(&path),
Command::ToKDL { file: _ } => todo!(),
Command::ToKDL { file: path } => cmd_to_kdl(&path),
Command::ToHVM { file: path } => cmd_to_hvm(&path),
}
}