diff --git a/Cargo.lock b/Cargo.lock index c8c8e330..6a1c06ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1100,6 +1100,29 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "flatbuffers" +version = "24.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8add37afff2d4ffa83bc748a70b4b1370984f6980768554182424ef71447c35f" +dependencies = [ + "bitflags 1.3.2", + "rustc_version", +] + +[[package]] +name = "flexbuffers" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15d14128f06405808ce75bfebe11e9b0f9da18719ede6d7bdb1702d6bfe0f7e8" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "num_enum", + "serde", + "serde_derive", +] + [[package]] name = "flume" version = "0.11.0" @@ -1774,6 +1797,7 @@ dependencies = [ "clap_derive", "color-eyre", "eyre", + "moor-compiler", "moor-values", "owo-colors", "rpc-common", @@ -1934,6 +1958,7 @@ dependencies = [ "escargot", "eyre", "futures-util", + "moor-compiler", "moor-moot", "moor-values", "rpc-async-client", @@ -1959,6 +1984,8 @@ dependencies = [ "daumtils", "decorum", "enum-primitive-derive", + "flatbuffers", + "flexbuffers", "im", "itertools 0.13.0", "lazy_static", @@ -2059,6 +2086,27 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "object" version = "0.32.2" @@ -2382,6 +2430,16 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + [[package]] name = "proc-macro2" version = "1.0.86" @@ -3366,7 +3424,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.22.17", ] [[package]] @@ -3378,6 +3436,17 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow 0.5.40", +] + [[package]] name = "toml_edit" version = "0.22.17" @@ -3388,7 +3457,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.16", ] [[package]] @@ -3946,6 +4015,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "winnow" version = "0.6.16" diff --git a/Cargo.toml b/Cargo.toml index 288acdc4..e48d7fb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -101,6 +101,8 @@ daumtils = { git = "https://github.com/rdaum/daumtils.git", version = "0.2.0" } decorum = "0.3" # For ordering & comparing our floats encoding_rs = "0.8.34" enum-primitive-derive = "0.3" +flatbuffers = "24.3.25" +flexbuffers = "2.0.0" im = "15.1" inventory = "0.3.15" itertools = "0.13.0" diff --git a/crates/compiler/src/ast.rs b/crates/compiler/src/ast.rs index c0382aa1..1c143339 100644 --- a/crates/compiler/src/ast.rs +++ b/crates/compiler/src/ast.rs @@ -12,10 +12,10 @@ // this program. If not, see . // -use moor_values::var::Symbol; +use moor_values::Symbol; use std::fmt::Display; -use moor_values::var::Var; +use moor_values::Var; /// The abstract syntax tree produced by the parser and converted by codegen into opcodes. use crate::names::UnboundName; diff --git a/crates/compiler/src/builtins.rs b/crates/compiler/src/builtins.rs index 289866c7..4d71c6d5 100644 --- a/crates/compiler/src/builtins.rs +++ b/crates/compiler/src/builtins.rs @@ -14,9 +14,9 @@ use bincode::{Decode, Encode}; use lazy_static::lazy_static; -use moor_values::var::Symbol; -use moor_values::var::VarType; -use moor_values::var::VarType::TYPE_MAP; +use moor_values::Symbol; +use moor_values::VarType; +use moor_values::VarType::TYPE_MAP; /// Global registry of built-in function names. use std::collections::HashMap; use ArgCount::{Q, U}; diff --git a/crates/compiler/src/codegen.rs b/crates/compiler/src/codegen.rs index 6d3c3776..7f22129a 100644 --- a/crates/compiler/src/codegen.rs +++ b/crates/compiler/src/codegen.rs @@ -18,8 +18,8 @@ use std::sync::Arc; use tracing::error; -use moor_values::var::Var; -use moor_values::var::Variant; +use moor_values::Var; +use moor_values::Variant; use crate::ast::{ Arg, BinaryOp, CatchCodes, Expr, ScatterItem, ScatterKind, Stmt, StmtNode, UnaryOp, diff --git a/crates/compiler/src/codegen_tests.rs b/crates/compiler/src/codegen_tests.rs index 413dfa88..ceda89aa 100644 --- a/crates/compiler/src/codegen_tests.rs +++ b/crates/compiler/src/codegen_tests.rs @@ -21,9 +21,9 @@ mod tests { use crate::opcode::{ScatterArgs, ScatterLabel}; use crate::CompileOptions; use moor_values::model::CompileError; - use moor_values::var::Error::{E_INVARG, E_INVIND, E_PERM, E_PROPNF, E_RANGE}; - use moor_values::var::Objid; - use moor_values::var::Symbol; + use moor_values::Error::{E_INVARG, E_INVIND, E_PERM, E_PROPNF, E_RANGE}; + use moor_values::Objid; + use moor_values::Symbol; use moor_values::SYSTEM_OBJECT; #[test] diff --git a/crates/compiler/src/decompile.rs b/crates/compiler/src/decompile.rs index b6c9e293..e7c712b8 100644 --- a/crates/compiler/src/decompile.rs +++ b/crates/compiler/src/decompile.rs @@ -12,8 +12,8 @@ // this program. If not, see . // -use moor_values::var::{v_err, v_int, v_none, v_objid, Var}; -use moor_values::var::{v_float, Variant}; +use moor_values::{v_err, v_int, v_none, v_objid, Var}; +use moor_values::{v_float, Variant}; use std::collections::{HashMap, VecDeque}; use crate::ast::{ diff --git a/crates/compiler/src/lib.rs b/crates/compiler/src/lib.rs index c0aa8f3c..deea43cd 100644 --- a/crates/compiler/src/lib.rs +++ b/crates/compiler/src/lib.rs @@ -37,7 +37,7 @@ pub use crate::names::{Name, UnboundNames}; pub use crate::opcode::{Op, ScatterLabel}; pub use crate::parse::CompileOptions; pub use crate::program::{Program, EMPTY_PROGRAM}; -pub use crate::unparse::unparse; +pub use crate::unparse::{to_literal, unparse}; #[macro_use] extern crate pest_derive; diff --git a/crates/compiler/src/names.rs b/crates/compiler/src/names.rs index 4fd498b3..7bdd0d76 100644 --- a/crates/compiler/src/names.rs +++ b/crates/compiler/src/names.rs @@ -15,7 +15,7 @@ use crate::GlobalName; use bincode::{Decode, Encode}; use moor_values::model::CompileError; -use moor_values::var::Symbol; +use moor_values::Symbol; use std::collections::HashMap; use strum::IntoEnumIterator; diff --git a/crates/compiler/src/opcode.rs b/crates/compiler/src/opcode.rs index 8aa98e2f..68c67f31 100644 --- a/crates/compiler/src/opcode.rs +++ b/crates/compiler/src/opcode.rs @@ -16,8 +16,8 @@ use crate::builtins::BuiltinId; use crate::labels::{Label, Offset}; use crate::names::Name; use bincode::{Decode, Encode}; -use moor_values::var::Error; -use moor_values::var::Objid; +use moor_values::Error; +use moor_values::Objid; #[derive(Clone, Debug, PartialEq, PartialOrd, Encode, Decode)] pub enum Op { diff --git a/crates/compiler/src/parse.rs b/crates/compiler/src/parse.rs index b004b83a..23b76c87 100644 --- a/crates/compiler/src/parse.rs +++ b/crates/compiler/src/parse.rs @@ -19,18 +19,18 @@ use std::collections::HashMap; use std::rc::Rc; use std::str::FromStr; -use moor_values::var::{v_none, Symbol}; use moor_values::SYSTEM_OBJECT; +use moor_values::{v_none, Symbol}; use pest::pratt_parser::{Assoc, Op, PrattParser}; pub use pest::Parser as PestParser; use tracing::{instrument, warn}; -use moor_values::var::Error::{ +use moor_values::Error::{ E_ARGS, E_DIV, E_FLOAT, E_INVARG, E_INVIND, E_MAXREC, E_NACC, E_NONE, E_PERM, E_PROPNF, E_QUOTA, E_RANGE, E_RECMOVE, E_TYPE, E_VARNF, E_VERBNF, }; -use moor_values::var::Objid; -use moor_values::var::{v_err, v_float, v_int, v_objid, v_str, v_string}; +use moor_values::Objid; +use moor_values::{v_err, v_float, v_int, v_objid, v_str, v_string}; use crate::ast::Arg::{Normal, Splice}; use crate::ast::StmtNode::Scope; @@ -1189,9 +1189,9 @@ pub fn unquote_str(s: &str) -> Result { #[cfg(test)] mod tests { - use moor_values::var::Error::{E_INVARG, E_PROPNF, E_VARNF}; - use moor_values::var::{v_err, v_float, v_int, v_obj, v_str}; - use moor_values::var::{v_none, Symbol}; + use moor_values::Error::{E_INVARG, E_PROPNF, E_VARNF}; + use moor_values::{v_err, v_float, v_int, v_obj, v_str}; + use moor_values::{v_none, Symbol}; use crate::ast::Arg::{Normal, Splice}; use crate::ast::Expr::{Call, Id, Prop, Value, Verb}; diff --git a/crates/compiler/src/program.rs b/crates/compiler/src/program.rs index 34247093..c23e0ba9 100644 --- a/crates/compiler/src/program.rs +++ b/crates/compiler/src/program.rs @@ -15,10 +15,11 @@ use crate::labels::{JumpLabel, Label}; use crate::names::{Name, Names}; use crate::opcode::Op; +use crate::unparse::to_literal; use bincode::{Decode, Encode}; use bytes::Bytes; use lazy_static::lazy_static; -use moor_values::var::Var; +use moor_values::Var; use moor_values::{AsByteBuffer, CountingWriter, DecodingError, EncodingError, BINCODE_CONFIG}; use std::fmt::{Display, Formatter}; use std::sync::Arc; @@ -84,7 +85,7 @@ impl Display for Program { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { // Write literals indexed by their offset # for (i, l) in self.literals.iter().enumerate() { - writeln!(f, "L{}: {}", i, l.to_literal())?; + writeln!(f, "L{}: {}", i, to_literal(l))?; } // Write jump labels indexed by their offset & showing position & optional name diff --git a/crates/compiler/src/unparse.rs b/crates/compiler/src/unparse.rs index 74e04505..ae789e50 100644 --- a/crates/compiler/src/unparse.rs +++ b/crates/compiler/src/unparse.rs @@ -13,7 +13,7 @@ // use moor_values::util::quote_str; -use moor_values::var::Variant; +use moor_values::{Var, Variant}; use crate::ast::{Expr, Stmt, StmtNode}; use crate::decompile::DecompileError; @@ -109,13 +109,13 @@ impl<'a> Unparse<'a> { } } - fn unparse_var(&self, var: &moor_values::var::Var, aggressive: bool) -> String { + fn unparse_var(&self, var: &moor_values::Var, aggressive: bool) -> String { if !aggressive { - return format!("{var}"); + return to_literal(var); } if let Variant::Str(s) = var.variant() { - let s = s.as_str(); + let s = s.as_string(); // If the string contains anything that isn't alphanumeric and _, it's // not a valid ident and needs to be quoted. Likewise if it begins with a non-alpha/underscore @@ -123,12 +123,12 @@ impl<'a> Unparse<'a> { || (s.chars().next().unwrap().is_numeric() && !s.starts_with('_')); if !needs_quotes { - s.into() + s } else { - format!("({})", quote_str(s)) + format!("({})", quote_str(&s)) } } else { - format!("{var}") + to_literal(var) } } @@ -190,7 +190,7 @@ impl<'a> Unparse<'a> { Expr::Unary(op, expr) => Ok(format!("{}{}", op, brace_if_lower(expr))), Expr::Prop { location, property } => { let location = match (&**location, &**property) { - (Expr::Value(var), Expr::Value(_)) if var.is_root() => String::from("$"), + (Expr::Value(var), Expr::Value(_)) if var.is_sysobj() => String::from("$"), _ => format!("{}.", brace_if_lower(location)), }; let prop = match &**property { @@ -205,7 +205,7 @@ impl<'a> Unparse<'a> { args, } => { let location = match (&**location, &**verb) { - (Expr::Value(var), Expr::Value(_)) if var.is_root() => String::from("$"), + (Expr::Value(var), Expr::Value(_)) if var.is_sysobj() => String::from("$"), _ => format!("{}:", brace_if_lower(location)), }; let verb = match &**verb { @@ -727,6 +727,50 @@ pub fn annotate_line_numbers(start_line_no: usize, tree: &mut [Stmt]) -> usize { line_no } +/// Utility function to produce a MOO literal from a Var/Variant. +/// This is kept in `compiler` and not in `values` because it's specific to the MOO language, and +/// other languages could have different representations. +pub fn to_literal(v: &Var) -> String { + match v.variant() { + Variant::None => "None".to_string(), + Variant::Obj(oid) => { + format!("{}", oid) + } + Variant::Int(i) => i.to_string(), + Variant::Float(f) => { + format!("{f:?}") + } + Variant::List(l) => { + let mut result = String::new(); + result.push('{'); + for (i, v) in l.iter().enumerate() { + if i > 0 { + result.push_str(", "); + } + result.push_str(to_literal(&v).as_str()); + } + result.push('}'); + result + } + Variant::Str(s) => quote_str(&s.as_string()), + Variant::Map(m) => { + let mut result = String::new(); + result.push('['); + for (i, (k, v)) in m.iter().enumerate() { + if i > 0 { + result.push_str(", "); + } + result.push_str(to_literal(&k).as_str()); + result.push_str(" -> "); + result.push_str(to_literal(&v).as_str()); + } + result.push(']'); + result + } + Variant::Err(e) => e.name().to_string(), + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/console-host/Cargo.toml b/crates/console-host/Cargo.toml index ecaa5482..005a57a2 100644 --- a/crates/console-host/Cargo.toml +++ b/crates/console-host/Cargo.toml @@ -12,6 +12,7 @@ rust-version.workspace = true description = "A tool to connect to a local or remote moor daemon and interact with it via the TTY." [dependencies] +moor-compiler = { path = "../compiler" } moor-values = { path = "../values" } rpc-common = { path = "../rpc-common" } rpc-sync-client = { path = "../rpc-sync-client" } diff --git a/crates/console-host/src/main.rs b/crates/console-host/src/main.rs index e2a7f9ad..7bcfefe2 100644 --- a/crates/console-host/src/main.rs +++ b/crates/console-host/src/main.rs @@ -20,19 +20,19 @@ use std::time::SystemTime; use clap::Parser; use clap_derive::Parser; use color_eyre::owo_colors::OwoColorize; -use moor_values::var::Objid; -use rustyline::config::Configurer; -use rustyline::error::ReadlineError; -use rustyline::{ColorMode, DefaultEditor, ExternalPrinter}; -use tracing::{debug, error, info, trace, warn}; -use uuid::Uuid; - +use moor_compiler::to_literal; +use moor_values::Objid; use rpc_common::{ AuthToken, BroadcastEvent, ClientToken, ConnectionEvent, RpcRequest, RpcResponse, RpcResult, BROADCAST_TOPIC, }; use rpc_sync_client::RpcSendClient; use rpc_sync_client::{broadcast_recv, events_recv}; +use rustyline::config::Configurer; +use rustyline::error::ReadlineError; +use rustyline::{ColorMode, DefaultEditor, ExternalPrinter}; +use tracing::{debug, error, info, trace, warn}; +use uuid::Uuid; #[derive(Parser, Debug)] struct Args { @@ -249,14 +249,18 @@ fn console_loop( } match events_recv(client_id, &narr_sub_socket) { Ok(ConnectionEvent::Narrative(_, msg)) => { - printer - .print( - (match msg.event() { - moor_values::tasks::Event::Notify(s, _content_type) => s, - }) - .to_string(), - ) - .unwrap(); + let var = match msg.event() { + moor_values::tasks::Event::Notify(s, _content_type) => s, + }; + match var.variant() { + moor_values::Variant::Str(s) => { + printer.print(s.as_string().to_string()).unwrap(); + } + _ => { + let literal = to_literal(&var); + printer.print(format!("{}", literal.yellow())).unwrap(); + } + } } Ok(ConnectionEvent::SystemMessage(o, msg)) => { printer diff --git a/crates/daemon/src/connections.rs b/crates/daemon/src/connections.rs index d6323b30..07cc23f1 100644 --- a/crates/daemon/src/connections.rs +++ b/crates/daemon/src/connections.rs @@ -17,7 +17,7 @@ use std::time::{Duration, SystemTime}; use uuid::Uuid; use moor_kernel::tasks::sessions::SessionError; -use moor_values::var::Objid; +use moor_values::Objid; use rpc_common::RpcRequestError; pub const CONNECTION_TIMEOUT_DURATION: Duration = Duration::from_secs(30); diff --git a/crates/daemon/src/connections_rb.rs b/crates/daemon/src/connections_rb.rs index a9bd0596..bcf1367c 100644 --- a/crates/daemon/src/connections_rb.rs +++ b/crates/daemon/src/connections_rb.rs @@ -27,8 +27,8 @@ use uuid::Uuid; use bytes::Bytes; use daumtils::SliceRef; use moor_kernel::tasks::sessions::SessionError; -use moor_values::var::Objid; use moor_values::AsByteBuffer; +use moor_values::Objid; use relbox::{relation_info_for, RelBox, RelationId, RelationInfo, Transaction}; use rpc_common::RpcRequestError; @@ -444,7 +444,7 @@ impl ConnectionsDB for ConnectionsRb { mod tests { use std::sync::Arc; - use moor_values::var::Objid; + use moor_values::Objid; use crate::connections::ConnectionsDB; use crate::connections_rb::ConnectionsRb; diff --git a/crates/daemon/src/connections_wt.rs b/crates/daemon/src/connections_wt.rs index b05e056c..f697191e 100644 --- a/crates/daemon/src/connections_wt.rs +++ b/crates/daemon/src/connections_wt.rs @@ -31,7 +31,7 @@ use bytes::Bytes; use moor_db_wiredtiger::{WiredTigerRelDb, WiredTigerRelTransaction, WiredTigerRelation}; use moor_kernel::tasks::sessions::SessionError; use moor_values::model::{CommitResult, ValSet}; -use moor_values::var::Objid; +use moor_values::Objid; use moor_values::{AsByteBuffer, DecodingError, EncodingError}; use rpc_common::RpcRequestError; @@ -484,7 +484,7 @@ impl ConnectionsDB for ConnectionsWT { mod tests { use std::sync::Arc; - use moor_values::var::Objid; + use moor_values::Objid; use crate::connections::ConnectionsDB; use crate::connections_wt::ConnectionsWT; diff --git a/crates/daemon/src/rpc_server.rs b/crates/daemon/src/rpc_server.rs index e335b1d8..1d3382f3 100644 --- a/crates/daemon/src/rpc_server.rs +++ b/crates/daemon/src/rpc_server.rs @@ -31,11 +31,11 @@ use moor_kernel::SchedulerClient; use moor_values::tasks::SchedulerError::CommandExecutionError; use moor_values::tasks::{CommandError, NarrativeEvent, TaskId}; use moor_values::util::parse_into_words; -use moor_values::var::Objid; -use moor_values::var::Symbol; -use moor_values::var::Variant; -use moor_values::var::{v_objid, v_string}; +use moor_values::Objid; +use moor_values::Symbol; +use moor_values::Variant; use moor_values::SYSTEM_OBJECT; +use moor_values::{v_objid, v_string}; use rpc_common::RpcResponse::{LoginResult, NewConnection}; use rpc_common::{ AuthToken, BroadcastEvent, ClientToken, ConnectType, ConnectionEvent, RpcRequest, diff --git a/crates/daemon/src/rpc_session.rs b/crates/daemon/src/rpc_session.rs index 2029922f..4acd5eab 100644 --- a/crates/daemon/src/rpc_session.rs +++ b/crates/daemon/src/rpc_session.rs @@ -20,7 +20,7 @@ use uuid::Uuid; use moor_kernel::tasks::sessions::{Session, SessionError}; use moor_values::tasks::NarrativeEvent; -use moor_values::var::Objid; +use moor_values::Objid; use crate::rpc_server::RpcServer; diff --git a/crates/db-wiredtiger/src/wtrel/rel_transaction.rs b/crates/db-wiredtiger/src/wtrel/rel_transaction.rs index e08ce568..e2f94fc3 100644 --- a/crates/db-wiredtiger/src/wtrel/rel_transaction.rs +++ b/crates/db-wiredtiger/src/wtrel/rel_transaction.rs @@ -582,7 +582,7 @@ mod tests { use moor_db::RelationalTransaction; use moor_values::model::{ObjSet, ValSet}; - use moor_values::var::Objid; + use moor_values::Objid; use TestRelation::{CompositeToOne, OneToOne, OneToOneSecondaryIndexed, Sequences}; use crate::wtrel::rel_db::WiredTigerRelDb; diff --git a/crates/db-wiredtiger/third-party/wiredtiger b/crates/db-wiredtiger/third-party/wiredtiger index 4b37cbe4..f8a6f387 160000 --- a/crates/db-wiredtiger/third-party/wiredtiger +++ b/crates/db-wiredtiger/third-party/wiredtiger @@ -1 +1 @@ -Subproject commit 4b37cbe4e3992389e0bfb903ba8ca88791f6e4ca +Subproject commit f8a6f387e6753237f69463af8d2b7e66bf5aecc9 diff --git a/crates/db/src/db_loader_client.rs b/crates/db/src/db_loader_client.rs index bc480a81..fcec48cd 100644 --- a/crates/db/src/db_loader_client.rs +++ b/crates/db/src/db_loader_client.rs @@ -25,9 +25,9 @@ use moor_values::model::{CommitResult, WorldStateError}; use moor_values::model::{HasUuid, PropPerms, ValSet}; use moor_values::model::{PropDef, PropDefs}; use moor_values::util::BitEnum; -use moor_values::var::Objid; -use moor_values::var::Symbol; -use moor_values::var::Var; +use moor_values::Objid; +use moor_values::Symbol; +use moor_values::Var; use crate::db_worldstate::DbTxWorldState; use crate::loader::LoaderInterface; diff --git a/crates/db/src/db_worldstate.rs b/crates/db/src/db_worldstate.rs index ca523921..256400d9 100644 --- a/crates/db/src/db_worldstate.rs +++ b/crates/db/src/db_worldstate.rs @@ -29,11 +29,11 @@ use moor_values::model::{PropAttrs, PropFlag}; use moor_values::model::{PropDef, PropDefs}; use moor_values::model::{VerbDef, VerbDefs}; use moor_values::util::BitEnum; -use moor_values::var::Symbol; -use moor_values::var::Variant; -use moor_values::var::{v_int, v_objid, Var}; -use moor_values::var::{v_listv, Objid}; +use moor_values::Objid; +use moor_values::Variant; use moor_values::NOTHING; +use moor_values::{v_int, v_objid, Var}; +use moor_values::{v_list, Symbol}; use crate::worldstate_transaction::WorldStateTransaction; @@ -234,7 +234,7 @@ impl WorldState for DbTxWorldState { return self.location_of(perms, obj).map(Var::from); } else if pname == *CONTENTS_SYM { let contents: Vec<_> = self.contents_of(perms, obj)?.iter().map(v_objid).collect(); - return Ok(v_listv(contents)); + return Ok(v_list(&contents)); } else if pname == *OWNER_SYM { return self.owner_of(obj).map(Var::from); } else if pname == *PROGRAMMER_SYM { @@ -355,7 +355,7 @@ impl WorldState for DbTxWorldState { let Variant::Str(name) = value.variant() else { return Err(WorldStateError::PropertyTypeMismatch); }; - self.tx.set_object_name(obj, name.to_string())?; + self.tx.set_object_name(obj, name.as_string())?; return Ok(()); } @@ -750,7 +750,13 @@ impl WorldState for DbTxWorldState { // Then grab aliases property. let aliases = match self.retrieve_property(perms, obj, *ALIASES_SYM) { Ok(a) => match a.variant() { - Variant::List(a) => a.iter().map(|v| v.to_string()).collect(), + Variant::List(a) => a + .iter() + .map(|v| match v.variant() { + Variant::Str(s) => s.as_string(), + _ => "".to_string(), + }) + .collect(), _ => { vec![] } diff --git a/crates/db/src/loader.rs b/crates/db/src/loader.rs index 3cdee37a..d5d459da 100644 --- a/crates/db/src/loader.rs +++ b/crates/db/src/loader.rs @@ -24,8 +24,8 @@ use moor_values::model::{CommitResult, WorldStateError}; use moor_values::model::{ObjAttrs, PropPerms}; use moor_values::model::{PropDef, PropDefs}; use moor_values::util::BitEnum; -use moor_values::var::Objid; -use moor_values::var::Var; +use moor_values::Objid; +use moor_values::Var; /// Interface exposed to be used by the textdump loader. Overlap of functionality with what /// WorldState could provide, but potentially different constraints/semantics (e.g. no perms checks) diff --git a/crates/db/src/relational_worldstate.rs b/crates/db/src/relational_worldstate.rs index 234a9c83..9d830dcb 100644 --- a/crates/db/src/relational_worldstate.rs +++ b/crates/db/src/relational_worldstate.rs @@ -24,9 +24,9 @@ use moor_values::model::{ WorldStateError, }; use moor_values::util::BitEnum; -use moor_values::var::Symbol; -use moor_values::var::{v_none, Objid, Var}; +use moor_values::Symbol; use moor_values::NOTHING; +use moor_values::{v_none, Objid, Var}; use std::collections::{HashMap, HashSet, VecDeque}; use uuid::Uuid; diff --git a/crates/db/src/worldstate_tests.rs b/crates/db/src/worldstate_tests.rs index a23947e5..6536f28e 100644 --- a/crates/db/src/worldstate_tests.rs +++ b/crates/db/src/worldstate_tests.rs @@ -23,10 +23,10 @@ use moor_values::model::{CommitResult, WorldStateError}; use moor_values::model::{HasUuid, Named}; use moor_values::model::{ObjAttrs, PropFlag, ValSet}; use moor_values::util::BitEnum; -use moor_values::var::Objid; -use moor_values::var::Symbol; -use moor_values::var::{v_int, v_str}; +use moor_values::Objid; +use moor_values::Symbol; use moor_values::NOTHING; +use moor_values::{v_int, v_str}; pub fn perform_test_create_object(begin_tx: F) where diff --git a/crates/db/src/worldstate_transaction.rs b/crates/db/src/worldstate_transaction.rs index ebb5f910..15120711 100644 --- a/crates/db/src/worldstate_transaction.rs +++ b/crates/db/src/worldstate_transaction.rs @@ -24,9 +24,9 @@ use moor_values::model::{ObjSet, PropPerms}; use moor_values::model::{PropDef, PropDefs}; use moor_values::model::{VerbDef, VerbDefs}; use moor_values::util::BitEnum; -use moor_values::var::Objid; -use moor_values::var::Symbol; -use moor_values::var::Var; +use moor_values::Objid; +use moor_values::Symbol; +use moor_values::Var; /// A trait defining a generic interface to a database for storing the per-attribute values /// of our objects and their properties and verbs. Used by DbTxWorldState. diff --git a/crates/kernel/benches/vm_benches.rs b/crates/kernel/benches/vm_benches.rs index 0d223a7b..a91a306d 100644 --- a/crates/kernel/benches/vm_benches.rs +++ b/crates/kernel/benches/vm_benches.rs @@ -35,8 +35,8 @@ use moor_values::model::{BinaryType, VerbFlag}; use moor_values::model::{WorldState, WorldStateSource}; use moor_values::tasks::AbortLimitReason; use moor_values::util::BitEnum; -use moor_values::var::{List, Symbol}; -use moor_values::{AsByteBuffer, NOTHING, SYSTEM_OBJECT}; +use moor_values::{AsByteBuffer, Var, NOTHING, SYSTEM_OBJECT}; +use moor_values::Symbol; fn create_worldstate() -> WiredTigerDB { let (ws_source, _) = WiredTigerDB::open(None); @@ -51,7 +51,7 @@ fn create_worldstate() -> WiredTigerDB { pub fn prepare_call_verb( world_state: &mut dyn WorldState, verb_name: &str, - args: List, + args: Vec, max_ticks: usize, ) -> VmHost { let mut vm_host = VmHost::new(0, 20, max_ticks, Duration::from_secs(15)); @@ -95,7 +95,7 @@ fn prepare_vm_execution( BinaryType::LambdaMoo18X, ) .unwrap(); - let vm_host = prepare_call_verb(tx.as_mut(), "test", List::new(), max_ticks); + let vm_host = prepare_call_verb(tx.as_mut(), "test", vec![], max_ticks); assert_eq!(tx.commit().unwrap(), CommitResult::Success); vm_host } diff --git a/crates/kernel/src/builtins/bf_list_sets.rs b/crates/kernel/src/builtins/bf_list_sets.rs index 00bf9333..33a0901b 100644 --- a/crates/kernel/src/builtins/bf_list_sets.rs +++ b/crates/kernel/src/builtins/bf_list_sets.rs @@ -15,16 +15,16 @@ use std::ops::BitOr; use moor_compiler::offset_for_builtin; -use moor_values::var::Error::{E_ARGS, E_INVARG, E_TYPE}; -use moor_values::var::Variant; -use moor_values::var::{v_empty_list, v_int, v_list, v_string}; -use moor_values::var::{v_listv, Error}; +use moor_values::Error::{E_ARGS, E_INVARG, E_TYPE}; +use moor_values::{ + v_empty_list, v_int, v_list, v_list_iter, v_string, IndexMode, Sequence, VarType, +}; +use moor_values::{Error, Variant}; use onig::{Region, SearchOptions, SyntaxOperator}; use crate::bf_declare; use crate::builtins::BfRet::Ret; use crate::builtins::{BfCallState, BfErr, BfRet, BuiltinFunction}; -use crate::vm::moo_execute::one_to_zero_index; fn bf_is_member(bf_args: &mut BfCallState<'_>) -> Result { if bf_args.args.len() != 2 { @@ -35,7 +35,7 @@ fn bf_is_member(bf_args: &mut BfCallState<'_>) -> Result { // is not *really* a correct place for it, but `bf_list_sets_and_maps_too_i_guess.rs` is a bit silly. match container.variant() { Variant::List(list) => { - if list.contains_case_sensitive(value) { + if list.index_in(value, true).map_err(BfErr::Code)?.is_some() { Ok(Ret(v_int(1))) } else { Ok(Ret(v_int(0))) @@ -43,7 +43,7 @@ fn bf_is_member(bf_args: &mut BfCallState<'_>) -> Result { } Variant::Map(map) => Ok(Ret(v_int( map.iter() - .position(|(_item_key, item_value)| value.eq_case_sensitive(item_value)) + .position(|(_item_key, item_value)| value.eq_case_sensitive(&item_value)) .map(|pos| pos + 1) .unwrap_or(0) as i64, ))), @@ -56,48 +56,38 @@ fn bf_listinsert(bf_args: &mut BfCallState<'_>) -> Result { if bf_args.args.len() < 2 || bf_args.args.len() > 3 { return Err(BfErr::Code(E_ARGS)); } - let len = bf_args.args.len(); - let value = bf_args.args[1].clone(); - if len == 2 { - let list = &mut bf_args.args[0]; - let Variant::List(list) = list.variant_mut() else { - return Err(BfErr::Code(E_TYPE)); - }; - Ok(Ret(list.push(value))) - } else { - let index = bf_args.args[2].clone(); - let list = &mut bf_args.args[0]; - let Variant::List(list) = list.variant_mut() else { - return Err(BfErr::Code(E_TYPE)); - }; - let index = match one_to_zero_index(&index) { - Ok(i) => i, - Err(e) => return Err(BfErr::Code(e)), - }; - Ok(Ret(list.insert(index as isize, value))) + let value = &bf_args.args[1]; + let list = &bf_args.args[0]; + if list.type_code() != VarType::TYPE_LIST { + return Err(BfErr::Code(E_TYPE)); + } + // If two args, treat as push. If three, treat as insert. + if bf_args.args.len() == 2 { + return Ok(Ret(list.push(value).map_err(BfErr::Code)?)); } + let index = &bf_args.args[2]; + let res = list.insert(index, value, IndexMode::OneBased); + Ok(Ret(res.map_err(BfErr::Code)?)) } + bf_declare!(listinsert, bf_listinsert); fn bf_listappend(bf_args: &mut BfCallState<'_>) -> Result { if bf_args.args.len() < 2 || bf_args.args.len() > 3 { return Err(BfErr::Code(E_ARGS)); } - let value = bf_args.args[1].clone(); - let list = &mut bf_args.args[0]; - let Variant::List(mut list) = list.variant().clone() else { + let value = &bf_args.args[1]; + let list = &bf_args.args[0]; + if list.type_code() != VarType::TYPE_LIST { return Err(BfErr::Code(E_TYPE)); - }; - let new_list = if bf_args.args.len() == 2 { - list.push(value.clone()) - } else { - let index = bf_args.args[2].variant(); - let Variant::Int(index) = index else { - return Err(BfErr::Code(E_TYPE)); - }; - list.insert(*index as isize, value.clone()) - }; - Ok(Ret(new_list)) + } + // If two args, treat as push. If three, treat as insert. + if bf_args.args.len() == 2 { + return Ok(Ret(list.push(value).map_err(BfErr::Code)?)); + } + let index = &bf_args.args[2]; + let res = list.insert(index, value, IndexMode::ZeroBased); + Ok(Ret(res.map_err(BfErr::Code)?)) } bf_declare!(listappend, bf_listappend); @@ -106,15 +96,10 @@ fn bf_listdelete(bf_args: &mut BfCallState<'_>) -> Result { return Err(BfErr::Code(E_ARGS)); } let index = bf_args.args[1].clone(); - let list = bf_args.args[0].variant_mut(); - let Variant::List(list) = list else { - return Err(BfErr::Code(E_TYPE)); - }; - let index = match one_to_zero_index(&index) { - Ok(i) => i, - Err(e) => return Err(BfErr::Code(e)), - }; - Ok(Ret(list.remove_at(index))) + let list = &bf_args.args[0]; + Ok(Ret(list + .remove_at(&index, IndexMode::OneBased) + .map_err(BfErr::Code)?)) } bf_declare!(listdelete, bf_listdelete); @@ -125,14 +110,12 @@ fn bf_listset(bf_args: &mut BfCallState<'_>) -> Result { let index = bf_args.args[2].clone(); let value = bf_args.args[1].clone(); let list = &mut bf_args.args[0]; - let Variant::List(ref mut list) = list.variant_mut() else { + if list.type_code() != VarType::TYPE_LIST { return Err(BfErr::Code(E_TYPE)); - }; - let index = match one_to_zero_index(&index) { - Ok(i) => i, - Err(e) => return Err(BfErr::Code(e)), - }; - Ok(Ret(list.set(index as usize, value.clone()))) + } + Ok(Ret(list + .index_set(&index, &value, IndexMode::OneBased) + .map_err(BfErr::Code)?)) } bf_declare!(listset, bf_listset); @@ -142,13 +125,10 @@ fn bf_setadd(bf_args: &mut BfCallState<'_>) -> Result { } let value = bf_args.args[1].clone(); let list = &mut bf_args.args[0]; - let Variant::List(ref mut list) = list.variant_mut() else { + let Variant::List(list) = list.variant() else { return Err(BfErr::Code(E_TYPE)); }; - if !list.contains(&value) { - return Ok(Ret(list.push(value.clone()))); - } - Ok(Ret(bf_args.args[0].clone())) + Ok(Ret(list.set_add(&value).map_err(BfErr::Code)?)) } bf_declare!(setadd, bf_setadd); @@ -157,11 +137,11 @@ fn bf_setremove(bf_args: &mut BfCallState<'_>) -> Result { return Err(BfErr::Code(E_ARGS)); } let value = bf_args.args[1].clone(); - let list = bf_args.args[0].variant_mut(); - let Variant::List(ref mut list) = list else { + let list = bf_args.args[0].variant(); + let Variant::List(list) = list else { return Err(BfErr::Code(E_TYPE)); }; - Ok(Ret(list.setremove(&value))) + Ok(Ret(list.set_remove(&value).map_err(BfErr::Code)?)) } bf_declare!(setremove, bf_setremove); @@ -290,18 +270,21 @@ fn do_re_match(bf_args: &mut BfCallState<'_>, reverse: bool) -> Result>(), + .map(|(start, end)| v_list(&[v_int(*start as i64), v_int(*end as i64)])), ); Ok(Ret(v_list(&[ v_int(overall.0 as i64), @@ -399,7 +382,7 @@ fn bf_substitute(bf_args: &mut BfCallState<'_>) -> Result { return Err(BfErr::Code(E_INVARG)); } - let (Some(a), Some(b)) = (subs.get(2), subs.get(3)) else { + let (Ok(a), Ok(b)) = (subs.index(2), subs.index(3)) else { return Err(BfErr::Code(E_INVARG)); }; let (Variant::List(subs), Variant::Str(source)) = (a.variant(), b.variant()) else { @@ -415,7 +398,7 @@ fn bf_substitute(bf_args: &mut BfCallState<'_>) -> Result { if sub.len() != 2 { return Err(BfErr::Code(E_INVARG)); } - let (Some(start), Some(end)) = (sub.get(0), sub.get(1)) else { + let (Ok(start), Ok(end)) = (sub.index(0), sub.index(1)) else { return Err(BfErr::Code(E_INVARG)); }; let (Variant::Int(start), Variant::Int(end)) = (start.variant(), end.variant()) else { @@ -424,7 +407,7 @@ fn bf_substitute(bf_args: &mut BfCallState<'_>) -> Result { mysubs.push((*start as isize, *end as isize)); } - match substitute(template.as_str(), &mysubs, source.as_str()) { + match substitute(&template.as_string(), &mysubs, &source.as_string()) { Ok(r) => Ok(Ret(v_string(r))), Err(e) => Err(BfErr::Code(e)), } diff --git a/crates/kernel/src/builtins/bf_maps.rs b/crates/kernel/src/builtins/bf_maps.rs index 632bdc4f..36f76b66 100644 --- a/crates/kernel/src/builtins/bf_maps.rs +++ b/crates/kernel/src/builtins/bf_maps.rs @@ -15,9 +15,9 @@ use crate::bf_declare; use crate::builtins::{BfCallState, BfErr, BfRet, BuiltinFunction}; use moor_compiler::offset_for_builtin; -use moor_values::var::Error::{E_ARGS, E_RANGE, E_TYPE}; -use moor_values::var::{v_bool, v_listv, Var, Variant}; - +use moor_values::Associative; +use moor_values::Error::{E_ARGS, E_RANGE, E_TYPE}; +use moor_values::{v_bool, v_list, Var, Variant}; /// Returns a copy of map with the value corresponding to key removed. If key is not a valid key, then E_RANGE is raised. fn bf_mapdelete(bf_args: &mut BfCallState<'_>) -> Result { if bf_args.args.len() != 2 { @@ -35,7 +35,7 @@ fn bf_mapdelete(bf_args: &mut BfCallState<'_>) -> Result { return Err(BfErr::Code(E_TYPE)); } - let (nm, Some(_)) = m.remove(&bf_args.args[1]) else { + let (nm, Some(_)) = m.remove(&bf_args.args[1], false) else { return Err(BfErr::Code(E_RANGE)); }; @@ -54,7 +54,7 @@ fn bf_mapkeys(bf_args: &mut BfCallState<'_>) -> Result { let keys: Vec = m.iter().map(|kv| kv.0.clone()).collect(); - Ok(BfRet::Ret(v_listv(keys))) + Ok(BfRet::Ret(v_list(&keys))) } bf_declare!(mapkeys, bf_mapkeys); @@ -69,7 +69,7 @@ fn bf_mapvalues(bf_args: &mut BfCallState<'_>) -> Result { let values: Vec = m.iter().map(|kv| kv.1.clone()).collect(); - Ok(BfRet::Ret(v_listv(values))) + Ok(BfRet::Ret(v_list(&values))) } bf_declare!(mapvalues, bf_mapvalues); @@ -89,9 +89,10 @@ fn bf_maphaskey(bf_args: &mut BfCallState<'_>) -> Result { return Err(BfErr::Code(E_TYPE)); } - let v = m.get(&bf_args.args[1]); - - Ok(BfRet::Ret(v_bool(v.is_some()))) + let contains = m + .contains_key(&bf_args.args[1], false) + .map_err(BfErr::Code)?; + Ok(BfRet::Ret(v_bool(contains))) } bf_declare!(maphaskey, bf_maphaskey); diff --git a/crates/kernel/src/builtins/bf_num.rs b/crates/kernel/src/builtins/bf_num.rs index 1adab5be..5f3ddb78 100644 --- a/crates/kernel/src/builtins/bf_num.rs +++ b/crates/kernel/src/builtins/bf_num.rs @@ -16,9 +16,9 @@ use decorum::R64; use rand::Rng; use moor_compiler::offset_for_builtin; -use moor_values::var::Error::{E_ARGS, E_INVARG, E_TYPE}; -use moor_values::var::Variant; -use moor_values::var::{v_float, v_int, v_str}; +use moor_values::Error::{E_ARGS, E_INVARG, E_TYPE}; +use moor_values::Variant; +use moor_values::{v_float, v_int, v_str}; use crate::bf_declare; use crate::builtins::BfRet::Ret; diff --git a/crates/kernel/src/builtins/bf_objects.rs b/crates/kernel/src/builtins/bf_objects.rs index 73b53bca..e7d3eb6f 100644 --- a/crates/kernel/src/builtins/bf_objects.rs +++ b/crates/kernel/src/builtins/bf_objects.rs @@ -20,12 +20,11 @@ use moor_values::model::Named; use moor_values::model::WorldStateError; use moor_values::model::{ObjFlag, ValSet}; use moor_values::util::BitEnum; -use moor_values::var::v_listv; -use moor_values::var::Error::{E_ARGS, E_INVARG, E_NACC, E_PERM, E_TYPE}; -use moor_values::var::Symbol; -use moor_values::var::{v_bool, v_int, v_none, v_objid, v_str}; -use moor_values::var::{List, Variant}; -use moor_values::NOTHING; +use moor_values::Error::{E_ARGS, E_INVARG, E_NACC, E_PERM, E_TYPE}; +use moor_values::{v_bool, v_int, v_none, v_objid, v_str}; +use moor_values::{v_list, Sequence, Symbol}; +use moor_values::{v_list_iter, NOTHING}; +use moor_values::Variant; use crate::bf_declare; use crate::builtins::BfRet::{Ret, VmInstr}; @@ -109,7 +108,7 @@ fn bf_children(bf_args: &mut BfCallState<'_>) -> Result { .map_err(world_state_bf_err)?; let children = children.iter().map(v_objid).collect::>(); - Ok(Ret(v_listv(children))) + Ok(Ret(v_list(&children))) } bf_declare!(children, bf_children); @@ -170,7 +169,7 @@ fn bf_create(bf_args: &mut BfCallState<'_>) -> Result { location: new_obj, this: new_obj, player: bf_args.exec_state.top().player, - args: List::new(), + args: vec![], argstr: "".to_string(), caller: bf_args.exec_state.top().this, }, @@ -261,7 +260,7 @@ fn bf_recycle(bf_args: &mut BfCallState<'_>) -> Result { } } } - let contents = v_listv(contents); + let contents = v_list(&contents); match bf_args.world_state.find_method_verb_on( bf_args.task_perms_who(), obj, @@ -280,7 +279,7 @@ fn bf_recycle(bf_args: &mut BfCallState<'_>) -> Result { location: obj, this: obj, player: bf_args.exec_state.top().player, - args: List::new(), + args: Vec::new(), argstr: "".to_string(), caller: bf_args.exec_state.top().this, }, @@ -311,14 +310,14 @@ fn bf_recycle(bf_args: &mut BfCallState<'_>) -> Result { panic!("Invalid trampoline argument for bf_recycle"); }; 'inner: loop { - debug!(?obj, contents = ?contents, "Calling :exitfunc for objects contents"); if contents.is_empty() { let bf_frame = bf_args.bf_frame_mut(); bf_frame.bf_trampoline_arg = None; bf_frame.bf_trampoline = Some(BF_RECYCLE_TRAMPOLINE_DONE_MOVE); continue 'outer; } - let (head_obj, contents) = contents.pop_front(); + let (head_obj, contents) = + contents.pop_front().map_err(|_| BfErr::Code(E_INVARG))?; let Variant::Obj(head_obj) = head_obj.variant() else { panic!("Invalid trampoline argument for bf_recycle"); }; @@ -348,7 +347,7 @@ fn bf_recycle(bf_args: &mut BfCallState<'_>) -> Result { location: *head_obj, this: *head_obj, player: bf_args.exec_state.top().player, - args: List::from_slice(&[v_objid(obj)]), + args: vec![v_objid(obj)], argstr: "".to_string(), caller: bf_args.exec_state.top().this, }, @@ -449,7 +448,7 @@ fn bf_move(bf_args: &mut BfCallState<'_>) -> Result { location: whereto, this: whereto, player: bf_args.exec_state.top().player, - args: List::from_slice(&[v_objid(what)]), + args: vec![v_objid(what)], argstr: "".to_string(), caller: bf_args.exec_state.top().this, }, @@ -526,7 +525,7 @@ fn bf_move(bf_args: &mut BfCallState<'_>) -> Result { location: original_location, this: original_location, player: bf_args.exec_state.top().player, - args: List::from_slice(&[v_objid(what)]), + args: vec![v_objid(what)], argstr: "".to_string(), caller: bf_args.exec_state.top().this, }, @@ -572,7 +571,7 @@ fn bf_move(bf_args: &mut BfCallState<'_>) -> Result { location: whereto, this: whereto, player: bf_args.exec_state.top().player, - args: List::from_slice(&[v_objid(what)]), + args: vec![v_objid(what)], argstr: "".to_string(), caller: bf_args.exec_state.top().this, }, @@ -620,7 +619,7 @@ fn bf_verbs(bf_args: &mut BfCallState<'_>) -> Result { .iter() .map(|v| v_str(v.names().first().unwrap())) .collect(); - Ok(Ret(v_listv(verbs))) + Ok(Ret(v_list(&verbs))) } bf_declare!(verbs, bf_verbs); @@ -640,7 +639,7 @@ fn bf_properties(bf_args: &mut BfCallState<'_>) -> Result { .properties(bf_args.task_perms_who(), *obj) .map_err(world_state_bf_err)?; let props: Vec<_> = props.iter().map(|p| v_str(p.name())).collect(); - Ok(Ret(v_listv(props))) + Ok(Ret(v_list(&props))) } bf_declare!(properties, bf_properties); @@ -696,9 +695,7 @@ fn bf_players(bf_args: &mut BfCallState<'_>) -> Result { } let players = bf_args.world_state.players().map_err(world_state_bf_err)?; - Ok(Ret(v_listv( - players.iter().map(v_objid).collect::>(), - ))) + Ok(Ret(v_list_iter(players.iter().map(v_objid)))) } bf_declare!(players, bf_players); diff --git a/crates/kernel/src/builtins/bf_properties.rs b/crates/kernel/src/builtins/bf_properties.rs index f4ca3752..cad2c3c2 100644 --- a/crates/kernel/src/builtins/bf_properties.rs +++ b/crates/kernel/src/builtins/bf_properties.rs @@ -15,11 +15,11 @@ use moor_compiler::offset_for_builtin; use moor_values::model::{PropAttrs, PropFlag}; use moor_values::util::BitEnum; -use moor_values::var::Error::{E_ARGS, E_INVARG, E_TYPE}; -use moor_values::var::Symbol; -use moor_values::var::Variant; -use moor_values::var::{v_bool, v_list, v_none, v_objid, v_string}; -use moor_values::var::{v_empty_list, List}; +use moor_values::Error::{E_ARGS, E_INVARG, E_TYPE}; +use moor_values::Variant; +use moor_values::{v_bool, v_list, v_none, v_objid, v_string}; +use moor_values::{v_empty_list, List}; +use moor_values::{Sequence, Symbol}; use crate::bf_declare; use crate::builtins::BfErr::Code; @@ -43,7 +43,7 @@ fn bf_property_info(bf_args: &mut BfCallState<'_>) -> Result { .get_property_info( bf_args.task_perms_who(), *obj, - Symbol::mk_case_insensitive(prop_name.as_str()), + Symbol::mk_case_insensitive(&prop_name.as_string()), ) .map_err(world_state_bf_err)?; let owner = perms.owner(); @@ -66,7 +66,7 @@ fn bf_property_info(bf_args: &mut BfCallState<'_>) -> Result { bf_declare!(property_info, bf_property_info); enum InfoParseResult { - Fail(moor_values::var::Error), + Fail(moor_values::Error), Success(PropAttrs), } @@ -75,26 +75,26 @@ fn info_to_prop_attrs(info: &List) -> InfoParseResult { return InfoParseResult::Fail(E_ARGS); } - let owner = info.get(0).unwrap(); + let owner = info.index(0).unwrap(); let Variant::Obj(owner) = owner.variant() else { return InfoParseResult::Fail(E_TYPE); }; - let perms = info.get(1).unwrap(); + let perms = info.index(1).unwrap(); let Variant::Str(perms) = perms.variant() else { return InfoParseResult::Fail(E_TYPE); }; let name = if info.len() == 3 { - let name = info.get(2).unwrap(); + let name = info.index(2).unwrap(); let Variant::Str(name) = name.variant() else { return InfoParseResult::Fail(E_TYPE); }; - Some(name.to_string()) + Some(name.as_string()) } else { None }; let mut flags = BitEnum::new(); - for c in perms.as_str().chars() { + for c in perms.as_string().chars() { match c { 'r' => flags |= PropFlag::Read, 'w' => flags |= PropFlag::Write, @@ -138,7 +138,7 @@ fn bf_set_property_info(bf_args: &mut BfCallState<'_>) -> Result { .set_property_info( bf_args.task_perms_who(), *obj, - Symbol::mk_case_insensitive(prop_name.as_str()), + Symbol::mk_case_insensitive(prop_name.as_string().as_str()), attrs, ) .map_err(world_state_bf_err)?; @@ -161,7 +161,7 @@ fn bf_is_clear_property(bf_args: &mut BfCallState<'_>) -> Result { .is_property_clear( bf_args.task_perms_who(), *obj, - Symbol::mk_case_insensitive(prop_name.as_str()), + Symbol::mk_case_insensitive(prop_name.as_string().as_str()), ) .map_err(world_state_bf_err)?; Ok(Ret(v_bool(is_clear))) @@ -183,7 +183,7 @@ fn bf_clear_property(bf_args: &mut BfCallState<'_>) -> Result { .clear_property( bf_args.task_perms_who(), *obj, - Symbol::mk_case_insensitive(prop_name.as_str()), + Symbol::mk_case_insensitive(prop_name.as_string().as_str()), ) .map_err(world_state_bf_err)?; Ok(Ret(v_empty_list())) @@ -218,7 +218,7 @@ fn bf_add_property(bf_args: &mut BfCallState<'_>) -> Result { bf_args.task_perms_who(), *location, *location, - Symbol::mk_case_insensitive(name.as_str()), + Symbol::mk_case_insensitive(name.as_string().as_str()), attrs.owner.unwrap(), attrs.flags.unwrap(), Some(value), @@ -243,7 +243,7 @@ fn bf_delete_property(bf_args: &mut BfCallState<'_>) -> Result { .delete_property( bf_args.task_perms_who(), *obj, - Symbol::mk_case_insensitive(prop_name.as_str()), + Symbol::mk_case_insensitive(prop_name.as_string().as_str()), ) .map_err(world_state_bf_err)?; Ok(Ret(v_empty_list())) diff --git a/crates/kernel/src/builtins/bf_server.rs b/crates/kernel/src/builtins/bf_server.rs index f528591a..ee723860 100644 --- a/crates/kernel/src/builtins/bf_server.rs +++ b/crates/kernel/src/builtins/bf_server.rs @@ -24,18 +24,18 @@ use moor_compiler::compile; use moor_compiler::{offset_for_builtin, ArgCount, ArgType, Builtin, BUILTINS}; use moor_values::model::{ObjFlag, WorldStateError}; use moor_values::tasks::NarrativeEvent; -use moor_values::var::Error::{E_ARGS, E_INVARG, E_INVIND, E_PERM, E_TYPE}; -use moor_values::var::Symbol; -use moor_values::var::Variant; -use moor_values::var::{v_bool, v_int, v_list, v_none, v_objid, v_str, v_string, Var}; -use moor_values::var::{v_listv, Error}; +use moor_values::Error::{E_ARGS, E_INVARG, E_INVIND, E_PERM, E_TYPE}; +use moor_values::Symbol; +use moor_values::Variant; +use moor_values::{v_bool, v_int, v_list, v_none, v_objid, v_str, v_string, Var}; +use moor_values::{v_list_iter, Error}; use crate::bf_declare; use crate::builtins::BfRet::{Ret, VmInstr}; use crate::builtins::{world_state_bf_err, BfCallState, BfErr, BfRet, BuiltinFunction}; use crate::vm::ExecutionResult; use moor_values::tasks::TaskId; -use moor_values::var::VarType::TYPE_STR; +use moor_values::VarType::TYPE_STR; fn bf_noop(bf_args: &mut BfCallState<'_>) -> Result { error!( @@ -57,7 +57,7 @@ fn bf_notify(bf_args: &mut BfCallState<'_>) -> Result { if bf_args.args.len() != 2 { return Err(BfErr::Code(E_ARGS)); } - if bf_args.args[1].type_id() != TYPE_STR { + if bf_args.args[1].type_code() != TYPE_STR { return Err(BfErr::Code(E_TYPE)); } } @@ -82,7 +82,9 @@ fn bf_notify(bf_args: &mut BfCallState<'_>) -> Result { let Variant::Str(content_type) = bf_args.args[2].variant() else { return Err(BfErr::Code(E_TYPE)); }; - Some(Symbol::mk_case_insensitive(content_type.as_str())) + Some(Symbol::mk_case_insensitive( + content_type.as_string().as_str(), + )) } else { None }; @@ -103,14 +105,13 @@ fn bf_connected_players(bf_args: &mut BfCallState<'_>) -> Result { return Err(BfErr::Code(E_ARGS)); } - Ok(Ret(v_listv( + Ok(Ret(v_list_iter( bf_args .session .connected_players() .unwrap() .iter() - .map(|p| v_objid(*p)) - .collect::>(), + .map(|p| v_objid(*p)), ))) } bf_declare!(connected_players, bf_connected_players); @@ -168,28 +169,23 @@ fn bf_callers(bf_args: &mut BfCallState<'_>) -> Result { // We have to exempt ourselves from the callers list. let callers = bf_args.exec_state.callers()[1..].to_vec(); - Ok(Ret(v_listv( - callers - .iter() - .map(|c| { - let callers = vec![ - // this - v_objid(c.this), - // verb name - v_string(c.verb_name.to_string()), - // 'programmer' - v_objid(c.programmer), - // verb location - v_objid(c.definer), - // player - v_objid(c.player), - // line number - v_int(c.line_number as i64), - ]; - v_listv(callers) - }) - .collect::>(), - ))) + Ok(Ret(v_list_iter(callers.iter().map(|c| { + let callers = vec![ + // this + v_objid(c.this), + // verb name + v_string(c.verb_name.to_string()), + // 'programmer' + v_objid(c.programmer), + // verb location + v_objid(c.definer), + // player + v_objid(c.player), + // line number + v_int(c.line_number as i64), + ]; + v_list(&callers) + })))) } bf_declare!(callers, bf_callers); @@ -277,7 +273,7 @@ fn bf_shutdown(bf_args: &mut BfCallState<'_>) -> Result { let Variant::Str(msg) = bf_args.args[0].variant() else { return Err(BfErr::Code(E_TYPE)); }; - Some(msg.as_str().to_string()) + Some(msg.as_string()) }; bf_args @@ -354,7 +350,7 @@ fn bf_raise(bf_args: &mut BfCallState<'_>) -> Result { let Variant::Str(msg) = bf_args.args[1].variant() else { return Err(BfErr::Code(E_TYPE)); }; - Some(msg.to_string()) + Some(msg.as_string()) } else { None }; @@ -449,31 +445,28 @@ fn bf_queued_tasks(bf_args: &mut BfCallState<'_>) -> Result { // return in form: // {, , , , // , , , , } - let tasks: Vec<_> = tasks - .iter() - .map(|task| { - let task_id = v_int(task.task_id as i64); - let start_time = match task.start_time { - None => v_none(), - Some(start_time) => { - let time = start_time.duration_since(SystemTime::UNIX_EPOCH).unwrap(); - v_int(time.as_secs() as i64) - } - }; - let x = v_none(); - let y = v_none(); - let programmer = v_objid(task.permissions); - let verb_loc = v_objid(task.verb_definer); - let verb_name = v_str(task.verb_name.as_str()); - let line = v_int(task.line_number as i64); - let this = v_objid(task.this); - v_list(&[ - task_id, start_time, x, y, programmer, verb_loc, verb_name, line, this, - ]) - }) - .collect(); - - Ok(Ret(v_listv(tasks))) + let tasks = tasks.iter().map(|task| { + let task_id = v_int(task.task_id as i64); + let start_time = match task.start_time { + None => v_none(), + Some(start_time) => { + let time = start_time.duration_since(SystemTime::UNIX_EPOCH).unwrap(); + v_int(time.as_secs() as i64) + } + }; + let x = v_none(); + let y = v_none(); + let programmer = v_objid(task.permissions); + let verb_loc = v_objid(task.verb_definer); + let verb_name = v_str(task.verb_name.as_str()); + let line = v_int(task.line_number as i64); + let this = v_objid(task.this); + v_list(&[ + task_id, start_time, x, y, programmer, verb_loc, verb_name, line, this, + ]) + }); + + Ok(Ret(v_list_iter(tasks))) } bf_declare!(queued_tasks, bf_queued_tasks); @@ -617,7 +610,7 @@ fn bf_call_function(bf_args: &mut BfCallState<'_>) -> Result { let args = &bf_args.args[1..]; // Find the function id for the given function name. - let func_name = Symbol::mk_case_insensitive(func_name.as_str()); + let func_name = Symbol::mk_case_insensitive(func_name.as_string().as_str()); let builtin = BUILTINS .find_builtin(func_name) .ok_or(BfErr::Code(E_ARGS))?; @@ -690,22 +683,16 @@ fn bf_function_info_to_list(bf: &Builtin) -> Var { ArgCount::Q(q) => v_int(q as i64), ArgCount::U => v_int(-1), }; - let types = bf - .types - .iter() - .map(|t| match t { - ArgType::Typed(ty) => v_int(*ty as i64), - ArgType::Any => v_int(-1), - ArgType::AnyNum => v_int(-2), - }) - .collect::>(); - - v_listv(vec![ - v_str(bf.name.as_str()), + let types = bf.types.iter().map(|t| match t { + ArgType::Typed(ty) => v_int(*ty as i64), + ArgType::Any => v_int(-1), + ArgType::AnyNum => v_int(-2), + }); + + v_list(&[v_str(bf.name.as_str()), min_args, max_args, - v_listv(types), - ]) + v_list_iter(types)]) } fn bf_function_info(bf_args: &mut BfCallState<'_>) -> Result { @@ -717,7 +704,7 @@ fn bf_function_info(bf_args: &mut BfCallState<'_>) -> Result { let Variant::Str(func_name) = bf_args.args[0].variant() else { return Err(BfErr::Code(E_TYPE)); }; - let func_name = Symbol::mk_case_insensitive(func_name.as_str()); + let func_name = Symbol::mk_case_insensitive(&func_name.as_string()); let bf = BUILTINS .find_builtin(func_name) .ok_or(BfErr::Code(E_ARGS))?; @@ -728,12 +715,11 @@ fn bf_function_info(bf_args: &mut BfCallState<'_>) -> Result { return Ok(Ret(desc)); } - let bf_list: Vec<_> = BUILTINS + let bf_list = BUILTINS .descriptions() .filter(|&bf| bf.implemented) - .map(bf_function_info_to_list) - .collect(); - Ok(Ret(v_listv(bf_list))) + .map(bf_function_info_to_list); + Ok(Ret(v_list_iter(bf_list))) } bf_declare!(function_info, bf_function_info); @@ -776,10 +762,10 @@ fn bf_eval(bf_args: &mut BfCallState<'_>) -> Result { match tramp { BF_SERVER_EVAL_TRAMPOLINE_START_INITIALIZE => { - let program_code = program_code.as_str(); - let program = match compile(program_code, bf_args.config.compile_options()) { + let program_code = program_code.as_string(); + let program = match compile(&program_code, bf_args.config.compile_options()) { Ok(program) => program, - Err(e) => return Ok(Ret(v_listv(vec![v_int(0), v_string(e.to_string())]))), + Err(e) => return Ok(Ret(v_list(&[v_int(0), v_string(e.to_string())]))), }; let bf_frame = bf_args.bf_frame_mut(); bf_frame.bf_trampoline = Some(BF_SERVER_EVAL_TRAMPOLINE_RESUME); @@ -794,7 +780,7 @@ fn bf_eval(bf_args: &mut BfCallState<'_>) -> Result { BF_SERVER_EVAL_TRAMPOLINE_RESUME => { // Value must be on in our activation's "return value" let value = bf_args.exec_state.top().frame.return_value(); - Ok(Ret(v_listv(vec![v_bool(true), value]))) + Ok(Ret(v_list(&[v_bool(true), value]))) } _ => { panic!("Invalid trampoline value for bf_eval: {}", tramp); diff --git a/crates/kernel/src/builtins/bf_strings.rs b/crates/kernel/src/builtins/bf_strings.rs index 8a3ae962..865adef4 100644 --- a/crates/kernel/src/builtins/bf_strings.rs +++ b/crates/kernel/src/builtins/bf_strings.rs @@ -17,9 +17,9 @@ use rand::distributions::Alphanumeric; use rand::Rng; use moor_compiler::offset_for_builtin; -use moor_values::var::Error::{E_ARGS, E_INVARG, E_TYPE}; -use moor_values::var::Variant; -use moor_values::var::{v_int, v_str, v_string}; +use moor_values::Error::{E_ARGS, E_INVARG, E_TYPE}; +use moor_values::Variant; +use moor_values::{v_int, v_str, v_string}; use crate::bf_declare; use crate::builtins::BfRet::Ret; @@ -68,7 +68,13 @@ fn bf_strsub(bf_args: &mut BfCallState<'_>) -> Result { ); match (subject, what, with) { (Variant::Str(subject), Variant::Str(what), Variant::Str(with)) => Ok(Ret(v_str( - strsub(subject.as_str(), what.as_str(), with.as_str(), case_matters).as_str(), + strsub( + subject.as_string().as_str(), + what.as_string().as_str(), + with.as_string().as_str(), + case_matters, + ) + .as_str(), ))), _ => Err(BfErr::Code(E_TYPE)), } @@ -114,8 +120,8 @@ fn bf_index(bf_args: &mut BfCallState<'_>) -> Result { let (subject, what) = (bf_args.args[0].variant(), bf_args.args[1].variant()); match (subject, what) { (Variant::Str(subject), Variant::Str(what)) => Ok(Ret(v_int(str_index( - subject.as_str(), - what.as_str(), + subject.as_string().as_str(), + what.as_string().as_str(), case_matters, )))), _ => Err(BfErr::Code(E_TYPE)), @@ -138,8 +144,8 @@ fn bf_rindex(bf_args: &mut BfCallState<'_>) -> Result { let (subject, what) = (bf_args.args[0].variant(), bf_args.args[1].variant()); match (subject, what) { (Variant::Str(subject), Variant::Str(what)) => Ok(Ret(v_int(str_rindex( - subject.as_str(), - what.as_str(), + subject.as_string().as_str(), + what.as_string().as_str(), case_matters, )))), _ => Err(BfErr::Code(E_TYPE)), @@ -153,9 +159,9 @@ fn bf_strcmp(bf_args: &mut BfCallState<'_>) -> Result { } let (str1, str2) = (bf_args.args[0].variant(), bf_args.args[1].variant()); match (str1, str2) { - (Variant::Str(str1), Variant::Str(str2)) => { - Ok(Ret(v_int(str1.as_str().cmp(str2.as_str()) as i64))) - } + (Variant::Str(str1), Variant::Str(str2)) => Ok(Ret(v_int( + str1.as_string().as_str().cmp(str2.as_string().as_str()) as i64, + ))), _ => Err(BfErr::Code(E_TYPE)), } } @@ -187,10 +193,10 @@ fn bf_crypt(bf_args: &mut BfCallState<'_>) -> Result { let Variant::Str(salt) = bf_args.args[1].variant() else { return Err(BfErr::Code(E_TYPE)); }; - String::from(salt.as_str()) + String::from(salt.as_string().as_str()) }; if let Variant::Str(text) = bf_args.args[0].variant() { - let crypted = pwhash::unix::crypt(text.as_str(), salt.as_str()).unwrap(); + let crypted = pwhash::unix::crypt(text.as_string().as_str(), salt.as_str()).unwrap(); Ok(Ret(v_string(crypted))) } else { Err(BfErr::Code(E_TYPE)) @@ -204,7 +210,7 @@ fn bf_string_hash(bf_args: &mut BfCallState<'_>) -> Result { } match bf_args.args[0].variant() { Variant::Str(s) => { - let hash_digest = md5::Md5::digest(s.as_str().as_bytes()); + let hash_digest = md5::Md5::digest(s.as_string().as_bytes()); Ok(Ret(v_str( format!("{:x}", hash_digest).to_uppercase().as_str(), ))) diff --git a/crates/kernel/src/builtins/bf_values.rs b/crates/kernel/src/builtins/bf_values.rs index a2eb5511..1e68acde 100644 --- a/crates/kernel/src/builtins/bf_values.rs +++ b/crates/kernel/src/builtins/bf_values.rs @@ -13,19 +13,20 @@ // use md5::Digest; -use moor_compiler::offset_for_builtin; -use moor_values::var::Error::{E_ARGS, E_INVARG, E_TYPE}; -use moor_values::var::Variant; -use moor_values::var::{v_bool, v_float, v_int, v_obj, v_str}; -use moor_values::AsByteBuffer; +use moor_compiler::{offset_for_builtin, to_literal}; +use moor_values::Error::{E_ARGS, E_INVARG, E_TYPE}; +use moor_values::Variant; +use moor_values::{v_bool, v_float, v_int, v_obj, v_str}; +use moor_values::{AsByteBuffer, Sequence}; use crate::bf_declare; use crate::builtins::BfRet::Ret; use crate::builtins::{world_state_bf_err, BfCallState, BfErr, BfRet, BuiltinFunction}; +use moor_values::Associative; fn bf_typeof(bf_args: &mut BfCallState<'_>) -> Result { let arg = &bf_args.args[0]; - Ok(Ret(v_int(arg.type_id() as i64))) + Ok(Ret(v_int(arg.type_code() as i64))) } bf_declare!(typeof, bf_typeof); @@ -36,7 +37,7 @@ fn bf_tostr(bf_args: &mut BfCallState<'_>) -> Result { Variant::None => result.push_str("None"), Variant::Int(i) => result.push_str(&i.to_string()), Variant::Float(f) => result.push_str(format!("{:?}", f).as_str()), - Variant::Str(s) => result.push_str(s.as_str()), + Variant::Str(s) => result.push_str(s.as_string().as_str()), Variant::Obj(o) => result.push_str(&o.to_string()), Variant::List(_) => result.push_str("{list}"), Variant::Map(_) => result.push_str("[map]"), @@ -51,7 +52,7 @@ fn bf_toliteral(bf_args: &mut BfCallState<'_>) -> Result { if bf_args.args.len() != 1 { return Err(BfErr::Code(E_ARGS)); } - let literal = bf_args.args[0].to_literal(); + let literal = to_literal(&bf_args.args[0]); Ok(Ret(v_str(literal.as_str()))) } bf_declare!(toliteral, bf_toliteral); @@ -65,7 +66,7 @@ fn bf_toint(bf_args: &mut BfCallState<'_>) -> Result { Variant::Float(f) => Ok(Ret(v_int(*f as i64))), Variant::Obj(o) => Ok(Ret(v_int(o.0))), Variant::Str(s) => { - let i = s.as_str().parse::(); + let i = s.as_string().as_str().parse::(); match i { Ok(i) => Ok(Ret(v_int(i as i64))), Err(_) => Ok(Ret(v_int(0))), @@ -84,15 +85,15 @@ fn bf_toobj(bf_args: &mut BfCallState<'_>) -> Result { match bf_args.args[0].variant() { Variant::Int(i) => Ok(Ret(v_obj(*i))), Variant::Float(f) => Ok(Ret(v_obj(*f as i64))), - Variant::Str(s) if s.as_str().starts_with('#') => { - let i = s.as_str()[1..].parse::(); + Variant::Str(s) if s.as_string().as_str().starts_with('#') => { + let i = s.as_string().as_str()[1..].parse::(); match i { Ok(i) => Ok(Ret(v_obj(i))), Err(_) => Ok(Ret(v_obj(0))), } } Variant::Str(s) => { - let i = s.as_str().parse::(); + let i = s.as_string().as_str().parse::(); match i { Ok(i) => Ok(Ret(v_obj(i))), Err(_) => Ok(Ret(v_obj(0))), @@ -111,7 +112,7 @@ fn bf_tofloat(bf_args: &mut BfCallState<'_>) -> Result { Variant::Int(i) => Ok(Ret(v_float(*i as f64))), Variant::Float(f) => Ok(Ret(v_float(*f))), Variant::Str(s) => { - let f = s.as_str().parse::(); + let f = s.as_string().as_str().parse::(); match f { Ok(f) => Ok(Ret(v_float(f))), Err(_) => Ok(Ret(v_float(0.0))), @@ -127,11 +128,8 @@ fn bf_equal(bf_args: &mut BfCallState<'_>) -> Result { if bf_args.args.len() != 2 { return Err(BfErr::Code(E_ARGS)); } - let result = match (bf_args.args[0].variant(), bf_args.args[1].variant()) { - (Variant::Str(s1), Variant::Str(s2)) => s1.as_str() == s2.as_str().to_lowercase(), - (Variant::Map(m1), Variant::Map(m2)) => m1.eq_case_sensitive(m2), - _ => bf_args.args[0] == bf_args.args[1], - }; + let (a1, a2) = (&bf_args.args[0], &bf_args.args[1]); + let result = a1.eq_case_sensitive(a2); Ok(Ret(v_bool(result))) } bf_declare!(equal, bf_equal); @@ -149,7 +147,7 @@ fn bf_value_hash(bf_args: &mut BfCallState<'_>) -> Result { if bf_args.args.len() != 1 { return Err(BfErr::Code(E_ARGS)); } - let s = bf_args.args[0].to_literal(); + let s = to_literal(&bf_args.args[0]); let hash_digest = md5::Md5::digest(s.as_bytes()); Ok(Ret(v_str( format!("{:x}", hash_digest).to_uppercase().as_str(), diff --git a/crates/kernel/src/builtins/bf_verbs.rs b/crates/kernel/src/builtins/bf_verbs.rs index 79c219f4..d1d90636 100644 --- a/crates/kernel/src/builtins/bf_verbs.rs +++ b/crates/kernel/src/builtins/bf_verbs.rs @@ -15,12 +15,12 @@ use strum::EnumCount; use tracing::{error, warn}; -use moor_compiler::compile; use moor_compiler::offset_for_builtin; use moor_compiler::program_to_tree; use moor_compiler::unparse; use moor_compiler::GlobalName; use moor_compiler::Program; +use moor_compiler::{compile, to_literal}; use moor_values::model::ObjFlag; use moor_values::model::VerbDef; use moor_values::model::WorldStateError; @@ -28,14 +28,14 @@ use moor_values::model::{ArgSpec, VerbArgsSpec}; use moor_values::model::{BinaryType, VerbAttrs, VerbFlag}; use moor_values::model::{HasUuid, Named}; use moor_values::util::BitEnum; -use moor_values::var::Error::{E_ARGS, E_INVARG, E_INVIND, E_PERM, E_TYPE, E_VERBNF}; -use moor_values::var::List; -use moor_values::var::Objid; -use moor_values::var::Symbol; -use moor_values::var::Variant; -use moor_values::var::{v_empty_list, v_list, v_none, v_objid, v_str, v_string, Var}; -use moor_values::var::{v_listv, Error}; -use moor_values::AsByteBuffer; +use moor_values::Error::{E_ARGS, E_INVARG, E_INVIND, E_PERM, E_TYPE, E_VERBNF}; +use moor_values::List; +use moor_values::Objid; +use moor_values::Symbol; +use moor_values::Variant; +use moor_values::{v_empty_list, v_list, v_none, v_objid, v_str, v_string, Var}; +use moor_values::{v_list_iter, Error}; +use moor_values::{AsByteBuffer, Sequence}; use crate::bf_declare; use crate::builtins::BfRet::Ret; @@ -65,7 +65,7 @@ fn bf_verb_info(bf_args: &mut BfCallState<'_>) -> Result { .get_verb( bf_args.task_perms_who(), *obj, - Symbol::mk_case_insensitive(verb_desc.as_str()), + Symbol::mk_case_insensitive(&verb_desc.as_string()), ) .map_err(world_state_bf_err)?, Variant::Int(verb_index) => { @@ -112,7 +112,7 @@ bf_declare!(verb_info, bf_verb_info); fn get_verbdef(obj: Objid, verbspec: Var, bf_args: &BfCallState<'_>) -> Result { let verbspec_result = match verbspec.variant() { Variant::Str(verb_desc) => { - let verb_desc = Symbol::mk_case_insensitive(verb_desc.as_str()); + let verb_desc = Symbol::mk_case_insensitive(&verb_desc.as_string()); bf_args .world_state .get_verb(bf_args.task_perms_who(), obj, verb_desc) @@ -144,13 +144,13 @@ fn parse_verb_info(info: &List) -> Result { return Err(E_INVARG); } match ( - info.get(0).unwrap().variant(), - info.get(1).unwrap().variant(), - info.get(2).unwrap().variant(), + info.index(0).unwrap().variant(), + info.index(1).unwrap().variant(), + info.index(2).unwrap().variant(), ) { (Variant::Obj(owner), Variant::Str(perms_str), Variant::Str(names)) => { let mut perms = BitEnum::new(); - for c in perms_str.as_str().chars() { + for c in perms_str.as_string().chars() { match c { 'r' => perms |= VerbFlag::Read, 'w' => perms |= VerbFlag::Write, @@ -162,7 +162,7 @@ fn parse_verb_info(info: &List) -> Result { // Split the names string into a list of symbols let name_strings = names - .as_str() + .as_string() .split(' ') .map(Symbol::mk_case_insensitive) .collect::>(); @@ -212,7 +212,7 @@ fn bf_set_verb_info(bf_args: &mut BfCallState<'_>) -> Result { .update_verb( bf_args.task_perms_who(), *obj, - Symbol::mk_case_insensitive(verb_name.as_str()), + Symbol::mk_case_insensitive(&verb_name.as_string()), update_attrs, ) .map_err(world_state_bf_err)?; @@ -271,15 +271,15 @@ fn parse_verb_args(verbinfo: &List) -> Result { return Err(E_ARGS); } match ( - verbinfo.get(0).unwrap().variant(), - verbinfo.get(1).unwrap().variant(), - verbinfo.get(2).unwrap().variant(), + verbinfo.index(0).unwrap().variant(), + verbinfo.index(1).unwrap().variant(), + verbinfo.index(2).unwrap().variant(), ) { (Variant::Str(dobj_str), Variant::Str(prep_str), Variant::Str(iobj_str)) => { let (Some(dobj), Some(prep), Some(iobj)) = ( - ArgSpec::from_string(dobj_str.as_str()), - parse_preposition_spec(prep_str.as_str()), - ArgSpec::from_string(iobj_str.as_str()), + ArgSpec::from_string(&dobj_str.as_string()), + parse_preposition_spec(&prep_str.as_string()), + ArgSpec::from_string(&iobj_str.as_string()), ) else { return Err(E_INVARG); }; @@ -329,7 +329,7 @@ fn bf_set_verb_args(bf_args: &mut BfCallState<'_>) -> Result { .update_verb( bf_args.task_perms_who(), *obj, - Symbol::mk_case_insensitive(verb_name.as_str()), + Symbol::mk_case_insensitive(&verb_name.as_string()), update_attrs, ) .map_err(world_state_bf_err)?; @@ -425,9 +425,7 @@ fn bf_verb_code(bf_args: &mut BfCallState<'_>) -> Result { return Err(BfErr::Code(E_INVARG)); } }; - Ok(Ret(v_listv( - unparsed.iter().map(|s| v_str(s)).collect::>(), - ))) + Ok(Ret(v_list_iter(unparsed.iter().map(|s| v_str(s))))) } bf_declare!(verb_code, bf_verb_code); @@ -477,7 +475,7 @@ fn bf_set_verb_code(bf_args: &mut BfCallState<'_>) -> Result { Variant::Str(line) => line, _ => return Err(BfErr::Code(E_TYPE)), }; - code_string.push_str(line.as_str()); + code_string.push_str(&line.as_string()); code_string.push('\n'); } // Now try to compile... @@ -658,7 +656,7 @@ fn bf_disassemble(bf_args: &mut BfCallState<'_>) -> Result { // Write literals indexed by their offset # disassembly.push(v_str("LITERALS:")); for (i, l) in program.literals.iter().enumerate() { - disassembly.push(v_string(format!("{: >3}: {}", i, l.to_literal()))); + disassembly.push(v_string(format!("{: >3}: {}", i, to_literal(l)))); } // Write jump labels indexed by their offset & showing position & optional name @@ -697,7 +695,7 @@ fn bf_disassemble(bf_args: &mut BfCallState<'_>) -> Result { disassembly.push(v_string(format!("{: >3}: {:?}{}", i, op, line_no_string))); } - Ok(Ret(v_listv(disassembly))) + Ok(Ret(v_list(&disassembly))) } bf_declare!(disassemble, bf_disassemble); diff --git a/crates/kernel/src/builtins/mod.rs b/crates/kernel/src/builtins/mod.rs index f46608e1..0bc33b83 100644 --- a/crates/kernel/src/builtins/mod.rs +++ b/crates/kernel/src/builtins/mod.rs @@ -20,10 +20,10 @@ use moor_compiler::{BuiltinId, BUILTINS}; use moor_values::model::Perms; use moor_values::model::WorldState; use moor_values::model::WorldStateError; -use moor_values::var::Error; -use moor_values::var::Objid; -use moor_values::var::Symbol; -use moor_values::var::Var; +use moor_values::Error; +use moor_values::Objid; +use moor_values::Symbol; +use moor_values::Var; use crate::builtins::bf_list_sets::register_bf_list_sets; use crate::builtins::bf_maps::register_bf_maps; diff --git a/crates/kernel/src/matching/match_env.rs b/crates/kernel/src/matching/match_env.rs index b84af44e..7bf3f6a3 100644 --- a/crates/kernel/src/matching/match_env.rs +++ b/crates/kernel/src/matching/match_env.rs @@ -14,7 +14,7 @@ use moor_values::model::WorldStateError; use moor_values::model::{ObjSet, ValSet}; -use moor_values::var::Objid; +use moor_values::Objid; use moor_values::{AMBIGUOUS, FAILED_MATCH, NOTHING}; use crate::tasks::command_parse::ParseMatcher; @@ -148,7 +148,7 @@ impl ParseMatcher for MatchEnvironmentParseMatcher { #[cfg(test)] mod tests { - use moor_values::var::Objid; + use moor_values::Objid; use moor_values::{FAILED_MATCH, NOTHING}; use crate::matching::match_env::{ diff --git a/crates/kernel/src/matching/mock_matching_env.rs b/crates/kernel/src/matching/mock_matching_env.rs index db75219b..f7fdd2fb 100644 --- a/crates/kernel/src/matching/mock_matching_env.rs +++ b/crates/kernel/src/matching/mock_matching_env.rs @@ -16,7 +16,7 @@ use std::collections::{HashMap, HashSet}; use moor_values::model::WorldStateError; use moor_values::model::{ObjSet, ValSet}; -use moor_values::var::Objid; +use moor_values::Objid; use moor_values::NOTHING; use crate::matching::match_env::MatchEnvironment; diff --git a/crates/kernel/src/matching/ws_match_env.rs b/crates/kernel/src/matching/ws_match_env.rs index ea438b4a..15e60cb6 100644 --- a/crates/kernel/src/matching/ws_match_env.rs +++ b/crates/kernel/src/matching/ws_match_env.rs @@ -15,7 +15,7 @@ use moor_values::model::ObjSet; use moor_values::model::WorldState; use moor_values::model::WorldStateError; -use moor_values::var::Objid; +use moor_values::Objid; use crate::matching::match_env::MatchEnvironment; diff --git a/crates/kernel/src/tasks/command_parse.rs b/crates/kernel/src/tasks/command_parse.rs index 4c58ee09..2d45fa3a 100644 --- a/crates/kernel/src/tasks/command_parse.rs +++ b/crates/kernel/src/tasks/command_parse.rs @@ -19,8 +19,8 @@ use bincode::{Decode, Encode}; use moor_values::model::WorldStateError; use moor_values::model::{PrepSpec, Preposition}; use moor_values::util; -use moor_values::var::Objid; -use moor_values::var::{v_str, Var}; +use moor_values::Objid; +use moor_values::{v_str, Var}; #[derive(Clone, Eq, PartialEq, Debug, Decode, Encode)] pub struct ParsedCommand { @@ -188,7 +188,7 @@ where mod tests { use moor_values::model::Preposition; use moor_values::util::parse_into_words; - use moor_values::var::v_str; + use moor_values::v_str; use moor_values::FAILED_MATCH; use crate::matching::match_env::MatchEnvironmentParseMatcher; diff --git a/crates/kernel/src/tasks/mod.rs b/crates/kernel/src/tasks/mod.rs index 833037a5..3b46de6c 100644 --- a/crates/kernel/src/tasks/mod.rs +++ b/crates/kernel/src/tasks/mod.rs @@ -18,8 +18,8 @@ use std::time::SystemTime; use bincode::{Decode, Encode}; use moor_compiler::Program; -use moor_values::var::{List, Objid}; -use moor_values::var::{Symbol, Var}; +use moor_values::Objid; +use moor_values::{Symbol, Var}; pub use crate::tasks::tasks_db::{NoopTasksDb, TasksDb, TasksDbError}; use crate::vm::Fork; @@ -75,7 +75,7 @@ pub struct VerbCall { pub location: Objid, pub this: Objid, pub player: Objid, - pub args: List, + pub args: Vec, pub argstr: String, pub caller: Objid, } @@ -124,9 +124,9 @@ pub mod vm_test_utils { use moor_compiler::Program; use moor_values::model::WorldState; - use moor_values::var::Symbol; - use moor_values::var::{List, Objid, Var}; + use moor_values::Symbol; use moor_values::SYSTEM_OBJECT; + use moor_values::{Objid, Var}; use crate::builtins::BuiltinRegistry; use crate::config::Config; @@ -217,7 +217,7 @@ pub mod vm_test_utils { location: SYSTEM_OBJECT, this: SYSTEM_OBJECT, player: SYSTEM_OBJECT, - args: List::from_slice(&args), + args, argstr: "".to_string(), caller: SYSTEM_OBJECT, }, @@ -244,7 +244,7 @@ pub mod scheduler_test_utils { use std::time::Duration; use moor_values::tasks::{CommandError, SchedulerError}; - use moor_values::var::{Error::E_VERBNF, Objid, Var}; + use moor_values::{Error::E_VERBNF, Objid, Var}; use super::TaskHandle; use crate::config::Config; @@ -316,7 +316,7 @@ pub enum TaskStart { player: Objid, vloc: Objid, verb: Symbol, - args: List, + args: Vec, argstr: String, }, /// The scheduler is telling the task to run a task that was forked from another task. diff --git a/crates/kernel/src/tasks/scheduler.rs b/crates/kernel/src/tasks/scheduler.rs index a2d159d5..92dc8b59 100644 --- a/crates/kernel/src/tasks/scheduler.rs +++ b/crates/kernel/src/tasks/scheduler.rs @@ -34,11 +34,11 @@ use moor_values::model::{CommitResult, Perms}; use moor_values::tasks::{ AbortLimitReason, CommandError, SchedulerError, TaskId, VerbProgramError, }; -use moor_values::var::Error::{E_INVARG, E_PERM}; -use moor_values::var::Symbol; -use moor_values::var::{v_err, v_int, v_none, v_string, List, Var}; -use moor_values::var::{Objid, Variant}; +use moor_values::Error::{E_INVARG, E_PERM}; +use moor_values::Symbol; +use moor_values::{v_err, v_int, v_none, v_string, Var}; use moor_values::{AsByteBuffer, SYSTEM_OBJECT}; +use moor_values::{Objid, Variant}; use crate::builtins::BuiltinRegistry; use crate::config::Config; @@ -392,7 +392,7 @@ impl Scheduler { player, vloc, verb, - args: List::from_slice(&args), + args, argstr, }); let task_id = self.next_task_id; @@ -461,7 +461,7 @@ impl Scheduler { player, vloc: SYSTEM_OBJECT, verb: *DO_OUT_OF_BAND_COMMAND, - args: List::from_slice(&args), + args, argstr, }); let task_id = self.next_task_id; diff --git a/crates/kernel/src/tasks/scheduler_client.rs b/crates/kernel/src/tasks/scheduler_client.rs index c880de5e..d7342434 100644 --- a/crates/kernel/src/tasks/scheduler_client.rs +++ b/crates/kernel/src/tasks/scheduler_client.rs @@ -20,8 +20,8 @@ use tracing::{instrument, trace}; use uuid::Uuid; use moor_compiler::{compile, Program}; -use moor_values::var::Symbol; -use moor_values::var::{Objid, Var}; +use moor_values::Symbol; +use moor_values::{Objid, Var}; use crate::config::Config; use crate::tasks::sessions::Session; diff --git a/crates/kernel/src/tasks/sessions.rs b/crates/kernel/src/tasks/sessions.rs index 4e015a84..841b47f6 100644 --- a/crates/kernel/src/tasks/sessions.rs +++ b/crates/kernel/src/tasks/sessions.rs @@ -18,7 +18,7 @@ use thiserror::Error; use uuid::Uuid; use moor_values::tasks::NarrativeEvent; -use moor_values::var::Objid; +use moor_values::Objid; /// The interface for managing the user I/O connection side of state, exposed by the scheduler to /// the VM during execution and by the host server to the scheduler. diff --git a/crates/kernel/src/tasks/suspension.rs b/crates/kernel/src/tasks/suspension.rs index 2c658d30..80d919c2 100644 --- a/crates/kernel/src/tasks/suspension.rs +++ b/crates/kernel/src/tasks/suspension.rs @@ -23,7 +23,7 @@ use bincode::{BorrowDecode, Decode, Encode}; use tracing::{debug, error, info, warn}; use uuid::Uuid; -use moor_values::var::{Objid, Var}; +use moor_values::{Objid, Var}; use crate::tasks::sessions::{NoopClientSession, Session, SessionFactory}; use crate::tasks::task::Task; diff --git a/crates/kernel/src/tasks/task.rs b/crates/kernel/src/tasks/task.rs index 1ff90759..fca8bc54 100644 --- a/crates/kernel/src/tasks/task.rs +++ b/crates/kernel/src/tasks/task.rs @@ -40,9 +40,9 @@ use moor_values::tasks::CommandError; use moor_values::tasks::CommandError::PermissionDenied; use moor_values::tasks::TaskId; use moor_values::util::parse_into_words; -use moor_values::var::Symbol; -use moor_values::var::{v_int, v_str}; -use moor_values::var::{List, Objid}; +use moor_values::Symbol; +use moor_values::{v_int, v_str}; +use moor_values::Objid; use moor_values::{NOTHING, SYSTEM_OBJECT}; use crate::builtins::BuiltinRegistry; @@ -436,13 +436,13 @@ impl Task { } Ok(verb_info) => { let arguments = parse_into_words(command); - let arguments = arguments.iter().map(|s| v_str(s)).collect::>(); + let args = arguments.iter().map(|s| v_str(s)).collect::>(); let verb_call = VerbCall { verb_name: Symbol::mk("do_command"), location: SYSTEM_OBJECT, this: SYSTEM_OBJECT, player, - args: List::from_slice(&arguments), + args, argstr: command.to_string(), caller: SYSTEM_OBJECT, }; @@ -534,7 +534,7 @@ impl Task { location: target, this: target, player, - args: List::from_slice(&parsed_command.args), + args: parsed_command.args.clone(), argstr: parsed_command.argstr.clone(), caller: player, }; @@ -653,9 +653,9 @@ mod tests { }; use moor_values::tasks::{CommandError, Event, TaskId}; use moor_values::util::BitEnum; - use moor_values::var::Error::E_DIV; - use moor_values::var::Symbol; - use moor_values::var::{v_int, v_str}; + use moor_values::Error::E_DIV; + use moor_values::Symbol; + use moor_values::{v_int, v_str}; use moor_values::{AsByteBuffer, NOTHING, SYSTEM_OBJECT}; use crate::builtins::BuiltinRegistry; diff --git a/crates/kernel/src/tasks/task_scheduler_client.rs b/crates/kernel/src/tasks/task_scheduler_client.rs index 57e59209..26968da0 100644 --- a/crates/kernel/src/tasks/task_scheduler_client.rs +++ b/crates/kernel/src/tasks/task_scheduler_client.rs @@ -18,9 +18,9 @@ use crossbeam_channel::Sender; use moor_values::model::Perms; use moor_values::tasks::{AbortLimitReason, CommandError, Exception, NarrativeEvent, TaskId}; -use moor_values::var::Objid; -use moor_values::var::Symbol; -use moor_values::var::Var; +use moor_values::Objid; +use moor_values::Symbol; +use moor_values::Var; use crate::tasks::task::Task; use crate::tasks::TaskDescription; diff --git a/crates/kernel/src/tasks/vm_host.rs b/crates/kernel/src/tasks/vm_host.rs index fc6afda8..d4d800e0 100644 --- a/crates/kernel/src/tasks/vm_host.rs +++ b/crates/kernel/src/tasks/vm_host.rs @@ -31,11 +31,11 @@ use moor_values::model::VerbInfo; use moor_values::model::WorldState; use moor_values::model::{BinaryType, ObjFlag}; use moor_values::tasks::{AbortLimitReason, Exception, TaskId}; -use moor_values::var::Error::E_MAXREC; -use moor_values::var::Var; -use moor_values::var::{v_none, Symbol}; -use moor_values::var::{List, Objid}; use moor_values::AsByteBuffer; +use moor_values::Error::E_MAXREC; +use moor_values::Var; +use moor_values::{v_none, Symbol}; +use moor_values::Objid; use crate::builtins::BuiltinRegistry; use crate::config::Config; @@ -306,7 +306,7 @@ impl VmHost { // After this we will loop around and check the result. result = self.vm_exec_state.call_builtin_function( bf_offset, - List::from_slice(&args), + args, &exec_params, world_state, session.clone(), @@ -458,8 +458,8 @@ impl VmHost { pub fn reset_time(&mut self) { self.vm_exec_state.start_time = Some(SystemTime::now()); } - pub fn args(&self) -> List { - self.vm_exec_state.top().args.clone() + pub fn args(&self) -> &Vec { + &self.vm_exec_state.top().args } } diff --git a/crates/kernel/src/textdump/load_db.rs b/crates/kernel/src/textdump/load_db.rs index 6e284c9d..9198f625 100644 --- a/crates/kernel/src/textdump/load_db.rs +++ b/crates/kernel/src/textdump/load_db.rs @@ -29,8 +29,8 @@ use moor_values::model::VerbFlag; use moor_values::model::{ArgSpec, PrepSpec, VerbArgsSpec}; use moor_values::model::{ObjAttrs, ObjFlag}; use moor_values::util::BitEnum; -use moor_values::var::Objid; -use moor_values::var::Var; +use moor_values::Objid; +use moor_values::Var; use moor_values::{AsByteBuffer, NOTHING}; use crate::textdump::read::TextdumpReaderError; diff --git a/crates/kernel/src/textdump/mod.rs b/crates/kernel/src/textdump/mod.rs index b74bc70d..52f16df9 100644 --- a/crates/kernel/src/textdump/mod.rs +++ b/crates/kernel/src/textdump/mod.rs @@ -18,8 +18,8 @@ use std::collections::BTreeMap; pub use load_db::{read_textdump, textdump_load}; -use moor_values::var::Objid; -use moor_values::var::Var; +use moor_values::Objid; +use moor_values::Var; pub use read::TextdumpReader; pub use write::TextdumpWriter; pub use write_db::make_textdump; diff --git a/crates/kernel/src/textdump/read.rs b/crates/kernel/src/textdump/read.rs index 4d99e20b..94b6199a 100644 --- a/crates/kernel/src/textdump/read.rs +++ b/crates/kernel/src/textdump/read.rs @@ -21,9 +21,9 @@ use tracing::info; use moor_compiler::Label; use moor_values::model::CompileError; use moor_values::model::WorldStateError; -use moor_values::var::{v_err, v_float, v_int, v_none, v_objid, v_str, Var, VarType}; -use moor_values::var::{v_listv, Error}; -use moor_values::var::{v_map_pairs, Objid}; +use moor_values::Objid; +use moor_values::{v_err, v_float, v_int, v_none, v_objid, v_str, Var, VarType}; +use moor_values::{v_list, v_map, Error}; use crate::textdump::{EncodingMode, Object, Propval, Textdump, Verb, Verbdef}; @@ -148,7 +148,7 @@ impl TextdumpReader { VarType::TYPE_LIST => { let l_size = self.read_num()?; let v: Vec = (0..l_size).map(|_l| self.read_var().unwrap()).collect(); - v_listv(v) + v_list(&v) } VarType::TYPE_MAP => { let num_pairs = self.read_num()?; @@ -159,7 +159,7 @@ impl TextdumpReader { (key, value) }) .collect(); - v_map_pairs(&pairs) + v_map(&pairs) } VarType::TYPE_NONE => v_none(), VarType::TYPE_FLOAT => v_float(self.read_float()?), diff --git a/crates/kernel/src/textdump/write.rs b/crates/kernel/src/textdump/write.rs index 01eb55f5..acb2a545 100644 --- a/crates/kernel/src/textdump/write.rs +++ b/crates/kernel/src/textdump/write.rs @@ -12,14 +12,14 @@ // this program. If not, see . // +use moor_values::{Objid, Sequence}; +use moor_values::{Var, VarType, Variant}; use std::collections::BTreeMap; use std::io; -use moor_values::var::Objid; -use moor_values::var::{Var, VarType, Variant}; - use crate::textdump::read::TYPE_CLEAR; use crate::textdump::{EncodingMode, Object, Propval, Textdump, Verb, Verbdef}; +use moor_values::Associative; pub struct TextdumpWriter { writer: W, @@ -61,8 +61,8 @@ impl TextdumpWriter { EncodingMode::ISO8859_1 => { // let encoding = encoding_rs::WINDOWS_1252; - let s = s.as_str(); - let s = encoding.encode(s); + let s = s.as_string(); + let s = encoding.encode(&s); let written = self.writer.write(&s.0).unwrap(); assert_eq!(written, s.0.len()); } @@ -84,8 +84,8 @@ impl TextdumpWriter { Variant::Map(m) => { writeln!(self.writer, "{}\n{}", VarType::TYPE_MAP as i64, m.len())?; for (k, v) in m.iter() { - self.write_var(k, false)?; - self.write_var(v, false)?; + self.write_var(&k, false)?; + self.write_var(&v, false)?; } } Variant::None => { diff --git a/crates/kernel/src/textdump/write_db.rs b/crates/kernel/src/textdump/write_db.rs index 7739133f..e304b2f8 100644 --- a/crates/kernel/src/textdump/write_db.rs +++ b/crates/kernel/src/textdump/write_db.rs @@ -20,8 +20,8 @@ use moor_values::model::{ArgSpec, PrepSpec, ValSet, VerbArgsSpec}; use moor_values::model::{BinaryType, VerbFlag}; use moor_values::model::{HasUuid, Named}; use moor_values::util::BitEnum; -use moor_values::var::v_none; -use moor_values::var::Objid; +use moor_values::v_none; +use moor_values::Objid; use moor_values::{AsByteBuffer, NOTHING}; use crate::textdump::{ diff --git a/crates/kernel/src/vm/activation.rs b/crates/kernel/src/vm/activation.rs index 6d4e45c8..ce3e033f 100644 --- a/crates/kernel/src/vm/activation.rs +++ b/crates/kernel/src/vm/activation.rs @@ -25,11 +25,11 @@ use moor_values::model::VerbDef; use moor_values::model::VerbInfo; use moor_values::model::{BinaryType, VerbFlag}; use moor_values::util::BitEnum; -use moor_values::var::Objid; -use moor_values::var::Symbol; -use moor_values::var::{v_empty_list, v_int, v_objid, v_str, v_string, Var, VarType}; -use moor_values::var::{v_empty_str, Error, List, Variant}; +use moor_values::Symbol; use moor_values::NOTHING; +use moor_values::{v_empty_list, v_int, v_objid, v_str, v_string, Var, VarType}; +use moor_values::{v_empty_str, Error}; +use moor_values::{v_list, Objid}; use crate::tasks::command_parse::ParsedCommand; use crate::vm::moo_frame::MooStackFrame; @@ -52,7 +52,7 @@ pub(crate) struct Activation { /// The object that is the 'player' role; that is, the active user of this task. pub(crate) player: Objid, /// The arguments to the verb or bf being called. - pub(crate) args: List, + pub(crate) args: Vec, /// The name of the verb that is currently being executed. pub(crate) verb_name: Symbol, /// The extended information about the verb that is currently being executed. @@ -169,10 +169,7 @@ impl Activation { GlobalName::verb, v_str(verb_call_request.call.verb_name.as_str()), ); - frame.set_global_variable( - GlobalName::args, - Var::new(Variant::List(verb_call_request.call.args.clone())), - ); + frame.set_global_variable(GlobalName::args, v_list(&verb_call_request.call.args)); // From the command, if any... if let Some(ref command) = verb_call_request.command { @@ -263,7 +260,7 @@ impl Activation { verb_info, verb_name: *EVAL_SYMBOL, command: None, - args: List::new(), + args: vec![], permissions, } } @@ -271,7 +268,7 @@ impl Activation { pub fn for_bf_call( bf_id: BuiltinId, bf_name: Symbol, - args: List, + args: Vec, _verb_flags: BitEnum, player: Objid, ) -> Self { diff --git a/crates/kernel/src/vm/exec_state.rs b/crates/kernel/src/vm/exec_state.rs index 5996c59a..0f688a0e 100644 --- a/crates/kernel/src/vm/exec_state.rs +++ b/crates/kernel/src/vm/exec_state.rs @@ -17,9 +17,9 @@ use std::time::{Duration, SystemTime}; use bincode::{Decode, Encode}; use daumtils::PhantomUnsync; -use moor_values::var::Var; -use moor_values::var::{Objid, Symbol}; +use moor_values::Var; use moor_values::NOTHING; +use moor_values::{Objid, Symbol}; use crate::vm::activation::{Activation, Frame}; use moor_values::tasks::TaskId; diff --git a/crates/kernel/src/vm/mod.rs b/crates/kernel/src/vm/mod.rs index 486808c6..f300e917 100644 --- a/crates/kernel/src/vm/mod.rs +++ b/crates/kernel/src/vm/mod.rs @@ -26,7 +26,7 @@ pub use exec_state::VMExecState; use moor_compiler::{BuiltinId, Name}; use moor_compiler::{Offset, Program}; use moor_values::model::VerbInfo; -use moor_values::var::{Objid, Var}; +use moor_values::{Objid, Var}; pub use vm_call::VerbExecutionRequest; pub use vm_unwind::FinallyReason; diff --git a/crates/kernel/src/vm/moo_execute.rs b/crates/kernel/src/vm/moo_execute.rs index 35d7da85..390b3720 100644 --- a/crates/kernel/src/vm/moo_execute.rs +++ b/crates/kernel/src/vm/moo_execute.rs @@ -17,21 +17,22 @@ use std::time::Duration; use tracing::debug; -use moor_compiler::{Op, ScatterLabel}; -use moor_values::model::WorldState; -use moor_values::model::WorldStateError; -use moor_values::var::Error::{E_ARGS, E_DIV, E_INVARG, E_INVIND, E_RANGE, E_TYPE, E_VARNF}; -use moor_values::var::Symbol; -use moor_values::var::Variant; -use moor_values::var::{v_bool, v_empty_list, v_err, v_int, v_list, v_none, v_obj, v_objid, Var}; -use moor_values::var::{v_empty_map, v_float}; -use moor_values::var::{v_listv, Error}; - use crate::tasks::sessions::Session; use crate::vm::activation::Frame; use crate::vm::moo_frame::{CatchType, ScopeType}; use crate::vm::vm_unwind::FinallyReason; use crate::vm::{ExecutionResult, Fork, VMExecState, VmExecParams}; +use moor_compiler::{Op, ScatterLabel}; +use moor_values::model::WorldState; +use moor_values::model::WorldStateError; + +use moor_values::Error::{E_ARGS, E_DIV, E_INVARG, E_INVIND, E_TYPE, E_VARNF}; +use moor_values::Variant; +use moor_values::{ + v_bool, v_empty_list, v_empty_map, v_err, v_float, v_int, v_list, v_none, v_obj, v_objid, + IndexMode, Sequence, +}; +use moor_values::{Symbol, VarType}; macro_rules! binary_bool_op { ( $f:ident, $op:tt ) => { @@ -57,18 +58,6 @@ macro_rules! binary_var_op { }; } -#[inline] -pub(crate) fn one_to_zero_index(v: &Var) -> Result { - let Variant::Int(index) = v.variant() else { - return Err(E_TYPE); - }; - let index = index - 1; - if index < 0 { - return Err(E_RANGE); - } - Ok(index as usize) -} - /// Main VM opcode execution for MOO stack frames. The actual meat of the MOO virtual machine. pub fn moo_frame_execute( exec_params: &VmExecParams, @@ -201,7 +190,7 @@ pub fn moo_frame_execute( // Track iteration count for range; set id to current list element for the count, // then increment the count, rewind the program counter to the top of the loop, and // continue. - f.set_env(id, l.get(count).unwrap().clone()); + f.set_env(id, l.index(count).unwrap().clone()); f.poke(0, v_int((count + 1) as i64)); } Op::ForRange { @@ -299,62 +288,51 @@ pub fn moo_frame_execute( Op::ImmEmptyList => f.push(v_empty_list()), Op::ListAddTail => { let (tail, list) = (f.pop(), f.peek_top_mut()); - let Variant::List(ref mut list) = list.variant_mut() else { + if list.type_code() != VarType::TYPE_LIST { f.pop(); return state.push_error(E_TYPE); - }; - + } // TODO: quota check SVO_MAX_LIST_CONCAT -> E_QUOTA in list add and append - let result = list.push(tail); - f.poke(0, result); + let result = list.push(&tail); + match result { + Ok(v) => { + f.poke(0, v); + } + Err(e) => { + f.pop(); + return state.push_error(e); + } + } } Op::ListAppend => { let (tail, list) = (f.pop(), f.peek_top_mut()); - let Variant::List(list) = list.variant_mut() else { + // Don't allow strings here. + if tail.type_code() != list.type_code() || list.type_code() != VarType::TYPE_LIST { f.pop(); - - return state.push_error(E_TYPE); - }; - - let Variant::List(tail) = tail.take_variant() else { - f.pop(); - return state.push_error(E_TYPE); - }; - + } let new_list = list.append(&tail); - f.poke(0, new_list); + match new_list { + Ok(v) => { + f.poke(0, v); + } + Err(e) => { + f.pop(); + return state.push_error(e); + } + } } Op::IndexSet => { let (rhs, index, lhs) = (f.pop(), f.pop(), f.peek_top_mut()); - match lhs.variant() { - Variant::Map(m) => { - if matches!(index.variant(), Variant::Map(_) | Variant::List(_)) { - f.pop(); - return state.push_error(E_TYPE); - } else { - let nmap = m.insert(index, rhs.clone()); - f.poke(0, nmap); - } + let result = lhs.index_set(&index, &rhs, IndexMode::OneBased); + match result { + Ok(v) => { + f.poke(0, v); } - _ => { - let i = match one_to_zero_index(&index) { - Ok(i) => i, - Err(e) => { - f.pop(); - return state.push_error(e); - } - }; - match lhs.index_set(i, rhs) { - Ok(v) => { - f.poke(0, v); - } - Err(e) => { - f.pop(); - return state.push_error(e); - } - } + Err(e) => { + f.pop(); + return state.push_error(e); } } } @@ -367,16 +345,16 @@ pub fn moo_frame_execute( } Op::MapInsert => { let (value, key, map) = (f.pop(), f.pop(), f.peek_top_mut()); - if matches!(key.variant(), Variant::Map(_) | Variant::List(_)) { - f.pop(); - return state.push_error(E_TYPE); + let result = map.index_set(&key, &value, IndexMode::OneBased); + match result { + Ok(v) => { + f.poke(0, v); + } + Err(e) => { + f.pop(); + return state.push_error(e); + } } - let Variant::Map(map) = map.variant_mut() else { - f.pop(); - return state.push_error(E_TYPE); - }; - let result = map.insert(key, value); - f.poke(0, result); } Op::PutTemp => { f.temp = f.peek_top().clone(); @@ -406,12 +384,16 @@ pub fn moo_frame_execute( } Op::In => { let (lhs, rhs) = (f.pop(), f.peek_top()); - let r = lhs.index_in(rhs); - if let Variant::Err(e) = r.variant() { - f.pop(); - return state.push_error(*e); + let r = lhs.index_in(rhs, false, IndexMode::OneBased); + match r { + Ok(v) => { + f.poke(0, v); + } + Err(e) => { + f.pop(); + return state.push_error(e); + } } - f.poke(0, r); } Op::Mul => { binary_var_op!(self, f, state, mul); @@ -484,114 +466,49 @@ pub fn moo_frame_execute( } Op::PushRef => { let (index, value) = f.peek2(); - - match value.variant() { - Variant::Map(m) => match m.get(index) { - Some(v) => { - f.push(v.clone()); - } - None => { - f.pop(); - return state.push_error(E_RANGE); - } - }, - _ => { - let index = match one_to_zero_index(index) { - Ok(i) => i, - Err(e) => return state.push_error(e), - }; - match value.index(index) { - Err(e) => return state.push_error(e), - Ok(v) => f.push(v), - } + let result = value.index(index, IndexMode::OneBased); + match result { + Ok(v) => f.push(v), + Err(e) => { + f.pop(); + return state.push_error(e); } } } Op::Ref => { let (index, value) = (f.pop(), f.peek_top()); - match value.variant() { - Variant::Map(m) => { - let v = match m.get(&index) { - Some(v) => v.clone(), - None => { - f.pop(); - return state.push_error(E_RANGE); - } - }; - f.poke(0, v); - } - _ => { - let index = match one_to_zero_index(&index) { - Ok(i) => i, - Err(e) => { - f.pop(); - return state.push_error(e); - } - }; - - match value.index(index) { - Err(e) => { - f.pop(); - return state.push_error(e); - } - Ok(v) => f.poke(0, v), - } + let result = value.index(&index, IndexMode::OneBased); + match result { + Ok(v) => f.poke(0, v), + Err(e) => { + f.pop(); + return state.push_error(e); } } } Op::RangeRef => { let (to, from, base) = (f.pop(), f.pop(), f.peek_top()); - match base.variant() { - Variant::Map(m) => { - let r = m.range(from, to); - f.poke(0, r); - } - _ => { - match (to.variant(), from.variant()) { - (Variant::Int(to), Variant::Int(from)) => { - match base.range(*from, *to) { - Err(e) => { - f.pop(); - return state.push_error(e); - } - Ok(v) => f.poke(0, v), - } - } - (_, _) => return state.push_error(E_TYPE), - }; - } + let result = base.range(&from, &to, IndexMode::OneBased); + if let Err(e) = result { + f.pop(); + return state.push_error(e); } + f.poke(0, result.unwrap()); } Op::RangeSet => { let (value, to, from, base) = (f.pop(), f.pop(), f.pop(), f.peek_top()); - - match base.variant() { - Variant::Map(_) => { - f.pop(); - return state.push_error(E_TYPE); - } - _ => match (to.variant(), from.variant()) { - (Variant::Int(to), Variant::Int(from)) => { - match base.range_set(value, *from, *to) { - Err(e) => { - f.pop(); - return state.push_error(e); - } - Ok(v) => f.poke(0, v), - } - } - _ => { - f.pop(); - return state.push_error(E_TYPE); - } - }, + let result = base.range_set(&from, &to, &value, IndexMode::OneBased); + if let Err(e) = result { + f.pop(); + return state.push_error(e); } + f.poke(0, result.unwrap()); } Op::Length(offset) => { let v = f.peek_abs(offset.0 as usize); match v.len() { - Ok(l) => f.push(l), + Ok(l) => f.push(v_int(l as i64)), Err(e) => return state.push_error(e), } } @@ -605,7 +522,7 @@ pub fn moo_frame_execute( let Variant::Obj(obj) = obj.variant() else { return state.push_error(E_INVIND); }; - let propname = Symbol::mk_case_insensitive(propname.as_str()); + let propname = Symbol::mk_case_insensitive(&propname.as_string()); let result = world_state.retrieve_property(a.permissions, *obj, propname); match result { Ok(v) => { @@ -629,7 +546,7 @@ pub fn moo_frame_execute( let Variant::Obj(obj) = obj.variant() else { return state.push_error(E_INVIND); }; - let propname = Symbol::mk_case_insensitive(propname.as_str()); + let propname = Symbol::mk_case_insensitive(&propname.as_string()); let result = world_state.retrieve_property(a.permissions, *obj, propname); match result { Ok(v) => { @@ -654,7 +571,7 @@ pub fn moo_frame_execute( } }; - let propname = Symbol::mk_case_insensitive(propname.as_str()); + let propname = Symbol::mk_case_insensitive(&propname.as_string()); let update_result = world_state.update_property(a.permissions, *obj, propname, &rhs.clone()); @@ -711,7 +628,7 @@ pub fn moo_frame_execute( return state.push_error(E_TYPE); } }; - let verb = Symbol::mk_case_insensitive(verb.as_str()); + let verb = Symbol::mk_case_insensitive(&verb.as_string()); return state.prepare_call_verb(world_state, *obj, verb, args.clone()); } Op::Return => { @@ -732,7 +649,7 @@ pub fn moo_frame_execute( }; return state.call_builtin_function( *id, - args.clone(), + args.iter().collect(), exec_params, world_state, session, @@ -896,7 +813,7 @@ pub fn moo_frame_execute( }; v.push(rest.clone()); } - let rest = v_listv(v); + let rest = v_list(&v); f.set_env(id, rest); } ScatterLabel::Required(id) => { diff --git a/crates/kernel/src/vm/moo_frame.rs b/crates/kernel/src/vm/moo_frame.rs index ce17c4d9..fa570e36 100644 --- a/crates/kernel/src/vm/moo_frame.rs +++ b/crates/kernel/src/vm/moo_frame.rs @@ -21,8 +21,8 @@ use daumtils::{BitArray, Bitset16}; use crate::vm::FinallyReason; use moor_compiler::Name; use moor_compiler::{GlobalName, Label, Op, Program}; -use moor_values::var::Error::E_VARNF; -use moor_values::var::{v_none, Error, Var}; +use moor_values::Error::E_VARNF; +use moor_values::{v_none, Error, Var}; /// The MOO stack-frame specific portions of the activation: /// the value stack, local variables, program, program counter, handler stack, etc. diff --git a/crates/kernel/src/vm/vm_call.rs b/crates/kernel/src/vm/vm_call.rs index fe122cc7..52300062 100644 --- a/crates/kernel/src/vm/vm_call.rs +++ b/crates/kernel/src/vm/vm_call.rs @@ -16,14 +16,14 @@ use std::sync::Arc; use tracing::trace; -use moor_compiler::{BuiltinId, Program, BUILTINS}; +use moor_compiler::{to_literal, BuiltinId, Program, BUILTINS}; use moor_values::model::VerbInfo; use moor_values::model::WorldState; use moor_values::model::WorldStateError; -use moor_values::var::v_int; -use moor_values::var::Error::{E_INVIND, E_PERM, E_VERBNF}; -use moor_values::var::Symbol; -use moor_values::var::{List, Objid}; +use moor_values::Error::{E_INVIND, E_PERM, E_VERBNF}; +use moor_values::Symbol; +use moor_values::{v_int, Var}; +use moor_values::{List, Objid}; use crate::builtins::{BfCallState, BfErr, BfRet}; use crate::tasks::command_parse::ParsedCommand; @@ -34,9 +34,9 @@ use crate::vm::vm_unwind::FinallyReason; use crate::vm::{ExecutionResult, Fork}; use crate::vm::{VMExecState, VmExecParams}; -pub(crate) fn args_literal(args: &List) -> String { +pub(crate) fn args_literal(args: &[Var]) -> String { args.iter() - .map(|v| v.to_literal()) + .map(to_literal) .collect::>() .join(", ") } @@ -79,7 +79,7 @@ impl VMExecState { location: this, this, player: self.top().player, - args, + args: args.iter().collect(), // caller her is current-activation 'this', not activation caller() ... // unless we're a builtin, in which case we're #-1. argstr: "".to_string(), @@ -163,7 +163,7 @@ impl VMExecState { location: parent, this: self.top().this, player: self.top().player, - args: args.clone(), + args: args.iter().collect(), argstr: "".to_string(), caller, }; @@ -223,7 +223,7 @@ impl VMExecState { pub(crate) fn call_builtin_function( &mut self, bf_id: BuiltinId, - args: List, + args: Vec, exec_args: &VmExecParams, world_state: &mut dyn WorldState, session: Arc, @@ -255,7 +255,7 @@ impl VMExecState { world_state, session: session.clone(), // TODO: avoid copy here by using List inside BfCallState - args: args.iter().collect(), + args, task_scheduler_client: exec_args.task_scheduler_client.clone(), config: exec_args.config.clone(), }; @@ -304,7 +304,7 @@ impl VMExecState { world_state, session: sessions, // TODO: avoid copy here by using List inside BfCallState - args: args.iter().collect(), + args, task_scheduler_client: exec_args.task_scheduler_client.clone(), config: exec_args.config.clone(), }; diff --git a/crates/kernel/src/vm/vm_test.rs b/crates/kernel/src/vm/vm_test.rs index cc2d4b40..d4cf526a 100644 --- a/crates/kernel/src/vm/vm_test.rs +++ b/crates/kernel/src/vm/vm_test.rs @@ -21,11 +21,11 @@ mod tests { use moor_values::model::{BinaryType, VerbFlag}; use moor_values::model::{WorldState, WorldStateSource}; use moor_values::util::BitEnum; - use moor_values::var::Error::E_DIV; - use moor_values::var::{ - v_bool, v_empty_list, v_err, v_int, v_list, v_none, v_obj, v_objid, v_str, Var, + use moor_values::Error::E_DIV; + use moor_values::Objid; + use moor_values::{ + v_bool, v_empty_list, v_err, v_int, v_list, v_map, v_none, v_obj, v_objid, v_str, Var, }; - use moor_values::var::{v_map_pairs, Objid}; use moor_values::NOTHING; use moor_values::{AsByteBuffer, SYSTEM_OBJECT}; @@ -39,7 +39,7 @@ mod tests { use moor_compiler::{compile, UnboundNames}; use moor_compiler::{CompileOptions, Names}; use moor_db_wiredtiger::WiredTigerDB; - use moor_values::var::Symbol; + use moor_values::Symbol; use test_case::test_case; fn mk_program(main_vector: Vec, literals: Vec, var_names: Names) -> Program { @@ -1126,11 +1126,11 @@ mod tests { #[test_case("return -9223372036854775808;", v_int(i64::MIN); "minint")] #[test_case("return [ 1 -> 2][1];", v_int(2); "map index")] #[test_case("return [ 0 -> 1, 1 -> 2, 2 -> 3, 3 -> 4][1..3];", - v_map_pairs(&[(v_int(1),v_int(2)), (v_int(2),v_int(3))]); "map range")] + v_map(&[(v_int(1),v_int(2)), (v_int(2),v_int(3))]); "map range")] #[test_case(r#"m = [ 1 -> "one", 2 -> "two", 3 -> "three" ]; m[1] = "abc"; return m;"#, - v_map_pairs(&[(v_int(1),v_str("abc")), (v_int(2),v_str("two")), (v_int(3),v_str("three"))]); "map assignment")] + v_map(&[(v_int(1),v_str("abc")), (v_int(2),v_str("two")), (v_int(3),v_str("three"))]); "map assignment")] #[test_case("return [ 0 -> 1, 1 -> 2, 2 -> 3, 3 -> 4][1..$];", - v_map_pairs(&[(v_int(1),v_int(2)), (v_int(2),v_int(3),), (v_int(3),v_int(4))]); "map range to end")] + v_map(&[(v_int(1),v_int(2)), (v_int(2),v_int(3),), (v_int(3),v_int(4))]); "map range to end")] #[test_case("l = {1,2,3}; l[2..3] = {6, 7, 8, 9}; return l;", v_list(&[v_int(1), v_int(6), v_int(7), v_int(8), v_int(9)]); "list assignment to range")] fn test_run(program: &str, expected_result: Var) { @@ -1145,4 +1145,22 @@ mod tests { ); assert_eq!(result, Ok(expected_result)); } + + #[test] + fn test_list_assignment_to_range() { + let program = r#"l = {1,2,3}; l[2..3] = {6, 7, 8, 9}; return l;"#; + let mut state = world_with_test_program(program); + let session = Arc::new(NoopClientSession::new()); + let result = call_verb( + state.as_mut(), + session, + Arc::new(BuiltinRegistry::new()), + "test", + vec![], + ); + assert_eq!( + result, + Ok(v_list(&[v_int(1), v_int(6), v_int(7), v_int(8), v_int(9)])) + ); + } } diff --git a/crates/kernel/src/vm/vm_unwind.rs b/crates/kernel/src/vm/vm_unwind.rs index c7ce468e..397ad1ff 100644 --- a/crates/kernel/src/vm/vm_unwind.rs +++ b/crates/kernel/src/vm/vm_unwind.rs @@ -17,10 +17,9 @@ use moor_compiler::{Label, Offset, BUILTINS}; use moor_values::model::Named; use moor_values::model::VerbFlag; use moor_values::tasks::Exception; -use moor_values::var::v_listv; -use moor_values::var::{v_err, v_int, v_list, v_none, v_objid, v_str, Var}; -use moor_values::var::{Error, ErrorPack}; use moor_values::NOTHING; +use moor_values::{v_err, v_int, v_list, v_none, v_objid, v_str, Var}; +use moor_values::{Error, ErrorPack}; use tracing::trace; use crate::vm::activation::{Activation, Frame}; @@ -74,7 +73,7 @@ impl VMExecState { } }; - stack_list.push(v_listv(traceback_entry)); + stack_list.push(v_list(&traceback_entry)); } stack_list } diff --git a/crates/kernel/tests/textdump.rs b/crates/kernel/tests/textdump.rs index 11025c0f..0e70b6d7 100644 --- a/crates/kernel/tests/textdump.rs +++ b/crates/kernel/tests/textdump.rs @@ -34,8 +34,8 @@ mod test { use moor_values::model::WorldStateSource; use moor_values::model::{CommitResult, ValSet}; use moor_values::model::{HasUuid, Named}; - use moor_values::var::Objid; - use moor_values::var::Symbol; + use moor_values::Objid; + use moor_values::Symbol; use moor_values::{AsByteBuffer, SYSTEM_OBJECT}; fn get_minimal_db() -> File { diff --git a/crates/kernel/testsuite/common/mod.rs b/crates/kernel/testsuite/common/mod.rs index 0f413a21..64397dbd 100644 --- a/crates/kernel/testsuite/common/mod.rs +++ b/crates/kernel/testsuite/common/mod.rs @@ -37,8 +37,8 @@ use moor_values::model::Named; use moor_values::model::VerbArgsSpec; use moor_values::model::WorldStateSource; use moor_values::model::{BinaryType, VerbFlag}; -use moor_values::var::Objid; -use moor_values::var::Symbol; +use moor_values::Objid; +use moor_values::Symbol; use moor_values::{AsByteBuffer, SYSTEM_OBJECT}; #[allow(dead_code)] diff --git a/crates/kernel/testsuite/moot/map.moot b/crates/kernel/testsuite/moot/map.moot index 09e908fb..63d646b3 100644 --- a/crates/kernel/testsuite/moot/map.moot +++ b/crates/kernel/testsuite/moot/map.moot @@ -127,8 +127,10 @@ "[map]" // Note: item order is different here than in Stunt (but stable). That's *probably* fine. // Seems like ordering difference between different key types. +// Note that stunt's is: +// ""[5 -> 5, #-1 -> #-1, 3.14 -> 3.14, "1" -> {}, "2" -> []]"" ; return toliteral($tmp); -"[#-1 -> #-1, \"1\" -> {}, \"2\" -> [], 5 -> 5, 3.14 -> 3.14]" +"[#-1 -> #-1, 5 -> 5, 3.14 -> 3.14, \"1\" -> {}, \"2\" -> []]" // test_that_assignment_copies ; x = [$nothing -> $nothing, "2" -> [], "1" -> {}, 5 -> 5, 3.14 -> 3.14]; y = x; return x == y && "yes" || "no"; diff --git a/crates/kernel/testsuite/moot_suite.rs b/crates/kernel/testsuite/moot_suite.rs index 4bfe76c2..6183c077 100644 --- a/crates/kernel/testsuite/moot_suite.rs +++ b/crates/kernel/testsuite/moot_suite.rs @@ -23,6 +23,7 @@ use eyre::Context; #[cfg(feature = "relbox")] use common::create_relbox_db; use common::{create_wiredtiger_db, testsuite_dir}; +use moor_compiler::to_literal; use moor_db::Database; use moor_kernel::tasks::sessions::{SessionError, SessionFactory}; use moor_kernel::tasks::NoopTasksDb; @@ -36,7 +37,7 @@ use moor_kernel::{ SchedulerClient, }; use moor_moot::{execute_moot_test, MootRunner}; -use moor_values::var::{v_none, Objid, Var}; +use moor_values::{v_none, Objid, Var}; mod common; @@ -102,11 +103,11 @@ impl MootRunner for SchedulerMootRunner { unimplemented!("Not supported on SchedulerMootRunner"); } - fn read_eval_result(&mut self, player: Objid) -> eyre::Result> { + fn read_eval_result(&mut self, player: Objid) -> eyre::Result> { Ok(self .eval_result .take() - .inspect(|var| eprintln!("{player} << {var}"))) + .inspect(|var| eprintln!("{player} << {}", to_literal(var)))) } } diff --git a/crates/moot/src/lib.rs b/crates/moot/src/lib.rs index 5ce0e73a..11d386ff 100644 --- a/crates/moot/src/lib.rs +++ b/crates/moot/src/lib.rs @@ -25,7 +25,7 @@ use std::{ }; use eyre::{eyre, ContextCompat, WrapErr}; -use moor_values::var::Objid; +use moor_values::Objid; use pretty_assertions::assert_eq; diff --git a/crates/rpc-common/src/lib.rs b/crates/rpc-common/src/lib.rs index 18133bb3..4b86a0a2 100644 --- a/crates/rpc-common/src/lib.rs +++ b/crates/rpc-common/src/lib.rs @@ -14,8 +14,8 @@ use bincode::{Decode, Encode}; use moor_values::tasks::{NarrativeEvent, SchedulerError}; -use moor_values::var::Objid; -use moor_values::var::Var; +use moor_values::Objid; +use moor_values::Var; use std::time::SystemTime; use thiserror::Error; diff --git a/crates/telnet-host/Cargo.toml b/crates/telnet-host/Cargo.toml index f1303b08..27dc032a 100644 --- a/crates/telnet-host/Cargo.toml +++ b/crates/telnet-host/Cargo.toml @@ -12,6 +12,7 @@ rust-version.workspace = true description = "A server which presents a classic LambdaMOO-style line-based TCP interface for interacting with a moor daemon." [dependencies] +moor-compiler = { path = "../compiler" } moor-moot = { path = "../moot" } moor-values = { path = "../values" } rpc-async-client = { path = "../rpc-async-client" } diff --git a/crates/telnet-host/src/telnet.rs b/crates/telnet-host/src/telnet.rs index 993e6f4b..bb6fb206 100644 --- a/crates/telnet-host/src/telnet.rs +++ b/crates/telnet-host/src/telnet.rs @@ -22,18 +22,10 @@ use eyre::Context; use futures_util::stream::{SplitSink, SplitStream}; use futures_util::SinkExt; use futures_util::StreamExt; -use termimad::MadSkin; -use tmq::subscribe::Subscribe; -use tmq::{request, subscribe}; -use tokio::net::{TcpListener, TcpStream}; -use tokio::select; -use tokio_util::codec::{Framed, LinesCodec}; -use tracing::{debug, error, info, trace, warn}; -use uuid::Uuid; - +use moor_compiler::to_literal; use moor_values::tasks::{AbortLimitReason, CommandError, Event, SchedulerError, VerbProgramError}; use moor_values::util::parse_into_words; -use moor_values::var::{Objid, Symbol, Variant}; +use moor_values::{Objid, Symbol, Variant}; use rpc_async_client::pubsub_client::{broadcast_recv, events_recv}; use rpc_async_client::rpc_client::RpcSendClient; use rpc_common::RpcRequest::ConnectionEstablish; @@ -42,6 +34,14 @@ use rpc_common::{ RpcResult, BROADCAST_TOPIC, }; use rpc_common::{RpcRequest, RpcResponse}; +use termimad::MadSkin; +use tmq::subscribe::Subscribe; +use tmq::{request, subscribe}; +use tokio::net::{TcpListener, TcpStream}; +use tokio::select; +use tokio_util::codec::{Framed, LinesCodec}; +use tracing::{debug, error, info, trace, warn}; +use uuid::Uuid; /// Out of band messages are prefixed with this string, e.g. for MCP clients. const OUT_OF_BAND_PREFIX: &str = "#$#"; @@ -124,7 +124,7 @@ impl TelnetConnection { // literal form (for e.g. lists, objrefs, etc) match msg.variant() { Variant::Str(msg_text) => { - let formatted = output_format(msg_text.as_str(), content_type); + let formatted = output_format(&msg_text.as_string(), content_type); self.write .send(formatted) .await @@ -136,7 +136,7 @@ impl TelnetConnection { trace!("Non-string in list output"); continue; }; - let formatted = output_format(line.as_str(), content_type); + let formatted = output_format(&line.as_string(), content_type); self.write .send(formatted) .await @@ -145,7 +145,7 @@ impl TelnetConnection { } _ => { self.write - .send(msg.to_literal()) + .send(to_literal(&msg)) .await .with_context(|| "Unable to send message to client")?; } diff --git a/crates/values/Cargo.toml b/crates/values/Cargo.toml index bd055ced..f10b834b 100644 --- a/crates/values/Cargo.toml +++ b/crates/values/Cargo.toml @@ -18,6 +18,8 @@ bytes.workspace = true daumtils.workspace = true decorum.workspace = true enum-primitive-derive.workspace = true +flatbuffers.workspace = true +flexbuffers.workspace = true im.workspace = true itertools.workspace = true lazy_static.workspace = true diff --git a/crates/values/src/lib.rs b/crates/values/src/lib.rs index ddfed29d..cc5dd796 100644 --- a/crates/values/src/lib.rs +++ b/crates/values/src/lib.rs @@ -19,13 +19,18 @@ pub use encode::{ BINCODE_CONFIG, }; -use crate::var::Objid; +pub use var::{ + v_bool, v_empty_list, v_empty_map, v_empty_str, v_err, v_float, v_floatr, v_int, v_list, + v_list_iter, v_map, v_none, v_obj, v_objid, v_str, v_string, Associative, ErrorPack, IndexMode, + List, Map, Sequence, Str, Var, Variant, +}; +pub use var::{Error, Objid, Symbol, VarType}; mod encode; pub mod model; pub mod tasks; pub mod util; -pub mod var; +mod var; /// When encoding or decoding types to/from data or network, this is a version tag put into headers /// for validity / version checking. diff --git a/crates/values/src/model/defset.rs b/crates/values/src/model/defset.rs index 10f1e3a1..639e1c9d 100644 --- a/crates/values/src/model/defset.rs +++ b/crates/values/src/model/defset.rs @@ -14,8 +14,8 @@ use crate::encode::{DecodingError, EncodingError}; use crate::model::ValSet; -use crate::var::Symbol; use crate::AsByteBuffer; +use crate::Symbol; use bytes::BufMut; use bytes::Bytes; use itertools::Itertools; diff --git a/crates/values/src/model/mod.rs b/crates/values/src/model/mod.rs index e8c58c4b..a5d409ff 100644 --- a/crates/values/src/model/mod.rs +++ b/crates/values/src/model/mod.rs @@ -41,7 +41,7 @@ mod verbdef; mod verbs; mod world_state; -use crate::var::Symbol; +use crate::Symbol; pub use world_state::WorldStateError; /// The result code from a commit/complete operation on the world's state. diff --git a/crates/values/src/model/objects.rs b/crates/values/src/model/objects.rs index 8a48ace5..aca88cb5 100644 --- a/crates/values/src/model/objects.rs +++ b/crates/values/src/model/objects.rs @@ -21,7 +21,7 @@ use bytes::Bytes; use enum_primitive_derive::Primitive; use crate::util::BitEnum; -use crate::var::Objid; +use crate::Objid; #[derive(Debug, Ord, PartialOrd, Copy, Clone, Eq, PartialEq, Hash, Primitive, Encode, Decode)] pub enum ObjFlag { diff --git a/crates/values/src/model/objset.rs b/crates/values/src/model/objset.rs index 21fb5898..57efeff6 100644 --- a/crates/values/src/model/objset.rs +++ b/crates/values/src/model/objset.rs @@ -14,8 +14,8 @@ use crate::encode::{DecodingError, EncodingError}; use crate::model::ValSet; -use crate::var::Objid; use crate::AsByteBuffer; +use crate::Objid; use bytes::BufMut; use bytes::Bytes; use itertools::Itertools; diff --git a/crates/values/src/model/permissions.rs b/crates/values/src/model/permissions.rs index a649f790..19ca2a42 100644 --- a/crates/values/src/model/permissions.rs +++ b/crates/values/src/model/permissions.rs @@ -17,7 +17,7 @@ use crate::model::props::PropFlag; use crate::model::verbs::VerbFlag; use crate::model::{PropPerms, WorldStateError}; use crate::util::BitEnum; -use crate::var::Objid; +use crate::Objid; /// Combination of who a set of permissions is for, and what permissions they have. #[derive(Debug, Clone, Eq, PartialEq)] diff --git a/crates/values/src/model/propdef.rs b/crates/values/src/model/propdef.rs index dccec959..658beb62 100644 --- a/crates/values/src/model/propdef.rs +++ b/crates/values/src/model/propdef.rs @@ -14,8 +14,8 @@ use crate::encode::{DecodingError, EncodingError}; use crate::model::defset::{Defs, HasUuid, Named}; -use crate::var::Objid; -use crate::var::Symbol; +use crate::Objid; +use crate::Symbol; use crate::{AsByteBuffer, DATA_LAYOUT_VERSION}; use binary_layout::{binary_layout, Field}; use bytes::Bytes; @@ -144,9 +144,9 @@ mod tests { use crate::model::defset::HasUuid; use crate::model::propdef::{PropDef, PropDefs}; use crate::model::ValSet; - use crate::var::Objid; - use crate::var::Symbol; use crate::AsByteBuffer; + use crate::Objid; + use crate::Symbol; use bytes::Bytes; use uuid::Uuid; diff --git a/crates/values/src/model/props.rs b/crates/values/src/model/props.rs index d0429a40..436df83f 100644 --- a/crates/values/src/model/props.rs +++ b/crates/values/src/model/props.rs @@ -13,8 +13,8 @@ // use crate::util::BitEnum; -use crate::var::Objid; -use crate::var::Var; +use crate::Objid; +use crate::Var; use crate::{AsByteBuffer, DecodingError, EncodingError}; use binary_layout::binary_layout; use bincode::{Decode, Encode}; diff --git a/crates/values/src/model/verbdef.rs b/crates/values/src/model/verbdef.rs index 4a725580..eb02670c 100644 --- a/crates/values/src/model/verbdef.rs +++ b/crates/values/src/model/verbdef.rs @@ -18,8 +18,8 @@ use crate::model::r#match::VerbArgsSpec; use crate::model::verbs::{BinaryType, VerbFlag}; use crate::util::verbname_cmp; use crate::util::BitEnum; -use crate::var::Objid; -use crate::var::Symbol; +use crate::Objid; +use crate::Symbol; use crate::{AsByteBuffer, DATA_LAYOUT_VERSION}; use binary_layout::{binary_layout, Field}; use bytes::BufMut; @@ -208,8 +208,8 @@ mod tests { use crate::model::verbs::VerbFlag; use crate::model::ValSet; use crate::util::BitEnum; - use crate::var::Objid; use crate::AsByteBuffer; + use crate::Objid; use bytes::Bytes; #[test] diff --git a/crates/values/src/model/verbs.rs b/crates/values/src/model/verbs.rs index 63137482..9f73e947 100644 --- a/crates/values/src/model/verbs.rs +++ b/crates/values/src/model/verbs.rs @@ -15,8 +15,8 @@ use crate::encode::{DecodingError, EncodingError}; use crate::model::r#match::VerbArgsSpec; use crate::util::BitEnum; -use crate::var::Objid; -use crate::var::Symbol; +use crate::Objid; +use crate::Symbol; use binary_layout::LayoutAs; use bincode::{Decode, Encode}; use enum_primitive_derive::Primitive; diff --git a/crates/values/src/model/world_state.rs b/crates/values/src/model/world_state.rs index 3eb9fd0c..aaa586ef 100644 --- a/crates/values/src/model/world_state.rs +++ b/crates/values/src/model/world_state.rs @@ -27,9 +27,9 @@ use crate::model::verbs::{BinaryType, VerbAttrs, VerbFlag}; use crate::model::{CommitResult, PropPerms}; use crate::model::{ObjAttr, Vid}; use crate::util::BitEnum; -use crate::var::Symbol; -use crate::var::Var; -use crate::var::{Error, Objid}; +use crate::Symbol; +use crate::Var; +use crate::{Error, Objid}; /// Errors related to the world state and operations on it. #[derive(Error, Debug, Eq, PartialEq, Clone, Decode, Encode)] diff --git a/crates/values/src/tasks/errors.rs b/crates/values/src/tasks/errors.rs index ebeb2771..5f9ddcd3 100644 --- a/crates/values/src/tasks/errors.rs +++ b/crates/values/src/tasks/errors.rs @@ -14,7 +14,7 @@ use crate::model::{CompileError, WorldStateError}; use crate::tasks::TaskId; -use crate::var::{Error, Var}; +use crate::{Error, Var}; use bincode::{Decode, Encode}; use std::fmt::Display; use std::time::Duration; diff --git a/crates/values/src/tasks/events.rs b/crates/values/src/tasks/events.rs index 8ee36c36..b3c5bc3d 100644 --- a/crates/values/src/tasks/events.rs +++ b/crates/values/src/tasks/events.rs @@ -12,7 +12,7 @@ // this program. If not, see . // -use crate::var::{Objid, Symbol, Var}; +use crate::{Objid, Symbol, Var}; use bincode::{Decode, Encode}; use std::time::SystemTime; diff --git a/crates/values/src/var/list.rs b/crates/values/src/var/list.rs index 220eea81..1844a4bb 100644 --- a/crates/values/src/var/list.rs +++ b/crates/values/src/var/list.rs @@ -12,146 +12,207 @@ // this program. If not, see . // -use std::fmt::{Display, Formatter, Result as FmtResult}; -use std::hash::{Hash, Hasher}; - -use bincode::{Decode, Encode}; -use bytes::Bytes; - -#[allow(unused_imports)] -use crate::var::list_impl_buffer::ListImplBuffer; -#[allow(unused_imports)] -use crate::var::list_impl_vector::ListImplVector; - +use crate::v_list_iter; +use crate::var::storage::VarBuffer; +use crate::var::var::Var; use crate::var::variant::Variant; -use crate::var::Var; -use crate::{AsByteBuffer, DecodingError, EncodingError}; - -#[cfg(feature = "list_impl_buffer")] -type ListImpl = ListImplBuffer; - -#[cfg(not(feature = "list_impl_buffer"))] -type ListImpl = ListImplVector; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct List(ListImpl); +use crate::var::Error::E_RANGE; +use crate::var::Sequence; +use crate::var::{Error, VarType}; +use bytes::Bytes; +use flexbuffers::{BuilderOptions, Reader, VectorReader}; +use num_traits::ToPrimitive; +use std::cmp::max; +use std::hash::Hash; + +#[derive(Clone)] +pub struct List { + // Reader must be boxed to avoid overfilling the stack. + pub reader: Box>, +} impl List { - pub fn new() -> Self { - Self(ListImpl::new()) - } - - pub fn len(&self) -> usize { - self.0.len() + pub fn build(values: &[Var]) -> Var { + let mut builder = flexbuffers::Builder::new(BuilderOptions::empty()); + let mut vb = builder.start_vector(); + vb.push(VarType::TYPE_LIST as u8); + let mut lv = vb.start_vector(); + for v in values { + let v = v.variant(); + v.push_item(&mut lv); + } + lv.end_vector(); + vb.end_vector(); + let buf = builder.take_buffer(); + let buf = Bytes::from(buf); + let reader = Reader::get_root(VarBuffer(buf)).unwrap(); + let v = Variant::from_reader(reader); + Var(v) } - pub fn is_empty(&self) -> bool { - self.0.is_empty() + pub fn iter(&self) -> impl Iterator + '_ { + (0..self.len()).map(move |i| self.index(i).unwrap()) } - pub fn get(&self, index: usize) -> Option { - self.0.get(index) + /// Remove the first found instance of `item` from the list. + pub fn set_remove(&self, item: &Var) -> Result { + let mut found = false; + let set_removed_iter = self.iter().filter_map(|v| { + if v == *item && !found { + found = true; + None + } else { + Some(v) + } + }); + Ok(v_list_iter(set_removed_iter)) } - pub fn from_slice(vec: &[Var]) -> List { - Self(ListImpl::from_slice(vec)) + /// Add `item` to the list but only if it's not already there. + pub fn set_add(&self, item: &Var) -> Result { + // Is the item already in the list? If so, just clone. + if self.iter().any(|v| v == *item) { + return Ok(Var(Variant::List(self.clone()))); + } + let set_added_iter = self.iter().chain(std::iter::once(item.clone())); + Ok(v_list_iter(set_added_iter)) } - // expensive because we need to extend both buffer length and the offsets length... - pub fn push(&mut self, v: Var) -> Var { - Var::new(Variant::List(Self(self.0.push(v)))) + pub fn pop_front(&self) -> Result<(Var, Var), Error> { + if self.is_empty() { + return Err(E_RANGE); + } + let mut iter = self.iter(); + let first = iter.next().unwrap(); + let rest = v_list_iter(iter); + Ok((first, rest)) } +} - pub fn pop_front(&self) -> (Var, Var) { - let results = self.0.pop_front(); - (results.0, Var::new(Variant::List(Self(results.1)))) +impl Sequence for List { + fn is_empty(&self) -> bool { + self.reader.len() == 0 } - pub fn append(&mut self, other: &Self) -> Var { - Var::new(Variant::List(Self(self.0.append(other.0.clone())))) + fn len(&self) -> usize { + self.reader.len() } - pub fn remove_at(&mut self, index: usize) -> Var { - Var::new(Variant::List(Self(self.0.remove_at(index)))) + fn contains(&self, value: &Var, case_sensitive: bool) -> Result { + for v in self.iter() { + if case_sensitive { + if v.eq_case_sensitive(value) { + return Ok(true); + } + } else if v == *value { + return Ok(true); + } + } + Ok(false) } - - /// Remove the first found instance of the given value from the list. - #[must_use] - pub fn setremove(&mut self, value: &Var) -> Var { - Var::new(Variant::List(Self(self.0.setremove(value)))) + fn index_in(&self, value: &Var, case_sensitive: bool) -> Result, Error> { + for (i, v) in self.iter().enumerate() { + if case_sensitive { + if v.eq_case_sensitive(value) { + return Ok(Some(i)); + } + } else if v == *value { + return Ok(Some(i)); + } + } + Ok(None) } - pub fn insert(&mut self, index: isize, value: Var) -> Var { - Var::new(Variant::List(Self(self.0.insert(index, value)))) + fn index(&self, index: usize) -> Result { + if index >= self.reader.len() { + return Err(E_RANGE); + } + let result = self.reader.index(index).unwrap(); + let v = Variant::from_reader(result); + let v = Var(v); + Ok(v) } - pub fn set(&mut self, index: usize, value: Var) -> Var { - Var::new(Variant::List(Self(self.0.set(index, value)))) + fn index_set(&self, index: usize, value: &Var) -> Result { + if index >= self.reader.len() { + return Err(E_RANGE); + } + let replaced_iter = self + .iter() + .enumerate() + .map(|(i, v)| if i == index { value.clone() } else { v }); + Ok(v_list_iter(replaced_iter)) } - // Case insensitive - pub fn contains(&self, v: &Var) -> bool { - self.iter().any(|item| item.eq(v)) + fn push(&self, value: &Var) -> Result { + let with_added = self.iter().chain(std::iter::once(value.clone())); + Ok(Var::mk_list_iter(with_added)) } - pub fn iter(&self) -> impl Iterator + '_ { - (0..self.len()).map(move |i| self.get(i).unwrap()) + fn insert(&self, index: usize, value: &Var) -> Result { + let inserted_iter = self + .iter() + .take(index) + .chain(std::iter::once(value.clone())) + .chain(self.iter().skip(index)); + Ok(v_list_iter(inserted_iter)) } - pub fn contains_case_sensitive(&self, v: &Var) -> bool { - if let Variant::Str(s) = v.variant() { - for item in self.iter() { - if let Variant::Str(s2) = item.variant() { - if s.as_str() == s2.as_str() { - return true; - } - } - } - return false; + fn range(&self, from: isize, to: isize) -> Result { + let len = self.len() as isize; + if to < from { + return Ok(Var::mk_list(&[])); } - self.contains(v) - } -} - -impl From for Vec { - fn from(value: List) -> Self { - let len = value.len(); - let mut result = Vec::with_capacity(len); - for i in 0..len { - result.push(value.get(i).unwrap()); + if from > len + 1 || to > len { + return Err(E_RANGE); } - result + let (from, to) = (max(from, 0) as usize, to as usize); + let range_iter = self.iter().skip(from).take(to - from + 1); + Ok(Var::mk_list_iter(range_iter)) } -} -impl AsByteBuffer for List { - fn size_bytes(&self) -> usize { - self.0.size_bytes() - } - - fn with_byte_buffer R>(&self, mut f: F) -> Result { - self.0.with_byte_buffer(|buf| f(buf)) + fn range_set(&self, from: isize, to: isize, with: &Var) -> Result { + let with_val = match with.variant() { + Variant::List(s) => s, + _ => return Err(Error::E_TYPE), + }; + + let base_len = self.len(); + let from = from.to_usize().unwrap_or(0); + let to = to.to_usize().unwrap_or(0); + if to + 1 > base_len { + return Err(E_RANGE); + } + // Iterator taking up to `from` + let base_iter = self.iter().take(from); + // Iterator for with_val... + let with_iter = with_val.iter(); + // Iterator from after to, up to the end + let end_iter = self.iter().skip(to + 1); + let new_iter = base_iter.chain(with_iter).chain(end_iter); + Ok(v_list_iter(new_iter)) } - fn make_copy_as_vec(&self) -> Result, EncodingError> { - self.0.make_copy_as_vec() - } + fn append(&self, other: &Var) -> Result { + let other = match other.variant() { + Variant::List(l) => l, + _ => return Err(Error::E_TYPE), + }; - fn from_bytes(bytes: Bytes) -> Result - where - Self: Sized, - { - Ok(Self(ListImpl::from_bytes(bytes)?)) + let combined_iter = self.iter().chain(other.iter()); + Ok(Var::mk_list_iter(combined_iter)) } - fn as_bytes(&self) -> Result { - self.0.as_bytes() - } -} + fn remove_at(&self, index: usize) -> Result { + if index >= self.len() { + return Err(E_RANGE); + } -impl Default for List { - fn default() -> Self { - Self::new() + let new = self + .iter() + .enumerate() + .filter_map(|(i, v)| if i == index { None } else { Some(v) }); + Ok(Var::mk_list_iter(new)) } } @@ -160,23 +221,22 @@ impl PartialEq for List { if self.len() != other.len() { return false; } - for (a, b) in self.iter().zip(other.iter()) { - if !a.eq(&b) { + + // elements comparison + for i in 0..self.len() { + let a = self.index(i).unwrap(); + let b = other.index(i).unwrap(); + if a != b { return false; } } + true } } + impl Eq for List {} -impl Hash for List { - fn hash(&self, state: &mut H) { - for item in self.iter() { - item.hash(state); - } - } -} impl PartialOrd for List { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) @@ -185,73 +245,385 @@ impl PartialOrd for List { impl Ord for List { fn cmp(&self, other: &Self) -> std::cmp::Ordering { - let len = self.len(); - if len != other.len() { - return len.cmp(&other.len()); + if self.len() != other.len() { + return self.len().cmp(&other.len()); } - for (a, b) in self.iter().zip(other.iter()) { + // elements comparison + for i in 0..self.len() { + let a = self.index(i).unwrap(); + let b = other.index(i).unwrap(); match a.cmp(&b) { std::cmp::Ordering::Equal => continue, x => return x, } } + std::cmp::Ordering::Equal } } -impl From> for List { - fn from(value: Vec) -> Self { - Self::from_slice(&value) +impl Hash for List { + fn hash(&self, state: &mut H) { + for item in self.iter() { + item.hash(state); + } } } -impl From<&[Var]> for List { - fn from(value: &[Var]) -> Self { - Self::from_slice(value) +impl FromIterator for Var { + fn from_iter>(iter: T) -> Self { + let mut builder = flexbuffers::Builder::new(BuilderOptions::empty()); + let mut vb = builder.start_vector(); + vb.push(VarType::TYPE_LIST as u8); + let mut lv = vb.start_vector(); + for v in iter { + let v = v.variant(); + v.push_item(&mut lv); + } + lv.end_vector(); + vb.end_vector(); + let buf = builder.take_buffer(); + let buf = Bytes::from(buf); + let reader = Reader::get_root(VarBuffer(buf)).unwrap(); + let v = Variant::from_reader(reader); + Var(v) } } +#[cfg(test)] +mod tests { + use crate::v_bool; + use crate::var::var::{v_empty_list, v_int, v_list, v_str, Var}; + use crate::var::variant::Variant; + use crate::var::Error; + use crate::var::Error::{E_RANGE, E_TYPE}; + use crate::var::{IndexMode, Sequence}; -impl Display for List { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "{{")?; - let mut first = true; - for v in self.iter() { - if !first { - write!(f, ", ")?; + #[test] + fn test_list_pack_unpack_index() { + let l = Var::mk_list(&[Var::mk_integer(1), Var::mk_integer(2), Var::mk_integer(3)]); + + match l.variant() { + Variant::List(l) => { + assert_eq!(l.len(), 3); } - first = false; - write!(f, "{v}")?; + _ => panic!("Expected list, got {:?}", l.variant()), + } + eprintln!("List: {:?}", l.variant()); + let r = l.index(&Var::mk_integer(1), IndexMode::ZeroBased).unwrap(); + let r = r.variant(); + match r { + Variant::Int(i) => assert_eq!(*i, 2), + _ => panic!("Expected integer, got {:?}", r), } - write!(f, "}}") } -} -#[cfg(test)] -mod tests { - use crate::var::list::List; - use crate::var::{v_int, v_list, v_string}; + #[test] + fn test_list_equality_inequality() { + let l1 = Var::mk_list(&[Var::mk_integer(1), Var::mk_integer(2), Var::mk_integer(3)]); + let l2 = Var::mk_list(&[Var::mk_integer(1), Var::mk_integer(2), Var::mk_integer(3)]); + let l3 = Var::mk_list(&[Var::mk_integer(1), Var::mk_integer(2), Var::mk_integer(4)]); + let l4 = Var::mk_list(&[Var::mk_integer(1), Var::mk_integer(2)]); + let l5 = Var::mk_list(&[ + Var::mk_integer(1), + Var::mk_integer(2), + Var::mk_integer(3), + Var::mk_integer(4), + ]); + + assert_eq!(l1, l2); + assert_ne!(l1, l3); + assert_ne!(l1, l4); + assert_ne!(l1, l5); + } #[test] - pub fn weird_moo_insert_scenarios() { - // MOO supports negative indexes, which just floor to 0... - let mut list = List::from_slice(&[v_int(1), v_int(2), v_int(3)]); + fn test_list_is_funcs() { + let l = Var::mk_list(&[Var::mk_integer(1), Var::mk_integer(2), Var::mk_integer(3)]); + assert!(l.is_true()); + assert!(l.is_sequence()); + assert!(!l.is_associative()); + assert!(!l.is_scalar()); + assert_eq!(l.len().unwrap(), 3); + assert!(!l.is_empty().unwrap()); + + let l = Var::mk_list(&[]); + assert!(!l.is_true()); + assert!(l.is_sequence()); + assert!(!l.is_associative()); + assert!(!l.is_scalar()); + assert_eq!(l.len().unwrap(), 0); + assert!(l.is_empty().unwrap()); + } + + #[test] + fn test_list_index() { + let l = Var::mk_list(&[Var::mk_integer(1), Var::mk_integer(2), Var::mk_integer(3)]); + let r = l.index(&Var::mk_integer(1), IndexMode::ZeroBased).unwrap(); + let r = r.variant(); + match r { + Variant::Int(i) => assert_eq!(*i, 2), + _ => panic!("Expected integer, got {:?}", r), + } + } + + #[test] + fn test_list_index_set() { + let l = Var::mk_list(&[Var::mk_integer(1), Var::mk_integer(2), Var::mk_integer(3)]); + let r = l + .index_set( + &Var::mk_integer(1), + &Var::mk_integer(42), + IndexMode::ZeroBased, + ) + .unwrap(); + let r = r.variant(); + match r { + Variant::List(l) => { + let r = l.index(1).unwrap(); + let r = r.variant(); + match r { + Variant::Int(i) => assert_eq!(*i, 42), + _ => panic!("Expected integer, got {:?}", r), + } + } + _ => panic!("Expected list, got {:?}", r), + } + + let fail_bad_index = l.index_set( + &Var::mk_integer(10), + &Var::mk_integer(42), + IndexMode::ZeroBased, + ); + assert!(fail_bad_index.is_err()); + assert_eq!(fail_bad_index.unwrap_err(), crate::var::Error::E_RANGE); + } + + #[test] + fn test_list_set_remove() { + let l = Var::mk_list(&[ + Var::mk_integer(1), + Var::mk_integer(2), + Var::mk_integer(3), + Var::mk_integer(2), + ]); + // Only works on list variants. + let l = match l.variant() { + Variant::List(l) => l, + _ => panic!("Expected list"), + }; + // This will only remove the first instance of 2... + let removed = l.set_remove(&Var::mk_integer(2)).unwrap(); + let removed_v = match removed.variant() { + Variant::List(l) => l, + _ => panic!("Expected list"), + }; + // should now b e [1, 3, 2] + assert_eq!(removed_v.len(), 3); + assert_eq!( + removed, + Var::mk_list(&[Var::mk_integer(1), Var::mk_integer(3), Var::mk_integer(2)]) + ); + } + + #[test] + fn test_list_set_add() { + let l = Var::mk_list(&[Var::mk_integer(1), Var::mk_integer(2), Var::mk_integer(3)]); + // Only works on list variants. + let l = match l.variant() { + Variant::List(l) => l, + _ => panic!("Expected list"), + }; + // This will only add the first instance of 2... + let added = l.set_add(&Var::mk_integer(2)).unwrap(); + let added_v = match added.variant() { + Variant::List(l) => l, + _ => panic!("Expected list"), + }; + // should still be [1, 2, 3] + assert_eq!(added_v.len(), 3); + assert_eq!( + added, + Var::mk_list(&[Var::mk_integer(1), Var::mk_integer(2), Var::mk_integer(3)]) + ); + + // now add 4 + let added = l.set_add(&Var::mk_integer(4)).unwrap(); + let added_v = match added.variant() { + Variant::List(l) => l, + _ => panic!("Expected list"), + }; + // should now be [1, 2, 3, 4] + assert_eq!(added_v.len(), 4); + assert_eq!( + added, + Var::mk_list(&[ + Var::mk_integer(1), + Var::mk_integer(2), + Var::mk_integer(3), + Var::mk_integer(4) + ]) + ); + } + + #[test] + fn test_list_range() -> Result<(), Error> { + // test on integer list + let int_list = v_list(&[1.into(), 2.into(), 3.into(), 4.into(), 5.into()]); + assert_eq!( + int_list.range(&v_int(2), &v_int(4), IndexMode::ZeroBased)?, + v_list(&[3.into(), 4.into(), 5.into()]) + ); + + let int_list = v_list(&[1.into(), 2.into(), 3.into(), 4.into(), 5.into()]); + assert_eq!( + int_list.range(&v_int(3), &v_int(5), IndexMode::OneBased)?, + v_list(&[3.into(), 4.into(), 5.into()]) + ); + + // range with upper higher than lower, moo returns empty list for this (!) + let empty_list = v_empty_list(); + assert_eq!( + empty_list.range(&v_int(1), &v_int(0), IndexMode::ZeroBased), + Ok(v_empty_list()) + ); + // test on out of range + let int_list = v_list(&[1.into(), 2.into(), 3.into()]); + assert_eq!( + int_list.range(&v_int(2), &v_int(4), IndexMode::ZeroBased), + Err(E_RANGE) + ); + // test on type mismatch + let var_int = v_int(10); assert_eq!( - list.insert(-1, v_int(0)), - v_list(&[v_int(0), v_int(1), v_int(2), v_int(3)]) + var_int.range(&v_int(1), &v_int(5), IndexMode::ZeroBased), + Err(E_TYPE) ); - // MOO supports indexes beyond length of the list, which just append to the end... - let mut list = List::from_slice(&[v_int(1), v_int(2), v_int(3)]); + let list = v_list(&[v_int(0), v_int(0)]); assert_eq!( - list.insert(100, v_int(0)), - v_list(&[v_int(1), v_int(2), v_int(3), v_int(0)]) + list.range(&v_int(1), &v_int(2), IndexMode::OneBased)?, + v_list(&[v_int(0), v_int(0)]) ); + Ok(()) + } + + #[test] + fn test_list_range_set() { + let base = v_list(&[v_int(1), v_int(2), v_int(3), v_int(4)]); + + // {1,2,3,4}[1..2] = {"a", "b", "c"} => {1, "a", "b", "c", 4} + let value = v_list(&[v_str("a"), v_str("b"), v_str("c")]); + let expected = v_list(&[v_int(1), v_str("a"), v_str("b"), v_str("c"), v_int(4)]); + let result = base.range_set(&v_int(2), &v_int(3), &value, IndexMode::OneBased); + assert_eq!(result, Ok(expected)); + + // {1,2,3,4}[1..2] = {"a"} => {1, "a", 4} + let value = v_list(&[v_str("a")]); + let expected = v_list(&[v_int(1), v_str("a"), v_int(4)]); + let result = base.range_set(&v_int(2), &v_int(3), &value, IndexMode::OneBased); + assert_eq!(result, Ok(expected)); + + // {1,2,3,4}[1..2] = {} => {1,4} + let value = v_empty_list(); + let expected = v_list(&[v_int(1), v_int(4)]); + let result = base.range_set(&v_int(2), &v_int(3), &value, IndexMode::OneBased); + assert_eq!(result, Ok(expected)); + + // {1,2,3,4}[1..2] = {"a", "b"} => {1, "a", "b", 4} + let value = v_list(&[v_str("a"), v_str("b")]); + let expected = v_list(&[v_int(1), v_str("a"), v_str("b"), v_int(4)]); + let result = base.range_set(&v_int(2), &v_int(3), &value, IndexMode::OneBased); + assert_eq!(result, Ok(expected)); + } + + #[test] + fn test_list_range_set2() { + let base = v_list(&[v_int(1), v_int(2), v_int(3), v_int(4)]); + let with_val = v_list(&[v_int(3), v_int(4)]); + let expected = v_list(&[v_int(3), v_int(4), v_int(3), v_int(4)]); + let result = base.range_set(&v_int(1), &v_int(2), &with_val, IndexMode::OneBased); + assert_eq!(result, Ok(expected)); + } + + #[test] + fn test_list_push() { + let l = v_list(&[v_int(1), v_int(2), v_int(3)]); + let r = l.push(&v_int(4)).unwrap(); + assert_eq!(r, v_list(&[v_int(1), v_int(2), v_int(3), v_int(4)])); + } + + #[test] + fn test_list_append() { + let l1 = v_list(&[v_int(1), v_int(2), v_int(3)]); + let l2 = v_list(&[v_int(4), v_int(5), v_int(6)]); + let l3 = v_list(&[v_int(1), v_int(2), v_int(3), v_int(4), v_int(5), v_int(6)]); + assert_eq!(l1.append(&l2), Ok(l3)); + } + + #[test] + fn test_list_remove_at() { + let l = v_list(&[v_int(1), v_int(2), v_int(3), v_int(4)]); + let r = l.remove_at(&v_int(1), IndexMode::ZeroBased).unwrap(); + assert_eq!(r, v_list(&[v_int(1), v_int(3), v_int(4)])); + } + + #[test] + fn test_list_contains() { + // Case sensitive and case-insensitive tests + let l = v_list(&[v_str("a"), v_str("b"), v_str("c")]); + assert_eq!(l.contains(&v_str("a"), true), Ok(v_bool(true))); + assert_eq!(l.contains(&v_str("A"), false), Ok(v_bool(true))); + assert_eq!(l.contains(&v_str("A"), true), Ok(v_bool(false))); + } + + #[test] + fn test_index_in() { + let l = v_list(&[v_str("a"), v_str("b"), v_str("c")]); + assert_eq!( + l.index_in(&v_str("a"), false, IndexMode::OneBased).unwrap(), + v_int(1) + ); + assert_eq!( + l.index_in(&v_str("A"), false, IndexMode::OneBased).unwrap(), + v_int(1) + ); + assert_eq!( + l.index_in(&v_str("A"), true, IndexMode::OneBased).unwrap(), + v_int(0) + ); + + assert_eq!( + l.index_in(&v_str("A"), true, IndexMode::ZeroBased).unwrap(), + v_int(-1) + ); + } + + #[test] + fn test_list_case_sensitive_compare() { + let a = v_list(&[v_str("a"), v_str("b"), v_str("c")]); + let b = v_list(&[v_str("A"), v_str("B"), v_str("C")]); + + assert!(!a.eq_case_sensitive(&b)); + assert!(a == b); } #[test] - pub fn list_display() { - let list = List::from_slice(&[v_int(1), v_string("foo".into()), v_int(3)]); - assert_eq!(format!("{list}"), "{1, \"foo\", 3}"); + fn test_list_insert() { + let l = v_list(&[v_int(1), v_int(2), v_int(3)]); + let r = l.insert(&v_int(0), &v_int(0), IndexMode::OneBased).unwrap(); + assert_eq!(r, v_list(&[v_int(0), v_int(1), v_int(2), v_int(3)])); + + // Insert to the end + let l = v_list(&[v_int(1), v_int(2), v_int(3)]); + let r = l.insert(&v_int(1), &v_int(1), IndexMode::OneBased).unwrap(); + assert_eq!(r, v_list(&[v_int(1), v_int(1), v_int(2), v_int(3)])); + + // Out of range just goes to the end + let l = v_list(&[v_int(1), v_int(2), v_int(3)]); + let r = l + .insert(&v_int(10), &v_int(10), IndexMode::OneBased) + .unwrap(); + assert_eq!(r, v_list(&[v_int(1), v_int(2), v_int(3), v_int(10)])); } } diff --git a/crates/values/src/var/list_impl_buffer.rs b/crates/values/src/var/list_impl_buffer.rs deleted file mode 100644 index 0fb95faa..00000000 --- a/crates/values/src/var/list_impl_buffer.rs +++ /dev/null @@ -1,665 +0,0 @@ -// Copyright (C) 2024 Ryan Daum -// -// This program is free software: you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free Software -// Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along with -// this program. If not, see . -// - -use std::cmp::min; - -use bincode::de::{BorrowDecoder, Decoder}; -use bincode::enc::Encoder; -use bincode::error::{DecodeError, EncodeError}; -use bincode::{BorrowDecode, Decode, Encode}; -use bytes::Bytes; - -use crate::var::variant::Variant; -use crate::var::{v_empty_list, Var}; -use crate::{AsByteBuffer, DecodingError, EncodingError}; - -#[derive(Clone, Debug)] -pub struct ListImplBuffer(Bytes); - -fn offsets_end_pos(buf: &[u8]) -> usize { - u32::from_le_bytes(buf[0..4].try_into().unwrap()) as usize -} - -fn offset_at(buf: &[u8], index: usize) -> usize { - u32::from_le_bytes(buf[4 + index * 4..4 + (index + 1) * 4].try_into().unwrap()) as usize -} - -impl ListImplBuffer { - pub fn new() -> Self { - Self(Bytes::from(Vec::new())) - } - - pub fn len(&self) -> usize { - let l = self.0.len(); - if l == 0 || l == 4 { - return 0; - } - - let slc = self.0.as_ref(); - let offsets_end = offsets_end_pos(slc); - let offsets_len = offsets_end - 4; - // The offsets table is 4 bytes per offset. - offsets_len >> 2 - } - - pub fn is_empty(&self) -> bool { - let l = self.0.len(); - if l == 0 || l == 4 { - return true; - } - false - } - - pub fn get(&self, index: usize) -> Option { - let len = self.len(); - if index >= len { - return None; - } - - let slc = self.0.as_ref(); - let offsets_end = offsets_end_pos(slc); - - // The offsets table is 4 bytes per offset. - let data_offset = offset_at(slc, index); - - let data_section = &self.0.slice(offsets_end..); - - // If this is the last item, we can just slice to the end of the data section. - if index == len - 1 { - let slice_ref = data_section.slice(data_offset..); - return Some(Var::from_bytes(slice_ref).expect("could not decode var")); - } - - // Otherwise, we need to slice from this offset to the next offset. - let next_offset = offset_at(slc, index + 1); - - // Note that the offsets are relative to the start of the data section. - let data = data_section.slice(data_offset..next_offset); - Var::from_bytes(data.clone()).ok() - } - - pub fn from_slice(vec: &[Var]) -> Self { - let mut data = Vec::new(); - - let mut relative_offset: u32 = 0; - let mut offsets = Vec::with_capacity(vec.len() * 4); - for v in vec.iter() { - offsets.extend_from_slice(&relative_offset.to_le_bytes()); - let vsr = v.as_bytes().unwrap(); - let bytes = vsr.as_ref(); - data.extend_from_slice(bytes); - relative_offset += bytes.len() as u32; - } - - let mut result = Vec::with_capacity(4 + offsets.len() + data.len()); - result.extend_from_slice(&(offsets.len() as u32 + 4).to_le_bytes()); - result.extend_from_slice(&offsets); - result.extend_from_slice(&data); - - Self(Bytes::from(result)) - } - - pub fn push(&self, v: Var) -> Self { - let len = self.len(); - - let data_sr = v.as_bytes().unwrap(); - - // Special case if we're empty. - if len == 0 { - let mut new_offsets = Vec::with_capacity(4); - let offset: u32 = 0; - new_offsets.extend_from_slice(&offset.to_le_bytes()); - - let mut result = Vec::with_capacity(4 + new_offsets.len() + data_sr.len()); - result.extend_from_slice(&8u32.to_le_bytes()); - result.extend_from_slice(&new_offsets); - result.extend_from_slice(data_sr.as_ref()); - - return Self(Bytes::from(result)); - } - - let slc = self.0.as_ref(); - let offsets_end = offsets_end_pos(slc); - - let existing_offset_table = &slc[4..offsets_end]; - let existing_data = &slc[offsets_end..]; - - // Add the new offset to the offsets table. The new offset is end of the old data section, - // that is, the length of the whole buffer. - let mut new_offsets = Vec::with_capacity(existing_offset_table.len() + 4); - new_offsets.extend_from_slice(existing_offset_table); - let new_offset = existing_data.len() as u32; - new_offsets.extend_from_slice(&new_offset.to_le_bytes()); - - // Add the new data to the data section. - let mut new_data = Vec::with_capacity(existing_data.len() + data_sr.len()); - new_data.extend_from_slice(existing_data); - new_data.extend_from_slice(data_sr.as_ref()); - - // Update offsets end - let new_offsets_end = new_offsets.len() as u32 + 4; - - // Result is new_offsets_len + new_offsets + new_data - let mut result = Vec::with_capacity(4 + new_offsets.len() + new_data.len()); - result.extend_from_slice(&new_offsets_end.to_le_bytes()); - result.extend_from_slice(&new_offsets); - result.extend_from_slice(&new_data); - - Self(Bytes::from(result)) - } - - pub fn pop_front(&self) -> (Var, Self) { - let len = self.len(); - if len == 0 { - return (v_empty_list(), self.clone()); - } - - if len == 1 { - return (self.get(0).unwrap(), ListImplBuffer::new()); - } - - let slc = self.0.as_ref(); - let offsets_end = offsets_end_pos(slc); - - // Get the offset table - let offsets_table = &slc[4..offsets_end]; - - // Splice off the data section after the first item - let data_section = &self.0.slice(offsets_end..); - let first_offset = offset_at(slc, 0); - let next_offset = offset_at(slc, 1); - let length = next_offset - first_offset; - let data = data_section.slice(first_offset..next_offset); - - // Now rebuild the offset table, subtracting the length of the first item - let mut new_offsets = Vec::with_capacity(offsets_table.len() - 4); - for i in 1..len { - let offset = offset_at(slc, i); - let new_offset = (offset - length) as u32; - new_offsets.extend_from_slice(&new_offset.to_le_bytes()); - } - - // Now reconstruct - let mut result = Vec::with_capacity(4 + new_offsets.len() + data.len()); - result.extend_from_slice(&(new_offsets.len() as u32 + 4).to_le_bytes()); - result.extend_from_slice(&new_offsets); - result.extend_from_slice(data_section.slice(next_offset..).as_ref()); - - (Var::from_bytes(data).unwrap(), Self(Bytes::from(result))) - } - - pub fn append(&self, other: Self) -> Self { - let len = self.len(); - if len == 0 { - return other.clone(); - } - - let other_len = other.len(); - if other_len == 0 { - return self.clone(); - } - - // Find the starts of the two data sections - let slc = self.0.as_ref(); - let oth_slc = other.0.as_ref(); - - let data_start_self = offsets_end_pos(slc); - let data_start_other = offsets_end_pos(oth_slc); - - // Get their data sections - let data_self = &slc[data_start_self..]; - let data_other = &oth_slc[data_start_other..]; - - // Get the two offsets tables - let offset_table_self = &slc[4..data_start_self]; - let offset_table_other = &oth_slc[4..data_start_other]; - - let self_offset_len = offset_table_self.len(); - - // Construct a new offset table, leaving self intact and then adjusting other. - let mut new_offset_table = Vec::with_capacity(self_offset_len + offset_table_other.len()); - new_offset_table.extend_from_slice(offset_table_self); - for i in 0..other_len { - let offset = offset_at(oth_slc, i); - let new_offset = (offset + data_self.len()) as u32; - new_offset_table.extend_from_slice(&new_offset.to_le_bytes()); - } - - let mut result = - Vec::with_capacity(4 + new_offset_table.len() + data_self.len() + data_other.len()); - result.extend_from_slice(&(new_offset_table.len() as u32 + 4).to_le_bytes()); - result.extend_from_slice(&new_offset_table); - result.extend_from_slice(data_self); - result.extend_from_slice(data_other); - - Self(Bytes::from(result)) - } - - pub fn remove_at(&self, index: usize) -> Self { - let len = self.len(); - if len == 0 { - return self.clone(); - } - - if len == 1 { - return ListImplBuffer::new(); - } - - // This will involve rebuilding both the offsets and data sections. - let slc = self.0.as_ref(); - let old_data_start = offsets_end_pos(slc); - - let old_data = &slc[old_data_start..]; - let old_offsets = &slc[4..old_data_start]; - - let remove_item_offset = - u32::from_le_bytes(old_offsets[index * 4..(index + 1) * 4].try_into().unwrap()) - as usize; - let remove_item_length = if index == len - 1 { - old_data.len() - remove_item_offset - } else { - let next_offset = offset_at(slc, index + 1); - next_offset - remove_item_offset - }; - - let mut new_offsets = Vec::with_capacity(old_offsets.len() - 4); - let mut new_data = Vec::with_capacity(old_data.len() - remove_item_length); - - // Iterate to generate - for i in 0..len { - if i == index { - continue; - } - - let offset = offset_at(slc, i); - let data = if i == len - 1 { - &old_data[offset..] - } else { - let next_offset = offset_at(slc, i + 1); - &old_data[offset..next_offset] - }; - let new_offset = new_data.len() as u32; - new_data.extend_from_slice(data); - new_offsets.extend_from_slice(&new_offset.to_le_bytes()); - } - - let mut result = Vec::with_capacity(4 + new_offsets.len() + new_data.len()); - result.extend_from_slice(&(new_offsets.len() as u32 + 4).to_le_bytes()); - result.extend_from_slice(&new_offsets); - result.extend_from_slice(&new_data); - Self(Bytes::from(result)) - } - - /// Remove the first found instance of the given value from the list. - #[must_use] - pub fn setremove(&self, value: &Var) -> Self { - let len = self.len(); - if len == 0 { - return self.clone(); - } - - if len == 1 { - if self.get(0).unwrap().eq(value) { - return ListImplBuffer::new(); - } - return self.clone(); - } - - // This will involve rebuilding both the offsets and data sections. - let slc = self.0.as_ref(); - let old_data_start = offsets_end_pos(slc); - - let old_data = &self.0.slice(old_data_start..); - let old_offsets = &slc[4..old_data_start]; - - let mut new_offsets = Vec::with_capacity(old_offsets.len() - 4); - let mut new_data = Vec::with_capacity(old_data.len()); - - // Iterate to generate - let mut found = false; - for i in 0..len { - let offset = offset_at(slc, i); - let data = if i == len - 1 { - old_data.slice(offset..) - } else { - let next_offset = offset_at(slc, i + 1); - old_data.slice(offset..next_offset) - }; - let v = Var::from_bytes(data.clone()).unwrap(); - if !found && v.eq(value) { - found = true; - continue; - } - - let new_offset = new_data.len() as u32; - new_data.extend_from_slice(data.as_ref()); - new_offsets.extend_from_slice(&new_offset.to_le_bytes()); - } - - let mut result = Vec::with_capacity(4 + new_offsets.len() + new_data.len()); - result.extend_from_slice(&(new_offsets.len() as u32 + 4).to_le_bytes()); - result.extend_from_slice(&new_offsets); - result.extend_from_slice(&new_data); - Self(Bytes::from(result)) - } - - pub fn insert(&self, index: isize, value: Var) -> Self { - let index = if index < 0 { - 0 - } else { - min(index as usize, self.len()) - }; - - // Special case if inserting at end, it's just push - if index == self.len() { - return self.push(value); - } - - // Special case if we're empty. - if self.is_empty() { - return Self::from_slice(&[value]); - } - - // Accumulate up to the insertion point, building the new offsets and data sections. - // Then add the new item, and then add the rest of the items. - let slc = self.0.as_ref(); - let old_data_start = offsets_end_pos(slc); - let old_data = &slc[old_data_start..]; - let old_offsets = &slc[4..old_data_start]; - - let mut new_offsets = Vec::with_capacity(old_offsets.len() + 4); - let mut new_data = Vec::with_capacity(old_data.len() + value.as_bytes().unwrap().len()); - - for i in 0..self.len() { - if i == index { - let new_offset = new_data.len() as u32; - new_offsets.extend_from_slice(&new_offset.to_le_bytes()); - new_data.extend_from_slice(value.as_bytes().unwrap().as_ref()); - } - let offset = offset_at(slc, i); - let length = if i == self.len() - 1 { - old_data.len() - offset - } else { - let next_offset = offset_at(slc, i + 1); - next_offset - offset - }; - let new_offset = new_data.len() as u32; - new_offsets.extend_from_slice(&new_offset.to_le_bytes()); - new_data.extend_from_slice(&old_data[offset..offset + length]); - } - - let mut result = Vec::with_capacity(4 + new_offsets.len() + new_data.len()); - result.extend_from_slice(&(new_offsets.len() as u32 + 4).to_le_bytes()); - result.extend_from_slice(&new_offsets); - result.extend_from_slice(&new_data); - Self(Bytes::from(result)) - } - - pub fn set(&self, index: usize, value: Var) -> Self { - let len = self.len(); - if index >= len { - return self.clone(); - } - - // This will involve rebuilding both the offsets and data sections. - let slc = self.0.as_ref(); - let old_data_start = offsets_end_pos(slc); - - let old_data = &self.0.slice(old_data_start..); - let old_offsets = &slc[4..old_data_start]; - - let mut new_offsets = Vec::with_capacity(old_offsets.len()); - let mut new_data = Vec::with_capacity(old_data.len()); - - // Iterate to generate - for i in 0..len { - let offset = offset_at(slc, i); - let data = if i == len - 1 { - old_data.slice(offset..) - } else { - let next_offset = offset_at(slc, i + 1); - old_data.slice(offset..next_offset) - }; - if i == index { - let new_offset = new_data.len() as u32; - new_offsets.extend_from_slice(&new_offset.to_le_bytes()); - new_data.extend_from_slice(value.as_bytes().unwrap().as_ref()); - } else { - let new_offset = new_data.len() as u32; - new_offsets.extend_from_slice(&new_offset.to_le_bytes()); - new_data.extend_from_slice(data.as_ref()); - } - } - - let mut result = Vec::with_capacity(4 + new_offsets.len() + new_data.len()); - result.extend_from_slice(&(new_offsets.len() as u32 + 4).to_le_bytes()); - result.extend_from_slice(&new_offsets); - result.extend_from_slice(&new_data); - Self(Bytes::from(result)) - } - - // Case insensitive - pub fn contains(&self, v: &Var) -> bool { - self.iter().any(|item| item.eq(v)) - } - - pub fn iter(&self) -> impl Iterator + '_ { - (0..self.len()).map(move |i| self.get(i).unwrap()) - } - - pub fn contains_case_sensitive(&self, v: &Var) -> bool { - if let Variant::Str(s) = v.variant() { - for item in self.iter() { - if let Variant::Str(s2) = item.variant() { - if s.as_str() == s2.as_str() { - return true; - } - } - } - return false; - } - self.contains(v) - } -} - -impl AsByteBuffer for ListImplBuffer { - fn size_bytes(&self) -> usize { - self.0.len() - } - - fn with_byte_buffer R>(&self, mut f: F) -> Result { - Ok(f(self.0.as_ref())) - } - - fn make_copy_as_vec(&self) -> Result, EncodingError> { - Ok(self.0.as_ref().to_vec()) - } - - fn from_bytes(bytes: Bytes) -> Result - where - Self: Sized, - { - Ok(Self(bytes)) - } - - fn as_bytes(&self) -> Result { - Ok(self.0.clone()) - } -} - -impl Encode for ListImplBuffer { - fn encode(&self, encoder: &mut E) -> Result<(), EncodeError> { - self.0.as_ref().encode(encoder) - } -} - -impl Decode for ListImplBuffer { - fn decode(decoder: &mut D) -> Result { - let vec = Vec::::decode(decoder)?; - Ok(Self(Bytes::from(vec))) - } -} - -impl<'de> BorrowDecode<'de> for ListImplBuffer { - fn borrow_decode>(decoder: &mut D) -> Result { - let vec = Vec::::borrow_decode(decoder)?; - Ok(Self(Bytes::from(vec))) - } -} - -impl From> for ListImplBuffer { - fn from(value: Vec) -> Self { - Self::from_slice(&value) - } -} - -impl From<&[Var]> for ListImplBuffer { - fn from(value: &[Var]) -> Self { - Self::from_slice(value) - } -} - -#[cfg(test)] -mod tests { - use crate::var::list_impl_buffer::ListImplBuffer; - use crate::var::{v_int, v_string}; - - #[test] - pub fn list_make_get() { - let l = ListImplBuffer::new(); - assert_eq!(l.len(), 0); - assert!(l.is_empty()); - // MOO is a bit weird here, it returns None for out of bounds. - assert_eq!(l.get(0), None); - - let l = ListImplBuffer::from_slice(&[v_int(1)]); - assert_eq!(l.len(), 1); - assert!(!l.is_empty()); - assert_eq!(l.get(0), Some(v_int(1))); - assert_eq!(l.get(1), None); - - let l = ListImplBuffer::from_slice(&[v_int(1), v_int(2), v_int(3)]); - assert_eq!(l.len(), 3); - assert!(!l.is_empty()); - - assert_eq!(l.get(0), Some(v_int(1))); - assert_eq!(l.get(1), Some(v_int(2))); - assert_eq!(l.get(2), Some(v_int(3))); - } - - #[test] - pub fn list_push() { - let l = ListImplBuffer::new(); - let l = l.push(v_int(1)); - - assert_eq!(l.len(), 1); - let l = l.push(v_int(2)); - assert_eq!(l.len(), 2); - let l = l.push(v_int(3)); - assert_eq!(l.len(), 3); - - assert_eq!(l.get(0), Some(v_int(1))); - assert_eq!(l.get(2), Some(v_int(3))); - assert_eq!(l.get(1), Some(v_int(2))); - } - - #[test] - fn list_pop_front() { - let l = ListImplBuffer::from_slice(&[v_int(1), v_int(2), v_int(3)]); - let (item, l) = l.pop_front(); - assert_eq!(item, v_int(1)); - let (item, l) = l.pop_front(); - assert_eq!(item, v_int(2)); - let (item, l) = l.pop_front(); - assert_eq!(item, v_int(3)); - assert_eq!(l.len(), 0); - } - - #[test] - fn test_list_append() { - let l1 = ListImplBuffer::from_slice(&[v_int(1), v_int(2), v_int(3)]); - let l2 = ListImplBuffer::from_slice(&[v_int(4), v_int(5), v_int(6)]); - let l = l1.append(l2); - assert_eq!(l.len(), 6); - assert_eq!(l.get(0), Some(v_int(1))); - assert_eq!(l.get(5), Some(v_int(6))); - } - - #[test] - fn test_list_remove() { - let l = ListImplBuffer::from_slice(&[v_int(1), v_int(2), v_int(3)]); - - let l = l.remove_at(1); - assert_eq!(l.len(), 2); - assert_eq!(l.get(1), Some(v_int(3))); - assert_eq!(l.get(0), Some(v_int(1))); - } - - #[test] - fn test_list_setremove() { - let l = ListImplBuffer::from_slice(&[v_int(1), v_int(2), v_int(3), v_int(2)]); - let l = l.setremove(&v_int(2)); - assert_eq!(l.len(), 3); - assert_eq!(l.get(0), Some(v_int(1))); - assert_eq!(l.get(1), Some(v_int(3))); - assert_eq!(l.get(2), Some(v_int(2))); - - // setremove til empty - let l = ListImplBuffer::from_slice(&[v_int(1)]); - let l = l.setremove(&v_int(1)); - assert_eq!(l.len(), 0); - assert_eq!(l.get(0), None); - } - - #[test] - fn test_list_insert() { - let l = ListImplBuffer::new(); - let l = l.insert(0, v_int(4)); - assert_eq!(l.len(), 1); - assert_eq!(l.get(0), Some(v_int(4))); - - let l = l.insert(0, v_int(3)); - assert_eq!(l.len(), 2); - assert_eq!(l.get(0), Some(v_int(3))); - assert_eq!(l.get(1), Some(v_int(4))); - - let l = l.insert(-1, v_int(5)); - assert_eq!(l.len(), 3); - assert_eq!(l.get(0), Some(v_int(5))); - assert_eq!(l.get(1), Some(v_int(3))); - assert_eq!(l.get(2), Some(v_int(4))); - } - - #[test] - fn test_list_set() { - let l = ListImplBuffer::from_slice(&[v_int(1), v_int(2), v_int(3)]); - let l = l.set(1, v_int(4)); - assert_eq!(l.len(), 3); - assert_eq!(l.get(1), Some(v_int(4))); - } - - #[test] - fn test_list_contains_case_insenstive() { - let l = ListImplBuffer::from_slice(&[v_string("foo".into()), v_string("bar".into())]); - assert!(l.contains(&v_string("FOO".into()))); - assert!(l.contains(&v_string("BAR".into()))); - } - - #[test] - fn test_list_contains_case_senstive() { - let l = ListImplBuffer::from_slice(&[v_string("foo".into()), v_string("bar".into())]); - assert!(!l.contains_case_sensitive(&v_string("FOO".into()))); - assert!(!l.contains_case_sensitive(&v_string("BAR".into()))); - } -} diff --git a/crates/values/src/var/list_impl_vector.rs b/crates/values/src/var/list_impl_vector.rs deleted file mode 100644 index b10d8a42..00000000 --- a/crates/values/src/var/list_impl_vector.rs +++ /dev/null @@ -1,412 +0,0 @@ -// Copyright (C) 2024 Ryan Daum -// -// This program is free software: you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free Software -// Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along with -// this program. If not, see . -// - -use std::cmp::min; -use std::ops::{Index, Range, RangeFrom, RangeFull, RangeTo}; -use std::sync::Arc; - -use crate::BincodeAsByteBufferExt; -use bincode::{Decode, Encode}; - -use crate::var::variant::Variant; -use crate::var::{v_empty_list, Var}; - -#[derive(Clone, Debug, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct ListImplVector { - // TODO: Implement our own zero-copy list type and get rid of bincoding - // To support nested content, would require an offsets table at the front, etc. - // Take a look at how flatbufers, capnproto, and other zero-copy serialization formats do this. - inner: Arc>, -} - -impl ListImplVector { - #[must_use] - pub fn new() -> Self { - Self { - inner: Arc::new(Vec::new()), - } - } - - #[must_use] - pub fn from_vec(vec: Vec) -> Self { - Self { - inner: Arc::new(vec), - } - } - - pub fn from_slice(vec: &[Var]) -> Self { - Self { - inner: Arc::new(vec.to_vec()), - } - } - - #[must_use] - pub fn push(&mut self, v: Var) -> Self { - // If there's only one copy of us, mutate that directly. - match Arc::get_mut(&mut self.inner) { - Some(vec) => { - vec.push(v); - self.clone() - } - None => { - let mut new_vec = (*self.inner).clone(); - new_vec.push(v); - Self::from_vec(new_vec) - } - } - } - - /// Take the first item from the front, and return (item, `new_list`) - #[must_use] - pub fn pop_front(&self) -> (Var, Self) { - if self.inner.is_empty() { - return (v_empty_list(), Self::new()); - } - let mut new_list = (*self.inner).clone(); - let item = new_list.remove(0); - (item.clone(), Self::from_vec(new_list)) - } - - #[must_use] - pub fn append(&mut self, other: Self) -> Self { - match Arc::get_mut(&mut self.inner) { - Some(vec) => { - vec.extend_from_slice(&other.inner); - self.clone() - } - None => { - let mut new_list = (*self.inner).clone(); - new_list.extend_from_slice(&other.inner); - Self::from_vec(new_list) - } - } - } - - #[must_use] - pub fn remove_at(&mut self, index: usize) -> Self { - match Arc::get_mut(&mut self.inner) { - Some(vec) => { - vec.remove(index); - self.clone() - } - None => { - let mut new_list = (*self.inner).clone(); - new_list.remove(index); - Self::from_vec(new_list) - } - } - } - - /// Remove the first found instance of the given value from the list. - #[must_use] - pub fn setremove(&mut self, value: &Var) -> Self { - if self.inner.is_empty() { - return self.clone(); - } - match Arc::get_mut(&mut self.inner) { - Some(vec) => { - for i in 0..vec.len() { - if vec[i].eq(value) { - vec.remove(i); - break; - } - } - self.clone() - } - None => { - let mut new_list = Vec::with_capacity(self.inner.len() - 1); - let mut found = false; - for v in self.inner.iter() { - if !found && v.eq(value) { - found = true; - continue; - } - new_list.push(v.clone()); - } - Self::from_vec(new_list) - } - } - } - - #[must_use] - pub fn insert(&mut self, index: isize, v: Var) -> Self { - match Arc::get_mut(&mut self.inner) { - Some(vec) => { - let index = if index < 0 { - 0 - } else { - min(index as usize, vec.len()) - }; - vec.insert(index, v); - self.clone() - } - None => { - let mut new_list = Vec::with_capacity(self.inner.len() + 1); - let index = if index < 0 { - 0 - } else { - min(index as usize, self.inner.len()) - }; - new_list.extend_from_slice(&self.inner[..index]); - new_list.push(v); - new_list.extend_from_slice(&self.inner[index..]); - Self::from_vec(new_list) - } - } - } - - #[must_use] - pub fn len(&self) -> usize { - self.inner.len() - } - - #[must_use] - pub fn is_empty(&self) -> bool { - self.inner.is_empty() - } - - // "in" operator is case insensitive... - #[must_use] - pub fn contains(&self, v: &Var) -> bool { - self.inner.contains(v) - } - - // but bf_is_member is not... sigh. - #[must_use] - pub fn contains_case_sensitive(&self, v: &Var) -> bool { - if let Variant::Str(s) = v.variant() { - for item in self.inner.iter() { - if let Variant::Str(s2) = item.variant() { - if s.as_str() == s2.as_str() { - return true; - } - } - } - return false; - } - self.inner.contains(v) - } - - #[must_use] - pub fn get(&self, index: usize) -> Option { - self.inner.get(index).cloned() - } - - #[must_use] - pub fn set(&mut self, index: usize, value: Var) -> Self { - match Arc::get_mut(&mut self.inner) { - Some(vec) => { - vec[index] = value; - self.clone() - } - None => { - let mut new_vec = (*self.inner).clone(); - new_vec[index] = value; - Self::from_vec(new_vec) - } - } - } - - pub fn iter(&self) -> impl Iterator { - self.inner.iter() - } -} - -impl From for Vec { - fn from(val: ListImplVector) -> Self { - val.inner[..].to_vec() - } -} - -impl Default for ListImplVector { - fn default() -> Self { - Self::new() - } -} - -impl Index for ListImplVector { - type Output = Var; - - fn index(&self, index: usize) -> &Self::Output { - &self.inner[index] - } -} - -impl Index> for ListImplVector { - type Output = [Var]; - - fn index(&self, index: Range) -> &Self::Output { - &self.inner[index] - } -} - -impl Index> for ListImplVector { - type Output = [Var]; - - fn index(&self, index: RangeFrom) -> &Self::Output { - &self.inner[index] - } -} - -impl Index> for ListImplVector { - type Output = [Var]; - - fn index(&self, index: RangeTo) -> &Self::Output { - &self.inner[index] - } -} - -impl Index for ListImplVector { - type Output = [Var]; - - fn index(&self, index: RangeFull) -> &Self::Output { - &self.inner[index] - } -} - -impl BincodeAsByteBufferExt for ListImplVector {} - -#[cfg(test)] -mod tests { - use crate::var::list_impl_vector::ListImplVector; - use crate::var::{v_int, v_string}; - - #[test] - pub fn list_make_get() { - let l = ListImplVector::new(); - assert_eq!(l.len(), 0); - assert!(l.is_empty()); - // MOO is a bit weird here, it returns None for out of bounds. - assert_eq!(l.get(0), None); - - let l = ListImplVector::from_slice(&[v_int(1)]); - assert_eq!(l.len(), 1); - assert!(!l.is_empty()); - assert_eq!(l.get(0), Some(v_int(1))); - assert_eq!(l.get(1), None); - - let l = ListImplVector::from_slice(&[v_int(1), v_int(2), v_int(3)]); - assert_eq!(l.len(), 3); - assert!(!l.is_empty()); - - assert_eq!(l.get(0), Some(v_int(1))); - assert_eq!(l.get(1), Some(v_int(2))); - assert_eq!(l.get(2), Some(v_int(3))); - } - - #[test] - pub fn list_push() { - let mut l = ListImplVector::new(); - let mut l = l.push(v_int(1)); - - assert_eq!(l.len(), 1); - let mut l = l.push(v_int(2)); - assert_eq!(l.len(), 2); - let l = l.push(v_int(3)); - assert_eq!(l.len(), 3); - - assert_eq!(l.get(0), Some(v_int(1))); - assert_eq!(l.get(2), Some(v_int(3))); - assert_eq!(l.get(1), Some(v_int(2))); - } - - #[test] - fn list_pop_front() { - let l = ListImplVector::from_slice(&[v_int(1), v_int(2), v_int(3)]); - let (item, l) = l.pop_front(); - assert_eq!(item, v_int(1)); - let (item, l) = l.pop_front(); - assert_eq!(item, v_int(2)); - let (item, l) = l.pop_front(); - assert_eq!(item, v_int(3)); - assert_eq!(l.len(), 0); - } - - #[test] - fn test_list_append() { - let mut l1 = ListImplVector::from_slice(&[v_int(1), v_int(2), v_int(3)]); - let l2 = ListImplVector::from_slice(&[v_int(4), v_int(5), v_int(6)]); - let l = l1.append(l2); - assert_eq!(l.len(), 6); - assert_eq!(l.get(0), Some(v_int(1))); - assert_eq!(l.get(5), Some(v_int(6))); - } - - #[test] - fn test_list_remove() { - let mut l = ListImplVector::from_slice(&[v_int(1), v_int(2), v_int(3)]); - - let l = l.remove_at(1); - assert_eq!(l.len(), 2); - assert_eq!(l.get(1), Some(v_int(3))); - assert_eq!(l.get(0), Some(v_int(1))); - } - - #[test] - fn test_list_setremove() { - let mut l = ListImplVector::from_slice(&[v_int(1), v_int(2), v_int(3), v_int(2)]); - let l = l.setremove(&v_int(2)); - assert_eq!(l.len(), 3); - assert_eq!(l.get(0), Some(v_int(1))); - assert_eq!(l.get(1), Some(v_int(3))); - assert_eq!(l.get(2), Some(v_int(2))); - - // setremove til empty - let mut l = ListImplVector::from_slice(&[v_int(1)]); - let l = l.setremove(&v_int(1)); - assert_eq!(l.len(), 0); - assert_eq!(l.get(0), None); - } - - #[test] - fn test_list_insert() { - let mut l = ListImplVector::new(); - let mut l = l.insert(0, v_int(4)); - assert_eq!(l.len(), 1); - assert_eq!(l.get(0), Some(v_int(4))); - - let mut l = l.insert(0, v_int(3)); - assert_eq!(l.len(), 2); - assert_eq!(l.get(0), Some(v_int(3))); - assert_eq!(l.get(1), Some(v_int(4))); - - let l = l.insert(-1, v_int(5)); - assert_eq!(l.len(), 3); - assert_eq!(l.get(0), Some(v_int(5))); - assert_eq!(l.get(1), Some(v_int(3))); - assert_eq!(l.get(2), Some(v_int(4))); - } - - #[test] - fn test_list_set() { - let mut l = ListImplVector::from_slice(&[v_int(1), v_int(2), v_int(3)]); - let l = l.set(1, v_int(4)); - assert_eq!(l.len(), 3); - assert_eq!(l.get(1), Some(v_int(4))); - } - - #[test] - fn test_list_contains_case_insensitive() { - let l = ListImplVector::from_slice(&[v_string("foo".into()), v_string("bar".into())]); - assert!(l.contains(&v_string("FOO".into()))); - assert!(l.contains(&v_string("BAR".into()))); - } - - #[test] - fn test_list_contains_case_sensitive() { - let l = ListImplVector::from_slice(&[v_string("foo".into()), v_string("bar".into())]); - assert!(!l.contains_case_sensitive(&v_string("FOO".into()))); - assert!(!l.contains_case_sensitive(&v_string("BAR".into()))); - } -} diff --git a/crates/values/src/var/map.rs b/crates/values/src/var/map.rs index 97d5ada2..5f612382 100644 --- a/crates/values/src/var/map.rs +++ b/crates/values/src/var/map.rs @@ -12,162 +12,709 @@ // this program. If not, see . // -use crate::var::{Var, Variant}; -use crate::BincodeAsByteBufferExt; -use bincode::de::{BorrowDecoder, Decoder}; -use bincode::enc::Encoder; -use bincode::error::{DecodeError, EncodeError}; -use bincode::{BorrowDecode, Decode, Encode}; -use std::fmt::{Display, Formatter}; - -#[derive(Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub struct Map(im::OrdMap); -impl Default for Map { - fn default() -> Self { - Self::new() - } +use crate::var::storage::VarBuffer; +use crate::var::var::Var; +use crate::var::variant::Variant; +use crate::var::Associative; +use crate::var::Error::{E_RANGE, E_TYPE}; +use crate::var::{Error, VarType}; +use bytes::Bytes; +use flexbuffers::{BuilderOptions, Reader, VectorReader}; +use std::cmp::Ordering; +use std::hash::Hash; + +#[derive(Clone)] +pub struct Map { + // Reader must be boxed to avoid overfilling the stack. + pub reader: Box>, } impl Map { - pub fn new() -> Self { - Self(im::OrdMap::new()) + // Construct from an Iterator of paris + pub(crate) fn build<'a, I: Iterator>(pairs: I) -> Var { + // Our maps don't use the flexbuffers map type because that only allows strings for keys. + // Instead, we just use a vector of pairs, sorted, so binary search can be used to find + // keys in O(log n) time. + // Construction, however, is O(n) because we need to insert the pairs in sorted order. + // And make a copy, to boot. + let mut sorted: Vec<_> = pairs.collect(); + sorted.sort(); + + Self::build_presorted(sorted.into_iter()) } - pub fn from_pairs(pairs: &[(Var, Var)]) -> Self { - Self(pairs.iter().cloned().collect()) + pub(crate) fn build_presorted<'a, I: Iterator>(pairs: I) -> Var { + let mut builder = flexbuffers::Builder::new(BuilderOptions::empty()); + let mut vb = builder.start_vector(); + vb.push(VarType::TYPE_MAP as u8); + let mut mv = vb.start_vector(); + for (k, v) in pairs { + k.variant().push_item(&mut mv); + v.variant().push_item(&mut mv); + } + mv.end_vector(); + vb.end_vector(); + let buf = builder.take_buffer(); + let buf = Bytes::from(buf); + let reader = Reader::get_root(VarBuffer(buf)).unwrap(); + let v = Variant::from_reader(reader); + Var(v) } - fn from_map(map: im::OrdMap) -> Self { - Self(map) + pub fn iter(&self) -> impl Iterator + '_ { + (0..self.len()).map(move |i| { + let k = self.reader.idx(i * 2); + let v = self.reader.idx(i * 2 + 1); + let key = Variant::from_reader(k); + let value = Variant::from_reader(v); + (Var(key), Var(value)) + }) } - pub fn len(&self) -> usize { - self.0.len() + fn binary_search<'a, F: Fn(&'a Var, &Var) -> Ordering>( + &self, + f: F, + c: &'a Var, + ) -> Option { + let n = self.reader.len() / 2; + let mut low = 0; + let mut high = (n as isize) - 1; + while low <= high { + let mid = (low + high) / 2; + let k = self.reader.idx((mid * 2) as usize); + let k = Variant::from_reader(k); + let v = Var(k); + match f(c, &v) { + Ordering::Less => high = mid - 1, + Ordering::Greater => low = mid + 1, + Ordering::Equal => return Some(mid as usize), + } + } + + None } +} - pub fn is_empty(&self) -> bool { - self.0.is_empty() +impl PartialEq for Map { + fn eq(&self, other: &Self) -> bool { + if self.len() != other.len() { + return false; + } + + // elements comparison using iterator + for (a, b) in self.iter().zip(other.iter()) { + if a != b { + return false; + } + } + + true } +} - pub fn get(&self, key: &Var) -> Option<&Var> { - self.0.get(key) +impl Associative for Map { + fn is_empty(&self) -> bool { + self.reader.len() == 0 } - /// Copy-on-write insert. - /// Add a key-value pair to the map, returning a new map with the pair added. - pub fn insert(&self, key: Var, value: Var) -> Var { - let mut new_map = self.0.clone(); - new_map.insert(key, value); - Var::new(Variant::Map(Map::from_map(new_map))) + fn len(&self) -> usize { + self.reader.len() / 2 } - /// Return a map which is subset of this map where the keys lay between `start` and `end`, - /// (exclusive of `end`, inclusive of `start`.) - pub fn range(&self, start: Var, end: Var) -> Var { - let mut new_map = im::OrdMap::new(); - let range = self.0.range(start..end); - for (key, value) in range { - new_map.insert(key.clone(), value.clone()); + fn index(&self, key: &Var) -> Result { + let n = self.reader.len() / 2; + + if n == 0 { + return Err(E_RANGE); } - Var::new(Variant::Map(Map::from_map(new_map))) + // Items are in sorted order, so we can binary search. + let Some(pos) = self.binary_search(|a, b| a.cmp(b), key) else { + return Err(E_RANGE); + }; + + let v = self.reader.idx((pos * 2) + 1); + let v = Variant::from_reader(v); + let v = Var(v); + Ok(v) } - /// Replace the specified range in the map. `from` and `to` must be valid keys in the map, and - /// `to` must be a Map. All tuples between from and to are removed, and the values from `to` - /// are inserted in their place. Unlike above, the whole range is inclusive. - pub fn range_set(&self, start: Var, end: Var, to: &Map) -> Var { - let mut new_map = self.0.clone(); - let range = self.0.range(start..=end); - for (key, _) in range { - new_map.remove(key); + fn index_in(&self, key: &Var, case_sensitive: bool) -> Result, Error> { + // Check the values in the key-value pairs and return the index of the first match. + // Linear O(N) operation. + for i in 0..self.len() { + let v = self.reader.idx((i * 2) + 1); + let v = Variant::from_reader(v); + let v = Var(v); + let matches = if case_sensitive { + v.eq_case_sensitive(key) + } else { + v.eq(key) + }; + if matches { + return Ok(Some(i)); + } } - let new_map = new_map.union(to.0.clone()); - Var::new(Variant::Map(Map::from_map(new_map))) + Ok(None) } - /// Return a map with `key` removed, if it exists. If it does not exist, return the original map. - /// The removed value is returned as the second element of the tuple. - pub fn remove(&self, key: &Var) -> (Var, Option) { - let mut removed = self.0.clone(); - let removed_value = removed.remove(key); - let nm = Var::new(Variant::Map(Map::from_map(removed))); - (nm, removed_value) + fn index_set(&self, key: &Var, value: &Var) -> Result { + // Stunt has a restriction that non-scalars cannot be keys (unless they're strings). + // So we enforce that here, even though it's not strictly necessary. + if !key.is_scalar() && !key.is_string() { + return Err(E_TYPE); + } + + // If the key is already in the map, we replace the value. + // Otherwise, we add a new key-value pair, which requires re-sorting... + // So no matter what, this is an expensive O(N) operation, requiring multiple copies. + // We'll just build a new, vector, and then pass the iterator into the build function. + + // TODO: find a way to construct chained iterators for this instead... + + let mut new_vec = Vec::with_capacity(self.len() + 1); + let mut found = false; + for (k, v) in self.iter() { + if k == *key { + new_vec.push((key.clone(), value.clone())); + found = true; + } else { + new_vec.push((k, v)); + } + } + if !found { + new_vec.push((key.clone(), value.clone())); + } + Ok(Self::build(new_vec.iter())) } - pub fn iter(&self) -> impl Iterator { - self.0.iter() + /// Return the range of key-value pairs between the two keys. + fn range(&self, from: &Var, to: &Var) -> Result { + // Find start with binary search. + let start = match self.binary_search(|a, b| a.cmp(b), from) { + Some(pos) => pos, + None => return Err(E_RANGE), + }; + + // Now scan forward to find the end. + let mut new_vec = Vec::new(); + let to = to.variant(); + for i in start..self.len() { + let k = self.reader.idx(i * 2); + let k = Variant::from_reader(k); + let order = k.cmp(to); + if order == Ordering::Greater || order == Ordering::Equal { + break; + } + let v = self.reader.idx(i * 2 + 1); + let v = Variant::from_reader(v); + new_vec.push((Var(k), Var(v))); + } + + Ok(Self::build_presorted(new_vec.iter())) } - pub fn eq_case_sensitive(&self, other: &Map) -> bool { - // TODO: Surely there's a smarter way of doing this with a single well-aimed call to - // some API on self.0. - for (k1, v1) in self.iter() { - if !other - .iter() - .any(|(k2, v2)| k1.eq_case_sensitive(k2) && v1.eq_case_sensitive(v2)) - { - return false; + fn range_set(&self, _from: &Var, _to: &Var, _with: &Var) -> Result { + // We reject range assignment, because it's tricky to get right, and Stunt does weird + // things. But code to do it is here, if we ever need it. + return Err(E_TYPE); + + #[allow(unreachable_code)] + { + let with = match _with.variant() { + Variant::Map(m) => m, + _ => return Err(E_TYPE), + }; + + let mut new_pairs = Vec::with_capacity(self.len() + with.len()); + for (k, v) in self.iter() { + if k.eq(_from) + || k.cmp(_from) == Ordering::Greater && k.cmp(_to) == Ordering::Less + || k.eq(_to) + { + continue; + } + + new_pairs.push((k, v)); + } + for (k, v) in with.iter() { + new_pairs.push((k, v)); } + + Ok(Self::build(new_pairs.iter())) } - for (k2, _) in other.iter() { - // We already handled value mismatches and (self has, other hasn't) - // Just need to handle (self hasn't, other has) here - if !self.iter().any(|(k1, _)| k1.eq_case_sensitive(k2)) { - return false; + } + + fn keys(&self) -> Vec { + let mut keys = Vec::new(); + for i in 0..self.len() { + let k = self.reader.idx(i * 2); + let key = Variant::from_reader(k); + keys.push(Var(key)); + } + keys + } + + fn values(&self) -> Vec { + let mut values = Vec::new(); + for i in 0..self.len() { + let v = self.reader.idx(i * 2 + 1); + let value = Variant::from_reader(v); + values.push(Var(value)); + } + values + } + + fn contains_key(&self, key: &Var, case_sensitive: bool) -> Result { + let n = self.reader.len() / 2; + + if n == 0 { + return Ok(false); + } + let cmp = |a: &Var, b: &Var| { + if case_sensitive { + b.cmp_case_sensitive(a) + } else { + b.cmp(a) + } + }; + let result = self.binary_search(cmp, key).is_some(); + Ok(result) + } + + /// Return this map with the key/value pair removed. + /// Return the new map and the value that was removed, if any + fn remove(&self, key: &Var, case_sensitive: bool) -> (Var, Option) { + // Return a copy of self without the key, and the value that was removed, if any. + let mut new_pairs = Vec::with_capacity(self.len()); + let mut removed = None; + for (k, v) in self.iter() { + let matches = if case_sensitive { + k.cmp_case_sensitive(key) == Ordering::Equal + } else { + k == *key + }; + if matches { + removed = Some(v); + } else { + new_pairs.push((k, v)); } } - true + (Self::build(new_pairs.iter()), removed) } } -impl BincodeAsByteBufferExt for Map {} +impl Eq for Map {} -impl Encode for Map { - fn encode(&self, encoder: &mut E) -> Result<(), EncodeError> { - // There's no bincode impl for im::HashMap, so we'll encode the pairs as a list of tuples. - // Starting with the count of the number of pairs. - self.len().encode(encoder)?; - for (key, value) in self.iter() { - key.encode(encoder)?; - value.encode(encoder)?; - } - Ok(()) +impl PartialOrd for Map { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) } } -impl Decode for Map { - fn decode(decoder: &mut D) -> Result { - let tuple_count = usize::decode(decoder)?; - let mut pair_vec = Vec::with_capacity(tuple_count); - for _ in 0..tuple_count { - let key = Var::decode(decoder)?; - let value = Var::decode(decoder)?; - pair_vec.push((key, value)); +impl Ord for Map { + fn cmp(&self, other: &Self) -> Ordering { + if self.len() != other.len() { + return self.len().cmp(&other.len()); } - Ok(Map::from_pairs(&pair_vec)) + + // elements comparison + for (a, b) in self.iter().zip(other.iter()) { + match a.cmp(&b) { + Ordering::Equal => continue, + x => return x, + } + } + + Ordering::Equal } } -impl<'de> BorrowDecode<'de> for Map { - fn borrow_decode>(decoder: &mut D) -> Result { - let tuple_count = usize::decode(decoder)?; - let mut pair_vec = Vec::with_capacity(tuple_count); - for _ in 0..tuple_count { - let key = Var::borrow_decode(decoder)?; - let value = Var::borrow_decode(decoder)?; - pair_vec.push((key, value)); +impl Hash for Map { + fn hash(&self, state: &mut H) { + for item in self.iter() { + item.0.hash(state); + item.1.hash(state); } - Ok(Map::from_pairs(&pair_vec)) } } -impl Display for Map { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str("[ ")?; - for (key, value) in self.iter() { - write!(f, "{:?} -> {:?}, ", key, value)?; +#[cfg(test)] +mod tests { + use crate::var::var::Var; + use crate::var::variant::Variant; + use crate::var::{Associative, IndexMode}; + use crate::{v_bool, v_int, v_str}; + + #[test] + fn test_map_pack_unpack_index() { + let m = Var::mk_map(&[ + (Var::mk_str("a"), Var::mk_integer(1)), + (Var::mk_str("b"), Var::mk_integer(2)), + (Var::mk_integer(3), Var::mk_str("c")), + ]); + + match m.variant() { + Variant::Map(m) => { + assert_eq!(m.len(), 3); + } + _ => panic!("Expected map"), + } + + let key = Var::mk_str("a"); + let value = m.index(&key, IndexMode::ZeroBased).unwrap(); + match value.variant() { + Variant::Int(i) => assert_eq!(*i, 1), + _ => panic!("Expected integer"), } - f.write_str(" ]") + } + + #[test] + fn test_map_is_funcs() { + let m = Var::mk_map(&[ + (Var::mk_str("a"), Var::mk_integer(1)), + (Var::mk_str("b"), Var::mk_integer(2)), + (Var::mk_integer(3), Var::mk_str("c")), + ]); + + assert!(m.is_true()); + assert!(!m.is_sequence()); + assert!(m.is_associative()); + assert!(!m.is_scalar()); + assert_eq!(m.len().unwrap(), 3); + assert!(!m.is_empty().unwrap()); + + let m = Var::mk_map(&[]); + assert!(!m.is_true()); + assert!(!m.is_sequence()); + assert!(m.is_associative()); + assert!(!m.is_scalar()); + assert_eq!(m.len().unwrap(), 0); + assert!(m.is_empty().unwrap()); + } + + #[test] + fn test_map_equality_inequality() { + let m1 = Var::mk_map(&[ + (Var::mk_str("a"), Var::mk_integer(1)), + (Var::mk_str("b"), Var::mk_integer(2)), + (Var::mk_integer(3), Var::mk_str("c")), + ]); + + let m2 = Var::mk_map(&[ + (Var::mk_str("a"), Var::mk_integer(1)), + (Var::mk_str("b"), Var::mk_integer(2)), + (Var::mk_integer(3), Var::mk_str("c")), + ]); + + let m3 = Var::mk_map(&[ + (Var::mk_str("a"), Var::mk_integer(1)), + (Var::mk_str("b"), Var::mk_integer(2)), + (Var::mk_integer(3), Var::mk_str("d")), + ]); + + let m4 = Var::mk_map(&[ + (Var::mk_str("a"), Var::mk_integer(1)), + (Var::mk_str("b"), Var::mk_integer(2)), + ]); + + let m5 = Var::mk_map(&[ + (Var::mk_str("a"), Var::mk_integer(1)), + (Var::mk_str("b"), Var::mk_integer(2)), + (Var::mk_integer(3), Var::mk_str("c")), + (Var::mk_integer(4), Var::mk_str("d")), + ]); + + assert_eq!(m1, m2); + assert_ne!(m1, m3); + assert_ne!(m1, m4); + assert_ne!(m1, m5); + } + + #[test] + fn test_map_index() { + let m = Var::mk_map(&[ + (Var::mk_str("a"), Var::mk_integer(1)), + (Var::mk_str("b"), Var::mk_integer(2)), + (Var::mk_integer(3), Var::mk_str("c")), + ]); + + let key = Var::mk_str("b"); + let value = m.index(&key, IndexMode::ZeroBased).unwrap(); + match value.variant() { + Variant::Int(i) => assert_eq!(*i, 2), + _ => panic!("Expected integer"), + } + } + + #[test] + fn test_map_index_set() { + let m = Var::mk_map(&[ + (Var::mk_str("a"), Var::mk_integer(1)), + (Var::mk_str("b"), Var::mk_integer(2)), + (Var::mk_integer(3), Var::mk_str("c")), + ]); + + let r = m + .index_set( + &Var::mk_str("b"), + &Var::mk_integer(42), + IndexMode::ZeroBased, + ) + .unwrap(); + let r = r.variant(); + match r { + Variant::Map(m) => { + let r = m.index(&Var::mk_str("b")).unwrap(); + match r.variant() { + Variant::Int(i) => assert_eq!(*i, 42), + _ => panic!("Expected integer, got {:?}", r), + } + } + _ => panic!("Expected map, got {:?}", r), + } + + // Insert new item + let r = m + .index_set( + &Var::mk_str("d"), + &Var::mk_integer(42), + IndexMode::ZeroBased, + ) + .unwrap(); + let r = r.variant(); + match r { + Variant::Map(m) => { + let r = m.index(&Var::mk_str("d")).unwrap(); + match r.variant() { + Variant::Int(i) => assert_eq!(*i, 42), + _ => panic!("Expected integer, got {:?}", r), + } + } + _ => panic!("Expected map, got {:?}", r), + } + } + + #[test] + fn test_map_keys_values() { + let m = Var::mk_map(&[ + (Var::mk_str("a"), Var::mk_integer(1)), + (Var::mk_str("b"), Var::mk_integer(2)), + (Var::mk_integer(3), Var::mk_str("c")), + ]); + + let m = match m.variant() { + Variant::Map(m) => m, + _ => panic!("Expected map"), + }; + + // The keys come out in sorted order. + let keys = m.keys(); + assert_eq!(keys.len(), 3); + assert_eq!(keys[0], Var::mk_integer(3)); + assert_eq!(keys[1], Var::mk_str("a")); + assert_eq!(keys[2], Var::mk_str("b")); + + let values = m.values(); + assert_eq!(values.len(), 3); + assert_eq!(values[0], Var::mk_str("c")); + assert_eq!(values[1], Var::mk_integer(1)); + assert_eq!(values[2], Var::mk_integer(2)); + } + + #[test] + fn test_map_range() { + let m = Var::mk_map(&[ + (Var::mk_integer(0), Var::mk_integer(1)), + (Var::mk_integer(1), Var::mk_integer(2)), + (Var::mk_integer(2), Var::mk_integer(3)), + (Var::mk_integer(3), Var::mk_integer(4)), + ]); + + let r = m + .range( + &Var::mk_integer(1), + &Var::mk_integer(3), + IndexMode::OneBased, + ) + .unwrap(); + let r = match r.variant() { + Variant::Map(m) => m, + _ => panic!("Expected map"), + }; + + let key_value_results = r.iter().collect::>(); + assert_eq!( + key_value_results, + vec![(v_int(1), v_int(2)), (v_int(2), v_int(3))] + ); + } + + #[test] + // Disable because range_set is stubbed out in our impl + #[ignore] + fn test_map_range_set() { + let m = Var::mk_map(&[ + (Var::mk_str("a"), Var::mk_integer(1)), + (Var::mk_str("b"), Var::mk_integer(2)), + (Var::mk_str("c"), Var::mk_integer(3)), + (Var::mk_str("d"), Var::mk_integer(4)), + (Var::mk_str("e"), Var::mk_integer(5)), + ]); + + // Now replace b, c, d with x, y + let r = m + .range_set( + &Var::mk_str("b"), + &Var::mk_str("d"), + &Var::mk_map(&[ + (Var::mk_str("x"), Var::mk_integer(42)), + (Var::mk_str("y"), Var::mk_integer(43)), + ]), + IndexMode::ZeroBased, + ) + .unwrap(); + + let r = match r.variant() { + Variant::Map(m) => m, + _ => panic!("Expected map"), + }; + + assert_eq!(r.len(), 4); + let keys = r.keys(); + assert_eq!(keys.len(), 4); + assert_eq!(keys[0], Var::mk_str("a")); + assert_eq!(keys[1], Var::mk_str("e")); + assert_eq!(keys[2], Var::mk_str("x")); + assert_eq!(keys[3], Var::mk_str("y")); + } + + #[test] + fn test_map_contains_key() { + let m = Var::mk_map(&[ + (Var::mk_str("a"), Var::mk_integer(1)), + (Var::mk_str("b"), Var::mk_integer(2)), + (Var::mk_str("c"), Var::mk_integer(3)), + ]); + + let key = Var::mk_str("B"); + let not_key = Var::mk_str("d"); + + // Case-insensitive + assert_eq!(m.contains(&key, false).unwrap(), v_bool(true)); + assert_eq!(m.contains(¬_key, true).unwrap(), v_bool(false)); + + // Case sensitive + assert_eq!(m.contains(&key, true).unwrap(), v_bool(false)); + assert_eq!(m.contains(¬_key, false).unwrap(), v_bool(false)); + } + + #[test] + fn test_map_remove_key() { + let m = Var::mk_map(&[ + (Var::mk_str("a"), Var::mk_integer(1)), + (Var::mk_str("b"), Var::mk_integer(2)), + (Var::mk_str("c"), Var::mk_integer(3)), + ]); + + let key = Var::mk_str("b"); + let not_key = Var::mk_str("d"); + + let (r, removed) = m.remove(&key, false).expect("remove failed"); + assert_eq!(r.len().unwrap(), 2); + assert_eq!(removed.unwrap(), Var::mk_integer(2)); + + let (r, removed) = m.remove(¬_key, false).expect("remove failed"); + assert_eq!(r.len().unwrap(), 3); + assert_eq!(removed, None); + + // Case sensitive + let not_key = Var::mk_str("B"); + let (r, removed) = m.remove(¬_key, true).expect("remove failed"); + assert_eq!(r.len().unwrap(), 3); + assert_eq!(removed, None); + + let (r, removed) = m.remove(&key, true).expect("remove failed"); + assert_eq!(r.len().unwrap(), 2); + assert_eq!(removed.unwrap(), Var::mk_integer(2)); + } + + #[test] + /// Verify that sort order is preserved after insertion + fn test_map_insertion_ordering() { + let m = Var::mk_map(&[ + (Var::mk_integer(3), Var::mk_integer(3)), + (Var::mk_integer(1), Var::mk_integer(1)), + (Var::mk_integer(4), Var::mk_integer(4)), + (Var::mk_integer(5), Var::mk_integer(5)), + (Var::mk_integer(9), Var::mk_integer(9)), + (Var::mk_integer(2), Var::mk_integer(2)), + ]); + + let m = m + .index_set(&Var::mk_str("a"), &Var::mk_str("a"), IndexMode::OneBased) + .unwrap(); + let m = m + .index_set( + &Var::mk_integer(6), + &Var::mk_integer(6), + IndexMode::OneBased, + ) + .unwrap(); + + let m = match m.variant() { + Variant::Map(m) => m, + _ => panic!("Expected map"), + }; + + let pairs = m.iter().collect::>(); + assert_eq!( + pairs, + vec![ + (v_int(1), v_int(1)), + (v_int(2), v_int(2)), + (v_int(3), v_int(3)), + (v_int(4), v_int(4)), + (v_int(5), v_int(5)), + (v_int(6), v_int(6)), + (v_int(9), v_int(9)), + (v_str("a"), v_str("a")), + ] + ); + } + + #[test] + fn test_index_in() { + // ["3" -> "3", "1" -> "1", "4" -> "4", "5" -> "5", "9" -> "9", "2" -> "2"]; + let m = Var::mk_map(&[ + (Var::mk_str("3"), Var::mk_str("3")), + (Var::mk_str("1"), Var::mk_str("1")), + (Var::mk_str("4"), Var::mk_str("4")), + (Var::mk_str("5"), Var::mk_str("5")), + (Var::mk_str("9"), Var::mk_str("9")), + (Var::mk_str("2"), Var::mk_str("2")), + ]); + // "2" -> 2nd position + let key = Var::mk_str("2"); + let pos = m.index_in(&key, false, IndexMode::OneBased).unwrap(); + assert_eq!(pos, v_int(2)); + } + + #[test] + fn test_case_sensitive_compare() { + let m_a = Var::mk_map(&[ + (Var::mk_str("a"), Var::mk_str("a")), + (Var::mk_str("b"), Var::mk_str("b")), + (Var::mk_str("c"), Var::mk_str("c")), + ]); + + let m_b = Var::mk_map(&[ + (Var::mk_str("A"), Var::mk_str("A")), + (Var::mk_str("B"), Var::mk_str("B")), + (Var::mk_str("C"), Var::mk_str("C")), + ]); + + assert!(!m_a.eq_case_sensitive(&m_b)); + assert!(m_a.eq(&m_b)); } } diff --git a/crates/values/src/var/mod.rs b/crates/values/src/var/mod.rs index 6d10e834..dd219464 100644 --- a/crates/values/src/var/mod.rs +++ b/crates/values/src/var/mod.rs @@ -12,66 +12,47 @@ // this program. If not, see . // -#![allow(non_camel_case_types, non_snake_case)] - -use std::cmp::Ordering; -use std::fmt::{Debug, Display, Formatter}; -use std::hash::{Hash, Hasher}; -use std::str::FromStr; - -use bincode::de::{BorrowDecoder, Decoder}; -use bincode::enc::Encoder; -use bincode::error::{DecodeError, EncodeError}; -use bincode::{BorrowDecode, Decode, Encode}; -use bytes::Bytes; -use decorum::R64; -use lazy_static::lazy_static; -use strum::FromRepr; - -use crate::encode::BINCODE_CONFIG; -use crate::util::quote_str; -pub use crate::var::error::{Error, ErrorPack}; -pub use crate::var::list::List; -pub use crate::var::map::Map; -pub use crate::var::objid::Objid; -pub use crate::var::string::Str; -pub use crate::var::symbol::Symbol; -pub use crate::var::variant::Variant; -use crate::{AsByteBuffer, DecodingError, EncodingError}; +//! TODO: +//! to_literal / formatting +//! move everything up and over into `../var` +//! go through all uses of existing var and update them to use the new var +//! any missed functionality along the way? +//! more tests, and make pass moot once above is done +//! later: +//! more documentation +//! builder vs reader mode? - optimization for values under construction. +//! some From/Into impls for common types might be missing mod error; mod list; -#[allow(dead_code)] -mod list_impl_buffer; -#[allow(dead_code)] -mod list_impl_vector; mod map; mod objid; +mod scalar; +mod storage; mod string; mod symbol; +#[allow(clippy::module_inception)] +mod var; mod variant; -mod varops; -lazy_static! { - static ref VAR_NONE: Var = Variant::None.into(); - static ref VAR_EMPTY_LIST: Var = Variant::List(List::new()).into(); - static ref VAR_EMPTY_STR: Var = Var::new(Variant::Str(Str::from_str("").unwrap())); -} - -// Macro to call v_list with vector arguments to construct instead of having to do v_list(&[...]) -#[allow(unused_macros)] -macro_rules! v_lst { - () => ( - $crate::values::var::v_empty_list() - ); - ($($x:expr),+ $(,)?) => ( - vec![$($x),+] - ); -} +pub use error::{Error, ErrorPack}; +pub use list::List; +pub use map::Map; +pub use objid::Objid; +use std::fmt::Debug; +pub use string::Str; +use strum::FromRepr; +pub use symbol::Symbol; +pub use var::{ + v_bool, v_empty_list, v_empty_map, v_empty_str, v_err, v_float, v_floatr, v_int, v_list, + v_list_iter, v_map, v_none, v_obj, v_objid, v_str, v_string, Var, +}; +pub use variant::Variant; /// Integer encoding of values as represented in a `LambdaMOO` textdump, and by `bf_typeof` #[repr(u8)] #[derive(Clone, Copy, Debug, Eq, PartialEq, FromRepr)] +#[allow(non_camel_case_types)] pub enum VarType { TYPE_INT = 0, TYPE_OBJ = 1, @@ -84,873 +65,120 @@ pub enum VarType { TYPE_MAP = 10, } -/// Var is our variant type / tagged union used to represent MOO's dynamically typed values. -#[derive(Clone)] -pub struct Var { - value: Variant, -} - -impl Var { - #[must_use] - pub fn new(value: Variant) -> Self { - Self { value } - } - - #[must_use] - pub fn is_root(&self) -> bool { - match self.variant() { - Variant::Obj(o) => o.is_sysobj(), - _ => false, - } - } -} - -impl Encode for Var { - fn encode(&self, encoder: &mut E) -> Result<(), EncodeError> { - // Use our own encoded form. - let encoded = encode(self); - bincode::encode_into_writer(encoded.as_ref(), encoder.writer(), *BINCODE_CONFIG)?; - Ok(()) - } -} - -impl Decode for Var { - fn decode(decoder: &mut D) -> Result { - let slc: Vec = bincode::decode_from_reader(decoder.reader(), *BINCODE_CONFIG)?; - Ok(decode(Bytes::from(slc))) - } -} - -impl<'de> BorrowDecode<'de> for Var { - fn borrow_decode>(decoder: &mut D) -> Result { - let slc: Vec = bincode::decode_from_reader(decoder.reader(), *BINCODE_CONFIG)?; - Ok(decode(Bytes::from(slc))) - } -} - -fn encoded_size(v: &Var) -> usize { - match v.variant() { - Variant::None => 1, - Variant::Str(s) => 1 + s.as_bytes().unwrap().len(), - Variant::Obj(o) => 1 + o.as_bytes().unwrap().len(), - Variant::Int(_) => 9, - Variant::Float(_) => 9, - Variant::Err(_) => 2, - Variant::List(l) => 1 + l.as_bytes().unwrap().len(), - Variant::Map(m) => 1 + m.as_bytes().unwrap().len(), - } -} - -fn encode(v: &Var) -> Bytes { - let mut buffer = vec![]; - // Push the type first. - buffer.push(v.type_id() as u8); - match v.variant() { - Variant::None => { - // Nothing to do. - } - Variant::Str(s) => { - buffer.extend_from_slice(s.as_bytes().unwrap().as_ref()); - } - Variant::Obj(o) => { - buffer.extend_from_slice(o.as_bytes().unwrap().as_ref()); - } - Variant::Int(i) => { - buffer.extend_from_slice(&i.to_le_bytes()); - } - Variant::Float(f) => { - buffer.extend_from_slice(&f.to_le_bytes()); - } - Variant::Err(e) => { - buffer.extend_from_slice(&[*e as u8]); - } - Variant::List(l) => { - buffer.extend_from_slice(l.as_bytes().unwrap().as_ref()); - } - Variant::Map(m) => { - buffer.extend_from_slice(m.as_bytes().unwrap().as_ref()); - } - } - Bytes::from(buffer) -} - -fn decode(s: Bytes) -> Var { - assert!(!s.is_empty()); - let type_id = s.as_ref()[0]; - let type_id = VarType::from_repr(type_id).expect("Invalid type id"); - let bytes = s.slice(1..); - match type_id { - VarType::TYPE_NONE => VAR_NONE.clone(), - VarType::TYPE_STR => { - let s = Str::from_bytes(bytes).unwrap(); - Var::new(Variant::Str(s)) - } - VarType::TYPE_OBJ => { - let o = Objid::from_bytes(bytes).unwrap(); - Var::new(Variant::Obj(o)) - } - VarType::TYPE_INT => { - let mut i_bytes = [0; 8]; - i_bytes.copy_from_slice(bytes.as_ref()); - let i = i64::from_le_bytes(i_bytes); - Var::new(Variant::Int(i)) - } - VarType::TYPE_FLOAT => { - let mut f_bytes = [0; 8]; - f_bytes.copy_from_slice(bytes.as_ref()); - let f = f64::from_le_bytes(f_bytes); - Var::new(Variant::Float(f)) - } - VarType::TYPE_ERR => { - let e = Error::from_repr(bytes.as_ref()[0]).unwrap(); - Var::new(Variant::Err(e)) - } - VarType::TYPE_LIST => { - let l = List::from_bytes(bytes).unwrap(); - Var::new(Variant::List(l)) - } - VarType::TYPE_MAP => { - let m = Map::from_bytes(bytes).unwrap(); - Var::new(Variant::Map(m)) - } - _ => panic!("Invalid type id: {:?}", type_id), - } -} - -// For now the byte buffer repr of a Var is its Bincode encoding, but this will likely change in -// the future. -impl AsByteBuffer for Var { - fn size_bytes(&self) -> usize { - encoded_size(self) - } - - fn with_byte_buffer R>(&self, mut f: F) -> Result - where - Self: Sized, - { - let encoded = encode(self); - Ok(f(encoded.as_ref())) - } - - fn make_copy_as_vec(&self) -> Result, EncodingError> - where - Self: Sized, - { - let encoded = encode(self); - Ok(encoded.as_ref().to_vec()) - } - - fn from_bytes(bytes: Bytes) -> Result - where - Self: Sized, - { - Ok(decode(bytes)) - } - - fn as_bytes(&self) -> Result { - let encoded = encode(self); - Ok(encoded) - } -} - -#[must_use] -pub fn v_bool(b: bool) -> Var { - Var::new(Variant::Int(i64::from(b))) -} - -#[must_use] -pub fn v_int(i: i64) -> Var { - Var::new(Variant::Int(i)) -} - -#[must_use] -pub fn v_float(f: f64) -> Var { - Var::new(Variant::Float(f)) -} - -#[must_use] -pub fn v_str(s: &str) -> Var { - Var::new(Variant::Str(Str::from_str(s).unwrap())) -} - -#[must_use] -pub fn v_string(s: String) -> Var { - Var::new(Variant::Str(Str::from_string(s))) -} - -#[must_use] -pub fn v_objid(o: Objid) -> Var { - Var::new(Variant::Obj(o)) -} - -#[must_use] -pub fn v_obj(o: i64) -> Var { - Var::new(Variant::Obj(Objid(o))) -} - -#[must_use] -pub fn v_err(e: Error) -> Var { - Var::new(Variant::Err(e)) -} - -#[must_use] -pub fn v_list(l: &[Var]) -> Var { - Var::new(Variant::List(List::from_slice(l))) -} - -#[must_use] -pub fn v_listv(l: Vec) -> Var { - Var::new(Variant::List(List::from_slice(&l))) -} - -#[must_use] -pub fn v_empty_list() -> Var { - VAR_EMPTY_LIST.clone() -} - -#[must_use] -pub fn v_empty_str() -> Var { - VAR_EMPTY_STR.clone() -} - -#[must_use] -pub fn v_none() -> Var { - VAR_NONE.clone() -} - -#[must_use] -pub fn v_empty_map() -> Var { - Var::new(Variant::Map(Map::new())) -} - -#[must_use] -pub fn v_map_pairs(pairs: &[(Var, Var)]) -> Var { - Var::new(Variant::Map(Map::from_pairs(pairs))) +/// Sequence index modes: 0 or 1 indexed. +/// This is used to determine how to handle index operations on sequences. Internally containers use +/// 0-based indexing, but MOO uses 1-based indexing, so we allow the user to choose. +#[derive(Clone, Copy, Debug)] +pub enum IndexMode { + ZeroBased, + OneBased, } -impl Var { - /// Return a reference to the inner variant that this Var is wrapping. - #[must_use] - #[inline] - pub fn variant(&self) -> &Variant { - &self.value - } - - #[must_use] - #[inline] - pub fn variant_mut(&mut self) -> &mut Variant { - &mut self.value - } - - /// Destroy the Var and return the inner variant that it was wrapping. - #[must_use] - #[inline] - pub fn take_variant(self) -> Variant { - self.value - } - - #[must_use] - pub fn type_id(&self) -> VarType { - match self.variant() { - Variant::None => VarType::TYPE_NONE, - Variant::Str(_) => VarType::TYPE_STR, - Variant::Obj(_) => VarType::TYPE_OBJ, - Variant::Int(_) => VarType::TYPE_INT, - Variant::Float(_) => VarType::TYPE_FLOAT, - Variant::Err(_) => VarType::TYPE_ERR, - Variant::List(_) => VarType::TYPE_LIST, - Variant::Map(_) => VarType::TYPE_MAP, - } - } - - #[must_use] - pub fn eq_case_sensitive(&self, other: &Self) -> bool { - match (self.variant(), other.variant()) { - (Variant::Str(l), Variant::Str(r)) => l.as_str().eq(r.as_str()), - _ => self == other, - } - } - - #[must_use] - pub fn to_literal(&self) -> String { - match self.variant() { - Variant::None => "None".to_string(), - Variant::Int(i) => i.to_string(), - Variant::Float(f) => format!("{f:?}"), - Variant::Str(s) => quote_str(s.as_str()), - Variant::Obj(o) => format!("{o}"), - Variant::List(l) => { - let mut result = String::new(); - result.push('{'); - for (i, v) in l.iter().enumerate() { - if i > 0 { - result.push_str(", "); - } - result.push_str(&v.to_literal()); - } - result.push('}'); - result - } - Variant::Map(m) => { - let mut result = String::new(); - result.push('['); - for (i, (k, v)) in m.iter().enumerate() { - if i > 0 { - result.push_str(", "); - } - result.push_str(&k.to_literal()); - result.push_str(" -> "); - result.push_str(&v.to_literal()); - } - result.push(']'); - result - } - Variant::Err(e) => e.name().to_string(), - } - } -} - -impl Display for Var { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(self.to_literal().as_str()) - } -} - -impl Debug for Var { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(self.to_literal().as_str()) - } -} - -impl PartialEq for Var { - fn eq(&self, other: &Self) -> bool { - match (self.variant(), other.variant()) { - (Variant::None, Variant::None) => true, - (Variant::Str(l), Variant::Str(r)) => l == r, - (Variant::Obj(l), Variant::Obj(r)) => l == r, - (Variant::Int(l), Variant::Int(r)) => l == r, - (Variant::Float(l), Variant::Float(r)) => l == r, - (Variant::Err(l), Variant::Err(r)) => l == r, - (Variant::List(l), Variant::List(r)) => l == r, - (Variant::Map(l), Variant::Map(r)) => l == r, - (Variant::None, _) => false, - (Variant::Str(_), _) => false, - (Variant::Obj(_), _) => false, - (Variant::Int(_), _) => false, - (Variant::Float(_), _) => false, - (Variant::Err(_), _) => false, - (Variant::List(_), _) => false, - (Variant::Map(_), _) => false, - } - } -} - -impl PartialOrd for Var { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Var { - fn cmp(&self, other: &Self) -> Ordering { - match (self.variant(), other.variant()) { - (Variant::None, Variant::None) => Ordering::Equal, - (Variant::Str(l), Variant::Str(r)) => l.cmp(r), - (Variant::Obj(l), Variant::Obj(r)) => l.cmp(r), - (Variant::Int(l), Variant::Int(r)) => l.cmp(r), - (Variant::Float(l), Variant::Float(r)) => R64::from(*l).cmp(&R64::from(*r)), - (Variant::Err(l), Variant::Err(r)) => l.cmp(r), - (Variant::List(l), Variant::List(r)) => l.cmp(r), - (Variant::Map(l), Variant::Map(r)) => l.cmp(r), - (Variant::None, _) => Ordering::Less, - (Variant::Str(_), _) => Ordering::Less, - (Variant::Obj(_), _) => Ordering::Less, - (Variant::Int(_), _) => Ordering::Less, - (Variant::Float(_), _) => Ordering::Less, - (Variant::Err(_), _) => Ordering::Less, - (Variant::List(_), _) => Ordering::Less, - (Variant::Map(_), _) => Ordering::Less, - } - } -} - -impl Hash for Var { - fn hash(&self, state: &mut H) { - let t = self.type_id() as u8; - t.hash(state); - match self.variant() { - Variant::None => {} - Variant::Str(s) => s.hash(state), - Variant::Obj(o) => o.hash(state), - Variant::Int(i) => i.hash(state), - Variant::Float(f) => R64::from(*f).hash(state), - Variant::Err(e) => e.hash(state), - Variant::List(l) => l.hash(state), - Variant::Map(m) => m.hash(state), - } - } -} - -impl Eq for Var {} - -impl<'a> From<&'a str> for Var { - fn from(s: &'a str) -> Self { - v_str(s) - } -} - -impl From for Var { - fn from(s: String) -> Self { - v_str(&s) - } -} - -impl From for Var { - fn from(i: i64) -> Self { - v_int(i) - } -} -impl From<&i64> for Var { - fn from(i: &i64) -> Self { - v_int(*i) - } -} - -impl From for Var { - fn from(f: f64) -> Self { - v_float(f) - } -} - -impl From for Var { - fn from(o: Objid) -> Self { - v_objid(o) - } -} - -impl From> for Var { - fn from(l: Vec) -> Self { - v_listv(l) - } -} - -impl From<[T; COUNT]> for Var -where - for<'a> Var: From<&'a T>, -{ - fn from(a: [T; COUNT]) -> Self { - v_list(&a.iter().map(|v| v.into()).collect::>()) - } -} - -impl From for Var { - fn from(e: Error) -> Self { - v_err(e) - } -} - -#[cfg(test)] -mod tests { - use std::cmp::Ordering; - - use crate::var::error::Error; - use crate::var::error::Error::{E_RANGE, E_TYPE}; - use crate::var::{v_empty_list, v_err, v_float, v_int, v_list, v_obj, v_str}; - - #[test] - fn test_add() { - assert_eq!(v_int(1).add(&v_int(2)), Ok(v_int(3))); - assert_eq!(v_int(1).add(&v_float(2.0)), Ok(v_float(3.0))); - assert_eq!(v_float(1.).add(&v_int(2)), Ok(v_float(3.))); - assert_eq!(v_float(1.).add(&v_float(2.)), Ok(v_float(3.))); - assert_eq!(v_str("a").add(&v_str("b")), Ok(v_str("ab"))); - } - - #[test] - fn test_sub() -> Result<(), Error> { - assert_eq!(v_int(1).sub(&v_int(2))?, v_int(-1)); - assert_eq!(v_int(1).sub(&v_float(2.))?, v_float(-1.)); - assert_eq!(v_float(1.).sub(&v_int(2))?, v_float(-1.)); - assert_eq!(v_float(1.).sub(&v_float(2.))?, v_float(-1.)); - Ok(()) - } - - #[test] - fn test_mul() -> Result<(), Error> { - assert_eq!(v_int(1).mul(&v_int(2))?, v_int(2)); - assert_eq!(v_int(1).mul(&v_float(2.))?, v_float(2.)); - assert_eq!(v_float(1.).mul(&v_int(2))?, v_float(2.)); - assert_eq!(v_float(1.).mul(&v_float(2.))?, v_float(2.)); - Ok(()) - } - - #[test] - fn test_div() -> Result<(), Error> { - assert_eq!(v_int(1).div(&v_int(2))?, v_int(0)); - assert_eq!(v_int(1).div(&v_float(2.))?, v_float(0.5)); - assert_eq!(v_float(1.).div(&v_int(2))?, v_float(0.5)); - assert_eq!(v_float(1.).div(&v_float(2.))?, v_float(0.5)); - Ok(()) - } - - #[test] - fn test_modulus() { - assert_eq!(v_int(1).modulus(&v_int(2)), Ok(v_int(1))); - assert_eq!(v_int(1).modulus(&v_float(2.)), Ok(v_float(1.))); - assert_eq!(v_float(1.).modulus(&v_int(2)), Ok(v_float(1.))); - assert_eq!(v_float(1.).modulus(&v_float(2.)), Ok(v_float(1.))); - assert_eq!(v_str("moop").modulus(&v_int(2)), Ok(v_err(E_TYPE))); - } - - #[test] - fn test_pow() { - assert_eq!(v_int(1).pow(&v_int(2)), Ok(v_int(1))); - assert_eq!(v_int(2).pow(&v_int(2)), Ok(v_int(4))); - assert_eq!(v_int(2).pow(&v_float(2.)), Ok(v_float(4.))); - assert_eq!(v_float(2.).pow(&v_int(2)), Ok(v_float(4.))); - assert_eq!(v_float(2.).pow(&v_float(2.)), Ok(v_float(4.))); - } - - #[test] - fn test_negative() { - assert_eq!(v_int(1).negative(), Ok(v_int(-1))); - assert_eq!(v_float(1.).negative(), Ok(v_float(-1.0))); - } - - #[test] - fn test_index() { - assert_eq!(v_list(&[v_int(1), v_int(2)]).index(0), Ok(v_int(1))); - assert_eq!(v_list(&[v_int(1), v_int(2)]).index(1), Ok(v_int(2))); - assert_eq!(v_list(&[v_int(1), v_int(2)]).index(2), Ok(v_err(E_RANGE))); - assert_eq!(v_str("ab").index(0), Ok(v_str("a"))); - assert_eq!(v_str("ab").index(1), Ok(v_str("b"))); - assert_eq!(v_str("ab").index(2), Ok(v_err(E_RANGE))); - } - - #[test] - fn test_eq() { - assert_eq!(v_int(1), v_int(1)); - assert_eq!(v_float(1.), v_float(1.)); - assert_eq!(v_str("a"), v_str("a")); - assert_eq!(v_str("a"), v_str("A")); - assert_eq!(v_list(&[v_int(1), v_int(2)]), v_list(&[v_int(1), v_int(2)])); - assert_eq!(v_obj(1), v_obj(1)); - assert_eq!(v_err(E_TYPE), v_err(E_TYPE)); - } - - #[test] - fn test_ne() { - assert_ne!(v_int(1), v_int(2)); - assert_ne!(v_float(1.), v_float(2.)); - assert_ne!(v_str("a"), v_str("b")); - assert_ne!(v_list(&[v_int(1), v_int(2)]), v_list(&[v_int(1), v_int(3)])); - assert_ne!(v_obj(1), v_obj(2)); - assert_ne!(v_err(E_TYPE), v_err(E_RANGE)); - } - - #[test] - fn test_lt() { - assert!(v_int(1) < v_int(2)); - assert!(v_float(1.) < v_float(2.)); - assert!(v_str("a") < v_str("b")); - assert!(v_list(&[v_int(1), v_int(2)]) < v_list(&[v_int(1), v_int(3)])); - assert!(v_obj(1) < v_obj(2)); - assert!(v_err(E_TYPE) < v_err(E_RANGE)); - } - - #[test] - fn test_le() { - assert!(v_int(1) <= v_int(2)); - assert!(v_float(1.) <= v_float(2.)); - assert!(v_str("a") <= v_str("b")); - assert!(v_list(&[v_int(1), v_int(2)]) <= v_list(&[v_int(1), v_int(3)])); - assert!(v_obj(1) <= v_obj(2)); - assert!(v_err(E_TYPE) <= v_err(E_RANGE)); - } - - #[test] - fn test_gt() { - assert!(v_int(2) > v_int(1)); - assert!(v_float(2.) > v_float(1.)); - assert!(v_str("b") > v_str("a")); - assert!(v_list(&[v_int(1), v_int(3)]) > v_list(&[v_int(1), v_int(2)])); - assert!(v_obj(2) > v_obj(1)); - assert!(v_err(E_RANGE) > v_err(E_TYPE)); - } - - #[test] - fn test_ge() { - assert!(v_int(2) >= v_int(1)); - assert!(v_float(2.) >= v_float(1.)); - assert!(v_str("b") >= v_str("a")); - assert!(v_list(&[v_int(1), v_int(3)]) >= v_list(&[v_int(1), v_int(2)])); - assert!(v_obj(2) >= v_obj(1)); - assert!(v_err(E_RANGE) >= v_err(E_TYPE)); - } - - #[test] - fn test_partial_cmp() { - assert_eq!(v_int(1).partial_cmp(&v_int(1)), Some(Ordering::Equal)); - assert_eq!(v_float(1.).partial_cmp(&v_float(1.)), Some(Ordering::Equal)); - assert_eq!(v_str("a").partial_cmp(&v_str("a")), Some(Ordering::Equal)); - assert_eq!( - v_list(&[v_int(1), v_int(2)]).partial_cmp(&v_list(&[v_int(1), v_int(2)])), - Some(Ordering::Equal) - ); - assert_eq!(v_obj(1).partial_cmp(&v_obj(1)), Some(Ordering::Equal)); - assert_eq!( - v_err(E_TYPE).partial_cmp(&v_err(E_TYPE)), - Some(Ordering::Equal) - ); - - assert_eq!(v_int(1).partial_cmp(&v_int(2)), Some(Ordering::Less)); - assert_eq!(v_float(1.).partial_cmp(&v_float(2.)), Some(Ordering::Less)); - assert_eq!(v_str("a").partial_cmp(&v_str("b")), Some(Ordering::Less)); - assert_eq!( - v_list(&[v_int(1), v_int(2)]).partial_cmp(&v_list(&[v_int(1), v_int(3)])), - Some(Ordering::Less) - ); - assert_eq!(v_obj(1).partial_cmp(&v_obj(2)), Some(Ordering::Less)); - assert_eq!( - v_err(E_TYPE).partial_cmp(&v_err(E_RANGE)), - Some(Ordering::Less) - ); - - assert_eq!(v_int(2).partial_cmp(&v_int(1)), Some(Ordering::Greater)); - assert_eq!( - v_float(2.).partial_cmp(&v_float(1.)), - Some(Ordering::Greater) - ); - assert_eq!(v_str("b").partial_cmp(&v_str("a")), Some(Ordering::Greater)); - assert_eq!( - v_list(&[v_int(1), v_int(3)]).partial_cmp(&v_list(&[v_int(1), v_int(2)])), - Some(Ordering::Greater) - ); - assert_eq!(v_obj(2).partial_cmp(&v_obj(1)), Some(Ordering::Greater)); - assert_eq!( - v_err(E_RANGE).partial_cmp(&v_err(E_TYPE)), - Some(Ordering::Greater) - ); - } - - #[test] - fn test_cmp() { - assert_eq!(v_int(1).cmp(&v_int(1)), Ordering::Equal); - assert_eq!(v_float(1.).cmp(&v_float(1.)), Ordering::Equal); - assert_eq!(v_str("a").cmp(&v_str("a")), Ordering::Equal); - assert_eq!( - v_list(&[v_int(1), v_int(2)]).cmp(&v_list(&[v_int(1), v_int(2)])), - Ordering::Equal - ); - assert_eq!(v_obj(1).cmp(&v_obj(1)), Ordering::Equal); - assert_eq!(v_err(E_TYPE).cmp(&v_err(E_TYPE)), Ordering::Equal); - - assert_eq!(v_int(1).cmp(&v_int(2)), Ordering::Less); - assert_eq!(v_float(1.).cmp(&v_float(2.)), Ordering::Less); - assert_eq!(v_str("a").cmp(&v_str("b")), Ordering::Less); - assert_eq!( - v_list(&[v_int(1), v_int(2)]).cmp(&v_list(&[v_int(1), v_int(3)])), - Ordering::Less - ); - assert_eq!(v_obj(1).cmp(&v_obj(2)), Ordering::Less); - assert_eq!(v_err(E_TYPE).cmp(&v_err(E_RANGE)), Ordering::Less); - - assert_eq!(v_int(2).cmp(&v_int(1)), Ordering::Greater); - assert_eq!(v_float(2.).cmp(&v_float(1.)), Ordering::Greater); - assert_eq!(v_str("b").cmp(&v_str("a")), Ordering::Greater); - assert_eq!( - v_list(&[v_int(1), v_int(3)]).cmp(&v_list(&[v_int(1), v_int(2)])), - Ordering::Greater - ); - assert_eq!(v_obj(2).cmp(&v_obj(1)), Ordering::Greater); - assert_eq!(v_err(E_RANGE).cmp(&v_err(E_TYPE)), Ordering::Greater); - } - - #[test] - fn test_partial_ord() { - assert_eq!(v_int(1).partial_cmp(&v_int(1)).unwrap(), Ordering::Equal); - assert_eq!( - v_float(1.).partial_cmp(&v_float(1.)).unwrap(), - Ordering::Equal - ); - assert_eq!( - v_str("a").partial_cmp(&v_str("a")).unwrap(), - Ordering::Equal - ); - assert_eq!( - v_list(&[v_int(1), v_int(2)]) - .partial_cmp(&v_list(&[v_int(1), v_int(2)])) - .unwrap(), - Ordering::Equal - ); - assert_eq!(v_obj(1).partial_cmp(&v_obj(1)).unwrap(), Ordering::Equal); - assert_eq!( - v_err(E_TYPE).partial_cmp(&v_err(E_TYPE)).unwrap(), - Ordering::Equal - ); - - assert_eq!(v_int(1).partial_cmp(&v_int(2)).unwrap(), Ordering::Less); - assert_eq!( - v_float(1.).partial_cmp(&v_float(2.)).unwrap(), - Ordering::Less - ); - assert_eq!(v_str("a").partial_cmp(&v_str("b")).unwrap(), Ordering::Less); - assert_eq!( - v_list(&[v_int(1), v_int(2)]) - .partial_cmp(&v_list(&[v_int(1), v_int(3)])) - .unwrap(), - Ordering::Less - ); - assert_eq!(v_obj(1).partial_cmp(&v_obj(2)).unwrap(), Ordering::Less); - assert_eq!( - v_err(E_TYPE).partial_cmp(&v_err(E_RANGE)).unwrap(), - Ordering::Less - ); - - assert_eq!(v_int(2).partial_cmp(&v_int(1)).unwrap(), Ordering::Greater); - assert_eq!( - v_float(2.).partial_cmp(&v_float(1.)).unwrap(), - Ordering::Greater - ); - assert_eq!( - v_str("b").partial_cmp(&v_str("a")).unwrap(), - Ordering::Greater - ); - assert_eq!( - v_list(&[v_int(1), v_int(3)]) - .partial_cmp(&v_list(&[v_int(1), v_int(2)])) - .unwrap(), - Ordering::Greater - ); - assert_eq!(v_obj(2).partial_cmp(&v_obj(1)).unwrap(), Ordering::Greater); - assert_eq!( - v_err(E_RANGE).partial_cmp(&v_err(E_TYPE)).unwrap(), - Ordering::Greater - ); - } - - #[test] - fn test_ord() { - assert_eq!(v_int(1).cmp(&v_int(1)), Ordering::Equal); - assert_eq!(v_float(1.).cmp(&v_float(1.)), Ordering::Equal); - assert_eq!(v_str("a").cmp(&v_str("a")), Ordering::Equal); - assert_eq!( - v_list(&[v_int(1), v_int(2)]).cmp(&v_list(&[v_int(1), v_int(2)])), - Ordering::Equal - ); - assert_eq!(v_obj(1).cmp(&v_obj(1)), Ordering::Equal); - assert_eq!(v_err(E_TYPE).cmp(&v_err(E_TYPE)), Ordering::Equal); - - assert_eq!(v_int(1).cmp(&v_int(2)), Ordering::Less); - assert_eq!(v_float(1.).cmp(&v_float(2.)), Ordering::Less); - assert_eq!(v_str("a").cmp(&v_str("b")), Ordering::Less); - assert_eq!( - v_list(&[v_int(1), v_int(2)]).cmp(&v_list(&[v_int(1), v_int(3)])), - Ordering::Less - ); - assert_eq!(v_obj(1).cmp(&v_obj(2)), Ordering::Less); - assert_eq!(v_err(E_TYPE).cmp(&v_err(E_RANGE)), Ordering::Less); - - assert_eq!(v_int(2).cmp(&v_int(1)), Ordering::Greater); - assert_eq!(v_float(2.).cmp(&v_float(1.)), Ordering::Greater); - assert_eq!(v_str("b").cmp(&v_str("a")), Ordering::Greater); - assert_eq!( - v_list(&[v_int(1), v_int(3)]).cmp(&v_list(&[v_int(1), v_int(2)])), - Ordering::Greater - ); - assert_eq!(v_obj(2).cmp(&v_obj(1)), Ordering::Greater); - assert_eq!(v_err(E_RANGE).cmp(&v_err(E_TYPE)), Ordering::Greater); - } - - #[test] - fn test_is_true() { - assert!(v_int(1).is_true()); - assert!(v_float(1.).is_true()); - assert!(v_str("a").is_true()); - assert!(v_list(&[v_int(1), v_int(2)]).is_true()); - assert!(!v_obj(1).is_true()); - assert!(!v_err(E_TYPE).is_true()); - } - - #[test] - fn test_listrangeset() { - let base = v_list(&[v_int(1), v_int(2), v_int(3), v_int(4)]); - - // {1,2,3,4}[1..2] = {"a", "b", "c"} => {1, "a", "b", "c", 4} - let value = v_list(&[v_str("a"), v_str("b"), v_str("c")]); - let expected = v_list(&[v_int(1), v_str("a"), v_str("b"), v_str("c"), v_int(4)]); - assert_eq!(base.range_set(value, 2, 3).unwrap(), expected); - - // {1,2,3,4}[1..2] = {"a"} => {1, "a", 4} - let value = v_list(&[v_str("a")]); - let expected = v_list(&[v_int(1), v_str("a"), v_int(4)]); - assert_eq!(base.range_set(value, 2, 3).unwrap(), expected); - - // {1,2,3,4}[1..2] = {} => {1,4} - let value = v_empty_list(); - let expected = v_list(&[v_int(1), v_int(4)]); - assert_eq!(base.range_set(value, 2, 3).unwrap(), expected); - - // {1,2,3,4}[1..2] = {"a", "b"} => {1, "a", "b", 4} - let value = v_list(&[v_str("a"), v_str("b")]); - let expected = v_list(&[v_int(1), v_str("a"), v_str("b"), v_int(4)]); - assert_eq!(base.range_set(value, 2, 3).unwrap(), expected); - } - - #[test] - fn test_strrangeset() { - // Test interior insertion - let base = v_str("12345"); - let value = v_str("abc"); - let expected = v_str("1abc45"); - let result = base.range_set(value, 2, 3); - assert_eq!(result, Ok(expected)); - - // Test interior replacement - let base = v_str("12345"); - let value = v_str("ab"); - let expected = v_str("1ab45"); - let result = base.range_set(value, 2, 3); - assert_eq!(result, Ok(expected)); - - // Test interior deletion - let base = v_str("12345"); - let value = v_str(""); - let expected = v_str("145"); - let result = base.range_set(value, 2, 3); - assert_eq!(result, Ok(expected)); - - // Test interior subtraction - let base = v_str("12345"); - let value = v_str("z"); - let expected = v_str("1z45"); - let result = base.range_set(value, 2, 3); - assert_eq!(result, Ok(expected)); - } - - #[test] - fn test_range() -> Result<(), Error> { - // test on integer list - let int_list = v_list(&[1.into(), 2.into(), 3.into(), 4.into(), 5.into()]); - assert_eq!( - int_list.range(2, 4)?, - v_list(&[2.into(), 3.into(), 4.into()]) - ); - - // test on string - let string = v_str("hello world"); - assert_eq!(string.range(2, 7)?, v_str("ello w")); - - // range with upper higher than lower, moo returns empty list for this (!) - let empty_list = v_empty_list(); - assert_eq!(empty_list.range(1, 0), Ok(v_empty_list())); - // test on out of range - let int_list = v_list(&[1.into(), 2.into(), 3.into()]); - assert_eq!(int_list.range(2, 4), Ok(v_err(E_RANGE))); - // test on type mismatch - let var_int = v_int(10); - assert_eq!(var_int.range(1, 5), Ok(v_err(E_TYPE))); - - Ok(()) - } +impl IndexMode { + pub fn adjust_i64(&self, index: i64) -> isize { + match self { + IndexMode::ZeroBased => index as isize, + IndexMode::OneBased => (index - 1) as isize, + } + } + + pub fn adjust_isize(&self, index: isize) -> isize { + match self { + IndexMode::ZeroBased => index, + IndexMode::OneBased => index - 1, + } + } + + pub fn reverse_adjust_isize(&self, index: isize) -> isize { + match self { + IndexMode::ZeroBased => index, + IndexMode::OneBased => index + 1, + } + } + + pub fn reverse_adjust_i64(&self, index: i64) -> isize { + match self { + IndexMode::ZeroBased => index as isize, + IndexMode::OneBased => (index + 1) as isize, + } + } +} + +pub enum TypeClass<'a> { + Sequence(&'a dyn Sequence), + Associative(&'a dyn Associative), + Scalar, +} + +impl<'a> TypeClass<'a> { + fn is_sequence(&self) -> bool { + matches!(self, TypeClass::Sequence(_)) + } + + fn is_associative(&self) -> bool { + matches!(self, TypeClass::Associative(_)) + } + + fn is_scalar(&self) -> bool { + matches!(self, TypeClass::Scalar) + } +} + +pub trait Sequence { + /// Return true if the sequence is empty. + fn is_empty(&self) -> bool; + /// Return the length of the sequence. + fn len(&self) -> usize; + /// Check if the sequence contains the element, returning its offset if it does. + /// `case_sensitive` is used to determine if the comparison should be case-sensitive. + /// (MOO case sensitivity is often false) + fn index_in(&self, value: &Var, case_sensitive: bool) -> Result, Error>; + /// Check if the sequence contains the element, returning true if it does. + fn contains(&self, value: &Var, case_sensitive: bool) -> Result; + /// Get the `index`nth element of the sequence. + fn index(&self, index: usize) -> Result; + /// Assign a new value to `index`nth element of the sequence. + fn index_set(&self, index: usize, value: &Var) -> Result; + // Take a copy, add a new value to the end, and return it. + fn push(&self, value: &Var) -> Result; + /// Insert a new value at `index` in the sequence. + fn insert(&self, index: usize, value: &Var) -> Result; + /// Return a sequence which is a subset of this sequence where the indices lay between `from` + /// and `to`, inclusive. + fn range(&self, from: isize, to: isize) -> Result; + /// Assign new values to the sequence where the indices lay between `from` and `to`, inclusive. + fn range_set(&self, from: isize, to: isize, with: &Var) -> Result; + /// Append the given sequence to this sequence. + fn append(&self, other: &Var) -> Result; + /// Remove the `index`nth element of the sequence and return it. + fn remove_at(&self, index: usize) -> Result; +} + +pub trait Associative { + /// Return true if the associative container is empty. + fn is_empty(&self) -> bool; + /// Return the number of key-value pairs in the associative container. + fn len(&self) -> usize; + /// Get the value associated with the given key. + fn index(&self, key: &Var) -> Result; + /// Find the position of the key in the associative container, that is, the offset of the key in + /// the list of keys. + /// `case_sensitive` is used to determine if the comparison should be case-sensitive. + /// (MOO case sensitivity is often false) + fn index_in(&self, key: &Var, case_sensitive: bool) -> Result, Error>; + /// Assign a new value to the given key. + fn index_set(&self, key: &Var, value: &Var) -> Result; + /// Return the key-value pairs in the associative container between the given `from` and `to` + fn range(&self, from: &Var, to: &Var) -> Result; + /// Assign new values to the key-value pairs in the associative container between the given `from` and `to` + fn range_set(&self, from: &Var, to: &Var, with: &Var) -> Result; + /// Return the keys in the associative container. + fn keys(&self) -> Vec; + /// Return the values in the associative container. + fn values(&self) -> Vec; + /// Check if the associative container contains the key, returning true if it does. + fn contains_key(&self, key: &Var, case_sensitive: bool) -> Result; + /// Return this map with the key/value pair removed. + /// Return the new map and the value that was removed, if any + fn remove(&self, key: &Var, case_sensitive: bool) -> (Var, Option); } diff --git a/crates/values/src/var/scalar.rs b/crates/values/src/var/scalar.rs new file mode 100644 index 00000000..9049814e --- /dev/null +++ b/crates/values/src/var/scalar.rs @@ -0,0 +1,221 @@ +// Copyright (C) 2024 Ryan Daum +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free Software +// Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with +// this program. If not, see . +// + +use crate::var::var::{v_err, v_float, v_int, Var}; +use crate::var::variant::Variant; +use crate::var::Error; +use crate::var::Error::{E_INVARG, E_TYPE}; +use num_traits::ToPrimitive; +use paste::paste; +use std::ops::{Div, Mul, Neg, Sub}; + +macro_rules! binary_numeric_coercion_op { + ($op:tt ) => { + pub fn $op(&self, v: &Var) -> Result { + match (self.variant(), v.variant()) { + (Variant::Float(l), Variant::Float(r)) => { + Ok(v_float(l.to_f64().unwrap().$op(r.to_f64().unwrap()))) + } + (Variant::Int(l), Variant::Int(r)) => { + paste! { l.[](*r).map(v_int).ok_or(E_INVARG) } + } + (Variant::Float(l), Variant::Int(r)) => { + Ok(v_float(l.to_f64().unwrap().$op(*r as f64))) + } + (Variant::Int(l), Variant::Float(r)) => { + Ok(v_float((*l as f64).$op(r.to_f64().unwrap()))) + } + (_, _) => Ok(v_err(E_TYPE)), + } + } + }; +} + +impl Var { + binary_numeric_coercion_op!(mul); + binary_numeric_coercion_op!(div); + binary_numeric_coercion_op!(sub); + + pub fn add(&self, v: &Self) -> Result { + match (self.variant(), v.variant()) { + (Variant::Float(l), Variant::Float(r)) => { + Ok(v_float(l.to_f64().unwrap() + r.to_f64().unwrap())) + } + (Variant::Int(l), Variant::Int(r)) => l.checked_add(*r).map(v_int).ok_or(E_INVARG), + (Variant::Float(l), Variant::Int(r)) => Ok(v_float(l.to_f64().unwrap() + (*r as f64))), + (Variant::Int(l), Variant::Float(r)) => Ok(v_float(*l as f64 + r.to_f64().unwrap())), + (Variant::Str(s), Variant::Str(r)) => Ok(s.append(r)), + (_, _) => Ok(v_err(E_TYPE)), + } + } + + pub fn negative(&self) -> Result { + match self.variant() { + Variant::Int(l) => l.checked_neg().map(v_int).ok_or(E_INVARG), + Variant::Float(f) => Ok(v_float(f.neg())), + _ => Ok(v_err(E_TYPE)), + } + } + + pub fn modulus(&self, v: &Self) -> Result { + match (self.variant(), v.variant()) { + (Variant::Float(l), Variant::Float(r)) => Ok(v_float(*l % *r)), + (Variant::Int(l), Variant::Int(r)) => l.checked_rem(*r).map(v_int).ok_or(E_INVARG), + (Variant::Float(l), Variant::Int(r)) => Ok(v_float(l.to_f64().unwrap() % (*r as f64))), + (Variant::Int(l), Variant::Float(r)) => Ok(v_float(*l as f64 % (r.to_f64().unwrap()))), + (_, _) => Ok(v_err(E_TYPE)), + } + } + + pub fn pow(&self, v: &Self) -> Result { + match (self.variant(), v.variant()) { + (Variant::Float(l), Variant::Float(r)) => Ok(v_float(l.powf(*r))), + (Variant::Int(l), Variant::Int(r)) => { + let r = u32::try_from(*r).map_err(|_| E_INVARG)?; + l.checked_pow(r).map(v_int).ok_or(E_INVARG) + } + (Variant::Float(l), Variant::Int(r)) => Ok(v_float(l.powi(*r as i32))), + (Variant::Int(l), Variant::Float(r)) => Ok(v_float((*l as f64).powf(*r))), + (_, _) => Ok(v_err(E_TYPE)), + } + } + + pub fn is_sysobj(&self) -> bool { + matches!(self.variant(), Variant::Obj(o) if o.0 == 0) + } +} + +#[cfg(test)] +mod tests { + use crate::var::var::{v_err, v_float, v_int, v_list, v_obj, v_str}; + use crate::var::Error; + use crate::var::Error::{E_RANGE, E_TYPE}; + + #[test] + fn test_add() { + assert_eq!(v_int(1).add(&v_int(2)), Ok(v_int(3))); + assert_eq!(v_int(1).add(&v_float(2.0)), Ok(v_float(3.0))); + assert_eq!(v_float(1.).add(&v_int(2)), Ok(v_float(3.))); + assert_eq!(v_float(1.).add(&v_float(2.)), Ok(v_float(3.))); + assert_eq!(v_str("a").add(&v_str("b")), Ok(v_str("ab"))); + } + + #[test] + fn test_sub() -> Result<(), Error> { + assert_eq!(v_int(1).sub(&v_int(2))?, v_int(-1)); + assert_eq!(v_int(1).sub(&v_float(2.))?, v_float(-1.)); + assert_eq!(v_float(1.).sub(&v_int(2))?, v_float(-1.)); + assert_eq!(v_float(1.).sub(&v_float(2.))?, v_float(-1.)); + Ok(()) + } + + #[test] + fn test_mul() -> Result<(), Error> { + assert_eq!(v_int(1).mul(&v_int(2))?, v_int(2)); + assert_eq!(v_int(1).mul(&v_float(2.))?, v_float(2.)); + assert_eq!(v_float(1.).mul(&v_int(2))?, v_float(2.)); + assert_eq!(v_float(1.).mul(&v_float(2.))?, v_float(2.)); + Ok(()) + } + + #[test] + fn test_div() -> Result<(), Error> { + assert_eq!(v_int(1).div(&v_int(2))?, v_int(0)); + assert_eq!(v_int(1).div(&v_float(2.))?, v_float(0.5)); + assert_eq!(v_float(1.).div(&v_int(2))?, v_float(0.5)); + assert_eq!(v_float(1.).div(&v_float(2.))?, v_float(0.5)); + Ok(()) + } + + #[test] + fn test_modulus() { + assert_eq!(v_int(1).modulus(&v_int(2)), Ok(v_int(1))); + assert_eq!(v_int(1).modulus(&v_float(2.)), Ok(v_float(1.))); + assert_eq!(v_float(1.).modulus(&v_int(2)), Ok(v_float(1.))); + assert_eq!(v_float(1.).modulus(&v_float(2.)), Ok(v_float(1.))); + assert_eq!(v_str("moop").modulus(&v_int(2)), Ok(v_err(E_TYPE))); + } + + #[test] + fn test_pow() { + assert_eq!(v_int(1).pow(&v_int(2)), Ok(v_int(1))); + assert_eq!(v_int(2).pow(&v_int(2)), Ok(v_int(4))); + assert_eq!(v_int(2).pow(&v_float(2.)), Ok(v_float(4.))); + assert_eq!(v_float(2.).pow(&v_int(2)), Ok(v_float(4.))); + assert_eq!(v_float(2.).pow(&v_float(2.)), Ok(v_float(4.))); + } + + #[test] + fn test_negative() { + assert_eq!(v_int(1).negative(), Ok(v_int(-1))); + assert_eq!(v_float(1.).negative(), Ok(v_float(-1.0))); + } + + #[test] + fn test_eq() { + assert_eq!(v_int(1), v_int(1)); + assert_eq!(v_float(1.), v_float(1.)); + assert_eq!(v_str("a"), v_str("a")); + assert_eq!(v_str("a"), v_str("A")); + assert_eq!(v_list(&[v_int(1), v_int(2)]), v_list(&[v_int(1), v_int(2)])); + assert_eq!(v_obj(1), v_obj(1)); + assert_eq!(v_err(E_TYPE), v_err(E_TYPE)); + } + + #[test] + fn test_ne() { + assert_ne!(v_int(1), v_int(2)); + assert_ne!(v_float(1.), v_float(2.)); + assert_ne!(v_str("a"), v_str("b")); + assert_ne!(v_list(&[v_int(1), v_int(2)]), v_list(&[v_int(1), v_int(3)])); + assert_ne!(v_obj(1), v_obj(2)); + assert_ne!(v_err(E_TYPE), v_err(E_RANGE)); + } + + #[test] + fn test_lt() { + assert!(v_int(1) < v_int(2)); + assert!(v_float(1.) < v_float(2.)); + assert!(v_str("a") < v_str("b")); + assert!(v_obj(1) < v_obj(2)); + assert!(v_err(E_TYPE) < v_err(E_RANGE)); + } + + #[test] + fn test_le() { + assert!(v_int(1) <= v_int(2)); + assert!(v_float(1.) <= v_float(2.)); + assert!(v_str("a") <= v_str("b")); + assert!(v_obj(1) <= v_obj(2)); + assert!(v_err(E_TYPE) <= v_err(E_RANGE)); + } + + #[test] + fn test_gt() { + assert!(v_int(2) > v_int(1)); + assert!(v_float(2.) > v_float(1.)); + assert!(v_str("b") > v_str("a")); + assert!(v_obj(2) > v_obj(1)); + assert!(v_err(E_RANGE) > v_err(E_TYPE)); + } + + #[test] + fn test_ge() { + assert!(v_int(2) >= v_int(1)); + assert!(v_float(2.) >= v_float(1.)); + assert!(v_str("b") >= v_str("a")); + assert!(v_obj(2) >= v_obj(1)); + assert!(v_err(E_RANGE) >= v_err(E_TYPE)); + } +} diff --git a/crates/values/src/var/storage.rs b/crates/values/src/var/storage.rs new file mode 100644 index 00000000..a19c5c2e --- /dev/null +++ b/crates/values/src/var/storage.rs @@ -0,0 +1,201 @@ +// Copyright (C) 2024 Ryan Daum +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free Software +// Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with +// this program. If not, see . +// + +use crate::{AsByteBuffer, DecodingError, EncodingError, Var}; +use bincode::de::{BorrowDecoder, Decoder}; +use bincode::enc::Encoder; +use bincode::error::{DecodeError, EncodeError}; +use bincode::{BorrowDecode, Decode, Encode}; +use bytes::Bytes; +use flexbuffers::{Buffer, BuilderOptions}; +use std::ops::{Deref, Range}; +use std::str::Utf8Error; + +/// A wrapper around bytes::Bytes that implements the Buffer trait for flexbuffers. +#[derive(Clone)] +pub struct VarBuffer(pub Bytes); + +impl Buffer for VarBuffer { + type BufferString = String; + + fn slice(&self, range: Range) -> Option { + if range.start > range.end { + return None; + } + Some(Self(self.0.slice(range))) + } + + fn shallow_copy(&self) -> Self { + Self(self.0.clone()) + } + + fn empty() -> Self { + Self(Bytes::new()) + } + + fn empty_str() -> Self::BufferString { + "".to_string() + } + + fn buffer_str(&self) -> Result { + std::str::from_utf8(self.0.as_ref()).map(|s| s.to_string()) + } +} + +impl Deref for VarBuffer { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.0.as_ref() + } +} + +impl Var { + pub fn to_bytes(&self) -> Bytes { + let mut builder = flexbuffers::Builder::new(BuilderOptions::default()); + let mut vb = builder.start_vector(); + self.variant().push_to(&mut vb); + vb.end_vector(); + Bytes::from(builder.take_buffer()) + } +} + +impl AsByteBuffer for Var { + fn size_bytes(&self) -> usize { + let buf = self.to_bytes(); + buf.len() + } + + fn with_byte_buffer R>(&self, mut f: F) -> Result { + let buf = self.to_bytes(); + Ok(f(buf.as_ref())) + } + + fn make_copy_as_vec(&self) -> Result, EncodingError> { + let buf = self.to_bytes(); + Ok(buf.to_vec()) + } + + fn from_bytes(bytes: Bytes) -> Result + where + Self: Sized, + { + Ok(Var::from_bytes(bytes)) + } + + fn as_bytes(&self) -> Result { + Ok(self.to_bytes()) + } +} + +impl Encode for Var { + fn encode(&self, encoder: &mut E) -> Result<(), EncodeError> { + let buf = self.to_bytes().to_vec(); + buf.encode(encoder) + } +} + +impl Decode for Var { + fn decode(decoder: &mut D) -> Result { + let vec = Vec::::decode(decoder)?; + let bytes = Bytes::from(vec); + Ok(Var::from_bytes(bytes)) + } +} + +impl<'de> BorrowDecode<'de> for Var { + fn borrow_decode>(decoder: &mut D) -> Result { + let vec = Vec::::decode(decoder)?; + let bytes = Bytes::from(vec); + Ok(Var::from_bytes(bytes)) + } +} + +#[cfg(test)] +mod tests { + use crate::Error::E_TYPE; + use crate::{v_err, v_float, v_int, v_list, v_map, v_objid, v_str, Objid, Var}; + + #[test] + fn pack_unpack_int() { + let v = v_int(1); + let bytes = v.to_bytes(); + let v2 = Var::from_bytes(bytes); + assert_eq!(v, v2); + } + + #[test] + fn pack_unpack_float() { + let v = v_float(42.42); + let bytes = v.to_bytes(); + let v2 = Var::from_bytes(bytes); + assert_eq!(v, v2); + } + + #[test] + fn pack_unpack_error() { + let v = v_err(E_TYPE); + let bytes = v.to_bytes(); + let v2 = Var::from_bytes(bytes); + assert_eq!(v, v2); + } + #[test] + fn pack_unpack_string() { + let v = v_str("hello"); + let bytes = v.to_bytes(); + let v2 = Var::from_bytes(bytes); + assert_eq!(v, v2); + } + + #[test] + fn pack_unpack_objid() { + let v = v_objid(Objid(1)); + let bytes = v.to_bytes(); + let v2 = Var::from_bytes(bytes); + assert_eq!(v, v2); + } + #[test] + fn pack_unpack_list() { + let l = v_list(&[v_int(1), v_int(2), v_int(3)]); + let bytes = l.to_bytes(); + let l2 = Var::from_bytes(bytes); + assert_eq!(l, l2); + } + + #[test] + fn pack_unpack_list_nested() { + let l = v_list(&[v_int(1), v_int(2), v_int(3)]); + let l2 = v_list(&[v_int(1), l.clone(), v_int(3)]); + let bytes = l2.to_bytes(); + let l3 = Var::from_bytes(bytes); + assert_eq!(l2, l3); + } + + #[test] + fn pack_unpack_map() { + let m = v_map(&[(v_int(1), v_int(2)), (v_int(3), v_int(4))]); + let bytes = m.to_bytes(); + let m2 = Var::from_bytes(bytes); + assert_eq!(m, m2); + } + + #[test] + fn pack_unpack_map_nested() { + let m = v_map(&[(v_int(1), v_int(2)), (v_int(3), v_int(4))]); + let m2 = v_map(&[(v_int(1), m.clone()), (v_int(3), m.clone())]); + let bytes = m2.to_bytes(); + let m3 = Var::from_bytes(bytes); + assert_eq!(m2, m3); + } +} diff --git a/crates/values/src/var/string.rs b/crates/values/src/var/string.rs index 26815a22..93893751 100644 --- a/crates/values/src/var/string.rs +++ b/crates/values/src/var/string.rs @@ -12,190 +12,443 @@ // this program. If not, see . // +use crate::var::storage::VarBuffer; +use crate::var::var::{v_str, Var}; +use crate::var::variant::Variant; +use crate::var::Error; +use crate::var::Error::{E_INVARG, E_RANGE, E_TYPE}; +use crate::var::Sequence; +use flexbuffers::Reader; +use num_traits::ToPrimitive; +use std::cmp::max; use std::fmt::{Display, Formatter}; use std::hash::Hash; -use std::ops::Range; -use std::str; -use std::str::FromStr; -use str::from_utf8; -use bincode::de::{BorrowDecoder, Decoder}; -use bincode::enc::Encoder; -use bincode::error::{DecodeError, EncodeError}; -use bincode::{BorrowDecode, Decode, Encode}; -use bytes::Bytes; +#[derive(Clone)] +pub struct Str { + // Reader must be boxed to avoid overfilling the stack. + pub(crate) reader: Box>, +} -use crate::var::error::Error; -use crate::var::{v_err, v_str, v_string, Var}; -use crate::{AsByteBuffer, DecodingError, EncodingError}; +impl Str { + pub fn as_string(&self) -> String { + self.reader.get_str().unwrap() + } -#[derive(Clone, Debug)] -pub struct Str(Bytes); + pub fn index_set(&self, index: usize, value: &Self) -> Result { + if value.len() != 1 { + return Err(E_INVARG); + } + let string = self.as_string(); + if index >= string.len() { + return Err(E_RANGE); + } + let mut s = string.clone(); + s.replace_range(index..=index, &value.as_string()); + Ok(Var::mk_str(&s)) + } -impl Str { - #[must_use] - pub fn from_string(s: String) -> Self { - let s = s.into_bytes(); - let sr = Bytes::from(s); - Self(sr) + pub fn append(&self, other: &Self) -> Var { + let mut s = self.as_string().clone(); + s.push_str(&other.as_string()); + Var::mk_str(&s) + } +} + +impl Sequence for Str { + fn is_empty(&self) -> bool { + self.as_string().is_empty() } - pub fn get(&self, offset: usize) -> Option { - let s = from_utf8(&self.0).unwrap(); - let r = s.get(offset..offset + 1); - r.map(v_str) + fn len(&self) -> usize { + self.as_string().len() } - #[must_use] - pub fn set(&self, offset: usize, r: &Self) -> Var { - if r.len() != 1 { - return v_err(Error::E_RANGE); + fn index(&self, index: usize) -> Result { + if index >= self.as_string().len() { + return Err(E_RANGE); } - if offset >= self.0.len() { - return v_err(Error::E_RANGE); + let c = self.as_string().chars().nth(index).unwrap(); + let c_str = c.to_string(); + let v = Var::mk_str(&c_str); + Ok(v) + } + + fn index_set(&self, index: usize, value: &Var) -> Result { + if index >= self.as_string().len() { + return Err(E_RANGE); + } + + // Index set for strings requires that the `value` being set is a string, otherwise it's. + // E_TYPE. + // And it must be a single character character, otherwise, E_INVARG is returned. + let value = match value.variant() { + Variant::Str(s) => s, + _ => return Err(E_TYPE), + }; + + if value.len() != 1 { + return Err(E_INVARG); } - let mut s = from_utf8(&self.0).unwrap().to_string(); - s.replace_range(offset..=offset, r.as_str()); - v_string(s) + + let mut s = self.as_string().clone(); + s.replace_range(index..=index, value.as_string().as_str()); + Ok(Var::mk_str(&s)) } - pub fn get_range(&self, range: Range) -> Option { - let s = from_utf8(&self.0).unwrap(); - let r = s.get(range); - r.map(v_str) + fn insert(&self, index: usize, value: &Var) -> Result { + // If value is not a string, return E_TYPE. + let value = match value.variant() { + Variant::Str(s) => s, + _ => return Err(E_TYPE), + }; + + let mut new_copy = self.as_string().clone(); + new_copy.insert_str(index, value.as_string().as_str()); + Ok(Var::mk_str(&new_copy)) } - #[must_use] - pub fn append(&self, other: &Self) -> Var { - v_string(format!( - "{}{}", - from_utf8(&self.0).unwrap(), - from_utf8(&other.0).unwrap() - )) + fn range(&self, from: isize, to: isize) -> Result { + if to < from { + return Ok(Var::mk_str("")); + } + let s = self.as_string(); + let start = max(from, 0) as usize; + let to = to as usize; + if start >= s.len() || to >= s.len() { + return Err(E_RANGE); + } + let s = s.get(start..=to).unwrap(); + Ok(Var::mk_str(s)) } - #[must_use] - pub fn append_str(&self, other: &str) -> Var { - v_string(format!("{}{}", from_utf8(&self.0).unwrap(), other)) + fn range_set(&self, from: isize, to: isize, with: &Var) -> Result { + let with_val = match with.variant() { + Variant::Str(s) => s, + _ => return Err(Error::E_TYPE), + }; + + let base_len = self.len(); + let from = from.to_usize().unwrap_or(0); + let to = to.to_usize().unwrap_or(0); + if to + 1 > base_len { + return Err(E_RANGE); + } + let base_str = self.as_string(); + let mut ans = base_str.get(..from).unwrap_or("").to_string(); + ans.push_str(&with_val.as_string()); + if to == 0 { + ans.push_str(&base_str); + } else { + ans.push_str(base_str.get(to + 1..).unwrap_or("")); + } + Ok(v_str(&ans)) } - #[must_use] - pub fn append_string(&self, other: String) -> Var { - v_string(format!("{}{}", from_utf8(&self.0).unwrap(), other)) + fn push(&self, value: &Var) -> Result { + let value = match value.variant() { + Variant::Str(s) => s, + _ => return Err(E_TYPE), + }; + + let mut new_copy = self.as_string().clone(); + new_copy.push_str(value.as_string().as_str()); + Ok(Var::mk_str(&new_copy)) } - #[must_use] - pub fn len(&self) -> usize { - from_utf8(&self.0).unwrap().len() + fn append(&self, other: &Var) -> Result { + let other = match other.variant() { + Variant::Str(s) => s, + _ => return Err(E_TYPE), + }; + + let mut new_copy = self.as_string().clone(); + new_copy.push_str(other.as_string().as_str()); + Ok(Var::mk_str(&new_copy)) } - #[must_use] - pub fn is_empty(&self) -> bool { - from_utf8(&self.0).unwrap().is_empty() + fn remove_at(&self, index: usize) -> Result { + if index >= self.as_string().len() { + return Err(E_RANGE); + } + + let mut new_copy = self.as_string().clone(); + new_copy.remove(index); + Ok(Var::mk_str(&new_copy)) } - #[must_use] - pub fn as_str(&self) -> &str { - from_utf8(&self.0).unwrap() + fn contains(&self, value: &Var, case_sensitive: bool) -> Result { + let value = match value.variant() { + Variant::Str(s) => s, + _ => return Err(E_TYPE), + }; + + let s = self.as_string(); + let value = value.as_string(); + let contains = if case_sensitive { + s.contains(&value) + } else { + s.to_lowercase().contains(&value.to_lowercase()) + }; + + Ok(contains) } + fn index_in(&self, value: &Var, case_sensitive: bool) -> Result, Error> { + let value = match value.variant() { + Variant::Str(s) => s, + _ => return Err(E_TYPE), + }; - #[must_use] - pub fn substring(&self, range: Range) -> Self { - let s = from_utf8(&self.0).unwrap(); - let s = s.get(range).unwrap_or(""); - Self::from_string(s.to_string()) + let s: String = self.as_string(); + let value = value.as_string(); + let contains = if case_sensitive { + // Get the index of the substring in the string. + s.find(&value) + } else { + s.to_lowercase().find(&value.to_lowercase()) + }; + + Ok(contains) } } -// MOO's string comparisons are all case-insensitive. To get case-sensitive you have to use -// bf_is_member and bf_strcmp. +impl Display for Str { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.as_string()) + } +} impl PartialEq for Str { + // MOO strings are case-insensitive on comparison unless an explicit case sensitive comparison + // is needed. fn eq(&self, other: &Self) -> bool { - let s = from_utf8(&self.0).unwrap(); - let o = from_utf8(&other.0).unwrap(); - s.eq_ignore_ascii_case(o) + self.as_string().to_lowercase() == other.as_string().to_lowercase() } } + impl Eq for Str {} +impl PartialOrd for Str { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Str { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.as_string() + .to_lowercase() + .cmp(&other.as_string().to_lowercase()) + } +} + impl Hash for Str { fn hash(&self, state: &mut H) { - let s = from_utf8(&self.0).unwrap(); - s.to_lowercase().hash(state) + self.as_string().to_lowercase().hash(state) } } -impl FromStr for Str { - type Err = (); +#[cfg(test)] +mod tests { + use crate::v_bool; + use crate::var::var::{v_int, v_str, Var}; + use crate::var::variant::Variant; + use crate::var::IndexMode; - fn from_str(s: &str) -> Result { - let s = s.to_string(); - Ok(Self::from_string(s)) + #[test] + fn test_str_pack_unpack() { + let s = Var::mk_str("hello"); + match s.variant() { + Variant::Str(s) => assert_eq!(s.as_string().as_str(), "hello"), + _ => panic!("Expected string"), + } } -} -impl Display for Str { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!("{}", from_utf8(&self.0).unwrap())) + #[test] + fn test_string_is_funcs() { + let s = Var::mk_str("hello"); + assert!(s.is_true()); + assert!(s.is_sequence()); + assert!(!s.is_associative()); + assert!(!s.is_scalar()); + assert_eq!(s.len().unwrap(), 5); + assert!(!s.is_empty().unwrap()); + + let s = Var::mk_str(""); + assert!(!s.is_true()); + assert!(s.is_sequence()); + assert!(!s.is_associative()); + assert!(!s.is_scalar()); + assert_eq!(s.len().unwrap(), 0); + assert!(s.is_empty().unwrap()); } -} -impl Encode for Str { - fn encode(&self, encoder: &mut E) -> Result<(), EncodeError> { - let str = self.as_str().to_string(); - str.encode(encoder) + #[test] + fn test_string_equality_inquality() { + let s1 = Var::mk_str("hello"); + let s2 = Var::mk_str("hello"); + let s3 = Var::mk_str("world"); + let s4 = Var::mk_str("hello world"); + + assert_eq!(s1, s2); + assert_ne!(s1, s3); + assert_ne!(s1, s4); } -} -impl Decode for Str { - fn decode(decoder: &mut D) -> Result { - let str = String::decode(decoder)?; - Ok(Self::from_string(str)) + #[test] + fn test_string_index() { + let s = Var::mk_str("hello"); + let r = s.index(&Var::mk_integer(1), IndexMode::ZeroBased).unwrap(); + let r = r.variant(); + match r { + Variant::Str(s) => assert_eq!(s.as_string().as_str(), "e"), + _ => panic!("Expected string, got {:?}", r), + } } -} -impl<'de> BorrowDecode<'de> for Str { - fn borrow_decode>(decoder: &mut D) -> Result { - let str = String::borrow_decode(decoder)?; - Ok(Self::from_string(str)) + #[test] + fn test_string_index_set() { + let s = Var::mk_str("hello"); + let r = s + .index_set(&Var::mk_integer(1), &Var::mk_str("a"), IndexMode::ZeroBased) + .unwrap(); + let r = r.variant(); + match r { + Variant::Str(s) => assert_eq!(s.as_string().as_str(), "hallo"), + _ => panic!("Expected string, got {:?}", r), + } + + let fail_bad_index = s.index_set( + &Var::mk_integer(10), + &Var::mk_str("a"), + IndexMode::ZeroBased, + ); + assert!(fail_bad_index.is_err()); + assert_eq!(fail_bad_index.unwrap_err(), crate::var::Error::E_RANGE); } -} -impl Ord for Str { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - let s = from_utf8(&self.0).unwrap(); - let o = from_utf8(&other.0).unwrap(); - s.to_lowercase().cmp(&o.to_lowercase()) + #[test] + fn test_one_index_slice() { + let s = v_str("hello world"); + let result = s.range(&v_int(2), &v_int(7), IndexMode::OneBased).unwrap(); + assert_eq!(result, v_str("ello w")); } -} -impl PartialOrd for Str { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) + #[test] + fn test_zero_index_slice() { + let s = v_str("hello world"); + let result = s.range(&v_int(1), &v_int(6), IndexMode::ZeroBased).unwrap(); + assert_eq!(result, v_str("ello w")); } -} -impl AsByteBuffer for Str { - fn size_bytes(&self) -> usize { - self.0.len() + #[test] + fn test_string_range_set() { + // Test a one-indexed assignment, comparing against a known MOO behaviour. + let base = v_str("mandalorian"); + let (start, end) = (v_int(4), v_int(7)); + let replace = v_str("bozo"); + let expected = v_str("manbozorian"); + let result = base.range_set(&start, &end, &replace, IndexMode::OneBased); + assert_eq!(result, Ok(expected)); + + // Test interior insertion + let base = v_str("12345"); + let value = v_str("abc"); + let expected = v_str("1abc45"); + let result = base.range_set(&v_int(2), &v_int(3), &value, IndexMode::OneBased); + assert_eq!(result, Ok(expected)); + + // Test interior replacement + let base = v_str("12345"); + let value = v_str("ab"); + let expected = v_str("1ab45"); + let result = base.range_set(&v_int(1), &v_int(2), &value, IndexMode::ZeroBased); + assert_eq!(result, Ok(expected)); + + // Test interior deletion + let base = v_str("12345"); + let value = v_str(""); + let expected = v_str("145"); + let result = base.range_set(&v_int(1), &v_int(2), &value, IndexMode::ZeroBased); + assert_eq!(result, Ok(expected)); + + // Test interior subtraction + let base = v_str("12345"); + let value = v_str("z"); + let expected = v_str("1z45"); + let result = base.range_set(&v_int(1), &v_int(2), &value, IndexMode::ZeroBased); + assert_eq!(result, Ok(expected)); } - fn with_byte_buffer R>(&self, mut f: F) -> Result { - Ok(f(self.0.as_ref())) + /// Moo supports this weird behavior + #[test] + fn test_string_range_set_odd_range_end() { + let base = v_str("me:words"); + let value = v_str(""); + let expected = v_str("me:words"); + let result = base.range_set(&v_int(1), &v_int(0), &value, IndexMode::OneBased); + assert_eq!(result, Ok(expected)); } - fn make_copy_as_vec(&self) -> Result, EncodingError> { - Ok(self.0.to_vec()) + #[test] + fn test_string_push() { + let s = Var::mk_str("hello"); + let r = s.push(&Var::mk_str(" world")).unwrap(); + let r = r.variant(); + match r { + Variant::Str(s) => assert_eq!(s.as_string().as_str(), "hello world"), + _ => panic!("Expected string, got {:?}", r), + } + } + + #[test] + fn test_string_append() { + let s1 = Var::mk_str("hello"); + let s2 = Var::mk_str(" world"); + let r = s1.append(&s2).unwrap(); + let r = r.variant(); + match r { + Variant::Str(s) => assert_eq!(s.as_string().as_str(), "hello world"), + _ => panic!("Expected string, got {:?}", r), + } + } + + #[test] + fn test_string_remove_at() { + let s = Var::mk_str("hello"); + let r = s + .remove_at(&Var::mk_integer(1), IndexMode::ZeroBased) + .unwrap(); + let r = r.variant(); + match r { + Variant::Str(s) => assert_eq!(s.as_string().as_str(), "hllo"), + _ => panic!("Expected string, got {:?}", r), + } + + let fail_bad_index = s.remove_at(&Var::mk_integer(10), IndexMode::ZeroBased); + assert!(fail_bad_index.is_err()); + assert_eq!(fail_bad_index.unwrap_err(), crate::var::Error::E_RANGE); } - fn from_bytes(bytes: Bytes) -> Result - where - Self: Sized, - { - Ok(Self(bytes)) + #[test] + fn test_string_contains() { + // Check both case-sensitive and case-insensitive + let s = Var::mk_str("hello"); + assert_eq!(s.contains(&Var::mk_str("ell"), true).unwrap(), v_bool(true)); + assert_eq!( + s.contains(&Var::mk_str("Ell"), false).unwrap(), + v_bool(true) + ); + assert_eq!( + s.contains(&Var::mk_str("world"), true).unwrap(), + v_bool(false) + ); } - fn as_bytes(&self) -> Result { - Ok(self.0.clone()) + #[test] + fn test_string_case_sensitive() { + let s = Var::mk_str("hello"); + let s2 = Var::mk_str("Hello"); + assert_eq!(s, s2); + assert!(!s.eq_case_sensitive(&s2)); } } diff --git a/crates/values/src/var/var.rs b/crates/values/src/var/var.rs new file mode 100644 index 00000000..84b0b0f8 --- /dev/null +++ b/crates/values/src/var/var.rs @@ -0,0 +1,607 @@ +// Copyright (C) 2024 Ryan Daum +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free Software +// Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with +// this program. If not, see . +// + +use crate::var::list::List; +use crate::var::storage::VarBuffer; +use crate::var::variant::Variant; +use crate::var::Associative; +use crate::var::Error::{E_INVARG, E_RANGE, E_TYPE}; +use crate::var::{map, IndexMode, Sequence, TypeClass}; +use crate::var::{Error, Objid, VarType}; +use bytes::Bytes; +use decorum::R64; +use flexbuffers::{BuilderOptions, Reader}; +use num_traits::ToPrimitive; +use std::cmp::{min, Ordering}; +use std::fmt::{Debug, Formatter}; + +#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub struct Var(pub Variant); + +impl Debug for Var { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } +} + +impl Var { + pub fn from_bytes(data: Bytes) -> Self { + let buf = VarBuffer(data); + let reader = Reader::get_root(buf).unwrap(); + let v = Variant::from_reader(reader); + Var(v) + } + + pub fn mk_integer(i: i64) -> Self { + let mut builder = flexbuffers::Builder::new(BuilderOptions::empty()); + let mut vb = builder.start_vector(); + vb.push(VarType::TYPE_INT as u8); + vb.push(i); + vb.end_vector(); + let buf = builder.take_buffer(); + let buf = Bytes::from(buf); + let reader = Reader::get_root(VarBuffer(buf)).unwrap(); + let v = Variant::from_reader(reader); + Var(v) + } + + pub fn mk_none() -> Self { + let mut builder = flexbuffers::Builder::new(BuilderOptions::empty()); + let mut vb = builder.start_vector(); + vb.push(VarType::TYPE_NONE as u8); + vb.end_vector(); + let buf = builder.take_buffer(); + let buf = Bytes::from(buf); + let reader = Reader::get_root(VarBuffer(buf)).unwrap(); + let v = Variant::from_reader(reader); + Var(v) + } + + pub fn mk_str(s: &str) -> Self { + let mut builder = flexbuffers::Builder::new(BuilderOptions::empty()); + let mut vb = builder.start_vector(); + vb.push(VarType::TYPE_STR as u8); + vb.push(s); + vb.end_vector(); + let buf = builder.take_buffer(); + let buf = Bytes::from(buf); + let reader = Reader::get_root(VarBuffer(buf)).unwrap(); + let v = Variant::from_reader(reader); + Var(v) + } + + pub fn mk_float(f: R64) -> Self { + let mut builder = flexbuffers::Builder::new(BuilderOptions::empty()); + let mut vb = builder.start_vector(); + vb.push(VarType::TYPE_FLOAT as u8); + vb.push(f.to_f64().unwrap()); + vb.end_vector(); + let buf = builder.take_buffer(); + let buf = Bytes::from(buf); + let reader = Reader::get_root(VarBuffer(buf)).unwrap(); + let v = Variant::from_reader(reader); + Var(v) + } + + pub fn mk_error(e: Error) -> Self { + let mut builder = flexbuffers::Builder::new(BuilderOptions::empty()); + let mut vb = builder.start_vector(); + vb.push(VarType::TYPE_ERR as u8); + vb.push(e as u8); + vb.end_vector(); + let buf = builder.take_buffer(); + let buf = Bytes::from(buf); + let reader = Reader::get_root(VarBuffer(buf)).unwrap(); + let v = Variant::from_reader(reader); + Var(v) + } + + pub fn type_code(&self) -> VarType { + match self.variant() { + Variant::Int(_) => VarType::TYPE_INT, + Variant::Obj(_) => VarType::TYPE_OBJ, + Variant::Str(_) => VarType::TYPE_STR, + Variant::Err(_) => VarType::TYPE_ERR, + Variant::List(_) => VarType::TYPE_LIST, + Variant::None => VarType::TYPE_NONE, + Variant::Float(_) => VarType::TYPE_FLOAT, + Variant::Map(_) => VarType::TYPE_MAP, + } + } + + pub fn mk_list(values: &[Var]) -> Self { + List::build(values) + } + + pub fn mk_list_iter>(values: IT) -> Self { + Var::from_iter(values) + } + + pub fn mk_map(pairs: &[(Var, Var)]) -> Self { + map::Map::build(pairs.iter()) + } + + pub fn mk_object(o: Objid) -> Self { + let mut builder = flexbuffers::Builder::new(BuilderOptions::empty()); + let mut vb = builder.start_vector(); + vb.push(VarType::TYPE_OBJ as u8); + vb.push(o.0); + vb.end_vector(); + let buf = builder.take_buffer(); + let buf = Bytes::from(buf); + let reader = Reader::get_root(VarBuffer(buf)).unwrap(); + let v = Variant::from_reader(reader); + Var(v) + } + + pub fn variant(&self) -> &Variant { + &self.0 + } + + pub fn is_true(&self) -> bool { + match self.variant() { + Variant::None => false, + Variant::Obj(_) => false, + Variant::Int(i) => *i != 0, + Variant::Float(f) => *f != 0.0, + Variant::List(l) => !l.reader.is_empty(), + Variant::Str(s) => !s.as_string().is_empty(), + Variant::Map(m) => !m.reader.is_empty(), + Variant::Err(_) => false, + } + } + + /// 0-based index into a sequence type, or map lookup by key + /// If not a sequence, returns Err(E_INVARG) + /// For strings and lists, the index must be a positive integer, or Err(E_TYPE) + /// Range errors are Err(E_RANGE) + /// Otherwise returns the value + pub fn index(&self, index: &Var, index_mode: IndexMode) -> Result { + match self.type_class() { + TypeClass::Sequence(s) => { + let idx = match index.variant() { + Variant::Int(i) => { + let i = index_mode.adjust_i64(*i); + if i < 0 { + return Err(E_RANGE); + } + i as usize + } + _ => { + return Err(E_TYPE); + } + }; + + if idx >= s.len() { + return Err(E_RANGE); + } + + s.index(idx) + } + TypeClass::Associative(a) => a.index(index), + TypeClass::Scalar => Err(E_TYPE), + } + } + + /// Assign a new value to `index`nth element of the sequence, or to a key in an associative type. + pub fn index_set( + &self, + idx: &Self, + value: &Self, + index_mode: IndexMode, + ) -> Result { + match self.type_class() { + TypeClass::Sequence(s) => { + let idx = match idx.variant() { + Variant::Int(i) => { + let i = index_mode.adjust_i64(*i); + + if i < 0 { + return Err(E_RANGE); + } + i as usize + } + _ => return Err(E_INVARG), + }; + s.index_set(idx, value) + } + TypeClass::Associative(a) => a.index_set(idx, value), + TypeClass::Scalar => Err(E_TYPE), + } + } + + /// Insert a new value at `index` in a sequence only. + /// If the value is not a sequence, returns Err(E_INVARG). + /// To add a value to a map use `index_set`. + /// If the index is negative, it is treated as 0. + pub fn insert(&self, index: &Var, value: &Var, index_mode: IndexMode) -> Result { + match self.type_class() { + TypeClass::Sequence(s) => { + let index = match index.variant() { + Variant::Int(i) => index_mode.adjust_i64(*i), + _ => return Err(E_INVARG), + }; + let index = if index < 0 { + 0 + } else { + min(index as usize, s.len()) + }; + + if index > s.len() { + return Err(E_RANGE); + } + + s.insert(index, value) + } + _ => Err(E_TYPE), + } + } + + pub fn range(&self, from: &Var, to: &Var, index_mode: IndexMode) -> Result { + match self.type_class() { + TypeClass::Sequence(s) => { + let from = match from.variant() { + Variant::Int(i) => index_mode.adjust_i64(*i), + _ => return Err(E_INVARG), + }; + + let to = match to.variant() { + Variant::Int(i) => index_mode.adjust_i64(*i), + _ => return Err(E_INVARG), + }; + + s.range(from, to) + } + TypeClass::Associative(a) => a.range(from, to), + TypeClass::Scalar => Err(E_TYPE), + } + } + + pub fn range_set( + &self, + from: &Var, + to: &Var, + with: &Var, + index_mode: IndexMode, + ) -> Result { + match self.type_class() { + TypeClass::Sequence(s) => { + let from = match from.variant() { + Variant::Int(i) => index_mode.adjust_i64(*i), + _ => return Err(E_INVARG), + }; + + let to = match to.variant() { + Variant::Int(i) => index_mode.adjust_i64(*i), + _ => return Err(E_INVARG), + }; + + s.range_set(from, to, with) + } + TypeClass::Associative(a) => a.range_set(from, to, with), + TypeClass::Scalar => Err(E_TYPE), + } + } + + pub fn append(&self, other: &Var) -> Result { + match self.type_class() { + TypeClass::Sequence(s) => s.append(other), + _ => Err(E_TYPE), + } + } + + pub fn push(&self, value: &Var) -> Result { + match self.type_class() { + TypeClass::Sequence(s) => s.push(value), + _ => Err(E_TYPE), + } + } + + pub fn contains(&self, value: &Var, case_sensitive: bool) -> Result { + match self.type_class() { + TypeClass::Sequence(s) => { + let c = s.contains(value, case_sensitive)?; + Ok(v_bool(c)) + } + TypeClass::Associative(a) => { + let c = a.contains_key(value, case_sensitive)?; + Ok(v_bool(c)) + } + TypeClass::Scalar => Err(E_INVARG), + } + } + + pub fn index_in( + &self, + value: &Var, + case_sensitive: bool, + index_mode: IndexMode, + ) -> Result { + match self.type_class() { + TypeClass::Sequence(s) => { + let idx = s + .index_in(value, case_sensitive)? + .map(|i| i as i64) + .unwrap_or(-1); + Ok(v_int(index_mode.reverse_adjust_isize(idx as isize) as i64)) + } + TypeClass::Associative(a) => { + let idx = a + .index_in(value, case_sensitive)? + .map(|i| i as i64) + .unwrap_or(-1); + Ok(v_int(index_mode.reverse_adjust_isize(idx as isize) as i64)) + } + _ => Err(E_INVARG), + } + } + + pub fn remove_at(&self, index: &Var, index_mode: IndexMode) -> Result { + match self.type_class() { + TypeClass::Sequence(s) => { + let index = match index.variant() { + Variant::Int(i) => index_mode.adjust_i64(*i), + _ => return Err(E_INVARG), + }; + + if index < 0 { + return Err(E_RANGE); + } + + s.remove_at(index as usize) + } + _ => Err(E_TYPE), + } + } + + pub fn remove(&self, value: &Var, case_sensitive: bool) -> Result<(Var, Option), Error> { + match self.type_class() { + TypeClass::Associative(a) => Ok(a.remove(value, case_sensitive)), + _ => Err(E_INVARG), + } + } + + pub fn is_sequence(&self) -> bool { + self.type_class().is_sequence() + } + + pub fn is_associative(&self) -> bool { + self.type_class().is_associative() + } + + pub fn is_scalar(&self) -> bool { + self.type_class().is_scalar() + } + + pub fn is_string(&self) -> bool { + matches!(self.variant(), Variant::Str(_)) + } + + pub fn as_sequence(&self) -> Option<&dyn Sequence> { + match self.variant() { + Variant::List(l) => Some(l), + Variant::Str(s) => Some(s), + _ => None, + } + } + + pub fn is_empty(&self) -> Result { + match self.type_class() { + TypeClass::Sequence(s) => Ok(s.is_empty()), + TypeClass::Associative(a) => Ok(a.is_empty()), + TypeClass::Scalar => Err(E_INVARG), + } + } + + pub fn eq_case_sensitive(&self, other: &Var) -> bool { + match (self.variant(), other.variant()) { + (Variant::Str(s1), Variant::Str(s2)) => s1.as_string() == s2.as_string(), + (Variant::List(l1), Variant::List(l2)) => { + if l1.len() != l2.len() { + return false; + } + let left_items = l1.iter(); + let right_items = l2.iter(); + for (left, right) in left_items.zip(right_items) { + if !left.eq_case_sensitive(&right) { + return false; + } + } + true + } + (Variant::Map(m1), Variant::Map(m2)) => { + if m1.len() != m2.len() { + return false; + } + let left_pairs = m1.iter(); + let right_pairs = m2.iter(); + for (left, right) in left_pairs.zip(right_pairs) { + if !left.0.eq_case_sensitive(&right.0) || !left.1.eq_case_sensitive(&right.1) { + return false; + } + } + true + } + _ => self.eq(other), + } + } + + pub fn cmp_case_sensitive(&self, other: &Var) -> Ordering { + match (self.variant(), other.variant()) { + (Variant::Str(s1), Variant::Str(s2)) => s1.as_string().cmp(&s2.as_string()), + _ => self.cmp(other), + } + } + + pub fn len(&self) -> Result { + match self.type_class() { + TypeClass::Sequence(s) => Ok(s.len()), + TypeClass::Associative(a) => Ok(a.len()), + TypeClass::Scalar => Err(E_INVARG), + } + } + + pub fn type_class(&self) -> TypeClass { + match self.variant() { + Variant::List(s) => TypeClass::Sequence(s), + Variant::Str(s) => TypeClass::Sequence(s), + Variant::Map(m) => TypeClass::Associative(m), + _ => TypeClass::Scalar, + } + } +} + +pub fn v_int(i: i64) -> Var { + Var::mk_integer(i) +} + +pub fn v_bool(b: bool) -> Var { + if b { + v_int(1) + } else { + v_int(0) + } +} + +pub fn v_none() -> Var { + // TODO lazy_static singleton + Var::mk_none() +} + +pub fn v_str(s: &str) -> Var { + Var::mk_str(s) +} + +pub fn v_string(s: String) -> Var { + Var::mk_str(&s) +} + +pub fn v_list(values: &[Var]) -> Var { + Var::mk_list(values) +} + +pub fn v_list_iter>(values: IT) -> Var { + Var::mk_list_iter(values) +} + +pub fn v_map(pairs: &[(Var, Var)]) -> Var { + Var::mk_map(pairs) +} + +pub fn v_float(f: f64) -> Var { + Var::mk_float(R64::from(f)) +} + +pub fn v_floatr(f: R64) -> Var { + Var::mk_float(f) +} + +pub fn v_err(e: Error) -> Var { + Var::mk_error(e) +} + +pub fn v_obj(o: i64) -> Var { + Var::mk_object(Objid(o)) +} + +pub fn v_objid(o: Objid) -> Var { + Var::mk_object(o) +} + +pub fn v_empty_list() -> Var { + // TODO: lazy static + v_list(&[]) +} + +pub fn v_empty_str() -> Var { + // TODO: lazy static + v_str("") +} + +pub fn v_empty_map() -> Var { + // TODO: lazy static + v_map(&[]) +} + +impl From for Var { + fn from(i: i64) -> Self { + Var::mk_integer(i) + } +} + +impl From<&str> for Var { + fn from(s: &str) -> Self { + Var::mk_str(s) + } +} + +impl From for Var { + fn from(s: String) -> Self { + Var::mk_str(&s) + } +} + +impl From for Var { + fn from(o: Objid) -> Self { + Var::mk_object(o) + } +} + +impl From for Var { + fn from(e: Error) -> Self { + Var::mk_error(e) + } +} + +#[cfg(test)] +mod tests { + use crate::var::var::Var; + use crate::var::variant::Variant; + + #[test] + fn test_int_pack_unpack() { + let i = Var::mk_integer(42); + + match i.variant() { + Variant::Int(i) => assert_eq!(*i, 42), + _ => panic!("Expected integer"), + } + } + + #[test] + fn test_float_pack_unpack() { + let f = Var::mk_float(42.0.into()); + + match f.variant() { + Variant::Float(f) => assert_eq!(*f, 42.0), + _ => panic!("Expected float"), + } + } + + #[test] + fn test_alpha_numeric_sort_order() { + // "a" should come after "6" + let six = Var::mk_integer(6); + let a = Var::mk_str("a"); + assert_eq!(six.cmp(&a), std::cmp::Ordering::Less); + + // and 9 before "a" as well + let nine = Var::mk_integer(9); + assert_eq!(nine.cmp(&a), std::cmp::Ordering::Less); + + // now in the other order. + assert_eq!(a.cmp(&six), std::cmp::Ordering::Greater); + assert_eq!(a.cmp(&nine), std::cmp::Ordering::Greater); + } +} diff --git a/crates/values/src/var/variant.rs b/crates/values/src/var/variant.rs index eab78a77..0dc14217 100644 --- a/crates/values/src/var/variant.rs +++ b/crates/values/src/var/variant.rs @@ -12,45 +12,250 @@ // this program. If not, see . // -use std::fmt::{Display, Formatter, Result as FmtResult}; - -use crate::var::error::Error; use crate::var::list::List; -use crate::var::map::Map; -use crate::var::objid::Objid; -use crate::var::string::Str; - -use super::Var; +use crate::var::storage::VarBuffer; +use crate::var::Associative; +use crate::var::{map, string, Sequence}; +use crate::var::{Error, Objid, VarType}; +use decorum::R64; +use flexbuffers::{Reader, VectorBuilder}; +use num_traits::ToPrimitive; +use std::cmp::Ordering; +use std::fmt::{Debug, Formatter}; +use std::hash::{Hash, Hasher}; -#[derive(Clone, Debug)] +/// Our series of types +#[derive(Clone)] pub enum Variant { None, - Str(Str), Obj(Objid), Int(i64), Float(f64), - Err(Error), List(List), - Map(Map), + Str(string::Str), + Map(map::Map), + Err(Error), +} + +impl Hash for Variant { + fn hash(&self, state: &mut H) { + match self { + Variant::None => 0.hash(state), + Variant::Obj(o) => o.0.hash(state), + Variant::Int(i) => i.hash(state), + Variant::Float(f) => f.to_f64().unwrap().to_bits().hash(state), + Variant::List(l) => l.hash(state), + Variant::Str(s) => s.hash(state), + Variant::Map(m) => m.hash(state), + Variant::Err(e) => e.hash(state), + } + } +} + +impl Ord for Variant { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (Variant::None, Variant::None) => Ordering::Equal, + (Variant::Obj(l), Variant::Obj(r)) => l.cmp(r), + (Variant::Int(l), Variant::Int(r)) => l.cmp(r), + (Variant::Float(l), Variant::Float(r)) => { + // For floats, we wrap in decorum first. + let l = R64::from(l.to_f64().unwrap()); + let r = R64::from(r.to_f64().unwrap()); + l.cmp(&r) + } + (Variant::List(l), Variant::List(r)) => l.cmp(r), + (Variant::Str(l), Variant::Str(r)) => l.cmp(r), + (Variant::Map(l), Variant::Map(r)) => l.cmp(r), + (Variant::Err(l), Variant::Err(r)) => l.cmp(r), + (Variant::None, _) => Ordering::Less, + (_, Variant::None) => Ordering::Greater, + (Variant::Obj(_), _) => Ordering::Less, + (_, Variant::Obj(_)) => Ordering::Greater, + (Variant::Int(_), _) => Ordering::Less, + (_, Variant::Int(_)) => Ordering::Greater, + (Variant::Float(_), _) => Ordering::Less, + (_, Variant::Float(_)) => Ordering::Greater, + (Variant::List(_), _) => Ordering::Less, + (_, Variant::List(_)) => Ordering::Greater, + (Variant::Str(_), _) => Ordering::Less, + (_, Variant::Str(_)) => Ordering::Greater, + (Variant::Map(_), _) => Ordering::Less, + (_, Variant::Map(_)) => Ordering::Greater, + } + } +} + +impl PartialOrd for Variant { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } } -impl Display for Variant { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { +impl Debug for Variant { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - Self::None => write!(f, "None"), - Self::Str(s) => write!(f, "{s}"), - Self::Obj(o) => write!(f, "{o}"), - Self::Int(i) => write!(f, "{i}"), - Self::Float(fl) => write!(f, "{fl}"), - Self::Err(e) => write!(f, "{e}"), - Self::List(l) => write!(f, "{l}"), - Self::Map(m) => write!(f, "{m}"), + Variant::None => write!(f, "None"), + Variant::Obj(o) => write!(f, "Object({})", o.0), + Variant::Int(i) => write!(f, "Integer({})", i), + Variant::Float(fl) => write!(f, "Float({})", fl), + Variant::List(l) => { + // Items... + let r = l.reader.iter(); + let i: Vec<_> = r.map(Variant::from_reader).collect(); + write!(f, "List([size = {}, items = {:?}])", l.len(), i) + } + Variant::Str(s) => write!(f, "String({:?})", s.as_string()), + Variant::Map(m) => { + // Items... + let r = m.reader.iter(); + let i: Vec<_> = r.map(Variant::from_reader).collect(); + write!(f, "Map([size = {}, items = {:?}])", m.len(), i) + } + Variant::Err(e) => write!(f, "Error({:?})", e), } } } -impl From for Var { - fn from(val: Variant) -> Self { - Self::new(val) +impl Variant { + pub(crate) fn from_reader(vec: Reader) -> Self { + // Each Var is a vector of two elements, the type and the value. + let vec = vec.as_vector(); + + let mut vec_iter = vec.iter(); + let type_reader = vec_iter.next().unwrap(); + let t = type_reader.as_u8(); + let t = VarType::from_repr(t).unwrap(); + + // Now we can match on the type and pull out the value. + match t { + VarType::TYPE_NONE => Variant::None, + VarType::TYPE_INT => { + let v = vec_iter.next().unwrap(); + let i = v.as_i64(); + Self::Int(i) + } + VarType::TYPE_FLOAT => { + let v = vec_iter.next().unwrap(); + let f = v.as_f64(); + Self::Float(f) + } + VarType::TYPE_OBJ => { + let v = vec_iter.next().unwrap(); + let o = v.as_i64(); + Self::Obj(Objid(o)) + } + VarType::TYPE_STR => { + let v = vec_iter.next().unwrap(); + Self::Str(string::Str { + reader: Box::new(v), + }) + } + VarType::TYPE_ERR => { + // Error code encoded as u8 + let v = vec_iter.next().unwrap(); + let e = v.as_u8(); + let e = Error::from_repr(e).unwrap(); + Self::Err(e) + } + VarType::TYPE_LIST => { + let v = vec_iter.next().unwrap(); + let l = v.as_vector(); + Self::List(List { + reader: Box::new(l), + }) + } + VarType::TYPE_LABEL => { + unimplemented!("Labels are not supported in actual values") + } + VarType::TYPE_MAP => { + let v = vec_iter.next().unwrap(); + let m = v.as_vector(); + Self::Map(map::Map { + reader: Box::new(m), + }) + } + } + } + + /// Push a copy of Self into a flexbuffer + pub(crate) fn push_to(&self, item_vec: &mut VectorBuilder) { + match self { + Variant::None => { + item_vec.push(VarType::TYPE_NONE as u8); + } + Variant::Obj(o) => { + item_vec.push(VarType::TYPE_OBJ as u8); + item_vec.push(o.0); + } + Variant::Int(i) => { + item_vec.push(VarType::TYPE_INT as u8); + item_vec.push(*i); + } + Variant::Float(f) => { + item_vec.push(VarType::TYPE_FLOAT as u8); + item_vec.push(f.to_f64().unwrap()); + } + Variant::List(l) => { + item_vec.push(VarType::TYPE_LIST as u8); + let mut vb = item_vec.start_vector(); + // Then we iterate over the rest of the elements. + for i in 0..l.reader.len() { + let item_reader = l.reader.idx(i); + let v = Variant::from_reader(item_reader); + v.push_item(&mut vb); + } + vb.end_vector(); + } + Variant::Map(m) => { + item_vec.push(VarType::TYPE_MAP as u8); + let mut vb = item_vec.start_vector(); + let mut iter = m.reader.iter(); + // Now iterate over the pairs. + for _ in 0..m.len() { + let k = iter.next().unwrap(); + let v = iter.next().unwrap(); + let key = Variant::from_reader(k); + let value = Variant::from_reader(v); + key.push_item(&mut vb); + value.push_item(&mut vb); + } + vb.end_vector(); + } + Variant::Str(s) => { + item_vec.push(VarType::TYPE_STR as u8); + item_vec.push(s.as_string().as_str()); + } + Variant::Err(e) => { + item_vec.push(VarType::TYPE_ERR as u8); + item_vec.push(*e as u8); + } + } + } + + /// Push a copy along with an item vector + pub(crate) fn push_item(&self, item_vec: &mut VectorBuilder) { + let mut vb = item_vec.start_vector(); + self.push_to(&mut vb); + vb.end_vector(); } } + +impl PartialEq for Variant { + fn eq(&self, other: &Self) -> bool { + // If the types are different, they're not equal. + match (self, other) { + (Variant::Str(s), Variant::Str(o)) => s == o, + (Variant::Int(s), Variant::Int(o)) => s == o, + (Variant::Float(s), Variant::Float(o)) => s == o, + (Variant::Obj(s), Variant::Obj(o)) => s == o, + (Variant::List(s), Variant::List(o)) => s == o, + (Variant::Map(s), Variant::Map(o)) => s == o, + (Variant::Err(s), Variant::Err(o)) => s == o, + (Variant::None, Variant::None) => true, + _ => false, + } + } +} + +impl Eq for Variant {} diff --git a/crates/values/src/var/varops.rs b/crates/values/src/var/varops.rs deleted file mode 100644 index f0db2e2d..00000000 --- a/crates/values/src/var/varops.rs +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (C) 2024 Ryan Daum -// -// This program is free software: you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free Software -// Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along with -// this program. If not, see . -// - -use std::ops::{Div, Mul, Neg, Sub}; - -use num_traits::Zero; -use paste::paste; - -use crate::var::error::Error; -use crate::var::error::Error::{E_INVARG, E_RANGE, E_TYPE}; -use crate::var::variant::Variant; -use crate::var::{v_empty_list, v_empty_str, v_listv, Var}; -use crate::var::{v_err, v_float, v_int}; - -macro_rules! binary_numeric_coercion_op { - ($op:tt ) => { - pub fn $op(&self, v: &Var) -> Result { - match (self.variant(), v.variant()) { - (Variant::Float(l), Variant::Float(r)) => Ok(v_float(l.$op(*r))), - (Variant::Int(l), Variant::Int(r)) => { - paste! { l.[](*r).map(v_int).ok_or(E_INVARG) } - } - (Variant::Float(l), Variant::Int(r)) => Ok(v_float(l.$op(*r as f64))), - (Variant::Int(l), Variant::Float(r)) => Ok(v_float((*l as f64).$op(*r))), - (_, _) => Ok(v_err(E_TYPE)), - } - } - }; -} - -impl Var { - #[must_use] - pub fn is_true(&self) -> bool { - match self.variant() { - Variant::Str(s) => !s.is_empty(), - Variant::Int(i) => *i != 0, - Variant::Float(f) => !f.is_zero(), - Variant::List(l) => !l.is_empty(), - Variant::Map(m) => !m.is_empty(), - _ => false, - } - } - - pub fn index_set(&mut self, i: usize, value: Self) -> Result { - match self.variant_mut() { - Variant::List(l) => { - if !i < l.len() { - return Err(E_RANGE); - } - - Ok(l.set(i, value)) - } - Variant::Str(s) => { - if !i < s.len() { - return Err(E_RANGE); - } - - let Variant::Str(value) = value.variant() else { - return Err(E_INVARG); - }; - - if value.len() != 1 { - return Err(E_INVARG); - } - - Ok(s.set(i, value)) - } - _ => Err(E_TYPE), - } - } - - /// 1-indexed position of the first occurrence of `v` in `self`, or `E_TYPE` if `self` is not a - /// list. - // TODO: Make Var consistent on 0-indexing vs 1-indexing - // Various places have 1-indexing polluting the Var API, but in others we - // assume 0-indexing and adjust in the opcodes. 0 indexing should be done in Var, and opcodes and builtins - // should be the ones to adjust 1-indexing. - // Examples: index_in, range, rangeset - #[must_use] - pub fn index_in(&self, v: &Self) -> Self { - let position = match self.variant() { - Variant::List(l) => l.iter().position(|x| x == *v), - Variant::Map(m) => m.iter().position(|(_k, x)| x == v), - _ => { - return v_err(E_TYPE); - } - }; - - v_int(position.map(|pos| pos + 1).unwrap_or(0) as i64) - } - - binary_numeric_coercion_op!(mul); - binary_numeric_coercion_op!(div); - binary_numeric_coercion_op!(sub); - - pub fn add(&self, v: &Self) -> Result { - match (self.variant(), v.variant()) { - (Variant::Float(l), Variant::Float(r)) => Ok(v_float(*l + *r)), - (Variant::Int(l), Variant::Int(r)) => l.checked_add(*r).map(v_int).ok_or(E_INVARG), - (Variant::Float(l), Variant::Int(r)) => Ok(v_float(*l + (*r as f64))), - (Variant::Int(l), Variant::Float(r)) => Ok(v_float(*l as f64 + *r)), - (Variant::Str(s), Variant::Str(r)) => Ok(s.append(r)), - (_, _) => Ok(v_err(E_TYPE)), - } - } - - pub fn negative(&self) -> Result { - match self.variant() { - Variant::Int(l) => l.checked_neg().map(v_int).ok_or(E_INVARG), - Variant::Float(f) => Ok(v_float(f.neg())), - _ => Ok(v_err(E_TYPE)), - } - } - - pub fn modulus(&self, v: &Self) -> Result { - match (self.variant(), v.variant()) { - (Variant::Float(l), Variant::Float(r)) => Ok(v_float(*l % *r)), - (Variant::Int(l), Variant::Int(r)) => l.checked_rem(*r).map(v_int).ok_or(E_INVARG), - (Variant::Float(l), Variant::Int(r)) => Ok(v_float(*l % (*r as f64))), - (Variant::Int(l), Variant::Float(r)) => Ok(v_float(*l as f64 % (*r))), - (_, _) => Ok(v_err(E_TYPE)), - } - } - - pub fn pow(&self, v: &Self) -> Result { - match (self.variant(), v.variant()) { - (Variant::Float(l), Variant::Float(r)) => Ok(v_float(l.powf(*r))), - (Variant::Int(l), Variant::Int(r)) => { - let r = u32::try_from(*r).map_err(|_| E_INVARG)?; - l.checked_pow(r).map(v_int).ok_or(E_INVARG) - } - (Variant::Float(l), Variant::Int(r)) => Ok(v_float(l.powi(*r as i32))), - (Variant::Int(l), Variant::Float(r)) => Ok(v_float((*l as f64).powf(*r))), - (_, _) => Ok(v_err(E_TYPE)), - } - } - - pub fn len(&self) -> Result { - match self.variant() { - Variant::Str(s) => Ok(v_int(s.len() as i64)), - Variant::List(l) => Ok(v_int(l.len() as i64)), - _ => Ok(v_err(E_TYPE)), - } - } - - pub fn index(&self, idx: usize) -> Result { - match self.variant() { - Variant::List(l) => match l.get(idx) { - None => Ok(v_err(E_RANGE)), - Some(v) => Ok(v.clone()), - }, - Variant::Str(s) => match s.get(idx) { - None => Ok(v_err(E_RANGE)), - Some(v) => Ok(v), - }, - _ => Ok(v_err(E_TYPE)), - } - } - - pub fn range(&self, from: i64, to: i64) -> Result { - match self.variant() { - Variant::Str(s) => { - let len = s.len() as i64; - if to < from { - return Ok(v_empty_str()); - } - if from <= 0 || from > len + 1 || to > len { - return Ok(v_err(E_RANGE)); - } - let (from, to) = (from as usize, to as usize); - Ok(s.get_range(from - 1..to).unwrap()) - } - Variant::List(l) => { - let len = l.len() as i64; - if to < from { - return Ok(v_empty_list()); - } - if from <= 0 || from > len + 1 || to < 1 || to > len { - return Ok(v_err(E_RANGE)); - } - let mut res = Vec::with_capacity((to - from + 1) as usize); - for i in from..=to { - let v = l.get((i - 1) as usize).unwrap(); - res.push(v); - } - Ok(v_listv(res)) - } - _ => Ok(v_err(E_TYPE)), - } - } - - // TODO: consolidate Map logic into here. - pub fn range_set(&self, value: Self, from: i64, to: i64) -> Result { - let (base_len, val_len) = match (self.variant(), value.variant()) { - (Variant::Str(base_str), Variant::Str(val_str)) => { - (base_str.len() as i64, val_str.len() as i64) - } - (Variant::List(base_list), Variant::List(val_list)) => { - (base_list.len() as i64, val_list.len() as i64) - } - _ => return Ok(v_err(E_TYPE)), - }; - - // In range assignments only, MOO treats "0" for start (and even negative values) as - // "start of range." - // So we'll just min 'from' to '1' here. - // Does not hold for range retrievals. - let from = from.max(1); - if from > base_len + 1 || to > base_len { - return Ok(v_err(E_RANGE)); - } - - let lenleft = if from > 1 { from - 1 } else { 0 }; - let lenmiddle = val_len; - let lenright = if base_len > to { base_len - to } else { 0 }; - let newsize = lenleft + lenmiddle + lenright; - - let (from, to) = (from as usize, to as usize); - let ans = match (self.variant(), value.variant()) { - (Variant::Str(base_str), Variant::Str(_value_str)) => { - let ans = base_str.get_range(0..from - 1).unwrap_or_else(v_empty_str); - let ans = ans.add(&value)?; - - ans.add( - &base_str - .get_range(to..base_str.len()) - .unwrap_or_else(v_empty_str), - )? - } - (Variant::List(base_list), Variant::List(value_list)) => { - let mut ans: Vec = Vec::with_capacity(newsize as usize); - - let base_list = base_list.iter().collect::>(); - ans.extend_from_slice(&base_list[..from - 1]); - ans.extend(value_list.iter()); - ans.extend_from_slice(&base_list[to..]); - v_listv(ans) - } - _ => unreachable!(), - }; - - Ok(ans) - } -} diff --git a/crates/web-host/src/host/mod.rs b/crates/web-host/src/host/mod.rs index d1d86331..ff999ff9 100644 --- a/crates/web-host/src/host/mod.rs +++ b/crates/web-host/src/host/mod.rs @@ -15,8 +15,7 @@ pub mod web_host; mod ws_connection; -use moor_values::var::Var; -use moor_values::var::Variant; +use moor_values::{Var, Variant}; use serde::Serialize; use serde_derive::Deserialize; use serde_json::{json, Number}; @@ -58,12 +57,8 @@ pub fn var_as_json(v: &Var) -> serde_json::Value { } serde_json::Value::Array(v) } - Variant::Map(m) => { - let mut v = serde_json::Map::new(); - for (k, e) in m.iter() { - v.insert(k.to_string(), var_as_json(e)); - } - serde_json::Value::Object(v) + Variant::Map(_m) => { + unimplemented!("Maps are not supported in JSON serialization"); } } } diff --git a/crates/web-host/src/host/web_host.rs b/crates/web-host/src/host/web_host.rs index edcc562c..f95fde7d 100644 --- a/crates/web-host/src/host/web_host.rs +++ b/crates/web-host/src/host/web_host.rs @@ -21,7 +21,7 @@ use axum::response::{IntoResponse, Response}; use axum::{Form, Json}; use eyre::eyre; -use moor_values::var::Objid; +use moor_values::Objid; use rpc_async_client::rpc_client::RpcSendClient; use rpc_common::AuthToken; use rpc_common::RpcRequest::{Attach, ConnectionEstablish}; diff --git a/crates/web-host/src/host/ws_connection.rs b/crates/web-host/src/host/ws_connection.rs index 3a52c1a8..a3d8a596 100644 --- a/crates/web-host/src/host/ws_connection.rs +++ b/crates/web-host/src/host/ws_connection.rs @@ -17,7 +17,7 @@ use axum::extract::ws::{Message, WebSocket}; use futures_util::stream::SplitSink; use futures_util::{SinkExt, StreamExt}; use moor_values::tasks::{AbortLimitReason, CommandError, Event, SchedulerError, VerbProgramError}; -use moor_values::var::{Objid, Var}; +use moor_values::{Objid, Var}; use rpc_async_client::pubsub_client::broadcast_recv; use rpc_async_client::pubsub_client::events_recv; use rpc_async_client::rpc_client::RpcSendClient;