use bio use std const strdup = {s var buf = std.slalloc(s.len) std.slcp(buf, s) -> buf } const upcase = {s var i var buf = std.mksb() for i = 0; i < s.len; i++ std.sbputc(buf, std.toupper((s[i] : char))) ;; -> std.sbfin(buf) } const readline = {line var cmt = std.strfind(line, ";") match cmt | `std.Some (pos): line = line[:pos] | `std.None: ;; if line.len == 0 -> (`std.None, [][:]) ;; var parts = std.strtok(line) if std.isspace((line[0] : char)) -> (`std.None, parts) else -> (`std.Some(parts[0]), parts[1:]) ;; } const parsehex = {s if s[0] == ('#' : byte) -> std.intparsebase((s[1:] : byte[:]), 16) else -> `std.None ;; } const emitinstr = {p var b = `std.None var mark = `std.None if p.len < 1 -> `std.Ok(b, mark) ;; match upcase(p[0]) | "CLS": b = `std.Some([0x00, 0xe0]) | "SYS": if p.len < 2 -> `std.Err("SYS missing subroutine address") ;; match parsehex(p[1]) | `std.Some(n): b = `std.Some([0x20 | (n & 0x0f00), (n & 0xff)]) | `std.None: b = `std.Some([0x20, 0x00]) mark = `std.Some(strdup(p[1])) ;; | _: -> `std.Err("unrecognized instruction") ;; -> `std.Ok((b, mark)) } const main = { var fd = bio.mkfile(std.In, bio.Rd) var src = bio.byline(fd) var lc = 0 var offset = 0 var queue = [][:] var buf = [][:] var labels = std.mkht() var res for line : src ; match readline(line) | (`std.None, i): res = emitinstr(i) | (`std.Some (label), i): res = emitinstr(i) std.htput(labels, strdup(label), offset) ;; match res | `std.Err(msg): std.die(std.fmt("{} on line {}\n", msg, lc)) | `std.Ok((bytes, marker)): match marker | `std.Some(label): queue = std.slpush(&queue, (label, offset, lc)) | `std.None: ;; match bytes | `std.Some(arr): buf = std.sljoin(&buf, arr[:]) offset += 2 | `std.None: ;; ;; lc++ ;; for replacement : queue ; match replacement | (l, buf_pos, src_pos): match std.htget(labels, l) | `std.None: std.die(std.fmt("reference to undefined label '{}' on line {}\n", l, src_pos)) | `std.Some(addr): buf[buf_pos] |= addr & 0x0f00 buf[buf_pos + 1] |= addr & 0xff ;; ;; ;; std.write(std.Out, buf) bio.free(fd) }