language change: change how the experimental dot operators work

This commit is contained in:
Andreas Rumpf 2017-11-29 13:31:31 +01:00
commit 33814cf63e
5 changed files with 25 additions and 19 deletions

View file

@ -111,3 +111,9 @@ This now needs to be written as:
- ``strutils.split`` and ``strutils.rsplit`` with an empty string and a - ``strutils.split`` and ``strutils.rsplit`` with an empty string and a
separator now returns that empty string. separator now returns that empty string.
See issue [#4377](https://github.com/nim-lang/Nim/issues/4377). See issue [#4377](https://github.com/nim-lang/Nim/issues/4377).
- The experimental overloading of the dot ``.`` operators now take
an ``untyped``` parameter as the field name, it used to be
a ``static[string]``. You can use ``when defined(nimNewDot)`` to make
your code work with both old and new Nim versions.
See [special-operators](https://nim-lang.org/docs/manual.html#special-operators)
for more information.

View file

@ -111,3 +111,4 @@ proc initDefines*() =
defineSymbol("nimNoArrayToCstringConversion") defineSymbol("nimNoArrayToCstringConversion")
defineSymbol("nimNewRoof") defineSymbol("nimNewRoof")
defineSymbol("nimHasRunnableExamples") defineSymbol("nimHasRunnableExamples")
defineSymbol("nimNewDot")

View file

@ -235,12 +235,11 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
if nfDotField in n.flags: if nfDotField in n.flags:
internalAssert f.kind == nkIdent and n.sonsLen >= 2 internalAssert f.kind == nkIdent and n.sonsLen >= 2
let calleeName = newStrNode(nkStrLit, f.ident.s).withInfo(n.info)
# leave the op head symbol empty, # leave the op head symbol empty,
# we are going to try multiple variants # we are going to try multiple variants
n.sons[0..1] = [nil, n[1], calleeName] n.sons[0..1] = [nil, n[1], f]
orig.sons[0..1] = [nil, orig[1], calleeName] orig.sons[0..1] = [nil, orig[1], f]
template tryOp(x) = template tryOp(x) =
let op = newIdentNode(getIdent(x), n.info) let op = newIdentNode(getIdent(x), n.info)
@ -255,8 +254,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
tryOp "." tryOp "."
elif nfDotSetter in n.flags and f.kind == nkIdent and n.len == 3: elif nfDotSetter in n.flags and f.kind == nkIdent and n.len == 3:
let calleeName = newStrNode(nkStrLit, # we need to strip away the trailing '=' here:
f.ident.s[0..f.ident.s.len-2]).withInfo(n.info) let calleeName = newIdentNode(getIdent(f.ident.s[0..f.ident.s.len-2]), n.info)
let callOp = newIdentNode(getIdent".=", n.info) let callOp = newIdentNode(getIdent".=", n.info)
n.sons[0..1] = [callOp, n[1], calleeName] n.sons[0..1] = [callOp, n[1], calleeName]
orig.sons[0..1] = [callOp, orig[1], calleeName] orig.sons[0..1] = [callOp, orig[1], calleeName]

View file

@ -17,8 +17,8 @@ or dynamic file formats such as JSON or XML.
When Nim encounters an expression that cannot be resolved by the When Nim encounters an expression that cannot be resolved by the
standard overload resolution rules, the current scope will be searched standard overload resolution rules, the current scope will be searched
for a dot operator that can be matched against a re-written form of for a dot operator that can be matched against a re-written form of
the expression, where the unknown field or proc name is converted to the expression, where the unknown field or proc name is passed to
an additional static string parameter: an ``untyped`` parameter:
.. code-block:: nim .. code-block:: nim
a.b # becomes `.`(a, "b") a.b # becomes `.`(a, "b")
@ -28,7 +28,7 @@ The matched dot operators can be symbols of any callable kind (procs,
templates and macros), depending on the desired effect: templates and macros), depending on the desired effect:
.. code-block:: nim .. code-block:: nim
proc `.` (js: PJsonNode, field: string): JSON = js[field] template `.` (js: PJsonNode, field: untyped): JSON = js[astToStr(field)]
var js = parseJson("{ x: 1, y: 2}") var js = parseJson("{ x: 1, y: 2}")
echo js.x # outputs 1 echo js.x # outputs 1

View file

@ -177,7 +177,7 @@ proc `==`*(x, y: JsRoot): bool {. importcpp: "(# === #)" .}
## and not strings or numbers, this is a *comparison of references*. ## and not strings or numbers, this is a *comparison of references*.
{. experimental .} {. experimental .}
macro `.`*(obj: JsObject, field: static[cstring]): JsObject = macro `.`*(obj: JsObject, field: untyped): JsObject =
## Experimental dot accessor (get) for type JsObject. ## Experimental dot accessor (get) for type JsObject.
## Returns the value of a property of name `field` from a JsObject `x`. ## Returns the value of a property of name `field` from a JsObject `x`.
## ##
@ -196,14 +196,14 @@ macro `.`*(obj: JsObject, field: static[cstring]): JsObject =
helper(`obj`) helper(`obj`)
else: else:
if not mangledNames.hasKey($field): if not mangledNames.hasKey($field):
mangledNames[$field] = $mangleJsName(field) mangledNames[$field] = $mangleJsName($field)
let importString = "#." & mangledNames[$field] let importString = "#." & mangledNames[$field]
result = quote do: result = quote do:
proc helper(o: JsObject): JsObject proc helper(o: JsObject): JsObject
{. importcpp: `importString`, gensym .} {. importcpp: `importString`, gensym .}
helper(`obj`) helper(`obj`)
macro `.=`*(obj: JsObject, field: static[cstring], value: untyped): untyped = macro `.=`*(obj: JsObject, field, value: untyped): untyped =
## Experimental dot accessor (set) for type JsObject. ## Experimental dot accessor (set) for type JsObject.
## Sets the value of a property of name `field` in a JsObject `x` to `value`. ## Sets the value of a property of name `field` in a JsObject `x` to `value`.
if validJsName($field): if validJsName($field):
@ -214,7 +214,7 @@ macro `.=`*(obj: JsObject, field: static[cstring], value: untyped): untyped =
helper(`obj`, `value`) helper(`obj`, `value`)
else: else:
if not mangledNames.hasKey($field): if not mangledNames.hasKey($field):
mangledNames[$field] = $mangleJsName(field) mangledNames[$field] = $mangleJsName($field)
let importString = "#." & mangledNames[$field] & " = #" let importString = "#." & mangledNames[$field] & " = #"
result = quote do: result = quote do:
proc helper(o: JsObject, v: auto) proc helper(o: JsObject, v: auto)
@ -222,7 +222,7 @@ macro `.=`*(obj: JsObject, field: static[cstring], value: untyped): untyped =
helper(`obj`, `value`) helper(`obj`, `value`)
macro `.()`*(obj: JsObject, macro `.()`*(obj: JsObject,
field: static[cstring], field: untyped,
args: varargs[JsObject, jsFromAst]): JsObject = args: varargs[JsObject, jsFromAst]): JsObject =
## Experimental "method call" operator for type JsObject. ## Experimental "method call" operator for type JsObject.
## Takes the name of a method of the JavaScript object (`field`) and calls ## Takes the name of a method of the JavaScript object (`field`) and calls
@ -245,7 +245,7 @@ macro `.()`*(obj: JsObject,
importString = "#." & $field & "(@)" importString = "#." & $field & "(@)"
else: else:
if not mangledNames.hasKey($field): if not mangledNames.hasKey($field):
mangledNames[$field] = $mangleJsName(field) mangledNames[$field] = $mangleJsName($field)
importString = "#." & mangledNames[$field] & "(@)" importString = "#." & mangledNames[$field] & "(@)"
result = quote: result = quote:
proc helper(o: JsObject): JsObject proc helper(o: JsObject): JsObject
@ -257,7 +257,7 @@ macro `.()`*(obj: JsObject,
result[1].add args[idx].copyNimTree result[1].add args[idx].copyNimTree
macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V], macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V],
field: static[cstring]): V = field: untyped): V =
## Experimental dot accessor (get) for type JsAssoc. ## Experimental dot accessor (get) for type JsAssoc.
## Returns the value of a property of name `field` from a JsObject `x`. ## Returns the value of a property of name `field` from a JsObject `x`.
var importString: string var importString: string
@ -265,7 +265,7 @@ macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V],
importString = "#." & $field importString = "#." & $field
else: else:
if not mangledNames.hasKey($field): if not mangledNames.hasKey($field):
mangledNames[$field] = $mangleJsName(field) mangledNames[$field] = $mangleJsName($field)
importString = "#." & mangledNames[$field] importString = "#." & mangledNames[$field]
result = quote do: result = quote do:
proc helper(o: type(`obj`)): `obj`.V proc helper(o: type(`obj`)): `obj`.V
@ -273,7 +273,7 @@ macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V],
helper(`obj`) helper(`obj`)
macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V], macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V],
field: static[cstring], field: untyped,
value: V): untyped = value: V): untyped =
## Experimental dot accessor (set) for type JsAssoc. ## Experimental dot accessor (set) for type JsAssoc.
## Sets the value of a property of name `field` in a JsObject `x` to `value`. ## Sets the value of a property of name `field` in a JsObject `x` to `value`.
@ -282,7 +282,7 @@ macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V],
importString = "#." & $field & " = #" importString = "#." & $field & " = #"
else: else:
if not mangledNames.hasKey($field): if not mangledNames.hasKey($field):
mangledNames[$field] = $mangleJsName(field) mangledNames[$field] = $mangleJsName($field)
importString = "#." & mangledNames[$field] & " = #" importString = "#." & mangledNames[$field] & " = #"
result = quote do: result = quote do:
proc helper(o: type(`obj`), v: `obj`.V) proc helper(o: type(`obj`), v: `obj`.V)
@ -290,7 +290,7 @@ macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V],
helper(`obj`, `value`) helper(`obj`, `value`)
macro `.()`*[K: string | cstring, V: proc](obj: JsAssoc[K, V], macro `.()`*[K: string | cstring, V: proc](obj: JsAssoc[K, V],
field: static[cstring], field: untyped,
args: varargs[untyped]): auto = args: varargs[untyped]): auto =
## Experimental "method call" operator for type JsAssoc. ## Experimental "method call" operator for type JsAssoc.
## Takes the name of a method of the JavaScript object (`field`) and calls ## Takes the name of a method of the JavaScript object (`field`) and calls