commit 1f6da723640693cbe9b549a6340c23caa3a5d2e2 Author: Lucia Ceionia Date: Fri May 12 16:54:29 2023 -0500 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2bc1730 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "lang-lucia" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..ef38820 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "lang-lucia" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a1c04bf --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +objects = test.o extra.o + +all: $(objects) + gcc -g -m32 -no-pie -o a.out $^ + +.PRECIOUS: %.S + +%.S: %.src + cat $< | ./target/debug/lang-lucia -q > $@ + +%.o: %.S + nasm -Ox -g -F dwarf -felf -o $@ $< + +%.o: %.c + gcc -m32 -g -c -o $@ $< + +clean: + rm -f test.o test.S + +compiler: + cargo build diff --git a/a.out b/a.out new file mode 100755 index 0000000..6b19653 Binary files /dev/null and b/a.out differ diff --git a/ack.src b/ack.src new file mode 100644 index 0000000..5deb513 --- /dev/null +++ b/ack.src @@ -0,0 +1,12 @@ +fn ack(m i32 n i32) + endvar + if m then + if n then + m 1- : m n 1- call ack call ack ret; + else + m 1- 1 call ack ret; + end + else + n 1+ ret + end +endfn diff --git a/ex.src b/ex.src new file mode 100644 index 0000000..d0b79f5 --- /dev/null +++ b/ex.src @@ -0,0 +1,43 @@ +extern printfd; +extern puts; + +fn ack(m i32 n i32) endvar + if m 0 eq then + n 1+ ret; + elif n 0 eq then + (m 1- 1)ack ret; + else + (m 1- (m n 1-)ack)ack ret; + end +endfn + +fn foo(ptr pi32) endvar + ptr* ptr* 1+:= +endfn + +fn main(argc i32 argv ppi8) + x y u32; + ptr ppi8; + endvar + + 0 y=:; + loop + y 4 eq breakif; + 0 x=:; + loop + x 4 eq breakif; + ((y x)ack)printfd; + (x&)foo + end + y 1 + y=:; + end + + x argc :=; + ptr argv :=; + loop + x 0 eq ptr* 0 eq or breakif; + (ptr*)puts; + x 1 - x=:; + ptr 4+ ptr=:; + end +endfn diff --git a/extra.c b/extra.c new file mode 100644 index 0000000..e95185e --- /dev/null +++ b/extra.c @@ -0,0 +1,3 @@ +void printfd(int v) { + printf("%d\n", v); +} diff --git a/extra.o b/extra.o new file mode 100644 index 0000000..e17c7af Binary files /dev/null and b/extra.o differ diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..fa17732 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,1464 @@ +#![feature(if_let_guard)] +#![feature(iter_intersperse)] +use std::{io::{self, Read, Cursor, Seek}, collections::HashMap, fmt::{Display, Debug}, str::FromStr, env, error::Error}; + +#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] +enum Width { + Dword = 4, Word = 2, Byte = 1 +} + +#[derive(Clone, Copy, PartialEq)] +enum Register { + EAX, AX, /* AH, */ AL, + EBX, BX, /* BH, */ BL, + ECX, CX, /* CH, */ CL, + EDX, DX, /* DH, */ DL, +} + +impl Display for Register { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", match self { + Self::EAX => "eax", Self::EBX => "ebx", Self::ECX => "ecx", Self::EDX => "edx", + Self::AX => "ax", Self::BX => "bx", Self::CX => "cx", Self::DX => "dx", + //Self::AH => "ah", Self::BH => "bh", Self::CH => "ch", Self::DH => "dh", + Self::AL => "al", Self::BL => "bl", Self::CL => "cl", Self::DL => "dl", + }) + } +} + +impl Register { + #[allow(unused)] + fn width(&self) -> Width { + match self { + Self::EAX | Self::EBX | Self::ECX | Self::EDX => + Width::Dword, + Self::AX | Self::BX | Self::CX | Self::DX => + Width::Word, + //Self::AH | Self::BH | Self::CH | Self::DH | + Self::AL | Self::BL | Self::CL | Self::DL => + Width::Byte, + } + } + + fn sized(&self, width: Width) -> Register { + match (self, width) { + (Self::EAX | Self::AX | Self::AL, Width::Dword) => Self::EAX, + (Self::EBX | Self::BX | Self::BL, Width::Dword) => Self::EBX, + (Self::ECX | Self::CX | Self::CL, Width::Dword) => Self::ECX, + (Self::EDX | Self::DX | Self::DL, Width::Dword) => Self::EDX, + (Self::EAX | Self::AX | Self::AL, Width::Word) => Self::AX, + (Self::EBX | Self::BX | Self::BL, Width::Word) => Self::BX, + (Self::ECX | Self::CX | Self::CL, Width::Word) => Self::CX, + (Self::EDX | Self::DX | Self::DL, Width::Word) => Self::DX, + (Self::EAX | Self::AX | Self::AL, Width::Byte) => Self::AL, + (Self::EBX | Self::BX | Self::BL, Width::Byte) => Self::BL, + (Self::ECX | Self::CX | Self::CL, Width::Byte) => Self::CL, + (Self::EDX | Self::DX | Self::DL, Width::Byte) => Self::DL, + } + } +} + +#[derive(Clone, Copy, Default)] +enum Location { + #[default] None, + Imm(bool, u32), + Local(i32), + #[allow(unused)] + Label(u32), + Reg(Register), +} + +#[derive(Clone, Copy, Default, PartialEq)] +struct ExReg { + eax: bool, + ebx: bool, + ecx: bool, + edx: bool, + esi: bool, + edi: bool, +} + +impl From for ExReg { + fn from(reg: Register) -> Self { + match reg.sized(Width::Dword) { + Register::EAX => Self { eax: true, ebx: false, ecx: false, edx: false, esi: false, edi: false }, + Register::EBX => Self { eax: false, ebx: true, ecx: false, edx: false, esi: false, edi: false }, + Register::ECX => Self { eax: false, ebx: false, ecx: true, edx: false, esi: false, edi: false }, + Register::EDX => Self { eax: false, ebx: false, ecx: false, edx: true, esi: false, edi: false }, + // Register::ESI => Self { eax: false, ebx: false, ecx: false, edx: false, esi: true, edi: false }, + // Register::EDI => Self { eax: false, ebx: false, ecx: false, edx: false, esi: false, edi: true }, + _ => Self::default() + } + } +} + +impl Into for Location { + fn into(self) -> ExReg { + match self { + Location::Reg(r) => ExReg::from(r), + _ => ExReg::default() + } + } +} + +#[derive(Clone, Copy, PartialEq, Default)] +enum DataType { + #[default] + U32, U16, U8, + I32, I16, I8, +} + +#[derive(Clone, Copy, Default, PartialEq)] +enum Type { + #[default]None, + Value(DataType), + P(u32, bool, DataType) +} + +impl DataType { + fn width(&self) -> Width { + match self { + Self::U32 | Self::I32 => Width::Dword, + Self::U16 | Self::I16 => Width::Word, + Self::U8 | Self::I8 => Width::Byte, + } + } + fn is_signed(&self) -> bool { + match self { + Self::U32 | Self::U16 | Self::U8 => false, + Self::I32 | Self::I16 | Self::I8 => true, + } + } +} +impl Debug for DataType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::U32 => write!(f, "U32"), + Self::U16 => write!(f, "U16"), + Self::U8 => write!(f, "U8"), + Self::I32 => write!(f, "I32"), + Self::I16 => write!(f, "I16"), + Self::I8 => write!(f, "I8"), + } + } +} + +impl FromStr for Type { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "u32" | "uint" => Ok(Self::Value(DataType::U32)), + "i32" | "int" => Ok(Self::Value(DataType::I32)), + "u16" => Ok(Self::Value(DataType::U16)), + "u8" => Ok(Self::Value(DataType::U8)), + "i16" => Ok(Self::Value(DataType::I16)), + "i8" => Ok(Self::Value(DataType::I8)), + _ if s.starts_with('p') => match Self::from_str(&s[1..]) { + Ok(Self::Value(v)) => Ok(Self::P(1, false, v)), + Ok(Self::P(n, _, v)) => Ok(Self::P(n + 1, false, v)), + e => e + }, + _ => Err(format!("Invalid type '{}'", s)) + } + } +} + +impl Type { + fn width(&self) -> Width { + match self { + Self::None => Width::Dword, + Self::Value(d) => d.width(), + Self::P(..) => Width::Dword, + } + } + fn val_width(&self) -> Width { + match self { + Self::None => Width::Dword, + Self::Value(d) => d.width(), + Self::P(0,_,d) => d.width(), + Self::P(..) => Width::Dword + } + } + fn is_signed(&self) -> bool { + match self { + Self::None => false, + Self::Value(d) => d.is_signed(), + Self::P(..) => false, + } + } + fn val_is_signed(&self) -> bool { + match self { + Self::None => false, + Self::Value(d) => d.is_signed(), + Self::P(0,_,d) => d.is_signed(), + Self::P(..) => false, + } + } +} + +impl Debug for Type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::None => write!(f, "N"), + Self::Value(v) => write!(f, "{:?}", v), + Self::P(n, b, t) => { + write!(f, "P{}", n)?; + write!(f, ",{}", if *b { "T" } else { "F" })?; + write!(f, ",{:?}", t) + } + } + } +} + +#[derive(Clone, Copy, Default)] +struct TypedLoc { + ty: Type, + loc: Location +} + +impl<'a> Display for TypedLoc { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match (self.ty, self.loc) { + (Type::P(..) | Type::Value(DataType::U32) | Type::Value(DataType::I32), Location::Local(i)) | + (Type::Value(DataType::U16) | Type::Value(DataType::I16), Location::Local(i)) | + (Type::Value(DataType::U8) | Type::Value(DataType::I8), Location::Local(i)) => + if i < 0 { write!(f, "ebp - {}", -i) } else { write!(f, "ebp + {}", i) }, + (_, Location::Label(_)) => panic!(), + // Signed + (_, Location::Imm(true, i)) => write!(f, "{}", i as i32), + // Unsigned + (_, Location::Imm(false, i)) => write!(f, "{}", i as u32), + (_, Location::Reg(r)) => write!(f, "{}", r), + (_, Location::None) => panic!(), + (Type::None, _) => panic!() + } + } +} + +#[derive(Copy, Clone)] +enum Token { + #[allow(unused)] + None, + // Only used in definitions + Identifier, + Expression(TypedLoc), + CastOperator, + CallOpen, + DidRet, + BlackHole +} + +impl Debug for Token { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::None => write!(f, "NT"), + Self::Identifier => write!(f, "ID"), + Self::CallOpen => write!(f, "CL"), + Self::CastOperator => write!(f, "CO"), + Self::BlackHole => write!(f, "BH"), + Self::DidRet => write!(f, "RET"), + Self::Expression(TypedLoc { ty: t, loc: Location::None }) => write!(f, "EN({:?})", t), + Self::Expression(TypedLoc { ty: t, loc: Location::Imm(_,i) }) => write!(f, "EI({:?},{})", t, i), + Self::Expression(TypedLoc { ty: t, loc: Location::Reg(r) }) => write!(f, "ER({:?},{})", t, r), + Self::Expression(TypedLoc { ty: t, loc: Location::Local(l) }) => write!(f, "EL({:?},{})", t, l), + Self::Expression(TypedLoc { ty: t, loc: Location::Label(s) }) => write!(f, "EL({:?},{})", t, s), + } + } +} + +#[derive(Clone, Copy)] +enum Control { + None, + Loop, + If, + Elif(u32), + Else, +} + +#[derive(Default, Clone, Copy)] +enum Scope { + #[default] Global, + FnDef, + ExternFnDef, + FnArgSetup, + FnStackSetup, + Code(Control), + FnCall, +} + +#[derive(Default)] +struct State { + used_eax: Type, + used_ebx: Type, + used_ecx: Type, + used_edx: Type, + + scope: Scope, + token_stack: Vec, + + controls_used: u32, + control_stack: Vec<(Control, u32)>, + + args_count: i32, + used_stack: u32, + extra_stack: u32, + call_stack: u32, + locals: HashMap, + + strings: Vec, + + do_debug: bool, +} + +impl State { + fn dbg_token_stack(&self) -> String { + let str: String = self.token_stack.iter().map(|t| format!("{:?}",t).chars().collect::>()).intersperse(", ".chars().collect()).flatten().collect(); + format!("Tokens: {}", str) + } + + fn dbg_print_locals(&self) { + let mut sorted_locals: Vec<(&String, &(Type, i32))> = self.locals.iter().collect(); + sorted_locals.sort_by(|(_, (_, a_i)), (_,(_, b_i))| a_i.cmp(b_i)); + let str: String = sorted_locals.iter().map(|(k, v)| format!("{}:{:?}@{}", k, v.0, v.1).chars().collect::>()).intersperse(", ".chars().collect()).flatten().collect(); + println!("; Locals: {}", str); + } + + + fn clear_reg(&mut self, reg: Register) { + let reg = reg.sized(Width::Dword); + match reg { + Register::EAX => self.used_eax = Type::None, + Register::EBX => self.used_ebx = Type::None, + Register::ECX => self.used_ecx = Type::None, + Register::EDX => self.used_edx = Type::None, + _ => panic!() + } + } + + fn use_reg(&mut self, reg: Register, t: &Type) { + let reg = reg.sized(Width::Dword); + match reg { + Register::EAX => self.used_eax = t.clone(), + Register::EBX => self.used_ebx = t.clone(), + Register::ECX => self.used_ecx = t.clone(), + Register::EDX => self.used_edx = t.clone(), + _ => panic!() + } + } + + // Removes a token from the top of the stack, clearing its register if used + fn pop_token(&mut self) { + if let Some(Token::Expression(TypedLoc { loc: Location::Reg(r), .. })) = self.token_stack.pop() { + self.clear_reg(r); + } + } + + // Pushes a token to the stack, setting its register if used + fn push_token(&mut self, token: Token) { + if let Token::Expression(TypedLoc { loc: Location::Reg(r), ty }) = token { + self.use_reg(r, &ty); + } + self.token_stack.push(token); + } + + fn reset_regs(&mut self) { + self.used_eax = Type::None; + self.used_ebx = Type::None; + self.used_ecx = Type::None; + self.used_edx = Type::None; + } + + // FIXME: This SUUUUUUUUUCKS + fn free_reg(&mut self, reg: Register) { + // get reg max size + let reg = reg.sized(Width::Dword); + + let t = match reg { + Register::EAX => &mut self.used_eax, + Register::EBX => &mut self.used_ebx, + Register::ECX => &mut self.used_ecx, + Register::EDX => &mut self.used_edx, + _ => panic!() + }; + + // Already free + if *t == Type::None { + return + } + + println!("push {}", reg.to_string()); + if let Some(token) = self.token_stack.iter_mut().rev().rfind(|t| match t { + Token::Expression(TypedLoc { loc: Location::Reg(test_r), .. }) => *test_r == reg, + _ => false + }) { + + self.extra_stack += 4; + *token = Token::Expression(TypedLoc { + ty: *t, + loc: Location::Local(-(self.used_stack as i32 + self.extra_stack as i32)) + }); + } + + *t = Type::None; + } + + fn get_free_reg_exclude(&mut self, ex_reg: ExReg) -> Register { + if self.used_ebx == Type::None && !ex_reg.ebx { return Register::EBX } + else if self.used_eax == Type::None && !ex_reg.eax { return Register::EAX } + else if self.used_ecx == Type::None && !ex_reg.ecx { return Register::ECX } + else if self.used_edx == Type::None && !ex_reg.edx { return Register::EDX } + else if !ex_reg.ebx { self.free_reg(Register::EBX); return Register::EBX; } + else if !ex_reg.eax { self.free_reg(Register::EAX); return Register::EAX; } + else if !ex_reg.ecx { self.free_reg(Register::ECX); return Register::ECX; } + else if !ex_reg.edx { self.free_reg(Register::EDX); return Register::EDX; } + + // no regs can be used + panic!("No registers can be used, check syntax?"); + } + + fn get_val_in_reg(&mut self, e0: &TypedLoc, reg: Register) -> Register { + // Get e0 in register + let reg0 = match e0 { + // References & locals get moved to reg + TypedLoc { ty: Type::P(_, true, _), .. } | + TypedLoc { ty: _, loc: Location::Local(_) } => { + let r = reg.sized(e0.ty.width()); + println!("mov {}, [{}]", r, e0); + r + }, + // Don't need to move something already in the reg + TypedLoc { loc: Location::Reg(r), .. } if *r == reg => *r, + TypedLoc { loc: Location::None, .. } => panic!(), + _ => { + let r = reg.sized(e0.ty.width()); + println!("mov {}, {}", r, e0); + r + } + }; + reg0 + } + + fn get_val_in_free_reg(&mut self, e0: &TypedLoc, ex_reg: ExReg) -> Register { + // Get e0 in register + let reg0 = match e0 { + // References & locals get moved to reg + TypedLoc { ty: Type::P(_, true, _), .. } | + TypedLoc { ty: _, loc: Location::Local(_) } => { + let r = if let TypedLoc { loc: Location::Reg(r), .. } = e0 { r.sized(e0.ty.val_width()) } else { + self.get_free_reg_exclude(ex_reg).sized(e0.ty.val_width()) + }; + println!("mov {}, [{}]", r, e0); + r + }, + // Don't need to move something already in reg, if it's not a reference + TypedLoc { loc: Location::Reg(r), .. } => *r, + TypedLoc { loc: Location::None, .. } => panic!(), + _ => { + let r = self.get_free_reg_exclude(ex_reg).sized(e0.ty.width()); + println!("mov {}, {}", r, e0); + r + } + }; + reg0 + } + + fn get_val_in_free_reg_szex(&mut self, e0: &TypedLoc, ex_reg: ExReg, new_type: Type) -> Register { + // Get e0 in register + let (reg0, str0) = match e0 { + // References & locals get moved to reg + TypedLoc { ty: Type::P(_, true, _), .. } | + TypedLoc { ty: _, loc: Location::Local(_) } => { + let r = if let TypedLoc { loc: Location::Reg(r), .. } = e0 { r.sized(new_type.width()) } else { + self.get_free_reg_exclude(ex_reg).sized(new_type.width()) + }; + let str0_size = match new_type.width().min(e0.ty.val_width()) { + Width::Dword => "dword", + Width::Word => "word", + Width::Byte => "byte", + }; + (r, format!("{} [{}]", str0_size, e0)) + }, + // Don't need to move something already in reg, if it's not a reference + TypedLoc { loc: Location::Reg(r), .. } => (r.sized(new_type.width()), r.to_string()), + TypedLoc { loc: Location::None, .. } => panic!(), + _ => { + let r = self.get_free_reg_exclude(ex_reg).sized(new_type.width()); + (r, e0.to_string()) + } + }; + + let op = match new_type.width().partial_cmp(&e0.ty.val_width()) { + // new width greater than old width + Some(std::cmp::Ordering::Greater) => { + if new_type.is_signed() { + // Sign extend + "movsx" + } else { + // Zero extend + "movzx" + } + }, + // new width less or equal to old width, nothing special + _ => { + "mov" + } + }; + println!("{} {}, {}", op, reg0, str0); + reg0 + } + + fn get_location_to_string(&mut self, e0: &TypedLoc, ex_reg: ExReg, allow_imm: bool, allow_mem: bool) -> String { + // Get e0 in location + match e0 { + TypedLoc { ty: t@Type::P(_, true, _), .. } if allow_mem => format!("{} [{}]", match t.val_width() { + Width::Dword => "dword", + Width::Word => "word", + Width::Byte => "byte", + }, e0), + TypedLoc { loc: Location::Imm(..), .. } if allow_imm => e0.to_string(), + + e0 => self.get_val_in_free_reg(e0, ex_reg).to_string() + } + } + + // P(0,T,t)@l e op -> mov r,[l] op r,e -> t@r + // t@l e op -> op r,e -> t@r + // P(n,T,t)@l e op -> mov r,[l] op r,e -> P(n,F,t)@r + // P(n,F,t)@l e op -> op r,e -> P(n,F,t)@r + fn binary_operator(&mut self, op: &str, e0: &TypedLoc, e1: &TypedLoc, allow_imm: bool, allow_mem: bool) -> Result { + // Make sure sizes work + if e0.ty.val_width() != e1.ty.val_width() && !( + if let Location::Imm(..) = e0.loc { true } else { false } || + if let Location::Imm(..) = e1.loc { true } else { false }) { + return Err("Type mismatch".to_string()); + } + + // Get e0 in register + let reg0 = self.get_val_in_free_reg(&e0, e1.loc.into()); + + // Get new type + let new_e0 = match e0.ty { + Type::P(0, _, t) | Type::Value(t) => { TypedLoc { ty: Type::Value(t), loc: Location::Reg(reg0) } }, + // Pointers push a version of themselves made not a reference + Type::P(n, _, t) => { TypedLoc { ty: Type::P(n, false, t), loc: Location::Reg(reg0) } }, + _ => panic!() + }; + + // Get e1 in location + let str1 = self.get_location_to_string(&e1, reg0.into(), true, true); + + // Pop old tokens, freeing registers + self.pop_token(); + self.pop_token(); + + // Do the operation + println!("{} {}, {}", op, reg0, str1); + + // Return new token + return Ok(new_e0); + } + + fn binary_operator_with_reg(&mut self, op: &str, e0: &TypedLoc, e1: &TypedLoc, e0_in: Register, allow_imm: bool, allow_mem: bool) -> Result { + // Make sure sizes work + if e0.ty.val_width() != e1.ty.val_width() && !( + if let Location::Imm(..) = e0.loc { true } else { false } || + if let Location::Imm(..) = e1.loc { true } else { false }) { + return Err("Type mismatch".to_string()); + } + + // Get e0 in register + let reg0 = self.get_val_in_reg(&e0, e0_in); + + // Get new type + let new_e0 = match e0.ty { + Type::P(0, _, t) | Type::Value(t) => { TypedLoc { ty: Type::Value(t), loc: Location::Reg(reg0) } }, + // Pointers push a version of themselves made not a reference + Type::P(n, _, t) => { TypedLoc { ty: Type::P(n, false, t), loc: Location::Reg(reg0) } }, + _ => panic!() + }; + + // Get e1 in location + let str1 = self.get_location_to_string(&e1, reg0.into(), true, true); + + // Pop old tokens, freeing registers + self.pop_token(); + self.pop_token(); + + // Do the operation + println!("{} {}, {}", op, reg0, str1); + + // Return new token + return Ok(new_e0); + } +} + +// This is outside of State just for my formatting sanity +fn proc_token(state: &mut State, token: &str) -> Result { + let penultimate = *state.token_stack.iter().nth_back(1).unwrap_or(&Token::None); + let ultimate = *state.token_stack.last().unwrap_or(&Token::None); + + // Processing match + match (state.scope, penultimate, ultimate, token) { + // --- Global Declaration --- + (Scope::Global, _, _, "fn") => { + state.scope = Scope::FnDef; + state.token_stack.clear(); + }, + (Scope::Global, _, _, "extern") => { + state.scope = Scope::ExternFnDef; + state.token_stack.clear(); + }, + + // --- Function Declaration --- + (Scope::FnDef, _, Token::Identifier, "(") => { + if let Some(id) = state.strings.last() { + println!("global {}", id); + println!("{}:", id); + state.scope = Scope::FnArgSetup; + state.token_stack.clear(); + // Args start at ebp + 20 + state.args_count = 20; + state.token_stack.clear(); + state.used_stack = 0; + state.extra_stack = 0; + state.controls_used = 0; + state.locals.clear(); + state.control_stack.clear(); + state.reset_regs(); + } + }, + (Scope::ExternFnDef, _, Token::Identifier, ";") => { + if let Some(id) = state.strings.last() { + println!("extern {}", id); + state.scope = Scope::Global; + state.token_stack.clear(); + } + }, + (Scope::FnDef | Scope::ExternFnDef, _, _, s) => { + state.strings.push(s.to_string()); + state.token_stack.push(Token::Identifier); + }, + (Scope::Code(Control::None), _, last, "endfn") => { + // Don't redo a ret we just did + if let Token::DidRet = last { + } else { + println!("mov esp, ebp"); + // Restore regs preserved by ABI + println!("pop ebx\npop esi\npop edi\npop ebp"); + println!("ret"); + } + state.scope = Scope::Global; + state.token_stack.clear(); + state.locals.clear(); + state.control_stack.clear(); + state.reset_regs(); + }, + + // --- Local Declaration --- + (Scope::FnArgSetup, _, _, ")") => { + state.scope = Scope::FnStackSetup; + state.token_stack.clear(); + }, + (Scope::FnArgSetup, _, Token::Identifier, s) if let Ok(t) = s.parse::() => { + let id = state.strings.pop().unwrap(); + state.locals.insert(id, (t, state.args_count)); + state.args_count += 4; + state.token_stack.pop(); + }, + (Scope::FnStackSetup, _, _, "endvar") => { + // align stack to 4 bytes + state.used_stack += -(state.used_stack as i32) as u32 & 3; + // Save regs preserved by ABI + println!("push ebp\npush edi\npush esi\npush ebx"); + println!("mov ebp, esp"); + if state.used_stack > 0 { + println!("sub esp, {}", state.used_stack); + } + if state.do_debug { + state.dbg_print_locals(); + println!("; Defines over"); + } + state.scope = Scope::Code(Control::None); + state.token_stack.clear(); + state.reset_regs(); + }, + (Scope::FnStackSetup, _, Token::Identifier, s) if let Ok(t) = s.parse::() => { + while let Some(Token::Identifier) = state.token_stack.last() { + state.used_stack += t.width() as u32; + match t.width() { + Width::Dword => { + // align stack to 4 bytes + state.used_stack += -(state.used_stack as i32) as u32 & 3; + }, + Width::Word => { + // align stack to 2 bytes + state.used_stack += state.used_stack % 2; + }, + Width::Byte => { } + } + let id = state.strings.pop(); + state.locals.insert(id.unwrap(), (t.clone(), -(state.used_stack as i32))); + state.token_stack.pop(); + } + }, + (Scope::FnStackSetup, _, _, s) if let Ok(t) = s.parse::() => { + return Err(format!("Error: Declaration '{}' does not declare anything.", s)); + }, + (Scope::FnStackSetup, _, _, ";") => { + state.token_stack.clear(); + }, + (Scope::FnStackSetup | Scope::FnArgSetup, _, _, s) if s.chars().all(|c| c.is_alphanumeric()) => { + state.strings.push(s.to_string()); + state.token_stack.push(Token::Identifier); + }, + (Scope::FnStackSetup, _, _, _) => { + return Err(format!("Invalid syntax in variable declarations")); + }, + + // --- Assignment --- + // e _ =: -> nop + // e t@l =: -> SYNTAX ERROR (not ref) + // e P(n,F,t)@l =: -> SYNTAX ERROR (not ref) + // e P(n,T,t)@l =: -> mov [l], e + (Scope::Code(_),Token::Expression(e1), Token::Expression(e0 @ TypedLoc { ty: Type::P(_, true, _), .. }), "=:") => { + // Make sure sizes work + if e0.ty.val_width() != e1.ty.val_width() && !( + if let Location::Imm(..) = e0.loc { true } else { false } || + if let Location::Imm(..) = e1.loc { true } else { false }) { + return Err("Type mismatch".to_string()); + } + + // Will always be a memory location or reg, since e0 is a ref + let str0 = state.get_location_to_string(&e0, e1.loc.into(), false, true); + // Never can be memory + let str1 = state.get_location_to_string(&e1, e0.loc.into(), true, false); + + // Do the move + println!("mov {}, {}", str0, str1); + + // Pop old tokens, freeing registers + state.pop_token(); + state.pop_token(); + }, + (Scope::Code(_),Token::Expression(_), Token::BlackHole, "=:") => { + // Pop old tokens, freeing registers + state.pop_token(); + state.pop_token(); + }, + (Scope::Code(_), Token::Expression(_), Token::Expression(_), "=:") => { + return Err("Could not generate op '=:', assigned expression is not a reference".to_string()); + }, + // _ e := -> nop + // t@l e := -> SYNTAX ERROR (not ref) + // P(n,F,t)@l e := -> SYNTAX ERROR (not ref) + // P(n,T,t)@l e := -> mov [l], e + (Scope::Code(_),Token::Expression(e0 @ TypedLoc { ty: Type::P(_, true, _), .. }), Token::Expression(e1), ":=") => { + // Make sure sizes work + if e0.ty.val_width() != e1.ty.val_width() && !( + if let Location::Imm(..) = e0.loc { true } else { false } || + if let Location::Imm(..) = e1.loc { true } else { false }) { + return Err("Type mismatch".to_string()); + } + + // Will always be a memory location or reg, since e0 is a ref + let str0 = state.get_location_to_string(&e0, e1.loc.into(), false, true); + // Never can be memory + let str1 = state.get_location_to_string(&e1, e0.loc.into(), true, false); + + // Do the move + println!("mov {}, {}", str0, str1); + + // Pop old tokens, freeing registers + state.pop_token(); + state.pop_token(); + }, + (Scope::Code(_),Token::BlackHole, Token::Expression(_), ":=") => { + // Pop old tokens, freeing registers + state.pop_token(); + state.pop_token(); + }, + (Scope::Code(_), Token::Expression(_), Token::Expression(_), ":=") => { + return Err("Could not generate op ':=', assigned expression is not a reference".to_string()); + }, + + // --- Ref & Deref --- + // t@l * -> Syntax Error (not a pointer) + // P(0,_,t)@l * -> Syntax Error (not a pointer) + // P(n,T,t)@l * -> mov r,[l] -> P(n-1,T,t)@r + // P(n,F,t)@l * -> nop -> P(n-1,T,t)@l + (Scope::Code(_), _, Token::Expression(e0 @ TypedLoc { ty: Type::P(e0_n @ 1.., e0_ref, e0_t), .. }), "*") => { + let new_ty = Type::P(e0_n - 1, true, e0_t); + let new_loc = + if e0_ref { + let r = + if let TypedLoc { loc: Location::Reg(r), .. } = e0 { + r.sized(new_ty.width()) + } else { + state.get_free_reg_exclude(ExReg::default()).sized(new_ty.width()) + }; + let str0 = state.get_location_to_string(&e0, ExReg::default(), false, true); + println!("mov {}, {}", r, str0); + Location::Reg(r) + } else { + e0.loc + }; + // Remove old token + state.pop_token(); + // Add new token + state.push_token(Token::Expression(TypedLoc { + ty: new_ty, + loc: new_loc + })); + }, + + // P(n,T,t)@r & -> nop -> P(n+1,F,t)@r + // P(n,T,t)@l & -> lea r,[l] -> P(n+1,F,t)@r + (Scope::Code(_), _, Token::Expression(e0 @ TypedLoc { ty: Type::P(e0_n, true, e0_t), .. }), "&") => { + let new_ty = Type::P(e0_n + 1, false, e0_t); + let new_loc = + if let TypedLoc { loc: Location::Reg(r), .. } = e0 { + Location::Reg(r) + } else { + let str0 = state.get_location_to_string(&e0, ExReg::default(), false, true); + let r = state.get_free_reg_exclude(ExReg::default()); + println!("lea {}, {}", r, str0); + Location::Reg(r) + }; + + // Remove old token + state.pop_token(); + // Add new token + state.push_token(Token::Expression(TypedLoc { + ty: new_ty, + loc: new_loc + })); + }, + + // --- Arithmetic Operations --- + (Scope::Code(_),Token::Expression(e0), Token::Expression(e1), "+") => { + // Compile time calculation... not sure how to make this work nice + // + //if let (Location::Imm(e0_sign, e0_i), Location::Imm(_, e1_i)) = (e0.loc, e1.loc) { + // state.pop_token(); + // state.pop_token(); + // state.push_token(Token::Expression(TypedLoc { ty: e0.ty, loc: Location::Imm(e0_sign, e0_i + e1_i) })); + //} else { + match state.binary_operator("add", &e0, &e1, true, true) { + Ok(new_token) => state.push_token(Token::Expression(new_token)), + Err(e) => return Err(format!("Could not generate '+': {}", e)) + } + //} + }, + (Scope::Code(_),Token::Expression(e0), Token::Expression(e1), "-") => { + match state.binary_operator("sub", &e0, &e1, true, true) { + Ok(new_token) => state.push_token(Token::Expression(new_token)), + Err(e) => return Err(format!("Could not generate '-': {}", e)) + } + }, + (Scope::Code(_),Token::Expression(e0), Token::Expression(e1), "xor") => { + match state.binary_operator("xor", &e0, &e1, true, true) { + Ok(new_token) => state.push_token(Token::Expression(new_token)), + Err(e) => return Err(format!("Could not generate 'xor': {}", e)) + } + }, + (Scope::Code(_),Token::Expression(e0), Token::Expression(e1), "and") => { + match state.binary_operator("and", &e0, &e1, true, true) { + Ok(new_token) => state.push_token(Token::Expression(new_token)), + Err(e) => return Err(format!("Could not generate 'and': {}", e)) + } + }, + (Scope::Code(_),Token::Expression(e0), Token::Expression(e1), "or") => { + match state.binary_operator("or", &e0, &e1, true, true) { + Ok(new_token) => state.push_token(Token::Expression(new_token)), + Err(e) => return Err(format!("Could not generate 'or': {}", e)) + } + }, + // --- Conditional Operations --- + (Scope::Code(_),Token::Expression(e0), Token::Expression(e1), "ne") => { + let new_token = state.binary_operator("cmp", &e0, &e1, true, true); + match new_token { + Ok(TypedLoc { loc: Location::Reg(r), .. }) => { + let r_sized = r.sized(Width::Byte); + println!("setne {}", r_sized); + state.push_token(Token::Expression(TypedLoc { ty: Type::Value(DataType::U8), loc: Location::Reg(r_sized)} )); + }, + Err(e) => return Err(format!("Could not generate 'ne': {}", e)), + _ => panic!() + } + }, + (Scope::Code(_),Token::Expression(e0), Token::Expression(e1), "eq") => { + let new_token = state.binary_operator("cmp", &e0, &e1, true, true); + match new_token { + Ok(TypedLoc { loc: Location::Reg(r), .. }) => { + let r_sized = r.sized(Width::Byte); + println!("sete {}", r_sized); + state.push_token(Token::Expression(TypedLoc { ty: Type::Value(DataType::U8), loc: Location::Reg(r_sized)} )); + }, + Err(e) => return Err(format!("Could not generate 'eq': {}", e)), + _ => panic!() + } + }, + (Scope::Code(_),Token::Expression(e0), Token::Expression(e1), "gt") => { + let new_token = state.binary_operator("cmp", &e0, &e1, true, true); + match new_token { + Ok(TypedLoc { loc: Location::Reg(r), .. }) => { + let r_sized = r.sized(Width::Byte); + println!("setg {}", r_sized); + state.push_token(Token::Expression(TypedLoc { ty: Type::Value(DataType::U8), loc: Location::Reg(r_sized)} )); + }, + Err(e) => return Err(format!("Could not generate 'gt': {}", e)), + _ => panic!() + } + }, + (Scope::Code(_),Token::Expression(e0), Token::Expression(e1), "lt") => { + let new_token = state.binary_operator("cmp", &e0, &e1, true, true); + match new_token { + Ok(TypedLoc { loc: Location::Reg(r), .. }) => { + let r_sized = r.sized(Width::Byte); + println!("setl {}", r_sized); + state.push_token(Token::Expression(TypedLoc { ty: Type::Value(DataType::U8), loc: Location::Reg(r_sized)} )); + }, + Err(e) => return Err(format!("Could not generate 'lt': {}", e)), + _ => panic!() + } + }, + (Scope::Code(_),Token::Expression(e0), Token::Expression(e1), "ge") => { + let new_token = state.binary_operator("cmp", &e0, &e1, true, true); + match new_token { + Ok(TypedLoc { loc: Location::Reg(r), .. }) => { + let r_sized = r.sized(Width::Byte); + println!("setge {}", r_sized); + state.push_token(Token::Expression(TypedLoc { ty: Type::Value(DataType::U8), loc: Location::Reg(r_sized)} )); + }, + Err(e) => return Err(format!("Could not generate 'ge': {}", e)), + _ => panic!() + } + }, + (Scope::Code(_),Token::Expression(e0), Token::Expression(e1), "le") => { + let new_token = state.binary_operator("cmp", &e0, &e1, true, true); + match new_token { + Ok(TypedLoc { loc: Location::Reg(r), .. }) => { + let r_sized = r.sized(Width::Byte); + println!("setle {}", r_sized); + state.push_token(Token::Expression(TypedLoc { ty: Type::Value(DataType::U8), loc: Location::Reg(r_sized)} )); + }, + Err(e) => return Err(format!("Could not generate 'le': {}", e)), + _ => panic!() + } + }, + // --- Unary Operations --- + (Scope::Code(_), _, Token::Expression(e0), "not") => { + // Get e0 in register + let reg0 = state.get_val_in_free_reg(&e0, ExReg::default()); + + // Get new type + let new_e0 = match e0.ty { + Type::P(0, _, t) | Type::Value(t) => { TypedLoc { ty: Type::Value(t), loc: Location::Reg(reg0) } }, + // Pointers push a version of themselves made not a reference + Type::P(n, _, t) => { TypedLoc { ty: Type::P(n, false, t), loc: Location::Reg(reg0) } }, + _ => panic!() + }; + + // Do operation + println!("not {}", reg0); + + // Pop old token + state.pop_token(); + + // Push new token + state.push_token(Token::Expression(new_e0)); + }, + (Scope::Code(_), _, Token::Expression(e0), "neg") => { + // Get e0 in register + let reg0 = state.get_val_in_free_reg(&e0, ExReg::default()); + + // Get new type + let new_e0 = match e0.ty { + Type::P(0, _, t) | Type::Value(t) => { TypedLoc { ty: Type::Value(t), loc: Location::Reg(reg0) } }, + // Pointers push a version of themselves made not a reference + Type::P(n, _, t) => { TypedLoc { ty: Type::P(n, false, t), loc: Location::Reg(reg0) } }, + _ => panic!() + }; + + // Do operation + println!("neg {}", reg0); + + // Pop old token + state.pop_token(); + + // Push new token + state.push_token(Token::Expression(new_e0)); + }, + (Scope::Code(_), _, Token::Expression(e0), "!") => { + // Get e0 in register + let reg0 = state.get_val_in_reg(&e0, Register::EAX); + + // Get new type + let new_e0 = match e0.ty { + Type::P(0, _, t) | Type::Value(t) => { TypedLoc { ty: Type::Value(t), loc: Location::Reg(reg0) } }, + // Pointers push a version of themselves made not a reference + Type::P(n, _, t) => { TypedLoc { ty: Type::P(n, false, t), loc: Location::Reg(reg0) } }, + _ => panic!() + }; + + // Pop old token + state.pop_token(); + + // Do operation + println!("cmp {}, 0", reg0); + if let TypedLoc { loc: Location::Reg(r), .. } = new_e0 { + let r_sized = r.sized(Width::Byte); + println!("sete {}", r_sized); + state.push_token(Token::Expression(TypedLoc { ty: Type::Value(DataType::U8), loc: Location::Reg(r_sized)} )); + } else { panic!() } + }, + + // --- Casting --- + (Scope::Code(_), _, Token::Expression(_), "$") => { + state.push_token(Token::CastOperator); + }, + (Scope::Code(_), _, _, "$") => { + return Err("Cast operator $ must follow an expression".to_string()); + }, + (Scope::Code(_), Token::Expression(e0), Token::CastOperator, s) if let Ok(t) = s.parse::() => { + // Get e0 in register, extending as needed + let reg0 = state.get_val_in_free_reg_szex(&e0, ExReg::default(), t); + // Pop old tokens + state.pop_token(); + state.pop_token(); + // Push new token + state.push_token(Token::Expression(TypedLoc { ty: t, loc: Location::Reg(reg0) })); + }, + (Scope::Code(_), _, Token::CastOperator, s) => { + return Err(format!("Casted type '{}' is not a type",s)); + } + + // --- Code Syntax --- + (Scope::Code(_), _, last, ";") => { + if let Token::DidRet = last { + } else { + state.token_stack.clear(); + } + state.reset_regs(); + if state.extra_stack > 0 { + println!("add esp, {}", state.extra_stack); + state.extra_stack = 0; + } + }, + // Black Hole disposes of assignment stuff + (Scope::Code(_), _, _, "_") => { + state.push_token(Token::BlackHole); + }, + + // --- Control Flow --- + // - Loop - + (Scope::Code(_), _, _, "loop") => { + state.control_stack.push((Control::Loop, state.controls_used)); + println!(".l{}:", state.controls_used); + state.scope = Scope::Code(Control::Loop); + state.controls_used += 1; + state.token_stack.clear(); + state.reset_regs(); + }, + (Scope::Code(Control::Loop), _, Token::Expression(e0), s @ ("breakif" | "breakifn")) => { + if let Some((Control::Loop, n)) = state.control_stack.last() { + let n = *n; + let str0 = state.get_location_to_string(&e0, ExReg::default(), false, false); + println!("test {}, {}", str0, str0); + match s { + "breakif" => + println!("jnz .le{}", n), + "breakifn" => + println!("jz .le{}", n), + _ => panic!() + } + state.token_stack.clear(); + state.reset_regs(); + } else { + // error + return Err("breakif found but no loop?".to_string()); + } + }, + (Scope::Code(Control::Loop), _, Token::Expression(e0), s @ ("contif" | "contifn")) => { + if let Some((Control::Loop, n)) = state.control_stack.last() { + let n = *n; + let str0 = state.get_location_to_string(&e0, ExReg::default(), false, false); + println!("test {}, {}", str0, str0); + match s { + "contif" => + println!("jnz .l{}", n), + "contifn" => + println!("jz .l{}", n), + _ => panic!() + } + state.token_stack.clear(); + state.reset_regs(); + } else { + // error + return Err("contif found but no loop?".to_string()); + } + }, + (Scope::Code(Control::Loop), _, _, "end") => { + if let Some((Control::Loop, n)) = state.control_stack.last() { + println!("jmp .l{}", *n); + println!(".le{}:", *n); + // End while + state.control_stack.pop(); + // Restore last state + state.scope = Scope::Code( + if let Some((c, _)) = state.control_stack.last() { *c } + else { Control::None } + ); + state.token_stack.clear(); + state.reset_regs(); + } else { + // error + return Err("loop end found but not in loop?".to_string()); + } + }, + // - If/Elif/Else - + (Scope::Code(_), _, _, "if") => { + state.control_stack.push((Control::If, state.controls_used)); + state.scope = Scope::Code(Control::If); + state.controls_used += 1; + state.token_stack.clear(); + state.reset_regs(); + }, + (Scope::Code(Control::If | Control::Elif(_)), _, _, "elif") => { + // Next end point .liEn + // True end point .lEn + if let Some((c @ (Control::If | Control::Elif(_)), n)) = state.control_stack.last() { + let n = *n; + let c = *c; + // Have end of if/elif code jump to new end + println!("jmp .lE{}", n); + let next_i; + if let Control::Elif(i) = c { + // Last elif jumps to this + println!(".l{}e{}:", i, n); + next_i = i + 1; + } else { + // The if jumps to this + println!(".le{}:", n); + next_i = 0; + } + // New scope + state.control_stack.pop(); + state.control_stack.push((Control::Elif(next_i), n)); + state.scope = Scope::Code(Control::Elif(next_i)); + state.token_stack.clear(); + state.reset_regs(); + } else { + // error + return Err("elif .. elif found but no elif?".to_string()); + } + }, + (Scope::Code(Control::If | Control::Elif(_)), _, _, "else") => { + // Next end point .liEn + // True end point .lEn + if let Some((c @ (Control::If | Control::Elif(_)), n)) = state.control_stack.last() { + let n = *n; + // Have end of if/elif code jump to new end + println!("jmp .lE{}", n); + if let Control::Elif(i) = c { + // Last elif jumps to this + println!(".l{}e{}:", *i, n); + } else { + // If jumps to this + println!(".le{}:", n); + } + // New scope + state.control_stack.pop(); + state.control_stack.push((Control::Else, n)); + state.scope = Scope::Code(Control::Else); + state.token_stack.clear(); + state.reset_regs(); + } else { + // error + return Err("elif .. else found but no elif?".to_string()); + } + }, + (Scope::Code(Control::If | Control::Elif(_)), _, Token::Expression(e0), "then") => { + // Next end point .liEn + // True end point .lEn + if let Some((c @ (Control::If | Control::Elif(_)), n)) = state.control_stack.last() { + let n = *n; + let c = *c; + let str0 = state.get_location_to_string(&e0, ExReg::default(), false, false); + println!("test {}, {}", str0, str0); + if let Control::Elif(i) = c { + println!("jz .l{}e{}", i, n); + } else { + println!("jz .le{}", n); + } + state.token_stack.clear(); + state.reset_regs(); + } else { + // error + return Err("if/elif .. then found but no if/elif?".to_string()); + } + }, + (Scope::Code(Control::If | Control::Elif(_) | Control::Else), _, _, "end") => { + if let Some((c @ (Control::If | Control::Elif(_) | Control::Else), n)) = state.control_stack.last() { + if let Control::If = c { + println!(".le{}:", *n); + } else { + if let Control::Elif(i) = c { + println!(".l{}e{}:", *i, *n); + } + println!(".lE{}:", *n); + } + // End if + state.control_stack.pop(); + // Restore last state + state.scope = Scope::Code( + if let Some((c, _)) = state.control_stack.last() { *c } + else { Control::None } + ); + state.token_stack.clear(); + state.reset_regs(); + } else { + // error + return Err("if/else end found but not in if/else?".to_string()); + } + }, + + // --- Calling --- + (Scope::Code(c), _, _, ")") => { + state.control_stack.push((c, 0)); + + // Free non-preserved regs (eax, ecx, edx) on the stack + let mut regs_to_free = Vec::new(); + let mut split_iter = state.token_stack.rsplit(|t| match t { + Token::CallOpen => true, + _ => false + }); + // Skip first group of stuff (our args) + split_iter.next(); + while let Some(iter) = split_iter.next() { + for t in iter { + match t { + Token::Expression(TypedLoc { loc: Location::Reg(r), .. }) if match r.sized(Width::Dword) { + Register::EAX | Register::ECX | Register::EDX => true, + _ => false + } => regs_to_free.push(*r), + _ => { } + } + } + } + // Free them + for reg in regs_to_free { + state.free_reg(reg); + } + + let mut got_open = false; + // Push our args + state.call_stack = 0; + loop { + match state.token_stack.pop() { + None => break, + Some(Token::CallOpen) => { got_open = true; break } + Some(Token::Expression(exp)) => { + let str = match exp.ty.val_width() { + // Always push 4 bytes / arg + Width::Dword => { + state.get_location_to_string(&exp, ExReg::default(), true, true) + }, + // Get in reg if less than 4 bytes + _ => { + let reg = state.get_val_in_free_reg_szex(&exp, ExReg::default(), Type::Value(DataType::U32)); + reg.to_string() + }, + }; + println!("push {}", str); + state.call_stack += 4; + if let TypedLoc { loc: Location::Reg(r), .. } = exp { + state.clear_reg(r); + } + } + _ => { } + } + } + if !got_open { + return Err("Call does not have matching open".to_string()); + } + + state.scope = Scope::FnCall; + }, + (Scope::Code(_), _, _, "(") => { + state.token_stack.push(Token::CallOpen); + }, + (Scope::FnCall, _, _, s) => { + println!("call {}", s); + + if state.call_stack > 0 { + println!("add esp, {}", state.call_stack); + state.call_stack = 0; + } + + // state.ntoken_stack.clear(); + // state.reset_regs_t(); + + state.push_token(Token::Expression(TypedLoc { ty: Type::Value(DataType::U32), loc: Location::Reg(Register::EAX) })); + + let (c, _) = state.control_stack.pop().unwrap(); + state.scope = Scope::Code(c); + }, + + // - Return - + (Scope::Code(_), _, _, "ret") => { + if let Some(Token::Expression(exp)) = state.token_stack.pop() { + state.get_val_in_reg(&exp, Register::EAX); + } + println!("mov esp, ebp"); + // Restore regs preserved by ABI + println!("pop ebx\npop esi\npop edi\npop ebp"); + println!("ret"); + state.push_token(Token::DidRet); + }, + + // --- Code Label --- + (Scope::Code(_), _, _, s) => { + if let Ok(i) = s.parse::() { + state.token_stack.push(Token::Expression(TypedLoc { + ty: Type::Value(DataType::U32), + loc: Location::Imm(true, i as u32) + })); + } else if let Ok(i) = s.parse::() { + state.token_stack.push(Token::Expression(TypedLoc { + ty: Type::Value(DataType::U32), + loc: Location::Imm(false, i) + })); + } else if let Some((st, sl)) = state.locals.get(s) { + state.token_stack.push(Token::Expression(TypedLoc { + ty: match *st { + Type::None => return Err("Bad local value".to_string()), + Type::Value(dt) => Type::P(0, true, dt), + Type::P(n,_,dt) => Type::P(n, true, dt) + }, + loc: Location::Local(*sl) + })); + } else { + return Err(format!("Could not parse token '{}'", token)) + } + }, + + + _ => { return Err(format!("Could not parse token '{}'", token)) } + } + + Ok(0) +} + +struct ParseResult { new_lines: u32, eof: bool } +fn get_next(input: &mut T, state: &mut State) -> Result { + let mut s = [0; 256]; + let mut s_idx = 0; + let mut b = [0; 1]; + let mut got_eof = false; + let mut newlines = 0; + + let mut last_b: Option = Option::None; + while s_idx < s.len() { + match input.read(&mut b) { + Ok(1) => { }, + Ok(_) | Err(_) => { got_eof = true; break } + } + + fn can_follow(last: u8, next: u8) -> bool { + if last == b'=' && next == b':' { return true } + if last == b':' && next == b'=' { return true } + if last == b'/' && next == b'/' { return true } + if last == b'-' && next.is_ascii_digit() { return true } + if last.is_ascii_punctuation() || next.is_ascii_punctuation() { return false } + if last.is_ascii_whitespace() != next.is_ascii_whitespace() { return false } + // alphanumeric stuff + else { return true } + } + + match (last_b, b[0]) { + (Some(last), b) if !can_follow(last, b) => { + input.seek(io::SeekFrom::Current(-1)).unwrap(); + break + }, + //b' ' => break, + (_, b'\n') => { newlines += 1 }, + //b';' | b'=' | b'+' | b'-' | b'*' | b'(' | b')' | b'&' | b'<' => { + // if s_idx == 0 { + // s[s_idx] = b[0]; + // s_idx += 1; + // } else { + // input.seek(io::SeekFrom::Current(-1)).unwrap(); + // } + // break + //}, + _ => { + s[s_idx] = b[0]; + s_idx += 1; + } + } + + last_b = Some(b[0]); + } + + if s_idx != 0 { + let str = match std::str::from_utf8(&s[0..s_idx]) { + Ok(s) => s, + Err(_) => return Err("Input is not unicode".to_string()) + }; + + if str.chars().all(|c| c.is_whitespace()) { + // Skip whitespace + return Ok(ParseResult { new_lines: newlines, eof: got_eof }) + } + + if str.starts_with("//") { + loop { + match input.read(&mut b) { + Ok(1) => {}, + Ok(_) | Err(_) => { return Err("Read Error".to_string()) } + } + if b[0] == b'\n' { + return Ok(ParseResult { new_lines: newlines + 1, eof: got_eof }) + } + } + } + + if state.do_debug { + println!("; PROC {}\t{}", str, state.dbg_token_stack()); + } + + match proc_token(state, str) { + Ok(_) => { }, + Err(e) => { + return Err(format!("Parse Error: {}", e)); + } + } + } + + return Ok(ParseResult { new_lines: newlines, eof: got_eof }) +} + +fn main() -> Result<(), Box> { + let do_debug = env::args().any(|arg| arg == "-d"); + let shut_up = env::args().any(|arg| arg == "-q"); + + let mut string = String::new(); + io::stdin().read_to_string(&mut string).unwrap(); + let mut buff = Cursor::new(string); + let mut state = State::default(); + state.do_debug = do_debug; + + let mut line = 1; + loop { match get_next(&mut buff, &mut state) { + Ok(ParseResult { new_lines, eof}) => { + line += new_lines; + if eof { + if !shut_up { eprintln!("Done"); } + return Ok(()) + } + }, + Err(e) => { return Err(format!("Line {}: {}", line, e).into()); }, + }} +} diff --git a/test.S b/test.S new file mode 100644 index 0000000..7e5a1a3 --- /dev/null +++ b/test.S @@ -0,0 +1,160 @@ +extern printfd +extern puts +global ack +ack: +push ebp +push edi +push esi +push ebx +mov ebp, esp +mov ebx, [ebp + 20] +cmp ebx, 0 +sete bl +test bl, bl +jz .le0 +mov ebx, [ebp + 24] +add ebx, 1 +mov eax, ebx +mov esp, ebp +pop ebx +pop esi +pop edi +pop ebp +ret +jmp .lE0 +.le0: +mov ebx, [ebp + 24] +cmp ebx, 0 +sete bl +test bl, bl +jz .l0e0 +mov ebx, [ebp + 20] +sub ebx, 1 +push 1 +push ebx +call ack +add esp, 8 +mov esp, ebp +pop ebx +pop esi +pop edi +pop ebp +ret +jmp .lE0 +.l0e0: +mov ebx, [ebp + 20] +sub ebx, 1 +mov eax, [ebp + 24] +sub eax, 1 +push eax +push dword [ebp + 20] +call ack +add esp, 8 +push eax +push ebx +call ack +add esp, 8 +mov esp, ebp +pop ebx +pop esi +pop edi +pop ebp +ret +.lE0: +mov esp, ebp +pop ebx +pop esi +pop edi +pop ebp +ret +global foo +foo: +push ebp +push edi +push esi +push ebx +mov ebp, esp +mov ebx, dword [ebp + 20] +mov eax, dword [ebp + 20] +mov eax, [eax] +add eax, 1 +mov dword [ebx], eax +mov esp, ebp +pop ebx +pop esi +pop edi +pop ebp +ret +global main +main: +push ebp +push edi +push esi +push ebx +mov ebp, esp +sub esp, 12 +mov dword [ebp - 4], 0 +.l0: +mov ebx, [ebp - 4] +cmp ebx, 4 +sete bl +test bl, bl +jnz .le0 +mov dword [ebp - 8], 0 +.l1: +mov ebx, [ebp - 8] +cmp ebx, 4 +sete bl +test bl, bl +jnz .le1 +push dword [ebp - 8] +push dword [ebp - 4] +call ack +add esp, 8 +push eax +call printfd +add esp, 4 +lea ebx, dword [ebp - 8] +push ebx +call foo +add esp, 4 +jmp .l1 +.le1: +mov ebx, [ebp - 4] +add ebx, 1 +mov dword [ebp - 4], ebx +jmp .l0 +.le0: +mov ebx, [ebp + 20] +mov dword [ebp - 8], ebx +mov ebx, [ebp + 24] +mov dword [ebp - 12], ebx +.l2: +mov ebx, [ebp - 8] +cmp ebx, 0 +sete bl +mov eax, dword [ebp - 12] +mov eax, [eax] +cmp eax, 0 +sete al +or bl, al +test bl, bl +jnz .le2 +mov ebx, dword [ebp - 12] +push dword [ebx] +call puts +add esp, 4 +mov ebx, [ebp - 8] +sub ebx, 1 +mov dword [ebp - 8], ebx +mov ebx, [ebp - 12] +add ebx, 4 +mov dword [ebp - 12], ebx +jmp .l2 +.le2: +mov esp, ebp +pop ebx +pop esi +pop edi +pop ebp +ret diff --git a/test.o b/test.o new file mode 100644 index 0000000..f976c4c Binary files /dev/null and b/test.o differ diff --git a/test.src b/test.src new file mode 100644 index 0000000..d0b79f5 --- /dev/null +++ b/test.src @@ -0,0 +1,43 @@ +extern printfd; +extern puts; + +fn ack(m i32 n i32) endvar + if m 0 eq then + n 1+ ret; + elif n 0 eq then + (m 1- 1)ack ret; + else + (m 1- (m n 1-)ack)ack ret; + end +endfn + +fn foo(ptr pi32) endvar + ptr* ptr* 1+:= +endfn + +fn main(argc i32 argv ppi8) + x y u32; + ptr ppi8; + endvar + + 0 y=:; + loop + y 4 eq breakif; + 0 x=:; + loop + x 4 eq breakif; + ((y x)ack)printfd; + (x&)foo + end + y 1 + y=:; + end + + x argc :=; + ptr argv :=; + loop + x 0 eq ptr* 0 eq or breakif; + (ptr*)puts; + x 1 - x=:; + ptr 4+ ptr=:; + end +endfn diff --git a/test2.src b/test2.src new file mode 100644 index 0000000..bbfb513 --- /dev/null +++ b/test2.src @@ -0,0 +1,21 @@ + +extern printfd; +extern puts; + +fn main(argc i32 argv ppi8) + a b i32; + endvar + + a 20:=; + 5 b=:; + a 10 a=: b+1+ call printvalue; + + a 20:=; + 5 b=:; + a 0+ 10 a=: b+1+ call printvalue; +endfn + +fn printvalue(a i32) + endvar + a call printfd; +endfn