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 false 0 end
const true 1 end const true 1 end
fn div with int int returns int int divmod drop end fn div with int int returns int then divmod drop done
fn mod with int int returns int int divmod swap drop end fn mod with int int returns int then divmod swap drop done
fn / with int int returns int int div end
fn % with int int returns int int mod end
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(u64) 8 end
const sizeof(u32) 4 end const sizeof(u32) 4 end

View File

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

View File

@ -5,11 +5,11 @@
// @arg str_len: Int // @arg str_len: Int
// @arg str_ptr: Ptr // @arg str_ptr: Ptr
// @ret NULL/NEVER // @ret NULL/NEVER
fn assert with bool int ptr returns null do fn assert with bool int ptr returns void then
rot rot
if else if else
"Assert failed: \"" eputs eputs "Assert failed: \"" eputs eputs
"\". Exiting!\n" eputs "\". Exiting!\n" eputs
1 exit 1 exit
end 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 crate::{constants::{Operator, OpType, KeywordType}, Args};
use color_eyre::Result; use color_eyre::Result;
use crate::compile::commands::linux_x86_64_compile_and_link; use crate::compile::commands::linux_x86_64_compile_and_link;
use crate::constants::InstructionType; 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>{ 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_c = PathBuf::from(&args.out_file);
let (mut of_o, mut of_a) = if args.out_file == *crate::DEFAULT_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"); of_a.set_extension("nasm");
let file = fs::File::create(&of_a)?; let file = fs::File::create(&of_a)?;
let mut writer = BufWriter::new(&file); let mut writer = BufWriter::new(&file);
let mut memories: Vec<(usize, usize)> = Vec::new(); let mut memories: Vec<Memory> = Vec::new();
let mut constants: Vec<(String, Option<usize>, Option<String>)> = Vec::new(); let mut constants: HashMap<String, Constant> = HashMap::new();
let mut functions: Vec<Function> = Vec::new();
// println!("{}", tokens.len()); // println!("{}", tokens.len());
let mut strings: Vec<String> = Vec::new(); let mut strings: Vec<String> = Vec::new();
writeln!(writer, "BITS 64")?; writeln!(writer, "BITS 64")?;
writeln!(writer, "segment .text")?; writeln!(writer, "segment .text")?;
writeln!(writer, "print:")?; writeln!(writer, "_dbg_print:")?;
writeln!(writer, " mov r9, -3689348814741910323")?; writeln!(writer, " mov r9, -3689348814741910323")?;
writeln!(writer, " sub rsp, 40")?; writeln!(writer, " sub rsp, 40")?;
writeln!(writer, " mov BYTE [rsp+31], 10")?; 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, "global _start")?;
writeln!(writer, "_start:")?; writeln!(writer, "_start:")?;
writeln!(writer, " lea rbp, [rel ret_stack]")?; writeln!(writer, " lea rbp, [rel ret_stack]")?;
writeln!(writer, " call func_main")?; writeln!(writer, " call main")?;
writeln!(writer, " jmp end")?; writeln!(writer, " jmp end")?;
@ -85,36 +86,30 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
writeln!(writer, "addr_{ti}:")?; writeln!(writer, "addr_{ti}:")?;
if token.typ == OpType::Instruction(InstructionType::PushInt) { if token.typ == OpType::Instruction(InstructionType::PushInt) {
writeln!(writer, " ;; -- {:?} {}", token.typ, token.value)?; writeln!(writer, " ;; -- {:?} {}", token.typ, token.value)?;
} else } else if token.typ == OpType::Instruction(InstructionType::PushStr) {
if token.typ == OpType::Instruction(InstructionType::PushStr) { writeln!(writer, " ;; -- {:?} {}", token.typ, token.text.escape_debug())?;
writeln!(writer, " ;; -- {:?} {}", token.typ, strings[token.value].escape_debug())?;
} else { } else {
writeln!(writer, " ;; -- {:?}", token.typ)?; writeln!(writer, " ;; -- {:?}", token.typ)?;
} }
} else { } else {
if ti != 0{
if &tokens[ti-1].typ == &OpType::Keyword(KeywordType::Else) || if ti != 0 && tokens[ti-1].typ == OpType::Keyword(KeywordType::Else) ||
&tokens[ti-1].typ == &OpType::Keyword(KeywordType::End){ 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) {
writeln!(writer, "addr_{ti}:")?; writeln!(writer, "addr_{ti}:")?;
} }
match &token.typ { if ti + 1 < tokens.len() && tokens[ti+1].typ == OpType::Keyword(KeywordType::End) {
OpType::Keyword(keyword) => { writeln!(writer, "addr_{ti}:")?;
match keyword { }
&KeywordType::End |
&KeywordType::While => { if let OpType::Keyword(keyword) = &token.typ {
writeln!(writer, "addr_{ti}:")?; 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 => { InstructionType::Print => {
writeln!(writer, " pop rdi")?; writeln!(writer, " pop rdi")?;
writeln!(writer, " call print")?; writeln!(writer, " call _dbg_print")?;
ti += 1; ti += 1;
}, },
@ -412,11 +407,11 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
ti += 1; ti += 1;
}, },
InstructionType::None => { InstructionType::None => {
println!("{:?}", token); println!("{token:?}");
unreachable!() unreachable!()
}, },
InstructionType::FnCall => { InstructionType::FnCall => {
writeln!(writer, " call func_{}", token.text)?; writeln!(writer, " call {}", token.text)?;
ti += 1; ti += 1;
}, },
InstructionType::Return => { InstructionType::Return => {
@ -426,45 +421,28 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
writeln!(writer, " ret")?; writeln!(writer, " ret")?;
ti += 1; ti += 1;
}, },
InstructionType::CastBool => { InstructionType::CastBool |
ti += 1; InstructionType::CastPtr |
} InstructionType::CastInt |
InstructionType::CastPtr => { InstructionType::CastVoid |
ti += 1; InstructionType::TypeBool |
} InstructionType::TypePtr |
InstructionType::CastInt => { InstructionType::TypeInt |
ti += 1; InstructionType::TypeVoid |
} InstructionType::TypeStr |
InstructionType::CastVoid => { InstructionType::TypeAny |
ti += 1; InstructionType::Returns |
}
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::With => { InstructionType::With => {
ti += 1; ti += 1;
} }
InstructionType::ConstUse => { InstructionType::ConstUse => {
writeln!(writer, " mov rax, qword [const_{}]", token.text)?; writeln!(writer, " mov rax, qword [const_{}]", token.text)?;
writeln!(writer, " push rax")?; 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; ti += 1;
}, },
} }
@ -475,12 +453,13 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
match keyword { match keyword {
// block // block
KeywordType::If => { KeywordType::If |
KeywordType::Do => {
writeln!(writer, " pop rax")?; writeln!(writer, " pop rax")?;
writeln!(writer, " test rax, rax")?; writeln!(writer, " test rax, rax")?;
writeln!(writer, " jz addr_{}", token.jmp)?; writeln!(writer, " jz addr_{}", token.jmp)?;
ti += 1; ti += 1;
}, }
KeywordType::Else => { KeywordType::Else => {
writeln!(writer, " jmp addr_{}", token.jmp)?; writeln!(writer, " jmp addr_{}", token.jmp)?;
ti += 1; ti += 1;
@ -488,12 +467,6 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
KeywordType::While => { KeywordType::While => {
ti += 1; ti += 1;
} }
KeywordType::Do => {
writeln!(writer, " pop rax")?;
writeln!(writer, " test rax, rax")?;
writeln!(writer, " jz addr_{}", token.jmp)?;
ti += 1;
}
KeywordType::End => { KeywordType::End => {
if ti + 1 != token.jmp { if ti + 1 != token.jmp {
// writeln!(writer, " jmp addr_{}", token.jmp)?; // writeln!(writer, " jmp addr_{}", token.jmp)?;
@ -501,23 +474,42 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
ti += 1; ti += 1;
}, },
KeywordType::Memory => { 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; ti += 1;
} }
KeywordType::Include => unreachable!(), KeywordType::ConstantDef => {
KeywordType::Constant => {
// TODO: after we add c style strings add supoort for them in constants // 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; ti += 1;
}, },
KeywordType::Function => { KeywordType::FunctionDef => {
writeln!(writer, "func_{}:", token.text)?; writeln!(writer, "{}:", token.text)?;
writeln!(writer, " pop rbx")?; writeln!(writer, " pop rbx")?;
writeln!(writer, " mov qword [rbp], rbx")?; writeln!(writer, " mov qword [rbp], rbx")?;
writeln!(writer, " add rbp, 8")?; writeln!(writer, " add rbp, 8")?;
functions.push(Function { loc: token.loc.clone(), name: token.text.clone() });
ti += 1; 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())?; writeln!(writer, " str_{}: db {} ; {}", i, s_list, s.escape_default())?;
} }
for (_, s) in constants.iter().enumerate() { for (_, c) in constants {
if let Some(v) = &s.1 { if !c.used {
writeln!(writer, " const_{}: dq {}", s.0, v)?; continue;
} else if let Some(_v) = &s.2 { }
if let Some(v) = &c.value_i {
writeln!(writer, " const_{}: dq {}", c.name, v)?;
} else if let Some(_v) = &c.value_s {
todo!(); todo!();
} else { } else {
unreachable!(); unreachable!();
@ -547,8 +543,8 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
writeln!(writer, "segment .bss")?; writeln!(writer, "segment .bss")?;
for (_, s) in memories.iter().enumerate() { for s in memories {
writeln!(writer, " mem_{}: resb {}", s.0, s.1)?; writeln!(writer, " mem_{}: resb {}", s.id, s.size)?;
} }
writeln!(writer, " ret_stack: resq 256")?; writeln!(writer, " ret_stack: resq 256")?;
@ -557,6 +553,13 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
// } // }
writer.flush()?; 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)?; linux_x86_64_compile_and_link(&of_a, &of_o, &of_c, args.quiet)?;
if args.run { if args.run {
let c = linux_x86_64_run(&of_c, &[], args.quiet)?; 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) 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 linux_x86_64;
pub mod commands; pub mod commands;
pub const MEM_SZ: usize = 640 * 1000; // 4kb #[derive(Debug, Clone)]
pub const STRING_SZ: usize = 640 * 1000; // 4kb 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 None // Used for macros and any other non built in word definitions
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum KeywordType { pub enum KeywordType {
If, If,
Else, Else,
@ -81,8 +81,11 @@ pub enum KeywordType {
Include, Include,
Memory, Memory,
Constant, Constant,
ConstantDef,
Function, Function,
FunctionDo FunctionDef,
FunctionThen,
FunctionDone
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -129,7 +132,7 @@ impl OpType {
InstructionType::PushInt => "Number", InstructionType::PushInt => "Number",
InstructionType::PushStr => "String", InstructionType::PushStr => "String",
InstructionType::Print => "print", InstructionType::Print => "_dbg_print",
InstructionType::Dup => "dup", InstructionType::Dup => "dup",
InstructionType::Drop => "drop", InstructionType::Drop => "drop",
InstructionType::Rot => "rot", InstructionType::Rot => "rot",
@ -192,7 +195,10 @@ impl OpType {
KeywordType::Memory => "memory", KeywordType::Memory => "memory",
KeywordType::Function => "fn", KeywordType::Function => "fn",
KeywordType::Constant => "const", 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 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 crate::util::logger;
use color_eyre::Result; use color_eyre::Result;
use eyre::eyre; use eyre::eyre;
use super::{Memory, Function, Constant};
mod syscalls; mod syscalls;
fn stack_pop(stack: &mut Vec<usize>, pos: &Loc) -> Result<usize> { 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 stack: Vec<usize> = Vec::new();
let mut ti = 0; let mut mem: Vec<u64> = vec![0; crate::MEM_SZ + crate::STRING_SZ];
let mut mem: Vec<u64> = vec![0; crate::compile::MEM_SZ + crate::compile::STRING_SZ];
let mut string_idx = 0; 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 { // for token in &tokens {
// println!("{{typ: \"{:?}\", val: {}, jmp: {}}}", token.typ, token.value, token.jmp); // println!("{{typ: \"{:?}\", val: {}, jmp: {}}}", token.typ, token.value, token.jmp);
// } // }
while ti < tokens.len() {
let token = &tokens[ti]; // jump to main func
let pos = token.loc.clone(); let mut ip = if let Some(i) = functions.get("main") {i.id} else {
// println!("{:?}", token.typ); crate::errors::missing_main_fn();
match token.typ.clone() { return Err(eyre!(""));
};
while ip < ops.len() {
let op = &ops[ip];
let pos = op.loc.clone();
match op.typ.clone() {
OpType::Instruction(instruction) => { OpType::Instruction(instruction) => {
match instruction { match instruction {
InstructionType::PushInt => { InstructionType::PushInt => {
stack.push(token.value); stack.push(op.value);
ti += 1; ip += 1;
}, },
InstructionType::PushStr => { InstructionType::PushStr => {
if token.addr.is_none() { if op.addr.is_none() {
stack.push(token.text.len()); // string len stack.push(op.text.len()); // string len
stack.push(string_idx + crate::compile::MEM_SZ); stack.push(string_idx + crate::MEM_SZ);
for c in token.text.bytes() { for c in op.text.bytes() {
mem[crate::compile::MEM_SZ + string_idx] = c as u64; mem[crate::MEM_SZ + string_idx] = u64::from(c);
string_idx += 1; string_idx += 1;
} }
} else { } else {
stack.push(token.text.len()); stack.push(op.text.len());
if let Some(addr) = token.addr { if let Some(addr) = op.addr {
stack.push(addr); stack.push(addr);
} }
} }
ti += 1; ip += 1;
}, },
InstructionType::Drop => { InstructionType::Drop => {
stack.pop(); stack.pop();
ti += 1; ip += 1;
}, },
InstructionType::Dup => { InstructionType::Dup => {
let a = stack_pop(&mut stack, &pos)?; let a = stack_pop(&mut stack, &pos)?;
stack.push(a); stack.push(a);
stack.push(a); stack.push(a);
ti += 1; ip += 1;
}, },
InstructionType::Rot => { InstructionType::Rot => {
@ -72,14 +84,14 @@ pub fn run(tokens: &[crate::constants::Operator]) -> Result<i32>{
stack.push(b); stack.push(b);
stack.push(a); stack.push(a);
stack.push(c); stack.push(c);
ti += 1; ip += 1;
} }
InstructionType::Swap => { InstructionType::Swap => {
let a = stack_pop(&mut stack, &pos)?; let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?; let b = stack_pop(&mut stack, &pos)?;
stack.push(a); stack.push(a);
stack.push(b); stack.push(b);
ti += 1; ip += 1;
} }
InstructionType::Over => { InstructionType::Over => {
let a = stack_pop(&mut stack, &pos)?; 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(b);
stack.push(a); stack.push(a);
stack.push(b); stack.push(b);
ti += 1; ip += 1;
} }
InstructionType::Print => { InstructionType::Print => {
let a = stack_pop(&mut stack, &pos)?; let a = stack_pop(&mut stack, &pos)?;
println!("{a}"); println!("{a}");
// let _ = io::stdout().flush(); // let _ = io::stdout().flush();
ti += 1; ip += 1;
}, },
#[allow(clippy::cast_possible_truncation)]
InstructionType::Load8 | InstructionType::Load8 |
InstructionType::Load32 | InstructionType::Load32 |
InstructionType::Load64 => { InstructionType::Load64 => {
let a = stack_pop(&mut stack, &pos)?; let a = stack_pop(&mut stack, &pos)?;
if a > crate::compile::MEM_SZ { if a > crate::MEM_SZ {
lerror!(&token.loc, "Invalid memory address {a}"); lerror!(&op.loc, "Invalid memory address {a}");
return Ok(1); return Ok(1);
} }
let byte = mem[a]; let byte = mem[a];
stack.push(byte as usize); stack.push(byte as usize);
ti += 1; ip += 1;
} }
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
InstructionType::Store8 => { InstructionType::Store8 => {
let val = stack_pop(&mut stack, &pos)?; let val = stack_pop(&mut stack, &pos)?;
let addr = stack_pop(&mut stack, &pos)?; let addr = stack_pop(&mut stack, &pos)?;
if addr > crate::compile::MEM_SZ { if addr > crate::MEM_SZ {
lerror!(&token.loc, "Invalid memory address {addr}"); lerror!(&op.loc, "Invalid memory address {addr}");
return Ok(1); return Ok(1);
} }
mem[addr] = val as u8 as u64; mem[addr] = u64::from(val as u8);
ti += 1; ip += 1;
} }
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
InstructionType::Store32 => { InstructionType::Store32 => {
let val = stack_pop(&mut stack, &pos)?; let val = stack_pop(&mut stack, &pos)?;
let addr = stack_pop(&mut stack, &pos)?; let addr = stack_pop(&mut stack, &pos)?;
if addr > crate::compile::MEM_SZ { if addr > crate::MEM_SZ {
lerror!(&token.loc, "Invalid memory address {addr}"); lerror!(&op.loc, "Invalid memory address {addr}");
return Ok(1); return Ok(1);
} }
mem[addr] = val as u32 as u64; mem[addr] = u64::from(val as u32);
ti += 1; ip += 1;
} }
#[allow(clippy::cast_possible_truncation)] #[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 val = stack_pop(&mut stack, &pos)?;
let addr = stack_pop(&mut stack, &pos)?; let addr = stack_pop(&mut stack, &pos)?;
if addr > crate::compile::MEM_SZ { if addr > crate::MEM_SZ {
lerror!(&token.loc, "Invalid memory address {addr}"); lerror!(&op.loc, "Invalid memory address {addr}");
return Ok(1); return Ok(1);
} }
mem[addr] = val as u64; mem[addr] = val as u64;
ti += 1; ip += 1;
} }
// math // math
@ -154,77 +167,77 @@ pub fn run(tokens: &[crate::constants::Operator]) -> Result<i32>{
let a = stack_pop(&mut stack, &pos)?; let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?; let b = stack_pop(&mut stack, &pos)?;
stack.push(b + a); stack.push(b + a);
ti += 1; ip += 1;
}, },
InstructionType::Minus => { InstructionType::Minus => {
let a = stack_pop(&mut stack, &pos)?; let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?; let b = stack_pop(&mut stack, &pos)?;
stack.push(b - a); stack.push(b - a);
ti += 1; ip += 1;
}, },
InstructionType::Equals => { InstructionType::Equals => {
let a = stack_pop(&mut stack, &pos)?; let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?; let b = stack_pop(&mut stack, &pos)?;
stack.push(usize::from(b == a)); stack.push(usize::from(b == a));
ti += 1; ip += 1;
}, },
InstructionType::Gt => { InstructionType::Gt => {
let a = stack_pop(&mut stack, &pos)?; let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?; let b = stack_pop(&mut stack, &pos)?;
stack.push(usize::from(b > a)); stack.push(usize::from(b > a));
ti += 1; ip += 1;
}, },
InstructionType::Lt => { InstructionType::Lt => {
let a = stack_pop(&mut stack, &pos)?; let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?; let b = stack_pop(&mut stack, &pos)?;
stack.push(usize::from(b < a)); stack.push(usize::from(b < a));
ti += 1; ip += 1;
}, },
InstructionType::NotEquals => { InstructionType::NotEquals => {
let a = stack_pop(&mut stack, &pos)?; let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?; let b = stack_pop(&mut stack, &pos)?;
stack.push(usize::from(b != a)); stack.push(usize::from(b != a));
ti += 1; ip += 1;
}, },
InstructionType::Ge => { InstructionType::Ge => {
let a = stack_pop(&mut stack, &pos)?; let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?; let b = stack_pop(&mut stack, &pos)?;
stack.push(usize::from(b >= a)); stack.push(usize::from(b >= a));
ti += 1; ip += 1;
}, },
InstructionType::Le => { InstructionType::Le => {
let a = stack_pop(&mut stack, &pos)?; let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?; let b = stack_pop(&mut stack, &pos)?;
stack.push(usize::from(b <= a)); stack.push(usize::from(b <= a));
ti += 1; ip += 1;
}, },
InstructionType::Band => { InstructionType::Band => {
let a = stack_pop(&mut stack, &pos)?; let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?; let b = stack_pop(&mut stack, &pos)?;
stack.push(a & b); stack.push(a & b);
ti += 1; ip += 1;
} }
InstructionType::Bor => { InstructionType::Bor => {
let a = stack_pop(&mut stack, &pos)?; let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?; let b = stack_pop(&mut stack, &pos)?;
stack.push(a | b); stack.push(a | b);
ti += 1; ip += 1;
} }
InstructionType::Shr => { InstructionType::Shr => {
let a = stack_pop(&mut stack, &pos)?; let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?; let b = stack_pop(&mut stack, &pos)?;
stack.push(b >> a); stack.push(b >> a);
ti += 1; ip += 1;
} }
InstructionType::Shl => { InstructionType::Shl => {
let a = stack_pop(&mut stack, &pos)?; let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?; let b = stack_pop(&mut stack, &pos)?;
stack.push(b << a); stack.push(b << a);
ti += 1; ip += 1;
} }
InstructionType::DivMod => { InstructionType::DivMod => {
@ -232,13 +245,13 @@ pub fn run(tokens: &[crate::constants::Operator]) -> Result<i32>{
let b = stack_pop(&mut stack, &pos)?; let b = stack_pop(&mut stack, &pos)?;
stack.push(b / a); stack.push(b / a);
stack.push(b % a); stack.push(b % a);
ti += 1; ip += 1;
} }
InstructionType::Mul => { InstructionType::Mul => {
let a = stack_pop(&mut stack, &pos)?; let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?; let b = stack_pop(&mut stack, &pos)?;
stack.push(b * a); stack.push(b * a);
ti += 1; ip += 1;
} }
InstructionType::Syscall0 => { InstructionType::Syscall0 => {
todo!(); todo!();
@ -268,7 +281,7 @@ pub fn run(tokens: &[crate::constants::Operator]) -> Result<i32>{
}; };
stack.push(ret); stack.push(ret);
// println!("{}", stack.len()); // println!("{}", stack.len());
ti += 1; ip += 1;
}, },
InstructionType::Syscall4 => { InstructionType::Syscall4 => {
todo!(); todo!();
@ -284,15 +297,32 @@ pub fn run(tokens: &[crate::constants::Operator]) -> Result<i32>{
}, },
InstructionType::MemUse => { InstructionType::MemUse => {
let m = memories.get(&token.addr.unwrap()).unwrap(); let m = memories.get(&op.addr.unwrap()).unwrap();
stack.push(*m); stack.push(m.id);
ti += 1; 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::CastBool |
InstructionType::CastPtr | InstructionType::CastPtr |
InstructionType::CastInt | InstructionType::CastInt |
InstructionType::FnCall |
InstructionType::Return |
InstructionType::CastVoid | InstructionType::CastVoid |
InstructionType::TypeBool | InstructionType::TypeBool |
InstructionType::TypePtr | InstructionType::TypePtr |
@ -301,9 +331,8 @@ pub fn run(tokens: &[crate::constants::Operator]) -> Result<i32>{
InstructionType::TypeStr | InstructionType::TypeStr |
InstructionType::TypeAny | InstructionType::TypeAny |
InstructionType::Returns | InstructionType::Returns |
InstructionType::With => ti += 1, InstructionType::With => ip += 1,
InstructionType::None => unreachable!(), 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)?; let a = stack_pop(&mut stack, &pos)?;
if a == 0 { if a == 0 {
// println!("If({ti}) => t: {:?} j: {}", tokens[token.jmp as usize].typ, token.jmp); // println!("If({ti}) => t: {:?} j: {}", tokens[token.jmp as usize].typ, token.jmp);
ti = token.jmp; ip = op.jmp;
} else { } else {
ti += 1; ip += 1;
} }
}, },
KeywordType::Else | KeywordType::End => { KeywordType::Else | KeywordType::End => {
ti = token.jmp; ip = op.jmp;
}
KeywordType::While => {
ti += 1;
} }
KeywordType::Do => { KeywordType::Do => {
let a = stack.pop().unwrap(); let a = stack.pop().unwrap();
if a == 0 { if a == 0 {
ti = token.jmp; ip = op.jmp;
} else { } else {
ti += 1; ip += 1;
} }
} }
KeywordType::Memory => { KeywordType::While | //* exept this one, this one should just skip over
memories.insert(token.addr.unwrap(), token.value); KeywordType::Memory |
ti += 1; 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::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) 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 { pub fn sys_write(sys_n: usize, fd: usize, buff: usize, count: usize, mem: &Vec<u64> ) -> usize {
let mem = (*mem).clone(); let mem = (*mem).clone();
// println!("{:?}", &mem[buff..(buff + count)]); // println!("{:?}", &mem[buff..(buff + count)]);

View File

@ -1 +1,27 @@
use crate::constants::Loc;
pub mod linux_x86_64; 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 crate::{constants::{Token, TokenType}, Args};
use color_eyre::Result;
fn lex_word(s: String, tok_type: TokenType) -> (TokenType, String) { fn lex_word(s: String, tok_type: TokenType) -> (TokenType, String) {
match s { match s {
@ -89,7 +88,7 @@ fn lex_line(text: &str) -> Vec<(usize, String, TokenType)> {
tokens 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 let lines: Vec<(usize, &str)> = code
.split(['\n', '\r']) .split(['\n', '\r'])
.enumerate() .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 { for (col, tok, tok_type) in lt {
let (tok_type, tok) = lex_word(tok, tok_type); let (tok_type, tok) = lex_word(tok, tok_type);
let t = Token{ let t = Token{
file: file.into(), file: file.to_string(),
line: row + 1, line: row + 1,
col, col,
text: tok, 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 constants;
mod interpret; mod interpret;
mod util; mod util;
@ -7,18 +9,14 @@ mod lexer;
mod preprocessor; mod preprocessor;
mod typechecker; mod typechecker;
mod precompiler; mod precompiler;
mod config;
use std::fs; mod errors;
use config::*;
use std::{fs, collections::HashMap};
use clap::Parser; use clap::Parser;
use color_eyre::Result;
pub const DEFAULT_OUT_FILE: &str = "a.out"; use eyre::eyre;
pub const DEFAULT_INCLUDES: [&str;2] = [
"./include",
"~/.mclang/include",
];
#[derive(Parser, Debug, Clone)] #[derive(Parser, Debug, Clone)]
#[command(author, version, about, long_about = None)] #[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 args = Args::parse();
let Ok(code) = fs::read_to_string(&args.in_file) else { let Ok(code) = fs::read_to_string(&args.in_file) else {
error!("Failed to read file {}, exiting!", &args.in_file); error!("Failed to read file {}, exiting!", &args.in_file);
return; return Ok(());
};
let Ok(tokens) = lexer::lex(&code, &args.in_file, &args) else {
error!("Lexing failed, exiting!");
return;
}; };
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 { let mut parser = parser::Parser::new(tokens, &args, None);
error!("Parsing failed, exiting!"); let tokens = match parser.parse(){
return; 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 { match typechecker::typecheck(tokens.clone(), &args, None, HashMap::new(), HashMap::new()) {
error!("Typechecking failed, exiting!"); Ok(_) => (),
return; Err(e) => {
error!("Typechecking failed, exiting!");
if crate::DEV_MODE {
return Err(e);
}
return Ok(());
}
}; };
let c = if args.compile && args.interpret { 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() { for ip in 0..program.len() {
let op = &program.clone()[ip]; let op = &program.clone()[ip];
// println!("{op:?}");
match op.typ { match op.typ {
OpType::Keyword(KeywordType::If) | // OpType::Keyword(KeywordType::FunctionDef) |
OpType::Keyword(KeywordType::Function) | OpType::Keyword(KeywordType::If | KeywordType::While) => {
OpType::Keyword(KeywordType::While) => {
stack.push(ip); stack.push(ip);
} }
OpType::Keyword(KeywordType::Else) => { 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"); lerror!(&op.loc, "Unclosed-if else block");
return Err(eyre!("Cross referencing")); return Err(eyre!("Cross referencing"));
}; };
@ -29,14 +29,13 @@ pub fn cross_ref(mut program: Vec<Operator>) -> Result<Vec<Operator>> {
stack.push(ip); stack.push(ip);
}, },
OpType::Keyword(KeywordType::End) => { 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"); lerror!(&op.loc, "Unclosed if, if-else, while-do, function, memory, or constant");
return Err(eyre!("Cross referencing")); return Err(eyre!("Cross referencing"));
}; };
match &program[block_ip].typ { match &program[block_ip].typ {
OpType::Keyword(KeywordType::If) | OpType::Keyword(KeywordType::If | KeywordType::Else) => {
OpType::Keyword(KeywordType::Else) => {
program[block_ip].jmp = ip; program[block_ip].jmp = ip;
program[ip].jmp = ip + 1; 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[ip].jmp = program[block_ip].jmp;
program[block_ip].jmp = ip + 1; program[block_ip].jmp = ip + 1;
} }
OpType::Keyword(KeywordType::FunctionDo) => { OpType::Keyword(KeywordType::FunctionThen) => {
program[ip].typ = OpType::Instruction(InstructionType::Return); program[ip].typ = OpType::Instruction(InstructionType::Return);
} }
OpType::Keyword(KeywordType::Memory) | OpType::Keyword(KeywordType::Memory | KeywordType::Constant) => (),
OpType::Keyword(KeywordType::Function) |
OpType::Keyword(KeywordType::Constant) => (),
a => { a => {
println!("{a:?}"); println!("{a:?}");
@ -61,15 +58,11 @@ pub fn cross_ref(mut program: Vec<Operator>) -> Result<Vec<Operator>> {
} }
OpType::Keyword(KeywordType::Do) => { 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"); lerror!(&op.loc, "Unclosed while-do block");
return Err(eyre!("Cross referencing")); 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; program[ip].jmp = block_ip;
stack.push(ip); stack.push(ip);
} }
@ -78,7 +71,7 @@ pub fn cross_ref(mut program: Vec<Operator>) -> Result<Vec<Operator>> {
} }
if !stack.is_empty() { 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()); lerror!(&program[stack.pop().expect("Empy stack")].clone().loc,"Unclosed block, {:?}", program[stack.pop().expect("Empy stack")].clone());
return Err(eyre!("Unclosed block")); return Err(eyre!("Unclosed block"));
} }
@ -86,18 +79,27 @@ pub fn cross_ref(mut program: Vec<Operator>) -> Result<Vec<Operator>> {
Ok(program.clone()) Ok(program.clone())
} }
pub struct Parser { pub struct Parser<'a> {
tokens: Vec<Token> tokens: Vec<Token>,
pub preprocessor: Preprocessor<'a>,
#[allow(dead_code)]
args: &'a Args
} }
impl Parser { impl<'a> Parser<'a> {
pub fn new(file: Vec<Token>) -> Self { 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{ 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(); let mut tokens = Vec::new();
for token in &self.tokens { for token in &self.tokens {
@ -134,9 +136,9 @@ impl Parser {
} }
self.preprocessor.program = tokens;
let t = Preprocessor::new(tokens.clone(), args).preprocess()?.get_ops(); let t = self.preprocessor.preprocess()?.get_ops();
let t = cross_ref(t.clone())?; let t = cross_ref(t)?;
Ok(t) Ok(t)
} }
@ -145,12 +147,12 @@ impl Parser {
pub fn lookup_word<P: Deref<Target = Loc>>(s: &str, _pos: P) -> OpType { pub fn lookup_word<P: Deref<Target = Loc>>(s: &str, _pos: P) -> OpType {
let n = s.parse::<usize>(); let n = s.parse::<usize>();
if let Ok(_) = n { if n.is_ok() {
return OpType::Instruction(InstructionType::PushInt); return OpType::Instruction(InstructionType::PushInt);
} }
match s { match s {
//stack //stack
"print" => OpType::Instruction(InstructionType::Print), "_dbg_print" => OpType::Instruction(InstructionType::Print),
"dup" => OpType::Instruction(InstructionType::Dup), "dup" => OpType::Instruction(InstructionType::Dup),
"drop" => OpType::Instruction(InstructionType::Drop), "drop" => OpType::Instruction(InstructionType::Drop),
"rot" => OpType::Instruction(InstructionType::Rot), "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), "memory" => OpType::Keyword(KeywordType::Memory),
"const" => OpType::Keyword(KeywordType::Constant), "const" => OpType::Keyword(KeywordType::Constant),
"fn" => OpType::Keyword(KeywordType::Function), "fn" => OpType::Keyword(KeywordType::Function),
"then" => OpType::Keyword(KeywordType::FunctionThen),
"done" => OpType::Keyword(KeywordType::FunctionDone),
"return" => OpType::Instruction(InstructionType::Return), "return" => OpType::Instruction(InstructionType::Return),
"returns" => OpType::Instruction(InstructionType::Returns), "returns" => OpType::Instruction(InstructionType::Returns),
"bool" => OpType::Instruction(InstructionType::TypeBool), "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); lerror!(&token.loc, "Unsupported precompiler keyword {:?}", token.typ);
dbg!(tokens); dbg!(tokens);
return Err(eyre!("")); return Err(eyre!(""));

View File

@ -2,6 +2,7 @@ use std::collections::HashMap;
use std::ops::Deref; use std::ops::Deref;
use std::path::{PathBuf, Path}; use std::path::{PathBuf, Path};
use color_eyre::Result; use color_eyre::Result;
use eyre::eyre; use eyre::eyre;
@ -13,19 +14,19 @@ use crate::parser::lookup_word;
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Function { pub struct Function {
pub loc: Loc, pub loc: Loc,
pub name: String pub name: String
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Constant { pub struct Constant {
pub loc: Loc, pub loc: Loc,
pub name: String pub name: String
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Memory { pub struct Memory {
pub loc: Loc, pub loc: Loc,
pub id: usize pub id: usize
@ -36,11 +37,12 @@ type Functions = HashMap<String, Function>;
type Memories = HashMap<String, Memory>; type Memories = HashMap<String, Memory>;
type Constants = HashMap<String, Constant>; type Constants = HashMap<String, Constant>;
#[derive(Debug, Clone)]
pub struct Preprocessor<'a> { pub struct Preprocessor<'a> {
program: Vec<Operator>, pub program: Vec<Operator>,
functions: Functions, pub functions: Functions,
memories: Memories, pub memories: Memories,
constants: Constants, pub constants: Constants,
args: &'a Args args: &'a Args
} }
@ -49,7 +51,7 @@ impl<'a> Preprocessor<'a> {
pub fn new(prog: Vec<Operator>, args: &'a Args) -> Self { pub fn new(prog: Vec<Operator>, args: &'a Args) -> Self {
Self { Self {
program: prog, program: prog,
args: args, args,
functions: HashMap::new(), functions: HashMap::new(),
memories: HashMap::new(), memories: HashMap::new(),
constants: 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>>()); 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 include_code = String::new();
let mut pth = PathBuf::new();
if include_path.text.chars().collect::<Vec<char>>()[0] == '.' { if include_path.text.chars().collect::<Vec<char>>()[0] == '.' {
let p = Path::new(include_path.loc.0.as_str()); let p = Path::new(include_path.loc.0.as_str());
let p = p.parent().unwrap(); let p = p.parent().unwrap();
let p = p.join(&include_path.text); let p = p.join(&include_path.text);
pth = p.clone();
include_code = std::fs::read_to_string(p)?; include_code = std::fs::read_to_string(p)?;
} else { } else {
for path in in_paths { for path in in_paths {
let p = PathBuf::from(path); let p = PathBuf::from(path);
let p = p.join(&include_path.text); let p = p.join(&include_path.text);
pth = p.clone();
if p.exists() { if p.exists() {
include_code = std::fs::read_to_string(p)?; 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); lerror!(&include_path.loc, "Include file in path '{}' was not found or is empty", include_path.text);
return Err(eyre!("")); return Err(eyre!(""));
} }
let code = lex(&include_code, &self.args.in_file, &self.args)?; let a = pth.to_str().unwrap().to_string();
let mut p = parser::Parser::new(code); let code = lex(&include_code, a.as_str(), self.args);
let mut code = p.parse(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(); code.reverse();
rtokens.append(&mut code); rtokens.append(&mut code);
@ -124,9 +133,9 @@ impl<'a> Preprocessor<'a> {
return Err(eyre!("")); 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(); let mut code: Vec<Operator> = Vec::new();
@ -157,7 +166,7 @@ impl<'a> Preprocessor<'a> {
token.addr = Some(self.memories.len()); token.addr = Some(self.memories.len());
program.push(token.clone()); 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) => { _ if op_type == OpType::Keyword(KeywordType::Function) => {
@ -166,47 +175,115 @@ impl<'a> Preprocessor<'a> {
return Err(eyre!("")); 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{ self.functions.insert(name.text.clone(), Function{
loc: function_name.loc.clone(), loc: name.loc.clone(),
name: function_name.text.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); // println!("{:?}", token);
program.push(token); program.push(fn_def);
} }
_ if op_type == OpType::Keyword(KeywordType::Constant) => { _ if op_type == OpType::Keyword(KeywordType::Constant) => {
if rtokens.is_empty() { if rtokens.is_empty() {
lerror!(&token.loc, "Constant name not found, expected {} but found nothing", TokenType::Word.human()); lerror!(&token.loc, "Constant name not found, expected {} but found nothing", TokenType::Word.human());
return Err(eyre!("")); 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)?; 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!(""));
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 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"); lerror!(&token.loc, "Constant was not closed with an 'end' instruction, expected 'end' but found nothing");
return Err(eyre!("")); return Err(eyre!(""));
} }
// token.value = // token.value =
program.push(token); program.push(const_def);
} }
_ => { _ => {
@ -216,14 +293,18 @@ impl<'a> Preprocessor<'a> {
} }
} }
self.program = program; 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>>>()); // 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 //* Feel free to fix this horrifying shit
//* i wanna kms //* i wanna kms
let mut times = 0; let mut times = 0;
// dbg!(program.clone()); // dbg!(program.clone());
while self.program.iter().map(|f| { 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) lookup_word(&f.text, &f.loc)
} else { } else {
OpType::Instruction(InstructionType::PushInt) // i hate myself, this is a randomly picked optype so its happy and works 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()?; self.expand()?;
times += 1; times += 1;
} }
Ok(self) Ok(self)
} }
@ -251,7 +331,7 @@ impl<'a> Preprocessor<'a> {
while !rtokens.is_empty() { while !rtokens.is_empty() {
let op = rtokens.pop().unwrap(); let op = rtokens.pop().unwrap();
let op_type = op.typ.clone(); let op_type = op.typ.clone();
if op.tok_typ.clone() == TokenType::Word { if op.tok_typ == TokenType::Word {
match op_type { match op_type {
OpType::Instruction(InstructionType::None) => { OpType::Instruction(InstructionType::None) => {
let m = self.functions.get(&op.text); let m = self.functions.get(&op.text);
@ -299,7 +379,7 @@ impl<'a> Preprocessor<'a> {
self.program = program; self.program = program;
// println!("{:#?}", self.program); // println!("{:#?}", self.program);
println!("{:?}", self.program.last().unwrap()); // println!("{:?}", self.program.last().unwrap());
Ok(()) Ok(())
} }
@ -318,47 +398,78 @@ impl<'a> Preprocessor<'a> {
} }
if word.tok_typ != TokenType::Word { 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!("")); return Err(eyre!(""));
} }
let m = self.memories.get(&word.text); let m = self.memories.get(&word.text);
if let Some(m) = m { if let Some(m) = m {
if typ != KeywordType::Memory { if typ == KeywordType::Memory {
lerror!(&word.loc, "{typ:?} cannot replace memory, got {}", word.text);
linfo!(&m.loc, "first definition here");
return Err(eyre!(""));
} else {
lerror!(&word.loc, "Memories cannot be redefined, got {}", word.text); lerror!(&word.loc, "Memories cannot be redefined, got {}", word.text);
linfo!(&m.loc, "first definition here"); linfo!(&m.loc, "first definition here");
if crate::DEV_MODE {println!("{word:?}")}
return Err(eyre!("")); 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); let f = self.functions.get(&word.text);
if let Some(f) = f { if let Some(f) = f {
if typ != KeywordType::Function { if typ == KeywordType::Function {
lerror!(&word.loc, "{typ:?} cannot replace function, got {}", word.text);
linfo!(&f.loc, "first definition here");
return Err(eyre!(""));
} else {
lerror!(&word.loc, "Functions cannot be redefined, got {}", word.text); lerror!(&word.loc, "Functions cannot be redefined, got {}", word.text);
linfo!(&f.loc, "first definition here"); linfo!(&f.loc, "first definition here");
if crate::DEV_MODE {println!("{word:?}")}
return Err(eyre!("")); 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); let c = self.constants.get(&word.text);
if let Some(c) = c { if let Some(c) = c {
if typ != KeywordType::Constant { if typ == KeywordType::Constant {
lerror!(&word.loc, "{typ:?} cannot replace constant, got {}", word.text);
linfo!(&c.loc, "first definition here");
return Err(eyre!(""));
} else {
lerror!(&word.loc, "Constants cannot be redefined, got {}", word.text); lerror!(&word.loc, "Constants cannot be redefined, got {}", word.text);
linfo!(&c.loc, "first definition here"); linfo!(&c.loc, "first definition here");
if crate::DEV_MODE {println!("{word:?}")}
return Err(eyre!("")); 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) 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 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 color_eyre::Result;
use eyre::eyre; use eyre::eyre;
#[allow(dead_code)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Function { pub struct Function {
loc: Loc,
args: Vec<Types>, args: Vec<Types>,
returns: Vec<Types>, returns: Vec<Types>,
} }
#[derive(Debug, Clone)]
pub struct Constant {
#[allow(dead_code)]
loc: Loc,
types: Vec<Types>,
}
impl Function { impl Function {
#[allow(dead_code)]
pub fn default() -> Self { pub fn default() -> Self {
Self { Self {
args: Vec::new(), args: Vec::new(),
returns: 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.unsaf {
if !args.quiet { if !args.quiet {
warn!("Unsafe mode enabled, disabling typechecker, goodluck"); 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 functions: HashMap<String, Function> = funcs;
// let mut in_function: (String, Function) = (String::new(), Function::default()); let mut constants: HashMap<String, Constant> = consts;
let mut stack: Vec<Types> = Vec::new(); // 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 stack_snapshots: Vec<Vec<Types>> = Vec::new();
let mut rtokens = ops.clone(); let mut rtokens = ops;
rtokens.reverse(); rtokens.reverse();
// println!("{:#?}", ops); // println!("{:#?}", ops);
while !rtokens.is_empty() { while !rtokens.is_empty() {
let op = rtokens.pop().unwrap(); let op = rtokens.pop().unwrap();
println!("{:?}", stack.clone()); // println!("{:?}", stack.clone());
// println!("{:?}", op); // println!("{:?}", op);
// println!("{}", ops.len()); // println!("{}", ops.len());
match op.typ.clone() { match op.typ.clone() {
OpType::Keyword(keyword) => { OpType::Keyword(keyword) => {
match keyword { match keyword {
KeywordType::If => { KeywordType::If |
stack_pop(&mut stack, &op, &[Types::Bool])?;
},
KeywordType::Do => { KeywordType::Do => {
stack_pop(&mut stack, &op, &[Types::Bool])?; stack_pop(&mut stack, &op, &[Types::Bool])?;
}, },
KeywordType::Function => { KeywordType::FunctionDef => {
let name = op.text.clone(); let name = op.text.clone();
if let Some(p) = rtokens.pop() { 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 { let mut func = Function {
args: Vec::new(), args: Vec::new(),
returns: Vec::new(), returns: Vec::new(),
loc: op.loc
}; };
let mut return_args = false; let mut return_args = false;
while p.as_ref().is_some() { 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) || if op.typ == OpType::Instruction(InstructionType::TypeBool) ||
op.typ == OpType::Instruction(InstructionType::TypeInt) || op.typ == OpType::Instruction(InstructionType::TypeInt) ||
op.typ == OpType::Instruction(InstructionType::TypePtr) || op.typ == OpType::Instruction(InstructionType::TypePtr) ||
op.typ == OpType::Instruction(InstructionType::TypeAny) ||
op.typ == OpType::Instruction(InstructionType::TypeVoid) { op.typ == OpType::Instruction(InstructionType::TypeVoid) {
let t = if op.typ == OpType::Instruction(InstructionType::TypeInt) { let t = if op.typ == OpType::Instruction(InstructionType::TypeInt) {
Types::Int Types::Int
} else } else if op.typ == OpType::Instruction(InstructionType::TypeBool) {
if op.typ == OpType::Instruction(InstructionType::TypeBool) {
Types::Bool Types::Bool
} else } else if op.typ == OpType::Instruction(InstructionType::TypePtr) {
if op.typ == OpType::Instruction(InstructionType::TypePtr) {
Types::Ptr Types::Ptr
} else } else if op.typ == OpType::Instruction(InstructionType::TypeVoid) {
if op.typ == OpType::Instruction(InstructionType::TypeVoid) {
if return_args { if return_args {
func.returns = vec![Types::Void]; func.returns = vec![Types::Void];
} else { } else {
@ -92,11 +105,9 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args) -> Result<Vec<Operator>>{
continue; continue;
} }
Types::Void Types::Void
} else } else if op.typ == OpType::Instruction(InstructionType::TypeStr) {
if op.typ == OpType::Instruction(InstructionType::TypeStr) {
Types::Str Types::Str
} else } else if op.typ == OpType::Instruction(InstructionType::TypeAny) {
if op.typ == OpType::Instruction(InstructionType::TypeAny) {
Types::Any Types::Any
} else { } else {
panic!() panic!()
@ -113,17 +124,40 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args) -> Result<Vec<Operator>>{
return_args = true; return_args = true;
} }
if op.typ == OpType::Keyword(KeywordType::FunctionDo) { if op.typ == OpType::Keyword(KeywordType::FunctionThen) {
break; break;
} }
p = rtokens.pop(); p = rtokens.pop();
}; };
functions.insert(name.clone(), func.clone());
// if name == "main" {
// in_function = (name, func.clone()); let mut code: Vec<Operator> = Vec::new();
// }
if func.args != vec![Types::Void] { 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.append(&mut func.args);
} }
stack_snapshots.push(stack.clone()); stack_snapshots.push(stack.clone());
@ -135,7 +169,14 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args) -> Result<Vec<Operator>>{
KeywordType::Include | KeywordType::Include |
KeywordType::Constant | KeywordType::Constant |
KeywordType::Memory => (), 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) => { OpType::Instruction(instruction) => {
@ -179,97 +220,41 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args) -> Result<Vec<Operator>>{
stack.push(a); stack.push(a);
stack.push(b); 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_pop(&mut stack, &op, &[Types::Int])?; stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Int); stack.push(Types::Int);
}, },
InstructionType::Plus => { InstructionType::Equals |
stack_pop(&mut stack, &op, &[Types::Int])?; InstructionType::Gt |
stack_pop(&mut stack, &op, &[Types::Int])?; InstructionType::Lt |
stack.push(Types::Int); InstructionType::Ge |
}, InstructionType::Le |
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::NotEquals => { InstructionType::NotEquals => {
stack_pop(&mut stack, &op, &[Types::Int])?; stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Int])?; stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Bool); 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 => { InstructionType::DivMod => {
stack_pop(&mut stack, &op, &[Types::Int])?; stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Int])?; stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Int); stack.push(Types::Int);
stack.push(Types::Int); stack.push(Types::Int);
}, },
InstructionType::Mul => { InstructionType::Load8 |
stack_pop(&mut stack, &op, &[Types::Int])?; InstructionType::Load32 |
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::Load64 => { InstructionType::Load64 => {
stack_pop(&mut stack, &op, &[Types::Ptr])?; stack_pop(&mut stack, &op, &[Types::Ptr])?;
stack.push(Types::Int); stack.push(Types::Int);
}, },
InstructionType::Store8 |
InstructionType::Store32 |
InstructionType::Store64 => { InstructionType::Store64 => {
stack_pop(&mut stack, &op, &[Types::Int])?; stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Ptr])?; stack_pop(&mut stack, &op, &[Types::Ptr])?;
@ -345,7 +330,9 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args) -> Result<Vec<Operator>>{
InstructionType::FnCall => { InstructionType::FnCall => {
stack_snapshots.push(stack.clone()); 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 s = stack.clone();
let mut a = f.args.clone(); let mut a = f.args.clone();
@ -366,24 +353,8 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args) -> Result<Vec<Operator>>{
} }
InstructionType::Return => { InstructionType::Return |
let snap = stack_snapshots.pop().unwrap(); InstructionType::None |
// 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::TypeBool | InstructionType::TypeBool |
InstructionType::TypePtr | InstructionType::TypePtr |
InstructionType::TypeInt | InstructionType::TypeInt |
@ -392,7 +363,11 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args) -> Result<Vec<Operator>>{
InstructionType::TypeStr | InstructionType::TypeStr |
InstructionType::Returns | InstructionType::Returns |
InstructionType::With => (), 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) { 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); 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 { 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! 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()) }; } #[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! 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! 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! 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" include "std.mcl"
fn main with void returns void do fn main with void returns void then
69 putd "hi\n" puts
end done