Initial
This commit is contained in:
125
src/cli.rs
Normal file
125
src/cli.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
use clap::{builder::PossibleValue, Parser, ValueEnum};
|
||||
use camino::Utf8PathBuf;
|
||||
lazy_static::lazy_static! {
|
||||
static ref DEFAULT_INCLUDE_PATHS: Vec<Utf8PathBuf> = vec![
|
||||
Utf8PathBuf::from("./"),
|
||||
Utf8PathBuf::from("./include"),
|
||||
Utf8PathBuf::from("~/.mclang/include"),
|
||||
];
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct CliArgs {
|
||||
/// Only compile, dont link
|
||||
#[arg(long, short)]
|
||||
pub compile: bool,
|
||||
|
||||
/// Verosity
|
||||
/// -1 - Nothing
|
||||
/// 0 - Only errors
|
||||
/// 1 - Normal
|
||||
/// 2 - Verbose
|
||||
/// 3 - Tracing
|
||||
#[arg(long, short, default_value_t=1)]
|
||||
pub verbose: i8,
|
||||
|
||||
/// Runt the program after compilation
|
||||
#[arg(long, short)]
|
||||
pub run: bool,
|
||||
|
||||
/// Output execuable file path
|
||||
#[arg(long, short, default_value="./a.out")]
|
||||
pub output: Utf8PathBuf,
|
||||
|
||||
/// Paths to search for libraries
|
||||
#[arg(long="include", short='I', default_values_t=DEFAULT_INCLUDE_PATHS.clone().into_iter())]
|
||||
pub include_path: Vec<Utf8PathBuf>,
|
||||
|
||||
/// Target to compile to
|
||||
#[arg(long, short='T', default_value_t=CompilationTarget::X86_64_linux_nasm)]
|
||||
pub target: CompilationTarget,
|
||||
|
||||
/// Input code files
|
||||
pub input: Vec<Utf8PathBuf>,
|
||||
|
||||
#[clap(skip)]
|
||||
pub passthrough: Vec<String>
|
||||
}
|
||||
|
||||
impl CliArgs {
|
||||
pub fn parse_with_passthrough() -> Self {
|
||||
let mut clap_args = Vec::new();
|
||||
let mut pt_args = Vec::new();
|
||||
let mut switch = false;
|
||||
for arg in std::env::args() {
|
||||
if arg == String::from("--") {
|
||||
switch = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if !switch {
|
||||
//clap args
|
||||
clap_args.push(arg);
|
||||
} else {
|
||||
// passwthrough
|
||||
pt_args.push(arg);
|
||||
}
|
||||
}
|
||||
|
||||
let mut cargs = Self::parse_from(clap_args);
|
||||
cargs.passthrough = pt_args;
|
||||
|
||||
cargs
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CompilationTarget {
|
||||
X86_64_linux_nasm
|
||||
}
|
||||
|
||||
impl ValueEnum for CompilationTarget {
|
||||
fn value_variants<'a>() -> &'a [Self] {
|
||||
&[
|
||||
Self::X86_64_linux_nasm
|
||||
]
|
||||
}
|
||||
|
||||
fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
|
||||
match self {
|
||||
CompilationTarget::X86_64_linux_nasm => Some(PossibleValue::new("x86_64-linux-nasm")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CompilationTarget {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let r = match self {
|
||||
CompilationTarget::X86_64_linux_nasm => "x86_64-linux-nasm",
|
||||
};
|
||||
write!(f, "{}", r)
|
||||
}
|
||||
}
|
||||
|
||||
// impl From<CompilationTarget> for clap::builder::OsStr {
|
||||
// fn from(value: CompilationTarget) -> Self {
|
||||
// match value {
|
||||
// CompilationTarget::X86_64_linux_nasm => "X86_64_linux_nasm".into()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl TryFrom<&str> for CompilationTarget {
|
||||
// type Error = anyhow::Error;
|
||||
// fn try_from(value: &str) -> Result<Self, Error> {
|
||||
// match value {
|
||||
// "X86_64_linux_nasm" => Ok(CompilationTarget::X86_64_linux_nasm)
|
||||
// _ => bail!("Unknown compilation target {value}")
|
||||
// }
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
84
src/compiler/mod.rs
Normal file
84
src/compiler/mod.rs
Normal file
@@ -0,0 +1,84 @@
|
||||
mod x86_64_linux_nasm;
|
||||
mod utils;
|
||||
|
||||
use anyhow::bail;
|
||||
|
||||
use crate::{cli::{CliArgs, CompilationTarget}, types::ast::Program};
|
||||
use std::{collections::HashMap, fs::File, io::{BufWriter, Write}, path::{Path, PathBuf}};
|
||||
|
||||
use self::utils::run_cmd;
|
||||
|
||||
|
||||
pub trait Compiler {
|
||||
fn new() -> Self;
|
||||
fn generate_asm(&mut self, prog: &Program, fd: &mut BufWriter<File>) -> anyhow::Result<()>;
|
||||
fn compile(&mut self, asm_fp: &Path, obj: &Path) -> anyhow::Result<()>;
|
||||
fn link(&mut self, obj_files: Vec<PathBuf>, bin_fp: &Path) -> anyhow::Result<()>;
|
||||
/// Return programs that are needed
|
||||
fn needed_dependencies(&mut self) -> Vec<&str>;
|
||||
}
|
||||
|
||||
//NOTE: No bsd cause im not about to create 3 or 4 diffrent compilation targets
|
||||
|
||||
pub fn compile_program(cli_args: &CliArgs, prog_map: HashMap<&Path, Program>) -> anyhow::Result<()> {
|
||||
let mut compiler = match cli_args.target {
|
||||
CompilationTarget::X86_64_linux_nasm => x86_64_linux_nasm::X86_64LinuxNasmCompiler::new(),
|
||||
};
|
||||
let bin_p = cli_args.output.as_std_path();
|
||||
let mut objs = Vec::new();
|
||||
for (k, v) in prog_map {
|
||||
let mut asm_p = k.to_path_buf();
|
||||
let mut obj_p = k.to_path_buf();
|
||||
|
||||
asm_p.set_extension("s");
|
||||
obj_p.set_extension("o");
|
||||
|
||||
|
||||
if let Err(_) = compile_file(&mut compiler, cli_args, asm_p.as_path(), obj_p.as_path(), &v) {
|
||||
error!("Failed to compile file {k:?}");
|
||||
bail!("")
|
||||
}
|
||||
objs.push(obj_p.clone());
|
||||
}
|
||||
|
||||
if let Err(e) = compiler.link(objs, bin_p) {
|
||||
error!("Failed to link program: {e}");
|
||||
bail!("")
|
||||
}
|
||||
|
||||
info!("Finished building program");
|
||||
|
||||
if cli_args.run {
|
||||
run_cmd(format!("./{}", bin_p.to_string_lossy()), cli_args.passthrough.clone())?;
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compile_file<C: Compiler>(compiler: &mut C, _: &CliArgs, asm_file: &Path, obj_file: &Path, prog: &Program) -> anyhow::Result<()> {
|
||||
|
||||
let asm_fd = std::fs::File::options()
|
||||
.write(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.append(false)
|
||||
.open(asm_file);
|
||||
|
||||
let asm_fd = match asm_fd {
|
||||
Ok(fd) => fd,
|
||||
Err(e) => {
|
||||
error!("Failed to open file {asm_file:?}: {e}");
|
||||
bail!("");
|
||||
}
|
||||
};
|
||||
|
||||
let mut buf_asm_fd = BufWriter::new(asm_fd);
|
||||
|
||||
compiler.generate_asm(prog, &mut buf_asm_fd)?;
|
||||
buf_asm_fd.flush()?;
|
||||
|
||||
compiler.compile(asm_file, obj_file)?;
|
||||
Ok(())
|
||||
}
|
||||
36
src/compiler/utils.rs
Normal file
36
src/compiler/utils.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use std::{fmt::Debug, process::{Command, Stdio}};
|
||||
|
||||
use anyhow::bail;
|
||||
|
||||
|
||||
|
||||
pub fn run_cmd<'a, S: Into<String> + Debug + Clone>(bin: S, args: Vec<String>) -> anyhow::Result<()> {
|
||||
let debug = unsafe {
|
||||
crate::logger::LOGGER.enabled(crate::logger::Level::Debug)
|
||||
};
|
||||
let mut cmd = Command::new(bin.clone().into());
|
||||
let cmd = cmd.args(args);
|
||||
let cmd = if debug {
|
||||
cmd.stdout(Stdio::inherit())
|
||||
} else {
|
||||
cmd.stdout(Stdio::null())
|
||||
};
|
||||
let cmd = cmd.stderr(Stdio::inherit());
|
||||
|
||||
let child = match cmd.spawn() {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
error!("Unable to run {cmd:?}: {e}");
|
||||
bail!("");
|
||||
}
|
||||
};
|
||||
let ret = child.wait_with_output().expect("fuck i know");
|
||||
|
||||
|
||||
if !ret.status.success() {
|
||||
error!("Process running {bin:?} exited abnormaly, run with -v 2 for more output");
|
||||
bail!("")
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
485
src/compiler/x86_64_linux_nasm/mod.rs
Normal file
485
src/compiler/x86_64_linux_nasm/mod.rs
Normal file
@@ -0,0 +1,485 @@
|
||||
mod utils;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::{fs::File, io::BufWriter, path::Path};
|
||||
use std::io::Write;
|
||||
use crate::types::ast::{AstNode, Function, Module, Program};
|
||||
use crate::types::token::{InstructionType, Token, TokenType};
|
||||
|
||||
use super::utils::run_cmd;
|
||||
use super::Compiler;
|
||||
|
||||
|
||||
|
||||
|
||||
pub struct X86_64LinuxNasmCompiler {
|
||||
strings: Vec<String>,
|
||||
if_i: usize,
|
||||
while_i: usize,
|
||||
used_consts: Vec<String>
|
||||
}
|
||||
|
||||
impl X86_64LinuxNasmCompiler {
|
||||
fn handle_token(&mut self, fd: &mut BufWriter<File>, _: &Program, token: &Token) -> anyhow::Result<()> {
|
||||
match &token.typ {
|
||||
TokenType::Instruction(it) => {
|
||||
match it {
|
||||
InstructionType::PushInt(i) => {
|
||||
writeln!(fd, " mov rax, {i} ; PUSHINT({i})")?;
|
||||
writeln!(fd, " push rax")?;
|
||||
},
|
||||
InstructionType::PushStr(s) => {
|
||||
writeln!(fd, " push {}", s.len())?;
|
||||
writeln!(fd, " push str_{}; PUSHSTR({})", self.strings.len(), s.escape_debug())?;
|
||||
self.strings.push(s.clone());
|
||||
},
|
||||
InstructionType::PushCStr(s) => {
|
||||
writeln!(fd, " push str_{}; PUSHCSTR({})", self.strings.len(), s.escape_debug())?;
|
||||
self.strings.push(s.clone());
|
||||
},
|
||||
InstructionType::PushChar(c) => {
|
||||
writeln!(fd, " push {}; PUSHCHAR({})", *c as u8, c.escape_debug())?;
|
||||
},
|
||||
InstructionType::Drop => {
|
||||
writeln!(fd, " pop rax ; DROP")?;
|
||||
},
|
||||
InstructionType::Print => {
|
||||
writeln!(fd, " pop rdi")?;
|
||||
writeln!(fd, " call _dbg_print ; _DBG_PRINT")?;
|
||||
},
|
||||
InstructionType::Dup => {
|
||||
writeln!(fd, " pop rax ; DUP")?;
|
||||
writeln!(fd, " push rax")?;
|
||||
writeln!(fd, " push rax")?;
|
||||
},
|
||||
InstructionType::Rot => {
|
||||
writeln!(fd, " pop rax ; ROT")?;
|
||||
writeln!(fd, " pop rbx")?;
|
||||
writeln!(fd, " pop rcx")?;
|
||||
writeln!(fd, " push rbx")?;
|
||||
writeln!(fd, " push rax")?;
|
||||
writeln!(fd, " push rcx")?;
|
||||
},
|
||||
InstructionType::Over => {
|
||||
writeln!(fd, " pop rax ; OVER")?;
|
||||
writeln!(fd, " pop rbx")?;
|
||||
writeln!(fd, " push rbx")?;
|
||||
writeln!(fd, " push rax")?;
|
||||
writeln!(fd, " push rbx")?;
|
||||
},
|
||||
InstructionType::Swap => {
|
||||
writeln!(fd, " pop rax ; SWAP")?;
|
||||
writeln!(fd, " pop rbx")?;
|
||||
writeln!(fd, " push rax")?;
|
||||
writeln!(fd, " push rbx")?;
|
||||
}
|
||||
InstructionType::Minus => {
|
||||
writeln!(fd, " pop rax ; SUB")?;
|
||||
writeln!(fd, " pop rbx")?;
|
||||
writeln!(fd, " sub rbx, rax")?;
|
||||
writeln!(fd, " push rbx")?;
|
||||
},
|
||||
InstructionType::Plus => {
|
||||
writeln!(fd, " pop rax ; ADD")?;
|
||||
writeln!(fd, " pop rbx")?;
|
||||
writeln!(fd, " add rax, rbx")?;
|
||||
writeln!(fd, " push rax")?;
|
||||
},
|
||||
InstructionType::Equals => {
|
||||
writeln!(fd, " mov rcx, 0 ; EQ")?;
|
||||
writeln!(fd, " mov rdx, 1")?;
|
||||
writeln!(fd, " pop rax")?;
|
||||
writeln!(fd, " pop rbx")?;
|
||||
writeln!(fd, " cmp rax, rbx")?;
|
||||
writeln!(fd, " cmove rcx, rdx")?;
|
||||
writeln!(fd, " push rcx")?;
|
||||
},
|
||||
InstructionType::Gt => {
|
||||
writeln!(fd, " mov rcx, 0 ; GT")?;
|
||||
writeln!(fd, " mov rdx, 1")?;
|
||||
writeln!(fd, " pop rbx")?;
|
||||
writeln!(fd, " pop rax")?;
|
||||
writeln!(fd, " cmp rax, rbx")?;
|
||||
writeln!(fd, " cmovg rcx, rdx")?;
|
||||
writeln!(fd, " push rcx")?;
|
||||
},
|
||||
InstructionType::Lt => {
|
||||
writeln!(fd, " mov rcx, 0 ; LT")?;
|
||||
writeln!(fd, " mov rdx, 1")?;
|
||||
writeln!(fd, " pop rbx")?;
|
||||
writeln!(fd, " pop rax")?;
|
||||
writeln!(fd, " cmp rax, rbx")?;
|
||||
writeln!(fd, " cmovl rcx, rdx")?;
|
||||
writeln!(fd, " push rcx")?;
|
||||
},
|
||||
InstructionType::Ge => {
|
||||
writeln!(fd, " mov rcx, 0 ; GE")?;
|
||||
writeln!(fd, " mov rdx, 1")?;
|
||||
writeln!(fd, " pop rbx")?;
|
||||
writeln!(fd, " pop rax")?;
|
||||
writeln!(fd, " cmp rax, rbx")?;
|
||||
writeln!(fd, " cmovge rcx, rdx")?;
|
||||
writeln!(fd, " push rcx")?;
|
||||
},
|
||||
InstructionType::Le => {
|
||||
writeln!(fd, " mov rcx, 0 ; LE")?;
|
||||
writeln!(fd, " mov rdx, 1")?;
|
||||
writeln!(fd, " pop rbx")?;
|
||||
writeln!(fd, " pop rax")?;
|
||||
writeln!(fd, " cmp rax, rbx")?;
|
||||
writeln!(fd, " cmovle rcx, rdx")?;
|
||||
writeln!(fd, " push rcx")?;
|
||||
},
|
||||
InstructionType::NotEquals => {
|
||||
writeln!(fd, " mov rdx, 1 ; NEQ")?;
|
||||
writeln!(fd, " mov rcx, 0")?;
|
||||
writeln!(fd, " pop rax")?;
|
||||
writeln!(fd, " pop rbx")?;
|
||||
writeln!(fd, " cmp rax, rbx")?;
|
||||
writeln!(fd, " cmove rcx, rdx")?;
|
||||
writeln!(fd, " push rcx")?;
|
||||
},
|
||||
InstructionType::Band => {
|
||||
writeln!(fd, " pop rax ; BAND")?;
|
||||
writeln!(fd, " pop rbx")?;
|
||||
writeln!(fd, " and rbx, rax")?;
|
||||
writeln!(fd, " push rbx")?;
|
||||
},
|
||||
InstructionType::Bor => {
|
||||
writeln!(fd, " pop rax ; BOR")?;
|
||||
writeln!(fd, " pop rbx")?;
|
||||
writeln!(fd, " or rbx, rax")?;
|
||||
writeln!(fd, " push rbx")?;
|
||||
}
|
||||
InstructionType::Shr => {
|
||||
writeln!(fd, " pop rcx")?;
|
||||
writeln!(fd, " pop rbx")?;
|
||||
writeln!(fd, " shr rbx, cl")?;
|
||||
writeln!(fd, " push rbx")?;
|
||||
},
|
||||
InstructionType::Shl => {
|
||||
writeln!(fd, " pop rcx")?;
|
||||
writeln!(fd, " pop rbx")?;
|
||||
writeln!(fd, " shl rbx, cl")?;
|
||||
writeln!(fd, " push rbx")?;
|
||||
},
|
||||
InstructionType::DivMod => {
|
||||
writeln!(fd, " xor rdx, rdx")?;
|
||||
writeln!(fd, " pop rbx")?;
|
||||
writeln!(fd, " pop rax")?;
|
||||
writeln!(fd, " div rbx")?;
|
||||
writeln!(fd, " push rax")?;
|
||||
writeln!(fd, " push rdx")?;
|
||||
},
|
||||
InstructionType::Mul => {
|
||||
writeln!(fd, " pop rax ; MUL")?;
|
||||
writeln!(fd, " pop rbx")?;
|
||||
writeln!(fd, " mul rbx")?;
|
||||
writeln!(fd, " push rax")?;
|
||||
},
|
||||
InstructionType::Read8 => {
|
||||
writeln!(fd, " pop rax ; READ8")?;
|
||||
writeln!(fd, " xor rbx, rbx")?;
|
||||
writeln!(fd, " mov bl, byte [rax]")?;
|
||||
writeln!(fd, " push rbx")?;
|
||||
}
|
||||
InstructionType::Write8 => {
|
||||
writeln!(fd, " pop rax ; WRITE 8")?;
|
||||
writeln!(fd, " xor rbx, rbx")?;
|
||||
writeln!(fd, " mov ebx, dword [rax]")?;
|
||||
writeln!(fd, " push rbx")?;
|
||||
},
|
||||
InstructionType::Read32 => {
|
||||
writeln!(fd, " pop rax ; READ 32")?;
|
||||
writeln!(fd, " xor rbx, rbx")?;
|
||||
writeln!(fd, " mov ebx, dword [rax]")?;
|
||||
writeln!(fd, " push rbx")?;
|
||||
},
|
||||
InstructionType::Write32 => {
|
||||
writeln!(fd, " pop rbx ; WRITE 32")?;
|
||||
writeln!(fd, " pop rax")?;
|
||||
writeln!(fd, " mov dword[rax], ebx")?;
|
||||
},
|
||||
InstructionType::Read64 => {
|
||||
writeln!(fd, " pop rax ; READ 32")?;
|
||||
writeln!(fd, " xor rbx, rbx")?;
|
||||
writeln!(fd, " mov rbx, qword [rax]")?;
|
||||
writeln!(fd, " push rbx")?;
|
||||
},
|
||||
InstructionType::Write64 => {
|
||||
writeln!(fd, " pop rbx ; WRITE 64")?;
|
||||
writeln!(fd, " pop rax")?;
|
||||
writeln!(fd, " mov qword[rax], rbx")?;
|
||||
},
|
||||
InstructionType::Syscall0 => {
|
||||
writeln!(fd, " pop rax")?;
|
||||
writeln!(fd, " syscall")?;
|
||||
writeln!(fd, " push rax")?;
|
||||
},
|
||||
InstructionType::Syscall1 => {
|
||||
writeln!(fd, " pop rax")?;
|
||||
writeln!(fd, " pop rdi")?;
|
||||
writeln!(fd, " syscall")?;
|
||||
writeln!(fd, " push rax")?;
|
||||
},
|
||||
InstructionType::Syscall2 => {
|
||||
writeln!(fd, " pop rax")?;
|
||||
writeln!(fd, " pop rdi")?;
|
||||
writeln!(fd, " pop rsi")?;
|
||||
writeln!(fd, " syscall")?;
|
||||
writeln!(fd, " push rax")?;
|
||||
},
|
||||
InstructionType::Syscall3 => {
|
||||
writeln!(fd, " pop rax")?;
|
||||
writeln!(fd, " pop rdi")?;
|
||||
writeln!(fd, " pop rsi")?;
|
||||
writeln!(fd, " pop rdx")?;
|
||||
writeln!(fd, " syscall")?;
|
||||
writeln!(fd, " push rax")?;
|
||||
},
|
||||
InstructionType::Syscall4 => {
|
||||
writeln!(fd, " pop rax")?;
|
||||
writeln!(fd, " pop rdi")?;
|
||||
writeln!(fd, " pop rsi")?;
|
||||
writeln!(fd, " pop rdx")?;
|
||||
writeln!(fd, " pop r10")?;
|
||||
writeln!(fd, " syscall")?;
|
||||
writeln!(fd, " push rax")?;
|
||||
},
|
||||
InstructionType::Syscall5 => {
|
||||
writeln!(fd, " pop rax")?;
|
||||
writeln!(fd, " pop rdi")?;
|
||||
writeln!(fd, " pop rsi")?;
|
||||
writeln!(fd, " pop rdx")?;
|
||||
writeln!(fd, " pop r10")?;
|
||||
writeln!(fd, " pop r8")?;
|
||||
writeln!(fd, " syscall")?;
|
||||
writeln!(fd, " push rax")?;
|
||||
},
|
||||
InstructionType::Syscall6 => {
|
||||
writeln!(fd, " pop rax")?;
|
||||
writeln!(fd, " pop rdi")?;
|
||||
writeln!(fd, " pop rsi")?;
|
||||
writeln!(fd, " pop rdx")?;
|
||||
writeln!(fd, " pop r10")?;
|
||||
writeln!(fd, " pop r8")?;
|
||||
writeln!(fd, " pop r9")?;
|
||||
writeln!(fd, " syscall")?;
|
||||
writeln!(fd, " push rax")?;
|
||||
},
|
||||
InstructionType::CastBool |
|
||||
InstructionType::CastPtr |
|
||||
InstructionType::CastInt |
|
||||
InstructionType::CastVoid => (), //? Possibly have a use for this
|
||||
InstructionType::TypeBool |
|
||||
InstructionType::TypePtr |
|
||||
InstructionType::TypeInt |
|
||||
InstructionType::TypeVoid |
|
||||
InstructionType::TypeAny |
|
||||
InstructionType::FnCall |
|
||||
InstructionType::MemUse |
|
||||
InstructionType::ConstUse => unreachable!(),
|
||||
InstructionType::Return => {
|
||||
writeln!(fd, " sub rbp, 8")?;
|
||||
writeln!(fd, " mov rbx, qword [rbp]")?;
|
||||
writeln!(fd, " push rbx")?;
|
||||
writeln!(fd, " ret")?;
|
||||
},
|
||||
}
|
||||
},
|
||||
TokenType::Keyword(_) |
|
||||
TokenType::Unknown(_) => unreachable!(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_module(&mut self, fd: &mut BufWriter<File>, prog: &Program, module: &Module) -> anyhow::Result<()> {
|
||||
writeln!(fd, "; {} Module {} START", module.path.join("::"), module.ident)?;
|
||||
self.handle_ast_list(fd, prog, module.body.clone())?;
|
||||
writeln!(fd, "; {} Module {} END", module.path.join("::"), module.ident)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_function(&mut self, fd: &mut BufWriter<File>, prog: &Program, func: &Function) -> anyhow::Result<()> {
|
||||
writeln!(fd, "{f}: ; fn {f}", f=func.ident)?;
|
||||
writeln!(fd, " pop rbx")?;
|
||||
writeln!(fd, " mov qword [rbp], rbx")?;
|
||||
writeln!(fd, " add rbp, 8")?;
|
||||
|
||||
self.handle_ast_list(fd, prog, func.body.clone())?;
|
||||
|
||||
writeln!(fd, " sub rbp, 8")?;
|
||||
writeln!(fd, " mov rbx, qword [rbp]")?;
|
||||
writeln!(fd, " push rbx")?;
|
||||
writeln!(fd, " ret")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_ast_list(&mut self, fd: &mut BufWriter<File>, prog: &Program, ast: Vec<AstNode>) -> anyhow::Result<()> {
|
||||
for node in ast {
|
||||
match &node {
|
||||
AstNode::Function(f) => self.handle_function(fd, prog, f)?,
|
||||
AstNode::Constant(_) => (),
|
||||
AstNode::If(i) => {
|
||||
let id = self.if_i;
|
||||
self.if_i += 1;
|
||||
|
||||
writeln!(fd, "; IF({id}) START")?;
|
||||
self.handle_ast_list(fd, prog, i.test.clone())?;
|
||||
writeln!(fd, " pop rax")?;
|
||||
writeln!(fd, " test rax, rax")?;
|
||||
writeln!(fd, " jz if_{id}_else")?;
|
||||
writeln!(fd, "if_{id}_start:")?;
|
||||
self.handle_ast_list(fd, prog, i.body.clone())?;
|
||||
writeln!(fd, " jmp if_{id}_end")?;
|
||||
writeln!(fd, "if_{id}_else:")?;
|
||||
self.handle_ast_list(fd, prog, vec![Box::leak(i.els.clone()).clone()])?;
|
||||
writeln!(fd, "if_{id}_end:")?;
|
||||
writeln!(fd, "; IF({id}) END")?;
|
||||
},
|
||||
AstNode::While(w) => {
|
||||
let id = self.while_i;
|
||||
self.while_i += 1;
|
||||
writeln!(fd, "; WHILE({id}) START")?;
|
||||
writeln!(fd, "while_{id}_test:")?;
|
||||
self.handle_ast_list(fd, prog, w.test.clone())?;
|
||||
writeln!(fd, " pop rax")?;
|
||||
writeln!(fd, " test rax, rax")?;
|
||||
writeln!(fd, " jz while_{id}_exit")?;
|
||||
writeln!(fd, "while_{id}_start:")?;
|
||||
self.handle_ast_list(fd, prog, w.body.clone())?;
|
||||
writeln!(fd, "while_{id}_end:")?;
|
||||
writeln!(fd, " jmp while_{id}_test")?;
|
||||
writeln!(fd, "while_{id}_exit:")?;
|
||||
writeln!(fd, "; WHILE({id}) END")?;
|
||||
},
|
||||
AstNode::Module(m) => self.handle_module(fd, prog, m)?,
|
||||
AstNode::Memory(_) => todo!(),
|
||||
AstNode::MemUse(_) => todo!(),
|
||||
AstNode::ConstUse(c) => {
|
||||
self.used_consts.push(c.ident.clone());
|
||||
writeln!(fd, " mov rax, qword [c_{}]", c.ident)?;
|
||||
writeln!(fd, " push rax")?;
|
||||
},
|
||||
AstNode::FnCall(f)=> {
|
||||
writeln!(fd, " call {f} ; FUNCTIONCALL({f:?})", f=f.ident)?;
|
||||
},
|
||||
AstNode::Block(b)=> {
|
||||
writeln!(fd, "; BLOCK({}) START", b.comment)?;
|
||||
self.handle_ast_list(fd, prog, b.body.clone())?;
|
||||
writeln!(fd, "; BLOCK({}) END", b.comment)?;
|
||||
},
|
||||
AstNode::Token(t) => self.handle_token(fd, prog, t)?,
|
||||
AstNode::Int(_, _) |
|
||||
AstNode::Str(_, _) |
|
||||
AstNode::CStr(_, _) |
|
||||
AstNode::Char(_, _) => unreachable!(),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Compiler for X86_64LinuxNasmCompiler {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
strings: Vec::new(),
|
||||
used_consts: Vec::new(),
|
||||
if_i: 0,
|
||||
while_i: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_asm(&mut self, prog: &Program, fd: &mut BufWriter<File>) -> anyhow::Result<()> {
|
||||
|
||||
writeln!(fd, "BITS 64")?;
|
||||
writeln!(fd, "segment .text")?;
|
||||
writeln!(fd, "{}", utils::DBG_PRINT)?;
|
||||
writeln!(fd, "global _start")?;
|
||||
writeln!(fd, "_start:")?;
|
||||
writeln!(fd, " lea rbp, [rel ret_stack]")?;
|
||||
writeln!(fd, " call main")?;
|
||||
writeln!(fd, " jmp __MCL_END__")?;
|
||||
|
||||
match &prog.ast {
|
||||
AstNode::Module(m) => {
|
||||
self.handle_module(fd, prog, m)?;
|
||||
},
|
||||
_ => panic!()
|
||||
}
|
||||
|
||||
|
||||
writeln!(fd, "__MCL_END__:")?;
|
||||
writeln!(fd, " mov rax, 60")?;
|
||||
writeln!(fd, " mov rdi, 0")?;
|
||||
writeln!(fd, " syscall")?;
|
||||
|
||||
writeln!(fd, "segment .data")?;
|
||||
for (_, v) in prog.constants.iter() {
|
||||
|
||||
if !self.used_consts.contains(&v.ident) {
|
||||
continue;
|
||||
}
|
||||
|
||||
match Box::leak(v.value.clone()) {
|
||||
AstNode::Int(_, val) => {
|
||||
writeln!(fd, "c_{}: dq {}", v.ident, val)?;
|
||||
}
|
||||
AstNode::Str(_, val) |
|
||||
AstNode::CStr(_, val) => {
|
||||
let s_chars = val.chars().map(|c| (c as u32).to_string()).collect::<Vec<String>>();
|
||||
let s_list = s_chars.join(",");
|
||||
writeln!(fd, "c_{}: db {} ; {}", v.ident, s_list, val.escape_debug())?;
|
||||
}
|
||||
AstNode::Char(_, val) => {
|
||||
writeln!(fd, "c_{}: db {} ; '{}'", v.ident, *val as u8, val)?;
|
||||
}
|
||||
c => panic!("{c:?}")
|
||||
};
|
||||
}
|
||||
|
||||
for (i, s) in self.strings.iter().enumerate() {
|
||||
let s_chars = s.chars().map(|c| (c as u32).to_string()).collect::<Vec<String>>();
|
||||
let s_list = s_chars.join(",");
|
||||
writeln!(fd, "str_{i}: db {} ; STRDEF({})", s_list, s.escape_debug())?;
|
||||
}
|
||||
writeln!(fd, "segment .bss")?;
|
||||
writeln!(fd, "ret_stack: resq 256")?;
|
||||
|
||||
//TODO: Memories
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
fn compile(&mut self, asm_fp: &Path, obj_fp: &Path) -> anyhow::Result<()> {
|
||||
run_cmd("nasm", vec![
|
||||
String::from("-felf64"),
|
||||
String::from("-o"),
|
||||
obj_fp.to_string_lossy().to_string(),
|
||||
asm_fp.to_string_lossy().to_string()
|
||||
])
|
||||
}
|
||||
|
||||
fn link(&mut self, obj_files: Vec<PathBuf>, bin_fp: &Path) -> anyhow::Result<()> {
|
||||
let mut args = vec![
|
||||
String::from("-o"),
|
||||
bin_fp.to_string_lossy().to_string(),
|
||||
];
|
||||
|
||||
for f in obj_files {
|
||||
args.push(f.to_string_lossy().to_string())
|
||||
}
|
||||
|
||||
run_cmd("ld", args)
|
||||
}
|
||||
|
||||
fn needed_dependencies(&mut self) -> Vec<&str> {
|
||||
vec![
|
||||
"nasm",
|
||||
"ld"
|
||||
]
|
||||
}
|
||||
}
|
||||
37
src/compiler/x86_64_linux_nasm/utils.rs
Normal file
37
src/compiler/x86_64_linux_nasm/utils.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
|
||||
pub const DBG_PRINT: &'static str = "
|
||||
_dbg_print:
|
||||
mov r9, -3689348814741910323
|
||||
sub rsp, 40
|
||||
mov BYTE [rsp+31], 10
|
||||
lea rcx, [rsp+30]
|
||||
.L2:
|
||||
mov rax, rdi
|
||||
lea r8, [rsp+32]
|
||||
mul r9
|
||||
mov rax, rdi
|
||||
sub r8, rcx
|
||||
shr rdx, 3
|
||||
lea rsi, [rdx+rdx*4]
|
||||
add rsi, rsi
|
||||
sub rax, rsi
|
||||
add eax, 48
|
||||
mov BYTE [rcx], al
|
||||
mov rax, rdi
|
||||
mov rdi, rdx
|
||||
mov rdx, rcx
|
||||
sub rcx, 1
|
||||
cmp rax, 9
|
||||
ja .L2
|
||||
lea rax, [rsp+32]
|
||||
mov edi, 1
|
||||
sub rdx, rax
|
||||
xor eax, eax
|
||||
lea rsi, [rsp+32+rdx]
|
||||
mov rdx, r8
|
||||
mov rax, 1
|
||||
syscall
|
||||
add rsp, 40
|
||||
ret
|
||||
";
|
||||
302
src/lexer/mod.rs
Normal file
302
src/lexer/mod.rs
Normal file
@@ -0,0 +1,302 @@
|
||||
use std::path::Path;
|
||||
use anyhow::bail;
|
||||
|
||||
use crate::{error, types::{common::Loc, token::{InstructionType, KeywordType, Token, TokenType}}};
|
||||
|
||||
|
||||
|
||||
pub struct Lexer {
|
||||
pub loc: Loc,
|
||||
pub tokens: Vec<Token>
|
||||
}
|
||||
|
||||
impl Lexer {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
loc: Default::default(),
|
||||
tokens: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn lex(&mut self, file: &Path) -> anyhow::Result<&mut Self> {
|
||||
self.reset(file);
|
||||
|
||||
let chars = match std::fs::read_to_string(file) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
error!("Failed to open file {file:?} : {e}");
|
||||
bail!("");
|
||||
}
|
||||
}.chars().collect::<Vec<char>>();
|
||||
|
||||
|
||||
let mut idx = 0;
|
||||
let mut buf = String::new();
|
||||
let mut is_searching = false;
|
||||
|
||||
if let Err(_) = self.go_to_first_char(&chars, &mut idx) {
|
||||
return Ok(self);
|
||||
}
|
||||
|
||||
let mut start_loc = self.loc.clone();
|
||||
while idx < chars.len() {
|
||||
|
||||
match chars[idx] {
|
||||
|
||||
'c' if chars.get(idx + 1) == Some(&'"') => {
|
||||
start_loc = self.loc.clone();
|
||||
is_searching = true;
|
||||
idx += 2; // skip c and "
|
||||
self.loc.col += 2;
|
||||
|
||||
if !buf.is_empty() {
|
||||
debug!({loc => self.loc() }, "buffer was not empty, intresting");
|
||||
}
|
||||
|
||||
loop {
|
||||
if chars[idx] == '"' && chars[idx-1] != '\\' {
|
||||
break;
|
||||
}
|
||||
buf.push(chars[idx]);
|
||||
if chars[idx] == '\n' {
|
||||
self.loc.inc_line()
|
||||
}
|
||||
self.loc.inc_col();
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
buf.push('\0');
|
||||
let str = self.unescape(&&buf);
|
||||
self.loc.inc_col();
|
||||
self.tokens.push(Token::new(TokenType::Instruction(InstructionType::PushCStr(str)), self.loc(), buf.clone()));
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
'"' => {
|
||||
start_loc = self.loc.clone();
|
||||
is_searching = true;
|
||||
idx += 1; // skip "
|
||||
self.loc.col += 1;
|
||||
|
||||
if !buf.is_empty() {
|
||||
debug!({loc => self.loc() }, "buffer was not empty, intresting ({buf:?})");
|
||||
}
|
||||
|
||||
// while chars.get(idx+1) != Some(&'"') && chars[idx] != '\\' && chars.get(idx+1).is_some() {
|
||||
loop {
|
||||
if chars[idx] == '"' && chars[idx-1] != '\\' {
|
||||
break;
|
||||
}
|
||||
buf.push(chars[idx]);
|
||||
if chars[idx] == '\n' {
|
||||
self.loc.inc_line()
|
||||
}
|
||||
self.loc.inc_col();
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
|
||||
let str = self.unescape(&buf);
|
||||
self.loc.inc_col();
|
||||
self.tokens.push(Token::new(TokenType::Instruction(InstructionType::PushStr(str)), start_loc.clone(), buf.clone()));
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
'\'' => {
|
||||
start_loc = self.loc.clone();
|
||||
is_searching = true;
|
||||
idx += 1; // skip '
|
||||
self.loc.col += 1;
|
||||
|
||||
if !buf.is_empty() {
|
||||
debug!({loc => self.loc() }, "buffer was not empty, intresting ({buf})");
|
||||
}
|
||||
|
||||
loop {
|
||||
if chars[idx] == '"' && chars[idx-1] != '\\' {
|
||||
break;
|
||||
}
|
||||
buf.push(chars[idx]);
|
||||
if chars[idx] == '\n' {
|
||||
self.loc.inc_line()
|
||||
}
|
||||
self.loc.inc_col();
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
let str = self.unescape(&&&buf);
|
||||
if str.len() > 1 {
|
||||
error!({loc => self.loc()}, "Chars can only have 1 char");
|
||||
bail!("")
|
||||
}
|
||||
|
||||
self.loc.inc_col();
|
||||
self.tokens.push(Token::new(TokenType::Instruction(InstructionType::PushStr(str)), self.loc(), buf.clone()));
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
ch @ (' ' | '\n' | '\r') => {
|
||||
if ch == '\n' {
|
||||
self.loc.inc_line();
|
||||
} else {
|
||||
self.loc.inc_col();
|
||||
}
|
||||
if !buf.is_empty() {
|
||||
//TODO: Implement signed ints
|
||||
if let Ok(int) = parse_int::parse::<usize>(&buf) {
|
||||
self.tokens.push(Token::new(TokenType::Instruction(InstructionType::PushInt(int)), start_loc.clone(), buf.clone()));
|
||||
} else {
|
||||
let token_type = self.match_token_type(&buf);
|
||||
self.tokens.push(Token::new(token_type, start_loc.clone(), buf.clone()));
|
||||
}
|
||||
|
||||
buf.clear();
|
||||
is_searching = true;
|
||||
}
|
||||
}
|
||||
|
||||
'/' if chars.get(idx + 1) == Some(&'/') => {
|
||||
while chars.get(idx) != Some(&'\n') {
|
||||
self.loc.inc_col();
|
||||
idx += 1;
|
||||
}
|
||||
self.loc.inc_line();
|
||||
}
|
||||
|
||||
|
||||
ch => {
|
||||
if is_searching {
|
||||
is_searching = false;
|
||||
start_loc = self.loc.clone();
|
||||
}
|
||||
|
||||
buf.push(ch);
|
||||
self.loc.inc_col();
|
||||
}
|
||||
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
//? Add last token
|
||||
//TODO: Implement signed ints
|
||||
if !buf.is_empty() {
|
||||
if let Ok(int) = parse_int::parse::<usize>(&buf) {
|
||||
self.tokens.push(Token::new(TokenType::Instruction(InstructionType::PushInt(int)), start_loc.clone(), buf.clone()));
|
||||
} else {
|
||||
let token_type = self.match_token_type(&buf);
|
||||
self.tokens.push(Token::new(token_type, start_loc.clone(), buf.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
// for t in &self.tokens {
|
||||
// debug!({loc => t.loc.clone()}, "token: {:?}", t.typ);
|
||||
// }
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn go_to_first_char(&mut self, chars: &Vec<char>, idx: &mut usize) -> anyhow::Result<()> {
|
||||
loop {
|
||||
if let Some(c) = chars.get(*idx) {
|
||||
match c {
|
||||
' ' | '\r' => self.loc.inc_col(),
|
||||
'\n' => self.loc.inc_line(),
|
||||
_ => break,
|
||||
}
|
||||
*idx += 1;
|
||||
} else {
|
||||
warn!("Empty program");
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn match_token_type(&self, s: &str) -> TokenType {
|
||||
match s {
|
||||
"if" => TokenType::Keyword(KeywordType::If),
|
||||
"else" => TokenType::Keyword(KeywordType::Else),
|
||||
"end" => TokenType::Keyword(KeywordType::End),
|
||||
"while" => TokenType::Keyword(KeywordType::While),
|
||||
"do" => TokenType::Keyword(KeywordType::Do),
|
||||
"include" => TokenType::Keyword(KeywordType::Include),
|
||||
"memory" => TokenType::Keyword(KeywordType::Memory),
|
||||
"const" => TokenType::Keyword(KeywordType::Constant),
|
||||
"fn" => TokenType::Keyword(KeywordType::Function),
|
||||
"then" => TokenType::Keyword(KeywordType::Then),
|
||||
"done" => TokenType::Keyword(KeywordType::Done),
|
||||
"struct" => TokenType::Keyword(KeywordType::Struct),
|
||||
"inline" => TokenType::Keyword(KeywordType::Inline),
|
||||
"export" => TokenType::Keyword(KeywordType::Export),
|
||||
"extern" => TokenType::Keyword(KeywordType::Extern),
|
||||
"returns" => TokenType::Keyword(KeywordType::Returns),
|
||||
"with" => TokenType::Keyword(KeywordType::With),
|
||||
"drop" => TokenType::Instruction(InstructionType::Drop),
|
||||
"_dbg_print"=> TokenType::Instruction(InstructionType::Print),
|
||||
"dup" => TokenType::Instruction(InstructionType::Dup),
|
||||
"rot" => TokenType::Instruction(InstructionType::Rot),
|
||||
"over" => TokenType::Instruction(InstructionType::Over),
|
||||
"swap" => TokenType::Instruction(InstructionType::Swap),
|
||||
"sub" => TokenType::Instruction(InstructionType::Minus),
|
||||
"add" => TokenType::Instruction(InstructionType::Plus),
|
||||
"eq" => TokenType::Instruction(InstructionType::Equals),
|
||||
"gt" => TokenType::Instruction(InstructionType::Gt),
|
||||
"lt" => TokenType::Instruction(InstructionType::Lt),
|
||||
"ge" => TokenType::Instruction(InstructionType::Ge),
|
||||
"le" => TokenType::Instruction(InstructionType::Le),
|
||||
"neq" => TokenType::Instruction(InstructionType::NotEquals),
|
||||
"band" => TokenType::Instruction(InstructionType::Band),
|
||||
"bor" => TokenType::Instruction(InstructionType::Bor),
|
||||
"shr" => TokenType::Instruction(InstructionType::Shr),
|
||||
"shl" => TokenType::Instruction(InstructionType::Shl),
|
||||
"divmod" => TokenType::Instruction(InstructionType::DivMod),
|
||||
"mul" => TokenType::Instruction(InstructionType::Mul),
|
||||
"read8" => TokenType::Instruction(InstructionType::Read8),
|
||||
"write8" => TokenType::Instruction(InstructionType::Write8),
|
||||
"read32" => TokenType::Instruction(InstructionType::Read32),
|
||||
"write32" => TokenType::Instruction(InstructionType::Write32),
|
||||
"read64" => TokenType::Instruction(InstructionType::Read64),
|
||||
"write64" => TokenType::Instruction(InstructionType::Write64),
|
||||
"syscall0" => TokenType::Instruction(InstructionType::Syscall0),
|
||||
"syscall1" => TokenType::Instruction(InstructionType::Syscall1),
|
||||
"syscall2" => TokenType::Instruction(InstructionType::Syscall2),
|
||||
"syscall3" => TokenType::Instruction(InstructionType::Syscall3),
|
||||
"syscall4" => TokenType::Instruction(InstructionType::Syscall4),
|
||||
"syscall5" => TokenType::Instruction(InstructionType::Syscall5),
|
||||
"syscall6" => TokenType::Instruction(InstructionType::Syscall6),
|
||||
"(bool)" => TokenType::Instruction(InstructionType::CastBool),
|
||||
"(ptr)" => TokenType::Instruction(InstructionType::CastPtr),
|
||||
"(int)" => TokenType::Instruction(InstructionType::CastInt),
|
||||
"(void)" => TokenType::Instruction(InstructionType::CastVoid),
|
||||
"bool" => TokenType::Instruction(InstructionType::TypeBool),
|
||||
"ptr" => TokenType::Instruction(InstructionType::TypePtr),
|
||||
"int" => TokenType::Instruction(InstructionType::TypeInt),
|
||||
"void" => TokenType::Instruction(InstructionType::TypeVoid),
|
||||
"any" => TokenType::Instruction(InstructionType::TypeAny),
|
||||
"return" => TokenType::Instruction(InstructionType::Return),
|
||||
t => TokenType::Unknown(t.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self, file: &Path) -> &mut Self {
|
||||
self.loc.file = file.to_string_lossy().to_string();
|
||||
self.loc.line = 1;
|
||||
self.loc.col = 0;
|
||||
self.tokens = Vec::new();
|
||||
self
|
||||
}
|
||||
|
||||
fn loc(&self) -> Loc {
|
||||
self.loc.clone()
|
||||
}
|
||||
fn unescape(&self, s: &String) -> String {
|
||||
//TODO: add more escapes
|
||||
s
|
||||
.replace("\\n", "\n")
|
||||
.replace("\\0", "\0")
|
||||
}
|
||||
|
||||
}
|
||||
32
src/logger/colors.rs
Normal file
32
src/logger/colors.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
#![allow(dead_code)]
|
||||
pub const RESET: &str = "\x1b[0m";
|
||||
pub const BOLD: &str = "\x1b[1m";
|
||||
pub const ITALIC: &str = "\x1b[3m";
|
||||
pub const UNDERLINE: &str = "\x1b[4m";
|
||||
pub const BLINK: &str = "\x1b[5m";
|
||||
pub const BLINK2: &str = "\x1b[6m";
|
||||
pub const SELECTED: &str = "\x1b[7m";
|
||||
pub const BLACK: &str = "\x1b[30m";
|
||||
pub const RED: &str = "\x1b[31m";
|
||||
pub const GREEN: &str = "\x1b[32m";
|
||||
pub const YELLOW: &str = "\x1b[33m";
|
||||
pub const BLUE: &str = "\x1b[34m";
|
||||
pub const MAGENTA: &str = "\x1b[35m";
|
||||
pub const BEIGE: &str = "\x1b[36m";
|
||||
pub const WHITE: &str = "\x1b[37m";
|
||||
pub const BLACKBG: &str = "\x1b[40m";
|
||||
pub const REDBG: &str = "\x1b[41m";
|
||||
pub const GREENBG: &str = "\x1b[42m";
|
||||
pub const YELLOWBG: &str = "\x1b[43m";
|
||||
pub const BLUEBG: &str = "\x1b[44m";
|
||||
pub const MAGENTABG: &str = "\x1b[45m";
|
||||
pub const BEIGEBG: &str = "\x1b[46m";
|
||||
pub const WHITEBG: &str = "\x1b[47m";
|
||||
pub const GREY: &str = "\x1b[90m";
|
||||
pub const RED2: &str = "\x1b[91m";
|
||||
pub const GREEN2: &str = "\x1b[92m";
|
||||
pub const YELLOW2: &str = "\x1b[93m";
|
||||
pub const BLUE2: &str = "\x1b[94m";
|
||||
pub const MAGENTA2: &str = "\x1b[95m";
|
||||
pub const BEIGE2: &str = "\x1b[96m";
|
||||
pub const WHITE2: &str = "\x1b[97m";
|
||||
106
src/logger/macros.rs
Normal file
106
src/logger/macros.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! log {
|
||||
({$($k: expr => $v: expr),* $(,)? }, $lvl:expr, $($arg:tt),+) => {
|
||||
crate::log_tagged!({$($k => $v,)*}, crate::logger::Level::Info, $($arg)+)
|
||||
};
|
||||
(module: $module:expr, $lvl:expr, $($arg:tt)+) => {
|
||||
unsafe {
|
||||
crate::logger::LOGGER.log(
|
||||
crate::logger::LogEvent {
|
||||
level: $lvl,
|
||||
module_path: $module.to_string(),
|
||||
message: format!($($arg)+),
|
||||
tags: std::collections::HashMap::new()
|
||||
}
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
($lvl:expr, $($arg:tt)+) => {
|
||||
crate::log!(module: module_path!(), $lvl, $($arg)+)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! log_tagged {
|
||||
({$($k: expr => $v: expr),* $(,)? }, $module:expr, $lvl:expr, $($arg:tt)+) => {
|
||||
unsafe {
|
||||
crate::logger::LOGGER.log(
|
||||
crate::logger::LogEvent {
|
||||
level: $lvl,
|
||||
module_path: $module.to_string(),
|
||||
message: format!($($arg)+),
|
||||
tags: map_macro::hash_map!{$(stringify!($k).to_string() => Box::new($v) as Box<dyn core::any::Any>,)*}
|
||||
}
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! debug {
|
||||
(module: $module:expr, $($arg:tt)+) => {
|
||||
crate::log!(module: $module, crate::logger::Level::Debug, $($arg:tt)+)
|
||||
};
|
||||
({$($k: expr => $v: expr),* $(,)? }, $($arg:tt)+) => {
|
||||
crate::log_tagged!({$($k => $v,)*}, module_path!(), crate::logger::Level::Debug, $($arg)+)
|
||||
};
|
||||
({$($k: expr => $v: expr),* $(,)? }, module: $module:expr, $($arg:tt)+) => {
|
||||
crate::log_tagged!({$($k => $v,)*}, $module, crate::logger::Level::Debug, $($arg)+)
|
||||
};
|
||||
($($arg:tt)+) => {
|
||||
crate::log!(crate::logger::Level::Debug, $($arg)+)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! info {
|
||||
(module: $module:expr, $($arg:tt)+) => {
|
||||
crate::log!(module: $module, crate::logger::Level::Info, $($arg:tt)+)
|
||||
};
|
||||
({$($k: expr => $v: expr),* $(,)? }, $($arg:tt)+) => {
|
||||
crate::log_tagged!({$($k => $v,)*}, module_path!(), crate::logger::Level::Info, $($arg)+)
|
||||
};
|
||||
({$($k: expr => $v: expr),* $(,)? }, module: $module:expr, $($arg:tt)+) => {
|
||||
crate::log_tagged!({$($k => $v,)*}, $module, crate::logger::Level::Info, $($arg)+)
|
||||
};
|
||||
($($arg:tt)+) => {
|
||||
crate::log!(crate::logger::Level::Info, $($arg)+)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! warn {
|
||||
(module: $module:expr, $($arg:tt)+) => {
|
||||
crate::log!(module: $module, crate::logger::Level::Warn, $($arg:tt)+)
|
||||
};
|
||||
({$($k: expr => $v: expr),* $(,)? }, $($arg:tt)+) => {
|
||||
crate::log_tagged!({$($k => $v,)*}, module_path!(), crate::logger::Level::Warn, $($arg)+)
|
||||
};
|
||||
({$($k: expr => $v: expr),* $(,)? }, module: $module:expr, $($arg:tt)+) => {
|
||||
crate::log_tagged!({$($k => $v,)*}, $module, crate::logger::Level::Warn, $($arg)+)
|
||||
};
|
||||
($($arg:tt)+) => {
|
||||
crate::log!(crate::logger::Level::Warn, $($arg)+)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! error {
|
||||
(module: $module:expr, $($arg:tt)+) => {
|
||||
crate::log!(module: $module, crate::logger::Level::Error, $($arg:tt)+)
|
||||
};
|
||||
({$($k: expr => $v: expr),* $(,)? }, $($arg:tt)+) => {
|
||||
crate::log_tagged!({$($k => $v,)*}, module_path!(), crate::logger::Level::Error, $($arg)+)
|
||||
};
|
||||
({$($k: expr => $v: expr),* $(,)? }, module: $module:expr, $($arg:tt)+) => {
|
||||
crate::log_tagged!({$($k => $v,)*}, $module, crate::logger::Level::Error, $($arg)+)
|
||||
};
|
||||
($($arg:tt)+) => {
|
||||
crate::log!(crate::logger::Level::Error, $($arg)+)
|
||||
};
|
||||
}
|
||||
83
src/logger/mod.rs
Normal file
83
src/logger/mod.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
// use log::{Level, LevelFilter, Metadata, Record, SetLoggerError};
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
use crate::{cli::CliArgs, types::common::Loc};
|
||||
|
||||
mod types;
|
||||
mod colors;
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
pub use types::{Level, LogEvent, LOGGER};
|
||||
use types::*;
|
||||
|
||||
|
||||
pub struct Logger{
|
||||
pub level: i8
|
||||
}
|
||||
|
||||
|
||||
impl Logger {
|
||||
pub fn new(args: &CliArgs) -> Box<Self> {
|
||||
Box::new(Self {
|
||||
level: args.verbose
|
||||
})
|
||||
}
|
||||
|
||||
pub fn init(args: &CliArgs) -> anyhow::Result<()>{
|
||||
unsafe {
|
||||
types::LOGGER = Box::leak(
|
||||
Self::new(args)
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_prefix(&self, level: Level) -> String {
|
||||
use colors::{BOLD, RESET, RED, YELLOW, BLUE, GREEN, MAGENTA};
|
||||
match level {
|
||||
Level::Error => format!("{BOLD}{RED}error{RESET}", ),
|
||||
Level::Warn => format!("{BOLD}{YELLOW}warn{RESET}", ),
|
||||
Level::Info => format!("{BOLD}{GREEN}info{RESET}", ),
|
||||
Level::Debug => format!("{BOLD}{BLUE}debug{RESET}", ),
|
||||
Level::Trace => format!("{BOLD}{MAGENTA}trace{RESET}", ),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Log for Logger {
|
||||
fn enabled(&self, level: Level) -> bool {
|
||||
match level {
|
||||
Level::Error if self.level >= 0 => true,
|
||||
Level::Warn |
|
||||
Level::Info if self.level >= 1 => true,
|
||||
Level::Debug if self.level >= 2 => true,
|
||||
Level::Trace if self.level >= 3 => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
fn log(&self, event: LogEvent) {
|
||||
|
||||
if self.enabled(event.level) {
|
||||
let modpath = if event.level > Level::Info {
|
||||
format!(" [{}]", event.module_path)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
if let Some(loc) = event.tags.get("loc") {
|
||||
let loc: String = (*loc.deref()).downcast_ref::<Loc>()
|
||||
.map_or(String::from("INVALID"), |l| l.to_string());
|
||||
println!("{} {}{modpath}: {}", loc, self.get_prefix(event.level), event.message);
|
||||
} else {
|
||||
println!("{}{modpath}: {}", self.get_prefix(event.level), event.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn level(&self) -> i8 {
|
||||
self.level
|
||||
}
|
||||
}
|
||||
40
src/logger/types.rs
Normal file
40
src/logger/types.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use std::{any::Any, collections::HashMap, fmt::Debug };
|
||||
|
||||
|
||||
pub static mut LOGGER: &dyn Log = &NopLogger;
|
||||
|
||||
struct NopLogger;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub enum Level {
|
||||
Error = 1,
|
||||
Warn,
|
||||
Info,
|
||||
Debug,
|
||||
Trace,
|
||||
}
|
||||
|
||||
// pub trait Tag: Display + Debug + Any {}
|
||||
|
||||
|
||||
pub struct LogEvent {
|
||||
pub level: Level,
|
||||
pub module_path: String,
|
||||
pub message: String,
|
||||
pub tags: HashMap<String, Box<dyn Any>>
|
||||
}
|
||||
|
||||
|
||||
impl Log for NopLogger {
|
||||
fn enabled(&self, _: Level) -> bool {false}
|
||||
fn level(&self) -> i8 {0}
|
||||
fn log(&self, _: LogEvent) {}
|
||||
}
|
||||
|
||||
|
||||
pub trait Log {
|
||||
fn enabled(&self, level: Level) -> bool;
|
||||
fn log(&self, event: LogEvent);
|
||||
fn level(&self) -> i8;
|
||||
}
|
||||
47
src/main.rs
Normal file
47
src/main.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[macro_use]
|
||||
mod logger;
|
||||
mod cli;
|
||||
mod types;
|
||||
mod lexer;
|
||||
pub mod parser;
|
||||
mod compiler;
|
||||
|
||||
fn main() {
|
||||
let cli_args = cli::CliArgs::parse_with_passthrough();
|
||||
logger::Logger::init(&cli_args).expect("Failed to init logger");
|
||||
|
||||
let mut prog_map = HashMap::new();
|
||||
for file in &cli_args.input {
|
||||
let mut lexer = lexer::Lexer::new();
|
||||
|
||||
info!("Lexing file {file:?}");
|
||||
if let Err(_) = lexer.lex(file.as_std_path()) {
|
||||
error!("Lexing failed, exiting");
|
||||
return;
|
||||
}
|
||||
|
||||
// for t in &lexer.tokens {
|
||||
// info!({loc => t.loc.clone()}, "{:?}", t.typ);
|
||||
// }
|
||||
// dbg!(&lexer.tokens);
|
||||
|
||||
info!("Parsing file {file:?}");
|
||||
let prog = match parser::parse(&cli_args, &mut lexer.tokens) {
|
||||
Ok(r) => r,
|
||||
Err(_) => {
|
||||
error!("Parsing failed, exiting");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
prog_map.insert(file.as_std_path(), prog);
|
||||
|
||||
}
|
||||
if let Err(_) = compiler::compile_program(&cli_args, prog_map) {
|
||||
error!("Failed to compile program, exiting");
|
||||
}
|
||||
|
||||
}
|
||||
46
src/parser/builtin.rs
Normal file
46
src/parser/builtin.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use crate::types::{ast::{AstNode, Constant, Module, Program}, common::Loc};
|
||||
|
||||
|
||||
lazy_static!(
|
||||
static ref DEFAULT_CONSTANTS: HashMap<&'static str, AstNode> = {
|
||||
let mut h = HashMap::new();
|
||||
// No bsd cause im not about to create 3 or 4 diffrent compilation targets
|
||||
h.insert("__WINDOWS", AstNode::Int(Loc::default(), cfg!(target_os = "windows") as usize));
|
||||
h.insert("__LINUX", AstNode::Int(Loc::default(), cfg!(target_os = "linux") as usize));
|
||||
h.insert("__ENDIAN_LITTLE", AstNode::Int(Loc::default(), cfg!(target_endian="little") as usize));
|
||||
h.insert("__ENDIAN_BIG", AstNode::Int(Loc::default(), cfg!(target_endian="big") as usize));
|
||||
|
||||
|
||||
h
|
||||
};
|
||||
);
|
||||
|
||||
|
||||
|
||||
pub fn get_builtin_symbols(prog: &mut Program) -> AstNode {
|
||||
let mut md = Module {
|
||||
loc: Loc::new(String::from("BUILTIN"), 0, 0),
|
||||
path: vec![String::from("builtin")],
|
||||
ident: String::from("BUILTIN"),
|
||||
body: Vec::new(),
|
||||
};
|
||||
|
||||
|
||||
|
||||
for (k, v) in DEFAULT_CONSTANTS.iter() {
|
||||
let c = Constant {
|
||||
loc: Loc::default(),
|
||||
ident: k.to_string(),
|
||||
value: Box::from(v.clone()),
|
||||
};
|
||||
prog.constants.insert(k.to_string(), c.clone());
|
||||
md.body.push(AstNode::Constant(c));
|
||||
}
|
||||
|
||||
|
||||
AstNode::Module(md)
|
||||
}
|
||||
551
src/parser/mod.rs
Normal file
551
src/parser/mod.rs
Normal file
@@ -0,0 +1,551 @@
|
||||
mod utils;
|
||||
mod precompiler;
|
||||
mod builtin;
|
||||
|
||||
use std::{collections::HashMap, path::Path};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
use crate::{cli::CliArgs, lexer::Lexer, types::{ast::{AstNode, Block, ConstUse, Constant, FnCall, Function, If, MemUse, Module, Program, While}, common::Loc, token::{InstructionType, KeywordType, Token, TokenType}}};
|
||||
|
||||
use self::{builtin::get_builtin_symbols, precompiler::precompile, utils::{expect, peek_check, peek_check_multiple, PeekResult}};
|
||||
|
||||
|
||||
bitflags::bitflags! {
|
||||
struct Flags: u8 {
|
||||
const EXTERN = 1 << 0;
|
||||
const EXPORT = 1 << 1;
|
||||
const INLINE = 1 << 2;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Implement Module paths
|
||||
pub fn parse(cli_args: &CliArgs, tokens: &mut Vec<Token>) -> Result<Program> {
|
||||
tokens.reverse();
|
||||
let module = Module {
|
||||
loc: Loc::new(&tokens[0].loc.file, 0, 0),
|
||||
ident: Path::new(&tokens[0].loc.file).file_stem().expect("Something went horribly wrong").to_string_lossy().to_string(),
|
||||
body: Vec::new(),
|
||||
path: vec![],
|
||||
};
|
||||
|
||||
|
||||
let mut prog = Program {
|
||||
ast: AstNode::Module(module.clone()),
|
||||
functions: HashMap::new(),
|
||||
constants: HashMap::new(),
|
||||
memories: HashMap::new(),
|
||||
|
||||
};
|
||||
|
||||
let syms = get_builtin_symbols(&mut prog);
|
||||
match &mut prog.ast {
|
||||
AstNode::Module(module) => {
|
||||
module.body.push(syms)
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
|
||||
while !tokens.is_empty() {
|
||||
let node = parse_next(cli_args, &mut prog, tokens, Flags::empty(), true)?;
|
||||
match &mut prog.ast {
|
||||
AstNode::Module(module) => {
|
||||
module.body.push(node);
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
// prog.ast = module;
|
||||
|
||||
Ok(prog)
|
||||
}
|
||||
|
||||
fn parse_next(cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>, flags: Flags, is_module_root: bool) -> Result<AstNode> {
|
||||
let token = tokens.pop().expect("We broke reality!");
|
||||
// debug!({loc => token.loc.clone()}, "t: {:?}", token.typ);
|
||||
let ret = match &token.typ {
|
||||
TokenType::Keyword(kw) => {
|
||||
match kw {
|
||||
KeywordType::If => parse_if(&token, cli_args, prog, tokens)?,
|
||||
KeywordType::While => parse_while(&token, cli_args, prog, tokens)?,
|
||||
KeywordType::Include => parse_include(&token, cli_args, prog, tokens)?, //TODO: implement include
|
||||
KeywordType::Memory => todo!(),
|
||||
KeywordType::Constant => parse_const(&token, cli_args, prog, tokens)?,
|
||||
KeywordType::Function => parse_function(&token, cli_args, prog, tokens, flags)?,
|
||||
KeywordType::Struct => todo!(),
|
||||
KeywordType::Inline => parse_inline(&token, cli_args, prog, tokens, flags)?,
|
||||
KeywordType::Export => parse_export(&token, cli_args, prog, tokens, flags)?,
|
||||
KeywordType::Extern => parse_extern(&token, cli_args, prog, tokens, flags)?,
|
||||
kw => {
|
||||
dbg!(&prog.constants);
|
||||
error!({loc => token.loc}, "Unexpected token {kw:?}");
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
},
|
||||
TokenType::Instruction(it) => {
|
||||
if is_module_root {
|
||||
error!({loc => token.loc}, "Unexpected token {it:?}, please create a main function, this is not a scripting language");
|
||||
bail!("")
|
||||
} else {
|
||||
AstNode::Token(token)
|
||||
}
|
||||
},
|
||||
TokenType::Unknown(ut) => {
|
||||
if is_module_root {
|
||||
error!({loc => token.loc}, "Unexpected token {ut:?}, please create a main function, this is not a scripting language");
|
||||
bail!("")
|
||||
} else {
|
||||
// AstNode::Token(token)
|
||||
parse_unknown(&token, cli_args, prog, tokens, flags)?
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
// TODO: Extern functions
|
||||
fn parse_function(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>, flags: Flags ) -> Result<AstNode> {
|
||||
|
||||
|
||||
let name = expect(tokens, TokenType::Unknown(String::new()))?;
|
||||
expect(tokens, TokenType::Keyword(KeywordType::With))?;
|
||||
let mut args = Vec::new();
|
||||
|
||||
loop {
|
||||
if let PeekResult::Correct(t) = peek_check_multiple(tokens, vec![
|
||||
TokenType::Instruction(InstructionType::TypeAny),
|
||||
TokenType::Instruction(InstructionType::TypeBool),
|
||||
TokenType::Instruction(InstructionType::TypeInt),
|
||||
TokenType::Instruction(InstructionType::TypePtr),
|
||||
TokenType::Instruction(InstructionType::TypeVoid),
|
||||
]) {
|
||||
args.push(t.typ.clone());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
tokens.pop();
|
||||
}
|
||||
|
||||
expect(tokens, TokenType::Keyword(KeywordType::Returns))?;
|
||||
|
||||
let mut ret_args = Vec::new();
|
||||
|
||||
loop {
|
||||
if let PeekResult::Correct(t) = peek_check_multiple(tokens, vec![
|
||||
TokenType::Instruction(InstructionType::TypeAny),
|
||||
TokenType::Instruction(InstructionType::TypeBool),
|
||||
TokenType::Instruction(InstructionType::TypeInt),
|
||||
TokenType::Instruction(InstructionType::TypePtr),
|
||||
TokenType::Instruction(InstructionType::TypeVoid),
|
||||
]) {
|
||||
ret_args.push(t.typ.clone());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
tokens.pop();
|
||||
}
|
||||
|
||||
|
||||
expect(tokens, TokenType::Keyword(KeywordType::Then))?;
|
||||
let mut body = Vec::new();
|
||||
loop {
|
||||
|
||||
let fn_got = peek_check(tokens, TokenType::Keyword(KeywordType::Done));
|
||||
match fn_got {
|
||||
PeekResult::Correct(_) => break,
|
||||
PeekResult::Wrong(_) => (),
|
||||
PeekResult::None => panic!("idk what to do herre"),
|
||||
}
|
||||
body.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
|
||||
}
|
||||
expect(tokens, TokenType::Keyword(KeywordType::Done))?;
|
||||
|
||||
let fn_def = Function {
|
||||
loc: org.loc(),
|
||||
inline: flags.contains(Flags::INLINE),
|
||||
extrn: flags.contains(Flags::EXTERN),
|
||||
export: flags.contains(Flags::EXPORT),
|
||||
ident: name.lexem.clone(),
|
||||
arg_types: args,
|
||||
ret_types: ret_args,
|
||||
body,
|
||||
};
|
||||
//TODO: Support module paths without double definitions
|
||||
// let mut mp = match &prog.ast {
|
||||
// AstNode::Module(m) => {
|
||||
// m.path.clone()
|
||||
// }
|
||||
// _ => panic!("")
|
||||
// };
|
||||
// mp.push(name.lexem.clone());
|
||||
// let mp = mp.join("::");
|
||||
|
||||
// prog.function_aliases.insert(mp, name.lexem.clone());
|
||||
prog.functions.insert(name.lexem.clone(), fn_def.clone());
|
||||
Ok(AstNode::Function(fn_def))
|
||||
}
|
||||
|
||||
fn parse_if(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>) -> Result<AstNode> {
|
||||
let mut test: Vec<AstNode> = Vec::new();
|
||||
let mut body: Vec<AstNode> = Vec::new();
|
||||
let mut els: Vec<AstNode> = Vec::new();
|
||||
loop {
|
||||
test.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
|
||||
match peek_check(tokens, TokenType::Keyword(KeywordType::Do)) {
|
||||
PeekResult::Correct(_) => break,
|
||||
PeekResult::Wrong(w) => {
|
||||
match w.typ {
|
||||
TokenType::Keyword(KeywordType::Then) => {
|
||||
warn!("If is defined as `if ... do ... done`");
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
},
|
||||
PeekResult::None => panic!("idk what to do herre"),
|
||||
}
|
||||
}
|
||||
|
||||
expect(tokens, TokenType::Keyword(KeywordType::Do))?;
|
||||
|
||||
|
||||
|
||||
loop {
|
||||
body.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
|
||||
match peek_check_multiple(tokens, vec![
|
||||
TokenType::Keyword(KeywordType::Else),
|
||||
TokenType::Keyword(KeywordType::Done),
|
||||
]) {
|
||||
PeekResult::Correct(_) => break,
|
||||
PeekResult::Wrong(_) => (),
|
||||
PeekResult::None => panic!("idk what to do herre"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let els_t = tokens.last().expect("IMPOSSIBLEEE!!!!!!111").clone();
|
||||
let els = match els_t.typ.clone() {
|
||||
TokenType::Keyword(kw) => {
|
||||
match kw {
|
||||
KeywordType::Done => {
|
||||
expect(tokens, TokenType::Keyword(KeywordType::Done))?;
|
||||
AstNode::Block(Block{
|
||||
comment: String::new(),
|
||||
loc: els_t.loc,
|
||||
body: Vec::new(),
|
||||
})
|
||||
},
|
||||
KeywordType::Else => {
|
||||
expect(tokens, TokenType::Keyword(KeywordType::Else))?;
|
||||
if peek_check(tokens, TokenType::Keyword(KeywordType::If)).correct() {
|
||||
let if_org =expect(tokens, TokenType::Keyword(KeywordType::If))?;
|
||||
parse_if(&if_org, cli_args, prog, tokens)?
|
||||
} else {
|
||||
loop {
|
||||
els.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
|
||||
match peek_check(tokens, TokenType::Keyword(KeywordType::Done)) {
|
||||
PeekResult::Correct(_) => break,
|
||||
PeekResult::Wrong(w) => {
|
||||
match w.typ {
|
||||
TokenType::Keyword(KeywordType::Then) => {
|
||||
warn!("If is defined as `if ... do ... done`");
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
},
|
||||
PeekResult::None => panic!("idk what to do herre"),
|
||||
}
|
||||
}
|
||||
expect(tokens, TokenType::Keyword(KeywordType::Done))?;
|
||||
|
||||
AstNode::Block(Block{
|
||||
comment: String::new(),
|
||||
loc: els_t.loc,
|
||||
body: els,
|
||||
})
|
||||
}
|
||||
},
|
||||
e => {
|
||||
error!({loc => els_t.loc.clone()}, "Expected {:?} or {:?} but got {:?}", KeywordType::Done, KeywordType::Else, e);
|
||||
bail!("");
|
||||
}
|
||||
}
|
||||
},
|
||||
e => {
|
||||
error!({loc => els_t.loc.clone()}, "Expected {:?} or {:?} but got {:?}", KeywordType::Done, KeywordType::Else, e);
|
||||
bail!("");
|
||||
}
|
||||
};
|
||||
Ok(AstNode::If(If{
|
||||
test,
|
||||
body,
|
||||
els: Box::new(els),
|
||||
loc: org.loc(),
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
fn parse_while(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>) -> Result<AstNode> {
|
||||
let mut test: Vec<AstNode> = Vec::new();
|
||||
let mut body: Vec<AstNode> = Vec::new();
|
||||
loop {
|
||||
test.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
|
||||
match peek_check(tokens, TokenType::Keyword(KeywordType::Do)) {
|
||||
PeekResult::Correct(_) => break,
|
||||
PeekResult::Wrong(w) => {
|
||||
match w.typ {
|
||||
TokenType::Keyword(KeywordType::Then) => {
|
||||
warn!("while is defined as `while ... do ... done`");
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
},
|
||||
PeekResult::None => panic!("idk what to do herre"),
|
||||
}
|
||||
}
|
||||
|
||||
expect(tokens, TokenType::Keyword(KeywordType::Do))?;
|
||||
|
||||
|
||||
|
||||
loop {
|
||||
body.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
|
||||
match peek_check_multiple(tokens, vec![
|
||||
TokenType::Keyword(KeywordType::Else),
|
||||
TokenType::Keyword(KeywordType::Done),
|
||||
]) {
|
||||
PeekResult::Correct(_) => break,
|
||||
PeekResult::Wrong(_) => (),
|
||||
PeekResult::None => panic!("idk what to do herre"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
expect(tokens, TokenType::Keyword(KeywordType::Done))?;
|
||||
|
||||
Ok(AstNode::While(While{
|
||||
test,
|
||||
body,
|
||||
loc: org.loc(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_inline(_: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>, flags: Flags) -> Result<AstNode> {
|
||||
let allowed_tokens = vec!{
|
||||
TokenType::Keyword(KeywordType::Function)
|
||||
};
|
||||
|
||||
|
||||
let Some(t) = tokens.last() else {
|
||||
error!("Expected one of {:?} after {:?} but found nothing", allowed_tokens, TokenType::Keyword(KeywordType::Inline));
|
||||
bail!("")
|
||||
};
|
||||
|
||||
|
||||
let mut found = false;
|
||||
|
||||
for at in &allowed_tokens {
|
||||
if utils::cmp(at, &t.typ) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
error!({loc => t.loc.clone()}, "Expected one of {:?} after {:?} but found {:?}", allowed_tokens, TokenType::Keyword(KeywordType::Inline), t.typ);
|
||||
bail!("");
|
||||
}
|
||||
|
||||
|
||||
parse_next(cli_args, prog, tokens, flags | Flags::INLINE, false)
|
||||
}
|
||||
|
||||
fn parse_extern(_: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>, flags: Flags) -> Result<AstNode> {
|
||||
let allowed_tokens = vec!{
|
||||
TokenType::Keyword(KeywordType::Function),
|
||||
TokenType::Keyword(KeywordType::Constant),
|
||||
TokenType::Keyword(KeywordType::Memory),
|
||||
};
|
||||
|
||||
|
||||
let Some(t) = tokens.last() else {
|
||||
error!("Expected one of {:?} after {:?} but found nothing", allowed_tokens, TokenType::Keyword(KeywordType::Extern));
|
||||
bail!("")
|
||||
};
|
||||
|
||||
|
||||
let mut found = false;
|
||||
|
||||
for at in &allowed_tokens {
|
||||
if utils::cmp(at, &t.typ) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
error!({loc => t.loc.clone()}, "Expected one of {:?} after {:?} but found {:?}", allowed_tokens, TokenType::Keyword(KeywordType::Extern), t.typ);
|
||||
bail!("");
|
||||
}
|
||||
|
||||
|
||||
parse_next(cli_args, prog, tokens, flags | Flags::EXTERN, false)
|
||||
}
|
||||
|
||||
fn parse_export(_: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>, flags: Flags) -> Result<AstNode> {
|
||||
let allowed_tokens = vec!{
|
||||
TokenType::Keyword(KeywordType::Function),
|
||||
TokenType::Keyword(KeywordType::Constant),
|
||||
TokenType::Keyword(KeywordType::Memory),
|
||||
};
|
||||
|
||||
|
||||
let Some(t) = tokens.last() else {
|
||||
error!("Expected one of {:?} after {:?} but found nothing", allowed_tokens, TokenType::Keyword(KeywordType::Export));
|
||||
bail!("")
|
||||
};
|
||||
|
||||
|
||||
let mut found = false;
|
||||
|
||||
for at in &allowed_tokens {
|
||||
if utils::cmp(at, &t.typ) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
error!({loc => t.loc.clone()}, "Expected one of {:?} after {:?} but found {:?}", allowed_tokens, TokenType::Keyword(KeywordType::Export), t.typ);
|
||||
bail!("");
|
||||
}
|
||||
|
||||
|
||||
parse_next(cli_args, prog, tokens, flags | Flags::EXPORT, false)
|
||||
}
|
||||
|
||||
|
||||
fn parse_include(_: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>) -> Result<AstNode> {
|
||||
let path = expect(tokens,
|
||||
TokenType::Instruction(
|
||||
InstructionType::PushStr(
|
||||
String::new()
|
||||
)
|
||||
)
|
||||
)?;
|
||||
|
||||
for ip in &cli_args.include_path {
|
||||
let p = ip.join(&path.lexem).to_path_buf();
|
||||
if p.exists() {
|
||||
info!({loc => path.loc.clone()}, "Lexing file {}", path.lexem.clone());
|
||||
let mut lexer = Lexer::new();
|
||||
lexer.lex(p.as_std_path())?;
|
||||
|
||||
let mut mod_tokens = lexer.tokens;
|
||||
|
||||
mod_tokens.reverse();
|
||||
|
||||
let mut mp = match &prog.ast {
|
||||
AstNode::Module(m) => {
|
||||
m.path.clone()
|
||||
}
|
||||
_ => panic!("")
|
||||
};
|
||||
|
||||
mp.push(p.file_stem().unwrap().to_string());
|
||||
|
||||
let module = Module {
|
||||
loc: Loc::new(path.loc.file.clone(), 0, 0),
|
||||
ident: Path::new(&path.loc.file).file_stem().expect("Something went horribly wrong").to_string_lossy().to_string(),
|
||||
body: Vec::new(),
|
||||
path: mp,
|
||||
};
|
||||
|
||||
|
||||
let mut mod_prog = Program {
|
||||
ast: AstNode::Module(module),
|
||||
functions: prog.functions.clone(),
|
||||
constants: prog.constants.clone(),
|
||||
memories: prog.memories.clone(),
|
||||
|
||||
};
|
||||
|
||||
info!({loc => path.loc.clone()}, "Parsing file {}", path.lexem.clone());
|
||||
while !mod_tokens.is_empty() {
|
||||
let node = parse_next(cli_args, &mut mod_prog, &mut mod_tokens, Flags::empty(), true)?;
|
||||
match &mut mod_prog.ast {
|
||||
AstNode::Module(module) => {
|
||||
module.body.push(node);
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
prog.constants = mod_prog.constants;
|
||||
prog.functions = mod_prog.functions;
|
||||
prog.memories = mod_prog.memories;
|
||||
return Ok(mod_prog.ast)
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
error!("Could not find file {:?} in these locations: {:?}", path.lexem, cli_args.include_path);
|
||||
bail!("")
|
||||
|
||||
}
|
||||
|
||||
fn parse_const(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>) -> Result<AstNode> {
|
||||
let name = expect(tokens, TokenType::Unknown(String::new()))?;
|
||||
|
||||
|
||||
let mut body = Vec::new();
|
||||
loop {
|
||||
|
||||
let t = peek_check(tokens, TokenType::Keyword(KeywordType::End));
|
||||
match t {
|
||||
PeekResult::Correct(_) => break,
|
||||
PeekResult::Wrong(_) => (),
|
||||
PeekResult::None => panic!("idk what to do herre"),
|
||||
}
|
||||
body.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
|
||||
}
|
||||
expect(tokens, TokenType::Keyword(KeywordType::End))?;
|
||||
|
||||
let val = precompile(prog, body, &mut Vec::new())?;
|
||||
|
||||
let name = name.lexem.clone()
|
||||
.replace("(", "_OPRN_")
|
||||
.replace(")", "_CPRN_");
|
||||
|
||||
let def = Constant{
|
||||
loc: org.loc(),
|
||||
ident: name.clone(),
|
||||
value: Box::new(val),
|
||||
};
|
||||
|
||||
|
||||
prog.constants.insert(name, def.clone());
|
||||
|
||||
Ok(AstNode::Constant(def))
|
||||
}
|
||||
|
||||
fn parse_unknown(org: &Token, _: &CliArgs, prog: &mut Program, _: &mut Vec<Token>, _: Flags ) -> Result<AstNode> {
|
||||
//TODO: Typing?
|
||||
if let Some(func) = prog.functions.get(&org.lexem) {
|
||||
if func.inline {
|
||||
return Ok(AstNode::Block(Block{ loc: org.loc.clone(), body: func.body.clone(), comment: format!("inline fn {}", func.ident) }))
|
||||
} else {
|
||||
return Ok(AstNode::FnCall(FnCall{ loc: org.loc.clone(), ident: org.lexem.clone() }));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(_) = prog.constants.get(&org.lexem) {
|
||||
return Ok(AstNode::ConstUse(ConstUse{ loc: org.loc.clone(), ident: org.lexem.clone() }));
|
||||
}
|
||||
|
||||
if let Some(_) = prog.memories.get(&org.lexem) {
|
||||
return Ok(AstNode::MemUse(MemUse{ loc: org.loc.clone(), ident: org.lexem.clone() }));
|
||||
}
|
||||
|
||||
dbg!(&prog.constants);
|
||||
error!({loc => org.loc.clone()}, "Unknown token {:?}", org);
|
||||
bail!("")
|
||||
}
|
||||
154
src/parser/precompiler.rs
Normal file
154
src/parser/precompiler.rs
Normal file
@@ -0,0 +1,154 @@
|
||||
use anyhow::bail;
|
||||
|
||||
use crate::types::{ast::{AstNode, Program}, common::Loc, token::{InstructionType, TokenType}};
|
||||
|
||||
|
||||
pub fn precompile(prog: &Program, ast: Vec<AstNode>, stack: &mut Vec<usize> ) -> anyhow::Result<AstNode> {
|
||||
for node in ast.clone() {
|
||||
match &node {
|
||||
AstNode::ConstUse(c) => {
|
||||
let Some(val) = prog.constants.get(&c.ident) else {
|
||||
error!({loc => c.loc.clone()}, "Unknown constant {:?}", c.ident) ;
|
||||
bail!("")
|
||||
};
|
||||
match Box::leak(val.value.clone()) {
|
||||
t @ AstNode::Int(..) => {
|
||||
return Ok(t.clone());
|
||||
}
|
||||
|
||||
t @ AstNode::Str(..) => {
|
||||
return Ok(t.clone());
|
||||
}
|
||||
|
||||
t @ AstNode::CStr(..) => {
|
||||
return Ok(t.clone());
|
||||
}
|
||||
|
||||
t @ AstNode::Char(..) => {
|
||||
return Ok(t.clone());
|
||||
}
|
||||
|
||||
|
||||
// AstNode::Token(t) => {
|
||||
// match t.typ.clone() {
|
||||
// TokenType::Instruction(it) => {
|
||||
// match it {
|
||||
// InstructionType::PushInt(i) => stack.push(i),
|
||||
// InstructionType::PushCStr(_) => {
|
||||
// //TODO: Handle this better
|
||||
// return Ok(AstNode::Token(t.clone()));
|
||||
// },
|
||||
// InstructionType::PushChar(_) => {
|
||||
// //TODO: Handle this better
|
||||
// return Ok(AstNode::Token(t.clone()));
|
||||
// },
|
||||
// _ => panic!()
|
||||
// }
|
||||
// },
|
||||
// _ => panic!()
|
||||
// }
|
||||
// },
|
||||
_ => panic!()
|
||||
}
|
||||
|
||||
},
|
||||
AstNode::Token(t) => {
|
||||
match t.typ.clone() {
|
||||
TokenType::Keyword(_) => {
|
||||
error!({loc => t.loc.clone()}, "Unsupported token {t:?}, we dont support precompilation of this") ;
|
||||
bail!("")
|
||||
},
|
||||
TokenType::Instruction(it) => {
|
||||
match it {
|
||||
InstructionType::PushInt(i) => {
|
||||
stack.push(i);
|
||||
},
|
||||
InstructionType::PushCStr(s) => {
|
||||
//TODO: Handle this better
|
||||
return Ok(AstNode::CStr(t.loc.clone(), s));
|
||||
},
|
||||
InstructionType::PushStr(s) => {
|
||||
//TODO: Handle this better
|
||||
return Ok(AstNode::Str(t.loc.clone(), s));
|
||||
},
|
||||
InstructionType::PushChar(c) => {
|
||||
//TODO: Handle this better
|
||||
return Ok(AstNode::Char(t.loc.clone(), c));
|
||||
},
|
||||
InstructionType::Minus => {
|
||||
let a = stack_pop(stack, &t.loc)?;
|
||||
let b = stack_pop(stack, &t.loc)?;
|
||||
stack.push(b - a);
|
||||
},
|
||||
InstructionType::Plus => {
|
||||
let a = stack_pop(stack, &t.loc)?;
|
||||
let b = stack_pop(stack, &t.loc)?;
|
||||
stack.push(b + a);
|
||||
},
|
||||
InstructionType::DivMod => {
|
||||
let a = stack_pop(stack, &t.loc)?;
|
||||
let b = stack_pop(stack, &t.loc)?;
|
||||
stack.push(b / a);
|
||||
stack.push(b % a);
|
||||
},
|
||||
InstructionType::Mul => {
|
||||
let a = stack_pop(stack, &t.loc)?;
|
||||
let b = stack_pop(stack, &t.loc)?;
|
||||
stack.push(b * a);
|
||||
},
|
||||
InstructionType::Drop => {
|
||||
stack_pop(stack, &t.loc)?;
|
||||
},
|
||||
//TODO: Support these later
|
||||
// InstructionType::Dup => todo!(),
|
||||
// InstructionType::Rot => todo!(),
|
||||
// InstructionType::Over => todo!(),
|
||||
// InstructionType::Swap => todo!(),
|
||||
// InstructionType::Equals => todo!(),
|
||||
// InstructionType::Gt => todo!(),
|
||||
// InstructionType::Lt => todo!(),
|
||||
// InstructionType::Ge => todo!(),
|
||||
// InstructionType::Le => todo!(),
|
||||
// InstructionType::NotEquals => todo!(),
|
||||
// InstructionType::Band => todo!(),
|
||||
// InstructionType::Bor => todo!(),
|
||||
// InstructionType::Shr => todo!(),
|
||||
// InstructionType::Shl => todo!(),
|
||||
//TODO: Support this when we have types
|
||||
// InstructionType::CastBool => todo!(),
|
||||
// InstructionType::CastPtr => todo!(),
|
||||
// InstructionType::CastInt => todo!(),
|
||||
// InstructionType::CastVoid => todo!(),
|
||||
InstructionType::ConstUse => unreachable!(),
|
||||
_ => {
|
||||
error!({loc => t.loc.clone()}, "Unsupported token {t:?}, we dont support precompilation of this") ;
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
},
|
||||
TokenType::Unknown(_) => todo!(),
|
||||
}
|
||||
},
|
||||
//TODO: Implement these
|
||||
t @ AstNode::If { .. } |
|
||||
t @ AstNode::While { .. } |
|
||||
t => {
|
||||
error!({loc => t.loc()}, "Unsupported token {t:?}, we dont support precompilation of this") ;
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Ok(AstNode::Int(ast[0].loc(), stack[0]))
|
||||
}
|
||||
|
||||
fn stack_pop(stack: &mut Vec<usize>, loc: &Loc) -> anyhow::Result<usize> {
|
||||
match stack.pop() {
|
||||
Some(i) => Ok(i),
|
||||
None => {
|
||||
error!({loc => loc.clone()}, "Failed to precompile tokens, failed to pop from stack");
|
||||
bail!("")
|
||||
},
|
||||
}
|
||||
}
|
||||
100
src/parser/utils.rs
Normal file
100
src/parser/utils.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
use crate::types::token::{Token, TokenType};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||
pub enum PeekResult<T> {
|
||||
Correct(T),
|
||||
Wrong(T),
|
||||
None
|
||||
}
|
||||
|
||||
impl<T> PeekResult<T> {
|
||||
pub fn correct(&self) -> bool{
|
||||
match self {
|
||||
PeekResult::Correct(_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn wrong(&self) -> bool{
|
||||
match self {
|
||||
PeekResult::Wrong(_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn none(&self) -> bool{
|
||||
match self {
|
||||
PeekResult::None => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cmp(lhs: &TokenType, rhs: &TokenType) -> bool {
|
||||
match (lhs, rhs) {
|
||||
(TokenType::Keyword(lhs), TokenType::Keyword(rhs)) => {
|
||||
std::mem::discriminant(lhs) == std::mem::discriminant(rhs)
|
||||
},
|
||||
(TokenType::Instruction(lhs), TokenType::Instruction(rhs)) => {
|
||||
std::mem::discriminant(lhs) == std::mem::discriminant(rhs)
|
||||
},
|
||||
(TokenType::Unknown(_), TokenType::Unknown(_)) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn peek_check_multiple(tokens: &Vec<Token>, typs: Vec<TokenType>) -> PeekResult<&Token>{
|
||||
let t = tokens.last();
|
||||
|
||||
if let Some(t) = t {
|
||||
for tt in typs {
|
||||
if cmp(&t.typ, &tt) {
|
||||
return PeekResult::Correct(t);
|
||||
}
|
||||
}
|
||||
PeekResult::Wrong(t)
|
||||
} else {
|
||||
PeekResult::None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn peek_check(tokens: &Vec<Token>, typ: TokenType) -> PeekResult<&Token> {
|
||||
let t = tokens.last();
|
||||
|
||||
match t {
|
||||
Some(t) => {
|
||||
//? Source: https://doc.rust-lang.org/std/mem/fn.discriminant.html
|
||||
if cmp(&t.typ, &typ) {
|
||||
PeekResult::Correct(t)
|
||||
} else {
|
||||
PeekResult::Wrong(t)
|
||||
}
|
||||
},
|
||||
None => {
|
||||
PeekResult::None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect(tokens: &mut Vec<Token>, typ: TokenType) -> Result<Token> {
|
||||
let t = tokens.pop();
|
||||
|
||||
match t {
|
||||
Some(t) => {
|
||||
//? Source: https://doc.rust-lang.org/std/mem/fn.discriminant.html
|
||||
if std::mem::discriminant(&t.typ) != std::mem::discriminant(&typ) {
|
||||
error!("Expected {:?}, but got {:?}", typ, t.typ);
|
||||
bail!("")
|
||||
}
|
||||
Ok(t)
|
||||
},
|
||||
None => {
|
||||
error!("Expected {:?}, but found nothing", typ);
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
148
src/types/ast/mod.rs
Normal file
148
src/types/ast/mod.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::{common::Loc, token::{Token, TokenType}};
|
||||
|
||||
|
||||
//TODO: Implement missing stuff
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AstNode {
|
||||
Int(Loc, usize),
|
||||
Str(Loc, String),
|
||||
CStr(Loc, String),
|
||||
Char(Loc, char),
|
||||
// ExternFnDef {
|
||||
// loc: Loc,
|
||||
// ident: String,
|
||||
// arg_types: Vec<TokenType>,
|
||||
// ret_type: TokenType,
|
||||
// },
|
||||
Function(Function),
|
||||
Constant(Constant),
|
||||
// ExternConstantDef{
|
||||
// loc: Loc,
|
||||
// ident: String,
|
||||
// value: InstructionType
|
||||
// },
|
||||
// Struct{
|
||||
// loc: Loc,
|
||||
// ident: String,
|
||||
// body: Vec<(String, usize)> // (field ident, size in bytes)
|
||||
// },
|
||||
// StructDef{
|
||||
// loc: Loc,
|
||||
// extrn: bool,
|
||||
// ident: String,
|
||||
// body: Vec<(String, usize)> // (field ident, size in bytes)
|
||||
// },
|
||||
If(If),
|
||||
While(While),
|
||||
Module(Module),
|
||||
Memory(Memory),
|
||||
MemUse(MemUse),
|
||||
ConstUse(ConstUse),
|
||||
FnCall(FnCall),
|
||||
Block(Block),
|
||||
Token(Token),
|
||||
}
|
||||
|
||||
impl AstNode {
|
||||
pub fn loc(&self) -> Loc {
|
||||
match self {
|
||||
AstNode::Function(f) => f.loc.clone(),
|
||||
AstNode::Constant(c) => c.loc.clone(),
|
||||
AstNode::If(t)=> t.loc.clone(),
|
||||
AstNode::While(t)=> t.loc.clone(),
|
||||
AstNode::Module(m) => m.loc.clone(),
|
||||
AstNode::Memory(m) => m.loc.clone(),
|
||||
AstNode::MemUse(t)=> t.loc.clone(),
|
||||
AstNode::ConstUse(t)=> t.loc.clone(),
|
||||
AstNode::FnCall(t)=> t.loc.clone(),
|
||||
AstNode::Block(t)=> t.loc.clone(),
|
||||
AstNode::Token(tok) => tok.loc.clone(),
|
||||
AstNode::Int(loc, _) => loc.clone(),
|
||||
AstNode::Str(loc, _) => loc.clone(),
|
||||
AstNode::CStr(loc, _) => loc.clone(),
|
||||
AstNode::Char(loc, _) => loc.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MemUse {
|
||||
pub loc: Loc,
|
||||
pub ident: String,
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConstUse {
|
||||
pub loc: Loc,
|
||||
pub ident: String,
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FnCall {
|
||||
pub loc: Loc,
|
||||
pub ident: String,
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Block {
|
||||
pub comment: String,
|
||||
pub loc: Loc,
|
||||
pub body: Vec<AstNode>
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct While {
|
||||
pub loc: Loc,
|
||||
pub test: Vec<AstNode>,
|
||||
pub body: Vec<AstNode>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct If {
|
||||
pub loc: Loc,
|
||||
pub test: Vec<AstNode>,
|
||||
pub body: Vec<AstNode>,
|
||||
pub els: Box<AstNode>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Module {
|
||||
pub loc: Loc,
|
||||
pub path: Vec<String>,
|
||||
pub ident: String,
|
||||
pub body: Vec<AstNode>
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Function {
|
||||
pub loc: Loc,
|
||||
pub ident: String,
|
||||
pub inline: bool,
|
||||
pub extrn: bool,
|
||||
pub export: bool,
|
||||
pub arg_types: Vec<TokenType>,
|
||||
pub ret_types: Vec<TokenType>,
|
||||
pub body: Vec<AstNode>
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Constant {
|
||||
pub loc: Loc,
|
||||
pub ident: String,
|
||||
pub value: Box<AstNode>
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Memory {
|
||||
pub loc: Loc,
|
||||
pub ident: String,
|
||||
pub size: usize // bytes
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Program {
|
||||
pub ast: AstNode,
|
||||
pub functions: HashMap<String, Function>,
|
||||
pub constants: HashMap<String, Constant>,
|
||||
pub memories: HashMap<String, Memory>,
|
||||
}
|
||||
37
src/types/common.rs
Normal file
37
src/types/common.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq)]
|
||||
pub struct Loc {
|
||||
pub file: String,
|
||||
pub line: usize,
|
||||
pub col: usize
|
||||
}
|
||||
|
||||
|
||||
impl Loc {
|
||||
pub fn new<T: Into<String>>(f: T, line: usize, col: usize) -> Self {
|
||||
Self {
|
||||
file: f.into(),
|
||||
line,
|
||||
col,
|
||||
}
|
||||
}
|
||||
pub fn inc_line(&mut self) {
|
||||
self.line += 1;
|
||||
self.col = 0;
|
||||
}
|
||||
|
||||
pub fn inc_col(&mut self) {
|
||||
self.col += 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Loc {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}:{}:{}", self.file, self.line, self.col)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
3
src/types/mod.rs
Normal file
3
src/types/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod common;
|
||||
pub mod token;
|
||||
pub mod ast;
|
||||
120
src/types/token/mod.rs
Normal file
120
src/types/token/mod.rs
Normal file
@@ -0,0 +1,120 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use super::common::Loc;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum InstructionType {
|
||||
|
||||
// stack
|
||||
PushInt(usize),
|
||||
PushStr(String),
|
||||
PushCStr(String),
|
||||
PushChar(char),
|
||||
Drop,
|
||||
Print,
|
||||
Dup,
|
||||
Rot, // a b c => b c a
|
||||
Over, // a b => a b a
|
||||
Swap, // a b => b a
|
||||
|
||||
// math
|
||||
Minus,
|
||||
Plus,
|
||||
Equals,
|
||||
Gt,
|
||||
Lt,
|
||||
Ge,
|
||||
Le,
|
||||
NotEquals,
|
||||
Band, // &
|
||||
Bor, // |
|
||||
Shr, // >>
|
||||
Shl, // <<
|
||||
DivMod, // /
|
||||
Mul,
|
||||
|
||||
|
||||
// mem
|
||||
Read8,
|
||||
Write8,
|
||||
Read32,
|
||||
Write32,
|
||||
Read64,
|
||||
Write64,
|
||||
|
||||
// syscalls
|
||||
Syscall0,
|
||||
Syscall1,
|
||||
Syscall2,
|
||||
Syscall3,
|
||||
Syscall4,
|
||||
Syscall5,
|
||||
Syscall6,
|
||||
|
||||
CastBool,
|
||||
CastPtr,
|
||||
CastInt,
|
||||
CastVoid,
|
||||
|
||||
// typing
|
||||
TypeBool,
|
||||
TypePtr,
|
||||
TypeInt,
|
||||
TypeVoid,
|
||||
// TypeStr,
|
||||
TypeAny,
|
||||
|
||||
FnCall,
|
||||
MemUse,
|
||||
ConstUse,
|
||||
|
||||
Return,
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum KeywordType {
|
||||
If,
|
||||
Else,
|
||||
End,
|
||||
While,
|
||||
Do,
|
||||
Include,
|
||||
Memory,
|
||||
Constant,
|
||||
Function,
|
||||
Then,
|
||||
Done,
|
||||
Struct,
|
||||
Inline,
|
||||
Export,
|
||||
Extern,
|
||||
Returns,
|
||||
With,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum TokenType {
|
||||
Keyword(KeywordType),
|
||||
Instruction(InstructionType),
|
||||
Unknown(String)
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Token {
|
||||
pub typ: TokenType,
|
||||
pub loc: Loc,
|
||||
pub lexem: String,
|
||||
}
|
||||
|
||||
impl Token {
|
||||
pub fn new(typ: TokenType, loc: Loc, lexem: String) -> Self {
|
||||
Self {
|
||||
typ,
|
||||
loc,
|
||||
lexem,
|
||||
}
|
||||
}
|
||||
pub fn loc(&self) -> Loc {
|
||||
self.loc.clone()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user