improvements for 'pretty'
This commit is contained in:
parent
f2b9905b4e
commit
1101a40f91
15 changed files with 99 additions and 91 deletions
|
|
@ -20,12 +20,12 @@ type
|
||||||
PStrEntry* = ref TStrEntry
|
PStrEntry* = ref TStrEntry
|
||||||
TLinkedList* = object # for the "find" operation:
|
TLinkedList* = object # for the "find" operation:
|
||||||
head*, tail*: PListEntry
|
head*, tail*: PListEntry
|
||||||
Counter*: int
|
counter*: int
|
||||||
|
|
||||||
TCompareProc* = proc (entry: PListEntry, closure: pointer): bool {.nimcall.}
|
TCompareProc* = proc (entry: PListEntry, closure: pointer): bool {.nimcall.}
|
||||||
|
|
||||||
proc initLinkedList*(list: var TLinkedList) =
|
proc initLinkedList*(list: var TLinkedList) =
|
||||||
list.Counter = 0
|
list.counter = 0
|
||||||
list.head = nil
|
list.head = nil
|
||||||
list.tail = nil
|
list.tail = nil
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ const
|
||||||
type
|
type
|
||||||
TGen = object of TPassContext
|
TGen = object of TPassContext
|
||||||
module*: PSym
|
module*: PSym
|
||||||
|
checkExtern: bool
|
||||||
PGen = ref TGen
|
PGen = ref TGen
|
||||||
|
|
||||||
TSourceFile = object
|
TSourceFile = object
|
||||||
|
|
@ -44,13 +45,19 @@ proc loadFile(info: TLineInfo) =
|
||||||
gSourceFiles[i].lines.add(line)
|
gSourceFiles[i].lines.add(line)
|
||||||
|
|
||||||
proc overwriteFiles*() =
|
proc overwriteFiles*() =
|
||||||
|
let overWrite = options.getConfigVar("pretty.overwrite").normalize == "on"
|
||||||
|
let doStrip = options.getConfigVar("pretty.strip").normalize == "on"
|
||||||
for i in 0 .. high(gSourceFiles):
|
for i in 0 .. high(gSourceFiles):
|
||||||
if not gSourceFiles[i].dirty: continue
|
if not gSourceFiles[i].dirty: continue
|
||||||
let newFile = gSourceFiles[i].fullpath #.changeFileExt(".pretty.nim")
|
let newFile = if overWrite: gSourceFiles[i].fullpath
|
||||||
|
else: gSourceFiles[i].fullpath.changeFileExt(".pretty.nim")
|
||||||
try:
|
try:
|
||||||
var f = open(newFile, fmWrite)
|
var f = open(newFile, fmWrite)
|
||||||
for line in gSourceFiles[i].lines:
|
for line in gSourceFiles[i].lines:
|
||||||
f.write line #.strip(leading = false, trailing = true)
|
if doStrip:
|
||||||
|
f.write line.strip(leading = false, trailing = true)
|
||||||
|
else:
|
||||||
|
f.write line
|
||||||
f.write("\L")
|
f.write("\L")
|
||||||
f.close
|
f.close
|
||||||
except EIO:
|
except EIO:
|
||||||
|
|
@ -131,8 +138,6 @@ proc differ(line: string, a, b: int, x: string): bool =
|
||||||
inc j
|
inc j
|
||||||
return false
|
return false
|
||||||
|
|
||||||
var cannotRename = initIntSet()
|
|
||||||
|
|
||||||
proc checkDef(c: PGen; n: PNode) =
|
proc checkDef(c: PGen; n: PNode) =
|
||||||
if n.kind != nkSym: return
|
if n.kind != nkSym: return
|
||||||
let s = n.sym
|
let s = n.sym
|
||||||
|
|
@ -141,10 +146,11 @@ proc checkDef(c: PGen; n: PNode) =
|
||||||
if s.kind in {skResult, skTemp} or s.name.s[0] notin Letters: return
|
if s.kind in {skResult, skTemp} or s.name.s[0] notin Letters: return
|
||||||
if s.kind in {skType, skGenericParam} and sfAnon in s.flags: return
|
if s.kind in {skType, skGenericParam} and sfAnon in s.flags: return
|
||||||
|
|
||||||
checkStyle(n.info, s.name.s, s.kind)
|
if {sfImportc, sfExportc} * s.flags == {} or c.checkExtern:
|
||||||
|
checkStyle(n.info, s.name.s, s.kind)
|
||||||
|
|
||||||
proc checkUse(c: PGen; n: PNode) =
|
proc checkUse*(n: PNode) =
|
||||||
if n.info.fileIndex < 0: return
|
if n.info.fileIndex < 0 or n.kind != nkSym: return
|
||||||
let s = n.sym
|
let s = n.sym
|
||||||
# we simply convert it to what it looks like in the definition
|
# we simply convert it to what it looks like in the definition
|
||||||
# for consistency
|
# for consistency
|
||||||
|
|
@ -177,8 +183,9 @@ proc checkUse(c: PGen; n: PNode) =
|
||||||
system.shallowCopy(gSourceFiles[n.info.fileIndex].lines[n.info.line-1], x)
|
system.shallowCopy(gSourceFiles[n.info.fileIndex].lines[n.info.line-1], x)
|
||||||
gSourceFiles[n.info.fileIndex].dirty = true
|
gSourceFiles[n.info.fileIndex].dirty = true
|
||||||
|
|
||||||
|
|
||||||
when false:
|
when false:
|
||||||
|
var cannotRename = initIntSet()
|
||||||
|
|
||||||
proc beautifyName(s: string, k: TSymKind): string =
|
proc beautifyName(s: string, k: TSymKind): string =
|
||||||
let allUpper = allCharsInSet(s, {'A'..'Z', '0'..'9', '_'})
|
let allUpper = allCharsInSet(s, {'A'..'Z', '0'..'9', '_'})
|
||||||
result = newStringOfCap(s.len)
|
result = newStringOfCap(s.len)
|
||||||
|
|
@ -260,9 +267,9 @@ when false:
|
||||||
|
|
||||||
proc check(c: PGen, n: PNode) =
|
proc check(c: PGen, n: PNode) =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkSym: checkUse(c, n)
|
of nkSym: checkUse(n)
|
||||||
of nkBlockStmt, nkBlockExpr, nkBlockType:
|
of nkBlockStmt, nkBlockExpr, nkBlockType:
|
||||||
if n.sons[0].kind != nkEmpty: checkDef(c, n[0])
|
checkDef(c, n[0])
|
||||||
check(c, n.sons[1])
|
check(c, n.sons[1])
|
||||||
of nkForStmt, nkParForStmt:
|
of nkForStmt, nkParForStmt:
|
||||||
let L = n.len
|
let L = n.len
|
||||||
|
|
@ -300,6 +307,7 @@ proc myOpen(module: PSym): PPassContext =
|
||||||
var g: PGen
|
var g: PGen
|
||||||
new(g)
|
new(g)
|
||||||
g.module = module
|
g.module = module
|
||||||
|
g.checkExtern = options.getConfigVar("pretty.checkextern").normalize == "on"
|
||||||
result = g
|
result = g
|
||||||
if rules.isNil:
|
if rules.isNil:
|
||||||
rules = newStringTable(modeStyleInsensitive)
|
rules = newStringTable(modeStyleInsensitive)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
# included from sigmatch.nim
|
# included from sigmatch.nim
|
||||||
|
|
||||||
import algorithm, sequtils
|
import algorithm, sequtils, pretty
|
||||||
|
|
||||||
const
|
const
|
||||||
sep = '\t'
|
sep = '\t'
|
||||||
|
|
@ -327,6 +327,7 @@ proc markUsed(n: PNode, s: PSym) =
|
||||||
if sfDeprecated in s.flags: message(n.info, warnDeprecated, s.name.s)
|
if sfDeprecated in s.flags: message(n.info, warnDeprecated, s.name.s)
|
||||||
if sfError in s.flags: localError(n.info, errWrongSymbolX, s.name.s)
|
if sfError in s.flags: localError(n.info, errWrongSymbolX, s.name.s)
|
||||||
suggestSym(n, s)
|
suggestSym(n, s)
|
||||||
|
if gCmd == cmdPretty: checkUse(n)
|
||||||
|
|
||||||
proc useSym*(sym: PSym): PNode =
|
proc useSym*(sym: PSym): PNode =
|
||||||
result = newSymNode(sym)
|
result = newSymNode(sym)
|
||||||
|
|
|
||||||
|
|
@ -96,8 +96,8 @@ elif defined(macos):
|
||||||
CurDir* = ':'
|
CurDir* = ':'
|
||||||
ParDir* = "::"
|
ParDir* = "::"
|
||||||
Dirsep* = ':'
|
Dirsep* = ':'
|
||||||
Altsep* = dirsep
|
Altsep* = Dirsep
|
||||||
Pathsep* = ','
|
PathSep* = ','
|
||||||
FileSystemCaseSensitive* = false
|
FileSystemCaseSensitive* = false
|
||||||
ExeExt* = ""
|
ExeExt* = ""
|
||||||
ScriptExt* = ""
|
ScriptExt* = ""
|
||||||
|
|
@ -135,7 +135,7 @@ elif doslike:
|
||||||
elif defined(PalmOS) or defined(MorphOS):
|
elif defined(PalmOS) or defined(MorphOS):
|
||||||
const
|
const
|
||||||
Dirsep* = '/'
|
Dirsep* = '/'
|
||||||
Altsep* = dirsep
|
Altsep* = Dirsep
|
||||||
PathSep* = ';'
|
PathSep* = ';'
|
||||||
Pardir* = ".."
|
Pardir* = ".."
|
||||||
FileSystemCaseSensitive* = false
|
FileSystemCaseSensitive* = false
|
||||||
|
|
@ -157,7 +157,7 @@ else: # UNIX-like operating system
|
||||||
Curdir* = '.'
|
Curdir* = '.'
|
||||||
Pardir* = ".."
|
Pardir* = ".."
|
||||||
Dirsep* = '/'
|
Dirsep* = '/'
|
||||||
Altsep* = dirsep
|
Altsep* = Dirsep
|
||||||
PathSep* = ':'
|
PathSep* = ':'
|
||||||
FileSystemCaseSensitive* = true
|
FileSystemCaseSensitive* = true
|
||||||
ExeExt* = ""
|
ExeExt* = ""
|
||||||
|
|
@ -304,11 +304,11 @@ proc unixToNativePath*(path: string): string {.
|
||||||
elif defined(macos):
|
elif defined(macos):
|
||||||
result = "" # must not start with ':'
|
result = "" # must not start with ':'
|
||||||
else:
|
else:
|
||||||
result = $dirSep
|
result = $DirSep
|
||||||
start = 1
|
start = 1
|
||||||
elif path[0] == '.' and path[1] == '/':
|
elif path[0] == '.' and path[1] == '/':
|
||||||
# current directory
|
# current directory
|
||||||
result = $curdir
|
result = $Curdir
|
||||||
start = 2
|
start = 2
|
||||||
else:
|
else:
|
||||||
result = ""
|
result = ""
|
||||||
|
|
@ -322,12 +322,12 @@ proc unixToNativePath*(path: string): string {.
|
||||||
if result[high(result)] == ':':
|
if result[high(result)] == ':':
|
||||||
add result, ':'
|
add result, ':'
|
||||||
else:
|
else:
|
||||||
add result, pardir
|
add result, ParDir
|
||||||
else:
|
else:
|
||||||
add result, pardir & dirSep
|
add result, ParDir & DirSep
|
||||||
inc(i, 3)
|
inc(i, 3)
|
||||||
elif path[i] == '/':
|
elif path[i] == '/':
|
||||||
add result, dirSep
|
add result, DirSep
|
||||||
inc(i)
|
inc(i)
|
||||||
else:
|
else:
|
||||||
add result, path[i]
|
add result, path[i]
|
||||||
|
|
@ -538,7 +538,7 @@ proc splitPath*(path: string): tuple[head, tail: string] {.
|
||||||
## splitPath("") -> ("", "")
|
## splitPath("") -> ("", "")
|
||||||
var sepPos = -1
|
var sepPos = -1
|
||||||
for i in countdown(len(path)-1, 0):
|
for i in countdown(len(path)-1, 0):
|
||||||
if path[i] in {dirsep, altsep}:
|
if path[i] in {Dirsep, Altsep}:
|
||||||
sepPos = i
|
sepPos = i
|
||||||
break
|
break
|
||||||
if sepPos >= 0:
|
if sepPos >= 0:
|
||||||
|
|
@ -550,9 +550,9 @@ proc splitPath*(path: string): tuple[head, tail: string] {.
|
||||||
|
|
||||||
proc parentDirPos(path: string): int =
|
proc parentDirPos(path: string): int =
|
||||||
var q = 1
|
var q = 1
|
||||||
if path[len(path)-1] in {dirsep, altsep}: q = 2
|
if path[len(path)-1] in {Dirsep, Altsep}: q = 2
|
||||||
for i in countdown(len(path)-q, 0):
|
for i in countdown(len(path)-q, 0):
|
||||||
if path[i] in {dirsep, altsep}: return i
|
if path[i] in {Dirsep, Altsep}: return i
|
||||||
result = -1
|
result = -1
|
||||||
|
|
||||||
proc parentDir*(path: string): string {.
|
proc parentDir*(path: string): string {.
|
||||||
|
|
@ -593,8 +593,8 @@ iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string =
|
||||||
else:
|
else:
|
||||||
for i in countup(0, path.len - 2): # ignore the last /
|
for i in countup(0, path.len - 2): # ignore the last /
|
||||||
# deal with non-normalized paths such as /foo//bar//baz
|
# deal with non-normalized paths such as /foo//bar//baz
|
||||||
if path[i] in {dirsep, altsep} and
|
if path[i] in {Dirsep, Altsep} and
|
||||||
(i == 0 or path[i-1] notin {dirsep, altsep}):
|
(i == 0 or path[i-1] notin {Dirsep, Altsep}):
|
||||||
yield path.substr(0, i)
|
yield path.substr(0, i)
|
||||||
|
|
||||||
if inclusive: yield path
|
if inclusive: yield path
|
||||||
|
|
@ -609,17 +609,17 @@ proc `/../` * (head, tail: string): string {.noSideEffect.} =
|
||||||
result = head / tail
|
result = head / tail
|
||||||
|
|
||||||
proc normExt(ext: string): string =
|
proc normExt(ext: string): string =
|
||||||
if ext == "" or ext[0] == extSep: result = ext # no copy needed here
|
if ext == "" or ext[0] == ExtSep: result = ext # no copy needed here
|
||||||
else: result = extSep & ext
|
else: result = ExtSep & ext
|
||||||
|
|
||||||
proc searchExtPos(s: string): int =
|
proc searchExtPos(s: string): int =
|
||||||
# BUGFIX: do not search until 0! .DS_Store is no file extension!
|
# BUGFIX: do not search until 0! .DS_Store is no file extension!
|
||||||
result = -1
|
result = -1
|
||||||
for i in countdown(len(s)-1, 1):
|
for i in countdown(len(s)-1, 1):
|
||||||
if s[i] == extsep:
|
if s[i] == ExtSep:
|
||||||
result = i
|
result = i
|
||||||
break
|
break
|
||||||
elif s[i] in {dirsep, altsep}:
|
elif s[i] in {Dirsep, Altsep}:
|
||||||
break # do not skip over path
|
break # do not skip over path
|
||||||
|
|
||||||
proc splitFile*(path: string): tuple[dir, name, ext: string] {.
|
proc splitFile*(path: string): tuple[dir, name, ext: string] {.
|
||||||
|
|
@ -639,7 +639,7 @@ proc splitFile*(path: string): tuple[dir, name, ext: string] {.
|
||||||
## If `path` has no extension, `ext` is the empty string.
|
## If `path` has no extension, `ext` is the empty string.
|
||||||
## If `path` has no directory component, `dir` is the empty string.
|
## If `path` has no directory component, `dir` is the empty string.
|
||||||
## If `path` has no filename component, `name` and `ext` are empty strings.
|
## If `path` has no filename component, `name` and `ext` are empty strings.
|
||||||
if path.len == 0 or path[path.len-1] in {dirSep, altSep}:
|
if path.len == 0 or path[path.len-1] in {DirSep, Altsep}:
|
||||||
result = (path, "", "")
|
result = (path, "", "")
|
||||||
else:
|
else:
|
||||||
var sepPos = -1
|
var sepPos = -1
|
||||||
|
|
@ -647,8 +647,8 @@ proc splitFile*(path: string): tuple[dir, name, ext: string] {.
|
||||||
for i in countdown(len(path)-1, 0):
|
for i in countdown(len(path)-1, 0):
|
||||||
if path[i] == ExtSep:
|
if path[i] == ExtSep:
|
||||||
if dotPos == path.len and i > 0 and
|
if dotPos == path.len and i > 0 and
|
||||||
path[i-1] notin {dirsep, altsep}: dotPos = i
|
path[i-1] notin {Dirsep, Altsep}: dotPos = i
|
||||||
elif path[i] in {dirsep, altsep}:
|
elif path[i] in {Dirsep, Altsep}:
|
||||||
sepPos = i
|
sepPos = i
|
||||||
break
|
break
|
||||||
result.dir = substr(path, 0, sepPos-1)
|
result.dir = substr(path, 0, sepPos-1)
|
||||||
|
|
@ -659,7 +659,7 @@ proc extractFilename*(path: string): string {.
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Extracts the filename of a given `path`. This is the same as
|
## Extracts the filename of a given `path`. This is the same as
|
||||||
## ``name & ext`` from ``splitFile(path)``.
|
## ``name & ext`` from ``splitFile(path)``.
|
||||||
if path.len == 0 or path[path.len-1] in {dirSep, altSep}:
|
if path.len == 0 or path[path.len-1] in {DirSep, Altsep}:
|
||||||
result = ""
|
result = ""
|
||||||
else:
|
else:
|
||||||
result = splitPath(path).tail
|
result = splitPath(path).tail
|
||||||
|
|
@ -816,7 +816,7 @@ proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1",
|
||||||
return false
|
return false
|
||||||
var bufA = alloc(bufsize)
|
var bufA = alloc(bufsize)
|
||||||
var bufB = alloc(bufsize)
|
var bufB = alloc(bufsize)
|
||||||
while True:
|
while true:
|
||||||
var readA = readBuffer(a, bufA, bufsize)
|
var readA = readBuffer(a, bufA, bufsize)
|
||||||
var readB = readBuffer(b, bufB, bufsize)
|
var readB = readBuffer(b, bufB, bufsize)
|
||||||
if readA != readB:
|
if readA != readB:
|
||||||
|
|
@ -1032,7 +1032,7 @@ when defined(windows):
|
||||||
env = getEnvironmentStringsW()
|
env = getEnvironmentStringsW()
|
||||||
e = env
|
e = env
|
||||||
if e == nil: return # an error occured
|
if e == nil: return # an error occured
|
||||||
while True:
|
while true:
|
||||||
var eend = strEnd(e)
|
var eend = strEnd(e)
|
||||||
add(environment, $e)
|
add(environment, $e)
|
||||||
e = cast[WideCString](cast[TAddress](eend)+2)
|
e = cast[WideCString](cast[TAddress](eend)+2)
|
||||||
|
|
@ -1043,7 +1043,7 @@ when defined(windows):
|
||||||
env = getEnvironmentStringsA()
|
env = getEnvironmentStringsA()
|
||||||
e = env
|
e = env
|
||||||
if e == nil: return # an error occured
|
if e == nil: return # an error occured
|
||||||
while True:
|
while true:
|
||||||
var eend = strEnd(e)
|
var eend = strEnd(e)
|
||||||
add(environment, $e)
|
add(environment, $e)
|
||||||
e = cast[CString](cast[TAddress](eend)+1)
|
e = cast[CString](cast[TAddress](eend)+1)
|
||||||
|
|
@ -1308,7 +1308,7 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
|
||||||
when defined(doslike):
|
when defined(doslike):
|
||||||
omitNext = isAbsolute(dir)
|
omitNext = isAbsolute(dir)
|
||||||
for i in 1.. dir.len-1:
|
for i in 1.. dir.len-1:
|
||||||
if dir[i] in {dirsep, altsep}:
|
if dir[i] in {Dirsep, Altsep}:
|
||||||
if omitNext:
|
if omitNext:
|
||||||
omitNext = false
|
omitNext = false
|
||||||
else:
|
else:
|
||||||
|
|
@ -1635,10 +1635,10 @@ proc findExe*(exe: string): string {.tags: [FReadDir, FReadEnv].} =
|
||||||
## in directories listed in the ``PATH`` environment variable.
|
## in directories listed in the ``PATH`` environment variable.
|
||||||
## Returns "" if the `exe` cannot be found. On DOS-like platforms, `exe`
|
## Returns "" if the `exe` cannot be found. On DOS-like platforms, `exe`
|
||||||
## is added an ``.exe`` file extension if it has no extension.
|
## is added an ``.exe`` file extension if it has no extension.
|
||||||
result = addFileExt(exe, os.exeExt)
|
result = addFileExt(exe, os.ExeExt)
|
||||||
if existsFile(result): return
|
if existsFile(result): return
|
||||||
var path = string(os.getEnv("PATH"))
|
var path = string(os.getEnv("PATH"))
|
||||||
for candidate in split(path, pathSep):
|
for candidate in split(path, PathSep):
|
||||||
var x = candidate / result
|
var x = candidate / result
|
||||||
if existsFile(x): return x
|
if existsFile(x): return x
|
||||||
result = ""
|
result = ""
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ proc toUpper*(c: char): char {.noSideEffect, procvar,
|
||||||
## Converts `c` into upper case. This works only for the letters a-z.
|
## Converts `c` into upper case. This works only for the letters a-z.
|
||||||
## See `unicode.toUpper` for a version that works for any Unicode character.
|
## See `unicode.toUpper` for a version that works for any Unicode character.
|
||||||
if c in {'a'..'z'}:
|
if c in {'a'..'z'}:
|
||||||
result = chr(ord(c) - (Ord('a') - Ord('A')))
|
result = chr(ord(c) - (ord('a') - ord('A')))
|
||||||
else:
|
else:
|
||||||
result = c
|
result = c
|
||||||
|
|
||||||
|
|
@ -497,31 +497,31 @@ iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[
|
||||||
|
|
||||||
proc wordWrap*(s: string, maxLineWidth = 80,
|
proc wordWrap*(s: string, maxLineWidth = 80,
|
||||||
splitLongWords = true,
|
splitLongWords = true,
|
||||||
seps: set[char] = whitespace,
|
seps: set[char] = Whitespace,
|
||||||
newLine = "\n"): string {.
|
newLine = "\n"): string {.
|
||||||
noSideEffect, rtl, extern: "nsuWordWrap".} =
|
noSideEffect, rtl, extern: "nsuWordWrap".} =
|
||||||
## word wraps `s`.
|
## word wraps `s`.
|
||||||
result = newStringOfCap(s.len + s.len shr 6)
|
result = newStringOfCap(s.len + s.len shr 6)
|
||||||
var SpaceLeft = maxLineWidth
|
var spaceLeft = maxLineWidth
|
||||||
for word, isSep in tokenize(s, seps):
|
for word, isSep in tokenize(s, seps):
|
||||||
if len(word) > SpaceLeft:
|
if len(word) > spaceLeft:
|
||||||
if splitLongWords and len(word) > maxLineWidth:
|
if splitLongWords and len(word) > maxLineWidth:
|
||||||
result.add(substr(word, 0, SpaceLeft-1))
|
result.add(substr(word, 0, spaceLeft-1))
|
||||||
var w = SpaceLeft+1
|
var w = spaceLeft+1
|
||||||
var wordLeft = len(word) - SpaceLeft
|
var wordLeft = len(word) - spaceLeft
|
||||||
while wordLeft > 0:
|
while wordLeft > 0:
|
||||||
result.add(newLine)
|
result.add(newLine)
|
||||||
var L = min(maxLineWidth, wordLeft)
|
var L = min(maxLineWidth, wordLeft)
|
||||||
SpaceLeft = maxLineWidth - L
|
spaceLeft = maxLineWidth - L
|
||||||
result.add(substr(word, w, w+L-1))
|
result.add(substr(word, w, w+L-1))
|
||||||
inc(w, L)
|
inc(w, L)
|
||||||
dec(wordLeft, L)
|
dec(wordLeft, L)
|
||||||
else:
|
else:
|
||||||
SpaceLeft = maxLineWidth - len(word)
|
spaceLeft = maxLineWidth - len(word)
|
||||||
result.add(newLine)
|
result.add(newLine)
|
||||||
result.add(word)
|
result.add(word)
|
||||||
else:
|
else:
|
||||||
SpaceLeft = SpaceLeft - len(word)
|
spaceLeft = spaceLeft - len(word)
|
||||||
result.add(word)
|
result.add(word)
|
||||||
|
|
||||||
proc unindent*(s: string, eatAllIndent = false): string {.
|
proc unindent*(s: string, eatAllIndent = false): string {.
|
||||||
|
|
|
||||||
|
|
@ -593,7 +593,7 @@ proc format*(info: TTimeInfo, f: string): string =
|
||||||
result = ""
|
result = ""
|
||||||
var i = 0
|
var i = 0
|
||||||
var currentF = ""
|
var currentF = ""
|
||||||
while True:
|
while true:
|
||||||
case f[i]
|
case f[i]
|
||||||
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
|
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
|
||||||
case currentF
|
case currentF
|
||||||
|
|
|
||||||
|
|
@ -1262,13 +1262,13 @@ proc getRefcount*[T](x: seq[T]): int {.importc: "getRefcount", noSideEffect.}
|
||||||
## retrieves the reference count of an heap-allocated object. The
|
## retrieves the reference count of an heap-allocated object. The
|
||||||
## value is implementation-dependent.
|
## value is implementation-dependent.
|
||||||
|
|
||||||
# new constants:
|
|
||||||
const
|
const
|
||||||
inf* {.magic: "Inf".} = 1.0 / 0.0
|
Inf* {.magic: "Inf".} = 1.0 / 0.0
|
||||||
## contains the IEEE floating point value of positive infinity.
|
## contains the IEEE floating point value of positive infinity.
|
||||||
neginf* {.magic: "NegInf".} = -inf
|
NegInf* {.magic: "NegInf".} = -Inf
|
||||||
## contains the IEEE floating point value of negative infinity.
|
## contains the IEEE floating point value of negative infinity.
|
||||||
nan* {.magic: "NaN".} = 0.0 / 0.0
|
NaN* {.magic: "NaN".} = 0.0 / 0.0
|
||||||
## contains an IEEE floating point value of *Not A Number*. Note
|
## contains an IEEE floating point value of *Not A Number*. Note
|
||||||
## that you cannot compare a floating point value to this value
|
## that you cannot compare a floating point value to this value
|
||||||
## and expect a reasonable result - use the `classify` procedure
|
## and expect a reasonable result - use the `classify` procedure
|
||||||
|
|
|
||||||
|
|
@ -628,12 +628,12 @@ proc rawDealloc(a: var TMemRegion, p: pointer) =
|
||||||
# check if it is not in the freeSmallChunks[s] list:
|
# check if it is not in the freeSmallChunks[s] list:
|
||||||
if c.free < s:
|
if c.free < s:
|
||||||
# add it to the freeSmallChunks[s] array:
|
# add it to the freeSmallChunks[s] array:
|
||||||
listAdd(a.freeSmallChunks[s div memAlign], c)
|
listAdd(a.freeSmallChunks[s div MemAlign], c)
|
||||||
inc(c.free, s)
|
inc(c.free, s)
|
||||||
else:
|
else:
|
||||||
inc(c.free, s)
|
inc(c.free, s)
|
||||||
if c.free == SmallChunkSize-smallChunkOverhead():
|
if c.free == SmallChunkSize-smallChunkOverhead():
|
||||||
listRemove(a.freeSmallChunks[s div memAlign], c)
|
listRemove(a.freeSmallChunks[s div MemAlign], c)
|
||||||
c.size = SmallChunkSize
|
c.size = SmallChunkSize
|
||||||
freeBigChunk(a, cast[PBigChunk](c))
|
freeBigChunk(a, cast[PBigChunk](c))
|
||||||
sysAssert(((cast[TAddress](p) and PageMask) - smallChunkOverhead()) %%
|
sysAssert(((cast[TAddress](p) and PageMask) - smallChunkOverhead()) %%
|
||||||
|
|
@ -739,7 +739,7 @@ proc realloc(allocator: var TMemRegion, p: pointer, newsize: int): pointer =
|
||||||
proc deallocOsPages(a: var TMemRegion) =
|
proc deallocOsPages(a: var TMemRegion) =
|
||||||
# we free every 'ordinarily' allocated page by iterating over the page bits:
|
# we free every 'ordinarily' allocated page by iterating over the page bits:
|
||||||
for p in elements(a.chunkStarts):
|
for p in elements(a.chunkStarts):
|
||||||
var page = cast[PChunk](p shl pageShift)
|
var page = cast[PChunk](p shl PageShift)
|
||||||
when not weirdUnmap:
|
when not weirdUnmap:
|
||||||
var size = if page.size < PageSize: PageSize else: page.size
|
var size = if page.size < PageSize: PageSize else: page.size
|
||||||
osDeallocPages(page, size)
|
osDeallocPages(page, size)
|
||||||
|
|
|
||||||
|
|
@ -37,12 +37,12 @@ proc genericAssignAux(dest, src: pointer, n: ptr TNimNode, shallow: bool) =
|
||||||
# echo "ugh memory corruption! ", n.kind
|
# echo "ugh memory corruption! ", n.kind
|
||||||
# quit 1
|
# quit 1
|
||||||
|
|
||||||
proc genericAssignAux(dest, src: Pointer, mt: PNimType, shallow: bool) =
|
proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
|
||||||
var
|
var
|
||||||
d = cast[TAddress](dest)
|
d = cast[TAddress](dest)
|
||||||
s = cast[TAddress](src)
|
s = cast[TAddress](src)
|
||||||
sysAssert(mt != nil, "genericAssignAux 2")
|
sysAssert(mt != nil, "genericAssignAux 2")
|
||||||
case mt.Kind
|
case mt.kind
|
||||||
of tyString:
|
of tyString:
|
||||||
var x = cast[PPointer](dest)
|
var x = cast[PPointer](dest)
|
||||||
var s2 = cast[PPointer](s)[]
|
var s2 = cast[PPointer](s)[]
|
||||||
|
|
@ -67,7 +67,7 @@ proc genericAssignAux(dest, src: Pointer, mt: PNimType, shallow: bool) =
|
||||||
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
|
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
|
||||||
cast[pointer](cast[TAddress](s2) +% i *% mt.base.size +%
|
cast[pointer](cast[TAddress](s2) +% i *% mt.base.size +%
|
||||||
GenericSeqSize),
|
GenericSeqSize),
|
||||||
mt.Base, shallow)
|
mt.base, shallow)
|
||||||
of tyObject:
|
of tyObject:
|
||||||
# we need to copy m_type field for tyObject, as it could be empty for
|
# we need to copy m_type field for tyObject, as it could be empty for
|
||||||
# sequence reallocations:
|
# sequence reallocations:
|
||||||
|
|
@ -152,7 +152,7 @@ proc objectInitAux(dest: pointer, n: ptr TNimNode) =
|
||||||
var m = selectBranch(dest, n)
|
var m = selectBranch(dest, n)
|
||||||
if m != nil: objectInitAux(dest, m)
|
if m != nil: objectInitAux(dest, m)
|
||||||
|
|
||||||
proc objectInit(dest: Pointer, typ: PNimType) =
|
proc objectInit(dest: pointer, typ: PNimType) =
|
||||||
# the generic init proc that takes care of initialization of complex
|
# the generic init proc that takes care of initialization of complex
|
||||||
# objects on the stack or heap
|
# objects on the stack or heap
|
||||||
var d = cast[TAddress](dest)
|
var d = cast[TAddress](dest)
|
||||||
|
|
@ -185,7 +185,7 @@ else:
|
||||||
for i in countup(0, r.len - 1): destroy(r[i])
|
for i in countup(0, r.len - 1): destroy(r[i])
|
||||||
|
|
||||||
proc genericReset(dest: pointer, mt: PNimType) {.compilerProc.}
|
proc genericReset(dest: pointer, mt: PNimType) {.compilerProc.}
|
||||||
proc genericResetAux(dest: Pointer, n: ptr TNimNode) =
|
proc genericResetAux(dest: pointer, n: ptr TNimNode) =
|
||||||
var d = cast[TAddress](dest)
|
var d = cast[TAddress](dest)
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkNone: sysAssert(false, "genericResetAux")
|
of nkNone: sysAssert(false, "genericResetAux")
|
||||||
|
|
@ -197,10 +197,10 @@ proc genericResetAux(dest: Pointer, n: ptr TNimNode) =
|
||||||
if m != nil: genericResetAux(dest, m)
|
if m != nil: genericResetAux(dest, m)
|
||||||
zeroMem(cast[pointer](d +% n.offset), n.typ.size)
|
zeroMem(cast[pointer](d +% n.offset), n.typ.size)
|
||||||
|
|
||||||
proc genericReset(dest: Pointer, mt: PNimType) =
|
proc genericReset(dest: pointer, mt: PNimType) =
|
||||||
var d = cast[TAddress](dest)
|
var d = cast[TAddress](dest)
|
||||||
sysAssert(mt != nil, "genericReset 2")
|
sysAssert(mt != nil, "genericReset 2")
|
||||||
case mt.Kind
|
case mt.kind
|
||||||
of tyString, tyRef, tySequence:
|
of tyString, tyRef, tySequence:
|
||||||
unsureAsgnRef(cast[PPointer](dest), nil)
|
unsureAsgnRef(cast[PPointer](dest), nil)
|
||||||
of tyObject, tyTuple:
|
of tyObject, tyTuple:
|
||||||
|
|
|
||||||
|
|
@ -253,7 +253,7 @@ proc reraiseException() {.compilerRtl.} =
|
||||||
else:
|
else:
|
||||||
raiseExceptionAux(currException)
|
raiseExceptionAux(currException)
|
||||||
|
|
||||||
proc WriteStackTrace() =
|
proc writeStackTrace() =
|
||||||
when hasSomeStackTrace:
|
when hasSomeStackTrace:
|
||||||
var s = ""
|
var s = ""
|
||||||
rawWriteStackTrace(s)
|
rawWriteStackTrace(s)
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ var
|
||||||
gch {.rtlThreadVar.}: TGcHeap
|
gch {.rtlThreadVar.}: TGcHeap
|
||||||
|
|
||||||
when not defined(useNimRtl):
|
when not defined(useNimRtl):
|
||||||
InstantiateForRegion(gch.region)
|
instantiateForRegion(gch.region)
|
||||||
|
|
||||||
template acquire(gch: TGcHeap) =
|
template acquire(gch: TGcHeap) =
|
||||||
when hasThreadSupport and hasSharedHeap:
|
when hasThreadSupport and hasSharedHeap:
|
||||||
|
|
@ -329,11 +329,11 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
|
||||||
if m != nil: forAllSlotsAux(dest, m, op)
|
if m != nil: forAllSlotsAux(dest, m, op)
|
||||||
of nkNone: sysAssert(false, "forAllSlotsAux")
|
of nkNone: sysAssert(false, "forAllSlotsAux")
|
||||||
|
|
||||||
proc forAllChildrenAux(dest: Pointer, mt: PNimType, op: TWalkOp) =
|
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) =
|
||||||
var d = cast[TAddress](dest)
|
var d = cast[TAddress](dest)
|
||||||
if dest == nil: return # nothing to do
|
if dest == nil: return # nothing to do
|
||||||
if ntfNoRefs notin mt.flags:
|
if ntfNoRefs notin mt.flags:
|
||||||
case mt.Kind
|
case mt.kind
|
||||||
of tyRef, tyString, tySequence: # leaf:
|
of tyRef, tyString, tySequence: # leaf:
|
||||||
doOperation(cast[PPointer](d)[], op)
|
doOperation(cast[PPointer](d)[], op)
|
||||||
of tyObject, tyTuple:
|
of tyObject, tyTuple:
|
||||||
|
|
@ -341,7 +341,7 @@ proc forAllChildrenAux(dest: Pointer, mt: PNimType, op: TWalkOp) =
|
||||||
of tyArray, tyArrayConstr, tyOpenArray:
|
of tyArray, tyArrayConstr, tyOpenArray:
|
||||||
for i in 0..(mt.size div mt.base.size)-1:
|
for i in 0..(mt.size div mt.base.size)-1:
|
||||||
forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op)
|
forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op)
|
||||||
else: nil
|
else: discard
|
||||||
|
|
||||||
proc forAllChildren(cell: PCell, op: TWalkOp) =
|
proc forAllChildren(cell: PCell, op: TWalkOp) =
|
||||||
gcAssert(cell != nil, "forAllChildren: 1")
|
gcAssert(cell != nil, "forAllChildren: 1")
|
||||||
|
|
@ -352,7 +352,7 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
|
||||||
if marker != nil:
|
if marker != nil:
|
||||||
marker(cellToUsr(cell), op.int)
|
marker(cellToUsr(cell), op.int)
|
||||||
else:
|
else:
|
||||||
case cell.typ.Kind
|
case cell.typ.kind
|
||||||
of tyRef: # common case
|
of tyRef: # common case
|
||||||
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
|
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
|
||||||
of tySequence:
|
of tySequence:
|
||||||
|
|
@ -970,7 +970,7 @@ proc collectCTBody(gch: var TGcHeap) =
|
||||||
#discard collectZCT(gch)
|
#discard collectZCT(gch)
|
||||||
inc(gch.stat.cycleCollections)
|
inc(gch.stat.cycleCollections)
|
||||||
gch.cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() *
|
gch.cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() *
|
||||||
cycleIncrease)
|
CycleIncrease)
|
||||||
gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
|
gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
|
||||||
unmarkStackAndRegisters(gch)
|
unmarkStackAndRegisters(gch)
|
||||||
sysAssert(allocInv(gch.region), "collectCT: end")
|
sysAssert(allocInv(gch.region), "collectCT: end")
|
||||||
|
|
|
||||||
|
|
@ -164,7 +164,7 @@ when not defined(useNimRtl):
|
||||||
for i in 0..cast[PGenericSeq](p).len-1:
|
for i in 0..cast[PGenericSeq](p).len-1:
|
||||||
if i > 0: add result, ", "
|
if i > 0: add result, ", "
|
||||||
reprAux(result, cast[pointer](cast[TAddress](p) + GenericSeqSize + i*bs),
|
reprAux(result, cast[pointer](cast[TAddress](p) + GenericSeqSize + i*bs),
|
||||||
typ.Base, cl)
|
typ.base, cl)
|
||||||
add result, "]"
|
add result, "]"
|
||||||
|
|
||||||
proc reprRecordAux(result: var string, p: pointer, n: ptr TNimNode,
|
proc reprRecordAux(result: var string, p: pointer, n: ptr TNimNode,
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ else:
|
||||||
IONBF {.importc: "_IONBF", nodecl.}: cint
|
IONBF {.importc: "_IONBF", nodecl.}: cint
|
||||||
|
|
||||||
const
|
const
|
||||||
buf_size = 4000
|
BufSize = 4000
|
||||||
|
|
||||||
proc raiseEIO(msg: string) {.noinline, noreturn.} =
|
proc raiseEIO(msg: string) {.noinline, noreturn.} =
|
||||||
sysFatal(EIO, msg)
|
sysFatal(EIO, msg)
|
||||||
|
|
@ -71,7 +71,7 @@ proc readLine(f: TFile, line: var TaintedString): bool =
|
||||||
# of course this could be optimized a bit; but IO is slow anyway...
|
# of course this could be optimized a bit; but IO is slow anyway...
|
||||||
# and it was difficult to get this CORRECT with Ansi C's methods
|
# and it was difficult to get this CORRECT with Ansi C's methods
|
||||||
setLen(line.string, 0) # reuse the buffer!
|
setLen(line.string, 0) # reuse the buffer!
|
||||||
while True:
|
while true:
|
||||||
var c = fgetc(f)
|
var c = fgetc(f)
|
||||||
if c < 0'i32:
|
if c < 0'i32:
|
||||||
if line.len > 0: break
|
if line.len > 0: break
|
||||||
|
|
@ -94,8 +94,8 @@ proc write(f: TFile, i: int) =
|
||||||
else:
|
else:
|
||||||
fprintf(f, "%ld", i)
|
fprintf(f, "%ld", i)
|
||||||
|
|
||||||
proc write(f: TFile, i: biggestInt) =
|
proc write(f: TFile, i: BiggestInt) =
|
||||||
when sizeof(biggestint) == 8:
|
when sizeof(Biggestint) == 8:
|
||||||
fprintf(f, "%lld", i)
|
fprintf(f, "%lld", i)
|
||||||
else:
|
else:
|
||||||
fprintf(f, "%ld", i)
|
fprintf(f, "%ld", i)
|
||||||
|
|
@ -104,7 +104,7 @@ proc write(f: TFile, b: bool) =
|
||||||
if b: write(f, "true")
|
if b: write(f, "true")
|
||||||
else: write(f, "false")
|
else: write(f, "false")
|
||||||
proc write(f: TFile, r: float32) = fprintf(f, "%g", r)
|
proc write(f: TFile, r: float32) = fprintf(f, "%g", r)
|
||||||
proc write(f: TFile, r: biggestFloat) = fprintf(f, "%g", r)
|
proc write(f: TFile, r: BiggestFloat) = fprintf(f, "%g", r)
|
||||||
|
|
||||||
proc write(f: TFile, c: char) = putc(c, f)
|
proc write(f: TFile, c: char) = putc(c, f)
|
||||||
proc write(f: TFile, a: varargs[string, `$`]) =
|
proc write(f: TFile, a: varargs[string, `$`]) =
|
||||||
|
|
@ -114,10 +114,10 @@ proc readAllBuffer(file: TFile): string =
|
||||||
# This proc is for TFile we want to read but don't know how many
|
# This proc is for TFile we want to read but don't know how many
|
||||||
# bytes we need to read before the buffer is empty.
|
# bytes we need to read before the buffer is empty.
|
||||||
result = ""
|
result = ""
|
||||||
var buffer = newString(buf_size)
|
var buffer = newString(BufSize)
|
||||||
var bytesRead = buf_size
|
var bytesRead = BufSize
|
||||||
while bytesRead == buf_size:
|
while bytesRead == BufSize:
|
||||||
bytesRead = readBuffer(file, addr(buffer[0]), buf_size)
|
bytesRead = readBuffer(file, addr(buffer[0]), BufSize)
|
||||||
result.add(buffer)
|
result.add(buffer)
|
||||||
|
|
||||||
proc rawFileSize(file: TFile): int =
|
proc rawFileSize(file: TFile): int =
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} =
|
||||||
if len > 0:
|
if len > 0:
|
||||||
result = rawNewString(len)
|
result = rawNewString(len)
|
||||||
result.len = len
|
result.len = len
|
||||||
c_memcpy(result.data, addr(s.data[start]), len * sizeof(Char))
|
c_memcpy(result.data, addr(s.data[start]), len * sizeof(char))
|
||||||
#result.data[len] = '\0'
|
#result.data[len] = '\0'
|
||||||
else:
|
else:
|
||||||
result = rawNewString(len)
|
result = rawNewString(len)
|
||||||
|
|
@ -66,7 +66,7 @@ proc copyStr(s: NimString, start: int): NimString {.compilerProc.} =
|
||||||
proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} =
|
proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} =
|
||||||
result = rawNewString(len)
|
result = rawNewString(len)
|
||||||
result.len = len
|
result.len = len
|
||||||
c_memcpy(result.data, str, (len+1) * sizeof(Char))
|
c_memcpy(result.data, str, (len+1) * sizeof(char))
|
||||||
#result.data[len] = '\0' # readline relies on this!
|
#result.data[len] = '\0' # readline relies on this!
|
||||||
|
|
||||||
proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} =
|
proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} =
|
||||||
|
|
@ -79,7 +79,7 @@ proc copyString(src: NimString): NimString {.compilerRtl.} =
|
||||||
else:
|
else:
|
||||||
result = rawNewString(src.space)
|
result = rawNewString(src.space)
|
||||||
result.len = src.len
|
result.len = src.len
|
||||||
c_memcpy(result.data, src.data, (src.len + 1) * sizeof(Char))
|
c_memcpy(result.data, src.data, (src.len + 1) * sizeof(char))
|
||||||
|
|
||||||
proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
|
proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
|
||||||
if src != nil:
|
if src != nil:
|
||||||
|
|
@ -236,7 +236,7 @@ proc nimIntToStr(x: int): string {.compilerRtl.} =
|
||||||
result = newString(sizeof(x)*4)
|
result = newString(sizeof(x)*4)
|
||||||
var i = 0
|
var i = 0
|
||||||
var y = x
|
var y = x
|
||||||
while True:
|
while true:
|
||||||
var d = y div 10
|
var d = y div 10
|
||||||
result[i] = chr(abs(int(y - d*10)) + ord('0'))
|
result[i] = chr(abs(int(y - d*10)) + ord('0'))
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
@ -259,7 +259,7 @@ proc nimInt64ToStr(x: int64): string {.compilerRtl.} =
|
||||||
result = newString(sizeof(x)*4)
|
result = newString(sizeof(x)*4)
|
||||||
var i = 0
|
var i = 0
|
||||||
var y = x
|
var y = x
|
||||||
while True:
|
while true:
|
||||||
var d = y div 10
|
var d = y div 10
|
||||||
result[i] = chr(abs(int(y - d*10)) + ord('0'))
|
result[i] = chr(abs(int(y - d*10)) + ord('0'))
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
|
||||||
3
todo.txt
3
todo.txt
|
|
@ -1,8 +1,8 @@
|
||||||
version 0.9.4
|
version 0.9.4
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
- convert all with "nimrod pretty": macosx and linux versions
|
||||||
- document new templating symbol binding rules
|
- document new templating symbol binding rules
|
||||||
- convert all with "nimrod pretty"
|
|
||||||
- make '--implicitStatic:on' the default
|
- make '--implicitStatic:on' the default
|
||||||
- test&finish first class iterators:
|
- test&finish first class iterators:
|
||||||
* nested iterators
|
* nested iterators
|
||||||
|
|
@ -87,7 +87,6 @@ Concurrency/Effect system
|
||||||
version 0.9.XX
|
version 0.9.XX
|
||||||
==============
|
==============
|
||||||
|
|
||||||
- document nimdoc properly finally
|
|
||||||
- make 'clamp' a magic for the range stuff
|
- make 'clamp' a magic for the range stuff
|
||||||
- better type syntax for functions and tuples: tuple(int, int); (int,int)->int
|
- better type syntax for functions and tuples: tuple(int, int); (int,int)->int
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue