Start of expression parsing
This commit is contained in:
@@ -53,3 +53,75 @@ pub struct Argname {
|
||||
pub name: String,
|
||||
pub typename: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
pub struct Block {
|
||||
pub statements: Vec<Statement>,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
pub enum Statement {
|
||||
Return(Return),
|
||||
Assignment(Assignment),
|
||||
Call(Call),
|
||||
VarDecl(VarDecl),
|
||||
Conditional(Conditional),
|
||||
WhileLoop(WhileLoop),
|
||||
RepeatLoop(RepeatLoop),
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
pub struct Return {}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
pub struct Assignment {}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
pub struct Call {}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
pub struct VarDecl {}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
pub struct Conditional {}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
pub struct WhileLoop {}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
pub struct RepeatLoop {}
|
||||
|
||||
/// One of the five arithmetical operators
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
pub enum Operator {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Mod,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub enum Node {
|
||||
Number(i32),
|
||||
Call(Call),
|
||||
ArrayRef(ArrayRef),
|
||||
Name(String),
|
||||
Address(String),
|
||||
Expr(Box<Node>, Vec<(Operator, Node)>),
|
||||
}
|
||||
|
||||
impl From<i32> for Node {
|
||||
fn from(val: i32) -> Self {
|
||||
Self::Number(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Node {
|
||||
fn from(s: &str) -> Self {
|
||||
Self::Name(String::from(s))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
pub struct ArrayRef {}
|
||||
|
||||
@@ -31,17 +31,16 @@ val = { // These are what get evaluated and left on the stack:
|
||||
// (call, or pointer arithmetic, since ids all reference addresses)
|
||||
// (multiple calls / subscripts aren't allowed; use CALL or collapse to one
|
||||
// subscript)
|
||||
call | arrayref | name |
|
||||
//(name ~ (arglist | subscript)?) |
|
||||
("&" ~ name) } // The address for an name
|
||||
call | arrayref | name | address }
|
||||
|
||||
address = { "&" ~ name } // The address for a name
|
||||
arrayref = { name ~ subscript } // Subscripting is only literal IDs. Anything else can be pointer arithmetic
|
||||
call = { name ~ arglist } // Call syntax is only literal IDs. Calling things dynamically is done with a builtin; CALL(foo[3], 1, 2, 3)
|
||||
arg = { expr | string }
|
||||
arglist = { "(" ~ (arg ~ ("," ~ arg)*)? ~ ")" }
|
||||
subscript = { "[" ~ expr ~ "]" }
|
||||
|
||||
statement = { ((return_stmt | assignment | call | var_decl) ~ ";") | conditional | loop_stmt }
|
||||
statement = { ((return_stmt | assignment | call | var_decl) ~ ";") | conditional | while_loop | repeat_loop }
|
||||
|
||||
block = { "{" ~ statement* ~ "}" }
|
||||
|
||||
@@ -60,9 +59,8 @@ program = { (COMMENT | WHITESPACE)? ~ declaration* ~ EOI }
|
||||
|
||||
return_stmt = { "return" ~ expr }
|
||||
conditional = { "if" ~ "(" ~ expr ~ ")" ~ block ~ ("else" ~ block)? }
|
||||
loop_stmt = { while_stmt | repeat }
|
||||
while_stmt = { "while" ~ "(" ~ expr ~ ")" ~ block }
|
||||
repeat = { "repeat" ~ "(" ~ expr ~ ")" ~ name ~ block }
|
||||
while_loop = { "while" ~ "(" ~ expr ~ ")" ~ block }
|
||||
repeat_loop = { "repeat" ~ "(" ~ expr ~ ")" ~ name ~ block }
|
||||
var_decl = { "var" ~ name ~ varinfo ~ ("=" ~ expr)? }
|
||||
global = { "global" ~ name ~ varinfo ~ ";" }
|
||||
typename = { ":" ~ name }
|
||||
|
||||
@@ -281,6 +281,153 @@ impl AstNode for Argname {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl AstNode for Statement {
|
||||
const RULE: Rule = Rule::declaration;
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
match pair.as_rule() {
|
||||
Rule::return_stmt => Self::Return(Return::from_pair(pair)),
|
||||
Rule::assignment => Self::Assignment(Assignment::from_pair(pair)),
|
||||
Rule::call => Self::Call(Call::from_pair(pair)),
|
||||
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)),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl AstNode for Return {
|
||||
const RULE: Rule = Rule::return_stmt;
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl AstNode for Assignment {
|
||||
const RULE: Rule = Rule::assignment;
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl AstNode for Call {
|
||||
const RULE: Rule = Rule::call;
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl AstNode for VarDecl {
|
||||
const RULE: Rule = Rule::var_decl;
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl AstNode for Conditional {
|
||||
const RULE: Rule = Rule::conditional;
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl AstNode for WhileLoop {
|
||||
const RULE: Rule = Rule::while_loop;
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl AstNode for RepeatLoop {
|
||||
const RULE: Rule = Rule::repeat_loop;
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl AstNode for Node {
|
||||
const RULE: Rule = Rule::expr; // Also used for vals / terms / the whole tree
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
let expr = match pair.as_rule() {
|
||||
Rule::val => Node::from_pair(pair.first()),
|
||||
Rule::number => Node::Number(pair.into_number()),
|
||||
Rule::name => Node::Name(String::from(pair.as_str())),
|
||||
Rule::call => todo!(),
|
||||
Rule::arrayref => todo!(),
|
||||
Rule::address => todo!(),
|
||||
Rule::expr | Rule::term => {
|
||||
let mut children = pair.into_inner();
|
||||
let car = Node::from_pair(children.next().unwrap());
|
||||
Node::Expr(Box::new(car), parse_expr(children))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
shake_expr(expr)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_expr(mut cdr: Pairs) -> Vec<(Operator, Node)> {
|
||||
let mut terms = Vec::new();
|
||||
while let Some(operator) = cdr.next() {
|
||||
let rhs = cdr.next().unwrap();
|
||||
let rhs_r = rhs.as_rule();
|
||||
let rhs = Node::from_pair(rhs);
|
||||
let op = Operator::from_pair(operator);
|
||||
terms.push((op, rhs));
|
||||
}
|
||||
terms
|
||||
}
|
||||
|
||||
fn shake_expr(node: Node) -> Node {
|
||||
match node {
|
||||
Node::Expr(car, cdr) => {
|
||||
let shaken_car = shake_expr(*car);
|
||||
if cdr.is_empty() {
|
||||
shaken_car
|
||||
} else {
|
||||
let shaken_cdr = cdr.into_iter().map(|(op, n)| (op, shake_expr(n))).collect();
|
||||
Node::Expr(Box::from(shaken_car), shaken_cdr)
|
||||
}
|
||||
}
|
||||
_ => node,
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl AstNode for Operator {
|
||||
const RULE: Rule = Rule::sign; // also term_op
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
match pair.as_str() {
|
||||
"+" => Self::Add,
|
||||
"-" => Self::Sub,
|
||||
"*" => Self::Mul,
|
||||
"/" => Self::Div,
|
||||
"%" => Self::Mod,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
@@ -480,4 +627,56 @@ mod test {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_exprs() {
|
||||
use Node::*;
|
||||
use Operator::*;
|
||||
// A very, very basic expression
|
||||
assert_eq!(Node::parse("23"), Ok(Number(23)));
|
||||
|
||||
// Two vals with an operator
|
||||
assert_eq!(
|
||||
Node::parse("23 + 5"),
|
||||
Ok(Expr(Number(23).into(), vec![(Add, Number(5))]))
|
||||
);
|
||||
|
||||
// Multiple terms at the same precedence level
|
||||
assert_eq!(
|
||||
Node::parse("1 + 2 + 3"),
|
||||
Ok(Expr(
|
||||
Number(1).into(),
|
||||
vec![(Add, 2.into()), (Add, 3.into())]
|
||||
))
|
||||
);
|
||||
|
||||
// Higher precedence levels
|
||||
assert_eq!(
|
||||
Node::parse("1 + 2 * 3"),
|
||||
Ok(Node::Expr(
|
||||
Number(1).into(),
|
||||
vec![(Add, Expr(Number(2).into(), vec![(Mul, 3.into())]))]
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
Node::parse("2 * 3"),
|
||||
Ok(Expr(Number(2).into(), vec![(Mul, 3.into())]))
|
||||
);
|
||||
assert_eq!(
|
||||
Node::parse("2 * 3 + 4"),
|
||||
Ok(Expr(
|
||||
Expr(Number(2).into(), vec![(Mul, 3.into())]).into(),
|
||||
vec![(Add, 4.into())]
|
||||
))
|
||||
);
|
||||
|
||||
// Parens
|
||||
assert_eq!(
|
||||
Node::parse("(1 + 2) * 3"),
|
||||
Ok(Node::Expr(
|
||||
Expr(Number(1).into(), vec![(Add, 2.into())]).into(),
|
||||
vec![(Mul, 3.into())]
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user