Skip to content

Commit

Permalink
random fixes related to codegen; bump 0.3.0-a11
Browse files Browse the repository at this point in the history
  • Loading branch information
tiye committed Apr 28, 2021
1 parent b01b8ef commit 56dc00c
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 42 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "calcit_runner"
version = "0.3.0-a10"
version = "0.3.0-a11"
authors = ["jiyinyiyong <jiyinyiyong@gmail.com>"]
edition = "2018"
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@calcit/procs",
"version": "0.3.0-a10",
"version": "0.3.0-a11",
"main": "./lib/calcit.procs.js",
"devDependencies": {
"@types/node": "^14.14.41",
Expand Down
2 changes: 1 addition & 1 deletion src/builtins/effects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ pub fn get_env(xs: &CalcitItems) -> Result<Calcit, String> {
Some(Calcit::Str(s)) => match env::var(s) {
Ok(v) => Ok(Calcit::Str(v)),
Err(e) => {
println!("get-env {}", e);
println!("(get-env {}): {}", s, e);
Ok(Calcit::Nil)
}
},
Expand Down
4 changes: 3 additions & 1 deletion src/call_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ pub enum StackKind {
Fn,
Proc,
Macro,
Syntax, // rarely used
Syntax, // rarely used
Codegen, // track preprocessing
}

// TODO impl fmt
Expand Down Expand Up @@ -112,5 +113,6 @@ fn name_kind(k: &StackKind) -> String {
StackKind::Proc => String::from("proc"),
StackKind::Macro => String::from("macro"),
StackKind::Syntax => String::from("syntax"),
StackKind::Codegen => String::from("codegen"),
}
}
1 change: 1 addition & 0 deletions src/cirru/calcit-core.cirru
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,7 @@
|do $ quote
defmacro do (& body)
; echo "|body:" (format-to-lisp body)
assert "|empty do is not okay" $ not $ empty? body
quasiquote
&let nil
~@ body
Expand Down
104 changes: 70 additions & 34 deletions src/codegen/emit_js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ mod internal_states;
mod snippets;

use std::cell::RefCell;
use std::cmp::Ordering;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::fs;
use std::path::Path;

use crate::builtins::meta::{js_gensym, reset_js_gensym_index};
use crate::builtins::{is_proc_name, is_syntax_name};
use crate::call_stack;
use crate::call_stack::StackKind;
use crate::primes;
use crate::primes::{Calcit, CalcitItems, SymbolResolved::*};
use crate::program;
Expand Down Expand Up @@ -266,12 +269,14 @@ fn gen_call_code(
match s.as_str() {
"if" => match (body.get(0), body.get(1)) {
(Some(condition), Some(true_branch)) => {
call_stack::push_call_stack(ns, "if", StackKind::Codegen, &Some(xs.to_owned()), &im::vector![]);
let false_code = match body.get(2) {
Some(fal) => to_js_code(fal, ns, local_defs, file_imports)?,
None => String::from("null"),
};
let cond_code = to_js_code(condition, ns, local_defs, file_imports)?;
let true_code = to_js_code(true_branch, ns, local_defs, file_imports)?;
call_stack::pop_call_stack();
Ok(format!("( {} ? {} : {} )", cond_code, true_code, false_code))
}
(_, _) => Err(format!("if expected 2~3 nodes, got: {:?}", body)),
Expand All @@ -289,7 +294,9 @@ fn gen_call_code(
(Some(Calcit::Symbol(sym, ..)), Some(v)) => {
// let _name = escape_var(sym); // TODO
let ref_path = wrap_js_str(&format!("{}/{}", ns, sym.clone()));
call_stack::push_call_stack(ns, sym, StackKind::Codegen, &Some(xs.to_owned()), &im::vector![]);
let value_code = &to_js_code(v, ns, local_defs, file_imports)?;
call_stack::pop_call_stack();
Ok(format!(
"\n({}peekDefatom({}) ?? {}defatom({}, {}))\n",
&var_prefix, &ref_path, &var_prefix, &ref_path, value_code
Expand All @@ -302,7 +309,10 @@ fn gen_call_code(
"defn" => match (body.get(0), body.get(1)) {
(Some(Calcit::Symbol(sym, ..)), Some(Calcit::List(ys))) => {
let func_body = body.skip(2);
gen_js_func(sym, &ys, &func_body, ns, false, local_defs, file_imports)
call_stack::push_call_stack(ns, sym, StackKind::Codegen, &Some(xs.to_owned()), &im::vector![]);
let ret = gen_js_func(sym, &ys, &func_body, ns, false, local_defs, file_imports);
call_stack::pop_call_stack();
ret
}
(_, _) => Err(format!("defn expected name arguments, got: {:?}", body)),
},
Expand Down Expand Up @@ -330,10 +340,13 @@ fn gen_call_code(
}
"try" => match (body.get(0), body.get(1)) {
(Some(expr), Some(handler)) => {
call_stack::push_call_stack(ns, "try", StackKind::Codegen, &Some(xs.to_owned()), &im::vector![]);
let code = to_js_code(expr, ns, local_defs, file_imports)?;
let err_var = js_gensym("errMsg");
let handler = to_js_code(handler, ns, local_defs, file_imports)?;

call_stack::pop_call_stack();

Ok(snippets::tmpl_fn_wrapper(snippets::tmpl_try(err_var, code, handler)))
}
(_, _) => Err(format!("try expected 2 nodes, got {:?}", body)),
Expand Down Expand Up @@ -540,13 +553,12 @@ fn gen_let_code(
// defined new local variable
let mut scoped_defs = local_defs.clone();
let mut defs_code = String::from("");
let mut variable_existed = false;
let mut body_part = String::from("");

// break unless nested &let is found
loop {
if let_def_body.len() <= 1 {
return Err(format!("unexpected empty content in let, {:?}", xs));
return Err(format!("&let expected body, but got empty, {}", xs.lisp_str()));
}
let pair = let_def_body[0].clone();
let content = let_def_body.skip(1);
Expand All @@ -569,23 +581,16 @@ fn gen_let_code(
}
Calcit::List(xs) if xs.len() == 2 => {
let def_name = xs[0].clone();
let expr_code = xs[1].clone();
let def_code = xs[1].clone();

match def_name {
Calcit::Symbol(sym, ..) => {
// TODO `let` inside expressions makes syntax error
let left = escape_var(&sym);
let right = to_js_code(&expr_code, &ns, &scoped_defs, file_imports)?;

let right = to_js_code(&def_code, &ns, &scoped_defs, file_imports)?;
defs_code.push_str(&format!("let {} = {};\n", left, right));

if scoped_defs.contains(&sym) {
variable_existed = true;
} else {
scoped_defs.insert(sym.clone());
}

if variable_existed {
for (idx, x) in content.iter().enumerate() {
if idx == content.len() - 1 {
body_part.push_str("return ");
Expand All @@ -604,14 +609,19 @@ fn gen_let_code(
return Ok(make_let_with_wrapper(&left, &right, &body_part));
}
} else {
// track variable
scoped_defs.insert(sym.clone());

if content.len() == 1 {
let child = content[0].clone();
match child {
Calcit::List(ys) if ys.len() == 2 => match (&ys[0], &ys[1]) {
(Calcit::Symbol(sym, ..), Calcit::List(zs)) if sym == "&let" && zs.len() == 2 => {
let_def_body = ys.skip(1);
continue;
}
match &content[0] {
Calcit::List(ys) if ys.len() > 2 => match (&ys[0], &ys[1]) {
(Calcit::Syntax(sym, _ns), Calcit::List(zs)) if sym == "&let" && zs.len() == 2 => match &zs[0] {
Calcit::Symbol(s2, ..) if !scoped_defs.contains(s2) => {
let_def_body = ys.skip(1);
continue;
}
_ => (),
},
_ => (),
},
_ => (),
Expand All @@ -632,7 +642,7 @@ fn gen_let_code(
break;
}
}
_ => return Err(format!("Expected symbol behind let, got: {}", &pair)),
_ => return Err(format!("Expected symbol in &let binding, got: {}", &pair)),
}
}
Calcit::List(_xs) => return Err(format!("expected pair of length 2, got: {}", &pair)),
Expand Down Expand Up @@ -838,8 +848,6 @@ fn contains_symbol(xs: &Calcit, y: &str) -> bool {
}

fn sort_by_deps(deps: &HashMap<String, Calcit>) -> Vec<String> {
let mut result: Vec<String> = vec![];

let mut deps_graph: HashMap<String, HashSet<String>> = HashMap::new();
let mut def_names: Vec<String> = vec![];
for (k, v) in deps {
Expand All @@ -856,26 +864,40 @@ fn sort_by_deps(deps: &HashMap<String, Calcit>) -> Vec<String> {
}
deps_graph.insert(k.to_string(), deps_info);
}
// echo depsGraph
def_names.sort();
for x in def_names {
let mut inserted = false;
// println!("\ndefs graph {:?}", deps_graph);
def_names.sort(); // alphabet order first

let mut result: Vec<String> = vec![];
'outer: for x in def_names {
for (idx, y) in result.iter().enumerate() {
if deps_graph.contains_key(y) && deps_graph[y].contains(&x) {
if depends_on(y, &x, &deps_graph, 3) {
result.insert(idx, x.clone());
inserted = true;
break;
continue 'outer;
}
}
if inserted {
continue;
}
result.push(x.clone());
}
// println!("\ndef names {:?}", def_names);

result
}

// could be slow, need real topology sorting
fn depends_on(x: &str, y: &str, deps: &HashMap<String, HashSet<String>>, decay: usize) -> bool {
if decay == 0 {
false
} else {
for item in &deps[x] {
if item == y || depends_on(&item, y, &deps, decay - 1) {
return true;
} else {
// nothing
}
}
false
}
}

fn write_file_if_changed(filename: &Path, content: &str) -> bool {
if filename.exists() && fs::read_to_string(filename).unwrap() == content {
return false;
Expand Down Expand Up @@ -972,17 +994,27 @@ pub fn emit_js(entry_ns: &str, emit_path: &str) -> Result<(), String> {
escape_var(&def)
));
}
Calcit::Fn(_name, _def_ns, _, _, args, code) => {
Calcit::Fn(name, def_ns, _, _, args, code) => {
call_stack::push_call_stack(def_ns, name, StackKind::Codegen, &Some(f.to_owned()), &im::vector![]);
defs_code.push_str(&gen_js_func(&def, args, code, &ns, true, &def_names, &file_imports)?);
call_stack::pop_call_stack();
}
Calcit::Thunk(code) => {
// TODO need topological sorting for accuracy
// values are called directly, put them after fns
call_stack::push_call_stack(
&ns,
&def,
StackKind::Codegen,
&Some((**code).to_owned()),
&im::vector![],
);
vals_code.push_str(&format!(
"\nexport var {} = {};\n",
escape_var(&def),
to_js_code(code, &ns, &def_names, &file_imports)?
));
call_stack::pop_call_stack()
}
Calcit::Macro(..) => {
// macro should be handled during compilation, psuedo code
Expand Down Expand Up @@ -1017,7 +1049,11 @@ pub fn emit_js(entry_ns: &str, emit_path: &str) -> Result<(), String> {
));
}
ImportedTarget::FromNs(target_ns) => {
let import_target = to_js_import_name(&target_ns, false); // TODO js_mode
let import_target = if is_cirru_string(&target_ns) {
wrap_js_str(&target_ns[1..])
} else {
to_js_import_name(&target_ns, false) // TODO js_mode
};
import_code.push_str(&format!(
"\nimport {{ {} }} from {};\n",
escape_var(&def),
Expand Down
4 changes: 3 additions & 1 deletion src/data/cirru.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub fn code_to_calcit(xs: &Cirru, ns: &str) -> Result<Calcit, String> {
_ => match s.chars().next().unwrap() {
':' => Ok(Calcit::Keyword(String::from(&s[1..]))),
'"' | '|' => Ok(Calcit::Str(String::from(&s[1..]))),
'0' if s.starts_with("0x") => match u8::from_str_radix(&s[2..], 16) {
'0' if s.starts_with("0x") => match u32::from_str_radix(&s[2..], 16) {
Ok(n) => Ok(Calcit::Number(n as f64)),
Err(e) => Err(format!("failed to parse hex: {} => {:?}", s, e)),
},
Expand Down Expand Up @@ -139,6 +139,8 @@ pub fn calcit_to_cirru(x: &Calcit) -> Cirru {
}
Cirru::List(ys)
}
Calcit::Proc(s) => Cirru::Leaf(s.to_owned()),
Calcit::Syntax(s, _ns) => Cirru::Leaf(s.to_owned()),
a => Cirru::List(vec![Cirru::Leaf(String::from("TODO")), Cirru::Leaf(a.to_string())]),
}
}
3 changes: 2 additions & 1 deletion src/data/edn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ pub fn calcit_to_edn(x: &Calcit) -> Edn {
Edn::Record(name.clone(), fields.clone(), ys)
}
Calcit::Fn(name, ..) => Edn::Str(format!("&fn {}", name)),
Calcit::Proc(name) => Edn::Str(format!("&proc {}", name)),
Calcit::Proc(name) => Edn::Symbol(name.to_owned()),
Calcit::Syntax(name, _ns) => Edn::Symbol(name.to_owned()),
a => Edn::Str(format!("TODO {}", a)), // TODO more types to handle
}
}
Expand Down
10 changes: 9 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,15 @@ fn run_codegen(
return Err(failure);
}
}
emit_js(&init_ns, &emit_path)?; // TODO entry ns
// TODO entry ns
match emit_js(&init_ns, &emit_path) {
Ok(_) => (),
Err(failure) => {
println!("\nfailed codegen, {}", failure);
call_stack::display_stack(&failure);
return Err(failure);
}
}
let duration = Instant::now().duration_since(started_time);
println!("took {}ms", duration.as_micros() as f64 / 1000.0);
Ok(())
Expand Down

0 comments on commit 56dc00c

Please sign in to comment.