Add all of the code gen, want to show it off

This commit is contained in:
Gvidas Juknevičius 2025-03-27 18:49:17 +02:00
parent f338f07e7d
commit 06d8c1b0f3
Signed by: MCorange
GPG Key ID: 5BE6B533CB76FE86
33 changed files with 1199 additions and 95 deletions

3
.gitignore vendored
View File

@ -1 +1,4 @@
/target
/*.c
/*.s
/*.ssa

4
Cargo.lock generated
View File

@ -188,9 +188,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.90"
version = "2.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
dependencies = [
"proc-macro2",
"quote",

View File

@ -9,5 +9,7 @@ default-run = "mclangc"
anyhow = "1.0.94"
camino = "1.1.9"
clap = { version = "4.5.23", features = ["derive"] }
# inkwell = { version = "0.5.0", default-features = false, features = ["target-x86"] }
lazy_static = "1.5.0"
#llvm-lib = "0.8.1"
parse_int = "0.6.0"

View File

@ -11,6 +11,8 @@ pub struct CliArgs {
/// Output nothing, except errors, will overwrite -v|--verbose
#[arg(long, short)]
quiet: bool,
#[arg(long, short, value_parser=crate::targets::get_all_targets(), default_value_t=crate::targets::get_default_target())]
pub target: String,
/// Output file
#[arg(long, short, default_value="a.out")]
pub output: String,

View File

@ -5,3 +5,5 @@ pub mod cli;
#[macro_use]
pub mod logger;
pub mod validator;
#[macro_use]
pub mod targets;

View File

@ -11,6 +11,7 @@ fn main() -> anyhow::Result<()> {
let cli = mclangc::cli::CliArgs::parse();
cli.set_log_level();
cli.validate();
let mut progs = Vec::new();
for file in &cli.input {
let fp = PathBuf::from(file);
if !fp.exists() {
@ -18,14 +19,17 @@ fn main() -> anyhow::Result<()> {
anyhow::bail!("")
}
let data = std::fs::read_to_string(fp).unwrap();
let data = std::fs::read_to_string(&fp).unwrap();
info!("Tokenising {file}");
let tokens = mclangc::tokeniser::tokenise(&data, &file)?;
info!("Parsing {file}");
let mut prog = mclangc::parser::parse_program(tokens)?;
info!("Validating {file}");
mclangc::validator::validate_code(&mut prog)?;
dbg!(&prog);
// dbg!(&prog);
progs.push((fp, prog));
}
mclangc::targets::compile(&cli, progs)?;
Ok(())
}

View File

@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::{collections::HashMap, fmt::Display};
use crate::{common::loc::LocBox, tokeniser::tokentype::*};
@ -87,6 +87,18 @@ pub struct Block(pub Vec<Ast>);
#[derive(Debug, Clone)]
pub struct Path(pub Vec<Ident>);
impl Display for Path {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut buf = String::new();
for p in &self.0 {
if !buf.is_empty() {
buf.push_str("::");
}
buf.push_str(&p.to_string());
}
write!(f, "{}", buf)
}
}
#[derive(Debug, Clone)]
pub struct IfExpr {

View File

@ -12,11 +12,7 @@ pub enum Literal {
Char(Char),
Array(Vec<LocBox<Expr>>),
ArrayRepeat {
typ: Box<LocBox<Type>>,
val: Box<LocBox<Expr>>,
count: Box<LocBox<Expr>>,
},
Struct {
name: Ident,
fields: HashMap<Ident, Ast>
},
}

View File

@ -1,6 +1,6 @@
use std::collections::HashMap;
use statement::{Enum, Function, Struct};
use statement::{ConstVar, Enum, Function, StaticVar, Struct};
use crate::{common::loc::LocBox, validator::predefined::TypeType};
pub use crate::tokeniser::tokentype::*;
@ -18,6 +18,8 @@ pub struct Program {
pub types: HashMap<Ident, TypeType>,
pub functions: HashMap<Ident, LocBox<Function>>,
pub member_functions: HashMap<Ident, HashMap<Ident, LocBox<Function>>>,
pub static_vars: HashMap<Ident, StaticVar>,
pub const_vars: HashMap<Ident, ConstVar>
}
#[derive(Debug, Clone)]

View File

@ -1,7 +1,11 @@
use crate::common::{loc::LocBox, Loc};
use std::collections::HashMap;
use super::{expr::{Block, Expr}, typ::Type, Ident, TString};
use anyhow::bail;
use crate::{common::{loc::LocBox, Loc}, lerror};
use super::{expr::{Block, Expr}, literal::Literal, typ::Type, Char, Ident, Number, Program, TString};
#[derive(Debug, Clone)]
pub enum Statement {
@ -9,16 +13,8 @@ pub enum Statement {
TypeAlias(TypeAlias),
Struct(Struct),
Enum(Enum),
ConstVar {
name: Ident,
typ: LocBox<Type>,
val: LocBox<Expr>
},
StaticVar {
name: Ident,
typ: LocBox<Type>,
val: LocBox<Expr>,
},
ConstVar(ConstVar),
StaticVar(StaticVar),
Let {
name: Ident,
typ: Option<LocBox<Type>>,
@ -26,6 +22,20 @@ pub enum Statement {
},
}
#[derive(Debug, Clone)]
pub struct ConstVar {
pub name: Ident,
pub typ: LocBox<Type>,
pub val: LocBox<Expr>,
}
#[derive(Debug, Clone)]
pub struct StaticVar {
pub name: Ident,
pub typ: LocBox<Type>,
pub val: LocBox<Expr>,
}
#[derive(Debug, Clone)]
pub struct TypeAlias {
pub name: Ident,
@ -54,3 +64,183 @@ pub struct Function {
pub qual_extern: Option<TString>, // abi
pub body: Option<Block>, // If None then its a type declaration
}
impl Function {
pub fn get_full_name(&self) -> String {
if let Some(sn) = &self.struct_name {
format!("{sn}__S__{}", self.name.0)
} else {
format!("{}", self.name.0)
}
}
pub fn get_full_name_pretty(&self) -> String {
if let Some(sn) = &self.struct_name {
format!("{sn}::{}", self.name.0)
} else {
format!("{}", self.name.0)
}
}
pub fn get_def_as_str(&self) -> String {
let mut f = String::new();
f.push_str(&format!("fn {} (", self.get_full_name_pretty()));
let mut first = true;
for (name, typ) in &self.params {
if first {
first = false;
} else {
f.push_str(", ");
}
f.push_str(&format!("{name}: {}", typ.inner()));
}
f.push_str(")");
if let Some(ret) = &self.ret_type {
f.push_str(&format!(" -> {}", ret.inner()));
}
f.push_str(";");
f
}
}
impl Struct {
pub fn get_size(&self, program: &Program) -> anyhow::Result<usize> {
let mut size = 0;
for (_, v) in &self.fields {
size += v.inner().size_of(program)?;
}
Ok(size)
}
pub fn get_offset_of(&self, program: &Program, field: &Ident) -> anyhow::Result<usize> {
let mut size = 0;
for (k, v) in &self.fields {
if k == field {
return Ok(size);
}
size += v.inner().size_of(program)?;
}
bail!("Field not found")
}
}
#[derive(Debug, Clone)]
pub enum ConstDataTyp {
Byte(u8),
AddrOfConst(Ident),
AddrOfStatic(Ident),
AddrOfFunc(Ident),
}
pub fn get_constant_data_as_bytes(program: &Program, value: LocBox<Expr>, is_little_endian: bool, must_be_number: bool) -> anyhow::Result<Vec<ConstDataTyp>> {
match value.inner() {
Expr::Literal(lit) => {
match lit {
Literal::Char(Char(c)) => {
if must_be_number {
lerror!(value.loc(), "Expected number got char");
bail!("")
}
Ok(vec![ConstDataTyp::Byte(*c as u8)])
},
Literal::String(TString { val, cstr }) => {
if must_be_number {
lerror!(value.loc(), "Expected number got string");
bail!("")
}
let mut val = val.chars().into_iter().map(|f| ConstDataTyp::Byte(f as u8)).collect::<Vec<_>>();
if *cstr {
val.push(ConstDataTyp::Byte(0));
}
Ok(val)
}
Literal::Number(Number { val, base: _, signed: _ }) => {
let mut buf = Vec::new();
let mut inc = 0;
while inc < 8 {
buf.push(ConstDataTyp::Byte(((val << 8*inc) & 0xff) as u8));
inc += 1;
}
if is_little_endian {
buf.reverse();
}
Ok(buf)
}
Literal::Array(arr) => {
if must_be_number {
lerror!(value.loc(), "Expected number got array");
bail!("")
}
let mut bytes = Vec::new();
if arr.len() < 1 {
return Ok(vec![]);
}
for item in arr {
let mut data = get_constant_data_as_bytes(program, item.clone(), is_little_endian, must_be_number)?;
bytes.append(&mut data);
}
Ok(bytes)
}
Literal::ArrayRepeat { val, count } => {
if must_be_number {
lerror!(value.loc(), "Expected number got repeating array");
bail!("")
}
let mut val = get_constant_data_as_bytes(program, (**val).clone(), is_little_endian, must_be_number)?;
let num = get_constant_data_as_bytes(program, (**count).clone(), is_little_endian, true)?;
let mut count = 0 as usize;
for b in num {
let ConstDataTyp::Byte(b) = b else {unreachable!()};
count = b as usize;
count <<= 8;
}
let orig = val.clone();
for _ in 0..count {
val.append(&mut orig.clone());
}
Ok(val)
}
Literal::Ident(name) => {
if let Some(var) = program.const_vars.get(name) {
Ok(get_constant_data_as_bytes(program, var.val.clone(), is_little_endian, must_be_number)?)
} else if let Some(_) = program.static_vars.get(name) {
lerror!(value.loc(), "Statics cannot be passed by value, use a reference");
bail!("")
} else if let Some(_) = program.functions.get(name) {
Ok(vec![ConstDataTyp::AddrOfFunc(name.clone())])
} else {
lerror!(value.loc(), "Unable to find ident '{name}'");
bail!("")
}
}
//Literal::Struct { name, fields } => {
// if must_be_number {
// lerror!(value.loc(), "Expected number got struct literal");
// bail!("")
// }
// let Some(strct) = program.structs.get(name) else {
// lerror!(value.loc(), "Could not find struct {name}");
// bail!("")
// };
//
// let mut strct_fields = HashMap::new();
// for (name, typ) in &strct.inner().fields {
// strct_fields.insert(name, typ);
// }
//
// for (name, val) in fields {
// if let Some(_fld) = strct_fields.get(name) {
// // TODO: Actually check if the fields are the right type
//
// }
// }
// todo!()
//}
}
}
_ => unreachable!()
}
}

View File

@ -1,6 +1,10 @@
use crate::common::{loc::LocBox, Loc};
use std::fmt::Display;
use super::{expr::Expr, Ident};
use anyhow::bail;
use crate::{common::{loc::LocBox, Loc}, logger::log, validator::predefined::TypeType};
use super::{expr::Expr, literal::Literal, Ident, Program};
#[derive(Debug, Clone)]
pub enum Type {
@ -8,12 +12,88 @@ pub enum Type {
inner: Box<Type>,
mutable: bool,
},
Array {
inner: Box<Type>,
},
ArrayRepeat {
SizedArray {
inner: Box<Type>,
count: LocBox<Expr>,
},
UnsizedArray {
inner: Box<Type>,
},
Owned(Ident),
}
impl Type {
pub fn size_of(&self, program: &Program) -> anyhow::Result<usize> {
match self {
Self::Ref { inner, mutable } => {
// TODO: Use the actual ptr size
Ok(size_of::<*const ()>())
}
Self::UnsizedArray { .. } => {
bail!("Unsized arrays dont have a known size and must be behind a pointer")
}
Self::SizedArray { inner, .. } => {
Ok(inner.size_of(program)? )
}
Self::Owned(name) => {
if let Some(v) = program.structs.get(&name) {
return Ok(v.inner().get_size(program)?);
}
if let Some(v) = program.types.get(&name) {
match v {
TypeType::Normal(v) => {
return Ok(v.inner().size_of(program)?);
}
TypeType::Builtin { size, .. } => {
return Ok(*size);
}
}
}
if let Some(_) = program.enums.get(&name) {
return Ok(4); // TODO: Make enum size changeable
}
bail!("Unknown type '{name}'")
}
}
}
pub fn size_of_allow_unsized_arrays(&self, program: &Program) -> anyhow::Result<usize> {
match self.size_of(program) {
Ok(v) => Ok(v),
Err(e) => {
if e.to_string().as_str() == "Unsized arrays dont have a known size and must be behind a pointer" {
return Ok(0);
}
Err(e)
}
}
}
}
impl Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Ref { inner, mutable } => {
if *mutable {
write!(f, "&mut {inner}")
} else {
write!(f, "&{inner}")
}
}
Self::UnsizedArray { inner } => {
write!(f, "[{inner}]")
}
Self::SizedArray { inner, count } => {
match count.inner() {
Expr::Path(p) => {
write!(f, "[{inner}; {p}]")
}
Expr::Literal(Literal::Number(n)) => {
write!(f, "[{inner}; {n}]")
}
_ => unreachable!()
}
}
Self::Owned(ident) => write!(f, "{ident}")
}
}
}

View File

@ -89,7 +89,7 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
res = parse_field_access(tokens, res)?;
}
if utils::check(tokens, TokenType::Punct(Punctuation::Arrow)).is_some() {
res =parse_ptr_field_access(tokens, res)?;
res = parse_ptr_field_access(tokens, res)?;
}
if utils::check(tokens, TokenType::Delim(Delimiter::ParenL)).is_some() {
res = parse_fn_call(tokens, res)?;
@ -312,11 +312,14 @@ fn parse_literal(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
return Ok(LocBox::new(start.loc(), Expr::Literal(Literal::Array(values))));
} else if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Semi) {
let typ = parse_type(tokens)?;
let Some(typ) = parse_expr(tokens, 0, true)? else {
lerror!(start.loc(), "Expected value, found nothing");
bail!("")
};
let count = parse_expr(tokens, 0, false)?.unwrap();
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
return Ok(LocBox::new(start.loc(), Expr::Literal(Literal::ArrayRepeat {
typ: Box::new(typ),
val: Box::new(typ),
count: Box::new(count)
})));
} else {

View File

@ -30,7 +30,9 @@ pub fn parse_program(mut tokens: Vec<Token>) -> Result<Program> {
functions: HashMap::new(),
member_functions: HashMap::new(),
types: HashMap::new(),
structs: HashMap::new()
structs: HashMap::new(),
static_vars: HashMap::new(),
const_vars: HashMap::new()
})
}

View File

@ -10,7 +10,7 @@ use super::ast::typ::Type;
use super::expr::parse_block;
use super::typ::parse_type;
use super::utils;
use super::ast::statement::{Enum, Function, Statement, Struct, TypeAlias};
use super::ast::statement::{ConstVar, Enum, Function, Statement, StaticVar, Struct, TypeAlias};
type Result<T> = anyhow::Result<T>;
@ -102,7 +102,7 @@ fn parse_static(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
bail!("")
};
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
Ok(LocBox::new(kw.loc(), Statement::StaticVar { name, typ, val }))
Ok(LocBox::new(kw.loc(), Statement::StaticVar(StaticVar { name, typ, val })))
}
fn parse_let(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
@ -138,7 +138,7 @@ fn parse_constant(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
bail!("")
};
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
Ok(LocBox::new(kw.loc(), Statement::ConstVar { name, typ, val }))
Ok(LocBox::new(kw.loc(), Statement::ConstVar(ConstVar { name, typ, val })))
}
fn parse_type_alias(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {

View File

@ -1,6 +1,6 @@
use anyhow::Result;
use crate::{common::loc::LocBox, parser::Delimiter, tokeniser::Token};
use crate::{common::loc::LocBox, error, logger::log, parser::Delimiter, tokeniser::Token};
use super::{ast::{typ::Type, TokenType}, expr::parse_expr, utils, Keyword, Punctuation};
@ -26,14 +26,14 @@ pub fn parse_type(tokens: &mut Vec<Token>) -> Result<LocBox<Type>> {
let itm_typ = parse_type(tokens)?;
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Semi)) {
let count = parse_expr(tokens, 0, false)?.unwrap();
typ = Type::ArrayRepeat {
typ = Type::SizedArray {
inner: Box::new(itm_typ.inner().clone()),
count
}
};
} else {
typ = Type::Array {
typ = Type::UnsizedArray {
inner: Box::new(itm_typ.inner().clone()),
}
};
}
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
} else {

74
src/targets/mod.rs Normal file
View File

@ -0,0 +1,74 @@
use std::{fs::File, path::{Path, PathBuf}};
use crate::{cli::CliArgs, parser::ast::Program};
mod x86_64;
mod none;
pub fn get_default_target() -> String {
x86_64::cgen::linux::CGen::get_target_triple().to_string()
}
pub fn get_all_targets() -> Vec<&'static str> {
vec![
// x86_64::asmgen::linux::AsmGen::get_target_triple(),
// x86_64::cgen::linux::CGen::get_target_triple(),
// x86_64::llvmgen::linux::LlvmGen::get_target_triple(),
none::luagen::cct::LuaGen::get_target_triple(),
x86_64::cgen::linux::CGen::get_target_triple(),
]
}
fn get_target_from_triple(triple: &str) -> Box<dyn Target> {
match triple {
// _ if triple == x86_64::asmgen::linux::AsmGen::get_target_triple() => {
// Box::new(x86_64::asmgen::linux::AsmGen::new())
// }
_ if triple == x86_64::cgen::linux::CGen::get_target_triple() => {
Box::new(x86_64::cgen::linux::CGen::new())
}
// _ if triple == x86_64::qbegen::linux::QbeGen::get_target_triple() => {
// Box::new(x86_64::qbegen::linux::QbeGen::new())
// }
_ => unreachable!()
}
}
pub fn compile(cli: &CliArgs, programs: Vec<(PathBuf, Program)>) -> anyhow::Result<()> {
let mut target = get_target_from_triple(&cli.target);
let build_dir = PathBuf::from("./");
let mut objs = Vec::new();
for (fp, program) in programs {
let int_p = build_dir.join(&fp).with_extension(target.get_int_ext());
let obj_p = build_dir.join(&fp).with_extension("o");
if !int_p.exists() {
std::fs::File::create(&int_p)?;
}
let mut f = std::fs::File::options()
.write(true)
.create(true)
.truncate(true)
.open(&int_p)?;
target.write_code(&program, &mut f)?;
target.compile(&int_p, &obj_p)?;
objs.push(obj_p);
}
let out = Path::new(&cli.output);
target.link(objs, &out)?;
Ok(())
}
pub trait Target {
fn new() -> Self where Self: Sized;
/// Get extension for intermediate file (aka 'asm' or 'c')
fn get_int_ext(&self) -> &'static str {
""
}
fn get_target_triple() -> &'static str where Self: Sized {
""
}
fn write_code(&mut self, program: &Program, f: &mut File) -> anyhow::Result<()>;
fn compile(&mut self, from: &Path, to: &Path) -> anyhow::Result<()>;
fn link(&mut self, from: Vec<PathBuf>, to: &Path) -> anyhow::Result<()>;
}

View File

@ -0,0 +1,26 @@
use std::fs::File;
use crate::{targets::Target, validator::predefined::TypeType};
pub struct LuaGen;
impl Target for LuaGen {
fn new() -> Self where Self: Sized {
Self
}
fn get_target_triple() -> &'static str {
"none-luagen-cct"
}
fn get_int_ext(&self) -> &'static str {
"lua"
}
fn write_code(&mut self, program: &crate::parser::ast::Program, f: &mut File) -> anyhow::Result<()> {
Ok(())
}
fn compile(&mut self, _from: &std::path::Path, _to: &std::path::Path) -> anyhow::Result<()> {
Ok(())
}
fn link(&mut self, _from: Vec<std::path::PathBuf>, _to: &std::path::Path) -> anyhow::Result<()> {
Ok(())
}
}

View File

@ -0,0 +1 @@
pub mod cct;

1
src/targets/none/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod luagen;

View File

@ -0,0 +1,31 @@
use crate::targets::Target;
use std::io::Write;
pub struct AsmGen;
impl Target for AsmGen {
fn new() -> Self {
Self {}
}
fn get_target_triple() -> &'static str {
"x86_64-asmgen-linux"
}
fn get_int_ext(&self) -> &'static str {
"s"
}
fn write_code(&mut self, program: &crate::parser::ast::Program, path: &std::path::Path) -> anyhow::Result<()> {
let mut f = std::fs::File::open(path)?;
writeln!(f, "section .text")?;
writeln!(f, "section .rodata")?;
for (name, constant) in &program.const_vars {
writeln!(f, "{name}:")?;
}
Ok(())
}
fn compile(&mut self, _from: &std::path::Path, _to: &std::path::Path) -> anyhow::Result<()> {
Ok(())
}
fn link(&mut self, _from: Vec<std::path::PathBuf>, _to: &std::path::Path) -> anyhow::Result<()> {
Ok(())
}
}

View File

@ -0,0 +1 @@
pub mod linux;

View File

@ -0,0 +1,50 @@
int main();
static char** __INTERNALS__ARGV_PTR = 0;
static int __INTERNALS__ARGC = 0;
__attribute__((noreturn))
void _start() {
long *stack = (long *) __builtin_frame_address(0);
__INTERNALS__ARGC = stack[0];
__INTERNALS__ARGV_PTR = (char **) &stack[1];
int exit_code = main();
__asm__ volatile (
"syscall\n"
:: "a" (60), "D" (exit_code)
:
);
__builtin_unreachable();
}
unsigned long long syscall(unsigned char arg_count, unsigned char sc_num, void *const *const args) {
register long long ret;
switch (arg_count) {
case 6:
__asm__ volatile ("mov 40(%0), %%r9" :: "r"(args) : "r9");
case 5:
__asm__ volatile ("mov 32(%0), %%r8" :: "r"(args) : "r8");
case 4:
__asm__ volatile ("mov 24(%0), %%r10" :: "r"(args) : "r10");
case 3:
__asm__ volatile ("mov 16(%0), %%rdx" :: "r"(args) : "rdx");
case 2:
__asm__ volatile ("mov 8(%0), %%rsi" :: "r"(args) : "rsi");
case 1:
__asm__ volatile (
"mov (%0), %%rdi\n"
"syscall"
: "=a"(ret)
: "r"(args), "a"(sc_num)
: "rdi", "rsi", "rdx", "r10", "r8", "r9", "rcx", "r11", "memory"
);
return ret;
default:
return -1;
}
}

View File

@ -0,0 +1,310 @@
use std::{fs::File, ops::Deref};
use std::io::Write;
use crate::{common::Loc, parser::ast::{expr::Expr, literal::Literal, statement::{Function, Statement, Struct}, typ::Type, Ast, Ident, Program, Punctuation}, targets::Target, validator::predefined::TypeType};
const INTERNAL_CODE: &'static str = include_str!("internals.c");
pub struct CGen;
impl Target for CGen {
fn new() -> Self where Self: Sized {
Self
}
fn get_target_triple() -> &'static str {
"x86_64-cgen-linux"
}
fn get_int_ext(&self) -> &'static str {
"c"
}
fn write_code(&mut self, program: &crate::parser::ast::Program, f: &mut File) -> anyhow::Result<()> {
self.write_fat_comment(f, "Internal number types");
for (name, typ) in &program.types {
if let TypeType::Builtin { size: _, signed: _ } = typ {
self.write_internal_type(f, program, name, typ)?;
}
}
writeln!(f, "{}", INTERNAL_CODE)?;
self.write_fat_comment(f, "Struct forward declarations");
for (name, _) in &program.structs {
writeln!(f, "typedef struct {name} {name};")?;
}
self.write_fat_comment(f, "Type aliases");
for (name, typ) in &program.types {
if let TypeType::Normal(t) = typ {
self.write_typedef(f, program, t.inner(), name)?;
}
}
self.write_fat_comment(f, "Struct definitions");
for (name, strct) in &program.structs {
self.write_struct(f, program, strct.inner(), name)?;
}
self.write_fat_comment(f, "Function forward declarations");
fn write_fn_fwdclr(slf: &mut CGen, program: &Program, f: &mut File, name: &Ident, func: &Function) -> anyhow::Result<()> {
writeln!(f, "// {}", func.get_def_as_str())?;
if let Some(rett) = &func.ret_type {
slf.write_type(f, program, rett.inner())?;
} else {
write!(f, "void")?;
}
write!(f, " {}(", func.get_full_name())?;
let mut first = true;
for (name, typ) in &func.params {
if !first {
write!(f, ", ")?;
}
slf.write_type(f, program, typ.inner())?;
write!(f, " {name}")?;
first = false;
}
writeln!(f, ");")?;
Ok(())
}
for (name, func) in &program.functions {
// skip the main function cause the declaration will be included in the internals
if name.0 == "main" {
continue;
}
write_fn_fwdclr(self, program, f, name, func.inner())?;
}
for (sname, funcs) in &program.member_functions {
for (fname, func) in funcs {
write_fn_fwdclr(self, program, f, &Ident(format!("{sname}::{fname}")), func.inner())?;
}
}
for i in &program.ast.0 {
match i {
Ast::Statement(stat) => {
match stat.inner() {
Statement::Fn(func) => self.write_fn(f, program, func, stat.loc())?,
_ => (),
}
}
Ast::Expr(_) => unreachable!(),
}
}
Ok(())
}
fn compile(&mut self, _from: &std::path::Path, _to: &std::path::Path) -> anyhow::Result<()> {
Ok(())
}
fn link(&mut self, _from: Vec<std::path::PathBuf>, _to: &std::path::Path) -> anyhow::Result<()> {
Ok(())
}
}
impl CGen {
fn write_fat_comment(&mut self, f: &mut File, s: &str) {
let _ = writeln!(f, "\n/**");
let _ = writeln!(f, " * {s}");
let _ = writeln!(f, " */\n");
}
fn write_expr(&mut self, f: &mut File, program: &Program, expr: &Expr, loc: &Loc) -> anyhow::Result<()> {
// https://c9x.me/compile/doc/il.html#Memory
match expr {
Expr::Literal(lit) => {
match lit {
_ => todo!(),
}
}
Expr::If(_ifexpr) => {
}
Expr::BinOp { typ, left, right } => {
match typ {
Punctuation::Eq => {
}
_ => todo!()
}
}
Expr::Path(p) => {
for (n, p) in p.0.iter().enumerate() {
if n > 0 {
write!(f, "::")?;
}
write!(f, "{p}")?;
}
},
Expr::Return(ret) => {
write!(f, "return ")?;
if let Some(ret) = ret.deref() {
self.write_expr(f, program, ret.inner(), loc)?;
}
writeln!(f, ";")?;
},
Expr::Struct { path, fields } => {
writeln!(f, "({path}) {{")?;
for (name, val) in fields {
write!(f, " .{name} = ")?;
self.write_expr(f, program, val.inner(), val.loc())?;
writeln!(f, ",")?;
}
write!(f, "}}")?;
}
_ => todo!("{expr:?}")
}
Ok(())
}
fn write_typedef(&mut self, f: &mut File, program: &Program, typ: &Type, name: &Ident) -> anyhow::Result<()> {
fn part(f: &mut File, program: &Program, typ: &Type) -> anyhow::Result<String> {
let mut pf = String::new();
match typ {
Type::Ref { inner, mutable } => {
pf.push_str(&part(f, program, inner)?);
write!(f, "*")?;
if !*mutable {
write!(f, "const")?;
}
}
Type::Owned(name) => {
write!(f, "{name}")?;
}
Type::SizedArray { inner, count } => {
pf.push_str(&part(f, program, inner)?);
let Expr::Literal(cnt) = count.inner() else {panic!()};
let Literal::Number(cnt) = cnt else {panic!()};
pf.push_str(&format!("[{}]", cnt.val));
}
Type::UnsizedArray { inner } => {
pf.push_str(&part(f, program, inner)?);
pf.push_str("[]");
}
}
Ok(pf)
}
write!(f, "typedef ")?;
let pf = part(f, program, typ)?;
writeln!(f, " {name}{pf};")?;
Ok(())
}
fn write_type(&mut self, f: &mut File, program: &Program, typ: &Type) -> anyhow::Result<()> {
static mut WAS_REF: bool = false;
match typ {
Type::Ref { inner, mutable } => {
self.write_type(f, program, inner)?;
write!(f, " *")?;
if !*mutable {
write!(f, "const")?;
}
unsafe {
WAS_REF = true;
}
}
Type::Owned(name) => {
write!(f, "{name}")?;
}
Type::SizedArray { inner, count } => {
self.write_type(f, program, inner)?;
let Expr::Literal(cnt) = count.inner() else {panic!()};
let Literal::Number(cnt) = cnt else {panic!()};
if !unsafe { WAS_REF } {
write!(f, "[{}]", cnt.val)?;
}
}
Type::UnsizedArray { inner } => {
self.write_type(f, program, inner)?;
if !unsafe { WAS_REF } {
write!(f, "[]")?;
}
}
}
Ok(())
}
fn write_internal_type(&mut self, f: &mut File, program: &Program, name: &Ident, typ: &TypeType) -> anyhow::Result<()> {
match typ {
TypeType::Normal(typ) => unreachable!(),
TypeType::Builtin{ size, signed } => {
if *size == 0 {
return Ok(());
}
write!(f, "typedef ")?;
if *signed {
write!(f, "signed ")?;
} else {
write!(f, "unsigned ")?;
}
match size {
9.. => unimplemented!(),
0 => (),
1 => write!(f, "char ")?,
2 => write!(f, "short ")?,
4 => write!(f, "int ")?,
8 => write!(f, "long long ")?,
3 | 5 | 6 | 7 => unreachable!(),
}
writeln!(f, "{};", name.0)?;
}
_ => (),
}
Ok(())
}
fn write_struct(&mut self, f: &mut File, program: &Program, strct: &Struct, name: &Ident) -> anyhow::Result<()> {
writeln!(f, "struct {name} {{")?;
for (name, typ) in &strct.fields {
write!(f, " ")?;
self.write_type(f, program, typ.inner())?;
writeln!(f, " {name};")?;
}
writeln!(f, "}};")?;
Ok(())
}
fn write_fn(&mut self, f: &mut File, program: &Program, func: &Function, loc: &Loc) -> anyhow::Result<()> {
writeln!(f, "\n\n// {}", func.get_def_as_str())?;
if let Some(rett) = &func.ret_type {
self.write_type(f, program, rett.inner())?;
} else {
write!(f, "void")?;
}
write!(f, " {}(", func.get_full_name())?;
let mut first = true;
for (name, typ) in &func.params {
if !first {
write!(f, ", ")?;
}
self.write_type(f, program, typ.inner())?;
write!(f, " {name}")?;
first = false;
}
write!(f, ")")?;
if let Some(body) = &func.body {
writeln!(f, " {{")?;
for ast in &body.0 {
match ast {
Ast::Expr(expr) => {
self.write_expr(f, program, expr.inner(), expr.loc())?
}
Ast::Statement(stat) => {
//todo!()
}
}
}
writeln!(f, "}}")?;
} else {
write!(f, ";")?;
}
Ok(())
}
}

View File

@ -0,0 +1,2 @@
pub mod linux;

View File

@ -0,0 +1,36 @@
use std::fs::File;
use llvm_lib::core::module;
use crate::targets::Target;
// https://github.com/TheDan64/inkwell/blob/master/examples/kaleidoscope/implementation_typed_pointers.rs
pub struct LlvmGen {
}
impl Target for LlvmGen {
fn new() -> Self where Self: Sized {
Self {
}
}
fn get_target_triple() -> &'static str {
"x86_64-llvm-linux"
}
fn get_int_ext(&self) -> &'static str {
""
}
fn write_code(&mut self, _program: &crate::parser::ast::Program, _ofp: &mut File) -> anyhow::Result<()> {
let mdl = module::ModuleRef::new("main");
Ok(())
}
fn compile(&mut self, _from: &std::path::Path, _to: &std::path::Path) -> anyhow::Result<()> {
Ok(())
}
fn link(&mut self, _from: Vec<std::path::PathBuf>, _to: &std::path::Path) -> anyhow::Result<()> {
Ok(())
}
}

View File

@ -0,0 +1 @@
pub mod linux;

View File

@ -0,0 +1,4 @@
// pub mod asmgen;
pub mod cgen;
// pub mod qbegen;
// pub mod llvmgen;

View File

@ -0,0 +1,149 @@
use std::fs::File;
use std::io::Write;
use crate::{common::Loc, parser::ast::{expr::Expr, statement::{Function, Statement}, typ::Type, Ast, Ident, Program, Punctuation}, targets::Target, validator::predefined::TypeType};
pub struct QbeGen {
var_idx: usize,
}
impl Target for QbeGen {
fn new() -> Self where Self: Sized {
Self {
var_idx: 0
}
}
fn get_target_triple() -> &'static str {
"x86_64-qbegen-linux"
}
fn get_int_ext(&self) -> &'static str {
"ssa"
}
fn write_code(&mut self, program: &Program, f: &mut File) -> anyhow::Result<()> {
for (name, typ) in &program.types {
self.write_typetype(f, program, name, typ)?;
}
for (name, strct) in &program.structs {
self.write_type_with_size(f, program, name, strct.inner().get_size(program)?)?;
}
for i in &program.ast.0 {
match i {
Ast::Statement(stat) => {
match stat.inner() {
Statement::Fn(func) => self.write_fn(f, program, func, stat.loc())?,
_ => (),
}
}
Ast::Expr(_) => unreachable!(),
}
}
Ok(())
}
fn compile(&mut self, _from: &std::path::Path, _to: &std::path::Path) -> anyhow::Result<()> {
Ok(())
}
fn link(&mut self, _from: Vec<std::path::PathBuf>, _to: &std::path::Path) -> anyhow::Result<()> {
Ok(())
}
}
impl QbeGen {
fn write_expr(&mut self, f: &mut File, program: &Program, expr: &Expr, loc: &Loc) -> anyhow::Result<()> {
// https://c9x.me/compile/doc/il.html#Memory
match expr {
Expr::If(_ifexpr) => {
}
Expr::BinOp { typ, left, right } => {
match typ {
Punctuation::Eq => {
}
_ => todo!()
}
}
_ => todo!("{expr:?}")
}
Ok(())
}
fn write_typetype(&mut self, f: &mut File, program: &Program, name: &Ident, typ: &TypeType) -> anyhow::Result<()> {
match typ {
TypeType::Normal(typ) => {
writeln!(f, "type :{:<16} = {{ z {} }}", name.0, typ.inner().size_of_allow_unsized_arrays(program)?)?;
}
TypeType::Builtin(size) => {
writeln!(f, "type :{:<16} = {{ z {size} }}", name.0)?;
}
_ => (),
}
Ok(())
}
fn write_type_with_size(&mut self, f: &mut File, program: &Program, name: &Ident, size: usize) -> anyhow::Result<()> {
writeln!(f, "type :{:<16} = {{ z {size} }}", name.0)?;
Ok(())
}
fn size_to_qbe_size(&self, size: usize) -> Option<&'static str> {
match size {
1 => Some("b"),
2 => Some("h"),
4 => Some("w"),
8 => Some("l"),
_ => None,
}
}
fn write_fn(&mut self, f: &mut File, program: &Program, func: &Function, loc: &Loc) -> anyhow::Result<()> {
writeln!(f, "\n\n# {}", func.get_def_as_str())?;
let mut ret = " ";
if let Some(r) = &func.ret_type {
if let Some(s) = self.size_to_qbe_size(r.inner().size_of(program)?) {
ret = s;
} else {
match r.inner() {
Type::Owned(name) => {
ret = &name.0;
}
_ => unreachable!(),
}
}
}
let mut args = String::new();
for (name, typ) in &func.params {
if !args.is_empty() {
args.push_str(", ");
}
if let Some(s) = self.size_to_qbe_size(typ.inner().size_of(program)?) {
args.push_str(s);
} else {
match typ.inner() {
Type::Owned(name) => {
args.push_str(&name.0);
}
_ => unreachable!(),
}
}
args.push_str(&format!(" %{}", name.0));
}
writeln!(f, "function {ret} ${}({args}) {{", func.get_full_name())?;
writeln!(f, "@start")?;
if let Some(body) = &func.body {
for ast in &body.0 {
match ast {
Ast::Expr(expr) => {
self.write_expr(f, program, expr.inner(), expr.loc())?
}
Ast::Statement(stat) => {
todo!()
}
}
}
}
writeln!(f, "}}")?;
Ok(())
}
}

View File

@ -0,0 +1 @@
pub mod linux;

View File

@ -4,12 +4,13 @@ use std::fmt::Display;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Ident(pub String);
impl ToString for Ident {
fn to_string(&self) -> String {
self.0.clone()
impl Display for Ident {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Number {
pub val: usize,
@ -17,6 +18,24 @@ pub struct Number {
pub signed: bool,
}
impl Display for Number {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.base {
2 => write!(f, "{:#b}", self.val),
8 => write!(f, "{:#o}", self.val),
10 => {
if self.signed {
write!(f, "{}", self.val as isize)
} else {
write!(f, "{}", self.val as usize)
}
}
16 => write!(f, "{:#x}", self.val),
_ => unreachable!()
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct TString {
pub val: String,
@ -24,7 +43,7 @@ pub struct TString {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Char(char);
pub struct Char(pub char);
impl Into<char> for Char {
fn into(self) -> char {

View File

@ -1,13 +1,20 @@
use std::collections::HashMap;
use crate::{common::loc::LocBox, parser::ast::{expr::Block, statement::{Statement, TypeAlias}, Ast, Program}};
use anyhow::{anyhow, bail};
use crate::{common::{loc::LocBox, Loc}, parser::ast::{expr::{Block, Expr}, statement::{Statement, TypeAlias}, typ::Type, Ast, Program}};
pub mod predefined;
struct Scope {
}
pub fn validate_code(prog: &mut Program) -> anyhow::Result<()> {
let Block(items) = prog.ast.clone();
predefined::load_builtin(prog);
collect_types(prog, &items);
collect_types_and_constants(prog, &items);
check_that_types_exist_for_items(prog, &items)?;
//dbg!(&prog.types);
//dbg!(&prog.structs);
//dbg!(&prog.enums);
@ -18,9 +25,14 @@ pub fn validate_code(prog: &mut Program) -> anyhow::Result<()> {
Ast::Statement(stat) => {
match stat.inner() {
Statement::Fn(func) => {}
Statement::Let { name, typ, val } => {}
Statement::ConstVar { name, typ, val } => {}
Statement::StaticVar { name, typ, val } => {}
Statement::Let { .. } => {
lerror!(stat.loc(), "Let statements are not allowed outside a function");
bail!("")
}
Statement::ConstVar(var) => {
}
Statement::StaticVar(var) => {
}
Statement::Enum(enm) => {}
Statement::Struct(strct) => {}
Statement::TypeAlias(alias) => {}
@ -34,7 +46,88 @@ pub fn validate_code(prog: &mut Program) -> anyhow::Result<()> {
Ok(())
}
fn collect_types(prog: &mut Program, items: &Vec<Ast>) {
fn check_that_types_exist_for_items(prog: &mut Program, items: &Vec<Ast>) -> anyhow::Result<()> {
let mut errored = false;
for item in items {
match item {
Ast::Statement(stat) => {
match stat.inner() {
Statement::Fn(func) => {
for (name, t) in &func.params {
if let Err(_) = validate_type(prog, t) {
if let Some(sn) = &func.struct_name {
lerror!(t.loc(), "Type '{}', of argument, '{name}' in function '{}::{}' does not exist", t.inner(), sn, func.name);
} else {
lerror!(t.loc(), "Type '{}', of argument, '{name}' in function '{}' does not exist", t.inner(), func.name);
}
errored = true;
}
}
if let Some(ret_typ) = &func.ret_type {
if let Err(_) = validate_type(prog, &ret_typ) {
lerror!(ret_typ.loc(), "Return type '{}' of function '{}' does not exist", ret_typ.inner(), func.name);
errored = true;
}
}
},
Statement::Let { .. } => (),
Statement::ConstVar(var) => {
validate_type(prog, &var.typ)?;
}
Statement::StaticVar(var) => {
validate_type(prog, &var.typ)?;
}
Statement::Enum(_) => (),
Statement::Struct(strct) => {
for (name, t) in &strct.fields {
if let Err(_) = validate_type(prog, t) {
lerror!(t.loc(), "Type '{}', of field, '{}.{name}' does not exist", t.inner(), strct.name);
errored = true;
}
}
}
Statement::TypeAlias(alias) => {
if let Err(_) = validate_type(prog, &alias.typ) {
lerror!(alias.typ.loc(), "Type '{}' of type alias '{}' does not exist", alias.typ.inner(), alias.name);
errored = true;
}
}
}
}
Ast::Expr(_) => unreachable!()
}
}
if errored {
bail!("")
}
Ok(())
}
fn validate_type(prog: &mut Program, typ: &LocBox<Type>) -> anyhow::Result<()> {
fn f(prog: &mut Program, typ: &Type, loc: &Loc) -> anyhow::Result<()> {
match typ {
Type::SizedArray { inner, .. } |
Type::UnsizedArray { inner, .. } |
Type::Ref { inner, .. } => f(prog, inner, loc),
Type::Owned(typ) => {
if prog.enums.get(typ).is_some() ||
prog.types.get(typ).is_some() ||
prog.structs.get(typ).is_some() {
Ok(())
} else {
// lerror!(loc, "Could not find type '{}'", typ.0);
bail!("")
}
}
}
}
f(prog, typ.inner(), typ.loc())
}
fn collect_types_and_constants(prog: &mut Program, items: &Vec<Ast>) {
for item in items {
match item {
Ast::Statement(stat) => {
@ -62,9 +155,13 @@ fn collect_types(prog: &mut Program, items: &Vec<Ast>) {
let typ = alias.clone().typ.inner().clone();
prog.types.insert(alias.name.clone(), predefined::TypeType::Normal(LocBox::new(stat.loc(), typ)));
}
Statement::Let { .. } |
Statement::ConstVar { .. } |
Statement::StaticVar { .. } => (),
Statement::Let { .. } => (),
Statement::ConstVar(var) => {
prog.const_vars.insert(var.name.clone(), var.clone());
}
Statement::StaticVar(var) => {
prog.static_vars.insert(var.name.clone(), var.clone());
},
}
}
Ast::Expr(_) => unreachable!()

View File

@ -10,23 +10,23 @@ const SIZE: usize = 8;
const SIZE: usize = 4;
lazy_static!(
pub static ref TYPES_RAW: HashMap<&'static str, usize> = [
("void", 0),
("usize", SIZE),
("isize", SIZE),
("u8", 1),
("u16", 2),
("u32", 4),
("u64", 8),
("i8", 1),
("i16", 2),
("i32", 4),
("i64", 8),
pub static ref TYPES_RAW: HashMap<&'static str, (usize, bool)> = [
("void", (0, false)),
("usize", (SIZE, false)),
("isize", (SIZE, true)),
("u8", (1, false)),
("u16", (2, false)),
("u32", (4, false)),
("u64", (8, false)),
("i8", (1, true)),
("i16", (2, true)),
("i32", (4, true)),
("i64", (8, true)),
].into();
pub static ref FUNCTIONS: HashMap<&'static str, (Vec<(&'static str, &'static str)>, &'static str)> = [
("syscall", (vec![
("arg_count", "&u8"),
("sc_num", "usize"),
("arg_count", "u8"),
("sc_num", "u8"),
("args", "&[&void]")
], "usize")),
].into();
@ -35,12 +35,15 @@ lazy_static!(
#[derive(Debug, Clone)]
pub enum TypeType {
Normal(LocBox<Type>),
Builtin(usize),
Builtin {
size: usize,
signed: bool,
}
}
pub fn load_builtin(prog: &mut Program) {
for (name, size) in TYPES_RAW.iter() {
prog.types.insert(Ident(name.to_string()), TypeType::Builtin(*size));
for (name, (size, signed)) in TYPES_RAW.iter() {
prog.types.insert(Ident(name.to_string()), TypeType::Builtin{ size: *size, signed: *signed });
}
for (name, (args, ret_typ)) in FUNCTIONS.iter() {

View File

@ -1,21 +1,21 @@
//type str = [u8];
//
//struct Foo {
// a: usize,
// b: &str
//}
//
//fn Foo.new(a: usize, b: &str) -> Foo {
// return Foo {
// a: a,
// b: b
// };
//}
//
//
//fn main() {
// let obj = Foo::new();
//
//}
type str = [u8];
struct Foo {
a: usize,
b: &str
}
fn Foo.new(a: usize, b: &str) -> Foo {
return Foo {
a: a,
b: b
};
}
fn main() -> i32 {
let obj = Foo::new();
}