chore(tvixbolt): move from //corp to //web
Assigning copyright to the TVL community (whatever that is), and adding AGPL-3.0-or-later license. I also cleaned up some of the stuff on the landing page. Change-Id: I4dbca19406e00e5105fed50e8fb64e0fcca23e3a Reviewed-on: https://cl.tvl.fyi/c/depot/+/11013 Autosubmit: tazjin <tazjin@tvl.su> Reviewed-by: flokli <flokli@flokli.de> Tested-by: BuildkiteCI
This commit is contained in:
parent
91d5745c3d
commit
782cfa9e33
9 changed files with 676 additions and 41 deletions
2
corp/tvixbolt/.gitignore
vendored
2
corp/tvixbolt/.gitignore
vendored
|
|
@ -1,2 +0,0 @@
|
|||
/target
|
||||
dist
|
||||
1103
corp/tvixbolt/Cargo.lock
generated
1103
corp/tvixbolt/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,26 +0,0 @@
|
|||
[package]
|
||||
name = "tvixbolt"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
yew = "0.19.3"
|
||||
yew-router = "0.16"
|
||||
codemap = "0.1.3"
|
||||
serde_urlencoded = "*" # pinned by yew
|
||||
rnix = "0.11.0"
|
||||
|
||||
# needs to be in sync with nixpkgs
|
||||
wasm-bindgen = "= 0.2.91"
|
||||
|
||||
[dependencies.tvix-eval]
|
||||
path = "../../tvix/eval"
|
||||
default-features = false
|
||||
|
||||
[dependencies.serde]
|
||||
version = "*" # pinned by yew
|
||||
features = [ "derive" ]
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "*" # pinned by yew
|
||||
features = [ "HtmlDetailsElement" ]
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
{ depot, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
wasmRust = pkgs.rust-bin.stable.latest.default.override {
|
||||
targets = [ "wasm32-unknown-unknown" ];
|
||||
};
|
||||
|
||||
cargoToml = with builtins; fromTOML (readFile ./Cargo.toml);
|
||||
|
||||
wasmBindgenMatch =
|
||||
cargoToml.dependencies.wasm-bindgen == "= ${pkgs.wasm-bindgen-cli.version}";
|
||||
|
||||
assertWasmBindgen = assert (lib.assertMsg wasmBindgenMatch ''
|
||||
Due to instability in the Rust WASM ecosystem, the trunk build
|
||||
tool enforces that the Cargo-dependency version of `wasm-bindgen`
|
||||
MUST match the version of the CLI supplied in the environment.
|
||||
|
||||
This can get out of sync when nixpkgs is updated. To resolve it,
|
||||
wasm-bindgen must be bumped in the Cargo.toml file and cargo needs
|
||||
to be run to resolve the dependencies.
|
||||
|
||||
Versions of `wasm-bindgen` in Cargo.toml:
|
||||
|
||||
Expected: '= ${pkgs.wasm-bindgen-cli.version}'
|
||||
Actual: '${cargoToml.dependencies.wasm-bindgen}'
|
||||
''); pkgs.wasm-bindgen-cli;
|
||||
|
||||
deps = [
|
||||
pkgs.binaryen
|
||||
pkgs.sass
|
||||
pkgs.trunk
|
||||
|
||||
wasmRust
|
||||
assertWasmBindgen
|
||||
];
|
||||
|
||||
# Cargo.toml needs to be patched with the /nix/store source path of
|
||||
# tvix-eval.
|
||||
cargoTomlPatch = pkgs.writeText "tvix-eval-src.patch" ''
|
||||
diff --git a/Cargo.toml b/Cargo.toml
|
||||
index 75006bec18..6ca244bbb2 100644
|
||||
--- a/Cargo.toml
|
||||
+++ b/Cargo.toml
|
||||
@@ -16,7 +16,7 @@ rnix = "0.11.0"
|
||||
wasm-bindgen = "= 0.2.83"
|
||||
|
||||
[dependencies.tvix-eval]
|
||||
-path = "../../tvix/eval"
|
||||
+path = "${depot.tvix.crates.workspaceMembers.tvix-eval.build.src}"
|
||||
default-features = false
|
||||
|
||||
[dependencies.serde]
|
||||
'';
|
||||
in
|
||||
pkgs.rustPlatform.buildRustPackage rec {
|
||||
pname = "tvixbolt";
|
||||
version = "canon";
|
||||
src = lib.cleanSource ./.;
|
||||
|
||||
cargoLock.lockFile = ./Cargo.lock;
|
||||
|
||||
patches = [
|
||||
cargoTomlPatch
|
||||
];
|
||||
|
||||
buildPhase = ''
|
||||
export PATH=${lib.makeBinPath deps}:$PATH
|
||||
mkdir home
|
||||
export HOME=$PWD/home
|
||||
trunk build --release -d $out
|
||||
'';
|
||||
|
||||
dontInstall = true;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
.footer {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.lod {
|
||||
text-align: center;
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet"
|
||||
href="https://static.tvl.su/latest/terminal.min.css" />
|
||||
<link data-trunk rel="inline" href="index.css">
|
||||
<title>Tvixbolt</title>
|
||||
|
||||
<!-- Yandex.RTB -->
|
||||
<script>window.yaContextCb=window.yaContextCb||[]</script>
|
||||
<script src="https://yandex.ru/ads/system/context.js" async></script>
|
||||
</head>
|
||||
</html>
|
||||
|
|
@ -1,337 +0,0 @@
|
|||
use std::fmt::Write;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tvix_eval::observer::{DisassemblingObserver, TracingObserver};
|
||||
use web_sys::HtmlDetailsElement;
|
||||
use web_sys::HtmlTextAreaElement;
|
||||
use yew::prelude::*;
|
||||
use yew::TargetCast;
|
||||
use yew_router::{prelude::*, AnyRoute};
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Msg {
|
||||
CodeChange(String),
|
||||
ToggleTrace(bool),
|
||||
ToggleDisplayAst(bool),
|
||||
|
||||
// Required because browsers are stupid and it's easy to get into
|
||||
// infinite loops with `ontoggle` events.
|
||||
NoOp,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
struct Model {
|
||||
code: String,
|
||||
|
||||
// #[serde(skip_serializing)]
|
||||
trace: bool,
|
||||
|
||||
// #[serde(skip_serializing)]
|
||||
display_ast: bool,
|
||||
}
|
||||
|
||||
fn tvixbolt_overview() -> Html {
|
||||
html! {
|
||||
<>
|
||||
<p>
|
||||
{"This page lets you explore the bytecode generated by the "}
|
||||
<a href="https://cs.tvl.fyi/depot/-/tree/tvix">{"Tvix"}</a>
|
||||
{" compiler for the Nix language. See the "}
|
||||
<a href="https://tvl.fyi/blog/rewriting-nix">{"Tvix announcement"}</a>
|
||||
{" for some background information on Tvix itself."}
|
||||
</p>
|
||||
<p>
|
||||
{"Tvix is still "}<i>{"extremely work-in-progress"}</i>{" and you "}
|
||||
{"should expect to be able to cause bugs and errors in this tool."}
|
||||
</p>
|
||||
<p>
|
||||
{"Tvixbolt is a project from "}
|
||||
<a href="https://tvl.su">
|
||||
{"TVL LLC"}
|
||||
</a>
|
||||
{". If you're looking for the TVL Community, click "}
|
||||
<a href="https://tvl.fyi">
|
||||
{"here"}
|
||||
</a>
|
||||
{"."}
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
/// 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! {
|
||||
<div id="ad">
|
||||
<div id="yandex_rtb_R-A-1943274-1"></div>
|
||||
<script>{ad_code}</script>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
fn footer_link(location: &'static str, name: &str) -> Html {
|
||||
html! {
|
||||
<>
|
||||
<a class="uncoloured-link" href={location}>{name}</a>{" | "}
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
fn footer() -> Html {
|
||||
html! {
|
||||
<>
|
||||
<hr/>
|
||||
<footer>
|
||||
<p class="footer">
|
||||
{footer_link("https://tvl.su", "home")}
|
||||
{footer_link("https://cs.tvl.fyi", "code")}
|
||||
{footer_link("https://tvl.fyi/builds", "ci")}
|
||||
{footer_link("https://b.tvl.fyi", "bugs")}
|
||||
{"© ООО ТВЛ"}
|
||||
</p>
|
||||
<p class="lod">{"ಠ_ಠ"}</p>
|
||||
{ad()}
|
||||
</footer>
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Model {
|
||||
type Message = Msg;
|
||||
type Properties = ();
|
||||
|
||||
fn create(_: &Context<Self>) -> Self {
|
||||
BrowserHistory::new()
|
||||
.location()
|
||||
.query::<Self>()
|
||||
.unwrap_or_else(|_| Self {
|
||||
code: String::new(),
|
||||
trace: false,
|
||||
display_ast: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn update(&mut self, _: &Context<Self>, msg: Self::Message) -> bool {
|
||||
match msg {
|
||||
Msg::ToggleTrace(trace) => {
|
||||
self.trace = trace;
|
||||
}
|
||||
|
||||
Msg::ToggleDisplayAst(display_ast) => {
|
||||
self.display_ast = display_ast;
|
||||
}
|
||||
|
||||
Msg::CodeChange(new_code) => {
|
||||
self.code = new_code;
|
||||
}
|
||||
|
||||
Msg::NoOp => {}
|
||||
}
|
||||
|
||||
let _ = BrowserHistory::new().replace_with_query(AnyRoute::new("/"), self.clone());
|
||||
|
||||
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 0.1-alpha"}</h1>
|
||||
{tvixbolt_overview()}
|
||||
<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" value={self.code.clone()}>
|
||||
</textarea>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
<hr />
|
||||
{self.run(ctx)}
|
||||
{footer()}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Model {
|
||||
fn run(&self, ctx: &Context<Self>) -> Html {
|
||||
if self.code.is_empty() {
|
||||
return html! {
|
||||
<p>
|
||||
{"Enter some Nix code above to get started. Don't know Nix yet? "}
|
||||
{"Check out "}
|
||||
<a href="https://code.tvl.fyi/about/nix/nix-1p/README.md">{"nix-1p"}</a>
|
||||
{"!"}
|
||||
</p>
|
||||
};
|
||||
}
|
||||
|
||||
html! {
|
||||
<>
|
||||
<h2>{"Result:"}</h2>
|
||||
{eval(self).display(ctx, self)}
|
||||
</>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Output {
|
||||
errors: String,
|
||||
warnings: String,
|
||||
output: String,
|
||||
bytecode: Vec<u8>,
|
||||
trace: Vec<u8>,
|
||||
ast: String,
|
||||
}
|
||||
|
||||
fn maybe_show(title: &str, s: &str) -> Html {
|
||||
if s.is_empty() {
|
||||
html! {}
|
||||
} else {
|
||||
html! {
|
||||
<>
|
||||
<h3>{title}</h3>
|
||||
<pre>{s}</pre>
|
||||
</>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_details(
|
||||
ctx: &Context<Model>,
|
||||
title: &str,
|
||||
s: &str,
|
||||
display: bool,
|
||||
toggle: fn(bool) -> Msg,
|
||||
) -> Html {
|
||||
let link = ctx.link();
|
||||
if display {
|
||||
let msg = toggle(false);
|
||||
html! {
|
||||
<details open=true
|
||||
ontoggle={link.callback(move |e: Event| {
|
||||
let details = e.target_unchecked_into::<HtmlDetailsElement>();
|
||||
if !details.open() {
|
||||
msg.clone()
|
||||
} else {
|
||||
Msg::NoOp
|
||||
}
|
||||
})}>
|
||||
|
||||
<summary><h3 style="display: inline;">{title}</h3></summary>
|
||||
<pre>{s}</pre>
|
||||
</details>
|
||||
}
|
||||
} else {
|
||||
let msg = toggle(true);
|
||||
html! {
|
||||
<details ontoggle={link.callback(move |e: Event| {
|
||||
let details = e.target_unchecked_into::<HtmlDetailsElement>();
|
||||
if details.open() {
|
||||
msg.clone()
|
||||
} else {
|
||||
Msg::NoOp
|
||||
}
|
||||
})}>
|
||||
<summary><h3 style="display: inline;">{title}</h3></summary>
|
||||
</details>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Output {
|
||||
fn display(self, ctx: &Context<Model>, model: &Model) -> Html {
|
||||
html! {
|
||||
<>
|
||||
{maybe_show("Errors:", &self.errors)}
|
||||
{maybe_show("Warnings:", &self.warnings)}
|
||||
{maybe_show("Output:", &self.output)}
|
||||
{maybe_show("Bytecode:", &String::from_utf8_lossy(&self.bytecode))}
|
||||
{maybe_details(ctx, "Runtime trace:", &String::from_utf8_lossy(&self.trace), model.trace, Msg::ToggleTrace)}
|
||||
{maybe_details(ctx, "Parsed AST:", &self.ast, model.display_ast, Msg::ToggleDisplayAst)}
|
||||
</>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn eval(model: &Model) -> Output {
|
||||
let mut out = Output::default();
|
||||
|
||||
if model.code.is_empty() {
|
||||
return out;
|
||||
}
|
||||
|
||||
let mut eval = tvix_eval::Evaluation::new_pure();
|
||||
let source = eval.source_map();
|
||||
|
||||
let result = {
|
||||
let mut compiler_observer = DisassemblingObserver::new(source.clone(), &mut out.bytecode);
|
||||
eval.compiler_observer = Some(&mut compiler_observer);
|
||||
|
||||
let mut runtime_observer = TracingObserver::new(&mut out.trace);
|
||||
if model.trace {
|
||||
eval.runtime_observer = Some(&mut runtime_observer);
|
||||
}
|
||||
|
||||
eval.evaluate(&model.code, Some("/nixbolt".into()))
|
||||
};
|
||||
|
||||
if model.display_ast {
|
||||
if let Some(ref expr) = result.expr {
|
||||
out.ast = tvix_eval::pretty_print_expr(expr);
|
||||
}
|
||||
}
|
||||
|
||||
out.output = match result.value {
|
||||
Some(val) => val.to_string(),
|
||||
None => "".to_string(),
|
||||
};
|
||||
|
||||
for warning in result.warnings {
|
||||
writeln!(
|
||||
&mut out.warnings,
|
||||
"{}\n",
|
||||
warning.fancy_format_str(&source).trim(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if !result.errors.is_empty() {
|
||||
for error in &result.errors {
|
||||
writeln!(&mut out.errors, "{}\n", error.fancy_format_str().trim(),).unwrap();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
fn main() {
|
||||
yew::start_app::<Model>();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue