Skip to content

Commit

Permalink
Implement interactive mode
Browse files Browse the repository at this point in the history
  • Loading branch information
voltrevo committed Nov 3, 2023
1 parent 746e447 commit edbd27d
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 20 deletions.
66 changes: 46 additions & 20 deletions vstc/src/db_command.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{process::exit, rc::Rc};
use std::{io::Write, process::exit, rc::Rc};

use storage::{storage_head_ptr, SledBackend, Storage, StorageReader};
use valuescript_compiler::{asm, assemble, compile_str};
Expand All @@ -9,6 +9,7 @@ use valuescript_vm::{

use crate::{
handle_diagnostics_cli::handle_diagnostics_cli,
parse_command_line::parse_command_line,
to_bytecode::{format_from_path, to_bytecode},
};

Expand Down Expand Up @@ -39,15 +40,16 @@ pub fn db_command(args: &[String]) {
}
};

let mut storage = Storage::new(SledBackend::open(path).unwrap());

match args.get(3).map(|s| s.as_str()) {
Some("new") => db_new(&path, args.get(4..).unwrap_or_default()),
Some("call") => db_call(&path, args.get(4..).unwrap_or_default()),
Some("-i") => println!("TODO: use database {} interactively", path),
Some("new") => db_new(&mut storage, args.get(4..).unwrap_or_default()),
Some("call") => db_call(&mut storage, args.get(4..).unwrap_or_default()),
Some("-i") => db_interactive(&mut storage),
arg => 'b: {
if let Some(arg) = arg {
// TODO: Document variations here
if arg.starts_with("this.") || arg.starts_with('{') || arg.starts_with('(') {
break 'b db_run_inline(&path, arg);
if arg.starts_with('{') || arg.starts_with('(') {
break 'b db_run_inline(&mut storage, arg);
}
}

Expand All @@ -70,17 +72,20 @@ fn show_help() {
println!(" help, -h, --help Show this message");
println!(" new [CLASS_FILE] [ARGS] Create a new database");
println!(" call [FN_FILE] [ARGS] Call a function on the database");
println!(" 'this.[CODE]' Run inline code within the database context");
println!(" '([EXPRESSION])' Run expression with database as `this`");
println!(" '{{[FN BODY]}}' Run code block with database as `this`");
println!(" -i Enter interactive mode");
println!();
println!("Examples:");
println!(" vstc db path/widget.vsdb new Widget.ts Create a new widget database");
println!(" vstc db path/widget.vsdb call useWidget.ts Call useWidget.ts on the widget");
println!(" vstc db path/widget.vsdb 'this.info()' Call info method");
println!(" vstc db path/widget.vsdb '(this.info())' Call info method");
println!(" vstc db path/widget.vsdb '{{ const t = this; return t.info(); }}'");
println!(" Call info method (enforcing read-only)");
println!(" vstc db path/widget.vsdb -i Enter interactive mode");
}

fn db_new(path: &str, args: &[String]) {
fn db_new(storage: &mut Storage<SledBackend>, args: &[String]) {
let class_file = match args.get(0) {
Some(class_file) => class_file,
None => {
Expand Down Expand Up @@ -121,16 +126,14 @@ fn db_new(path: &str, args: &[String]) {
}
};

let mut storage = Storage::new(SledBackend::open(path).unwrap());

storage
.set_head(storage_head_ptr(b"state"), &instance)
.unwrap();

println!("Created database at {}", path);
println!("Created database");
}

fn db_call(path: &str, args: &[String]) {
fn db_call(storage: &mut Storage<SledBackend>, args: &[String]) {
let fn_file = match args.get(0) {
Some(fn_file) => fn_file,
None => {
Expand All @@ -151,8 +154,6 @@ fn db_call(path: &str, args: &[String]) {
.map(|s| s.clone().to_val())
.collect::<Vec<_>>();

let mut storage = Storage::new(SledBackend::open(path).unwrap());

let mut vm = VirtualMachine::default();

let mut instance = storage
Expand All @@ -175,9 +176,7 @@ fn db_call(path: &str, args: &[String]) {
.unwrap();
}

fn db_run_inline(path: &str, source: &str) {
let mut storage = Storage::new(SledBackend::open(path).unwrap());

fn db_run_inline(storage: &mut Storage<SledBackend>, source: &str) {
let mut vm = VirtualMachine::default();

let mut instance = storage
Expand All @@ -186,7 +185,7 @@ fn db_run_inline(path: &str, source: &str) {
.unwrap();

let full_source = {
if source.starts_with("this.") || source.starts_with('(') {
if source.starts_with('(') {
format!("export default function() {{ return (\n {}\n); }}", source)
} else if source.starts_with('{') {
format!("export default function() {}", source)
Expand Down Expand Up @@ -224,3 +223,30 @@ fn db_run_inline(path: &str, source: &str) {
.set_head(storage_head_ptr(b"state"), &instance)
.unwrap();
}

fn db_interactive(storage: &mut Storage<SledBackend>) {
loop {
let mut input = String::new();

print!("> ");
std::io::stdout().flush().unwrap();
std::io::stdin().read_line(&mut input).unwrap();
input.pop();

let args = parse_command_line(&input);

match args.get(0).map(|s| s.as_str()) {
// TODO: help (it's a bit different - code isn't quoted (TODO: quoted should work too))
Some("exit" | "quit") => break,
Some("new") => db_new(storage, args.get(1..).unwrap_or_default()),
Some("call") => db_call(storage, args.get(1..).unwrap_or_default()),
_ => 'b: {
if input.starts_with('{') || input.starts_with('(') {
break 'b db_run_inline(storage, &input);
}

println!("ERROR: Unrecognized db command {:?}\n", args);
}
}
}
}
1 change: 1 addition & 0 deletions vstc/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod assemble_command;
mod compile_command;
mod db_command;
mod handle_diagnostics_cli;
mod parse_command_line;
mod resolve_entry_path;
mod run_command;
mod test_inputs;
Expand Down
55 changes: 55 additions & 0 deletions vstc/src/parse_command_line.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
pub fn parse_command_line(input: &str) -> Vec<String> {
let mut args = Vec::new();
let mut current_arg = String::new();
let mut in_single_quote = false;
let mut in_double_quote = false;
let mut escape_next_char = false; // To handle escaping of characters

for c in input.chars() {
if escape_next_char {
current_arg.push(c);
escape_next_char = false;
continue;
}

match c {
' ' if !in_single_quote && !in_double_quote => {
if !current_arg.is_empty() {
args.push(current_arg.clone());
current_arg.clear();
}
}
'\'' if !in_double_quote => {
if !in_single_quote || !current_arg.ends_with('\\') {
in_single_quote = !in_single_quote;
} else {
current_arg.pop(); // Remove the escape character
current_arg.push('\''); // Add the literal quote
}
}
'"' if !in_single_quote => {
if !in_double_quote || !current_arg.ends_with('\\') {
in_double_quote = !in_double_quote;
} else {
current_arg.pop(); // Remove the escape character
current_arg.push('"'); // Add the literal quote
}
}
'\\' if in_single_quote || in_double_quote => escape_next_char = true,
_ => {
current_arg.push(c);
}
}
}

if escape_next_char {
// If the input ends with an unprocessed escape character, add it to the argument
current_arg.push('\\');
}

if !current_arg.is_empty() {
args.push(current_arg);
}

args
}

0 comments on commit edbd27d

Please sign in to comment.