Skip to content

Commit

Permalink
Merge branch 'codegen'
Browse files Browse the repository at this point in the history
  • Loading branch information
Jujumba committed Aug 26, 2023
2 parents 12fa149 + 967c088 commit 16f285a
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 97 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ llvm-sys = "160.1.3"
regex = "1.8.3"
pitusyastd = "0.0.1"
clap = { version = "4.3.22", features = ["derive"] }
colored = "2.0.4"

[profile.release]
strip = true
strip = true
129 changes: 83 additions & 46 deletions src/ast/parser.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,21 @@
use super::Proto;

use crate::abort;
use crate::ast::Ast;
use crate::input::CursoredFile;
use crate::lexer::next_token;
use crate::lexer::tokens::{BinaryOperatorKind, KeywordKind, OperatorKind, TokenKind};

macro_rules! abort_syntax_analysis {
($pos: expr) => {
abort!("Compilation error at position {}", $pos)
};
($pos:expr, $msg:expr) => {
abort!("Compilation error at position {}\n\t{}", $pos, $msg)
};
($pos: expr, $expected: expr, $error: expr) => {
abort!(
"Compilation error at position {}:\n\tExpected {}, but got {:?}",
$pos,
$expected,
$error
)
($token:expr, $input:expr, $help:expr) => {
$crate::abort!("{}", error::construct_error_message(&$token, &$input, $help))
};
}

pub fn parse(input: &mut CursoredFile) -> Vec<Ast> {
let mut ast = Vec::new();
loop {
match next_token(input).kind {
let token = next_token(input);
match &token.kind {
TokenKind::Keyword(KeywordKind::Fn) => ast.push(Ast::FunctionNode {
proto: parse_prototype(input, true),
body: parse_block(input),
Expand All @@ -35,7 +24,7 @@ pub fn parse(input: &mut CursoredFile) -> Vec<Ast> {
ast.push(Ast::ExternNode(parse_prototype(input, true)));
}
TokenKind::EOF => break,
e => abort_syntax_analysis!(input.get_cursor(), "`extern` or `fn`", e),
_ => abort_syntax_analysis!(token, input, "expected `extern` or `fn`"),
}
}
ast
Expand All @@ -44,44 +33,44 @@ fn parse_prototype(input: &mut CursoredFile, definition: bool) -> Proto {
let name_token = next_token(input);
let name = match name_token.kind {
TokenKind::Identifier(name) => name,
e => abort_syntax_analysis!(input.get_cursor(), "function's name in its definition", e),
_ => abort_syntax_analysis!(name_token, input, "expected function's name in it's definition"),
};

match next_token(input).kind {
let paren_token = next_token(input);
match paren_token.kind {
TokenKind::Operator(OperatorKind::LParen) => (),
e => abort_syntax_analysis!(input.get_cursor(), "`(`", e),
_ => abort_syntax_analysis!(paren_token, input, "expected `(`"),
}

let mut args = Vec::<Ast>::new();
let mut t = next_token(input);

while t.kind != TokenKind::Operator(OperatorKind::RParen) {
match t.kind {
TokenKind::Identifier(_) if name == "main" => {
abort_syntax_analysis!(input.get_cursor(), "Main function cannot accept parameters!");
}
TokenKind::Identifier(_) if name == "main" => abort_syntax_analysis!(t, input, "Main function cannot accept parameters!"),
TokenKind::Identifier(param) if definition => args.push(Ast::IdentifierNode(param)),
_ if !definition => {
input.move_back_cursor(t.len);
args.push(parse_expression(input));
}
e => abort_syntax_analysis!(input.get_cursor(), "an identifier", e),
_ => abort_syntax_analysis!(t, input, "expected an identifier"),
}
match next_token(input).kind {
let next = next_token(input);
match next.kind {
TokenKind::Operator(OperatorKind::Coma) => {
t = next_token(input);
continue;
}
TokenKind::Operator(OperatorKind::RParen) => break,
e => abort_syntax_analysis!(input.get_cursor(), "a coma or `)`", e),
_ => abort_syntax_analysis!(next, input, "expected `,` or `)`"),
}
}
Proto { name, args }
}
fn parse_block(input: &mut CursoredFile) -> Vec<Ast> {
let curly = next_token(input);
if curly.kind != TokenKind::Operator(OperatorKind::LCurly) {
abort_syntax_analysis!(input.get_cursor(), "`{`", curly);
abort_syntax_analysis!(curly, input, "`{`");
}
let mut body = vec![];
loop {
Expand All @@ -102,23 +91,23 @@ fn parse_block(input: &mut CursoredFile) -> Vec<Ast> {
}
TokenKind::Keyword(KeywordKind::Ret) => body.push(Ast::RetNode(Box::new(parse_expression(input)))),
TokenKind::Operator(OperatorKind::RCurly) => break,
_ => abort_syntax_analysis!(input.get_cursor(), "Unexpected token"),
_ => abort_syntax_analysis!(t, input, "unexpected token"),
}
}
body
}
fn parse_expression(input: &mut CursoredFile) -> Ast {
let ast = fetch_lhs(input, "an identifier or literal");
let ast = fetch_lhs(input);
let token = next_token(input);
if let TokenKind::Operator(op) = token.kind {
if let TokenKind::Operator(op) = &token.kind {
match op {
OperatorKind::Binary(BinaryOperatorKind::Assigment) if matches!(ast, Ast::ValueNode(_)) => {
abort_syntax_analysis!(input.get_cursor(), format!("Cannot assign to the const-value of {ast:?}"));
abort_syntax_analysis!(token, input, format!("cannot assign to the const-value of {ast:?}"))
}
OperatorKind::Binary(op) => Ast::BinaryNode {
left: Box::new(ast),
right: Box::new(parse_expression(input)),
op,
op: *op,
},
_ => {
input.move_back_cursor(token.len);
Expand All @@ -131,21 +120,22 @@ fn parse_expression(input: &mut CursoredFile) -> Ast {
}
}
fn parse_unit_expr(input: &mut CursoredFile) -> Ast {
let ast = fetch_lhs(input, "an identifier or literal");
match next_token(input).kind {
let ast = fetch_lhs(input);
let token = next_token(input);
match token.kind {
TokenKind::Operator(op) => match op {
OperatorKind::Binary(op) => Ast::BinaryNode {
left: Box::new(ast),
right: Box::new(parse_unit_expr(input)),
op,
},
OperatorKind::RParen => ast,
e => abort_syntax_analysis!(input.get_cursor(), "a binary operator or `)`", e),
_ => abort_syntax_analysis!(token, input, "expected a binary operator or `)`"),
},
e => abort_syntax_analysis!(input.get_cursor(), "`)`", e),
_ => abort_syntax_analysis!(token, input, "expected `)`"),
}
}
fn fetch_lhs(input: &mut CursoredFile, expected: &str) -> Ast {
fn fetch_lhs(input: &mut CursoredFile) -> Ast {
let lhs_token = next_token(input);
match lhs_token.kind {
TokenKind::Identifier(_) => {
Expand All @@ -154,14 +144,14 @@ fn fetch_lhs(input: &mut CursoredFile, expected: &str) -> Ast {
}
TokenKind::Literal(l) => Ast::ValueNode(l),
TokenKind::Operator(OperatorKind::LParen) => Ast::UnitNode(Box::new(parse_unit_expr(input))),
e => abort_syntax_analysis!(input.get_cursor(), expected, e),
_ => abort_syntax_analysis!(lhs_token, input, "expected an identifier or literal"),
}
}
fn fetch_ident_or_call(input: &mut CursoredFile) -> Ast {
let name_token = next_token(input);
let name = match name_token.kind {
TokenKind::Identifier(i) => i,
e => abort_syntax_analysis!(input.get_cursor(), "an identifier", e),
_ => abort_syntax_analysis!(name_token, input, "expected an identifier"),
};
let paren = next_token(input);
input.move_back_cursor(paren.len);
Expand All @@ -174,13 +164,60 @@ fn fetch_ident_or_call(input: &mut CursoredFile) -> Ast {
fn parse_let_expr(input: &mut CursoredFile) -> Ast {
let token = next_token(input);
match token.kind {
TokenKind::Identifier(assignee) => match next_token(input).kind {
TokenKind::Operator(OperatorKind::Binary(BinaryOperatorKind::Assigment)) => Ast::LetNode {
assignee,
value: Box::new(parse_expression(input)),
},
e => abort_syntax_analysis!(input.get_cursor(), "`=`", e),
},
e => abort_syntax_analysis!(input.get_cursor(), "an identifier", e),
TokenKind::Identifier(assignee) => {
let token = next_token(input);
match token.kind {
TokenKind::Operator(OperatorKind::Binary(BinaryOperatorKind::Assigment)) => Ast::LetNode {
assignee,
value: Box::new(parse_expression(input)),
},
_ => abort_syntax_analysis!(token, input, "expected `=`"),
}
}
_ => abort_syntax_analysis!(token, input, "expected an identifier"),
}
}

mod error {
use crate::{input::CursoredFile, lexer::tokens::Token};
use colored::Colorize;

pub fn construct_error_message<A: AsRef<str>>(token: &Token, file: &CursoredFile, help: A) -> String {
let help = help.as_ref();
let chars: &[char] = file.as_ref();
let start = token.start
- chars[..token.start]
.iter()
.rev()
.position(|c| *c == '\n')
.unwrap_or(token.start);
let end = token.start + chars[token.start..].iter().position(|c| *c == '\n').unwrap_or(file.content.len());
let line: String = chars[start..end].iter().collect();
let line_number = chars[..token.start].iter().filter(|c| **c == '\n').count();
let span_start = token.start - start;
let span_len = token.len;

if span_start == 0 {
format!(
"{error} in {file_name} on line {line_number}:\n\t{line}\n\t{sep:^>span_len$}\n{col_help}: {actual_help}",
file_name = file.name.display().to_string().bright_cyan().bold(), // I'm sorry
line_number = line_number,
error = "error".bright_red(),
sep = "^".bright_red(),
col_help = "note".bright_cyan(),
actual_help = help
)
} else {
format!(
"{error} in {file_name} on line {line_number}:\n\t{line}\n\t{space:>span_start$}{sep:^>span_len$}\n{col_help}: {actual_help}",
file_name = file.name.display().to_string().bright_cyan().bold(),
line_number = line_number,
error = "error".bright_red(),
space = ' ',
sep = "^".bright_red(),
col_help = "note".bright_cyan(),
actual_help = help
)
}
}
}
23 changes: 18 additions & 5 deletions src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,22 @@ pub use clap::{Parser, Subcommand};
use crate::abort;

#[derive(Parser)]
#[command(author, version, about)]
#[command(
author = "Jujumba",
version,
about = "
The Pitusya Programming Language (=^ ◡ ^=)
"
)]
pub struct Cli {
file: PathBuf
file: PathBuf,
}
#[derive(Debug, PartialEq, Clone)]
pub struct CursoredFile {
content: Vec<char>,
content_str: String,
cursor: RefCell<usize>,
pub(crate) name: PathBuf,
pub(crate) content: Vec<char>,
pub(crate) content_str: String,
pub(crate) cursor: RefCell<usize>,
}
impl From<Cli> for PathBuf {
fn from(value: Cli) -> Self {
Expand All @@ -30,6 +37,7 @@ impl CursoredFile {
Err(_) => abort!("File {} does not exist!", file_name.display()),
};
Self {
name: file_name,
content: content.chars().collect(),
content_str: content,
cursor: RefCell::new(0),
Expand Down Expand Up @@ -69,3 +77,8 @@ impl AsRef<str> for CursoredFile {
&self.content_str
}
}
impl AsRef<[char]> for CursoredFile {
fn as_ref(&self) -> &[char] {
&self.content
}
}
6 changes: 4 additions & 2 deletions src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,18 @@ pub fn next_token(input: &mut CursoredFile) -> Token {
match regex.find_at(content, curs) {
Some(m) if m.start() == curs => {
let len = m.len();
let start = input.get_cursor();
input.move_cursor(len);
let kind = closure(m.as_str());
return Token { kind, len };
return Token { kind, len, start };
}
_ => (),
}
}
let c = input.current_char();
let start = input.get_cursor();
input.move_cursor(1);
Token::undefined(c)
Token::undefined(c, start)
}

fn get_specification() -> &'static Vec<(Regex, Box<Handler>)> {
Expand Down
Loading

0 comments on commit 16f285a

Please sign in to comment.