use std::fmt::Write; use serde::{Deserialize, Serialize}; use std::rc::Rc; use tvix_eval::observer::TracingObserver; use tvix_eval::observer::{DisassemblingObserver, NoOpObserver}; use web_sys::HtmlInputElement; use web_sys::HtmlTextAreaElement; use yew::prelude::*; use yew::TargetCast; use yew_router::{prelude::*, AnyRoute}; enum Msg { CodeChange(String), ToggleTrace(bool), } #[derive(Clone, Serialize, Deserialize)] struct Model { code: String, trace: bool, } fn tvixbolt_overview() -> Html { html! { <>
{"This page lets you explore the bytecode generated by the "} {"Tvix"} {" compiler for the Nix language. See the "} {"Tvix announcement"} {" for some background information on Tvix itself."}
{"Tvix is still "}{"extremely work-in-progress"}{" and you "} {"should expect to be able to cause bugs and errors in this tool."}
> } } /// This renders an ad in the Tvixbolt footer. Most people that end up /// on Tvixbolt will probably block this anyways, but might as well. fn ad() -> Html { let ad_code = r#" window.yaContextCb.push(()=>{ Ya.Context.AdvManager.render({ renderTo: 'yandex_rtb_R-A-1943274-1', blockId: 'R-A-1943274-1' }) }) "#; html! {{"Enter some Nix code above to get started. Don't know Nix yet? "} {"Check out "} {"nix-1p"} {"!"}
}; } html! { <>{s}
>
}
}
}
impl Output {
fn display(self) -> Html {
html! {
<>
{maybe_show("Parse errors:", &self.parse_errors)}
{maybe_show("Warnings:", &self.warnings)}
{maybe_show("Output:", &self.output)}
{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("Runtime trace:", &String::from_utf8_lossy(&self.trace))}
>
}
}
}
fn eval(trace: bool, code: &str) -> Output {
let mut out = Output::default();
if code.is_empty() {
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 codemap = Rc::new(codemap);
let mut compilation_observer = DisassemblingObserver::new(codemap.clone(), &mut out.bytecode);
let result = tvix_eval::compile(
root_expr,
Some("/nixbolt".into()),
file.clone(),
tvix_eval::global_builtins(),
&mut compilation_observer,
)
.unwrap();
for warning in result.warnings {
writeln!(
&mut out.warnings,
"{}\n",
warning.fancy_format_str(&codemap).trim(),
)
.unwrap();
}
if !result.errors.is_empty() {
for error in &result.errors {
writeln!(
&mut out.compiler_errors,
"{}\n",
error.fancy_format_str(&codemap).trim(),
)
.unwrap();
}
return out;
}
let result = if trace {
tvix_eval::run_lambda(&mut TracingObserver::new(&mut out.trace), result.lambda)
} else {
tvix_eval::run_lambda(&mut NoOpObserver::default(), result.lambda)
};
match result {
Ok(value) => writeln!(&mut out.output, "{}", value).unwrap(),
Err(err) => writeln!(
&mut out.runtime_errors,
"{}",
err.fancy_format_str(&codemap).trim()
)
.unwrap(),
};
out
}
fn main() {
yew::start_app::