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 8e7b7aa..be02ec6 100755 Binary files a/test and b/test differ diff --git a/test.mcl b/test.mcl index ba74081..821c6d8 100644 --- a/test.mcl +++ b/test.mcl @@ -1,6 +1,6 @@ type str = [u8]; - +include "std.mcl"; struct Foo { a: usize, diff --git a/test.o b/test.o index ad674bd..8afc65a 100644 Binary files a/test.o and b/test.o differ