From 06d8c1b0f3e9fcdde86a6983b42b5493a232d3f5 Mon Sep 17 00:00:00 2001 From: MCorange Date: Thu, 27 Mar 2025 18:49:17 +0200 Subject: [PATCH] Add all of the code gen, want to show it off --- .gitignore | 3 + Cargo.lock | 4 +- Cargo.toml | 2 + src/cli.rs | 2 + src/lib.rs | 2 + src/main.rs | 8 +- src/parser/ast/expr.rs | 14 +- src/parser/ast/literal.rs | 6 +- src/parser/ast/mod.rs | 4 +- src/parser/ast/statement.rs | 214 ++++++++++++++- src/parser/ast/typ.rs | 92 ++++++- src/parser/expr.rs | 9 +- src/parser/mod.rs | 4 +- src/parser/stat.rs | 8 +- src/parser/typ.rs | 18 +- src/targets/mod.rs | 74 ++++++ src/targets/none/luagen/cct/mod.rs | 26 ++ src/targets/none/luagen/mod.rs | 1 + src/targets/none/mod.rs | 1 + src/targets/x86_64/asmgen/linux/mod.rs | 31 +++ src/targets/x86_64/asmgen/mod.rs | 1 + src/targets/x86_64/cgen/linux/internals.c | 50 ++++ src/targets/x86_64/cgen/linux/mod.rs | 310 ++++++++++++++++++++++ src/targets/x86_64/cgen/mod.rs | 2 + src/targets/x86_64/llvmgen/linux/mod.rs | 36 +++ src/targets/x86_64/llvmgen/mod.rs | 1 + src/targets/x86_64/mod.rs | 4 + src/targets/x86_64/qbegen/linux/mod.rs | 149 +++++++++++ src/targets/x86_64/qbegen/mod.rs | 1 + src/tokeniser/tokentype.rs | 27 +- src/validator/mod.rs | 115 +++++++- src/validator/predefined.rs | 37 +-- test.mcl | 38 +-- 33 files changed, 1199 insertions(+), 95 deletions(-) create mode 100644 src/targets/mod.rs create mode 100644 src/targets/none/luagen/cct/mod.rs create mode 100644 src/targets/none/luagen/mod.rs create mode 100644 src/targets/none/mod.rs create mode 100644 src/targets/x86_64/asmgen/linux/mod.rs create mode 100644 src/targets/x86_64/asmgen/mod.rs create mode 100644 src/targets/x86_64/cgen/linux/internals.c create mode 100644 src/targets/x86_64/cgen/linux/mod.rs create mode 100644 src/targets/x86_64/cgen/mod.rs create mode 100644 src/targets/x86_64/llvmgen/linux/mod.rs create mode 100644 src/targets/x86_64/llvmgen/mod.rs create mode 100644 src/targets/x86_64/mod.rs create mode 100644 src/targets/x86_64/qbegen/linux/mod.rs create mode 100644 src/targets/x86_64/qbegen/mod.rs diff --git a/.gitignore b/.gitignore index ea8c4bf..aea5fbe 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ /target +/*.c +/*.s +/*.ssa diff --git a/Cargo.lock b/Cargo.lock index 9b644b6..92fd356 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index 09ae605..6528049 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/cli.rs b/src/cli.rs index 3624277..642bf82 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -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, diff --git a/src/lib.rs b/src/lib.rs index 8003470..1e8395c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,3 +5,5 @@ pub mod cli; #[macro_use] pub mod logger; pub mod validator; +#[macro_use] +pub mod targets; diff --git a/src/main.rs b/src/main.rs index 74258f1..1a9a3f9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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(()) } diff --git a/src/parser/ast/expr.rs b/src/parser/ast/expr.rs index 600551e..89c6a92 100644 --- a/src/parser/ast/expr.rs +++ b/src/parser/ast/expr.rs @@ -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); #[derive(Debug, Clone)] pub struct Path(pub Vec); +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 { diff --git a/src/parser/ast/literal.rs b/src/parser/ast/literal.rs index 670196f..0fd4b53 100644 --- a/src/parser/ast/literal.rs +++ b/src/parser/ast/literal.rs @@ -12,11 +12,7 @@ pub enum Literal { Char(Char), Array(Vec>), ArrayRepeat { - typ: Box>, + val: Box>, count: Box>, }, - Struct { - name: Ident, - fields: HashMap - }, } diff --git a/src/parser/ast/mod.rs b/src/parser/ast/mod.rs index 6fd4886..eea25c4 100644 --- a/src/parser/ast/mod.rs +++ b/src/parser/ast/mod.rs @@ -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, pub functions: HashMap>, pub member_functions: HashMap>>, + pub static_vars: HashMap, + pub const_vars: HashMap } #[derive(Debug, Clone)] diff --git a/src/parser/ast/statement.rs b/src/parser/ast/statement.rs index 69f0513..b1ad7f9 100644 --- a/src/parser/ast/statement.rs +++ b/src/parser/ast/statement.rs @@ -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, - val: LocBox - }, - StaticVar { - name: Ident, - typ: LocBox, - val: LocBox, - }, + ConstVar(ConstVar), + StaticVar(StaticVar), Let { name: Ident, typ: Option>, @@ -26,6 +22,20 @@ pub enum Statement { }, } +#[derive(Debug, Clone)] +pub struct ConstVar { + pub name: Ident, + pub typ: LocBox, + pub val: LocBox, +} + +#[derive(Debug, Clone)] +pub struct StaticVar { + pub name: Ident, + pub typ: LocBox, + pub val: LocBox, +} + #[derive(Debug, Clone)] pub struct TypeAlias { pub name: Ident, @@ -54,3 +64,183 @@ pub struct Function { pub qual_extern: Option, // abi pub body: Option, // 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 { + 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 { + 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, is_little_endian: bool, must_be_number: bool) -> anyhow::Result> { + 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::>(); + 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!() + } +} diff --git a/src/parser/ast/typ.rs b/src/parser/ast/typ.rs index ce45660..20dc928 100644 --- a/src/parser/ast/typ.rs +++ b/src/parser/ast/typ.rs @@ -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, mutable: bool, }, - Array { - inner: Box, - }, - ArrayRepeat { + SizedArray { inner: Box, count: LocBox, }, + UnsizedArray { + inner: Box, + }, Owned(Ident), } + +impl Type { + pub fn size_of(&self, program: &Program) -> anyhow::Result { + 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 { + 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}") + } + } +} diff --git a/src/parser/expr.rs b/src/parser/expr.rs index aa013db..43ac222 100644 --- a/src/parser/expr.rs +++ b/src/parser/expr.rs @@ -89,7 +89,7 @@ pub fn parse_expr(tokens: &mut Vec, 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) -> Result> { 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 { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 3425305..669eb02 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -30,7 +30,9 @@ pub fn parse_program(mut tokens: Vec) -> Result { functions: HashMap::new(), member_functions: HashMap::new(), types: HashMap::new(), - structs: HashMap::new() + structs: HashMap::new(), + static_vars: HashMap::new(), + const_vars: HashMap::new() }) } diff --git a/src/parser/stat.rs b/src/parser/stat.rs index a181c15..5e333e6 100644 --- a/src/parser/stat.rs +++ b/src/parser/stat.rs @@ -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 = anyhow::Result; @@ -102,7 +102,7 @@ fn parse_static(tokens: &mut Vec) -> Result> { 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) -> Result> { @@ -138,7 +138,7 @@ fn parse_constant(tokens: &mut Vec) -> Result> { 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) -> Result> { @@ -177,7 +177,7 @@ fn parse_fn(tokens: &mut Vec) -> Result> { _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?; body = None; } - + Ok(LocBox::new(kw.loc(), Statement::Fn(Function{ struct_name, name, diff --git a/src/parser/typ.rs b/src/parser/typ.rs index 10ead5a..22e6942 100644 --- a/src/parser/typ.rs +++ b/src/parser/typ.rs @@ -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}; @@ -25,15 +25,15 @@ pub fn parse_type(tokens: &mut Vec) -> Result> { } 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 { - inner: Box::new(itm_typ.inner().clone()), - count - } + let count = parse_expr(tokens, 0, false)?.unwrap(); + typ = Type::SizedArray { + inner: Box::new(itm_typ.inner().clone()), + count + }; } else { - typ = Type::Array { - inner: Box::new(itm_typ.inner().clone()), - } + typ = Type::UnsizedArray { + inner: Box::new(itm_typ.inner().clone()), + }; } _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?; } else { diff --git a/src/targets/mod.rs b/src/targets/mod.rs new file mode 100644 index 0000000..d2d91ed --- /dev/null +++ b/src/targets/mod.rs @@ -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 { + 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, to: &Path) -> anyhow::Result<()>; +} diff --git a/src/targets/none/luagen/cct/mod.rs b/src/targets/none/luagen/cct/mod.rs new file mode 100644 index 0000000..f0c8d9b --- /dev/null +++ b/src/targets/none/luagen/cct/mod.rs @@ -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, _to: &std::path::Path) -> anyhow::Result<()> { + Ok(()) + } +} diff --git a/src/targets/none/luagen/mod.rs b/src/targets/none/luagen/mod.rs new file mode 100644 index 0000000..6d8a91d --- /dev/null +++ b/src/targets/none/luagen/mod.rs @@ -0,0 +1 @@ +pub mod cct; diff --git a/src/targets/none/mod.rs b/src/targets/none/mod.rs new file mode 100644 index 0000000..f00c4e8 --- /dev/null +++ b/src/targets/none/mod.rs @@ -0,0 +1 @@ +pub mod luagen; diff --git a/src/targets/x86_64/asmgen/linux/mod.rs b/src/targets/x86_64/asmgen/linux/mod.rs new file mode 100644 index 0000000..8b5ebcf --- /dev/null +++ b/src/targets/x86_64/asmgen/linux/mod.rs @@ -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, _to: &std::path::Path) -> anyhow::Result<()> { + Ok(()) + } +} diff --git a/src/targets/x86_64/asmgen/mod.rs b/src/targets/x86_64/asmgen/mod.rs new file mode 100644 index 0000000..057cec9 --- /dev/null +++ b/src/targets/x86_64/asmgen/mod.rs @@ -0,0 +1 @@ +pub mod linux; diff --git a/src/targets/x86_64/cgen/linux/internals.c b/src/targets/x86_64/cgen/linux/internals.c new file mode 100644 index 0000000..b44d51a --- /dev/null +++ b/src/targets/x86_64/cgen/linux/internals.c @@ -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; + } +} diff --git a/src/targets/x86_64/cgen/linux/mod.rs b/src/targets/x86_64/cgen/linux/mod.rs new file mode 100644 index 0000000..39f5256 --- /dev/null +++ b/src/targets/x86_64/cgen/linux/mod.rs @@ -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, _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 { + 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(()) + } +} diff --git a/src/targets/x86_64/cgen/mod.rs b/src/targets/x86_64/cgen/mod.rs new file mode 100644 index 0000000..d68e920 --- /dev/null +++ b/src/targets/x86_64/cgen/mod.rs @@ -0,0 +1,2 @@ + +pub mod linux; diff --git a/src/targets/x86_64/llvmgen/linux/mod.rs b/src/targets/x86_64/llvmgen/linux/mod.rs new file mode 100644 index 0000000..cb4763f --- /dev/null +++ b/src/targets/x86_64/llvmgen/linux/mod.rs @@ -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, _to: &std::path::Path) -> anyhow::Result<()> { + Ok(()) + } +} diff --git a/src/targets/x86_64/llvmgen/mod.rs b/src/targets/x86_64/llvmgen/mod.rs new file mode 100644 index 0000000..057cec9 --- /dev/null +++ b/src/targets/x86_64/llvmgen/mod.rs @@ -0,0 +1 @@ +pub mod linux; diff --git a/src/targets/x86_64/mod.rs b/src/targets/x86_64/mod.rs new file mode 100644 index 0000000..2bacecf --- /dev/null +++ b/src/targets/x86_64/mod.rs @@ -0,0 +1,4 @@ +// pub mod asmgen; +pub mod cgen; +// pub mod qbegen; +// pub mod llvmgen; diff --git a/src/targets/x86_64/qbegen/linux/mod.rs b/src/targets/x86_64/qbegen/linux/mod.rs new file mode 100644 index 0000000..81420f9 --- /dev/null +++ b/src/targets/x86_64/qbegen/linux/mod.rs @@ -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, _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(()) + } +} diff --git a/src/targets/x86_64/qbegen/mod.rs b/src/targets/x86_64/qbegen/mod.rs new file mode 100644 index 0000000..057cec9 --- /dev/null +++ b/src/targets/x86_64/qbegen/mod.rs @@ -0,0 +1 @@ +pub mod linux; diff --git a/src/tokeniser/tokentype.rs b/src/tokeniser/tokentype.rs index fc8baca..d90cc03 100644 --- a/src/tokeniser/tokentype.rs +++ b/src/tokeniser/tokentype.rs @@ -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 for Char { fn into(self) -> char { diff --git a/src/validator/mod.rs b/src/validator/mod.rs index fd5c404..01cbc81 100644 --- a/src/validator/mod.rs +++ b/src/validator/mod.rs @@ -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) { +fn check_that_types_exist_for_items(prog: &mut Program, items: &Vec) -> 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) -> 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) { for item in items { match item { Ast::Statement(stat) => { @@ -62,9 +155,13 @@ fn collect_types(prog: &mut Program, items: &Vec) { 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!() diff --git a/src/validator/predefined.rs b/src/validator/predefined.rs index 01ff06f..9a90bcd 100644 --- a/src/validator/predefined.rs +++ b/src/validator/predefined.rs @@ -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), - 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() { diff --git a/test.mcl b/test.mcl index 815c2c3..c469b72 100644 --- a/test.mcl +++ b/test.mcl @@ -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(); + +}