summaryrefslogtreecommitdiff
path: root/lib/std/optparse.myr
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std/optparse.myr')
-rw-r--r--lib/std/optparse.myr210
1 files changed, 210 insertions, 0 deletions
diff --git a/lib/std/optparse.myr b/lib/std/optparse.myr
new file mode 100644
index 0000000..ae65b77
--- /dev/null
+++ b/lib/std/optparse.myr
@@ -0,0 +1,210 @@
+use "alloc.use"
+use "die.use"
+use "extremum.use"
+use "fmt.use"
+use "strbuf.use"
+use "option.use"
+use "sleq.use"
+use "slpush.use"
+use "syswrap-ss.use"
+use "syswrap.use"
+use "types.use"
+use "utf.use"
+
+pkg std =
+ type optdef = struct
+ argdesc : byte[:] /* the description for the usage */
+ minargs : std.size /* the minimum number of positional args */
+ maxargs : std.size /* the maximum number of positional args (0 = unlimited) */
+ noargs : std.bool /* whether we accept args at all */
+ opts : optdesc[:] /* the description of the options */
+ ;;
+
+ type optdesc = struct
+ opt : char
+ arg : byte[:]
+ desc : byte[:]
+ optional : bool
+ ;;
+
+ type optparsed = struct
+ opts : (char, byte[:])[:]
+ args : byte[:][:]
+ ;;
+
+ const optparse : (optargs : byte[:][:], def : optdef# -> optparsed)
+ const optusage : (prog : byte[:], def : optdef# -> void)
+;;
+
+type optctx = struct
+ /* public variables */
+ args : byte[:][:]
+
+ /* data passed in */
+ optdef : optdef#
+ optargs : byte[:][:]
+
+ /* state */
+ argidx : size
+ curarg : byte[:]
+ optdone : bool /* if we've seen '--', everything's an arg */
+ finished : bool /* if we've processed all the optargs */
+;;
+
+
+const optparse = {args, def
+ var ctx : optctx
+ var parsed
+
+ parsed = [
+ .opts=[][:],
+ .args=[][:]
+ ]
+ optinit(&ctx, args, def)
+ while !optdone(&ctx)
+ parsed.opts = slpush(parsed.opts, optnext(&ctx))
+ ;;
+ if ctx.args.len < def.minargs
+ put("error: expected at least {} args, got {}\n", def.minargs, ctx.args.len)
+ optusage(ctx.optargs[0], ctx.optdef)
+ exit(1)
+ ;;
+ if def.maxargs > 0 && ctx.args.len < def.minargs
+ put("error: expected at most {} args, got {}\n", def.minargs, ctx.args.len)
+ optusage(ctx.optargs[0], ctx.optdef)
+ exit(1)
+ ;;
+ if def.noargs && ctx.args.len != 0
+ put("error: expected no args, got {}\n", ctx.args.len)
+ optusage(ctx.optargs[0], ctx.optdef)
+ exit(1)
+ ;;
+ parsed.args = ctx.args
+ -> parsed
+}
+
+const optinit = {ctx, args, def
+ ctx# = [
+ .optargs = args,
+ .optdef = def,
+ .optdone = false,
+ .finished = false,
+ .argidx = 0,
+ .curarg = [][:],
+ .args = [][:],
+ ]
+
+ next(ctx)
+ -> ctx
+}
+
+const optnext = {ctx
+ var c
+ var arg
+
+ (c, ctx.curarg) = striter(ctx.curarg)
+
+ match optinfo(ctx, c)
+ | `None:
+ if c == 'h' || c == '?'
+ optusage(ctx.optargs[0], ctx.optdef)
+ exit(0)
+ else
+ fatal("unexpected argument '{}'\n", c)
+ ;;
+ | `Some (true, needed):
+ /* -arg => '-a' 'rg' */
+ if ctx.curarg.len > 0
+ arg = ctx.curarg
+ ctx.curarg = ctx.curarg[ctx.curarg.len:]
+ next(ctx)
+ /* '-a rg' => '-a' 'rg' */
+ elif ctx.argidx < (ctx.optargs.len - 1)
+ arg = ctx.optargs[ctx.argidx + 1]
+ ctx.argidx++
+ next(ctx)
+ elif needed
+ put("Expected argument for {}\n", c)
+ exit(1)
+ ;;
+ | `Some (false, _):
+ arg = ""
+ if ctx.curarg.len == 0
+ next(ctx)
+ ;;
+ ;;
+
+
+ -> (c, arg)
+}
+
+const optdone = {ctx
+ -> ctx.curarg.len == 0 && ctx.finished
+}
+
+const optinfo = {ctx, opt
+ for o in ctx.optdef.opts
+ if o.opt == opt
+ -> `Some (o.arg.len != 0, !o.optional)
+ ;;
+ ;;
+ -> `None
+}
+
+const next = {ctx
+ var i
+
+ for i = ctx.argidx + 1; i < ctx.optargs.len; i++
+ if !ctx.optdone && decode(ctx.optargs[i]) == '-'
+ if sleq(ctx.optargs[i], "--")
+ ctx.optdone = true
+ else
+ goto foundopt
+ ;;
+ else
+ ctx.args = slpush(ctx.args, ctx.optargs[i])
+ ;;
+ ;;
+:finishedopt
+ ctx.finished = true
+ -> false
+:foundopt
+ ctx.argidx = i
+ ctx.curarg = ctx.optargs[i][1:]
+ -> true
+}
+
+const optusage = {prog, def
+ var sb, s
+
+ sb = mksb()
+ std.sbfmt(sb, "usage: {} [-h?", prog)
+ for o in def.opts
+ if o.arg.len == 0
+ std.sbfmt(sb, "{}", o.opt)
+ ;;
+ ;;
+ std.sbfmt(sb, "] ")
+ for o in def.opts
+ if o.arg.len != 0
+ std.sbfmt(sb, "[-{} {}] ", o.opt, o.arg)
+ ;;
+ ;;
+ std.sbfmt(sb, "{}\n", def.argdesc)
+ std.sbfmt(sb, "\t-h\tprint this help message\n")
+ std.sbfmt(sb, "\t-?\tprint this help message\n")
+ for o in def.opts
+ std.sbfmt(sb, "\t-{}{}{}\t{}\n", o.opt, sep(o.arg), o.arg, o.desc)
+ ;;
+ s = sbfin(sb)
+ write(1, s)
+ slfree(s)
+}
+
+const sep = {s
+ if s.len > 0
+ -> " "
+ else
+ -> ""
+ ;;
+}