Commit 5e811526dad2ef339649fec64e147d17314cc0be

Authored by Göran Krampe
1 parent 85eedc50

Added updated lapp.nim

Showing 1 changed file with 98 additions and 43 deletions
lapp.nim
1 1 import strutils
2   -from os import paramCount, paramStr
  2 +import os
3 3 import tables
4 4 export tables.`[]`
5 5  
... ... @@ -50,10 +50,10 @@ proc get(L: PLexer; t: var TLexType): string =
50 50 next(L)
51 51 if thisChar(L) == '.':
52 52 t = tfloat
53   - result.add(c)
  53 + result.add(thisChar(L))
54 54 next(L)
55 55 while thisChar(L) in Digits:
56   - result.add(c)
  56 + result.add(thisChar(L))
57 57 next(L)
58 58 of '.': # ".", "..", "..."
59 59 if thisChar(L) == '.':
... ... @@ -89,17 +89,17 @@ type
89 89 vFile,
90 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 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 104 proc boolValue(c: bool): PValue = PValue(kind: vBool, asBool: c)
105 105  
... ... @@ -125,7 +125,7 @@ var
125 125 progname, usage: string
126 126 aliases: array[char,string]
127 127 parm_spec = initTable[string,PSpec]()
128   -
  128 +
129 129 proc fail(msg: string) =
130 130 stderr.writeln(progname & ": " & msg)
131 131 quit(usage)
... ... @@ -205,6 +205,7 @@ proc parseSpec(u: string) =
205 205 defValue = "false"
206 206  
207 207 if name != nil:
  208 + # echo("Param: " & name & " type: " & $ftype & " needsvalue: " & $(ftype != "bool") & " default: " & $defValue & " multiple: " & $multiple)
208 209 let spec = PSpec(defVal:defValue, ptype: ftype, needsValue: ftype != "bool",multiple:multiple)
209 210 aliases[alias] = name
210 211 parm_spec[name] = spec
... ... @@ -212,14 +213,13 @@ proc parseSpec(u: string) =
212 213 proc tail(s: string): string = s[1..(-1)]
213 214  
214 215 var
215   - files: array[1..MAX_FILES,File]
216   - nfiles = 0
  216 + files = newSeq[File]()
217 217  
218 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 223 var
224 224 vars = initTable[string,PValue]()
225 225 n = len(args) - 1
... ... @@ -253,6 +253,9 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] =
253 253 parseSpec(usage)
254 254 addQuitProc(closeFiles)
255 255  
  256 + # Collect failures
  257 + var failures = newSeq[string]()
  258 +
256 259 # parse the flags and arguments
257 260 while i <= n:
258 261 arg = next()
... ... @@ -275,7 +278,8 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] =
275 278 for c in arg.tail:
276 279 let f = get_alias(c)
277 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 283 flagvalues.add(@[f,"true"])
280 284 i.used = true
281 285 else: # argument (stored as \001, \002, etc
... ... @@ -286,14 +290,28 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] =
286 290 if not info.multiple: k += 1
287 291 flagvalues.add(@[flag,value])
288 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 302 # any flags not mentioned?
291 303 for flag,info in parm_spec:
292 304 if not info.used:
293 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 315 # cool, we have the info, can convert known flags
298 316 for item in flagvalues:
299 317 var pval: PValue;
... ... @@ -307,14 +325,14 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] =
307 325 try:
308 326 v = value.parseInt
309 327 except:
310   - fail("bad integer")
  328 + fail("bad integer for " & flag)
311 329 pval = intValue(v)
312 330 of "float":
313 331 var v: float
314 332 try:
315 333 v = value.parseFloat
316 334 except:
317   - fail("bad integer")
  335 + fail("bad float for " & flag)
318 336 pval = floatValue(v)
319 337 of "bool":
320 338 pval = boolValue(value.parseBool)
... ... @@ -324,12 +342,17 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] =
324 342 var f: File
325 343 try:
326 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 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 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 356 except:
334 357 fail("cannot open " & value)
335 358 pval = fileValue(f,value)
... ... @@ -357,23 +380,55 @@ proc parse*(usage: string): Table[string,PValue] =
357 380 args[i] = paramStr(i)
358 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 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 429 let myfiles = args["files"].asSeq
  430 + var outFile = args["out"].asFile
  431 +
377 432 for f in myfiles:
378   - echo f.asFile.readLine()
379   -
  433 + for i in 1..n:
  434 + writeln(outFile, string(f.asFile.readLine()))
... ...