Skip to content

Commit

Permalink
Merge pull request #291 from GreasySlug/fix/#281
Browse files Browse the repository at this point in the history
Fixed an issue where multiple chunks can be declared on a single line
  • Loading branch information
GreasySlug authored Dec 30, 2022
2 parents b78e677 + ac9020b commit 73e4561
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 25 deletions.
45 changes: 45 additions & 0 deletions compiler/erg_parser/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,34 @@ 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}");
self.next_expr();
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);
Expand Down Expand Up @@ -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 => {
Expand All @@ -287,6 +315,7 @@ impl Parser {
Ok(chunks)
}

// expect the block`= ; . -> =>`
fn try_reduce_block(&mut self) -> ParseResult<Block> {
debug_call_info!(self);
let mut block = Block::with_capacity(2);
Expand All @@ -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;
Expand Down Expand Up @@ -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!(),
Expand Down
97 changes: 97 additions & 0 deletions compiler/erg_parser/tests/invalid_chunk.er
Original file line number Diff line number Diff line change
@@ -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
63 changes: 38 additions & 25 deletions compiler/erg_parser/tests/parse_test.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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(())
}
}
}
}

0 comments on commit 73e4561

Please sign in to comment.