Skip to content

Commit

Permalink
Seems like the parser is complete
Browse files Browse the repository at this point in the history
  • Loading branch information
3top1a committed Jan 15, 2025
1 parent c89bfdb commit 2efe93d
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 67 deletions.
32 changes: 16 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,28 +74,28 @@ basm("<>+-")
// Iterators work like in Rust
for ch in input_array {
// Indents are 8 wide tabs
printf(ch);
printf(ch)
// \n prints out a new line, a single \ does not need to be escaped
print('\n');
print('\n')
}

// Example foobar implementation
hit = false
for i in 0..254 {
if %(i 5) == 0 {
hit = true
print("Foo")
}
if %(i 7) == 0 {
hit = true
print("Bar")
}
if hit == true {
print("\n")
} else {
printf(i)
print('\n')
}
if eq( %( i 5 ) 0 ) {
hit = true
print("Foo")
}
if eq( %( i 7 ) 0 ) {
hit = true
print("Bar")
}
if eq( hit true ) {
print("\n")
} else {
printf(i)
print('\n')
}
}

```
Expand Down
11 changes: 6 additions & 5 deletions examples/basic.🍺
Original file line number Diff line number Diff line change
Expand Up @@ -58,23 +58,24 @@ basm("<>+-")
// Iterators work like in Rust
for ch in input_array {
// Indents are 8 wide tabs
printf(ch);
printf(ch)
// \n prints out a new line,
print('\n');
print('\n')
}

// Example foobar implementation
hit = false
for i in 0..254 {
if %(i 5) == 0 {
// The if branch will be hit if the expression inside is not 0
if eq( %( i 5 ) 0 ) {
hit = true
print("Foo")
}
if %(i 7) == 0 {
if eq( %( i 7 ) 0 ) {
hit = true
print("Bar")
}
if hit == true {
if eq( hit true ) {
print("\n")
} else {
printf(i)
Expand Down
25 changes: 23 additions & 2 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,21 @@ pub enum Expression {
/// Variable name
name: String,
/// Range
range: (Box<Expression>, Box<Expression>),
range: Iterator,
/// Body
body: Vec<Expression>,
/// Should be of type Expression::Expression
body: Box<Expression>,
},

// If
If {
/// Condition
condition: Box<Expression>,
/// Then branch
/// Should be of type Expression::Expression
then_branch: Box<Expression>,
/// Else branch
else_branch: Option<Box<Expression>>,
},
}

Expand All @@ -80,3 +92,12 @@ pub enum UnaryOperator {
/// Works by subtracting the number from 0 and returning the absolute value.
Negate,
}

#[derive(Debug, PartialEq, Clone)]
pub enum Iterator {
Range {
start: Box<Expression>,
end: Box<Expression>,
},
Path(Box<Expression>),
}
77 changes: 42 additions & 35 deletions src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,6 @@ pub enum Token {
#[token("<")]
Less,

// #[token("+")]
// Plus,
// #[token("-")]
// Minus,
// #[token("*")]
// Multiply,
// #[token("/")]
// Divide,

// Brackets
#[token("[")]
SquareOpen,
Expand All @@ -33,15 +24,12 @@ pub enum Token {
RoundOpen,
#[token(")")]
RoundClose,

#[token("{")]
CurlyOpen,
#[token("}")]
CurlyClose,

// Separators
// #[token(",")]
// Comma,
#[token(";")]
Semicolon,

Expand All @@ -50,6 +38,10 @@ pub enum Token {
True,
#[token("false")]
False,
#[token("if")]
If,
#[token("else")]
Else,

// Iterators
#[token("for")]
Expand Down Expand Up @@ -77,21 +69,54 @@ pub enum Token {

// Identifiers
#[regex(r"[a-zA-Z_][a-zA-Z0-9_]*", |lex| lex.slice().to_string())]
#[regex(r"\+|\-|\*|\/|%", |lex| lex.slice().to_string())]
#[regex(r"\+|\-|\*|\/|%", |lex| lex.slice().to_string())] // Because math ops are also functions
Identifier(String),
}

impl Token {
pub fn is_literal(&self) -> bool {
match self {
Token::Char(_) | Token::False | Token::True | Token::String(_) | Token::Integer(_) => {
true
}
_ => false,
}
}

pub fn is_identifier(&self) -> bool {
match self {
Token::Identifier(_) => true,
_ => false,
}
}

pub fn inner_string(&self) -> Option<String> {
match self {
Token::Identifier(x) => Some(x.clone()),
Token::String(x) => Some(x.clone()),
_ => None
}
}
}

#[derive(Debug, PartialEq, Clone)]
pub struct IndexedToken {
pub token: Token,
pub range: Range<usize>,
pub line: String,
pub line_number: usize,
pub chars_before: usize,
}

impl Into<Token> for IndexedToken {
fn into(self) -> Token {
self.token
impl IndexedToken {
pub fn new_hidden(token: Token) -> Self {
IndexedToken {
token,
range: 0..0,
line: "".to_string(),
line_number: 0,
chars_before: 0,
}
}
}

Expand All @@ -109,6 +134,7 @@ pub fn tokenize_indexed(input: &str) -> Vec<IndexedToken> {
range: span.start..span.end,
line: line_text_before.to_owned() + line_text_after,
chars_before: line_text_before.len(),
line_number,
},
Err(e) => {
eprintln!("Failed lexing `{}` on line {}:", input[span].to_string(), line_number);
Expand All @@ -118,32 +144,13 @@ pub fn tokenize_indexed(input: &str) -> Vec<IndexedToken> {
}
}
}).collect();
#[cfg(debug_assertions)]
dbg!(&output);
output
}

pub fn tokenize(input: &str) -> Vec<Token> {
tokenize_indexed(input).into_iter().map(|x| x.token).collect()
}

impl Token {
pub fn is_literal(&self) -> bool {
match self {
Token::Char(_) | Token::False | Token::True | Token::String(_) | Token::Integer(_) => {
true
}
_ => false,
}
}

pub fn is_identifier(&self) -> bool {
match self {
Token::Identifier(_) => true,
_ => false,
}
}
}

#[cfg(test)]
mod tests {
Expand Down
4 changes: 4 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ fn main() {

let tokens = lexer::tokenize_indexed(&input);

let output = tokens.iter().map(|x| x.token.clone()).collect::<Vec<_>>();
#[cfg(debug_assertions)]
dbg!(&output);

let mut parser = parser::Parser::new(tokens, input);
let ast = parser.parse();

Expand Down
76 changes: 67 additions & 9 deletions src/parser.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::ast;
use crate::ast::{Expression, UnaryOperator};
use crate::lexer::{IndexedToken, Token};
use crate::utils::repeat;
Expand Down Expand Up @@ -59,11 +60,15 @@ impl Parser {
if let Some(expr) = self.parse_expression() {
expressions.push(expr);
} else {
// Print the generated AST anyway
dbg!(expressions);

let itoken = self.ipeek().unwrap();
eprintln!("Failed to parse expression {:?}", self.peek().unwrap());
eprintln!("At line:\n{}", itoken.line);
eprintln!("{}^", " ".repeat(itoken.chars_before - 1));
std::process::exit(1);
eprintln!("Failed to parse token {:?}", self.peek().unwrap());
eprintln!("At line {}:\n{}", itoken.line_number, itoken.line);
eprintln!("{}{}", " ".repeat(itoken.chars_before - itoken.range.len()), "^".repeat(itoken.range.len()));

panic!();
}
}

Expand All @@ -76,7 +81,7 @@ impl Parser {

if token.is_literal() {
self.advance();
return Some(Self::parse_literal(token));
return Some(Self::parse_literal(token)?);
}

match token {
Expand Down Expand Up @@ -163,18 +168,71 @@ impl Parser {
})
}

// If statements
Token::If => {
self.advance();

let condition = self.parse_expression()?;

let then_branch = self.parse_expression()?;

// Check for optional else
let else_branch = if self.consume(Token::Else).is_some() {
Some(Box::new(self.parse_expression()?))
} else {
None
};

Some(Expression::If {
condition: Box::new(condition),
then_branch: Box::new(then_branch),
else_branch,
})
}

// For loops
Token::For => {
// for i in 0..10 { ... }
self.advance();
let name = self.next()?.token.inner_string()?;
self.consume(Token::In);

// Can be either a range or a single path
let start = self.parse_expression()?;

if self.consume(Token::DoubleDot).is_none() {
let body = self.parse_expression()?;
Some(Expression::For {
name,
range: ast::Iterator::Path(start.into()),
body: body.into(),
})
} else {
let end = self.parse_expression()?;
let body = self.parse_expression()?;
Some(Expression::For {
name,
range: ast::Iterator::Range {
start: start.into(),
end: end.into(),
},
body: body.into(),
})
}
}

_ => { None }
}
}

fn parse_literal(lit: Token) -> Expression {
match lit {
fn parse_literal(lit: Token) -> Option<Expression> {
Some(match lit {
Token::Integer(num) => Expression::Number(num),
Token::Char(num) => Expression::Number(num),
Token::String(s) => Expression::Array(s.bytes().map(|x| Expression::Number(x)).collect()),
Token::True => Expression::Number(1),
Token::False => Expression::Number(0),
_ => panic!("Unexpected literal: {:?}", lit),
}
_ => return None,
})
}
}

0 comments on commit 2efe93d

Please sign in to comment.