Refactor series types and value enum
This commit refactors the way series are represented and handled within the AST. Key changes include: - Introducing `SeriesStorage` and `PushableStorage` traits to provide a more unified and type-safe interface for series data. - Renaming `Object` trait and its methods to clarify that it's for RTL extensions other than series (like Streams). - Updating the `Value` enum to have a distinct `Series` variant, separating it from `Object`. - Adjusting various parts of the `VM` and `register` functions to work with the new series traits and `Value::Series` variant. This change aims to improve the type system's clarity and safety when dealing with series data, aligning with Rust's best practices for trait design.
This commit is contained in:
+104
-98
@@ -138,37 +138,36 @@ impl VM {
|
||||
result = self.eval_observed(observer, &closure.exec_node);
|
||||
self.frames.pop();
|
||||
}
|
||||
Value::Object(obj) => {
|
||||
if let Some(series) = obj.as_series() {
|
||||
if next_args.len() != 1 {
|
||||
return Err(format!(
|
||||
"{} indexer expects exactly 1 argument (the lookback index), got {}",
|
||||
obj.type_name(),
|
||||
next_args.len()
|
||||
));
|
||||
}
|
||||
if let Value::Int(idx) = &next_args[0] {
|
||||
if *idx < 0 {
|
||||
return Err(format!(
|
||||
"{} lookback index cannot be negative: {}",
|
||||
obj.type_name(),
|
||||
idx
|
||||
));
|
||||
}
|
||||
result = Ok(series.get_item(*idx as usize).unwrap_or(Value::Void));
|
||||
} else {
|
||||
return Err(format!(
|
||||
"{} index must be an integer, got {}",
|
||||
obj.type_name(),
|
||||
next_args[0]
|
||||
));
|
||||
}
|
||||
} else {
|
||||
Value::Series(s) => {
|
||||
if next_args.len() != 1 {
|
||||
return Err(format!(
|
||||
"Tail call target is not callable: {}",
|
||||
obj.type_name()
|
||||
"{} indexer expects exactly 1 argument (the lookback index), got {}",
|
||||
s.series_type_name(),
|
||||
next_args.len()
|
||||
));
|
||||
}
|
||||
if let Value::Int(idx) = &next_args[0] {
|
||||
if *idx < 0 {
|
||||
return Err(format!(
|
||||
"{} lookback index cannot be negative: {}",
|
||||
s.series_type_name(),
|
||||
idx
|
||||
));
|
||||
}
|
||||
result = Ok(s.get_item(*idx as usize).unwrap_or(Value::Void));
|
||||
} else {
|
||||
return Err(format!(
|
||||
"{} index must be an integer, got {}",
|
||||
s.series_type_name(),
|
||||
next_args[0]
|
||||
));
|
||||
}
|
||||
}
|
||||
Value::Object(obj) => {
|
||||
return Err(format!(
|
||||
"Tail call target is not callable: {}",
|
||||
obj.type_name()
|
||||
));
|
||||
}
|
||||
other => {
|
||||
return Err(format!("Tail call target is not callable: {}", other));
|
||||
@@ -348,21 +347,26 @@ impl VM {
|
||||
Err(format!("Record does not have field :{}", field.name()))
|
||||
}
|
||||
}
|
||||
Value::Object(obj) => {
|
||||
let any_ptr = obj.as_any();
|
||||
if let Some(record_series) =
|
||||
any_ptr.downcast_ref::<RecordSeries>()
|
||||
{
|
||||
Value::Series(s) => {
|
||||
if let Some(record_series) = s.as_any().downcast_ref::<RecordSeries>() {
|
||||
if let Some(field_series) = record_series.field(*field) {
|
||||
let view =
|
||||
SeriesView::new(field_series, *field);
|
||||
return Ok(Value::Object(std::rc::Rc::new(view))); } else {
|
||||
let view = SeriesView::new(field_series, *field);
|
||||
return Ok(Value::Series(std::rc::Rc::new(view)));
|
||||
} else {
|
||||
return Err(format!(
|
||||
"RecordSeries does not have field :{}",
|
||||
field.name()
|
||||
));
|
||||
}
|
||||
} else if let Some(sn) = obj.as_any().downcast_ref::<StreamNode>() {
|
||||
}
|
||||
Err(format!(
|
||||
"Field accessor .{} expects a record or RecordSeries, got series:{}",
|
||||
field.name(),
|
||||
s.series_type_name()
|
||||
))
|
||||
}
|
||||
Value::Object(obj) => {
|
||||
if let Some(sn) = obj.as_any().downcast_ref::<StreamNode>() {
|
||||
let mapped = build_map_stream(sn.inner.clone(), *field);
|
||||
return Ok(Value::Object(Rc::new(mapped)));
|
||||
}
|
||||
@@ -433,7 +437,7 @@ impl VM {
|
||||
self.stack.truncate(base);
|
||||
|
||||
match func_val {
|
||||
Value::Closure(_) | Value::Object(_) => {
|
||||
Value::Closure(_) | Value::Object(_) | Value::Series(_) => {
|
||||
self.tail_call = Some((func_val, arg_vals));
|
||||
return Ok(Value::Void);
|
||||
}
|
||||
@@ -456,33 +460,33 @@ impl VM {
|
||||
k.name()
|
||||
));
|
||||
}
|
||||
} else if let Value::Object(obj) = rec {
|
||||
// Polymorphic Field Access: Allow `.field` on a RecordSeries
|
||||
if let Some(rs) = obj
|
||||
.as_any()
|
||||
.downcast_ref::<RecordSeries>()
|
||||
{
|
||||
} else if let Value::Series(s) = rec {
|
||||
if let Some(rs) = s.as_any().downcast_ref::<RecordSeries>() {
|
||||
if let Some(field_series) = rs.field(k) {
|
||||
let view = SeriesView::new(
|
||||
field_series,
|
||||
k,
|
||||
);
|
||||
return Ok(Value::Object(std::rc::Rc::new(view)));
|
||||
} else {
|
||||
let view = SeriesView::new(field_series, k);
|
||||
return Ok(Value::Series(std::rc::Rc::new(view)));
|
||||
} else {
|
||||
return Err(format!(
|
||||
"RecordSeries does not have field :{}",
|
||||
k.name()
|
||||
));
|
||||
}
|
||||
} else if let Some(sn) = obj.as_any().downcast_ref::<StreamNode>() {
|
||||
let mapped = build_map_stream(sn.inner.clone(), k);
|
||||
return Ok(Value::Object(Rc::new(mapped)));
|
||||
}
|
||||
return Err(format!(
|
||||
"Field accessor .{} expects a record, RecordSeries or Stream, got {}",
|
||||
k.name(),
|
||||
obj.type_name()
|
||||
));
|
||||
}
|
||||
}
|
||||
return Err(format!(
|
||||
"Field accessor .{} expects a record or RecordSeries, got series:{}",
|
||||
k.name(),
|
||||
s.series_type_name()
|
||||
));
|
||||
} else if let Value::Object(obj) = rec {
|
||||
if let Some(sn) = obj.as_any().downcast_ref::<StreamNode>() {
|
||||
let mapped = build_map_stream(sn.inner.clone(), k);
|
||||
return Ok(Value::Object(Rc::new(mapped)));
|
||||
}
|
||||
return Err(format!(
|
||||
"Field accessor .{} expects a record, RecordSeries or Stream, got {}",
|
||||
k.name(),
|
||||
obj.type_name()
|
||||
));
|
||||
} else {
|
||||
return Err(format!(
|
||||
"Field accessor .{} expects a record, RecordSeries or Stream, got {}",
|
||||
@@ -526,24 +530,26 @@ impl VM {
|
||||
} else {
|
||||
Err(format!("Record does not have field :{}", k.name()))
|
||||
}
|
||||
} else if let Value::Object(obj) = rec {
|
||||
if let Some(rs) = obj
|
||||
.as_any()
|
||||
.downcast_ref::<RecordSeries>()
|
||||
{
|
||||
} else if let Value::Series(s) = rec {
|
||||
if let Some(rs) = s.as_any().downcast_ref::<RecordSeries>() {
|
||||
if let Some(field_series) = rs.field(*k) {
|
||||
let view = SeriesView::new(
|
||||
field_series,
|
||||
*k,
|
||||
);
|
||||
Ok(Value::Object(std::rc::Rc::new(view)))
|
||||
let view = SeriesView::new(field_series, *k);
|
||||
Ok(Value::Series(std::rc::Rc::new(view)))
|
||||
} else {
|
||||
Err(format!(
|
||||
"RecordSeries does not have field :{}",
|
||||
k.name()
|
||||
))
|
||||
}
|
||||
} else if let Some(sn) = obj.as_any().downcast_ref::<StreamNode>() {
|
||||
} else {
|
||||
Err(format!(
|
||||
"Field accessor .{} expects a record or RecordSeries, got series:{}",
|
||||
k.name(),
|
||||
s.series_type_name()
|
||||
))
|
||||
}
|
||||
} else if let Value::Object(obj) = rec {
|
||||
if let Some(sn) = obj.as_any().downcast_ref::<StreamNode>() {
|
||||
let mapped = build_map_stream(sn.inner.clone(), *k);
|
||||
Ok(Value::Object(Rc::new(mapped)))
|
||||
} else {
|
||||
@@ -612,37 +618,36 @@ impl VM {
|
||||
self.frames.pop();
|
||||
res
|
||||
}
|
||||
Value::Object(obj) => {
|
||||
if let Some(series) = obj.as_series() {
|
||||
let arg_len = self.stack.len() - base;
|
||||
let res = if arg_len != 1 {
|
||||
Value::Series(s) => {
|
||||
let arg_len = self.stack.len() - base;
|
||||
let res = if arg_len != 1 {
|
||||
Err(format!(
|
||||
"{} indexer expects exactly 1 argument (the lookback index)",
|
||||
s.series_type_name()
|
||||
))
|
||||
} else if let Value::Int(idx) = self.stack[base] {
|
||||
if idx < 0 {
|
||||
Err(format!(
|
||||
"{} indexer expects exactly 1 argument (the lookback index)",
|
||||
obj.type_name()
|
||||
"{} lookback index cannot be negative",
|
||||
s.series_type_name()
|
||||
))
|
||||
} else if let Value::Int(idx) = self.stack[base] {
|
||||
if idx < 0 {
|
||||
Err(format!(
|
||||
"{} lookback index cannot be negative",
|
||||
obj.type_name()
|
||||
))
|
||||
} else if let Some(val) = series.get_item(idx as usize) {
|
||||
Ok(val)
|
||||
} else {
|
||||
Ok(Value::Void)
|
||||
}
|
||||
} else if let Some(val) = s.get_item(idx as usize) {
|
||||
Ok(val)
|
||||
} else {
|
||||
Err(format!(
|
||||
"{} index must be an integer",
|
||||
obj.type_name()
|
||||
))
|
||||
};
|
||||
self.stack.truncate(base);
|
||||
return res;
|
||||
Ok(Value::Void)
|
||||
}
|
||||
} else {
|
||||
self.stack.truncate(base);
|
||||
return Err(format!("Object is not callable: {}", obj.type_name()));
|
||||
}
|
||||
Err(format!(
|
||||
"{} index must be an integer",
|
||||
s.series_type_name()
|
||||
))
|
||||
};
|
||||
self.stack.truncate(base);
|
||||
return res;
|
||||
}
|
||||
Value::Object(obj) => {
|
||||
self.stack.truncate(base);
|
||||
return Err(format!("Object is not callable: {}", obj.type_name()));
|
||||
}
|
||||
_ => {
|
||||
self.stack.truncate(base);
|
||||
@@ -660,6 +665,7 @@ impl VM {
|
||||
Value::Function(_) => "Function",
|
||||
Value::Closure(_) => "Closure",
|
||||
Value::Quote(_) => "Quote",
|
||||
Value::Series(_) => "Series",
|
||||
Value::Object(_) => "Object",
|
||||
Value::Cell(_) => "Cell",
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user