no segfault!

This commit is contained in:
Gvidas Juknevičius 2026-01-28 22:30:02 +02:00
parent 1f4645ed24
commit 834b5b1213
21 changed files with 1311 additions and 338 deletions

View File

@ -1,6 +1,6 @@
use std::fmt::{Debug, Display}; use std::fmt::{Debug, Display};
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)] #[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
pub struct Loc { pub struct Loc {
file: String, file: String,
line: usize, 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<T: Clone + Debug> { pub struct LocBox<T: Clone + Debug> {
inner: T, inner: T,
loc: Loc loc: Loc

View File

@ -25,9 +25,8 @@ fn main() -> anyhow::Result<()> {
info!("Parsing {file}"); info!("Parsing {file}");
let mut prog = mclangc::parser::parse_program(tokens)?; let mut prog = mclangc::parser::parse_program(tokens)?;
info!("Validating {file}"); info!("Validating {file}");
dbg!(&prog);
mclangc::validator::validate_code(&mut prog)?; mclangc::validator::validate_code(&mut prog)?;
//dbg!(&prog); // dbg!(&prog.literals);
progs.push((fp, prog)); progs.push((fp, prog));
} }

View File

@ -4,7 +4,7 @@ use crate::{common::loc::LocBox, parser::ast::{Program, literal::Literal}, token
use super::{typ::Type, Ast}; use super::{typ::Type, Ast};
#[derive(Debug, Clone, PartialEq, PartialOrd)] #[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub enum Expr { pub enum Expr {
// Comment(Comment), // Comment(Comment),
Group(Box<LocBox<Expr>>), Group(Box<LocBox<Expr>>),
@ -16,8 +16,9 @@ pub enum Expr {
typ: Punctuation, typ: Punctuation,
left: Box<LocBox<Expr>>, left: Box<LocBox<Expr>>,
right: Box<LocBox<Expr>>, right: Box<LocBox<Expr>>,
signed: bool,
}, },
Literal(super::literal::Literal), Literal(String, super::literal::Literal), // string is id
ArrayIndex { ArrayIndex {
name: Box<LocBox<Expr>>, name: Box<LocBox<Expr>>,
index: Box<LocBox<Expr>>, index: Box<LocBox<Expr>>,
@ -27,20 +28,21 @@ pub enum Expr {
path: Box<LocBox<Expr>>, path: Box<LocBox<Expr>>,
params: CallParams, // LocBox<Expr> ~ (, Expr)* params: CallParams, // LocBox<Expr> ~ (, Expr)*
}, },
//MethodCall { MethodCall {
// var_name: Box<LocBox<Expr>>, left: Box<LocBox<Expr>>,
// method_name: Ident, params: CallParams,
// params: CallParams, },
//},
/// the left side only exists on the /.|->/ chain /// the left side only exists on the /.|->/ chain
FieldAccess { FieldAccess {
left: Box<Option<LocBox<Expr>>>, left: Box<Option<LocBox<Expr>>>,
right: Box<LocBox<Expr>>, right: Box<LocBox<Expr>>,
offset: usize
}, },
PtrFieldAccess { PtrFieldAccess {
left: Box<Option<LocBox<Expr>>>, left: Box<Option<LocBox<Expr>>>,
right: Box<LocBox<Expr>>, right: Box<LocBox<Expr>>,
offset: usize
}, },
ForLoop { ForLoop {
init: Box<Ast>, init: Box<Ast>,
@ -56,10 +58,7 @@ pub enum Expr {
body: Block, body: Block,
}, },
If(IfExpr), If(IfExpr),
Struct { Struct(String, StructLit),
path: Path,
fields: BTreeMap<Ident, LocBox<Expr>>,
},
Return(Box<Option<LocBox<Expr>>>), Return(Box<Option<LocBox<Expr>>>),
Break, Break,
Continue, Continue,
@ -69,6 +68,12 @@ pub enum Expr {
}, },
} }
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd)]
pub struct StructLit {
pub path: Path,
pub fields: BTreeMap<Ident, LocBox<Expr>>,
}
impl Expr { impl Expr {
pub fn unwrap_path(&self) -> Path { pub fn unwrap_path(&self) -> Path {
let Expr::Path(p) = self else {panic!("Unwrapping")}; let Expr::Path(p) = self else {panic!("Unwrapping")};
@ -76,7 +81,7 @@ impl Expr {
} }
pub fn as_number(&self, prog: &Program) -> Option<Number> { pub fn as_number(&self, prog: &Program) -> Option<Number> {
match self { match self {
Self::Literal(Literal::Number(num)) => Some(*num), Self::Literal(_, Literal::Number(num)) => Some(*num),
Self::Path(Path(path)) => { Self::Path(Path(path)) => {
if let Some(val) = prog.get_const_var(path.last().unwrap()) { if let Some(val) = prog.get_const_var(path.last().unwrap()) {
val.1.inner().val.inner().as_number(prog) 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<LocBox<Expr>>); pub struct CallParams(pub Vec<LocBox<Expr>>);
#[derive(Debug, Clone, PartialEq, PartialOrd)] #[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub struct Block(pub Vec<Ast>); pub struct Block(pub Vec<Ast>);
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Hash)] #[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 struct IfExpr {
pub test: Box<LocBox<Expr>>, pub test: Box<LocBox<Expr>>,
pub body: Block, pub body: Block,
pub else_if: Option<IfBranchExpr> pub else_if: Option<IfBranchExpr>
} }
#[derive(Debug, Clone, PartialEq, PartialOrd)] #[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub enum IfBranchExpr { pub enum IfBranchExpr {
ElseIf(Box<IfExpr>), ElseIf(Box<IfExpr>),
Else(Block) Else(Block)

View File

@ -3,13 +3,14 @@ use crate::{common::{Loc, loc::LocBox}, parser::ast::{Program, typ::Type}, token
use super::expr::Expr; use super::expr::Expr;
#[derive(Debug, Clone, PartialEq, PartialOrd)] #[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub enum Literal { pub enum Literal {
Number(Number), Number(Number),
Ident(Ident), Ident(Ident),
String(TString), String(TString),
Char(Char), Char(Char),
Array(Vec<LocBox<Expr>>), Array(Vec<LocBox<Expr>>),
Bool(bool),
ArrayRepeat { ArrayRepeat {
val: Box<LocBox<Expr>>, val: Box<LocBox<Expr>>,
count: usize, count: usize,
@ -19,26 +20,31 @@ pub enum Literal {
impl Literal { impl Literal {
pub fn to_type(&self, prog: &mut Program) -> anyhow::Result<Type> { pub fn to_type(&self, prog: &mut Program) -> anyhow::Result<Type> {
let t = match self { 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::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::Char(_) => Type::Owned(Ident(String::from("char"))),
Self::Bool(_) => Type::Owned(Ident(String::from("bool"))),
Self::Array(arr) => { Self::Array(arr) => {
if arr.is_empty() { if arr.is_empty() {
Type::SizedArray { Type::SizedArray {
inner: Box::new(Type::Builtin { name: "void".to_string(), size: 0, signed: false }), 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 { } else {
let item = arr.first().unwrap(); let item = arr.first().unwrap();
let loc = item.loc().clone();
let mut item = item.inner().clone();
Type::SizedArray { Type::SizedArray {
inner: Box::new(validate_expr(prog, item.inner())?.unwrap()), inner: Box::new(validate_expr(prog, &mut item)?.unwrap()),
count: LocBox::new(item.loc(), Expr::Literal(Literal::Number(Number { val: arr.len(), base: 10, signed: false }))) count: LocBox::new(&loc, Expr::Literal(String::new(), Literal::Number(Number { val: arr.len(), base: 10, signed: false })))
} }
} }
} }
Self::ArrayRepeat { val, count } => { 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 })))}
} }
}; };

View File

@ -2,7 +2,7 @@ use std::collections::HashMap;
use statement::{ConstVar, Enum, Function, StaticVar, Struct}; 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 use crate::tokeniser::tokentype::*;
pub mod expr; pub mod expr;
@ -11,7 +11,7 @@ pub mod statement;
pub mod typ; pub mod typ;
#[derive(Debug, Clone)] #[derive(Debug, Clone, Default)]
pub struct Scope { pub struct Scope {
pub vars: HashMap<Ident, LocBox<Let>>, pub vars: HashMap<Ident, LocBox<Let>>,
pub static_vars: HashMap<Ident, StaticVar>, pub static_vars: HashMap<Ident, StaticVar>,
@ -29,7 +29,10 @@ pub struct Program {
pub member_functions: HashMap<Ident, HashMap<Ident, LocBox<Function>>>, pub member_functions: HashMap<Ident, HashMap<Ident, LocBox<Function>>>,
pub static_vars: HashMap<Ident, StaticVar>, pub static_vars: HashMap<Ident, StaticVar>,
pub const_vars: HashMap<Ident, ConstVar>, pub const_vars: HashMap<Ident, ConstVar>,
pub scope: Option<Scope> pub literals: HashMap<String, Literal>, // string is id of literal
pub struct_literals: HashMap<String, StructLit>, // string is id of literal
pub scope: Option<Scope>,
pub curr_fn_args: HashMap<Ident, LocBox<Type>>
} }
@ -102,7 +105,7 @@ impl Program {
} }
#[derive(Debug, Clone, PartialEq, PartialOrd)] #[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub enum Ast { pub enum Ast {
Expr(LocBox<expr::Expr>), Expr(LocBox<expr::Expr>),
Statement(LocBox<statement::Statement>), Statement(LocBox<statement::Statement>),

View File

@ -1,12 +1,14 @@
use std::collections::HashMap;
use anyhow::bail; 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}; 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 { pub enum Statement {
Fn(Function), Fn(Function),
TypeAlias(TypeAlias), TypeAlias(TypeAlias),
@ -18,45 +20,45 @@ pub enum Statement {
} }
#[derive(Debug, Clone, PartialEq, PartialOrd)] #[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub struct Let { pub struct Let {
pub name: Ident, pub name: Ident,
pub typ: Option<LocBox<Type>>, pub typ: Option<LocBox<Type>>,
pub val: Option<LocBox<Expr>>, pub val: Option<LocBox<Expr>>,
} }
#[derive(Debug, Clone, PartialEq, PartialOrd)] #[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub struct ConstVar { pub struct ConstVar {
pub name: Ident, pub name: Ident,
pub typ: LocBox<Type>, pub typ: LocBox<Type>,
pub val: LocBox<Expr>, pub val: LocBox<Expr>,
} }
#[derive(Debug, Clone, PartialEq, PartialOrd)] #[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub struct StaticVar { pub struct StaticVar {
pub name: Ident, pub name: Ident,
pub typ: LocBox<Type>, pub typ: LocBox<Type>,
pub val: LocBox<Expr>, pub val: LocBox<Expr>,
} }
#[derive(Debug, Clone, PartialEq, PartialOrd)] #[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub struct TypeAlias { pub struct TypeAlias {
pub name: Ident, pub name: Ident,
pub typ: LocBox<Type>, pub typ: LocBox<Type>,
} }
#[derive(Debug, Clone, PartialEq, PartialOrd)] #[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub struct Struct { pub struct Struct {
pub name: Ident, pub name: Ident,
pub fields: Vec<(Ident, LocBox<Type>)>, pub fields: Vec<(Ident, LocBox<Type>)>,
} }
#[derive(Debug, Clone, PartialEq, PartialOrd)] #[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub struct Enum { pub struct Enum {
pub name: Ident, pub name: Ident,
pub fields: Vec<Ident>, pub fields: Vec<Ident>,
} }
#[derive(Debug, Clone, PartialEq, PartialOrd)] #[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub struct Function { pub struct Function {
pub struct_name: Option<Ident>, pub struct_name: Option<Ident>,
pub name: Ident, pub name: Ident,
@ -125,49 +127,51 @@ impl Struct {
} }
} }
#[derive(Debug, Clone, PartialEq, PartialOrd)] #[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub enum ConstDataTyp { pub enum ConstDataTyp {
Byte(u8), Bytes(Vec<u8>),
AddrOfConst(Ident), AddrOfConst(Ident),
AddrOfStatic(Ident), AddrOfStatic(Ident),
AddrOfFunc(Ident), AddrOfFunc(Ident),
Variable(Ident, usize),
Array(Vec<ConstDataTyp>)
} }
pub fn get_constant_data_as_bytes(program: &Program, value: LocBox<Expr>, is_little_endian: bool, must_be_number: bool) -> anyhow::Result<Vec<ConstDataTyp>> { pub fn get_constant_data_as_bytes(program: &Program, struct_items: &HashMap<Ident, usize>, value: LocBox<Expr>, is_big_endian: bool, must_be_number: bool) -> anyhow::Result<ConstDataTyp> {
match value.inner() { match value.inner() {
Expr::Literal(lit) => { Expr::Literal(_, lit) => {
match lit { match lit {
Literal::Char(Char(c)) => { Literal::Char(Char(c)) => {
if must_be_number { if must_be_number {
lerror!(value.loc(), "Expected number got char"); lerror!(value.loc(), "Expected number got char");
bail!("") bail!("")
} }
Ok(vec![ConstDataTyp::Byte(*c as u8)]) Ok(ConstDataTyp::Bytes(vec![*c as u8]))
}, },
Literal::String(TString { val, cstr }) => { Literal::String(TString { val, cstr }) => {
if must_be_number { if must_be_number {
lerror!(value.loc(), "Expected number got string"); lerror!(value.loc(), "Expected number got string");
bail!("") bail!("")
} }
let mut val = val.chars().into_iter().map(|f| ConstDataTyp::Byte(f as u8)).collect::<Vec<_>>(); let mut val = val.chars().into_iter().map(|v| v as u8).collect::<Vec<u8>>();
if *cstr { if *cstr {
val.push(ConstDataTyp::Byte(0)); val.push(0);
} }
Ok(val) Ok(ConstDataTyp::Bytes(val))
} }
Literal::Number(Number { val, base: _, signed: _ }) => { Literal::Number(Number { val, base: _, signed: _ }) => {
let mut buf = Vec::new(); let mut buf = Vec::new();
let mut inc = 0; let mut inc = 0;
while inc < 8 { while inc < 8 {
buf.push(ConstDataTyp::Byte(((val << 8*inc) & 0xff) as u8)); buf.push(((val << 8*inc) & 0xff) as u8);
inc += 1; inc += 1;
} }
if is_little_endian { if is_big_endian {
buf.reverse(); buf.reverse();
} }
Ok(buf) Ok(ConstDataTyp::Bytes(buf))
} }
Literal::Array(arr) => { Literal::Array(arr) => {
if must_be_number { if must_be_number {
@ -176,84 +180,89 @@ pub fn get_constant_data_as_bytes(program: &Program, value: LocBox<Expr>, is_lit
} }
let mut bytes = Vec::new(); let mut bytes = Vec::new();
if arr.len() < 1 { if arr.len() < 1 {
return Ok(vec![]); return Ok(ConstDataTyp::Bytes(vec![]));
} }
for item in arr { for item in arr {
let mut data = get_constant_data_as_bytes(program, item.clone(), is_little_endian, must_be_number)?; let data = get_constant_data_as_bytes(program, struct_items, item.clone(), is_big_endian, must_be_number)?;
bytes.append(&mut data); bytes.push(data);
} }
Ok(bytes) Ok(ConstDataTyp::Array(bytes))
} }
Literal::ArrayRepeat { val, count } => { Literal::ArrayRepeat { val, count } => {
if must_be_number { if must_be_number {
lerror!(value.loc(), "Expected number got repeating array"); lerror!(value.loc(), "Expected number got repeating array");
bail!("") 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 num = Vec::new();
let mut inc = 0; let mut inc = 0;
while inc < 8 { while inc < 8 {
num.push(ConstDataTyp::Byte(((count << 8*inc) & 0xff) as u8)); num.push(((count << 8*inc) & 0xff) as u8);
inc += 1; inc += 1;
} }
if is_little_endian { if is_big_endian {
num.reverse(); num.reverse();
} }
let mut count = 0 as usize; let mut count = 0 as usize;
for b in num { for b in num {
let ConstDataTyp::Byte(b) = b else {unreachable!()};
count = b as usize; count = b as usize;
count <<= 8; count <<= 8;
} }
let orig = val.clone(); let orig = val.clone();
let mut arr = Vec::new();
for _ in 0..count { for _ in 0..count {
val.append(&mut orig.clone()); arr.push(orig.clone());
} }
Ok(val) Ok(ConstDataTyp::Array(arr))
} }
Literal::Ident(name) => { Literal::Bool(v) => {
Ok(ConstDataTyp::Bytes(vec![*v as u8]))
}
Literal::Ident(_) => 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) { 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)?) 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) { } else if let Some(_) = program.static_vars.get(name) {
lerror!(value.loc(), "Statics cannot be passed by value, use a reference"); lerror!(value.loc(), "Statics cannot be passed by value, use a reference");
bail!("") bail!("")
} else if let Some(_) = program.functions.get(name) { } else if let Some(_) = program.functions.get(name) {
Ok(vec![ConstDataTyp::AddrOfFunc(name.clone())]) Ok(ConstDataTyp::AddrOfFunc(name.clone()))
} else if let Some(size) = struct_items.get(name) {
Ok(ConstDataTyp::Variable(name.clone(), *size))
} else { } else {
lerror!(value.loc(), "Unable to find ident '{name}'"); lerror!(value.loc(), "Unable to find ident '{name}'");
bail!("") bail!("")
} }
}
//Literal::Struct { name, fields } => {
// if must_be_number {
// lerror!(value.loc(), "Expected number got struct literal");
// bail!("")
// }
// let Some(strct) = program.structs.get(name) else {
// lerror!(value.loc(), "Could not find struct {name}");
// bail!("")
// };
//
// let mut strct_fields = HashMap::new();
// for (name, typ) in &strct.inner().fields {
// strct_fields.insert(name, typ);
// }
//
// for (name, val) in fields {
// if let Some(_fld) = strct_fields.get(name) {
// // TODO: Actually check if the fields are the right type
//
// }
// }
// todo!()
//}
} }
} v => unreachable!("{v:?}")
_ => unreachable!()
} }
} }

View File

@ -2,11 +2,11 @@ use std::fmt::Display;
use anyhow::bail; 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}; use super::{expr::Expr, literal::Literal, Ident, Program};
#[derive(Debug, Clone, PartialEq, PartialOrd)] #[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub enum Type { pub enum Type {
Ref { Ref {
inner: Box<Type>, inner: Box<Type>,
@ -32,12 +32,44 @@ impl Type {
match self { match self {
Self::Ref { inner, .. } => inner.get_absolute_value(program), Self::Ref { inner, .. } => inner.get_absolute_value(program),
Self::Owned(ident) => { 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<Self> {
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) { 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()) Ok(var.1.inner().typ.clone().expect("type should be computed if were already using it").inner().clone())
} else { } else {
bail!(""); bail!("owo? missing value: {ident}");
} }
} }
Self::Ref { .. } |
Self::SizedArray { .. } | Self::SizedArray { .. } |
Self::Builtin { .. } | Self::Builtin { .. } |
Self::UnsizedArray { .. } => Ok(self.clone()), Self::UnsizedArray { .. } => Ok(self.clone()),
@ -69,6 +101,40 @@ impl Type {
} }
} }
} }
// Returns None if non numeric
pub fn is_signed(&self, program: &Program) -> Option<bool> {
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 { pub fn is_ptr(&self, program: &Program) -> bool {
match self { match self {
@ -96,26 +162,31 @@ impl Type {
} }
} }
pub fn size_of(&self, program: &Program) -> anyhow::Result<usize> { pub fn size_of(&self, program: &Program) -> anyhow::Result<usize> {
match self { fn f(t: &Type, program: &Program, behind_a_ptr: bool) -> anyhow::Result<usize> {
Self::Ref { .. } => { match t {
Type::Ref { .. } => {
// TODO: Use the actual ptr size // TODO: Use the actual ptr size
Ok(size_of::<*const ()>()) Ok(size_of::<*const ()>())
} }
Self::UnsizedArray { .. } => { Type::UnsizedArray { .. } => {
bail!("Unsized arrays dont have a known size and must be behind a pointer") if behind_a_ptr {
Ok(size_of::<*const ()>())
} else {
bail!("Unsized arrays dont have a known size and must be behind a pointer");
} }
Self::SizedArray { inner, .. } => { }
Type::SizedArray { inner, .. } => {
Ok(inner.size_of(program)? ) Ok(inner.size_of(program)? )
} }
Self::Builtin { size, .. } => { Type::Builtin { size, .. } => {
Ok(*size as usize) Ok(*size as usize)
} }
Self::Owned(name) => { Type::Owned(name) => {
if let Some(v) = program.structs.get(&name) { if let Some(v) = program.structs.get(&name) {
return Ok(v.inner().get_size(program)?); return Ok(v.inner().get_size(program)?);
} }
if let Some(v) = program.types.get(&name) { if let Some(v) = program.types.get(&name) {
return Ok(v.inner().size_of(program)?); return Ok(f(v.inner(), program, behind_a_ptr)?);
} }
if let Some(_) = program.enums.get(&name) { if let Some(_) = program.enums.get(&name) {
return Ok(4); // TODO: Make enum size changeable return Ok(4); // TODO: Make enum size changeable
@ -124,6 +195,9 @@ impl Type {
} }
} }
} }
f(self, program, false)
}
pub fn size_of_allow_unsized_arrays(&self, program: &Program) -> anyhow::Result<usize> { pub fn size_of_allow_unsized_arrays(&self, program: &Program) -> anyhow::Result<usize> {
match self.size_of(program) { match self.size_of(program) {
Ok(v) => Ok(v), Ok(v) => Ok(v),
@ -155,7 +229,7 @@ impl Display for Type {
Expr::Path(p) => { Expr::Path(p) => {
write!(f, "[{inner}; {p}]") write!(f, "[{inner}; {p}]")
} }
Expr::Literal(Literal::Number(n)) => { Expr::Literal(_, Literal::Number(n)) => {
write!(f, "[{inner}; {n}]") write!(f, "[{inner}; {n}]")
} }
_ => unreachable!() _ => unreachable!()

View File

@ -2,7 +2,7 @@ use std::collections::{BTreeMap, HashMap};
use anyhow::{bail, Result}; 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}; 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<Token>, precedence: usize, consume_semi: bool
} else } else
if let Some(_) = utils::check(tokens, TokenType::ident("")) { if let Some(_) = utils::check(tokens, TokenType::ident("")) {
let p = parse_path(tokens)?; 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())?) Some(parse_struct_literal(tokens, p.inner().unwrap_path())?)
} else { } else {
Some(p) Some(p)
@ -63,6 +63,8 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
TokenType::number(0, 0, false), TokenType::number(0, 0, false),
TokenType::char('\0'), TokenType::char('\0'),
TokenType::Delim(Delimiter::SquareL), TokenType::Delim(Delimiter::SquareL),
TokenType::Keyword(Keyword::True),
TokenType::Keyword(Keyword::False)
]) { ]) {
Some(parse_literal(tokens)?) Some(parse_literal(tokens)?)
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::While)) { } else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::While)) {
@ -74,8 +76,10 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Return)) { } else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Return)) {
return Ok(Some(parse_return(tokens)?)); return Ok(Some(parse_return(tokens)?));
} else if let Some(kw) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Break)) { } 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))); return Ok(Some(LocBox::new(kw.loc(), Expr::Break)));
} else if let Some(kw) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Continue)) { } 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))); return Ok(Some(LocBox::new(kw.loc(), Expr::Continue)));
} else if let Some(kw) = utils::check(tokens, TokenType::Keyword(Keyword::If)) { } 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)?)))); return Ok(Some(LocBox::new(&kw.loc().clone(), Expr::If(parse_if(tokens)?))));
@ -102,7 +106,11 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
} }
if let Some(_) = utils::check_from_many(tokens, BINOP_LIST) { 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 { } else {
if consume_semi { if consume_semi {
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?; _ = 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<Token>, precedence: usize, consume_semi: bool
return Ok(Some(res)); 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) Ok(res)
} }
@ -129,10 +140,12 @@ fn parse_cast(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr
} }
fn parse_if(tokens: &mut Vec<Token>) -> Result<IfExpr> { fn parse_if(tokens: &mut Vec<Token>) -> Result<IfExpr> {
let loc = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::If), "")?; 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 { let Some(test) = parse_expr(tokens, 0, false)? else {
lerror!(loc.loc(), "Expected test for if statement, got nothing"); lerror!(loc.loc(), "Expected test for if statement, got nothing");
bail!("") bail!("")
}; };
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
let block = if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) { let block = if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
if let Some(_) = utils::check_2_last(tokens, TokenType::Delim(Delimiter::CurlyR)) { if let Some(_) = utils::check_2_last(tokens, TokenType::Delim(Delimiter::CurlyR)) {
_ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR)); _ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR));
@ -170,10 +183,12 @@ fn parse_if(tokens: &mut Vec<Token>) -> Result<IfExpr> {
} }
fn parse_while_loop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> { fn parse_while_loop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::While), "")?; 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 { let Some(test) = parse_expr(tokens, 0, false)? else {
lerror!(kw.loc(), "Expected test comparrison for while loop, got nothing"); lerror!(kw.loc(), "Expected test comparrison for while loop, got nothing");
bail!("") bail!("")
}; };
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
let block = parse_block(tokens)?; let block = parse_block(tokens)?;
Ok(LocBox::new(kw.loc(), Expr::WhileLoop { Ok(LocBox::new(kw.loc(), Expr::WhileLoop {
test: Box::new(test), test: Box::new(test),
@ -182,6 +197,7 @@ fn parse_while_loop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
} }
fn parse_for_loop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> { fn parse_for_loop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::For), "")?; 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 { let Some(pre) = parse_item(tokens)? else {
lerror!(kw.loc(), "Expected init stat for a for loop, got nothing"); lerror!(kw.loc(), "Expected init stat for a for loop, got nothing");
bail!("") bail!("")
@ -196,6 +212,7 @@ fn parse_for_loop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
lerror!(kw.loc(), "Expected post expression (usually an index increment) for a for loop, got nothing"); lerror!(kw.loc(), "Expected post expression (usually an index increment) for a for loop, got nothing");
bail!("") bail!("")
}; };
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
let block = parse_block(tokens)?; let block = parse_block(tokens)?;
Ok(LocBox::new(kw.loc(), Expr::ForLoop { Ok(LocBox::new(kw.loc(), Expr::ForLoop {
@ -211,6 +228,15 @@ fn parse_inf_loop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
Ok(LocBox::new(kw.loc(), Expr::InfLoop { body: block })) Ok(LocBox::new(kw.loc(), Expr::InfLoop { body: block }))
} }
fn parse_fn_call(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr>> { fn parse_fn_call(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr>> {
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 start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
let mut params = Vec::new(); let mut params = Vec::new();
@ -257,7 +283,33 @@ fn parse_field_access(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<Loc
}; };
Ok(LocBox::new(start.loc(), Expr::FieldAccess { Ok(LocBox::new(start.loc(), Expr::FieldAccess {
left: Box::new(Some(left)), left: Box::new(Some(left)),
right: Box::new(right) right: Box::new(right),
offset: 0
}))
}
fn parse_member_function_call(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr>> {
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<Token>, left: LocBox<Expr>) -> Result
}; };
Ok(LocBox::new(start.loc(), Expr::PtrFieldAccess { Ok(LocBox::new(start.loc(), Expr::PtrFieldAccess {
left: Box::new(Some(left)), left: Box::new(Some(left)),
right: Box::new(right) right: Box::new(right),
offset: 0
})) }))
} }
fn parse_literal(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> { fn parse_literal(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
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)) { if let Some(tkn) = utils::check_consume(tokens, TokenType::string("", false)) {
let TokenType::String(str) = tkn.tt() else {unreachable!()}; 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 } else
if let Some(tkn) = utils::check_consume(tokens, TokenType::number(0, 0, false)) { if let Some(tkn) = utils::check_consume(tokens, TokenType::number(0, 0, false)) {
let TokenType::Number(val) = tkn.tt() else {unreachable!()}; 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 } else
if let Some(tkn) = utils::check_consume(tokens, TokenType::char('\0')) { if let Some(tkn) = utils::check_consume(tokens, TokenType::char('\0')) {
let TokenType::Char(val) = tkn.tt() else {unreachable!()}; 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 } else
if let Some(start) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareL)) { if let Some(start) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareL)) {
if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareR)) { 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) { if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Comma) {
let first = parse_expr(tokens, 0, false)?; let first = parse_expr(tokens, 0, false)?;
@ -310,7 +369,7 @@ fn parse_literal(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
} }
} }
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?; 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) { } else if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Semi) {
let Some(typ) = parse_expr(tokens, 0, true)? else { let Some(typ) = parse_expr(tokens, 0, true)? else {
lerror!(start.loc(), "Expected value, found nothing"); lerror!(start.loc(), "Expected value, found nothing");
@ -318,12 +377,12 @@ fn parse_literal(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
}; };
let count = parse_expr(tokens, 0, false)?.unwrap(); 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"); lerror!(count.loc(), "a repeating array accepts only literal numbers for count argument");
bail!("") bail!("")
}; };
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?; 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), val: Box::new(typ),
count: count.val count: count.val
}))); })));
@ -348,7 +407,7 @@ fn parse_struct_literal(tokens: &mut Vec<Token>, name: Path) -> Result<LocBox<Ex
} }
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?; 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), "wtf?")?;
let typ = parse_expr(tokens, 0, false)?.unwrap(); let typ = parse_expr(tokens, 0, false)?.unwrap();
fields.insert(name.tt().unwrap_ident(), typ); fields.insert(name.tt().unwrap_ident(), typ);
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) { if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
@ -356,7 +415,7 @@ fn parse_struct_literal(tokens: &mut Vec<Token>, name: Path) -> Result<LocBox<Ex
break; break;
} }
} }
Ok(LocBox::new(start.loc(), Expr::Struct { path: name, fields })) Ok(LocBox::new(start.loc(), Expr::Struct(String::new(), StructLit { path: name, fields })))
} }
fn parse_group(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> { fn parse_group(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
@ -442,7 +501,8 @@ fn parse_binop(tokens: &mut Vec<Token>, mut lhs: LocBox<Expr>, precedence: usize
lhs = LocBox::new(&op_loc, Expr::BinOp { lhs = LocBox::new(&op_loc, Expr::BinOp {
typ: op, typ: op,
left: Box::new(lhs), left: Box::new(lhs),
right: Box::new(rhs) right: Box::new(rhs),
signed: false
}); });
} }

View File

@ -33,7 +33,10 @@ pub fn parse_program(mut tokens: Vec<Token>) -> Result<Program> {
structs: HashMap::new(), structs: HashMap::new(),
static_vars: HashMap::new(), static_vars: HashMap::new(),
const_vars: HashMap::new(), const_vars: HashMap::new(),
scope: None literals: HashMap::new(),
struct_literals: HashMap::new(),
scope: None,
curr_fn_args: HashMap::new()
}) })
} }

View File

@ -37,6 +37,7 @@ pub fn parse_statement(tokens: &mut Vec<Token>) -> Result<Option<LocBox<Statemen
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Let)) { if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Let)) {
Ok(Some(parse_let(tokens)?)) Ok(Some(parse_let(tokens)?))
} else { } else {
Ok(None) Ok(None)
} }
} }
@ -200,7 +201,7 @@ fn parse_fn_params(tokens: &mut Vec<Token>) -> Result<Vec<(Ident, LocBox<Type>)>
break; break;
} }
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?; 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); //dbg!(&name);
let typ = parse_type(tokens)?; let typ = parse_type(tokens)?;
args.push((name.tt().unwrap_ident(), typ)); args.push((name.tt().unwrap_ident(), typ));

View File

@ -28,7 +28,7 @@ pub fn parse_type(tokens: &mut Vec<Token>) -> Result<LocBox<Type>> {
let count = parse_expr(tokens, 0, false)?.unwrap(); let count = parse_expr(tokens, 0, false)?.unwrap();
match count.inner() { match count.inner() {
Expr::Literal(Literal::Number(_)) | Expr::Literal(_, Literal::Number(_)) |
Expr::Path(_) => (), Expr::Path(_) => (),
_ => { _ => {
lerror!(count.loc(), "Only literal numbers are allowed in sized arrays"); lerror!(count.loc(), "Only literal numbers are allowed in sized arrays");

View File

@ -1,31 +1,32 @@
use std::{fs::File, path::{Path, PathBuf}}; use std::{fs::File, path::{Path, PathBuf}};
use crate::{cli::CliArgs, parser::ast::Program}; use crate::{cli::CliArgs, parser::ast::Program};
mod x86_64; pub mod x86_64;
//mod none; //mod none;
pub fn get_default_target() -> String { 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> { pub fn get_all_targets() -> Vec<&'static str> {
vec![ 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::cgen::linux::CGen::get_target_triple(),
// x86_64::llvmgen::linux::LlvmGen::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(), // x86_64::cgen::linux::CGen::get_target_triple(),
] ]
} }
fn get_target_from_triple(triple: &str) -> Box<dyn Target> { fn get_target_from_triple(triple: &str) -> Box<dyn Target> {
match triple { match triple {
// _ if triple == x86_64::asmgen::linux::AsmGen::get_target_triple() => { _ if triple == x86_64::asmgen::linux::AsmGen::get_target_triple() => {
// Box::new(x86_64::asmgen::linux::AsmGen::new()) 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::cgen::linux::CGen::get_target_triple() => {
// Box::new(x86_64::cgen::linux::CGen::new())
// }
// _ if triple == x86_64::qbegen::linux::QbeGen::get_target_triple() => { // _ if triple == x86_64::qbegen::linux::QbeGen::get_target_triple() => {
// Box::new(x86_64::qbegen::linux::QbeGen::new()) // Box::new(x86_64::qbegen::linux::QbeGen::new())
// } // }

View File

@ -1,7 +1,9 @@
use crate::targets::Target; 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::io::Write; use std::{collections::HashMap, fs::File, io::Write};
pub struct AsmGen; pub struct AsmGen;
const RUNTIME_CODE: &'static str = include_str!("./runtime.s");
impl Target for AsmGen { impl Target for AsmGen {
fn new() -> Self { fn new() -> Self {
Self {} Self {}
@ -12,20 +14,672 @@ impl Target for AsmGen {
fn get_int_ext(&self) -> &'static str { fn get_int_ext(&self) -> &'static str {
"s" "s"
} }
fn write_code(&mut self, program: &crate::parser::ast::Program, path: &std::path::Path) -> anyhow::Result<()> { fn write_code(&mut self, program: &crate::parser::ast::Program, f: &mut File) -> anyhow::Result<()> {
let mut f = std::fs::File::open(path)?; writeln!(f, "bits 64")?;
writeln!(f, "global _start")?;
writeln!(f, "section .text")?; writeln!(f, "section .text")?;
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 .rodata")?;
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<()> {
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::<Vec<String>>().join(" "));
cmd.output()?;
Ok(())
}
fn link(&mut self, from: Vec<std::path::PathBuf>, 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::<Vec<String>>().join(" "));
cmd.output()?;
Ok(())
}
}
// also used for args
#[derive(Debug, Clone)]
pub enum VarMapT {
Stack(usize, Type),
}
pub struct FunctionCtx {
vars: HashMap<Ident, VarMapT>,
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<u8> = 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(&param.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<IfBranchExpr>, 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(&lt.name, typ.size_of(program)?, typ.clone());
if let Some(value) = &lt.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 { for (name, constant) in &program.const_vars {
writeln!(f, "{name}:")?; 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(()) Ok(())
} }
fn compile(&mut self, _from: &std::path::Path, _to: &std::path::Path) -> anyhow::Result<()> { 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(()) Ok(())
} }
fn link(&mut self, _from: Vec<std::path::PathBuf>, _to: &std::path::Path) -> anyhow::Result<()> { x(&bytes, f)?;
}
Ok(()) 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(())
}
} }

View File

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

View File

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

View File

@ -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(())
}

View File

@ -1,30 +1,29 @@
use std::collections::HashMap; use std::{collections::HashMap, panic};
use anyhow::bail; 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 predefined;
pub mod calculate_types;
pub fn validate_code(prog: &mut Program) -> anyhow::Result<()> { 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); 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)?; check_that_types_exist_for_items(prog, &items)?;
//dbg!(&prog.types); //dbg!(&prog.types);
//dbg!(&prog.structs); //dbg!(&prog.structs);
//dbg!(&prog.enums); //dbg!(&prog.enums);
//dbg!(&prog.member_functions); //dbg!(&prog.member_functions);
//dbg!(&prog.functions); //dbg!(&prog.functions);
for item in items.iter() { for item in items.iter_mut() {
match item { match item {
Ast::Statement(stat) => validate_stat(prog, &stat, CurrentState::Outside)?, Ast::Statement(stat) => validate_stat(prog, stat, CurrentState::Outside)?,
Ast::Expr(_) => unreachable!() Ast::Expr(_) => unreachable!()
} }
} }
prog.ast.0 = items;
Ok(()) Ok(())
} }
@ -37,9 +36,9 @@ enum CurrentState {
fn validate_stat(prog: &mut Program, stat: &LocBox<Statement>, current_state: CurrentState) -> anyhow::Result<()> { fn validate_stat(prog: &mut Program, stat: &mut LocBox<Statement>, current_state: CurrentState) -> anyhow::Result<()> {
match stat.inner() { match stat.inner_mut() {
Statement::Fn(func) => validate_fn(prog, &func)?, Statement::Fn(func) => validate_fn(prog, func)?,
Statement::Let(lt) if current_state == CurrentState::InFunc => validate_stat_let(prog, lt)?, Statement::Let(lt) if current_state == CurrentState::InFunc => validate_stat_let(prog, lt)?,
Statement::Let(_) if current_state == CurrentState::Outside => { Statement::Let(_) if current_state == CurrentState::Outside => {
lerror!(stat.loc(), "Let statements are not allowed outside a function"); lerror!(stat.loc(), "Let statements are not allowed outside a function");
@ -55,56 +54,75 @@ fn validate_stat(prog: &mut Program, stat: &LocBox<Statement>, current_state: Cu
Ok(()) Ok(())
} }
fn validate_stat_let(prog: &mut Program, lt: &Let) -> anyhow::Result<()> { fn validate_stat_let(prog: &mut Program, lt: &mut Let) -> anyhow::Result<()> {
if let Some(val) = &lt.val && let Some(t) = &lt.typ { if let Some(val) = &mut lt.val && let Some(t) = &mut lt.typ {
let val_t = validate_expr(prog, &val.inner())?.unwrap(); let val_t = validate_expr(prog, val.inner_mut())?.unwrap();
if val_t != *t.inner() { if val_t != *t.inner_mut() {
lerror!(t.loc(), "Cannot assign {val_t} to {}", t.inner()); lerror!(t.loc(), "Cannot assign {val_t} to {}", t.inner_mut());
bail!("") 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(()) Ok(())
} }
fn validate_ast(prog: &mut Program, ast: &Ast) -> anyhow::Result<Option<Type>> { fn validate_ast(prog: &mut Program, ast: &mut Ast) -> anyhow::Result<Option<Type>> {
match ast { match ast {
Ast::Expr(expr) => { Ast::Expr(expr) => {
validate_expr(prog, &expr.inner()) validate_expr(prog, expr.inner_mut())
} }
Ast::Statement(stat) => { Ast::Statement(stat) => {
validate_stat(prog, &stat, CurrentState::InFunc)?; validate_stat(prog, stat, CurrentState::InFunc)?;
Ok(None) Ok(None)
} }
} }
} }
pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result<Option<Type>> { pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Option<Type>> {
match expr { match expr {
Expr::Break | Expr::Continue => Ok(None), Expr::Break | Expr::Continue => Ok(None),
Expr::Return(ret) => { Expr::Return(ret) => {
if let Some(expr) = &**ret { if let Some(expr) = &mut**ret {
validate_expr(prog, expr.inner()) validate_expr(prog, expr.inner_mut())
} else { } else {
Ok(None) Ok(None)
} }
} }
Expr::Struct { path, fields } => { Expr::Struct(id, strct) => {
// this is probably fucked so fix this later // 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()); let typ = Type::Owned(name.clone());
// this is so ass, this checks if struct exists // this is so ass, this checks if struct exists
validate_type(prog, &LocBox::new(&Loc::default(), typ.clone()))?; validate_type(prog, &LocBox::new(&Loc::default(), typ.clone()))?;
for field in fields { for field in strct.fields.iter_mut() {
validate_expr(prog, &field.1.inner())?; 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)) Ok(Some(typ))
}, },
Expr::If(ifs) => { Expr::If(ifs) => {
validate_expr(prog, ifs.test.inner())?; validate_expr(prog, ifs.test.inner_mut())?;
for item in &ifs.body.0 { for item in ifs.body.0.iter_mut() {
validate_ast(prog, item)?; validate_ast(prog, item)?;
} }
Ok(None) Ok(None)
@ -113,19 +131,42 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result<Option<T
// TODO: Path hell TBD // TODO: Path hell TBD
Ok(Some(Type::Owned(path.0.last().unwrap().clone()))) Ok(Some(Type::Owned(path.0.last().unwrap().clone())))
} }
Expr::Group(group) => validate_expr(prog, &group.inner()), Expr::Group(group) => validate_expr(prog, group.inner_mut()),
Expr::Call { path, params } => { Expr::Call { path, params } => {
let loc = path.loc(); let loc = path.loc().clone();
let Expr::Path(path) = path.inner() else { let Expr::Path(path) = path.inner_mut() else {
panic!("fml"); panic!("fml");
}; };
let func;
match path.0.len() { match path.0.len() {
1 => { 1 => {
let f = prog.functions.get(&path.0[0]); let f = prog.functions.get(&path.0[0]).cloned();
match f { 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 => { None => {
lerror!(loc, "Could not find function {}", path); lerror!(loc, "Could not find function {}", path);
panic!("") panic!("")
@ -134,9 +175,34 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result<Option<T
} }
2 => { 2 => {
let s = prog.member_functions.get(&path.0[0]).unwrap(); 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 { 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 => { None => {
lerror!(loc, "Could not find function {}", path); lerror!(loc, "Could not find function {}", path);
panic!("") panic!("")
@ -147,16 +213,41 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result<Option<T
} }
for (i, param) in params.0.iter().enumerate() {
let t = validate_expr(prog, param.inner())?; }
let ft = func.inner().params[i].1.inner(); Expr::MethodCall { left, params } => {
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) { if t.as_ref() != Some(ft) {
lerror!(param.loc(), "expected {ft}, got {}", t.unwrap()); lerror!(param.loc(), "expected {ft}, got {}", t.unwrap());
bail!("") bail!("")
} }
} }
if let Some(t) = &func.inner().ret_type { if let Some(t) = &mut func.inner_mut().ret_type {
Ok(Some(t.inner().clone())) Ok(Some(t.inner_mut().clone()))
} else { } else {
Ok(None) Ok(None)
@ -164,24 +255,34 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result<Option<T
} }
Expr::ForLoop { init, test, on_loop, body } => { Expr::ForLoop { init, test, on_loop, body } => {
let _ = validate_ast(prog, init)?; let _ = validate_ast(prog, init)?;
let _ = validate_expr(prog, test.inner())?; let _ = validate_expr(prog, test.inner_mut())?;
let _ = validate_expr(prog, on_loop.inner())?; let _ = validate_expr(prog, on_loop.inner_mut())?;
for item in &body.0 { for item in body.0.iter_mut() {
let _ = validate_ast(prog, item)?; let _ = validate_ast(prog, item)?;
} }
Ok(None) Ok(None)
}, },
Expr::Cast { left, right } => { Expr::Cast { left, right } => {
validate_expr(prog, left.inner())?; validate_expr(prog, left.inner_mut())?;
Ok(Some(right.inner().clone())) 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)?)) Ok(Some(lit.to_type(prog)?))
} }
Expr::UnOp { typ, right } => { Expr::UnOp { typ, right } => {
match typ { match typ {
Punctuation::Not => { 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) { if !t.is_bool(prog) {
lerror!(right.loc(), "Expected bool, got {t}"); lerror!(right.loc(), "Expected bool, got {t}");
} }
@ -189,7 +290,7 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result<Option<T
} }
Punctuation::Minus | Punctuation::Minus |
Punctuation::Plus => { Punctuation::Plus => {
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) { if !t.is_numeric(prog) {
lerror!(right.loc(), "Expected number, got {t}"); lerror!(right.loc(), "Expected number, got {t}");
} }
@ -197,11 +298,11 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result<Option<T
} }
Punctuation::Ampersand => { Punctuation::Ampersand => {
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 })) Ok(Some(Type::Ref { inner: Box::new(t), mutable: false }))
} }
Punctuation::Star => { 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) { if !t.is_ptr(prog) {
lerror!(right.loc(), "Expected pointer, got {t}"); lerror!(right.loc(), "Expected pointer, got {t}");
} }
@ -214,7 +315,7 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result<Option<T
_ => unreachable!() _ => unreachable!()
} }
}, },
Expr::BinOp { typ, left, right } => { Expr::BinOp { typ, left, right, signed } => {
match typ { match typ {
Punctuation::Ampersand | Punctuation::Ampersand |
Punctuation::Or | Punctuation::Or |
@ -228,23 +329,24 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result<Option<T
Punctuation::Shr => { Punctuation::Shr => {
let t1_s; let t1_s;
let t2_s; let t2_s;
let t1 = validate_expr(prog, left.inner())?.unwrap(); let t1 = validate_expr(prog, left.inner_mut())?.unwrap().get_absolute_value(prog)?;
let t2 = validate_expr(prog, right.inner())?.unwrap(); let t2 = validate_expr(prog, right.inner_mut())?.unwrap().get_absolute_value(prog)?;
if !t1.is_numeric(prog) { if !(t1.is_numeric(prog) || t1.is_ptr(prog)) {
lerror!(right.loc(), "Expected bool, got {t1}"); lerror!(right.loc(), "Expected number, got {t1}");
} }
if !t2.is_numeric(prog) { if !(t2.is_numeric(prog) || t2.is_ptr(prog)) {
lerror!(right.loc(), "Expected bool, got {t2}"); lerror!(right.loc(), "Expected number, got {t2}");
} }
match &t1 { match &t1 {
Type::Builtin { name: _, size, .. } => t1_s = *size, Type::Builtin { name: _, size, .. } => t1_s = *size,
_ => unreachable!() _ => unreachable!("1: {t1:?}\n2: {t2:?}")
} }
match &t2 { match &t2 {
Type::Builtin { name: _, size, .. } => t2_s = *size, Type::Builtin { name: _, size, .. } => t2_s = *size,
_ => unreachable!() _ => unreachable!()
} }
*signed = t1.is_signed(prog).expect("verified as numeric");
if t2_s > t1_s { if t2_s > t1_s {
Ok(Some(t2)) Ok(Some(t2))
} else { } else {
@ -258,14 +360,15 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result<Option<T
Punctuation::Ge | Punctuation::Ge |
Punctuation::AndAnd | Punctuation::AndAnd |
Punctuation::OrOr => { Punctuation::OrOr => {
let t1 = validate_expr(prog, left.inner())?.unwrap(); let t1 = validate_expr(prog, left.inner_mut())?.unwrap().get_absolute_value(prog)?;
let t2 = validate_expr(prog, right.inner())?.unwrap(); let t2 = validate_expr(prog, right.inner_mut())?.unwrap().get_absolute_value(prog)?;
if !t1.is_bool(prog) { if !(t1.is_numeric(prog) || t1.is_ptr(prog)) {
lerror!(right.loc(), "Expected bool, got {t1}"); lerror!(right.loc(), "Expected number or pointer, got {t1}");
} }
if !t2.is_bool(prog) { if !(t2.is_numeric(prog) || t2.is_ptr(prog)) {
lerror!(right.loc(), "Expected bool, got {t2}"); lerror!(right.loc(), "Expected number or pointer, got {t2}");
} }
*signed = t1.is_signed(prog).expect("verified as numeric");
Ok(Some(t1)) Ok(Some(t1))
} }
@ -279,21 +382,22 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result<Option<T
Punctuation::AndEq | Punctuation::AndEq |
Punctuation::OrEq | Punctuation::OrEq |
Punctuation::XorEq => { Punctuation::XorEq => {
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 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}"); lerror!(left.loc(), "Mismatched types, assigning {val_t} to {var_t}");
bail!(""); bail!("");
} }
*signed = var_t.is_signed(prog).expect("verified as numeric");
Ok(None) Ok(None)
} }
Punctuation::Eq => { 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 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)) { 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}"); 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<Option<T
} }
}, },
Expr::ArrayIndex { name, index } => { Expr::ArrayIndex { name, index } => {
let left = validate_expr(prog, name.inner())?; let left = validate_expr(prog, name.inner_mut())?;
let Some(left) = left else { let Some(left) = left else {
lerror!(name.loc(), "expected value, got nothing, cannot index nothing"); lerror!(name.loc(), "expected value, got nothing, cannot index nothing");
bail!("") bail!("")
@ -332,7 +436,7 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result<Option<T
} }
Type::Owned(name) => { Type::Owned(name) => {
let t = prog.types.get(name).cloned(); let t = prog.types.get(name).cloned();
if let Some(t) = t { if let Some(t) = &t {
f(prog, t.inner(), t.loc(), index) f(prog, t.inner(), t.loc(), index)
} else { } else {
lerror!(loc, "Unknown type {name}"); lerror!(loc, "Unknown type {name}");
@ -348,38 +452,39 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result<Option<T
} }
f(prog, &left, name.loc(), &index) f(prog, &left, name.loc(), &index)
}, },
Expr::PtrFieldAccess { left, right } | Expr::PtrFieldAccess { left, right, offset } |
Expr::FieldAccess { left, right } => { Expr::FieldAccess { left, right, offset } => {
let left = validate_expr(prog, left.clone().unwrap().inner())?; let left = validate_expr(prog, left.clone().unwrap().inner_mut())?;
let right = validate_expr(prog, right.clone().inner())?.unwrap(); let right = validate_expr(prog, right.clone().inner_mut())?.unwrap();
let Type::Owned(right) = right else { let Type::Owned(right) = right else {
panic!() panic!()
}; };
match left.unwrap() { match left.unwrap().get_absolute_value(prog)? {
Type::Owned(name) => { Type::Owned(name) => {
if let Some(strct) = prog.structs.get(&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 { if field.0 == right {
*offset = strct.inner().clone().get_offset_of(prog, &right)?;
return Ok(Some(field.1.inner().clone())); return Ok(Some(field.1.inner().clone()));
} }
} }
} }
} }
_ => panic!() v => panic!("{v:?}"),
} }
Ok(None) Ok(None)
}, },
Expr::WhileLoop { test, body } => { Expr::WhileLoop { test, body } => {
let _ = validate_expr(prog, test.inner())?; let _ = validate_expr(prog, test.inner_mut())?;
for item in &body.0 { for item in body.0.iter_mut() {
let _ = validate_ast(prog, item)?; let _ = validate_ast(prog, item)?;
} }
Ok(None) Ok(None)
}, },
Expr::InfLoop { body } => { Expr::InfLoop { body } => {
for item in &body.0 { for item in body.0.iter_mut() {
let _ = validate_ast(prog, item)?; let _ = validate_ast(prog, item)?;
} }
Ok(None) Ok(None)
@ -388,12 +493,14 @@ pub fn validate_expr(prog: &mut Program, expr: &Expr) -> anyhow::Result<Option<T
} }
} }
fn validate_fn(prog: &mut Program, func: &Function) -> anyhow::Result<()> { fn validate_fn(prog: &mut Program, func: &mut Function) -> anyhow::Result<()> {
prog.scope = Some(Scope::default());
for param in &func.params { for param in &func.params {
validate_type(prog, &param.1)?; let t = validate_type(prog, &param.1)?;
prog.curr_fn_args.insert(param.0.clone(), LocBox::new(&Loc::default(), t.clone()));
} }
if let Some(body) = &func.body { if let Some(body) = &mut func.body {
for item in &body.0 { for item in body.0.iter_mut() {
validate_ast(prog, item)?; validate_ast(prog, item)?;
} }
} }
@ -401,12 +508,12 @@ fn validate_fn(prog: &mut Program, func: &Function) -> anyhow::Result<()> {
Ok(()) 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)?; validate_type(prog, &var.typ)?;
Ok(()) 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)?; validate_type(prog, &var.typ)?;
Ok(()) Ok(())
} }
@ -416,7 +523,7 @@ fn validate_enum(_: &mut Program, _: &Enum) -> anyhow::Result<()> {
Ok(()) 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 { for field in &strct.fields {
if let Err(e) = validate_type(prog, &field.1) { if let Err(e) = validate_type(prog, &field.1) {
error!("Could not find type in field {}::{}", strct.name, field.0); 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<Ast>) -> any
} }
Statement::Enum(_) => (), Statement::Enum(_) => (),
Statement::Struct(strct) => { Statement::Struct(strct) => {
for (name, t) in &strct.fields { for (name, t) in strct.fields.iter() {
if let Err(_) = validate_type(prog, t) { if let Err(_) = validate_type(prog, t) {
lerror!(t.loc(), "Type '{}', of field, '{}.{name}' does not exist", t.inner(), strct.name); lerror!(t.loc(), "Type '{}', of field, '{}.{name}' does not exist", t.inner(), strct.name);
errored = true; errored = true;
@ -490,23 +597,26 @@ fn check_that_types_exist_for_items(prog: &mut Program, items: &Vec<Ast>) -> any
Ok(()) Ok(())
} }
fn validate_type(prog: &mut Program, typ: &LocBox<Type>) -> anyhow::Result<()> { fn validate_type(prog: &mut Program, typ: &LocBox<Type>) -> anyhow::Result<Type> {
fn f(prog: &mut Program, typ: &Type, loc: &Loc) -> anyhow::Result<()> { fn f(prog: &mut Program, typ: &Type, loc: &Loc) -> anyhow::Result<Type> {
match typ { match typ {
Type::SizedArray { inner, .. } | Type::SizedArray { inner, .. } |
Type::UnsizedArray { inner, .. } | Type::UnsizedArray { inner, .. } |
Type::Ref { inner, .. } => f(prog, inner, loc), Type::Ref { inner, .. } => f(prog, inner, loc),
Type::Owned(typ) => { Type::Owned(ident) => {
if prog.enums.get(typ).is_some() || if let Some(builtin) = get_builtin_from_name(&ident.0) {
prog.types.get(typ).is_some() || Ok(builtin)
prog.structs.get(typ).is_some() { } else
Ok(()) if prog.enums.get(ident).is_some() ||
prog.types.get(ident).is_some() ||
prog.structs.get(ident).is_some() {
Ok(typ.clone())
} else { } else {
lerror!(loc, "Could not find type '{}'", typ.0); lerror!(loc, "Could not find type '{}'", ident.0);
bail!("") bail!("")
} }
} }
Type::Builtin { .. } => Ok(()) Type::Builtin { .. } => Ok(typ.clone())
} }
} }
f(prog, typ.inner(), typ.loc()) f(prog, typ.inner(), typ.loc())
@ -514,32 +624,33 @@ fn validate_type(prog: &mut Program, typ: &LocBox<Type>) -> anyhow::Result<()> {
fn collect_types_and_constants(prog: &mut Program, items: &Vec<Ast>) { fn collect_types_and_constants(prog: &mut Program, items: &mut Vec<Ast>) {
for item in items { for item in items.iter_mut() {
match item { match item {
Ast::Statement(stat) => { Ast::Statement(stat) => {
match stat.inner() { let loc = stat.loc().clone();
match stat.inner_mut() {
Statement::Fn(func)=> { Statement::Fn(func)=> {
if let Some(struct_name) = &func.struct_name { if let Some(struct_name) = &func.struct_name {
if let Some(v) = prog.member_functions.get_mut(&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 { } else {
let mut v = HashMap::new(); 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); prog.member_functions.insert(struct_name.clone(), v);
} }
} else { } 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) => { 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) => { 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) => { 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)); prog.types.insert(alias.name.clone(), LocBox::new(stat.loc(), typ));
} }
Statement::Let { .. } => (), Statement::Let { .. } => (),
@ -551,7 +662,33 @@ fn collect_types_and_constants(prog: &mut Program, items: &Vec<Ast>) {
}, },
} }
} }
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());
}
_ => ()
}
}
} }
} }

View File

@ -34,6 +34,13 @@ lazy_static!(
].into(); ].into();
); );
pub fn get_builtin_from_name(name: &str) -> Option<Type> {
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) { pub fn load_builtin(prog: &mut Program) {
let loc = Loc::new("(internal)", 0, 0); let loc = Loc::new("(internal)", 0, 0);

BIN
test Executable file

Binary file not shown.

View File

@ -1,21 +1,47 @@
type str = [u8]; type str = [u8];
struct Foo { struct Foo {
a: usize, a: usize,
b: &str b: &str
} }
fn Foo.new(a: usize, b: &str) -> Foo { fn Foo.new(a: usize, b: &str) -> &Foo {
return Foo { return &Foo {
a: a, a: a,
b: b b: b
}; };
} }
fn print(s: &str) {
fn main() -> i32 { // do nothign for now
let obj = Foo::new();
} }
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;

BIN
test.o Normal file

Binary file not shown.