use std::{fmt::Write, rc::Rc}; use web_sys::HtmlTextAreaElement; use yew::prelude::*; use yew::TargetCast; enum Msg { CodeChange(String), } struct Model { code: String, } impl Component for Model { type Message = Msg; type Properties = (); fn create(_ctx: &Context) -> Self { Self { code: String::new(), } } fn update(&mut self, _ctx: &Context, msg: Self::Message) -> bool { match msg { Msg::CodeChange(new_code) => { self.code = new_code; true } } } fn view(&self, ctx: &Context) -> Html { // This gives us a component's "`Scope`" which allows us to send messages, etc to the component. let link = ctx.link(); html! {

{"tvixbolt"}

{"Input"}

{"Result:"}

{eval(&self.code).display()}
} } } #[derive(Default)] struct Output { parse_errors: String, warnings: String, compiler_errors: String, runtime_errors: String, output: String, bytecode: Vec, } fn maybe_show(title: &str, s: &str) -> Html { if s.is_empty() { html! {} } else { html! { <>

{title}

{s}
} } } impl Output { fn display(self) -> Html { html! { <> {maybe_show("Parse errors:", &self.parse_errors)} {maybe_show("Warnings:", &self.warnings)} {maybe_show("Compiler errors:", &self.compiler_errors)} {maybe_show("Bytecode:", &String::from_utf8_lossy(&self.bytecode))} {maybe_show("Runtime errors:", &self.runtime_errors)} {maybe_show("Output:", &self.output)} } } } fn eval(code: &str) -> Output { let mut out = Output::default(); if code == "" { return out; } let mut codemap = codemap::CodeMap::new(); let file = codemap.add_file("nixbolt".to_string(), code.into()); let parsed = rnix::ast::Root::parse(code); let errors = parsed.errors(); if !errors.is_empty() { for err in errors { writeln!(&mut out.parse_errors, "parse error: {}", err).unwrap(); } return out; } // If we've reached this point, there are no errors. let root_expr = parsed .tree() .expr() .expect("expression should exist if no errors occured"); let mut result = tvix_eval::compiler::compile( root_expr, Some("/nixbolt".into()), &file, tvix_eval::builtins::global_builtins(), Rc::new(codemap), ) .unwrap(); let lambda = Rc::new(result.lambda); tvix_eval::disassembler::disassemble_lambda(&mut out.bytecode, lambda.clone()); out.bytecode.append(&mut result.output); for warning in result.warnings { writeln!( &mut out.warnings, "warning: {:?} at `{}` [line {}]", warning.kind, file.source_slice(warning.span), file.find_line(warning.span.low()) + 1 ) .unwrap(); } if !result.errors.is_empty() { for error in &result.errors { writeln!( &mut out.compiler_errors, "error: {:?} at `{}` [line {}]", error.kind, file.source_slice(error.span), file.find_line(error.span.low()) + 1 ) .unwrap(); } return out; } let result = tvix_eval::vm::run_lambda(lambda); match result { Ok(value) => writeln!(&mut out.output, "{}", value).unwrap(), Err(err) => writeln!(&mut out.runtime_errors, "runtime error: {:?}", err).unwrap(), }; out } fn main() { yew::start_app::(); }