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:
parent
63636e1f83
commit
7e46c07cca
|
@ -1 +1 @@
|
|||
// todo: add some sort of macro
|
||||
// todo: add some sort of macrow
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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 {
|
||||
|
@ -24,19 +25,19 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
|
|||
of_o.set_extension("o");
|
||||
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 != 0 && 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 + 1 < tokens.len() && 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 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)?;
|
||||
|
@ -565,4 +568,23 @@ 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(())
|
||||
}
|
|
@ -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
20
src/config.rs
Normal 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
|
|
@ -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
18
src/errors/mod.rs
Normal 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"
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
|
@ -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 prerunned = pre_run(ops);
|
||||
let functions = prerunned.functions;
|
||||
let constants = prerunned.constants;
|
||||
let memories = prerunned.memories;
|
||||
|
||||
let mut memories: HashMap<usize, usize> = HashMap::new();
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -349,4 +387,34 @@ 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
|
||||
}
|
|
@ -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)]);
|
||||
|
|
|
@ -1 +1,27 @@
|
|||
pub mod linux_x86_64;
|
||||
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
|
||||
}
|
|
@ -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
|
||||
}
|
74
src/main.rs
74
src/main.rs
|
@ -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 {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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!(""));
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
||||
|
|
18
src/util.rs
18
src/util.rs
|
@ -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()) }; }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user