Initial commit
This commit is contained in:
7
aurac_parser/Cargo.toml
Normal file
7
aurac_parser/Cargo.toml
Normal 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
75
aurac_parser/src/ast.rs
Normal 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
87
aurac_parser/src/lib.rs
Normal 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
278
aurac_parser/src/parser.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user