From 1f6da723640693cbe9b549a6340c23caa3a5d2e2 Mon Sep 17 00:00:00 2001 From: Lucia Ceionia Date: Fri, 12 May 2023 16:54:29 -0500 Subject: [PATCH] Initial commit --- .gitignore | 1 + Cargo.lock | 7 + Cargo.toml | 8 + Makefile | 21 + a.out | Bin 0 -> 15484 bytes ack.src | 12 + ex.src | 43 ++ extra.c | 3 + extra.o | Bin 0 -> 1316 bytes src/main.rs | 1464 +++++++++++++++++++++++++++++++++++++++++++++++++++ test.S | 160 ++++++ test.o | Bin 0 -> 2128 bytes test.src | 43 ++ test2.src | 21 + 14 files changed, 1783 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 Makefile create mode 100755 a.out create mode 100644 ack.src create mode 100644 ex.src create mode 100644 extra.c create mode 100644 extra.o create mode 100644 src/main.rs create mode 100644 test.S create mode 100644 test.o create mode 100644 test.src create mode 100644 test2.src 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 0000000000000000000000000000000000000000..6b196530eb3489430b7e63fcd09b844928a7e747 GIT binary patch literal 15484 zcmb<-^>JflWMqH=CI)5(5U*eY3x^2<1A~q^M8wbT9U$}z`($>fQ18u!C?gr8<2e9(~=aB6VNfp zegOsshM=b*y-nFCh6y-=;mbRl~(8#=j$0k!d--cfdLfu?tY;R zS|RRhPk3LcYO&o@@?sXxs{EOz+d=wy7#J8p;RsU0)WC26BnC1QM1$-Gu|aYmw}He! z7{msJ89M_cZb14O6c|7;zyK0k>;3ouNDVR;MdAoCFfjNc@j)8Zk@z5aklipL5I+;f zVPF7TRDs3^xszo93kS%3f}lu^_wuUt&Ci3H84pp4RUrcd69XdyGdMpng3~l30~3QD0|UslVBZL2X7Yk! z%mOO5W#Tk8kS-^%ECa)f{VW`8ObiTfKr|}@!v_!zN+btZIM^8(7+63wGXnz$hz11& zRNp8y8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd72bLf|vM-0$G#HykfEGcYhT9)7OR zz|h_NJ!tANyH2{;U20^_>|Q82+m=9AM$d04aI-;Q#;s z|5ZV~k_-+828NdxzmFdwJmh|NkKS4By6eK5TySAs{5^g(TS80(}OCEQTzG zz_8xtwHN;X4|uWU|NsAAx@A-jgEYLXW@KPUn*f3!?qLuEf+v8KK$sA6hsXgI4qFgW zqSDO@>Or+0D3J>7eA@g*Bm*Q2@{_>H!@XP7KyID@W5D=e{kkB9orey-kU#i~iSfe4 zA0-OiqM+Va>!lJguvr2D5W_At9|0Lw#sD)1`%vQi+W zuNku#gI^r{|NnpU5e}F#O^8glt3>k)#(@8&A};Lt^5Fmfpw7cD!ofxeOz4jNbMkOEi^^dJ22i|#^TF!B|Nl20zOMsvFk0FN zd2=HHC|TQJUG>0Mut< zU|>-B@c(}a0|P_KhyVY57#J9qeE9z#GzWg-)Bpc77#JA7efs}@4+8@OXwDhrcgCt9 z2F4l=Mrj@vmJUY994BZ_T;Rk1|5hLcF6(2XHXr`~2hSlx=XPBf7#LQ(|Nq|s zq>hinjgQBRpSy&mg27(OTFXd92{c~}8e!1*^#4C-{*=LykE4~ziI>%-nK|v~VICG1 zh{-g(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2Eg+RkZ z7LI_4ETCo8;5E6Rb=eLI6(8#pta-D&~@8V3=9l^_OWn)`m~__<=21z z^EH}SI6(c_6HP1}pk=upO)MN-3=9mD7#JA1p=)|!>!3mX>@WLSI6&*DC7}IgK?Vi} zanM@%W)=<*drmV82WagxNFKCa9yE#m@Bjb&oMsk||NsBzgVsEM`2RmYs+k2O1@Z=Y z7&>Yg(GYN3r8AgtvyJ9fq}u@+1W}#!`&}bQ^C+!&rr`$!N|bK*uccd61>bE zpOcuEt_u-k@N*3IRWQ;sG}ALMWMFJy0+|6aje${+QIJuHQGkt^Lz+XC zLztC;fk}@+kkJ7Y@cY2F%7T`sGydk|WnhHtXkY{_SBKgRHiQ*yo|vMdqN2X7t*yR3 z2!nWv1~9H-q^&+%NZ(pC{Bqy&~w2FZdn+Ug_o%fJReNL!GuNLyRQNL!G(AZ%v~ zk_TC*Z>t{(;@H~S8h{O8Vq`$te*tzU$RjWYIQ@P64~p5)Jm?H5t85kHmk@$uT3=Gvse9#z88xr3cl#Y@3-V6*3XOZ|JXzoMeGO#dw z{2v1nfTlA>22qAo2FQL0kT3&-AOmPUKPW$d_>2tV41FL56oc}>YA72-fyRH1GcYh5 zm;hPtFT}*hz{$wKz;gf+9tsQ+40k~ap%|q99RnnBf`kMZ_!vGhFff1;BWV0ch=Gsc z7bp)x^$RdafTIAE??LK?nD`k$>+50WgXBe!5;@3xWh6exeMX=$FQ|EfOcD(Cp!NAk z`xE>a85p>s;RkY05F-PF1XR5MgCs*ZBLl;R36Syx zGBAKvlz`j|(%;0$z@Um`emf%r1Jh(k_<+oti=-apzLjY1KMdkS%K#xJ9tH+zzDZ2Z z*2_sXU;xtwFovrE186&iUJi^lgz{72448}&OvZ>IwW6dbQ7@Sx-X+q{(bvbAD>iQ3=%IY0S6mHd_0Ex6Dt_v<3oI%<6Zq+;_+=jiO1jRW?o5ZQ2~QqUVcfcUV2`sUQ%ghPKs`33X)JkQGP*cQAs6K$kEADwYDRooQDSatd`3zUf(OwNpIB6sSP9VqW2aQ+CFW)(Gw7w~mw*X9 zumupql8TEN^pf*)b5rw581zz7lS6J+|0bRd>A`1DXA#6493gJ%mbBQ zpu9#52DKSL*$Grpg7&|G_QHYcbC_O`9Eh#VzyRJq1yTpvX9n7HrUH_HVo*B+#56-v z2ik82+It3Shk)cj7^cn>NgYQM3kRsN0V zv|bQuFQ^R@#S9s@huI6#55jc}3=E)l4~PwF`{Xb)Fo5O_Kzq$VW`XPlnb*s}z`zNv z`WYBNZKpZR3=E*Q6U;o2I#9hi4XO^buWCy(3&$2Db)fbaOx=8Fy$jY4DhmY|7_i01 zEF^UX6IeJ51Q-~=a~B{-LEC*GGnXQ%1C`|k0t^h(K%PQU2V#QiI*`9W_JZml4q=Et zKyn}qG7Cg+gqjC70<`y0fq?R*sLP@Ce5K4$oV)PXkP z!OR2I?K}ny3?O?zc7iZi4ajYv{gzOFfa+Kg162Ql%mAt91~)JvX%oZ;sRQMG5F3O+ z>OdIOb^y^JF%Z@_faEt28-!tUpf&}J4HEY^U|@&@F`yVGrw_FkD$KCLfPn$j_=WLd z>TJ=}9W`KJI0RD!4KolIq#l$8Kx`21Fl1nu04)JflWMqH=Mh0dE1doAX2@^!d1Wai#2r%%2<=Gh+7(zRr1~O2gVXg(q^p*!}^$-}BCTnybTDu)@|ot>=|G~E3{H5Ckv^$hh4 z6^smwj15eTEEyOWSQr=>Bp4VNSQ!`?-2Flsm>3ujfW$yXGD6uPt3*Ho3=9m6RY44l zH5`o6JS;37j0_A43=9l1P&qvi?a0T`%H+h$>e9@dcJwe03kw5-00RSq14s^PAC!qg zf$V?@urPf54`YL9W(H`WfH({c49pB{P!@z@W?*OFgfJOEZV+N%Vi1J#L6i^^JA*oi z0mUHq*h1ML3dCnfttcr<)JtY4D9X$$NlRgfkFT&W(@Re+i7!ZwFUcs)%hpS*V2F43 z@pp3ciT8JN3w8~O4{>zzagB%SVc=k3U|?cnVBlq7VBlhBV31^BVBlnCU{D14n*$P7 zOiT<6E+BQFK-Md+%q>YwV$drtDT2@$Fji4&j$TP>MG1pmN@7VOgI-c`F@s)FK7<33 z(8KOPz2yAd+|;}h2EDxel2ko+zfj%clEma}C_g=~RIi{YzaX`!q>@1oWJzj9d|FXr zZYqObdQpC90Vq6(#eED6DB%oBv!E~l`5zQRATNTjJSg2TFu-HYhJgVbPcVC6aW4%D zFQl|`2b#V>=7CZG$Sjcgs!;Qop$23@X&41FPn&@O94Ca-89>bg*#R;GWFE*a5F3PH z=2<}1u|XB)f#MaU0E%JiLZIqE=7OY(kn#wK4^juhu~2m&F%SlYCy0h&kQxv>mw|x+ zl>edDGJx{C21o*mVQy`Kng>(2gMon|0ICK=!PGUNh4UXqNFD`+GdI*vpl}A+4>Jp- zpM#MBY&RdYya1U8$~&OE2Mc46x*3d+G!9~eFvuJb4a$?q>UfwK7^Z?GpctkO09f#c A!vFvP literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..f976c4cecf2429435f4a4d8ec46b3b4bc199e1b0 GIT binary patch literal 2128 zcmb<-^>JflWMqH=Mh0dE1doBi0V-hvrZpG@77#KJh7#K9s%yR%q zK=ptqn0c01)J1|6Kru|6E1JCvSQr>I7#J8p_Oc?`8;WM$1r`PdS*Tec3T9q1nz|1x z3=D@r3ZNLKE+5U_1FR7Lf$RnO6XY!rU5{qo1(2hlW`QV>8W7z>Kpo89=?n}E%peU= z3^H#wlntU_>J~!79V7tNv4>v=fCON&QXr+T z8M7FJUmX1Z|9|rl4wy1ch)lPuMDq*Afd8c;FBU))1O&gB24O*!LMgb_-L4|t49y1^ zJDE{rn&0pocl`tMS9k26=HDz}=asq~cl`ko`vDhI{a^ayf9anW`VeaZUi<+wSbAfB z1izRHVZS~HGUYI1XY8NX>$_t`I%9uy$4Yd@e(8?=0r7jc>z8hZ<{u3FQx0$(Zn<5m z*d6=jzcI`#kh0f|U|nCD53_W}etCTq>Rga>U|0qokDw5Sh$81xQ2u3PhQtLU69WS% zorCfl0|N^vTHtxQB(=CiFPK3;BR@A)Kc_S~Gf_XFC_gJTxujS>CowNw7b3>s=NRm( zV5Dbgre|Qtz}NsW3Supo1lz~L04{Yv_JPt6h!z2*UseW&01%C&i&2nKkWq+HfQ^|$ znnRUCn3aKnNsmF0(E(%-0|Uc8u>U|gk&*E?A1?zVr0`<|l{!#2gY9Jnn<=KKsHmuK zYip~o55gdxq5+Jn7-_2y7qL~ejkHyS2`PakltHo}jkfy8{4%fs5YiT;E7I0hG13-f zE(qJ%g5*Kg>D%f@f;hIewgzAWm>3z5ifXVswb1;}pjVPwQNo~?lA2VS9-mm0n3tYf z%%E44nu9I`QBhEul$V$bQUc*;=B4E$D~IqQa*0VvMX6;lvvV@@Qjt|c_z;z8MTxno zaE1BF40^>G#U({0iAfB4#g(}b8p;Pb4eVyHf6xhVqJj#tFns(EWr8WxnhI1-p%!+` z44~QstQ1TzGjK3SGB7ZxfO#N-kwKAxfx#QZ1Y<@9PC?=9Q$SFcg%Q6f-0yXM?a_PO1Tf zHh?i)4H(k$^BHmzGxNZNUXB5jHiXita0X1q2qpt^%QcYy_!$_K7#SFt1Q-|qcZg-k literal 0 HcmV?d00001 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