From ed4a267399ed6420d979b4594f60edb698229084 Mon Sep 17 00:00:00 2001 From: hlaaf <hlaaftana@users.noreply.github.com> Date: Tue, 14 Aug 2018 01:33:56 +0300 Subject: Small documentation typo in math --- lib/pure/math.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/pure') diff --git a/lib/pure/math.nim b/lib/pure/math.nim index eb09bf4a7..641f07c43 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -529,8 +529,8 @@ proc sgn*[T: SomeNumber](x: T): int {.inline.} = {.pop.} proc `^`*[T](x: T, y: Natural): T = - ## Computes ``x`` to the power ``y`. ``x`` must be non-negative, use - ## `pow <#pow,float,float>` for negative exponents. + ## Computes ``x`` to the power ``y``. ``x`` must be non-negative, use + ## `pow <#pow,float,float>`_ for negative exponents. when compiles(y >= T(0)): assert y >= T(0) else: -- cgit 1.4.1-2-gfad0 From feda366d86e723ff1877a1751cb3eadde8b00cb0 Mon Sep 17 00:00:00 2001 From: Dominik Picheta <dominikpicheta@googlemail.com> Date: Tue, 14 Aug 2018 00:09:08 +0100 Subject: Adds get for Option[T]. (#8462) --- lib/pure/options.nim | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'lib/pure') diff --git a/lib/pure/options.nim b/lib/pure/options.nim index ce58943f9..b547c722a 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -129,7 +129,7 @@ proc get*[T](self: Option[T]): T = ## Returns contents of the Option. If it is none, then an exception is ## thrown. if self.isNone: - raise UnpackError(msg : "Can't obtain a value from a `none`") + raise UnpackError(msg: "Can't obtain a value from a `none`") self.val proc get*[T](self: Option[T], otherwise: T): T = @@ -139,6 +139,13 @@ proc get*[T](self: Option[T], otherwise: T): T = else: otherwise +proc get*[T](self: var Option[T]): var T = + ## Returns contents of the Option. If it is none, then an exception is + ## thrown. + if self.isNone: + raise UnpackError(msg: "Can't obtain a value from a `none`") + return self.val + proc map*[T](self: Option[T], callback: proc (input: T)) = ## Applies a callback to the value in this Option if self.isSome: -- cgit 1.4.1-2-gfad0 From 7ef268274f387530b85f5a9f704e0eda5a7aa022 Mon Sep 17 00:00:00 2001 From: alaviss <alaviss@users.noreply.github.com> Date: Tue, 14 Aug 2018 14:35:07 +0700 Subject: Haiku support for Nim (#8542) * posix_other: Haiku now has spawn.h This is added per https://dev.haiku-os.org/ticket/13446 * posix_other: Add Haiku specific Dirent members * cpuinfo: Add an implementation for Haiku * distros: Add basic Haiku support * encodings: update Haiku support * fenv, math: Haiku now provides libm * times: Add Haiku struct members * ansi_c, osalloc: Add Haiku constants * threads: Add Haiku support * testament: Haiku uses LIBRARY_PATH * nim.cfg: Update Haiku support libnetwork should only be linked if network functions are used * threads: Haiku does not support -pthread switch * tworkingdir: Haiku's env is in /bin * posix_other: add SIGKILLTHR for Haiku * sockets: link with libnetwork on Haiku * coro: correct ucontext.h location http://pubs.opengroup.org/onlinepubs/009696699/basedefs/ucontext.h.html * coro: ucontext backend is not available on Haiku Haiku doesn't provide the <ucontext.h> header, as it was removed from POSIX * coro: fix setjmp backend The compiler does not allow statements after a noreturn function * nativesockets: Haiku doesn't support AI_V4MAPPED * system: hostOS can contains "haiku" * os: add support for Haiku's packagefs packagefs is read-only, but there are writable holes to the underlying file system as well * os: update constant for Haiku --- config/nim.cfg | 16 +++++++++------- lib/deprecated/pure/sockets.nim | 2 ++ lib/posix/posix_other.nim | 12 +++++++++++- lib/pure/concurrency/cpuinfo.nim | 12 ++++++++++++ lib/pure/coro.nim | 18 +++++++++++------- lib/pure/distros.nim | 6 ++++++ lib/pure/encodings.nim | 4 +++- lib/pure/fenv.nim | 2 +- lib/pure/math.nim | 2 +- lib/pure/nativesockets.nim | 5 +++-- lib/pure/os.nim | 13 ++++++++++++- lib/pure/times.nim | 2 +- lib/system.nim | 2 +- lib/system/ansi_c.nim | 11 +++++++++++ lib/system/osalloc.nim | 3 +++ lib/system/threads.nim | 16 ++++++++++++++-- tests/osproc/texitsignal.nim | 5 ++++- tests/osproc/tworkingdir.nim | 2 ++ tests/testament/categories.nim | 10 +++++++--- 19 files changed, 114 insertions(+), 29 deletions(-) (limited to 'lib/pure') diff --git a/config/nim.cfg b/config/nim.cfg index c30190a18..af9df9c55 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -82,19 +82,21 @@ path="$lib/pure" clang.cpp.options.linker = "-ldl" tcc.options.linker = "-ldl" @end - @if bsd or haiku: + @if bsd: # BSD got posix_spawn only recently, so we deactivate it for osproc: define:useFork # at least NetBSD has problems with thread local storage: tlsEmulation:on @end @if haiku: - # -fopenmp - gcc.options.linker = "-lroot -lnetwork" - gcc.cpp.options.linker = "-lroot -lnetwork" - clang.options.linker = "-lroot -lnetwork" - clang.cpp.options.linker = "-lroot -lnetwork" - tcc.options.linker = "-lroot -lnetwork" + # Haiku currently have problems with TLS + # https://dev.haiku-os.org/ticket/14342 + tlsEmulation:on + gcc.options.linker = "-Wl,--as-needed -lnetwork" + gcc.cpp.options.linker = "-Wl,--as-needed -lnetwork" + clang.options.linker = "-Wl,--as-needed -lnetwork" + clang.cpp.options.linker = "-Wl,--as-needed -lnetwork" + tcc.options.linker = "-Wl,--as-needed -lnetwork" @end @end diff --git a/lib/deprecated/pure/sockets.nim b/lib/deprecated/pure/sockets.nim index 05aebef76..76a9044d8 100644 --- a/lib/deprecated/pure/sockets.nim +++ b/lib/deprecated/pure/sockets.nim @@ -36,6 +36,8 @@ include "system/inclrtl" when hostOS == "solaris": {.passl: "-lsocket -lnsl".} +elif hostOS == "haiku": + {.passl: "-lnetwork".} import os, parseutils from times import epochTime diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim index b7570bd15..99d67824e 100644 --- a/lib/posix/posix_other.nim +++ b/lib/posix/posix_other.nim @@ -10,7 +10,7 @@ {.deadCodeElim: on.} # dce option deprecated const - hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays + hasSpawnH = true # should exist for every Posix system nowadays hasAioH = defined(linux) when defined(linux) and not defined(android): @@ -43,6 +43,9 @@ type Dirent* {.importc: "struct dirent", header: "<dirent.h>", final, pure.} = object ## dirent_t struct + when defined(haiku): + d_dev*: Dev ## Device (not POSIX) + d_pdev*: Dev ## Parent device (only for queries) (not POSIX) d_ino*: Ino ## File serial number. when defined(dragonfly): # DragonflyBSD doesn't have `d_reclen` field. @@ -54,6 +57,9 @@ type ## (not POSIX) when defined(linux) or defined(openbsd): d_off*: Off ## Not an offset. Value that ``telldir()`` would return. + elif defined(haiku): + d_pino*: Ino ## Parent inode (only for queries) (not POSIX) + d_reclen*: cushort ## Length of this record. (not POSIX) d_name*: array[0..255, char] ## Name of entry. @@ -599,6 +605,10 @@ else: MSG_NOSIGNAL* {.importc, header: "<sys/socket.h>".}: cint ## No SIGPIPE generated when an attempt to send is made on a stream-oriented socket that is no longer connected. +when defined(haiku): + const + SIGKILLTHR* = 21 ## BeOS specific: Kill just the thread, not team + when hasSpawnH: when defined(linux): # better be safe than sorry; Linux has this flag, macosx doesn't, don't diff --git a/lib/pure/concurrency/cpuinfo.nim b/lib/pure/concurrency/cpuinfo.nim index 6d41aa1b2..541265da9 100644 --- a/lib/pure/concurrency/cpuinfo.nim +++ b/lib/pure/concurrency/cpuinfo.nim @@ -43,6 +43,14 @@ when defined(genode): proc affinitySpaceTotal(env: GenodeEnvPtr): cuint {. importcpp: "@->cpu().affinity_space().total()".} +when defined(haiku): + {.emit: "#include <OS.h>".} + type + SystemInfo {.importc: "system_info", bycopy.} = object + cpuCount {.importc: "cpu_count".}: uint32 + + proc getSystemInfo(info: ptr SystemInfo): int32 {.importc: "get_system_info".} + proc countProcessors*(): int {.rtl, extern: "ncpi$1".} = ## returns the numer of the processors/cores the machine has. ## Returns 0 if it cannot be detected. @@ -86,6 +94,10 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} = result = sysconf(SC_NPROC_ONLN) elif defined(genode): result = runtimeEnv.affinitySpaceTotal().int + elif defined(haiku): + var sysinfo: SystemInfo + if getSystemInfo(addr sysinfo) == 0: + result = sysinfo.cpuCount.int else: result = sysconf(SC_NPROCESSORS_ONLN) if result <= 0: result = 0 diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim index b6ef30e7c..6d7dcf078 100644 --- a/lib/pure/coro.nim +++ b/lib/pure/coro.nim @@ -43,6 +43,10 @@ when defined(windows): {.warning: "ucontext coroutine backend is not available on windows, defaulting to fibers.".} when defined(nimCoroutinesSetjmp): {.warning: "setjmp coroutine backend is not available on windows, defaulting to fibers.".} +elif defined(haiku): + const coroBackend = CORO_BACKEND_SETJMP + when defined(nimCoroutinesUcontext): + {.warning: "ucontext coroutine backend is not available on haiku, defaulting to setjmp".} elif defined(nimCoroutinesSetjmp) or defined(nimCoroutinesSetjmpBundled): const coroBackend = CORO_BACKEND_SETJMP else: @@ -55,21 +59,21 @@ when coroBackend == CORO_BACKEND_FIBERS: elif coroBackend == CORO_BACKEND_UCONTEXT: type - stack_t {.importc, header: "<sys/ucontext.h>".} = object + stack_t {.importc, header: "<ucontext.h>".} = object ss_sp: pointer ss_flags: int ss_size: int - ucontext_t {.importc, header: "<sys/ucontext.h>".} = object + ucontext_t {.importc, header: "<ucontext.h>".} = object uc_link: ptr ucontext_t uc_stack: stack_t Context = ucontext_t - proc getcontext(context: var ucontext_t): int32 {.importc, header: "<sys/ucontext.h>".} - proc setcontext(context: var ucontext_t): int32 {.importc, header: "<sys/ucontext.h>".} - proc swapcontext(fromCtx, toCtx: var ucontext_t): int32 {.importc, header: "<sys/ucontext.h>".} - proc makecontext(context: var ucontext_t, fn: pointer, argc: int32) {.importc, header: "<sys/ucontext.h>", varargs.} + proc getcontext(context: var ucontext_t): int32 {.importc, header: "<ucontext.h>".} + proc setcontext(context: var ucontext_t): int32 {.importc, header: "<ucontext.h>".} + proc swapcontext(fromCtx, toCtx: var ucontext_t): int32 {.importc, header: "<ucontext.h>".} + proc makecontext(context: var ucontext_t, fn: pointer, argc: int32) {.importc, header: "<ucontext.h>", varargs.} elif coroBackend == CORO_BACKEND_SETJMP: proc coroExecWithStack*(fn: pointer, stack: pointer) {.noreturn, importc: "narch_$1", fastcall.} @@ -190,7 +194,7 @@ proc switchTo(current, to: CoroutinePtr) = elif to.state == CORO_CREATED: # Coroutine is started. coroExecWithStack(runCurrentTask, to.stack.bottom) - doAssert false + #doAssert false else: {.error: "Invalid coroutine backend set.".} # Execution was just resumed. Restore frame information and set active stack. diff --git a/lib/pure/distros.nim b/lib/pure/distros.nim index 0adba5b1e..5847cfadb 100644 --- a/lib/pure/distros.nim +++ b/lib/pure/distros.nim @@ -126,6 +126,8 @@ type OpenBSD DragonFlyBSD + Haiku + const LacksDevPackages* = {Distribution.Gentoo, Distribution.Slackware, @@ -166,6 +168,8 @@ proc detectOsImpl(d: Distribution): bool = of Distribution.Solaris: let uname = toLowerAscii(uname()) result = ("sun" in uname) or ("solaris" in uname) + of Distribution.Haiku: + result = defined(haiku) else: let dd = toLowerAscii($d) result = dd in toLowerAscii(uname()) or dd in toLowerAscii(release()) @@ -224,6 +228,8 @@ proc foreignDepInstallCmd*(foreignPackageName: string): (string, bool) = result = ("pacman -S " & p, true) else: result = ("<your package manager here> install " & p, true) + elif defined(haiku): + result = ("pkgman install " & p, true) else: result = ("brew install " & p, false) diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim index 3c1cf73f4..2039a31be 100644 --- a/lib/pure/encodings.nim +++ b/lib/pure/encodings.nim @@ -255,7 +255,7 @@ when defined(windows): else: when defined(haiku): - const iconvDll = "(libc.so.6|libiconv.so|libtextencoding.so)" + const iconvDll = "libiconv.so" elif defined(macosx): const iconvDll = "libiconv.dylib" else: @@ -272,6 +272,8 @@ else: const EILSEQ = 86.cint elif defined(solaris): const EILSEQ = 88.cint + elif defined(haiku): + const EILSEQ = -2147454938.cint var errno {.importc, header: "<errno.h>".}: cint diff --git a/lib/pure/fenv.nim b/lib/pure/fenv.nim index c946c4261..0725973ca 100644 --- a/lib/pure/fenv.nim +++ b/lib/pure/fenv.nim @@ -12,7 +12,7 @@ {.deadCodeElim: on.} # dce option deprecated -when defined(Posix) and not defined(haiku): +when defined(Posix): {.passl: "-lm".} var diff --git a/lib/pure/math.nim b/lib/pure/math.nim index eb09bf4a7..785d4c183 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -49,7 +49,7 @@ proc fac*(n: int): int = {.push checks:off, line_dir:off, stack_trace:off.} -when defined(Posix) and not defined(haiku): +when defined(Posix): {.passl: "-lm".} const diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 5545ca2d1..d5fb0f89b 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -248,9 +248,10 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET, hints.ai_socktype = toInt(sockType) hints.ai_protocol = toInt(protocol) # OpenBSD doesn't support AI_V4MAPPED and doesn't define the macro AI_V4MAPPED. - # FreeBSD doesn't support AI_V4MAPPED but defines the macro. + # FreeBSD, Haiku don't support AI_V4MAPPED but defines the macro. # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198092 - when not defined(freebsd) and not defined(openbsd) and not defined(netbsd) and not defined(android): + # https://dev.haiku-os.org/ticket/14323 + when not defined(freebsd) and not defined(openbsd) and not defined(netbsd) and not defined(android) and not defined(haiku): if domain == AF_INET6: hints.ai_flags = AI_V4MAPPED var gaiResult = getaddrinfo(address, $port, addr(hints), result) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 9cc83c372..c05e33e31 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -615,7 +615,10 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1", when not declared(ENOENT) and not defined(Windows): when NoFakeVars: - const ENOENT = cint(2) # 2 on most systems including Solaris + when not defined(haiku): + const ENOENT = cint(2) # 2 on most systems including Solaris + else: + const ENOENT = cint(-2147459069) else: var ENOENT {.importc, header: "<errno.h>".}: cint @@ -972,6 +975,14 @@ proc rawCreateDir(dir: string): bool = result = false else: raiseOSError(osLastError(), dir) + elif defined(haiku): + let res = mkdir(dir, 0o777) + if res == 0'i32: + result = true + elif errno == EEXIST or errno == EROFS: + result = false + else: + raiseOSError(osLastError(), dir) elif defined(posix): let res = mkdir(dir, 0o777) if res == 0'i32: diff --git a/lib/pure/times.nim b/lib/pure/times.nim index cdb7a4466..073216e22 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -892,7 +892,7 @@ else: weekday {.importc: "tm_wday".}, yearday {.importc: "tm_yday".}, isdst {.importc: "tm_isdst".}: cint - when defined(linux) and defined(amd64): + when defined(linux) and defined(amd64) or defined(haiku): gmtoff {.importc: "tm_gmtoff".}: clong zone {.importc: "tm_zone".}: cstring type diff --git a/lib/system.nim b/lib/system.nim index 53605f9fd..de6e96611 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1365,7 +1365,7 @@ const hostOS* {.magic: "HostOS".}: string = "" ## a string that describes the host operating system. Possible values: ## "windows", "macosx", "linux", "netbsd", "freebsd", "openbsd", "solaris", - ## "aix", "standalone". + ## "aix", "haiku", "standalone". hostCPU* {.magic: "HostCPU".}: string = "" ## a string that describes the host CPU. Possible values: diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index f593d4c99..c6b89afe1 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -59,6 +59,15 @@ elif defined(macosx) or defined(linux) or defined(freebsd) or SIGSEGV = cint(11) SIGTERM = cint(15) SIGPIPE = cint(13) +elif defined(haiku): + const + SIGABRT = cint(6) + SIGFPE = cint(8) + SIGILL = cint(4) + SIGINT = cint(2) + SIGSEGV = cint(11) + SIGTERM = cint(15) + SIGPIPE = cint(7) else: when NoFakeVars: {.error: "SIGABRT not ported to your platform".} @@ -74,6 +83,8 @@ else: when defined(macosx): const SIGBUS = cint(10) +elif defined(haiku): + const SIGBUS = cint(30) else: template SIGBUS: untyped = SIGSEGV diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim index 85c796ba0..06e89f130 100644 --- a/lib/system/osalloc.nim +++ b/lib/system/osalloc.nim @@ -207,6 +207,9 @@ elif defined(posix): # some arches like mips and alpha use different values const MAP_ANONYMOUS = 0x20 const MAP_PRIVATE = 0x02 # Changes are private + elif defined(haiku): + const MAP_ANONYMOUS = 0x08 + const MAP_PRIVATE = 0x02 else: var MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 3bfaa1dc2..2434ea21e 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -162,10 +162,12 @@ elif defined(genode): mainTls else: - when not defined(macosx): + when not (defined(macosx) or defined(haiku)): {.passL: "-pthread".} - {.passC: "-pthread".} + when not defined(haiku): + {.passC: "-pthread".} + const schedh = "#define _GNU_SOURCE\n#include <sched.h>" pthreadh = "#define _GNU_SOURCE\n#include <pthread.h>" @@ -714,3 +716,13 @@ elif defined(solaris): if threadId == 0: threadId = int(thr_self()) result = threadId + +elif defined(haiku): + type thr_id {.importc: "thread_id", header: "<OS.h>".} = distinct int32 + proc find_thread(name: cstring): thr_id {.importc, header: "<OS.h>".} + + proc getThreadId*(): int = + ## get the ID of the currently running thread. + if threadId == 0: + threadId = int(find_thread(nil)) + result = threadId diff --git a/tests/osproc/texitsignal.nim b/tests/osproc/texitsignal.nim index c0bed70ee..fbf5068ea 100644 --- a/tests/osproc/texitsignal.nim +++ b/tests/osproc/texitsignal.nim @@ -28,9 +28,12 @@ if paramCount() == 0: # windows kill happens using TerminateProcess(h, 0), so we should get a # 0 here echo p.waitForExit() == 0 + elif defined(haiku): + # on Haiku, the program main thread receive SIGKILLTHR + echo p.waitForExit() == 128 + SIGKILLTHR else: # on posix (non-windows), kill sends SIGKILL echo p.waitForExit() == 128 + SIGKILL else: - sleep(5000) # should get killed before this \ No newline at end of file + sleep(5000) # should get killed before this diff --git a/tests/osproc/tworkingdir.nim b/tests/osproc/tworkingdir.nim index eeed9240d..7d58d5ae6 100644 --- a/tests/osproc/tworkingdir.nim +++ b/tests/osproc/tworkingdir.nim @@ -12,6 +12,8 @@ else: var process: Process when defined(android): process = startProcess("/system/bin/env", "/system/bin", ["true"]) + elif defined(haiku): + process = startProcess("/bin/env", "/bin", ["true"]) else: process = startProcess("/usr/bin/env", "/usr/bin", ["true"]) let dir2 = getCurrentDir() diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 95521449a..fca6927d4 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -109,10 +109,14 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) = safeCopyFile("lib" / nimrtlDll, "tests/dll" / nimrtlDll) else: # posix relies on crappy LD_LIBRARY_PATH (ugh!): - var libpath = getEnv"LD_LIBRARY_PATH".string + const libpathenv = when defined(haiku): + "LIBRARY_PATH" + else: + "LD_LIBRARY_PATH" + var libpath = getEnv(libpathenv).string # Temporarily add the lib directory to LD_LIBRARY_PATH: - putEnv("LD_LIBRARY_PATH", "tests/dll" & (if libpath.len > 0: ":" & libpath else: "")) - defer: putEnv("LD_LIBRARY_PATH", libpath) + putEnv(libpathenv, "tests/dll" & (if libpath.len > 0: ":" & libpath else: "")) + defer: putEnv(libpathenv, libpath) var nimrtlDll = DynlibFormat % "nimrtl" safeCopyFile("lib" / nimrtlDll, "tests/dll" / nimrtlDll) -- cgit 1.4.1-2-gfad0 From ed9fd2b63cde3b82dac26bbe9f92634cfd009884 Mon Sep 17 00:00:00 2001 From: Timothee Cour <timothee.cour2@gmail.com> Date: Tue, 14 Aug 2018 01:44:28 -0700 Subject: fixes #8577, fixes #8580, other bug fixes (#8584) --- lib/pure/collections/sequtils.nim | 124 ++++++++++++++++++++++++++------------ 1 file changed, 86 insertions(+), 38 deletions(-) (limited to 'lib/pure') diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 99ce91979..c8080546a 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -25,6 +25,28 @@ import macros when not defined(nimhygiene): {.pragma: dirty.} + +macro evalOnceAs(expAlias, exp: untyped, letAssigneable: static[bool]): untyped = + ## Injects ``expAlias`` in caller scope, to avoid bugs involving multiple + ## substitution in macro arguments such as + ## https://github.com/nim-lang/Nim/issues/7187 + ## ``evalOnceAs(myAlias, myExp)`` will behave as ``let myAlias = myExp`` + ## except when ``letAssigneable`` is false (eg to handle openArray) where + ## it just forwards ``exp`` unchanged + expectKind(expAlias, nnkIdent) + var val = exp + + result = newStmtList() + # If `exp` is not a symbol we evaluate it once here and then use the temporary + # symbol as alias + if exp.kind != nnkSym and letAssigneable: + val = genSym() + result.add(newLetStmt(val, exp)) + + result.add( + newProc(name = genSym(nskTemplate, $expAlias), params = [getType(untyped)], + body = val, procType = nnkTemplateDef)) + proc concat*[T](seqs: varargs[seq[T]]): seq[T] = ## Takes several sequences' items and returns them inside a new sequence. ## @@ -635,30 +657,7 @@ template mapIt*(s, typ, op: untyped): untyped = result.add(op) result -# This is needed in order not to break the bootstrap, the fallback -# implementation is a "dumb" let that won't work in some cases (eg. when `exp` -# is an openArray) -when declared(macros.symKind): - macro evalOnce(v, exp: untyped): untyped = - expectKind(v, nnkIdent) - var val = exp - - result = newStmtList() - - # Not a parameter we can pass as-is, evaluate and store in a temporary - # variable - if exp.kind != nnkSym or exp.symKind != nskParam: - val = genSym() - result.add(newLetStmt(val, exp)) - - result.add( - newProc(name = genSym(nskTemplate, $v), params = [getType(untyped)], - body = val, procType = nnkTemplateDef)) -else: - macro evalOnce(v, exp: untyped): untyped = - result = newLetStmt(v, exp) - -template mapIt*(s, op: untyped): untyped = +template mapIt*(s: typed, op: untyped): untyped = ## Convenience template around the ``map`` proc to reduce typing. ## ## The template injects the ``it`` variable which you can use directly in an @@ -675,19 +674,24 @@ template mapIt*(s, op: untyped): untyped = block: var it{.inject.}: type(items(s)); op)) - var result: seq[outType] when compiles(s.len): - evalOnce(t, s) - var i = 0 - result = newSeq[outType](t.len) - for it {.inject.} in t: - result[i] = op - i += 1 + block: # using a block avoids https://github.com/nim-lang/Nim/issues/8580 + + # BUG: `evalOnceAs(s2, s, false)` would lead to C compile errors + # (`error: use of undeclared identifier`) instead of Nim compile errors + evalOnceAs(s2, s, compiles((let _ = s))) + + var i = 0 + var result = newSeq[outType](s2.len) + for it {.inject.} in s2: + result[i] = op + i += 1 + result else: - result = @[] + var result: seq[outType] = @[] for it {.inject.} in s: result.add(op) - result + result template applyIt*(varSeq, op: untyped) = ## Convenience template around the mutable ``apply`` proc to reduce typing. @@ -774,6 +778,14 @@ macro mapLiterals*(constructor, op: untyped; when isMainModule: import strutils + + # helper for testing double substitution side effects which are handled + # by `evalOnceAs` + var counter = 0 + proc identity[T](a:T):auto= + counter.inc + a + block: # concat test let s1 = @[1, 2, 3] @@ -1045,10 +1057,12 @@ when isMainModule: assert multiplication == 495, "Multiplication is (5*(9*(11)))" assert concatenation == "nimiscool" - block: # mapIt tests + block: # mapIt + applyIt test + counter = 0 var nums = @[1, 2, 3, 4] - strings = nums.mapIt($(4 * it)) + strings = nums.identity.mapIt($(4 * it)) + doAssert counter == 1 nums.applyIt(it * 3) assert nums[0] + nums[3] == 15 assert strings[2] == "12" @@ -1067,9 +1081,43 @@ when isMainModule: doAssert mapLiterals(([1], ("abc"), 2), `$`, nested=true) == (["1"], "abc", "2") block: # mapIt with openArray - when declared(macros.symKind): - proc foo(x: openArray[int]): seq[int] = x.mapIt(it + 1) - doAssert foo([1,2,3]) == @[2,3,4] + counter = 0 + proc foo(x: openArray[int]): seq[int] = x.mapIt(it * 10) + doAssert foo([identity(1),identity(2)]) == @[10, 20] + doAssert counter == 2 + + block: # mapIt with direct openArray + proc foo1(x: openArray[int]): seq[int] = x.mapIt(it * 10) + counter = 0 + doAssert foo1(openArray[int]([identity(1),identity(2)])) == @[10,20] + doAssert counter == 2 + + template foo2(x: openArray[int]): seq[int] = x.mapIt(it * 10) + counter = 0 + doAssert foo2(openArray[int]([identity(1),identity(2)])) == @[10,20] + # TODO: this fails; not sure how to fix this case + # doAssert counter == 2 + + block: # mapIt empty test, see https://github.com/nim-lang/Nim/pull/8584#pullrequestreview-144723468 + # NOTE: `[].mapIt(it)` is illegal, just as `let a = @[]` is (lacks type + # of elements) + doAssert: not compiles(mapIt(@[], it)) + doAssert: not compiles(mapIt([], it)) + doAssert newSeq[int](0).mapIt(it) == @[] + + block: # mapIt redifinition check, see https://github.com/nim-lang/Nim/issues/8580 + let t = [1,2].mapIt(it) + doAssert t == @[1,2] + + block: + var counter = 0 + proc getInput():auto = + counter.inc + [1, 2] + doAssert getInput().mapIt(it*2).mapIt(it*10) == @[20, 40] + # make sure argument evaluated only once, analog to + # https://github.com/nim-lang/Nim/issues/7187 test case + doAssert counter == 1 block: # mapIt with invalid RHS for `let` (#8566) type X = enum -- cgit 1.4.1-2-gfad0