Merge branch 'main' into pr/320

This commit is contained in:
Shunsuke Shibayama 2023-06-09 22:04:44 +09:00
commit 924b22a171
91 changed files with 1836 additions and 1027 deletions

View file

@ -1004,6 +1004,15 @@ impl_nested_display_for_enum!(RecordAttrOrIdent; Attr, Ident);
impl_display_for_enum!(RecordAttrOrIdent; Attr, Ident);
impl_locational_for_enum!(RecordAttrOrIdent; Attr, Ident);
impl RecordAttrOrIdent {
pub fn ident(&self) -> Option<&Identifier> {
match self {
Self::Attr(attr) => attr.sig.ident(),
Self::Ident(ident) => Some(ident),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct NormalSet {
pub l_brace: Token,

View file

@ -4,7 +4,7 @@ use erg_common::Str;
use crate::ast::AST;
use crate::desugar::Desugarer;
use crate::error::{ParserRunnerError, ParserRunnerErrors};
use crate::error::{CompleteArtifact, IncompleteArtifact, ParserRunnerError, ParserRunnerErrors};
use crate::parse::ParserRunner;
/// Summarize parsing and desugaring
@ -45,31 +45,55 @@ impl Runnable for ASTBuilder {
fn exec(&mut self) -> Result<ExitStatus, Self::Errs> {
let src = self.cfg_mut().input.read();
let ast = self.build(src)?;
println!("{ast}");
let artifact = self.build(src).map_err(|iart| iart.errors)?;
println!("{}", artifact.ast);
Ok(ExitStatus::OK)
}
fn eval(&mut self, src: String) -> Result<String, ParserRunnerErrors> {
let ast = self.build(src)?;
Ok(format!("{ast}"))
let artifact = self.build(src).map_err(|iart| iart.errors)?;
Ok(format!("{}", artifact.ast))
}
}
impl ASTBuilder {
pub fn build(&mut self, src: String) -> Result<AST, ParserRunnerErrors> {
let module = self.runner.parse(src)?;
pub fn build(
&mut self,
src: String,
) -> Result<
CompleteArtifact<AST, ParserRunnerErrors>,
IncompleteArtifact<AST, ParserRunnerErrors>,
> {
let name = Str::rc(self.runner.cfg().input.unescaped_filename());
let artifact = self
.runner
.parse(src)
.map_err(|iart| iart.map_mod(|module| AST::new(name.clone(), module)))?;
let mut desugarer = Desugarer::new();
let module = desugarer.desugar(module);
let name = self.runner.cfg().input.unescaped_filename();
let ast = AST::new(Str::rc(name), module);
Ok(ast)
let module = desugarer.desugar(artifact.ast);
let ast = AST::new(name, module);
Ok(CompleteArtifact::new(
ast,
ParserRunnerErrors::convert(self.input(), artifact.warns),
))
}
pub fn build_without_desugaring(&mut self, src: String) -> Result<AST, ParserRunnerErrors> {
let module = self.runner.parse(src)?;
let name = self.runner.cfg().input.unescaped_filename();
let ast = AST::new(Str::rc(name), module);
Ok(ast)
pub fn build_without_desugaring(
&mut self,
src: String,
) -> Result<
CompleteArtifact<AST, ParserRunnerErrors>,
IncompleteArtifact<AST, ParserRunnerErrors>,
> {
let name = Str::rc(self.runner.cfg().input.unescaped_filename());
let artifact = self
.runner
.parse(src)
.map_err(|iart| iart.map_mod(|module| AST::new(name.clone(), module)))?;
let ast = AST::new(name, artifact.ast);
Ok(CompleteArtifact::new(
ast,
ParserRunnerErrors::convert(self.input(), artifact.warns),
))
}
}

View file

@ -3,14 +3,15 @@
//! パーサーが出すエラーを定義
use std::fmt;
use erg_common::config::Input;
use erg_common::error::{
ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay, SubMessage,
};
use erg_common::style::{Attribute, Color, StyledStr, StyledStrings, THEME};
use erg_common::io::Input;
use erg_common::style::{Attribute, Color, StyledStr, StyledString, StyledStrings, THEME};
use erg_common::traits::Stream;
use erg_common::{fmt_iter, fmt_vec_split_with, impl_display_and_error, impl_stream, switch_lang};
use crate::ast::Module;
use crate::token::TokenKind;
#[derive(Debug)]
@ -50,6 +51,7 @@ impl fmt::Display for LexErrors {
impl std::error::Error for LexErrors {}
const ERR: Color = THEME.colors.error;
const WARN: Color = THEME.colors.warning;
const HINT: Color = THEME.colors.hint;
#[cfg(not(feature = "pretty"))]
const ATTR: Attribute = Attribute::Bold;
@ -534,12 +536,30 @@ impl LexError {
);
Self::syntax_error(errno, loc, msg, None)
}
pub fn duplicate_elem_warning(errno: usize, loc: Location, elem: String) -> Self {
let elem = StyledString::new(elem, Some(WARN), Some(Attribute::Underline));
Self::new(ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => format!("重複する要素です: {elem}"),
"simplified_chinese" => format!("{elem}"),
"traditional_chinese" => format!("{elem}"),
"english" => format!("duplicated element: {elem}"),
),
errno,
SyntaxWarning,
loc,
))
}
}
pub type LexResult<T> = Result<T, LexError>;
pub type ParseError = LexError;
pub type ParseErrors = LexErrors;
pub type ParseWarning = LexError;
pub type ParseWarnings = LexErrors;
pub type ParseResult<T> = Result<T, ()>;
#[derive(Debug)]
@ -618,4 +638,71 @@ pub type ParserRunnerResult<T> = Result<T, ParserRunnerError>;
pub type LexerRunnerError = ParserRunnerError;
pub type LexerRunnerErrors = ParserRunnerErrors;
pub type ParserRunnerWarning = ParserRunnerError;
pub type ParserRunnerWarnings = ParserRunnerErrors;
pub type LexerRunnerResult<T> = Result<T, LexerRunnerError>;
#[derive(Debug)]
pub struct CompleteArtifact<A = Module, Es = ParseErrors> {
pub ast: A,
pub warns: Es,
}
impl<A, Es> CompleteArtifact<A, Es> {
pub fn new(ast: A, warns: Es) -> Self {
Self { ast, warns }
}
}
#[derive(Debug)]
pub struct IncompleteArtifact<A = Module, Es = ParseErrors> {
pub ast: Option<A>,
pub warns: Es,
pub errors: Es,
}
impl<A> From<ParserRunnerErrors> for IncompleteArtifact<A, ParserRunnerErrors> {
fn from(value: ParserRunnerErrors) -> IncompleteArtifact<A, ParserRunnerErrors> {
IncompleteArtifact::new(None, ParserRunnerErrors::empty(), value)
}
}
impl<A> From<LexErrors> for IncompleteArtifact<A, ParseErrors> {
fn from(value: LexErrors) -> IncompleteArtifact<A, ParseErrors> {
IncompleteArtifact::new(None, ParseErrors::empty(), value)
}
}
impl<A, Es> IncompleteArtifact<A, Es> {
pub fn new(ast: Option<A>, warns: Es, errors: Es) -> Self {
Self { ast, warns, errors }
}
pub fn map_errs<U>(self, f: impl Fn(Es) -> U) -> IncompleteArtifact<A, U> {
IncompleteArtifact {
ast: self.ast,
warns: f(self.warns),
errors: f(self.errors),
}
}
pub fn map_mod<U>(self, f: impl Fn(A) -> U) -> IncompleteArtifact<U, Es> {
IncompleteArtifact {
ast: self.ast.map(f),
warns: self.warns,
errors: self.errors,
}
}
}
#[derive(Debug)]
pub struct ErrorArtifact<Es = ParseErrors> {
pub warns: Es,
pub errors: Es,
}
impl<Es> ErrorArtifact<Es> {
pub fn new(warns: Es, errors: Es) -> ErrorArtifact<Es> {
Self { warns, errors }
}
}

View file

@ -6,7 +6,7 @@ use unicode_xid::UnicodeXID;
use erg_common::cache::CacheSet;
use erg_common::config::ErgConfig;
use erg_common::config::Input;
use erg_common::io::Input;
use erg_common::traits::DequeStream;
use erg_common::traits::{Locational, Runnable, Stream};
use erg_common::{debug_power_assert, fn_name_full, normalize_newline, switch_lang};

View file

@ -5,8 +5,8 @@
use std::mem;
use erg_common::config::ErgConfig;
use erg_common::config::{Input, InputKind};
use erg_common::error::Location;
use erg_common::io::{Input, InputKind};
use erg_common::set::Set as HashSet;
use erg_common::str::Str;
use erg_common::traits::{DequeStream, ExitStatus, Locational, Runnable, Stream};
@ -17,7 +17,10 @@ use erg_common::{
use crate::ast::*;
use crate::desugar::Desugarer;
use crate::error::{ParseError, ParseErrors, ParseResult, ParserRunnerError, ParserRunnerErrors};
use crate::error::{
CompleteArtifact, IncompleteArtifact, ParseError, ParseErrors, ParseResult, ParserRunnerError,
ParserRunnerErrors,
};
use crate::lex::Lexer;
use crate::token::{Token, TokenCategory, TokenKind, TokenStream};
@ -96,13 +99,13 @@ macro_rules! expect_pop {
}
pub trait Parsable {
fn parse(code: String) -> Result<Module, ParseErrors>;
fn parse(code: String) -> Result<CompleteArtifact, IncompleteArtifact<Module, ParseErrors>>;
}
pub struct SimpleParser {}
impl Parsable for SimpleParser {
fn parse(code: String) -> Result<Module, ParseErrors> {
fn parse(code: String) -> Result<CompleteArtifact, IncompleteArtifact> {
let ts = Lexer::from_str(code).lex()?;
let mut parser = Parser::new(ts);
parser.parse()
@ -416,59 +419,77 @@ impl Runnable for ParserRunner {
fn exec(&mut self) -> Result<ExitStatus, Self::Errs> {
let src = self.cfg_mut().input.read();
let ast = self.parse(src)?;
println!("{ast}");
let artifact = self.parse(src).map_err(|iart| iart.errors)?;
println!("{}", artifact.ast);
Ok(ExitStatus::OK)
}
fn eval(&mut self, src: String) -> Result<String, ParserRunnerErrors> {
let ast = self.parse(src)?;
Ok(format!("{ast}"))
let artifact = self.parse(src).map_err(|iart| iart.errors)?;
Ok(format!("{}", artifact.ast))
}
}
impl ParserRunner {
pub fn parse_token_stream(&mut self, ts: TokenStream) -> Result<Module, ParserRunnerErrors> {
pub fn parse_token_stream(
&mut self,
ts: TokenStream,
) -> Result<CompleteArtifact, IncompleteArtifact<Module, ParserRunnerErrors>> {
Parser::new(ts)
.parse()
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))
.map_err(|iart| iart.map_errs(|errs| ParserRunnerErrors::convert(self.input(), errs)))
}
pub fn parse(&mut self, src: String) -> Result<Module, ParserRunnerErrors> {
pub fn parse(
&mut self,
src: String,
) -> Result<CompleteArtifact, IncompleteArtifact<Module, ParserRunnerErrors>> {
let ts = Lexer::new(Input::new(InputKind::Str(src), self.cfg.input.id()))
.lex()
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))?;
Parser::new(ts)
.parse()
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))
.map_err(|iart| iart.map_errs(|errs| ParserRunnerErrors::convert(self.input(), errs)))
}
}
impl Parser {
pub fn parse(&mut self) -> Result<Module, ParseErrors> {
pub fn parse(&mut self) -> Result<CompleteArtifact, IncompleteArtifact> {
if self.tokens.is_empty() {
return Ok(Module::empty());
return Ok(CompleteArtifact::new(Module::empty(), ParseErrors::empty()));
}
log!(info "the parsing process has started.");
log!(info "token stream: {}", self.tokens);
let module = match self.try_reduce_module() {
Ok(module) => module,
Err(_) => {
return Err(mem::take(&mut self.errs));
return Err(IncompleteArtifact::new(
None,
mem::take(&mut self.warns),
mem::take(&mut self.errs),
));
}
};
if !self.cur_is(EOF) {
let loc = self.peek().map(|t| t.loc()).unwrap_or_default();
self.errs
.push(ParseError::compiler_bug(0, loc, fn_name!(), line!()));
return Err(mem::take(&mut self.errs));
return Err(IncompleteArtifact::new(
Some(module),
mem::take(&mut self.warns),
mem::take(&mut self.errs),
));
}
log!(info "the parsing process has completed (errs: {}).", self.errs.len());
log!(info "AST:\n{module}");
if self.errs.is_empty() {
Ok(module)
Ok(CompleteArtifact::new(module, mem::take(&mut self.warns)))
} else {
Err(mem::take(&mut self.errs))
Err(IncompleteArtifact::new(
Some(module),
mem::take(&mut self.warns),
mem::take(&mut self.errs),
))
}
}
@ -2839,6 +2860,17 @@ impl Parser {
})?;
match next {
Expr::Def(def) => {
if attrs.iter().any(|attr| {
attr.ident()
.zip(def.sig.ident())
.is_some_and(|(l, r)| l == r)
}) {
self.warns.push(ParseError::duplicate_elem_warning(
line!() as usize,
def.sig.loc(),
def.sig.to_string(),
));
}
attrs.push(RecordAttrOrIdent::Attr(def));
}
Expr::Accessor(acc) => {

View file

@ -1,83 +1,97 @@
use erg_common::config::{ErgConfig, Input};
use erg_common::config::ErgConfig;
use erg_common::consts::DEBUG_MODE;
use erg_common::error::MultiErrorDisplay;
use erg_common::io::Input;
use erg_common::spawn::exec_new_thread;
use erg_common::traits::{Runnable, Stream};
use erg_parser::error::ParserRunnerErrors;
use erg_parser::error::{ErrorArtifact, ParseWarnings, ParserRunnerErrors};
use erg_parser::lex::Lexer;
use erg_parser::ParserRunner;
#[test]
fn parse_args() -> Result<(), ()> {
expect_success("tests/args.er")
expect_success("tests/args.er", 0)
}
#[test]
fn parse_containers() -> Result<(), ()> {
expect_success("tests/containers.er")
expect_success("tests/containers.er", 0)
}
#[test]
fn parse_dependent() -> Result<(), ()> {
expect_success("tests/dependent.er")
expect_success("tests/dependent.er", 0)
}
#[test]
fn parse_fib() -> Result<(), ()> {
expect_success("tests/fib.er")
expect_success("tests/fib.er", 0)
}
#[test]
fn parse_hello_world() -> Result<(), ()> {
expect_success("tests/hello_world.er")
expect_success("tests/hello_world.er", 0)
}
#[test]
fn parse_simple_if() -> Result<(), ()> {
expect_success("tests/simple_if.er")
expect_success("tests/simple_if.er", 0)
}
#[test]
fn parse_stream() -> Result<(), ()> {
expect_success("tests/stream.er")
expect_success("tests/stream.er", 0)
}
#[test]
fn parse_test1_basic_syntax() -> Result<(), ()> {
expect_success("tests/test1_basic_syntax.er")
expect_success("tests/test1_basic_syntax.er", 0)
}
#[test]
fn parse_test2_advanced_syntax() -> Result<(), ()> {
expect_success("tests/test2_advanced_syntax.er")
expect_success("tests/test2_advanced_syntax.er", 0)
}
#[test]
fn parse_stack() -> Result<(), ()> {
expect_failure("tests/stack.er", 2)
expect_failure("tests/stack.er", 0, 2)
}
#[test]
fn parse_str_literal() -> Result<(), ()> {
expect_failure("tests/failed_str_lit.er", 2)
expect_failure("tests/failed_str_lit.er", 0, 2)
}
#[test]
fn parse_invalid_chunk() -> Result<(), ()> {
expect_failure("tests/invalid_chunk.er", 62)
expect_failure("tests/invalid_chunk.er", 0, 62)
}
#[test]
fn parse_invalid_collections() -> Result<(), ()> {
expect_failure("tests/invalid_collections.er", 29)
expect_failure("tests/invalid_collections.er", 0, 29)
}
#[test]
fn parse_invalid_class_definition() -> Result<(), ()> {
expect_failure("tests/invalid_class_definition.er", 7)
expect_failure("tests/invalid_class_definition.er", 0, 7)
}
fn _parse_test_from_code(file_path: &'static str) -> Result<(), ParserRunnerErrors> {
#[test]
fn exec_invalid_chunk_prs_err() -> Result<(), ()> {
expect_failure("tests/invalid_chunk.er", 0, 62)
}
#[test]
fn exec_warns() -> Result<(), ()> {
expect_success("tests/warns.er", 1)
}
fn _parse_test_from_code(
file_path: &'static str,
) -> Result<ParseWarnings, ErrorArtifact<ParserRunnerErrors>> {
let input = Input::file(file_path.into());
let cfg = ErgConfig {
input: input.clone(),
@ -86,43 +100,81 @@ fn _parse_test_from_code(file_path: &'static str) -> Result<(), ParserRunnerErro
};
let lexer = Lexer::new(input.clone());
let mut parser = ParserRunner::new(cfg);
match parser.parse_token_stream(
lexer
.lex()
.map_err(|errs| ParserRunnerErrors::convert(&input, errs))?,
) {
Ok(module) => {
println!("{module}");
Ok(())
match parser.parse_token_stream(lexer.lex().map_err(|errs| {
ErrorArtifact::new(
ParserRunnerErrors::empty(),
ParserRunnerErrors::convert(&input, errs),
)
})?) {
Ok(artifact) => {
if DEBUG_MODE {
println!("{}", artifact.ast);
}
Ok(artifact.warns)
}
Err(e) => {
e.fmt_all_stderr();
Err(e)
Err(artifact) => {
if DEBUG_MODE {
artifact.warns.write_all_stderr();
artifact.errors.write_all_stderr();
}
Err(ErrorArtifact::new(artifact.warns, artifact.errors))
}
}
}
fn parse_test_from_code(file_path: &'static str) -> Result<(), ParserRunnerErrors> {
fn parse_test_from_code(
file_path: &'static str,
) -> Result<ParseWarnings, ErrorArtifact<ParserRunnerErrors>> {
exec_new_thread(move || _parse_test_from_code(file_path), file_path)
}
fn expect_success(file_path: &'static str) -> Result<(), ()> {
fn expect_success(file_path: &'static str, num_warns: usize) -> Result<(), ()> {
match parse_test_from_code(file_path) {
Ok(_) => Ok(()),
Ok(warns) => {
if warns.len() == num_warns {
Ok(())
} else {
println!(
"err: number of warnings is not {num_warns} but {}",
warns.len()
);
Err(())
}
}
Err(_) => Err(()),
}
}
fn expect_failure(file_path: &'static str, errs_len: usize) -> Result<(), ()> {
fn expect_failure(file_path: &'static str, num_warns: usize, num_errs: usize) -> Result<(), ()> {
match parse_test_from_code(file_path) {
Ok(_) => Err(()),
Err(errs) => {
if errs.len() == errs_len {
Ok(())
} else {
println!("err: error length is not {errs_len} but {}", errs.len());
Err(eart) => match (eart.errors.len() == num_errs, eart.warns.len() == num_warns) {
(true, true) => Ok(()),
(true, false) => {
println!(
"err: number of warnings is not {num_warns} but {}",
eart.warns.len()
);
Err(())
}
}
(false, true) => {
println!(
"err: number of errors is not {num_errs} but {}",
eart.errors.len()
);
Err(())
}
(false, false) => {
println!(
"err: number of warnings is not {num_warns} but {}",
eart.warns.len()
);
println!(
"err: number of errors is not {num_errs} but {}",
eart.errors.len()
);
Err(())
}
},
}
}

View file

@ -1,6 +1,6 @@
use std::iter::Iterator;
use erg_common::config::Input;
use erg_common::io::Input;
// use erg_compiler::parser;

View file

@ -0,0 +1 @@
_ = { .foo = 1; .foo = 1 } # WARN