Commit 5e811526dad2ef339649fec64e147d17314cc0be
1 parent
85eedc50
Added updated lapp.nim
Showing
1 changed file
with
98 additions
and
43 deletions
lapp.nim
| 1 | import strutils | 1 | import strutils |
| 2 | -from os import paramCount, paramStr | 2 | +import os |
| 3 | import tables | 3 | import tables |
| 4 | export tables.`[]` | 4 | export tables.`[]` |
| 5 | 5 | ||
| @@ -50,10 +50,10 @@ proc get(L: PLexer; t: var TLexType): string = | @@ -50,10 +50,10 @@ proc get(L: PLexer; t: var TLexType): string = | ||
| 50 | next(L) | 50 | next(L) |
| 51 | if thisChar(L) == '.': | 51 | if thisChar(L) == '.': |
| 52 | t = tfloat | 52 | t = tfloat |
| 53 | - result.add(c) | 53 | + result.add(thisChar(L)) |
| 54 | next(L) | 54 | next(L) |
| 55 | while thisChar(L) in Digits: | 55 | while thisChar(L) in Digits: |
| 56 | - result.add(c) | 56 | + result.add(thisChar(L)) |
| 57 | next(L) | 57 | next(L) |
| 58 | of '.': # ".", "..", "..." | 58 | of '.': # ".", "..", "..." |
| 59 | if thisChar(L) == '.': | 59 | if thisChar(L) == '.': |
| @@ -89,17 +89,17 @@ type | @@ -89,17 +89,17 @@ type | ||
| 89 | vFile, | 89 | vFile, |
| 90 | vSeq | 90 | vSeq |
| 91 | 91 | ||
| 92 | - PValue = ref TValue | ||
| 93 | - TValue = object | ||
| 94 | - case kind: TValueKind | ||
| 95 | - of vInt: asInt *: int | ||
| 96 | - of vFloat: asFloat *: float | ||
| 97 | - of vString: asString *: string | ||
| 98 | - of vBool: asBool *: bool | 92 | + PValue* = ref TValue |
| 93 | + TValue* = object | ||
| 94 | + case kind*: TValueKind | ||
| 95 | + of vInt: asInt*: int | ||
| 96 | + of vFloat: asFloat*: float | ||
| 97 | + of vString: asString*: string | ||
| 98 | + of vBool: asBool*: bool | ||
| 99 | of vFile: | 99 | of vFile: |
| 100 | - asFile *: File | ||
| 101 | - fileName *: string | ||
| 102 | - of vSeq: asSeq *: seq[PValue] | 100 | + asFile*: File |
| 101 | + fileName*: string | ||
| 102 | + of vSeq: asSeq*: seq[PValue] | ||
| 103 | 103 | ||
| 104 | proc boolValue(c: bool): PValue = PValue(kind: vBool, asBool: c) | 104 | proc boolValue(c: bool): PValue = PValue(kind: vBool, asBool: c) |
| 105 | 105 | ||
| @@ -125,7 +125,7 @@ var | @@ -125,7 +125,7 @@ var | ||
| 125 | progname, usage: string | 125 | progname, usage: string |
| 126 | aliases: array[char,string] | 126 | aliases: array[char,string] |
| 127 | parm_spec = initTable[string,PSpec]() | 127 | parm_spec = initTable[string,PSpec]() |
| 128 | - | 128 | + |
| 129 | proc fail(msg: string) = | 129 | proc fail(msg: string) = |
| 130 | stderr.writeln(progname & ": " & msg) | 130 | stderr.writeln(progname & ": " & msg) |
| 131 | quit(usage) | 131 | quit(usage) |
| @@ -205,6 +205,7 @@ proc parseSpec(u: string) = | @@ -205,6 +205,7 @@ proc parseSpec(u: string) = | ||
| 205 | defValue = "false" | 205 | defValue = "false" |
| 206 | 206 | ||
| 207 | if name != nil: | 207 | if name != nil: |
| 208 | + # echo("Param: " & name & " type: " & $ftype & " needsvalue: " & $(ftype != "bool") & " default: " & $defValue & " multiple: " & $multiple) | ||
| 208 | let spec = PSpec(defVal:defValue, ptype: ftype, needsValue: ftype != "bool",multiple:multiple) | 209 | let spec = PSpec(defVal:defValue, ptype: ftype, needsValue: ftype != "bool",multiple:multiple) |
| 209 | aliases[alias] = name | 210 | aliases[alias] = name |
| 210 | parm_spec[name] = spec | 211 | parm_spec[name] = spec |
| @@ -212,14 +213,13 @@ proc parseSpec(u: string) = | @@ -212,14 +213,13 @@ proc parseSpec(u: string) = | ||
| 212 | proc tail(s: string): string = s[1..(-1)] | 213 | proc tail(s: string): string = s[1..(-1)] |
| 213 | 214 | ||
| 214 | var | 215 | var |
| 215 | - files: array[1..MAX_FILES,File] | ||
| 216 | - nfiles = 0 | 216 | + files = newSeq[File]() |
| 217 | 217 | ||
| 218 | proc closeFiles() {.noconv.} = | 218 | proc closeFiles() {.noconv.} = |
| 219 | - if nfiles == 0: return | ||
| 220 | - for i in 1..nfiles: files[i].close() | 219 | + for f in files: |
| 220 | + f.close() | ||
| 221 | 221 | ||
| 222 | -proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | 222 | +proc parseArguments*(usage: string, args: seq[string]): Table[string,PValue] = |
| 223 | var | 223 | var |
| 224 | vars = initTable[string,PValue]() | 224 | vars = initTable[string,PValue]() |
| 225 | n = len(args) - 1 | 225 | n = len(args) - 1 |
| @@ -253,6 +253,9 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | @@ -253,6 +253,9 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | ||
| 253 | parseSpec(usage) | 253 | parseSpec(usage) |
| 254 | addQuitProc(closeFiles) | 254 | addQuitProc(closeFiles) |
| 255 | 255 | ||
| 256 | + # Collect failures | ||
| 257 | + var failures = newSeq[string]() | ||
| 258 | + | ||
| 256 | # parse the flags and arguments | 259 | # parse the flags and arguments |
| 257 | while i <= n: | 260 | while i <= n: |
| 258 | arg = next() | 261 | arg = next() |
| @@ -275,7 +278,8 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | @@ -275,7 +278,8 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | ||
| 275 | for c in arg.tail: | 278 | for c in arg.tail: |
| 276 | let f = get_alias(c) | 279 | let f = get_alias(c) |
| 277 | let i = get_spec(f) | 280 | let i = get_spec(f) |
| 278 | - if i.needsValue: fail("needs value! " & f) | 281 | + if i.needsValue: |
| 282 | + failures.add("option " & f & " needs a value") | ||
| 279 | flagvalues.add(@[f,"true"]) | 283 | flagvalues.add(@[f,"true"]) |
| 280 | i.used = true | 284 | i.used = true |
| 281 | else: # argument (stored as \001, \002, etc | 285 | else: # argument (stored as \001, \002, etc |
| @@ -286,14 +290,28 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | @@ -286,14 +290,28 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | ||
| 286 | if not info.multiple: k += 1 | 290 | if not info.multiple: k += 1 |
| 287 | flagvalues.add(@[flag,value]) | 291 | flagvalues.add(@[flag,value]) |
| 288 | info.used = true | 292 | info.used = true |
| 289 | - | 293 | + |
| 294 | + # Some options disables checking | ||
| 295 | + var enableChecks = true | ||
| 296 | + for flag,info in parm_spec: | ||
| 297 | + if info.used: | ||
| 298 | + if flag == "help" or flag == "version": | ||
| 299 | + enableChecks = false | ||
| 300 | + | ||
| 301 | + | ||
| 290 | # any flags not mentioned? | 302 | # any flags not mentioned? |
| 291 | for flag,info in parm_spec: | 303 | for flag,info in parm_spec: |
| 292 | if not info.used: | 304 | if not info.used: |
| 293 | if info.defVal == "": # no default! | 305 | if info.defVal == "": # no default! |
| 294 | - fail("required option or argument missing: " & flag) | ||
| 295 | - flagvalues.add(@[flag,info.defVal]) | ||
| 296 | - | 306 | + failures.add("required option or argument missing: " & flag) |
| 307 | + else: | ||
| 308 | + flagvalues.add(@[flag,info.defVal]) | ||
| 309 | + | ||
| 310 | + if enableChecks: | ||
| 311 | + # any failures up until now - then we fail | ||
| 312 | + if failures.len > 0: | ||
| 313 | + fail(failures.join("\n") & "\n") | ||
| 314 | + | ||
| 297 | # cool, we have the info, can convert known flags | 315 | # cool, we have the info, can convert known flags |
| 298 | for item in flagvalues: | 316 | for item in flagvalues: |
| 299 | var pval: PValue; | 317 | var pval: PValue; |
| @@ -307,14 +325,14 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | @@ -307,14 +325,14 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | ||
| 307 | try: | 325 | try: |
| 308 | v = value.parseInt | 326 | v = value.parseInt |
| 309 | except: | 327 | except: |
| 310 | - fail("bad integer") | 328 | + fail("bad integer for " & flag) |
| 311 | pval = intValue(v) | 329 | pval = intValue(v) |
| 312 | of "float": | 330 | of "float": |
| 313 | var v: float | 331 | var v: float |
| 314 | try: | 332 | try: |
| 315 | v = value.parseFloat | 333 | v = value.parseFloat |
| 316 | except: | 334 | except: |
| 317 | - fail("bad integer") | 335 | + fail("bad float for " & flag) |
| 318 | pval = floatValue(v) | 336 | pval = floatValue(v) |
| 319 | of "bool": | 337 | of "bool": |
| 320 | pval = boolValue(value.parseBool) | 338 | pval = boolValue(value.parseBool) |
| @@ -324,12 +342,17 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | @@ -324,12 +342,17 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | ||
| 324 | var f: File | 342 | var f: File |
| 325 | try: | 343 | try: |
| 326 | if info.ptype == "infile": | 344 | if info.ptype == "infile": |
| 327 | - f = if value=="stdin": stdin else: open(value,fmRead) | 345 | + if value == "stdin": |
| 346 | + f = stdin | ||
| 347 | + else: | ||
| 348 | + f = open(value, fmRead) | ||
| 328 | else: | 349 | else: |
| 329 | - f = if value=="stdout": stdout else: open(value,fmWrite) | 350 | + if value == "stdout": |
| 351 | + f = stdout | ||
| 352 | + else: | ||
| 353 | + f = open(value, fmWrite) | ||
| 330 | # they will be closed automatically on program exit | 354 | # they will be closed automatically on program exit |
| 331 | - nfiles += 1 | ||
| 332 | - if nfiles <= MAX_FILES: files[nfiles] = f | 355 | + files.add(f) |
| 333 | except: | 356 | except: |
| 334 | fail("cannot open " & value) | 357 | fail("cannot open " & value) |
| 335 | pval = fileValue(f,value) | 358 | pval = fileValue(f,value) |
| @@ -357,23 +380,55 @@ proc parse*(usage: string): Table[string,PValue] = | @@ -357,23 +380,55 @@ proc parse*(usage: string): Table[string,PValue] = | ||
| 357 | args[i] = paramStr(i) | 380 | args[i] = paramStr(i) |
| 358 | return parseArguments(usage,args) | 381 | return parseArguments(usage,args) |
| 359 | 382 | ||
| 383 | +# Helper proc for verbosity level. | ||
| 384 | +proc verbosityLevel*(args: Table[string,PValue]): int = | ||
| 385 | + if args.hasKey("verbose"): | ||
| 386 | + let verbosity = args["verbose"].asSeq | ||
| 387 | + result = verbosity.len | ||
| 388 | + if not verbosity[0].asBool: | ||
| 389 | + result = 0 | ||
| 390 | + else: | ||
| 391 | + result = 0 | ||
| 392 | + | ||
| 393 | +# Helper to check if we should show version | ||
| 394 | +proc showVersion*(args: Table[string,PValue]): bool = | ||
| 395 | + args["version"].asBool | ||
| 396 | + | ||
| 397 | +# Helper to check if we should show help | ||
| 398 | +proc showHelp*(args: Table[string,PValue]): bool = | ||
| 399 | + args["help"].asBool | ||
| 400 | + | ||
| 401 | + | ||
| 402 | +# Typical usage | ||
| 360 | when isMainModule: | 403 | when isMainModule: |
| 361 | - var args = parse""" | ||
| 362 | - head [flags] filename | ||
| 363 | - -n: (default 10) number of lines | ||
| 364 | - -v,--verbose: (bool...) verbosity level | ||
| 365 | - -a,--alpha useless parm | ||
| 366 | - <file>: (default stdin...) | ||
| 367 | - |<out>: (default stdout) | 404 | + let help = """ |
| 405 | + head [flags] files... [out] | ||
| 406 | + -h,--help Show this help | ||
| 407 | + --version Show version | ||
| 408 | + -n: (default 10) Number of lines to show | ||
| 409 | + -v,--verbose: (bool...) Verbosity level, ignored | ||
| 410 | + -o,--out: (default stdout) Optional outfile, defaults to stdout | ||
| 411 | + <files>: (default stdin...) Files to take head of | ||
| 368 | """ | 412 | """ |
| 413 | + # On parsing failure this will show usage automatically | ||
| 414 | + var args = parse(help) | ||
| 369 | 415 | ||
| 370 | - echo args["n"].asInt | ||
| 371 | - echo args["alpha"].asBool | 416 | + # These two are special, they short out |
| 417 | + if args.showHelp: quit(help) | ||
| 418 | + if args.showVersion: quit("Version: 1.99") | ||
| 419 | + | ||
| 420 | + # Ok, so what did we get... | ||
| 421 | + let n = args["n"].asInt | ||
| 422 | + | ||
| 423 | + # This one is a helper | ||
| 424 | + let v = verbosityLevel(args) | ||
| 372 | 425 | ||
| 373 | - for v in args["verbose"].asSeq: | ||
| 374 | - echo "got ",v.asBool | 426 | + echo "Lines to show: " & $n |
| 427 | + echo "Verbosity level: " & $verbosityLevel(args) | ||
| 375 | 428 | ||
| 376 | let myfiles = args["files"].asSeq | 429 | let myfiles = args["files"].asSeq |
| 430 | + var outFile = args["out"].asFile | ||
| 431 | + | ||
| 377 | for f in myfiles: | 432 | for f in myfiles: |
| 378 | - echo f.asFile.readLine() | ||
| 379 | - | 433 | + for i in 1..n: |
| 434 | + writeln(outFile, string(f.asFile.readLine())) |