From 80f3ad7bde1b98d11e016c38a0d194ae95482b77 Mon Sep 17 00:00:00 2001 From: GreasySlug <9619abgoni@gmail.com> Date: Fri, 30 Dec 2022 10:03:31 +0900 Subject: [PATCH 1/2] fix: disable to declare multi var in single line --- compiler/erg_parser/parse.rs | 45 ++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/compiler/erg_parser/parse.rs b/compiler/erg_parser/parse.rs index 245b45089..99b08719b 100644 --- a/compiler/erg_parser/parse.rs +++ b/compiler/erg_parser/parse.rs @@ -147,6 +147,21 @@ impl Parser { } } + fn next_line(&mut self) { + while let Some(t) = self.peek() { + match t.kind { + Newline => { + self.skip(); + return; + } + EOF => return, + _ => { + self.skip(); + } + } + } + } + fn skip_and_throw_syntax_err(&mut self, caused_by: &str) -> ParseError { let loc = self.peek().map(|t| t.loc()).unwrap_or_default(); log!(err "error caused by: {caused_by}"); @@ -154,6 +169,12 @@ impl Parser { ParseError::simple_syntax_error(0, loc) } + fn skip_and_throw_invalid_chunk_err(&mut self, caused_by: &str, loc: Location) -> ParseError { + log!(err "error caused by: {caused_by}"); + self.next_line(); + ParseError::invalid_chunk_error(line!() as usize, loc) + } + #[inline] fn restore(&mut self, token: Token) { self.tokens.insert(0, token); @@ -271,6 +292,13 @@ impl Parser { Some(_) => { if let Ok(expr) = self.try_reduce_chunk(true, false) { chunks.push(expr); + if !self.cur_is(EOF) && !self.cur_category_is(TC::Separator) { + let err = self.skip_and_throw_invalid_chunk_err( + "try_reduce_module", + chunks.last().unwrap().loc(), + ); + self.errs.push(err); + } } } None => { @@ -287,6 +315,7 @@ impl Parser { Ok(chunks) } + // expect the block`= ; . -> =>` fn try_reduce_block(&mut self) -> ParseResult { debug_call_info!(self); let mut block = Block::with_capacity(2); @@ -296,6 +325,14 @@ impl Parser { .try_reduce_chunk(true, false) .map_err(|_| self.stack_dec())?; block.push(chunk); + if !self.cur_is(Dedent) && !self.cur_category_is(TC::Separator) { + let err = self.skip_and_throw_invalid_chunk_err( + "try_reduce_block", + block.last().unwrap().loc(), + ); + self.level -= 1; + self.errs.push(err); + } if block.last().unwrap().is_definition() { let err = ParseError::simple_syntax_error(0, block.last().unwrap().loc()); self.level -= 1; @@ -344,6 +381,14 @@ impl Parser { Some(_) => { if let Ok(expr) = self.try_reduce_chunk(true, false) { block.push(expr); + if !self.cur_is(Dedent) && !self.cur_category_is(TC::Separator) { + let err = self.skip_and_throw_invalid_chunk_err( + "try_reduce_block", + block.last().unwrap().loc(), + ); + self.level -= 1; + self.errs.push(err); + } } } _ => switch_unreachable!(), From ac9020b04b1051feacff7a67f0696e5e20595ccb Mon Sep 17 00:00:00 2001 From: GreasySlug <9619abgoni@gmail.com> Date: Fri, 30 Dec 2022 10:03:57 +0900 Subject: [PATCH 2/2] test: add invalid chunk and adjust to the test's --- compiler/erg_parser/tests/invalid_chunk.er | 97 ++++++++++++++++++++++ compiler/erg_parser/tests/parse_test.rs | 63 ++++++++------ 2 files changed, 135 insertions(+), 25 deletions(-) create mode 100644 compiler/erg_parser/tests/invalid_chunk.er diff --git a/compiler/erg_parser/tests/invalid_chunk.er b/compiler/erg_parser/tests/invalid_chunk.er new file mode 100644 index 000000000..77c027d0f --- /dev/null +++ b/compiler/erg_parser/tests/invalid_chunk.er @@ -0,0 +1,97 @@ +a = 1 b = 1 +d = True {1} +e = False {"a": 2} +f = 0.1 (1) +g = {x = 1} Class {name = Str; age = Nat} + +[1, 2, 3] {1} +[1, 2, 3] {"c": 3} +[1, 2, 3] (1) +[1, 2, 3] {x = 1} + +{1, 2, 3} {1} +{1, 2, 3} {"c": 3} +{1, 2, 3} (1) +{1, 2, 3} {x = 1} + +{x = 1} {1} +{x = 1} {"c": 3} +{x = 1} (1) +{x = 1} {x = 1} + +{"a": 1, "b": 2} {1} +{"a": 1, "b": 2} {"c": 3} +{"a": 1, "b": 2} (1) +{"a": 1, "b": 2} {x = 1; y = 1} + +{1, 2, 3} g = 1 +(1, 2, 3) {1} +(1, 2, 3) {"c": 3} +(1, 2, 3) {x = 1} + +Class {name = Str; age = Nat} z = 1 + +# different error +[1, 2, 3] e = 1 +{"a": 1, "b": 2} h = 1 +{x = 1} i = 1 +(1, 2, 3) j = 1 + +h = 1 [0, 1] +[1, 2, 3] [0, 1] +{"a": 1, "b": 2} [0, 1] +{x = 1} [0, 1] +(1, 2, 3) [0, 1] + +# passed: index access, but invalid +{x = 1} [0] +(1, 2, 3) [0] +Class {name = Str; age = Nat} [0] + +# passed: as args +[2, 2, 3] Class {name = Str; age = Nat} +{1, 2, 3} Class {name = Str; age = Nat} +{"a": 1, "b": 2} Class {name = Str; age = Nat} +(1, 2, 3) Class {name = Str; age = Nat} + +Class {name = Str; age = Nat} [1, 2, 2] +Class {name = Str; age = Nat} {1, 2, 3} +Class {name = Str; age = Nat} {"c": 3, "d": 4} +Class {name = Str; age = Nat} (1, 2, 3) +Class {name = Str; age = Nat} Class {x = Int; y = Int} + +block = + a = 1 b = 1 + c = "Hello, world" [0] # is it possible to parse? + d = True {1} + e = False {"a": 2} + f = 0.1 (1) + g = {x = 1} Class {name = Str; age = Nat} + + [1, 2, 3] {1} + [1, 2, 3] {"c": 3} + [1, 2, 3] (1) + [1, 2, 3] {x = 1} + + {1, 2, 3} [1] + {1, 2, 3} {1} + {1, 2, 3} {"c": 3} + {1, 2, 3} (1) + {1, 2, 3} {x = 1} + + {x = 1} {1} + {x = 1} {"c": 3} + {x = 1} (1) + {x = 1} {x = 1} + + {"a": 1, "b": 2} {1} + {"a": 1, "b": 2} {"c": 3} + {"a": 1, "b": 2} (1) + {"a": 1, "b": 2} {x = 1; y = 1} + + {1, 2, 3} g = 1 + (1, 2, 3) {1} + (1, 2, 3) {"c": 3} + (1, 2, 3) {x = 1} + + Class {name = Str; age = Nat} z = 1 \ No newline at end of file diff --git a/compiler/erg_parser/tests/parse_test.rs b/compiler/erg_parser/tests/parse_test.rs index 599b22aff..1d315e379 100644 --- a/compiler/erg_parser/tests/parse_test.rs +++ b/compiler/erg_parser/tests/parse_test.rs @@ -1,57 +1,62 @@ use erg_common::config::{ErgConfig, Input}; use erg_common::error::MultiErrorDisplay; use erg_common::spawn::exec_new_thread; -use erg_common::traits::Runnable; +use erg_common::traits::{Runnable, Stream}; use erg_parser::error::ParserRunnerErrors; use erg_parser::lex::Lexer; use erg_parser::ParserRunner; #[test] -fn parse_str_literal() -> Result<(), ParserRunnerErrors> { - expect_failure("tests/failed_str_lit.er") -} - -#[test] -fn parse_dependent() -> Result<(), ParserRunnerErrors> { +fn parse_dependent() -> Result<(), ()> { expect_success("tests/dependent.er") } #[test] -fn parse_fib() -> Result<(), ParserRunnerErrors> { +fn parse_fib() -> Result<(), ()> { expect_success("tests/fib.er") } #[test] -fn parse_hello_world() -> Result<(), ParserRunnerErrors> { +fn parse_hello_world() -> Result<(), ()> { expect_success("tests/hello_world.er") } #[test] -fn parse_simple_if() -> Result<(), ParserRunnerErrors> { +fn parse_simple_if() -> Result<(), ()> { expect_success("tests/simple_if.er") } #[test] -fn parse_stack() -> Result<(), ParserRunnerErrors> { - expect_failure("tests/stack.er") -} - -#[test] -fn parse_stream() -> Result<(), ParserRunnerErrors> { +fn parse_stream() -> Result<(), ()> { expect_success("tests/stream.er") } #[test] -fn parse_test1_basic_syntax() -> Result<(), ParserRunnerErrors> { +fn parse_test1_basic_syntax() -> Result<(), ()> { expect_success("tests/test1_basic_syntax.er") } #[test] -fn parse_test2_advanced_syntax() -> Result<(), ParserRunnerErrors> { +fn parse_test2_advanced_syntax() -> Result<(), ()> { expect_success("tests/test2_advanced_syntax.er") } +#[test] +fn parse_stack() -> Result<(), ()> { + expect_failure("tests/stack.er", 2) +} + +#[test] +fn parse_str_literal() -> Result<(), ()> { + expect_failure("tests/failed_str_lit.er", 2) +} + +#[test] +fn exec_invalid_chunk_prs_err() -> Result<(), ()> { + expect_failure("tests/invalid_chunk.er", 62) +} + fn _parse_test_from_code(file_path: &'static str) -> Result<(), ParserRunnerErrors> { let input = Input::File(file_path.into()); let cfg = ErgConfig { @@ -81,19 +86,27 @@ fn parse_test_from_code(file_path: &'static str) -> Result<(), ParserRunnerError exec_new_thread(move || _parse_test_from_code(file_path)) } -fn expect_success(file_path: &'static str) -> Result<(), ParserRunnerErrors> { +fn expect_success(file_path: &'static str) -> Result<(), ()> { match parse_test_from_code(file_path) { Ok(_) => Ok(()), - Err(e) => { - e.fmt_all_stderr(); - Err(e) + Err(errs) => { + errs.fmt_all_stderr(); + Err(()) } } } -fn expect_failure(file_path: &'static str) -> Result<(), ParserRunnerErrors> { +fn expect_failure(file_path: &'static str, errs_len: usize) -> Result<(), ()> { match parse_test_from_code(file_path) { - Ok(_) => Err(ParserRunnerErrors::empty()), - Err(_) => Ok(()), + Ok(_) => Err(()), + Err(errs) => { + errs.fmt_all_stderr(); + if errs.len() == errs_len { + Ok(()) + } else { + println!("err: error length is not {errs_len} but {}", errs.len()); + Err(()) + } + } } }