1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! 字符串字面量 —— 对应 Java `freemarker.core.StringLiteral`
//! (`_eval` :88-106;含 `${}` 插值的 InterpStr 分支见 `eval_interp_str`)
use crate::core::environment::model_to_string;
use crate::core::eval::eval;
use crate::core::{OutputFormatKind, StrPart};
use crate::error::{Result, TemplateError};
use crate::template::TModel;
/// 字符串字面量(对应 StringLiteral.java;解析器经 `ExprKind::Str` 承载)
pub struct StringLiteral {
pub value: String,
}
impl StringLiteral {
/// 构造(Java 构造器;Rust 侧由解析器产生)
pub fn new(value: String) -> Self {
StringLiteral { value }
}
/// 求值(Java `_eval` 纯文本分支)
pub(crate) fn eval(&self, _env: &mut crate::core::Environment) -> Result<TModel> {
Ok(TModel::from_scalar(self.value.clone()))
}
}
/// 插值字符串求值 —— 对应 Java `StringLiteral._eval` 插值分支
/// (StringLiteral.java:96-126 → EvalUtil.concatMarkupOutputs):
/// 任一插值部分是 markup 输出 → 整串**提升为 markup**("an interpolated expression
/// that returns markup promotes the result of the whole expression to markup")——
/// markup 部分原样并入,字符串部分按 **markup 结果的格式**转义后并入
/// (markupResult.getOutputFormat().fromPlainTextByEscaping,Java :100-107);
/// 源纯文本槽在全部段可逆时保留(跨格式转换依赖,见 ?esc 语义),否则 None。
/// 全部为纯文本 → 普通字符串拼接。
pub(crate) fn eval_interp_str(
env: &mut crate::core::Environment,
parts: &[StrPart],
) -> Result<TModel> {
// Java plainTextResult / markupResult 双轨:markup 未出现时累积纯文本,
// 首见 markup 后转为 markup 拼接(含纯文本槽合并,CommonMarkupOutputFormat.concat)
let mut plain: Option<String> = Some(String::new());
let mut markup: Option<String> = None;
let mut fmt: Option<OutputFormatKind> = None;
for part in parts {
match part {
StrPart::Text(t) => {
if let Some(cur) = &mut markup {
let f = fmt.unwrap();
cur.push_str(
&crate::core::built_ins_for_markup_outputs::escape_plain_text(f, t),
);
if let Some(p) = &mut plain {
p.push_str(t);
}
} else if let Some(p) = &mut plain {
p.push_str(t);
}
}
StrPart::Interp(e) => {
let m = eval(env, e)?;
if m.is_nothing() {
// Java EvalUtil.coerceModelToTextualCommon:tm == null 时 classic 兼容
// 模式回退空串(EvalUtil.java:486-489),否则 InvalidReferenceException。
if env.settings.classic_compatible {
continue;
}
return Err(TemplateError::invalid_reference(
crate::core::environment::expr_desc(e),
));
}
if m.is_markup_output() {
let mfmt = m.markup_format.unwrap_or(env.settings.output_format);
if let Some(cur) = &mut markup {
// 后续 markup 段:与既有结果拼接(EvalUtil.concatMarkupOutputs
// :573-597 —— 跨格式任一侧有源纯文本则可转成对方格式,
// 均无 → 报错)
let cur_fmt = fmt.unwrap();
if cur_fmt == mfmt {
cur.push_str(&m.get_scalar()?);
} else if let Some(rp) = &m.markup_plain {
cur.push_str(
&crate::core::built_ins_for_markup_outputs::escape_plain_text(
cur_fmt, rp,
),
);
} else if let Some(lp) = &plain {
// 左侧纯文本可转义 → 整体按右侧格式重建
// (rightOF.concat(rightOF.fromPlainTextByEscaping(leftPT), rightMO))
*cur = format!(
"{}{}",
crate::core::built_ins_for_markup_outputs::escape_plain_text(
mfmt, lp
),
m.get_scalar()?
);
fmt = Some(mfmt);
} else {
return Err(TemplateError::misc(format!(
"Concatenation left hand operand is in {} format, while the \
right hand operand is in {}. Conversion to common format \
wasn't possible.",
cur_fmt.name(),
mfmt.name()
)));
}
// 纯文本槽:任一段无源纯文本 → 整体不可逆(Java concat 的
// pc3 = pc1 != null && pc2 != null ? ... : null)
match &m.markup_plain {
Some(mp) => {
if let Some(p) = &mut plain {
p.push_str(mp);
}
}
None => plain = None,
}
} else {
// 首见 markup(Java :112-116):此前累积的纯文本按该格式
// 转义并入(fromPlainTextByEscaping(plainTextResult))
let p = plain.take().unwrap_or_default();
fmt = Some(mfmt);
let mut s = String::new();
if !p.is_empty() {
s.push_str(
&crate::core::built_ins_for_markup_outputs::escape_plain_text(
mfmt, &p,
),
);
}
s.push_str(&m.get_scalar()?);
markup = Some(s);
plain = m.markup_plain.as_ref().map(|mp| format!("{p}{mp}"));
}
} else if let Some(cur) = &mut markup {
// markup 结果后的纯文本段:按 markup 格式转义(Java :101-104)
let s = model_to_string(env, &m)?;
cur.push_str(
&crate::core::built_ins_for_markup_outputs::escape_plain_text(
fmt.unwrap(),
&s,
),
);
if let Some(p) = &mut plain {
p.push_str(&s);
}
} else if let Some(p) = &mut plain {
p.push_str(&model_to_string(env, &m)?);
}
}
}
}
match markup {
Some(m) => Ok(
crate::core::built_ins_for_markup_outputs::markup_model_with(m, plain, fmt.unwrap()),
),
None => Ok(TModel::from_scalar(plain.unwrap_or_default())),
}
}