use crate::bytecode::{BuiltinFunction, Constant, Instruction, OpCode, Operand};
use shape_ast::ast::Expr;
use shape_ast::error::Result;
use super::super::BytecodeCompiler;
impl BytecodeCompiler {
pub(super) fn compile_expr_time_ref(
&mut self,
time_ref: &shape_ast::ast::TimeReference,
) -> Result<()> {
let const_idx = self
.program
.add_constant(Constant::TimeReference(time_ref.clone()));
self.emit(Instruction::new(
OpCode::PushConst,
Some(Operand::Const(const_idx)),
));
self.emit(Instruction::new(
OpCode::BuiltinCall,
Some(Operand::Builtin(BuiltinFunction::EvalTimeRef)),
));
Ok(())
}
pub(super) fn compile_expr_datetime(
&mut self,
datetime_expr: &shape_ast::ast::DateTimeExpr,
) -> Result<()> {
let const_idx = self
.program
.add_constant(Constant::DateTimeExpr(datetime_expr.clone()));
self.emit(Instruction::new(
OpCode::PushConst,
Some(Operand::Const(const_idx)),
));
self.emit(Instruction::new(
OpCode::BuiltinCall,
Some(Operand::Builtin(BuiltinFunction::EvalDateTimeExpr)),
));
self.last_expr_type_info =
Some(crate::type_tracking::VariableTypeInfo::named("DateTime".to_string()));
Ok(())
}
pub(super) fn compile_expr_duration(
&mut self,
duration: &shape_ast::ast::Duration,
) -> Result<()> {
let const_idx = self
.program
.add_constant(Constant::Duration(duration.clone()));
self.emit(Instruction::new(
OpCode::PushConst,
Some(Operand::Const(const_idx)),
));
self.last_expr_type_info =
Some(crate::type_tracking::VariableTypeInfo::named("Duration".to_string()));
Ok(())
}
pub(super) fn compile_expr_timeframe_context(
&mut self,
timeframe: shape_ast::ast::Timeframe,
expr: &Expr,
) -> Result<()> {
let tf_const = self.program.add_constant(Constant::Timeframe(timeframe));
self.emit(Instruction::new(
OpCode::PushConst,
Some(Operand::Const(tf_const)),
));
self.emit(Instruction::simple(OpCode::PushTimeframe));
self.compile_expr(expr)?;
self.emit(Instruction::simple(OpCode::PopTimeframe));
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::test_utils::eval;
use shape_value::{KindedSlot, NativeKind};
use shape_value::heap_value::{HeapKind, TemporalData};
fn as_temporal(slot: &KindedSlot) -> &TemporalData {
assert_eq!(
slot.kind,
NativeKind::Ptr(HeapKind::Temporal),
"expected Ptr(Temporal) kind, got {:?}",
slot.kind,
);
let bits = slot.slot.raw();
assert!(bits != 0, "Temporal slot bits are null");
unsafe { &*(bits as *const TemporalData) }
}
fn expect_datetime(slot: &KindedSlot) -> chrono::DateTime<chrono::FixedOffset> {
match as_temporal(slot) {
TemporalData::DateTime(dt) => *dt,
other => panic!("expected DateTime, got {}", other.type_name()),
}
}
fn expect_timespan(slot: &KindedSlot) -> chrono::Duration {
match as_temporal(slot) {
TemporalData::TimeSpan(ts) => *ts,
other => panic!("expected TimeSpan, got {}", other.type_name()),
}
}
#[test]
fn test_datetime_literal_iso8601() {
let result = eval(r#"@"2024-06-15T14:30:00+00:00""#);
let dt = expect_datetime(&result);
assert_eq!(dt.timestamp(), 1718461800);
}
#[test]
fn test_datetime_literal_date_only() {
let result = eval(r#"@"2024-01-15""#);
let dt = expect_datetime(&result);
assert_eq!(dt.timestamp(), 1705276800);
}
#[test]
fn test_datetime_literal_datetime_no_tz() {
let result = eval(r#"@"2024-06-15T14:30:00""#);
let dt = expect_datetime(&result);
assert_eq!(dt.timestamp(), 1718461800);
}
#[test]
fn test_datetime_literal_in_fn() {
let result = eval(
r#"
fn get_dt() {
@"2024-01-15"
}
get_dt()
"#,
);
let dt = expect_datetime(&result);
assert_eq!(dt.timestamp(), 1705276800);
}
#[test]
fn test_datetime_named_now() {
let result = eval("@now");
let dt = expect_datetime(&result);
assert!(dt.timestamp() > 1704067200);
}
#[test]
fn test_datetime_named_today() {
use chrono::Timelike;
let result = eval("@today");
let dt = expect_datetime(&result);
assert!(dt.timestamp() > 1704067200);
assert_eq!(dt.hour(), 0);
assert_eq!(dt.minute(), 0);
assert_eq!(dt.second(), 0);
}
#[test]
fn test_duration_value_exists() {
let result = eval("3d");
let ts = expect_timespan(&result);
assert_eq!(ts.num_seconds(), 259200);
}
#[test]
fn test_datetime_plus_duration_days() {
let result = eval(
r#"
fn make_dt() { @"2024-01-15" }
fn make_dur() { 3d }
fn test() { make_dt() + make_dur() }
test()
"#,
);
let dt = expect_datetime(&result);
assert_eq!(dt.timestamp(), 1705536000);
}
#[test]
fn test_datetime_plus_duration_hours() {
let result = eval(
r#"
fn make_dt() { @"2024-01-15" }
fn make_dur() { 2h }
fn test() { make_dt() + make_dur() }
test()
"#,
);
let dt = expect_datetime(&result);
assert_eq!(dt.timestamp(), 1705284000);
}
#[test]
fn test_datetime_minus_duration() {
let result = eval(
r#"
fn make_dt() { @"2024-01-15" }
fn make_dur() { 1d }
fn test() { make_dt() - make_dur() }
test()
"#,
);
let dt = expect_datetime(&result);
assert_eq!(dt.timestamp(), 1705190400);
}
#[test]
fn test_datetime_subtraction_yields_timespan() {
let result = eval(
r#"
fn make_dt1() { @"2024-01-15" }
fn make_dt2() { @"2024-01-10" }
fn test() {
make_dt1() - make_dt2()
}
test()
"#,
);
let ts = expect_timespan(&result);
assert_eq!(ts.num_seconds(), 432000);
}
#[test]
fn test_duration_seconds() {
let result = eval("10s");
let ts = expect_timespan(&result);
assert_eq!(ts.num_seconds(), 10);
}
#[test]
fn test_duration_minutes() {
let result = eval("30m");
let ts = expect_timespan(&result);
assert_eq!(ts.num_seconds(), 1800);
}
#[test]
fn test_duration_addition() {
let result = eval(
r#"
fn make_a() { 3d }
fn make_b() { 2d }
fn test() { make_a() + make_b() }
test()
"#,
);
let ts = expect_timespan(&result);
assert_eq!(ts.num_seconds(), 432000);
}
}