diff --git a/README.md b/README.md index 3ec421c..e290269 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Akai Lang (yeah I'm not creative and the name is still unused on github, sooo) i ## Planned Features - Object-Oriented -- dynamic typing +- dynamic typing / Type inference - LLVM Backend ## Hello World @@ -14,4 +14,7 @@ Akai Lang (yeah I'm not creative and the name is still unused on github, sooo) i fun helloWorld() { print("Hello World!") } -``` \ No newline at end of file +``` + +## Shoutout to these amazing ressources that help(ed) me understand language design: +- [https://mukulrathi.com/create-your-own-programming-language/intro-to-compiler/](https://mukulrathi.com/create-your-own-programming-language/intro-to-compiler/) \ No newline at end of file diff --git a/src/language_frontend/abstract_syntax_tree/ast.rs b/src/language_frontend/abstract_syntax_tree/ast.rs new file mode 100644 index 0000000..8e2394a --- /dev/null +++ b/src/language_frontend/abstract_syntax_tree/ast.rs @@ -0,0 +1,71 @@ +/// Abstract Syntax Tree + +#[derive(Debug)] +pub enum Expression<'src> { + Ident(&'src str), + + Integer(i64), + Float(f64), + String(String), + Bool(bool), + + Negatation(Box>), + Add(Box>, Box>), + Substract(Box>, Box>), + Multiply(Box>, Box>), + Divide(Box>, Box>), + + Var { + name: &'src str, + rhs: Box>, + then: Box>, + }, + + Function { + name: &'src str, + args: Vec<&'src str>, + body: Box>, + then: Box>, + }, + + Unit, +} + +pub fn eval<'src>( + expr: &'src Expression<'src>, + vars: &mut Vec<(&'src str, f64)>, + funcs: &mut Vec<(&'src str, &'src [&'src str], &'src Expression<'src>)>, +) -> Result { + match expr { + Expression::Ident(_) => todo!(), + + Expression::Integer(x) => Ok((*x) as f64), + + Expression::Float(_) => todo!(), + + Expression::String(_) => todo!(), + + Expression::Bool(_) => todo!(), + + Expression::Negatation(lhs) => todo!(), + + Expression::Add(lhs, rhs) => Ok(eval(lhs, vars, funcs)? + eval(rhs, vars, funcs)?), + + Expression::Substract(lhs, rhs) => Ok(eval(lhs, vars, funcs)? - eval(rhs, vars, funcs)?), + + Expression::Multiply(lhs, rhs) => Ok(eval(lhs, vars, funcs)? * eval(rhs, vars, funcs)?), + + Expression::Divide(lhs, rhs) => Ok(eval(lhs, vars, funcs)? / eval(rhs, vars, funcs)?), + + Expression::Var { name, rhs, then } => todo!(), + + Expression::Function { + name, + args, + body, + then, + } => todo!(), + + Expression::Unit => todo!(), + } +} diff --git a/src/language_frontend/ast/mod.rs b/src/language_frontend/abstract_syntax_tree/mod.rs similarity index 52% rename from src/language_frontend/ast/mod.rs rename to src/language_frontend/abstract_syntax_tree/mod.rs index 10d92bc..a2d9679 100644 --- a/src/language_frontend/ast/mod.rs +++ b/src/language_frontend/abstract_syntax_tree/mod.rs @@ -1,2 +1,2 @@ pub mod ast; -pub mod op; \ No newline at end of file +pub mod op; diff --git a/src/language_frontend/ast/op.rs b/src/language_frontend/abstract_syntax_tree/op.rs similarity index 62% rename from src/language_frontend/ast/op.rs rename to src/language_frontend/abstract_syntax_tree/op.rs index fce3a58..075022d 100644 --- a/src/language_frontend/ast/op.rs +++ b/src/language_frontend/abstract_syntax_tree/op.rs @@ -9,11 +9,11 @@ pub enum Op { impl Op { pub fn eval(&self) -> String { let text: &str = match self { - Op::Add => "+", - Op::Subtract => "-", - Op::Multiply => "*", - Op::Divide => "/", + Op::Add => "+", + Op::Subtract => "-", + Op::Multiply => "*", + Op::Divide => "/", }; text.to_string() } -} \ No newline at end of file +} diff --git a/src/language_frontend/ast/ast.rs b/src/language_frontend/ast/ast.rs deleted file mode 100644 index 1f695cc..0000000 --- a/src/language_frontend/ast/ast.rs +++ /dev/null @@ -1,62 +0,0 @@ -/// Abstract Syntax Tree -#[derive(Debug)] -pub enum Expression<'src> { - VariableName(&'src str), - Integer(i64), - Float(f64), - String(String), - Bool(bool), - - Negatation(Box>), - Add(Box>, Box>), - Substract(Box>, Box>), - Multiply(Box>, Box>), - Divide(Box>, Box>), - - Var { - name: &'src str, - rhs: Box>, - then: Box>, - }, - - Function { - name: &'src str, - args: Vec<&'src str>, - body: Box>, - then: Box>, - }, - - Unit -} - -impl<'src> Expression<'src> { - pub fn evaluate(&self) -> String { - match self { - Expression::VariableName(_) => todo!(), - - Expression::Integer(_) => todo!(), - - Expression::Float(_) => todo!(), - - Expression::String(_) => todo!(), - - Expression::Bool(_) => todo!(), - - Expression::Negatation(expression) => todo!(), - - Expression::Add(expression, expression1) => todo!(), - - Expression::Substract(expression, expression1) => todo!(), - - Expression::Multiply(expression, expression1) => todo!(), - - Expression::Divide(expression, expression1) => todo!(), - - Expression::Var { name, rhs, then } => todo!(), - - Expression::Function { name, args, body, then } => todo!(), - - Expression::Unit => todo!(), - } - } -} diff --git a/src/language_frontend/lexer/mod.rs b/src/language_frontend/lexer/mod.rs new file mode 100644 index 0000000..5c76635 --- /dev/null +++ b/src/language_frontend/lexer/mod.rs @@ -0,0 +1 @@ +pub mod tokens; diff --git a/src/language_frontend/tokens.rs b/src/language_frontend/lexer/tokens.rs similarity index 99% rename from src/language_frontend/tokens.rs rename to src/language_frontend/lexer/tokens.rs index ec77d9e..3d8ff21 100644 --- a/src/language_frontend/tokens.rs +++ b/src/language_frontend/lexer/tokens.rs @@ -39,7 +39,7 @@ pub enum Token<'src> { #[regex("[0-9]+", |lex| lex.slice().parse::().unwrap())] Integer(i64), - + #[regex(r"[_a-zA-Z][_0-9a-zA-Z]*")] Ident(&'src str), @@ -53,5 +53,3 @@ pub enum Token<'src> { #[token("else")] Keyword(&'src str), } - - diff --git a/src/language_frontend/mod.rs b/src/language_frontend/mod.rs index 2e33aaf..8c28b19 100644 --- a/src/language_frontend/mod.rs +++ b/src/language_frontend/mod.rs @@ -1,3 +1,3 @@ -pub mod ast; +pub mod abstract_syntax_tree; +pub mod lexer; pub mod parser; -pub mod tokens; \ No newline at end of file diff --git a/src/language_frontend/parser.rs b/src/language_frontend/parser.rs index 2790b65..e4bed75 100644 --- a/src/language_frontend/parser.rs +++ b/src/language_frontend/parser.rs @@ -1,7 +1,14 @@ -use chumsky::{combinator::Or, prelude::{choice, just, recursive}, recursive, select, text::{self, ascii::ident}, IterParser, Parser}; +use chumsky::{ + IterParser, Parser, + combinator::Or, + prelude::{choice, just, recursive}, + recursive, select, + text::{self, ascii::ident}, +}; -use crate::{language_frontend::ast::ast::Expression, language_frontend::tokens::Token}; +use crate::{language_frontend::abstract_syntax_tree::ast::Expression, language_frontend::lexer::tokens::Token}; +// goal of parsing is to construct an abstract syntax tree #[allow(clippy::let_and_return)] pub fn parser<'src>() -> impl Parser<'src, &'src [Token<'src>], Expression<'src>> { @@ -9,16 +16,16 @@ pub fn parser<'src>() -> impl Parser<'src, &'src [Token<'src>], Expression<'src> Token::Ident(ident) => ident }; - let keyword = |kw: &'static str| select! { - Token::Keyword(k) if k == kw => () + let keyword = |kw: &'static str| { + select! { + Token::Keyword(k) if k == kw => () + } }; let eq = just(Token::Equals); - let expr = recursive( - |expr| - { - let atom = { + let expr = recursive(|expr| { + let atom = { let parenthesized = expr .clone() .delimited_by(just(Token::ParenBegin), just(Token::ParenEnd)); @@ -63,7 +70,7 @@ pub fn parser<'src>() -> impl Parser<'src, &'src [Token<'src>], Expression<'src> let decl = recursive(|decl| { let r#var = keyword("var") - .ignore_then(ident.clone()) + .ignore_then(ident.clone()) .then_ignore(eq.clone()) .then(decl.clone()) .then(decl.clone()) @@ -84,10 +91,10 @@ pub fn parser<'src>() -> impl Parser<'src, &'src [Token<'src>], Expression<'src> args, body: Box::new(body), then: Box::new(then), - }); - + }); + var.or(r#fun).or(expr) }); decl -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index 7a64e31..9c2d4c8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,23 @@ use chumsky::Parser; use logos::Logos; -use crate::{language_frontend::parser::parser, language_frontend::tokens::Token}; +use crate::language_frontend::abstract_syntax_tree; +use crate::{ + language_frontend::lexer::tokens::Token, language_frontend::parser::parser}; +use crate::language_frontend::abstract_syntax_tree::ast::{eval, Expression}; mod language_frontend; +/* +Simple Compiler -> 4 Stages: +- lex +- parse +- type-check +- translate to machine instructions +*/ + fn main() { - let lexer = Token::lexer("(1 + 1) * 3"); + let lexer = Token::lexer("1 + 1 * 3"); let mut tokens = vec![]; for (token, span) in lexer.spanned() { @@ -19,16 +30,28 @@ fn main() { } } - let ast = match parser().parse(&tokens).into_result() { - Ok(expr) => { - println!("[AST]\n{:#?}", expr); - expr + /* + match parser().parse(&tokens).into_result() { + Ok(expr) => match eval(&ast, &mut Vec::new(), &mut Vec::new()) { + Ok(output) => println!("{output}"), + Err(eval_err) => println!("Evaluation error: {eval_err}"), } Err(e) => { println!("parse error: {:#?}", e); return; } - }; + }; */ - println!("\n[result]\n{}", ast.evaluate()); + + match parser().parse(&tokens).into_result() { + Ok(ast) => match eval(&ast, &mut Vec::new(), &mut Vec::new()) { + Ok(output) => println!("{output}"), + Err(eval_err) => println!("Evaluation error: {eval_err}"), + }, + Err(parse_errs) => parse_errs + .into_iter() + .for_each(|err| println!("Parse error: {err}")), + }; + + //println!("\n[result]\n{}", abstract_syntax_tree::ast::eval(absyntr, vars, funcs)); } diff --git a/syntax.akai b/syntax.akai index 52ad6bb..5c5707c 100644 --- a/syntax.akai +++ b/syntax.akai @@ -17,6 +17,7 @@ class Cat derive Animal: fun helloWorld() { // Variables either dynamically or statically typed + // -> Type inference var String: test = "I'm a string" var foo = 12 // reads as int