From 081ff9a27a5a1bbc12f256e6819da2f8379a49ec Mon Sep 17 00:00:00 2001 From: MCorange Date: Thu, 29 Jan 2026 22:37:04 +0200 Subject: [PATCH] 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 --- .gitignore | 2 + editor_support/mclang4.vim | 55 +++++++++ include/std.mcl | 7 ++ src/cli.rs | 4 +- src/main.rs | 4 +- src/parser/ast/statement.rs | 1 + src/parser/expr.rs | 140 +++++++++++----------- src/parser/mod.rs | 12 +- src/parser/stat.rs | 60 +++++++--- src/parser/typ.rs | 4 +- src/targets/x86_64/asmgen/linux/mod.rs | 19 ++- src/targets/x86_64/asmgen/linux/runtime.s | 2 +- src/validator/mod.rs | 7 +- src/validator/predefined.rs | 2 +- test | Bin 9992 -> 10016 bytes test.mcl | 2 +- test.o | Bin 2720 -> 2768 bytes 17 files changed, 220 insertions(+), 101 deletions(-) create mode 100644 editor_support/mclang4.vim create mode 100644 include/std.mcl diff --git a/.gitignore b/.gitignore index aea5fbe..b138ae5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /target /*.c /*.s +/*.o /*.ssa +/test diff --git a/editor_support/mclang4.vim b/editor_support/mclang4.vim new file mode 100644 index 0000000..4666208 --- /dev/null +++ b/editor_support/mclang4.vim @@ -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 '\' containedin=ALLBUT,MCLangString,MCLangChar,MCLangComment +"syn match MCLangConstName '\' 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 + + + diff --git a/include/std.mcl b/include/std.mcl new file mode 100644 index 0000000..ffb1822 --- /dev/null +++ b/include/std.mcl @@ -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]); +} diff --git a/src/cli.rs b/src/cli.rs index 642bf82..d7a2146 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -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, /// Output file #[arg(long, short, default_value="a.out")] pub output: String, diff --git a/src/main.rs b/src/main.rs index 4c537b9..c7cc397 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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); diff --git a/src/parser/ast/statement.rs b/src/parser/ast/statement.rs index 8aee698..2cc4b40 100644 --- a/src/parser/ast/statement.rs +++ b/src/parser/ast/statement.rs @@ -16,6 +16,7 @@ pub enum Statement { Enum(Enum), ConstVar(ConstVar), StaticVar(StaticVar), + Include, Let(Let), } diff --git a/src/parser/expr.rs b/src/parser/expr.rs index 1a383f6..2135dc3 100644 --- a/src/parser/expr.rs +++ b/src/parser/expr.rs @@ -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, precedence: usize, consume_semi: bool) -> Result>> { +pub fn parse_expr(tokens: &mut Vec, precedence: usize, consume_semi: bool, cli: &CliArgs) -> Result>> { 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, 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, 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, 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, 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, precedence: usize, consume_semi: bool Ok(res) } -fn parse_return(tokens: &mut Vec) -> Result> { +fn parse_return(tokens: &mut Vec, cli: &CliArgs) -> Result> { 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, left: LocBox) -> Result) -> Result { +fn parse_if(tokens: &mut Vec, cli: &CliArgs) -> Result { 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) -> Result { _ = 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) -> Result { }; 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) -> Result { }) } } -fn parse_while_loop(tokens: &mut Vec) -> Result> { +fn parse_while_loop(tokens: &mut Vec, cli: &CliArgs) -> Result> { 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) -> Result> { +fn parse_for_loop(tokens: &mut Vec, cli: &CliArgs) -> Result> { 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) -> Result> { body: block })) } -fn parse_inf_loop(tokens: &mut Vec) -> Result> { +fn parse_inf_loop(tokens: &mut Vec, cli: &CliArgs) -> Result> { 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, left: LocBox) -> Result> { +fn parse_fn_call(tokens: &mut Vec, left: LocBox, cli: &CliArgs) -> Result> { 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, left: LocBox) -> Result, left: LocBox) -> Result, left: LocBox) -> Result> { +fn parse_array_index(tokens: &mut Vec, left: LocBox, cli: &CliArgs) -> Result> { 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, left: LocBox) -> Result, left: LocBox) -> Result> { +fn parse_member_function_call(tokens: &mut Vec, left: LocBox, cli: &CliArgs) -> Result> { 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, left: LocBox) -> 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, left: LocBox) -> Result })) } -fn parse_literal(tokens: &mut Vec) -> Result> { +fn parse_literal(tokens: &mut Vec, cli: &CliArgs) -> Result> { 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) -> Result> { 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) -> Result> { 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, name: Path) -> Result> { +fn parse_struct_literal(tokens: &mut Vec, name: Path, cli: &CliArgs) -> Result> { 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, name: Path) -> Result, name: Path) -> Result) -> Result> { +fn parse_group(tokens: &mut Vec, cli: &CliArgs) -> Result> { 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) -> Result> { Ok(LocBox::new(part.loc(), Expr::Path(Path(buf)))) } -fn parse_unop(tokens: &mut Vec) -> Result> { +fn parse_unop(tokens: &mut Vec, cli: &CliArgs) -> Result> { 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) -> Result> { 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) -> Result> { })) } -fn parse_binop(tokens: &mut Vec, mut lhs: LocBox, precedence: usize) -> Result> { +fn parse_binop(tokens: &mut Vec, mut lhs: LocBox, precedence: usize, cli: &CliArgs) -> Result> { // TODO: https://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudocode loop { @@ -497,7 +501,7 @@ fn parse_binop(tokens: &mut Vec, mut lhs: LocBox, 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, mut lhs: LocBox, precedence: usize } -pub fn parse_block(tokens: &mut Vec) -> Result { +pub fn parse_block(tokens: &mut Vec, cli: &CliArgs) -> Result { 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; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 64a2dd3..92073d1 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -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 = anyhow::Result; -pub fn parse_program(mut tokens: Vec) -> Result { +pub fn parse_program(mut tokens: Vec, cli: &CliArgs) -> Result { 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) -> Result { }) } -fn parse_item(tokens: &mut Vec) -> Result> { - if let Some(stat) = stat::parse_statement(tokens)? { +fn parse_item(tokens: &mut Vec, cli: &CliArgs) -> Result> { + 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) diff --git a/src/parser/stat.rs b/src/parser/stat.rs index f2ffcaf..aa81f48 100644 --- a/src/parser/stat.rs +++ b/src/parser/stat.rs @@ -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 = anyhow::Result; -pub fn parse_statement(tokens: &mut Vec) -> Result>> { +pub fn parse_statement(tokens: &mut Vec, cli: &CliArgs) -> Result>> { 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) -> Result, cli: &CliArgs) -> Result> { + 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) -> Result> { 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) -> Result> { Ok(LocBox::new(kw.loc(), Statement::Struct(Struct { name, fields }))) } -fn parse_static(tokens: &mut Vec) -> Result> { +fn parse_static(tokens: &mut Vec, cli: &CliArgs) -> Result> { 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) -> Result> { Ok(LocBox::new(kw.loc(), Statement::StaticVar(StaticVar { name, typ, val }))) } -fn parse_let(tokens: &mut Vec) -> Result> { +fn parse_let(tokens: &mut Vec, cli: &CliArgs) -> Result> { 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) -> Result> { 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) -> Result> { _ = 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) -> Result> { +fn parse_constant(tokens: &mut Vec, cli: &CliArgs) -> Result> { 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) -> Result> { _ = 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) -> Result> { Ok(LocBox::new(kw.loc(), Statement::TypeAlias(TypeAlias { name, typ }))) } -fn parse_fn(tokens: &mut Vec) -> Result> { +fn parse_fn(tokens: &mut Vec, cli: &CliArgs) -> Result> { // 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) -> Result> { } 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), "")?; diff --git a/src/parser/typ.rs b/src/parser/typ.rs index e9d954f..7dcff38 100644 --- a/src/parser/typ.rs +++ b/src/parser/typ.rs @@ -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) -> Result> { } 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(_)) | diff --git a/src/targets/x86_64/asmgen/linux/mod.rs b/src/targets/x86_64/asmgen/linux/mod.rs index 4d0585b..a8de32a 100644 --- a/src/targets/x86_64/asmgen/linux/mod.rs +++ b/src/targets/x86_64/asmgen/linux/mod.rs @@ -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 { diff --git a/src/targets/x86_64/asmgen/linux/runtime.s b/src/targets/x86_64/asmgen/linux/runtime.s index dfe8911..976f9be 100644 --- a/src/targets/x86_64/asmgen/linux/runtime.s +++ b/src/targets/x86_64/asmgen/linux/runtime.s @@ -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 diff --git a/src/validator/mod.rs b/src/validator/mod.rs index 06ea57a..d444af8 100644 --- a/src/validator/mod.rs +++ b/src/validator/mod.rs @@ -49,7 +49,8 @@ fn validate_stat(prog: &mut Program, stat: &mut LocBox, 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 { - 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) -> 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) { 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()); diff --git a/src/validator/predefined.rs b/src/validator/predefined.rs index 9c7f8c2..10324fa 100644 --- a/src/validator/predefined.rs +++ b/src/validator/predefined.rs @@ -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]") diff --git a/test b/test index 8e7b7aab9613d2c6e567defdd230a859396e6e05..be02ec62c0b1392372cc6742f84f816c4a224179 100755 GIT binary patch delta 744 zcmeD1Ti`cAgK@z`&3w*Dj0|8fY2(68{0cEZwnyjj9X3FkpY^au^BVz=P8$`E&Jq<4 zpI*^(lZyo0)x7w@iadH*bHS`*FDCu}|KFpNMa82Vh?@^^0Hxd}-xrWbRs}0?Q4#Pk zJmAp_q&+(id33Y>?J*Cw|nK zT%h2g2nqtAPKHFNnJ}%gQ1PV62NlF6yU}E|p|U-bKLTZ8?t$qwfy%y^tf(k1`4vsC zFI4vHu+Xdl?o$B`h|-RPtq< zd_+Zha;J*yWC>N7$tFNLS4C#>QWd$$4}juyX^C$w7V`$ zdt%;S_R;E=Nb*A_W}imPgytN#svclj9?_$2HbX3Fly!G+`?*|>m=2AkXe6^vss`X$ z_VxJKz>p@Bt}L-=9W`%BN-F)sTiL|)Kj!#jmLx%|;`iRT@&lgt`hO#*5LR@2lG#+& zT*D|WhuDjku#8Y_CB{2c#v5aS-TVvD(yCz?bH-9aU5-T5Xe5B`&o+pw1RhnV-{AWS zOMu;0>()b7w+ekU}gXVRv@V`IhRqJa{)U<5%PIhE@E~yDt%*c=g*92i0PIhD!=QM}P_DoJ>6|dinrp5s(`vOhe7b^Z0 zO*|5Zcp6lkkr7oxF;rXvB3=)dZH7uDplO&46;DDFpAQu;L=#_)LwpBRycb11FshDB zPGl38JOP$wV&DMN2;%AFiEQHa5MMAcNFXG@EIF`YObn7};?_`cMKtkns5m5s5RxTO zaRY=9m^A?+uEk)ACb0&p!2(VE#NYna=|iSB8jBR_78-)Iw9E16AXK kCN2sUkHR6IiY9IXRo^&SkxP8?8AgtY4J;xchoEN>08as3`Tzg` delta 932 zcmZ{iu}i~16vpqOv59CEYKUS(t%6_`kt*6jkSK_&&_x_HXhFfo76sA4c2!i2Z*yuL z1lM-asY3^Kb9GU{!GAz-aWJ{OSa{Goa^Jn*_r1Ft@}iZr%My2-a;=lKFfn9zjX{== zs>P5`-l5EA_&U7uIbMW5=_tJF8cQdS_p`2c$<-Takvlax<;8aoc0KV%yD!Yz5|d+T zA6k~RVcGiwdzs8YHWTNLX@J@K)i)**oSkAakR+J0u1=#{0ZY~kUxA6bgzh)t=_cGl z&VpxQpN(86{22L~&v!7+*aaFEX}Ct7BmA}ruOdGq`5I$ja*fx)Y10cIA|&>cJ}{g6_l z@To-MTrsXx1M==R>Y}v!-6;m-RE_7sQT4(D9woWz{j9@S*9q^y`i<}y)<1-IVI3Av vW-K^gM?UED#J>?-JVF}Av0(w??K)ra){#w;nZ!(y@L?w(>G5u^U?7k`g*H|j