use crate::*;
use nom::{
branch::alt,
bytes::complete::{tag, take_while1, take_while, take_until},
character::complete::{char, digit1, alpha1, alphanumeric1, line_ending, not_line_ending},
combinator::{opt, map, recognize, value, cut},
multi::{many0, separated_list0, separated_list1},
sequence::{delimited, preceded, tuple, pair},
IResult, Err as NomErr,
error::{ErrorKind, ParseError as NomParseError}
};
#[derive(Debug, Clone)]
pub struct ResilientParseError {
pub message: String,
pub line: u32,
pub column: u32,
}
#[derive(Debug, Clone)]
pub struct ParseResult {
pub program: Program,
pub errors: Vec<ResilientParseError>,
}
impl ParseResult {
pub fn new() -> Self {
Self {
program: Program { statements: Vec::new() },
errors: Vec::new(),
}
}
}
fn calculate_position(original_input: &str, current_input: &str) -> (u32, u32) {
let consumed_bytes = original_input.len() - current_input.len();
let consumed_text = &original_input[..consumed_bytes];
let line = consumed_text.matches('\n').count() as u32;
let column = if let Some(last_newline) = consumed_text.rfind('\n') {
(consumed_text.len() - last_newline - 1) as u32
} else {
consumed_text.len() as u32
};
(line, column)
}
pub fn parse_program_resilient(input: &str) -> ParseResult {
let mut result = ParseResult::new();
let original_input = input;
let mut remaining = input;
if let Ok((new_input, _)) = multispace_and_comments(remaining) {
remaining = new_input;
}
while !remaining.trim().is_empty() {
match parse_statement_resilient(remaining) {
Ok((new_input, statement)) => {
result.program.statements.push(statement);
remaining = new_input;
if let Ok((new_input, _)) = multispace_and_comments(remaining) {
remaining = new_input;
}
}
Err(error) => {
let (line, column) = calculate_position(original_input, remaining);
result.errors.push(ResilientParseError {
message: format!("Parse error: {}", error),
line,
column,
});
remaining = skip_to_next_statement(remaining);
if remaining.trim().is_empty() {
break;
}
}
}
}
result
}
fn parse_statement_resilient(input: &str) -> IResult<&str, Statement> {
alt((
map(type_decl_resilient, Statement::TypeDecl),
map(enum_decl_resilient, Statement::EnumDecl),
map(instance_or_assignment_resilient, Statement::Assignment),
map(solve_call_resilient, Statement::SolveCall),
))(input)
}
fn type_decl_resilient(input: &str) -> IResult<&str, TypeDecl> {
let (input, _) = ws(tag("type"))(input)?;
let (input, name) = ws(type_name)(input)?;
let (input, _) = ws(char(':'))(input)?;
let (input, base_type) = ws(base_type)(input)?;
let (input, fields) = match opt(delimited(
ws(char('{')),
field_decl_list_resilient,
ws(char('}'))
))(input) {
Ok((input, fields)) => (input, fields.unwrap_or_default()),
Err(_) => {
let (input, _) = skip_to_closing_brace(input)?;
(input, Vec::new())
}
};
Ok((input, TypeDecl {
name: name.to_string(),
base_type,
fields,
}))
}
fn enum_decl_resilient(input: &str) -> IResult<&str, EnumDecl> {
let (input, _) = ws(tag("enum"))(input)?;
let (input, name) = ws(type_name)(input)?;
let (input, _) = ws(char('{'))(input)?;
let (input, variants) = enum_variant_list_resilient(input)?;
let (input, _) = ws(char('}'))(input)?;
Ok((input, EnumDecl {
name: name.to_string(),
variants,
}))
}
fn instance_or_assignment_resilient(input: &str) -> IResult<&str, Assignment> {
if let Ok((remaining, type_name)) = ws(type_name)(input) {
if let Ok((remaining, instance_name)) = ws(identifier)(remaining) {
if let Ok((remaining, _)) = ws(char('='))(remaining) {
let (input, expr) = ws(expr_resilient)(remaining)?;
return Ok((input, Assignment {
type_name: type_name.to_string(),
name: instance_name.to_string(),
value: expr,
}));
} else if let Ok((remaining, _)) = ws(char('{'))(remaining) {
let (input, fields) = field_assign_list_resilient(remaining)?;
let (input, _) = ws(char('}'))(input)?;
return Ok((input, Assignment {
type_name: type_name.to_string(),
name: instance_name.to_string(),
value: Expr::AnonymousObjectLiteral(fields),
}));
}
}
}
if let Ok((remaining, prim_type)) = ws(alt((
tag("int"), tag("float"), tag("string"), tag("bool")
)))(input) {
if let Ok((remaining, var_name)) = ws(identifier)(remaining) {
if let Ok((remaining, _)) = ws(char('='))(remaining) {
let (input, expr) = ws(expr_resilient)(remaining)?;
return Ok((input, Assignment {
type_name: prim_type.to_string(),
name: var_name.to_string(),
value: expr,
}));
}
}
}
assignment_resilient(input)
}
fn solve_call_resilient(input: &str) -> IResult<&str, SolveCall> {
solve_call(input)
}
fn assignment_resilient(input: &str) -> IResult<&str, Assignment> {
let (input, name) = ws(identifier)(input)?;
let (input, _) = ws(char('='))(input)?;
let (input, value) = ws(expr_resilient)(input)?;
Ok((input, Assignment {
type_name: String::new(), name: name.to_string(),
value,
}))
}
fn field_decl_list_resilient(input: &str) -> IResult<&str, Vec<FieldDecl>> {
let mut fields = Vec::new();
let mut remaining = input;
if let Ok((new_input, _)) = multispace_and_comments(remaining) {
remaining = new_input;
}
while !remaining.is_empty() && !remaining.starts_with('}') {
match field_decl_resilient(remaining) {
Ok((new_input, field)) => {
fields.push(field);
remaining = new_input;
if let Ok((new_input, _)) = opt(ws(char(',')))(remaining) {
remaining = new_input;
}
}
Err(_) => {
remaining = skip_to_next_field_or_end(remaining);
}
}
}
Ok((remaining, fields))
}
fn field_decl_resilient(input: &str) -> IResult<&str, FieldDecl> {
let (input, name) = ws(identifier)(input)?;
let (input, _) = ws(char(':'))(input)?;
let (input, type_ref) = ws(type_ref)(input)?;
let (input, is_optional) = opt(ws(char('?')))(input)?;
let (input, default) = if let Ok((new_input, _)) = ws(char('='))(input) {
match expr_resilient(new_input) {
Ok((new_input, expr)) => (new_input, Some(expr)),
Err(_) => {
let skipped_input = skip_to_next_field_or_end(new_input);
(skipped_input, None)
}
}
} else {
(input, None)
};
Ok((input, FieldDecl {
name: name.to_string(),
type_ref,
is_optional: is_optional.is_some(),
default,
}))
}
fn field_assign_list_resilient(input: &str) -> IResult<&str, Vec<FieldAssign>> {
let mut fields = Vec::new();
let mut remaining = input;
if let Ok((new_input, _)) = multispace_and_comments(remaining) {
remaining = new_input;
}
while !remaining.is_empty() && !remaining.starts_with('}') {
match ws(field_assign_resilient)(remaining) {
Ok((new_input, field)) => {
fields.push(field);
remaining = new_input;
if let Ok((new_input, _)) = opt(ws(char(',')))(remaining) {
remaining = new_input;
}
}
Err(_) => {
remaining = skip_to_next_field_or_end(remaining);
}
}
}
Ok((remaining, fields))
}
fn field_assign_resilient(input: &str) -> IResult<&str, FieldAssign> {
let (input, name) = ws(identifier)(input)?;
let (input, _) = ws(char(':'))(input)?;
let (input, value) = ws(expr_resilient)(input)?;
Ok((input, FieldAssign {
name: name.to_string(),
value,
}))
}
fn enum_variant_list_resilient(input: &str) -> IResult<&str, Vec<EnumVariant>> {
let mut variants = Vec::new();
let mut remaining = input;
if let Ok((new_input, _)) = multispace_and_comments(remaining) {
remaining = new_input;
}
while !remaining.is_empty() && !remaining.starts_with('}') {
match ws(enum_variant)(remaining) {
Ok((new_input, variant)) => {
variants.push(variant);
remaining = new_input;
if let Ok((new_input, _)) = opt(ws(char(',')))(remaining) {
remaining = new_input;
}
}
Err(_) => {
remaining = skip_to_next_field_or_end(remaining);
}
}
}
Ok((remaining, variants))
}
fn expr_resilient(input: &str) -> IResult<&str, Expr> {
match expr(input) {
Ok(result) => Ok(result),
Err(_) => {
alt((
map(identifier, |s| Expr::Identifier(s.to_string())),
map(literal, Expr::Literal),
map(take_while1(|c: char| !c.is_whitespace() && c != ',' && c != '}'),
|s: &str| Expr::Identifier(format!("__error__{}", s))),
))(input)
}
}
}
fn skip_to_next_statement(input: &str) -> &str {
let mut chars = input.chars();
let mut pos = 0;
while let Some(ch) = chars.next() {
if ch == '\n' {
let remaining = &input[pos + ch.len_utf8()..];
if let Ok((_, _)) = multispace_and_comments(remaining) {
if remaining.trim_start().starts_with("type ") ||
remaining.trim_start().starts_with("enum ") ||
is_instance_decl_start(remaining.trim_start()) {
return remaining.trim_start();
}
}
}
pos += ch.len_utf8();
}
"" }
fn skip_to_closing_brace(input: &str) -> IResult<&str, ()> {
let mut brace_count = 1;
let mut pos = 0;
for ch in input.chars() {
match ch {
'{' => brace_count += 1,
'}' => {
brace_count -= 1;
if brace_count == 0 {
return Ok((&input[pos + 1..], ()));
}
}
_ => {}
}
pos += ch.len_utf8();
}
Err(NomErr::Error(nom::error::Error::new(input, ErrorKind::Char)))
}
fn skip_to_next_field_or_end(input: &str) -> &str {
let mut pos = 0;
let mut brace_depth = 0;
let mut paren_depth = 0;
let mut in_string = false;
let mut escape_next = false;
for ch in input.chars() {
if escape_next {
escape_next = false;
pos += ch.len_utf8();
continue;
}
match ch {
'\\' if in_string => escape_next = true,
'"' => in_string = !in_string,
'{' if !in_string => brace_depth += 1,
'}' if !in_string => {
if brace_depth == 0 {
return &input[pos..];
}
brace_depth -= 1;
},
'(' if !in_string => paren_depth += 1,
')' if !in_string => paren_depth -= 1,
',' if !in_string && brace_depth == 0 && paren_depth == 0 => {
return &input[pos..];
},
_ => {}
}
pos += ch.len_utf8();
}
""
}
fn is_instance_decl_start(input: &str) -> bool {
if let Ok((remaining, _)) = type_name(input) {
if let Ok((remaining, _)) = ws(identifier)(remaining) {
if let Ok((_, _)) = ws(char('{'))(remaining) {
return true;
}
}
}
false
}
use crate::{
multispace_and_comments, type_name, identifier, base_type, field_decl, solve_call,
expr, literal, enum_variant, ws
};