diff --git a/src/common/loc.rs b/src/common/loc.rs index 7b47475..0b15bce 100644 --- a/src/common/loc.rs +++ b/src/common/loc.rs @@ -1,6 +1,6 @@ use std::fmt::{Debug, Display}; -#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)] pub struct Loc { file: String, line: usize, @@ -27,7 +27,7 @@ impl Loc { } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct LocBox { inner: T, loc: Loc diff --git a/src/main.rs b/src/main.rs index ce20f78..4c537b9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,9 +25,8 @@ 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.literals); progs.push((fp, prog)); } diff --git a/src/parser/ast/expr.rs b/src/parser/ast/expr.rs index b0c7eeb..4b5b4c9 100644 --- a/src/parser/ast/expr.rs +++ b/src/parser/ast/expr.rs @@ -4,7 +4,7 @@ use crate::{common::loc::LocBox, parser::ast::{Program, literal::Literal}, token use super::{typ::Type, Ast}; -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)] pub enum Expr { // Comment(Comment), Group(Box>), @@ -16,8 +16,9 @@ pub enum Expr { typ: Punctuation, left: Box>, right: Box>, + signed: bool, }, - Literal(super::literal::Literal), + Literal(String, super::literal::Literal), // string is id ArrayIndex { name: Box>, index: Box>, @@ -27,20 +28,21 @@ pub enum Expr { path: Box>, params: CallParams, // LocBox ~ (, Expr)* }, - //MethodCall { - // var_name: Box>, - // method_name: Ident, - // params: CallParams, - //}, + MethodCall { + left: Box>, + params: CallParams, + }, /// the left side only exists on the /.|->/ chain FieldAccess { left: Box>>, right: Box>, + offset: usize }, PtrFieldAccess { left: Box>>, right: Box>, + offset: usize }, ForLoop { init: Box, @@ -56,10 +58,7 @@ pub enum Expr { body: Block, }, If(IfExpr), - Struct { - path: Path, - fields: BTreeMap>, - }, + Struct(String, StructLit), Return(Box>>), Break, Continue, @@ -69,6 +68,12 @@ pub enum Expr { }, } +#[derive(Debug, Clone, Hash, PartialEq, PartialOrd)] +pub struct StructLit { + pub path: Path, + pub fields: BTreeMap>, +} + impl Expr { pub fn unwrap_path(&self) -> Path { let Expr::Path(p) = self else {panic!("Unwrapping")}; @@ -76,7 +81,7 @@ impl Expr { } pub fn as_number(&self, prog: &Program) -> Option { match self { - Self::Literal(Literal::Number(num)) => Some(*num), + 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) @@ -91,10 +96,10 @@ impl Expr { -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)] pub struct CallParams(pub Vec>); -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)] pub struct Block(pub Vec); #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Hash)] @@ -113,14 +118,20 @@ impl Display for Path { } } -#[derive(Debug, Clone, PartialEq, PartialOrd)] +impl Path { + pub fn display_asm_compat(&self) -> String { + self.to_string().replace("::", "$") + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)] pub struct IfExpr { pub test: Box>, pub body: Block, pub else_if: Option } -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)] pub enum IfBranchExpr { ElseIf(Box), Else(Block) diff --git a/src/parser/ast/literal.rs b/src/parser/ast/literal.rs index f0396ce..7725c68 100644 --- a/src/parser/ast/literal.rs +++ b/src/parser/ast/literal.rs @@ -3,13 +3,14 @@ use crate::{common::{Loc, loc::LocBox}, parser::ast::{Program, typ::Type}, token use super::expr::Expr; -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)] pub enum Literal { Number(Number), Ident(Ident), String(TString), Char(Char), Array(Vec>), + Bool(bool), ArrayRepeat { val: Box>, count: usize, @@ -19,26 +20,31 @@ pub enum Literal { 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::Number(_) => Type::Builtin { name: String::from("usize"), size: SIZE as u8, signed: false }, Self::Ident(ident) => Type::Owned(ident.clone()), - Self::String(_) => Type::Owned(Ident(String::from("str"))), + Self::String(_) => Type::Ref { inner: Box::new(Type::Owned(Ident(String::from("str")))), mutable: false}, Self::Char(_) => Type::Owned(Ident(String::from("char"))), + Self::Bool(_) => Type::Owned(Ident(String::from("bool"))), 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 }))) + count: LocBox::new(&Loc::default(), Expr::Literal(String::new(), Literal::Number(Number { val: 0, base: 10, signed: false }))) } } else { let item = arr.first().unwrap(); + let loc = item.loc().clone(); + let mut item = item.inner().clone(); 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 }))) + inner: Box::new(validate_expr(prog, &mut item)?.unwrap()), + count: LocBox::new(&loc, Expr::Literal(String::new(), 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 })))} + let loc = val.loc().clone(); + let mut val = val.inner().clone(); + Type::SizedArray { inner: Box::new(validate_expr(prog, &mut val)?.unwrap()), count: LocBox::new(&loc, Expr::Literal(String::new(), Literal::Number(Number { val: *count, base: 10, signed: false })))} } }; diff --git a/src/parser/ast/mod.rs b/src/parser/ast/mod.rs index 287582a..2fb5ca9 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, parser::ast::{expr::Path, statement::Let, typ::Type}}; +use crate::{common::loc::LocBox, parser::ast::{expr::StructLit, literal::Literal, statement::Let, typ::Type}}; pub use crate::tokeniser::tokentype::*; pub mod expr; @@ -11,7 +11,7 @@ pub mod statement; pub mod typ; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct Scope { pub vars: HashMap>, pub static_vars: HashMap, @@ -29,7 +29,10 @@ pub struct Program { pub member_functions: HashMap>>, pub static_vars: HashMap, pub const_vars: HashMap, - pub scope: Option + pub literals: HashMap, // string is id of literal + pub struct_literals: HashMap, // string is id of literal + pub scope: Option, + pub curr_fn_args: HashMap> } @@ -102,7 +105,7 @@ impl Program { } -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)] pub enum Ast { Expr(LocBox), Statement(LocBox), diff --git a/src/parser/ast/statement.rs b/src/parser/ast/statement.rs index 74d7cfb..8aee698 100644 --- a/src/parser/ast/statement.rs +++ b/src/parser/ast/statement.rs @@ -1,12 +1,14 @@ +use std::collections::HashMap; + use anyhow::bail; -use crate::{common::loc::LocBox, lerror, parser::ast::expr::Path}; +use crate::{common::loc::LocBox, lerror}; use super::{expr::{Block, Expr}, literal::Literal, typ::Type, Char, Ident, Number, Program, TString}; -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)] pub enum Statement { Fn(Function), TypeAlias(TypeAlias), @@ -18,45 +20,45 @@ pub enum Statement { } -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)] pub struct Let { pub name: Ident, pub typ: Option>, pub val: Option>, } -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)] pub struct ConstVar { pub name: Ident, pub typ: LocBox, pub val: LocBox, } -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)] pub struct StaticVar { pub name: Ident, pub typ: LocBox, pub val: LocBox, } -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)] pub struct TypeAlias { pub name: Ident, pub typ: LocBox, } -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)] pub struct Struct { pub name: Ident, pub fields: Vec<(Ident, LocBox)>, } -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)] pub struct Enum { pub name: Ident, pub fields: Vec, } -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)] pub struct Function { pub struct_name: Option, pub name: Ident, @@ -125,49 +127,51 @@ impl Struct { } } -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)] pub enum ConstDataTyp { - Byte(u8), + Bytes(Vec), AddrOfConst(Ident), AddrOfStatic(Ident), AddrOfFunc(Ident), + Variable(Ident, usize), + Array(Vec) } -pub fn get_constant_data_as_bytes(program: &Program, value: LocBox, is_little_endian: bool, must_be_number: bool) -> anyhow::Result> { +pub fn get_constant_data_as_bytes(program: &Program, struct_items: &HashMap, value: LocBox, is_big_endian: bool, must_be_number: bool) -> anyhow::Result { match value.inner() { - Expr::Literal(lit) => { + 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)]) + Ok(ConstDataTyp::Bytes(vec![*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::>(); + let mut val = val.chars().into_iter().map(|v| v as u8).collect::>(); if *cstr { - val.push(ConstDataTyp::Byte(0)); + val.push(0); } - Ok(val) + Ok(ConstDataTyp::Bytes(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)); + buf.push(((val << 8*inc) & 0xff) as u8); inc += 1; } - if is_little_endian { + if is_big_endian { buf.reverse(); } - Ok(buf) + Ok(ConstDataTyp::Bytes(buf)) } Literal::Array(arr) => { if must_be_number { @@ -176,84 +180,89 @@ pub fn get_constant_data_as_bytes(program: &Program, value: LocBox, is_lit } let mut bytes = Vec::new(); if arr.len() < 1 { - return Ok(vec![]); + return Ok(ConstDataTyp::Bytes(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); + let data = get_constant_data_as_bytes(program, struct_items, item.clone(), is_big_endian, must_be_number)?; + bytes.push(data); } - Ok(bytes) + Ok(ConstDataTyp::Array(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 val = get_constant_data_as_bytes(program, struct_items, (**val).clone(), is_big_endian, must_be_number)?; let mut num = Vec::new(); let mut inc = 0; while inc < 8 { - num.push(ConstDataTyp::Byte(((count << 8*inc) & 0xff) as u8)); + num.push(((count << 8*inc) & 0xff) as u8); inc += 1; } - if is_little_endian { + if is_big_endian { num.reverse(); } 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(); + let mut arr = Vec::new(); for _ in 0..count { - val.append(&mut orig.clone()); + arr.push(orig.clone()); } - Ok(val) + Ok(ConstDataTyp::Array(arr)) } - 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::Bool(v) => { + Ok(ConstDataTyp::Bytes(vec![*v as u8])) } - //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!() - //} - + Literal::Ident(_) => unreachable!() } + } - _ => unreachable!() + Expr::Struct(_, strct) => { + let mut bytes = Vec::new(); + if must_be_number { + lerror!(value.loc(), "Expected number got struct literal"); + bail!("") + } + + let mut items = HashMap::new(); + + for (name, item) in program.structs.get(&strct.path.0.first().unwrap()).unwrap().inner().fields.iter() { + items.insert(name.clone(), item.inner().size_of(program)?); + } + + for field in &strct.fields { + bytes.push(get_constant_data_as_bytes(program, &items, field.1.clone(), is_big_endian, must_be_number)?); + } + + Ok(ConstDataTyp::Array(bytes)) + } + Expr::Path(path) => { + let name = path.0.last().unwrap(); + if let Some(var) = program.const_vars.get(name) { + Ok(get_constant_data_as_bytes(program, struct_items, var.val.clone(), is_big_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(ConstDataTyp::AddrOfFunc(name.clone())) + } else if let Some(size) = struct_items.get(name) { + Ok(ConstDataTyp::Variable(name.clone(), *size)) + } else { + lerror!(value.loc(), "Unable to find ident '{name}'"); + bail!("") + } + + } + v => unreachable!("{v:?}") } } diff --git a/src/parser/ast/typ.rs b/src/parser/ast/typ.rs index 1e12e7c..da1fb02 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; +use crate::{common::loc::LocBox, validator::predefined::get_builtin_from_name}; use super::{expr::Expr, literal::Literal, Ident, Program}; -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)] pub enum Type { Ref { inner: Box, @@ -32,12 +32,44 @@ impl Type { match self { Self::Ref { inner, .. } => inner.get_absolute_value(program), Self::Owned(ident) => { + if let Some(t) = get_builtin_from_name(ident.0.as_str()) { + return Ok(t) + } + if let Some(t) = program.curr_fn_args.get(ident) { + return Ok(t.inner().clone().get_absolute_value(program)?); + } else + if program.types.get(ident).is_some() || + program.structs.get(ident).is_some() || + program.enums.get(ident).is_some() + { + return Ok(self.clone()); + } + 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().get_absolute_value(program)?) + } else { + bail!("owo? missing value: {ident}"); + } + } + Self::SizedArray { .. } | + Self::Builtin { .. } | + Self::UnsizedArray { .. } => Ok(self.clone()), + + } + } + + pub fn get_variable_type(&self, program: &Program) -> anyhow::Result { + match self { + Self::Owned(ident) => { + if let Some(t) = program.types.get(ident) { + Ok(t.inner().clone()) + } else 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!(""); + bail!("owo? missing value: {ident}"); } } + Self::Ref { .. } | Self::SizedArray { .. } | Self::Builtin { .. } | Self::UnsizedArray { .. } => Ok(self.clone()), @@ -69,6 +101,40 @@ impl Type { } } } + // Returns None if non numeric + pub fn is_signed(&self, program: &Program) -> Option { + if self.is_ptr(program) { + return Some(false); + } + match self { + Self::Ref { inner, .. } => { + inner.is_signed(program) + } + Self::Owned(name) => { + match program.types.get(&name) { + Some(t) => t.inner().is_signed(program), + _ => None + } + }, + Self::SizedArray { .. } => None, + Self::UnsizedArray { .. } => None, + Self::Builtin { name, .. } => { + match name.as_str() { + "i8" | + "i16" | + "i32" | + "i64" | + "isize" => Some(true), + "u8" | + "u16" | + "u32" | + "u64" | + "usize" => Some(false), + _ => None, + } + } + } + } pub fn is_ptr(&self, program: &Program) -> bool { match self { @@ -96,33 +162,41 @@ impl Type { } } pub fn size_of(&self, program: &Program) -> anyhow::Result { - match self { - Self::Ref { .. } => { - // 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::Builtin { size, .. } => { - Ok(*size as usize) - } - Self::Owned(name) => { - if let Some(v) = program.structs.get(&name) { - return Ok(v.inner().get_size(program)?); + fn f(t: &Type, program: &Program, behind_a_ptr: bool) -> anyhow::Result { + match t { + Type::Ref { .. } => { + // TODO: Use the actual ptr size + Ok(size_of::<*const ()>()) } - if let Some(v) = program.types.get(&name) { - return Ok(v.inner().size_of(program)?); + Type::UnsizedArray { .. } => { + if behind_a_ptr { + Ok(size_of::<*const ()>()) + } else { + bail!("Unsized arrays dont have a known size and must be behind a pointer"); + } } - if let Some(_) = program.enums.get(&name) { - return Ok(4); // TODO: Make enum size changeable + Type::SizedArray { inner, .. } => { + Ok(inner.size_of(program)? ) + } + Type::Builtin { size, .. } => { + Ok(*size as usize) + } + Type::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) { + return Ok(f(v.inner(), program, behind_a_ptr)?); + } + if let Some(_) = program.enums.get(&name) { + return Ok(4); // TODO: Make enum size changeable + } + bail!("Unknown type '{name}'") } - bail!("Unknown type '{name}'") } } + + f(self, program, false) } pub fn size_of_allow_unsized_arrays(&self, program: &Program) -> anyhow::Result { match self.size_of(program) { @@ -155,7 +229,7 @@ impl Display for Type { Expr::Path(p) => { write!(f, "[{inner}; {p}]") } - Expr::Literal(Literal::Number(n)) => { + Expr::Literal(_, Literal::Number(n)) => { write!(f, "[{inner}; {n}]") } _ => unreachable!() diff --git a/src/parser/expr.rs b/src/parser/expr.rs index a75d44c..1a383f6 100644 --- a/src/parser/expr.rs +++ b/src/parser/expr.rs @@ -2,7 +2,7 @@ use std::collections::{BTreeMap, HashMap}; use anyhow::{bail, Result}; -use crate::{common::loc::LocBox, debug, lerror, parser::{typ::parse_type, Punctuation}, tokeniser::Token}; +use crate::{common::loc::LocBox, debug, lerror, parser::{Punctuation, ast::expr::StructLit, typ::parse_type}, tokeniser::Token}; use super::{ast::{expr::{Block, CallParams, Expr, IfBranchExpr, IfExpr, Path}, literal::Literal, TokenType}, parse_item, utils, Delimiter, Keyword}; @@ -43,7 +43,7 @@ pub fn parse_expr(tokens: &mut Vec, precedence: usize, consume_semi: bool } else if let Some(_) = utils::check(tokens, TokenType::ident("")) { let p = parse_path(tokens)?; - if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) { + if let Some(t) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) { Some(parse_struct_literal(tokens, p.inner().unwrap_path())?) } else { Some(p) @@ -63,6 +63,8 @@ pub fn parse_expr(tokens: &mut Vec, precedence: usize, consume_semi: bool TokenType::number(0, 0, false), TokenType::char('\0'), TokenType::Delim(Delimiter::SquareL), + TokenType::Keyword(Keyword::True), + TokenType::Keyword(Keyword::False) ]) { Some(parse_literal(tokens)?) } else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::While)) { @@ -74,8 +76,10 @@ pub fn parse_expr(tokens: &mut Vec, precedence: usize, consume_semi: bool } else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Return)) { return Ok(Some(parse_return(tokens)?)); } else if let Some(kw) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Break)) { + let _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?; return Ok(Some(LocBox::new(kw.loc(), Expr::Break))); } else if let Some(kw) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Continue)) { + let _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?; return Ok(Some(LocBox::new(kw.loc(), Expr::Continue))); } else if let Some(kw) = utils::check(tokens, TokenType::Keyword(Keyword::If)) { return Ok(Some(LocBox::new(&kw.loc().clone(), Expr::If(parse_if(tokens)?)))); @@ -102,7 +106,11 @@ pub fn parse_expr(tokens: &mut Vec, precedence: usize, consume_semi: bool } if let Some(_) = utils::check_from_many(tokens, BINOP_LIST) { - return Ok(Some(parse_binop(tokens, res, precedence)?)); + let v = parse_binop(tokens, res, precedence)?; + if consume_semi { + _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?; + } + return Ok(Some(v)); } else { if consume_semi { _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?; @@ -110,6 +118,9 @@ pub fn parse_expr(tokens: &mut Vec, precedence: usize, consume_semi: bool return Ok(Some(res)); } } + if consume_semi { + _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?; + } Ok(res) } @@ -129,10 +140,12 @@ fn parse_cast(tokens: &mut Vec, left: LocBox) -> Result) -> Result { let loc = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::If), "")?; + let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?; let Some(test) = parse_expr(tokens, 0, false)? else { lerror!(loc.loc(), "Expected test for if statement, got nothing"); bail!("") }; + let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?; let block = if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) { if let Some(_) = utils::check_2_last(tokens, TokenType::Delim(Delimiter::CurlyR)) { _ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR)); @@ -170,10 +183,12 @@ fn parse_if(tokens: &mut Vec) -> Result { } fn parse_while_loop(tokens: &mut Vec) -> Result> { let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::While), "")?; + let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?; let Some(test) = parse_expr(tokens, 0, false)? else { lerror!(kw.loc(), "Expected test comparrison for while loop, got nothing"); bail!("") }; + let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?; let block = parse_block(tokens)?; Ok(LocBox::new(kw.loc(), Expr::WhileLoop { test: Box::new(test), @@ -182,6 +197,7 @@ fn parse_while_loop(tokens: &mut Vec) -> Result> { } fn parse_for_loop(tokens: &mut Vec) -> Result> { let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::For), "")?; + let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?; let Some(pre) = parse_item(tokens)? else { lerror!(kw.loc(), "Expected init stat for a for loop, got nothing"); bail!("") @@ -196,6 +212,7 @@ fn parse_for_loop(tokens: &mut Vec) -> Result> { lerror!(kw.loc(), "Expected post expression (usually an index increment) for a for loop, got nothing"); bail!("") }; + let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?; let block = parse_block(tokens)?; Ok(LocBox::new(kw.loc(), Expr::ForLoop { @@ -211,6 +228,15 @@ fn parse_inf_loop(tokens: &mut Vec) -> Result> { Ok(LocBox::new(kw.loc(), Expr::InfLoop { body: block })) } fn parse_fn_call(tokens: &mut Vec, left: LocBox) -> Result> { + match left.inner() { + Expr::FieldAccess { .. } | + Expr::PtrFieldAccess { .. } => { + return parse_member_function_call(tokens, left); + } + _ => () + } + + let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?; let mut params = Vec::new(); @@ -257,7 +283,33 @@ fn parse_field_access(tokens: &mut Vec, left: LocBox) -> Result, left: LocBox) -> Result> { + let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "unreachable")?; + let mut params = Vec::new(); + + while !tokens.is_empty() { + if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) { + break; + } + let Some(param) = parse_expr(tokens, 0, false)? else {break}; + params.push(param); + if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) { + if let None = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) { + lerror!(&utils::get_last_loc(), "Expected ',' or ')' but didnt find either"); + bail!("") + } + } + } + _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), ""); + + Ok(LocBox::new(start.loc(), Expr::MethodCall { + left: Box::new(left), + params: CallParams(params) })) } @@ -274,26 +326,33 @@ fn parse_ptr_field_access(tokens: &mut Vec, left: LocBox) -> Result }; Ok(LocBox::new(start.loc(), Expr::PtrFieldAccess { left: Box::new(Some(left)), - right: Box::new(right) + right: Box::new(right), + offset: 0 })) } fn parse_literal(tokens: &mut Vec) -> Result> { + if let Some(tkn) = utils::check_consume(tokens, TokenType::Keyword(Keyword::True)) { + return Ok(LocBox::new(tkn.loc(), Expr::Literal(String::new(), Literal::Bool(true)))); + } else + if let Some(tkn) = utils::check_consume(tokens, TokenType::Keyword(Keyword::False)) { + return Ok(LocBox::new(tkn.loc(), Expr::Literal(String::new(), Literal::Bool(false)))); + } else if let Some(tkn) = utils::check_consume(tokens, TokenType::string("", false)) { let TokenType::String(str) = tkn.tt() else {unreachable!()}; - return Ok(LocBox::new(tkn.loc(), Expr::Literal(Literal::String(str.clone())))); + return Ok(LocBox::new(tkn.loc(), Expr::Literal(String::new(), Literal::String(str.clone())))); } else if let Some(tkn) = utils::check_consume(tokens, TokenType::number(0, 0, false)) { let TokenType::Number(val) = tkn.tt() else {unreachable!()}; - return Ok(LocBox::new(tkn.loc(), Expr::Literal(Literal::Number(val.clone())))); + return Ok(LocBox::new(tkn.loc(), Expr::Literal(String::new(), Literal::Number(val.clone())))); } else if let Some(tkn) = utils::check_consume(tokens, TokenType::char('\0')) { let TokenType::Char(val) = tkn.tt() else {unreachable!()}; - return Ok(LocBox::new(tkn.loc(), Expr::Literal(Literal::Char(val.clone())))); + return Ok(LocBox::new(tkn.loc(), Expr::Literal(String::new(), Literal::Char(val.clone())))); } else if let Some(start) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareL)) { if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareR)) { - return Ok(LocBox::new(start.loc(), Expr::Literal(Literal::Array(Vec::new())))); + return Ok(LocBox::new(start.loc(), Expr::Literal(String::new(), Literal::Array(Vec::new())))); } if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Comma) { let first = parse_expr(tokens, 0, false)?; @@ -310,7 +369,7 @@ 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)))); + return Ok(LocBox::new(start.loc(), Expr::Literal(String::new(), Literal::Array(values)))); } else if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Semi) { let Some(typ) = parse_expr(tokens, 0, true)? else { lerror!(start.loc(), "Expected value, found nothing"); @@ -318,12 +377,12 @@ fn parse_literal(tokens: &mut Vec) -> Result> { }; let count = parse_expr(tokens, 0, false)?.unwrap(); - let Expr::Literal(Literal::Number(count)) = count.inner() else { + 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 { + return Ok(LocBox::new(start.loc(), Expr::Literal(String::new(), Literal::ArrayRepeat { val: Box::new(typ), count: count.val }))); @@ -348,7 +407,7 @@ fn parse_struct_literal(tokens: &mut Vec, name: Path) -> Result, name: Path) -> Result) -> Result> { @@ -442,7 +501,8 @@ fn parse_binop(tokens: &mut Vec, mut lhs: LocBox, precedence: usize lhs = LocBox::new(&op_loc, Expr::BinOp { typ: op, left: Box::new(lhs), - right: Box::new(rhs) + right: Box::new(rhs), + signed: false }); } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 5da0352..64a2dd3 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -33,7 +33,10 @@ pub fn parse_program(mut tokens: Vec) -> Result { structs: HashMap::new(), static_vars: HashMap::new(), const_vars: HashMap::new(), - scope: None + literals: HashMap::new(), + struct_literals: HashMap::new(), + scope: None, + curr_fn_args: HashMap::new() }) } diff --git a/src/parser/stat.rs b/src/parser/stat.rs index 3d70718..f2ffcaf 100644 --- a/src/parser/stat.rs +++ b/src/parser/stat.rs @@ -37,6 +37,7 @@ pub fn parse_statement(tokens: &mut Vec) -> Result) -> Result)> break; } let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?; - utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?; + utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "huhhhhhhhh")?; //dbg!(&name); let typ = parse_type(tokens)?; args.push((name.tt().unwrap_ident(), typ)); diff --git a/src/parser/typ.rs b/src/parser/typ.rs index 209411b..e9d954f 100644 --- a/src/parser/typ.rs +++ b/src/parser/typ.rs @@ -28,7 +28,7 @@ pub fn parse_type(tokens: &mut Vec) -> Result> { let count = parse_expr(tokens, 0, false)?.unwrap(); match count.inner() { - Expr::Literal(Literal::Number(_)) | + Expr::Literal(_, Literal::Number(_)) | Expr::Path(_) => (), _ => { lerror!(count.loc(), "Only literal numbers are allowed in sized arrays"); diff --git a/src/targets/mod.rs b/src/targets/mod.rs index ee94219..2af9308 100644 --- a/src/targets/mod.rs +++ b/src/targets/mod.rs @@ -1,31 +1,32 @@ use std::{fs::File, path::{Path, PathBuf}}; use crate::{cli::CliArgs, parser::ast::Program}; -mod x86_64; +pub mod x86_64; //mod none; pub fn get_default_target() -> String { - x86_64::cgen::linux::CGen::get_target_triple().to_string() + // x86_64::cgen::linux::CGen::get_target_triple().to_string() + x86_64::asmgen::linux::AsmGen::get_target_triple().to_string() } pub fn get_all_targets() -> Vec<&'static str> { vec![ - // x86_64::asmgen::linux::AsmGen::get_target_triple(), + 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(), + // 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::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()) // } diff --git a/src/targets/x86_64/asmgen/linux/mod.rs b/src/targets/x86_64/asmgen/linux/mod.rs index 8b5ebcf..4d0585b 100644 --- a/src/targets/x86_64/asmgen/linux/mod.rs +++ b/src/targets/x86_64/asmgen/linux/mod.rs @@ -1,7 +1,9 @@ -use crate::targets::Target; -use std::io::Write; +use crate::{common::{Loc, loc::LocBox}, parser::ast::{Ast, Ident, Program, Punctuation, expr::{Expr, IfBranchExpr}, literal::Literal, statement::{ConstDataTyp, Function, Statement, get_constant_data_as_bytes}, typ::Type}, targets::Target}; +use std::{collections::HashMap, fs::File, io::Write}; pub struct AsmGen; +const RUNTIME_CODE: &'static str = include_str!("./runtime.s"); + impl Target for AsmGen { fn new() -> Self { Self {} @@ -12,20 +14,672 @@ impl Target for AsmGen { 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)?; + fn write_code(&mut self, program: &crate::parser::ast::Program, f: &mut File) -> anyhow::Result<()> { + writeln!(f, "bits 64")?; + writeln!(f, "global _start")?; writeln!(f, "section .text")?; - - writeln!(f, "section .rodata")?; - for (name, constant) in &program.const_vars { - writeln!(f, "{name}:")?; + writeln!(f, "{}", RUNTIME_CODE)?; + for item in &program.ast.0 { + match item { + Ast::Statement(stat) => { + match stat.inner() { + Statement::Fn(func) => self.write_func(program, f, func.clone(), stat.loc().clone())?, + _ => () + } + } + _ => () + } } + + + writeln!(f, "section .data")?; + self.write_constants(program, f)?; + self.write_literals(program, f)?; + writeln!(f, "section .data")?; + self.write_statics(program, f)?; + Ok(()) } - fn compile(&mut self, _from: &std::path::Path, _to: &std::path::Path) -> anyhow::Result<()> { + fn compile(&mut self, from: &std::path::Path, to: &std::path::Path) -> anyhow::Result<()> { + let mut cmd = std::process::Command::new("nasm"); + let cmd = cmd.args(&[ + "-felf64", + "-o", + to.to_string_lossy().to_string().as_str(), + from.to_string_lossy().to_string().as_str(), + ]); + info!("Running: {} {}", cmd.get_program().to_string_lossy(), cmd.get_args().map(|f| f.to_string_lossy().to_string()).collect::>().join(" ")); + cmd.output()?; Ok(()) } - fn link(&mut self, _from: Vec, _to: &std::path::Path) -> anyhow::Result<()> { + fn link(&mut self, from: Vec, to: &std::path::Path) -> anyhow::Result<()> { + let mut cmd = std::process::Command::new("ld"); + cmd.args(&[ + "-o", + to.to_string_lossy().to_string().as_str(), + ]); + + for item in &from { + cmd.arg(item.to_string_lossy().to_string().as_str()); + } + info!("Running: {} {}", cmd.get_program().to_string_lossy(), cmd.get_args().map(|f| f.to_string_lossy().to_string()).collect::>().join(" ")); + cmd.output()?; Ok(()) } } + +// also used for args +#[derive(Debug, Clone)] +pub enum VarMapT { + Stack(usize, Type), +} + +pub struct FunctionCtx { + vars: HashMap, + stack_offset: usize, + used_registers: usize, + loop_level: usize, // used for loops + if_level: usize, // used for if stats + cmp_level: usize, // used for logical comparisons + pub emit_short_circuit_label: bool, + pub is_last_item: bool, + +} + +impl FunctionCtx { + pub fn new() -> Self { + Self { + vars: Default::default(), + stack_offset: 0, + used_registers: 0, + loop_level: 0, + if_level: 0, + cmp_level: 0, + emit_short_circuit_label: true, + is_last_item: false + } + } + + pub fn insert_stack(&mut self, name: &Ident, size: usize, typ: Type) -> usize { + let offset = self.stack_offset; + self.vars.insert(name.clone(), VarMapT::Stack(self.stack_offset, typ)); + self.stack_offset += size; + offset + } + + pub fn get(&self, name: &Ident) -> Option<&VarMapT> { + self.vars.get(name) + } + pub fn get_stack_offset(&self) -> usize { + self.stack_offset + } + pub fn register_id_to_str(&self, id: usize) -> &str { + match id { + 0 => "rdi", + 1 => "rsi", + 2 => "rdx", + 3 => "rcx", + 4 => "r8", + 5 => "r9", + _ => unreachable!() + } + } + pub fn inc_loop_level(&mut self) -> usize { + self.loop_level += 1; + self.loop_level + } + pub fn dec_loop_level(&mut self) -> usize { + self.loop_level -= 1; + self.loop_level + } + pub fn loop_level(&self) -> usize { + self.loop_level + } + pub fn inc_if_level(&mut self) -> usize { + self.if_level += 1; + self.if_level + } + pub fn dec_if_level(&mut self) -> usize { + self.if_level -= 1; + self.if_level + } + pub fn if_level(&self) -> usize { + self.if_level + } + pub fn inc_cmp_level(&mut self) -> usize { + self.if_level += 1; + self.if_level + } + pub fn dec_cmp_level(&mut self) -> usize { + self.if_level -= 1; + self.if_level + } + pub fn cmp_level(&self) -> usize { + self.cmp_level + } +} + + +impl AsmGen { + pub fn write_func(&self, program: &Program, f: &mut File, func: Function, loc: Loc) -> anyhow::Result<()> { + if let Some(body) = &func.body { + let mut fc = FunctionCtx::new(); + let name = if let Some(struct_name) = &func.struct_name { + format!("{}${}", struct_name, func.name) + } else { + func.name.to_string() + }; + writeln!(f, "{}: ; {} {}", name, loc, func.get_full_name_pretty())?; + if body.0.is_empty() { + writeln!(f, " ret")?; + } else { + let mut buf: Vec = Vec::new(); + let mut last_item = None; + for (i, param) in func.params.iter().enumerate() { + let typ = param.1.clone(); + let typ = typ.inner().clone(); + let offset = fc.insert_stack(¶m.0.clone(), typ.size_of(program)?, typ.clone()); + + writeln!(&mut buf, " mov [rsp+{offset}], {} ; func arg", fc.register_id_to_str(i))?; + } + let body = func.body.expect("Safe as its checked already").0; + for (i, item) in body.iter().enumerate() { + if i == body.len() - 1 { + fc.is_last_item = true; + } + self.write_ast(program, &mut buf, &mut fc, item)?; + last_item = Some(item.clone()); + } + + if fc.stack_offset > 0 { + writeln!(f, " sub rsp, {}", fc.stack_offset)?; + } + f.write(&buf)?; + if fc.stack_offset > 0 { + writeln!(f, " add rsp, {}", fc.stack_offset)?; + } + match last_item { + Some(Ast::Expr(expr)) => { + match expr.inner() { + Expr::Return(_) => (), + _ => writeln!(f, " ret")?, + } + } + _ => () + } + // while writing the return expr, it changes is_last_item to false if it didnt write ret + // and it was the last item in a body + if !fc.is_last_item { + writeln!(f, " ret")?; + } + } + } else { + writeln!(f, " ret")?; + } + writeln!(f, "\n")?; + + Ok(()) + } + pub fn write_ast(&self, program: &Program, f: &mut impl Write, fc: &mut FunctionCtx, ast: &Ast) -> anyhow::Result<()> { + match ast { + Ast::Expr(expr) => self.write_expr(program, f, fc, expr.inner())?, + Ast::Statement(stat) => self.write_stat(program, f, fc, stat.inner())?, + }; + Ok(()) + } + pub fn write_expr(&self, program: &Program, f: &mut impl Write, fc: &mut FunctionCtx, expr: &Expr) -> anyhow::Result<()> { + match expr { + Expr::Cast { .. } => (), + Expr::Literal(id, val) => { + match val { + Literal::Ident(_) => unreachable!(), + Literal::Array(_) | + Literal::String(_) | + Literal::ArrayRepeat { .. } => { + writeln!(f, " lea rax, [rel mcl_lit_{id}]")?; + } + Literal::Bool(v) => { + writeln!(f, " mov rax, {} ; {}", *v as u8, v)?; + } + Literal::Number(_) | + Literal::Char(_) => { + writeln!(f, " mov rax, [rel mcl_lit_{id}]")?; + } + } + } + Expr::Struct(id, strct) => { + writeln!(f, " lea r10, [rel mcl_lit_{id}]")?; + let strct_t = program.structs.get(&strct.path.0[0]).unwrap(); + for (name, expr) in &strct.fields { + self.write_expr(program, f, fc, expr.inner())?; + let offset = strct_t.inner().get_offset_of(program, name)?; + writeln!(f, " mov [r10+{offset}], rax")?; + } + } + Expr::PtrFieldAccess { left, right, offset } => { + self.write_expr(program, f, fc, left.clone().unwrap().inner())?; + writeln!(f, " add rax, {offset} ; ->{:?}", right.inner())?; + }, + Expr::FieldAccess { left, right, offset } => { + self.write_expr(program, f, fc, left.clone().unwrap().inner())?; + writeln!(f, " mov rax, [rel rax] ; .{:?}", right.inner())?; + writeln!(f, " add rax, {offset} ; .{:?}", right.inner())?; + }, + Expr::InfLoop { body } => { + let sl = fc.inc_loop_level(); + writeln!(f, ".L{sl}_test: ; inf loop (named test for tehnical reason)")?; + for item in &body.0 { + self.write_ast(program, f, fc, item)?; + } + writeln!(f, ".L{sl}_end: ; inf loop")?; + + } + Expr::ForLoop { init, test, on_loop, body } => { + let sl = fc.inc_loop_level(); + writeln!(f, ".L{sl}_init: ; for loop")?; + self.write_ast(program, f, fc, &init)?; + writeln!(f, ".L{sl}_test: ; for loop")?; + writeln!(f, " xor rax, rax")?; + self.write_expr(program, f, fc, &test.inner())?; + writeln!(f, " test rax, rax")?; + writeln!(f, " jz .L{sl}_end")?; + writeln!(f, ".L{sl}_on_loop: ; for loop")?; + self.write_expr(program, f, fc, &on_loop.inner())?; + for item in &body.0 { + self.write_ast(program, f, fc, item)?; + } + writeln!(f, ".L{sl}_end: ; for loop")?; + } + Expr::WhileLoop { test, body } => { + let sl = fc.inc_loop_level(); + writeln!(f, ".L{sl}_test: ; while loop")?; + writeln!(f, " xor rax, rax")?; + self.write_expr(program, f, fc, &test.inner())?; + writeln!(f, " test rax, rax")?; + writeln!(f, " jz .L{sl}_end")?; + for item in &body.0 { + self.write_ast(program, f, fc, item)?; + } + writeln!(f, ".L{sl}_end: ; while loop")?; + } + Expr::Continue => { + let sl = fc.loop_level(); + writeln!(f, " jmp .L{sl}_test ; continue")?; + } + Expr::Break => { + let sl = fc.loop_level(); + writeln!(f, " jmp .L{sl}_end ; break")?; + } + Expr::If(ifs) => { + let cl = fc.inc_if_level(); + writeln!(f, " xor rax, rax")?; + self.write_expr(program, f, fc, ifs.test.inner())?; + writeln!(f, " test rax, rax")?; + if ifs.else_if.is_some() { + writeln!(f, " jz .C{cl}_branch0")?; + } else { + writeln!(f, " jz .C{cl}_end")?; + } + for item in &ifs.body.0 { + self.write_ast(program, f, fc, item)?; + } + self.write_expr(program, f, fc, ifs.test.inner())?; + if ifs.else_if.is_some() { + writeln!(f, " jmp .C{cl}_end")?; + } + fn x(slf: &AsmGen, program: &Program, f: &mut impl Write, fc: &mut FunctionCtx, els: &Option, depth: usize) -> anyhow::Result<()> { + let cl = fc.if_level(); + if let Some(els) = els { + match els { + IfBranchExpr::Else(els) => { + for item in &els.0 { + writeln!(f, ".C{cl}_branch{depth}: ; if")?; + slf.write_ast(program, f, fc, item)?; + } + } + IfBranchExpr::ElseIf(elsif) => { + slf.write_expr(program, f, fc, elsif.test.inner())?; + writeln!(f, " test rax, rax")?; + if elsif.else_if.is_some() { + writeln!(f, " jz .C{cl}_branch{}", depth + 1)?; + } else { + writeln!(f, " jz .C{cl}_end")?; + } + for item in &elsif.body.0 { + slf.write_ast(program, f, fc, item)?; + } + writeln!(f, " jmp .C{cl}_end")?; + x(slf, program, f, fc, &elsif.else_if, depth+1)?; + } + } + } + Ok(()) + } + x(self, program, f, fc, &ifs.else_if, 0)?; + writeln!(f, ".C{cl}_end: ; if")?; + } + Expr::Group(grp) => { + self.write_expr(program, f, fc, grp.inner())?; + } + Expr::Return(ret) => { + if let Some(ret) = &**ret { + self.write_expr(program, f, fc, ret.inner())?; + } + if !fc.is_last_item { + writeln!(f, " ret")?; + } else { + fc.is_last_item = false; + } + } + Expr::Call { path, params } => { + for (i, param) in params.0.iter().enumerate() { + self.write_expr(program, f, fc, param.inner())?; + if i <= 5 { + let reg = fc.register_id_to_str(i); + writeln!(f, " mov {reg}, rax")?; + } else { + writeln!(f, " push rax")?; + } + } + + writeln!(f, " call {}", path.inner().unwrap_path().display_asm_compat())?; + } + Expr::UnOp { typ, right } => { + self.write_expr(program, f, fc, right.inner())?; + match typ { + Punctuation::Not => { + writeln!(f, " test rax, rax ; logical not")?; + writeln!(f, " sete al")?; + writeln!(f, " movzx rax, al")?; + }, + Punctuation::Plus => { + writeln!(f, " mov rdx, rax ; +x")?; + writeln!(f, " sar rdx, 63")?; + writeln!(f, " xor rax, rdx")?; + writeln!(f, " sub rax, rdx")?; + }, + Punctuation::Minus => { + writeln!(f, " neg rax ; -x")?; + }, + Punctuation::Ampersand => { + writeln!(f, " ; noop?")?; + }, + Punctuation::Star => { + writeln!(f, " ; noop deref")?; + }, + _ => unreachable!() + } + } + Expr::BinOp { typ, left, right, signed } => { + self.write_expr(program, f, fc, left.inner())?; + writeln!(f, " mov r10, rax")?; + self.write_expr(program, f, fc, right.inner())?; + match typ { + Punctuation::Plus => { + writeln!(f, " add rax, r10")?; + }, + Punctuation::Minus => { + writeln!(f, " sub rax, r10")?; + }, + Punctuation::Div => { + writeln!(f, " mov rdi, rax")?; + writeln!(f, " mov rax, r10 ")?; + if *signed { + writeln!(f, " cqo")?; + writeln!(f, " idiv rdi")?; + } else { + writeln!(f, " xor rdx, rdx")?; + writeln!(f, " div rdi")?; + } + }, + Punctuation::Star => { + writeln!(f, " mov rdi, rax")?; + writeln!(f, " mov rax, r10 ")?; + + if *signed { + writeln!(f, " imul r10 ")?; + } else { + writeln!(f, " mul r10 ")?; + } + }, + Punctuation::Mod => { + writeln!(f, " mov rdi, rax")?; + writeln!(f, " mov rax, r10 ")?; + if *signed { + writeln!(f, " cqo")?; + writeln!(f, " idiv rdi")?; + } else { + writeln!(f, " xor rdx, rdx")?; + writeln!(f, " div rdi")?; + } + writeln!(f, " mov rax, rdx")?; + }, + Punctuation::Shl => { + writeln!(f, " shl r10, rax")?; + }, + Punctuation::Shr => { + writeln!(f, " shr r10, rax")?; + }, + Punctuation::AndAnd => { + let l = fc.cmp_level(); + let should_emit = fc.emit_short_circuit_label; + self.write_expr(program, f, fc, left.inner())?; + writeln!(f, " test rax, rax")?; + writeln!(f, " jz .M{l}_false")?; + fc.emit_short_circuit_label = false; + self.write_expr(program, f, fc, right.inner())?; + fc.emit_short_circuit_label = should_emit; + if !should_emit { + writeln!(f, " setnz al")?; + writeln!(f, " movzx rax, al")?; + writeln!(f, " jmp .M{l}_sc")?; + writeln!(f, ".M{l}_true:")?; + writeln!(f, " mov rax, 1")?; + writeln!(f, ".M{l}_false:")?; + writeln!(f, " xor rax, rax")?; + writeln!(f, ".M{l}_sc:")?; + fc.inc_cmp_level(); + } + }, + Punctuation::OrOr => { + let l = fc.cmp_level(); + let should_emit = fc.emit_short_circuit_label; + self.write_expr(program, f, fc, left.inner())?; + writeln!(f, " cmp rax, rax")?; + writeln!(f, " jnz .M{l}_true")?; + fc.emit_short_circuit_label = false; + self.write_expr(program, f, fc, right.inner())?; + fc.emit_short_circuit_label = should_emit; + if !should_emit { + writeln!(f, " setnz al")?; + writeln!(f, " movzx rax, al")?; + writeln!(f, " jmp .M{l}_sc")?; + writeln!(f, ".M{l}_true:")?; + writeln!(f, " mov rax, 1")?; + writeln!(f, ".M{l}_false:")?; + writeln!(f, " xor rax, rax")?; + writeln!(f, ".M{l}_sc:")?; + fc.inc_cmp_level(); + } + }, + Punctuation::Ampersand => {}, + Punctuation::Or => {}, + Punctuation::Xor => {}, + Punctuation::AddEq => {}, + Punctuation::SubEq => {}, + Punctuation::DivEq => {}, + Punctuation::MulEq => {}, + Punctuation::ModEq => {}, + Punctuation::ShlEq => {}, + Punctuation::ShrEq => {}, + Punctuation::AndEq => {}, + Punctuation::OrEq => {}, + Punctuation::XorEq => {}, + Punctuation::Eq => { + self.write_expr(program, f, fc, right.inner())?; + let VarMapT::Stack(offset, _) = fc.get(&left.inner().unwrap_path().0.clone()[0]).unwrap(); + writeln!(f, "mov [rbx+{offset}], rax")?; + }, + Punctuation::EqEq => {}, + Punctuation::Lt => {}, + Punctuation::Gt => {}, + Punctuation::Le => {}, + Punctuation::Ge => {}, + _ => unreachable!() + } + } + Expr::Path(path) => { + dbg!(&path); + let ident = path.0.last().unwrap().clone(); + if let Some(var) = fc.get(&ident) { + match var { + VarMapT::Stack(offset, typ) => { + match typ { + Type::Builtin { .. } => writeln!(f, " lea rax, [rsp + {offset}]")?, + _ => writeln!(f, " mov rax, [rsp + {offset}]")?, + } + } + } + } else if let Some(_) = program.get_const_var(&ident) { + writeln!(f, " lea rax, [rel {ident}]")?; + } else { + panic!() + } + } + v => unreachable!("{v:?}") + } + Ok(()) + } + pub fn write_stat(&self, program: &Program, f: &mut impl Write, fc: &mut FunctionCtx, stat: &Statement) -> anyhow::Result<()> { + match stat { + Statement::Let(lt) => { + let typ = lt.typ.clone().unwrap(); + let loc = typ.loc().clone(); + let typ = typ.inner().clone(); + let offset = fc.insert_stack(<.name, typ.size_of(program)?, typ.clone()); + + if let Some(value) = <.val { + self.write_expr(program, f, fc, value.inner())?; + writeln!(f, " mov [rsp+{offset}], rax ; {loc} let {};", lt.name)?; + } + } + _ => unreachable!() + } + Ok(()) + } + pub fn write_constants(&self, program: &Program, f: &mut File) -> anyhow::Result<()> { + for (name, constant) in &program.const_vars { + writeln!(f, "{name}: ; const")?; + let bytes = get_constant_data_as_bytes(program, &HashMap::new(), constant.val.clone(), false, false)?; + fn x(bytes: &ConstDataTyp, f: &mut File) -> anyhow::Result<()> { + match bytes { + ConstDataTyp::Array(arr) => { + for v in arr { + x(v, f)?; + } + } + ConstDataTyp::Bytes(bytes) => { + write!(f, " db ")?; + for (i, b) in bytes.into_iter().enumerate() { + if i != 0 { + write!(f, ", ")?; + } + write!(f, "0x{:02x}", b)?; + } + writeln!(f, "")?; + } + ConstDataTyp::AddrOfFunc(name) | + ConstDataTyp::AddrOfConst(name) | + ConstDataTyp::AddrOfStatic(name) => { + writeln!(f, " dq {name}")?; + }, + ConstDataTyp::Variable(..) => unreachable!(), + } + /* + + */ + Ok(()) + } + x(&bytes, f)?; + } + Ok(()) + } + pub fn write_statics(&self, program: &Program, f: &mut File) -> anyhow::Result<()> { + for (name, statc) in &program.static_vars { + writeln!(f, "{name}: ; static")?; + let bytes = get_constant_data_as_bytes(program, &HashMap::new(), statc.val.clone(), false, false)?; + fn x(bytes: &ConstDataTyp, f: &mut File) -> anyhow::Result<()> { + match bytes { + ConstDataTyp::Array(arr) => { + for v in arr { + x(v, f)?; + } + } + ConstDataTyp::Bytes(bytes) => { + write!(f, " db ")?; + for (i, b) in bytes.into_iter().enumerate() { + if i != 0 { + write!(f, ", ")?; + } + write!(f, "0x{:02x}", b)?; + } + writeln!(f, "")?; + } + ConstDataTyp::AddrOfFunc(name) | + ConstDataTyp::AddrOfConst(name) | + ConstDataTyp::AddrOfStatic(name) => { + writeln!(f, " dq {name}")?; + }, + ConstDataTyp::Variable(..) => unreachable!(), + } + /* + + */ + Ok(()) + } + x(&bytes, f)?; + } + Ok(()) + } + pub fn write_literals(&self, program: &Program, f: &mut File) -> anyhow::Result<()> { + fn w(bytes: ConstDataTyp, f: &mut File) -> anyhow::Result<()> { + match bytes { + ConstDataTyp::Array(arr) => { + for v in arr { + w(v, f)?; + } + } + ConstDataTyp::Bytes(bytes) => { + write!(f, " db ")?; + for (i, b) in bytes.into_iter().enumerate() { + if i != 0 { + write!(f, ", ")?; + } + write!(f, "0x{:02x}", b)?; + } + writeln!(f, "")?; + } + ConstDataTyp::AddrOfFunc(name) | + ConstDataTyp::AddrOfConst(name) | + ConstDataTyp::AddrOfStatic(name) => { + writeln!(f, " dq {name}")?; + }, + ConstDataTyp::Variable(name, size) => { + writeln!(f, " resb {size} ; {name}")?; + } + } + Ok(()) + } + + for lit in &program.literals { + writeln!(f, "mcl_lit_{}:", lit.0)?; + w(get_constant_data_as_bytes(program, &HashMap::new(), LocBox::new(&Loc::default(), Expr::Literal(lit.0.clone(), lit.1.clone())), false, false)?, f)?; + } + for lit in &program.struct_literals { + writeln!(f, "mcl_lit_{}:", lit.0)?; + w(get_constant_data_as_bytes(program, &HashMap::new(), LocBox::new(&Loc::default(), Expr::Struct(lit.0.clone(), lit.1.clone())), false, false)?, f)?; + } + Ok(()) + } + +} diff --git a/src/targets/x86_64/asmgen/linux/runtime.s b/src/targets/x86_64/asmgen/linux/runtime.s new file mode 100644 index 0000000..dfe8911 --- /dev/null +++ b/src/targets/x86_64/asmgen/linux/runtime.s @@ -0,0 +1,41 @@ +; generated with godbolt: https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA7AQwFtMQByARg9KtQYEAysib0QXACx8BBAKoBnTAAUAHpwAMvAFYTStJg1AB9U8lJL6yAngGVG6AMKpaAVxYMQAJlIOAMngMmABy7gBGmMQgAMw%2BAA6oCoS2DM5uHt6kCUk2AgFBoSwRUbEWmFa5DEIETMQEae6ePpaY1inVtQT5IeGRMc01dQ0ZA53dhcX9AJQWqK7EyOwcAPQAVADUACoAnnGY69tzxOtoWOsIkZik6yTrtKhM6OuG65iqrHH0AHTrq8sApBoAIL/LzRQLINxnf7RRwKAjoWh4MJfBAw7CAkFgiFQ/YwuEI2yo9GY0HghiQ1zQ2Hw/CCYnRDHAzFJABemGMBHWLG2Cl5olotAgrkCBAAHJznsRgMY0K5BNcRYIJVy%2BQoBbRjAx3NcAG6oPBPVarWrABRTdb/ADsACFMesHes2RyucRMAQYXbgY71m7gHh4ZEnXh2ZLiExVM8FCwIKCvOHVHGLTCACJO/liTXalie%2B2Ov0BghB51h/BRmNx4j4JOW6JpjS5735zD%2BwPHEuupLl2NeeNJGup9YN6JeoE%2Bgtt4Oh13oSNMaM9%2BOzgd1oeNsfN1tF9shl2%2BrgabuVg8r%2Bvr8ctwvF3dhsUO%2BcV3vEMU12tnkd5h0T7dTvfEACc94LpW/6voOw6jp%2B6x4FQ6wQKaspzIItbYEOFpVngb5wR2Uymgo/wAKw2g2BEpuejowXBCFysh6LrFw6FdoOEA4XhhE2lwhFkR%2BTYOpR8HSoh8pcnRXjobOWEsTeBC4dK%2BFEV4XHkXxsECTKNEiYy6zROhB6SaxcnsdESk8RuKlUYJGkoeskjoXe%2BnSbJZrsZIJmjj6/HUUhmmoQR6GAQ505OfJNoEW5pK8eW6z6gYNj0HBUE%2BnGaoanGiWOiAlq9qmTBxhAboyelDqZXGuW9vlEYzFlXhkeVGFVXGQh5cQSQNb26DNbOMxFY6lbNQebXxs1YqDcQzX/lMPUlU%2ByCJr21zHpx83VWwLAkNsaWRZNplQQV8wML67rrtaKYcDMtCcARvCeBwWikKgnCOE6RyLFl0Q8KQBCaGdMwANYgARGj6JwkjXd992cLwCggEDX23WdpBwLASBoCwcR0JE5CUKj6P0FEyDAFwBE%2BDQtDbtDEBhODYSBLU2ycB9NPMMQ2wAPJhNorRwx9qNsIIrMMLQ9Pw6QWBhK4wCOJm0PcLwWAsIYwDiCL%2BBum0uqYDLd1vK0rhFgzvCiuU4NImE4Ys84WDgwQxB4CwBukBrxBhIkmAppgCtGEiRjfTMVAGGaABqeCYAA7qzew3R9/CCCIYjsFIMiCIoKjqCLuhcPoisgKYsr6Mi0OQDMqBxJUMsALSs9EvCoE7ttYIXEAzC0bR2BADjDJ4XA%2BP4gQ9EUfTvVkiTJAInd6Nko8MOMvRRJnLeVB0QwuI0EjNOUXPtIMXR9xMg/z9v48H2Mu%2BzxIzcvQn52XWDIsPRw6yqGKABs5fP5I6zAMgyD0QRXxeHBXAhBbhkgYrwOGWgph/QBkDC6HBQakBunde%2BUMYafV9tfDgXhb7IMhug%2BGUDHaRCSHYSQQA +; removed "qword ptr" and replaced with "qword" +syscall: + mov ecx, edi + mov r11, rdx + movzx eax, sil + xor edi, edi + xor esi, esi + xor edx, edx + xor r10d, r10d + xor r8d, r8d + xor r9d, r9d + test cl, cl + je .L3 + mov rdi, QWORD [r11] + cmp cl, 1 + je .L3 + mov rsi, QWORD [r11+8] + cmp cl, 2 + je .L3 + mov rdx, QWORD [r11+16] + cmp cl, 3 + je .L3 + mov r10, QWORD [r11+24] + cmp cl, 4 + je .L3 + mov r8, QWORD [r11+32] + cmp cl, 5 + je .L3 + mov r9, QWORD [r11+40] +.L3: + syscall + ret +global _start +_start: + xor rax, rax + call main + + mov rdi, rax + mov rax, 60 + syscall diff --git a/src/targets/x86_64/mod.rs b/src/targets/x86_64/mod.rs index 2bacecf..0b7ffad 100644 --- a/src/targets/x86_64/mod.rs +++ b/src/targets/x86_64/mod.rs @@ -1,4 +1,4 @@ -// pub mod asmgen; -pub mod cgen; +pub mod asmgen; +// pub mod cgen; // pub mod qbegen; // pub mod llvmgen; diff --git a/src/validator/calculate_types.rs b/src/validator/calculate_types.rs deleted file mode 100644 index 67621dc..0000000 --- a/src/validator/calculate_types.rs +++ /dev/null @@ -1,59 +0,0 @@ -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(stat) => calc_stat(prog, stat.inner_mut())?, - Ast::Expr(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 6214699..06ea57a 100644 --- a/src/validator/mod.rs +++ b/src/validator/mod.rs @@ -1,30 +1,29 @@ -use std::collections::HashMap; +use std::{collections::HashMap, panic}; use anyhow::bail; -use crate::{common::{Loc, loc::LocBox}, parser::ast::{Ast, Program, Punctuation, TokenType, expr::*, statement::*, typ::Type}}; +use crate::{common::{Loc, loc::LocBox}, parser::ast::{Ast, Program, Punctuation, Scope, TokenType, expr::*, statement::*, typ::Type}, validator::predefined::get_builtin_from_name}; pub mod predefined; -pub mod calculate_types; pub fn validate_code(prog: &mut Program) -> anyhow::Result<()> { - let Block(items) = prog.ast.clone(); + let Block(mut items) = prog.ast.clone(); predefined::load_builtin(prog); - collect_types_and_constants(prog, &items); + collect_types_and_constants(prog, &mut items); check_that_types_exist_for_items(prog, &items)?; //dbg!(&prog.types); //dbg!(&prog.structs); //dbg!(&prog.enums); //dbg!(&prog.member_functions); //dbg!(&prog.functions); - for item in items.iter() { + for item in items.iter_mut() { match item { - Ast::Statement(stat) => validate_stat(prog, &stat, CurrentState::Outside)?, + Ast::Statement(stat) => validate_stat(prog, stat, CurrentState::Outside)?, Ast::Expr(_) => unreachable!() } } - - + + prog.ast.0 = items; Ok(()) } @@ -37,9 +36,9 @@ enum CurrentState { -fn validate_stat(prog: &mut Program, stat: &LocBox, current_state: CurrentState) -> anyhow::Result<()> { - match stat.inner() { - Statement::Fn(func) => validate_fn(prog, &func)?, +fn validate_stat(prog: &mut Program, stat: &mut LocBox, current_state: CurrentState) -> anyhow::Result<()> { + match stat.inner_mut() { + 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"); @@ -55,56 +54,75 @@ fn validate_stat(prog: &mut Program, stat: &LocBox, current_state: Cu Ok(()) } -fn validate_stat_let(prog: &mut Program, lt: &Let) -> anyhow::Result<()> { - if let Some(val) = <.val && let Some(t) = <.typ { - let val_t = validate_expr(prog, &val.inner())?.unwrap(); - if val_t != *t.inner() { - lerror!(t.loc(), "Cannot assign {val_t} to {}", t.inner()); +fn validate_stat_let(prog: &mut Program, lt: &mut Let) -> anyhow::Result<()> { + if let Some(val) = &mut lt.val && let Some(t) = &mut lt.typ { + let val_t = validate_expr(prog, val.inner_mut())?.unwrap(); + if val_t != *t.inner_mut() { + lerror!(t.loc(), "Cannot assign {val_t} to {}", t.inner_mut()); bail!("") } } + if let Some(val) = &mut lt.val && let None = &mut lt.typ { + let Some(t) = validate_expr(prog, val.inner_mut())? else { + lerror!(val.loc(), "Expected a type, go none"); + bail!(""); + }; + lt.typ = Some(LocBox::new(val.loc(), t)); + } + if let Some(scope) = &mut prog.scope { + scope.vars.insert(lt.name.clone(), LocBox::new(&Default::default(), lt.clone())); + } Ok(()) } -fn validate_ast(prog: &mut Program, ast: &Ast) -> anyhow::Result> { +fn validate_ast(prog: &mut Program, ast: &mut Ast) -> anyhow::Result> { match ast { Ast::Expr(expr) => { - validate_expr(prog, &expr.inner()) + validate_expr(prog, expr.inner_mut()) } Ast::Statement(stat) => { - validate_stat(prog, &stat, CurrentState::InFunc)?; + validate_stat(prog, stat, CurrentState::InFunc)?; Ok(None) } } } -pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result> { +pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result> { match expr { Expr::Break | Expr::Continue => Ok(None), Expr::Return(ret) => { - if let Some(expr) = &**ret { - validate_expr(prog, expr.inner()) + if let Some(expr) = &mut**ret { + validate_expr(prog, expr.inner_mut()) } else { Ok(None) } } - Expr::Struct { path, fields } => { + Expr::Struct(id, strct) => { // this is probably fucked so fix this later - let name = path.0.last().expect("Paths always have at least one part"); + let name = strct.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())?; + for field in strct.fields.iter_mut() { + validate_expr(prog, field.1.inner_mut())?; } + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + let mut hasher = DefaultHasher::new(); + (*strct).hash(&mut hasher); + let hash = hasher.finish(); + *id = hash.to_string(); + + prog.struct_literals.insert(id.clone(), strct.clone()); Ok(Some(typ)) }, Expr::If(ifs) => { - validate_expr(prog, ifs.test.inner())?; - for item in &ifs.body.0 { + validate_expr(prog, ifs.test.inner_mut())?; + for item in ifs.body.0.iter_mut() { validate_ast(prog, item)?; } Ok(None) @@ -113,19 +131,42 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result validate_expr(prog, &group.inner()), + Expr::Group(group) => validate_expr(prog, group.inner_mut()), Expr::Call { path, params } => { - let loc = path.loc(); - let Expr::Path(path) = path.inner() else { + let loc = path.loc().clone(); + let Expr::Path(path) = path.inner_mut() else { panic!("fml"); }; - let func; match path.0.len() { 1 => { - let f = prog.functions.get(&path.0[0]); + let f = prog.functions.get(&path.0[0]).cloned(); match f { - Some(f) => func = f.clone(), + Some(func) => { + for (i, param) in params.0.iter_mut().enumerate() { + let ft = func.inner().params[i].1.inner().clone(); + let t = validate_expr(prog, param.inner_mut())?.clone(); + match t { + Some(t) => { + let t = t.get_variable_type(prog)?; + let ft = ft.get_variable_type(prog)?; + if t != ft { + lerror!(param.loc(), "expected {ft:?}, got {t:?}"); + bail!("owo") + } + } + None => { + lerror!(param.loc(), "expected {ft}, got Nothing"); + bail!("nya") + }, + } + } + if let Some(t) = &func.inner().ret_type { + Ok(Some(t.inner().clone())) + } else { + Ok(None) + } + }, None => { lerror!(loc, "Could not find function {}", path); panic!("") @@ -134,9 +175,34 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result { let s = prog.member_functions.get(&path.0[0]).unwrap(); - let f = s.get(&path.0[1]); + let f = s.get(&path.0[1]).cloned(); match f { - Some(f) => func = LocBox::new(loc, f.clone().inner().clone()), + Some(func) => { + for (i, param) in params.0.iter_mut().enumerate() { + let ft = func.inner().params[i].1.inner().clone(); + let t = validate_expr(prog, param.inner_mut())?.clone(); + match t { + Some(t) => { + dbg!(&t); + dbg!(&ft); + if t.get_absolute_value(prog)? != ft.get_absolute_value(prog)? { + lerror!(param.loc(), "expected {ft}, got {}", t); + bail!("") + } + } + None => { + lerror!(param.loc(), "expected {ft}, got Nothing"); + bail!("") + }, + } + } + if let Some(t) = &func.inner().ret_type { + Ok(Some(t.inner().clone())) + } else { + Ok(None) + } + + }, None => { lerror!(loc, "Could not find function {}", path); panic!("") @@ -147,16 +213,41 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result { + let var_t; + let method_name; + match left.inner_mut() { + Expr::FieldAccess { left, right, .. } | + Expr::PtrFieldAccess { left, right, .. } => { + var_t = validate_expr(prog, left.clone().unwrap().inner_mut())?; + let name = validate_expr(prog, right.inner_mut())?; + match name.unwrap() { + Type::Owned(name) => method_name = name, + _ => unreachable!() + } + } + _ => unreachable!() + } + + let Type::Owned(struct_name) = var_t.unwrap().get_absolute_value(prog)? else { + panic!("fml"); + }; + + let mut strct = prog.member_functions.get_mut(&struct_name).unwrap().clone(); + let func = strct.get_mut(&method_name).unwrap(); + + for (i, param) in params.0.iter_mut().enumerate() { + let t = validate_expr(prog, param.inner_mut())?; + let ft = func.inner_mut().params[i].1.inner_mut(); 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())) + if let Some(t) = &mut func.inner_mut().ret_type { + Ok(Some(t.inner_mut().clone())) } else { Ok(None) @@ -164,24 +255,34 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result { 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_expr(prog, test.inner_mut())?; + let _ = validate_expr(prog, on_loop.inner_mut())?; + for item in body.0.iter_mut() { let _ = validate_ast(prog, item)?; } Ok(None) }, Expr::Cast { left, right } => { - validate_expr(prog, left.inner())?; - Ok(Some(right.inner().clone())) + validate_expr(prog, left.inner_mut())?; + Ok(Some(right.inner_mut().clone())) } - Expr::Literal(lit) => { + Expr::Literal(id, lit) => { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + let mut hasher = DefaultHasher::new(); + (*lit).hash(&mut hasher); + let hash = hasher.finish(); + *id = hash.to_string(); + + prog.literals.insert(id.clone(), lit.clone()); + Ok(Some(lit.to_type(prog)?)) } Expr::UnOp { typ, right } => { match typ { Punctuation::Not => { - let t = validate_expr(prog, right.inner())?.unwrap(); + let t = validate_expr(prog, right.inner_mut())?.unwrap().get_absolute_value(prog)?; if !t.is_bool(prog) { lerror!(right.loc(), "Expected bool, got {t}"); } @@ -189,7 +290,7 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result { - let t = validate_expr(prog, right.inner())?.unwrap(); + let t = validate_expr(prog, right.inner_mut())?.unwrap().get_absolute_value(prog)?; if !t.is_numeric(prog) { lerror!(right.loc(), "Expected number, got {t}"); } @@ -197,11 +298,11 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result { - let t = validate_expr(prog, right.inner())?.unwrap(); + let t = validate_expr(prog, right.inner_mut())?.unwrap().get_absolute_value(prog)?; Ok(Some(Type::Ref { inner: Box::new(t), mutable: false })) } Punctuation::Star => { - let t = validate_expr(prog, right.inner())?.unwrap(); + let t = validate_expr(prog, right.inner_mut())?.unwrap().get_absolute_value(prog)?; if !t.is_ptr(prog) { lerror!(right.loc(), "Expected pointer, got {t}"); } @@ -214,7 +315,7 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result unreachable!() } }, - Expr::BinOp { typ, left, right } => { + Expr::BinOp { typ, left, right, signed } => { match typ { Punctuation::Ampersand | Punctuation::Or | @@ -228,23 +329,24 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result { 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}"); + let t1 = validate_expr(prog, left.inner_mut())?.unwrap().get_absolute_value(prog)?; + let t2 = validate_expr(prog, right.inner_mut())?.unwrap().get_absolute_value(prog)?; + if !(t1.is_numeric(prog) || t1.is_ptr(prog)) { + lerror!(right.loc(), "Expected number, got {t1}"); } - if !t2.is_numeric(prog) { - lerror!(right.loc(), "Expected bool, got {t2}"); + if !(t2.is_numeric(prog) || t2.is_ptr(prog)) { + lerror!(right.loc(), "Expected number, got {t2}"); } match &t1 { Type::Builtin { name: _, size, .. } => t1_s = *size, - _ => unreachable!() + _ => unreachable!("1: {t1:?}\n2: {t2:?}") } match &t2 { Type::Builtin { name: _, size, .. } => t2_s = *size, _ => unreachable!() } + *signed = t1.is_signed(prog).expect("verified as numeric"); if t2_s > t1_s { Ok(Some(t2)) } else { @@ -258,14 +360,15 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result { - 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}"); + let t1 = validate_expr(prog, left.inner_mut())?.unwrap().get_absolute_value(prog)?; + let t2 = validate_expr(prog, right.inner_mut())?.unwrap().get_absolute_value(prog)?; + if !(t1.is_numeric(prog) || t1.is_ptr(prog)) { + lerror!(right.loc(), "Expected number or pointer, got {t1}"); } - if !t2.is_bool(prog) { - lerror!(right.loc(), "Expected bool, got {t2}"); + if !(t2.is_numeric(prog) || t2.is_ptr(prog)) { + lerror!(right.loc(), "Expected number or pointer, got {t2}"); } + *signed = t1.is_signed(prog).expect("verified as numeric"); Ok(Some(t1)) } @@ -279,21 +382,22 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result { - let var = validate_expr(prog, left.inner())?.unwrap(); + let var = validate_expr(prog, left.inner_mut())?.unwrap(); let var_t = var.get_absolute_value(prog)?; - let val_t = validate_expr(prog, right.inner())?.unwrap(); + let val_t = validate_expr(prog, right.inner_mut())?.unwrap().get_absolute_value(prog)?; - if !(var_t.is_numeric(prog) && val_t.is_numeric(prog)) { + if !((var_t.is_numeric(prog) || var_t.is_ptr(prog)) && (val_t.is_numeric(prog) || val_t.is_ptr(prog))) { lerror!(left.loc(), "Mismatched types, assigning {val_t} to {var_t}"); bail!(""); } + *signed = var_t.is_signed(prog).expect("verified as numeric"); Ok(None) } Punctuation::Eq => { - let var = validate_expr(prog, left.inner())?.unwrap(); + let var = validate_expr(prog, left.inner_mut())?.unwrap(); let var_t = var.get_absolute_value(prog)?; - let val_t = validate_expr(prog, right.inner())?.unwrap(); + let val_t = validate_expr(prog, right.inner_mut())?.unwrap().get_absolute_value(prog)?; 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}"); @@ -306,7 +410,7 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result { - let left = validate_expr(prog, name.inner())?; + let left = validate_expr(prog, name.inner_mut())?; let Some(left) = left else { lerror!(name.loc(), "expected value, got nothing, cannot index nothing"); bail!("") @@ -332,7 +436,7 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result { let t = prog.types.get(name).cloned(); - if let Some(t) = t { + if let Some(t) = &t { f(prog, t.inner(), t.loc(), index) } else { lerror!(loc, "Unknown type {name}"); @@ -348,38 +452,39 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result { - let left = validate_expr(prog, left.clone().unwrap().inner())?; - let right = validate_expr(prog, right.clone().inner())?.unwrap(); + Expr::PtrFieldAccess { left, right, offset } | + Expr::FieldAccess { left, right, offset } => { + let left = validate_expr(prog, left.clone().unwrap().inner_mut())?; + let right = validate_expr(prog, right.clone().inner_mut())?.unwrap(); let Type::Owned(right) = right else { panic!() }; - match left.unwrap() { + match left.unwrap().get_absolute_value(prog)? { Type::Owned(name) => { if let Some(strct) = prog.structs.get(&name) { - for field in &strct.inner().fields { + for field in strct.inner().fields.iter() { if field.0 == right { + *offset = strct.inner().clone().get_offset_of(prog, &right)?; return Ok(Some(field.1.inner().clone())); } } } } - _ => panic!() + v => panic!("{v:?}"), } Ok(None) }, Expr::WhileLoop { test, body } => { - let _ = validate_expr(prog, test.inner())?; - for item in &body.0 { + let _ = validate_expr(prog, test.inner_mut())?; + for item in body.0.iter_mut() { let _ = validate_ast(prog, item)?; } Ok(None) }, Expr::InfLoop { body } => { - for item in &body.0 { + for item in body.0.iter_mut() { let _ = validate_ast(prog, item)?; } Ok(None) @@ -388,12 +493,14 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result anyhow::Result<()> { +fn validate_fn(prog: &mut Program, func: &mut Function) -> anyhow::Result<()> { + prog.scope = Some(Scope::default()); for param in &func.params { - validate_type(prog, ¶m.1)?; + let t = validate_type(prog, ¶m.1)?; + prog.curr_fn_args.insert(param.0.clone(), LocBox::new(&Loc::default(), t.clone())); } - if let Some(body) = &func.body { - for item in &body.0 { + if let Some(body) = &mut func.body { + for item in body.0.iter_mut() { validate_ast(prog, item)?; } } @@ -401,12 +508,12 @@ fn validate_fn(prog: &mut Program, func: &Function) -> anyhow::Result<()> { Ok(()) } -fn validate_const_var(prog: &mut Program, var: &ConstVar) -> anyhow::Result<()> { +fn validate_const_var(prog: &mut Program, var: &mut ConstVar) -> anyhow::Result<()> { validate_type(prog, &var.typ)?; Ok(()) } -fn validate_static_var(prog: &mut Program, var: &StaticVar) -> anyhow::Result<()> { +fn validate_static_var(prog: &mut Program, var: &mut StaticVar) -> anyhow::Result<()> { validate_type(prog, &var.typ)?; Ok(()) } @@ -416,7 +523,7 @@ fn validate_enum(_: &mut Program, _: &Enum) -> anyhow::Result<()> { Ok(()) } -fn validate_struct(prog: &mut Program, strct: &Struct) -> anyhow::Result<()> { +fn validate_struct(prog: &mut Program, strct: &mut 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); @@ -465,7 +572,7 @@ fn check_that_types_exist_for_items(prog: &mut Program, items: &Vec) -> any } Statement::Enum(_) => (), Statement::Struct(strct) => { - for (name, t) in &strct.fields { + for (name, t) in strct.fields.iter() { if let Err(_) = validate_type(prog, t) { lerror!(t.loc(), "Type '{}', of field, '{}.{name}' does not exist", t.inner(), strct.name); errored = true; @@ -490,23 +597,26 @@ fn check_that_types_exist_for_items(prog: &mut Program, items: &Vec) -> any Ok(()) } -fn validate_type(prog: &mut Program, typ: &LocBox) -> anyhow::Result<()> { - fn f(prog: &mut Program, typ: &Type, loc: &Loc) -> anyhow::Result<()> { +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(()) + Type::Owned(ident) => { + if let Some(builtin) = get_builtin_from_name(&ident.0) { + Ok(builtin) + } else + if prog.enums.get(ident).is_some() || + prog.types.get(ident).is_some() || + prog.structs.get(ident).is_some() { + Ok(typ.clone()) } else { - lerror!(loc, "Could not find type '{}'", typ.0); + lerror!(loc, "Could not find type '{}'", ident.0); bail!("") } } - Type::Builtin { .. } => Ok(()) + Type::Builtin { .. } => Ok(typ.clone()) } } f(prog, typ.inner(), typ.loc()) @@ -514,32 +624,33 @@ fn validate_type(prog: &mut Program, typ: &LocBox) -> anyhow::Result<()> { -fn collect_types_and_constants(prog: &mut Program, items: &Vec) { - for item in items { +fn collect_types_and_constants(prog: &mut Program, items: &mut Vec) { + for item in items.iter_mut() { match item { Ast::Statement(stat) => { - match stat.inner() { + let loc = stat.loc().clone(); + match stat.inner_mut() { Statement::Fn(func)=> { if let Some(struct_name) = &func.struct_name { if let Some(v) = prog.member_functions.get_mut(&struct_name) { - v.insert(func.name.clone(), LocBox::new(stat.loc(), func.clone())); + v.insert(func.name.clone(), LocBox::new(&loc, func.clone())); } else { let mut v = HashMap::new(); - v.insert(func.name.clone(), LocBox::new(stat.loc(), func.clone())); + v.insert(func.name.clone(), LocBox::new(&loc, func.clone())); prog.member_functions.insert(struct_name.clone(), v); } } else { - prog.functions.insert(func.name.clone(), LocBox::new(stat.loc(), func.clone())); + prog.functions.insert(func.name.clone(), LocBox::new(&loc, func.clone())); } } Statement::Enum(enm) => { - prog.enums.insert(enm.name.clone(), LocBox::new(stat.loc(), enm.clone())); + prog.enums.insert(enm.name.clone(), LocBox::new(&loc, enm.clone())); } Statement::Struct(strct) => { - prog.structs.insert(strct.name.clone(), LocBox::new(stat.loc(), strct.clone())); + prog.structs.insert(strct.name.clone(), LocBox::new(&loc, strct.clone())); } Statement::TypeAlias(alias) => { - let typ = alias.clone().typ.inner().clone(); + let typ = alias.clone().typ.inner_mut().clone(); prog.types.insert(alias.name.clone(), LocBox::new(stat.loc(), typ)); } Statement::Let { .. } => (), @@ -551,7 +662,33 @@ fn collect_types_and_constants(prog: &mut Program, items: &Vec) { }, } } - Ast::Expr(_) => unreachable!() + Ast::Expr(expr) => { + match expr.inner_mut() { + Expr::Literal(id, val) => { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + let mut hasher = DefaultHasher::new(); + (*val).hash(&mut hasher); + let hash = hasher.finish(); + *id = hash.to_string(); + + prog.literals.insert(id.clone(), val.clone()); + } + Expr::Struct(id, strct) => { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + let mut hasher = DefaultHasher::new(); + (*strct).hash(&mut hasher); + let hash = hasher.finish(); + *id = hash.to_string(); + + prog.struct_literals.insert(id.clone(), strct.clone()); + } + _ => () + } + } } } diff --git a/src/validator/predefined.rs b/src/validator/predefined.rs index 900abc2..9c7f8c2 100644 --- a/src/validator/predefined.rs +++ b/src/validator/predefined.rs @@ -34,6 +34,13 @@ lazy_static!( ].into(); ); +pub fn get_builtin_from_name(name: &str) -> Option { + if let Some(t) = TYPES_RAW.get(name) { + Some(Type::Builtin { name: name.to_string(), size: t.0 as u8, signed: t.1 }) + } else { + None + } +} pub fn load_builtin(prog: &mut Program) { let loc = Loc::new("(internal)", 0, 0); diff --git a/test b/test new file mode 100755 index 0000000..8e7b7aa Binary files /dev/null and b/test differ diff --git a/test.mcl b/test.mcl index c469b72..ba74081 100644 --- a/test.mcl +++ b/test.mcl @@ -1,21 +1,47 @@ type str = [u8]; + + struct Foo { a: usize, b: &str } -fn Foo.new(a: usize, b: &str) -> Foo { - return Foo { +fn Foo.new(a: usize, b: &str) -> &Foo { + return &Foo { a: a, b: b }; } - -fn main() -> i32 { - let obj = Foo::new(); - +fn print(s: &str) { + // do nothign for now } +fn mul(n: usize, n2: usize) -> usize { + return n * n2; +} +fn main() -> i32 { + let obj = Foo::new(1, "owo"); + obj->b; + let owo = "ahahaha"; + loop { + print(owo); + } + + for (let i = 0; i < 10; i += 1) { + print("nyaaa"); + if (i > 7) { + break; + } else { + continue; + } + print("cant see me!"); + } + while (true) { + mul(1); + } +} + +const FOO: usize = main; diff --git a/test.o b/test.o new file mode 100644 index 0000000..ad674bd Binary files /dev/null and b/test.o differ