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 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()))