Skip to content

Commit

Permalink
Started working on type checker
Browse files Browse the repository at this point in the history
  • Loading branch information
Traumatism committed Jul 10, 2024
1 parent 09d66f8 commit 3e225b2
Show file tree
Hide file tree
Showing 6 changed files with 297 additions and 13 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# maeel


![](./preview.png)

### a minimalistic stack based programming language.

Expand Down
3 changes: 2 additions & 1 deletion maeel.maeel
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,9 @@ fun inline map (list rot swap (over! rot rot+ swap) for drop)
# Reduces a list using a provided function.
#
# Example:
# - ``` {1 2 3 4} (+) reduce ``` will return 10
# - ``` {1 2 3 4} (+) 0 reduce ``` will return 10
fun inline reduce (swap ~ _reduce_fn swap (swap _reduce_fn!) for)
fun inline reduceR (swap ~ _reduce_fn swap reverse (swap _reduce_fn!) for)

# Filter function
# Usage: xs pr filter
Expand Down
3 changes: 1 addition & 2 deletions maeel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ impl BocchiVM {

while let Some(temp_token) = tokens.pop() {
match temp_token.clone() {
(Token::Annotation(_), _, _) => {},
(Token::Block(temp_tokens), _, _) => {
function_tokens.reverse(); /* uhm */
function_tokens.extend(temp_tokens);
Expand Down Expand Up @@ -250,9 +251,7 @@ impl BocchiVM {
/* Push an object to the stack */
fn push(&mut self, value: Cord) {
let future_head = Guitar::new(value);

if !self.head.is_null() { unsafe { (*future_head).next = self.head; } }

self.head = future_head;
}

Expand Down
2 changes: 1 addition & 1 deletion maeellex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ pub enum Token {
macro_rules! emit_error {
($fl:expr, $line:expr, $message:expr) => {{
println!("\n{}:{} {}", $fl, $line, $message);
std::process::exit(1)
panic!();
}};
}

Expand Down
300 changes: 292 additions & 8 deletions maeells.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,297 @@
/*
Maeel type checking server, TBD.
use std::fs::File;
use std::io::Read;

[server] <--~ file content ~-- [user] <------------~,
| |
'~ [maeel engine] --~ { types, functions, errors } ~'
mod maeellex;

*/
#[macro_use] use maeellex::*;

type FunDataB = (std::rc::Rc<[TokenData]>, bool, Vec<String>, Vec<String>);

macro_rules! expect_token {
($token:tt, $tokens:expr, $fl:expr, $line:expr) => {{
match $tokens.pop() {
Some((Token::$token(value), _, _)) => value,
Some((other, file, line)) => emit_error!(file, line, format!("expected {:?}, got {other:?}", TokenRepr::$token)),
None => emit_error!($fl, $line, format!("expected {:?}, got EOF", TokenRepr::$token)),
}
}};
}

macro_rules! expect_stack {
($tpe:tt, $stack:expr, $fl:expr, $line:expr) => {{
match $stack.pop() {
Ok(Cord::$tpe) => Cord::$tpe,
Ok(other) => emit_error!($fl, $line, format!("expected {:?} on the stack, got {other:?}", CordRepr::$tpe)),
Err(_) => emit_error!($fl, $line, format!("expected {:?}, got EOF", CordRepr::$tpe)),
}
}};
}

macro_rules! binop {
($app:expr, $self:expr, $file:expr, $line:expr) => {{
let output = $app(
$self.pop().unwrap_or_else(|_| emit_error!($file, $line, "stack is empty! (binary operation LHS)")),
$self.pop().unwrap_or_else(|_| emit_error!($file, $line, "stack is empty! (binary operation RHS)")),
);

$self.push(output)
}};
}

/* Types that are used by the VM */
#[derive(Debug, Clone)]
enum Cord {
Flt,
Int,
Fun,
Str,
Lst,
}

/* Used for error messages */
#[derive(Debug)] enum CordRepr { Flt, Int, Fun, Str, Lst }

/* Maeel Stack VM */
struct BocchiVM { stack: Vec<Cord> }

impl BocchiVM {
fn process_tokens(
&mut self,
tokens: &mut Vec<TokenData>,
variables: &mut Mapper<Cord>,
functions: &mut Mapper<FunDataB>,
reverse: bool,
) {
if reverse /* Sometimes we might act like the tokens vec was a stack */ { tokens.reverse(); }

while let Some((token, file, line)) = tokens.pop() {
match token {
Token::Comment(_) | Token::Annotation(_) => {},
Token::Sym(M_ADD!()) => binop!(|a, b: Cord| b.add(a), self, &file, line),
Token::Sym(M_SUB!()) => binop!(|a, b: Cord| b.sub(a), self, &file, line),
Token::Sym(M_MUL!()) => binop!(|a, b: Cord| b.mul(a), self, &file, line),
Token::Sym(M_DIV!()) => binop!(|a, b: Cord| b.div(a), self, &file, line),
Token::Sym(M_MOD!()) => binop!(|a, b: Cord| b.rem(a), self, &file, line),

Token::Sym(M_EQ!()) => binop!(|a, b| Cord::Int, self, &file, line),
Token::Sym(M_LT!()) => binop!(|a, b| Cord::Int, self, &file, line),
Token::Sym(M_GT!()) => binop!(|a, b| Cord::Int, self, &file, line),

Token::Str(_) | Token::Flt(_) | Token::Int(_) => self.push(token.into()),

Token::Block(mut block) => {
block.reverse(); /* meow */
self.push(Cord::Fun)
}

Token::Sym(M_FUN_PUSH!()) => {
let function_name /* Function name */ = expect_token!(Name, tokens, file, line);
let function /* Function object */ = functions.get(&function_name).unwrap_or_else(|| {
emit_error!(file, line, format!("undefined function: {function_name:?}"))
});

self.push(Cord::Fun)
}
Token::Sym(M_EXEC!()) /* Manually call a function */ => {}
Token::Sym(M_THEN!()) /* Basically "if" statement */ => {}
Token::Sym(M_FORCE_DEF!()) /* Can be pushed by interpreter only */ => {
variables.insert(
expect_token!(Name, tokens, file, line),
self.pop().unwrap_or_else(|_| emit_error!(file, line, "stack is empty! (maeel)")),
);
}
Token::Sym(M_DEF!()) /* Assign stack top value to next name */ => {
let name = expect_token!(Name, tokens, file, line);
if name.starts_with("__") /* Private field */ { panic!(/* TODO: make the error message */) }
variables.insert(name, self.pop().unwrap_or_else(|_| emit_error!(file, line, "stack is empty!")));
}
Token::Sym(char) => emit_error!(file, line, format!("unknown symbol: {char}.")),
Token::Name(name) => match name.as_str() {
"putsstack" => println!("{:?}", self.stack),
M_PUTS!() => print!("{}", self.pop().unwrap() /* TODO: make an error message when stack is empty */),
M_ELIST!() => self.push(Cord::Lst),
M_FUN!() => {
let mut function_name = expect_token!(Name, tokens, file, line);
let mut function_tokens = Vec::default();
let is_inline = function_name == M_INLINE!();

if is_inline { function_name = expect_token!(Name, tokens, file, line) }

let mut input = Vec::default();
let mut output = Vec::default();

while let Some(temp_token) = tokens.pop() {
match temp_token.clone() {
(Token::Annotation(annotation), _, _) => {
let mut iter = annotation.split(" -> ")
.map(|p| p.split(" ").map(|s| s.to_string()).collect::<Vec<String>>());

input = iter.next().unwrap_or_else(|| Vec::default());
output = iter.next().unwrap_or_else(|| Vec::default());
},

(Token::Block(temp_tokens), _, _) => {
function_tokens.reverse(); /* uhm */
function_tokens.extend(temp_tokens);
function_tokens.reverse(); /* never ask if maeel could be faster */
break; /* TODO: remove this break, f*ck breaks */
}
(Token::Name(_), file, line) => {
function_tokens.push(temp_token);
function_tokens.push((Token::Sym(M_FORCE_DEF!()), file, line));
}
(other, file, line) => {
emit_error!(
file,
line,
format!("expected name(s) or a code block after 'function {function_name}'; got {other:?} instead.")
)
}
}
}

functions.insert(function_name.clone(), (function_tokens.as_slice().into(), is_inline, input, output));
}
M_LEN!() => {
match self.pop() {
Ok(Cord::Str) | Ok(Cord::Lst) => Cord::Int,
Ok(other) => emit_error!(file, line, format!("expected string or list, got {other:?}")),
Err(_) => emit_error!(file, line, "expected string or list, got EOS."),
};

self.push(Cord::Int)
}
M_GET!() => {}
M_READ!() => {
self.push(Cord::Lst)
}
M_INCLUDE!() /* This is bad */ => {
let target = expect_stack!(Str, self, file, line);
}
name => {
if let Some(value) = variables.get(name) {
self.push(value.clone())
} else if let Some((function_tokens, inline, input, output)) = functions.get(name) {
if !input.is_empty() {
let stack_len = self.stack.len();
let part = self.stack[stack_len - input.len()..].into_iter().map(|c| c.to_string()).collect::<Vec<String>>();

if &part != input {
println!("Typing error: expected {input:?}, got {part:?}")
}

drop(part);
}

match inline {
true => function_tokens.iter().for_each(|t| tokens.push(t.clone())),
false => self.process_tokens(&mut function_tokens.to_vec(), &mut variables.clone(), functions, false),
}
} else {}
}
},
};
}
}

/* Push an object to the stack */
fn push(&mut self, value: Cord) {
self.stack.push(value);
}

/* Drop-and-return the stack head */
fn pop(&mut self) -> Result<Cord, Box<dyn std::error::Error>> {
match self.stack.is_empty() {
true => Err("Stack is empty".into()),
false => {
Ok(self.stack.pop().unwrap())
}
}
}
}

impl std::fmt::Display for Cord {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Str => write!(f, "Str"),
Self::Fun => write!(f, "Fun"),
Self::Flt => write!(f, "Flt"),
Self::Int => write!(f, "Int"),
Self::Lst => write!(f, "List"),
}
}
}

impl Cord {
fn sub(self, rhs: Self) -> Self {
match (self, rhs) {
(Self::Int, Self::Int) => Self::Int,
(Self::Flt, Self::Flt)
| (Self::Flt, Self::Int)
| (Self::Int, Self::Flt) => Self::Flt,
(a, b) => panic!("Cannot substract {a} and {b}"),
}
}

fn mul(self, rhs: Self) -> Self {
match (self, rhs) {
(Self::Int, Self::Int) => Self::Int,
(Self::Flt, Self::Flt)
| (Self::Flt, Self::Int)
| (Self::Int, Self::Flt) => Self::Flt,
(Self::Int, Self::Str) | (Self::Str, Self::Int) => Self::Str,
(a, b) => panic!("Cannot multiply {a} and {b}"),
}
}

fn add(self, rhs: Self) -> Self {
match (self, rhs) {
(Self::Str, Self::Str) => Self::Str,
(Self::Int, Self::Int) => Self::Int,
(Self::Flt, Self::Flt)
| (Self::Int, Self::Flt)
| (Self::Flt, Self::Int) => Self::Flt,
(_, Self::Lst) | (Self::Lst, _) => Self::Lst,
(a, b) => panic!("Cannot add {a} and {b}"),
}
}

fn rem(self, rhs: Self) -> Self {
match (self, rhs) {
(Self::Int, Self::Int) => Self::Int,
(Self::Flt, Self::Flt)
| (Self::Int, Self::Flt)
| (Self::Flt, Self::Int) => Self::Flt,
(a, b) => panic!("Cannot divide {a} and {b}"),
}
}

fn div(self, rhs: Self) -> Self {
Self::Flt
}
}

impl From<Token> for Cord {
fn from(val: Token) -> Self {
match val {
Token::Str(x) => Cord::Str,
Token::Int(x) => Cord::Int,
Token::Flt(x) => Cord::Flt,
Token::Block(x) => Cord::Fun,
_ => panic!(),
}
}
}

fn main() {
let file = std::env::args().nth(1).unwrap();

BocchiVM { stack: Vec::default() }
.process_tokens(
&mut lex_into_tokens(&std::fs::read_to_string(&file).unwrap(), &file),
&mut std::collections::HashMap::default(), /* Variables Hashmap */
&mut std::collections::HashMap::default(), /* functions Hashmap */
true,
)

fn main() -> std::io::Result<()> {
Ok(())
}
Binary file added preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 3e225b2

Please sign in to comment.