use crate::parse::ast;
use crate::parse::lexer;
use crate::parse::token;
use lexer::LexicalError;
use num_rational::Ratio;
grammar<'input>(input: &'input str);
extern {
type Location = usize;
type Error = LexicalError;
enum lexer::LexTok<'input> {
TokNewline => lexer::LexTok::Newline,
TokDot => lexer::LexTok::Dot,
TokStar => lexer::LexTok::Star,
TokMinus => lexer::LexTok::Minus,
TokPercent => lexer::LexTok::Percent,
TokString => lexer::LexTok::String(<&'input str>),
TokInlineComment => lexer::LexTok::InlineComment(<&'input str>),
TokComment => lexer::LexTok::Comment(<&'input str>),
TokInteger => lexer::LexTok::Integer(<&'input str>),
TokLetters => lexer::LexTok::Letters(<&'input str>),
TokWhitespace => lexer::LexTok::Whitespace(<&'input str>)
}
}
Checksum: token::Checksum = <left:@L> <star:TokStar> <checksum:TokInteger> <right:@R> =>? Ok(token::Checksum {
inner: checksum.parse::<u8>().map_err(|e| LexicalError::ParseIntError(e, left + 1, right))?,
span: ast::Span(left, right)
});
Field: token::Field<'input> = {
<left:@L> <letters:TokLetters> <value:TokInteger> <right:@R> =>? {
Ok(token::Field {
letters,
value: token::Value::Integer(value.parse::<usize>().map_err(|e| LexicalError::ParseIntError(e, left + letters.len(), right))?),
raw_value: vec![value],
span: ast::Span(left, right)
})
},
<left:@L> <letters:TokLetters> <neg:TokMinus> <value:TokInteger> <right:@R> =>? {
let value_start = left + letters.len() + 1;
let value_end = right;
Ok(token::Field {
letters,
value: token::Value::Rational(-value.parse::<Ratio<i64>>().map_err(|e| LexicalError::ParseRatioError(e, value_start, value_end))?),
raw_value: vec!["-", value],
span: ast::Span(left, right)
})
},
<left:@L> <letters:TokLetters> <neg:TokMinus?> <lhs:TokInteger> <dot:TokDot> <rhs:TokInteger?> <right:@R> =>? {
let lhs_start = left + letters.len() + neg.as_ref().map(|_| 1).unwrap_or(0);
let lhs_end = lhs_start + lhs.len();
let rhs_start = lhs_end + 1;
let rhs_end = rhs_start + rhs.map(|x| x.len()).unwrap_or(0);
Ok(token::Field {
letters,
value: token::Value::Rational(lhs.parse::<Ratio<i64>>()
.map_err(|e| LexicalError::ParseRatioError(e, lhs_start, lhs_end))
.and_then(|lhs| if let Some(rhs_str) = rhs {
rhs_str.parse::<i64>()
.map(|rhs| Ratio::new(rhs, 10i64.pow(rhs_str.len() as u32)))
.map(|rhs| lhs + rhs)
.map(|value| if neg.is_some() { -value } else { value })
.map_err(|e| LexicalError::ParseIntError(e, rhs_start, rhs_end))
} else {
Ok(lhs)
})?),
raw_value: if neg.is_some() { vec!["-", lhs, ".", rhs.unwrap_or("")] } else { vec![".", rhs.unwrap_or("")] },
span: ast::Span(left, right)
})
},
<left:@L> <letters:TokLetters> <neg:TokMinus?> <dot:TokDot> <rhs_str:TokInteger> <right:@R> =>? {
let rhs_start = left + letters.len() + neg.as_ref().map(|_| 1).unwrap_or(0) + 1;
let rhs_end = right;
Ok(token::Field {
letters,
value: token::Value::Rational(rhs_str.parse::<i64>()
.map(|rhs| Ratio::new(rhs, 10i64.pow(rhs_str.len() as u32)))
.map(|rhs| if neg.is_some() { -rhs } else { rhs })
.map_err(|e| LexicalError::ParseIntError(e, rhs_start, rhs_end))?),
raw_value: if neg.is_some() { vec!["-", ".", rhs_str] } else { vec![".", rhs_str] },
span: ast::Span(left, right)
})
},
<left:@L> <letters:TokLetters> <string:TokString> <right:@R> => token::Field {
letters,
value: token::Value::String(string),
raw_value: vec![string],
span: ast::Span(left, right)
}
};
Comment: token::Comment<'input> = <left:@L> <comment:TokComment> => token::Comment {
inner: comment,
pos: left,
};
InlineComment: token::InlineComment<'input> = <left:@L> <comment:TokInlineComment> => token::InlineComment {
inner: comment,
pos: left,
};
Whitespace: token::Whitespace<'input> = <left:@L> <whitespace:TokWhitespace> => token::Whitespace {
inner: whitespace,
pos: left,
};
Line: ast::Line<'input> = <left:@L> <fields:(InlineComment* (Whitespace InlineComment*)* Field)*> <checksum:(InlineComment* (Whitespace InlineComment*)* Checksum)?> <comment:(InlineComment* (Whitespace InlineComment*)* Comment)?> <whitespace:((Whitespace InlineComment*)* Whitespace)?> <inline_comment:InlineComment*> <right:@R> => ast::Line {
fields,
checksum,
comment,
whitespace,
inline_comment,
span: ast::Span(left, right)
};
Newline: token::Newline = <left:@L> TokNewline => token::Newline { pos: left };
pub File: ast::File<'input> = {
<left:@L> <lines:(Line Newline)*> <last_line:Line> <right:@R> => ast::File {
start_percent: false,
lines,
last_line: if last_line.fields.is_empty() && last_line.checksum.is_none() && last_line.comment.is_none() && last_line.inline_comment.is_empty() {
None
} else {
Some(last_line)
},
end_percent: false,
span: ast::Span(left, right)
},
<left:@L> <start_percent:TokPercent> <lines:(Line Newline)*> <last_line:Line> <end_percent:TokPercent> <right:@R> => ast::File {
start_percent: false,
lines,
last_line: if last_line.fields.is_empty() && last_line.checksum.is_none() && last_line.comment.is_none() && last_line.inline_comment.is_empty() {
None
} else {
Some(last_line)
},
end_percent: false,
span: ast::Span(left, right)
},
};
pub Snippet: ast::Snippet<'input> = {
<left:@L> <lines:(Line Newline)*> <last_line:Line> <right:@R> => ast::Snippet {
lines,
last_line: if last_line.fields.is_empty() && last_line.checksum.is_none() && last_line.comment.is_none() && last_line.inline_comment.is_empty() {
None
} else {
Some(last_line)
},
span: ast::Span(left, right)
},
};