diff --git a/.gitignore b/.gitignore index 693d5d5..388364e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ /target -a \ No newline at end of file +/a \ No newline at end of file diff --git a/examples/rule110.mcl b/examples/rule110.mcl index ff3cfca..f5442ad 100755 --- a/examples/rule110.mcl +++ b/examples/rule110.mcl @@ -1,25 +1,30 @@ -mem 98 + 1 @8 -0 while dup 98 < do - 0 while dup 100 < do +macro puts 1 1 syscall3 drop end + +macro BOARD_SIZE 100 end + +mem BOARD_SIZE 2 - + 1 @8 + +0 while dup BOARD_SIZE 2 - < do + 0 while dup BOARD_SIZE < do dup mem + !8 if - dup mem + 100 + 42 @8 + dup mem + BOARD_SIZE + 42 @8 else - dup mem + 100 + 32 @8 + dup mem + BOARD_SIZE + 32 @8 end 1 + end - mem + 100 + 10 @8 + mem + BOARD_SIZE + 10 @8 - 101 mem 100 + 1 1 syscall3 drop + BOARD_SIZE 1 + mem BOARD_SIZE + puts // pattern mem !8 1 shl mem 1 + !8 bor - 1 while dup 98 < do + 1 while dup BOARD_SIZE 2 - < do swap 1 shl 7 band over mem + 1 + !8 bor 2dup 110 swap shr 1 band diff --git a/src/compile/linux_x86_64.rs b/src/compile/linux_x86_64.rs index ee81201..16f9ec0 100644 --- a/src/compile/linux_x86_64.rs +++ b/src/compile/linux_x86_64.rs @@ -312,6 +312,10 @@ pub fn compile(tokens: Vec, args: Args) -> Result<()>{ } ti += 1; }, + + OpType::Macro => { + panic!(); + } OpType::Syscall0 => { writeln!(writer, " ;; -- syscall0")?; writeln!(writer, " pop rax")?; @@ -383,6 +387,7 @@ pub fn compile(tokens: Vec, args: Args) -> Result<()>{ writeln!(writer, " push rax")?; ti += 1; }, + OpType::None => unreachable!() } } writeln!(writer, "addr_{}:", ti)?; diff --git a/src/constants.rs b/src/constants.rs index 815968a..25c1d01 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -38,6 +38,7 @@ pub enum OpType { End, While, Do, + Macro, // syscalls Syscall0, @@ -48,6 +49,7 @@ pub enum OpType { Syscall5, Syscall6, + None } @@ -72,8 +74,52 @@ impl Operator { pos: (file, row, col) } } + } +impl OpType { + pub fn human(&self) -> String { + match self { + &OpType::PushInt => "Number", + &OpType::PushStr => "String", + &OpType::Print => "print", + &OpType::Dup => "dup", + &OpType::Drop => "drop", + &OpType::Dup2 => "2dup", + &OpType::Rot => "rot", + &OpType::Over => "over", + &OpType::Swap => "swap", + &OpType::Plus => "+", + &OpType::Minus => "-", + &OpType::Equals => "=", + &OpType::Gt => ">", + &OpType::Lt => "<", + &OpType::Band => "band", + &OpType::Bor => "bor", + &OpType::Shr => "shr", + &OpType::Shl => "shl", + &OpType::Div => "/", + &OpType::Mul => "*", + &OpType::If => "if", + &OpType::Else => "else", + &OpType::End => "end", + &OpType::While => "while", + &OpType::Do => "do", + &OpType::Macro => "macro", + &OpType::Mem => "mem", + &OpType::Load8 => "!8", + &OpType::Store8 => "@8", + &OpType::Syscall0 => "syscall0", + &OpType::Syscall1 => "syscall1", + &OpType::Syscall2 => "syscall2", + &OpType::Syscall3 => "syscall3", + &OpType::Syscall4 => "syscall4", + &OpType::Syscall5 => "syscall5", + &OpType::Syscall6 => "syscall6", + &OpType::None => "None" + }.to_string() + } +} #[derive(Debug, Clone)] pub struct Token { @@ -92,4 +138,24 @@ pub enum TokenType { //TODO: Add char } +impl Token { + pub fn loc(&self) -> Loc { + ( + self.file.clone(), + self.line, + self.col + ) + } +} + +impl TokenType { + pub fn human(&self) -> String { + match self { + TokenType::Word => "Word", + TokenType::Int => "Int", + TokenType::String => "String", + }.to_string() + } +} + pub type Loc = (String, u32, u32); \ No newline at end of file diff --git a/src/interpret/linux_x86_64/mod.rs b/src/interpret/linux_x86_64/mod.rs index b73912c..58a17b5 100644 --- a/src/interpret/linux_x86_64/mod.rs +++ b/src/interpret/linux_x86_64/mod.rs @@ -223,7 +223,11 @@ pub fn run(tokens: Vec) -> Result<()>{ } else { ti += 1; } - } + } + OpType::Macro => { + panic!(); + } + OpType::Syscall0 => { todo!(); // ti += 1; @@ -265,6 +269,7 @@ pub fn run(tokens: Vec) -> Result<()>{ todo!(); // ti += 1; }, + OpType::None => unreachable!() } } diff --git a/src/lexer.rs b/src/lexer.rs index bdd2f97..33a6cd6 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -1,5 +1,5 @@ -use crate::constants::{Token, TokenType}; +use crate::{constants::{Token, TokenType}, preprocessor::preprocess}; use color_eyre::Result; fn lex_word(s: String, tok_type: TokenType) -> (TokenType, String) { @@ -65,7 +65,6 @@ fn lex_line(text: String) -> Result> { col = find_col(text.clone(), col_end, |x, _| !x.is_whitespace())?; } } - Ok(tokens) } @@ -113,5 +112,6 @@ pub fn lex(code: String, file: &String) -> Result> { // for token in tokens.clone() { // println!("tok: {:?}", token.text); // } + tokens = preprocess(tokens)?; Ok(tokens) } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index d91ecd6..1be3af6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ mod util; mod compile; mod parser; mod lexer; +mod preprocessor; use std::fs; diff --git a/src/parser.rs b/src/parser.rs index 2b6f2ce..11cb48d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -103,7 +103,7 @@ impl Parser { } -fn lookup_word>(s: String, pos: P) -> Result{ +pub fn lookup_word>(s: String, _pos: P) -> Result{ let lookup_table: HashMap<&str, OpType> = HashMap::from([ //stack ("print", OpType::Print), @@ -133,6 +133,7 @@ fn lookup_word>(s: String, pos: P) -> Resu ("end", OpType::End), ("while", OpType::While), ("do", OpType::Do), + ("macro", OpType::Macro), // mem ("mem", OpType::Mem), @@ -151,8 +152,7 @@ fn lookup_word>(s: String, pos: P) -> Resu match lookup_table.get(s.as_str()) { Some(v) => Ok(v.clone()), None => { - lerror!(pos, "Unknown word '{}'", s); - return Err(eyre!("Unknown word")) + Ok(OpType::None) } } } \ No newline at end of file diff --git a/src/preprocessor.rs b/src/preprocessor.rs new file mode 100644 index 0000000..79bbe98 --- /dev/null +++ b/src/preprocessor.rs @@ -0,0 +1,125 @@ +use std::collections::HashMap; + +use color_eyre::Result; +use eyre::eyre; + +use crate::constants::{Token, Loc, OpType, TokenType}; +use crate::{lerror, lnote}; +use crate::parser::lookup_word; + +#[derive(Debug)] +pub struct Macro { + pub loc: Loc, + pub tokens: Vec +} + +pub fn preprocess(tokens: Vec) -> Result>{ + let mut program: Vec = Vec::new(); + let mut macros: HashMap = HashMap::new(); + + let mut rtokens = tokens.clone(); + rtokens.reverse(); + while rtokens.len() > 0 { + let token = rtokens.pop().unwrap(); + + let op_type = lookup_word(token.text.clone(), &token.loc())?; + match token.clone() { + _ if op_type == OpType::Macro => { + if rtokens.len() == 0 { + lerror!(&token.loc(), "Macro name not found, expected {} but found nothing", TokenType::Word.human()); + return Err(eyre!("")); + } + let macro_name = rtokens.pop().unwrap(); + + if macro_name.typ != TokenType::Word { + lerror!(¯o_name.loc(), "Bad macro name, expected {} but found {}", TokenType::Word.human(), macro_name.typ.human()); + return Err(eyre!("")); + } + let word = lookup_word(macro_name.text.clone(), ¯o_name.loc())?; + if word != OpType::None { + lerror!(¯o_name.loc(), "Macro name cannot be a built in word, got '{}'", word.human()); + return Err(eyre!("")); + } + + + if macros.get(¯o_name.text.clone()).is_some() { //? Maybe allow? + lerror!(¯o_name.loc(), "Macro redefinition is not allowed"); + lnote!(¯os.get(¯o_name.text.clone()).unwrap().loc, "First definition here"); + return Err(eyre!("")); + } + + let mut macr = Macro{ loc: macro_name.loc(), tokens: Vec::new() }; + + let mut depth = 0; + while rtokens.len() > 0 { + let t = rtokens.pop().unwrap(); + let typ = lookup_word(t.text.clone(), &t.loc())?; + if typ == OpType::End && depth == 0 { + break; + } else if typ == OpType::End && depth != 0 { + depth -= 1; + macr.tokens.push(t); + } else { + if typ == OpType::If || typ == OpType::Do { + macr.tokens.push(t); + depth += 1; + } else { + macr.tokens.push(t); + } + } + } + + + macros.insert(macro_name.text, macr); + + + } + + _ => { + program.push(token); + } + } + } + + program = expand_macros(program, macros)?; + + Ok(program) +} + +pub fn expand_macros(tokens: Vec, macros: HashMap) -> Result> { + let mut program: Vec = Vec::new(); + + let mut rtokens = tokens.clone(); + rtokens.reverse(); + + while rtokens.len() > 0 { + let op = rtokens.pop().unwrap(); + let op_type = lookup_word(op.text.clone(), &op.loc())?; + if op.typ == TokenType::Word { + match op_type { + OpType::None => { + let m = macros.get(&op.text); + if m.is_some() { + program.append(&mut m.unwrap().tokens.clone()) + } else { + lerror!(&op.loc(), "Unknown word '{}'", op.text.clone()); + return Err(eyre!("")); + } + } + _ => { + program.push(op); + } + } + } else { + program.push(op); + } + + + } + + + + + + Ok(program) +} \ No newline at end of file diff --git a/test.mcl b/test.mcl index 47f58d5..facf167 100644 --- a/test.mcl +++ b/test.mcl @@ -1,6 +1,5 @@ -mem 0 @8 +macro write + 1 1 syscall3 drop +end -while mem !8 10 < do - "Hello world!\n" 1 1 syscall3 drop - mem !8 1 + mem swap @8 -end drop \ No newline at end of file +"HEnlo world!\n" write \ No newline at end of file