├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "macro-forth" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "macro-forth" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Tom Niget 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # macro-forth 2 | 3 | Forth, in macros. 4 | 5 | ```rust 6 | fn main() { 7 | const TWO: i32 = forth!(5 3 -); 8 | forth!( 9 | TWO . // 2 10 | ); 11 | 12 | const HUNDRED: i8 = forth!( 13 | 1 2 dup * + dup + // 10 14 | dup * // 100 15 | hundred ! 16 | 3 dup swap drop drop 17 | hundred @ 18 | ); 19 | 20 | forth!( 21 | HUNDRED @ dup . // 100 22 | 50 > if "bigger" else "smaller" then . // bigger 23 | ); 24 | } 25 | ``` 26 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | macro_rules! forth { 2 | (@binop [$b:expr, $a:expr, $($e:tt)*] $name:tt |$an:ident, $bn:ident|$res:block $($x:tt)*) => { 3 | { 4 | let $an = $a; 5 | let $bn = $b; 6 | forth!(@t [$res, $($e)*] $($x)*) 7 | } 8 | }; 9 | 10 | (@binop [$($e:tt)*] $name:ident $func:block $($x:tt)*) => { 11 | compile_error!("Expected 2 parameters for '" stringify!(name) "'") 12 | }; 13 | 14 | // end of code is top of stack 15 | (@t [$top:expr, $($e:tt)*]) => { 16 | $top 17 | }; 18 | 19 | // empty stack is void 20 | (@t []) => { 21 | () 22 | }; 23 | 24 | (@t [$var:ident, $($e:tt)*] @ $($x:tt)*) => { 25 | forth!(@t [$var, $($e)*] $($x)*) 26 | }; 27 | 28 | (@t [$($e:tt)*] @ $($x:tt)*) => { 29 | compile_error!("Expected variable name for @") 30 | }; 31 | 32 | (@t [$var:ident, $val:expr, $($e:tt)*] ! $($x:tt)*) => { 33 | { 34 | let $var = $val; 35 | forth!(@t [$($e)*] $($x)*) 36 | } 37 | }; 38 | 39 | (@t [$($e:tt)*] ! $($x:tt)*) => { 40 | compile_error!("Expected 2 parameters for '!'") 41 | }; 42 | 43 | (@t [$c:expr, $b:expr, $a:expr, $($e:tt)*] rot $($x:tt)*) => { 44 | forth!(@t [$a, $c, $b, $($e)*] $($x)*) 45 | }; 46 | 47 | (@t [$($e:tt)*] rot $($x:tt)*) => { 48 | compile_error!("Expected 3 parameters for 'rot'") 49 | }; 50 | 51 | (@t [$b:expr, $a:expr, $($e:tt)*] swap $($x:tt)*) => { 52 | forth!(@t [$a, $b, $($e)*] $($x)*) 53 | }; 54 | 55 | (@t [$($e:tt)*] swap $($x:tt)*) => { 56 | compile_error!("Expected 2 parameters for 'swap'") 57 | }; 58 | 59 | (@t [$($e:tt)*] + $($x:tt)*) => { 60 | forth!(@binop [$($e)*] + |a, b|{a + b} $($x)*) 61 | }; 62 | 63 | (@t [$($e:tt)*] - $($x:tt)*) => { 64 | forth!(@binop [$($e)*] - |a, b|{a - b} $($x)*) 65 | }; 66 | 67 | (@t [$($e:tt)*] * $($x:tt)*) => { 68 | forth!(@binop [$($e)*] * |a, b|{a * b} $($x)*) 69 | }; 70 | 71 | (@t [$($e:tt)*] / $($x:tt)*) => { 72 | forth!(@binop [$($e)*] / |a, b|{a / b} $($x)*) 73 | }; 74 | 75 | (@t [$($e:tt)*] < $($x:tt)*) => { 76 | forth!(@binop [$($e)*] < |a, b|{if a < b { 1 } else { 0 }} $($x)*) 77 | }; 78 | 79 | (@t [$($e:tt)*] > $($x:tt)*) => { 80 | forth!(@binop [$($e)*] > |a, b|{if a > b { 1 } else { 0 }} $($x)*) 81 | }; 82 | 83 | (@t [$($e:tt)*] = $($x:tt)*) => { 84 | forth!(@binop [$($e)*] = |a, b|{if a == b { 1 } else { 0 }} $($x)*) 85 | }; 86 | 87 | (@t [$top:expr, $next:expr, $($e:tt)*] over $($x:tt)*) => { 88 | forth!(@t [$next, $top, $next, $($e)*] $($x)*) 89 | }; 90 | 91 | (@t [] over $($x:tt)*) => { 92 | compile_error!("'over' on empty stack") 93 | }; 94 | 95 | (@t [$top:expr, $next:expr, $($e:tt)*] tuck $($x:tt)*) => { 96 | forth!(@t [$top, $next, $top, $($e)*] $($x)*) 97 | }; 98 | 99 | (@t [] tuck $($x:tt)*) => { 100 | compile_error!("'tuck' on empty stack") 101 | }; 102 | 103 | (@t [$top:expr, $($e:tt)*] dup $($x:tt)*) => { 104 | forth!(@t [$top, $top, $($e)*] $($x)*) 105 | }; 106 | 107 | (@t [] dup $($x:tt)*) => { 108 | compile_error!("'dup' on empty stack") 109 | }; 110 | 111 | (@t [$top:expr, $($e:tt)*] drop $($x:tt)*) => { 112 | forth!(@t [$($e)*] $($x)*) 113 | }; 114 | 115 | (@t [] drop $($x:tt)*) => { 116 | compile_error!("'drop' on empty stack") 117 | }; 118 | 119 | (@if_zero [$($e:tt)*] else $($x:tt)*) => { 120 | forth!(@t [$($e)*] $($x)*) 121 | }; 122 | 123 | (@if_zero [$($e:tt)*] $top:tt $($x:tt)*) => { 124 | forth!(@if_zero [$($e)*] $($x)*) 125 | }; 126 | 127 | (@t [$($e:tt)*] then $($x:tt)*) => { 128 | forth!(@t [$($e)*] $($x)*) 129 | }; 130 | 131 | (@if_nonzero [$($e:tt)*] then $($x:tt)*) => { 132 | forth!(@t [$($e)*] $($x)*) 133 | }; 134 | 135 | (@if_nonzero [$($e:tt)*] $top:tt $($x:tt)*) => { 136 | forth!(@if_nonzero [$($e)*] $($x)*) 137 | }; 138 | 139 | (@t [$($e:tt)*] else $($x:tt)*) => { 140 | forth!(@if_nonzero [$($e)*] $($x)*) 141 | }; 142 | 143 | // nested ifs are not supported, but should be easy to implement with an additional stack 144 | (@t [$top:expr, $($e:tt)*] if $($x:tt)*) => { 145 | if $top != 0 { 146 | forth!(@t [$($e)*] $($x)*) 147 | } else { 148 | forth!(@if_zero [$($e:tt)*] $($x)*) 149 | } 150 | }; 151 | 152 | (@t [] if $($x:tt)*) => { 153 | compile_error!("'if' on empty stack") 154 | }; 155 | 156 | (@t [$top:expr, $($e:tt)*] . $($x:tt)*) => { 157 | { 158 | println!("{}", $top); 159 | forth!(@t [$($e)*] $($x)*) 160 | } 161 | }; 162 | 163 | (@t [] . $($x:tt)*) => { 164 | compile_error!("'.' on empty stack") 165 | }; 166 | 167 | (@t [$($e:tt)*] $num:literal $($x:tt)*) => { 168 | forth!(@t [$num, $($e)*] $($x)*) 169 | }; 170 | 171 | (@t [$($e:tt)*] $var:ident $($x:tt)*) => { 172 | forth!(@t [$var, $($e)*] $($x)*) 173 | }; 174 | 175 | ($($x:tt)*) => { 176 | forth!(@t [] $($x)*) 177 | }; 178 | } 179 | 180 | fn main() { 181 | const TWO: i32 = forth!(5 3 -); 182 | forth!( 183 | TWO . // 2 184 | ); 185 | 186 | const HUNDRED: i8 = forth!( 187 | 1 2 dup * + dup + // 10 188 | dup * // 100 189 | hundred ! 190 | 3 dup swap drop drop 191 | hundred @ 192 | ); 193 | 194 | forth!( 195 | HUNDRED @ dup . // 100 196 | 50 > if "bigger" else "smaller" then . // bigger 197 | ); 198 | } 199 | 200 | --------------------------------------------------------------------------------