///|
struct ScriptProgram {
parser : ScriptParser
commands : Array[ParsedCommand]
mut failure : String?
} derive(Debug)
///|
struct ParseCache {
scripts : Map[String, ScriptProgram]
expressions : Map[String, Expr]
mut script_units : Int
mut expression_units : Int
mut script_hits : Int
mut expression_hits : Int
} derive(Debug)
///|
fn ParseCache::new() -> ParseCache {
{
scripts: Map([]),
expressions: Map([]),
script_units: 0,
expression_units: 0,
script_hits: 0,
expression_hits: 0,
}
}
///|
pub struct CacheStats {
scripts : Int
expressions : Int
source_units : Int
script_hits : Int
expression_hits : Int
} derive(Debug)
///|
pub fn Interpreter::cache_stats(self : Interpreter) -> CacheStats {
let cache = self.state.cache
{
scripts: cache.scripts.length(),
expressions: cache.expressions.length(),
source_units: cache.script_units + cache.expression_units,
script_hits: cache.script_hits,
expression_hits: cache.expression_hits,
}
}
///|
pub fn Interpreter::clear_cache(self : Interpreter) -> Unit {
let cache = self.state.cache
cache.scripts.clear()
cache.expressions.clear()
cache.script_units = 0
cache.expression_units = 0
}
///|
fn Interpreter::cached_script(
self : Interpreter,
source : String,
) -> ScriptProgram {
let cache = self.state.cache
if cache.scripts.get(source) is Some(program) {
if cache.script_hits < 2147483647 {
cache.script_hits += 1
}
return program
}
// Eviction affects only future lookups; an executing program retains its
// own reference, including recursive calls and incremental parse errors.
if cache.scripts.length() >= 256 ||
cache.script_units + source.length() > 262144 {
cache.scripts.clear()
cache.script_units = 0
}
let program = ScriptProgram::{
parser: ScriptParser::new(source),
commands: [],
failure: None,
}
if source.length() <= 65536 {
cache.scripts[source] = program
cache.script_units += source.length()
}
program
}
///|
fn Interpreter::cached_expression(
self : Interpreter,
source : String,
) -> Expr raise TclError {
let cache = self.state.cache
if cache.expressions.get(source) is Some(expr) {
if cache.expression_hits < 2147483647 {
cache.expression_hits += 1
}
return expr
}
let parser = ScriptParser::new(source)
let expr = parser.expression(1, 0)
parser.expr_space()
if parser.pos != parser.chars.length() {
raise Invalid("trailing expression token")
}
if cache.expressions.length() >= 256 ||
cache.expression_units + source.length() > 131072 {
cache.expressions.clear()
cache.expression_units = 0
}
if source.length() <= 16384 {
cache.expressions[source] = expr
cache.expression_units += source.length()
}
expr
}