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:
Gvidas Juknevičius 2026-01-29 22:37:04 +02:00
parent 834b5b1213
commit 081ff9a27a
17 changed files with 220 additions and 101 deletions

2
.gitignore vendored
View File

@ -1,4 +1,6 @@
/target
/*.c
/*.s
/*.o
/*.ssa
/test

View File

@ -0,0 +1,55 @@
" Vim syntax file
" Language: MClang4
" Maintainer: MCorange
" Maintainer: xomf
if exists("b:current_syntax")
finish
endif
syn match MCLangSlash '/' containedin=ALLBUT,MCLangComment
syn match MCLangComment '//.*'
syn region MCLangComment start='/\*' end='\*/' contains=@Spell
syn keyword MCLangKeywords fn if else struct enum type while for break continue let const mut static include extern return loop as
" syn keyword MCLangOp + -
syn keyword MCLangTypes void u8 u16 u32 u64 i8 i16 i32 i64 usize isize str
syn keyword MCLangBool true false
syn keyword MCLangConst NULL
" i8 i16 i32 i64 f32 f64
syn region MCLangString start=+"+ skip=+\\.+ end=+"+ contains=@Spell
syn region MCLangChar start=+'+ skip=+\\.+ end=+'+
syn match MCLangFuncCall '\<[A-Za-z_][A-Za-z0-9_]*\>\ze\s*(' containedin=ALLBUT,MCLangString,MCLangChar,MCLangComment
syn match MCLangFuncCall '\<[A-Za-z_][A-Za-z0-9_]*::[A-Za-z_][A-Za-z0-9_]*\>\ze\s*(' containedin=ALLBUT,MCLangString,MCLangChar,MCLangComment
"syn match MCLangFuncDef '\<fn\s\+\zs[A-Za-z_][A-Za-z0-9_]*\.\?\>' containedin=ALLBUT,MCLangString,MCLangChar,MCLangComment
"syn match MCLangConstName '\<const\s\+\zs[A-Za-z_][A-Za-z0-9_]*\>' containedin=ALLBUT,MCLangString,MCLangChar,MCLangComment
syn match MCLangIdentifier '\<[A-Za-z_][A-Za-z0-9_]*\>'
syn match MCLangOp '[+\-*%&|^!~<>]=\?\|==\|!=\|<=\|>=\|&&\|\\|\|::\|:\|[;,\.]' containedin=ALLBUT,MCLangString,MCLangChar,MCLangComment
syn match MCLangNumber '\<\(0b[01]\+\|0x[0-9A-Fa-f]\+\|\d\+\.\d*\|\.\d\+\|\d\+\)\>' containedin=ALLBUT,MCLangString,MCLangChar,MCLangComment
syn match MCLangDelim /[()]/
syn match MCLangDelim /[{}]/
syn match MCLangDelim /[\[\]]/
let b:current_syntax = "MCLang4"
hi def link MCLangComment Comment
hi def link MCLangTypes Type
hi def link MCLangString String
hi def link MCLangChar Character
hi def link MCLangNumber Constant
hi def link MCLangConst Constant
"hi def link MCLangConstName Constant
hi def link MCLangBool Boolean
hi def link MCLangDelim Delimiter
hi def link MCLangOp Operator
hi def link MCLangKeywords Keyword
"hi def link MCLangIdentifier Identifier
hi def link MCLangFuncCall Function
"hi def link MCLangFuncDef Function

7
include/std.mcl Normal file
View File

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

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]")

BIN
test

Binary file not shown.

View File

@ -1,6 +1,6 @@
type str = [u8];
include "std.mcl";
struct Foo {
a: usize,

BIN
test.o

Binary file not shown.