From 6d396041ed8c00f6ac58a1b90473ec7ea5db43d8 Mon Sep 17 00:00:00 2001 From: Jonas Maier <> Date: Sat, 7 Mar 2026 11:19:57 +0100 Subject: parse builtin that emits compact ast --- src/parse.rs | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/run/builtin.rs | 34 ++++++++++++- src/run/mod.rs | 1 + 3 files changed, 178 insertions(+), 1 deletion(-) diff --git a/src/parse.rs b/src/parse.rs index 809f76b..61d268d 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -4,6 +4,10 @@ pub trait Stage { type Str: std::fmt::Debug + Clone; } +pub trait CmdDisplay { + fn cdisplay(&self, w: &mut dyn std::io::Write) -> std::io::Result<()>; +} + #[derive(Debug, Clone)] pub struct PreExpansion; #[derive(Debug, Clone)] @@ -32,6 +36,146 @@ pub enum Ast { Pipes(Pipes), } +pub fn decl(name: ExpString, body: Ast) -> Ast { + Ast::FunDecl(FunDecl { + name: name, + body: FunBody { + body: Box::new(body), + }, + }) +} + +pub fn assign(var: ExpString, val: ExpString) -> Ast { + Ast::VarAssign(VarAssign { var, val }) +} + +pub fn pipes(cmds: [Command; N]) -> Ast { + Ast::Pipes(Pipes { + cmds: cmds.to_vec(), + }) +} + +pub fn estr(x: &[u8]) -> ExpString { + ExpString { + parts: vec![StringPart::Boring(x.to_vec())], + } +} + +pub fn str(parts: [StringPart; N]) -> ExpString { + ExpString { + parts: parts.to_vec(), + } +} + +pub fn plain(x: &[u8]) -> StringPart { + StringPart::Boring(x.to_vec()) +} + +pub fn var(x: &[u8]) -> StringPart { + StringPart::Var(VarName { name: x.to_vec() }) +} + +pub fn cmdp(x: Ast) -> StringPart { + StringPart::Cmd(x) +} + +pub fn cmd(x: [ExpString; N]) -> Command { + Command { + cmd: x[0].clone(), + args: x[1..].to_vec(), + } +} + +impl CmdDisplay for Ast { + fn cdisplay(&self, w: &mut dyn std::io::Write) -> std::io::Result<()> { + match self { + Ast::FunDecl(fun_decl) => { + write!(w, "decl(")?; + fun_decl.name.cdisplay(w)?; + write!(w, ", ")?; + fun_decl.body.body.cdisplay(w)?; + write!(w, ")")?; + } + Ast::VarAssign(var_assign) => { + write!(w, "assign(")?; + var_assign.var.cdisplay(w)?; + write!(w, ", ")?; + var_assign.val.cdisplay(w)?; + write!(w, ")")?; + } + Ast::Pipes(pipes) => { + write!(w, "pipes([")?; + for cmd in pipes.cmds.iter() { + cmd.cdisplay(w)?; + write!(w, ",")?; + } + write!(w, "])")?; + } + } + Ok(()) + } +} + +impl CmdDisplay for ExpString { + fn cdisplay(&self, w: &mut dyn std::io::Write) -> std::io::Result<()> { + if self.parts.len() == 1 && self.parts[0].is_boring() { + write!( + w, + "estr({})", + self.parts[0].clone().unwrap_boring().escape_ascii() + ) + } else { + write!(w, "str([")?; + for part in self.parts.iter() { + part.cdisplay(w)?; + } + write!(w, "])") + } + } +} + +impl CmdDisplay for StringPart { + fn cdisplay(&self, w: &mut dyn std::io::Write) -> std::io::Result<()> { + match self { + StringPart::Boring(items) => { + write!(w, "bstr(")?; + items.as_slice().cdisplay(w)?; + write!(w, ")") + } + StringPart::Var(var_name) => { + write!(w, "var(")?; + var_name.name.as_slice().cdisplay(w)?; + write!(w, ")") + }, + StringPart::Cmd(ast) => { + write!(w, "cmdp(")?; + ast.cdisplay(w)?; + write!(w, ")") + } + } + } +} + +impl CmdDisplay for Command { + fn cdisplay(&self, w: &mut dyn std::io::Write) -> std::io::Result<()> { + write!(w, "cmd([")?; + self.cmd.cdisplay(w)?; + for arg in self.args.iter() { + write!(w, ", ")?; + arg.cdisplay(w)?; + } + write!(w, "])") + } +} + +impl CmdDisplay for &[u8] { + fn cdisplay(&self, w: &mut dyn std::io::Write) -> std::io::Result<()> { + write!(w, "b\"")?; + write!(w, "{}", self.escape_ascii())?; + write!(w, "\"") + } +} + impl Ast { pub fn expand(self, e: &mut E) -> Res, E::Error> { match self { diff --git a/src/run/builtin.rs b/src/run/builtin.rs index c71f10f..0b2b3bc 100644 --- a/src/run/builtin.rs +++ b/src/run/builtin.rs @@ -3,7 +3,8 @@ use std::sync::{Arc, Mutex}; use std::{env::*, fs::OpenOptions, path::PathBuf}; -use super::{Builtin, BuiltinResult as Result}; +use super::{Builtin, BuiltinError as Error, BuiltinResult as Result}; +use crate::parse::CmdDisplay; use crate::*; pub struct cd; @@ -242,3 +243,34 @@ impl Builtin for escape { Ok(()) } } + +pub struct parse; +impl Builtin for parse { + fn name(&self) -> &str { + "parse" + } + + fn io( + &self, + _session: Arc>, + args: &[BString], + _stdin: &mut dyn Read, + stdout: &mut dyn Write, + ) -> Result { + let mut is_ok = true; + for arg in args { + match crate::parse::do_parse(arg) { + Ok(parsed) => { + write!(stdout, "ok ")?; + parsed.cdisplay(stdout)?; + writeln!(stdout)?; + }, + Err(err) => { + is_ok = false; + writeln!(stdout, "err {err:?}")?; + } + } + } + if is_ok { Ok(()) } else { Err(Error::Exit(-1)) } + } +} diff --git a/src/run/mod.rs b/src/run/mod.rs index 2d137c6..c0a108a 100644 --- a/src/run/mod.rs +++ b/src/run/mod.rs @@ -305,6 +305,7 @@ const BUILTINS: &[&'static dyn Builtin] = &[ &builtin::_type, &builtin::history, &builtin::escape, + &builtin::parse, ]; pub struct CommandDispatch { -- cgit v1.2.3