mod utils; mod precompiler; mod builtin; use std::{collections::HashMap, path::Path}; use anyhow::{bail, Result}; use crate::{cli::CliArgs, lexer::Lexer, types::{ast::{AstNode, Block, ConstUse, Constant, FnCall, Function, If, MemUse, Module, Program, While}, common::Loc, token::{InstructionType, KeywordType, Token, TokenType}}}; use self::{builtin::get_builtin_symbols, precompiler::precompile, utils::{expect, peek_check, peek_check_multiple, PeekResult}}; bitflags::bitflags! { struct Flags: u8 { const EXTERN = 1 << 0; const EXPORT = 1 << 1; const INLINE = 1 << 2; } } //TODO: Implement Module paths pub fn parse(cli_args: &CliArgs, tokens: &mut Vec) -> Result { tokens.reverse(); let module = Module { loc: Loc::new(&tokens[0].loc.file, 0, 0), ident: Path::new(&tokens[0].loc.file).file_stem().expect("Something went horribly wrong").to_string_lossy().to_string(), body: Vec::new(), path: vec![], }; let mut prog = Program { ast: AstNode::Module(module.clone()), functions: HashMap::new(), constants: HashMap::new(), memories: HashMap::new(), }; let syms = get_builtin_symbols(&mut prog); match &mut prog.ast { AstNode::Module(module) => { module.body.push(syms) } _ => unreachable!() } while !tokens.is_empty() { let node = parse_next(cli_args, &mut prog, tokens, Flags::empty(), true)?; match &mut prog.ast { AstNode::Module(module) => { module.body.push(node); } _ => unreachable!() } } // prog.ast = module; Ok(prog) } fn parse_next(cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec, flags: Flags, is_module_root: bool) -> Result { let token = tokens.pop().expect("We broke reality!"); // debug!({loc => token.loc.clone()}, "t: {:?}", token.typ); let ret = match &token.typ { TokenType::Keyword(kw) => { match kw { KeywordType::If => parse_if(&token, cli_args, prog, tokens)?, KeywordType::While => parse_while(&token, cli_args, prog, tokens)?, KeywordType::Include => parse_include(&token, cli_args, prog, tokens)?, //TODO: implement include KeywordType::Memory => todo!(), KeywordType::Constant => parse_const(&token, cli_args, prog, tokens)?, KeywordType::Function => parse_function(&token, cli_args, prog, tokens, flags)?, KeywordType::Struct => todo!(), KeywordType::Inline => parse_inline(&token, cli_args, prog, tokens, flags)?, KeywordType::Export => parse_export(&token, cli_args, prog, tokens, flags)?, KeywordType::Extern => parse_extern(&token, cli_args, prog, tokens, flags)?, kw => { dbg!(&prog.constants); error!({loc => token.loc}, "Unexpected token {kw:?}"); bail!("") } } }, TokenType::Instruction(it) => { if is_module_root { error!({loc => token.loc}, "Unexpected token {it:?}, please create a main function, this is not a scripting language"); bail!("") } else { AstNode::Token(token) } }, TokenType::Unknown(ut) => { if is_module_root { error!({loc => token.loc}, "Unexpected token {ut:?}, please create a main function, this is not a scripting language"); bail!("") } else { // AstNode::Token(token) parse_unknown(&token, cli_args, prog, tokens, flags)? } }, }; Ok(ret) } // TODO: Extern functions fn parse_function(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec, flags: Flags ) -> Result { let name = expect(tokens, TokenType::Unknown(String::new()))?; expect(tokens, TokenType::Keyword(KeywordType::With))?; let mut args = Vec::new(); loop { if let PeekResult::Correct(t) = peek_check_multiple(tokens, vec![ TokenType::Instruction(InstructionType::TypeAny), TokenType::Instruction(InstructionType::TypeBool), TokenType::Instruction(InstructionType::TypeInt), TokenType::Instruction(InstructionType::TypePtr), TokenType::Instruction(InstructionType::TypeVoid), ]) { args.push(t.typ.clone()); } else { break; } tokens.pop(); } expect(tokens, TokenType::Keyword(KeywordType::Returns))?; let mut ret_args = Vec::new(); loop { if let PeekResult::Correct(t) = peek_check_multiple(tokens, vec![ TokenType::Instruction(InstructionType::TypeAny), TokenType::Instruction(InstructionType::TypeBool), TokenType::Instruction(InstructionType::TypeInt), TokenType::Instruction(InstructionType::TypePtr), TokenType::Instruction(InstructionType::TypeVoid), ]) { ret_args.push(t.typ.clone()); } else { break; } tokens.pop(); } expect(tokens, TokenType::Keyword(KeywordType::Then))?; let mut body = Vec::new(); loop { let fn_got = peek_check(tokens, TokenType::Keyword(KeywordType::Done)); match fn_got { PeekResult::Correct(_) => break, PeekResult::Wrong(_) => (), PeekResult::None => panic!("idk what to do herre"), } body.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?); } expect(tokens, TokenType::Keyword(KeywordType::Done))?; let fn_def = Function { loc: org.loc(), inline: flags.contains(Flags::INLINE), extrn: flags.contains(Flags::EXTERN), export: flags.contains(Flags::EXPORT), ident: name.lexem.clone(), arg_types: args, ret_types: ret_args, body, }; //TODO: Support module paths without double definitions // let mut mp = match &prog.ast { // AstNode::Module(m) => { // m.path.clone() // } // _ => panic!("") // }; // mp.push(name.lexem.clone()); // let mp = mp.join("::"); // prog.function_aliases.insert(mp, name.lexem.clone()); prog.functions.insert(name.lexem.clone(), fn_def.clone()); Ok(AstNode::Function(fn_def)) } fn parse_if(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec) -> Result { let mut test: Vec = Vec::new(); let mut body: Vec = Vec::new(); let mut els: Vec = Vec::new(); loop { test.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?); match peek_check(tokens, TokenType::Keyword(KeywordType::Do)) { PeekResult::Correct(_) => break, PeekResult::Wrong(w) => { match w.typ { TokenType::Keyword(KeywordType::Then) => { warn!("If is defined as `if ... do ... done`"); } _ => () } }, PeekResult::None => panic!("idk what to do herre"), } } expect(tokens, TokenType::Keyword(KeywordType::Do))?; loop { body.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?); match peek_check_multiple(tokens, vec![ TokenType::Keyword(KeywordType::Else), TokenType::Keyword(KeywordType::Done), ]) { PeekResult::Correct(_) => break, PeekResult::Wrong(_) => (), PeekResult::None => panic!("idk what to do herre"), } } let els_t = tokens.last().expect("IMPOSSIBLEEE!!!!!!111").clone(); let els = match els_t.typ.clone() { TokenType::Keyword(kw) => { match kw { KeywordType::Done => { expect(tokens, TokenType::Keyword(KeywordType::Done))?; AstNode::Block(Block{ comment: String::new(), loc: els_t.loc, body: Vec::new(), }) }, KeywordType::Else => { expect(tokens, TokenType::Keyword(KeywordType::Else))?; if peek_check(tokens, TokenType::Keyword(KeywordType::If)).correct() { let if_org =expect(tokens, TokenType::Keyword(KeywordType::If))?; parse_if(&if_org, cli_args, prog, tokens)? } else { loop { els.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?); match peek_check(tokens, TokenType::Keyword(KeywordType::Done)) { PeekResult::Correct(_) => break, PeekResult::Wrong(w) => { match w.typ { TokenType::Keyword(KeywordType::Then) => { warn!("If is defined as `if ... do ... done`"); } _ => () } }, PeekResult::None => panic!("idk what to do herre"), } } expect(tokens, TokenType::Keyword(KeywordType::Done))?; AstNode::Block(Block{ comment: String::new(), loc: els_t.loc, body: els, }) } }, e => { error!({loc => els_t.loc.clone()}, "Expected {:?} or {:?} but got {:?}", KeywordType::Done, KeywordType::Else, e); bail!(""); } } }, e => { error!({loc => els_t.loc.clone()}, "Expected {:?} or {:?} but got {:?}", KeywordType::Done, KeywordType::Else, e); bail!(""); } }; Ok(AstNode::If(If{ test, body, els: Box::new(els), loc: org.loc(), })) } fn parse_while(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec) -> Result { let mut test: Vec = Vec::new(); let mut body: Vec = Vec::new(); loop { test.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?); match peek_check(tokens, TokenType::Keyword(KeywordType::Do)) { PeekResult::Correct(_) => break, PeekResult::Wrong(w) => { match w.typ { TokenType::Keyword(KeywordType::Then) => { warn!("while is defined as `while ... do ... done`"); } _ => () } }, PeekResult::None => panic!("idk what to do herre"), } } expect(tokens, TokenType::Keyword(KeywordType::Do))?; loop { body.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?); match peek_check_multiple(tokens, vec![ TokenType::Keyword(KeywordType::Else), TokenType::Keyword(KeywordType::Done), ]) { PeekResult::Correct(_) => break, PeekResult::Wrong(_) => (), PeekResult::None => panic!("idk what to do herre"), } } expect(tokens, TokenType::Keyword(KeywordType::Done))?; Ok(AstNode::While(While{ test, body, loc: org.loc(), })) } fn parse_inline(_: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec, flags: Flags) -> Result { let allowed_tokens = vec!{ TokenType::Keyword(KeywordType::Function) }; let Some(t) = tokens.last() else { error!("Expected one of {:?} after {:?} but found nothing", allowed_tokens, TokenType::Keyword(KeywordType::Inline)); bail!("") }; let mut found = false; for at in &allowed_tokens { if utils::cmp(at, &t.typ) { found = true; } } if !found { error!({loc => t.loc.clone()}, "Expected one of {:?} after {:?} but found {:?}", allowed_tokens, TokenType::Keyword(KeywordType::Inline), t.typ); bail!(""); } parse_next(cli_args, prog, tokens, flags | Flags::INLINE, false) } fn parse_extern(_: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec, flags: Flags) -> Result { let allowed_tokens = vec!{ TokenType::Keyword(KeywordType::Function), TokenType::Keyword(KeywordType::Constant), TokenType::Keyword(KeywordType::Memory), }; let Some(t) = tokens.last() else { error!("Expected one of {:?} after {:?} but found nothing", allowed_tokens, TokenType::Keyword(KeywordType::Extern)); bail!("") }; let mut found = false; for at in &allowed_tokens { if utils::cmp(at, &t.typ) { found = true; } } if !found { error!({loc => t.loc.clone()}, "Expected one of {:?} after {:?} but found {:?}", allowed_tokens, TokenType::Keyword(KeywordType::Extern), t.typ); bail!(""); } parse_next(cli_args, prog, tokens, flags | Flags::EXTERN, false) } fn parse_export(_: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec, flags: Flags) -> Result { let allowed_tokens = vec!{ TokenType::Keyword(KeywordType::Function), TokenType::Keyword(KeywordType::Constant), TokenType::Keyword(KeywordType::Memory), }; let Some(t) = tokens.last() else { error!("Expected one of {:?} after {:?} but found nothing", allowed_tokens, TokenType::Keyword(KeywordType::Export)); bail!("") }; let mut found = false; for at in &allowed_tokens { if utils::cmp(at, &t.typ) { found = true; } } if !found { error!({loc => t.loc.clone()}, "Expected one of {:?} after {:?} but found {:?}", allowed_tokens, TokenType::Keyword(KeywordType::Export), t.typ); bail!(""); } parse_next(cli_args, prog, tokens, flags | Flags::EXPORT, false) } fn parse_include(_: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec) -> Result { let path = expect(tokens, TokenType::Instruction( InstructionType::PushStr( String::new() ) ) )?; for ip in &cli_args.include_path { let p = ip.join(&path.lexem).to_path_buf(); if p.exists() { info!({loc => path.loc.clone()}, "Lexing file {}", path.lexem.clone()); let mut lexer = Lexer::new(); lexer.lex(p.as_std_path())?; let mut mod_tokens = lexer.tokens; mod_tokens.reverse(); let mut mp = match &prog.ast { AstNode::Module(m) => { m.path.clone() } _ => panic!("") }; mp.push(p.file_stem().unwrap().to_string()); let module = Module { loc: Loc::new(path.loc.file.clone(), 0, 0), ident: Path::new(&path.loc.file).file_stem().expect("Something went horribly wrong").to_string_lossy().to_string(), body: Vec::new(), path: mp, }; let mut mod_prog = Program { ast: AstNode::Module(module), functions: prog.functions.clone(), constants: prog.constants.clone(), memories: prog.memories.clone(), }; info!({loc => path.loc.clone()}, "Parsing file {}", path.lexem.clone()); while !mod_tokens.is_empty() { let node = parse_next(cli_args, &mut mod_prog, &mut mod_tokens, Flags::empty(), true)?; match &mut mod_prog.ast { AstNode::Module(module) => { module.body.push(node); } _ => unreachable!() } } prog.constants = mod_prog.constants; prog.functions = mod_prog.functions; prog.memories = mod_prog.memories; return Ok(mod_prog.ast) } }; error!("Could not find file {:?} in these locations: {:?}", path.lexem, cli_args.include_path); bail!("") } fn parse_const(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec) -> Result { let name = expect(tokens, TokenType::Unknown(String::new()))?; let mut body = Vec::new(); loop { let t = peek_check(tokens, TokenType::Keyword(KeywordType::End)); match t { PeekResult::Correct(_) => break, PeekResult::Wrong(_) => (), PeekResult::None => panic!("idk what to do herre"), } body.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?); } expect(tokens, TokenType::Keyword(KeywordType::End))?; let val = precompile(prog, body, &mut Vec::new())?; let name = name.lexem.clone() .replace("(", "_OPRN_") .replace(")", "_CPRN_"); let def = Constant{ loc: org.loc(), ident: name.clone(), value: Box::new(val), }; prog.constants.insert(name, def.clone()); Ok(AstNode::Constant(def)) } fn parse_unknown(org: &Token, _: &CliArgs, prog: &mut Program, _: &mut Vec, _: Flags ) -> Result { //TODO: Typing? if let Some(func) = prog.functions.get(&org.lexem) { if func.inline { return Ok(AstNode::Block(Block{ loc: org.loc.clone(), body: func.body.clone(), comment: format!("inline fn {}", func.ident) })) } else { return Ok(AstNode::FnCall(FnCall{ loc: org.loc.clone(), ident: org.lexem.clone() })); } } if let Some(_) = prog.constants.get(&org.lexem) { return Ok(AstNode::ConstUse(ConstUse{ loc: org.loc.clone(), ident: org.lexem.clone() })); } if let Some(_) = prog.memories.get(&org.lexem) { return Ok(AstNode::MemUse(MemUse{ loc: org.loc.clone(), ident: org.lexem.clone() })); } dbg!(&prog.constants); error!({loc => org.loc.clone()}, "Unknown token {:?}", org); bail!("") }