Files
vulcan/forge_core/src/parser/mod.rs
T

565 lines
18 KiB
Rust
Raw Normal View History

2023-08-13 23:55:49 -05:00
use pest::pratt_parser::PrattParser;
2023-07-08 19:09:32 -05:00
use pest::Parser;
2023-07-02 02:10:06 -05:00
2023-07-08 22:31:53 -05:00
#[derive(Parser)]
2023-11-09 22:58:26 -06:00
#[grammar = "parser/forge.pest"]
2023-07-08 22:31:53 -05:00
struct ForgeParser;
lazy_static::lazy_static! {
static ref PRATT_PARSER: PrattParser<Rule> = {
use pest::pratt_parser::{Assoc::*, Op};
use Rule::*;
// Precedence is defined lowest to highest
PrattParser::new()
2023-07-08 23:08:22 -05:00
.op(Op::infix(log_or, Left))
.op(Op::infix(log_and, Left))
.op(Op::infix(bit_or, Left))
.op(Op::infix(xor, Left))
.op(Op::infix(bit_and, Left))
.op(Op::infix(eq, Left) | Op::infix(ne, Left))
.op(Op::infix(gt, Left) | Op::infix(ge, Left) | Op::infix(lt, Left) | Op::infix(le, Left))
.op(Op::infix(lshift, Left) | Op::infix(rshift, Left))
2023-07-08 22:31:53 -05:00
.op(Op::infix(add, Left) | Op::infix(sub, Left))
.op(Op::infix(mul, Left) | Op::infix(div, Left) | Op::infix(modulus, Left))
2023-07-08 23:33:30 -05:00
.op(Op::prefix(Rule::prefix))
2023-08-05 11:07:09 -05:00
.op(Op::postfix(Rule::arglist))
.op(Op::postfix(Rule::subscript))
2023-07-08 22:31:53 -05:00
};
2023-07-02 02:10:06 -05:00
}
2023-11-09 23:23:44 -06:00
/// Make some Pest types a little more ergonomic to refer to, especially outside this file:
2023-11-09 22:58:26 -06:00
pub type PestRule = Rule;
2023-07-03 01:35:37 -05:00
pub(crate) type Pair<'a> = pest::iterators::Pair<'a, Rule>;
pub(crate) type Pairs<'i, R = Rule> = pest::iterators::Pairs<'i, R>;
2023-07-02 02:10:06 -05:00
2023-11-09 22:58:26 -06:00
mod parse_error;
mod pairs_ext;
2023-07-02 02:10:06 -05:00
2023-11-09 23:23:44 -06:00
use crate::ast::*;
pub use pairs_ext::*;
2023-11-09 22:58:26 -06:00
pub use parse_error::ParseError;
2023-07-02 02:10:06 -05:00
2023-11-09 23:23:44 -06:00
/// A trait that represents something that can be parsed into an AST node: impl this to turn
/// a pair into your node, by from_pair-ing child nodes.
2023-07-03 23:11:44 -05:00
trait AstNode: Sized {
2023-07-03 01:35:37 -05:00
const RULE: Rule;
2023-07-03 23:11:44 -05:00
fn from_pair(pair: Pair) -> Self;
fn from_pair_located(pair: Pair) -> Located<Self> {
let loc = pair.line_col();
Located {
location: loc.into(),
ast: Self::from_pair(pair)
}
}
2023-07-03 23:11:44 -05:00
}
2023-11-09 23:23:44 -06:00
/// A companion trait to AstNode: Parseable things can be parsed from strings, and Parseable
/// AstNodes have a default way of doing that (using the ForgeParser to make a pair and then
/// from_pair-ing it
2023-07-03 23:11:44 -05:00
trait Parseable: Sized {
2023-07-08 22:31:53 -05:00
fn from_str(src: &str) -> Result<Self, ParseError>;
2023-07-03 23:11:44 -05:00
}
impl<T: AstNode> Parseable for T {
2023-07-08 22:31:53 -05:00
fn from_str(src: &str) -> Result<Self, ParseError> {
2023-07-03 01:35:37 -05:00
let pair = ForgeParser::parse(Self::RULE, src)
.map_err(ParseError::from)?
.next()
.unwrap();
2023-07-03 23:11:44 -05:00
Ok(Self::from_pair(pair))
2023-07-03 01:35:37 -05:00
}
}
2023-11-09 23:23:44 -06:00
/// Try and parse a program into an AST node. A convenience method (since Program is both
/// Parseable and an AstNode) but also the outside entry point into this whole module.
pub fn parse(src: &str) -> Result<Program, ParseError> {
Program::from_str(src)
}
2023-11-09 23:23:44 -06:00
mod ast_nodes;
2023-07-03 01:35:37 -05:00
2023-07-03 23:11:44 -05:00
///////////////////////////////////////////////////////////////////////////////////////////
2023-07-03 01:35:37 -05:00
2023-07-03 23:11:44 -05:00
///////////////////////////////////////////////////////////////////////////////////////////
2023-07-03 01:35:37 -05:00
2023-07-03 23:11:44 -05:00
///////////////////////////////////////////////////////////////////////////////////////////
2023-07-03 01:35:37 -05:00
2023-07-03 23:11:44 -05:00
///////////////////////////////////////////////////////////////////////////////////////////
2023-07-02 02:10:06 -05:00
2023-08-26 00:42:41 -05:00
///////////////////////////////////////////////////////////////////////////////////////////
2023-07-04 02:49:29 -05:00
///////////////////////////////////////////////////////////////////////////////////////////
2023-08-15 01:30:35 -05:00
///////////////////////////////////////////////////////////////////////////////////////////
2023-07-04 02:49:29 -05:00
///////////////////////////////////////////////////////////////////////////////////////////
2023-07-08 19:09:32 -05:00
///////////////////////////////////////////////////////////////////////////////////////////
2023-07-04 02:49:29 -05:00
impl AstNode for VarDecl {
2023-11-09 23:23:44 -06:00
const RULE: PestRule = PestRule::var_decl;
2023-07-04 02:49:29 -05:00
fn from_pair(pair: Pair) -> Self {
2023-07-08 19:09:32 -05:00
let mut inner = pair.into_inner();
let name = String::from(inner.next().unwrap().as_str());
2023-08-15 00:39:53 -05:00
let mut size = None;
let mut initial = None;
for p in inner {
match p.as_rule() {
2023-11-09 23:23:44 -06:00
PestRule::size => { size = Some(Expr::from_pair(p.first())) }
PestRule::expr => { initial = Some(Expr::from_pair(p)) }
2023-08-15 00:39:53 -05:00
_ => unreachable!()
}
2023-07-08 19:09:32 -05:00
}
2023-08-15 00:39:53 -05:00
Self { name, size, initial }
2023-07-04 02:49:29 -05:00
}
}
///////////////////////////////////////////////////////////////////////////////////////////
2023-07-08 20:54:23 -05:00
impl AstNode for Block {
2023-11-09 23:23:44 -06:00
const RULE: PestRule = PestRule::block;
2023-07-08 20:54:23 -05:00
fn from_pair(pair: Pair) -> Self {
Self(pair.into_inner().map(Statement::from_pair_located).collect())
2023-07-08 20:54:23 -05:00
}
}
///////////////////////////////////////////////////////////////////////////////////////////
2023-07-04 02:49:29 -05:00
impl AstNode for Conditional {
2023-11-09 23:23:44 -06:00
const RULE: PestRule = PestRule::conditional;
2023-07-04 02:49:29 -05:00
fn from_pair(pair: Pair) -> Self {
2023-07-08 20:54:23 -05:00
let mut inner = pair.into_inner();
2023-08-02 21:56:26 -05:00
let condition = Expr::from_pair(inner.next().unwrap());
2023-07-08 20:54:23 -05:00
let body = Block::from_pair(inner.next().unwrap());
let alternative = inner.next().map(Block::from_pair);
Self {
condition,
body,
alternative,
}
2023-07-04 02:49:29 -05:00
}
}
///////////////////////////////////////////////////////////////////////////////////////////
impl AstNode for WhileLoop {
2023-11-09 23:23:44 -06:00
const RULE: PestRule = PestRule::while_loop;
2023-07-04 02:49:29 -05:00
fn from_pair(pair: Pair) -> Self {
2023-07-08 20:54:23 -05:00
let mut inner = pair.into_inner();
Self {
2023-08-02 21:56:26 -05:00
condition: Expr::from_pair(inner.next().unwrap()),
2023-07-08 20:54:23 -05:00
body: Block::from_pair(inner.next().unwrap()),
}
2023-07-04 02:49:29 -05:00
}
}
///////////////////////////////////////////////////////////////////////////////////////////
impl AstNode for RepeatLoop {
2023-11-09 23:23:44 -06:00
const RULE: PestRule = PestRule::repeat_loop;
2023-07-04 02:49:29 -05:00
fn from_pair(pair: Pair) -> Self {
2023-07-08 20:54:23 -05:00
let mut inner = pair.into_inner().peekable();
2023-08-02 21:56:26 -05:00
let count = Expr::from_pair(inner.next().unwrap());
2023-07-08 20:54:23 -05:00
let name = inner
2023-11-09 23:23:44 -06:00
.next_if_rule(PestRule::name)
2023-07-08 20:54:23 -05:00
.map(|p| String::from(p.as_str()));
let body = Block::from_pair(inner.next().unwrap());
Self { count, name, body }
2023-07-04 02:49:29 -05:00
}
}
///////////////////////////////////////////////////////////////////////////////////////////
2023-08-02 21:56:26 -05:00
impl AstNode for Expr {
2023-11-09 23:23:44 -06:00
const RULE: PestRule = PestRule::expr;
2023-07-04 02:49:29 -05:00
fn from_pair(pair: Pair) -> Self {
// The way this works is, the rule has to be of the form:
// expr = { prefix* ~ val ~ suffix* ~ (operator ~ prefix* ~ val ~ suffix*)* }
// Each of these map methods turns a thing into an expr. Which means expr HAS
// to be an enum with the different possible forms these things can take:
2023-08-04 17:27:28 -05:00
// - if it's a term, it goes into map_primary and returns an Expr::Number or Name
// - If it's a prefix or suffix, it goes into map_prefix or map_postfix, and
// returns an Expr::Prefix or Expr::Suffix
// - Operators go into map_infix along with two exprs for the left and right
// sides
// The output of all this is an Expr, containing a tree of other Exprs of
// various forms.
2023-07-08 22:31:53 -05:00
PRATT_PARSER
2023-08-04 21:38:09 -05:00
.map_primary(|term| match term.as_rule() {
2023-11-09 23:23:44 -06:00
PestRule::number => Expr::Number(term.into_number()),
PestRule::name => Expr::Name(String::from(term.as_str())),
PestRule::expr => Expr::from_pair(term),
PestRule::string => Self::String(term.into_quoted_string()),
2023-08-04 21:38:09 -05:00
_ => unreachable!(),
2023-07-08 22:31:53 -05:00
})
2023-08-04 21:38:09 -05:00
.map_infix(|lhs, op, rhs| Expr::Infix(lhs.into(), Operator::from_pair(op), rhs.into()))
.map_prefix(|prefix, expr| match prefix.as_str() {
"-" => Expr::Neg(expr.into()),
"!" => Expr::Not(expr.into()),
2023-08-13 18:58:47 -05:00
"*" => Expr::Deref(expr.into()),
2023-08-13 23:55:49 -05:00
"&" => Expr::Address(expr.into()),
2023-08-04 21:38:09 -05:00
_ => unreachable!(),
})
2023-08-05 11:07:09 -05:00
.map_postfix(|expr, suffix| match suffix.as_rule() {
2023-11-09 23:23:44 -06:00
PestRule::arglist => Expr::Call(
2023-09-01 22:24:36 -05:00
Call {
target: expr.into(),
args: suffix.into_inner().map(Expr::from_pair).collect(),
}
2023-08-05 11:07:09 -05:00
),
2023-11-09 23:23:44 -06:00
PestRule::subscript => {
2023-08-05 11:07:09 -05:00
Expr::Subscript(expr.into(), Expr::from_pair(suffix.first()).into())
}
_ => unreachable!(),
})
2023-07-08 22:31:53 -05:00
.parse(pair.into_inner())
2023-07-04 02:49:29 -05:00
}
}
2023-08-02 21:56:26 -05:00
impl Expr {
pub(crate) fn parse(src: &str) -> Result<Self, ParseError> {
Self::from_str(src)
}
}
2023-07-04 02:49:29 -05:00
///////////////////////////////////////////////////////////////////////////////////////////
impl AstNode for Operator {
2023-11-09 23:23:44 -06:00
const RULE: PestRule = PestRule::operator;
2023-07-04 02:49:29 -05:00
fn from_pair(pair: Pair) -> Self {
match pair.as_str() {
"+" => Self::Add,
"-" => Self::Sub,
"*" => Self::Mul,
"/" => Self::Div,
"%" => Self::Mod,
2023-07-08 23:08:22 -05:00
"&&" => Self::And,
"||" => Self::Or,
"&" => Self::BitAnd,
"|" => Self::BitOr,
"^" => Self::Xor,
">" => Self::Gt,
">=" => Self::Ge,
"<" => Self::Lt,
"<=" => Self::Le,
"==" => Self::Eq,
"!=" => Self::Ne,
"<<" => Self::Lshift,
">>" => Self::Rshift,
2023-07-04 02:49:29 -05:00
_ => unreachable!(),
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////
2023-07-08 20:54:23 -05:00
impl AstNode for Program {
2023-11-09 23:23:44 -06:00
const RULE: PestRule = PestRule::program;
2023-07-08 20:54:23 -05:00
fn from_pair(pair: Pair) -> Self {
// Program captures EOI, to make sure that it's parsing the entire stream. We need to
// ignore that though:
Self(
pair.into_inner()
2023-11-09 23:23:44 -06:00
.filter(|p| p.as_rule() != PestRule::EOI)
.map(Declaration::from_pair_located)
2023-07-08 20:54:23 -05:00
.collect(),
)
}
}
///////////////////////////////////////////////////////////////////////////////////////////
2023-07-02 02:10:06 -05:00
#[cfg(test)]
mod test {
use super::*;
2023-07-04 02:49:29 -05:00
#[test]
fn parse_exprs() {
2023-08-04 21:38:09 -05:00
use Expr::Infix;
2023-07-04 02:49:29 -05:00
use Operator::*;
2023-07-04 02:49:29 -05:00
// A very, very basic expression
2023-08-04 21:38:09 -05:00
assert_eq!(Expr::from_str("23"), Ok(Expr::Number(23)));
2023-07-04 02:49:29 -05:00
// Two vals with an operator
assert_eq!(
2023-08-02 21:56:26 -05:00
Expr::from_str("23 + 5"),
Ok(Infix(23.into(), Add, 5.into()))
2023-07-04 02:49:29 -05:00
);
// Multiple terms at the same precedence level
assert_eq!(
2023-08-02 21:56:26 -05:00
Expr::from_str("1 + 2 + 3"),
Ok(Infix(Infix(1.into(), Add, 2.into()).into(), Add, 3.into()))
);
// Simple prefix
2023-08-04 21:38:09 -05:00
assert_eq!(Expr::from_str("-5"), Ok(Expr::Neg(5.into())));
// Multiple prefixes
assert_eq!(
2023-08-04 21:38:09 -05:00
Expr::from_str("!-foo"),
Ok(Expr::Not(Expr::Neg("foo".into()).into()))
);
// Simple suffix
assert_eq!(
Expr::from_str("foo[10]"),
2023-08-05 11:07:09 -05:00
Ok(Expr::Subscript("foo".into(), 10.into()))
);
// Multi-suffix
assert_eq!(
2023-08-15 00:39:53 -05:00
Expr::from_str("foo[10][3]"),
Ok(Expr::Subscript(
2023-08-05 11:07:09 -05:00
Expr::Subscript("foo".into(), 10.into()).into(),
2023-08-15 00:39:53 -05:00
3.into()
2023-08-04 21:38:09 -05:00
))
);
// Higher precedence levels
assert_eq!(
Expr::from_str("1 + 2 * 3"),
Ok(Infix(1.into(), Add, Infix(2.into(), Mul, 3.into()).into()))
);
2023-08-04 21:38:09 -05:00
assert_eq!(Expr::from_str("2 * 3"), Ok(Infix(2.into(), Mul, 3.into())));
assert_eq!(
Expr::from_str("2 * 3 + 4"),
Ok(Infix(Infix(2.into(), Mul, 3.into()).into(), Add, 4.into()))
);
// Various operators
assert_eq!(
Expr::from_str("1 || 2 && 3"),
Ok(Infix(1.into(), Or, Infix(2.into(), And, 3.into()).into()))
);
assert_eq!(
Expr::from_str("2 && &blah"),
2023-08-04 21:38:09 -05:00
Ok(Infix(2.into(), And, Expr::Address("blah".into()).into()))
);
assert_eq!(
Expr::from_str("2 & &blah"),
2023-08-04 21:38:09 -05:00
Ok(Infix(2.into(), BitAnd, Expr::Address("blah".into()).into()))
);
assert_eq!(
Expr::from_str("1 | 2 ^ 3"),
2023-08-04 21:38:09 -05:00
Ok(Infix(
1.into(),
BitOr,
Infix(2.into(), Xor, 3.into()).into()
))
);
assert_eq!(
Expr::from_str("x == y > z"),
2023-08-04 21:38:09 -05:00
Ok(Infix(
"x".into(),
Eq,
Infix("y".into(), Gt, "z".into()).into()
))
);
assert_eq!(
Expr::from_str("1 << 6"),
Ok(Infix(1.into(), Lshift, 6.into()))
);
assert_eq!(
Expr::from_str("!a - -3"),
2023-08-04 21:38:09 -05:00
Ok(Infix(
Expr::Not("a".into()).into(),
Sub,
Expr::Neg(3.into()).into()
))
);
assert_eq!(
Expr::from_str("-(4 * 5)"),
2023-08-04 21:38:09 -05:00
Ok(Expr::Neg(Infix(4.into(), Mul, 5.into()).into()))
);
// Parens
assert_eq!(
Expr::from_str("(1 + 2) * 3"),
2023-08-04 21:38:09 -05:00
Ok(Infix(Infix(1.into(), Add, 2.into()).into(), Mul, 3.into()))
);
// Addresses
assert_eq!(
2023-08-15 00:39:53 -05:00
Expr::from_str("&foo[7]"),
Ok(Expr::Address(Expr::Subscript("foo".into(), 7.into()).into()))
2023-08-04 21:38:09 -05:00
);
assert_eq!(
Expr::from_str("&foo + 7"),
Ok(Expr::Infix(
Expr::Address("foo".into()).into(),
Add,
7.into()
))
);
2023-08-04 21:38:09 -05:00
2023-08-13 23:55:49 -05:00
// This is an example of a thing that will parse but not compile. This parses as an address
// of a call, which doesn't make sense, but because Expr::Address contains an Lvalue the
// compiler can detect this and error at that stage.
2023-08-04 21:38:09 -05:00
assert_eq!(
Expr::from_str("&foo()"),
2023-09-01 22:24:36 -05:00
Ok(Expr::Address(Expr::Call(Call { target: "foo".into(), args: vec![] }).into()))
2023-08-13 18:58:47 -05:00
);
// Dereferencing
assert_eq!(
Expr::from_str("*foo"),
Ok(Expr::Deref("foo".into()))
);
assert_eq!(
Expr::from_str("*foo[3]"), // The subscript happens before the dereference
Ok(Expr::Deref(Expr::Subscript("foo".into(), 3.into()).into()))
);
2023-07-04 02:49:29 -05:00
}
2023-07-05 23:45:04 -05:00
#[test]
fn parse_calls() {
2023-09-01 22:24:36 -05:00
let blah = Expr::Call(Call { target: "blah".into(), args: vec![] });
// Can Node parse a call?
assert_eq!(Expr::from_str("blah()"), Ok(blah.clone()));
// Can Statement parse a call?
assert_eq!(Statement::from_str("blah();"), Ok(Statement::Expr(blah)));
// Calls with args
assert_eq!(
Expr::from_str("blah(1, 2)"),
2023-09-01 22:24:36 -05:00
Ok(Expr::Call(Call { target: "blah".into(), args: vec![1.into(), 2.into()] }))
);
//Calls with strings
assert_eq!(
Expr::from_str("blah(\"foo\", 2)"),
2023-09-01 22:24:36 -05:00
Ok(Expr::Call(Call { target: "blah".into(), args: vec![Expr::String("foo".into()), 2.into()] }))
);
2023-08-04 21:38:09 -05:00
}
2023-07-08 19:09:32 -05:00
#[test]
fn parse_var_decl() {
assert_eq!(
2023-07-08 22:31:53 -05:00
Statement::from_str("var blah;"),
2023-07-08 19:09:32 -05:00
Ok(Statement::VarDecl(VarDecl {
name: "blah".into(),
size: None,
initial: None,
}))
);
assert_eq!(
2023-08-15 00:39:53 -05:00
VarDecl::from_str("var blah[7] = 35"),
2023-07-08 19:09:32 -05:00
Ok(VarDecl {
name: "blah".into(),
2023-08-02 21:56:26 -05:00
size: Some(7.into()),
initial: Some(35.into()),
2023-07-08 19:09:32 -05:00
})
);
}
2023-07-08 20:54:23 -05:00
2023-08-04 17:27:28 -05:00
#[test]
fn parse_block() {
let block = Block::from_str("{ foo(); bar(); }").unwrap();
let statements: Vec<_> = block.0.into_iter().map(|s| s.ast).collect();
2023-08-04 17:27:28 -05:00
assert_eq!(
statements,
vec![
2023-09-01 22:24:36 -05:00
Statement::Expr(Expr::Call(Call { target: "foo".into(), args: vec![] })),
Statement::Expr(Expr::Call(Call { target: "bar".into(), args: vec![] })),
]
2023-08-04 17:27:28 -05:00
);
}
2023-07-08 20:54:23 -05:00
fn dislocate<T>(block: Vec<Located<T>>) -> Vec<T> {
block.into_iter().map(|l| l.ast).collect()
}
fn dislocated_block(src: &str) -> Vec<Statement> {
dislocate(Block::from_str(src).unwrap().0)
}
2023-07-08 20:54:23 -05:00
#[test]
fn parse_conditional() {
if let Ok(Statement::Conditional(Conditional { condition, body, alternative })) =
Statement::from_str("if(cond) { foo(); }") {
assert_eq!(condition, Expr::from_str("cond").unwrap());
assert_eq!(dislocate(body.0), dislocated_block("{ foo(); }"));
assert_eq!(alternative, None);
} else {
panic!()
}
2023-07-08 20:54:23 -05:00
if let Ok(Statement::Conditional(Conditional { condition, body, alternative })) =
Statement::from_str("if(cond) { foo(); } else { bar(); }") {
assert_eq!(condition, Expr::from_str("cond").unwrap());
assert_eq!(dislocate(body.0), dislocated_block("{ foo(); }"));
assert_eq!(alternative.unwrap().0[0].ast, Block::from_str("{ bar(); }").unwrap().0[0].ast);
} else {
panic!()
}
2023-07-08 20:54:23 -05:00
}
#[test]
fn parse_while_loops() {
assert!(match Statement::from_str("while(cond) { foo(); }") {
Ok(Statement::WhileLoop(WhileLoop { condition, body })) => {
assert_eq!(condition, Expr::from_str("cond").unwrap());
assert_eq!(dislocate(body.0), dislocated_block("{ foo(); }"));
true
}
_ => false
});
2023-07-08 20:54:23 -05:00
}
#[test]
fn parse_repeat_loops() {
assert!(match Statement::from_str("repeat(10) x { foo(x); }") {
Ok(Statement::RepeatLoop(RepeatLoop { count, name, body })) => {
assert_eq!(count, 10.into());
assert_eq!(name, Some("x".into()));
assert_eq!(dislocate(body.0), dislocated_block("{ foo(x); }"));
true
},
_ => false
});
assert!(match Statement::from_str("repeat(10) { foo(); }") {
Ok(Statement::RepeatLoop(RepeatLoop { count, name, body })) => {
assert_eq!(count, 10.into());
assert_eq!(name, None);
assert_eq!(dislocate(body.0), dislocated_block("{ foo(); }"));
true
},
_ => false
});
2023-07-08 20:54:23 -05:00
}
#[test]
fn parse_program() {
let prog = Program::from_str("global foo; const blah = 3;").unwrap();
let decls: Vec<_> = dislocate(prog.0);
2023-08-04 17:27:28 -05:00
assert_eq!(
decls,
vec![
2023-08-04 17:27:28 -05:00
Declaration::from_str("global foo;").unwrap(),
Declaration::from_str("const blah = 3;").unwrap(),
]
2023-08-04 17:27:28 -05:00
)
2023-07-08 20:54:23 -05:00
}
2023-07-02 02:10:06 -05:00
}