moved to workspaces

This commit is contained in:
LunarAkai 2025-12-04 00:44:28 +01:00
commit 7c0dc98ef7
16 changed files with 91 additions and 103 deletions

View file

@ -0,0 +1,8 @@
[package]
name = "akailang-parser"
version.workspace = true
edition.workspace = true
[dependencies]
logos = "0.15.0"
chumsky = "0.10.1"

View file

@ -0,0 +1,49 @@
use crate::abstract_syntax_tree::definitions::*;
// Option and Result -> define in Std
#[derive(Clone, Debug, PartialEq)]
pub enum Expr {
// var/fun_name
Ident(Identifier),
// 4
IntLiteral(i64),
// 12.5
FloatLiteral(f64),
// "eepy"
StringLiteral(String),
// true
BoolLiteral(bool),
// 'c'
CharLiteral(char),
// var x = intPlusOne(12)
CallExpr(Call),
// !x
UnaryExpr(Unary),
// 1 + 11
BinaryExpr(Binary),
// x = 64
AssignmentExpr(Assignment),
// var foo = "bar"
VarExpr(Var),
// if(...) { ... } else { ... }
Condition(Condition),
// fun helloWorld() { ... }
FunctionExpr(Function),
// ->
ReturnExpr,
Error,
}

View file

@ -0,0 +1,161 @@
use std::{ops::Range, rc::Rc};
use crate::abstract_syntax_tree::ast::Expr;
// Abstract Syntax Tree
pub type BlockExpression = Vec<Expr>;
pub type Span = Range<usize>;
#[derive(Clone, Debug, PartialEq)]
pub enum BinaryOp {
// Arithmetic
Multiply,
Divide,
Add,
Substract,
// Comparision
Equals,
NotEquals,
Less,
LessEquals,
Greater,
GreaterEquals,
// Logical
And,
Or,
// todo: bitwise
}
#[derive(Clone, Debug, PartialEq)]
pub enum UnaryOp {
Not,
Negate,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Literal {
UnsignedInteger(u64),
Bool(bool),
Char(char),
String(Rc<str>),
Int(i64),
Float(f64),
}
#[derive(Clone, Debug, PartialEq)]
pub enum Type {
Integer,
Float,
Bool,
Char,
String,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
Integer(i64),
Float(f64),
Bool(bool),
Char(char),
String(String),
}
impl Value {
pub fn is_type(&self, ty: &Type) -> bool {
match (ty, self) {
(Type::Bool, Value::Bool(_)) => true,
(Type::Char, Value::Char(_)) => true,
(Type::Integer, Value::Integer(_)) => true,
(Type::Float, Value::Float(_)) => true,
(Type::String, Value::String(_)) => true,
_ => false,
}
}
}
//---------------------------------------
// Structs
//---------------------------------------
#[derive(Clone, Debug, PartialEq)]
pub struct Identifier(pub String);
#[derive(Clone, Debug, PartialEq)]
pub struct While {
pub condition: Box<Expr>,
pub body: BlockExpression,
}
/// Example: `if (a > 12) { ... }`
#[derive(Clone, Debug, PartialEq)]
pub struct Condition {
pub condition: Box<Expr>,
pub if_body: BlockExpression,
pub else_body: Option<BlockExpression>,
}
/// Example: `x = y`
#[derive(Clone, Debug, PartialEq)]
pub struct Assignment {
pub target: String,
pub value: Box<Expr>,
}
/// Example: `var String: foo = "Test"`
#[derive(Clone, Debug, PartialEq)]
pub struct Var {
pub ty: Option<Type>,
pub ident: Identifier,
pub value: Box<Expr>,
}
/// Example: `x++`
#[derive(Clone, Debug, PartialEq)]
pub struct Unary {
pub operator: UnaryOp,
pub operand: Box<Expr>,
}
/// Example: `1 + 1`
#[derive(Clone, Debug, PartialEq)]
pub struct Binary {
pub lhs: Box<Expr>,
pub operator: BinaryOp,
pub rhs: Box<Expr>,
}
/// Represents the Structure of a `Function` in AkaiLang
///
/// Examples:
///```AkaiLang
///fun helloWorld() {
/// print("Hello World")
///}
///```
/// <br>
///
///```AkaiLang
///fun returnsIntPlusOne(i: int): int {
/// -> i + 1
///}
///```
#[derive(Clone, Debug, PartialEq)]
pub struct Function {
pub name: String,
pub params: Option<Vec<(String, Type)>>,
pub return_type: Option<Type>,
pub body: Option<BlockExpression>,
pub return_expr: Box<Option<Expr>>, // ' -> (return)'
}
#[derive(Clone, Debug, PartialEq)]
pub struct Call {
/// name of the function being called
pub name: Box<Identifier>,
/// arguments supplied
pub arguments: Vec<Expr>,
}

View file

@ -0,0 +1,52 @@
pub mod ast;
pub mod definitions;
pub mod parser;
#[macro_export]
macro_rules! token {
(var) => {
just(Token::Var)
};
(=) => {
just(Token::Assign)
};
(if) => {
just(Token::If)
};
(else) => {
just(Token::Else)
};
(->) => {
just(Token::Return)
};
(:) => {
just(Token::Colon)
};
(,) => {
just(Token::Comma)
};
(*) => {
just(Token::Multiply)
};
(/) => {
just(Token::Divide)
};
(+) => {
just(Token::Add)
};
(-) => {
just(Token::Substract)
};
(lp) => {
just(Token::LParen)
};
(rp) => {
just(Token::RParen)
};
(lbrace) => {
just(Token::LBrace)
};
(rbrace) => {
just(Token::RBrace)
};
}

View file

@ -0,0 +1,324 @@
use chumsky::{
Boxed, ConfigIterParser, IterParser, ParseResult, Parser,
combinator::Or,
container::Seq,
error::{Rich, Simple},
extra,
input::{Input, Stream, ValueInput},
prelude::{choice, end, just, nested_delimiters, recursive, skip_then_retry_until, via_parser},
primitive::select,
recursive, select, select_ref,
span::{self, SimpleSpan},
text::{
self,
ascii::{ident, keyword},
newline, whitespace,
},
};
use logos::{Logos, source};
use crate::{
token,
{
abstract_syntax_tree::{ast::Expr, definitions::*},
lexer::tokens::Token,
},
};
// goal of parsing is to construct an abstract syntax tree
pub fn parse(source: &str) -> Result<Vec<Expr>, Vec<Rich<'_, Token>>> {
let token_iter = Token::lexer(source)
.spanned()
.map(|(token, span)| (token.unwrap_or(Token::Error), span.into()));
let end_of_input: SimpleSpan = (0..source.len()).into();
let token_stream = Stream::from_iter(token_iter).map(
(0..end_of_input.into_iter().len()).into(),
|(t, s): (_, _)| (t, s),
);
parser().parse(token_stream).into_result()
}
fn parser<'src, I>() -> impl Parser<'src, I, Vec<Expr>, extra::Err<Rich<'src, Token>>>
where
I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
{
let ident = select! { Token::Identifier(s) => s, }.labelled("identifier");
let expr = recursive(|expr| {
let atom = select! {
Token::FloatLiteral(x) => Expr::FloatLiteral(x),
Token::IntLiteral(x) => Expr::IntLiteral(x),
Token::BoolLiteral(x) => Expr::BoolLiteral(x),
Token::StringLiteral(s) => Expr::StringLiteral(s),
}
.labelled("value")
.or(expr.clone().delimited_by(token!(lp), token!(rp)));
// Product
let mul_div = atom.clone().foldl(
choice((
token!(*).to(BinaryOp::Multiply),
token!(/).to(BinaryOp::Divide),
))
.then(atom)
.then_ignore(just(Token::NewLine).or_not())
.repeated(),
|lhs, (op, rhs)| {
Expr::BinaryExpr(Binary {
lhs: Box::new(lhs),
operator: op,
rhs: Box::new(rhs),
})
},
);
// Sum
let add_sub = mul_div.clone().foldl(
choice((
token!(+).to(BinaryOp::Add),
token!(-).to(BinaryOp::Substract),
))
.then(mul_div)
.then_ignore(just(Token::NewLine).or_not())
.repeated(),
|lhs, (op, rhs)| {
Expr::BinaryExpr(Binary {
lhs: Box::new(lhs),
operator: op,
rhs: Box::new(rhs),
})
},
);
let assign_expr = ident
.then_ignore(token!(=))
.then(expr.clone())
.map(|(name, value)| {
Expr::AssignmentExpr(Assignment {
target: name,
value: Box::new(value),
})
});
assign_expr.or(add_sub)
});
let decl = recursive(|decl| {
let var = token!(var)
.ignore_then(ident)
.then_ignore(token!(=))
.then(expr.clone())
.then_ignore(just(Token::NewLine).or_not())
.map(|(name, rhs)| {
Expr::VarExpr(Var {
ty: None,
ident: Identifier(String::from(name)),
value: Box::new(rhs),
})
});
let type_parser = choice((
just(Token::IntType).to(Type::Integer),
just(Token::FloatType).to(Type::Float),
just(Token::BoolType).to(Type::Bool),
just(Token::StringType).to(Type::String),
));
let return_type_parser = token!(:).ignore_then(type_parser.clone()).or_not();
let return_expr = token!(->).ignore_then(expr.clone()).map(|expr| expr);
//---------------------------------------------------------------------------------------
// Function Parser
//---------------------------------------------------------------------------------------
let fun = just(Token::Fun)
.ignore_then(ident) // function name
.then(
// arguments
ident
.then_ignore(token!(:))
.then(type_parser.clone())
.separated_by(token!(,))
.allow_trailing()
.collect::<Vec<_>>()
.delimited_by(token!(lp), token!(rp))
.or_not(),
)
.then(return_type_parser.clone()) // return type
.then_ignore(token!(lbrace)) // {
.then_ignore(just(Token::NewLine).repeated().or_not())
.then(
expr.clone()
.then_ignore(just(Token::NewLine))
.repeated()
.collect::<Vec<Expr>>()
.then(return_expr.or_not()),
)
.then_ignore(just(Token::NewLine).repeated().or_not())
.then_ignore(token!(rbrace)) // }
.map(|(((name, params), return_ty), (stmts, return_value))| {
Expr::FunctionExpr(Function {
name: name,
params,
return_type: return_ty,
body: Some(stmts),
return_expr: Box::new(return_value),
})
});
var.or(fun).or(expr)
});
decl.clone()
.then_ignore(just(Token::NewLine).repeated())
.repeated()
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unary_expr() {
let negate_two = parse("!2");
assert!(negate_two.is_ok());
assert_eq!(
negate_two.clone().unwrap(),
vec![Expr::UnaryExpr(Unary {
operator: UnaryOp::Negate,
operand: Box::new(Expr::IntLiteral(2)),
})]
)
}
#[test]
fn test_bool() {
let var_bool = parse("var isUwU = true");
assert!(var_bool.is_ok());
assert_eq!(
var_bool.clone().unwrap(),
vec![Expr::VarExpr(Var {
ty: None,
ident: Identifier(String::from("isUwU")),
value: Box::new(Expr::BoolLiteral(true))
})]
)
}
#[test]
fn test_binary_expr() {
let sum = parse("1 + 2");
assert!(sum.is_ok());
assert_eq!(
sum.clone().unwrap(),
vec![Expr::BinaryExpr(Binary {
lhs: Box::new(Expr::IntLiteral(1)),
operator: BinaryOp::Add,
rhs: Box::new(Expr::IntLiteral(2))
})]
)
}
#[test]
fn test_variable_decl() {
let var_without_expl_type = parse("var x = 12");
assert!(var_without_expl_type.is_ok());
assert_eq!(
var_without_expl_type.clone().unwrap(),
vec![Expr::VarExpr(Var {
ty: None,
ident: Identifier(String::from("x")),
value: Box::new(Expr::IntLiteral(12))
})]
)
}
#[test]
fn test_assignment() {
let assign = parse("x = 12");
assert!(assign.is_ok());
assert_eq!(
assign.clone().unwrap(),
vec![Expr::AssignmentExpr(Assignment {
target: String::from("x"),
value: Box::new(Expr::IntLiteral(12))
})]
)
}
#[test]
fn test_function_decl() {
// test without a body or args
let empty_fun = parse("fun helloWorld() { }");
assert!(empty_fun.is_ok());
assert_eq!(
empty_fun.clone().unwrap(),
vec![Expr::FunctionExpr(Function {
name: String::from("helloWorld"),
params: Some([].to_vec()),
return_type: None,
body: Some([].to_vec()),
return_expr: Box::new(None),
})]
);
// tests if empty new lines within a function works
let empty_fun_with_new_lines = parse(
r"fun emptyMulLines() {
}
",
);
assert_eq!(
empty_fun_with_new_lines.clone().unwrap(),
vec![Expr::FunctionExpr(Function {
name: String::from("emptyMulLines"),
params: Some([].to_vec()),
return_type: None,
body: Some([].to_vec()),
return_expr: Box::new(None),
})]
);
// tests for return expr in functions
let fun_that_returns_int = parse(
r"fun returnsInt(): int {
-> 12
}
",
);
assert_eq!(
fun_that_returns_int.clone().unwrap(),
vec![Expr::FunctionExpr(Function {
name: String::from("returnsInt"),
params: Some([].to_vec()),
return_type: Some(Type::Integer),
body: Some([].to_vec()),
return_expr: Box::new(Some(Expr::IntLiteral(12))),
})]
);
// tests for return expr with previous statements in functions
let fun_multi_line_return = parse(
r"fun returnsInt(): int {
var x = 12
x = x + 1
-> x
}
",
);
assert_eq!(
fun_multi_line_return.clone().unwrap(),
vec![Expr::FunctionExpr(Function {
name: String::from("returnsInt"),
params: Some([].to_vec()),
return_type: Some(Type::Integer),
body: Some(vec![Expr::IntLiteral(12)]),
return_expr: Box::new(Some(Expr::ReturnExpr)),
})]
)
}
}

View file

@ -0,0 +1 @@
pub mod tokens;

View file

@ -0,0 +1,205 @@
use std::fmt;
use logos::Logos;
#[derive(Logos, Debug, Clone, PartialEq)]
#[regex(r"[\t\f]+", logos::skip)]
pub enum Token {
// Identifier
#[regex(r"[a-zA-Z_][a-zA-Z0-9_]*", |lex| lex.slice().to_owned())]
Identifier(String),
// Keywords
#[token("fun")]
Fun,
#[token("class")]
Class,
#[token("var")]
Var,
#[token("interface")]
Interface,
#[token("derive")]
Derive,
#[token("impl")]
Impl,
#[token("if")]
If,
#[token("else")]
Else,
#[token("->")]
Return,
#[token("enum")]
Enum,
// Types
#[token("int")]
IntType,
#[token("float")]
FloatType,
#[token("bool")]
BoolType,
#[token("String")]
StringType,
// Literals
#[regex(r#""([^"\\]|\\.)*""#, |lex| lex.slice().to_owned())]
StringLiteral(String),
#[regex(r"[0-9]+", |lex| lex.slice().parse::<i64>().ok())]
IntLiteral(i64),
#[regex(r"[0-9]+\.[0-9]+", |lex| lex.slice().parse::<f64>().ok())]
FloatLiteral(f64),
#[token("true", |_| true)]
#[token("false", |_| false)]
BoolLiteral(bool),
// Operators
#[token("=")]
Assign,
#[token("==")]
Equals,
#[token("!=")]
NotEquals,
#[token("<", priority = 2)]
Less,
#[token("<=")]
LessEquals,
#[token(">", priority = 2)]
Greater,
#[token(">=")]
GreaterEquals,
#[token("*")]
Multiply,
#[token("/")]
Divide,
#[token("+")]
Add,
#[token("-")]
Substract,
#[token("&&")]
And,
#[token("||")]
Or,
// Punctiuation
#[token("(")]
LParen,
#[token(")")]
RParen,
#[token("{")]
LBrace,
#[token("}")]
RBrace,
#[token("[")]
LBracket,
#[token("]")]
RBracket,
#[token(":")]
Colon,
#[token(",")]
Comma,
#[token(".")]
Dot,
// Special
#[regex(r"\n")]
NewLine,
#[regex(r"//[^\r]*", logos::skip)]
#[regex(r"/\*([^*]|\*[^/])*\*/", logos::skip)]
Comment,
#[regex(r"[ \t\f]+", logos::skip)]
Whitespace,
Eof,
Error,
}
impl fmt::Display for Token {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Token::Identifier(ident) => write!(f, "{ident}"),
Token::Fun => write!(f, "fun"),
Token::Class => write!(f, "class"),
Token::Var => write!(f, "var"),
Token::Interface => write!(f, "interface"),
Token::Derive => write!(f, "derive"),
Token::Impl => write!(f, "impl"),
Token::If => write!(f, "if"),
Token::Else => write!(f, "else"),
Token::Return => write!(f, "->"),
Token::Enum => write!(f, "enum"),
Token::IntType => write!(f, "int"),
Token::FloatType => write!(f, "float"),
Token::BoolType => write!(f, "bool"),
Token::StringType => write!(f, "String"),
Token::StringLiteral(s) => write!(f, "{s}"),
Token::IntLiteral(i) => write!(f, "{i}"),
Token::FloatLiteral(fl) => write!(f, "{fl}"),
Token::BoolLiteral(b) => write!(f, "{b}"),
Token::Assign => write!(f, "="),
Token::Equals => write!(f, "=="),
Token::NotEquals => write!(f, "!="),
Token::Less => write!(f, "<"),
Token::LessEquals => write!(f, "<="),
Token::Greater => write!(f, ">"),
Token::GreaterEquals => write!(f, ">="),
Token::Multiply => write!(f, "*"),
Token::Divide => write!(f, "/"),
Token::Add => write!(f, "+"),
Token::Substract => write!(f, "-"),
Token::And => write!(f, "&&"),
Token::Or => write!(f, "||"),
Token::LParen => write!(f, "("),
Token::RParen => write!(f, ")"),
Token::LBrace => write!(f, "{{"),
Token::RBrace => write!(f, "}}"),
Token::LBracket => write!(f, "["),
Token::RBracket => write!(f, "]"),
Token::Colon => write!(f, ":"),
Token::Comma => write!(f, ","),
Token::Dot => write!(f, "."),
Token::Comment => write!(f, ""),
Token::NewLine => write!(f, "\n"),
Token::Whitespace => write!(f, ""),
Token::Eof => write!(f, ""),
Token::Error => write!(f, "<error>"),
}
}
}

View file

@ -0,0 +1,17 @@
pub mod abstract_syntax_tree;
pub mod lexer;
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}