dc.ha (5916B)
1 use ascii; 2 use bufio; 3 use encoding::utf8; 4 use fmt; 5 use fs; 6 use getopt; 7 use io; 8 use math; 9 use os; 10 use os::exec; 11 use strconv; 12 use strings; 13 14 let S: []f64 = []; 15 16 export fn main() void = { 17 const help: [_]getopt::help = [ 18 "desk calculator", 19 "[file ...]", 20 ]; 21 const cmd = getopt::parse(os::args, help...); 22 defer getopt::finish(&cmd); 23 24 static const buf: [1]u8 = [0...]; 25 26 for (let i = 0z; i < len(cmd.args); i += 1) { 27 const filename = switch (cmd.args[i]) { 28 case "-" => 29 fmt::fatal("dc: invalid filename '-'"); 30 case "" => 31 fmt::fatal("dc: invalid filename ''"); 32 case => 33 yield cmd.args[i]; 34 }; 35 36 const file = match (os::open(filename)) { 37 case let f: io::file => 38 yield f; 39 case let err: fs::error => 40 fmt::fatal("dc: {} '{}'", fs::strerror(err), filename); 41 }; 42 defer io::close(file)!; 43 44 const in = bufio::buffered(file, buf, []); 45 match (dc(&in)) { 46 case void => 47 void; 48 case io::error => 49 fmt::fatal("dc: IO error"); 50 }; 51 }; 52 53 const in = bufio::buffered(os::stdin, buf, []); 54 match (dc(&in)) { 55 case void => 56 void; 57 case io::error => 58 fmt::fatal("dc: IO error"); 59 }; 60 }; 61 62 fn dc(in: *bufio::bufstream) (void | io::error) = { 63 for (true) { 64 const r = match (bufio::scanrune(&in.stream)) { 65 case utf8::invalid => 66 fmt::fatal("dc: invalid utf8 input"); 67 case io::error => 68 fmt::fatal("dc: IO error"); 69 case io::EOF => 70 break; 71 case let r: rune => 72 yield r; 73 }; 74 75 if (!ascii::isgraph(r)) { 76 continue; 77 }; 78 79 if (ascii::isdigit(r)) { 80 bufio::unreadrune(in, r); 81 push(scan_number(in)); 82 continue; 83 }; 84 85 if (r == '_') { 86 push(-scan_number(in)); 87 continue; 88 }; 89 90 switch (r) { 91 // misc 92 case 'q' => 93 os::exit(0); 94 case '!' => 95 const cmdline = match (bufio::scanline(&in.stream)) { 96 case io::error => 97 fmt::fatal("dc: IO error"); 98 case io::EOF => 99 fmt::errorln("dc: no shell command given")?; 100 continue; 101 case let input: []u8 => 102 yield match (strings::try_fromutf8(input)) { 103 case utf8::invalid => 104 fmt::errorln("dc: invalid shell command input")?; 105 continue; 106 case let c: str => 107 yield c; 108 }; 109 }; 110 const argv = strings::split(cmdline, " "); 111 if (len(argv) == 0) { 112 continue; 113 }; 114 const cmd = os::exec::cmd(argv[0], argv[1..]...)!; //TODO 115 116 const pipe = os::exec::pipe(); 117 os::exec::addfile(&cmd, pipe.1, os::stdout_file); 118 119 const proc = os::exec::start(&cmd)!; //TODO 120 io::close(pipe.1)!; 121 122 let data = io::drain(pipe.0); 123 io::close(pipe.0)!; 124 125 const status = os::exec::wait(&proc); 126 // printing 127 case 'p' => 128 if (len(S) == 0) { 129 fmt::errorln("dc: stack empty")?; 130 continue; 131 }; 132 fmt::println(peek())?; 133 case 'n' => 134 if (len(S) == 0) { 135 fmt::errorln("dc: stack empty")?; 136 continue; 137 }; 138 fmt::println(pop())?; 139 case 'f' => 140 for (let i = len(S) - 1; i < len(S); i -= 1) { 141 fmt::println(S[i])?; 142 }; 143 // stack control 144 case 'c' => 145 clear(); 146 case 'd' => 147 if (len(S) == 0) { 148 fmt::errorln("dc: stack empty")?; 149 continue; 150 }; 151 push(peek()); 152 case 'r' => 153 if (len(S) < 2) { 154 fmt::errorln("dc: stack has too few elements")?; 155 continue; 156 }; 157 const b = pop(); 158 const a = pop(); 159 push(b); 160 push(a); 161 case 'R' => 162 let n = pop(): int; 163 if (n > 1) { 164 const l = len(S): int; 165 const n = if (n < l) n else l; 166 const i = l - n; 167 const a = S[i]; 168 delete(S[i]); 169 append(S, a); 170 } else if (n < -1) { 171 const l = len(S): int; 172 const n = if (-n < l) -n else l; 173 const i = l - n; 174 const a = pop(); 175 insert(S[i], a); 176 }; 177 // arithmetic 178 case '+' => 179 if (len(S) < 2) { 180 fmt::errorln("dc: stack has too few elements")?; 181 continue; 182 }; 183 const b = pop(); 184 const a = pop(); 185 push(a + b); 186 case '-' => 187 if (len(S) < 2) { 188 fmt::errorln("dc: stack has too few elements")?; 189 continue; 190 }; 191 const b = pop(); 192 const a = pop(); 193 push(a - b); 194 case '*' => 195 if (len(S) < 2) { 196 fmt::errorln("dc: stack has too few elements")?; 197 continue; 198 }; 199 const b = pop(); 200 const a = pop(); 201 push(a * b); 202 case '/' => 203 if (len(S) < 2) { 204 fmt::errorln("dc: stack has too few elements")?; 205 continue; 206 }; 207 const b = pop(); 208 const a = pop(); 209 push(a / b); 210 case '%' => 211 if (len(S) < 2) { 212 fmt::errorln("dc: stack has too few elements")?; 213 continue; 214 }; 215 const b = pop(); 216 const a = pop(); 217 push(math::modf64(a, b)); 218 case '^' => 219 if (len(S) < 2) { 220 fmt::errorln("dc: stack has too few elements")?; 221 continue; 222 }; 223 const b = pop(); 224 const a = pop(); 225 push(math::powf64(a, b)); 226 case 'v' => 227 if (len(S) < 1) { 228 fmt::errorln("dc: stack has too few elements")?; 229 continue; 230 }; 231 const a = pop(); 232 push(math::sqrtf64(a)); 233 case => 234 fmt::errorfln("dc: unimplemented '{}'", r)?; 235 }; 236 }; 237 }; 238 239 @noreturn fn usage_exit(help: []getopt::help) void = { 240 getopt::printusage(os::stderr, os::args[0], help); 241 os::exit(1); 242 }; 243 244 fn scan_number(in: *bufio::bufstream) f64 = { 245 let num: []u8 = []; 246 defer free(num); 247 let seen_decimal = false; 248 for (true) { 249 const r = match (bufio::scanrune(in)) { 250 case utf8::invalid => 251 fmt::fatal("dc: invalid utf8 input"); 252 case io::error => 253 fmt::fatal("dc: IO error"); 254 case io::EOF => 255 yield ' '; 256 case let r: rune => 257 yield r; 258 }; 259 if (ascii::isdigit(r) || (!seen_decimal && r == '.')) { 260 append(num, r: u32: u8); 261 if (r == '.') { 262 seen_decimal = true; 263 }; 264 } else { 265 bufio::unreadrune(in, r); 266 match (strconv::stof64(strings::fromutf8(num))) { 267 case (strconv::invalid | strconv::overflow) => 268 abort("dc: invalid numerical input"); 269 case let n: f64 => 270 return n; 271 }; 272 }; 273 }; 274 abort("Unreachable"); 275 }; 276 277 fn pop() f64 = { 278 const a = S[len(S) - 1]; 279 delete(S[len(S) - 1]); 280 return a; 281 }; 282 283 fn push(el: f64) void = { 284 append(S, el); 285 }; 286 287 fn peek() f64 = { 288 return S[len(S) - 1]; 289 }; 290 291 fn clear() void = { 292 for (len(S) != 0) { 293 delete(S[len(S) - 1]); 294 }; 295 };