diff --git a/Cargo.lock b/Cargo.lock index 0e03084b..498b54b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,7 +81,7 @@ dependencies = [ [[package]] name = "calcit_runner" -version = "0.3.0-a8" +version = "0.3.0-a9" dependencies = [ "cirru_edn", "cirru_parser", diff --git a/Cargo.toml b/Cargo.toml index 2908d1b5..d310ebff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "calcit_runner" -version = "0.3.0-a8" +version = "0.3.0-a9" authors = ["jiyinyiyong "] edition = "2018" license = "MIT" diff --git a/calcit/snapshots/test-string.cirru b/calcit/snapshots/test-string.cirru index a5caa244..1be932ca 100644 --- a/calcit/snapshots/test-string.cirru +++ b/calcit/snapshots/test-string.cirru @@ -125,6 +125,10 @@ assert= "|do 's" trim $ write-cirru-edn 's + assert= (escape "|\n") "|\"\\n\"" + assert= (escape "|\t") "|\"\\t\"" + assert= (escape "|a") "|\"a\"" + |test-char $ quote fn () log-title "|Test char" diff --git a/package.json b/package.json index d10a7740..a6741970 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@calcit/procs", - "version": "0.3.0-a8", + "version": "0.3.0-a9", "main": "./lib/calcit.procs.js", "devDependencies": { "@types/node": "^14.14.41", diff --git a/src/builtins.rs b/src/builtins.rs index 1aaac9e0..b401cfda 100644 --- a/src/builtins.rs +++ b/src/builtins.rs @@ -42,6 +42,8 @@ pub fn is_proc_name(s: &str) -> bool { | "quit" | "get-env" | "&get-calcit-backend" + | "read-file" + | "write-file" // logics | "&=" | "&<" @@ -87,6 +89,7 @@ pub fn is_proc_name(s: &str) -> bool { | "re-find-index" | "re-find-all" | "blank?" + | "escape" // lists | "[]" | "'" // used as an alias for `[]`, experimental @@ -126,6 +129,8 @@ pub fn is_proc_name(s: &str) -> bool { // refs | "deref" | "reset!" + | "add-watch" + | "remove-watch" // records | "new-record" | "&%{}" @@ -162,6 +167,8 @@ pub fn handle_proc(name: &str, args: &CalcitItems) -> Result { "quit" => effects::quit(args), "get-env" => effects::get_env(args), "&get-calcit-backend" => effects::call_get_calcit_backend(args), + "read-file" => effects::read_file(args), + "write-file" => effects::write_file(args), // logics "&=" => logics::binary_equal(args), "&<" => logics::binary_less(args), @@ -203,6 +210,7 @@ pub fn handle_proc(name: &str, args: &CalcitItems) -> Result { "parse-float" => strings::parse_float(args), "pr-str" => strings::pr_str(args), "blank?" => strings::blank_ques(args), + "escape" => strings::escape(args), // regex "re-matches" => regexes::re_matches(args), "re-find" => regexes::re_find(args), @@ -247,6 +255,8 @@ pub fn handle_proc(name: &str, args: &CalcitItems) -> Result { // refs "deref" => refs::deref(args), "reset!" => refs::reset_bang(args), + "add-watch" => refs::add_watch(args), + "remove-watch" => refs::add_watch(args), // records "new-record" => records::new_record(args), "&%{}" => records::call_record(args), diff --git a/src/builtins/effects.rs b/src/builtins/effects.rs index 844fc42b..7b9d5e00 100644 --- a/src/builtins/effects.rs +++ b/src/builtins/effects.rs @@ -1,4 +1,5 @@ use std::env; +use std::fs; use std::process::exit; use std::sync::Mutex; use std::time::Instant; @@ -118,3 +119,25 @@ pub fn get_env(xs: &CalcitItems) -> Result { None => Err(String::from("get-env expected an argument, got nothing")), } } + +pub fn read_file(xs: &CalcitItems) -> Result { + match xs.get(0) { + Some(Calcit::Str(s)) => match fs::read_to_string(s) { + Ok(content) => Ok(Calcit::Str(content)), + Err(e) => Err(format!("read-file failed: {}", e)), + }, + Some(a) => Err(format!("read-file expected a string, got: {}", a)), + None => Err(String::from("read-file expected a filename, got nothing")), + } +} + +pub fn write_file(xs: &CalcitItems) -> Result { + match (xs.get(0), xs.get(1)) { + (Some(Calcit::Str(path)), Some(Calcit::Str(content))) => match fs::write(path, content) { + Ok(_) => Ok(Calcit::Nil), + Err(e) => Err(format!("write-file failed, {}", e)), + }, + (Some(a), Some(b)) => Err(format!("write-file expected 3 strings, got: {} {}", a, b)), + (a, b) => Err(format!("write-file expected 2 strings, got: {:?} {:?}", a, b)), + } +} diff --git a/src/builtins/meta.rs b/src/builtins/meta.rs index b37f7e41..86443f50 100644 --- a/src/builtins/meta.rs +++ b/src/builtins/meta.rs @@ -41,7 +41,7 @@ pub fn recur(xs: &CalcitItems) -> Result { pub fn format_to_lisp(xs: &CalcitItems) -> Result { match xs.get(0) { - Some(v) => Ok(Calcit::Str(primes::format_to_lisp(v))), + Some(v) => Ok(Calcit::Str(v.lisp_str())), None => Err(String::from("format-to-lisp expected 1 argument")), } } diff --git a/src/builtins/refs.rs b/src/builtins/refs.rs index 23539c86..8144b0c2 100644 --- a/src/builtins/refs.rs +++ b/src/builtins/refs.rs @@ -74,4 +74,11 @@ pub fn reset_bang(xs: &CalcitItems) -> Result { } } -// TODO add-watch remove-watch +// TODO +pub fn add_watch(_xs: &CalcitItems) -> Result { + Ok(Calcit::Nil) +} +// TODO +pub fn remove_watch(_xs: &CalcitItems) -> Result { + Ok(Calcit::Nil) +} diff --git a/src/builtins/strings.rs b/src/builtins/strings.rs index fc4a0f44..08523b05 100644 --- a/src/builtins/strings.rs +++ b/src/builtins/strings.rs @@ -204,3 +204,16 @@ pub fn blank_ques(xs: &CalcitItems) -> Result { None => Err(String::from("blank? expected 1 argument, got nothing")), } } + +pub fn escape(xs: &CalcitItems) -> Result { + match xs.get(0) { + Some(Calcit::Str(s)) => { + let mut chunk = String::from("\""); + chunk.push_str(&s.escape_default().to_string()); + chunk.push('"'); + Ok(Calcit::Str(chunk)) + } + Some(a) => Err(format!("escape expected 1 string, got {}", a)), + None => Err(String::from("escape expected 1 argument, got nothing")), + } +} diff --git a/src/call_stack.rs b/src/call_stack.rs index f878e119..67e1c52c 100644 --- a/src/call_stack.rs +++ b/src/call_stack.rs @@ -56,6 +56,11 @@ pub fn show_stack() { } } +pub fn clear_stack() { + let stack = &mut CALL_STACK.lock().unwrap(); + stack.clear(); +} + pub fn display_stack(failure: &str) { let stack: &Vec = &mut CALL_STACK.lock().unwrap(); println!("\ncall stack:"); diff --git a/src/cirru/calcit-core.cirru b/src/cirru/calcit-core.cirru index c45bb819..74f4ce09 100644 --- a/src/cirru/calcit-core.cirru +++ b/src/cirru/calcit-core.cirru @@ -315,7 +315,7 @@ |&case $ quote defmacro &case (item default pattern & others) - assert "|expects pattern in a pair" + assert "|`case` expects pattern in a pair" if (list? pattern) (&= 2 (count pattern)) false let x $ first pattern @@ -817,7 +817,7 @@ &let nil echo "|Failed assertion:" (quote ~xs) raise - ~ $ &str-concat (&str-concat message "| ") xs + ~ $ &str-concat (&str-concat message "| ") (format-to-lisp xs) |join-str $ quote defn join-str (xs0 sep) diff --git a/src/cli_args.rs b/src/cli_args.rs index 3a05c46b..28ab97d7 100644 --- a/src/cli_args.rs +++ b/src/cli_args.rs @@ -1,8 +1,8 @@ -use crate::primes; +pub const CALCIT_VERSION: &str = "0.3.0-a9"; pub fn parse_cli<'a>() -> clap::ArgMatches<'a> { clap::App::new("Calcit Runner") - .version(primes::CALCI_VERSION) + .version(CALCIT_VERSION) .author("Jon. ") .about("Calcit Runner") .arg( diff --git a/src/codegen/emit_js.rs b/src/codegen/emit_js.rs index 885fbc7f..bb83e7f8 100644 --- a/src/codegen/emit_js.rs +++ b/src/codegen/emit_js.rs @@ -9,7 +9,7 @@ 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::primes; -use crate::primes::{format_to_lisp, Calcit, CalcitItems, SymbolResolved::*}; +use crate::primes::{Calcit, CalcitItems, SymbolResolved::*}; use crate::program; use crate::util::string::has_ns_part; use crate::util::string::{matches_js_var, wrap_js_str}; @@ -308,7 +308,7 @@ fn gen_call_code( }, "defmacro" => Ok(format!("/* Unexpected macro {} */", xs)), - "quote-replace" | "quasiquote" => Ok(format!("(/* Unexpected quasiquote {} */ null)", format_to_lisp(xs))), + "quote-replace" | "quasiquote" => Ok(format!("(/* Unexpected quasiquote {} */ null)", xs.lisp_str())), "raise" => { // not core syntax, but treat as macro for better debugging experience @@ -546,7 +546,7 @@ fn gen_let_code( // 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!("unexpected empty content in let, {:?}", xs)); } let pair = let_def_body[0].clone(); let content = let_def_body.skip(1); diff --git a/src/main.rs b/src/main.rs index dc31dfd3..adb4db67 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,6 +32,8 @@ fn main() -> Result<(), String> { let mut eval_once = cli_matches.is_present("once"); + println!("calcit_runner version: {}", cli_args::CALCIT_VERSION); + // load core libs let bytes = include_bytes!("./cirru/calcit-core.cirru"); let core_content = String::from_utf8_lossy(bytes).to_string(); @@ -85,10 +87,22 @@ fn main() -> Result<(), String> { None => "js-out".to_owned(), }; - if cli_matches.is_present("emit-js") { - run_codegen(&init_fn, &reload_fn, &program_code, &emit_path)?; + let task = if cli_matches.is_present("emit-js") { + run_codegen(&init_fn, &reload_fn, &program_code, &emit_path) + } else { + run_program(&init_fn, &reload_fn, &program_code) + }; + + if eval_once { + task?; } else { - run_program(&init_fn, &reload_fn, &program_code)?; + // error are only printed in watch mode + match task { + Ok(_) => {} + Err(e) => { + println!("\nfailed to run, {}", e); + } + } } if !eval_once { @@ -111,6 +125,7 @@ fn main() -> Result<(), String> { } notify::DebouncedEvent::Write(_) => { println!("\n-------- file change --------\n"); + call_stack::clear_stack(); // Steps: // 1. load changes file, and patch to program_code diff --git a/src/primes.rs b/src/primes.rs index 193965c1..704cfbe7 100644 --- a/src/primes.rs +++ b/src/primes.rs @@ -410,8 +410,6 @@ impl PartialEq for Calcit { pub const CORE_NS: &str = "calcit.core"; pub const GENERATED_NS: &str = "calcit.gen"; -pub const CALCI_VERSION: &str = "0.3.0-a8"; - impl Calcit { pub fn turn_string(&self) -> String { match self { @@ -419,4 +417,8 @@ impl Calcit { _ => format!("{}", self), } } + + pub fn lisp_str(&self) -> String { + format_to_lisp(self) + } } diff --git a/src/runner.rs b/src/runner.rs index aef924cd..1f1f294e 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -14,7 +14,7 @@ pub fn evaluate_expr( file_ns: &str, program_code: &program::ProgramCodeData, ) -> Result { - // println!("eval code: {}", primes::format_to_lisp(expr)); + // println!("eval code: {}", expr.lisp_str()); match expr { Calcit::Nil => Ok(expr.clone()), @@ -33,7 +33,7 @@ pub fn evaluate_expr( Calcit::List(xs) => match xs.get(0) { None => Err(format!("cannot evaluate empty expr: {}", expr)), Some(x) => { - // println!("eval expr: {}", primes::format_to_lisp(expr)); + // println!("eval expr: {}", expr.lisp_str()); // println!("eval expr: {}", x); let mut added_stack = false; @@ -64,7 +64,7 @@ pub fn evaluate_expr( // TODO moving to preprocess let mut current_values = rest_nodes.clone(); - // println!("eval macro: {} {}", x, primes::format_to_lisp(expr)); + // println!("eval macro: {} {}", x, expr.lisp_str())); // println!("macro... {} {}", x, CrListWrap(current_values.clone())); push_call_stack(file_ns, &name, StackKind::Macro, &Some(expr.clone()), &rest_nodes); @@ -79,7 +79,7 @@ pub fn evaluate_expr( current_values = ys; } _ => { - // println!("gen code: {} {}", x, primes::format_to_lisp(&code)); + // println!("gen code: {} {}", x, &code.lisp_str())); break evaluate_expr(&code, scope, file_ns, program_code)?; } } diff --git a/src/runner/preprocess.rs b/src/runner/preprocess.rs index e83d8fbc..dd7cae26 100644 --- a/src/runner/preprocess.rs +++ b/src/runner/preprocess.rs @@ -220,7 +220,7 @@ fn process_list_call( current_values = ys; } _ => { - // println!("gen code: {} {}", code, primes::format_to_lisp(&code)); + // println!("gen code: {} {}", code, &code.lisp_str()); let (final_code, v) = preprocess_expr(&code, scope_defs, file_ns, program_code)?; pop_call_stack(); return Ok((final_code, v));