mirror of
https://github.com/uutils/awk.git
synced 2026-06-10 16:15:04 -07:00
interpreter: print statements; calling conventions
This commit is contained in:
+12
-2
@@ -15,6 +15,7 @@ pub mod lower;
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
pub use lower::test_interpreter;
|
||||
use parser::{Command, Redirection};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(transparent)]
|
||||
@@ -72,6 +73,7 @@ pub enum Instruction {
|
||||
StoreUserArray(MemArrayArg),
|
||||
StoreBuiltinArray(MemArrayArg),
|
||||
IntrinsicCall(CallArgs),
|
||||
OutputCall(OutputCallArgs),
|
||||
UserCall(IndCallArgs),
|
||||
IndirectCall(CallArgs),
|
||||
Jump(JumpArg),
|
||||
@@ -88,8 +90,9 @@ pub type MemArrayArg = (Reg, Reg, NonLocal);
|
||||
pub type JumpArg = Label;
|
||||
pub type RetArg = Reg;
|
||||
pub type BranchArg = (Reg, Label, Label);
|
||||
pub type CallArgs = (Reg, NonLocal, ArgCount);
|
||||
pub type IndCallArgs = (Reg, Reg, ArgCount);
|
||||
pub type CallArgs = (Reg, Reg, NonLocal);
|
||||
pub type OutputCallArgs = (Reg, Reg, Command, Option<Redirection>);
|
||||
pub type IndCallArgs = (Reg, Reg, Reg);
|
||||
|
||||
impl Instruction {
|
||||
fn set_label(&mut self, label: Label) {
|
||||
@@ -166,6 +169,12 @@ impl Display for Instruction {
|
||||
Self::IntrinsicCall((dest, code, args)) | Self::IndirectCall((dest, code, args)) => {
|
||||
write!(f, "{dest} <- {op} {code}, {args}")
|
||||
}
|
||||
Self::OutputCall((start, end, call, Some(redir))) => {
|
||||
write!(f, "{call}{redir:?} {start}, {end}")
|
||||
}
|
||||
Self::OutputCall((start, end, call, None)) => {
|
||||
write!(f, "{call} {start}, {end}")
|
||||
}
|
||||
Self::UserCall((dest, src, args)) => {
|
||||
write!(f, "{dest} <- {op} {src}, {args}")
|
||||
}
|
||||
@@ -210,6 +219,7 @@ impl Instruction {
|
||||
Self::IntrinsicCall(_) => "icall",
|
||||
Self::UserCall(_) => "ucall",
|
||||
Self::IndirectCall(_) => "vcall",
|
||||
Self::OutputCall(_) => "out",
|
||||
Self::Jump(_) => "jmp",
|
||||
Self::Return(_) => "ret",
|
||||
Self::Branch(_) => "brif",
|
||||
|
||||
@@ -121,6 +121,19 @@ impl<'a> Code<'a> {
|
||||
let reg = self.lower_expr(expr);
|
||||
self.free_reg(reg);
|
||||
}
|
||||
Statement::Simple(SimpleStatement::Command { name, args, redirection }) => {
|
||||
let (call_start, call_end, redir) = self.gen_call_convention(args, |this| {
|
||||
redirection.as_ref().map(|(r, expr)| {
|
||||
let redir_reg = this.alloc_reg();
|
||||
this.lower_expr_into(expr, *redir_reg);
|
||||
this.free_reg(redir_reg);
|
||||
*r
|
||||
})
|
||||
});
|
||||
self.bc.emit(Instruction::OutputCall((
|
||||
call_start, call_end, *name, redir,
|
||||
)));
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
@@ -187,9 +200,11 @@ impl<'a> Code<'a> {
|
||||
Label(0),
|
||||
)));
|
||||
|
||||
state = state.scope(self, |c| {
|
||||
c.lower_expr_into(true_then, dest);
|
||||
});
|
||||
state = state
|
||||
.scope(self, |c| {
|
||||
c.lower_expr_into(true_then, dest);
|
||||
})
|
||||
.0;
|
||||
|
||||
let jump = self.bc.emit(Instruction::Jump(Label(0)));
|
||||
let label = self.following_instr(0);
|
||||
@@ -340,6 +355,25 @@ impl<'a> Code<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
fn gen_call_convention<T>(
|
||||
&mut self,
|
||||
args: &[Expr<'_>],
|
||||
extra: impl FnOnce(&mut Code) -> T,
|
||||
) -> (Reg, Reg, T) {
|
||||
RegsState::new(self)
|
||||
.scope(self, |this| {
|
||||
let call_start = this.reg_pointer;
|
||||
let call_end = call_start + args.len() as u16;
|
||||
|
||||
this.reg_pointer = call_end;
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
this.lower_expr_into(arg, Reg(call_start + i as u16));
|
||||
}
|
||||
(Reg(call_start), Reg(call_end), extra(this))
|
||||
})
|
||||
.1
|
||||
}
|
||||
|
||||
fn free_reg(&mut self, reg: LinearReg) {
|
||||
self.free_regs.push(reg.into_inner());
|
||||
}
|
||||
@@ -391,12 +425,12 @@ impl RegsState {
|
||||
n_free_regs: code.free_regs.len(),
|
||||
}
|
||||
}
|
||||
fn scope<T>(self, code: &mut Code, f: impl FnOnce(&mut Code) -> T) -> Self {
|
||||
f(code);
|
||||
fn scope<T>(self, code: &mut Code, f: impl FnOnce(&mut Code) -> T) -> (Self, T) {
|
||||
let ret = f(code);
|
||||
let old = code.reg_pointer;
|
||||
code.reg_pointer = self.reg_pointer;
|
||||
code.free_regs.truncate(self.n_free_regs);
|
||||
Self { reg_pointer: old, n_free_regs: self.n_free_regs }
|
||||
(Self { reg_pointer: old, ..self }, ret)
|
||||
}
|
||||
fn scope_hwm<T>(self, code: &mut Code, f: impl FnOnce(&mut Code) -> T) {
|
||||
f(code);
|
||||
|
||||
@@ -246,12 +246,10 @@ impl Display for Value<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Value::Float(n) => <_ as Display>::fmt(n, f),
|
||||
Value::String(s) => write!(f, "{:?}", String::from_utf8_lossy(s)),
|
||||
Value::String(s) => write!(f, "{}", String::from_utf8_lossy(s)),
|
||||
Value::Regex(s) => write!(f, "/{}/", String::from_utf8_lossy(s)),
|
||||
&Value::Bool(b) => write!(f, "{}", b as usize),
|
||||
Value::Array(_) => write!(f, "array"),
|
||||
Value::Untyped => write!(f, "untyped"),
|
||||
Value::Unassigned => write!(f, "unassigned"),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+47
-1
@@ -5,7 +5,9 @@
|
||||
|
||||
use std::{
|
||||
fmt::{self, Display},
|
||||
io::{self, Write},
|
||||
mem::replace,
|
||||
ops::Range,
|
||||
vec::Vec as StdVec,
|
||||
};
|
||||
|
||||
@@ -13,7 +15,7 @@ use ahash::RandomState;
|
||||
use bumpalo::{Bump, collections::Vec};
|
||||
use hashbrown::HashMap;
|
||||
use indexmap_allocator_api::{IndexMap, IndexSet};
|
||||
use parser::Identifier;
|
||||
use parser::{Command, Identifier, Redirection};
|
||||
|
||||
use crate::{
|
||||
ir::{
|
||||
@@ -49,6 +51,8 @@ pub struct SymbolTable<'a> {
|
||||
user: IndexMap<Identifier<'a>, Value<'a>, RandomState, &'a Bump>,
|
||||
// separate table for cheap invalidation. It's an arena _visibly shrugs_.
|
||||
records: HashMap<usize, Value<'a>, RandomState, &'a Bump>,
|
||||
ofs: Value<'a>,
|
||||
rfs: Value<'a>,
|
||||
// etc
|
||||
}
|
||||
|
||||
@@ -74,6 +78,8 @@ impl<'a> SymbolTable<'a> {
|
||||
Self {
|
||||
user: IndexMap::new_in(arena),
|
||||
records: HashMap::with_hasher_in(RandomState::new(), arena),
|
||||
ofs: Value::String(b" ".into()),
|
||||
rfs: Value::String(b"\n".into()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +103,12 @@ impl<'a> SymbolTable<'a> {
|
||||
NonLocal(self.user.insert_full(ident, Value::Untyped).0 as _)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn record(&self, value: Value<'a>) -> &Value<'a> {
|
||||
self.records
|
||||
.get(&(value.to_num() as usize))
|
||||
.unwrap_or(&Value::Unassigned)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Consts<'a> {
|
||||
@@ -188,6 +200,9 @@ impl Interpreter<'_> {
|
||||
Instruction::StoreBuiltinScalar((_dest, _src)) => todo!(),
|
||||
Instruction::StoreBuiltinArray((_dest, _src, _place)) => todo!(),
|
||||
Instruction::IntrinsicCall((_dest, _code, _args)) => todo!(),
|
||||
Instruction::OutputCall((start, end, fun, redir)) => {
|
||||
self.intrinsic_print(start, end, fun, redir);
|
||||
}
|
||||
Instruction::UserCall((_dest, _code, _args)) => todo!(),
|
||||
Instruction::IndirectCall((_dest, _code, _args)) => todo!(),
|
||||
Instruction::Jump(Label(label)) => {
|
||||
@@ -207,6 +222,34 @@ impl Interpreter<'_> {
|
||||
self.program_counter += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn intrinsic_print(&mut self, start: Reg, end: Reg, fun: Command, redir: Option<Redirection>) {
|
||||
let Command::Print = fun else { todo!() };
|
||||
let None = redir else { todo!() };
|
||||
let out = &mut io::stdout().lock();
|
||||
let range = self.registers.get_range(start..end);
|
||||
|
||||
if range.is_empty() {
|
||||
let record = self.symbols.record(Value::Float(0.));
|
||||
self.write_fmt(out, format_args!("{record}"));
|
||||
} else {
|
||||
for reg in range {
|
||||
self.write_fmt(out, format_args!("{ofs}{reg}", ofs = self.symbols.ofs));
|
||||
}
|
||||
}
|
||||
self.write_fmt(out, format_args!("{rfs}", rfs = self.symbols.rfs));
|
||||
}
|
||||
|
||||
fn write_fmt(&self, out: &mut impl Write, args: fmt::Arguments<'_>) {
|
||||
if let Err(e) = out.write_fmt(args)
|
||||
&& e.kind() != io::ErrorKind::BrokenPipe
|
||||
{
|
||||
let _ = write!(
|
||||
io::stderr().lock(),
|
||||
"awk: warning: error writing to standard output: {e}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Registers<'a> {
|
||||
@@ -223,6 +266,9 @@ impl<'a> Registers<'a> {
|
||||
fn write(&mut self, dest: Reg, src: Value<'a>) {
|
||||
self.0[dest.0 as usize] = src;
|
||||
}
|
||||
fn get_range(&self, regs: Range<Reg>) -> &[Value<'a>] {
|
||||
&self.0[regs.start.0 as usize..regs.end.0 as _]
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Interpreter<'_> {
|
||||
|
||||
@@ -168,6 +168,8 @@ pub enum Place<'a> {
|
||||
}
|
||||
|
||||
/// GNU docs: https://www.gnu.org/software/gawk/manual/html_node/Redirection.html
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
pub enum Redirection {
|
||||
WriteFile,
|
||||
AppendFile,
|
||||
@@ -250,6 +252,7 @@ pub struct Function<'a> {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
pub enum Command {
|
||||
Print,
|
||||
Printf,
|
||||
|
||||
Reference in New Issue
Block a user