mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
remove Bag
This commit is contained in:
parent
ca04411523
commit
2c9a80153d
11 changed files with 127 additions and 210 deletions
|
@ -1,7 +1,7 @@
|
||||||
use const_format::concatcp;
|
use const_format::concatcp;
|
||||||
use gen::{gen_and_eval, ReplOutput};
|
use gen::{gen_and_eval, ReplOutput};
|
||||||
use roc_gen::llvm::build::OptLevel;
|
use roc_gen::llvm::build::OptLevel;
|
||||||
use roc_parse::parser::{Bag, SyntaxError};
|
use roc_parse::parser::SyntaxError;
|
||||||
use rustyline::error::ReadlineError;
|
use rustyline::error::ReadlineError;
|
||||||
use rustyline::validate::{self, ValidationContext, ValidationResult, Validator};
|
use rustyline::validate::{self, ValidationContext, ValidationResult, Validator};
|
||||||
use rustyline::Editor;
|
use rustyline::Editor;
|
||||||
|
@ -191,11 +191,11 @@ pub fn main() -> io::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_parse_error<'a>(fail: Bag<'a, SyntaxError<'a>>) {
|
fn report_parse_error<'a>(fail: SyntaxError<'a>) {
|
||||||
println!("TODO Gracefully report parse error in repl: {:?}", fail);
|
println!("TODO Gracefully report parse error in repl: {:?}", fail);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_and_format<'a>(src: &str) -> Result<String, Bag<'a, SyntaxError<'a>>> {
|
fn eval_and_format<'a>(src: &str) -> Result<String, SyntaxError<'a>> {
|
||||||
gen_and_eval(src.as_bytes(), Triple::host(), OptLevel::Normal).map(|output| match output {
|
gen_and_eval(src.as_bytes(), Triple::host(), OptLevel::Normal).map(|output| match output {
|
||||||
ReplOutput::NoProblems { expr, expr_type } => {
|
ReplOutput::NoProblems { expr, expr_type } => {
|
||||||
format!("\n{} {}:{} {}", expr, PINK, END_COL, expr_type)
|
format!("\n{} {}:{} {}", expr, PINK, END_COL, expr_type)
|
||||||
|
|
|
@ -7,7 +7,7 @@ use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_fmt::annotation::Formattable;
|
use roc_fmt::annotation::Formattable;
|
||||||
use roc_fmt::annotation::{Newlines, Parens};
|
use roc_fmt::annotation::{Newlines, Parens};
|
||||||
use roc_gen::llvm::build::{build_proc, build_proc_header, OptLevel};
|
use roc_gen::llvm::build::{build_proc, build_proc_header, OptLevel};
|
||||||
use roc_parse::parser::{Bag, SyntaxError};
|
use roc_parse::parser::SyntaxError;
|
||||||
use roc_types::pretty_print::{content_to_string, name_all_type_vars};
|
use roc_types::pretty_print::{content_to_string, name_all_type_vars};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::from_utf8_unchecked;
|
use std::str::from_utf8_unchecked;
|
||||||
|
@ -22,7 +22,7 @@ pub fn gen_and_eval<'a>(
|
||||||
src: &[u8],
|
src: &[u8],
|
||||||
target: Triple,
|
target: Triple,
|
||||||
opt_level: OptLevel,
|
opt_level: OptLevel,
|
||||||
) -> Result<ReplOutput, Bag<'a, SyntaxError<'a>>> {
|
) -> Result<ReplOutput, SyntaxError<'a>> {
|
||||||
use roc_reporting::report::{
|
use roc_reporting::report::{
|
||||||
can_problem, mono_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE,
|
can_problem, mono_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE,
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,7 +11,7 @@ use crate::number_literal::number_literal;
|
||||||
use crate::parser::{
|
use crate::parser::{
|
||||||
self, allocated, and_then_with_indent_level, ascii_char, ascii_string, attempt, backtrackable,
|
self, allocated, and_then_with_indent_level, ascii_char, ascii_string, attempt, backtrackable,
|
||||||
fail, map, newline_char, not, not_followed_by, optional, sep_by1, then, unexpected,
|
fail, map, newline_char, not, not_followed_by, optional, sep_by1, then, unexpected,
|
||||||
unexpected_eof, Bag, Either, ParseResult, Parser, State, SyntaxError,
|
unexpected_eof, Either, ParseResult, Parser, State, SyntaxError,
|
||||||
};
|
};
|
||||||
use crate::type_annotation;
|
use crate::type_annotation;
|
||||||
use bumpalo::collections::string::String;
|
use bumpalo::collections::string::String;
|
||||||
|
@ -100,7 +100,7 @@ macro_rules! loc_parenthetical_expr {
|
||||||
// Re-parse the Expr as a Pattern.
|
// Re-parse the Expr as a Pattern.
|
||||||
let pattern = match expr_to_pattern(arena, &loc_expr.value) {
|
let pattern = match expr_to_pattern(arena, &loc_expr.value) {
|
||||||
Ok(valid) => valid,
|
Ok(valid) => valid,
|
||||||
Err(fail) => return Err((progress, Bag::from_state(arena, &state, fail), state)),
|
Err(fail) => return Err((progress, fail, state)),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Make sure we don't discard the spaces - might be comments in there!
|
// Make sure we don't discard the spaces - might be comments in there!
|
||||||
|
@ -493,22 +493,14 @@ pub fn assigned_pattern_field_to_pattern<'a>(
|
||||||
fn equals_for_def<'a>() -> impl Parser<'a, (), SyntaxError<'a>> {
|
fn equals_for_def<'a>() -> impl Parser<'a, (), SyntaxError<'a>> {
|
||||||
|arena, state: State<'a>| match state.bytes.get(0) {
|
|arena, state: State<'a>| match state.bytes.get(0) {
|
||||||
Some(b'=') => match state.bytes.get(1) {
|
Some(b'=') => match state.bytes.get(1) {
|
||||||
Some(b'=') | Some(b'>') => Err((
|
Some(b'=') | Some(b'>') => Err((NoProgress, SyntaxError::ConditionFailed, state)),
|
||||||
NoProgress,
|
|
||||||
Bag::from_state(arena, &state, SyntaxError::ConditionFailed),
|
|
||||||
state,
|
|
||||||
)),
|
|
||||||
_ => {
|
_ => {
|
||||||
let state = state.advance_without_indenting(arena, 1)?;
|
let state = state.advance_without_indenting(arena, 1)?;
|
||||||
|
|
||||||
Ok((MadeProgress, (), state))
|
Ok((MadeProgress, (), state))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => Err((
|
_ => Err((NoProgress, SyntaxError::ConditionFailed, state)),
|
||||||
NoProgress,
|
|
||||||
Bag::from_state(arena, &state, SyntaxError::ConditionFailed),
|
|
||||||
state,
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -738,22 +730,14 @@ fn parse_def_expr<'a>(
|
||||||
spaces_after_equals: &'a [CommentOrNewline<'a>],
|
spaces_after_equals: &'a [CommentOrNewline<'a>],
|
||||||
) -> ParseResult<'a, Expr<'a>, SyntaxError<'a>> {
|
) -> ParseResult<'a, Expr<'a>, SyntaxError<'a>> {
|
||||||
if def_start_col < min_indent {
|
if def_start_col < min_indent {
|
||||||
Err((
|
Err((NoProgress, SyntaxError::OutdentedTooFar, state))
|
||||||
NoProgress,
|
|
||||||
Bag::from_state(arena, &state, SyntaxError::OutdentedTooFar),
|
|
||||||
state,
|
|
||||||
))
|
|
||||||
// `<` because '=' should be same indent (or greater) as the entire def-expr
|
// `<` because '=' should be same indent (or greater) as the entire def-expr
|
||||||
} else if equals_sign_indent < def_start_col {
|
} else if equals_sign_indent < def_start_col {
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
r"TODO the = in this declaration seems outdented. equals_sign_indent was {} and def_start_col was {}",
|
r"TODO the = in this declaration seems outdented. equals_sign_indent was {} and def_start_col was {}",
|
||||||
equals_sign_indent, def_start_col
|
equals_sign_indent, def_start_col
|
||||||
);
|
);
|
||||||
Err((
|
Err((NoProgress, SyntaxError::NotYetImplemented(msg), state))
|
||||||
NoProgress,
|
|
||||||
Bag::from_state(arena, &state, SyntaxError::NotYetImplemented(msg)),
|
|
||||||
state,
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
// Indented more beyond the original indent of the entire def-expr.
|
// Indented more beyond the original indent of the entire def-expr.
|
||||||
let indented_more = def_start_col + 1;
|
let indented_more = def_start_col + 1;
|
||||||
|
@ -831,21 +815,13 @@ fn parse_def_signature<'a>(
|
||||||
let original_indent = state.indent_col;
|
let original_indent = state.indent_col;
|
||||||
|
|
||||||
if original_indent < min_indent {
|
if original_indent < min_indent {
|
||||||
Err((
|
Err((NoProgress, SyntaxError::OutdentedTooFar, state))
|
||||||
NoProgress,
|
|
||||||
Bag::from_state(arena, &state, SyntaxError::OutdentedTooFar),
|
|
||||||
state,
|
|
||||||
))
|
|
||||||
// `<` because ':' should be same indent or greater
|
// `<` because ':' should be same indent or greater
|
||||||
} else if colon_indent < original_indent {
|
} else if colon_indent < original_indent {
|
||||||
Err((
|
Err((
|
||||||
NoProgress,
|
NoProgress,
|
||||||
Bag::from_state(
|
SyntaxError::NotYetImplemented(
|
||||||
arena,
|
"TODO the : in this declaration seems outdented".to_string(),
|
||||||
&state,
|
|
||||||
SyntaxError::NotYetImplemented(
|
|
||||||
"TODO the : in this declaration seems outdented".to_string(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
state,
|
state,
|
||||||
))
|
))
|
||||||
|
@ -1336,9 +1312,7 @@ fn loc_ident_pattern<'a>(
|
||||||
Ident::Malformed(malformed) => {
|
Ident::Malformed(malformed) => {
|
||||||
debug_assert!(!malformed.is_empty());
|
debug_assert!(!malformed.is_empty());
|
||||||
|
|
||||||
let bag = Bag::from_state(arena, &state, SyntaxError::InvalidPattern);
|
Err((MadeProgress, SyntaxError::InvalidPattern, state))
|
||||||
|
|
||||||
Err((MadeProgress, bag, state))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1368,12 +1342,8 @@ mod when {
|
||||||
if case_indent < min_indent {
|
if case_indent < min_indent {
|
||||||
return Err((
|
return Err((
|
||||||
progress,
|
progress,
|
||||||
Bag::from_state(
|
SyntaxError::NotYetImplemented(
|
||||||
arena,
|
"TODO case wasn't indented enough".to_string(),
|
||||||
&state,
|
|
||||||
SyntaxError::NotYetImplemented(
|
|
||||||
"TODO case wasn't indented enough".to_string(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
state,
|
state,
|
||||||
));
|
));
|
||||||
|
@ -1440,11 +1410,10 @@ mod when {
|
||||||
} else {
|
} else {
|
||||||
Err((
|
Err((
|
||||||
MadeProgress,
|
MadeProgress,
|
||||||
Bag::from_state( arena, &state,
|
|
||||||
SyntaxError::NotYetImplemented(
|
SyntaxError::NotYetImplemented(
|
||||||
"TODO additional branch didn't have same indentation as first branch".to_string(),
|
"TODO additional branch didn't have same indentation as first branch".to_string(),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
state,
|
state,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -1702,8 +1671,7 @@ fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<'a>>
|
||||||
(Some(loc_args), Some((_spaces_before_equals, Either::First(_equals_indent)))) => {
|
(Some(loc_args), Some((_spaces_before_equals, Either::First(_equals_indent)))) => {
|
||||||
// We got args with an '=' after them, e.g. `foo a b = ...` This is a syntax error!
|
// We got args with an '=' after them, e.g. `foo a b = ...` This is a syntax error!
|
||||||
let region = Region::across_all(loc_args.iter().map(|v| &v.region));
|
let region = Region::across_all(loc_args.iter().map(|v| &v.region));
|
||||||
let fail =
|
let fail = SyntaxError::ArgumentsBeforeEquals(region);
|
||||||
Bag::from_state(arena, &state, SyntaxError::ArgumentsBeforeEquals(region));
|
|
||||||
Err((MadeProgress, fail, state))
|
Err((MadeProgress, fail, state))
|
||||||
}
|
}
|
||||||
(None, Some((spaces_before_equals, Either::First(equals_indent)))) => {
|
(None, Some((spaces_before_equals, Either::First(equals_indent)))) => {
|
||||||
|
@ -1778,14 +1746,10 @@ fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<'a>>
|
||||||
Err(malformed) => {
|
Err(malformed) => {
|
||||||
return Err((
|
return Err((
|
||||||
MadeProgress,
|
MadeProgress,
|
||||||
Bag::from_state(
|
SyntaxError::NotYetImplemented(format!(
|
||||||
arena,
|
"TODO early return malformed pattern {:?}",
|
||||||
&state,
|
malformed
|
||||||
SyntaxError::NotYetImplemented(format!(
|
)),
|
||||||
"TODO early return malformed pattern {:?}",
|
|
||||||
malformed
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
state,
|
state,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -2014,9 +1978,7 @@ fn record_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<
|
||||||
match assigned_expr_field_to_pattern(arena, &loc_assigned_field.value) {
|
match assigned_expr_field_to_pattern(arena, &loc_assigned_field.value) {
|
||||||
Ok(value) => loc_patterns.push(Located { region, value }),
|
Ok(value) => loc_patterns.push(Located { region, value }),
|
||||||
// an Expr became a pattern that should not be.
|
// an Expr became a pattern that should not be.
|
||||||
Err(fail) => {
|
Err(fail) => return Err((progress, fail, state)),
|
||||||
return Err((progress, Bag::from_state(arena, &state, fail), state))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2054,9 +2016,7 @@ fn record_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<
|
||||||
match assigned_expr_field_to_pattern(arena, &loc_assigned_field.value) {
|
match assigned_expr_field_to_pattern(arena, &loc_assigned_field.value) {
|
||||||
Ok(value) => loc_patterns.push(Located { region, value }),
|
Ok(value) => loc_patterns.push(Located { region, value }),
|
||||||
// an Expr became a pattern that should not be.
|
// an Expr became a pattern that should not be.
|
||||||
Err(fail) => {
|
Err(fail) => return Err((progress, fail, state)),
|
||||||
return Err((progress, Bag::from_state(arena, &state, fail), state))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::ast::Attempting;
|
use crate::ast::Attempting;
|
||||||
use crate::keyword;
|
use crate::keyword;
|
||||||
use crate::parser::Progress::{self, *};
|
use crate::parser::Progress::{self, *};
|
||||||
use crate::parser::{peek_utf8_char, unexpected, Bag, ParseResult, Parser, State, SyntaxError};
|
use crate::parser::{peek_utf8_char, unexpected, ParseResult, Parser, State, SyntaxError};
|
||||||
use bumpalo::collections::string::String;
|
use bumpalo::collections::string::String;
|
||||||
use bumpalo::collections::vec::Vec;
|
use bumpalo::collections::vec::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
@ -399,11 +399,7 @@ pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str, SyntaxError<'a>> {
|
||||||
{
|
{
|
||||||
// TODO Calculate the correct region based on state
|
// TODO Calculate the correct region based on state
|
||||||
let region = Region::zero();
|
let region = Region::zero();
|
||||||
Err((
|
Err((MadeProgress, SyntaxError::ReservedKeyword(region), state))
|
||||||
MadeProgress,
|
|
||||||
Bag::from_state(arena, &state, SyntaxError::ReservedKeyword(region)),
|
|
||||||
state,
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
Ok((MadeProgress, ident, state))
|
Ok((MadeProgress, ident, state))
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,12 +59,9 @@ impl<'a> State<'a> {
|
||||||
self,
|
self,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
) -> Result<Self, (Bag<'a, SyntaxError<'a>>, Self)> {
|
) -> Result<Self, (SyntaxError<'a>, Self)> {
|
||||||
if self.indent_col < min_indent {
|
if self.indent_col < min_indent {
|
||||||
Err((
|
Err((SyntaxError::OutdentedTooFar, self))
|
||||||
Bag::from_state(arena, &self, SyntaxError::OutdentedTooFar),
|
|
||||||
self,
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
@ -84,10 +81,7 @@ impl<'a> State<'a> {
|
||||||
|
|
||||||
/// Increments the line, then resets column, indent_col, and is_indenting.
|
/// Increments the line, then resets column, indent_col, and is_indenting.
|
||||||
/// Advances the input by 1, to consume the newline character.
|
/// Advances the input by 1, to consume the newline character.
|
||||||
pub fn newline(
|
pub fn newline(&self, arena: &'a Bump) -> Result<Self, (Progress, SyntaxError<'a>, Self)> {
|
||||||
&self,
|
|
||||||
arena: &'a Bump,
|
|
||||||
) -> Result<Self, (Progress, Bag<'a, SyntaxError<'a>>, Self)> {
|
|
||||||
match self.line.checked_add(1) {
|
match self.line.checked_add(1) {
|
||||||
Some(line) => Ok(State {
|
Some(line) => Ok(State {
|
||||||
bytes: &self.bytes[1..],
|
bytes: &self.bytes[1..],
|
||||||
|
@ -100,7 +94,7 @@ impl<'a> State<'a> {
|
||||||
}),
|
}),
|
||||||
None => Err((
|
None => Err((
|
||||||
Progress::NoProgress,
|
Progress::NoProgress,
|
||||||
Bag::from_state(arena, &self, SyntaxError::TooManyLines),
|
SyntaxError::TooManyLines,
|
||||||
self.clone(),
|
self.clone(),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
|
@ -114,7 +108,7 @@ impl<'a> State<'a> {
|
||||||
self,
|
self,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
quantity: usize,
|
quantity: usize,
|
||||||
) -> Result<Self, (Progress, Bag<'a, SyntaxError<'a>>, Self)> {
|
) -> Result<Self, (Progress, SyntaxError<'a>, Self)> {
|
||||||
match (self.column as usize).checked_add(quantity) {
|
match (self.column as usize).checked_add(quantity) {
|
||||||
Some(column_usize) if column_usize <= u16::MAX as usize => {
|
Some(column_usize) if column_usize <= u16::MAX as usize => {
|
||||||
Ok(State {
|
Ok(State {
|
||||||
|
@ -134,7 +128,7 @@ impl<'a> State<'a> {
|
||||||
&self,
|
&self,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
spaces: usize,
|
spaces: usize,
|
||||||
) -> Result<Self, (Progress, Bag<'a, SyntaxError<'a>>, Self)> {
|
) -> Result<Self, (Progress, SyntaxError<'a>, Self)> {
|
||||||
match (self.column as usize).checked_add(spaces) {
|
match (self.column as usize).checked_add(spaces) {
|
||||||
Some(column_usize) if column_usize <= u16::MAX as usize => {
|
Some(column_usize) if column_usize <= u16::MAX as usize => {
|
||||||
// Spaces don't affect is_indenting; if we were previously indneting,
|
// Spaces don't affect is_indenting; if we were previously indneting,
|
||||||
|
@ -187,13 +181,13 @@ impl<'a> State<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a failing ParseResult for the given FailReason
|
/// Return a failing ParseResult for the given FailReason
|
||||||
pub fn fail<T>(
|
pub fn fail<T, X>(
|
||||||
self,
|
self,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
progress: Progress,
|
progress: Progress,
|
||||||
reason: SyntaxError<'a>,
|
reason: X,
|
||||||
) -> Result<(Progress, T, Self), (Progress, Bag<'a, SyntaxError<'a>>, Self)> {
|
) -> Result<(Progress, T, Self), (Progress, X, Self)> {
|
||||||
Err((progress, Bag::from_state(arena, &self, reason), self))
|
Err((progress, reason, self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,7 +219,7 @@ fn state_size() {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ParseResult<'a, Output, Error> =
|
pub type ParseResult<'a, Output, Error> =
|
||||||
Result<(Progress, Output, State<'a>), (Progress, Bag<'a, Error>, State<'a>)>;
|
Result<(Progress, Output, State<'a>), (Progress, Error, State<'a>)>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Progress {
|
pub enum Progress {
|
||||||
|
@ -275,6 +269,22 @@ pub enum SyntaxError<'a> {
|
||||||
Type(Type<'a>),
|
Type(Type<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> SyntaxError<'a> {
|
||||||
|
pub fn into_parse_problem(
|
||||||
|
self,
|
||||||
|
filename: std::path::PathBuf,
|
||||||
|
bytes: &'a [u8],
|
||||||
|
) -> ParseProblem<'a, SyntaxError<'a>> {
|
||||||
|
ParseProblem {
|
||||||
|
line: 0,
|
||||||
|
column: 0,
|
||||||
|
problem: self,
|
||||||
|
filename,
|
||||||
|
bytes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Row = u32;
|
type Row = u32;
|
||||||
type Col = u16;
|
type Col = u16;
|
||||||
|
|
||||||
|
@ -351,55 +361,6 @@ pub struct DeadEnd<'a, T> {
|
||||||
pub context_stack: ContextStack<'a>,
|
pub context_stack: ContextStack<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub struct Bag<'a, T>(Vec<'a, DeadEnd<'a, T>>);
|
|
||||||
|
|
||||||
impl<'a, T> Bag<'a, T> {
|
|
||||||
pub fn new_in(arena: &'a Bump) -> Self {
|
|
||||||
Bag(Vec::new_in(arena))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_state(arena: &'a Bump, state: &State<'a>, x: T) -> Self {
|
|
||||||
let mut dead_ends = Vec::with_capacity_in(1, arena);
|
|
||||||
|
|
||||||
let dead_end = DeadEnd {
|
|
||||||
line: state.line,
|
|
||||||
column: state.column,
|
|
||||||
problem: x,
|
|
||||||
context_stack: state.context_stack.clone(),
|
|
||||||
};
|
|
||||||
dead_ends.push(dead_end);
|
|
||||||
|
|
||||||
Bag(dead_ends)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pop(&mut self) -> Option<DeadEnd<'a, T>> {
|
|
||||||
self.0.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_parse_problem(
|
|
||||||
mut self,
|
|
||||||
filename: std::path::PathBuf,
|
|
||||||
bytes: &[u8],
|
|
||||||
) -> ParseProblem<'_, T> {
|
|
||||||
match self.pop() {
|
|
||||||
None => unreachable!("there is a parse error, but no problem"),
|
|
||||||
Some(dead_end) => {
|
|
||||||
let context_stack = dead_end.context_stack.into_vec();
|
|
||||||
|
|
||||||
ParseProblem {
|
|
||||||
line: dead_end.line,
|
|
||||||
column: dead_end.column,
|
|
||||||
problem: dead_end.problem,
|
|
||||||
context_stack,
|
|
||||||
filename,
|
|
||||||
bytes,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// use std vec to escape the arena's lifetime bound
|
/// use std vec to escape the arena's lifetime bound
|
||||||
/// since this is only used when there is in fact an error
|
/// since this is only used when there is in fact an error
|
||||||
/// I think this is fine
|
/// I think this is fine
|
||||||
|
@ -408,19 +369,12 @@ pub struct ParseProblem<'a, T> {
|
||||||
pub line: u32,
|
pub line: u32,
|
||||||
pub column: u16,
|
pub column: u16,
|
||||||
pub problem: T,
|
pub problem: T,
|
||||||
pub context_stack: std::vec::Vec<ContextItem>,
|
|
||||||
pub filename: std::path::PathBuf,
|
pub filename: std::path::PathBuf,
|
||||||
pub bytes: &'a [u8],
|
pub bytes: &'a [u8],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fail<'a, T>() -> impl Parser<'a, T, SyntaxError<'a>> {
|
pub fn fail<'a, T>() -> impl Parser<'a, T, SyntaxError<'a>> {
|
||||||
move |arena, state: State<'a>| {
|
move |arena, state: State<'a>| Err((NoProgress, SyntaxError::ConditionFailed, state))
|
||||||
Err((
|
|
||||||
NoProgress,
|
|
||||||
Bag::from_state(arena, &state, SyntaxError::ConditionFailed),
|
|
||||||
state,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Parser<'a, Output, Error> {
|
pub trait Parser<'a, Output, Error> {
|
||||||
|
@ -467,11 +421,9 @@ where
|
||||||
let after_parse = state.clone();
|
let after_parse = state.clone();
|
||||||
|
|
||||||
match by.parse(arena, state) {
|
match by.parse(arena, state) {
|
||||||
Ok((_, _, state)) => Err((
|
Ok((_, _, state)) => {
|
||||||
NoProgress,
|
Err((NoProgress, SyntaxError::ConditionFailed, original_state))
|
||||||
Bag::from_state(arena, &state, SyntaxError::ConditionFailed),
|
}
|
||||||
original_state,
|
|
||||||
)),
|
|
||||||
Err(_) => Ok((progress, answer, after_parse)),
|
Err(_) => Ok((progress, answer, after_parse)),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -486,11 +438,7 @@ where
|
||||||
let original_state = state.clone();
|
let original_state = state.clone();
|
||||||
|
|
||||||
match parser.parse(arena, state) {
|
match parser.parse(arena, state) {
|
||||||
Ok((_, _, _)) => Err((
|
Ok((_, _, _)) => Err((NoProgress, SyntaxError::ConditionFailed, original_state)),
|
||||||
NoProgress,
|
|
||||||
Bag::from_state(arena, &original_state, SyntaxError::ConditionFailed),
|
|
||||||
original_state,
|
|
||||||
)),
|
|
||||||
Err((_, _, _)) => Ok((NoProgress, (), original_state)),
|
Err((_, _, _)) => Ok((NoProgress, (), original_state)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -571,7 +519,7 @@ pub fn unexpected_eof<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
chars_consumed: usize,
|
chars_consumed: usize,
|
||||||
) -> (Progress, Bag<'a, SyntaxError<'a>>, State<'a>) {
|
) -> (Progress, SyntaxError<'a>, State<'a>) {
|
||||||
checked_unexpected(arena, state, chars_consumed, |region| {
|
checked_unexpected(arena, state, chars_consumed, |region| {
|
||||||
SyntaxError::Eof(region)
|
SyntaxError::Eof(region)
|
||||||
})
|
})
|
||||||
|
@ -582,7 +530,7 @@ pub fn unexpected<'a>(
|
||||||
chars_consumed: usize,
|
chars_consumed: usize,
|
||||||
_attempting: Attempting,
|
_attempting: Attempting,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
) -> (Progress, Bag<'a, SyntaxError<'a>>, State<'a>) {
|
) -> (Progress, SyntaxError<'a>, State<'a>) {
|
||||||
// NOTE state is the last argument because chars_consumed often depends on the state's fields
|
// NOTE state is the last argument because chars_consumed often depends on the state's fields
|
||||||
// having state be the final argument prevents borrowing issues
|
// having state be the final argument prevents borrowing issues
|
||||||
checked_unexpected(arena, state, chars_consumed, |region| {
|
checked_unexpected(arena, state, chars_consumed, |region| {
|
||||||
|
@ -599,7 +547,7 @@ fn checked_unexpected<'a, F>(
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
chars_consumed: usize,
|
chars_consumed: usize,
|
||||||
problem_from_region: F,
|
problem_from_region: F,
|
||||||
) -> (Progress, Bag<'a, SyntaxError<'a>>, State<'a>)
|
) -> (Progress, SyntaxError<'a>, State<'a>)
|
||||||
where
|
where
|
||||||
F: FnOnce(Region) -> SyntaxError<'a>,
|
F: FnOnce(Region) -> SyntaxError<'a>,
|
||||||
{
|
{
|
||||||
|
@ -617,13 +565,7 @@ where
|
||||||
end_line: state.line,
|
end_line: state.line,
|
||||||
};
|
};
|
||||||
|
|
||||||
let problem = problem_from_region(region);
|
(Progress::NoProgress, problem_from_region(region), state)
|
||||||
|
|
||||||
(
|
|
||||||
Progress::NoProgress,
|
|
||||||
Bag::from_state(arena, &state, problem),
|
|
||||||
state,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let (_progress, fail, state) = line_too_long(arena, state);
|
let (_progress, fail, state) = line_too_long(arena, state);
|
||||||
|
@ -632,10 +574,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn line_too_long<'a>(
|
fn line_too_long<'a>(arena: &'a Bump, state: State<'a>) -> (Progress, SyntaxError<'a>, State<'a>) {
|
||||||
arena: &'a Bump,
|
|
||||||
state: State<'a>,
|
|
||||||
) -> (Progress, Bag<'a, SyntaxError<'a>>, State<'a>) {
|
|
||||||
let problem = SyntaxError::LineTooLong(state.line);
|
let problem = SyntaxError::LineTooLong(state.line);
|
||||||
// Set column to MAX and advance the parser to end of input.
|
// Set column to MAX and advance the parser to end of input.
|
||||||
// This way, all future parsers will fail on EOF, and then
|
// This way, all future parsers will fail on EOF, and then
|
||||||
|
@ -654,11 +593,7 @@ fn line_too_long<'a>(
|
||||||
|
|
||||||
// TODO do we make progress in this case?
|
// TODO do we make progress in this case?
|
||||||
// isn't this error fatal?
|
// isn't this error fatal?
|
||||||
(
|
(Progress::NoProgress, problem, state)
|
||||||
Progress::NoProgress,
|
|
||||||
Bag::from_state(arena, &state, problem),
|
|
||||||
state,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single ASCII char that isn't a newline.
|
/// A single ASCII char that isn't a newline.
|
||||||
|
@ -987,7 +922,7 @@ where
|
||||||
|
|
||||||
pub fn fail_when_progress<'a, T, E>(
|
pub fn fail_when_progress<'a, T, E>(
|
||||||
progress: Progress,
|
progress: Progress,
|
||||||
fail: Bag<'a, E>,
|
fail: E,
|
||||||
value: T,
|
value: T,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
) -> ParseResult<'a, T, E> {
|
) -> ParseResult<'a, T, E> {
|
||||||
|
@ -1006,11 +941,9 @@ where
|
||||||
Ok((progress, output, next_state)) if predicate(&output) => {
|
Ok((progress, output, next_state)) if predicate(&output) => {
|
||||||
Ok((progress, output, next_state))
|
Ok((progress, output, next_state))
|
||||||
}
|
}
|
||||||
Ok((progress, _, _)) | Err((progress, _, _)) => Err((
|
Ok((progress, _, _)) | Err((progress, _, _)) => {
|
||||||
progress,
|
Err((progress, SyntaxError::ConditionFailed, state))
|
||||||
Bag::from_state(arena, &state, SyntaxError::ConditionFailed),
|
}
|
||||||
state,
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1212,6 +1145,45 @@ macro_rules! one_of {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! one_of_with_error {
|
||||||
|
($toerror:expr; $p1:expr, $p2:expr) => {
|
||||||
|
move |arena: &'a bumpalo::Bump, state: $crate::parser::State<'a>| {
|
||||||
|
|
||||||
|
match $p1.parse(arena, state) {
|
||||||
|
valid @ Ok(_) => valid,
|
||||||
|
Err((MadeProgress, _, state)) => Err((MadeProgress, $toerror(state.line, state.column), state)),
|
||||||
|
Err((NoProgress, _, state)) => $p2.parse( arena, state),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
($toerror:expr; $p1:expr, $($others:expr),+) => {
|
||||||
|
one_of_with_error!($toerror, $p1, one_of!($($others),+))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn word1<'a, ToError, E>(word: u8, to_error: ToError) -> impl Parser<'a, (), E>
|
||||||
|
where
|
||||||
|
ToError: Fn(Row, Col) -> E,
|
||||||
|
E: 'a,
|
||||||
|
{
|
||||||
|
debug_assert_ne!(word, b'\n');
|
||||||
|
|
||||||
|
move |_arena: &'a Bump, state: State<'a>| match state.bytes.get(0) {
|
||||||
|
Some(x) if *x == word => Ok((
|
||||||
|
MadeProgress,
|
||||||
|
(),
|
||||||
|
State {
|
||||||
|
bytes: &state.bytes[1..],
|
||||||
|
column: state.column + 1,
|
||||||
|
..state
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
_ => Err((NoProgress, to_error(state.line, state.column), state)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! map {
|
macro_rules! map {
|
||||||
($parser:expr, $transform:expr) => {
|
($parser:expr, $transform:expr) => {
|
||||||
|
@ -1580,11 +1552,7 @@ pub fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> {
|
||||||
if state.has_reached_end() {
|
if state.has_reached_end() {
|
||||||
Ok((NoProgress, (), state))
|
Ok((NoProgress, (), state))
|
||||||
} else {
|
} else {
|
||||||
Err((
|
Err((NoProgress, SyntaxError::ConditionFailed, state))
|
||||||
NoProgress,
|
|
||||||
Bag::from_state(arena, &state, SyntaxError::ConditionFailed),
|
|
||||||
state,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::ast::{Attempting, EscapedChar, StrLiteral, StrSegment};
|
||||||
use crate::expr;
|
use crate::expr;
|
||||||
use crate::parser::Progress::*;
|
use crate::parser::Progress::*;
|
||||||
use crate::parser::{
|
use crate::parser::{
|
||||||
allocated, ascii_char, ascii_hex_digits, loc, parse_utf8, unexpected, unexpected_eof, Bag,
|
allocated, ascii_char, ascii_hex_digits, loc, parse_utf8, unexpected, unexpected_eof,
|
||||||
ParseResult, Parser, State, SyntaxError,
|
ParseResult, Parser, State, SyntaxError,
|
||||||
};
|
};
|
||||||
use bumpalo::collections::vec::Vec;
|
use bumpalo::collections::vec::Vec;
|
||||||
|
@ -291,14 +291,10 @@ where
|
||||||
// Ok((StrLiteral::Block(lines.into_bump_slice()), state))
|
// Ok((StrLiteral::Block(lines.into_bump_slice()), state))
|
||||||
Err((
|
Err((
|
||||||
MadeProgress,
|
MadeProgress,
|
||||||
Bag::from_state(
|
SyntaxError::NotYetImplemented(format!(
|
||||||
arena,
|
"TODO parse this line in a block string: {:?}",
|
||||||
&state,
|
line
|
||||||
SyntaxError::NotYetImplemented(format!(
|
)),
|
||||||
"TODO parse this line in a block string: {:?}",
|
|
||||||
line
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
state,
|
state,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::ast::{self, Attempting};
|
||||||
use crate::blankspace::space0_before;
|
use crate::blankspace::space0_before;
|
||||||
use crate::expr::expr;
|
use crate::expr::expr;
|
||||||
use crate::module::{header, module_defs};
|
use crate::module::{header, module_defs};
|
||||||
use crate::parser::{loc, Bag, Parser, State, SyntaxError};
|
use crate::parser::{loc, Parser, State, SyntaxError};
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_region::all::Located;
|
use roc_region::all::Located;
|
||||||
|
@ -11,14 +11,14 @@ use roc_region::all::Located;
|
||||||
pub fn parse_expr_with<'a>(
|
pub fn parse_expr_with<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
input: &'a str,
|
input: &'a str,
|
||||||
) -> Result<ast::Expr<'a>, Bag<'a, SyntaxError<'a>>> {
|
) -> Result<ast::Expr<'a>, SyntaxError<'a>> {
|
||||||
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
|
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_header_with<'a>(
|
pub fn parse_header_with<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
input: &'a str,
|
input: &'a str,
|
||||||
) -> Result<ast::Module<'a>, Bag<'a, SyntaxError<'a>>> {
|
) -> Result<ast::Module<'a>, SyntaxError<'a>> {
|
||||||
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
||||||
let answer = header().parse(arena, state);
|
let answer = header().parse(arena, state);
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ pub fn parse_header_with<'a>(
|
||||||
pub fn parse_defs_with<'a>(
|
pub fn parse_defs_with<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
input: &'a str,
|
input: &'a str,
|
||||||
) -> Result<Vec<'a, Located<ast::Def<'a>>>, Bag<'a, SyntaxError<'a>>> {
|
) -> Result<Vec<'a, Located<ast::Def<'a>>>, SyntaxError<'a>> {
|
||||||
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
||||||
let answer = module_defs().parse(arena, state);
|
let answer = module_defs().parse(arena, state);
|
||||||
answer
|
answer
|
||||||
|
@ -43,7 +43,7 @@ pub fn parse_defs_with<'a>(
|
||||||
pub fn parse_loc_with<'a>(
|
pub fn parse_loc_with<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
input: &'a str,
|
input: &'a str,
|
||||||
) -> Result<Located<ast::Expr<'a>>, Bag<'a, SyntaxError<'a>>> {
|
) -> Result<Located<ast::Expr<'a>>, SyntaxError<'a>> {
|
||||||
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
||||||
let parser = space0_before(loc(expr(0)), 0);
|
let parser = space0_before(loc(expr(0)), 0);
|
||||||
let answer = parser.parse(&arena, state);
|
let answer = parser.parse(&arena, state);
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::expr::{global_tag, private_tag};
|
||||||
use crate::ident::join_module_parts;
|
use crate::ident::join_module_parts;
|
||||||
use crate::keyword;
|
use crate::keyword;
|
||||||
use crate::parser::{
|
use crate::parser::{
|
||||||
allocated, ascii_char, ascii_string, not, optional, peek_utf8_char, unexpected, Bag, Either,
|
allocated, ascii_char, ascii_string, not, optional, peek_utf8_char, unexpected, Either,
|
||||||
ParseResult, Parser,
|
ParseResult, Parser,
|
||||||
Progress::{self, *},
|
Progress::{self, *},
|
||||||
State, SyntaxError,
|
State, SyntaxError,
|
||||||
|
@ -269,11 +269,7 @@ fn expression<'a>(
|
||||||
let msg =
|
let msg =
|
||||||
"TODO: Decide the correct error to return for 'Invalid function signature'"
|
"TODO: Decide the correct error to return for 'Invalid function signature'"
|
||||||
.to_string();
|
.to_string();
|
||||||
Err((
|
Err((progress, SyntaxError::NotYetImplemented(msg), state))
|
||||||
progress,
|
|
||||||
Bag::from_state(arena, &state, SyntaxError::NotYetImplemented(msg)),
|
|
||||||
state,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,8 @@ pub fn parse_problem<'b>(
|
||||||
let doc = alloc.stack(vec![
|
let doc = alloc.stack(vec![
|
||||||
alloc.concat(vec![
|
alloc.concat(vec![
|
||||||
alloc.reflow("Unexpected token "),
|
alloc.reflow("Unexpected token "),
|
||||||
context(alloc, &parse_problem.context_stack, "here"),
|
// context(alloc, &parse_problem.context_stack, "here"),
|
||||||
|
todo!(),
|
||||||
alloc.text(":"),
|
alloc.text(":"),
|
||||||
]),
|
]),
|
||||||
alloc.region(region),
|
alloc.region(region),
|
||||||
|
|
|
@ -19,7 +19,7 @@ use roc_parse::ast::StrLiteral;
|
||||||
use roc_parse::ast::{self, Attempting};
|
use roc_parse::ast::{self, Attempting};
|
||||||
use roc_parse::blankspace::space0_before;
|
use roc_parse::blankspace::space0_before;
|
||||||
use roc_parse::expr::expr;
|
use roc_parse::expr::expr;
|
||||||
use roc_parse::parser::{loc, Bag, Parser, State, SyntaxError};
|
use roc_parse::parser::{loc, Parser, State, SyntaxError};
|
||||||
use roc_problem::can::{Problem, RuntimeError};
|
use roc_problem::can::{Problem, RuntimeError};
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
|
@ -232,7 +232,7 @@ pub fn str_to_expr2<'a>(
|
||||||
env: &mut Env<'a>,
|
env: &mut Env<'a>,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> Result<(Expr2, self::Output), Bag<'a, SyntaxError<'a>>> {
|
) -> Result<(Expr2, self::Output), SyntaxError<'a>> {
|
||||||
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
||||||
let parser = space0_before(loc(expr(0)), 0);
|
let parser = space0_before(loc(expr(0)), 0);
|
||||||
let parse_res = parser.parse(&arena, state);
|
let parse_res = parser.parse(&arena, state);
|
||||||
|
|
|
@ -21,8 +21,8 @@ pub struct File<'a> {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ReadError<'a> {
|
pub enum ReadError<'a> {
|
||||||
Read(std::io::Error),
|
Read(std::io::Error),
|
||||||
ParseDefs(parser::Bag<'a, SyntaxError<'a>>),
|
ParseDefs(SyntaxError<'a>),
|
||||||
ParseHeader(parser::Bag<'a, SyntaxError<'a>>),
|
ParseHeader(SyntaxError<'a>),
|
||||||
DoesntHaveRocExtension,
|
DoesntHaveRocExtension,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue