version 0.7.6
This commit is contained in:
parent
439aa2d04d
commit
e792940f52
106 changed files with 8394 additions and 10083 deletions
242
cogapp.py
242
cogapp.py
|
|
@ -5,13 +5,10 @@
|
|||
"""
|
||||
|
||||
# $Id: cogapp.py 141 2008-05-22 10:56:43Z nedbat $
|
||||
# modified to run with Python1.5.2 by Andreas Rumpf
|
||||
# modified to run with Python1.5.2 and Python3.0 by Andreas Rumpf
|
||||
|
||||
import md5, os, re, string, sys, traceback, types
|
||||
import imp
|
||||
import copy, getopt, shlex
|
||||
from cStringIO import StringIO
|
||||
from string import strip, split, join, replace
|
||||
import os, re, string, sys, traceback, types, imp, copy, getopt, shlex
|
||||
from pycompab import *
|
||||
|
||||
__all__ = ['Cog', 'CogUsageError']
|
||||
|
||||
|
|
@ -37,15 +34,14 @@ OPTIONS:
|
|||
-s STRING Suffix all generated output lines with STRING.
|
||||
-U Write the output with Unix newlines (only LF line-endings).
|
||||
-w CMD Use CMD if the output file needs to be made writable.
|
||||
A %s in the CMD will be filled with the filename.
|
||||
A $1 in the CMD will be filled with the filename.
|
||||
-x Excise all the generated output without running the generators.
|
||||
-z The [[[end]]] marker can be omitted, and is assumed at eof.
|
||||
-v Print the version of cog and exit.
|
||||
-h Print this help.
|
||||
"""
|
||||
|
||||
# Get True and False right even if they aren't already defined.
|
||||
True, False = 0==0, 0==1
|
||||
gErrorMsg = "" # this is needed to support Python 1.5.2 till 3.0
|
||||
|
||||
# Other package modules
|
||||
from whiteutils import *
|
||||
|
|
@ -54,10 +50,12 @@ class CogError(Exception):
|
|||
""" Any exception raised by Cog.
|
||||
"""
|
||||
def __init__(self, msg, file='', line=0):
|
||||
global gErrorMsg
|
||||
if file:
|
||||
Exception.__init__(self, "%s(%d): %s" % (file, line, msg))
|
||||
gErrorMsg = Subs("$1($2): $2", file, line, msg)
|
||||
else:
|
||||
Exception.__init__(self, msg)
|
||||
gErrorMsg = msg
|
||||
Exception.__init__(self, gErrorMsg)
|
||||
|
||||
class CogUsageError(CogError):
|
||||
""" An error in usage of command-line arguments in cog.
|
||||
|
|
@ -97,15 +95,15 @@ class CogGenerator(Redirectable):
|
|||
self.markers = []
|
||||
self.lines = []
|
||||
|
||||
def parseMarker(self, l):
|
||||
self.markers.append(l)
|
||||
def parseMarker(self, L):
|
||||
self.markers.append(L)
|
||||
|
||||
def parseLine(self, l):
|
||||
def parseLine(self, L):
|
||||
s = 0
|
||||
e = len(l)
|
||||
while s < len(l) and l[s] == '\n': s = s + 1
|
||||
while e >= 1 and l[e-1] == '\n': e = e - 1
|
||||
self.lines.append(l[s:e+1])
|
||||
e = len(L)
|
||||
while s < len(L) and L[s] == '\n': s = s + 1
|
||||
while e >= 1 and L[e-1] == '\n': e = e - 1
|
||||
self.lines.append(L[s:e+1])
|
||||
|
||||
def getCode(self):
|
||||
""" Extract the executable Python code from the generator.
|
||||
|
|
@ -116,10 +114,10 @@ class CogGenerator(Redirectable):
|
|||
prefIn = commonPrefix(self.markers + self.lines)
|
||||
if prefIn:
|
||||
tmp = []
|
||||
for l in self.markers: tmp.append(replace(l, prefIn, '', 1))
|
||||
for L in self.markers: tmp.append(replace(L, prefIn, '', 1))
|
||||
self.markers = tmp
|
||||
tmp = []
|
||||
for l in self.lines: tmp.append(replace(l, prefIn, '', 1))
|
||||
for L in self.lines: tmp.append(replace(L, prefIn, '', 1))
|
||||
self.lines = tmp
|
||||
#self.markers = [ l.replace(prefIn, '', 1) for l in self.markers ]
|
||||
#self.lines = [ l.replace(prefIn, '', 1) for l in self.lines ]
|
||||
|
|
@ -163,7 +161,7 @@ class CogGenerator(Redirectable):
|
|||
def msg(self, s):
|
||||
self.stdout.write("Message: "+s+"\n")
|
||||
|
||||
def out(self, sOut='', dedent=False, trimblanklines=False):
|
||||
def out(self, sOut='', dedent=false, trimblanklines=false):
|
||||
""" The cog.out function.
|
||||
"""
|
||||
if trimblanklines and ('\n' in sOut):
|
||||
|
|
@ -177,7 +175,7 @@ class CogGenerator(Redirectable):
|
|||
sOut = reindentBlock(sOut)
|
||||
self.outstring = self.outstring + sOut
|
||||
|
||||
def outl(self, sOut='', dedent=False, trimblanklines=False):
|
||||
def outl(self, sOut='', dedent=false, trimblanklines=false):
|
||||
""" The cog.outl function.
|
||||
"""
|
||||
self.out(sOut, dedent, trimblanklines)
|
||||
|
|
@ -200,10 +198,10 @@ class NumberedFileReader:
|
|||
self.n = 0
|
||||
|
||||
def readline(self):
|
||||
l = self.f.readline()
|
||||
if l:
|
||||
L = self.f.readline()
|
||||
if L:
|
||||
self.n = self.n + 1
|
||||
return l
|
||||
return L
|
||||
|
||||
def linenumber(self):
|
||||
return self.n
|
||||
|
|
@ -217,17 +215,17 @@ class CogOptions:
|
|||
self.args = []
|
||||
self.includePath = []
|
||||
self.defines = {}
|
||||
self.bShowVersion = False
|
||||
self.bShowVersion = false
|
||||
self.sMakeWritableCmd = None
|
||||
self.bReplace = False
|
||||
self.bNoGenerate = False
|
||||
self.bReplace = false
|
||||
self.bNoGenerate = false
|
||||
self.sOutputName = None
|
||||
self.bWarnEmpty = False
|
||||
self.bHashOutput = False
|
||||
self.bDeleteCode = False
|
||||
self.bEofCanBeEnd = False
|
||||
self.bWarnEmpty = false
|
||||
self.bHashOutput = false
|
||||
self.bDeleteCode = false
|
||||
self.bEofCanBeEnd = false
|
||||
self.sSuffix = None
|
||||
self.bNewlines = False
|
||||
self.bNewlines = false
|
||||
|
||||
def __cmp__(self, other):
|
||||
""" Comparison operator for tests to use.
|
||||
|
|
@ -249,44 +247,44 @@ class CogOptions:
|
|||
# Parse the command line arguments.
|
||||
try:
|
||||
opts, self.args = getopt.getopt(argv, 'cdD:eI:o:rs:Uvw:xz')
|
||||
except getopt.error, msg:
|
||||
raise CogUsageError(msg)
|
||||
except getopt.error:
|
||||
raise CogUsageError("invalid command line")
|
||||
|
||||
# Handle the command line arguments.
|
||||
for o, a in opts:
|
||||
if o == '-c':
|
||||
self.bHashOutput = True
|
||||
self.bHashOutput = true
|
||||
elif o == '-d':
|
||||
self.bDeleteCode = True
|
||||
self.bDeleteCode = true
|
||||
elif o == '-D':
|
||||
if a.count('=') < 1:
|
||||
raise CogUsageError("-D takes a name=value argument")
|
||||
name, value = split(a, '=', 1)
|
||||
self.defines[name] = value
|
||||
elif o == '-e':
|
||||
self.bWarnEmpty = True
|
||||
self.bWarnEmpty = true
|
||||
elif o == '-I':
|
||||
self.addToIncludePath(a)
|
||||
elif o == '-o':
|
||||
self.sOutputName = a
|
||||
elif o == '-r':
|
||||
self.bReplace = True
|
||||
self.bReplace = true
|
||||
elif o == '-s':
|
||||
self.sSuffix = a
|
||||
elif o == '-U':
|
||||
self.bNewlines = True
|
||||
self.bNewlines = true
|
||||
elif o == '-v':
|
||||
self.bShowVersion = True
|
||||
self.bShowVersion = true
|
||||
elif o == '-w':
|
||||
self.sMakeWritableCmd = a
|
||||
elif o == '-x':
|
||||
self.bNoGenerate = True
|
||||
self.bNoGenerate = true
|
||||
elif o == '-z':
|
||||
self.bEofCanBeEnd = True
|
||||
self.bEofCanBeEnd = true
|
||||
else:
|
||||
# Since getopt.getopt is given a list of possible flags,
|
||||
# this is an internal error.
|
||||
raise CogInternalError("Don't understand argument %s" % o)
|
||||
raise CogInternalError(Subs("Don't understand argument $1", o))
|
||||
|
||||
def validate(self):
|
||||
""" Does nothing if everything is OK, raises CogError's if it's not.
|
||||
|
|
@ -297,15 +295,6 @@ class CogOptions:
|
|||
if self.bReplace and self.sOutputName:
|
||||
raise CogUsageError("Can't use -o with -r (they are opposites)")
|
||||
|
||||
|
||||
def mydigest(hasher):
|
||||
result = ""
|
||||
for c in hasher.digest():
|
||||
x = hex(ord(c))[2:]
|
||||
if len(x) == 1: x = "0" + x
|
||||
result = result + x
|
||||
return result
|
||||
|
||||
class Cog(Redirectable):
|
||||
""" The Cog engine.
|
||||
"""
|
||||
|
|
@ -315,7 +304,7 @@ class Cog(Redirectable):
|
|||
self.sEndSpec = ']]]'
|
||||
self.sEndOutput = '[[[end]]]'
|
||||
self.reEndOutput = re.compile(r'\[\[\[end]]](?P<hashsect> *\(checksum: (?P<hash>[a-f0-9]+)\))')
|
||||
self.sEndFormat = '[[[end]]] (checksum: %s)'
|
||||
self.sEndFormat = '[[[end]]] (checksum: $1)'
|
||||
|
||||
self.options = CogOptions()
|
||||
self.sOutputMode = 'w'
|
||||
|
|
@ -326,14 +315,14 @@ class Cog(Redirectable):
|
|||
self.stdout.write("Warning: " + msg + "\n")
|
||||
|
||||
def isBeginSpecLine(self, s):
|
||||
return string.find(s, self.sBeginSpec) >= 0
|
||||
return find(s, self.sBeginSpec) >= 0
|
||||
|
||||
def isEndSpecLine(self, s):
|
||||
return string.find(s, self.sEndSpec) >= 0 and \
|
||||
return find(s, self.sEndSpec) >= 0 and \
|
||||
not self.isEndOutputLine(s)
|
||||
|
||||
def isEndOutputLine(self, s):
|
||||
return string.find(s, self.sEndOutput) >= 0
|
||||
return find(s, self.sEndOutput) >= 0
|
||||
|
||||
def installCogModule(self):
|
||||
""" Magic mumbo-jumbo so that imported Python modules
|
||||
|
|
@ -362,7 +351,7 @@ class Cog(Redirectable):
|
|||
|
||||
fIn = NumberedFileReader(fIn)
|
||||
|
||||
bSawCog = False
|
||||
bSawCog = false
|
||||
|
||||
self.cogmodule.inFile = sFileIn
|
||||
self.cogmodule.outFile = sFileOut
|
||||
|
|
@ -375,102 +364,102 @@ class Cog(Redirectable):
|
|||
globals.update(self.options.defines)
|
||||
|
||||
# loop over generator chunks
|
||||
l = fIn.readline()
|
||||
while l:
|
||||
L = fIn.readline()
|
||||
while L:
|
||||
# Find the next spec begin
|
||||
while l and not self.isBeginSpecLine(l):
|
||||
if self.isEndSpecLine(l):
|
||||
raise CogError("Unexpected '%s'" % self.sEndSpec,
|
||||
while L and not self.isBeginSpecLine(L):
|
||||
if self.isEndSpecLine(L):
|
||||
raise CogError(Subs("Unexpected '$1'", self.sEndSpec),
|
||||
file=sFileIn, line=fIn.linenumber())
|
||||
if self.isEndOutputLine(l):
|
||||
raise CogError("Unexpected '%s'" % self.sEndOutput,
|
||||
if self.isEndOutputLine(L):
|
||||
raise CogError(Subs("Unexpected '$1'", self.sEndOutput),
|
||||
file=sFileIn, line=fIn.linenumber())
|
||||
fOut.write(l)
|
||||
l = fIn.readline()
|
||||
if not l:
|
||||
fOut.write(L)
|
||||
L = fIn.readline()
|
||||
if not L:
|
||||
break
|
||||
if not self.options.bDeleteCode:
|
||||
fOut.write(l)
|
||||
fOut.write(L)
|
||||
|
||||
# l is the begin spec
|
||||
# L is the begin spec
|
||||
gen = CogGenerator()
|
||||
gen.setOutput(stdout=self.stdout)
|
||||
gen.parseMarker(l)
|
||||
gen.parseMarker(L)
|
||||
firstLineNum = fIn.linenumber()
|
||||
self.cogmodule.firstLineNum = firstLineNum
|
||||
|
||||
# If the spec begin is also a spec end, then process the single
|
||||
# line of code inside.
|
||||
if self.isEndSpecLine(l):
|
||||
beg = string.find(l, self.sBeginSpec)
|
||||
end = string.find(l, self.sEndSpec)
|
||||
if self.isEndSpecLine(L):
|
||||
beg = find(L, self.sBeginSpec)
|
||||
end = find(L, self.sEndSpec)
|
||||
if beg > end:
|
||||
raise CogError("Cog code markers inverted",
|
||||
file=sFileIn, line=firstLineNum)
|
||||
else:
|
||||
sCode = strip(l[beg+len(self.sBeginSpec):end])
|
||||
sCode = strip(L[beg+len(self.sBeginSpec):end])
|
||||
gen.parseLine(sCode)
|
||||
else:
|
||||
# Deal with an ordinary code block.
|
||||
l = fIn.readline()
|
||||
L = fIn.readline()
|
||||
|
||||
# Get all the lines in the spec
|
||||
while l and not self.isEndSpecLine(l):
|
||||
if self.isBeginSpecLine(l):
|
||||
raise CogError("Unexpected '%s'" % self.sBeginSpec,
|
||||
while L and not self.isEndSpecLine(L):
|
||||
if self.isBeginSpecLine(L):
|
||||
raise CogError(Subs("Unexpected '$1'", self.sBeginSpec),
|
||||
file=sFileIn, line=fIn.linenumber())
|
||||
if self.isEndOutputLine(l):
|
||||
raise CogError("Unexpected '%s'" % self.sEndOutput,
|
||||
if self.isEndOutputLine(L):
|
||||
raise CogError(Subs("Unexpected '$1'", self.sEndOutput),
|
||||
file=sFileIn, line=fIn.linenumber())
|
||||
if not self.options.bDeleteCode:
|
||||
fOut.write(l)
|
||||
gen.parseLine(l)
|
||||
l = fIn.readline()
|
||||
if not l:
|
||||
fOut.write(L)
|
||||
gen.parseLine(L)
|
||||
L = fIn.readline()
|
||||
if not L:
|
||||
raise CogError(
|
||||
"Cog block begun but never ended.",
|
||||
file=sFileIn, line=firstLineNum)
|
||||
|
||||
if not self.options.bDeleteCode:
|
||||
fOut.write(l)
|
||||
gen.parseMarker(l)
|
||||
fOut.write(L)
|
||||
gen.parseMarker(L)
|
||||
|
||||
l = fIn.readline()
|
||||
L = fIn.readline()
|
||||
|
||||
# Eat all the lines in the output section. While reading past
|
||||
# them, compute the md5 hash of the old output.
|
||||
hasher = md5.new()
|
||||
while l and not self.isEndOutputLine(l):
|
||||
if self.isBeginSpecLine(l):
|
||||
raise CogError("Unexpected '%s'" % self.sBeginSpec,
|
||||
hasher = newMD5()
|
||||
while L and not self.isEndOutputLine(L):
|
||||
if self.isBeginSpecLine(L):
|
||||
raise CogError(Subs("Unexpected '$1'", self.sBeginSpec),
|
||||
file=sFileIn, line=fIn.linenumber())
|
||||
if self.isEndSpecLine(l):
|
||||
raise CogError("Unexpected '%s'" % self.sEndSpec,
|
||||
if self.isEndSpecLine(L):
|
||||
raise CogError(Subs("Unexpected '$1'", self.sEndSpec),
|
||||
file=sFileIn, line=fIn.linenumber())
|
||||
hasher.update(l)
|
||||
l = fIn.readline()
|
||||
MD5update(hasher, L)
|
||||
L = fIn.readline()
|
||||
curHash = mydigest(hasher)
|
||||
|
||||
if not l and not self.options.bEofCanBeEnd:
|
||||
if not L and not self.options.bEofCanBeEnd:
|
||||
# We reached end of file before we found the end output line.
|
||||
raise CogError("Missing '%s' before end of file." % self.sEndOutput,
|
||||
raise CogError(Subs("Missing '$1' before end of file.", self.sEndOutput),
|
||||
file=sFileIn, line=fIn.linenumber())
|
||||
|
||||
# Write the output of the spec to be the new output if we're
|
||||
# supposed to generate code.
|
||||
hasher = md5.new()
|
||||
hasher = newMD5()
|
||||
if not self.options.bNoGenerate:
|
||||
sFile = "%s+%d" % (sFileIn, firstLineNum)
|
||||
sFile = Subs("$1+$2", sFileIn, firstLineNum)
|
||||
sGen = gen.evaluate(cog=self, globals=globals, fname=sFile)
|
||||
sGen = self.suffixLines(sGen)
|
||||
hasher.update(sGen)
|
||||
MD5update(hasher, sGen)
|
||||
fOut.write(sGen)
|
||||
newHash = mydigest(hasher)
|
||||
|
||||
bSawCog = True
|
||||
bSawCog = true
|
||||
|
||||
# Write the ending output line
|
||||
hashMatch = self.reEndOutput.search(l)
|
||||
hashMatch = self.reEndOutput.search(L)
|
||||
if self.options.bHashOutput:
|
||||
if hashMatch:
|
||||
oldHash = hashMatch.groupdict()['hash']
|
||||
|
|
@ -478,23 +467,23 @@ class Cog(Redirectable):
|
|||
raise CogError("Output has been edited! Delete old checksum to unprotect.",
|
||||
file=sFileIn, line=fIn.linenumber())
|
||||
# Create a new end line with the correct hash.
|
||||
endpieces = split(l, hashMatch.group(0), 1)
|
||||
endpieces = split(L, hashMatch.group(0), 1)
|
||||
else:
|
||||
# There was no old hash, but we want a new hash.
|
||||
endpieces = split(l, self.sEndOutput, 1)
|
||||
l = join(endpieces, (self.sEndFormat % newHash))
|
||||
endpieces = split(L, self.sEndOutput, 1)
|
||||
L = join(endpieces, Subs(self.sEndFormat, newHash))
|
||||
else:
|
||||
# We don't want hashes output, so if there was one, get rid of
|
||||
# it.
|
||||
if hashMatch:
|
||||
l = replace(l, hashMatch.groupdict()['hashsect'], '', 1)
|
||||
L = replace(L, hashMatch.groupdict()['hashsect'], '', 1)
|
||||
|
||||
if not self.options.bDeleteCode:
|
||||
fOut.write(l)
|
||||
l = fIn.readline()
|
||||
fOut.write(L)
|
||||
L = fIn.readline()
|
||||
|
||||
if not bSawCog and self.options.bWarnEmpty:
|
||||
self.showWarning("no cog code found in %s" % sFileIn)
|
||||
self.showWarning("no cog code found in " + sFileIn)
|
||||
|
||||
# A regex for non-empty lines, used by suffixLines.
|
||||
reNonEmptyLines = re.compile("^\s*\S+.*$", re.MULTILINE)
|
||||
|
|
@ -525,13 +514,13 @@ class Cog(Redirectable):
|
|||
# Need to ensure we can write.
|
||||
if self.options.sMakeWritableCmd:
|
||||
# Use an external command to make the file writable.
|
||||
cmd = replace(self.options.sMakeWritableCmd, '%s', sOldPath)
|
||||
cmd = replace(self.options.sMakeWritableCmd, '$1', sOldPath)
|
||||
self.stdout.write(os.popen(cmd).read())
|
||||
if not os.access(sOldPath, os.W_OK):
|
||||
raise CogError("Couldn't make %s writable" % sOldPath)
|
||||
raise CogError(Subs("Couldn't make $1 writable", sOldPath))
|
||||
else:
|
||||
# Can't write!
|
||||
raise CogError("Can't overwrite %s" % sOldPath)
|
||||
raise CogError("Can't overwrite " + sOldPath)
|
||||
f = open(sOldPath, self.sOutputMode)
|
||||
f.write(sNewText)
|
||||
f.close()
|
||||
|
|
@ -573,8 +562,8 @@ class Cog(Redirectable):
|
|||
elif self.options.bReplace:
|
||||
# We want to replace the cog file with the output,
|
||||
# but only if they differ.
|
||||
self.stdout.write("Cogging %s" % sFile)
|
||||
bNeedNewline = True
|
||||
self.stdout.write("Cogging " + sFile)
|
||||
bNeedNewline = true
|
||||
|
||||
try:
|
||||
fOldFile = open(sFile)
|
||||
|
|
@ -583,7 +572,7 @@ class Cog(Redirectable):
|
|||
sNewText = self.processString(sOldText, fname=sFile)
|
||||
if sOldText != sNewText:
|
||||
self.stdout.write(" (changed)\n")
|
||||
bNeedNewline = False
|
||||
bNeedNewline = false
|
||||
self.replaceFile(sFile, sNewText)
|
||||
finally:
|
||||
# The try-finally block is so we can print a partial line
|
||||
|
|
@ -600,10 +589,10 @@ class Cog(Redirectable):
|
|||
def processFileList(self, sFileList):
|
||||
""" Process the files in a file list.
|
||||
"""
|
||||
for l in open(sFileList).readlines():
|
||||
for L in open(sFileList).readlines():
|
||||
# Use shlex to parse the line like a shell.
|
||||
lex = shlex.shlex(l, posix=True)
|
||||
lex.whitespace_split = True
|
||||
lex = shlex.shlex(L, posix=true)
|
||||
lex.whitespace_split = true
|
||||
lex.commenters = '#'
|
||||
# No escapes, so that backslash can be part of the path
|
||||
lex.escape = ''
|
||||
|
|
@ -645,7 +634,7 @@ class Cog(Redirectable):
|
|||
self.options.validate()
|
||||
|
||||
if self.options.bShowVersion:
|
||||
self.stdout.write("Cog version %s\n" % __version__)
|
||||
self.stdout.write(Subs("Cog version $1\n", __version__))
|
||||
return
|
||||
|
||||
if self.options.args:
|
||||
|
|
@ -657,19 +646,20 @@ class Cog(Redirectable):
|
|||
def main(self, argv):
|
||||
""" Handle the command-line execution for cog.
|
||||
"""
|
||||
global gErrorMsg
|
||||
|
||||
try:
|
||||
self.callableMain(argv)
|
||||
return 0
|
||||
except CogUsageError, err:
|
||||
self.stderr.write(err + "\n")
|
||||
except CogUsageError:
|
||||
self.stderr.write(gErrorMsg + "\n")
|
||||
self.stderr.write("(for help use -?)\n")
|
||||
return 2
|
||||
except CogGeneratedError, err:
|
||||
self.stderr.write("Error: %s\n" % err)
|
||||
except CogGeneratedError:
|
||||
self.stderr.write(Subs("Error: $1\n", gErrorMsg))
|
||||
return 3
|
||||
except CogError, err:
|
||||
self.stderr.write(err + "\n")
|
||||
except CogError:
|
||||
self.stderr.write(gErrorMsg + "\n")
|
||||
return 1
|
||||
except:
|
||||
traceback.print_exc(None, self.stderr)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# This is the config file for the documentation generator.
|
||||
# (c) 2008 Andreas Rumpf
|
||||
# (c) 2009 Andreas Rumpf
|
||||
# Feel free to edit the templates as you need.
|
||||
|
||||
split.item.toc = "20"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# Configuration file for the Nimrod Compiler.
|
||||
# (c) 2008 Andreas Rumpf
|
||||
# (c) 2009 Andreas Rumpf
|
||||
|
||||
# Feel free to edit the default values as you need.
|
||||
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
# @putenv "key" "val"
|
||||
# Environment variables cannot be used in the options, however!
|
||||
|
||||
cc = @if windows: llvm_gcc @else: gcc @end
|
||||
cc = gcc
|
||||
lib="$nimrod/lib"
|
||||
path="$lib/base"
|
||||
path="$lib/base/gtk"
|
||||
|
|
@ -68,10 +68,8 @@ vcc.options.size = "/O1"
|
|||
|
||||
# Configuration for the GNU C/C++ compiler:
|
||||
@if windows:
|
||||
gcc.path = r"C:\eigenes\compiler\mingw\bin"
|
||||
gcc.path = r"$nimrod\dist\mingw\bin"
|
||||
@end
|
||||
#gcc.exe = "gcc-4.1"
|
||||
#gcc.linkerExe = "gcc-4.1"
|
||||
gcc.options.debug = "-g"
|
||||
@if macosx:
|
||||
gcc.options.always = "-w -fasm-blocks"
|
||||
|
|
@ -83,13 +81,8 @@ gcc.options.size = "-Os"
|
|||
|
||||
# Configuration for the Digital Mars C/C++ compiler:
|
||||
@if windows:
|
||||
dmc.path = r"C:\eigenes\compiler\d\dm\bin"
|
||||
dmc.path = r"$nimrod\dist\dm\bin"
|
||||
@end
|
||||
|
||||
# Configuration for the Tiny C Compiler:
|
||||
@if windows:
|
||||
tcc.path = r"C:\Eigenes\compiler\tcc-0.9.23\tcc"
|
||||
tcc.options.always = r"-IC:\Eigenes\compiler\tcc-0.9.23\include " &
|
||||
r"-IC:\Eigenes\compiler\tcc-0.9.23\include\winapi"
|
||||
@end
|
||||
tcc.options.always = "-w"
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ Advanced options:
|
|||
--checkpoints:on|off turn on|off checkpoints; for debugging Nimrod
|
||||
--skip_cfg do not read the general configuration file
|
||||
--skip_proj_cfg do not read the project's configuration file
|
||||
--import:MODULE_FILE import the given module implicitly for each module
|
||||
--index:FILE use FILE to generate a documenation index file
|
||||
--putenv:key=value set an environment variable
|
||||
--list_cmd list the commands used to execute external programs
|
||||
|
|
|
|||
|
|
@ -212,7 +212,6 @@
|
|||
'nkFromStmt', # a from * import statement
|
||||
'nkImportAs', # an `import xyx as abc` section
|
||||
'nkIncludeStmt', # an include statement
|
||||
'nkAccessStmt', # used internally for iterators
|
||||
'nkCommentStmt', # a comment statement
|
||||
'nkStmtListExpr', # a statement list followed by an expr; this is used
|
||||
# to allow powerful multi-line templates
|
||||
|
|
|
|||
|
|
@ -19,14 +19,14 @@ Options:
|
|||
--debugger:on|off turn Embedded Nimrod Debugger ON|OFF
|
||||
-x, --checks:on|off code generation for all runtime checks ON|OFF
|
||||
--obj_checks:on|off code generation for obj conversion checks ON|OFF
|
||||
--field_checks:on|off code generation for case record fields ON|OFF
|
||||
--field_checks:on|off code generation for case variant fields ON|OFF
|
||||
--range_checks:on|off code generation for range checks ON|OFF
|
||||
--bound_checks:on|off code generation for bound checks ON|OFF
|
||||
--overflow_checks:on|off code generation for over-/underflow checks ON|OFF
|
||||
-a, --assertions:on|off code generation for assertions ON|OFF
|
||||
--dead_code_elim:on|off whole program dead code elimination ON|OFF
|
||||
--opt:none|speed|size optimize not at all or for speed|size
|
||||
--app:console|gui|lib generate a console|GUI application or a shared lib
|
||||
--app:console|gui generate a console|GUI application
|
||||
-r, --run run the compiled program with given arguments
|
||||
--advanced show advanced command line switches
|
||||
-h, --help show this help
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# This file contains all the messages of the Nimrod compiler
|
||||
# (c) 2008 Andreas Rumpf
|
||||
# (c) 2009 Andreas Rumpf
|
||||
|
||||
[
|
||||
# fatal errors:
|
||||
|
|
@ -208,7 +208,7 @@
|
|||
{'errXNotAllowedHere': '$1 here not allowed'},
|
||||
{'errInvalidControlFlowX': 'invalid control flow: $1'},
|
||||
{'errATypeHasNoValue': 'a type has no value'},
|
||||
{'errXisNoType': "'$1' is no type"},
|
||||
{'errXisNoType': "invalid type: '$1'"},
|
||||
{'errCircumNeedsPointer': "'^' needs a pointer or reference type"},
|
||||
{'errInvalidContextForBuiltinX': "invalid context for builtin '$1'"},
|
||||
{'errInvalidExpression': 'invalid expression'},
|
||||
|
|
@ -254,6 +254,7 @@
|
|||
{'errXRequiresOneArgument': "converter requires one parameter"},
|
||||
{'errUnhandledExceptionX': "unhandled exception: $1"},
|
||||
{'errCyclicTree': "macro returned a cyclic abstract syntax tree"},
|
||||
{'errXisNoMacroOrTemplate': "'$1' is no macro or template"},
|
||||
|
||||
# user error message:
|
||||
{'errUser': '$1'},
|
||||
|
|
|
|||
21
doc/docs.txt
21
doc/docs.txt
|
|
@ -1,28 +1,25 @@
|
|||
"Incorrect documentation is often worse than no documentation."
|
||||
Incorrect documentation is often worse than no documentation.
|
||||
-- Bertrand Meyer
|
||||
|
||||
The documentation consists of several documents:
|
||||
|
||||
- | `Nimrod tutorial (part I) <tut1.html>`_
|
||||
- | `Tutorial (part I) <tut1.html>`_
|
||||
| The Nimrod tutorial part one deals with the basics.
|
||||
|
||||
- | `Nimrod tutorial (part II) <tut2.html>`_
|
||||
- | `Tutorial (part II) <tut2.html>`_
|
||||
| The Nimrod tutorial part two deals with the advanced language constructs.
|
||||
|
||||
- | `Nimrod manual <manual.html>`_
|
||||
| The Nimrod manual is a draft that will evolve into a proper specification.
|
||||
- | `Library documentation <lib.html>`_
|
||||
| This document describes Nimrod's standard library.
|
||||
|
||||
- | `User guide for the Nimrod Compiler <nimrodc.html>`_
|
||||
- | `User guide <nimrodc.html>`_
|
||||
| The user guide lists command line arguments, special features of the
|
||||
compiler, etc.
|
||||
|
||||
- | `User guide for the Embedded Nimrod Debugger <endb.html>`_
|
||||
| This document describes how to use the Embedded Debugger.
|
||||
- | `Manual <manual.html>`_
|
||||
| The Nimrod manual is a draft that will evolve into a proper specification.
|
||||
|
||||
- | `Nimrod library documentation <lib.html>`_
|
||||
| This document describes Nimrod's standard library.
|
||||
|
||||
- | `Nimrod internal documentation <intern.html>`_
|
||||
- | `Internal documentation <intern.html>`_
|
||||
| The internal documentation describes how the compiler is implemented. Read
|
||||
this if you want to hack the compiler.
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Short description of Nimrod's modules
|
|||
Module Description
|
||||
============== ==========================================================
|
||||
nimrod main module: parses the command line and calls
|
||||
```main.MainCommand``
|
||||
``main.MainCommand``
|
||||
main implements the top-level command dispatching
|
||||
lexbase buffer handling of the lexical analyser
|
||||
scanner lexical analyser
|
||||
|
|
@ -31,6 +31,7 @@ sigmatch contains the matching algorithm that is used for proc
|
|||
semexprs contains the semantic checking phase for expressions
|
||||
semstmts contains the semantic checking phase for statements
|
||||
semtypes contains the semantic checking phase for types
|
||||
seminst instantiation of generic procs and types
|
||||
semfold contains code to deal with constant folding
|
||||
evals contains an AST interpreter for compile time evaluation
|
||||
pragmas semantic checking of pragmas
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ ifStmt ::= 'if' expr ':' stmt ('elif' expr ':' stmt)* ['else' ':' stmt]
|
|||
whenStmt ::= 'when' expr ':' stmt ('elif' expr ':' stmt)* ['else' ':' stmt]
|
||||
caseStmt ::= 'case' expr [':'] ('of' sliceExprList ':' stmt)*
|
||||
('elif' expr ':' stmt)*
|
||||
['else' ':' stmt]
|
||||
['else' ':' stmt]
|
||||
whileStmt ::= 'while' expr ':' stmt
|
||||
forStmt ::= 'for' symbol (comma symbol)* 'in' expr ['..' expr] ':' stmt
|
||||
exceptList ::= [qualifiedIdent (comma qualifiedIdent)*]
|
||||
|
|
|
|||
50
doc/lib.txt
50
doc/lib.txt
|
|
@ -6,12 +6,15 @@ Nimrod Standard Library
|
|||
:Version: |nimrodversion|
|
||||
|
||||
Though the Nimrod Standard Library is still evolving, it is already quite
|
||||
usable. It is divided into basic libraries that contains modules that virtually
|
||||
every program will need and advanced libraries which are more heavy weight.
|
||||
Advanced libraries are in the ``lib/base`` directory.
|
||||
usable. It is divided into *pure libraries*, *impure libraries* and *wrappers*.
|
||||
|
||||
Basic libraries
|
||||
===============
|
||||
Pure libraries do not depend on any external ``*.dll`` or ``lib*.so`` binary
|
||||
while impure libraries do. A wrapper is an impure library that is a very
|
||||
low-level interface to a C library.
|
||||
|
||||
|
||||
Pure libraries
|
||||
==============
|
||||
|
||||
* `system <system.html>`_
|
||||
Basic procs and operators that every program needs. It also provides IO
|
||||
|
|
@ -51,6 +54,12 @@ Basic libraries
|
|||
literals, raw string literals and triple quote string literals are supported
|
||||
as in the Nimrod programming language.
|
||||
|
||||
* `parsexml <parsexml.html>`_
|
||||
The ``parsexml`` module implements a simple high performance XML/HTML parser.
|
||||
The only encoding that is supported is UTF-8. The parser has been designed
|
||||
to be somewhat error correcting, so that even some "wild HTML" found on the
|
||||
web can be parsed with it.
|
||||
|
||||
* `strtabs <strtabs.html>`_
|
||||
The ``strtabs`` module implements an efficient hash table that is a mapping
|
||||
from strings to strings. Supports a case-sensitive, case-insensitive and
|
||||
|
|
@ -68,17 +77,31 @@ Basic libraries
|
|||
Nimrod types.
|
||||
|
||||
* `lexbase <lexbase.html>`_
|
||||
This is a low level module that implements an extremely efficent buffering
|
||||
This is a low level module that implements an extremely efficient buffering
|
||||
scheme for lexers and parsers. This is used by the ``parsecfg`` module.
|
||||
|
||||
* `terminal <terminal.html>`_
|
||||
This module contains a few procedures to control the *terminal*
|
||||
(also called *console*). The implementation simply uses ANSI escape
|
||||
sequences and does not depend on any other module.
|
||||
|
||||
Advanced libaries
|
||||
=================
|
||||
* `cgi <cgi.html>`_
|
||||
This module implements helper procs for CGI applictions.
|
||||
|
||||
* `unicode <unicode.html>`_
|
||||
This module provides support to handle the Unicode UTF-8 encoding.
|
||||
|
||||
* `regexprs <regexprs.html>`_
|
||||
This module contains procedures and operators for handling regular
|
||||
expressions.
|
||||
|
||||
* `md5 <md5.html>`_
|
||||
This module implements the MD5 checksum algorithm.
|
||||
|
||||
|
||||
Impure libraries
|
||||
================
|
||||
|
||||
* `dialogs <dialogs.html>`_
|
||||
This module implements portable dialogs for Nimrod; the implementation
|
||||
builds on the GTK interface. On Windows, native dialogs are shown if
|
||||
|
|
@ -86,6 +109,11 @@ Advanced libaries
|
|||
|
||||
* `zipfiles <zipfiles.html>`_
|
||||
This module implements a zip archive creator/reader/modifier.
|
||||
|
||||
* `web <web.html>`_
|
||||
This module contains simple high-level procedures for dealing with the
|
||||
web like loading the contents of a web page from an URL.
|
||||
|
||||
|
||||
Wrappers
|
||||
========
|
||||
|
|
@ -97,6 +125,12 @@ not contained in the distribution. You can then find them on the website.
|
|||
Contains a wrapper for the POSIX standard.
|
||||
* `windows <windows.html>`_
|
||||
Contains a wrapper for the Win32 API.
|
||||
* `mysql <mysql.html>`_
|
||||
Contains a wrapper for the mySQL API.
|
||||
* `sqlite3 <sqlite3.html>`_
|
||||
Contains a wrapper for SQLite 3 API.
|
||||
* `libcurl <libcurl.html>`_
|
||||
Contains a wrapper for the libcurl library.
|
||||
* `shellapi <shellapi.html>`_
|
||||
Contains a wrapper for the ``shellapi.h`` header.
|
||||
* `shfolder <shfolder.html>`_
|
||||
|
|
|
|||
|
|
@ -1011,8 +1011,8 @@ char '\0'
|
|||
bool false
|
||||
ref or pointer type nil
|
||||
procedural type nil
|
||||
sequence nil (**not** ``@[]``)
|
||||
string nil (**not** "")
|
||||
sequence nil (*not* ``@[]``)
|
||||
string nil (*not* "")
|
||||
tuple[x: A, y: B, ...] (default(A), default(B), ...)
|
||||
(analogous for objects)
|
||||
array[0..., T] [default(T), ...]
|
||||
|
|
@ -1080,9 +1080,9 @@ Case statement
|
|||
|
||||
Syntax::
|
||||
|
||||
caseStmt ::= 'case' expr ('of' sliceExprList ':' stmt)*
|
||||
('elif' expr ':' stmt)*
|
||||
['else' ':' stmt]
|
||||
caseStmt ::= 'case' expr [':'] ('of' sliceExprList ':' stmt)*
|
||||
('elif' expr ':' stmt)*
|
||||
['else' ':' stmt]
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -1241,11 +1241,11 @@ sugar for:
|
|||
``return`` without an expression is a short notation for ``return result`` if
|
||||
the proc has a return type. The `result`:idx: variable is always the return
|
||||
value of the procedure. It is automatically declared by the compiler. As all
|
||||
variables, ``result`` is initialized to (binary) zero::
|
||||
variables, ``result`` is initialized to (binary) zero:
|
||||
|
||||
.. code-block:: nimrod
|
||||
proc returnZero(): int =
|
||||
# implicitely returns 0
|
||||
proc returnZero(): int =
|
||||
# implicitely returns 0
|
||||
|
||||
|
||||
Yield statement
|
||||
|
|
@ -1649,7 +1649,7 @@ possible within a single ``type`` section.
|
|||
Generics
|
||||
~~~~~~~~
|
||||
|
||||
`Version 0.7.4: Complex generic types like in the example do not work.`:red:
|
||||
`Version 0.7.6: Generic types like in the example do not work.`:red:
|
||||
|
||||
Example:
|
||||
|
||||
|
|
|
|||
|
|
@ -208,8 +208,9 @@ Acyclic Pragma
|
|||
~~~~~~~~~~~~~~
|
||||
The `acyclic`:idx: pragma can be used for object types to mark them as acyclic
|
||||
even though they seem to be cyclic. This is an **optimization** for the garbage
|
||||
collector to not consider objects of this type as part of a cycle::
|
||||
collector to not consider objects of this type as part of a cycle:
|
||||
|
||||
.. code-block:: nimrod
|
||||
type
|
||||
PNode = ref TNode
|
||||
TNode {.acyclic, final.} = object
|
||||
|
|
|
|||
12308
doc/theindex.txt
12308
doc/theindex.txt
File diff suppressed because it is too large
Load diff
110
doc/tut1.txt
110
doc/tut1.txt
|
|
@ -46,12 +46,14 @@ The most used commands and switches have abbreviations, so you can also use::
|
|||
|
||||
Though it should be pretty obvious what the program does, I will explain the
|
||||
syntax: Statements which are not indented are executed when the program
|
||||
starts. Indentation is Nimrod's way of grouping statements. String literals
|
||||
are enclosed in double quotes. The ``var`` statement declares a new variable
|
||||
named ``name`` of type ``string`` with the value that is returned by the
|
||||
``readline`` procedure. Since the compiler knows that ``readline`` returns
|
||||
a string, you can leave out the type in the declaration (this is called
|
||||
`local type inference`:idx:). So this will work too:
|
||||
starts. Indentation is Nimrod's way of grouping statements. Indentation is
|
||||
done with spaces only, tabulators are not allowed.
|
||||
|
||||
String literals are enclosed in double quotes. The ``var`` statement declares
|
||||
a new variable named ``name`` of type ``string`` with the value that is
|
||||
returned by the ``readline`` procedure. Since the compiler knows that
|
||||
``readline`` returns a string, you can leave out the type in the declaration
|
||||
(this is called `local type inference`:idx:). So this will work too:
|
||||
|
||||
.. code-block:: Nimrod
|
||||
var name = readline(stdin)
|
||||
|
|
@ -73,7 +75,7 @@ keywords, comments, operators, and other punctation marks. Case is
|
|||
*insignificant* in Nimrod and even underscores are ignored:
|
||||
``This_is_an_identifier`` and this is the same identifier
|
||||
``ThisIsAnIdentifier``. This feature enables you to use other
|
||||
peoples code without bothering about a naming convention that conflicts with
|
||||
people's code without bothering about a naming convention that conflicts with
|
||||
yours. It also frees you from remembering the exact spelling of an identifier
|
||||
(was it ``parseURL`` or ``parseUrl`` or ``parse_URL``?).
|
||||
|
||||
|
|
@ -129,6 +131,9 @@ the syntax, watch their indentation:
|
|||
Echo("Hi!")
|
||||
# comment has not the right indentation -> syntax error!
|
||||
|
||||
**Note**: To comment out a large piece of code, it is often better to use a
|
||||
``when false:`` statement.
|
||||
|
||||
|
||||
Numbers
|
||||
-------
|
||||
|
|
@ -137,7 +142,7 @@ Numerical literals are written as in most other languages. As a special twist,
|
|||
underscores are allowed for better readability: ``1_000_000`` (one million).
|
||||
A number that contains a dot (or 'e' or 'E') is a floating point literal:
|
||||
``1.0e9`` (one million). Hexadecimal literals are prefixed with ``0x``,
|
||||
binary literals with ``0b`` and octal literals with ``0c``. A leading zero
|
||||
binary literals with ``0b`` and octal literals with ``0o``. A leading zero
|
||||
alone does not produce an octal.
|
||||
|
||||
|
||||
|
|
@ -426,7 +431,11 @@ The ``when`` statement is useful for writing platform specific code, similar to
|
|||
the ``#ifdef`` construct in the C programming language.
|
||||
|
||||
**Note**: The documentation generator currently always follows the first branch
|
||||
of when statements.
|
||||
of when statements.
|
||||
|
||||
**Note**: To comment out a large piece of code, it is often better to use a
|
||||
``when false:`` statement than to use real comments. This way nesting is
|
||||
possible.
|
||||
|
||||
|
||||
Statements and indentation
|
||||
|
|
@ -440,7 +449,7 @@ statements*. *Simple statements* cannot contain other statements:
|
|||
Assignment, procedure calls or the ``return`` statement belong to the simple
|
||||
statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can
|
||||
contain other statements. To avoid ambiguities, complex statements always have
|
||||
to be intended, but single simple statements do not:
|
||||
to be indented, but single simple statements do not:
|
||||
|
||||
.. code-block:: nimrod
|
||||
# no indentation needed for single assignment statement:
|
||||
|
|
@ -518,9 +527,11 @@ shorthand for ``return result``. So all tree code snippets are equivalent:
|
|||
.. code-block:: nimrod
|
||||
return 42
|
||||
|
||||
.. code-block:: nimrod
|
||||
result = 42
|
||||
return
|
||||
|
||||
.. code-block:: nimrod
|
||||
result = 42
|
||||
return result
|
||||
|
||||
|
|
@ -880,7 +891,7 @@ In Nimrod new types can be defined within a ``type`` statement:
|
|||
.. code-block:: nimrod
|
||||
type
|
||||
biggestInt = int64 # biggest integer type that is available
|
||||
biggestFLoat = float64 # biggest float type that is available
|
||||
biggestFloat = float64 # biggest float type that is available
|
||||
|
||||
Enumeration and object types cannot be defined on the fly, but only within a
|
||||
``type`` statement.
|
||||
|
|
@ -980,7 +991,7 @@ basetype can only be an ordinal type. The reason is that sets are implemented
|
|||
as high performance bit vectors.
|
||||
|
||||
Sets can be constructed via the set constructor: ``{}`` is the empty set. The
|
||||
empty set is type combatible with any concrete set type. The constructor
|
||||
empty set is type compatible with any concrete set type. The constructor
|
||||
can also be used to include elements (and ranges of elements):
|
||||
|
||||
.. code-block:: nimrod
|
||||
|
|
@ -1013,7 +1024,7 @@ operation meaning
|
|||
================== ========================================================
|
||||
|
||||
Sets are often used to define a type for the *flags* of a procedure. This is
|
||||
much cleaner (and type safe solution) solution than just defining integer
|
||||
much cleaner (and type safe) solution than just defining integer
|
||||
constants that should be ``or``'ed together.
|
||||
|
||||
|
||||
|
|
@ -1047,36 +1058,6 @@ The built-in ``len`` proc returns the array's length. ``low(a)`` returns the
|
|||
lowest valid index for the array `a` and ``high(a)`` the highest valid index.
|
||||
|
||||
|
||||
Open arrays
|
||||
-----------
|
||||
Often fixed size arrays turn out to be too inflexible; procedures should
|
||||
be able to deal with arrays of different sizes. The `openarray`:idx: type
|
||||
allows this. Openarrays are always indexed with an ``int`` starting at
|
||||
position 0. The ``len``, ``low`` and ``high`` operations are available
|
||||
for open arrays too. Any array with a compatible base type can be passed to
|
||||
an openarray parameter, the index type does not matter.
|
||||
|
||||
The openarray type cannot be nested: Multidimensional openarrays are not
|
||||
supported because this is seldom needed and cannot be done efficiently.
|
||||
|
||||
An openarray is also a means to implement passing a variable number of
|
||||
arguments to a procedure. The compiler converts the list of arguments
|
||||
to an array automatically:
|
||||
|
||||
.. code-block:: nimrod
|
||||
proc myWriteln(f: TFile, a: openarray[string]) =
|
||||
for s in items(a):
|
||||
write(f, s)
|
||||
write(f, "\n")
|
||||
|
||||
myWriteln(stdout, "abc", "def", "xyz")
|
||||
# is transformed by the compiler to:
|
||||
myWriteln(stdout, ["abc", "def", "xyz"])
|
||||
|
||||
This transformation is only done if the openarray parameter is the
|
||||
last parameter in the procedure header.
|
||||
|
||||
|
||||
Sequences
|
||||
---------
|
||||
`Sequences`:idx: are similar to arrays but of dynamic length which may change
|
||||
|
|
@ -1108,6 +1089,38 @@ rather than ``nil`` as the *empty* value. But ``@[]`` creates a sequence
|
|||
object on the heap, so there is a trade-off to be made here.
|
||||
|
||||
|
||||
Open arrays
|
||||
-----------
|
||||
**Note**: Openarrays can only be used for parameters.
|
||||
|
||||
Often fixed size arrays turn out to be too inflexible; procedures should
|
||||
be able to deal with arrays of different sizes. The `openarray`:idx: type
|
||||
allows this. Openarrays are always indexed with an ``int`` starting at
|
||||
position 0. The ``len``, ``low`` and ``high`` operations are available
|
||||
for open arrays too. Any array with a compatible base type can be passed to
|
||||
an openarray parameter, the index type does not matter.
|
||||
|
||||
The openarray type cannot be nested: Multidimensional openarrays are not
|
||||
supported because this is seldom needed and cannot be done efficiently.
|
||||
|
||||
An openarray is also a means to implement passing a variable number of
|
||||
arguments to a procedure. The compiler converts the list of arguments
|
||||
to an array automatically:
|
||||
|
||||
.. code-block:: nimrod
|
||||
proc myWriteln(f: TFile, a: openarray[string]) =
|
||||
for s in items(a):
|
||||
write(f, s)
|
||||
write(f, "\n")
|
||||
|
||||
myWriteln(stdout, "abc", "def", "xyz")
|
||||
# is transformed by the compiler to:
|
||||
myWriteln(stdout, ["abc", "def", "xyz"])
|
||||
|
||||
This transformation is only done if the openarray parameter is the
|
||||
last parameter in the procedure header.
|
||||
|
||||
|
||||
Tuples
|
||||
------
|
||||
|
||||
|
|
@ -1271,8 +1284,19 @@ with an asterisk (``*``) are exported:
|
|||
# multiply two int sequences:
|
||||
for i in 0..len(a)-1: result[i] = a[i] * b[i]
|
||||
|
||||
when isMainModule:
|
||||
# test the new ``*`` operator for sequences:
|
||||
assert(@[1, 2, 3] * @[1, 2, 3] == @[1, 4, 9])
|
||||
|
||||
The above module exports ``x`` and ``*``, but not ``y``.
|
||||
|
||||
The top-level statements of a module are executed at the start of the program.
|
||||
This can be used to initalize complex data structures for example.
|
||||
|
||||
Each module has a special magic constant ``isMainModule`` that is true if the
|
||||
module is compiled as the main file. This is very useful to embed tests within
|
||||
the module as shown by the above example.
|
||||
|
||||
Modules that depend on each other are possible, but strongly discouraged,
|
||||
because then one module cannot be reused without the other.
|
||||
|
||||
|
|
|
|||
160
doc/tut2.txt
160
doc/tut2.txt
|
|
@ -33,7 +33,8 @@ Object Oriented Programming
|
|||
While Nimrod's support for object oriented programming (OOP) is minimalistic,
|
||||
powerful OOP technics can be used. OOP is seen as *one* way to design a
|
||||
program, not *the only* way. Often a procedural approach leads to simpler
|
||||
and more efficient code.
|
||||
and more efficient code. In particular, prefering aggregation over inheritance
|
||||
often yields to a better design.
|
||||
|
||||
|
||||
Objects
|
||||
|
|
@ -196,8 +197,8 @@ for any type:
|
|||
stdout.writeln("Hallo") # the same as write(stdout, "Hallo")
|
||||
|
||||
If it gives you warm fuzzy feelings, you can even write ``1.`+`(2)`` instead of
|
||||
``1 + 2`` and claim that Nimrod is a pure object oriented language. (That
|
||||
would not even be lying: *pure OO* has no meaning anyway. :-)
|
||||
``1 + 2`` and claim that Nimrod is a pure object oriented language. (But
|
||||
that's not true. :-)
|
||||
|
||||
|
||||
Properties
|
||||
|
|
@ -237,15 +238,15 @@ The ``[]`` array access operator can be overloaded to provide
|
|||
type
|
||||
TVector* = object
|
||||
x, y, z: float
|
||||
|
||||
|
||||
proc `[]=`* (v: var TVector, i: int, value: float) =
|
||||
# setter
|
||||
# setter
|
||||
case i
|
||||
of 0: v.x = value
|
||||
of 1: v.y = value
|
||||
of 2: v.z = value
|
||||
else: assert(false)
|
||||
|
||||
|
||||
proc `[]`* (v: TVector, i: int): float =
|
||||
# getter
|
||||
case i
|
||||
|
|
@ -253,25 +254,29 @@ The ``[]`` array access operator can be overloaded to provide
|
|||
of 1: result = v.y
|
||||
of 2: result = v.z
|
||||
else: assert(false)
|
||||
|
||||
|
||||
The example is silly, since a vector is better modelled by a tuple which
|
||||
already provides ``v[]`` access.
|
||||
|
||||
|
||||
Dynamic binding
|
||||
---------------
|
||||
In Nimrod procedural types are used to implement dynamic binding. The following
|
||||
example also shows some more conventions: The ``self`` or ``this`` object
|
||||
is named ``my`` (because it is shorter than the alternatives), each class
|
||||
provides a constructor, etc.
|
||||
Dynamic dispatch
|
||||
----------------
|
||||
In Nimrod procedural types are used to implement dynamic dispatch. The
|
||||
following example also shows some more conventions: The ``self`` or ``this``
|
||||
object is named ``my`` (because it is shorter than the alternatives), each
|
||||
class provides a constructor, etc.
|
||||
|
||||
.. code-block:: nimrod
|
||||
type
|
||||
TFigure = object of TObject # abstract base class:
|
||||
draw: proc (my: var TFigure) # concrete classes implement this proc
|
||||
TFigure = object of TObject # abstract base class:
|
||||
fDraw: proc (my: var TFigure) # concrete classes implement this proc
|
||||
|
||||
proc init(f: var TFigure) =
|
||||
f.draw = nil
|
||||
f.fDraw = nil
|
||||
|
||||
proc draw(f: var TFigure) =
|
||||
# ``draw`` dispatches dynamically:
|
||||
f.fDraw(f)
|
||||
|
||||
type
|
||||
TCircle = object of TFigure
|
||||
|
|
@ -282,7 +287,7 @@ provides a constructor, etc.
|
|||
proc init(my: var TCircle) =
|
||||
init(TFigure(my)) # call base constructor
|
||||
my.radius = 5
|
||||
my.draw = drawCircle
|
||||
my.fdraw = drawCircle
|
||||
|
||||
type
|
||||
TRectangle = object of TFigure
|
||||
|
|
@ -294,50 +299,7 @@ provides a constructor, etc.
|
|||
init(TFigure(my)) # call base constructor
|
||||
my.width = 5
|
||||
my.height = 10
|
||||
my.draw = drawRectangle
|
||||
|
||||
# now use these classes:
|
||||
var
|
||||
r: TRectangle
|
||||
c: TCircle
|
||||
init(r)
|
||||
init(c)
|
||||
r.draw(r)
|
||||
c.draw(c)
|
||||
|
||||
The last line shows the syntactical difference between static and dynamic
|
||||
binding: The ``r.draw(r)`` dynamic call refers to ``r`` twice. This difference
|
||||
is not necessarily bad. But if you want to eliminate the somewhat redundant
|
||||
``r``, it can be done by using *closures*:
|
||||
|
||||
.. code-block:: nimrod
|
||||
type
|
||||
TFigure = object of TObject # abstract base class:
|
||||
draw: proc () {.closure.} # concrete classes implement this proc
|
||||
|
||||
proc init(f: var TFigure) =
|
||||
f.draw = nil
|
||||
|
||||
type
|
||||
TCircle = object of TFigure
|
||||
radius: int
|
||||
|
||||
proc init(me: var TCircle) =
|
||||
init(TFigure(me)) # call base constructor
|
||||
me.radius = 5
|
||||
me.draw = lambda () =
|
||||
echo("o " & $me.radius)
|
||||
|
||||
type
|
||||
TRectangle = object of TFigure
|
||||
width, height: int
|
||||
|
||||
proc init(me: var TRectangle) =
|
||||
init(TFigure(me)) # call base constructor
|
||||
me.width = 5
|
||||
me.height = 10
|
||||
me.draw = lambda () =
|
||||
echo("[]")
|
||||
my.fdraw = drawRectangle
|
||||
|
||||
# now use these classes:
|
||||
var
|
||||
|
|
@ -348,10 +310,12 @@ is not necessarily bad. But if you want to eliminate the somewhat redundant
|
|||
r.draw()
|
||||
c.draw()
|
||||
|
||||
The example also introduces `lambda`:idx: expressions: A ``lambda`` expression
|
||||
defines a new proc with the ``closure`` calling convention on the fly.
|
||||
The code uses a ``draw`` procedure that is bound statically, but inside it
|
||||
the dynamic dispatch happens with the help of the ``fdraw`` field. This is
|
||||
slightly more inconvienent than in traditional OOP-languages, but has the
|
||||
advantage of being much more flexible (and somewhat faster). The above approach
|
||||
also allows some form *monkey patching* by modifying the ``fdraw`` field.
|
||||
|
||||
`Version 0.7.4: Closures and lambda expressions are not implemented.`:red:
|
||||
|
||||
|
||||
Exceptions
|
||||
|
|
@ -432,7 +396,7 @@ is not executed (if an exception occurs).
|
|||
Generics
|
||||
========
|
||||
|
||||
`Version 0.7.4: Complex generic types like in the example do not work.`:red:
|
||||
`Version 0.7.6: Complex generic types like in the example do not work.`:red:
|
||||
|
||||
`Generics`:idx: are Nimrod's means to parametrize procs, iterators or types
|
||||
with `type parameters`:idx:. They are most useful for efficient type safe
|
||||
|
|
@ -458,8 +422,8 @@ containers:
|
|||
else:
|
||||
var it = root
|
||||
while it != nil:
|
||||
# compare the data items; uses the generic ``cmd`` proc that works for
|
||||
# any type that has a ``==`` and ``<`` operator
|
||||
# compare the data items; uses the generic ``cmd`` proc
|
||||
# that works for any type that has a ``==`` and ``<`` operator
|
||||
var c = cmp(it.data, n.data)
|
||||
if c < 0:
|
||||
if it.le == nil:
|
||||
|
|
@ -491,7 +455,7 @@ containers:
|
|||
|
||||
var
|
||||
root: PBinaryTree[string] # instantiate a PBinaryTree with ``string``
|
||||
add(root, newNode("hallo")) # instantiates generic procs ``newNode`` and ``add``
|
||||
add(root, newNode("hallo")) # instantiates ``newNode`` and ``add``
|
||||
add(root, "world") # instantiates the second ``add`` proc
|
||||
for str in preorder(root):
|
||||
stdout.writeln(str)
|
||||
|
|
@ -567,6 +531,7 @@ Turning the ``log`` proc into a template solves this problem in an elegant way:
|
|||
The "types" of templates can be the symbols ``expr`` (stands for *expression*),
|
||||
``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type
|
||||
description*). These are no real types, they just help the compiler parsing.
|
||||
In later versions, real types will be supported too.
|
||||
|
||||
The template body does not open a new scope. To open a new scope
|
||||
use a ``block`` statement:
|
||||
|
|
@ -587,6 +552,35 @@ use a ``block`` statement:
|
|||
b = 42 # does not work, `b` is unknown
|
||||
|
||||
|
||||
If there is a ``stmt`` parameter it should be the last in the template
|
||||
declaration. The reason is that statements can be passed to a template
|
||||
via a special ``:`` syntax:
|
||||
|
||||
.. code-block:: nimrod
|
||||
|
||||
template withFile(f, filename, mode: expr, actions: stmt): stmt =
|
||||
block:
|
||||
var fn = filename
|
||||
var f: TFile
|
||||
if openFile(f, fn, mode):
|
||||
try:
|
||||
actions
|
||||
finally:
|
||||
closeFile(f)
|
||||
else:
|
||||
quit("cannot open: " & fn)
|
||||
|
||||
withFile(txt, "ttempl3.txt", fmWrite):
|
||||
txt.writeln("line 1")
|
||||
txt.writeln("line 2")
|
||||
|
||||
In the example the two ``writeln`` statements are bound to the ``actions``
|
||||
parameter. The ``withFile`` template contains boilerplate code and helps to
|
||||
avoid a common bug: To forget to close the file. Note how the
|
||||
``var fn = filename`` statement ensures that ``filename`` is evaluated only
|
||||
once.
|
||||
|
||||
|
||||
Macros
|
||||
======
|
||||
|
||||
|
|
@ -658,36 +652,6 @@ The macro call expands to:
|
|||
writeln(stdout, x)
|
||||
|
||||
|
||||
Lets return to the dynamic binding ``r.draw(r)`` notational "problem". Apart
|
||||
from closures, there is another "solution": Define an infix ``!`` macro
|
||||
operator which hides it:
|
||||
|
||||
.. code-block::
|
||||
|
||||
macro `!` (n: expr): expr =
|
||||
result = newNimNode(nnkCall, n)
|
||||
var dot = newNimNode(nnkDotExpr, n)
|
||||
dot.add(n[1]) # obj
|
||||
if n[2].kind == nnkCall:
|
||||
# transforms ``obj!method(arg1, arg2, ...)`` to
|
||||
# ``(obj.method)(obj, arg1, arg2, ...)``
|
||||
dot.add(n[2][0]) # method
|
||||
result.add(dot)
|
||||
result.add(n[1]) # obj
|
||||
for i in 1..n[2].len-1:
|
||||
result.add(n[2][i])
|
||||
else:
|
||||
# transforms ``obj!method`` to
|
||||
# ``(obj.method)(obj)``
|
||||
dot.add(n[2]) # method
|
||||
result.add(dot)
|
||||
result.add(n[1]) # obj
|
||||
|
||||
r!draw(a, b, c) # will be transfomed into ``r.draw(r, a, b, c)``
|
||||
|
||||
Great! 20 lines of complex code to safe a few keystrokes! Obviously, this is
|
||||
exactly you should not do! (But it makes a cool example.)
|
||||
|
||||
|
||||
Statement Macros
|
||||
----------------
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ type
|
|||
menu: PGtkMenuBar
|
||||
notebook: PGtkNotebook
|
||||
tabname: int # used for generating tab names
|
||||
tabs: seq[PTab]
|
||||
currTab: int
|
||||
PEditor = ptr TEditor
|
||||
|
||||
proc on_window_destroy(obj: PGtkObject, event: PGdkEvent,
|
||||
|
|
@ -46,9 +48,50 @@ proc getTabIndex(e: PEditor, tab: PTab): int =
|
|||
if tab.hbox == v: return i
|
||||
inc(i)
|
||||
|
||||
proc getActiveTab(e: PEditor): PTab =
|
||||
nil
|
||||
|
||||
type
|
||||
TAnswer = enum
|
||||
answerYes, answerNo, answerCancel
|
||||
|
||||
proc askWhetherToSave(e: PEditor): TAnswer =
|
||||
var dialog = gtk_dialog_new_with_buttons("Should the changes be saved?",
|
||||
e.window, GTK_DIALOG_MODAL, GTK_STOCK_SAVE, 1,
|
||||
"gtk-discard", 2,
|
||||
GTK_STOCK_CANCEL, 3, nil)
|
||||
result = TAnswer(gtk_dialog_run(dialog)+1)
|
||||
gtk_widget_destroy(dialog)
|
||||
|
||||
proc saveTab(tab: PTab) =
|
||||
if tab.untitled:
|
||||
tab.filename = ChooseFileToSave(tab.e.window, getRoot(tab))
|
||||
tab.untitled = false
|
||||
XXX
|
||||
|
||||
proc OnCloseTab(button: PGtkButton, tab: PTab) {.cdecl.} =
|
||||
var idx = getTabIndex(tab.e, tab)
|
||||
if idx >= 0: gtk_notebook_remove_page(tab.e.notebook, idx)
|
||||
var idx = -1
|
||||
for i in 0..high(tab.e.tabs):
|
||||
if tab.e.tabs[i] == tab:
|
||||
idx = i
|
||||
break
|
||||
if idx >= 0:
|
||||
if gtk_text_buffer_get_modified(gtk_text_view_get_buffer(tab.textView)):
|
||||
case askWhetherToSave(tab.e)
|
||||
of answerCancel: return
|
||||
of answerYes: saveTab(tab)
|
||||
of answerNo: nil
|
||||
|
||||
gtk_notebook_remove_page(tab.e.notebook, idx)
|
||||
if idx < high(tab.e.tabs):
|
||||
for i in idx..high(tab.e.tabs)-1:
|
||||
tab.e.tabs[i] = tab.e.tabs[i+1]
|
||||
else:
|
||||
dec currTab
|
||||
GC_unref(tab.filename)
|
||||
dealloc(tab)
|
||||
#var idx = getTabIndex(tab.e, tab)
|
||||
#if idx >= 0: gtk_notebook_remove_page(tab.e.notebook, idx)
|
||||
|
||||
proc createTab(e: PEditor, filename: string, untitled: bool) =
|
||||
var t = cast[PTab](alloc0(sizeof(TTab)))
|
||||
|
|
@ -78,7 +121,21 @@ proc createTab(e: PEditor, filename: string, untitled: bool) =
|
|||
gtk_widget_show(lab)
|
||||
|
||||
var idx = gtk_notebook_append_page(e.notebook, scroll, t.hbox)
|
||||
e.currTab = idx
|
||||
add(e.tabs, t)
|
||||
gtk_notebook_set_current_page(e.notebook, idx)
|
||||
|
||||
|
||||
proc on_open_menu_item_activate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
|
||||
var files = ChooseFilesToOpen(e.window, getRoot(getActiveTab(e)))
|
||||
for f in items(files): createTab(e, f, untitled=false)
|
||||
|
||||
proc on_save_menu_item_activate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
|
||||
var cp = gtk_notebook_get_current_page(e.notebook)
|
||||
|
||||
|
||||
proc on_save_as_menu_item_activate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
|
||||
nil
|
||||
|
||||
proc on_new_menu_item_activate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
|
||||
inc(e.tabname)
|
||||
|
|
@ -91,6 +148,8 @@ proc main(e: PEditor) =
|
|||
e.window = GTK_WINDOW(glade_xml_get_widget(builder, "window"))
|
||||
e.statusbar = GTK_STATUSBAR(glade_xml_get_widget(builder, "statusbar"))
|
||||
e.notebook = GTK_NOTEBOOK(glade_xml_get_widget(builder, "notebook"))
|
||||
e.tabs = @[]
|
||||
e.currTab = -1
|
||||
setHomogeneous(e.notebook^, 1)
|
||||
|
||||
# connect the signal handlers:
|
||||
|
|
@ -108,6 +167,18 @@ proc main(e: PEditor) =
|
|||
discard g_signal_connect(quitItem, "activate",
|
||||
G_CALLBACK(on_window_destroy), e)
|
||||
|
||||
var openItem = GTK_MENU_ITEM(glade_xml_get_widget(builder, "open_menu_item"))
|
||||
discard g_signal_connect(openItem, "activate",
|
||||
G_CALLBACK(on_open_menu_item_activate), e)
|
||||
|
||||
var saveItem = GTK_MENU_ITEM(glade_xml_get_widget(builder, "save_menu_item"))
|
||||
discard g_signal_connect(saveItem, "activate",
|
||||
G_CALLBACK(on_save_menu_item_activate), e)
|
||||
|
||||
var saveAsItem = GTK_MENU_ITEM(glade_xml_get_widget(builder, "save_as_menu_item"))
|
||||
discard g_signal_connect(saveAsItem, "activate",
|
||||
G_CALLBACK(on_save_as_menu_item_activate), e)
|
||||
|
||||
gtk_window_set_default_icon_name(GTK_STOCK_EDIT)
|
||||
gtk_widget_show(e.window)
|
||||
gtk_main()
|
||||
|
|
|
|||
219
koch.py
219
koch.py
|
|
@ -3,26 +3,19 @@
|
|||
##########################################################################
|
||||
## ##
|
||||
## Build script of the Nimrod Compiler ##
|
||||
## (c) 2008 Andreas Rumpf ##
|
||||
## (c) 2009 Andreas Rumpf ##
|
||||
## ##
|
||||
##########################################################################
|
||||
|
||||
import os, os.path, sys, re, shutil, cPickle, time, getopt, glob, zlib
|
||||
from string import split, replace, lower, join, find, strip
|
||||
|
||||
if sys.version[0] >= "3": # this script does not work with Python 3.0
|
||||
sys.exit("wrong python version: use Python 1.5.2 - 2.6")
|
||||
|
||||
True = 0 == 0 # Python 1.5 does not have True and False :-(
|
||||
False = 0 == 1
|
||||
import sys, os, os.path, re, shutil, time, getopt, glob, zlib, pickle
|
||||
from pycompab import *
|
||||
|
||||
# --------------------- constants ----------------------------------------
|
||||
|
||||
NIMROD_VERSION = '0.7.4'
|
||||
NIMROD_VERSION = '0.7.6'
|
||||
# This string contains Nimrod's version. It is the only place
|
||||
# where the version needs to be updated. The rest is done by
|
||||
# the build process automatically. It is replaced **everywhere**
|
||||
# automatically!
|
||||
# the build process automatically. It is replaced **everywhere**!
|
||||
# Format is: Major.Minor.Patch
|
||||
# Major part: plan is to use number 1 for the first version that is stable;
|
||||
# higher versions may be incompatible with previous versions
|
||||
|
|
@ -30,39 +23,39 @@ NIMROD_VERSION = '0.7.4'
|
|||
# backwards-compatible)
|
||||
# Patch level: is increased for every patch
|
||||
|
||||
EXPLAIN = True
|
||||
force = False
|
||||
EXPLAIN = true
|
||||
force = false
|
||||
|
||||
GENERATE_DIFF = False
|
||||
GENERATE_DIFF = false
|
||||
# if set, a diff.log file is generated when bootstrapping
|
||||
|
||||
USE_FPC = True
|
||||
USE_FPC = true
|
||||
|
||||
BOOTCMD = "%s cc --compile:build/platdef.c %s rod/nimrod.nim"
|
||||
BOOTCMD = "$1 cc --compile:build/platdef.c $2 rod/nimrod.nim"
|
||||
# the command used for bootstrapping
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
def Error(msg): sys.exit("[Koch] *** ERROR: " + msg)
|
||||
def Warn(msg): print "[Koch] *** WARNING: " + msg
|
||||
def Echo(msg): print "[Koch] " + msg
|
||||
def _Info(msg): print "[Koch] " + msg
|
||||
def Warn(msg): print("[Koch] *** WARNING: " + msg)
|
||||
def Echo(msg): print("[Koch] " + msg)
|
||||
def _Info(msg): print("[Koch] " + msg)
|
||||
|
||||
_FINGERPRINTS_FILE = "koch.dat"
|
||||
# in this file all the fingerprints are kept to allow recognizing when a file
|
||||
# has changed. This works reliably, which cannot be said from just taking
|
||||
# filetime-stamps.
|
||||
|
||||
def FileCmp(filenameA, filenameB):
|
||||
def SameFileContent(filenameA, filenameB):
|
||||
SIZE = 4096*2
|
||||
result = True
|
||||
result = true
|
||||
a = open(filenameA, "rb")
|
||||
b = open(filenameB, "rb")
|
||||
while True:
|
||||
while true:
|
||||
x = a.read(SIZE)
|
||||
y = b.read(SIZE)
|
||||
if x != y:
|
||||
result = False
|
||||
result = false
|
||||
break
|
||||
elif len(x) < SIZE: # EOF?
|
||||
break
|
||||
|
|
@ -70,41 +63,11 @@ def FileCmp(filenameA, filenameB):
|
|||
b.close()
|
||||
return result
|
||||
|
||||
def Subs(frmt, **substitution):
|
||||
import string
|
||||
chars = string.digits+string.letters+"_"
|
||||
d = substitution
|
||||
result = []
|
||||
i = 0
|
||||
while i < len(frmt):
|
||||
if frmt[i] == '$':
|
||||
i = i+1
|
||||
if frmt[i] == '$':
|
||||
result.append('$')
|
||||
i = i+1
|
||||
elif frmt[i] == '{':
|
||||
i = i+1
|
||||
j = i
|
||||
while frmt[i] != '}': i = i+1
|
||||
i = i+1 # skip }
|
||||
result.append(d[frmt[j:i-1]])
|
||||
elif frmt[i] in string.letters+"_":
|
||||
j = i
|
||||
i = i+1
|
||||
while i < len(frmt) and frmt[i] in chars: i = i + 1
|
||||
result.append(d[frmt[j:i]])
|
||||
else:
|
||||
assert(false)
|
||||
else:
|
||||
result.append(frmt[i])
|
||||
i = i+1
|
||||
return join(result, "")
|
||||
|
||||
def SplitArg(s):
|
||||
if ':' in s: c = ':'
|
||||
elif '=' in s: c = '='
|
||||
else: return (s, '')
|
||||
i = s.find(c)
|
||||
i = find(s, c)
|
||||
return (s[:i], s[i+1:])
|
||||
|
||||
_baseDir = os.getcwd()
|
||||
|
|
@ -150,7 +113,7 @@ def Remove(f):
|
|||
try:
|
||||
os.remove(Path(f))
|
||||
except OSError:
|
||||
Warn("could not remove: %s" % f)
|
||||
Warn("could not remove: " + f)
|
||||
|
||||
def Move(src, dest):
|
||||
try:
|
||||
|
|
@ -164,22 +127,26 @@ def Move(src, dest):
|
|||
d = Path(dest)
|
||||
try:
|
||||
m(s, d)
|
||||
except IOError, OSError:
|
||||
Warn("could not move %s to %s" % (s, d))
|
||||
except IOError:
|
||||
Warn(Subs("could not move $1 to $2", s, d))
|
||||
except OSError:
|
||||
Warn(Subs("could not move $1 to $2", s, d))
|
||||
|
||||
def Copy(src, dest):
|
||||
s = Path(src)
|
||||
d = Path(dest)
|
||||
try:
|
||||
shutil.copyfile(s, d)
|
||||
except IOError, OSError:
|
||||
Warn("could not copy %s to %s" % (s, d))
|
||||
except IOError:
|
||||
Warn(Subs("could not copy $1 to $2", s, d))
|
||||
except OSError:
|
||||
Warn(Subs("could not copy $1 to $2", s, d))
|
||||
|
||||
def RemoveDir(f):
|
||||
try:
|
||||
shutil.rmtree(Path(f))
|
||||
except OSError:
|
||||
Warn("could not remove: %s" % f)
|
||||
Warn("could not remove: " + f)
|
||||
|
||||
def Exists(f): return os.path.exists(Path(f))
|
||||
|
||||
|
|
@ -198,7 +165,7 @@ def Mkdir(dest):
|
|||
Warn("could not create directory: " + d)
|
||||
|
||||
def Glob(pattern): # needed because glob.glob() is buggy on Windows 95:
|
||||
# things like tests/t*.mor won't work
|
||||
# things like tests/t*.nim won't work
|
||||
global _baseDir
|
||||
(head, tail) = os.path.split(Path(pattern))
|
||||
result = []
|
||||
|
|
@ -250,14 +217,14 @@ class Changed:
|
|||
# a success:
|
||||
c.success()
|
||||
"""
|
||||
def __init__(self, id, files, explain=False,
|
||||
def __init__(self, id, files, explain=false,
|
||||
fingerprintsfile=_FINGERPRINTS_FILE):
|
||||
# load the fingerprints file:
|
||||
# fingerprints is a dict[target, files] where files is a dict[filename, hash]
|
||||
self.fingers = {} # default value
|
||||
if Exists(fingerprintsfile):
|
||||
try:
|
||||
self.fingers = cPickle.load(open(fingerprintsfile))
|
||||
self.fingers = pickle.load(open(fingerprintsfile, "rb"))
|
||||
except OSError:
|
||||
Error("Cannot read from " + fingerprintsfile)
|
||||
self.filename = fingerprintsfile
|
||||
|
|
@ -267,7 +234,7 @@ class Changed:
|
|||
self.explain = explain
|
||||
|
||||
def _hashFile(self, f):
|
||||
x = open(f)
|
||||
x = open(f, "rb")
|
||||
result = self._hashStr(x.read())
|
||||
x.close() # for other Python implementations
|
||||
return result
|
||||
|
|
@ -275,29 +242,29 @@ class Changed:
|
|||
def check(self):
|
||||
if type(self.files) == type(""):
|
||||
self.files = split(self.files)
|
||||
result = False
|
||||
result = false
|
||||
target = self.id
|
||||
if not self.fingers.has_key(target):
|
||||
if not has_key(self.fingers, target):
|
||||
self.fingers[target] = {}
|
||||
if self.explain: _Info("no entries for target '%s'" % target)
|
||||
result = True
|
||||
if self.explain: _Info(Subs("no entries for target '$1'", target))
|
||||
result = true
|
||||
for d in self.files:
|
||||
if Exists(d):
|
||||
n = self._hashFile(d)
|
||||
if not self.fingers[target].has_key(d) or n != self.fingers[target][d]:
|
||||
result = True
|
||||
if self.explain: _Info("'%s' modified since last build" % d)
|
||||
if not has_key(self.fingers[target], d) or n != self.fingers[target][d]:
|
||||
result = true
|
||||
if self.explain: _Info(Subs("'$1' modified since last build", d))
|
||||
self.fingers[target][d] = n
|
||||
else:
|
||||
Warn("'%s' does not exist!" % d)
|
||||
result = True
|
||||
Warn(Subs("'$1' does not exist!", d))
|
||||
result = true
|
||||
return result
|
||||
|
||||
def update(self, filename):
|
||||
self.fingers[self.id][filename] = self._hashFile(filename)
|
||||
|
||||
def success(self):
|
||||
cPickle.dump(self.fingers, open(self.filename, "w+"))
|
||||
pickle.dump(self.fingers, open(self.filename, "wb+"))
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
|
|
@ -318,12 +285,12 @@ def CogRule(name, filename, dependson):
|
|||
|
||||
_nim_exe = os.path.join(os.getcwd(), "bin", ExeExt("nim"))
|
||||
_output_obj = os.path.join(os.getcwd(), "obj")
|
||||
FPC_CMD = (r"fpc -Cs16777216 -gl -bl -Crtoi -Sgidh -vw -Se1 -o%s "
|
||||
r"-FU%s %s") % (_nim_exe, _output_obj,
|
||||
os.path.join(os.getcwd(), "nim", "nimrod.pas"))
|
||||
FPC_CMD = Subs(r"fpc -Cs16777216 -gl -bl -Crtoi -Sgidh -vw -Se1 -o$1 "
|
||||
r"-FU$2 $3", _nim_exe, _output_obj,
|
||||
os.path.join(os.getcwd(), "nim", "nimrod.pas"))
|
||||
|
||||
def buildRod(options):
|
||||
Exec("nim compile --compile:build/platdef.c %s rod/nimrod" % options)
|
||||
Exec(Subs("nim compile --compile:build/platdef.c $1 rod/nimrod", options))
|
||||
Move(ExeExt("rod/nimrod"), ExeExt("bin/nimrod"))
|
||||
|
||||
def cmd_nim():
|
||||
|
|
@ -341,8 +308,8 @@ def cmd_nim():
|
|||
Exec(FPC_CMD)
|
||||
if Exists(ExeExt("bin/nim")):
|
||||
c.success()
|
||||
return True
|
||||
return False
|
||||
return true
|
||||
return false
|
||||
|
||||
def cmd_rod(options):
|
||||
prereqs = Glob("lib/*.nim") + Glob("rod/*.nim") + [
|
||||
|
|
@ -356,13 +323,13 @@ def cmd_rod(options):
|
|||
|
||||
# ------------------- constants -----------------------------------------------
|
||||
|
||||
HELP = """\
|
||||
HELP = Subs("""\
|
||||
+-----------------------------------------------------------------+
|
||||
| Maintenance script for Nimrod |
|
||||
| Version %s|
|
||||
| (c) 2008 Andreas Rumpf |
|
||||
| Version $1|
|
||||
| (c) 2009 Andreas Rumpf |
|
||||
+-----------------------------------------------------------------+
|
||||
Your Python version: %s
|
||||
Your Python version: $2
|
||||
|
||||
Usage:
|
||||
koch.py [options] command [options for command]
|
||||
|
|
@ -382,27 +349,27 @@ Possible Commands:
|
|||
csource build the C sources for installation
|
||||
zip build the installation ZIP package
|
||||
inno build the Inno Setup installer
|
||||
""" % (NIMROD_VERSION + ' ' * (44-len(NIMROD_VERSION)), sys.version)
|
||||
""", NIMROD_VERSION + ' ' * (44-len(NIMROD_VERSION)), sys.version)
|
||||
|
||||
def main(args):
|
||||
if len(args) == 0:
|
||||
print HELP
|
||||
print(HELP)
|
||||
else:
|
||||
i = 0
|
||||
while args[i][:1] == "-":
|
||||
a = args[i]
|
||||
if a in ("--force", "-f", "-B", "-b"):
|
||||
global force
|
||||
force = True
|
||||
force = true
|
||||
elif a in ("-h", "--help", "-?"):
|
||||
print HELP
|
||||
print(HELP)
|
||||
return
|
||||
elif a == "--diff":
|
||||
global GENERATE_DIFF
|
||||
GENERATE_DIFF = True
|
||||
GENERATE_DIFF = true
|
||||
elif a == "--no_fpc":
|
||||
global USE_FPC
|
||||
USE_FPC = False
|
||||
USE_FPC = false
|
||||
else:
|
||||
Error("illegal option: " + a)
|
||||
i = i + 1
|
||||
|
|
@ -417,19 +384,23 @@ def main(args):
|
|||
elif cmd == "zip": cmd_zip()
|
||||
elif cmd == "inno": cmd_inno()
|
||||
elif cmd == "csource": cmd_csource()
|
||||
elif cmd == "install": cmd_install() # for backwards compability
|
||||
else: Error("illegal command: " + cmd)
|
||||
|
||||
def cmd_csource():
|
||||
Exec("nimrod cc -r tools/niminst --var:version=%s csource rod/nimrod" %
|
||||
NIMROD_VERSION)
|
||||
Exec(Subs("nimrod cc -r tools/niminst --var:version=$1 csource rod/nimrod",
|
||||
NIMROD_VERSION))
|
||||
|
||||
def cmd_zip():
|
||||
Exec("nimrod cc -r tools/niminst --var:version=%s zip rod/nimrod" %
|
||||
NIMROD_VERSION)
|
||||
Exec(Subs("nimrod cc -r tools/niminst --var:version=$1 zip rod/nimrod",
|
||||
NIMROD_VERSION))
|
||||
|
||||
def cmd_inno():
|
||||
Exec("nimrod cc -r tools/niminst --var:version=%s inno rod/nimrod" %
|
||||
NIMROD_VERSION)
|
||||
Exec(Subs("nimrod cc -r tools/niminst --var:version=$1 inno rod/nimrod",
|
||||
NIMROD_VERSION))
|
||||
|
||||
def cmd_install():
|
||||
Exec("sh ./build.sh")
|
||||
|
||||
# -------------------------- bootstrap ----------------------------------------
|
||||
|
||||
|
|
@ -448,13 +419,13 @@ def genBootDiff(genA, genB):
|
|||
return a != b
|
||||
|
||||
BOOTLOG = "bootdiff.log"
|
||||
result = False
|
||||
result = false
|
||||
for f in Glob("diff/*.c"): Remove(f)
|
||||
if Exists(BOOTLOG): Remove(BOOTLOG)
|
||||
if GENERATE_DIFF:
|
||||
lines = [] # lines of the generated logfile
|
||||
if len(genA) != len(genB): Warn("number of generated files differ!")
|
||||
for filename, acontent in genA.iteritems():
|
||||
for filename, acontent in genA.items():
|
||||
bcontent = genB[filename]
|
||||
if bcontent != acontent:
|
||||
lines.append("------------------------------------------------------")
|
||||
|
|
@ -464,8 +435,8 @@ def genBootDiff(genA, genB):
|
|||
la = acontent[i][:-1] # without newline!
|
||||
lb = bcontent[i][:-1]
|
||||
if interestingDiff(la, lb):
|
||||
lines.append("%6d - %s" % (i, la))
|
||||
lines.append("%6d + %s" % (i, lb))
|
||||
lines.append(Subs("$1 - $2", i, la))
|
||||
lines.append(Subs("$1 + $2", i, lb))
|
||||
if len(acontent) > len(bcontent):
|
||||
cont = acontent
|
||||
marker = "-"
|
||||
|
|
@ -473,10 +444,10 @@ def genBootDiff(genA, genB):
|
|||
cont = bcontent
|
||||
marker = "+"
|
||||
for i in range(min(len(acontent), len(bcontent)), len(cont)):
|
||||
lines.append("%6d %s %s" % (i, marker, cont[i]))
|
||||
lines.append(Subs("$1 $2 $3", i, marker, cont[i]))
|
||||
open(os.path.join("diff", "a_"+filename), "w+").write(join(acontent, ""))
|
||||
open(os.path.join("diff", "b_"+filename), "w+").write(join(bcontent, ""))
|
||||
if lines: result = True
|
||||
if lines: result = true
|
||||
open(BOOTLOG, "w+").write(join(lines, "\n"))
|
||||
return result
|
||||
|
||||
|
|
@ -488,17 +459,17 @@ def cmd_rodsrc():
|
|||
compiler = "nim"
|
||||
else:
|
||||
compiler = "nimrod"
|
||||
CMD = "%s boot --skip_proj_cfg -o:rod/%s.nim nim/%s"
|
||||
result = False
|
||||
CMD = "$1 boot --skip_proj_cfg -o:rod/$2.nim nim/$3"
|
||||
result = false
|
||||
for fi in Glob("nim/*.pas"):
|
||||
f = FilenameNoExt(fi)
|
||||
if f in PAS_FILES_BLACKLIST: continue
|
||||
c = Changed(f+"__rodsrc", fi, EXPLAIN)
|
||||
if c.check() or force:
|
||||
Exec(CMD % (compiler, f, f+".pas"))
|
||||
Exec("%s parse rod/%s.nim" % (compiler, f))
|
||||
Exec(Subs(CMD, compiler, f, f+".pas"))
|
||||
Exec(Subs("$1 parse rod/$2.nim", compiler, f))
|
||||
c.success()
|
||||
result = True
|
||||
result = true
|
||||
return result
|
||||
|
||||
def moveExes():
|
||||
|
|
@ -506,7 +477,7 @@ def moveExes():
|
|||
|
||||
def cmd_boot(args):
|
||||
def myExec(compiler, args=args):
|
||||
Exec(BOOTCMD % (compiler, args))
|
||||
Exec(Subs(BOOTCMD, compiler, args))
|
||||
# some C compilers (PellesC) output the executable to the
|
||||
# wrong directory. We work around this bug here:
|
||||
if Exists(ExeExt("rod/nimcache/nimrod")):
|
||||
|
|
@ -515,7 +486,7 @@ def cmd_boot(args):
|
|||
writePlatdefC(getNimrodPath())
|
||||
d = detect("fpc -h")
|
||||
if USE_FPC and d:
|
||||
Echo("'%s' detected" % d)
|
||||
Echo(Subs("'$1' detected", d))
|
||||
cmd_nim()
|
||||
compiler = "nim"
|
||||
else:
|
||||
|
|
@ -536,34 +507,34 @@ def cmd_boot(args):
|
|||
if diff:
|
||||
Warn("generated C files are not equal: cycle once again...")
|
||||
# check if the executables are the same (they should!):
|
||||
if FileCmp(Path(ExeExt("rod/nimrod")),
|
||||
Path(ExeExt("bin/nimrod"))):
|
||||
if SameFileContent(Path(ExeExt("rod/nimrod")),
|
||||
Path(ExeExt("bin/nimrod"))):
|
||||
Echo("executables are equal: SUCCESS!")
|
||||
else:
|
||||
Echo("executables are not equal: cycle once again...")
|
||||
diff = True
|
||||
diff = true
|
||||
if diff:
|
||||
# move the new executable to bin directory:
|
||||
moveExes()
|
||||
# use the new executable to compile Nimrod:
|
||||
myExec("nimrod")
|
||||
if FileCmp(Path(ExeExt("rod/nimrod")),
|
||||
Path(ExeExt("bin/nimrod"))):
|
||||
if SameFileContent(Path(ExeExt("rod/nimrod")),
|
||||
Path(ExeExt("bin/nimrod"))):
|
||||
Echo("executables are equal: SUCCESS!")
|
||||
else:
|
||||
Warn("executables are still not equal")
|
||||
|
||||
# ------------------ profile --------------------------------------------------
|
||||
def cmd_profile():
|
||||
Exec(BOOTCMD % ("nimrod", "-d:release --profiler:on"))
|
||||
Exec(Subs(BOOTCMD, "nimrod", "-d:release --profiler:on"))
|
||||
moveExes()
|
||||
Exec(BOOTCMD % ("nimrod", "--compile_only"))
|
||||
Exec(Subs(BOOTCMD, "nimrod", "--compile_only"))
|
||||
|
||||
# ------------------ web ------------------------------------------------------
|
||||
|
||||
def cmd_web():
|
||||
Exec("nimrod cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=%s"
|
||||
% NIMROD_VERSION)
|
||||
Exec(Subs("nimrod cc -r tools/nimweb.nim web/nimrod "
|
||||
"--putenv:nimrodversion=$1", NIMROD_VERSION))
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
|
@ -587,7 +558,7 @@ def cmd_clean(dir = "."):
|
|||
for f in Glob("rod/*.nim"): Remove(f) # remove generated source code
|
||||
def visit(extRegEx, dirname, names):
|
||||
if os.path.split(dirname)[1] == "nimcache":
|
||||
shutil.rmtree(path=dirname, ignore_errors=True)
|
||||
shutil.rmtree(path=dirname, ignore_errors=true)
|
||||
del names
|
||||
else:
|
||||
for name in names:
|
||||
|
|
@ -637,10 +608,10 @@ def writePlatdefC(nimrodpath):
|
|||
import os
|
||||
host, processor = getOSandProcessor()
|
||||
f = open(os.path.join(nimrodpath, "build/platdef.c"), "w+")
|
||||
f.write('/* Generated by koch.py */\n'
|
||||
'char* nimOS(void) { return "%s"; }\n'
|
||||
'char* nimCPU(void) { return "%s"; }\n'
|
||||
'\n' % (host, processor))
|
||||
f.write(Subs('/* Generated by koch.py */\n'
|
||||
'char* nimOS(void) { return "$1"; }\n'
|
||||
'char* nimCPU(void) { return "$2"; }\n'
|
||||
'\n', host, processor))
|
||||
f.close()
|
||||
|
||||
def detect(cmd, lookFor="version"):
|
||||
|
|
|
|||
667
lib/alloc.nim
667
lib/alloc.nim
|
|
@ -1,13 +1,20 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2008 Andreas Rumpf
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
# Low level allocator for Nimrod.
|
||||
# TODO:
|
||||
# - eliminate "used" field
|
||||
# - make searching for block O(1)
|
||||
|
||||
proc raiseOutOfMem {.noinline.} =
|
||||
assert false
|
||||
quit(1)
|
||||
|
||||
# ------------ platform specific chunk allocation code -----------------------
|
||||
|
||||
|
|
@ -15,20 +22,14 @@ when defined(posix):
|
|||
const # XXX: make these variables for portability?
|
||||
PROT_READ = 1 # page can be read
|
||||
PROT_WRITE = 2 # page can be written
|
||||
PROT_EXEC = 4 # page can be executed
|
||||
PROT_NONE = 0 # page can not be accessed
|
||||
|
||||
MAP_SHARED = 1 # Share changes
|
||||
MAP_PRIVATE = 2 # Changes are private
|
||||
MAP_TYPE = 0xf # Mask for type of mapping
|
||||
MAP_FIXED = 0x10 # Interpret addr exactly
|
||||
MAP_ANONYMOUS = 0x20 # don't use a file
|
||||
|
||||
MAP_GROWSDOWN = 0x100 # stack-like segment
|
||||
MAP_DENYWRITE = 0x800 # ETXTBSY
|
||||
MAP_EXECUTABLE = 0x1000 # mark it as an executable
|
||||
MAP_LOCKED = 0x2000 # pages are locked
|
||||
MAP_NORESERVE = 0x4000 # don't check for reservations
|
||||
MAP_PRIVATE = 2 # Changes are private
|
||||
|
||||
when defined(linux):
|
||||
const MAP_ANONYMOUS = 0x20 # don't use a file
|
||||
elif defined(macosx):
|
||||
const MAP_ANONYMOUS = 0x1000
|
||||
else:
|
||||
const MAP_ANONYMOUS = 0 # other operating systems may not know about this
|
||||
|
||||
proc mmap(adr: pointer, len: int, prot, flags, fildes: cint,
|
||||
off: int): pointer {.header: "<sys/mman.h>".}
|
||||
|
|
@ -42,7 +43,7 @@ when defined(posix):
|
|||
raiseOutOfMem()
|
||||
|
||||
proc osDeallocPages(p: pointer, size: int) {.inline} =
|
||||
munmap(p, len)
|
||||
munmap(p, size)
|
||||
|
||||
elif defined(windows):
|
||||
const
|
||||
|
|
@ -51,20 +52,27 @@ elif defined(windows):
|
|||
MEM_TOP_DOWN = 0x100000
|
||||
PAGE_READWRITE = 0x04
|
||||
|
||||
MEM_DECOMMIT = 0x4000
|
||||
MEM_RELEASE = 0x8000
|
||||
|
||||
proc VirtualAlloc(lpAddress: pointer, dwSize: int, flAllocationType,
|
||||
flProtect: int32): pointer {.
|
||||
header: "<windows.h>", stdcall.}
|
||||
|
||||
proc VirtualFree(lpAddress: pointer, dwSize: int,
|
||||
dwFreeType: int32) {.header: "<windows.h>", stdcall.}
|
||||
|
||||
proc osAllocPages(size: int): pointer {.inline.} =
|
||||
result = VirtualAlloc(nil, size, MEM_RESERVE or MEM_COMMIT,
|
||||
PAGE_READWRITE)
|
||||
if result == nil: raiseOutOfMem()
|
||||
|
||||
proc osDeallocPages(p: pointer, size: int) {.inline.} =
|
||||
nil
|
||||
proc osDeallocPages(p: pointer, size: int) {.inline.} =
|
||||
# according to Microsoft, 0 is the only correct value here:
|
||||
VirtualFree(p, 0, MEM_RELEASE)
|
||||
|
||||
else:
|
||||
{.error: "Port GC to your platform".}
|
||||
{.error: "Port memory manager to your platform".}
|
||||
|
||||
# --------------------- end of non-portable code -----------------------------
|
||||
|
||||
|
|
@ -78,59 +86,79 @@ else:
|
|||
# Guess the page size of the system; if it is the
|
||||
# wrong value, performance may be worse (this is not
|
||||
# for sure though), but GC still works; must be a power of two!
|
||||
when defined(linux) or defined(windows) or defined(macosx):
|
||||
const
|
||||
PageShift = 12
|
||||
PageSize = 1 shl PageShift # on 32 bit systems 4096
|
||||
else:
|
||||
{.error: "unkown page size".}
|
||||
|
||||
const
|
||||
PageShift = if sizeof(pointer) == 4: 12 else: 13
|
||||
PageSize = 1 shl PageShift # on 32 bit systems 4096
|
||||
PageMask = PageSize-1
|
||||
|
||||
SmallChunkSize = PageSize # * 4
|
||||
|
||||
MemAlignment = sizeof(pointer)*2 # minimal memory block that can be allocated
|
||||
BitsPerUnit = sizeof(int)*8
|
||||
# a "unit" is a word, i.e. 4 bytes
|
||||
# on a 32 bit system; I do not use the term "word" because under 32-bit
|
||||
# Windows it is sometimes only 16 bits
|
||||
MemAlign = 8 # minimal memory block that can be allocated
|
||||
|
||||
BitsPerPage = PageSize div MemAlignment
|
||||
UnitsPerPage = BitsPerPage div BitsPerUnit
|
||||
# how many units do we need to describe a page:
|
||||
BitsPerPage = PageSize div MemAlign
|
||||
UnitsPerPage = BitsPerPage div (sizeof(int)*8)
|
||||
# how many ints do we need to describe a page:
|
||||
# on 32 bit systems this is only 16 (!)
|
||||
|
||||
smallRequest = PageSize div 4
|
||||
ChunkOsReturn = 1024 # in pages
|
||||
ChunkOsReturn = 64 * PageSize
|
||||
InitialMemoryRequest = ChunkOsReturn div 2 # < ChunkOsReturn!
|
||||
debugMemMan = true # we wish to debug the memory manager...
|
||||
|
||||
type
|
||||
PChunkDesc = ptr TChunkDesc
|
||||
TChunkDesc {.final, pure.} = object
|
||||
key: TAddress # address at bit 0
|
||||
next: PChunkDesc
|
||||
bits: array[0..127, int] # a bit vector
|
||||
|
||||
PChunkDescArray = ptr array[0..1000_000, PChunkDesc]
|
||||
TChunkSet {.final, pure.} = object
|
||||
counter, max: int
|
||||
head: PChunkDesc
|
||||
data: PChunkDescArray
|
||||
|
||||
when sizeof(int) == 4:
|
||||
type THalfWord = int16
|
||||
else:
|
||||
type THalfWord = int32
|
||||
# Compile time options:
|
||||
coalescRight = true
|
||||
coalescLeft = true
|
||||
|
||||
const
|
||||
TrunkShift = 9
|
||||
BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and divisible by 64
|
||||
TrunkMask = BitsPerTrunk - 1
|
||||
IntsPerTrunk = BitsPerTrunk div (sizeof(int)*8)
|
||||
IntShift = 5 + ord(sizeof(int) == 8) # 5 or 6, depending on int width
|
||||
IntMask = 1 shl IntShift - 1
|
||||
|
||||
type
|
||||
PTrunk = ptr TTrunk
|
||||
TTrunk {.final.} = object
|
||||
next: PTrunk # all nodes are connected with this pointer
|
||||
key: int # start address at bit 0
|
||||
bits: array[0..IntsPerTrunk-1, int] # a bit vector
|
||||
|
||||
TTrunkBuckets = array[0..1023, PTrunk]
|
||||
TIntSet {.final.} = object
|
||||
data: TTrunkBuckets
|
||||
|
||||
type
|
||||
TAlignType = float
|
||||
TFreeCell {.final, pure.} = object
|
||||
zeroField: pointer # type info nil means cell is not used
|
||||
next: ptr TFreeCell # next free cell in chunk
|
||||
next: ptr TFreeCell # next free cell in chunk (overlaid with refcount)
|
||||
zeroField: pointer # nil means cell is not used (overlaid with typ field)
|
||||
|
||||
PChunk = ptr TChunk
|
||||
TChunk {.final, pure.} = object
|
||||
size: int # lowest two bits are used for merging:
|
||||
# bit 0: chunk to the left is accessible and free
|
||||
# bit 1: chunk to the right is accessible and free
|
||||
len: int # for small object allocation
|
||||
prev, next: PChunk # chunks of the same (or bigger) size
|
||||
#len, used: THalfWord # index of next to allocate cell
|
||||
PChunk = ptr TBaseChunk
|
||||
PBigChunk = ptr TBigChunk
|
||||
PSmallChunk = ptr TSmallChunk
|
||||
TBaseChunk {.pure.} = object
|
||||
prevSize: int # size of previous chunk; for coalescing
|
||||
size: int # if < PageSize it is a small chunk
|
||||
used: bool # later will be optimized into prevSize...
|
||||
|
||||
TSmallChunk = object of TBaseChunk
|
||||
next, prev: PSmallChunk # chunks of the same size
|
||||
freeList: ptr TFreeCell
|
||||
data: float # a float for alignment purposes
|
||||
free: int # how many bytes remain
|
||||
acc: int # accumulator for small object allocation
|
||||
data: TAlignType # start of usable memory
|
||||
|
||||
TBigChunk = object of TBaseChunk # not necessarily > PageSize!
|
||||
next: PBigChunk # chunks of the same (or bigger) size
|
||||
prev: PBigChunk
|
||||
data: TAlignType # start of usable memory
|
||||
|
||||
template smallChunkOverhead(): expr = sizeof(TSmallChunk)-sizeof(TAlignType)
|
||||
template bigChunkOverhead(): expr = sizeof(TBigChunk)-sizeof(TAlignType)
|
||||
|
||||
proc roundup(x, v: int): int {.inline.} = return ((-x) and (v-1)) +% x
|
||||
|
||||
|
|
@ -147,240 +175,325 @@ assert(roundup(15, 8) == 16)
|
|||
type
|
||||
PLLChunk = ptr TLLChunk
|
||||
TLLChunk {.pure.} = object ## *low-level* chunk
|
||||
size: int
|
||||
when sizeof(int) == 4:
|
||||
align: int
|
||||
size: int # remaining size
|
||||
acc: int # accumulator
|
||||
|
||||
TAllocator {.final, pure.} = object
|
||||
llmem: PLLChunk
|
||||
UsedPagesCount, FreePagesCount, maxPagesCount: int
|
||||
freeSmallChunks: array[0..smallRequest div MemAlign-1, PChunk]
|
||||
freeBigChunks: array[0..ChunkOsReturn-1, PChunk]
|
||||
|
||||
currMem, maxMem: int # currently and maximum used memory size (allocated from OS)
|
||||
freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk]
|
||||
freeChunksList: PBigChunk # XXX make this a datastructure with O(1) access
|
||||
chunkStarts: TIntSet
|
||||
|
||||
proc incCurrMem(a: var TAllocator, bytes: int) {.inline.} =
|
||||
inc(a.currMem, bytes)
|
||||
|
||||
proc decCurrMem(a: var TAllocator, bytes: int) {.inline.} =
|
||||
a.maxMem = max(a.maxMem, a.currMem)
|
||||
dec(a.currMem, bytes)
|
||||
|
||||
proc getMaxMem(a: var TAllocator): int =
|
||||
# Since we update maxPagesCount only when freeing pages,
|
||||
# maxPagesCount may not be up to date. Thus we use the
|
||||
# maximum of these both values here:
|
||||
return max(a.currMem, a.maxMem)
|
||||
|
||||
var
|
||||
allocator: TAllocator
|
||||
|
||||
proc llAlloc(a: var TAllocator, size: int): pointer =
|
||||
# *low-level* alloc for the memory managers data structures. Deallocation
|
||||
# is never done.
|
||||
assert(size <= PageSize-8)
|
||||
if a.llmem.size + size > PageSize:
|
||||
a.llmem = osGetPages(PageSize)
|
||||
inc(a.gUsedPages)
|
||||
a.llmem.size = 8
|
||||
result = cast[pointer](cast[TAddress](a.llmem) + a.llmem.size)
|
||||
inc(llmem.size, size)
|
||||
if a.llmem == nil or size > a.llmem.size:
|
||||
var request = roundup(size+sizeof(TLLChunk), PageSize)
|
||||
a.llmem = cast[PLLChunk](osAllocPages(request))
|
||||
incCurrMem(a, request)
|
||||
a.llmem.size = request - sizeof(TLLChunk)
|
||||
a.llmem.acc = sizeof(TLLChunk)
|
||||
result = cast[pointer](cast[TAddress](a.llmem) + a.llmem.acc)
|
||||
dec(a.llmem.size, size)
|
||||
inc(a.llmem.acc, size)
|
||||
zeroMem(result, size)
|
||||
|
||||
|
||||
const
|
||||
InitChunkSetSize = 1024 # must be a power of two!
|
||||
|
||||
proc ChunkSetInit(s: var TChunkSet) =
|
||||
s.data = cast[PChunkDescArray](llAlloc(InitChunkSetSize * sizeof(PChunkDesc)))
|
||||
s.max = InitChunkSetSize-1
|
||||
s.counter = 0
|
||||
s.head = nil
|
||||
|
||||
proc ChunkSetGet(t: TChunkSet, key: TAddress): PChunkDesc =
|
||||
var h = cast[int](key) and t.max
|
||||
while t.data[h] != nil:
|
||||
if t.data[h].key == key: return t.data[h]
|
||||
h = nextTry(h, t.max)
|
||||
return nil
|
||||
|
||||
proc ChunkSetRawInsert(t: TChunkSet, data: PChunkDescArray,
|
||||
desc: PChunkDesc) =
|
||||
var h = cast[int](desc.key) and t.max
|
||||
while data[h] != nil:
|
||||
assert(data[h] != desc)
|
||||
h = nextTry(h, t.max)
|
||||
assert(data[h] == nil)
|
||||
data[h] = desc
|
||||
|
||||
proc ChunkSetEnlarge(t: var TChunkSet) =
|
||||
var oldMax = t.max
|
||||
t.max = ((t.max+1)*2)-1
|
||||
var n = cast[PChunkDescArray](llAlloc((t.max + 1) * sizeof(PChunkDescArray)))
|
||||
for i in 0 .. oldmax:
|
||||
if t.data[i] != nil:
|
||||
ChunkSetRawInsert(t, n, t.data[i])
|
||||
tlsf_free(t.data)
|
||||
t.data = n
|
||||
|
||||
proc ChunkSetPut(t: var TChunkSet, key: TAddress): PChunkDesc =
|
||||
var h = cast[int](key) and t.max
|
||||
while true:
|
||||
var x = t.data[h]
|
||||
if x == nil: break
|
||||
if x.key == key: return x
|
||||
h = nextTry(h, t.max)
|
||||
|
||||
if ((t.max+1)*2 < t.counter*3) or ((t.max+1)-t.counter < 4):
|
||||
ChunkSetEnlarge(t)
|
||||
inc(t.counter)
|
||||
h = cast[int](key) and t.max
|
||||
while t.data[h] != nil: h = nextTry(h, t.max)
|
||||
assert(t.data[h] == nil)
|
||||
# the new page descriptor goes into result
|
||||
result = cast[PChunkDesc](llAlloc(sizeof(TChunkDesc)))
|
||||
result.next = t.head
|
||||
result.key = key
|
||||
t.head = result
|
||||
t.data[h] = result
|
||||
|
||||
# ---------- slightly higher level procs --------------------------------------
|
||||
|
||||
proc in_Operator(s: TChunkSet, cell: PChunk): bool =
|
||||
var u = cast[TAddress](cell)
|
||||
var t = ChunkSetGet(s, u shr PageShift)
|
||||
if t != nil:
|
||||
u = (u %% PageSize) /% MemAlignment
|
||||
result = (t.bits[u /% BitsPerUnit] and (1 shl (u %% BitsPerUnit))) != 0
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc incl(s: var TCellSet, cell: PCell) =
|
||||
var u = cast[TAddress](cell)
|
||||
var t = ChunkSetPut(s, u shr PageShift)
|
||||
u = (u %% PageSize) /% MemAlignment
|
||||
t.bits[u /% BitsPerUnit] = t.bits[u /% BitsPerUnit] or
|
||||
(1 shl (u %% BitsPerUnit))
|
||||
|
||||
proc excl(s: var TCellSet, cell: PCell) =
|
||||
var u = cast[TAddress](cell)
|
||||
var t = ChunkSetGet(s, u shr PageShift)
|
||||
if t != nil:
|
||||
u = (u %% PageSize) /% MemAlignment
|
||||
t.bits[u /% BitsPerUnit] = (t.bits[u /% BitsPerUnit] and
|
||||
not (1 shl (u %% BitsPerUnit)))
|
||||
|
||||
iterator elements(t: TChunkSet): PChunk {.inline.} =
|
||||
# while traversing it is forbidden to add pointers to the tree!
|
||||
var r = t.head
|
||||
while r != nil:
|
||||
var i = 0
|
||||
while i <= high(r.bits):
|
||||
var w = r.bits[i] # taking a copy of r.bits[i] here is correct, because
|
||||
# modifying operations are not allowed during traversation
|
||||
var j = 0
|
||||
while w != 0: # test all remaining bits for zero
|
||||
if (w and 1) != 0: # the bit is set!
|
||||
yield cast[PCell]((r.key shl PageShift) or # +%
|
||||
(i*%BitsPerUnit+%j) *% MemAlignment)
|
||||
inc(j)
|
||||
w = w shr 1
|
||||
inc(i)
|
||||
r = r.next
|
||||
|
||||
|
||||
# ------------- chunk management ----------------------------------------------
|
||||
proc removeChunk(a: var TAllocator, c: PChunk) {.inline.} =
|
||||
if c.prev != nil: c.prev.next = c.next
|
||||
if c.next != nil: c.next.prev = c.prev
|
||||
if a.freeChunks[c.size div PageSize] == c:
|
||||
a.freeChunks[c.size div PageSize] = c.next
|
||||
|
||||
proc addChunk(a: var TAllocator, c: PChunk) {.inline.} =
|
||||
var s = abs(c.size) div PageSize
|
||||
proc IntSetGet(t: TIntSet, key: int): PTrunk =
|
||||
var it = t.data[key and high(t.data)]
|
||||
while it != nil:
|
||||
if it.key == key: return it
|
||||
it = it.next
|
||||
result = nil
|
||||
|
||||
proc IntSetPut(t: var TIntSet, key: int): PTrunk =
|
||||
result = IntSetGet(t, key)
|
||||
if result == nil:
|
||||
result = cast[PTrunk](llAlloc(allocator, sizeof(result^)))
|
||||
result.next = t.data[key and high(t.data)]
|
||||
t.data[key and high(t.data)] = result
|
||||
result.key = key
|
||||
|
||||
proc Contains(s: TIntSet, key: int): bool =
|
||||
var t = IntSetGet(s, key shr TrunkShift)
|
||||
if t != nil:
|
||||
var u = key and TrunkMask
|
||||
result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc Incl(s: var TIntSet, key: int) =
|
||||
var t = IntSetPut(s, key shr TrunkShift)
|
||||
var u = key and TrunkMask
|
||||
t.bits[u shr IntShift] = t.bits[u shr IntShift] or (1 shl (u and IntMask))
|
||||
|
||||
proc Excl(s: var TIntSet, key: int) =
|
||||
var t = IntSetGet(s, key shr TrunkShift)
|
||||
if t != nil:
|
||||
var u = key and TrunkMask
|
||||
t.bits[u shr IntShift] = t.bits[u shr IntShift] and not
|
||||
(1 shl (u and IntMask))
|
||||
|
||||
proc ContainsOrIncl(s: var TIntSet, key: int): bool =
|
||||
var t = IntSetGet(s, key shr TrunkShift)
|
||||
if t != nil:
|
||||
var u = key and TrunkMask
|
||||
result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0
|
||||
if not result:
|
||||
t.bits[u shr IntShift] = t.bits[u shr IntShift] or
|
||||
(1 shl (u and IntMask))
|
||||
else:
|
||||
Incl(s, key)
|
||||
result = false
|
||||
|
||||
# ------------- chunk management ----------------------------------------------
|
||||
proc pageIndex(c: PChunk): int {.inline.} =
|
||||
result = cast[TAddress](c) shr PageShift
|
||||
|
||||
proc pageIndex(p: pointer): int {.inline.} =
|
||||
result = cast[TAddress](p) shr PageShift
|
||||
|
||||
proc pageAddr(p: pointer): PChunk {.inline.} =
|
||||
result = cast[PChunk](cast[TAddress](p) and not PageMask)
|
||||
assert(Contains(allocator.chunkStarts, pageIndex(result)))
|
||||
|
||||
var lastSize = PageSize
|
||||
|
||||
proc requestOsChunks(a: var TAllocator, size: int): PBigChunk =
|
||||
incCurrMem(a, size)
|
||||
result = cast[PBigChunk](osAllocPages(size))
|
||||
assert((cast[TAddress](result) and PageMask) == 0)
|
||||
result.next = nil
|
||||
result.prev = nil
|
||||
result.used = false
|
||||
result.size = size
|
||||
# update next.prevSize:
|
||||
var nxt = cast[TAddress](result) +% size
|
||||
assert((nxt and PageMask) == 0)
|
||||
var next = cast[PChunk](nxt)
|
||||
if pageIndex(next) in a.chunkStarts:
|
||||
#echo("Next already allocated!")
|
||||
next.prevSize = size
|
||||
# set result.prevSize:
|
||||
var prv = cast[TAddress](result) -% lastSize
|
||||
assert((nxt and PageMask) == 0)
|
||||
var prev = cast[PChunk](prv)
|
||||
if pageIndex(prev) in a.chunkStarts and prev.size == lastSize:
|
||||
#echo("Prev already allocated!")
|
||||
result.prevSize = lastSize
|
||||
else:
|
||||
result.prevSize = 0 # unknown
|
||||
lastSize = size # for next request
|
||||
|
||||
proc freeOsChunks(a: var TAllocator, p: pointer, size: int) =
|
||||
# update next.prevSize:
|
||||
var c = cast[PChunk](p)
|
||||
var nxt = cast[TAddress](p) +% c.size
|
||||
assert((nxt and PageMask) == 0)
|
||||
var next = cast[PChunk](nxt)
|
||||
if pageIndex(next) in a.chunkStarts:
|
||||
next.prevSize = 0 # XXX used
|
||||
excl(a.chunkStarts, pageIndex(p))
|
||||
osDeallocPages(p, size)
|
||||
decCurrMem(a, size)
|
||||
|
||||
proc isAccessible(p: pointer): bool {.inline.} =
|
||||
result = Contains(allocator.chunkStarts, pageIndex(p))
|
||||
|
||||
proc ListAdd[T](head: var T, c: T) {.inline.} =
|
||||
assert c.prev == nil
|
||||
assert c.next == nil
|
||||
c.next = head
|
||||
if head != nil:
|
||||
assert head.prev == nil
|
||||
head.prev = c
|
||||
head = c
|
||||
|
||||
proc ListRemove[T](head: var T, c: T) {.inline.} =
|
||||
if c == head:
|
||||
head = c.next
|
||||
assert c.prev == nil
|
||||
if head != nil: head.prev = nil
|
||||
else:
|
||||
assert c.prev != nil
|
||||
c.prev.next = c.next
|
||||
if c.next != nil: c.next.prev = c.prev
|
||||
c.next = nil
|
||||
c.prev = nil
|
||||
c.next = a.freeChunks[s]
|
||||
a.freeChunks[s] = c
|
||||
|
||||
proc isSmallChunk(c: PChunk): bool {.inline.} =
|
||||
return c.size <= SmallChunkSize-smallChunkOverhead()
|
||||
#return c.size < SmallChunkSize
|
||||
|
||||
proc chunkUnused(c: PChunk): bool {.inline.} =
|
||||
result = not c.used
|
||||
|
||||
proc freeBigChunk(a: var TAllocator, c: PBigChunk) =
|
||||
var c = c
|
||||
assert(c.size >= PageSize)
|
||||
when coalescRight:
|
||||
var ri = cast[PChunk](cast[TAddress](c) +% c.size)
|
||||
assert((cast[TAddress](ri) and PageMask) == 0)
|
||||
if isAccessible(ri) and chunkUnused(ri):
|
||||
if not isSmallChunk(ri):
|
||||
ListRemove(a.freeChunksList, cast[PBigChunk](ri))
|
||||
inc(c.size, ri.size)
|
||||
excl(a.chunkStarts, pageIndex(ri))
|
||||
when coalescLeft:
|
||||
if c.prevSize != 0:
|
||||
var le = cast[PChunk](cast[TAddress](c) -% c.prevSize)
|
||||
assert((cast[TAddress](le) and PageMask) == 0)
|
||||
if isAccessible(le) and chunkUnused(le):
|
||||
if not isSmallChunk(le):
|
||||
ListRemove(a.freeChunksList, cast[PBigChunk](le))
|
||||
inc(le.size, c.size)
|
||||
excl(a.chunkStarts, pageIndex(c))
|
||||
c = cast[PBigChunk](le)
|
||||
|
||||
proc freeChunk(a: var TAllocator, c: PChunk) =
|
||||
assert(c.size > 0)
|
||||
if c.size < PageSize: c.size = PageSize
|
||||
var le = cast[PChunk](cast[TAddress](p) and not PageMask -% PageSize)
|
||||
var ri = cast[PChunk](cast[TAddress](p) and not PageMask +%
|
||||
c.size +% PageSize)
|
||||
if isStartOfAChunk(ri) and ri.size < 0:
|
||||
removeChunk(a, ri)
|
||||
inc(c.size, -ri.size)
|
||||
if isEndOfAChunk(le):
|
||||
le = cast[PChunk](cast[TAddress](p) and not PageMask -%
|
||||
le.chunkStart+PageSize)
|
||||
if le.size < 0:
|
||||
removeChunk(a, le)
|
||||
inc(le.size, c.size)
|
||||
addChunk(a, le)
|
||||
return
|
||||
c.size = -c.size
|
||||
addChunk(a, c)
|
||||
if c.size < ChunkOsReturn:
|
||||
ListAdd(a.freeChunksList, c)
|
||||
c.used = false
|
||||
else:
|
||||
freeOsChunks(a, c, c.size)
|
||||
|
||||
proc splitChunk(a: var TAllocator, c: PChunk, size: int) =
|
||||
var rest = cast[PChunk](cast[TAddress](p) + size)
|
||||
rest.size = size - c.size # results in negative number, because rest is free
|
||||
addChunk(a, rest)
|
||||
# mark pages as accessible:
|
||||
ChunkTablePut(a, rest, bitAccessible)
|
||||
proc splitChunk(a: var TAllocator, c: PBigChunk, size: int) =
|
||||
var rest = cast[PBigChunk](cast[TAddress](c) +% size)
|
||||
rest.size = c.size - size
|
||||
rest.used = false
|
||||
rest.next = nil # XXX
|
||||
rest.prev = nil
|
||||
rest.prevSize = size
|
||||
c.size = size
|
||||
incl(a.chunkStarts, pageIndex(rest))
|
||||
ListAdd(a.freeChunksList, rest)
|
||||
|
||||
proc getChunkOfSize(a: var TAllocator, size: int): PChunk =
|
||||
for i in size..high(a.freeChunks):
|
||||
result = a.freeChunks[i]
|
||||
if result != nil:
|
||||
if i != size: splitChunk(a, result, size)
|
||||
else: removeChunk(a, result)
|
||||
result.prev = nil
|
||||
result.next = nil
|
||||
break
|
||||
proc getBigChunk(a: var TAllocator, size: int): PBigChunk =
|
||||
# use first fit for now:
|
||||
assert((size and PageMask) == 0)
|
||||
result = a.freeChunksList
|
||||
block search:
|
||||
while result != nil:
|
||||
assert chunkUnused(result)
|
||||
if result.size == size:
|
||||
ListRemove(a.freeChunksList, result)
|
||||
break search
|
||||
elif result.size > size:
|
||||
splitChunk(a, result, size)
|
||||
ListRemove(a.freeChunksList, result)
|
||||
break search
|
||||
result = result.next
|
||||
if size < InitialMemoryRequest:
|
||||
result = requestOsChunks(a, InitialMemoryRequest)
|
||||
splitChunk(a, result, size)
|
||||
else:
|
||||
result = requestOsChunks(a, size)
|
||||
result.prevSize = 0
|
||||
result.used = true
|
||||
incl(a.chunkStarts, pageIndex(result))
|
||||
|
||||
proc getSmallChunk(a: var TAllocator): PSmallChunk =
|
||||
var res = getBigChunk(a, PageSize)
|
||||
assert res.prev == nil
|
||||
assert res.next == nil
|
||||
result = cast[PSmallChunk](res)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
proc getChunk(p: pointer): PChunk {.inline.} =
|
||||
result = cast[PChunk](cast[TAddress](p) and not PageMask)
|
||||
|
||||
proc getCellSize(p: pointer): int {.inline.} =
|
||||
var c = getChunk(p)
|
||||
result = abs(c.size)
|
||||
var c = pageAddr(p)
|
||||
result = c.size
|
||||
|
||||
proc alloc(a: var TAllocator, size: int): pointer =
|
||||
if size <= smallRequest:
|
||||
# allocate a small block
|
||||
proc alloc(a: var TAllocator, requestedSize: int): pointer =
|
||||
var size = roundup(max(requestedSize, sizeof(TFreeCell)), MemAlign)
|
||||
if size <= SmallChunkSize-smallChunkOverhead():
|
||||
# allocate a small block: for small chunks, we use only its next pointer
|
||||
var s = size div MemAlign
|
||||
var c = a.freeSmallChunks[s]
|
||||
if c == nil:
|
||||
c = getChunkOfSize(0)
|
||||
c = getSmallChunk(a)
|
||||
c.freeList = nil
|
||||
assert c.size == PageSize
|
||||
c.size = size
|
||||
a.freeSmallChunks[s] = c
|
||||
c.len = 1
|
||||
c.used = 1
|
||||
c.chunkStart = 0
|
||||
result = addr(c.data[0])
|
||||
elif c.freeList != nil:
|
||||
result = c.freeList
|
||||
assert(c.freeList.zeroField == nil)
|
||||
c.freeList = c.freeList.next
|
||||
inc(c.used)
|
||||
if c.freeList == nil: removeChunk(a, c)
|
||||
c.acc = size
|
||||
c.free = SmallChunkSize - smallChunkOverhead() - size
|
||||
c.next = nil
|
||||
c.prev = nil
|
||||
ListAdd(a.freeSmallChunks[s], c)
|
||||
result = addr(c.data)
|
||||
else:
|
||||
assert(c.len*size <= high(c.data))
|
||||
result = addr(c.data[c.len*size])
|
||||
inc(c.len)
|
||||
inc(c.used)
|
||||
if c.len*size > high(c.data): removeChunk(a, c)
|
||||
assert c.next != c
|
||||
assert c.size == size
|
||||
if c.freeList == nil:
|
||||
assert(c.acc + smallChunkOverhead() + size <= SmallChunkSize)
|
||||
result = cast[pointer](cast[TAddress](addr(c.data)) +% c.acc)
|
||||
inc(c.acc, size)
|
||||
else:
|
||||
result = c.freeList
|
||||
assert(c.freeList.zeroField == nil)
|
||||
c.freeList = c.freeList.next
|
||||
dec(c.free, size)
|
||||
if c.free < size:
|
||||
ListRemove(a.freeSmallChunks[s], c)
|
||||
else:
|
||||
size = roundup(requestedSize+bigChunkOverhead(), PageSize)
|
||||
# allocate a large block
|
||||
var c = getChunkOfSize(size shr PageShift)
|
||||
result = addr(c.data[0])
|
||||
c.freeList = nil
|
||||
c.size = size
|
||||
c.len = 0
|
||||
c.used = 0
|
||||
c.chunkStart = 0
|
||||
var c = getBigChunk(a, size)
|
||||
assert c.prev == nil
|
||||
assert c.next == nil
|
||||
assert c.size == size
|
||||
result = addr(c.data)
|
||||
cast[ptr TFreeCell](result).zeroField = cast[ptr TFreeCell](1) # make it != nil
|
||||
#echo("setting to one: ", $cast[TAddress](addr(cast[ptr TFreeCell](result).zeroField)))
|
||||
|
||||
proc contains(list, x: PSmallChunk): bool =
|
||||
var it = list
|
||||
while it != nil:
|
||||
if it == x: return true
|
||||
it = it.next
|
||||
|
||||
proc dealloc(a: var TAllocator, p: pointer) =
|
||||
var c = getChunk(p)
|
||||
if c.size <= smallRequest:
|
||||
# free small block:
|
||||
var c = pageAddr(p)
|
||||
if isSmallChunk(c):
|
||||
# `p` is within a small chunk:
|
||||
var c = cast[PSmallChunk](c)
|
||||
var s = c.size
|
||||
var f = cast[ptr TFreeCell](p)
|
||||
#echo("setting to nil: ", $cast[TAddress](addr(f.zeroField)))
|
||||
assert(f.zeroField != nil)
|
||||
f.zeroField = nil
|
||||
f.next = c.freeList
|
||||
c.freeList = p
|
||||
dec(c.used)
|
||||
if c.used == 0: freeChunk(c)
|
||||
c.freeList = f
|
||||
# check if it is not in the freeSmallChunks[s] list:
|
||||
if c.free < s:
|
||||
assert c notin a.freeSmallChunks[s div memAlign]
|
||||
# add it to the freeSmallChunks[s] array:
|
||||
ListAdd(a.freeSmallChunks[s div memAlign], c)
|
||||
inc(c.free, s)
|
||||
else:
|
||||
inc(c.free, s)
|
||||
if c.free == SmallChunkSize-smallChunkOverhead():
|
||||
ListRemove(a.freeSmallChunks[s div memAlign], c)
|
||||
c.size = SmallChunkSize
|
||||
freeBigChunk(a, cast[PBigChunk](c))
|
||||
else:
|
||||
# free big chunk
|
||||
freeChunk(c)
|
||||
freeBigChunk(a, cast[PBigChunk](c))
|
||||
|
||||
proc realloc(a: var TAllocator, p: pointer, size: int): pointer =
|
||||
# could be made faster, but this is unnecessary, the GC does not use it anyway
|
||||
|
|
@ -389,6 +502,34 @@ proc realloc(a: var TAllocator, p: pointer, size: int): pointer =
|
|||
dealloc(a, p)
|
||||
|
||||
proc isAllocatedPtr(a: TAllocator, p: pointer): bool =
|
||||
var c = getChunk(p)
|
||||
if c in a.accessibleChunks and c.size > 0:
|
||||
result = cast[ptr TFreeCell](p).zeroField != nil
|
||||
if isAccessible(p):
|
||||
var c = pageAddr(p)
|
||||
if not chunkUnused(c):
|
||||
if isSmallChunk(c):
|
||||
result = (cast[TAddress](p) -% cast[TAddress](c) -%
|
||||
smallChunkOverhead()) %% c.size == 0 and
|
||||
cast[ptr TFreeCell](p).zeroField != nil
|
||||
else:
|
||||
var c = cast[PBigChunk](c)
|
||||
result = p == addr(c.data)
|
||||
|
||||
when isMainModule:
|
||||
const iterations = 4000_000
|
||||
incl(allocator.chunkStarts, 11)
|
||||
assert 11 in allocator.chunkStarts
|
||||
excl(allocator.chunkStarts, 11)
|
||||
assert 11 notin allocator.chunkStarts
|
||||
var p: array [1..iterations, pointer]
|
||||
for i in 7..7:
|
||||
var x = i * 8
|
||||
for j in 1.. iterations:
|
||||
p[j] = alloc(allocator, x)
|
||||
for j in 1..iterations:
|
||||
assert isAllocatedPtr(allocator, p[j])
|
||||
echo($i, " used memory: ", $(allocator.currMem))
|
||||
for j in countdown(iterations, 1):
|
||||
#echo("j: ", $j)
|
||||
dealloc(allocator, p[j])
|
||||
assert(not isAllocatedPtr(allocator, p[j]))
|
||||
echo($i, " after freeing: ", $(allocator.currMem))
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
; that is the only convention any C compiler supports.
|
||||
|
||||
\python{
|
||||
# as usual I use my own preprocessor :-)
|
||||
import os
|
||||
|
||||
def c(name):
|
||||
|
|
|
|||
|
|
@ -43,8 +43,12 @@ proc genericAssign(dest, src: Pointer, mt: PNimType) =
|
|||
x^ = nil
|
||||
return
|
||||
assert(dest != nil)
|
||||
unsureAsgnRef(cast[ppointer](dest),
|
||||
newObj(mt, seq.len * mt.base.size + GenericSeqSize))
|
||||
when defined(boehmGC):
|
||||
unsureAsgnRef(cast[ppointer](dest),
|
||||
newObj(seq.len * mt.base.size + GenericSeqSize))
|
||||
else:
|
||||
unsureAsgnRef(cast[ppointer](dest),
|
||||
newObj(mt, seq.len * mt.base.size + GenericSeqSize))
|
||||
var dst = cast[taddress](cast[ppointer](dest)^)
|
||||
for i in 0..seq.len-1:
|
||||
genericAssign(
|
||||
|
|
|
|||
|
|
@ -1,486 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<!-- This file is generated by Nimrod. -->
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>Module cgi</title>
|
||||
<style type="text/css">
|
||||
|
||||
span.DecNumber {color: blue}
|
||||
span.BinNumber {color: blue}
|
||||
span.HexNumber {color: blue}
|
||||
span.OctNumber {color: blue}
|
||||
span.FloatNumber {color: blue}
|
||||
span.Identifier {color: black}
|
||||
span.Keyword {font-weight: bold}
|
||||
span.StringLit {color: blue}
|
||||
span.LongStringLit {color: blue}
|
||||
span.CharLit {color: blue}
|
||||
span.EscapeSequence {color: black}
|
||||
span.Operator {color: black}
|
||||
span.Punctation {color: black}
|
||||
span.Comment, span.LongComment {font-style:italic; color: green}
|
||||
span.RegularExpression {color: pink}
|
||||
span.TagStart {color: yellow}
|
||||
span.TagEnd {color: yellow}
|
||||
span.Key {color: blue}
|
||||
span.Value {color: black}
|
||||
span.RawData {color: blue}
|
||||
span.Assembler {color: blue}
|
||||
span.Preprocessor {color: yellow}
|
||||
span.Directive {color: yellow}
|
||||
span.Command, span.Rule, span.Hyperlink, span.Label, span.Reference,
|
||||
span.Other {color: black}
|
||||
|
||||
div.navigation {
|
||||
float: left;
|
||||
width: 27%; //25em;
|
||||
margin: 0; padding: 0; /*
|
||||
border: 1px dashed gold; */
|
||||
outline: 3px outset #7F7F7F; //#99ff99; //gold;
|
||||
background-color: #7F7F7F;
|
||||
}
|
||||
|
||||
div.navigation ul {list-style-type: none;}
|
||||
div.navigation ul li a, div.navigation ul li a:visited {
|
||||
font-weight: bold;
|
||||
color: #FFFFFF; // #CC0000;
|
||||
text-decoration: none;
|
||||
}
|
||||
div.navigation ul li a:hover {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
/*outline: 2px outset #7F7F7F;*/
|
||||
color: gold;
|
||||
/* background-color: #FFFFFF; // #1A1A1A; // #779977;*/
|
||||
}
|
||||
|
||||
div.content {
|
||||
margin-left: 27%; // 25em;
|
||||
padding: 0 1em;
|
||||
/*border: 1px dashed #1A1A1A;*/
|
||||
min-width: 16em;
|
||||
}
|
||||
|
||||
dl.item dd, dl.item dd p {
|
||||
margin-top:3px;
|
||||
}
|
||||
dl.item dd pre {
|
||||
margin-left: 15pt;
|
||||
border: 0px;
|
||||
}
|
||||
dl.item dt, dl.item dt pre {
|
||||
margin: 20pt 0 0 0;
|
||||
}
|
||||
|
||||
pre, span.tok {
|
||||
background-color:#F9F9F9;
|
||||
border:1px dotted #2F6FAB;
|
||||
color:black;
|
||||
}
|
||||
|
||||
/*
|
||||
:Author: David Goodger
|
||||
:Contact: goodger@python.org
|
||||
:Date: Date: 2006-05-21 22:44:42 +0200 (Sun, 21 May 2006)
|
||||
:Revision: Revision: 4564
|
||||
:Copyright: This stylesheet has been placed in the public domain.
|
||||
|
||||
Default cascading style sheet for the HTML output of Docutils.
|
||||
|
||||
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
|
||||
customize this style sheet.
|
||||
*/
|
||||
/* used to remove borders from tables and images */
|
||||
.borderless, table.borderless td, table.borderless th { border: 0 }
|
||||
|
||||
table.borderless td, table.borderless th {
|
||||
/* Override padding for "table.docutils td" with "! important".
|
||||
The right padding separates the table cells. */
|
||||
padding: 0 0.5em 0 0 ! important }
|
||||
|
||||
.first { margin-top: 0 ! important }
|
||||
.last, .with-subtitle { margin-bottom: 0 ! important }
|
||||
.hidden { display: none }
|
||||
a.toc-backref { text-decoration: none ; color: black }
|
||||
blockquote.epigraph { margin: 2em 5em ; }
|
||||
dl.docutils dd { margin-bottom: 0.5em }
|
||||
div.abstract { margin: 2em 5em }
|
||||
div.abstract p.topic-title { font-weight: bold ; text-align: center }
|
||||
div.admonition, div.attention, div.caution, div.danger, div.error,
|
||||
div.hint, div.important, div.note, div.tip, div.warning {
|
||||
margin: 2em ; border: medium outset ; padding: 1em }
|
||||
div.admonition p.admonition-title, div.hint p.admonition-title,
|
||||
div.important p.admonition-title, div.note p.admonition-title,
|
||||
div.tip p.admonition-title { font-weight: bold ; font-family: sans-serif }
|
||||
|
||||
div.attention p.admonition-title, div.caution p.admonition-title,
|
||||
div.danger p.admonition-title, div.error p.admonition-title,
|
||||
div.warning p.admonition-title { color: red ; font-weight: bold ;
|
||||
font-family: sans-serif }
|
||||
|
||||
/* Uncomment (and remove this text!) to get reduced vertical space in
|
||||
compound paragraphs.
|
||||
div.compound .compound-first, div.compound .compound-middle {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
div.compound .compound-last, div.compound .compound-middle {
|
||||
margin-top: 0.5em }
|
||||
*/
|
||||
|
||||
div.dedication { margin: 2em 5em ; text-align: center ; font-style: italic }
|
||||
div.dedication p.topic-title { font-weight: bold ; font-style: normal }
|
||||
div.figure { margin-left: 2em ; margin-right: 2em }
|
||||
div.footer, div.header { clear: both; font-size: smaller }
|
||||
div.line-block { display: block ; margin-top: 1em ; margin-bottom: 1em }
|
||||
div.line-block div.line-block { margin-top: 0 ; margin-bottom: 0 ;
|
||||
margin-left: 1.5em }
|
||||
div.sidebar { margin-left: 1em ; border: medium outset ;
|
||||
padding: 1em ; background-color: #ffffee ; width: 40% ; float: right ;
|
||||
clear: right }
|
||||
|
||||
div.sidebar p.rubric { font-family: sans-serif ; font-size: medium }
|
||||
div.system-messages { margin: 5em }
|
||||
div.system-messages h1 { color: red }
|
||||
div.system-message { border: medium outset ; padding: 1em }
|
||||
div.system-message p.system-message-title { color: red ; font-weight: bold }
|
||||
div.topic { margin: 2em;}
|
||||
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
|
||||
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
|
||||
margin-top: 0.4em }
|
||||
h1.title { text-align: center }
|
||||
h2.subtitle { text-align: center }
|
||||
hr.docutils { width: 75% }
|
||||
img.align-left { clear: left }
|
||||
img.align-right { clear: right }
|
||||
ol.simple, ul.simple { margin-bottom: 1em }
|
||||
ol.arabic { list-style: decimal }
|
||||
ol.loweralpha { list-style: lower-alpha }
|
||||
ol.upperalpha { list-style: upper-alpha }
|
||||
ol.lowerroman { list-style: lower-roman }
|
||||
ol.upperroman { list-style: upper-roman }
|
||||
p.attribution { text-align: right ; margin-left: 50% }
|
||||
p.caption { font-style: italic }
|
||||
p.credits { font-style: italic ; font-size: smaller }
|
||||
p.label { white-space: nowrap }
|
||||
p.rubric { font-weight:bold;font-size:larger;color:maroon;text-align:center}
|
||||
p.sidebar-title {font-family: sans-serif ;font-weight: bold ;font-size: larger }
|
||||
p.sidebar-subtitle {font-family: sans-serif ; font-weight: bold }
|
||||
p.topic-title { font-weight: bold }
|
||||
pre.address { margin-bottom: 0;margin-top:0;font-family:serif;font-size:100% }
|
||||
pre.literal-block, pre.doctest-block {margin-left: 2em ;margin-right: 2em }
|
||||
span.classifier {font-family: sans-serif;font-style: oblique }
|
||||
span.classifier-delimiter {font-family: sans-serif;font-weight: bold }
|
||||
span.interpreted {font-family: sans-serif }
|
||||
span.option {white-space: nowrap }
|
||||
span.pre {white-space: pre }
|
||||
span.problematic {color: red }
|
||||
span.section-subtitle {
|
||||
/* font-size relative to parent (h1..h6 element) */
|
||||
font-size: 80% }
|
||||
|
||||
table.citation { border-left: solid 1px gray; margin-left: 1px }
|
||||
table.docinfo {margin: 2em 4em }
|
||||
table.docutils {margin-top: 0.5em;margin-bottom: 0.5em }
|
||||
table.footnote {border-left: solid 1px black;margin-left: 1px }
|
||||
|
||||
table.docutils td, table.docutils th,
|
||||
table.docinfo td, table.docinfo th {padding-left: 0.5em;padding-right: 0.5em;
|
||||
vertical-align: top}
|
||||
|
||||
table.docutils th.field-name, table.docinfo th.docinfo-name {
|
||||
font-weight: bold;text-align: left;white-space: nowrap;padding-left: 0 }
|
||||
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
|
||||
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {font-size: 100% }
|
||||
ul.auto-toc { list-style-type: none }
|
||||
/*a.reference { color: #E00000; font-weight:bold;}
|
||||
a.reference:hover {color: #E00000;background-color: #ffff00;display: margin;
|
||||
font-weight:bold;}*/
|
||||
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
function toggleElem(id) {
|
||||
var e = document.getElementById(id);
|
||||
e.style.display = e.style.display == 'none' ? 'block' : 'none';
|
||||
}
|
||||
|
||||
var gOpen = 'none'
|
||||
function toggleAll() {
|
||||
gOpen = gOpen == 'none' ? 'block' : 'none';
|
||||
var i = 1
|
||||
while (1) {
|
||||
var e = document.getElementById("m"+i)
|
||||
if (!e) break;
|
||||
e.style.display = gOpen
|
||||
i++;
|
||||
}
|
||||
document.getElementById('toggleButton').value =
|
||||
gOpen == 'none' ? 'Show Details' : 'Hide Details';
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="documentId">
|
||||
<h1 class="title">Module cgi</h1>
|
||||
<div class="navigation">
|
||||
<p class="topic-title first">Navigation</p>
|
||||
<ul class="simple">
|
||||
<li>
|
||||
<a class="reference" href="#6" id="56">Types</a>
|
||||
<ul class="simple">
|
||||
<li><a class="reference" href="#103">ECgi</a></li>
|
||||
<li><a class="reference" href="#104">TRequestMethod</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a class="reference" href="#9" id="59">Procs</a>
|
||||
<ul class="simple">
|
||||
<li><a class="reference" href="#101">URLencode</a></li>
|
||||
<li><a class="reference" href="#102">URLdecode</a></li>
|
||||
<li><a class="reference" href="#105">readData</a></li>
|
||||
<li><a class="reference" href="#106">validateData</a></li>
|
||||
<li><a class="reference" href="#107">getContentLength</a></li>
|
||||
<li><a class="reference" href="#108">getContentType</a></li>
|
||||
<li><a class="reference" href="#109">getDocumentRoot</a></li>
|
||||
<li><a class="reference" href="#110">getGatewayInterface</a></li>
|
||||
<li><a class="reference" href="#111">getHttpAccept</a></li>
|
||||
<li><a class="reference" href="#112">getHttpAcceptCharset</a></li>
|
||||
<li><a class="reference" href="#113">getHttpAccept Encoding</a></li>
|
||||
<li><a class="reference" href="#114">getHttpAccept Language</a></li>
|
||||
<li><a class="reference" href="#115">getHttpConnection</a></li>
|
||||
<li><a class="reference" href="#116">getHttpCookie</a></li>
|
||||
<li><a class="reference" href="#117">getHttpHost</a></li>
|
||||
<li><a class="reference" href="#118">getHttpReferer</a></li>
|
||||
<li><a class="reference" href="#119">getHttpUserAgent</a></li>
|
||||
<li><a class="reference" href="#120">getPathInfo</a></li>
|
||||
<li><a class="reference" href="#121">getPathTranslated</a></li>
|
||||
<li><a class="reference" href="#122">getQueryString</a></li>
|
||||
<li><a class="reference" href="#123">getRemoteAddr</a></li>
|
||||
<li><a class="reference" href="#124">getRemoteHost</a></li>
|
||||
<li><a class="reference" href="#125">getRemoteIdent</a></li>
|
||||
<li><a class="reference" href="#126">getRemotePort</a></li>
|
||||
<li><a class="reference" href="#127">getRemoteUser</a></li>
|
||||
<li><a class="reference" href="#128">getRequestMethod</a></li>
|
||||
<li><a class="reference" href="#129">getRequestURI</a></li>
|
||||
<li><a class="reference" href="#130">getScriptFilename</a></li>
|
||||
<li><a class="reference" href="#131">getScriptName</a></li>
|
||||
<li><a class="reference" href="#132">getServerAddr</a></li>
|
||||
<li><a class="reference" href="#133">getServerAdmin</a></li>
|
||||
<li><a class="reference" href="#134">getServerName</a></li>
|
||||
<li><a class="reference" href="#135">getServerPort</a></li>
|
||||
<li><a class="reference" href="#136">getServerProtocol</a></li>
|
||||
<li><a class="reference" href="#137">getServerSignature</a></li>
|
||||
<li><a class="reference" href="#138">getServerSoftware</a></li>
|
||||
<li><a class="reference" href="#139">setTestData</a></li>
|
||||
<li><a class="reference" href="#140">writeContentType</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<div class="content">
|
||||
This module implements helper procs for CGI applictions. Example:<pre>
|
||||
<span class="Keyword">import</span> <span class="Identifier">strtabs</span><span class="Punctation">,</span> <span class="Identifier">cgi</span>
|
||||
|
||||
<span class="Comment"># Fill the values when debugging:</span>
|
||||
<span class="Keyword">when</span> <span class="Identifier">debug</span><span class="Punctation">:</span>
|
||||
<span class="Identifier">setTestData</span><span class="Punctation">(</span><span class="StringLit">"name"</span><span class="Punctation">,</span> <span class="StringLit">"Klaus"</span><span class="Punctation">,</span> <span class="StringLit">"password"</span><span class="Punctation">,</span> <span class="StringLit">"123456"</span><span class="Punctation">)</span>
|
||||
<span class="Comment"># read the data into `myData`</span>
|
||||
<span class="Keyword">var</span> <span class="Identifier">myData</span> <span class="Operator">=</span> <span class="Identifier">readData</span><span class="Punctation">(</span><span class="Punctation">)</span>
|
||||
<span class="Comment"># check that the data's variable names are "name" or "passwort"</span>
|
||||
<span class="Identifier">validateData</span><span class="Punctation">(</span><span class="Identifier">myData</span><span class="Punctation">,</span> <span class="StringLit">"name"</span><span class="Punctation">,</span> <span class="StringLit">"password"</span><span class="Punctation">)</span>
|
||||
<span class="Comment"># start generating content:</span>
|
||||
<span class="Identifier">writeContentType</span><span class="Punctation">(</span><span class="Punctation">)</span>
|
||||
<span class="Comment"># generate content:</span>
|
||||
<span class="Identifier">write</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">"<!DOCTYPE HTML PUBLIC </span><span class="EscapeSequence">\"</span>-//W3C//DTD HTML 4.01//EN<span class="EscapeSequence">\"</span><span class="EscapeSequence">>\n</span><span class="StringLit">"</span><span class="Punctation">)</span>
|
||||
<span class="Identifier">write</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">"<html><head><title>Test</title></head><body></span><span class="EscapeSequence">\n</span><span class="StringLit">"</span><span class="Punctation">)</span>
|
||||
<span class="Identifier">writeln</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">"your name: "</span> <span class="Operator">&</span> <span class="Identifier">myData</span><span class="Punctation">[</span><span class="StringLit">"name"</span><span class="Punctation">]</span><span class="Punctation">)</span>
|
||||
<span class="Identifier">writeln</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">"your password: "</span> <span class="Operator">&</span> <span class="Identifier">myData</span><span class="Punctation">[</span><span class="StringLit">"password"</span><span class="Punctation">]</span><span class="Punctation">)</span>
|
||||
<span class="Identifier">writeln</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">"</body></html>"</span><span class="Punctation">)</span></pre>
|
||||
<div class="section" id="6">
|
||||
<h1><a class="toc-backref" href="#56">Types</a></h1>
|
||||
<dl class="item">
|
||||
<dt id="103"><pre><span class="Identifier">ECgi</span><span class="Operator">*</span> <span class="Other">=</span> <span class="Keyword">object</span> <span class="Keyword">of</span> <span class="Identifier">EIO</span></pre></dt>
|
||||
<dd>
|
||||
the exception that is raised, if a CGI error occurs
|
||||
</dd>
|
||||
<dt id="104"><pre><span class="Identifier">TRequestMethod</span><span class="Operator">*</span> <span class="Other">=</span> <span class="Keyword">enum</span>
|
||||
<span class="Identifier">methodPost</span><span class="Other">,</span> <span class="Comment">## query uses the POST method</span>
|
||||
<span class="Identifier">methodGet</span> <span class="Comment">## query uses the GET method</span></pre></dt>
|
||||
<dd>
|
||||
the used request method
|
||||
</dd>
|
||||
|
||||
</dl></div>
|
||||
<div class="section" id="9">
|
||||
<h1><a class="toc-backref" href="#59">Procs</a></h1>
|
||||
<dl class="item">
|
||||
<dt id="101"><pre><span class="Keyword">proc</span> <span class="Identifier">URLencode</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">s</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
Encodes a value to be HTTP safe: This means that characters in the set <tt class="docutils literal"><span class="pre">{'A'..'Z', 'a'..'z', '0'..'9', '_'}</span></tt> are carried over to the result, a space is converted to <tt class="docutils literal"><span class="pre">'+'</span></tt> and every other character is encoded as <tt class="docutils literal"><span class="pre">'%xx'</span></tt> where <tt class="docutils literal"><span class="pre">xx</span></tt> denotes its hexadecimal value.
|
||||
</dd>
|
||||
<dt id="102"><pre><span class="Keyword">proc</span> <span class="Identifier">URLdecode</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">s</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
Decodes a value from its HTTP representation: This means that a <tt class="docutils literal"><span class="pre">'+'</span></tt> is converted to a space, <tt class="docutils literal"><span class="pre">'%xx'</span></tt> (where <tt class="docutils literal"><span class="pre">xx</span></tt> denotes a hexadecimal value) is converted to the character with ordinal number <tt class="docutils literal"><span class="pre">xx</span></tt>, and and every other character is carried over.
|
||||
</dd>
|
||||
<dt id="105"><pre><span class="Keyword">proc</span> <span class="Identifier">readData</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">allowedMethods</span><span class="Other">:</span> <span class="Identifier">set</span><span class="Other">[</span><span class="Identifier">TRequestMethod</span><span class="Other">]</span> <span class="Other">=</span> <span class="Other">{</span><span class="Identifier">methodPost</span><span class="Other">,</span> <span class="Identifier">methodGet</span><span class="Other">}</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">PStringTable</span></pre></dt>
|
||||
<dd>
|
||||
Read CGI data. If the client does not use a method listed in the <cite>allowedMethods</cite> set, an <cite>ECgi</cite> exception is raised.
|
||||
</dd>
|
||||
<dt id="106"><pre><span class="Keyword">proc</span> <span class="Identifier">validateData</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">data</span><span class="Other">:</span> <span class="Identifier">PStringTable</span><span class="Other">,</span> <span class="Identifier">validKeys</span><span class="Other">:</span> <span class="Identifier">openarray</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span></pre></dt>
|
||||
<dd>
|
||||
validates data; raises <cite>ECgi</cite> if this fails. This checks that each variable name of the CGI <cite>data</cite> occurs in the <cite>validKeys</cite> array.
|
||||
</dd>
|
||||
<dt id="107"><pre><span class="Keyword">proc</span> <span class="Identifier">getContentLength</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">CONTENT_LENGTH</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="108"><pre><span class="Keyword">proc</span> <span class="Identifier">getContentType</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">CONTENT_TYPE</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="109"><pre><span class="Keyword">proc</span> <span class="Identifier">getDocumentRoot</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">DOCUMENT_ROOT</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="110"><pre><span class="Keyword">proc</span> <span class="Identifier">getGatewayInterface</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">GATEWAY_INTERFACE</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="111"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpAccept</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_ACCEPT</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="112"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpAcceptCharset</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_ACCEPT_CHARSET</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="113"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpAcceptEncoding</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_ACCEPT_ENCODING</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="114"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpAcceptLanguage</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_ACCEPT_LANGUAGE</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="115"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpConnection</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_CONNECTION</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="116"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpCookie</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_COOKIE</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="117"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpHost</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_HOST</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="118"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpReferer</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_REFERER</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="119"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpUserAgent</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_USER_AGENT</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="120"><pre><span class="Keyword">proc</span> <span class="Identifier">getPathInfo</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">PATH_INFO</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="121"><pre><span class="Keyword">proc</span> <span class="Identifier">getPathTranslated</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">PATH_TRANSLATED</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="122"><pre><span class="Keyword">proc</span> <span class="Identifier">getQueryString</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">QUERY_STRING</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="123"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemoteAddr</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_ADDR</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="124"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemoteHost</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_HOST</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="125"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemoteIdent</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_IDENT</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="126"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemotePort</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_PORT</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="127"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemoteUser</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_USER</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="128"><pre><span class="Keyword">proc</span> <span class="Identifier">getRequestMethod</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">REQUEST_METHOD</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="129"><pre><span class="Keyword">proc</span> <span class="Identifier">getRequestURI</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">REQUEST_URI</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="130"><pre><span class="Keyword">proc</span> <span class="Identifier">getScriptFilename</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">SCRIPT_FILENAME</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="131"><pre><span class="Keyword">proc</span> <span class="Identifier">getScriptName</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">SCRIPT_NAME</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="132"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerAddr</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_ADDR</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="133"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerAdmin</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_ADMIN</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="134"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerName</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_NAME</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="135"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerPort</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_PORT</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="136"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerProtocol</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_PROTOCOL</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="137"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerSignature</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_SIGNATURE</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="138"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerSoftware</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
||||
<dd>
|
||||
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_SOFTWARE</span></tt> environment variable
|
||||
</dd>
|
||||
<dt id="139"><pre><span class="Keyword">proc</span> <span class="Identifier">setTestData</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">keysvalues</span><span class="Other">:</span> <span class="Identifier">openarray</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span></pre></dt>
|
||||
<dd>
|
||||
fills the appropriate environment variables to test your CGI application. This can only simulate the 'GET' 'REQUEST_METHOD'. <cite>keysvalues</cite> should provide embedded (name, value)-pairs. Example:<pre><span class="Identifier">setTestData</span><span class="Punctation">(</span><span class="StringLit">"name"</span><span class="Punctation">,</span> <span class="StringLit">"Hanz"</span><span class="Punctation">,</span> <span class="StringLit">"password"</span><span class="Punctation">,</span> <span class="StringLit">"12345"</span><span class="Punctation">)</span></pre>
|
||||
</dd>
|
||||
<dt id="140"><pre><span class="Keyword">proc</span> <span class="Identifier">writeContentType</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span></pre></dt>
|
||||
<dd>
|
||||
call this before starting to send your HTML data to <cite>stdout</cite>. This is just a shorthand for: .. code-block:: Nimrod<blockquote>write(stdout, "Content-type: text/htmlnn")</blockquote>
|
||||
|
||||
</dd>
|
||||
|
||||
</dl></div>
|
||||
|
||||
</div>
|
||||
|
||||
<small>Generated: 2008-12-08 20:29:49 UTC</small>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2008 Andreas Rumpf
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -71,20 +71,40 @@ proc URLdecode*(s: string): string =
|
|||
else: add(result, s[i])
|
||||
inc(i)
|
||||
|
||||
proc addXmlChar(dest: var string, c: Char) {.inline.} =
|
||||
case c
|
||||
of '&': add(dest, "&")
|
||||
of '<': add(dest, "<")
|
||||
of '>': add(dest, ">")
|
||||
of '\"': add(dest, """)
|
||||
else: add(dest, c)
|
||||
|
||||
proc XMLencode*(s: string): string =
|
||||
## Encodes a value to be XML safe:
|
||||
## * ``"`` is replaced by ``"``
|
||||
## * ``<`` is replaced by ``<``
|
||||
## * ``>`` is replaced by ``>``
|
||||
## * ``&`` is replaced by ``&``
|
||||
## * every other character is carried over.
|
||||
result = ""
|
||||
for i in 0..len(s)-1: addXmlChar(result, s[i])
|
||||
|
||||
type
|
||||
ECgi* = object of EIO ## the exception that is raised, if a CGI error occurs
|
||||
TRequestMethod* = enum ## the used request method
|
||||
methodNone, ## no REQUEST_METHOD environment variable
|
||||
methodPost, ## query uses the POST method
|
||||
methodGet ## query uses the GET method
|
||||
|
||||
proc cgiError(msg: string) {.noreturn.} =
|
||||
proc cgiError*(msg: string) {.noreturn.} =
|
||||
## raises an ECgi exception with message `msg`.
|
||||
var e: ref ECgi
|
||||
new(e)
|
||||
e.msg = msg
|
||||
raise e
|
||||
|
||||
proc readData*(allowedMethods: set[TRequestMethod] =
|
||||
{methodPost, methodGet}): PStringTable =
|
||||
{methodNone, methodPost, methodGet}): PStringTable =
|
||||
## Read CGI data. If the client does not use a method listed in the
|
||||
## `allowedMethods` set, an `ECgi` exception is raised.
|
||||
result = newStringTable()
|
||||
|
|
@ -103,7 +123,11 @@ proc readData*(allowedMethods: set[TRequestMethod] =
|
|||
cgiError("'REQUEST_METHOD' 'GET' is not supported")
|
||||
# read from the QUERY_STRING environment variable:
|
||||
enc = getenv("QUERY_STRING")
|
||||
else: cgiError("'REQUEST_METHOD' must be 'POST' or 'GET'")
|
||||
else:
|
||||
if methodNone in allowedMethods:
|
||||
return result
|
||||
else:
|
||||
cgiError("'REQUEST_METHOD' must be 'POST' or 'GET'")
|
||||
|
||||
# decode everything in one pass:
|
||||
var i = 0
|
||||
|
|
@ -129,7 +153,6 @@ proc readData*(allowedMethods: set[TRequestMethod] =
|
|||
setLen(value, 0) # reuse memory
|
||||
while true:
|
||||
case enc[i]
|
||||
of '\0': return
|
||||
of '%':
|
||||
var x = 0
|
||||
handleHexChar(enc[i+1], x)
|
||||
|
|
@ -137,12 +160,13 @@ proc readData*(allowedMethods: set[TRequestMethod] =
|
|||
inc(i, 2)
|
||||
add(value, chr(x))
|
||||
of '+': add(value, ' ')
|
||||
of '=', '&': break
|
||||
of '&', '\0': break
|
||||
else: add(value, enc[i])
|
||||
inc(i)
|
||||
if enc[i] != '&': cgiError("'&' expected")
|
||||
inc(i) # skip '='
|
||||
result[name] = value
|
||||
if enc[i] == '&': inc(i)
|
||||
elif enc[i] == '\0': break
|
||||
else: cgiError("'&' expected")
|
||||
|
||||
proc validateData*(data: PStringTable, validKeys: openarray[string]) =
|
||||
## validates data; raises `ECgi` if this fails. This checks that each variable
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2008 Andreas Rumpf
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
|
|||
|
|
@ -17267,5 +17267,5 @@ proc gtk_file_chooser_set_do_overwrite_confirmation*(chooser: PGtkFileChooser,
|
|||
proc gtk_nimrod_init*() =
|
||||
var
|
||||
cmdLine {.importc: "cmdLine".}: array [0..255, cstring]
|
||||
cmdCount {.importc: "cmdCount".}: int
|
||||
cmdCount {.importc: "cmdCount".}: cint
|
||||
gtk_init(addr(cmdLine), addr(cmdCount))
|
||||
|
|
|
|||
|
|
@ -226,8 +226,8 @@ const
|
|||
SQL_DRIVER_NOPROMPT* = 0
|
||||
SQL_DRIVER_COMPLETE* = 1
|
||||
SQL_DRIVER_PROMPT* = 2
|
||||
SQL_DRIVER_COMPLETE_REQUIRED* = 3 # whether an attribute is a pointer or not
|
||||
SQL_IS_POINTER* = (- 4)
|
||||
SQL_DRIVER_COMPLETE_REQUIRED* = 3
|
||||
SQL_IS_POINTER* = (- 4) # whether an attribute is a pointer or not
|
||||
SQL_IS_UINTEGER* = (- 5)
|
||||
SQL_IS_INTEGER* = (- 6)
|
||||
SQL_IS_USMALLINT* = (- 7)
|
||||
|
|
@ -737,7 +737,8 @@ proc SQLFreeStmt*(StatementHandle: TSqlHStmt, Option: TSqlUSmallInt): TSqlSmallI
|
|||
proc SQLColAttribute*(StatementHandle: TSqlHStmt, ColumnNumber: TSqlUSmallInt,
|
||||
FieldIdentifier: TSqlUSmallInt,
|
||||
CharacterAttribute: PSQLCHAR, BufferLength: TSqlSmallInt,
|
||||
StringLength: PSQLSMALLINT, NumericAttribute: TSqlPointer): TSqlSmallInt{.
|
||||
StringLength: PSQLSMALLINT,
|
||||
NumericAttribute: TSqlPointer): TSqlSmallInt{.
|
||||
dynlib: odbclib, importc.}
|
||||
proc SQLEndTran*(HandleType: TSqlSmallInt, Handle: TSqlHandle,
|
||||
CompletionType: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib,
|
||||
|
|
@ -756,7 +757,8 @@ proc SQLSpecialColumns*(StatementHandle: TSqlHStmt, IdentifierType: TSqlUSmallIn
|
|||
CatalogName: PSQLCHAR, NameLength1: TSqlSmallInt,
|
||||
SchemaName: PSQLCHAR, NameLength2: TSqlSmallInt,
|
||||
TableName: PSQLCHAR, NameLength3: TSqlSmallInt,
|
||||
Scope: TSqlUSmallInt, Nullable: TSqlUSmallInt): TSqlSmallInt{.
|
||||
Scope: TSqlUSmallInt,
|
||||
Nullable: TSqlUSmallInt): TSqlSmallInt{.
|
||||
dynlib: odbclib, importc.}
|
||||
proc SQLProcedures*(hstmt: TSqlHStmt, szTableQualifier: PSQLCHAR,
|
||||
cbTableQualifier: TSqlSmallInt, szTableOwner: PSQLCHAR,
|
||||
|
|
@ -778,6 +780,7 @@ proc SQLStatistics*(hstmt: TSqlHStmt, CatalogName: PSQLCHAR,
|
|||
NameLength1: TSqlSmallInt, SchemaName: PSQLCHAR,
|
||||
NameLength2: TSqlSmallInt, TableName: PSQLCHAR,
|
||||
NameLength3: TSqlSmallInt, Unique: TSqlUSmallInt,
|
||||
Reserved: TSqlUSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.}
|
||||
Reserved: TSqlUSmallInt): TSqlSmallInt {.
|
||||
dynlib: odbclib, importc.}
|
||||
|
||||
{.pop.}
|
||||
{.pop.}
|
||||
|
|
|
|||
|
|
@ -1397,34 +1397,34 @@ proc glVertexPointer*(size: TGLint, atype: TGLenum, stride: TGLsizei,
|
|||
pointer: Pointer){.dynlib: dllname, importc.}
|
||||
proc glViewport*(x, y: TGLint, width, height: TGLsizei){.dynlib: dllname, importc.}
|
||||
type
|
||||
PFNGLARRAYELEMENTEXTPROC* = proc (i: TGLint)
|
||||
PFNGLDRAWARRAYSEXTPROC* = proc (mode: TGLenum, first: TGLint, count: TGLsizei)
|
||||
PFNGLVERTEXPOINTEREXTPROC* = proc (size: TGLint, atype: TGLenum,
|
||||
PFN_GLARRAY_ELEMENT_EXTPROC* = proc (i: TGLint)
|
||||
PFN_GLDRAW_ARRAYS_EXTPROC* = proc (mode: TGLenum, first: TGLint, count: TGLsizei)
|
||||
PFN_GLVERTEX_POINTER_EXTPROC* = proc (size: TGLint, atype: TGLenum,
|
||||
stride, count: TGLsizei, pointer: Pointer)
|
||||
PFNGLNORMALPOINTEREXTPROC* = proc (atype: TGLenum, stride, count: TGLsizei,
|
||||
PFN_GLNORMAL_POINTER_EXTPROC* = proc (atype: TGLenum, stride, count: TGLsizei,
|
||||
pointer: Pointer)
|
||||
PFNGLCOLORPOINTEREXTPROC* = proc (size: TGLint, atype: TGLenum,
|
||||
PFN_GLCOLOR_POINTER_EXTPROC* = proc (size: TGLint, atype: TGLenum,
|
||||
stride, count: TGLsizei, pointer: Pointer)
|
||||
PFNGLINDEXPOINTEREXTPROC* = proc (atype: TGLenum, stride, count: TGLsizei,
|
||||
PFN_GLINDEX_POINTER_EXTPROC* = proc (atype: TGLenum, stride, count: TGLsizei,
|
||||
pointer: Pointer)
|
||||
PFNGLTEXCOORDPOINTEREXTPROC* = proc (size: TGLint, atype: TGLenum,
|
||||
PFN_GLTEXCOORD_POINTER_EXTPROC* = proc (size: TGLint, atype: TGLenum,
|
||||
stride, count: TGLsizei, pointer: Pointer)
|
||||
PFNGLEDGEFLAGPOINTEREXTPROC* = proc (stride, count: TGLsizei,
|
||||
PFN_GLEDGEFLAG_POINTER_EXTPROC* = proc (stride, count: TGLsizei,
|
||||
pointer: PGLboolean)
|
||||
PFNGLGETPOINTERVEXTPROC* = proc (pname: TGLenum, params: Pointer)
|
||||
PFNGLARRAYELEMENTARRAYEXTPROC* = proc (mode: TGLenum, count: TGLsizei,
|
||||
PFN_GLGET_POINTER_VEXT_PROC* = proc (pname: TGLenum, params: Pointer)
|
||||
PFN_GLARRAY_ELEMENT_ARRAY_EXTPROC* = proc (mode: TGLenum, count: TGLsizei,
|
||||
pi: Pointer) # WIN_swap_hint
|
||||
PFNGLADDSWAPHINTRECTWINPROC* = proc (x, y: TGLint, width, height: TGLsizei)
|
||||
PFNGLCOLORTABLEEXTPROC* = proc (target, internalFormat: TGLenum,
|
||||
PFN_GLADDSWAPHINT_RECT_WINPROC* = proc (x, y: TGLint, width, height: TGLsizei)
|
||||
PFN_GLCOLOR_TABLE_EXTPROC* = proc (target, internalFormat: TGLenum,
|
||||
width: TGLsizei, format, atype: TGLenum,
|
||||
data: Pointer)
|
||||
PFNGLCOLORSUBTABLEEXTPROC* = proc (target: TGLenum, start, count: TGLsizei,
|
||||
PFN_GLCOLOR_SUBTABLE_EXTPROC* = proc (target: TGLenum, start, count: TGLsizei,
|
||||
format, atype: TGLenum, data: Pointer)
|
||||
PFNGLGETCOLORTABLEEXTPROC* = proc (target, format, atype: TGLenum,
|
||||
PFN_GLGETCOLOR_TABLE_EXTPROC* = proc (target, format, atype: TGLenum,
|
||||
data: Pointer)
|
||||
PFNGLGETCOLORTABLEPARAMETERIVEXTPROC* = proc (target, pname: TGLenum,
|
||||
PFN_GLGETCOLOR_TABLE_PARAMETER_IVEXTPROC* = proc (target, pname: TGLenum,
|
||||
params: PGLint)
|
||||
PFNGLGETCOLORTABLEPARAMETERFVEXTPROC* = proc (target, pname: TGLenum,
|
||||
PFN_GLGETCOLOR_TABLE_PARAMETER_FVEXTPROC* = proc (target, pname: TGLenum,
|
||||
params: PGLfloat)
|
||||
|
||||
{.pop.}
|
||||
|
|
|
|||
|
|
@ -7,18 +7,6 @@
|
|||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
# This file was created by a complicated procedure which saved me a considerable
|
||||
# amount of time: the pcre.h header was converted to modpcre.h by hand, so that
|
||||
# h2pas could handle it. Then I used pas2mor to generate a Nimrod binding.
|
||||
# Unfortunately, I had to fix some things later on; thus don't do all this
|
||||
# again! My manual changes will be lost!
|
||||
|
||||
# Converted by Pas2mor v1.37
|
||||
#
|
||||
# Automatically converted by H2Pas 0.99.16 from modpcre.h
|
||||
# The following command line parameters were used:
|
||||
# -D -c -l pcre.lib -T modpcre.h
|
||||
|
||||
{.compile: "pcre_all.c" .}
|
||||
|
||||
type
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2006 Andreas Rumpf
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -35,7 +35,7 @@ const
|
|||
|
||||
proc match*(s, pattern: string, matches: var openarray[string],
|
||||
start: int = 0): bool
|
||||
## returns ``true`` if ``s`` matches the ``pattern[start..]`` and
|
||||
## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
|
||||
## the captured substrings in the array ``matches``. If it does not
|
||||
## match, nothing is written into ``matches`` and ``false`` is
|
||||
## returned.
|
||||
|
|
@ -113,3 +113,42 @@ proc match(s, pattern: string, start: int = 0): bool =
|
|||
|
||||
proc find(s, pattern: string, start: int = 0): bool =
|
||||
return matchOrFind(s, rawCompile(pattern, PCRE_MULTILINE), start) >= 0'i32
|
||||
|
||||
template `=~` *(s, pattern: expr): expr =
|
||||
## This calls ``match`` with an implicit declared ``matches`` array that
|
||||
## can be used in the scope of the ``=~`` call:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
##
|
||||
## if line =~ r"\s*(\w+)\s*\=\s*(\w+)":
|
||||
## # matches a key=value pair:
|
||||
## echo("Key: ", matches[1])
|
||||
## echo("Value: ", matches[2])
|
||||
## elif line =~ r"\s*(\#.*)":
|
||||
## # matches a comment
|
||||
## # note that the implicit ``matches`` array is different from the
|
||||
## # ``matches`` array of the first branch
|
||||
## echo("comment: ", matches[1])
|
||||
## else:
|
||||
## echo("syntax error")
|
||||
##
|
||||
var matches: array[0..maxSubPatterns-1, string]
|
||||
match(s, pattern, matches)
|
||||
|
||||
|
||||
const ## common regular expressions
|
||||
reIdentifier* = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b" ## describes an identifier
|
||||
reNatural* = r"\b\d+\b" ## describes a natural number
|
||||
reInteger* = r"\b[-+]?\d+\b" ## describes an integer
|
||||
reHex* = r"\b0[xX][0-9a-fA-F]+\b" ## describes a hexadecimal number
|
||||
reBinary* = r"\b0[bB][01]+\b" ## describes a binary number (example: 0b11101)
|
||||
reOctal* = r"\b0[oO][0-7]+\b" ## describes an octal number (example: 0o777)
|
||||
reFloat* = r"\b[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\b"
|
||||
## describes a floating point number
|
||||
reEmail* = r"\b[a-zA-Z0-9!#$%&'*+/=?^_`{|}~\-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)" &
|
||||
r"*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+(?:[a-zA-Z]{2}|com|org|" &
|
||||
r"net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\b"
|
||||
## describes a common email address
|
||||
reURL* = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms\-help):" &
|
||||
r"((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b"
|
||||
## describes an URL
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
=======================================================
|
||||
The Nimrod Runtime Library
|
||||
Copyright (C) 2004-2007 Andreas Rumpf
|
||||
Copyright (C) 2004-2009 Andreas Rumpf
|
||||
=======================================================
|
||||
|
||||
This is the file copying.txt, it applies to the Nimrod Run-Time Library
|
||||
|
|
|
|||
|
|
@ -348,9 +348,9 @@ proc dbgStackFrame(s: string, start: int, currFrame: PExtendedFrame) =
|
|||
proc CommandPrompt() =
|
||||
# if we return from this routine, user code executes again
|
||||
var
|
||||
again: bool = True
|
||||
again = True
|
||||
dbgFramePtr = framePtr # for going down and up the stack
|
||||
dbgDown: int = 0 # how often we did go down
|
||||
dbgDown = 0 # how often we did go down
|
||||
|
||||
while again:
|
||||
write(stdout, "*** endb| >>")
|
||||
|
|
@ -478,10 +478,10 @@ proc dbgRegisterGlobal(name: cstring, address: pointer,
|
|||
inc(dbgGlobalData.f.len)
|
||||
|
||||
proc endb(line: int) {.compilerproc.} =
|
||||
# This proc is called before any Nimrod code line!
|
||||
# This proc is called before every Nimrod code line!
|
||||
# Thus, it must have as few parameters as possible to keep the
|
||||
# code size small!
|
||||
# check if we are at an enabled breakpoint or "in the mood"
|
||||
# Check if we are at an enabled breakpoint or "in the mood"
|
||||
framePtr.line = line # this is done here for smaller code size!
|
||||
if dbgLineHook != nil: dbgLineHook()
|
||||
case dbgState
|
||||
|
|
|
|||
|
|
@ -58,7 +58,10 @@ when defined(posix):
|
|||
proc nimLoadLibrary(path: string): TLibHandle =
|
||||
result = dlopen(path, RTLD_NOW)
|
||||
if result == nil:
|
||||
raise newException(EInvalidLibrary, "could not load: " & path)
|
||||
writeToStdErr("could not load: ")
|
||||
writeToStdErr(path)
|
||||
writeToStdErr("\n")
|
||||
#raise newException(EInvalidLibrary, "could not load: " & path)
|
||||
|
||||
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr =
|
||||
result = dlsym(lib, name)
|
||||
|
|
@ -84,7 +87,10 @@ elif defined(windows) or defined(dos):
|
|||
proc nimLoadLibrary(path: string): TLibHandle =
|
||||
result = cast[TLibHandle](winLoadLibrary(path))
|
||||
if result == nil:
|
||||
raise newException(EInvalidLibrary, "could not load: " & path)
|
||||
writeToStdErr("could not load: ")
|
||||
writeToStdErr(path)
|
||||
writeToStdErr("\n")
|
||||
#raise newException(EInvalidLibrary, "could not load: " & path)
|
||||
|
||||
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr =
|
||||
result = GetProcAddress(cast[THINSTANCE](lib), name)
|
||||
|
|
@ -118,7 +124,10 @@ elif defined(mac):
|
|||
NSDestroyObjectFileImage(img)
|
||||
result = TLibHandle(modul)
|
||||
if result == nil:
|
||||
raise newException(EInvalidLibrary, "could not load: " & path)
|
||||
writeToStdErr("could not load: ")
|
||||
writeToStdErr(path)
|
||||
writeToStdErr("\n")
|
||||
#raise newException(EInvalidLibrary, "could not load: " & path)
|
||||
|
||||
proc nimGetProcAddr(lib: TLibHandle, cname: string): TProcAddr =
|
||||
var
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2008 Andreas Rumpf
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
|
|||
286
lib/gc.nim
286
lib/gc.nim
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2008 Andreas Rumpf
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -15,36 +15,9 @@
|
|||
# * generational
|
||||
|
||||
# Future Improvements:
|
||||
# * Both dlmalloc and TLSF lack zero-overhead object allocation. Thus, for
|
||||
# small objects we should use our own allocator.
|
||||
# * Support for multi-threading. However, locks for the reference counting
|
||||
# might turn out to be too slow.
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Interface to TLSF:
|
||||
const
|
||||
useTLSF = false # benchmarking showed that *dlmalloc* is faster than *TLSF*
|
||||
|
||||
when useTLSF:
|
||||
{.compile: "tlsf.c".}
|
||||
|
||||
proc tlsfUsed: int {.importc: "TLSF_GET_USED_SIZE", noconv.}
|
||||
proc tlsfMax: int {.importc: "TLSF_GET_MAX_SIZE", noconv.}
|
||||
|
||||
proc tlsf_malloc(size: int): pointer {.importc, noconv.}
|
||||
proc tlsf_free(p: pointer) {.importc, noconv.}
|
||||
proc tlsf_realloc(p: pointer, size: int): pointer {.importc, noconv.}
|
||||
else:
|
||||
# use DL malloc
|
||||
{.compile: "dlmalloc.c".}
|
||||
proc tlsfUsed: int {.importc: "dlmalloc_footprint", noconv.}
|
||||
proc tlsfMax: int {.importc: "dlmalloc_max_footprint", noconv.}
|
||||
|
||||
proc tlsf_malloc(size: int): pointer {.importc: "dlmalloc", noconv.}
|
||||
proc tlsf_free(p: pointer) {.importc: "dlfree", noconv.}
|
||||
proc tlsf_realloc(p: pointer, size: int): pointer {.
|
||||
importc: "dlrealloc", noconv.}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
proc getOccupiedMem(): int = return tlsfUsed()
|
||||
|
|
@ -54,38 +27,13 @@ proc getTotalMem(): int = return tlsfMax()
|
|||
# ---------------------------------------------------------------------------
|
||||
|
||||
const
|
||||
debugGC = false # we wish to debug the GC...
|
||||
logGC = false
|
||||
traceGC = false # extensive debugging
|
||||
reallyDealloc = true # for debugging purposes this can be set to false
|
||||
cycleGC = true # (de)activate the cycle GC
|
||||
stressGC = debugGC
|
||||
|
||||
# Guess the page size of the system; if it is the
|
||||
# wrong value, performance may be worse (this is not
|
||||
# for sure though), but GC still works; must be a power of two!
|
||||
const
|
||||
PageShift = if sizeof(pointer) == 4: 12 else: 13
|
||||
PageSize = 1 shl PageShift # on 32 bit systems 4096
|
||||
CycleIncrease = 2 # is a multiplicative increase
|
||||
|
||||
InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow
|
||||
ZctThreshold = 256 # we collect garbage if the ZCT's size
|
||||
# reaches this threshold
|
||||
# this seems to be a good value
|
||||
|
||||
const
|
||||
MemAlignment = 8 # BUGFIX: on AMD64, dlmalloc aligns at 8 byte boundary
|
||||
BitsPerUnit = sizeof(int)*8
|
||||
# a "unit" is a word, i.e. 4 bytes
|
||||
# on a 32 bit system; I do not use the term "word" because under 32-bit
|
||||
# Windows it is sometimes only 16 bits
|
||||
|
||||
BitsPerPage = PageSize div MemAlignment
|
||||
UnitsPerPage = BitsPerPage div BitsPerUnit
|
||||
# how many units do we need to describe a page:
|
||||
# on 32 bit systems this is only 16 (!)
|
||||
|
||||
rcIncrement = 0b1000 # so that lowest 3 bits are not touched
|
||||
# NOTE: Most colors are currently unused
|
||||
rcBlack = 0b000 # cell is colored black; in use or free
|
||||
|
|
@ -101,40 +49,9 @@ type
|
|||
TWalkOp = enum
|
||||
waZctDecRef, waPush, waCycleDecRef
|
||||
|
||||
TCell {.pure.} = object
|
||||
refcount: int # the refcount and some flags
|
||||
typ: PNimType
|
||||
when debugGC:
|
||||
filename: cstring
|
||||
line: int
|
||||
|
||||
PCell = ptr TCell
|
||||
TFinalizer {.compilerproc.} = proc (self: pointer)
|
||||
# A ref type can have a finalizer that is called before the object's
|
||||
# storage is freed.
|
||||
PPointer = ptr pointer
|
||||
TByteArray = array[0..1000_0000, byte]
|
||||
PByte = ptr TByteArray
|
||||
PString = ptr string
|
||||
|
||||
PPageDesc = ptr TPageDesc
|
||||
TBitIndex = range[0..UnitsPerPage-1]
|
||||
TPageDesc {.final, pure.} = object
|
||||
next: PPageDesc # all nodes are connected with this pointer
|
||||
key: TAddress # start address at bit 0
|
||||
bits: array[TBitIndex, int] # a bit vector
|
||||
|
||||
PPageDescArray = ptr array[0..1000_000, PPageDesc]
|
||||
TCellSet {.final, pure.} = object
|
||||
counter, max: int
|
||||
head: PPageDesc
|
||||
data: PPageDescArray
|
||||
|
||||
PCellArray = ptr array[0..100_000_000, PCell]
|
||||
TCellSeq {.final, pure.} = object
|
||||
len, cap: int
|
||||
d: PCellArray
|
||||
|
||||
TGcHeap {.final, pure.} = object # this contains the zero count and
|
||||
# non-zero count table
|
||||
mask: TAddress # mask for fast pointer detection
|
||||
|
|
@ -152,7 +69,6 @@ type
|
|||
tempStack: TCellSeq # temporary stack for recursion elimination
|
||||
|
||||
var
|
||||
gOutOfMem: ref EOutOfMemory
|
||||
stackBottom: pointer
|
||||
gch: TGcHeap
|
||||
cycleThreshold: int = InitialCycleThreshold
|
||||
|
|
@ -171,12 +87,10 @@ proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerproc.}
|
|||
proc newObj(typ: PNimType, size: int): pointer {.compilerproc.}
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.}
|
||||
|
||||
proc raiseOutOfMem() {.noreturn.} =
|
||||
if gOutOfMem == nil:
|
||||
writeToStdErr("out of memory; cannot even throw an exception")
|
||||
quit(1)
|
||||
gOutOfMem.msg = "out of memory"
|
||||
raise gOutOfMem
|
||||
proc addZCT(s: var TCellSeq, c: PCell) {.noinline.} =
|
||||
if (c.refcount and colorMask) != rcZct:
|
||||
c.refcount = c.refcount and not colorMask or rcZct
|
||||
add(s, c)
|
||||
|
||||
proc cellToUsr(cell: PCell): pointer {.inline.} =
|
||||
# convert object (=pointer to refcount) to pointer to userdata
|
||||
|
|
@ -198,11 +112,6 @@ proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
|
|||
if result > 0: result = result shr rcShift
|
||||
else: result = 0
|
||||
|
||||
proc gcAlloc(size: int): pointer =
|
||||
result = tlsf_malloc(size)
|
||||
if result == nil: raiseOutOfMem()
|
||||
zeroMem(result, size)
|
||||
|
||||
proc GC_disable() = inc(recGcLock)
|
||||
proc GC_enable() =
|
||||
if recGcLock > 0: dec(recGcLock)
|
||||
|
|
@ -221,160 +130,10 @@ proc GC_disableMarkAndSweep() =
|
|||
cycleThreshold = high(cycleThreshold)-1
|
||||
# set to the max value to suppress the cycle detector
|
||||
|
||||
proc nextTry(h, maxHash: int): int {.inline.} =
|
||||
result = ((5*h) + 1) and maxHash
|
||||
# For any initial h in range(maxHash), repeating that maxHash times
|
||||
# generates each int in range(maxHash) exactly once (see any text on
|
||||
# random-number generation for proof).
|
||||
|
||||
# this that has to equals zero, otherwise we have to round up UnitsPerPage:
|
||||
when BitsPerPage mod BitsPerUnit != 0:
|
||||
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
|
||||
|
||||
# ------------------- cell set handling ---------------------------------------
|
||||
|
||||
proc inOperator(s: TCellSeq, c: PCell): bool {.inline.} =
|
||||
for i in 0 .. s.len-1:
|
||||
if s.d[i] == c: return True
|
||||
return False
|
||||
|
||||
proc add(s: var TCellSeq, c: PCell) {.inline.} =
|
||||
if s.len >= s.cap:
|
||||
s.cap = s.cap * 3 div 2
|
||||
var d = cast[PCellArray](tlsf_malloc(s.cap * sizeof(PCell)))
|
||||
if d == nil: raiseOutOfMem()
|
||||
copyMem(d, s.d, s.len * sizeof(PCell))
|
||||
tlsf_free(s.d)
|
||||
s.d = d
|
||||
# BUGFIX: realloc failes on AMD64, sigh...
|
||||
#s.d = cast[PCellArray](tlsf_realloc(s.d, s.cap * sizeof(PCell)))
|
||||
#if s.d == nil: raiseOutOfMem()
|
||||
s.d[s.len] = c
|
||||
inc(s.len)
|
||||
|
||||
proc addZCT(s: var TCellSeq, c: PCell) =
|
||||
if (c.refcount and colorMask) != rcZct:
|
||||
c.refcount = c.refcount and not colorMask or rcZct
|
||||
add(s, c)
|
||||
|
||||
proc init(s: var TCellSeq, cap: int = 1024) =
|
||||
s.len = 0
|
||||
s.cap = cap
|
||||
s.d = cast[PCellArray](gcAlloc(cap * sizeof(PCell)))
|
||||
|
||||
const
|
||||
InitCellSetSize = 1024 # must be a power of two!
|
||||
|
||||
proc CellSetInit(s: var TCellSet) =
|
||||
s.data = cast[PPageDescArray](gcAlloc(InitCellSetSize * sizeof(PPageDesc)))
|
||||
s.max = InitCellSetSize-1
|
||||
s.counter = 0
|
||||
s.head = nil
|
||||
|
||||
proc CellSetDeinit(s: var TCellSet) =
|
||||
var it = s.head
|
||||
while it != nil:
|
||||
var n = it.next
|
||||
tlsf_free(it)
|
||||
it = n
|
||||
s.head = nil # play it safe here
|
||||
tlsf_free(s.data)
|
||||
s.data = nil
|
||||
s.counter = 0
|
||||
|
||||
proc CellSetGet(t: TCellSet, key: TAddress): PPageDesc =
|
||||
var h = cast[int](key) and t.max
|
||||
while t.data[h] != nil:
|
||||
if t.data[h].key == key: return t.data[h]
|
||||
h = nextTry(h, t.max)
|
||||
return nil
|
||||
|
||||
proc CellSetRawInsert(t: TCellSet, data: PPageDescArray,
|
||||
desc: PPageDesc) =
|
||||
var h = cast[int](desc.key) and t.max
|
||||
while data[h] != nil:
|
||||
assert(data[h] != desc)
|
||||
h = nextTry(h, t.max)
|
||||
assert(data[h] == nil)
|
||||
data[h] = desc
|
||||
|
||||
proc CellSetEnlarge(t: var TCellSet) =
|
||||
var oldMax = t.max
|
||||
t.max = ((t.max+1)*2)-1
|
||||
var n = cast[PPageDescArray](gcAlloc((t.max + 1) * sizeof(PPageDesc)))
|
||||
for i in 0 .. oldmax:
|
||||
if t.data[i] != nil:
|
||||
CellSetRawInsert(t, n, t.data[i])
|
||||
tlsf_free(t.data)
|
||||
t.data = n
|
||||
|
||||
proc CellSetPut(t: var TCellSet, key: TAddress): PPageDesc =
|
||||
var h = cast[int](key) and t.max
|
||||
while true:
|
||||
var x = t.data[h]
|
||||
if x == nil: break
|
||||
if x.key == key: return x
|
||||
h = nextTry(h, t.max)
|
||||
|
||||
if ((t.max+1)*2 < t.counter*3) or ((t.max+1)-t.counter < 4):
|
||||
CellSetEnlarge(t)
|
||||
inc(t.counter)
|
||||
h = cast[int](key) and t.max
|
||||
while t.data[h] != nil: h = nextTry(h, t.max)
|
||||
assert(t.data[h] == nil)
|
||||
# the new page descriptor goes into result
|
||||
result = cast[PPageDesc](gcAlloc(sizeof(TPageDesc)))
|
||||
result.next = t.head
|
||||
result.key = key
|
||||
t.head = result
|
||||
t.data[h] = result
|
||||
|
||||
# ---------- slightly higher level procs --------------------------------------
|
||||
|
||||
proc contains(s: TCellSet, cell: PCell): bool =
|
||||
var u = cast[TAddress](cell)
|
||||
var t = CellSetGet(s, u shr PageShift)
|
||||
if t != nil:
|
||||
u = (u %% PageSize) /% MemAlignment
|
||||
result = (t.bits[u /% BitsPerUnit] and (1 shl (u %% BitsPerUnit))) != 0
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc incl(s: var TCellSet, cell: PCell) =
|
||||
var u = cast[TAddress](cell)
|
||||
var t = CellSetPut(s, u shr PageShift)
|
||||
u = (u %% PageSize) /% MemAlignment
|
||||
t.bits[u /% BitsPerUnit] = t.bits[u /% BitsPerUnit] or
|
||||
(1 shl (u %% BitsPerUnit))
|
||||
|
||||
proc excl(s: var TCellSet, cell: PCell) =
|
||||
var u = cast[TAddress](cell)
|
||||
var t = CellSetGet(s, u shr PageShift)
|
||||
if t != nil:
|
||||
u = (u %% PageSize) /% MemAlignment
|
||||
t.bits[u /% BitsPerUnit] = (t.bits[u /% BitsPerUnit] and
|
||||
not (1 shl (u %% BitsPerUnit)))
|
||||
|
||||
iterator elements(t: TCellSet): PCell {.inline.} =
|
||||
# while traversing it is forbidden to add pointers to the tree!
|
||||
var r = t.head
|
||||
while r != nil:
|
||||
var i = 0
|
||||
while i <= high(r.bits):
|
||||
var w = r.bits[i] # taking a copy of r.bits[i] here is correct, because
|
||||
# modifying operations are not allowed during traversation
|
||||
var j = 0
|
||||
while w != 0: # test all remaining bits for zero
|
||||
if (w and 1) != 0: # the bit is set!
|
||||
yield cast[PCell]((r.key shl PageShift) or # +%
|
||||
(i*%BitsPerUnit+%j) *% MemAlignment)
|
||||
inc(j)
|
||||
w = w shr 1
|
||||
inc(i)
|
||||
r = r.next
|
||||
|
||||
# --------------- end of Cellset routines -------------------------------------
|
||||
|
||||
when debugGC:
|
||||
proc writeCell(msg: CString, c: PCell) =
|
||||
var kind = -1
|
||||
|
|
@ -450,7 +209,6 @@ proc IsOnStack(p: pointer): bool {.noinline.}
|
|||
proc forAllChildren(cell: PCell, op: TWalkOp)
|
||||
proc doOperation(p: pointer, op: TWalkOp)
|
||||
proc forAllChildrenAux(dest: Pointer, mt: PNimType, op: TWalkOp)
|
||||
proc reprAny(p: pointer, typ: PNimType): string {.compilerproc.}
|
||||
# we need the prototype here for debugging purposes
|
||||
|
||||
proc prepareDealloc(cell: PCell) =
|
||||
|
|
@ -479,13 +237,13 @@ proc decRef(c: PCell) {.inline.} =
|
|||
if c.refcount <% rcIncrement:
|
||||
addZCT(gch.zct, c)
|
||||
elif canBeCycleRoot(c):
|
||||
possibleRoot(gch, c)
|
||||
incl(gch.cycleRoots, c)
|
||||
|
||||
proc incRef(c: PCell) {.inline.} =
|
||||
c.refcount = c.refcount +% rcIncrement
|
||||
if canBeCycleRoot(c):
|
||||
# OPT: the code generator should special case this
|
||||
possibleRoot(gch, c)
|
||||
incl(gch.cycleRoots, c)
|
||||
|
||||
proc nimGCref(p: pointer) {.compilerproc, inline.} = incRef(usrToCell(p))
|
||||
proc nimGCunref(p: pointer) {.compilerproc, inline.} = decRef(usrToCell(p))
|
||||
|
|
@ -534,26 +292,6 @@ proc initGC() =
|
|||
gch.mask = 0
|
||||
new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
|
||||
|
||||
proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int =
|
||||
assert(n.kind == nkCase)
|
||||
var d: int
|
||||
var a = cast[TAddress](aa)
|
||||
case n.typ.size
|
||||
of 1: d = ze(cast[ptr int8](a +% n.offset)^)
|
||||
of 2: d = ze(cast[ptr int16](a +% n.offset)^)
|
||||
of 4: d = int(cast[ptr int32](a +% n.offset)^)
|
||||
else: assert(false)
|
||||
return d
|
||||
|
||||
proc selectBranch(aa: Pointer, n: ptr TNimNode): ptr TNimNode =
|
||||
var discr = getDiscriminant(aa, n)
|
||||
if discr <% n.len:
|
||||
result = n.sons[discr]
|
||||
if result == nil: result = n.sons[n.len]
|
||||
# n.sons[n.len] contains the ``else`` part (but may be nil)
|
||||
else:
|
||||
result = n.sons[n.len]
|
||||
|
||||
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
|
||||
var d = cast[TAddress](dest)
|
||||
case n.kind
|
||||
|
|
@ -749,7 +487,7 @@ proc collectCycles(gch: var TGcHeap) =
|
|||
CellSetDeinit(gch.cycleRoots)
|
||||
gch.cycleRoots = newRoots
|
||||
|
||||
proc gcMark(p: pointer) = # {.fastcall.} =
|
||||
proc gcMark(p: pointer) {.noinline.} =
|
||||
# the addresses are not as objects on the stack, so turn them to objects:
|
||||
var cell = usrToCell(p)
|
||||
var c = cast[TAddress](cell)
|
||||
|
|
@ -759,7 +497,7 @@ proc gcMark(p: pointer) = # {.fastcall.} =
|
|||
incl(gch.stackCells, cell) # yes: mark it
|
||||
|
||||
# ----------------- stack management --------------------------------------
|
||||
# inspired from Smart Eiffel (c)
|
||||
# inspired from Smart Eiffel
|
||||
|
||||
proc stackSize(): int {.noinline.} =
|
||||
var stackTop: array[0..1, pointer]
|
||||
|
|
@ -885,7 +623,7 @@ else:
|
|||
# in a platform independant way
|
||||
|
||||
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} =
|
||||
when true:
|
||||
when false:
|
||||
# new version: several C compilers are too smart here
|
||||
var
|
||||
max = cast[TAddress](stackBottom)
|
||||
|
|
@ -910,7 +648,7 @@ else:
|
|||
max = stackBottom
|
||||
registers: C_JmpBuf # The jmp_buf buffer is in the C stack.
|
||||
sp: PPointer # Used to traverse the stack and registers assuming
|
||||
# that `setjmp' will save registers in the C stack.
|
||||
# that 'setjmp' will save registers in the C stack.
|
||||
if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
|
||||
sp = cast[ppointer](addr(registers))
|
||||
while sp <= max:
|
||||
|
|
@ -937,7 +675,7 @@ proc updateZCT() =
|
|||
dec(L)
|
||||
d[j] = d[L]
|
||||
c.refcount = c.refcount and not colorMask
|
||||
# we have a new cell at position i, so don't increment i
|
||||
# we have a new cell at position j, so don't increment j
|
||||
else:
|
||||
inc(j)
|
||||
gch.zct.len = L
|
||||
|
|
|
|||
|
|
@ -58,12 +58,12 @@ type
|
|||
nnkTypeDef, nnkYieldStmt, nnkTryStmt, nnkFinally,
|
||||
nnkRaiseStmt, nnkReturnStmt, nnkBreakStmt, nnkContinueStmt,
|
||||
nnkBlockStmt, nnkDiscardStmt, nnkStmtList, nnkImportStmt,
|
||||
nnkFromStmt, nnkImportAs, nnkIncludeStmt, nnkAccessStmt,
|
||||
nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, nnkStmtListType,
|
||||
nnkBlockType, nnkVm, nnkTypeOfExpr, nnkObjectTy,
|
||||
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,
|
||||
nnkRefTy, nnkPtrTy, nnkVarTy, nnkProcTy,
|
||||
nnkEnumTy, nnkEnumFieldDef, nnkReturnToken
|
||||
nnkFromStmt, nnkImportAs, nnkIncludeStmt, nnkCommentStmt,
|
||||
nnkStmtListExpr, nnkBlockExpr, nnkStmtListType, nnkBlockType,
|
||||
nnkVm, nnkTypeOfExpr, nnkObjectTy, nnkTupleTy,
|
||||
nnkRecList, nnkRecCase, nnkRecWhen, nnkRefTy,
|
||||
nnkPtrTy, nnkVarTy, nnkProcTy, nnkEnumTy,
|
||||
nnkEnumFieldDef, nnkReturnToken
|
||||
TNimNodeKinds* = set[TNimrodNodeKind]
|
||||
TNimrodTypeKind* = enum
|
||||
ntyNone, ntyBool, ntyChar, ntyEmpty,
|
||||
|
|
@ -86,12 +86,24 @@ type
|
|||
#[[[end]]]
|
||||
|
||||
type
|
||||
TNimrodIdent = object of TObject
|
||||
## represents a Nimrod identifier in the AST
|
||||
|
||||
TNimrodNode {.final.} = object # hidden
|
||||
TNimrodSymbol {.final.} = object # hidden
|
||||
TNimrodType {.final.} = object # hidden
|
||||
|
||||
PNimrodType* {.compilerproc.} = ref TNimrodType
|
||||
## represents a Nimrod type in the compiler; currently this is not very
|
||||
## useful as there is no API to deal with Nimrod types.
|
||||
|
||||
PNimrodSymbol* {.compilerproc.} = ref TNimrodSymbol
|
||||
## represents a Nimrod *symbol* in the compiler; a *symbol* is a looked-up
|
||||
## *ident*.
|
||||
|
||||
PNimrodNode* {.compilerproc.} = ref TNimrodNode
|
||||
## represents a Nimrod AST node. Macros operate on this type.
|
||||
|
||||
expr* = PNimrodNode
|
||||
stmt* = PNimrodNode
|
||||
|
||||
|
|
@ -100,23 +112,35 @@ type
|
|||
# its father. How to do this without back references?
|
||||
|
||||
proc `[]`* (n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild".}
|
||||
## get `n`'s `i`'th child.
|
||||
|
||||
proc `[]=`* (n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild".}
|
||||
## provide access to `n`'s children
|
||||
## set `n`'s `i`'th child to `child`.
|
||||
|
||||
type
|
||||
TNimrodIdent = object of TObject
|
||||
proc `!` *(s: string): TNimrodIdent {.magic: "StrToIdent".}
|
||||
## constructs an identifier from the string `s`
|
||||
|
||||
converter StrToIdent*(s: string): TNimrodIdent {.magic: "StrToIdent".}
|
||||
proc `$`*(i: TNimrodIdent): string {.magic: "IdentToStr".}
|
||||
## converts a Nimrod identifier to a string
|
||||
|
||||
proc `==`* (a, b: TNimrodIdent): bool {.magic: "EqIdent".}
|
||||
## compares two Nimrod identifiers
|
||||
|
||||
proc len*(n: PNimrodNode): int {.magic: "NLen".}
|
||||
## returns the number of children of `n`.
|
||||
|
||||
## returns the number of children that a node has
|
||||
proc add*(father, child: PNimrodNode) {.magic: "NAdd".}
|
||||
proc add*(father: PNimrodNode, child: openArray[PNimrodNode]) {.magic: "NAddMultiple".}
|
||||
## adds the `child` to the `father` node
|
||||
|
||||
proc add*(father: PNimrodNode, children: openArray[PNimrodNode]) {.
|
||||
magic: "NAddMultiple".}
|
||||
## adds each `children` to the `father` node
|
||||
|
||||
proc del*(father: PNimrodNode, idx = 0, n = 1) {.magic: "NDel".}
|
||||
## deletes `n` children of `father` starting at index `idx`.
|
||||
|
||||
proc kind*(n: PNimrodNode): TNimrodNodeKind {.magic: "NKind".}
|
||||
## returns the `kind` of the node `n`.
|
||||
|
||||
proc intVal*(n: PNimrodNode): biggestInt {.magic: "NIntVal".}
|
||||
proc floatVal*(n: PNimrodNode): biggestFloat {.magic: "NFloatVal".}
|
||||
|
|
@ -133,43 +157,81 @@ proc `typ=`*(n: PNimrodNode, typ: PNimrodType) {.magic: "NSetType".}
|
|||
proc `strVal=`*(n: PNimrodNode, val: string) {.magic: "NSetStrVal".}
|
||||
|
||||
proc newNimNode*(kind: TNimrodNodeKind,
|
||||
n: PNimrodNode=nil): PNimrodNode {.magic: "NNewNimNode".}
|
||||
n: PNimrodNode=nil): PNimrodNode {.magic: "NNewNimNode".}
|
||||
|
||||
proc copyNimNode*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimNode".}
|
||||
proc copyNimTree*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimTree".}
|
||||
|
||||
proc error*(msg: string) {.magic: "NError".}
|
||||
## writes an error message at compile time
|
||||
|
||||
proc warning*(msg: string) {.magic: "NWarning".}
|
||||
## writes a warning message at compile time
|
||||
|
||||
proc hint*(msg: string) {.magic: "NHint".}
|
||||
## writes a hint message at compile time
|
||||
|
||||
proc newStrLitNode*(s: string): PNimrodNode {.compileTime.} =
|
||||
## creates a string literal node from `s`
|
||||
result = newNimNode(nnkStrLit)
|
||||
result.strVal = s
|
||||
|
||||
proc newIntLitNode*(i: biggestInt): PNimrodNode {.compileTime.} =
|
||||
## creates a int literal node from `i`
|
||||
result = newNimNode(nnkIntLit)
|
||||
result.intVal = i
|
||||
|
||||
proc newIntLitNode*(f: biggestFloat): PNimrodNode {.compileTime.} =
|
||||
proc newFloatLitNode*(f: biggestFloat): PNimrodNode {.compileTime.} =
|
||||
## creates a float literal node from `f`
|
||||
result = newNimNode(nnkFloatLit)
|
||||
result.floatVal = f
|
||||
|
||||
proc newIdentNode*(i: TNimrodIdent): PNimrodNode {.compileTime.} =
|
||||
## creates an identifier node from `i`
|
||||
result = newNimNode(nnkIdent)
|
||||
result.ident = i
|
||||
|
||||
proc newIdentNode*(i: string): PNimrodNode {.compileTime.} =
|
||||
## creates an identifier node from `i`
|
||||
result = newNimNode(nnkIdent)
|
||||
result.ident = !i
|
||||
|
||||
proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||
## converts the AST `n` to the concrete Nimrod code and wraps that
|
||||
## in a string literal node
|
||||
return newStrLitNode(repr(n))
|
||||
|
||||
proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
|
||||
## checks that `n` is of kind `k`. If this is not the case,
|
||||
## compilation aborts with an error message. This is useful for writing
|
||||
## macros that check the AST that is passed to them.
|
||||
if n.kind != k: error("macro expects a node of kind: " & repr(k))
|
||||
|
||||
proc expectMinLen*(n: PNimrodNode, min: int) {.compileTime.} =
|
||||
## checks that `n` has at least `min` children. If this is not the case,
|
||||
## compilation aborts with an error message. This is useful for writing
|
||||
## macros that check its number of arguments.
|
||||
if n.len < min: error("macro expects a node with " & $min & " children")
|
||||
|
||||
proc expectLen*(n: PNimrodNode, len: int) {.compileTime.} =
|
||||
## checks that `n` has exactly `len` children. If this is not the case,
|
||||
## compilation aborts with an error message. This is useful for writing
|
||||
## macros that check its number of arguments.
|
||||
if n.len != len: error("macro expects a node with " & $len & " children")
|
||||
|
||||
proc newCall*(theProc: TNimrodIdent,
|
||||
args: openArray[PNimrodNode]): PNimrodNode {.compileTime.} =
|
||||
## produces a new call node. `theProc` is the proc that is called with
|
||||
## the arguments ``args[0..]``.
|
||||
result = newNimNode(nnkCall)
|
||||
result.add(newIdentNode(theProc))
|
||||
result.add(args)
|
||||
|
||||
proc newCall*(theProc: string,
|
||||
args: openArray[PNimrodNode]): PNimrodNode {.compileTime.} =
|
||||
## produces a new call node. `theProc` is the proc that is called with
|
||||
## the arguments ``args[0..]``.
|
||||
result = newNimNode(nnkCall)
|
||||
result.add(newIdentNode(theProc))
|
||||
result.add(args)
|
||||
|
||||
|
|
|
|||
33
lib/math.nim
33
lib/math.nim
|
|
@ -51,6 +51,20 @@ proc classify*(x: float): TFloatClass =
|
|||
# XXX: fcSubnormal is not detected!
|
||||
|
||||
|
||||
proc binom*(n, k: int): int {.noSideEffect.} =
|
||||
## computes the binomial coefficient
|
||||
if k <= 0: return 1
|
||||
if 2*k > n: return binom(n, n-k)
|
||||
result = n
|
||||
for i in countup(2, k):
|
||||
result = (result * (n + 1 - i)) div i
|
||||
|
||||
proc fac*(n: int): int {.noSideEffect.} =
|
||||
## computes the faculty function
|
||||
result = 1
|
||||
for i in countup(2, n):
|
||||
result = result * i
|
||||
|
||||
proc isPowerOfTwo*(x: int): bool {.noSideEffect.} =
|
||||
## returns true, if x is a power of two, false otherwise.
|
||||
## Negative numbers are not a power of two.
|
||||
|
|
@ -74,6 +88,25 @@ proc countBits*(n: int32): int {.noSideEffect.}
|
|||
|
||||
include cntbits
|
||||
|
||||
proc sum*[T](x: openarray[T]): T {.noSideEffect.} =
|
||||
## computes the sum of the elements in `x`.
|
||||
## If `x` is empty, 0 is returned.
|
||||
for i in items(x): result = result + i
|
||||
|
||||
proc mean*(x: openarray[float]): float {.noSideEffect.} =
|
||||
## computes the mean of the elements in `x`.
|
||||
## If `x` is empty, NaN is returned.
|
||||
result = sum(x) / toFloat(len(x))
|
||||
|
||||
proc variance*(x: openarray[float]): float {.noSideEffect.} =
|
||||
## computes the mean of the elements in `x`.
|
||||
## If `x` is empty, NaN is returned.
|
||||
result = 0.0
|
||||
var m = mean(x)
|
||||
for i in 0 .. high(x):
|
||||
var diff = x[i] - m
|
||||
result = result + diff*diff
|
||||
result = result / toFloat(len(x))
|
||||
|
||||
when not defined(ECMAScript):
|
||||
proc random*(max: int): int
|
||||
|
|
|
|||
|
|
@ -360,7 +360,7 @@ proc findSuitableBlock(t: var TLSF, fl, sl: var int): Pbhdr =
|
|||
|
||||
proc extractBlockHdr(b: Pbhdr, t: var TLSF, fl, sl: int) {.inline.} =
|
||||
t.matrix[fl][sl] = b.freePtr.next
|
||||
if t.matrix[fl][sl] != 0:
|
||||
if t.matrix[fl][sl] != nil:
|
||||
t.matrix[fl][sl].freePtr.prev = nil
|
||||
else:
|
||||
clear_bit(sl, t.slBitmap[fl])
|
||||
|
|
|
|||
|
|
@ -1,12 +1,24 @@
|
|||
/*
|
||||
|
||||
Nimrod's Runtime Library
|
||||
(c) Copyright 2008 Andreas Rumpf
|
||||
(c) Copyright 2009 Andreas Rumpf
|
||||
|
||||
See the file "copying.txt", included in this
|
||||
distribution, for details about the copyright.
|
||||
*/
|
||||
|
||||
/* compiler symbols:
|
||||
__BORLANDC__
|
||||
_MSC_VER
|
||||
__WATCOMC__
|
||||
__LCC__
|
||||
__GNUC__
|
||||
__DMC__
|
||||
__POCC__
|
||||
__TINYC__
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NIMBASE_H
|
||||
#define NIMBASE_H
|
||||
|
||||
|
|
@ -239,17 +251,6 @@ static unsigned long nimInf[2]={0xffffffff, 0x7fffffff};
|
|||
# define INF (*(double*) nimInf)
|
||||
#endif */
|
||||
|
||||
/* compiler symbols:
|
||||
__BORLANDC__
|
||||
_MSC_VER
|
||||
__WATCOMC__
|
||||
__LCC__
|
||||
__GNUC__
|
||||
__DMC__
|
||||
__POCC__
|
||||
__TINYC__
|
||||
*/
|
||||
|
||||
/* C99 compiler? */
|
||||
#if (defined(__STD_VERSION__) && (__STD_VERSION__ >= 199901))
|
||||
# define HAVE_STDINT_H
|
||||
|
|
|
|||
34
lib/os.nim
34
lib/os.nim
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2008 Andreas Rumpf
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -178,12 +178,14 @@ proc ExistsFile*(filename: string): bool
|
|||
proc JoinPath*(head, tail: string): string {.noSideEffect.}
|
||||
## Joins two directory names to one.
|
||||
##
|
||||
## For example on Unix::
|
||||
## For example on Unix:
|
||||
##
|
||||
## ..code-block:: nimrod
|
||||
## JoinPath("usr", "lib")
|
||||
##
|
||||
## results in::
|
||||
## results in:
|
||||
##
|
||||
## ..code-block:: nimrod
|
||||
## "usr/lib"
|
||||
##
|
||||
## If head is the empty string, tail is returned.
|
||||
|
|
@ -312,7 +314,7 @@ proc createDir*(dir: string)
|
|||
##
|
||||
## The directory may contain several
|
||||
## subdirectories that do not exist yet. The full path is created. If this
|
||||
## fails, `EOS` is raised. It does NOT fail if the path already exists
|
||||
## fails, `EOS` is raised. It does **not** fail if the path already exists
|
||||
## because for most usages this does not indicate an error.
|
||||
|
||||
proc existsDir*(dir: string): bool
|
||||
|
|
@ -650,8 +652,7 @@ proc searchExtPos(s: string): int =
|
|||
break # do not skip over path
|
||||
|
||||
proc SplitFilename(filename: string, name, extension: var string) =
|
||||
var
|
||||
extPos = searchExtPos(filename)
|
||||
var extPos = searchExtPos(filename)
|
||||
if extPos >= 0:
|
||||
name = copy(filename, 0, extPos-1)
|
||||
extension = copy(filename, extPos)
|
||||
|
|
@ -664,14 +665,12 @@ proc normExt(ext: string): string =
|
|||
else: result = extSep & ext
|
||||
|
||||
proc ChangeFileExt(filename, ext: string): string =
|
||||
var
|
||||
extPos = searchExtPos(filename)
|
||||
var extPos = searchExtPos(filename)
|
||||
if extPos < 0: result = filename & normExt(ext)
|
||||
else: result = copy(filename, 0, extPos-1) & normExt(ext)
|
||||
|
||||
proc AppendFileExt(filename, ext: string): string =
|
||||
var
|
||||
extPos = searchExtPos(filename)
|
||||
var extPos = searchExtPos(filename)
|
||||
if extPos < 0: result = filename & normExt(ext)
|
||||
else: result = filename #make a string copy here
|
||||
|
||||
|
|
@ -850,12 +849,12 @@ proc rawCreateDir(dir: string) =
|
|||
OSError()
|
||||
|
||||
proc createDir(dir: string) =
|
||||
for i in 0.. dir.len-1:
|
||||
for i in 1.. dir.len-1:
|
||||
if dir[i] in {dirsep, altsep}: rawCreateDir(copy(dir, 0, i-1))
|
||||
rawCreateDir(dir)
|
||||
|
||||
proc executeShellCommand(command: string): int =
|
||||
return csystem(command)
|
||||
result = csystem(command)
|
||||
|
||||
var
|
||||
envComputed: bool = false
|
||||
|
|
@ -892,8 +891,7 @@ else:
|
|||
proc getEnvVarsC() =
|
||||
# retrieves the variables of char** env of C's main proc
|
||||
if not envComputed:
|
||||
var
|
||||
i: int = 0
|
||||
var i = 0
|
||||
while True:
|
||||
if gEnv[i] == nil: break
|
||||
add environment, $gEnv[i]
|
||||
|
|
@ -904,13 +902,13 @@ proc findEnvVar(key: string): int =
|
|||
getEnvVarsC()
|
||||
var temp = key & '='
|
||||
for i in 0..high(environment):
|
||||
if findSubStr(temp, environment[i]) == 0: return i
|
||||
if startsWith(environment[i], temp): return i
|
||||
return -1
|
||||
|
||||
proc getEnv(key: string): string =
|
||||
var i = findEnvVar(key)
|
||||
if i >= 0:
|
||||
return copy(environment[i], findSubStr("=", environment[i])+1)
|
||||
return copy(environment[i], find(environment[i], '=')+1)
|
||||
else:
|
||||
var env = cgetenv(key)
|
||||
if env == nil: return ""
|
||||
|
|
@ -925,7 +923,7 @@ iterator iterOverEnvironment*(): tuple[key, value: string] =
|
|||
## tuple is the name of the current variable stored, in the second its value.
|
||||
getEnvVarsC()
|
||||
for i in 0..high(environment):
|
||||
var p = findSubStr("=", environment[i])
|
||||
var p = find(environment[i], '=')
|
||||
yield (copy(environment[i], 0, p-1), copy(environment[i], p+1))
|
||||
|
||||
proc putEnv(key, val: string) =
|
||||
|
|
@ -1140,7 +1138,7 @@ else:
|
|||
proc GetConfigDir(): string = return getEnv("HOME") & "/.config/"
|
||||
|
||||
var
|
||||
cmdCount {.importc: "cmdCount".}: int
|
||||
cmdCount {.importc: "cmdCount".}: cint
|
||||
cmdLine {.importc: "cmdLine".}: cstringArray
|
||||
|
||||
proc paramStr(i: int): string =
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
## The ``parsecfg`` module implements a high performance configuration file
|
||||
## parser. The configuration file's syntax is similar to the Windows ``.ini``
|
||||
## format, but much more powerful, as it is not a line based parser. String
|
||||
## literals, raw string literals and triple quote string literals are supported
|
||||
## literals, raw string literals and triple quoted string literals are supported
|
||||
## as in the Nimrod programming language.
|
||||
|
||||
## This is an example of how a configuration file may look like:
|
||||
|
|
@ -135,7 +135,6 @@ proc handleDecChars(c: var TCfgParser, xi: var int) =
|
|||
inc(c.bufpos)
|
||||
|
||||
proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
|
||||
var xi: int
|
||||
inc(c.bufpos) # skip '\'
|
||||
case c.buf[c.bufpos]
|
||||
of 'n', 'N':
|
||||
|
|
@ -173,12 +172,12 @@ proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
|
|||
Inc(c.bufpos)
|
||||
of 'x', 'X':
|
||||
inc(c.bufpos)
|
||||
xi = 0
|
||||
var xi = 0
|
||||
handleHexChar(c, xi)
|
||||
handleHexChar(c, xi)
|
||||
add(tok.literal, Chr(xi))
|
||||
of '0'..'9':
|
||||
xi = 0
|
||||
var xi = 0
|
||||
handleDecChars(c, xi)
|
||||
if (xi <= 255): add(tok.literal, Chr(xi))
|
||||
else: tok.kind = tkInvalid
|
||||
|
|
@ -191,12 +190,8 @@ proc HandleCRLF(c: var TCfgParser, pos: int): int =
|
|||
else: result = pos
|
||||
|
||||
proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
|
||||
var
|
||||
pos: int
|
||||
ch: Char
|
||||
buf: cstring
|
||||
pos = c.bufPos + 1 # skip "
|
||||
buf = c.buf # put `buf` in a register
|
||||
var pos = c.bufPos + 1 # skip "
|
||||
var buf = c.buf # put `buf` in a register
|
||||
tok.kind = tkSymbol
|
||||
if (buf[pos] == '\"') and (buf[pos + 1] == '\"'):
|
||||
# long string literal:
|
||||
|
|
@ -211,19 +206,18 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
|
|||
Inc(pos)
|
||||
of '\c', '\L':
|
||||
pos = HandleCRLF(c, pos)
|
||||
tok.literal = tok.literal & nl
|
||||
add(tok.literal, nl)
|
||||
of lexbase.EndOfFile:
|
||||
tok.kind = tkInvalid
|
||||
break
|
||||
else:
|
||||
add(tok.literal, buf[pos])
|
||||
Inc(pos)
|
||||
c.bufpos = pos +
|
||||
3 # skip the three """
|
||||
c.bufpos = pos + 3 # skip the three """
|
||||
else:
|
||||
# ordinary string literal
|
||||
while true:
|
||||
ch = buf[pos]
|
||||
var ch = buf[pos]
|
||||
if ch == '\"':
|
||||
inc(pos) # skip '"'
|
||||
break
|
||||
|
|
@ -240,11 +234,8 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
|
|||
c.bufpos = pos
|
||||
|
||||
proc getSymbol(c: var TCfgParser, tok: var TToken) =
|
||||
var
|
||||
pos: int
|
||||
buf: cstring
|
||||
pos = c.bufpos
|
||||
buf = c.buf
|
||||
var pos = c.bufpos
|
||||
var buf = c.buf
|
||||
while true:
|
||||
add(tok.literal, buf[pos])
|
||||
Inc(pos)
|
||||
|
|
@ -253,11 +244,8 @@ proc getSymbol(c: var TCfgParser, tok: var TToken) =
|
|||
tok.kind = tkSymbol
|
||||
|
||||
proc skip(c: var TCfgParser) =
|
||||
var
|
||||
buf: cstring
|
||||
pos: int
|
||||
pos = c.bufpos
|
||||
buf = c.buf
|
||||
var pos = c.bufpos
|
||||
var buf = c.buf
|
||||
while true:
|
||||
case buf[pos]
|
||||
of ' ', '\t':
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2008 Andreas Rumpf
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -73,8 +73,7 @@ proc parseWord(s: string, i: int, w: var string,
|
|||
inc(result)
|
||||
|
||||
proc handleShortOption(p: var TOptParser) =
|
||||
var i: int
|
||||
i = p.pos
|
||||
var i = p.pos
|
||||
p.kind = cmdShortOption
|
||||
add(p.key, p.cmd[i])
|
||||
inc(i)
|
||||
|
|
@ -91,8 +90,7 @@ proc handleShortOption(p: var TOptParser) =
|
|||
p.pos = i
|
||||
|
||||
proc next(p: var TOptParser) =
|
||||
var i: int
|
||||
i = p.pos
|
||||
var i = p.pos
|
||||
while p.cmd[i] in {'\x09', ' '}: inc(i)
|
||||
p.pos = i
|
||||
setlen(p.key, 0)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
14
lib/repr.nim
14
lib/repr.nim
|
|
@ -104,17 +104,17 @@ proc reprSet(p: pointer, typ: PNimType): string {.compilerproc.} =
|
|||
type
|
||||
TReprClosure {.final.} = object # we cannot use a global variable here
|
||||
# as this wouldn't be thread-safe
|
||||
marked: TCellSet
|
||||
marked: TCellSeq
|
||||
recdepth: int # do not recurse endless
|
||||
indent: int # indentation
|
||||
|
||||
proc initReprClosure(cl: var TReprClosure) =
|
||||
CellSetInit(cl.marked)
|
||||
Init(cl.marked)
|
||||
cl.recdepth = -1 # default is to display everything!
|
||||
cl.indent = 0
|
||||
|
||||
proc deinitReprClosure(cl: var TReprClosure) =
|
||||
CellSetDeinit(cl.marked)
|
||||
Deinit(cl.marked)
|
||||
|
||||
proc reprBreak(result: var string, cl: TReprClosure) =
|
||||
add result, "\n"
|
||||
|
|
@ -145,7 +145,6 @@ proc reprSequence(result: var string, p: pointer, typ: PNimType,
|
|||
typ.Base, cl)
|
||||
add result, "]"
|
||||
|
||||
|
||||
proc reprRecordAux(result: var string, p: pointer, n: ptr TNimNode,
|
||||
cl: var TReprClosure) =
|
||||
case n.kind
|
||||
|
|
@ -172,11 +171,14 @@ proc reprRecord(result: var string, p: pointer, typ: PNimType,
|
|||
proc reprRef(result: var string, p: pointer, typ: PNimType,
|
||||
cl: var TReprClosure) =
|
||||
# we know that p is not nil here:
|
||||
var cell = usrToCell(p)
|
||||
when defined(boehmGC):
|
||||
var cell = cast[PCell](p)
|
||||
else:
|
||||
var cell = usrToCell(p)
|
||||
add result, "ref " & reprPointer(p)
|
||||
if cell notin cl.marked:
|
||||
# only the address is shown:
|
||||
incl(cl.marked, cell)
|
||||
add(cl.marked, cell)
|
||||
add result, " --> "
|
||||
reprAux(result, p, typ.base, cl)
|
||||
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ proc readLine*(s: PStream): string =
|
|||
type
|
||||
PStringStream* = ref TStringStream ## a stream that encapsulates a string
|
||||
TStringStream* = object of TStream
|
||||
data: string
|
||||
data*: string
|
||||
pos: int
|
||||
|
||||
proc ssAtEnd(s: PStringStream): bool =
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ import
|
|||
os, hashes, strutils
|
||||
|
||||
type
|
||||
TStringTableMode* = enum # describes the tables operation mode
|
||||
modeCaseSensitive, # the table is case sensitive
|
||||
modeCaseInsensitive, # the table is case insensitive
|
||||
modeStyleInsensitive # the table is style insensitive
|
||||
TStringTableMode* = enum ## describes the tables operation mode
|
||||
modeCaseSensitive, ## the table is case sensitive
|
||||
modeCaseInsensitive, ## the table is case insensitive
|
||||
modeStyleInsensitive ## the table is style insensitive
|
||||
TKeyValuePair = tuple[key, val: string]
|
||||
TKeyValuePairSeq = seq[TKeyValuePair]
|
||||
TStringTable* = object of TObject
|
||||
|
|
@ -61,14 +61,14 @@ iterator pairs*(t: PStringTable): tuple[key, value: string] =
|
|||
yield (t.data[h].key, t.data[h].val)
|
||||
|
||||
type
|
||||
TFormatFlag* = enum # flags for the `%` operator
|
||||
useEnvironment, # use environment variable if the ``$key``
|
||||
# is not found in the table
|
||||
useEmpty, # use the empty string as a default, thus it
|
||||
# won't throw an exception if ``$key`` is not
|
||||
# in the table
|
||||
useKey # do not replace ``$key`` if it is not found
|
||||
# in the table (or in the environment)
|
||||
TFormatFlag* = enum ## flags for the `%` operator
|
||||
useEnvironment, ## use environment variable if the ``$key``
|
||||
## is not found in the table
|
||||
useEmpty, ## use the empty string as a default, thus it
|
||||
## won't throw an exception if ``$key`` is not
|
||||
## in the table
|
||||
useKey ## do not replace ``$key`` if it is not found
|
||||
## in the table (or in the environment)
|
||||
|
||||
proc `%`*(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string
|
||||
## The `%` operator for string tables.
|
||||
|
|
@ -110,9 +110,7 @@ proc mustRehash(length, counter: int): bool =
|
|||
result = (length * 2 < counter * 3) or (length - counter < 4)
|
||||
|
||||
proc nextTry(h, maxHash: THash): THash =
|
||||
result = ((5 * h) + 1) and maxHash # For any initial h in range(maxHash), repeating that maxHash times
|
||||
# generates each int in range(maxHash) exactly once (see any text on
|
||||
# random-number generation for proof).
|
||||
result = ((5 * h) + 1) and maxHash
|
||||
|
||||
proc RawGet(t: PStringTable, key: string): int =
|
||||
var h: THash
|
||||
|
|
@ -166,35 +164,30 @@ proc getValue(t: PStringTable, flags: set[TFormatFlag], key: string): string =
|
|||
if hasKey(t, key): return t[key]
|
||||
if useEnvironment in flags: result = os.getEnv(key)
|
||||
else: result = ""
|
||||
if (result == ""):
|
||||
if result.len == 0:
|
||||
if useKey in flags: result = '$' & key
|
||||
elif not (useEmpty in flags): raiseFormatException(key)
|
||||
|
||||
proc `%`(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string =
|
||||
const
|
||||
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'}
|
||||
var
|
||||
i, j: int
|
||||
key: string
|
||||
result = ""
|
||||
i = strStart
|
||||
while i <= len(f) + strStart - 1:
|
||||
var i = 0
|
||||
while i < len(f):
|
||||
if f[i] == '$':
|
||||
case f[i + 1]
|
||||
case f[i+1]
|
||||
of '$':
|
||||
add(result, '$')
|
||||
inc(i, 2)
|
||||
of '{':
|
||||
j = i + 1
|
||||
while (j <= len(f) + strStart - 1) and (f[j] != '}'): inc(j)
|
||||
key = copy(f, i + 2, j - 1)
|
||||
result = result & getValue(t, flags, key)
|
||||
var j = i + 1
|
||||
while j < f.len and f[j] != '}': inc(j)
|
||||
add(result, getValue(t, flags, copy(f, i+2, j-1)))
|
||||
i = j + 1
|
||||
of 'a'..'z', 'A'..'Z', '\x80'..'\xFF', '_':
|
||||
j = i + 1
|
||||
while (j <= len(f) + strStart - 1) and (f[j] in PatternChars): inc(j)
|
||||
key = copy(f, i+1, j - 1)
|
||||
result = result & getValue(t, flags, key)
|
||||
var j = i + 1
|
||||
while j < f.len and f[j] in PatternChars: inc(j)
|
||||
add(result, getValue(t, flags, copy(f, i+1, j-1)))
|
||||
i = j
|
||||
else:
|
||||
add(result, f[i])
|
||||
|
|
|
|||
312
lib/strutils.nim
312
lib/strutils.nim
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2006 Andreas Rumpf
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -9,8 +9,7 @@
|
|||
|
||||
## This module contains various string utility routines.
|
||||
## See the module `regexprs` for regular expression support.
|
||||
## All the routines here are avaiable for the EMCAScript target
|
||||
## too!
|
||||
## All the routines here are avaiable for the EMCAScript target too!
|
||||
|
||||
{.deadCodeElim: on.}
|
||||
|
||||
|
|
@ -33,13 +32,66 @@ type
|
|||
const
|
||||
Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'}
|
||||
## All the characters that count as whitespace.
|
||||
|
||||
Letters* = {'A'..'Z', 'a'..'z'}
|
||||
## the set of letters
|
||||
|
||||
Digits* = {'0'..'9'}
|
||||
## the set of digits
|
||||
|
||||
IdentChars* = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
|
||||
## the set of characters an identifier can consist of
|
||||
|
||||
IdentStartChars* = {'a'..'z', 'A'..'Z', '_'}
|
||||
## the set of characters an identifier can start with
|
||||
|
||||
strStart* = 0 # this is only for bootstraping
|
||||
# XXX: remove this someday
|
||||
nl* = "\n" # this is only for bootstraping XXX: remove this somehow
|
||||
|
||||
proc strip*(s: string): string {.noSideEffect.}
|
||||
## Strips leading and trailing whitespace from `s`.
|
||||
proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect.}
|
||||
## The `substitution`:idx: operator performs string substitutions in
|
||||
## `formatstr` and returns a modified `formatstr`. This is often called
|
||||
## `string interpolation`:idx:.
|
||||
##
|
||||
## This is best explained by an example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## "$1 eats $2." % ["The cat", "fish"]
|
||||
##
|
||||
## Results in:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## "The cat eats fish."
|
||||
##
|
||||
## The substitution variables (the thing after the ``$``)
|
||||
## are enumerated from 1 to 9.
|
||||
## Substitution variables can also be words (that is
|
||||
## ``[A-Za-z_]+[A-Za-z0-9_]*``) in which case the arguments in `a` with even
|
||||
## indices are keys and with odd indices are the corresponding values.
|
||||
## An example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## "$animal eats $food." % ["animal", "The cat", "food", "fish"]
|
||||
##
|
||||
## Results in:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## "The cat eats fish."
|
||||
##
|
||||
## The variables are compared with `cmpIgnoreStyle`. `EInvalidValue` is
|
||||
## raised if an ill-formed format string has been passed to the `%` operator.
|
||||
|
||||
proc `%` *(formatstr, a: string): string {.noSideEffect.}
|
||||
## This is the same as ``formatstr % [a]``.
|
||||
|
||||
proc addf*(s: var string, formatstr: string, a: openarray[string])
|
||||
## The same as ``add(s, formatstr % a)``, but more efficient.
|
||||
|
||||
proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect.}
|
||||
## Strips whitespace from `s` and returns the resulting string.
|
||||
## If `leading` is true, leading whitespace is stripped.
|
||||
## If `trailing` is true, trailing whitespace is stripped.
|
||||
|
||||
proc toLower*(s: string): string {.noSideEffect.}
|
||||
## Converts `s` into lower case. This works only for the letters A-Z.
|
||||
|
|
@ -65,15 +117,36 @@ proc normalize*(s: string): string {.noSideEffect.}
|
|||
## Normalizes the string `s`. That means to convert it to lower case and
|
||||
## remove any '_'. This is needed for Nimrod identifiers for example.
|
||||
|
||||
proc findSubStr*(sub, s: string, start: int = 0): int {.noSideEffect.}
|
||||
proc findSubStr*(sub, s: string, start: int = 0): int {.
|
||||
noSideEffect, deprecated.}
|
||||
## Searches for `sub` in `s` starting at position `start`. Searching is
|
||||
## case-sensitive. If `sub` is not in `s`, -1 is returned.
|
||||
## **Deprecated since version 0.7.6**: Use `find` instead, but beware that
|
||||
## this has a different parameter order.
|
||||
|
||||
proc findSubStr*(sub: char, s: string, start: int = 0): int {.
|
||||
noSideEffect, deprecated.}
|
||||
## Searches for `sub` in `s` starting at position `start`. Searching is
|
||||
## case-sensitive. If `sub` is not in `s`, -1 is returned.
|
||||
## **Deprecated since version 0.7.6**: Use `find` instead, but beware that
|
||||
## this has a different parameter order.
|
||||
|
||||
proc findChars*(chars: set[char], s: string, start: int = 0): int {.
|
||||
noSideEffect, deprecated.}
|
||||
## Searches for `chars` in `s` starting at position `start`. If `s` contains
|
||||
## none of the characters in `chars`, -1 is returned.
|
||||
## **Deprecated since version 0.7.6**: Use `find` instead, but beware that
|
||||
## this has a different parameter order.
|
||||
|
||||
proc find*(s, sub: string, start: int = 0): int {.noSideEffect.}
|
||||
## Searches for `sub` in `s` starting at position `start`. Searching is
|
||||
## case-sensitive. If `sub` is not in `s`, -1 is returned.
|
||||
|
||||
proc findSubStr*(sub: char, s: string, start: int = 0): int {.noSideEffect.}
|
||||
proc find*(s: string, sub: char, start: int = 0): int {.noSideEffect.}
|
||||
## Searches for `sub` in `s` starting at position `start`. Searching is
|
||||
## case-sensitive. If `sub` is not in `s`, -1 is returned.
|
||||
|
||||
proc findChars*(chars: set[char], s: string, start: int = 0): int {.noSideEffect.}
|
||||
proc find*(s: string, chars: set[char], start: int = 0): int {.noSideEffect.}
|
||||
## Searches for `chars` in `s` starting at position `start`. If `s` contains
|
||||
## none of the characters in `chars`, -1 is returned.
|
||||
|
||||
|
|
@ -95,15 +168,15 @@ iterator split*(s: string, seps: set[char] = Whitespace): string =
|
|||
## Splits the string `s` into substrings.
|
||||
##
|
||||
## Substrings are separated by a substring containing only `seps`.
|
||||
## The seperator substrings are not returned in `sub`, nor are they part
|
||||
## of `sub`.
|
||||
## Examples::
|
||||
## Examples:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## for word in split(" this is an example "):
|
||||
## writeln(stdout, word)
|
||||
##
|
||||
## Results in::
|
||||
## Results in:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## "this"
|
||||
## "is"
|
||||
## "an"
|
||||
|
|
@ -123,18 +196,54 @@ iterator split*(s: string, seps: set[char] = Whitespace): string =
|
|||
while last < len(s) and s[last] not_in seps: inc(last) # BUGFIX!
|
||||
yield copy(s, first, last-1)
|
||||
|
||||
iterator split*(s: string, sep: char): string =
|
||||
## Splits the string `s` into substrings.
|
||||
##
|
||||
## Substrings are separated by the character `sep`.
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## for word in split(";;this;is;an;;example;;;", ';'):
|
||||
## writeln(stdout, word)
|
||||
##
|
||||
## Results in:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## ""
|
||||
## ""
|
||||
## "this"
|
||||
## "is"
|
||||
## "an"
|
||||
## ""
|
||||
## "example"
|
||||
## ""
|
||||
## ""
|
||||
## ""
|
||||
##
|
||||
var last = 0
|
||||
assert('\0' != sep)
|
||||
if len(s) > 0:
|
||||
# `<=` is correct here for the edge cases!
|
||||
while last <= len(s):
|
||||
var first = last
|
||||
while last < len(s) and s[last] != sep: inc(last)
|
||||
yield copy(s, first, last-1)
|
||||
inc(last)
|
||||
|
||||
iterator splitLines*(s: string): string =
|
||||
## Splits the string `s` into its containing lines. Each newline
|
||||
## combination (CR, LF, CR-LF) is supported. The result strings contain
|
||||
## no trailing ``\n``.
|
||||
##
|
||||
## Example::
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## for line in lines("\nthis\nis\nan\n\nexample\n"):
|
||||
## writeln(stdout, line)
|
||||
##
|
||||
## Results in::
|
||||
## Results in:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## ""
|
||||
## "this"
|
||||
## "is"
|
||||
|
|
@ -164,6 +273,11 @@ proc splitSeq*(s: string, seps: set[char] = Whitespace): seq[string] {.
|
|||
noSideEffect.}
|
||||
## The same as `split`, but is a proc that returns a sequence of substrings.
|
||||
|
||||
proc splitSeq*(s: string, sep: char): seq[string] {.noSideEffect.} =
|
||||
## The same as `split`, but is a proc that returns a sequence of substrings.
|
||||
result = @[]
|
||||
for sub in split(s, sep): add(result, sub)
|
||||
|
||||
proc cmpIgnoreCase*(a, b: string): int {.noSideEffect.}
|
||||
## Compares two strings in a case insensitive manner. Returns:
|
||||
##
|
||||
|
|
@ -207,7 +321,7 @@ proc ParseBiggestInt*(s: string): biggestInt {.noSideEffect.}
|
|||
## Parses a decimal integer value contained in `s`. If `s` is not
|
||||
## a valid integer, `EInvalidValue` is raised.
|
||||
|
||||
proc ParseFloat*(s: string): float {.noSideEffect.}
|
||||
proc ParseFloat*(s: string, start = 0): float {.noSideEffect.}
|
||||
## Parses a decimal floating point value contained in `s`. If `s` is not
|
||||
## a valid floating point number, `EInvalidValue` is raised. ``NAN``,
|
||||
## ``INF``, ``-INF`` are also supported (case insensitive comparison).
|
||||
|
|
@ -217,37 +331,6 @@ proc ParseFloat*(s: string): float {.noSideEffect.}
|
|||
proc toString*[Ty](x: Ty): string
|
||||
## This generic proc is the same as the stringify operator `$`.
|
||||
|
||||
proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect.}
|
||||
## The substitution operator performs string substitutions in `formatstr`
|
||||
## and returns the modified `formatstr`.
|
||||
##
|
||||
## This is best explained by an example::
|
||||
##
|
||||
## "$1 eats $2." % ["The cat", "fish"]
|
||||
##
|
||||
## Results in::
|
||||
##
|
||||
## "The cat eats fish."
|
||||
##
|
||||
## The substitution variables (the thing after the ``$``)
|
||||
## are enumerated from 1 to 9.
|
||||
## Substitution variables can also be words (that is
|
||||
## ``[A-Za-z_]+[A-Za-z0-9_]*``) in which case the arguments in `a` with even
|
||||
## indices are keys and with odd indices are the corresponding values. Again
|
||||
## an example::
|
||||
##
|
||||
## "$animal eats $food." % ["animal", "The cat", "food", "fish"]
|
||||
##
|
||||
## Results in::
|
||||
##
|
||||
## "The cat eats fish."
|
||||
##
|
||||
## The variables are compared with `cmpIgnoreStyle`. `EInvalidValue` is
|
||||
## raised if an ill-formed format string has been passed to the `%` operator.
|
||||
|
||||
proc `%` *(formatstr, a: string): string {.noSideEffect.}
|
||||
## This is the same as `formatstr % [a]`.
|
||||
|
||||
proc repeatChar*(count: int, c: Char = ' '): string
|
||||
## Returns a string of length `count` consisting only of
|
||||
## the character `c`.
|
||||
|
|
@ -260,7 +343,25 @@ proc endsWith*(s, suffix: string): bool {.noSideEffect.}
|
|||
## Returns true iff ``s`` ends with ``suffix``.
|
||||
## If ``suffix == ""`` true is returned.
|
||||
|
||||
# implementation
|
||||
proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
|
||||
inline.} =
|
||||
## A shorthand for:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## if dest.len > startLen: add(dest, sep)
|
||||
##
|
||||
## This is often useful for generating some code where the items need to
|
||||
## be *separated* by `sep`. `sep` is only added if `dest` is longer than
|
||||
## `startLen`. The following example creates a string describing
|
||||
## an array of integers:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## var arr = "["
|
||||
## for x in items([2, 3, 5, 7, 11]):
|
||||
## addSep(arr, startLen=len("["))
|
||||
## add(arr, $x)
|
||||
## add(arr, "]")
|
||||
if dest.len > startLen: add(dest, sep)
|
||||
|
||||
proc allCharsInSet*(s: string, theSet: TCharSet): bool =
|
||||
## returns true iff each character of `s` is in the set `theSet`.
|
||||
|
|
@ -271,7 +372,7 @@ proc allCharsInSet*(s: string, theSet: TCharSet): bool =
|
|||
proc quoteIfContainsWhite*(s: string): string =
|
||||
## returns ``'"' & s & '"'`` if `s` contains a space and does not
|
||||
## start with a quote, else returns `s`
|
||||
if findChars({' ', '\t'}, s) >= 0 and s[0] != '"':
|
||||
if find(s, {' ', '\t'}) >= 0 and s[0] != '"':
|
||||
result = '"' & s & '"'
|
||||
else:
|
||||
result = s
|
||||
|
|
@ -307,10 +408,8 @@ proc intToStr(x: int, minchars: int = 1): string =
|
|||
proc toString[Ty](x: Ty): string = return $x
|
||||
|
||||
proc toOctal(c: char): string =
|
||||
var
|
||||
val: int
|
||||
result = newString(3)
|
||||
val = ord(c)
|
||||
var val = ord(c)
|
||||
for i in countdown(2, 0):
|
||||
result[i] = Chr(val mod 8 + ord('0'))
|
||||
val = val div 8
|
||||
|
|
@ -326,18 +425,15 @@ proc findNormalized(x: string, inArray: openarray[string]): int =
|
|||
# security whole ...
|
||||
return -1
|
||||
|
||||
proc `%`(formatstr: string, a: openarray[string]): string =
|
||||
# the format operator
|
||||
const
|
||||
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
|
||||
result = ""
|
||||
proc addf(s: var string, formatstr: string, a: openarray[string]) =
|
||||
const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
|
||||
var i = 0
|
||||
while i < len(formatstr):
|
||||
if formatstr[i] == '$':
|
||||
case formatstr[i+1] # again we use the fact that strings
|
||||
# are zero-terminated here
|
||||
of '$':
|
||||
add result, '$'
|
||||
add s, '$'
|
||||
inc(i, 2)
|
||||
of '1'..'9':
|
||||
var j = 0
|
||||
|
|
@ -345,25 +441,29 @@ proc `%`(formatstr: string, a: openarray[string]): string =
|
|||
while formatstr[i] in {'0'..'9'}:
|
||||
j = j * 10 + ord(formatstr[i]) - ord('0')
|
||||
inc(i)
|
||||
add result, a[j - 1]
|
||||
add s, a[j - 1]
|
||||
of '{':
|
||||
var j = i+1
|
||||
while formatstr[j] notin {'\0', '}'}: inc(j)
|
||||
var x = findNormalized(copy(formatstr, i+2, j-1), a)
|
||||
if x >= 0 and x < high(a): add result, a[x+1]
|
||||
if x >= 0 and x < high(a): add s, a[x+1]
|
||||
else: raise newException(EInvalidValue, "invalid format string")
|
||||
i = j+1
|
||||
of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':
|
||||
var j = i+1
|
||||
while formatstr[j] in PatternChars: inc(j)
|
||||
var x = findNormalized(copy(formatstr, i+1, j-1), a)
|
||||
if x >= 0 and x < high(a): add result, a[x+1]
|
||||
if x >= 0 and x < high(a): add s, a[x+1]
|
||||
else: raise newException(EInvalidValue, "invalid format string")
|
||||
i = j
|
||||
else: raise newException(EInvalidValue, "invalid format string")
|
||||
else:
|
||||
add result, formatstr[i]
|
||||
add s, formatstr[i]
|
||||
inc(i)
|
||||
|
||||
proc `%`(formatstr: string, a: openarray[string]): string =
|
||||
result = ""
|
||||
addf(result, formatstr, a)
|
||||
|
||||
proc cmpIgnoreCase(a, b: string): int =
|
||||
# makes usage of the fact that strings are zero-terminated
|
||||
|
|
@ -377,9 +477,8 @@ proc cmpIgnoreCase(a, b: string): int =
|
|||
# thus we compile without checks here
|
||||
|
||||
proc cmpIgnoreStyle(a, b: string): int =
|
||||
var
|
||||
i = 0
|
||||
j = 0
|
||||
var i = 0
|
||||
var j = 0
|
||||
while True:
|
||||
while a[i] == '_': inc(i)
|
||||
while b[j] == '_': inc(j) # BUGFIX: typo
|
||||
|
|
@ -400,14 +499,16 @@ proc splitSeq(s: string, seps: set[char]): seq[string] =
|
|||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
proc strip(s: string): string =
|
||||
proc strip(s: string, leading = true, trailing = true): string =
|
||||
const
|
||||
chars: set[Char] = Whitespace
|
||||
var
|
||||
first = 0
|
||||
last = len(s)-1
|
||||
while s[first] in chars: inc(first)
|
||||
while last >= 0 and s[last] in chars: dec(last)
|
||||
if leading:
|
||||
while s[first] in chars: inc(first)
|
||||
if trailing:
|
||||
while last >= 0 and s[last] in chars: dec(last)
|
||||
result = copy(s, first, last)
|
||||
|
||||
proc toLower(c: Char): Char =
|
||||
|
|
@ -451,7 +552,7 @@ proc preprocessSub(sub: string, a: var TSkipTable) =
|
|||
for i in 0..0xff: a[chr(i)] = m+1
|
||||
for i in 0..m-1: a[sub[i]] = m-i
|
||||
|
||||
proc findSubStrAux(sub, s: string, start: int, a: TSkipTable): int =
|
||||
proc findSubStrAux(s, sub: string, start: int, a: TSkipTable): int =
|
||||
# fast "quick search" algorithm:
|
||||
var
|
||||
m = len(sub)
|
||||
|
|
@ -469,7 +570,7 @@ proc findSubStrAux(sub, s: string, start: int, a: TSkipTable): int =
|
|||
proc findSubStr(sub, s: string, start: int = 0): int =
|
||||
var a: TSkipTable
|
||||
preprocessSub(sub, a)
|
||||
result = findSubStrAux(sub, s, start, a)
|
||||
result = findSubStrAux(s, sub, start, a)
|
||||
# slow linear search:
|
||||
#var
|
||||
# i, j, M, N: int
|
||||
|
|
@ -492,6 +593,20 @@ proc findSubStr(sub, s: string, start: int = 0): int =
|
|||
# elif (i >= N):
|
||||
# return -1
|
||||
|
||||
proc find(s, sub: string, start: int = 0): int =
|
||||
var a: TSkipTable
|
||||
preprocessSub(sub, a)
|
||||
result = findSubStrAux(s, sub, start, a)
|
||||
|
||||
proc find(s: string, sub: char, start: int = 0): int =
|
||||
for i in start..len(s)-1:
|
||||
if sub == s[i]: return i
|
||||
return -1
|
||||
|
||||
proc find(s: string, chars: set[char], start: int = 0): int =
|
||||
for i in start..s.len-1:
|
||||
if s[i] in chars: return i
|
||||
return -1
|
||||
|
||||
proc findSubStr(sub: char, s: string, start: int = 0): int =
|
||||
for i in start..len(s)-1:
|
||||
|
|
@ -504,23 +619,21 @@ proc findChars(chars: set[char], s: string, start: int = 0): int =
|
|||
return -1
|
||||
|
||||
proc contains(s: string, chars: set[char]): bool =
|
||||
return findChars(chars, s) >= 0
|
||||
return find(s, chars) >= 0
|
||||
|
||||
proc contains(s: string, c: char): bool =
|
||||
return findSubStr(c, s) >= 0
|
||||
return find(s, c) >= 0
|
||||
|
||||
proc contains(s, sub: string): bool =
|
||||
return findSubStr(sub, s) >= 0
|
||||
return find(s, sub) >= 0
|
||||
|
||||
proc replaceStr(s, sub, by: string): string =
|
||||
var
|
||||
i, j: int
|
||||
a: TSkipTable
|
||||
var a: TSkipTable
|
||||
result = ""
|
||||
preprocessSub(sub, a)
|
||||
i = 0
|
||||
var i = 0
|
||||
while true:
|
||||
j = findSubStrAux(sub, s, i, a)
|
||||
var j = findSubStrAux(s, sub, i, a)
|
||||
if j < 0: break
|
||||
add result, copy(s, i, j - 1)
|
||||
add result, by
|
||||
|
|
@ -583,7 +696,10 @@ proc rawParseInt(s: string, index: var int): BiggestInt =
|
|||
while s[i] == '_':
|
||||
inc(i) # underscores are allowed and ignored
|
||||
result = result * sign
|
||||
index = i # store index back
|
||||
if s[i] == '\0':
|
||||
index = i # store index back
|
||||
else:
|
||||
index = -1 # BUGFIX: error!
|
||||
else:
|
||||
index = -1
|
||||
|
||||
|
|
@ -602,17 +718,17 @@ proc parseInt(s: string): int =
|
|||
result = int(res) # convert to smaller integer type
|
||||
|
||||
proc ParseBiggestInt(s: string): biggestInt =
|
||||
var
|
||||
index: int = 0
|
||||
var index = 0
|
||||
result = rawParseInt(s, index)
|
||||
if index == -1:
|
||||
raise newException(EInvalidValue, "invalid integer: " & s)
|
||||
|
||||
proc ParseFloat(s: string): float =
|
||||
proc ParseFloat(s: string, start = 0): float =
|
||||
var
|
||||
esign = 1.0
|
||||
sign = 1.0
|
||||
exponent, i: int
|
||||
i = start
|
||||
exponent: int
|
||||
flags: int
|
||||
result = 0.0
|
||||
if s[i] == '+': inc(i)
|
||||
|
|
@ -677,7 +793,7 @@ proc ParseFloat(s: string): float =
|
|||
|
||||
proc toOct*(x: BiggestInt, len: int): string =
|
||||
## converts `x` into its octal representation. The resulting string is
|
||||
## always `len` characters long. No leading ``0c`` prefix is generated.
|
||||
## always `len` characters long. No leading ``0o`` prefix is generated.
|
||||
var
|
||||
mask: BiggestInt = 7
|
||||
shift: BiggestInt = 0
|
||||
|
|
@ -701,7 +817,7 @@ proc toBin*(x: BiggestInt, len: int): string =
|
|||
shift = shift + 1
|
||||
mask = mask shl 1
|
||||
|
||||
proc escape*(s: string, prefix, suffix = "\""): string =
|
||||
proc escape*(s: string, prefix = "\"", suffix = "\""): string =
|
||||
## Escapes a string `s`. This does these operations (at the same time):
|
||||
## * replaces any ``\`` by ``\\``
|
||||
## * replaces any ``'`` by ``\'``
|
||||
|
|
@ -723,8 +839,34 @@ proc escape*(s: string, prefix, suffix = "\""): string =
|
|||
else: add(result, c)
|
||||
add(result, suffix)
|
||||
|
||||
proc validEmailAddress*(s: string): bool =
|
||||
## returns true if `s` seems to be a valid e-mail address.
|
||||
## The checking also uses a domain list.
|
||||
const
|
||||
chars = Letters + Digits + {'!','#','$','%','&',
|
||||
'\'','*','+','/','=','?','^','_','`','{','}','|','~','-','.'}
|
||||
var i = 0
|
||||
if s[i] notin chars or s[i] == '.': return false
|
||||
while s[i] in chars:
|
||||
if s[i] == '.' and s[i+1] == '.': return false
|
||||
inc(i)
|
||||
if s[i] != '@': return false
|
||||
var j = len(s)-1
|
||||
if s[j] notin letters: return false
|
||||
while j >= i and s[j] in letters: dec(j)
|
||||
inc(i) # skip '@'
|
||||
while s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i)
|
||||
if s[i] != '\0': return false
|
||||
|
||||
var x = copy(s, j+1)
|
||||
if len(x) == 2 and x[0] in Letters and x[1] in Letters: return true
|
||||
case toLower(x)
|
||||
of "com", "org", "net", "gov", "mil", "biz", "info", "mobi", "name",
|
||||
"aero", "jobs", "museum": return true
|
||||
return false
|
||||
|
||||
proc editDistance*(a, b: string): int =
|
||||
## returns the edit distance between `s` and `t`. This uses the Levenshtein
|
||||
## returns the edit distance between `a` and `b`. This uses the Levenshtein
|
||||
## distance algorithm with only a linear memory overhead. This implementation
|
||||
## is highly optimized!
|
||||
var len1 = a.len
|
||||
|
|
|
|||
|
|
@ -37,16 +37,21 @@ proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
|
|||
proc rawNewString(space: int): NimString {.compilerProc.} =
|
||||
var s = space
|
||||
if s < 8: s = 7
|
||||
result = cast[NimString](newObj(addr(strDesc), sizeof(TGenericSeq) +
|
||||
(s+1) * sizeof(char)))
|
||||
#result.len = 0
|
||||
when defined(boehmGC):
|
||||
result = cast[NimString](boehmAllocAtomic(
|
||||
sizeof(TGenericSeq) + (s+1) * sizeof(char)))
|
||||
result.len = 0
|
||||
result.data[0] = '\0'
|
||||
else:
|
||||
result = cast[NimString](newObj(addr(strDesc), sizeof(TGenericSeq) +
|
||||
(s+1) * sizeof(char)))
|
||||
result.space = s
|
||||
#result.data[0] = '\0'
|
||||
|
||||
proc mnewString(len: int): NimString {.exportc.} =
|
||||
result = rawNewString(len)
|
||||
result.len = len
|
||||
#result.data[len] = '\0'
|
||||
when defined(boehmGC):
|
||||
result.data[len] = '\0'
|
||||
|
||||
proc toNimStr(str: CString, len: int): NimString {.compilerProc.} =
|
||||
result = rawNewString(len)
|
||||
|
|
@ -224,10 +229,11 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
|
|||
GenericSeqSize))
|
||||
elif newLen < result.len:
|
||||
# we need to decref here, otherwise the GC leaks!
|
||||
for i in newLen..result.len-1:
|
||||
forAllChildrenAux(cast[pointer](cast[TAddress](result) +%
|
||||
GenericSeqSize +% (i*%elemSize)),
|
||||
extGetCellType(result).base, waZctDecRef)
|
||||
when not defined(boehmGC):
|
||||
for i in newLen..result.len-1:
|
||||
forAllChildrenAux(cast[pointer](cast[TAddress](result) +%
|
||||
GenericSeqSize +% (i*%elemSize)),
|
||||
extGetCellType(result).base, waZctDecRef)
|
||||
# and set the memory to nil:
|
||||
zeroMem(cast[pointer](cast[TAddress](result) +% GenericSeqSize +%
|
||||
(newLen*%elemSize)), (result.len-%newLen) *% elemSize)
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@
|
|||
## explicitly. Because of this there can not be a user-defined module named
|
||||
## ``system``.
|
||||
##
|
||||
## *"The good thing about reinventing the wheel is that you can get a
|
||||
## round one."*
|
||||
## *The good thing about reinventing the wheel is that you can get a
|
||||
## round one.*
|
||||
|
||||
{.push hints: off.}
|
||||
|
||||
|
|
@ -1044,8 +1044,9 @@ proc isNil*(x: cstring): bool {.noSideEffect, magic: "IsNil".}
|
|||
|
||||
|
||||
# Fixup some magic symbols here:
|
||||
{.fixup_system.} # This is an undocumented pragma that can only be used
|
||||
# once in the system module.
|
||||
#{.fixup_system.}
|
||||
# This is an undocumented pragma that can only be used
|
||||
# once in the system module.
|
||||
|
||||
proc `&` *[T](x, y: seq[T]): seq[T] {.noSideEffect.} =
|
||||
newSeq(result, x.len + y.len)
|
||||
|
|
@ -1099,6 +1100,13 @@ proc find*[T, S](a: T, item: S): int {.inline.} =
|
|||
inc(result)
|
||||
result = -1
|
||||
|
||||
proc pop*[T](s: var seq[T]): T {.inline.} =
|
||||
## returns the last item of `s` and decreases ``s.len`` by one. This treats
|
||||
## `s` as a stack and implements the common *pop* operation.
|
||||
var L = s.len-1
|
||||
result = s[L]
|
||||
setLen(s, L)
|
||||
|
||||
# ----------------- FPU ------------------------------------------------------
|
||||
|
||||
#proc disableFPUExceptions*()
|
||||
|
|
@ -1401,16 +1409,95 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
|||
# as it would recurse endlessly!
|
||||
include arithm
|
||||
{.pop.} # stack trace
|
||||
include dyncalls
|
||||
|
||||
const
|
||||
GenericSeqSize = (2 * sizeof(int))
|
||||
|
||||
proc reprAny(p: pointer, typ: PNimType): string {.compilerproc.}
|
||||
|
||||
when not defined(boehmgc) and not defined(nogc):
|
||||
proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int =
|
||||
assert(n.kind == nkCase)
|
||||
var d: int
|
||||
var a = cast[TAddress](aa)
|
||||
case n.typ.size
|
||||
of 1: d = ze(cast[ptr int8](a +% n.offset)^)
|
||||
of 2: d = ze(cast[ptr int16](a +% n.offset)^)
|
||||
of 4: d = int(cast[ptr int32](a +% n.offset)^)
|
||||
else: assert(false)
|
||||
return d
|
||||
|
||||
proc selectBranch(aa: Pointer, n: ptr TNimNode): ptr TNimNode =
|
||||
var discr = getDiscriminant(aa, n)
|
||||
if discr <% n.len:
|
||||
result = n.sons[discr]
|
||||
if result == nil: result = n.sons[n.len]
|
||||
# n.sons[n.len] contains the ``else`` part (but may be nil)
|
||||
else:
|
||||
result = n.sons[n.len]
|
||||
|
||||
when defined(boehmgc):
|
||||
const
|
||||
boehmLib = "/opt/lib/libgc.so"
|
||||
|
||||
proc boehmGC_disable {.importc: "GC_disable", dynlib: boehmLib.}
|
||||
proc boehmGC_enable {.importc: "GC_enable", dynlib: boehmLib.}
|
||||
proc boehmGCincremental {.
|
||||
importc: "GC_enable_incremental", dynlib: boehmLib.}
|
||||
proc boehmGCfullCollect {.importc: "GC_gcollect", dynlib: boehmLib.}
|
||||
proc boehmAlloc(size: int): pointer {.
|
||||
importc: "GC_malloc", dynlib: boehmLib.}
|
||||
proc boehmAllocAtomic(size: int): pointer {.
|
||||
importc: "GC_malloc_atomic", dynlib: boehmLib.}
|
||||
proc boehmRealloc(p: pointer, size: int): pointer {.
|
||||
importc: "GC_realloc", dynlib: boehmLib.}
|
||||
proc boehmDealloc(p: pointer) {.importc: "GC_free", dynlib: boehmLib.}
|
||||
|
||||
include cellsets
|
||||
|
||||
when defined(boehmGC):
|
||||
proc initGC() = nil
|
||||
|
||||
#boehmGCincremental()
|
||||
|
||||
proc GC_disable() = boehmGC_disable()
|
||||
proc GC_enable() = boehmGC_enable()
|
||||
proc GC_fullCollect() = boehmGCfullCollect()
|
||||
proc GC_setStrategy(strategy: TGC_Strategy) = nil
|
||||
proc GC_enableMarkAndSweep() = nil
|
||||
proc GC_disableMarkAndSweep() = nil
|
||||
proc GC_getStatistics(): string = return ""
|
||||
|
||||
proc getOccupiedMem(): int = return -1
|
||||
proc getFreeMem(): int = return -1
|
||||
proc getTotalMem(): int = return -1
|
||||
|
||||
proc growObj(old: pointer, newsize: int): pointer {.inline.} =
|
||||
result = boehmRealloc(old, newsize)
|
||||
proc newObj(size: int): pointer {.compilerproc.} =
|
||||
result = boehmAlloc(size)
|
||||
proc newSeq(baseSize, len: int): pointer {.compilerproc.} =
|
||||
# XXX: overflow checks!
|
||||
result = newObj(len * baseSize + GenericSeqSize)
|
||||
cast[PGenericSeq](result).len = len
|
||||
cast[PGenericSeq](result).space = len
|
||||
|
||||
proc setStackBottom(theStackBottom: pointer) {.compilerproc.} = nil
|
||||
proc nimGCref(p: pointer) {.compilerproc, inline.} = nil
|
||||
proc nimGCunref(p: pointer) {.compilerproc, inline.} = nil
|
||||
|
||||
proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerproc, inline.} =
|
||||
dest^ = src
|
||||
proc asgnRef(dest: ppointer, src: pointer) {.compilerproc, inline.} =
|
||||
dest^ = src
|
||||
proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerproc, inline.} =
|
||||
dest^ = src
|
||||
|
||||
elif not defined(nogc):
|
||||
include gc
|
||||
|
||||
include sysstr
|
||||
include assign
|
||||
include dyncalls
|
||||
include repr
|
||||
|
||||
# we have to implement it here after gentostr for the cstrToNimStrDummy proc
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
# Memory handling for small objects
|
||||
|
||||
const
|
||||
minRequestSize = 2 * sizeof(pointer) # minimal block is 16 bytes
|
||||
pageSize = 1024 * sizeof(int)
|
||||
pageBits = pageSize div minRequestSize
|
||||
pageMask = pageSize-1
|
||||
|
||||
bitarraySize = pageBits div (sizeof(int)*8)
|
||||
dataSize = pageSize - (bitarraySize+6) * sizeof(pointer)
|
||||
|
||||
type
|
||||
TMinRequest {.final.} = object
|
||||
next, prev: ptr TMinRequest # stores next free bit
|
||||
|
||||
TChunk {.pure.} = object # a chunk manages at least a page
|
||||
size: int # lowest bit signals if it is a small chunk (0) or
|
||||
# a big chunk (1)
|
||||
typ: PNimType
|
||||
next, prev: ptr TChunk
|
||||
nextOfSameType: ptr TChunk
|
||||
|
||||
TSmallChunk = object of TChunk ## manages pageSize bytes for a type and a
|
||||
## fixed size
|
||||
free: int ## index of first free bit
|
||||
bits: array[0..bitarraySize-1, int]
|
||||
data: array[0..dataSize div minRequestSize - 1, TMinRequest]
|
||||
|
||||
PSmallChunk = ptr TSmallChunk
|
||||
|
||||
assert(sizeof(TSmallChunk) == pageSize)
|
||||
|
||||
proc getNewChunk(size: int, typ: PNimType): PSmallChunk =
|
||||
result = cast[PSmallChunk](getPages(1))
|
||||
result.size = PageSize
|
||||
result.typ = typ
|
||||
result.next = chunkHead
|
||||
result.prev = nil
|
||||
chunkHead.prev = result
|
||||
chunkHead = result.next
|
||||
result.nextOfSameType = cast[PSmallChunk](typ.chunk)
|
||||
typ.chunk = result
|
||||
result.free = addr(result.data[0])
|
||||
result.data[0].next = addr(result.data[1])
|
||||
result.data[0].prev = nil
|
||||
result.data[high(result.data)].next = nil
|
||||
result.data[high(result.data)].prev = addr(result.data[high(result.data)-1])
|
||||
for i in 1..high(result.data)-1:
|
||||
result.data[i].next = addr(result.data[i+1])
|
||||
result.data[i].prev = addr(result.data[i-1])
|
||||
|
||||
proc newSmallObj(size: int, typ: PNimType): pointer =
|
||||
var chunk = cast[PSmallChunk](typ.chunk)
|
||||
if chunk == nil or chunk.free <= 0:
|
||||
if chunk.free < 0: GC_collect()
|
||||
chunk = getNewChunk(size, typ)
|
||||
chunk.nextOfSameType = typ.chunk
|
||||
typ.chunk = chunk
|
||||
var idx = chunk.free
|
||||
setBit(chunk.bits[idx /% bitarraySize], idx %% bitarraySize)
|
||||
result = cast[pointer](cast[TAddress](addr(chunk.data)) +
|
||||
minRequestSize * idx)
|
||||
var res = cast[PMinRequest](result)
|
||||
chunk.free = res.next
|
||||
res.next
|
||||
|
||||
proc freeObj(obj: pointer) =
|
||||
var chunk = cast[PChunk](cast[TAddress(obj) and not pageMask)
|
||||
if size and 1 == 0: # small chunk
|
||||
var idx = (cast[TAddress](obj) shr pageShift) div minRequestSize
|
||||
resetBit(chunk.bits[idx /% bitarraySize], idx %% bitarraySize)
|
||||
|
||||
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
## This module provides support to handle the Unicode UTF-8 encoding.
|
||||
|
||||
{.deadCodeElim: on.}
|
||||
|
||||
type
|
||||
TRune* = int ## type that can hold any Unicode character
|
||||
TRune16* = int16 ## 16 bit Unicode character
|
||||
|
|
|
|||
27
nim/ast.pas
27
nim/ast.pas
|
|
@ -93,12 +93,12 @@ type
|
|||
nkTypeDef, nkYieldStmt, nkTryStmt, nkFinally,
|
||||
nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt,
|
||||
nkBlockStmt, nkDiscardStmt, nkStmtList, nkImportStmt,
|
||||
nkFromStmt, nkImportAs, nkIncludeStmt, nkAccessStmt,
|
||||
nkCommentStmt, nkStmtListExpr, nkBlockExpr, nkStmtListType,
|
||||
nkBlockType, nkVm, nkTypeOfExpr, nkObjectTy,
|
||||
nkTupleTy, nkRecList, nkRecCase, nkRecWhen,
|
||||
nkRefTy, nkPtrTy, nkVarTy, nkProcTy,
|
||||
nkEnumTy, nkEnumFieldDef, nkReturnToken);
|
||||
nkFromStmt, nkImportAs, nkIncludeStmt, nkCommentStmt,
|
||||
nkStmtListExpr, nkBlockExpr, nkStmtListType, nkBlockType,
|
||||
nkVm, nkTypeOfExpr, nkObjectTy, nkTupleTy,
|
||||
nkRecList, nkRecCase, nkRecWhen, nkRefTy,
|
||||
nkPtrTy, nkVarTy, nkProcTy, nkEnumTy,
|
||||
nkEnumFieldDef, nkReturnToken);
|
||||
TNodeKinds = set of TNodeKind;
|
||||
const
|
||||
NodeKindToStr: array [TNodeKind] of string = (
|
||||
|
|
@ -128,12 +128,12 @@ const
|
|||
'nkTypeDef', 'nkYieldStmt', 'nkTryStmt', 'nkFinally',
|
||||
'nkRaiseStmt', 'nkReturnStmt', 'nkBreakStmt', 'nkContinueStmt',
|
||||
'nkBlockStmt', 'nkDiscardStmt', 'nkStmtList', 'nkImportStmt',
|
||||
'nkFromStmt', 'nkImportAs', 'nkIncludeStmt', 'nkAccessStmt',
|
||||
'nkCommentStmt', 'nkStmtListExpr', 'nkBlockExpr', 'nkStmtListType',
|
||||
'nkBlockType', 'nkVm', 'nkTypeOfExpr', 'nkObjectTy',
|
||||
'nkTupleTy', 'nkRecList', 'nkRecCase', 'nkRecWhen',
|
||||
'nkRefTy', 'nkPtrTy', 'nkVarTy', 'nkProcTy',
|
||||
'nkEnumTy', 'nkEnumFieldDef', 'nkReturnToken');
|
||||
'nkFromStmt', 'nkImportAs', 'nkIncludeStmt', 'nkCommentStmt',
|
||||
'nkStmtListExpr', 'nkBlockExpr', 'nkStmtListType', 'nkBlockType',
|
||||
'nkVm', 'nkTypeOfExpr', 'nkObjectTy', 'nkTupleTy',
|
||||
'nkRecList', 'nkRecCase', 'nkRecWhen', 'nkRefTy',
|
||||
'nkPtrTy', 'nkVarTy', 'nkProcTy', 'nkEnumTy',
|
||||
'nkEnumFieldDef', 'nkReturnToken');
|
||||
type
|
||||
TSymFlag = (
|
||||
sfUsed, sfStar, sfMinus, sfInInterface,
|
||||
|
|
@ -327,7 +327,8 @@ type
|
|||
);
|
||||
|
||||
TLocFlag = (
|
||||
lfIndirect, // backend introduced a pointer
|
||||
lfIndirect, // backend introduced a pointer
|
||||
lfParamCopy, // backend introduced a parameter copy (LLVM)
|
||||
lfNoDeepCopy, // no need for a deep copy
|
||||
lfNoDecl, // do not declare it in C
|
||||
lfDynamicLib, // link symbol to dynamic library
|
||||
|
|
|
|||
|
|
@ -1233,16 +1233,14 @@ begin
|
|||
app(r, '.Sup');
|
||||
s := skipGeneric(s.sons[0]);
|
||||
end;
|
||||
appf(p.s[cpsStmts], '$1.m_type = $2;$n',
|
||||
[r, genTypeInfo(p.module, t)])
|
||||
appf(p.s[cpsStmts], '$1.m_type = $2;$n', [r, genTypeInfo(p.module, t)])
|
||||
end;
|
||||
frEmbedded: begin
|
||||
// worst case for performance:
|
||||
useMagic(p.module, 'objectInit');
|
||||
if takeAddr then r := addrLoc(a)
|
||||
else r := rdLoc(a);
|
||||
appf(p.s[cpsStmts], 'objectInit($1, $2);$n',
|
||||
[r, genTypeInfo(p.module, t)])
|
||||
appf(p.s[cpsStmts], 'objectInit($1, $2);$n', [r, genTypeInfo(p.module, t)])
|
||||
end
|
||||
end
|
||||
end;
|
||||
|
|
@ -1256,9 +1254,15 @@ begin
|
|||
refType := skipVarGenericRange(e.sons[1].typ);
|
||||
InitLocExpr(p, e.sons[1], a);
|
||||
initLoc(b, locExpr, a.t, OnHeap);
|
||||
b.r := ropef('($1) newObj($2, sizeof($3))',
|
||||
[getTypeDesc(p.module, reftype), genTypeInfo(p.module, refType),
|
||||
getTypeDesc(p.module, skipGenericRange(reftype.sons[0]))]);
|
||||
|
||||
if optBoehmGC in gGlobalOptions then
|
||||
b.r := ropef('($1) newObj(sizeof($2))',
|
||||
[getTypeDesc(p.module, reftype),
|
||||
getTypeDesc(p.module, skipGenericRange(reftype.sons[0]))])
|
||||
else
|
||||
b.r := ropef('($1) newObj($2, sizeof($3))',
|
||||
[getTypeDesc(p.module, reftype), genTypeInfo(p.module, refType),
|
||||
getTypeDesc(p.module, skipGenericRange(reftype.sons[0]))]);
|
||||
genAssignment(p, a, b, {@set}[]);
|
||||
// set the object type:
|
||||
bt := skipGenericRange(refType.sons[0]);
|
||||
|
|
@ -1275,10 +1279,16 @@ begin
|
|||
InitLocExpr(p, e.sons[1], a);
|
||||
InitLocExpr(p, e.sons[2], b);
|
||||
initLoc(c, locExpr, a.t, OnHeap);
|
||||
c.r := ropef('($1) newSeq($2, $3)',
|
||||
[getTypeDesc(p.module, seqtype),
|
||||
genTypeInfo(p.module, seqType),
|
||||
rdLoc(b)]);
|
||||
if optBoehmGC in gGlobalOptions then
|
||||
c.r := ropef('($1) newSeq(sizeof($2), $3)',
|
||||
[getTypeDesc(p.module, seqtype),
|
||||
getTypeDesc(p.module, skipGenericRange(seqtype.sons[0])),
|
||||
rdLoc(b)])
|
||||
else
|
||||
c.r := ropef('($1) newSeq($2, $3)',
|
||||
[getTypeDesc(p.module, seqtype),
|
||||
genTypeInfo(p.module, seqType),
|
||||
rdLoc(b)]);
|
||||
genAssignment(p, a, c, {@set}[]);
|
||||
end;
|
||||
|
||||
|
|
@ -1324,7 +1334,8 @@ begin
|
|||
refType := skipVarGenericRange(e.sons[1].typ);
|
||||
InitLocExpr(p, e.sons[1], a);
|
||||
|
||||
// This is a little hack:
|
||||
// This is a little hack:
|
||||
// XXX this is also a bug, if the finalizer expression produces side-effects
|
||||
oldModule := p.module;
|
||||
p.module := gmti;
|
||||
InitLocExpr(p, e.sons[2], f);
|
||||
|
|
@ -2290,7 +2301,7 @@ begin
|
|||
len := sonsLen(n);
|
||||
result := toRope('{'+'');
|
||||
for i := 0 to len - 2 do
|
||||
app(result, ropef('$1,$n', [genConstExpr(p, n.sons[i])]));
|
||||
appf(result, '$1,$n', [genConstExpr(p, n.sons[i])]);
|
||||
if len > 0 then app(result, genConstExpr(p, n.sons[len-1]));
|
||||
app(result, '}' + tnl)
|
||||
end;
|
||||
|
|
@ -2299,6 +2310,7 @@ function genConstExpr(p: BProc; n: PNode): PRope;
|
|||
var
|
||||
trans: PNode;
|
||||
cs: TBitSet;
|
||||
d: TLoc;
|
||||
begin
|
||||
case n.Kind of
|
||||
nkHiddenStdConv, nkHiddenSubConv: result := genConstExpr(p, n.sons[1]);
|
||||
|
|
@ -2317,7 +2329,10 @@ begin
|
|||
trans := n;
|
||||
result := genConstSimpleList(p, trans);
|
||||
end
|
||||
else
|
||||
result := genLiteral(p, n)
|
||||
else begin
|
||||
// result := genLiteral(p, n)
|
||||
initLocExpr(p, n, d);
|
||||
result := rdLoc(d)
|
||||
end
|
||||
end
|
||||
end;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
//
|
||||
// The Nimrod Compiler
|
||||
// (c) Copyright 2008 Andreas Rumpf
|
||||
// (c) Copyright 2009 Andreas Rumpf
|
||||
//
|
||||
// See the file "copying.txt", included in this
|
||||
// distribution, for details about the copyright.
|
||||
|
|
@ -18,25 +18,41 @@ begin
|
|||
line := toLinenumber(t.info); // BUGFIX
|
||||
if line < 0 then line := 0; // negative numbers are not allowed in #line
|
||||
if optLineDir in p.Options then
|
||||
appf(p.s[cpsStmts], '#line $2 "$1"$n',
|
||||
appff(p.s[cpsStmts],
|
||||
'#line $2 "$1"$n',
|
||||
'; line $2 "$1"$n',
|
||||
[toRope(toFilename(t.info)), toRope(line)]);
|
||||
if ([optStackTrace, optEndb] * p.Options = [optStackTrace, optEndb]) and
|
||||
((p.prc = nil) or not (sfPure in p.prc.flags)) then begin
|
||||
useMagic(p.module, 'endb'); // new: endb support
|
||||
appf(p.s[cpsStmts], 'endb($1);$n', [toRope(line)])
|
||||
appff(p.s[cpsStmts], 'endb($1);$n',
|
||||
'call void @endb(%NI $1)$n',
|
||||
[toRope(line)])
|
||||
end
|
||||
else if ([optLineTrace, optStackTrace] * p.Options =
|
||||
[optLineTrace, optStackTrace]) and ((p.prc = nil) or
|
||||
not (sfPure in p.prc.flags)) then
|
||||
appf(p.s[cpsStmts], 'F.line = $1;$n', [toRope(line)])
|
||||
not (sfPure in p.prc.flags)) then begin
|
||||
inc(p.labels);
|
||||
appff(p.s[cpsStmts], 'F.line = $1;$n',
|
||||
'%LOC$2 = getelementptr %TF %F, %NI 2$n' +
|
||||
'store %NI $1, %NI* %LOC$2$n',
|
||||
[toRope(line), toRope(p.labels)])
|
||||
end
|
||||
end;
|
||||
|
||||
procedure finishTryStmt(p: BProc; howMany: int);
|
||||
var
|
||||
i: int;
|
||||
begin
|
||||
for i := 1 to howMany do
|
||||
app(p.s[cpsStmts], 'excHandler = excHandler->prev;' + tnl);
|
||||
for i := 1 to howMany do begin
|
||||
inc(p.labels, 3);
|
||||
appff(p.s[cpsStmts], 'excHandler = excHandler->prev;$n',
|
||||
'%LOC$1 = load %TSafePoint** @excHandler$n' +
|
||||
'%LOC$2 = getelementptr %TSafePoint* %LOC$1, %NI 0$n' +
|
||||
'%LOC$3 = load %TSafePoint** %LOC$2$n' +
|
||||
'store %TSafePoint* %LOC$3, %TSafePoint** @excHandler$n',
|
||||
[toRope(p.labels), toRope(p.labels-1), toRope(p.labels-2)]);
|
||||
end
|
||||
end;
|
||||
|
||||
procedure genReturnStmt(p: BProc; t: PNode);
|
||||
|
|
@ -45,7 +61,7 @@ begin
|
|||
genLineDir(p, t);
|
||||
if (t.sons[0] <> nil) then genStmts(p, t.sons[0]);
|
||||
finishTryStmt(p, p.nestedTryStmts);
|
||||
app(p.s[cpsStmts], 'goto BeforeRet;' + tnl)
|
||||
appff(p.s[cpsStmts], 'goto BeforeRet;$n', 'br label %BeforeRet$n', [])
|
||||
end;
|
||||
|
||||
procedure initVariable(p: BProc; v: PSym);
|
||||
|
|
@ -53,11 +69,29 @@ begin
|
|||
if containsGarbageCollectedRef(v.typ) or (v.ast = nil) then
|
||||
// Language change: always initialize variables if v.ast == nil!
|
||||
if not (skipVarGenericRange(v.typ).Kind in [tyArray, tyArrayConstr, tySet,
|
||||
tyTuple, tyObject]) then
|
||||
appf(p.s[cpsStmts], '$1 = 0;$n', [rdLoc(v.loc)])
|
||||
else
|
||||
appf(p.s[cpsStmts], 'memset((void*)$1, 0, sizeof($2));$n',
|
||||
[addrLoc(v.loc), rdLoc(v.loc)])
|
||||
tyTuple, tyObject]) then begin
|
||||
if gCmd = cmdCompileToLLVM then
|
||||
appf(p.s[cpsStmts], 'store $2 0, $2* $1$n',
|
||||
[addrLoc(v.loc), getTypeDesc(p.module, v.loc.t)])
|
||||
else
|
||||
appf(p.s[cpsStmts], '$1 = 0;$n', [rdLoc(v.loc)])
|
||||
end
|
||||
else begin
|
||||
if gCmd = cmdCompileToLLVM then begin
|
||||
app(p.module.s[cfsProcHeaders],
|
||||
'declare void @llvm.memset.i32(i8*, i8, i32, i32)' + tnl);
|
||||
inc(p.labels, 2);
|
||||
appf(p.s[cpsStmts],
|
||||
'%LOC$3 = getelementptr $2* null, %NI 1$n' +
|
||||
'%LOC$4 = cast $2* %LOC$3 to i32$n' +
|
||||
'call void @llvm.memset.i32(i8* $1, i8 0, i32 %LOC$4, i32 0)$n',
|
||||
[addrLoc(v.loc), getTypeDesc(p.module, v.loc.t),
|
||||
toRope(p.labels), toRope(p.labels-1)])
|
||||
end
|
||||
else
|
||||
appf(p.s[cpsStmts], 'memset((void*)$1, 0, sizeof($2));$n',
|
||||
[addrLoc(v.loc), rdLoc(v.loc)])
|
||||
end
|
||||
end;
|
||||
|
||||
procedure genVarStmt(p: BProc; n: PNode);
|
||||
|
|
@ -73,7 +107,7 @@ begin
|
|||
assert(a.sons[0].kind = nkSym);
|
||||
v := a.sons[0].sym;
|
||||
if sfGlobal in v.flags then
|
||||
assignGlobalVar(p.module, v)
|
||||
assignGlobalVar(p, v)
|
||||
else begin
|
||||
assignLocalVar(p, v);
|
||||
initVariable(p, v) // XXX: this is not required if a.sons[2] != nil,
|
||||
|
|
@ -140,10 +174,14 @@ begin
|
|||
nkElifBranch: begin
|
||||
initLocExpr(p, it.sons[0], a);
|
||||
Lelse := getLabel(p);
|
||||
appf(p.s[cpsStmts], 'if (!$1) goto $2;$n', [rdLoc(a), Lelse]);
|
||||
inc(p.labels);
|
||||
appff(p.s[cpsStmts], 'if (!$1) goto $2;$n',
|
||||
'br i1 $1, label %LOC$3, label %$2$n' +
|
||||
'LOC$3: $n',
|
||||
[rdLoc(a), Lelse, toRope(p.labels)]);
|
||||
genStmts(p, it.sons[1]);
|
||||
if sonsLen(n) > 1 then
|
||||
appf(p.s[cpsStmts], 'goto $1;$n', [Lend]);
|
||||
appff(p.s[cpsStmts], 'goto $1;$n', 'br label %$1$n', [Lend]);
|
||||
fixLabel(p, Lelse);
|
||||
end;
|
||||
nkElse: begin
|
||||
|
|
@ -857,7 +895,7 @@ begin
|
|||
nkCaseStmt: genCaseStmt(p, t);
|
||||
nkReturnStmt: genReturnStmt(p, t);
|
||||
nkBreakStmt: genBreakStmt(p, t);
|
||||
nkCall: begin
|
||||
nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand: begin
|
||||
genLineDir(p, t);
|
||||
initLocExpr(p, t, a);
|
||||
end;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
//
|
||||
// The Nimrod Compiler
|
||||
// (c) Copyright 2008 Andreas Rumpf
|
||||
// (c) Copyright 2009 Andreas Rumpf
|
||||
//
|
||||
// See the file "copying.txt", included in this
|
||||
// distribution, for details about the copyright.
|
||||
|
|
@ -45,7 +45,19 @@ function mangleName(s: PSym): PRope;
|
|||
begin
|
||||
result := s.loc.r;
|
||||
if result = nil then begin
|
||||
result := toRope(mangle(s.name.s));
|
||||
if gCmd = cmdCompileToLLVM then begin
|
||||
case s.kind of
|
||||
skProc, skConverter, skConst: result := toRope('@'+'');
|
||||
skVar: begin
|
||||
if (sfGlobal in s.flags) then result := toRope('@'+'')
|
||||
else result := toRope('%'+'');
|
||||
end;
|
||||
skForVar, skTemp, skParam, skType, skEnumField, skModule:
|
||||
result := toRope('%'+'');
|
||||
else InternalError(s.info, 'mangleName');
|
||||
end;
|
||||
end;
|
||||
app(result, toRope(mangle(s.name.s)));
|
||||
app(result, '_'+'');
|
||||
app(result, toRope(s.id));
|
||||
if optGenMapping in gGlobalOptions then
|
||||
|
|
@ -58,15 +70,33 @@ end;
|
|||
|
||||
function getTypeName(typ: PType): PRope;
|
||||
begin
|
||||
if (typ.sym <> nil) and ([sfImportc, sfExportc] * typ.sym.flags <> []) then
|
||||
if (typ.sym <> nil) and ([sfImportc, sfExportc] * typ.sym.flags <> [])
|
||||
and (gCmd <> cmdCompileToLLVM) then
|
||||
result := typ.sym.loc.r
|
||||
else begin
|
||||
if typ.loc.r = nil then typ.loc.r := con('TY', toRope(typ.id));
|
||||
if typ.loc.r = nil then
|
||||
typ.loc.r := ropeff('TY$1', '%TY$1', [toRope(typ.id)]);
|
||||
result := typ.loc.r
|
||||
end;
|
||||
if result = nil then InternalError('getTypeName: ' + typeKindToStr[typ.kind]);
|
||||
end;
|
||||
|
||||
// ----------------------------- other helpers ----------------------------
|
||||
(*
|
||||
function getSizeof(m: BModule; var labels: int;
|
||||
var body: PRope; typ: PType): PRope;
|
||||
begin
|
||||
if (gCmd <> cmdCompileToLLVM) then
|
||||
result := ropef('sizeof($1)', getTypeDesc(m, typ))
|
||||
else begin
|
||||
inc(labels, 2);
|
||||
result := ropef('%UOC$1', [toRope(labels)]);
|
||||
appf(body, '%UOC$1 = getelementptr $3* null, %NI 1$n' +
|
||||
'$2 = cast $3* %UOC$1 to i32$n',
|
||||
[toRope(labels-1), result, getTypeDesc(m, typ)]);
|
||||
end
|
||||
end; *)
|
||||
|
||||
// ------------------------------ C type generator ------------------------
|
||||
|
||||
function mapType(typ: PType): TCTypeKind;
|
||||
|
|
@ -104,10 +134,6 @@ begin
|
|||
tyPtr, tyVar, tyRef: begin
|
||||
case typ.sons[0].kind of
|
||||
tyOpenArray, tyArrayConstr, tyArray: result := ctArray;
|
||||
(*tySet: begin
|
||||
if mapType(typ.sons[0]) = ctArray then result := ctArray
|
||||
else result := ctPtr
|
||||
end*)
|
||||
else result := ctPtr
|
||||
end
|
||||
end;
|
||||
|
|
@ -155,6 +181,10 @@ const
|
|||
// but one can //define it to what you want so there will no problem
|
||||
'N_INLINE', 'N_NOINLINE', 'N_FASTCALL', 'N_CLOSURE', 'N_NOCONV');
|
||||
|
||||
CallingConvToStrLLVM: array [TCallingConvention] of string = ('fastcc $1',
|
||||
'stdcall $1', 'ccc $1', 'safecall $1', 'syscall $1',
|
||||
'$1 alwaysinline', '$1 noinline', 'fastcc $1', 'ccc $1', '$1');
|
||||
|
||||
function CacheGetType(const tab: TIdTable; key: PType): PRope;
|
||||
begin
|
||||
// returns nil if we need to declare this type
|
||||
|
|
@ -165,7 +195,13 @@ end;
|
|||
|
||||
function getTempName(): PRope;
|
||||
begin
|
||||
result := con('TMP', toRope(gId));
|
||||
result := ropeff('TMP$1', '%TMP$1', [toRope(gId)]);
|
||||
inc(gId);
|
||||
end;
|
||||
|
||||
function getGlobalTempName(): PRope;
|
||||
begin
|
||||
result := ropeff('TMP$1', '@TMP$1', [toRope(gId)]);
|
||||
inc(gId);
|
||||
end;
|
||||
|
||||
|
|
@ -194,7 +230,8 @@ end;
|
|||
|
||||
procedure fillResult(param: PSym);
|
||||
begin
|
||||
fillLoc(param.loc, locParam, param.typ, toRope('Result'), OnStack);
|
||||
fillLoc(param.loc, locParam, param.typ, ropeff('Result', '%Result', []),
|
||||
OnStack);
|
||||
if (mapType(param.typ) <> ctArray) and IsInvalidReturnType(param.typ) then
|
||||
begin
|
||||
include(param.loc.flags, lfIndirect);
|
||||
|
|
@ -232,7 +269,7 @@ begin
|
|||
if arr.kind = tyVar then arr := arr.sons[0];
|
||||
j := 0;
|
||||
while arr.Kind = tyOpenArray do begin // need to pass hidden parameter:
|
||||
appf(params, ', NI $1Len$2', [param.loc.r, toRope(j)]);
|
||||
appff(params, ', NI $1Len$2', ', @NI $1Len$2', [param.loc.r, toRope(j)]);
|
||||
inc(j);
|
||||
arr := arr.sons[0]
|
||||
end;
|
||||
|
|
@ -241,8 +278,9 @@ begin
|
|||
if (t.sons[0] <> nil) and isInvalidReturnType(t.sons[0]) then begin
|
||||
if params <> nil then app(params, ', ');
|
||||
app(params, getTypeDescAux(m, t.sons[0], check));
|
||||
if mapType(t.sons[0]) <> ctArray then app(params, '*'+'');
|
||||
app(params, ' Result');
|
||||
if (mapType(t.sons[0]) <> ctArray) or (gCmd = cmdCompileToLLVM) then
|
||||
app(params, '*'+'');
|
||||
appff(params, ' Result', ' @Result', []);
|
||||
end;
|
||||
if t.callConv = ccClosure then begin
|
||||
if params <> nil then app(params, ', ');
|
||||
|
|
@ -252,7 +290,7 @@ begin
|
|||
if params <> nil then app(params, ', ');
|
||||
app(params, '...')
|
||||
end;
|
||||
if params = nil then
|
||||
if (params = nil) and (gCmd <> cmdCompileToLLVM) then
|
||||
app(params, 'void)')
|
||||
else
|
||||
app(params, ')'+'');
|
||||
|
|
@ -621,7 +659,7 @@ begin
|
|||
end
|
||||
end;
|
||||
|
||||
function getTypeDesc(m: BModule; typ: PType): PRope;
|
||||
function getTypeDesc(m: BModule; typ: PType): PRope; overload;
|
||||
var
|
||||
check: TIntSet;
|
||||
begin
|
||||
|
|
@ -629,6 +667,19 @@ begin
|
|||
result := getTypeDescAux(m, typ, check);
|
||||
end;
|
||||
|
||||
function getTypeDesc(m: BModule; const magic: string): PRope; overload;
|
||||
var
|
||||
sym: PSym;
|
||||
begin
|
||||
sym := magicsys.getCompilerProc(magic);
|
||||
if sym <> nil then
|
||||
result := getTypeDesc(m, sym.typ)
|
||||
else begin
|
||||
rawMessage(errSystemNeeds, magic);
|
||||
result := nil
|
||||
end
|
||||
end;
|
||||
|
||||
procedure finishTypeDescriptions(m: BModule);
|
||||
var
|
||||
i: int;
|
||||
|
|
@ -1012,4 +1063,4 @@ begin
|
|||
end
|
||||
end
|
||||
end;
|
||||
*)
|
||||
*)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ uses
|
|||
|
||||
function toCChar(c: Char): string;
|
||||
function makeCString(const s: string): PRope;
|
||||
function makeLLVMString(const s: string): PRope;
|
||||
|
||||
function TableGetType(const tab: TIdTable; key: PType): PObject;
|
||||
function GetUniqueType(key: PType): PType;
|
||||
|
|
@ -154,6 +155,33 @@ begin
|
|||
app(result, toRope(res));
|
||||
end;
|
||||
|
||||
function makeLLVMString(const s: string): PRope;
|
||||
const
|
||||
MaxLineLength = 64;
|
||||
var
|
||||
i: int;
|
||||
res: string;
|
||||
begin
|
||||
result := nil;
|
||||
res := 'c"';
|
||||
for i := strStart to length(s)+strStart-1 do begin
|
||||
if (i-strStart+1) mod MaxLineLength = 0 then begin
|
||||
app(result, toRope(res));
|
||||
setLength(res, 0);
|
||||
end;
|
||||
case s[i] of
|
||||
#0..#31, #128..#255, '"', '\': begin
|
||||
addChar(res, '\');
|
||||
add(res, toHex(ord(s[i]), 2));
|
||||
end
|
||||
else
|
||||
addChar(res, s[i])
|
||||
end;
|
||||
end;
|
||||
add(res, '\00"');
|
||||
app(result, toRope(res));
|
||||
end;
|
||||
|
||||
begin
|
||||
InitTypeTables();
|
||||
end.
|
||||
|
|
|
|||
482
nim/cgen.pas
482
nim/cgen.pas
|
|
@ -37,9 +37,9 @@ type
|
|||
// reasons
|
||||
cfsFieldInfo, // section for field information
|
||||
cfsTypeInfo, // section for type information
|
||||
cfsProcHeaders, // section for C procs prototypes
|
||||
cfsData, // section for C constant data
|
||||
cfsVars, // section for C variable declarations
|
||||
cfsProcHeaders, // section for C procs prototypes
|
||||
cfsProcs, // section for C procs that are not inline
|
||||
cfsTypeInit1, // section 1 for declarations of type information
|
||||
cfsTypeInit2, // section 2 for initialization of type information
|
||||
|
|
@ -120,6 +120,7 @@ type
|
|||
forwardedProcs: TSymSeq; // keep forwarded procs here
|
||||
typeNodes, nimTypes: int;// used for type info generation
|
||||
typeNodesName, nimTypesName: PRope; // used for type info generation
|
||||
labels: natural; // for generating unique module-scope names
|
||||
end;
|
||||
|
||||
var
|
||||
|
|
@ -132,6 +133,24 @@ var
|
|||
gForwardedProcsCounter: int = 0;
|
||||
gmti: BModule; // generated type info: no need to initialize: defaults fit
|
||||
|
||||
function ropeff(const cformat, llvmformat: string;
|
||||
const args: array of PRope): PRope;
|
||||
begin
|
||||
if gCmd = cmdCompileToLLVM then
|
||||
result := ropef(llvmformat, args)
|
||||
else
|
||||
result := ropef(cformat, args)
|
||||
end;
|
||||
|
||||
procedure appff(var dest: PRope; const cformat, llvmformat: string;
|
||||
const args: array of PRope);
|
||||
begin
|
||||
if gCmd = cmdCompileToLLVM then
|
||||
appf(dest, llvmformat, args)
|
||||
else
|
||||
appf(dest, cformat, args);
|
||||
end;
|
||||
|
||||
procedure addForwardedProc(m: BModule; prc: PSym);
|
||||
var
|
||||
L: int;
|
||||
|
|
@ -240,8 +259,12 @@ end;
|
|||
procedure getTemp(p: BProc; t: PType; var result: TLoc);
|
||||
begin
|
||||
inc(p.labels);
|
||||
result.r := con('LOC', toRope(p.labels));
|
||||
appf(p.s[cpsLocals], '$1 $2;$n', [getTypeDesc(p.module, t), result.r]);
|
||||
if gCmd = cmdCompileToLLVM then
|
||||
result.r := con('%LOC', toRope(p.labels))
|
||||
else begin
|
||||
result.r := con('LOC', toRope(p.labels));
|
||||
appf(p.s[cpsLocals], '$1 $2;$n', [getTypeDesc(p.module, t), result.r]);
|
||||
end;
|
||||
result.k := locTemp;
|
||||
result.a := -1;
|
||||
result.t := getUniqueType(t);
|
||||
|
|
@ -251,6 +274,86 @@ end;
|
|||
|
||||
// -------------------------- Variable manager ----------------------------
|
||||
|
||||
function cstringLit(p: BProc; var r: PRope; const s: string): PRope; overload;
|
||||
begin
|
||||
if gCmd = cmdCompileToLLVM then begin
|
||||
inc(p.module.labels);
|
||||
inc(p.labels);
|
||||
result := ropef('%LOC$1', [toRope(p.labels)]);
|
||||
appf(p.module.s[cfsData], '@C$1 = private constant [$2 x i8] $3$n', [
|
||||
toRope(p.module.labels), toRope(length(s)), makeLLVMString(s)]);
|
||||
appf(r, '$1 = getelementptr [$2 x i8]* @C$3, %NI 0, %NI 0$n',
|
||||
[result, toRope(length(s)), toRope(p.module.labels)]);
|
||||
end
|
||||
else
|
||||
result := makeCString(s)
|
||||
end;
|
||||
|
||||
function cstringLit(m: BModule; var r: PRope; const s: string): PRope; overload;
|
||||
begin
|
||||
if gCmd = cmdCompileToLLVM then begin
|
||||
inc(m.labels, 2);
|
||||
result := ropef('%MOC$1', [toRope(m.labels-1)]);
|
||||
appf(m.s[cfsData], '@MOC$1 = private constant [$2 x i8] $3$n', [
|
||||
toRope(m.labels), toRope(length(s)), makeLLVMString(s)]);
|
||||
appf(r, '$1 = getelementptr [$2 x i8]* @MOC$3, %NI 0, %NI 0$n',
|
||||
[result, toRope(length(s)), toRope(m.labels)]);
|
||||
end
|
||||
else
|
||||
result := makeCString(s)
|
||||
end;
|
||||
|
||||
procedure allocParam(p: BProc; s: PSym);
|
||||
var
|
||||
tmp: PRope;
|
||||
begin
|
||||
assert(s.kind = skParam);
|
||||
if not (lfParamCopy in s.loc.flags) then begin
|
||||
inc(p.labels);
|
||||
tmp := con('%LOC', toRope(p.labels));
|
||||
include(s.loc.flags, lfParamCopy);
|
||||
include(s.loc.flags, lfIndirect);
|
||||
appf(p.s[cpsInit],
|
||||
'$1 = alloca $3$n' +
|
||||
'store $3 $2, $3* $1$n', [tmp, s.loc.r, getTypeDesc(p.module, s.loc.t)]);
|
||||
s.loc.r := tmp
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure localDebugInfo(p: BProc; s: PSym);
|
||||
var
|
||||
name, a: PRope;
|
||||
begin
|
||||
if [optStackTrace, optEndb] * p.options <> [optStackTrace, optEndb] then exit;
|
||||
if gCmd = cmdCompileToLLVM then begin
|
||||
// "address" is the 0th field
|
||||
// "typ" is the 1rst field
|
||||
// "name" is the 2nd field
|
||||
name := cstringLit(p, p.s[cpsInit], normalize(s.name.s));
|
||||
if (s.kind = skParam) and not ccgIntroducedPtr(s) then allocParam(p, s);
|
||||
inc(p.labels, 3);
|
||||
appf(p.s[cpsInit],
|
||||
'%LOC$6 = getelementptr %TF* %F, %NI 0, $1, %NI 0$n' +
|
||||
'%LOC$7 = getelementptr %TF* %F, %NI 0, $1, %NI 1$n' +
|
||||
'%LOC$8 = getelementptr %TF* %F, %NI 0, $1, %NI 2$n' +
|
||||
'store i8* $2, i8** %LOC$6$n' +
|
||||
'store $3* $4, $3** %LOC$7$n' +
|
||||
'store i8* $5, i8** %LOC$8$n',
|
||||
[toRope(p.frameLen), s.loc.r, getTypeDesc(p.module, 'TNimType'),
|
||||
genTypeInfo(p.module, s.loc.t), name, toRope(p.labels),
|
||||
toRope(p.labels-1), toRope(p.labels-2)])
|
||||
end
|
||||
else begin
|
||||
a := con('&'+'', s.loc.r);
|
||||
if (s.kind = skParam) and ccgIntroducedPtr(s) then a := s.loc.r;
|
||||
appf(p.s[cpsInit],
|
||||
'F.s[$1].address = (void*)$3; F.s[$1].typ = $4; F.s[$1].name = $2;$n',
|
||||
[toRope(p.frameLen), makeCString(normalize(s.name.s)), a,
|
||||
genTypeInfo(p.module, s.loc.t)]);
|
||||
end;
|
||||
inc(p.frameLen);
|
||||
end;
|
||||
|
||||
procedure assignLocalVar(p: BProc; s: PSym);
|
||||
begin
|
||||
//assert(s.loc.k == locNone) // not yet assigned
|
||||
|
|
@ -258,45 +361,54 @@ begin
|
|||
// for each module that uses them!
|
||||
if s.loc.k = locNone then
|
||||
fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack);
|
||||
app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t));
|
||||
if sfRegister in s.flags then
|
||||
app(p.s[cpsLocals], ' register');
|
||||
if (sfVolatile in s.flags) or (p.nestedTryStmts > 0) then
|
||||
app(p.s[cpsLocals], ' volatile');
|
||||
|
||||
appf(p.s[cpsLocals], ' $1;$n', [s.loc.r]);
|
||||
// if debugging we need a new slot for the local variable:
|
||||
if [optStackTrace, optEndb] * p.Options = [optStackTrace, optEndb] then begin
|
||||
appf(p.s[cpsInit],
|
||||
'F.s[$1].name = $2; F.s[$1].address = (void*)&$3; F.s[$1].typ = $4;$n',
|
||||
[toRope(p.frameLen), makeCString(normalize(s.name.s)), s.loc.r,
|
||||
genTypeInfo(p.module, s.loc.t)]);
|
||||
inc(p.frameLen);
|
||||
if gCmd = cmdCompileToLLVM then begin
|
||||
appf(p.s[cpsLocals], '$1 = alloca $2$n',
|
||||
[s.loc.r, getTypeDesc(p.module, s.loc.t)]);
|
||||
include(s.loc.flags, lfIndirect);
|
||||
end
|
||||
else begin
|
||||
app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t));
|
||||
if sfRegister in s.flags then
|
||||
app(p.s[cpsLocals], ' register');
|
||||
if (sfVolatile in s.flags) or (p.nestedTryStmts > 0) then
|
||||
app(p.s[cpsLocals], ' volatile');
|
||||
|
||||
appf(p.s[cpsLocals], ' $1;$n', [s.loc.r]);
|
||||
end;
|
||||
// if debugging we need a new slot for the local variable:
|
||||
localDebugInfo(p, s);
|
||||
end;
|
||||
|
||||
procedure assignGlobalVar(m: BModule; s: PSym);
|
||||
procedure assignGlobalVar(p: BProc; s: PSym);
|
||||
begin
|
||||
if s.loc.k = locNone then
|
||||
fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap);
|
||||
useHeader(m, s);
|
||||
if lfNoDecl in s.loc.flags then exit;
|
||||
if sfImportc in s.flags then app(m.s[cfsVars], 'extern ');
|
||||
app(m.s[cfsVars], getTypeDesc(m, s.loc.t));
|
||||
if sfRegister in s.flags then
|
||||
app(m.s[cfsVars], ' register');
|
||||
if sfVolatile in s.flags then
|
||||
app(m.s[cfsVars], ' volatile');
|
||||
if sfThreadVar in s.flags then
|
||||
app(m.s[cfsVars], ' NIM_THREADVAR');
|
||||
appf(m.s[cfsVars], ' $1;$n', [s.loc.r]);
|
||||
if [optStackTrace, optEndb] * m.module.options =
|
||||
if gCmd = cmdCompileToLLVM then begin
|
||||
appf(p.module.s[cfsVars], '$1 = linkonce global $2 zeroinitializer$n',
|
||||
[s.loc.r, getTypeDesc(p.module, s.loc.t)]);
|
||||
include(s.loc.flags, lfIndirect);
|
||||
end
|
||||
else begin
|
||||
useHeader(p.module, s);
|
||||
if lfNoDecl in s.loc.flags then exit;
|
||||
if sfImportc in s.flags then app(p.module.s[cfsVars], 'extern ');
|
||||
app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t));
|
||||
if sfRegister in s.flags then app(p.module.s[cfsVars], ' register');
|
||||
if sfVolatile in s.flags then app(p.module.s[cfsVars], ' volatile');
|
||||
if sfThreadVar in s.flags then app(p.module.s[cfsVars], ' NIM_THREADVAR');
|
||||
appf(p.module.s[cfsVars], ' $1;$n', [s.loc.r]);
|
||||
end;
|
||||
if [optStackTrace, optEndb] * p.module.module.options =
|
||||
[optStackTrace, optEndb] then begin
|
||||
useMagic(m, 'dbgRegisterGlobal');
|
||||
appf(m.s[cfsDebugInit],
|
||||
useMagic(p.module, 'dbgRegisterGlobal');
|
||||
appff(p.module.s[cfsDebugInit],
|
||||
'dbgRegisterGlobal($1, &$2, $3);$n',
|
||||
[makeCString(normalize(s.owner.name.s + '.' +{&} s.name.s)), s.loc.r,
|
||||
genTypeInfo(m, s.typ)])
|
||||
'call void @dbgRegisterGlobal(i8* $1, i8* $2, $4* $3)$n',
|
||||
[cstringLit(p, p.module.s[cfsDebugInit],
|
||||
normalize(s.owner.name.s + '.' +{&} s.name.s)),
|
||||
s.loc.r,
|
||||
genTypeInfo(p.module, s.typ),
|
||||
getTypeDesc(p.module, 'TNimType')]);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
|
@ -308,15 +420,9 @@ end;
|
|||
procedure assignParam(p: BProc; s: PSym);
|
||||
begin
|
||||
assert(s.loc.r <> nil);
|
||||
if [optStackTrace, optEndb] * p.options = [optStackTrace, optEndb] then begin
|
||||
appf(p.s[cpsInit],
|
||||
'F.s[$1].name = $2; F.s[$1].address = (void*)$3; ' +
|
||||
'F.s[$1].typ = $4;$n',
|
||||
[toRope(p.frameLen), makeCString(normalize(s.name.s)),
|
||||
iff(ccgIntroducedPtr(s), s.loc.r, con('&'+'', s.loc.r)),
|
||||
genTypeInfo(p.module, s.loc.t)]);
|
||||
inc(p.frameLen)
|
||||
end
|
||||
if (sfAddrTaken in s.flags) and (gCmd = cmdCompileToLLVM) then
|
||||
allocParam(p, s);
|
||||
localDebugInfo(p, s);
|
||||
end;
|
||||
|
||||
procedure fillProcLoc(sym: PSym);
|
||||
|
|
@ -359,19 +465,26 @@ begin
|
|||
assert(lib <> nil);
|
||||
if not lib.generated then begin
|
||||
lib.generated := true;
|
||||
tmp := getGlobalTempName();
|
||||
assert(lib.name = nil);
|
||||
lib.name := tmp;
|
||||
// BUGFIX: useMagic has awful side-effects
|
||||
appff(m.s[cfsVars], 'static void* $1;$n',
|
||||
'$1 = linkonce global i8* zeroinitializer$n', [tmp]);
|
||||
inc(m.labels);
|
||||
appff(m.s[cfsDynLibInit],
|
||||
'$1 = nimLoadLibrary((NimStringDesc*) &$2);$n',
|
||||
'%MOC$4 = call i8* @nimLoadLibrary($3 $2)$n' +
|
||||
'store i8* %MOC$4, i8** $1$n',
|
||||
[tmp, getStrLit(m, lib.path), getTypeDesc(m, getSysType(tyString)),
|
||||
toRope(m.labels)]);
|
||||
//appf(m.s[cfsDynLibDeinit],
|
||||
// 'if ($1 != NIM_NIL) nimUnloadLibrary($1);$n', [tmp]);
|
||||
useMagic(m, 'nimLoadLibrary');
|
||||
useMagic(m, 'nimUnloadLibrary');
|
||||
useMagic(m, 'NimStringDesc');
|
||||
tmp := getTempName();
|
||||
appf(m.s[cfsVars], 'static void* $1;$n', [tmp]);
|
||||
appf(m.s[cfsDynLibInit],
|
||||
'$1 = nimLoadLibrary((NimStringDesc*) &$2);$n',
|
||||
[tmp, getStrLit(m, lib.path)]);
|
||||
appf(m.s[cfsDynLibDeinit],
|
||||
'if ($1 != NIM_NIL) nimUnloadLibrary($1);$n', [tmp]);
|
||||
assert(lib.name = nil);
|
||||
lib.name := tmp
|
||||
end
|
||||
end;
|
||||
if lib.name = nil then InternalError('loadDynamicLib');
|
||||
end;
|
||||
|
||||
procedure SymInDynamicLib(m: BModule; sym: PSym);
|
||||
|
|
@ -383,14 +496,25 @@ begin
|
|||
extname := sym.loc.r;
|
||||
loadDynamicLib(m, lib);
|
||||
useMagic(m, 'nimGetProcAddr');
|
||||
tmp := ropef('Dl_$1', [toRope(sym.id)]);
|
||||
if gCmd = cmdCompileToLLVM then include(sym.loc.flags, lfIndirect);
|
||||
|
||||
tmp := ropeff('Dl_$1', '@Dl_$1', [toRope(sym.id)]);
|
||||
sym.loc.r := tmp; // from now on we only need the internal name
|
||||
sym.typ.sym := nil; // generate a new name
|
||||
appf(m.s[cfsDynLibInit], '$1 = ($2) nimGetProcAddr($3, $4);$n',
|
||||
[tmp, getTypeDesc(m, sym.typ), lib.name, makeCString(ropeToStr(extname))]);
|
||||
inc(m.labels, 2);
|
||||
appff(m.s[cfsDynLibInit],
|
||||
'$1 = ($2) nimGetProcAddr($3, $4);$n',
|
||||
'%MOC$5 = load i8* $3$n' +
|
||||
'%MOC$6 = call $2 @nimGetProcAddr(i8* %MOC$5, i8* $4)$n' +
|
||||
'store $2 %MOC$6, $2* $1$n',
|
||||
[tmp, getTypeDesc(m, sym.typ), lib.name,
|
||||
cstringLit(m, m.s[cfsDynLibInit], ropeToStr(extname)),
|
||||
toRope(m.labels), toRope(m.labels-1)]);
|
||||
|
||||
app(m.s[cfsVars], getTypeDesc(m, sym.loc.t));
|
||||
appf(m.s[cfsVars], ' $1;$n', [sym.loc.r]);
|
||||
appff(m.s[cfsVars],
|
||||
'$2 $1;$n',
|
||||
'$1 = linkonce global $2 zeroinitializer$n',
|
||||
[sym.loc.r, getTypeDesc(m, sym.loc.t)]);
|
||||
end;
|
||||
|
||||
// ----------------------------- sections ---------------------------------
|
||||
|
|
@ -433,14 +557,23 @@ var
|
|||
begin
|
||||
if p.frameLen > 0 then begin
|
||||
useMagic(p.module, 'TVarSlot');
|
||||
slots := ropef(' TVarSlot s[$1];$n', [toRope(p.frameLen)])
|
||||
slots := ropeff(' TVarSlot s[$1];$n',
|
||||
', [$1 x %TVarSlot]', [toRope(p.frameLen)])
|
||||
end
|
||||
else
|
||||
slots := nil;
|
||||
appf(p.s[cpsLocals], 'volatile struct {TFrame* prev;' +
|
||||
appff(p.s[cpsLocals],
|
||||
'volatile struct {TFrame* prev;' +
|
||||
'NCSTRING procname;NI line;NCSTRING filename;' +
|
||||
'NI len;$n$1} F;$n', [slots]);
|
||||
prepend(p.s[cpsInit], ropef('F.len = $1;$n', [toRope(p.frameLen)]))
|
||||
'NI len;$n$1} F;$n',
|
||||
'%TF = type {%TFrame*, i8*, %NI, %NI$1}$n' +
|
||||
'%F = alloca %TF$n',
|
||||
[slots]);
|
||||
inc(p.labels);
|
||||
prepend(p.s[cpsInit], ropeff('F.len = $1;$n',
|
||||
'%LOC$2 = getelementptr %TF %F, %NI 4$n' +
|
||||
'store %NI $1, %NI* %LOC$2$n',
|
||||
[toRope(p.frameLen), toRope(p.labels)]))
|
||||
end;
|
||||
|
||||
function retIsNotVoid(s: PSym): bool;
|
||||
|
|
@ -448,10 +581,48 @@ begin
|
|||
result := (s.typ.sons[0] <> nil) and not isInvalidReturnType(s.typ.sons[0])
|
||||
end;
|
||||
|
||||
function initFrame(p: BProc; procname, filename: PRope): PRope;
|
||||
begin
|
||||
inc(p.labels, 5);
|
||||
result := ropeff(
|
||||
'F.procname = $1;$n' +
|
||||
'F.prev = framePtr;$n' +
|
||||
'F.filename = $2;$n' +
|
||||
'F.line = 0;$n' +
|
||||
'framePtr = (TFrame*)&F;$n',
|
||||
|
||||
'%LOC$3 = getelementptr %TF %F, %NI 1$n' +
|
||||
'%LOC$4 = getelementptr %TF %F, %NI 0$n' +
|
||||
'%LOC$5 = getelementptr %TF %F, %NI 3$n' +
|
||||
'%LOC$6 = getelementptr %TF %F, %NI 2$n' +
|
||||
|
||||
'store i8* $1, i8** %LOC$3$n' +
|
||||
'store %TFrame* @framePtr, %TFrame** %LOC$4$n' +
|
||||
'store i8* $2, i8** %LOC$5$n' +
|
||||
'store %NI 0, %NI* %LOC$6$n' +
|
||||
|
||||
'%LOC$7 = bitcast %TF* %F to %TFrame*$n' +
|
||||
'store %TFrame* %LOC$7, %TFrame** @framePtr$n',
|
||||
[procname, filename, toRope(p.labels), toRope(p.labels-1),
|
||||
toRope(p.labels-2), toRope(p.labels-3), toRope(p.labels-4)]);
|
||||
end;
|
||||
|
||||
function deinitFrame(p: BProc): PRope;
|
||||
begin
|
||||
inc(p.labels, 3);
|
||||
result := ropeff('framePtr = framePtr->prev;$n',
|
||||
|
||||
'%LOC$1 = load %TFrame* @framePtr$n' +
|
||||
'%LOC$2 = getelementptr %TFrame* %LOC$1, %NI 0$n' +
|
||||
'%LOC$3 = load %TFrame** %LOC$2$n' +
|
||||
'store %TFrame* $LOC$3, %TFrame** @framePtr', [
|
||||
toRope(p.labels), toRope(p.labels-1), toRope(p.labels-2)])
|
||||
end;
|
||||
|
||||
procedure genProcAux(m: BModule; prc: PSym);
|
||||
var
|
||||
p: BProc;
|
||||
generatedProc, header, returnStmt: PRope;
|
||||
generatedProc, header, returnStmt, procname, filename: PRope;
|
||||
i: int;
|
||||
res, param: PSym;
|
||||
begin
|
||||
|
|
@ -466,7 +637,7 @@ begin
|
|||
// declare the result symbol:
|
||||
assignLocalVar(p, res);
|
||||
assert(res.loc.r <> nil);
|
||||
returnStmt := ropef('return $1;$n', [rdLoc(res.loc)]);
|
||||
returnStmt := ropeff('return $1;$n', 'ret $1$n', [rdLoc(res.loc)]);
|
||||
end
|
||||
else begin
|
||||
fillResult(res);
|
||||
|
|
@ -482,22 +653,21 @@ begin
|
|||
|
||||
genStmts(p, prc.ast.sons[codePos]); // modifies p.locals, p.init, etc.
|
||||
if sfPure in prc.flags then
|
||||
generatedProc := ropef('$1 {$n$2$3$4}$n',
|
||||
generatedProc := ropeff('$1 {$n$2$3$4}$n', 'define $1 {$n$2$3$4}$n',
|
||||
[header, p.s[cpsLocals], p.s[cpsInit], p.s[cpsStmts]])
|
||||
else begin
|
||||
generatedProc := con(header, '{' + tnl);
|
||||
generatedProc := ropeff('$1 {$n', 'define $1 {$n', [header]);
|
||||
if optStackTrace in prc.options then begin
|
||||
getFrameDecl(p);
|
||||
prepend(p.s[cpsInit], ropef(
|
||||
'F.procname = $1;$n' +
|
||||
'F.prev = framePtr;$n' +
|
||||
'F.filename = $2;$n' +
|
||||
'F.line = 0;$n' +
|
||||
'framePtr = (TFrame*)&F;$n',
|
||||
[makeCString(prc.owner.name.s +{&} '.' +{&} prc.name.s),
|
||||
makeCString(toFilename(prc.info))]));
|
||||
end;
|
||||
if optProfiler in prc.options then begin
|
||||
app(generatedProc, p.s[cpsLocals]);
|
||||
procname := CStringLit(p, generatedProc,
|
||||
prc.owner.name.s +{&} '.' +{&} prc.name.s);
|
||||
filename := CStringLit(p, generatedProc, toFilename(prc.info));
|
||||
app(generatedProc, initFrame(p, procname, filename));
|
||||
end
|
||||
else
|
||||
app(generatedProc, p.s[cpsLocals]);
|
||||
if (optProfiler in prc.options) and (gCmd <> cmdCompileToLLVM) then begin
|
||||
if gProcProfile >= 64*1024 then // XXX: hard coded value!
|
||||
InternalError(prc.info, 'too many procedures for profiling');
|
||||
useMagic(m, 'profileData');
|
||||
|
|
@ -511,12 +681,13 @@ begin
|
|||
end;
|
||||
prepend(p.s[cpsInit], toRope('NIM_profilingStart = getticks();' + tnl));
|
||||
end;
|
||||
app(generatedProc, con(p.s));
|
||||
app(generatedProc, p.s[cpsInit]);
|
||||
app(generatedProc, p.s[cpsStmts]);
|
||||
if p.beforeRetNeeded then
|
||||
app(generatedProc, 'BeforeRet: ;' + tnl);
|
||||
if optStackTrace in prc.options then
|
||||
app(generatedProc, 'framePtr = framePtr->prev;' + tnl);
|
||||
if optProfiler in prc.options then
|
||||
app(generatedProc, deinitFrame(p));
|
||||
if (optProfiler in prc.options) and (gCmd <> cmdCompileToLLVM) then
|
||||
appf(generatedProc,
|
||||
'profileData[$1].total += elapsed(getticks(), NIM_profilingStart);$n',
|
||||
[toRope(prc.loc.a)]);
|
||||
|
|
@ -533,8 +704,10 @@ begin
|
|||
if lfDynamicLib in sym.loc.Flags then begin
|
||||
if (sym.owner.id <> m.module.id) and
|
||||
not intSetContainsOrIncl(m.declaredThings, sym.id) then begin
|
||||
appf(m.s[cfsVars], 'extern $1 Dl_$2;$n',
|
||||
[getTypeDesc(m, sym.loc.t), toRope(sym.id)])
|
||||
appff(m.s[cfsVars], 'extern $1 Dl_$2;$n',
|
||||
'@Dl_$2 = linkonce global $1 zeroinitializer$n',
|
||||
[getTypeDesc(m, sym.loc.t), toRope(sym.id)]);
|
||||
if gCmd = cmdCompileToLLVM then include(sym.loc.flags, lfIndirect);
|
||||
end
|
||||
end
|
||||
else begin
|
||||
|
|
@ -586,15 +759,22 @@ begin
|
|||
if sym.owner.id <> m.module.id then begin
|
||||
// else we already have the symbol generated!
|
||||
assert(sym.loc.r <> nil);
|
||||
app(m.s[cfsVars], 'extern ');
|
||||
app(m.s[cfsVars], getTypeDesc(m, sym.loc.t));
|
||||
if sfRegister in sym.flags then
|
||||
app(m.s[cfsVars], ' register');
|
||||
if sfVolatile in sym.flags then
|
||||
app(m.s[cfsVars], ' volatile');
|
||||
if sfThreadVar in sym.flags then
|
||||
app(m.s[cfsVars], ' NIM_THREADVAR');
|
||||
appf(m.s[cfsVars], ' $1;$n', [sym.loc.r])
|
||||
if gCmd = cmdCompileToLLVM then begin
|
||||
include(sym.loc.flags, lfIndirect);
|
||||
appf(m.s[cfsVars], '$1 = linkonce global $2 zeroinitializer$n',
|
||||
[sym.loc.r, getTypeDesc(m, sym.loc.t)]);
|
||||
end
|
||||
else begin
|
||||
app(m.s[cfsVars], 'extern ');
|
||||
app(m.s[cfsVars], getTypeDesc(m, sym.loc.t));
|
||||
if sfRegister in sym.flags then
|
||||
app(m.s[cfsVars], ' register');
|
||||
if sfVolatile in sym.flags then
|
||||
app(m.s[cfsVars], ' volatile');
|
||||
if sfThreadVar in sym.flags then
|
||||
app(m.s[cfsVars], ' NIM_THREADVAR');
|
||||
appf(m.s[cfsVars], ' $1;$n', [sym.loc.r])
|
||||
end
|
||||
end
|
||||
end;
|
||||
|
||||
|
|
@ -609,8 +789,9 @@ begin
|
|||
if sym.owner.id <> m.module.id then begin
|
||||
// else we already have the symbol generated!
|
||||
assert(sym.loc.r <> nil);
|
||||
app(m.s[cfsData], 'extern ');
|
||||
appf(m.s[cfsData], 'NIM_CONST $1 $2;$n',
|
||||
appff(m.s[cfsData],
|
||||
'extern NIM_CONST $1 $2;$n',
|
||||
'$1 = linkonce constant $2 zeroinitializer',
|
||||
[getTypeDesc(m, sym.loc.t), sym.loc.r])
|
||||
end
|
||||
end;
|
||||
|
|
@ -618,27 +799,36 @@ end;
|
|||
function getFileHeader(const cfilenoext: string): PRope;
|
||||
begin
|
||||
if optCompileOnly in gGlobalOptions then
|
||||
result := ropef(
|
||||
result := ropeff(
|
||||
'/* Generated by the Nimrod Compiler v$1 */$n' +
|
||||
'/* (c) 2008 Andreas Rumpf */$n',
|
||||
'; Generated by the Nimrod Compiler v$1$n' +
|
||||
'; (c) 2008 Andreas Rumpf$n',
|
||||
[toRope(versionAsString)])
|
||||
else
|
||||
result := ropef(
|
||||
result := ropeff(
|
||||
'/* Generated by the Nimrod Compiler v$1 */$n' +
|
||||
'/* (c) 2008 Andreas Rumpf */$n' +
|
||||
'/* Compiled for: $2, $3, $4 */$n' +
|
||||
'/* Command for C compiler:$n $5 */$n',
|
||||
'; Generated by the Nimrod Compiler v$1$n' +
|
||||
'; (c) 2008 Andreas Rumpf$n' +
|
||||
'; Compiled for: $2, $3, $4$n' +
|
||||
'; Command for C compiler:$n $5$n',
|
||||
[toRope(versionAsString), toRope(platform.OS[targetOS].name),
|
||||
toRope(platform.CPU[targetCPU].name),
|
||||
toRope(extccomp.CC[extccomp.ccompiler].name),
|
||||
toRope(getCompileCFileCmd(cfilenoext))]);
|
||||
case platform.CPU[targetCPU].intSize of
|
||||
16: appf(result, '$ntypedef short int NI;$n' +
|
||||
'typedef unsigned short int NU;$n', []);
|
||||
32: appf(result, '$ntypedef long int NI;$n' +
|
||||
'typedef unsigned long int NU;$n', []);
|
||||
64: appf(result, '$ntypedef long long int NI;$n' +
|
||||
'typedef unsigned long long int NU;$n', []);
|
||||
16: appff(result, '$ntypedef short int NI;$n' +
|
||||
'typedef unsigned short int NU;$n',
|
||||
'$n%NI = type i16$n', []);
|
||||
32: appff(result, '$ntypedef long int NI;$n' +
|
||||
'typedef unsigned long int NU;$n',
|
||||
'$n%NI = type i32$n', []);
|
||||
64: appff(result, '$ntypedef long long int NI;$n' +
|
||||
'typedef unsigned long long int NU;$n',
|
||||
'$n%NI = type i64$n', []);
|
||||
else begin end
|
||||
end
|
||||
end;
|
||||
|
|
@ -651,18 +841,37 @@ const
|
|||
' systemInit();$n' +
|
||||
'$1' +
|
||||
'$2';
|
||||
CommonMainBodyLLVM =
|
||||
' %MOC$3 = bitcast [8 x %NI]* %dummy to i8*$n' +
|
||||
' call void @setStackBottom(i8* %MOC$3)$n' +
|
||||
' call void @nim__datInit()$n' +
|
||||
' call void systemInit()$n' +
|
||||
'$1' +
|
||||
'$2';
|
||||
PosixMain =
|
||||
'NI cmdCount;$n' +
|
||||
'int cmdCount;$n' +
|
||||
'char** cmdLine;$n' +
|
||||
'char** gEnv;$n' +
|
||||
'int main(int argc, char** args, char** env) {$n' +
|
||||
' int dummy[8];$n' +
|
||||
' cmdLine = args;$n' +
|
||||
' cmdCount = (NI)argc;$n' +
|
||||
' cmdCount = argc;$n' +
|
||||
' gEnv = env;$n' +{&}
|
||||
CommonMainBody +{&}
|
||||
' return 0;$n' +
|
||||
'}$n';
|
||||
PosixMainLLVM =
|
||||
'@cmdCount = linkonce i32$n' +
|
||||
'@cmdLine = linkonce i8**$n' +
|
||||
'@gEnv = linkonce i8**$n' +
|
||||
'define i32 @main(i32 %argc, i8** %args, i8** %env) {$n' +
|
||||
' %dummy = alloca [8 x %NI]$n' +
|
||||
' store i8** %args, i8*** @cmdLine$n' +
|
||||
' store i32 %argc, i32* @cmdCount$n' +
|
||||
' store i8** %env, i8*** @gEnv$n' +{&}
|
||||
CommonMainBodyLLVM +{&}
|
||||
' ret i32 0$n' +
|
||||
'}$n';
|
||||
WinMain =
|
||||
'N_STDCALL(int, WinMain)(HINSTANCE hCurInstance, $n' +
|
||||
' HINSTANCE hPrevInstance, $n' +
|
||||
|
|
@ -671,6 +880,14 @@ const
|
|||
CommonMainBody +{&}
|
||||
' return 0;$n' +
|
||||
'}$n';
|
||||
WinMainLLVM =
|
||||
'define stdcall i32 @WinMain(i32 %hCurInstance, $n' +
|
||||
' i32 %hPrevInstance, $n' +
|
||||
' i8* %lpCmdLine, i32 %nCmdShow) {$n' +
|
||||
' %dummy = alloca [8 x %NI]$n' +{&}
|
||||
CommonMainBodyLLVM +{&}
|
||||
' ret i32 0$n' +
|
||||
'}$n';
|
||||
WinDllMain =
|
||||
'BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $n' +
|
||||
' LPVOID lpvReserved) {$n' +
|
||||
|
|
@ -678,6 +895,13 @@ const
|
|||
CommonMainBody +{&}
|
||||
' return 1;$n' +
|
||||
'}$n';
|
||||
WinDllMainLLVM =
|
||||
'define stdcall i32 @DllMain(i32 %hinstDLL, i32 %fwdreason, $n' +
|
||||
' i8* %lpvReserved) {$n' +
|
||||
' %dummy = alloca [8 x %NI]$n' +{&}
|
||||
CommonMainBodyLLVM +{&}
|
||||
' ret i32 1$n' +
|
||||
'}$n';
|
||||
var
|
||||
frmt: TFormatStr;
|
||||
begin
|
||||
|
|
@ -685,21 +909,28 @@ begin
|
|||
if (platform.targetOS = osWindows) and
|
||||
(gGlobalOptions * [optGenGuiApp, optGenDynLib] <> []) then begin
|
||||
if optGenGuiApp in gGlobalOptions then
|
||||
frmt := WinMain
|
||||
if gCmd = cmdCompileToLLVM then frmt := WinMainLLVM else frmt := WinMain
|
||||
else
|
||||
frmt := WinDllMain;
|
||||
if gCmd = cmdCompileToLLVM then
|
||||
frmt := WinDllMainLLVM
|
||||
else
|
||||
frmt := WinDllMain;
|
||||
{@discard} lists.IncludeStr(m.headerFiles, '<windows.h>')
|
||||
end
|
||||
else
|
||||
frmt := PosixMain;
|
||||
else
|
||||
if gCmd = cmdCompileToLLVM then
|
||||
frmt := PosixMainLLVM
|
||||
else
|
||||
frmt := PosixMain;
|
||||
if gBreakpoints <> nil then
|
||||
useMagic(m, 'dbgRegisterBreakpoint');
|
||||
appf(m.s[cfsProcs], frmt, [gBreakpoints, mainModInit])
|
||||
inc(m.labels);
|
||||
appf(m.s[cfsProcs], frmt, [gBreakpoints, mainModInit, toRope(m.labels)])
|
||||
end;
|
||||
|
||||
function getInitName(m: PSym): PRope;
|
||||
begin
|
||||
result := con(m.name.s, toRope('Init'));
|
||||
result := ropeff('$1Init', '@$1Init', [toRope(m.name.s)]);
|
||||
end;
|
||||
|
||||
procedure registerModuleToMain(m: PSym);
|
||||
|
|
@ -707,14 +938,15 @@ var
|
|||
initname: PRope;
|
||||
begin
|
||||
initname := getInitName(m);
|
||||
appf(mainModProcs, 'N_NOINLINE(void, $1)(void);$n', [initname]);
|
||||
appff(mainModProcs, 'N_NOINLINE(void, $1)(void);$n',
|
||||
'declare void $1() noinline$n', [initname]);
|
||||
if not (sfSystemModule in m.flags) then
|
||||
appf(mainModInit, '$1();$n', [initname]);
|
||||
appff(mainModInit, '$1();$n', 'call void ()* $1$n', [initname]);
|
||||
end;
|
||||
|
||||
procedure genInitCode(m: BModule);
|
||||
var
|
||||
initname, prc: PRope;
|
||||
initname, prc, procname, filename: PRope;
|
||||
begin
|
||||
if optProfiler in m.initProc.options then begin
|
||||
// This does not really belong here, but there is no good place for this
|
||||
|
|
@ -723,31 +955,28 @@ begin
|
|||
{@discard} lists.IncludeStr(m.headerFiles, '<cycle.h>');
|
||||
end;
|
||||
initname := getInitName(m.module);
|
||||
prc := ropef('N_NOINLINE(void, $1)(void) {$n', [initname]);
|
||||
|
||||
prc := ropeff('N_NOINLINE(void, $1)(void) {$n',
|
||||
'define void $1() noinline {$n', [initname]);
|
||||
if m.typeNodes > 0 then begin
|
||||
useMagic(m, 'TNimNode');
|
||||
appf(m.s[cfsTypeInit1], 'static TNimNode $1[$2];$n',
|
||||
appff(m.s[cfsTypeInit1], 'static TNimNode $1[$2];$n',
|
||||
'$1 = private alloca [$2 x @TNimNode]$n',
|
||||
[m.typeNodesName, toRope(m.typeNodes)]);
|
||||
end;
|
||||
if m.nimTypes > 0 then begin
|
||||
useMagic(m, 'TNimType');
|
||||
appf(m.s[cfsTypeInit1], 'static TNimType $1[$2];$n',
|
||||
appff(m.s[cfsTypeInit1], 'static TNimType $1[$2];$n',
|
||||
'$1 = private alloca [$2 x @TNimType]$n',
|
||||
[m.nimTypesName, toRope(m.nimTypes)]);
|
||||
end;
|
||||
if optStackTrace in m.initProc.options then begin
|
||||
getFrameDecl(m.initProc);
|
||||
app(prc, m.initProc.s[cpsLocals]);
|
||||
app(prc, m.s[cfsTypeInit1]);
|
||||
appf(prc,
|
||||
'F.len = 0;$n' + // IMPORTANT: else the debugger crashes!
|
||||
'F.procname = $1;$n' +
|
||||
'F.prev = framePtr;$n' +
|
||||
'F.filename = $2;$n' +
|
||||
'F.line = 0;$n' +
|
||||
'framePtr = (TFrame*)&F;$n',
|
||||
[makeCString('module ' + m.module.name.s),
|
||||
makeCString(toFilename(m.module.info))])
|
||||
|
||||
procname := CStringLit(m.initProc, prc, 'module ' +{&} m.module.name.s);
|
||||
filename := CStringLit(m.initProc, prc, toFilename(m.module.info));
|
||||
app(prc, initFrame(m.initProc, procname, filename));
|
||||
end
|
||||
else begin
|
||||
app(prc, m.initProc.s[cpsLocals]);
|
||||
|
|
@ -760,7 +989,7 @@ begin
|
|||
app(prc, m.initProc.s[cpsInit]);
|
||||
app(prc, m.initProc.s[cpsStmts]);
|
||||
if optStackTrace in m.initProc.options then
|
||||
app(prc, 'framePtr = framePtr->prev;' + tnl);
|
||||
app(prc, deinitFrame(m.initProc));
|
||||
app(prc, '}' +{&} tnl +{&} tnl);
|
||||
app(m.s[cfsProcs], prc)
|
||||
end;
|
||||
|
|
@ -817,7 +1046,8 @@ begin
|
|||
s := NewSym(skModule, getIdent(moduleName), nil);
|
||||
gmti := rawNewModule(s, joinPath(options.projectPath, moduleName)+'.nim');
|
||||
addPendingModule(gmti);
|
||||
appf(mainModProcs, 'N_NOINLINE(void, $1)(void);$n', [getInitName(s)]);
|
||||
appff(mainModProcs, 'N_NOINLINE(void, $1)(void);$n',
|
||||
'declare void $1() noinline$n', [getInitName(s)]);
|
||||
end;
|
||||
|
||||
function myOpen(module: PSym; const filename: string): PPassContext;
|
||||
|
|
|
|||
|
|
@ -77,14 +77,14 @@ const
|
|||
+{&} ' --debugger:on|off turn Embedded Nimrod Debugger ON|OFF' +{&} nl
|
||||
+{&} ' -x, --checks:on|off code generation for all runtime checks ON|OFF' +{&} nl
|
||||
+{&} ' --obj_checks:on|off code generation for obj conversion checks ON|OFF' +{&} nl
|
||||
+{&} ' --field_checks:on|off code generation for case record fields ON|OFF' +{&} nl
|
||||
+{&} ' --field_checks:on|off code generation for case variant fields ON|OFF' +{&} nl
|
||||
+{&} ' --range_checks:on|off code generation for range checks ON|OFF' +{&} nl
|
||||
+{&} ' --bound_checks:on|off code generation for bound checks ON|OFF' +{&} nl
|
||||
+{&} ' --overflow_checks:on|off code generation for over-/underflow checks ON|OFF' +{&} nl
|
||||
+{&} ' -a, --assertions:on|off code generation for assertions ON|OFF' +{&} nl
|
||||
+{&} ' --dead_code_elim:on|off whole program dead code elimination ON|OFF' +{&} nl
|
||||
+{&} ' --opt:none|speed|size optimize not at all or for speed|size' +{&} nl
|
||||
+{&} ' --app:console|gui|lib generate a console|GUI application or a shared lib' +{&} nl
|
||||
+{&} ' --app:console|gui generate a console|GUI application' +{&} nl
|
||||
+{&} ' -r, --run run the compiled program with given arguments' +{&} nl
|
||||
+{&} ' --advanced show advanced command line switches' +{&} nl
|
||||
+{&} ' -h, --help show this help' +{&} nl
|
||||
|
|
@ -125,7 +125,6 @@ const
|
|||
+{&} ' --checkpoints:on|off turn on|off checkpoints; for debugging Nimrod' +{&} nl
|
||||
+{&} ' --skip_cfg do not read the general configuration file' +{&} nl
|
||||
+{&} ' --skip_proj_cfg do not read the project''s configuration file' +{&} nl
|
||||
+{&} ' --import:MODULE_FILE import the given module implicitly for each module' +{&} nl
|
||||
+{&} ' --index:FILE use FILE to generate a documenation index file' +{&} nl
|
||||
+{&} ' --putenv:key=value set an environment variable' +{&} nl
|
||||
+{&} ' --list_cmd list the commands used to execute external programs' +{&} nl
|
||||
|
|
@ -506,7 +505,7 @@ begin
|
|||
expectArg(switch, arg, pass, info);
|
||||
gErrorMax := parseInt(arg);
|
||||
end;
|
||||
else if findSubStr('.', switch) >= strStart then
|
||||
else if strutils.find(switch, '.') >= strStart then
|
||||
options.setConfigVar(switch, arg)
|
||||
else
|
||||
InvalidCmdLineOption(pass, switch, info)
|
||||
|
|
|
|||
129
nim/debugids.pas
129
nim/debugids.pas
|
|
@ -1,129 +0,0 @@
|
|||
//
|
||||
//
|
||||
// The Nimrod Compiler
|
||||
// (c) Copyright 2008 Andreas Rumpf
|
||||
//
|
||||
// See the file "copying.txt", included in this
|
||||
// distribution, for details about the copyright.
|
||||
//
|
||||
unit debugids;
|
||||
|
||||
interface
|
||||
|
||||
{$include 'config.inc'}
|
||||
|
||||
uses
|
||||
nsystem, nos, strutils, ast;
|
||||
|
||||
const
|
||||
idfile = 'debugids.txt';
|
||||
|
||||
// This module implements debugging facilities for the ID mechanism.
|
||||
procedure registerID(s: PSym);
|
||||
|
||||
procedure writeIDTable();
|
||||
procedure loadIDTable();
|
||||
|
||||
implementation
|
||||
|
||||
type
|
||||
TIdSymTuple = record{@tuple} // keep id from sym to better detect bugs
|
||||
id: int;
|
||||
s: PSym;
|
||||
end;
|
||||
TIdSymTupleSeq = array of TIdSymTuple;
|
||||
TIdSymTable = record
|
||||
counter: int;
|
||||
data: TIdSymTupleSeq;
|
||||
end;
|
||||
|
||||
function TableRawGet(const t: TTable; key: PObject): int;
|
||||
var
|
||||
h: THash;
|
||||
begin
|
||||
h := hashNode(key) and high(t.data); // start with real hash value
|
||||
while t.data[h].key <> nil do begin
|
||||
if (t.data[h].key = key) then begin
|
||||
result := h; exit
|
||||
end;
|
||||
h := nextTry(h, high(t.data))
|
||||
end;
|
||||
result := -1
|
||||
end;
|
||||
|
||||
function TableSearch(const t: TTable; key, closure: PObject;
|
||||
comparator: TCmpProc): PObject;
|
||||
var
|
||||
h: THash;
|
||||
begin
|
||||
h := hashNode(key) and high(t.data); // start with real hash value
|
||||
while t.data[h].key <> nil do begin
|
||||
if (t.data[h].key = key) then
|
||||
if comparator(t.data[h].val, closure) then begin // BUGFIX 1
|
||||
result := t.data[h].val; exit
|
||||
end;
|
||||
h := nextTry(h, high(t.data))
|
||||
end;
|
||||
result := nil
|
||||
end;
|
||||
|
||||
function TableGet(const t: TTable; key: PObject): PObject;
|
||||
var
|
||||
index: int;
|
||||
begin
|
||||
index := TableRawGet(t, key);
|
||||
if index >= 0 then result := t.data[index].val
|
||||
else result := nil
|
||||
end;
|
||||
|
||||
procedure TableRawInsert(var data: TPairSeq; key, val: PObject);
|
||||
var
|
||||
h: THash;
|
||||
begin
|
||||
h := HashNode(key) and high(data);
|
||||
while data[h].key <> nil do begin
|
||||
assert(data[h].key <> key);
|
||||
h := nextTry(h, high(data))
|
||||
end;
|
||||
assert(data[h].key = nil);
|
||||
data[h].key := key;
|
||||
data[h].val := val;
|
||||
end;
|
||||
|
||||
procedure TableEnlarge(var t: TTable);
|
||||
var
|
||||
n: TPairSeq;
|
||||
i: int;
|
||||
begin
|
||||
{@ignore}
|
||||
n := emptySeq;
|
||||
setLength(n, length(t.data) * growthFactor);
|
||||
fillChar(n[0], length(n)*sizeof(n[0]), 0);
|
||||
{@emit
|
||||
newSeq(n, length(t.data) * growthFactor); }
|
||||
for i := 0 to high(t.data) do
|
||||
if t.data[i].key <> nil then
|
||||
TableRawInsert(n, t.data[i].key, t.data[i].val);
|
||||
{@ignore}
|
||||
t.data := n;
|
||||
{@emit
|
||||
swap(t.data, n);
|
||||
}
|
||||
end;
|
||||
|
||||
procedure TablePut(var t: TTable; key, val: PObject);
|
||||
var
|
||||
index: int;
|
||||
begin
|
||||
index := TableRawGet(t, key);
|
||||
if index >= 0 then
|
||||
t.data[index].val := val
|
||||
else begin
|
||||
if mustRehash(length(t.data), t.counter) then TableEnlarge(t);
|
||||
TableRawInsert(t.data, key, val);
|
||||
inc(t.counter)
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
|
@ -40,7 +40,7 @@ type
|
|||
modDesc: PRope; // module description
|
||||
dependsOn: PRope; // dependencies
|
||||
id: int; // for generating IDs
|
||||
splitAfter: int; // split to long entries in the TOC
|
||||
splitAfter: int; // split too long entries in the TOC
|
||||
tocPart: array of TTocEntry;
|
||||
hasToc: bool;
|
||||
toc, section: TSections;
|
||||
|
|
@ -777,7 +777,7 @@ begin
|
|||
rnLineBlock: outer := '<p>$1</p>';
|
||||
rnLineBlockItem: outer := '$1<br />';
|
||||
|
||||
rnBlockQuote: outer := '<blockquote>$1</blockquote>$n';
|
||||
rnBlockQuote: outer := '<blockquote><p>$1</p></blockquote>$n';
|
||||
|
||||
rnTable, rnGridTable:
|
||||
outer := '<table border="1" class="docutils">$1</table>';
|
||||
|
|
|
|||
|
|
@ -352,13 +352,13 @@ end;
|
|||
|
||||
procedure addCompileOption(const option: string);
|
||||
begin
|
||||
if strutils.findSubStr(option, compileOptions, strStart) < strStart then
|
||||
if strutils.find(compileOptions, option, strStart) < strStart then
|
||||
addOpt(compileOptions, option)
|
||||
end;
|
||||
|
||||
procedure addLinkOption(const option: string);
|
||||
begin
|
||||
if findSubStr(option, linkOptions, strStart) < strStart then
|
||||
if find(linkOptions, option, strStart) < strStart then
|
||||
addOpt(linkOptions, option)
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
//
|
||||
// The Nimrod Compiler
|
||||
// (c) Copyright 2008 Andreas Rumpf
|
||||
// (c) Copyright 2009 Andreas Rumpf
|
||||
//
|
||||
// See the file "copying.txt", included in this
|
||||
// distribution, for details about the copyright.
|
||||
|
|
@ -38,8 +38,15 @@ function getIdent(identifier: cstring; len: int; h: THash): PIdent; overload;
|
|||
// special version for the scanner; the scanner's buffering scheme makes
|
||||
// this horribly efficient. Most of the time no character copying is needed!
|
||||
|
||||
function IdentEq(id: PIdent; const name: string): bool;
|
||||
|
||||
implementation
|
||||
|
||||
function IdentEq(id: PIdent; const name: string): bool;
|
||||
begin
|
||||
result := id.id = getIdent(name).id;
|
||||
end;
|
||||
|
||||
var
|
||||
buckets: array [0..4096*2-1] of PIdent;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ type
|
|||
llsFile, // stream encapsulates a file
|
||||
llsStdIn); // stream encapsulates stdin
|
||||
TLLStream = object(NObject)
|
||||
kind: TLLStreamKind; // exposed for low-level access (lexbase uses this)
|
||||
kind: TLLStreamKind; // accessible for low-level access (lexbase uses this)
|
||||
f: TBinaryFile;
|
||||
s: string;
|
||||
pos: int; // for string streams
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ procedure CompileProject(const filename: string);
|
|||
begin
|
||||
{@discard} CompileModule(
|
||||
JoinPath(options.libpath, appendFileExt('system', nimExt)), false, true);
|
||||
{@discard} CompileModule(filename, true, false);
|
||||
{@discard} CompileModule(appendFileExt(filename, nimExt), true, false);
|
||||
end;
|
||||
|
||||
procedure semanticPasses;
|
||||
|
|
@ -352,6 +352,11 @@ begin
|
|||
wantFile(filename);
|
||||
CommandCompileToEcmaScript(filename);
|
||||
end;
|
||||
wCompileToLLVM: begin
|
||||
gCmd := cmdCompileToLLVM;
|
||||
wantFile(filename);
|
||||
CommandCompileToC(filename);
|
||||
end;
|
||||
wPretty: begin
|
||||
gCmd := cmdPretty;
|
||||
wantFile(filename);
|
||||
|
|
|
|||
66
nim/msgs.pas
66
nim/msgs.pas
|
|
@ -278,6 +278,7 @@ type
|
|||
errXRequiresOneArgument,
|
||||
errUnhandledExceptionX,
|
||||
errCyclicTree,
|
||||
errXisNoMacroOrTemplate,
|
||||
errUser,
|
||||
warnCannotOpenFile,
|
||||
warnOctalEscape,
|
||||
|
|
@ -491,7 +492,7 @@ const
|
|||
'$1 here not allowed',
|
||||
'invalid control flow: $1',
|
||||
'a type has no value',
|
||||
'''$1'' is no type',
|
||||
'invalid type: ''$1''',
|
||||
'''^'' needs a pointer or reference type',
|
||||
'invalid context for builtin ''$1''',
|
||||
'invalid expression',
|
||||
|
|
@ -536,6 +537,7 @@ const
|
|||
'converter requires one parameter',
|
||||
'unhandled exception: $1',
|
||||
'macro returned a cyclic abstract syntax tree',
|
||||
'''$1'' is no macro or template',
|
||||
'$1',
|
||||
'cannot open ''$1'' [CannotOpenFile]',
|
||||
'octal escape sequences do not exist; leading zero is ignored [OctalEscape]',
|
||||
|
|
@ -645,8 +647,8 @@ const // this format is understood by many text editors: it is the same that
|
|||
procedure MessageOut(const s: string);
|
||||
|
||||
procedure rawMessage(const msg: TMsgKind; const arg: string = ''); overload;
|
||||
procedure rawMessage(const msg: TMsgKind; const args: array of string); overload;
|
||||
|
||||
procedure rawMessage(const msg: TMsgKind; const args: array of string); overload;
|
||||
|
||||
procedure liMessage(const info: TLineInfo; const msg: TMsgKind;
|
||||
const arg: string = '');
|
||||
|
||||
|
|
@ -840,37 +842,37 @@ begin
|
|||
getMessageStr(errInstantiationFrom, '')]));
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure rawMessage(const msg: TMsgKind; const args: array of string);
|
||||
var
|
||||
frmt: string;
|
||||
begin
|
||||
case msg of
|
||||
errMin..errMax: begin
|
||||
writeContext();
|
||||
frmt := rawErrorFormat;
|
||||
end;
|
||||
warnMin..warnMax: begin
|
||||
if not (optWarns in gOptions) then exit;
|
||||
if not (msg in gNotes) then exit;
|
||||
frmt := rawWarningFormat;
|
||||
inc(gWarnCounter);
|
||||
end;
|
||||
hintMin..hintMax: begin
|
||||
if not (optHints in gOptions) then exit;
|
||||
if not (msg in gNotes) then exit;
|
||||
frmt := rawHintFormat;
|
||||
inc(gHintCounter);
|
||||
end;
|
||||
else assert(false) // cannot happen
|
||||
end;
|
||||
MessageOut(Format(frmt, format(msgKindToString(msg), args)));
|
||||
handleError(msg);
|
||||
end;
|
||||
|
||||
procedure rawMessage(const msg: TMsgKind; const args: array of string);
|
||||
var
|
||||
frmt: string;
|
||||
begin
|
||||
case msg of
|
||||
errMin..errMax: begin
|
||||
writeContext();
|
||||
frmt := rawErrorFormat;
|
||||
end;
|
||||
warnMin..warnMax: begin
|
||||
if not (optWarns in gOptions) then exit;
|
||||
if not (msg in gNotes) then exit;
|
||||
frmt := rawWarningFormat;
|
||||
inc(gWarnCounter);
|
||||
end;
|
||||
hintMin..hintMax: begin
|
||||
if not (optHints in gOptions) then exit;
|
||||
if not (msg in gNotes) then exit;
|
||||
frmt := rawHintFormat;
|
||||
inc(gHintCounter);
|
||||
end;
|
||||
else assert(false) // cannot happen
|
||||
end;
|
||||
MessageOut(Format(frmt, format(msgKindToString(msg), args)));
|
||||
handleError(msg);
|
||||
end;
|
||||
|
||||
procedure rawMessage(const msg: TMsgKind; const arg: string = '');
|
||||
begin
|
||||
rawMessage(msg, [arg]);
|
||||
begin
|
||||
rawMessage(msg, [arg]);
|
||||
end;
|
||||
|
||||
procedure liMessage(const info: TLineInfo; const msg: TMsgKind;
|
||||
|
|
|
|||
|
|
@ -9,9 +9,7 @@
|
|||
|
||||
unit nimconf;
|
||||
|
||||
// This module used to handle the reading of the config file. We now just
|
||||
// read environment variables. This is easier to avoid bootstraping issues.
|
||||
|
||||
// This module handles the reading of the config file.
|
||||
{$include 'config.inc'}
|
||||
|
||||
interface
|
||||
|
|
@ -258,7 +256,7 @@ begin
|
|||
addChar(s, '.');
|
||||
confTok(L, tok);
|
||||
checkSymbol(L, tok);
|
||||
s := s +{&} tokToStr(tok);
|
||||
add(s, tokToStr(tok));
|
||||
confTok(L, tok)
|
||||
end;
|
||||
if tok.tokType = tkBracketLe then begin
|
||||
|
|
@ -266,7 +264,7 @@ begin
|
|||
// BUGFIX: do not copy '['!
|
||||
confTok(L, tok);
|
||||
checkSymbol(L, tok);
|
||||
val := val +{&} tokToStr(tok);
|
||||
add(val, tokToStr(tok));
|
||||
confTok(L, tok);
|
||||
if tok.tokType = tkBracketRi then confTok(L, tok)
|
||||
else lexMessage(L, errTokenExpected, ''']''');
|
||||
|
|
@ -276,12 +274,12 @@ begin
|
|||
if length(val) > 0 then addChar(val, ':'); // BUGFIX
|
||||
confTok(L, tok); // skip ':' or '='
|
||||
checkSymbol(L, tok);
|
||||
val := val +{&} tokToStr(tok);
|
||||
add(val, tokToStr(tok));
|
||||
confTok(L, tok); // skip symbol
|
||||
while (tok.ident <> nil) and (tok.ident.id = getIdent('&'+'').id) do begin
|
||||
confTok(L, tok);
|
||||
checkSymbol(L, tok);
|
||||
val := val +{&} tokToStr(tok);
|
||||
add(val, tokToStr(tok));
|
||||
confTok(L, tok)
|
||||
end
|
||||
end;
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ type
|
|||
|
||||
procedure HandleCmdLine;
|
||||
var
|
||||
command, filename: string;
|
||||
command, filename, prog: string;
|
||||
start: TTime;
|
||||
begin
|
||||
{@emit start := getTime(); }
|
||||
|
|
@ -92,13 +92,21 @@ begin
|
|||
toString(getTime() - start)]);
|
||||
}
|
||||
end;
|
||||
if optRun in gGlobalOptions then
|
||||
execExternalProgram(quoteIfContainsWhite(changeFileExt(filename, '')) +{&}
|
||||
' ' +{&} arguments)
|
||||
if optRun in gGlobalOptions then begin
|
||||
{$ifdef unix}
|
||||
prog := './' + quoteIfContainsWhite(changeFileExt(filename, ''));
|
||||
{$else}
|
||||
prog := quoteIfContainsWhite(changeFileExt(filename, ''));
|
||||
{$endif}
|
||||
execExternalProgram(prog +{&} ' ' +{&} arguments)
|
||||
end
|
||||
end
|
||||
end;
|
||||
|
||||
begin
|
||||
//{@emit
|
||||
// GC_disableMarkAndSweep();
|
||||
//}
|
||||
cmdLineInfo := newLineInfo('command line', -1, -1);
|
||||
condsyms.InitDefines();
|
||||
HandleCmdLine();
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ procedure createDir(const dir: string);
|
|||
var
|
||||
i: int;
|
||||
begin
|
||||
for i := 1 to length(dir) do begin
|
||||
for i := 2 to length(dir) do begin
|
||||
if dir[i] in [sep, altsep] then sysutils.createDir(ncopy(dir, 1, i-1));
|
||||
end;
|
||||
sysutils.createDir(dir);
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ type
|
|||
{$endif}
|
||||
EOutOfRange = class(Exception)
|
||||
end;
|
||||
EOS = class(Exception) end;
|
||||
|
||||
float32 = single;
|
||||
float64 = double;
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@ const
|
|||
//cog.outl('VersionMinor = %s;' % ver[1])
|
||||
//cog.outl('VersionPatch = %s;' % ver[2])
|
||||
//]]]
|
||||
VersionAsString = '0.7.4';
|
||||
VersionAsString = '0.7.6';
|
||||
VersionMajor = 0;
|
||||
VersionMinor = 7;
|
||||
VersionPatch = 4;
|
||||
VersionPatch = 6;
|
||||
//[[[[end]]]]
|
||||
|
||||
implementation
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ type
|
|||
cmdCompileToC,
|
||||
cmdCompileToCpp,
|
||||
cmdCompileToEcmaScript,
|
||||
cmdCompileToLLVM,
|
||||
cmdInterpret,
|
||||
cmdPretty,
|
||||
cmdDoc,
|
||||
|
|
@ -207,7 +208,15 @@ begin
|
|||
if startsWith(dir, prefix) then begin
|
||||
result := ncopy(dir, length(prefix) + strStart); exit
|
||||
end;
|
||||
result := dir
|
||||
result := dir;
|
||||
end;
|
||||
|
||||
function removeTrailingDirSep(const path: string): string;
|
||||
begin
|
||||
if (length(path) > 0) and (path[length(path)+strStart-1] = dirSep) then
|
||||
result := ncopy(path, strStart, length(path)+strStart-2)
|
||||
else
|
||||
result := path
|
||||
end;
|
||||
|
||||
function toGeneratedFile(const path, ext: string): string;
|
||||
|
|
@ -215,7 +224,8 @@ var
|
|||
head, tail: string;
|
||||
begin
|
||||
splitPath(path, head, tail);
|
||||
result := joinPath([projectPath, genSubDir, shortenDir(head +{&} dirSep),
|
||||
if length(head) > 0 then head := shortenDir(head +{&} dirSep);
|
||||
result := joinPath([projectPath, genSubDir, head,
|
||||
changeFileExt(tail, ext)])
|
||||
end;
|
||||
|
||||
|
|
@ -225,10 +235,18 @@ var
|
|||
head, tail, subdir: string;
|
||||
begin
|
||||
splitPath(f, head, tail);
|
||||
subdir := joinPath([projectPath, genSubDir, shortenDir(head +{&} dirSep)]);
|
||||
if length(head) > 0 then
|
||||
head := removeTrailingDirSep(shortenDir(head +{&} dirSep));
|
||||
subdir := joinPath([projectPath, genSubDir, head]);
|
||||
if createSubDir then begin
|
||||
//Writeln(output, subdir);
|
||||
createDir(subdir);
|
||||
try
|
||||
createDir(subdir);
|
||||
except
|
||||
on EOS do begin
|
||||
writeln(output, 'cannot create directory: ' + subdir);
|
||||
halt(1)
|
||||
end
|
||||
end
|
||||
end;
|
||||
result := joinPath(subdir, tail)
|
||||
end;
|
||||
|
|
|
|||
|
|
@ -350,7 +350,7 @@ begin
|
|||
addChar(result.key, '.');
|
||||
rawGetTok(c, c.tok);
|
||||
if c.tok.kind = tkSymbol then begin
|
||||
result.key := result.key +{&} c.tok.literal;
|
||||
add(result.key, c.tok.literal);
|
||||
rawGetTok(c, c.tok);
|
||||
end
|
||||
else begin
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ function astNeeded(s: PSym): bool;
|
|||
// appropriate to free the procedure body's memory. This is important
|
||||
// to keep memory usage down.
|
||||
|
||||
// some passes (the semantic checker) need these:
|
||||
// the semantic checker needs these:
|
||||
var
|
||||
gImportModule: function (const filename: string): PSym;
|
||||
gIncludeFile: function (const filename: string): PNode;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ unit platform;
|
|||
// and operating systems.
|
||||
// Note: Unfortunately if an OS or CPU is listed here this does not mean that
|
||||
// Nimrod has been tested on this platform or that the RTL has been ported.
|
||||
// Feel free to test for your exentric platform!
|
||||
// Feel free to test for your excentric platform!
|
||||
|
||||
interface
|
||||
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ procedure processDynLib(c: PContext; n: PNode; sym: PSym);
|
|||
var
|
||||
lib: PLib;
|
||||
begin
|
||||
if sym = nil then
|
||||
if (sym = nil) or (sym.kind = skModule) then
|
||||
POptionEntry(c.optionStack.tail).dynlib := getLib(c, libDynamic,
|
||||
expectStrLit(c, n))
|
||||
else begin
|
||||
|
|
@ -553,13 +553,6 @@ begin
|
|||
wPassL: extccomp.addLinkOption(expectStrLit(c, it));
|
||||
wPassC: extccomp.addCompileOption(expectStrLit(c, it));
|
||||
|
||||
// fixupSystem not even documented:
|
||||
wFixupSystem: begin
|
||||
if c.module = magicSys.SystemModule then
|
||||
magicsys.FinishSystem(magicsys.SystemModule.tab)
|
||||
else
|
||||
invalidPragma(it)
|
||||
end;
|
||||
wBreakpoint: PragmaBreakpoint(c, it);
|
||||
wCheckpoint: PragmaCheckpoint(c, it);
|
||||
|
||||
|
|
@ -585,7 +578,7 @@ begin
|
|||
processNote(c, n)
|
||||
end;
|
||||
end;
|
||||
if sym <> nil then begin
|
||||
if (sym <> nil) and (sym.kind <> skModule) then begin
|
||||
lib := POptionEntry(c.optionstack.tail).dynlib;
|
||||
if ([lfDynamicLib, lfHeader] * sym.loc.flags = []) and
|
||||
(sfImportc in sym.flags) and
|
||||
|
|
@ -625,7 +618,7 @@ begin
|
|||
wHints, wLinedir, wStacktrace, wLinetrace, wOptimization,
|
||||
wHint, wWarning, wError, wFatal, wDefine, wUndef,
|
||||
wCompile, wLink, wLinkSys, wPure,
|
||||
wPush, wPop, wFixupSystem, wBreakpoint, wCheckpoint,
|
||||
wPush, wPop, wBreakpoint, wCheckpoint,
|
||||
wPassL, wPassC, wDeadCodeElim]);
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
//
|
||||
// The Nimrod Compiler
|
||||
// (c) Copyright 2008 Andreas Rumpf
|
||||
// (c) Copyright 2009 Andreas Rumpf
|
||||
//
|
||||
// See the file "copying.txt", included in this
|
||||
// distribution, for details about the copyright.
|
||||
|
|
@ -123,7 +123,7 @@ type
|
|||
files: TStringSeq;
|
||||
dataIdx: int; // offset of start of data section
|
||||
convertersIdx: int; // offset of start of converters section
|
||||
initIdx, interfIdx, compilerProcsIdx: int;
|
||||
initIdx, interfIdx, compilerProcsIdx, cgenIdx: int;
|
||||
filename: string;
|
||||
index, imports: TIndex;
|
||||
readerIndex: int;
|
||||
|
|
@ -841,6 +841,10 @@ begin
|
|||
r.initIdx := r.pos+2; // "(\10"
|
||||
skipSection(r);
|
||||
end
|
||||
else if section = 'CGEN' then begin
|
||||
r.cgenIdx := r.pos+2;
|
||||
skipSection(r);
|
||||
end
|
||||
else begin
|
||||
MessageOut('skipping section: ' + toString(r.pos));
|
||||
skipSection(r);
|
||||
|
|
|
|||
|
|
@ -530,6 +530,7 @@ begin
|
|||
nkVarSection: begin
|
||||
for i := 0 to sonsLen(n)-1 do begin
|
||||
a := n.sons[i];
|
||||
if a.kind = nkCommentStmt then continue;
|
||||
if a.kind <> nkIdentDefs then InternalError(a.info, 'rodwrite.process');
|
||||
addInterfaceSym(w, a.sons[0].sym);
|
||||
end
|
||||
|
|
@ -537,6 +538,7 @@ begin
|
|||
nkConstSection: begin
|
||||
for i := 0 to sonsLen(n)-1 do begin
|
||||
a := n.sons[i];
|
||||
if a.kind = nkCommentStmt then continue;
|
||||
if a.kind <> nkConstDef then InternalError(a.info, 'rodwrite.process');
|
||||
addInterfaceSym(w, a.sons[0].sym);
|
||||
end
|
||||
|
|
@ -544,6 +546,7 @@ begin
|
|||
nkTypeSection: begin
|
||||
for i := 0 to sonsLen(n)-1 do begin
|
||||
a := n.sons[i];
|
||||
if a.kind = nkCommentStmt then continue;
|
||||
if a.sons[0].kind <> nkSym then
|
||||
InternalError(a.info, 'rodwrite.process');
|
||||
s := a.sons[0].sym;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
//
|
||||
// The Nimrod Compiler
|
||||
// (c) Copyright 2008 Andreas Rumpf
|
||||
// (c) Copyright 2009 Andreas Rumpf
|
||||
//
|
||||
// See the file "copying.txt", included in this
|
||||
// distribution, for details about the copyright.
|
||||
|
|
@ -112,8 +112,6 @@ function ropef(const frmt: TFormatStr; const args: array of PRope): PRope;
|
|||
procedure appf(var c: PRope; const frmt: TFormatStr;
|
||||
const args: array of PRope);
|
||||
|
||||
procedure RopeSeqInsert(var rs: TRopeSeq; r: PRope; at: Natural);
|
||||
|
||||
function getCacheStats: string;
|
||||
|
||||
function RopeEqualsFile(r: PRope; const f: string): Boolean;
|
||||
|
|
@ -524,8 +522,7 @@ begin
|
|||
assert(RopeInvariant(result));
|
||||
end;
|
||||
|
||||
procedure appf(var c: PRope; const frmt: TFormatStr;
|
||||
const args: array of PRope);
|
||||
procedure appf(var c: PRope; const frmt: TFormatStr; const args: array of PRope);
|
||||
begin
|
||||
app(c, ropef(frmt, args))
|
||||
end;
|
||||
|
|
|
|||
|
|
@ -1967,6 +1967,10 @@ begin
|
|||
initParser(q, p.s);
|
||||
q.filename := filename;
|
||||
getTokens(readFile(path), false, q.tok);
|
||||
// workaround a GCC bug:
|
||||
if find(q.tok[high(q.tok)].symbol, #0#1#2) > 0 then begin
|
||||
InternalError('Too many binary zeros in include file');
|
||||
end;
|
||||
result := parseDoc(q);
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -428,7 +428,7 @@ begin
|
|||
L.bufpos := pos; // restore position
|
||||
|
||||
try
|
||||
if (L.buf[pos] = '0') and (L.buf[pos+1] in ['x','X','b','B','o','O'])
|
||||
if (L.buf[pos] = '0') and (L.buf[pos+1] in ['x','X','b','B','o','O','c','C'])
|
||||
then begin
|
||||
inc(pos, 2);
|
||||
xi := 0;
|
||||
|
|
@ -451,7 +451,7 @@ begin
|
|||
end
|
||||
end
|
||||
end;
|
||||
'o': begin
|
||||
'o', 'c', 'C': begin
|
||||
result.base := base8;
|
||||
while true do begin
|
||||
case L.buf[pos] of
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
//
|
||||
// The Nimrod Compiler
|
||||
// (c) Copyright 2008 Andreas Rumpf
|
||||
// (c) Copyright 2009 Andreas Rumpf
|
||||
//
|
||||
// See the file "copying.txt", included in this
|
||||
// distribution, for details about the copyright.
|
||||
|
|
@ -183,7 +183,7 @@ begin
|
|||
c := newContext(module, filename);
|
||||
if (c.p <> nil) then InternalError(module.info, 'sem.myOpen');
|
||||
c.semConstExpr := semConstExpr;
|
||||
c.p := newProcCon(nil);
|
||||
c.p := newProcCon(module);
|
||||
pushOwner(c.module);
|
||||
openScope(c.tab); // scope for imported symbols
|
||||
SymTabAdd(c.tab, module); // a module knows itself
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
//
|
||||
// The Nimrod Compiler
|
||||
// (c) Copyright 2008 Andreas Rumpf
|
||||
// (c) Copyright 2009 Andreas Rumpf
|
||||
//
|
||||
// See the file "copying.txt", included in this
|
||||
// distribution, for details about the copyright.
|
||||
|
|
@ -128,6 +128,7 @@ end;
|
|||
|
||||
function newProcCon(owner: PSym): PProcCon;
|
||||
begin
|
||||
if owner = nil then InternalError('owner is nil');
|
||||
new(result);
|
||||
{@ignore}
|
||||
fillChar(result^, sizeof(result^), 0);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
//
|
||||
// The Ethexor Morpork Compiler
|
||||
// (c) Copyright 2008 Andreas Rumpf
|
||||
// (c) Copyright 2009 Andreas Rumpf
|
||||
//
|
||||
// See the file "copying.txt", included in this
|
||||
// distribution, for details about the copyright.
|
||||
|
|
@ -87,19 +87,23 @@ begin
|
|||
end
|
||||
end;
|
||||
|
||||
function isCastable(castDest, src: PType): Boolean;
|
||||
function isCastable(dst, src: PType): Boolean;
|
||||
//const
|
||||
// castableTypeKinds = {@set}[tyInt, tyPtr, tyRef, tyCstring, tyString,
|
||||
// tySequence, tyPointer, tyNil, tyOpenArray,
|
||||
// tyProc, tySet, tyEnum, tyBool, tyChar];
|
||||
var
|
||||
ds, ss: biggestInt;
|
||||
begin
|
||||
// this is very unrestrictive; cast is allowed if castDest.size >= src.size
|
||||
ds := computeSize(castDest);
|
||||
ds := computeSize(dst);
|
||||
ss := computeSize(src);
|
||||
if ds < 0 then result := false
|
||||
else if ss < 0 then result := false
|
||||
else
|
||||
else
|
||||
result := (ds >= ss) or
|
||||
(castDest.kind in [tyInt..tyFloat128]) or
|
||||
(src.kind in [tyInt..tyFloat128])
|
||||
(skipGeneric(dst).kind in [tyInt..tyFloat128]) or
|
||||
(skipGeneric(src).kind in [tyInt..tyFloat128])
|
||||
end;
|
||||
|
||||
function semConv(c: PContext; n: PNode; s: PSym): PNode;
|
||||
|
|
@ -620,6 +624,11 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
procedure checkDeprecated(n: PNode; s: PSym);
|
||||
begin
|
||||
if sfDeprecated in s.flags then liMessage(n.info, warnDeprecated, s.name.s);
|
||||
end;
|
||||
|
||||
function semSym(c: PContext; n: PNode; s: PSym; flags: TExprFlags): PNode;
|
||||
begin
|
||||
result := newSymNode(s);
|
||||
|
|
@ -652,7 +661,8 @@ begin
|
|||
end
|
||||
end
|
||||
else begin end
|
||||
end
|
||||
end;
|
||||
checkDeprecated(n, s);
|
||||
end;
|
||||
|
||||
function isTypeExpr(n: PNode): bool;
|
||||
|
|
@ -787,6 +797,7 @@ begin
|
|||
result := newSymNode(f);
|
||||
result.info := n.info;
|
||||
result.typ := ty;
|
||||
checkDeprecated(n, f);
|
||||
end
|
||||
else
|
||||
liMessage(n.sons[1].info, errEnumHasNoValueX, i.s);
|
||||
|
|
@ -814,6 +825,7 @@ begin
|
|||
n.sons[0] := makeDeref(n.sons[0]);
|
||||
n.sons[1] := newSymNode(f); // we now have the correct field
|
||||
n.typ := f.typ;
|
||||
checkDeprecated(n, f);
|
||||
if check = nil then result := n
|
||||
else begin
|
||||
check.sons[0] := n;
|
||||
|
|
@ -831,14 +843,17 @@ begin
|
|||
n.sons[1] := newSymNode(f);
|
||||
n.typ := f.typ;
|
||||
result := n;
|
||||
checkDeprecated(n, f);
|
||||
exit
|
||||
end
|
||||
end;
|
||||
// allow things like "".replace(...)
|
||||
// --> replace("", ...)
|
||||
f := SymTabGet(c.tab, i);
|
||||
if (f <> nil) and (f.kind = skStub) then loadStub(f);
|
||||
if (f <> nil) and (f.kind in [skProc, skIterator]) then begin
|
||||
//if (f <> nil) and (f.kind = skStub) then loadStub(f);
|
||||
// XXX ``loadStub`` is not correct here as we don't care for ``f`` really
|
||||
if (f <> nil) then begin
|
||||
// BUGFIX: do not check for (f.kind in [skProc, skIterator]) here
|
||||
result := newNodeI(nkDotCall, n.info);
|
||||
// This special node kind is to merge with the call handler in `semExpr`.
|
||||
addSon(result, newIdentNode(i, n.info));
|
||||
|
|
@ -1121,6 +1136,57 @@ begin
|
|||
result := semFieldAccess(c, n, flags);
|
||||
end;
|
||||
|
||||
function isCallExpr(n: PNode): bool;
|
||||
begin
|
||||
result := n.kind in [nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand];
|
||||
end;
|
||||
|
||||
function semMacroStmt(c: PContext; n: PNode): PNode;
|
||||
var
|
||||
s: PSym;
|
||||
a: PNode;
|
||||
i: int;
|
||||
begin
|
||||
checkMinSonsLen(n, 2);
|
||||
if isCallExpr(n.sons[0]) then
|
||||
a := n.sons[0].sons[0]
|
||||
else
|
||||
a := n.sons[0];
|
||||
s := qualifiedLookup(c, a, false);
|
||||
if (s <> nil) then begin
|
||||
checkDeprecated(n, s);
|
||||
case s.kind of
|
||||
skMacro: begin
|
||||
include(s.flags, sfUsed);
|
||||
result := semMacroExpr(c, n, s);
|
||||
end;
|
||||
skTemplate: begin
|
||||
include(s.flags, sfUsed);
|
||||
// transform
|
||||
// nkMacroStmt(nkCall(a...), stmt, b...)
|
||||
// to
|
||||
// nkCall(a..., stmt, b...)
|
||||
result := newNodeI(nkCall, n.info);
|
||||
addSon(result, a);
|
||||
if isCallExpr(n.sons[0]) then begin
|
||||
for i := 1 to sonsLen(n.sons[0])-1 do
|
||||
addSon(result, n.sons[0].sons[i]);
|
||||
end;
|
||||
for i := 1 to sonsLen(n)-1 do
|
||||
addSon(result, n.sons[i]);
|
||||
pushInfoContext(n.info);
|
||||
result := evalTemplate(c, result, s);
|
||||
popInfoContext();
|
||||
end;
|
||||
else
|
||||
liMessage(n.info, errXisNoMacroOrTemplate, s.name.s);
|
||||
end
|
||||
end
|
||||
else
|
||||
liMessage(n.info, errInvalidExpressionX,
|
||||
renderTree(a, {@set}[renderNoComments]));
|
||||
end;
|
||||
|
||||
function semExpr(c: PContext; n: PNode; flags: TExprFlags = {@set}[]): PNode;
|
||||
var
|
||||
s: PSym;
|
||||
|
|
@ -1174,6 +1240,7 @@ begin
|
|||
checkMinSonsLen(n, 1);
|
||||
s := qualifiedLookup(c, n.sons[0], false);
|
||||
if (s <> nil) then begin
|
||||
checkDeprecated(n, s);
|
||||
case s.kind of
|
||||
skMacro: begin
|
||||
include(s.flags, sfUsed);
|
||||
|
|
@ -1200,6 +1267,9 @@ begin
|
|||
end
|
||||
else result := semIndirectOp(c, n);
|
||||
end;
|
||||
nkMacroStmt: begin
|
||||
result := semMacroStmt(c, n);
|
||||
end;
|
||||
nkBracketExpr: begin
|
||||
checkMinSonsLen(n, 1);
|
||||
s := qualifiedLookup(c, n.sons[0], false);
|
||||
|
|
@ -1221,7 +1291,7 @@ begin
|
|||
nkPar: begin
|
||||
case checkPar(n) of
|
||||
paNone: result := nil;
|
||||
paTuplePositions: result := semTuplePositionsConstr(c, n);
|
||||
paTuplePositions: result := semTuplePositionsConstr(c, n);
|
||||
paTupleFields: result := semTupleFieldsConstr(c, n);
|
||||
paSingle: result := semExpr(c, n.sons[0]);
|
||||
end;
|
||||
|
|
|
|||
|
|
@ -406,6 +406,8 @@ begin
|
|||
else result := copyTree(s.ast); // BUGFIX
|
||||
end
|
||||
end
|
||||
else if s.kind = skProc then // BUGFIX
|
||||
result := n
|
||||
end;
|
||||
nkCharLit..nkNilLit: result := copyNode(n);
|
||||
nkIfExpr: result := getConstIfExpr(module, n);
|
||||
|
|
|
|||
|
|
@ -58,10 +58,29 @@ begin
|
|||
end
|
||||
end;
|
||||
|
||||
procedure genericToConcreteTypeKind(t: PType);
|
||||
var
|
||||
body: PNode;
|
||||
begin
|
||||
if (t.kind = tyGeneric) and (t.sym <> nil) then begin
|
||||
body := t.sym.ast.sons[2];
|
||||
case body.kind of
|
||||
nkObjectTy: t.kind := tyObject;
|
||||
nkTupleTy: t.kind := tyTuple;
|
||||
nkRefTy: t.kind := tyRef;
|
||||
nkPtrTy: t.kind := tyPtr;
|
||||
nkVarTy: t.kind := tyVar;
|
||||
nkProcTy: t.kind := tyProc;
|
||||
else InternalError('genericToConcreteTypeKind');
|
||||
end
|
||||
end
|
||||
end;
|
||||
|
||||
function instantiateType(c: PInstantiateClosure; typ: PType): PType;
|
||||
var
|
||||
i: int;
|
||||
begin
|
||||
if typ = nil then begin result := nil; exit end;
|
||||
result := PType(idTableGet(c.typeMap, typ));
|
||||
if result <> nil then exit;
|
||||
//if typ.kind = tyOpenArray then
|
||||
|
|
@ -73,6 +92,7 @@ begin
|
|||
result.sons[i] := instantiateType(c, result.sons[i]);
|
||||
if result.n <> nil then
|
||||
result.n := instTypeNode(c, result.n);
|
||||
genericToConcreteTypeKind(result);
|
||||
end
|
||||
else
|
||||
result := typ;
|
||||
|
|
@ -272,6 +292,20 @@ begin
|
|||
result := instantiateType(c, t);
|
||||
end;
|
||||
|
||||
function newInstantiateClosure(p: PContext;
|
||||
const instantiator: TLineInfo): PInstantiateClosure;
|
||||
begin
|
||||
new(result);
|
||||
{@ignore}
|
||||
fillChar(result^, sizeof(result^), 0);
|
||||
{@emit}
|
||||
InitIdTable(result.typeMap);
|
||||
InitIdTable(result.symMap);
|
||||
result.fn := nil;
|
||||
result.instantiator := instantiator;
|
||||
result.module := p.module;
|
||||
end;
|
||||
|
||||
function partialSpecialization(c: PContext; n: PNode; s: PSym): PNode;
|
||||
begin
|
||||
result := n;
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ begin
|
|||
end;
|
||||
if result = nil then result := newNodeI(nkNilLit, n.info);
|
||||
// The ``when`` statement implements the mechanism for platform dependant
|
||||
// code. Thus we try to ensure here consistent ID distribution after the
|
||||
// code. Thus we try to ensure here consistent ID allocation after the
|
||||
// ``when`` statement.
|
||||
IDsynchronizationPoint(200);
|
||||
end;
|
||||
|
|
@ -59,9 +59,11 @@ begin
|
|||
case it.kind of
|
||||
nkElifBranch: begin
|
||||
checkSonsLen(it, 2);
|
||||
openScope(c.tab);
|
||||
it.sons[0] := semExprWithType(c, it.sons[0]);
|
||||
checkBool(it.sons[0]);
|
||||
it.sons[1] := semStmtScope(c, it.sons[1])
|
||||
it.sons[1] := semStmt(c, it.sons[1]);
|
||||
closeScope(c.tab);
|
||||
end;
|
||||
nkElse: begin
|
||||
if sonsLen(it) = 1 then it.sons[0] := semStmtScope(c, it.sons[0])
|
||||
|
|
@ -144,7 +146,7 @@ begin
|
|||
// now parse the string literal and substitute symbols:
|
||||
a := strStart;
|
||||
repeat
|
||||
b := findSubStr(marker, str, a);
|
||||
b := strutils.find(str, marker, a);
|
||||
if b < strStart then
|
||||
sub := ncopy(str, a)
|
||||
else
|
||||
|
|
@ -153,7 +155,7 @@ begin
|
|||
addSon(result, newStrNode(nkStrLit, sub));
|
||||
|
||||
if b < strStart then break;
|
||||
c := findSubStr(marker, str, b+1);
|
||||
c := strutils.find(str, marker, b+1);
|
||||
if c < strStart then
|
||||
sub := ncopy(str, b+1)
|
||||
else
|
||||
|
|
@ -179,11 +181,13 @@ function semWhile(c: PContext; n: PNode): PNode;
|
|||
begin
|
||||
result := n;
|
||||
checkSonsLen(n, 2);
|
||||
openScope(c.tab);
|
||||
n.sons[0] := semExprWithType(c, n.sons[0]);
|
||||
CheckBool(n.sons[0]);
|
||||
inc(c.p.nestedLoopCounter);
|
||||
n.sons[1] := semStmtScope(c, n.sons[1]);
|
||||
n.sons[1] := semStmt(c, n.sons[1]);
|
||||
dec(c.p.nestedLoopCounter);
|
||||
closeScope(c.tab);
|
||||
end;
|
||||
|
||||
function semCase(c: PContext; n: PNode): PNode;
|
||||
|
|
@ -197,6 +201,7 @@ begin
|
|||
// check selector:
|
||||
result := n;
|
||||
checkMinSonsLen(n, 2);
|
||||
openScope(c.tab);
|
||||
n.sons[0] := semExprWithType(c, n.sons[0]);
|
||||
chckCovered := false;
|
||||
covered := 0;
|
||||
|
|
@ -216,7 +221,7 @@ begin
|
|||
end;
|
||||
nkElifBranch: begin
|
||||
chckCovered := false;
|
||||
checkSonsLen(n, 2);
|
||||
checkSonsLen(x, 2);
|
||||
x.sons[0] := semExprWithType(c, x.sons[0]);
|
||||
checkBool(x.sons[0]);
|
||||
x.sons[1] := semStmtScope(c, x.sons[1])
|
||||
|
|
@ -231,6 +236,7 @@ begin
|
|||
end;
|
||||
if chckCovered and (covered <> lengthOrd(n.sons[0].typ)) then
|
||||
liMessage(n.info, errNotAllCasesCovered);
|
||||
closeScope(c.tab);
|
||||
end;
|
||||
|
||||
function semAsgn(c: PContext; n: PNode): PNode;
|
||||
|
|
@ -388,7 +394,7 @@ begin
|
|||
if (a.kind <> nkIdentDefs) and (a.kind <> nkVarTuple) then IllFormedAst(a);
|
||||
checkMinSonsLen(a, 3);
|
||||
len := sonsLen(a);
|
||||
if a.sons[len-2] <> nil then
|
||||
if a.sons[len-2] <> nil then
|
||||
typ := semTypeNode(c, a.sons[len-2], nil)
|
||||
else
|
||||
typ := nil;
|
||||
|
|
@ -401,6 +407,8 @@ begin
|
|||
end
|
||||
else
|
||||
def := nil;
|
||||
if not typeAllowed(typ, skVar) then
|
||||
liMessage(a.info, errXisNoType, typeToString(typ));
|
||||
tup := skipGeneric(typ);
|
||||
if a.kind = nkVarTuple then begin
|
||||
if tup.kind <> tyTuple then liMessage(a.info, errXExpected, 'tuple');
|
||||
|
|
@ -408,7 +416,7 @@ begin
|
|||
liMessage(a.info, errWrongNumberOfVariables);
|
||||
end;
|
||||
for j := 0 to len-3 do begin
|
||||
if c.p.owner = nil then begin
|
||||
if (c.p.owner.kind = skModule) then begin
|
||||
v := semIdentWithPragma(c, skVar, a.sons[j], {@set}[sfStar, sfMinus]);
|
||||
include(v.flags, sfGlobal);
|
||||
end
|
||||
|
|
@ -441,7 +449,7 @@ begin
|
|||
if a.kind = nkCommentStmt then continue;
|
||||
if (a.kind <> nkConstDef) then IllFormedAst(a);
|
||||
checkSonsLen(a, 3);
|
||||
if (c.p.owner = nil) then begin
|
||||
if (c.p.owner.kind = skModule) then begin
|
||||
v := semIdentWithPragma(c, skConst, a.sons[0], {@set}[sfStar, sfMinus]);
|
||||
include(v.flags, sfGlobal);
|
||||
end
|
||||
|
|
@ -456,6 +464,8 @@ begin
|
|||
def := fitRemoveHiddenConv(c, typ, def);
|
||||
end
|
||||
else typ := def.typ;
|
||||
if not typeAllowed(typ, skConst) then
|
||||
liMessage(a.info, errXisNoType, typeToString(typ));
|
||||
|
||||
v.typ := typ;
|
||||
v.ast := def; // no need to copy
|
||||
|
|
@ -480,6 +490,7 @@ begin
|
|||
result := n;
|
||||
checkMinSonsLen(n, 3);
|
||||
len := sonsLen(n);
|
||||
openScope(c.tab);
|
||||
if n.sons[len-2].kind = nkRange then begin
|
||||
checkSonsLen(n.sons[len-2], 2);
|
||||
// convert ``in 3..5`` to ``in countup(3, 5)``
|
||||
|
|
@ -500,7 +511,6 @@ begin
|
|||
end;
|
||||
n.sons[len-2] := semExprWithType(c, n.sons[len-2]);
|
||||
iter := skipGeneric(n.sons[len-2].typ);
|
||||
openScope(c.tab);
|
||||
if iter.kind <> tyTuple then begin
|
||||
if len <> 3 then liMessage(n.info, errWrongNumberOfVariables);
|
||||
v := newSymS(skForVar, n.sons[0], c);
|
||||
|
|
@ -573,7 +583,7 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
procedure semGenericParamList(c: PContext; n: PNode);
|
||||
procedure semGenericParamList(c: PContext; n: PNode; father: PType = nil);
|
||||
var
|
||||
i: int;
|
||||
s: PSym;
|
||||
|
|
@ -590,6 +600,7 @@ begin
|
|||
s.typ := newTypeS(tyGenericParam, c);
|
||||
s.typ.sym := s;
|
||||
end;
|
||||
if father <> nil then addSon(father, s.typ);
|
||||
s.position := i;
|
||||
n.sons[i] := newSymNode(s);
|
||||
addDecl(c, s);
|
||||
|
|
@ -631,7 +642,7 @@ begin
|
|||
if a.kind = nkCommentStmt then continue;
|
||||
if (a.kind <> nkTypeDef) then IllFormedAst(a);
|
||||
checkSonsLen(a, 3);
|
||||
if (c.p.owner = nil) then begin
|
||||
if (c.p.owner.kind = skModule) then begin
|
||||
s := semIdentWithPragma(c, skType, a.sons[0], {@set}[sfStar, sfMinus]);
|
||||
include(s.flags, sfGlobal);
|
||||
end
|
||||
|
|
@ -665,7 +676,8 @@ begin
|
|||
openScope(c.tab);
|
||||
pushOwner(s);
|
||||
s.typ.kind := tyGeneric;
|
||||
semGenericParamList(c, a.sons[1]);
|
||||
semGenericParamList(c, a.sons[1], s.typ);
|
||||
addSon(s.typ, nil);
|
||||
// process the type body for symbol lookup of generic params
|
||||
// we can use the same algorithm as for template parameters:
|
||||
a.sons[2] := resolveTemplateParams(c, a.sons[2]);
|
||||
|
|
@ -736,7 +748,7 @@ var
|
|||
begin
|
||||
result := n;
|
||||
checkSonsLen(n, codePos+1);
|
||||
if c.p.owner <> nil then
|
||||
if c.p.owner.kind <> skModule then
|
||||
liMessage(n.info, errIteratorNotAllowed);
|
||||
oldP := c.p; // restore later
|
||||
s := semIdentVis(c, skIterator, n.sons[0], {@set}[sfStar]);
|
||||
|
|
@ -857,7 +869,7 @@ var
|
|||
begin
|
||||
result := n;
|
||||
checkSonsLen(n, codePos+1);
|
||||
if c.p.owner = nil then begin
|
||||
if c.p.owner.kind = skModule then begin
|
||||
s := semIdentVis(c, kind, n.sons[0], {@set}[sfStar]);
|
||||
include(s.flags, sfGlobal);
|
||||
end
|
||||
|
|
@ -885,7 +897,7 @@ begin
|
|||
proto := SearchForProc(c, s, c.tab.tos-2); // -2 because we have a scope open
|
||||
// for parameters
|
||||
if proto = nil then begin
|
||||
if oldP.owner <> nil then // we are in a nested proc
|
||||
if oldP.owner.kind <> skModule then // we are in a nested proc
|
||||
s.typ.callConv := ccClosure
|
||||
else
|
||||
s.typ.callConv := lastOptionEntry(c).defaultCC;
|
||||
|
|
@ -1025,7 +1037,7 @@ begin
|
|||
if nfSem in n.flags then exit;
|
||||
case n.kind of
|
||||
nkAsgn: result := semAsgn(c, n);
|
||||
nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand:
|
||||
nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkMacroStmt:
|
||||
result := semCommand(c, n);
|
||||
nkEmpty, nkCommentStmt, nkNilLit: begin end;
|
||||
nkBlockStmt: result := semBlock(c, n);
|
||||
|
|
@ -1051,8 +1063,7 @@ begin
|
|||
nkDiscardStmt: result := semDiscard(c, n);
|
||||
nkWhileStmt: result := semWhile(c, n);
|
||||
nkTryStmt: result := semTry(c, n);
|
||||
nkBreakStmt, nkContinueStmt:
|
||||
result := semBreakOrContinue(c, n);
|
||||
nkBreakStmt, nkContinueStmt: result := semBreakOrContinue(c, n);
|
||||
nkForStmt: result := semFor(c, n);
|
||||
nkCaseStmt: result := semCase(c, n);
|
||||
nkReturnStmt: result := semReturn(c, n);
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ var
|
|||
i, j, len, counter: int;
|
||||
params, p, paramKind: PNode;
|
||||
begin
|
||||
if c.p.owner = nil then begin
|
||||
if c.p.owner.kind = skModule then begin
|
||||
s := semIdentVis(c, skTemplate, n.sons[0], {@set}[sfStar]);
|
||||
include(s.flags, sfGlobal);
|
||||
end
|
||||
|
|
|
|||
|
|
@ -51,10 +51,12 @@ begin
|
|||
e := newSymS(skEnumField, n.sons[i].sons[0], c);
|
||||
v := semConstExpr(c, n.sons[i].sons[1]);
|
||||
x := getOrdValue(v);
|
||||
if (x <> counter) and (i <> 1) then
|
||||
include(result.flags, tfEnumHasWholes);
|
||||
if x < counter then
|
||||
liMessage(n.sons[i].info, errInvalidOrderInEnumX, e.name.s);
|
||||
if i <> 1 then begin
|
||||
if (x <> counter) then
|
||||
include(result.flags, tfEnumHasWholes);
|
||||
if x < counter then
|
||||
liMessage(n.sons[i].info, errInvalidOrderInEnumX, e.name.s);
|
||||
end;
|
||||
counter := x;
|
||||
end;
|
||||
nkSym: e := n.sons[i].sym;
|
||||
|
|
@ -247,7 +249,7 @@ begin
|
|||
end
|
||||
end
|
||||
end;
|
||||
|
||||
(*
|
||||
function instGenericAux(c: PContext; templ, actual: PNode;
|
||||
sym: PSym): PNode;
|
||||
var
|
||||
|
|
@ -271,34 +273,48 @@ begin
|
|||
result.sons[i] := instGenericAux(c, templ.sons[i], actual, sym);
|
||||
end
|
||||
end
|
||||
end;
|
||||
end; *)
|
||||
|
||||
function semGeneric(c: PContext; n: PNode; s: PSym; prev: PType): PType;
|
||||
var
|
||||
i: int;
|
||||
elem: PType;
|
||||
inst: PNode;
|
||||
cl: PInstantiateClosure;
|
||||
begin
|
||||
if (s.typ = nil) or (s.typ.kind <> tyGeneric) then
|
||||
liMessage(n.info, errCannotInstantiateX, s.name.s);
|
||||
result := newOrPrevType(tyGenericInst, prev, c); // new ID...
|
||||
result.containerID := s.typ.containerID; // ... but the same containerID
|
||||
result.sym := s;
|
||||
if (s.typ.containerID = 0) then
|
||||
InternalError(n.info, 'semtypes.semGeneric');
|
||||
if (s.typ.containerID = 0) then InternalError(n.info, 'semtypes.semGeneric');
|
||||
cl := newInstantiateClosure(c, n.info);
|
||||
// check the number of supplied arguments suffices:
|
||||
if sonsLen(n) <> sonsLen(s.typ) then begin
|
||||
//MessageOut('n: ' +{&} toString(sonsLen(n)) +{&} ' s: '
|
||||
// +{&} toString(sonsLen(s.typ)));
|
||||
liMessage(n.info, errWrongNumberOfTypeParams);
|
||||
end;
|
||||
// a generic type should be instantiated with itself:
|
||||
// idTablePut(cl.typeMap, s.typ, result);
|
||||
// iterate over arguments:
|
||||
for i := 1 to sonsLen(n)-1 do begin
|
||||
elem := semTypeNode(c, n.sons[i], nil);
|
||||
if elem.kind = tyGenericParam then
|
||||
result.kind := tyGeneric; // prevend type from instantiation
|
||||
result.kind := tyGeneric // prevend type from instantiation
|
||||
else
|
||||
idTablePut(cl.typeMap, s.typ.sons[i-1], elem);
|
||||
addSon(result, elem);
|
||||
end;
|
||||
if s.ast <> nil then begin
|
||||
if (result.kind = tyGenericInst) then begin
|
||||
inst := instGenericAux(c, s.ast.sons[2], n, s);
|
||||
// inst := instGenericAux(c, s.ast.sons[2], n, s);
|
||||
internalError(n.info, 'Generic containers not implemented');
|
||||
// XXX: implementation does not work this way
|
||||
// we need to do the following:
|
||||
// traverse and copy the type and replace any tyGenericParam type
|
||||
// does checking of instantiated type for us:
|
||||
elem := semTypeNode(c, inst, nil);
|
||||
elem := instantiateType(cl, s.typ); //semTypeNode(c, inst, nil);
|
||||
elem.id := result.containerID;
|
||||
addSon(result, elem);
|
||||
end
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ var
|
|||
begin
|
||||
result := msgKindToString(errTypeMismatch);
|
||||
for i := 1 to sonsLen(n)-1 do begin
|
||||
debug(n.sons[i].typ);
|
||||
//debug(n.sons[i].typ);
|
||||
add(result, typeToString(n.sons[i].typ));
|
||||
if i <> sonsLen(n)-1 then add(result, ', ');
|
||||
end;
|
||||
|
|
@ -361,9 +361,7 @@ begin // is a subtype of f?
|
|||
tyProc: begin
|
||||
if (sonsLen(f) = sonsLen(a)) and (f.callconv = a.callconv) then begin
|
||||
// Note: We have to do unification for the parameters before the
|
||||
// return type! Otherwise it'd be counter-intuitive for the standard
|
||||
// Nimrod syntax. For the C-based syntax it IS counter-intuitive.
|
||||
// But that is one of the reasons a standard syntax was picked.
|
||||
// return type!
|
||||
result := isEqual; // start with maximum; also correct for no
|
||||
// params at all
|
||||
for i := 1 to sonsLen(f)-1 do begin
|
||||
|
|
@ -433,8 +431,9 @@ begin // is a subtype of f?
|
|||
end;
|
||||
tyAnyEnum: begin
|
||||
case a.kind of
|
||||
tyRange: result := typeRel(mapping, f, base(a));
|
||||
tyEnum: result := isSubtype;
|
||||
tyRange: result := typeRel(mapping, f, base(a));
|
||||
tyEnum: result := isSubtype;
|
||||
tyAnyEnum: result := isEqual;
|
||||
else begin end
|
||||
end
|
||||
end;
|
||||
|
|
@ -726,11 +725,11 @@ begin
|
|||
end
|
||||
else begin
|
||||
setSon(m.call, formal.position+1, arg);
|
||||
end;
|
||||
inc(f);
|
||||
end
|
||||
end
|
||||
end;
|
||||
inc(a);
|
||||
inc(f);
|
||||
end;
|
||||
// iterate over all formal params and check all are provided:
|
||||
f := 1;
|
||||
|
|
|
|||
|
|
@ -269,14 +269,14 @@ begin
|
|||
j := i+1;
|
||||
while (j <= length(f)+strStart-1) and (f[j] <> '}') do inc(j);
|
||||
key := ncopy(f, i+2+strStart-1, j-1+strStart-1);
|
||||
result := result +{&} getValue(t, flags, key);
|
||||
add(result, getValue(t, flags, key));
|
||||
i := j+1
|
||||
end;
|
||||
'a'..'z', 'A'..'Z', #128..#255, '_': begin
|
||||
j := i+1;
|
||||
while (j <= length(f)+strStart-1) and (f[j] in PatternChars) do inc(j);
|
||||
key := ncopy(f, i+1+strStart-1, j-1+strStart-1);
|
||||
result := result +{&} getValue(t, flags, key);
|
||||
add(result, getValue(t, flags, key));
|
||||
i := j
|
||||
end
|
||||
else begin
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ function cmp(const x, y: string): int;
|
|||
function cmpIgnoreCase(const x, y: string): int;
|
||||
|
||||
function format(const f: string; const args: array of string): string;
|
||||
procedure addf(var result: string; const f: string; args: array of string);
|
||||
|
||||
function toHex(x: BiggestInt; len: int): string;
|
||||
function toOctal(value: Char): string;
|
||||
|
|
@ -47,7 +48,7 @@ function ToString(b: Boolean): string; overload;
|
|||
|
||||
function IntToStr(i: BiggestInt; minChars: int): string;
|
||||
|
||||
function findSubStr(const sub, s: string; start: int = 1): int;
|
||||
function find(const s, sub: string; start: int = 1): int; overload;
|
||||
function replaceStr(const s, search, by: string): string;
|
||||
procedure deleteStr(var s: string; first, last: int);
|
||||
|
||||
|
|
@ -81,8 +82,8 @@ implementation
|
|||
|
||||
function quoteIfContainsWhite(const s: string): string;
|
||||
begin
|
||||
if ((findSubStr(' ', s) >= strStart)
|
||||
or (findSubStr(#9, s) >= strStart)) and (s[strStart] <> '"') then
|
||||
if ((find(s, ' ') >= strStart)
|
||||
or (find(s, #9) >= strStart)) and (s[strStart] <> '"') then
|
||||
result := '"' +{&} s +{&} '"'
|
||||
else
|
||||
result := s
|
||||
|
|
@ -247,7 +248,7 @@ begin
|
|||
result := result + s[i]
|
||||
end;
|
||||
|
||||
function findSubStr(const sub, s: string; start: int = 1): int;
|
||||
function find(const s, sub: string; start: int = 1): int;
|
||||
var
|
||||
i, j, M, N: int;
|
||||
begin
|
||||
|
|
@ -277,7 +278,7 @@ begin
|
|||
result := '';
|
||||
i := 1;
|
||||
repeat
|
||||
j := findSubStr(search, s, i);
|
||||
j := find(s, search, i);
|
||||
if j = 0 then begin
|
||||
// copy the rest:
|
||||
result := result + copy(s, i, length(s) - i + 1);
|
||||
|
|
@ -475,7 +476,7 @@ begin
|
|||
until false
|
||||
end;
|
||||
|
||||
function find(const x: string; const inArray: array of string): int;
|
||||
function find(const x: string; const inArray: array of string): int; overload;
|
||||
var
|
||||
i: int;
|
||||
y: string;
|
||||
|
|
@ -491,30 +492,29 @@ begin
|
|||
result := -1
|
||||
end;
|
||||
|
||||
function format(const f: string; const args: array of string): string;
|
||||
procedure addf(var result: string; const f: string; args: array of string);
|
||||
const
|
||||
PatternChars = ['a'..'z', 'A'..'Z', '0'..'9', '_', #128..#255];
|
||||
var
|
||||
i, j, x: int;
|
||||
begin
|
||||
result := '';
|
||||
i := 1;
|
||||
while i <= length(f) do
|
||||
if f[i] = '$' then begin
|
||||
case f[i+1] of
|
||||
'$': begin
|
||||
result := result + '$';
|
||||
addChar(result, '$');
|
||||
inc(i, 2);
|
||||
end;
|
||||
'1'..'9': begin
|
||||
result := result + args[ord(f[i+1]) - ord('0') - 1];
|
||||
add(result, args[ord(f[i+1]) - ord('0') - 1]);
|
||||
inc(i, 2);
|
||||
end;
|
||||
'{': begin
|
||||
j := i+1;
|
||||
while (j <= length(f)) and (f[j] <> '}') do inc(j);
|
||||
x := find(ncopy(f, i+2, j-1), args);
|
||||
if (x >= 0) and (x < high(args)) then result := result + args[x+1]
|
||||
if (x >= 0) and (x < high(args)) then add(result, args[x+1])
|
||||
else raise EInvalidFormatStr.create('');
|
||||
i := j+1
|
||||
end;
|
||||
|
|
@ -522,7 +522,7 @@ begin
|
|||
j := i+1;
|
||||
while (j <= length(f)) and (f[j] in PatternChars) do inc(j);
|
||||
x := find(ncopy(f, i+1, j-1), args);
|
||||
if (x >= 0) and (x < high(args)) then result := result + args[x+1]
|
||||
if (x >= 0) and (x < high(args)) then add(result, args[x+1])
|
||||
else raise EInvalidFormatStr.create(ncopy(f, i+1, j-1));
|
||||
i := j
|
||||
end
|
||||
|
|
@ -530,11 +530,17 @@ begin
|
|||
end
|
||||
end
|
||||
else begin
|
||||
result := result + f[i];
|
||||
addChar(result, f[i]);
|
||||
inc(i)
|
||||
end
|
||||
end;
|
||||
|
||||
function format(const f: string; const args: array of string): string;
|
||||
begin
|
||||
result := '';
|
||||
addf(result, f, args)
|
||||
end;
|
||||
|
||||
{@ignore}
|
||||
{$ifopt Q-} {$Q+}
|
||||
{$else} {$define Q_off}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ unit transf;
|
|||
// * inlines iterators
|
||||
// * inlines constants
|
||||
// * performes contant folding
|
||||
// * introduces nkHiddenDeref, nkHiddenSubConv, etc.
|
||||
|
||||
interface
|
||||
|
||||
|
|
@ -446,12 +447,51 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
function skipPassAsOpenArray(n: PNode): PNode;
|
||||
begin
|
||||
result := n;
|
||||
while result.kind = nkPassAsOpenArray do
|
||||
result := result.sons[0]
|
||||
end;
|
||||
|
||||
type
|
||||
TPutArgInto = (paDirectMapping, paFastAsgn, paVarAsgn);
|
||||
|
||||
function putArgInto(arg: PNode; formal: PType): TPutArgInto;
|
||||
// This analyses how to treat the mapping "formal <-> arg" in an
|
||||
// inline context.
|
||||
var
|
||||
i: int;
|
||||
begin
|
||||
if skipGeneric(formal).kind = tyOpenArray then begin
|
||||
result := paDirectMapping; // XXX really correct?
|
||||
// what if ``arg`` has side-effects?
|
||||
exit
|
||||
end;
|
||||
case arg.kind of
|
||||
nkEmpty..nkNilLit: result := paDirectMapping;
|
||||
nkPar, nkCurly, nkBracket: begin
|
||||
result := paFastAsgn;
|
||||
for i := 0 to sonsLen(arg)-1 do
|
||||
if putArgInto(arg.sons[i], formal) <> paDirectMapping then
|
||||
exit;
|
||||
result := paDirectMapping;
|
||||
end;
|
||||
else begin
|
||||
if skipGeneric(formal).kind = tyVar then
|
||||
result := paVarAsgn
|
||||
else
|
||||
result := paFastAsgn
|
||||
end
|
||||
end
|
||||
end;
|
||||
|
||||
function transformFor(c: PTransf; n: PNode): PNode;
|
||||
// generate access statements for the parameters (unless they are constant)
|
||||
// put mapping from formal parameters to actual parameters
|
||||
var
|
||||
i, len: int;
|
||||
call, e, v, body: PNode;
|
||||
call, v, body, arg: PNode;
|
||||
newC: PTransCon;
|
||||
temp, formal: PSym;
|
||||
begin
|
||||
|
|
@ -473,24 +513,24 @@ begin
|
|||
// generate access statements for the parameters (unless they are constant)
|
||||
pushTransCon(c, newC);
|
||||
for i := 1 to sonsLen(call)-1 do begin
|
||||
e := getConstExpr(c.module, call.sons[i]);
|
||||
arg := skipPassAsOpenArray(transform(c, call.sons[i]));
|
||||
formal := skipGeneric(newC.owner.typ).n.sons[i].sym;
|
||||
if e <> nil then
|
||||
IdNodeTablePut(newC.mapping, formal, e)
|
||||
else if (skipConv(call.sons[i]).kind = nkSym) then begin
|
||||
// since parameters cannot be modified, we can identify the formal and
|
||||
// the actual params
|
||||
IdNodeTablePut(newC.mapping, formal, call.sons[i]);
|
||||
end
|
||||
else begin
|
||||
// generate a temporary and produce an assignment statement:
|
||||
temp := newTemp(c, formal.typ, formal.info);
|
||||
addVar(v, newSymNode(temp));
|
||||
// BUGFIX: do not copy call.sons[i], but transform it!
|
||||
addSon(result, newAsgnStmt(c, newSymNode(temp),
|
||||
transform(c, call.sons[i])));
|
||||
IdNodeTablePut(newC.mapping, formal, newSymNode(temp)); // BUGFIX
|
||||
end
|
||||
//if IdentEq(newc.Owner.name, 'items') then
|
||||
// liMessage(arg.info, warnUser, 'items: ' + nodeKindToStr[arg.kind]);
|
||||
case putArgInto(arg, formal.typ) of
|
||||
paDirectMapping: IdNodeTablePut(newC.mapping, formal, arg);
|
||||
paFastAsgn: begin
|
||||
// generate a temporary and produce an assignment statement:
|
||||
temp := newTemp(c, formal.typ, formal.info);
|
||||
addVar(v, newSymNode(temp));
|
||||
addSon(result, newAsgnStmt(c, newSymNode(temp), arg));
|
||||
IdNodeTablePut(newC.mapping, formal, newSymNode(temp));
|
||||
end;
|
||||
paVarAsgn: begin
|
||||
assert(skipGeneric(formal.typ).kind = tyVar);
|
||||
InternalError(arg.info, 'not implemented: pass to var parameter');
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
body := newC.owner.ast.sons[codePos];
|
||||
pushInfoContext(n.info);
|
||||
|
|
@ -668,7 +708,7 @@ function transformCase(c: PTransf; n: PNode): PNode;
|
|||
// removes `elif` branches of a case stmt
|
||||
var
|
||||
len, i, j: int;
|
||||
ifs: PNode;
|
||||
ifs, elsen: PNode;
|
||||
begin
|
||||
len := sonsLen(n);
|
||||
i := len-1;
|
||||
|
|
@ -678,9 +718,11 @@ begin
|
|||
if (n.sons[i].kind <> nkOfBranch) then
|
||||
InternalError(n.sons[i].info, 'transformCase');
|
||||
ifs := newNodeI(nkIfStmt, n.sons[i+1].info);
|
||||
elsen := newNodeI(nkElse, ifs.info);
|
||||
for j := i+1 to len-1 do addSon(ifs, n.sons[j]);
|
||||
setLength(n.sons, i+2);
|
||||
n.sons[i+1] := ifs;
|
||||
addSon(elsen, ifs);
|
||||
n.sons[i+1] := elsen;
|
||||
end;
|
||||
result := n;
|
||||
for j := 0 to sonsLen(n)-1 do result.sons[j] := transform(c, n.sons[j]);
|
||||
|
|
|
|||
106
nim/types.pas
106
nim/types.pas
|
|
@ -1,12 +1,11 @@
|
|||
//
|
||||
//
|
||||
// The Nimrod Compiler
|
||||
// (c) Copyright 2008 Andreas Rumpf
|
||||
// (c) Copyright 2009 Andreas Rumpf
|
||||
//
|
||||
// See the file "copying.txt", included in this
|
||||
// distribution, for details about the copyright.
|
||||
//
|
||||
|
||||
unit types;
|
||||
|
||||
// this module contains routines for accessing and iterating over types
|
||||
|
|
@ -47,7 +46,6 @@ function mutateType(t: PType; iter: TTypeMutator; closure: PObject): PType;
|
|||
// Returns result of `iter`.
|
||||
|
||||
|
||||
|
||||
function SameType(x, y: PType): Boolean;
|
||||
function SameTypeOrNil(a, b: PType): Boolean;
|
||||
|
||||
|
|
@ -115,6 +113,8 @@ function analyseObjectWithTypeField(t: PType): TTypeFieldResult;
|
|||
// made or intializing of the type field suffices or if there is no type field
|
||||
// at all in this type.
|
||||
|
||||
function typeAllowed(t: PType; kind: TSymKind): bool;
|
||||
|
||||
implementation
|
||||
|
||||
function InvalidGenericInst(f: PType): bool;
|
||||
|
|
@ -185,11 +185,13 @@ begin
|
|||
n := sym.typ.n;
|
||||
for i := 1 to sonsLen(n)-1 do begin
|
||||
p := n.sons[i];
|
||||
assert(p.kind = nkSym);
|
||||
result := result +{&} p.sym.name.s +{&} ': ' +{&} typeToString(p.sym.typ);
|
||||
if i <> sonsLen(n)-1 then result := result + ', ';
|
||||
if (p.kind <> nkSym) then InternalError('getProcHeader');
|
||||
add(result, p.sym.name.s);
|
||||
add(result, ': ');
|
||||
add(result, typeToString(p.sym.typ));
|
||||
if i <> sonsLen(n)-1 then add(result, ', ');
|
||||
end;
|
||||
result := result + ')';
|
||||
addChar(result, ')');
|
||||
if n.sons[0].typ <> nil then
|
||||
result := result +{&} ': ' +{&} typeToString(n.sons[0].typ);
|
||||
end;
|
||||
|
|
@ -928,6 +930,96 @@ begin
|
|||
end
|
||||
end;
|
||||
|
||||
function typeAllowedAux(var marker: TIntSet; t: PType;
|
||||
kind: TSymKind): bool; forward;
|
||||
|
||||
function typeAllowedNode(var marker: TIntSet; n: PNode; kind: TSymKind): bool;
|
||||
var
|
||||
i: int;
|
||||
begin
|
||||
result := true;
|
||||
if n <> nil then begin
|
||||
result := typeAllowedAux(marker, n.typ, kind);
|
||||
if result then
|
||||
case n.kind of
|
||||
nkNone..nkNilLit: begin end;
|
||||
else begin
|
||||
for i := 0 to sonsLen(n)-1 do begin
|
||||
result := typeAllowedNode(marker, n.sons[i], kind);
|
||||
if not result then exit
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end;
|
||||
|
||||
function typeAllowedAux(var marker: TIntSet; t: PType; kind: TSymKind): bool;
|
||||
var
|
||||
i: int;
|
||||
begin
|
||||
assert(kind in [skVar, skConst, skParam]);
|
||||
result := true;
|
||||
if t = nil then exit;
|
||||
// if we have already checked the type, return true, because we stop the
|
||||
// evaluation if something is wrong:
|
||||
if IntSetContainsOrIncl(marker, t.id) then exit;
|
||||
case skipGeneric(t).kind of
|
||||
tyVar: begin
|
||||
case skipGeneric(t.sons[0]).kind of
|
||||
tyVar: result := false; // ``var var`` is always an invalid type:
|
||||
tyOpenArray: result := (kind = skParam) and
|
||||
typeAllowedAux(marker, t.sons[0], kind);
|
||||
else result := (kind <> skConst) and
|
||||
typeAllowedAux(marker, t.sons[0], kind);
|
||||
end
|
||||
end;
|
||||
tyProc: begin
|
||||
for i := 1 to sonsLen(t)-1 do begin
|
||||
result := typeAllowedAux(marker, t.sons[i], skParam);
|
||||
if not result then exit;
|
||||
end;
|
||||
if t.sons[0] <> nil then
|
||||
result := typeAllowedAux(marker, t.sons[0], skVar)
|
||||
end;
|
||||
tyGeneric, tyGenericParam, tyForward, tyNone: result := false;
|
||||
tyEmpty, tyNil: result := kind = skConst;
|
||||
tyString, tyBool, tyChar, tyEnum, tyInt..tyFloat128, tyCString, tyPointer:
|
||||
result := true;
|
||||
tyAnyEnum: result := kind = skParam;
|
||||
tyGenericInst: result := typeAllowedAux(marker, lastSon(t), kind);
|
||||
tyRange: result := skipGeneric(t.sons[0]).kind in
|
||||
[tyChar, tyEnum, tyInt..tyFloat128];
|
||||
tyOpenArray:
|
||||
result := (kind = skParam) and typeAllowedAux(marker, t.sons[0], skVar);
|
||||
tySequence: result := (kind <> skConst)
|
||||
and typeAllowedAux(marker, t.sons[0], skVar)
|
||||
or (t.sons[0].kind = tyEmpty);
|
||||
tyArray: result := typeAllowedAux(marker, t.sons[1], skVar);
|
||||
tyPtr, tyRef: result := typeAllowedAux(marker, t.sons[0], skVar);
|
||||
tyArrayConstr, tyTuple, tySet: begin
|
||||
for i := 0 to sonsLen(t)-1 do begin
|
||||
result := typeAllowedAux(marker, t.sons[i], kind);
|
||||
if not result then exit
|
||||
end;
|
||||
end;
|
||||
tyObject: begin
|
||||
for i := 0 to sonsLen(t)-1 do begin
|
||||
result := typeAllowedAux(marker, t.sons[i], skVar);
|
||||
if not result then exit
|
||||
end;
|
||||
if t.n <> nil then result := typeAllowedNode(marker, t.n, skVar)
|
||||
end
|
||||
end
|
||||
end;
|
||||
|
||||
function typeAllowed(t: PType; kind: TSymKind): bool;
|
||||
var
|
||||
marker: TIntSet;
|
||||
begin
|
||||
IntSetInit(marker);
|
||||
result := typeAllowedAux(marker, t, kind);
|
||||
end;
|
||||
|
||||
function align(address, alignment: biggestInt): biggestInt;
|
||||
begin
|
||||
result := (address + (alignment-1)) and not (alignment-1);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
//
|
||||
// The Nimrod Compiler
|
||||
// (c) Copyright 2008 Andreas Rumpf
|
||||
// (c) Copyright 2009 Andreas Rumpf
|
||||
//
|
||||
// See the file "copying.txt", included in this
|
||||
// distribution, for details about the copyright.
|
||||
|
|
@ -66,7 +66,7 @@ type
|
|||
wMerge, wLib, wDynlib, wCompilerproc, wCppmethod, wFatal,
|
||||
wError, wWarning, wHint, wLine, wPush, wPop,
|
||||
wDefine, wUndef, wLinedir, wStacktrace, wLinetrace, wPragma,
|
||||
wLink, wCompile, wLinksys, wFixupsystem, wDeprecated, wVarargs,
|
||||
wLink, wCompile, wLinksys, wDeprecated, wVarargs,
|
||||
wByref, wCallconv, wBreakpoint, wDebugger, wNimcall, wStdcall,
|
||||
wCdecl, wSafecall, wSyscall, wInline, wNoInline, wFastcall, wClosure,
|
||||
wNoconv, wOn, wOff, wChecks, wRangechecks, wBoundchecks,
|
||||
|
|
@ -84,7 +84,7 @@ type
|
|||
wMaxErr, wExpr, wStmt, wTypeDesc,
|
||||
wSubsChar, wAstCache, wAcyclic, wIndex,
|
||||
// commands:
|
||||
wCompileToC, wCompileToCpp, wCompileToEcmaScript,
|
||||
wCompileToC, wCompileToCpp, wCompileToEcmaScript, wCompileToLLVM,
|
||||
wPretty, wDoc, wPas,
|
||||
wGenDepend, wListDef, wCheck, wParse, wScan, wBoot, wLazy,
|
||||
wRst2html, wI,
|
||||
|
|
@ -140,7 +140,7 @@ const
|
|||
'merge', 'lib', 'dynlib', 'compilerproc', 'cppmethod', 'fatal',
|
||||
'error', 'warning', 'hint', 'line', 'push', 'pop',
|
||||
'define', 'undef', 'linedir', 'stacktrace', 'linetrace', 'pragma',
|
||||
'link', 'compile', 'linksys', 'fixupsystem', 'deprecated', 'varargs',
|
||||
'link', 'compile', 'linksys', 'deprecated', 'varargs',
|
||||
'byref', 'callconv', 'breakpoint', 'debugger', 'nimcall', 'stdcall',
|
||||
'cdecl', 'safecall', 'syscall', 'inline', 'noinline', 'fastcall', 'closure',
|
||||
'noconv', 'on', 'off', 'checks', 'rangechecks', 'boundchecks',
|
||||
|
|
@ -158,7 +158,7 @@ const
|
|||
'maxerr', 'expr', 'stmt', 'typedesc',
|
||||
'subschar', 'astcache', 'acyclic', 'index',
|
||||
// commands:
|
||||
'compiletoc', 'compiletocpp', 'compiletoecmascript',
|
||||
'compiletoc', 'compiletocpp', 'compiletoecmascript', 'compiletollvm',
|
||||
'pretty', 'doc', 'pas', 'gendepend', 'listdef', 'check', 'parse',
|
||||
'scan', 'boot', 'lazy', 'rst2html', 'i'+'',
|
||||
|
||||
|
|
|
|||
|
|
@ -13,3 +13,4 @@
|
|||
@elif vcc:
|
||||
cgen.speed = ""
|
||||
@end
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,12 @@ Files: "tests/*.cfg"
|
|||
Files: "tests/*.tmpl"
|
||||
Files: "tests/gtk/*.nim"
|
||||
|
||||
Files: "examples/*.nim"
|
||||
Files: "examples/*.html"
|
||||
Files: "examples/*.txt"
|
||||
Files: "examples/*.cfg"
|
||||
Files: "examples/*.tmpl"
|
||||
|
||||
Files: "lib/base/*.c"
|
||||
Files: "lib/base/*.nim"
|
||||
Files: "lib/base/gtk/*.nim"
|
||||
|
|
@ -75,8 +81,8 @@ Files: "lib/base/lua/*.nim"
|
|||
|
||||
[Windows]
|
||||
Files: "bin/nimrod.exe"
|
||||
Files: "dist/llvm-gcc4.2"
|
||||
BinPath: r"bin;dist\llvm-gcc4.2\bin"
|
||||
Files: "dist/mingw"
|
||||
BinPath: r"bin;dist\mingw\bin"
|
||||
|
||||
[Unix]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Test the case statment
|
||||
# Test the case statement
|
||||
|
||||
type
|
||||
tenum = enum eA, eB, eC
|
||||
|
|
@ -6,6 +6,7 @@ type
|
|||
var
|
||||
x: string
|
||||
y: Tenum = eA
|
||||
i: int
|
||||
|
||||
case y
|
||||
of eA: write(stdout, "a\n")
|
||||
|
|
@ -18,3 +19,14 @@ of "aa", "bb": write(stdout, "Du bist nicht mein Meister\n")
|
|||
of "cc", "hash", "when": nil
|
||||
of "will", "it", "finally", "be", "generated": nil
|
||||
else: write(stdout, "das sollte nicht passieren!\N")
|
||||
|
||||
case i
|
||||
of 0..5, 8, 9: nil
|
||||
of 6, 7: nil
|
||||
elif x == "Ha":
|
||||
nil
|
||||
elif x == "Ho":
|
||||
nil
|
||||
else:
|
||||
nil
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
# Test wether the bindings at least compile...
|
||||
|
||||
import
|
||||
unicode,
|
||||
unicode, cgi, terminal, libcurl, web,
|
||||
parsexml, parseopt, parsecfg,
|
||||
osproc, zipfiles,
|
||||
sdl, smpeg, sdl_gfx, sdl_net, sdl_mixer, sdl_ttf,
|
||||
sdl_image, sdl_mixer_nosmpeg,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,13 @@
|
|||
# test variables of type proc
|
||||
|
||||
proc pa() {.cdecl.} = write(stdout, "pa")
|
||||
proc pb() {.cdecl.} = write(stdout, "pb")
|
||||
proc pc() {.cdecl.} = write(stdout, "pc")
|
||||
proc pd() {.cdecl.} = write(stdout, "pd")
|
||||
proc pe() {.cdecl.} = write(stdout, "pe")
|
||||
|
||||
const
|
||||
algos = [pa, pb, pc, pd, pe]
|
||||
|
||||
var
|
||||
x: proc (a, b: int): int {.cdecl.}
|
||||
|
|
@ -6,9 +15,12 @@ var
|
|||
proc ha(c, d: int): int {.cdecl.} =
|
||||
echo(c + d)
|
||||
result = c + d
|
||||
|
||||
for a in items(algos):
|
||||
a()
|
||||
|
||||
x = ha
|
||||
discard x(3, 4)
|
||||
|
||||
#OUT 7
|
||||
#OUT papbpcpdpe7
|
||||
|
||||
|
|
|
|||
|
|
@ -13,5 +13,9 @@ type
|
|||
PB = ref TB
|
||||
TB = array [0..3, P1]
|
||||
T1 = array [0..6, PB]
|
||||
|
||||
var
|
||||
x: PA
|
||||
new(x)
|
||||
#ERROR_MSG internal error: cannot generate C type for: PA
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,17 @@
|
|||
|
||||
import
|
||||
regexprs
|
||||
|
||||
if "keyA = valueA" =~ r"\s*(\w+)\s*\=\s*(\w+)":
|
||||
write(stdout, "key: ", matches[1])
|
||||
elif "# comment!" =~ r"\s*(\#.*)":
|
||||
echo("comment: ", matches[1])
|
||||
else:
|
||||
echo("Bug!")
|
||||
|
||||
if "Username".match("[A-Za-z]+"):
|
||||
echo("Yes!")
|
||||
else:
|
||||
echo("Bug!")
|
||||
|
||||
#OUT Yes!
|
||||
#OUT key: keyAYes!
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue