8
8
9
9
use std:: borrow:: Borrow ;
10
10
use std:: cell:: RefCell ;
11
- use std:: collections:: hash_map:: DefaultHasher ;
12
11
use std:: collections:: HashMap ;
13
12
use std:: fmt:: Write ;
14
- use std:: hash:: { Hash , Hasher } ;
15
13
16
14
use fluent_syntax:: ast;
17
15
use fluent_syntax:: unicode:: unescape_unicode;
@@ -40,7 +38,7 @@ pub struct Scope<'bundle, R: Borrow<FluentResource>> {
40
38
/// Local args
41
39
pub local_args : Option < HashMap < & ' bundle str , FluentValue < ' bundle > > > ,
42
40
/// 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 ] > > ,
44
42
/// Track errors accumulated during resolving.
45
43
pub errors : Vec < ResolverError > ,
46
44
}
@@ -59,23 +57,46 @@ impl<'bundle, R: Borrow<FluentResource>> Scope<'bundle, R> {
59
57
}
60
58
}
61
59
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 > (
63
67
& mut self ,
64
- entry : DisplayableNode < ' bundle > ,
68
+ pattern : & ' bundle ast:: Pattern ,
69
+ entry : Option < DisplayableNode < ' bundle > > ,
65
70
mut action : F ,
66
71
) -> FluentValue < ' bundle >
67
72
where
68
73
F : FnMut ( & mut Scope < ' bundle , R > ) -> FluentValue < ' bundle > ,
69
74
{
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
+ }
73
81
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) {
75
92
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
+ }
77
98
} else {
78
- self . travelled . borrow_mut ( ) . push ( hash ) ;
99
+ self . travelled . borrow_mut ( ) . push ( pattern ) ;
79
100
let result = action ( self ) ;
80
101
self . travelled . borrow_mut ( ) . pop ( ) ;
81
102
result
@@ -84,64 +105,17 @@ impl<'bundle, R: Borrow<FluentResource>> Scope<'bundle, R> {
84
105
85
106
fn maybe_resolve_attribute (
86
107
& mut self ,
87
- attributes : & [ ast:: Attribute < ' bundle > ] ,
108
+ attributes : & ' bundle [ ast:: Attribute < ' bundle > ] ,
88
109
entry : DisplayableNode < ' bundle > ,
89
110
name : & str ,
90
111
) -> Option < FluentValue < ' bundle > > {
91
112
attributes
92
113
. iter ( )
93
114
. 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) ) )
95
116
}
96
117
}
97
118
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
-
145
119
fn generate_ref_error < ' source , R > (
146
120
scope : & mut Scope < ' source , R > ,
147
121
node : DisplayableNode < ' source > ,
@@ -157,29 +131,22 @@ where
157
131
158
132
// Converts an AST node to a `FluentValue`.
159
133
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 >
161
135
where
162
136
R : Borrow < FluentResource > ;
163
137
}
164
138
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
-
174
139
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 >
176
141
where
177
142
R : Borrow < FluentResource > ,
178
143
{
179
144
if self . elements . len ( ) == 1 {
180
145
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
+ }
183
150
} ;
184
151
}
185
152
@@ -190,7 +157,9 @@ impl<'source> ResolveValue<'source> for ast::Pattern<'source> {
190
157
string. push_str ( & s) ;
191
158
}
192
159
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 ( ) ;
194
163
let needs_isolation = scope. bundle . use_isolating
195
164
&& match p {
196
165
ast:: Expression :: InlineExpression (
@@ -217,7 +186,7 @@ impl<'source> ResolveValue<'source> for ast::Pattern<'source> {
217
186
}
218
187
219
188
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 >
221
190
where
222
191
R : Borrow < FluentResource > ,
223
192
{
@@ -263,7 +232,7 @@ impl<'source> ResolveValue<'source> for ast::Expression<'source> {
263
232
}
264
233
265
234
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 >
267
236
where
268
237
R : Borrow < FluentResource > ,
269
238
{
@@ -280,7 +249,7 @@ impl<'source> ResolveValue<'source> for ast::InlineExpression<'source> {
280
249
. maybe_resolve_attribute ( & msg. attributes , self . into ( ) , attr. name )
281
250
. unwrap_or_else ( || generate_ref_error ( scope, self . into ( ) ) )
282
251
} 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) )
284
253
} else {
285
254
generate_ref_error ( scope, self . into ( ) )
286
255
}
@@ -306,7 +275,9 @@ impl<'source> ResolveValue<'source> for ast::InlineExpression<'source> {
306
275
. maybe_resolve_attribute ( & term. attributes , self . into ( ) , attr. name )
307
276
. unwrap_or_else ( || generate_ref_error ( scope, self . into ( ) ) )
308
277
} else {
309
- term. resolve ( & mut scope)
278
+ scope. track ( & term. value , Some ( term. into ( ) ) , |scope| {
279
+ term. value . resolve ( scope)
280
+ } )
310
281
}
311
282
} else {
312
283
generate_ref_error ( scope, self . into ( ) )
@@ -351,7 +322,7 @@ impl<'source> ResolveValue<'source> for ast::InlineExpression<'source> {
351
322
352
323
fn get_arguments < ' bundle , R > (
353
324
scope : & mut Scope < ' bundle , R > ,
354
- arguments : & Option < ast:: CallArguments < ' bundle > > ,
325
+ arguments : & ' bundle Option < ast:: CallArguments < ' bundle > > ,
355
326
) -> (
356
327
Vec < FluentValue < ' bundle > > ,
357
328
HashMap < & ' bundle str , FluentValue < ' bundle > > ,
0 commit comments