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 22:58:26 -06:00
|
|
|
/// Expose Pest's generated `Rule` type so other things can see it.
|
|
|
|
|
pub type PestRule = Rule;
|
|
|
|
|
|
2023-07-03 23:11:44 -05:00
|
|
|
use crate::ast::*;
|
2023-07-03 01:35:37 -05:00
|
|
|
use std::iter::Peekable;
|
2023-07-02 02:10:06 -05:00
|
|
|
use std::str::FromStr;
|
|
|
|
|
|
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-07-03 23:11:44 -05:00
|
|
|
trait PairExt {
|
2023-07-03 01:35:37 -05:00
|
|
|
fn first(self) -> Self;
|
2023-07-03 17:19:19 -05:00
|
|
|
fn first_as_string(self) -> String;
|
2023-07-03 01:35:37 -05:00
|
|
|
fn only(self) -> Self;
|
2023-07-03 23:11:44 -05:00
|
|
|
fn into_number(self) -> i32;
|
|
|
|
|
|
|
|
|
|
/// Create a String containing the string represented by a given pair. Since the pair will
|
|
|
|
|
/// reference bytes containing escape sequences, this isn't the same as an &str to the
|
|
|
|
|
/// original code; this is a new string translating those escape sequences to their actual
|
|
|
|
|
/// bytes.
|
|
|
|
|
fn into_quoted_string(self) -> String;
|
2023-07-03 01:35:37 -05:00
|
|
|
}
|
|
|
|
|
|
2023-07-03 23:11:44 -05:00
|
|
|
impl<'a> PairExt for Pair<'a> {
|
2023-07-03 01:35:37 -05:00
|
|
|
fn first(self) -> Self {
|
|
|
|
|
self.into_inner().next().unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-03 17:19:19 -05:00
|
|
|
fn first_as_string(self) -> String {
|
|
|
|
|
String::from(self.first().as_str())
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-03 01:35:37 -05:00
|
|
|
fn only(self) -> Pair<'a> {
|
|
|
|
|
let mut iter = self.into_inner();
|
|
|
|
|
let child = iter.next().unwrap();
|
|
|
|
|
debug_assert_eq!(iter.next(), None);
|
|
|
|
|
child
|
|
|
|
|
}
|
2023-07-03 23:11:44 -05:00
|
|
|
|
|
|
|
|
fn into_number(self) -> i32 {
|
|
|
|
|
let first = self.into_inner().next().unwrap();
|
|
|
|
|
match first.as_rule() {
|
2023-07-09 00:24:04 -05:00
|
|
|
Rule::dec_number | Rule::dec_zero => i32::from_str(first.as_str()).unwrap(),
|
2023-07-03 23:11:44 -05:00
|
|
|
Rule::hex_number => i32::from_str_radix(first.as_str().get(2..).unwrap(), 16).unwrap(),
|
|
|
|
|
Rule::bin_number => i32::from_str_radix(first.as_str().get(2..).unwrap(), 2).unwrap(),
|
|
|
|
|
Rule::oct_number => i32::from_str_radix(first.as_str().get(2..).unwrap(), 8).unwrap(),
|
|
|
|
|
_ => panic!("Expected a number, got {}", first.as_str()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn into_quoted_string(self) -> String {
|
|
|
|
|
let mut string = String::with_capacity(self.as_str().len());
|
|
|
|
|
for inner in self.into_inner() {
|
|
|
|
|
let string_inner = inner.as_str();
|
|
|
|
|
match string_inner {
|
|
|
|
|
"\\t" => string.push('\t'),
|
|
|
|
|
"\\r" => string.push('\r'),
|
|
|
|
|
"\\n" => string.push('\n'),
|
|
|
|
|
"\\0" => string.push('\0'),
|
|
|
|
|
"\\\\" => string.push('\\'),
|
|
|
|
|
"\\\"" => string.push('\"'),
|
|
|
|
|
_ => string.push_str(string_inner),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
string
|
|
|
|
|
}
|
2023-07-03 01:35:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
trait PairsExt {
|
|
|
|
|
fn next_if_rule(&mut self, rule: Rule) -> Option<Pair>;
|
2023-07-03 17:19:19 -05:00
|
|
|
fn first(&mut self) -> Pair;
|
2023-07-03 01:35:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl PairsExt for Peekable<Pairs<'_>> {
|
|
|
|
|
fn next_if_rule(&mut self, rule: Rule) -> Option<Pair> {
|
|
|
|
|
self.next_if(|p| p.as_rule() == rule)
|
|
|
|
|
}
|
2023-07-03 17:19:19 -05:00
|
|
|
|
|
|
|
|
fn first(&mut self) -> Pair {
|
|
|
|
|
self.next().unwrap().first()
|
|
|
|
|
}
|
2023-07-03 01:35:37 -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 22:58:26 -06:00
|
|
|
pub use parse_error::ParseError;
|
2023-07-02 02:10:06 -05:00
|
|
|
|
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;
|
2023-08-20 01:38:42 -05:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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-07-12 00:53:52 -05:00
|
|
|
pub fn parse(src: &str) -> Result<Program, ParseError> {
|
|
|
|
|
Program::from_str(src)
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-03 23:11:44 -05:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
2023-07-03 01:35:37 -05:00
|
|
|
|
2023-07-03 23:11:44 -05:00
|
|
|
impl AstNode for Declaration {
|
|
|
|
|
const RULE: Rule = Rule::declaration;
|
|
|
|
|
fn from_pair(pair: Pair) -> Self {
|
2023-07-08 20:54:23 -05:00
|
|
|
let pair = pair.first();
|
2023-07-03 01:35:37 -05:00
|
|
|
match pair.as_rule() {
|
2023-07-03 23:11:44 -05:00
|
|
|
Rule::function => Self::Function(Function::from_pair(pair)),
|
|
|
|
|
Rule::global => Self::Global(Global::from_pair(pair)),
|
|
|
|
|
Rule::const_decl => Self::Const(Const::from_pair(pair)),
|
2023-08-26 00:42:41 -05:00
|
|
|
Rule::function_prototype => Self::Prototype(FunctionPrototype::from_pair(pair)),
|
2023-07-08 19:09:32 -05:00
|
|
|
_ => unreachable!(),
|
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
|
|
|
impl AstNode for Global {
|
|
|
|
|
const RULE: Rule = Rule::global;
|
|
|
|
|
fn from_pair(pair: Pair) -> Self {
|
|
|
|
|
let mut inner = pair.into_inner().peekable();
|
2023-07-03 01:35:37 -05:00
|
|
|
let name = String::from(inner.next().unwrap().as_str());
|
2023-08-15 00:39:53 -05:00
|
|
|
let size = inner.next().map(Expr::from_pair);
|
2023-07-03 01:35:37 -05:00
|
|
|
|
2023-08-15 00:39:53 -05:00
|
|
|
Global { name, size }
|
2023-07-02 02:10:06 -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
|
|
|
impl AstNode for Const {
|
|
|
|
|
const RULE: Rule = Rule::const_decl;
|
|
|
|
|
fn from_pair(pair: Pair) -> Self {
|
2023-07-03 01:35:37 -05:00
|
|
|
let mut inner = pair.into_inner();
|
|
|
|
|
let name = String::from(inner.next().unwrap().as_str());
|
|
|
|
|
let value = inner.next().unwrap();
|
|
|
|
|
match value.as_rule() {
|
|
|
|
|
Rule::string => Const {
|
|
|
|
|
name,
|
2023-07-08 19:09:32 -05:00
|
|
|
string: Some(value.into_quoted_string()),
|
2023-07-03 01:35:37 -05:00
|
|
|
value: None,
|
|
|
|
|
},
|
2023-07-09 00:24:04 -05:00
|
|
|
Rule::expr => Const {
|
2023-07-03 01:35:37 -05:00
|
|
|
name,
|
|
|
|
|
string: None,
|
2023-08-02 21:56:26 -05:00
|
|
|
value: Some(Expr::from_pair(value)),
|
2023-07-03 01:35:37 -05:00
|
|
|
},
|
2023-07-02 02:10:06 -05:00
|
|
|
_ => unreachable!(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-03 23:11:44 -05:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
2023-07-03 01:35:37 -05:00
|
|
|
|
2023-07-03 23:11:44 -05:00
|
|
|
impl AstNode for Function {
|
|
|
|
|
const RULE: Rule = Rule::function;
|
|
|
|
|
fn from_pair(pair: Pair<'_>) -> Self {
|
2023-07-03 17:19:19 -05:00
|
|
|
let mut inner = pair.into_inner().peekable();
|
|
|
|
|
let name = String::from(inner.next().unwrap().as_str());
|
|
|
|
|
let args: Vec<_> = inner
|
|
|
|
|
.next()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.into_inner()
|
2023-08-15 00:39:53 -05:00
|
|
|
.map(|p| String::from(p.as_str()))
|
2023-07-03 17:19:19 -05:00
|
|
|
.collect();
|
|
|
|
|
|
2023-07-08 20:54:23 -05:00
|
|
|
let body = Block::from_pair(inner.next().unwrap());
|
|
|
|
|
|
2023-08-04 21:38:09 -05:00
|
|
|
Self { name, args, body }
|
2023-07-03 17:19:19 -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
|
|
|
impl AstNode for FunctionPrototype {
|
|
|
|
|
const RULE: Rule = Rule::function_prototype;
|
|
|
|
|
fn from_pair(pair: Pair<'_>) -> Self {
|
|
|
|
|
let mut inner = pair.into_inner().peekable();
|
|
|
|
|
let name = String::from(inner.next().unwrap().as_str());
|
|
|
|
|
let args: Vec<_> = inner
|
|
|
|
|
.next()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.into_inner()
|
|
|
|
|
.map(|p| String::from(p.as_str()))
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
Self { name, args }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2023-07-04 02:49:29 -05:00
|
|
|
impl AstNode for Statement {
|
2023-07-08 19:09:32 -05:00
|
|
|
const RULE: Rule = Rule::statement;
|
2023-07-04 02:49:29 -05:00
|
|
|
fn from_pair(pair: Pair) -> Self {
|
2023-07-08 19:09:32 -05:00
|
|
|
let pair = pair.first();
|
2023-07-04 02:49:29 -05:00
|
|
|
match pair.as_rule() {
|
|
|
|
|
Rule::return_stmt => Self::Return(Return::from_pair(pair)),
|
|
|
|
|
Rule::assignment => Self::Assignment(Assignment::from_pair(pair)),
|
2023-08-02 21:56:26 -05:00
|
|
|
Rule::expr => Self::Expr(Expr::from_pair(pair)),
|
2023-07-04 02:49:29 -05:00
|
|
|
Rule::var_decl => Self::VarDecl(VarDecl::from_pair(pair)),
|
|
|
|
|
Rule::conditional => Self::Conditional(Conditional::from_pair(pair)),
|
|
|
|
|
Rule::while_loop => Self::WhileLoop(WhileLoop::from_pair(pair)),
|
|
|
|
|
Rule::repeat_loop => Self::RepeatLoop(RepeatLoop::from_pair(pair)),
|
2023-08-15 01:30:35 -05:00
|
|
|
Rule::asm => Self::Asm(Asm::from_pair(pair)),
|
2023-07-08 19:09:32 -05:00
|
|
|
_ => unreachable!(),
|
2023-07-04 02:49:29 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2023-08-15 01:30:35 -05:00
|
|
|
impl AstNode for Asm {
|
|
|
|
|
const RULE: Rule = Rule::asm;
|
|
|
|
|
fn from_pair(pair: Pair) -> Self {
|
|
|
|
|
let mut args = Vec::new();
|
|
|
|
|
let mut body = None;
|
|
|
|
|
for p in pair.into_inner() {
|
|
|
|
|
match p.as_rule() {
|
|
|
|
|
Rule::expr => args.push(Expr::from_pair(p).into()),
|
|
|
|
|
Rule::asm_body => body = Some(String::from(p.as_str().trim())),
|
|
|
|
|
_ => unreachable!()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Self { args, body: body.unwrap() }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2023-07-04 02:49:29 -05:00
|
|
|
impl AstNode for Return {
|
|
|
|
|
const RULE: Rule = Rule::return_stmt;
|
|
|
|
|
fn from_pair(pair: Pair) -> Self {
|
2023-07-08 19:09:32 -05:00
|
|
|
pair.into_inner()
|
|
|
|
|
.next()
|
2023-08-02 21:56:26 -05:00
|
|
|
.map_or(Self(None), |expr| Self(Some(Expr::from_pair(expr))))
|
2023-07-04 02:49:29 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
impl AstNode for Assignment {
|
|
|
|
|
const RULE: Rule = Rule::assignment;
|
|
|
|
|
fn from_pair(pair: Pair) -> Self {
|
2023-07-08 19:09:32 -05:00
|
|
|
let mut pairs = pair.into_inner();
|
2023-08-13 23:55:49 -05:00
|
|
|
let lvalue = Expr::from_pair(pairs.next().unwrap()).into();
|
2023-08-14 00:26:02 -05:00
|
|
|
let rvalue = Expr::from_pair(pairs.next().unwrap());
|
2023-07-08 19:09:32 -05:00
|
|
|
Self { lvalue, rvalue }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2023-07-04 02:49:29 -05:00
|
|
|
impl AstNode for VarDecl {
|
|
|
|
|
const RULE: Rule = Rule::var_decl;
|
|
|
|
|
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() {
|
|
|
|
|
Rule::size => { size = Some(Expr::from_pair(p.first())) }
|
|
|
|
|
Rule::expr => { initial = Some(Expr::from_pair(p)) }
|
|
|
|
|
_ => 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 {
|
|
|
|
|
const RULE: Rule = Rule::block;
|
|
|
|
|
|
|
|
|
|
fn from_pair(pair: Pair) -> Self {
|
2023-08-20 01:38:42 -05:00
|
|
|
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 {
|
|
|
|
|
const RULE: Rule = Rule::conditional;
|
|
|
|
|
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 {
|
|
|
|
|
const RULE: Rule = Rule::while_loop;
|
|
|
|
|
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 {
|
|
|
|
|
const RULE: Rule = Rule::repeat_loop;
|
|
|
|
|
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
|
|
|
|
|
.next_if_rule(Rule::name)
|
|
|
|
|
.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 {
|
|
|
|
|
const RULE: Rule = Rule::expr;
|
2023-07-04 02:49:29 -05:00
|
|
|
fn from_pair(pair: Pair) -> Self {
|
2023-08-03 00:53:43 -05:00
|
|
|
// 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
|
2023-08-03 00:53:43 -05:00
|
|
|
// - 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() {
|
|
|
|
|
Rule::number => Expr::Number(term.into_number()),
|
|
|
|
|
Rule::name => Expr::Name(String::from(term.as_str())),
|
|
|
|
|
Rule::expr => Expr::from_pair(term),
|
2023-08-14 00:26:02 -05:00
|
|
|
Rule::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() {
|
|
|
|
|
Rule::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
|
|
|
),
|
|
|
|
|
Rule::subscript => {
|
|
|
|
|
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 {
|
2023-07-12 00:53:52 -05:00
|
|
|
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-08-02 21:56:26 -05:00
|
|
|
const RULE: Rule = Rule::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 {
|
|
|
|
|
const RULE: Rule = Rule::program;
|
|
|
|
|
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()
|
|
|
|
|
.filter(|p| p.as_rule() != Rule::EOI)
|
2023-08-20 01:38:42 -05:00
|
|
|
.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::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parse_globals() {
|
|
|
|
|
assert_eq!(
|
2023-07-08 22:31:53 -05:00
|
|
|
Global::from_str("global foo;"),
|
2023-07-03 01:35:37 -05:00
|
|
|
Ok(Global {
|
2023-07-02 02:10:06 -05:00
|
|
|
name: "foo".into(),
|
2023-08-02 21:56:26 -05:00
|
|
|
size: None,
|
2023-07-03 01:35:37 -05:00
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
2023-07-08 22:31:53 -05:00
|
|
|
Global::from_str("global foo[10];"),
|
2023-07-03 01:35:37 -05:00
|
|
|
Ok(Global {
|
|
|
|
|
name: "foo".into(),
|
2023-08-02 21:56:26 -05:00
|
|
|
size: Some(10.into()),
|
2023-07-03 01:35:37 -05:00
|
|
|
})
|
2023-07-02 02:10:06 -05:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parse_consts() {
|
|
|
|
|
assert_eq!(
|
2023-07-08 22:31:53 -05:00
|
|
|
Const::from_str("const a = 123;"),
|
2023-07-03 01:35:37 -05:00
|
|
|
Ok(Const {
|
2023-07-02 02:10:06 -05:00
|
|
|
name: "a".into(),
|
2023-08-02 21:56:26 -05:00
|
|
|
value: Some(123.into()),
|
|
|
|
|
string: None,
|
2023-07-03 01:35:37 -05:00
|
|
|
})
|
2023-07-02 02:10:06 -05:00
|
|
|
);
|
|
|
|
|
assert_eq!(
|
2023-07-08 22:31:53 -05:00
|
|
|
Const::from_str("const a = 0xaa;"),
|
2023-07-03 01:35:37 -05:00
|
|
|
Ok(Const {
|
2023-07-02 02:10:06 -05:00
|
|
|
name: "a".into(),
|
2023-08-02 21:56:26 -05:00
|
|
|
value: Some(0xaa.into()),
|
|
|
|
|
string: None,
|
2023-07-03 01:35:37 -05:00
|
|
|
})
|
2023-07-02 02:10:06 -05:00
|
|
|
);
|
|
|
|
|
assert_eq!(
|
2023-07-08 22:31:53 -05:00
|
|
|
Const::from_str("const a = -7;"),
|
2023-07-03 01:35:37 -05:00
|
|
|
Ok(Const {
|
2023-07-02 02:10:06 -05:00
|
|
|
name: "a".into(),
|
2023-08-04 21:38:09 -05:00
|
|
|
value: Some(Expr::Neg(7.into()).into()),
|
2023-08-02 21:56:26 -05:00
|
|
|
string: None,
|
2023-07-03 01:35:37 -05:00
|
|
|
})
|
2023-07-02 02:10:06 -05:00
|
|
|
);
|
|
|
|
|
assert_eq!(
|
2023-07-08 22:31:53 -05:00
|
|
|
Const::from_str("const a = \"foo bar\";"),
|
2023-07-03 01:35:37 -05:00
|
|
|
Ok(Const {
|
2023-07-02 02:10:06 -05:00
|
|
|
name: "a".into(),
|
|
|
|
|
value: None,
|
2023-08-02 21:56:26 -05:00
|
|
|
string: Some("foo bar".into()),
|
2023-07-03 01:35:37 -05:00
|
|
|
})
|
2023-07-02 02:10:06 -05:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-03 17:19:19 -05:00
|
|
|
#[test]
|
|
|
|
|
fn parse_function_headers() {
|
|
|
|
|
assert_eq!(
|
2023-07-08 22:31:53 -05:00
|
|
|
Function::from_str("fn foo() {}"),
|
2023-07-03 17:19:19 -05:00
|
|
|
Ok(Function {
|
|
|
|
|
name: "foo".into(),
|
|
|
|
|
args: vec![],
|
2023-07-08 20:54:23 -05:00
|
|
|
body: Block(vec![]),
|
2023-07-03 17:19:19 -05:00
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
2023-07-08 22:31:53 -05:00
|
|
|
Function::from_str("fn foo(a, b) {}"),
|
2023-07-03 17:19:19 -05:00
|
|
|
Ok(Function {
|
|
|
|
|
name: "foo".into(),
|
2023-08-15 00:39:53 -05:00
|
|
|
args: vec!["a".into(), "b".into()],
|
2023-07-08 20:54:23 -05:00
|
|
|
body: Block(vec![]),
|
2023-07-03 17:19:19 -05:00
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
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-08-03 00:53:43 -05:00
|
|
|
|
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"),
|
2023-08-03 00:53:43 -05:00
|
|
|
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"),
|
2023-08-03 00:53:43 -05:00
|
|
|
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())));
|
2023-08-03 00:53:43 -05:00
|
|
|
|
|
|
|
|
// Multiple prefixes
|
|
|
|
|
assert_eq!(
|
2023-08-04 21:38:09 -05:00
|
|
|
Expr::from_str("!-foo"),
|
|
|
|
|
Ok(Expr::Not(Expr::Neg("foo".into()).into()))
|
|
|
|
|
);
|
2023-08-03 00:53:43 -05:00
|
|
|
|
|
|
|
|
// Simple suffix
|
|
|
|
|
assert_eq!(
|
|
|
|
|
Expr::from_str("foo[10]"),
|
2023-08-05 11:07:09 -05:00
|
|
|
Ok(Expr::Subscript("foo".into(), 10.into()))
|
2023-08-03 00:53:43 -05:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
))
|
2023-08-03 00:53:43 -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())));
|
2023-08-03 00:53:43 -05:00
|
|
|
|
|
|
|
|
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()))
|
2023-08-03 00:53:43 -05:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
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()))
|
2023-08-03 00:53:43 -05:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
))
|
2023-08-03 00:53:43 -05:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
))
|
2023-08-03 00:53:43 -05:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
))
|
2023-08-03 00:53:43 -05:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
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()))
|
2023-08-03 00:53:43 -05:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 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-03 00:53:43 -05:00
|
|
|
);
|
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]
|
2023-08-03 00:53:43 -05:00
|
|
|
fn parse_calls() {
|
2023-09-01 22:24:36 -05:00
|
|
|
let blah = Expr::Call(Call { target: "blah".into(), args: vec![] });
|
2023-08-03 00:53:43 -05:00
|
|
|
|
|
|
|
|
// 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()] }))
|
2023-08-03 00:53:43 -05:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
//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-03 00:53:43 -05:00
|
|
|
);
|
2023-08-04 21:38:09 -05:00
|
|
|
}
|
2023-07-08 19:09:32 -05:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parse_return() {
|
2023-08-03 00:53:43 -05:00
|
|
|
assert_eq!(
|
|
|
|
|
Statement::from_str("return;"),
|
|
|
|
|
Ok(Statement::Return(Return(None)))
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
Statement::from_str("return 17;"),
|
|
|
|
|
Ok(Statement::Return(Return(Some(17.into()))))
|
|
|
|
|
);
|
2023-07-08 19:09:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parse_assignment() {
|
|
|
|
|
assert_eq!(
|
2023-07-08 22:31:53 -05:00
|
|
|
Statement::from_str("foo = 7;"),
|
2023-07-08 19:09:32 -05:00
|
|
|
Ok(Statement::Assignment(Assignment {
|
2023-08-04 17:27:28 -05:00
|
|
|
lvalue: "foo".into(),
|
2023-08-14 00:26:02 -05:00
|
|
|
rvalue: 7.into(),
|
2023-07-08 19:09:32 -05:00
|
|
|
}))
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
2023-07-08 22:31:53 -05:00
|
|
|
Assignment::from_str("foo[45] = 7"),
|
2023-07-08 19:09:32 -05:00
|
|
|
Ok(Assignment {
|
2023-08-13 23:55:49 -05:00
|
|
|
lvalue: Expr::Subscript("foo".into(), 45.into()).into(),
|
2023-08-14 00:26:02 -05:00
|
|
|
rvalue: 7.into(),
|
2023-07-08 19:09:32 -05:00
|
|
|
})
|
|
|
|
|
);
|
2023-08-13 19:29:50 -05:00
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
Assignment::from_str("*foo = 12"),
|
|
|
|
|
Ok(Assignment {
|
2023-08-13 23:55:49 -05:00
|
|
|
lvalue: Expr::Deref("foo".into()).into(),
|
2023-08-14 00:26:02 -05:00
|
|
|
rvalue: 12.into()
|
2023-08-13 19:29:50 -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() {
|
2023-08-20 01:38:42 -05:00
|
|
|
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!(
|
2023-08-20 01:38:42 -05:00
|
|
|
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-20 01:38:42 -05:00
|
|
|
]
|
2023-08-04 17:27:28 -05:00
|
|
|
);
|
|
|
|
|
}
|
2023-07-08 20:54:23 -05:00
|
|
|
|
2023-08-20 01:38:42 -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() {
|
2023-08-20 01:38:42 -05:00
|
|
|
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
|
|
|
|
2023-08-20 01:38:42 -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() {
|
2023-08-20 01:38:42 -05:00
|
|
|
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() {
|
2023-08-20 01:38:42 -05:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2023-08-15 01:30:35 -05:00
|
|
|
#[test]
|
|
|
|
|
fn parse_asm() {
|
|
|
|
|
assert_eq!(
|
|
|
|
|
Asm::from_str("asm { push 34 }"),
|
|
|
|
|
Ok(Asm { args: vec![], body: "push 34".into() })
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
Asm::from_str("asm (&a) { swap 34\nstorew }"),
|
|
|
|
|
Ok(Asm { args: vec![Expr::Address("a".into()).into()], body: "swap 34\nstorew".into() })
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-26 00:42:41 -05:00
|
|
|
#[test]
|
|
|
|
|
fn parse_fn_prototype() {
|
|
|
|
|
assert_eq!(
|
|
|
|
|
FunctionPrototype::from_str("fn foo();"),
|
|
|
|
|
Ok(FunctionPrototype {
|
|
|
|
|
name: "foo".into(),
|
|
|
|
|
args: vec![],
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-08 20:54:23 -05:00
|
|
|
#[test]
|
|
|
|
|
fn parse_program() {
|
2023-08-20 01:38:42 -05:00
|
|
|
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!(
|
2023-08-20 01:38:42 -05:00
|
|
|
decls,
|
|
|
|
|
vec![
|
2023-08-04 17:27:28 -05:00
|
|
|
Declaration::from_str("global foo;").unwrap(),
|
2023-08-20 01:38:42 -05:00
|
|
|
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
|
|
|
}
|