commit
9a54fd6fae
13
CHANGELOG.md
13
CHANGELOG.md
@ -4,10 +4,16 @@ Rhai Release Notes
|
|||||||
Version 0.20.2
|
Version 0.20.2
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
This version adds a number of convenience features:
|
||||||
|
|
||||||
|
* Ability for a `Dynamic` to hold an `i32` _tag_ of arbitrary data
|
||||||
|
|
||||||
|
* Simplifies dynamic properties access by falling back to an indexer (passing the name of the property as a string) when a property is not found.
|
||||||
|
|
||||||
Bug fixes
|
Bug fixes
|
||||||
---------
|
---------
|
||||||
|
|
||||||
* Constant propagation during optimization for constants held in a custom scope now works properly instead of always replacing by `()`.
|
* Propagation of constants held in a custom scope now works properly instead of always replacing by `()`.
|
||||||
|
|
||||||
Breaking changes
|
Breaking changes
|
||||||
----------------
|
----------------
|
||||||
@ -15,14 +21,17 @@ Breaking changes
|
|||||||
* `Engine::disable_doc_comments` is removed because doc-comments are now placed under the `metadata` feature flag.
|
* `Engine::disable_doc_comments` is removed because doc-comments are now placed under the `metadata` feature flag.
|
||||||
* Registering a custom syntax now only requires specifying whether the `Scope` is adjusted (i.e. whether variables are added or removed). There is no need to specify the number of variables added/removed.
|
* Registering a custom syntax now only requires specifying whether the `Scope` is adjusted (i.e. whether variables are added or removed). There is no need to specify the number of variables added/removed.
|
||||||
* Assigning to a property of a constant is now allowed and no longer raise an `EvalAltResult::ErrorAssignmentToConstant` error. This is to facilitate the Singleton pattern. Registered setter functions are automatically guarded against setters calling on constants and will continue to raise errors unless the `pure` attribute is present (for plugins).
|
* Assigning to a property of a constant is now allowed and no longer raise an `EvalAltResult::ErrorAssignmentToConstant` error. This is to facilitate the Singleton pattern. Registered setter functions are automatically guarded against setters calling on constants and will continue to raise errors unless the `pure` attribute is present (for plugins).
|
||||||
|
* If a property getter/setter is not found, an indexer with string index, if any, is tried.
|
||||||
|
* The indexers API (`Engine::register_indexer_XXX` and `Module::set_indexer_XXX`) are now also exposed under `no_index`.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* Each `Dynamic` value can now contain arbitrary data (type `i16`) in the form of a _tag_. This is to use up otherwise wasted space in the `Dynamic` type.
|
* Each `Dynamic` value can now contain arbitrary data (type `i32`) in the form of a _tag_. This is to use up otherwise wasted space in the `Dynamic` type.
|
||||||
* A new internal feature `no_smartstring` to turn off `SmartString` for those rare cases that it is needed.
|
* A new internal feature `no_smartstring` to turn off `SmartString` for those rare cases that it is needed.
|
||||||
* `DynamicReadLock` and `DynamicWriteLoc` are exposed under `internals`.
|
* `DynamicReadLock` and `DynamicWriteLoc` are exposed under `internals`.
|
||||||
* `From<Shared<Locked<Dynamic>>>` is added for `Dynamic` mapping directly to a shared value, together with support for `Dynamic::from`.
|
* `From<Shared<Locked<Dynamic>>>` is added for `Dynamic` mapping directly to a shared value, together with support for `Dynamic::from`.
|
||||||
|
* An indexer with string index acts as a _fallback_ to a property getter/setter.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
@ -92,7 +92,7 @@ default_features = false
|
|||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.rust_decimal]
|
[dependencies.rust_decimal]
|
||||||
version = "1.13"
|
version = "1.14"
|
||||||
default_features = false
|
default_features = false
|
||||||
features = ["maths"]
|
features = ["maths"]
|
||||||
optional = true
|
optional = true
|
||||||
|
@ -6,7 +6,7 @@ const REPEAT = 5;
|
|||||||
|
|
||||||
fn fib(n) {
|
fn fib(n) {
|
||||||
if n < 2 {
|
if n < 2 {
|
||||||
n
|
n
|
||||||
} else {
|
} else {
|
||||||
fib(n-1) + fib(n-2)
|
fib(n-1) + fib(n-2)
|
||||||
}
|
}
|
||||||
@ -19,7 +19,7 @@ let result;
|
|||||||
let now = timestamp();
|
let now = timestamp();
|
||||||
|
|
||||||
for n in range(0, REPEAT) {
|
for n in range(0, REPEAT) {
|
||||||
result = fib(TARGET);
|
result = fib(TARGET);
|
||||||
}
|
}
|
||||||
|
|
||||||
print(`Finished. Run time = ${now.elapsed} seconds.`);
|
print(`Finished. Run time = ${now.elapsed} seconds.`);
|
||||||
|
@ -8,7 +8,7 @@ loop {
|
|||||||
|
|
||||||
x -= 1;
|
x -= 1;
|
||||||
|
|
||||||
if x <= 0 { break; }
|
if x <= 0 { break; }
|
||||||
}
|
}
|
||||||
|
|
||||||
export x as foo;
|
export x as foo;
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
const SIZE = 50;
|
const SIZE = 50;
|
||||||
|
|
||||||
fn new_mat(x, y) {
|
fn new_mat(x, y) {
|
||||||
let row = [];
|
let row = [];
|
||||||
row.pad(y, 0.0);
|
row.pad(y, 0.0);
|
||||||
|
|
||||||
let matrix = [];
|
let matrix = [];
|
||||||
matrix.pad(x, row);
|
matrix.pad(x, row);
|
||||||
|
|
||||||
matrix
|
matrix
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mat_gen() {
|
fn mat_gen() {
|
||||||
@ -35,13 +35,13 @@ fn mat_mul(a, b) {
|
|||||||
|
|
||||||
let c = new_mat(a.len, b[0].len);
|
let c = new_mat(a.len, b[0].len);
|
||||||
|
|
||||||
for i in range(0, c.len) {
|
for i in range(0, c.len) {
|
||||||
for j in range(0, c[i].len) {
|
for j in range(0, c[i].len) {
|
||||||
c[i][j] = 0.0;
|
c[i][j] = 0.0;
|
||||||
|
|
||||||
for z in range(0, a[i].len) {
|
for z in range(0, a[i].len) {
|
||||||
c[i][j] += a[i][z] * b2[j][z];
|
c[i][j] += a[i][z] * b2[j][z];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ const c = mat_mul(a, b);
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
for i in range(0, SIZE) {
|
for i in range(0, SIZE) {
|
||||||
print(c[i]);
|
print(c[i]);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -5,79 +5,79 @@ let now = timestamp();
|
|||||||
let adverbs = [ "moderately", "really", "slightly", "very" ];
|
let adverbs = [ "moderately", "really", "slightly", "very" ];
|
||||||
|
|
||||||
let adjectives = [
|
let adjectives = [
|
||||||
"abandoned", "able", "absolute", "academic", "acceptable", "acclaimed",
|
"abandoned", "able", "absolute", "academic", "acceptable", "acclaimed",
|
||||||
"accomplished", "accurate", "aching", "acidic", "acrobatic", "active",
|
"accomplished", "accurate", "aching", "acidic", "acrobatic", "active",
|
||||||
"actual", "adept", "admirable", "admired", "adolescent", "adorable", "adored",
|
"actual", "adept", "admirable", "admired", "adolescent", "adorable", "adored",
|
||||||
"advanced", "adventurous", "affectionate", "afraid", "aged", "aggravating",
|
"advanced", "adventurous", "affectionate", "afraid", "aged", "aggravating",
|
||||||
"aggressive", "agile", "agitated", "agonizing", "agreeable", "ajar",
|
"aggressive", "agile", "agitated", "agonizing", "agreeable", "ajar",
|
||||||
"alarmed", "alarming", "alert", "alienated", "alive", "all", "altruistic",
|
"alarmed", "alarming", "alert", "alienated", "alive", "all", "altruistic",
|
||||||
"amazing", "ambitious", "ample", "amused", "amusing", "anchored", "ancient",
|
"amazing", "ambitious", "ample", "amused", "amusing", "anchored", "ancient",
|
||||||
"angelic", "angry", "anguished", "animated", "annual", "another", "antique",
|
"angelic", "angry", "anguished", "animated", "annual", "another", "antique",
|
||||||
"anxious", "any", "apprehensive", "appropriate", "apt", "arctic", "arid",
|
"anxious", "any", "apprehensive", "appropriate", "apt", "arctic", "arid",
|
||||||
"aromatic", "artistic", "ashamed", "assured", "astonishing", "athletic",
|
"aromatic", "artistic", "ashamed", "assured", "astonishing", "athletic",
|
||||||
"attached", "attentive", "attractive", "austere", "authentic", "authorized",
|
"attached", "attentive", "attractive", "austere", "authentic", "authorized",
|
||||||
"automatic", "avaricious", "average", "aware", "awesome", "awful", "awkward",
|
"automatic", "avaricious", "average", "aware", "awesome", "awful", "awkward",
|
||||||
"babyish", "back", "bad", "baggy", "bare", "barren", "basic", "beautiful",
|
"babyish", "back", "bad", "baggy", "bare", "barren", "basic", "beautiful",
|
||||||
"belated", "beloved", "beneficial", "best", "better", "bewitched", "big",
|
"belated", "beloved", "beneficial", "best", "better", "bewitched", "big",
|
||||||
"big-hearted", "biodegradable", "bite-sized", "bitter", "black",
|
"big-hearted", "biodegradable", "bite-sized", "bitter", "black",
|
||||||
"black-and-white", "bland", "blank", "blaring", "bleak", "blind", "blissful",
|
"black-and-white", "bland", "blank", "blaring", "bleak", "blind", "blissful",
|
||||||
"blond", "blue", "blushing", "bogus", "boiling", "bold", "bony", "boring",
|
"blond", "blue", "blushing", "bogus", "boiling", "bold", "bony", "boring",
|
||||||
"bossy", "both", "bouncy", "bountiful", "bowed", "brave", "breakable",
|
"bossy", "both", "bouncy", "bountiful", "bowed", "brave", "breakable",
|
||||||
"brief", "bright", "brilliant", "brisk", "broken", "bronze", "brown",
|
"brief", "bright", "brilliant", "brisk", "broken", "bronze", "brown",
|
||||||
"bruised", "bubbly", "bulky", "bumpy", "buoyant", "burdensome", "burly",
|
"bruised", "bubbly", "bulky", "bumpy", "buoyant", "burdensome", "burly",
|
||||||
"bustling", "busy", "buttery", "buzzing", "calculating", "calm", "candid",
|
"bustling", "busy", "buttery", "buzzing", "calculating", "calm", "candid",
|
||||||
"canine", "capital", "carefree", "careful", "careless", "caring", "cautious",
|
"canine", "capital", "carefree", "careful", "careless", "caring", "cautious",
|
||||||
"cavernous", "celebrated", "charming", "cheap", "cheerful", "cheery", "chief",
|
"cavernous", "celebrated", "charming", "cheap", "cheerful", "cheery", "chief",
|
||||||
"chilly", "chubby", "circular", "classic", "clean", "clear", "clear-cut",
|
"chilly", "chubby", "circular", "classic", "clean", "clear", "clear-cut",
|
||||||
"clever", "close", "closed", "cloudy", "clueless", "clumsy", "cluttered",
|
"clever", "close", "closed", "cloudy", "clueless", "clumsy", "cluttered",
|
||||||
"coarse", "cold", "colorful", "colorless", "colossal", "comfortable",
|
"coarse", "cold", "colorful", "colorless", "colossal", "comfortable",
|
||||||
"common", "compassionate", "competent", "complete", "complex", "complicated",
|
"common", "compassionate", "competent", "complete", "complex", "complicated",
|
||||||
"composed", "concerned", "concrete", "confused", "conscious", "considerate",
|
"composed", "concerned", "concrete", "confused", "conscious", "considerate",
|
||||||
"constant", "content", "conventional", "cooked", "cool", "cooperative",
|
"constant", "content", "conventional", "cooked", "cool", "cooperative",
|
||||||
"coordinated", "corny", "corrupt", "costly", "courageous", "courteous",
|
"coordinated", "corny", "corrupt", "costly", "courageous", "courteous",
|
||||||
"crafty"
|
"crafty"
|
||||||
];
|
];
|
||||||
|
|
||||||
let animals = [
|
let animals = [
|
||||||
"aardvark", "african buffalo", "albatross", "alligator", "alpaca", "ant",
|
"aardvark", "african buffalo", "albatross", "alligator", "alpaca", "ant",
|
||||||
"anteater", "antelope", "ape", "armadillo", "baboon", "badger", "barracuda",
|
"anteater", "antelope", "ape", "armadillo", "baboon", "badger", "barracuda",
|
||||||
"bat", "bear", "beaver", "bee", "bison", "black panther", "blue jay", "boar",
|
"bat", "bear", "beaver", "bee", "bison", "black panther", "blue jay", "boar",
|
||||||
"butterfly", "camel", "capybara", "carduelis", "caribou", "cassowary", "cat",
|
"butterfly", "camel", "capybara", "carduelis", "caribou", "cassowary", "cat",
|
||||||
"caterpillar", "cattle", "chamois", "cheetah", "chicken", "chimpanzee",
|
"caterpillar", "cattle", "chamois", "cheetah", "chicken", "chimpanzee",
|
||||||
"chinchilla", "chough", "clam", "cobra", "cockroach", "cod", "cormorant",
|
"chinchilla", "chough", "clam", "cobra", "cockroach", "cod", "cormorant",
|
||||||
"coyote", "crab", "crane", "crocodile", "crow", "curlew", "deer", "dinosaur",
|
"coyote", "crab", "crane", "crocodile", "crow", "curlew", "deer", "dinosaur",
|
||||||
"dog", "dolphin", "domestic pig", "donkey", "dotterel", "dove", "dragonfly",
|
"dog", "dolphin", "domestic pig", "donkey", "dotterel", "dove", "dragonfly",
|
||||||
"duck", "dugong", "dunlin", "eagle", "echidna", "eel", "elephant seal",
|
"duck", "dugong", "dunlin", "eagle", "echidna", "eel", "elephant seal",
|
||||||
"elephant", "elk", "emu", "falcon", "ferret", "finch", "fish", "flamingo",
|
"elephant", "elk", "emu", "falcon", "ferret", "finch", "fish", "flamingo",
|
||||||
"fly", "fox", "frog", "gaur", "gazelle", "gerbil", "giant panda", "giraffe",
|
"fly", "fox", "frog", "gaur", "gazelle", "gerbil", "giant panda", "giraffe",
|
||||||
"gnat", "goat", "goldfish", "goose", "gorilla", "goshawk", "grasshopper",
|
"gnat", "goat", "goldfish", "goose", "gorilla", "goshawk", "grasshopper",
|
||||||
"grouse", "guanaco", "guinea fowl", "guinea pig", "gull", "hamster", "hare",
|
"grouse", "guanaco", "guinea fowl", "guinea pig", "gull", "hamster", "hare",
|
||||||
"hawk", "hedgehog", "heron", "herring", "hippopotamus", "hornet", "horse",
|
"hawk", "hedgehog", "heron", "herring", "hippopotamus", "hornet", "horse",
|
||||||
"human", "hummingbird", "hyena", "ibex", "ibis", "jackal", "jaguar", "jay",
|
"human", "hummingbird", "hyena", "ibex", "ibis", "jackal", "jaguar", "jay",
|
||||||
"jellyfish", "kangaroo", "kingfisher", "koala", "komodo dragon", "kookabura",
|
"jellyfish", "kangaroo", "kingfisher", "koala", "komodo dragon", "kookabura",
|
||||||
"kouprey", "kudu", "lapwing", "lark", "lemur", "leopard", "lion", "llama",
|
"kouprey", "kudu", "lapwing", "lark", "lemur", "leopard", "lion", "llama",
|
||||||
"lobster", "locust", "loris", "louse", "lyrebird", "magpie", "mallard",
|
"lobster", "locust", "loris", "louse", "lyrebird", "magpie", "mallard",
|
||||||
"manatee", "mandrill", "mantis", "marten", "meerkat", "mink", "mole",
|
"manatee", "mandrill", "mantis", "marten", "meerkat", "mink", "mole",
|
||||||
"mongoose", "monkey", "moose", "mosquito", "mouse", "mule", "narwhal", "newt",
|
"mongoose", "monkey", "moose", "mosquito", "mouse", "mule", "narwhal", "newt",
|
||||||
"nightingale", "octopus", "okapi", "opossum", "oryx", "ostrich", "otter",
|
"nightingale", "octopus", "okapi", "opossum", "oryx", "ostrich", "otter",
|
||||||
"owl", "oyster", "parrot", "partridge", "peafowl", "pelican", "penguin",
|
"owl", "oyster", "parrot", "partridge", "peafowl", "pelican", "penguin",
|
||||||
"pheasant", "pigeon", "pinniped", "polar bear", "pony", "porcupine",
|
"pheasant", "pigeon", "pinniped", "polar bear", "pony", "porcupine",
|
||||||
"porpoise", "prairie dog", "quail", "quelea", "quetzal", "rabbit", "raccoon",
|
"porpoise", "prairie dog", "quail", "quelea", "quetzal", "rabbit", "raccoon",
|
||||||
"ram", "rat", "raven", "red deer", "red panda", "reindeer", "rhinoceros",
|
"ram", "rat", "raven", "red deer", "red panda", "reindeer", "rhinoceros",
|
||||||
"rook", "salamander", "salmon", "sand dollar", "sandpiper", "sardine",
|
"rook", "salamander", "salmon", "sand dollar", "sandpiper", "sardine",
|
||||||
"scorpion", "sea lion", "sea urchin", "seahorse", "shark", "sheep", "shrew",
|
"scorpion", "sea lion", "sea urchin", "seahorse", "shark", "sheep", "shrew",
|
||||||
"skunk", "snail", "snake", "sparrow", "spider", "spoonbill", "squid",
|
"skunk", "snail", "snake", "sparrow", "spider", "spoonbill", "squid",
|
||||||
"wallaby", "wildebeest"
|
"wallaby", "wildebeest"
|
||||||
];
|
];
|
||||||
|
|
||||||
let keys = [];
|
let keys = [];
|
||||||
|
|
||||||
for animal in animals {
|
for animal in animals {
|
||||||
for adjective in adjectives {
|
for adjective in adjectives {
|
||||||
for adverb in adverbs {
|
for adverb in adverbs {
|
||||||
keys.push(`${adverb} ${adjective} ${animal}`)
|
keys.push(`${adverb} ${adjective} ${animal}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let map = #{};
|
let map = #{};
|
||||||
@ -85,18 +85,18 @@ let map = #{};
|
|||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
for key in keys {
|
for key in keys {
|
||||||
map[key] = i;
|
map[key] = i;
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let sum = 0;
|
let sum = 0;
|
||||||
|
|
||||||
for key in keys {
|
for key in keys {
|
||||||
sum += map[key];
|
sum += map[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
for key in keys {
|
for key in keys {
|
||||||
map.remove(key);
|
map.remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
print(`Sum = ${sum}`);
|
print(`Sum = ${sum}`);
|
||||||
|
78
src/ast.rs
78
src/ast.rs
@ -13,7 +13,6 @@ use std::{
|
|||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
fmt,
|
fmt,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
iter::empty,
|
|
||||||
mem,
|
mem,
|
||||||
num::{NonZeroU8, NonZeroUsize},
|
num::{NonZeroU8, NonZeroUsize},
|
||||||
ops::{Add, AddAssign, Deref, DerefMut},
|
ops::{Add, AddAssign, Deref, DerefMut},
|
||||||
@ -254,12 +253,11 @@ impl AST {
|
|||||||
/// Set the source.
|
/// Set the source.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_source(&mut self, source: impl Into<Identifier>) -> &mut Self {
|
pub fn set_source(&mut self, source: impl Into<Identifier>) -> &mut Self {
|
||||||
self.source = Some(source.into());
|
let source = Some(source.into());
|
||||||
|
Shared::get_mut(&mut self.functions)
|
||||||
if let Some(module) = Shared::get_mut(&mut self.functions) {
|
.as_mut()
|
||||||
module.set_id(self.source.clone());
|
.map(|m| m.set_id(source.clone()));
|
||||||
}
|
self.source = source;
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Clear the source.
|
/// Clear the source.
|
||||||
@ -910,6 +908,7 @@ impl DerefMut for StmtBlock {
|
|||||||
impl fmt::Debug for StmtBlock {
|
impl fmt::Debug for StmtBlock {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("Block")?;
|
||||||
fmt::Debug::fmt(&self.0, f)?;
|
fmt::Debug::fmt(&self.0, f)?;
|
||||||
self.1.debug_print(f)
|
self.1.debug_print(f)
|
||||||
}
|
}
|
||||||
@ -1226,6 +1225,7 @@ impl Stmt {
|
|||||||
path: &mut Vec<ASTNode<'a>>,
|
path: &mut Vec<ASTNode<'a>>,
|
||||||
on_node: &mut impl FnMut(&[ASTNode]) -> bool,
|
on_node: &mut impl FnMut(&[ASTNode]) -> bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
// Push the current node onto the path
|
||||||
path.push(self.into());
|
path.push(self.into());
|
||||||
|
|
||||||
if !on_node(path) {
|
if !on_node(path) {
|
||||||
@ -1341,7 +1341,8 @@ impl Stmt {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
path.pop().unwrap();
|
path.pop()
|
||||||
|
.expect("never fails because `path` always contains the current node");
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -1399,13 +1400,13 @@ impl OpAssignment {
|
|||||||
pub fn new(op: Token) -> Self {
|
pub fn new(op: Token) -> Self {
|
||||||
let op_raw = op
|
let op_raw = op
|
||||||
.map_op_assignment()
|
.map_op_assignment()
|
||||||
.expect("token must be an op-assignment operator")
|
.expect("never fails because token must be an op-assignment operator")
|
||||||
.keyword_syntax();
|
.keyword_syntax();
|
||||||
let op_assignment = op.keyword_syntax();
|
let op_assignment = op.keyword_syntax();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
hash_op_assign: calc_fn_hash(empty(), op_assignment, 2),
|
hash_op_assign: calc_fn_hash(op_assignment, 2),
|
||||||
hash_op: calc_fn_hash(empty(), op_raw, 2),
|
hash_op: calc_fn_hash(op_raw, 2),
|
||||||
op: op_assignment,
|
op: op_assignment,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1493,13 +1494,9 @@ impl FnCallHashes {
|
|||||||
self.script.is_none()
|
self.script.is_none()
|
||||||
}
|
}
|
||||||
/// Get the script function hash from this [`FnCallHashes`].
|
/// Get the script function hash from this [`FnCallHashes`].
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if the [`FnCallHashes`] is native Rust only.
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn script_hash(&self) -> u64 {
|
pub fn script_hash(&self) -> Option<u64> {
|
||||||
self.script.unwrap()
|
self.script
|
||||||
}
|
}
|
||||||
/// Get the naive Rust function hash from this [`FnCallHashes`].
|
/// Get the naive Rust function hash from this [`FnCallHashes`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -1715,7 +1712,13 @@ pub enum Expr {
|
|||||||
)>,
|
)>,
|
||||||
),
|
),
|
||||||
/// Property access - ((getter, hash), (setter, hash), prop)
|
/// Property access - ((getter, hash), (setter, hash), prop)
|
||||||
Property(Box<((Identifier, u64), (Identifier, u64), Ident)>),
|
Property(
|
||||||
|
Box<(
|
||||||
|
(Identifier, u64),
|
||||||
|
(Identifier, u64),
|
||||||
|
(ImmutableString, Position),
|
||||||
|
)>,
|
||||||
|
),
|
||||||
/// { [statement][Stmt] ... }
|
/// { [statement][Stmt] ... }
|
||||||
Stmt(Box<StmtBlock>),
|
Stmt(Box<StmtBlock>),
|
||||||
/// func `(` expr `,` ... `)`
|
/// func `(` expr `,` ... `)`
|
||||||
@ -1780,16 +1783,14 @@ impl fmt::Debug for Expr {
|
|||||||
}
|
}
|
||||||
f.write_str(")")
|
f.write_str(")")
|
||||||
}
|
}
|
||||||
Self::Property(x) => write!(f, "Property({})", x.2.name),
|
Self::Property(x) => write!(f, "Property({})", (x.2).0),
|
||||||
Self::Stmt(x) => {
|
Self::Stmt(x) => {
|
||||||
f.write_str("Stmt")?;
|
f.write_str("ExprStmtBlock")?;
|
||||||
f.debug_list().entries(x.0.iter()).finish()
|
f.debug_list().entries(x.0.iter()).finish()
|
||||||
}
|
}
|
||||||
Self::FnCall(x, _) => {
|
Self::FnCall(x, _) => {
|
||||||
let mut ff = f.debug_struct("FnCall");
|
let mut ff = f.debug_struct("FnCall");
|
||||||
if let Some(ref ns) = x.namespace {
|
x.namespace.as_ref().map(|ns| ff.field("namespace", ns));
|
||||||
ff.field("namespace", ns);
|
|
||||||
}
|
|
||||||
ff.field("name", &x.name)
|
ff.field("name", &x.name)
|
||||||
.field("hash", &x.hashes)
|
.field("hash", &x.hashes)
|
||||||
.field("args", &x.args);
|
.field("args", &x.args);
|
||||||
@ -1843,7 +1844,10 @@ impl Expr {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::Array(x, _) if self.is_constant() => {
|
Self::Array(x, _) if self.is_constant() => {
|
||||||
let mut arr = Array::with_capacity(x.len());
|
let mut arr = Array::with_capacity(x.len());
|
||||||
arr.extend(x.iter().map(|v| v.get_constant_value().unwrap()));
|
arr.extend(x.iter().map(|v| {
|
||||||
|
v.get_constant_value()
|
||||||
|
.expect("never fails because a constant array always has a constant value")
|
||||||
|
}));
|
||||||
Dynamic::from_array(arr)
|
Dynamic::from_array(arr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1851,7 +1855,10 @@ impl Expr {
|
|||||||
Self::Map(x, _) if self.is_constant() => {
|
Self::Map(x, _) if self.is_constant() => {
|
||||||
let mut map = x.1.clone();
|
let mut map = x.1.clone();
|
||||||
x.0.iter().for_each(|(k, v)| {
|
x.0.iter().for_each(|(k, v)| {
|
||||||
*map.get_mut(k.name.as_str()).unwrap() = v.get_constant_value().unwrap()
|
*map.get_mut(k.name.as_str())
|
||||||
|
.expect("never fails because the template should contain all the keys") = v
|
||||||
|
.get_constant_value()
|
||||||
|
.expect("never fails because a constant map always has a constant value")
|
||||||
});
|
});
|
||||||
Dynamic::from_map(map)
|
Dynamic::from_map(map)
|
||||||
}
|
}
|
||||||
@ -1894,9 +1901,14 @@ impl Expr {
|
|||||||
| Self::FnCall(_, pos)
|
| Self::FnCall(_, pos)
|
||||||
| Self::Custom(_, pos) => *pos,
|
| Self::Custom(_, pos) => *pos,
|
||||||
|
|
||||||
Self::InterpolatedString(x) => x.first().unwrap().position(),
|
Self::InterpolatedString(x) => x
|
||||||
|
.first()
|
||||||
|
.expect(
|
||||||
|
"never fails because an interpolated string always contains at least one item",
|
||||||
|
)
|
||||||
|
.position(),
|
||||||
|
|
||||||
Self::Property(x) => (x.2).pos,
|
Self::Property(x) => (x.2).1,
|
||||||
Self::Stmt(x) => x.1,
|
Self::Stmt(x) => x.1,
|
||||||
|
|
||||||
Self::And(x, _) | Self::Or(x, _) | Self::Dot(x, _) | Self::Index(x, _) => {
|
Self::And(x, _) | Self::Or(x, _) | Self::Dot(x, _) | Self::Index(x, _) => {
|
||||||
@ -1928,10 +1940,12 @@ impl Expr {
|
|||||||
| Self::Custom(_, pos) => *pos = new_pos,
|
| Self::Custom(_, pos) => *pos = new_pos,
|
||||||
|
|
||||||
Self::InterpolatedString(x) => {
|
Self::InterpolatedString(x) => {
|
||||||
x.first_mut().unwrap().set_position(new_pos);
|
x.first_mut()
|
||||||
|
.expect("never fails because an interpolated string always contains at least one item")
|
||||||
|
.set_position(new_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::Property(x) => (x.2).pos = new_pos,
|
Self::Property(x) => (x.2).1 = new_pos,
|
||||||
Self::Stmt(x) => x.1 = new_pos,
|
Self::Stmt(x) => x.1 = new_pos,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2045,6 +2059,7 @@ impl Expr {
|
|||||||
path: &mut Vec<ASTNode<'a>>,
|
path: &mut Vec<ASTNode<'a>>,
|
||||||
on_node: &mut impl FnMut(&[ASTNode]) -> bool,
|
on_node: &mut impl FnMut(&[ASTNode]) -> bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
// Push the current node onto the path
|
||||||
path.push(self.into());
|
path.push(self.into());
|
||||||
|
|
||||||
if !on_node(path) {
|
if !on_node(path) {
|
||||||
@ -2098,7 +2113,8 @@ impl Expr {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
path.pop().unwrap();
|
path.pop()
|
||||||
|
.expect("never fails because `path` always contains the current node");
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -2128,7 +2144,7 @@ mod tests {
|
|||||||
96
|
96
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
assert_eq!(size_of::<Scope>(), 288);
|
assert_eq!(size_of::<Scope>(), 160);
|
||||||
assert_eq!(size_of::<LexError>(), 56);
|
assert_eq!(size_of::<LexError>(), 56);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
size_of::<ParseError>(),
|
size_of::<ParseError>(),
|
||||||
|
230
src/dynamic.rs
230
src/dynamic.rs
@ -141,6 +141,11 @@ pub enum AccessMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Arbitrary data attached to a [`Dynamic`] value.
|
/// Arbitrary data attached to a [`Dynamic`] value.
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
pub type Tag = i32;
|
||||||
|
|
||||||
|
/// Arbitrary data attached to a [`Dynamic`] value.
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
pub type Tag = i16;
|
pub type Tag = i16;
|
||||||
|
|
||||||
/// Default tag value for [`Dynamic`].
|
/// Default tag value for [`Dynamic`].
|
||||||
@ -235,9 +240,10 @@ impl<'d, T: Any + Clone> Deref for DynamicReadLock<'d, T> {
|
|||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
DynamicReadLockInner::Reference(reference) => *reference,
|
DynamicReadLockInner::Reference(reference) => *reference,
|
||||||
// Unwrapping is safe because all checking is already done in its constructor
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
DynamicReadLockInner::Guard(guard) => guard.downcast_ref().unwrap(),
|
DynamicReadLockInner::Guard(guard) => guard.downcast_ref().expect(
|
||||||
|
"never fails because the read guard was created after checking the data type",
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -277,9 +283,10 @@ impl<'d, T: Any + Clone> Deref for DynamicWriteLock<'d, T> {
|
|||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
DynamicWriteLockInner::Reference(reference) => *reference,
|
DynamicWriteLockInner::Reference(reference) => *reference,
|
||||||
// Unwrapping is safe because all checking is already done in its constructor
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
DynamicWriteLockInner::Guard(guard) => guard.downcast_ref().unwrap(),
|
DynamicWriteLockInner::Guard(guard) => guard.downcast_ref().expect(
|
||||||
|
"never fails because the write guard was created after checking the data type",
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -289,16 +296,17 @@ impl<'d, T: Any + Clone> DerefMut for DynamicWriteLock<'d, T> {
|
|||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
match &mut self.0 {
|
match &mut self.0 {
|
||||||
DynamicWriteLockInner::Reference(reference) => *reference,
|
DynamicWriteLockInner::Reference(reference) => *reference,
|
||||||
// Unwrapping is safe because all checking is already done in its constructor
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
DynamicWriteLockInner::Guard(guard) => guard.downcast_mut().unwrap(),
|
DynamicWriteLockInner::Guard(guard) => guard.downcast_mut().expect(
|
||||||
|
"never fails because the write guard was created after checking the data type",
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dynamic {
|
impl Dynamic {
|
||||||
/// Get the arbitrary data attached to this [`Dynamic`].
|
/// Get the arbitrary data attached to this [`Dynamic`].
|
||||||
pub fn tag(&self) -> Tag {
|
pub const fn tag(&self) -> Tag {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Unit(_, tag, _)
|
Union::Unit(_, tag, _)
|
||||||
| Union::Bool(_, tag, _)
|
| Union::Bool(_, tag, _)
|
||||||
@ -350,7 +358,7 @@ impl Dynamic {
|
|||||||
/// Does this [`Dynamic`] hold a variant data type
|
/// Does this [`Dynamic`] hold a variant data type
|
||||||
/// instead of one of the supported system primitive types?
|
/// instead of one of the supported system primitive types?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_variant(&self) -> bool {
|
pub const fn is_variant(&self) -> bool {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Variant(_, _, _) => true,
|
Union::Variant(_, _, _) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
@ -361,7 +369,7 @@ impl Dynamic {
|
|||||||
/// Not available under `no_closure`.
|
/// Not available under `no_closure`.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_shared(&self) -> bool {
|
pub const fn is_shared(&self) -> bool {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Shared(_, _, _) => return true,
|
Union::Shared(_, _, _) => return true,
|
||||||
@ -409,7 +417,7 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Union::TimeStamp(_, _, _) => TypeId::of::<Instant>(),
|
Union::TimeStamp(_, _, _) => TypeId::of::<Instant>(),
|
||||||
|
|
||||||
Union::Variant(value, _, _) => (***value).type_id(),
|
Union::Variant(value, _, _) => value.as_ref().as_ref().type_id(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(cell, _, _) => {
|
Union::Shared(cell, _, _) => {
|
||||||
@ -447,7 +455,7 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Union::TimeStamp(_, _, _) => "timestamp",
|
Union::TimeStamp(_, _, _) => "timestamp",
|
||||||
|
|
||||||
Union::Variant(value, _, _) => (***value).type_name(),
|
Union::Variant(value, _, _) => value.as_ref().as_ref().type_name(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
@ -473,17 +481,12 @@ impl Hash for Dynamic {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Union::Float(f, _, _) => f.hash(state),
|
Union::Float(f, _, _) => f.hash(state),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Array(a, _, _) => (**a).hash(state),
|
Union::Array(a, _, _) => a.as_ref().hash(state),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(m, _, _) => {
|
Union::Map(m, _, _) => m.iter().for_each(|(key, value)| {
|
||||||
let mut buf: crate::StaticVec<_> = m.iter().collect();
|
key.hash(state);
|
||||||
buf.sort_by(|(a, _), (b, _)| a.cmp(b));
|
value.hash(state);
|
||||||
|
}),
|
||||||
buf.into_iter().for_each(|(key, value)| {
|
|
||||||
key.hash(state);
|
|
||||||
value.hash(state);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Union::FnPtr(f, _, _) if f.is_curried() => {
|
Union::FnPtr(f, _, _) if f.is_curried() => {
|
||||||
unimplemented!(
|
unimplemented!(
|
||||||
"{} with curried arguments cannot be hashed",
|
"{} with curried arguments cannot be hashed",
|
||||||
@ -566,43 +569,47 @@ impl fmt::Display for Dynamic {
|
|||||||
Union::TimeStamp(_, _, _) => f.write_str("<timestamp>"),
|
Union::TimeStamp(_, _, _) => f.write_str("<timestamp>"),
|
||||||
|
|
||||||
Union::Variant(value, _, _) => {
|
Union::Variant(value, _, _) => {
|
||||||
let _type_id = (***value).type_id();
|
let value = value.as_ref().as_ref();
|
||||||
|
let _type_id = value.type_id();
|
||||||
|
let _value_any = value.as_any();
|
||||||
|
|
||||||
|
const CHECKED: &str = "never fails because the type was checked";
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
if _type_id == TypeId::of::<u8>() {
|
if _type_id == TypeId::of::<u8>() {
|
||||||
return write!(f, "{}", (**value).as_any().downcast_ref::<u8>().unwrap());
|
return fmt::Display::fmt(_value_any.downcast_ref::<u8>().expect(CHECKED), f);
|
||||||
} else if _type_id == TypeId::of::<u16>() {
|
} else if _type_id == TypeId::of::<u16>() {
|
||||||
return write!(f, "{}", (**value).as_any().downcast_ref::<u16>().unwrap());
|
return fmt::Display::fmt(_value_any.downcast_ref::<u16>().expect(CHECKED), f);
|
||||||
} else if _type_id == TypeId::of::<u32>() {
|
} else if _type_id == TypeId::of::<u32>() {
|
||||||
return write!(f, "{}", (**value).as_any().downcast_ref::<u32>().unwrap());
|
return fmt::Display::fmt(_value_any.downcast_ref::<u32>().expect(CHECKED), f);
|
||||||
} else if _type_id == TypeId::of::<u64>() {
|
} else if _type_id == TypeId::of::<u64>() {
|
||||||
return write!(f, "{}", (**value).as_any().downcast_ref::<u64>().unwrap());
|
return fmt::Display::fmt(_value_any.downcast_ref::<u64>().expect(CHECKED), f);
|
||||||
} else if _type_id == TypeId::of::<i8>() {
|
} else if _type_id == TypeId::of::<i8>() {
|
||||||
return write!(f, "{}", (**value).as_any().downcast_ref::<i8>().unwrap());
|
return fmt::Display::fmt(_value_any.downcast_ref::<i8>().expect(CHECKED), f);
|
||||||
} else if _type_id == TypeId::of::<i16>() {
|
} else if _type_id == TypeId::of::<i16>() {
|
||||||
return write!(f, "{}", (**value).as_any().downcast_ref::<i16>().unwrap());
|
return fmt::Display::fmt(_value_any.downcast_ref::<i16>().expect(CHECKED), f);
|
||||||
} else if _type_id == TypeId::of::<i32>() {
|
} else if _type_id == TypeId::of::<i32>() {
|
||||||
return write!(f, "{}", (**value).as_any().downcast_ref::<i32>().unwrap());
|
return fmt::Display::fmt(_value_any.downcast_ref::<i32>().expect(CHECKED), f);
|
||||||
} else if _type_id == TypeId::of::<i64>() {
|
} else if _type_id == TypeId::of::<i64>() {
|
||||||
return write!(f, "{}", (**value).as_any().downcast_ref::<i64>().unwrap());
|
return fmt::Display::fmt(_value_any.downcast_ref::<i64>().expect(CHECKED), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
if _type_id == TypeId::of::<f32>() {
|
if _type_id == TypeId::of::<f32>() {
|
||||||
return write!(f, "{}", (**value).as_any().downcast_ref::<f32>().unwrap());
|
return fmt::Display::fmt(_value_any.downcast_ref::<f32>().expect(CHECKED), f);
|
||||||
} else if _type_id == TypeId::of::<f64>() {
|
} else if _type_id == TypeId::of::<f64>() {
|
||||||
return write!(f, "{}", (**value).as_any().downcast_ref::<f64>().unwrap());
|
return fmt::Display::fmt(_value_any.downcast_ref::<f64>().expect(CHECKED), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||||
if _type_id == TypeId::of::<u128>() {
|
if _type_id == TypeId::of::<u128>() {
|
||||||
return write!(f, "{}", (**value).as_any().downcast_ref::<u128>().unwrap());
|
return fmt::Display::fmt(_value_any.downcast_ref::<u128>().expect(CHECKED), f);
|
||||||
} else if _type_id == TypeId::of::<i128>() {
|
} else if _type_id == TypeId::of::<i128>() {
|
||||||
return write!(f, "{}", (**value).as_any().downcast_ref::<i128>().unwrap());
|
return fmt::Display::fmt(_value_any.downcast_ref::<i128>().expect(CHECKED), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
f.write_str((***value).type_name())
|
f.write_str(value.type_name())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
@ -645,51 +652,47 @@ impl fmt::Debug for Dynamic {
|
|||||||
Union::TimeStamp(_, _, _) => write!(f, "<timestamp>"),
|
Union::TimeStamp(_, _, _) => write!(f, "<timestamp>"),
|
||||||
|
|
||||||
Union::Variant(value, _, _) => {
|
Union::Variant(value, _, _) => {
|
||||||
let _type_id = (***value).type_id();
|
let value = value.as_ref().as_ref();
|
||||||
|
let _type_id = value.type_id();
|
||||||
|
let _value_any = value.as_any();
|
||||||
|
|
||||||
|
const CHECKED: &str = "never fails because the type was checked";
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
if _type_id == TypeId::of::<u8>() {
|
if _type_id == TypeId::of::<u8>() {
|
||||||
return write!(f, "{:?}", (**value).as_any().downcast_ref::<u8>().unwrap());
|
return fmt::Debug::fmt(_value_any.downcast_ref::<u8>().expect(CHECKED), f);
|
||||||
} else if _type_id == TypeId::of::<u16>() {
|
} else if _type_id == TypeId::of::<u16>() {
|
||||||
return write!(f, "{:?}", (**value).as_any().downcast_ref::<u16>().unwrap());
|
return fmt::Debug::fmt(_value_any.downcast_ref::<u16>().expect(CHECKED), f);
|
||||||
} else if _type_id == TypeId::of::<u32>() {
|
} else if _type_id == TypeId::of::<u32>() {
|
||||||
return write!(f, "{:?}", (**value).as_any().downcast_ref::<u32>().unwrap());
|
return fmt::Debug::fmt(_value_any.downcast_ref::<u32>().expect(CHECKED), f);
|
||||||
} else if _type_id == TypeId::of::<u64>() {
|
} else if _type_id == TypeId::of::<u64>() {
|
||||||
return write!(f, "{:?}", (**value).as_any().downcast_ref::<u64>().unwrap());
|
return fmt::Debug::fmt(_value_any.downcast_ref::<u64>().expect(CHECKED), f);
|
||||||
} else if _type_id == TypeId::of::<i8>() {
|
} else if _type_id == TypeId::of::<i8>() {
|
||||||
return write!(f, "{:?}", (**value).as_any().downcast_ref::<i8>().unwrap());
|
return fmt::Debug::fmt(_value_any.downcast_ref::<i8>().expect(CHECKED), f);
|
||||||
} else if _type_id == TypeId::of::<i16>() {
|
} else if _type_id == TypeId::of::<i16>() {
|
||||||
return write!(f, "{:?}", (**value).as_any().downcast_ref::<i16>().unwrap());
|
return fmt::Debug::fmt(_value_any.downcast_ref::<i16>().expect(CHECKED), f);
|
||||||
} else if _type_id == TypeId::of::<i32>() {
|
} else if _type_id == TypeId::of::<i32>() {
|
||||||
return write!(f, "{:?}", (**value).as_any().downcast_ref::<i32>().unwrap());
|
return fmt::Debug::fmt(_value_any.downcast_ref::<i32>().expect(CHECKED), f);
|
||||||
} else if _type_id == TypeId::of::<i64>() {
|
} else if _type_id == TypeId::of::<i64>() {
|
||||||
return write!(f, "{:?}", (**value).as_any().downcast_ref::<i64>().unwrap());
|
return fmt::Debug::fmt(_value_any.downcast_ref::<i64>().expect(CHECKED), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
if _type_id == TypeId::of::<f32>() {
|
if _type_id == TypeId::of::<f32>() {
|
||||||
return write!(f, "{}", (**value).as_any().downcast_ref::<f32>().unwrap());
|
return fmt::Debug::fmt(_value_any.downcast_ref::<f32>().expect(CHECKED), f);
|
||||||
} else if _type_id == TypeId::of::<f64>() {
|
} else if _type_id == TypeId::of::<f64>() {
|
||||||
return write!(f, "{}", (**value).as_any().downcast_ref::<f64>().unwrap());
|
return fmt::Debug::fmt(_value_any.downcast_ref::<f64>().expect(CHECKED), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||||
if _type_id == TypeId::of::<u128>() {
|
if _type_id == TypeId::of::<u128>() {
|
||||||
return write!(
|
return fmt::Debug::fmt(_value_any.downcast_ref::<u128>().expect(CHECKED), f);
|
||||||
f,
|
|
||||||
"{:?}",
|
|
||||||
(**value).as_any().downcast_ref::<u128>().unwrap()
|
|
||||||
);
|
|
||||||
} else if _type_id == TypeId::of::<i128>() {
|
} else if _type_id == TypeId::of::<i128>() {
|
||||||
return write!(
|
return fmt::Debug::fmt(_value_any.downcast_ref::<i128>().expect(CHECKED), f);
|
||||||
f,
|
|
||||||
"{:?}",
|
|
||||||
(**value).as_any().downcast_ref::<i128>().unwrap()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(f, "{}", (*value).type_name())
|
write!(f, "{}", value.type_name())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
@ -746,7 +749,7 @@ impl Clone for Dynamic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Union::Variant(ref value, tag, _) => {
|
Union::Variant(ref value, tag, _) => {
|
||||||
let mut x = (***value).clone_into_dynamic();
|
let mut x = value.as_ref().as_ref().clone_into_dynamic();
|
||||||
x.set_tag(tag);
|
x.set_tag(tag);
|
||||||
x
|
x
|
||||||
}
|
}
|
||||||
@ -830,7 +833,7 @@ impl Dynamic {
|
|||||||
));
|
));
|
||||||
|
|
||||||
/// Get the [`AccessMode`] for this [`Dynamic`].
|
/// Get the [`AccessMode`] for this [`Dynamic`].
|
||||||
pub(crate) fn access_mode(&self) -> AccessMode {
|
pub(crate) const fn access_mode(&self) -> AccessMode {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Unit(_, _, access)
|
Union::Unit(_, _, access)
|
||||||
| Union::Bool(_, _, access)
|
| Union::Bool(_, _, access)
|
||||||
@ -980,53 +983,40 @@ impl Dynamic {
|
|||||||
pub fn from<T: Variant + Clone>(mut value: T) -> Self {
|
pub fn from<T: Variant + Clone>(mut value: T) -> Self {
|
||||||
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
||||||
|
|
||||||
|
const CHECKED: &str = "never fails because the type was checked";
|
||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
return unsafe_try_cast::<_, Dynamic>(value).ok().unwrap();
|
return unsafe_try_cast::<_, Dynamic>(value).ok().expect(CHECKED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let val = value.as_any();
|
||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||||
return <dyn Any>::downcast_ref::<INT>(&value)
|
return val.downcast_ref::<INT>().expect(CHECKED).clone().into();
|
||||||
.unwrap()
|
|
||||||
.clone()
|
|
||||||
.into();
|
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
||||||
return <dyn Any>::downcast_ref::<FLOAT>(&value)
|
return val.downcast_ref::<FLOAT>().expect(CHECKED).clone().into();
|
||||||
.unwrap()
|
|
||||||
.clone()
|
|
||||||
.into();
|
|
||||||
}
|
}
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Decimal>() {
|
if TypeId::of::<T>() == TypeId::of::<Decimal>() {
|
||||||
return <dyn Any>::downcast_ref::<Decimal>(&value)
|
return val.downcast_ref::<Decimal>().expect(CHECKED).clone().into();
|
||||||
.unwrap()
|
|
||||||
.clone()
|
|
||||||
.into();
|
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||||
return <dyn Any>::downcast_ref::<bool>(&value)
|
return val.downcast_ref::<bool>().expect(CHECKED).clone().into();
|
||||||
.unwrap()
|
|
||||||
.clone()
|
|
||||||
.into();
|
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<char>() {
|
if TypeId::of::<T>() == TypeId::of::<char>() {
|
||||||
return <dyn Any>::downcast_ref::<char>(&value)
|
return val.downcast_ref::<char>().expect(CHECKED).clone().into();
|
||||||
.unwrap()
|
|
||||||
.clone()
|
|
||||||
.into();
|
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
||||||
return <dyn Any>::downcast_ref::<ImmutableString>(&value)
|
return val
|
||||||
.unwrap()
|
.downcast_ref::<ImmutableString>()
|
||||||
|
.expect(CHECKED)
|
||||||
.clone()
|
.clone()
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<&str>() {
|
if TypeId::of::<T>() == TypeId::of::<&str>() {
|
||||||
return <dyn Any>::downcast_ref::<&str>(&value)
|
return val.downcast_ref::<&str>().expect(CHECKED).deref().into();
|
||||||
.unwrap()
|
|
||||||
.deref()
|
|
||||||
.into();
|
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<()>() {
|
if TypeId::of::<T>() == TypeId::of::<()>() {
|
||||||
return ().into();
|
return ().into();
|
||||||
@ -1450,9 +1440,9 @@ impl Dynamic {
|
|||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
let value = cell.read().unwrap();
|
let value = cell.read().unwrap();
|
||||||
|
|
||||||
let type_id = (*value).type_id();
|
if (*value).type_id() != TypeId::of::<T>()
|
||||||
|
&& TypeId::of::<Dynamic>() != TypeId::of::<T>()
|
||||||
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
|
{
|
||||||
return None;
|
return None;
|
||||||
} else {
|
} else {
|
||||||
return Some(DynamicReadLock(DynamicReadLockInner::Guard(value)));
|
return Some(DynamicReadLock(DynamicReadLockInner::Guard(value)));
|
||||||
@ -1483,9 +1473,9 @@ impl Dynamic {
|
|||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
let value = cell.write().unwrap();
|
let value = cell.write().unwrap();
|
||||||
|
|
||||||
let type_id = (*value).type_id();
|
if (*value).type_id() != TypeId::of::<T>()
|
||||||
|
&& TypeId::of::<Dynamic>() != TypeId::of::<T>()
|
||||||
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
|
{
|
||||||
return None;
|
return None;
|
||||||
} else {
|
} else {
|
||||||
return Some(DynamicWriteLock(DynamicWriteLockInner::Guard(value)));
|
return Some(DynamicWriteLock(DynamicWriteLockInner::Guard(value)));
|
||||||
@ -1507,77 +1497,77 @@ impl Dynamic {
|
|||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Int(value, _, _) => <dyn Any>::downcast_ref::<T>(value),
|
Union::Int(value, _, _) => value.as_any().downcast_ref::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Float(value, _, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
Union::Float(value, _, _) => value.as_ref().as_any().downcast_ref::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Decimal>() {
|
if TypeId::of::<T>() == TypeId::of::<Decimal>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Decimal(value, _, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
Union::Decimal(value, _, _) => value.as_ref().as_any().downcast_ref::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Bool(value, _, _) => <dyn Any>::downcast_ref::<T>(value),
|
Union::Bool(value, _, _) => value.as_any().downcast_ref::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Str(value, _, _) => <dyn Any>::downcast_ref::<T>(value),
|
Union::Str(value, _, _) => value.as_any().downcast_ref::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<char>() {
|
if TypeId::of::<T>() == TypeId::of::<char>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Char(value, _, _) => <dyn Any>::downcast_ref::<T>(value),
|
Union::Char(value, _, _) => value.as_any().downcast_ref::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Array(value, _, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
Union::Array(value, _, _) => value.as_ref().as_any().downcast_ref::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Map(value, _, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
Union::Map(value, _, _) => value.as_ref().as_any().downcast_ref::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::FnPtr(value, _, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
Union::FnPtr(value, _, _) => value.as_ref().as_any().downcast_ref::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::TimeStamp(value, _, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
Union::TimeStamp(value, _, _) => value.as_ref().as_any().downcast_ref::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<()>() {
|
if TypeId::of::<T>() == TypeId::of::<()>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Unit(value, _, _) => <dyn Any>::downcast_ref::<T>(value),
|
Union::Unit(value, _, _) => value.as_any().downcast_ref::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
return <dyn Any>::downcast_ref::<T>(self);
|
return self.as_any().downcast_ref::<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
@ -1597,77 +1587,77 @@ impl Dynamic {
|
|||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::Int(value, _, _) => <dyn Any>::downcast_mut::<T>(value),
|
Union::Int(value, _, _) => value.as_mut_any().downcast_mut::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::Float(value, _, _) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
Union::Float(value, _, _) => value.as_mut().as_mut_any().downcast_mut::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Decimal>() {
|
if TypeId::of::<T>() == TypeId::of::<Decimal>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::Decimal(value, _, _) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
Union::Decimal(value, _, _) => value.as_mut().as_mut_any().downcast_mut::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::Bool(value, _, _) => <dyn Any>::downcast_mut::<T>(value),
|
Union::Bool(value, _, _) => value.as_mut_any().downcast_mut::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::Str(value, _, _) => <dyn Any>::downcast_mut::<T>(value),
|
Union::Str(value, _, _) => value.as_mut_any().downcast_mut::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<char>() {
|
if TypeId::of::<T>() == TypeId::of::<char>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::Char(value, _, _) => <dyn Any>::downcast_mut::<T>(value),
|
Union::Char(value, _, _) => value.as_mut_any().downcast_mut::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::Array(value, _, _) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
Union::Array(value, _, _) => value.as_mut().as_mut_any().downcast_mut::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::Map(value, _, _) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
Union::Map(value, _, _) => value.as_mut().as_mut_any().downcast_mut::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::FnPtr(value, _, _) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
Union::FnPtr(value, _, _) => value.as_mut().as_mut_any().downcast_mut::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::TimeStamp(value, _, _) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
Union::TimeStamp(value, _, _) => value.as_mut().as_mut_any().downcast_mut::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<()>() {
|
if TypeId::of::<T>() == TypeId::of::<()>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::Unit(value, _, _) => <dyn Any>::downcast_mut::<T>(value),
|
Union::Unit(value, _, _) => value.as_mut_any().downcast_mut::<T>(),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
return <dyn Any>::downcast_mut::<T>(self);
|
return self.as_mut_any().downcast_mut::<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
match &mut self.0 {
|
match &mut self.0 {
|
||||||
@ -1720,8 +1710,8 @@ impl Dynamic {
|
|||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_decimal(&self) -> Result<Decimal, &'static str> {
|
pub fn as_decimal(&self) -> Result<Decimal, &'static str> {
|
||||||
match &self.0 {
|
match self.0 {
|
||||||
Union::Decimal(n, _, _) => Ok(**n),
|
Union::Decimal(ref n, _, _) => Ok(**n),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_, _, _) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
Union::Shared(_, _, _) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
@ -1757,8 +1747,8 @@ impl Dynamic {
|
|||||||
/// Panics if the value is shared.
|
/// Panics if the value is shared.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn as_str_ref(&self) -> Result<&str, &'static str> {
|
pub(crate) fn as_str_ref(&self) -> Result<&str, &'static str> {
|
||||||
match &self.0 {
|
match self.0 {
|
||||||
Union::Str(s, _, _) => Ok(s),
|
Union::Str(ref s, _, _) => Ok(s),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_, _, _) => panic!("as_str() cannot be called on shared values"),
|
Union::Shared(_, _, _) => panic!("as_str() cannot be called on shared values"),
|
||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
|
485
src/engine.rs
485
src/engine.rs
@ -29,7 +29,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::{calc_fn_hash, Array};
|
use crate::Array;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
@ -224,9 +224,9 @@ pub const KEYWORD_GLOBAL: &str = "global";
|
|||||||
pub const FN_GET: &str = "get$";
|
pub const FN_GET: &str = "get$";
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
pub const FN_SET: &str = "set$";
|
pub const FN_SET: &str = "set$";
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
pub const FN_IDX_GET: &str = "index$get$";
|
pub const FN_IDX_GET: &str = "index$get$";
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
pub const FN_IDX_SET: &str = "index$set$";
|
pub const FN_IDX_SET: &str = "index$set$";
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub const FN_ANONYMOUS: &str = "anon$";
|
pub const FN_ANONYMOUS: &str = "anon$";
|
||||||
@ -429,15 +429,15 @@ impl<'a> Target<'a> {
|
|||||||
/// This has no effect except for string indexing.
|
/// This has no effect except for string indexing.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn propagate_changed_value(&mut self) {
|
pub fn propagate_changed_value(&mut self) -> Result<(), Box<EvalAltResult>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Ref(_) | Self::Value(_) => (),
|
Self::Ref(_) | Self::Value(_) => Ok(()),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::LockGuard(_) => (),
|
Self::LockGuard(_) => Ok(()),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::StringChar(_, _, ch) => {
|
Self::StringChar(_, _, ch) => {
|
||||||
let char_value = ch.clone();
|
let char_value = ch.clone();
|
||||||
self.set_value(char_value, Position::NONE).unwrap();
|
self.set_value(char_value, Position::NONE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -455,7 +455,9 @@ impl<'a> Target<'a> {
|
|||||||
Self::Value(_) => panic!("cannot update a value"),
|
Self::Value(_) => panic!("cannot update a value"),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::StringChar(s, index, _) => {
|
Self::StringChar(s, index, _) => {
|
||||||
let s = &mut *s.write_lock::<ImmutableString>().unwrap();
|
let s = &mut *s
|
||||||
|
.write_lock::<ImmutableString>()
|
||||||
|
.expect("never fails because `StringChar` always holds an `ImmutableString`");
|
||||||
|
|
||||||
// Replace the character at the specified index position
|
// Replace the character at the specified index position
|
||||||
let new_ch = new_val.as_char().map_err(|err| {
|
let new_ch = new_val.as_char().map_err(|err| {
|
||||||
@ -488,7 +490,12 @@ impl<'a> From<&'a mut Dynamic> for Target<'a> {
|
|||||||
if value.is_shared() {
|
if value.is_shared() {
|
||||||
// Cloning is cheap for a shared value
|
// Cloning is cheap for a shared value
|
||||||
let container = value.clone();
|
let container = value.clone();
|
||||||
return Self::LockGuard((value.write_lock::<Dynamic>().unwrap(), container));
|
return Self::LockGuard((
|
||||||
|
value
|
||||||
|
.write_lock::<Dynamic>()
|
||||||
|
.expect("never fails when casting to `Dynamic`"),
|
||||||
|
container,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::Ref(value)
|
Self::Ref(value)
|
||||||
@ -598,9 +605,12 @@ impl State {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache {
|
pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache {
|
||||||
if self.fn_resolution_caches.0.is_empty() {
|
if self.fn_resolution_caches.0.is_empty() {
|
||||||
|
// Push a new function resolution cache if the stack is empty
|
||||||
self.fn_resolution_caches.0.push(BTreeMap::new());
|
self.fn_resolution_caches.0.push(BTreeMap::new());
|
||||||
}
|
}
|
||||||
self.fn_resolution_caches.0.last_mut().unwrap()
|
self.fn_resolution_caches.0.last_mut().expect(
|
||||||
|
"never fails because there is at least one function resolution cache by this point",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
/// Push an empty function resolution cache onto the stack and make it current.
|
/// Push an empty function resolution cache onto the stack and make it current.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -617,7 +627,11 @@ impl State {
|
|||||||
/// Panics if there are no more function resolution cache in the stack.
|
/// Panics if there are no more function resolution cache in the stack.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn pop_fn_resolution_cache(&mut self) {
|
pub fn pop_fn_resolution_cache(&mut self) {
|
||||||
let mut cache = self.fn_resolution_caches.0.pop().unwrap();
|
let mut cache = self
|
||||||
|
.fn_resolution_caches
|
||||||
|
.0
|
||||||
|
.pop()
|
||||||
|
.expect("there should be at least one function resolution cache");
|
||||||
cache.clear();
|
cache.clear();
|
||||||
self.fn_resolution_caches.1.push(cache);
|
self.fn_resolution_caches.1.push(cache);
|
||||||
}
|
}
|
||||||
@ -899,11 +913,7 @@ impl Engine {
|
|||||||
progress: None,
|
progress: None,
|
||||||
|
|
||||||
// optimization level
|
// optimization level
|
||||||
optimization_level: if cfg!(feature = "no_optimize") {
|
optimization_level: Default::default(),
|
||||||
OptimizationLevel::None
|
|
||||||
} else {
|
|
||||||
OptimizationLevel::Simple
|
|
||||||
},
|
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
limits: Limits {
|
limits: Limits {
|
||||||
@ -956,11 +966,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
progress: None,
|
progress: None,
|
||||||
|
|
||||||
optimization_level: if cfg!(feature = "no_optimize") {
|
optimization_level: Default::default(),
|
||||||
OptimizationLevel::None
|
|
||||||
} else {
|
|
||||||
OptimizationLevel::Simple
|
|
||||||
},
|
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
limits: Limits {
|
limits: Limits {
|
||||||
@ -1004,10 +1010,16 @@ impl Engine {
|
|||||||
|
|
||||||
if let Some(index) = index {
|
if let Some(index) = index {
|
||||||
let offset = mods.len() - index.get();
|
let offset = mods.len() - index.get();
|
||||||
Some(mods.get(offset).expect("invalid index in Imports"))
|
Some(
|
||||||
|
mods.get(offset)
|
||||||
|
.expect("never fails because offset should be within range"),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
mods.find(root)
|
mods.find(root)
|
||||||
.map(|n| mods.get(n).expect("invalid index in Imports"))
|
.map(|n| {
|
||||||
|
mods.get(n)
|
||||||
|
.expect("never fails because the index came from `find`")
|
||||||
|
})
|
||||||
.or_else(|| self.global_sub_modules.get(root).cloned())
|
.or_else(|| self.global_sub_modules.get(root).cloned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1059,6 +1071,10 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Search for a variable within the scope
|
/// Search for a variable within the scope
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `expr` is not [`Expr::Variable`].
|
||||||
pub(crate) fn search_scope_only<'s>(
|
pub(crate) fn search_scope_only<'s>(
|
||||||
&self,
|
&self,
|
||||||
scope: &'s mut Scope,
|
scope: &'s mut Scope,
|
||||||
@ -1096,9 +1112,13 @@ impl Engine {
|
|||||||
this_ptr,
|
this_ptr,
|
||||||
level: 0,
|
level: 0,
|
||||||
};
|
};
|
||||||
if let Some(mut result) =
|
if let Some(mut result) = resolve_var(
|
||||||
resolve_var(expr.get_variable_name(true).unwrap(), index, &context)
|
expr.get_variable_name(true)
|
||||||
.map_err(|err| err.fill_position(var_pos))?
|
.expect("`expr` should be `Variable`"),
|
||||||
|
index,
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.map_err(|err| err.fill_position(var_pos))?
|
||||||
{
|
{
|
||||||
result.set_access_mode(AccessMode::ReadOnly);
|
result.set_access_mode(AccessMode::ReadOnly);
|
||||||
return Ok((result.into(), var_pos));
|
return Ok((result.into(), var_pos));
|
||||||
@ -1109,7 +1129,9 @@ impl Engine {
|
|||||||
scope.len() - index
|
scope.len() - index
|
||||||
} else {
|
} else {
|
||||||
// Find the variable in the scope
|
// Find the variable in the scope
|
||||||
let var_name = expr.get_variable_name(true).unwrap();
|
let var_name = expr
|
||||||
|
.get_variable_name(true)
|
||||||
|
.expect("`expr` should be `Variable`");
|
||||||
scope
|
scope
|
||||||
.get_index(var_name)
|
.get_index(var_name)
|
||||||
.ok_or_else(|| EvalAltResult::ErrorVariableNotFound(var_name.to_string(), var_pos))?
|
.ok_or_else(|| EvalAltResult::ErrorVariableNotFound(var_name.to_string(), var_pos))?
|
||||||
@ -1138,18 +1160,22 @@ impl Engine {
|
|||||||
level: usize,
|
level: usize,
|
||||||
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
|
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
|
fn match_chain_type(expr: &Expr) -> ChainType {
|
||||||
|
match expr {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Expr::Index(_, _) => ChainType::Index,
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
Expr::Dot(_, _) => ChainType::Dot,
|
||||||
|
_ => unreachable!("`expr` should only be `Index` or `Dot`, but got {:?}", expr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let is_ref = target.is_ref();
|
let is_ref = target.is_ref();
|
||||||
|
|
||||||
let rhs_chain = match rhs {
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(_, _) => Some(ChainType::Index),
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Expr::Dot(_, _) => Some(ChainType::Dot),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Pop the last index value
|
// Pop the last index value
|
||||||
let idx_val = idx_values.pop().unwrap();
|
let idx_val = idx_values
|
||||||
|
.pop()
|
||||||
|
.expect("never fails because an index chain is never empty");
|
||||||
|
|
||||||
match chain_type {
|
match chain_type {
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -1162,9 +1188,9 @@ impl Engine {
|
|||||||
let idx_pos = x.lhs.position();
|
let idx_pos = x.lhs.position();
|
||||||
let idx_val = idx_val.as_index_value();
|
let idx_val = idx_val.as_index_value();
|
||||||
let obj_ptr = &mut self.get_indexed_mut(
|
let obj_ptr = &mut self.get_indexed_mut(
|
||||||
mods, state, lib, target, idx_val, idx_pos, false, is_ref, true, level,
|
mods, state, lib, target, idx_val, idx_pos, false, true, level,
|
||||||
)?;
|
)?;
|
||||||
let rhs_chain = rhs_chain.unwrap();
|
let rhs_chain = match_chain_type(rhs);
|
||||||
|
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, idx_values,
|
mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, idx_values,
|
||||||
@ -1174,69 +1200,48 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// xxx[rhs] op= new_val
|
// xxx[rhs] op= new_val
|
||||||
_ if new_val.is_some() => {
|
_ if new_val.is_some() => {
|
||||||
|
let ((mut new_val, new_pos), (op_info, op_pos)) =
|
||||||
|
new_val.expect("never fails because `new_val` is `Some`");
|
||||||
let idx_val = idx_val.as_index_value();
|
let idx_val = idx_val.as_index_value();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
let mut idx_val2 = idx_val.clone();
|
let mut idx_val_for_setter = idx_val.clone();
|
||||||
|
|
||||||
// `call_setter` is introduced to bypass double mutable borrowing of target
|
match self.get_indexed_mut(
|
||||||
let _call_setter = match self.get_indexed_mut(
|
mods, state, lib, target, idx_val, pos, true, false, level,
|
||||||
mods, state, lib, target, idx_val, pos, true, is_ref, false, level,
|
|
||||||
) {
|
) {
|
||||||
// Indexed value is a reference - update directly
|
// Indexed value is a reference - update directly
|
||||||
Ok(obj_ptr) => {
|
Ok(obj_ptr) => {
|
||||||
let ((new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
|
|
||||||
self.eval_op_assignment(
|
self.eval_op_assignment(
|
||||||
mods, state, lib, op_info, op_pos, obj_ptr, root, new_val,
|
mods, state, lib, op_info, op_pos, obj_ptr, root, new_val,
|
||||||
new_pos,
|
new_pos,
|
||||||
)?;
|
)?;
|
||||||
None
|
return Ok((Dynamic::UNIT, true));
|
||||||
}
|
}
|
||||||
Err(err) => match *err {
|
// Can't index - try to call an index setter
|
||||||
// No index getter - try to call an index setter
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[cfg(not(feature = "no_index"))]
|
Err(err) if matches!(*err, EvalAltResult::ErrorIndexingType(_, _)) => {}
|
||||||
EvalAltResult::ErrorIndexingType(_, _) => Some(new_val.unwrap()),
|
// Any other error
|
||||||
// Any other error - return
|
Err(err) => return Err(err),
|
||||||
err => return err.into(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
if let Some(mut new_val) = _call_setter {
|
|
||||||
let val_type_name = target.type_name();
|
|
||||||
let ((_, val_pos), _) = new_val;
|
|
||||||
|
|
||||||
let hash_set = FnCallHashes::from_native(calc_fn_hash(
|
|
||||||
std::iter::empty(),
|
|
||||||
FN_IDX_SET,
|
|
||||||
3,
|
|
||||||
));
|
|
||||||
let args = &mut [target, &mut idx_val2, &mut (new_val.0).0];
|
|
||||||
|
|
||||||
self.exec_fn_call(
|
|
||||||
mods, state, lib, FN_IDX_SET, hash_set, args, is_ref, true,
|
|
||||||
val_pos, None, level,
|
|
||||||
)
|
|
||||||
.map_err(|err| match *err {
|
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
|
||||||
if fn_sig.ends_with("]=") =>
|
|
||||||
{
|
|
||||||
EvalAltResult::ErrorIndexingType(
|
|
||||||
self.map_type_name(val_type_name).into(),
|
|
||||||
Position::NONE,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
err => err,
|
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to call index setter
|
||||||
|
let hash_set =
|
||||||
|
FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_SET, 3));
|
||||||
|
let args = &mut [target, &mut idx_val_for_setter, &mut new_val];
|
||||||
|
|
||||||
|
self.exec_fn_call(
|
||||||
|
mods, state, lib, FN_IDX_SET, hash_set, args, is_ref, true, new_pos,
|
||||||
|
None, level,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok((Dynamic::UNIT, true))
|
Ok((Dynamic::UNIT, true))
|
||||||
}
|
}
|
||||||
// xxx[rhs]
|
// xxx[rhs]
|
||||||
_ => {
|
_ => {
|
||||||
let idx_val = idx_val.as_index_value();
|
let idx_val = idx_val.as_index_value();
|
||||||
self.get_indexed_mut(
|
self.get_indexed_mut(
|
||||||
mods, state, lib, target, idx_val, pos, false, is_ref, true, level,
|
mods, state, lib, target, idx_val, pos, false, true, level,
|
||||||
)
|
)
|
||||||
.map(|v| (v.take_or_clone(), false))
|
.map(|v| (v.take_or_clone(), false))
|
||||||
}
|
}
|
||||||
@ -1264,12 +1269,13 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// {xxx:map}.id op= ???
|
// {xxx:map}.id op= ???
|
||||||
Expr::Property(x) if target.is::<Map>() && new_val.is_some() => {
|
Expr::Property(x) if target.is::<Map>() && new_val.is_some() => {
|
||||||
let Ident { name, pos, .. } = &x.2;
|
let (name, pos) = &x.2;
|
||||||
let index = name.into();
|
let index = name.into();
|
||||||
let val = self.get_indexed_mut(
|
let val = self.get_indexed_mut(
|
||||||
mods, state, lib, target, index, *pos, true, is_ref, false, level,
|
mods, state, lib, target, index, *pos, true, false, level,
|
||||||
)?;
|
)?;
|
||||||
let ((new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
|
let ((new_val, new_pos), (op_info, op_pos)) =
|
||||||
|
new_val.expect("never fails because `new_val` is `Some`");
|
||||||
self.eval_op_assignment(
|
self.eval_op_assignment(
|
||||||
mods, state, lib, op_info, op_pos, val, root, new_val, new_pos,
|
mods, state, lib, op_info, op_pos, val, root, new_val, new_pos,
|
||||||
)?;
|
)?;
|
||||||
@ -1277,27 +1283,46 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// {xxx:map}.id
|
// {xxx:map}.id
|
||||||
Expr::Property(x) if target.is::<Map>() => {
|
Expr::Property(x) if target.is::<Map>() => {
|
||||||
let Ident { name, pos, .. } = &x.2;
|
let (name, pos) = &x.2;
|
||||||
let index = name.into();
|
let index = name.into();
|
||||||
let val = self.get_indexed_mut(
|
let val = self.get_indexed_mut(
|
||||||
mods, state, lib, target, index, *pos, false, is_ref, false, level,
|
mods, state, lib, target, index, *pos, false, false, level,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok((val.take_or_clone(), false))
|
Ok((val.take_or_clone(), false))
|
||||||
}
|
}
|
||||||
// xxx.id op= ???
|
// xxx.id op= ???
|
||||||
Expr::Property(x) if new_val.is_some() => {
|
Expr::Property(x) if new_val.is_some() => {
|
||||||
let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) =
|
let ((getter, hash_get), (setter, hash_set), (name, pos)) = x.as_ref();
|
||||||
x.as_ref();
|
let ((mut new_val, new_pos), (op_info, op_pos)) =
|
||||||
let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
|
new_val.expect("never fails because `new_val` is `Some`");
|
||||||
|
|
||||||
if op_info.is_some() {
|
if op_info.is_some() {
|
||||||
let hash = FnCallHashes::from_native(*hash_get);
|
let hash = FnCallHashes::from_native(*hash_get);
|
||||||
let mut args = [target.as_mut()];
|
let mut args = [target.as_mut()];
|
||||||
let (mut orig_val, _) = self.exec_fn_call(
|
let (mut orig_val, _) = self
|
||||||
mods, state, lib, getter, hash, &mut args, is_ref, true, *pos,
|
.exec_fn_call(
|
||||||
None, level,
|
mods, state, lib, getter, hash, &mut args, is_ref, true, *pos,
|
||||||
)?;
|
None, level,
|
||||||
|
)
|
||||||
|
.or_else(|err| match *err {
|
||||||
|
// Try an indexer if property does not exist
|
||||||
|
EvalAltResult::ErrorDotExpr(_, _) => {
|
||||||
|
let prop = name.into();
|
||||||
|
self.get_indexed_mut(
|
||||||
|
mods, state, lib, target, prop, *pos, false, true,
|
||||||
|
level,
|
||||||
|
)
|
||||||
|
.map(|v| (v.take_or_clone(), false))
|
||||||
|
.map_err(
|
||||||
|
|idx_err| match *idx_err {
|
||||||
|
EvalAltResult::ErrorIndexingType(_, _) => err,
|
||||||
|
_ => idx_err,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => Err(err),
|
||||||
|
})?;
|
||||||
let obj_ptr = (&mut orig_val).into();
|
let obj_ptr = (&mut orig_val).into();
|
||||||
self.eval_op_assignment(
|
self.eval_op_assignment(
|
||||||
mods, state, lib, op_info, op_pos, obj_ptr, root, new_val, new_pos,
|
mods, state, lib, op_info, op_pos, obj_ptr, root, new_val, new_pos,
|
||||||
@ -1311,28 +1336,65 @@ impl Engine {
|
|||||||
mods, state, lib, setter, hash, &mut args, is_ref, true, *pos, None,
|
mods, state, lib, setter, hash, &mut args, is_ref, true, *pos, None,
|
||||||
level,
|
level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| (v, true))
|
.or_else(|err| match *err {
|
||||||
|
// Try an indexer if property does not exist
|
||||||
|
EvalAltResult::ErrorDotExpr(_, _) => {
|
||||||
|
let mut prop = name.into();
|
||||||
|
let args = &mut [target, &mut prop, &mut new_val];
|
||||||
|
let hash_set =
|
||||||
|
FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_SET, 3));
|
||||||
|
self.exec_fn_call(
|
||||||
|
mods, state, lib, FN_IDX_SET, hash_set, args, is_ref, true,
|
||||||
|
*pos, None, level,
|
||||||
|
)
|
||||||
|
.map_err(
|
||||||
|
|idx_err| match *idx_err {
|
||||||
|
EvalAltResult::ErrorIndexingType(_, _) => err,
|
||||||
|
_ => idx_err,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => Err(err),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
// xxx.id
|
// xxx.id
|
||||||
Expr::Property(x) => {
|
Expr::Property(x) => {
|
||||||
let ((getter, hash_get), _, Ident { pos, .. }) = x.as_ref();
|
let ((getter, hash_get), _, (name, pos)) = x.as_ref();
|
||||||
let hash = FnCallHashes::from_native(*hash_get);
|
let hash = FnCallHashes::from_native(*hash_get);
|
||||||
let mut args = [target.as_mut()];
|
let mut args = [target.as_mut()];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, None,
|
mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, None,
|
||||||
level,
|
level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| (v, false))
|
.map_or_else(
|
||||||
|
|err| match *err {
|
||||||
|
// Try an indexer if property does not exist
|
||||||
|
EvalAltResult::ErrorDotExpr(_, _) => {
|
||||||
|
let prop = name.into();
|
||||||
|
self.get_indexed_mut(
|
||||||
|
mods, state, lib, target, prop, *pos, false, true, level,
|
||||||
|
)
|
||||||
|
.map(|v| (v.take_or_clone(), false))
|
||||||
|
.map_err(|idx_err| {
|
||||||
|
match *idx_err {
|
||||||
|
EvalAltResult::ErrorIndexingType(_, _) => err,
|
||||||
|
_ => idx_err,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Err(err),
|
||||||
|
},
|
||||||
|
|(v, _)| Ok((v, false)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
// {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr
|
// {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr
|
||||||
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::<Map>() => {
|
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::<Map>() => {
|
||||||
let mut val = match &x.lhs {
|
let mut val = match &x.lhs {
|
||||||
Expr::Property(p) => {
|
Expr::Property(p) => {
|
||||||
let Ident { name, pos, .. } = &p.2;
|
let (name, pos) = &p.2;
|
||||||
let index = name.into();
|
let index = name.into();
|
||||||
self.get_indexed_mut(
|
self.get_indexed_mut(
|
||||||
mods, state, lib, target, index, *pos, false, is_ref, true,
|
mods, state, lib, target, index, *pos, false, true, level,
|
||||||
level,
|
|
||||||
)?
|
)?
|
||||||
}
|
}
|
||||||
// {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr
|
// {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr
|
||||||
@ -1351,7 +1413,7 @@ impl Engine {
|
|||||||
// Others - syntax error
|
// Others - syntax error
|
||||||
expr => unreachable!("invalid dot expression: {:?}", expr),
|
expr => unreachable!("invalid dot expression: {:?}", expr),
|
||||||
};
|
};
|
||||||
let rhs_chain = rhs_chain.unwrap();
|
let rhs_chain = match_chain_type(rhs);
|
||||||
|
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
mods, state, lib, this_ptr, &mut val, root, &x.rhs, idx_values,
|
mods, state, lib, this_ptr, &mut val, root, &x.rhs, idx_values,
|
||||||
@ -1364,17 +1426,36 @@ impl Engine {
|
|||||||
match &x.lhs {
|
match &x.lhs {
|
||||||
// xxx.prop[expr] | xxx.prop.expr
|
// xxx.prop[expr] | xxx.prop.expr
|
||||||
Expr::Property(p) => {
|
Expr::Property(p) => {
|
||||||
let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) =
|
let ((getter, hash_get), (setter, hash_set), (name, pos)) =
|
||||||
p.as_ref();
|
p.as_ref();
|
||||||
let rhs_chain = rhs_chain.unwrap();
|
let rhs_chain = match_chain_type(rhs);
|
||||||
let hash_get = FnCallHashes::from_native(*hash_get);
|
let hash_get = FnCallHashes::from_native(*hash_get);
|
||||||
let hash_set = FnCallHashes::from_native(*hash_set);
|
let hash_set = FnCallHashes::from_native(*hash_set);
|
||||||
let arg_values = &mut [target.as_mut(), &mut Default::default()];
|
let mut arg_values = [target.as_mut(), &mut Default::default()];
|
||||||
let args = &mut arg_values[..1];
|
let args = &mut arg_values[..1];
|
||||||
let (mut val, updated) = self.exec_fn_call(
|
let (mut val, updated) = self
|
||||||
mods, state, lib, getter, hash_get, args, is_ref, true, *pos,
|
.exec_fn_call(
|
||||||
None, level,
|
mods, state, lib, getter, hash_get, args, is_ref, true,
|
||||||
)?;
|
*pos, None, level,
|
||||||
|
)
|
||||||
|
.or_else(|err| match *err {
|
||||||
|
// Try an indexer if property does not exist
|
||||||
|
EvalAltResult::ErrorDotExpr(_, _) => {
|
||||||
|
let prop = name.into();
|
||||||
|
self.get_indexed_mut(
|
||||||
|
mods, state, lib, target, prop, *pos, false, true,
|
||||||
|
level,
|
||||||
|
)
|
||||||
|
.map(|v| (v.take_or_clone(), false))
|
||||||
|
.map_err(
|
||||||
|
|idx_err| match *idx_err {
|
||||||
|
EvalAltResult::ErrorIndexingType(_, _) => err,
|
||||||
|
_ => idx_err,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => Err(err),
|
||||||
|
})?;
|
||||||
|
|
||||||
let val = &mut val;
|
let val = &mut val;
|
||||||
|
|
||||||
@ -1397,17 +1478,33 @@ impl Engine {
|
|||||||
// Feed the value back via a setter just in case it has been updated
|
// Feed the value back via a setter just in case it has been updated
|
||||||
if updated || may_be_changed {
|
if updated || may_be_changed {
|
||||||
// Re-use args because the first &mut parameter will not be consumed
|
// Re-use args because the first &mut parameter will not be consumed
|
||||||
arg_values[1] = val;
|
let mut arg_values = [target.as_mut(), val];
|
||||||
|
let args = &mut arg_values;
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, setter, hash_set, arg_values, is_ref,
|
mods, state, lib, setter, hash_set, args, is_ref, true,
|
||||||
true, *pos, None, level,
|
*pos, None, level,
|
||||||
)
|
)
|
||||||
.or_else(
|
.or_else(
|
||||||
|err| match *err {
|
|err| match *err {
|
||||||
// If there is no setter, no need to feed it back because
|
// Try an indexer if property does not exist
|
||||||
// the property is read-only
|
|
||||||
EvalAltResult::ErrorDotExpr(_, _) => {
|
EvalAltResult::ErrorDotExpr(_, _) => {
|
||||||
Ok((Dynamic::UNIT, false))
|
let mut prop = name.into();
|
||||||
|
let args = &mut [target.as_mut(), &mut prop, val];
|
||||||
|
let hash_set = FnCallHashes::from_native(
|
||||||
|
crate::calc_fn_hash(FN_IDX_SET, 3),
|
||||||
|
);
|
||||||
|
self.exec_fn_call(
|
||||||
|
mods, state, lib, FN_IDX_SET, hash_set, args,
|
||||||
|
is_ref, true, *pos, None, level,
|
||||||
|
)
|
||||||
|
.or_else(|idx_err| match *idx_err {
|
||||||
|
EvalAltResult::ErrorIndexingType(_, _) => {
|
||||||
|
// If there is no setter, no need to feed it back because
|
||||||
|
// the property is read-only
|
||||||
|
Ok((Dynamic::UNIT, false))
|
||||||
|
}
|
||||||
|
_ => Err(idx_err),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
},
|
},
|
||||||
@ -1419,7 +1516,7 @@ impl Engine {
|
|||||||
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
|
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
|
||||||
Expr::FnCall(f, pos) if !f.is_qualified() => {
|
Expr::FnCall(f, pos) if !f.is_qualified() => {
|
||||||
let FnCallExpr { name, hashes, .. } = f.as_ref();
|
let FnCallExpr { name, hashes, .. } = f.as_ref();
|
||||||
let rhs_chain = rhs_chain.unwrap();
|
let rhs_chain = match_chain_type(rhs);
|
||||||
let mut args = idx_val.as_fn_call_args();
|
let mut args = idx_val.as_fn_call_args();
|
||||||
let (mut val, _) = self.make_method_call(
|
let (mut val, _) = self.make_method_call(
|
||||||
mods, state, lib, name, *hashes, target, &mut args, *pos, level,
|
mods, state, lib, name, *hashes, target, &mut args, *pos, level,
|
||||||
@ -1558,7 +1655,7 @@ impl Engine {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Property(x) if _parent_chain_type == ChainType::Dot => {
|
Expr::Property(x) if _parent_chain_type == ChainType::Dot => {
|
||||||
idx_values.push(ChainArgument::Property(x.2.pos))
|
idx_values.push(ChainArgument::Property((x.2).1))
|
||||||
}
|
}
|
||||||
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
||||||
|
|
||||||
@ -1569,7 +1666,7 @@ impl Engine {
|
|||||||
let lhs_val = match lhs {
|
let lhs_val = match lhs {
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Property(x) if _parent_chain_type == ChainType::Dot => {
|
Expr::Property(x) if _parent_chain_type == ChainType::Dot => {
|
||||||
ChainArgument::Property(x.2.pos)
|
ChainArgument::Property((x.2).1)
|
||||||
}
|
}
|
||||||
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
||||||
|
|
||||||
@ -1646,16 +1743,15 @@ impl Engine {
|
|||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
fn get_indexed_mut<'t>(
|
fn get_indexed_mut<'t>(
|
||||||
&self,
|
&self,
|
||||||
_mods: &mut Imports,
|
mods: &mut Imports,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
_lib: &[&Module],
|
lib: &[&Module],
|
||||||
target: &'t mut Dynamic,
|
target: &'t mut Dynamic,
|
||||||
mut _idx: Dynamic,
|
mut idx: Dynamic,
|
||||||
idx_pos: Position,
|
idx_pos: Position,
|
||||||
_create: bool,
|
_create: bool,
|
||||||
_is_ref: bool,
|
indexers: bool,
|
||||||
_indexers: bool,
|
level: usize,
|
||||||
_level: usize,
|
|
||||||
) -> Result<Target<'t>, Box<EvalAltResult>> {
|
) -> Result<Target<'t>, Box<EvalAltResult>> {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, Position::NONE)?;
|
self.inc_operations(state, Position::NONE)?;
|
||||||
@ -1664,7 +1760,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Dynamic(Union::Array(arr, _, _)) => {
|
Dynamic(Union::Array(arr, _, _)) => {
|
||||||
// val_array[idx]
|
// val_array[idx]
|
||||||
let index = _idx
|
let index = idx
|
||||||
.as_int()
|
.as_int()
|
||||||
.map_err(|err| self.make_type_mismatch_err::<crate::INT>(err, idx_pos))?;
|
.map_err(|err| self.make_type_mismatch_err::<crate::INT>(err, idx_pos))?;
|
||||||
|
|
||||||
@ -1706,8 +1802,8 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Dynamic(Union::Map(map, _, _)) => {
|
Dynamic(Union::Map(map, _, _)) => {
|
||||||
// val_map[idx]
|
// val_map[idx]
|
||||||
let index = &*_idx.read_lock::<ImmutableString>().ok_or_else(|| {
|
let index = &*idx.read_lock::<ImmutableString>().ok_or_else(|| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(_idx.type_name(), idx_pos)
|
self.make_type_mismatch_err::<ImmutableString>(idx.type_name(), idx_pos)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if _create && !map.contains_key(index.as_str()) {
|
if _create && !map.contains_key(index.as_str()) {
|
||||||
@ -1723,7 +1819,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Dynamic(Union::Str(s, _, _)) => {
|
Dynamic(Union::Str(s, _, _)) => {
|
||||||
// val_string[idx]
|
// val_string[idx]
|
||||||
let index = _idx
|
let index = idx
|
||||||
.as_int()
|
.as_int()
|
||||||
.map_err(|err| self.make_type_mismatch_err::<crate::INT>(err, idx_pos))?;
|
.map_err(|err| self.make_type_mismatch_err::<crate::INT>(err, idx_pos))?;
|
||||||
|
|
||||||
@ -1754,27 +1850,14 @@ impl Engine {
|
|||||||
Ok(Target::StringChar(target, offset, ch.into()))
|
Ok(Target::StringChar(target, offset, ch.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
_ if indexers => {
|
||||||
_ if _indexers => {
|
let args = &mut [target, &mut idx];
|
||||||
let type_name = target.type_name();
|
let hash_get = FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_GET, 2));
|
||||||
let args = &mut [target, &mut _idx];
|
|
||||||
let hash_get =
|
|
||||||
FnCallHashes::from_native(calc_fn_hash(std::iter::empty(), FN_IDX_GET, 2));
|
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
_mods, state, _lib, FN_IDX_GET, hash_get, args, _is_ref, true, idx_pos, None,
|
mods, state, lib, FN_IDX_GET, hash_get, args, true, true, idx_pos, None, level,
|
||||||
_level,
|
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v.into())
|
.map(|(v, _)| v.into())
|
||||||
.map_err(|err| match *err {
|
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.ends_with(']') => {
|
|
||||||
Box::new(EvalAltResult::ErrorIndexingType(
|
|
||||||
type_name.into(),
|
|
||||||
Position::NONE,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => err,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => EvalAltResult::ErrorIndexingType(
|
_ => EvalAltResult::ErrorIndexingType(
|
||||||
@ -1878,7 +1961,10 @@ impl Engine {
|
|||||||
Expr::Map(x, _) => {
|
Expr::Map(x, _) => {
|
||||||
let mut map = x.1.clone();
|
let mut map = x.1.clone();
|
||||||
for (Ident { name: key, .. }, expr) in &x.0 {
|
for (Ident { name: key, .. }, expr) in &x.0 {
|
||||||
*map.get_mut(key.as_str()).unwrap() = self
|
let value_ref = map
|
||||||
|
.get_mut(key.as_str())
|
||||||
|
.expect("never fails because the template should contain all the keys");
|
||||||
|
*value_ref = self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
.flatten();
|
.flatten();
|
||||||
}
|
}
|
||||||
@ -1895,7 +1981,9 @@ impl Engine {
|
|||||||
literal_args: c_args,
|
literal_args: c_args,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let namespace = namespace.as_ref();
|
let namespace = namespace
|
||||||
|
.as_ref()
|
||||||
|
.expect("never fails because function call is qualified");
|
||||||
let hash = hashes.native_hash();
|
let hash = hashes.native_hash();
|
||||||
self.make_qualified_function_call(
|
self.make_qualified_function_call(
|
||||||
scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos,
|
scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos,
|
||||||
@ -1954,10 +2042,12 @@ impl Engine {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
.collect::<StaticVec<_>>();
|
.collect::<StaticVec<_>>();
|
||||||
|
let key_token = custom.tokens.first().expect(
|
||||||
|
"never fails because a custom syntax stream must contain at least one token",
|
||||||
|
);
|
||||||
let custom_def = self
|
let custom_def = self
|
||||||
.custom_syntax
|
.custom_syntax.get(key_token)
|
||||||
.get(custom.tokens.first().unwrap())
|
.expect("never fails because the custom syntax leading token should match with definition");
|
||||||
.unwrap();
|
|
||||||
let mut context = EvalContext {
|
let mut context = EvalContext {
|
||||||
engine: self,
|
engine: self,
|
||||||
scope,
|
scope,
|
||||||
@ -2088,7 +2178,9 @@ impl Engine {
|
|||||||
let target_is_shared = false;
|
let target_is_shared = false;
|
||||||
|
|
||||||
if target_is_shared {
|
if target_is_shared {
|
||||||
lock_guard = target.write_lock::<Dynamic>().unwrap();
|
lock_guard = target
|
||||||
|
.write_lock::<Dynamic>()
|
||||||
|
.expect("never fails when casting to `Dynamic`");
|
||||||
lhs_ptr_inner = &mut *lock_guard;
|
lhs_ptr_inner = &mut *lock_guard;
|
||||||
} else {
|
} else {
|
||||||
lhs_ptr_inner = &mut *target;
|
lhs_ptr_inner = &mut *target;
|
||||||
@ -2158,12 +2250,13 @@ impl Engine {
|
|||||||
let (lhs_ptr, pos) =
|
let (lhs_ptr, pos) =
|
||||||
self.search_namespace(scope, mods, state, lib, this_ptr, lhs_expr)?;
|
self.search_namespace(scope, mods, state, lib, this_ptr, lhs_expr)?;
|
||||||
|
|
||||||
|
let var_name = lhs_expr
|
||||||
|
.get_variable_name(false)
|
||||||
|
.expect("never fails because `lhs_ptr` is a `Variable`s");
|
||||||
|
|
||||||
if !lhs_ptr.is_ref() {
|
if !lhs_ptr.is_ref() {
|
||||||
return EvalAltResult::ErrorAssignmentToConstant(
|
return EvalAltResult::ErrorAssignmentToConstant(var_name.to_string(), pos)
|
||||||
lhs_expr.get_variable_name(false).unwrap().to_string(),
|
.into();
|
||||||
pos,
|
|
||||||
)
|
|
||||||
.into();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
@ -2176,7 +2269,7 @@ impl Engine {
|
|||||||
op_info.clone(),
|
op_info.clone(),
|
||||||
*op_pos,
|
*op_pos,
|
||||||
lhs_ptr,
|
lhs_ptr,
|
||||||
(lhs_expr.get_variable_name(false).unwrap(), pos),
|
(var_name, pos),
|
||||||
rhs_val,
|
rhs_val,
|
||||||
rhs_expr.position(),
|
rhs_expr.position(),
|
||||||
)?;
|
)?;
|
||||||
@ -2401,7 +2494,10 @@ impl Engine {
|
|||||||
let loop_var_is_shared = false;
|
let loop_var_is_shared = false;
|
||||||
|
|
||||||
if loop_var_is_shared {
|
if loop_var_is_shared {
|
||||||
*loop_var.write_lock().unwrap() = value;
|
let mut value_ref = loop_var
|
||||||
|
.write_lock()
|
||||||
|
.expect("never fails when casting to `Dynamic`");
|
||||||
|
*value_ref = value;
|
||||||
} else {
|
} else {
|
||||||
*loop_var = value;
|
*loop_var = value;
|
||||||
}
|
}
|
||||||
@ -2451,7 +2547,9 @@ impl Engine {
|
|||||||
literal_args: c_args,
|
literal_args: c_args,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let namespace = namespace.as_ref();
|
let namespace = namespace
|
||||||
|
.as_ref()
|
||||||
|
.expect("never fails because function call is qualified");
|
||||||
let hash = hashes.native_hash();
|
let hash = hashes.native_hash();
|
||||||
self.make_qualified_function_call(
|
self.make_qualified_function_call(
|
||||||
scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos,
|
scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos,
|
||||||
@ -2505,26 +2603,22 @@ impl Engine {
|
|||||||
|
|
||||||
err_map.insert("message".into(), err.to_string().into());
|
err_map.insert("message".into(), err.to_string().into());
|
||||||
|
|
||||||
if let Some(ref source) = state.source {
|
state
|
||||||
err_map.insert("source".into(), source.into());
|
.source
|
||||||
}
|
.as_ref()
|
||||||
|
.map(|source| err_map.insert("source".into(), source.into()));
|
||||||
|
|
||||||
if err_pos.is_none() {
|
if err_pos.is_none() {
|
||||||
// No position info
|
// No position info
|
||||||
} else {
|
} else {
|
||||||
err_map.insert(
|
let line = err_pos.line().expect("never fails because a non-NONE `Position` always has a line number") as INT;
|
||||||
"line".into(),
|
let position = if err_pos.is_beginning_of_line() {
|
||||||
(err_pos.line().unwrap() as INT).into(),
|
0
|
||||||
);
|
} else {
|
||||||
err_map.insert(
|
err_pos.position().expect("never fails because a non-NONE `Position` always has a character position")
|
||||||
"position".into(),
|
} as INT;
|
||||||
if err_pos.is_beginning_of_line() {
|
err_map.insert("line".into(), line.into());
|
||||||
0
|
err_map.insert("position".into(), position.into());
|
||||||
} else {
|
|
||||||
err_pos.position().unwrap() as INT
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err.dump_fields(&mut err_map);
|
err.dump_fields(&mut err_map);
|
||||||
@ -2535,9 +2629,9 @@ impl Engine {
|
|||||||
let orig_scope_len = scope.len();
|
let orig_scope_len = scope.len();
|
||||||
state.scope_level += 1;
|
state.scope_level += 1;
|
||||||
|
|
||||||
if let Some(Ident { name, .. }) = err_var {
|
err_var.as_ref().map(|Ident { name, .. }| {
|
||||||
scope.push(unsafe_cast_var_name_to_lifetime(&name), err_value);
|
scope.push(unsafe_cast_var_name_to_lifetime(name), err_value)
|
||||||
}
|
});
|
||||||
|
|
||||||
let result = self.eval_stmt_block(
|
let result = self.eval_stmt_block(
|
||||||
scope, mods, state, lib, this_ptr, catch_stmt, true, level,
|
scope, mods, state, lib, this_ptr, catch_stmt, true, level,
|
||||||
@ -2604,7 +2698,10 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
if entry_type == AccessMode::ReadOnly && lib.iter().any(|&m| !m.is_empty()) {
|
if entry_type == AccessMode::ReadOnly && lib.iter().any(|&m| !m.is_empty()) {
|
||||||
let global = if let Some(index) = mods.find(KEYWORD_GLOBAL) {
|
let global = if let Some(index) = mods.find(KEYWORD_GLOBAL) {
|
||||||
match mods.get_mut(index).unwrap() {
|
match mods
|
||||||
|
.get_mut(index)
|
||||||
|
.expect("never fails because the index came from `find`")
|
||||||
|
{
|
||||||
m if m.internal => Some(m),
|
m if m.internal => Some(m),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@ -2613,12 +2710,16 @@ impl Engine {
|
|||||||
let mut global = Module::new();
|
let mut global = Module::new();
|
||||||
global.internal = true;
|
global.internal = true;
|
||||||
mods.push(KEYWORD_GLOBAL, global);
|
mods.push(KEYWORD_GLOBAL, global);
|
||||||
Some(mods.get_mut(mods.len() - 1).unwrap())
|
Some(
|
||||||
|
mods.get_mut(mods.len() - 1)
|
||||||
|
.expect("never fails because the global module was just added"),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(global) = global {
|
if let Some(global) = global {
|
||||||
let global = Shared::get_mut(global).unwrap();
|
Shared::get_mut(global)
|
||||||
global.set_var(name.clone(), value.clone());
|
.expect("never fails because the global module is never shared")
|
||||||
|
.set_var(name.clone(), value.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2635,9 +2736,8 @@ impl Engine {
|
|||||||
scope.push_dynamic_value(var_name, entry_type, value);
|
scope.push_dynamic_value(var_name, entry_type, value);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
if let Some(alias) = _alias {
|
_alias.map(|alias| scope.add_entry_alias(scope.len() - 1, alias));
|
||||||
scope.add_entry_alias(scope.len() - 1, alias);
|
|
||||||
}
|
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2673,7 +2773,7 @@ impl Engine {
|
|||||||
self.module_resolver.resolve(self, source, &path, expr_pos)
|
self.module_resolver.resolve(self, source, &path, expr_pos)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if let Some(name) = export.as_ref().map(|x| x.name.clone()) {
|
export.as_ref().map(|x| x.name.clone()).map(|name| {
|
||||||
if !module.is_indexed() {
|
if !module.is_indexed() {
|
||||||
// Index the module (making a clone copy if necessary) if it is not indexed
|
// Index the module (making a clone copy if necessary) if it is not indexed
|
||||||
let mut module = crate::fn_native::shared_take_or_clone(module);
|
let mut module = crate::fn_native::shared_take_or_clone(module);
|
||||||
@ -2682,7 +2782,7 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
mods.push(name, module);
|
mods.push(name, module);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
state.modules += 1;
|
state.modules += 1;
|
||||||
|
|
||||||
@ -2710,14 +2810,14 @@ impl Engine {
|
|||||||
// Share statement
|
// Share statement
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Stmt::Share(name) => {
|
Stmt::Share(name) => {
|
||||||
if let Some((index, _)) = scope.get_index(name) {
|
scope.get_index(name).map(|(index, _)| {
|
||||||
let val = scope.get_mut_by_index(index);
|
let val = scope.get_mut_by_index(index);
|
||||||
|
|
||||||
if !val.is_shared() {
|
if !val.is_shared() {
|
||||||
// Replace the variable with a shared value.
|
// Replace the variable with a shared value.
|
||||||
*val = std::mem::take(val).into_shared();
|
*val = std::mem::take(val).into_shared();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -2732,9 +2832,10 @@ impl Engine {
|
|||||||
/// Check a result to ensure that the data size is within allowable limit.
|
/// Check a result to ensure that the data size is within allowable limit.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
fn check_data_size(&self, result: &RhaiResult) -> Result<(), Box<EvalAltResult>> {
|
fn check_data_size(&self, result: &RhaiResult) -> Result<(), Box<EvalAltResult>> {
|
||||||
if result.is_err() {
|
let result = match result {
|
||||||
return Ok(());
|
Err(_) => return Ok(()),
|
||||||
}
|
Ok(r) => r,
|
||||||
|
};
|
||||||
|
|
||||||
// If no data size limits, just return
|
// If no data size limits, just return
|
||||||
let mut _has_limit = self.limits.max_string_size.is_some();
|
let mut _has_limit = self.limits.max_string_size.is_some();
|
||||||
@ -2803,7 +2904,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (_arr, _map, s) = calc_size(result.as_ref().unwrap());
|
let (_arr, _map, s) = calc_size(result);
|
||||||
|
|
||||||
if s > self
|
if s > self
|
||||||
.limits
|
.limits
|
||||||
@ -2860,7 +2961,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Report progress - only in steps
|
// Report progress - only in steps
|
||||||
if let Some(progress) = &self.progress {
|
if let Some(ref progress) = self.progress {
|
||||||
if let Some(token) = progress(state.operations) {
|
if let Some(token) = progress(state.operations) {
|
||||||
// Terminate script if progress returns a termination token
|
// Terminate script if progress returns a termination token
|
||||||
return EvalAltResult::ErrorTerminated(token, pos).into();
|
return EvalAltResult::ErrorTerminated(token, pos).into();
|
||||||
|
@ -477,9 +477,7 @@ impl Engine {
|
|||||||
set_fn: impl Fn(&mut T, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
set_fn: impl Fn(&mut T, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
use crate::engine::make_setter;
|
use crate::engine::make_setter;
|
||||||
self.register_result_fn(&make_setter(name), move |obj: &mut T, value: V| {
|
self.register_result_fn(&make_setter(name), set_fn)
|
||||||
set_fn(obj, value)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
/// Short-hand for registering both getter and setter functions
|
/// Short-hand for registering both getter and setter functions
|
||||||
/// of a registered type with the [`Engine`].
|
/// of a registered type with the [`Engine`].
|
||||||
@ -536,7 +534,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// The function signature must start with `&mut self` and not `&self`.
|
/// The function signature must start with `&mut self` and not `&self`.
|
||||||
///
|
///
|
||||||
/// Not available under `no_index`.
|
/// Not available under both `no_index` and `no_object`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
@ -572,16 +570,18 @@ impl Engine {
|
|||||||
/// // Register an indexer.
|
/// // Register an indexer.
|
||||||
/// .register_indexer_get(TestStruct::get_field);
|
/// .register_indexer_get(TestStruct::get_field);
|
||||||
///
|
///
|
||||||
|
/// # #[cfg(not(feature = "no_index"))]
|
||||||
/// assert_eq!(engine.eval::<i64>("let a = new_ts(); a[2]")?, 3);
|
/// assert_eq!(engine.eval::<i64>("let a = new_ts(); a[2]")?, 3);
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_indexer_get<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
pub fn register_indexer_get<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static,
|
get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
}
|
}
|
||||||
@ -602,7 +602,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// The function signature must start with `&mut self` and not `&self`.
|
/// The function signature must start with `&mut self` and not `&self`.
|
||||||
///
|
///
|
||||||
/// Not available under `no_index`.
|
/// Not available under both `no_index` and `no_object`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
@ -640,11 +640,12 @@ impl Engine {
|
|||||||
/// // Register an indexer.
|
/// // Register an indexer.
|
||||||
/// .register_indexer_get_result(TestStruct::get_field);
|
/// .register_indexer_get_result(TestStruct::get_field);
|
||||||
///
|
///
|
||||||
|
/// # #[cfg(not(feature = "no_index"))]
|
||||||
/// assert_eq!(engine.eval::<i64>("let a = new_ts(); a[2]")?, 3);
|
/// assert_eq!(engine.eval::<i64>("let a = new_ts(); a[2]")?, 3);
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_indexer_get_result<
|
pub fn register_indexer_get_result<
|
||||||
T: Variant + Clone,
|
T: Variant + Clone,
|
||||||
@ -654,6 +655,7 @@ impl Engine {
|
|||||||
&mut self,
|
&mut self,
|
||||||
get_fn: impl Fn(&mut T, X) -> Result<V, Box<EvalAltResult>> + SendSync + 'static,
|
get_fn: impl Fn(&mut T, X) -> Result<V, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
}
|
}
|
||||||
@ -672,7 +674,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Register an index setter for a custom type with the [`Engine`].
|
/// Register an index setter for a custom type with the [`Engine`].
|
||||||
///
|
///
|
||||||
/// Not available under `no_index`.
|
/// Not available under both `no_index` and `no_object`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
@ -707,6 +709,7 @@ impl Engine {
|
|||||||
/// // Register an indexer.
|
/// // Register an indexer.
|
||||||
/// .register_indexer_set(TestStruct::set_field);
|
/// .register_indexer_set(TestStruct::set_field);
|
||||||
///
|
///
|
||||||
|
/// # #[cfg(not(feature = "no_index"))]
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// engine.eval::<TestStruct>("let a = new_ts(); a[2] = 42; a")?.fields[2],
|
/// engine.eval::<TestStruct>("let a = new_ts(); a[2] = 42; a")?.fields[2],
|
||||||
/// 42
|
/// 42
|
||||||
@ -714,12 +717,13 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_indexer_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
pub fn register_indexer_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
set_fn: impl Fn(&mut T, X, V) + SendSync + 'static,
|
set_fn: impl Fn(&mut T, X, V) + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
}
|
}
|
||||||
@ -738,7 +742,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Register an index setter for a custom type with the [`Engine`].
|
/// Register an index setter for a custom type with the [`Engine`].
|
||||||
///
|
///
|
||||||
/// Not available under `no_index`.
|
/// Not available under both `no_index` and `no_object`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
@ -776,6 +780,7 @@ impl Engine {
|
|||||||
/// // Register an indexer.
|
/// // Register an indexer.
|
||||||
/// .register_indexer_set_result(TestStruct::set_field);
|
/// .register_indexer_set_result(TestStruct::set_field);
|
||||||
///
|
///
|
||||||
|
/// # #[cfg(not(feature = "no_index"))]
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// engine.eval::<TestStruct>("let a = new_ts(); a[2] = 42; a")?.fields[2],
|
/// engine.eval::<TestStruct>("let a = new_ts(); a[2] = 42; a")?.fields[2],
|
||||||
/// 42
|
/// 42
|
||||||
@ -783,7 +788,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_indexer_set_result<
|
pub fn register_indexer_set_result<
|
||||||
T: Variant + Clone,
|
T: Variant + Clone,
|
||||||
@ -793,6 +798,7 @@ impl Engine {
|
|||||||
&mut self,
|
&mut self,
|
||||||
set_fn: impl Fn(&mut T, X, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
set_fn: impl Fn(&mut T, X, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
}
|
}
|
||||||
@ -807,14 +813,11 @@ impl Engine {
|
|||||||
panic!("Cannot register indexer for strings.");
|
panic!("Cannot register indexer for strings.");
|
||||||
}
|
}
|
||||||
|
|
||||||
self.register_result_fn(
|
self.register_result_fn(crate::engine::FN_IDX_SET, set_fn)
|
||||||
crate::engine::FN_IDX_SET,
|
|
||||||
move |obj: &mut T, index: X, value: V| set_fn(obj, index, value),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
/// Short-hand for register both index getter and setter functions for a custom type with the [`Engine`].
|
/// Short-hand for registering both index getter and setter functions for a custom type with the [`Engine`].
|
||||||
///
|
///
|
||||||
/// Not available under `no_index`.
|
/// Not available under both `no_index` and `no_object`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
@ -852,11 +855,12 @@ impl Engine {
|
|||||||
/// // Register an indexer.
|
/// // Register an indexer.
|
||||||
/// .register_indexer_get_set(TestStruct::get_field, TestStruct::set_field);
|
/// .register_indexer_get_set(TestStruct::get_field, TestStruct::set_field);
|
||||||
///
|
///
|
||||||
|
/// # #[cfg(not(feature = "no_index"))]
|
||||||
/// assert_eq!(engine.eval::<i64>("let a = new_ts(); a[2] = 42; a[2]")?, 42);
|
/// assert_eq!(engine.eval::<i64>("let a = new_ts(); a[2] = 42; a[2]")?, 42);
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_indexer_get_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
pub fn register_indexer_get_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -947,8 +951,14 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut iter = name.as_ref().splitn(2, separator.as_ref());
|
let mut iter = name.as_ref().splitn(2, separator.as_ref());
|
||||||
let sub_module = iter.next().unwrap().trim();
|
let sub_module = iter
|
||||||
let remainder = iter.next().unwrap().trim();
|
.next()
|
||||||
|
.expect("never fails because the name contains a separator")
|
||||||
|
.trim();
|
||||||
|
let remainder = iter
|
||||||
|
.next()
|
||||||
|
.expect("never fails because the name contains a separator")
|
||||||
|
.trim();
|
||||||
|
|
||||||
if !root.contains_key(sub_module) {
|
if !root.contains_key(sub_module) {
|
||||||
let mut m: Module = Default::default();
|
let mut m: Module = Default::default();
|
||||||
@ -956,7 +966,9 @@ impl Engine {
|
|||||||
m.build_index();
|
m.build_index();
|
||||||
root.insert(sub_module.into(), m.into());
|
root.insert(sub_module.into(), m.into());
|
||||||
} else {
|
} else {
|
||||||
let m = root.remove(sub_module).unwrap();
|
let m = root
|
||||||
|
.remove(sub_module)
|
||||||
|
.expect("never fails because the root contains the sub-module");
|
||||||
let mut m = crate::fn_native::shared_take_or_clone(m);
|
let mut m = crate::fn_native::shared_take_or_clone(m);
|
||||||
register_static_module_raw(m.sub_modules_mut(), remainder, module);
|
register_static_module_raw(m.sub_modules_mut(), remainder, module);
|
||||||
m.build_index();
|
m.build_index();
|
||||||
@ -1074,7 +1086,10 @@ impl Engine {
|
|||||||
resolver: &StaticModuleResolver,
|
resolver: &StaticModuleResolver,
|
||||||
imports: &mut BTreeSet<Identifier>,
|
imports: &mut BTreeSet<Identifier>,
|
||||||
) {
|
) {
|
||||||
ast.walk(&mut |path| match path.last().unwrap() {
|
ast.walk(&mut |path| match path
|
||||||
|
.last()
|
||||||
|
.expect("never fails because `path` always contains the current node")
|
||||||
|
{
|
||||||
// Collect all `import` statements with a string constant path
|
// Collect all `import` statements with a string constant path
|
||||||
ASTNode::Stmt(Stmt::Import(Expr::StringConstant(s, _), _, _))
|
ASTNode::Stmt(Stmt::Import(Expr::StringConstant(s, _), _, _))
|
||||||
if !resolver.contains_path(s) && !imports.contains(s.as_str()) =>
|
if !resolver.contains_path(s) && !imports.contains(s.as_str()) =>
|
||||||
|
@ -13,6 +13,8 @@ use crate::FLOAT;
|
|||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
|
|
||||||
|
const BUILTIN: &str = "never fails because this is built-in code and the type is already checked";
|
||||||
|
|
||||||
/// Is the type a numeric type?
|
/// Is the type a numeric type?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn is_numeric(type_id: TypeId) -> bool {
|
fn is_numeric(type_id: TypeId) -> bool {
|
||||||
@ -75,22 +77,22 @@ pub fn get_builtin_binary_op_fn(
|
|||||||
macro_rules! impl_op {
|
macro_rules! impl_op {
|
||||||
($xx:ident $op:tt $yy:ident) => {
|
($xx:ident $op:tt $yy:ident) => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let x = &*args[0].read_lock::<$xx>().unwrap();
|
let x = &*args[0].read_lock::<$xx>().expect(BUILTIN);
|
||||||
let y = &*args[1].read_lock::<$yy>().unwrap();
|
let y = &*args[1].read_lock::<$yy>().expect(BUILTIN);
|
||||||
Ok((x $op y).into())
|
Ok((x $op y).into())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
($xx:ident . $func:ident ( $yy:ty )) => {
|
($xx:ident . $func:ident ( $yy:ty )) => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let x = &*args[0].read_lock::<$xx>().unwrap();
|
let x = &*args[0].read_lock::<$xx>().expect(BUILTIN);
|
||||||
let y = &*args[1].read_lock::<$yy>().unwrap();
|
let y = &*args[1].read_lock::<$yy>().expect(BUILTIN);
|
||||||
Ok(x.$func(y).into())
|
Ok(x.$func(y).into())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
($xx:ident . $func:ident ( $yy:ident . $yyy:ident () )) => {
|
($xx:ident . $func:ident ( $yy:ident . $yyy:ident () )) => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let x = &*args[0].read_lock::<$xx>().unwrap();
|
let x = &*args[0].read_lock::<$xx>().expect(BUILTIN);
|
||||||
let y = &*args[1].read_lock::<$yy>().unwrap();
|
let y = &*args[1].read_lock::<$yy>().expect(BUILTIN);
|
||||||
Ok(x.$func(y.$yyy()).into())
|
Ok(x.$func(y.$yyy()).into())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
@ -102,43 +104,43 @@ pub fn get_builtin_binary_op_fn(
|
|||||||
};
|
};
|
||||||
($base:ty => $xx:ident $op:tt $yy:ident) => {
|
($base:ty => $xx:ident $op:tt $yy:ident) => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let x = args[0].$xx().unwrap() as $base;
|
let x = args[0].$xx().expect(BUILTIN) as $base;
|
||||||
let y = args[1].$yy().unwrap() as $base;
|
let y = args[1].$yy().expect(BUILTIN) as $base;
|
||||||
Ok((x $op y).into())
|
Ok((x $op y).into())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
($base:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty)) => {
|
($base:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty)) => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let x = args[0].$xx().unwrap() as $base;
|
let x = args[0].$xx().expect(BUILTIN) as $base;
|
||||||
let y = args[1].$yy().unwrap() as $base;
|
let y = args[1].$yy().expect(BUILTIN) as $base;
|
||||||
Ok(x.$func(y as $yyy).into())
|
Ok(x.$func(y as $yyy).into())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
($base:ty => $func:ident ( $xx:ident, $yy:ident )) => {
|
($base:ty => $func:ident ( $xx:ident, $yy:ident )) => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let x = args[0].$xx().unwrap() as $base;
|
let x = args[0].$xx().expect(BUILTIN) as $base;
|
||||||
let y = args[1].$yy().unwrap() as $base;
|
let y = args[1].$yy().expect(BUILTIN) as $base;
|
||||||
$func(x, y).map(Into::<Dynamic>::into)
|
$func(x, y).map(Into::<Dynamic>::into)
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
(from $base:ty => $xx:ident $op:tt $yy:ident) => {
|
(from $base:ty => $xx:ident $op:tt $yy:ident) => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let x = <$base>::from(args[0].$xx().unwrap());
|
let x = <$base>::from(args[0].$xx().expect(BUILTIN));
|
||||||
let y = <$base>::from(args[1].$yy().unwrap());
|
let y = <$base>::from(args[1].$yy().expect(BUILTIN));
|
||||||
Ok((x $op y).into())
|
Ok((x $op y).into())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
(from $base:ty => $xx:ident . $func:ident ( $yy:ident )) => {
|
(from $base:ty => $xx:ident . $func:ident ( $yy:ident )) => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let x = <$base>::from(args[0].$xx().unwrap());
|
let x = <$base>::from(args[0].$xx().expect(BUILTIN));
|
||||||
let y = <$base>::from(args[1].$yy().unwrap());
|
let y = <$base>::from(args[1].$yy().expect(BUILTIN));
|
||||||
Ok(x.$func(y).into())
|
Ok(x.$func(y).into())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
(from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => {
|
(from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let x = <$base>::from(args[0].$xx().unwrap());
|
let x = <$base>::from(args[0].$xx().expect(BUILTIN));
|
||||||
let y = <$base>::from(args[1].$yy().unwrap());
|
let y = <$base>::from(args[1].$yy().expect(BUILTIN));
|
||||||
$func(x, y).map(Into::<Dynamic>::into)
|
$func(x, y).map(Into::<Dynamic>::into)
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
@ -175,7 +177,7 @@ pub fn get_builtin_binary_op_fn(
|
|||||||
($x:ty, $xx:ident, $y:ty, $yy:ident) => {
|
($x:ty, $xx:ident, $y:ty, $yy:ident) => {
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unBUILTIN")) {
|
||||||
use crate::packages::arithmetic::decimal_functions::*;
|
use crate::packages::arithmetic::decimal_functions::*;
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
@ -222,8 +224,8 @@ pub fn get_builtin_binary_op_fn(
|
|||||||
if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
||||||
let x = args[0].as_char().unwrap();
|
let x = args[0].as_char().expect(BUILTIN);
|
||||||
let y = &*args[1].read_lock::<ImmutableString>().unwrap();
|
let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
let s1 = [x, '\0'];
|
let s1 = [x, '\0'];
|
||||||
let mut y = y.chars();
|
let mut y = y.chars();
|
||||||
let s2 = [y.next().unwrap_or('\0'), y.next().unwrap_or('\0')];
|
let s2 = [y.next().unwrap_or('\0'), y.next().unwrap_or('\0')];
|
||||||
@ -233,8 +235,8 @@ pub fn get_builtin_binary_op_fn(
|
|||||||
match op {
|
match op {
|
||||||
"+" => {
|
"+" => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let x = args[0].as_char().unwrap();
|
let x = args[0].as_char().expect(BUILTIN);
|
||||||
let y = &*args[1].read_lock::<ImmutableString>().unwrap();
|
let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
Ok(format!("{}{}", x, y).into())
|
Ok(format!("{}{}", x, y).into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -251,8 +253,8 @@ pub fn get_builtin_binary_op_fn(
|
|||||||
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
||||||
let x = &*args[0].read_lock::<ImmutableString>().unwrap();
|
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
let y = args[1].as_char().unwrap();
|
let y = args[1].as_char().expect(BUILTIN);
|
||||||
let mut x = x.chars();
|
let mut x = x.chars();
|
||||||
let s1 = [x.next().unwrap_or('\0'), x.next().unwrap_or('\0')];
|
let s1 = [x.next().unwrap_or('\0'), x.next().unwrap_or('\0')];
|
||||||
let s2 = [y, '\0'];
|
let s2 = [y, '\0'];
|
||||||
@ -262,15 +264,15 @@ pub fn get_builtin_binary_op_fn(
|
|||||||
match op {
|
match op {
|
||||||
"+" => {
|
"+" => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let x = &*args[0].read_lock::<ImmutableString>().unwrap();
|
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
let y = args[1].as_char().unwrap();
|
let y = args[1].as_char().expect(BUILTIN);
|
||||||
Ok((x + y).into())
|
Ok((x + y).into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
"-" => {
|
"-" => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let x = &*args[0].read_lock::<ImmutableString>().unwrap();
|
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
let y = args[1].as_char().unwrap();
|
let y = args[1].as_char().expect(BUILTIN);
|
||||||
Ok((x - y).into())
|
Ok((x - y).into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -282,8 +284,8 @@ pub fn get_builtin_binary_op_fn(
|
|||||||
"<=" => impl_op!(get_s1s2(<=)),
|
"<=" => impl_op!(get_s1s2(<=)),
|
||||||
OP_CONTAINS => {
|
OP_CONTAINS => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let s = &*args[0].read_lock::<ImmutableString>().unwrap();
|
let s = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
let c = args[1].as_char().unwrap();
|
let c = args[1].as_char().expect(BUILTIN);
|
||||||
Ok((s.contains(c)).into())
|
Ok((s.contains(c)).into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -314,7 +316,7 @@ pub fn get_builtin_binary_op_fn(
|
|||||||
// Beyond here, type1 == type2
|
// Beyond here, type1 == type2
|
||||||
|
|
||||||
if type1 == TypeId::of::<INT>() {
|
if type1 == TypeId::of::<INT>() {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unBUILTIN")) {
|
||||||
use crate::packages::arithmetic::arith_basic::INT::functions::*;
|
use crate::packages::arithmetic::arith_basic::INT::functions::*;
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
@ -383,8 +385,8 @@ pub fn get_builtin_binary_op_fn(
|
|||||||
"<=" => impl_op!(ImmutableString <= ImmutableString),
|
"<=" => impl_op!(ImmutableString <= ImmutableString),
|
||||||
OP_CONTAINS => {
|
OP_CONTAINS => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let s1 = &*args[0].read_lock::<ImmutableString>().unwrap();
|
let s1 = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
let s2 = &*args[1].read_lock::<ImmutableString>().unwrap();
|
let s2 = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
Ok((s1.contains(s2.as_str())).into())
|
Ok((s1.contains(s2.as_str())).into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -396,8 +398,8 @@ pub fn get_builtin_binary_op_fn(
|
|||||||
match op {
|
match op {
|
||||||
"+" => {
|
"+" => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let x = args[0].as_char().unwrap();
|
let x = args[0].as_char().expect(BUILTIN);
|
||||||
let y = args[1].as_char().unwrap();
|
let y = args[1].as_char().expect(BUILTIN);
|
||||||
Ok(format!("{}{}", x, y).into())
|
Ok(format!("{}{}", x, y).into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -440,55 +442,55 @@ pub fn get_builtin_op_assignment_fn(
|
|||||||
macro_rules! impl_op {
|
macro_rules! impl_op {
|
||||||
($x:ty = x $op:tt $yy:ident) => {
|
($x:ty = x $op:tt $yy:ident) => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let x = args[0].$yy().unwrap();
|
let x = args[0].$yy().expect(BUILTIN);
|
||||||
let y = args[1].$yy().unwrap() as $x;
|
let y = args[1].$yy().expect(BUILTIN) as $x;
|
||||||
Ok((*args[0].write_lock::<$x>().unwrap() = x $op y).into())
|
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x $op y).into())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
($x:ident $op:tt $yy:ident) => {
|
($x:ident $op:tt $yy:ident) => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let y = args[1].$yy().unwrap() as $x;
|
let y = args[1].$yy().expect(BUILTIN) as $x;
|
||||||
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
($x:ident $op:tt $yy:ident as $yyy:ty) => {
|
($x:ident $op:tt $yy:ident as $yyy:ty) => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let y = args[1].$yy().unwrap() as $yyy;
|
let y = args[1].$yy().expect(BUILTIN) as $yyy;
|
||||||
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
($x:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty )) => {
|
($x:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty )) => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let x = args[0].$xx().unwrap();
|
let x = args[0].$xx().expect(BUILTIN);
|
||||||
let y = args[1].$yy().unwrap() as $x;
|
let y = args[1].$yy().expect(BUILTIN) as $x;
|
||||||
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y as $yyy)).into())
|
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x.$func(y as $yyy)).into())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
($x:ty => $func:ident ( $xx:ident, $yy:ident )) => {
|
($x:ty => $func:ident ( $xx:ident, $yy:ident )) => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let x = args[0].$xx().unwrap();
|
let x = args[0].$xx().expect(BUILTIN);
|
||||||
let y = args[1].$yy().unwrap() as $x;
|
let y = args[1].$yy().expect(BUILTIN) as $x;
|
||||||
Ok((*args[0].write_lock().unwrap() = $func(x, y)?).into())
|
Ok((*args[0].write_lock().expect(BUILTIN) = $func(x, y)?).into())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
(from $x:ident $op:tt $yy:ident) => {
|
(from $x:ident $op:tt $yy:ident) => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let y = <$x>::from(args[1].$yy().unwrap());
|
let y = <$x>::from(args[1].$yy().expect(BUILTIN));
|
||||||
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
(from $x:ty => $xx:ident . $func:ident ( $yy:ident )) => {
|
(from $x:ty => $xx:ident . $func:ident ( $yy:ident )) => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let x = args[0].$xx().unwrap();
|
let x = args[0].$xx().expect(BUILTIN);
|
||||||
let y = <$x>::from(args[1].$yy().unwrap());
|
let y = <$x>::from(args[1].$yy().expect(BUILTIN));
|
||||||
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y)).into())
|
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x.$func(y)).into())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
(from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => {
|
(from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let x = args[0].$xx().unwrap();
|
let x = args[0].$xx().expect(BUILTIN);
|
||||||
let y = <$x>::from(args[1].$yy().unwrap());
|
let y = <$x>::from(args[1].$yy().expect(BUILTIN));
|
||||||
Ok((*args[0].write_lock().unwrap() = $func(x, y)?).into())
|
Ok((*args[0].write_lock().expect(BUILTIN) = $func(x, y)?).into())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -517,7 +519,7 @@ pub fn get_builtin_op_assignment_fn(
|
|||||||
($x:ident, $xx:ident, $y:ty, $yy:ident) => {
|
($x:ident, $xx:ident, $y:ty, $yy:ident) => {
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unBUILTIN")) {
|
||||||
use crate::packages::arithmetic::decimal_functions::*;
|
use crate::packages::arithmetic::decimal_functions::*;
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
@ -562,10 +564,15 @@ pub fn get_builtin_op_assignment_fn(
|
|||||||
match op {
|
match op {
|
||||||
"+=" => {
|
"+=" => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let mut ch = args[0].as_char().unwrap().to_string();
|
let mut ch = args[0].as_char().expect(BUILTIN).to_string();
|
||||||
ch.push_str(args[1].read_lock::<ImmutableString>().unwrap().as_str());
|
ch.push_str(
|
||||||
|
args[1]
|
||||||
|
.read_lock::<ImmutableString>()
|
||||||
|
.expect(BUILTIN)
|
||||||
|
.as_str(),
|
||||||
|
);
|
||||||
|
|
||||||
let mut x = args[0].write_lock::<Dynamic>().unwrap();
|
let mut x = args[0].write_lock::<Dynamic>().expect(BUILTIN);
|
||||||
Ok((*x = ch.into()).into())
|
Ok((*x = ch.into()).into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -580,7 +587,7 @@ pub fn get_builtin_op_assignment_fn(
|
|||||||
|
|
||||||
// Beyond here, type1 == type2
|
// Beyond here, type1 == type2
|
||||||
if type1 == TypeId::of::<INT>() {
|
if type1 == TypeId::of::<INT>() {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unBUILTIN")) {
|
||||||
use crate::packages::arithmetic::arith_basic::INT::functions::*;
|
use crate::packages::arithmetic::arith_basic::INT::functions::*;
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
@ -628,8 +635,8 @@ pub fn get_builtin_op_assignment_fn(
|
|||||||
match op {
|
match op {
|
||||||
"+=" => {
|
"+=" => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let y = args[1].as_char().unwrap();
|
let y = args[1].as_char().expect(BUILTIN);
|
||||||
let mut x = args[0].write_lock::<Dynamic>().unwrap();
|
let mut x = args[0].write_lock::<Dynamic>().expect(BUILTIN);
|
||||||
Ok((*x = format!("{}{}", *x, y).into()).into())
|
Ok((*x = format!("{}{}", *x, y).into()).into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -641,17 +648,17 @@ pub fn get_builtin_op_assignment_fn(
|
|||||||
match op {
|
match op {
|
||||||
"+=" => {
|
"+=" => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let (first, second) = args.split_first_mut().unwrap();
|
let (first, second) = args.split_first_mut().expect(BUILTIN);
|
||||||
let mut x = first.write_lock::<ImmutableString>().unwrap();
|
let mut x = first.write_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
let y = &*second[0].read_lock::<ImmutableString>().unwrap();
|
let y = &*second[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
Ok((*x += y).into())
|
Ok((*x += y).into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
"-=" => {
|
"-=" => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let (first, second) = args.split_first_mut().unwrap();
|
let (first, second) = args.split_first_mut().expect(BUILTIN);
|
||||||
let mut x = first.write_lock::<ImmutableString>().unwrap();
|
let mut x = first.write_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
let y = &*second[0].read_lock::<ImmutableString>().unwrap();
|
let y = &*second[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
Ok((*x -= y).into())
|
Ok((*x -= y).into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
142
src/fn_call.rs
142
src/fn_call.rs
@ -24,7 +24,7 @@ use std::prelude::v1::*;
|
|||||||
use std::{
|
use std::{
|
||||||
any::{type_name, TypeId},
|
any::{type_name, TypeId},
|
||||||
convert::TryFrom,
|
convert::TryFrom,
|
||||||
iter::{empty, once},
|
iter::once,
|
||||||
mem,
|
mem,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -49,7 +49,12 @@ impl<'a> ArgBackup<'a> {
|
|||||||
///
|
///
|
||||||
/// This method blindly casts a reference to another lifetime, which saves allocation and string cloning.
|
/// This method blindly casts a reference to another lifetime, which saves allocation and string cloning.
|
||||||
///
|
///
|
||||||
/// If `restore_first_arg` is called before the end of the scope, the shorter lifetime will not leak.
|
/// As long as `restore_first_arg` is called before the end of the scope, the shorter lifetime
|
||||||
|
/// will not leak.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics when `args` is empty.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn change_first_arg_to_copy(&mut self, args: &mut FnCallArgs<'a>) {
|
fn change_first_arg_to_copy(&mut self, args: &mut FnCallArgs<'a>) {
|
||||||
// Clone the original value.
|
// Clone the original value.
|
||||||
@ -65,7 +70,7 @@ impl<'a> ArgBackup<'a> {
|
|||||||
//
|
//
|
||||||
// We can do this here because, before the end of this scope, we'd restore the original reference
|
// We can do this here because, before the end of this scope, we'd restore the original reference
|
||||||
// via `restore_first_arg`. Therefore this shorter lifetime does not leak.
|
// via `restore_first_arg`. Therefore this shorter lifetime does not leak.
|
||||||
self.orig_mut = Some(mem::replace(args.get_mut(0).unwrap(), unsafe {
|
self.orig_mut = Some(mem::replace(&mut args[0], unsafe {
|
||||||
mem::transmute(&mut self.value_copy)
|
mem::transmute(&mut self.value_copy)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -77,9 +82,7 @@ impl<'a> ArgBackup<'a> {
|
|||||||
/// the current scope. Otherwise it is undefined behavior as the shorter lifetime will leak.
|
/// the current scope. Otherwise it is undefined behavior as the shorter lifetime will leak.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn restore_first_arg(mut self, args: &mut FnCallArgs<'a>) {
|
fn restore_first_arg(mut self, args: &mut FnCallArgs<'a>) {
|
||||||
if let Some(this_pointer) = self.orig_mut.take() {
|
self.orig_mut.take().map(|p| args[0] = p);
|
||||||
args[0] = this_pointer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,12 +165,10 @@ impl Engine {
|
|||||||
allow_dynamic: bool,
|
allow_dynamic: bool,
|
||||||
is_op_assignment: bool,
|
is_op_assignment: bool,
|
||||||
) -> &'s Option<Box<FnResolutionCacheEntry>> {
|
) -> &'s Option<Box<FnResolutionCacheEntry>> {
|
||||||
let mut hash = if let Some(ref args) = args {
|
let mut hash = args.as_ref().map_or(hash_script, |args| {
|
||||||
let hash_params = calc_fn_params_hash(args.iter().map(|a| a.type_id()));
|
let hash_params = calc_fn_params_hash(args.iter().map(|a| a.type_id()));
|
||||||
combine_hashes(hash_script, hash_params)
|
combine_hashes(hash_script, hash_params)
|
||||||
} else {
|
});
|
||||||
hash_script
|
|
||||||
};
|
|
||||||
|
|
||||||
&*state
|
&*state
|
||||||
.fn_resolution_cache_mut()
|
.fn_resolution_cache_mut()
|
||||||
@ -239,7 +240,8 @@ impl Engine {
|
|||||||
FnResolutionCacheEntry { func, source: None }
|
FnResolutionCacheEntry { func, source: None }
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
let (first, second) = args.split_first().unwrap();
|
let (first, second) = args.split_first()
|
||||||
|
.expect("never fails because an op-assignment must have two arguments");
|
||||||
|
|
||||||
get_builtin_op_assignment_fn(fn_name, *first, second[0]).map(
|
get_builtin_op_assignment_fn(fn_name, *first, second[0]).map(
|
||||||
|f| {
|
|f| {
|
||||||
@ -257,7 +259,9 @@ impl Engine {
|
|||||||
// Try all permutations with `Dynamic` wildcards
|
// Try all permutations with `Dynamic` wildcards
|
||||||
None => {
|
None => {
|
||||||
let hash_params = calc_fn_params_hash(
|
let hash_params = calc_fn_params_hash(
|
||||||
args.as_ref().unwrap().iter().enumerate().map(|(i, a)| {
|
args.as_ref().expect("never fails because there are no permutations if there are no arguments")
|
||||||
|
.iter().enumerate().map(|(i, a)|
|
||||||
|
{
|
||||||
let mask = 1usize << (num_args - i - 1);
|
let mask = 1usize << (num_args - i - 1);
|
||||||
if bitmask & mask != 0 {
|
if bitmask & mask != 0 {
|
||||||
// Replace with `Dynamic`
|
// Replace with `Dynamic`
|
||||||
@ -320,7 +324,7 @@ impl Engine {
|
|||||||
let mut backup: Option<ArgBackup> = None;
|
let mut backup: Option<ArgBackup> = None;
|
||||||
if is_ref && func.is_pure() && !args.is_empty() {
|
if is_ref && func.is_pure() && !args.is_empty() {
|
||||||
backup = Some(Default::default());
|
backup = Some(Default::default());
|
||||||
backup.as_mut().unwrap().change_first_arg_to_copy(args);
|
backup.as_mut().map(|bk| bk.change_first_arg_to_copy(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run external function
|
// Run external function
|
||||||
@ -336,9 +340,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Restore the original reference
|
// Restore the original reference
|
||||||
if let Some(backup) = backup {
|
backup.map(|bk| bk.restore_first_arg(args));
|
||||||
backup.restore_first_arg(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = result.map_err(|err| err.fill_position(pos))?;
|
let result = result.map_err(|err| err.fill_position(pos))?;
|
||||||
|
|
||||||
@ -371,32 +373,24 @@ impl Engine {
|
|||||||
|
|
||||||
match fn_name {
|
match fn_name {
|
||||||
// index getter function not found?
|
// index getter function not found?
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
crate::engine::FN_IDX_GET => {
|
crate::engine::FN_IDX_GET => {
|
||||||
assert!(args.len() == 2);
|
assert!(args.len() == 2);
|
||||||
|
|
||||||
EvalAltResult::ErrorFunctionNotFound(
|
EvalAltResult::ErrorIndexingType(
|
||||||
format!(
|
self.map_type_name(args[0].type_name()).to_string(),
|
||||||
"{} [{}]",
|
|
||||||
self.map_type_name(args[0].type_name()),
|
|
||||||
self.map_type_name(args[1].type_name()),
|
|
||||||
),
|
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
// index setter function not found?
|
// index setter function not found?
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
crate::engine::FN_IDX_SET => {
|
crate::engine::FN_IDX_SET => {
|
||||||
assert!(args.len() == 3);
|
assert!(args.len() == 3);
|
||||||
|
|
||||||
EvalAltResult::ErrorFunctionNotFound(
|
EvalAltResult::ErrorIndexingType(
|
||||||
format!(
|
self.map_type_name(args[0].type_name()).to_string(),
|
||||||
"{} [{}]=",
|
|
||||||
self.map_type_name(args[0].type_name()),
|
|
||||||
self.map_type_name(args[1].type_name()),
|
|
||||||
),
|
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
@ -654,14 +648,18 @@ impl Engine {
|
|||||||
crate::engine::KEYWORD_IS_DEF_FN
|
crate::engine::KEYWORD_IS_DEF_FN
|
||||||
if args.len() == 2 && args[0].is::<FnPtr>() && args[1].is::<crate::INT>() =>
|
if args.len() == 2 && args[0].is::<FnPtr>() && args[1].is::<crate::INT>() =>
|
||||||
{
|
{
|
||||||
let fn_name = &*args[0].read_lock::<ImmutableString>().unwrap();
|
let fn_name = &*args[0]
|
||||||
let num_params = args[1].as_int().unwrap();
|
.read_lock::<ImmutableString>()
|
||||||
|
.expect("never fails because `args[0]` is `FnPtr`");
|
||||||
|
let num_params = args[1]
|
||||||
|
.as_int()
|
||||||
|
.expect("never fails because `args[1]` is `INT`");
|
||||||
|
|
||||||
return Ok((
|
return Ok((
|
||||||
if num_params < 0 {
|
if num_params < 0 {
|
||||||
Dynamic::FALSE
|
Dynamic::FALSE
|
||||||
} else {
|
} else {
|
||||||
let hash_script = calc_fn_hash(empty(), fn_name, num_params as usize);
|
let hash_script = calc_fn_hash(fn_name, num_params as usize);
|
||||||
self.has_script_fn(Some(mods), state, lib, hash_script)
|
self.has_script_fn(Some(mods), state, lib, hash_script)
|
||||||
.into()
|
.into()
|
||||||
},
|
},
|
||||||
@ -712,11 +710,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Scripted function call?
|
// Scripted function call?
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let hash_script = if hash.is_native_only() {
|
let hash_script = hash.script_hash();
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(hash.script_hash())
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
if let Some(f) = hash_script.and_then(|hash| {
|
if let Some(f) = hash_script.and_then(|hash| {
|
||||||
@ -737,21 +731,23 @@ impl Engine {
|
|||||||
|
|
||||||
// Move captured variables into scope
|
// Move captured variables into scope
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
if let Some(captured) = _capture_scope {
|
if !func.externals.is_empty() {
|
||||||
if !func.externals.is_empty() {
|
_capture_scope.map(|captured| {
|
||||||
captured
|
captured
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|(name, _, _)| func.externals.contains(name.as_ref()))
|
.filter(|(name, _, _)| func.externals.contains(name.as_ref()))
|
||||||
.for_each(|(name, value, _)| {
|
.for_each(|(name, value, _)| {
|
||||||
// Consume the scope values.
|
// Consume the scope values.
|
||||||
scope.push_dynamic(name, value);
|
scope.push_dynamic(name, value);
|
||||||
});
|
})
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = if _is_method {
|
let result = if _is_method {
|
||||||
// Method call of script function - map first argument to `this`
|
// Method call of script function - map first argument to `this`
|
||||||
let (first, rest) = args.split_first_mut().unwrap();
|
let (first, rest) = args
|
||||||
|
.split_first_mut()
|
||||||
|
.expect("never fails because a method call always has a first parameter");
|
||||||
|
|
||||||
let orig_source = state.source.take();
|
let orig_source = state.source.take();
|
||||||
state.source = source;
|
state.source = source;
|
||||||
@ -780,7 +776,7 @@ impl Engine {
|
|||||||
let mut backup: Option<ArgBackup> = None;
|
let mut backup: Option<ArgBackup> = None;
|
||||||
if is_ref && !args.is_empty() {
|
if is_ref && !args.is_empty() {
|
||||||
backup = Some(Default::default());
|
backup = Some(Default::default());
|
||||||
backup.as_mut().unwrap().change_first_arg_to_copy(args);
|
backup.as_mut().map(|bk| bk.change_first_arg_to_copy(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
let orig_source = state.source.take();
|
let orig_source = state.source.take();
|
||||||
@ -795,9 +791,7 @@ impl Engine {
|
|||||||
state.source = orig_source;
|
state.source = orig_source;
|
||||||
|
|
||||||
// Restore the original reference
|
// Restore the original reference
|
||||||
if let Some(backup) = backup {
|
backup.map(|bk| bk.restore_first_arg(args));
|
||||||
backup.restore_first_arg(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
result?
|
result?
|
||||||
};
|
};
|
||||||
@ -914,12 +908,14 @@ impl Engine {
|
|||||||
let (result, updated) = match fn_name {
|
let (result, updated) = match fn_name {
|
||||||
KEYWORD_FN_PTR_CALL if obj.is::<FnPtr>() => {
|
KEYWORD_FN_PTR_CALL if obj.is::<FnPtr>() => {
|
||||||
// FnPtr call
|
// FnPtr call
|
||||||
let fn_ptr = obj.read_lock::<FnPtr>().unwrap();
|
let fn_ptr = obj
|
||||||
|
.read_lock::<FnPtr>()
|
||||||
|
.expect("never fails because `obj` is `FnPtr`");
|
||||||
// Redirect function name
|
// Redirect function name
|
||||||
let fn_name = fn_ptr.fn_name();
|
let fn_name = fn_ptr.fn_name();
|
||||||
let args_len = call_args.len() + fn_ptr.curry().len();
|
let args_len = call_args.len() + fn_ptr.curry().len();
|
||||||
// Recalculate hashes
|
// Recalculate hashes
|
||||||
let new_hash = FnCallHashes::from_script(calc_fn_hash(empty(), fn_name, args_len));
|
let new_hash = FnCallHashes::from_script(calc_fn_hash(fn_name, args_len));
|
||||||
// Arguments are passed as-is, adding the curried arguments
|
// Arguments are passed as-is, adding the curried arguments
|
||||||
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
||||||
let mut args = curry
|
let mut args = curry
|
||||||
@ -955,8 +951,8 @@ impl Engine {
|
|||||||
let args_len = call_args.len() + fn_ptr.curry().len();
|
let args_len = call_args.len() + fn_ptr.curry().len();
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
let new_hash = FnCallHashes::from_script_and_native(
|
let new_hash = FnCallHashes::from_script_and_native(
|
||||||
calc_fn_hash(empty(), fn_name, args_len),
|
calc_fn_hash(fn_name, args_len),
|
||||||
calc_fn_hash(empty(), fn_name, args_len + 1),
|
calc_fn_hash(fn_name, args_len + 1),
|
||||||
);
|
);
|
||||||
// Replace the first argument with the object pointer, adding the curried arguments
|
// Replace the first argument with the object pointer, adding the curried arguments
|
||||||
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
||||||
@ -978,7 +974,9 @@ impl Engine {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fn_ptr = obj.read_lock::<FnPtr>().unwrap();
|
let fn_ptr = obj
|
||||||
|
.read_lock::<FnPtr>()
|
||||||
|
.expect("never fails because `obj` is `FnPtr`");
|
||||||
|
|
||||||
// Curry call
|
// Curry call
|
||||||
Ok((
|
Ok((
|
||||||
@ -1029,8 +1027,8 @@ impl Engine {
|
|||||||
});
|
});
|
||||||
// Recalculate the hash based on the new function name and new arguments
|
// Recalculate the hash based on the new function name and new arguments
|
||||||
hash = FnCallHashes::from_script_and_native(
|
hash = FnCallHashes::from_script_and_native(
|
||||||
calc_fn_hash(empty(), fn_name, call_args.len()),
|
calc_fn_hash(fn_name, call_args.len()),
|
||||||
calc_fn_hash(empty(), fn_name, call_args.len() + 1),
|
calc_fn_hash(fn_name, call_args.len() + 1),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1049,7 +1047,9 @@ impl Engine {
|
|||||||
|
|
||||||
// Propagate the changed value back to the source if necessary
|
// Propagate the changed value back to the source if necessary
|
||||||
if updated {
|
if updated {
|
||||||
target.propagate_changed_value();
|
target
|
||||||
|
.propagate_changed_value()
|
||||||
|
.map_err(|err| err.fill_position(pos))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((result, updated))
|
Ok((result, updated))
|
||||||
@ -1115,9 +1115,9 @@ impl Engine {
|
|||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
let args_len = total_args + curry.len();
|
let args_len = total_args + curry.len();
|
||||||
hashes = if !hashes.is_native_only() {
|
hashes = if !hashes.is_native_only() {
|
||||||
FnCallHashes::from_script(calc_fn_hash(empty(), name, args_len))
|
FnCallHashes::from_script(calc_fn_hash(name, args_len))
|
||||||
} else {
|
} else {
|
||||||
FnCallHashes::from_native(calc_fn_hash(empty(), name, args_len))
|
FnCallHashes::from_native(calc_fn_hash(name, args_len))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Handle Fn()
|
// Handle Fn()
|
||||||
@ -1214,7 +1214,7 @@ impl Engine {
|
|||||||
return Ok(if num_params < 0 {
|
return Ok(if num_params < 0 {
|
||||||
Dynamic::FALSE
|
Dynamic::FALSE
|
||||||
} else {
|
} else {
|
||||||
let hash_script = calc_fn_hash(empty(), &fn_name, num_params as usize);
|
let hash_script = calc_fn_hash(&fn_name, num_params as usize);
|
||||||
self.has_script_fn(Some(mods), state, lib, hash_script)
|
self.has_script_fn(Some(mods), state, lib, hash_script)
|
||||||
.into()
|
.into()
|
||||||
});
|
});
|
||||||
@ -1332,9 +1332,8 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
// Turn it into a method call only if the object is not shared and not a simple value
|
// Turn it into a method call only if the object is not shared and not a simple value
|
||||||
is_ref = true;
|
is_ref = true;
|
||||||
once(target.take_ref().unwrap())
|
let obj_ref = target.take_ref().expect("never fails because `target` is a reference if it is not a value and not shared");
|
||||||
.chain(arg_values.iter_mut())
|
once(obj_ref).chain(arg_values.iter_mut()).collect()
|
||||||
.collect()
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// func(..., ...)
|
// func(..., ...)
|
||||||
@ -1365,7 +1364,7 @@ impl Engine {
|
|||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
namespace: Option<&NamespaceRef>,
|
namespace: &NamespaceRef,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
literal_args: &[(Dynamic, Position)],
|
literal_args: &[(Dynamic, Position)],
|
||||||
@ -1373,7 +1372,6 @@ impl Engine {
|
|||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let namespace = namespace.unwrap();
|
|
||||||
let mut arg_values: StaticVec<_>;
|
let mut arg_values: StaticVec<_>;
|
||||||
let mut first_arg_value = None;
|
let mut first_arg_value = None;
|
||||||
let mut args: StaticVec<_>;
|
let mut args: StaticVec<_>;
|
||||||
@ -1418,11 +1416,12 @@ impl Engine {
|
|||||||
arg_values[0] = target.take_or_clone().flatten();
|
arg_values[0] = target.take_or_clone().flatten();
|
||||||
args = arg_values.iter_mut().collect();
|
args = arg_values.iter_mut().collect();
|
||||||
} else {
|
} else {
|
||||||
let (first, rest) = arg_values.split_first_mut().unwrap();
|
let (first, rest) = arg_values
|
||||||
|
.split_first_mut()
|
||||||
|
.expect("never fails because the arguments list is not empty");
|
||||||
first_arg_value = Some(first);
|
first_arg_value = Some(first);
|
||||||
args = once(target.take_ref().unwrap())
|
let obj_ref = target.take_ref().expect("never fails because `target` is a reference if it is not a value and not shared");
|
||||||
.chain(rest.iter_mut())
|
args = once(obj_ref).chain(rest.iter_mut()).collect();
|
||||||
.collect();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// func(..., ...) or func(mod::x, ...)
|
// func(..., ...) or func(mod::x, ...)
|
||||||
@ -1459,12 +1458,11 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Clone first argument if the function is not a method after-all
|
// Clone first argument if the function is not a method after-all
|
||||||
if let Some(first) = first_arg_value {
|
if !func.map(|f| f.is_method()).unwrap_or(true) {
|
||||||
if !func.map(|f| f.is_method()).unwrap_or(true) {
|
first_arg_value.map(|first| {
|
||||||
let first_val = args[0].clone();
|
*first = args[0].clone();
|
||||||
args[0] = first;
|
args[0] = first;
|
||||||
*args[0] = first_val;
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match func {
|
match func {
|
||||||
|
@ -13,7 +13,7 @@ use std::prelude::v1::*;
|
|||||||
use std::{
|
use std::{
|
||||||
convert::{TryFrom, TryInto},
|
convert::{TryFrom, TryInto},
|
||||||
fmt,
|
fmt,
|
||||||
iter::{empty, once},
|
iter::once,
|
||||||
mem,
|
mem,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -202,11 +202,11 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
|
|
||||||
let hash = if is_method {
|
let hash = if is_method {
|
||||||
FnCallHashes::from_script_and_native(
|
FnCallHashes::from_script_and_native(
|
||||||
calc_fn_hash(empty(), fn_name, args.len() - 1),
|
calc_fn_hash(fn_name, args.len() - 1),
|
||||||
calc_fn_hash(empty(), fn_name, args.len()),
|
calc_fn_hash(fn_name, args.len()),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
FnCallHashes::from_script(calc_fn_hash(empty(), fn_name, args.len()))
|
FnCallHashes::from_script(calc_fn_hash(fn_name, args.len()))
|
||||||
};
|
};
|
||||||
|
|
||||||
self.engine()
|
self.engine()
|
||||||
@ -253,7 +253,9 @@ pub fn shared_try_take<T>(value: Shared<T>) -> Result<T, Shared<T>> {
|
|||||||
/// Panics if the resource is shared (i.e. has other outstanding references).
|
/// Panics if the resource is shared (i.e. has other outstanding references).
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn shared_take<T>(value: Shared<T>) -> T {
|
pub fn shared_take<T>(value: Shared<T>) -> T {
|
||||||
shared_try_take(value).map_err(|_| ()).unwrap()
|
shared_try_take(value)
|
||||||
|
.ok()
|
||||||
|
.expect("resource should have no outstanding references")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Arguments to a function call, which is a list of [`&mut Dynamic`][Dynamic].
|
/// Arguments to a function call, which is a list of [`&mut Dynamic`][Dynamic].
|
||||||
|
@ -32,7 +32,8 @@ pub struct Mut<T>(T);
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn by_ref<T: Variant + Clone>(data: &mut Dynamic) -> DynamicWriteLock<T> {
|
pub fn by_ref<T: Variant + Clone>(data: &mut Dynamic) -> DynamicWriteLock<T> {
|
||||||
// Directly cast the &mut Dynamic into DynamicWriteLock to access the underlying data.
|
// Directly cast the &mut Dynamic into DynamicWriteLock to access the underlying data.
|
||||||
data.write_lock::<T>().unwrap()
|
data.write_lock::<T>()
|
||||||
|
.expect("never fails because the type was checked")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dereference into value.
|
/// Dereference into value.
|
||||||
@ -43,12 +44,15 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
|
|||||||
data.flatten_in_place();
|
data.flatten_in_place();
|
||||||
let ref_str = data
|
let ref_str = data
|
||||||
.as_str_ref()
|
.as_str_ref()
|
||||||
.expect("argument passed by value should not be shared");
|
.expect("never fails because argument passed by value should not be shared");
|
||||||
let ref_t = unsafe { mem::transmute::<_, &T>(&ref_str) };
|
let ref_t = unsafe { mem::transmute::<_, &T>(&ref_str) };
|
||||||
ref_t.clone()
|
ref_t.clone()
|
||||||
} else if TypeId::of::<T>() == TypeId::of::<String>() {
|
} else if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||||
// If T is `String`, data must be `ImmutableString`, so map directly to it
|
// If T is `String`, data must be `ImmutableString`, so map directly to it
|
||||||
unsafe_try_cast(mem::take(data).take_string().unwrap()).unwrap()
|
let value = mem::take(data)
|
||||||
|
.take_string()
|
||||||
|
.expect("never fails because the type was checked");
|
||||||
|
unsafe_try_cast(value).expect("never fails because the type was checked")
|
||||||
} else {
|
} else {
|
||||||
// We consume the argument and then replace it with () - the argument is not supposed to be used again.
|
// We consume the argument and then replace it with () - the argument is not supposed to be used again.
|
||||||
// This way, we avoid having to clone the argument again, because it is already a clone when passed here.
|
// This way, we avoid having to clone the argument again, because it is already a clone when passed here.
|
||||||
@ -118,7 +122,7 @@ macro_rules! def_register {
|
|||||||
|
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
let mut _drain = args.iter_mut();
|
let mut _drain = args.iter_mut();
|
||||||
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
$($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )*
|
||||||
|
|
||||||
// Call the function with each argument value
|
// Call the function with each argument value
|
||||||
let r = self($($arg),*);
|
let r = self($($arg),*);
|
||||||
@ -146,7 +150,7 @@ macro_rules! def_register {
|
|||||||
|
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
let mut _drain = args.iter_mut();
|
let mut _drain = args.iter_mut();
|
||||||
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
$($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )*
|
||||||
|
|
||||||
// Call the function with each argument value
|
// Call the function with each argument value
|
||||||
let r = self(ctx, $($arg),*);
|
let r = self(ctx, $($arg),*);
|
||||||
@ -174,7 +178,7 @@ macro_rules! def_register {
|
|||||||
|
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
let mut _drain = args.iter_mut();
|
let mut _drain = args.iter_mut();
|
||||||
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
$($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )*
|
||||||
|
|
||||||
// Call the function with each argument value
|
// Call the function with each argument value
|
||||||
self($($arg),*).map(Dynamic::from)
|
self($($arg),*).map(Dynamic::from)
|
||||||
@ -199,7 +203,7 @@ macro_rules! def_register {
|
|||||||
|
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
let mut _drain = args.iter_mut();
|
let mut _drain = args.iter_mut();
|
||||||
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
$($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )*
|
||||||
|
|
||||||
// Call the function with each argument value
|
// Call the function with each argument value
|
||||||
self(ctx, $($arg),*).map(Dynamic::from)
|
self(ctx, $($arg),*).map(Dynamic::from)
|
||||||
|
@ -169,7 +169,7 @@ pub use fn_native::Shared;
|
|||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
use fn_native::Locked;
|
use fn_native::Locked;
|
||||||
|
|
||||||
pub(crate) use utils::{calc_fn_hash, calc_fn_params_hash, combine_hashes};
|
pub(crate) use utils::{calc_fn_hash, calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes};
|
||||||
|
|
||||||
pub use rhai_codegen::*;
|
pub use rhai_codegen::*;
|
||||||
|
|
||||||
|
@ -7,8 +7,8 @@ use crate::fn_register::RegisterNativeFunction;
|
|||||||
use crate::token::Token;
|
use crate::token::Token;
|
||||||
use crate::utils::IdentifierBuilder;
|
use crate::utils::IdentifierBuilder;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, EvalAltResult, Identifier,
|
calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, EvalAltResult,
|
||||||
ImmutableString, NativeCallContext, Position, Shared, StaticVec,
|
Identifier, ImmutableString, NativeCallContext, Position, Shared, StaticVec,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -24,7 +24,6 @@ use std::{
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::Array;
|
use crate::Array;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
|
||||||
@ -117,7 +116,7 @@ fn calc_native_fn_hash<'a>(
|
|||||||
fn_name: impl AsRef<str>,
|
fn_name: impl AsRef<str>,
|
||||||
params: &[TypeId],
|
params: &[TypeId],
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let hash_script = calc_fn_hash(modules, fn_name, params.len());
|
let hash_script = calc_qualified_fn_hash(modules, fn_name, params.len());
|
||||||
let hash_params = calc_fn_params_hash(params.iter().cloned());
|
let hash_params = calc_fn_params_hash(params.iter().cloned());
|
||||||
combine_hashes(hash_script, hash_params)
|
combine_hashes(hash_script, hash_params)
|
||||||
}
|
}
|
||||||
@ -156,20 +155,7 @@ pub struct Module {
|
|||||||
impl Default for Module {
|
impl Default for Module {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self::new()
|
||||||
id: None,
|
|
||||||
internal: false,
|
|
||||||
modules: Default::default(),
|
|
||||||
variables: Default::default(),
|
|
||||||
all_variables: Default::default(),
|
|
||||||
functions: Default::default(),
|
|
||||||
all_functions: Default::default(),
|
|
||||||
type_iterators: Default::default(),
|
|
||||||
all_type_iterators: Default::default(),
|
|
||||||
indexed: true,
|
|
||||||
contains_indexed_global_functions: false,
|
|
||||||
identifiers: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,9 +163,7 @@ impl fmt::Debug for Module {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let mut d = f.debug_struct("Module");
|
let mut d = f.debug_struct("Module");
|
||||||
|
|
||||||
if let Some(ref id) = self.id {
|
self.id.as_ref().map(|id| d.field("id", id));
|
||||||
d.field("id", id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.modules.is_empty() {
|
if !self.modules.is_empty() {
|
||||||
d.field(
|
d.field(
|
||||||
@ -257,7 +241,20 @@ impl Module {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Default::default()
|
Self {
|
||||||
|
id: None,
|
||||||
|
internal: false,
|
||||||
|
modules: Default::default(),
|
||||||
|
variables: Default::default(),
|
||||||
|
all_variables: Default::default(),
|
||||||
|
functions: Default::default(),
|
||||||
|
all_functions: Default::default(),
|
||||||
|
type_iterators: Default::default(),
|
||||||
|
all_type_iterators: Default::default(),
|
||||||
|
indexed: true,
|
||||||
|
contains_indexed_global_functions: false,
|
||||||
|
identifiers: Default::default(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the ID of the [`Module`], if any.
|
/// Get the ID of the [`Module`], if any.
|
||||||
@ -441,7 +438,7 @@ impl Module {
|
|||||||
let value = Dynamic::from(value);
|
let value = Dynamic::from(value);
|
||||||
|
|
||||||
if self.indexed {
|
if self.indexed {
|
||||||
let hash_var = crate::calc_fn_hash(once(""), &ident, 0);
|
let hash_var = crate::calc_qualified_fn_hash(once(""), &ident, 0);
|
||||||
self.all_variables.insert(hash_var, value.clone());
|
self.all_variables.insert(hash_var, value.clone());
|
||||||
}
|
}
|
||||||
self.variables.insert(ident, value);
|
self.variables.insert(ident, value);
|
||||||
@ -467,7 +464,7 @@ impl Module {
|
|||||||
|
|
||||||
// None + function name + number of arguments.
|
// None + function name + number of arguments.
|
||||||
let num_params = fn_def.params.len();
|
let num_params = fn_def.params.len();
|
||||||
let hash_script = crate::calc_fn_hash(empty(), &fn_def.name, num_params);
|
let hash_script = crate::calc_fn_hash(&fn_def.name, num_params);
|
||||||
let mut param_names = fn_def.params.clone();
|
let mut param_names = fn_def.params.clone();
|
||||||
param_names.push("Dynamic".into());
|
param_names.push("Dynamic".into());
|
||||||
self.functions.insert(
|
self.functions.insert(
|
||||||
@ -622,9 +619,10 @@ impl Module {
|
|||||||
.map(|&name| self.identifiers.get(name))
|
.map(|&name| self.identifiers.get(name))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if let Some(f) = self.functions.get_mut(&hash_fn) {
|
self.functions
|
||||||
f.param_names = param_names;
|
.get_mut(&hash_fn)
|
||||||
}
|
.map(|f| f.param_names = param_names);
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -956,7 +954,7 @@ impl Module {
|
|||||||
/// });
|
/// });
|
||||||
/// assert!(module.contains_fn(hash));
|
/// assert!(module.contains_fn(hash));
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_indexer_get_fn<ARGS, A, B, T, F>(&mut self, func: F) -> u64
|
pub fn set_indexer_get_fn<ARGS, A, B, T, F>(&mut self, func: F) -> u64
|
||||||
where
|
where
|
||||||
@ -966,6 +964,7 @@ impl Module {
|
|||||||
F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>,
|
F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>,
|
||||||
F: Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
F: Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
{
|
{
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
}
|
}
|
||||||
@ -1016,7 +1015,7 @@ impl Module {
|
|||||||
/// });
|
/// });
|
||||||
/// assert!(module.contains_fn(hash));
|
/// assert!(module.contains_fn(hash));
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_indexer_set_fn<ARGS, A, B, C, F>(&mut self, func: F) -> u64
|
pub fn set_indexer_set_fn<ARGS, A, B, C, F>(&mut self, func: F) -> u64
|
||||||
where
|
where
|
||||||
@ -1026,6 +1025,7 @@ impl Module {
|
|||||||
F: RegisterNativeFunction<ARGS, Result<(), Box<EvalAltResult>>>,
|
F: RegisterNativeFunction<ARGS, Result<(), Box<EvalAltResult>>>,
|
||||||
F: Fn(&mut A, B, C) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
F: Fn(&mut A, B, C) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
{
|
{
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
}
|
}
|
||||||
@ -1082,7 +1082,7 @@ impl Module {
|
|||||||
/// assert!(module.contains_fn(hash_get));
|
/// assert!(module.contains_fn(hash_get));
|
||||||
/// assert!(module.contains_fn(hash_set));
|
/// assert!(module.contains_fn(hash_set));
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_indexer_get_set_fn<A, B, T>(
|
pub fn set_indexer_get_set_fn<A, B, T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -1414,7 +1414,10 @@ impl Module {
|
|||||||
match aliases.len() {
|
match aliases.len() {
|
||||||
0 => (),
|
0 => (),
|
||||||
1 => {
|
1 => {
|
||||||
module.set_var(aliases.pop().unwrap(), value);
|
let alias = aliases
|
||||||
|
.pop()
|
||||||
|
.expect("never fails because the list has one item");
|
||||||
|
module.set_var(alias, value);
|
||||||
}
|
}
|
||||||
_ => aliases.into_iter().for_each(|alias| {
|
_ => aliases.into_iter().for_each(|alias| {
|
||||||
module.set_var(alias, value.clone());
|
module.set_var(alias, value.clone());
|
||||||
@ -1490,7 +1493,7 @@ impl Module {
|
|||||||
|
|
||||||
// Index all variables
|
// Index all variables
|
||||||
module.variables.iter().for_each(|(var_name, value)| {
|
module.variables.iter().for_each(|(var_name, value)| {
|
||||||
let hash_var = crate::calc_fn_hash(path.iter().map(|&v| v), var_name, 0);
|
let hash_var = crate::calc_qualified_fn_hash(path.iter().map(|&v| v), var_name, 0);
|
||||||
variables.insert(hash_var, value.clone());
|
variables.insert(hash_var, value.clone());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1520,8 +1523,11 @@ impl Module {
|
|||||||
calc_native_fn_hash(path.iter().cloned(), f.name.as_str(), &f.param_types);
|
calc_native_fn_hash(path.iter().cloned(), f.name.as_str(), &f.param_types);
|
||||||
functions.insert(hash_qualified_fn, f.func.clone());
|
functions.insert(hash_qualified_fn, f.func.clone());
|
||||||
} else if cfg!(not(feature = "no_function")) {
|
} else if cfg!(not(feature = "no_function")) {
|
||||||
let hash_qualified_script =
|
let hash_qualified_script = crate::calc_qualified_fn_hash(
|
||||||
crate::calc_fn_hash(path.iter().cloned(), f.name.as_str(), f.params);
|
path.iter().cloned(),
|
||||||
|
f.name.as_str(),
|
||||||
|
f.params,
|
||||||
|
);
|
||||||
functions.insert(hash_qualified_script, f.func.clone());
|
functions.insert(hash_qualified_script, f.func.clone());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -31,8 +31,8 @@ impl DummyModuleResolver {
|
|||||||
/// engine.set_module_resolver(resolver);
|
/// engine.set_module_resolver(resolver);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Default::default()
|
Self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
118
src/optimize.rs
118
src/optimize.rs
@ -4,7 +4,6 @@ use crate::ast::{Expr, OpAssignment, Stmt};
|
|||||||
use crate::dynamic::AccessMode;
|
use crate::dynamic::AccessMode;
|
||||||
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
||||||
use crate::fn_builtin::get_builtin_binary_op_fn;
|
use crate::fn_builtin::get_builtin_binary_op_fn;
|
||||||
use crate::parser::map_dynamic_to_expr;
|
|
||||||
use crate::token::Token;
|
use crate::token::Token;
|
||||||
use crate::utils::get_hasher;
|
use crate::utils::get_hasher;
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -16,7 +15,6 @@ use std::prelude::v1::*;
|
|||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
iter::empty,
|
|
||||||
mem,
|
mem,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -32,24 +30,14 @@ pub enum OptimizationLevel {
|
|||||||
Full,
|
Full,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OptimizationLevel {
|
impl Default for OptimizationLevel {
|
||||||
/// Is the `OptimizationLevel` [`None`][OptimizationLevel::None]?
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_none(self) -> bool {
|
fn default() -> Self {
|
||||||
self == Self::None
|
if cfg!(feature = "no_optimize") {
|
||||||
}
|
Self::None
|
||||||
/// Is the `OptimizationLevel` [`Simple`][OptimizationLevel::Simple]?
|
} else {
|
||||||
#[allow(dead_code)]
|
Self::Simple
|
||||||
#[inline(always)]
|
}
|
||||||
pub fn is_simple(self) -> bool {
|
|
||||||
self == Self::Simple
|
|
||||||
}
|
|
||||||
/// Is the `OptimizationLevel` [`Full`][OptimizationLevel::Full]?
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_full(self) -> bool {
|
|
||||||
self == Self::Full
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +47,7 @@ struct State<'a> {
|
|||||||
/// Has the [`AST`] been changed during this pass?
|
/// Has the [`AST`] been changed during this pass?
|
||||||
changed: bool,
|
changed: bool,
|
||||||
/// Collection of constants to use for eager function evaluations.
|
/// Collection of constants to use for eager function evaluations.
|
||||||
variables: Vec<(String, AccessMode, Expr)>,
|
variables: Vec<(String, AccessMode, Option<Dynamic>)>,
|
||||||
/// Activate constants propagation?
|
/// Activate constants propagation?
|
||||||
propagate_constants: bool,
|
propagate_constants: bool,
|
||||||
/// An [`Engine`] instance for eager function evaluation.
|
/// An [`Engine`] instance for eager function evaluation.
|
||||||
@ -109,21 +97,21 @@ impl<'a> State<'a> {
|
|||||||
}
|
}
|
||||||
/// Add a new constant to the list.
|
/// Add a new constant to the list.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push_var(&mut self, name: &str, access: AccessMode, value: Expr) {
|
pub fn push_var(&mut self, name: &str, access: AccessMode, value: Option<Dynamic>) {
|
||||||
self.variables.push((name.into(), access, value))
|
self.variables.push((name.into(), access, value))
|
||||||
}
|
}
|
||||||
/// Look up a constant from the list.
|
/// Look up a constant from the list.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn find_constant(&self, name: &str) -> Option<&Expr> {
|
pub fn find_constant(&self, name: &str) -> Option<&Dynamic> {
|
||||||
if !self.propagate_constants {
|
if !self.propagate_constants {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.variables.iter().rev().find_map(|(n, access, expr)| {
|
self.variables.iter().rev().find_map(|(n, access, value)| {
|
||||||
if n == name {
|
if n == name {
|
||||||
match access {
|
match access {
|
||||||
AccessMode::ReadWrite => None,
|
AccessMode::ReadWrite => None,
|
||||||
AccessMode::ReadOnly => Some(expr),
|
AccessMode::ReadOnly => value.as_ref(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -158,7 +146,7 @@ fn call_fn_with_constant_arguments(
|
|||||||
&mut Default::default(),
|
&mut Default::default(),
|
||||||
state.lib,
|
state.lib,
|
||||||
fn_name,
|
fn_name,
|
||||||
calc_fn_hash(empty(), fn_name, arg_values.len()),
|
calc_fn_hash(fn_name, arg_values.len()),
|
||||||
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
|
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
@ -217,13 +205,17 @@ fn optimize_stmt_block(
|
|||||||
optimize_expr(value_expr, state);
|
optimize_expr(value_expr, state);
|
||||||
|
|
||||||
if value_expr.is_constant() {
|
if value_expr.is_constant() {
|
||||||
state.push_var(&x.name, AccessMode::ReadOnly, value_expr.clone());
|
state.push_var(
|
||||||
|
&x.name,
|
||||||
|
AccessMode::ReadOnly,
|
||||||
|
value_expr.get_constant_value(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add variables into the state
|
// Add variables into the state
|
||||||
Stmt::Let(value_expr, x, _, _) => {
|
Stmt::Let(value_expr, x, _, _) => {
|
||||||
optimize_expr(value_expr, state);
|
optimize_expr(value_expr, state);
|
||||||
state.push_var(&x.name, AccessMode::ReadWrite, Expr::Unit(x.pos));
|
state.push_var(&x.name, AccessMode::ReadWrite, None);
|
||||||
}
|
}
|
||||||
// Optimize the statement
|
// Optimize the statement
|
||||||
_ => optimize_stmt(stmt, state, preserve_result),
|
_ => optimize_stmt(stmt, state, preserve_result),
|
||||||
@ -512,12 +504,13 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
Stmt::Switch(match_expr, x, _) => {
|
Stmt::Switch(match_expr, x, _) => {
|
||||||
optimize_expr(match_expr, state);
|
optimize_expr(match_expr, state);
|
||||||
x.0.values_mut().for_each(|block| {
|
x.0.values_mut().for_each(|block| {
|
||||||
let condition = if let Some(mut condition) = mem::take(&mut block.0) {
|
let condition = mem::take(&mut block.0).map_or_else(
|
||||||
optimize_expr(&mut condition, state);
|
|| Expr::Unit(Position::NONE),
|
||||||
condition
|
|mut condition| {
|
||||||
} else {
|
optimize_expr(&mut condition, state);
|
||||||
Expr::Unit(Position::NONE)
|
condition
|
||||||
};
|
},
|
||||||
|
);
|
||||||
|
|
||||||
match condition {
|
match condition {
|
||||||
Expr::Unit(_) | Expr::BoolConstant(true, _) => (),
|
Expr::Unit(_) | Expr::BoolConstant(true, _) => (),
|
||||||
@ -703,7 +696,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
Expr::Dot(x, _) => match (&mut x.lhs, &mut x.rhs) {
|
Expr::Dot(x, _) => match (&mut x.lhs, &mut x.rhs) {
|
||||||
// map.string
|
// map.string
|
||||||
(Expr::Map(m, pos), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
(Expr::Map(m, pos), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
||||||
let prop = &p.2.name;
|
let prop = p.2.0.as_str();
|
||||||
// Map literal where everything is pure - promote the indexed item.
|
// Map literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
@ -925,15 +918,16 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
|
|
||||||
// Search for overloaded operators (can override built-in).
|
// Search for overloaded operators (can override built-in).
|
||||||
if !has_native_fn(state, x.hashes.native_hash(), arg_types.as_ref()) {
|
if !has_native_fn(state, x.hashes.native_hash(), arg_types.as_ref()) {
|
||||||
if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
if let Some(mut result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
||||||
.and_then(|f| {
|
.and_then(|f| {
|
||||||
let ctx = (state.engine, x.name.as_ref(), state.lib).into();
|
let ctx = (state.engine, x.name.as_ref(), state.lib).into();
|
||||||
let (first, second) = arg_values.split_first_mut().unwrap();
|
let (first, second) = arg_values.split_first_mut().unwrap();
|
||||||
(f)(ctx, &mut [ first, &mut second[0] ]).ok()
|
(f)(ctx, &mut [ first, &mut second[0] ]).ok()
|
||||||
})
|
})
|
||||||
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
.map(Expr::from)
|
||||||
{
|
{
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
result.set_position(*pos);
|
||||||
*expr = result;
|
*expr = result;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -977,18 +971,19 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
""
|
""
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(result) = call_fn_with_constant_arguments(&state, x.name.as_ref(), &mut arg_values)
|
if let Some(mut result) = call_fn_with_constant_arguments(&state, x.name.as_ref(), &mut arg_values)
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
if !arg_for_type_of.is_empty() {
|
if !arg_for_type_of.is_empty() {
|
||||||
// Handle `type_of()`
|
// Handle `type_of()`
|
||||||
Some(arg_for_type_of.to_string().into())
|
Some(arg_for_type_of.to_string().into())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
.map(Expr::from)
|
||||||
{
|
{
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
result.set_position(*pos);
|
||||||
*expr = result;
|
*expr = result;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1014,12 +1009,11 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
|
|
||||||
// constant-name
|
// constant-name
|
||||||
Expr::Variable(_, pos, x) if x.1.is_none() && state.find_constant(&x.2).is_some() => {
|
Expr::Variable(_, pos, x) if x.1.is_none() && state.find_constant(&x.2).is_some() => {
|
||||||
state.set_dirty();
|
|
||||||
|
|
||||||
// Replace constant with value
|
// Replace constant with value
|
||||||
let mut result = state.find_constant(&x.2).unwrap().clone();
|
let pos = *pos;
|
||||||
result.set_position(*pos);
|
*expr = Expr::from(state.find_constant(&x.2).unwrap().clone());
|
||||||
*expr = result;
|
expr.set_position(pos);
|
||||||
|
state.set_dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom syntax
|
// Custom syntax
|
||||||
@ -1055,13 +1049,9 @@ fn optimize_top_level(
|
|||||||
// Add constants and variables from the scope
|
// Add constants and variables from the scope
|
||||||
scope.iter().for_each(|(name, constant, value)| {
|
scope.iter().for_each(|(name, constant, value)| {
|
||||||
if !constant {
|
if !constant {
|
||||||
state.push_var(name, AccessMode::ReadWrite, Expr::Unit(Position::NONE));
|
state.push_var(name, AccessMode::ReadWrite, None);
|
||||||
} else {
|
} else {
|
||||||
state.push_var(
|
state.push_var(name, AccessMode::ReadOnly, Some(value));
|
||||||
name,
|
|
||||||
AccessMode::ReadOnly,
|
|
||||||
Expr::DynamicConstant(Box::new(value), Position::NONE),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1078,7 +1068,7 @@ pub fn optimize_into_ast(
|
|||||||
optimization_level: OptimizationLevel,
|
optimization_level: OptimizationLevel,
|
||||||
) -> AST {
|
) -> AST {
|
||||||
let level = if cfg!(feature = "no_optimize") {
|
let level = if cfg!(feature = "no_optimize") {
|
||||||
OptimizationLevel::None
|
Default::default()
|
||||||
} else {
|
} else {
|
||||||
optimization_level
|
optimization_level
|
||||||
};
|
};
|
||||||
@ -1087,7 +1077,7 @@ pub fn optimize_into_ast(
|
|||||||
let lib = {
|
let lib = {
|
||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
|
|
||||||
if !level.is_none() {
|
if level != OptimizationLevel::None {
|
||||||
// We only need the script library's signatures for optimization purposes
|
// We only need the script library's signatures for optimization purposes
|
||||||
let mut lib2 = Module::new();
|
let mut lib2 = Module::new();
|
||||||
|
|
||||||
|
@ -604,27 +604,13 @@ mod array_functions {
|
|||||||
.call_dynamic(&ctx, None, [x.clone(), y.clone()])
|
.call_dynamic(&ctx, None, [x.clone(), y.clone()])
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|v| v.as_int().ok())
|
.and_then(|v| v.as_int().ok())
|
||||||
.map(|v| {
|
.map(|v| match v {
|
||||||
if v > 0 {
|
v if v > 0 => Ordering::Greater,
|
||||||
Ordering::Greater
|
v if v < 0 => Ordering::Less,
|
||||||
} else if v < 0 {
|
0 => Ordering::Equal,
|
||||||
Ordering::Less
|
_ => unreachable!(),
|
||||||
} else {
|
|
||||||
Ordering::Equal
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
let x_type_id = x.type_id();
|
|
||||||
let y_type_id = y.type_id();
|
|
||||||
|
|
||||||
if x_type_id > y_type_id {
|
|
||||||
Ordering::Greater
|
|
||||||
} else if x_type_id < y_type_id {
|
|
||||||
Ordering::Less
|
|
||||||
} else {
|
|
||||||
Ordering::Equal
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
.unwrap_or_else(|| x.type_id().cmp(&y.type_id()))
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -45,29 +45,31 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
|
|||||||
namespace: Option<Identifier>,
|
namespace: Option<Identifier>,
|
||||||
f: &ScriptFnDef,
|
f: &ScriptFnDef,
|
||||||
) -> Map {
|
) -> Map {
|
||||||
|
const DICT: &str = "never fails because the dictionary is pre-filled with all the keys";
|
||||||
|
|
||||||
let mut map = Map::new();
|
let mut map = Map::new();
|
||||||
|
|
||||||
if let Some(ns) = namespace {
|
if let Some(ns) = namespace {
|
||||||
map.insert(dict.get("namespace").unwrap().clone().into(), ns.into());
|
map.insert(dict.get("namespace").expect(DICT).clone().into(), ns.into());
|
||||||
}
|
}
|
||||||
map.insert(
|
map.insert(
|
||||||
dict.get("name").unwrap().clone().into(),
|
dict.get("name").expect(DICT).clone().into(),
|
||||||
f.name.clone().into(),
|
f.name.clone().into(),
|
||||||
);
|
);
|
||||||
map.insert(
|
map.insert(
|
||||||
dict.get("access").unwrap().clone().into(),
|
dict.get("access").expect(DICT).clone().into(),
|
||||||
match f.access {
|
match f.access {
|
||||||
FnAccess::Public => dict.get("public").unwrap().clone(),
|
FnAccess::Public => dict.get("public").expect(DICT).clone(),
|
||||||
FnAccess::Private => dict.get("private").unwrap().clone(),
|
FnAccess::Private => dict.get("private").expect(DICT).clone(),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
map.insert(
|
map.insert(
|
||||||
dict.get("is_anonymous").unwrap().clone().into(),
|
dict.get("is_anonymous").expect(DICT).clone().into(),
|
||||||
f.name.starts_with(crate::engine::FN_ANONYMOUS).into(),
|
f.name.starts_with(crate::engine::FN_ANONYMOUS).into(),
|
||||||
);
|
);
|
||||||
map.insert(
|
map.insert(
|
||||||
dict.get("params").unwrap().clone().into(),
|
dict.get("params").expect(DICT).clone().into(),
|
||||||
f.params
|
f.params
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
|
@ -311,21 +311,14 @@ mod decimal_functions {
|
|||||||
|
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn sqrt(x: Decimal) -> Result<Decimal, Box<EvalAltResult>> {
|
pub fn sqrt(x: Decimal) -> Result<Decimal, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
x.sqrt()
|
||||||
x.sqrt()
|
.ok_or_else(|| make_err(format!("Error taking the square root of {}", x,)))
|
||||||
.ok_or_else(|| make_err(format!("Error taking the square root of {}", x,)))
|
|
||||||
} else {
|
|
||||||
Ok(x.sqrt().unwrap())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn exp(x: Decimal) -> Result<Decimal, Box<EvalAltResult>> {
|
pub fn exp(x: Decimal) -> Result<Decimal, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
if x > Decimal::from_parts(117578, 0, 0, false, 4) {
|
x.checked_exp()
|
||||||
Err(make_err(format!("Exponential overflow: e ** {}", x,)))
|
.ok_or_else(|| make_err(format!("Exponential overflow: e ** {}", x,)))
|
||||||
} else {
|
|
||||||
Ok(x.exp())
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Ok(x.exp())
|
Ok(x.exp())
|
||||||
}
|
}
|
||||||
@ -348,15 +341,15 @@ mod decimal_functions {
|
|||||||
#[rhai_fn(name = "round", return_raw)]
|
#[rhai_fn(name = "round", return_raw)]
|
||||||
pub fn round_dp(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
|
pub fn round_dp(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
|
||||||
return Ok(x);
|
|
||||||
}
|
|
||||||
if dp < 0 {
|
if dp < 0 {
|
||||||
return Err(make_err(format!(
|
return Err(make_err(format!(
|
||||||
"Decimal value {} round to a negative index: {}",
|
"Invalid number of digits for rounding: {}",
|
||||||
x, dp
|
dp
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
||||||
|
return Ok(x);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(x.round_dp(dp as u32))
|
Ok(x.round_dp(dp as u32))
|
||||||
@ -364,15 +357,15 @@ mod decimal_functions {
|
|||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn round_up(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
|
pub fn round_up(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
|
||||||
return Ok(x);
|
|
||||||
}
|
|
||||||
if dp < 0 {
|
if dp < 0 {
|
||||||
return Err(make_err(format!(
|
return Err(make_err(format!(
|
||||||
"Decimal value {} round to a negative index: {}",
|
"Invalid number of digits for rounding: {}",
|
||||||
x, dp
|
dp
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
||||||
|
return Ok(x);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::AwayFromZero))
|
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::AwayFromZero))
|
||||||
@ -380,15 +373,15 @@ mod decimal_functions {
|
|||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn round_down(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
|
pub fn round_down(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
|
||||||
return Ok(x);
|
|
||||||
}
|
|
||||||
if dp < 0 {
|
if dp < 0 {
|
||||||
return Err(make_err(format!(
|
return Err(make_err(format!(
|
||||||
"Decimal value {} round to a negative index: {}",
|
"Invalid number of digits for rounding: {}",
|
||||||
x, dp
|
dp
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
||||||
|
return Ok(x);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::ToZero))
|
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::ToZero))
|
||||||
@ -396,15 +389,15 @@ mod decimal_functions {
|
|||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn round_half_up(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
|
pub fn round_half_up(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
|
||||||
return Ok(x);
|
|
||||||
}
|
|
||||||
if dp < 0 {
|
if dp < 0 {
|
||||||
return Err(make_err(format!(
|
return Err(make_err(format!(
|
||||||
"Decimal value {} round to a negative index: {}",
|
"Invalid number of digits for rounding: {}",
|
||||||
x, dp
|
dp
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
||||||
|
return Ok(x);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::MidpointAwayFromZero))
|
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::MidpointAwayFromZero))
|
||||||
@ -412,15 +405,15 @@ mod decimal_functions {
|
|||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn round_half_down(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
|
pub fn round_half_down(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
|
||||||
return Ok(x);
|
|
||||||
}
|
|
||||||
if dp < 0 {
|
if dp < 0 {
|
||||||
return Err(make_err(format!(
|
return Err(make_err(format!(
|
||||||
"Decimal value {} round to a negative index: {}",
|
"Invalid number of digits for rounding: {}",
|
||||||
x, dp
|
dp
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
||||||
|
return Ok(x);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::MidpointTowardZero))
|
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::MidpointTowardZero))
|
||||||
|
@ -27,9 +27,9 @@ pub fn print_with_func(
|
|||||||
value: &mut Dynamic,
|
value: &mut Dynamic,
|
||||||
) -> crate::ImmutableString {
|
) -> crate::ImmutableString {
|
||||||
match ctx.call_fn_dynamic_raw(fn_name, true, &mut [value]) {
|
match ctx.call_fn_dynamic_raw(fn_name, true, &mut [value]) {
|
||||||
Ok(result) if result.is::<crate::ImmutableString>() => {
|
Ok(result) if result.is::<crate::ImmutableString>() => result
|
||||||
result.take_immutable_string().unwrap()
|
.take_immutable_string()
|
||||||
}
|
.expect("never fails as the result is `ImmutableString`"),
|
||||||
Ok(result) => ctx.engine().map_type_name(result.type_name()).into(),
|
Ok(result) => ctx.engine().map_type_name(result.type_name()).into(),
|
||||||
Err(_) => ctx.engine().map_type_name(value.type_name()).into(),
|
Err(_) => ctx.engine().map_type_name(value.type_name()).into(),
|
||||||
}
|
}
|
||||||
|
384
src/parser.rs
384
src/parser.rs
File diff suppressed because it is too large
Load Diff
@ -181,7 +181,9 @@ impl fmt::Display for EvalAltResult {
|
|||||||
| Self::ErrorTerminated(_, _) => f.write_str(desc)?,
|
| Self::ErrorTerminated(_, _) => f.write_str(desc)?,
|
||||||
|
|
||||||
Self::ErrorRuntime(d, _) if d.is::<ImmutableString>() => {
|
Self::ErrorRuntime(d, _) if d.is::<ImmutableString>() => {
|
||||||
let s = &*d.read_lock::<ImmutableString>().unwrap();
|
let s = &*d
|
||||||
|
.read_lock::<ImmutableString>()
|
||||||
|
.expect("never fails because the type was checked");
|
||||||
write!(f, "{}: {}", desc, if s.is_empty() { desc } else { s })?
|
write!(f, "{}: {}", desc, if s.is_empty() { desc } else { s })?
|
||||||
}
|
}
|
||||||
Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?,
|
Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?,
|
||||||
@ -336,7 +338,11 @@ impl EvalAltResult {
|
|||||||
pub(crate) fn dump_fields(&self, map: &mut crate::Map) {
|
pub(crate) fn dump_fields(&self, map: &mut crate::Map) {
|
||||||
map.insert(
|
map.insert(
|
||||||
"error".into(),
|
"error".into(),
|
||||||
format!("{:?}", self).split('(').next().unwrap().into(),
|
format!("{:?}", self)
|
||||||
|
.split('(')
|
||||||
|
.next()
|
||||||
|
.expect("never fails because the debug format of an error is `ErrorXXX(...)`")
|
||||||
|
.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
|
50
src/scope.rs
50
src/scope.rs
@ -7,7 +7,7 @@ use std::prelude::v1::*;
|
|||||||
use std::{borrow::Cow, iter::Extend};
|
use std::{borrow::Cow, iter::Extend};
|
||||||
|
|
||||||
/// Keep a number of entries inline (since [`Dynamic`] is usually small enough).
|
/// Keep a number of entries inline (since [`Dynamic`] is usually small enough).
|
||||||
const SCOPE_SIZE: usize = 16;
|
const SCOPE_SIZE: usize = 8;
|
||||||
|
|
||||||
/// Type containing information about the current scope.
|
/// Type containing information about the current scope.
|
||||||
/// Useful for keeping state between [`Engine`][crate::Engine] evaluation runs.
|
/// Useful for keeping state between [`Engine`][crate::Engine] evaluation runs.
|
||||||
@ -62,10 +62,7 @@ pub struct Scope<'a> {
|
|||||||
impl Default for Scope<'_> {
|
impl Default for Scope<'_> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self::new()
|
||||||
values: Default::default(),
|
|
||||||
names: Vec::with_capacity(SCOPE_SIZE),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,7 +96,10 @@ impl<'a> Scope<'a> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Default::default()
|
Self {
|
||||||
|
values: Default::default(),
|
||||||
|
names: Default::default(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// Empty the [`Scope`].
|
/// Empty the [`Scope`].
|
||||||
///
|
///
|
||||||
@ -373,7 +373,11 @@ impl<'a> Scope<'a> {
|
|||||||
}
|
}
|
||||||
Some((_, AccessMode::ReadOnly)) => panic!("variable {} is constant", name),
|
Some((_, AccessMode::ReadOnly)) => panic!("variable {} is constant", name),
|
||||||
Some((index, AccessMode::ReadWrite)) => {
|
Some((index, AccessMode::ReadWrite)) => {
|
||||||
*self.values.get_mut(index).unwrap() = Dynamic::from(value);
|
let value_ref = self
|
||||||
|
.values
|
||||||
|
.get_mut(index)
|
||||||
|
.expect("never fails because the index is returned by `get_index`");
|
||||||
|
*value_ref = Dynamic::from(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
@ -406,24 +410,38 @@ impl<'a> Scope<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
/// Get a mutable reference to an entry in the [`Scope`] based on the index.
|
/// Get a mutable reference to an entry in the [`Scope`] based on the index.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the index is out of bounds.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn get_mut_by_index(&mut self, index: usize) -> &mut Dynamic {
|
pub(crate) fn get_mut_by_index(&mut self, index: usize) -> &mut Dynamic {
|
||||||
self.values.get_mut(index).expect("invalid index in Scope")
|
self.values
|
||||||
|
.get_mut(index)
|
||||||
|
.expect("never fails unless the index is out of bounds")
|
||||||
}
|
}
|
||||||
/// Update the access type of an entry in the [`Scope`].
|
/// Update the access type of an entry in the [`Scope`].
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the index is out of bounds.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn add_entry_alias(
|
pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self {
|
||||||
&mut self,
|
let entry = self
|
||||||
index: usize,
|
.names
|
||||||
alias: impl Into<Identifier> + PartialEq<Identifier>,
|
.get_mut(index)
|
||||||
) -> &mut Self {
|
.expect("never fails unless the index is out of bounds");
|
||||||
let entry = self.names.get_mut(index).expect("invalid index in Scope");
|
|
||||||
if entry.1.is_none() {
|
if entry.1.is_none() {
|
||||||
|
// Initialize the alias list if it is empty.
|
||||||
entry.1 = Some(Default::default());
|
entry.1 = Some(Default::default());
|
||||||
}
|
}
|
||||||
if !entry.1.as_ref().unwrap().iter().any(|a| &alias == a) {
|
let list = entry
|
||||||
entry.1.as_mut().unwrap().push(alias.into());
|
.1
|
||||||
|
.as_mut()
|
||||||
|
.expect("never fails because the list is initialized");
|
||||||
|
if !list.iter().any(|a| &alias == a) {
|
||||||
|
list.push(alias);
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -91,21 +91,19 @@ struct FnMetadata {
|
|||||||
|
|
||||||
impl PartialOrd for FnMetadata {
|
impl PartialOrd for FnMetadata {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
Some(match self.name.partial_cmp(&other.name).unwrap() {
|
Some(self.cmp(other))
|
||||||
Ordering::Less => Ordering::Less,
|
|
||||||
Ordering::Greater => Ordering::Greater,
|
|
||||||
Ordering::Equal => match self.num_params.partial_cmp(&other.num_params).unwrap() {
|
|
||||||
Ordering::Less => Ordering::Less,
|
|
||||||
Ordering::Greater => Ordering::Greater,
|
|
||||||
Ordering::Equal => self.params.partial_cmp(&other.params).unwrap(),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ord for FnMetadata {
|
impl Ord for FnMetadata {
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
self.partial_cmp(other).unwrap()
|
match self.name.cmp(&other.name) {
|
||||||
|
Ordering::Equal => match self.num_params.cmp(&other.num_params) {
|
||||||
|
Ordering::Equal => self.params.cmp(&other.params),
|
||||||
|
cmp => cmp,
|
||||||
|
},
|
||||||
|
cmp => cmp,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
99
src/token.rs
99
src/token.rs
@ -1181,7 +1181,10 @@ pub fn parse_string_literal(
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_position"))]
|
#[cfg(not(feature = "no_position"))]
|
||||||
{
|
{
|
||||||
skip_whitespace_until = start.position().unwrap() + 1;
|
let start_position = start
|
||||||
|
.position()
|
||||||
|
.expect("never fails because the string must have a starting position");
|
||||||
|
skip_whitespace_until = start_position + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1201,7 +1204,11 @@ pub fn parse_string_literal(
|
|||||||
|
|
||||||
// Whitespace to skip
|
// Whitespace to skip
|
||||||
#[cfg(not(feature = "no_position"))]
|
#[cfg(not(feature = "no_position"))]
|
||||||
_ if next_char.is_whitespace() && pos.position().unwrap() < skip_whitespace_until => {}
|
_ if next_char.is_whitespace()
|
||||||
|
&& pos
|
||||||
|
.position()
|
||||||
|
.expect("never fails because a character must have a position")
|
||||||
|
< skip_whitespace_until => {}
|
||||||
|
|
||||||
// All other characters
|
// All other characters
|
||||||
_ => {
|
_ => {
|
||||||
@ -1237,37 +1244,29 @@ fn scan_block_comment(
|
|||||||
stream: &mut impl InputStream,
|
stream: &mut impl InputStream,
|
||||||
mut level: usize,
|
mut level: usize,
|
||||||
pos: &mut Position,
|
pos: &mut Position,
|
||||||
comment: &mut Option<String>,
|
mut comment: Option<&mut String>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
let comment = &mut comment;
|
||||||
|
|
||||||
while let Some(c) = stream.get_next() {
|
while let Some(c) = stream.get_next() {
|
||||||
pos.advance();
|
pos.advance();
|
||||||
|
|
||||||
if let Some(ref mut comment) = comment {
|
comment.as_mut().map(|comment| comment.push(c));
|
||||||
comment.push(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
match c {
|
match c {
|
||||||
'/' => {
|
'/' => {
|
||||||
if let Some(c2) = stream.peek_next() {
|
stream.peek_next().filter(|&c2| c2 == '*').map(|c2| {
|
||||||
if c2 == '*' {
|
eat_next(stream, pos);
|
||||||
eat_next(stream, pos);
|
comment.as_mut().map(|comment| comment.push(c2));
|
||||||
if let Some(ref mut comment) = comment {
|
level += 1;
|
||||||
comment.push(c2);
|
});
|
||||||
}
|
|
||||||
level += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
'*' => {
|
'*' => {
|
||||||
if let Some(c2) = stream.peek_next() {
|
stream.peek_next().filter(|&c2| c2 == '/').map(|c2| {
|
||||||
if c2 == '/' {
|
eat_next(stream, pos);
|
||||||
eat_next(stream, pos);
|
comment.as_mut().map(|comment| comment.push(c2));
|
||||||
if let Some(ref mut comment) = comment {
|
level -= 1;
|
||||||
comment.push(c2);
|
});
|
||||||
}
|
|
||||||
level -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
'\n' => pos.new_line(),
|
'\n' => pos.new_line(),
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -1347,21 +1346,27 @@ fn get_next_token_inner(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
state.comment_level = scan_block_comment(stream, state.comment_level, pos, &mut comment);
|
state.comment_level =
|
||||||
|
scan_block_comment(stream, state.comment_level, pos, comment.as_mut());
|
||||||
|
|
||||||
let include_comments = state.include_comments;
|
let return_comment = state.include_comments;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
let include_comments = if is_doc_comment(comment.as_ref().unwrap()) {
|
let return_comment = return_comment
|
||||||
true
|
|| is_doc_comment(
|
||||||
} else {
|
comment
|
||||||
include_comments
|
.as_ref()
|
||||||
};
|
.expect("never fails because `include_comments` is true"),
|
||||||
|
);
|
||||||
|
|
||||||
if include_comments {
|
if return_comment {
|
||||||
return Some((Token::Comment(comment.unwrap()), start_pos));
|
return Some((
|
||||||
} else if state.comment_level > 0 {
|
Token::Comment(comment.expect("never fails because `return_comment` is true")),
|
||||||
|
start_pos,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if state.comment_level > 0 {
|
||||||
// Reached EOF without ending comment block
|
// Reached EOF without ending comment block
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -1409,7 +1414,7 @@ fn get_next_token_inner(
|
|||||||
}
|
}
|
||||||
#[cfg(any(not(feature = "no_float"), feature = "decimal"))]
|
#[cfg(any(not(feature = "no_float"), feature = "decimal"))]
|
||||||
'.' => {
|
'.' => {
|
||||||
stream.get_next().unwrap();
|
stream.get_next().expect("never fails because it is `.`");
|
||||||
|
|
||||||
// Check if followed by digits or something that cannot start a property name
|
// Check if followed by digits or something that cannot start a property name
|
||||||
match stream.peek_next().unwrap_or('\0') {
|
match stream.peek_next().unwrap_or('\0') {
|
||||||
@ -1443,7 +1448,7 @@ fn get_next_token_inner(
|
|||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
'e' => {
|
'e' => {
|
||||||
stream.get_next().unwrap();
|
stream.get_next().expect("never fails it is `e`");
|
||||||
|
|
||||||
// Check if followed by digits or +/-
|
// Check if followed by digits or +/-
|
||||||
match stream.peek_next().unwrap_or('\0') {
|
match stream.peek_next().unwrap_or('\0') {
|
||||||
@ -1456,7 +1461,11 @@ fn get_next_token_inner(
|
|||||||
'+' | '-' => {
|
'+' | '-' => {
|
||||||
result.push(next_char);
|
result.push(next_char);
|
||||||
pos.advance();
|
pos.advance();
|
||||||
result.push(stream.get_next().unwrap());
|
result.push(
|
||||||
|
stream
|
||||||
|
.get_next()
|
||||||
|
.expect("never fails because it is `+` or `-`"),
|
||||||
|
);
|
||||||
pos.advance();
|
pos.advance();
|
||||||
}
|
}
|
||||||
// Not a floating-point number
|
// Not a floating-point number
|
||||||
@ -1492,12 +1501,10 @@ fn get_next_token_inner(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let num_pos = if let Some(negated_pos) = negated {
|
let num_pos = negated.map_or(start_pos, |negated_pos| {
|
||||||
result.insert(0, '-');
|
result.insert(0, '-');
|
||||||
negated_pos
|
negated_pos
|
||||||
} else {
|
});
|
||||||
start_pos
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse number
|
// Parse number
|
||||||
return Some((
|
return Some((
|
||||||
@ -1718,9 +1725,7 @@ fn get_next_token_inner(
|
|||||||
pos.new_line();
|
pos.new_line();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if let Some(ref mut comment) = comment {
|
comment.as_mut().map(|comment| comment.push(c));
|
||||||
comment.push(c);
|
|
||||||
}
|
|
||||||
pos.advance();
|
pos.advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1749,7 +1754,7 @@ fn get_next_token_inner(
|
|||||||
};
|
};
|
||||||
|
|
||||||
state.comment_level =
|
state.comment_level =
|
||||||
scan_block_comment(stream, state.comment_level, pos, &mut comment);
|
scan_block_comment(stream, state.comment_level, pos, comment.as_mut());
|
||||||
|
|
||||||
if let Some(comment) = comment {
|
if let Some(comment) = comment {
|
||||||
return Some((Token::Comment(comment), start_pos));
|
return Some((Token::Comment(comment), start_pos));
|
||||||
@ -2179,8 +2184,8 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Run the mapper, if any
|
// Run the mapper, if any
|
||||||
let token = if let Some(map) = self.map {
|
let token = if let Some(map_func) = self.map {
|
||||||
map(token)
|
map_func(token)
|
||||||
} else {
|
} else {
|
||||||
token
|
token
|
||||||
};
|
};
|
||||||
|
21
src/utils.rs
21
src/utils.rs
@ -10,7 +10,7 @@ use std::{
|
|||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
fmt,
|
fmt,
|
||||||
hash::{BuildHasher, Hash, Hasher},
|
hash::{BuildHasher, Hash, Hasher},
|
||||||
iter::FromIterator,
|
iter::{empty, FromIterator},
|
||||||
ops::{Add, AddAssign, Deref, Sub, SubAssign},
|
ops::{Add, AddAssign, Deref, Sub, SubAssign},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
@ -58,9 +58,8 @@ pub fn get_hasher() -> ahash::AHasher {
|
|||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _(INTERNALS)_ Calculate a [`u64`] hash key from a namespace-qualified function name
|
/// Calculate a [`u64`] hash key from a namespace-qualified function name
|
||||||
/// and the number of parameters, but no parameter types.
|
/// and the number of parameters, but no parameter types.
|
||||||
/// Exported under the `internals` feature only.
|
|
||||||
///
|
///
|
||||||
/// Module names are passed in via `&str` references from an iterator.
|
/// Module names are passed in via `&str` references from an iterator.
|
||||||
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
||||||
@ -68,8 +67,8 @@ pub fn get_hasher() -> ahash::AHasher {
|
|||||||
/// # Note
|
/// # Note
|
||||||
///
|
///
|
||||||
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn calc_fn_hash<'a>(
|
pub fn calc_qualified_fn_hash<'a>(
|
||||||
modules: impl Iterator<Item = &'a str>,
|
modules: impl Iterator<Item = &'a str>,
|
||||||
fn_name: impl AsRef<str>,
|
fn_name: impl AsRef<str>,
|
||||||
num: usize,
|
num: usize,
|
||||||
@ -88,11 +87,19 @@ pub fn calc_fn_hash<'a>(
|
|||||||
s.finish()
|
s.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _(INTERNALS)_ Calculate a [`u64`] hash key from a list of parameter types.
|
/// Calculate a [`u64`] hash key from a non-namespace-qualified function name
|
||||||
/// Exported under the `internals` feature only.
|
/// and the number of parameters, but no parameter types.
|
||||||
///
|
///
|
||||||
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
pub fn calc_fn_hash(fn_name: impl AsRef<str>, num: usize) -> u64 {
|
||||||
|
calc_qualified_fn_hash(empty(), fn_name, num)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate a [`u64`] hash key from a list of parameter types.
|
||||||
|
///
|
||||||
|
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
||||||
|
#[inline]
|
||||||
pub fn calc_fn_params_hash(params: impl Iterator<Item = TypeId>) -> u64 {
|
pub fn calc_fn_params_hash(params: impl Iterator<Item = TypeId>) -> u64 {
|
||||||
let s = &mut get_hasher();
|
let s = &mut get_hasher();
|
||||||
let mut len = 0;
|
let mut len = 0;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#![cfg(not(feature = "no_object"))]
|
#![cfg(not(feature = "no_object"))]
|
||||||
|
|
||||||
use rhai::{Engine, EvalAltResult, ImmutableString, INT};
|
use rhai::{Engine, EvalAltResult, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_set() -> Result<(), Box<EvalAltResult>> {
|
fn test_get_set() -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -46,21 +46,26 @@ fn test_get_set() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(engine.eval::<INT>("let a = new_ts(); a.x.add(); a.x")?, 42);
|
assert_eq!(engine.eval::<INT>("let a = new_ts(); a.x.add(); a.x")?, 42);
|
||||||
assert_eq!(engine.eval::<INT>("let a = new_ts(); a.y.add(); a.y")?, 0);
|
assert_eq!(engine.eval::<INT>("let a = new_ts(); a.y.add(); a.y")?, 0);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
engine.register_indexer_get_set(
|
||||||
{
|
|value: &mut TestStruct, index: &str| value.array[index.len()],
|
||||||
engine.register_indexer_get_set(
|
|value: &mut TestStruct, index: &str, new_val: INT| value.array[index.len()] = new_val,
|
||||||
|value: &mut TestStruct, index: ImmutableString| value.array[index.len()],
|
);
|
||||||
|value: &mut TestStruct, index: ImmutableString, new_val: INT| {
|
|
||||||
value.array[index.len()] = new_val
|
#[cfg(not(feature = "no_index"))]
|
||||||
},
|
assert_eq!(engine.eval::<INT>(r#"let a = new_ts(); a["abc"]"#)?, 4);
|
||||||
);
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(r#"let a = new_ts(); a["abc"] = 42; a["abc"]"#)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<INT>(r"let a = new_ts(); a.abc")?, 4);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(r"let a = new_ts(); a.abc = 42; a.abc")?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(engine.eval::<INT>(r#"let a = new_ts(); a["abc"]"#)?, 4);
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>(r#"let a = new_ts(); a["abc"] = 42; a["abc"]"#)?,
|
|
||||||
42
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,21 +59,21 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{:?}", ast),
|
format!("{:?}", ast),
|
||||||
"AST { source: None, body: [Expr(123 @ 1:53)], functions: Module, resolver: None }"
|
"AST { source: None, body: Block[Expr(123 @ 1:53)], functions: Module, resolver: None }"
|
||||||
);
|
);
|
||||||
|
|
||||||
let ast = engine.compile("const DECISION = false; if DECISION { 42 } else { 123 }")?;
|
let ast = engine.compile("const DECISION = false; if DECISION { 42 } else { 123 }")?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{:?}", ast),
|
format!("{:?}", ast),
|
||||||
r#"AST { source: None, body: [Const(false @ 1:18, "DECISION" @ 1:7, false, 1:1), Expr(123 @ 1:51)], functions: Module, resolver: None }"#
|
r#"AST { source: None, body: Block[Const(false @ 1:18, "DECISION" @ 1:7, false, 1:1), Expr(123 @ 1:51)], functions: Module, resolver: None }"#
|
||||||
);
|
);
|
||||||
|
|
||||||
let ast = engine.compile("if 1 == 2 { 42 }")?;
|
let ast = engine.compile("if 1 == 2 { 42 }")?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{:?}", ast),
|
format!("{:?}", ast),
|
||||||
"AST { source: None, body: [], functions: Module, resolver: None }"
|
"AST { source: None, body: Block[], functions: Module, resolver: None }"
|
||||||
);
|
);
|
||||||
|
|
||||||
engine.set_optimization_level(OptimizationLevel::Full);
|
engine.set_optimization_level(OptimizationLevel::Full);
|
||||||
@ -82,7 +82,7 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{:?}", ast),
|
format!("{:?}", ast),
|
||||||
"AST { source: None, body: [Expr(42 @ 1:1)], functions: Module, resolver: None }"
|
"AST { source: None, body: Block[Expr(42 @ 1:1)], functions: Module, resolver: None }"
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -61,7 +61,7 @@ fn test_scope_eval() -> Result<(), Box<EvalAltResult>> {
|
|||||||
// First invocation
|
// First invocation
|
||||||
engine
|
engine
|
||||||
.eval_with_scope::<()>(&mut scope, " let x = 4 + 5 - y + z; y = 1;")
|
.eval_with_scope::<()>(&mut scope, " let x = 4 + 5 - y + z; y = 1;")
|
||||||
.expect("y and z not found?");
|
.expect("variables y and z should exist");
|
||||||
|
|
||||||
// Second invocation using the same state
|
// Second invocation using the same state
|
||||||
let result = engine.eval_with_scope::<INT>(&mut scope, "x")?;
|
let result = engine.eval_with_scope::<INT>(&mut scope, "x")?;
|
||||||
|
Loading…
Reference in New Issue
Block a user