use syntax::ast::Expression;
use syntax::types::peel_to_range_type;
use crate::Emitter;
use crate::expressions::context::ExpressionContext;
use crate::expressions::emission::EmittedExpression;
use crate::types::native::NativeGoType;
impl Emitter<'_> {
pub(crate) fn emit_index_access(
&mut self,
output: &mut String,
expression: &Expression,
index: &Expression,
) -> String {
if let Expression::Range {
start,
end,
inclusive,
..
} = index
{
return self.emit_range_slice(
output,
expression,
start.as_deref(),
end.as_deref(),
*inclusive,
);
}
let base_staged = self.stage_base_with_deref(expression);
let index_ty = index.get_type();
if let Some(range_kind) = peel_to_range_type(&index_ty).and_then(|t| t.get_name()) {
let needs_cap = self.is_native_shape(&expression.get_type(), NativeGoType::Slice);
let index_staged = self.stage_or_capture(index, "range");
let values = self.sequence(output, vec![base_staged, index_staged], "_base");
return self.emit_range_var_slice(&values[0], &values[1], range_kind, needs_cap);
}
let index_staged = self.stage_composite(index, ExpressionContext::value());
let values = self.sequence(output, vec![base_staged, index_staged], "_base");
format!("{}[{}]", values[0], values[1])
}
fn stage_base_with_deref(&mut self, expression: &Expression) -> EmittedExpression {
let Some(inner) = expression.deref_inner() else {
return self.stage_operand(expression, ExpressionContext::value());
};
let s = self.stage_operand(inner, ExpressionContext::value());
EmittedExpression {
value: format!("(*{})", s.value),
setup: s.setup,
capture: s.capture,
}
}
fn emit_range_slice(
&mut self,
output: &mut String,
expression: &Expression,
start: Option<&Expression>,
end: Option<&Expression>,
inclusive: bool,
) -> String {
let needs_cap = self.is_native_shape(&expression.get_type(), NativeGoType::Slice);
let base_staged = self.stage_base_with_deref(expression);
let mut all_stages = vec![base_staged];
if let Some(s) = start {
all_stages.push(self.stage_operand(s, ExpressionContext::value()));
}
if let Some(e) = end {
all_stages.push(self.stage_operand(e, ExpressionContext::value()));
}
let values = self.sequence(output, all_stages, "_base");
let base_str = &values[0];
let (start_str, end_expression) = if start.is_some() {
(values[1].as_str(), values.get(2).map(|s| s.as_str()))
} else {
("", values.get(1).map(|s| s.as_str()))
};
let end_str = match (end_expression, inclusive) {
(None, _) => String::new(),
(Some(e), false) => e.to_string(),
(Some(e), true) => format!("{}+1", e),
};
if !needs_cap {
return format!("{}[{}:{}]", base_str, start_str, end_str);
}
if end_str.is_empty() {
let len_var = self.hoist_tmp_value(output, "len", &format!("len({})", base_str));
return format!("{}[{}:{}:{}]", base_str, start_str, len_var, len_var);
}
if end_str.contains('(') {
let end_var = self.hoist_tmp_value(output, "end", &end_str);
return format!("{}[{}:{}:{}]", base_str, start_str, end_var, end_var);
}
format!("{}[{}:{}:{}]", base_str, start_str, end_str, end_str)
}
fn emit_range_var_slice(
&self,
base: &str,
range: &str,
range_kind: &str,
needs_cap: bool,
) -> String {
let (start, end) = range_var_bounds(range, range_kind);
let start_str = start.as_deref().unwrap_or("");
let end_str = end.as_deref().unwrap_or("");
if !needs_cap {
return format!("{}[{}:{}]", base, start_str, end_str);
}
let cap = if end_str.is_empty() {
format!("len({})", base)
} else {
end_str.to_string()
};
format!("{}[{}:{}:{}]", base, start_str, end_str, cap)
}
}
pub(crate) fn range_var_bounds(
range_var: &str,
range_kind: &str,
) -> (Option<String>, Option<String>) {
match range_kind {
"Range" => (
Some(format!("{}.Start", range_var)),
Some(format!("{}.End", range_var)),
),
"RangeInclusive" => (
Some(format!("{}.Start", range_var)),
Some(format!("{}.End+1", range_var)),
),
"RangeFrom" => (Some(format!("{}.Start", range_var)), None),
"RangeTo" => (None, Some(format!("{}.End", range_var))),
"RangeToInclusive" => (None, Some(format!("{}.End+1", range_var))),
_ => unreachable!("unexpected range kind: {}", range_kind),
}
}