function calls & improved prefix ops
This commit is contained in:
parent
706f4b3763
commit
6049299cab
20
src/lexer.rs
20
src/lexer.rs
|
@ -66,13 +66,15 @@ pub enum Token {
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum Precedence {
|
pub enum Precedence {
|
||||||
Min,
|
Min,
|
||||||
|
Return,
|
||||||
Assign,
|
Assign,
|
||||||
|
Logical,
|
||||||
|
Equality,
|
||||||
|
Relational,
|
||||||
AddSub,
|
AddSub,
|
||||||
MulDiv,
|
MulDiv,
|
||||||
Pow,
|
Pow,
|
||||||
Logical,
|
NegateNot,
|
||||||
Relational,
|
|
||||||
Equality,
|
|
||||||
}
|
}
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
pub enum Associativity {
|
pub enum Associativity {
|
||||||
|
@ -80,8 +82,14 @@ pub enum Associativity {
|
||||||
Right,
|
Right,
|
||||||
}
|
}
|
||||||
impl Token {
|
impl Token {
|
||||||
// binop precedence ^_^
|
pub fn prefix_precedence(&self) -> Option<Precedence> {
|
||||||
pub fn precedence(&self) -> Option<(Precedence, Associativity)> {
|
Some(match self {
|
||||||
|
Token::Return => Precedence::Return,
|
||||||
|
Token::Minus | Token::Not => Precedence::NegateNot,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn infix_precedence(&self) -> Option<(Precedence, Associativity)> {
|
||||||
Some(match self {
|
Some(match self {
|
||||||
Token::EqualTo | Token::NotEqualTo => (Precedence::Equality, Associativity::Left),
|
Token::EqualTo | Token::NotEqualTo => (Precedence::Equality, Associativity::Left),
|
||||||
Token::LessThan
|
Token::LessThan
|
||||||
|
@ -234,7 +242,7 @@ where
|
||||||
'{' => self.eat_to(Token::CurlyOpen),
|
'{' => self.eat_to(Token::CurlyOpen),
|
||||||
'}' => self.eat_to(Token::CurlyClose),
|
'}' => self.eat_to(Token::CurlyClose),
|
||||||
|
|
||||||
// ( and ) start/end of parens (idk)
|
// ( and ) start/end of groups
|
||||||
'(' => self.eat_to(Token::ParenOpen),
|
'(' => self.eat_to(Token::ParenOpen),
|
||||||
')' => self.eat_to(Token::ParenClose),
|
')' => self.eat_to(Token::ParenClose),
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
use crate::{lexer::Lexer, parser::Parser};
|
use crate::{lexer::Lexer, parser::Parser};
|
||||||
|
|
||||||
mod lexer;
|
mod lexer;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod runtime;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let script = std::fs::read_to_string("./start.leaf").unwrap();
|
let script = std::fs::read_to_string("./start.leaf").unwrap();
|
||||||
let lexer = Lexer::new(script.chars());
|
let lexer = Lexer::new(script.chars());
|
||||||
let mut parser = Parser::new(lexer.map(Result::unwrap));
|
let mut parser = Parser::new(lexer.map(Result::unwrap));
|
||||||
|
let start = Instant::now();
|
||||||
let block = parser.parse().unwrap();
|
let block = parser.parse().unwrap();
|
||||||
println!("{block:?}");
|
println!("Parse took {:?}", start.elapsed());
|
||||||
|
parser::util::display(parser::Expr::Block(block));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,18 @@ use std::iter::Peekable;
|
||||||
|
|
||||||
use crate::lexer::{Associativity, LexError, Literal, Precedence, Token};
|
use crate::lexer::{Associativity, LexError, Literal, Precedence, Token};
|
||||||
|
|
||||||
|
pub mod util;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
// Data and variables
|
// Data and variables
|
||||||
Assignment(Box<Expr>, Box<Expr>),
|
Assignment(Box<Expr>, Box<Expr>),
|
||||||
Literal(Literal),
|
Literal(Literal),
|
||||||
// Control flow
|
|
||||||
Call(Box<Expr>, Vec<Expr>),
|
|
||||||
Return(Box<Expr>),
|
|
||||||
// Runtime datatypes
|
// Runtime datatypes
|
||||||
Block(Block),
|
Block(Block),
|
||||||
|
// Control flow
|
||||||
|
Return(Box<Expr>),
|
||||||
|
Call(Box<Expr>, Vec<Expr>),
|
||||||
// Unary operations
|
// Unary operations
|
||||||
Negate(Box<Expr>),
|
Negate(Box<Expr>),
|
||||||
Not(Box<Expr>),
|
Not(Box<Expr>),
|
||||||
|
@ -35,7 +37,7 @@ pub enum Expr {
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
exprs: Vec<Expr>,
|
pub exprs: Vec<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -96,6 +98,17 @@ where
|
||||||
self.eat();
|
self.eat();
|
||||||
Box::new(Expr::Block(b))
|
Box::new(Expr::Block(b))
|
||||||
}
|
}
|
||||||
|
// unary ops!! (prefix)
|
||||||
|
t if t.prefix_precedence().is_some() => {
|
||||||
|
let prec = t.prefix_precedence().unwrap();
|
||||||
|
let rhs = self.parse_expr(prec, in_group)?;
|
||||||
|
Box::new(match t {
|
||||||
|
Token::Minus => Expr::Negate(rhs),
|
||||||
|
Token::Not => Expr::Not(rhs),
|
||||||
|
Token::Return => Expr::Return(rhs),
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
}
|
||||||
// return
|
// return
|
||||||
Token::Return => Box::new(Expr::Return(self.parse_expr(Precedence::Min, false)?)),
|
Token::Return => Box::new(Expr::Return(self.parse_expr(Precedence::Min, false)?)),
|
||||||
// not
|
// not
|
||||||
|
@ -110,15 +123,28 @@ where
|
||||||
Ok(Token::ParenClose) if in_group => break,
|
Ok(Token::ParenClose) if in_group => break,
|
||||||
// end (stream)
|
// end (stream)
|
||||||
Err(_) if !in_group => break,
|
Err(_) if !in_group => break,
|
||||||
|
// function call
|
||||||
|
Ok(Token::ParenOpen) => {
|
||||||
|
// eat opening paren
|
||||||
|
self.eat();
|
||||||
|
let mut exprs = Vec::new();
|
||||||
|
while !matches!(self.try_peek()?, Token::ParenClose) {
|
||||||
|
exprs.push(*self.parse_expr(Precedence::Min, false)?);
|
||||||
|
}
|
||||||
|
// eat closing paren
|
||||||
|
self.eat();
|
||||||
|
lhs = Box::new(Expr::Call(lhs, exprs));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// operator
|
// operator
|
||||||
Ok(t) if t.precedence().is_some() => t,
|
Ok(t) if t.infix_precedence().is_some() => t,
|
||||||
// unexpected token (stop trying to parse)
|
// unexpected token (stop trying to parse)
|
||||||
Ok(_) => break,
|
Ok(_) => break,
|
||||||
// unexpected end
|
// unexpected end
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (prec, assoc) = op.precedence().unwrap();
|
let (prec, assoc) = op.infix_precedence().unwrap();
|
||||||
|
|
||||||
// break if this op is meant for previous recursion
|
// break if this op is meant for previous recursion
|
||||||
// or it's equal and we would prefer to build leftward..
|
// or it's equal and we would prefer to build leftward..
|
||||||
|
@ -138,9 +164,9 @@ where
|
||||||
Token::NotEqualTo => Expr::NotEqualTo(lhs, rhs),
|
Token::NotEqualTo => Expr::NotEqualTo(lhs, rhs),
|
||||||
// relational
|
// relational
|
||||||
Token::LessThan => Expr::LessThan(lhs, rhs),
|
Token::LessThan => Expr::LessThan(lhs, rhs),
|
||||||
Token::LessThanOrEqualTo => Expr::LessThan(lhs, rhs),
|
Token::LessThanOrEqualTo => Expr::LessThanOrEqualTo(lhs, rhs),
|
||||||
Token::GreaterThan => Expr::LessThan(lhs, rhs),
|
Token::GreaterThan => Expr::GreaterThan(lhs, rhs),
|
||||||
Token::GreaterThanOrEqualTo => Expr::LessThan(lhs, rhs),
|
Token::GreaterThanOrEqualTo => Expr::GreaterThanOrEqualTo(lhs, rhs),
|
||||||
// logical
|
// logical
|
||||||
Token::And => Expr::And(lhs, rhs),
|
Token::And => Expr::And(lhs, rhs),
|
||||||
Token::Or => Expr::Or(lhs, rhs),
|
Token::Or => Expr::Or(lhs, rhs),
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
use crate::parser::Expr;
|
||||||
|
|
||||||
|
pub fn display(e: Expr) {
|
||||||
|
let result = fmt_expr(e, 0);
|
||||||
|
println!("{result}");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_binop(left: Expr, right: Expr, op: &str, depth: usize) -> String {
|
||||||
|
format!(
|
||||||
|
"({} {} {})",
|
||||||
|
fmt_expr(left, depth),
|
||||||
|
op,
|
||||||
|
fmt_expr(right, depth)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_expr(e: Expr, depth: usize) -> String {
|
||||||
|
match e {
|
||||||
|
Expr::Assignment(l, r) => fmt_binop(*l, *r, "=", depth),
|
||||||
|
Expr::Literal(l) => l.to_string(),
|
||||||
|
Expr::Call(l, r) => {
|
||||||
|
let mut result = fmt_expr(*l, depth);
|
||||||
|
result.push('(');
|
||||||
|
let len = r.len();
|
||||||
|
for (i, e) in r.into_iter().enumerate() {
|
||||||
|
result.push_str(&fmt_expr(e, depth));
|
||||||
|
if i + 1 != len {
|
||||||
|
result.push_str(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push(')');
|
||||||
|
result
|
||||||
|
}
|
||||||
|
Expr::Return(l) => format!("return {}", fmt_expr(*l, depth)),
|
||||||
|
Expr::Block(b) => {
|
||||||
|
let mut result = String::new();
|
||||||
|
let len = b.exprs.len();
|
||||||
|
for (i, expr) in b.exprs.into_iter().enumerate() {
|
||||||
|
result.push_str(&" ".repeat(depth));
|
||||||
|
result.push_str(&fmt_expr(expr, depth + 1));
|
||||||
|
if depth != 0 || i + 1 != len {
|
||||||
|
result.push('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if depth != 0 {
|
||||||
|
result = format!("{{\n{result}{}}}", " ".repeat(depth - 1));
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
Expr::Negate(l) => format!("(-{})", fmt_expr(*l, depth)),
|
||||||
|
Expr::Not(l) => format!("(!{})", fmt_expr(*l, depth)),
|
||||||
|
Expr::EqualTo(l, r) => fmt_binop(*l, *r, "==", depth),
|
||||||
|
Expr::NotEqualTo(l, r) => fmt_binop(*l, *r, "!=", depth),
|
||||||
|
Expr::And(l, r) => fmt_binop(*l, *r, "&&", depth),
|
||||||
|
Expr::Or(l, r) => fmt_binop(*l, *r, "||", depth),
|
||||||
|
Expr::LessThan(l, r) => fmt_binop(*l, *r, "<", depth),
|
||||||
|
Expr::LessThanOrEqualTo(l, r) => fmt_binop(*l, *r, "<=", depth),
|
||||||
|
Expr::GreaterThan(l, r) => fmt_binop(*l, *r, ">", depth),
|
||||||
|
Expr::GreaterThanOrEqualTo(l, r) => fmt_binop(*l, *r, ">=", depth),
|
||||||
|
Expr::Add(l, r) => fmt_binop(*l, *r, "+", depth),
|
||||||
|
Expr::Subtract(l, r) => fmt_binop(*l, *r, "-", depth),
|
||||||
|
Expr::Multiply(l, r) => fmt_binop(*l, *r, "*", depth),
|
||||||
|
Expr::Divide(l, r) => fmt_binop(*l, *r, "/", depth),
|
||||||
|
Expr::Exponent(l, r) => fmt_binop(*l, *r, "^", depth),
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +0,0 @@
|
||||||
use crate::parser::{Block, Expr};
|
|
||||||
|
|
||||||
pub enum Value {
|
|
||||||
String(String),
|
|
||||||
Integer(i64),
|
|
||||||
Boolean(bool),
|
|
||||||
Nil,
|
|
||||||
Block(Block),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn eval(e: Expr) -> Value {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates all expressions of a block.
|
|
||||||
pub fn exec(b: Block) -> Value {
|
|
||||||
todo!()
|
|
||||||
}
|
|
Loading…
Reference in New Issue