[[package]]
name = "prolog_parser"
-version = "0.7.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
+version = "0.8.0"
dependencies = [
"num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ordered-float 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
[[package]]
name = "rusty-wam"
-version = "0.7.17"
+version = "0.8.0"
dependencies = [
"downcast 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ordered-float 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "prolog_parser 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)",
+ "prolog_parser 0.8.0",
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
"checksum num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cacfcab5eb48250ee7d0c7896b51a2c5eec99c1feea5f32025635f5ae4b00070"
"checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe"
"checksum ordered-float 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "58d25b6c0e47b20d05226d288ff434940296e7e2f8b877975da32f862152241f"
-"checksum prolog_parser 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "73457bb9d32e6c60048f7cb10f41ed6f9d07053bccd188ff14f79e0af3046789"
"checksum redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "ab105df655884ede59d45b7070c8a65002d921461ee813a024558ca16030eea0"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
[package]
name = "rusty-wam"
-version = "0.7.17"
+version = "0.8.0"
repository = "https://github.com/mthom/rusty-wam"
description = "The Warren Abstract Machine in Rust."
downcast = "0.9.1"
num = "0.2"
ordered-float = "0.5.0"
-prolog_parser = "0.7.22"
+prolog_parser = { version = "0.8.0", path = "../prolog_parser" }
[dependencies.termion]
version = "1.4.0"
\ No newline at end of file
scan += 1;
},
- Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(_, _) => {
+ Addr::AttrVar(..) | Addr::HeapCell(_) | Addr::StackCell(_, _) => {
let ra = a;
let rd = self.store(self.deref(ra.clone()));
match rd.clone() {
- Addr::AttrVar(h) | Addr::HeapCell(h) if h >= old_h => {
+ Addr::AttrVar(h, _) | Addr::HeapCell(h) if h >= old_h => {
self[scan] = HeapCellValue::Addr(rd);
scan += 1;
},
da
},
- Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(_, _) => da,
+ Addr::HeapCell(_) | Addr::StackCell(_, _) => da,
+ Addr::AttrVar(h, _) => self.follow_heap(h + 1),
Addr::Str(s) => self.follow_heap(s) // record terms of structure.
}
}
fn offset_as_string(&self, addr: Addr) -> Option<String> {
match addr {
+ Addr::AttrVar(h, _) =>
+ Some(format!("_{}", h + 1)),
Addr::HeapCell(h) | Addr::Lis(h) | Addr::Str(h) =>
Some(format!("_{}", h)),
Addr::StackCell(fr, sc) =>
#[derive(Copy, Clone, PartialEq)]
pub enum SystemClauseType {
CheckCutPoint,
+ DeleteAttribute,
+ DeleteHeadAttribute,
DynamicModuleResolution,
ExpandGoal,
ExpandTerm,
+ GetAttributedVariableList,
GetBValue,
GetSCCCleaner,
InstallSCCCleaner,
pub fn name(&self) -> ClauseName {
match self {
&SystemClauseType::CheckCutPoint => clause_name!("$check_cp"),
+ &SystemClauseType::DeleteAttribute => clause_name!("$del_attr"),
+ &SystemClauseType::DeleteHeadAttribute => clause_name!("$del_attr_head"),
&SystemClauseType::DynamicModuleResolution => clause_name!("$module_call"),
&SystemClauseType::ExpandTerm => clause_name!("$expand_term"),
&SystemClauseType::ExpandGoal => clause_name!("$expand_goal"),
+ &SystemClauseType::GetAttributedVariableList => clause_name!("$get_attr_list"),
&SystemClauseType::GetBValue => clause_name!("$get_b_value"),
&SystemClauseType::GetDoubleQuotes => clause_name!("$get_double_quotes"),
&SystemClauseType::GetSCCCleaner => clause_name!("$get_scc_cleaner"),
pub fn from(name: &str, arity: usize) -> Option<SystemClauseType> {
match (name, arity) {
("$check_cp", 1) => Some(SystemClauseType::CheckCutPoint),
+ ("$del_attr", 1) => Some(SystemClauseType::DeleteAttribute),
+ ("$del_attr_head", 1) => Some(SystemClauseType::DeleteHeadAttribute),
("$module_call", 2) => Some(SystemClauseType::DynamicModuleResolution),
("$expand_term", 2) => Some(SystemClauseType::ExpandTerm),
("$expand_goal", 2) => Some(SystemClauseType::ExpandGoal),
+ ("$get_attr_list", 2) => Some(SystemClauseType::GetAttributedVariableList),
("$get_b_value", 1) => Some(SystemClauseType::GetBValue),
("$get_double_quotes", 1) => Some(SystemClauseType::GetDoubleQuotes),
("$get_scc_cleaner", 1) => Some(SystemClauseType::GetSCCCleaner),
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Addr {
- AttrVar(usize),
+ AttrVar(usize, usize), // the location of the AttrVal in the heap, the location of the attribute list.
Con(Constant),
Lis(usize),
HeapCell(usize),
impl Addr {
pub fn is_ref(&self) -> bool {
match self {
- &Addr::HeapCell(_) | &Addr::StackCell(_, _) => true,
+ &Addr::AttrVar(..) | &Addr::HeapCell(_) | &Addr::StackCell(_, _) => true,
_ => false
}
}
pub fn as_var(&self) -> Option<Ref> {
match self {
- &Addr::HeapCell(hc) => Some(Ref::HeapCell(hc)),
+ &Addr::AttrVar(h, _) => Some(Ref::HeapCell(h)),
+ &Addr::HeapCell(h) => Some(Ref::HeapCell(h)),
&Addr::StackCell(fr, sc) => Some(Ref::StackCell(fr, sc)),
_ => None
}
fn add(self, rhs: usize) -> Self::Output {
match self {
- Addr::AttrVar(a) => Addr::AttrVar(a + rhs),
Addr::Lis(a) => Addr::Lis(a + rhs),
Addr::HeapCell(h) => Addr::HeapCell(h + rhs),
Addr::Str(s) => Addr::Str(s + rhs),
fn sub(self, rhs: usize) -> Self::Output {
match self {
- Addr::AttrVar(a) => Addr::AttrVar(a - rhs),
Addr::Lis(a) => Addr::Lis(a - rhs),
Addr::HeapCell(h) => Addr::HeapCell(h - rhs),
Addr::Str(s) => Addr::Str(s - rhs),
--- /dev/null
+:- module(atts, [attribute/1, absent_attr/2, get_attr/2, put_attr/2,
+ absent_from_list/2, get_from_list/2, add_to_list/2,
+ del_attr/3, del_attr_step/2, del_attr_non_head/3]).
+
+:- use_module(library(control)).
+:- use_module(library(dcgs)).
+:- use_module(library(terms)).
+
+:- op(1199, fx, attribute).
+
+absent_attr(V, Attr) :-
+ '$get_attr_list'(V, Ls),
+ absent_from_list(Ls, Attr).
+
+absent_from_list(X, _) :-
+ var(X), !.
+absent_from_list([L|Ls], Attr) :-
+ ( L \= Attr -> absent_from_list(Ls, Attr) ).
+
+get_attr(V, Attr) :-
+ '$get_attr_list'(V, Ls), nonvar(Ls), get_from_list(Ls, Attr).
+
+get_from_list([L|Ls], Attr) :-
+ nonvar(L),
+ ( L \= Attr -> nonvar(Ls), get_from_list(Ls, Attr)
+ ; copy_term(L, L0), L0 = Attr
+ ; get_from_list(Ls, Attr)
+ ).
+
+put_attr(V, Attr) :-
+ '$get_attr_list'(V, Ls), add_to_list(Ls, Attr).
+
+add_to_list(Ls, Attr) :-
+ ( var(Ls) -> Ls = [Attr | _]
+ ; Ls = [_ | Ls0] -> add_to_list(Ls0, Attr)
+ ).
+
+del_attr(Ls0, _, _) :-
+ var(Ls0), !.
+del_attr(Ls0, V, Attr) :-
+ Ls0 = [Att | Ls1],
+ nonvar(Att),
+ ( Att \= Attr -> del_attr_non_head(Ls0, Ls1, Attr)
+ ; '$del_attr_head'(V), del_attr(Ls1, V, Attr)
+ ).
+
+del_attr_step(Ls1, Attr) :-
+ ( nonvar(Ls1) -> Ls1 = [_ | Ls2], del_attr_non_head(Ls1, Ls2, Attr)
+ ; true ).
+
+%% assumptions: Ls0 is a list, Ls1 is its tail;
+%% the head of Ls0 can be ignored.
+del_attr_non_head(Ls0, Ls1, Attr) :-
+ Ls0 = [_, Att | _],
+ nonvar(Att),
+ !,
+ ( Att \= Attr -> del_attr_step(Ls1, Attr)
+ ; '$del_attr'(Ls0), %% set tail of Ls0 = tail of Ls1. can be undone by backtracking.
+ del_attr_step(Ls1, Attr)
+ ).
+del_attr_non_head(_, _, _).
+
+user:term_expansion(Term0, Terms) :-
+ nonvar(Term0),
+ Term0 = (:- attribute Atts),
+ nonvar(Atts),
+ phrase(put_attrs_var_check, Terms, Terms1),
+ phrase(put_attrs(Atts), Terms1, Terms2),
+ phrase(get_attrs_var_check, Terms2, Terms3),
+ phrase(get_attrs(Atts), Terms3).
+
+put_attrs_var_check -->
+ { numbervars([Var, Attr], 0, _) },
+ [(put_atts(Var, Attr) :- nonvar(Var), throw(error(type_error(variable, Var), put_atts/2))),
+ (put_atts(Var, Attr) :- var(Attr), throw(error(instantiation_error, put_atts/2)))].
+
+get_attrs_var_check -->
+ { numbervars([Var, Attr], 0, _) },
+ [(get_atts(Var, Attr) :- nonvar(Var), throw(error(type_error(variable, Var), get_atts/2))),
+ (get_atts(Var, Attr) :- var(Attr), !, '$get_attr_list'(Var, Attr))].
+
+put_attrs(Name/Arity) -->
+ put_attr(Name, Arity),
+ { numbervars([Var, Attr], 0, _) },
+ [(put_atts(Var, Attr) :- lists:maplist(put_atts(Var), Attr))].
+put_attrs((Name/Arity, Atts)) -->
+ { nonvar(Atts) },
+ put_attr(Name, Arity),
+ put_attrs(Atts).
+
+get_attrs(Name/Arity) -->
+ get_attr(Name, Arity).
+get_attrs((Name/Arity, Atts)) -->
+ { nonvar(Atts) },
+ get_attr(Name, Arity),
+ get_attrs(Atts).
+
+put_attr(Name, Arity) -->
+ { functor(Attr, Name, Arity),
+ numbervars(Attr, 0, Arity),
+ V = '$VAR'(Arity) },
+ [(put_atts(V, +Attr) :- !, functor(Attr, _, _), put_attr(V, Attr)),
+ (put_atts(V, Attr) :- !, functor(Attr, _, _), put_attr(V, Attr)),
+ (put_atts(V, -Attr) :- !, functor(Attr, _, _), '$get_attr_list'(V, Ls), del_attr(Ls, V, Attr))].
+
+get_attr(Name, Arity) -->
+ { functor(Attr, Name, Arity),
+ numbervars(Attr, 0, Arity),
+ V = '$VAR'(Arity) },
+ [(get_atts(V, +Attr) :- !, functor(Attr, _, _), get_attr(V, Attr)),
+ (get_atts(V, Attr) :- !, functor(Attr, _, _), get_attr(V, Attr)),
+ (get_atts(V, -Attr) :- !, functor(Attr, _, _), absent_attr(V, Attr))].
+
+user:goal_expansion(Term, [M:put_atts(Var, Attr)]) :-
+ nonvar(Term),
+ Term = put_atts(Var, M, Attr).
+user:goal_expansion(Term, [M:get_atts(Var, Attr)]) :-
+ nonvar(Term),
+ Term = get_atts(Var, M, Attr).
println!("{} : {}", h, self.heap[h]);
}
}
-
+
#[inline]
pub fn machine_flags(&self) -> MachineFlags {
self.flags
pub(crate) fn store(&self, a: Addr) -> Addr {
match a {
- Addr::AttrVar(h) => self.heap[h+1].as_addr(h+1),
+ Addr::AttrVar(h, _) => self.heap[h].as_addr(h),
Addr::HeapCell(h) => self.heap[h].as_addr(h),
Addr::StackCell(fr, sc) => self.and_stack[fr][sc].clone(),
addr => addr
fn print_var_eq<Outputter>(&self, var: Rc<Var>, addr: Addr, var_dir: &HeapVarDict,
mut output: Outputter)
-> Outputter
- where Outputter: HCValueOutputter
+ where Outputter: HCValueOutputter
{
let orig_len = output.len();
pub(super)
fn print_exception<Outputter>(&self, addr: Addr, var_dir: &HeapVarDict, output: Outputter)
-> Outputter
- where Outputter: HCValueOutputter
+ where Outputter: HCValueOutputter
{
let printer = HCPrinter::from_heap_locs(&self, output, var_dir);
printer.print(addr)
if d1 != d2 {
match (self.store(d1.clone()), self.store(d2.clone())) {
- (Addr::AttrVar(h), addr) | (addr, Addr::AttrVar(h)) => {
+ (Addr::AttrVar(h, _), addr) | (addr, Addr::AttrVar(h, _)) => {
pdl.push(Addr::HeapCell(h+1));
pdl.push(addr);
},
self.fail = true;
},
(Addr::Lis(a1), Addr::Con(Constant::String(ref mut s)))
- | (Addr::Con(Constant::String(ref mut s)), Addr::Lis(a1))
+ | (Addr::Con(Constant::String(ref mut s)), Addr::Lis(a1))
if self.flags.double_quotes.is_chars() => {
if let Some(c) = s.head() {
pdl.push(Addr::Con(Constant::String(s.tail())));
self.fail = true;
},
(Addr::Con(Constant::EmptyList), Addr::Con(Constant::String(ref s)))
- | (Addr::Con(Constant::String(ref s)), Addr::Con(Constant::EmptyList))
+ | (Addr::Con(Constant::String(ref s)), Addr::Con(Constant::EmptyList))
if self.flags.double_quotes.is_chars() => {
if s.is_expandable() && s.is_empty() {
self.pstr_trail(s.clone());
(Addr::Con(Constant::String(ref mut s1)),
Addr::Con(Constant::String(ref mut s2))) =>
self.fail = !(self.unify_string(&mut pdl, s1, s2)
- || self.unify_string(&mut pdl, s2, s1)),
+ || self.unify_string(&mut pdl, s2, s1)),
(Addr::Con(ref c1), Addr::Con(ref c2)) =>
if c1 != c2 {
self.fail = true;
self.pstr_tr += 1;
}
- fn trail(&mut self, r: TrailRef) {
+ pub(super) fn trail(&mut self, r: TrailRef) {
match r {
TrailRef::HeapCell(h) =>
if h < self.hb {
}
pub(super) fn write_constant_to_var(&mut self, addr: Addr, c: Constant) {
- match self.store(self.deref(addr)) {
+ match self.store(self.deref(addr)) {
Addr::HeapCell(h) => {
self.heap[h] = HeapCellValue::Addr(Addr::Con(c.clone()));
self.trail(TrailRef::HeapCell(h));
let addr = self.store(self.deref(a1));
let offset = match addr {
- Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(_, _) => v,
+ Addr::HeapCell(_) | Addr::StackCell(..) | Addr::AttrVar(..) => v,
Addr::Con(Constant::String(_)) if self.flags.double_quotes.is_chars() => l,
Addr::Con(_) => c,
Addr::Lis(_) => l,
- Addr::Str(_) => s
+ Addr::Str(_) => s,
};
match offset {
match d {
Addr::HeapCell(_) | Addr::StackCell(..) => self.fail = true,
+ Addr::AttrVar(h, _) => {
+ let addr = self.heap[h].as_addr(h);
+
+ if let Some(_) = addr.as_var() {
+ if self.store(self.deref(addr)).as_var().is_some() {
+ self.fail = true;
+ }
+ }
+
+ self.p += 1;
+ },
_ => self.p += 1
};
},
match d {
Addr::HeapCell(_) | Addr::StackCell(_,_) => self.p += 1,
+ Addr::AttrVar(h, _) => {
+ let addr = self.heap[h].as_addr(h);
+
+ if let Some(_) = addr.as_var() {
+ if self.store(self.deref(addr)).as_var().is_none() {
+ self.fail = true;
+ }
+ }
+
+ self.p += 1;
+ },
_ => self.fail = true
};
},
},
Addr::Lis(_) =>
self.try_functor_compound_case(clause_name!("."), 2),
- Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(..) => {
+ Addr::AttrVar(..) | Addr::HeapCell(_) | Addr::StackCell(..) => {
let name = self.store(self.deref(self[temp_v!(2)].clone()));
let arity = self.store(self.deref(self[temp_v!(3)].clone()));
_ => self.fail = true
};
},
+ &SystemClauseType::DeleteAttribute => {
+ let ls0 = self.store(self.deref(self[temp_v!(1)].clone()));
+
+ if let Addr::Lis(l1) = ls0 {
+ if let Addr::Lis(l2) = self.store(self.deref(Addr::HeapCell(l1 + 1))) {
+ let addr = self.heap[l1 + 1].as_addr(l1 + 1);
+ self.heap[l1 + 1] = HeapCellValue::Addr(Addr::HeapCell(l2 + 1));
+ self.trail(TrailRef::AttrVarLink(l2 + 1, addr));
+ }
+ }
+ },
+ &SystemClauseType::DeleteHeadAttribute => {
+ let addr = self.store(self.deref(self[temp_v!(1)].clone()));
+
+ match addr {
+ Addr::AttrVar(_, attr_var) => {
+ let addr = self.heap[attr_var].as_addr(attr_var).clone();
+ let addr = self.store(self.deref(addr));
+
+ match addr {
+ Addr::Lis(l) => {
+ self.heap[attr_var] = HeapCellValue::Addr(Addr::HeapCell(l+1));
+ self.trail(TrailRef::AttrVarLink(attr_var, Addr::Lis(l)));
+ },
+ _ => {}
+ }
+ },
+ _ => {}
+ }
+ },
&SystemClauseType::DynamicModuleResolution => {
let module_name = self.store(self.deref(self[temp_v!(1)].clone()));
for i in 1 .. arity + 1 {
self.registers[i] = self.heap[a+i].as_addr(a+i);
}
-
+
return self.module_lookup(indices, (name, arity), module_name, true);
},
Addr::Con(Constant::Atom(name, _)) =>
return self.module_lookup(indices, (name, 0), module_name, true),
addr => {
let stub = MachineError::functor_stub(clause_name!("(:)"), 2);
-
+
let type_error = MachineError::type_error(ValidType::Callable, addr);
let type_error = self.error_form(type_error, stub);
self.p = CodePtr::Local(LocalCodePtr::UserTermExpansion(0));
return Ok(());
},
+ &SystemClauseType::GetAttributedVariableList => {
+ let attr_var = self.store(self.deref(self[temp_v!(1)].clone()));
+ let mut attr_var_list = match attr_var {
+ Addr::AttrVar(_, attr_var_list) => attr_var_list,
+ attr_var @ Addr::HeapCell(_) | attr_var @ Addr::StackCell(..) => {
+ // create an AttrVar in the heap.
+ let h = self.heap.h;
+
+ self.heap.push(HeapCellValue::Addr(Addr::AttrVar(h, h + 2)));
+ self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h + 1)));
+ self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h + 2)));
+
+ match attr_var.as_var().unwrap() {
+ Ref::HeapCell(r) => {
+ self.heap[r] = HeapCellValue::Addr(Addr::HeapCell(h));
+ self.trail(TrailRef::HeapCell(r));
+ },
+ Ref::StackCell(fr, sc) => {
+ self.and_stack[fr][sc] = Addr::HeapCell(h);
+ self.trail(TrailRef::StackCell(fr, sc));
+ }
+ }
+
+ h + 2
+ },
+ _ => {
+ self.fail = true;
+ return Ok(());
+ }
+ };
+
+ let list_var = self[temp_v!(2)].clone();
+ self.unify(Addr::HeapCell(attr_var_list), list_var);
+ },
&SystemClauseType::GetDoubleQuotes => {
let a1 = self[temp_v!(1)].clone();
fn parse_expansion_output(&mut self, term_string: &str, op_dir: &OpDir)
-> Result<Term, ParserError>
{
- let mut parser = Parser::new(term_string.trim().as_bytes(), self.indices.atom_tbl.clone(),
+ let mut parser = Parser::new(term_string.trim().as_bytes(), self.parser.get_atom_tbl(),
self.flags);
parser.read_term(composite_op!(self.in_module, &self.indices.op_dir, op_dir))
}
}
impl MachineState {
+ fn print_with_locs(&self, target: usize, var_dict: &HeapVarDict) -> PrinterOutputter {
+ let output = PrinterOutputter::new();
+ let mut printer = HCPrinter::from_heap_locs(&self, output, &var_dict);
+
+ printer.quoted = true;
+ printer.numbervars = true;
+
+ printer.see_all_locs();
+
+ printer.print(Addr::HeapCell(target))
+ }
+
fn try_expand_term(&mut self, indices: &mut IndexStore, policies: &mut MachinePolicies,
code_repo: &mut CodeRepo, term: &Term, hook: CompileTimeHook)
-> Option<String>
self.reset();
None
} else {
- let mut output = {
- let output = PrinterOutputter::new();
- let mut printer = HCPrinter::from_heap_locs(&self, output, &var_dict);
-
- printer.quoted = true;
- printer.numbervars = true;
-
- printer.see_all_locs();
-
- printer.print(Addr::HeapCell(h))
- };
+ let mut output = self.print_with_locs(h, &var_dict);
output.push_char('.');
self.reset();
Term(Term)
}
-pub fn read_toplevel(wam: &mut Machine) -> Result<Input, ParserError> {
+pub fn read_toplevel(wam: &mut Machine) -> Result<Input, ParserError> {
let mut buffer = String::new();
let stdin = stdin();
let mut term_stream = TermStream::new(stdin.lock(), wam.indices.atom_tbl.clone(),
wam.machine_flags(), &mut wam.indices,
&mut wam.policies, &mut wam.code_repo);
-
+
term_stream.add_to_top(buffer.as_str());
Ok(Input::Term(term_stream.read_term(&mut wam.machine_st, &OpDir::new())?))
}
match self {
&Addr::Con(ref c) => write!(f, "Addr::Con({})", c),
&Addr::Lis(l) => write!(f, "Addr::Lis({})", l),
- &Addr::AttrVar(h) => write!(f, "Addr::AttrVar({})", h),
+ &Addr::AttrVar(h, attr_var_list) => write!(f, "Addr::AttrVar({}, {})", h, attr_var_list),
&Addr::HeapCell(h) => write!(f, "Addr::HeapCell({})", h),
&Addr::StackCell(fr, sc)=> write!(f, "Addr::StackCell({}, {})", fr, sc),
&Addr::Str(s) => write!(f, "Addr::Str({})", s)