Formatting
This commit is contained in:
@@ -237,7 +237,11 @@ impl<'a> Analyzer<'a> {
|
|||||||
Purity::Impure,
|
Purity::Impure,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
BoundKind::Pipe { inputs, lambda, out_type } => {
|
BoundKind::Pipe {
|
||||||
|
inputs,
|
||||||
|
lambda,
|
||||||
|
out_type,
|
||||||
|
} => {
|
||||||
let mut analyzed_inputs = Vec::with_capacity(inputs.len());
|
let mut analyzed_inputs = Vec::with_capacity(inputs.len());
|
||||||
for input in inputs {
|
for input in inputs {
|
||||||
analyzed_inputs.push(self.visit(Rc::new(input.clone())));
|
analyzed_inputs.push(self.visit(Rc::new(input.clone())));
|
||||||
@@ -288,7 +292,13 @@ impl<'a> Analyzer<'a> {
|
|||||||
p = p.min(vm.ty.purity);
|
p = p.min(vm.ty.purity);
|
||||||
new_values.push(vm);
|
new_values.push(vm);
|
||||||
}
|
}
|
||||||
(BoundKind::Record { layout: layout.clone(), values: new_values }, p)
|
(
|
||||||
|
BoundKind::Record {
|
||||||
|
layout: layout.clone(),
|
||||||
|
values: new_values,
|
||||||
|
},
|
||||||
|
p,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
BoundKind::Expansion {
|
BoundKind::Expansion {
|
||||||
|
|||||||
+738
-708
File diff suppressed because it is too large
Load Diff
@@ -206,9 +206,10 @@ where
|
|||||||
},
|
},
|
||||||
) => na == nb && aa == ab && ka == kb && va == vb && ca == cb,
|
) => na == nb && aa == ab && ka == kb && va == vb && ca == cb,
|
||||||
(BoundKind::FieldAccessor(a), BoundKind::FieldAccessor(b)) => a == b,
|
(BoundKind::FieldAccessor(a), BoundKind::FieldAccessor(b)) => a == b,
|
||||||
(BoundKind::GetField { rec: ra, field: fa }, BoundKind::GetField { rec: rb, field: fb }) => {
|
(
|
||||||
ra == rb && fa == fb
|
BoundKind::GetField { rec: ra, field: fa },
|
||||||
}
|
BoundKind::GetField { rec: rb, field: fb },
|
||||||
|
) => ra == rb && fa == fb,
|
||||||
(
|
(
|
||||||
BoundKind::If {
|
BoundKind::If {
|
||||||
cond: ca,
|
cond: ca,
|
||||||
@@ -258,9 +259,16 @@ where
|
|||||||
(BoundKind::Again { args: aa }, BoundKind::Again { args: ab }) => aa == ab,
|
(BoundKind::Again { args: aa }, BoundKind::Again { args: ab }) => aa == ab,
|
||||||
(BoundKind::Block { exprs: ea }, BoundKind::Block { exprs: eb }) => ea == eb,
|
(BoundKind::Block { exprs: ea }, BoundKind::Block { exprs: eb }) => ea == eb,
|
||||||
(BoundKind::Tuple { elements: ea }, BoundKind::Tuple { elements: eb }) => ea == eb,
|
(BoundKind::Tuple { elements: ea }, BoundKind::Tuple { elements: eb }) => ea == eb,
|
||||||
(BoundKind::Record { layout: la, values: va }, BoundKind::Record { layout: lb, values: vb }) => {
|
(
|
||||||
std::sync::Arc::ptr_eq(la, lb) && va == vb
|
BoundKind::Record {
|
||||||
}
|
layout: la,
|
||||||
|
values: va,
|
||||||
|
},
|
||||||
|
BoundKind::Record {
|
||||||
|
layout: lb,
|
||||||
|
values: vb,
|
||||||
|
},
|
||||||
|
) => std::sync::Arc::ptr_eq(la, lb) && va == vb,
|
||||||
(
|
(
|
||||||
BoundKind::Expansion {
|
BoundKind::Expansion {
|
||||||
original_call: ca,
|
original_call: ca,
|
||||||
|
|||||||
@@ -89,7 +89,11 @@ impl CapturePass {
|
|||||||
args: Box::new(Self::transform(*args, capture_map)),
|
args: Box::new(Self::transform(*args, capture_map)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
BoundKind::Pipe { inputs, lambda, out_type } => {
|
BoundKind::Pipe {
|
||||||
|
inputs,
|
||||||
|
lambda,
|
||||||
|
out_type,
|
||||||
|
} => {
|
||||||
let mut t_inputs = Vec::with_capacity(inputs.len());
|
let mut t_inputs = Vec::with_capacity(inputs.len());
|
||||||
for input in inputs {
|
for input in inputs {
|
||||||
t_inputs.push(Self::transform(input, capture_map));
|
t_inputs.push(Self::transform(input, capture_map));
|
||||||
|
|||||||
@@ -66,9 +66,7 @@ impl Dumper {
|
|||||||
self.log(&format!("Get: {} ({:?})", name.name, addr), node)
|
self.log(&format!("Get: {} ({:?})", name.name, addr), node)
|
||||||
}
|
}
|
||||||
|
|
||||||
BoundKind::FieldAccessor(k) => {
|
BoundKind::FieldAccessor(k) => self.log(&format!("FieldAccessor: .{}", k.name()), node),
|
||||||
self.log(&format!("FieldAccessor: .{}", k.name()), node)
|
|
||||||
}
|
|
||||||
|
|
||||||
BoundKind::GetField { rec, field } => {
|
BoundKind::GetField { rec, field } => {
|
||||||
self.log(&format!("GetField: .{}", field.name()), node);
|
self.log(&format!("GetField: .{}", field.name()), node);
|
||||||
@@ -232,7 +230,10 @@ impl Dumper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BoundKind::Record { layout, values } => {
|
BoundKind::Record { layout, values } => {
|
||||||
self.log(&format!("Record (Layout: {} fields)", layout.fields.len()), node);
|
self.log(
|
||||||
|
&format!("Record (Layout: {} fields)", layout.fields.len()),
|
||||||
|
node,
|
||||||
|
);
|
||||||
self.indent += 1;
|
self.indent += 1;
|
||||||
for v in values {
|
for v in values {
|
||||||
self.visit(v);
|
self.visit(v);
|
||||||
|
|||||||
@@ -386,7 +386,11 @@ impl Optimizer {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
BoundKind::Pipe { inputs, lambda, out_type } => {
|
BoundKind::Pipe {
|
||||||
|
inputs,
|
||||||
|
lambda,
|
||||||
|
out_type,
|
||||||
|
} => {
|
||||||
let mut o_inputs = Vec::with_capacity(inputs.len());
|
let mut o_inputs = Vec::with_capacity(inputs.len());
|
||||||
for input in inputs {
|
for input in inputs {
|
||||||
o_inputs.push(self.visit_node(input, sub, path));
|
o_inputs.push(self.visit_node(input, sub, path));
|
||||||
@@ -559,17 +563,28 @@ impl Optimizer {
|
|||||||
.collect();
|
.collect();
|
||||||
(BoundKind::Tuple { elements }, node.ty.clone())
|
(BoundKind::Tuple { elements }, node.ty.clone())
|
||||||
}
|
}
|
||||||
BoundKind::Record { ref layout, ref values } => {
|
BoundKind::Record {
|
||||||
|
ref layout,
|
||||||
|
ref values,
|
||||||
|
} => {
|
||||||
let mapped_values: Vec<_> = values
|
let mapped_values: Vec<_> = values
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| self.visit_node(v.clone(), sub, path))
|
.map(|v| self.visit_node(v.clone(), sub, path))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if self.enabled && let Some(folded) = folder.try_fold_record(layout, &mapped_values, &node) {
|
if self.enabled
|
||||||
|
&& let Some(folded) = folder.try_fold_record(layout, &mapped_values, &node)
|
||||||
|
{
|
||||||
return folded;
|
return folded;
|
||||||
}
|
}
|
||||||
|
|
||||||
(BoundKind::Record { layout: layout.clone(), values: mapped_values }, node.ty.clone())
|
(
|
||||||
|
BoundKind::Record {
|
||||||
|
layout: layout.clone(),
|
||||||
|
values: mapped_values,
|
||||||
|
},
|
||||||
|
node.ty.clone(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
BoundKind::Expansion {
|
BoundKind::Expansion {
|
||||||
ref original_call,
|
ref original_call,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, NodeMetrics};
|
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, NodeMetrics};
|
||||||
use crate::ast::nodes::Node;
|
use crate::ast::nodes::Node;
|
||||||
use crate::ast::types::{Purity, StaticType, Value, RecordLayout};
|
use crate::ast::types::{Purity, RecordLayout, StaticType, Value};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
|||||||
@@ -158,7 +158,10 @@ impl UsageInfo {
|
|||||||
}
|
}
|
||||||
self.collect(lambda);
|
self.collect(lambda);
|
||||||
}
|
}
|
||||||
BoundKind::Nop | BoundKind::FieldAccessor(_) | BoundKind::Extension(_) | BoundKind::Error => {}
|
BoundKind::Nop
|
||||||
|
| BoundKind::FieldAccessor(_)
|
||||||
|
| BoundKind::Extension(_)
|
||||||
|
| BoundKind::Error => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,10 +132,7 @@ impl Specializer {
|
|||||||
(BoundKind::Tuple { elements }, node.ty.clone())
|
(BoundKind::Tuple { elements }, node.ty.clone())
|
||||||
}
|
}
|
||||||
BoundKind::Record { layout, values } => {
|
BoundKind::Record { layout, values } => {
|
||||||
let values = values
|
let values = values.into_iter().map(|v| self.visit_node(v)).collect();
|
||||||
.into_iter()
|
|
||||||
.map(|v| self.visit_node(v))
|
|
||||||
.collect();
|
|
||||||
(BoundKind::Record { layout, values }, node.ty.clone())
|
(BoundKind::Record { layout, values }, node.ty.clone())
|
||||||
}
|
}
|
||||||
BoundKind::Expansion {
|
BoundKind::Expansion {
|
||||||
|
|||||||
@@ -48,7 +48,11 @@ impl TCO {
|
|||||||
args: Box::new(Self::transform(Rc::new((**args).clone()), false)),
|
args: Box::new(Self::transform(Rc::new((**args).clone()), false)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BoundKind::Pipe { inputs, lambda, out_type } => {
|
BoundKind::Pipe {
|
||||||
|
inputs,
|
||||||
|
lambda,
|
||||||
|
out_type,
|
||||||
|
} => {
|
||||||
let mut t_inputs = Vec::with_capacity(inputs.len());
|
let mut t_inputs = Vec::with_capacity(inputs.len());
|
||||||
for input in inputs {
|
for input in inputs {
|
||||||
t_inputs.push(Self::transform(Rc::new(input.clone()), false));
|
t_inputs.push(Self::transform(Rc::new(input.clone()), false));
|
||||||
|
|||||||
@@ -79,7 +79,12 @@ impl TypeChecker {
|
|||||||
Self { global_types }
|
Self { global_types }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check(&self, node: BoundNode, arg_types: &[StaticType], diag: &mut Diagnostics) -> TypedNode {
|
pub fn check(
|
||||||
|
&self,
|
||||||
|
node: BoundNode,
|
||||||
|
arg_types: &[StaticType],
|
||||||
|
diag: &mut Diagnostics,
|
||||||
|
) -> TypedNode {
|
||||||
match node.kind {
|
match node.kind {
|
||||||
BoundKind::Lambda {
|
BoundKind::Lambda {
|
||||||
params,
|
params,
|
||||||
@@ -105,7 +110,12 @@ impl TypeChecker {
|
|||||||
StaticType::Tuple(arg_types.to_vec())
|
StaticType::Tuple(arg_types.to_vec())
|
||||||
};
|
};
|
||||||
|
|
||||||
let params_typed = self.check_params(params.as_ref().clone(), &arg_tuple_ty, &mut lambda_ctx, diag);
|
let params_typed = self.check_params(
|
||||||
|
params.as_ref().clone(),
|
||||||
|
&arg_tuple_ty,
|
||||||
|
&mut lambda_ctx,
|
||||||
|
diag,
|
||||||
|
);
|
||||||
|
|
||||||
// 4. Check body with the new types
|
// 4. Check body with the new types
|
||||||
let body_typed = self.check_node((*body).clone(), &mut lambda_ctx, diag);
|
let body_typed = self.check_node((*body).clone(), &mut lambda_ctx, diag);
|
||||||
@@ -212,7 +222,13 @@ impl TypeChecker {
|
|||||||
| StaticType::Record(_)
|
| StaticType::Record(_)
|
||||||
| StaticType::Error => {}
|
| StaticType::Error => {}
|
||||||
_ => {
|
_ => {
|
||||||
diag.push_error(format!("Cannot destructure type {} as a tuple/vector", specialized_ty), Some(node.identity.clone()));
|
diag.push_error(
|
||||||
|
format!(
|
||||||
|
"Cannot destructure type {} as a tuple/vector",
|
||||||
|
specialized_ty
|
||||||
|
),
|
||||||
|
Some(node.identity.clone()),
|
||||||
|
);
|
||||||
return Node {
|
return Node {
|
||||||
identity: node.identity,
|
identity: node.identity,
|
||||||
kind: BoundKind::Error,
|
kind: BoundKind::Error,
|
||||||
@@ -251,7 +267,10 @@ impl TypeChecker {
|
|||||||
}
|
}
|
||||||
BoundKind::Error => (BoundKind::Error, StaticType::Error),
|
BoundKind::Error => (BoundKind::Error, StaticType::Error),
|
||||||
_ => {
|
_ => {
|
||||||
diag.push_error("Invalid node in parameter list", Some(node.identity.clone()));
|
diag.push_error(
|
||||||
|
"Invalid node in parameter list",
|
||||||
|
Some(node.identity.clone()),
|
||||||
|
);
|
||||||
(BoundKind::Error, StaticType::Error)
|
(BoundKind::Error, StaticType::Error)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -263,7 +282,12 @@ impl TypeChecker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_node(&self, node: BoundNode, ctx: &mut TypeContext, diag: &mut Diagnostics) -> TypedNode {
|
fn check_node(
|
||||||
|
&self,
|
||||||
|
node: BoundNode,
|
||||||
|
ctx: &mut TypeContext,
|
||||||
|
diag: &mut Diagnostics,
|
||||||
|
) -> TypedNode {
|
||||||
let (kind, ty) = match node.kind {
|
let (kind, ty) = match node.kind {
|
||||||
BoundKind::Nop => (BoundKind::Nop, StaticType::Void),
|
BoundKind::Nop => (BoundKind::Nop, StaticType::Void),
|
||||||
|
|
||||||
@@ -306,7 +330,9 @@ impl TypeChecker {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
BoundKind::FieldAccessor(k) => (BoundKind::FieldAccessor(k), StaticType::FieldAccessor(k)),
|
BoundKind::FieldAccessor(k) => {
|
||||||
|
(BoundKind::FieldAccessor(k), StaticType::FieldAccessor(k))
|
||||||
|
}
|
||||||
|
|
||||||
BoundKind::GetField { rec, field } => {
|
BoundKind::GetField { rec, field } => {
|
||||||
let rec_typed = self.check_node(*rec, ctx, diag);
|
let rec_typed = self.check_node(*rec, ctx, diag);
|
||||||
@@ -315,14 +341,24 @@ impl TypeChecker {
|
|||||||
if let Some(idx) = layout.index_of(field) {
|
if let Some(idx) = layout.index_of(field) {
|
||||||
layout.fields[idx].1.clone()
|
layout.fields[idx].1.clone()
|
||||||
} else {
|
} else {
|
||||||
diag.push_error(format!("Record does not have field :{}", field.name()), Some(rec_typed.identity.clone()));
|
diag.push_error(
|
||||||
|
format!("Record does not have field :{}", field.name()),
|
||||||
|
Some(rec_typed.identity.clone()),
|
||||||
|
);
|
||||||
StaticType::Error
|
StaticType::Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StaticType::Any => StaticType::Any,
|
StaticType::Any => StaticType::Any,
|
||||||
StaticType::Error => StaticType::Error,
|
StaticType::Error => StaticType::Error,
|
||||||
_ => {
|
_ => {
|
||||||
diag.push_error(format!("Cannot access field :{} on non-record type {}", field.name(), rec_typed.ty), Some(rec_typed.identity.clone()));
|
diag.push_error(
|
||||||
|
format!(
|
||||||
|
"Cannot access field :{} on non-record type {}",
|
||||||
|
field.name(),
|
||||||
|
rec_typed.ty
|
||||||
|
),
|
||||||
|
Some(rec_typed.identity.clone()),
|
||||||
|
);
|
||||||
StaticType::Error
|
StaticType::Error
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -463,7 +499,12 @@ impl TypeChecker {
|
|||||||
TypeContext::new(64, upvalue_types, ctx.global_types, Some(ctx));
|
TypeContext::new(64, upvalue_types, ctx.global_types, Some(ctx));
|
||||||
|
|
||||||
// 3. Check parameters and body
|
// 3. Check parameters and body
|
||||||
let params_typed = self.check_params(params.as_ref().clone(), &StaticType::Any, &mut lambda_ctx, diag);
|
let params_typed = self.check_params(
|
||||||
|
params.as_ref().clone(),
|
||||||
|
&StaticType::Any,
|
||||||
|
&mut lambda_ctx,
|
||||||
|
diag,
|
||||||
|
);
|
||||||
|
|
||||||
// Set current params type for 'again' validation
|
// Set current params type for 'again' validation
|
||||||
lambda_ctx.current_params_ty = Some(params_typed.ty.clone());
|
lambda_ctx.current_params_ty = Some(params_typed.ty.clone());
|
||||||
@@ -515,7 +556,13 @@ impl TypeChecker {
|
|||||||
let ret_ty = match callee_typed.ty.resolve_call(&args_typed.ty) {
|
let ret_ty = match callee_typed.ty.resolve_call(&args_typed.ty) {
|
||||||
Some(ty) => ty,
|
Some(ty) => ty,
|
||||||
None => {
|
None => {
|
||||||
diag.push_error(format!("Invalid arguments for function call. Expected {}, got {}", callee_typed.ty, args_typed.ty), Some(node.identity.clone()));
|
diag.push_error(
|
||||||
|
format!(
|
||||||
|
"Invalid arguments for function call. Expected {}, got {}",
|
||||||
|
callee_typed.ty, args_typed.ty
|
||||||
|
),
|
||||||
|
Some(node.identity.clone()),
|
||||||
|
);
|
||||||
StaticType::Error
|
StaticType::Error
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -553,7 +600,13 @@ impl TypeChecker {
|
|||||||
if let Some(expected_ty) = &ctx.current_params_ty
|
if let Some(expected_ty) = &ctx.current_params_ty
|
||||||
&& !expected_ty.is_assignable_from(&args_typed.ty)
|
&& !expected_ty.is_assignable_from(&args_typed.ty)
|
||||||
{
|
{
|
||||||
diag.push_error(format!("Type mismatch in 'again' call: expected {}, but got {}", expected_ty, args_typed.ty), Some(node.identity.clone()));
|
diag.push_error(
|
||||||
|
format!(
|
||||||
|
"Type mismatch in 'again' call: expected {}, but got {}",
|
||||||
|
expected_ty, args_typed.ty
|
||||||
|
),
|
||||||
|
Some(node.identity.clone()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
(
|
||||||
@@ -606,13 +659,13 @@ impl TypeChecker {
|
|||||||
BoundKind::Record { layout, values } => {
|
BoundKind::Record { layout, values } => {
|
||||||
let mut typed_values = Vec::with_capacity(values.len());
|
let mut typed_values = Vec::with_capacity(values.len());
|
||||||
let mut fields_ty = Vec::with_capacity(values.len());
|
let mut fields_ty = Vec::with_capacity(values.len());
|
||||||
|
|
||||||
for (i, v) in values.into_iter().enumerate() {
|
for (i, v) in values.into_iter().enumerate() {
|
||||||
let vt = self.check_node(v, ctx, diag);
|
let vt = self.check_node(v, ctx, diag);
|
||||||
fields_ty.push((layout.fields[i].0, vt.ty.clone()));
|
fields_ty.push((layout.fields[i].0, vt.ty.clone()));
|
||||||
typed_values.push(vt);
|
typed_values.push(vt);
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_layout = crate::ast::types::RecordLayout::get_or_create(fields_ty);
|
let new_layout = crate::ast::types::RecordLayout::get_or_create(fields_ty);
|
||||||
(
|
(
|
||||||
BoundKind::Record {
|
BoundKind::Record {
|
||||||
@@ -639,7 +692,13 @@ impl TypeChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BoundKind::Extension(_ext) => {
|
BoundKind::Extension(_ext) => {
|
||||||
diag.push_error(format!("TypeChecking for extension '{}' not implemented", _ext.display_name()), Some(node.identity.clone()));
|
diag.push_error(
|
||||||
|
format!(
|
||||||
|
"TypeChecking for extension '{}' not implemented",
|
||||||
|
_ext.display_name()
|
||||||
|
),
|
||||||
|
Some(node.identity.clone()),
|
||||||
|
);
|
||||||
(BoundKind::Error, StaticType::Error)
|
(BoundKind::Error, StaticType::Error)
|
||||||
}
|
}
|
||||||
BoundKind::Error => (BoundKind::Error, StaticType::Error),
|
BoundKind::Error => (BoundKind::Error, StaticType::Error),
|
||||||
|
|||||||
+51
-21
@@ -36,7 +36,7 @@ impl CompilationResult {
|
|||||||
diagnostics: Diagnostics::new(),
|
diagnostics: Diagnostics::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn error(msg: impl Into<String>) -> Self {
|
pub fn error(msg: impl Into<String>) -> Self {
|
||||||
let mut diag = Diagnostics::new();
|
let mut diag = Diagnostics::new();
|
||||||
diag.push_error(msg, None);
|
diag.push_error(msg, None);
|
||||||
@@ -123,11 +123,16 @@ impl MacroEvaluator for RuntimeMacroEvaluator {
|
|||||||
let bound_ast = crate::ast::compiler::captures::CapturePass::apply(bound_ast, &captures);
|
let bound_ast = crate::ast::compiler::captures::CapturePass::apply(bound_ast, &captures);
|
||||||
let checker = TypeChecker::new(self.global_types.clone());
|
let checker = TypeChecker::new(self.global_types.clone());
|
||||||
let typed_ast = checker.check(bound_ast, &[], &mut diag);
|
let typed_ast = checker.check(bound_ast, &[], &mut diag);
|
||||||
|
|
||||||
if diag.has_errors() {
|
if diag.has_errors() {
|
||||||
return Err(diag.items.into_iter().map(|d| d.message).collect::<Vec<_>>().join("\n"));
|
return Err(diag
|
||||||
|
.items
|
||||||
|
.into_iter()
|
||||||
|
.map(|d| d.message)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let exec_ast = TCO::optimize(Analyzer::analyze(&typed_ast, &HashMap::new())); // Minimal analysis for macro eval
|
let exec_ast = TCO::optimize(Analyzer::analyze(&typed_ast, &HashMap::new())); // Minimal analysis for macro eval
|
||||||
|
|
||||||
let mut vm = VM::new(self.global_values.clone());
|
let mut vm = VM::new(self.global_values.clone());
|
||||||
@@ -191,7 +196,9 @@ impl Environment {
|
|||||||
let mut parser = crate::ast::parser::Parser::new(source);
|
let mut parser = crate::ast::parser::Parser::new(source);
|
||||||
let untyped_ast = parser.parse_expression();
|
let untyped_ast = parser.parse_expression();
|
||||||
let mut expander = self.get_expander();
|
let mut expander = self.get_expander();
|
||||||
let _ = expander.expand(untyped_ast).expect("Failed to expand prelude");
|
let _ = expander
|
||||||
|
.expand(untyped_ast)
|
||||||
|
.expect("Failed to expand prelude");
|
||||||
*self.macro_registry.borrow_mut() = expander.into_registry();
|
*self.macro_registry.borrow_mut() = expander.into_registry();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,7 +268,9 @@ impl Environment {
|
|||||||
let untyped_ast = parser.parse_expression();
|
let untyped_ast = parser.parse_expression();
|
||||||
|
|
||||||
if !parser.at_eof() {
|
if !parser.at_eof() {
|
||||||
parser.diagnostics.push_error("Unexpected trailing expressions in script.", None);
|
parser
|
||||||
|
.diagnostics
|
||||||
|
.push_error("Unexpected trailing expressions in script.", None);
|
||||||
return CompilationResult {
|
return CompilationResult {
|
||||||
ast: None,
|
ast: None,
|
||||||
diagnostics: parser.diagnostics,
|
diagnostics: parser.diagnostics,
|
||||||
@@ -273,17 +282,24 @@ impl Environment {
|
|||||||
Ok(ast) => ast,
|
Ok(ast) => ast,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
diagnostics.push_error(e, None);
|
diagnostics.push_error(e, None);
|
||||||
return CompilationResult { ast: None, diagnostics };
|
return CompilationResult {
|
||||||
|
ast: None,
|
||||||
|
diagnostics,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (bound_ast, captures) = match Binder::bind_root(self.global_names.clone(), &expanded_ast, &mut diagnostics) {
|
let (bound_ast, captures) =
|
||||||
Ok(res) => res,
|
match Binder::bind_root(self.global_names.clone(), &expanded_ast, &mut diagnostics) {
|
||||||
Err(e) => {
|
Ok(res) => res,
|
||||||
diagnostics.push_error(e, None);
|
Err(e) => {
|
||||||
return CompilationResult { ast: None, diagnostics };
|
diagnostics.push_error(e, None);
|
||||||
}
|
return CompilationResult {
|
||||||
};
|
ast: None,
|
||||||
|
diagnostics,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let bound_ast = crate::ast::compiler::captures::CapturePass::apply(bound_ast, &captures);
|
let bound_ast = crate::ast::compiler::captures::CapturePass::apply(bound_ast, &captures);
|
||||||
|
|
||||||
@@ -373,7 +389,10 @@ impl Environment {
|
|||||||
|
|
||||||
let mut final_res = res;
|
let mut final_res = res;
|
||||||
if let Value::Object(obj) = &final_res
|
if let Value::Object(obj) = &final_res
|
||||||
&& obj.as_any().downcast_ref::<crate::ast::vm::Closure>().is_some()
|
&& obj
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<crate::ast::vm::Closure>()
|
||||||
|
.is_some()
|
||||||
{
|
{
|
||||||
final_res = match vm.run_with_args(obj.clone(), args) {
|
final_res = match vm.run_with_args(obj.clone(), args) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
@@ -407,9 +426,14 @@ impl Environment {
|
|||||||
let mut diag = Diagnostics::new();
|
let mut diag = Diagnostics::new();
|
||||||
let checker = TypeChecker::new(global_types.clone());
|
let checker = TypeChecker::new(global_types.clone());
|
||||||
let retyped_ast = checker.check(func_template, arg_types, &mut diag);
|
let retyped_ast = checker.check(func_template, arg_types, &mut diag);
|
||||||
|
|
||||||
if diag.has_errors() {
|
if diag.has_errors() {
|
||||||
return Err(diag.items.into_iter().map(|d| d.message).collect::<Vec<_>>().join("\n"));
|
return Err(diag
|
||||||
|
.items
|
||||||
|
.into_iter()
|
||||||
|
.map(|d| d.message)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let analyzed = Analyzer::analyze(&retyped_ast, &global_purity.borrow());
|
let analyzed = Analyzer::analyze(&retyped_ast, &global_purity.borrow());
|
||||||
@@ -465,7 +489,9 @@ impl Environment {
|
|||||||
}
|
}
|
||||||
res
|
res
|
||||||
} else {
|
} else {
|
||||||
self.compile(source).into_result().and_then(|ast| self.run_script_compiled(ast))
|
self.compile(source)
|
||||||
|
.into_result()
|
||||||
|
.and_then(|ast| self.run_script_compiled(ast))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,7 +518,11 @@ impl Environment {
|
|||||||
|
|
||||||
while let Ok(Value::TailCallRequest(payload)) = result {
|
while let Ok(Value::TailCallRequest(payload)) = result {
|
||||||
let (next_obj, next_args) = *payload;
|
let (next_obj, next_args) = *payload;
|
||||||
if next_obj.as_any().downcast_ref::<crate::ast::vm::Closure>().is_some() {
|
if next_obj
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<crate::ast::vm::Closure>()
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
result = vm.run_with_args_observed(&mut observer, next_obj, next_args);
|
result = vm.run_with_args_observed(&mut observer, next_obj, next_args);
|
||||||
} else {
|
} else {
|
||||||
result = Err(format!(
|
result = Err(format!(
|
||||||
@@ -502,9 +532,9 @@ impl Environment {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.run_pipeline();
|
self.run_pipeline();
|
||||||
|
|
||||||
Ok((result, observer.logs))
|
Ok((result, observer.logs))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-9
@@ -1,7 +1,7 @@
|
|||||||
|
use crate::ast::diagnostics::Diagnostics;
|
||||||
use crate::ast::lexer::{Lexer, Token, TokenKind};
|
use crate::ast::lexer::{Lexer, Token, TokenKind};
|
||||||
use crate::ast::nodes::{Node, Symbol, UntypedKind};
|
use crate::ast::nodes::{Node, Symbol, UntypedKind};
|
||||||
use crate::ast::types::{Identity, Keyword, NodeIdentity, Value};
|
use crate::ast::types::{Identity, Keyword, NodeIdentity, Value};
|
||||||
use crate::ast::diagnostics::Diagnostics;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct Parser<'a> {
|
pub struct Parser<'a> {
|
||||||
@@ -35,7 +35,8 @@ impl<'a> Parser<'a> {
|
|||||||
let next = match self.lexer.next_token() {
|
let next = match self.lexer.next_token() {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.diagnostics.push_error(e, Some(NodeIdentity::new(self.current_token.location)));
|
self.diagnostics
|
||||||
|
.push_error(e, Some(NodeIdentity::new(self.current_token.location)));
|
||||||
Token {
|
Token {
|
||||||
kind: TokenKind::EOF,
|
kind: TokenKind::EOF,
|
||||||
location: self.current_token.location,
|
location: self.current_token.location,
|
||||||
@@ -331,7 +332,10 @@ impl<'a> Parser<'a> {
|
|||||||
fn parse_param_vector(&mut self) -> Node<UntypedKind> {
|
fn parse_param_vector(&mut self) -> Node<UntypedKind> {
|
||||||
if *self.peek() != TokenKind::LeftBracket {
|
if *self.peek() != TokenKind::LeftBracket {
|
||||||
self.diagnostics.push_error(
|
self.diagnostics.push_error(
|
||||||
format!("Expected parameter vector [...] for fn, found {:?}", self.peek()),
|
format!(
|
||||||
|
"Expected parameter vector [...] for fn, found {:?}",
|
||||||
|
self.peek()
|
||||||
|
),
|
||||||
Some(NodeIdentity::new(self.current_token.location)),
|
Some(NodeIdentity::new(self.current_token.location)),
|
||||||
);
|
);
|
||||||
return Node {
|
return Node {
|
||||||
@@ -376,7 +380,10 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.diagnostics.push_error(
|
self.diagnostics.push_error(
|
||||||
format!("Expected identifier or pattern vector [...] for definition, found {:?}", next),
|
format!(
|
||||||
|
"Expected identifier or pattern vector [...] for definition, found {:?}",
|
||||||
|
next
|
||||||
|
),
|
||||||
Some(NodeIdentity::new(self.current_token.location)),
|
Some(NodeIdentity::new(self.current_token.location)),
|
||||||
);
|
);
|
||||||
Node {
|
Node {
|
||||||
@@ -388,11 +395,7 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_call(
|
fn parse_call(&mut self, callee: Node<UntypedKind>, identity: Identity) -> Node<UntypedKind> {
|
||||||
&mut self,
|
|
||||||
callee: Node<UntypedKind>,
|
|
||||||
identity: Identity,
|
|
||||||
) -> Node<UntypedKind> {
|
|
||||||
let mut elements = Vec::new();
|
let mut elements = Vec::new();
|
||||||
|
|
||||||
while *self.peek() != TokenKind::RightParen && *self.peek() != TokenKind::EOF {
|
while *self.peek() != TokenKind::RightParen && *self.peek() != TokenKind::EOF {
|
||||||
|
|||||||
+176
-173
@@ -1,173 +1,176 @@
|
|||||||
use crate::ast::environment::Environment;
|
use crate::ast::environment::Environment;
|
||||||
use crate::ast::types::{Purity, Signature, StaticType, Value};
|
use crate::ast::types::{Purity, Signature, StaticType, Value};
|
||||||
use std::f64::consts;
|
use std::f64::consts;
|
||||||
|
|
||||||
pub fn register(env: &Environment) {
|
pub fn register(env: &Environment) {
|
||||||
// Constants
|
// Constants
|
||||||
env.register_constant("PI", StaticType::Float, Value::Float(consts::PI));
|
env.register_constant("PI", StaticType::Float, Value::Float(consts::PI));
|
||||||
env.register_constant("E", StaticType::Float, Value::Float(consts::E));
|
env.register_constant("E", StaticType::Float, Value::Float(consts::E));
|
||||||
|
|
||||||
// Helper to get f64 from Value (Int or Float)
|
// Helper to get f64 from Value (Int or Float)
|
||||||
fn to_f64(v: &Value) -> Option<f64> {
|
fn to_f64(v: &Value) -> Option<f64> {
|
||||||
match v {
|
match v {
|
||||||
Value::Float(f) => Some(*f),
|
Value::Float(f) => Some(*f),
|
||||||
Value::Int(i) => Some(*i as f64),
|
Value::Int(i) => Some(*i as f64),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unary functions (float -> float)
|
// Unary functions (float -> float)
|
||||||
let register_unary = |name: &'static str, f: fn(f64) -> f64| {
|
let register_unary = |name: &'static str, f: fn(f64) -> f64| {
|
||||||
let ty = StaticType::Function(Box::new(Signature {
|
let ty = StaticType::Function(Box::new(Signature {
|
||||||
params: StaticType::Tuple(vec![StaticType::Float]),
|
params: StaticType::Tuple(vec![StaticType::Float]),
|
||||||
ret: StaticType::Float,
|
ret: StaticType::Float,
|
||||||
}));
|
}));
|
||||||
env.register_native_fn(name, ty, Purity::Pure, move |args| {
|
env.register_native_fn(name, ty, Purity::Pure, move |args| {
|
||||||
if let Some(x) = to_f64(&args[0]) {
|
if let Some(x) = to_f64(&args[0]) {
|
||||||
Value::Float(f(x))
|
Value::Float(f(x))
|
||||||
} else {
|
} else {
|
||||||
Value::Void
|
Value::Void
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Unary functions (float -> int)
|
// Unary functions (float -> int)
|
||||||
let register_to_int = |name: &'static str, f: fn(f64) -> f64| {
|
let register_to_int = |name: &'static str, f: fn(f64) -> f64| {
|
||||||
let ty = StaticType::Function(Box::new(Signature {
|
let ty = StaticType::Function(Box::new(Signature {
|
||||||
params: StaticType::Tuple(vec![StaticType::Float]),
|
params: StaticType::Tuple(vec![StaticType::Float]),
|
||||||
ret: StaticType::Int,
|
ret: StaticType::Int,
|
||||||
}));
|
}));
|
||||||
env.register_native_fn(name, ty, Purity::Pure, move |args| {
|
env.register_native_fn(name, ty, Purity::Pure, move |args| {
|
||||||
if let Some(x) = to_f64(&args[0]) {
|
if let Some(x) = to_f64(&args[0]) {
|
||||||
Value::Int(f(x) as i64)
|
Value::Int(f(x) as i64)
|
||||||
} else {
|
} else {
|
||||||
Value::Void
|
Value::Void
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
register_unary("sqrt", f64::sqrt);
|
register_unary("sqrt", f64::sqrt);
|
||||||
register_unary("sin", f64::sin);
|
register_unary("sin", f64::sin);
|
||||||
register_unary("cos", f64::cos);
|
register_unary("cos", f64::cos);
|
||||||
register_unary("tan", f64::tan);
|
register_unary("tan", f64::tan);
|
||||||
register_unary("asin", f64::asin);
|
register_unary("asin", f64::asin);
|
||||||
register_unary("acos", f64::acos);
|
register_unary("acos", f64::acos);
|
||||||
register_unary("atan", f64::atan);
|
register_unary("atan", f64::atan);
|
||||||
register_unary("exp", f64::exp);
|
register_unary("exp", f64::exp);
|
||||||
register_unary("ln", f64::ln);
|
register_unary("ln", f64::ln);
|
||||||
register_unary("log10", f64::log10);
|
register_unary("log10", f64::log10);
|
||||||
register_unary("fract", f64::fract);
|
register_unary("fract", f64::fract);
|
||||||
|
|
||||||
// Rounding functions returning Int
|
// Rounding functions returning Int
|
||||||
register_to_int("round", f64::round);
|
register_to_int("round", f64::round);
|
||||||
register_to_int("floor", f64::floor);
|
register_to_int("floor", f64::floor);
|
||||||
register_to_int("ceil", f64::ceil);
|
register_to_int("ceil", f64::ceil);
|
||||||
register_to_int("trunc", f64::trunc);
|
register_to_int("trunc", f64::trunc);
|
||||||
|
|
||||||
// Binary functions (float, float -> float)
|
// Binary functions (float, float -> float)
|
||||||
let register_binary = |name: &'static str, f: fn(f64, f64) -> f64| {
|
let register_binary = |name: &'static str, f: fn(f64, f64) -> f64| {
|
||||||
let ty = StaticType::Function(Box::new(Signature {
|
let ty = StaticType::Function(Box::new(Signature {
|
||||||
params: StaticType::Tuple(vec![StaticType::Float, StaticType::Float]),
|
params: StaticType::Tuple(vec![StaticType::Float, StaticType::Float]),
|
||||||
ret: StaticType::Float,
|
ret: StaticType::Float,
|
||||||
}));
|
}));
|
||||||
env.register_native_fn(name, ty, Purity::Pure, move |args| {
|
env.register_native_fn(name, ty, Purity::Pure, move |args| {
|
||||||
if let (Some(a), Some(b)) = (to_f64(&args[0]), to_f64(&args[1])) {
|
if let (Some(a), Some(b)) = (to_f64(&args[0]), to_f64(&args[1])) {
|
||||||
Value::Float(f(a, b))
|
Value::Float(f(a, b))
|
||||||
} else {
|
} else {
|
||||||
Value::Void
|
Value::Void
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
register_binary("atan2", f64::atan2);
|
register_binary("atan2", f64::atan2);
|
||||||
register_binary("pow", f64::powf);
|
register_binary("pow", f64::powf);
|
||||||
register_binary("log", f64::log);
|
register_binary("log", f64::log);
|
||||||
|
|
||||||
// Specialized abs to handle Int -> Int, Float -> Float
|
// Specialized abs to handle Int -> Int, Float -> Float
|
||||||
let abs_ty = StaticType::Function(Box::new(Signature {
|
let abs_ty = StaticType::Function(Box::new(Signature {
|
||||||
params: StaticType::Tuple(vec![StaticType::Any]),
|
params: StaticType::Tuple(vec![StaticType::Any]),
|
||||||
ret: StaticType::Any,
|
ret: StaticType::Any,
|
||||||
}));
|
}));
|
||||||
env.register_native_fn("abs", abs_ty, Purity::Pure, |args| {
|
env.register_native_fn("abs", abs_ty, Purity::Pure, |args| {
|
||||||
if args.len() != 1 {
|
if args.len() != 1 {
|
||||||
return Value::Void;
|
return Value::Void;
|
||||||
}
|
}
|
||||||
match args[0] {
|
match args[0] {
|
||||||
Value::Int(i) => Value::Int(i.abs()),
|
Value::Int(i) => Value::Int(i.abs()),
|
||||||
Value::Float(f) => Value::Float(f.abs()),
|
Value::Float(f) => Value::Float(f.abs()),
|
||||||
_ => Value::Void,
|
_ => Value::Void,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Variadic min / max
|
// Variadic min / max
|
||||||
let variadic_ty = StaticType::Function(Box::new(Signature {
|
let variadic_ty = StaticType::Function(Box::new(Signature {
|
||||||
params: StaticType::Any,
|
params: StaticType::Any,
|
||||||
ret: StaticType::Any,
|
ret: StaticType::Any,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
env.register_native_fn("min", variadic_ty.clone(), Purity::Pure, |args| {
|
env.register_native_fn("min", variadic_ty.clone(), Purity::Pure, |args| {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return Value::Void;
|
return Value::Void;
|
||||||
}
|
}
|
||||||
let mut best = args[0].clone();
|
let mut best = args[0].clone();
|
||||||
for arg in &args[1..] {
|
for arg in &args[1..] {
|
||||||
if let Some(ord) = arg.partial_cmp(&best)
|
if let Some(ord) = arg.partial_cmp(&best)
|
||||||
&& ord == std::cmp::Ordering::Less
|
&& ord == std::cmp::Ordering::Less
|
||||||
{
|
{
|
||||||
best = arg.clone();
|
best = arg.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
best
|
best
|
||||||
});
|
});
|
||||||
|
|
||||||
env.register_native_fn("max", variadic_ty, Purity::Pure, |args| {
|
env.register_native_fn("max", variadic_ty, Purity::Pure, |args| {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return Value::Void;
|
return Value::Void;
|
||||||
}
|
}
|
||||||
let mut best = args[0].clone();
|
let mut best = args[0].clone();
|
||||||
for arg in &args[1..] {
|
for arg in &args[1..] {
|
||||||
if let Some(ord) = arg.partial_cmp(&best)
|
if let Some(ord) = arg.partial_cmp(&best)
|
||||||
&& ord == std::cmp::Ordering::Greater
|
&& ord == std::cmp::Ordering::Greater
|
||||||
{
|
{
|
||||||
best = arg.clone();
|
best = arg.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
best
|
best
|
||||||
});
|
});
|
||||||
|
|
||||||
// Random generator factory (Closure-based)
|
// Random generator factory (Closure-based)
|
||||||
let generator_sig = Signature {
|
let generator_sig = Signature {
|
||||||
params: StaticType::Tuple(vec![]),
|
params: StaticType::Tuple(vec![]),
|
||||||
ret: StaticType::Float,
|
ret: StaticType::Float,
|
||||||
};
|
};
|
||||||
|
|
||||||
let make_random_ty = StaticType::FunctionOverloads(vec![
|
let make_random_ty = StaticType::FunctionOverloads(vec![
|
||||||
Signature {
|
Signature {
|
||||||
params: StaticType::Tuple(vec![]),
|
params: StaticType::Tuple(vec![]),
|
||||||
ret: StaticType::Function(Box::new(generator_sig.clone())),
|
ret: StaticType::Function(Box::new(generator_sig.clone())),
|
||||||
},
|
},
|
||||||
Signature {
|
Signature {
|
||||||
params: StaticType::Tuple(vec![StaticType::Int]),
|
params: StaticType::Tuple(vec![StaticType::Int]),
|
||||||
ret: StaticType::Function(Box::new(generator_sig)),
|
ret: StaticType::Function(Box::new(generator_sig)),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
env.register_native_fn("make-random", make_random_ty, Purity::SideEffectFree, |args| {
|
env.register_native_fn(
|
||||||
let rng = if args.is_empty() {
|
"make-random",
|
||||||
fastrand::Rng::new()
|
make_random_ty,
|
||||||
} else if let Value::Int(s) = args[0] {
|
Purity::SideEffectFree,
|
||||||
fastrand::Rng::with_seed(s as u64)
|
|args| {
|
||||||
} else {
|
let rng = if args.is_empty() {
|
||||||
fastrand::Rng::new()
|
fastrand::Rng::new()
|
||||||
};
|
} else if let Value::Int(s) = args[0] {
|
||||||
|
fastrand::Rng::with_seed(s as u64)
|
||||||
let prng = std::rc::Rc::new(std::cell::RefCell::new(rng));
|
} else {
|
||||||
|
fastrand::Rng::new()
|
||||||
Value::Function(std::rc::Rc::new(crate::ast::types::NativeFunction {
|
};
|
||||||
func: std::rc::Rc::new(move |_| {
|
|
||||||
Value::Float(prng.borrow_mut().f64())
|
let prng = std::rc::Rc::new(std::cell::RefCell::new(rng));
|
||||||
}),
|
|
||||||
purity: Purity::Impure,
|
Value::Function(std::rc::Rc::new(crate::ast::types::NativeFunction {
|
||||||
}))
|
func: std::rc::Rc::new(move |_| Value::Float(prng.borrow_mut().f64())),
|
||||||
});
|
purity: Purity::Impure,
|
||||||
}
|
}))
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -14,4 +14,4 @@ pub fn register(env: &Environment) {
|
|||||||
math::register(env);
|
math::register(env);
|
||||||
series::register(env);
|
series::register(env);
|
||||||
streams::register(env);
|
streams::register(env);
|
||||||
}
|
}
|
||||||
|
|||||||
+558
-530
File diff suppressed because it is too large
Load Diff
+641
-593
File diff suppressed because it is too large
Load Diff
@@ -202,7 +202,12 @@ mod tests {
|
|||||||
// Check static type
|
// Check static type
|
||||||
let st = TypeRegistry::resolve_type::<Person>(®istry);
|
let st = TypeRegistry::resolve_type::<Person>(®istry);
|
||||||
if let StaticType::Record(layout) = st {
|
if let StaticType::Record(layout) = st {
|
||||||
assert!(layout.fields.iter().any(|(k, _)| *k == Keyword::intern("greet")));
|
assert!(
|
||||||
|
layout
|
||||||
|
.fields
|
||||||
|
.iter()
|
||||||
|
.any(|(k, _)| *k == Keyword::intern("greet"))
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected Record type");
|
panic!("Expected Record type");
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-19
@@ -168,11 +168,7 @@ impl RecordLayout {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let idx = self.fmap[offset];
|
let idx = self.fmap[offset];
|
||||||
if idx < 0 {
|
if idx < 0 { None } else { Some(idx as usize) }
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(idx as usize)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +176,7 @@ impl RecordLayout {
|
|||||||
pub trait Object: fmt::Debug {
|
pub trait Object: fmt::Debug {
|
||||||
fn type_name(&self) -> &'static str;
|
fn type_name(&self) -> &'static str;
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
|
|
||||||
/// Optional optimization: If this object behaves like a time series (indexable lookbacks),
|
/// Optional optimization: If this object behaves like a time series (indexable lookbacks),
|
||||||
/// it can return a reference to its Series trait implementation.
|
/// it can return a reference to its Series trait implementation.
|
||||||
fn as_series(&self) -> Option<&dyn Series> {
|
fn as_series(&self) -> Option<&dyn Series> {
|
||||||
@@ -295,11 +291,11 @@ pub enum StaticType {
|
|||||||
DateTime,
|
DateTime,
|
||||||
Text,
|
Text,
|
||||||
Keyword,
|
Keyword,
|
||||||
Optional(Box<StaticType>), // Represents T | Void (e.g. for filter pipes)
|
Optional(Box<StaticType>), // Represents T | Void (e.g. for filter pipes)
|
||||||
List(Box<StaticType>), // Legacy / Dynamic list
|
List(Box<StaticType>), // Legacy / Dynamic list
|
||||||
Series(Box<StaticType>), // Time series of a specific type
|
Series(Box<StaticType>), // Time series of a specific type
|
||||||
Tuple(Vec<StaticType>), // Heterogeneous fixed-size
|
Tuple(Vec<StaticType>), // Heterogeneous fixed-size
|
||||||
Vector(Box<StaticType>, usize), // Homogeneous fixed-size
|
Vector(Box<StaticType>, usize), // Homogeneous fixed-size
|
||||||
Matrix(Box<StaticType>, Vec<usize>), // Multi-dimensional homogeneous
|
Matrix(Box<StaticType>, Vec<usize>), // Multi-dimensional homogeneous
|
||||||
Record(std::sync::Arc<RecordLayout>),
|
Record(std::sync::Arc<RecordLayout>),
|
||||||
FieldAccessor(Keyword),
|
FieldAccessor(Keyword),
|
||||||
@@ -371,7 +367,12 @@ impl fmt::Display for StaticType {
|
|||||||
impl StaticType {
|
impl StaticType {
|
||||||
/// Returns true if `other` can be assigned to a location of type `self`.
|
/// Returns true if `other` can be assigned to a location of type `self`.
|
||||||
pub fn is_assignable_from(&self, other: &StaticType) -> bool {
|
pub fn is_assignable_from(&self, other: &StaticType) -> bool {
|
||||||
if self == other || matches!(self, StaticType::Any) || matches!(other, StaticType::Any) || matches!(self, StaticType::Error) || matches!(other, StaticType::Error) {
|
if self == other
|
||||||
|
|| matches!(self, StaticType::Any)
|
||||||
|
|| matches!(other, StaticType::Any)
|
||||||
|
|| matches!(self, StaticType::Error)
|
||||||
|
|| matches!(other, StaticType::Error)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,9 +412,7 @@ impl StaticType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Records are assignable if their layouts match (Structural identity via interning)
|
// Records are assignable if their layouts match (Structural identity via interning)
|
||||||
(StaticType::Record(a), StaticType::Record(b)) => {
|
(StaticType::Record(a), StaticType::Record(b)) => std::sync::Arc::ptr_eq(a, b),
|
||||||
std::sync::Arc::ptr_eq(a, b)
|
|
||||||
}
|
|
||||||
// Series are assignable if their inner types are assignable
|
// Series are assignable if their inner types are assignable
|
||||||
(StaticType::Series(inner_a), StaticType::Series(inner_b)) => {
|
(StaticType::Series(inner_a), StaticType::Series(inner_b)) => {
|
||||||
inner_a.is_assignable_from(inner_b)
|
inner_a.is_assignable_from(inner_b)
|
||||||
@@ -550,7 +549,7 @@ impl Value {
|
|||||||
}
|
}
|
||||||
Value::Record(layout, _) => StaticType::Record(layout.clone()),
|
Value::Record(layout, _) => StaticType::Record(layout.clone()),
|
||||||
Value::FieldAccessor(k) => StaticType::FieldAccessor(*k),
|
Value::FieldAccessor(k) => StaticType::FieldAccessor(*k),
|
||||||
Value::Function(_) => StaticType::Any, // Dynamic function
|
Value::Function(_) => StaticType::Any, // Dynamic function
|
||||||
Value::Object(o) => StaticType::Object(o.type_name()),
|
Value::Object(o) => StaticType::Object(o.type_name()),
|
||||||
Value::Cell(c) => c.borrow().static_type(),
|
Value::Cell(c) => c.borrow().static_type(),
|
||||||
Value::TailCallRequest(_) => StaticType::Any, // Internal state, but typable as Any
|
Value::TailCallRequest(_) => StaticType::Any, // Internal state, but typable as Any
|
||||||
@@ -596,10 +595,20 @@ impl fmt::Display for Value {
|
|||||||
Value::FieldAccessor(k) => write!(f, ".{}", k.name()),
|
Value::FieldAccessor(k) => write!(f, ".{}", k.name()),
|
||||||
Value::Function(f_meta) => write!(f, "<native fn, {:?}>", f_meta.purity),
|
Value::Function(f_meta) => write!(f, "<native fn, {:?}>", f_meta.purity),
|
||||||
Value::Object(o) => {
|
Value::Object(o) => {
|
||||||
if let Some(pipe) = o.as_any().downcast_ref::<crate::ast::rtl::streams::PipelineNode>() {
|
if let Some(pipe) = o
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<crate::ast::rtl::streams::PipelineNode>()
|
||||||
|
{
|
||||||
write!(f, "PipelineNode[last: {:?}]", pipe.series.get_item(0))
|
write!(f, "PipelineNode[last: {:?}]", pipe.series.get_item(0))
|
||||||
} else if let Some(val_series) = o.as_any().downcast_ref::<crate::ast::rtl::series::SharedValueSeries>() {
|
} else if let Some(val_series) =
|
||||||
write!(f, "SharedValueSeries[len: {}]", val_series.buffer.borrow().len())
|
o.as_any()
|
||||||
|
.downcast_ref::<crate::ast::rtl::series::SharedValueSeries>()
|
||||||
|
{
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"SharedValueSeries[len: {}]",
|
||||||
|
val_series.buffer.borrow().len()
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
write!(f, "<{}>", o.type_name())
|
write!(f, "<{}>", o.type_name())
|
||||||
}
|
}
|
||||||
|
|||||||
+95
-31
@@ -177,7 +177,11 @@ impl VM {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_with_args(&mut self, closure_obj: Rc<dyn Object>, args: Vec<Value>) -> Result<Value, String> {
|
pub fn run_with_args(
|
||||||
|
&mut self,
|
||||||
|
closure_obj: Rc<dyn Object>,
|
||||||
|
args: Vec<Value>,
|
||||||
|
) -> Result<Value, String> {
|
||||||
let closure = closure_obj.as_any().downcast_ref::<Closure>().unwrap();
|
let closure = closure_obj.as_any().downcast_ref::<Closure>().unwrap();
|
||||||
self.stack.clear();
|
self.stack.clear();
|
||||||
self.frames.clear();
|
self.frames.clear();
|
||||||
@@ -323,29 +327,40 @@ impl VM {
|
|||||||
|
|
||||||
// 2. Downcast! We check at runtime if the pointer actually points to a `RecordSeries`.
|
// 2. Downcast! We check at runtime if the pointer actually points to a `RecordSeries`.
|
||||||
// `downcast_ref` is very fast (essentially an O(1) type ID comparison under the hood).
|
// `downcast_ref` is very fast (essentially an O(1) type ID comparison under the hood).
|
||||||
if let Some(record_series) = any_ptr.downcast_ref::<crate::ast::rtl::series::RecordSeries>() {
|
if let Some(record_series) =
|
||||||
|
any_ptr.downcast_ref::<crate::ast::rtl::series::RecordSeries>()
|
||||||
|
{
|
||||||
// 3. We call our highly performant 0-copy method on the series.
|
// 3. We call our highly performant 0-copy method on the series.
|
||||||
// It returns an `Rc<RefCell<dyn SeriesMember>>`, which is a shared pointer
|
// It returns an `Rc<RefCell<dyn SeriesMember>>`, which is a shared pointer
|
||||||
// to the concrete column array (e.g., a `ScalarSeries<f64>`).
|
// to the concrete column array (e.g., a `ScalarSeries<f64>`).
|
||||||
if let Some(field_series) = record_series.field(*field) {
|
if let Some(field_series) = record_series.field(*field) {
|
||||||
// 4. We wrap this RefCell in our `SeriesView` struct.
|
// 4. We wrap this RefCell in our `SeriesView` struct.
|
||||||
// The `SeriesView` acts as a pure `Object` for the VM, holding the reference.
|
// The `SeriesView` acts as a pure `Object` for the VM, holding the reference.
|
||||||
// CRITICAL: No array elements are copied here! This is pure, fast pointer juggling.
|
// CRITICAL: No array elements are copied here! This is pure, fast pointer juggling.
|
||||||
// This single operation turns a SoA `RecordSeries` into a high-speed `FloatSeries` view.
|
// This single operation turns a SoA `RecordSeries` into a high-speed `FloatSeries` view.
|
||||||
let view = crate::ast::rtl::series::SeriesView::new(field_series, *field);
|
let view =
|
||||||
|
crate::ast::rtl::series::SeriesView::new(field_series, *field);
|
||||||
return Ok(Value::Object(std::rc::Rc::new(view)));
|
return Ok(Value::Object(std::rc::Rc::new(view)));
|
||||||
} else {
|
} else {
|
||||||
return Err(format!("RecordSeries does not have field :{}", field.name()));
|
return Err(format!(
|
||||||
|
"RecordSeries does not have field :{}",
|
||||||
|
field.name()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback if it's another type of object that is not a RecordSeries.
|
// Fallback if it's another type of object that is not a RecordSeries.
|
||||||
Err(format!("Attempt to access field on non-record object: {}", obj.type_name()))
|
Err(format!(
|
||||||
|
"Attempt to access field on non-record object: {}",
|
||||||
|
obj.type_name()
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error handling for primitives (Int, Float, etc.).
|
// Error handling for primitives (Int, Float, etc.).
|
||||||
_ => Err(format!("Attempt to access field on non-record: {}", rec_val)),
|
_ => Err(format!(
|
||||||
|
"Attempt to access field on non-record: {}",
|
||||||
|
rec_val
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,11 +428,8 @@ impl VM {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Delegate to the RTL Factory for specialized buffer instantiation
|
// Delegate to the RTL Factory for specialized buffer instantiation
|
||||||
let node = crate::ast::rtl::streams::build_pipeline_node(
|
let node =
|
||||||
obs_streams,
|
crate::ast::rtl::streams::build_pipeline_node(obs_streams, executor, out_type);
|
||||||
executor,
|
|
||||||
out_type,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(Value::Object(node))
|
Ok(Value::Object(node))
|
||||||
}
|
}
|
||||||
@@ -489,23 +501,44 @@ impl VM {
|
|||||||
if let Some(idx) = layout.index_of(k) {
|
if let Some(idx) = layout.index_of(k) {
|
||||||
return Ok(values[idx].clone());
|
return Ok(values[idx].clone());
|
||||||
} else {
|
} else {
|
||||||
return Err(format!("Record does not have field :{}", k.name()));
|
return Err(format!(
|
||||||
|
"Record does not have field :{}",
|
||||||
|
k.name()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
} else if let Value::Object(obj) = rec {
|
} else if let Value::Object(obj) = rec {
|
||||||
// Polymorphic Field Access: Allow `.field` on a RecordSeries
|
// Polymorphic Field Access: Allow `.field` on a RecordSeries
|
||||||
if let Some(rs) = obj.as_any().downcast_ref::<crate::ast::rtl::series::RecordSeries>() {
|
if let Some(rs) = obj
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<crate::ast::rtl::series::RecordSeries>()
|
||||||
|
{
|
||||||
if let Some(field_series) = rs.field(k) {
|
if let Some(field_series) = rs.field(k) {
|
||||||
let view = crate::ast::rtl::series::SeriesView::new(field_series, k);
|
let view = crate::ast::rtl::series::SeriesView::new(
|
||||||
|
field_series,
|
||||||
|
k,
|
||||||
|
);
|
||||||
return Ok(Value::Object(std::rc::Rc::new(view)));
|
return Ok(Value::Object(std::rc::Rc::new(view)));
|
||||||
} else {
|
} else {
|
||||||
return Err(format!("RecordSeries does not have field :{}", k.name()));
|
return Err(format!(
|
||||||
|
"RecordSeries does not have field :{}",
|
||||||
|
k.name()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Err(format!("Field accessor .{} expects a record or RecordSeries, got {}", k.name(), obj.type_name()));
|
return Err(format!(
|
||||||
|
"Field accessor .{} expects a record or RecordSeries, got {}",
|
||||||
|
k.name(),
|
||||||
|
obj.type_name()
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
return Err(format!("Field accessor .{} expects a record or RecordSeries, got {}", k.name(), rec));
|
return Err(format!(
|
||||||
|
"Field accessor .{} expects a record or RecordSeries, got {}",
|
||||||
|
k.name(),
|
||||||
|
rec
|
||||||
|
));
|
||||||
}
|
}
|
||||||
} _ => {
|
}
|
||||||
|
_ => {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Tail call target is not a function: {}",
|
"Tail call target is not a function: {}",
|
||||||
func_val
|
func_val
|
||||||
@@ -534,19 +567,37 @@ impl VM {
|
|||||||
}
|
}
|
||||||
} else if let Value::Object(obj) = rec {
|
} else if let Value::Object(obj) = rec {
|
||||||
// Polymorphic Field Access: Allow `.field` on a RecordSeries
|
// Polymorphic Field Access: Allow `.field` on a RecordSeries
|
||||||
if let Some(rs) = obj.as_any().downcast_ref::<crate::ast::rtl::series::RecordSeries>() {
|
if let Some(rs) = obj
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<crate::ast::rtl::series::RecordSeries>()
|
||||||
|
{
|
||||||
if let Some(field_series) = rs.field(k) {
|
if let Some(field_series) = rs.field(k) {
|
||||||
let view = crate::ast::rtl::series::SeriesView::new(field_series, k);
|
let view = crate::ast::rtl::series::SeriesView::new(
|
||||||
|
field_series,
|
||||||
|
k,
|
||||||
|
);
|
||||||
break Ok(Value::Object(std::rc::Rc::new(view)));
|
break Ok(Value::Object(std::rc::Rc::new(view)));
|
||||||
} else {
|
} else {
|
||||||
break Err(format!("RecordSeries does not have field :{}", k.name()));
|
break Err(format!(
|
||||||
|
"RecordSeries does not have field :{}",
|
||||||
|
k.name()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break Err(format!("Field accessor .{} expects a record or RecordSeries, got {}", k.name(), obj.type_name()));
|
break Err(format!(
|
||||||
|
"Field accessor .{} expects a record or RecordSeries, got {}",
|
||||||
|
k.name(),
|
||||||
|
obj.type_name()
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
break Err(format!("Field accessor .{} expects a record or RecordSeries, got {}", k.name(), rec));
|
break Err(format!(
|
||||||
|
"Field accessor .{} expects a record or RecordSeries, got {}",
|
||||||
|
k.name(),
|
||||||
|
rec
|
||||||
|
));
|
||||||
}
|
}
|
||||||
} Value::Object(obj) => {
|
}
|
||||||
|
Value::Object(obj) => {
|
||||||
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
|
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
|
||||||
let old_stack_top = self.stack.len();
|
let old_stack_top = self.stack.len();
|
||||||
self.frames.push(CallFrame {
|
self.frames.push(CallFrame {
|
||||||
@@ -586,11 +637,17 @@ impl VM {
|
|||||||
// Unified Series Access (Step 1 of Dual Series Architecture)
|
// Unified Series Access (Step 1 of Dual Series Architecture)
|
||||||
// This handles RecordSeries, SeriesView, and future SharedSeries polymorphically.
|
// This handles RecordSeries, SeriesView, and future SharedSeries polymorphically.
|
||||||
if arg_vals.len() != 1 {
|
if arg_vals.len() != 1 {
|
||||||
break Err(format!("{} indexer expects exactly 1 argument (the lookback index)", obj.type_name()));
|
break Err(format!(
|
||||||
|
"{} indexer expects exactly 1 argument (the lookback index)",
|
||||||
|
obj.type_name()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
if let Value::Int(idx) = arg_vals[0] {
|
if let Value::Int(idx) = arg_vals[0] {
|
||||||
if idx < 0 {
|
if idx < 0 {
|
||||||
break Err(format!("{} lookback index cannot be negative", obj.type_name()));
|
break Err(format!(
|
||||||
|
"{} lookback index cannot be negative",
|
||||||
|
obj.type_name()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
if let Some(val) = series.get_item(idx as usize) {
|
if let Some(val) = series.get_item(idx as usize) {
|
||||||
break Ok(val);
|
break Ok(val);
|
||||||
@@ -599,12 +656,16 @@ impl VM {
|
|||||||
break Ok(Value::Void);
|
break Ok(Value::Void);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
break Err(format!("{} index must be an integer", obj.type_name()));
|
break Err(format!(
|
||||||
|
"{} index must be an integer",
|
||||||
|
obj.type_name()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
break Err(format!("Object is not callable: {}", obj.type_name()));
|
break Err(format!("Object is not callable: {}", obj.type_name()));
|
||||||
}
|
}
|
||||||
} _ => break Err(format!("Attempt to call non-function: {}", func_val)),
|
}
|
||||||
|
_ => break Err(format!("Attempt to call non-function: {}", func_val)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -651,7 +712,10 @@ impl VM {
|
|||||||
for v in values {
|
for v in values {
|
||||||
evaluated_values.push(self.eval_internal(obs, v)?);
|
evaluated_values.push(self.eval_internal(obs, v)?);
|
||||||
}
|
}
|
||||||
Ok(Value::Record(layout.clone(), std::rc::Rc::new(evaluated_values)))
|
Ok(Value::Record(
|
||||||
|
layout.clone(),
|
||||||
|
std::rc::Rc::new(evaluated_values),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
BoundKind::Expansion { bound_expanded, .. } => self.eval_internal(obs, bound_expanded),
|
BoundKind::Expansion { bound_expanded, .. } => self.eval_internal(obs, bound_expanded),
|
||||||
BoundKind::Extension(ext) => Err(format!(
|
BoundKind::Extension(ext) => Err(format!(
|
||||||
|
|||||||
+6
-1
@@ -103,7 +103,12 @@ fn execute(env: &Environment, source: &str) {
|
|||||||
let result = env.compile(source);
|
let result = env.compile(source);
|
||||||
for diag in &result.diagnostics.items {
|
for diag in &result.diagnostics.items {
|
||||||
let level = format!("{:?}", diag.level).to_uppercase();
|
let level = format!("{:?}", diag.level).to_uppercase();
|
||||||
let loc = diag.identity.as_ref().and_then(|id| id.location).map(|l| format!(" at {}:{}", l.line, l.col)).unwrap_or_default();
|
let loc = diag
|
||||||
|
.identity
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|id| id.location)
|
||||||
|
.map(|l| format!(" at {}:{}", l.line, l.col))
|
||||||
|
.unwrap_or_default();
|
||||||
eprintln!("{}{} : {}", level, loc, diag.message);
|
eprintln!("{}{} : {}", level, loc, diag.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+759
-686
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user