Compare commits

...

2 Commits
dev ... main

Author SHA1 Message Date
MCorange99
5b51430df1 IDK 2024-03-07 17:37:38 +02:00
MCorange
9625256554 Added cstrings 2023-04-23 17:51:05 +03:00
37 changed files with 2287 additions and 1048 deletions

20
.gitignore vendored
View File

@ -1,6 +1,18 @@
/target
/a
/*
test
test.nasm
test.o
# files
!/.gitignore
!/cargo.lock
!/cargo.toml
!/README.md
# folders
!/.github
!/editor
!/examples
!/include
!/playground
!/src
!/tests
!/tools

205
Cargo.lock generated
View File

@ -3,34 +3,10 @@
version = 3
[[package]]
name = "addr2line"
version = "0.19.0"
name = "anyhow"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "backtrace"
version = "0.3.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "bitflags"
@ -44,12 +20,6 @@ version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.1.8"
@ -87,33 +57,6 @@ dependencies = [
"os_str_bytes",
]
[[package]]
name = "color-eyre"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204"
dependencies = [
"backtrace",
"color-spantrace",
"eyre",
"indenter",
"once_cell",
"owo-colors",
"tracing-error",
]
[[package]]
name = "color-spantrace"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce"
dependencies = [
"once_cell",
"owo-colors",
"tracing-core",
"tracing-error",
]
[[package]]
name = "errno"
version = "0.2.8"
@ -135,22 +78,6 @@ dependencies = [
"libc",
]
[[package]]
name = "eyre"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb"
dependencies = [
"indenter",
"once_cell",
]
[[package]]
name = "gimli"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
[[package]]
name = "heck"
version = "0.4.1"
@ -163,12 +90,6 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "indenter"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]]
name = "io-lifetimes"
version = "1.0.6"
@ -191,12 +112,6 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.140"
@ -210,36 +125,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "mclang"
name = "mclangc"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"color-eyre",
"eyre",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "miniz_oxide"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
dependencies = [
"adler",
]
[[package]]
name = "object"
version = "0.30.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439"
dependencies = [
"memchr",
]
[[package]]
@ -254,18 +144,6 @@ version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]]
name = "owo-colors"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]]
name = "pin-project-lite"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
@ -308,12 +186,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "rustix"
version = "0.36.9"
@ -328,15 +200,6 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "sharded-slab"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
dependencies = [
"lazy_static",
]
[[package]]
name = "strsim"
version = "0.10.0"
@ -363,70 +226,12 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "thread_local"
version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
name = "tracing"
version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [
"cfg-if",
"pin-project-lite",
"tracing-core",
]
[[package]]
name = "tracing-core"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-error"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e"
dependencies = [
"tracing",
"tracing-subscriber",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70"
dependencies = [
"sharded-slab",
"thread_local",
"tracing-core",
]
[[package]]
name = "unicode-ident"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "version_check"
version = "0.9.4"

View File

@ -1,10 +1,13 @@
[package]
name = "mclangc"
description="The McLang Programming language compiler"
version = "0.1.0"
edition = "2021"
authors=[
"MCorange <mcorangecodes@gmail.com> (https://mcorangehq.xyz/)"
]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.79"
clap = { version = "4.1.8", features = ["derive"] }
color-eyre = "0.6.2"
eyre = "0.6.8"

View File

@ -18,6 +18,14 @@ This is the second revision of [MCLang](https://github.com/mc-lang/mclang) now w
The docs are currently are just made in MarkDown.
You can find the docs [here](/docs/index.md)
## Cheatsheet
Usefull things that i search for a lot in the sourcecode so i added them here
add them in reverse order in mclang
Syscall arg order: \[rax ,rdi ,rsi ,rdx ,r10 ,r8 ,r9\]
## Credits
[MCotange](https://github.com/MCorange99) - The one and only me, the creator and current maintainer or mclang rev1 and rev2

3
examples/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/*
!/.gitignore
!/*.mcl

View File

@ -1,7 +1,13 @@
const FS_O_RDONLY 0 end
const FS_O_WRONLY 1 end
const FS_O_RDWR 2 end
const FS_O_APPEND 1024 end // append to existing file
const FS_O_TRUNC 512 end // if file exists, ovewrite it (careful!)
const FS_O_CREAT 64 end // create file if it doesnt exist
const FS_O_ASYNC 8192 end // use signal-driven IO
const FS_O_CLOEXEC 524288 end // use close-on-exec (avoid race conditions and lock contentions)
const FS_O_CREAT 64 end // create file if it doesnt exist
const FS_O_DIRECT 16384 end // bypass cache (slower)
const FS_O_DIRECTORY 65536 end // fail if pathname isnt a directory
const FS_O_DSYNC 4096 end // ensure output is sent to hardware and metadata written before return
@ -15,4 +21,9 @@ const FS_O_NDELAY 2048 end // same as O_NONBLOCK
const FS_O_PATH 2097152 end // open descriptor for obtaining permissions and status of a file but does not allow read/write operations
const FS_O_SYNC 1052672 end // wait for IO to complete before returning
const FS_O_TMPFILE 4259840 end // create an unnamed, unreachable (via any other open call) temporary file
const FS_O_TRUNC 512 end // if file exists, ovewrite it (careful!)
fn fs_read_to_string with int ptr returns int ptr then
done

View File

@ -12,4 +12,9 @@ inline fn drop2 with any any returns void then drop drop done
const sizeof(u64) 8 end
const sizeof(u32) 4 end
const sizeof(u16) 2 end
const sizeof(u8) 1 end
const sizeof(u8) 1 end
const u64 8 end
const u32 4 end
const u16 2 end
const u8 1 end

View File

@ -5,7 +5,7 @@
// @arg buff_ptr: Ptr - pointer to the buffer to write
// @arg fd: Int - file descriptor
// @ret Int
inline fn write with int ptr int returns int then
inline fn fwrite with int ptr int returns int then
SYS_write syscall3
done
@ -15,18 +15,29 @@ done
// @arg buff_ptr: Ptr - pointer to the buffer to write
// @arg fd: Int - file descriptor
// @ret Int
inline fn read with int ptr int returns int then
inline fn fread with int ptr int returns int then
SYS_read syscall3
done
// Write to a file descriptor using the SYS_write syscall
// args: [buff_ptr, flags, mode]
// @arg buff_ptr: Ptr - File to open
// @arg flags: Int - Flags
// @arg mode: Int - Mode
// @ret Int - Fd
inline fn fopen with int ptr int returns int then
SYS_open syscall3
done
// Print a string to STDOUT
// 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
inline fn puts with int ptr returns void then
STDOUT write drop
STDOUT fwrite drop
done
// Print a string to STDERR
@ -35,10 +46,10 @@ done
// @arg buff_ptr: Ptr - pointer to the buffer to write
// @ret NULL
inline fn eputs with int ptr returns void then
STDOUT write drop
STDOUT fwrite drop
done
// TODO: make putc and eputc after we make local mem
// TODO: make putc, eputc, putd, and eputd after we make local mem
// Exit the program with exit_code
// args: [exit_code]

View File

@ -0,0 +1 @@

5
include/string.mcl Normal file
View File

@ -0,0 +1,5 @@
fn cstr_len with ptr returns int then
dup while dup load8 '\0' != do 1 + end swap -
done

4
playground/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/*
!/.gitignore
!/*.mcl
!/mclangc

13
playground/mclangc Executable file
View File

@ -0,0 +1,13 @@
#!/usr/bin/bash
#? This is just a passthrough for the compiled mclangc binary for ease of use
#? It also compiles mclangc every time its ran
pushd ../ > /dev/null
cargo build --release -q
popd > /dev/null
../target/release/mclangc -I../include ${@:1}

35
playground/test.mcl Normal file
View File

@ -0,0 +1,35 @@
include "std.mcl"
struct StatDef do
val -> u32
val2 -> i8
end
alloc Stat StatDef end
fn main with int ptr returns void then
// p l
"Hello!\n" puts
Stat.val 69 write32
Stat.val read32 _dbg_print
Stat.__size read32 _dbg_print
// memory fd 4 end
// NULL
// FS_O_RDWR
// c"/home/mcorange/@Projects/rust/programming_languages/mclang/mclangc/playground/test.mcl"
// fopen
// dup _dbg_print
// fd swap write32
done

View File

@ -3,8 +3,7 @@ use std::path::{PathBuf, Path};
use std::process::Stdio;
use std::{process, fs};
use clap::Parser;
use color_eyre::Result;
use eyre::eyre;
use anyhow::{Result, bail};
pub mod color {
#![allow(dead_code)]
@ -104,21 +103,21 @@ fn compare_results(intp: &TestOutput, comp: &TestOutput, f_in: &Path) -> Result<
println!("{b}[ {r}ERR{rs}{b} ]{rs} {f} compiled and interpreted stdout versions differ", r=color::FG_RED, rs=color::RESET, b=color::BRIGHT, f=f_in.display());
println!("compiled:\n{}", comp.stdout);
println!("interpreted:\n{}", intp.stdout);
return Err(eyre!("Testing failed"));
bail!("Testing failed");
}
if intp.stderr != comp.stderr {
println!("{b}[ {r}ERR{rs}{b} ]{rs} {f} compiled and interpreted stderr versions differ", r=color::FG_RED, rs=color::RESET, b=color::BRIGHT, f=f_in.display());
println!("compiled:\n{}", comp.stderr);
println!("interpreted:\n{}", intp.stderr);
return Err(eyre!("Testing failed"));
bail!("Testing failed");
}
if intp.status != comp.status {
println!("{b}[ {r}ERR{rs}{b} ]{rs} {f} compiled and interpreted status codes differ", r=color::FG_RED, rs=color::RESET, b=color::BRIGHT, f=f_in.display());
println!("compiled:\n{}", comp.status);
println!("interpreted:\n{}", intp.status);
return Err(eyre!("Testing failed"));
bail!("Testing failed");
}
println!("{b}[ {g}OK{rs}{b} ]{rs} {f} ", g=color::FG_GREEN, rs=color::RESET, b=color::BRIGHT, f=f_in.display());
@ -164,7 +163,7 @@ fn main() -> Result<()> {
"record" => todo!("Implement test result recording"),
s => {
eprintln!("Unknown mode '{s}'");
return Err(eyre!("Bad subcommand"));
bail!("Bad subcommand");
}
}?;

View File

@ -1,7 +1,7 @@
use std::path::{PathBuf, Path};
use std::process::{Command, Stdio};
use color_eyre::Result;
use crate::info;
use anyhow::Result;
use crate::{info, error};
pub fn linux_x86_64_compile_and_link(of_a: &Path, of_o: &Path, of_c: &Path, quiet: bool) -> Result<()> {
@ -22,11 +22,16 @@ pub fn linux_x86_64_compile_and_link(of_a: &Path, of_o: &Path, of_c: &Path, quie
let mut proc = if cfg!(target_os = "windows") {
return Ok(());
} else {
Command::new("nasm")
let ret = Command::new("nasm")
.args(nasm_args)
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.spawn()?
.spawn();
if ret.is_err() {
error!("Nasm not installed")
}
ret?
};
if !quiet {
info!("running 'nasm {}'", nasm_args.join(" "));

View File

@ -1,13 +1,13 @@
use std::{fs, path::PathBuf, io::{Write, BufWriter}, collections::HashMap};
use crate::{constants::{Operator, OpType, KeywordType}, Args, warn, lerror};
use color_eyre::Result;
use crate::{definitions::*, Args, warn, lerror};
use crate::compile::commands::linux_x86_64_compile_and_link;
use crate::constants::InstructionType;
use crate::definitions::InstructionType;
use super::{commands::linux_x86_64_run, Constant, Memory, Function};
use eyre::eyre;
use anyhow::{Result, bail};
pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
pub fn compile(program: &Program, args: &Args) -> Result<i32>{
let debug = args.get_opt_level()? < 1;
let mut of_c = PathBuf::from(&args.out_file);
@ -32,60 +32,30 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
let mut memories: Vec<Memory> = Vec::new();
let mut constants: HashMap<String, Constant> = HashMap::new();
let mut functions: Vec<Function> = Vec::new();
let mut alloced_structs: Vec<(String, String)> = Vec::new();
// println!("{}", tokens.len());
let mut strings: Vec<String> = Vec::new();
writeln!(writer, "BITS 64")?;
writeln!(writer, "segment .text")?;
writeln!(writer, "_dbg_print:")?;
writeln!(writer, " mov r9, -3689348814741910323")?;
writeln!(writer, " sub rsp, 40")?;
writeln!(writer, " mov BYTE [rsp+31], 10")?;
writeln!(writer, " lea rcx, [rsp+30]")?;
writeln!(writer, ".L2:")?;
writeln!(writer, " mov rax, rdi")?;
writeln!(writer, " lea r8, [rsp+32]")?;
writeln!(writer, " mul r9")?;
writeln!(writer, " mov rax, rdi")?;
writeln!(writer, " sub r8, rcx")?;
writeln!(writer, " shr rdx, 3")?;
writeln!(writer, " lea rsi, [rdx+rdx*4]")?;
writeln!(writer, " add rsi, rsi")?;
writeln!(writer, " sub rax, rsi")?;
writeln!(writer, " add eax, 48")?;
writeln!(writer, " mov BYTE [rcx], al")?;
writeln!(writer, " mov rax, rdi")?;
writeln!(writer, " mov rdi, rdx")?;
writeln!(writer, " mov rdx, rcx")?;
writeln!(writer, " sub rcx, 1")?;
writeln!(writer, " cmp rax, 9")?;
writeln!(writer, " ja .L2")?;
writeln!(writer, " lea rax, [rsp+32]")?;
writeln!(writer, " mov edi, 1")?;
writeln!(writer, " sub rdx, rax")?;
writeln!(writer, " xor eax, eax")?;
writeln!(writer, " lea rsi, [rsp+32+rdx]")?;
writeln!(writer, " mov rdx, r8")?;
writeln!(writer, " mov rax, 1")?;
writeln!(writer, " syscall")?;
writeln!(writer, " add rsp, 40")?;
writeln!(writer, " ret")?;
writeln!(writer, "{}", super::MACRO_DEFINITIONS)?;
writeln!(writer, "{}", super::DBG_PRINT)?;
if crate::config::ENABLE_EXPORTED_FUNCTIONS && !args.lib_mode {
if !crate::config::ENABLE_EXPORTED_FUNCTIONS && !args.lib_mode {
writeln!(writer, "global _start")?;
writeln!(writer, "_start:")?;
writeln!(writer, " lea rbp, [rel ret_stack]")?;
writeln!(writer, " call main")?;
writeln!(writer, " jmp end")?;
}
let mut ti = 0;
while ti < tokens.len() {
let token = &tokens[ti];
// println!("{:?}", token);
while ti < program.ops.len() {
let token = &program.ops[ti];
if debug {
writeln!(writer, "addr_{ti}:")?;
if token.typ == OpType::Instruction(InstructionType::PushInt) {
@ -97,13 +67,13 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
}
} else {
if ti > 0 {
if tokens[ti-1].typ == OpType::Keyword(KeywordType::Else) ||
tokens[ti-1].typ == OpType::Keyword(KeywordType::End){
if program.ops[ti-1].typ == OpType::Keyword(KeywordType::Else) ||
program.ops[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 < program.ops.len() && program.ops[ti+1].typ == OpType::Keyword(KeywordType::End) {
writeln!(writer, "addr_{ti}:")?;
}
@ -116,299 +86,168 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
_ => ()
}
}
}
match token.typ.clone() {
// stack
OpType::Instruction(instruction) => {
match instruction {
InstructionType::PushInt => {
writeln!(writer, " mov rax, {}", token.value)?;
writeln!(writer, " push rax")?;
writeln!(writer, " OP_PushInt {}", token.value)?;
ti += 1;
},
InstructionType::PushStr => {
writeln!(writer, " mov rax, {}", token.text.len())?;
writeln!(writer, " push rax")?;
writeln!(writer, " mov rax, str_{}", strings.len())?;
writeln!(writer, " push rax")?;
writeln!(writer, " OP_PushStr {}, str_{}", token.text.len(), strings.len())?;
strings.push(token.text.clone());
ti += 1;
}
InstructionType::PushCStr => {
writeln!(writer, " OP_PushCStr str_{}", strings.len())?;
strings.push(token.text.clone());
ti += 1;
}
InstructionType::Drop => {
writeln!(writer, " pop rax")?;
writeln!(writer, " OP_Drop")?;
ti += 1;
},
InstructionType::Print => {
writeln!(writer, " pop rdi")?;
writeln!(writer, " call _dbg_print")?;
writeln!(writer, " OP_Print")?;
ti += 1;
},
InstructionType::Dup => {
writeln!(writer, " pop rax")?;
writeln!(writer, " push rax")?;
writeln!(writer, " push rax")?;
writeln!(writer, " OP_Dup")?;
ti += 1;
},
InstructionType::Rot => {
writeln!(writer, " pop rax")?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " pop rcx")?;
writeln!(writer, " push rbx")?;
writeln!(writer, " push rax")?;
writeln!(writer, " push rcx")?;
writeln!(writer, " OP_Rot")?;
ti += 1;
},
InstructionType::Swap => {
writeln!(writer, " pop rax")?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " push rax")?;
writeln!(writer, " push rbx")?;
writeln!(writer, " OP_Swap")?;
ti += 1;
},
InstructionType::Over => {
writeln!(writer, " pop rax")?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " push rbx")?;
writeln!(writer, " push rax")?;
writeln!(writer, " push rbx")?;
writeln!(writer, " OP_Over")?;
ti += 1;
},
InstructionType::Load8 => {
writeln!(writer, " pop rax")?;
writeln!(writer, " xor rbx, rbx")?;
writeln!(writer, " mov bl, byte [rax]")?;
writeln!(writer, " push rbx")?;
InstructionType::Read8 => {
writeln!(writer, " OP_Load8")?;
ti += 1;
}
InstructionType::Store8 => {
writeln!(writer, " pop rbx")?;
writeln!(writer, " pop rax")?;
writeln!(writer, " mov byte [rax], bl")?;
InstructionType::Write8 => {
writeln!(writer, " OP_Store8")?;
ti += 1;
}
InstructionType::Load32 => {
writeln!(writer, " pop rax")?;
writeln!(writer, " xor rbx, rbx")?;
writeln!(writer, " mov bl, dword [rax]")?;
writeln!(writer, " push rbx")?;
InstructionType::Read32 => {
writeln!(writer, " OP_Load32")?;
ti += 1;
}
InstructionType::Store32 => {
writeln!(writer, " pop rbx")?;
writeln!(writer, " pop rax")?;
writeln!(writer, " mov dword[rax], bl")?;
InstructionType::Write32 => {
writeln!(writer, " OP_Store32")?;
ti += 1;
}
InstructionType::Load64 => {
writeln!(writer, " pop rax")?;
writeln!(writer, " xor rbx, rbx")?;
writeln!(writer, " mov bl, qword [rax]")?;
writeln!(writer, " push rbx")?;
InstructionType::Read64 => {
writeln!(writer, " OP_Load64")?;
ti += 1;
}
InstructionType::Store64 => {
writeln!(writer, " pop rbx")?;
writeln!(writer, " pop rax")?;
writeln!(writer, " mov qword [rax], bl")?;
InstructionType::Write64 => {
writeln!(writer, " OP_Store64")?;
ti += 1;
}
// math
InstructionType::Plus => {
writeln!(writer, " pop rax")?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " add rax, rbx")?;
writeln!(writer, " push rax")?;
writeln!(writer, " OP_Plus")?;
ti += 1;
},
InstructionType::Minus => {
writeln!(writer, " pop rax")?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " sub rbx, rax")?;
writeln!(writer, " push rbx")?;
writeln!(writer, " OP_Minus")?;
ti += 1;
},
InstructionType::Equals => {
writeln!(writer, " mov rcx, 0")?;
writeln!(writer, " mov rdx, 1")?;
writeln!(writer, " pop rax")?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " cmp rax, rbx")?;
writeln!(writer, " cmove rcx, rdx")?;
writeln!(writer, " push rcx")?;
writeln!(writer, " OP_Equals")?;
ti += 1;
},
InstructionType::Lt => {
writeln!(writer, " mov rcx, 0")?;
writeln!(writer, " mov rdx, 1")?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " pop rax")?;
writeln!(writer, " cmp rax, rbx")?;
writeln!(writer, " cmovl rcx, rdx")?;
writeln!(writer, " push rcx")?;
writeln!(writer, " OP_Lt")?;
ti += 1;
},
InstructionType::Gt => {
writeln!(writer, " mov rcx, 0")?;
writeln!(writer, " mov rdx, 1")?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " pop rax")?;
writeln!(writer, " cmp rax, rbx")?;
writeln!(writer, " cmovg rcx, rdx")?;
writeln!(writer, " push rcx")?;
writeln!(writer, " OP_Gt")?;
ti += 1;
},
InstructionType::NotEquals => {
writeln!(writer, " mov rcx, 1")?;
writeln!(writer, " mov rdx, 0")?;
writeln!(writer, " pop rax")?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " cmp rax, rbx")?;
writeln!(writer, " cmove rcx, rdx")?;
writeln!(writer, " push rcx")?;
writeln!(writer, " OP_NotEquals")?;
ti += 1;
},
InstructionType::Le => {
writeln!(writer, " mov rcx, 0")?;
writeln!(writer, " mov rdx, 1")?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " pop rax")?;
writeln!(writer, " cmp rax, rbx")?;
writeln!(writer, " cmovle rcx, rdx")?;
writeln!(writer, " push rcx")?;
writeln!(writer, " OP_Le")?;
ti += 1;
},
InstructionType::Ge => {
writeln!(writer, " mov rcx, 0")?;
writeln!(writer, " mov rdx, 1")?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " pop rax")?;
writeln!(writer, " cmp rax, rbx")?;
writeln!(writer, " cmovge rcx, rdx")?;
writeln!(writer, " push rcx")?;
writeln!(writer, " OP_Ge")?;
ti += 1;
},
InstructionType::Band => {
writeln!(writer, " pop rax")?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " and rbx, rax")?;
writeln!(writer, " push rbx")?;
writeln!(writer, " OP_Band")?;
ti += 1;
},
InstructionType::Bor => {
writeln!(writer, " pop rax")?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " or rbx, rax")?;
writeln!(writer, " push rbx")?;
writeln!(writer, " OP_Bor")?;
ti += 1;
},
InstructionType::Shr => {
writeln!(writer, " pop rcx")?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " shr rbx, cl")?;
writeln!(writer, " push rbx")?;
writeln!(writer, " OP_Shr")?;
ti += 1;
},
InstructionType::Shl => {
writeln!(writer, " pop rcx")?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " shl rbx, cl")?;
writeln!(writer, " push rbx")?;
writeln!(writer, " OP_Shl")?;
ti += 1;
},
InstructionType::DivMod => {
writeln!(writer, " xor rdx, rdx")?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " pop rax")?;
writeln!(writer, " div rbx")?;
writeln!(writer, " push rax")?;
writeln!(writer, " push rdx")?;
writeln!(writer, " OP_DivMod")?;
ti += 1;
},
InstructionType::Mul => {
writeln!(writer, " pop rax")?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " mul rbx")?;
writeln!(writer, " push rax")?;
writeln!(writer, " OP_Mul")?;
ti += 1;
},
InstructionType::Syscall0 => {
writeln!(writer, " pop rax")?;
writeln!(writer, " syscall")?;
writeln!(writer, " push rax")?;
writeln!(writer, " OP_Syscall0")?;
ti += 1;
},
InstructionType::Syscall1 => {
writeln!(writer, " pop rax")?;
writeln!(writer, " pop rdi")?;
writeln!(writer, " syscall")?;
writeln!(writer, " push rax")?;
writeln!(writer, " OP_Syscall1")?;
ti += 1;
},
InstructionType::Syscall2 => {
writeln!(writer, " pop rax")?;
writeln!(writer, " pop rdi")?;
writeln!(writer, " pop rsi")?;
writeln!(writer, " syscall")?;
writeln!(writer, " push rax")?;
writeln!(writer, " OP_Syscall2")?;
ti += 1;
},
InstructionType::Syscall3 => {
writeln!(writer, " pop rax")?;
writeln!(writer, " pop rdi")?;
writeln!(writer, " pop rsi")?;
writeln!(writer, " pop rdx")?;
writeln!(writer, " syscall")?;
writeln!(writer, " push rax")?;
writeln!(writer, " OP_Syscall3")?;
ti += 1;
},
InstructionType::Syscall4 => {
writeln!(writer, " pop rax")?;
writeln!(writer, " pop rdi")?;
writeln!(writer, " pop rsi")?;
writeln!(writer, " pop rdx")?;
writeln!(writer, " pop r10")?;
writeln!(writer, " syscall")?;
writeln!(writer, " push rax")?;
writeln!(writer, " OP_Syscall4")?;
ti += 1;
},
InstructionType::Syscall5 => {
writeln!(writer, " pop rax")?;
writeln!(writer, " pop rdi")?;
writeln!(writer, " pop rsi")?;
writeln!(writer, " pop rdx")?;
writeln!(writer, " pop r10")?;
writeln!(writer, " pop r8")?;
writeln!(writer, " syscall")?;
writeln!(writer, " push rax")?;
writeln!(writer, " OP_Syscall5")?;
ti += 1;
},
InstructionType::Syscall6 => {
writeln!(writer, " pop rax")?;
writeln!(writer, " pop rdi")?;
writeln!(writer, " pop rsi")?;
writeln!(writer, " pop rdx")?;
writeln!(writer, " pop r10")?;
writeln!(writer, " pop r8")?;
writeln!(writer, " pop r9")?;
writeln!(writer, " syscall")?;
writeln!(writer, " push rax")?;
writeln!(writer, " OP_Syscall6")?;
ti += 1;
},
InstructionType::MemUse => {
writeln!(writer, " push mem_{}", token.addr.unwrap())?;
writeln!(writer, " OP_MemUse {}", token.addr.unwrap())?;
ti += 1;
},
InstructionType::None => {
@ -416,11 +255,12 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
unreachable!()
},
InstructionType::FnCall => {
writeln!(writer, " call {}", token.text)?;
writeln!(writer, " OP_FnCall {}", token.text)?;
ti += 1;
},
InstructionType::Return => {
// Experimental feature exported functions
if crate::config::ENABLE_EXPORTED_FUNCTIONS && should_push_ret {
writeln!(writer, " pop rdx")?;
should_push_ret = false;
@ -446,8 +286,7 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
ti += 1;
}
InstructionType::ConstUse => {
writeln!(writer, " mov rax, qword [const_{}]", token.text)?;
writeln!(writer, " push rax")?;
writeln!(writer, " OP_ConstUse {}", token.text)?;
let mut c = constants.get(&token.text).unwrap().clone();
c.used = true;
@ -455,6 +294,10 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
constants.insert(token.text.clone(), c);
ti += 1;
},
InstructionType::StructUse => {
writeln!(writer, " OP_StructUse {}", token.text)?;
ti += 1;
},
}
}
@ -479,7 +322,7 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
}
KeywordType::End => {
if ti + 1 != token.jmp {
// writeln!(writer, " jmp addr_{}", token.jmp)?;
writeln!(writer, " jmp addr_{}", token.jmp)?;
}
ti += 1;
},
@ -523,16 +366,11 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
ti += 1;
}
KeywordType::FunctionThen => ti += 1,
KeywordType::Function |
KeywordType::Include |
KeywordType::Inline |
KeywordType::Export |
KeywordType::Constant => unreachable!(),
KeywordType::FunctionDefExported => {
if !crate::config::ENABLE_EXPORTED_FUNCTIONS {
lerror!(&token.loc, "Experimental feature 'exported functions' is not enabled");
return Err(eyre!(""));
bail!("");
}
writeln!(writer, "global {}", token.text)?;
@ -565,7 +403,7 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
}
if token.types.0 >= 7 {
lerror!(&token.loc, "More than 6 arguments in an external function is not supported");
return Err(eyre!(""));
bail!("");
}
}
@ -573,18 +411,33 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
should_push_ret = true;
} else if token.types.1 > 1 {
lerror!(&token.loc, "More than 1 return arguments in an external function is not supported");
return Err(eyre!(""));
bail!("");
}
functions.push(Function { loc: token.loc.clone(), name: token.text.clone(), exter: false});
ti += 1;
},
KeywordType::Function |
KeywordType::Include |
KeywordType::Inline |
KeywordType::Export |
KeywordType::Struct |
KeywordType::Constant => unreachable!(),
}
}
OpType::Internal(t) => {
match t {
InternalType::StructAlloc{name} => {
alloced_structs.push((name, token.text.clone()));
ti += 1;
},
InternalType::Arrow => panic!("{t:?}"),
}
},
}
}
writeln!(writer, "addr_{ti}:")?;
if crate::config::ENABLE_EXPORTED_FUNCTIONS && !args.lib_mode {
if !crate::config::ENABLE_EXPORTED_FUNCTIONS && !args.lib_mode {
writeln!(writer, "end:")?;
writeln!(writer, " mov rax, 60")?;
writeln!(writer, " mov rdi, 0")?;
@ -609,16 +462,37 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
} else {
unreachable!();
}
}
writeln!(writer, "segment .bss")?;
for s in memories {
writeln!(writer, " mem_{}: resb {}", s.id, s.size)?;
for m in memories {
writeln!(writer, " mem_{}: resb {}", m.id, m.size)?;
}
writeln!(writer, " ret_stack: resq 256")?;
for s in alloced_structs {
let Some(st) = program.struct_defs.get(&s.0) else {
// TODO: Make better error
panic!("Couldn find struct in struct defs");
};
let name = &s.1;
let mut st_size = 0;
writeln!(writer, " struct_{name}:")?;
for f in &st.fields {
let size = f.1.get_size();
writeln!(writer, " struct_{name}.{}: resb {}", f.0, size)?;
st_size += size;
}
writeln!(writer, " struct_{name}.__size: db {}", st_size)?;
}
writeln!(writer, " ret_stack: resq 256")?;
// for t in tokens {
// println!("{t:?}");
// }
@ -631,7 +505,8 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
functions
)?;
linux_x86_64_compile_and_link(&of_a, &of_o, &of_c, args.quiet)?;
linux_x86_64_compile_and_link(&of_a, &of_o, &of_c, args.quiet);
if args.run {
let c = linux_x86_64_run(&of_c, &[], args.quiet)?;
return Ok(c);
@ -654,7 +529,7 @@ fn pre_compile_steps(_code: &str, functions: Vec<Function>) -> Result<()> {
if !has_main {
crate::errors::missing_main_fn();
return Err(eyre!(""));
bail!("");
}
Ok(())

View File

@ -1,4 +1,4 @@
use crate::constants::Loc;
use crate::definitions::Loc;
pub mod linux_x86_64;
pub mod commands;
@ -25,4 +25,338 @@ pub struct Function {
pub loc: Loc,
pub name: String,
pub exter: bool,
}
}
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
";
const MACRO_DEFINITIONS: &'static str = "\
%macro OP_PushInt 1
mov rax, %1
push rax
%endmacro
; str_len, str_id
%macro OP_PushStr 2
mov rax, %1
push rax
mov rax, %2
push rax
%endmacro
; str_id
%macro OP_PushCStr 1
push rax
mov rax, %1
push rax
%endmacro
%macro OP_Drop 0
pop rax
%endmacro
%macro OP_Print 0
pop rdi
call _dbg_print
%endmacro
%macro OP_Dup 0
pop rax
push rax
push rax
%endmacro
%macro OP_Rot 0
pop rax
pop rbx
pop rcx
push rbx
push rax
push rcx
%endmacro
%macro OP_Swap 0
pop rax
pop rbx
push rax
push rbx
%endmacro
%macro OP_Over 0
pop rax
pop rbx
push rbx
push rax
push rbx
%endmacro
%macro OP_Load8 0
pop rax
xor rbx, rbx
mov bl, byte [rax]
push rbx
%endmacro
%macro OP_Store8 0
pop rbx
pop rax
mov byte [rax], bl
%endmacro
%macro OP_Load32 0
pop rax
xor rbx, rbx
mov ebx, dword [rax]
push rbx
%endmacro
%macro OP_Store32 0
pop rbx
pop rax
mov dword[rax], ebx
%endmacro
%macro OP_Load64 0
pop rax
xor rbx, rbx
mov rbx, qword [rax]
push rbx
%endmacro
%macro OP_Store64 0
pop rbx
pop rax
mov qword [rax], rbx
%endmacro
%macro OP_Plus 0
pop rax
pop rbx
add rax, rbx
push rax
%endmacro
%macro OP_Minus 0
pop rax
pop rbx
sub rbx, rax
push rbx
%endmacro
%macro OP_Equals 0
mov rcx, 0
mov rdx, 1
pop rax
pop rbx
cmp rax, rbx
cmove rcx, rdx
push rcx
%endmacro
%macro OP_Lt 0
mov rcx, 0
mov rdx, 1
pop rbx
pop rax
cmp rax, rbx
cmovl rcx, rdx
push rcx
%endmacro
%macro OP_Gt 0
mov rcx, 0
mov rdx, 1
pop rbx
pop rax
cmp rax, rbx
cmovg rcx, rdx
push rcx
%endmacro
%macro OP_NotEquals 0
mov rcx, 1
mov rdx, 0
pop rax
pop rbx
cmp rax, rbx
cmove rcx, rdx
push rcx
%endmacro
%macro OP_Le 0
mov rcx, 0
mov rdx, 1
pop rbx
pop rax
cmp rax, rbx
cmovle rcx, rdx
push rcx
%endmacro
%macro OP_Ge 0
mov rcx, 0
mov rdx, 1
pop rbx
pop rax
cmp rax, rbx
cmovge rcx, rdx
push rcx
%endmacro
%macro OP_Band 0
pop rax
pop rbx
and rbx, rax
push rbx
%endmacro
%macro OP_Bor 0
pop rax
pop rbx
or rbx, rax
push rbx
%endmacro
%macro OP_Shr 0
pop rcx
pop rbx
shr rbx, cl
push rbx
%endmacro
%macro OP_Shl 0
pop rcx
pop rbx
shl rbx, cl
push rbx
%endmacro
%macro OP_DivMod 0
xor rdx, rdx
pop rbx
pop rax
div rbx
push rax
push rdx
%endmacro
%macro OP_Mul 0
pop rax
pop rbx
mul rbx
push rax
%endmacro
%macro OP_Syscall0 0
pop rax
syscall
push rax
%endmacro
%macro OP_Syscall1 0
pop rax
pop rdi
syscall
push rax
%endmacro
%macro OP_Syscall2 0
pop rax
pop rdi
pop rsi
syscall
push rax
%endmacro
%macro OP_Syscall3 0
pop rax
pop rdi
pop rsi
pop rdx
syscall
push rax
%endmacro
%macro OP_Syscall4 0
pop rax
pop rdi
pop rsi
pop rdx
pop r10
syscall
push rax
%endmacro
%macro OP_Syscall5 0
pop rax
pop rdi
pop rsi
pop rdx
pop r10
pop r8
syscall
push rax
%endmacro
%macro OP_Syscall6 0
pop rax
pop rdi
pop rsi
pop rdx
pop r10
pop r8
pop r9
syscall
push rax
%endmacro
%macro OP_MemUse 1
push mem_%1
%endmacro
%macro OP_FnCall 1
call %1
%endmacro
%macro OP_ConstUse 1
mov rax, qword [const_%1]
push rax
%endmacro
%macro OP_StructUse 1
push struct_%1
%endmacro
";

View File

@ -1,25 +1,15 @@
/**
* Prints out extra information
*/
pub const DEV_MODE: bool = false;
pub const DEV_MODE: bool = true;
pub const DEFAULT_OUT_FILE: &str = "a.out";
pub const DEFAULT_INCLUDES: [&str;1] = [
pub const DEFAULT_INCLUDES: [&str;2] = [
"./include",
// "~/.mclang/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
/**
* Experimental options
*/

View File

@ -7,6 +7,7 @@ pub enum InstructionType {
// stack
PushInt,
PushStr,
PushCStr,
Drop,
Print,
Dup,
@ -32,12 +33,12 @@ pub enum InstructionType {
// mem
Load8,
Store8,
Load32,
Store32,
Load64,
Store64,
Read8,
Write8,
Read32,
Write32,
Read64,
Write64,
// syscalls
Syscall0,
@ -88,7 +89,8 @@ pub enum KeywordType {
FunctionThen,
FunctionDone,
Inline,
Export
Export,
Struct
}
#[derive(Debug, Clone, PartialEq)]
@ -131,7 +133,7 @@ impl Operator {
// self.types = (args, rets);
// (*self).clone()
// }
}
impl OpType {
@ -139,9 +141,10 @@ impl OpType {
match (*self).clone() {
OpType::Instruction(instruction) => {
match instruction {
InstructionType::PushInt => "Number",
InstructionType::PushStr => "String",
InstructionType::PushCStr => "CString",
InstructionType::Print => "_dbg_print",
InstructionType::Dup => "dup",
InstructionType::Drop => "drop",
@ -162,12 +165,12 @@ impl OpType {
InstructionType::Shl => "shl",
InstructionType::DivMod => "divmod",
InstructionType::Mul => "*",
InstructionType::Load8 => "load8",
InstructionType::Store8 => "store8",
InstructionType::Load32 => "load32",
InstructionType::Store32 => "store32",
InstructionType::Load64 => "load64",
InstructionType::Store64 => "store64",
InstructionType::Read8 => "read8",
InstructionType::Write8 => "write8",
InstructionType::Read32 => "read32",
InstructionType::Write32 => "write32",
InstructionType::Read64 => "read64",
InstructionType::Write64 => "write64",
InstructionType::Syscall0 => "syscall0",
InstructionType::Syscall1 => "syscall1",
InstructionType::Syscall2 => "syscall2",
@ -211,6 +214,7 @@ impl OpType {
KeywordType::FunctionDefExported => "extern function definition (internal)",
KeywordType::Inline => "inline",
KeywordType::Export => "export",
KeywordType::Struct => "struct",
}
}
@ -235,6 +239,7 @@ pub enum TokenType {
Word,
Int,
String,
CString,
Char
}
@ -254,6 +259,7 @@ impl TokenType {
TokenType::Word => "Word",
TokenType::Int => "Int",
TokenType::String => "String",
TokenType::CString => "CString",
TokenType::Char => "Char"
}.to_string()
}

392
src/definitions/mod.rs Normal file
View File

@ -0,0 +1,392 @@
use std::collections::HashMap;
use anyhow::{Result, bail};
#[derive(Debug, Clone, PartialEq)]
pub enum InstructionType {
// stack
PushInt,
PushStr,
PushCStr,
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,
Returns,
With,
FnCall,
MemUse,
ConstUse,
StructUse,
Return,
None // Used for macros and any other non built in word definitions
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum KeywordType {
If,
Else,
End,
While,
Do,
Include,
Memory,
Constant,
ConstantDef,
Function,
FunctionDef,
FunctionDefExported,
FunctionThen,
FunctionDone,
Inline,
Export,
Struct,
}
#[derive(Debug, Clone, PartialEq)]
pub enum InternalType {
Arrow,
StructAlloc {
name: String
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum OpType {
Keyword(KeywordType),
Instruction(InstructionType),
Internal(InternalType)
}
#[derive(Debug, Clone)]
pub struct Operator{
pub typ: OpType,
pub tok_typ: TokenType,
pub value: usize,
pub text: String, //? only used for OpType::PushStr
pub addr: Option<usize>, //? only used for OpType::PushStr
pub jmp: usize,
pub loc: Loc,
pub types: (usize, usize)
}
impl Operator {
pub fn new(typ: OpType, tok_typ: TokenType, value: usize, text: String, file: String, row: usize, col: usize) -> Self {
Self {
typ,
value,
jmp: 0,
addr: None,
text,
loc: (file, row, col),
tok_typ,
types: (0, 0)
}
}
pub fn set_addr(&mut self, addr: usize) -> Self {
self.addr = Some(addr);
(*self).clone()
}
// pub fn set_types(&mut self, args: usize, rets: usize) -> Self {
// self.types = (args, rets);
// (*self).clone()
// }
}
impl OpType {
pub fn human(&self) -> String {
match (*self).clone() {
OpType::Instruction(instruction) => {
match instruction {
InstructionType::PushInt => "Number",
InstructionType::PushStr => "String",
InstructionType::PushCStr => "CString",
InstructionType::Print => "_dbg_print",
InstructionType::Dup => "dup",
InstructionType::Drop => "drop",
InstructionType::Rot => "rot",
InstructionType::Over => "over",
InstructionType::Swap => "swap",
InstructionType::Plus => "+",
InstructionType::Minus => "-",
InstructionType::Equals => "=",
InstructionType::Gt => ">",
InstructionType::Lt => "<",
InstructionType::NotEquals => "!=",
InstructionType::Le => "<=",
InstructionType::Ge => ">=",
InstructionType::Band => "band",
InstructionType::Bor => "bor",
InstructionType::Shr => "shr",
InstructionType::Shl => "shl",
InstructionType::DivMod => "divmod",
InstructionType::Mul => "*",
InstructionType::Read8 => "read8",
InstructionType::Write8 => "write8",
InstructionType::Read32 => "read32",
InstructionType::Write32 => "write32",
InstructionType::Read64 => "read64",
InstructionType::Write64 => "write64",
InstructionType::Syscall0 => "syscall0",
InstructionType::Syscall1 => "syscall1",
InstructionType::Syscall2 => "syscall2",
InstructionType::Syscall3 => "syscall3",
InstructionType::Syscall4 => "syscall4",
InstructionType::Syscall5 => "syscall5",
InstructionType::Syscall6 => "syscall6",
InstructionType::CastBool => "cast(bool",
InstructionType::CastPtr => "cast(ptr)",
InstructionType::CastInt => "cast(int)",
InstructionType::CastVoid => "cast(void)",
InstructionType::None => "None",
InstructionType::MemUse => "Memory use (internal)",
InstructionType::FnCall => "Function Call (Internal)",
InstructionType::ConstUse => "Constant Use (Internal)",
InstructionType::StructUse => "Struct Use (Internal)",
InstructionType::Return => "return",
InstructionType::TypeBool => "bool",
InstructionType::TypePtr => "ptr",
InstructionType::TypeInt => "int",
InstructionType::TypeVoid => "void",
InstructionType::Returns => "returns",
InstructionType::With => "with",
InstructionType::TypeAny => "any",
}
}
OpType::Keyword(keyword) => {
match keyword {
KeywordType::If => "if",
KeywordType::Else => "else",
KeywordType::End => "end",
KeywordType::While => "while",
KeywordType::Do => "do",
KeywordType::Include => "include",
KeywordType::Memory => "memory",
KeywordType::Function => "fn",
KeywordType::Constant => "const",
KeywordType::FunctionThen => "then",
KeywordType::FunctionDone => "done",
KeywordType::ConstantDef => "constant Definition (internal)",
KeywordType::FunctionDef => "function definition (internal)",
KeywordType::FunctionDefExported => "extern function definition (internal)",
KeywordType::Inline => "inline",
KeywordType::Export => "export",
KeywordType::Struct => "struct",
}
}
OpType::Internal(t) => {
match t {
InternalType::Arrow => "->",
InternalType::StructAlloc{..} => "Struct alloc ( internal )",
}
},
}.to_string()
}
}
#[derive(Debug, Clone)]
pub struct Token {
pub file: String,
pub line: usize,
pub col: usize,
pub text: String,
pub typ: TokenType,
pub value: Option<usize>, //* only used for Memories
pub addr: Option<usize>, //* only used for Memories
pub op_typ: OpType //* only used for Memories
}
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum TokenType {
Word,
Int,
String,
CString,
Char
}
impl Token {
pub fn loc(&self) -> Loc {
(
self.file.clone(),
self.line,
self.col
)
}
}
impl TokenType {
pub fn human(self) -> String {
match self {
TokenType::Word => "Word",
TokenType::Int => "Int",
TokenType::String => "String",
TokenType::CString => "CString",
TokenType::Char => "Char"
}.to_string()
}
}
pub type Loc = (String, usize, usize);
#[derive(Debug, PartialEq, Clone)]
pub enum Types {
Any,
Bool,
Ptr,
Void,
U8,
U16,
U32,
U64,
I8,
I16,
I32,
I64,
#[allow(dead_code)] //TODO: Implement custom types
Custom{
size: u64 // in bytes
},
// todo: add signed numbers since we dont have them yet lol
}
impl Types {
pub fn get_size(&self) -> u64 {
match *self {
Types::Any => 0, // any cant be a known size
Types::Void => 0,
Types::Bool => 1,
Types::U8 |
Types::I8 => 1,
Types::U16 |
Types::I16 => 2,
Types::U32 |
Types::I32 => 4,
Types::Ptr |
Types::U64 |
Types::I64 => 8,
Types::Custom { size } => size,
}
}
pub fn from_string<S: Into<String> + std::fmt::Display>(s: &S) -> Result<Self> {
match s.to_string().as_str() {
"any" => Ok(Types::Any),
"void" => Ok(Types::Void),
"bool" => Ok(Types::Bool),
"u8" => Ok(Types::U8),
"i8" => Ok(Types::I8),
"u16" => Ok(Types::U16),
"i16" => Ok(Types::I16),
"u32" => Ok(Types::U32),
"i32" => Ok(Types::I32),
"ptr" => Ok(Types::Ptr),
"u64" => Ok(Types::U64),
"i64" => Ok(Types::I64),
_ => bail!("Unknown type {s}")
}
}
}
#[derive(Debug, Clone)]
pub struct Function {
pub loc: Loc,
pub name: String,
pub inline: bool,
pub tokens: Option<Vec<Operator>>
}
#[derive(Debug, Clone)]
pub struct Constant {
pub loc: Loc,
pub name: String
}
#[derive(Debug, Clone)]
pub struct Memory {
pub loc: Loc,
pub id: usize
}
#[derive(Debug, Clone)]
pub struct StructDef {
pub loc: Loc,
pub name: String,
pub fields: Vec<(String, Types)>
}
pub type Functions = HashMap<String, Function>;
pub type Memories = HashMap<String, Memory>;
pub type Constants = HashMap<String, Constant>;
pub type StructDefs = HashMap<String, StructDef>;
#[derive(Debug, Clone)]
pub struct Program {
pub ops: Vec<Operator>,
pub functions: Functions,
pub memories: Memories,
pub constants: Constants,
pub struct_defs: StructDefs,
pub struct_allocs: HashMap<String, String>
}

View File

@ -48,7 +48,7 @@ pub fn run(ops: &[crate::constants::Operator]) -> Result<i32>{
ip += 1;
},
InstructionType::PushStr => {
if op.addr.is_none() {
if op.addr.is_none() {
stack.push(op.text.len()); // string len
stack.push(string_idx + crate::MEM_SZ);
@ -64,6 +64,23 @@ pub fn run(ops: &[crate::constants::Operator]) -> Result<i32>{
}
ip += 1;
},
InstructionType::PushCStr => {
if op.addr.is_none() {
stack.push(string_idx + crate::MEM_SZ);
for c in op.text.bytes() {
mem[crate::MEM_SZ + string_idx] = u64::from(c);
string_idx += 1;
}
} else {
if let Some(addr) = op.addr {
stack.push(addr);
}
}
ip += 1;
},
InstructionType::Drop => {
@ -109,11 +126,11 @@ pub fn run(ops: &[crate::constants::Operator]) -> Result<i32>{
ip += 1;
},
#[allow(clippy::cast_possible_truncation)]
InstructionType::Load8 |
InstructionType::Load32 |
InstructionType::Load64 => {
InstructionType::Read8 |
InstructionType::Read32 |
InstructionType::Read64 => {
let a = stack_pop(&mut stack, &pos)?;
if a > crate::MEM_SZ {
if a > crate::MEM_SZ + crate::STRING_SZ {
lerror!(&op.loc, "Invalid memory address {a}");
return Ok(1);
}
@ -122,7 +139,7 @@ pub fn run(ops: &[crate::constants::Operator]) -> Result<i32>{
ip += 1;
}
#[allow(clippy::cast_possible_truncation)]
InstructionType::Store8 => {
InstructionType::Write8 => {
let val = stack_pop(&mut stack, &pos)?;
let addr = stack_pop(&mut stack, &pos)?;
@ -135,7 +152,7 @@ pub fn run(ops: &[crate::constants::Operator]) -> Result<i32>{
ip += 1;
}
#[allow(clippy::cast_possible_truncation)]
InstructionType::Store32 => {
InstructionType::Write32 => {
let val = stack_pop(&mut stack, &pos)?;
let addr = stack_pop(&mut stack, &pos)?;
@ -149,7 +166,7 @@ pub fn run(ops: &[crate::constants::Operator]) -> Result<i32>{
}
#[allow(clippy::cast_possible_truncation)]
InstructionType::Store64 => {
InstructionType::Write64 => {
let val = stack_pop(&mut stack, &pos)?;
let addr = stack_pop(&mut stack, &pos)?;

View File

@ -1,5 +1,5 @@
use crate::{constants::{Token, TokenType}, Args};
use crate::{definitions::{Token, TokenType}, Args};
fn lex_word(s: String, tok_type: TokenType) -> (TokenType, String) {
match s {
@ -12,6 +12,9 @@ fn lex_word(s: String, tok_type: TokenType) -> (TokenType, String) {
s if tok_type == TokenType::String => {
(TokenType::String, s)
}
s if tok_type == TokenType::CString => {
(TokenType::CString, s)
}
s if tok_type == TokenType::Char => {
(TokenType::Char, s)
}
@ -72,17 +75,35 @@ fn lex_line(text: &str) -> Vec<(usize, String, TokenType)> {
} else {
col_end = find_col(text, col, |x, _| x.is_whitespace());
let t = &text[col..col_end];
if t == "//" {
return tokens;
if &text[col..=col] == "c" && text.len() - 1 + col > 0 && &text[col+1..=col+1] == "\"" {
col_end = find_col(text, col + 2, |x, x2| x == '"' && x2 != '\\');
let t = &text[(col + 2)..col_end];
let mut t = t.replace("\\n", "\n")
.replace("\\t", "\t")
.replace("\\r", "\r")
.replace("\\\'", "\'")
.replace("\\\"", "\"")
.replace("\\0", "\0");
if !t.is_empty() {
t.push('\0');
tokens.push((col, t.to_string(), TokenType::CString));
}
col = find_col(text, col_end + 1, |x, _| !x.is_whitespace());
} else {
col_end = find_col(text, col, |x, _| x.is_whitespace());
let t = &text[col..col_end];
if t == "//" {
return tokens;
}
if !t.is_empty() {
tokens.push((col, t.to_string(), TokenType::Word));
}
col = find_col(text, col_end, |x, _| !x.is_whitespace());
}
if !t.is_empty() {
tokens.push((col, t.to_string(), TokenType::Word));
}
col = find_col(text, col_end, |x, _| !x.is_whitespace());
}
}
tokens
@ -110,7 +131,7 @@ pub fn lex(code: &str, file: &str, _args: &Args) -> Vec<Token> {
typ: tok_type,
value: None,
addr: None,
op_typ: crate::constants::OpType::Instruction(crate::constants::InstructionType::None)
op_typ: crate::definitions::OpType::Instruction(crate::definitions::InstructionType::None)
};
tokens.push(t);
}

View File

@ -1,7 +1,6 @@
#![allow(clippy::wildcard_imports)]
#![allow(clippy::too_many_lines)]
mod constants;
mod interpret;
mod definitions;
mod util;
mod compile;
mod parser;
@ -11,28 +10,23 @@ mod typechecker;
mod precompiler;
mod config;
mod errors;
pub mod test;
use config::*;
use std::{fs, collections::HashMap};
use clap::Parser;
use color_eyre::Result;
use eyre::eyre;
use anyhow::{Result, bail};
#[derive(Parser, Debug, Clone)]
#[command(author, version, about, long_about = None)]
#[command(author=env!("CARGO_PKG_AUTHORS"), version=env!("CARGO_PKG_VERSION"), about=env!("CARGO_PKG_DESCRIPTION"), long_about=env!("CARGO_PKG_DESCRIPTION"))]
pub struct Args {
/// Input source file
#[arg(long, short)]
in_file: String,
/// Output compiled file
#[arg(long, short, default_value_t=String::from(DEFAULT_OUT_FILE))]
out_file: String,
/// Compile
#[arg(long, short)]
compile: bool,
/// Interpert
#[arg(long, short='s')]
interpret: bool,
@ -78,7 +72,7 @@ impl Args {
"0" | "" => Ok(1),
o => {
error!("Unknown optimisation level {o}");
Err(eyre!(""))
bail!("")
}
}
}
@ -98,7 +92,7 @@ fn main() -> Result<()>{
let mut parser = parser::Parser::new(tokens, &args, None);
let tokens = match parser.parse(){
let program = match parser.parse(){
Ok(t) => t,
Err(e) => {
error!("Parsing failed, exiting!");
@ -109,7 +103,7 @@ fn main() -> Result<()>{
}
};
match typechecker::typecheck(tokens.clone(), &args, None, HashMap::new(), HashMap::new()) {
match typechecker::typecheck(program.ops.clone(), &args, None, HashMap::new(), HashMap::new()) {
Ok(_) => (),
Err(e) => {
error!("Typechecking failed, exiting!");
@ -120,22 +114,14 @@ fn main() -> Result<()>{
}
};
let c = if args.compile && args.interpret {
error!("Cannot compile and interpret at the same time");
0
} else if args.interpret {
if let Ok(c) = interpret::linux_x86_64::run(&tokens) { c } else {
error!("Interpretation failed, exiting!");
1
}
} else if args.compile {
if let Ok(c) = compile::linux_x86_64::compile(&tokens, &args) { c } else {
let c =match compile::linux_x86_64::compile(&program, &args) {
Ok(c) => c,
Err(e) => {
error!("Compilation failed, exiting!");
println!("{e}");
1
}
} else {
error!("Did not choose to compile or to interpret, exiting");
0
};
std::process::exit(c);
}

View File

@ -1,8 +1,7 @@
use std::ops::Deref;
use crate::{constants::{Operator, OpType, Token, TokenType, Loc, KeywordType, InstructionType}, lerror, preprocessor::Preprocessor, Args};
use color_eyre::Result;
use eyre::eyre;
use crate::{definitions::{Operator, OpType, Token, TokenType, Loc, KeywordType, InstructionType, InternalType, Program}, lerror, preprocessor::Preprocessor, Args};
use anyhow::{Result, bail};
pub fn cross_ref(mut program: Vec<Operator>) -> Result<Vec<Operator>> {
let mut stack: Vec<usize> = Vec::new();
@ -18,11 +17,11 @@ pub fn cross_ref(mut program: Vec<Operator>) -> Result<Vec<Operator>> {
OpType::Keyword(KeywordType::Else) => {
let Some(if_ip) = stack.pop() else {
lerror!(&op.loc, "Unclosed-if else block");
return Err(eyre!("Cross referencing"));
bail!("Cross referencing")
};
if program[if_ip].typ != OpType::Keyword(KeywordType::If) {
lerror!(&op.clone().loc,"'else' can only close 'if' blocks");
return Err(eyre!("Bad block"));
bail!("Bad block")
}
program[if_ip].jmp = ip + 1;
@ -31,7 +30,7 @@ pub fn cross_ref(mut program: Vec<Operator>) -> Result<Vec<Operator>> {
OpType::Keyword(KeywordType::End) => {
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"));
bail!("Cross referencing")
};
match &program[block_ip].typ {
@ -50,7 +49,7 @@ pub fn cross_ref(mut program: Vec<Operator>) -> Result<Vec<Operator>> {
a => {
println!("{a:?}");
lerror!(&op.clone().loc,"'end' can only close if, if-else, while-do, function, memory, or constant blocks");
return Err(eyre!(""));
bail!("")
}
}
@ -58,7 +57,7 @@ pub fn cross_ref(mut program: Vec<Operator>) -> Result<Vec<Operator>> {
OpType::Keyword(KeywordType::Do) => {
let Some(block_ip) = stack.pop() else {
lerror!(&op.loc, "Unclosed while-do block");
return Err(eyre!("Cross referencing"));
bail!("Cross referencing")
};
program[ip].jmp = block_ip;
@ -70,8 +69,9 @@ pub fn cross_ref(mut program: Vec<Operator>) -> Result<Vec<Operator>> {
}
if !stack.is_empty() {
// 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"));
let i = stack.pop().expect("Empy stack");
lerror!(&program[i].clone().loc,"Unclosed block, {:?}", program[i].clone());
bail!("Unclosed block")
}
Ok(program.clone())
@ -97,7 +97,7 @@ impl<'a> Parser<'a> {
}
}
pub fn parse(&mut self) -> Result<Vec<Operator>> {
pub fn parse(&mut self) -> Result<Program> {
let mut tokens = Vec::new();
for token in &self.tokens {
@ -120,12 +120,15 @@ impl<'a> Parser<'a> {
},
TokenType::String => {
tokens.push(Operator::new(OpType::Instruction(InstructionType::PushStr), token.typ, 0, token.text.clone(), token.file.clone(), token.line, token.col));
}
},
TokenType::CString => {
tokens.push(Operator::new(OpType::Instruction(InstructionType::PushCStr), token.typ, 0, token.text.clone(), token.file.clone(), token.line, token.col));
},
TokenType::Char => {
let c = token.text.clone();
if c.len() != 1 {
lerror!(&token.loc(), "Chars can only be of lenght 1, got {}", c.len());
return Err(eyre!(""));
bail!("")
}
tokens.push(Operator::new(OpType::Instruction(InstructionType::PushInt), token.typ, token.text.chars().next().unwrap() as usize, String::new(), token.file.clone(), token.line, token.col));
@ -134,9 +137,9 @@ impl<'a> Parser<'a> {
}
self.preprocessor.program = tokens;
let t = self.preprocessor.preprocess()?.get_ops();
let t = cross_ref(t)?;
self.preprocessor.program.ops = tokens;
let mut t = self.preprocessor.preprocess()?.get_program();
t.ops = cross_ref(t.ops)?;
Ok(t)
}
@ -176,12 +179,12 @@ pub fn lookup_word<P: Deref<Target = Loc>>(s: &str, _pos: P) -> OpType {
// mem
"load8" => OpType::Instruction(InstructionType::Load8),
"store8" => OpType::Instruction(InstructionType::Store8),
"load32" => OpType::Instruction(InstructionType::Load32),
"store32" => OpType::Instruction(InstructionType::Store32),
"load64" => OpType::Instruction(InstructionType::Load64),
"store64" => OpType::Instruction(InstructionType::Store64),
"read8" => OpType::Instruction(InstructionType::Read8),
"write8" => OpType::Instruction(InstructionType::Write8),
"read32" => OpType::Instruction(InstructionType::Read32),
"write32" => OpType::Instruction(InstructionType::Write32),
"read64" => OpType::Instruction(InstructionType::Read64),
"write64" => OpType::Instruction(InstructionType::Write64),
"syscall0" => OpType::Instruction(InstructionType::Syscall0),
"syscall1" => OpType::Instruction(InstructionType::Syscall1),
@ -208,6 +211,7 @@ pub fn lookup_word<P: Deref<Target = Loc>>(s: &str, _pos: P) -> OpType {
"done" => OpType::Keyword(KeywordType::FunctionDone),
"inline" => OpType::Keyword(KeywordType::Inline),
"export" => OpType::Keyword(KeywordType::Export),
"struct" => OpType::Keyword(KeywordType::Struct),
"return" => OpType::Instruction(InstructionType::Return),
"returns" => OpType::Instruction(InstructionType::Returns),
"bool" => OpType::Instruction(InstructionType::TypeBool),
@ -216,6 +220,9 @@ pub fn lookup_word<P: Deref<Target = Loc>>(s: &str, _pos: P) -> OpType {
"void" => OpType::Instruction(InstructionType::TypeVoid),
"any" => OpType::Instruction(InstructionType::TypeAny),
"with" => OpType::Instruction(InstructionType::With),
"->" => OpType::Internal(InternalType::Arrow),
_ => OpType::Instruction(InstructionType::None)
}

View File

@ -1,13 +1,12 @@
use color_eyre::Result;
use eyre::eyre;
use anyhow::{Result, bail};
use crate::{constants::{ OpType, InstructionType, Loc, Operator}, lerror};
use crate::{definitions::{ OpType, InstructionType, Loc, Operator}, lerror};
fn stack_pop(stack: &mut Vec<usize>, loc: &Loc) -> Result<usize> {
if let Some(i) = stack.pop() { Ok(i) } else {
lerror!(&loc.clone(), "Stack underflow");
Err(eyre!("Stack underflow"))
bail!("Stack underflow")
}
}
@ -131,15 +130,16 @@ pub fn precompile(tokens: &Vec<Operator>) -> Result<Vec<usize>>{
_ => {
lerror!(&token.loc, "Unsupported precompiler instruction {:?}", i);
dbg!(tokens);
return Err(eyre!(""));
bail!("");
}
}
}
OpType::Keyword(_) => {
lerror!(&token.loc, "Unsupported precompiler keyword {:?}", token.typ);
dbg!(tokens);
return Err(eyre!(""));
bail!("");
}
OpType::Internal(t) => panic!("{t:?}"),
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,7 @@
use std::collections::HashMap;
use crate::{constants::{Operator, Types, OpType, KeywordType, InstructionType, Loc}, Args, lerror, warn};
use color_eyre::Result;
use eyre::eyre;
use crate::{definitions::{Operator, Types, OpType, KeywordType, InstructionType, Loc}, Args, lerror, warn};
use anyhow::{Result, bail};
#[allow(dead_code)]
#[derive(Debug, Clone)]
@ -69,12 +68,12 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>
if let Some(p) = rtokens.pop() {
if p.typ != OpType::Instruction(InstructionType::With){
lerror!(&op.loc, "Expected {:?}, got {:?}", OpType::Instruction(InstructionType::With), p.typ);
return Err(eyre!(""));
bail!("");
}
} else {
lerror!(&op.loc, "Expected {:?}, got nothing", OpType::Instruction(InstructionType::With));
return Err(eyre!(""));
bail!("");
}
let mut p = rtokens.pop();
@ -92,7 +91,7 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>
op.typ == OpType::Instruction(InstructionType::TypeAny) ||
op.typ == OpType::Instruction(InstructionType::TypeVoid) {
let t = if op.typ == OpType::Instruction(InstructionType::TypeInt) {
Types::Int
Types::U64
} else if op.typ == OpType::Instruction(InstructionType::TypeBool) {
Types::Bool
} else if op.typ == OpType::Instruction(InstructionType::TypePtr) {
@ -153,7 +152,7 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>
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!(""))
bail!("");
}
if !func.args.contains(&Types::Void) {
@ -170,7 +169,7 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>
KeywordType::Memory => (),
KeywordType::ConstantDef => {
// println!("defined constant");
constants.insert(op.text, Constant { loc: op.loc.clone(), types: vec![Types::Int] });
constants.insert(op.text, Constant { loc: op.loc.clone(), types: vec![Types::U64] });
},
KeywordType::FunctionThen |
@ -181,23 +180,27 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>
println!("{:?}", op);
unreachable!()
},
KeywordType::Struct => todo!(),
}
},
OpType::Instruction(instruction) => {
match instruction {
InstructionType::PushInt => {
stack.push(Types::Int);
stack.push(Types::U64);
},
InstructionType::PushStr => {
stack.push(Types::Int);
stack.push(Types::U64);
stack.push(Types::Ptr);
},
InstructionType::PushCStr => {
stack.push(Types::U64);
stack.push(Types::Ptr);
},
InstructionType::Drop => {
stack_pop(&mut stack, &op, &[Types::Any])?;
},
InstructionType::Print => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::U64])?;
},
InstructionType::Dup => {
let a = stack_pop(&mut stack, &op, &[Types::Any])?;
@ -231,9 +234,9 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>
InstructionType::Shr |
InstructionType::Shl |
InstructionType::Mul => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Int);
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::U64])?;
stack.push(Types::U64);
},
InstructionType::Equals |
InstructionType::Gt |
@ -241,76 +244,76 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>
InstructionType::Ge |
InstructionType::Le |
InstructionType::NotEquals => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::U64])?;
stack.push(Types::Bool);
},
InstructionType::DivMod => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Int);
stack.push(Types::Int);
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::U64])?;
stack.push(Types::U64);
stack.push(Types::U64);
},
InstructionType::Load8 |
InstructionType::Load32 |
InstructionType::Load64 => {
InstructionType::Read8 |
InstructionType::Read32 |
InstructionType::Read64 => {
stack_pop(&mut stack, &op, &[Types::Ptr])?;
stack.push(Types::Int);
stack.push(Types::U64);
},
InstructionType::Store8 |
InstructionType::Store32 |
InstructionType::Store64 => {
stack_pop(&mut stack, &op, &[Types::Int])?;
InstructionType::Write8 |
InstructionType::Write32 |
InstructionType::Write64 => {
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::Ptr])?;
},
InstructionType::Syscall0 => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack.push(Types::Int);
stack_pop(&mut stack, &op, &[Types::U64])?;
stack.push(Types::U64);
},
InstructionType::Syscall1 => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(Types::Int);
stack.push(Types::U64);
},
InstructionType::Syscall2 => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(Types::Int);
stack.push(Types::U64);
},
InstructionType::Syscall3 => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(Types::Int);
stack.push(Types::U64);
},
InstructionType::Syscall4 => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(Types::Int);
stack.push(Types::U64);
},
InstructionType::Syscall5 => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(Types::Int);
stack.push(Types::U64);
},
InstructionType::Syscall6 => {
stack_pop(&mut stack, &op, &[Types::Int])?;
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(Types::Int);
stack.push(Types::U64);
},
InstructionType::CastBool => {
stack_pop(&mut stack, &op, &[Types::Any])?;
@ -322,7 +325,7 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>
},
InstructionType::CastInt => {
stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(Types::Int);
stack.push(Types::U64);
},
InstructionType::CastVoid => {
stack_pop(&mut stack, &op, &[Types::Any])?;
@ -336,7 +339,7 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>
let f = if let Some(f) = functions.get(&op.text) {f} else {
lerror!(&op.loc, "Could not find function {}", op.text);
return Err(eyre!(""));
bail!("");
};
// in_function = (op.text.clone(), f.clone(), op.loc.clone());
@ -350,11 +353,11 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>
if let Some(s2) = s.pop(){
if t != s2 {
lerror!(&op.loc, "Expected {:?}, but got {:?}", t, s2);
return Err(eyre!(""));
bail!("");
}
} else {
lerror!(&op.loc, "Expected {:?}, but got nothing", t);
return Err(eyre!(""));
bail!("");
}
}
@ -374,8 +377,10 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>
let mut c = constants.get(&op.text).unwrap().clone();
stack.append(&mut c.types);
},
InstructionType::StructUse => todo!(),
}
},
OpType::Internal(t) => panic!("{t:?}"),
}
@ -390,13 +395,13 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>
fn stack_pop(v: &mut Vec<Types>, op: &Operator, t: &[Types]) -> Result<Types> {
if v.is_empty() {
lerror!(&op.loc, "Expected {:?}, but got nothing", t);
return Err(eyre!(""));
bail!("");
}
let r = v.pop().unwrap();
if !t.contains(&r) && t[0] != Types::Any {
lerror!(&op.loc, "Expected {:?}, but got {:?}", t, r);
return Err(eyre!(""));
bail!("");
}
Ok(r)

392
src/types.rs Normal file
View File

@ -0,0 +1,392 @@
use std::collections::{HashMap, HashSet};
use eyre::bail;
#[derive(Debug, Clone, PartialEq)]
pub enum InstructionType {
// stack
PushInt,
PushStr,
PushCStr,
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,
Returns,
With,
FnCall,
MemUse,
ConstUse,
Return,
None // Used for macros and any other non built in word definitions
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum KeywordType {
If,
Else,
End,
While,
Do,
Include,
Memory,
Constant,
ConstantDef,
Function,
FunctionDef,
FunctionDefExported,
FunctionThen,
FunctionDone,
Inline,
Export,
Struct,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum InternalType {
Arrow
}
#[derive(Debug, Clone, PartialEq)]
pub enum OpType {
Keyword(KeywordType),
Instruction(InstructionType),
Internal(InternalType)
}
#[derive(Debug, Clone)]
pub struct Operator{
pub typ: OpType,
pub tok_typ: TokenType,
pub value: usize,
pub text: String, //? only used for OpType::PushStr
pub addr: Option<usize>, //? only used for OpType::PushStr
pub jmp: usize,
pub loc: Loc,
pub types: (usize, usize)
}
impl Operator {
pub fn new(typ: OpType, tok_typ: TokenType, value: usize, text: String, file: String, row: usize, col: usize) -> Self {
Self {
typ,
value,
jmp: 0,
addr: None,
text,
loc: (file, row, col),
tok_typ,
types: (0, 0)
}
}
pub fn set_addr(&mut self, addr: usize) -> Self {
self.addr = Some(addr);
(*self).clone()
}
// pub fn set_types(&mut self, args: usize, rets: usize) -> Self {
// self.types = (args, rets);
// (*self).clone()
// }
}
impl OpType {
pub fn human(&self) -> String {
match (*self).clone() {
OpType::Instruction(instruction) => {
match instruction {
InstructionType::PushInt => "Number",
InstructionType::PushStr => "String",
InstructionType::PushCStr => "CString",
InstructionType::Print => "_dbg_print",
InstructionType::Dup => "dup",
InstructionType::Drop => "drop",
InstructionType::Rot => "rot",
InstructionType::Over => "over",
InstructionType::Swap => "swap",
InstructionType::Plus => "+",
InstructionType::Minus => "-",
InstructionType::Equals => "=",
InstructionType::Gt => ">",
InstructionType::Lt => "<",
InstructionType::NotEquals => "!=",
InstructionType::Le => "<=",
InstructionType::Ge => ">=",
InstructionType::Band => "band",
InstructionType::Bor => "bor",
InstructionType::Shr => "shr",
InstructionType::Shl => "shl",
InstructionType::DivMod => "divmod",
InstructionType::Mul => "*",
InstructionType::Read8 => "read8",
InstructionType::Write8 => "write8",
InstructionType::Read32 => "read32",
InstructionType::Write32 => "write32",
InstructionType::Read64 => "read64",
InstructionType::Write64 => "write64",
InstructionType::Syscall0 => "syscall0",
InstructionType::Syscall1 => "syscall1",
InstructionType::Syscall2 => "syscall2",
InstructionType::Syscall3 => "syscall3",
InstructionType::Syscall4 => "syscall4",
InstructionType::Syscall5 => "syscall5",
InstructionType::Syscall6 => "syscall6",
InstructionType::CastBool => "cast(bool",
InstructionType::CastPtr => "cast(ptr)",
InstructionType::CastInt => "cast(int)",
InstructionType::CastVoid => "cast(void)",
InstructionType::None => "None",
InstructionType::MemUse => "Memory use (internal)",
InstructionType::FnCall => "Function Call (Internal)",
InstructionType::ConstUse => "Constant Use (Internal)",
InstructionType::Return => "return",
InstructionType::TypeBool => "bool",
InstructionType::TypePtr => "ptr",
InstructionType::TypeInt => "int",
InstructionType::TypeVoid => "void",
InstructionType::Returns => "returns",
InstructionType::With => "with",
InstructionType::TypeAny => "any",
}
}
OpType::Keyword(keyword) => {
match keyword {
KeywordType::If => "if",
KeywordType::Else => "else",
KeywordType::End => "end",
KeywordType::While => "while",
KeywordType::Do => "do",
KeywordType::Include => "include",
KeywordType::Memory => "memory",
KeywordType::Function => "fn",
KeywordType::Constant => "const",
KeywordType::FunctionThen => "then",
KeywordType::FunctionDone => "done",
KeywordType::ConstantDef => "constant Definition (internal)",
KeywordType::FunctionDef => "function definition (internal)",
KeywordType::FunctionDefExported => "extern function definition (internal)",
KeywordType::Inline => "inline",
KeywordType::Export => "export",
KeywordType::Struct => "struct",
}
}
OpType::Internal(t) => panic!("{t:?}"),
}.to_string()
}
}
#[derive(Debug, Clone)]
pub struct Token {
pub file: String,
pub line: usize,
pub col: usize,
pub text: String,
pub typ: TokenType,
pub value: Option<usize>, //* only used for Memories
pub addr: Option<usize>, //* only used for Memories
pub op_typ: OpType //* only used for Memories
}
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum TokenType {
Word,
Int,
String,
CString,
Char
}
impl Token {
pub fn loc(&self) -> Loc {
(
self.file.clone(),
self.line,
self.col
)
}
}
impl TokenType {
pub fn human(self) -> String {
match self {
TokenType::Word => "Word",
TokenType::Int => "Int",
TokenType::String => "String",
TokenType::CString => "CString",
TokenType::Char => "Char"
}.to_string()
}
}
pub type Loc = (String, usize, usize);
#[derive(Debug, PartialEq, Clone)]
pub enum Types {
Any,
Bool,
Ptr,
Void,
U8,
U16,
U32,
U64,
I8,
I16,
I32,
I64,
Custom{
size: u64 // in bytes
},
// todo: add signed numbers since we dont have them yet lol
}
impl Types {
pub fn get_size(&self) -> u64 {
match *self {
Types::Any => 0, // any cant be a known size
Types::Void => 0,
Types::Bool => 1,
Types::U8 |
Types::I8 => 1,
Types::U16 |
Types::I16 => 2,
Types::U32 |
Types::I32 => 4,
Types::Ptr |
Types::U64 |
Types::I64 => 8,
Types::Custom { size } => size,
}
}
}
impl TryInto<Types> for &str {
type Error = color_eyre::eyre::Error;
fn try_into(self) -> Result<Types, Self::Error> {
match self {
"Any" => Ok(Types::Any),
"Void" => Ok(Types::Void),
"Bool" => Ok(Types::Bool),
"U8" => Ok(Types::U8),
"I8" => Ok(Types::I8),
"U16" => Ok(Types::U16),
"I16" => Ok(Types::I16),
"U32" => Ok(Types::U32),
"I32" => Ok(Types::I32),
"Ptr" => Ok(Types::Ptr),
"U64" => Ok(Types::U64),
"I64" => Ok(Types::I64),
_ => bail!("Unknown type {self}")
}
}
}
impl TryInto<Types> for String {
type Error = color_eyre::eyre::Error;
fn try_into(self) -> Result<Types, Self::Error> {
self.into()
}
}
#[derive(Debug, Clone)]
pub struct Function {
pub loc: Loc,
pub name: String,
pub inline: bool,
pub tokens: Option<Vec<Operator>>
}
#[derive(Debug, Clone)]
pub struct Constant {
pub loc: Loc,
pub name: String
}
#[derive(Debug, Clone)]
pub struct Memory {
pub loc: Loc,
pub id: usize
}
#[derive(Debug, Clone)]
pub struct StructDef {
pub loc: Loc,
pub name: String,
pub fields: HashSet<(String, Types)>
}
pub type Functions = HashMap<String, Function>;
pub type Memories = HashMap<String, Memory>;
pub type Constants = HashMap<String, Constant>;
pub type StructDefs = HashMap<String, StructDef>;
#[derive(Debug, Clone)]
pub struct Program {
pub ops: Vec<Operator>,
pub functions: Functions,
pub memories: Memories,
pub constants: Constants,
pub struct_defs: StructDefs
}

29
src/types/common/loc.rs Normal file
View File

@ -0,0 +1,29 @@
pub struct Loc {
pub ln: usize,
pub col: usize,
pub file: String
}
impl Loc {
pub fn new(file: String, ln: usize, col: usize) -> Self {
Self {
ln,
col,
file,
}
}
}
impl ToString for Loc {
fn to_string(&self) -> String {
format!("{}:{}:{}", self.file, self.ln, self.col)
}
}
impl Into<String> for Loc {
fn into(self) -> String {
self.to_string()
}
}

4
src/types/common/mod.rs Normal file
View File

@ -0,0 +1,4 @@
mod loc;
pub use loc::*;

4
src/types/mod.rs Normal file
View File

@ -0,0 +1,4 @@
pub mod common;
pub mod token;

65
src/types/token.rs Normal file
View File

@ -0,0 +1,65 @@
use super::common::Loc;
struct Token {
loc: Loc,
typ: TokenType,
val: TokenValue
}
pub enum TokenValue {
Int(usize),
Str(String)
}
pub enum TokenType {
DbgPrint,
Keyword(TokenKeyword),
Syscall(u8),
//? Literal
PushInt,
PushStr,
//? Stack manipulation
Dup,
Rot, // a b c => b c a
Over, // a b => a b a
Swap, // a b => b a
//? Math
Plus,
Minus,
Mul,
Div,
Mod,
//? Logical
And,
Or,
Eq,
Gt,
Lt,
Ge,
Le,
Ne,
//? Bitwise
Shr,
Shl,
Bor,
Band,
}
pub enum TokenKeyword {
Function,
If,
Else,
End,
Done,
Macro,
While,
Do
}

View File

@ -32,7 +32,7 @@ pub mod logger {
#![allow(dead_code)]
use std::ops::Deref;
use crate::{util::color, constants::Loc};
use crate::{util::color, definitions::Loc};
pub fn error(msg: &str) {
println!("{red}error{r}: {msg}", red=color::FG_RED, r=color::RESET);

View File

@ -1,12 +1,13 @@
// include "std.mcl"
fn mcl_print with int ptr returns void then
1 1 syscall3 drop
done
include "std.mcl"
fn mcl_dump with int returns void then
_dbg_print
done
fn main with void returns void then
"hi\n" mcl_print
done
fn main with int ptr returns void then
// p l
"Hello!\n" puts
// memory fd 4 end
// c"./test.mcl" FS_O_SYNC 0 fopen
// dup _dbg_print
// fd swap write32
done

4
tools/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/*
!/.gitignore
!/*.c
!/build_tools.sh

3
tools/build_tools.sh Normal file
View File

@ -0,0 +1,3 @@
#!/usr/bin/bash
gcc intcnv.c -o intcnv

47
tools/intcnv.c Normal file
View File

@ -0,0 +1,47 @@
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char** argv) {
if (argc < 3) {
printf("Usage: intcnv i32 134\n");
return 1;
}
const char* typ = argv[1];
__uint64_t num = atoi(argv[2]);
if (strcmp(typ, "u8") == 0) {
__uint8_t num2 = (__uint8_t)num;
printf("%d\n", num2);
}
if (strcmp(typ, "i8") == 0) {
__int8_t num2 = (__int8_t)num;
printf("%d\n", num2);
}
if (strcmp(typ, "u16") == 0) {
__uint16_t num2 = (__uint16_t)num;
printf("%d\n", num2);
}
if (strcmp(typ, "i16") == 0) {
__int16_t num2 = (__int16_t)num;
printf("%d\n", num2);
}
if (strcmp(typ, "u32") == 0) {
__uint32_t num2 = (__uint32_t)num;
printf("%d\n", num2);
}
if (strcmp(typ, "i32") == 0) {
__int32_t num2 = (__int32_t)num;
printf("%d\n", num2);
}
if (strcmp(typ, "u64") == 0) {
__uint64_t num2 = (__uint64_t)num;
printf("%d\n", num2);
}
if (strcmp(typ, "i64") == 0) {
__int64_t num2 = (__int64_t)num;
printf("%d\n", num2);
}
}