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

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

View File

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

View File

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

View File

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

View File

@@ -9,10 +9,14 @@ pub enum Literal {
Ident(Ident), Ident(Ident),
String(TString), String(TString),
Char(Char), Char(Char),
Array(Vec<LocBox<Expr>>), Array{
items: Vec<LocBox<Expr>>,
item_size: Option<usize>,
},
Bool(bool), Bool(bool),
ArrayRepeat { ArrayRepeat {
val: Box<LocBox<Expr>>, val: Box<LocBox<Expr>>,
item_size: Option<usize>,
count: usize, count: usize,
}, },
} }
@@ -22,28 +26,29 @@ impl Literal {
let t = match self { let t = match self {
Self::Number(_) => Type::Builtin { name: String::from("usize"), size: SIZE as u8, signed: false }, Self::Number(_) => Type::Builtin { name: String::from("usize"), size: SIZE as u8, signed: false },
Self::Ident(ident) => Type::Owned(ident.clone()), Self::Ident(ident) => Type::Owned(ident.clone()),
Self::String(_) => Type::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::Char(_) => Type::Owned(Ident(String::from("char"))),
Self::Bool(_) => Type::Owned(Ident(String::from("bool"))), Self::Bool(_) => Type::Owned(Ident(String::from("bool"))),
Self::Array(arr) => { Self::Array { items: arr, ..} => {
if arr.is_empty() { if arr.is_empty() {
Type::SizedArray { Type::SizedArray {
inner: Box::new(Type::Builtin { name: "void".to_string(), size: 0, signed: false }), inner: Box::new(Type::Builtin { name: "void".to_string(), size: 0, signed: false }),
count: LocBox::new(&Loc::default(), Expr::Literal(String::new(), Literal::Number(Number { val: 0, base: 10, signed: false }))) count: LocBox::new(&Loc::default(), Expr::Literal(String::new(), Literal::Number(Number { val: 0, base: 10, signed: false })))
} }
} else { } else {
let item = arr.first().unwrap(); let mut item = arr.first().unwrap().clone();
let loc = item.loc().clone(); let loc = item.loc().clone();
let mut item = item.inner().clone();
Type::SizedArray { Type::SizedArray {
inner: Box::new(validate_expr(prog, &mut item)?.unwrap()), 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 }))) 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 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 })))} 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>> pub inner_scope: Option<Box<Scope>>
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Default)]
pub struct Program { pub struct Program {
pub ast: expr::Block, pub ast: expr::Block,
pub structs: HashMap<Ident, LocBox<Struct>>, pub structs: HashMap<Ident, LocBox<Struct>>,
@@ -32,7 +32,9 @@ pub struct Program {
pub literals: HashMap<String, Literal>, // string is id of literal pub literals: HashMap<String, Literal>, // string is id of literal
pub struct_literals: HashMap<String, StructLit>, // string is id of literal pub struct_literals: HashMap<String, StructLit>, // string is id of literal
pub scope: Option<Scope>, 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 anyhow::bail;
use lazy_static::lazy_static;
use crate::{common::loc::LocBox, lerror}; use crate::{common::loc::LocBox, lerror};
@@ -70,6 +71,32 @@ pub struct Function {
pub body: Option<Block>, // If None then its a type declaration 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 { impl Function {
pub fn get_full_name(&self) -> String { pub fn get_full_name(&self) -> String {
if let Some(sn) = &self.struct_name { if let Some(sn) = &self.struct_name {
@@ -138,7 +165,19 @@ pub enum ConstDataTyp {
Array(Vec<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() { match value.inner() {
Expr::Literal(_, lit) => { Expr::Literal(_, lit) => {
match 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"); lerror!(value.loc(), "Expected number got string");
bail!("") bail!("")
} }
let mut val = val.chars().into_iter().map(|v| v as u8).collect::<Vec<u8>>();
if *cstr { if *cstr {
let mut val = val.chars().into_iter().map(|v| v as u8).collect::<Vec<u8>>();
val.push(0); 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: _ }) => { Literal::Number(Number { val, base: _, signed: _ }) => {
let mut buf = Vec::new(); 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)) Ok(ConstDataTyp::Bytes(buf))
} }
Literal::Array(arr) => { Literal::Array { items: arr, item_size } => {
if must_be_number { if must_be_number {
lerror!(value.loc(), "Expected number got array"); lerror!(value.loc(), "Expected number got array");
bail!("") bail!("")
@@ -183,20 +239,24 @@ pub fn get_constant_data_as_bytes(program: &Program, struct_items: &HashMap<Iden
if arr.len() < 1 { if arr.len() < 1 {
return Ok(ConstDataTyp::Bytes(vec![])); return Ok(ConstDataTyp::Bytes(vec![]));
} }
CTX.lock().unwrap().current_array_item_size = *item_size;
for item in arr { 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); bytes.push(data);
} }
CTX.lock().unwrap().current_array_item_size = None;
Ok(ConstDataTyp::Array(bytes)) Ok(ConstDataTyp::Array(bytes))
} }
Literal::ArrayRepeat { val, count } => { Literal::ArrayRepeat { val, count, item_size} => {
if must_be_number { if must_be_number {
lerror!(value.loc(), "Expected number got repeating array"); lerror!(value.loc(), "Expected number got repeating array");
bail!("") bail!("")
} }
let 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 num = Vec::new();
let mut inc = 0; 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)?); items.insert(name.clone(), item.inner().size_of(program)?);
} }
CTX.lock().unwrap().current_struct_items = Some(items);
for field in &strct.fields { 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)) Ok(ConstDataTyp::Array(bytes))
} }
Expr::Path(path) => { Expr::Path(path) => {
let ctx = {CTX.lock().unwrap().clone()};
let name = path.0.last().unwrap(); let name = path.0.last().unwrap();
if let Some(var) = program.const_vars.get(name) { if let Some(var) = program.const_vars.get(name) {
Ok(get_constant_data_as_bytes(program, 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) { } else if let Some(_) = program.static_vars.get(name) {
lerror!(value.loc(), "Statics cannot be passed by value, use a reference"); lerror!(value.loc(), "Statics cannot be passed by value, use a reference");
bail!("") bail!("")
} else if let Some(_) = program.functions.get(name) { } else if let Some(_) = program.functions.get(name) {
Ok(ConstDataTyp::AddrOfFunc(name.clone())) Ok(ConstDataTyp::AddrOfFunc(name.clone()))
} else if let Some(size) = struct_items.get(name) { } else if let Some(struct_items) = ctx.current_struct_items &&
Ok(ConstDataTyp::Variable(name.clone(), *size)) 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 { } else {
lerror!(value.loc(), "Unable to find ident '{name}'"); lerror!(value.loc(), "Unable to find ident '{name}'");
bail!("") bail!("")

View File

@@ -2,7 +2,7 @@ use std::fmt::Display;
use anyhow::bail; 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}; use super::{expr::Expr, literal::Literal, Ident, Program};
@@ -28,6 +28,53 @@ pub enum Type {
} }
impl 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> { pub fn get_absolute_value(&self, program: &Program) -> anyhow::Result<Self> {
match self { match self {
Self::Ref { inner, .. } => inner.get_absolute_value(program), 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> { pub fn get_variable_type(&self, program: &Program) -> anyhow::Result<Self> {
match self { match self {
Self::Owned(ident) => { Self::Owned(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) { if let Some(t) = program.types.get(ident) {
Ok(t.inner().clone()) Ok(t.inner().clone())
} else } 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) { if let Some(var) = program.get_var(ident) {
Ok(var.1.inner().typ.clone().expect("type should be computed if were already using it").inner().clone()) Ok(var.1.inner().typ.clone().expect("type should be computed if were already using it").inner().clone())
} else { } else {

View File

@@ -2,7 +2,7 @@ use std::collections::{BTreeMap, HashMap};
use anyhow::{bail, Result}; 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}; 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), 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)) { 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 } else
if let Some(_) = utils::check(tokens, TokenType::ident("")) { if let Some(_) = utils::check(tokens, TokenType::ident("")) {
let p = parse_path(tokens)?; let p = parse_path(tokens)?;
if let Some(t) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) { if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
Some(parse_struct_literal(tokens, p.inner().unwrap_path(), cli)?) Some(parse_struct_literal(tokens, p.inner().unwrap_path(), cli, prog)?)
} else { } else {
Some(p) 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::Ampersand),
TokenType::Punct(Punctuation::Star), TokenType::Punct(Punctuation::Star),
]) { ]) {
Some(parse_unop(tokens, cli)?) Some(parse_unop(tokens, cli, prog)?)
} else } else
if let Some(_) = utils::check_from_many(tokens, &[ if let Some(_) = utils::check_from_many(tokens, &[
TokenType::string("", false), 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::True),
TokenType::Keyword(Keyword::False) 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)) { } 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)) { } 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)) { } 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)) { } 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)) { } 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")?; let _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
return Ok(Some(LocBox::new(kw.loc(), Expr::Break))); return Ok(Some(LocBox::new(kw.loc(), Expr::Break)));
@@ -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")?; let _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
return Ok(Some(LocBox::new(kw.loc(), Expr::Continue))); return Ok(Some(LocBox::new(kw.loc(), Expr::Continue)));
} else if let Some(kw) = utils::check(tokens, TokenType::Keyword(Keyword::If)) { } else if let Some(kw) = utils::check(tokens, TokenType::Keyword(Keyword::If)) {
return Ok(Some(LocBox::new(&kw.loc().clone(), Expr::If(parse_if(tokens, cli)?)))); return Ok(Some(LocBox::new(&kw.loc().clone(), Expr::If(parse_if(tokens, cli, prog)?))));
} else { } else {
None 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)?; res = parse_ptr_field_access(tokens, res)?;
} }
if utils::check(tokens, TokenType::Delim(Delimiter::ParenL)).is_some() { 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() { if utils::check(tokens, TokenType::Keyword(Keyword::As)).is_some() {
res = parse_cast(tokens, res)?; res = parse_cast(tokens, res)?;
} }
if utils::check(tokens, TokenType::Delim(Delimiter::SquareL)).is_some() { 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) { 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 { if consume_semi {
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?; _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
} }
@@ -124,9 +127,9 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
Ok(res) 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 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)))) 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) 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 loc = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::If), "")?;
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?; 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"); lerror!(loc.loc(), "Expected test for if statement, got nothing");
bail!("") bail!("")
}; };
@@ -151,7 +154,7 @@ fn parse_if(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<IfExpr> {
_ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR)); _ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR));
Block(Vec::new()) Block(Vec::new())
} else { } else {
parse_block(tokens, cli)? parse_block(tokens, cli, prog)?
} }
} else { } else {
lerror!(loc.loc(), "Expected '{{'"); 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_consume(tokens, TokenType::Keyword(Keyword::Else)) {
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::If)) { 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 { Ok(IfExpr {
test: Box::new(test), test: Box::new(test),
body: block, body: block,
else_if: Some(branch) else_if: Some(branch)
}) })
} else { } else {
let branch = IfBranchExpr::Else(parse_block(tokens, cli)?); let branch = IfBranchExpr::Else(parse_block(tokens, cli, prog)?);
Ok(IfExpr { Ok(IfExpr {
test: Box::new(test), test: Box::new(test),
body: block, 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 kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::While), "")?;
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?; 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"); lerror!(kw.loc(), "Expected test comparrison for while loop, got nothing");
bail!("") bail!("")
}; };
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?; let _ = 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 { Ok(LocBox::new(kw.loc(), Expr::WhileLoop {
test: Box::new(test), test: Box::new(test),
body: block 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 kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::For), "")?;
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?; 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"); lerror!(kw.loc(), "Expected init stat for a for loop, got nothing");
bail!("") bail!("")
}; };
// Semicolon parsed out by parse_item above // 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"); lerror!(kw.loc(), "Expected test comparrison for a for loop, got nothing");
bail!("") bail!("")
}; };
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), ""); _ = 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"); lerror!(kw.loc(), "Expected post expression (usually an index increment) for a for loop, got nothing");
bail!("") bail!("")
}; };
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?; let _ = 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 { Ok(LocBox::new(kw.loc(), Expr::ForLoop {
init: Box::new(pre), init: Box::new(pre),
@@ -222,16 +225,16 @@ fn parse_for_loop(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>
body: block 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 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 })) 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() { match left.inner() {
Expr::FieldAccess { .. } | Expr::FieldAccess { .. } |
Expr::PtrFieldAccess { .. } => { 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)) { if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
break; 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); params.push(param);
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) { if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
if let None = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) { 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), ""); _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "");
Ok(LocBox::new(start.loc(), Expr::Call { path: Box::new(left), params: CallParams(params) })) 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 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."); lerror!(start.loc(), "Expected index for in array index but found nothing.");
bail!("") 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 { Ok(LocBox::new(start.loc(), Expr::FieldAccess {
left: Box::new(Some(left)), left: Box::new(Some(left)),
right: Box::new(right), right: Box::new(right),
offset: 0 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 start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "unreachable")?;
let mut params = Vec::new(); 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)) { if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
break; 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); params.push(param);
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) { if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
if let None = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) { if let None = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
@@ -309,6 +314,7 @@ fn parse_member_function_call(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli:
Ok(LocBox::new(start.loc(), Expr::MethodCall { Ok(LocBox::new(start.loc(), Expr::MethodCall {
left: Box::new(left), left: Box::new(left),
strct: None,
params: CallParams(params) 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 { Ok(LocBox::new(start.loc(), Expr::PtrFieldAccess {
left: Box::new(Some(left)), left: Box::new(Some(left)),
right: Box::new(right), right: Box::new(right),
offset: 0 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)) { 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)))); return Ok(LocBox::new(tkn.loc(), Expr::Literal(String::new(), Literal::Bool(true))));
} else } else
@@ -352,17 +360,17 @@ fn parse_literal(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>>
} else } else
if let Some(start) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareL)) { if let Some(start) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareL)) {
if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareR)) { if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareR)) {
return Ok(LocBox::new(start.loc(), Expr::Literal(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) { /*if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Comma) {
} else */ } else */
if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Semi) { 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"); lerror!(start.loc(), "Expected value, found nothing");
bail!("") 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 { let Expr::Literal(_, Literal::Number(count)) = count.inner() else {
lerror!(count.loc(), "a repeating array accepts only literal numbers for count argument"); lerror!(count.loc(), "a repeating array accepts only literal numbers for count argument");
@@ -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), "")?; utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
return Ok(LocBox::new(start.loc(), Expr::Literal(String::new(), Literal::ArrayRepeat { return Ok(LocBox::new(start.loc(), Expr::Literal(String::new(), Literal::ArrayRepeat {
val: Box::new(typ), val: Box::new(typ),
count: count.val count: count.val,
item_size: None,
}))); })));
} else { } else {
let first = parse_expr(tokens, 0, false, cli)?;
let Some(first) = first else { unreachable!() };
let mut values = Vec::new(); let mut values = Vec::new();
values.push(first);
while !tokens.is_empty() { 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); values.push(val);
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) { 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), "")?; 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() { 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()); 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!() 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 start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyL), "")?;
let mut fields = BTreeMap::new(); let mut fields = BTreeMap::new();
while !tokens.is_empty() { 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(""), "")?; let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?;
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "wtf?")?; _ = 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); fields.insert(name.tt().unwrap_ident(), typ);
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) { if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyR), "")?; 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 }))) 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 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"); lerror!(start.loc(), "Expected expr found nothing");
bail!("") bail!("")
}; };
@@ -449,7 +455,7 @@ fn parse_path(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
Ok(LocBox::new(part.loc(), Expr::Path(Path(buf)))) 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, &[ let typ = utils::check_consume_or_err_from_many(tokens, &[
TokenType::Punct(Punctuation::Not), TokenType::Punct(Punctuation::Not),
TokenType::Punct(Punctuation::Plus), // Make number positive 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 loc = typ.loc().clone();
let TokenType::Punct(typ) = typ.tt().clone() else {unreachable!()}; 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"); lerror!(&loc, "Expected expression after unary token, found nothing");
bail!("") 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 // TODO: https://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudocode
loop { loop {
@@ -501,7 +507,7 @@ fn parse_binop(tokens: &mut Vec<Token>, mut lhs: LocBox<Expr>, precedence: usize
} }
_ = tokens.pop(); _ = 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 { lhs = LocBox::new(&op_loc, Expr::BinOp {
typ: op, typ: op,
left: Box::new(lhs), 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), "")?; utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyL), "")?;
let mut items = Vec::new(); let mut items = Vec::new();
while !tokens.is_empty() { while !tokens.is_empty() {
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyR)) { if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyR)) {
break; break;
} }
if let Some(item) = parse_item(tokens, cli)? { if let Some(item) = parse_item(tokens, cli, prog)? {
items.push(item); items.push(item);
} else { } else {
break; 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> { pub fn parse_program(mut tokens: Vec<Token>, cli: &CliArgs) -> Result<Program> {
let mut prog_body = Vec::new(); let mut prog_body = Vec::new();
let mut prog = Program {
while !tokens.is_empty() { ast: Block(prog_body.clone()),
if let Some(item) = parse_item(&mut tokens, cli)? {
prog_body.push(item);
} else {
break
}
}
Ok(Program {
ast: Block(prog_body),
enums: HashMap::new(), enums: HashMap::new(),
functions: HashMap::new(), functions: HashMap::new(),
member_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(), literals: HashMap::new(),
struct_literals: HashMap::new(), struct_literals: HashMap::new(),
scope: None, 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>> { fn parse_item(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<Option<Ast>> {
if let Some(stat) = stat::parse_statement(tokens, cli)? { if let Some(stat) = stat::parse_statement(tokens, cli, prog)? {
return Ok(Some(Ast::Statement(stat))); 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))); return Ok(Some(Ast::Expr(expr)));
} }
Ok(None) Ok(None)

View File

@@ -1,9 +1,11 @@
use std::str;
use anyhow::bail; use anyhow::bail;
use crate::cli::CliArgs; use crate::cli::CliArgs;
use crate::common::loc::LocBox; use crate::common::loc::LocBox;
use crate::{cli, lerror}; use crate::{cli, error, lerror};
use crate::parser::ast::{TString, TokenType}; use crate::parser::ast::{Program, TString, TokenType};
use crate::parser::ast::statement::Let; use crate::parser::ast::statement::Let;
use crate::parser::expr::parse_expr; use crate::parser::expr::parse_expr;
use crate::parser::{Delimiter, Ident, Keyword, Punctuation}; 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>; 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)) { if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Fn)) {
Ok(Some(parse_fn(tokens, cli)?)) Ok(Some(parse_fn(tokens, cli, prog)?))
} else } else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Type)) { if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Type)) {
Ok(Some(parse_type_alias(tokens)?)) Ok(Some(parse_type_alias(tokens)?))
} else } else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Const)) { if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Const)) {
Ok(Some(parse_constant(tokens, cli)?)) Ok(Some(parse_constant(tokens, cli, prog)?))
} else } else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Static)) { if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Static)) {
Ok(Some(parse_static(tokens, cli)?)) Ok(Some(parse_static(tokens, cli, prog)?))
} else } else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Struct)) { if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Struct)) {
Ok(Some(parse_struct(tokens)?)) 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)?)) Ok(Some(parse_enum(tokens)?))
} else } else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Let)) { if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Let)) {
Ok(Some(parse_let(tokens, cli)?)) Ok(Some(parse_let(tokens, cli, prog)?))
} else } else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Include)) { if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Include)) {
Ok(Some(parse_include(tokens, cli)?)) Ok(Some(parse_include(tokens, cli, prog)?))
} else { } else {
Ok(None) 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 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!()}; 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), "")?; _ = 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 = cwd.join(p);
let path = path.join(&include_path.val); let path = path.join(&include_path.val);
if path.exists() { 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 data = std::fs::read_to_string(&path)?;
let mut tokens_imp = crate::tokeniser::tokenise(&data, &path.to_string_lossy().to_string())?; let mut tokens_imp = crate::tokeniser::tokenise(&data, &path.to_string_lossy().to_string())?;
tokens.append(&mut tokens_imp); 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 }))) 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 kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Static), "")?;
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident(); let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?; _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
let typ = parse_type(tokens)?; let typ = parse_type(tokens)?;
let eq = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?; 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"); lerror!(eq.loc(), "Expected expression found nothing");
bail!("") 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 }))) 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 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 name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
let mut typ = None; 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)?); typ = Some(parse_type(tokens)?);
} }
if let Some(eq) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Eq)) { 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"); lerror!(eq.loc(), "Expected expression found nothing");
bail!("") 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), "")?; _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
Ok(LocBox::new(kw.loc(), Statement::Let(Let{ name, typ, val }))) 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), "")?; let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Const), "")?;
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Fn)) { 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), "")?; _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
let typ = parse_type(tokens)?; let typ = parse_type(tokens)?;
let eq = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?; 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"); lerror!(eq.loc(), "Expected expression found nothing");
bail!("") 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 }))) 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 // Just remove the kw since we checked it before
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Fn), "")?; let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Fn), "")?;
let mut struct_name = None; let mut struct_name = None;
let mut name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident(); let mut name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
// Check if this is a struct method // 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); struct_name = Some(name);
name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident(); 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 // Check for return type cause it optional
let mut ret_type = None; let mut ret_type = None;
@@ -203,13 +212,22 @@ fn parse_fn(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Statement>>
} }
let body; let body;
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) { if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
body = Some(parse_block(tokens, cli)?); body = Some(parse_block(tokens, cli, prog)?);
} else { } else {
// Check if its just a declaration // Check if its just a declaration
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?; _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
body = None; 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{ Ok(LocBox::new(kw.loc(), Statement::Fn(Function{
struct_name, struct_name,
name, name,
@@ -222,10 +240,23 @@ fn parse_fn(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Statement>>
} }
// usize is: 0 = no self, static; 1 = self, ref; 2 = self, mut ref
fn parse_fn_params(tokens: &mut Vec<Token>) -> Result<Vec<(Ident, LocBox<Type>)>> { fn parse_fn_params(tokens: &mut Vec<Token>) -> Result<(usize, Vec<(Ident, LocBox<Type>)>)> {
let mut args = Vec::new(); let mut args = Vec::new();
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?; 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() { while !tokens.is_empty() {
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) { if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
break; 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), "")?; 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 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}; 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)?; let itm_typ = parse_type(tokens)?;
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Semi)) { 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() { match count.inner() {
Expr::Literal(_, Literal::Number(_)) | 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), "")?; _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
} else { } else {
let ident = utils::check_consume_or_err(tokens, TokenType::ident(""), "a")?; 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 { if let None = loc {
loc = Some(ident.loc().clone()); 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); objs.push(obj_p);
} }
let out = Path::new(&cli.output); let out = Path::new(&cli.output);
target.link(objs, &out)?; target.link(objs, &out, &cli.linker_args)?;
Ok(()) Ok(())
} }
@@ -71,5 +71,5 @@ pub trait Target {
} }
fn write_code(&mut self, program: &Program, f: &mut File) -> anyhow::Result<()>; fn write_code(&mut self, program: &Program, f: &mut File) -> anyhow::Result<()>;
fn compile(&mut self, from: &Path, to: &Path) -> 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}; use std::{collections::HashMap, fs::File, io::Write, process};
pub struct AsmGen; pub struct AsmGen;
@@ -54,13 +54,16 @@ impl Target for AsmGen {
cmd.output()?; cmd.output()?;
Ok(()) 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"); let mut cmd = std::process::Command::new("ld");
cmd.args(extra_args);
cmd.args(&[ cmd.args(&[
"-o", "-o",
to.to_string_lossy().to_string().as_str(), to.to_string_lossy().to_string().as_str(),
]); ]);
for item in &from { for item in &from {
cmd.arg(item.to_string_lossy().to_string().as_str()); cmd.arg(item.to_string_lossy().to_string().as_str());
} }
@@ -79,7 +82,6 @@ pub enum VarMapT {
pub struct FunctionCtx { pub struct FunctionCtx {
vars: HashMap<Ident, VarMapT>, vars: HashMap<Ident, VarMapT>,
stack_offset: usize, stack_offset: usize,
used_registers: usize,
loop_level: usize, // used for loops loop_level: usize, // used for loops
if_level: usize, // used for if stats if_level: usize, // used for if stats
cmp_level: usize, // used for logical comparisons cmp_level: usize, // used for logical comparisons
@@ -93,7 +95,6 @@ impl FunctionCtx {
Self { Self {
vars: Default::default(), vars: Default::default(),
stack_offset: 0, stack_offset: 0,
used_registers: 0,
loop_level: 0, loop_level: 0,
if_level: 0, if_level: 0,
cmp_level: 0, cmp_level: 0,
@@ -171,6 +172,7 @@ impl AsmGen {
} else { } else {
func.name.to_string() func.name.to_string()
}; };
info!("writing norm function: {name}");
writeln!(f, "{}: ; {} {}", name, loc, func.get_full_name_pretty())?; writeln!(f, "{}: ; {} {}", name, loc, func.get_full_name_pretty())?;
if body.0.is_empty() { if body.0.is_empty() {
writeln!(f, " ret")?; writeln!(f, " ret")?;
@@ -216,6 +218,7 @@ impl AsmGen {
} }
} }
} else { } else {
info!("writing function stub: {}", func.name);
writeln!(f, " ret")?; writeln!(f, " ret")?;
} }
writeln!(f, "\n")?; 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<()> { pub fn write_expr(&self, program: &Program, f: &mut impl Write, fc: &mut FunctionCtx, expr: &Expr) -> anyhow::Result<()> {
match expr { match expr {
Expr::Cast { .. } => (), Expr::Cast { left, .. } => self.write_expr(program, f, fc, left.inner())?,
Expr::Literal(id, val) => { Expr::Literal(id, val) => {
match 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::Ident(_) => unreachable!(),
Literal::Array(_) | Literal::String(_) => {
Literal::String(_) | writeln!(f, " lea rax, [rel mcl_lit_{id}] ; str")?;
Literal::ArrayRepeat { .. } => {
writeln!(f, " lea rax, [rel mcl_lit_{id}]")?;
} }
Literal::Bool(v) => { Literal::Bool(v) => {
writeln!(f, " mov rax, {} ; {}", *v as u8, v)?; writeln!(f, " mov rax, {} ; {}", *v as u8, v)?;
} }
Literal::Number(_) | Literal::Number(_) |
Literal::Char(_) => { 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)?; let offset = strct_t.inner().get_offset_of(program, name)?;
writeln!(f, " mov [r10+{offset}], rax")?; 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())?; 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())?; self.write_expr(program, f, fc, left.clone().unwrap().inner())?;
writeln!(f, " mov rax, [rel rax] ; .{:?}", right.inner())?; if *offset > 0 {
writeln!(f, " add rax, {offset} ; .{:?}", right.inner())?; 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 } => { Expr::InfLoop { body } => {
let sl = fc.inc_loop_level(); let sl = fc.inc_loop_level();
@@ -387,6 +422,35 @@ impl AsmGen {
fc.is_last_item = false; 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 } => { Expr::Call { path, params } => {
for (i, param) in params.0.iter().enumerate() { for (i, param) in params.0.iter().enumerate() {
self.write_expr(program, f, fc, param.inner())?; self.write_expr(program, f, fc, param.inner())?;
@@ -550,16 +614,29 @@ impl AsmGen {
match var { match var {
VarMapT::Stack(offset, typ) => { VarMapT::Stack(offset, typ) => {
match typ { match typ {
Type::Builtin { .. } => writeln!(f, " lea rax, [rsp + {offset}]")?, /*Type::Builtin { .. } => {
_ => writeln!(f, " mov rax, [rsp + {offset}]")?, 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) { } else if let Some(_) = program.get_const_var(&ident) {
writeln!(f, " lea rax, [rel {ident}]")?; writeln!(f, " lea rax, [rel {ident}] ; const {ident}")?;
} else { } else {
panic!() 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:?}") v => unreachable!("{v:?}")
} }
@@ -585,7 +662,7 @@ impl AsmGen {
pub fn write_constants(&self, program: &Program, f: &mut File) -> anyhow::Result<()> { pub fn write_constants(&self, program: &Program, f: &mut File) -> anyhow::Result<()> {
for (name, constant) in &program.const_vars { for (name, constant) in &program.const_vars {
writeln!(f, "{name}: ; const")?; 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<()> { fn x(bytes: &ConstDataTyp, f: &mut File) -> anyhow::Result<()> {
match bytes { match bytes {
ConstDataTyp::Array(arr) => { ConstDataTyp::Array(arr) => {
@@ -622,7 +699,7 @@ impl AsmGen {
pub fn write_statics(&self, program: &Program, f: &mut File) -> anyhow::Result<()> { pub fn write_statics(&self, program: &Program, f: &mut File) -> anyhow::Result<()> {
for (name, statc) in &program.static_vars { for (name, statc) in &program.static_vars {
writeln!(f, "{name}: ; static")?; 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<()> { fn x(bytes: &ConstDataTyp, f: &mut File) -> anyhow::Result<()> {
match bytes { match bytes {
ConstDataTyp::Array(arr) => { ConstDataTyp::Array(arr) => {
@@ -688,11 +765,11 @@ impl AsmGen {
for lit in &program.literals { for lit in &program.literals {
writeln!(f, "mcl_lit_{}:", lit.0)?; 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 { for lit in &program.struct_literals {
writeln!(f, "mcl_lit_{}:", lit.0)?; 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(()) 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_syscall0:
__INTERNAL_syscall: mov rax, rdi
mov ecx, edi syscall
mov r11, rdx ret
movzx eax, sil
xor edi, edi __INTERNAL_syscall1:
xor esi, esi mov rax, rdi
xor edx, edx mov rdi, rsi
xor r10d, r10d syscall
xor r8d, r8d ret
xor r9d, r9d
test cl, cl __INTERNAL_syscall2:
je .L3 mov rax, rdi
mov rdi, QWORD [r11] mov rdi, rsi
cmp cl, 1 mov rsi, rdx
je .L3 syscall
mov rsi, QWORD [r11+8] ret
cmp cl, 2
je .L3 __INTERNAL_syscall3:
mov rdx, QWORD [r11+16] mov rax, rdi
cmp cl, 3 mov rdi, rsi
je .L3 mov rsi, rdx
mov r10, QWORD [r11+24] mov rdx, rcx
cmp cl, 4 syscall
je .L3 ret
mov r8, QWORD [r11+32]
cmp cl, 5 __INTERNAL_syscall4:
je .L3 mov rax, rdi
mov r9, QWORD [r11+40] mov rdi, rsi
.L3: mov rsi, rdx
syscall mov rdx, rcx
ret 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 global _start
_start: _start:
xor rax, rax xor rax, rax

View File

@@ -30,6 +30,12 @@ impl Token {
pub fn tt(&self) -> &TokenType { pub fn tt(&self) -> &TokenType {
&self.tt &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); buf.push(*c);
last = *c; last = *c;
} }
let buf = buf
.replace("\\n", "\n")
.replace("\\r", "\r");
tokens.push(Token::new(TokenType::string(&buf, false), &loc)); tokens.push(Token::new(TokenType::string(&buf, false), &loc));
} }
'\'' => { '\'' => {
@@ -251,6 +260,7 @@ lazy_static::lazy_static!(
("return", TokenType::Keyword(Keyword::Return)), ("return", TokenType::Keyword(Keyword::Return)),
("loop", TokenType::Keyword(Keyword::Loop)), ("loop", TokenType::Keyword(Keyword::Loop)),
("as", TokenType::Keyword(Keyword::As)), ("as", TokenType::Keyword(Keyword::As)),
("self", TokenType::Keyword(Keyword::Self_)),
("{", TokenType::Delim(Delimiter::CurlyL)), ("{", TokenType::Delim(Delimiter::CurlyL)),
("}", TokenType::Delim(Delimiter::CurlyR)), ("}", TokenType::Delim(Delimiter::CurlyR)),
("[", TokenType::Delim(Delimiter::SquareL)), ("[", TokenType::Delim(Delimiter::SquareL)),

View File

@@ -16,6 +16,9 @@ impl Ident {
pub fn as_path(self) -> Path { pub fn as_path(self) -> Path {
Path(vec![self]) 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, Type, While, For, Break, Continue,
Let, Const, Mut, Static, Let, Const, Mut, Static,
True, False, Include, Extern, Return, True, False, Include, Extern, Return,
As, Loop As, Loop, Self_
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]

View File

@@ -2,7 +2,7 @@ use std::{collections::HashMap, panic};
use anyhow::bail; 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; 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<()> { 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 { 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() { if val_t != *t.inner_mut() {
lerror!(t.loc(), "Cannot assign {val_t} to {}", t.inner_mut()); lerror!(t.loc(), "Cannot assign {val_t} to {}", t.inner_mut());
bail!("") bail!("")
} }
} }
if let Some(val) = &mut lt.val && let None = &mut lt.typ { 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"); lerror!(val.loc(), "Expected a type, go none");
bail!(""); 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>> { fn validate_ast(prog: &mut Program, ast: &mut Ast) -> anyhow::Result<Option<Type>> {
match ast { match ast {
Ast::Expr(expr) => { Ast::Expr(expr) => {
validate_expr(prog, expr.inner_mut()) validate_expr(prog, expr)
} }
Ast::Statement(stat) => { Ast::Statement(stat) => {
validate_stat(prog, stat, CurrentState::InFunc)?; 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>> { pub fn validate_expr(prog: &mut Program, expr: &mut LocBox<Expr>) -> anyhow::Result<Option<Type>> {
match expr { 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::Break | Expr::Continue => Ok(None),
Expr::Return(ret) => { Expr::Return(ret) => {
if let Some(expr) = &mut**ret { if let Some(expr) = &mut**ret {
validate_expr(prog, expr.inner_mut()) validate_expr(prog, expr)
} else { } else {
Ok(None) 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()))?; validate_type(prog, &LocBox::new(&Loc::default(), typ.clone()))?;
for field in strct.fields.iter_mut() { 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::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher}; 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)) Ok(Some(typ))
}, },
Expr::If(ifs) => { Expr::If(ifs) => {
validate_expr(prog, ifs.test.inner_mut())?; validate_expr(prog, &mut ifs.test)?;
for item in ifs.body.0.iter_mut() { for item in ifs.body.0.iter_mut() {
validate_ast(prog, item)?; 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 // TODO: Path hell TBD
Ok(Some(Type::Owned(path.0.last().unwrap().clone()))) Ok(Some(Type::Owned(path.0.last().unwrap().clone())))
} }
Expr::Group(group) => validate_expr(prog, group.inner_mut()), Expr::Group(group) => validate_expr(prog, group),
Expr::Call { path, params } => { Expr::Call { path, params } => {
let loc = path.loc().clone(); let loc = path.loc().clone();
let Expr::Path(path) = path.inner_mut() else { 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 { match f {
Some(func) => { Some(func) => {
for (i, param) in params.0.iter_mut().enumerate() { for (i, param) in params.0.iter_mut().enumerate() {
let ft = func.inner().params[i].1.inner().clone(); let mut 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 { 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 t = t.get_variable_type(prog)?;
let ft = ft.get_variable_type(prog)?; let ft = ft.get_variable_type(prog)?;
if t != ft { if t != ft {
lerror!(param.loc(), "expected {ft:?}, got {t:?}"); lerror!(param.loc(), "expected {ft}, got {t}");
bail!("owo") bail!("owo")
} }
} }
@@ -181,7 +194,7 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
Some(func) => { Some(func) => {
for (i, param) in params.0.iter_mut().enumerate() { for (i, param) in params.0.iter_mut().enumerate() {
let ft = func.inner().params[i].1.inner().clone(); 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 { match t {
Some(t) => { Some(t) => {
if t.get_absolute_value(prog)? != ft.get_absolute_value(prog)? { 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 var_t;
let method_name; let method_name;
match left.inner_mut() { match left.inner_mut() {
Expr::FieldAccess { left, right, .. } | Expr::FieldAccess { left, right, .. } |
Expr::PtrFieldAccess { left, right, .. } => { Expr::PtrFieldAccess { left, right, .. } => {
var_t = validate_expr(prog, left.clone().unwrap().inner_mut())?; var_t = validate_expr(prog, &mut left.clone().unwrap())?;
let name = validate_expr(prog, right.inner_mut())?; let name = validate_expr(prog, right)?;
match name.unwrap() { match name.unwrap() {
Type::Owned(name) => method_name = name, Type::Owned(name) => method_name = name,
_ => unreachable!() _ => 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 { let Type::Owned(struct_name) = var_t.unwrap().get_absolute_value(prog)? else {
panic!("fml"); panic!("fml");
}; };
*strct = Some(struct_name.clone());
let mut strct = prog.member_functions.get_mut(&struct_name).unwrap().clone(); let mut strct = prog.member_functions.get_mut(&struct_name).unwrap().clone();
let func = strct.get_mut(&method_name).unwrap(); let func = strct.get_mut(&method_name).unwrap();
for (i, param) in params.0.iter_mut().enumerate() { 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(); let ft = func.inner_mut().params[i].1.inner_mut();
if t.as_ref() != Some(ft) { if t.as_ref() != Some(ft) {
lerror!(param.loc(), "expected {ft}, got {}", t.unwrap()); lerror!(param.loc(), "expected {ft}, got {}", t.unwrap());
@@ -254,15 +267,15 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
} }
Expr::ForLoop { init, test, on_loop, body } => { Expr::ForLoop { init, test, on_loop, body } => {
let _ = validate_ast(prog, init)?; let _ = validate_ast(prog, init)?;
let _ = validate_expr(prog, test.inner_mut())?; let _ = validate_expr(prog, test)?;
let _ = validate_expr(prog, on_loop.inner_mut())?; let _ = validate_expr(prog, on_loop)?;
for item in body.0.iter_mut() { for item in body.0.iter_mut() {
let _ = validate_ast(prog, item)?; let _ = validate_ast(prog, item)?;
} }
Ok(None) Ok(None)
}, },
Expr::Cast { left, right } => { Expr::Cast { left, right } => {
validate_expr(prog, left.inner_mut())?; validate_expr(prog, left)?;
Ok(Some(right.inner_mut().clone())) Ok(Some(right.inner_mut().clone()))
} }
Expr::Literal(id, lit) => { 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); (*lit).hash(&mut hasher);
let hash = hasher.finish(); let hash = hasher.finish();
*id = hash.to_string(); *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()); prog.literals.insert(id.clone(), lit.clone());
Ok(Some(lit.to_type(prog)?)) Ok(Some(lit.to_type(prog)?))
} }
Expr::UnOp { typ, right } => { Expr::UnOp { typ, right } => {
match typ { match typ {
Punctuation::Not => { Punctuation::Not => {
let t = validate_expr(prog, right.inner_mut())?.unwrap().get_absolute_value(prog)?; let t = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
if !t.is_bool(prog) { if !t.is_bool(prog) {
lerror!(right.loc(), "Expected bool, got {t}"); lerror!(right.loc(), "Expected bool, got {t}");
} }
@@ -289,7 +319,7 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
} }
Punctuation::Minus | Punctuation::Minus |
Punctuation::Plus => { 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) { if !t.is_numeric(prog) {
lerror!(right.loc(), "Expected number, got {t}"); 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 => { 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 })) Ok(Some(Type::Ref { inner: Box::new(t), mutable: false }))
} }
Punctuation::Star => { 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) { if !t.is_ptr(prog) {
lerror!(right.loc(), "Expected pointer, got {t}"); 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 => { Punctuation::Shr => {
let t1_s; let t1_s;
let t2_s; let t2_s;
let t1 = validate_expr(prog, left.inner_mut())?.unwrap().get_absolute_value(prog)?; let t1 = validate_expr(prog, left)?.unwrap().get_absolute_value(prog)?;
let t2 = validate_expr(prog, right.inner_mut())?.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)) { if !(t1.is_numeric(prog) || t1.is_ptr(prog)) {
lerror!(right.loc(), "Expected number, got {t1}"); 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::Ge |
Punctuation::AndAnd | Punctuation::AndAnd |
Punctuation::OrOr => { Punctuation::OrOr => {
let t1 = validate_expr(prog, left.inner_mut())?.unwrap().get_absolute_value(prog)?; let t1 = validate_expr(prog, left)?.unwrap().get_absolute_value(prog)?;
let t2 = validate_expr(prog, right.inner_mut())?.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)) { if !(t1.is_numeric(prog) || t1.is_ptr(prog)) {
lerror!(right.loc(), "Expected number or pointer, got {t1}"); 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::AndEq |
Punctuation::OrEq | Punctuation::OrEq |
Punctuation::XorEq => { 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 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))) { if !((var_t.is_numeric(prog) || var_t.is_ptr(prog)) && (val_t.is_numeric(prog) || val_t.is_ptr(prog))) {
lerror!(left.loc(), "Mismatched types, assigning {val_t} to {var_t}"); lerror!(left.loc(), "Mismatched types, assigning {val_t} to {var_t}");
@@ -394,9 +424,9 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
Ok(None) Ok(None)
} }
Punctuation::Eq => { 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 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)) { if !(var_t == val_t || var_t.is_numeric(prog) && val_t.is_numeric(prog)) {
lerror!(left.loc(), "Mismatched types, assigning {val_t} to {var_t}"); lerror!(left.loc(), "Mismatched types, assigning {val_t} to {var_t}");
@@ -409,7 +439,7 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
} }
}, },
Expr::ArrayIndex { name, index } => { Expr::ArrayIndex { name, index } => {
let left = validate_expr(prog, name.inner_mut())?; let left = validate_expr(prog, name)?;
let Some(left) = left else { let Some(left) = left else {
lerror!(name.loc(), "expected value, got nothing, cannot index nothing"); lerror!(name.loc(), "expected value, got nothing, cannot index nothing");
bail!("") bail!("")
@@ -451,22 +481,61 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
} }
f(prog, &left, name.loc(), &index) f(prog, &left, name.loc(), &index)
}, },
Expr::PtrFieldAccess { left, right, offset } | Expr::PtrFieldAccess { left, right, offset, l_typ, r_typ } => {
Expr::FieldAccess { left, right, offset } => { let left = validate_expr(prog, &mut left.clone().unwrap())?;
let left = validate_expr(prog, left.clone().unwrap().inner_mut())?; let right = validate_expr(prog, &mut right.clone())?.unwrap();
let right = validate_expr(prog, right.clone().inner_mut())?.unwrap();
let Type::Owned(right) = right else { let Type::Owned(right) = right else {
panic!() panic!()
}; };
match left.unwrap().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) => { Type::Owned(name) => {
if let Some(strct) = prog.structs.get(&name) { if let Some(strct) = prog.structs.get(&name) {
for field in strct.inner().fields.iter() { for field in strct.inner().fields.iter() {
if field.0 == right { if field.0 == right {
*r_typ = Some(Box::new(field.1.inner().clone()));
*offset = strct.inner().clone().get_offset_of(prog, &right)?; *offset = strct.inner().clone().get_offset_of(prog, &right)?;
return Ok(Some(field.1.inner().clone())); return Ok(Some(field.1.inner().clone()));
} }
} }
} else {
panic!("couldnt find struct {name}");
} }
} }
v => panic!("{v:?}"), v => panic!("{v:?}"),
@@ -475,7 +544,7 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
Ok(None) Ok(None)
}, },
Expr::WhileLoop { test, body } => { 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() { for item in body.0.iter_mut() {
let _ = validate_ast(prog, item)?; 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<()> { fn validate_fn(prog: &mut Program, func: &mut Function) -> anyhow::Result<()> {
prog.scope = Some(Scope::default()); prog.scope = Some(Scope::default());
prog.curr_struct = func.struct_name.clone();
for param in &func.params { for param in &func.params {
let t = 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())); 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(()) 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 validate_type(prog: &mut Program, typ: &LocBox<Type>) -> anyhow::Result<Type> {
fn f(prog: &mut Program, typ: &Type, loc: &Loc) -> anyhow::Result<Type> { fn f(prog: &mut Program, typ: &Type, loc: &Loc) -> anyhow::Result<Type> {
match typ { match typ {
Type::SizedArray { inner, .. } | Type::SizedArray { .. } |
Type::UnsizedArray { inner, .. } | Type::UnsizedArray { .. } |
Type::Ref { inner, .. } => f(prog, inner, loc), Type::Ref { .. } => Ok(typ.clone()),
Type::Owned(ident) => { Type::Owned(ident) => {
if let Some(builtin) = get_builtin_from_name(&ident.0) { if let Some(builtin) = get_builtin_from_name(&ident.0) {
Ok(builtin) Ok(builtin)

View File

@@ -10,43 +10,130 @@ pub const SIZE: usize = 8;
#[cfg(target_arch="x86")] #[cfg(target_arch="x86")]
pub const SIZE: usize = 4; 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!( lazy_static!(
pub static ref TYPES_RAW: HashMap<&'static str, (usize, bool)> = [ pub static ref TYPES_RAW: Vec<Type> = [
("void", (0, false)), BuiltinType::void(),
("usize", (SIZE, false)), BuiltinType::usize(),
("isize", (SIZE, true)), BuiltinType::isize(),
("u8", (1, false)), BuiltinType::u8(),
("u16", (2, false)), BuiltinType::u16(),
("u32", (4, false)), BuiltinType::u32(),
("u64", (8, false)), BuiltinType::u64(),
("i8", (1, true)), BuiltinType::i8(),
("i16", (2, true)), BuiltinType::i16(),
("i32", (4, true)), BuiltinType::i32(),
("i64", (8, true)), BuiltinType::i64(),
("bool", (1, true)), BuiltinType::bool(),
].into(); BuiltinType::char()
pub static ref FUNCTIONS: HashMap<&'static str, (Vec<(&'static str, &'static str)>, &'static str)> = [ ].to_vec();
("__INTERNAL_syscall", (vec![ pub static ref FUNCTIONS: HashMap<&'static str, (Vec<(&'static str, Type)>, Type)> = [
("arg_count", "u8"), ("__INTERNAL_syscall0", (vec![
("sc_num", "u8"), ("sc_num", BuiltinType::usize()),
("args", "&[&void]") ], BuiltinType::usize())),
], "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(); ].into();
); );
pub fn get_builtin_from_name(name: &str) -> Option<Type> { pub fn get_builtin_from_name(type_name: &str) -> Option<Type> {
if let Some(t) = TYPES_RAW.get(name) { for t in TYPES_RAW.iter() {
Some(Type::Builtin { name: name.to_string(), size: t.0 as u8, signed: t.1 }) match t {
} else { Type::Builtin { name, .. } if name.as_str() == type_name => {
None return Some(t.clone());
}
Type::Builtin { .. } => (),
_ => unreachable!()
}
} }
None
} }
pub fn load_builtin(prog: &mut Program) { pub fn load_builtin(prog: &mut Program) {
let loc = Loc::new("(internal)", 0, 0); let loc = Loc::new("(internal)", 0, 0);
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( 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 }) 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() { for (name, (args, ret_typ)) in FUNCTIONS.iter() {
let mut params = Vec::new(); let mut params = Vec::new();
let mut ret_type = None; let mut ret_type = None;
if ret_typ.len() > 0 { if !ret_typ.is_void() {
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(), ret_typ.clone()));
ret_type = Some(LocBox::new(&Loc::default(), typ.inner().clone()));
} }
for (name, typ) in args { for (name, typ) in args {
let mut tokens = crate::tokeniser::tokenise(&typ, "(internal)").unwrap(); params.push((Ident(name.to_string()), LocBox::new(&Loc::new("(internal)", 0, 0), typ.clone())));
let typ = parse_type(&mut tokens).unwrap();
params.push((Ident(name.to_string()), LocBox::new(&Loc::new("(internal)", 0, 0), typ.inner().clone())));
} }
let f = Function { 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 b: &str
} }
fn Foo.new(a: usize, b: &str) -> &Foo { fn Foo.new(a1: usize, b: &str) -> &Foo {
return &Foo { return &Foo {
a: a, a: a1,
b: b b: b
}; };
} }
@@ -31,7 +31,7 @@ fn main() -> i32 {
} }
for (let i = 0; i < 10; i += 1) { for (let i = 0; i < 10; i += 1) {
print("nyaaa"); puts("nyaaa");
if (i > 7) { if (i > 7) {
break; break;
} else { } 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 @@
[]