use crate::bytecode::{BuiltinFunction, Constant, Instruction, OpCode, Operand};
use shape_ast::ast::{DataIndex, Expr};
use shape_ast::error::{Result, ShapeError};
use super::super::BytecodeCompiler;
impl BytecodeCompiler {
pub(super) fn compile_expr_data_ref(
&mut self,
data_ref: &shape_ast::ast::DataRef,
) -> Result<()> {
if let Some(timeframe) = data_ref.timeframe {
let tf_const = self.program.add_constant(Constant::Timeframe(timeframe));
self.emit(Instruction::new(
OpCode::PushTimeframe,
Some(Operand::Const(tf_const)),
));
}
let has_schema = self.program.data_schema.is_some();
match &data_ref.index {
DataIndex::Single(idx) if has_schema => {
let const_idx = self.program.add_constant(Constant::Number(*idx as f64));
self.emit(Instruction::new(
OpCode::PushConst,
Some(Operand::Const(const_idx)),
));
self.emit(Instruction::simple(OpCode::GetDataRow));
}
DataIndex::Expression(expr) if has_schema => {
self.compile_expr(expr)?;
self.emit(Instruction::simple(OpCode::GetDataRow));
}
_ => {
if let Some(local_idx) = self.resolve_local("data") {
self.emit(Instruction::new(
OpCode::LoadLocal,
Some(Operand::Local(local_idx)),
));
} else {
return Err(ShapeError::SemanticError {
message: "data[...] requires explicit data binding. Either: \
(1) Set a DataSchema on the compiler for optimized access, \
(2) Pass 'data' as a function parameter, or \
(3) Bind 'data' with: let data = ..."
.to_string(),
location: None,
});
}
match &data_ref.index {
DataIndex::Single(idx) => {
let const_idx = self.program.add_constant(Constant::Number(*idx as f64));
self.emit(Instruction::new(
OpCode::PushConst,
Some(Operand::Const(const_idx)),
));
self.emit(Instruction::simple(OpCode::GetProp));
}
DataIndex::Range(start, end) => {
let start_const =
self.program.add_constant(Constant::Number(*start as f64));
self.emit(Instruction::new(
OpCode::PushConst,
Some(Operand::Const(start_const)),
));
let end_const = self.program.add_constant(Constant::Number(*end as f64));
self.emit(Instruction::new(
OpCode::PushConst,
Some(Operand::Const(end_const)),
));
self.emit(Instruction::simple(OpCode::SliceAccess));
}
DataIndex::Expression(expr) => {
self.compile_expr(expr)?;
self.emit(Instruction::simple(OpCode::GetProp));
}
DataIndex::ExpressionRange(start, end) => {
self.compile_expr(start)?;
self.compile_expr(end)?;
self.emit(Instruction::simple(OpCode::SliceAccess));
}
}
}
}
if data_ref.timeframe.is_some() {
self.emit(Instruction::simple(OpCode::PopTimeframe));
}
Ok(())
}
pub(super) fn compile_expr_data_datetime_ref(
&mut self,
datetime_ref: &shape_ast::ast::DataDateTimeRef,
) -> Result<()> {
let const_idx = self
.program
.add_constant(Constant::DataDateTimeRef(datetime_ref.clone()));
self.emit(Instruction::new(
OpCode::PushConst,
Some(Operand::Const(const_idx)),
));
self.emit(Instruction::new(
OpCode::BuiltinCall,
Some(Operand::Builtin(BuiltinFunction::EvalDataDateTimeRef)),
));
Ok(())
}
pub(super) fn compile_expr_data_relative_access(
&mut self,
reference: &Expr,
index: &DataIndex,
) -> Result<()> {
let is_range = matches!(
index,
DataIndex::Range(_, _) | DataIndex::ExpressionRange(_, _)
);
self.compile_expr(reference)?;
match index {
DataIndex::Single(idx) => {
let const_idx = self.program.add_constant(Constant::Number(*idx as f64));
self.emit(Instruction::new(
OpCode::PushConst,
Some(Operand::Const(const_idx)),
));
}
DataIndex::Range(start, end) => {
let start_const = self.program.add_constant(Constant::Number(*start as f64));
self.emit(Instruction::new(
OpCode::PushConst,
Some(Operand::Const(start_const)),
));
let end_const = self.program.add_constant(Constant::Number(*end as f64));
self.emit(Instruction::new(
OpCode::PushConst,
Some(Operand::Const(end_const)),
));
}
DataIndex::Expression(expr) => {
self.compile_expr(expr)?;
}
DataIndex::ExpressionRange(start, end) => {
self.compile_expr(start)?;
self.compile_expr(end)?;
}
}
let builtin = if is_range {
BuiltinFunction::EvalDataRelativeRange
} else {
BuiltinFunction::EvalDataRelative
};
self.emit(Instruction::new(
OpCode::BuiltinCall,
Some(Operand::Builtin(builtin)),
));
Ok(())
}
}