distinction between re.replace and re.replacef; speed improvements for re module

This commit is contained in:
Araq 2011-03-06 21:16:05 +01:00
commit 3005955d20
23 changed files with 206 additions and 171 deletions

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2010 Andreas Rumpf # (c) Copyright 2011 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -32,46 +32,57 @@ type
reIgnoreCase = 0, ## do caseless matching reIgnoreCase = 0, ## do caseless matching
reMultiLine = 1, ## ``^`` and ``$`` match newlines within data reMultiLine = 1, ## ``^`` and ``$`` match newlines within data
reDotAll = 2, ## ``.`` matches anything including NL reDotAll = 2, ## ``.`` matches anything including NL
reExtended = 3 ## ignore whitespace and ``#`` comments reExtended = 3, ## ignore whitespace and ``#`` comments
reStudy = 4 ## study the expression (may be omitted if the
## expression will be used only once)
TRegExDesc {.pure, final.} = object TRegExDesc {.pure, final.} = object
h: PPcre h: PPcre
e: ptr TExtra
TRegEx* = ref TRegExDesc ## a compiled regular expression TRegEx* = ref TRegExDesc ## a compiled regular expression
EInvalidRegEx* = object of EInvalidValue EInvalidRegEx* = object of EInvalidValue
## is raised if the pattern is no valid regular expression. ## is raised if the pattern is no valid regular expression.
proc raiseInvalidRegex(msg: string) {.noinline, noreturn.} =
var e: ref EInvalidRegEx
new(e)
e.msg = msg
raise e
proc rawCompile(pattern: string, flags: cint): PPcre = proc rawCompile(pattern: string, flags: cint): PPcre =
var var
msg: CString msg: CString
offset: cint offset: cint
com = pcre.Compile(pattern, flags, addr(msg), addr(offset), nil) result = pcre.Compile(pattern, flags, addr(msg), addr(offset), nil)
if com == nil: if result == nil:
var e: ref EInvalidRegEx raiseInvalidRegEx($msg & "\n" & pattern & "\n" & repeatChar(offset) & "^\n")
new(e)
e.msg = $msg & "\n" & pattern & "\n" & repeatChar(offset) & "^\n"
raise e
return com
proc finalizeRegEx(x: TRegEx) = proc finalizeRegEx(x: TRegEx) =
# XXX This is a hack, but PCRE does not export its "free" function properly. # XXX This is a hack, but PCRE does not export its "free" function properly.
# Sigh. The hack relies on PCRE's implementation (see ``pcre_get.c``). # Sigh. The hack relies on PCRE's implementation (see ``pcre_get.c``).
# Fortunately the implementation is unlikely to change. # Fortunately the implementation is unlikely to change.
pcre.free_substring(cast[cstring](x.h)) pcre.free_substring(cast[cstring](x.h))
if not isNil(x.e):
pcre.free_substring(cast[cstring](x.e))
proc re*(s: string, flags = {reExtended}): TRegEx = proc re*(s: string, flags = {reExtended, reStudy}): TRegEx =
## Constructor of regular expressions. Note that Nimrod's ## Constructor of regular expressions. Note that Nimrod's
## extended raw string literals support this syntax ``re"[abc]"`` as ## extended raw string literals support this syntax ``re"[abc]"`` as
## a short form for ``re(r"[abc]")``. ## a short form for ``re(r"[abc]")``.
new(result, finalizeRegEx) new(result, finalizeRegEx)
result.h = rawCompile(s, cast[cint](flags)) result.h = rawCompile(s, cast[cint](flags - {reStudy}))
if reStudy in flags:
var msg: cstring
result.e = pcre.study(result.h, 0, msg)
if not isNil(msg): raiseInvalidRegex($msg)
proc matchOrFind(s: string, pattern: TRegEx, matches: var openarray[string], proc matchOrFind(s: string, pattern: TRegEx, matches: var openarray[string],
start, flags: cint): cint = start, flags: cint): cint =
var var
rawMatches: array[0..maxSubpatterns * 3 - 1, cint] rawMatches: array[0..maxSubpatterns * 3 - 1, cint]
res = pcre.Exec(pattern.h, nil, s, len(s), start, flags, res = pcre.Exec(pattern.h, pattern.e, s, len(s), start, flags,
cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3) cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3)
if res < 0'i32: return res if res < 0'i32: return res
for i in 1..int(res)-1: for i in 1..int(res)-1:
@ -83,13 +94,13 @@ proc matchOrFind(s: string, pattern: TRegEx, matches: var openarray[string],
proc findBounds*(s: string, pattern: TRegEx, matches: var openarray[string], proc findBounds*(s: string, pattern: TRegEx, matches: var openarray[string],
start = 0): tuple[first, last: int] = start = 0): tuple[first, last: int] =
## returns the starting position and end position of ``pattern`` in ``s`` ## returns the starting position and end position of `pattern` in `s`
## and the captured ## and the captured
## substrings in the array ``matches``. If it does not match, nothing ## substrings in the array `matches`. If it does not match, nothing
## is written into ``matches`` and (-1,0) is returned. ## is written into `matches` and ``(-1,0)`` is returned.
var var
rawMatches: array[0..maxSubpatterns * 3 - 1, cint] rawMatches: array[0..maxSubpatterns * 3 - 1, cint]
res = pcre.Exec(pattern.h, nil, s, len(s), start, 0'i32, res = pcre.Exec(pattern.h, pattern.e, s, len(s), start, 0'i32,
cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3) cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3)
if res < 0'i32: return (-1, 0) if res < 0'i32: return (-1, 0)
for i in 1..int(res)-1: for i in 1..int(res)-1:
@ -99,9 +110,39 @@ proc findBounds*(s: string, pattern: TRegEx, matches: var openarray[string],
else: matches[i-1] = "" else: matches[i-1] = ""
return (rawMatches[0].int, rawMatches[1].int - 1) return (rawMatches[0].int, rawMatches[1].int - 1)
proc findBounds*(s: string, pattern: TRegEx,
matches: var openarray[tuple[first, last: int]],
start = 0): tuple[first, last: int] =
## returns the starting position and end position of ``pattern`` in ``s``
## and the captured substrings in the array `matches`.
## If it does not match, nothing is written into `matches` and
## ``(-1,0)`` is returned.
var
rawMatches: array[0..maxSubpatterns * 3 - 1, cint]
res = pcre.Exec(pattern.h, pattern.e, s, len(s), start, 0'i32,
cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3)
if res < 0'i32: return (-1, 0)
for i in 1..int(res)-1:
var a = rawMatches[i * 2]
var b = rawMatches[i * 2 + 1]
if a >= 0'i32: matches[i-1] = (int(a), int(b)-1)
else: matches[i-1] = (-1,0)
return (rawMatches[0].int, rawMatches[1].int - 1)
proc findBounds*(s: string, pattern: TRegEx,
start = 0): tuple[first, last: int] =
## returns the starting position of `pattern` in `s`. If it does not
## match, ``(-1,0)`` is returned.
var
rawMatches: array[0..3 - 1, cint]
res = pcre.Exec(pattern.h, nil, s, len(s), start, 0'i32,
cast[ptr cint](addr(rawMatches)), 3)
if res < 0'i32: return (int(res), 0)
return (int(rawMatches[0]), int(rawMatches[1]-1))
proc matchOrFind(s: string, pattern: TRegEx, start, flags: cint): cint = proc matchOrFind(s: string, pattern: TRegEx, start, flags: cint): cint =
var rawMatches: array [0..maxSubpatterns * 3 - 1, cint] var rawMatches: array [0..maxSubpatterns * 3 - 1, cint]
result = pcre.Exec(pattern.h, nil, s, len(s), start, flags, result = pcre.Exec(pattern.h, pattern.e, s, len(s), start, flags,
cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3) cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3)
if result >= 0'i32: if result >= 0'i32:
result = rawMatches[1] - rawMatches[0] result = rawMatches[1] - rawMatches[0]
@ -139,7 +180,7 @@ proc find*(s: string, pattern: TRegEx, matches: var openarray[string],
## is written into ``matches`` and -1 is returned. ## is written into ``matches`` and -1 is returned.
var var
rawMatches: array[0..maxSubpatterns * 3 - 1, cint] rawMatches: array[0..maxSubpatterns * 3 - 1, cint]
res = pcre.Exec(pattern.h, nil, s, len(s), start, 0'i32, res = pcre.Exec(pattern.h, pattern.e, s, len(s), start, 0'i32,
cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3) cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3)
if res < 0'i32: return res if res < 0'i32: return res
for i in 1..int(res)-1: for i in 1..int(res)-1:
@ -219,7 +260,29 @@ proc endsWith*(s: string, suffix: TRegEx): bool =
for i in 0 .. s.len-1: for i in 0 .. s.len-1:
if matchLen(s, suffix, i) == s.len - i: return true if matchLen(s, suffix, i) == s.len - i: return true
proc replace*(s: string, sub: TRegEx, by: string): string = proc replace*(s: string, sub: TRegEx, by = ""): string =
## Replaces `sub` in `s` by the string `by`. Captures cannot be
## accessed in `by`. Examples:
##
## .. code-block:: nimrod
## "var1=key; var2=key2".replace(re"(\w+)'='(\w+)")
##
## Results in:
##
## .. code-block:: nimrod
##
## "; "
result = ""
var prev = 0
while true:
var match = findBounds(s, sub, prev)
if match.first < 0: break
add(result, copy(s, prev, match.first-1))
add(result, by)
prev = match.last + 1
add(result, copy(s, prev))
proc replacef*(s: string, sub: TRegEx, by: string): string =
## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by` ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples: ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
## ##
@ -231,6 +294,17 @@ proc replace*(s: string, sub: TRegEx, by: string): string =
## .. code-block:: nimrod ## .. code-block:: nimrod
## ##
## "var1<-keykey; val2<-key2key2" ## "var1<-keykey; val2<-key2key2"
result = ""
var caps: array[0..maxSubpatterns-1, string]
var prev = 0
while true:
var match = findBounds(s, sub, caps, prev)
if match.first < 0: break
add(result, copy(s, prev, match.first-1))
addf(result, by, caps)
prev = match.last + 1
add(result, copy(s, prev))
when false:
result = "" result = ""
var i = 0 var i = 0
var caps: array[0..maxSubpatterns-1, string] var caps: array[0..maxSubpatterns-1, string]
@ -376,8 +450,10 @@ when isMainModule:
assert false assert false
assert "var1=key; var2=key2".endsWith(re"\w+=\w+") assert "var1=key; var2=key2".endsWith(re"\w+=\w+")
assert("var1=key; var2=key2".replace(re"(\w+)=(\w+)", "$1<-$2$2") == assert("var1=key; var2=key2".replacef(re"(\w+)=(\w+)", "$1<-$2$2") ==
"var1<-keykey; var2<-key2key2") "var1<-keykey; var2<-key2key2")
assert("var1=key; var2=key2".replace(re"(\w+)=(\w+)", "$1<-$2$2") ==
"$1<-$2$2; $1<-$2$2")
for word in split("00232this02939is39an22example111", re"\d+"): for word in split("00232this02939is39an22example111", re"\d+"):
writeln(stdout, word) writeln(stdout, word)

View file

@ -37,7 +37,7 @@
when defined(WIN32): when defined(WIN32):
const dllName = "tcl(85|84|83|82|81|80).dll" const dllName = "tcl(85|84|83|82|81|80).dll"
elif defined(macosx): elif defined(macosx):
const dllName = "libtcl(8.5|8.4|8.3|8.2|8.1).dynlib" const dllName = "libtcl(8.5|8.4|8.3|8.2|8.1).dylib"
else: else:
const dllName = "libtcl(8.5|8.4|8.3|8.2|8.1).so.(1|0)" const dllName = "libtcl(8.5|8.4|8.3|8.2|8.1).so.(1|0)"

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2010 Andreas Rumpf # (c) Copyright 2011 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.

View file

@ -874,7 +874,7 @@ proc endsWith*(s: string, suffix: TPeg, start = 0): bool {.
for i in start .. s.len-1: for i in start .. s.len-1:
if matchLen(s, suffix, i) == s.len - i: return true if matchLen(s, suffix, i) == s.len - i: return true
proc replace*(s: string, sub: TPeg, by: string): string {. proc replacef*(s: string, sub: TPeg, by: string): string {.
nosideEffect, rtl, extern: "npegs$1".} = nosideEffect, rtl, extern: "npegs$1".} =
## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by` ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples: ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
@ -898,7 +898,23 @@ proc replace*(s: string, sub: TPeg, by: string): string {.
else: else:
addf(result, by, caps) addf(result, by, caps)
inc(i, x) inc(i, x)
# copy the rest: add(result, copy(s, i))
proc replace*(s: string, sub: TPeg, by = ""): string {.
nosideEffect, rtl, extern: "npegs$1".} =
## Replaces `sub` in `s` by the string `by`. Captures cannot be accessed
## in `by`.
result = ""
var i = 0
var caps: array[0..maxSubpatterns-1, string]
while i < s.len:
var x = matchLen(s, sub, caps, i)
if x <= 0:
add(result, s[i])
inc(i)
else:
addf(result, by, caps)
inc(i, x)
add(result, copy(s, i)) add(result, copy(s, i))
proc parallelReplace*(s: string, subs: openArray[ proc parallelReplace*(s: string, subs: openArray[
@ -1691,7 +1707,7 @@ when isMainModule:
""" """
assert($g2 == "((A B) / (C D))") assert($g2 == "((A B) / (C D))")
assert match("cccccdddddd", g2) assert match("cccccdddddd", g2)
assert("var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2") == assert("var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2") ==
"var1<-keykey; var2<-key2key2") "var1<-keykey; var2<-key2key2")
assert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}") assert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}")
@ -1722,7 +1738,7 @@ when isMainModule:
assert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+") assert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+")
assert(not match("456678", peg"(\letter)+")) assert(not match("456678", peg"(\letter)+"))
assert("var1 = key; var2 = key2".replace( assert("var1 = key; var2 = key2".replacef(
peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") == peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") ==
"var1<-keykey;var2<-key2key2") "var1<-keykey;var2<-key2key2")

View file

@ -687,7 +687,7 @@ proc contains*(s: string, chars: set[char]): bool {.noSideEffect.} =
## Same as ``find(s, chars) >= 0``. ## Same as ``find(s, chars) >= 0``.
return find(s, chars) >= 0 return find(s, chars) >= 0
proc replace*(s, sub, by: string): string {.noSideEffect, proc replace*(s, sub: string, by = ""): string {.noSideEffect,
rtl, extern: "nsuReplaceStr".} = rtl, extern: "nsuReplaceStr".} =
## Replaces `sub` in `s` by the string `by`. ## Replaces `sub` in `s` by the string `by`.
var a: TSkipTable var a: TSkipTable
@ -800,7 +800,7 @@ proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
## by ``\xHH`` where ``HH`` is its hexadecimal value. ## by ``\xHH`` where ``HH`` is its hexadecimal value.
## The procedure has been designed so that its output is usable for many ## The procedure has been designed so that its output is usable for many
## different common syntaxes. The resulting string is prefixed with ## different common syntaxes. The resulting string is prefixed with
## ``prefix`` and suffixed with ``suffix``. Both may be empty strings. ## `prefix` and suffixed with `suffix`. Both may be empty strings.
result = prefix result = prefix
for c in items(s): for c in items(s):
case c case c

View file

@ -65,11 +65,11 @@ proc `not` *(x: bool): bool {.magic: "Not", noSideEffect.}
proc `and`*(x, y: bool): bool {.magic: "And", noSideEffect.} proc `and`*(x, y: bool): bool {.magic: "And", noSideEffect.}
## Boolean ``and``; returns true iff ``x == y == true``. ## Boolean ``and``; returns true iff ``x == y == true``.
## Evaluation is short-circuited: this means that if ``x`` is false, ## Evaluation is lazy: if ``x`` is false,
## ``y`` will not even be evaluated. ## ``y`` will not even be evaluated.
proc `or`*(x, y: bool): bool {.magic: "Or", noSideEffect.} proc `or`*(x, y: bool): bool {.magic: "Or", noSideEffect.}
## Boolean ``or``; returns true iff ``not (not x and not y)``. ## Boolean ``or``; returns true iff ``not (not x and not y)``.
## Evaluation is short-circuited: this means that if ``x`` is true, ## Evaluation is lazy: if ``x`` is true,
## ``y`` will not even be evaluated. ## ``y`` will not even be evaluated.
proc `xor`*(x, y: bool): bool {.magic: "Xor", noSideEffect.} proc `xor`*(x, y: bool): bool {.magic: "Xor", noSideEffect.}
## Boolean `exclusive or`; returns true iff ``x != y``. ## Boolean `exclusive or`; returns true iff ``x != y``.
@ -623,7 +623,7 @@ template `not_in` * (x, y: expr): expr = not contains(y, x)
proc `is` *[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.} proc `is` *[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.}
template `is_not` *(x, y: expr): expr = not (x is y) template `is_not` *(x, y: expr): expr = not (x is y)
proc cmp*[T, S: typeDesc](x: T, y: S): int {.procvar.} = proc cmp*[T](x, y: T): int {.procvar.} =
## Generic compare proc. Returns a value < 0 iff x < y, a value > 0 iff x > y ## Generic compare proc. Returns a value < 0 iff x < y, a value > 0 iff x > y
## and 0 iff x == y. This is useful for writing generic algorithms without ## and 0 iff x == y. This is useful for writing generic algorithms without
## performance loss. This generic implementation uses the `==` and `<` ## performance loss. This generic implementation uses the `==` and `<`
@ -1034,9 +1034,6 @@ iterator countup*[S, T](a: S, b: T, step = 1): T {.inline.} =
while res <= b: while res <= b:
yield res yield res
inc(res, step) inc(res, step)
# we cannot use ``for x in a..b: `` here, because that is not
# known in the System module
proc min*(x, y: int): int {.magic: "MinI", noSideEffect.} proc min*(x, y: int): int {.magic: "MinI", noSideEffect.}
proc min*(x, y: int8): int8 {.magic: "MinI", noSideEffect.} proc min*(x, y: int8): int8 {.magic: "MinI", noSideEffect.}

View file

@ -31,7 +31,7 @@ elif defined(useNimRtl):
when hostOS == "windows": when hostOS == "windows":
const nimrtl* = "nimrtl.dll" const nimrtl* = "nimrtl.dll"
elif hostOS == "macosx": elif hostOS == "macosx":
const nimrtl* = "nimrtl.dynlib" const nimrtl* = "nimrtl.dylib"
else: else:
const nimrtl* = "libnimrtl.so" const nimrtl* = "libnimrtl.so"
{.pragma: rtl, importc: "nimrtl_$1", dynlib: nimrtl.} {.pragma: rtl, importc: "nimrtl_$1", dynlib: nimrtl.}

View file

@ -27,7 +27,7 @@ when defined(windows):
clarodll = "claro.dll" clarodll = "claro.dll"
elif defined(macosx): elif defined(macosx):
const const
clarodll = "libclaro.dynlib" clarodll = "libclaro.dylib"
else: else:
const const
clarodll = "libclaro.so" clarodll = "libclaro.so"

View file

@ -39,7 +39,7 @@ when not defined(pcreDll):
when hostOS == "windows": when hostOS == "windows":
const pcreDll = "pcre3.dll" const pcreDll = "pcre3.dll"
elif hostOS == "macosx": elif hostOS == "macosx":
const pcreDll = "libpcre.dynlib" const pcreDll = "libpcre(.3|).dylib"
else: else:
const pcreDll = "libpcre.so(.3|)" const pcreDll = "libpcre.so(.3|)"
@ -275,7 +275,7 @@ proc maketables*(): ptr char{.cdecl, importc: "pcre_maketables",
dynlib: pcredll.} dynlib: pcredll.}
proc refcount*(a2: ptr TPcre, a3: cint): cint{.cdecl, importc: "pcre_refcount", proc refcount*(a2: ptr TPcre, a3: cint): cint{.cdecl, importc: "pcre_refcount",
dynlib: pcredll.} dynlib: pcredll.}
proc study*(a2: ptr TPcre, a3: cint, a4: cstringArray): ptr Textra{.cdecl, proc study*(a2: ptr TPcre, a3: cint, a4: var cstring): ptr Textra{.cdecl,
importc: "pcre_study", dynlib: pcredll.} importc: "pcre_study", dynlib: pcredll.}
proc version*(): cstring{.cdecl, importc: "pcre_version", dynlib: pcredll.} proc version*(): cstring{.cdecl, importc: "pcre_version", dynlib: pcredll.}

View file

@ -39,7 +39,7 @@ when defined(WIN32):
dllName = "tcl(85|84|83|82|81|80).dll" dllName = "tcl(85|84|83|82|81|80).dll"
elif defined(macosx): elif defined(macosx):
const const
dllName = "libtcl(8.5|8.4|8.3|8.2|8.1).dynlib" dllName = "libtcl(8.5|8.4|8.3|8.2|8.1).dylib"
else: else:
const const
dllName = "libtcl(8.5|8.4|8.3|8.2|8.1).so.(1|0)" dllName = "libtcl(8.5|8.4|8.3|8.2|8.1).so.(1|0)"

View file

@ -10,7 +10,7 @@ when not defined(treDll):
when hostOS == "windows": when hostOS == "windows":
const treDll = "tre.dll" const treDll = "tre.dll"
elif hostOS == "macosx": elif hostOS == "macosx":
const treDll = "libtre.dynlib" const treDll = "libtre.dylib"
else: else:
const treDll = "libtre.so(.5|)" const treDll = "libtre.so(.5|)"

View file

@ -1,15 +0,0 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2008 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This file implements interactive sessions.
import
llstream, strutils, ropes, nstrtabs, msgs
# implementation

View file

@ -15,7 +15,7 @@ import
os, lists, condsyms, rodread, rodwrite, ropes, trees, os, lists, condsyms, rodread, rodwrite, ropes, trees,
wordrecg, sem, semdata, idents, passes, docgen, extccomp, wordrecg, sem, semdata, idents, passes, docgen, extccomp,
cgen, ecmasgen, cgen, ecmasgen,
platform, interact, nimconf, importer, passaux, depends, transf, evals, types platform, nimconf, importer, passaux, depends, transf, evals, types
const const
has_LLVM_Backend = false has_LLVM_Backend = false

View file

@ -412,8 +412,7 @@ proc includeFilename*(f: string): int =
if filenames[i] == f: if filenames[i] == f:
return i return i
result = len(filenames) result = len(filenames)
setlen(filenames, result + 1) filenames.add(f)
filenames[result] = f
proc newLineInfo*(filename: string, line, col: int): TLineInfo = proc newLineInfo*(filename: string, line, col: int): TLineInfo =
result.fileIndex = includeFilename(filename) result.fileIndex = includeFilename(filename)
@ -421,7 +420,7 @@ proc newLineInfo*(filename: string, line, col: int): TLineInfo =
result.col = int16(col) result.col = int16(col)
proc ToFilename*(info: TLineInfo): string = proc ToFilename*(info: TLineInfo): string =
if info.fileIndex == - 1: result = "???" if info.fileIndex < 0: result = "???"
else: result = filenames[info.fileIndex] else: result = filenames[info.fileIndex]
proc ToLinenumber*(info: TLineInfo): int {.inline.} = proc ToLinenumber*(info: TLineInfo): int {.inline.} =
@ -456,7 +455,7 @@ proc MsgKindToString*(kind: TMsgKind): string =
result = msgKindToStr[kind] result = msgKindToStr[kind]
proc getMessageStr(msg: TMsgKind, arg: string): string = proc getMessageStr(msg: TMsgKind, arg: string): string =
result = `%`(msgKindToString(msg), [arg]) result = msgKindToString(msg) % [arg]
type type
TCheckPointResult* = enum TCheckPointResult* = enum
@ -489,19 +488,17 @@ proc handleError(msg: TMsgKind, eh: TErrorHandling) =
elif eh == doRaise: elif eh == doRaise:
raiseRecoverableError() raiseRecoverableError()
proc sameLineInfo(a, b: TLineInfo): bool = proc `==`(a, b: TLineInfo): bool =
result = (a.line == b.line) and (a.fileIndex == b.fileIndex) result = a.line == b.line and a.fileIndex == b.fileIndex
proc writeContext(lastinfo: TLineInfo) = proc writeContext(lastinfo: TLineInfo) =
var info: TLineInfo var info = lastInfo
info = lastInfo
for i in countup(0, len(msgContext) - 1): for i in countup(0, len(msgContext) - 1):
if not sameLineInfo(msgContext[i], lastInfo) and if msgContext[i] != lastInfo and msgContext[i] != info:
not sameLineInfo(msgContext[i], info): MsgWriteln(posErrorFormat % [toFilename(msgContext[i]),
MsgWriteln(`%`(posErrorFormat, [toFilename(msgContext[i]),
coordToStr(msgContext[i].line), coordToStr(msgContext[i].line),
coordToStr(msgContext[i].col), coordToStr(msgContext[i].col),
getMessageStr(errInstantiationFrom, "")])) getMessageStr(errInstantiationFrom, "")])
info = msgContext[i] info = msgContext[i]
proc rawMessage*(msg: TMsgKind, args: openarray[string]) = proc rawMessage*(msg: TMsgKind, args: openarray[string]) =
@ -539,7 +536,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
frmt = posErrorFormat frmt = posErrorFormat
# we try to filter error messages so that not two error message # we try to filter error messages so that not two error message
# in the same file and line are produced: # in the same file and line are produced:
ignoreMsg = sameLineInfo(lastError, info) ignoreMsg = lastError == info
lastError = info lastError = info
of warnMin..warnMax: of warnMin..warnMax:
ignoreMsg = optWarns notin gOptions or msg notin gNotes ignoreMsg = optWarns notin gOptions or msg notin gNotes

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
# implements some little helper passes ## implements some little helper passes
import import
strutils, ast, astalgo, passes, msgs, options strutils, ast, astalgo, passes, msgs, options
@ -20,7 +20,11 @@ proc verboseOpen(s: PSym, filename: string): PPassContext =
proc verboseProcess(context: PPassContext, n: PNode): PNode = proc verboseProcess(context: PPassContext, n: PNode): PNode =
result = n result = n
if context != nil: InternalError("logpass: context is not nil") if context != nil: InternalError("logpass: context is not nil")
if gVerbosity == 3: Message(n.info, hintProcessing, $ast.gid) if gVerbosity == 3:
# system.nim deactivates all hints, for verbosity:3 we want the processing
# messages nonetheless, so we activate them again unconditionally:
incl(msgs.gNotes, hintProcessing)
Message(n.info, hintProcessing, $ast.gid)
proc verbosePass*(): TPass = proc verbosePass*(): TPass =
initPass(result) initPass(result)
@ -28,7 +32,6 @@ proc verbosePass*(): TPass =
result.process = verboseProcess result.process = verboseProcess
proc cleanUp(c: PPassContext, n: PNode): PNode = proc cleanUp(c: PPassContext, n: PNode): PNode =
var s: PSym
result = n result = n
# we cannot clean up if dead code elimination is activated # we cannot clean up if dead code elimination is activated
if optDeadCodeElim in gGlobalOptions: return if optDeadCodeElim in gGlobalOptions: return
@ -36,9 +39,9 @@ proc cleanUp(c: PPassContext, n: PNode): PNode =
of nkStmtList: of nkStmtList:
for i in countup(0, sonsLen(n) - 1): discard cleanup(c, n.sons[i]) for i in countup(0, sonsLen(n) - 1): discard cleanup(c, n.sons[i])
of nkProcDef, nkMethodDef: of nkProcDef, nkMethodDef:
if (n.sons[namePos].kind == nkSym): if n.sons[namePos].kind == nkSym:
s = n.sons[namePos].sym var s = n.sons[namePos].sym
if not (sfDeadCodeElim in getModule(s).flags) and not astNeeded(s): if sfDeadCodeElim notin getModule(s).flags and not astNeeded(s):
s.ast.sons[codePos] = ast.emptyNode # free the memory s.ast.sons[codePos] = ast.emptyNode # free the memory
else: else:
nil nil

View file

@ -398,7 +398,8 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic in FakeVarParams): if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic in FakeVarParams):
# BUGFIX: check for L-Value still needs to be done for the arguments! # BUGFIX: check for L-Value still needs to be done for the arguments!
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
if i < sonsLen(t) and skipTypes(t.sons[i], abstractInst).kind == tyVar: if i < sonsLen(t) and t.sons[i] != nil and
skipTypes(t.sons[i], abstractInst).kind == tyVar:
if isAssignable(n.sons[i]) != arLValue: if isAssignable(n.sons[i]) != arLValue:
LocalError(n.sons[i].info, errVarForOutParamNeeded) LocalError(n.sons[i].info, errVarForOutParamNeeded)
return return

View file

@ -47,6 +47,22 @@ proc GenericCacheAdd(c: PContext, genericSym, instSym: PSym) =
addSon(n, newSymNode(instSym)) addSon(n, newSymNode(instSym))
addSon(c.generics, n) addSon(c.generics, n)
proc removeDefaultParamValues(n: PNode) =
# we remove default params, because they cannot be instantiated properly
# and they are not needed anyway for instantiation (each param is already
# provided).
when false:
for i in countup(1, sonsLen(n)-1):
var a = n.sons[i]
if a.kind != nkIdentDefs: IllFormedAst(a)
var L = a.len
if a.sons[L-1].kind != nkEmpty and a.sons[L-2].kind != nkEmpty:
# ``param: typ = defaultVal``.
# We don't need defaultVal for semantic checking and it's wrong for
# ``cmp: proc (a, b: T): int = cmp``. Hm, for ``cmp = cmp`` that is
# not possible... XXX We don't solve this issue here.
a.sons[L-1] = ast.emptyNode
proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
info: TLineInfo): PSym = info: TLineInfo): PSym =
# generates an instantiated proc # generates an instantiated proc
@ -76,6 +92,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
n.sons[genericParamsPos] = ast.emptyNode n.sons[genericParamsPos] = ast.emptyNode
# semantic checking for the parameters: # semantic checking for the parameters:
if n.sons[paramsPos].kind != nkEmpty: if n.sons[paramsPos].kind != nkEmpty:
removeDefaultParamValues(n.sons[ParamsPos])
semParamList(c, n.sons[ParamsPos], nil, result) semParamList(c, n.sons[ParamsPos], nil, result)
addParams(c, result.typ.n) addParams(c, result.typ.n)
else: else:

View file

@ -11,7 +11,9 @@
proc fitNode(c: PContext, formal: PType, arg: PNode): PNode = proc fitNode(c: PContext, formal: PType, arg: PNode): PNode =
result = IndexTypesMatch(c, formal, arg.typ, arg) result = IndexTypesMatch(c, formal, arg.typ, arg)
if result == nil: typeMismatch(arg, formal, arg.typ) if result == nil:
#debug(arg)
typeMismatch(arg, formal, arg.typ)
proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType = proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
if prev == nil: if prev == nil:
@ -518,7 +520,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
# check type compability between def.typ and typ: # check type compability between def.typ and typ:
if typ == nil: if typ == nil:
typ = def.typ typ = def.typ
elif def != nil and def.typ != nil and def.typ.kind != tyNone: elif def != nil:
# and def.typ != nil and def.typ.kind != tyNone:
# example code that triggers it: # example code that triggers it:
# proc sort[T](cmp: proc(a, b: T): int = cmp) # proc sort[T](cmp: proc(a, b: T): int = cmp)
def = fitNode(c, typ, def) def = fitNode(c, typ, def)

View file

@ -1,71 +0,0 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2010 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Creates a server, opens a browser and starts serving a Repl for the user.
## Unfortunately it doesn't ever stop...
import httpserver, sockets, browsers, strutils, cgi, options
const
gui = """
<html>
<head>
<title>Nimrod Interactive Web Console</title>
</head>
<body>
<form action="exec" method="get">
<input type="submit" value="Run" /><br />
<textarea name="code" cols="80" rows="30">import strutils, os
# your code here</textarea>
<table border="0">
<tr>
<td><input type="checkbox" name="objChecks" checked="true"
value="on">objChecks</input></td>
<td><input type="checkbox" name="fieldChecks" checked="true"
value="on">fieldChecks</input></td>
<td><input type="checkbox" name="rangeChecks" checked="true"
value="on">rangeChecks</input></td>
</tr><tr>
<td><input type="checkbox" name="boundChecks" checked="true"
value="on">boundChecks</input></td>
<td><input type="checkbox" name="overflowChecks" checked="true"
value="on">overflowChecks</input></td>
<td><input type="checkbox" name="nanChecks" checked="true"
value="on">nanChecks</input></td>
</tr><tr>
<td><input type="checkbox" name="infChecks" checked="true"
value="on">infChecks</input></td>
<td><input type="checkbox" name="assertions" checked="true"
value="on">assertions</input></td>
</tr>
</table>
</form>
$1
</body>
</html>
"""
proc runCode(input: string): string =
nil
proc handleRequest(client: TSocket, path, query: string) =
var output = query
client.send(gui % output & wwwNL)
var s: TServer
open(s, TPort(0))
browsers.openDefaultBrowser("http://localhost:" & $s.port)
while true:
next(s)
handleRequest(s.client, s.path, s.query)
close(s.client)
close(s)

View file

@ -50,7 +50,7 @@ when isMainModule:
for j in 0 .. L-1: for j in 0 .. L-1:
data[j] = (math.random(90) - 10) data[j] = (math.random(90) - 10)
var copy = data var copy = data
sort(data, cmp[int, int], order) sort(data, cmp[int], order)
if not sorted(data, order): if not sorted(data, order):
#for x in items(data): echo x #for x in items(data): echo x
break break

View file

@ -8,8 +8,6 @@
- deprecate ^ and make it available as operator - deprecate ^ and make it available as operator
- test branch coverage - test branch coverage
- checked exceptions - checked exceptions
- do not ambiguity error for methods if ambiguity only affects the same
dispatcher anyway
- slicing - slicing
@ -109,6 +107,13 @@ Version 2
type type
PWindow = ref TWindow not nil PWindow = ref TWindow not nil
The problem with ``nil`` is that the language currently relies on it for
implicit initialization. Initialization is different from assignment. The
issues can "easily" dealt with by ensuring:
var x = myProc() # checks myProc() initializes every pointer explicitely
- the two other parsers - the two other parsers

5
tools/nimgrep.cfg Normal file
View file

@ -0,0 +1,5 @@
# The GC is still too fragile; I don't want that ``--replace`` does bad things
# to important files. Nimgrep does not really need a GC anyway.
--gc:none

View file

@ -34,7 +34,8 @@ Changes affecting backwards compatibility
``module.re"abc"`` is now supported. ``module.re"abc"`` is now supported.
- Changed the behaviour of ``strutils.%``, ``ropes.%`` - Changed the behaviour of ``strutils.%``, ``ropes.%``
if both ``$#`` and ``$i`` are involved. if both ``$#`` and ``$i`` are involved.
- The ``pegs`` and ``re`` modules distinguish between ``replace``
and ``replacef`` operations.
Additions Additions
--------- ---------