interpreter: strings & regexes support

This commit is contained in:
Guillem L. Jara
2026-05-25 14:07:45 +02:00
parent d30e5d33fc
commit 5b69891bcf
3 changed files with 84 additions and 44 deletions
+40 -3
View File
@@ -3,7 +3,7 @@
// For the full copyright and license information, please view the LICENSE
// files that was distributed with this source code.
use std::mem::forget;
use std::{borrow::Cow, mem::forget};
use bumpalo::{Bump, collections::Vec};
use parser::{
@@ -38,7 +38,7 @@ pub enum ValueContext {
Array,
}
impl Code<'_> {
impl<'a> Code<'a> {
fn lower_body(&mut self, body: &Body) {
for stmnt in &body.0 {
self.lower_statement(stmnt);
@@ -123,6 +123,43 @@ impl Code<'_> {
));
return Hint::UnboxedFloat64;
}
atom @ (Atom::String(s) | Atom::TypedRegex(s)) => {
let val = if matches!(atom, Atom::String(_)) {
Value::String
} else {
Value::Regex
};
let src = self.register_const(val(Cow::Borrowed(
self.arena.alloc_slice_copy(s.as_ref()),
)));
self.bc.emit(Instruction::load_store(
OpCode::LoadConst,
dest,
src,
ValueContext::Scalar,
));
}
Atom::Regex(r) => {
let src = self.register_const(Value::Regex(Cow::Borrowed(
self.arena.alloc_slice_copy(r.as_ref()),
)));
self.bc.emit(Instruction::load_store(
OpCode::LoadConst,
dest,
src,
ValueContext::Scalar,
));
let dest = LinearReg(dest, Hint::None);
let rec = self.lower_expr(&Expr::Leaf(Atom::Number(0.)), ctx);
self.bc.emit(Instruction::binary(
OpCode::Matches,
dest.reg(),
&rec,
&dest,
));
forget(dest);
self.free_reg(rec);
}
_ => todo!(),
},
Expr::Node(node) => match node.as_ref() {
@@ -190,7 +227,7 @@ impl Code<'_> {
self.free_regs.push(reg.into_inner());
}
fn register_const(&mut self, value: Value) -> NonLocal {
fn register_const(&mut self, value: Value<'a>) -> NonLocal {
NonLocal(self.consts.0.insert_full(value).0 as u16)
}
+15 -13
View File
@@ -1,4 +1,5 @@
use std::{
borrow::Cow,
hash::Hash,
mem::discriminant,
ops::{Add, Div, Mul, Sub},
@@ -8,16 +9,17 @@ use ahash::RandomState;
use hashbrown::HashMap;
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
pub enum Value<'a> {
Float(f64),
String(Vec<u8>),
String(Cow<'a, [u8]>),
Regex(Cow<'a, [u8]>),
Array(HashMap<String, Self, RandomState>),
Bool(bool),
Untyped,
Unassigned,
}
impl Value {
impl Value<'_> {
/// Called when loading a variable's value. Forces subsequent uses to be
/// typed as an AWK scalar (anything that's not an array, basically).
pub fn scalar_context(&mut self) -> &mut Self {
@@ -62,32 +64,32 @@ impl Value {
}
}
impl Add for &'_ Value {
type Output = Value;
impl<'a> Add for &'_ Value<'a> {
type Output = Value<'a>;
fn add(self, rhs: Self) -> Self::Output {
Value::Float(self.to_num() + rhs.to_num())
}
}
impl Sub for &'_ Value {
type Output = Value;
impl<'a> Sub for &'_ Value<'a> {
type Output = Value<'a>;
fn sub(self, rhs: Self) -> Self::Output {
Value::Float(self.to_num() - rhs.to_num())
}
}
impl Mul for &'_ Value {
type Output = Value;
impl<'a> Mul for &'_ Value<'a> {
type Output = Value<'a>;
fn mul(self, rhs: Self) -> Self::Output {
Value::Float(self.to_num() * rhs.to_num())
}
}
impl Div for &'_ Value {
type Output = Value;
impl<'a> Div for &'_ Value<'a> {
type Output = Value<'a>;
fn div(self, rhs: Self) -> Self::Output {
// TODO: panic "nicely" on div by zero.
@@ -95,8 +97,8 @@ impl Div for &'_ Value {
}
}
impl Eq for Value {}
impl Hash for Value {
impl Eq for Value<'_> {}
impl Hash for Value<'_> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
discriminant(self).hash(state);
match self {
+29 -28
View File
@@ -36,18 +36,18 @@ pub struct Interpreter<'a> {
}
#[derive(Debug)]
pub struct Registers<'a>(Vec<'a, Value>);
pub struct Registers<'a>(Vec<'a, Value<'a>>);
#[derive(Debug)]
pub struct SymbolTable<'a> {
user: IndexMap<Identifier<'a>, Value, RandomState, &'a Bump>,
user: IndexMap<Identifier<'a>, Value<'a>, RandomState, &'a Bump>,
// separate table for cheap invalidation. It's an arena _visibly shrugs_.
records: HashMap<usize, Value, RandomState, &'a Bump>,
records: HashMap<usize, Value<'a>, RandomState, &'a Bump>,
// etc
}
#[derive(Debug)]
pub struct Consts<'a>(pub IndexSet<Value, RandomState, &'a Bump>);
pub struct Consts<'a>(pub IndexSet<Value<'a>, RandomState, &'a Bump>);
impl<'a> Interpreter<'a> {
pub fn new(compat: ExecMode, code: Code<'a>) -> Self {
@@ -71,7 +71,7 @@ impl<'a> SymbolTable<'a> {
}
}
fn lookup_user_var(&mut self, var: NonLocal, ctx: ValueContext) -> &Value {
fn lookup_user_var(&mut self, var: NonLocal, ctx: ValueContext) -> &Value<'a> {
let v = self.user.get_index_mut(var.0 as _).unwrap().1;
match ctx {
ValueContext::Untyped => v,
@@ -80,7 +80,7 @@ impl<'a> SymbolTable<'a> {
}
}
fn write_user_val(&mut self, var: NonLocal, value: Value) {
fn write_user_val(&mut self, var: NonLocal, value: Value<'a>) {
*self.user.get_index_mut(var.0 as _).unwrap().1 = value;
}
@@ -109,30 +109,31 @@ impl Interpreter<'_> {
match instr {
// ix if let Some(&(dest, src)) = ix.get_unary() => {}
ix if let Some(&(dest, lhs, rhs)) = ix.get_binary() => {
let lhs = self.registers.get(lhs);
let rhs = self.registers.get(rhs);
let val = match ix.opcode {
OpCode::Add => lhs + rhs,
OpCode::Subtract => lhs - rhs,
OpCode::Multiply => lhs * rhs,
OpCode::Divide => lhs / rhs,
_ => todo!(),
let val = {
let lhs = self.registers.get(lhs);
let rhs = self.registers.get(rhs);
match ix.opcode {
OpCode::Add => lhs + rhs,
OpCode::Subtract => lhs - rhs,
OpCode::Multiply => lhs * rhs,
OpCode::Divide => lhs / rhs,
_ => todo!(),
}
};
self.registers.write(dest, val);
}
ix if let Some(&(dest, src)) = ix.get_load_store() => match ix.opcode {
OpCode::LoadConst => self
.registers
.write(dest, self.consts.0.get_index(src.0 as _).unwrap().clone()),
OpCode::LoadConst => {
let val = self.consts.0.get_index(src.0 as _).unwrap().clone();
self.registers.write(dest, val);
}
OpCode::LoadUser => {
self.registers.write(
dest,
self.symbols.lookup_user_var(src, ix.hint.into()).clone(),
);
let val = self.symbols.lookup_user_var(src, ix.hint.into()).clone();
self.registers.write(dest, val);
}
OpCode::StoreUser => {
self.symbols
.write_user_val(src, self.registers.get(dest).clone());
let val = self.registers.get(dest).clone();
self.symbols.write_user_val(src, val);
}
_ => todo!(),
},
@@ -157,18 +158,18 @@ impl Interpreter<'_> {
}
}
impl Registers<'_> {
fn replace(&mut self, src: Reg, f: impl FnOnce(Value) -> Value) {
impl<'a> Registers<'a> {
fn replace(&mut self, src: Reg, f: impl FnOnce(Value<'a>) -> Value<'a>) {
let val = replace(self.get_mut(src), Value::Untyped);
self.write(src, f(val));
}
fn get(&self, src: Reg) -> &Value {
fn get(&self, src: Reg) -> &Value<'a> {
&self.0[src.0 as usize]
}
fn get_mut(&mut self, src: Reg) -> &mut Value {
fn get_mut(&mut self, src: Reg) -> &mut Value<'a> {
&mut self.0[src.0 as usize]
}
fn write(&mut self, dest: Reg, src: Value) {
fn write(&mut self, dest: Reg, src: Value<'a>) {
self.0[dest.0 as usize] = src;
}
}