rinha 0.0.6

Competição saudável de compiladores
Documentation
use crate::ast::Element;
use std::str::FromStr;
use lalrpop_util::ErrorRecovery;

grammar<'err>(errors: &'err mut Vec<ErrorRecovery<usize, Token<'input>, crate::parser::InnerError>>, filename: &str);

extern {
  type Location = usize;
  type Error = crate::parser::InnerError;
}

match {
    r"\s*" => { }, // The default whitespace skipping is disabled if an `ignore pattern` is specified
    r"//[^\n\r]*[\n\r]*" => { }, // Skip `// comments`
    r"/\*[^*]*\*+(?:[^/*][^*]*\*+)*/" => { },  // Skip `/* comments */`
} else {
  _
}

pub File: crate::ast::File = {
  <s: @L> <expression:Term> <e: @R> => crate::ast::File {
    name: filename.to_string(),
    expression,
    location: crate::ast::Location::new(s, e, filename),
  },
};

IsExternal: bool = {
  "external" => true
};

Semi: std::string::String = {
  ";" => ";".to_string(),
};

Primary: crate::ast::Term = {
  "(" <Term> ")" => <>,

  <s: @L> "true" <e: @R> => crate::ast::Term::Bool(crate::ast::Bool {
    value: true,
    location: crate::ast::Location::new(s, e, filename),
  }),

  <s: @L> "false" <e: @R> => crate::ast::Term::Bool(crate::ast::Bool {
    value: false,
    location: crate::ast::Location::new(s, e, filename),
  }),

  <Reference> => crate::ast::Term::Var(<>),
  <s: @L> <value:String> <e: @R> => crate::ast::Term::Str(crate::ast::Str {
    value,
    location: crate::ast::Location::new(s, e, filename),
  }),
  <s: @L> <value:Int> <e: @R> => crate::ast::Term::Int(crate::ast::Int {
    value,
    location: crate::ast::Location::new(s, e, filename),
  }),
};

Call: crate::ast::Term = {
  <s: @L> "print" "(" <value:Term> ")" <e: @R> => crate::ast::Term::Print(crate::ast::Print {
    value: Box::new(value),
    location: crate::ast::Location::new(s, e, filename),
  }),
  <s: @L> "first" "(" <value:Term> ")" <e: @R> => crate::ast::Term::First(crate::ast::First {
    value: Box::new(value),
    location: crate::ast::Location::new(s, e, filename),
  }),
  <s: @L> "second" "(" <value:Term> ")" <e: @R> => crate::ast::Term::Second(crate::ast::Second {
    value: Box::new(value),
    location: crate::ast::Location::new(s, e, filename),
  }),
  <s: @L> <callee:Apply> "(" <arguments:Sep<",", Term>> ")" <e: @R> =>crate::ast::Term::Call(crate::ast::Call {
    callee: Box::new(callee),
    arguments,
    location: crate::ast::Location::new(s, e, filename),
  }),
};

FactorOp: crate::ast::BinaryOp = {
  "*" => crate::ast::BinaryOp::Mul,
  "/" => crate::ast::BinaryOp::Div,
  "%" => crate::ast::BinaryOp::Rem,
};

Factor: crate::ast::Term = {
  Apply,
  <s: @L> <value:Apply> "." <name:Reference> <e: @R> => {
    // Report the error
    errors.push(lalrpop_util::ErrorRecovery {
        dropped_tokens: vec![],
        error: lalrpop_util::ParseError::User {
            error: crate::parser::InnerError::UnsupportedRecordIndex {
                err_span: name.location.into(),
            }
        },
    });

    crate::ast::Term::Error(crate::ast::Error {
      message: format!("unsupported record indexing `{}`", name.text),
      full_text: (&input[s..e]).to_string(),
      location: crate::ast::Location::new(s, e, filename),
    })
  },
  <s: @L> <a:Apply> <op:FactorOp> <b:Factor> <e: @R> => crate::ast::Term::Binary(crate::ast::Binary {
    location: crate::ast::Location::new(s, e, filename),
    op,
    lhs: a.into(),
    rhs: b.into(),
  }),
};

ArithmeticOp: crate::ast::BinaryOp = {
  "+" => crate::ast::BinaryOp::Add,
  "-" => crate::ast::BinaryOp::Sub,
}

Arithmetic: crate::ast::Term = {
  Factor,
  <s: @L> <a:Factor> <op:ArithmeticOp> <b:Arithmetic> <e: @R> => crate::ast::Term::Binary(crate::ast::Binary {
    location: crate::ast::Location::new(s, e, filename),
    op,
    lhs: a.into(),
    rhs: b.into(),
  }),
};

LogicalOp: crate::ast::BinaryOp = {
  "&&" => crate::ast::BinaryOp::And,
  "||" => crate::ast::BinaryOp::Or,
  "==" => crate::ast::BinaryOp::Eq,
  "!=" => crate::ast::BinaryOp::Neq,
  "<=" => crate::ast::BinaryOp::Lte,
  ">=" => crate::ast::BinaryOp::Gte,
  "<"  => crate::ast::BinaryOp::Lt,
  ">"  => crate::ast::BinaryOp::Gt,
};

Logical: crate::ast::Term = {
  Arithmetic,

  <s: @L> <a:Arithmetic> <op:LogicalOp> <b:Logical> <e: @R> => crate::ast::Term::Binary(crate::ast::Binary {
    location: crate::ast::Location::new(s, e, filename),
    op,
    lhs: a.into(),
    rhs: b.into(),
  }),
};

Apply: crate::ast::Term = {
  Primary,
  Call,
};

pub Term: crate::ast::Term = {
  Logical,

  <s: @L> "(" <first: Term> "," <second: Term> ")" <e: @R> => crate::ast::Term::Tuple(crate::ast::Tuple {
    first: Box::new(first),
    second: Box::new(second),
    location: crate::ast::Location::new(s, e, filename),
  }),

  "{" <term: Term> "}" => term,

  <s: @L> "let" <name:Reference> "=" <value:Term> ";" <next:Term> <e: @R> => crate::ast::Term::Let(crate::ast::Let {
    name,
    value: value.into(),
    next: next.into(),
    location: crate::ast::Location::new(s, e, filename),
  }),
  <s: @L> "if" "(" <condition:Term> ")" "{" <then:Term> "}" "else" "{" <otherwise:Term> "}" <e: @R> => crate::ast::Term::If(crate::ast::If {
    condition: condition.into(),
    then: then.into(),
    otherwise: otherwise.into(),
    location: crate::ast::Location::new(s, e, filename),
  }),

  <s: @L> "fn" "(" <parameters:Sep<",", Reference>> ")" "=>" <body:Term?> <e: @R> => crate::ast::Term::Function(crate::ast::Function {
    parameters,
    value: match body {
      Some(value) => Box::new(value),
      None => {
        // Report the error
        errors.push(lalrpop_util::ErrorRecovery {
            dropped_tokens: vec![],
            error: lalrpop_util::ParseError::User {
                error: crate::parser::InnerError::FunctionBodyMissing {
                    err_span: crate::ast::Location::new(s, e, filename).into(),
                }
            },
        });

        crate::ast::Term::Int(crate::ast::Int { value: 0, location: crate::ast::Location::new(s, e, filename).into() }).into()
      },
    },
    location: crate::ast::Location::new(s, e, filename),
  }),

  <s: @L> <error:!> <e: @R> => {
    let message = error.error.to_string();

    errors.push(error);

    crate::ast::Term::Error(crate::ast::Error {
      message,
      full_text: (&input[s..e]).to_string(),
      location: crate::ast::Location::new(s, e, filename),
    })
  },
}

Int: i32 = <s:r"[0123456789]+"> => i32::from_str(s).unwrap();
String: std::string::String = <text:r#""(\\\\|\\"|[^"\\])*""#> => (&text[1..text.len() - 1]).to_string();

Text: std::string::String = {
  <text:"_"> => text.to_string(),
  <text:r"[a-zA-Z][a-zA-Z0-9/_]*"> => text.to_string(),
}

Reference: crate::parser::Var = {
  <s: @L> <text:Text> <e: @R> => crate::parser::Var {
    text: text.to_string(),
    location: crate::ast::Location::new(s, e, filename),
  }
};

#[inline]
Sep<U, T>: Vec<T> = {
  <mut v:(<T> U)*> <e:T?> => match e {
    Some(e) => {
        v.push(e);
        v
    }
    None => v
  }
}