refactor(tvix/eval): add compiler accessor for current scope
Change-Id: I7488087d95c1b3fb7f70fc29af0d5b0d0a25a428 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6245 Tested-by: BuildkiteCI Reviewed-by: grfn <grfn@gws.fyi>
This commit is contained in:
parent
a060c8f467
commit
9d1451773b
1 changed files with 42 additions and 38 deletions
|
@ -33,7 +33,7 @@ pub struct CompilationResult {
|
||||||
pub errors: Vec<Error>,
|
pub errors: Vec<Error>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Represents a single local already known to the compiler.
|
/// Represents a single local already known to the compiler.
|
||||||
struct Local {
|
struct Local {
|
||||||
// Definition name, which can be different kinds of tokens (plain
|
// Definition name, which can be different kinds of tokens (plain
|
||||||
// string or identifier). Nix does not allow dynamic names inside
|
// string or identifier). Nix does not allow dynamic names inside
|
||||||
|
@ -106,6 +106,14 @@ impl Compiler {
|
||||||
.expect("compiler flaw: long-lived chunk reference")
|
.expect("compiler flaw: long-lived chunk reference")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn scope(&self) -> &Scope {
|
||||||
|
&self.scope
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scope_mut(&mut self) -> &mut Scope {
|
||||||
|
&mut self.scope
|
||||||
|
}
|
||||||
|
|
||||||
fn emit_constant(&mut self, value: Value) {
|
fn emit_constant(&mut self, value: Value) {
|
||||||
let idx = self.chunk().push_constant(value);
|
let idx = self.chunk().push_constant(value);
|
||||||
self.chunk().push_op(OpCode::OpConstant(idx));
|
self.chunk().push_op(OpCode::OpConstant(idx));
|
||||||
|
@ -658,9 +666,9 @@ impl Compiler {
|
||||||
// optimised information about any "weird" stuff that's
|
// optimised information about any "weird" stuff that's
|
||||||
// happened to the scope (such as overrides of these
|
// happened to the scope (such as overrides of these
|
||||||
// literals, or builtins).
|
// literals, or builtins).
|
||||||
"true" if self.scope.poisoned_true == 0 => self.chunk().push_op(OpCode::OpTrue),
|
"true" if self.scope().poisoned_true == 0 => self.chunk().push_op(OpCode::OpTrue),
|
||||||
"false" if self.scope.poisoned_false == 0 => self.chunk().push_op(OpCode::OpFalse),
|
"false" if self.scope().poisoned_false == 0 => self.chunk().push_op(OpCode::OpFalse),
|
||||||
"null" if self.scope.poisoned_null == 0 => self.chunk().push_op(OpCode::OpNull),
|
"null" if self.scope().poisoned_null == 0 => self.chunk().push_op(OpCode::OpNull),
|
||||||
|
|
||||||
name => {
|
name => {
|
||||||
// Note: `with` and some other special scoping
|
// Note: `with` and some other special scoping
|
||||||
|
@ -668,7 +676,7 @@ impl Compiler {
|
||||||
match self.resolve_local(name) {
|
match self.resolve_local(name) {
|
||||||
Some(idx) => self.chunk().push_op(OpCode::OpGetLocal(idx)),
|
Some(idx) => self.chunk().push_op(OpCode::OpGetLocal(idx)),
|
||||||
None => {
|
None => {
|
||||||
if self.scope.with_stack.is_empty() {
|
if self.scope().with_stack.is_empty() {
|
||||||
self.emit_error(
|
self.emit_error(
|
||||||
node.syntax().clone(),
|
node.syntax().clone(),
|
||||||
ErrorKind::UnknownStaticVariable,
|
ErrorKind::UnknownStaticVariable,
|
||||||
|
@ -696,11 +704,10 @@ impl Compiler {
|
||||||
self.compile(node.namespace().unwrap());
|
self.compile(node.namespace().unwrap());
|
||||||
|
|
||||||
self.declare_phantom();
|
self.declare_phantom();
|
||||||
self.scope.with_stack.push(With {
|
let depth = self.scope().scope_depth;
|
||||||
depth: self.scope.scope_depth,
|
self.scope_mut().with_stack.push(With { depth });
|
||||||
});
|
|
||||||
|
|
||||||
let with_idx = self.scope.locals.len() - 1;
|
let with_idx = self.scope().locals.len() - 1;
|
||||||
self.chunk().push_op(OpCode::OpPushWith(with_idx));
|
self.chunk().push_op(OpCode::OpPushWith(with_idx));
|
||||||
|
|
||||||
self.compile(node.body().unwrap());
|
self.compile(node.body().unwrap());
|
||||||
|
@ -729,25 +736,25 @@ impl Compiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn begin_scope(&mut self) {
|
fn begin_scope(&mut self) {
|
||||||
self.scope.scope_depth += 1;
|
self.scope_mut().scope_depth += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end_scope(&mut self) {
|
fn end_scope(&mut self) {
|
||||||
debug_assert!(self.scope.scope_depth != 0, "can not end top scope");
|
debug_assert!(self.scope().scope_depth != 0, "can not end top scope");
|
||||||
|
|
||||||
// If this scope poisoned any builtins or special identifiers,
|
// If this scope poisoned any builtins or special identifiers,
|
||||||
// they need to be reset.
|
// they need to be reset.
|
||||||
if self.scope.poisoned_true == self.scope.scope_depth {
|
if self.scope().poisoned_true == self.scope().scope_depth {
|
||||||
self.scope.poisoned_true = 0;
|
self.scope_mut().poisoned_true = 0;
|
||||||
}
|
}
|
||||||
if self.scope.poisoned_false == self.scope.scope_depth {
|
if self.scope().poisoned_false == self.scope().scope_depth {
|
||||||
self.scope.poisoned_false = 0;
|
self.scope_mut().poisoned_false = 0;
|
||||||
}
|
}
|
||||||
if self.scope.poisoned_null == self.scope.scope_depth {
|
if self.scope().poisoned_null == self.scope().scope_depth {
|
||||||
self.scope.poisoned_null = 0;
|
self.scope_mut().poisoned_null = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.scope.scope_depth -= 1;
|
self.scope_mut().scope_depth -= 1;
|
||||||
|
|
||||||
// When ending a scope, all corresponding locals need to be
|
// When ending a scope, all corresponding locals need to be
|
||||||
// removed, but the value of the body needs to remain on the
|
// removed, but the value of the body needs to remain on the
|
||||||
|
@ -756,8 +763,8 @@ impl Compiler {
|
||||||
|
|
||||||
// TL;DR - iterate from the back while things belonging to the
|
// TL;DR - iterate from the back while things belonging to the
|
||||||
// ended scope still exist.
|
// ended scope still exist.
|
||||||
while !self.scope.locals.is_empty()
|
while !self.scope().locals.is_empty()
|
||||||
&& self.scope.locals[self.scope.locals.len() - 1].depth > self.scope.scope_depth
|
&& self.scope().locals[self.scope().locals.len() - 1].depth > self.scope().scope_depth
|
||||||
{
|
{
|
||||||
pops += 1;
|
pops += 1;
|
||||||
|
|
||||||
|
@ -768,7 +775,7 @@ impl Compiler {
|
||||||
node: Some(node),
|
node: Some(node),
|
||||||
used,
|
used,
|
||||||
..
|
..
|
||||||
}) = self.scope.locals.pop()
|
}) = self.scope_mut().locals.pop()
|
||||||
{
|
{
|
||||||
if !used {
|
if !used {
|
||||||
self.emit_warning(node, WarningKind::UnusedBinding);
|
self.emit_warning(node, WarningKind::UnusedBinding);
|
||||||
|
@ -780,11 +787,12 @@ impl Compiler {
|
||||||
self.chunk().push_op(OpCode::OpCloseScope(pops));
|
self.chunk().push_op(OpCode::OpCloseScope(pops));
|
||||||
}
|
}
|
||||||
|
|
||||||
while !self.scope.with_stack.is_empty()
|
while !self.scope().with_stack.is_empty()
|
||||||
&& self.scope.with_stack[self.scope.with_stack.len() - 1].depth > self.scope.scope_depth
|
&& self.scope().with_stack[self.scope().with_stack.len() - 1].depth
|
||||||
|
> self.scope().scope_depth
|
||||||
{
|
{
|
||||||
self.chunk().push_op(OpCode::OpPopWith);
|
self.chunk().push_op(OpCode::OpPopWith);
|
||||||
self.scope.with_stack.pop();
|
self.scope_mut().with_stack.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -794,43 +802,39 @@ impl Compiler {
|
||||||
fn declare_local<S: Into<String>>(&mut self, node: rnix::SyntaxNode, name: S) {
|
fn declare_local<S: Into<String>>(&mut self, node: rnix::SyntaxNode, name: S) {
|
||||||
// Set up scope poisoning if required.
|
// Set up scope poisoning if required.
|
||||||
let name = name.into();
|
let name = name.into();
|
||||||
|
let mut scope = self.scope_mut();
|
||||||
match name.as_str() {
|
match name.as_str() {
|
||||||
"true" if self.scope.poisoned_true == 0 => {
|
"true" if scope.poisoned_true == 0 => scope.poisoned_true = scope.scope_depth,
|
||||||
self.scope.poisoned_true = self.scope.scope_depth
|
|
||||||
}
|
|
||||||
|
|
||||||
"false" if self.scope.poisoned_false == 0 => {
|
"false" if scope.poisoned_false == 0 => scope.poisoned_false = scope.scope_depth,
|
||||||
self.scope.poisoned_false = self.scope.scope_depth
|
|
||||||
}
|
|
||||||
|
|
||||||
"null" if self.scope.poisoned_null == 0 => {
|
"null" if scope.poisoned_null == 0 => scope.poisoned_null = scope.scope_depth,
|
||||||
self.scope.poisoned_null = self.scope.scope_depth
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.scope.locals.push(Local {
|
scope.locals.push(Local {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
node: Some(node),
|
node: Some(node),
|
||||||
depth: self.scope.scope_depth,
|
depth: scope.scope_depth,
|
||||||
phantom: false,
|
phantom: false,
|
||||||
used: false,
|
used: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_phantom(&mut self) {
|
fn declare_phantom(&mut self) {
|
||||||
self.scope.locals.push(Local {
|
let depth = self.scope().scope_depth;
|
||||||
|
self.scope_mut().locals.push(Local {
|
||||||
|
depth,
|
||||||
name: "".into(),
|
name: "".into(),
|
||||||
node: None,
|
node: None,
|
||||||
depth: self.scope.scope_depth,
|
|
||||||
phantom: true,
|
phantom: true,
|
||||||
used: true,
|
used: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_local(&mut self, name: &str) -> Option<usize> {
|
fn resolve_local(&mut self, name: &str) -> Option<usize> {
|
||||||
let scope = &mut self.scope;
|
let scope = self.scope_mut();
|
||||||
|
|
||||||
for (idx, local) in scope.locals.iter_mut().enumerate().rev() {
|
for (idx, local) in scope.locals.iter_mut().enumerate().rev() {
|
||||||
if !local.phantom && local.name == name {
|
if !local.phantom && local.name == name {
|
||||||
|
|
Loading…
Reference in a new issue