Skip to content

Commit bae6083

Browse files
committed
Minor cleanups for the resolver.
1 parent d37df07 commit bae6083

File tree

2 files changed

+55
-84
lines changed

2 files changed

+55
-84
lines changed

fluent-bundle/src/bundle.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use intl_pluralrules::{IntlPluralRules, PluralRuleType};
1515
use crate::entry::Entry;
1616
use crate::entry::GetEntry;
1717
use crate::errors::FluentError;
18-
use crate::resolve::{resolve_value_for_entry, Scope};
18+
use crate::resolve::{ResolveValue, Scope};
1919
use crate::resource::FluentResource;
2020
use crate::types::FluentValue;
2121

@@ -392,14 +392,14 @@ impl<R> FluentBundle<R> {
392392
.attributes
393393
.iter()
394394
.find(|attr| attr.id.name == attr_name)?;
395-
resolve_value_for_entry(&attr.value, (message, attr).into(), &mut scope).to_string()
395+
attr.value.resolve(&mut scope).to_string()
396396
} else {
397397
let message_id = path;
398398
let message = self.get_message(message_id)?;
399399
message
400400
.value
401401
.as_ref()
402-
.map(|value| resolve_value_for_entry(value, message.into(), &mut scope))?
402+
.map(|value| value.resolve(&mut scope))?
403403
.to_string()
404404
};
405405

@@ -467,7 +467,7 @@ impl<R> FluentBundle<R> {
467467
let value = message
468468
.value
469469
.as_ref()
470-
.map(|value| resolve_value_for_entry(value, message.into(), &mut scope).to_string());
470+
.map(|value| value.resolve(&mut scope).to_string());
471471

472472
// Setting capacity helps performance for cases with attributes,
473473
// but is slower than `::new` for cases without.
@@ -479,7 +479,7 @@ impl<R> FluentBundle<R> {
479479
};
480480

481481
for attr in message.attributes.iter() {
482-
let val = resolve_value_for_entry(&attr.value, (message, attr).into(), &mut scope);
482+
let val = attr.value.resolve(&mut scope);
483483
attributes.insert(attr.id.name, val.to_string());
484484
}
485485

fluent-bundle/src/resolve.rs

Lines changed: 50 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,8 @@
88
99
use std::borrow::Borrow;
1010
use std::cell::RefCell;
11-
use std::collections::hash_map::DefaultHasher;
1211
use std::collections::HashMap;
1312
use std::fmt::Write;
14-
use std::hash::{Hash, Hasher};
1513

1614
use fluent_syntax::ast;
1715
use fluent_syntax::unicode::unescape_unicode;
@@ -40,7 +38,7 @@ pub struct Scope<'bundle, R: Borrow<FluentResource>> {
4038
/// Local args
4139
pub local_args: Option<HashMap<&'bundle str, FluentValue<'bundle>>>,
4240
/// Tracks hashes to prevent infinite recursion.
43-
pub travelled: RefCell<smallvec::SmallVec<[u64; 2]>>,
41+
pub travelled: RefCell<smallvec::SmallVec<[&'bundle ast::Pattern<'bundle>; 2]>>,
4442
/// Track errors accumulated during resolving.
4543
pub errors: Vec<ResolverError>,
4644
}
@@ -59,23 +57,46 @@ impl<'bundle, R: Borrow<FluentResource>> Scope<'bundle, R> {
5957
}
6058
}
6159

62-
pub fn track<F>(
60+
// This method allows us to lazily add Pattern on the stack,
61+
// only if the Pattern::resolve has been called on an empty stack.
62+
//
63+
// This is the case when pattern is called from Bundle and it
64+
// allows us to fast-path simple resolutions, and only use the stack
65+
// for placeables.
66+
pub fn maybe_track<F>(
6367
&mut self,
64-
entry: DisplayableNode<'bundle>,
68+
pattern: &'bundle ast::Pattern,
69+
entry: Option<DisplayableNode<'bundle>>,
6570
mut action: F,
6671
) -> FluentValue<'bundle>
6772
where
6873
F: FnMut(&mut Scope<'bundle, R>) -> FluentValue<'bundle>,
6974
{
70-
let mut hasher = DefaultHasher::new();
71-
entry.hash(&mut hasher);
72-
let hash = hasher.finish();
75+
if self.travelled.borrow().is_empty() {
76+
self.track(pattern, entry, action)
77+
} else {
78+
action(self)
79+
}
80+
}
7381

74-
if self.travelled.borrow().contains(&hash) {
82+
pub fn track<F>(
83+
&mut self,
84+
pattern: &'bundle ast::Pattern,
85+
entry: Option<DisplayableNode<'bundle>>,
86+
mut action: F,
87+
) -> FluentValue<'bundle>
88+
where
89+
F: FnMut(&mut Scope<'bundle, R>) -> FluentValue<'bundle>,
90+
{
91+
if self.travelled.borrow().contains(&pattern) {
7592
self.errors.push(ResolverError::Cyclic);
76-
FluentValue::Error(entry)
93+
if let Some(entry) = entry {
94+
FluentValue::Error(entry)
95+
} else {
96+
FluentValue::None()
97+
}
7798
} else {
78-
self.travelled.borrow_mut().push(hash);
99+
self.travelled.borrow_mut().push(pattern);
79100
let result = action(self);
80101
self.travelled.borrow_mut().pop();
81102
result
@@ -84,64 +105,17 @@ impl<'bundle, R: Borrow<FluentResource>> Scope<'bundle, R> {
84105

85106
fn maybe_resolve_attribute(
86107
&mut self,
87-
attributes: &[ast::Attribute<'bundle>],
108+
attributes: &'bundle [ast::Attribute<'bundle>],
88109
entry: DisplayableNode<'bundle>,
89110
name: &str,
90111
) -> Option<FluentValue<'bundle>> {
91112
attributes
92113
.iter()
93114
.find(|attr| attr.id.name == name)
94-
.map(|attr| self.track(entry, |scope| attr.value.resolve(scope)))
115+
.map(|attr| self.track(&attr.value, Some(entry), |scope| attr.value.resolve(scope)))
95116
}
96117
}
97118

98-
pub fn resolve_value_for_entry<'source, R>(
99-
value: &ast::Pattern<'source>,
100-
entry: DisplayableNode<'source>,
101-
scope: &mut Scope<'source, R>,
102-
) -> FluentValue<'source>
103-
where
104-
R: Borrow<FluentResource>,
105-
{
106-
if value.elements.len() == 1 {
107-
return match value.elements[0] {
108-
ast::PatternElement::TextElement(s) => FluentValue::String(s.into()),
109-
ast::PatternElement::Placeable(ref p) => scope.track(entry, |scope| p.resolve(scope)),
110-
};
111-
}
112-
113-
let mut string = String::new();
114-
for elem in &value.elements {
115-
match elem {
116-
ast::PatternElement::TextElement(s) => {
117-
string.push_str(&s);
118-
}
119-
ast::PatternElement::Placeable(p) => {
120-
let result = scope.track(entry, |scope| p.resolve(scope));
121-
let needs_isolation = scope.bundle.use_isolating
122-
&& match p {
123-
ast::Expression::InlineExpression(
124-
ast::InlineExpression::MessageReference { .. },
125-
)
126-
| ast::Expression::InlineExpression(
127-
ast::InlineExpression::TermReference { .. },
128-
)
129-
| ast::Expression::InlineExpression(
130-
ast::InlineExpression::StringLiteral { .. },
131-
) => false,
132-
_ => true,
133-
};
134-
if needs_isolation {
135-
write!(string, "\u{2068}{}\u{2069}", result).expect("Writing succeeded");
136-
} else {
137-
write!(string, "{}", result).expect("Writing succeeded");
138-
};
139-
}
140-
}
141-
}
142-
FluentValue::String(string.into())
143-
}
144-
145119
fn generate_ref_error<'source, R>(
146120
scope: &mut Scope<'source, R>,
147121
node: DisplayableNode<'source>,
@@ -157,29 +131,22 @@ where
157131

158132
// Converts an AST node to a `FluentValue`.
159133
pub trait ResolveValue<'source> {
160-
fn resolve<R>(&self, scope: &mut Scope<'source, R>) -> FluentValue<'source>
134+
fn resolve<R>(&'source self, scope: &mut Scope<'source, R>) -> FluentValue<'source>
161135
where
162136
R: Borrow<FluentResource>;
163137
}
164138

165-
impl<'source> ResolveValue<'source> for ast::Term<'source> {
166-
fn resolve<R>(&self, scope: &mut Scope<'source, R>) -> FluentValue<'source>
167-
where
168-
R: Borrow<FluentResource>,
169-
{
170-
resolve_value_for_entry(&self.value, self.into(), scope)
171-
}
172-
}
173-
174139
impl<'source> ResolveValue<'source> for ast::Pattern<'source> {
175-
fn resolve<R>(&self, scope: &mut Scope<'source, R>) -> FluentValue<'source>
140+
fn resolve<R>(&'source self, scope: &mut Scope<'source, R>) -> FluentValue<'source>
176141
where
177142
R: Borrow<FluentResource>,
178143
{
179144
if self.elements.len() == 1 {
180145
return match self.elements[0] {
181-
ast::PatternElement::TextElement(s) => FluentValue::String(s.into()),
182-
ast::PatternElement::Placeable(ref p) => p.resolve(scope),
146+
ast::PatternElement::TextElement(s) => s.into(),
147+
ast::PatternElement::Placeable(ref p) => {
148+
scope.maybe_track(self, None, |scope| p.resolve(scope))
149+
}
183150
};
184151
}
185152

@@ -190,7 +157,9 @@ impl<'source> ResolveValue<'source> for ast::Pattern<'source> {
190157
string.push_str(&s);
191158
}
192159
ast::PatternElement::Placeable(p) => {
193-
let result = p.resolve(scope).to_string();
160+
let result = scope
161+
.maybe_track(self, None, |scope| p.resolve(scope))
162+
.to_string();
194163
let needs_isolation = scope.bundle.use_isolating
195164
&& match p {
196165
ast::Expression::InlineExpression(
@@ -217,7 +186,7 @@ impl<'source> ResolveValue<'source> for ast::Pattern<'source> {
217186
}
218187

219188
impl<'source> ResolveValue<'source> for ast::Expression<'source> {
220-
fn resolve<R>(&self, scope: &mut Scope<'source, R>) -> FluentValue<'source>
189+
fn resolve<R>(&'source self, scope: &mut Scope<'source, R>) -> FluentValue<'source>
221190
where
222191
R: Borrow<FluentResource>,
223192
{
@@ -263,7 +232,7 @@ impl<'source> ResolveValue<'source> for ast::Expression<'source> {
263232
}
264233

265234
impl<'source> ResolveValue<'source> for ast::InlineExpression<'source> {
266-
fn resolve<R>(&self, mut scope: &mut Scope<'source, R>) -> FluentValue<'source>
235+
fn resolve<R>(&'source self, mut scope: &mut Scope<'source, R>) -> FluentValue<'source>
267236
where
268237
R: Borrow<FluentResource>,
269238
{
@@ -280,7 +249,7 @@ impl<'source> ResolveValue<'source> for ast::InlineExpression<'source> {
280249
.maybe_resolve_attribute(&msg.attributes, self.into(), attr.name)
281250
.unwrap_or_else(|| generate_ref_error(scope, self.into()))
282251
} else if let Some(value) = msg.value.as_ref() {
283-
scope.track(self.into(), |scope| value.resolve(scope))
252+
scope.track(value, Some(msg.into()), |scope| value.resolve(scope))
284253
} else {
285254
generate_ref_error(scope, self.into())
286255
}
@@ -306,7 +275,9 @@ impl<'source> ResolveValue<'source> for ast::InlineExpression<'source> {
306275
.maybe_resolve_attribute(&term.attributes, self.into(), attr.name)
307276
.unwrap_or_else(|| generate_ref_error(scope, self.into()))
308277
} else {
309-
term.resolve(&mut scope)
278+
scope.track(&term.value, Some(term.into()), |scope| {
279+
term.value.resolve(scope)
280+
})
310281
}
311282
} else {
312283
generate_ref_error(scope, self.into())
@@ -351,7 +322,7 @@ impl<'source> ResolveValue<'source> for ast::InlineExpression<'source> {
351322

352323
fn get_arguments<'bundle, R>(
353324
scope: &mut Scope<'bundle, R>,
354-
arguments: &Option<ast::CallArguments<'bundle>>,
325+
arguments: &'bundle Option<ast::CallArguments<'bundle>>,
355326
) -> (
356327
Vec<FluentValue<'bundle>>,
357328
HashMap<&'bundle str, FluentValue<'bundle>>,

0 commit comments

Comments
 (0)