iffy / nim-argparse

Argument parsing for Nim
MIT License
120 stars 8 forks source link

`Error: undeclared identifier: 'opts'` when prologue is imported #81

Closed cmd410 closed 1 year ago

cmd410 commented 1 year ago

I encountered a weird bug. Consider the following code:

import os

import argparse
import prologue

var p = newParser:
  flag("-v", "--version", help="Print version info and quit")
  run:
    if opts.version:
      echo "0.1.0"
      quit(0)
    echo "work"

try:
  p.run(commandLineParams())
except UsageError:
  stderr.writeLine getCurrentExceptionMsg()
  quit(1)

This does not compile due to Error: undeclared identifier: 'opts'. The strange part is, this error only occurs when prologue is imported. If you comment out import prologue the code works fine. Not sure what to make of it :/

$ nim --version
Nim Compiler Version 1.6.6 [Windows: amd64]
Compiled at 2022-05-05
Copyright (c) 2006-2021 by Andreas Rumpf

active boot switches: -d:release

argparse version: 3.0.0 prologue version: 0.6.0

iffy commented 1 year ago

That's weird, but I've seen errors like that before. Can you run it with expandMacros added and paste the output?

...
import macros
expandMacros:
  var p = newParser:
...
cmd410 commented 1 year ago

It wouldn't compile with opts, so i removed if clause

import argparse
import prologue
import macros

expandMacros:
  var p = newParser:
    run:
      echo "work"

and it can't compile either with error:

Error: type mismatch: got <void>
but expected one of:
proc echo(x: varargs[typed, `$`])
  first type mismatch at position: 1
  required type for x: varargs[typed]
  but expression '["work"]' is of type: void

expression: echo ["work"]

The only compileable code i got is:

import argparse
import prologue
import macros

expandMacros:
  var p = newParser:
    run:
      discard

the output is


var p =
  macro domkParser(): untyped =
    result =
      let builder`gensym1 = addParser("", "", proc () =
        add_runProc(replaceNodes(getAst(`:anonymous`(newIdentNode("result"))))))
      generateDefs(builder`gensym1)

  type
    OptsArgparse_436207631 = object

    ParserArgparse_436207631 = object
  proc help(parser: ParserArgparse_436207631): string {.used.} =
    var prog = ""
    if prog == "":
      prog = extractFilename(getAppFilename())
    add(result, replace("{prog}\n\nUsage:\n   [options] \n\nOptions:\n  -h, --help\n",
                        "{prog}", prog))

  proc parse(parser: ParserArgparse_436207631; opts: ref OptsArgparse_436207631;
             state: ref ParseState; runblocks = false; quitOnHelp = true;
             output: Stream = ARGPARSE_STDOUT) {.used.} =
    try:
      var switches_seen: `[]`(seq, string)
      proc takeArgsFromExtra(opts: ref OptsArgparse_436207631;
                             state: ref ParseState) =

      add(state.runProcs, proc () = discard )
      var argCount = 0
      var argsTaken = false
      var doneProcessingFlags = false
      while not state.done:
        let token = get(state.token)
        if not doneProcessingFlags:
          case token
          of "-h", "--help":
            raiseShortCircuit("argparse_help")
          else:
            discard
          let key = get(state.key)
          discard
        if (
          0 <= len(state.extra)):
          discard
        if token == "--":
          doneProcessingFlags = true
          consume(state, ArgArgument)
          continue
        skip(state)
      if not argsTaken:
        takeArgsFromExtra(opts, state)
      if (
        0 < len(state.extra)):
        raise
          (ref UsageError)(msg: "Unknown argument(s): " &
              join(state.extra, ", "), parent: nil)
      if runblocks:
        for p in items(state.runProcs):
          p()
    except ShortCircuit as e:
      if e.flag == "argparse_help" and runblocks:
        write(output, help(parser))
        if quitOnHelp:
          quit(1)
      else:
        raise e

  proc parse(parser: ParserArgparse_436207631; args: `[]`(seq, string);
             quitOnHelp = true): ref OptsArgparse_436207631 {.used.} =
    var state = newParseState(args)
    var opts: ref OptsArgparse_436207631
    new(opts)
    parse(parser, opts, state, false, quitOnHelp, ARGPARSE_STDOUT)
    result = opts

  proc parse(parser: ParserArgparse_436207631; quitOnHelp = true): ref OptsArgparse_436207631 {.
      used.} =
    result =
      parse(parser,
        type
          OutType`gensym23 = typeof(items(commandLineParams()))
        block:
          let :tmp_436208019 = commandLineParams()
          template s2_436208020(): untyped =
            :tmp_436208019

          var i`gensym23 = 0
          var result`gensym23 = newSeq(len(:tmp_436208019))
          for it`gensym23 in items(:tmp_436208019):
            result`gensym23[i`gensym23] = it`gensym23
            i`gensym23 += 1
          result`gensym23, quitOnHelp)

  proc run(parser: ParserArgparse_436207631; args: `[]`(seq, string);
           quitOnHelp = true; output: Stream = ARGPARSE_STDOUT) {.used.} =
    var state = newParseState(args)
    var opts: ref OptsArgparse_436207631
    new(opts)
    parse(parser, opts, state, true, quitOnHelp, output)

  proc run(parser: ParserArgparse_436207631) {.used.} =
    run(parser,
      type
        OutType`gensym30 = typeof(items(commandLineParams()))
      block:
        let :tmp_436208108 = commandLineParams()
        template s2_436208109(): untyped =
          :tmp_436208108

        var i`gensym30 = 0
        var result`gensym30 = newSeq(len(:tmp_436208108))
        for it`gensym30 in items(:tmp_436208108):
          result`gensym30[i`gensym30] = it`gensym30
          i`gensym30 += 1
        result`gensym30, true, ARGPARSE_STDOUT)

  var parser`gensym12 = ParserArgparse_436207631()
  parser`gensym12
E:\...\test.nim(7, 1) template/generic instantiation of `expandMacros` from here
C:\Users\user\.nimble\pkgs\argparse-3.0.0\argparse.nim(141, 3) Error: illformed AST: macro domkParser(): untyped =
  result =
    let builder`gensym1 = addParser("", "", proc () =
      add_runProc(replaceNodes(getAst(`:anonymous`(newIdentNode("result"))))))
    generateDefs(builder`gensym1)
iffy commented 1 year ago

Sorry it took me so long, but you can fix this by using argparse.run: instead of run:. Please reopen if that doesn't work for you!

import os

import argparse
import prologue

var p = newParser:
  flag("-v", "--version", help="Print version info and quit")
  argparse.run:
    if opts.version:
      echo "0.1.0"
      quit(0)
    echo "work"

try:
  p.run(commandLineParams())
except UsageError:
  stderr.writeLine getCurrentExceptionMsg()
  quit(1)