#![allow(clippy::result_large_err)]
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::fmt;
use std::rc::Rc;
use std::sync::{Arc, OnceLock};
use indexmap::IndexMap;
use tabnas::{
AltMatch, AltSpec, Context, ListRef, Plugin, PluginError, Rule, RuleSnapshot, RuleSpec, Tabnas,
TabnasError, Tin, Token, Value, TIN_CA, TIN_CB, TIN_CL, TIN_CS, TIN_NR, TIN_ST, TIN_TX, TIN_VL,
TIN_ZZ,
};
pub const VERSION: &str = "0.5.15";
pub const PLUGIN_NAME: &str = "Expr";
#[cfg(doctest)]
#[doc = include_str!("../README.md")]
mod readme_examples {}
pub use tabnas::TabnasError as ExprError;
pub const MIN_SAFE_INTEGER: i64 = -9_007_199_254_740_991;
pub const MAX_SAFE_INTEGER: i64 = 9_007_199_254_740_991;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct PrevalDef {
pub active: bool,
pub required: bool,
pub allow: Option<Vec<String>>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct OpDef {
pub src: Vec<String>,
pub osrc: String,
pub csrc: String,
pub left: Option<i64>,
pub right: Option<i64>,
pub prefix: bool,
pub suffix: bool,
pub infix: bool,
pub ternary: bool,
pub paren: bool,
pub preval: Option<PrevalDef>,
pub use_data: Value,
}
impl Default for OpDef {
fn default() -> Self {
OpDef {
src: Vec::new(),
osrc: String::new(),
csrc: String::new(),
left: None,
right: None,
prefix: false,
suffix: false,
infix: false,
ternary: false,
paren: false,
preval: None,
use_data: Value::Undefined,
}
}
}
impl OpDef {
pub fn infix(src: impl Into<String>, left: i64, right: i64) -> Self {
OpDef {
src: vec![src.into()],
infix: true,
left: Some(left),
right: Some(right),
..Default::default()
}
}
pub fn prefix(src: impl Into<String>, right: i64) -> Self {
OpDef {
src: vec![src.into()],
prefix: true,
right: Some(right),
..Default::default()
}
}
pub fn suffix(src: impl Into<String>, left: i64) -> Self {
OpDef {
src: vec![src.into()],
suffix: true,
left: Some(left),
..Default::default()
}
}
pub fn ternary(first: impl Into<String>, second: impl Into<String>) -> Self {
OpDef {
src: vec![first.into(), second.into()],
ternary: true,
..Default::default()
}
}
pub fn paren(osrc: impl Into<String>, csrc: impl Into<String>) -> Self {
OpDef {
osrc: osrc.into(),
csrc: csrc.into(),
paren: true,
..Default::default()
}
}
pub fn with_preval(mut self, preval: PrevalDef) -> Self {
self.preval = Some(preval);
self
}
pub fn with_use(mut self, use_data: Value) -> Self {
self.use_data = use_data;
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Op {
pub name: String,
pub src: String,
pub left: i64,
pub right: i64,
pub use_data: Value,
pub prefix: bool,
pub suffix: bool,
pub infix: bool,
pub ternary: bool,
pub paren: bool,
pub terms: usize,
pub tkn: String,
pub tin: Tin,
pub osrc: String,
pub csrc: String,
pub otkn: String,
pub otin: Tin,
pub ctkn: String,
pub ctin: Tin,
pub preval: PrevalDef,
pub ternary_index: Option<usize>,
}
impl Default for Op {
fn default() -> Self {
Op {
name: String::new(),
src: String::new(),
left: MIN_SAFE_INTEGER,
right: MAX_SAFE_INTEGER,
use_data: Value::Undefined,
prefix: false,
suffix: false,
infix: false,
ternary: false,
paren: false,
terms: 0,
tkn: String::new(),
tin: -1,
osrc: String::new(),
csrc: String::new(),
otkn: String::new(),
otin: -1,
ctkn: String::new(),
ctin: -1,
preval: PrevalDef::default(),
ternary_index: None,
}
}
}
impl Op {
fn to_value(&self, token: Option<&Token>) -> Value {
let mut map: IndexMap<String, Value> = IndexMap::new();
map.insert("src".into(), Value::String(self.src.clone()));
map.insert("left".into(), number(self.left));
map.insert("right".into(), number(self.right));
map.insert("name".into(), Value::String(self.name.clone()));
map.insert("infix".into(), Value::Bool(self.infix));
map.insert("prefix".into(), Value::Bool(self.prefix));
map.insert("suffix".into(), Value::Bool(self.suffix));
map.insert("ternary".into(), Value::Bool(self.ternary));
map.insert("tkn".into(), Value::String(self.tkn.clone()));
map.insert("tin".into(), Value::Number(f64::from(self.tin)));
map.insert("terms".into(), Value::Number(self.terms as f64));
map.insert("use".into(), self.use_value());
map.insert("paren".into(), Value::Bool(self.paren));
map.insert("osrc".into(), Value::String(self.osrc.clone()));
map.insert("csrc".into(), Value::String(self.csrc.clone()));
map.insert("otkn".into(), Value::String(self.otkn.clone()));
map.insert("ctkn".into(), Value::String(self.ctkn.clone()));
map.insert("otin".into(), Value::Number(f64::from(self.otin)));
map.insert("ctin".into(), Value::Number(f64::from(self.ctin)));
map.insert("preval".into(), self.preval_value());
map.insert("token".into(), token_value(token));
Value::object(map)
}
fn use_value(&self) -> Value {
let mut used: IndexMap<String, Value> = match &self.use_data {
Value::Object(entries) => (**entries).clone(),
_ => IndexMap::new(),
};
if let Some(index) = self.ternary_index {
let mut ternary: IndexMap<String, Value> = IndexMap::new();
ternary.insert("opI".into(), Value::Number(index as f64));
used.insert("ternary".into(), Value::object(ternary));
}
Value::object(used)
}
fn preval_value(&self) -> Value {
let mut preval: IndexMap<String, Value> = IndexMap::new();
preval.insert("active".into(), Value::Bool(self.preval.active));
preval.insert("required".into(), Value::Bool(self.preval.required));
if let Some(allow) = &self.preval.allow {
preval.insert(
"allow".into(),
Value::array(allow.iter().cloned().map(Value::String).collect()),
);
}
Value::object(preval)
}
}
fn number(value: i64) -> Value {
Value::Number(value as f64)
}
fn token_value(token: Option<&Token>) -> Value {
let Some(token) = token else {
return Value::object(IndexMap::new());
};
let mut map: IndexMap<String, Value> = IndexMap::new();
map.insert("isToken".into(), Value::Bool(true));
map.insert("name".into(), Value::String(token.name.as_str().into()));
map.insert("tin".into(), Value::Number(f64::from(token.tin)));
map.insert("src".into(), Value::String(token.src.as_str().into()));
map.insert("len".into(), Value::Number(token.len as f64));
map.insert("sI".into(), Value::Number(token.site.pos as f64));
map.insert("rI".into(), Value::Number(token.site.ri as f64));
map.insert("cI".into(), Value::Number(token.site.ci as f64));
Value::object(map)
}
pub struct EvalSite<'a> {
rule: Option<Rc<RuleSnapshot>>,
context: Option<&'a mut Context>,
token: Option<Token>,
}
impl<'a> EvalSite<'a> {
pub fn detached() -> Self {
EvalSite {
rule: None,
context: None,
token: None,
}
}
pub fn new(rule: Option<Rc<RuleSnapshot>>, context: &'a mut Context) -> Self {
EvalSite {
rule,
context: Some(context),
token: None,
}
}
pub fn rule(&self) -> Option<&RuleSnapshot> {
self.rule.as_deref()
}
pub fn token(&self) -> Option<&Token> {
self.token.as_ref()
}
pub fn context(&mut self) -> Option<&mut Context> {
self.context.as_deref_mut()
}
pub fn flag(&self, name: &str) -> bool {
self.rule
.as_ref()
.is_some_and(|rule| matches!(rule.u.get(name), Some(Value::Bool(true))))
}
fn reborrow(&mut self) -> EvalSite<'_> {
EvalSite {
rule: self.rule.clone(),
context: self.context.as_deref_mut(),
token: None,
}
}
}
pub type Evaluate = Arc<dyn Fn(&mut EvalSite<'_>, &Op, &[Value]) -> Value + Send + Sync>;
#[derive(Clone, Default)]
pub struct ExprOptions {
pub op: IndexMap<String, Option<OpDef>>,
pub evaluate: Option<Evaluate>,
}
impl fmt::Debug for ExprOptions {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ExprOptions")
.field("op", &self.op)
.field("evaluate", &self.evaluate.as_ref().map(|_| "<function>"))
.finish()
}
}
impl ExprOptions {
pub fn new() -> Self {
Self::default()
}
pub fn with_op(mut self, name: impl Into<String>, def: OpDef) -> Self {
self.op.insert(name.into(), Some(def));
self
}
pub fn without_op(mut self, name: impl Into<String>) -> Self {
self.op.insert(name.into(), None);
self
}
pub fn with_evaluate(
mut self,
evaluate: impl Fn(&mut EvalSite<'_>, &Op, &[Value]) -> Value + Send + Sync + 'static,
) -> Self {
self.evaluate = Some(Arc::new(evaluate));
self
}
pub fn from_value(options: &Value) -> Self {
let mut resolved = ExprOptions::new();
let Some(op) = object_get(options, "op") else {
return resolved;
};
let Value::Object(entries) = op else {
return resolved;
};
for (name, def) in entries.iter() {
match def {
Value::Object(_) => {
resolved
.op
.insert(name.clone(), Some(op_def_from_value(def)));
}
_ => {
resolved.op.insert(name.clone(), None);
}
}
}
resolved
}
}
fn object_get<'a>(value: &'a Value, key: &str) -> Option<&'a Value> {
match value {
Value::Object(map) => map.get(key),
Value::MapRef(map) => map.value.get(key),
_ => None,
}
}
fn value_bool(value: Option<&Value>) -> bool {
matches!(value, Some(Value::Bool(true)))
}
fn value_i64(value: Option<&Value>) -> Option<i64> {
match value {
Some(Value::Number(number)) => Some(*number as i64),
_ => None,
}
}
fn value_string(value: Option<&Value>) -> String {
match value {
Some(Value::String(text)) => text.clone(),
Some(Value::Text(text)) => text.string.clone(),
_ => String::new(),
}
}
fn op_def_from_value(def: &Value) -> OpDef {
let mut out = OpDef {
osrc: value_string(object_get(def, "osrc")),
csrc: value_string(object_get(def, "csrc")),
left: value_i64(object_get(def, "left")),
right: value_i64(object_get(def, "right")),
prefix: value_bool(object_get(def, "prefix")),
suffix: value_bool(object_get(def, "suffix")),
infix: value_bool(object_get(def, "infix")),
ternary: value_bool(object_get(def, "ternary")),
paren: value_bool(object_get(def, "paren")),
use_data: object_get(def, "use").cloned().unwrap_or(Value::Undefined),
..Default::default()
};
match object_get(def, "src") {
Some(Value::String(src)) => out.src = vec![src.clone()],
Some(Value::Array(srcs)) => {
out.src = srcs
.iter()
.map(|src| value_string(Some(src)))
.collect::<Vec<_>>()
}
_ => {}
}
out.preval = match object_get(def, "preval") {
None | Some(Value::Undefined) | Some(Value::Null) => None,
Some(Value::Bool(active)) => Some(PrevalDef {
active: *active,
required: false,
allow: None,
}),
Some(preval) => Some(PrevalDef {
active: match object_get(preval, "active") {
None | Some(Value::Undefined) | Some(Value::Null) => true,
Some(Value::Bool(active)) => *active,
Some(_) => true,
},
required: value_bool(object_get(preval, "required")),
allow: match object_get(preval, "allow") {
Some(Value::Array(allow)) => Some(
allow
.iter()
.map(|entry| value_string(Some(entry)))
.collect::<Vec<_>>(),
),
_ => None,
},
}),
};
out
}
pub fn defaults() -> Value {
let mut ops: IndexMap<String, Value> = IndexMap::new();
ops.insert("positive".into(), prefix_value("+", 4_000_000));
ops.insert("negative".into(), prefix_value("-", 4_000_000));
ops.insert("addition".into(), infix_value("+", 2_000_000, 2_100_000));
ops.insert("subtraction".into(), infix_value("-", 2_000_000, 2_100_000));
ops.insert(
"multiplication".into(),
infix_value("*", 3_000_000, 3_100_000),
);
ops.insert("division".into(), infix_value("/", 3_000_000, 3_100_000));
ops.insert("remainder".into(), infix_value("%", 3_000_000, 3_100_000));
ops.insert("plain".into(), paren_value("(", ")"));
let mut map: IndexMap<String, Value> = IndexMap::new();
map.insert("op".into(), Value::object(ops));
Value::object(map)
}
fn prefix_value(src: &str, right: i64) -> Value {
let mut def: IndexMap<String, Value> = IndexMap::new();
def.insert("prefix".into(), Value::Bool(true));
def.insert("right".into(), number(right));
def.insert("src".into(), Value::String(src.into()));
Value::object(def)
}
fn infix_value(src: &str, left: i64, right: i64) -> Value {
let mut def: IndexMap<String, Value> = IndexMap::new();
def.insert("infix".into(), Value::Bool(true));
def.insert("left".into(), number(left));
def.insert("right".into(), number(right));
def.insert("src".into(), Value::String(src.into()));
Value::object(def)
}
fn paren_value(osrc: &str, csrc: &str) -> Value {
let mut def: IndexMap<String, Value> = IndexMap::new();
def.insert("paren".into(), Value::Bool(true));
def.insert("osrc".into(), Value::String(osrc.into()));
def.insert("csrc".into(), Value::String(csrc.into()));
Value::object(def)
}
fn default_ops() -> IndexMap<String, Option<OpDef>> {
ExprOptions::from_value(&defaults()).op
}
fn resolve_options(options: &ExprOptions) -> IndexMap<String, Option<OpDef>> {
let mut resolved = default_ops();
for (name, def) in &options.op {
resolved.shift_remove(name);
match def {
Some(def) => {
resolved.insert(name.clone(), Some(def.clone()));
}
None => {
resolved.insert(name.clone(), None);
}
}
}
resolved
}
#[derive(Debug, Clone)]
struct ExprNode {
op: Option<Arc<Op>>,
token: Option<Token>,
terms: Vec<Value>,
root: u64,
size: usize,
}
#[derive(Default)]
struct Arena {
nodes: HashMap<u64, ExprNode>,
next_id: u64,
active: usize,
}
thread_local! {
static ARENA: RefCell<Arena> = RefCell::new(Arena::default());
static OVER_LIMIT: Cell<bool> = const { Cell::new(false) };
}
const EXPR_META: &str = "expr";
pub const NODE_LIMIT: usize = 127;
pub const RULE_LIMIT: usize = 1024;
fn node_id(value: &Value) -> Option<u64> {
let Value::ListRef(list) = value else {
return None;
};
match list.meta.get(EXPR_META) {
Some(Value::Number(id)) => Some(*id as u64),
_ => None,
}
}
pub fn is_op(value: &Value) -> bool {
op_of(value).is_some()
}
fn node_length(value: &Value) -> usize {
if let Some(length) = with_node(value, |entry| {
entry.terms.len() + usize::from(entry.op.is_some())
}) {
return length;
}
match value {
Value::Array(entries) => entries.len(),
Value::ListRef(list) => list.value.len(),
_ => 0,
}
}
fn is_op_kind(value: &Value, kind: fn(&Op) -> bool) -> bool {
op_of(value).is_some_and(|op| kind(&op))
}
fn is_paren_op(value: &Value) -> bool {
is_op_kind(value, |op| op.paren)
}
fn is_ternary_op(value: &Value) -> bool {
is_op_kind(value, |op| op.ternary)
}
pub fn op_of(value: &Value) -> Option<Arc<Op>> {
let id = node_id(value)?;
ARENA.with(|arena| {
arena
.borrow()
.nodes
.get(&id)
.and_then(|node| node.op.clone())
})
}
pub fn terms_of(value: &Value) -> Option<Vec<Value>> {
let id = node_id(value)?;
ARENA.with(|arena| arena.borrow().nodes.get(&id).map(|node| node.terms.clone()))
}
fn new_node_under(
op: Option<Arc<Op>>,
token: Option<Token>,
terms: Vec<Value>,
under: Option<&Value>,
) -> Value {
let inherited = under.and_then(root_id);
let terms_taken = terms.clone();
let id = ARENA.with(|arena| {
let mut arena = arena.borrow_mut();
arena.next_id += 1;
let id = arena.next_id;
arena.nodes.insert(
id,
ExprNode {
op,
token,
terms,
root: inherited.unwrap_or(id),
size: 1,
},
);
id
});
let handle = handle_for(id);
if under.is_some() {
grow(&handle, 1);
}
adopt_all(&handle, &terms_taken);
handle
}
fn new_node(op: Option<Arc<Op>>, token: Option<Token>, terms: Vec<Value>) -> Value {
new_node_under(op, token, terms, None)
}
fn grow(node: &Value, count: usize) {
let Some(root) = root_id(node) else {
return;
};
let over = ARENA.with(|arena| {
let mut arena = arena.borrow_mut();
match arena.nodes.get_mut(&root) {
Some(entry) => {
entry.size += count;
entry.size > NODE_LIMIT
}
None => false,
}
});
if over {
OVER_LIMIT.with(|flag| flag.set(true));
}
}
fn expression_size(node: &Value) -> usize {
let Some(root) = root_id(node) else {
return 0;
};
ARENA.with(|arena| {
arena
.borrow()
.nodes
.get(&root)
.map_or(0, |entry| entry.size)
})
}
fn adopt(container: &Value, term: &Value) {
let (Some(into), Some(from)) = (root_id(container), root_id(term)) else {
return;
};
if into == from {
return;
}
grow(container, expression_size(term));
}
fn adopt_all(container: &Value, terms: &[Value]) {
for term in terms {
adopt(container, term);
}
}
fn handle_for(id: u64) -> Value {
let mut meta: IndexMap<String, Value> = IndexMap::new();
meta.insert(EXPR_META.into(), Value::Number(id as f64));
Value::ListRef(Arc::new(ListRef {
value: Vec::new(),
implicit: false,
child: None,
meta,
}))
}
fn root_id(value: &Value) -> Option<u64> {
let id = node_id(value)?;
ARENA.with(|arena| arena.borrow().nodes.get(&id).map(|node| node.root))
}
fn root_node(value: &Value) -> Value {
match root_id(value) {
Some(id) => handle_for(id),
None => value.clone(),
}
}
fn with_node<R>(value: &Value, read: impl FnOnce(&ExprNode) -> R) -> Option<R> {
let id = node_id(value)?;
ARENA.with(|arena| arena.borrow().nodes.get(&id).map(read))
}
fn with_node_mut<R>(value: &Value, write: impl FnOnce(&mut ExprNode) -> R) -> Option<R> {
let id = node_id(value)?;
ARENA.with(|arena| arena.borrow_mut().nodes.get_mut(&id).map(write))
}
fn update_expr_node(node: &Value, op: Option<Arc<Op>>, token: Option<Token>, terms: Vec<Value>) {
adopt_all(node, &terms);
with_node_mut(node, |entry| {
entry.op = op;
entry.token = token;
entry.terms = terms;
});
}
fn dup_node(node: &Value) -> Value {
let Some((op, token, terms)) = with_node(node, |entry| {
(entry.op.clone(), entry.token.clone(), entry.terms.clone())
}) else {
return node.clone();
};
new_node_under(op, token, terms, Some(node))
}
fn term_at(node: &Value, index: usize) -> Value {
with_node(node, |entry| {
entry
.terms
.get(index.saturating_sub(1))
.cloned()
.unwrap_or(Value::Undefined)
})
.unwrap_or(Value::Undefined)
}
fn set_term(node: &Value, index: usize, value: Value) {
adopt(node, &value);
with_node_mut(node, |entry| {
let slot = index.saturating_sub(1);
while entry.terms.len() <= slot {
entry.terms.push(Value::Undefined);
}
entry.terms[slot] = value;
});
}
fn push_term(node: &Value, value: Value) {
adopt(node, &value);
with_node_mut(node, |entry| entry.terms.push(value));
}
fn term_count(node: &Value) -> usize {
with_node(node, |entry| entry.terms.len()).unwrap_or(0)
}
fn released_handle(id: u64) -> ! {
panic!(
"tabnas-expr: expression node {id} has been released. The value \
holding this handle outlived its parse: realize it with \
`tabnas_expr::realize` inside the `parse_scope` closure, or before \
the next parse on this thread, and let the realized value escape \
instead."
)
}
fn release_arena() {
ARENA.with(|arena| {
let mut arena = arena.borrow_mut();
if 0 == arena.active {
arena.nodes.clear();
EVALUATED.with(|set| set.borrow_mut().clear());
OVER_LIMIT.with(|flag| flag.set(false));
}
});
}
struct ArenaGuard;
impl ArenaGuard {
fn enter() -> Self {
ARENA.with(|arena| arena.borrow_mut().active += 1);
OVER_LIMIT.with(|flag| flag.set(false));
ArenaGuard
}
}
impl Drop for ArenaGuard {
fn drop(&mut self) {
ARENA.with(|arena| {
let mut arena = arena.borrow_mut();
arena.active = arena.active.saturating_sub(1);
if 0 == arena.active {
arena.nodes.clear();
EVALUATED.with(|set| set.borrow_mut().clear());
OVER_LIMIT.with(|flag| flag.set(false));
}
});
}
}
pub fn realize(value: &Value) -> Value {
let mut open: Vec<u64> = Vec::new();
realize_seen(value, &mut open)
}
fn realize_seen(value: &Value, open: &mut Vec<u64>) -> Value {
match value {
Value::ListRef(list) => {
let Some(id) = node_id(value) else {
let mut out = (**list).clone();
out.value = out
.value
.iter()
.map(|entry| realize_seen(entry, open))
.collect();
return Value::ListRef(Arc::new(out));
};
if open.contains(&id) {
return Value::String("[CIRCLE]".into());
}
let Some((op, token, terms)) = with_node(value, |entry| {
(entry.op.clone(), entry.token.clone(), entry.terms.clone())
}) else {
released_handle(id)
};
open.push(id);
let mut out = Vec::with_capacity(terms.len() + 1);
if let Some(op) = &op {
out.push(op.to_value(token.as_ref()));
}
for term in &terms {
out.push(realize_seen(term, open));
}
open.pop();
Value::array(out)
}
Value::Array(entries) => Value::array(
entries
.iter()
.map(|entry| realize_seen(entry, open))
.collect(),
),
Value::Object(entries) => Value::object(
entries
.iter()
.map(|(key, entry)| (key.clone(), realize_seen(entry, open)))
.collect(),
),
Value::MapRef(map) => {
let mut out = (**map).clone();
out.value = out
.value
.iter()
.map(|(key, entry)| (key.clone(), realize_seen(entry, open)))
.collect();
Value::MapRef(Arc::new(out))
}
other => other.clone(),
}
}
pub fn simplify(value: &Value) -> Value {
let mut open: Vec<u64> = Vec::new();
simplify_seen(value, &mut open)
}
fn simplify_seen(value: &Value, open: &mut Vec<u64>) -> Value {
match value {
Value::ListRef(list) => {
let Some(id) = node_id(value) else {
return Value::array(
list.value
.iter()
.map(|entry| simplify_seen(entry, open))
.collect(),
);
};
if open.contains(&id) {
return Value::String("[CIRCLE]".into());
}
let Some((op, terms)) =
with_node(value, |entry| (entry.op.clone(), entry.terms.clone()))
else {
released_handle(id)
};
open.push(id);
let mut out = Vec::with_capacity(terms.len() + 1);
if let Some(op) = &op {
out.push(Value::String(op.src.clone()));
}
for term in &terms {
if term.is_undefined() {
continue;
}
out.push(simplify_seen(term, open));
}
open.pop();
Value::array(out)
}
Value::Array(entries) if realized_source(entries.first()).is_some() => {
let mut out = Vec::with_capacity(entries.len());
out.push(Value::String(
realized_source(entries.first()).unwrap_or_default(),
));
for entry in entries.iter().skip(1) {
if entry.is_undefined() {
continue;
}
out.push(simplify_seen(entry, open));
}
Value::array(out)
}
Value::Array(entries) => Value::array(
entries
.iter()
.map(|entry| simplify_seen(entry, open))
.collect(),
),
Value::Object(entries) => Value::object(
entries
.iter()
.map(|(key, entry)| (key.clone(), simplify_seen(entry, open)))
.collect(),
),
Value::MapRef(map) => Value::object(
map.value
.iter()
.map(|(key, entry)| (key.clone(), simplify_seen(entry, open)))
.collect(),
),
Value::Text(text) => Value::String(text.string.clone()),
other => other.clone(),
}
}
fn realized_source(value: Option<&Value>) -> Option<String> {
match object_get(value?, "src") {
Some(Value::String(src)) if !src.is_empty() => Some(src.clone()),
_ => None,
}
}
#[derive(Debug, Clone)]
pub struct OpRef {
pub op: Arc<Op>,
pub token: Option<Token>,
}
impl OpRef {
pub fn new(op: Op) -> Self {
OpRef {
op: Arc::new(opify(op)),
token: None,
}
}
fn at(op: &Arc<Op>, token: Option<Token>) -> Self {
OpRef {
op: op.clone(),
token,
}
}
pub fn node(&self, terms: Vec<Value>) -> Value {
new_node(Some(self.op.clone()), self.token.clone(), terms)
}
fn node_under(&self, terms: Vec<Value>, under: &Value) -> Value {
new_node_under(
Some(self.op.clone()),
self.token.clone(),
terms,
Some(under),
)
}
}
pub fn opify(mut op: Op) -> Op {
if 0 == op.terms {
op.terms = if op.ternary {
3
} else if op.infix {
2
} else {
1
};
}
op
}
pub fn prattify(expr: &Value, op: &OpRef) -> Value {
let mut out = expr.clone();
let Some(expr_op) = op_of(expr) else {
return out;
};
let new = &op.op;
if new.infix {
if expr_op.suffix || new.left <= expr_op.right {
let prior = dup_node(expr);
update_expr_node(expr, Some(new.clone()), op.token.clone(), vec![prior]);
} else {
let end = expr_op.terms;
let last = term_at(expr, end);
let drill = op_of(&last).is_some_and(|last_op| last_op.right < new.left);
if drill {
out = prattify(&last, op);
} else {
out = op.node_under(vec![last], expr);
set_term(expr, end, out.clone());
}
}
} else if new.prefix {
out = op.node_under(Vec::new(), expr);
set_term(expr, expr_op.terms, out.clone());
} else if new.suffix {
if !expr_op.suffix && expr_op.right <= new.left {
let end = expr_op.terms;
let last = term_at(expr, end);
let drill =
op_of(&last).is_some_and(|last_op| last_op.prefix && last_op.right < new.left);
if drill {
prattify(&last, op);
} else {
set_term(expr, end, op.node_under(vec![last], expr));
}
} else {
let prior = dup_node(expr);
update_expr_node(expr, Some(new.clone()), op.token.clone(), vec![prior]);
}
}
out
}
fn prior_expr(rule: &mut Rule, prior: Option<&Rc<RuleSnapshot>>, op: &OpRef) -> Value {
let prior_value = prior.map_or(Value::Undefined, |prior| prior.node.borrow().clone());
let node = if is_op(&prior_value) && !op.op.prefix {
let duplicate = dup_node(&prior_value);
update_expr_node(
&prior_value,
Some(op.op.clone()),
op.token.clone(),
vec![duplicate],
);
prior_value
} else {
let terms = if op.op.prefix {
Vec::new()
} else {
vec![prior_value]
};
let fresh = op.node(terms);
if let Some(prior) = prior {
*prior.node.borrow_mut() = fresh.clone();
}
fresh
};
if let Some(prior) = prior {
rule.parent_rule = Some(prior.clone());
rule.parent_node = Some(prior.node.clone());
}
node
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
enum EvalKey {
Node(u64),
Shared(usize, usize),
}
thread_local! {
static EVALUATED: RefCell<Vec<EvalKey>> = const { RefCell::new(Vec::new()) };
}
fn eval_key(value: &Value) -> Option<EvalKey> {
if let Some(id) = node_id(value) {
return Some(EvalKey::Node(id));
}
match value {
Value::Array(entries) if !entries.is_empty() => Some(EvalKey::Shared(
Arc::as_ptr(entries) as usize,
entries.len(),
)),
Value::Object(entries) if !entries.is_empty() => Some(EvalKey::Shared(
Arc::as_ptr(entries) as usize,
entries.len(),
)),
Value::ListRef(list) => Some(EvalKey::Shared(
Arc::as_ptr(list) as usize,
list.value.len(),
)),
Value::MapRef(map) => Some(EvalKey::Shared(Arc::as_ptr(map) as usize, map.value.len())),
_ => None,
}
}
fn evaluated_seen(value: &Value) -> bool {
eval_key(value).is_some_and(|key| EVALUATED.with(|set| set.borrow().contains(&key)))
}
fn evaluated_mark(value: &Value) {
if let Some(key) = eval_key(value) {
EVALUATED.with(|set| {
let mut set = set.borrow_mut();
if !set.contains(&key) {
set.push(key);
}
});
}
}
pub fn evaluation(site: &mut EvalSite<'_>, value: &Value, evaluate: &Evaluate) -> Value {
if value.is_undefined() || matches!(value, Value::Null) {
return Value::Null;
}
if evaluated_seen(value) {
return value.clone();
}
let out = if is_op(value) {
let Some((Some(op), token, terms)) = with_node(value, |entry| {
(entry.op.clone(), entry.token.clone(), entry.terms.clone())
}) else {
return value.clone();
};
let reduced: Vec<Value> = terms
.iter()
.map(|term| evaluation(&mut site.reborrow(), term, evaluate))
.collect();
let previous = std::mem::replace(&mut site.token, token);
let reduction = evaluate(site, &op, &reduced);
site.token = previous;
reduction
} else if let Value::Array(entries) = value {
Value::array(
entries
.iter()
.map(|entry| evaluation(&mut site.reborrow(), entry, evaluate))
.collect(),
)
} else {
value.clone()
};
evaluated_mark(&out);
out
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum Anyfix {
Prefix,
Suffix,
Infix,
Ternary,
}
impl Anyfix {
fn name(self) -> &'static str {
match self {
Anyfix::Prefix => "prefix",
Anyfix::Suffix => "suffix",
Anyfix::Infix => "infix",
Anyfix::Ternary => "ternary",
}
}
fn selects(self, def: &OpDef) -> bool {
match self {
Anyfix::Prefix => def.prefix,
Anyfix::Suffix => def.suffix,
Anyfix::Infix => def.infix,
Anyfix::Ternary => def.ternary,
}
}
fn terms(self) -> usize {
match self {
Anyfix::Ternary => 3,
Anyfix::Infix => 2,
_ => 1,
}
}
}
fn operator_tin(parser: &mut Tabnas, src: &str) -> (Tin, String) {
if let Some(tin) = parser.fixed(src) {
let name = parser.token_name(tin);
return (tin, name);
}
let name = format!("#E{src}");
let tin = parser.token_with_source(name.clone(), src);
(tin, name)
}
fn binding_power(power: Option<i64>, unset: i64) -> i64 {
match power {
Some(0) | None => unset,
Some(power) => power,
}
}
fn make_op_map(
parser: &mut Tabnas,
ops: &IndexMap<String, Option<OpDef>>,
anyfix: Anyfix,
) -> IndexMap<Tin, Arc<Op>> {
let mut map: IndexMap<Tin, Arc<Op>> = IndexMap::new();
for (name, def) in ops {
let Some(def) = def else { continue };
if !anyfix.selects(def) {
continue;
}
let src = def.src.first().cloned().unwrap_or_default();
let (tin, tkn) = operator_tin(parser, &src);
let suffix = format!("-{}", anyfix.name());
let op = Op {
src: src.clone(),
left: binding_power(def.left, MIN_SAFE_INTEGER),
right: binding_power(def.right, MAX_SAFE_INTEGER),
name: if name.ends_with(&suffix) {
name.clone()
} else {
format!("{name}{suffix}")
},
infix: Anyfix::Infix == anyfix,
prefix: Anyfix::Prefix == anyfix,
suffix: Anyfix::Suffix == anyfix,
ternary: Anyfix::Ternary == anyfix,
tkn,
tin,
terms: anyfix.terms(),
use_data: def.use_data.clone(),
..Default::default()
};
if Anyfix::Ternary == anyfix {
let second = def.src.get(1).cloned().unwrap_or_default();
let (second_tin, second_tkn) = operator_tin(parser, &second);
let mut first = op.clone();
first.ternary_index = Some(0);
map.insert(tin, Arc::new(first));
let mut last = op;
last.src = second;
last.ternary_index = Some(1);
last.tkn = second_tkn;
last.tin = second_tin;
map.insert(second_tin, Arc::new(last));
} else {
map.insert(tin, Arc::new(op));
}
}
map
}
fn make_paren_map(
parser: &mut Tabnas,
ops: &IndexMap<String, Option<OpDef>>,
) -> IndexMap<Tin, Arc<Op>> {
let mut map: IndexMap<Tin, Arc<Op>> = IndexMap::new();
for (name, def) in ops {
let Some(def) = def else { continue };
if !def.paren {
continue;
}
let (otin, otkn) = operator_tin(parser, &def.osrc);
let (ctin, ctkn) = operator_tin(parser, &def.csrc);
let op = Op {
name: format!("{name}-paren"),
osrc: def.osrc.clone(),
csrc: def.csrc.clone(),
otkn,
otin,
ctkn,
ctin,
preval: def.preval.clone().unwrap_or_default(),
paren: true,
src: def.osrc.clone(),
terms: 1,
use_data: def.use_data.clone(),
..Default::default()
};
map.insert(otin, Arc::new(op));
}
map
}
struct Grammar {
prefix: IndexMap<Tin, Arc<Op>>,
suffix: IndexMap<Tin, Arc<Op>>,
infix: IndexMap<Tin, Arc<Op>>,
ternary: IndexMap<Tin, Arc<Op>>,
paren_open: IndexMap<Tin, Arc<Op>>,
paren_close: IndexMap<Tin, Arc<Op>>,
tin_prefix: Vec<Tin>,
tin_suffix: Vec<Tin>,
tin_infix: Vec<Tin>,
tin_tern0: Vec<Tin>,
tin_tern1: Vec<Tin>,
tin_open: Vec<Tin>,
tin_close: Vec<Tin>,
evaluate: Option<Evaluate>,
}
const VAL_TINS: [Tin; 4] = [TIN_TX, TIN_NR, TIN_ST, TIN_VL];
impl Grammar {
fn has_prefix(&self) -> bool {
!self.tin_prefix.is_empty()
}
fn has_infix(&self) -> bool {
!self.tin_infix.is_empty()
}
fn has_suffix(&self) -> bool {
!self.tin_suffix.is_empty()
}
fn has_ternary(&self) -> bool {
!self.tin_tern0.is_empty() && !self.tin_tern1.is_empty()
}
fn has_paren(&self) -> bool {
!self.tin_open.is_empty() && !self.tin_close.is_empty()
}
fn op_use(map: &IndexMap<Tin, Arc<Op>>, token: Option<&Token>) -> Option<OpRef> {
let token = token?;
map.get(&token.tin)
.map(|op| OpRef::at(op, Some(token.clone())))
}
}
fn counter(rule: &Rule, name: &str) -> i32 {
rule.n.get(name).copied().unwrap_or(0)
}
fn local_number(rule: &Rule, name: &str) -> i64 {
match rule.u.get(name) {
Some(Value::Number(number)) => *number as i64,
_ => 0,
}
}
fn set_local_number(rule: &mut Rule, name: &str, value: i64) {
rule.u_mut()
.insert(name.to_string(), Value::Number(value as f64));
}
fn set_node(rule: &mut Rule, value: Value) {
rule.node = Rc::new(RefCell::new(value));
}
fn node_of(rule: &Rule) -> Value {
rule.node.borrow().clone()
}
const ATTACH: &str = "expr_attach";
fn publish_expr(rule: &mut Rule, attach: Value) {
set_node(rule, attach.clone());
rule.u_mut().insert(ATTACH.to_string(), attach);
}
fn publish(rule: &mut Rule, value: Value) {
*rule.node.borrow_mut() = value;
}
fn expression_root(rule: &Rule) -> Value {
root_node(&node_of(rule))
}
fn linked_node(link: Option<&Rc<RuleSnapshot>>) -> Value {
let Some(link) = link else {
return Value::Undefined;
};
if let Some(attach) = link.u.get(ATTACH) {
if is_op(attach) {
return attach.clone();
}
}
link.node.borrow().clone()
}
fn write_linked_node(link: Option<&Rc<RuleSnapshot>>, value: Value) {
if let Some(link) = link {
*link.node.borrow_mut() = value;
}
}
fn counters(pairs: &[(&str, i32)]) -> HashMap<String, i32> {
pairs
.iter()
.map(|(name, value)| ((*name).to_string(), *value))
.collect()
}
fn locals(pairs: &[(&str, Value)]) -> HashMap<String, Value> {
pairs
.iter()
.map(|(name, value)| ((*name).to_string(), value.clone()))
.collect()
}
fn on(tins: &[Tin]) -> AltSpec {
AltSpec {
s: vec![tins.to_vec()],
..Default::default()
}
}
fn on2(first: &[Tin], second: &[Tin]) -> AltSpec {
AltSpec {
s: vec![first.to_vec(), second.to_vec()],
..Default::default()
}
}
fn always() -> AltSpec {
AltSpec::default()
}
fn group_has(tags: &str, tag: &str) -> bool {
tags.split(',').any(|entry| entry.trim() == tag)
}
fn prepend_open_all(spec: &mut RuleSpec, alts: Vec<AltSpec>) {
for alt in alts.into_iter().rev() {
spec.prepend_open(alt);
}
}
fn prepend_close_all(spec: &mut RuleSpec, alts: Vec<AltSpec>) {
for alt in alts.into_iter().rev() {
spec.prepend_close(alt);
}
}
pub fn expr(parser: &mut Tabnas, options: &ExprOptions) -> Result<(), PluginError> {
let rules = parser.rule_names();
if rules.iter().any(|name| "expr" == name) {
return Ok(());
}
if !rules.iter().any(|name| "val" == name) {
return Err(PluginError(
"tabnas-expr: the instance carries no `val` rule. This plugin layers on the \
jsonic base grammar; install that first (tabnas_jsonic::jsonic, or \
tabnas_expr::make)."
.into(),
));
}
let ops = resolve_options(options);
let prefix = make_op_map(parser, &ops, Anyfix::Prefix);
let suffix = make_op_map(parser, &ops, Anyfix::Suffix);
let infix = make_op_map(parser, &ops, Anyfix::Infix);
let ternary = make_op_map(parser, &ops, Anyfix::Ternary);
let paren_open = make_paren_map(parser, &ops);
let paren_close: IndexMap<Tin, Arc<Op>> = paren_open
.values()
.map(|op| (op.ctin, op.clone()))
.collect();
let grammar = Arc::new(Grammar {
tin_prefix: prefix.keys().copied().collect(),
tin_suffix: suffix.keys().copied().collect(),
tin_infix: infix.keys().copied().collect(),
tin_tern0: ternary
.values()
.filter(|op| Some(0) == op.ternary_index)
.map(|op| op.tin)
.collect(),
tin_tern1: ternary
.values()
.filter(|op| Some(1) == op.ternary_index)
.map(|op| op.tin)
.collect(),
tin_open: paren_open.values().map(|op| op.otin).collect(),
tin_close: paren_close.values().map(|op| op.ctin).collect(),
prefix,
suffix,
infix,
ternary,
paren_open,
paren_close,
evaluate: options.evaluate.clone(),
});
comment_before_fixed(parser, &grammar);
parser.parse_prepare(|_context: &mut Context| release_arena());
bound_expression_size(parser);
install_val(parser, &grammar);
install_list(parser, &grammar);
install_map(parser, &grammar);
install_elem(parser, &grammar);
install_pair(parser, &grammar);
install_expr(parser, &grammar);
if grammar.has_paren() {
install_paren(parser, &grammar);
}
if grammar.has_ternary() {
install_ternary(parser, &grammar);
}
Ok(())
}
fn comment_before_fixed(parser: &mut Tabnas, grammar: &Grammar) {
let options = parser.config();
let markers: Vec<String> = options
.comment
.definitions
.values()
.filter(|def| def.lex)
.map(|def| def.start.clone())
.filter(|start| !start.is_empty())
.collect();
if markers.is_empty() {
return;
}
let sources: Vec<String> = grammar
.prefix
.values()
.chain(grammar.suffix.values())
.chain(grammar.infix.values())
.chain(grammar.ternary.values())
.map(|op| op.src.clone())
.chain(
grammar
.paren_open
.values()
.flat_map(|op| [op.osrc.clone(), op.csrc.clone()]),
)
.filter(|src| !src.is_empty())
.collect();
let contested: Vec<String> = markers
.into_iter()
.filter(|marker| {
sources
.iter()
.any(|src| src.len() < marker.len() && marker.starts_with(src.as_str()))
})
.collect();
if contested.is_empty() {
return;
}
parser.lex_check_ref(COMMENT_FIRST, move |remaining: &str| {
if contested
.iter()
.any(|marker| remaining.starts_with(marker.as_str()))
{
tabnas::LexCheckResult::Skip
} else {
tabnas::LexCheckResult::Continue
}
});
let _ = parser.grammar_json(COMMENT_FIRST_DOCUMENT);
}
fn bound_expression_size(parser: &mut Tabnas) {
let budget = parser.config().parse.budget.clone();
let existing = budget.on_check.clone();
let every = match budget.check_every_n {
0 => 32,
set => set.min(32),
};
parser.parse_budget(every, move |context: &Context| {
if OVER_LIMIT.with(Cell::get) {
return false;
}
if context.rule_stack.len() > RULE_LIMIT {
return false;
}
existing.as_ref().is_none_or(|check| check(context))
});
}
const COMMENT_FIRST: &str = "@expr-comment-before-fixed";
const COMMENT_FIRST_DOCUMENT: &str =
r#"{"options":{"fixed":{"check":"@expr-comment-before-fixed"}}}"#;
fn install_val(parser: &mut Tabnas, grammar: &Arc<Grammar>) {
let grammar = grammar.clone();
parser.define_rule("val", move |spec| {
if grammar.has_ternary() && grammar.tin_tern1.contains(&TIN_CL) {
for alt in spec.open.iter_mut().filter(|alt| group_has(&alt.g, "pair")) {
let previous = alt.c_fn.clone();
alt.c_fn = Some(Arc::new(move |rule: &mut Rule, context: &mut Context| {
previous
.as_ref()
.is_none_or(|condition| condition(rule, context))
&& 0 == counter(rule, "expr_ternary")
}));
}
}
let mut open: Vec<AltSpec> = Vec::new();
if grammar.has_prefix() {
let mut alt = on(&grammar.tin_prefix);
alt.b = 1;
alt.n = counters(&[("expr_prefix", 1), ("expr_suffix", 0)]);
alt.p = Some("expr".into());
alt.g = "expr,expr-prefix".into();
alt.add_action(detach_node);
open.push(alt);
}
if grammar.has_paren() {
let mut alt = on2(&VAL_TINS, &grammar.tin_open);
alt.b = 1;
alt.p = Some("expr".into());
let condition = grammar.clone();
alt.c_fn = Some(Arc::new(move |rule: &mut Rule, context: &mut Context| {
let Some(pdef) = rule
.o1()
.and_then(|token| condition.paren_open.get(&token.tin))
.cloned()
else {
return false;
};
if !pdef.preval.active {
return false;
}
let Some(allow) = &pdef.preval.allow else {
return true;
};
let preval = rule.resolve_open_value(0, context);
allow.contains(&value_string(Some(&preval)))
}));
alt.u = locals(&[("paren_preval", Value::Bool(true))]);
alt.g = "expr,expr-paren,expr-paren-preval".into();
alt.add_action(|rule: &mut Rule, context: &mut Context| {
let preval = rule.resolve_open_value(0, context);
set_node(rule, preval);
});
open.push(alt);
}
if grammar.has_paren() {
let mut alt = on(&grammar.tin_open);
alt.b = 1;
alt.p = Some("expr".into());
let condition = grammar.clone();
alt.c_fn = Some(Arc::new(move |rule: &mut Rule, _context: &mut Context| {
rule.o0()
.and_then(|token| condition.paren_open.get(&token.tin))
.is_some_and(|pdef| !pdef.preval.required)
}));
alt.g = "expr,expr-paren".into();
alt.add_action(detach_node);
open.push(alt);
}
prepend_open_all(spec, open);
let mut close: Vec<AltSpec> = Vec::new();
if grammar.has_infix() {
let mut alt = on(&grammar.tin_infix);
alt.c_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
0 < counter(rule, "no_comma_op")
&& rule.c0().is_some_and(|token| "," == token.src.as_str())
}));
alt.b = 1;
alt.g = "expr,no-comma-op-bail".into();
close.push(alt);
}
if grammar.has_ternary() {
let mut alt = on(&grammar.tin_tern0);
alt.c_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
0 == counter(rule, "expr")
}));
alt.b = 1;
alt.r = Some("ternary".into());
alt.g = "expr,expr-ternary".into();
close.push(alt);
}
if grammar.has_infix() {
let mut alt = on(&grammar.tin_infix);
alt.b = 1;
alt.n = counters(&[("expr_prefix", 0), ("expr_suffix", 0)]);
alt.r_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
(0 == counter(rule, "expr")).then(|| "expr".to_string())
}));
alt.g = "expr,expr-infix".into();
close.push(alt);
}
if grammar.has_suffix() {
let mut alt = on(&grammar.tin_suffix);
alt.b = 1;
alt.n = counters(&[("expr_prefix", 0), ("expr_suffix", 1)]);
alt.r_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
(0 == counter(rule, "expr")).then(|| "expr".to_string())
}));
alt.g = "expr,expr-suffix".into();
close.push(alt);
}
if grammar.has_paren() {
let mut alt = on(&grammar.tin_close);
alt.c_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
0 != counter(rule, "expr_paren")
}));
alt.b = 1;
alt.g = "expr,expr-paren".into();
close.push(alt);
}
if grammar.has_paren() {
let mut alt = on(&grammar.tin_open);
alt.b = 1;
let condition = grammar.clone();
alt.c_fn = Some(Arc::new(move |rule: &mut Rule, _context: &mut Context| {
let Some(pdef) = rule
.c0()
.and_then(|token| condition.paren_open.get(&token.tin))
.cloned()
else {
return false;
};
let node = node_of(rule);
if !pdef.preval.active || node.is_undefined() {
return false;
}
let Some(allow) = &pdef.preval.allow else {
return true;
};
allow.contains(&value_string(Some(&node)))
}));
alt.p = Some("expr".into());
alt.u = locals(&[("paren_preval", Value::Bool(true))]);
alt.g = "expr,expr-paren,expr-paren-preval-chain".into();
close.push(alt);
}
if grammar.has_ternary() {
let mut alt = on(&grammar.tin_tern1);
alt.c_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
0 != counter(rule, "expr_ternary")
}));
alt.b = 1;
alt.g = "expr,expr-ternary".into();
close.push(alt);
}
let mut comma = on(&[TIN_CA]);
comma.c_fn = Some(Arc::new(no_implicit_in_expr));
comma.b = 1;
comma.g = "expr,list,val,imp,comma,top".into();
close.push(comma);
let mut space = on(&VAL_TINS);
space.c_fn = Some(Arc::new(no_implicit_in_expr));
space.b = 1;
space.g = "expr,list,val,imp,space,top".into();
close.push(space);
prepend_close_all(spec, close);
});
}
fn detach_node(rule: &mut Rule, _context: &mut Context) {
let node = node_of(rule);
set_node(rule, node);
}
fn no_implicit_in_expr(rule: &mut Rule, _context: &mut Context) -> bool {
(1 == rule.d && (1 <= counter(rule, "expr") || 1 <= counter(rule, "expr_ternary")))
|| (1 <= counter(rule, "expr_ternary") && 1 <= counter(rule, "expr_paren"))
}
fn clear_expr_counters(rule: &mut Rule) {
let counters = rule.n_mut();
counters.insert("expr".into(), 0);
counters.insert("expr_prefix".into(), 0);
counters.insert("expr_suffix".into(), 0);
counters.insert("expr_paren".into(), 0);
counters.insert("expr_ternary".into(), 0);
}
fn install_list(parser: &mut Tabnas, grammar: &Arc<Grammar>) {
let grammar = grammar.clone();
parser.define_rule("list", move |spec| {
spec.prepend_bo(|rule: &mut Rule, _context: &mut Context| {
let implicit = rule
.prev_rule
.as_ref()
.is_some_and(|prev| matches!(prev.u.get("implist"), Some(Value::Bool(true))));
if !implicit {
clear_expr_counters(rule);
}
});
if grammar.has_paren() {
let mut alt = on(&grammar.tin_close);
alt.b_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
let square = rule.c0().is_some_and(|token| TIN_CS == token.tin);
if square && 0 == counter(rule, "expr_paren") {
0
} else {
1
}
}));
alt.g = "expr".into();
prepend_close_all(spec, vec![alt]);
spec.add_ac(|rule: &mut Rule, _context: &mut Context| {
propagate_to_paren(rule);
});
}
});
}
fn install_map(parser: &mut Tabnas, grammar: &Arc<Grammar>) {
let grammar = grammar.clone();
parser.define_rule("map", move |spec| {
spec.prepend_bo(|rule: &mut Rule, _context: &mut Context| clear_expr_counters(rule));
if grammar.has_paren() {
let mut alt = on(&grammar.tin_close);
alt.b_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
let brace = rule.c0().is_some_and(|token| TIN_CB == token.tin);
if brace && 0 == counter(rule, "expr_paren") {
0
} else {
1
}
}));
alt.g = "expr".into();
prepend_close_all(spec, vec![alt]);
}
});
}
fn install_elem(parser: &mut Tabnas, grammar: &Arc<Grammar>) {
let grammar = grammar.clone();
parser.define_rule("elem", move |spec| {
if !grammar.has_paren() {
return;
}
let mut close: Vec<AltSpec> = Vec::new();
let mut alt = on(&grammar.tin_close);
alt.b = 1;
alt.c_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
0 != counter(rule, "expr_paren")
}));
alt.g = "expr,expr-paren,imp,close,list".into();
close.push(alt);
let mut alt = on(&grammar.tin_open);
alt.b = 1;
alt.r = Some("elem".into());
alt.g = "expr,expr-paren,imp,open,list".into();
close.push(alt);
prepend_close_all(spec, close);
spec.add_ac(|rule: &mut Rule, _context: &mut Context| {
propagate_to_paren(rule);
});
});
}
fn propagate_to_paren(rule: &mut Rule) {
if 0 == counter(rule, "expr_paren") {
return;
}
let node = node_of(rule);
let mut cursor = rule.parent_rule.clone();
while let Some(current) = cursor {
if "paren" == current.name.as_ref() {
*current.node.borrow_mut() = node;
return;
}
cursor = current.parent_rule.clone();
}
}
fn install_pair(parser: &mut Tabnas, grammar: &Arc<Grammar>) {
let grammar = grammar.clone();
parser.define_rule("pair", move |spec| {
if !grammar.has_paren() {
return;
}
let mut alt = on(&grammar.tin_close);
alt.b = 1;
alt.c_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
0 != counter(rule, "expr_paren") || 0 < counter(rule, "pk")
}));
alt.g = "expr,expr-paren,imp,map".into();
prepend_close_all(spec, vec![alt]);
});
}
fn expr_root(rule: &Rule) -> Value {
let parent = linked_node(rule.parent_rule.as_ref());
if rule.parent_rule.is_some() && is_op(&parent) {
return parent;
}
let mut root: Option<Value> = None;
if "expr" == rule.name.as_ref() {
let node = node_of(rule);
if is_op(&node) {
root = Some(node);
}
}
let mut cursor = rule.prev_rule.clone();
while let Some(current) = cursor {
if "expr" == current.name.as_ref() {
let node = current.node.borrow().clone();
if is_op(&node) {
root = Some(node);
}
}
cursor = current.prev_rule.clone();
}
root.unwrap_or_else(|| node_of(rule))
}
fn top_list(rule: &mut Rule) -> Value {
let list = Value::array(vec![expr_root(rule)]);
if rule.parent_rule.is_some() {
write_linked_node(rule.parent_rule.as_ref(), list.clone());
return list;
}
set_node(rule, list.clone());
let mut cursor = rule.prev_rule.clone();
while let Some(current) = cursor {
*current.node.borrow_mut() = list.clone();
cursor = current.prev_rule.clone();
}
list
}
fn implicit_list(mut matched: AltMatch, rule: &mut Rule, context: &mut Context) -> AltMatch {
let mut paren: Option<Rc<RuleSnapshot>> = None;
let mut paren_index = 0usize;
for index in (0..context.rule_stack.len()).rev() {
let frame = &context.rule_stack[index];
let name = frame.name.as_ref();
if "paren" == name {
paren = Some(frame.clone());
paren_index = index;
break;
}
if "map" == name || "list" == name {
return matched;
}
}
let Some(paren) = paren else {
return matched;
};
let expr_node = node_of(rule);
let expr_node = if is_op(&expr_node) {
expression_root(rule)
} else {
expr_node
};
let paren_node = paren.node.borrow().clone();
if matches!(&paren_node, Value::Array(members) if !members.is_empty()) {
return matched;
}
*paren.node.borrow_mut() = Value::array(vec![expr_node.clone()]);
matched.r = Some("elem".into());
matched.b = 0;
let list = paren.node.borrow().clone();
set_node(rule, list.clone());
write_linked_node(rule.parent_rule.as_ref(), list.clone());
for index in (paren_index + 1)..context.rule_stack.len() {
let frame = context.rule_stack[index].clone();
let name = frame.name.as_ref().to_string();
let node = frame.node.borrow().clone();
let carries = "expr" == name
|| ("val" == name
&& (node.is_undefined()
|| matches!(node, Value::Null)
|| same_node(&node, &expr_node)
|| is_op(&node)));
if carries {
*frame.node.borrow_mut() = list.clone();
}
}
matched
}
fn same_node(left: &Value, right: &Value) -> bool {
match (node_id(left), node_id(right)) {
(Some(left), Some(right)) => left == right,
_ => false,
}
}
fn install_expr(parser: &mut Tabnas, grammar: &Arc<Grammar>) {
let grammar = grammar.clone();
parser.define_rule("expr", move |spec| {
let mut open: Vec<AltSpec> = Vec::new();
if grammar.has_paren() {
let mut alt = on(&grammar.tin_open);
alt.p = Some("paren".into());
alt.b = 1;
alt.g = "expr,expr-paren,expr-start".into();
open.push(alt);
}
if grammar.has_prefix() {
let mut alt = on(&grammar.tin_prefix);
alt.c_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
0 != counter(rule, "expr_prefix")
}));
alt.n = counters(&[("expr", 1), ("dlist", 1), ("dmap", 1)]);
alt.p = Some("val".into());
alt.g = "expr,expr-prefix".into();
let ops = grammar.clone();
alt.add_action(move |rule: &mut Rule, _context: &mut Context| {
let Some(op) = Grammar::op_use(&ops.prefix, rule.o0()) else {
return;
};
let parent = linked_node(rule.parent_rule.as_ref());
if is_op(&parent) && !is_ternary_op(&parent) && !is_paren_op(&parent) {
let attach = prattify(&parent, &op);
publish_expr(rule, attach);
} else {
let prior = rule.parent_rule.clone();
let node = prior_expr(rule, prior.as_ref(), &op);
publish_expr(rule, node);
}
});
open.push(alt);
}
if grammar.has_infix() {
let mut alt = on(&grammar.tin_infix);
alt.p = Some("val".into());
alt.n = counters(&[("expr", 1), ("expr_prefix", 0), ("dlist", 1), ("dmap", 1)]);
alt.g = "expr,expr-infix".into();
let ops = grammar.clone();
alt.add_action(move |rule: &mut Rule, _context: &mut Context| {
let Some(op) = Grammar::op_use(&ops.infix, rule.o0()) else {
return;
};
let parent = linked_node(rule.parent_rule.as_ref());
let previous = linked_node(rule.prev_rule.as_ref());
if is_op(&parent) && !is_ternary_op(&parent) {
let attach = prattify(&parent, &op);
publish_expr(rule, attach);
}
else if is_op(&previous) {
let attach = prattify(&previous, &op);
if let Some(prev) = rule.prev_rule.clone() {
rule.parent_node = Some(prev.node.clone());
rule.parent_rule = Some(prev);
}
publish_expr(rule, attach);
}
else {
let prior = rule.prev_rule.clone();
let node = prior_expr(rule, prior.as_ref(), &op);
publish_expr(rule, node);
}
});
open.push(alt);
}
if grammar.has_suffix() {
let mut alt = on(&grammar.tin_suffix);
alt.n = counters(&[("expr", 1), ("expr_prefix", 0), ("dlist", 1), ("dmap", 1)]);
alt.g = "expr,expr-suffix".into();
let ops = grammar.clone();
alt.add_action(move |rule: &mut Rule, _context: &mut Context| {
let Some(op) = Grammar::op_use(&ops.suffix, rule.o0()) else {
return;
};
let previous = linked_node(rule.prev_rule.as_ref());
if is_op(&previous) {
let attach = prattify(&previous, &op);
publish_expr(rule, attach);
} else {
let prior = rule.prev_rule.clone();
let node = prior_expr(rule, prior.as_ref(), &op);
publish_expr(rule, node);
}
});
open.push(alt);
}
prepend_open_all(spec, open);
spec.add_bc(|rule: &mut Rule, _context: &mut Context| {
let node = node_of(rule);
let Some(op) = op_of(&node) else {
return;
};
let child = rule.child_node.clone();
if term_count(&node) < op.terms && !same_node(&node, &child) {
push_term(&node, child);
}
});
let mut close: Vec<AltSpec> = Vec::new();
let mut alt = always();
alt.c_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
rule.child_rule
.as_ref()
.is_some_and(|child| "paren" == child.name.as_ref())
}));
alt.n = counters(&[("expr", 0)]);
alt.g = "expr,expr-end,expr-paren-end".into();
close.push(alt);
if grammar.has_infix() {
let mut alt = on(&grammar.tin_infix);
alt.c_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
0 < counter(rule, "no_comma_op")
&& rule.c0().is_some_and(|token| "," == token.src.as_str())
}));
alt.b = 1;
alt.n = counters(&[("expr", 0)]);
alt.g = "expr,no-comma-op-bail".into();
close.push(alt);
}
if grammar.has_infix() {
let mut alt = on(&grammar.tin_infix);
alt.c_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
0 == counter(rule, "expr_prefix")
}));
alt.b = 1;
alt.r = Some("expr".into());
alt.g = "expr,expr-infix,expr-prefix".into();
close.push(alt);
let mut alt = on(&grammar.tin_infix);
alt.c_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
0 != counter(rule, "expr_prefix")
}));
alt.b = 1;
alt.g = "expr,expr-infix".into();
close.push(alt);
}
if grammar.has_suffix() {
let mut alt = on(&grammar.tin_suffix);
alt.c_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
0 == counter(rule, "expr_prefix")
}));
alt.b = 1;
alt.r = Some("expr".into());
alt.g = "expr,expr-suffix,expr-prefix".into();
close.push(alt);
}
if grammar.has_paren() {
let mut alt = on(&grammar.tin_close);
alt.c_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
0 != counter(rule, "expr_paren")
}));
alt.b = 1;
alt.g = "expr".into();
close.push(alt);
}
if grammar.has_ternary() {
let mut alt = on(&grammar.tin_tern0);
alt.c_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
0 == counter(rule, "expr_prefix")
}));
alt.b = 1;
alt.r = Some("ternary".into());
alt.g = "expr,expr-ternary".into();
close.push(alt);
}
let mut alt = on(&[TIN_CA]);
alt.c_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
0 == rule.d
}));
alt.n = counters(&[("expr", 0)]);
alt.r = Some("elem".into());
alt.add_action(|rule: &mut Rule, _context: &mut Context| {
let list = top_list(rule);
set_node(rule, list);
});
alt.g = "expr,comma,list,top".into();
close.push(alt);
let mut alt = on(&VAL_TINS);
alt.c_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
0 == rule.d
}));
alt.n = counters(&[("expr", 0)]);
alt.b = 1;
alt.r = Some("elem".into());
alt.add_action(|rule: &mut Rule, _context: &mut Context| {
let list = top_list(rule);
set_node(rule, list);
});
alt.g = "expr,space,list,top".into();
close.push(alt);
let mut alt = on(&[TIN_CA]);
alt.c_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
rule.lte("pk", 0)
}));
alt.n = counters(&[("expr", 0)]);
alt.b = 1;
alt.h_match = Some(Arc::new(|matched, rule, context, _next| {
implicit_list(matched, rule, context)
}));
alt.g = "expr,list,val,imp,comma".into();
close.push(alt);
let mut alt = always();
let suffix_tins = grammar.tin_suffix.clone();
alt.c_fn = Some(Arc::new(move |rule: &mut Rule, context: &mut Context| {
rule.lte("pk", 0)
&& rule.lte("expr_suffix", 0)
&& !context
.t0()
.is_some_and(|token| suffix_tins.contains(&token.tin))
}));
alt.n = counters(&[("expr", 0)]);
alt.h_match = Some(Arc::new(|matched, rule, context, _next| {
implicit_list(matched, rule, context)
}));
alt.g = "expr,list,val,imp,space".into();
close.push(alt);
let mut alt = always();
alt.n = counters(&[("expr", 0)]);
alt.g = "expr,expr-end".into();
close.push(alt);
prepend_close_all(spec, close);
spec.add_ac(|rule: &mut Rule, _context: &mut Context| {
if is_op(&node_of(rule)) {
let root = expression_root(rule);
set_node(rule, root);
}
});
if let Some(evaluate) = grammar.evaluate.clone() {
spec.add_ac(move |rule: &mut Rule, context: &mut Context| {
if 0 != counter(rule, "expr") {
return;
}
let Some(parent) = rule.parent_rule.clone() else {
return;
};
let parent_node = parent.node.borrow().clone();
if let Value::Array(members) = &parent_node {
let mut site = EvalSite::new(Some(parent.clone()), context);
let reduced: Vec<Value> = members
.iter()
.map(|member| evaluation(&mut site.reborrow(), member, &evaluate))
.collect();
let reduced = Value::array(reduced);
*parent.node.borrow_mut() = reduced.clone();
publish(rule, reduced);
return;
}
let out = {
let mut site = EvalSite::new(Some(parent.clone()), context);
evaluation(&mut site, &parent_node, &evaluate)
};
*parent.node.borrow_mut() = out.clone();
publish(rule, out);
});
}
});
}
fn paren_depth(name: &str) -> String {
format!("expr_paren_depth_{name}")
}
fn install_paren(parser: &mut Tabnas, grammar: &Arc<Grammar>) {
let grammar = grammar.clone();
parser.define_rule("paren", move |spec| {
spec.add_bo(|rule: &mut Rule, _context: &mut Context| {
let counters = rule.n_mut();
counters.insert("dmap".into(), 0);
counters.insert("dlist".into(), 0);
counters.insert("pk".into(), 0);
});
let mut open: Vec<AltSpec> = Vec::new();
let mut alt = on2(&grammar.tin_open, &grammar.tin_close);
alt.b = 1;
alt.g = "expr,expr-paren,empty".into();
let condition = grammar.clone();
alt.c_fn = Some(Arc::new(move |rule: &mut Rule, _context: &mut Context| {
let open = rule
.o0()
.and_then(|token| condition.paren_open.get(&token.tin));
let close = rule
.o1()
.and_then(|token| condition.paren_close.get(&token.tin));
match (open, close) {
(Some(open), Some(close)) => open.name == close.name,
_ => false,
}
}));
let ops = grammar.clone();
alt.add_action(move |rule: &mut Rule, _context: &mut Context| {
open_paren(&ops, rule);
});
open.push(alt);
let mut alt = on(&grammar.tin_open);
alt.p = Some("val".into());
alt.n = counters(&[
("expr_paren", 1),
("expr", 0),
("expr_prefix", 0),
("expr_suffix", 0),
]);
alt.g = "expr,expr-paren,open".into();
let ops = grammar.clone();
alt.add_action(move |rule: &mut Rule, _context: &mut Context| {
open_paren(&ops, rule);
});
open.push(alt);
prepend_open_all(spec, open);
let mut alt = on(&grammar.tin_close);
let condition = grammar.clone();
alt.c_fn = Some(Arc::new(move |rule: &mut Rule, _context: &mut Context| {
let Some(pdef) = rule
.c0()
.and_then(|token| condition.paren_close.get(&token.tin))
.cloned()
else {
return false;
};
0 != counter(rule, &paren_depth(&pdef.name))
}));
let ops = grammar.clone();
alt.add_action(move |rule: &mut Rule, _context: &mut Context| {
close_paren(&ops, rule);
});
alt.g = "expr,expr-paren,close".into();
prepend_close_all(spec, vec![alt]);
spec.add_ac(|rule: &mut Rule, _context: &mut Context| {
let node = node_of(rule);
let parent = rule.parent_rule.clone();
write_linked_node(parent.as_ref(), node.clone());
let grandparent = parent.and_then(|parent| parent.parent_rule.clone());
write_linked_node(grandparent.as_ref(), node);
});
});
}
fn open_paren(grammar: &Grammar, rule: &mut Rule) {
let Some(op) = Grammar::op_use(&grammar.paren_open, rule.o0()) else {
return;
};
let depth = paren_depth(&op.op.name);
rule.u_mut().insert(depth.clone(), Value::Number(1.0));
rule.n_mut().insert(depth, 1);
set_node(rule, Value::Undefined);
}
fn close_paren(grammar: &Grammar, rule: &mut Rule) {
let child = rule.child_node.clone();
let mut node = node_of(rule);
if node.is_undefined() {
node = child;
}
let Some(op) = Grammar::op_use(&grammar.paren_close, rule.c0()) else {
set_node(rule, node);
return;
};
let depth = paren_depth(&op.op.name);
if local_number(rule, &depth) == i64::from(counter(rule, &depth)) {
let mut terms: Vec<Value> = Vec::new();
let grandparent = rule
.parent_rule
.as_ref()
.and_then(|parent| parent.parent_rule.clone());
if let Some(grandparent) = &grandparent {
let preval = grandparent.node.borrow().clone();
if matches!(grandparent.u.get("paren_preval"), Some(Value::Bool(true)))
&& !preval.is_undefined()
{
terms.push(preval);
}
}
if !node.is_undefined() {
terms.push(node);
}
set_node(rule, op.node(terms));
} else {
set_node(rule, node);
}
}
fn install_ternary(parser: &mut Tabnas, grammar: &Arc<Grammar>) {
let grammar = grammar.clone();
parser.define_rule("ternary", move |spec| {
let mut open: Vec<AltSpec> = Vec::new();
let mut alt = on(&grammar.tin_tern0);
alt.p = Some("val".into());
alt.n = counters(&[
("expr_ternary", 1),
("expr", 0),
("expr_prefix", 0),
("expr_suffix", 0),
]);
alt.u = locals(&[("expr_ternary_step", Value::Number(1.0))]);
alt.g = "expr,expr-ternary,open".into();
let ops = grammar.clone();
alt.add_action(move |rule: &mut Rule, _context: &mut Context| {
let Some(op) = Grammar::op_use(&ops.ternary, rule.o0()) else {
return;
};
rule.u_mut().insert(
"expr_ternary_name".into(),
Value::String(op.op.name.clone()),
);
let previous = linked_node(rule.prev_rule.as_ref());
let node = if is_op(&previous) {
let duplicate = dup_node(&previous);
update_expr_node(
&previous,
Some(op.op.clone()),
op.token.clone(),
vec![duplicate],
);
previous
} else {
let node = op.node(vec![previous]);
write_linked_node(rule.prev_rule.as_ref(), node.clone());
node
};
set_node(rule, node);
let paren = if 0 != counter(rule, "expr_paren") {
i64::from(counter(rule, "expr_paren"))
} else {
rule.prev_rule
.as_ref()
.map_or(0, |prev| snapshot_number(prev, "expr_ternary_paren"))
};
rule.u_mut()
.insert("expr_ternary_paren".into(), Value::Number(paren as f64));
rule.n_mut().insert("expr_paren".into(), 0);
});
open.push(alt);
let mut alt = always();
alt.p = Some("val".into());
alt.c_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
rule.prev_rule
.as_ref()
.is_some_and(|prev| 2 == snapshot_number(prev, "expr_ternary_step"))
}));
alt.add_action(|rule: &mut Rule, _context: &mut Context| {
let (step, paren) = rule.prev_rule.as_ref().map_or((0, 0), |prev| {
(
snapshot_number(prev, "expr_ternary_step"),
snapshot_number(prev, "expr_ternary_paren"),
)
});
set_local_number(rule, "expr_ternary_step", step);
set_local_number(rule, "expr_ternary_paren", paren);
rule.n_mut().insert("expr_paren".into(), paren as i32);
});
alt.g = "expr,expr-ternary,step".into();
open.push(alt);
prepend_open_all(spec, open);
let mut close: Vec<AltSpec> = Vec::new();
let mut alt = on(&grammar.tin_tern1);
let condition = grammar.clone();
alt.c_fn = Some(Arc::new(move |rule: &mut Rule, _context: &mut Context| {
if 1 != local_number(rule, "expr_ternary_step") {
return false;
}
let Some(op) = rule
.c0()
.and_then(|token| condition.ternary.get(&token.tin))
.cloned()
else {
return false;
};
matches!(rule.u.get("expr_ternary_name"), Some(Value::String(name)) if name == &op.name)
}));
alt.r = Some("ternary".into());
alt.add_action(|rule: &mut Rule, _context: &mut Context| {
let step = local_number(rule, "expr_ternary_step");
set_local_number(rule, "expr_ternary_step", step + 1);
let node = node_of(rule);
push_term(&node, rule.child_node.clone());
});
alt.g = "expr,expr-ternary,step".into();
close.push(alt);
let mut tins = vec![TIN_CA];
tins.extend(grammar.tin_close.iter().copied());
let mut alt = on(&tins);
alt.c_fn = Some(Arc::new(implicit_ternary_cond));
let closers = grammar.tin_close.clone();
alt.b_fn = Some(Arc::new(move |_rule: &mut Rule, context: &mut Context| {
usize::from(next_is_close(context, &closers))
}));
let closers = grammar.tin_close.clone();
alt.r_fn = Some(Arc::new(move |rule: &mut Rule, context: &mut Context| {
let to_elem = !next_is_close(context, &closers)
&& (0 == rule.d
|| (rule
.prev_rule
.as_ref()
.is_some_and(|prev| 0 != snapshot_number(prev, "expr_ternary_paren"))
&& 0 == node_length(&linked_node(rule.parent_rule.as_ref()))));
to_elem.then(|| "elem".to_string())
}));
alt.add_action_with_match(|rule: &mut Rule, _context: &mut Context, matched| {
implicit_ternary_action(rule, matched);
None
});
alt.g = "expr,expr-ternary,list,val,imp,comma".into();
close.push(alt);
let mut alt = always();
alt.c_fn = Some(Arc::new(implicit_ternary_cond));
let closers = grammar.tin_close.clone();
alt.r_fn = Some(Arc::new(move |rule: &mut Rule, context: &mut Context| {
let ended = context.t0().is_some_and(|token| TIN_ZZ == token.tin);
let to_elem = (0 == rule.d
|| !next_is_close(context, &closers)
|| rule
.prev_rule
.as_ref()
.is_some_and(|prev| 0 != snapshot_number(prev, "expr_ternary_paren")))
&& 0 == node_length(&linked_node(rule.parent_rule.as_ref()))
&& !ended;
to_elem.then(|| "elem".to_string())
}));
alt.add_action_with_match(|rule: &mut Rule, _context: &mut Context, matched| {
implicit_ternary_action(rule, matched);
None
});
alt.g = "expr,expr-ternary,list,val,imp,space".into();
close.push(alt);
let mut alt = always();
alt.c_fn = Some(Arc::new(|rule: &mut Rule, _context: &mut Context| {
0 < rule.d && 2 == local_number(rule, "expr_ternary_step")
}));
alt.add_action(|rule: &mut Rule, _context: &mut Context| {
let node = node_of(rule);
push_term(&node, rule.child_node.clone());
});
alt.g = "expr,expr-ternary,close".into();
close.push(alt);
prepend_close_all(spec, close);
if let Some(evaluate) = grammar.evaluate.clone() {
spec.add_ac(move |rule: &mut Rule, context: &mut Context| {
if rule
.next_rule_name
.as_ref()
.is_some_and(|name| "ternary" == name.as_ref())
{
return;
}
let node = node_of(rule);
if !is_op(&node) {
return;
}
if node_length(&node) < 4 {
return;
}
let out = {
let site_rule = rule.snapshot();
let mut site = EvalSite::new(Some(site_rule), context);
evaluation(&mut site, &node, &evaluate)
};
publish(rule, out.clone());
let mut cursor = rule.prev_rule.clone();
while let Some(current) = cursor {
*current.node.borrow_mut() = out.clone();
cursor = current.prev_rule.clone();
}
write_linked_node(rule.parent_rule.as_ref(), out);
});
}
});
}
fn snapshot_number(snapshot: &RuleSnapshot, name: &str) -> i64 {
match snapshot.u.get(name) {
Some(Value::Number(number)) => *number as i64,
_ => 0,
}
}
fn next_is_close(context: &mut Context, closers: &[Tin]) -> bool {
context
.t0()
.is_some_and(|token| closers.contains(&token.tin))
}
fn implicit_ternary_cond(rule: &mut Rule, _context: &mut Context) -> bool {
(0 == rule.d || 1 <= counter(rule, "expr_paren"))
&& 0 == counter(rule, "pk")
&& 2 == local_number(rule, "expr_ternary_step")
}
fn implicit_ternary_action(rule: &mut Rule, matched: &AltMatch) {
let paren = rule
.prev_rule
.as_ref()
.map_or(0, |prev| snapshot_number(prev, "expr_ternary_paren"));
rule.n_mut().insert("expr_paren".into(), paren as i32);
let node = node_of(rule);
push_term(&node, rule.child_node.clone());
if matched.r.as_deref() != Some("elem") {
return;
}
let list = Value::array(vec![node]);
publish(rule, list.clone());
if 1 <= counter(rule, "expr_paren") {
let mut cursor = rule.parent_rule.clone();
while let Some(current) = cursor {
if "paren" == current.name.as_ref() {
*current.node.borrow_mut() = list;
break;
}
cursor = current.parent_rule.clone();
}
}
}
pub fn plugin() -> Plugin {
Plugin::new(PLUGIN_NAME, |parser: &mut Tabnas, options: &Value| {
expr(parser, &ExprOptions::from_value(options))
})
.with_defaults(defaults())
}
pub fn plugin_with(options: ExprOptions) -> Plugin {
Plugin::new(PLUGIN_NAME, move |parser: &mut Tabnas, _options: &Value| {
expr(parser, &options)
})
}
pub fn apply(parser: &mut Tabnas, options: ExprOptions) -> Result<(), PluginError> {
parser.use_plugin(plugin_with(options), None).map(|_| ())
}
pub fn make() -> Tabnas {
make_with(ExprOptions::new())
}
pub fn make_with(options: ExprOptions) -> Tabnas {
let mut parser = tabnas_jsonic::make();
parser
.use_plugin(plugin_with(options), None)
.expect("the expression grammar installs on the jsonic base");
parser
}
static DEFAULT_PARSER: OnceLock<Tabnas> = OnceLock::new();
pub fn parse(src: &str) -> Result<Value, TabnasError> {
parse_with(DEFAULT_PARSER.get_or_init(make), src)
}
pub fn parse_with(parser: &Tabnas, src: &str) -> Result<Value, TabnasError> {
let _guard = ArenaGuard::enter();
let value = parser.parse(src)?;
Ok(realize(&value))
}
pub fn parse_scope<R>(
parser: &Tabnas,
src: &str,
use_value: impl FnOnce(Value) -> R,
) -> Result<R, TabnasError> {
let _guard = ArenaGuard::enter();
let value = parser.parse(src)?;
Ok(use_value(value))
}
pub fn parse_simplified(parser: &Tabnas, src: &str) -> Result<Value, TabnasError> {
let _guard = ArenaGuard::enter();
let value = parser.parse(src)?;
Ok(simplify(&value))
}