Archive Rust bootstrap — El compiler is now self-hosting
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "elvm"
|
||||
description = "El Virtual Machine — executes compiled El bytecode (.elc) natively"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[[bin]]
|
||||
name = "elvm"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
el-compiler = { workspace = true }
|
||||
el-vm = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
|
||||
# Native window / WebView (macOS: WKWebView via wry)
|
||||
wry = { version = "0.47", default-features = false }
|
||||
winit = { version = "0.29", default-features = false, features = ["rwh_05", "rwh_06"] }
|
||||
dpi = "0.1"
|
||||
@@ -0,0 +1,146 @@
|
||||
//! elvm — El Virtual Machine
|
||||
//!
|
||||
//! The standalone El VM binary. Loads and executes compiled El bytecode (.elc)
|
||||
//! files produced by `el compile` or `el build-file`.
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! elvm <file.elc> [args...]
|
||||
//! elvm --version
|
||||
//! elvm --help
|
||||
//!
|
||||
//! If the environment variable `NEURON_WINDOW_URL` is set, elvm opens a native
|
||||
//! macOS window (WKWebView via wry) at that URL instead of executing bytecode.
|
||||
//! This allows UI apps to be launched as proper desktop windows:
|
||||
//!
|
||||
//! NEURON_WINDOW_URL="http://localhost:7749" elvm dist/neuron-ui.elc
|
||||
|
||||
use clap::Parser;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(
|
||||
name = "elvm",
|
||||
about = "El Virtual Machine — execute compiled El bytecode (.elc)",
|
||||
long_about = "The El VM is the native El execution substrate.\n\
|
||||
Run .elc files produced by `el compile` or `el build-file`.\n\n\
|
||||
Set NEURON_WINDOW_URL=<url> to open a native WebView window instead.",
|
||||
version
|
||||
)]
|
||||
struct Cli {
|
||||
/// Compiled El bytecode file to execute (*.elc).
|
||||
file: PathBuf,
|
||||
|
||||
/// Arguments forwarded to the program (accessible via `args()`).
|
||||
#[arg(trailing_var_arg = true)]
|
||||
args: Vec<String>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let cli = Cli::parse();
|
||||
|
||||
// If NEURON_WINDOW_URL is set, open a native WebView window at that URL.
|
||||
if let Ok(url) = std::env::var("NEURON_WINDOW_URL") {
|
||||
if let Err(e) = open_window(&url) {
|
||||
eprintln!("elvm: window error: {e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(e) = run(cli) {
|
||||
eprintln!("elvm: error: {e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn run(cli: Cli) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let bytes = std::fs::read(&cli.file)
|
||||
.map_err(|e| format!("cannot read {}: {e}", cli.file.display()))?;
|
||||
|
||||
let instructions = el_compiler::Bytecode::deserialize_all(&bytes)
|
||||
.map_err(|e| format!("cannot load bytecode from {}: {e}", cli.file.display()))?;
|
||||
|
||||
// Detect format and print diagnostic.
|
||||
let is_elvm_container = bytes.starts_with(el_compiler::ELVM_MAGIC);
|
||||
if is_elvm_container {
|
||||
// Normal path — ELVM container.
|
||||
} else {
|
||||
eprintln!("elvm: warning: {} does not have an ELVM header — treating as legacy JSON bytecode", cli.file.display());
|
||||
}
|
||||
|
||||
let mut vm = el_vm::ElVm::new();
|
||||
vm.run(&instructions, &cli.args);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Opens a native WebView window at `url`.
|
||||
///
|
||||
/// On macOS: uses wry (WKWebView) + winit for a proper native desktop window.
|
||||
/// On other platforms: prints the URL (fallback).
|
||||
fn open_window(url: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
#[cfg(target_os = "macos")]
|
||||
return open_native_window(url);
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
{
|
||||
eprintln!("elvm: native window not supported on this platform");
|
||||
println!("elvm: open {url} in your browser");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn open_native_window(url: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
use dpi::LogicalSize;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
use wry::{Rect, WebViewBuilder};
|
||||
|
||||
let event_loop = EventLoop::new().map_err(|e| format!("event loop: {e}"))?;
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("Neuron")
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(1600u32, 1000u32))
|
||||
.with_min_inner_size(winit::dpi::LogicalSize::new(900u32, 600u32))
|
||||
.with_resizable(true)
|
||||
.build(&event_loop)
|
||||
.map_err(|e| format!("window: {e}"))?;
|
||||
|
||||
let url_owned = url.to_string();
|
||||
let webview = WebViewBuilder::new()
|
||||
.with_url(&url_owned)
|
||||
.build_as_child(&window)
|
||||
.map_err(|e| format!("webview: {e}"))?;
|
||||
|
||||
event_loop
|
||||
.run(move |event, evl| {
|
||||
evl.set_control_flow(ControlFlow::Wait);
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::Resized(size),
|
||||
..
|
||||
} => {
|
||||
let scale = window.scale_factor();
|
||||
let logical = size.to_logical::<u32>(scale);
|
||||
let _ = webview.set_bounds(Rect {
|
||||
position: dpi::LogicalPosition::new(0, 0).into(),
|
||||
size: LogicalSize::new(logical.width, logical.height).into(),
|
||||
});
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => evl.exit(),
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
.map_err(|e| format!("event loop run: {e}"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user