use crate::builtins::Variable;
use crate::bytecode::{Get, Instr, Label, Reg};
use crate::common::{NumTy, Result, Stage};
use crate::compile::{self, Ty};
use crate::pushdown::FieldSet;
use crate::runtime::{self, Float, Int, Line, LineReader, Str, UniqueStr};
use crossbeam::scope;
use crossbeam_channel::bounded;
use hashbrown::HashMap;
use rand::{self, rngs::StdRng, Rng, SeedableRng};
use regex::bytes::Regex;
use std::cmp;
use std::mem;
type ClassicReader = runtime::splitter::regex::RegexSplitter<Box<dyn std::io::Read>>;
#[derive(Default)]
pub(crate) struct Storage<T> {
pub(crate) regs: Vec<T>,
pub(crate) stack: Vec<T>,
}
pub(crate) struct Core<'a> {
pub vars: runtime::Variables<'a>,
pub regexes: runtime::RegexCache,
pub write_files: runtime::FileWrite,
pub rng: StdRng,
pub current_seed: u64,
pub slots: Slots,
}
impl<'a> Drop for Core<'a> {
fn drop(&mut self) {
if let Err(e) = self.write_files.shutdown() {
eprintln_ignore!("{}", e);
}
}
}
#[derive(Default, Clone)]
pub(crate) struct Slots {
pub int: Vec<Int>,
pub float: Vec<Float>,
pub strs: Vec<UniqueStr<'static>>,
pub intint: Vec<HashMap<Int, Int>>,
pub intfloat: Vec<HashMap<Int, Float>>,
pub intstr: Vec<HashMap<Int, UniqueStr<'static>>>,
pub strint: Vec<HashMap<UniqueStr<'static>, Int>>,
pub strfloat: Vec<HashMap<UniqueStr<'static>, Float>>,
pub strstr: Vec<HashMap<UniqueStr<'static>, UniqueStr<'static>>>,
}
trait Agg {
fn agg(self, other: Self) -> Self;
}
impl Agg for Int {
fn agg(self, other: Int) -> Int {
self + other
}
}
impl Agg for Float {
fn agg(self, other: Float) -> Float {
self + other
}
}
impl<'a> Agg for UniqueStr<'a> {
fn agg(self, other: UniqueStr<'a>) -> UniqueStr<'a> {
if other.is_empty() {
self
} else {
other
}
}
}
impl<K: std::hash::Hash + Eq, V: Agg + Default> Agg for HashMap<K, V> {
fn agg(mut self, other: HashMap<K, V>) -> HashMap<K, V> {
for (k, v) in other {
let entry = self.entry(k).or_default();
let v2 = mem::take(entry);
*entry = v2.agg(v);
}
self
}
}
pub(crate) struct StageResult {
slots: Slots,
nr: Int,
rc: i32,
}
impl Slots {
fn combine(&mut self, mut other: Slots) {
macro_rules! for_each_slot_pair {
($s1:ident, $s2:ident, $body:expr) => {
for_each_slot_pair!(
$s1, $s2, $body, int, float, strs, intint, intfloat, intstr, strint, strfloat,
strstr
);
};
($s1:ident, $s2:ident, $body:expr, $($fld:tt),*) => {$({
let $s1 = &mut self.$fld;
let $s2 = &mut other.$fld;
$body
});*};
}
for_each_slot_pair!(a, b, {
a.resize_with(std::cmp::max(a.len(), b.len()), Default::default);
for (a_elt, b_elt_v) in a.iter_mut().zip(b.drain(..)) {
let a_elt_v = mem::take(a_elt);
*a_elt = a_elt_v.agg(b_elt_v);
}
});
}
}
pub fn set_slot<T: Default>(vec: &mut Vec<T>, slot: usize, v: T) {
if slot < vec.len() {
vec[slot] = v;
return;
}
vec.resize_with(slot, Default::default);
vec.push(v)
}
pub fn combine_slot<T: Default>(vec: &mut Vec<T>, slot: usize, f: impl FnOnce(T) -> T) {
if slot < vec.len() {
let res = f(std::mem::take(&mut vec[slot]));
vec[slot] = res;
return;
}
vec.resize_with(slot, Default::default);
let res = f(Default::default());
vec.push(res)
}
impl<'a> Core<'a> {
pub fn shuttle(&self, pid: Int) -> impl FnOnce() -> Core<'a> + Send {
use crate::builtins::Variables;
let seed: u64 = rand::thread_rng().gen();
let fw = self.write_files.clone();
let fs: UniqueStr<'a> = self.vars.fs.clone().into();
let ofs: UniqueStr<'a> = self.vars.ofs.clone().into();
let rs: UniqueStr<'a> = self.vars.rs.clone().into();
let ors: UniqueStr<'a> = self.vars.ors.clone().into();
let filename: UniqueStr<'a> = self.vars.filename.clone().into();
let argv = self.vars.argv.shuttle();
let fi = self.vars.fi.shuttle();
let slots = self.slots.clone();
move || {
let vars = Variables {
fs: fs.into_str(),
ofs: ofs.into_str(),
ors: ors.into_str(),
rs: rs.into_str(),
filename: filename.into_str(),
pid,
nf: 0,
nr: 0,
fnr: 0,
rstart: 0,
rlength: 0,
argc: 0,
argv: argv.into(),
fi: fi.into(),
};
Core {
vars,
regexes: Default::default(),
write_files: fw,
rng: rand::rngs::StdRng::seed_from_u64(seed),
current_seed: seed,
slots,
}
}
}
pub fn new(ff: impl runtime::writers::FileFactory) -> Core<'a> {
let seed: u64 = rand::thread_rng().gen();
Core {
vars: Default::default(),
regexes: Default::default(),
write_files: runtime::FileWrite::new(ff),
rng: rand::rngs::StdRng::seed_from_u64(seed),
current_seed: seed,
slots: Default::default(),
}
}
pub fn extract_result(&mut self, rc: i32) -> StageResult {
StageResult {
slots: mem::take(&mut self.slots),
nr: self.vars.nr,
rc,
}
}
pub fn combine(&mut self, StageResult { slots, nr, rc: _ }: StageResult) {
self.slots.combine(slots);
self.vars.nr = self.vars.nr.agg(nr);
}
pub fn reseed(&mut self, seed: u64) -> u64 {
self.rng = StdRng::seed_from_u64(seed);
let old_seed = self.current_seed;
self.current_seed = seed;
old_seed
}
pub fn reseed_random(&mut self) -> u64 {
self.reseed(rand::thread_rng().gen::<u64>())
}
pub fn match_regex(&mut self, s: &Str<'a>, pat: &Str<'a>) -> Result<Int> {
self.regexes.regex_match_loc(&mut self.vars, pat, s)
}
pub fn match_const_regex(&mut self, s: &Str<'a>, pat: &Regex) -> Result<Int> {
runtime::RegexCache::regex_const_match_loc(&mut self.vars, pat, s)
}
pub fn is_match_regex(&mut self, s: &Str<'a>, pat: &Str<'a>) -> Result<bool> {
self.regexes.is_regex_match(pat, s)
}
pub fn load_int(&mut self, slot: usize) -> Int {
self.slots.int[slot]
}
pub fn load_float(&mut self, slot: usize) -> Float {
self.slots.float[slot]
}
pub fn load_str(&mut self, slot: usize) -> Str<'a> {
mem::take(&mut self.slots.strs[slot]).into_str().upcast()
}
pub fn load_intint(&mut self, slot: usize) -> runtime::IntMap<Int> {
mem::take(&mut self.slots.intint[slot]).into()
}
pub fn load_intfloat(&mut self, slot: usize) -> runtime::IntMap<Float> {
mem::take(&mut self.slots.intfloat[slot]).into()
}
pub fn load_intstr(&mut self, slot: usize) -> runtime::IntMap<Str<'a>> {
mem::take(&mut self.slots.intstr[slot])
.into_iter()
.map(|(k, v)| (k, v.into_str().upcast()))
.collect()
}
pub fn load_strint(&mut self, slot: usize) -> runtime::StrMap<'a, Int> {
mem::take(&mut self.slots.strint[slot])
.into_iter()
.map(|(k, v)| (k.into_str().upcast(), v))
.collect()
}
pub fn load_strfloat(&mut self, slot: usize) -> runtime::StrMap<'a, Float> {
mem::take(&mut self.slots.strfloat[slot])
.into_iter()
.map(|(k, v)| (k.into_str().upcast(), v))
.collect()
}
pub fn load_strstr(&mut self, slot: usize) -> runtime::StrMap<'a, Str<'a>> {
mem::take(&mut self.slots.strstr[slot])
.into_iter()
.map(|(k, v)| (k.into_str().upcast(), v.into_str().upcast()))
.collect()
}
pub fn store_int(&mut self, slot: usize, i: Int) {
set_slot(&mut self.slots.int, slot, i)
}
pub fn store_float(&mut self, slot: usize, f: Float) {
set_slot(&mut self.slots.float, slot, f)
}
pub fn store_str(&mut self, slot: usize, s: Str<'a>) {
set_slot(&mut self.slots.strs, slot, s.unmoor().into())
}
pub fn store_intint(&mut self, slot: usize, s: runtime::IntMap<Int>) {
set_slot(
&mut self.slots.intint,
slot,
s.iter(|i| i.map(|(k, v)| (*k, *v)).collect()),
)
}
pub fn store_intfloat(&mut self, slot: usize, s: runtime::IntMap<Float>) {
set_slot(
&mut self.slots.intfloat,
slot,
s.iter(|i| i.map(|(k, v)| (*k, *v)).collect()),
)
}
pub fn store_intstr(&mut self, slot: usize, s: runtime::IntMap<Str<'a>>) {
set_slot(
&mut self.slots.intstr,
slot,
s.iter(|i| i.map(|(k, v)| (*k, v.clone().unmoor().into())).collect()),
)
}
pub fn store_strint(&mut self, slot: usize, s: runtime::StrMap<'a, Int>) {
set_slot(
&mut self.slots.strint,
slot,
s.iter(|i| i.map(|(k, v)| (k.clone().unmoor().into(), *v)).collect()),
)
}
pub fn store_strfloat(&mut self, slot: usize, s: runtime::StrMap<'a, Float>) {
set_slot(
&mut self.slots.strfloat,
slot,
s.iter(|i| i.map(|(k, v)| (k.clone().unmoor().into(), *v)).collect()),
)
}
pub fn store_strstr(&mut self, slot: usize, s: runtime::StrMap<'a, Str<'a>>) {
set_slot(
&mut self.slots.strstr,
slot,
s.iter(|i| {
i.map(|(k, v)| (k.clone().unmoor().into(), v.clone().unmoor().into()))
.collect()
}),
)
}
}
macro_rules! map_regs {
($map_ty:expr, $map_reg:ident, $body:expr) => {{
let _placeholder_k = 0u32;
let _placeholder_v = 0u32;
map_regs!($map_ty, $map_reg, _placeholder_k, _placeholder_v, $body)
}};
($map_ty:expr, $map_reg:ident, $key_reg:ident, $val_reg:ident, $body:expr) => {{
let _placeholder_iter = 0u32;
map_regs!(
$map_ty,
$map_reg,
$key_reg,
$val_reg,
_placeholder_iter,
$body
)
}};
($map_ty:expr, $map_reg:ident, $key_reg:ident, $val_reg:ident, $iter_reg:ident, $body:expr) => {{
let map_ty = $map_ty;
match map_ty {
Ty::MapIntInt => {
let $map_reg: Reg<runtime::IntMap<Int>> = $map_reg.into();
let $key_reg: Reg<Int> = $key_reg.into();
let $val_reg: Reg<Int> = $val_reg.into();
let $iter_reg: Reg<runtime::Iter<Int>> = $iter_reg.into();
$body
}
Ty::MapIntFloat => {
let $map_reg: Reg<runtime::IntMap<Float>> = $map_reg.into();
let $key_reg: Reg<Int> = $key_reg.into();
let $val_reg: Reg<Float> = $val_reg.into();
let $iter_reg: Reg<runtime::Iter<Int>> = $iter_reg.into();
$body
}
Ty::MapIntStr => {
let $map_reg: Reg<runtime::IntMap<Str<'a>>> = $map_reg.into();
let $key_reg: Reg<Int> = $key_reg.into();
let $val_reg: Reg<Str<'a>> = $val_reg.into();
let $iter_reg: Reg<runtime::Iter<Int>> = $iter_reg.into();
$body
}
Ty::MapStrInt => {
let $map_reg: Reg<runtime::StrMap<'a, Int>> = $map_reg.into();
let $key_reg: Reg<Str<'a>> = $key_reg.into();
let $val_reg: Reg<Int> = $val_reg.into();
let $iter_reg: Reg<runtime::Iter<Str<'a>>> = $iter_reg.into();
$body
}
Ty::MapStrFloat => {
let $map_reg: Reg<runtime::StrMap<'a, Float>> = $map_reg.into();
let $key_reg: Reg<Str<'a>> = $key_reg.into();
let $val_reg: Reg<Float> = $val_reg.into();
let $iter_reg: Reg<runtime::Iter<Str<'a>>> = $iter_reg.into();
$body
}
Ty::MapStrStr => {
let $map_reg: Reg<runtime::StrMap<'a, Str<'a>>> = $map_reg.into();
let $key_reg: Reg<Str<'a>> = $key_reg.into();
let $val_reg: Reg<Str<'a>> = $val_reg.into();
let $iter_reg: Reg<runtime::Iter<Str<'a>>> = $iter_reg.into();
$body
}
Ty::Null | Ty::Int | Ty::Float | Ty::Str | Ty::IterInt | Ty::IterStr => panic!(
"attempting to perform map operations on non-map type: {:?}",
map_ty
),
}
}};
}
pub(crate) struct Interp<'a, LR: LineReader = ClassicReader> {
main_func: Stage<usize>,
num_workers: usize,
instrs: Vec<Vec<Instr<'a>>>,
stack: Vec<(usize /*function*/, Label /*instr*/)>,
line: LR::Line,
read_files: runtime::FileRead<LR>,
core: Core<'a>,
pub(crate) floats: Storage<Float>,
pub(crate) ints: Storage<Int>,
pub(crate) strs: Storage<Str<'a>>,
pub(crate) maps_int_float: Storage<runtime::IntMap<Float>>,
pub(crate) maps_int_int: Storage<runtime::IntMap<Int>>,
pub(crate) maps_int_str: Storage<runtime::IntMap<Str<'a>>>,
pub(crate) maps_str_float: Storage<runtime::StrMap<'a, Float>>,
pub(crate) maps_str_int: Storage<runtime::StrMap<'a, Int>>,
pub(crate) maps_str_str: Storage<runtime::StrMap<'a, Str<'a>>>,
pub(crate) iters_int: Storage<runtime::Iter<Int>>,
pub(crate) iters_str: Storage<runtime::Iter<Str<'a>>>,
}
fn default_of<T: Default>(n: usize) -> Storage<T> {
let mut regs = Vec::new();
regs.resize_with(n, Default::default);
Storage {
regs,
stack: Default::default(),
}
}
impl<'a, LR: LineReader> Interp<'a, LR> {
pub(crate) fn new(
instrs: Vec<Vec<Instr<'a>>>,
main_func: Stage<usize>,
num_workers: usize,
regs: impl Fn(compile::Ty) -> usize,
stdin: LR,
ff: impl runtime::writers::FileFactory,
used_fields: &FieldSet,
named_columns: Option<Vec<&[u8]>>,
) -> Self {
use compile::Ty::*;
Interp {
main_func,
num_workers,
instrs,
stack: Default::default(),
floats: default_of(regs(Float)),
ints: default_of(regs(Int)),
strs: default_of(regs(Str)),
core: Core::new(ff),
line: Default::default(),
read_files: runtime::FileRead::new(stdin, used_fields.clone(), named_columns),
maps_int_float: default_of(regs(MapIntFloat)),
maps_int_int: default_of(regs(MapIntInt)),
maps_int_str: default_of(regs(MapIntStr)),
maps_str_float: default_of(regs(MapStrFloat)),
maps_str_int: default_of(regs(MapStrInt)),
maps_str_str: default_of(regs(MapStrStr)),
iters_int: default_of(regs(IterInt)),
iters_str: default_of(regs(IterStr)),
}
}
pub(crate) fn instrs(&self) -> &Vec<Vec<Instr<'a>>> {
&self.instrs
}
fn format_arg(&self, (reg, ty): (NumTy, Ty)) -> Result<runtime::FormatArg<'a>> {
Ok(match ty {
Ty::Str => self.get(Reg::<Str<'a>>::from(reg)).clone().into(),
Ty::Int => (*self.get(Reg::<Int>::from(reg))).into(),
Ty::Float => (*self.get(Reg::<Float>::from(reg))).into(),
_ => return err!("non-scalar (s)printf argument type {:?}", ty),
})
}
fn reset_file_vars(&mut self) {
self.core.vars.fnr = 0;
self.core.vars.filename = self.read_files.stdin_filename().upcast();
}
pub(crate) fn run_parallel(&mut self) -> Result<i32> {
if self.num_workers <= 1 {
return self.run_serial();
}
let handles = self.read_files.try_resize(self.num_workers - 1);
if handles.is_empty() {
return self.run_serial();
}
let (begin, middle, end) = match self.main_func {
Stage::Par {
begin,
main_loop,
end,
} => (begin, main_loop, end),
Stage::Main(_) => {
return err!("unexpected Main-only configuration for parallel execution")
}
};
let main_loop = if let Some(main_loop) = middle {
main_loop
} else {
return self.run_serial();
};
if let Some(off) = begin {
let rc = self.run_at(off)?;
if rc != 0 {
return Ok(rc);
}
}
if self.core.write_files.flush_stdout().is_err() {
return Ok(1);
}
let mut old_read_files = mem::take(&mut self.read_files.inputs);
fn wrap_error<T, S>(r: std::result::Result<Result<T>, S>) -> Result<T> {
match r {
Ok(Ok(t)) => Ok(t),
Ok(Err(e)) => Err(e),
Err(_) => err!("error in executing worker thread"),
}
}
let scope_res = scope(|s| {
let (sender, receiver) = bounded(handles.len());
let float_size = self.floats.regs.len();
let ints_size = self.ints.regs.len();
let strs_size = self.strs.regs.len();
let maps_int_int_size = self.maps_int_int.regs.len();
let maps_int_float_size = self.maps_int_float.regs.len();
let maps_int_str_size = self.maps_int_str.regs.len();
let maps_str_int_size = self.maps_str_int.regs.len();
let maps_str_float_size = self.maps_str_float.regs.len();
let maps_str_str_size = self.maps_str_str.regs.len();
let iters_int_size = self.iters_int.regs.len();
let iters_str_size = self.iters_str.regs.len();
for (i, handle) in handles.into_iter().enumerate() {
let sender = sender.clone();
let core_shuttle = self.core.shuttle(i as Int + 2);
let instrs = self.instrs.clone();
s.spawn(move |_| {
if let Some(read_files) = handle() {
let mut interp = Interp {
main_func: Stage::Main(main_loop),
num_workers: 1,
instrs,
stack: Default::default(),
core: core_shuttle(),
line: Default::default(),
read_files,
floats: default_of(float_size),
ints: default_of(ints_size),
strs: default_of(strs_size),
maps_int_int: default_of(maps_int_int_size),
maps_int_float: default_of(maps_int_float_size),
maps_int_str: default_of(maps_int_str_size),
maps_str_int: default_of(maps_str_int_size),
maps_str_float: default_of(maps_str_float_size),
maps_str_str: default_of(maps_str_str_size),
iters_int: default_of(iters_int_size),
iters_str: default_of(iters_str_size),
};
let res = interp.run_at(main_loop);
let _ = match res {
Err(e) => sender.send(Err(e)),
Ok(rc) => sender.send(Ok(interp.core.extract_result(rc))),
};
}
});
}
mem::drop(sender);
self.core.vars.pid = 1;
let mut rc = self.run_at(main_loop)?;
self.core.vars.pid = 0;
while let Ok(res) = receiver.recv() {
let res = res?;
let sub_rc = res.rc;
self.core.combine(res);
if rc == 0 && sub_rc != 0 {
rc = sub_rc;
}
}
Ok(rc)
});
let rc = wrap_error(scope_res)?;
if rc != 0 {
return Ok(rc);
}
if let Some(end) = end {
mem::swap(&mut self.read_files.inputs, &mut old_read_files);
Ok(self.run_at(end)?)
} else {
Ok(0)
}
}
pub(crate) fn run_serial(&mut self) -> Result<i32> {
let offs: smallvec::SmallVec<[usize; 3]> = self.main_func.iter().cloned().collect();
for off in offs.into_iter() {
let rc = self.run_at(off)?;
if rc != 0 {
return Ok(rc);
}
}
Ok(0)
}
pub(crate) fn run(&mut self) -> Result<i32> {
match self.main_func {
Stage::Main(_) => self.run_serial(),
Stage::Par { .. } => self.run_parallel(),
}
}
#[allow(clippy::never_loop)]
pub(crate) fn run_at(&mut self, mut cur_fn: usize) -> Result<i32> {
use Instr::*;
let mut scratch: Vec<runtime::FormatArg> = Vec::new();
let mut instrs = (&mut self.instrs[cur_fn]) as *mut Vec<Instr<'a>>;
let mut cur = 0;
'outer: loop {
cur = loop {
debug_assert!(cur < unsafe { (*instrs).len() });
use Variable::*;
match unsafe { (*instrs).get_unchecked(cur) } {
StoreConstStr(sr, s) => {
let sr = *sr;
*self.get_mut(sr) = s.clone_str()
}
StoreConstInt(ir, i) => {
let ir = *ir;
*self.get_mut(ir) = *i
}
StoreConstFloat(fr, f) => {
let fr = *fr;
*self.get_mut(fr) = *f
}
IntToStr(sr, ir) => {
let s = runtime::convert::<_, Str>(*self.get(*ir));
let sr = *sr;
*self.get_mut(sr) = s;
}
FloatToStr(sr, fr) => {
let s = runtime::convert::<_, Str>(*self.get(*fr));
let sr = *sr;
*self.get_mut(sr) = s;
}
StrToInt(ir, sr) => {
let i = runtime::convert::<_, Int>(self.get(*sr));
let ir = *ir;
*self.get_mut(ir) = i;
}
HexStrToInt(ir, sr) => {
let i = self.get(*sr).with_bytes(runtime::hextoi);
let ir = *ir;
*self.get_mut(ir) = i;
}
StrToFloat(fr, sr) => {
let f = runtime::convert::<_, Float>(self.get(*sr));
let fr = *fr;
*self.get_mut(fr) = f;
}
FloatToInt(ir, fr) => {
let i = runtime::convert::<_, Int>(*self.get(*fr));
let ir = *ir;
*self.get_mut(ir) = i;
}
IntToFloat(fr, ir) => {
let f = runtime::convert::<_, Float>(*self.get(*ir));
let fr = *fr;
*self.get_mut(fr) = f;
}
AddInt(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = l + r;
}
AddFloat(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = l + r;
}
MulInt(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = l * r;
}
MulFloat(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = l * r;
}
MinusInt(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = l - r;
}
MinusFloat(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = l - r;
}
ModInt(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = l % r;
}
ModFloat(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = l % r;
}
Div(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = l / r;
}
Pow(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = l.powf(r);
}
Not(res, ir) => {
let res = *res;
let i = *self.get(*ir);
*self.get_mut(res) = (i == 0) as Int;
}
NotStr(res, sr) => {
let res = *res;
let sr = *sr;
let is_empty = self.get(sr).with_bytes(|bs| bs.is_empty());
*self.get_mut(res) = is_empty as Int;
}
NegInt(res, ir) => {
let res = *res;
let i = *self.get(*ir);
*self.get_mut(res) = -i;
}
NegFloat(res, fr) => {
let res = *res;
let f = *self.get(*fr);
*self.get_mut(res) = -f;
}
Float1(ff, dst, src) => {
let f = *index(&self.floats, src);
let dst = *dst;
*self.get_mut(dst) = ff.eval1(f);
}
Float2(ff, dst, x, y) => {
let fx = *index(&self.floats, x);
let fy = *index(&self.floats, y);
let dst = *dst;
*self.get_mut(dst) = ff.eval2(fx, fy);
}
Int1(bw, dst, src) => {
let i = *index(&self.ints, src);
let dst = *dst;
*self.get_mut(dst) = bw.eval1(i);
}
Int2(bw, dst, x, y) => {
let ix = *index(&self.ints, x);
let iy = *index(&self.ints, y);
let dst = *dst;
*self.get_mut(dst) = bw.eval2(ix, iy);
}
Rand(dst) => {
let res: f64 = self.core.rng.gen_range(0.0..=1.0);
*index_mut(&mut self.floats, dst) = res;
}
Srand(res, seed) => {
let old_seed = self.core.reseed(*index(&self.ints, seed) as u64);
*index_mut(&mut self.ints, res) = old_seed as Int;
}
ReseedRng(res) => {
*index_mut(&mut self.ints, res) = self.core.reseed_random() as Int;
}
StartsWithConst(res, s, bs) => {
let s_bytes = unsafe { &*index(&self.strs, s).get_bytes() };
*index_mut(&mut self.ints, res) =
(bs.len() <= s_bytes.len() && s_bytes[..bs.len()] == **bs) as Int;
}
Concat(res, l, r) => {
let res = *res;
let l = self.get(*l).clone();
let r = self.get(*r).clone();
*self.get_mut(res) = Str::concat(l, r);
}
Match(res, l, r) => {
*index_mut(&mut self.ints, res) = self
.core
.match_regex(index(&self.strs, l), index(&self.strs, r))?;
}
IsMatch(res, l, r) => {
*index_mut(&mut self.ints, res) = self
.core
.is_match_regex(index(&self.strs, l), index(&self.strs, r))?
as Int;
}
MatchConst(res, x, pat) => {
*index_mut(&mut self.ints, res) =
runtime::RegexCache::regex_const_match(&*pat, index(&self.strs, x))
as Int;
}
IsMatchConst(res, x, pat) => {
*index_mut(&mut self.ints, res) =
self.core.match_const_regex(index(&self.strs, x), &*pat)?;
}
SubstrIndex(res, s, t) => {
let res = *res;
let s = index(&self.strs, s);
let t = index(&self.strs, t);
*self.get_mut(res) = runtime::string_search::index_substr(t, s);
}
LenStr(res, s) => {
let res = *res;
let s = *s;
let len = self.get(s).len();
*self.get_mut(res) = len as Int;
}
Sub(res, pat, s, in_s) => {
let (subbed, new) = {
let pat = index(&self.strs, pat);
let s = index(&self.strs, s);
let in_s = index(&self.strs, in_s);
self.core
.regexes
.with_regex(pat, |re| in_s.subst_first(re, s))?
};
*index_mut(&mut self.strs, in_s) = subbed;
*index_mut(&mut self.ints, res) = new as Int;
}
GSub(res, pat, s, in_s) => {
let (subbed, subs_made) = {
let pat = index(&self.strs, pat);
let s = index(&self.strs, s);
let in_s = index(&self.strs, in_s);
self.core
.regexes
.with_regex(pat, |re| in_s.subst_all(re, s))?
};
*index_mut(&mut self.strs, in_s) = subbed;
*index_mut(&mut self.ints, res) = subs_made;
}
GenSubDynamic(res, pat, s, how, in_s) => {
let subbed = {
let pat = index(&self.strs, pat);
let s = index(&self.strs, s);
let how = index(&self.strs, how);
let in_s = index(&self.strs, in_s);
self.core
.regexes
.with_regex(pat, |re| in_s.gen_subst_dynamic(re, s, how))?
};
*index_mut(&mut self.strs, res) = subbed;
}
EscapeCSV(res, s) => {
*index_mut(&mut self.strs, res) = {
let s = index(&self.strs, s);
runtime::escape_csv(s)
};
}
EscapeTSV(res, s) => {
*index_mut(&mut self.strs, res) = {
let s = index(&self.strs, s);
runtime::escape_tsv(s)
};
}
Substr(res, base, l, r) => {
let base = index(&self.strs, base);
let len = base.len();
let l = cmp::max(0, -1 + *index(&self.ints, l));
*index_mut(&mut self.strs, res) = if l as usize >= len {
Str::default()
} else {
let r = cmp::min(len as Int, l.saturating_add(*index(&self.ints, r)))
as usize;
base.slice(l as usize, r)
};
}
LTFloat(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = (l < r) as Int;
}
LTInt(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = (l < r) as Int;
}
LTStr(res, l, r) => {
let res = *res;
let l = self.get(*l);
let r = self.get(*r);
*self.get_mut(res) = l.with_bytes(|l| r.with_bytes(|r| l < r)) as Int;
}
GTFloat(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = (l > r) as Int;
}
GTInt(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = (l > r) as Int;
}
GTStr(res, l, r) => {
let res = *res;
let l = self.get(*l);
let r = self.get(*r);
*self.get_mut(res) = l.with_bytes(|l| r.with_bytes(|r| l > r)) as Int;
}
LTEFloat(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = (l <= r) as Int;
}
LTEInt(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = (l <= r) as Int;
}
LTEStr(res, l, r) => {
let res = *res;
let l = self.get(*l);
let r = self.get(*r);
*self.get_mut(res) = l.with_bytes(|l| r.with_bytes(|r| l <= r)) as Int;
}
GTEFloat(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = (l >= r) as Int;
}
GTEInt(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = (l >= r) as Int;
}
GTEStr(res, l, r) => {
let res = *res;
let l = self.get(*l);
let r = self.get(*r);
*self.get_mut(res) = l.with_bytes(|l| r.with_bytes(|r| l >= r)) as Int;
}
EQFloat(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = (l == r) as Int;
}
EQInt(res, l, r) => {
let res = *res;
let l = *self.get(*l);
let r = *self.get(*r);
*self.get_mut(res) = (l == r) as Int;
}
EQStr(res, l, r) => {
let res = *res;
let l = self.get(*l);
let r = self.get(*r);
*self.get_mut(res) = (l == r) as Int;
}
SetColumn(dst, src) => {
let col = *self.get(*dst);
let v = index(&self.strs, src);
self.line
.set_col(col, v, &self.core.vars.ofs, &mut self.core.regexes)?;
}
GetColumn(dst, src) => {
let col = *self.get(*src);
let dst = *dst;
let res = self.line.get_col(
col,
&self.core.vars.fs,
&self.core.vars.ofs,
&mut self.core.regexes,
)?;
*self.get_mut(dst) = res;
}
JoinCSV(dst, start, end) => {
let nf = self.line.nf(&self.core.vars.fs, &mut self.core.regexes)?;
*index_mut(&mut self.strs, dst) = {
let start = *index(&self.ints, start);
let end = *index(&self.ints, end);
self.line.join_cols(start, end, &",".into(), nf, |s| {
runtime::escape_csv(&s)
})?
};
}
JoinTSV(dst, start, end) => {
let nf = self.line.nf(&self.core.vars.fs, &mut self.core.regexes)?;
*index_mut(&mut self.strs, dst) = {
let start = *index(&self.ints, start);
let end = *index(&self.ints, end);
self.line.join_cols(start, end, &"\t".into(), nf, |s| {
runtime::escape_tsv(&s)
})?
};
}
JoinColumns(dst, start, end, sep) => {
let nf = self.line.nf(&self.core.vars.fs, &mut self.core.regexes)?;
*index_mut(&mut self.strs, dst) = {
let sep = index(&self.strs, sep);
let start = *index(&self.ints, start);
let end = *index(&self.ints, end);
self.line.join_cols(start, end, sep, nf, |s| s)?
};
}
ToUpperAscii(dst, src) => {
let res = index(&self.strs, src).to_upper_ascii();
*index_mut(&mut self.strs, dst) = res;
}
ToLowerAscii(dst, src) => {
let res = index(&self.strs, src).to_lower_ascii();
*index_mut(&mut self.strs, dst) = res;
}
SplitInt(flds, to_split, arr, pat) => {
let to_split = index(&self.strs, to_split);
let arr = index(&self.maps_int_str, arr);
let pat = index(&self.strs, pat);
self.core.regexes.split_regex_intmap(pat, to_split, arr)?;
let res = arr.len() as Int;
let flds = *flds;
*self.get_mut(flds) = res;
}
SplitStr(flds, to_split, arr, pat) => {
let to_split = index(&self.strs, to_split);
let arr = index(&self.maps_str_str, arr);
let pat = index(&self.strs, pat);
self.core.regexes.split_regex_strmap(pat, to_split, arr)?;
let res = arr.len() as Int;
let flds = *flds;
*self.get_mut(flds) = res;
}
Sprintf { dst, fmt, args } => {
debug_assert_eq!(scratch.len(), 0);
for a in args.iter() {
scratch.push(self.format_arg(*a)?);
}
use runtime::str_impl::DynamicBuf;
let fmt_str = index(&self.strs, fmt);
let mut buf = DynamicBuf::new(0);
fmt_str
.with_bytes(|bs| runtime::printf::printf(&mut buf, bs, &scratch[..]))?;
scratch.clear();
let res = buf.into_str();
let dst = *dst;
*self.get_mut(dst) = res;
}
PrintAll { output, args } => {
let mut scratch_strs =
smallvec::SmallVec::<[&Str; 4]>::with_capacity(args.len());
for a in args {
scratch_strs.push(index(&self.strs, a));
}
let res = if let Some((out_path_reg, fspec)) = output {
let out_path = index(&self.strs, out_path_reg);
self.core
.write_files
.write_all(&scratch_strs[..], Some((out_path, *fspec)))
} else {
self.core.write_files.write_all(&scratch_strs[..], None)
};
if res.is_err() {
return Ok(0);
}
}
Printf { output, fmt, args } => {
debug_assert_eq!(scratch.len(), 0);
for a in args.iter() {
scratch.push(self.format_arg(*a)?);
}
let fmt_str = index(&self.strs, fmt);
let res = if let Some((out_path_reg, fspec)) = output {
let out_path = index(&self.strs, out_path_reg);
self.core.write_files.printf(
Some((out_path, *fspec)),
fmt_str,
&scratch[..],
)
} else {
self.core.write_files.printf(None, fmt_str, &scratch[..])
};
if res.is_err() {
return Ok(0);
}
scratch.clear();
}
Close(file) => {
let file = index(&self.strs, file);
self.core.write_files.close(file)?;
self.read_files.close(file);
}
RunCmd(dst, cmd) => {
*index_mut(&mut self.ints, dst) =
index(&self.strs, cmd).with_bytes(runtime::run_command);
}
Exit(code) => return Ok(*index(&self.ints, code) as i32),
Lookup {
map_ty,
dst,
map,
key,
} => self.lookup(*map_ty, *dst, *map, *key),
Contains {
map_ty,
dst,
map,
key,
} => self.contains(*map_ty, *dst, *map, *key),
Delete { map_ty, map, key } => self.delete(*map_ty, *map, *key),
Clear { map_ty, map } => self.clear(*map_ty, *map),
Len { map_ty, map, dst } => self.len(*map_ty, *map, *dst),
Store {
map_ty,
map,
key,
val,
} => self.store_map(*map_ty, *map, *key, *val),
IncInt {
map_ty,
map,
key,
by,
dst,
} => self.inc_map_int(*map_ty, *map, *key, *by, *dst),
IncFloat {
map_ty,
map,
key,
by,
dst,
} => self.inc_map_float(*map_ty, *map, *key, *by, *dst),
LoadVarStr(dst, var) => {
let s = self.core.vars.load_str(*var)?;
let dst = *dst;
*self.get_mut(dst) = s;
}
StoreVarStr(var, src) => {
let src = *src;
let s = self.get(src).clone();
self.core.vars.store_str(*var, s)?;
}
LoadVarInt(dst, var) => {
if let NF = *var {
self.core.vars.nf =
self.line.nf(&self.core.vars.fs, &mut self.core.regexes)? as Int;
}
let i = self.core.vars.load_int(*var)?;
let dst = *dst;
*self.get_mut(dst) = i;
}
StoreVarInt(var, src) => {
let src = *src;
let s = *self.get(src);
self.core.vars.store_int(*var, s)?;
}
LoadVarIntMap(dst, var) => {
let arr = self.core.vars.load_intmap(*var)?;
let dst = *dst;
*self.get_mut(dst) = arr;
}
StoreVarIntMap(var, src) => {
let src = *src;
let s = self.get(src).clone();
self.core.vars.store_intmap(*var, s)?;
}
LoadVarStrMap(dst, var) => {
let arr = self.core.vars.load_strmap(*var)?;
let dst = *dst;
*self.get_mut(dst) = arr;
}
StoreVarStrMap(var, src) => {
let src = *src;
let s = self.get(src).clone();
self.core.vars.store_strmap(*var, s)?;
}
IterBegin { map_ty, map, dst } => self.iter_begin(*map_ty, *map, *dst),
IterHasNext { iter_ty, dst, iter } => self.iter_has_next(*iter_ty, *dst, *iter),
IterGetNext { iter_ty, dst, iter } => self.iter_get_next(*iter_ty, *dst, *iter),
LoadSlot { ty, dst, slot } => self.load_slot(*ty, *dst, *slot),
StoreSlot { ty, src, slot } => self.store_slot(*ty, *src, *slot),
Mov(ty, dst, src) => self.mov(*ty, *dst, *src),
AllocMap(ty, reg) => self.alloc_map(*ty, *reg),
ReadErr(dst, file, is_file) => {
let dst = *dst;
let file = index(&self.strs, file);
let res = if *is_file {
self.read_files.read_err(file)?
} else {
self.read_files.read_err_cmd(file)?
};
*self.get_mut(dst) = res;
}
NextLine(dst, file, is_file) => {
let dst = *dst;
let file = index(&self.strs, file);
match self.core.regexes.get_line(
file,
&self.core.vars.rs,
&mut self.read_files,
*is_file,
) {
Ok(l) => *self.get_mut(dst) = l,
Err(_) => *self.get_mut(dst) = "".into(),
};
}
ReadErrStdin(dst) => {
let dst = *dst;
let res = self.read_files.read_err_stdin();
*self.get_mut(dst) = res;
}
NextLineStdin(dst) => {
let dst = *dst;
let (changed, res) = self
.core
.regexes
.get_line_stdin(&self.core.vars.rs, &mut self.read_files)?;
if changed {
self.reset_file_vars();
}
*self.get_mut(dst) = res;
}
NextLineStdinFused() => {
let changed = self.core.regexes.get_line_stdin_reuse(
&self.core.vars.rs,
&mut self.read_files,
&mut self.line,
)?;
if changed {
self.reset_file_vars()
}
}
NextFile() => {
self.read_files.next_file()?;
self.reset_file_vars();
}
UpdateUsedFields() => {
let fi = &self.core.vars.fi;
self.read_files.update_named_columns(fi);
}
SetFI(key, val) => {
let key = *index(&self.ints, key);
let val = *index(&self.ints, val);
let col = self.line.get_col(
key,
&self.core.vars.fs,
&self.core.vars.ofs,
&mut self.core.regexes,
)?;
self.core.vars.fi.insert(col, val);
}
JmpIf(cond, lbl) => {
let cond = *cond;
if *self.get(cond) != 0 {
break lbl.0 as usize;
}
}
Jmp(lbl) => {
break lbl.0 as usize;
}
Push(ty, reg) => self.push_reg(*ty, *reg),
Pop(ty, reg) => self.pop_reg(*ty, *reg),
Call(func) => {
self.stack.push((cur_fn, Label(cur + 1)));
cur_fn = *func;
instrs = &mut self.instrs[*func];
break 0;
}
Ret => {
if let Some((func, Label(inst))) = self.stack.pop() {
cur_fn = func;
instrs = &mut self.instrs[func];
break inst as usize;
} else {
break 'outer Ok(0);
}
}
};
break cur + 1;
};
}
}
fn mov(&mut self, ty: Ty, dst: NumTy, src: NumTy) {
match ty {
Ty::Int => {
let src = *index(&self.ints, &src.into());
*index_mut(&mut self.ints, &dst.into()) = src;
}
Ty::Float => {
let src = *index(&self.floats, &src.into());
*index_mut(&mut self.floats, &dst.into()) = src;
}
Ty::Str => {
let src = index(&self.strs, &src.into()).clone();
*index_mut(&mut self.strs, &dst.into()) = src;
}
Ty::MapIntInt => {
let src = index(&self.maps_int_int, &src.into()).clone();
*index_mut(&mut self.maps_int_int, &dst.into()) = src;
}
Ty::MapIntFloat => {
let src = index(&self.maps_int_float, &src.into()).clone();
*index_mut(&mut self.maps_int_float, &dst.into()) = src;
}
Ty::MapIntStr => {
let src = index(&self.maps_int_str, &src.into()).clone();
*index_mut(&mut self.maps_int_str, &dst.into()) = src;
}
Ty::MapStrInt => {
let src = index(&self.maps_str_int, &src.into()).clone();
*index_mut(&mut self.maps_str_int, &dst.into()) = src;
}
Ty::MapStrFloat => {
let src = index(&self.maps_str_float, &src.into()).clone();
*index_mut(&mut self.maps_str_float, &dst.into()) = src;
}
Ty::MapStrStr => {
let src = index(&self.maps_str_str, &src.into()).clone();
*index_mut(&mut self.maps_str_str, &dst.into()) = src;
}
Ty::Null | Ty::IterInt | Ty::IterStr => {
panic!("invalid type for move operation: {:?}", ty)
}
}
}
fn alloc_map(&mut self, ty: Ty, reg: NumTy) {
map_regs!(ty, reg, *self.get_mut(reg) = Default::default())
}
fn lookup(&mut self, map_ty: Ty, dst: NumTy, map: NumTy, key: NumTy) {
map_regs!(map_ty, map, key, dst, {
let res = self.get(map).get(self.get(key));
*self.get_mut(dst) = res;
});
}
fn contains(&mut self, map_ty: Ty, dst: NumTy, map: NumTy, key: NumTy) {
let _v = 0u32;
let dst: Reg<Int> = dst.into();
map_regs!(map_ty, map, key, _v, {
let res = self.get(map).contains(self.get(key)) as Int;
*self.get_mut(dst) = res;
});
}
fn delete(&mut self, map_ty: Ty, map: NumTy, key: NumTy) {
let _v = 0u32;
map_regs!(map_ty, map, key, _v, {
self.get(map).delete(self.get(key))
});
}
fn clear(&mut self, map_ty: Ty, map: NumTy) {
map_regs!(map_ty, map, self.get(map).clear());
}
#[allow(clippy::clone_on_copy)]
fn store_map(&mut self, map_ty: Ty, map: NumTy, key: NumTy, val: NumTy) {
map_regs!(map_ty, map, key, val, {
let k = self.get(key).clone();
let v = self.get(val).clone();
self.get(map).insert(k, v);
});
}
fn inc_map_int(&mut self, map_ty: Ty, map: NumTy, key: NumTy, by: Reg<Int>, dst: NumTy) {
map_regs!(map_ty, map, key, dst, {
let k = self.get(key);
let m = self.get(map);
let by = *self.get(by);
let res = m.inc_int(k, by);
*self.get_mut(dst) = res;
})
}
fn inc_map_float(&mut self, map_ty: Ty, map: NumTy, key: NumTy, by: Reg<Float>, dst: NumTy) {
map_regs!(map_ty, map, key, dst, {
let k = self.get(key);
let m = self.get(map);
let by = *self.get(by);
let res = m.inc_float(k, by);
*self.get_mut(dst) = res;
})
}
fn len(&mut self, map_ty: Ty, map: NumTy, dst: NumTy) {
let len = map_regs!(map_ty, map, self.get(map).len() as Int);
*index_mut(&mut self.ints, &dst.into()) = len;
}
fn iter_begin(&mut self, map_ty: Ty, map: NumTy, dst: NumTy) {
let _k = 0u32;
let _v = 0u32;
map_regs!(map_ty, map, _k, _v, dst, {
let iter = self.get(map).to_iter();
*self.get_mut(dst) = iter;
})
}
fn iter_has_next(&mut self, iter_ty: Ty, dst: NumTy, iter: NumTy) {
match iter_ty {
Ty::IterInt => {
let res = index(&self.iters_int, &iter.into()).has_next() as Int;
*index_mut(&mut self.ints, &dst.into()) = res;
}
Ty::IterStr => {
let res = index(&self.iters_str, &iter.into()).has_next() as Int;
*index_mut(&mut self.ints, &dst.into()) = res;
}
x => panic!("non-iterator type passed to has_next: {:?}", x),
}
}
fn iter_get_next(&mut self, iter_ty: Ty, dst: NumTy, iter: NumTy) {
match iter_ty {
Ty::IterInt => {
let res = unsafe { *index(&self.iters_int, &iter.into()).get_next() };
*index_mut(&mut self.ints, &dst.into()) = res;
}
Ty::IterStr => {
let res = unsafe { index(&self.iters_str, &iter.into()).get_next().clone() };
*index_mut(&mut self.strs, &dst.into()) = res;
}
x => panic!("non-iterator type passed to get_next: {:?}", x),
}
}
fn load_slot(&mut self, ty: Ty, dst: NumTy, slot: Int) {
let slot = slot as usize;
macro_rules! do_load {
($load_meth:tt, $reg_fld:tt) => {
*index_mut(&mut self.$reg_fld, &dst.into()) = self.core.$load_meth(slot)
};
}
match ty {
Ty::Int => do_load!(load_int, ints),
Ty::Float => do_load!(load_float, floats),
Ty::Str => do_load!(load_str, strs),
Ty::MapIntInt => do_load!(load_intint, maps_int_int),
Ty::MapIntFloat => do_load!(load_intfloat, maps_int_float),
Ty::MapIntStr => do_load!(load_intstr, maps_int_str),
Ty::MapStrInt => do_load!(load_strint, maps_str_int),
Ty::MapStrFloat => do_load!(load_strfloat, maps_str_float),
Ty::MapStrStr => do_load!(load_strstr, maps_str_str),
Ty::Null | Ty::IterInt | Ty::IterStr => {
panic!("unexpected operand type to slot operation: {:?}", ty)
}
}
}
fn store_slot(&mut self, ty: Ty, src: NumTy, slot: Int) {
let slot = slot as usize;
macro_rules! do_store {
($store_meth:tt, $reg_fld:tt) => {
self.core
.$store_meth(slot, index(&self.$reg_fld, &src.into()).clone())
};
}
match ty {
Ty::Int => do_store!(store_int, ints),
Ty::Float => do_store!(store_float, floats),
Ty::Str => do_store!(store_str, strs),
Ty::MapIntInt => do_store!(store_intint, maps_int_int),
Ty::MapIntFloat => do_store!(store_intfloat, maps_int_float),
Ty::MapIntStr => do_store!(store_intstr, maps_int_str),
Ty::MapStrInt => do_store!(store_strint, maps_str_int),
Ty::MapStrFloat => do_store!(store_strfloat, maps_str_float),
Ty::MapStrStr => do_store!(store_strstr, maps_str_str),
Ty::Null | Ty::IterInt | Ty::IterStr => panic!("unsupported slot type: {:?}", ty),
}
}
fn push_reg(&mut self, ty: Ty, src: NumTy) {
match ty {
Ty::Int => push(&mut self.ints, &src.into()),
Ty::Float => push(&mut self.floats, &src.into()),
Ty::Str => push(&mut self.strs, &src.into()),
Ty::MapIntInt => push(&mut self.maps_int_int, &src.into()),
Ty::MapIntFloat => push(&mut self.maps_int_float, &src.into()),
Ty::MapIntStr => push(&mut self.maps_int_str, &src.into()),
Ty::MapStrInt => push(&mut self.maps_str_int, &src.into()),
Ty::MapStrFloat => push(&mut self.maps_str_float, &src.into()),
Ty::MapStrStr => push(&mut self.maps_str_str, &src.into()),
Ty::Null | Ty::IterInt | Ty::IterStr => {
panic!("unsupported register type for push operation: {:?}", ty)
}
}
}
fn pop_reg(&mut self, ty: Ty, dst: NumTy) {
match ty {
Ty::Int => *index_mut(&mut self.ints, &dst.into()) = pop(&mut self.ints),
Ty::Float => *index_mut(&mut self.floats, &dst.into()) = pop(&mut self.floats),
Ty::Str => *index_mut(&mut self.strs, &dst.into()) = pop(&mut self.strs),
Ty::MapIntInt => {
*index_mut(&mut self.maps_int_int, &dst.into()) = pop(&mut self.maps_int_int)
}
Ty::MapIntFloat => {
*index_mut(&mut self.maps_int_float, &dst.into()) = pop(&mut self.maps_int_float)
}
Ty::MapIntStr => {
*index_mut(&mut self.maps_int_str, &dst.into()) = pop(&mut self.maps_int_str)
}
Ty::MapStrInt => {
*index_mut(&mut self.maps_str_int, &dst.into()) = pop(&mut self.maps_str_int)
}
Ty::MapStrFloat => {
*index_mut(&mut self.maps_str_float, &dst.into()) = pop(&mut self.maps_str_float)
}
Ty::MapStrStr => {
*index_mut(&mut self.maps_str_float, &dst.into()) = pop(&mut self.maps_str_float)
}
Ty::Null | Ty::IterInt | Ty::IterStr => {
panic!("unsupported register type for pop operation: {:?}", ty)
}
}
}
}
#[cfg(debug_assertions)]
const CHECKED: bool = true;
#[cfg(not(debug_assertions))]
const CHECKED: bool = false;
#[inline(always)]
pub(crate) fn index<'a, T>(Storage { regs, .. }: &'a Storage<T>, reg: &Reg<T>) -> &'a T {
if CHECKED {
®s[reg.index()]
} else {
debug_assert!(reg.index() < regs.len());
unsafe { regs.get_unchecked(reg.index()) }
}
}
#[inline(always)]
pub(crate) fn index_mut<'a, T>(
Storage { regs, .. }: &'a mut Storage<T>,
reg: &Reg<T>,
) -> &'a mut T {
if CHECKED {
&mut regs[reg.index()]
} else {
debug_assert!(reg.index() < regs.len());
unsafe { regs.get_unchecked_mut(reg.index()) }
}
}
pub(crate) fn push<T: Clone>(s: &mut Storage<T>, reg: &Reg<T>) {
let v = index(s, reg).clone();
s.stack.push(v);
}
pub(crate) fn pop<T: Clone>(s: &mut Storage<T>) -> T {
s.stack.pop().expect("pop must be called on nonempty stack")
}
#[cfg(test)]
impl<T: Default> Storage<T> {
#[cfg(feature = "unstable")]
fn reset(&mut self) {
self.stack.clear();
for i in self.regs.iter_mut() {
*i = Default::default();
}
}
}
#[cfg(test)]
impl<'a, LR: LineReader> Interp<'a, LR> {
#[cfg(feature = "unstable")]
pub(crate) fn reset(&mut self) {
self.stack = Default::default();
self.core.vars = Default::default();
self.line = Default::default();
self.core.regexes = Default::default();
self.floats.reset();
self.ints.reset();
self.strs.reset();
self.maps_int_int.reset();
self.maps_int_float.reset();
self.maps_int_str.reset();
self.maps_str_int.reset();
self.maps_str_float.reset();
self.maps_str_str.reset();
self.iters_int.reset();
self.iters_str.reset();
}
}