-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
macro pragmas
in type section should apply to TypeSection
, not TypeDef
#13830
Comments
@timotheecour, you'll be surprised by the flexibility you are given with template makeType: type =
type
Foo = object
a: int
b: int
Bar = distinct Foo
template a*(obj: Bar): untyped = Foo(obj).a
template b*(obj: Bar): untyped = Foo(obj).b
Bar
type
MyType = makeType() |
yes but that's missing the point though, both routine pragmas and var pragmas allow complete rewrite of the AST (including omitting the declaration or arbitrary other rewrites), but that's not yet the case with type pragmas, but would be under this RFC. As for your approach, you can't express simple things like:
(at least not unless you also combine with type pragma, but then again it still won't help eg for All of this is possible with this RFC, and results in simpler user code, eg: type Foo {.maybeExport.} = Bar
{.push maybeExportTypeAndFields.}
type
Foo1 = Bar1
Foo2 = Bar1
{.pop.} |
My example was just meant to demonstrate how You can do a rename or add export marker by modifying the returned specialTypeSection:
type
Foo = ...
Bar = ...
EDIT: Maybe it's not clear from my explanation that the body of the template represents the |
@zah @Araq my experience with #14008 confirms exactly what I wrote in this RFC. with the proposed change change, we can write these interchangeably, the 1st form being more user friendly. type EnumWithHole {.enumMap.} = enum
k1=1
k2=3
enumMap:
type EnumWithHole = enum
k1=1
k2=3 with the current state of affair, this just isn't possible (please prove me wrong); (and even if it were possible, it'd require a complex rewrite of With proposed change, the rewrite rule is the exact analog of macro pragma for routines and var, instead of some weird thing that takes a broken piece of AST (a The very fact that you'd need to jump through hoops to make |
@Araq I had opened this issue right after type macro pragma was implemented in #13778 but I'm now adding this issue as a blocker for milestone 1.4 (I hope you don't mind) for the following reason:
|
I don't understand. The macro should apply to the ast where you're manipulating the type; ie. the |
Would |
you mean this? type
A = B
E = variant: # maybe we can skip the colon
... that syntax looks very foreign, and doesn't make much sense of EDIT: template m1(body): untyped {.typesection.} =
when not defined(js): body
type
Foo1 = object
Foo2 {.m1.} = object
Foo3 = object alternatively, via type system would be possible too: template m1(body: TypeSectionStmt): untyped =
when not defined(js): body
type
Foo1 = object
Foo2 {.m1.} = object
Foo3 = object and then user would choose: template m1(body: TypeSectionStmt): untyped =. # applies to TypeSection
template m2(body: TypeDefStmt): untyped = ... # applies to TypeDef => no breaking change |
This change would be useful for generating custom getters and setters for fields. |
I feel that that a "push" macro should just be passed the AST of the entire section it applies to - this offers the most flexibility, and would be a nice alternative to wrapping giant sections of code (for instance, an entire module) under a macro invocation. |
took a surprisingly long time to figure out how to attach procs to a type declaration. thanks to zah's comment on this github issue nim-lang/Nim#13830 (comment)
custom procs (getters,setters,constructors) can be generated with @zah's nnkStmtListType idea. here's a POC constructor generator: import macros,sugar
template helper(body:untyped):untyped =
body
macro dataclass*(x:untyped):untyped =
## dataclass macro replaces a typedef with itself, plus some
## helper procs. for now, just an initFoo() proc
## input is, e.g.
## type
## Foo {. pragmas.. .} = object
## x*:int
## y,z*,w: float
## TODO: variants
##
## output is:
## template anonymous:type =
## type Foo {. pragmas..,inject .} = object
## x*:int
## y,z*,w: float
## proc initFoo(x:int,y,z,w:float):auto = Foo(x:x,y:y,z:z,w:w)
## Foo
## type
## FooDataClass {. pragmas.. .} = anonymous()
result = x.copyNimTree()
x.expectKind(nnkTypeDef)
let basename = x[0]
let outname = basename.copyNimTree()
outname[0] = ident(outname[0].strval & "Dataclass")
#type BaseNameDataclass{. pragmalist.. .} = helper(templatebody)
basename[1].add(ident"inject")
# type BaseName{.inject.} = object
x[2].expectKind(nnkObjectTy)
let typedef = x[2]
let identdefs = typedef[2].copyNimTree()
var ids: seq[NimNode]
#get identdef, ident seqs, stripped of `*` postfix
for i in 0..<identdefs.len:
for j in 0..<identdefs[i].len - 2: #last two are type and i think pragma?
if identdefs[i][j].kind == nnkPostfix:
identdefs[i][j] = identdefs[i][j][1]
ids.add identdefs[i][j]
let params = @[ident"auto"] & collect(newSeq,for c in identdefs.children: c)
let assignments = @[basename[0]] & collect(newSeq, for i in ids: nnkExprColonExpr.newTree(i,i))
let procdef = newProc(ident("init" & basename[0].strval),
params,
nnkObjConstr.newTree( assignments )
)
let templatebody = nnkStmtList.newTree(
nnkTypeSection.newTree(
nnkTypeDef.newTree(
basename,
newEmptyNode(),
typedef
)
),
procdef,
basename[0]
)
result[0] = outname
result[2] = newCall(ident"helper",[templatebody])
type
Foo = int
Bar{.dataclass.} = object
x*: int
y,z*,w: float
Baz = float
let y = initBar(3,1.0,2.0,3.0)
echo y |
|
/cc @Araq
#13778 introduced
macro pragmas
in type sections. It's very useful as it allows things like "export all fields" in library code, or other custom logic.However the semantics are too restrictive because we're passing a
TypeDef
AST which is not representable in regular nim code, preventing many useful cases (eg using a template instead of a macro, discarding the typedef, adding a statement before the type etc, all of which are possible with pragmas for other declarations (including routines and let statements)).It should be changed to something very similar but much more useful, see "Possible Solution" below.
In particular, pure forwarding via
template m2(def: untyped): untyped = def
works for other declarations:but not for type sections.
Example
Current Output
Expected Output
should work for m2, m3, m6, m7, m8, m9
(m4, m5 was just to verify this doesn't work either)
Possible Solution: pass the
TypeSection
, not theTypeDef
TypeSection
AST should be passed to the macro (or template), instead of passing theTypeDef
. This will make all examples passm2, m3, m6, m7, m8, m9
+m0,m1
TypeSection
, it's elements are inserted in the parent type section:TypeSection
, we could either issue aError: illformed AST
(simplest) or split the type section accordingly, eg:Additional Information
The text was updated successfully, but these errors were encountered: