Finalising functions

typechecking still buggy, but close enough cause i cant handle any more
typechecker dev

added:
 - Functions
 - Constants
 - Better file positions for error reporting
 - Better error reporting, with examples being drawn in term

-MC
This commit is contained in:
MCorange 2023-04-13 00:34:08 +03:00
parent 63636e1f83
commit 7e46c07cca
20 changed files with 744 additions and 430 deletions

View File

@ -1 +1 @@
// todo: add some sort of macro
// todo: add some sort of macrow

View File

@ -2,13 +2,12 @@ const NULL 0 end
const false 0 end
const true 1 end
fn div with int int returns int int divmod drop end
fn mod with int int returns int int divmod swap drop end
fn / with int int returns int int div end
fn % with int int returns int int mod end
fn div with int int returns int then divmod drop done
fn mod with int int returns int then divmod swap drop done
fn 2dup with any any returns any over over end
fn 2drop with any any returns any drop drop end
fn dup2 with any any returns any any any any then over over done
fn drop2 with any any returns void then drop drop done
const sizeof(u64) 8 end
const sizeof(u32) 4 end

View File

@ -5,9 +5,9 @@
// @arg buff_ptr: Ptr - pointer to the buffer to write
// @arg fd: Int - file descriptor
// @ret Int
fn write with int ptr int returns int do
fn write with int ptr int returns int then
SYS_write syscall3
end
done
// Write to a file descriptor using the SYS_write syscall
// args: [buff_size, buff_ptr, fd]
@ -15,9 +15,9 @@ end
// @arg buff_ptr: Ptr - pointer to the buffer to write
// @arg fd: Int - file descriptor
// @ret Int
fn read with int ptr int returns int do
fn read with int ptr int returns int then
SYS_read syscall3
end
done
// Print a string to STDOUT
@ -25,18 +25,18 @@ end
// @arg buff_size: Int - number of bytes to write
// @arg buff_ptr: Ptr - pointer to the buffer to write
// @ret NULL
fn puts with int ptr returns null do
fn puts with int ptr returns void then
STDOUT write drop
end
done
// Print a string to STDERR
// args: [str_size, str_ptr]
// @arg buff_size: Int - number of bytes to write
// @arg buff_ptr: Ptr - pointer to the buffer to write
// @ret NULL
fn eputs with int ptr returns null do
fn eputs with int ptr returns void then
STDOUT write drop
end
done
// TODO: make putc and eputc after we make local mem
@ -44,7 +44,7 @@ end
// args: [exit_code]
// @arg exit_code: Int
// @ret NULL/NEVER
fn exit with int returns null do
fn exit with int returns void then
SYS_exit syscall1 drop
end
done

View File

@ -5,11 +5,11 @@
// @arg str_len: Int
// @arg str_ptr: Ptr
// @ret NULL/NEVER
fn assert with bool int ptr returns null do
fn assert with bool int ptr returns void then
rot
if else
"Assert failed: \"" eputs eputs
"\". Exiting!\n" eputs
1 exit
end
end
done

View File

@ -1,13 +1,14 @@
use std::{fs, path::PathBuf, io::{Write, BufWriter}};
use std::{fs, path::PathBuf, io::{Write, BufWriter}, collections::HashMap};
use crate::{constants::{Operator, OpType, KeywordType}, Args};
use color_eyre::Result;
use crate::compile::commands::linux_x86_64_compile_and_link;
use crate::constants::InstructionType;
use super::commands::linux_x86_64_run;
use super::{commands::linux_x86_64_run, Constant, Memory, Function};
use eyre::eyre;
pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
let debug = args.optimisation == "D";
let debug = args.get_opt_level()? < 1;
let mut of_c = PathBuf::from(&args.out_file);
let (mut of_o, mut of_a) = if args.out_file == *crate::DEFAULT_OUT_FILE {
@ -25,18 +26,18 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
of_a.set_extension("nasm");
let file = fs::File::create(&of_a)?;
let mut writer = BufWriter::new(&file);
let mut memories: Vec<(usize, usize)> = Vec::new();
let mut constants: Vec<(String, Option<usize>, Option<String>)> = Vec::new();
let mut memories: Vec<Memory> = Vec::new();
let mut constants: HashMap<String, Constant> = HashMap::new();
let mut functions: Vec<Function> = Vec::new();
// println!("{}", tokens.len());
let mut strings: Vec<String> = Vec::new();
writeln!(writer, "BITS 64")?;
writeln!(writer, "segment .text")?;
writeln!(writer, "print:")?;
writeln!(writer, "_dbg_print:")?;
writeln!(writer, " mov r9, -3689348814741910323")?;
writeln!(writer, " sub rsp, 40")?;
writeln!(writer, " mov BYTE [rsp+31], 10")?;
@ -73,7 +74,7 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
writeln!(writer, "global _start")?;
writeln!(writer, "_start:")?;
writeln!(writer, " lea rbp, [rel ret_stack]")?;
writeln!(writer, " call func_main")?;
writeln!(writer, " call main")?;
writeln!(writer, " jmp end")?;
@ -85,36 +86,30 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
writeln!(writer, "addr_{ti}:")?;
if token.typ == OpType::Instruction(InstructionType::PushInt) {
writeln!(writer, " ;; -- {:?} {}", token.typ, token.value)?;
} else
if token.typ == OpType::Instruction(InstructionType::PushStr) {
writeln!(writer, " ;; -- {:?} {}", token.typ, strings[token.value].escape_debug())?;
} else if token.typ == OpType::Instruction(InstructionType::PushStr) {
writeln!(writer, " ;; -- {:?} {}", token.typ, token.text.escape_debug())?;
} else {
writeln!(writer, " ;; -- {:?}", token.typ)?;
}
} else {
if ti != 0{
if &tokens[ti-1].typ == &OpType::Keyword(KeywordType::Else) ||
&tokens[ti-1].typ == &OpType::Keyword(KeywordType::End){
writeln!(writer, "addr_{ti}:")?;
}
}
if ti + 1 < tokens.len() && &tokens[ti+1].typ == &OpType::Keyword(KeywordType::End) {
if ti != 0 && tokens[ti-1].typ == OpType::Keyword(KeywordType::Else) ||
tokens[ti-1].typ == OpType::Keyword(KeywordType::End){
writeln!(writer, "addr_{ti}:")?;
}
match &token.typ {
OpType::Keyword(keyword) => {
match keyword {
&KeywordType::End |
&KeywordType::While => {
writeln!(writer, "addr_{ti}:")?;
}
_ => ()
if ti + 1 < tokens.len() && tokens[ti+1].typ == OpType::Keyword(KeywordType::End) {
writeln!(writer, "addr_{ti}:")?;
}
if let OpType::Keyword(keyword) = &token.typ {
match keyword {
&KeywordType::End |
&KeywordType::While => {
writeln!(writer, "addr_{ti}:")?;
}
_ => ()
}
_ => ()
}
}
@ -142,7 +137,7 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
},
InstructionType::Print => {
writeln!(writer, " pop rdi")?;
writeln!(writer, " call print")?;
writeln!(writer, " call _dbg_print")?;
ti += 1;
},
@ -412,11 +407,11 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
ti += 1;
},
InstructionType::None => {
println!("{:?}", token);
println!("{token:?}");
unreachable!()
},
InstructionType::FnCall => {
writeln!(writer, " call func_{}", token.text)?;
writeln!(writer, " call {}", token.text)?;
ti += 1;
},
InstructionType::Return => {
@ -426,45 +421,28 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
writeln!(writer, " ret")?;
ti += 1;
},
InstructionType::CastBool => {
ti += 1;
}
InstructionType::CastPtr => {
ti += 1;
}
InstructionType::CastInt => {
ti += 1;
}
InstructionType::CastVoid => {
ti += 1;
}
InstructionType::TypeBool => {
ti += 1;
}
InstructionType::TypePtr => {
ti += 1;
}
InstructionType::TypeInt => {
ti += 1;
}
InstructionType::TypeVoid => {
ti += 1;
}
InstructionType::TypeStr => {
ti += 1;
}
InstructionType::TypeAny => {
ti += 1;
}
InstructionType::Returns => {
ti += 1;
}
InstructionType::CastBool |
InstructionType::CastPtr |
InstructionType::CastInt |
InstructionType::CastVoid |
InstructionType::TypeBool |
InstructionType::TypePtr |
InstructionType::TypeInt |
InstructionType::TypeVoid |
InstructionType::TypeStr |
InstructionType::TypeAny |
InstructionType::Returns |
InstructionType::With => {
ti += 1;
}
InstructionType::ConstUse => {
writeln!(writer, " mov rax, qword [const_{}]", token.text)?;
writeln!(writer, " push rax")?;
let mut c = constants.get(&token.text).unwrap().clone();
c.used = true;
constants.remove(&token.text);
constants.insert(token.text.clone(), c);
ti += 1;
},
}
@ -475,12 +453,13 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
match keyword {
// block
KeywordType::If => {
KeywordType::If |
KeywordType::Do => {
writeln!(writer, " pop rax")?;
writeln!(writer, " test rax, rax")?;
writeln!(writer, " jz addr_{}", token.jmp)?;
ti += 1;
},
}
KeywordType::Else => {
writeln!(writer, " jmp addr_{}", token.jmp)?;
ti += 1;
@ -488,12 +467,6 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
KeywordType::While => {
ti += 1;
}
KeywordType::Do => {
writeln!(writer, " pop rax")?;
writeln!(writer, " test rax, rax")?;
writeln!(writer, " jz addr_{}", token.jmp)?;
ti += 1;
}
KeywordType::End => {
if ti + 1 != token.jmp {
// writeln!(writer, " jmp addr_{}", token.jmp)?;
@ -501,23 +474,42 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
ti += 1;
},
KeywordType::Memory => {
memories.push((token.addr.unwrap(), token.value));
memories.push(Memory { size: token.value, loc: token.loc.clone(), id: token.addr.unwrap() });
ti += 1;
}
KeywordType::Include => unreachable!(),
KeywordType::Constant => {
KeywordType::ConstantDef => {
// TODO: after we add c style strings add supoort for them in constants
constants.push((token.text.clone(), Some(token.value), None));
let a = args.get_opt_level()? < 1;
let c = Constant{
loc: token.loc.clone(),
name: token.text.clone(),
value_i: Some(token.value),
value_s: None,
used: a,
};
constants.insert(token.text.clone(), c);
ti += 1;
},
KeywordType::Function => {
writeln!(writer, "func_{}:", token.text)?;
KeywordType::FunctionDef => {
writeln!(writer, "{}:", token.text)?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " mov qword [rbp], rbx")?;
writeln!(writer, " add rbp, 8")?;
functions.push(Function { loc: token.loc.clone(), name: token.text.clone() });
ti += 1;
},
KeywordType::FunctionDo => ti += 1,
KeywordType::FunctionDone => {
writeln!(writer, " sub rbp, 8")?;
writeln!(writer, " mov rbx, qword [rbp]")?;
writeln!(writer, " push rbx")?;
writeln!(writer, " ret")?;
ti += 1;
}
KeywordType::FunctionThen => ti += 1,
KeywordType::Function |
KeywordType::Include |
KeywordType::Constant => unreachable!(),
}
}
}
@ -534,10 +526,14 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
writeln!(writer, " str_{}: db {} ; {}", i, s_list, s.escape_default())?;
}
for (_, s) in constants.iter().enumerate() {
if let Some(v) = &s.1 {
writeln!(writer, " const_{}: dq {}", s.0, v)?;
} else if let Some(_v) = &s.2 {
for (_, c) in constants {
if !c.used {
continue;
}
if let Some(v) = &c.value_i {
writeln!(writer, " const_{}: dq {}", c.name, v)?;
} else if let Some(_v) = &c.value_s {
todo!();
} else {
unreachable!();
@ -547,8 +543,8 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
writeln!(writer, "segment .bss")?;
for (_, s) in memories.iter().enumerate() {
writeln!(writer, " mem_{}: resb {}", s.0, s.1)?;
for s in memories {
writeln!(writer, " mem_{}: resb {}", s.id, s.size)?;
}
writeln!(writer, " ret_stack: resq 256")?;
@ -557,6 +553,13 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
// }
writer.flush()?;
pre_compile_steps(
String::from_utf8_lossy(writer.buffer()).to_string().as_str(),
functions
)?;
linux_x86_64_compile_and_link(&of_a, &of_o, &of_c, args.quiet)?;
if args.run {
let c = linux_x86_64_run(&of_c, &[], args.quiet)?;
@ -566,3 +569,22 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
Ok(0)
}
fn pre_compile_steps(_code: &str, functions: Vec<Function>) -> Result<()> {
let mut has_main = false;
for func in functions {
if func.name == "main" {
has_main = true;
}
}
if !has_main {
crate::errors::missing_main_fn();
return Err(eyre!(""));
}
Ok(())
}

View File

@ -1,5 +1,27 @@
use crate::constants::Loc;
pub mod linux_x86_64;
pub mod commands;
pub const MEM_SZ: usize = 640 * 1000; // 4kb
pub const STRING_SZ: usize = 640 * 1000; // 4kb
#[derive(Debug, Clone)]
pub struct Constant {
pub loc: Loc,
pub name: String,
pub value_i: Option<usize>,
pub value_s: Option<String>,
pub used: bool
// extern: bool
}
#[derive(Debug, Clone)]
pub struct Memory {
pub size: usize,
pub loc: Loc,
pub id: usize
}
#[derive(Debug, Clone)]
pub struct Function {
pub loc: Loc,
pub name: String
}

20
src/config.rs Normal file
View File

@ -0,0 +1,20 @@
/**
* Prints out extra information
*/
pub const DEV_MODE: bool = true;
pub const DEFAULT_OUT_FILE: &str = "a.out";
pub const DEFAULT_INCLUDES: [&str;1] = [
"./include",
// "~/.mclang/include",
];
/**
* Interpreting configs
* `MEM_SZ` is the buffer size for memory
* `STRING_SZ` is the buffer size for strings
* if you have buffer overflow consider increasing these
*/
pub const MEM_SZ: usize = 640 * 1000; // 4kb
pub const STRING_SZ: usize = 640 * 1000; // 4kb

View File

@ -71,7 +71,7 @@ pub enum InstructionType {
None // Used for macros and any other non built in word definitions
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum KeywordType {
If,
Else,
@ -81,8 +81,11 @@ pub enum KeywordType {
Include,
Memory,
Constant,
ConstantDef,
Function,
FunctionDo
FunctionDef,
FunctionThen,
FunctionDone
}
#[derive(Debug, Clone, PartialEq)]
@ -129,7 +132,7 @@ impl OpType {
InstructionType::PushInt => "Number",
InstructionType::PushStr => "String",
InstructionType::Print => "print",
InstructionType::Print => "_dbg_print",
InstructionType::Dup => "dup",
InstructionType::Drop => "drop",
InstructionType::Rot => "rot",
@ -192,7 +195,10 @@ impl OpType {
KeywordType::Memory => "memory",
KeywordType::Function => "fn",
KeywordType::Constant => "const",
KeywordType::FunctionDo => "do",
KeywordType::FunctionThen => "then",
KeywordType::FunctionDone => "done",
KeywordType::ConstantDef => "constant Definition (internal)",
KeywordType::FunctionDef => "function definition (internal)"
}
}

18
src/errors/mod.rs Normal file
View File

@ -0,0 +1,18 @@
use crate::{error, help, code_block};
pub fn missing_main_fn() {
error!("Main function not found, please create one lol");
help!("Heres a basic main function with code that prints hello world:\n{}",
code_block!(
concat!(
"include \"std.mcl\"\n",
"\n",
"fn main with void retuns void then\n",
" \"Hello world!\\n\" puts\n",
"done\n"
)
)
);
}

View File

@ -1,9 +1,11 @@
use std::collections::HashMap;
use crate::{constants::{OpType, Loc, InstructionType, KeywordType}, lerror, error};
use crate::{constants::{OpType, Loc, InstructionType, KeywordType, Operator}, lerror, error};
// use crate::util::logger;
use color_eyre::Result;
use eyre::eyre;
use super::{Memory, Function, Constant};
mod syscalls;
fn stack_pop(stack: &mut Vec<usize>, pos: &Loc) -> Result<usize> {
@ -13,56 +15,66 @@ fn stack_pop(stack: &mut Vec<usize>, pos: &Loc) -> Result<usize> {
}
}
pub fn run(tokens: &[crate::constants::Operator]) -> Result<i32>{
pub fn run(ops: &[crate::constants::Operator]) -> Result<i32>{
let mut stack: Vec<usize> = Vec::new();
let mut ti = 0;
let mut mem: Vec<u64> = vec![0; crate::compile::MEM_SZ + crate::compile::STRING_SZ];
let mut mem: Vec<u64> = vec![0; crate::MEM_SZ + crate::STRING_SZ];
let mut string_idx = 0;
let mut memories: HashMap<usize, usize> = HashMap::new();
let prerunned = pre_run(ops);
let functions = prerunned.functions;
let constants = prerunned.constants;
let memories = prerunned.memories;
let mut ret_stack: Vec<usize> = Vec::new();
// for token in &tokens {
// println!("{{typ: \"{:?}\", val: {}, jmp: {}}}", token.typ, token.value, token.jmp);
// }
while ti < tokens.len() {
let token = &tokens[ti];
let pos = token.loc.clone();
// println!("{:?}", token.typ);
match token.typ.clone() {
// jump to main func
let mut ip = if let Some(i) = functions.get("main") {i.id} else {
crate::errors::missing_main_fn();
return Err(eyre!(""));
};
while ip < ops.len() {
let op = &ops[ip];
let pos = op.loc.clone();
match op.typ.clone() {
OpType::Instruction(instruction) => {
match instruction {
InstructionType::PushInt => {
stack.push(token.value);
ti += 1;
stack.push(op.value);
ip += 1;
},
InstructionType::PushStr => {
if token.addr.is_none() {
stack.push(token.text.len()); // string len
stack.push(string_idx + crate::compile::MEM_SZ);
if op.addr.is_none() {
stack.push(op.text.len()); // string len
stack.push(string_idx + crate::MEM_SZ);
for c in token.text.bytes() {
mem[crate::compile::MEM_SZ + string_idx] = c as u64;
for c in op.text.bytes() {
mem[crate::MEM_SZ + string_idx] = u64::from(c);
string_idx += 1;
}
} else {
stack.push(token.text.len());
if let Some(addr) = token.addr {
stack.push(op.text.len());
if let Some(addr) = op.addr {
stack.push(addr);
}
}
ti += 1;
ip += 1;
},
InstructionType::Drop => {
stack.pop();
ti += 1;
ip += 1;
},
InstructionType::Dup => {
let a = stack_pop(&mut stack, &pos)?;
stack.push(a);
stack.push(a);
ti += 1;
ip += 1;
},
InstructionType::Rot => {
@ -72,14 +84,14 @@ pub fn run(tokens: &[crate::constants::Operator]) -> Result<i32>{
stack.push(b);
stack.push(a);
stack.push(c);
ti += 1;
ip += 1;
}
InstructionType::Swap => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(a);
stack.push(b);
ti += 1;
ip += 1;
}
InstructionType::Over => {
let a = stack_pop(&mut stack, &pos)?;
@ -87,52 +99,53 @@ pub fn run(tokens: &[crate::constants::Operator]) -> Result<i32>{
stack.push(b);
stack.push(a);
stack.push(b);
ti += 1;
ip += 1;
}
InstructionType::Print => {
let a = stack_pop(&mut stack, &pos)?;
println!("{a}");
// let _ = io::stdout().flush();
ti += 1;
ip += 1;
},
#[allow(clippy::cast_possible_truncation)]
InstructionType::Load8 |
InstructionType::Load32 |
InstructionType::Load64 => {
let a = stack_pop(&mut stack, &pos)?;
if a > crate::compile::MEM_SZ {
lerror!(&token.loc, "Invalid memory address {a}");
if a > crate::MEM_SZ {
lerror!(&op.loc, "Invalid memory address {a}");
return Ok(1);
}
let byte = mem[a];
stack.push(byte as usize);
ti += 1;
ip += 1;
}
#[allow(clippy::cast_possible_truncation)]
InstructionType::Store8 => {
let val = stack_pop(&mut stack, &pos)?;
let addr = stack_pop(&mut stack, &pos)?;
if addr > crate::compile::MEM_SZ {
lerror!(&token.loc, "Invalid memory address {addr}");
if addr > crate::MEM_SZ {
lerror!(&op.loc, "Invalid memory address {addr}");
return Ok(1);
}
mem[addr] = val as u8 as u64;
ti += 1;
mem[addr] = u64::from(val as u8);
ip += 1;
}
#[allow(clippy::cast_possible_truncation)]
InstructionType::Store32 => {
let val = stack_pop(&mut stack, &pos)?;
let addr = stack_pop(&mut stack, &pos)?;
if addr > crate::compile::MEM_SZ {
lerror!(&token.loc, "Invalid memory address {addr}");
if addr > crate::MEM_SZ {
lerror!(&op.loc, "Invalid memory address {addr}");
return Ok(1);
}
mem[addr] = val as u32 as u64;
ti += 1;
mem[addr] = u64::from(val as u32);
ip += 1;
}
#[allow(clippy::cast_possible_truncation)]
@ -140,13 +153,13 @@ pub fn run(tokens: &[crate::constants::Operator]) -> Result<i32>{
let val = stack_pop(&mut stack, &pos)?;
let addr = stack_pop(&mut stack, &pos)?;
if addr > crate::compile::MEM_SZ {
lerror!(&token.loc, "Invalid memory address {addr}");
if addr > crate::MEM_SZ {
lerror!(&op.loc, "Invalid memory address {addr}");
return Ok(1);
}
mem[addr] = val as u64;
ti += 1;
ip += 1;
}
// math
@ -154,77 +167,77 @@ pub fn run(tokens: &[crate::constants::Operator]) -> Result<i32>{
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(b + a);
ti += 1;
ip += 1;
},
InstructionType::Minus => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(b - a);
ti += 1;
ip += 1;
},
InstructionType::Equals => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(usize::from(b == a));
ti += 1;
ip += 1;
},
InstructionType::Gt => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(usize::from(b > a));
ti += 1;
ip += 1;
},
InstructionType::Lt => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(usize::from(b < a));
ti += 1;
ip += 1;
},
InstructionType::NotEquals => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(usize::from(b != a));
ti += 1;
ip += 1;
},
InstructionType::Ge => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(usize::from(b >= a));
ti += 1;
ip += 1;
},
InstructionType::Le => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(usize::from(b <= a));
ti += 1;
ip += 1;
},
InstructionType::Band => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(a & b);
ti += 1;
ip += 1;
}
InstructionType::Bor => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(a | b);
ti += 1;
ip += 1;
}
InstructionType::Shr => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(b >> a);
ti += 1;
ip += 1;
}
InstructionType::Shl => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(b << a);
ti += 1;
ip += 1;
}
InstructionType::DivMod => {
@ -232,13 +245,13 @@ pub fn run(tokens: &[crate::constants::Operator]) -> Result<i32>{
let b = stack_pop(&mut stack, &pos)?;
stack.push(b / a);
stack.push(b % a);
ti += 1;
ip += 1;
}
InstructionType::Mul => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(b * a);
ti += 1;
ip += 1;
}
InstructionType::Syscall0 => {
todo!();
@ -268,7 +281,7 @@ pub fn run(tokens: &[crate::constants::Operator]) -> Result<i32>{
};
stack.push(ret);
// println!("{}", stack.len());
ti += 1;
ip += 1;
},
InstructionType::Syscall4 => {
todo!();
@ -284,15 +297,32 @@ pub fn run(tokens: &[crate::constants::Operator]) -> Result<i32>{
},
InstructionType::MemUse => {
let m = memories.get(&token.addr.unwrap()).unwrap();
stack.push(*m);
ti += 1;
let m = memories.get(&op.addr.unwrap()).unwrap();
stack.push(m.id);
ip += 1;
},
InstructionType::FnCall => {
ret_stack.push(ip);
let f = functions.get(&op.text).unwrap();
ip = f.id;
}
InstructionType::Return => {
ip = ret_stack.pop().unwrap();
ip += 1;
}
InstructionType::ConstUse => {
let a = constants.get(&op.text).unwrap();
if let Some(i) = a.value_i {
stack.push(i);
} else if let Some(_s) = a.value_s.clone() {
unimplemented!();
}
ip += 1;
},
InstructionType::CastBool |
InstructionType::CastPtr |
InstructionType::CastInt |
InstructionType::FnCall |
InstructionType::Return |
InstructionType::CastVoid |
InstructionType::TypeBool |
InstructionType::TypePtr |
@ -301,9 +331,8 @@ pub fn run(tokens: &[crate::constants::Operator]) -> Result<i32>{
InstructionType::TypeStr |
InstructionType::TypeAny |
InstructionType::Returns |
InstructionType::With => ti += 1,
InstructionType::With => ip += 1,
InstructionType::None => unreachable!(),
InstructionType::ConstUse => todo!(),
}
}
@ -314,33 +343,42 @@ pub fn run(tokens: &[crate::constants::Operator]) -> Result<i32>{
let a = stack_pop(&mut stack, &pos)?;
if a == 0 {
// println!("If({ti}) => t: {:?} j: {}", tokens[token.jmp as usize].typ, token.jmp);
ti = token.jmp;
ip = op.jmp;
} else {
ti += 1;
ip += 1;
}
},
KeywordType::Else | KeywordType::End => {
ti = token.jmp;
}
KeywordType::While => {
ti += 1;
ip = op.jmp;
}
KeywordType::Do => {
let a = stack.pop().unwrap();
if a == 0 {
ti = token.jmp;
ip = op.jmp;
} else {
ti += 1;
ip += 1;
}
}
KeywordType::Memory => {
memories.insert(token.addr.unwrap(), token.value);
ti += 1;
KeywordType::While | //* exept this one, this one should just skip over
KeywordType::Memory |
KeywordType::FunctionDef |
KeywordType::ConstantDef => {
//? Disabled since we now pre run the whole program
// constants.insert(op.text.clone(), Constant { loc: op.loc.clone(), name: op.text.clone(), value_i: Some(op.value), value_s: None, used: false });
ip += 1;
},
KeywordType::FunctionDone => {
if let Some(i) = ret_stack.pop() {
ip = i + 1;
} else {
break;
}
},
KeywordType::FunctionThen => ip += 1,
KeywordType::Constant |
KeywordType::Function |
KeywordType::Include => unreachable!(),
KeywordType::Constant => todo!(),
KeywordType::Function => todo!(),
KeywordType::FunctionDo => todo!(),
}
}
@ -350,3 +388,33 @@ pub fn run(tokens: &[crate::constants::Operator]) -> Result<i32>{
Ok(0)
}
pub struct Defineds {
pub memories: HashMap<usize, Memory>,
pub functions: HashMap<String, Function>,
pub constants: HashMap<String, Constant>
}
pub fn pre_run(ops: &[Operator]) -> Defineds {
let mut defineds = Defineds{
memories: HashMap::new(),
functions: HashMap::new(),
constants: HashMap::new(),
};
for (ip, op) in ops.iter().enumerate() {
match op.typ {
OpType::Keyword(KeywordType::Memory) => {
defineds.memories.insert(op.addr.unwrap(), Memory { size: op.value, loc: op.loc.clone(), id: op.addr.unwrap() });
},
OpType::Keyword(KeywordType::FunctionDef) => {
defineds.functions.insert(op.text.clone(), Function { loc: op.loc.clone(), name: op.text.clone(), id: ip });
},
OpType::Keyword(KeywordType::ConstantDef) => {
defineds.constants.insert(op.text.clone(), Constant { loc: op.loc.clone(), name: op.text.clone(), value_i: Some(op.value), value_s: None, used: false });
},
_ => ()
}
}
defineds
}

View File

@ -1,4 +1,4 @@
#[allow(clippy::cast_possible_truncation)]
pub fn sys_write(sys_n: usize, fd: usize, buff: usize, count: usize, mem: &Vec<u64> ) -> usize {
let mem = (*mem).clone();
// println!("{:?}", &mem[buff..(buff + count)]);

View File

@ -1 +1,27 @@
use crate::constants::Loc;
pub mod linux_x86_64;
#[derive(Debug, Clone)]
pub struct Constant {
pub loc: Loc,
pub name: String,
pub value_i: Option<usize>,
pub value_s: Option<String>,
pub used: bool
// extern: bool
}
#[derive(Debug, Clone)]
pub struct Memory {
pub size: usize,
pub loc: Loc,
pub id: usize
}
#[derive(Debug, Clone)]
pub struct Function {
pub loc: Loc,
pub name: String,
pub id: usize
}

View File

@ -1,6 +1,5 @@
use crate::{constants::{Token, TokenType}, Args};
use color_eyre::Result;
fn lex_word(s: String, tok_type: TokenType) -> (TokenType, String) {
match s {
@ -89,7 +88,7 @@ fn lex_line(text: &str) -> Vec<(usize, String, TokenType)> {
tokens
}
pub fn lex<S: Into<String> + std::marker::Copy>(code: &str, file: S, _args: &Args) -> Result<Vec<Token>> {
pub fn lex(code: &str, file: &str, _args: &Args) -> Vec<Token> {
let lines: Vec<(usize, &str)> = code
.split(['\n', '\r'])
.enumerate()
@ -104,7 +103,7 @@ pub fn lex<S: Into<String> + std::marker::Copy>(code: &str, file: S, _args: &Arg
for (col, tok, tok_type) in lt {
let (tok_type, tok) = lex_word(tok, tok_type);
let t = Token{
file: file.into(),
file: file.to_string(),
line: row + 1,
col,
text: tok,
@ -123,5 +122,5 @@ pub fn lex<S: Into<String> + std::marker::Copy>(code: &str, file: S, _args: &Arg
// }
Ok(tokens)
tokens
}

View File

@ -1,3 +1,5 @@
#![allow(clippy::wildcard_imports)]
#![allow(clippy::too_many_lines)]
mod constants;
mod interpret;
mod util;
@ -7,18 +9,14 @@ mod lexer;
mod preprocessor;
mod typechecker;
mod precompiler;
use std::fs;
mod config;
mod errors;
use config::*;
use std::{fs, collections::HashMap};
use clap::Parser;
pub const DEFAULT_OUT_FILE: &str = "a.out";
pub const DEFAULT_INCLUDES: [&str;2] = [
"./include",
"~/.mclang/include",
];
use color_eyre::Result;
use eyre::eyre;
#[derive(Parser, Debug, Clone)]
#[command(author, version, about, long_about = None)]
@ -64,31 +62,59 @@ pub struct Args {
}
fn main() {
impl Args {
/// Get optimisation level
/// 0 => no optimisations
/// 1 => slight optimisations, mostly size ones
/// # Errors
///
/// Throws when the opt level is not known
pub fn get_opt_level(&self) -> Result<usize>{
match self.optimisation.as_str() {
"D" | "d" => Ok(0),
"0" | "" => Ok(1),
o => {
error!("Unknown optimisation level {o}");
Err(eyre!(""))
}
}
}
}
fn main() -> Result<()>{
let args = Args::parse();
let Ok(code) = fs::read_to_string(&args.in_file) else {
error!("Failed to read file {}, exiting!", &args.in_file);
return;
};
let Ok(tokens) = lexer::lex(&code, &args.in_file, &args) else {
error!("Lexing failed, exiting!");
return;
return Ok(());
};
let tokens = lexer::lex(&code, args.in_file.as_str(), &args);
let mut parser = parser::Parser::new(tokens);
let Ok(tokens) = parser.parse(&args) else {
error!("Parsing failed, exiting!");
return;
let mut parser = parser::Parser::new(tokens, &args, None);
let tokens = match parser.parse(){
Ok(t) => t,
Err(e) => {
error!("Parsing failed, exiting!");
if crate::DEV_MODE {
return Err(e)
}
return Ok(());
}
};
let Ok(tokens) = typechecker::typecheck(tokens, &args) else {
error!("Typechecking failed, exiting!");
return;
match typechecker::typecheck(tokens.clone(), &args, None, HashMap::new(), HashMap::new()) {
Ok(_) => (),
Err(e) => {
error!("Typechecking failed, exiting!");
if crate::DEV_MODE {
return Err(e);
}
return Ok(());
}
};
let c = if args.compile && args.interpret {

View File

@ -9,14 +9,14 @@ pub fn cross_ref(mut program: Vec<Operator>) -> Result<Vec<Operator>> {
for ip in 0..program.len() {
let op = &program.clone()[ip];
// println!("{op:?}");
match op.typ {
OpType::Keyword(KeywordType::If) |
OpType::Keyword(KeywordType::Function) |
OpType::Keyword(KeywordType::While) => {
// OpType::Keyword(KeywordType::FunctionDef) |
OpType::Keyword(KeywordType::If | KeywordType::While) => {
stack.push(ip);
}
OpType::Keyword(KeywordType::Else) => {
let if_ip = if let Some(x) = stack.pop() { x } else {
let Some(if_ip) = stack.pop() else {
lerror!(&op.loc, "Unclosed-if else block");
return Err(eyre!("Cross referencing"));
};
@ -29,14 +29,13 @@ pub fn cross_ref(mut program: Vec<Operator>) -> Result<Vec<Operator>> {
stack.push(ip);
},
OpType::Keyword(KeywordType::End) => {
let block_ip = if let Some(block_ip) = stack.pop() { block_ip } else {
let Some(block_ip) = stack.pop() else {
lerror!(&op.loc, "Unclosed if, if-else, while-do, function, memory, or constant");
return Err(eyre!("Cross referencing"));
};
match &program[block_ip].typ {
OpType::Keyword(KeywordType::If) |
OpType::Keyword(KeywordType::Else) => {
OpType::Keyword(KeywordType::If | KeywordType::Else) => {
program[block_ip].jmp = ip;
program[ip].jmp = ip + 1;
}
@ -45,12 +44,10 @@ pub fn cross_ref(mut program: Vec<Operator>) -> Result<Vec<Operator>> {
program[ip].jmp = program[block_ip].jmp;
program[block_ip].jmp = ip + 1;
}
OpType::Keyword(KeywordType::FunctionDo) => {
OpType::Keyword(KeywordType::FunctionThen) => {
program[ip].typ = OpType::Instruction(InstructionType::Return);
}
OpType::Keyword(KeywordType::Memory) |
OpType::Keyword(KeywordType::Function) |
OpType::Keyword(KeywordType::Constant) => (),
OpType::Keyword(KeywordType::Memory | KeywordType::Constant) => (),
a => {
println!("{a:?}");
@ -61,15 +58,11 @@ pub fn cross_ref(mut program: Vec<Operator>) -> Result<Vec<Operator>> {
}
OpType::Keyword(KeywordType::Do) => {
let block_ip = if let Some(x) = stack.pop() { x } else {
let Some(block_ip) = stack.pop() else {
lerror!(&op.loc, "Unclosed while-do block");
return Err(eyre!("Cross referencing"));
};
if program[block_ip].typ == OpType::Keyword(KeywordType::Function) {
program[ip].typ = OpType::Keyword(KeywordType::FunctionDo);
}
program[ip].jmp = block_ip;
stack.push(ip);
}
@ -78,7 +71,7 @@ pub fn cross_ref(mut program: Vec<Operator>) -> Result<Vec<Operator>> {
}
if !stack.is_empty() {
println!("{:?}", stack);
// println!("{:?}", stack);
lerror!(&program[stack.pop().expect("Empy stack")].clone().loc,"Unclosed block, {:?}", program[stack.pop().expect("Empy stack")].clone());
return Err(eyre!("Unclosed block"));
}
@ -86,18 +79,27 @@ pub fn cross_ref(mut program: Vec<Operator>) -> Result<Vec<Operator>> {
Ok(program.clone())
}
pub struct Parser {
tokens: Vec<Token>
pub struct Parser<'a> {
tokens: Vec<Token>,
pub preprocessor: Preprocessor<'a>,
#[allow(dead_code)]
args: &'a Args
}
impl Parser {
pub fn new(file: Vec<Token>) -> Self {
impl<'a> Parser<'a> {
pub fn new(file: Vec<Token>, args: &'a Args, p: Option<Preprocessor<'a>>) -> Self {
let pre = if let Some(p) = p {p} else {
Preprocessor::new(Vec::new(), args)
};
Self{
tokens: file
tokens: file,
preprocessor: pre,
args
}
}
pub fn parse(&mut self, args: &Args) -> Result<Vec<Operator>> {
pub fn parse(&mut self) -> Result<Vec<Operator>> {
let mut tokens = Vec::new();
for token in &self.tokens {
@ -134,9 +136,9 @@ impl Parser {
}
let t = Preprocessor::new(tokens.clone(), args).preprocess()?.get_ops();
let t = cross_ref(t.clone())?;
self.preprocessor.program = tokens;
let t = self.preprocessor.preprocess()?.get_ops();
let t = cross_ref(t)?;
Ok(t)
}
@ -145,12 +147,12 @@ impl Parser {
pub fn lookup_word<P: Deref<Target = Loc>>(s: &str, _pos: P) -> OpType {
let n = s.parse::<usize>();
if let Ok(_) = n {
if n.is_ok() {
return OpType::Instruction(InstructionType::PushInt);
}
match s {
//stack
"print" => OpType::Instruction(InstructionType::Print),
"_dbg_print" => OpType::Instruction(InstructionType::Print),
"dup" => OpType::Instruction(InstructionType::Dup),
"drop" => OpType::Instruction(InstructionType::Drop),
"rot" => OpType::Instruction(InstructionType::Rot),
@ -204,6 +206,8 @@ pub fn lookup_word<P: Deref<Target = Loc>>(s: &str, _pos: P) -> OpType {
"memory" => OpType::Keyword(KeywordType::Memory),
"const" => OpType::Keyword(KeywordType::Constant),
"fn" => OpType::Keyword(KeywordType::Function),
"then" => OpType::Keyword(KeywordType::FunctionThen),
"done" => OpType::Keyword(KeywordType::FunctionDone),
"return" => OpType::Instruction(InstructionType::Return),
"returns" => OpType::Instruction(InstructionType::Returns),
"bool" => OpType::Instruction(InstructionType::TypeBool),

View File

@ -135,7 +135,7 @@ pub fn precompile(tokens: &Vec<Operator>) -> Result<Vec<usize>>{
}
}
}
_ => {
OpType::Keyword(_) => {
lerror!(&token.loc, "Unsupported precompiler keyword {:?}", token.typ);
dbg!(tokens);
return Err(eyre!(""));

View File

@ -2,6 +2,7 @@ use std::collections::HashMap;
use std::ops::Deref;
use std::path::{PathBuf, Path};
use color_eyre::Result;
use eyre::eyre;
@ -13,19 +14,19 @@ use crate::parser::lookup_word;
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Function {
pub loc: Loc,
pub name: String
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Constant {
pub loc: Loc,
pub name: String
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Memory {
pub loc: Loc,
pub id: usize
@ -36,11 +37,12 @@ type Functions = HashMap<String, Function>;
type Memories = HashMap<String, Memory>;
type Constants = HashMap<String, Constant>;
#[derive(Debug, Clone)]
pub struct Preprocessor<'a> {
program: Vec<Operator>,
functions: Functions,
memories: Memories,
constants: Constants,
pub program: Vec<Operator>,
pub functions: Functions,
pub memories: Memories,
pub constants: Constants,
args: &'a Args
}
@ -49,7 +51,7 @@ impl<'a> Preprocessor<'a> {
pub fn new(prog: Vec<Operator>, args: &'a Args) -> Self {
Self {
program: prog,
args: args,
args,
functions: HashMap::new(),
memories: HashMap::new(),
constants: HashMap::new(),
@ -88,16 +90,18 @@ impl<'a> Preprocessor<'a> {
in_paths.append(&mut crate::DEFAULT_INCLUDES.to_vec().clone().iter().map(|f| (*f).to_string()).collect::<Vec<String>>());
let mut include_code = String::new();
let mut pth = PathBuf::new();
if include_path.text.chars().collect::<Vec<char>>()[0] == '.' {
let p = Path::new(include_path.loc.0.as_str());
let p = p.parent().unwrap();
let p = p.join(&include_path.text);
pth = p.clone();
include_code = std::fs::read_to_string(p)?;
} else {
for path in in_paths {
let p = PathBuf::from(path);
let p = p.join(&include_path.text);
pth = p.clone();
if p.exists() {
include_code = std::fs::read_to_string(p)?;
@ -110,9 +114,14 @@ impl<'a> Preprocessor<'a> {
lerror!(&include_path.loc, "Include file in path '{}' was not found or is empty", include_path.text);
return Err(eyre!(""));
}
let code = lex(&include_code, &self.args.in_file, &self.args)?;
let mut p = parser::Parser::new(code);
let mut code = p.parse(self.args)?;
let a = pth.to_str().unwrap().to_string();
let code = lex(&include_code, a.as_str(), self.args);
let mut p = parser::Parser::new(code, self.args, Some(self.clone()));
let mut code = p.parse()?;
self.set_constants(p.preprocessor.get_constants());
self.set_functions(p.preprocessor.get_functions());
self.set_memories(p.preprocessor.get_memories());
code.reverse();
rtokens.append(&mut code);
@ -124,9 +133,9 @@ impl<'a> Preprocessor<'a> {
return Err(eyre!(""));
}
let memory_name = rtokens.pop().unwrap();
let name = rtokens.pop().unwrap();
self.is_word_available(&memory_name, KeywordType::Function)?;
self.is_word_available(&name, KeywordType::Memory)?;
let mut code: Vec<Operator> = Vec::new();
@ -157,7 +166,7 @@ impl<'a> Preprocessor<'a> {
token.addr = Some(self.memories.len());
program.push(token.clone());
self.memories.insert(memory_name.text, Memory { loc: token.loc, id: self.memories.len() });
self.memories.insert(name.text, Memory { loc: token.loc, id: self.memories.len() });
}
_ if op_type == OpType::Keyword(KeywordType::Function) => {
@ -166,47 +175,115 @@ impl<'a> Preprocessor<'a> {
return Err(eyre!(""));
}
let function_name = rtokens.pop().unwrap();
let mut name = rtokens.pop().unwrap();
self.is_word_available(&function_name, KeywordType::Function)?;
if let '0'..='9' = name.text.chars().next().unwrap() {
lerror!(&name.loc, "Function name starts with a number which is not allowed");
return Err(eyre!(""));
}
// let mut should_warn = false;
for c in name.text.clone().chars() {
match c {
'a'..='z' |
'A'..='Z' |
'0'..='9' |
'-' | '_' => (),
'(' | ')' => {
name.text = name.text.clone().replace('(', "__OP_PAREN__").replace(')', "__CL_PAREN__");
}
_ => {
lerror!(&name.loc, "Function name contains '{c}', which is unsupported");
return Err(eyre!(""));
}
}
}
// if should_warn {
//TODO: add -W option in cli args to enable more warnings
//lwarn!(&function_name.loc, "Function name contains '(' or ')', this character is not supported but will be replaced with '__OP_PAREN__' or '__CL_PAREN__' respectively ");
// }
self.is_word_available(&name, KeywordType::Function)?;
self.functions.insert(function_name.text.clone(), Function{
loc: function_name.loc.clone(),
name: function_name.text.clone(),
self.functions.insert(name.text.clone(), Function{
loc: name.loc.clone(),
name: name.text.clone(),
});
token.text = function_name.text;
let mut fn_def = token.clone();
fn_def.typ = OpType::Keyword(KeywordType::FunctionDef);
fn_def.text = name.text;
// println!("{:?}", token);
program.push(token);
program.push(fn_def);
}
_ if op_type == OpType::Keyword(KeywordType::Constant) => {
if rtokens.is_empty() {
lerror!(&token.loc, "Constant name not found, expected {} but found nothing", TokenType::Word.human());
return Err(eyre!(""));
}
// println!("{token:?}");
let const_name = rtokens.pop().unwrap();
let mut name = rtokens.pop().unwrap();
// let mut should_warn = false;
self.is_word_available(&const_name, KeywordType::Function)?;
self.constants.insert(const_name.text.clone(), Constant{
loc: const_name.loc.clone(),
name: const_name.text.clone(),
});
token.text = const_name.text;
let item = rtokens.pop().unwrap();
if item.tok_typ == TokenType::Int {
token.value = item.value;
if let '0'..='9' = name.text.chars().next().unwrap() {
lerror!(&name.loc, "Constant name starts with a number which is not allowed");
return Err(eyre!(""));
}
if let None = rtokens.pop() {
for c in name.text.clone().chars() {
match c {
'a'..='z' |
'A'..='Z' |
'0'..='9' |
'-' | '_' => (),
'(' | ')' => {
// should_warn = true;
name.text = name.text.clone().replace('(', "__OP_PAREN__").replace(')', "__CL_PAREN__");
}
_ => {
lerror!(&name.loc, "Constant name contains '{c}', which is unsupported");
return Err(eyre!(""));
}
}
}
// if should_warn {
//TODO: add -W option in cli args to enable more warnings
//lwarn!(&name.loc, "Constant name contains '(' or ')', this character is not supported but will be replaced with '__OP_PAREN__' or '__CL_PAREN__' respectively ");
// }
self.is_word_available(&name, KeywordType::Constant)?;
self.constants.insert(name.text.clone(), Constant{
loc: name.loc.clone(),
name: name.text.clone(),
});
// println!("{:?}", self.constants);
let mut const_def = token.clone();
const_def.typ = OpType::Keyword(KeywordType::ConstantDef);
const_def.text = name.text;
let item = rtokens.pop().unwrap();
if item.tok_typ == TokenType::Int {
const_def.value = item.value;
} else {
lerror!(&token.loc, "For now only {:?} is allowed in constants", TokenType::Int);
return Err(eyre!(""));
}
let posibly_end = rtokens.pop();
// println!("end: {posibly_end:?}");
if posibly_end.is_none() || posibly_end.unwrap().typ != OpType::Keyword(KeywordType::End) {
lerror!(&token.loc, "Constant was not closed with an 'end' instruction, expected 'end' but found nothing");
return Err(eyre!(""));
}
// token.value =
program.push(token);
program.push(const_def);
}
_ => {
@ -216,14 +293,18 @@ impl<'a> Preprocessor<'a> {
}
}
self.program = program;
// println!("has do tokens: {:?}", self.program.iter().map(|t| if t.typ == OpType::Keyword(KeywordType::Do) {Some(t)} else {None} ).collect::<Vec<Option<&Operator>>>());
//* Feel free to fix this horrifying shit
//* i wanna kms
let mut times = 0;
// dbg!(program.clone());
while self.program.iter().map(|f| {
if f.tok_typ == TokenType::Word && f.typ != OpType::Instruction(InstructionType::FnCall) && f.typ != OpType::Instruction(InstructionType::MemUse) && f.typ != OpType::Keyword(KeywordType::Function) && f.typ != OpType::Keyword(KeywordType::Constant) && f.typ != OpType::Instruction(InstructionType::ConstUse) {
if f.tok_typ == TokenType::Word &&
f.typ != OpType::Instruction(InstructionType::FnCall) &&
f.typ != OpType::Instruction(InstructionType::MemUse) &&
f.typ != OpType::Keyword(KeywordType::FunctionDef) &&
f.typ != OpType::Keyword(KeywordType::ConstantDef) &&
f.typ != OpType::Instruction(InstructionType::ConstUse) {
lookup_word(&f.text, &f.loc)
} else {
OpType::Instruction(InstructionType::PushInt) // i hate myself, this is a randomly picked optype so its happy and works
@ -238,7 +319,6 @@ impl<'a> Preprocessor<'a> {
self.expand()?;
times += 1;
}
Ok(self)
}
@ -251,7 +331,7 @@ impl<'a> Preprocessor<'a> {
while !rtokens.is_empty() {
let op = rtokens.pop().unwrap();
let op_type = op.typ.clone();
if op.tok_typ.clone() == TokenType::Word {
if op.tok_typ == TokenType::Word {
match op_type {
OpType::Instruction(InstructionType::None) => {
let m = self.functions.get(&op.text);
@ -299,7 +379,7 @@ impl<'a> Preprocessor<'a> {
self.program = program;
// println!("{:#?}", self.program);
println!("{:?}", self.program.last().unwrap());
// println!("{:?}", self.program.last().unwrap());
Ok(())
}
@ -318,47 +398,78 @@ impl<'a> Preprocessor<'a> {
}
if word.tok_typ != TokenType::Word {
lerror!(&word.loc, "Bad Function name, expected {} but found {}", TokenType::Word.human(), word.typ.human());
lerror!(&word.loc, "Bad {typ:?}, expected {} but found {}", TokenType::Word.human(), word.typ.human());
if crate::DEV_MODE {println!("{word:?}")}
return Err(eyre!(""));
}
let w = lookup_word(&word.text, &word.loc);
if w != OpType::Instruction(InstructionType::None) {
lerror!(&word.loc, "Bad {typ:?}, {typ:?} definition cannot be builtin word, got {:?}", word.text);
if crate::DEV_MODE {println!("{word:?}")}
return Err(eyre!(""));
}
let m = self.memories.get(&word.text);
if let Some(m) = m {
if typ != KeywordType::Memory {
lerror!(&word.loc, "{typ:?} cannot replace memory, got {}", word.text);
linfo!(&m.loc, "first definition here");
return Err(eyre!(""));
} else {
if typ == KeywordType::Memory {
lerror!(&word.loc, "Memories cannot be redefined, got {}", word.text);
linfo!(&m.loc, "first definition here");
if crate::DEV_MODE {println!("{word:?}")}
return Err(eyre!(""));
}
lerror!(&word.loc, "{typ:?} cannot replace memory, got {}", word.text);
linfo!(&m.loc, "first definition here");
if crate::DEV_MODE {println!("{word:?}")}
return Err(eyre!(""));
}
let f = self.functions.get(&word.text);
if let Some(f) = f {
if typ != KeywordType::Function {
lerror!(&word.loc, "{typ:?} cannot replace function, got {}", word.text);
linfo!(&f.loc, "first definition here");
return Err(eyre!(""));
} else {
if typ == KeywordType::Function {
lerror!(&word.loc, "Functions cannot be redefined, got {}", word.text);
linfo!(&f.loc, "first definition here");
if crate::DEV_MODE {println!("{word:?}")}
return Err(eyre!(""));
}
lerror!(&word.loc, "{typ:?} cannot replace function, got {}", word.text);
linfo!(&f.loc, "first definition here");
if crate::DEV_MODE {println!("{word:?}")}
return Err(eyre!(""));
}
let c = self.constants.get(&word.text);
if let Some(c) = c {
if typ != KeywordType::Constant {
lerror!(&word.loc, "{typ:?} cannot replace constant, got {}", word.text);
linfo!(&c.loc, "first definition here");
return Err(eyre!(""));
} else {
if typ == KeywordType::Constant {
lerror!(&word.loc, "Constants cannot be redefined, got {}", word.text);
linfo!(&c.loc, "first definition here");
if crate::DEV_MODE {println!("{word:?}")}
return Err(eyre!(""));
}
lerror!(&word.loc, "{typ:?} cannot replace constant, got {}", word.text);
linfo!(&c.loc, "first definition here");
if crate::DEV_MODE {println!("{word:?}")}
return Err(eyre!(""));
}
Ok(true)
}
pub fn set_functions(&mut self, f: Functions) {
self.functions = f;
}
pub fn set_constants(&mut self, f: Constants) {
self.constants = f;
}
pub fn set_memories(&mut self, f: Memories) {
self.memories = f;
}
pub fn get_functions(&mut self) -> Functions {
self.functions.clone()
}
pub fn get_constants(&mut self) -> Constants {
self.constants.clone()
}
pub fn get_memories(&mut self) -> Memories{
self.memories.clone()
}
}

View File

@ -1,54 +1,68 @@
use std::collections::HashMap;
use crate::{constants::{Operator, Types, OpType, KeywordType, InstructionType}, Args, lerror, warn, note};
use crate::{constants::{Operator, Types, OpType, KeywordType, InstructionType, Loc}, Args, lerror, warn};
use color_eyre::Result;
use eyre::eyre;
#[allow(dead_code)]
#[derive(Debug, Clone)]
struct Function {
pub struct Function {
loc: Loc,
args: Vec<Types>,
returns: Vec<Types>,
}
#[derive(Debug, Clone)]
pub struct Constant {
#[allow(dead_code)]
loc: Loc,
types: Vec<Types>,
}
impl Function {
#[allow(dead_code)]
pub fn default() -> Self {
Self {
args: Vec::new(),
returns: Vec::new(),
loc: (String::new(), 0, 0)
}
}
}
pub fn typecheck(ops: Vec<Operator>, args: &Args) -> Result<Vec<Operator>>{
type Functions = HashMap<String, Function>;
type Constants = HashMap<String, Constant>;
pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>, funcs: HashMap<String, Function>, consts: HashMap<String, Constant>) -> Result<(Vec<Types>, Functions, Constants)>{
if args.unsaf {
if !args.quiet {
warn!("Unsafe mode enabled, disabling typechecker, goodluck");
}
return Ok(ops.to_vec());
return Ok((Vec::new(), HashMap::new(), HashMap::new()));
}
let mut functions: HashMap<String, Function> = HashMap::new();
// let mut in_function: (String, Function) = (String::new(), Function::default());
let mut stack: Vec<Types> = Vec::new();
let mut functions: HashMap<String, Function> = funcs;
let mut constants: HashMap<String, Constant> = consts;
// let mut in_function: (String, Function, Loc) = (String::new(), Function::default(), (String::new(), 0, 0));
let mut stack: Vec<Types> = if let Some(i) = init_types {i} else {Vec::new()};
let mut stack_snapshots: Vec<Vec<Types>> = Vec::new();
let mut rtokens = ops.clone();
let mut rtokens = ops;
rtokens.reverse();
// println!("{:#?}", ops);
while !rtokens.is_empty() {
let op = rtokens.pop().unwrap();
println!("{:?}", stack.clone());
// println!("{:?}", stack.clone());
// println!("{:?}", op);
// println!("{}", ops.len());
match op.typ.clone() {
OpType::Keyword(keyword) => {
match keyword {
KeywordType::If => {
stack_pop(&mut stack, &op, &[Types::Bool])?;
},
KeywordType::If |
KeywordType::Do => {
stack_pop(&mut stack, &op, &[Types::Bool])?;
},
KeywordType::Function => {
KeywordType::FunctionDef => {
let name = op.text.clone();
if let Some(p) = rtokens.pop() {
@ -66,6 +80,7 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args) -> Result<Vec<Operator>>{
let mut func = Function {
args: Vec::new(),
returns: Vec::new(),
loc: op.loc
};
let mut return_args = false;
while p.as_ref().is_some() {
@ -73,17 +88,15 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args) -> Result<Vec<Operator>>{
if op.typ == OpType::Instruction(InstructionType::TypeBool) ||
op.typ == OpType::Instruction(InstructionType::TypeInt) ||
op.typ == OpType::Instruction(InstructionType::TypePtr) ||
op.typ == OpType::Instruction(InstructionType::TypeAny) ||
op.typ == OpType::Instruction(InstructionType::TypeVoid) {
let t = if op.typ == OpType::Instruction(InstructionType::TypeInt) {
Types::Int
} else
if op.typ == OpType::Instruction(InstructionType::TypeBool) {
} else if op.typ == OpType::Instruction(InstructionType::TypeBool) {
Types::Bool
} else
if op.typ == OpType::Instruction(InstructionType::TypePtr) {
} else if op.typ == OpType::Instruction(InstructionType::TypePtr) {
Types::Ptr
} else
if op.typ == OpType::Instruction(InstructionType::TypeVoid) {
} else if op.typ == OpType::Instruction(InstructionType::TypeVoid) {
if return_args {
func.returns = vec![Types::Void];
} else {
@ -92,11 +105,9 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args) -> Result<Vec<Operator>>{
continue;
}
Types::Void
} else
if op.typ == OpType::Instruction(InstructionType::TypeStr) {
} else if op.typ == OpType::Instruction(InstructionType::TypeStr) {
Types::Str
} else
if op.typ == OpType::Instruction(InstructionType::TypeAny) {
} else if op.typ == OpType::Instruction(InstructionType::TypeAny) {
Types::Any
} else {
panic!()
@ -113,17 +124,40 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args) -> Result<Vec<Operator>>{
return_args = true;
}
if op.typ == OpType::Keyword(KeywordType::FunctionDo) {
if op.typ == OpType::Keyword(KeywordType::FunctionThen) {
break;
}
p = rtokens.pop();
};
functions.insert(name.clone(), func.clone());
// if name == "main" {
// in_function = (name, func.clone());
// }
if func.args != vec![Types::Void] {
let mut code: Vec<Operator> = Vec::new();
while !rtokens.is_empty() {
let op = rtokens.pop().unwrap();
if op.typ == OpType::Keyword(KeywordType::FunctionDone) {
break;
}
code.push(op);
}
let ts = if func.args.clone() == vec![Types::Void] {
Vec::new()
} else {
func.args.clone()
};
if ts.contains(&Types::Void) {
continue;
}
functions.insert(name.clone(), func.clone());
let (ret_typs, _, _) = typecheck(code, args, Some(ts.clone()), functions.clone(), constants.clone())?;
if ret_typs != func.returns && !func.returns.contains(&Types::Void){
lerror!(&func.loc, "Expected {:?}, but got {:?}", func.returns, ret_typs);
return Err(eyre!(""))
}
if !func.args.contains(&Types::Void) {
stack.append(&mut func.args);
}
stack_snapshots.push(stack.clone());
@ -135,7 +169,14 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args) -> Result<Vec<Operator>>{
KeywordType::Include |
KeywordType::Constant |
KeywordType::Memory => (),
KeywordType::FunctionDo => (),
KeywordType::ConstantDef => {
// println!("defined constant");
constants.insert(op.text, Constant { loc: op.loc.clone(), types: vec![Types::Int] });
},
KeywordType::FunctionThen |
KeywordType::FunctionDone |
KeywordType::Function => unreachable!(),
}
},
OpType::Instruction(instruction) => {
@ -179,97 +220,41 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args) -> Result<Vec<Operator>>{
stack.push(a);
stack.push(b);
},
InstructionType::Minus => {
InstructionType::Minus |
InstructionType::Plus |
InstructionType::Band |
InstructionType::Bor |
InstructionType::Shr |
InstructionType::Shl |
InstructionType::Mul => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Int);
},
InstructionType::Plus => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Int);
},
InstructionType::Equals => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Bool);
},
InstructionType::Gt => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Bool);
},
InstructionType::Lt => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Bool);
},
InstructionType::Ge => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Bool);
},
InstructionType::Le => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Bool);
},
InstructionType::Equals |
InstructionType::Gt |
InstructionType::Lt |
InstructionType::Ge |
InstructionType::Le |
InstructionType::NotEquals => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Bool);
},
InstructionType::Band => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Int);
},
InstructionType::Bor => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Int);
},
InstructionType::Shr => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Int);
},
InstructionType::Shl => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Int);
},
InstructionType::DivMod => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Int);
stack.push(Types::Int);
},
InstructionType::Mul => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Int);
},
InstructionType::Load8 => {
stack_pop(&mut stack, &op, &[Types::Ptr])?;
stack.push(Types::Int);
},
InstructionType::Store8 => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Ptr])?;
},
InstructionType::Load32 => {
stack_pop(&mut stack, &op, &[Types::Ptr])?;
stack.push(Types::Int);
},
InstructionType::Store32 => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Ptr])?;
},
InstructionType::Load8 |
InstructionType::Load32 |
InstructionType::Load64 => {
stack_pop(&mut stack, &op, &[Types::Ptr])?;
stack.push(Types::Int);
},
InstructionType::Store8 |
InstructionType::Store32 |
InstructionType::Store64 => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Ptr])?;
@ -345,7 +330,9 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args) -> Result<Vec<Operator>>{
InstructionType::FnCall => {
stack_snapshots.push(stack.clone());
let f = functions.get(&op.text).unwrap();
let f = functions.get(&op.text).unwrap().clone();
// in_function = (op.text.clone(), f.clone(), op.loc.clone());
let mut s = stack.clone();
let mut a = f.args.clone();
@ -366,24 +353,8 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args) -> Result<Vec<Operator>>{
}
InstructionType::Return => {
let snap = stack_snapshots.pop().unwrap();
// snap.append(&mut f.returns.clone());
let mut st = stack.clone();
for s in snap{
if let Some(sn) = st.pop(){
if s != sn {
lerror!(&op.loc, "Expected {:?}, but got {:?}", s, sn);
return Err(eyre!(""));
}
} else {
lerror!(&op.loc, "Expected {:?}, but got nothing", s);
return Err(eyre!(""));
}
}
}
InstructionType::None => {},
InstructionType::Return |
InstructionType::None |
InstructionType::TypeBool |
InstructionType::TypePtr |
InstructionType::TypeInt |
@ -392,7 +363,11 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args) -> Result<Vec<Operator>>{
InstructionType::TypeStr |
InstructionType::Returns |
InstructionType::With => (),
InstructionType::ConstUse => todo!(),
InstructionType::ConstUse => {
// println!("{constants:?}");
let mut c = constants.get(&op.text).unwrap().clone();
stack.append(&mut c.types);
},
}
},
@ -401,7 +376,7 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args) -> Result<Vec<Operator>>{
}
Ok(ops.clone())
Ok((stack, functions, constants))
}

View File

@ -66,6 +66,21 @@ pub mod logger {
pub fn lnote<P: Deref<Target = Loc>>(loc: P, msg: &str) {
println!("{f}:{r}:{c} {blue}note{rs}: {msg}", blue=color::FG_BLUE, rs=color::RESET, f=loc.0, r=loc.1, c=loc.2);
}
pub fn help(msg: &str) {
println!("{blue}help{r}: {msg}", blue=color::FG_CYAN, r=color::RESET);
}
pub fn code_block(code: &str) -> String {
let mut ret = String::new();
let lines = code.lines();
for (i, line) in lines.enumerate() {
use std::fmt::Write;
writeln!(ret, "{}{} | {}{}",color::FG_BLUE, i + 1, line, color::RESET).unwrap();
}
ret
}
pub mod macros {
#[macro_export] macro_rules! error { ($($arg:tt)*) => { $crate::util::logger::error(std::format_args!($($arg)*).to_string().as_str()) }; }
#[macro_export] macro_rules! warn { ($($arg:tt)*) => { $crate::util::logger::warn( std::format_args!($($arg)*).to_string().as_str()) }; }
@ -76,6 +91,9 @@ pub mod logger {
#[macro_export] macro_rules! lwarn { ($dst:expr, $($arg:tt)*) => { $crate::util::logger::lwarn($dst, std::format_args!($($arg)*).to_string().as_str()) }; }
#[macro_export] macro_rules! linfo { ($dst:expr, $($arg:tt)*) => { $crate::util::logger::linfo($dst, std::format_args!($($arg)*).to_string().as_str()) }; }
#[macro_export] macro_rules! lnote { ($dst:expr, $($arg:tt)*) => { $crate::util::logger::lnote($dst, std::format_args!($($arg)*).to_string().as_str()) }; }
#[macro_export] macro_rules! help { ($($arg:tt)*) => { $crate::util::logger::help( std::format_args!($($arg)*).to_string().as_str()) }; }
#[macro_export] macro_rules! code_block { ($($arg:tt)*) => { $crate::util::logger::code_block( std::format_args!($($arg)*).to_string().as_str()) }; }
}
}

View File

@ -1,5 +1,5 @@
include "std.mcl"
fn main with void returns void do
69 putd
end
fn main with void returns void then
"hi\n" puts
done