Fix bugs, add propper tests

This commit is contained in:
2026-02-03 13:20:23 +02:00
parent 081ff9a27a
commit 4b61adea5d
58 changed files with 1103 additions and 285 deletions

View File

@@ -1,7 +0,0 @@
// fn syscall(arg_count: usize, syscall_num: usize, args: )
fn puts(s: &str) {
__INTERNAL_syscall(1 as u8, 1 as u8, &[s] as &[&void]);
}

BIN
libbeaker.a Normal file

Binary file not shown.

View File

@@ -0,0 +1 @@
[]

View File

@@ -59,4 +59,4 @@
SquareL,
),
},
]
]

View File

@@ -133,7 +133,7 @@ fn test_parser(cf: &CollectedFiles, compile: bool) -> anyhow::Result<usize> {
continue;
}
};
let ast = match mclangc::parser::parse_program(tokens) {
let ast = match mclangc::parser::parse_program(tokens, &mclangc::cli::CliArgs::default()) {
Ok(v) => v,
Err(e) => {
crate::error!("Test parser/{name} had an error: {e}");

View File

@@ -13,11 +13,16 @@ pub struct CliArgs {
quiet: bool,
#[arg(long, short, value_parser=crate::targets::get_all_targets(), default_value_t=crate::targets::get_default_target())]
pub target: String,
#[arg(long="include", short='I', default_values=["include"])]
#[arg(long="include", short='I', default_values=[".", "include"])]
pub include_paths: Vec<String>,
/// Output file
#[arg(long, short, default_value="a.out")]
pub output: String,
/// Linker args
#[arg(long, short='L')]
pub linker_args: Vec<String>,
/// All input files
#[clap(num_args = 1..)]
pub input: Vec<String>

View File

@@ -6,7 +6,6 @@ mod logger;
fn main() -> anyhow::Result<()> {
let cli = mclangc::cli::CliArgs::parse();
cli.set_log_level();
@@ -25,9 +24,12 @@ fn main() -> anyhow::Result<()> {
// dbg!(&tokens);
info!("Parsing {file}");
let mut prog = mclangc::parser::parse_program(tokens, &cli)?;
// dbg!(&prog.ast);
info!("Validating {file}");
mclangc::validator::validate_code(&mut prog)?;
dbg!(&prog.functions.len());
for f in &prog.functions {
error!("owo {}", f.1.inner());
}
// dbg!(&prog.literals);
progs.push((fp, prog));
}

View File

@@ -30,6 +30,7 @@ pub enum Expr {
},
MethodCall {
left: Box<LocBox<Expr>>,
strct: Option<Ident>,
params: CallParams,
},
@@ -37,12 +38,16 @@ pub enum Expr {
FieldAccess {
left: Box<Option<LocBox<Expr>>>,
right: Box<LocBox<Expr>>,
offset: usize
offset: usize,
l_typ: Option<Box<Type>>,
r_typ: Option<Box<Type>>,
},
PtrFieldAccess {
left: Box<Option<LocBox<Expr>>>,
right: Box<LocBox<Expr>>,
offset: usize
offset: usize,
l_typ: Option<Box<Type>>,
r_typ: Option<Box<Type>>
},
ForLoop {
init: Box<Ast>,
@@ -66,6 +71,9 @@ pub enum Expr {
left: Box<LocBox<Expr>>,
right: Box<LocBox<Type>>
},
Self_ {
strct: Option<Ident>
},
}
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd)]
@@ -102,6 +110,12 @@ pub struct CallParams(pub Vec<LocBox<Expr>>);
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub struct Block(pub Vec<Ast>);
impl Default for Block {
fn default() -> Self {
Self(vec![])
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Hash)]
pub struct Path(pub Vec<Ident>);

View File

@@ -9,10 +9,14 @@ pub enum Literal {
Ident(Ident),
String(TString),
Char(Char),
Array(Vec<LocBox<Expr>>),
Array{
items: Vec<LocBox<Expr>>,
item_size: Option<usize>,
},
Bool(bool),
ArrayRepeat {
val: Box<LocBox<Expr>>,
item_size: Option<usize>,
count: usize,
},
}
@@ -22,28 +26,29 @@ impl Literal {
let t = match self {
Self::Number(_) => Type::Builtin { name: String::from("usize"), size: SIZE as u8, signed: false },
Self::Ident(ident) => Type::Owned(ident.clone()),
Self::String(_) => Type::Ref { inner: Box::new(Type::Owned(Ident(String::from("str")))), mutable: false},
Self::String(str) if str.cstr => Type::Ref { inner: Box::new(Type::Owned(Ident(String::from("cstr")))), mutable: false},
Self::String(str) if !str.cstr => Type::Ref { inner: Box::new(Type::Owned(Ident(String::from("str")))), mutable: false},
Self::String(_) => unreachable!("stupid rust"),
Self::Char(_) => Type::Owned(Ident(String::from("char"))),
Self::Bool(_) => Type::Owned(Ident(String::from("bool"))),
Self::Array(arr) => {
Self::Array { items: 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(String::new(), Literal::Number(Number { val: 0, base: 10, signed: false })))
}
} else {
let item = arr.first().unwrap();
let mut item = arr.first().unwrap().clone();
let loc = item.loc().clone();
let mut item = item.inner().clone();
Type::SizedArray {
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 } => {
Self::ArrayRepeat { val, count, ..} => {
let loc = val.loc().clone();
let mut val = val.inner().clone();
let mut val = val.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

@@ -19,7 +19,7 @@ pub struct Scope {
pub inner_scope: Option<Box<Scope>>
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Default)]
pub struct Program {
pub ast: expr::Block,
pub structs: HashMap<Ident, LocBox<Struct>>,
@@ -32,7 +32,9 @@ pub struct Program {
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>>
pub curr_fn_args: HashMap<Ident, LocBox<Type>>,
pub curr_struct: Option<Ident>,
pub included_files: Vec<String>,
}

View File

@@ -1,8 +1,9 @@
use std::collections::HashMap;
use std::{collections::HashMap, fmt::{Display, write}, sync::Mutex};
use anyhow::bail;
use lazy_static::lazy_static;
use crate::{common::loc::LocBox, lerror};
@@ -70,6 +71,32 @@ pub struct Function {
pub body: Option<Block>, // If None then its a type declaration
}
impl Display for Function {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut extrn = String::new();
let mut args = Vec::new();
let mut ret_t = String::new();
if let Some(ex) = &self.qual_extern {
if ex.val.is_empty() {
extrn = format!("extern ");
} else {
extrn = format!("extern \"{}\"", ex.val);
}
}
for arg in &self.params {
args.push(format!("{}: {}", arg.0, arg.1.inner()));
}
if let Some(ret) = &self.ret_type {
ret_t = format!(" -> {}", ret.inner());
}
write!(f, "{extrn}fn {}({}){ret_t}", self.get_full_name_pretty(), args.join(", "))
}
}
impl Function {
pub fn get_full_name(&self) -> String {
if let Some(sn) = &self.struct_name {
@@ -138,7 +165,19 @@ pub enum ConstDataTyp {
Array(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> {
lazy_static!(
static ref CTX: Mutex<ConstantCalcCtx> = Mutex::new(ConstantCalcCtx::default());
);
#[derive(Debug, Default, Clone)]
struct ConstantCalcCtx {
current_array_item_size: Option<usize>,
current_struct_items: Option<HashMap<Ident, usize>>,
current_struct_item_name: Option<Ident>
}
pub fn get_constant_data_as_bytes(program: &Program, value: LocBox<Expr>, is_big_endian: bool, must_be_number: bool) -> anyhow::Result<ConstDataTyp> {
match value.inner() {
Expr::Literal(_, lit) => {
match lit {
@@ -154,11 +193,28 @@ pub fn get_constant_data_as_bytes(program: &Program, struct_items: &HashMap<Iden
lerror!(value.loc(), "Expected number got string");
bail!("")
}
let mut val = val.chars().into_iter().map(|v| v as u8).collect::<Vec<u8>>();
if *cstr {
let mut val = val.chars().into_iter().map(|v| v as u8).collect::<Vec<u8>>();
val.push(0);
Ok(ConstDataTyp::Bytes(val))
} else {
let mut buf = Vec::new();
let mut inc = 0;
while inc < 8 {
buf.push(((val.len() << 8*inc) & 0xff) as u8);
inc += 1;
}
if is_big_endian {
buf.reverse();
}
let val = val.chars().into_iter().map(|v| v as u8).collect::<Vec<u8>>();
Ok(ConstDataTyp::Array(vec![
ConstDataTyp::Bytes(buf),
ConstDataTyp::Bytes(val)
]))
}
Ok(ConstDataTyp::Bytes(val))
}
Literal::Number(Number { val, base: _, signed: _ }) => {
let mut buf = Vec::new();
@@ -174,7 +230,7 @@ pub fn get_constant_data_as_bytes(program: &Program, struct_items: &HashMap<Iden
Ok(ConstDataTyp::Bytes(buf))
}
Literal::Array(arr) => {
Literal::Array { items: arr, item_size } => {
if must_be_number {
lerror!(value.loc(), "Expected number got array");
bail!("")
@@ -183,20 +239,24 @@ pub fn get_constant_data_as_bytes(program: &Program, struct_items: &HashMap<Iden
if arr.len() < 1 {
return Ok(ConstDataTyp::Bytes(vec![]));
}
CTX.lock().unwrap().current_array_item_size = *item_size;
for item in arr {
let data = get_constant_data_as_bytes(program, struct_items, item.clone(), is_big_endian, must_be_number)?;
let data = get_constant_data_as_bytes(program, item.clone(), is_big_endian, must_be_number)?;
bytes.push(data);
}
CTX.lock().unwrap().current_array_item_size = None;
Ok(ConstDataTyp::Array(bytes))
}
Literal::ArrayRepeat { val, count } => {
Literal::ArrayRepeat { val, count, item_size} => {
if must_be_number {
lerror!(value.loc(), "Expected number got repeating array");
bail!("")
}
let val = get_constant_data_as_bytes(program, struct_items, (**val).clone(), is_big_endian, must_be_number)?;
CTX.lock().unwrap().current_array_item_size = *item_size;
let val = get_constant_data_as_bytes(program, (**val).clone(), is_big_endian, must_be_number)?;
CTX.lock().unwrap().current_array_item_size = None;
let mut num = Vec::new();
let mut inc = 0;
@@ -241,23 +301,31 @@ pub fn get_constant_data_as_bytes(program: &Program, struct_items: &HashMap<Iden
items.insert(name.clone(), item.inner().size_of(program)?);
}
CTX.lock().unwrap().current_struct_items = Some(items);
for field in &strct.fields {
bytes.push(get_constant_data_as_bytes(program, &items, field.1.clone(), is_big_endian, must_be_number)?);
CTX.lock().unwrap().current_struct_item_name = Some(field.0.clone());
bytes.push(get_constant_data_as_bytes(program, field.1.clone(), is_big_endian, must_be_number)?);
}
CTX.lock().unwrap().current_struct_item_name = None;
Ok(ConstDataTyp::Array(bytes))
}
Expr::Path(path) => {
let ctx = {CTX.lock().unwrap().clone()};
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)?)
Ok(get_constant_data_as_bytes(program, 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 if let Some(struct_items) = ctx.current_struct_items &&
let Some(typ) = struct_items.get(&ctx.current_struct_item_name.unwrap()) {
Ok(ConstDataTyp::Variable(name.clone(), *typ))
} else if let Some(array_item_size) = {CTX.lock().unwrap().clone()}.current_array_item_size {
Ok(ConstDataTyp::Variable(name.clone(), array_item_size))
} else {
lerror!(value.loc(), "Unable to find ident '{name}'");
bail!("")

View File

@@ -2,7 +2,7 @@ use std::fmt::Display;
use anyhow::bail;
use crate::{common::loc::LocBox, validator::predefined::get_builtin_from_name};
use crate::{common::loc::LocBox, info, validator::predefined::{BuiltinType, get_builtin_from_name}};
use super::{expr::Expr, literal::Literal, Ident, Program};
@@ -28,6 +28,53 @@ pub enum Type {
}
impl Type {
pub fn is_void(&self) -> bool {
*self == BuiltinType::void()
}
pub fn as_ref(self) -> Self {
Self::Ref {
inner: Box::new(self),
mutable: false
}
}
pub fn as_ref_mut(self) -> Self {
Self::Ref {
inner: Box::new(self),
mutable: true
}
}
pub fn new_builtin(name: &str, size: u8, signed: bool) -> Self {
Self::Builtin { name: name.to_string(), size, signed }
}
pub fn should_deref_pointer(&self, program: &Program) -> bool {
dbg!(&self);
let mut slf = self.clone();
match &mut slf {
Self::Ref { inner, .. } => {
match inner.as_mut() {
Self::Ref { .. } => true,
Self::Owned(ident) => {
if program.structs.get(ident).is_some() {
false
} else
if let Some(typ) = program.types.get(ident) {
**inner = typ.inner().clone();
slf.should_deref_pointer(program)
} else {
false
}
}
_ => true,
}
},
Self::Owned(_) |
Self::SizedArray { .. } |
Self::Builtin { .. } |
Self::UnsizedArray { .. } => false,
}
}
pub fn get_absolute_value(&self, program: &Program) -> anyhow::Result<Self> {
match self {
Self::Ref { inner, .. } => inner.get_absolute_value(program),
@@ -57,12 +104,42 @@ impl Type {
}
}
pub fn convert_owned_to_real_type(&mut self, program: &Program) {
match self {
Self::Owned(ident) => {
if let Some(t) = crate::validator::predefined::get_builtin_from_name(&ident.0) {
*self = t;
} else
if let Some(t) = program.types.get(ident) {
*self = t.inner().clone();
} else
if let Some(t) = program.curr_fn_args.get(ident) {
*self = t.inner().clone();
} else
if let Some(var) = program.get_var(ident) {
*self = var.1.inner().typ.clone().expect("type should be computed if were already using it").inner().clone();
}
}
Self::Ref { inner, .. } |
Self::SizedArray { inner, .. } |
Self::UnsizedArray { inner, .. } => inner.convert_owned_to_real_type(program),
Self::Builtin { .. } => (),
}
}
pub fn get_variable_type(&self, program: &Program) -> anyhow::Result<Self> {
match self {
Self::Owned(ident) => {
if let Some(t) = program.types.get(ident) {
dbg!(&ident);
dbg!(&crate::validator::predefined::get_builtin_from_name(&ident.0));
if let Some(t) = crate::validator::predefined::get_builtin_from_name(&ident.0) {
Ok(t)
} else
if let Some(t) = program.types.get(ident) {
Ok(t.inner().clone())
} else
if let Some(t) = program.curr_fn_args.get(ident) {
return 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 {

View File

@@ -2,7 +2,7 @@ use std::collections::{BTreeMap, HashMap};
use anyhow::{bail, Result};
use crate::{cli::CliArgs, common::loc::LocBox, debug, lerror, parser::{Punctuation, ast::expr::StructLit, typ::parse_type}, tokeniser::Token};
use crate::{cli::CliArgs, common::loc::LocBox, debug, lerror, parser::{Punctuation, ast::{Program, 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};
@@ -37,14 +37,17 @@ const BINOP_LIST: &[TokenType] = &[
TokenType::Punct(Punctuation::Ge),
];
pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool, cli: &CliArgs) -> Result<Option<LocBox<Expr>>> {
pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool, cli: &CliArgs, prog: &mut Program) -> Result<Option<LocBox<Expr>>> {
let res = if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenL)) {
Some(parse_group(tokens, cli)?)
Some(parse_group(tokens, cli, prog)?)
} else
if let Some(kw) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Self_)) {
Some(LocBox::new(kw.loc(), Expr::Self_ { strct: None }))
} else
if let Some(_) = utils::check(tokens, TokenType::ident("")) {
let p = parse_path(tokens)?;
if let Some(t) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
Some(parse_struct_literal(tokens, p.inner().unwrap_path(), cli)?)
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
Some(parse_struct_literal(tokens, p.inner().unwrap_path(), cli, prog)?)
} else {
Some(p)
}
@@ -56,7 +59,7 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
TokenType::Punct(Punctuation::Ampersand),
TokenType::Punct(Punctuation::Star),
]) {
Some(parse_unop(tokens, cli)?)
Some(parse_unop(tokens, cli, prog)?)
} else
if let Some(_) = utils::check_from_many(tokens, &[
TokenType::string("", false),
@@ -66,15 +69,15 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
TokenType::Keyword(Keyword::True),
TokenType::Keyword(Keyword::False)
]) {
Some(parse_literal(tokens, cli)?)
Some(parse_literal(tokens, cli, prog)?)
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::While)) {
return Ok(Some(parse_while_loop(tokens, cli)?));
return Ok(Some(parse_while_loop(tokens, cli, prog)?));
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::For)) {
return Ok(Some(parse_for_loop(tokens, cli)?));
return Ok(Some(parse_for_loop(tokens, cli, prog)?));
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Loop)) {
return Ok(Some(parse_inf_loop(tokens, cli)?));
return Ok(Some(parse_inf_loop(tokens, cli, prog)?));
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Return)) {
return Ok(Some(parse_return(tokens, cli)?));
return Ok(Some(parse_return(tokens, cli, prog)?));
} 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)));
@@ -82,7 +85,7 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
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, cli)?))));
return Ok(Some(LocBox::new(&kw.loc().clone(), Expr::If(parse_if(tokens, cli, prog)?))));
} else {
None
};
@@ -96,17 +99,17 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
res = parse_ptr_field_access(tokens, res)?;
}
if utils::check(tokens, TokenType::Delim(Delimiter::ParenL)).is_some() {
res = parse_fn_call(tokens, res, cli)?;
res = parse_fn_call(tokens, res, cli, prog)?;
}
if utils::check(tokens, TokenType::Keyword(Keyword::As)).is_some() {
res = parse_cast(tokens, res)?;
}
if utils::check(tokens, TokenType::Delim(Delimiter::SquareL)).is_some() {
res = parse_array_index(tokens, res, cli)?;
res = parse_array_index(tokens, res, cli, prog)?;
}
if let Some(_) = utils::check_from_many(tokens, BINOP_LIST) {
let v = parse_binop(tokens, res, precedence, cli)?;
let v = parse_binop(tokens, res, precedence, cli, prog)?;
if consume_semi {
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
}
@@ -124,9 +127,9 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
Ok(res)
}
fn parse_return(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>> {
fn parse_return(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Return), "")?;
let item = parse_expr(tokens, 0, true, cli)?;
let item = parse_expr(tokens, 0, true, cli, prog)?;
Ok(LocBox::new(kw.loc(), Expr::Return(Box::new(item))))
}
@@ -138,10 +141,10 @@ fn parse_cast(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr
right: Box::new(typ)
}))
}
fn parse_if(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<IfExpr> {
fn parse_if(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> 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, cli)? else {
let Some(test) = parse_expr(tokens, 0, false, cli, prog)? else {
lerror!(loc.loc(), "Expected test for if statement, got nothing");
bail!("")
};
@@ -151,7 +154,7 @@ fn parse_if(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<IfExpr> {
_ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR));
Block(Vec::new())
} else {
parse_block(tokens, cli)?
parse_block(tokens, cli, prog)?
}
} else {
lerror!(loc.loc(), "Expected '{{'");
@@ -159,14 +162,14 @@ fn parse_if(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<IfExpr> {
};
if let Some(_) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Else)) {
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::If)) {
let branch = IfBranchExpr::ElseIf(Box::new(parse_if(tokens, cli)?));
let branch = IfBranchExpr::ElseIf(Box::new(parse_if(tokens, cli, prog)?));
Ok(IfExpr {
test: Box::new(test),
body: block,
else_if: Some(branch)
})
} else {
let branch = IfBranchExpr::Else(parse_block(tokens, cli)?);
let branch = IfBranchExpr::Else(parse_block(tokens, cli, prog)?);
Ok(IfExpr {
test: Box::new(test),
body: block,
@@ -181,39 +184,39 @@ fn parse_if(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<IfExpr> {
})
}
}
fn parse_while_loop(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>> {
fn parse_while_loop(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> 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, cli)? else {
let Some(test) = parse_expr(tokens, 0, false, cli, prog)? 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, cli)?;
let block = parse_block(tokens, cli, prog)?;
Ok(LocBox::new(kw.loc(), Expr::WhileLoop {
test: Box::new(test),
body: block
}))
}
fn parse_for_loop(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>> {
fn parse_for_loop(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> 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, cli)? else {
let Some(pre) = parse_item(tokens, cli, prog)? else {
lerror!(kw.loc(), "Expected init stat for a for loop, got nothing");
bail!("")
};
// Semicolon parsed out by parse_item above
let Some(test) = parse_expr(tokens, 0, false, cli)? else {
let Some(test) = parse_expr(tokens, 0, false, cli, prog)? else {
lerror!(kw.loc(), "Expected test comparrison for a for loop, got nothing");
bail!("")
};
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "");
let Some(post) = parse_expr(tokens, 0, false, cli)? else {
let Some(post) = parse_expr(tokens, 0, false, cli, prog)? else {
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, cli)?;
let block = parse_block(tokens, cli, prog)?;
Ok(LocBox::new(kw.loc(), Expr::ForLoop {
init: Box::new(pre),
@@ -222,16 +225,16 @@ fn parse_for_loop(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>
body: block
}))
}
fn parse_inf_loop(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>> {
fn parse_inf_loop(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Loop), "")?;
let block = parse_block(tokens, cli)?;
let block = parse_block(tokens, cli, prog)?;
Ok(LocBox::new(kw.loc(), Expr::InfLoop { body: block }))
}
fn parse_fn_call(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli: &CliArgs) -> Result<LocBox<Expr>> {
fn parse_fn_call(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
match left.inner() {
Expr::FieldAccess { .. } |
Expr::PtrFieldAccess { .. } => {
return parse_member_function_call(tokens, left, cli);
return parse_member_function_call(tokens, left, cli, prog);
}
_ => ()
}
@@ -244,7 +247,7 @@ fn parse_fn_call(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli: &CliArgs) ->
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
break;
}
let Some(param) = parse_expr(tokens, 0, false, cli)? else {break};
let Some(param) = parse_expr(tokens, 0, false, cli, prog)? 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)) {
@@ -256,9 +259,9 @@ fn parse_fn_call(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli: &CliArgs) ->
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "");
Ok(LocBox::new(start.loc(), Expr::Call { path: Box::new(left), params: CallParams(params) }))
}
fn parse_array_index(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli: &CliArgs) -> Result<LocBox<Expr>> {
fn parse_array_index(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareL), "")?;
let Some(idx) = parse_expr(tokens, 0, false, cli)? else {
let Some(idx) = parse_expr(tokens, 0, false, cli, prog)? else {
lerror!(start.loc(), "Expected index for in array index but found nothing.");
bail!("")
};
@@ -284,11 +287,13 @@ 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),
offset: 0
offset: 0,
l_typ: None,
r_typ: None
}))
}
fn parse_member_function_call(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli: &CliArgs) -> Result<LocBox<Expr>> {
fn parse_member_function_call(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "unreachable")?;
let mut params = Vec::new();
@@ -296,7 +301,7 @@ fn parse_member_function_call(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli:
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
break;
}
let Some(param) = parse_expr(tokens, 0, false, cli)? else {break};
let Some(param) = parse_expr(tokens, 0, false, cli, prog)? 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)) {
@@ -308,7 +313,8 @@ fn parse_member_function_call(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli:
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "");
Ok(LocBox::new(start.loc(), Expr::MethodCall {
left: Box::new(left),
left: Box::new(left),
strct: None,
params: CallParams(params)
}))
}
@@ -327,11 +333,13 @@ 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),
offset: 0
offset: 0,
l_typ: None,
r_typ: None
}))
}
fn parse_literal(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>> {
fn parse_literal(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> 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
@@ -352,17 +360,17 @@ fn parse_literal(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>>
} 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(String::new(), Literal::Array(Vec::new()))));
return Ok(LocBox::new(start.loc(), Expr::Literal(String::new(), Literal::Array { items: Vec::new(), item_size: None })));
}
/*if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Comma) {
} else */
if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Semi) {
let Some(typ) = parse_expr(tokens, 0, true, cli)? else {
let Some(typ) = parse_expr(tokens, 0, true, cli, prog)? else {
lerror!(start.loc(), "Expected value, found nothing");
bail!("")
};
let count = parse_expr(tokens, 0, false, cli)?.unwrap();
let count = parse_expr(tokens, 0, false, cli, prog)?.unwrap();
let Expr::Literal(_, Literal::Number(count)) = count.inner() else {
lerror!(count.loc(), "a repeating array accepts only literal numbers for count argument");
@@ -371,16 +379,14 @@ fn parse_literal(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>>
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
return Ok(LocBox::new(start.loc(), Expr::Literal(String::new(), Literal::ArrayRepeat {
val: Box::new(typ),
count: count.val
count: count.val,
item_size: None,
})));
} else {
let first = parse_expr(tokens, 0, false, cli)?;
let Some(first) = first else { unreachable!() };
let mut values = Vec::new();
values.push(first);
while !tokens.is_empty() {
let Some(val) = parse_expr(tokens, 0, false, cli)? else{break};
let Some(val) = parse_expr(tokens, 0, false, cli, prog)? else{break};
values.push(val);
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
@@ -388,7 +394,7 @@ fn parse_literal(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>>
}
}
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
return Ok(LocBox::new(start.loc(), Expr::Literal(String::new(), Literal::Array(values))));
return Ok(LocBox::new(start.loc(), Expr::Literal(String::new(), Literal::Array { items: values, item_size: None })));
/*
if let Some(curr) = tokens.last() {
lerror!(start.loc(), "Expected a , or ; as a separator in a literal array (normal, or repeating, respectively), but found {}", curr.tt());
@@ -402,7 +408,7 @@ fn parse_literal(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>>
unreachable!()
}
fn parse_struct_literal(tokens: &mut Vec<Token>, name: Path, cli: &CliArgs) -> Result<LocBox<Expr>> {
fn parse_struct_literal(tokens: &mut Vec<Token>, name: Path, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyL), "")?;
let mut fields = BTreeMap::new();
while !tokens.is_empty() {
@@ -412,7 +418,7 @@ fn parse_struct_literal(tokens: &mut Vec<Token>, name: Path, cli: &CliArgs) -> R
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?;
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "wtf?")?;
let typ = parse_expr(tokens, 0, false, cli)?.unwrap();
let typ = parse_expr(tokens, 0, false, cli, prog)?.unwrap();
fields.insert(name.tt().unwrap_ident(), typ);
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyR), "")?;
@@ -422,9 +428,9 @@ fn parse_struct_literal(tokens: &mut Vec<Token>, name: Path, cli: &CliArgs) -> R
Ok(LocBox::new(start.loc(), Expr::Struct(String::new(), StructLit { path: name, fields })))
}
fn parse_group(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>> {
fn parse_group(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
let Some(expr) = parse_expr(tokens, 0, false, cli)? else {
let Some(expr) = parse_expr(tokens, 0, false, cli, prog)? else {
lerror!(start.loc(), "Expected expr found nothing");
bail!("")
};
@@ -449,7 +455,7 @@ fn parse_path(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
Ok(LocBox::new(part.loc(), Expr::Path(Path(buf))))
}
fn parse_unop(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>> {
fn parse_unop(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
let typ = utils::check_consume_or_err_from_many(tokens, &[
TokenType::Punct(Punctuation::Not),
TokenType::Punct(Punctuation::Plus), // Make number positive
@@ -460,7 +466,7 @@ fn parse_unop(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>> {
let loc = typ.loc().clone();
let TokenType::Punct(typ) = typ.tt().clone() else {unreachable!()};
let Some(right) = parse_expr(tokens, 5, false, cli)? else {
let Some(right) = parse_expr(tokens, 5, false, cli, prog)? else {
lerror!(&loc, "Expected expression after unary token, found nothing");
bail!("")
};
@@ -470,7 +476,7 @@ fn parse_unop(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>> {
}))
}
fn parse_binop(tokens: &mut Vec<Token>, mut lhs: LocBox<Expr>, precedence: usize, cli: &CliArgs) -> Result<LocBox<Expr>> {
fn parse_binop(tokens: &mut Vec<Token>, mut lhs: LocBox<Expr>, precedence: usize, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
// TODO: https://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudocode
loop {
@@ -501,7 +507,7 @@ fn parse_binop(tokens: &mut Vec<Token>, mut lhs: LocBox<Expr>, precedence: usize
}
_ = tokens.pop();
let Some(rhs) = parse_expr(tokens, rp, false, cli)? else {break;};
let Some(rhs) = parse_expr(tokens, rp, false, cli, prog)? else {break;};
lhs = LocBox::new(&op_loc, Expr::BinOp {
typ: op,
left: Box::new(lhs),
@@ -515,14 +521,14 @@ fn parse_binop(tokens: &mut Vec<Token>, mut lhs: LocBox<Expr>, precedence: usize
}
pub fn parse_block(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<Block> {
pub fn parse_block(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<Block> {
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyL), "")?;
let mut items = Vec::new();
while !tokens.is_empty() {
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyR)) {
break;
}
if let Some(item) = parse_item(tokens, cli)? {
if let Some(item) = parse_item(tokens, cli, prog)? {
items.push(item);
} else {
break;

View File

@@ -14,18 +14,8 @@ type Result<T> = anyhow::Result<T>;
pub fn parse_program(mut tokens: Vec<Token>, cli: &CliArgs) -> Result<Program> {
let mut prog_body = Vec::new();
while !tokens.is_empty() {
if let Some(item) = parse_item(&mut tokens, cli)? {
prog_body.push(item);
} else {
break
}
}
Ok(Program {
ast: Block(prog_body),
let mut prog = Program {
ast: Block(prog_body.clone()),
enums: HashMap::new(),
functions: HashMap::new(),
member_functions: HashMap::new(),
@@ -36,15 +26,27 @@ pub fn parse_program(mut tokens: Vec<Token>, cli: &CliArgs) -> Result<Program> {
literals: HashMap::new(),
struct_literals: HashMap::new(),
scope: None,
curr_fn_args: HashMap::new()
})
curr_fn_args: HashMap::new(),
curr_struct: None,
included_files: Vec::new()
};
while !tokens.is_empty() {
if let Some(item) = parse_item(&mut tokens, cli, &mut prog)? {
prog_body.push(item);
} else {
break
}
}
prog.ast = Block(prog_body);
Ok(prog)
}
fn parse_item(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<Option<Ast>> {
if let Some(stat) = stat::parse_statement(tokens, cli)? {
fn parse_item(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<Option<Ast>> {
if let Some(stat) = stat::parse_statement(tokens, cli, prog)? {
return Ok(Some(Ast::Statement(stat)));
}
if let Some(expr) = expr::parse_expr(tokens, 0, true, cli)? {
if let Some(expr) = expr::parse_expr(tokens, 0, true, cli, prog)? {
return Ok(Some(Ast::Expr(expr)));
}
Ok(None)

View File

@@ -1,9 +1,11 @@
use std::str;
use anyhow::bail;
use crate::cli::CliArgs;
use crate::common::loc::LocBox;
use crate::{cli, lerror};
use crate::parser::ast::{TString, TokenType};
use crate::{cli, error, lerror};
use crate::parser::ast::{Program, TString, TokenType};
use crate::parser::ast::statement::Let;
use crate::parser::expr::parse_expr;
use crate::parser::{Delimiter, Ident, Keyword, Punctuation};
@@ -16,18 +18,18 @@ use super::ast::statement::{ConstVar, Enum, Function, Statement, StaticVar, Stru
type Result<T> = anyhow::Result<T>;
pub fn parse_statement(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<Option<LocBox<Statement>>> {
pub fn parse_statement(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<Option<LocBox<Statement>>> {
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Fn)) {
Ok(Some(parse_fn(tokens, cli)?))
Ok(Some(parse_fn(tokens, cli, prog)?))
} else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Type)) {
Ok(Some(parse_type_alias(tokens)?))
} else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Const)) {
Ok(Some(parse_constant(tokens, cli)?))
Ok(Some(parse_constant(tokens, cli, prog)?))
} else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Static)) {
Ok(Some(parse_static(tokens, cli)?))
Ok(Some(parse_static(tokens, cli, prog)?))
} else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Struct)) {
Ok(Some(parse_struct(tokens)?))
@@ -36,17 +38,17 @@ pub fn parse_statement(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<Option<
Ok(Some(parse_enum(tokens)?))
} else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Let)) {
Ok(Some(parse_let(tokens, cli)?))
Ok(Some(parse_let(tokens, cli, prog)?))
} else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Include)) {
Ok(Some(parse_include(tokens, cli)?))
Ok(Some(parse_include(tokens, cli, prog)?))
} else {
Ok(None)
}
}
fn parse_include(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Statement>> {
fn parse_include(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Statement>> {
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Include), "")?;
let TokenType::String(include_path) = utils::check_consume_or_err(tokens, TokenType::String(TString::default()), "")?.tt().clone() else {panic!()};
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
@@ -56,6 +58,13 @@ fn parse_include(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Statem
let path = cwd.join(p);
let path = path.join(&include_path.val);
if path.exists() {
// This is the first path that the include found the file, so if its already included,
// skip it. Because the context is per file specified in the cli (not includes), that
// means weird includes (probably) wont clash
if prog.included_files.contains(&path.to_string_lossy().to_string()) {
break;
}
prog.included_files.push(path.to_string_lossy().to_string().clone());
let data = std::fs::read_to_string(&path)?;
let mut tokens_imp = crate::tokeniser::tokenise(&data, &path.to_string_lossy().to_string())?;
tokens.append(&mut tokens_imp);
@@ -122,14 +131,14 @@ fn parse_struct(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
Ok(LocBox::new(kw.loc(), Statement::Struct(Struct { name, fields })))
}
fn parse_static(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Statement>> {
fn parse_static(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Statement>> {
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Static), "")?;
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
let typ = parse_type(tokens)?;
let eq = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?;
let Some(val) = parse_expr(tokens, 0, false, cli)? else {
let Some(val) = parse_expr(tokens, 0, false, cli, prog)? else {
lerror!(eq.loc(), "Expected expression found nothing");
bail!("")
};
@@ -137,7 +146,7 @@ fn parse_static(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Stateme
Ok(LocBox::new(kw.loc(), Statement::StaticVar(StaticVar { name, typ, val })))
}
fn parse_let(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Statement>> {
fn parse_let(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Statement>> {
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Let), "")?;
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
let mut typ = None;
@@ -146,7 +155,7 @@ fn parse_let(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Statement>
typ = Some(parse_type(tokens)?);
}
if let Some(eq) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Eq)) {
let Some(_val) = parse_expr(tokens, 0, false, cli)? else {
let Some(_val) = parse_expr(tokens, 0, false, cli, prog)? else {
lerror!(eq.loc(), "Expected expression found nothing");
bail!("")
};
@@ -155,7 +164,7 @@ fn parse_let(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Statement>
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
Ok(LocBox::new(kw.loc(), Statement::Let(Let{ name, typ, val })))
}
fn parse_constant(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Statement>> {
fn parse_constant(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Statement>> {
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Const), "")?;
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Fn)) {
@@ -165,7 +174,7 @@ fn parse_constant(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<State
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
let typ = parse_type(tokens)?;
let eq = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?;
let Some(val) = parse_expr(tokens, 0, false, cli)? else {
let Some(val) = parse_expr(tokens, 0, false, cli, prog)? else {
lerror!(eq.loc(), "Expected expression found nothing");
bail!("")
};
@@ -183,10 +192,10 @@ fn parse_type_alias(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
Ok(LocBox::new(kw.loc(), Statement::TypeAlias(TypeAlias { name, typ })))
}
fn parse_fn(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Statement>> {
fn parse_fn(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Statement>> {
error!("fnc");
// Just remove the kw since we checked it before
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Fn), "")?;
let mut struct_name = None;
let mut name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
// Check if this is a struct method
@@ -194,7 +203,7 @@ fn parse_fn(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Statement>>
struct_name = Some(name);
name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
}
let params = parse_fn_params(tokens)?;
let mut params_partial = parse_fn_params(tokens)?;
// Check for return type cause it optional
let mut ret_type = None;
@@ -203,13 +212,22 @@ fn parse_fn(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Statement>>
}
let body;
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
body = Some(parse_block(tokens, cli)?);
body = Some(parse_block(tokens, cli, prog)?);
} else {
// Check if its just a declaration
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
body = None;
}
let mut params = Vec::new();
if let Some(name) = &struct_name {
match params_partial.0 {
1 => params.push((Ident(String::from("self")), LocBox::new(kw.loc(), Type::Owned(name.clone()).as_ref()))),
2 => params.push((Ident(String::from("self")), LocBox::new(kw.loc(), Type::Owned(name.clone()).as_ref_mut()))),
_ => ()
}
}
params.append(&mut params_partial.1);
Ok(LocBox::new(kw.loc(), Statement::Fn(Function{
struct_name,
name,
@@ -222,10 +240,23 @@ fn parse_fn(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Statement>>
}
fn parse_fn_params(tokens: &mut Vec<Token>) -> Result<Vec<(Ident, LocBox<Type>)>> {
// usize is: 0 = no self, static; 1 = self, ref; 2 = self, mut ref
fn parse_fn_params(tokens: &mut Vec<Token>) -> Result<(usize, Vec<(Ident, LocBox<Type>)>)> {
let mut args = Vec::new();
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
let mut dis = 0;
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Ampersand)) {
if let Some(_) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Mut)) {
_ = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Self_), "Expected 'this' keyword after &mut")?;
dis = 1
} else {
_ = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Self_), "Expected 'this' keyword after &")?;
dis = 2
}
}
while !tokens.is_empty() {
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
break;
@@ -242,7 +273,7 @@ fn parse_fn_params(tokens: &mut Vec<Token>) -> Result<Vec<(Ident, LocBox<Type>)>
}
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
Ok(args)
Ok((dis, args))
}

View File

@@ -1,6 +1,6 @@
use anyhow::{Result, bail};
use crate::{cli::CliArgs, common::loc::LocBox, lerror, parser::{Delimiter, ast::{expr::Expr, literal::Literal}}, tokeniser::Token};
use crate::{cli::CliArgs, common::loc::LocBox, lerror, parser::{Delimiter, ast::{Program, expr::Expr, literal::Literal}}, tokeniser::Token, validator::predefined::get_builtin_from_name};
use super::{ast::{typ::Type, TokenType}, expr::parse_expr, utils, Keyword, Punctuation};
@@ -25,7 +25,7 @@ pub fn parse_type(tokens: &mut Vec<Token>) -> Result<LocBox<Type>> {
}
let itm_typ = parse_type(tokens)?;
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Semi)) {
let count = parse_expr(tokens, 0, false, &CliArgs::default())?.unwrap();
let count = parse_expr(tokens, 0, false, &CliArgs::default(), &mut Program::default())?.unwrap();
match count.inner() {
Expr::Literal(_, Literal::Number(_)) |
@@ -47,7 +47,11 @@ pub fn parse_type(tokens: &mut Vec<Token>) -> Result<LocBox<Type>> {
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
} else {
let ident = utils::check_consume_or_err(tokens, TokenType::ident(""), "a")?;
typ = Type::Owned(ident.tt().unwrap_ident());
if let Some(t) = get_builtin_from_name(&ident.tt().unwrap_ident().0) {
typ = t;
} else {
typ = Type::Owned(ident.tt().unwrap_ident());
}
if let None = loc {
loc = Some(ident.loc().clone());
}

View File

@@ -56,7 +56,7 @@ pub fn compile(cli: &CliArgs, programs: Vec<(PathBuf, Program)>) -> anyhow::Resu
objs.push(obj_p);
}
let out = Path::new(&cli.output);
target.link(objs, &out)?;
target.link(objs, &out, &cli.linker_args)?;
Ok(())
}
@@ -71,5 +71,5 @@ pub trait Target {
}
fn write_code(&mut self, program: &Program, f: &mut File) -> anyhow::Result<()>;
fn compile(&mut self, from: &Path, to: &Path) -> anyhow::Result<()>;
fn link(&mut self, from: Vec<PathBuf>, to: &Path) -> anyhow::Result<()>;
fn link(&mut self, from: Vec<PathBuf>, to: &Path, extra_args: &Vec<String>) -> anyhow::Result<()>;
}

View File

@@ -1,4 +1,4 @@
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 crate::{common::{Loc, loc::LocBox}, parser::ast::{Ast, Ident, Program, Punctuation, expr::{Expr, IfBranchExpr, Path}, literal::Literal, statement::{ConstDataTyp, Function, Statement, get_constant_data_as_bytes}, typ::Type}, targets::Target};
use std::{collections::HashMap, fs::File, io::Write, process};
pub struct AsmGen;
@@ -54,13 +54,16 @@ impl Target for AsmGen {
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, extra_args: &Vec<String>) -> anyhow::Result<()> {
let mut cmd = std::process::Command::new("ld");
cmd.args(extra_args);
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());
}
@@ -79,7 +82,6 @@ pub enum VarMapT {
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
@@ -93,7 +95,6 @@ impl FunctionCtx {
Self {
vars: Default::default(),
stack_offset: 0,
used_registers: 0,
loop_level: 0,
if_level: 0,
cmp_level: 0,
@@ -171,6 +172,7 @@ impl AsmGen {
} else {
func.name.to_string()
};
info!("writing norm function: {name}");
writeln!(f, "{}: ; {} {}", name, loc, func.get_full_name_pretty())?;
if body.0.is_empty() {
writeln!(f, " ret")?;
@@ -216,6 +218,7 @@ impl AsmGen {
}
}
} else {
info!("writing function stub: {}", func.name);
writeln!(f, " ret")?;
}
writeln!(f, "\n")?;
@@ -245,21 +248,38 @@ impl AsmGen {
}
pub fn write_expr(&self, program: &Program, f: &mut impl Write, fc: &mut FunctionCtx, expr: &Expr) -> anyhow::Result<()> {
match expr {
Expr::Cast { .. } => (),
Expr::Cast { left, .. } => self.write_expr(program, f, fc, left.inner())?,
Expr::Literal(id, val) => {
match val {
Literal::Array{ items, item_size } => {
dbg!(&val);
writeln!(f, " lea r10, [rel mcl_lit_{id}] ; arr")?;
for (i, item) in items.iter().enumerate() {
self.write_expr(program, f, fc, item.inner())?;
let offset = i * item_size.unwrap();
writeln!(f, " mov [r10+{offset}], rax")?;
}
writeln!(f, " mov rax, r10")?;
}
Literal::ArrayRepeat { val, item_size, count} => {
writeln!(f, " lea r10, [rel mcl_lit_{id}] ; arr repeat")?;
for i in 0..*count {
self.write_expr(program, f, fc, val.inner())?;
let offset = i * item_size.unwrap();
writeln!(f, " mov [r10+{offset}], rax")?;
}
writeln!(f, " mov rax, r10")?;
},
Literal::Ident(_) => unreachable!(),
Literal::Array(_) |
Literal::String(_) |
Literal::ArrayRepeat { .. } => {
writeln!(f, " lea rax, [rel mcl_lit_{id}]")?;
Literal::String(_) => {
writeln!(f, " lea rax, [rel mcl_lit_{id}] ; str")?;
}
Literal::Bool(v) => {
writeln!(f, " mov rax, {} ; {}", *v as u8, v)?;
}
Literal::Number(_) |
Literal::Char(_) => {
writeln!(f, " mov rax, [rel mcl_lit_{id}]")?;
writeln!(f, " mov rax, [rel mcl_lit_{id}] ; num/char")?;
}
}
}
@@ -271,15 +291,30 @@ impl AsmGen {
let offset = strct_t.inner().get_offset_of(program, name)?;
writeln!(f, " mov [r10+{offset}], rax")?;
}
writeln!(f, " mov rax, r10")?;
}
Expr::PtrFieldAccess { left, right, offset } => {
Expr::PtrFieldAccess { left, right, offset, l_typ, r_typ } => {
self.write_expr(program, f, fc, left.clone().unwrap().inner())?;
writeln!(f, " add rax, {offset} ; ->{:?}", right.inner())?;
dbg!(&l_typ);
if l_typ.clone().unwrap().should_deref_pointer(program) {
writeln!(f, " mov rax, [rel rax] ; rqawr ->{:?}", right.inner())?;
}
if *offset > 0 {
writeln!(f, " add rax, {offset} ; ->{:?}", right.inner())?;
}
if let Some(t) = r_typ && t.is_numeric(program) {
writeln!(f, " mov rax, [rax] ; ->{:?}", right.inner())?;
}
},
Expr::FieldAccess { left, right, offset } => {
Expr::FieldAccess { left, right, offset, l_typ: _, r_typ } => {
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())?;
if *offset > 0 {
writeln!(f, " add rax, {offset} ; .{:?}", right.inner())?;
}
if let Some(t) = r_typ && t.is_numeric(program) {
writeln!(f, " mov rax, [rax] ; .{:?}", right.inner())?;
}
},
Expr::InfLoop { body } => {
let sl = fc.inc_loop_level();
@@ -387,6 +422,35 @@ impl AsmGen {
fc.is_last_item = false;
}
}
Expr::MethodCall { left, strct, params } => {
let method_name;
match left.inner() {
Expr::FieldAccess { left, right, .. } |
Expr::PtrFieldAccess { left, right, .. } => {
self.write_expr(program, f, fc, left.clone().unwrap().inner())?;
match right.inner() {
Expr::Path(path) => {
method_name = path.0.first().unwrap().clone();
}
_ => unreachable!()
}
}
_ => unreachable!()
}
let reg = fc.register_id_to_str(0);
writeln!(f, " mov {reg}, rax")?;
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+1);
writeln!(f, " mov {reg}, rax")?;
} else {
writeln!(f, " push rax")?;
}
}
writeln!(f, " call {}", Path(vec![strct.clone().unwrap(), method_name]).display_asm_compat())?;
}
Expr::Call { path, params } => {
for (i, param) in params.0.iter().enumerate() {
self.write_expr(program, f, fc, param.inner())?;
@@ -550,16 +614,29 @@ impl AsmGen {
match var {
VarMapT::Stack(offset, typ) => {
match typ {
Type::Builtin { .. } => writeln!(f, " lea rax, [rsp + {offset}]")?,
_ => writeln!(f, " mov rax, [rsp + {offset}]")?,
/*Type::Builtin { .. } => {
writeln!(f, " mov rax, [rsp+{offset}] ; var {ident}")?;
writeln!(f, " mov rax, [rax] ; var {ident}")?;
}*/
_ => writeln!(f, " mov rax, [rsp+{offset}] ; var {ident}")?,
}
}
}
} else if let Some(_) = program.get_const_var(&ident) {
writeln!(f, " lea rax, [rel {ident}]")?;
writeln!(f, " lea rax, [rel {ident}] ; const {ident}")?;
} else {
panic!()
}
}
Expr::Self_ { strct: _ } => {
if let Some(var) = fc.get(&Ident::new("self")) {
match var {
VarMapT::Stack(offset, _) => {
writeln!(f, " mov rax, [rsp+{offset}] ; self")?;
}
}
}
}
v => unreachable!("{v:?}")
}
@@ -585,7 +662,7 @@ impl AsmGen {
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)?;
let bytes = get_constant_data_as_bytes(program, constant.val.clone(), false, false)?;
fn x(bytes: &ConstDataTyp, f: &mut File) -> anyhow::Result<()> {
match bytes {
ConstDataTyp::Array(arr) => {
@@ -622,7 +699,7 @@ impl AsmGen {
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)?;
let bytes = get_constant_data_as_bytes(program, statc.val.clone(), false, false)?;
fn x(bytes: &ConstDataTyp, f: &mut File) -> anyhow::Result<()> {
match bytes {
ConstDataTyp::Array(arr) => {
@@ -688,11 +765,11 @@ impl AsmGen {
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)?;
w(get_constant_data_as_bytes(program, 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)?;
w(get_constant_data_as_bytes(program, LocBox::new(&Loc::default(), Expr::Struct(lit.0.clone(), lit.1.clone())), false, false)?, f)?;
}
Ok(())
}

View File

@@ -1,36 +1,61 @@
; 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"
__INTERNAL_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
__INTERNAL_syscall0:
mov rax, rdi
syscall
ret
__INTERNAL_syscall1:
mov rax, rdi
mov rdi, rsi
syscall
ret
__INTERNAL_syscall2:
mov rax, rdi
mov rdi, rsi
mov rsi, rdx
syscall
ret
__INTERNAL_syscall3:
mov rax, rdi
mov rdi, rsi
mov rsi, rdx
mov rdx, rcx
syscall
ret
__INTERNAL_syscall4:
mov rax, rdi
mov rdi, rsi
mov rsi, rdx
mov rdx, rcx
mov r10, r8
syscall
ret
__INTERNAL_syscall5:
mov rax, rdi
mov rdi, rsi
mov rsi, rdx
mov rdx, rcx
mov r10, r8
mov r8, r9
syscall
ret
__INTERNAL_syscall6:
mov rax, rdi
mov rdi, rsi
mov rsi, rdx
mov rdx, rcx
mov r10, r8
mov r8, r9
mov r9, [rsp + 8]
syscall
ret
global _start
_start:
xor rax, rax

View File

@@ -30,6 +30,12 @@ impl Token {
pub fn tt(&self) -> &TokenType {
&self.tt
}
pub fn new_test(tt: TokenType) -> Self {
Self {
loc: Loc::default(),
tt
}
}
}
@@ -81,6 +87,9 @@ pub fn tokenise(s: &str, file_p: &str) -> anyhow::Result<Vec<Token>> {
buf.push(*c);
last = *c;
}
let buf = buf
.replace("\\n", "\n")
.replace("\\r", "\r");
tokens.push(Token::new(TokenType::string(&buf, false), &loc));
}
'\'' => {
@@ -251,6 +260,7 @@ lazy_static::lazy_static!(
("return", TokenType::Keyword(Keyword::Return)),
("loop", TokenType::Keyword(Keyword::Loop)),
("as", TokenType::Keyword(Keyword::As)),
("self", TokenType::Keyword(Keyword::Self_)),
("{", TokenType::Delim(Delimiter::CurlyL)),
("}", TokenType::Delim(Delimiter::CurlyR)),
("[", TokenType::Delim(Delimiter::SquareL)),

View File

@@ -16,6 +16,9 @@ impl Ident {
pub fn as_path(self) -> Path {
Path(vec![self])
}
pub fn new(s: impl ToString) -> Self {
Self(s.to_string())
}
}
@@ -71,7 +74,7 @@ pub enum Keyword {
Type, While, For, Break, Continue,
Let, Const, Mut, Static,
True, False, Include, Extern, Return,
As, Loop
As, Loop, Self_
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]

View File

@@ -2,7 +2,7 @@ use std::{collections::HashMap, panic};
use anyhow::bail;
use crate::{common::{Loc, loc::LocBox}, parser::ast::{Ast, Program, Punctuation, Scope, TokenType, expr::*, statement::*, typ::Type}, validator::predefined::get_builtin_from_name};
use crate::{common::{Loc, loc::LocBox}, parser::ast::{Ast, Program, Punctuation, Scope, TokenType, expr::*, literal::Literal, statement::*, typ::Type}, validator::predefined::get_builtin_from_name};
pub mod predefined;
@@ -57,14 +57,14 @@ fn validate_stat(prog: &mut Program, stat: &mut LocBox<Statement>, current_state
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();
let val_t = validate_expr(prog, val)?.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 {
let Some(t) = validate_expr(prog, val)? else {
lerror!(val.loc(), "Expected a type, go none");
bail!("");
};
@@ -79,7 +79,7 @@ fn validate_stat_let(prog: &mut Program, lt: &mut Let) -> anyhow::Result<()> {
fn validate_ast(prog: &mut Program, ast: &mut Ast) -> anyhow::Result<Option<Type>> {
match ast {
Ast::Expr(expr) => {
validate_expr(prog, expr.inner_mut())
validate_expr(prog, expr)
}
Ast::Statement(stat) => {
validate_stat(prog, stat, CurrentState::InFunc)?;
@@ -89,12 +89,23 @@ fn validate_ast(prog: &mut Program, ast: &mut Ast) -> anyhow::Result<Option<Type
}
pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Option<Type>> {
match expr {
pub fn validate_expr(prog: &mut Program, expr: &mut LocBox<Expr>) -> anyhow::Result<Option<Type>> {
match expr.inner_mut() {
Expr::Self_ { strct } => {
if let Some(name) = &prog.curr_struct {
*strct = Some(name.clone());
let mut t = Type::Owned(name.clone());
t.convert_owned_to_real_type(prog);
Ok(Some(t))
} else {
lerror!(expr.loc(), "");
bail!("")
}
}
Expr::Break | Expr::Continue => Ok(None),
Expr::Return(ret) => {
if let Some(expr) = &mut**ret {
validate_expr(prog, expr.inner_mut())
validate_expr(prog, expr)
} else {
Ok(None)
}
@@ -107,7 +118,7 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
validate_type(prog, &LocBox::new(&Loc::default(), typ.clone()))?;
for field in strct.fields.iter_mut() {
validate_expr(prog, field.1.inner_mut())?;
validate_expr(prog, field.1)?;
}
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
@@ -122,7 +133,7 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
Ok(Some(typ))
},
Expr::If(ifs) => {
validate_expr(prog, ifs.test.inner_mut())?;
validate_expr(prog, &mut ifs.test)?;
for item in ifs.body.0.iter_mut() {
validate_ast(prog, item)?;
}
@@ -132,7 +143,7 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
// TODO: Path hell TBD
Ok(Some(Type::Owned(path.0.last().unwrap().clone())))
}
Expr::Group(group) => validate_expr(prog, group.inner_mut()),
Expr::Group(group) => validate_expr(prog, group),
Expr::Call { path, params } => {
let loc = path.loc().clone();
let Expr::Path(path) = path.inner_mut() else {
@@ -145,14 +156,16 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
match f {
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();
let mut ft = func.inner().params[i].1.inner().clone();
let t = validate_expr(prog, param)?.clone();
match t {
Some(t) => {
Some(mut t) => {
t.convert_owned_to_real_type(prog);
ft.convert_owned_to_real_type(prog);
let t = t.get_variable_type(prog)?;
let ft = ft.get_variable_type(prog)?;
if t != ft {
lerror!(param.loc(), "expected {ft:?}, got {t:?}");
lerror!(param.loc(), "expected {ft}, got {t}");
bail!("owo")
}
}
@@ -181,7 +194,7 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
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();
let t = validate_expr(prog, param)?.clone();
match t {
Some(t) => {
if t.get_absolute_value(prog)? != ft.get_absolute_value(prog)? {
@@ -214,14 +227,14 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
}
Expr::MethodCall { left, params } => {
Expr::MethodCall { left, strct, 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())?;
var_t = validate_expr(prog, &mut left.clone().unwrap())?;
let name = validate_expr(prog, right)?;
match name.unwrap() {
Type::Owned(name) => method_name = name,
_ => unreachable!()
@@ -233,12 +246,12 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
let Type::Owned(struct_name) = var_t.unwrap().get_absolute_value(prog)? else {
panic!("fml");
};
*strct = Some(struct_name.clone());
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 t = validate_expr(prog, param)?;
let ft = func.inner_mut().params[i].1.inner_mut();
if t.as_ref() != Some(ft) {
lerror!(param.loc(), "expected {ft}, got {}", t.unwrap());
@@ -254,15 +267,15 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
}
Expr::ForLoop { init, test, on_loop, body } => {
let _ = validate_ast(prog, init)?;
let _ = validate_expr(prog, test.inner_mut())?;
let _ = validate_expr(prog, on_loop.inner_mut())?;
let _ = validate_expr(prog, test)?;
let _ = validate_expr(prog, on_loop)?;
for item in body.0.iter_mut() {
let _ = validate_ast(prog, item)?;
}
Ok(None)
},
Expr::Cast { left, right } => {
validate_expr(prog, left.inner_mut())?;
validate_expr(prog, left)?;
Ok(Some(right.inner_mut().clone()))
}
Expr::Literal(id, lit) => {
@@ -273,15 +286,32 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
(*lit).hash(&mut hasher);
let hash = hasher.finish();
*id = hash.to_string();
match lit {
Literal::Array { items, item_size } => {
for item in items {
let typ = validate_expr(prog, item)?;
if item_size.is_none() {
*item_size = Some(typ.unwrap().get_variable_type(prog)?.size_of(prog)?);
}
}
},
Literal::ArrayRepeat { val, item_size, count: _ } => {
let typ = validate_expr(prog, val)?;
*item_size = Some(typ.unwrap().size_of(prog)?);
}
_ => (),
}
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_mut())?.unwrap().get_absolute_value(prog)?;
let t = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
if !t.is_bool(prog) {
lerror!(right.loc(), "Expected bool, got {t}");
}
@@ -289,7 +319,7 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
}
Punctuation::Minus |
Punctuation::Plus => {
let t = validate_expr(prog, right.inner_mut())?.unwrap().get_absolute_value(prog)?;
let t = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
if !t.is_numeric(prog) {
lerror!(right.loc(), "Expected number, got {t}");
}
@@ -297,11 +327,11 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
}
Punctuation::Ampersand => {
let t = validate_expr(prog, right.inner_mut())?.unwrap().get_absolute_value(prog)?;
let t = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
Ok(Some(Type::Ref { inner: Box::new(t), mutable: false }))
}
Punctuation::Star => {
let t = validate_expr(prog, right.inner_mut())?.unwrap().get_absolute_value(prog)?;
let t = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
if !t.is_ptr(prog) {
lerror!(right.loc(), "Expected pointer, got {t}");
}
@@ -328,8 +358,8 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
Punctuation::Shr => {
let t1_s;
let t2_s;
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)?;
let t1 = validate_expr(prog, left)?.unwrap().get_absolute_value(prog)?;
let t2 = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
if !(t1.is_numeric(prog) || t1.is_ptr(prog)) {
lerror!(right.loc(), "Expected number, got {t1}");
}
@@ -359,8 +389,8 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
Punctuation::Ge |
Punctuation::AndAnd |
Punctuation::OrOr => {
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)?;
let t1 = validate_expr(prog, left)?.unwrap().get_absolute_value(prog)?;
let t2 = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
if !(t1.is_numeric(prog) || t1.is_ptr(prog)) {
lerror!(right.loc(), "Expected number or pointer, got {t1}");
}
@@ -381,9 +411,9 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
Punctuation::AndEq |
Punctuation::OrEq |
Punctuation::XorEq => {
let var = validate_expr(prog, left.inner_mut())?.unwrap();
let var = validate_expr(prog, left)?.unwrap();
let var_t = var.get_absolute_value(prog)?;
let val_t = validate_expr(prog, right.inner_mut())?.unwrap().get_absolute_value(prog)?;
let val_t = validate_expr(prog, right)?.unwrap().get_absolute_value(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}");
@@ -394,9 +424,9 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
Ok(None)
}
Punctuation::Eq => {
let var = validate_expr(prog, left.inner_mut())?.unwrap();
let var = validate_expr(prog, left)?.unwrap();
let var_t = var.get_absolute_value(prog)?;
let val_t = validate_expr(prog, right.inner_mut())?.unwrap().get_absolute_value(prog)?;
let val_t = validate_expr(prog, right)?.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}");
@@ -409,7 +439,7 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
}
},
Expr::ArrayIndex { name, index } => {
let left = validate_expr(prog, name.inner_mut())?;
let left = validate_expr(prog, name)?;
let Some(left) = left else {
lerror!(name.loc(), "expected value, got nothing, cannot index nothing");
bail!("")
@@ -451,22 +481,61 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
}
f(prog, &left, name.loc(), &index)
},
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();
Expr::PtrFieldAccess { left, right, offset, l_typ, r_typ } => {
let left = validate_expr(prog, &mut left.clone().unwrap())?;
let right = validate_expr(prog, &mut right.clone())?.unwrap();
let Type::Owned(right) = right else {
panic!()
};
match left.unwrap().get_absolute_value(prog)? {
let mut left = left.unwrap();
left.convert_owned_to_real_type(prog);
*l_typ = Some(Box::new(left.clone()));
match left {
Type::Ref { inner, mutable: _ } => {
match *inner {
Type::Owned(name) => {
if let Some(strct) = prog.structs.get(&name) {
for field in strct.inner().fields.iter() {
if field.0 == right {
*r_typ = Some(Box::new(field.1.inner().clone()));
*offset = strct.inner().clone().get_offset_of(prog, &right)?;
info!("Offset {right:?} = {offset}");
return Ok(Some(field.1.inner().clone()));
}
}
} else {
panic!("couldnt find struct {name}");
}
}
v => panic!("{v:?}"),
}
}
v => panic!("{v:?}"),
}
Ok(None)
},
Expr::FieldAccess { left, right, offset, l_typ, r_typ } => {
let left = validate_expr(prog, &mut left.clone().unwrap())?;
let right = validate_expr(prog, &mut right.clone())?.unwrap();
let Type::Owned(right) = right else {
panic!()
};
let mut left = left.unwrap();
left.convert_owned_to_real_type(prog);
*l_typ = Some(Box::new(left.clone()));
match left {
Type::Owned(name) => {
if let Some(strct) = prog.structs.get(&name) {
for field in strct.inner().fields.iter() {
if field.0 == right {
*r_typ = Some(Box::new(field.1.inner().clone()));
*offset = strct.inner().clone().get_offset_of(prog, &right)?;
return Ok(Some(field.1.inner().clone()));
}
}
} else {
panic!("couldnt find struct {name}");
}
}
v => panic!("{v:?}"),
@@ -475,7 +544,7 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
Ok(None)
},
Expr::WhileLoop { test, body } => {
let _ = validate_expr(prog, test.inner_mut())?;
let _ = validate_expr(prog, test.as_mut())?;
for item in body.0.iter_mut() {
let _ = validate_ast(prog, item)?;
}
@@ -494,6 +563,8 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
fn validate_fn(prog: &mut Program, func: &mut Function) -> anyhow::Result<()> {
prog.scope = Some(Scope::default());
prog.curr_struct = func.struct_name.clone();
for param in &func.params {
let t = validate_type(prog, &param.1)?;
prog.curr_fn_args.insert(param.0.clone(), LocBox::new(&Loc::default(), t.clone()));
@@ -504,6 +575,7 @@ fn validate_fn(prog: &mut Program, func: &mut Function) -> anyhow::Result<()> {
}
}
prog.curr_struct = None;
Ok(())
}
@@ -600,9 +672,9 @@ fn check_that_types_exist_for_items(prog: &mut Program, items: &Vec<Ast>) -> any
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::SizedArray { .. } |
Type::UnsizedArray { .. } |
Type::Ref { .. } => Ok(typ.clone()),
Type::Owned(ident) => {
if let Some(builtin) = get_builtin_from_name(&ident.0) {
Ok(builtin)

View File

@@ -10,43 +10,130 @@ pub const SIZE: usize = 8;
#[cfg(target_arch="x86")]
pub const SIZE: usize = 4;
pub struct BuiltinType;
impl BuiltinType {
pub fn void() -> Type {
Type::new_builtin("void", 0, false)
}
pub fn usize() -> Type {
Type::new_builtin("usize", SIZE as u8, false)
}
pub fn isize() -> Type {
Type::new_builtin("isize", SIZE as u8, true)
}
pub fn u8() -> Type {
Type::new_builtin("u8", 1, false)
}
pub fn u16() -> Type {
Type::new_builtin("u16", 2, false)
}
pub fn u32() -> Type {
Type::new_builtin("u32", 4, false)
}
pub fn u64() -> Type {
Type::new_builtin("u64", 8, false)
}
pub fn i8() -> Type {
Type::new_builtin("i8", 1, true)
}
pub fn i16() -> Type {
Type::new_builtin("i16", 2, true)
}
pub fn i32() -> Type {
Type::new_builtin("i32", 4, true)
}
pub fn i64() -> Type {
Type::new_builtin("i64", 8, true)
}
pub fn bool() -> Type {
Type::new_builtin("bool", 1, true)
}
pub fn char() -> Type {
Type::new_builtin("char", 1, true)
}
}
lazy_static!(
pub static ref TYPES_RAW: HashMap<&'static str, (usize, bool)> = [
("void", (0, false)),
("usize", (SIZE, false)),
("isize", (SIZE, true)),
("u8", (1, false)),
("u16", (2, false)),
("u32", (4, false)),
("u64", (8, false)),
("i8", (1, true)),
("i16", (2, true)),
("i32", (4, true)),
("i64", (8, true)),
("bool", (1, true)),
].into();
pub static ref FUNCTIONS: HashMap<&'static str, (Vec<(&'static str, &'static str)>, &'static str)> = [
("__INTERNAL_syscall", (vec![
("arg_count", "u8"),
("sc_num", "u8"),
("args", "&[&void]")
], "usize")),
pub static ref TYPES_RAW: Vec<Type> = [
BuiltinType::void(),
BuiltinType::usize(),
BuiltinType::isize(),
BuiltinType::u8(),
BuiltinType::u16(),
BuiltinType::u32(),
BuiltinType::u64(),
BuiltinType::i8(),
BuiltinType::i16(),
BuiltinType::i32(),
BuiltinType::i64(),
BuiltinType::bool(),
BuiltinType::char()
].to_vec();
pub static ref FUNCTIONS: HashMap<&'static str, (Vec<(&'static str, Type)>, Type)> = [
("__INTERNAL_syscall0", (vec![
("sc_num", BuiltinType::usize()),
], BuiltinType::usize())),
("__INTERNAL_syscall1", (vec![
("sc_num", BuiltinType::usize()),
("arg0", BuiltinType::void().as_ref()),
], BuiltinType::usize())),
("__INTERNAL_syscall2", (vec![
("sc_num", BuiltinType::usize()),
("arg0", BuiltinType::void().as_ref()),
("arg1", BuiltinType::void().as_ref()),
], BuiltinType::usize())),
("__INTERNAL_syscall3", (vec![
("sc_num", BuiltinType::usize()),
("arg0", BuiltinType::void().as_ref()),
("arg1", BuiltinType::void().as_ref()),
("arg2", BuiltinType::void().as_ref()),
], BuiltinType::usize())),
("__INTERNAL_syscall4", (vec![
("sc_num", BuiltinType::usize()),
("arg0", BuiltinType::void().as_ref()),
("arg1", BuiltinType::void().as_ref()),
("arg2", BuiltinType::void().as_ref()),
("arg3", BuiltinType::void().as_ref()),
], BuiltinType::usize())),
("__INTERNAL_syscall5", (vec![
("sc_num", BuiltinType::usize()),
("arg0", BuiltinType::void().as_ref()),
("arg1", BuiltinType::void().as_ref()),
("arg2", BuiltinType::void().as_ref()),
("arg3", BuiltinType::void().as_ref()),
("arg4", BuiltinType::void().as_ref()),
], BuiltinType::usize())),
("__INTERNAL_syscall6", (vec![
("sc_num", BuiltinType::usize()),
("arg0", BuiltinType::void().as_ref()),
("arg1", BuiltinType::void().as_ref()),
("arg2", BuiltinType::void().as_ref()),
("arg3", BuiltinType::void().as_ref()),
("arg4", BuiltinType::void().as_ref()),
("arg5", BuiltinType::void().as_ref()),
], BuiltinType::usize())),
].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 get_builtin_from_name(type_name: &str) -> Option<Type> {
for t in TYPES_RAW.iter() {
match t {
Type::Builtin { name, .. } if name.as_str() == type_name => {
return Some(t.clone());
}
Type::Builtin { .. } => (),
_ => unreachable!()
}
}
None
}
pub fn load_builtin(prog: &mut Program) {
let loc = Loc::new("(internal)", 0, 0);
for (name, (size, signed)) in TYPES_RAW.iter() {
for t in TYPES_RAW.iter() {
let Type::Builtin { name, size, signed } = t else {unreachable!()};
prog.types.insert(
Ident(name.to_string()),
Ident(name.clone()),
LocBox::new(&loc, Type::Builtin{ name: name.to_string(), size: *size as u8, signed: *signed })
);
}
@@ -54,15 +141,12 @@ pub fn load_builtin(prog: &mut Program) {
for (name, (args, ret_typ)) in FUNCTIONS.iter() {
let mut params = Vec::new();
let mut ret_type = None;
if ret_typ.len() > 0 {
let mut ret_t_tokens = crate::tokeniser::tokenise(&ret_typ, "(internal)").unwrap();
let typ = parse_type(&mut ret_t_tokens).unwrap();
ret_type = Some(LocBox::new(&Loc::default(), typ.inner().clone()));
if !ret_typ.is_void() {
ret_type = Some(LocBox::new(&Loc::default(), ret_typ.clone()));
}
for (name, typ) in args {
let mut tokens = crate::tokeniser::tokenise(&typ, "(internal)").unwrap();
let typ = parse_type(&mut tokens).unwrap();
params.push((Ident(name.to_string()), LocBox::new(&Loc::new("(internal)", 0, 0), typ.inner().clone())));
params.push((Ident(name.to_string()), LocBox::new(&Loc::new("(internal)", 0, 0), typ.clone())));
}
let f = Function {

2
std/core.mcl Normal file
View File

@@ -0,0 +1,2 @@
include "std/str.mcl";
include "std/io/mod.mcl";

12
std/io/mod.mcl Normal file
View File

@@ -0,0 +1,12 @@
fn write_str(fd: usize, s: &str) -> usize {
return __INTERNAL_syscall3(1, fd as &void, s->inner as &void, s->len as &void);
}
fn puts(s: &str) {
write_str(1, s);
}
fn eputs(s: &str) {
write_str(2, s);
}

107
std/lib/beaker.mcl Normal file
View File

@@ -0,0 +1,107 @@
const MAX_CONTEXT_VARS: usize = 32;
const MAX_KEY_LEN: usize = 64;
const MAX_VALUE_LEN: usize = 256;
const MAX_PATH_LEN: usize = 256;
const MAX_HANDLERS: usize = 32;
const BUFFER_SIZE: usize = 4096;
const MAX_URL_PARAMS: usize = 16;
const MAX_COOKIES: usize = 10;
const MAX_OUTER_ARRAY_ITEMS: usize = 100;
const MAX_INNER_ARRAY_ITEMS: usize = 200;
const TEMPLATES_DIR: &str = "templates/";
const STATIC_DIR: &str = "static/";
/*
enum ContextType {
CONTEXT_TYPE_STRING,
CONTEXT_TYPE_STRING_ARRAY,
CONTEXT_TYPE_STRING_2D_ARRAY,
};
*/
type ContextType = i32;
struct CV_array_data {
values: &&char,
count: i32,
}
struct CV_2d_array_data {
values: &&&char,
count: i32,
}
struct ContextVarStr {
key: char[MAX_KEY_LEN],
typ: ContextType,
value: char[MAX_VALUE_LEN]
}
struct ContextVarArr {
key: char[MAX_KEY_LEN],
typ: ContextType,
value: CV_array_data,
}
struct ContextVar2dArr {
key: char[MAX_KEY_LEN],
typ: ContextType,
value: CV_2d_array_data,
}
struct ContextVar {
key: char[MAX_KEY_LEN],
typ: ContextType,
value: char[MAX_VALUE_LEN]
}
struct TemplateContext {
vars: ContextVar[MAX_CONTEXT_VARS],
count: i32,
}
struct UrlParam {
key: char[MAX_KEY_LEN],
value: char[MAX_VALUE_LEN],
}
struct UrlParams {
params: UrlParam[MAX_URL_PARAMS],
count: i32
}
struct Cookie {
name: char[MAX_KEY_LEN],
value: char[MAX_VALUE_LEN],
expires: char[MAX_VALUE_LEN],
path: char[MAX_KEY_LEN],
http_only: bool,
secure: bool
}
type RequestHandler = fn(params: &UrlParams);
struct RouteHandler {
path: char[MAX_PATH_LEN],
handler: RequestHandler
}
extern fn new_context() -> TemplateContext;
extern fn context_set(ctx: &mut TemplateContext, key: &cstr, value: &cstr);
extern fn context_set_string_array(ctx: &mut TemplateContext, key: &cstr, values: &cstr, count: i32);
extern fn context_set_array_of_arrays(ctx: &mut TemplateContext, key: &cstr, values: &cstr[][], outer_count: i32, inner_count: i32);
extern fn free_context(ctx: &mut TemplateContext);
extern fn render_template(template_file: &cstr, ctx: &mut TemplateContext) -> &cstr;
extern fn send_response(html: &cstr);
extern fn send_redirect(location: &cstr);
extern fn set_cookie(name: &cstr, value: &cstr, expires: &cstr, path: &cstr, http_only: bool, secure: bool);
extern fn get_cookie(name: &cstr) -> &cstr;
extern fn set_handler(path: &cstr, handler: RequestHandler);
extern fn parse_request_url(request_buffer: &cstr, params: &UrlParams) -> &cstr;
extern fn get_mime_type(file_path: &cstr) -> &cstr;
extern fn serve_static_file(request_path_relative_to_static: &cstr) -> bool;
extern fn beaker_run(ip: &cstr, port: i32) -> i32;

14
std/str.mcl Normal file
View File

@@ -0,0 +1,14 @@
type cstr = [u8];
struct str {
len: usize,
inner: &cstr
}
fn str.len(&self) -> usize {
return self.len;
}

BIN
test

Binary file not shown.

View File

@@ -7,9 +7,9 @@ struct Foo {
b: &str
}
fn Foo.new(a: usize, b: &str) -> &Foo {
fn Foo.new(a1: usize, b: &str) -> &Foo {
return &Foo {
a: a,
a: a1,
b: b
};
}
@@ -31,7 +31,7 @@ fn main() -> i32 {
}
for (let i = 0; i < 10; i += 1) {
print("nyaaa");
puts("nyaaa");
if (i > 7) {
break;
} else {

BIN
test.o

Binary file not shown.

6
test2.mcl Normal file
View File

@@ -0,0 +1,6 @@
// include "std/core.mcl";
include "beaker.mcl";
fn main() -> i32 {
//puts("owo\n");
}

1
tests/mod.rs Normal file
View File

@@ -0,0 +1 @@
mod parser;

3
tests/parser/expr.rs Normal file
View File

@@ -0,0 +1,3 @@

3
tests/parser/mod.rs Normal file
View File

@@ -0,0 +1,3 @@
mod expr;
mod stat;
mod typ;

0
tests/parser/stat.rs Normal file
View File

190
tests/parser/typ.rs Normal file
View File

@@ -0,0 +1,190 @@
use std::vec;
use mclangc::{common::{Loc, loc::LocBox}, parser::{self, ast::{Ident, Keyword, Program, Punctuation, TokenType, statement::{Enum, Struct}, typ::Type}}, tokeniser::Token, validator::predefined::{BuiltinType, get_builtin_from_name, load_builtin}};
fn get_prog() -> Program {
let mut prog = Program::default();
load_builtin(&mut prog);
let loc = Loc::default();
prog.structs.insert(Ident::new("MyStruct"), LocBox::new(&loc, Struct {
name: Ident::new("MyStruct"),
fields: vec![
(Ident::new("foo"), LocBox::new(&loc, BuiltinType::usize())),
(Ident::new("bar"), LocBox::new(&loc, BuiltinType::bool())),
(Ident::new("baz"), LocBox::new(&loc, Type::Owned(Ident::new("str")).as_ref())),
]
}));
prog.enums.insert(Ident::new("MyEnum"), LocBox::new(&loc, Enum {
name: Ident::new("MyEnum"),
fields: vec![],
}));
prog
}
#[test]
fn parse_type_named_struct() {
let mut tokens = vec![
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
];
let ret = parser::typ::parse_type(&mut tokens);
assert!(ret.is_ok());
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct"))))
}
#[test]
fn parse_type_named_enum() {
let mut tokens = vec![
Token::new_test(TokenType::Ident(Ident::new("MyEnum")))
];
let ret = parser::typ::parse_type(&mut tokens);
assert!(ret.is_ok());
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyEnum"))))
}
#[test]
fn parse_type_ref() {
let mut tokens = vec![
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
];
tokens.reverse();
let ret = parser::typ::parse_type(&mut tokens);
assert!(ret.is_ok());
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct")).as_ref()))
}
#[test]
fn parse_type_ref2() {
let mut tokens = vec![
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
];
tokens.reverse();
let ret = parser::typ::parse_type(&mut tokens);
assert!(ret.is_ok());
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct")).as_ref().as_ref()))
}
#[test]
fn parse_type_ref3() {
let mut tokens = vec![
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
];
tokens.reverse();
let ret = parser::typ::parse_type(&mut tokens);
assert!(ret.is_ok());
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct")).as_ref().as_ref().as_ref()))
}
#[test]
fn parse_type_mut_ref() {
let mut tokens = vec![
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
Token::new_test(TokenType::Keyword(Keyword::Mut)),
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
];
tokens.reverse();
let ret = parser::typ::parse_type(&mut tokens);
assert!(ret.is_ok());
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct")).as_ref_mut()))
}
#[test]
fn parse_type_mut_ref2() {
let mut tokens = vec![
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
Token::new_test(TokenType::Keyword(Keyword::Mut)),
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
Token::new_test(TokenType::Keyword(Keyword::Mut)),
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
];
tokens.reverse();
let ret = parser::typ::parse_type(&mut tokens);
assert!(ret.is_ok());
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct")).as_ref_mut().as_ref_mut()))
}
#[test]
fn parse_type_mut_ref3() {
let mut tokens = vec![
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
Token::new_test(TokenType::Keyword(Keyword::Mut)),
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
Token::new_test(TokenType::Keyword(Keyword::Mut)),
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
Token::new_test(TokenType::Keyword(Keyword::Mut)),
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
];
tokens.reverse();
let ret = parser::typ::parse_type(&mut tokens);
assert!(ret.is_ok());
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct")).as_ref_mut().as_ref_mut().as_ref_mut()))
}
fn test_builtin(s: &str) {
let mut tokens = vec![
Token::new_test(TokenType::Ident(Ident::new(s)))
];
let ret = parser::typ::parse_type(&mut tokens);
assert!(ret.is_ok());
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), get_builtin_from_name(s).unwrap()))
}
#[test]
fn parse_type_builtin_usize() {
test_builtin("usize");
}
#[test]
fn parse_type_builtin_isize() {
test_builtin("isize");
}
#[test]
fn parse_type_builtin_u8() {
test_builtin("u8");
}
#[test]
fn parse_type_builtin_i8() {
test_builtin("i8");
}
#[test]
fn parse_type_builtin_u16() {
test_builtin("u16");
}
#[test]
fn parse_type_builtin_i16() {
test_builtin("i16");
}
#[test]
fn parse_type_builtin_u32() {
test_builtin("u32");
}
#[test]
fn parse_type_builtin_i32() {
test_builtin("i32");
}
#[test]
fn parse_type_builtin_u64() {
test_builtin("u64");
}
#[test]
fn parse_type_builtin_i64() {
test_builtin("i64");
}
#[test]
fn parse_type_builtin_void() {
test_builtin("void");
}
#[test]
fn parse_type_builtin_char() {
test_builtin("char");
}
#[test]
fn parse_type_builtin_bool() {
test_builtin("bool");
}

View File

@@ -1 +0,0 @@
[]