deimos-lang/dm/src/repl.rs

119 lines
4.0 KiB
Rust

use crate::common::to_csr_diagnostic;
use codespan_reporting::files::SimpleFiles;
use codespan_reporting::term::{Config, WriteStyle, emit_to_write_style};
use dmc_lib::SyntheticFunctionSession;
use dmc_lib::constants_table::ConstantsTable;
use dmc_lib::diagnostic::Diagnostics;
use dmc_lib::parser::parse_statement;
use dvm_lib::vm::constant::{Constant, StringConstant};
use dvm_lib::vm::function::Function;
use dvm_lib::vm::operand::Operand;
use dvm_lib::vm::{CallStack, DvmContext, loop_instructions, prepare_for_instruction_loop};
use std::io::{BufRead, Write};
pub fn repl(
read: &mut impl BufRead,
out: &mut impl Write,
err: &mut impl WriteStyle,
register_count: usize,
) {
let diagnostics_config = Config::default();
let mut files: SimpleFiles<&str, String> = SimpleFiles::new();
let mut current_file_id;
let mut buffer = String::new();
let mut session = SyntheticFunctionSession::new("__repl");
let analysis_context = session.ctx_mut();
analysis_context.push_scope("__repl_root_scope");
analysis_context.push_scope("__repl_function_scope");
analysis_context.push_scope("__repl_body_scope");
let mut constants_table = ConstantsTable::new();
let mut dvm_context = DvmContext::new();
let mut repl_fn_stack_locals: Vec<Operand> = Vec::new();
'repl: loop {
write!(out, "> ").unwrap();
out.flush().unwrap();
read.read_line(&mut buffer).unwrap();
let input = buffer.trim();
if input.is_empty() {
buffer.clear();
continue;
}
let compile_result =
compile_statement(input, &mut session, register_count, &mut constants_table);
match compile_result {
Ok(function) => {
dvm_context
.functions_mut()
.insert(function.name_owned(), function);
}
Err(diagnostics) => {
current_file_id = files.add("input", String::from(input));
for diagnostic in diagnostics {
let csr_diagnostic = to_csr_diagnostic(current_file_id, &diagnostic);
emit_to_write_style(err, &diagnostics_config, &files, &csr_diagnostic).unwrap();
}
buffer.clear();
continue 'repl;
}
}
for (name, content) in constants_table.string_constants() {
dvm_context.constants_mut().insert(
name.clone(),
Constant::String(StringConstant::new(name, content)),
);
}
let mut call_stack = CallStack::new();
prepare_for_instruction_loop(&dvm_context, "__repl", &mut call_stack, &[]);
// copy all old locals to current call frame's stack
// this has to be done with indexing because the preparation above creates space for them
for (i, operand) in repl_fn_stack_locals.iter().enumerate() {
let target_index = call_stack.top().fp() + i;
call_stack.top_mut().stack_mut()[target_index] = operand.clone();
}
let result = loop_instructions(
&dvm_context,
&mut vec![Operand::Null; register_count],
&mut call_stack,
);
// copy the top frame's stack locals back to OUR stack locals for next iteration
repl_fn_stack_locals = std::mem::take(call_stack.top_mut().stack_mut());
if let Some(value) = result {
writeln!(out, "{}", value).unwrap();
}
buffer.clear();
}
}
fn compile_statement(
input: &str,
session: &mut SyntheticFunctionSession,
register_count: usize,
constants_table: &mut ConstantsTable,
) -> Result<Function, Diagnostics> {
let maybe_statement = parse_statement(input);
if !maybe_statement.is_ok() {
Err(maybe_statement.into_diagnostics())
} else {
session.compile_statement(
&maybe_statement.into_inner(),
register_count,
constants_table,
)
}
}