diff options
Diffstat (limited to 'corp/tvixbolt/src/main.rs')
-rw-r--r-- | corp/tvixbolt/src/main.rs | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/corp/tvixbolt/src/main.rs b/corp/tvixbolt/src/main.rs new file mode 100644 index 000000000000..9ed79aa0da82 --- /dev/null +++ b/corp/tvixbolt/src/main.rs @@ -0,0 +1,187 @@ +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 { + Self { + code: String::new(), + } + } + + fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool { + match msg { + Msg::CodeChange(new_code) => { + self.code = new_code; + true + } + } + } + + fn view(&self, ctx: &Context<Self>) -> Html { + // This gives us a component's "`Scope`" which allows us to send messages, etc to the component. + let link = ctx.link(); + html! { + <div class="container"> + <h1>{"tvixbolt"}</h1> + <form> + <fieldset> + <legend>{"Input"}</legend> + + <div class="form-group"> + <label for="code">{"Nix code:"}</label> + <textarea + oninput={link.callback(|e: InputEvent| { + let ta = e.target_unchecked_into::<HtmlTextAreaElement>().value(); + Msg::CodeChange(ta) + + })} + id="code" cols="30" rows="10"> + </textarea> + </div> + + <div class="form-group"> + <label for="disable-bytecode">{"Disassemble:"}</label> + <input for="disable-bytecode" type="checkbox" checked=true disabled=true /> + </div> + </fieldset> + </form> + <hr /> + <h2>{"Result:"}</h2> + {eval(&self.code).display()} + </div> + } + } +} + +#[derive(Default)] +struct Output { + parse_errors: String, + warnings: String, + compiler_errors: String, + runtime_errors: String, + output: String, + bytecode: Vec<u8>, +} + +fn maybe_show(title: &str, s: &str) -> Html { + if s.is_empty() { + html! {} + } else { + html! { + <> + <h3>{title}</h3> + <pre>{s}</pre> + </> + } + } +} + +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::<Model>(); +} |