From 158b76fe399a125af3f8aa971ef9726c00049eef Mon Sep 17 00:00:00 2001 From: MCorange Date: Sat, 17 Jan 2026 16:31:40 +0200 Subject: [PATCH] :3 --- src/common/loc.rs | 2 +- src/main.rs | 3 +- src/parser/ast/expr.rs | 31 +- src/parser/ast/literal.rs | 39 ++- src/parser/ast/mod.rs | 90 +++++- src/parser/ast/statement.rs | 45 ++- src/parser/ast/typ.rs | 95 +++++- src/parser/expr.rs | 17 +- src/parser/mod.rs | 3 +- src/parser/stat.rs | 3 +- src/parser/typ.rs | 15 +- src/targets/mod.rs | 4 +- src/targets/x86_64/cgen/linux/mod.rs | 27 +- src/tokeniser/mod.rs | 2 +- src/tokeniser/tokentype.rs | 8 + src/validator/calculate_types.rs | 59 ++++ src/validator/mod.rs | 433 +++++++++++++++++++++++++-- src/validator/predefined.rs | 20 +- 18 files changed, 783 insertions(+), 113 deletions(-) create mode 100644 src/validator/calculate_types.rs diff --git a/src/common/loc.rs b/src/common/loc.rs index 3747b06..7b47475 100644 --- a/src/common/loc.rs +++ b/src/common/loc.rs @@ -27,7 +27,7 @@ impl Loc { } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct LocBox { inner: T, loc: Loc diff --git a/src/main.rs b/src/main.rs index 1a9a3f9..ce20f78 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,8 +25,9 @@ fn main() -> anyhow::Result<()> { info!("Parsing {file}"); let mut prog = mclangc::parser::parse_program(tokens)?; info!("Validating {file}"); + dbg!(&prog); mclangc::validator::validate_code(&mut prog)?; - // dbg!(&prog); + //dbg!(&prog); progs.push((fp, prog)); } diff --git a/src/parser/ast/expr.rs b/src/parser/ast/expr.rs index 89c6a92..b0c7eeb 100644 --- a/src/parser/ast/expr.rs +++ b/src/parser/ast/expr.rs @@ -1,10 +1,10 @@ -use std::{collections::HashMap, fmt::Display}; +use std::{collections::BTreeMap, fmt::Display}; -use crate::{common::loc::LocBox, tokeniser::tokentype::*}; +use crate::{common::loc::LocBox, parser::ast::{Program, literal::Literal}, tokeniser::tokentype::*}; use super::{typ::Type, Ast}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, PartialOrd)] pub enum Expr { // Comment(Comment), Group(Box>), @@ -58,7 +58,7 @@ pub enum Expr { If(IfExpr), Struct { path: Path, - fields: HashMap>, + fields: BTreeMap>, }, Return(Box>>), Break, @@ -74,17 +74,30 @@ impl Expr { let Expr::Path(p) = self else {panic!("Unwrapping")}; p.clone() } + pub fn as_number(&self, prog: &Program) -> Option { + match self { + Self::Literal(Literal::Number(num)) => Some(*num), + Self::Path(Path(path)) => { + if let Some(val) = prog.get_const_var(path.last().unwrap()) { + val.1.inner().val.inner().as_number(prog) + } else { + None + } + } + _ => None + } + } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, PartialOrd)] pub struct CallParams(pub Vec>); -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, PartialOrd)] pub struct Block(pub Vec); -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Hash)] pub struct Path(pub Vec); impl Display for Path { @@ -100,14 +113,14 @@ impl Display for Path { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, PartialOrd)] pub struct IfExpr { pub test: Box>, pub body: Block, pub else_if: Option } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, PartialOrd)] pub enum IfBranchExpr { ElseIf(Box), Else(Block) diff --git a/src/parser/ast/literal.rs b/src/parser/ast/literal.rs index 0fd4b53..f0396ce 100644 --- a/src/parser/ast/literal.rs +++ b/src/parser/ast/literal.rs @@ -1,10 +1,9 @@ -use std::collections::HashMap; -use crate::{common::loc::LocBox, tokeniser::tokentype::*}; +use crate::{common::{Loc, loc::LocBox}, parser::ast::{Program, typ::Type}, tokeniser::tokentype::*, validator::{predefined::SIZE, validate_expr}}; -use super::{expr::Expr, typ::Type, Ast}; +use super::expr::Expr; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, PartialOrd)] pub enum Literal { Number(Number), Ident(Ident), @@ -13,6 +12,36 @@ pub enum Literal { Array(Vec>), ArrayRepeat { val: Box>, - count: Box>, + count: usize, }, } + +impl Literal { + pub fn to_type(&self, prog: &mut Program) -> anyhow::Result { + let t = match self { + Self::Number(_) => Type::Builtin { name: String::from("usize"), size: SIZE as u8, signed: true }, + Self::Ident(ident) => Type::Owned(ident.clone()), + Self::String(_) => Type::Owned(Ident(String::from("str"))), + Self::Char(_) => Type::Owned(Ident(String::from("char"))), + Self::Array(arr) => { + if arr.is_empty() { + Type::SizedArray { + inner: Box::new(Type::Builtin { name: "void".to_string(), size: 0, signed: false }), + count: LocBox::new(&Loc::default(), Expr::Literal(Literal::Number(Number { val: 0, base: 10, signed: false }))) + } + } else { + let item = arr.first().unwrap(); + Type::SizedArray { + inner: Box::new(validate_expr(prog, item.inner())?.unwrap()), + count: LocBox::new(item.loc(), Expr::Literal(Literal::Number(Number { val: arr.len(), base: 10, signed: false }))) + } + } + } + Self::ArrayRepeat { val, count } => { + Type::SizedArray { inner: Box::new(validate_expr(prog, val.inner())?.unwrap()), count: LocBox::new(val.loc(), Expr::Literal(Literal::Number(Number { val: *count, base: 10, signed: false })))} + } + + }; + Ok(t) + } +} diff --git a/src/parser/ast/mod.rs b/src/parser/ast/mod.rs index eea25c4..287582a 100644 --- a/src/parser/ast/mod.rs +++ b/src/parser/ast/mod.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use statement::{ConstVar, Enum, Function, StaticVar, Struct}; -use crate::{common::loc::LocBox, validator::predefined::TypeType}; +use crate::{common::loc::LocBox, parser::ast::{expr::Path, statement::Let, typ::Type}}; pub use crate::tokeniser::tokentype::*; pub mod expr; @@ -10,19 +10,99 @@ pub mod literal; pub mod statement; pub mod typ; + +#[derive(Debug, Clone)] +pub struct Scope { + pub vars: HashMap>, + pub static_vars: HashMap, + pub const_vars: HashMap, + pub inner_scope: Option> +} + #[derive(Debug, Clone)] pub struct Program { pub ast: expr::Block, pub structs: HashMap>, pub enums: HashMap>, - pub types: HashMap, + pub types: HashMap>, pub functions: HashMap>, pub member_functions: HashMap>>, pub static_vars: HashMap, - pub const_vars: HashMap -} + pub const_vars: HashMap, + pub scope: Option +} -#[derive(Debug, Clone)] + +impl Scope { + pub fn get_var(&self, name: &Ident) -> Option<(Ident, LocBox)> { + if let Some(inner) = &self.inner_scope { + if let Some((ident, var)) = inner.get_var(name) { + return Some((ident, var)); + } + } + if let Some(var) = self.vars.get(name) { + return Some((name.clone(), var.clone())); + } + None + } + pub fn get_static_var(&self, name: &Ident) -> Option<(Ident, LocBox)> { + if let Some(inner) = &self.inner_scope { + if let Some((ident, var)) = inner.get_static_var(name) { + return Some((ident, var)); + } + } + if let Some(var) = self.static_vars.get(name) { + return Some((name.clone(), LocBox::new(var.typ.loc(), var.clone()))); + } + None + } + pub fn get_const_var(&self, name: &Ident) -> Option<(Ident, LocBox)> { + if let Some(inner) = &self.inner_scope { + if let Some((ident, var)) = inner.get_const_var(name) { + return Some((ident, var)); + } + } + if let Some(var) = self.const_vars.get(name) { + return Some((name.clone(), LocBox::new(var.typ.loc(), var.clone()))); + } + None + } +} + +impl Program { + pub fn get_var(&self, name: &Ident) -> Option<(Ident, LocBox)> { + if let Some(scope) = &self.scope { + scope.get_var(name) + } else { + None + } + } + pub fn get_static_var(&self, name: &Ident) -> Option<(Ident, LocBox)> { + if let Some(scope) = &self.scope { + if let Some((name, var)) = scope.get_static_var(name) { + return Some((name, var)); + } + } + if let Some(var) = self.static_vars.get(name) { + return Some((name.clone(), LocBox::new(var.typ.loc(), var.clone()))); + } + None + } + pub fn get_const_var(&self, name: &Ident) -> Option<(Ident, LocBox)> { + if let Some(scope) = &self.scope { + if let Some((name, var)) = scope.get_const_var(name) { + return Some((name, var)); + } + } + if let Some(var) = self.const_vars.get(name) { + return Some((name.clone(), LocBox::new(var.typ.loc(), var.clone()))); + } + None + } +} + + +#[derive(Debug, Clone, PartialEq, PartialOrd)] pub enum Ast { Expr(LocBox), Statement(LocBox), diff --git a/src/parser/ast/statement.rs b/src/parser/ast/statement.rs index b1ad7f9..74d7cfb 100644 --- a/src/parser/ast/statement.rs +++ b/src/parser/ast/statement.rs @@ -1,13 +1,12 @@ -use std::collections::HashMap; use anyhow::bail; -use crate::{common::{loc::LocBox, Loc}, lerror}; +use crate::{common::loc::LocBox, lerror, parser::ast::expr::Path}; use super::{expr::{Block, Expr}, literal::Literal, typ::Type, Char, Ident, Number, Program, TString}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, PartialOrd)] pub enum Statement { Fn(Function), TypeAlias(TypeAlias), @@ -15,46 +14,49 @@ pub enum Statement { Enum(Enum), ConstVar(ConstVar), StaticVar(StaticVar), - Let { - name: Ident, - typ: Option>, - val: Option>, - }, + Let(Let), } -#[derive(Debug, Clone)] + +#[derive(Debug, Clone, PartialEq, PartialOrd)] +pub struct Let { + pub name: Ident, + pub typ: Option>, + pub val: Option>, +} +#[derive(Debug, Clone, PartialEq, PartialOrd)] pub struct ConstVar { pub name: Ident, pub typ: LocBox, pub val: LocBox, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, PartialOrd)] pub struct StaticVar { pub name: Ident, pub typ: LocBox, pub val: LocBox, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, PartialOrd)] pub struct TypeAlias { pub name: Ident, pub typ: LocBox, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, PartialOrd)] pub struct Struct { pub name: Ident, pub fields: Vec<(Ident, LocBox)>, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, PartialOrd)] pub struct Enum { pub name: Ident, pub fields: Vec, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, PartialOrd)] pub struct Function { pub struct_name: Option, pub name: Ident, @@ -123,7 +125,7 @@ impl Struct { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, PartialOrd)] pub enum ConstDataTyp { Byte(u8), AddrOfConst(Ident), @@ -189,7 +191,18 @@ pub fn get_constant_data_as_bytes(program: &Program, value: LocBox, is_lit 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 num = Vec::new(); + let mut inc = 0; + while inc < 8 { + num.push(ConstDataTyp::Byte(((count << 8*inc) & 0xff) as u8)); + inc += 1; + } + + if is_little_endian { + num.reverse(); + } + let mut count = 0 as usize; for b in num { let ConstDataTyp::Byte(b) = b else {unreachable!()}; diff --git a/src/parser/ast/typ.rs b/src/parser/ast/typ.rs index 20dc928..1e12e7c 100644 --- a/src/parser/ast/typ.rs +++ b/src/parser/ast/typ.rs @@ -2,11 +2,11 @@ use std::fmt::Display; use anyhow::bail; -use crate::{common::{loc::LocBox, Loc}, logger::log, validator::predefined::TypeType}; +use crate::common::loc::LocBox; use super::{expr::Expr, literal::Literal, Ident, Program}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, PartialOrd)] pub enum Type { Ref { inner: Box, @@ -20,12 +20,84 @@ pub enum Type { inner: Box, }, Owned(Ident), + Builtin { + name: String, + size: u8, + signed: bool, + } } impl Type { + pub fn get_absolute_value(&self, program: &Program) -> anyhow::Result { + match self { + Self::Ref { inner, .. } => inner.get_absolute_value(program), + Self::Owned(ident) => { + if let Some(var) = program.get_var(ident) { + Ok(var.1.inner().typ.clone().expect("type should be computed if were already using it").inner().clone()) + } else { + bail!(""); + } + } + Self::SizedArray { .. } | + Self::Builtin { .. } | + Self::UnsizedArray { .. } => Ok(self.clone()), + + } + } + pub fn is_numeric(&self, program: &Program) -> bool { + match self { + Self::Ref { inner, .. } => { + inner.is_numeric(program) + } + Self::Owned(name) => { + match program.types.get(&name) { + Some(t) => t.inner().is_numeric(program), + _ => false + } + }, + Self::SizedArray { .. } => false, + Self::UnsizedArray { .. } => false, + Self::Builtin { name, .. } => { + match name.as_str() { + "u8" | "i8" | + "u16" | "i16" | + "u32" | "i32" | + "u64" | "i64" | + "usize" | "isize" => true, + _ => false, + } + } + } + } + + pub fn is_ptr(&self, program: &Program) -> bool { + match self { + Self::Owned(name) => { + match program.types.get(&name) { + Some(t) => t.inner().is_ptr(program), + _ => false + } + }, + Self::Ref { .. } => true, + _ => false + } + } + + pub fn is_bool(&self, program: &Program) -> bool { + match self { + Self::Owned(name) => { + match program.types.get(&name) { + Some(t) => t.inner().is_bool(program), + _ => false + } + }, + Self::Builtin { name, .. } if name.as_str() == "bool" => true, + _ => false + } + } pub fn size_of(&self, program: &Program) -> anyhow::Result { match self { - Self::Ref { inner, mutable } => { + Self::Ref { .. } => { // TODO: Use the actual ptr size Ok(size_of::<*const ()>()) } @@ -35,19 +107,15 @@ impl Type { Self::SizedArray { inner, .. } => { Ok(inner.size_of(program)? ) } + Self::Builtin { size, .. } => { + Ok(*size as usize) + } 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); - } - } + return Ok(v.inner().size_of(program)?); } if let Some(_) = program.enums.get(&name) { return Ok(4); // TODO: Make enum size changeable @@ -93,7 +161,10 @@ impl Display for Type { _ => unreachable!() } } - Self::Owned(ident) => write!(f, "{ident}") + Self::Owned(ident) => write!(f, "{ident}"), + Self::Builtin { name, size, signed } => { + write!(f, "(builtin) {} {}({})", name, if *signed { "signed" } else { "unsigned" }, size) + } } } } diff --git a/src/parser/expr.rs b/src/parser/expr.rs index 43ac222..a75d44c 100644 --- a/src/parser/expr.rs +++ b/src/parser/expr.rs @@ -1,8 +1,8 @@ -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use anyhow::{bail, Result}; -use crate::{common::loc::LocBox, debug, error, lerror, parser::{typ::parse_type, Punctuation}, tokeniser::Token}; +use crate::{common::loc::LocBox, debug, lerror, parser::{typ::parse_type, Punctuation}, tokeniser::Token}; use super::{ast::{expr::{Block, CallParams, Expr, IfBranchExpr, IfExpr, Path}, literal::Literal, TokenType}, parse_item, utils, Delimiter, Keyword}; @@ -317,10 +317,15 @@ fn parse_literal(tokens: &mut Vec) -> Result> { bail!("") }; let count = parse_expr(tokens, 0, false)?.unwrap(); + + let Expr::Literal(Literal::Number(count)) = count.inner() else { + lerror!(count.loc(), "a repeating array accepts only literal numbers for count argument"); + bail!("") + }; utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?; return Ok(LocBox::new(start.loc(), Expr::Literal(Literal::ArrayRepeat { val: Box::new(typ), - count: Box::new(count) + count: count.val }))); } else { if let Some(curr) = tokens.last() { @@ -336,7 +341,7 @@ fn parse_literal(tokens: &mut Vec) -> Result> { fn parse_struct_literal(tokens: &mut Vec, name: Path) -> Result> { let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyL), "")?; - let mut fields = HashMap::new(); + let mut fields = BTreeMap::new(); while !tokens.is_empty() { if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR)) { break; @@ -384,8 +389,8 @@ fn parse_path(tokens: &mut Vec) -> Result> { fn parse_unop(tokens: &mut Vec) -> Result> { let typ = utils::check_consume_or_err_from_many(tokens, &[ TokenType::Punct(Punctuation::Not), - TokenType::Punct(Punctuation::Plus), - TokenType::Punct(Punctuation::Minus), + TokenType::Punct(Punctuation::Plus), // Make number positive + TokenType::Punct(Punctuation::Minus), // make number negative TokenType::Punct(Punctuation::Ampersand), TokenType::Punct(Punctuation::Star), ], "")?; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 669eb02..5da0352 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -32,7 +32,8 @@ pub fn parse_program(mut tokens: Vec) -> Result { types: HashMap::new(), structs: HashMap::new(), static_vars: HashMap::new(), - const_vars: HashMap::new() + const_vars: HashMap::new(), + scope: None }) } diff --git a/src/parser/stat.rs b/src/parser/stat.rs index 5e333e6..3d70718 100644 --- a/src/parser/stat.rs +++ b/src/parser/stat.rs @@ -3,6 +3,7 @@ use anyhow::bail; use crate::common::loc::LocBox; use crate::lerror; use crate::parser::ast::TokenType; +use crate::parser::ast::statement::Let; use crate::parser::expr::parse_expr; use crate::parser::{Delimiter, Ident, Keyword, Punctuation}; use crate::tokeniser::Token; @@ -121,7 +122,7 @@ fn parse_let(tokens: &mut Vec) -> Result> { val = Some(_val); } _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?; - Ok(LocBox::new(kw.loc(), Statement::Let { name, typ, val })) + Ok(LocBox::new(kw.loc(), Statement::Let(Let{ name, typ, val }))) } fn parse_constant(tokens: &mut Vec) -> Result> { let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Const), "")?; diff --git a/src/parser/typ.rs b/src/parser/typ.rs index 22e6942..209411b 100644 --- a/src/parser/typ.rs +++ b/src/parser/typ.rs @@ -1,6 +1,6 @@ -use anyhow::Result; +use anyhow::{Result, bail}; -use crate::{common::loc::LocBox, error, logger::log, parser::Delimiter, tokeniser::Token}; +use crate::{common::loc::LocBox, lerror, parser::{Delimiter, ast::{expr::Expr, literal::Literal}}, tokeniser::Token}; use super::{ast::{typ::Type, TokenType}, expr::parse_expr, utils, Keyword, Punctuation}; @@ -26,9 +26,18 @@ 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(); + + match count.inner() { + Expr::Literal(Literal::Number(_)) | + Expr::Path(_) => (), + _ => { + lerror!(count.loc(), "Only literal numbers are allowed in sized arrays"); + bail!("") + } + } typ = Type::SizedArray { inner: Box::new(itm_typ.inner().clone()), - count + count: count }; } else { typ = Type::UnsizedArray { diff --git a/src/targets/mod.rs b/src/targets/mod.rs index d2d91ed..ee94219 100644 --- a/src/targets/mod.rs +++ b/src/targets/mod.rs @@ -2,7 +2,7 @@ use std::{fs::File, path::{Path, PathBuf}}; use crate::{cli::CliArgs, parser::ast::Program}; mod x86_64; -mod none; +//mod none; pub fn get_default_target() -> String { x86_64::cgen::linux::CGen::get_target_triple().to_string() @@ -13,7 +13,7 @@ pub fn get_all_targets() -> Vec<&'static str> { // 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(), + //none::luagen::cct::LuaGen::get_target_triple(), x86_64::cgen::linux::CGen::get_target_triple(), ] } diff --git a/src/targets/x86_64/cgen/linux/mod.rs b/src/targets/x86_64/cgen/linux/mod.rs index 39f5256..2b0f1e1 100644 --- a/src/targets/x86_64/cgen/linux/mod.rs +++ b/src/targets/x86_64/cgen/linux/mod.rs @@ -1,6 +1,6 @@ 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}; +use crate::{common::Loc, parser::ast::{expr::Expr, literal::Literal, statement::{Function, Statement, Struct}, typ::Type, Ast, Ident, Program, Punctuation}, targets::Target}; const INTERNAL_CODE: &'static str = include_str!("internals.c"); @@ -19,8 +19,8 @@ impl Target for CGen { 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)?; + if let Type::Builtin { name: _, size: _, signed: _ } = typ.inner() { + self.write_internal_type(f, program, name, typ.inner())?; } } @@ -34,9 +34,7 @@ impl Target for CGen { 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_typedef(f, program, typ.inner(), name)?; } self.write_fat_comment(f, "Struct definitions"); @@ -45,7 +43,7 @@ impl Target for CGen { } 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<()> { + 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())?; @@ -121,7 +119,7 @@ impl CGen { Expr::If(_ifexpr) => { } - Expr::BinOp { typ, left, right } => { + Expr::BinOp { typ, .. } => { match typ { Punctuation::Eq => { @@ -182,6 +180,7 @@ impl CGen { pf.push_str(&part(f, program, inner)?); pf.push_str("[]"); } + Type::Builtin { .. } => () } Ok(pf) } @@ -223,14 +222,14 @@ impl CGen { write!(f, "[]")?; } } + Type::Builtin { .. } => () } Ok(()) } - fn write_internal_type(&mut self, f: &mut File, program: &Program, name: &Ident, typ: &TypeType) -> anyhow::Result<()> { + fn write_internal_type(&mut self, f: &mut File, _program: &Program, _name: &Ident, typ: &Type) -> anyhow::Result<()> { match typ { - TypeType::Normal(typ) => unreachable!(), - TypeType::Builtin{ size, signed } => { + Type::Builtin{ name, size, signed } => { if *size == 0 { return Ok(()); } @@ -249,7 +248,7 @@ impl CGen { 8 => write!(f, "long long ")?, 3 | 5 | 6 | 7 => unreachable!(), } - writeln!(f, "{};", name.0)?; + writeln!(f, "{};", name)?; } _ => (), } @@ -267,7 +266,7 @@ impl CGen { Ok(()) } - fn write_fn(&mut self, f: &mut File, program: &Program, func: &Function, loc: &Loc) -> anyhow::Result<()> { + 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())?; @@ -295,7 +294,7 @@ impl CGen { Ast::Expr(expr) => { self.write_expr(f, program, expr.inner(), expr.loc())? } - Ast::Statement(stat) => { + Ast::Statement(_) => { //todo!() } } diff --git a/src/tokeniser/mod.rs b/src/tokeniser/mod.rs index 577c6c3..576da0f 100644 --- a/src/tokeniser/mod.rs +++ b/src/tokeniser/mod.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, fmt::Display}; use anyhow::bail; use parse_int::parse; -use crate::{common::{loc::LocIncr, Loc}, error, lerror}; +use crate::{common::{loc::LocIncr, Loc},lerror}; pub mod tokentype; use tokentype::*; diff --git a/src/tokeniser/tokentype.rs b/src/tokeniser/tokentype.rs index d90cc03..22e1fb3 100644 --- a/src/tokeniser/tokentype.rs +++ b/src/tokeniser/tokentype.rs @@ -1,6 +1,8 @@ use core::panic; use std::fmt::Display; +use crate::parser::ast::expr::Path; + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Ident(pub String); @@ -10,6 +12,12 @@ impl Display for Ident { } } +impl Ident { + pub fn as_path(self) -> Path { + Path(vec![self]) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Number { diff --git a/src/validator/calculate_types.rs b/src/validator/calculate_types.rs new file mode 100644 index 0000000..90e2cea --- /dev/null +++ b/src/validator/calculate_types.rs @@ -0,0 +1,59 @@ +use anyhow::bail; + +use crate::{common::loc::LocBox, parser::ast::{Ast, Program, expr::Expr, statement::Statement}, validator::validate_expr}; + + + + +pub fn calc(prog: &mut Program) -> anyhow::Result<()> { + let mut body = prog.ast.0.clone(); + for item in body.iter_mut() { + calc_ast(prog, item)?; + } + prog.ast.0 = body; + Ok(()) +} + +pub fn calc_ast(prog: &mut Program, ast: &mut Ast) -> anyhow::Result<()> { + match ast { + Ast::Statement(ref mut stat) => calc_stat(prog, stat.inner_mut())?, + Ast::Expr(ref mut expr) => calc_expr(prog, expr.inner_mut())?, + } + Ok(()) +} + +pub fn calc_stat(prog: &mut Program, stat: &mut Statement) -> anyhow::Result<()> { + match stat { + Statement::Fn(func) => { + if let Some(body) = &mut func.body { + for item in &mut body.0 { + calc_ast(prog, item)?; + } + } + } + Statement::Let(lt) => { + match (lt.typ.clone(), lt.val.clone()) { + (None, None) => { + + } + (None, Some(val)) => { + let Some(t) = validate_expr(prog, val.inner())? else { + lerror!(val.loc(), "Expected a type, go none"); + bail!(""); + }; + lt.typ = Some(LocBox::new(val.loc(), t)); + } + _ => () + } + } + _ => () + } + + + Ok(()) +} + +pub fn calc_expr(_prog: &mut Program, _expr: &mut Expr) -> anyhow::Result<()> { + Ok(()) + +} diff --git a/src/validator/mod.rs b/src/validator/mod.rs index 01cbc81..95a3c55 100644 --- a/src/validator/mod.rs +++ b/src/validator/mod.rs @@ -1,14 +1,11 @@ use std::collections::HashMap; -use anyhow::{anyhow, bail}; +use anyhow::bail; -use crate::{common::{loc::LocBox, Loc}, parser::ast::{expr::{Block, Expr}, statement::{Statement, TypeAlias}, typ::Type, Ast, Program}}; +use crate::{common::{Loc, loc::LocBox}, parser::ast::{Ast, Program, Punctuation, TokenType, expr::*, statement::*, typ::Type}}; pub mod predefined; - -struct Scope { - -} +pub mod calculate_types; pub fn validate_code(prog: &mut Program) -> anyhow::Result<()> { let Block(items) = prog.ast.clone(); @@ -20,24 +17,9 @@ pub fn validate_code(prog: &mut Program) -> anyhow::Result<()> { //dbg!(&prog.enums); //dbg!(&prog.member_functions); //dbg!(&prog.functions); - for item in items { + for item in items.iter() { match item { - Ast::Statement(stat) => { - match stat.inner() { - Statement::Fn(func) => {} - 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) => {} - } - } + Ast::Statement(ref stat) => validate_stat(prog, &stat, CurrentState::Outside)?, Ast::Expr(_) => unreachable!() } } @@ -46,6 +28,406 @@ pub fn validate_code(prog: &mut Program) -> anyhow::Result<()> { Ok(()) } + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +enum CurrentState { + InFunc, + Outside, +} + + + +fn validate_stat(prog: &mut Program, stat: &LocBox, current_state: CurrentState) -> anyhow::Result<()> { + match stat.inner() { + Statement::Fn(func) => validate_fn(prog, &func)?, + Statement::Let(lt) if current_state == CurrentState::InFunc => validate_stat_let(prog, lt)?, + Statement::Let(_) if current_state == CurrentState::Outside => { + lerror!(stat.loc(), "Let statements are not allowed outside a function"); + bail!("") + } + Statement::ConstVar(var) => validate_const_var(prog, var)?, + Statement::StaticVar(var) => validate_static_var(prog, var)?, + Statement::Enum(enm) => validate_enum(prog, enm)?, + Statement::Struct(strct) => validate_struct(prog, strct)?, + Statement::TypeAlias(alias) => validate_type_alias(prog, alias)?, + Statement::Let(_) => unreachable!() + } + Ok(()) +} + +fn validate_stat_let(prog: &mut Program, lt: &Let) -> anyhow::Result<()> { + if let Some(val) = <.val && let Some(t) = <.typ { + let typ = validate_expr(prog, &val.inner())?; + } + Ok(()) +} + +fn validate_ast(prog: &mut Program, ast: &Ast) -> anyhow::Result> { + match ast { + Ast::Expr(expr) => { + validate_expr(prog, &expr.inner()) + } + Ast::Statement(stat) => { + validate_stat(prog, &stat, CurrentState::InFunc)?; + Ok(None) + } + } + +} + +pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result> { + match expr { + Expr::Break | Expr::Continue => Ok(None), + Expr::Return(ref ret) => { + if let Some(expr) = &**ret { + validate_expr(prog, expr.inner()) + } else { + Ok(None) + } + } + Expr::Struct { path, fields } => { + // this is probably fucked so fix this later + let name = path.0.last().expect("Paths always have at least one part"); + let typ = Type::Owned(name.clone()); + // this is so ass, this checks if struct exists + validate_type(prog, &LocBox::new(&Loc::default(), typ.clone()))?; + + for field in fields { + validate_expr(prog, &field.1.inner())?; + } + + Ok(Some(typ)) + }, + Expr::If(ifs) => { + validate_expr(prog, ifs.test.inner())?; + for item in &ifs.body.0 { + validate_ast(prog, item)?; + } + Ok(None) + } + Expr::Path(path) => { + // TODO: Path hell TBD + Ok(Some(Type::Owned(path.0.last().unwrap().clone()))) + } + Expr::Group(group) => validate_expr(prog, &group.inner()), + Expr::Call { path, params } => { + let loc = path.loc(); + let Expr::Path(path) = path.inner() else { + panic!("fml"); + }; + + let func; + match path.0.len() { + 1 => { + let f = prog.functions.get(&path.0[0]); + match f { + Some(f) => func = f.clone(), + None => { + lerror!(loc, "Could not find function {}", path); + panic!("") + } + } + } + 2 => { + let s = prog.member_functions.get(&path.0[0]).unwrap(); + let f = s.get(&path.0[1]); + match f { + Some(f) => func = LocBox::new(loc, f.clone().inner().clone()), + None => { + lerror!(loc, "Could not find function {}", path); + panic!("") + } + } + } + _ => panic!("") + } + + + for (i, param) in params.0.iter().enumerate() { + let t = validate_expr(prog, param.inner())?; + let ft = func.inner().params[i].1.inner(); + if t.as_ref() != Some(ft) { + lerror!(param.loc(), "expected {ft}, got {}", t.unwrap()); + bail!("") + } + } + if let Some(t) = &func.inner().ret_type { + Ok(Some(t.inner().clone())) + } else { + Ok(None) + + } + } + Expr::ForLoop { init, test, on_loop, body } => { + let _ = validate_ast(prog, init)?; + let _ = validate_expr(prog, test.inner())?; + let _ = validate_expr(prog, on_loop.inner())?; + for item in &body.0 { + let _ = validate_ast(prog, item)?; + } + Ok(None) + }, + Expr::Cast { left, right } => { + validate_expr(prog, left.inner())?; + Ok(Some(right.inner().clone())) + } + Expr::Literal(lit) => { + Ok(Some(lit.to_type(prog)?)) + } + Expr::UnOp { typ, right } => { + match typ { + Punctuation::Not => { + let t = validate_expr(prog, right.inner())?.unwrap(); + if !t.is_bool(prog) { + lerror!(right.loc(), "Expected bool, got {t}"); + } + Ok(Some(t)) + } + Punctuation::Minus | + Punctuation::Plus => { + let t = validate_expr(prog, right.inner())?.unwrap(); + if !t.is_numeric(prog) { + lerror!(right.loc(), "Expected number, got {t}"); + } + Ok(Some(t)) + } + + Punctuation::Ampersand => { + let t = validate_expr(prog, right.inner())?.unwrap(); + Ok(Some(Type::Ref { inner: Box::new(t), mutable: false })) + } + Punctuation::Star => { + let t = validate_expr(prog, right.inner())?.unwrap(); + if !t.is_ptr(prog) { + lerror!(right.loc(), "Expected pointer, got {t}"); + } + + match t { + Type::Ref { inner, .. } => Ok(Some(*inner)), + _ => unreachable!() + } + } + _ => unreachable!() + } + }, + Expr::BinOp { typ, left, right } => { + match typ { + Punctuation::Ampersand | + Punctuation::Or | + Punctuation::Xor | + Punctuation::Plus | + Punctuation::Minus | + Punctuation::Div | + Punctuation::Star | + Punctuation::Mod | + Punctuation::Shl | + Punctuation::Shr => { + let t1_s; + let t2_s; + let t1 = validate_expr(prog, left.inner())?.unwrap(); + let t2 = validate_expr(prog, right.inner())?.unwrap(); + if !t1.is_numeric(prog) { + lerror!(right.loc(), "Expected bool, got {t1}"); + } + if !t2.is_numeric(prog) { + lerror!(right.loc(), "Expected bool, got {t2}"); + } + match &t1 { + Type::Builtin { name: _, size, .. } => t1_s = *size, + _ => unreachable!() + } + + match &t2 { + Type::Builtin { name: _, size, .. } => t2_s = *size, + _ => unreachable!() + } + if t2_s > t1_s { + Ok(Some(t2)) + } else { + Ok(Some(t1)) + } + } + Punctuation::EqEq | + Punctuation::Lt | + Punctuation::Gt | + Punctuation::Le | + Punctuation::Ge | + Punctuation::AndAnd | + Punctuation::OrOr => { + let t1 = validate_expr(prog, left.inner())?.unwrap(); + let t2 = validate_expr(prog, right.inner())?.unwrap(); + if !t1.is_bool(prog) { + lerror!(right.loc(), "Expected bool, got {t1}"); + } + if !t2.is_bool(prog) { + lerror!(right.loc(), "Expected bool, got {t2}"); + } + Ok(Some(t1)) + } + + Punctuation::AddEq | + Punctuation::SubEq | + Punctuation::DivEq | + Punctuation::MulEq | + Punctuation::ModEq | + Punctuation::ShlEq | + Punctuation::ShrEq | + Punctuation::AndEq | + Punctuation::OrEq | + Punctuation::XorEq => { + let var = validate_expr(prog, left.inner())?.unwrap(); + let var_t = var.get_absolute_value(prog)?; + let val_t = validate_expr(prog, right.inner())?.unwrap(); + + if !(var_t.is_numeric(prog) && val_t.is_numeric(prog)) { + lerror!(left.loc(), "Mismatched types, assigning {val_t} to {var_t}"); + bail!(""); + } + + Ok(None) + } + Punctuation::Eq => { + let var = validate_expr(prog, left.inner())?.unwrap(); + let var_t = var.get_absolute_value(prog)?; + let val_t = validate_expr(prog, right.inner())?.unwrap(); + + if !(var_t == val_t || var_t.is_numeric(prog) && val_t.is_numeric(prog)) { + lerror!(left.loc(), "Mismatched types, assigning {val_t} to {var_t}"); + bail!(""); + } + + Ok(None) + } + _ => unreachable!() + } + }, + Expr::ArrayIndex { name, index } => { + let left = validate_expr(prog, name.inner())?; + let Some(left) = left else { + lerror!(name.loc(), "expected value, got nothing, cannot index nothing"); + bail!("") + }; + fn f(prog: &mut Program, left: &Type, loc: &Loc, index: &LocBox) -> anyhow::Result> { + match left { + Type::SizedArray { inner, count } => { + let val1 = count.inner().as_number(prog).unwrap(); + let val2 = index.inner().as_number(prog).unwrap(); + if val1.signed && (val1.val as isize) < 0 { + lerror!(index.loc(), "Cannot index bellow 0"); + bail!(""); + } + + if val1.val <= val2.val { + lerror!(index.loc(), "Cannot index outside aray boundaries"); + bail!(""); + } + Ok(Some(*inner.clone())) + } + Type::UnsizedArray { inner } => { + Ok(Some(*inner.clone())) + } + Type::Owned(name) => { + let t = prog.types.get(name).cloned(); + if let Some(t) = t { + f(prog, t.inner(), t.loc(), index) + } else { + lerror!(loc, "Unknown type {name}"); + bail!("") + } + } + Type::Ref { .. } | + Type::Builtin { .. } => { + lerror!(loc, "Numeric, pointer, and void types are not indexable (TODO: Maybe allow indexing pointers?)"); + bail!("") + } + } + } + f(prog, &left, name.loc(), &index) + }, + Expr::PtrFieldAccess { left, right } | + Expr::FieldAccess { left, right } => { + let left = validate_expr(prog, left.clone().unwrap().inner())?; + let right = validate_expr(prog, right.clone().inner())?.unwrap(); + let Type::Owned(right) = right else { + panic!() + }; + match left.unwrap() { + Type::Owned(name) => { + if let Some(strct) = prog.structs.get(&name) { + for field in &strct.inner().fields { + if field.0 == right { + return Ok(Some(field.1.inner().clone())); + } + } + } + } + _ => panic!() + } + + Ok(None) + }, + Expr::WhileLoop { test, body } => { + let _ = validate_expr(prog, test.inner())?; + for item in &body.0 { + let _ = validate_ast(prog, item)?; + } + Ok(None) + + }, + Expr::InfLoop { body } => { + for item in &body.0 { + let _ = validate_ast(prog, item)?; + } + Ok(None) + } + + } +} + +fn validate_fn(prog: &mut Program, func: &Function) -> anyhow::Result<()> { + for param in &func.params { + validate_type(prog, ¶m.1)?; + } + if let Some(body) = &func.body { + for item in &body.0 { + validate_ast(prog, item)?; + } + } + + Ok(()) +} + +fn validate_const_var(prog: &mut Program, var: &ConstVar) -> anyhow::Result<()> { + validate_type(prog, &var.typ)?; + Ok(()) +} + +fn validate_static_var(prog: &mut Program, var: &StaticVar) -> anyhow::Result<()> { + validate_type(prog, &var.typ)?; + Ok(()) +} + +fn validate_enum(_: &mut Program, _: &Enum) -> anyhow::Result<()> { + // Enum has no actual types + Ok(()) +} + +fn validate_struct(prog: &mut Program, strct: &Struct) -> anyhow::Result<()> { + for field in &strct.fields { + if let Err(e) = validate_type(prog, &field.1) { + error!("Could not find type in field {}::{}", strct.name, field.0); + anyhow::bail!(e); + } + } + Ok(()) +} + +fn validate_type_alias(prog: &mut Program, alias: &TypeAlias) -> anyhow::Result<()> { + validate_type(prog, &alias.typ)?; + Ok(()) +} + + fn check_that_types_exist_for_items(prog: &mut Program, items: &Vec) -> anyhow::Result<()> { let mut errored = false; for item in items { @@ -116,10 +498,11 @@ fn validate_type(prog: &mut Program, typ: &LocBox) -> anyhow::Result<()> { prog.structs.get(typ).is_some() { Ok(()) } else { - // lerror!(loc, "Could not find type '{}'", typ.0); + lerror!(loc, "Could not find type '{}'", typ.0); bail!("") } } + Type::Builtin { .. } => Ok(()) } } f(prog, typ.inner(), typ.loc()) @@ -153,7 +536,7 @@ fn collect_types_and_constants(prog: &mut Program, items: &Vec) { } Statement::TypeAlias(alias) => { let typ = alias.clone().typ.inner().clone(); - prog.types.insert(alias.name.clone(), predefined::TypeType::Normal(LocBox::new(stat.loc(), typ))); + prog.types.insert(alias.name.clone(), LocBox::new(stat.loc(), typ)); } Statement::Let { .. } => (), Statement::ConstVar(var) => { diff --git a/src/validator/predefined.rs b/src/validator/predefined.rs index 9a90bcd..900abc2 100644 --- a/src/validator/predefined.rs +++ b/src/validator/predefined.rs @@ -1,13 +1,14 @@ use std::collections::HashMap; use lazy_static::lazy_static; use crate::common::Loc; +use crate::parser::ast::expr::Path; use crate::parser::typ::parse_type; use crate::{common::loc::LocBox, parser::ast::{statement::Function, typ::Type, Ident, Program}}; #[cfg(target_arch="x86_64")] -const SIZE: usize = 8; +pub const SIZE: usize = 8; #[cfg(target_arch="x86")] -const SIZE: usize = 4; +pub const SIZE: usize = 4; lazy_static!( pub static ref TYPES_RAW: HashMap<&'static str, (usize, bool)> = [ @@ -22,6 +23,7 @@ lazy_static!( ("i16", (2, true)), ("i32", (4, true)), ("i64", (8, true)), + ("bool", (1, true)), ].into(); pub static ref FUNCTIONS: HashMap<&'static str, (Vec<(&'static str, &'static str)>, &'static str)> = [ ("syscall", (vec![ @@ -32,18 +34,14 @@ lazy_static!( ].into(); ); -#[derive(Debug, Clone)] -pub enum TypeType { - Normal(LocBox), - Builtin { - size: usize, - signed: bool, - } -} pub fn load_builtin(prog: &mut Program) { + let loc = Loc::new("(internal)", 0, 0); for (name, (size, signed)) in TYPES_RAW.iter() { - prog.types.insert(Ident(name.to_string()), TypeType::Builtin{ size: *size, signed: *signed }); + prog.types.insert( + Ident(name.to_string()), + LocBox::new(&loc, Type::Builtin{ name: name.to_string(), size: *size as u8, signed: *signed }) + ); } for (name, (args, ret_typ)) in FUNCTIONS.iter() {