|
@@ -5,11 +5,12 @@
|
|
|
// file of the crate. Do not expect to find those comments in the documentation of the crate.
|
|
|
|
|
|
//! This module parses eBPF assembly language source code.
|
|
|
-use combine::char::{alpha_num, char, digit, hex_digit, spaces, string};
|
|
|
-use combine::primitives::{Error, Info};
|
|
|
+
|
|
|
+use combine::parser::char::{alpha_num, char, digit, hex_digit, spaces, string};
|
|
|
+use combine::stream::position::{self, SourcePosition};
|
|
|
use combine::{
|
|
|
- between, eof, many, many1, one_of, optional, parser, sep_by, try, ParseError, ParseResult,
|
|
|
- Parser, State, Stream,
|
|
|
+ attempt, between, easy, eof, many, many1, one_of, optional, sep_by, EasyParser, ParseError,
|
|
|
+ Parser, Stream,
|
|
|
};
|
|
|
|
|
|
/// Operand of an instruction.
|
|
@@ -34,16 +35,18 @@ pub struct Instruction {
|
|
|
pub operands: Vec<Operand>,
|
|
|
}
|
|
|
|
|
|
-fn ident<I>(input: I) -> ParseResult<String, I>
|
|
|
+fn ident<I>() -> impl Parser<I, Output = String>
|
|
|
where
|
|
|
- I: Stream<Item = char>,
|
|
|
+ I: Stream<Token = char>,
|
|
|
+ I::Error: ParseError<I::Token, I::Range, I::Position>,
|
|
|
{
|
|
|
- many1(alpha_num()).parse_stream(input)
|
|
|
+ many1(alpha_num())
|
|
|
}
|
|
|
|
|
|
-fn integer<I>(input: I) -> ParseResult<i64, I>
|
|
|
+fn integer<I>() -> impl Parser<I, Output = i64>
|
|
|
where
|
|
|
- I: Stream<Item = char>,
|
|
|
+ I: Stream<Token = char>,
|
|
|
+ I::Error: ParseError<I::Token, I::Range, I::Position>,
|
|
|
{
|
|
|
let sign = optional(one_of("-+".chars())).map(|x| match x {
|
|
|
Some('-') => -1,
|
|
@@ -53,71 +56,62 @@ where
|
|
|
.with(many1(hex_digit()))
|
|
|
.map(|x: String| u64::from_str_radix(&x, 16).unwrap() as i64);
|
|
|
let dec = many1(digit()).map(|x: String| x.parse::<i64>().unwrap());
|
|
|
- (sign, try(hex).or(dec))
|
|
|
- .map(|(s, x)| s * x)
|
|
|
- .parse_stream(input)
|
|
|
+ (sign, attempt(hex).or(dec)).map(|(s, x)| s * x)
|
|
|
}
|
|
|
|
|
|
-fn register<I>(input: I) -> ParseResult<i64, I>
|
|
|
+fn register<I>() -> impl Parser<I, Output = i64>
|
|
|
where
|
|
|
- I: Stream<Item = char>,
|
|
|
+ I: Stream<Token = char>,
|
|
|
+ I::Error: ParseError<I::Token, I::Range, I::Position>,
|
|
|
{
|
|
|
char('r')
|
|
|
.with(many1(digit()))
|
|
|
.map(|x: String| x.parse::<i64>().unwrap())
|
|
|
- .parse_stream(input)
|
|
|
}
|
|
|
|
|
|
-fn operand<I>(input: I) -> ParseResult<Operand, I>
|
|
|
+fn operand<I>() -> impl Parser<I, Output = Operand>
|
|
|
where
|
|
|
- I: Stream<Item = char>,
|
|
|
+ I: Stream<Token = char>,
|
|
|
+ I::Error: ParseError<I::Token, I::Range, I::Position>,
|
|
|
{
|
|
|
- let register_operand = parser(register).map(Operand::Register);
|
|
|
- let immediate = parser(integer).map(Operand::Integer);
|
|
|
- let memory = between(
|
|
|
- char('['),
|
|
|
- char(']'),
|
|
|
- (parser(register), optional(parser(integer))),
|
|
|
- )
|
|
|
- .map(|t| Operand::Memory(t.0, t.1.unwrap_or(0)));
|
|
|
- register_operand
|
|
|
- .or(immediate)
|
|
|
- .or(memory)
|
|
|
- .parse_stream(input)
|
|
|
+ let register_operand = register().map(Operand::Register);
|
|
|
+ let immediate = integer().map(Operand::Integer);
|
|
|
+ let memory = between(char('['), char(']'), (register(), optional(integer())))
|
|
|
+ .map(|t| Operand::Memory(t.0, t.1.unwrap_or(0)));
|
|
|
+ register_operand.or(immediate).or(memory)
|
|
|
}
|
|
|
|
|
|
-fn instruction<I>(input: I) -> ParseResult<Instruction, I>
|
|
|
+fn instruction<I>() -> impl Parser<I, Output = Instruction>
|
|
|
where
|
|
|
- I: Stream<Item = char>,
|
|
|
+ I: Stream<Token = char>,
|
|
|
+ I::Error: ParseError<I::Token, I::Range, I::Position>,
|
|
|
{
|
|
|
- let operands = sep_by(parser(operand), char(',').skip(spaces()));
|
|
|
- (parser(ident).skip(spaces()), operands, spaces())
|
|
|
- .map(|t| Instruction {
|
|
|
- name: t.0,
|
|
|
- operands: t.1,
|
|
|
- })
|
|
|
- .parse_stream(input)
|
|
|
+ let operands = sep_by(operand(), char(',').skip(spaces()));
|
|
|
+ (ident().skip(spaces()), operands, spaces()).map(|t| Instruction {
|
|
|
+ name: t.0,
|
|
|
+ operands: t.1,
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
-fn format_info(info: &Info<char, &str>) -> String {
|
|
|
+fn format_info(info: &easy::Info<char, &str>) -> String {
|
|
|
match *info {
|
|
|
- Info::Token(x) => format!("{x:?}"),
|
|
|
- Info::Range(x) => format!("{x:?}"),
|
|
|
- Info::Owned(ref x) => x.clone(),
|
|
|
- Info::Borrowed(x) => x.to_string(),
|
|
|
+ easy::Info::Token(x) => format!("{x:?}"),
|
|
|
+ easy::Info::Range(x) => format!("{x:?}"),
|
|
|
+ easy::Info::Owned(ref x) => x.clone(),
|
|
|
+ easy::Info::Static(x) => x.to_string(),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-fn format_error(error: &Error<char, &str>) -> String {
|
|
|
+fn format_error(error: &easy::Error<char, &str>) -> String {
|
|
|
match *error {
|
|
|
- Error::Unexpected(ref x) => format!("unexpected {}", format_info(x)),
|
|
|
- Error::Expected(ref x) => format!("expected {}", format_info(x)),
|
|
|
- Error::Message(ref x) => format_info(x),
|
|
|
- Error::Other(ref x) => format!("{x:?}"),
|
|
|
+ easy::Error::Unexpected(ref x) => format!("unexpected {}", format_info(x)),
|
|
|
+ easy::Error::Expected(ref x) => format!("expected {}", format_info(x)),
|
|
|
+ easy::Error::Message(ref x) => format_info(x),
|
|
|
+ easy::Error::Other(ref x) => format!("{x:?}"),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-fn format_parse_error(parse_error: &ParseError<State<&str>>) -> String {
|
|
|
+fn format_parse_error(parse_error: easy::Errors<char, &str, SourcePosition>) -> String {
|
|
|
format!(
|
|
|
"Parse error at line {} column {}: {}",
|
|
|
parse_error.position.line,
|
|
@@ -136,74 +130,63 @@ fn format_parse_error(parse_error: &ParseError<State<&str>>) -> String {
|
|
|
/// The instructions are not validated and may have invalid names and operand types.
|
|
|
pub fn parse(input: &str) -> Result<Vec<Instruction>, String> {
|
|
|
match spaces()
|
|
|
- .with(many(parser(instruction)).skip(eof()))
|
|
|
- .parse(State::new(input))
|
|
|
+ .with(many(instruction()).skip(eof()))
|
|
|
+ .easy_parse(position::Stream::new(input))
|
|
|
{
|
|
|
Ok((insts, _)) => Ok(insts),
|
|
|
- Err(err) => Err(format_parse_error(&err)),
|
|
|
+ Err(err) => Err(format_parse_error(err)),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#[cfg(test)]
|
|
|
mod tests {
|
|
|
+
|
|
|
use super::{ident, instruction, integer, operand, parse, register, Instruction, Operand};
|
|
|
- use combine::{parser, Parser};
|
|
|
+ use combine::Parser;
|
|
|
|
|
|
// Unit tests for the different kinds of parsers.
|
|
|
|
|
|
#[test]
|
|
|
fn test_ident() {
|
|
|
- assert_eq!(parser(ident).parse("nop"), Ok(("nop".to_string(), "")));
|
|
|
- assert_eq!(parser(ident).parse("add32"), Ok(("add32".to_string(), "")));
|
|
|
- assert_eq!(
|
|
|
- parser(ident).parse("add32*"),
|
|
|
- Ok(("add32".to_string(), "*"))
|
|
|
- );
|
|
|
+ assert_eq!(ident().parse("nop"), Ok(("nop".to_string(), "")));
|
|
|
+ assert_eq!(ident().parse("add32"), Ok(("add32".to_string(), "")));
|
|
|
+ assert_eq!(ident().parse("add32*"), Ok(("add32".to_string(), "*")));
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn test_integer() {
|
|
|
- assert_eq!(parser(integer).parse("0"), Ok((0, "")));
|
|
|
- assert_eq!(parser(integer).parse("42"), Ok((42, "")));
|
|
|
- assert_eq!(parser(integer).parse("+42"), Ok((42, "")));
|
|
|
- assert_eq!(parser(integer).parse("-42"), Ok((-42, "")));
|
|
|
- assert_eq!(parser(integer).parse("0x0"), Ok((0, "")));
|
|
|
+ assert_eq!(integer().parse("0"), Ok((0, "")));
|
|
|
+ assert_eq!(integer().parse("42"), Ok((42, "")));
|
|
|
+ assert_eq!(integer().parse("+42"), Ok((42, "")));
|
|
|
+ assert_eq!(integer().parse("-42"), Ok((-42, "")));
|
|
|
+ assert_eq!(integer().parse("0x0"), Ok((0, "")));
|
|
|
assert_eq!(
|
|
|
- parser(integer).parse("0x123456789abcdef0"),
|
|
|
+ integer().parse("0x123456789abcdef0"),
|
|
|
Ok((0x123456789abcdef0, ""))
|
|
|
);
|
|
|
- assert_eq!(parser(integer).parse("-0x1f"), Ok((-31, "")));
|
|
|
+ assert_eq!(integer().parse("-0x1f"), Ok((-31, "")));
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn test_register() {
|
|
|
- assert_eq!(parser(register).parse("r0"), Ok((0, "")));
|
|
|
- assert_eq!(parser(register).parse("r15"), Ok((15, "")));
|
|
|
+ assert_eq!(register().parse("r0"), Ok((0, "")));
|
|
|
+ assert_eq!(register().parse("r15"), Ok((15, "")));
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn test_operand() {
|
|
|
- assert_eq!(parser(operand).parse("r0"), Ok((Operand::Register(0), "")));
|
|
|
- assert_eq!(
|
|
|
- parser(operand).parse("r15"),
|
|
|
- Ok((Operand::Register(15), ""))
|
|
|
- );
|
|
|
- assert_eq!(parser(operand).parse("0"), Ok((Operand::Integer(0), "")));
|
|
|
- assert_eq!(parser(operand).parse("42"), Ok((Operand::Integer(42), "")));
|
|
|
- assert_eq!(
|
|
|
- parser(operand).parse("[r1]"),
|
|
|
- Ok((Operand::Memory(1, 0), ""))
|
|
|
- );
|
|
|
- assert_eq!(
|
|
|
- parser(operand).parse("[r3+5]"),
|
|
|
- Ok((Operand::Memory(3, 5), ""))
|
|
|
- );
|
|
|
+ assert_eq!(operand().parse("r0"), Ok((Operand::Register(0), "")));
|
|
|
+ assert_eq!(operand().parse("r15"), Ok((Operand::Register(15), "")));
|
|
|
+ assert_eq!(operand().parse("0"), Ok((Operand::Integer(0), "")));
|
|
|
+ assert_eq!(operand().parse("42"), Ok((Operand::Integer(42), "")));
|
|
|
+ assert_eq!(operand().parse("[r1]"), Ok((Operand::Memory(1, 0), "")));
|
|
|
+ assert_eq!(operand().parse("[r3+5]"), Ok((Operand::Memory(3, 5), "")));
|
|
|
assert_eq!(
|
|
|
- parser(operand).parse("[r3+0x1f]"),
|
|
|
+ operand().parse("[r3+0x1f]"),
|
|
|
Ok((Operand::Memory(3, 31), ""))
|
|
|
);
|
|
|
assert_eq!(
|
|
|
- parser(operand).parse("[r3-0x1f]"),
|
|
|
+ operand().parse("[r3-0x1f]"),
|
|
|
Ok((Operand::Memory(3, -31), ""))
|
|
|
);
|
|
|
}
|
|
@@ -211,7 +194,7 @@ mod tests {
|
|
|
#[test]
|
|
|
fn test_instruction() {
|
|
|
assert_eq!(
|
|
|
- parser(instruction).parse("exit"),
|
|
|
+ instruction().parse("exit"),
|
|
|
Ok((
|
|
|
Instruction {
|
|
|
name: "exit".to_string(),
|
|
@@ -222,7 +205,7 @@ mod tests {
|
|
|
);
|
|
|
|
|
|
assert_eq!(
|
|
|
- parser(instruction).parse("call 2"),
|
|
|
+ instruction().parse("call 2"),
|
|
|
Ok((
|
|
|
Instruction {
|
|
|
name: "call".to_string(),
|
|
@@ -233,7 +216,7 @@ mod tests {
|
|
|
);
|
|
|
|
|
|
assert_eq!(
|
|
|
- parser(instruction).parse("addi r1, 2"),
|
|
|
+ instruction().parse("addi r1, 2"),
|
|
|
Ok((
|
|
|
Instruction {
|
|
|
name: "addi".to_string(),
|
|
@@ -244,7 +227,7 @@ mod tests {
|
|
|
);
|
|
|
|
|
|
assert_eq!(
|
|
|
- parser(instruction).parse("ldxb r2, [r1+12]"),
|
|
|
+ instruction().parse("ldxb r2, [r1+12]"),
|
|
|
Ok((
|
|
|
Instruction {
|
|
|
name: "ldxb".to_string(),
|
|
@@ -255,7 +238,7 @@ mod tests {
|
|
|
);
|
|
|
|
|
|
assert_eq!(
|
|
|
- parser(instruction).parse("lsh r3, 0x8"),
|
|
|
+ instruction().parse("lsh r3, 0x8"),
|
|
|
Ok((
|
|
|
Instruction {
|
|
|
name: "lsh".to_string(),
|
|
@@ -266,7 +249,7 @@ mod tests {
|
|
|
);
|
|
|
|
|
|
assert_eq!(
|
|
|
- parser(instruction).parse("jne r3, 0x8, +37"),
|
|
|
+ instruction().parse("jne r3, 0x8, +37"),
|
|
|
Ok((
|
|
|
Instruction {
|
|
|
name: "jne".to_string(),
|
|
@@ -282,7 +265,7 @@ mod tests {
|
|
|
|
|
|
// Whitespace between operands is optional.
|
|
|
assert_eq!(
|
|
|
- parser(instruction).parse("jne r3,0x8,+37"),
|
|
|
+ instruction().parse("jne r3,0x8,+37"),
|
|
|
Ok((
|
|
|
Instruction {
|
|
|
name: "jne".to_string(),
|
|
@@ -633,7 +616,7 @@ exit
|
|
|
assert_eq!(
|
|
|
parse("exit\n^"),
|
|
|
Err(
|
|
|
- "Parse error at line 2 column 1: unexpected '^', expected end of input".to_string()
|
|
|
+ "Parse error at line 2 column 1: unexpected '^', expected letter or digit, expected whitespaces, expected 'r', expected '-', expected '+', expected '[', expected end of input".to_string()
|
|
|
)
|
|
|
);
|
|
|
}
|