Skip to content

Commit

Permalink
Intermediate representation
Browse files Browse the repository at this point in the history
  • Loading branch information
3top1a committed Jan 19, 2025
1 parent e622370 commit 2fade2b
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 111 deletions.
6 changes: 0 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,6 @@ print(c)
// This prints `99`
printf(c)

// Raw Brainfuck
// This should only be used in the standard library, e.g. input(), not in user code
// If this project succeeds it's ultimate goal, basm will be the only function from
// the compiler except math, and all other functions will be written in alkoholiq
basm("<>+-")

// Iterators work like in Rust
// For simplicity, if the iterator is a single number it is still ran
for ch in input_array {
Expand Down
49 changes: 1 addition & 48 deletions examples/basic.🍺
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,4 @@

x = 1
y = 2

fn + a b {
ret = 0
copy a ret
copy b ret
return ret
}

z = +( x y)
goto(z)
basm(".")


---


set x
> (alloc) + (add)

set y
> (alloc) ++ (add)


function call
allocate z
> (alloc)
new stack
>

set a
> (alloc) (copy)

set b
> (alloc) (copy)

copy a
copy b
return ret
(move ret to z)

pop stack
(zero b)
(zero a)

goto z
< (goto z)
print
.
z = + x y
7 changes: 6 additions & 1 deletion src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ pub enum Expression {
// /// Right side
// rhs: Box<Expression>,
// },

/// Variable assignment
Assignment {
/// Name of the variable
Expand Down Expand Up @@ -84,6 +83,12 @@ pub enum Expression {
/// Body
body: Box<Expression>,
},

// Return
Return {
// Name of return value
name: String,
},
}

// Not used right now because maths are functions
Expand Down
12 changes: 4 additions & 8 deletions src/codegen.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
use crate::ast::Expression;
use std::collections::HashMap;
use std::str::from_utf8;
use crate::ast::Expression;

// Codegen notes for myself:
// - Make the codegen also do comments, they will get removed in the optimization step
// - Functions: https://brainfuck.org/function_tutorial.b
// -
// -


/// Because we do not yet know the length of functions, we need to make a list of all the
/// Because we do not yet know the length of functions, we need to make a list of all the
/// basic instructions and their purpose. This will be used to generate the code after initial
/// analysis.
enum AbstractInstruction {

}

enum AbstractInstruction {}
129 changes: 129 additions & 0 deletions src/ir.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use crate::ast::Expression;
use std::collections::HashMap;


/// Intermediate representation
/// The main purpuse of this is to convert the nested expression into a linear flat instruction list
/// that is more similar to the way brainfuck works
///
/// Resources:
/// - https://andreabergia.com/series/stack-based-virtual-machines/
#[derive(Clone, Debug)]
pub enum IR {
/// Push a single byte on the stack
Push(u8),
/// Pop a value from the stack (zero it out)
Pop,

/// Add the two top most values
ADD,
/// Subtract the two topmost values
SUB,
/// Multiply the two topmost values
MUL,
/// Check for equality of the two topmost values
EQ,

/// Duplicate the topmost value
DUP,

/// Push topmost byte to variable storage
STORE(String),
/// Retrieve a named value
/// This removes the variable from the storage
LOAD(String),
/// Start if block
START_IF,
/// End if block
CLOSE_IF,

// TODO Else blocks
// TODO Functions and frames
}

#[derive(Clone, Debug)]
pub struct IRGenerator {
ir: Vec<IR>,
variables: HashMap<String, usize>
}

impl IRGenerator {
pub fn new() -> IRGenerator {
Self {
ir: vec![],
variables: HashMap::new(),
}
}

pub fn generate(&mut self, ast: Vec<Expression>) -> Vec<IR> {
for expr in ast {
self.generate_expression(expr);
}

self.ir.clone()
}

fn load(&mut self, expr: Expression) {
match expr {
Expression::Number(n) => {
self.ir.push(IR::Push(n));
}

Expression::Path(path) => {
// Duplicate the value
self.ir.push(IR::LOAD(path.clone()));
self.ir.push(IR::DUP);
self.ir.push(IR::STORE(path));
}

_ => {
todo!("Not implemented: {:?}", expr);
}
}
}

fn generate_expression(&mut self, expression: Expression) {
match expression {
Expression::Number(n) => {
self.ir.push(IR::Push(n));
}

Expression::Closure {body} => {
for expr in body {
self.generate_expression(expr);
}
}

Expression::Assignment {name, value} => {
self.generate_expression(*value);

self.variables.insert(name.clone(), 0);
self.ir.push(IR::STORE(name));
}

Expression::Call {name, args} => {
// Load all arguments
for arg in args {
self.load(arg);
}

if name == "+" {
self.ir.push(IR::ADD);
} else if name == "-" {
self.ir.push(IR::SUB);
} else if name == "*" {
self.ir.push(IR::MUL);
} else if name == "==" {
self.ir.push(IR::EQ);
} else {
todo!("Not implemented: {:?}", name);
}
}

_ => {
dbg!("Not implemented:", expression);
}
}
}
}

6 changes: 1 addition & 5 deletions src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,7 @@ pub fn tokenize_indexed(input: &str) -> Vec<IndexedToken> {
line_number,
},
Err(_) => {
eprintln!(
"Failed lexing `{}` on line {}:",
&input[span],
line_number
);
eprintln!("Failed lexing `{}` on line {}:", &input[span], line_number);
eprintln!("{}{}", line_text_before, line_text_after);
eprintln!("{}^", " ".repeat(line_text_before.len() - 1));
std::process::exit(1)
Expand Down
7 changes: 4 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use std::io::Read;

mod ast;
mod codegen;
mod ir;
mod lexer;
mod parser;
mod tokens;
mod utils;
mod codegen;

fn main() {
let input = std::env::args().nth(1).map_or_else(
Expand Down Expand Up @@ -34,6 +35,6 @@ fn main() {
#[cfg(debug_assertions)]
dbg!(&ast);

let code = codegen::Codegen::new().generate(ast);
println!("{}", code);
let ir = ir::IRGenerator::new().generate(ast);
dbg!(ir);
}
79 changes: 41 additions & 38 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ pub struct Parser {

impl Parser {
pub fn new(tokens: Vec<IndexedToken>, _input: String) -> Self {
Parser {
tokens,
current: 0,
}
Parser { tokens, current: 0 }
}

fn next(&mut self) -> Option<&IndexedToken> {
Expand All @@ -35,13 +32,11 @@ impl Parser {
}

fn peek(&self) -> Option<Token> {
self.tokens
.get(self.current).map(|x| x.clone().token)
self.tokens.get(self.current).map(|x| x.clone().token)
}

fn ipeek(&self) -> Option<IndexedToken> {
self.tokens
.get(self.current).cloned()
self.tokens.get(self.current).cloned()
}

fn check(&self, token: Token) -> bool {
Expand All @@ -57,18 +52,22 @@ impl Parser {

while !self.is_at_end() {
if let Some(expr) = self.parse_expression() {
// Check if the new expression is valid
if !self.valid(&expressions, &expr) {
let last_parsed_token = self.tokens[self.current - 1].clone();
eprintln!("Unexpected expression {:?} on line {}:", expr, last_parsed_token.line_number);
eprintln!("{}", last_parsed_token.line);
eprintln!(
"{}{}",
" ".repeat(last_parsed_token.chars_before - last_parsed_token.range.len()),
"^".repeat(last_parsed_token.range.len())
);
panic!();
}
// Will be done in the IR generation
// // Check if the new expression is valid
// if !self.valid(&expressions, &expr) {
// let last_parsed_token = self.tokens[self.current - 1].clone();
// eprintln!(
// "Unexpected expression {:?} on line {}:",
// expr, last_parsed_token.line_number
// );
// eprintln!("{}", last_parsed_token.line);
// eprintln!(
// "{}{}",
// " ".repeat(last_parsed_token.chars_before - last_parsed_token.range.len()),
// "^".repeat(last_parsed_token.range.len())
// );
// panic!();
// }

expressions.push(expr);
} else {
Expand Down Expand Up @@ -258,7 +257,27 @@ impl Parser {
args,
body: Box::new(body),
})
},
}

// Return
Token::Return => {
self.advance();
let name = self.next()?.token.inner_string()?;

Some(Expression::Return { name })
}

// Math operations
Token::Add => {
// + a b
self.advance();
let a = self.parse_expression()?;
let b = self.parse_expression()?;
Some(Expression::Call {
name: "+".to_string(),
args: vec![a, b],
})
}

_ => None,
}
Expand All @@ -268,26 +287,10 @@ impl Parser {
Some(match lit {
Token::Integer(num) => Expression::Number(num),
Token::Char(num) => Expression::Number(num),
Token::String(s) => {
Expression::Array(s.bytes().map(Expression::Number).collect())
}
Token::String(s) => Expression::Array(s.bytes().map(Expression::Number).collect()),
Token::True => Expression::Number(1),
Token::False => Expression::Number(0),
_ => return None,
})
}

fn valid(&self, expressions: &Vec<Expression>, new: &Expression) -> bool {
match new {
Expression::Path(_) => {
// Check if the previous expression was also a path
if let Some(Expression::Path(_)) = expressions.last() {
return false;
}

true
},
_ => true,
}
}
}
Loading

0 comments on commit 2fade2b

Please sign in to comment.