Compare commits

..

2 Commits

Author SHA1 Message Date
dc014272c3 basic pratt parser 2025-11-01 22:41:09 +01:00
563f608a46 minor changes 2025-11-01 20:49:46 +01:00
2 changed files with 110 additions and 14 deletions

View File

@@ -1,4 +1,6 @@
#[derive(Debug)]
use std::rc::Rc;
#[derive(Debug, Clone, Copy)]
pub enum TokenKind {
// keywords
Func,
@@ -17,9 +19,9 @@ pub enum TokenKind {
#[derive(Debug)]
pub struct Token {
kind: TokenKind,
value: Box<str>,
pos: usize,
pub kind: TokenKind,
pub value: Rc<str>,
pub pos: usize,
}
pub struct Lexer {
@@ -28,18 +30,22 @@ pub struct Lexer {
}
impl Lexer {
pub fn new(src: impl Into<Box<str>>) -> Self {
pub fn new(src: impl AsRef<str>) -> Self {
Self {
src: src.into(),
src: src.as_ref().into(),
pos: 0,
}
}
fn eof(&self) -> bool {
self.pos >= self.src.len()
}
pub fn lex(&mut self) -> Result<Vec<Token>, String> {
let mut output = vec![];
let chars = self.src.chars().collect::<Vec<char>>();
while self.pos < self.src.len() {
while !self.eof() {
let ch = chars[self.pos];
match ch {
@@ -67,7 +73,7 @@ impl Lexer {
}
ch if ch.is_ascii_digit() => {
let start = self.pos;
while self.pos < self.src.len() && chars[self.pos].is_ascii_digit() {
while !self.eof() && chars[self.pos].is_ascii_digit() {
self.pos += 1;
}
output.push(Token {
@@ -79,11 +85,11 @@ impl Lexer {
}
ch if ch.is_ascii_alphabetic() => {
let start = self.pos;
while self.pos < self.src.len() && chars[self.pos].is_ascii_alphanumeric() {
while !self.eof() && chars[self.pos].is_ascii_alphanumeric() {
self.pos += 1;
}
let word: Box<str> = self.src[start..self.pos].into();
let kind = match word.as_ref() {
let word = &self.src[start..self.pos];
let kind = match word {
"func" => TokenKind::Func,
"do" => TokenKind::Do,
"end" => TokenKind::End,
@@ -91,7 +97,7 @@ impl Lexer {
};
output.push(Token {
kind,
value: word,
value: word.into(),
pos: start,
});
continue;

View File

@@ -1,8 +1,98 @@
use std::{fmt::Display, rc::Rc};
mod lexer;
use lexer::*;
#[derive(Debug, Clone, Copy)]
enum InfixOp {
Add,
}
impl InfixOp {
fn bp(&self) -> (f32, f32) {
match self {
Self::Add => (1.0, 1.1),
}
}
}
impl TryFrom<TokenKind> for InfixOp {
type Error = ();
fn try_from(value: TokenKind) -> Result<Self, Self::Error> {
Ok(match value {
TokenKind::Plus => Self::Add,
_ => return Err(()),
})
}
}
#[derive(Debug)]
enum Expr {
Integer(u64),
Variable(Rc<str>),
Infix(InfixOp, Box<Expr>, Box<Expr>),
}
impl Display for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expr::Integer(i) => write!(f, "{}", i),
Expr::Variable(v) => write!(f, "{}", v),
Expr::Infix(op, e1, e2) => write!(f, "({:?} {} {})", op, e1, e2),
}
}
}
fn parse_atom(tokens: &[Token]) -> Result<(Expr, &[Token]), String> {
let t = &tokens[0];
Ok(match t.kind {
TokenKind::Integer => {
let Ok(int) = t.value.parse() else {
return Err(format!("failed to parse integer at position {}", t.pos));
};
(Expr::Integer(int), &tokens[1..])
}
TokenKind::Identifier => (Expr::Variable(Rc::clone(&t.value)), &tokens[1..]),
_ => {
return Err(format!("unexpected token at position {}", t.pos));
}
})
}
fn parse_expr(tokens: &[Token], bp: f32) -> Result<(Expr, &[Token]), String> {
let mut left: Expr;
let mut tokens = tokens;
(left, tokens) = parse_atom(tokens)?;
while !tokens.is_empty() {
let t = &tokens[0];
let op: InfixOp = t
.kind
.try_into()
.map_err(|_| format!("invalid operator '{}' at position {}", t.value, t.pos))?;
let (lbp, rbp) = op.bp();
if lbp < bp {
break;
}
let (right, rest) = parse_expr(&tokens[1..], rbp)?;
left = Expr::Infix(op, Box::new(left), Box::new(right));
tokens = rest;
}
return Ok((left, tokens));
}
fn main() {
let mut lexer = lexer::Lexer::new("func do end foo(123)door+1");
let mut lexer = Lexer::new("34 + 35");
let tokens = lexer.lex().expect("failed to lex");
let (expr, rest) = parse_expr(&tokens, 0.0).expect("failed to parse");
eprintln!("{:#?}", tokens);
assert!(rest.is_empty(), "unexpected tokens");
eprintln!("{}", expr);
}