Added cstrings

This commit is contained in:
MCorange 2023-04-23 17:51:05 +03:00
parent 2d374d5d9d
commit 9625256554
15 changed files with 146 additions and 57 deletions

2
Cargo.lock generated
View File

@ -210,7 +210,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "mclang"
name = "mclangc"
version = "0.1.0"
dependencies = [
"clap",

View File

@ -1,7 +1,11 @@
[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]

View File

@ -18,6 +18,12 @@ 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
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

View File

@ -16,3 +16,9 @@ const FS_O_PATH 2097152 end // open descriptor for obtaining permissions and sta
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

@ -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,7 +46,7 @@ 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

View File

@ -72,20 +72,18 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
writeln!(writer, " add rsp, 40")?;
writeln!(writer, " ret")?;
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);
if debug {
writeln!(writer, "addr_{ti}:")?;
if token.typ == OpType::Instruction(InstructionType::PushInt) {
@ -116,8 +114,8 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
_ => ()
}
}
}
match token.typ.clone() {
// stack
@ -136,6 +134,13 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
strings.push(token.text.clone());
ti += 1;
}
InstructionType::PushCStr => {
writeln!(writer, " push rax")?;
writeln!(writer, " mov rax, str_{}", strings.len())?;
writeln!(writer, " push rax")?;
strings.push(token.text.clone());
ti += 1;
}
InstructionType::Drop => {
writeln!(writer, " pop rax")?;
ti += 1;
@ -198,7 +203,7 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
InstructionType::Load32 => {
writeln!(writer, " pop rax")?;
writeln!(writer, " xor rbx, rbx")?;
writeln!(writer, " mov bl, dword [rax]")?;
writeln!(writer, " mov ebx, dword [rax]")?;
writeln!(writer, " push rbx")?;
ti += 1;
}
@ -206,13 +211,13 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
InstructionType::Store32 => {
writeln!(writer, " pop rbx")?;
writeln!(writer, " pop rax")?;
writeln!(writer, " mov dword[rax], bl")?;
writeln!(writer, " mov dword[rax], ebx")?;
ti += 1;
}
InstructionType::Load64 => {
writeln!(writer, " pop rax")?;
writeln!(writer, " xor rbx, rbx")?;
writeln!(writer, " mov bl, qword [rax]")?;
writeln!(writer, " mov rbx, qword [rax]")?;
writeln!(writer, " push rbx")?;
ti += 1;
}
@ -220,7 +225,7 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
InstructionType::Store64 => {
writeln!(writer, " pop rbx")?;
writeln!(writer, " pop rax")?;
writeln!(writer, " mov qword [rax], bl")?;
writeln!(writer, " mov qword [rax], rbx")?;
ti += 1;
}
@ -421,6 +426,7 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
},
InstructionType::Return => {
// Experimental feature exported functions
if crate::config::ENABLE_EXPORTED_FUNCTIONS && should_push_ret {
writeln!(writer, " pop rdx")?;
should_push_ret = false;
@ -479,7 +485,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;
},
@ -584,7 +590,7 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
}
}
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")?;

View File

@ -4,9 +4,9 @@
pub const DEV_MODE: bool = false;
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",
];

View File

@ -7,6 +7,7 @@ pub enum InstructionType {
// stack
PushInt,
PushStr,
PushCStr,
Drop,
Print,
Dup,
@ -131,7 +132,7 @@ impl Operator {
// self.types = (args, rets);
// (*self).clone()
// }
}
impl OpType {
@ -139,9 +140,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",
@ -235,6 +237,7 @@ pub enum TokenType {
Word,
Int,
String,
CString,
Char
}
@ -254,6 +257,7 @@ impl TokenType {
TokenType::Word => "Word",
TokenType::Int => "Int",
TokenType::String => "String",
TokenType::CString => "CString",
TokenType::Char => "Char"
}.to_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 => {
@ -113,7 +130,7 @@ pub fn run(ops: &[crate::constants::Operator]) -> Result<i32>{
InstructionType::Load32 |
InstructionType::Load64 => {
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);
}

View File

@ -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

View File

@ -19,7 +19,7 @@ use color_eyre::Result;
use eyre::eyre;
#[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)]

View File

@ -70,7 +70,8 @@ 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());
let i = stack.pop().expect("Empy stack");
lerror!(&program[i].clone().loc,"Unclosed block, {:?}", program[i].clone());
return Err(eyre!("Unclosed block"));
}
@ -120,7 +121,10 @@ 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 {

View File

@ -45,6 +45,7 @@ pub struct Preprocessor<'a> {
pub functions: Functions,
pub memories: Memories,
pub constants: Constants,
pub in_function: Option<String>,
args: &'a Args
}
@ -57,6 +58,7 @@ impl<'a> Preprocessor<'a> {
functions: HashMap::new(),
memories: HashMap::new(),
constants: HashMap::new(),
in_function: None
}
}
@ -65,7 +67,7 @@ impl<'a> Preprocessor<'a> {
// println!("pre: has do tokens: {:?}", self.program.iter().map(|t| if t.typ == OpType::Keyword(KeywordType::Do) {Some(t)} else {None} ).collect::<Vec<Option<&Operator>>>());
let mut f_inline = false;
let mut f_extern = false;
let mut f_export = false;
let mut program: Vec<Operator> = Vec::new();
@ -94,7 +96,7 @@ impl<'a> Preprocessor<'a> {
let mut include_code = String::new();
let mut pth = PathBuf::new();
if include_path.text.chars().collect::<Vec<char>>()[0] == '.' {
if include_path.text.chars().next().unwrap() == '.' {
let p = Path::new(include_path.loc.0.as_str());
let p = p.parent().unwrap();
let p = p.join(&include_path.text);
@ -108,6 +110,7 @@ impl<'a> Preprocessor<'a> {
if p.exists() {
include_code = std::fs::read_to_string(p)?;
break;
}
}
@ -261,6 +264,9 @@ impl<'a> Preprocessor<'a> {
}
let mut pre = self.clone();
pre.program = prog;
if name.text.chars().next().unwrap() == '.' {
pre.in_function = Some(name.text[1..].to_string());
}
pre.preprocess()?;
prog = pre.get_ops();
@ -271,8 +277,8 @@ impl<'a> Preprocessor<'a> {
tokens: Some(prog)
});
} else if f_extern {
f_extern = false;
} else if f_export {
f_export = false;
self.functions.insert(name.text.clone(), Function{
loc: name.loc.clone(),
name: name.text.clone(),
@ -341,9 +347,10 @@ impl<'a> Preprocessor<'a> {
let mut name = rtokens.pop().unwrap();
// let mut should_warn = false;
if let '0'..='9' = name.text.chars().next().unwrap() {
lerror!(&name.loc, "Constant name starts with a number which is not allowed");
if let '0'..='9' | '.' = name.text.chars().next().unwrap() {
lerror!(&name.loc, "Constant name starts with a number or dot which is not allowed");
return Err(eyre!(""));
}
@ -363,6 +370,7 @@ impl<'a> Preprocessor<'a> {
}
}
}
// if should_warn {
//TODO: add -W option in cli args to enable more warnings
//lwarn!(&name.loc, "Constant name contains '(' or ')', this character is not supported but will be replaced with '__OP_PAREN__' or '__CL_PAREN__' respectively ");
@ -402,8 +410,8 @@ impl<'a> Preprocessor<'a> {
}
OpType::Keyword(KeywordType::Inline) => {
if f_extern {
lerror!(&op.loc, "Function is already marked as extern, function cannot be inline and extern at the same time");
if f_export {
lerror!(&op.loc, "Function is already marked as exported, function cannot be inline and exported at the same time");
return Err(eyre!(""));
} else if f_inline {
lerror!(&op.loc, "Function is already marked as inline, remove this inline Keyword");
@ -414,14 +422,18 @@ impl<'a> Preprocessor<'a> {
}
OpType::Keyword(KeywordType::Export) => {
if f_inline {
lerror!(&op.loc, "Function is already marked as inline, function cannot be inline and extern at the same time");
if !crate::config::ENABLE_EXPORTED_FUNCTIONS {
lerror!(&op.loc, "Experimental feature Exported functions not enabled");
return Err(eyre!(""));
} else if f_extern {
}
if f_inline {
lerror!(&op.loc, "Function is already marked as inline, function cannot be inline and exported at the same time");
return Err(eyre!(""));
} else if f_export {
lerror!(&op.loc, "Function is already marked as extern, remove this extern Keyword");
return Err(eyre!(""));
} else {
f_extern = true;
f_export = true;
}
}

View File

@ -191,7 +191,10 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>
InstructionType::PushStr => {
stack.push(Types::Int);
stack.push(Types::Ptr);
},
InstructionType::PushCStr => {
stack.push(Types::Int);
stack.push(Types::Ptr);
},
InstructionType::Drop => {
stack_pop(&mut stack, &op, &[Types::Any])?;

View File

@ -1,12 +1,7 @@
// 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 int ptr returns void then
// p l
c"bad, wait no, good\n\0" dup cstr_len swap puts
fn main with void returns void then
"hi\n" mcl_print
done
done