1
0
Fork 0
mirror of synced 2025-09-24 04:40:05 +00:00
ZoKrates/src/parser.rs
2017-11-26 00:37:53 +01:00

1588 lines
58 KiB
Rust

//!
//! @file parser.rs
//! @author Dennis Kuhnert <dennis.kuhnert@campus.tu-berlin.de>
//! @author Jacob Eberhardt <jacob.eberhardt@tu-berlin.de>
//! @date 2017
// Grammar:
// <prog> ::= <functions>
//
// <functions> ::= <function> <functions>
//
// <function> ::= `def' <ide> `(' <arguments> `):\\n' <stat-list>
//
// <arguments> ::= <ide> <more-args> | $\varepsilon$
//
// <more-args> ::= `,' <ide> <more-args> | $\varepsilon$
//
// <stat-list> ::= <statement> <stat-list> | <return>
//
// <expressions> ::= <expr> <more-expr> | $\varepsilon$
//
// <more-exprs> ::= `,' <ide> <more-args> | $\varepsilon$
//
// <statement> ::= <ide> <statement'>
// | `if' <expr> <comparator> <expr> `then' <expr> `else' <expr> `fi' <expr'> `==' <expr> `\\n'
// | `(' <expr> `)' <term'> <expr'> `==' <expr> `\\n'
// | <num> <term'> <expr'> `==' <expr> `\\n'
// | `#' <ide> `=' <expr> `\\n'
//
// <statement'> ::= `=' <expr> `\\n'
// | <term'> <expr'> `==' <expr> `\\n'
//
// <expr> ::= `if' <expr> <comparator> <expr> `then' <expr> `else' <expr> `fi' <expr'>
// | `(' <expr> `)' <term'> <expr'>
// | <ide> <term'> <expr'>
// | <num> <term'> <expr'>
// | <ide> `(' <expressions> `)' <term'> <expr'>
//
// <expr'> ::= `+' <term> <expr'>
// | `-' <term> <expr'>
// | `**' <num> <term'> <expr'>
// | $\varepsilon$
//
// <term> ::= <factor> <term'>
//
// <term'> ::= `*' <term>
// | `/' <term>
// | $\varepsilon$
//
// <factor> ::= `if' <expr> <comparator> <expr> `then' <expr> `else' <expr> `fi' <expr'> `**' <num>
// | `(' <expr> `)' <factor'>
// | <ide> <factor'>
// | <num> <factor'>
// | <ide> `(' <expressions> `)' <factor'>
//
// <factor'> ::= <term'> <expr'> `**' <num>
// | $\varepsilon$
//
// <comparator> ::= `<' | `<=' | `==' | `>=' | `>'
//
// <num> ::= `d' <num> | `d'
//
// <ide> ::= `l' <trail> | `l'
//
// <trail> ::= `d' <trail> | `l' <trail> | `d' | `l'
//
use std::fmt;
use std::io::{BufReader, Lines};
use std::io::prelude::*;
use std::fs::File;
use field::Field;
use absy::*;
#[derive(Clone, PartialEq)]
struct Position {
line: usize,
col: usize,
}
impl Position {
fn col(&self, delta: isize) -> Position {
assert!(self.col <= isize::max_value() as usize);
assert!(self.col as isize >= delta);
Position {
line: self.line,
col: (self.col as isize + delta) as usize,
}
}
}
impl fmt::Display for Position {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}:{}", self.line, self.col)
}
}
impl fmt::Debug for Position {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
}
}
#[derive(PartialEq)]
pub struct Error<T: Field> {
expected: Vec<Token<T>>,
got: Token<T>,
pos: Position,
}
impl<T: Field> fmt::Display for Error<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Error at {}: Expected one of {:?}, got {:?}",
self.pos.col(-(self.got.to_string().len() as isize)),
self.expected,
self.got
)
}
}
impl<T: Field> fmt::Debug for Error<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
}
}
#[derive(PartialEq)]
enum Token<T: Field> {
Open,
Close,
Comma,
Colon,
Hash,
Eq,
Return,
Def,
If,
Then,
Else,
Fi,
For,
In,
Dotdot,
Do,
Endfor,
Lt,
Le,
Eqeq,
Ge,
Gt,
Add,
Sub,
Mult,
Div,
Pow,
Private,
Ide(String),
Num(T),
Unknown(String),
InlineComment(String),
// following used for error messages
ErrIde,
ErrNum,
}
impl<T: Field> fmt::Display for Token<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Token::Open => write!(f, "("),
Token::Close => write!(f, ")"),
Token::Comma => write!(f, ","),
Token::Colon => write!(f, ":"),
Token::Hash => write!(f, "#"),
Token::Eq => write!(f, "="),
Token::Def => write!(f, "def"),
Token::Return => write!(f, "return"),
Token::If => write!(f, "if"),
Token::Then => write!(f, "then"),
Token::Else => write!(f, "else"),
Token::Fi => write!(f, "fi"),
Token::For => write!(f, "for"),
Token::In => write!(f, "in"),
Token::Dotdot => write!(f, ".."),
Token::Do => write!(f, "do"),
Token::Endfor => write!(f, "endfor"),
Token::Lt => write!(f, "<"),
Token::Le => write!(f, "<="),
Token::Eqeq => write!(f, "=="),
Token::Ge => write!(f, ">="),
Token::Gt => write!(f, ">"),
Token::Add => write!(f, "+"),
Token::Sub => write!(f, "-"),
Token::Mult => write!(f, "*"),
Token::Div => write!(f, "/"),
Token::Pow => write!(f, "**"),
Token::Private => write!(f, "private"),
Token::Ide(ref x) => write!(f, "{}", x),
Token::Num(ref x) => write!(f, "{}", x),
Token::Unknown(ref x) => write!(f, "{}", x),
Token::InlineComment(ref x) => write!(f, "// {}", x),
Token::ErrIde => write!(f, "identifier"),
Token::ErrNum => write!(f, "number"),
}
}
}
impl<T: Field> fmt::Debug for Token<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ref t @ Token::ErrIde | ref t @ Token::ErrNum => write!(f, "{}", t),
ref t => write!(f, "`{}`", t),
}
}
}
fn parse_num<T: Field>(input: &String, pos: &Position) -> (Token<T>, String, Position) {
let mut end = 0;
loop {
match input.chars().nth(end) {
Some(x) => match x {
'0'...'9' => end += 1,
_ => break,
},
None => break,
}
}
assert!(end > 0);
(
Token::Num(T::from(&input[0..end])),
input[end..].to_string(),
Position {
line: pos.line,
col: pos.col + end,
},
)
}
fn parse_ide<T: Field>(input: &String, pos: &Position) -> (Token<T>, String, Position) {
assert!(match input.chars().next().unwrap() {
'a'...'z' | 'A'...'Z' => true,
_ => false,
});
let mut end = 1;
loop {
match input.chars().nth(end) {
Some(x) => match x {
'a'...'z' | 'A'...'Z' | '0'...'9' => end += 1,
_ => break,
},
None => break,
}
}
(
Token::Ide(input[0..end].to_string()),
input[end..].to_string(),
Position {
line: pos.line,
col: pos.col + end,
},
)
}
fn skip_whitespaces(input: &String) -> usize {
let mut i = 0;
loop {
match input.chars().nth(i) {
Some(' ') | Some('\t') => i += 1,
_ => return i,
}
}
}
fn next_token<T: Field>(input: &String, pos: &Position) -> (Token<T>, String, Position) {
let offset = skip_whitespaces(input);
match input.chars().nth(offset) {
Some('(') => (
Token::Open,
input[offset + 1..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 1,
},
),
Some(')') => (
Token::Close,
input[offset + 1..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 1,
},
),
Some(',') => (
Token::Comma,
input[offset + 1..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 1,
},
),
Some(':') => (
Token::Colon,
input[offset + 1..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 1,
},
),
Some('#') => (
Token::Hash,
input[offset + 1..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 1,
},
),
Some('=') => match input.chars().nth(offset + 1) {
Some('=') => (
Token::Eqeq,
input[offset + 2..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 2,
},
),
_ => (
Token::Eq,
input[offset + 1..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 1,
},
),
},
Some('<') => match input.chars().nth(offset + 1) {
Some('=') => (
Token::Le,
input[offset + 2..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 2,
},
),
_ => (
Token::Lt,
input[offset + 1..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 1,
},
),
},
Some('>') => match input.chars().nth(offset + 1) {
Some('=') => (
Token::Ge,
input[offset + 2..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 2,
},
),
_ => (
Token::Gt,
input[offset + 1..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 1,
},
),
},
Some('+') => (
Token::Add,
input[offset + 1..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 1,
},
),
Some('-') => (
Token::Sub,
input[offset + 1..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 1,
},
),
Some('*') => match input.chars().nth(offset + 1) {
Some('*') => (
Token::Pow,
input[offset + 2..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 2,
},
),
_ => (
Token::Mult,
input[offset + 1..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 1,
},
),
},
Some('/') => match input.chars().nth(offset + 1) {
Some('/') => (
Token::InlineComment(input[offset + 2..].to_string()),
"".to_string(),
Position {
line: pos.line,
col: pos.col + offset + input[offset + 2..].len(),
},
),
_ => (
Token::Div,
input[offset + 1..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 1,
},
),
},
Some(_) if input[offset..].starts_with("def ") => (
Token::Def,
input[offset + 4..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 4,
},
),
Some(_) if input[offset..].starts_with("return ") => (
Token::Return,
input[offset + 7..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 7,
},
),
Some(_) if input[offset..].starts_with("if ") => (
Token::If,
input[offset + 3..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 3,
},
),
Some(_) if input[offset..].starts_with("then ") => (
Token::Then,
input[offset + 5..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 5,
},
),
Some(_) if input[offset..].starts_with("else ") => (
Token::Else,
input[offset + 5..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 5,
},
),
Some(_) if input[offset..].starts_with("fi ") || input[offset..].to_string() == "fi" => (
Token::Fi,
input[offset + 2..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 2,
},
),
Some(_) if input[offset..].starts_with("for ") => (
Token::For,
input[offset + 4..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 4,
},
),
Some(_) if input[offset..].starts_with("in ") => (
Token::In,
input[offset + 3..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 3,
},
),
Some(_) if input[offset..].starts_with("..") => (
Token::Dotdot,
input[offset + 2..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 2,
},
),
Some(_) if input[offset..].starts_with("do ") || input[offset..].to_string() == "do" => (
Token::Do,
input[offset + 2..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 2,
},
),
Some(_) if input[offset..].starts_with("endfor ") || input[offset..].to_string() == "endfor" => {
(
Token::For,
input[offset + 6..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 6,
},
)
}
Some(_) if input[offset..].starts_with("private ") => (
Token::Private,
input[offset + 8..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 8,
},
),
Some(x) => match x {
'0'...'9' => parse_num(
&input[offset..].to_string(),
&Position {
line: pos.line,
col: pos.col + offset,
},
),
'a'...'z' | 'A'...'Z' => parse_ide(
&input[offset..].to_string(),
&Position {
line: pos.line,
col: pos.col + offset,
},
),
_ => (
Token::Unknown(x.to_string()),
input[offset + 1..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 1,
},
),
},
None => (
Token::Unknown("".to_string()),
input[offset..].to_string(),
Position {
line: pos.line,
col: pos.col + offset,
},
),
}
}
fn parse_then_else<T: Field>(
cond: Condition<T>,
input: &String,
pos: &Position,
) -> Result<(Expression<T>, String, Position), Error<T>> {
match next_token(input, pos) {
(Token::Then, s5, p5) => match parse_expr(&s5, &p5) {
Ok((e6, s6, p6)) => match next_token(&s6, &p6) {
(Token::Else, s7, p7) => match parse_expr(&s7, &p7) {
Ok((e8, s8, p8)) => match next_token(&s8, &p8) {
(Token::Fi, s9, p9) => {
parse_expr1(Expression::IfElse(box cond, box e6, box e8), s9, p9)
}
(t10, _, p10) => Err(Error {
expected: vec![Token::Fi],
got: t10,
pos: p10,
}),
},
Err(err) => Err(err),
},
(t7, _, p7) => Err(Error {
expected: vec![Token::Else],
got: t7,
pos: p7,
}),
},
Err(err) => Err(err),
},
(t5, _, p5) => Err(Error {
expected: vec![Token::Then],
got: t5,
pos: p5,
}),
}
}
fn parse_if_then_else<T: Field>(
input: &String,
pos: &Position,
) -> Result<(Expression<T>, String, Position), Error<T>> {
match next_token(input, pos) {
(Token::If, s1, p1) => match parse_expr(&s1, &p1) {
Ok((e2, s2, p2)) => match next_token(&s2, &p2) {
(Token::Lt, s3, p3) => match parse_expr(&s3, &p3) {
Ok((e4, s4, p4)) => parse_then_else(Condition::Lt(e2, e4), &s4, &p4),
Err(err) => Err(err),
},
(Token::Le, s3, p3) => match parse_expr(&s3, &p3) {
Ok((e4, s4, p4)) => parse_then_else(Condition::Le(e2, e4), &s4, &p4),
Err(err) => Err(err),
},
(Token::Eqeq, s3, p3) => match parse_expr(&s3, &p3) {
Ok((e4, s4, p4)) => parse_then_else(Condition::Eq(e2, e4), &s4, &p4),
Err(err) => Err(err),
},
(Token::Ge, s3, p3) => match parse_expr(&s3, &p3) {
Ok((e4, s4, p4)) => parse_then_else(Condition::Ge(e2, e4), &s4, &p4),
Err(err) => Err(err),
},
(Token::Gt, s3, p3) => match parse_expr(&s3, &p3) {
Ok((e4, s4, p4)) => parse_then_else(Condition::Gt(e2, e4), &s4, &p4),
Err(err) => Err(err),
},
(t3, _, p3) => Err(Error {
expected: vec![Token::Lt, Token::Le, Token::Eqeq, Token::Ge, Token::Gt],
got: t3,
pos: p3,
}),
},
Err(err) => Err(err),
},
(t1, _, p1) => Err(Error {
expected: vec![Token::If],
got: t1,
pos: p1,
}),
}
}
fn parse_factor1<T: Field>(
expr: Expression<T>,
input: String,
pos: Position,
) -> Result<(Expression<T>, String, Position), Error<T>> {
match parse_term1(expr.clone(), input.clone(), pos.clone()) {
Ok((e1, s1, p1)) => match parse_expr1(e1, s1, p1) {
Ok((e2, s2, p2)) => match next_token::<T>(&s2, &p2) {
(Token::Pow, s3, p3) => match next_token(&s3, &p3) {
(Token::Num(x), s4, p4) => {
Ok((Expression::Pow(box e2, box Expression::Number(x)), s4, p4))
}
(t4, _, p4) => Err(Error {
expected: vec![Token::ErrNum],
got: t4,
pos: p4,
}),
},
_ => Ok((expr, input, pos)),
},
Err(err) => Err(err),
},
Err(err) => Err(err),
}
}
fn parse_factor<T: Field>(
input: &String,
pos: &Position,
) -> Result<(Expression<T>, String, Position), Error<T>> {
match next_token(input, pos) {
(Token::If, ..) => parse_if_then_else(input, pos),
(Token::Open, s1, p1) => match parse_expr(&s1, &p1) {
Ok((e2, s2, p2)) => match next_token(&s2, &p2) {
(Token::Close, s3, p3) => parse_factor1(e2, s3, p3),
(t3, _, p3) => Err(Error {
expected: vec![Token::Close],
got: t3,
pos: p3,
}),
},
Err(err) => Err(err),
},
(Token::Ide(x), s1, p1) => match next_token::<T>(&s1, &p1) {
(Token::Open, s2, p2) => parse_function_call(x, s2, p2),
_ => parse_factor1(Expression::Identifier(x), s1, p1),
},
(Token::Num(x), s1, p1) => parse_factor1(Expression::Number(x), s1, p1),
(t1, _, p1) => Err(Error {
expected: vec![Token::If, Token::Open, Token::ErrIde, Token::ErrNum],
got: t1,
pos: p1,
}),
}
}
fn parse_term1<T: Field>(
expr: Expression<T>,
input: String,
pos: Position,
) -> Result<(Expression<T>, String, Position), Error<T>> {
match next_token::<T>(&input, &pos) {
(Token::Mult, s1, p1) => match parse_term(&s1, &p1) {
Ok((e, s2, p2)) => Ok((Expression::Mult(box expr, box e), s2, p2)),
Err(err) => Err(err),
},
(Token::Div, s1, p1) => match parse_term(&s1, &p1) {
Ok((e, s2, p2)) => Ok((Expression::Div(box expr, box e), s2, p2)),
Err(err) => Err(err),
},
_ => Ok((expr, input, pos)),
}
}
fn parse_term<T: Field>(
input: &String,
pos: &Position,
) -> Result<(Expression<T>, String, Position), Error<T>> {
match parse_factor(input, pos) {
Ok((e, s1, p1)) => parse_term1(e, s1, p1),
Err(err) => Err(err),
}
}
fn parse_expr1<T: Field>(
expr: Expression<T>,
input: String,
pos: Position,
) -> Result<(Expression<T>, String, Position), Error<T>> {
match next_token::<T>(&input, &pos) {
(Token::Add, s1, p1) => match parse_term(&s1, &p1) {
Ok((e2, s2, p2)) => parse_expr1(Expression::Add(box expr, box e2), s2, p2),
Err(err) => Err(err),
},
(Token::Sub, s1, p1) => match parse_term(&s1, &p1) {
Ok((e2, s2, p2)) => parse_expr1(Expression::Sub(box expr, box e2), s2, p2),
Err(err) => Err(err),
},
(Token::Pow, s1, p1) => match parse_num(&s1, &p1) {
(Token::Num(x), s2, p2) => {
match parse_term1(Expression::Pow(box expr, box Expression::Number(x)), s2, p2) {
Ok((e3, s3, p3)) => parse_expr1(e3, s3, p3),
Err(err) => Err(err),
}
}
(t2, _, p2) => Err(Error {
expected: vec![Token::ErrNum],
got: t2,
pos: p2,
}),
},
_ => Ok((expr, input, pos)),
}
}
fn parse_function_call<T: Field>(
ide: String,
input: String,
pos: Position,
) -> Result<(Expression<T>, String, Position), Error<T>> {
// function call can have 0 .. n args
let mut args = Vec::new();
let mut s: String = input;
let mut p: Position = pos;
loop {
match next_token::<T>(&s, &p) {
// no arguments
(Token::Close, s1, p1) => {
match parse_term1(Expression::FunctionCall(ide, args), s1, p1) {
Ok((e2, s2, p2)) => return parse_expr1(e2, s2, p2),
Err(err) => return Err(err),
}
}
// at least one argument
(_, _, _) => match parse_expr(&s, &p) {
Ok((e1, s1, p1)) => {
args.push(e1);
match next_token::<T>(&s1, &p1) {
(Token::Comma, s2, p2) => {
s = s2;
p = p2;
}
(Token::Close, s2, p2) => {
match parse_term1(Expression::FunctionCall(ide, args), s2, p2) {
Ok((e3, s3, p3)) => return parse_expr1(e3, s3, p3),
Err(err) => return Err(err),
}
}
(t2, _, p2) => {
return Err(Error {
expected: vec![Token::Comma, Token::Close],
got: t2,
pos: p2,
})
}
}
}
Err(err) => return Err(err),
},
}
}
}
fn parse_expr<T: Field>(
input: &String,
pos: &Position,
) -> Result<(Expression<T>, String, Position), Error<T>> {
match next_token::<T>(input, pos) {
(Token::If, ..) => parse_if_then_else(input, pos),
(Token::Open, s1, p1) => match parse_expr(&s1, &p1) {
Ok((e2, s2, p2)) => match next_token(&s2, &p2) {
(Token::Close, s3, p3) => match parse_term1(e2, s3, p3) {
Ok((e4, s4, p4)) => parse_expr1(e4, s4, p4),
Err(err) => Err(err),
},
(t3, _, p3) => Err(Error {
expected: vec![Token::Close],
got: t3,
pos: p3,
}),
},
Err(err) => Err(err),
},
(Token::Ide(x), s1, p1) => match next_token::<T>(&s1, &p1) {
(Token::Open, s2, p2) => parse_function_call(x, s2, p2),
_ => match parse_term1(Expression::Identifier(x), s1, p1) {
Ok((e2, s2, p2)) => parse_expr1(e2, s2, p2),
Err(err) => Err(err),
},
},
(Token::Num(x), s1, p1) => match parse_term1(Expression::Number(x), s1, p1) {
Ok((e2, s2, p2)) => parse_expr1(e2, s2, p2),
Err(err) => Err(err),
},
(t1, _, p1) => Err(Error {
expected: vec![Token::If, Token::Open, Token::ErrIde, Token::ErrNum],
got: t1,
pos: p1,
}),
}
}
fn parse_statement1<T: Field>(
ide: String,
input: String,
pos: Position,
) -> Result<(Statement<T>, String, Position), Error<T>> {
match next_token::<T>(&input, &pos) {
(Token::Eq, s1, p1) => match parse_expr(&s1, &p1) {
Ok((e2, s2, p2)) => match next_token(&s2, &p2) {
(Token::InlineComment(_), ref s3, _) => {
assert_eq!(s3, "");
Ok((Statement::Definition(ide, e2), s2, p2))
}
(Token::Unknown(ref t3), ref s3, _) if t3 == "" => {
assert_eq!(s3, "");
Ok((Statement::Definition(ide, e2), s2, p2))
}
(t3, _, p3) => {
println!("here {}", input);
Err(Error {
expected: vec![
Token::Add,
Token::Sub,
Token::Pow,
Token::Mult,
Token::Div,
Token::Unknown("".to_string()),
],
got: t3,
pos: p3,
})
}
},
Err(err) => Err(err),
},
_ => match parse_term1(Expression::Identifier(ide), input, pos) {
Ok((e2, s2, p2)) => match parse_expr1(e2, s2, p2) {
Ok((e3, s3, p3)) => match next_token(&s3, &p3) {
(Token::Eqeq, s4, p4) => match parse_expr(&s4, &p4) {
Ok((e5, s5, p5)) => match next_token(&s5, &p5) {
(Token::InlineComment(_), ref s6, _) => {
assert_eq!(s6, "");
Ok((Statement::Condition(e3, e5), s5, p5))
}
(Token::Unknown(ref t6), ref s6, _) if t6 == "" => {
assert_eq!(s6, "");
Ok((Statement::Condition(e3, e5), s5, p5))
}
(t6, _, p6) => Err(Error {
expected: vec![
Token::Add,
Token::Sub,
Token::Pow,
Token::Mult,
Token::Div,
Token::Unknown("".to_string()),
],
got: t6,
pos: p6,
}),
},
Err(err) => Err(err),
},
(t4, _, p4) => Err(Error {
expected: vec![Token::Eqeq],
got: t4,
pos: p4,
}),
},
Err(err) => Err(err),
},
Err(err) => Err(err),
},
}
}
fn parse_statement<T: Field>(
lines: &mut Lines<BufReader<File>>,
input: &String,
pos: &Position,
) -> Result<(Statement<T>, String, Position), Error<T>> {
match next_token::<T>(input, pos) {
(Token::Ide(x1), s1, p1) => parse_statement1(x1, s1, p1),
(Token::If, ..) | (Token::Open, ..) | (Token::Num(_), ..) => match parse_expr(input, pos) {
Ok((e2, s2, p2)) => match next_token(&s2, &p2) {
(Token::Eqeq, s3, p3) => match parse_expr(&s3, &p3) {
Ok((e4, s4, p4)) => match next_token(&s4, &p4) {
(Token::InlineComment(_), ref s5, _) => {
assert_eq!(s5, "");
Ok((Statement::Condition(e2, e4), s4, p4))
}
(Token::Unknown(ref t5), ref s5, _) if t5 == "" => {
assert_eq!(s5, "");
Ok((Statement::Condition(e2, e4), s4, p4))
}
(t5, _, p5) => Err(Error {
expected: vec![Token::Unknown("".to_string())],
got: t5,
pos: p5,
}),
},
Err(err) => Err(err),
},
(t3, _, p3) => Err(Error {
expected: vec![Token::Eqeq],
got: t3,
pos: p3,
}),
},
Err(err) => Err(err),
},
(Token::For, s1, p1) => {
match parse_ide(&s1, &p1) {
(Token::Ide(x2), s2, p2) => {
match next_token(&s2, &p2) {
(Token::In, s3, p3) => {
match next_token(&s3, &p3) {
(Token::Num(x4), s4, p4) => {
match next_token(&s4, &p4) {
(Token::Dotdot, s5, p5) => {
match next_token(&s5, &p5) {
(Token::Num(x6), s6, p6) => {
match next_token(&s6, &p6) {
(Token::Do, s7, p7) => {
match next_token(&s7, &p7) {
(
Token::InlineComment(_),
ref s8,
_,
) => {
assert_eq!(s8, "");
}
(
Token::Unknown(ref t8),
ref s8,
_,
) if t8 == "" =>
{
assert_eq!(s8, "");
}
(t8, _, p8) => {
return Err(Error {
expected: vec![
Token::Unknown(
"".to_string(),
),
],
got: t8,
pos: p8,
})
}
}
let mut current_line = p7.line;
let mut statements = Vec::new();
loop {
current_line += 1;
match lines.next() {
Some(Ok(ref x)) if x.trim().starts_with("//") || x.trim() == "" => {}, // skip
Some(Ok(ref x)) if x.trim().starts_with("endfor") => {
let offset = skip_whitespaces(x);
let s8 = x[offset + 6..].to_string();
let p8 = Position{ line: current_line, col: offset + 7 };
match next_token(&s8, &p8) {
(Token::InlineComment(_), ref s9, _) => {
assert_eq!(s9, "");
return Ok((Statement::For(x2, x4, x6, statements), s8, p8))
}
(Token::Unknown(ref t9), ref s9, _) if t9 == "" => {
assert_eq!(s9, "");
return Ok((Statement::For(x2, x4, x6, statements), s8, p8))
},
(t9, _, p9) => return Err(Error { expected: vec![Token::Unknown("1432567iuhgvfc".to_string())], got: t9 , pos: p9 }),
}
},
Some(Ok(ref x)) if !x.trim().starts_with("return") => match parse_statement(lines, x, &Position { line: current_line, col: 1 }) {
Ok((statement, ..)) => statements.push(statement),
Err(err) => return Err(err),
},
Some(Err(err)) => panic!("Error while reading Definitions: {}", err),
Some(Ok(ref x)) => {
let (t, ..) = next_token(x, &Position{ line: current_line, col: 1 });
return Err(Error { expected: vec![Token::ErrIde, Token::ErrNum, Token::If, Token::Open, Token::Hash, Token::For, Token::Endfor], got: t , pos: Position{ line: current_line, col: 1 } })
},
None => return Err(Error { expected: vec![Token::ErrIde, Token::ErrNum, Token::If, Token::Open, Token::Hash, Token::For], got: Token::Unknown("".to_string()) , pos: Position{ line: current_line, col: 1 } }),
}
}
}
(t7, _, p7) => Err(Error {
expected: vec![Token::Do],
got: t7,
pos: p7,
}),
}
}
(t6, _, p6) => Err(Error {
expected: vec![Token::ErrNum],
got: t6,
pos: p6,
}),
}
}
(t5, _, p5) => Err(Error {
expected: vec![Token::Dotdot],
got: t5,
pos: p5,
}),
}
}
(t4, _, p4) => Err(Error {
expected: vec![Token::ErrNum],
got: t4,
pos: p4,
}),
}
}
(t3, _, p3) => Err(Error {
expected: vec![Token::In],
got: t3,
pos: p3,
}),
}
}
(t2, _, p2) => Err(Error {
expected: vec![Token::ErrIde],
got: t2,
pos: p2,
}),
}
}
(Token::Hash, s1, p1) => match parse_ide(&s1, &p1) {
(Token::Ide(x2), s2, p2) => match next_token(&s2, &p2) {
(Token::Eq, s3, p3) => match parse_expr(&s3, &p3) {
Ok((e4, s4, p4)) => Ok((Statement::Compiler(x2, e4), s4, p4)),
Err(err) => Err(err),
},
(t3, _, p3) => Err(Error {
expected: vec![Token::Eq],
got: t3,
pos: p3,
}),
},
(t2, _, p2) => Err(Error {
expected: vec![Token::ErrIde],
got: t2,
pos: p2,
}),
},
(Token::Return, s1, p1) => match parse_expr(&s1, &p1) {
Ok((expr, s2, p2)) => match next_token(&s2, &p2) {
(Token::InlineComment(_), ref s3, _) => {
assert_eq!(s3, "");
Ok((Statement::Return(expr), s2, p2))
}
(Token::Unknown(ref t3), ref s3, _) if t3 == "" => {
assert_eq!(s3, "");
Ok((Statement::Return(expr), s2, p2))
}
(t4, _, p4) => Err(Error {
expected: vec![
Token::Add,
Token::Sub,
Token::Pow,
Token::Mult,
Token::Div,
Token::Unknown("".to_string()),
],
got: t4,
pos: p4,
}),
},
Err(err) => Err(err),
},
(Token::Def, _, p1) => Err(Error {
expected: vec![Token::Return],
got: Token::Def,
pos: p1,
}), // This just covers an error case: Def Token is never part of a valid statement and indicates that a return statement is missing.
(t1, _, p1) => Err(Error {
expected: vec![
Token::ErrIde,
Token::ErrNum,
Token::If,
Token::Open,
Token::Hash,
Token::Return,
],
got: t1,
pos: p1,
}),
}
}
fn parse_function<T: Field>(
mut lines: &mut Lines<BufReader<File>>,
input: &String,
pos: &Position,
) -> Result<(Function<T>, Position), Error<T>> {
let mut current_line = pos.line;
let id;
let mut args = Vec::new();
// parse function signature
match next_token(input, pos) {
(Token::Ide(x2), s2, p2) => {
id = x2;
match next_token(&s2, &p2) {
(Token::Open, s3, p3) => {
let mut s = s3;
let mut p = p3;
loop {
match next_token(&s, &p) {
(Token::Private, s4, p4) => {
match next_token(&s4, &p4) {
(Token::Ide(x), s5, p5) => {
args.push(Parameter { id: x, private: true });
match next_token(&s5, &p5) {
(Token::Comma, s6, p6) => {
s = s6;
p = p6;
}
(Token::Close, s5, p5) => match next_token(&s5, &p5) {
(Token::Colon, s6, p6) => match next_token(&s6, &p6) {
(Token::InlineComment(_), _, _) => break,
(Token::Unknown(ref x6), ..) if x6 == "" => break,
(t6, _, p6) => {
return Err(Error {
expected: vec![Token::Unknown("".to_string())],
got: t6,
pos: p6,
})
}
},
(t6, _, p6) => {
return Err(Error {
expected: vec![Token::Colon],
got: t6,
pos: p6,
})
}
},
(t5, _, p5) => {
return Err(Error {
expected: vec![Token::Comma, Token::Close],
got: t5,
pos: p5,
})
}
}
}
(t5, _, p5) => {
return Err(Error {
expected: vec![Token::Comma, Token::Close],
got: t5,
pos: p5,
})
}
}
}
(Token::Ide(x), s4, p4) => {
args.push(Parameter { id: x, private: false });
match next_token(&s4, &p4) {
(Token::Comma, s5, p5) => {
s = s5;
p = p5;
}
(Token::Close, s4, p4) => match next_token(&s4, &p4) {
(Token::Colon, s5, p5) => match next_token(&s5, &p5) {
(Token::InlineComment(_), _, _) => break,
(Token::Unknown(ref x6), ..) if x6 == "" => break,
(t6, _, p6) => {
return Err(Error {
expected: vec![Token::Unknown("".to_string())],
got: t6,
pos: p6,
})
}
},
(t5, _, p5) => {
return Err(Error {
expected: vec![Token::Colon],
got: t5,
pos: p5,
})
}
},
(t5, _, p5) => {
return Err(Error {
expected: vec![Token::Comma, Token::Close],
got: t5,
pos: p5,
})
}
}
}
(Token::Close, s4, p4) => {
// case of no parameters
match next_token(&s4, &p4) {
(Token::Colon, s5, p5) => match next_token(&s5, &p5) {
(Token::InlineComment(_), _, _) => break,
(Token::Unknown(ref x6), ..) if x6 == "" => break,
(t6, _, p6) => {
return Err(Error {
expected: vec![Token::Unknown("".to_string())],
got: t6,
pos: p6,
})
}
},
(t5, _, p5) => {
return Err(Error {
expected: vec![Token::Colon],
got: t5,
pos: p5,
})
}
}
}
(t4, _, p4) => {
return Err(Error {
expected: vec![Token::Ide(String::from("ide")), Token::Close],
got: t4,
pos: p4,
})
}
}
}
}
(t3, _, p3) => {
return Err(Error {
expected: vec![Token::Open],
got: t3,
pos: p3,
})
}
}
}
(t2, _, p2) => {
return Err(Error {
expected: vec![Token::Ide(String::from("name"))],
got: t2,
pos: p2,
})
}
}
current_line += 1;
// parse function body
let mut stats = Vec::new();
loop {
match lines.next() {
Some(Ok(ref x)) if x.trim().starts_with("//") || x.trim() == "" => {} // skip
Some(Ok(ref x)) => match parse_statement(
&mut lines,
x,
&Position {
line: current_line,
col: 1,
},
) {
Ok((statement @ Statement::Return(_), ..)) => {
stats.push(statement);
break;
}
Ok((statement, _, pos)) => {
stats.push(statement);
current_line = pos.line // update the interal line counter to continue where statement ended.
}
Err(err) => return Err(err),
},
None => panic!("Function {} does not return before program ends", id),
Some(Err(err)) => panic!("Error while reading function statements: {}", err),
}
current_line += 1;
}
match stats.last() {
Some(&Statement::Return(_)) => {}
Some(x) => panic!("Last function statement not Return: {}", x),
None => panic!("Error while checking last function statement"),
}
Ok((
Function {
id: id,
arguments: args,
statements: stats,
},
Position {
line: current_line,
col: 1,
},
))
}
pub fn parse_program<T: Field>(file: File) -> Result<Prog<T>, Error<T>> {
let mut current_line = 1;
let reader = BufReader::new(file);
let mut lines = reader.lines();
let mut functions = Vec::new();
loop {
match lines.next() {
Some(Ok(ref x)) if x.trim().starts_with("//") || x.trim() == "" => current_line += 1,
Some(Ok(ref x)) => match next_token(
x,
&Position {
line: current_line,
col: 1,
},
) {
(Token::Def, ref s1, ref p1) => match parse_function(&mut lines, s1, p1) {
Ok((function, p2)) => {
// panic if signature is not unique
match functions.iter().find(|x: &&Function<T>| {
x.id == function.id && x.arguments.len() == function.arguments.len()
}) {
Some(_) => panic!(
"Error while reading function: {}({}). Duplicate function signatures.",
function.id,
function
.arguments
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<_>>()
.join(",")
),
_ => {}
}
functions.push(function);
current_line = p2.line; // this is the line of the return statement
current_line += 1;
}
Err(err) => return Err(err),
},
(t1, _, p1) => {
return Err(Error {
expected: vec![Token::Def],
got: t1,
pos: p1,
})
}
},
None => break,
Some(Err(err)) => panic!("Error while reading function definitions: {}", err),
}
}
//check if exactly one main function exists
let mut has_main = false;
for func in &functions {
if func.id == "main".to_string() {
if !has_main {
has_main = true;
} else {
panic!("Error while parsing program: Multiple main functions!");
}
}
}
if !has_main {
panic!("Error while parsing program: No main function found.")
};
Ok(Prog { functions })
}
#[cfg(test)]
mod tests {
use super::*;
use field::FieldPrime;
// Position.col()
#[test]
fn position_col() {
let pos = Position {
line: 100,
col: 258,
};
assert_eq!(
pos.col(26),
Position {
line: 100,
col: 284,
}
);
assert_eq!(
pos.col(-23),
Position {
line: 100,
col: 235,
}
);
}
// inline comment
#[test]
fn inline_comment() {
let pos = Position {
line: 100,
col: 258,
};
let (token, _, _) = next_token::<FieldPrime>(&" //inline comment".to_string(), &pos);
assert_eq!(Token::InlineComment("inline comment".to_string()), token);
}
#[cfg(test)]
mod parse_num {
use super::*;
#[test]
fn single() {
let pos = Position { line: 45, col: 121 };
assert_eq!(
(
Token::Num(FieldPrime::from(12234)),
String::from(""),
pos.col(5)
),
parse_num(&"12234".to_string(), &pos)
);
}
#[test]
fn add() {
let pos = Position { line: 45, col: 121 };
assert_eq!(
(
Token::Num(FieldPrime::from(354)),
String::from("+879"),
pos.col(3)
),
parse_num(&"354+879".to_string(), &pos)
);
}
#[test]
fn space_after() {
let pos = Position { line: 45, col: 121 };
assert_eq!(
(
Token::Num(FieldPrime::from(354)),
String::from(" "),
pos.col(3)
),
parse_num(&"354 ".to_string(), &pos)
);
}
#[test]
#[should_panic]
fn space_before() {
let pos = Position { line: 45, col: 121 };
parse_num::<FieldPrime>(&" 354".to_string(), &pos);
}
#[test]
#[should_panic]
fn x_before() {
let pos = Position { line: 45, col: 121 };
parse_num::<FieldPrime>(&"x324312".to_string(), &pos);
}
}
// parse_ide
// skip_whitespaces
// next_token
// parse_if_then_else
#[test]
fn parse_if_then_else_ok() {
let pos = Position { line: 45, col: 121 };
let string = String::from("if a < b then c else d fi");
let expr = Expression::IfElse::<FieldPrime>(
box Condition::Lt(
Expression::Identifier(String::from("a")),
Expression::Identifier(String::from("b")),
),
box Expression::Identifier(String::from("c")),
box Expression::Identifier(String::from("d")),
);
assert_eq!(
Ok((expr, String::from(""), pos.col(string.len() as isize))),
parse_if_then_else(&string, &pos)
);
}
// parse_factor1
#[cfg(test)]
mod parse_factor {
use super::*;
#[test]
fn if_then_else() {
let pos = Position { line: 45, col: 121 };
let string = String::from("if a < b then c else d fi");
let expr = Expression::IfElse::<FieldPrime>(
box Condition::Lt(
Expression::Identifier(String::from("a")),
Expression::Identifier(String::from("b")),
),
box Expression::Identifier(String::from("c")),
box Expression::Identifier(String::from("d")),
);
assert_eq!(
Ok((expr, String::from(""), pos.col(string.len() as isize))),
parse_factor(&string, &pos)
);
}
#[test]
fn brackets() {
let pos = Position { line: 45, col: 121 };
let string = String::from("(5 + a * 6)");
let expr = Expression::Add(
box Expression::Number(FieldPrime::from(5)),
box Expression::Mult(
box Expression::Identifier(String::from("a")),
box Expression::Number(FieldPrime::from(6)),
),
);
assert_eq!(
Ok((expr, String::from(""), pos.col(string.len() as isize))),
parse_factor(&string, &pos)
);
}
#[test]
fn ide() {
let pos = Position { line: 45, col: 121 };
let string = String::from("a");
let expr = Expression::Identifier::<FieldPrime>(String::from("a"));
assert_eq!(
Ok((expr, String::from(""), pos.col(string.len() as isize))),
parse_factor(&string, &pos)
);
}
#[test]
fn num() {
let pos = Position { line: 45, col: 121 };
let string = String::from("234");
let expr = Expression::Number(FieldPrime::from(234));
assert_eq!(
Ok((expr, String::from(""), pos.col(string.len() as isize))),
parse_factor(&string, &pos)
);
}
}
// parse function
// parse_term1
// parse_term
// parse_expr1
// parse_expr
// parse_statement
// parse_program
// #[should_panic(expected = "assertion failed")]
}