Initial commit

This commit is contained in:
2026-02-23 08:51:37 -05:00
commit 757a132930
25 changed files with 1780 additions and 0 deletions

7
aurac_parser/Cargo.toml Normal file
View File

@@ -0,0 +1,7 @@
[package]
name = "aurac_parser"
version = "0.1.0"
edition = "2021"
[dependencies]
aurac_lexer = { path = "../aurac_lexer" }

75
aurac_parser/src/ast.rs Normal file
View File

@@ -0,0 +1,75 @@
#[derive(Debug, Clone, PartialEq)]
pub struct Program {
pub decls: Vec<Decl>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Decl {
Struct(StructDecl),
Fn(FnDecl),
TypeAlias(String, TypeExpr),
}
#[derive(Debug, Clone, PartialEq)]
pub struct StructDecl {
pub name: String,
pub fields: Vec<FieldDecl>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FieldDecl {
pub name: String,
pub ty: TypeExpr,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FnDecl {
pub is_pure: bool,
pub is_gpu: bool,
pub name: String,
pub params: Vec<Param>,
pub return_type: TypeExpr,
pub body: Block,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Param {
pub name: String,
pub ty: TypeExpr,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Block {
pub statements: Vec<Stmt>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Stmt {
Return(Expr),
ExprStmt(Expr),
LetBind(String, Expr),
}
#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
Binary(Box<Expr>, BinaryOp, Box<Expr>),
Literal(String),
Identifier(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BinaryOp {
Add,
Sub,
Mul,
Div,
Gt,
Lt,
Eq,
}
#[derive(Debug, Clone, PartialEq)]
pub enum TypeExpr {
BaseType(String),
Refined(Box<TypeExpr>, String, Box<Expr>),
}

87
aurac_parser/src/lib.rs Normal file
View File

@@ -0,0 +1,87 @@
pub mod ast;
pub mod parser;
#[cfg(test)]
mod tests {
use super::parser::Parser;
use super::ast::{StructDecl, FieldDecl, TypeExpr, FnDecl, Param, Block, Stmt, Expr, BinaryOp};
#[test]
fn test_parse_pure_fn() {
let input = "pure fn add(a: f32, b: f32) -> f32:\n return a + b\n";
let mut lexer = Lexer::new(input);
let mut tokens = Vec::new();
loop {
let tok = lexer.next_token();
let is_eof = tok.kind == TokenKind::Eof;
tokens.push(tok);
if is_eof {
break;
}
}
let mut parser = Parser::new(&tokens);
let fn_decl = parser.parse_fn_decl().expect("Failed to parse pure fn decl");
let expected = FnDecl {
is_pure: true,
is_gpu: false,
name: "add".to_string(),
params: vec![
Param { name: "a".to_string(), ty: TypeExpr::BaseType("f32".to_string()) },
Param { name: "b".to_string(), ty: TypeExpr::BaseType("f32".to_string()) },
],
return_type: TypeExpr::BaseType("f32".to_string()),
body: Block {
statements: vec![
Stmt::Return(Expr::Binary(
Box::new(Expr::Identifier("a".to_string())),
BinaryOp::Add,
Box::new(Expr::Identifier("b".to_string())),
))
]
}
};
assert_eq!(fn_decl, expected);
}
use aurac_lexer::lexer::Lexer;
use aurac_lexer::token::TokenKind;
#[test]
fn test_parse_struct_decl() {
let input = "struct Position:\n x: f32\n y: f32\n";
let mut lexer = Lexer::new(input);
// Exhaust the lexer to contiguous slice to mimic real pipelines
let mut tokens = Vec::new();
loop {
let tok = lexer.next_token();
let is_eof = tok.kind == TokenKind::Eof;
tokens.push(tok);
if is_eof {
break;
}
}
let mut parser = Parser::new(&tokens);
let struct_decl = parser.parse_struct_decl().expect("Failed to parse struct decl");
let expected = StructDecl {
name: "Position".to_string(),
fields: vec![
FieldDecl {
name: "x".to_string(),
ty: TypeExpr::BaseType("f32".to_string()),
},
FieldDecl {
name: "y".to_string(),
ty: TypeExpr::BaseType("f32".to_string()),
},
],
};
assert_eq!(struct_decl, expected);
}
}

278
aurac_parser/src/parser.rs Normal file
View File

@@ -0,0 +1,278 @@
use aurac_lexer::token::{Token, TokenKind};
use crate::ast::*;
pub struct Parser<'a> {
tokens: &'a [Token<'a>],
current: usize,
}
impl<'a> Parser<'a> {
pub fn new(tokens: &'a [Token<'a>]) -> Self {
Self { tokens, current: 0 }
}
fn peek(&self) -> Option<&Token<'a>> {
self.tokens.get(self.current)
}
fn advance(&mut self) -> Option<&Token<'a>> {
let curr = self.peek();
self.current += 1;
curr
}
fn expect(&mut self, expected: TokenKind<'a>) -> Result<(), String> {
let peeked = self.peek();
match peeked {
Some(curr) if curr.kind == expected => {
self.advance();
Ok(())
},
Some(curr) => Err(format!("Expected {:?}, found {:?}", expected, curr.kind)),
None => Err(format!("Expected {:?}, found EOF", expected)),
}
}
pub fn parse_struct_decl(&mut self) -> Result<StructDecl, String> {
self.expect(TokenKind::Struct)?;
let name = match self.advance() {
Some(Token { kind: TokenKind::Ident(id), .. }) => id.to_string(),
Some(t) => return Err(format!("Expected identifier after struct, found {:?}", t.kind)),
None => return Err("Expected identifier after struct, found EOF".to_string()),
};
self.expect(TokenKind::Colon)?;
self.expect(TokenKind::Newline)?;
self.expect(TokenKind::Indent)?;
let mut fields = Vec::new();
loop {
// Ignore leading blank lines inside struct bodies
while let Some(Token { kind: TokenKind::Newline, .. }) = self.peek() {
self.advance();
}
match self.peek() {
Some(Token { kind: TokenKind::Dedent, .. }) => {
self.advance();
break;
}
Some(Token { kind: TokenKind::Ident(_), .. }) => {
let field_name = if let Token { kind: TokenKind::Ident(id), .. } = self.advance().unwrap() {
id.to_string()
} else {
unreachable!()
};
self.expect(TokenKind::Colon)?;
let ty = self.parse_type_expr()?;
fields.push(FieldDecl { name: field_name, ty });
// Fields must be terminated by Newline or immediately ended with Dedent
match self.peek() {
Some(Token { kind: TokenKind::Newline, .. }) => {
self.advance();
},
Some(Token { kind: TokenKind::Dedent, .. }) => (),
Some(t) => return Err(format!("Expected Newline or Dedent after field, found {:?}", t.kind)),
None => return Err("Unexpected EOF in struct fields".to_string()),
}
}
Some(t) => return Err(format!("Expected field declaration or Dedent, found {:?}", t.kind)),
None => return Err("Unexpected EOF parsing struct".to_string()),
}
}
Ok(StructDecl { name, fields })
}
pub fn parse_fn_decl(&mut self) -> Result<FnDecl, String> {
let mut is_pure = false;
let mut is_gpu = false;
if let Some(Token { kind: TokenKind::Pure, .. }) = self.peek() {
self.advance();
is_pure = true;
} else if let Some(Token { kind: TokenKind::Gpu, .. }) = self.peek() {
self.advance();
is_gpu = true;
is_pure = true; // GPU kernels are inherently pure
}
self.expect(TokenKind::Fn)?;
let name = match self.advance() {
Some(Token { kind: TokenKind::Ident(id), .. }) => id.to_string(),
Some(t) => return Err(format!("Expected identifier after fn, found {:?}", t.kind)),
None => return Err("Expected identifier after fn, found EOF".to_string()),
};
self.expect(TokenKind::OpenParen)?;
let mut params = Vec::new();
while let Some(tok) = self.peek() {
if tok.kind == TokenKind::CloseParen {
break;
}
let param_name = match self.advance() {
Some(Token { kind: TokenKind::Ident(id), .. }) => id.to_string(),
Some(t) => return Err(format!("Expected parameter name, found {:?}", t.kind)),
None => return Err("Expected parameter name, found EOF".to_string()),
};
self.expect(TokenKind::Colon)?;
let ty = self.parse_type_expr()?;
params.push(Param { name: param_name, ty });
if let Some(Token { kind: TokenKind::Comma, .. }) = self.peek() {
self.advance();
} else {
break;
}
}
self.expect(TokenKind::CloseParen)?;
self.expect(TokenKind::Arrow)?;
let return_type = self.parse_type_expr()?;
self.expect(TokenKind::Colon)?;
self.expect(TokenKind::Newline)?;
let body = self.parse_block()?;
Ok(FnDecl {
is_pure,
is_gpu,
name,
params,
return_type,
body,
})
}
fn parse_block(&mut self) -> Result<Block, String> {
self.expect(TokenKind::Indent)?;
let mut statements = Vec::new();
loop {
while let Some(Token { kind: TokenKind::Newline, .. }) = self.peek() {
self.advance();
}
match self.peek() {
Some(Token { kind: TokenKind::Dedent, .. }) => {
self.advance();
break;
}
Some(_) => {
statements.push(self.parse_stmt()?);
match self.peek() {
Some(Token { kind: TokenKind::Newline, .. }) => {
self.advance();
},
Some(Token { kind: TokenKind::Dedent, .. }) => (),
Some(t) => return Err(format!("Expected Newline or Dedent after statement, found {:?}", t.kind)),
None => return Err("Unexpected EOF in block".to_string()),
}
}
None => return Err("Unexpected EOF parsing block".to_string()),
}
}
Ok(Block { statements })
}
fn parse_stmt(&mut self) -> Result<Stmt, String> {
if let Some(Token { kind: TokenKind::Return, .. }) = self.peek() {
self.advance();
let expr = self.parse_expr()?;
self.expect(TokenKind::Newline)?;
return Ok(Stmt::Return(expr));
}
if let Some(Token { kind: TokenKind::Let, .. }) = self.peek() {
self.advance();
let name = match self.advance() {
Some(Token { kind: TokenKind::Ident(id), .. }) => id.to_string(),
Some(t) => return Err(format!("Expected identifier after 'let', found {:?}", t.kind)),
None => return Err("Expected identifier after 'let', found EOF".to_string()),
};
self.expect(TokenKind::Equal)?;
let expr = self.parse_expr()?;
self.expect(TokenKind::Newline)?;
return Ok(Stmt::LetBind(name, expr));
}
let expr = self.parse_expr()?;
Ok(Stmt::ExprStmt(expr))
}
fn parse_expr(&mut self) -> Result<Expr, String> {
let mut left = match self.advance() {
Some(Token { kind: TokenKind::Ident(id), .. }) => Expr::Identifier(id.to_string()),
Some(Token { kind: TokenKind::Number(num), .. }) => Expr::Literal(num.to_string()),
Some(t) => return Err(format!("Expected expression, found {:?}", t.kind)),
None => return Err("Expected expression, found EOF".to_string()),
};
if let Some(tok) = self.peek() {
let op = match tok.kind {
TokenKind::Plus => Some(BinaryOp::Add),
TokenKind::Minus => Some(BinaryOp::Sub),
TokenKind::Star => Some(BinaryOp::Mul),
TokenKind::Slash => Some(BinaryOp::Div),
TokenKind::Greater => Some(BinaryOp::Gt),
TokenKind::Less => Some(BinaryOp::Lt),
TokenKind::EqualEqual => Some(BinaryOp::Eq),
_ => None,
};
if let Some(bin_op) = op {
self.advance();
let right = self.parse_expr()?;
left = Expr::Binary(Box::new(left), bin_op, Box::new(right));
}
}
Ok(left)
}
pub fn parse_type_alias(&mut self) -> Result<Decl, String> {
self.expect(TokenKind::Type)?;
let name = match self.advance() {
Some(Token { kind: TokenKind::Ident(id), .. }) => id.to_string(),
Some(t) => return Err(format!("Expected identifier for type alias, found {:?}", t.kind)),
None => return Err("Expected identifier, found EOF".to_string()),
};
self.expect(TokenKind::Equal)?;
let ty = self.parse_type_expr()?;
self.expect(TokenKind::Newline)?;
Ok(Decl::TypeAlias(name, ty))
}
fn parse_type_expr(&mut self) -> Result<TypeExpr, String> {
let base_ty = match self.advance() {
Some(Token { kind: TokenKind::BaseType(bt), .. }) => TypeExpr::BaseType(bt.to_string()),
Some(Token { kind: TokenKind::Ident(id), .. }) => TypeExpr::BaseType(id.to_string()),
Some(t) => return Err(format!("Expected type, found {:?}", t.kind)),
None => return Err("Expected type, found EOF".to_string()),
};
if let Some(Token { kind: TokenKind::OpenBrace, .. }) = self.peek() {
self.advance();
let var_name = match self.advance() {
Some(Token { kind: TokenKind::Ident(id), .. }) => id.to_string(),
_ => return Err("Expected variable identifier in refinement clause".to_string()),
};
self.expect(TokenKind::Pipe)?;
let constraint = self.parse_expr()?;
self.expect(TokenKind::CloseBrace)?;
Ok(TypeExpr::Refined(Box::new(base_ty), var_name, Box::new(constraint)))
} else {
Ok(base_ty)
}
}
}