Add external functions (both import and export), make includes work

still need to fix literal arrays, it has the same problem as struct
literals had with moving the literal into memory and modifying the
memory with variables
This commit is contained in:
2026-01-29 22:37:04 +02:00
parent 834b5b1213
commit 081ff9a27a
17 changed files with 220 additions and 101 deletions

View File

@@ -3,7 +3,7 @@ use clap::{error::ErrorKind, CommandFactory};
use crate::logger::Level;
#[derive(Debug, clap::Parser)]
#[derive(Debug, Default, clap::Parser)]
pub struct CliArgs {
/// Output more info, will get overwritten if -q|--quiet is specified
#[arg(long, short)]
@@ -13,6 +13,8 @@ pub struct CliArgs {
quiet: bool,
#[arg(long, short, value_parser=crate::targets::get_all_targets(), default_value_t=crate::targets::get_default_target())]
pub target: String,
#[arg(long="include", short='I', default_values=["include"])]
pub include_paths: Vec<String>,
/// Output file
#[arg(long, short, default_value="a.out")]
pub output: String,

View File

@@ -22,8 +22,10 @@ fn main() -> anyhow::Result<()> {
let data = std::fs::read_to_string(&fp).unwrap();
info!("Tokenising {file}");
let tokens = mclangc::tokeniser::tokenise(&data, &file)?;
// dbg!(&tokens);
info!("Parsing {file}");
let mut prog = mclangc::parser::parse_program(tokens)?;
let mut prog = mclangc::parser::parse_program(tokens, &cli)?;
// dbg!(&prog.ast);
info!("Validating {file}");
mclangc::validator::validate_code(&mut prog)?;
// dbg!(&prog.literals);

View File

@@ -16,6 +16,7 @@ pub enum Statement {
Enum(Enum),
ConstVar(ConstVar),
StaticVar(StaticVar),
Include,
Let(Let),
}

View File

@@ -2,7 +2,7 @@ use std::collections::{BTreeMap, HashMap};
use anyhow::{bail, Result};
use crate::{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::expr::StructLit, typ::parse_type}, tokeniser::Token};
use super::{ast::{expr::{Block, CallParams, Expr, IfBranchExpr, IfExpr, Path}, literal::Literal, TokenType}, parse_item, utils, Delimiter, Keyword};
@@ -37,14 +37,14 @@ const BINOP_LIST: &[TokenType] = &[
TokenType::Punct(Punctuation::Ge),
];
pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool) -> Result<Option<LocBox<Expr>>> {
pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool, cli: &CliArgs) -> Result<Option<LocBox<Expr>>> {
let res = if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenL)) {
Some(parse_group(tokens)?)
Some(parse_group(tokens, cli)?)
} else
if let Some(_) = utils::check(tokens, TokenType::ident("")) {
let p = parse_path(tokens)?;
if let Some(t) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
Some(parse_struct_literal(tokens, p.inner().unwrap_path())?)
Some(parse_struct_literal(tokens, p.inner().unwrap_path(), cli)?)
} else {
Some(p)
}
@@ -56,7 +56,7 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
TokenType::Punct(Punctuation::Ampersand),
TokenType::Punct(Punctuation::Star),
]) {
Some(parse_unop(tokens)?)
Some(parse_unop(tokens, cli)?)
} else
if let Some(_) = utils::check_from_many(tokens, &[
TokenType::string("", false),
@@ -66,15 +66,15 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
TokenType::Keyword(Keyword::True),
TokenType::Keyword(Keyword::False)
]) {
Some(parse_literal(tokens)?)
Some(parse_literal(tokens, cli)?)
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::While)) {
return Ok(Some(parse_while_loop(tokens)?));
return Ok(Some(parse_while_loop(tokens, cli)?));
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::For)) {
return Ok(Some(parse_for_loop(tokens)?));
return Ok(Some(parse_for_loop(tokens, cli)?));
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Loop)) {
return Ok(Some(parse_inf_loop(tokens)?));
return Ok(Some(parse_inf_loop(tokens, cli)?));
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Return)) {
return Ok(Some(parse_return(tokens)?));
return Ok(Some(parse_return(tokens, cli)?));
} else if let Some(kw) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Break)) {
let _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
return Ok(Some(LocBox::new(kw.loc(), Expr::Break)));
@@ -82,7 +82,7 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
let _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
return Ok(Some(LocBox::new(kw.loc(), Expr::Continue)));
} else if let Some(kw) = utils::check(tokens, TokenType::Keyword(Keyword::If)) {
return Ok(Some(LocBox::new(&kw.loc().clone(), Expr::If(parse_if(tokens)?))));
return Ok(Some(LocBox::new(&kw.loc().clone(), Expr::If(parse_if(tokens, cli)?))));
} else {
None
};
@@ -96,17 +96,17 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
res = parse_ptr_field_access(tokens, res)?;
}
if utils::check(tokens, TokenType::Delim(Delimiter::ParenL)).is_some() {
res = parse_fn_call(tokens, res)?;
res = parse_fn_call(tokens, res, cli)?;
}
if utils::check(tokens, TokenType::Keyword(Keyword::As)).is_some() {
res = parse_cast(tokens, res)?;
}
if utils::check(tokens, TokenType::Delim(Delimiter::SquareL)).is_some() {
res = parse_array_index(tokens, res)?;
res = parse_array_index(tokens, res, cli)?;
}
if let Some(_) = utils::check_from_many(tokens, BINOP_LIST) {
let v = parse_binop(tokens, res, precedence)?;
let v = parse_binop(tokens, res, precedence, cli)?;
if consume_semi {
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
}
@@ -124,9 +124,9 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
Ok(res)
}
fn parse_return(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
fn parse_return(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>> {
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Return), "")?;
let item = parse_expr(tokens, 0, true)?;
let item = parse_expr(tokens, 0, true, cli)?;
Ok(LocBox::new(kw.loc(), Expr::Return(Box::new(item))))
}
@@ -138,10 +138,10 @@ fn parse_cast(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr
right: Box::new(typ)
}))
}
fn parse_if(tokens: &mut Vec<Token>) -> Result<IfExpr> {
fn parse_if(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<IfExpr> {
let loc = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::If), "")?;
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
let Some(test) = parse_expr(tokens, 0, false)? else {
let Some(test) = parse_expr(tokens, 0, false, cli)? else {
lerror!(loc.loc(), "Expected test for if statement, got nothing");
bail!("")
};
@@ -151,7 +151,7 @@ fn parse_if(tokens: &mut Vec<Token>) -> Result<IfExpr> {
_ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR));
Block(Vec::new())
} else {
parse_block(tokens)?
parse_block(tokens, cli)?
}
} else {
lerror!(loc.loc(), "Expected '{{'");
@@ -159,14 +159,14 @@ fn parse_if(tokens: &mut Vec<Token>) -> Result<IfExpr> {
};
if let Some(_) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Else)) {
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::If)) {
let branch = IfBranchExpr::ElseIf(Box::new(parse_if(tokens)?));
let branch = IfBranchExpr::ElseIf(Box::new(parse_if(tokens, cli)?));
Ok(IfExpr {
test: Box::new(test),
body: block,
else_if: Some(branch)
})
} else {
let branch = IfBranchExpr::Else(parse_block(tokens)?);
let branch = IfBranchExpr::Else(parse_block(tokens, cli)?);
Ok(IfExpr {
test: Box::new(test),
body: block,
@@ -181,39 +181,39 @@ fn parse_if(tokens: &mut Vec<Token>) -> Result<IfExpr> {
})
}
}
fn parse_while_loop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
fn parse_while_loop(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>> {
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::While), "")?;
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
let Some(test) = parse_expr(tokens, 0, false)? else {
let Some(test) = parse_expr(tokens, 0, false, cli)? else {
lerror!(kw.loc(), "Expected test comparrison for while loop, got nothing");
bail!("")
};
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
let block = parse_block(tokens)?;
let block = parse_block(tokens, cli)?;
Ok(LocBox::new(kw.loc(), Expr::WhileLoop {
test: Box::new(test),
body: block
}))
}
fn parse_for_loop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
fn parse_for_loop(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>> {
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::For), "")?;
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
let Some(pre) = parse_item(tokens)? else {
let Some(pre) = parse_item(tokens, cli)? else {
lerror!(kw.loc(), "Expected init stat for a for loop, got nothing");
bail!("")
};
// Semicolon parsed out by parse_item above
let Some(test) = parse_expr(tokens, 0, false)? else {
let Some(test) = parse_expr(tokens, 0, false, cli)? else {
lerror!(kw.loc(), "Expected test comparrison for a for loop, got nothing");
bail!("")
};
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "");
let Some(post) = parse_expr(tokens, 0, false)? else {
let Some(post) = parse_expr(tokens, 0, false, cli)? else {
lerror!(kw.loc(), "Expected post expression (usually an index increment) for a for loop, got nothing");
bail!("")
};
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
let block = parse_block(tokens)?;
let block = parse_block(tokens, cli)?;
Ok(LocBox::new(kw.loc(), Expr::ForLoop {
init: Box::new(pre),
@@ -222,16 +222,16 @@ fn parse_for_loop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
body: block
}))
}
fn parse_inf_loop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
fn parse_inf_loop(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>> {
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Loop), "")?;
let block = parse_block(tokens)?;
let block = parse_block(tokens, cli)?;
Ok(LocBox::new(kw.loc(), Expr::InfLoop { body: block }))
}
fn parse_fn_call(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr>> {
fn parse_fn_call(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli: &CliArgs) -> Result<LocBox<Expr>> {
match left.inner() {
Expr::FieldAccess { .. } |
Expr::PtrFieldAccess { .. } => {
return parse_member_function_call(tokens, left);
return parse_member_function_call(tokens, left, cli);
}
_ => ()
}
@@ -244,7 +244,7 @@ fn parse_fn_call(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<E
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
break;
}
let Some(param) = parse_expr(tokens, 0, false)? else {break};
let Some(param) = parse_expr(tokens, 0, false, cli)? else {break};
params.push(param);
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
if let None = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
@@ -256,9 +256,9 @@ fn parse_fn_call(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<E
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "");
Ok(LocBox::new(start.loc(), Expr::Call { path: Box::new(left), params: CallParams(params) }))
}
fn parse_array_index(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr>> {
fn parse_array_index(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli: &CliArgs) -> Result<LocBox<Expr>> {
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareL), "")?;
let Some(idx) = parse_expr(tokens, 0, false)? else {
let Some(idx) = parse_expr(tokens, 0, false, cli)? else {
lerror!(start.loc(), "Expected index for in array index but found nothing.");
bail!("")
};
@@ -288,7 +288,7 @@ fn parse_field_access(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<Loc
}))
}
fn parse_member_function_call(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr>> {
fn parse_member_function_call(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli: &CliArgs) -> Result<LocBox<Expr>> {
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "unreachable")?;
let mut params = Vec::new();
@@ -296,7 +296,7 @@ fn parse_member_function_call(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Re
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
break;
}
let Some(param) = parse_expr(tokens, 0, false)? else {break};
let Some(param) = parse_expr(tokens, 0, false, cli)? else {break};
params.push(param);
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
if let None = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
@@ -331,7 +331,7 @@ fn parse_ptr_field_access(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result
}))
}
fn parse_literal(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
fn parse_literal(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>> {
if let Some(tkn) = utils::check_consume(tokens, TokenType::Keyword(Keyword::True)) {
return Ok(LocBox::new(tkn.loc(), Expr::Literal(String::new(), Literal::Bool(true))));
} else
@@ -354,28 +354,15 @@ fn parse_literal(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
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()))));
}
if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Comma) {
let first = parse_expr(tokens, 0, false)?;
let Some(first) = first else { unreachable!() };
let mut values = Vec::new();
values.push(first);
while !tokens.is_empty() {
let Some(val) = parse_expr(tokens, 0, false)? else{break};
values.push(val);
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
break;
}
}
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
return Ok(LocBox::new(start.loc(), Expr::Literal(String::new(), Literal::Array(values))));
} else if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Semi) {
let Some(typ) = parse_expr(tokens, 0, true)? else {
/*if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Comma) {
} else */
if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Semi) {
let Some(typ) = parse_expr(tokens, 0, true, cli)? else {
lerror!(start.loc(), "Expected value, found nothing");
bail!("")
};
let count = parse_expr(tokens, 0, false)?.unwrap();
let count = parse_expr(tokens, 0, false, cli)?.unwrap();
let Expr::Literal(_, Literal::Number(count)) = count.inner() else {
lerror!(count.loc(), "a repeating array accepts only literal numbers for count argument");
@@ -387,18 +374,35 @@ fn parse_literal(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
count: count.val
})));
} else {
let first = parse_expr(tokens, 0, false, cli)?;
let Some(first) = first else { unreachable!() };
let mut values = Vec::new();
values.push(first);
while !tokens.is_empty() {
let Some(val) = parse_expr(tokens, 0, false, cli)? else{break};
values.push(val);
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
break;
}
}
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
return Ok(LocBox::new(start.loc(), Expr::Literal(String::new(), Literal::Array(values))));
/*
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());
} else {
lerror!(start.loc(), "Expected a , or ; as a separator in a literal array (normal, or repeating, respectively), but found nothing");
}
bail!("")
*/
}
}
unreachable!()
}
fn parse_struct_literal(tokens: &mut Vec<Token>, name: Path) -> Result<LocBox<Expr>> {
fn parse_struct_literal(tokens: &mut Vec<Token>, name: Path, cli: &CliArgs) -> Result<LocBox<Expr>> {
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyL), "")?;
let mut fields = BTreeMap::new();
while !tokens.is_empty() {
@@ -408,7 +412,7 @@ fn parse_struct_literal(tokens: &mut Vec<Token>, name: Path) -> Result<LocBox<Ex
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?;
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "wtf?")?;
let typ = parse_expr(tokens, 0, false)?.unwrap();
let typ = parse_expr(tokens, 0, false, cli)?.unwrap();
fields.insert(name.tt().unwrap_ident(), typ);
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyR), "")?;
@@ -418,9 +422,9 @@ fn parse_struct_literal(tokens: &mut Vec<Token>, name: Path) -> Result<LocBox<Ex
Ok(LocBox::new(start.loc(), Expr::Struct(String::new(), StructLit { path: name, fields })))
}
fn parse_group(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
fn parse_group(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>> {
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
let Some(expr) = parse_expr(tokens, 0, false)? else {
let Some(expr) = parse_expr(tokens, 0, false, cli)? else {
lerror!(start.loc(), "Expected expr found nothing");
bail!("")
};
@@ -445,7 +449,7 @@ fn parse_path(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
Ok(LocBox::new(part.loc(), Expr::Path(Path(buf))))
}
fn parse_unop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
fn parse_unop(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Expr>> {
let typ = utils::check_consume_or_err_from_many(tokens, &[
TokenType::Punct(Punctuation::Not),
TokenType::Punct(Punctuation::Plus), // Make number positive
@@ -456,7 +460,7 @@ fn parse_unop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
let loc = typ.loc().clone();
let TokenType::Punct(typ) = typ.tt().clone() else {unreachable!()};
let Some(right) = parse_expr(tokens, 5, false)? else {
let Some(right) = parse_expr(tokens, 5, false, cli)? else {
lerror!(&loc, "Expected expression after unary token, found nothing");
bail!("")
};
@@ -466,7 +470,7 @@ fn parse_unop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
}))
}
fn parse_binop(tokens: &mut Vec<Token>, mut lhs: LocBox<Expr>, precedence: usize) -> Result<LocBox<Expr>> {
fn parse_binop(tokens: &mut Vec<Token>, mut lhs: LocBox<Expr>, precedence: usize, cli: &CliArgs) -> Result<LocBox<Expr>> {
// TODO: https://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudocode
loop {
@@ -497,7 +501,7 @@ fn parse_binop(tokens: &mut Vec<Token>, mut lhs: LocBox<Expr>, precedence: usize
}
_ = tokens.pop();
let Some(rhs) = parse_expr(tokens, rp, false)? else {break;};
let Some(rhs) = parse_expr(tokens, rp, false, cli)? else {break;};
lhs = LocBox::new(&op_loc, Expr::BinOp {
typ: op,
left: Box::new(lhs),
@@ -511,14 +515,14 @@ fn parse_binop(tokens: &mut Vec<Token>, mut lhs: LocBox<Expr>, precedence: usize
}
pub fn parse_block(tokens: &mut Vec<Token>) -> Result<Block> {
pub fn parse_block(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<Block> {
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyL), "")?;
let mut items = Vec::new();
while !tokens.is_empty() {
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyR)) {
break;
}
if let Some(item) = parse_item(tokens)? {
if let Some(item) = parse_item(tokens, cli)? {
items.push(item);
} else {
break;

View File

@@ -2,7 +2,7 @@ use std::collections::HashMap;
use ast::{expr::Block, Ast, Program};
use crate::tokeniser::{Token, tokentype::*};
use crate::{cli::CliArgs, tokeniser::{Token, tokentype::*}};
pub mod ast;
mod expr;
@@ -12,11 +12,11 @@ pub mod typ;
type Result<T> = anyhow::Result<T>;
pub fn parse_program(mut tokens: Vec<Token>) -> Result<Program> {
pub fn parse_program(mut tokens: Vec<Token>, cli: &CliArgs) -> Result<Program> {
let mut prog_body = Vec::new();
while !tokens.is_empty() {
if let Some(item) = parse_item(&mut tokens)? {
if let Some(item) = parse_item(&mut tokens, cli)? {
prog_body.push(item);
} else {
break
@@ -40,11 +40,11 @@ pub fn parse_program(mut tokens: Vec<Token>) -> Result<Program> {
})
}
fn parse_item(tokens: &mut Vec<Token>) -> Result<Option<Ast>> {
if let Some(stat) = stat::parse_statement(tokens)? {
fn parse_item(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<Option<Ast>> {
if let Some(stat) = stat::parse_statement(tokens, cli)? {
return Ok(Some(Ast::Statement(stat)));
}
if let Some(expr) = expr::parse_expr(tokens, 0, true)? {
if let Some(expr) = expr::parse_expr(tokens, 0, true, cli)? {
return Ok(Some(Ast::Expr(expr)));
}
Ok(None)

View File

@@ -1,8 +1,9 @@
use anyhow::bail;
use crate::cli::CliArgs;
use crate::common::loc::LocBox;
use crate::lerror;
use crate::parser::ast::TokenType;
use crate::{cli, lerror};
use crate::parser::ast::{TString, TokenType};
use crate::parser::ast::statement::Let;
use crate::parser::expr::parse_expr;
use crate::parser::{Delimiter, Ident, Keyword, Punctuation};
@@ -15,18 +16,18 @@ use super::ast::statement::{ConstVar, Enum, Function, Statement, StaticVar, Stru
type Result<T> = anyhow::Result<T>;
pub fn parse_statement(tokens: &mut Vec<Token>) -> Result<Option<LocBox<Statement>>> {
pub fn parse_statement(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<Option<LocBox<Statement>>> {
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Fn)) {
Ok(Some(parse_fn(tokens)?))
Ok(Some(parse_fn(tokens, cli)?))
} else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Type)) {
Ok(Some(parse_type_alias(tokens)?))
} else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Const)) {
Ok(Some(parse_constant(tokens)?))
Ok(Some(parse_constant(tokens, cli)?))
} else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Static)) {
Ok(Some(parse_static(tokens)?))
Ok(Some(parse_static(tokens, cli)?))
} else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Struct)) {
Ok(Some(parse_struct(tokens)?))
@@ -35,13 +36,42 @@ pub fn parse_statement(tokens: &mut Vec<Token>) -> Result<Option<LocBox<Statemen
Ok(Some(parse_enum(tokens)?))
} else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Let)) {
Ok(Some(parse_let(tokens)?))
Ok(Some(parse_let(tokens, cli)?))
} else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Include)) {
Ok(Some(parse_include(tokens, cli)?))
} else {
Ok(None)
}
}
fn parse_include(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Statement>> {
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Include), "")?;
let TokenType::String(include_path) = utils::check_consume_or_err(tokens, TokenType::String(TString::default()), "")?.tt().clone() else {panic!()};
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
let cwd = std::env::current_dir()?;
let mut found = false;
for p in &cli.include_paths {
let path = cwd.join(p);
let path = path.join(&include_path.val);
if path.exists() {
let data = std::fs::read_to_string(&path)?;
let mut tokens_imp = crate::tokeniser::tokenise(&data, &path.to_string_lossy().to_string())?;
tokens.append(&mut tokens_imp);
found = true;
break;
}
}
if !found {
lerror!(kw.loc(), "Could not find file {:?}", include_path.val);
bail!("")
}
Ok(LocBox::new(kw.loc(), Statement::Include))
}
fn parse_enum(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Enum), "")?;
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
@@ -92,14 +122,14 @@ fn parse_struct(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
Ok(LocBox::new(kw.loc(), Statement::Struct(Struct { name, fields })))
}
fn parse_static(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
fn parse_static(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Statement>> {
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Static), "")?;
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
let typ = parse_type(tokens)?;
let eq = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?;
let Some(val) = parse_expr(tokens, 0, false)? else {
let Some(val) = parse_expr(tokens, 0, false, cli)? else {
lerror!(eq.loc(), "Expected expression found nothing");
bail!("")
};
@@ -107,7 +137,7 @@ fn parse_static(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
Ok(LocBox::new(kw.loc(), Statement::StaticVar(StaticVar { name, typ, val })))
}
fn parse_let(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
fn parse_let(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Statement>> {
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Let), "")?;
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
let mut typ = None;
@@ -116,7 +146,7 @@ fn parse_let(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
typ = Some(parse_type(tokens)?);
}
if let Some(eq) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Eq)) {
let Some(_val) = parse_expr(tokens, 0, false)? else {
let Some(_val) = parse_expr(tokens, 0, false, cli)? else {
lerror!(eq.loc(), "Expected expression found nothing");
bail!("")
};
@@ -125,7 +155,7 @@ fn parse_let(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
Ok(LocBox::new(kw.loc(), Statement::Let(Let{ name, typ, val })))
}
fn parse_constant(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
fn parse_constant(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Statement>> {
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Const), "")?;
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Fn)) {
@@ -135,7 +165,7 @@ fn parse_constant(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
let typ = parse_type(tokens)?;
let eq = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?;
let Some(val) = parse_expr(tokens, 0, false)? else {
let Some(val) = parse_expr(tokens, 0, false, cli)? else {
lerror!(eq.loc(), "Expected expression found nothing");
bail!("")
};
@@ -153,7 +183,7 @@ fn parse_type_alias(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
Ok(LocBox::new(kw.loc(), Statement::TypeAlias(TypeAlias { name, typ })))
}
fn parse_fn(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
fn parse_fn(tokens: &mut Vec<Token>, cli: &CliArgs) -> Result<LocBox<Statement>> {
// Just remove the kw since we checked it before
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Fn), "")?;
@@ -173,7 +203,7 @@ fn parse_fn(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
}
let body;
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
body = Some(parse_block(tokens)?);
body = Some(parse_block(tokens, cli)?);
} else {
// Check if its just a declaration
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;

View File

@@ -1,6 +1,6 @@
use anyhow::{Result, bail};
use crate::{common::loc::LocBox, lerror, parser::{Delimiter, ast::{expr::Expr, literal::Literal}}, tokeniser::Token};
use crate::{cli::CliArgs, common::loc::LocBox, lerror, parser::{Delimiter, ast::{expr::Expr, literal::Literal}}, tokeniser::Token};
use super::{ast::{typ::Type, TokenType}, expr::parse_expr, utils, Keyword, Punctuation};
@@ -25,7 +25,7 @@ pub fn parse_type(tokens: &mut Vec<Token>) -> Result<LocBox<Type>> {
}
let itm_typ = parse_type(tokens)?;
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Semi)) {
let count = parse_expr(tokens, 0, false)?.unwrap();
let count = parse_expr(tokens, 0, false, &CliArgs::default())?.unwrap();
match count.inner() {
Expr::Literal(_, Literal::Number(_)) |

View File

@@ -1,5 +1,5 @@
use crate::{common::{Loc, loc::LocBox}, parser::ast::{Ast, Ident, Program, Punctuation, expr::{Expr, IfBranchExpr}, literal::Literal, statement::{ConstDataTyp, Function, Statement, get_constant_data_as_bytes}, typ::Type}, targets::Target};
use std::{collections::HashMap, fs::File, io::Write};
use std::{collections::HashMap, fs::File, io::Write, process};
pub struct AsmGen;
const RUNTIME_CODE: &'static str = include_str!("./runtime.s");
@@ -17,8 +17,10 @@ impl Target for AsmGen {
fn write_code(&mut self, program: &crate::parser::ast::Program, f: &mut File) -> anyhow::Result<()> {
writeln!(f, "bits 64")?;
writeln!(f, "global _start")?;
self.write_extern_func_decls(program, f)?;
writeln!(f, "section .text")?;
writeln!(f, "{}", RUNTIME_CODE)?;
for item in &program.ast.0 {
match item {
Ast::Statement(stat) => {
@@ -220,6 +222,20 @@ impl AsmGen {
Ok(())
}
pub fn write_extern_func_decls(&self, program: &Program, f: &mut File) -> anyhow::Result<()> {
for (name, func) in &program.functions {
match func.inner().qual_extern {
Some(_) if func.inner().body.is_some() => {
writeln!(f, "global {name}")?;
}
Some(_) if func.inner().body.is_none() => {
writeln!(f, "extern {name}")?;
}
_ => ()
}
}
Ok(())
}
pub fn write_ast(&self, program: &Program, f: &mut impl Write, fc: &mut FunctionCtx, ast: &Ast) -> anyhow::Result<()> {
match ast {
Ast::Expr(expr) => self.write_expr(program, f, fc, expr.inner())?,
@@ -529,7 +545,6 @@ impl AsmGen {
}
}
Expr::Path(path) => {
dbg!(&path);
let ident = path.0.last().unwrap().clone();
if let Some(var) = fc.get(&ident) {
match var {

View File

@@ -1,6 +1,6 @@
; generated with godbolt: https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA7AQwFtMQByARg9KtQYEAysib0QXACx8BBAKoBnTAAUAHpwAMvAFYTStJg1AB9U8lJL6yAngGVG6AMKpaAVxYMQAJlIOAMngMmABy7gBGmMQgAMw%2BAA6oCoS2DM5uHt6kCUk2AgFBoSwRUbEWmFa5DEIETMQEae6ePpaY1inVtQT5IeGRMc01dQ0ZA53dhcX9AJQWqK7EyOwcAPQAVADUACoAnnGY69tzxOtoWOsIkZik6yTrtKhM6OuG65iqrHH0AHTrq8sApBoAIL/LzRQLINxnf7RRwKAjoWh4MJfBAw7CAkFgiFQ/YwuEI2yo9GY0HghiQ1zQ2Hw/CCYnRDHAzFJABemGMBHWLG2Cl5olotAgrkCBAAHJznsRgMY0K5BNcRYIJVy%2BQoBbRjAx3NcAG6oPBPVarWrABRTdb/ADsACFMesHes2RyucRMAQYXbgY71m7gHh4ZEnXh2ZLiExVM8FCwIKCvOHVHGLTCACJO/liTXalie%2B2Ov0BghB51h/BRmNx4j4JOW6JpjS5735zD%2BwPHEuupLl2NeeNJGup9YN6JeoE%2Bgtt4Oh13oSNMaM9%2BOzgd1oeNsfN1tF9shl2%2BrgabuVg8r%2Bvr8ctwvF3dhsUO%2BcV3vEMU12tnkd5h0T7dTvfEACc94LpW/6voOw6jp%2B6x4FQ6wQKaspzIItbYEOFpVngb5wR2Uymgo/wAKw2g2BEpuejowXBCFysh6LrFw6FdoOEA4XhhE2lwhFkR%2BTYOpR8HSoh8pcnRXjobOWEsTeBC4dK%2BFEV4XHkXxsECTKNEiYy6zROhB6SaxcnsdESk8RuKlUYJGkoeskjoXe%2BnSbJZrsZIJmjj6/HUUhmmoQR6GAQ505OfJNoEW5pK8eW6z6gYNj0HBUE%2BnGaoanGiWOiAlq9qmTBxhAboyelDqZXGuW9vlEYzFlXhkeVGFVXGQh5cQSQNb26DNbOMxFY6lbNQebXxs1YqDcQzX/lMPUlU%2ByCJr21zHpx83VWwLAkNsaWRZNplQQV8wML67rrtaKYcDMtCcARvCeBwWikKgnCOE6RyLFl0Q8KQBCaGdMwANYgARGj6JwkjXd992cLwCggEDX23WdpBwLASBoCwcR0JE5CUKj6P0FEyDAFwBE%2BDQtDbtDEBhODYSBLU2ycB9NPMMQ2wAPJhNorRwx9qNsIIrMMLQ9Pw6QWBhK4wCOJm0PcLwWAsIYwDiCL%2BBum0uqYDLd1vK0rhFgzvCiuU4NImE4Ys84WDgwQxB4CwBukBrxBhIkmAppgCtGEiRjfTMVAGGaABqeCYAA7qzew3R9/CCCIYjsFIMiCIoKjqCLuhcPoisgKYsr6Mi0OQDMqBxJUMsALSs9EvCoE7ttYIXEAzC0bR2BADjDJ4XA%2BP4gQ9EUfTvVkiTJAInd6Nko8MOMvRRJnLeVB0QwuI0EjNOUXPtIMXR9xMg/z9v48H2Mu%2BzxIzcvQn52XWDIsPRw6yqGKABs5fP5I6zAMgyD0QRXxeHBXAhBbhkgYrwOGWgph/QBkDC6HBQakBunde%2BUMYafV9tfDgXhb7IMhug%2BGUDHaRCSHYSQQA
; removed "qword ptr" and replaced with "qword"
syscall:
__INTERNAL_syscall:
mov ecx, edi
mov r11, rdx
movzx eax, sil

View File

@@ -49,7 +49,8 @@ fn validate_stat(prog: &mut Program, stat: &mut LocBox<Statement>, current_state
Statement::Enum(enm) => validate_enum(prog, enm)?,
Statement::Struct(strct) => validate_struct(prog, strct)?,
Statement::TypeAlias(alias) => validate_type_alias(prog, alias)?,
Statement::Let(_) => unreachable!()
Statement::Let(_) => unreachable!(),
Statement::Include => (),
}
Ok(())
}
@@ -183,8 +184,6 @@ pub fn validate_expr(prog: &mut Program, expr: &mut Expr) -> anyhow::Result<Opti
let t = validate_expr(prog, param.inner_mut())?.clone();
match t {
Some(t) => {
dbg!(&t);
dbg!(&ft);
if t.get_absolute_value(prog)? != ft.get_absolute_value(prog)? {
lerror!(param.loc(), "expected {ft}, got {}", t);
bail!("")
@@ -563,6 +562,7 @@ fn check_that_types_exist_for_items(prog: &mut Program, items: &Vec<Ast>) -> any
}
}
},
Statement::Include |
Statement::Let { .. } => (),
Statement::ConstVar(var) => {
validate_type(prog, &var.typ)?;
@@ -653,6 +653,7 @@ fn collect_types_and_constants(prog: &mut Program, items: &mut Vec<Ast>) {
let typ = alias.clone().typ.inner_mut().clone();
prog.types.insert(alias.name.clone(), LocBox::new(stat.loc(), typ));
}
Statement::Include |
Statement::Let { .. } => (),
Statement::ConstVar(var) => {
prog.const_vars.insert(var.name.clone(), var.clone());

View File

@@ -26,7 +26,7 @@ lazy_static!(
("bool", (1, true)),
].into();
pub static ref FUNCTIONS: HashMap<&'static str, (Vec<(&'static str, &'static str)>, &'static str)> = [
("syscall", (vec![
("__INTERNAL_syscall", (vec![
("arg_count", "u8"),
("sc_num", "u8"),
("args", "&[&void]")