Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
439 changes: 439 additions & 0 deletions interpreter/src/builtins.rs

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions interpreter/src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ mod tests;

use std::fmt::{self, Debug, Display, Formatter};

use parser::{Command, Redirection};
use parser::{BuiltinFunction, Command, Redirection};

pub type RegWidth = u8;
pub type IxWidth = u32;
Expand Down Expand Up @@ -69,7 +69,7 @@ pub enum Instruction {
StoreR { dest: Reg, src: Arg, arg: Arg, ty: ArgTy, tys: ArgTy },
StoreA { dest: Reg, ty_place: ArgTy, start: Reg, end: Reg, var: NonLocal, arg: Reg },
LoadA { dest: Reg, ty_place: ArgTy, start: Reg, end: Reg, var: NonLocal },
IntrinsicCall { dest: Reg, start: Reg, end: Reg, name: NonLocal },
IntrinsicCall { dest: Reg, start: Reg, end: Reg, fun: BuiltinFunction },
OutputCall { start: Reg, end: Reg, cmd: Command, redir: Option<Redirection> },
UserCall { dest: Reg, start: Reg, end: Reg, name: NonLocal },
IndirectCall { dest: Reg, start: Reg, end: Reg, name: Arg, ty: ArgTy },
Expand Down Expand Up @@ -261,8 +261,8 @@ impl Display for Instruction {
write!(f, "{op}")?;
fmt_arg(f, arg, ty, " ")
}
Self::IntrinsicCall { dest, start, end, name } => {
write!(f, "{dest} <- {op} {name}, {start}..{end}")
Self::IntrinsicCall { dest, start, end, fun } => {
write!(f, "{dest} <- {op} {fun}, {start}..{end}")
}
Self::IndirectCall { dest, start, end, name, ty } => {
write!(f, "{dest} <- {op}")?;
Expand Down
4 changes: 4 additions & 0 deletions interpreter/src/ir/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,10 @@ impl<'a> CodeGen<'a> {
let (start, end, ()) = this.gen_call_convention(args, |_| ());
this.emit(Instruction::UserCall { dest, start, end, name });
}
ExprNode::BuiltinCall(fun, args) => {
let (start, end, ()) = this.gen_call_convention(args, |_| ());
this.emit(Instruction::IntrinsicCall { dest, start, end, fun: *fun });
}
ExprNode::IndirectCall(place, args) => {
let (start, end, ()) = this.gen_call_convention(args, |_| ());
let TypedArg(name, ty) =
Expand Down
23 changes: 23 additions & 0 deletions interpreter/src/ir/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,3 +355,26 @@ fn array_multi_index_assignment_lowers_storea() {
assert!(bc.contains("astore"), "expected StoreA:\n{bc}");
});
}

#[test]
fn builtin_call_lowers_to_icall() {
with_lower("BEGIN { print int(3.7) }", |cg| {
let bc = format!("{}", cg.bc);
assert!(
bc.contains(" <- icall int,"),
"expected IntrinsicCall for int():\n{bc}"
);
});
}

#[test]
fn builtin_call_nested_in_expression_lowers_icall() {
with_lower("BEGIN { x = length(\"abc\") + sqrt(4) }", |cg| {
let bc = format!("{}", cg.bc);
assert!(
bc.contains(" <- icall length,"),
"expected length icall:\n{bc}"
);
assert!(bc.contains(" <- icall sqrt,"), "expected sqrt icall:\n{bc}");
});
}
1 change: 1 addition & 0 deletions interpreter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// For the full copyright and license information, please view the LICENSE
// files that was distributed with this source code.

mod builtins;
pub(crate) mod ir;
mod vm;

Expand Down
19 changes: 14 additions & 5 deletions interpreter/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub struct Interpreter<'a> {
program_counter: IxWidth,
code_end: IxWidth,
registers: Registers<'a>,
symbols: SymbolTable<'a>,
pub(crate) symbols: SymbolTable<'a>,
consts: Consts<'a>,
_compat: ExecMode,
frames: StdVec<CallFrame>,
Expand Down Expand Up @@ -562,7 +562,16 @@ impl<'a> Interpreter<'a> {
}
self.write_reg(dest, val);
}
Instruction::IntrinsicCall { dest: _, start: _, end: _, name: _ } => todo!(),
Instruction::IntrinsicCall { dest, start, end, fun } => {
let offset = self.reg_offset();
let args = self.registers.get_range(start..end, offset);
match self.call_builtin(fun, args) {
Ok(val) => self.write_reg(dest, val),
Err(err) => {
return Err(err.into_interpreter_error(self.get_span(metadata)));
}
}
}
Instruction::OutputCall { start, end, cmd, redir } => {
return Ok(Signal::Suspend(self.print_req(start, end, cmd, redir)));
}
Expand Down Expand Up @@ -775,7 +784,7 @@ impl<'a> Registers<'a> {
fn write(&mut self, dest: Reg, offset: IxWidth, src: impl Into<Value<'a>>) {
self.0[dest.0 as usize + offset as usize] = src.into();
}
fn get_range(&mut self, regs: Range<Reg>, offset: IxWidth) -> &[Value<'a>] {
fn get_range(&self, regs: Range<Reg>, offset: IxWidth) -> &[Value<'a>] {
let start = Self::index_of(regs.start, offset);
let end = Self::index_of(regs.end, offset);
&self.0[start..end]
Expand Down Expand Up @@ -842,7 +851,7 @@ impl Arg {
stack_space: &mut MaybeUninit<Value<'a>>,
) {
match ty {
ArgTy::Reg | ArgTy::Cnt => {}
ArgTy::Reg | ArgTy::Cnt | ArgTy::ImmF => {}
ArgTy::Rec => todo!(),
ArgTy::Imm => {
stack_space.write(Value::Int(unsafe { self.imm } as _));
Expand All @@ -869,7 +878,7 @@ impl Arg {
ArgTy::Reg => intrp.read_reg(unsafe { self.reg }),
ArgTy::Rec => todo!(),
ArgTy::Imm => unsafe { stack_space.assume_init_ref() },
ArgTy::Cnt => &intrp.consts.0[unsafe { self.sym.0 } as usize],
ArgTy::Cnt | ArgTy::ImmF => &intrp.consts.0[unsafe { self.sym.0 } as usize],
ArgTy::UsVal => intrp.symbols.raw_user_lookup(unsafe { self.sym }),
_ => todo!(),
}
Expand Down
1 change: 1 addition & 0 deletions parser/src/lex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ impl TokenExt for Token<'_> {
fn is_expr_start(&self) -> bool {
self.is_atom()
|| self.is_prefix_op()
|| self.maps_to_builtin().is_some()
|| matches!(
self,
Token::IndirectCall(_) | Token::Getline | Token::OpenParent
Expand Down
6 changes: 6 additions & 0 deletions parser/src/pratt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,12 @@ impl<'a, 'b> Pratt<'a, 'b> {
leaf_span,
))
}
} else if let Some(builtin) = next.maps_to_builtin() {
self.parser.parse_function_call(
lex,
|args| ExprNode::BuiltinCall(builtin, args),
lex.span(),
)
} else if let Token::IndirectCall(name) = next {
// BUG(gawk): it accepts special variables iff qualified,
// even if it is with the `awk` namespace.
Expand Down
12 changes: 12 additions & 0 deletions parser/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,18 @@ fn test_parser_unary_and_divide() {
});
}

#[test]
fn test_parser_builtin_in_expression_context() {
let source = r#"
BEGIN { print int(3.7); x = length("ab") }
"#;
test_parser!(source => {
begin: [
r#"(body (Print (Int 3.7)) (Assignment awk::x (Length "ab")))"#
],
});
}

#[test]
fn test_parser_proper_assignments() {
let source = r"
Expand Down
71 changes: 71 additions & 0 deletions tests/by-util/test_awk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,3 +358,74 @@ fn user_functions_fib() {
.succeeds()
.stdout_only("0\n1\n1\n2\n3\n5\n8\n13\n21\n34\n");
}

#[test]
fn builtin_numeric_and_string_functions() {
ucmd()
.arg(
r#"BEGIN {
print int(3.7)
print int(-3.7)
print sqrt(4)
print length("abc")
print length()
print toupper("ab")
print tolower("AB")
print index("foobar", "bar")
print substr("abcdef", 2, 3)
print substr("abcdef", 2)
print and(7, 3)
print or(1, 2, 4)
print xor(7, 3)
print compl(0)
print lshift(1, 3)
print rshift(8, 2)
print strtonum("0x10")
print strtonum("010")
print typeof(1)
print typeof("")
}"#,
)
.succeeds()
.stdout_only(
"\
3
-3
2
3
0
AB
ab
4
bcd
bcdef
3
7
4
9007199254740991
8
2
16
8
number
string
",
);
}

#[test]
fn builtin_math_trig_basics() {
ucmd()
.arg("BEGIN { print exp(0); print log(1); print sin(0); print cos(0); print atan2(0, 1) }")
.succeeds()
.stdout_only("1\n0\n0\n1\n0\n");
}

#[ignore = "FIXME: array arguments currently hit scalar_context before dispatch"]
#[test]
fn builtin_isarray_with_array_variable() {
ucmd()
.arg("BEGIN { a[1] = 1; print isarray(a); print length(a) }")
.succeeds()
.stdout_only("1\n1\n");
}
Loading