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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@ use std::collections::HashMap;
use statement::{ConstVar, Enum, Function, StaticVar, Struct};
use crate::{common::loc::LocBox, parser::ast::{expr::Path, statement::Let, typ::Type}};
use crate::{common::loc::LocBox, parser::ast::{expr::StructLit, literal::Literal, statement::Let, typ::Type}};
pub use crate::tokeniser::tokentype::*;
pub mod expr;
@ -11,7 +11,7 @@ pub mod statement;
pub mod typ;
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Default)]
pub struct Scope {
pub vars: HashMap<Ident, LocBox<Let>>,
pub static_vars: HashMap<Ident, StaticVar>,
@ -29,7 +29,10 @@ pub struct Program {
pub member_functions: HashMap<Ident, HashMap<Ident, LocBox<Function>>>,
pub static_vars: HashMap<Ident, StaticVar>,
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 {
Expr(LocBox<expr::Expr>),
Statement(LocBox<statement::Statement>),

View File

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

View File

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

View File

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

View File

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

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)) {
Ok(Some(parse_let(tokens)?))
} else {
Ok(None)
}
}
@ -200,7 +201,7 @@ fn parse_fn_params(tokens: &mut Vec<Token>) -> Result<Vec<(Ident, LocBox<Type>)>
break;
}
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?;
utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "huhhhhhhhh")?;
//dbg!(&name);
let typ = parse_type(tokens)?;
args.push((name.tt().unwrap_ident(), typ));

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();
match count.inner() {
Expr::Literal(Literal::Number(_)) |
Expr::Literal(_, Literal::Number(_)) |
Expr::Path(_) => (),
_ => {
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 crate::{cli::CliArgs, parser::ast::Program};
mod x86_64;
pub mod x86_64;
//mod none;
pub fn get_default_target() -> String {
x86_64::cgen::linux::CGen::get_target_triple().to_string()
// x86_64::cgen::linux::CGen::get_target_triple().to_string()
x86_64::asmgen::linux::AsmGen::get_target_triple().to_string()
}
pub fn get_all_targets() -> Vec<&'static str> {
vec![
// x86_64::asmgen::linux::AsmGen::get_target_triple(),
x86_64::asmgen::linux::AsmGen::get_target_triple(),
// x86_64::cgen::linux::CGen::get_target_triple(),
// x86_64::llvmgen::linux::LlvmGen::get_target_triple(),
//none::luagen::cct::LuaGen::get_target_triple(),
x86_64::cgen::linux::CGen::get_target_triple(),
// x86_64::cgen::linux::CGen::get_target_triple(),
]
}
fn get_target_from_triple(triple: &str) -> Box<dyn Target> {
match triple {
// _ if triple == x86_64::asmgen::linux::AsmGen::get_target_triple() => {
// Box::new(x86_64::asmgen::linux::AsmGen::new())
// }
_ if triple == x86_64::cgen::linux::CGen::get_target_triple() => {
Box::new(x86_64::cgen::linux::CGen::new())
_ if triple == x86_64::asmgen::linux::AsmGen::get_target_triple() => {
Box::new(x86_64::asmgen::linux::AsmGen::new())
}
// _ if triple == x86_64::cgen::linux::CGen::get_target_triple() => {
// Box::new(x86_64::cgen::linux::CGen::new())
// }
// _ if triple == x86_64::qbegen::linux::QbeGen::get_target_triple() => {
// Box::new(x86_64::qbegen::linux::QbeGen::new())
// }

View File

@ -1,7 +1,9 @@
use crate::targets::Target;
use std::io::Write;
use crate::{common::{Loc, loc::LocBox}, parser::ast::{Ast, Ident, Program, Punctuation, expr::{Expr, IfBranchExpr}, literal::Literal, statement::{ConstDataTyp, Function, Statement, get_constant_data_as_bytes}, typ::Type}, targets::Target};
use std::{collections::HashMap, fs::File, io::Write};
pub struct AsmGen;
const RUNTIME_CODE: &'static str = include_str!("./runtime.s");
impl Target for AsmGen {
fn new() -> Self {
Self {}
@ -12,20 +14,672 @@ impl Target for AsmGen {
fn get_int_ext(&self) -> &'static str {
"s"
}
fn write_code(&mut self, program: &crate::parser::ast::Program, path: &std::path::Path) -> anyhow::Result<()> {
let mut f = std::fs::File::open(path)?;
fn write_code(&mut self, program: &crate::parser::ast::Program, f: &mut File) -> anyhow::Result<()> {
writeln!(f, "bits 64")?;
writeln!(f, "global _start")?;
writeln!(f, "section .text")?;
writeln!(f, "section .rodata")?;
for (name, constant) in &program.const_vars {
writeln!(f, "{name}:")?;
writeln!(f, "{}", RUNTIME_CODE)?;
for item in &program.ast.0 {
match item {
Ast::Statement(stat) => {
match stat.inner() {
Statement::Fn(func) => self.write_func(program, f, func.clone(), stat.loc().clone())?,
_ => ()
}
}
_ => ()
}
}
writeln!(f, "section .data")?;
self.write_constants(program, f)?;
self.write_literals(program, f)?;
writeln!(f, "section .data")?;
self.write_statics(program, f)?;
Ok(())
}
fn compile(&mut self, _from: &std::path::Path, _to: &std::path::Path) -> anyhow::Result<()> {
fn compile(&mut self, from: &std::path::Path, to: &std::path::Path) -> anyhow::Result<()> {
let mut cmd = std::process::Command::new("nasm");
let cmd = cmd.args(&[
"-felf64",
"-o",
to.to_string_lossy().to_string().as_str(),
from.to_string_lossy().to_string().as_str(),
]);
info!("Running: {} {}", cmd.get_program().to_string_lossy(), cmd.get_args().map(|f| f.to_string_lossy().to_string()).collect::<Vec<String>>().join(" "));
cmd.output()?;
Ok(())
}
fn link(&mut self, _from: Vec<std::path::PathBuf>, _to: &std::path::Path) -> anyhow::Result<()> {
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 {
writeln!(f, "{name}: ; const")?;
let bytes = get_constant_data_as_bytes(program, &HashMap::new(), constant.val.clone(), false, false)?;
fn x(bytes: &ConstDataTyp, f: &mut File) -> anyhow::Result<()> {
match bytes {
ConstDataTyp::Array(arr) => {
for v in arr {
x(v, f)?;
}
}
ConstDataTyp::Bytes(bytes) => {
write!(f, " db ")?;
for (i, b) in bytes.into_iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
write!(f, "0x{:02x}", b)?;
}
writeln!(f, "")?;
}
ConstDataTyp::AddrOfFunc(name) |
ConstDataTyp::AddrOfConst(name) |
ConstDataTyp::AddrOfStatic(name) => {
writeln!(f, " dq {name}")?;
},
ConstDataTyp::Variable(..) => unreachable!(),
}
/*
*/
Ok(())
}
x(&bytes, f)?;
}
Ok(())
}
pub fn write_statics(&self, program: &Program, f: &mut File) -> anyhow::Result<()> {
for (name, statc) in &program.static_vars {
writeln!(f, "{name}: ; static")?;
let bytes = get_constant_data_as_bytes(program, &HashMap::new(), statc.val.clone(), false, false)?;
fn x(bytes: &ConstDataTyp, f: &mut File) -> anyhow::Result<()> {
match bytes {
ConstDataTyp::Array(arr) => {
for v in arr {
x(v, f)?;
}
}
ConstDataTyp::Bytes(bytes) => {
write!(f, " db ")?;
for (i, b) in bytes.into_iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
write!(f, "0x{:02x}", b)?;
}
writeln!(f, "")?;
}
ConstDataTyp::AddrOfFunc(name) |
ConstDataTyp::AddrOfConst(name) |
ConstDataTyp::AddrOfStatic(name) => {
writeln!(f, " dq {name}")?;
},
ConstDataTyp::Variable(..) => unreachable!(),
}
/*
*/
Ok(())
}
x(&bytes, f)?;
}
Ok(())
}
pub fn write_literals(&self, program: &Program, f: &mut File) -> anyhow::Result<()> {
fn w(bytes: ConstDataTyp, f: &mut File) -> anyhow::Result<()> {
match bytes {
ConstDataTyp::Array(arr) => {
for v in arr {
w(v, f)?;
}
}
ConstDataTyp::Bytes(bytes) => {
write!(f, " db ")?;
for (i, b) in bytes.into_iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
write!(f, "0x{:02x}", b)?;
}
writeln!(f, "")?;
}
ConstDataTyp::AddrOfFunc(name) |
ConstDataTyp::AddrOfConst(name) |
ConstDataTyp::AddrOfStatic(name) => {
writeln!(f, " dq {name}")?;
},
ConstDataTyp::Variable(name, size) => {
writeln!(f, " resb {size} ; {name}")?;
}
}
Ok(())
}
for lit in &program.literals {
writeln!(f, "mcl_lit_{}:", lit.0)?;
w(get_constant_data_as_bytes(program, &HashMap::new(), LocBox::new(&Loc::default(), Expr::Literal(lit.0.clone(), lit.1.clone())), false, false)?, f)?;
}
for lit in &program.struct_literals {
writeln!(f, "mcl_lit_{}:", lit.0)?;
w(get_constant_data_as_bytes(program, &HashMap::new(), LocBox::new(&Loc::default(), Expr::Struct(lit.0.clone(), lit.1.clone())), false, false)?, f)?;
}
Ok(())
}
}

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

BIN
test Executable file

Binary file not shown.

View File

@ -1,21 +1,47 @@
type str = [u8];
struct Foo {
a: usize,
b: &str
}
fn Foo.new(a: usize, b: &str) -> Foo {
return Foo {
fn Foo.new(a: usize, b: &str) -> &Foo {
return &Foo {
a: a,
b: b
};
}
fn main() -> i32 {
let obj = Foo::new();
fn print(s: &str) {
// do nothign for now
}
fn mul(n: usize, n2: usize) -> usize {
return n * n2;
}
fn main() -> i32 {
let obj = Foo::new(1, "owo");
obj->b;
let owo = "ahahaha";
loop {
print(owo);
}
for (let i = 0; i < 10; i += 1) {
print("nyaaa");
if (i > 7) {
break;
} else {
continue;
}
print("cant see me!");
}
while (true) {
mul(1);
}
}
const FOO: usize = main;

BIN
test.o Normal file

Binary file not shown.