use std::io::Read;
use nebu::Goldilocks;
use nox::data::{Reduction, Order, Data};
use nox::reduce::{reduce_with_registry, Outcome};
use nox::call::NullCalls;
use nox::trace::NoTrace;
use nox::jets::registry::JetRegistry;
const ORDER_SIZE: usize = 1 << 16;
fn parse_data(reduction: &mut Reduction<ORDER_SIZE>, input: &str) -> Result<Order, String> {
let tokens = tokenize(input)?;
let mut pos = 0;
let result = parse_expr(reduction, &tokens, &mut pos, 0)?;
Ok(result)
}
const MAX_PARSE_DEPTH: usize = 4096;
#[derive(Debug)]
enum Token {
Open,
Close,
Num(u64),
}
const MAX_NUM_DIGITS: usize = 20;
fn tokenize(input: &str) -> Result<Vec<Token>, String> {
let mut tokens = Vec::new();
let mut chars = input.chars().peekable();
while let Some(&ch) = chars.peek() {
match ch {
'[' => { tokens.push(Token::Open); chars.next(); }
']' => { tokens.push(Token::Close); chars.next(); }
' ' ๏ฟฟ '\t' ๏ฟฟ '\n' ๏ฟฟ '\r' => { chars.next(); }
'0'..='9' => {
let mut num = String::new();
while let Some(&c) = chars.peek() {
if c.is_ascii_digit() ๏ฟฟ๏ฟฟ c == '_' {
if c != '_' {
if num.len() >= MAX_NUM_DIGITS {
return Err(format!(
"numeric literal exceeds {} digits",
MAX_NUM_DIGITS
));
}
num.push(c);
}
chars.next();
} else {
break;
}
}
let v: u64 = num.parse().map_err(๏ฟฟe๏ฟฟ format!("bad number '{}': {}", num, e))?;
tokens.push(Token::Num(v));
}
_ => return Err(format!("unexpected character: {:?}", ch)),
}
}
Ok(tokens)
}
fn parse_expr(
reduction: &mut Reduction<ORDER_SIZE>,
tokens: &[Token],
pos: &mut usize,
depth: usize,
) -> Result<Order, String> {
if depth > MAX_PARSE_DEPTH {
return Err("expression too deeply nested".into());
}
if *pos >= tokens.len() {
return Err("unexpected end of input".to_string());
}
match &tokens[*pos] {
Token::Num(v) => {
*pos += 1;
reduction.atom(Goldilocks::new(*v))
.ok_or_else(๏ฟฟ๏ฟฟ "reduction full".to_string())
}
Token::Open => {
*pos += 1; let mut elems = Vec::new();
while *pos < tokens.len() && !matches!(tokens[*pos], Token::Close) {
elems.push(parse_expr(reduction, tokens, pos, depth + 1)?);
}
if *pos < tokens.len() && matches!(tokens[*pos], Token::Close) {
*pos += 1;
} else {
return Err("expected ']'".to_string());
}
if elems.is_empty() {
return Err("empty brackets".to_string());
}
if elems.len() == 1 {
return Err("single-element brackets are not a valid pair".to_string());
}
let mut result = elems.pop().unwrap();
while let Some(head) = elems.pop() {
result = reduction.pair(head, result)
.ok_or_else(|| "reduction full".to_string())?;
}
Ok(result)
}
Token::Close => {
Err("unexpected ']'".to_string())
}
}
}
fn print_data(reduction: &Reduction<ORDER_SIZE>, r: Order) -> String {
match reduction.get(r).map(|e| e.inner) {
Some(Data::Atom { value, .. }) => format!("{}", value.as_u64()),
Some(Data::Pair { left, right }) => {
format!("[{} {}]", print_data(reduction, left), print_data(reduction, right))
}
None => "<invalid>".to_string(),
}
}
fn main() {
let exit_code = std::thread::Builder::new()
.stack_size(32 * 1024 * 1024)
.spawn(run)
.expect("failed to spawn nox thread")
.join()
.expect("nox thread panicked");
std::process::exit(exit_code);
}
fn run() -> i32 {
let args: Vec<String> = std::env::args().collect();
let mut formula_text = String::new();
let mut object_text = String::from("0");
let mut budget: u64 = 1_000_000;
let mut i = 1;
while i < args.len() {
match args[i].as_str() {
"-e" => {
i += 1;
if i < args.len() {
formula_text = args[i].clone();
} else {
eprintln!("error: -e requires an argument");
return 1;
}
}
"--object" | "-o" => {
i += 1;
if i < args.len() {
object_text = args[i].clone();
} else {
eprintln!("error: --object requires an argument");
return 1;
}
}
"--budget" | "-b" => {
i += 1;
if i < args.len() {
match args[i].parse::<u64>() {
Ok(b) => budget = b,
Err(_) => { eprintln!("error: invalid budget"); return 1; }
}
} else {
eprintln!("error: --budget requires an argument");
return 1;
}
}
"--help" | "-h" => {
print_usage();
return 0;
}
other => {
match std::fs::read_to_string(other) {
Ok(s) => formula_text = s,
Err(e) => { eprintln!("error reading '{}': {}", other, e); return 1; }
}
}
}
i += 1;
}
if formula_text.is_empty() {
use std::io::IsTerminal;
if std::io::stdin().is_terminal() {
print_usage();
return 1;
}
if let Err(e) = std::io::stdin().read_to_string(&mut formula_text) {
eprintln!("error reading stdin: {}", e);
return 1;
}
}
let formula_text = formula_text.trim();
if formula_text.is_empty() {
eprintln!("error: no formula provided");
return 1;
}
let mut reduction = Reduction::<ORDER_SIZE>::new();
let hints = NullCalls;
let registry = JetRegistry::<ORDER_SIZE>::genesis();
let object = match parse_data(&mut reduction, &object_text) {
Ok(n) => n,
Err(e) => { eprintln!("error parsing object: {}", e); return 1; }
};
let formula = match parse_data(&mut reduction, &formula_text) {
Ok(n) => n,
Err(e) => { eprintln!("error parsing formula: {}", e); return 1; }
};
match reduce_with_registry(&mut reduction, object, formula, budget, &hints, &mut NoTrace, ®istry) {
Outcome::Ok(result, remaining) => {
println!("{}", print_data(&reduction, result));
eprintln!("cost: {} (budget remaining: {})", budget - remaining, remaining);
0
}
Outcome::Halt(remaining) => {
eprintln!("halted: budget exhausted (remaining: {})", remaining);
1
}
Outcome::Error(kind) => {
eprintln!("error: {:?}", kind);
1
}
}
}
fn print_usage() {
eprintln!(
"\
\x1b[31m
โโโโ โโโ โโโโโโโ โโโ โโโ
\x1b[33m โโโโโ โโโโโโโโโโโโโโโโโโโโ
\x1b[32m โโโโโโ โโโโโโ โโโ โโโโโโ
\x1b[36m โโโโโโโโโโโโโ โโโ โโโโโโ
\x1b[34m โโโ โโโโโโโโโโโโโโโโโโโ โโโ
\x1b[35m โโโ โโโโโ โโโโโโโ โโโ โโโ
\x1b[0m\x1b[37m the VM for superintelligence\x1b[0m
\x1b[90m
18 patterns ยท 36 genesis jets ยท 5 algebras
Goldilocks field ยท p = 2^64 - 2^32 + 1
computation IS linking ยท proof-native
\x1b[0m
nox <file.nox> evaluate formula from file
nox -e '[5 [[1 3] [1 5]]]' evaluate inline formula
echo '[1 42]' | nox evaluate from stdin
-o, --object <data> object (default: 0)
-b, --budget <n> budget (default: 1000000)
-e <formula> inline formula
"
);
}