nim-lang / RFCs

A repository for your Nim proposals.
137 stars 23 forks source link

Proc wrapper for `strdefine` #474

Closed metagn closed 2 years ago

metagn commented 2 years ago

Abstract

A getStrDefine (placeholder name) magic proc that gives the value of a define. Roughly const foo = getStrDefine(foo, default = "abc") is equal to const foo {.strdefine.} = "abc".

Motivation

The fact that strdefine is tied to constants can cause weird behavior. Its implementation is also pretty similar to a magic proc.

Using a name other than the constant name is easier here for the implementation ({.strdefine.} form would need https://github.com/nim-lang/Nim/pull/20115 to be merged).

The multiple hardcoded variants of strdefine, namely booldefine and intdefine, are also not needed in this form. One can do:

template getDefineAs[T](name: untyped, _: type T, default: T): T =
  when defined(name):
    T(getStrDefine(name, "unreachable"))
  else:
    default

const foo = getDefineAs(foo, bool, true)

One can also use custom converters this way rather than normal type conversion. This is not possible with booldefine/intdefine.

Description

An implementation could be:

# in system
proc getStrDefine*(name: untyped, defaultValue: string) {.magic: "StrDefine", noSideEffect, compileTime.}

We can leverage the already existing magic mStrDefine, normally only applicable to constants, for a proc here. In semMagic, we can add an of branch, something like:

of mStrDefine:
  markUsed(c, n.info, s)
  # move out as semStrDefine
  let name = considerQuotedIdentOrDot(c, n[1], n).s
  if isDefined(c.config, name):
    result = newStrNodeT(c.config.symbols[name], n, c)
  else:
    result = n[2]

Code Examples

From #181:

const bar = getStrDefine(myproj.mykey2, "foo2")

Backwards Compatibility

Overloading on the name getStrDefine might be affected.