def predecl

This commit is contained in:
JT 2021-07-31 16:04:42 +12:00
parent 61258d03ad
commit c2be740ad4
3 changed files with 69 additions and 10 deletions

View File

@ -2090,6 +2090,33 @@ impl<'a> ParserWorkingSet<'a> {
}
}
pub fn parse_def_predecl(&mut self, spans: &[Span]) {
let name = self.get_span_contents(spans[0]);
if name == b"def" && spans.len() >= 4 {
//FIXME: don't use expect here
let (name_expr, ..) = self.parse_string(spans[1]);
let name = name_expr
.as_string()
.expect("internal error: expected def name");
self.enter_scope();
let (sig, ..) = self.parse_signature(spans[2]);
let mut signature = sig
.as_signature()
.expect("internal error: expected param list");
self.exit_scope();
signature.name = name;
let decl = Declaration {
signature,
body: None,
};
self.add_decl(decl);
}
}
pub fn parse_def(&mut self, spans: &[Span]) -> (Statement, Option<ParseError>) {
let mut error = None;
let name = self.get_span_contents(spans[0]);
@ -2102,11 +2129,16 @@ impl<'a> ParserWorkingSet<'a> {
.expect("internal error: expected def name");
error = error.or(err);
let decl_id = self
.find_decl(name.as_bytes())
.expect("internal error: predeclaration failed to add definition");
self.enter_scope();
let (sig, err) = self.parse_signature(spans[2]);
let mut signature = sig
.as_signature()
.expect("internal error: expected param list");
signature.name = name;
error = error.or(err);
let (block, err) = self.parse_block_expression(spans[3]);
@ -2115,13 +2147,10 @@ impl<'a> ParserWorkingSet<'a> {
let block_id = block.as_block().expect("internal error: expected block");
error = error.or(err);
signature.name = name;
let decl = Declaration {
signature,
body: Some(block_id),
};
let declaration = self.get_decl_mut(decl_id);
declaration.signature = signature;
declaration.body = Some(block_id);
self.add_decl(decl);
let def_decl_id = self
.find_decl(b"def")
.expect("internal error: missing def command");
@ -2211,6 +2240,14 @@ impl<'a> ParserWorkingSet<'a> {
let mut block = Block::new();
// Pre-declare any definition so that definitions
// that share the same block can see each other
for pipeline in &lite_block.block {
if pipeline.commands.len() == 1 {
self.parse_def_predecl(&pipeline.commands[0].parts);
}
}
for pipeline in &lite_block.block {
if pipeline.commands.len() > 1 {
let mut output = vec![];

View File

@ -306,6 +306,11 @@ impl<'a> ParserWorkingSet<'a> {
None
}
pub fn update_decl(&mut self, decl_id: usize, block: Option<BlockId>) {
let decl = self.get_decl_mut(decl_id);
decl.body = block;
}
pub fn contains_decl_partial_match(&self, name: &[u8]) -> bool {
for scope in self.delta.scope.iter().rev() {
for decl in &scope.decls {
@ -401,6 +406,18 @@ impl<'a> ParserWorkingSet<'a> {
}
}
pub fn get_decl_mut(&mut self, decl_id: DeclId) -> &mut Declaration {
let num_permanent_decls = self.permanent_state.num_decls();
if decl_id < num_permanent_decls {
panic!("internal error: can only mutate declarations in working set")
} else {
self.delta
.decls
.get_mut(decl_id - num_permanent_decls)
.expect("internal error: missing declaration")
}
}
pub fn get_block(&self, block_id: BlockId) -> &Block {
let num_permanent_blocks = self.permanent_state.num_blocks();
if block_id < num_permanent_blocks {

View File

@ -78,7 +78,7 @@ fn if_test2() -> TestResult {
}
#[test]
fn no_leak1() -> TestResult {
fn no_scope_leak1() -> TestResult {
fail_test(
"if $false { let $x = 10 } else { let $x = 20 }; $x",
"VariableNotFound",
@ -86,7 +86,7 @@ fn no_leak1() -> TestResult {
}
#[test]
fn no_leak2() -> TestResult {
fn no_scope_leak2() -> TestResult {
fail_test(
"def foo [] { $x }; def bar [] { let $x = 10; foo }; bar",
"VariableNotFound",
@ -94,7 +94,7 @@ fn no_leak2() -> TestResult {
}
#[test]
fn no_leak3() -> TestResult {
fn no_scope_leak3() -> TestResult {
run_test(
"def foo [$x] { $x }; def bar [] { let $x = 10; foo 20}; bar",
"20",
@ -102,7 +102,7 @@ fn no_leak3() -> TestResult {
}
#[test]
fn no_leak4() -> TestResult {
fn no_scope_leak4() -> TestResult {
run_test(
"def foo [$x] { $x }; def bar [] { let $x = 10; (foo 20) + $x}; bar",
"30",
@ -113,3 +113,8 @@ fn no_leak4() -> TestResult {
fn simple_var_closing() -> TestResult {
run_test("let $x = 10; def foo [] { $x }; foo", "10")
}
#[test]
fn predecl_check() -> TestResult {
run_test("def bob [] { sam }; def sam [] { 3 }; bob", "3")
}