added repl, eof token, and a parser with a AST producer

This commit is contained in:
2026-07-08 23:44:39 -05:00
parent a0a7b85e11
commit 7ee19e4ab7
7 changed files with 288 additions and 5 deletions
+6
View File
@@ -1,27 +1,33 @@
pub trait Stmt {}
pub trait Expr: Stmt {}
#[derive(Debug)]
pub enum NodeType {
Program(Program),
NumericLiteral(NumericLiteral),
Identifier(Identifier),
BinaryExpr(BinaryExpr),
EOF,
}
#[derive(Debug)]
pub struct Program {
pub body: Vec<NodeType>,
}
#[derive(Debug)]
pub struct BinaryExpr {
pub left: Box<NodeType>,
pub right: Box<NodeType>,
pub operator: String,
}
#[derive(Debug)]
pub struct Identifier {
pub symbol: String,
}
#[derive(Debug)]
pub struct NumericLiteral {
pub value: f64,
}
+28
View File
@@ -0,0 +1,28 @@
use std::process;
use crate::parser::{self, Parser};
pub fn repl() {
// Read-Eval-Print Loop
use std::io::{self, Write};
println!("Welcome to the Riv Lang REPL! Type 'exit' to quit.");
loop {
print!("> ");
io::stdout().flush().unwrap();
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
let input = input.trim().to_string();
if input == "exit" {
process::exit(0);
}
let program = Parser::new(input).produce_ast();
println!("{:#?}", program);
}
}
+5
View File
@@ -28,6 +28,7 @@ pub mod interpreter {
OpenParen,
CloseParen,
BinaryOperator(String),
EOF,
}
static KEYWORDS: phf::Map<&'static str, Token> = phf::phf_map! {
@@ -77,6 +78,7 @@ pub mod interpreter {
Token::Let => write!(f, "Token::Let"),
Token::OpenParen => write!(f, "Token::OpenParen"),
Token::CloseParen => write!(f, "Token::CloseParen"),
Token::EOF => write!(f, "Token::EOF"),
}
}
}
@@ -124,6 +126,9 @@ pub mod interpreter {
}
}
// EOF Token
tokens.push(Token::EOF);
tokens
}
}
+26 -5
View File
@@ -1,13 +1,34 @@
use clap::Parser;
mod ast;
mod frontend;
mod lexer;
mod parser;
#[derive(clap::Parser)]
struct CLI {
/// Path to the source code file
#[arg(short, long)]
file: Option<String>,
/// Run the REPL (Read-Eval-Print Loop)
#[arg(short, long)]
repl: bool,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let source_code = std::fs::read_to_string("./test.riv")?;
let cli = CLI::parse();
let tokens = lexer::interpreter::tokenize(source_code);
for t in tokens {
println!("{t}");
if cli.repl {
frontend::repl();
} else if let Some(file_path) = cli.file {
let source_code = std::fs::read_to_string(file_path)?;
let mut parser = parser::Parser::new(source_code);
let program = parser.produce_ast();
println!("{:#?}", program);
} else {
eprintln!("Please provide a file path or use the --repl flag.");
std::process::exit(1);
}
Ok(())
+80
View File
@@ -0,0 +1,80 @@
use std::process;
use crate::ast::{BinaryExpr, Expr, Identifier, NodeType, NumericLiteral, Program, Stmt};
use crate::lexer::interpreter;
pub struct Parser {
tokens: std::iter::Peekable<std::vec::IntoIter<interpreter::Token>>,
}
impl Parser {
pub fn new(source_code: String) -> Self {
Self {
tokens: interpreter::tokenize(source_code).into_iter().peekable(),
}
}
pub fn produce_ast(&mut self) -> Program {
let mut program = Program { body: vec![] };
while let Some(t) = self.tokens.next() {
program.body.push(self.parse_stmt(t));
}
program
}
fn parse_stmt(&mut self, t: interpreter::Token) -> NodeType {
// skip to parse_expr
self.parse_expr(t)
}
fn parse_expr(&mut self, t: interpreter::Token) -> NodeType {
self.parse_additive_expr(t)
}
fn parse_primary_expr(&mut self, t: interpreter::Token) -> NodeType {
use crate::ast::NodeType;
use interpreter::Token;
match t {
Token::Identifier(i) => NodeType::Identifier(Identifier { symbol: i.clone() }),
Token::Number(n) => NodeType::NumericLiteral(NumericLiteral {
value: n.parse().expect(
format!("Failed to parse source code, {} was not a number", n).as_str(),
),
}),
Token::EOF => NodeType::EOF,
_ => {
eprintln!("Unexpected token: {}", t);
process::exit(1);
}
}
}
fn parse_additive_expr(&mut self, t: interpreter::Token) -> NodeType {
let mut left = self.parse_primary_expr(t);
while let Some(interpreter::Token::BinaryOperator(operator)) = self.tokens.peek() {
if operator != "+" && operator != "-" {
break;
}
let operator = operator.clone();
self.tokens.next();
let right = match self.tokens.next() {
Some(token) => self.parse_primary_expr(token),
None => break,
};
left = NodeType::BinaryExpr(BinaryExpr {
left: Box::new(left),
right: Box::new(right),
operator,
});
}
left
}
}