mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
feat!: Introduce structured destructuring plans for bindings (#485)
- add a dedicated `compiler/destructuring_planner` feature that precomputes binding plans for assignments, parameters, and `some in` expressions - enrich `ScopeContext` with same-scope tracking, local scheduling hints, and module globals so the planner enforces := shadowing rules without blocking parent scopes - wire the planner through compiler, hoist, interpreter, and engine paths while updating binding plan variants and adding query traversal helpers for dependency analysis - document the new planner architecture and ship interpreter regressions that exercise nested destructuring, shadowing, and error reporting Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
25a7ddad0a
commit
1e4ff952e6
+41
-7
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::ast::{Expr, Ref};
|
||||
use crate::builtins;
|
||||
use crate::builtins::utils::{ensure_args_count, ensure_numeric};
|
||||
use crate::builtins::utils::{ensure_args_count, ensure_numeric, validate_integer_arg};
|
||||
|
||||
use crate::lexer::Span;
|
||||
use crate::value::Value;
|
||||
@@ -19,77 +19,111 @@ pub fn register(m: &mut builtins::BuiltinsMap<&'static str, builtins::BuiltinFcn
|
||||
m.insert("bits.xor", (xor, 2));
|
||||
}
|
||||
|
||||
fn and(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
fn and(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
|
||||
let name = "bits.and";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
let v1 = ensure_numeric(name, ¶ms[0], &args[0])?;
|
||||
let v2 = ensure_numeric(name, ¶ms[1], &args[1])?;
|
||||
|
||||
if !validate_integer_arg(name, ¶ms[0], &args[0], &v1, strict, true)?
|
||||
|| !validate_integer_arg(name, ¶ms[1], &args[1], &v2, strict, true)?
|
||||
{
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
Ok(match v1.and(&v2) {
|
||||
Some(v) => Value::from(v),
|
||||
_ => Value::Undefined,
|
||||
})
|
||||
}
|
||||
|
||||
fn lsh(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
fn lsh(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
|
||||
let name = "bits.lsh";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
let v1 = ensure_numeric(name, ¶ms[0], &args[0])?;
|
||||
let v2 = ensure_numeric(name, ¶ms[1], &args[1])?;
|
||||
|
||||
if !validate_integer_arg(name, ¶ms[0], &args[0], &v1, strict, true)?
|
||||
|| !validate_integer_arg(name, ¶ms[1], &args[1], &v2, strict, false)?
|
||||
{
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
Ok(match v1.lsh(&v2) {
|
||||
Some(v) => Value::from(v),
|
||||
_ => Value::Undefined,
|
||||
})
|
||||
}
|
||||
|
||||
fn negate(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
fn negate(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
|
||||
let name = "bits.negate";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
|
||||
let v = ensure_numeric(name, ¶ms[0], &args[0])?;
|
||||
|
||||
if !validate_integer_arg(name, ¶ms[0], &args[0], &v, strict, true)? {
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
Ok(match v.neg() {
|
||||
Some(v) => Value::from(v),
|
||||
_ => Value::Undefined,
|
||||
})
|
||||
}
|
||||
|
||||
fn or(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
fn or(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
|
||||
let name = "bits.or";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
let v1 = ensure_numeric(name, ¶ms[0], &args[0])?;
|
||||
let v2 = ensure_numeric(name, ¶ms[1], &args[1])?;
|
||||
|
||||
if !validate_integer_arg(name, ¶ms[0], &args[0], &v1, strict, true)?
|
||||
|| !validate_integer_arg(name, ¶ms[1], &args[1], &v2, strict, true)?
|
||||
{
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
Ok(match v1.or(&v2) {
|
||||
Some(v) => Value::from(v),
|
||||
_ => Value::Undefined,
|
||||
})
|
||||
}
|
||||
|
||||
fn rsh(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
fn rsh(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
|
||||
let name = "bits.rsh";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
let v1 = ensure_numeric(name, ¶ms[0], &args[0])?;
|
||||
let v2 = ensure_numeric(name, ¶ms[1], &args[1])?;
|
||||
|
||||
if !validate_integer_arg(name, ¶ms[0], &args[0], &v1, strict, true)?
|
||||
|| !validate_integer_arg(name, ¶ms[1], &args[1], &v2, strict, false)?
|
||||
{
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
Ok(match v1.rsh(&v2) {
|
||||
Some(v) => Value::from(v),
|
||||
_ => Value::Undefined,
|
||||
})
|
||||
}
|
||||
|
||||
fn xor(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
fn xor(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
|
||||
let name = "bits.xor";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
let v1 = ensure_numeric(name, ¶ms[0], &args[0])?;
|
||||
let v2 = ensure_numeric(name, ¶ms[1], &args[1])?;
|
||||
|
||||
if !validate_integer_arg(name, ¶ms[0], &args[0], &v1, strict, true)?
|
||||
|| !validate_integer_arg(name, ¶ms[1], &args[1], &v2, strict, true)?
|
||||
{
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
Ok(match v1.xor(&v2) {
|
||||
Some(v) => Value::from(v),
|
||||
_ => Value::Undefined,
|
||||
|
||||
@@ -45,6 +45,50 @@ pub fn ensure_numeric(fcn: &str, arg: &Expr, v: &Value) -> Result<Number> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn validate_integer_arg(
|
||||
fcn: &str,
|
||||
param: &Ref<Expr>,
|
||||
original_value: &Value,
|
||||
numeric_value: &Number,
|
||||
strict: bool,
|
||||
allow_negative: bool,
|
||||
) -> Result<bool> {
|
||||
if !numeric_value.is_integer() {
|
||||
if strict {
|
||||
bail!(param.span().error(
|
||||
format!("`{fcn}` expects integer arguments. Got `{original_value}`").as_str()
|
||||
));
|
||||
}
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if !allow_negative {
|
||||
if let Some(int_value) = numeric_value.as_i128() {
|
||||
if int_value < 0 {
|
||||
if strict {
|
||||
bail!(param.span().error(
|
||||
format!("`{fcn}` expects non-negative integer arguments. Got `{original_value}`")
|
||||
.as_str(),
|
||||
));
|
||||
}
|
||||
return Ok(false);
|
||||
}
|
||||
} else if !numeric_value.is_positive() {
|
||||
if strict {
|
||||
bail!(param.span().error(
|
||||
format!(
|
||||
"`{fcn}` expects non-negative integer arguments. Got `{original_value}`"
|
||||
)
|
||||
.as_str(),
|
||||
));
|
||||
}
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn ensure_string(fcn: &str, arg: &Expr, v: &Value) -> Result<Rc<str>> {
|
||||
Ok(match &v {
|
||||
Value::String(s) => s.clone(),
|
||||
|
||||
Reference in New Issue
Block a user